前言#
参考にしたのは 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
文書内の 2 つの空白を 1 つの空白に置き換えます。
次に、以下のコードを使用して必要な顔属性を抽出できます。
#!/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 は、私が 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
}
}
出力が 3 つあり、slice_point は 2 まで削除します。
【2018.08.22 更新】-----------------------------------------------
このネットワーク構造は bn2 の後に 6 つのグループに分かれており、それぞれのグループには異なる数の出力があります。また、各グループの前には同じ構造の層があり、各タスクの全結合層と最終出力層に至ります。必要のないタスク層とグループを削除します。私のこの 3 つのカテゴリについて、gender は単独のグループであり、眼鏡は 4 番目のグループ、前髪は 6 番目のグループにあります。パラメータの数を減らすために、眼鏡と前髪を 6 番目のグループにまとめ、4 番目のグループの前のネットワークを削除しました。第一グループの gender については、3 つのタスクを 1 つのグループにまとめると収束しない可能性があるため、今のところ変更していません。しかし、将来的にはテストを行う予定です。
(トレーニング出力は良好で、検証セットでの精度は非常に高いですが、実際の検出時には単独のグループの性別は正確ですが、眼鏡と前髪はそれぞれ正確ではありません。次のステップでは、眼鏡と前髪をそれぞれ別のグループにし、全結合層をグローバル平均プーリングに変更する予定です。)
(前髪と眼鏡をそれぞれ別のグループにし、全結合層を削除しました。モデルは小さくなり、わずか 8M になりました。検証セットの精度はかなり高く、98% 程度ですが、実際の実験結果は非常に悪いです。次に、コアネットワークを mobilenet v2 または squeezenet に変更してみる予定です。また、ネットワークの問題だけではないかもしれないと考え、データセットを観察したところ、前髪や眼鏡をかけた画像が非常に少ないことがわかりました。ネットワークを変更しても効果がない場合、次のステップでは一部のデータに対してデータ拡張を行う必要があります。)
【2018.08.29 更新】-----------------------------------------------
検証セットとテストセットでの結果は非常に良好ですが、カメラやビデオを使用した実際のアプリケーションでは、効果が非常に悪いです。
原因がわかりました。テスト時の前処理方法が間違っていたためです。
【2018.09.04 更新】-----------------------------------------------
いくつかの心得:
-
全畳み込み + グローバルプーリングを全結合層の代わりに使用しても、効果に大きな影響はなく、モデルのパラメータ量は変わりません。
-
全結合層はパラメータ量が大きいですが、実行時間は非常に速いです。つまり、全結合層はモデルの実行時間に大きな影響を与えず、モデルのサイズには大きな影響を与えます。
-
各層の実行時間を確認したところ、大きな畳み込みカーネルの効率が非常に低いため、すべて 3*3 の小さな畳み込みカーネルに変更するのが最良です。
-
畳み込み層の出力数はできるだけ 2^n にするのが良いです。
最後に loss_weight を変更し、すべてのタスクの loss_weight を合計して 1 にします。私には 3 つのタスクしかないので、loss_weight はすべて 0.3333 に設定しました。
残りの solver.prototxt については、単一タスクのものと同じなので、説明しません。
最後にトレーニング出力のスクリーンショットを 1 枚貼ります:
後で継続的に更新します。。。#
1. 可視化コード 2018.08.21 GitHub にアップロード済み、show.py
2. caffe-python 推論コード 2018.08.21 GitHub にアップロード済み、face_attri.py
- ncnn-C++ デプロイコード