前言#
參考了 https://zhuanlan.zhihu.com/p/22190532
細節有一些問題,我這裡過一遍整個流程,涉及到的問題都會做講解。包括部署問題,最後我也會講講。
文中涉及到的我用的代碼都在這裡: https://github.com/HansRen1024/Face-Attributes-MultiTask-Classification
正文#
去原作者 github 網站下載 https://github.com/HolidayXue/CodeSnap
這個項目。
將 convert_multilabel.cpp 放到 caffe/tools/ 目錄下。
修改 81 行:
‘>>’ should be ‘> >’
然後註釋掉 149 行。
命令行在 caffe / 下運行:
make clean
make all -j8
make py
我是用的 CelebA 數據集,都是人臉屬性的。
下載地址: https://pan.baidu.com/s/17rp2gKqtvuT48yuPfJK3sA
在 Anno 目錄下找到 list_attr_celeba.txt
先命令行運行:
sed -i 's/ / /g' list_attr_celeba.txt
將文檔中兩個空格都替換為一個空格。
然後可以通過下面代碼提取你想要的人臉屬性
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Mon Aug 20 16:57:52 2018
@author: hans
"""
ReadTxt = 'list_attr_celeba.txt'
WriteTxt = 'train.txt'
r = open(ReadTxt,'r')
w = open(WriteTxt,'w')
rLine = r.readline().split('\n')[0]
while rLine:
rLine = r.readline().split('\n')[0]
if not rLine:
break
# image,bangs,eyeglasses,gender,
wLine = rLine.split(' ')[0]+' '+rLine.split(' ')[6]+' '+rLine.split(' ')[16]+' '+rLine.split(' ')[21]+'\n'
w.write(wLine)
r.close()
w.close()
接著命令行運行:
sed -i 's/-1/0/g' train.txt
將 train.txt 文檔中 - 1 改成索引 0。
對比了一下原圖片,提取的屬性是對的。
然後從 train.txt 拿出了一部分放到 val.txt 中
運行腳本,生成可供訓練用的 lmdb 數據:
echo "Creating train lmdb..."
~/caffe-multi/build/tools/convert_multilabel \
-resize_height=227 \
-resize_width=227 \
-shuffle=false \
/home/hans/data/face/CelebA/Img/img_align_celeba/ \
train.txt \
./train_db \
./train_lb \
3
echo "Creating val lmdb..."
~/caffe-multi/build/tools/convert_multilabel \
-resize_height=227 \
-resize_width=227 \
-shuffle=false \
/home/hans/data/face/CelebA/Img/img_align_celeba/ \
val.txt \
./val_db \
./val_lb \
3
img_align_celeba 是切好人臉的數據集
後面參數 3 表示我這邊提取了三種人臉屬性。
最後是修改 mcnn_Attri.prototxt,均值,歸一化,數據路徑,還有一個很重要,backend 一定要改成 LMDB!
name: "MCNN_Attri"
layer {
name: "data"
type: "Data"
top: "data"
transform_param {
scale: 0.007843
mean_value: 127.5
mean_value: 127.5
mean_value: 127.5
crop_size: 227
}
include {
phase: TRAIN
}
data_param {
source: "/home/hans/data/face/CelebA/attri/doc/train_db"
batch_size: 192
backend: LMDB
}
}
layer {
name: "labels"
type: "Data"
top: "labels"
include {
phase: TRAIN
}
data_param {
source: "/home/hans/data/face/CelebA/attri/doc/train_lb"
batch_size: 192
backend: LMDB
}
}
layer {
name: "data"
type: "Data"
top: "data"
transform_param {
scale: 0.007843
mean_value: 127.5
mean_value: 127.5
mean_value: 127.5
crop_size: 227
}
include {
phase: TEST
}
data_param {
source: "/home/hans/data/face/CelebA/attri/doc/val_db"
batch_size: 128
backend: LMDB
}
}
layer {
name: "labels"
type: "Data"
top: "labels"
include {
phase: TEST
}
data_param {
source: "/home/hans/data/face/CelebA/attri/doc/val_lb"
batch_size: 128
backend: LMDB
}
}
layer {
name: "sliceL"
type: "Slice"
bottom: "labels"
top: "label_attr6"
top: "label_attr16"
top: "label_attr21"
slice_param {
slice_dim: 1
slice_point: 1
slice_point: 2
}
}
有三個輸出,slice_point 刪到 2。
【2018.08.22 更新】-----------------------------------------------
這個網絡結構在 bn2 後分成了六組,每一組都有不同個輸出。並且每一組前面都有結構一樣的層,然後到各個任務的全連接層和最終輸出層。將對應不需要的任務層和組刪掉。對於我這三個類別,我發現 gender 是單獨一組的,眼鏡在第四組,劉海在第六組。為了減少參數量,我將眼鏡和劉海都放到了第六組,刪除了第四組的前面網絡。第一組 gender 的我暫時沒改,擔心三個任務都放到一組裡出現不收斂的情況。不過以後我肯定會做測試。
(訓練輸出很好,在驗證集上準確率非常高,但是實際檢測的時候發現對單獨在一組的性別分別準確,對眼鏡和劉海分別不準確。我下一步還是要將眼鏡和劉海分別放在一組,並且將全連接層改成全局平均池化。)
(將劉海和眼鏡分別放在了一組,同時刪了全連接層。模型是變小了,只有 8M 了。驗證集準確率還挺高,98% 左右,但是實際實驗效果太差。接下來嘗試換換核心網絡,用 mobilenet
v2 或者 squeezenet 試試。同時我考慮到可能不一定是網絡的原因,觀察了一下數據集,有劉海和戴眼鏡的圖片很少。如果換網絡也不行,那麼下一步應該要對部分數據做 argument 了。)
【2018.08.29 更新】-----------------------------------------------
在驗證集和測試集上結果都很美好,但是拿攝像頭或者視頻實際應用的時候效果差的離譜。
找到原因了,是我測試時候的預處理方式不對導致的。
【2018.09.04 更新】-----------------------------------------------
幾點心得:
- 用全卷積 + 全局池化替代全連接層,效果影響不大,模型參數量不變。
2. 全連接層參數量大,但執行時間很快。也就是說全連接層對模型執行時間影響不大,對模型大小影響較大。
3. 查看了每一層的執行時間,發現大卷積核效率很低,最好都改用 3*3 的小卷積核。
4. 卷積層輸出數量最好是 2^n。
最後改 loss_weight,所有任務的 loss_weight 加一起等於 1。我這裡只有三個任務,所以 loss_weight 我都設置為 0.3333 了。
剩下的 solver.prototxt 我就不說了,跟單任務的一樣。
最後放一張訓練輸出截圖:
後面我會持續更新。。。#
1. 可視化代碼 2018.08.21 已上傳到 github, show.py
2.caffe-python 推理代碼 2018.08.21 已上傳到 github, face_attri.py
3.ncnn-C++ 部署代碼