查看原文
其他

深度学习第21讲:迁移学习的基本原理和实践

louwill Python爱好者社区 2019-04-07

作者:鲁伟
一个数据科学践行者的学习日记。数据挖掘与机器学习,R与Python,理论与实践并行。
个人公众号:机器学习实验室 (微信ID:louwill12)


前文传送门:

深度学习笔记1:利用numpy从零搭建一个神经网络

深度学习笔记2:手写一个单隐层的神经网络

深度学习笔记3:手动搭建深度神经网络(DNN)

深度学习笔记4:深度神经网络的正则化

深度学习笔记5:正则化与dropout

深度学习笔记6:神经网络优化算法之从SGD到Adam

深度学习笔记7:Tensorflow入门

深度学习笔记8:利用Tensorflow搭建神经网络

深度学习笔记9:卷积神经网络(CNN)入门

深度学习笔记10:三维卷积、池化与全连接

深度学习笔记11:利用numpy搭建一个卷积神经网络

深度学习笔记12:卷积神经网络的Tensorflow实现

深度学习笔记13:Tensorflow实战之手写mnist手写数字识别

深度学习笔记14:CNN经典论文研读之Le-Net5及其Tensorflow实现

深度学习笔记15:ubuntu16.04 下深度学习开发环境搭建与配置

深度学习笔记16:CNN经典论文研读之AlexNet及其Tensorflow实现

深度学习第17讲:keras入门和快速上手指南

深度学习第18讲:CNN经典论文研读之VGG网络及其tensorflow实现

深度学习第19讲:CNN经典论文研读之残差网络ResNet及其keras实现

深度学习第20讲:CNN经典论文研读之GoogLeNet Inception网络


       作为一门实验性学科,深度学习通常需要反复的实验和结果论证。在现在和将来,是否有海量的数据资源和强大的计算资源,这是决定学界和业界深度学习和人工智能发展的关键因素。通常情况下,获取海量的数据资源对于企业而言并非易事,尤其是对于像医疗等特定领域,要想做一个基于深度学习的医学影像的自动化诊断系统,大量且高质量的打标数据非常关键。但通常而言,莫说高质量,就是想获取大量的影像数据就已困难重重。

       那怎么办呢?是不是获取不了海量的数据研究就一定进行不下去了呢?当然不是。因为我们有迁移学习。那究竟什么是迁移学习呢?顾名思义,迁移学习就是利用数据、任务或模型之间的相似性,将在旧的领域学习过或训练好的模型,应用于新的领域这样的一个过程。从这段定义里面,我们可以窥见迁移学习的关键点所在,即新的任务与旧的任务在数据、任务和模型之间的相似性。因而这也引出了迁移的应用场景。

迁移学习的使用场景

       迁移学习到底在什么情况下使用呢?是不是我模型训练不好就可以用迁移学习进行改进呢?当然不是。正如前文所言,使用迁移学习的主要原因在于数据资源的可获得性和训练任务的成本。当我们有海量的数据资源时,自然不需要迁移学习,机器学习系统很容易从海量数据中学习到一个鲁棒性很强的模型。但通常情况下,我们需要研究的领域可获得的数据极为有限,仅靠有限的数据量进行学习,所习得的模型必然是不稳健、效果差的,通常情况下很容易造成过拟合,在少量的训练样本上精度极高,但是泛化效果极差。另一个原因在于训练成本,即所依赖的计算资源和耗费的训练时间。通常情况下,很少有人从头开始训练一整个深度卷积网络,一个是上面提到的数据量的问题,另一个就是时间成本和计算资源的问题,从头开始训练一个卷积网络通常需要较长时间且依赖于强大的 GPU 计算资源,对于一门实验性极强的领域而言,花费好几天乃至一周的时间去训练一个自己心里都没谱的深度神经网络通常是不能忍受的。

       所以,迁移学习的使用场景如下:假设有两个任务系统 A 和 B,任务 A 拥有海量的数据资源且已训练好,但并不是我们的目标任务,任务 B 是我们的目标任务,但数据量少且极为珍贵,这种场景便是典型的迁移学习的应用场景。那究竟什么时候使用迁移学习是有效的呢?对此笔者不敢武断地下结论。但必须如前文所言,新的任务系统和旧的任务系统必须在数据、任务和模型等方面存在一定的相似性,你将一个训练好的语音识别系统迁移到放射科的图像识别系统上,恐怕结果不会太妙。所以,要判断一个迁移学习应用是否有效,最基本的原则还是要遵守,即任务 A 和任务 B 在输入上有一定的相似性,即两个任务的输入属于同一性质,要么同是图像、要么同是语音或其他,这便是前文所说到的任务系统的相似性的含义之一。

深度卷积网络的可迁移性

       还有一个值得探讨的问题在于,深度卷积网络的可迁移性在于什么呢?为什么说两个任务具有同等性质的输入旧具备可迁移性?一切都还得从卷积神经网络的基本原理说起。由之前的学习我们知道,卷积神经网络具备良好的层次结构,通常而言,普通的卷积神经网络都具备卷积-池化-卷积-池化-全连接这样的层次结构,在深度可观时,卷积神经网络可以提取图像各个 level 的特征。当我们要从图像中识别一张人脸的时候,通常在一开始我们会检测到图像的横的、竖的等边缘特征,然后会检测到脸部的一些曲线特征,再进一步会检测到脸部的鼻子、眼睛和嘴巴等具备明显识别要素的特征等等。

       这便揭示了深度卷积网络可迁移性的基本原理和卷积网络训练过程的基本事实。具备良好层次的深度卷积网络通常都是在最初的前几层学习到图像的通用特征(general feature),但随着网络层次的加深,卷积网络便逐渐开始检测到图像的特定的特征,两个任务系统的输入越相近,深度卷积网络检测到的通用特征越多,迁移学习的效果越好。所以,这也引出了笔者最后一个问题,怎样在实际操作中使用迁移学习?

迁移学习的使用方法

       通常而言,迁移学习有两种使用套路。第一种便是常说的 finetune,即微调,简单而言就是将别人训练好的网络拿来进行简单修改用于自己的学习任务。在实际操作中,通常用预训练的网络权值对自己网络的权值进行初始化,以代替原先的随机初始化。第二种称为 fixed feature extractor,即将预训练的网络作为新任务的特征提取器,在实际操作中通常将网络的前几层进行冻结,只训练最后的全连接层,这时候预训练网络便是一个特征提取器。

       keras 为我们提供了经典网络在 ImageNet 上为我们训练好的预训练模型,预训练模型的基本信息如下表所示:

       笔者以 VGG16 网络预训练为例对手写数字数据集 mnist 进行迁移学习任务,试验代码如下:

from keras.models import Model
from keras.layers import Dense, Flatten, Dropout
from keras import datasets
from keras.applications.vgg16 import VGG16
from keras.optimizers import SGD
from keras.datasets import mnist
import numpy as npimport cv2
# 查看 VGG16 预训练模型的基本信息
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(inputs=model_vgg.input, outputs=model, name='vgg16') model_vgg_mnist.summary()

       冻结预训练模型的卷积和池化层,仅修改全连接层:

model_vgg = VGG16(include_top=False, weights='imagenet', input_shape=(224, 224, 3))
for layers in model_vgg.layers:    layers.trainable = Falsemodel = Flatten()(model_vgg.output) model = Dense(10, activation='softmax')(model) model_vgg_mnist_pretrain = Model(inputs=model_vgg.input, outputs=model, name='vgg16_pretrain') sgd = SGD(lr=0.05, decay=1e-5) model_vgg_mnist_pretrain.compile(optimizer=sgd, loss='categorical_crossentropy',                                 metrics=['accuracy'])

       然后转换 mnist 训练数据的输入大小以适应 VGG16 的输入:

X_train = [cv2.cvtColor(cv2.resize(i, (img, img)), cv2.COLOR_GRAY2BGR) for i in X_train] X_train = np.concatenate([arr[np.newaxis] for arr in X_train]).astype('float32') X_test  = [cv2.cvtColor(cv2.resize(i, (img, img)), cv2.COLOR_GRAY2BGR) for i in X_test ] X_test  = np.concatenate([arr[np.newaxis] for arr in X_test] ).astype('float32')

       稍加处理后进行训练:

X_train /= X_train/255X_test /= X_test/255
np.where(X_train[0] != 0)

def train_y(y):    y_one = np.zeros(10)    y_one[y] = 1    return y_one y_train_one = np.array([train_y(y_train[i]) for i in range(len(y_train))]) y_test_one  = np.array([train_y(y_test [i]) for i in range(len(y_test ))]) model_vgg_mnist_pretrain.fit(X_train, y_train_one, validation_data=(X_test, y_test_one),                             epochs=10, batch_size=128)

       限于笔者 windows 单机的计算能力,笔者仅进行了10轮的简单训练作为示例。当训练次数足够时,基于 VGG16 预训练网络在 mnist 数据集上的迁移学习效果会不错的。

参考资料:

https://www.deeplearning.ai/

王晋东 迁移学习手册

Yosinski J, Clune J, Bengio Y, et al. How transferable are features in deep neural networks?[J]. Eprint Arxiv, 2014, 27:3320-3328.


作者最新课程,限时优惠中

扫码点击阅读原文了解详情&报名

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存