接下来使用迁移学习的思想,以VGG16作为模板搭建模型,训练识别手写字体。
VGG16模型是基于K.Simonyan和A.Zisserman写的Very Deep Convolutional Networks for Large-Scale Image Recognition,arXiv:1409.1556。
from keras.applications.vgg16 import VGG16 # VGG16模型
from keras.layers import Input, Flatten, Dense, Dropout # 搭建卷积神经网络需要的模块,分别为全展开方法,输出层方法,随机失活方法
from keras.models import Model 加载模型类
from keras.optimizers import SGD # 加载SGD优化函数
from keras.datasets import mnist # 加载数据集
import cv2 # 加载opencv,用于后期对图像的处理,比如尺寸变换和Channel变换。这些变换是为了使图像满足VGG16所需要的输入格式。
import h5py as h5py # 数据储存和导出类
import numpy as np # 必要的东西!
1、创建不包含权重的VGG_16迁移网络
model_vgg = VGG16(include_top=False, weights='imagenet', input_shape=(224, 224, 3))
model = Flatten(name = 'flatten')(model_vgg.output)
model = Dense(10, activation = "softmax")(model)
model_vgg_mnist = Model(model_vgg.input, model, name='vgg16')
VGG_16()中,include_top=False表示去除VGG16顶层(输出层),weights=’imagenet’表示使用图片网络的权重,input_shape=(224, 224, 3)表示输入的图片维度为224x224,图层为3(RBG)
model = Flatten(name = ‘flatten’)(model_vgg.output)表示在model_vgg模型的输出层再加上一层flatten(全展开)层,并把这层命名为flatten,把合起来的模型返回给model
model = Dense(10, activation = “softmax”)(model)表示在model上再加一层输出层,分类数为10个,激活函数使用softmax
model_vgg_mnist = Model(model_vgg.input, model, name=’vgg16’)表示创建一个新的模型,命名为vgg16,在keras的开发文档中查到Model(input=X, output=Y)
推测是以model_vgg的input层作为输入层,model作为输出层来构建新的网络
在这里可以看到,所有1496万个网络权重(VGG16网络权重加上我们搭建的权重)都需要训练,这是因为我们迁移了网络结构,但是没有迁移VGG16网络权重。迁移网络权重的好处在于网络权重不用重新训练,只需要训练最上层搭建的部分就行了。坏处是,新的数据不一定适用已训练好的权重,因为已训练好的权重是基于其他数据训练的,数据分布和我们关心的问题可能完全不一样。这里虽然引进了VGG在ImageNet中的结构,但是具体模型仍需要在VGG16的框架上加工。
另外,本地机器很有可能不能把整个模型和数据放入内存进行训练,出现Kill:9的内存不够的错误。如果想要训练,则建议用较少样本,或者把样本批量减小至比如32。有条件的话可以用AWS里的EC2 GPU Instance g2.2xlarge/g2.8xlarge进行训练。
作为对比,我们建立另外一个模型,这个模型的特点是把VGG16网络的结构和权重同时迁移。这里的关键点是把不需要重新训练的权重“冷冻”起来。这里使用trainable=false这个选项。注意,这里我们定义输入的维度为(224,224,3),因此需要较大的内存,除非读者使用数据生成器迭代对象。如果内存较小,那么可以将维度降为(112,112,3),这样在32GB内存的机器上也能顺利运行。
2、创建包含权重的迁移网络
ishape=224
model_vgg = VGG16(include_top=False, weights='imagenet', input_shape=(ishape, ishape, 3))
for layer in model_vgg.layers:
layer.trainable = False
model = Flatten()(model_vgg.output)
model = Dense(10, activation="softmax")(model)
model_vgg_mnist_pretrain = Model(model_vgg.input, model, name='vgg_16_pretrain')
model_vgg_mnist_pretrain.summary()
运行结果如下:
·····
block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808
_________________________________________________________________
block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808
_________________________________________________________________
block5_pool (MaxPooling2D) (None, 7, 7, 512) 0
_________________________________________________________________
flatten_1 (Flatten) (None, 25088) 0
_________________________________________________________________
dense_2 (Dense) (None, 10) 250890
=================================================================
Total params: 14,965,578
Trainable params: 250,890
Non-trainable params: 14,714,688
_________________________________________________________________
我们只需要训练25万个参数,比之前整整少了60倍!
(1)输入损失函数
sgd = SGD(lr = 0.05, decay = 1e-5)
model_vgg_mnist_pretrain.compile(loss='categorical_crossentropy', optimizer=sgd, metrics = ['accuracy'])
(2)数据预处理
因为VGG16网络对输入层的要求,我们用OpenCV把图像从32×32变成224×224(cv2.resize的命令),把黑白图像转换为RGB图像(cv2.COLOR_GRAY2BGR),并且把训练数据转化成张量形式,供Keras输入。
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
X_train = [cv2.cvtColor(cv2.resize(i, (ishape, ishape)), cv2.COLOR_GRAY2BGR) for i in X_train[:20000]]
X_train = np.concatenate([arr[np.newaxis] for arr in X_train]).astype("float32")
X_test = [cv2.cvtColor(cv2.resize(i, (ishape, ishape)), cv2.COLOR_GRAY2BGR) for i in X_test[:2000]]
X_test = np.concatenate([arr[np.newaxis] for arr in X_test]).astype("float32")
print("训练集的张量是",X_train.shape)
print("测试集的张量是",X_test.shape)
训练集原本有6w,但是因为笔者内存不足将测试集数量修改为10000,测试集数据量修改为1000
X_train /= 255
X_test /= 255
np.where(X_train[0] != 0)
def tran_y(y):
y_ohe = np.zeros(10)
y_ohe[y] = 1
return y_ohe
Y_train_ohe = np.array([tran_y(Y_train[i]) for i in range(len(Y_train[:20000]))])
Y_test_ohe = np.array([tran_y(Y_test[i]) for i in range(len(Y_test[:2000]))])
运行模型
model_vgg_mnist_pretrain.fit(X_train, Y_train_ohe, validation_data = (X_test, Y_test_ohe), epochs = 200, batch_size = 128
笔者内存不足!等我以后再来补充!