最近 GitHub 有一個非常有意思的專案,它可以使用多種預訓練 TensorFLow 模型計算影像特徵。對於每一個模型,它們都會輸出最後的全連線層,即 AlexNet 的第七個全連線層、VGG_19 的第 8 個全連線層等。這些層級將最終抽取出影像的特徵,並能進一步用於影像分類和聚類等。機器之心簡要地介紹了該專案,並測試了使用Inception_V1預訓練模型抽取影像特徵。
專案地址:https://github.com/cameronfabbri/Compute-Features
這個專案的用法非常簡單,我們只需要下載專案中的預訓練模型檢查點,例如 Inception V1 等。然後再使用如下命令列載入資料與預訓練模型就能抽取出影像的特徵:
tar -xvf inception_v1_2016_08_28.tar.gz
python compute_features.py --data_dir=test_images/ --checkpoint_file=inception_v1.ckpt --model=inception_v1
預訓練模型的輸出將會寫入 inception_v1_features.pkl 檔案,它包含了一個如下形式的字典:{image_path:feature}。案例 load_features.py 展示瞭如何使用預計算的特徵。例如我們可以使用如下命令列將預抽取的特徵用於其它任務:
python load_features.py features/inception_v1_features.pkl
有一些模型用上述方法可能並不能載入,例如 inception_v4 有不同的模型檢查點和模型定義等。讀者可查與具體的模型定義並構建適合於我們自己任務的用法。
預訓練模型
我們下載所有的預訓練模型:https://drive.google.com/file/d/13pyno-mdbazKs0o4N_Pk8ArDtk1RcE-U/view?usp=sharing
所有模型的獨立下載地址在專案中都有展示。這些預訓練的卷積神經網路都在 ILSVRC-2012-CLS 影像分類資料集中訓練,且它們在 ImageNet 測試集中的 Top-1 和 Top-5 準確度都在下表給出。每個模型都給出了對應的論文和 TF-Sim 寫的模型程式碼,這些模型程式碼都來自 TensorFLow 中的 models 子專案。
注意 VGG 和 ResNet V1 的引數轉換自它們的原始 Caffe 格式,而 Inception 和 ResNet V2 的預訓練引數由谷歌內部完成。此外,這些準確度都是在單個影像 crop 下評估的,也有一些學術論文使用多個 crops 在不同規模上有更好的準確度。
在使用預訓練模型抽取影像特徵的同時,我們還需要了解各個模型的架構與特點,這樣才能更有效地利用它們抽取出來的特徵向量。如下展示了經典 Inception_V3 模型的一個 Inception 模組,卷積層都是使用 TensorFlow-Slime 實現,因此我們可以在一行中實現一個卷積層的前向傳播演算法。一般而言,slim.conv2d 有三個引數必填,第一個引數是輸入矩陣、第二個是當前卷積層的卷積核數量,最後就是卷積核尺寸。
with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
stride=1, padding='SAME'):
# mixed: 35 x 35 x 256.
end_point = 'Mixed_5b'
with tf.variable_scope(end_point):
with tf.variable_scope('Branch_0'):
branch_0 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
with tf.variable_scope('Branch_1'):
branch_1 = slim.conv2d(net, depth(48), [1, 1], scope='Conv2d_0a_1x1')
branch_1 = slim.conv2d(branch_1, depth(64), [5, 5],
scope='Conv2d_0b_5x5')
with tf.variable_scope('Branch_2'):
branch_2 = slim.conv2d(net, depth(64), [1, 1], scope='Conv2d_0a_1x1')
branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
scope='Conv2d_0b_3x3')
branch_2 = slim.conv2d(branch_2, depth(96), [3, 3],
scope='Conv2d_0c_3x3')
with tf.variable_scope('Branch_3'):
branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
branch_3 = slim.conv2d(branch_3, depth(32), [1, 1],
scope='Conv2d_0b_1x1')
net = tf.concat(axis=3, values=[branch_0, branch_1, branch_2, branch_3])
end_points[end_point] = net
以上展示了 Inception_V3 的一個模組,簡單而言,該模組就是將四個並行的卷積(branch_0 到 branch_3)分別執行不同的卷積運算,例如 branch_2 是由三個卷積層疊加而成,然後再將 4 份卷積特徵圖拼接起來作為這一模組的輸出。
其它還有很多如特徵抽取的效果或模型架構等還需要各位讀者自己嘗試。
我們測試了 inception_V1 預訓練模型,它完成特徵計算後會輸出一個包含特徵的 pkl 檔案。我們發現其它如 Inception V3 會報錯說權重檔案中名為 InceptionV3/AuxLogits/Conv2d_2a_3x3/BatchNorm/beta 的張量沒有定義,Inception V4 中會報錯 tf.train.Saver() 沒有需要儲存的變數。
也許這些錯誤可能是由環境配置或其它問題,但還有很多如特徵抽取的效果或模型架構等還需要各位讀者自己嘗試。