エイバースの中の人

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

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

AppEngineのdevserverでSearchIndexを保持する

AppEngineのdevserverでは、サーバを再起動するとFullTextSearchApiのSearchIndexが初期化されます。これを初期化されないようにするには、devserverのExtraFlagsにsearch_indexes_pathを与えます。

--search_indexes_path=/Users/abars/gae_datastore_tmp/tdnet.search


また、データストアの保存先フォルダを変えるのも、同様に、--datastore_pathを使用することもできます。

--datastore_path=/Users/abars/gae_datastore_tmp/tdnet.datastore

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が動作します。

Android8で権限が必要なアプリが起動しない

Unity 5.5.4f1でビルドしたアプリをAndroid8で起動した場合、Unityのパーミッションの設定を行うコードに問題があり、アプリが起動せず、ブラックスクリーンのままになります。

この問題は、設定->アプリ->権限、から手動で権限を設定すると解消します。また、Unity 5.5.5p1で解消されています。

(945338, 946061) - Android: Fixed black screen on startup on Android Oreo devices.
Game does not work on new Android Oreo
Android8.0 Oreoで起動ができなくなる

Android7以降でSocialConnectorが動作しない

SocialConnectorでは以下のようにuri.fromFileでファイル共有を行います。

var uri = new AndroidJavaClass ("android.net.Uri");
var file = new AndroidJavaObject ("java.io.File", textureUrl);
intent.Call<AndroidJavaObject> ("putExtra", "android.intent.extra.STREAM", uri.CallStatic<AndroidJavaObject> ("fromFile", file));


しかし、Android7から権限管理が強化されたため、画像共有時にAndroidJavaException: android.os.FileUriExposedExceptionが発生します。この問題を解決するには、FileProviderを使用する必要があります。

int FLAG_GRANT_READ_URI_PERMISSION = intent.GetStatic<int>("FLAG_GRANT_READ_URI_PERMISSION");
intent.Call<AndroidJavaObject>("addFlags", FLAG_GRANT_READ_URI_PERMISSION);

AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject unityContext = currentActivity.Call<AndroidJavaObject>("getApplicationContext");

string packageName = unityContext.Call<string>("getPackageName");
string authority = packageName + ".fileprovider";

AndroidJavaObject fileObj = new AndroidJavaObject("java.io.File", textureUrl);
AndroidJavaClass fileProvider = new AndroidJavaClass("android.support.v4.content.FileProvider");
AndroidJavaObject uri = fileProvider.CallStatic<AndroidJavaObject>("getUriForFile", unityContext, authority, fileObj);

intent.Call<AndroidJavaObject>("putExtra", "android.intent.extra.STREAM", uri);


AndroidManifestに権限を追加します。

 <!-- Add fileprovider for android n -->
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="バンドルID.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"></meta-data>
</provider>


Plugins/Android/res/xmlにfile_paths.xmlを作成し、以下のように共有フォルダ指定をします。

<?xml version="1.0" encoding="utf-8"?>
<paths>
      <external-cache-path name="external_files" path="."/>
      <external-path name="external_files" path="."/>
</paths>


しかし、UnityとAndroid SDK 26以降を合わせて使うと、Plugins/Android/resフォルダが存在するとREAD_PHONE_STATEパーミッションが追加されます。(READ_PHONE_STATE permission added when using SDK tools 26.0.2)Unity5.5.4f1からUnity5.5.5p2に上げても回避はできませんでした。

尚、ビルド済みのapkからManifestを抽出するにはapktoolを使用します。

apktool d input.apk

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なども試した方がよいかもしれません。

Yoloで顔検出を行う

YOLO-version-2-Face-detectionにあるcfgファイルとweightファイルを使用すると、Yolo v1を使用して顔検出を行うことができます。

実行コマンド
./darknet yolo test yolo-face.cfg yolo-face_final.weights data/person.jpg

公式デモ


学習にはFDDB使用しているようです。顔画像のデータセットは、WiderFaceDatasetも有名ですね。

このネットはAnchorが含まれていないYolo v1の形式になっています。darknet2caffeを使用すれば、caffemodelに変換可能です。変換後のCaffeModelは178.1MBです。

python darknet2caffe.py yolo-face.cfg yolo-face_final.weights face.prototxt face.caffemodel

変換後の係数は、caffe-yoloで動作します。その際、num_class=1、grid_size=11に対応するよう、yolo_main.pyを書き換える必要があります。

python yolo_main.py -m face.prototxt -w face.caffemodel -i faces.jpg

初音ミクの顔も検出できたので、意外とアニメ顔もいけるかもしれません。

OpenCVのFace DetectionはFace Detection using Haar Cascadesというアルゴリズムを使用していますが、Yoloが動く環境であれば、OpenCVを使用しなくてもより手軽に顔検出を行うことができそうです。



また、現在、再学習をYoloKerasFaceDetectionで実験中です。

イーサリアムゲーム CryptoKittiesの流れ

CryptoKittiesは仮想通貨の一つであるイーサリアムでネコを育てるゲームです。PCブラウザのみ対応しています。

イーサリアムはビットコインとは異なり、スマートコントラクトによる付加データの保存に対応しています。そのため、ゲームのキャラクターなどのデータも保持することができます。

CryptoKittiesでサインアップします。イーサリアムはChrome PluginのMeta Maskを経由して使用するため、Meta Maskのインストールが必要です。

ログインすると、最初のネコの購入を促されます。

top


ネコにはチャートがあります。

buy


購入を選ぶとMeta Maskから支払いを求められます。

metamask


0.06ETHだと、5000円近く必要なようです。高い。

ということでゲームの開始を諦めました。仮想通貨の高騰前に遊びたかった。

Macでkerasを使う

インストール

KerasとTensorflowのインストールを行います。

pip install keras
pip install tensorflow

OpenCLバックエンド用にPlaidMLを入れます。

pip install plaidml-keras
plaidml-setup 

サンプルの実行

MNISTのサンプルコードをダウンロードします。
keras/examples/mnist_cnn.py

import kerasの前にバックエンドを選択します。

import plaidml.keras
plaidml.keras.install_backend()

実行します。

python mnist_cnn.py

学習が行われます。

python mnist_cnn.py 
x_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
INFO:plaidml:Opening device "amd_radeon_hd_-_firepro_d300_compute_engine.1"
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
INFO:plaidml:Analyzing Ops: 85 of 285 operations complete
59904/60000 [============================>.] - ETA: 0s - loss: 0.3243 - acc: 0.9024INFO:plaidml:Analyzing Ops: 85 of 285 operations complete
60000/60000 [==============================] - 43s - loss: 0.3241 - acc: 0.9025 - val_loss: 0.0758 - val_acc: 0.9751
Epoch 2/12
60000/60000 [==============================] - 34s - loss: 0.1109 - acc: 0.9673 - val_loss: 0.0546 - val_acc: 0.9831
Epoch 3/12
60000/60000 [==============================] - 34s - loss: 0.0847 - acc: 0.9753 - val_loss: 0.0444 - val_acc: 0.9847
Epoch 4/12
60000/60000 [==============================] - 34s - loss: 0.0723 - acc: 0.9789 - val_loss: 0.0377 - val_acc: 0.9873
Epoch 5/12
60000/60000 [==============================] - 34s - loss: 0.0622 - acc: 0.9813 - val_loss: 0.0350 - val_acc: 0.9882
Epoch 6/12
60000/60000 [==============================] - 34s - loss: 0.0563 - acc: 0.9839 - val_loss: 0.0326 - val_acc: 0.9890
Epoch 7/12
60000/60000 [==============================] - 34s - loss: 0.0525 - acc: 0.9839 - val_loss: 0.0314 - val_acc: 0.9888
Epoch 8/12
60000/60000 [==============================] - 34s - loss: 0.0474 - acc: 0.9857 - val_loss: 0.0302 - val_acc: 0.9892
Epoch 9/12
60000/60000 [==============================] - 34s - loss: 0.0446 - acc: 0.9868 - val_loss: 0.0312 - val_acc: 0.9896
Epoch 10/12
60000/60000 [==============================] - 34s - loss: 0.0418 - acc: 0.9878 - val_loss: 0.0304 - val_acc: 0.9897
Epoch 11/12
60000/60000 [==============================] - 34s - loss: 0.0379 - acc: 0.9886 - val_loss: 0.0294 - val_acc: 0.9906
Epoch 12/12
60000/60000 [==============================] - 34s - loss: 0.0377 - acc: 0.9890 - val_loss: 0.0295 - val_acc: 0.9901
Test loss: 0.0294891050935
Test accuracy: 0.9901

重み保存と変換

重み保存と読み込みをするには、以下のコマンドを使用します。
Keras FAQ: Kerasに関するよくある質問

json_string = model.to_json()
model.save_weights('my_model_weights.h5')

from keras.models import model_from_json
model = model_from_json(json_string)
model.load_weights('my_model_weights.h5')

重み変換をするには、以下のスクリプトを使用します。
keras2caffe (MIT license)
import keras2caffe
keras2caffe.convert(model,"my_model.prototxt","my_model.caffemodel")

尚、重み変換をする場合はInputLayerが存在しないとエラーになります。また、activationは分離して記載する必要があります。KerasのSequentialモデルでInputLayerを明示的に追加するを参考に以下のようにInputLayerの追加と、activationの分離を行います。

model = Sequential()
model.add(InputLayer(input_shape=input_shape))
model.add(Conv2D(32, kernel_size=(3, 3)))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
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'))

Fine Tuning

少ない画像から画像分類を学習させる方法(kerasで転移学習:fine tuning)
Kerasではtrainフォルダとvalidationフォルダにカテゴリ別のフォルダを作成し、画像を入れておくと、カテゴリ識別を学習させることができます。例えば、AnimeFace Character Datasetのthumbを適当なスクリプトでtrainとvalidationに分解します。また、その他のカテゴリを学習させるには、Bing Search APIが教師データ作成に便利です。

Yolo

basic-yolo-kerasを使用してYoloの係数を学習することができます。実際の学習を走らせる時はconfig.jsonのwarmup_epochsを0にします。ただし、plaidmlのバックエンドだと動作しないため、実用的な速度で動かすにはCUDAが必要です。

python train.py -c config.json
python predict.py -c config.json -w full_yolo_raccoon.h5 -i raccoon_dataset/images/raccoon-1.jpg

モナコインをマイニングしてみる

モナコインは2013年12月に誕生した日本初の暗号通貨です。

monacoin

仮想通貨は最初に正解を当てたクライアントのみが報酬を得るため、ソロマイニングをすると報酬が安定しません。そのため、複数のユーザでギルドのようなものを組み、報酬を分配することで、安定した報酬の獲得を目指します。

以下の手順でモナコインをマイニング可能です。

(1) Monappyでモナコインのウォレットを作成
(2) マイニングプール(ASICpoolやVIPpool等)のアカウントを作成
(3) cpuminerのインストール
(4) マイニングの実行

cpuminerは以下のリポジトリからCloneしてMakeします。lyra2rev2に対応している必要があるため、pooler/cpuminetではなく、tpruvot/cpuminer-multiが必要です。
https://github.com/tpruvot/cpuminer-multi

MacOSでビルドする場合、READMEに記載のCFLAGS="*-march=native*"を付けるとconfigureでgccが見つからないエラーが出るため、外します。シンボリックリンクを作成してOpenSSLにパスを通します。_neoscrypt_blkcpyでリンクエラーが発生するため、fix os x build with asm #14をマージします。

export PATH=/usr/local/Cellar/openssl/1.0.2m/bin:$PATH
cd /usr/local/include 
ln -s ../opt/openssl/include/openssl .


cpuminerは以下のように起動することができます。Webloginはユーザ名、WorkerNameはワーカー名、WorkerPasswordはワーカーパスワードが対応します。Stratumはマイニングのプロトコルです。lyra2rev2はハッシュアルゴリズムです。

./cpuminer -a lyra2rev2 -o stratum+tcp://stratum.asicpool.info:6969 -u Weblogin.WorkerName -p WorkerPassword


昔のモナコインはLiteCoinと同じscryptを使用していましたが、現在はハードフォークされており、lyra2rev2になっているため、-a scripyでは採掘できません。lyra2REv2は多くのメモリを必要とする構造になっているため、単純なSHA2を使用するビットコインに比べて、ASIC化が難しいと言われています。

モナコインの情報は、Ask Monaから取得可能です。マイニングプールはオープンソースのphp-mposを使用していることが多いようです。

[2017-12-09 20:53:28] Starting Stratum on stratum+tcp://stratum.asicpool.info:6969
[2017-12-09 20:53:28] 8 miner threads started, using 'lyra2rev2' algorithm.
[2017-12-09 20:53:32] Stratum difficulty set to 1 (0.00391)
[2017-12-09 20:53:32] lyra2rev2 block 1184360, diff 88512.700
[2017-12-09 20:53:33] CPU #0: 77.87 kH/s
[2017-12-09 20:53:33] CPU #5: 77.54 kH/s
[2017-12-09 20:53:33] CPU #7: 77.33 kH/s
[2017-12-09 20:53:33] CPU #2: 77.10 kH/s
[2017-12-09 20:53:33] CPU #1: 77.11 kH/s
[2017-12-09 20:53:33] CPU #6: 77.09 kH/s
[2017-12-09 20:53:33] CPU #3: 76.90 kH/s
[2017-12-09 20:53:33] CPU #4: 76.18 kH/s
[2017-12-09 20:53:36] accepted: 1/1 (diff 0.013), 616.59 kH/s yes!
[2017-12-09 20:53:42] CPU #1: 75.16 kH/s
[2017-12-09 20:53:42] accepted: 2/2 (diff 0.004), 615.18 kH/s yes!
[2017-12-09 20:53:53] CPU #1: 74.08 kH/s
[2017-12-09 20:53:53] accepted: 3/3 (diff 0.016), 614.10 kH/s yes!

yesが出れば掘れています。

2017年に買って良かったもの

今年もいろいろ買いました。今年はデバイス系の当たり年だった気がします。

Logicool MX Master 2S + KX1000s


マルチペアリング対応のマウスとキーボードです。複数のPCで共用できるため、デスクトップがすっきりします。Flowは動いたり動かなかったり微妙なところがあるのですが、キーボードとマウスのボタンでも切り替えられるので、Flowが使えなくても満足度は高いです。マウスについては、マラソンマウスからの乗り換えですが、ポインターの滑らかさと、スクロールホイールのメカニカルなスクロールモードの切り替えがとてもよく、会社用と家用の二台を買ってしまいました。キーボードはAppleのKeyboardからの乗り換えですが、Magic keyboard的な軽さと剛健さがあり、こちらも質感高くてよい感じです。WindowsとMacの両方の特殊キーに対応しており、なおかつ右上にある離席用のロックボタンも便利です。



iPhone8 / iPhoneX


ついにiPhoneがQiに対応しました。Xを待ちきれずに8も買ってしまいました。ローズゴールド風のゴールドの質感はとてもよかったです。XはXで重めですが、高級感があります。どちらも満足度の高い端末だと思います。



GalaxyS8


近年で最高のAndroid端末だと思います。Qiにも対応。GearVRの発熱問題がついに解消しました。



LG Ultrafine 5k display


MacPro 2013にも繋がる5k displayです。ソリッドでシンプルな形状と、Apple公式ならではのDellの4kよりも安定した表示が魅力です。なにげに、NUCなど、Thunderbolt3を搭載したWindows PCにも接続ができます。



GPD Pocket


打ち合わせ用です。Unityを動かすには流石に重すぎましたが、ブラウジングには十分です。481gでWindwos10が動くのはすごいです。



Gigabyte AorusゲームボックスGTX 1070


ディープラーニング用に購入。Thunderbolt経由でGPUが使えます。かなりコンパクトで静かなのに、CPUの240倍近い速度で学習できて便利です。現在はNUC + Windows10で使用していますが、将来的にはMac Book Pro + High Sierraにも接続してみたいです。



Kindle Oasis


物理的なページめくりボタンは便利です。Kindle Voyageの感圧ボタンよりも快適になりました。Kindle Voyageと同等の重量とサイズで、ディスプレイが大型化しており、コミックがより快適に読めるようになりました。



Bose Quiet Control 30


AirPodsではなくこちらを買いました。ノイズキャンセリングと、バッテリーの持ち、使っていない時に投げ出してもよい気軽さがよいです。飛行機などにも気軽に持っていける携帯性があります。



ニーアオートマタ


キャラデザ、世界感、音楽、全てが神ゲーでした。



銀河英雄伝説


言わずと知れた名作。いつも続刊を楽しみに待っています。

Windows10にDarknetを入れる

Gigabyte Aorusゲームボックスを使うため、Windows10にDarknetを入れたのでメモです。PCはNUCです。



Thunderbolt Driverのインストール


NUCのサイトからtbt_win10_64_17.2.71.250.zipをダウンロードしてインストール。

VisualStudio 2015のインストール


MSDNからVisual Studio 2015をダウンロード。2017のダウンロードページに飛ばされるので、最下部の以前のバージョンからダウンロード。(以前のバージョンをお使いになりたいですか?

OpenCVのインストール


opencv/opencvからopencv-3.1.0.exeを展開。opencv/build/vc14/binにパスを通す。

CUDAのインストール


CUDA Toolkit Downloadからインストール。バージョンは9.0ではなく8.0が必要。cuDNNをダウンロード。登録が必要。cuda/binにパスを通す。尚、後述するようにDarknetのcuDNN対応がまだ微妙なため、Darknetだけを使用する場合はCUDA Toolkitだけをインストールするのでもよい。Tensorflowを使用する場合は必要。

Darknetのインストール


AlexeyAB/darknetをClone。build/darknet/darknet.slnを開き、opencv/build/vc14/libとopencv/build/includeとcuda/includeとcuda/libにパスを通す。CUDNNを有効にすると正しく認識が動作しないため、CUDNNのdefineを外し、GPUのみdefineを残す。ビルドするとx64フォルダにdarknet.exeができる。

パフォーマンス


WindowsのパフォーマンスモニタではCUDAのGPU稼働率を見ることはできないが、GPUが熱を持っているので稼働はわかる。TinyYoloで100Epocが、CPU(Xeon)だと12時間、GPU(1070)だと3分。240倍の性能。

Anacondaのインストール


Anacondaの公式サイトから3.6のバージョンを入手。Python 3.5の環境に変更。

conda install python=3.5
conda install pip


Kerasのインストール


以下のコマンドでKerasとTensorflowをインストール。

pip install keras
pip install tensorflow-gpu


Pytorchのインストール


以下のコマンドでtorchをインストール。

conda install -c peterjc123 pytorch 


Caffeのインストール


BVLC/caffeからビルド済みバイナリのVisual Studio 2015, CPU only, Python 3.5: Caffe Releaseをダウンロード。Pythonから使えるようにするためにcaffe/python/caffeをc:/users/user_bane/Anaconda/Python3.5/Lib/site-packagesにコピー。import caffeでDLL load failedが発生するため、caffe-windowsからthirdparty20170624.rarをダウンロードして./windows/thirdparty/binsにパスを通す。

Darknetを使用してYoloの係数を学習する

Darknetの概要


DarknetはYoloの開発者が開発しているディープラーニングのフレームワークです。C++で記述されており、簡単にビルドすることができます。Yoloの歴史についてはYOLO9000: Better, Faster, Strongerの資料が詳しいです。

Darknetを使用してYoloの係数を学習するチュートリアルは以下が詳しいです。
How to train YOLOv2 to detect custom objects

Darknetのインストール


環境構築はMacかUbuntuが簡単です。まず、リポジトリをCloneします。

git clone https://github.com/AlexeyAB/darknet.git

GNUmakeを使用してmakeします。darknetのバイナリが生成されます。

make

nVidiaのGPUであればMakefileを書き換えることでCUDAを有効にすることができます。今回はAMDのGPUを搭載したMacなため、CPUで動かします。

学習させるデータの準備


学習させるデータをダウンロードします。上記記事のサンプルデータがテストには最適です。
https://timebutt.github.io/content/other/NFPA_dataset.zip

ただし、pos-234.jpgの拡張子が大文字のJPGになっているため、小文字のjpgに変換する必要があります。また、train.txtとtest.txtの改行コードをWindows StyleからLinux Styleに変換する必要があります。

データセットには、入力画像のjpgと、バウンディングボックスのtxtが含まれています。バウンディングボックスのtxtのフォーマットは以下のようになります。画像のどの位置にどのカテゴリのオブジェクトが存在するかが記載されています。同様の形式で、jpgとtxtのペアを準備することで、任意の画像で学習させることができます。

[category number] [object center in X] [object center in Y] [object width in X] [object width in Y]

YoloV2での学習


次に、obj.data、obj.names、yolo-obj.cfgを準備します。

obj.dataには学習のための基本情報が含まれます。上から、識別クラス数、入力画像のファイルリスト、評価画像のファイルリスト、クラス名称、係数の出力ディレクトリです。trainやvalidはdarknetのバイナリからの相対パスです。

classes=1
train=data/nfpa/train.txt
valid=data/nfpa/test.txt
names=data/nfpa/obj.names
backup=backup/

obj.namesにはカテゴリ名称が含まれます。

NFPA

yolo-obj.cfgにはYoloのネットワーク構成が含まれます。yolo-voc.cfgをコピーして、batchを64に、subdivisionsを8に、classesを識別させたいクラス数に書き換えます。最終段のfiltersを(classes + 5)*5に書き換えます。

[net]
# Testing
batch=64 #書き換える
subdivisions=8 #書き換える
# Training
# batch=64
# subdivisions=8
height=416
width=416
channels=3

省略...

[convolutional]
size=1
stride=1
pad=1
filters=30 #書き換える
activation=linear

[region]
anchors =  1.3221, 1.73145, 3.19275, 4.00944, 5.05587, 8.09892, 9.47112, 4.84053, 11.2364, 10.0071
bias_match=1
classes=1 #書き換える
coords=4
num=5
softmax=1
jitter=.3
rescore=1

省略...

収束を速めるため、初期係数列をダウンロードしておきます。
darknet19_448.conv.23

最後に、以下のコマンドで学習を行います。

./darknet detector train data/nfpa/obj.data data/nfpa/yolo-obj.cfg darknet19_448.conv.23

学習にはハイエンドのGPUで1時間かかるということで、CPUだけだと厳しい感はあります。
学習結果は以下のコマンドで確認します。

./darknet detector test data/nfpa/obj.data data/nfpa/yolo-obj.cfg yolo-obj1000.weights testimage.jpg

学習済みのTiny-Yoloを動かしてみるには、ofxDarknetからtiny-yolo-voc.weightsをダウンロードした後、以下のコマンドで確認します。

./darknet detector test cfg/voc.data cfg/tiny-yolo-voc.cfg tiny-yolo-voc.weights data/person.jpg

上記ページのCOCO版のtiny-yolo.weightは学習時のcfgが異なるためか、正しく動作しないため、digitalbrain79/pyyoloのウエイトファイルを使用します。

./darknet detector test cfg/coco.data cfg/tiny-yolo.cfg tiny-yolo.weights data/person.jpg

YoloV1での学習


係数をCaffemodelに変換するには、pytorch-caffe-darknet-convertを使用します。変換にはCaffeのInstallが必要です。

python darknet2caffe.py tiny-yolo.cfg tiny-yolo.weights out.prototxt out.caffemodel

ただし、YoloV2にはCaffeではサポートされていないRegion Layerが含まれています。そのため、Caffeで動かす場合は、cfg/yolov1を使用する必要があります。Tiny-Cocoのweightsはyolov1のページからダウンロードできます。

動作テスト
./darknet coco test cfg/yolov1/tiny-coco.cfg tiny-coco.weights data/giraffe.jpg

Caffeモデルへの変換
python darknet2caffe.py tiny-coco.cfg tiny-coco.weights out.prototxt out.caffemodel

Yolov1で学習するには以下のコマンドを使用します。使用する画像の設定はsrc/yolo.cで行う必要があります。(You Only Look Once:Unified, 統合されたリアルタイムオブジェクトの検出

char *train_images = "/home/pjreddie/data/voc/test/train.txt";
char *backup_directory = "/home/pjreddie/backup/";

cfgのclassesとconnectedのoutputを書き換えます。outputはcfgのside*side*((coords+1)*n+classes)です。nfpaの1カテゴリでtiny-yoloを使用する場合は539になります。

VOCの学習
./darknet yolo train cfg/yolov1/yolo.train.cfg

yolo.cfgとyolo.train.cfgの違いはbatchとsubdivisionsだけです。

nfpaの学習
./darknet yolo train cfg/yolov1/tiny-yolo-nfpa.cfg

学習はMac Pro 2013だとシングルスレッドで24時間で200 Epoc、OpenMPで12時間で1400Epocでした。GTX1070を使えば、1時間で2000Epoc回すことができました。Darknetで2クラスの物体をトレーニングによると8000Epoc近く回す必要があるみたいなので、CPUで8000Epoc回すには3日程度必要です。GPUだと4時間程度で終わります。

クラス数1の学習結果のテストを行うにはyolo.cのdraw_detectionsの最後の引数の20をl.classesに書き換える必要があります。

学習結果のテスト
./darknet yolo test cfg/yolov1/tiny-yolo-nfpa.cfg backup/tiny-yolo-nfpa_200.weights data/nfpa/pos-1.jpg

500Epoc
500
1000Epoc
1000
2000Epoc
2000

2000 Epocまでいけばわりと認識できるようです。

(補足)Caffeのインストール


darknet2caffe.pyの実行にはCaffeが必要です。CaffeのインストールにはMakefile.configに以下の設定が必要でした。PYTHON_LIBのパスが合ってないと、import CaffeでSegmentation Faultが起きるようです。リンカフラグへのlibstdc++の設定は不要でした。

CPU_ONLY := 1
OPENCV_VERSION := 3
WITH_PYTHON_LAYER := 1
PYTHON_LIB :=  /usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/
INCLUDE_DIRS += /usr/local/Cellar/boost@1.59/1.59.0/include /usr/local/Cellar/boost-python@1.59/1.59.0/include
LIBRARY_DIRS += /usr/local/Cellar/boost@1.59/1.59.0/lib /usr/local/Cellar/boost-python@1.59/1.59.0/lib
INCLUDE_DIRS += /usr/local/lib/python2.7/site-packages/numpy/core/include

(補足)Darknetのマルチスレッド化


Darknetはデフォルトでは1スレッドで動作します。CPUでマルチスレッド化するには、OpenMPを有効化します。XcodeのデフォルトではOpenMPが使用できないため、最新のLLVMをインストールします。
brew install llvm

Makefileを書き換えます。
OPENMP=1
CC=/usr/local/opt/llvm/bin/clang
CPP=/usr/local/opt/llvm/bin/clang++
LDFLAGS+= -L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib
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