20191230更新:
根據github上一位大神的作品,使用PyTorch框架,采用 Encoder-Decoder + Attention 方法重新完成image caption。當采用大小為3的Beam Search方式進行推理時,BLEU-4效果可以達到31%以上。在Flicker8K中隨機抽幾張圖片試試效果,以下分別為原圖和預測圖。
可以看到語法和語義效果都很不錯,直觀感受比上次好太多。先占個坑,等有時間再把過程詳細整理一下。
以下是2019年8月份記錄
小白一個,剛剛費了老大的勁完成一個練手項目——image caption,雖然跑通訓練,但是結果卻慘不忍睹。於是貼上大神的作品,留待日后慢慢消化。順便記錄下自己踩坑的一些問題。
本次項目采用的模型結構如下。一路輸入信息是利用VGG16提取的圖像特征,另一路輸入信息是利用LSTM提取的單詞串特征,輸出是預測的下一個單詞。即模型的功能是,在給定圖像特征和caption前面若干個單詞的情況下,能預測出caption的下一個單詞;所以循環若干次后即可得到一句完整的caption。采用的數據集是Flicker8K。
目前效果不怎么好,雖然BLEU4結果為17%,能完整輸出一句話,但大部分只能識別出狗、草地等這種特別明顯的特征,出現不少張冠李戴的描述,甚至於有些句子一直重復這些特征直到被截斷,直觀感受很不如意。暫認為可以從這幾個方面進行改進:試試其他模型結構(比如Seq2Seq等),增加注意力機制,采用更大的數據集,網絡調參。
問題目錄
交叉驗證
Bleu評價
迭代生成器問題(yield/next/send/generator)
Embedding層
模型保存與載入
LSTM層
keras中現成的模型及應用
模型的抽取、凍結、微調
文本預處理
圖像預處理及歸一化
交叉驗證問題
提到交叉驗證並非就特指k折交叉驗證。交叉驗證包括3種:簡單交叉驗證、k折交叉驗證、留一交叉驗證。
Bleu評價問題
生成器問題
yield、next、send:
- 有yield語句的函數,返回一個生成器對象;
- 調用next(g)或g.send()時才會正式執行該函數;
- 執行到yield語句時返回一個值,函數暫停,下一次調用時會接着這個斷點繼續執行;
- 項目中據此改寫了圖像生成器generator,使其每次返回多張圖像信息,加快訓練速度(每個epoch由700s縮減至220s)。
模型保存與載入問題
模型保存:
- 方法一:結構(存為json文件)+權重
model_json = model.to_json()
with open("model_architecture.json", "w") as f_obj:
f_obj.write(model_json)
model.save_weights("model_weights.h5")
- 方法二:直接保存模型
model.save('model.h5')
模型載入:
- 方法一:
model = keras.models.model_from_json(open('model_architecture.json').read())
model.load_weights('model_weight_epoch_1.h5')
- 方法二:
model = keras.models.load_model('CIFAR10_model_epoch_1.h5')
其他保存文件:
- np文件
np.save('bottleneck_features.npy', x_train_Dense) # 將提取出的特征保存在.npy文件中
train_data = np.load('bottleneck_features.npy')
- pickle庫(參考)
pickle.dump(svm_classifier, open('svm_model_iris.pkl', 'wb')) #寫入文件,需要二進制操作
model = pickle.load(open('svm_model_iris.pkl', 'rb'))
#下面還未見過應用
pickle.dumps(obj) #不需要寫入文件中,直接返回一個序列化的bytes對象
pickle.loads(bytes_object) #直接從bytes對象中讀取序列化的信息
Embedding問題
參考
main_input = Input(shape=(100,), dtype='int32', name='main_input')
x = Embedding(output_dim=512, input_dim=10000, input_length=100)(main_input)
- 任何layer均可以加name參數;
- Embedding層只能作為第一層;
- Input層到Embedding層,內部相當於已經進行onehot轉換了,僅需要提供字典大小10000即可;
LSTM問題
LSTM重要參數:keras.layers.LSTM(units, activation='tanh',return_sequences=False, return_state=False),其中units為\(a^{<t>}\)和\(C^{<t>}\)的神經元個數。
LSTM中兩個參數(return_sequences, return_state)的理解:
- 兩者均默認為false,LSTM對象默認返回一個值,開啟return_state后另外返回最后一個cell的隱態\(a^{<t>}\)和\(C^{<t>}\);
- keras中RNN和LSTM等模塊沒有參數V,要獲得\(\hat y^{<t>}\),則需要再接一個Dense層(才是ng課中真正的參數V)轉化一下;
- 理解1
- 理解2
LSTM中參數activation和recurrent_activation,暫時理解成前者為求\(\tilde C\)和\(a\)的激活函數,后者為求三大門的激活函數。(待驗證。)
keras中現成的模型和應用
keras中自帶了一些經典模型,比如VGG、ResNet、Inception等;並提供了這些模型的常見應用場景,比如利用ResNet50分類識別,利用VGG16提取圖像特征,抽取模型中間層的輸出來提取特征等等。(詳見文檔中Preprocessing模塊的Applications。)
模型抽取、凍結、微調
模型抽取:(模型抽取參考)
- 獲取層對象,以便獲取其參數
- model.get_layer(name=None,index=None),或者model.layers[index]
- 獲取模型輸入
- model.input
- 例如:model = Model(inputs=model.input,outputs=model.get_layer(layer_name).output)
模型凍結:(模型凍結參考)
- 通過設置layer對象參數layer.trainable=False,或者模型參數model.trainable=False來控制;(注意layer和model這一參數沖突時默認順序)
- 通過model.save()和load_model()方法載入的模型凍結時有問題,故推薦使用結構+權重分開的方式保存模型。(model.to_json() + model.save_weights())
模型fine-tune:模型微調參考【待消化】
文本預處理問題
text_to_word_sequence如何添加自定義過濾詞?比如加's
- 目前解決:采用正則表達式先換掉's,再統一過濾掉。
三種常用函數:
- keras.preprocessing.text.text_to_word_sequence
- 將一句話打散成單詞;輸入是字符串,輸出是單詞列表;
- 參數:filter可以濾掉不必要的標點符號;lower默認轉小寫;split默認以空格划分;
- keras.preprocessing.text.Tokenizer
- 單詞與編碼的互換工具;
- 常用方法(注意輸入輸出格式):
- tokenizer.fit_on_texts(["今天 北京 下 雨 了", "我 今天 加班"])
- tokenizer.texts_to_sequences(["下 雨 我 加班", "北京 下雨"])
- keras.preprocessing.sequence.pad_sequences
- 填充序列至固定長度
- pad_sequences([[1,2,3],[4,5,6]],maxlen=10,padding='pre')
- 填充序列至固定長度
圖像預處理及歸一化問題
2.項目中利用VGG16提取圖像特征前對圖像預處理,為啥要把RGB轉為BGR?這個VGG16訓練時前處理是用BGR?
20191011注:keras.applications.vgg16.preprocess_input中根據不同的模式進行了通道互換(將RGB轉為BGR)和通道零均值化(每個通道減去各自均值),源代碼見D:\Anaconda3\Lib\site-packages\keras_applications\imagenet_utils.py。(至於為啥要由RGB轉為BGR,推測可能是VGG16模型訓練時候用了OpenCV,因為只有OpenCV用的BGR格式)