动手学PaddlePaddle(5):迁移学习
本次練習,用遷移學習思想,結合paddle框架,來實現(xiàn)圖像的分類。
相關理論:
1. 原有模型作為一個特征提取器:
使用一個用ImageNet數(shù)據(jù)集提前訓練(pre-trained)好的CNN,再除去最后一層全連接層(fully-connected layer),即除去原有的分類器部分。然后再用剩下的神經(jīng)網(wǎng)絡作為特征提取器應用在新的數(shù)據(jù)集上。我們只需要用新的訓練集訓練一個嫁接到這個特征提取器上的分類器即可。
2.fine-turning原有模型
這種方法要求不僅僅除去原有模型的分類器,再用新的數(shù)據(jù)集訓練新的分類器,還要求微調(diào)(fine-tune)本身神經(jīng)網(wǎng)絡的參數(shù)(weights)。和方式1的區(qū)別是:方式1要求固定原有神經(jīng)網(wǎng)絡的參數(shù),而fine-tune允許訓練新的數(shù)據(jù)集時對原有神經(jīng)網(wǎng)絡的參數(shù)進行更改。為了防止過擬合,有時可以要求固定某些層的參數(shù),只微調(diào)剩下的一些層。
本次實驗總共分兩個steps;
- step-1: 在step-1中,加載在imagenet數(shù)據(jù)集上訓練好的Resnet50模型,作為預訓練模型,并且凍結除fc層之外的參數(shù),只訓練fc層。得到step_1_model;
拿到新數(shù)據(jù)集,想要用預訓練模型處理的時候,通常都會先用step-1的方法看看預訓練模型在新數(shù)據(jù)上的表現(xiàn)怎么樣,摸個底。如果表現(xiàn)不錯,還想看看能不能進一步提升,就可以試試Fine-tune(即解鎖比較少的卷積層繼續(xù)訓練),但是不要期待會有什么質的飛躍。如果由于新數(shù)據(jù)集與原數(shù)據(jù)集(例如ImageNet數(shù)據(jù)集)的差別太大導致表現(xiàn)很糟,那么一方面可以考慮從頭訓練模型,另一方面也可以考慮解鎖比較多層的訓練,亦或干脆只用預訓練模型的參數(shù)作為初始值,對模型進行完整訓練。
- step-2: 在step-2中,把step_1_model作為預訓練模型,并在此基礎上重新訓練,得到最終的模型step_2_model。
但是要注意:
事實上,step-2必須在已經(jīng)進行過‘凍結特征提取器參數(shù)的訓練之后再嘗試訓練模型,這時分類器的參數(shù)已經(jīng)經(jīng)過一次訓練。如果從隨機生成的分類器參數(shù)開始直接訓練,在做參數(shù)更新迭代過程中梯度將很可能過大,而導致模型的崩潰,使模型忘記學到的所有東西。
在finetune時,batch_size設置最好不要太大,以便于加速模型收斂。學習率也適當小一些。
目錄
step-1:
1. 導入庫
2. 定義ResNet網(wǎng)絡
3.訓練前準備
4.開始訓練
step-2:
5. 導入庫
6.定義ResNet網(wǎng)絡
7.訓練前準備
step-1:
在step-1中,加載在imagenet數(shù)據(jù)集上訓練好的Resnet50模型,作為預訓練模型,并且凍結除fc層之外的參數(shù),只訓練fc層。得到step_1_model。
1. 導入庫
實驗第一步,導入需要的庫,本實驗中專門定義了一個 reader.py文件,用來對數(shù)據(jù)集進行讀取和預處理,所以也需要把reader.py文件import進來。
import os import shutil import paddle as paddle import paddle.fluid as fluid from paddle.fluid.param_attr import ParamAttr import reader # 獲取flowers數(shù)據(jù) train_reader = paddle.batch(reader.train(), batch_size=16) test_reader = paddle.batch(reader.val(), batch_size=16)2. 定義ResNet網(wǎng)絡
- 本次實驗使用ResNet50這個殘差神經(jīng)網(wǎng)絡,所以,接下來需要定義一個殘差神經(jīng)網(wǎng)絡。
- PaddlePaddle官方已經(jīng)提供了ResNet以及其他經(jīng)典的網(wǎng)絡模型。鏈接:https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/resnet.py
- 網(wǎng)絡定義時,每一個層都由指定參數(shù)名字。
本次實驗,使用的圖片數(shù)據(jù)集是flowers。圖片是3通道寬高都是224的彩色圖,總類別是5種,每一個種類大約有六百多張。
3.訓練前準備
接下來,開始做訓練前的準備工作。首先,定義圖片數(shù)據(jù)和標簽數(shù)據(jù)的輸入層:
# 定義輸入層 image = fluid.layers.data(name='image', shape=[3, 224, 224], dtype='float32') label = fluid.layers.data(name='label', shape=[1], dtype='int64')利用stop_gradient,使得pool以上的層停止梯度傳遞,相當于keras中的freeze。這樣就可以只訓練最后的fc層,但是要注意:數(shù)據(jù)集是5分類,所以size要設為5.
# 獲取分類器的上一層 pool = resnet50(image) # 停止梯度下降 pool.stop_gradient = True # 由這里創(chuàng)建一個基本的主程序 base_model_program = fluid.default_main_program().clone()# 這里再重新加載網(wǎng)絡的分類器,大小為本項目的分類大小 model = fluid.layers.fc(input=pool, size=5, act='softmax')接下來,要做的工作有:
- 定義損失函數(shù);
- 求準確率;
- 定義優(yōu)化器;
- 設定訓練場所;
- 定義執(zhí)行器,并且完成參數(shù)初始化;
- 接下來加載預訓練模型,使用paddle官網(wǎng)上訓練好的ResNet50模型,這個模型存儲在“./ResNet50_pretrained/”,也可以去官網(wǎng)上下載,鏈接:http://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_pretrained.tar
- 通過if_exist函數(shù)判斷網(wǎng)絡所需的模型文件是否存在,然后再通過調(diào)用fluid.io.load_vars加載存在的模型文件。
4.開始訓練
接下來就定義一個雙層循環(huán)來開始訓練模型,并且還可以把訓練過程中的cost值和acc值打印出來,以此來直觀的感受訓練效果。
# 優(yōu)化內(nèi)存 # optimized = fluid.transpiler.memory_optimize(input_program=fluid.default_main_program(), print_log=False)# 定義輸入數(shù)據(jù)維度 feeder = fluid.DataFeeder(place=place, feed_list=[image, label])# 訓練10次 for pass_id in range(10):# 進行訓練for batch_id, data in enumerate(train_reader()):train_cost, train_acc = exe.run(program=fluid.default_main_program(),feed=feeder.feed(data),fetch_list=[avg_cost, acc])# 每100個batch打印一次信息if batch_id % 100 == 0:print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' %(pass_id, batch_id, train_cost[0], train_acc[0]))訓練結束之后,使用fluid.io.save_param保存訓練好的參數(shù)。
到這里為止,把從imagenet數(shù)據(jù)集上訓練好的的原預訓練模型,結合數(shù)據(jù)集,把最后一層fc進行了訓練。接下來就是使用這個已經(jīng)處理過的模型正式訓練了。
# 保存參數(shù)模型 save_pretrain_model_path = 'models/step-1_model/'# 刪除舊的模型文件 shutil.rmtree(save_pretrain_model_path, ignore_errors=True) # 創(chuàng)建保持模型文件目錄 os.makedirs(save_pretrain_model_path) # 保存參數(shù)模型 fluid.io.save_params(executor=exe, dirname=save_pretrain_model_path)step-2:
5. 導入庫
import os import shutil import paddle as paddle import paddle.dataset.flowers as flowers import paddle.fluid as fluid from paddle.fluid.param_attr import ParamAttr import reader# 獲取flowers數(shù)據(jù) train_reader = paddle.batch(reader.train(), batch_size=16) test_reader = paddle.batch(reader.val(), batch_size=16)6.定義ResNet網(wǎng)絡
仍然需要定義一個殘差神經(jīng)網(wǎng)絡,這個殘差神經(jīng)網(wǎng)絡跟第一步時的基本一樣的,只是把分類器(也就是fc層)也加進去了,這是一個完整的神經(jīng)網(wǎng)絡。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
# 定義殘差神經(jīng)網(wǎng)絡(ResNet) def resnet50(input, class_dim):def conv_bn_layer(input, num_filters, filter_size, stride=1, groups=1, act=None, name=None):conv = fluid.layers.conv2d(input=input,num_filters=num_filters,filter_size=filter_size,stride=stride,padding=(filter_size - 1) // 2,groups=groups,act=None,param_attr=ParamAttr(name=name + "_weights"),bias_attr=False,name=name + '.conv2d.output.1')if name == "conv1":bn_name = "bn_" + nameelse:bn_name = "bn" + name[3:]return fluid.layers.batch_norm(input=conv,act=act,name=bn_name + '.output.1',param_attr=ParamAttr(name=bn_name + '_scale'),bias_attr=ParamAttr(bn_name + '_offset'),moving_mean_name=bn_name + '_mean',moving_variance_name=bn_name + '_variance', )def shortcut(input, ch_out, stride, name):ch_in = input.shape[1]if ch_in != ch_out or stride != 1:return conv_bn_layer(input, ch_out, 1, stride, name=name)else:return inputdef bottleneck_block(input, num_filters, stride, name):conv0 = conv_bn_layer(input=input,num_filters=num_filters,filter_size=1,act='relu',name=name + "_branch2a")conv1 = conv_bn_layer(input=conv0,num_filters=num_filters,filter_size=3,stride=stride,act='relu',name=name + "_branch2b")conv2 = conv_bn_layer(input=conv1,num_filters=num_filters * 4,filter_size=1,act=None,name=name + "_branch2c")short = shortcut(input, num_filters * 4, stride, name=name + "_branch1")return fluid.layers.elementwise_add(x=short, y=conv2, act='relu', name=name + ".add.output.5")depth = [3, 4, 6, 3]num_filters = [64, 128, 256, 512]conv = conv_bn_layer(input=input, num_filters=64, filter_size=7, stride=2, act='relu', name="conv1")conv = fluid.layers.pool2d(input=conv, pool_size=3, pool_stride=2, pool_padding=1, pool_type='max')for block in range(len(depth)):for i in range(depth[block]):conv_name = "res" + str(block + 2) + chr(97 + i)conv = bottleneck_block(input=conv,num_filters=num_filters[block],stride=2 if i == 0 and block != 0 else 1,name=conv_name)pool = fluid.layers.pool2d(input=conv, pool_size=7, pool_type='avg', global_pooling=True)output = fluid.layers.fc(input=pool, size=class_dim, act='softmax')return output7.訓練前準備
接下來,開始做訓練前的準備工作:
- 定義圖片數(shù)據(jù)和標簽數(shù)據(jù)的輸入層;
- 定義損失函數(shù);
- 求準確率;
- 定義優(yōu)化器;
- 設定訓練場所;
- 定義執(zhí)行器,并且完成參數(shù)初始化;
加載經(jīng)過step-1訓練好的模型,作為新的預訓練模型。并在此基礎上進行重新訓練:
# 經(jīng)過step-1處理后的的預訓練模型 pretrained_model_path = 'models/step-1_model/'# 加載經(jīng)過處理的模型 fluid.io.load_params(executor=exe, dirname=pretrained_model_path)接下來就定義一個雙層循環(huán)來開始訓練模型,并且還可以把訓練過程中的cost值和acc值打印出來,以此來直觀的感受訓練效果。
# 定義輸入數(shù)據(jù)維度 feeder = fluid.DataFeeder(place=place, feed_list=[image, label])# 訓練10次 for pass_id in range(10):# 進行訓練for batch_id, data in enumerate(train_reader()):train_cost, train_acc = exe.run(program=fluid.default_main_program(),feed=feeder.feed(data),fetch_list=[avg_cost, acc])# 每100個batch打印一次信息if batch_id % 100 == 0:print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' %(pass_id, batch_id, train_cost[0], train_acc[0]))# 進行測試test_accs = []test_costs = []for batch_id, data in enumerate(test_reader()):test_cost, test_acc = exe.run(program=test_program,feed=feeder.feed(data),fetch_list=[avg_cost, acc])test_accs.append(test_acc[0])test_costs.append(test_cost[0])# 求測試結果的平均值test_cost = (sum(test_costs) / len(test_costs))test_acc = (sum(test_accs) / len(test_accs))print('Test:%d, Cost:%0.5f, Accuracy:%0.5f' % (pass_id, test_cost, test_acc))訓練結束之后,可以保存預測模型用于之后的預測使用。
# 保存預測模型 save_path = 'models/step_2_model/'# 刪除舊的模型文件 shutil.rmtree(save_path, ignore_errors=True) # 創(chuàng)建保持模型文件目錄 os.makedirs(save_path) # 保存預測模型 fluid.io.save_inference_model(save_path, feeded_var_names=[image.name], target_vars=[model], executor=exe)?
總結
以上是生活随笔為你收集整理的动手学PaddlePaddle(5):迁移学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2017最容易申请的白金卡排名 首卡也可
- 下一篇: 郑州高温动物园给老虎安排泳池解暑 视频火