1,mnist数据集

1.1 直接使用2个稠密层

#使用keras的完整mnist训练代码
from keras import models
from keras import layers
from keras.datasets import mnist
from keras.utils import to_categorical
(train_images,train_labels),(test_images,test_labels)=mnist.load_data()
network=models.Sequential()
#稠密层,也称为全连接层,操作为output=relu(dot(W,input)+b)
#relu(x)=max(x,0),即小于0时为0,大于0时为自己
#input_shape为每个图片像素信息对应张量,第二个留空(不指定批次大小)
network.add(layers.Dense(512,activation='relu',input_shape=(28*28,)))
#后面不需要指定输入大小,自动推算出为512
network.add(layers.Dense(10,activation='softmax'))
#编译需要优化器、损失函数、度量标准
network.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])
#将2维的像素降维。然后将像素转换为0-1之间的浮点数(因为是灰度图)
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255
#将标签转换,这个跟独热码是类似的,只是封装的更好用
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
#训练,指定输入数据、输入标签、批量大小、整体训练次数、verbose=0不打印过程,有利于提高性能
network.fit(train_images, train_labels, epochs=5, batch_size=128,verbose=0)
#测试结果
test_loss, test_acc = network.evaluate(test_images, test_labels)
#98.01%
print('test_acc:', test_acc)

1.2 使用卷积层

#基于卷积网络的mnist
from keras import models
from keras import layers
from keras.datasets import mnist
from keras.utils import to_categorical
import os
(train_images,train_labels),(test_images,test_labels)=mnist.load_data()
#reshape,指定颜色通道为1位
train_images=train_images.reshape((60000,28,28,1))
#转换成浮点数
train_images=train_images.astype('float32')/255
test_images=test_images.reshape((10000,28,28,1))
test_images=test_images.astype('float32')/255
#将标签转换成独热码形式
train_labels=to_categorical(train_labels)
test_labels=to_categorical(test_labels)
model=models.Sequential()
#卷积层,窗口3*3,输出(None,26,26,32)
#窗口一般为3*3或5*5
#32表示对于每一个窗口,提取32种特征行为
#填充padding:valid or same
#步长strides:默认1,很少设为其他值,使用池化层即可
model.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=(28,28,1)))
#最大池化层,窗口2*2,步长为2,输出(None,13,13,32)
#作用:一般取窗口中最大值。如果没有池化层,如使用3*3窗口,到第三个卷积层也不过是代表了7*7窗口
#仅能反映部分信息;另一方面,将产生过多的参数,难以训练也容易过拟合
#平均池化层:使用窗口中元素的平均值。一般最大值包含的信息量更多,表现优于平均值。
model.add(layers.MaxPooling2D((2,2)))
#输出(None,11,11,64)
model.add(layers.Conv2D(64,(3,3),activation="relu"))
#输出(None,5,5,64)
model.add(layers.MaxPooling2D((2,2)))
#输出(None,3,3,64)
model.add(layers.Conv2D(64,(3,3),activation="relu"))
#Flatten层,输出(None,3*3*64)
model.add(layers.Flatten())
# 输出(None,64)
model.add(layers.Dense(64,activation='relu'))
# 输出(None,10)
model.add(layers.Dense(10,activation='softmax'))
#输出模型概要
# sum=model.summary()
# print(sum)
model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['acc'])
model.fit(train_images,train_labels,epochs=5,batch_size=64)
result=model.evaluate(test_images,test_labels)
#[0.027329410206822648, 0.9928]从98.01%提高到99.28%,成功率虽然没提高多少,但是失败率降低了64%
print(result)

2,cat vs dog数据集

数据集:猫和狗各有1000个训练样本,500个验证样本,500个测试样本

2.1 使用4个卷积-池化层,精度70%

#图像分类,取自cat_dog数据集中的一小部分
from keras import models
from keras import layers
from keras import optimizers
import numpy as np
import json
from keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
#需要注意目录结构,首先分成train,validation和test,每个文件夹下另有cats和dogs文件夹
train_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/train'
validation_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/validation'
test_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/test'
model=models.Sequential()
model.add(layers.Conv2D(32,(3,3),activation='relu',input_shape=(150,150,3)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64,(3,3),activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation='relu'))
#到此输出7*7*128
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dense(512,activation='relu'))
model.add(layers.Dense(1,activation='sigmoid'))
model.compile(optimizer=optimizers.RMSprop(lr=1e-4),loss='binary_crossentropy',metrics=['acc'])
#定义generator,缩放为像素除以255
train_datagen=ImageDataGenerator(rescale=1./255)
test_datagen=ImageDataGenerator(rescale=1./255)
#从目录中引入数据
train_generator=train_datagen.flow_from_directory(train_dir,target_size=(150,150),batch_size=20,class_mode='binary')
validation_generator=test_datagen.flow_from_directory(validation_dir,target_size=(150,150),batch_size=20,class_mode='binary')
test_generator=test_datagen.flow_from_directory(test_dir,target_size=(150,150),batch_size=20,class_mode='binary')
#validation_data可以传入一个generator,训练数据2000,每批20,所以100批,验证数据1000共50批
history=model.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)
#保存模型结构
json_string=model.to_json()
open('D:/tmp/DeepLearningWithPython/cat_dog.json','w').write(json_string)
model.save_weights('D:/tmp/DeepLearningWithPython/cat_dog.h5')
#这个保存内存很大
# model.save('D:/tmp/DeepLearningWithPython/cat_dog.h5')
h=history.history
acc=h['acc']
val_acc=h['val_acc']
loss=h['loss']
val_loss=h['val_loss']
epochs=range(1,len(acc)+1)
plt.plot(epochs,acc,'ro',label='train_acc')
plt.plot(epochs,val_acc,'b^',label='val_acc')
plt.title('accurancy')
plt.legend()
#如果是show,这个显示完了才能显示下一个图,figure可以同时显示多个,最后使用show
plt.figure()

plt.plot(epochs,loss,'ro',label='train_loss')
plt.plot(epochs,val_loss,'b^',label='val_loss')
plt.title('loss')
plt.legend()
plt.show()

# [1.2409546909481286, 0.6980000002384186]
result=model.evaluate_generator(test_generator,1000)
print(result)
#以下是加载保存的模型,编译后进行测试
# from keras.models import model_from_json
# model=model_from_json(open('/tmp/DeepLearningWithPython/cat_dog.json','r').read())
# model.load_weights('/tmp/DeepLearningWithPython/cat_dog.h5')
# model.compile(optimizer=optimizers.RMSprop(lr=1e-4),loss='binary_crossentropy',metrics=['acc'])
# result=model.evaluate_generator(test_generator,steps=50)
# print(result)

2.2 在2.1基础上,使用数据扩张(这个对于小样本训练很重要)和dropout,精度提升至80.8%

#使用数据扩张和dropout改进
from keras import models
from keras import layers
from keras import optimizers
import numpy as np
import json
import os
from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing import image
import matplotlib.pyplot as plt
train_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/train'
validation_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/validation'
test_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/test'
model=models.Sequential()
model.add(layers.Conv2D(32,(3,3),activation='relu',input_shape=(150,150,3)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64,(3,3),activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
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.compile(optimizer=optimizers.RMSprop(lr=1e-4),loss='binary_crossentropy',metrics=['acc'])
#数据扩张
train_datagen=ImageDataGenerator(
	rescale=1./255,
	rotation_range=40,
	width_shift_range=0.2,
	height_shift_range=0.2,
	shear_range=0.2,
	zoom_range=0.2,
	horizontal_flip=True,
	fill_mode='nearest'
	)
#验证集不能扩张
test_datagen=ImageDataGenerator(rescale=1./255)

train_generator=train_datagen.flow_from_directory(train_dir,target_size=(150,150),batch_size=20,class_mode='binary')
validation_generator=test_datagen.flow_from_directory(validation_dir,target_size=(150,150),batch_size=20,class_mode='binary')
test_generator=test_datagen.flow_from_directory(test_dir,target_size=(150,150),batch_size=20,class_mode='binary')
history=model.fit_generator(train_generator,steps_per_epoch=100,epochs=100,validation_data=validation_generator,validation_steps=50,verbose=2)

json_string=model.to_json()
open('D:/tmp/DeepLearningWithPython/cat_dog_2.json','w').write(json_string)
model.save_weights('D:/tmp/DeepLearningWithPython/cat_dog_2.h5')
h=history.history
acc=h['acc']
val_acc=h['val_acc']
loss=h['loss']
val_loss=h['val_loss']
epochs=range(1,len(acc)+1)
plt.plot(epochs,acc,'ro',label='train_acc')
plt.plot(epochs,val_acc,'b^',label='val_acc')
plt.title('accurancy')
plt.legend()
plt.figure()

plt.plot(epochs,loss,'ro',label='train_loss')
plt.plot(epochs,val_loss,'b^',label='val_loss')
plt.title('loss')
plt.legend()
plt.show()
#[0.44894256442785263, 0.8080000078678131]比70%好很多
result=model.evaluate_generator(test_generator,steps=50)
print(result)

2.3 使用预训练的模型VGG16

将训练数据经过预训练模型,输出结果作为输入,进入后面的稠密层,仅训练这些稠密层。这个不知道哪里有错,精度太高了。

#使用预训练的模型
#方式1:先用预训练模型操作输入数据,输出结果
#以此结果作为输入,再使用后面的稠密层去训练
#这个感觉有问题,但又不知道在哪,精度太高了,验证集精度都在99%以上,测试集99.5%
from keras import models
from keras import layers
from keras import optimizers
import numpy as np
import json
# import os
from keras.preprocessing.image import ImageDataGenerator
# from keras.preprocessing import image
from keras.applications import VGG16
import matplotlib.pyplot as plt
#include_top=False不包括上面的稠密层,weights定义用于初始化模型的weight checkpoint
#input_shape可选参数,不设置则可以适应各种大小
conv_base=VGG16(weights='imagenet',include_top=False,input_shape=(150,150,3))
#包括输入层、卷积层*2、池化层、卷积层*2、池化层、卷积层*3、池化层、卷积层*3、池化层、卷积层*3、池化层
#仅这个分析都需要耗时8min
#最后是4*4*512
# print(conv_base.summary())
train_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/train'
validation_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/validation'
test_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/test'
datagen=ImageDataGenerator(rescale=1./255)
batch_size=20
#特征提取
def extract_features(directory,count):
	features=np.zeros(shape=(count,4,4,512))
	labels=np.zeros(shape=(count))
	generator=datagen.flow_from_directory(directory,target_size=(150,150),batch_size=batch_size,class_mode='binary')
	i=0
	#features_batch,label_batch表示一个批次的图像特征及标签
	#features_batch维度为(batch_size,4,4,512),另一个是batch_size大小的一维向量
	for input_batch,label_batch in generator:
		#根据输入,输出结果
		features_batch=conv_base.predict(input_batch)
		features[i*batch_size:(i+1)*batch_size]=features_batch
		labels[i*batch_size:(i+1)*batch_size]=label_batch
		i+=1
		if i*batch_size>=count:
			break
		return features,labels
#使用函数提取特征及标签
train_features,train_labels=extract_features(train_dir,2000)
validation_features,validation_labels=extract_features(validation_dir,1000)
test_features,test_labels=extract_features(test_dir,1000)
#flatten
train_features=np.reshape(train_features,(2000,4*4*512))
validation_features=np.reshape(validation_features,(1000,4*4*512))
test_features=np.reshape(test_features,(1000,4*4*512))
#model build
model=models.Sequential()
#input_dim
model.add(layers.Dense(256,activation='relu',input_dim=4*4*512))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1,activation='sigmoid'))
#
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),loss='binary_crossentropy',metrics=['acc'])
history=model.fit(train_features,train_labels,epochs=50,batch_size=20,validation_data=(validation_features,validation_labels),verbose=2)

# json_string=model.to_json()
# open('D:/tmp/DeepLearningWithPython/cat_dog_2.json','w').write(json_string)
# model.save_weights('D:/tmp/DeepLearningWithPython/cat_dog_2.h5')
h=history.history
acc=h['acc']
val_acc=h['val_acc']
loss=h['loss']
val_loss=h['val_loss']
epochs=range(1,len(acc)+1)
plt.plot(epochs,acc,'ro',label='train_acc')
plt.plot(epochs,val_acc,'b^',label='val_acc')
plt.title('accurancy')
plt.legend()
#如果是show,这个显示完了才能显示下一个图,figure可以同时显示多个,最后使用show
plt.figure()

plt.plot(epochs,loss,'ro',label='train_loss')
plt.plot(epochs,val_loss,'b^',label='val_loss')
plt.title('loss')
plt.legend()
plt.show()
result=model.evaluate(test_features,test_labels)
#[0.37746662998199465, 0.995]还没用数据扩张精度就如此之高,原因呢?
print(result)

2.4 同2.3,但是讲预训练模型作为整体训练模型的一部分,训练的是整体模型,使用了数据扩张。精度提升至89.4%

#方式2:将预训练的模型直接加入整体训练框架,耗时。此时需冻结预训练模型各层
#使用数据扩张
from keras import models
from keras import layers
from keras import optimizers
import numpy as np
import json
# import os
from keras.preprocessing.image import ImageDataGenerator
# from keras.preprocessing import image
from keras.applications import VGG16
import matplotlib.pyplot as plt
conv_base=VGG16(weights='imagenet',include_top=False,input_shape=(150,150,3))

train_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/train'
validation_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/validation'
test_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/test'

model=models.Sequential()
#将预训练模型作为layer加入
model.add(conv_base)
model.add(layers.Flatten())
#未使用dropout
model.add(layers.Dense(256,activation='relu'))
model.add(layers.Dense(1,activation='sigmoid'))
#数据扩张
train_datagen=ImageDataGenerator(rescale=1./255,
	rotation_range=40,
	width_shift_range=0.2,
	height_shift_range=0.2,
	shear_range=0.2,
	zoom_range=0.2,
	horizontal_flip=True,
	fill_mode='nearest')
test_dategen=ImageDataGenerator(rescale=1./255)
train_generator=train_datagen.flow_from_directory(train_dir,target_size=(150,150),batch_size=20,class_mode='binary')
validation_generator=test_dategen.flow_from_directory(validation_dir,target_size=(150,150),batch_size=20,class_mode='binary')
test_generator=test_dategen.flow_from_directory(test_dir,target_size=(150,150),batch_size=20,class_mode='binary')
#冻结预训练模型参数更新,只更新上面层的参数,如果编译后修改,则需要再次编译
conv_base.trainable=False
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),loss='binary_crossentropy',metrics=['acc'])
history=model.fit_generator(train_generator,epochs=30,steps_per_epoch=100,validation_data=validation_generator,validation_steps=50,verbose=2)
json_string=model.to_json()
open('D:/tmp/DeepLearningWithPython/cat_dog_3.json','w').write(json_string)
model.save_weights('D:/tmp/DeepLearningWithPython/cat_dog_3.h5')


h=history.history
acc=h['acc']
val_acc=h['val_acc']
loss=h['loss']
val_loss=h['val_loss']
epochs=range(1,len(acc)+1)
plt.plot(epochs,acc,'ro',label='train_acc')
plt.plot(epochs,val_acc,'b^',label='val_acc')
plt.title('accurancy')
plt.legend()
plt.figure()

plt.plot(epochs,loss,'ro',label='train_loss')
plt.plot(epochs,val_loss,'b^',label='val_loss')
plt.title('loss')
plt.legend()
plt.show()
#[0.25788496248424053, 0.8939999973773957]
result=model.evaluate_generator(test_generator,steps=50)
print(result)

2.5 在2.4训练完成后的模型基础上,进行再训练,也即微调。精度提升至93%左右

步骤:加载2.4训练完后的模型,解冻里面预训练模型顶端的若干卷积层,再训练就是针对这部分解冻的层。

#在方式2的基础上,微调,即先调整好顶层参数,再去解冻预训练层的顶端若干层,并训练解冻的这些层
from keras import models
from keras import layers
from keras import optimizers
import numpy as np
import json
# import os
from keras.preprocessing.image import ImageDataGenerator
# from keras.preprocessing import image
from keras.applications import VGG16
import matplotlib.pyplot as plt
conv_base=VGG16(weights='imagenet',include_top=False,input_shape=(150,150,3))

train_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/train'
validation_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/validation'
test_dir='D:/Data/DeepLearningWithPython/cat_dog/kaggle/cat_dog_small/test'

model=models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256,activation='relu'))
model.add(layers.Dense(1,activation='sigmoid'))
train_datagen=ImageDataGenerator(rescale=1./255,
	rotation_range=40,
	width_shift_range=0.2,
	height_shift_range=0.2,
	shear_range=0.2,
	zoom_range=0.2,
	horizontal_flip=True,
	fill_mode='nearest')
test_dategen=ImageDataGenerator(rescale=1./255)
train_generator=train_datagen.flow_from_directory(train_dir,target_size=(150,150),batch_size=20,class_mode='binary')
validation_generator=test_dategen.flow_from_directory(validation_dir,target_size=(150,150),batch_size=20,class_mode='binary')
test_generator=test_dategen.flow_from_directory(test_dir,target_size=(150,150),batch_size=20,class_mode='binary')
conv_base.trainable=False
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),loss='binary_crossentropy',metrics=['acc'])
model.fit_generator(train_generator,epochs=30,steps_per_epoch=100,validation_data=validation_generator,validation_steps=50,verbose=2)
#解冻
conv_base.trainable=True
set_trainable=False
#仅解冻最上3个卷积层
for layer in conv_base.layers:
	if layer.name=='block5_conv1':
		set_trainable=True
	if set_trainable:
		layer.trainable=True
	else:
		layer.trainable=False
#重新编译
model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-5),metrics=['acc'])
history=model.fit_generator(train_generator,steps_per_epoch=100,epochs=100,validation_data=validation_generator,validation_steps=50,verbose=2)
json_string=model.to_json()
open('D:/tmp/DeepLearningWithPython/cat_dog_4.json','w').write(json_string)
model.save_weights('D:/tmp/DeepLearningWithPython/cat_dog_4.h5')


h=history.history
acc=h['acc']
val_acc=h['val_acc']
loss=h['loss']
val_loss=h['val_loss']
epochs=range(1,len(acc)+1)
plt.plot(epochs,acc,'ro',label='train_acc')
plt.plot(epochs,val_acc,'b^',label='val_acc')
plt.title('accurancy')
plt.legend()
#如果是show,这个显示完了才能显示下一个图,figure可以同时显示多个,最后使用show
plt.figure()

plt.plot(epochs,loss,'ro',label='train_loss')
plt.plot(epochs,val_loss,'b^',label='val_loss')
plt.title('loss')
plt.legend()
plt.show()

plt.clf()
#曲线不好看,进行平滑处理
def smooth_curve(points, factor=0.8):
  smoothed_points = []
  for point in points:
    if smoothed_points:
      previous = smoothed_points[-1]
      smoothed_points.append(previous * factor + point * (1 - factor))
    else:
      smoothed_points.append(point)
  return smoothed_points
plt.plot(epochs,smooth_curve(acc),'ro',label='train_acc')
plt.plot(epochs,smooth_curve(val_acc),'b^',label='val_acc')
plt.title('accurancy')
plt.legend()
#如果是show,这个显示完了才能显示下一个图,figure可以同时显示多个,最后使用show
plt.figure()

plt.plot(epochs,smooth_curve(loss),'ro',label='train_loss')
plt.plot(epochs,smooth_curve(val_loss),'b^',label='val_loss')
plt.title('loss')
plt.legend()
plt.show()
result=model.evaluate_generator(test_generator,steps=50)
print(result)

总结:图像识别中卷积层是至关重要的,从底部到顶端,卷积层提取的信息越来越抽象,所以其卷积核也越来越大。数据扩张是小样本集训练的重要技术手段,它将一个原始图像通过拉伸、旋转、缩放、裁剪、翻转的操作生成新的图片,扩展了数据集。dropout也是解决过拟合的好手段。一般而言,自己训练的模型不如网上下载的预训练模型,因为预训练模型都是在庞大的数据集上进行训练的,其模型提取的特征更具普遍性,在其上训练自己的小样本集效果将更好。