大家好,又见面了,我是你们的朋友全栈君。
在有监督的机器学习和尤其是深度学习的场景应用中,需要大量的标注数据。标注数据是一项枯燥无味且花费巨大的任务,关键是现实场景中,往往无法标注足够的数据。而且模型的训练是极其耗时的。因此迁移学习营运而生。 传统机器学习(主要指监督学习)
迁移学习中有两个重要概念:
根据迁移学习的方法,大体可以将迁移学习分为 基于实例(样本)的迁移学习、基于特征的迁移学习、基于参数(模型)的迁移学习和基于关系的迁移学习
即源领域和目标领域具有很多交叠的特征,源领域和目标领域具有相同或相近的支撑集。(此处支撑可以理解为对任务目标有用的信息) 比如,利用评论分析客户情感的任务中,电子设备的评论和DVD的评论两种评论属于不同领域(Domain),虽然两种数据属于不同的域,但是可能存在一些电子设备的评论适用于DVD评论的情感分类任务。 我们的目标就是从源域的训练数据中找出那些适合目标域的实例,并将这些实例迁移到训练数据的学习中去。典型的算法是Tradaboosting,算法的关键想法是,利用boosting的技术来过滤掉源域数据中那些与目标域训练数据最不像的数据。其中,boosting的作用是建立一种自动调整权重的机制,于是重要的源域练数据的权重将会增加,不重要的源域训练数据的 权重将会减小。调整权重之后,这些带权重的源域训练数据将会作为额外的训练数据,与目标域训练数据一起从来提高分类模型的精度和可靠度。
针对source domain的大量数据进行训练的过程中,网络的前面几层可以看作特征抽取器。该特征抽取器抽取两个domain的特征,然后输入对抗网络;对抗网络尝试对特征进行区分。如果对抗网络对特征较难区分,则意味着两个domain的特征区分性较小、具有很好的迁移性,反之亦然。 最近几年,由于其良好的性能和实用性,基于对抗学习的深度迁移学习方法被广泛的研究。
通常的迁移学习可以分为两步完成:“预训练”和“微调”
我们的任务是对猫狗图片进行识别。
参考已有的成熟方案,此处我们采取Inception_v3结构网络。Inception_v3的具体结构如下:
可以看到,Inception_v3的结构是遍比较复杂的。使用个人PC对其训练是不现实的,因此此处采取基于参数的迁移学习方式对前几层的网络结构和参数进行复用,再利用本地20000张猫和狗的图片进行最后一层的训练。完成猫狗分类的任务。具体代码和解读如下: 引入必要的package
# -*- coding: utf-8 -*-
import os
import numpy as np
from keras.utils import plot_model
from keras.applications.inception_v3 import InceptionV3
from keras.layers import Dense,Flatten,GlobalAveragePooling2D
from keras.models import Model,load_model
from keras.optimizers import SGD
from keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
由于CNN本身并不具有识别图片缩放和图片旋转的能力(CNN+pooling具有识别图片平移的能力)。因此需要对样本数据进行数据增强
class PowerTransferMode:
#数据准备
def DataGen(self, dir_path, img_row, img_col, batch_size, is_train):
""" data enhancement Args: dir_path:dir path of data img_row: image width img_col: image height batch_size: bitch_size of trian is_train: boolean return: data enhancement result """
#数据增强可以增加训练数据,包括平移,旋转,镜像,加入噪声等
if is_train: #如果是训练集,进行数据增强
datagen = ImageDataGenerator(rescale=1./255,
zoom_range=0.25, rotation_range=15.,
channel_shift_range=25., width_shift_range=0.02, height_shift_range=0.02,
horizontal_flip=True, fill_mode='constant')
else: #如果是测试集,输出规范化的原始图片
datagen = ImageDataGenerator(rescale=1./255) #若不是训练集,则直接输出
generator = datagen.flow_from_directory(
dir_path, target_size=(img_row, img_col),
batch_size=batch_size,
#class_mode='binary',
shuffle=is_train)
return generator
接下来可以引入别人已经训练好的模型参数
def InceptionV3_model(self, lr=0.005, decay=1e-6, momentum=0.9, nb_classes=2, img_rows=197, img_cols=197, RGB=True,
is_plot_model=False):
""" InceptionV3_model Args: ... return: InceptionV3_model """
color = 3 if RGB else 1
base_model = InceptionV3(weights='imagenet', include_top=False, pooling=None,
input_shape=(img_rows, img_cols, color), classes=nb_classes) #引入InceptionV3_model模型和权重
# 迁移学习,复用冻结base_model所有层,直接复用其参数
for layer in base_model.layers:
layer.trainable = False
复用前面的结构和参数后,后面可以接上自己定义的结构和损失函数函数,来适用于自己的任务。
x = base_model.output
# 添加自己的全链接分类层
x = GlobalAveragePooling2D()(x) #添加average pooling层
x = Dense(1024, activation='relu')(x) #全连接层
predictions = Dense(nb_classes, activation='softmax')(x) #softmax输出预测结果
# 训练模型
model = Model(inputs=base_model.input, outputs=predictions)
sgd = SGD(lr=lr, decay=decay, momentum=momentum, nesterov=True) #优化器
model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])
return model
#训练模型
def train_model(self, model, epochs, train_generator, steps_per_epoch, validation_generator,
validation_steps, model_url, is_load_model=False):
""" train model Args: ... return: model trained """
if is_load_model and os.path.exists(model_url):
model = load_model(model_url) #载入模型
history_ft = model.fit_generator(
train_generator,
steps_per_epoch=steps_per_epoch,
epochs=epochs,
validation_data=validation_generator,
validation_steps=validation_steps) #模型训练
# 模型保存
model.save(model_url,overwrite=True) #模型保存
return history_ft
完整的训练代码如下:
# -*- coding: utf-8 -*-
import os
import numpy as np
from keras.utils import plot_model
from keras.applications.inception_v3 import InceptionV3
from keras.layers import Dense,Flatten,GlobalAveragePooling2D
from keras.models import Model,load_model
from keras.optimizers import SGD
from keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
class PowerTransferMode:
#数据准备
def DataGen(self, dir_path, img_row, img_col, batch_size, is_train):
""" data enhancement Args: dir_path:dir path of data img_row: image width img_col: image height batch_size: bitch_size of trian is_train: boolean return: data enhancement result """
#数据增强可以增加训练数据,包括平移,旋转,镜像,加入噪声等
if is_train:
datagen = ImageDataGenerator(rescale=1./255,
zoom_range=0.25, rotation_range=15.,
channel_shift_range=25., width_shift_range=0.02, height_shift_range=0.02,
horizontal_flip=True, fill_mode='constant')
else:
datagen = ImageDataGenerator(rescale=1./255) #若不是训练集,则直接输出
generator = datagen.flow_from_directory(
dir_path, target_size=(img_row, img_col),
batch_size=batch_size,
#class_mode='binary',
shuffle=is_train)
return generator
# InceptionV3模型
def InceptionV3_model(self, lr=0.005, decay=1e-6, momentum=0.9, nb_classes=2, img_rows=197, img_cols=197, RGB=True,
is_plot_model=False):
""" InceptionV3_model Args: ... return: InceptionV3_model """
color = 3 if RGB else 1
base_model = InceptionV3(weights='imagenet', include_top=False, pooling=None,
input_shape=(img_rows, img_cols, color),
classes=nb_classes) #迁移学习,InceptionV3_model模型复用
# 冻结base_model所有层,这样就可以正确获得bottleneck特征
for layer in base_model.layers:
layer.trainable = False
x = base_model.output
# 添加自己的全链接分类层
x = GlobalAveragePooling2D()(x) #添加average pooling层
x = Dense(1024, activation='relu')(x) #全连接层
predictions = Dense(nb_classes, activation='softmax')(x) #softmax输出预测结果
# 训练模型
model = Model(inputs=base_model.input, outputs=predictions)
sgd = SGD(lr=lr, decay=decay, momentum=momentum, nesterov=True) #优化器
model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])
return model
#训练模型
def train_model(self, model, epochs, train_generator, steps_per_epoch, validation_generator,
validation_steps, model_url, is_load_model=False):
""" train model Args: ... return: model trained """
if is_load_model and os.path.exists(model_url):
model = load_model(model_url) #载入模型
history_ft = model.fit_generator(
train_generator,
steps_per_epoch=steps_per_epoch,
epochs=epochs,
validation_data=validation_generator,
validation_steps=validation_steps) #模型训练
# 模型保存
model.save(model_url,overwrite=True) #模型保存
return history_ft
image_size = 197
batch_size = 32
transfer = PowerTransferMode()
train_generator = transfer.DataGen('D:/data/cat_vs_dog/train_set/', image_size, image_size, batch_size, True)
validation_generator = transfer.DataGen('D:/data/cat_vs_dog/validation_set/', image_size, image_size, batch_size, False)
model = transfer.InceptionV3_model(nb_classes=2, img_rows=image_size, img_cols=image_size, is_plot_model=False)
history_ft = transfer.train_model(model, 10, train_generator, 600, validation_generator, 60, 'D:/data/cat_vs_dog/inceptionv3_nbs.model', is_load_model=False)
利用训练好的模型对测试集进行预测:
test_model = load_model('D:/data/cat_vs_dog/inceptionv3_nbs.model')
image_size = 197
batch_size = 32
transfer = PowerTransferMode()
test_datagen = ImageDataGenerator(rescale=1./255)
test_generator = test_datagen.flow_from_directory('D:/data/cat_vs_dog/test_set/', target_size=(image_size, image_size),
batch_size=1,class_mode='binary',shuffle=False)
test_generator.reset()
pred = test_model.predict_generator(test_generator,steps=10, verbose=0)
predicted_list = np.argmax(pred, axis=1)
predicted_class = ["狗" if i==1 else "猫" for i in predicted_list]
具体的预测结果如下:
可见预测的分类准确率还是比较高的。
参考文献 迁移学习概述 迁移学习系列—基于实例方法的迁移学习 薛贵荣:迁移学习( Transfer Learning ) 综述:迁移学习发展现状及未来趋势
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/128443.html原文链接:https://javaforall.cn