1. 问题描述
在mnist数据集上,用0~4的图片训练得到一个模型,用它来进行迁移学习———根据这个模型和少量的训练数据,得到一个可以预测5~9的数字图片的模型。(在0~4上训练得到原模型时,训练集的规模可能是每类有大约6000张图片,在5~9上做迁移学习的时候,每一类训练集的规模小得多,例如100张)
不过值得一提的是,这个小例子里用迁移学习,效果明显不如普通训练方法(训练数据量相同的情况下),这可能也是个反面案例,告诉我们迁移学习也不总是效果显著。
2. TF2做迁移学习的关键技术点
2.1 导入模型
TF2比TF1在导入模型上方便的多,TF1还要先导入计算图,再导入参数,TF2只需要导入一个h5模型文件就搞定了。
下面这个模型是事先训练好的模型,在大约6000张0~4测试集上的accuracy是99.14% 。
best_model=tf.keras.models.load_model("chapter11/q8/models/chapter11_q8_bestmodel.h5")
best_model 是五个隐层,每层140个神经元。TF2在导入公开的知名的模型的时候(例如MobileNetV2),可以通过参数 include_top是True or False 来决定是否导入输出层。
2.2 复用、冻结、替换某些层
2.1中导入的模型best_model,可以很方便的对它及它的各层进行操作:
#返回每个隐层的对象,是个list
best_model.layers
#将第一层参数设置为不可变
best_model.layers[0].trainable=False
####复用和替换某些层
#复用原模型的前若干层
l_layer=len(best_model.layers)
new_model=Sequential(best_model.layers[0:(l_layer-1)])
#构造新的输出层
new_output=Dense(5,activation=tf.nn.softmax,kernel_initializer=tf.initializers.Constant(0.01))
#用原模型的前若干层和新的输出层合成新模型
new_model.add(new_output)
3.例子完整代码
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.datasets import mnist
import numpy as np
import time
first_K0=100 ##迁移学习的时候用first_K0张5~9的图片进行训练
def find_index(array,List=[],first_K=100): ##从原mnist数据集中选取标签为5~9的图片的坐标
index=[]
for i in List:
if first_K==-1:
index.extend(list(np.where(array==i)[0]))
else:
index.extend(list(np.where(array==i)[0])[0:first_K])
return(index)
####数据准备和处理
(train_x,train_y),(test_x,test_y)=mnist.load_data()
train_x=train_x.reshape([-1,28*28])/255.0
test_x=test_x.reshape([-1,28*28])/255.0
index_train=find_index(train_y,[5,6,7,8,9],first_K=first_K0) ##每种图片用first_K0张
index_test=find_index(test_y,[5,6,7,8,9],first_K=-1) ##测试图片全用
train_x,train_y=train_x[index_train,:],train_y[index_train]-5
test_x,test_y=test_x[index_test,:],test_y[index_test]-5
####导入模型
best_model=tf.keras.models.load_model("chapter11/q8/models/chapter11_q8_bestmodel.h5")
####更改模型输出层,冻结其他层
l_layer=len(best_model.layers)
new_model=Sequential(best_model.layers[0:(l_layer-1)])
new_output=Dense(5,activation=tf.nn.softmax,kernel_initializer=tf.initializers.Constant(0.01))
new_model.add(new_output)
####控制哪些层重新训练
for i in range(l_layer-1):
new_model.layers[i].trainable=False
####compile模型
new_model.compile(optimizor="adam",
loss=tf.losses.SparseCategoricalCrossentropy(),
metric=['accuracy'])
####训练模型,输出相关信息
time0=time.time()
new_model.fit(train_x,train_y,verbose=2,epochs=5)
time1=time.time()
print("first_K = %d"%first_K0)
print("cost time = %.4f second"%(time1-time0))
print(np.mean(new_model.predict_classes(test_x)==test_y))
实验结果显示,只修改输出层,100张图片,epoch=5,准确率大概80%左右。逐步减少冻结的层数,同样是100张图片,epoch=5的情况下,训练效果会越来越好。这说明这个例子里用迁移学习效果并不好。