エイバースの中の人

アプリとWEBサービスを作っているABARSのBLOGです。

ディープラーニング

性別推定モデルのSqueezeNetとMobileNetの比較

IMDB wikiデータセットを使用して性別推定モデルを構築した場合の、lossとaccuracyをSqueezeNetとMobileNetで計測しました。

SqueezeNet

agegender_gender_squeezenet_


MobileNet

agegender_gender_mobilenet_


MobileNetの方がTrainingのaccuracyは高くなるのですが、Validationのaccuracyが伸びていかないため、SqueezeNetでも十分そうです。

SqueezeNetは10.2MB、MobileNetは34.4MBの重みになります。

YoloKerasFaceDetection

Darknetにおける転移学習の効果

Darknetを使用した学習では、引数に学習済み係数を与えて転移学習を行うことができます。Darknetで使用するYOLOv2の前段はVGGライクな特徴検出器となっており、Imagenetなどの大規模データで学習したフィルタ係数を流用することで、効率的に学習を行うことができると言われています。

今回はFDDBを使用した顔検出において、転移学習の効果を確認します。モデルはYOLOv2-tinyを使用します。FDDBのデータセットは2845枚で、3/4をトレーニング、1/4をバリデーションとして用います。

まずは、転移学習をしない場合の学習の推移です。

yolov2-tiny-train-one-class_32600


次に、転移学習をする場合の学習の推移です。

yolov2-tiny-train_431800


転移学習をしない場合は、最初のlossが大きいですが、転移学習をする場合は最初のlossが低い地点からスタートすることがわかります。また、lossの低下速度が速くなっています。

ただ、転移学習をしない場合でも、Geforce1050tiで一晩で学習が終わっており、また、最終的にできあがるモデルの精度もあまり違わないため、Darknetにおいては転移学習をしなくてもよさそうという結論です。

ソースコード:YoloKerasFaceDetection

YAD2KでYolov2の重みをKerasに変換する

YAD2Kを使用することで、Darknetで学習したYolov2の重みをKerasに変換することができます。

変換を行うには以下のコマンドを使用します。
python3 yad2k.py yolov2-tiny.cfg yolov2-tiny.weights yolov2_tiny.h5

注意点として、64bitでビルドしたDarknetを使用している場合、yad2k.pyのweights_file.read(16)をweights_file.read(20)に書き換える必要があります。
    weights_header = np.ndarray(
        shape=(4, ), dtype='int32', buffer=weights_file.read(20))
(参考:Tiny Yolo conversion fails to detect any objects

以下のコマンドでテストを行います。
python3 test_yolo.py yolov2_tiny.h5

MMdnnを使用してモデルをcaffeからkerasに変換する

MMdnnはマイクロソフトが開発しているモデルコンバータです。モデルデータをIRに変換することで、モデルの相互変換を可能にしています。

MMdnn

mmdnnをインストールし、caffemodelとprototxtからhdf5に変換するには以下のようにします。inputShapeを指定しない場合、None例外が発生します。

pip2 install mmdnn
mmconvert --srcFramework caffe --inputWeight face.caffemodel --inputNetwork face.prototxt --dstFramework keras --outputModel face.hdf5 --inputShape 1,3,448,448

CoreMLでfloat配列から推論する

Caffeと同様に、CoreMLでもMLFeatureProviderという形式でデータを与え、predictionFromFeaturesを呼び出すことで、float配列から推論を行うことができます。

任意のfloat配列からMLFeatureProviderを取得するには、MLMultiArrayにfloat配列を書き込み、MLMultiArrayを持つNSMutableDictionaryを作成し、NSMutableDictionaryからMLDictionaryFeatureProviderを生成することになります。このとき、ディクショナリのキーはmlmodelの入力層の名前になります。

NSArray *shape = @[@1, [NSNumber numberWithInt:src_w], [NSNumber numberWithInt:src_z], [NSNumber numberWithInt:src_y], [NSNumber numberWithInt:src_x]];
MLMultiArray* data = [[MLMultiArray alloc] initWithShape:shape dataType:MLMultiArrayDataTypeFloat32 error:&error];
int shape_size=1*src_w*src_z*src_y*src_x*sizeof(float);
memcpy( data.dataPointer, src, shape_size );
NSMutableDictionary *inputDict = [[NSMutableDictionary alloc] init];
inputDict[key] = data;
MLDictionaryFeatureProvider *inFeatures = [[MLDictionaryFeatureProvider alloc] initWithDictionary:inputDict error:&error];

mlmodelに対してpredictionFromFeaturesを呼び出すことで推論します。

id outFeatures = [model predictionFromFeatures:static_cast(inFeatures) error:&error];

処理結果はMLFeatureProviderで出力されます。ディクショナリのキーは出力層の名前になります。

NSSet *keys = [outFeatures featureNames];
MLFeatureValue *value = [outFeatures featureValueForName:key];
MLMultiArray* res=value.multiArrayValue;
double* fsrc = (double*)res.dataPointer;
float* fdst = (float*)dest;
for( int i=0; i<res.count; i++ ){
	fdst[i] = (float)fsrc[i];
}


predictionFromFeaturesとMLFeatureProviderの使い方はcoremltools/coremlpython/のソースコードを参照してください。

CoreMLの出力はdoubleですが、内部的にはMetalPerformanceShaderでhalf floatで実行されるそうです。How can I use half floats with CoreML neural nets?

CaffeModelをmlmodelに変換する

CaffeModelからCoreMLで使えるmlmodelに変換するにはcoremltoolsを使います。

pip install coremltools


PIPが古い場合はpipをupgradeします。

pip install --upgrade pip


以下のスクリプトをpython3で実行することでcaffemodelをmlmodelに変換します。

import coremltools
coremlmodel = coremltools.converters.caffe.convert(
        ("lenet.caffemodel", "lenet.prototxt"))
coremlmodel.save("lenet.mlmodel")


python3 coreml.py 

================= Starting Conversion from Caffe to CoreML ======================
Layer 0: Type: 'Input', Name: 'data'. Output(s): 'data'.
Ignoring batch size and retaining only the trailing 3 dimensions for conversion. 
Layer 1: Type: 'Convolution', Name: 'conv1'. Input(s): 'data'. Output(s): 'conv1'.
Layer 2: Type: 'Pooling', Name: 'pool1'. Input(s): 'conv1'. Output(s): 'pool1'.
Layer 3: Type: 'Convolution', Name: 'conv2'. Input(s): 'pool1'. Output(s): 'conv2'.
Layer 4: Type: 'Pooling', Name: 'pool2'. Input(s): 'conv2'. Output(s): 'pool2'.
Layer 5: Type: 'InnerProduct', Name: 'ip1'. Input(s): 'pool2'. Output(s): 'ip1'.
Layer 6: Type: 'ReLU', Name: 'relu1'. Input(s): 'ip1'. Output(s): 'ip1'.
Layer 7: Type: 'InnerProduct', Name: 'ip2'. Input(s): 'ip1'. Output(s): 'ip2'.
Layer 8: Type: 'Softmax', Name: 'prob'. Input(s): 'ip2'. Output(s): 'prob'.

================= Summary of the conversion: ===================================
Detected input(s) and shape(s) (ignoring batch size):
'data' : 1, 28, 28

Network Input name(s): 'data'.
Network Output name(s): 'prob'.

Xcodeに取り込むと以下のように見えます。

xcode_lenet


アプリに組み込む場合、Xcodeでは拡張子がmlmodelと表示されますが、アプリからはmlmodelcとしてアクセスする必要があります。mlmodelcにはXcodeが自動的に変換します。mlmodelcには手動で変換することもできます。

/Applications/Xcode.app/Contents/Developer/usr/bin/coremlc compile in.mlmodel out.mlmodelc

Compile .mlmodel to .mlmodelc for Xamarin.iOS?

VAEによる正常品のみからの不良品検出

製造工程の不良品を検出する場合、製造工程の不良率は低いため、不良画像をなかなか集めることができません。そのため、正常品のみから不良個所を判定する手法が求められており、AutoEncoderを使用する方法が提案されています。

AutoEncoderでは、入力画像の特徴を表すベクトルを学習します。Encoderでは入力画像を低次元の潜在変数zに写像します。Decoderでは潜在変数zから元解像度まで復元します。学習では、X->Encoder->z->Decoder->xと計算し、Xとxの誤差を最小化します。

AutoEncoderは正常品から学習しているため、不良品の特徴は捉えることができません。そのため、AutoEncoderの出力と、不良品の画像の差分を取ることで、不良個所を判別することができます。

AutoEncoderのzを直接学習せず、正規分布のパラメータを学習するようにしたのがVAEです。VAEを使用してMNISTで不良品検出をするには、以下の記事が参考になります。

Variational Autoencoderを使った画像の異常検知 前編

Colaboratoryに上記サイトのコードをコピペして実行してみます。実行結果が以下の画像です。画像上(old)がVAE、画像下(new)がVAEの損失関数をMVAEのみにしたバージョンです。1の画像を正常画像、9の画像を異常画像として、濃い青の部分が異常個所になります。このように、正常画像のみから異常画像を検出できることがわかります。

2dim


潜在変数zを2次元から4次元に変更して実行してみます。こちらの方が精度が高いように思われます。

4dim


8次元版。次数を上げすぎるとすべての特徴を表現できてしまい、異常検知が困難になります。

8dim


学習自体は正常画像のみで実行できますが、潜在変数をどれくらいの次元数に設定するかの判断のために、いくつかは不良画像が必要かと思われます。

個人識別のための顔認識の学習済みモデル

個人識別のために顔認識を行う場合、FineTuningを行う方法と、FeatureExtractorを使う方法があります。

前者は、予め決まっている人物を識別したい場合に使用し、VGG16等をベースにFineTuningを行っておき、学習後の学習済みモデルを使用して人物を特定します。後者は再学習は不要な方法であり、事前に膨大な顔画像で学習させておいた学習済みモデルを使用して、その中間層のデータを取得することで、顔の特徴を示す特徴ベクトルを取得し、認識したい人物のベクトルと、カメラから取得した人物のベクトルとの距離を計算することで個人を識別します。

CNNをFeatureExtractorとして使う場合の学習済みモデルとしては、VGG Faceがあります。これは、2015年に公開されており、ネットワーク構造はVGG16です。しかし、VGG Faceは商用利用不可となっています。

そこでTensorFlowで動く商用利用可のモデルとして2017年に公開されたのが、FaceNetです。ネットワーク構造はInception ResNetとなっています。

2018年にはVGG Face2が公開されました。VGG Faceから精度が向上し、商用利用可のライセンスとなっています。ネットワーク構造はResNetとSENetの2バージョンがあります。

VGG Face2のデータセットはクリーニングされており精度が高いため、FaceNetの方でもVGG Face2のデータセットで再学習が行われ、精度が改善しています。

KerasでVGG FaceおよびVGG Face2を使うには、keras-vggfaceを使用することができます。VGG16、ResNet、SENetに対応しています。

test.pyをコメントアウトして実行すると、以下のようにデータセットの中から最も近い人を表示します。

('\n', 'RESNET50')
('\n', array([[1.2077236e-07, 6.9855632e-06, 9.1819614e-01, ..., 1.4105116e-07,
        9.3622623e-07, 9.7075758e-07]], dtype=float32))
('\n', 'Predicted:', [[[' A._J._Buckley', 0.91819614], [' Billy_Butler', 0.007661988], [' Chris_Hardwick', 0.007469104], [' A._J._Pierzynski', 0.0045922087], [' Michael_Voltaggio', 0.0044681374]]])
.('\n', 'VGG16')
('\n', array([[9.7901160e-01, 4.9870639e-08, 7.0865167e-07, ..., 5.4379225e-08,
        7.6642658e-07, 3.7070203e-07]], dtype=float32))
('\n', 'Predicted:', [[['A.J._Buckley', 0.9790116], ['David_Denman', 0.0014457349], ['Carmine_Giovinazzo', 0.0008676527], ['Robert_Buckley', 0.0007245681], ['Eddie_Cahill', 0.00041833066]]])

特徴ベクトルを取得するには、include_top=FalseにしてFC層を除外した上で推論します。VGG Face(VGG16)の場合は4096次元、VGG Face2(ResNet)の場合は2048次元になります。

model = VGGFace(include_top=False, input_shape=(224, 224, 3), pooling='avg',model='vgg16')
print model.predict(x)[0]

IMDB-WIKIによる高精度な年齢・性別推定

現在、公開されている学習済みモデルの中で、最も高精度な年齢・性別推定器であると言われているのが、IMDB-WIKI – 500k+ face images with age and gender labelsです。

imdb


Adience Benchmarkにおける認識精度は性別で91%、年齢で64%です。(Understanding and Comparing Deep Neural Networks for Age and Gender Classification

VGGベースで実装されており、モデルサイズは500MBと大きいのですが、NVIDIA ChaLearn LAP 2015 Best Paper Awardを獲得しており、miniXceptionやage/gender.netに比べ、とても安定した出力を得ることができます。

年齢に関しては0歳〜100歳までの推定確率が出力されるため、年齢ベクトルと内積することで推定年齢を出力します。性別に関してはfemaleとmaleの2カテゴリです。

学習は50万枚の顔画像を使用しており、データセットとラベルもダウンロード可能です。

FDDBのアノテーションを可視化

FDDB + YoloSmallを使用した顔認識の再学習に使用するアノテーションを可視化してみました。



再学習のコードと学習済みモデルは以下にあります。
abars/YoloKerasFaceDetection

VGG16で顔検索エンジンを作る

VGG16で顔画像から特徴量を抽出して顔検索エンジンを作ってみました。

demo

(image from adience_benchmark)

まず、AdienceBenchmarkOfUnfilteredFacesForGenderAndAgeClassificationから顔のデータベースをダウンロードし、feature_extract.pyでImagenetで学習したVGG16の4096次元の特徴ベクトルを取得します。

次に、YoloFaceでWebカメラもしくは画像から顔を検出し、VGG16の4096次元の特徴ベクトルを取得し、事前に計算しておいた顔のデータベースの特徴量とのベクトル間の距離を計算します。ベクトル間の距離が近い順に5件、検索結果として表示しています。

face_search.py captureを使うと、自分の顔もデータベースに登録することができます。再学習不要で認識対象を動的に追加できるので、エッジ側で使うには向いているかなと思います。

abars/FaceSearchVGG16

Kerasで表情を検出する

oarriaga/face_classificationに表情を検出する学習済みモデルがあります。
emotion

FER2013 datasetをベースに学習しており、以下の7カテゴリを検出可能です。

'angry'
'disgust'
'fear'
'happy'
'sad'
'surprise'
'neutral'

ネットワークは64x64x1を入力するXceptionV1となっています。入力画素のレンジは(-1,1)です。容量も1MB未満でとても軽量なので、モバイルでも動作させやすいと思います。

他の表情を検出する学習済みモデルとしては、Emotion Classification CNN - RGBがあり、こちらは227x227x3を入力するモデルです。両方試した印象としては、oarriaga/face_classificationの方がneutralとhappyを正しく認識するように思えました。

この結果を見ると、顔の認識はグレースケールに変換してしまった方が過学習を抑制できてロバストになるのかなと思いました。

人物検出用のデータセット一覧

DarknetやKerasで使える人物検出用のデータセットをまとめました。

Unfiltered faces for gender and age classification dataset

https://www.openu.ac.il/home/hassner/Adience/data.html#agegender

顔画像に対して、人物の年齢と性別が記載されたデータセット。1.76GB。11524枚。

face

学習済みモデル。
Age and Gender Classification using Convolutional Neural Networks

FDDB Dataset

http://vis-www.cs.umass.edu/fddb/

写真に対して、顔の位置が記載されたデータセット。顔の位置は楕円で記載されている。同時に映る人物が少なめなのでYolo向き。75MB。2845枚。

fddb

学習済みモデル。
dannyblueliu/YOLO-version-2-Face-detection

Widerface Dataset

http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/

写真に対して、顔の位置が記載されたデータセット。パレードなど、同時に映る人物が多めなので、Yolo向きではないかも。1.56GB。12880枚。

widerface

IMDB-WIKI Dataset

https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/

Wikiの顔画像に対して生年月日と撮影日が記載されたデータセット。Download Faces Onlyで7GB。460723枚。ラベルはMatlab形式。

imdb_wiki

学習済みモデル
Real-time face detection and emotion/gender classification using fer2013/imdb datasets with a keras CNN model and openCV.

ラベルの展開方法
顔画像から年齢・性別を推定するためのデータセットIMDB-WIKI

Vivahand Dataset

http://cvrr.ucsd.edu/vivachallenge/index.php/hands/hand-detection/

車内の画像に対して、手の位置が記載されたデータセット。運転席と助手席もラベル分けされている。3.05GB。5500枚。

vivahand

Hand Dataset

http://www.robots.ox.ac.uk/~vgg/data/hands/

Matlab形式だったので評価できず。194.5MB。4069枚。

handdataset

VGG16におけるKerasの前処理でmeanを引くかどうか

Fine-tuning a Keras model. Updated to the Keras 2.0 APIなど、Kerasのチュートリアルでは、学習画像に以下のような前処理を行っています。

# prepare data augmentation configuration
train_datagen = ImageDataGenerator(
    rescale=1. / 255,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True)

test_datagen = ImageDataGenerator(rescale=1. / 255)

これは、VGG16の学習済みのネットに対して、RGB順で0〜1.0の値を入力しています。

しかし、KerasにおけるVGG16の重みは、Oxford大学のVGGによりCreative Commons Attribution Licenseの下で公開されたものを移植しています。そのため、本来、期待する前処理は、BGR順で0〜255の値からImageNetのmeanを引いた値となります。ただし、CaffeModelからKerasModelへの変換の過程でRGB順への補正は行われているようですので、RGB順で0〜255がKerasとして期待する入力となります。

def preprocess_input(img):
  #img = img[...,::-1]  #RGB2BGR
  #img = img - (104,117,123) #BGR mean value of VGG16
  img = img - (123,117,104) #RGB mean value of VGG16
  return img

train_datagen = ImageDataGenerator(
   preprocessing_function=preprocess_input,
   shear_range=0.2,
   zoom_range=0.2,
   horizontal_flip=True
)

test_datagen = ImageDataGenerator(
   preprocessing_function=preprocess_input,
)

それでは、この変更でどれくらい性能が変わるのでしょうか。AgeGender NetのAgeのClassification問題に対して比較してみました。

変更前(0〜1.0、RGB順、lr=0.01)
agegender_age_vgg16


変更後(0〜255 - mean、RGB順、lr=0.01)
agegender_age_vgg16_with_preprocseeing_rgb

変更後(0〜255 - mean、BGR順、lr=0.01)
agegender_age_vgg16_with_preprocess


結論としては、ラーニングレートが変わるだけで、どちらを使っても問題ないようです。VGG16の畳み込みで画素間差分を取っている過程でDC値の意味が消失しがちなのと、最後の内積のパラメータで調整できてしまうのではないかと思います。個人的には、正しい前処理をした方が一貫性があって気分はよいです。

尚、本件は、チュートリアルの掲示板でも議論になっていますが、特に結論は出ていないようです。

a-ozbek commented on 6 Feb 2017 •  edited 
Excuse me if this issue was brought up before about this script. I couldn't find a resolution to this in the comments.

In this script, during the fine-tuning, the "train_generator" and "validation_generator" do not seem to do VGG16 pre-processing which is

      # 'RGB'->'BGR'  
        x = x[:, :, :, ::-1]  
        # Zero-center by mean pixel  
        x[:, :, :, 0] -= 103.939  
        x[:, :, :, 1] -= 116.779  
        x[:, :, :, 2] -= 123.68  
Isn't it wrong to do fine-tuning of VGG16 without this pre-processing step?
aidiary commented on 16 Feb 2017
@a-ozbek

I have the same question.
I have experimented with and without this pre-processing and get the slightly better result in the case of without this pre-processing...

without pre-processing => val_acc = 93.5%@ epoch 50
with pre-processing      => val_acc = 92.8% @ epoch 50

TensorFlowでcudnn64_6.dllが見つからない

マイナーバージョンがDLL名に含まれているという仕様なので、新しいCUDNNを入れると、cudnn64_7.dllが入り、cudnn64_6.dllを使用しているTensorFlowでエラーが出ます。

Getting the given error while installing gpu version of tensorflow によると、リネームするとよいようです。

if you not get the cudnn64_6.dll, then use below method

you should rename the cudnn64_7.dll or cudnn64_8.dll as cudnn64_6.dll in the below path of your system

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\bin

Unpredictable CUDNN_STATUS_NOT_INITIALIZED on Windowsが出る問題はまだ解決していません。

Python3.5に対応したdarknet2caffe

marvis/pytorch-caffe-darknet-convertのdarknet2caffeがPython3.5に対応していなかったので、ForkしてPython2.7とPython3.5に対応したabars/darknet2caffeを作りました。

主な修正点は、printの記述方法の変更と、除算の//による整数化です。WindowsのTensorflowが3.5必須なのに対して、CoreMLが2.7必須と混在してきているのが悩ましいですね。

Kerasで顔画像から年齢と性別を推定する

Kerasで顔画像から年齢と性別を推定してみました。リポジトリはYoloKerasFaceDetectionです。データセットには、 AdienceBenchmarkOfUnfilteredFacesForGenderAndAgeClassificationを使用させて頂きました。

年齢と性別の推定には顔領域の画像を与える必要があるため、Webカメラを使用する場合、顔領域を検出して切り出す必要があります。一般にはOpenCVを使用しますが、OpenCVは眼鏡付きの顔の認識精度が低いため、今回、顔検出にはYoloを使用しています。顔検出の係数はYOLOライセンスのYOLO-version-2-Face-detectionを使用させて頂きました。そのため、Caffeも必要です。

学習済みモデルを同じフォルダにおいた後、以下のコマンドでWebカメラから年齢と性別の推定が可能です。

python agegender_demo.py keras

年齢のカテゴリは以下の8カテゴリです。

age 0-2
age 4-6
age 8-13
age 15-20
age 25-32
age 38-43
age 48-53
age 60-

性別のカテゴリは以下の2カテゴリです。

female
male

学習はVGG16をファインチューニングしました。学習のコードはagegender_train.pyにあります。

最初は年齢と性別を同時に認識させたのですが、うまく精度が出ませんでした。そのため、年齢と性別を別のネットワークで学習させるように変更しました。ただ、分解してもAge Netが過学習ぎみなので、もう少し追い込む必要がありそうです。

Age & Gender
agegender_agegender_vgg16
Age
agegender_age_vgg16

Gender
agegender_gender_vgg16


先行事例としては、Age and Gender Classification using Convolutional Neural Networksがあり、そこからTensorFlow + InceptionV3版のAge/Gender detection in Tensorflowがあります。また、MicrosoftのVison APIのデモがHow-Old.netにあります。

リファレンスモデルも以下のコマンドで実行できます。自己学習モデルは現状はまだ精度が出ていないので、Age and Gender Classification using Convolutional Neural Networksのリファレンスモデルを使用することをオススメします。

python agegender_demo.py caffe

実行すると以下のように顔検出と年齢・性別検出を行います。

demo
(出典:WIDER face dataset

VGG16を特徴検出器として使う話

VGG16というConvolutional Neural Networkがあります。VGG16は224x224x3画素の画像を入力して、1000クラスの推定確率を出力するネットワークです。3x3のカーネルの畳み込みと2x2のプーリングを繰り返すことで、4096次元のベクトルを計算し、最後の全結合層で1000クラスの推定確率を計算します。


vgg16
(出典:VGG in TensorFlow


VGG16は深いネットワークなため、全体を再学習するには十分なデータが必要です。そのため、1000クラスに含まれていないオリジナルな画像を学習させようとした場合、画像の量が不足してうまく認識できません。

そこで、最後の全結合層だけを再学習するのが転移学習、最後の全結合層とその直前の畳み込み層を再学習するのがファインチューニングです。(VGG16のFine-tuningによる犬猫認識 (2)

なぜこれがうまくいくのかといえば、VGG16の前半が膨大な画像で学習しているため、画像に特化した特徴検出器になっているためです。最後の全結合層だけを見ると、やっていることは、VGG16が計算した4096次元のベクトルと、クラスごとの4096次元のベクトルの内積です。つまり、特徴空間でコサイン類似度を計算していることになります。

これより、新しいクラスを認識させたい場合、全体を再学習する必要はなく、クラスに固有の4096次元のベクトルを用意すれば十分ということになります。これを応用すると、データベース内の全ての画像に対してVGG16で計算した4096次元のベクトルをデータベースに格納し、ユーザから入力された画像に対してVGG16で計算した4096次元のベクトルとの内積を取るだけで、画像検索エンジンを作ることができます。(1時間で画像検索エンジンを作る

結局のところ、CNNはKL変換のような基底系を計算しているわけで、KL変換との違いは非直交であることと、非線形関数が入っていることであるといえます。

もちろん、新しいクラスの画像が加われば、最適な基底系は変化するため、全結合層だけでなく、もう一段上の畳み込み層まで含めて再学習すれば、より精度は向上します。

しかし、一見、再学習が必要そうな個人認証のような分野に対しても、再学習せずに特徴量の内積だけで実装可能というのは、可能性が広がって面白いのではないかと思います。

VGG16を使用した検索エンジンのデモとしては、KawaiiSearchがあります。リポジトリはblan4/KawaiiSearchです。ファッションECなどでも使用できそうです。

68747470733a2f2f692e696d6775722e636f6d2f653962707757592e706e67


68747470733a2f2f692e696d6775722e636f6d2f6444414a4375592e706e67

また、VGG16を使用した顔検索をabars/FaceSearchVGG16で実験中です。

keras2caffeでFlattenを含むと変換できない

uhfband/keras2caffeで以下のネットワークがうまく変換できません。

  model.add(InputLayer(input_shape=input_shape))
  model.add(Conv2D(32, kernel_size=(3, 3),use_bias=False))
  model.add(Activation('relu'))
  model.add(Conv2D(64, (3, 3),use_bias=False))
  model.add(Activation('relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Dropout(0.25))
  model.add(Flatten())
  model.add(Dense(128))
  model.add(Activation('relu'))
  model.add(Dropout(0.5))
  model.add(Dense(num_classes))
  model.add(Activation('softmax'))

これは、Kerasがchannels_lastであるinput_shape=(224, 224, 3)、Caffeがchannels_firstであるinput_shape=(3, 224, 224)で画像を扱うため、keras2caffeの内部でtransposeしているためです。Flattenは元のベクトルをそのまま1次元に変換するため、channels_lastとchannels_firstが異なると、異なるベクトルに変換され、その後のDenseでのInnerProductで正しくない値になります。

このネットをうまく変換するには、FlattenのDenseでInnnerProductのweightを並び替える必要があります。今回のネットワークでは、パッチワーク的に以下の変換をかけると動作します。

elif layer_type=='Dense':
    caffe_net[name] = L.InnerProduct(caffe_net[outputs[bottom]], 
    	num_output=config['units'], weight_filler=dict(type='xavier'))
    
    if config['use_bias']:
        weight=np.array(blobs[0]).transpose(1, 0)
        print(weight.shape)
        if weight.shape[1]==9216:
            for i in range(128):
                weight[i]=np.array(weight[i].reshape(12,12,64).transpose(2,0,1).reshape(9216))
        net_params[name] = (weight, np.array(blobs[1]))
    else:
        net_params[name] = (blobs[0])


VGG16もFlattenの後にDenseが続く構造なため、同様の変換が必要です。変換を一般化するには、Flattenでlayer.input_shapeを保存しておき、その次のDenseでweightの並び替えを行います。

abars/keras2caffeの該当コミット

他、Keras2caffeはConvの中にactivationを含む場合や、InputLayerが存在しない場合に変換に失敗するため、いくつか修正が必要です。

上記修正は本家にプルリクエストを出してみました。
uhfband/keras2caffe/pull/1

---2018/1/2追記
マージして頂けましたので、最新版ではVGG16が動作します。

Kerasでアニメキャラ識別器を作る

すでに先行事例が多いですが、Kerasでアニメキャラ識別器を作りました。ソースコードはgithubに上げてあります。
abars/AnimeFaceClassifier

result

Hatsune Miku / Crypton Future Media inc. / CC BY-NC

OpenCVでアニメキャラの顔を検出後、CNNにかけて、キャラを当てます。データセットはanimeface-character-datasetを使用させて頂きました。

32x32に縮小した画像を入力して2層の畳み込みを行うsmall_cnnで学習をかけた場合、認識精度は70%程度です。学習はMac Pro 2013のFireProD300で2日程度かかりました。

認識精度を上げるため、VGG16を使用したファインチューニングを実験し、テストデータセットでの認識精度は82%まで上がりました。ただ、small_cnnとは異なり、初音ミクとランカ・リーを間違えることがあります。InceptionV3なども試した方がよいかもしれません。
Search
Profile

abars

アプリとWEBサービスを開発しています。最近はUnityとGAE/pyが主戦場。

ブラウザ向けMMOのメトセライズデストラクタ、イラストSNSのイラストブック、東証の適時開示情報を検索できるTDnetSearchを開発しています。

かつてエンターブレインのTECH Win誌でATULADOを連載しました。

サイト:ABARS
Twitter:abars
Github:abars

Twitter
TopHatenar
HotEntry
Counter

アクセス解析付きカウンター。あなたのBLOGにもどうですか?登録はこちらから。

TOP/ BLOG/ LECTURE/ ONLINE/ RUINA/ ADDON/ THREAD/ METHUSELAYZE/ IPHONE/ MET_IPHONE/ ENGLISH/ RANKING