1. 描述
- 总述
数据集:kaggle的猫狗数据集
网络:自定义神经卷积网络
防过拟合措施:图像增强和dropout层
开发工具:jupyter+python+keras
- 数据集说明
原始猫狗数据集包含了25000张图像(猫狗各12500张),我们这里不适用全部数据集,我们将原始train中的cats中0~1000张图定义为training,1000-1500张定义为validation,1500-2000张定义为test;dogs同理
- 这里给出网络结构:
2. 从原始数据集中的train分离出我们需要的train、validation和test
import os,shutil
original_dataset_dir="dogs-vs-cats/train" # 原始数据集解压目录
## 创建一个一级文件夹
base_dir="dogs-vs-cats_small" # 保存小数据集的目录
if os.path.exists(base_dir): #如果不存在small文件夹则执行
shutil.rmtree(base_dir)
os.mkdir(base_dir) #创建文件夹
## 创建三个二级文件夹:train、validation、test
train_dir=os.path.join(base_dir,'train');os.mkdir(train_dir) # 定义训练数据文件夹并创建
validation_dir=os.path.join(base_dir,'validation');os.mkdir(validation_dir) # 定义验证数据文件夹并创建
test_dir=os.path.join(base_dir,'test');os.mkdir(test_dir) # 定义测试数据文件夹并创建
## 创建train下cats文件夹
train_cats_dir=os.path.join(train_dir,'cats')
os.mkdir(train_cats_dir)
## 创建train下dogs文件夹
train_dogs_dir=os.path.join(train_dir,'dogs')
os.mkdir(train_dogs_dir)
## 创建validation下cats文件夹
validation_cats_dir=os.path.join(validation_dir,'cats')
os.mkdir(validation_cats_dir)
validation_dogs_dir=os.path.join(validation_dir,'dogs');os.mkdir(validation_dogs_dir) #在训练集文件夹下定义dogs文件夹
test_cats_dir=os.path.join(test_dir,'cats');os.mkdir(test_cats_dir) #在训练集文件夹下定义cats文件夹
test_dogs_dir=os.path.join(test_dir,'dogs');os.mkdir(test_dogs_dir) #在训练集文件夹下定义dogs文件夹
#将原始数据集的train中cat和dog各前1000张复制到train/cats和train/dogs
for className in ["cat","dog"]:
imageNames=["{}.{}.jpg".format(className,i) for i in range(1000)]
for name in imageNames:
src=os.path.join(original_dataset_dir,name)
if className=="cat":
dst=os.path.join(train_cats_dir,name)
else:
dst=os.path.join(train_dogs_dir,name)
shutil.copyfile(src,dst)
#将原始数据集的train中cat和dog各1000~1500张复制到validation/cats和validation/dogs
for className in ["cat","dog"]:
imageNames=["{}.{}.jpg".format(className,i) for i in range(1000,1500)]
for name in imageNames:
src=os.path.join(original_dataset_dir,name)
if className=="cat":
dst=os.path.join(validation_cats_dir,name)
else:
dst=os.path.join(validation_dogs_dir,name)
shutil.copyfile(src,dst)
#将原始数据集的train中cat和dog各1500~2000张复制到test/cats和test/dogs
for className in ["cat","dog"]:
imageNames=["{}.{}.jpg".format(className,i) for i in range(1500,2000)]
for name in imageNames:
src=os.path.join(original_dataset_dir,name)
if className=="cat":
dst=os.path.join(test_cats_dir,name)
else:
dst=os.path.join(test_dogs_dir,name)
shutil.copyfile(src,dst)
3. 网络搭建
from keras import layers
from keras import models
model=models.Sequential()
# block 1
model.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=(150,150,3)))
model.add(layers.MaxPool2D((2,2)))
# block 2
model.add(layers.Conv2D(64,(3,3),activation="relu"))
model.add(layers.MaxPool2D((2,2)))
# block 3
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPool2D((2,2)))
# block 4
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPool2D((2,2)))
# output
model.add(layers.Flatten()) #展开为向量
#--------------------------------------------------------------
# 添加dropout层
model.add(layers.Dropout(0.5))
#--------------------------------------------------------------
model.add(layers.Dense(512,activation="relu"))
model.add(layers.Dense(1,activation="sigmoid"))
model.summary() # 打印网络结构,可以看到网络结构表
关于最后一层激活层和损失函数(下面会定义)的选择,参考:
4. 配置模型(优化器)
from keras import optimizers
# loss的选择从上图可以看出二分类问题选择binary_crossentropy
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['accuracy'])
- 数据预处理
- 读取图像
- 将JPEG解码为RGB文件
- 将RGB图像转为浮点数张量
- 将像素缩放到0~1间(归一化)
当然Keras有一个图像处理辅工具的模块,位于keras.preprocessing.image,特别是包含了ImageDataGenerator类,可以创建python生成器,将硬盘上的图像自动转换为与处理好的张量批量.
这里使用了python的生成器
- python生成器
def generator():
i=0
while True:
i+=1
yield i
for item in generator():
print(item)
if item>4:
break
"""
yield像是一个暂停键,并带有return功能:当运行到yield时,返回i值;
当下次再调用该函数时,用yield的下一句开始运行(并非从i=0开始,而是从
while True?开始),并且i还是上次i的值
"""
数据预处理代码:
from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing import image
import matplotlib.pyplot as plt
train_datagen=ImageDataGenerator(rescale=1./255,
rotation_range=40, #随机旋转的角度范围(0~180°) range代表范围
width_shift_range=0.2, # width_shift/height_shift:在图像水平或垂直方向上平移的范围(按总宽总高比例)
height_shift_range=0.2,
shear_range=0.2, # 随机错切变换的角度
zoom_range=0.2, # 图像随机缩放的范围
horizontal_flip=True, # 随机将一半图像水平翻转
) # 将所有图像乘以1/255缩放,归一化
validation_datagen=ImageDataGenerator(rescale=1./255)
#--------------------------------------------------------------------------
# 显示随机增强后的训练图像
# fnames=[os.path.join(train_cats_dir,fname) for fname in os.listdir(train_cats_dir)]
# img_path=fnames[3]
# img=image.load_img(img_path,target_size=(150,150)) # 读取图片并调整大小
# x=image.img_to_array(img) #转为(150,150,3)的Numpy数组
# x=x.reshape((1,)+x.shape) #改变形状为(1,150,150,3)
# i=0
# for batch in train_datagen.flow(x,batch_size=1):
# plt.figure(i)
# imgplot=plt.imshow(image.array_to_img(batch[0]))
# i+=1
# if i%4==0:
# break
# plt.show()
#--------------------------------------------------------------------------
# 参数分别指目标目录、调整为多少尺寸、batch_size、标签类型(因为使用了binary_crossentropy损失,所以需要用二进制标签)
train_generator=train_datagen.flow_from_directory(train_dir,
target_size=(150,150),
batch_size=20,
class_mode="binary"
)
validation_generator=validation_datagen.flow_from_directory(validation_dir,
target_size=(150,150),
batch_size=20,
class_mode="binary"
)
# 查看train_generator产生的数据和标签的性质
for data_batch,labels_batch in train_generator:
print("data batch shape:",data_batch.shape)
print("lables batch shape:",labels_batch.shape)
break
可以看到data_batch和labels_batch的形状:
5. 开始训练
在这里传入的数据和标签不再是以前的直接传入,而是使用生成器传入。fit_generator:API描述
fit_generator(generator,
steps_per_epoch=None, #一个epoch从generator产生的总步数,本例中一个generator是20张,故=2000/20=100,需要100次generator
epochs=1,
verbose=1, # 日志显示模式: 0 = 安静模式, 1 = 进度条, 2 = 每轮一行。
callbacks=None,
validation_data=None,
validation_steps=None, #同steps_per_epoch,仅当validation_data为生成器才可用
class_weight=None,
max_queue_size=10,
workers=1,
use_multiprocessing=False,
shuffle=True, #是否在每轮迭代之前打乱 batch 的顺序
initial_epoch=0)
代码:
history=model.fit_generator(train_generator,
steps_per_epoch=100,
epochs=100,
validation_data=validation_generator,
validation_steps=50)
效果如下:
6.保存模型
model.save("cats_and_dogs_small_V3.h5") # 保存到脚本文件所在目录
7.可视化
history实际上是一个字典,对于epoch=4的结果,history内容如下:
所以使用plt以epoch为横坐标,相关指标为纵坐标画图即可
import matplotlib.pyplot as plt
acc=history.history['accuracy']
print("训练最佳精度:",max(acc))
val_acc=history.history['val_accuracy']
print("验证最佳精度:",max(val_acc))
loss=history.history['loss']
val_loss=history.history['val_loss']
epochs=range(1,len(acc)+1)
plt.plot(epochs,acc,'bo',label='Training acc')
plt.plot(epochs,val_acc,'b',label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend() #创建图例
plt.figure()
plt.plot(epochs,loss,'bo',label='Training loss')
plt.plot(epochs,val_loss,'b',label='Validation loss')
plt.title('Training and validation loss')
plt.legend() #创建图例
plt.show()