Fine-turning(Tensorflow-Slim和Keras的迁移学习)
遷移學(xué)習(xí)是什么?
即:舉一反三。即將已經(jīng)訓(xùn)練好的模型稍加調(diào)整(fine-turning)即可應(yīng)用于一個新的領(lǐng)域或者任務(wù)。主要流程如上圖。
遷移學(xué)習(xí)為什么重要?
- 機(jī)器學(xué)習(xí)的默認(rèn)假設(shè),訓(xùn)練樣本和測試樣本滿足獨立同分布的前提是訓(xùn)練樣本足夠。
- 數(shù)據(jù)的稀缺性。如在想要做醫(yī)學(xué)領(lǐng)域的圖像處理,所能得到的樣本是極端的不平衡,重要的樣本太少,無法訓(xùn)練出一個效果好的網(wǎng)絡(luò)。
- 標(biāo)記的困難性。大數(shù)據(jù)時代動輒億萬數(shù)據(jù),標(biāo)記起來太費時費力。(題外話,半標(biāo)簽問題可以使用偽標(biāo)簽技術(shù),即將test的數(shù)據(jù)加到train數(shù)據(jù)集中,其對應(yīng)的標(biāo)簽為原數(shù)據(jù)集訓(xùn)練得到的。這種方法具有一定的泛化增強(qiáng)能力,最好是在網(wǎng)絡(luò)迭代幾輪后再將已有標(biāo)簽和無標(biāo)簽一起訓(xùn)練。一般會在batch有1/4-1/3的偽標(biāo)簽數(shù)據(jù)。)
- 框架太難訓(xùn)練。想要得到復(fù)雜有效的框架都需要長時間的訓(xùn)練,設(shè)備、時間、人力都成問題。
- 任務(wù)的擴(kuò)展性。在實際中如果有些應(yīng)用需要添加新的模塊,或者收集到的數(shù)據(jù)發(fā)生了變化,那么重新訓(xùn)練一次?沒有更輕松快捷的方法嗎?
在前一篇文章–目標(biāo)檢測中,就使用過Tensorflow Object Detection API里面所自帶的模型訓(xùn)練了自己的數(shù)據(jù)集。這篇再整理用TF-slim和keras完成任務(wù)。
TF-slim
它是TensorFlow(tensorflow.contrib.slim)的一個輕量級高級API,用于定義,訓(xùn)練和評估復(fù)雜模型。包含常用的網(wǎng)絡(luò)架構(gòu)模型如VGG, AlexNet,可以讓從頭開始訓(xùn)練模型,也可以利用預(yù)先訓(xùn)練好的網(wǎng)絡(luò)權(quán)重對其進(jìn)行微調(diào)(fine-turn)。
安裝:
python -c "import tensorflow.contrib.slim as slim; eval = slim.evaluation.evaluate_once"
但是它的出現(xiàn)本身其實是為了–代碼瘦身,即在不用keras,tensorlayer,tflearn這些高級庫的條件下就可以寫出簡單優(yōu)美的代碼。如定義變量它的寫法是:
# Model Variables weights = slim.model_variable('weights',shape=[10, 10, 3 , 3],initializer=tf.truncated_normal_initializer(stddev=0.1),regularizer=slim.l2_regularizer(0.05),device='/CPU:0') model_variables = slim.get_model_variables()#返回模型變量# Regular variables my_var = slim.variable('my_var',shape=[20, 1],initializer=tf.zeros_initializer())regular_variables_and_model_variables = slim.get_variables()#返回所有變量,包括局部變量定義網(wǎng)絡(luò)層則是一行代碼:
net1 = slim.conv2d(input, 128, [3, 3], scope='conv1') net2 = slim.repeat(net1, 3, slim.conv2d, 256, [3, 3], scope='conv3')#repeat可以直接搭3個卷積層 net3 = slim.max_pool2d(net2, [2, 2], scope='pool2')#stack和repert是不同是,它可以處理卷積核或者全連接層輸出不一樣的情況 slim.stack(x, slim.conv2d, [(32, [3, 3]), (32, [1, 1]), (64, [3, 3]), (64, [1, 1])], scope='core') slim.stack(x, slim.fully_connected, [32, 64, 128], scope='fc')用arg_scope來完成參數(shù)共享,整個VGG都十分輕松:
def vgg16(inputs):with slim.arg_scope([slim.conv2d, slim.fully_connected],activation_fn=tf.nn.relu,weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),weights_regularizer=slim.l2_regularizer(0.0005)):#共享的參數(shù)寫到一起net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope='conv1')#其他的單獨再搭net = slim.max_pool2d(net, [2, 2], scope='pool1')net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope='conv2')net = slim.max_pool2d(net, [2, 2], scope='pool2')net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')net = slim.max_pool2d(net, [2, 2], scope='pool3')net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv4')net = slim.max_pool2d(net, [2, 2], scope='pool4')net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv5')net = slim.max_pool2d(net, [2, 2], scope='pool5')net = slim.fully_connected(net, 4096, scope='fc6')net = slim.dropout(net, 0.5, scope='dropout6')net = slim.fully_connected(net, 4096, scope='fc7')net = slim.dropout(net, 0.5, scope='dropout7')net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc8')return net不過不需要,slim里面有已經(jīng)訓(xùn)練完成了的模型,可以直接用來預(yù)測。完整的tf-slim使用手冊:https://github.com/tensorflow/models/blob/master/research/slim/slim_walkthrough.ipynb
import tensorflow as tf vgg = tf.contrib.slim.nets.vgg#vgg模型images, labels = ...#載入圖像和標(biāo)簽 predictions, _ = vgg.vgg_16(images)#預(yù)測 loss = slim.losses.softmax_cross_entropy(predictions, labels)#損失函數(shù)
fine-turn新模型
以下都是基于inception進(jìn)行微調(diào),inception是一個組合的卷積核,能夠讓網(wǎng)絡(luò)進(jìn)行自行選擇,同時又加入了1X1的卷積減少參數(shù)。對它進(jìn)行微調(diào)可以理解為是把前面的卷積層一起當(dāng)成一個特征提取器,將后面的全卷積換成掉訓(xùn)練新的數(shù)據(jù)集如下圖(畢竟新的數(shù)據(jù)集無論從種類數(shù)量等等都還是不一樣的),便可以使它完成新的任務(wù)(比如可以拿普通的圖像識別網(wǎng)絡(luò)在數(shù)據(jù)少的醫(yī)學(xué)圖像上進(jìn)行遷移學(xué)習(xí))。所以操作時往往先會凍結(jié)前面卷積的權(quán)重,只需要fine-turn后面的參數(shù)就可以了。當(dāng)然可以基于它的權(quán)重進(jìn)行全網(wǎng)絡(luò)的微調(diào)。
先下載tf-slim完整的圖像模型庫:
git clone https://github.com/tensorflow/models/
由于Tensorflow的訓(xùn)練集需要是tfrecord模式,如果使用現(xiàn)成的數(shù)據(jù)集ImageNet等就不需要轉(zhuǎn)換,如果是自己的數(shù)據(jù)集需要先做處理。如有一個叫A的數(shù)據(jù)集,通過下來命令使用 download_and_convert_data.py可以轉(zhuǎn)換。
$ DATA_DIR=/tmp/data/A $ python download_and_convert_data.py \--dataset_name=flowers \--dataset_dir="${DATA_DIR}"由于slim本身有MNIST,CIFAR-10 ,Flowers ,ImageNet數(shù)據(jù)集,如果要加入自己的數(shù)據(jù),需要在數(shù)據(jù)集datasets里面注冊一下。便可以導(dǎo)入了。
import tensorflow as tf from datasets import A slim = tf.contrib.slimdataset = A.get_split('validation', DATA_DIR) provider = slim.dataset_data_provider.DatasetDataProvider(dataset) [image, label] = provider.get(['image', 'label'])下載模型并解壓,比如inception_v3
$ CHECKPOINT_DIR=/tmp/checkpoints $ mkdir ${CHECKPOINT_DIR} $ wget http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz $ tar -xvf inception_v3_2016_08_28.tar.gz $ mv inception_v3.ckpt ${CHECKPOINT_DIR} $ rm inception_v3_2016_08_28.tar.gz然后開始fine-turn。其中–checkpoint_exclude_scopes會阻止某些變量被加載。當(dāng)使用與訓(xùn)練模型不同數(shù)量的分類任務(wù)進(jìn)行微調(diào)時,新模型將具有最終的“分類”層,其尺寸與預(yù)先訓(xùn)練的模型不同。標(biāo)志–checkpoint_path和–checkpoint_exclude_scopes期間僅用于模型初始化。通常情況下,微調(diào)只需要訓(xùn)練一組子層,因此該標(biāo)志–trainable_scopes允許指定層的哪些子層應(yīng)該訓(xùn)練,其余的將保持凍結(jié)。
$ python train_image_classifier.py \--train_dir=${TRAIN_DIR} \--dataset_dir=${DATASET_DIR} \--dataset_name=A \ #訓(xùn)練數(shù)據(jù)集--dataset_split_name=train \--model_name=inception_v3 \--checkpoint_path=${CHECKPOINT_PATH} \--checkpoint_exclude_scopes=InceptionV3/Logits,InceptionV3/AuxLogits \ #調(diào)參范圍,只對輸出層做調(diào)整--trainable_scopes=InceptionV3/Logits,InceptionV3/AuxLogits #若不設(shè)定此項,將全參數(shù)調(diào)整--max_number_of _steps=10000 \ #其他的學(xué)習(xí)參數(shù)控制--batch size=64 \ --learning_rate=0.001 \ --learning_rate_decay_type=fixed \ #學(xué)習(xí)率是否遞減--save_interval secs=300 \ #保存模型的時間間隔--save_summaries_secs=2 \ 用tensorboard的更新時間--log_every_n_steps=10 \ #打印信息的步長間隔--optimizer=rmsprop \--weight_decay=0.00001 #正則化參數(shù)tensorboard --logdir=${TRAIN_DIR}查看訓(xùn)練情況。
測試性能:
TF-slim代碼實現(xiàn)fine-turn
不采用上面的命令方法可以通過代碼的方式完成:
用keras進(jìn)行fine-turn
from keras.applications.inception_v3 import InceptionV3 #同樣載入v3 from keras.preprocessing import image from keras.models import Model from keras.layers import Dense, GlobalAveragePooling2D from keras.preprocessing.image import ImageDataGenerator from keras import backend as K import os import tensorflow as tf os.environ["CUDA_VISIBLE_DEVICES"] = "6" gpu_options = tf.GPUOptions(allow_growth=True) sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) from keras.utils import plot_model from matplotlib import pyplot as plt#InceptionV3模型,加載預(yù)訓(xùn)練權(quán)重,但是不保留最后的三個全連接層,進(jìn)行微調(diào) base_model = InceptionV3(weights='imagenet', include_top=False) print(base_model.summary()) # summary便于顯示 plot_model(base_model,to_file = 'InceptionV3.png') # 保存模型結(jié)構(gòu)圖x = base_model.output x = GlobalAveragePooling2D()(x)#全局平均池化層x = Dense(1024, activation='relu')(x) predictions = Dense(2, activation='softmax')(x)#增加兩個全連接層model = Model(inputs=base_model.input, outputs=predictions)#模型合并,得到新模型 print(base_model.summary()) plot_model(model,to_file = 'InceptionV3.png') for layer in base_model.layers:layer.trainable = False #凍結(jié)層model.compile(optimizer='rmsprop', loss='categorical_crossentropy')#開始編譯#生成訓(xùn)練數(shù)據(jù) train_datagen = ImageDataGenerator(rescale=1./255,shear_range=0.2,zoom_range=0.2,horizontal_flip=True)train_generator = train_datagen.flow_from_directory('.train/', target_size=(150, 150), batch_size=32,class_mode='categorical') #開始測試 test_datagen = ImageDataGenerator(rescale=1./255)validation_generator = test_datagen.flow_from_directory('./validation/',target_size=(150, 150),batch_size=32,class_mode='categorical')#新數(shù)據(jù)開始訓(xùn)練 model.fit_generator(train_generator,steps_per_epoch=2000,epochs=1,validation_data=validation_generator,validation_steps=800)#凍結(jié)部分層,然后訓(xùn)練其他層 for i, layer in enumerate(base_model.layers): # 打印出每次的名字print(i, layer.name)for layer in model.layers[:249]:layer.trainable = False for layer in model.layers[249:]:layer.trainable = True#重新訓(xùn)練 from keras.optimizers import SGD model.compile(optimizer=SGD(lr=0.0001, momentum=0.9), loss='categorical_crossentropy')model.fit_generator(train_generator,steps_per_epoch=2000,epochs=1,validation_data=validation_generator,validation_steps=800)fine-turning效果如何?
一圖勝千言。
fune-turning技巧
- 只替掉最后一層,改成本任務(wù)的類別
- 替到最后一層,freeze backbone到收斂,再開放所有層一起
- 替到最后一層,用差分學(xué)習(xí)率(discriminative learning)即backbone和最后一層學(xué)習(xí)率不一樣,畢竟backbone已經(jīng)很好了可以選用比如10倍
- 替到最后一層,freeze淺層,訓(xùn)練深層,以增強(qiáng)泛化,減少過擬合
- 好的學(xué)習(xí)率(如3e-4是Adam最好學(xué)習(xí)率)
總結(jié)
以上是生活随笔為你收集整理的Fine-turning(Tensorflow-Slim和Keras的迁移学习)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 修理网站一个多月不录入缘由剖析
- 下一篇: I3C仿真:PGY I3C-EX-PD使