典型的卷积神经网络通常由以下三种层结构共同组成:
卷积层(Convolution)、下采样池化层(Pooling)、全连接层(Fully connected)
其中卷积是为了提取出局部特征值,池化是从这些特征值里面筛选更有特征的特征值,使得卷积输出的数组尺寸近一步缩小,而全连接就是把以前的局部特征重新通过权值矩阵组装成完整的图。
注:只用一层全连接层有时候没法解决非线性问题,而如果有两层或以上全连接层就可以很好地解决非线性问题了,这也是代码用两层全连接层(Dense)原因。
具体CNN原理可以看看我另一篇博客,相信仔细看完会对CNN有更深刻的理解:https://blog.csdn.net/weixin_39615182/article/details/109854446
分享两个数据下载链接,一个是kaggle提供的,另一个是我自己取了一部分并处理好的图片。说下区别,kaggle提供的训练集就有25000张图片,训练集太大了,如果每张图片要处理成150×150的图片,图片表示150×150的数组,也就是25000×150×150=562500000,数据量太大,我笔记本根本跑不动,如果你电脑配置很高,有很强大的独显可以拿原数据去跑下,我自己取了4000张图片作为训练集,1000张作为测试集,其中每个数据集中猫狗图片各一半(训练/测试=4/1)。
链接: https://pan.baidu.com/s/1XrVZ04IKr4jughz3OwcQPA 提取码:6swb
1、数据预处理部分:将所有原图片处理成相同大小的新图片,再将图片转成对应数组,保存为.npy文件,保存为npy文件是为了避免每次都要长时间将所有图片转换为数组,这样保存一次就能以后直接加载数组即可 → data_preprocessing.py
注:npy文件是一种用python程序生成,用于存放数据的文件格式,npy文件存的就是数组,由numpy库的save与load来生成与加载
2、对数据进行训练、建模、预测部分 → cat_dog_main.py
data_preprocessing.py
import os
import random
import cv2
import numpy as np
from keras.preprocessing import image
####################################### Method part
def load_data(path, flag):
files = os.listdir(path) # os.listdir() 方法用于返回指定的文件夹中的文件或文件夹的名字的列表
random.shuffle(files) # 打乱数据集图片顺序,随机化
images, labels = [], []
for filename in files:
# 图片批处理成128*128像素的数据集(opencv处理)
print(filename) # 打印正在转换的图片名
img_path = path + filename
img = cv2.imread(img_path)
res = cv2.resize(img, (128, 128), interpolation=cv2.INTER_CUBIC)
cv2.imwrite(img_path, res)
# 以下循环都是将图片转成数组存到images列表(数组)中,并且给每个图片上标签
img = image.load_img(img_path, target_size=(128, 128)) # 加载128*128的图片
img_array = image.img_to_array(img)
images.append(img_array)
if 'cat' in filename:
labels.append(0) # 0 表示猫,1表示狗
else:
labels.append(1)
images = np.array(images)
labels = np.array(labels)
if flag == 1:
np.save("./npy_file/train_images.npy", images) # 将images数组保存成npy文件,以防每次都要将图片转成数组
np.save("./npy_file/train_labels.npy", labels)
print("save train_data success...")
else:
np.save("./npy_file/test_images.npy", images)
np.save("./npy_file/test_labels.npy", labels)
print("save test_data success...")
return images, labels
cat_dog_main.py
import matplotlib.pyplot as plt
import numpy as np
from keras.utils import to_categorical
from keras.layers import Dense, Flatten, Conv2D, MaxPooling2D, Dropout
from keras import models, optimizers, regularizers, initializers
from data_preprocessing import load_data
####################################### 1、加载与处理数据,训练集/测试集=4/1
# train_images, train_labels = load_data(path='./cat_dog/train/', flag=1) # flag=1表示训练数据
# test_images, test_labels = load_data(path='./cat_dog/test/', flag=2)
train_images = np.load("./npy_file/train_images.npy")
train_labels = np.load("./npy_file/train_labels.npy")
test_images = np.load("./npy_file/test_images.npy")
test_labels = np.load("./npy_file/test_labels.npy")
print("train_image:", train_images.shape, "train_labels:", train_labels.shape) # (4000, 128, 128, 3)
print("test_image:", test_images.shape, "test_labels:", test_labels.shape)
img_w = train_images.shape[1]
img_h = train_images.shape[2]
train_images = train_images.astype("float")
test_images = test_images.astype("float")
train_labels = to_categorical(train_labels) # 转成一维行向量
test_labels = to_categorical(test_labels)
# 归一化
train_images = train_images / 255
test_images = test_images / 255
####################################### 2、搭建神经网络
# 卷积->池化->卷积->池化->卷积->池化->Flatten(扁平化)->全连接->全连接
model = models.Sequential()
# 第一次卷积可以提取出低层次的特征,32个卷积核
model.add(Conv2D(32, 3, input_shape=(128, 128, 3), activation="relu")) # 卷积
model.add(Dropout(0.3))
model.add(MaxPooling2D(pool_size=(2, 2)))
# 第二次卷积可以提取出中层次的特征
model.add(Conv2D(64, 3, activation='relu'))
model.add(Dropout(0.3))
model.add(MaxPooling2D(pool_size=(2, 2)))
# 第三次卷积可以提取出高层次的特征
model.add(Conv2D(128, 3, activation='relu'))
model.add(Dropout(0.3))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten()) # Flatten层用来将多维的输入一维化,常用在从卷积层到全连接层的过渡,即扁平化操作
model.add(Dense(units=256, activation='relu', kernel_regularizer=regularizers.l2(0.01),
kernel_initializer=initializers.truncated_normal(mean=0.0, stddev=0.01, seed=None)))
model.add(Dropout(0.3))
model.add(Dense(units=2, activation='softmax')) # 二分类问题
model.summary()
######################################## 3、训练
# TBCallbacks = callbacks.TensorBoard(log_dir='./logs', histogram_freq=1, write_graph=True, write_images=True)
# 模型编译
EPOCHS = 25
model.compile(loss='categorical_crossentropy', optimizer=optimizers.Adam(lr=0.0001), metrics=['accuracy'])
H = model.fit(train_images, train_labels, epochs=EPOCHS, batch_size=150, verbose=2,
validation_data=(test_images, test_labels))
######################################## 4、预测与绘图
# 预测(取前10个观察预测情况)
y_pre = model.predict(test_images[:10])
print("预测值:", y_pre, "\n真实值:", test_labels[:10])
# 绘图
N = np.arange(0, EPOCHS) # 迭代数
plt.figure() # 新建画布
plt.plot(N, H.history["accuracy"], label="train_acc")
plt.plot(N, H.history["val_accuracy"], label="test_acc")
plt.title("accuracy")
plt.xlabel("EPOCHS")
plt.ylabel("accuracy")
plt.legend()
plt.show()
第一次运行结果(EPochs=20,batch_size=150)
可以观察到训练集拟合的太好,出现过拟合,而且很大,相差27%,我们开始优化,用正则化与初始化以及dropout来解决过拟合问题
关于过拟合及其常见处理办法推荐下面的文章:
https://blog.csdn.net/jingbo18/article/details/80609006
第二次运行结果
训练准确率:0.7703,测试准确率为0.7110
第三次运行结果(Epochs=25,batch_size=150)
学习率:0.0001 l2(0.01) 初始化中参数stddev=0.01
结果:训练准确率=0.8192 测试准确率=0.7560,还是有点过拟合,这里我没有继续调了,因为跑一次就要30-40分钟,我这三次就用了将近两小时,太费时了,无奈没办法电脑还是2016年买的,现在有点小垃圾了,大家有兴趣可以继续调下,电脑配置好的,可以多取些图片作为样本跑,因为也不排除我取4000张图片训练,样本还是少,这也可能是过拟合的原因。
结论:取4000张作为训练集正确率达70-80%。
下面是代码中预测部分,取打乱后的前十张图片观察预测结果,发现7对,3错
注:softmax函数,预测结果是概率,第一个位置表示猫,第二个位置表示狗,观察预测出的哪个概率大,并与真实比较,看预测对不对。
以上就是猫狗大战的讲解,希望其中的某一点对你有帮助。