使用在ImageNet数据上预训练的VGG16网络模型对猫狗数据集进行分类识别。html
预训练网络是一个保存好的,已经在大型数据集上训练好的卷积神经网络。网络
若是这个数据集足够大且通用,那么预训练网络学习到的模型参数能够有效的对图片进行特征提取。即便新问题与本来的数据彻底不一样,但学习到的特征提取方法依然能够在不一样的问题之间进行移植,进而能够在全新的数据集上提取到有效的特征。对这些有效的高级特征进行分类能够大大提升模型分类的准确率。app
迁移学习主要适用于已有数据相对较少的状况,若是拥有的数据量足够大,即便不须要迁移学习也可以获得很是高的准确率。dom
首先,读入猫狗数据集中的图片。(实现过程的详细说明在Tensorflow学习笔记No.5中,这里再也不赘述)学习
1 import tensorflow as tf 2 import numpy as np 3 import pandas as pd 4 import matplotlib.pyplot as plt 5 %matplotlib inline 6 import pathlib 7 import random 8 9 data_root = pathlib.Path('../input/cat-and-dog/training_set/training_set') 10 11 all_image_path = list(data_root.glob('*/*.jpg')) 12 random.shuffle(all_image_path) 13 image_count = len(all_image_path) 14 15 label_name = sorted([item.name for item in data_root.glob('*')]) 16 name_to_indx = dict((name, indx) for indx, name in enumerate(label_name)) 17 18 all_image_path = [str(path) for path in all_image_path] 19 all_image_label = [name_to_indx[pathlib.Path(p).parent.name] for p in all_image_path] 20 21 def load_pregrosess_image(path, label): 22 image = tf.io.read_file(path) 23 image = tf.image.decode_jpeg(image, channels = 3) 24 image = tf.image.resize(image, [256, 256]) 25 image = tf.cast(image, tf.float32) 26 image = image / 255 27 return image, label 28 29 train_image_ds = tf.data.Dataset.from_tensor_slices((all_image_path, all_image_label)) 30 31 AUTOTUNE = tf.data.experimental.AUTOTUNE 32 dataset = train_image_ds.map(load_pregrosess_image, num_parallel_calls = AUTOTUNE) 33 34 BATCHSIZE = 16 35 train_count = int(image_count * 0.8) 36 test_count = image_count - train_count 37 38 train_dataset = dataset.take(train_count) 39 test_dataset = dataset.skip(train_count) 40 41 train_dataset = train_dataset.shuffle(train_count).repeat().batch(BATCHSIZE) 42 test_dataset = test_dataset.repeat().batch(BATCHSIZE)
与训练的网络由两个部分构成,训练好的卷积基和训练好的分类器。咱们须要使用训练好的卷积基来提取特征,并使用自定义的分类器对本身的数据集进行分类识别。测试
以下图所示:spa
训练过程当中,咱们仅仅对自定义的分类器进行训练,而不训练预训练好的卷积基部分。code
预训练的卷积基能够很是好的提取图像的某些特征,在训练过程当中,因为分类器是一个全新的没有训练过的分类器,在训练初期会产生很大的loss值,因为数据量较少,若是不对预训练的卷积基进行冻结(不更新参数)处理,产生的loss值经梯度传递会对预训练的卷积基形成很是大的影响,且因为可训练数据较少儿难以恢复,因此只对自定义的分类器进行训练,而不训练卷积基。htm
首先从tf.keras.applications中建立一个预训练VGG16的卷积基。blog
1 cov_base = tf.keras.applications.VGG16(weights = 'imagenet', include_top = False)
weight是咱们要使用的模型权重,咱们使用经imagenet训练过的模型的权重信息进行迁移学习。
include_top是指,是否使用预训练的分类器。在迁移学习过程当中咱们使用自定义的分类器,因此参数为False。
而后咱们对建立好的卷积基进行冻结处理,冻结全部的可训练参数。
1 cov_base.trainable = False
使用keras.Sequential()建立网络模型。
1 model = tf.keras.Sequential() 2 model.add(cov_base) 3 model.add(tf.keras.layers.GlobalAveragePooling2D()) 4 model.add(tf.keras.layers.Dense(512, activation = 'relu')) 5 model.add(tf.keras.layers.Dense(1, activation = 'sigmoid'))
在模型中加入卷积基和自定义的分类器。
模型结构以下图所示:
咱们获得了一个可训练参数仅为263,169的预训练VGG16网络模型。
此时模型已经搭建完毕,咱们使用以前处理好的数据对它进行训练。
1 model.compile(optimizer = 'adam', 2 loss = 'binary_crossentropy', 3 metrics = ['acc'] 4 ) 5 6 history = model.fit(train_dataset, 7 steps_per_epoch = train_count // BATCHSIZE, 8 epochs = 10, 9 validation_data = test_dataset, 10 validation_steps = test_count // BATCHSIZE 11 ) 12 13 plt.plot(history.epoch, history.history.get('acc'), label = 'acc') 14 plt.plot(history.epoch, history.history.get('val_acc'), label = 'acc')
训练结果以下图所示:
模型在训练集和测试机上的正确率均达到了94%左右,并且仅仅通过了10个epoch就达到了这样的效果,足以看出迁移学习在小规模数据上的优点。
虽然使用预训练网络能够轻易的达到94%左右的正确率,可是,若是咱们还想继续提升这个正确率该怎样进行调整呢?
所谓微调,是冻结卷积基底部的卷积层,共同训练新添加的分类器和卷积基顶部的部分卷积层。
根据卷积神经网络提取特征的原理咱们不难发现,越底层的卷积层提取到的图像特征越抽象越细小,而顶层的卷积层提取到的特征更大,更加的接近咱们能直接观察到的数据特征,因为咱们须要训练的数据和预训练时使用的数据不尽相同,因此越顶层的卷积层提取到的特征与咱们所须要的特征差异越大。因此,咱们只冻结底部的卷积层,将顶部的卷积层与训练好的分类器共同训练,会获得更好的拟合效果。
只有分类器以及训练好了,才能微调卷积基的顶部卷积层,不然因为训练初期的偏差很大,会将卷积层以前学习到的参数破坏掉。
因此咱们对卷积基进行解冻,并只对底部的卷积进行冻结。
1 cov_base.trainable = True 2 for layers in cov_base.layers[:-3]: 3 layers.trainable = False
而后将模型继续进行训练。
1 model.compile(optimizer = tf.keras.optimizers.Adam(lr = 0.0001), 2 loss = 'binary_crossentropy', 3 metrics = ['acc'] 4 ) 5 6 history = model.fit(train_dataset, 7 steps_per_epoch = train_count // BATCHSIZE, 8 epochs = 20, 9 initial_epoch = 10, 10 validation_data = test_dataset, 11 validation_steps = test_count // BATCHSIZE 12 ) 13 14 plt.plot(history.epoch, history.history.get('acc'), label = 'acc') 15 plt.plot(history.epoch, history.history.get('val_acc'), label = 'acc')
注意将学习率调小,以便尽量的达到loss的极小值点。
获得的结果以下图所示:
模型再训练集上达到了近乎100%的准确率,在测试集上也达到了96%左右准确率,微调的效果仍是较为明显的。
那么关于迁移学习的介绍到这里就结束了o(* ̄▽ ̄*)o,后续会更新更多内容。