關於VGG16預訓練的理解與實踐
今天看到了遷移學習的相關內容但是,感覺並不是很清楚,問了一波大概有了比較清楚的認識,結合網上相關內容,記錄如下。
1.概念:
遷移學習(Transfer Learning)的目標是將從一個環境中學到的知識用來幫助新環境中的學習任務。
2.遷移學習的適用情況
目前大多數機器學習演算法均是假設訓練資料以及測試資料的特徵分佈相同。然而這在現實世界中卻時常不可行。例如我們我們要對一個任務進行分類,但是此任務中資料不充足(在遷移學習中也被稱為目標域),然而卻又需要大量的相關的訓練資料(在遷移學習中也被稱為源域),但是此訓練資料與所需進行的分類任務中的測試資料特徵分佈不同(例如語音情感識別中,一種語言的語音資料充足,然而所需進行分類任務的情感資料卻極度缺乏),在這種情況下如果可以採用合適的遷移學習方法則可以大大提高樣本不充足任務的分類識別結果。也即是大家通常所說的將知識遷移到新環境中的能力,這通常被稱為遷移學習。
當兩者的相似度並不大的時候,會出現負遷移的情況,這個時候就不適合做遷移
3.預訓練模型
簡單來說,預訓練模型(pre-trained model)是前人為了解決類似問題所創造出來的模型。你在解決問題的時候,不用從零開始訓練一個新模型,可以從在類似問題中訓練過的模型入手。比如說,如果你想做一輛自動駕駛汽車,可以花數年時間從零開始構建一個性能優良的影象識別演算法,也可以從Google在ImageNet資料集上訓練得到的inception model(一個預訓練模型)起步,來識別影象。一個預訓練模型可能對於你的應用中並不是100%的準確對口,但是它可以為你節省大量功夫。
通過使用之前在大資料集上經過訓練的預訓練模型,我們可以直接使用相應的結構和權重,將它們應用到我們正在面對的問題上。這被稱作是“遷移學習”,即將預訓練的模型“遷移”到我們正在應對的特定問題中。在選擇預訓練模型的時候你需要非常仔細,如果你的問題與預訓練模型訓練情景下有很大的出入,那麼模型所得到的預測結果將會非常不準確。
預訓練模型已經訓練得很好,我們就不會在短時間內去修改過多的權重,在遷移學習中用到它的時候,往往只是進行微調(fine tune)。在修改模型的過程中,我們通過會採用比一般訓練模型更低的學習速率
如何使用預訓練模型:
場景一:資料集小,資料相似度高(與pre-trained model的訓練資料相比而言)
在這種情況下,因為資料與預訓練模型的訓練資料相似度很高,因此我們不需要重新訓練模型。我們只需要將輸出層改制成符合問題情境下的結構就好。我們使用預處理模型作為模式提取器。比如說我們使用在ImageNet上訓練的模型來辨認一組新照片中的小貓小狗。在這裡,需要被辨認的圖片與ImageNet庫中的圖片類似,但是我們的輸出結果中只需要兩項——貓或者狗。在這個例子中,我們需要做的就是把dense layer和最終softmax layer的輸出從1000個類別改為2個類別。
場景二:資料集小,資料相似度不高
在這種情況下,我們可以凍結預訓練模型中的前k個層中的權重,然後重新訓練後面的n-k個層,當然最後一層也需要根據相應的輸出格式來進行修改。因為資料的相似度不高,重新訓練的過程就變得非常關鍵。而新資料集大小的不足,則是通過凍結預訓練模型的前k層進行彌補。
場景三:資料集大,資料相似度不高
在這種情況下,因為我們有一個很大的資料集,所以神經網路的訓練過程將會比較有效率。然而,因為實際資料與預訓練模型的訓練資料之間存在很大差異,採用預訓練模型將不會是一種高效的方式。因此最好的方法還是將預處理模型中的權重全都初始化後在新資料集的基礎上重頭開始訓練。
場景四:資料集大,資料相似度高
這就是最理想的情況,採用預訓練模型會變得非常高效。最好的運用方式是保持模型原有的結構和初始權重不變,隨後在新資料集的基礎上重新訓練。
微調模型的方法
如何使用與訓練模型,是由資料集大小和新舊資料集(預訓練的資料集和我們要解決的資料集)之間資料的相似度來決定的。
特徵提取
我們可以將預訓練模型當做特徵提取裝置來使用。具體的做法是,將輸出層去掉,然後將剩下的整個網路當做一個固定的特徵提取機,從而應用到新的資料集中。
採用預訓練模型的結構
我們還可以採用預訓練模型的結構,但先將所有的權重隨機化,然後依據自己的資料集進行訓練。
訓練特定層,凍結其他層
另一種使用預訓練模型的方法是對它進行部分的訓練。具體的做法是,將模型起始的一些層的權重保持不變,重新訓練後面的層,得到新的權重。在這個過程中,我們可以多次進行嘗試,從而能夠依據結果找到frozen layers和retrain layers之間的最佳搭配。
4.基於 VGG16 預訓練權重對影象進行分類
先來一個網路視覺化的網站,把你的描述神經網路結構的prototxt檔案複製到該編輯框裡,按shift-enter,就可以直接以圖形方式顯示網路的結構
上一波vgg16的prototxt,太佔地方了,就放在最後面了
訓練完網路再填這一部分
4.1爬蟲獲取資料
需要新建一個name_lists1.txt,在這裡面將你想搜尋的關鍵詞放進去
#!/usr/bin/env python
# encoding: utf-8
import urllib2
import re
import os
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
def img_spider(name_file):
user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
headers = {'User-Agent':user_agent}
#讀取名單txt,生成包括所有人的名單列表
with open(name_file) as f:
name_list = [name.rstrip().decode('utf-8') for name in f.readlines()]
f.close()
#遍歷每一個人,爬取30張關於他的圖,儲存在以他名字命名的資料夾中
for name in name_list:
#生成資料夾(如果不存在的話)
if not os.path.exists('/home/andy/xxs/demo/Crawler/name/' + name):
os.makedirs('/home/andy/xxs/demo/Crawler/name/' + name)
try:
#有些外國人名字中間是空格,要把它替換成%20,不然訪問頁面會出錯。
url = "http://image.baidu.com/search/avatarjson?tn=resultjsonavatarnew&ie=utf-8&word=" + name.replace(' ','%20') + "&cg=girl&rn=60&pn=60"
req = urllib2.Request(url, headers=headers)
res = urllib2.urlopen(req)
page = res.read()
#print page
#因為JSON的原因,在瀏覽器頁面按F12看到的,和你打印出來的頁面內容是不一樣的,所以匹配的是objURL這個東西,對比一下頁面裡別的某某URL,那個能訪問就用那個
img_srcs = re.findall('"objURL":"(.*?)"', page, re.S)
print name,len(img_srcs)
except:
#如果訪問失敗,就跳到下一個繼續執行程式碼,而不終止程式
print name," error:"
continue
j = 1
src_txt = ''
#訪問上述得到的圖片路徑,儲存到本地
for src in img_srcs:
with open('/home/andy/xxs/demo/Crawler/name/' + name + '/' + str(j)+'.jpg','wb') as p:
try:
print "downloading No.%d"%j
req = urllib2.Request(src, headers=headers)
#設定一個urlopen的超時,如果3秒訪問不到,就跳到下一個地址,防止程式卡在一個地方。
img = urllib2.urlopen(src,timeout=3)
p.write(img.read())
except:
print "No.%d error:"%j
p.close()
continue
p.close()
src_txt = src_txt + src + '\n'
if j==60: # 設定需要下載多少張這個人的圖片
break
j = j+1
#儲存30個圖片的src路徑為txt,我要一行一個,所以加換行符
with open('/home/andy/xxs/demo/Crawler/name/' + name + '/' + name +'.txt','wb') as p2:
p2.write(src_txt)
p2.close()
print "save %s txt done"%name
#主程式,讀txt檔案開始爬
if __name__ == '__main__':
name_file = "name_lists1.txt"
img_spider(name_file)
4.2對爬到的圖片進行resize
name: "VGG16"
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
# transform_param {
# mirror: true
# crop_size: 224
# mean_file: "data/ilsvrc12_shrt_256/imagenet_mean.binaryproto"
# }
transform_param {
mirror: true
crop_size: 224
mean_value: 103.939
mean_value: 116.779
mean_value: 123.68
}
data_param {
source: "data/ilsvrc12_shrt_256/ilsvrc12_train_leveldb"
batch_size: 64
backend: LEVELDB
}
}
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
# transform_param {
# mirror: false
# crop_size: 224
# mean_file: "data/ilsvrc12_shrt_256/imagenet_mean.binaryproto"
# }
transform_param {
mirror: false
crop_size: 224
mean_value: 103.939
mean_value: 116.779
mean_value: 123.68
}
data_param {
source: "data/ilsvrc12_shrt_256/ilsvrc12_val_leveldb"
batch_size: 50
backend: LEVELDB
}
}
layer {
bottom: "data"
top: "conv1_1"
name: "conv1_1"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 64
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv1_1"
top: "conv1_1"
name: "relu1_1"
type: "ReLU"
}
layer {
bottom: "conv1_1"
top: "conv1_2"
name: "conv1_2"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 64
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv1_2"
top: "conv1_2"
name: "relu1_2"
type: "ReLU"
}
layer {
bottom: "conv1_2"
top: "pool1"
name: "pool1"
type: "Pooling"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
bottom: "pool1"
top: "conv2_1"
name: "conv2_1"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 128
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv2_1"
top: "conv2_1"
name: "relu2_1"
type: "ReLU"
}
layer {
bottom: "conv2_1"
top: "conv2_2"
name: "conv2_2"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 128
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv2_2"
top: "conv2_2"
name: "relu2_2"
type: "ReLU"
}
layer {
bottom: "conv2_2"
top: "pool2"
name: "pool2"
type: "Pooling"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
bottom: "pool2"
top: "conv3_1"
name: "conv3_1"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv3_1"
top: "conv3_1"
name: "relu3_1"
type: "ReLU"
}
layer {
bottom: "conv3_1"
top: "conv3_2"
name: "conv3_2"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv3_2"
top: "conv3_2"
name: "relu3_2"
type: "ReLU"
}
layer {
bottom: "conv3_2"
top: "conv3_3"
name: "conv3_3"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv3_3"
top: "conv3_3"
name: "relu3_3"
type: "ReLU"
}
layer {
bottom: "conv3_3"
top: "pool3"
name: "pool3"
type: "Pooling"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
bottom: "pool3"
top: "conv4_1"
name: "conv4_1"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 512
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv4_1"
top: "conv4_1"
name: "relu4_1"
type: "ReLU"
}
layer {
bottom: "conv4_1"
top: "conv4_2"
name: "conv4_2"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 512
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv4_2"
top: "conv4_2"
name: "relu4_2"
type: "ReLU"
}
layer {
bottom: "conv4_2"
top: "conv4_3"
name: "conv4_3"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 512
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv4_3"
top: "conv4_3"
name: "relu4_3"
type: "ReLU"
}
layer {
bottom: "conv4_3"
top: "pool4"
name: "pool4"
type: "Pooling"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
bottom: "pool4"
top: "conv5_1"
name: "conv5_1"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 512
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv5_1"
top: "conv5_1"
name: "relu5_1"
type: "ReLU"
}
layer {
bottom: "conv5_1"
top: "conv5_2"
name: "conv5_2"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 512
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv5_2"
top: "conv5_2"
name: "relu5_2"
type: "ReLU"
}
layer {
bottom: "conv5_2"
top: "conv5_3"
name: "conv5_3"
type: "Convolution"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 512
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
bottom: "conv5_3"
top: "conv5_3"
name: "relu5_3"
type: "ReLU"
}
layer {
bottom: "conv5_3"
top: "pool5"
name: "pool5"
type: "Pooling"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
bottom: "pool5"
top: "fc6"
name: "fc6"
type: "InnerProduct"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
bottom: "fc6"
top: "fc6"
name: "relu6"
type: "ReLU"
}
layer {
bottom: "fc6"
top: "fc6"
name: "drop6"
type: "Dropout"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
bottom: "fc6"
top: "fc7"
name: "fc7"
type: "InnerProduct"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
bottom: "fc7"
top: "fc7"
name: "relu7"
type: "ReLU"
}
layer {
bottom: "fc7"
top: "fc7"
name: "drop7"
type: "Dropout"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
bottom: "fc7"
top: "fc8"
name: "fc8"
type: "InnerProduct"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 1000
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "accuracy_at_1"
type: "Accuracy"
bottom: "fc8"
bottom: "label"
top: "accuracy_at_1"
accuracy_param {
top_k: 1
}
include {
phase: TEST
}
}
layer {
name: "accuracy_at_5"
type: "Accuracy"
bottom: "fc8"
bottom: "label"
top: "accuracy_at_5"
accuracy_param {
top_k: 5
}
include {
phase: TEST
}
}
layer {
bottom: "fc8"
bottom: "label"
top: "loss"
name: "loss"
type: "SoftmaxWithLoss"
}