Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

DCGANで名刺のデザインを試みた

- はじめに -

社内ハッカソンと社内勉強会のネタとして、今更ながらGenerative Adversarial Networks*1 (GAN)とその応用とも言えるモデルであるDeep Convolutional Generative Adversarial Networks*2 (DCGAN)について調査し、実際に検証を行った。

この記事は、DCGANについていくらか調査、検証した部分について記述しておくものである。
なお、画像生成系のモデルは以前より話題になっていたため論文には目を通していたが、実際に触ったのは初めてである。

題材として「名刺」の画像をDCGANで生成する事を試みた。
その過程と結果を示す。


- GANとDCGAN -

DCGANはGANに対してConvolutional Neural Networks(CNN)を適応する構成手法のようなものである。

生成モデルにおけるGAN

GANは、2つの生成モデルを相互に学習させることで学習データが形成する分布をより汎化な状態として保持できるというもので、PRML的に言うと伝承サンプリング(ancestral sampling)を使う生成モデルの一種である。
伝承サンプリングはMCMCのようなサンプリングを使うモデルに対して、計算量が少なく済む事がメリット。中でもHelmholtz Machineや変分AutoEncoderに代表される生成モデルと推論モデルを同時に学習させる手法に比べ、よりシャープな自然画像を生成できるのがGANである。

GAN自体は利用する生成モデルについては限定していないが、2つのモデル間のJSダイバージェンスに対してmin-max最適化を行うように捉える事で、2つのモデルは相互に更新しあいながら学習させられ、またBackpropagationが適応できるので勾配の近似と計算量削減ができて良いよねという旨である。

GANの2つのモデルはDiscriminatorとGeneratorに分かれる。
学習におけるGeneratorの目的は、ランダムノイズからDiscriminatorが誤認識するようなGenerator用の入力を作れるようになること。
学習におけるDiscriminatorの目的は、学習データとGeneratorが作る入力を正しく判定できること。
この2つが相互に上手く学習できれば、Generatorはより学習データに近いものが生成できるはずという旨である。

後述するが実際には、この学習は難しく、大きなネットワークモデル等を利用する場合にはパラメータ設定ゲーとなる。

DCGAN

本題のDCGANは、GANにCNNを上手く適応させるため以下のような事を行っている。

  • Pooling layerを以下に置き換えてアップサンプリング
    • D : Strided convolution
    • G : fractional-strided convolution
  • Batch Normalizationを使う
    • 学習を早くできるし過学習をそれなりに防げる
  • 全結合の隠れレイヤーは取り除く
    • 全結合層を全部除くとの文献を見かけるが正確にはglobal average poolingに置き換える(?)である。「出力層手前では、全結合するのではなくて一つの特徴マップに一つのクラスが対応するように設計する」というのが正しそう。
  • Leaky ReLU関数を使う
    • D : 全てLeaky ReLU
    • G : 基本はReLUで出力層はtanh
    • x<=0でも学習が進み、かつ過学習しないように

こういう感じでGANにCNNを適応していくと、CNN使っても安定して上手いこと画像データの生成ができるよ!というのがDCGANの立ち位置だと思われる(?)。
今回後述の実験では利用してないが、他にも「DCGAN(もしくはGAN)で上手いこと画像を生成する方法」は多く論文としてまとめられていて、社内勉強会で上記内容を発表した際にいくつか教えて頂いたので記述しておく。

[1606.03498] Improved Techniques for Training GANs
Feature matching含むいくつかの最適化手法と半教師あり学習によって、画像の生成の成功確率が上がる。

[1606.00709] f-GAN: Training Generative Neural Samplers using Variational Divergence Minimization
GANで使うJSダイバージェンスをfダイバージェンスというものに置き換えましょうという論文。
KLダイバージェンス等とも比較している。

他にも、GeneratorがDiscriminatorを確実に騙せるような画像を学習によって得てしまい、出力画像が特定の画風に固定化してしまう問題に対して「バッチ重みを上手く全体に適応すると良いよ」等の指摘を頂いた。今後時間があれば試してみたい。


- DCGAN関連のプロダクト -

社内勉強会なので、プロダクトと一緒に紹介した。
若干雑だがせっかくなのでそのままコピペで以下にまとめておく。

その他

- DCGAN参考文献 (Web) -

Web上で学ぶ時に有益だと感じた文献。

関連研究を知る

周辺のモデルの流れを掴むには@beam2d氏が公開する以下の資料が分かりやすい。
https://www.slideshare.net/beam2d/learning-generator

生成モデルに関する論文をまとめた以下のような記事もあり、RBMやVAE関連で調べる際は参考になる。
(こちらは随時更新されているようだが誰が書いているか知らない)
memonone: 生成モデル(Generative Model)関連の論文まとめ

GAN系の最適化とか応用研究についてのスライド
[DL輪読会] GAN系の研究まとめ (NIPS2016とICLR2016が中心)

概要を掴む

@miztiさんのブログ。DCGANの概要がつかめる。
できるだけ丁寧にGANとDCGANを理解する - 午睡二時四十分

わかりやすくDCGANについて書いている。概要がつかめる
なんちゃって!DCGANでコンピュータがリアルな絵を描く - PlayGround

スライド。概要がつかめる。画が多い。
Deep Convolutional Generative Adversarial Networks - Nextremer勉強会資料

GANの更新規則についての記事。GIFが分かりやすい。
An Alternative Update Rule for Generative Adversarial Networks

数式に対する解釈が分かりやすいと思う。
[Survey]Generative Image Modeling using Style and Structure Adversarial Networks - Qiita

めちゃくちゃ丁寧にGANについて書かれている。多分Webだと一番丁寧。
はじめてのGAN


- DCGANで名刺画像生成に挑戦した -

事前準備

今回は社内ハッカソンと社内勉強会のための時間を使って、DCGANを試した。
主には以下の@mattya氏がchainerで書いたDCGANをcloneし、モデルとパラメータを調整した。

GitHub - mattya/chainer-DCGAN: Chainer implementation of Deep Convolutional Generative Adversarial Network

課題として、「DCGANは文字や文字構成を学習できるか」という事を見ておきたく、画像サイズが小さく定形な物があるという事で「名刺」を選択した。

学習データに利用した名刺画像はStanfordのデータセットや「名刺 サンプル」「Business Card」で検索してスクレイピングし、2万枚程集めた。
たまに「企業名 名刺」で検索すると山のように名刺を出してる所があって、日本語の企業一覧使って検索してみて包含したので、データセットとしては若干偏っているかもしれない。

名刺のサンプル画像には1枚の画像に数枚入っていたり、お洒落を意識した傾きがあったりするので、ラプラシアンフィルタを使ってエッジを取って、四角があったら名刺だろうといったコードを書いた。
以下を参考に(というかほぼコピペ)したが、2値化するよりHough変換した方が良かった気がする。
ホワイトボードの画像からポストイットを検出する - Qiita

増量して大体5万枚くらいになって、1週間程仕事終わりに目視で「あ~まあこれならええやろ」くらいの絞り込みを行った。
学習データとしては画像集めるだけだったので、まだマシだった。

元々、これをやろうと思い立ったきっかけは画像認識系のコンテストで、そちらのデータセットには名刺の画像と各情報の矩形位置まであったのを見て「これ使ってアップサンプリングすれば行けそうだな」と思っていて使いたかったんだけど、コンテストページ行ったらもうダウンロードできなかった…

名刺管理系のサービスは大体スクレイピング禁止なる項目が書かれてあるので、大丈夫そうなデータしか集められなかったのは事実。

また、名刺は縦横があるんだけど、全部横向きの物を目で選んだ。
理由は同時に学習できるか分からないのと、widthとheightが違うので合わせるのが大変そうなのと、サンプル名刺では等に横向きの物が大半だったから。
縦向きの名刺ダサいし仕方ないね。

名刺は日本語や英語だけでなく、色々な言語の名刺を入れた。
(じゃないと学習データが確保できなかった)
一応アラビア文字とかは目視で見つけた場合のみ外したはず。
あと画像サイズを縮小して、名刺の縦横比率の2倍の110*182にした。

学習とその結果

学習には絶対最強のp2.16xlargeを利用した。申請して2日くらいかかって使えるようになったと思う。構成はChainer周りのDockerでドッカーン。

会社からAWSサービスを使う時に補助が出たりするけど、4~5日程p2インスタンスを利用したら、会社補助上限を突き抜けて+3万円くらいが消えた。
事前準備とサーバへの画像アップロードも時間がかかったが、大体学習の時間。
方法としてはモデルをまず考えて、数時間回してみて期待できそうなら~という感じ。アナログ。
最初の画像が見れるまでの速度的にp2.16xlargみたいな高火力なやつ使った方が、実験はできると思う。
これから見せる画像は、インターネットにある名刺画像と俺の財布が作り出した結果だと思って見ていって欲しい。

学習初期

> い つ も の <
f:id:vaaaaaanquish:20170319191349p:plain:w200:h200

f:id:vaaaaaanquish:20170319191754p:plain:w200:h200

それっぽさがあるがこの辺りはまだ不安。

画像が生成されはじめる

50万回程が学習を回した段階
f:id:vaaaaaanquish:20170319191920p:plain:w200:h200

100万
f:id:vaaaaaanquish:20170319192040p:plain:w200:h200

この辺で少し安心できる

かなり名刺

330万程学習を回した時

f:id:vaaaaaanquish:20170319193627p:plain:w550:h400

いやこれ名刺っぽいなと思った。
こういうデザインの名刺見たことある。
企業アイコンがあって、よくわからん線が入ってて名前の横に役職とかがあって~というやつ。
ちょっと感動した。

学習過程をGIFにしてみた。

f:id:vaaaaaanquish:20170319215634g:plain

学習終盤では一度生成画像がリセットされ固定化されてしまっている。
これは、GeneratorがDiscriminatorを確実に騙せるような画像を学習によって得てしまった故の結果であり、先述したようにバッチ重みを上手く全体に適応する等の対策があるらしい。一応途中途中でモデルと出力は保存しておくのが良い。


- おわりに -

GANとDCGANについて調べ、ソースを見てモデル調整を行った。
また、そのモデルを用いて名刺画像の生成を試みた。

文字を上手く出して欲しかったが、あまり文字として読めるものにはならなかった。
後で学習データを眺めると画像によっては文字が潰れていたので、学習結果で思ったより文字が上手く出てなかった原因はここにもあるかもしれない。

また、学習データが少なかった事と背景が真っ赤だったりする名刺が混ざっていたために、一部名刺とは言えない画像が生成されるのがネックという感じだった。学習データは大事。

ただ、名刺で見たことあるような全体のデザインや、有り得そうなロゴマーク、背景等が生成できた。
DCGANは、画像内に描かれているものが文字であってもそれらの配置を学習し、生成する事ができるという事が分かった。


ちなみに明日は僕の誕生日です。やったね。

Djangoで送信された画像データをPyhon上で処理するWebサービスを作る

- はじめに -

自分のWebサービスは基本PythonDjangoフレームを利用している。

Djangoでフォームから画像を投稿してもらって、それを受け取り、画像処理や機械学習で色々やって画像として返す、といったサービスを作りたい時のメモ。

実際に「ドイツのトリおるか」なる特定の鳥を赤枠で囲むクソサービスを運営しているので、そちらも参考に。


- Django周りのコード -

view.py辺りにいつもこんな感じで書いている。

# //-- Django周りのimport --

from PIL import Image
import sys
sys.path.append("/usr/local/lib/python2.7/site-packages")
import numpy
import dlib
import cv2

@csrf_protect
def main(request):
    # POSTを受けた時
    if request.method == 'POST':
        try:
      # 画像化して処理
            img_moto =Image.open(request.FILES['image'])
            img=numpy.asarray(numpy.array(img_moto))
            dets = original_detector_run(img)

        except Exception:
            # エラー(画像じゃない等)
            f = forms.TestForm(request.GET or None)
            return render_to_response('index.html',
                                      context_instance=RequestContext(request,
                                                                      {'form1': f,
                                                                       "text":"エラーだよ"}))
        # 画像を編集
        img = img_dets_ediy(img, dets)
        
        # Responseとして画像を返す
        response = HttpResponse(mimetype="image/png")
        img_moto.save(response, "PNG")
        return response
    
    # 普段の動作
    else:
        f = forms.TestForm(request.GET or None)
        return render_to_response('index.html', context_instance=RequestContext(request, {'form1': f}))


requestを受け取ったら、FILES['image']の中に画像情報が入っていてPILのImageベースで利用できる。
あとはいつもの変換作業でよしなに。
OpenCVとPIL(python Image library)のデータ変換 - tataboxの備忘録



- ありがちなやつ -

PYTHON_PATHだけでなく、`django-admin.py`にパスを書く必要がある。
やっておくとsys.path.appendは不要になる(場合がある)。

Django の django-admin.py でパスを通しても command not found の時 - Qiita

画像処理だと、OpenCVを使いたい場合があるが、Pythonバインディングはcv2.soファイルで行っているので、そこへのPATHも通す必要がある。(大体site-package内にあるはずだが自分は諸事情でなかったので)

また、wsgiのPATHの設定が居る場合もある。
wsgi.py(もしくはvirtualenv内のwsgiファイル)にWSGIPythonPathとしてsite-package周りを追加しておく。

Apacheだったらそれらでディレクトリの権限をちゃんと設定しておく必要あり。


- おわりに -

こんなのが出来る

vaaaaaanquish.hatenadiary.jp

『コンピューターで「脳」がつくれるか』を読んで

- はじめに -

この記事は以下
『コンピューターで「脳」がつくれるか』(2016/9/27 五木田 和也)
を読んだ所感をまとめたものです。

コンピューターで「脳」がつくれるか

コンピューターで「脳」がつくれるか


未購入の方やこれから読むぞという方、また読んだので語りたいという方向けに記録として残すものです。

ちなみにこの記事の筆者は、情報工学を専攻し機械学習を研究、現在エンジニアをやっている人間です。
そのような視点から書いているという前提をご理解下さい。


- 読了難度について -

まず、テーマが「コンピューター」「人工知能」「機械学習」という事もあり「一歩踏み出しにくいな」という方も居ると思います。

個人的には、そういった人にこそ薦められる書籍です。

実際に筆者の @kazoo04 は著書について以下のように述べています。

できる限り簡潔に、かつ専門用語を最小限に書かれてあるイメージです。
難しい部分は一般的な体験や現象に紐付けて説明されており、「あ、これ経験した事ある!」という形で頭に入ってきやすい文章だと思います。

また、かわいいイラストによる解説も魅力的です。とてもかわいいです。
僕はこれが好きです。
f:id:vaaaaaanquish:20160921181121j:plain
向かう相手を考えずクソリプを飛ばして噛み付かれてしまうツイッタラーみたいですね。

もちろん、脳や機械学習モデルの模式図も示されますが、内容を細かくセクションで区切り、難解な点は何度も詳しく説明が入るよう工夫されていると思います。

このように言うとコラム本のようなイメージですが、注釈が丁寧に付与されており、人工知能関連の研究を知り、未来を考えるための最初の書籍として最適だと思います。


- 満足感を得るために -

簡易な分、情報工学神経科学を修めた大学卒の人等からすると、満足いかない部分もあるかもしれません。
僕自身、通勤の行き帰り30分程でほとんどの内容を読み切りました。

僕からするとあっさり軽めな内容ですが、この本の目指す目的は「知識の蓄積」というより「汎用人工知能と未来」に尽きると思いました。

主に前半のセクションでは「コンピュータと人工知能の歴史」「神経科学」「機械学習」と分かれています。
この部分は主に"汎用人工知能の話をするため"に書かれた部分だと思います。
今わかっている事実や技術について、様々な側面から書かれています。

前半は知見のような話が続き、大学の講義だと眠くなるタイプの部分かも知れません。
僕の会社の営業職をする同期にこの書籍を渡した時、この部分で深く考え込んでいました。
難しいと感じれば、まだ考える部分ではなく「へえ~こんなのがあるんだ」程度で良いと思います。
簡単と感じれば、「神経科学」「機械学習」の両面を比較しながら読むと良いかも知れません。
良いアイデアの元になったり再確認にはぴったりだと思います。

後半のセクションでは、前半の知識をもとに「汎用人工知能」の生成について、映画やSF小説からイメージされるような所謂「人工知能」が生まれた未来について書かれています。
最後のこの部分に帰結するための前半といっても過言ではないかなと思います。
後半の汎用人工知能の議論を活発にするために、必要な知識や歴史、キーワード、小話が詰まっているといえます。

これらを意識して読むと、難しい部分や読み飛ばした部分も幸せに読めるかも知れません。


- 気になった所 -

個人的に面白いなと思った所、気になった所を紹介しておきます。

  • ニューラルネットワークおよびDeep Learningの技術史が端的にまとめられている
  • 脳の話に対して実例が多い
  • ちょいちょい入ってくるコラムが面白い
  • ブログでQ&Aしてる
  • 著者と絵のメガネ野郎が微妙に似ている
  • 筆者のプロフィールが真面目
  • カバーを外すと専門書っぽい
  • 絵がかわいい

中でも「絵がかわいい」は推したい所です。

LINEスタンプも出してるようなので僕は買いました!
可愛いしコミカルで伝わりやすいです。素晴らしい。

あとは @kazoo04 氏のブログとも連結していと面白いです。

さらに本を楽しみたければココを読めってことね。

コンテンツと金を作るのが上手いなあ…


- 僕が思う汎用人工知能と未来 -

僕は大体本に書いてある通りの事を考えてます。
まあ著者と親交が深いというのもありますけど。
仕事を人工知能にまかせて、自動運転が実現して、世界がアップデートされていく。幸せだと思います。
自動運転とかよく批判されますけど、「自動化」と捉えれば何てことはないんですよ。
洗濯機が自動で洗濯してくれるように、ドアが自動で開閉するように、エレベータが10階まで自動で連れて行ってくれるように、自然に生活の中に「人工知能」も入ってくると思います。

強いて言うなら、この本を読んで「もっと未来に関する議論は活発にすべきだな」と感じました。

エレベータが事故を起こした際、責任の所在はある程度明確になっています。
自動運転もお仕事も、ちゃんと議論してどこまで責任持つかとか、自動化で職業はどう変化してくかとか、未来について議論する事が大事だなと思います。
法の整備や仕組みを先んじて進めて、ドローンのように世界に遅れを取らないように。
技術が一方的に発展するだけでなく、みんなが幸せになれるように。

世界がそういう方向に向かうと良いなと思います。


- おわりに -

ここまで褒めちぎって書いて何ですが、発売前に見せてもらってチェックしたんですよ。
その時は「Deep Learningライン工みたいに表現してるけど違くね?」とか「ここ無駄多すぎね?」とか色々あったんですよ。
でもそれらを間違いなくかつ分かりやすく説明するのってすごく難しくて、本来ならば線形代数や統計の知識が居る部分を上手くコンテキストとしてまとめてるなと思います。
実際に @kazoo04 氏に反例を出して、ボコボコにしてやろうと思ったんですが、結構反例になるようなものが出せなくて大変でした。

脳とか機械学習とか結構分かってない部分もあり、適当な事言ってる人も多い状態なんですけど、この本に関してはよく考えられてるなと僕は思います。

追記ですがイラストレーターさんの名前でググると、同姓同名の方が出てくるようなのでよしなに。


みなさんの知見と議論が広がれば多分著書もハッピーです。
とりあえず買おう。

コンピューターで「脳」がつくれるか

コンピューターで「脳」がつくれるか

dlibのSimple_Object_detectorを用いたPythonでの物体検出器の学習

- はじめに -

これはこの記事の続きで、dlibを使って物体検出をしようというものである。

まあ正確には、dlibには「顔検出器の学習」ってのは無くて「物体検出器の学習」の機能を使って、顔検出器の再学習がしたいという記事です。

dlibを使う際の参考になればよいです。


- dlibのObjectDetectorについて -

dlibに物体検出の学習が入ったのは2014年の時。
内部にはHoG+SVMを使っていて、OpenCVで学習する場合に比べて、遥かに少ない学習データで、かなりの精度を出す事ができる。

リリース時の本家記事 : dlib C++ Library: Dlib 18.6 released: Make your own object detector!

本記事では、Pythonのdlib apiを使って、物体検出器の学習を行っていく。

Python用のドキュメント : Classes — dlib documentation

dlib.simple_object_detectorを使う。
一応、こちらに公式の学習サンプルがある。

http://dlib.net/train_object_detector.py.html

大体の事は書いてあるけど、パラメータ等が全部書いてある訳ではないので、日本語訳してごにょごにょしたものをリポジトリに置いておいたので見て頂ければ。

github.com



- 学習形式とサンプル -

ディレクトリ内の画像と矩形情報が入ったテキストファイルを元に学習するスクリプトは以下。

#! /usr/bin/python
# -*- coding: utf-8 -*-
u"""rect.txtと画像データを用いてdlibを追加学習するスクリプト."""

import dlib
import os
from skimage import io

input_folder = "./test/"
rect_file = "./true_rect.txt"
output_svm = "detector.svm"

def get_rect(rect_file):
    u"""矩形ファイルを読み込みリスト化."""
    rect_list = []
    for line in open(rect_file, 'r'):
        rect_list.append(line)
    return rect_list


def make_train_data(rect_list):
    u"""矩形リストから学習用データを生成する."""
    boxes = []
    images = []
    for i, x in enumerate(rect_list):

        # 改行と空白を除去してリスト化
        x = x.replace('\n', '')
        x = x.replace('\r', '')
        one_data = x.split(' ')
        # 矩形の数k
        k = len(one_data) / 4

        # 矩形をdlib.rectangle形式でリスト化
        img_rect = []
        for j in range(k):
            left = int(one_data[j*4])
            top = int(one_data[j*4+1])
            right = int(one_data[j*4+2])
            bottom = int(one_data[j*4+3])
            img_rect.append(dlib.rectangle(left, top, right, bottom))

        # boxesに矩形リストをtupleにして追加
        # imagesにファイル情報を追加
        f_path = input_folder + one_data[k*4] + '.jpg'
        if os.path.exists(f_path):
            boxes.append(tuple(img_rect))
            images.append(io.imread(f_path))

    return boxes, images


def training(boxes, images):
    u"""学習するマン."""
    # simple_object_detectorの訓練用オプションを取ってくる
    options = dlib.simple_object_detector_training_options()
    # 左右対照に学習データを増やすならtrueで訓練(メモリを使う)
    options.add_left_right_image_flips = True
    # SVMを使ってるのでC値を設定する必要がある
    options.C = 5
    # スレッド数指定
    options.num_threads = 16
    # 学習途中の出力をするかどうか
    options.be_verbose = True
    # 学習許容範囲
    options.epsilon = 0.001
    # サンプルを増やす最大数(大きすぎるとメモリを使う)
    options.upsample_limit = 8
    # 矩形検出の最小窓サイズ(80*80=6400となる)
    options.detection_window_size = 6400

    # 学習してsvmファイルを保存
    print('train...')
    detector = dlib.train_simple_object_detector(images, boxes, options)
    detector.save(output_svm)


if __name__ == '__main__':
    rect_list = get_rect(rect_file)
    boxes, images = make_train_data(rect_list)
    training(boxes, images)


simple_object_detector_training内部でデータの増量を行っており、optionのupsample_limitとadd_left_right_image_flipsで調整できる。
データの増量では、基本的なData Augmentationが行われているため、学習用のデータは最小で良い。

実際、公式のサンプルコードでは、22枚のサンプル画像と矩形情報を学習用データセットとして、高い精度の顔検出器を作っている。

あまり画像を入れるとMemoryErrorの原因となる。
大体こんな感じで止まったら、Optionのパラメータ調整しなおすか、画像を減らすか、メモリを増やす必要がある。

Traceback (most recent call last):
  File "detector.py", line 104, in <module>
    boxes, images = make_train_data(rect_list)
  File "detector.py", line 70, in make_train_data
    images.append(io.imread(f_path))
  File "C:\Python27\lib\site-packages\skimage\io\_io.py", line 61, in imread
    img = call_plugin('imread', fname, plugin=plugin, **plugin_args)
  File "C:\Python27\lib\site-packages\skimage\io\manage_plugins.py", line 211, in call_plugin
    return func(*args, **kwargs)
  File "C:\Python27\lib\site-packages\skimage\io\_plugins\pil_plugin.py", line 37, in imread
    return pil_to_ndarray(im, dtype=dtype, img_num=img_num)
  File "C:\Python27\lib\site-packages\skimage\io\_plugins\pil_plugin.py", line 111, in pil_to_ndarray
    frame = np.array(frame, dtype=dtype)
MemoryError

dlibの公式Q&Aで「MemoryErrorって出るんだけど…」という質問に対して、作者が「Buy Memory!」と応えているくらいなので仕方ない。

感覚としては、32Gメモリ積んだマシンでも、100*100サイズの画像1000枚を、add_left_right_image_flips=true、upsample_limit=4とかで学習させたら落ちる。
CPUもフルに使うので最悪PCフリーズが有り得る。
学習データを減らすのが手っ取り早いが、対応できる環境が少なくなる。
マシンかパラメータでなんとかこうとかするのが良い。
(こういう点から、dlibの物体検出器学習クラスは背景や周りの環境が固定な場合超強いって感じする。)

64Gメモリ、16コアのCPUでも100*100の画像2000枚くらいが限界っぽい。
それ以上はパラメータ調整云々でもなんともならなかった。


学習用の矩形情報と画像情報はPythonコードで言うと以下のような形式で入力する。
boxes[n]とimages[n]が共通の情報となれば良い。

boxes_img1 = ([dlib.rectangle(left=329, top=78, right=437, bottom=186),
               dlib.rectangle(left=224, top=95, right=314, bottom=185),
               dlib.rectangle(left=125, top=65, right=214, bottom=155)])
boxes_img2 = ([dlib.rectangle(left=154, top=46, right=228, bottom=121),
               dlib.rectangle(left=266, top=280, right=328, bottom=342)])
boxes = [boxes_img1, boxes_img2]
images = [io.imread(dir_path + '/xxxxxx.jpg'),
          io.imread(dir_path + '/yyyyyy.jpg')]


学習に使うrect.txtは

x1 y1 x2 y2 file_name
x1 y1 x2 y2 file_name2

のような空白CSVっぽくなってる前提。
矩形が複数ある場合の1行は

x1 y1 x2 y2 x3 y3 x4 y4 file_name

といった形式で保存してあるものをパースしている。

いつかxmlにもする。
学習データ作って、xmlで学習させてる人は居たのでリンク貼っとく。



- 学習結果のsvmを使う -

前回の記事のdetector.runする部分を修正する。

- detector = dlib.get_frontal_face_detector()
+ detector = dlib.simple_object_detector("detector.svm")

- dets, scores, idx = detector.run(img_rgb, 0)
+ dets = detector(img_rgb, 0)

自前で学習した学習器はスコアや第二候補を返さないっぽい。



- テスト -

前回の記事Google Cloud Vision APIの記事で出したデータを元に学習させる。
例によって河村友歌ちゃんの顔画像でテストする。

f:id:vaaaaaanquish:20160902172432j:plain

はい、かわいい。


いつも顔検出ばかりやっていては仕方ないので、それっぽく猫の画像を学習させ適応する。
学習データは手動で矩形を出して、たった20枚作っただけ。

f:id:vaaaaaanquish:20160902172729j:plain

はい、かわいい。


- 考察 -

dlibの物体検出は内部でサンプリングもしてくれるので、正データとなる画像と矩形だけ集めれば良いし、精度も良いので結構良い。
パラメータも少なく物体検出できる方だと思う。
メモリとCPUはバカ食いするけど愛嬌がある。

v19.01現在で、追加学習のようなクラスはないため、既存のfrontal_faceのsvmファイルをsaveしてさらに学習とかはできない。
あと、メモリ少ないから学習データ小分けにして食わせようとかもできないのでつらい。
SVMなので仕方ない感じではあるが。

文中にも書いたけど、固定的な環境(監視カメラとか背景が固定とか)だと、手軽にかなり高い精度を実現できる。
それ以外ならOpenCVとかの検出器と組み合わせるか、CNNに突っ込んだ方が吉。


- おわりに -

dlib、Deep Learningとか強化学習とか新しい手法をガンガン積んでいってるし、期待したい。

まだまだかゆい所に手が届かないので「コントリビュータにさせてくれよ!」と思ったけどGithubリポジトリなかったのでつらい。

まあでも、やり取りするより1からオレオレで書いた方が早いなと思った。

ほんとそれなわかるアカデミア。



もろもろのコードはGithubリポジトリに入れといたんでよしなに。
github.com



- 追記 -

--09/04--


がんばります。

dlibを用いた顔検出器と物体検出器とその学習

- はじめに -

dlibのSimple_Object_Detectorクラスを使った物体検出用カスケードの学習をする記事。

dlibは機械学習ライブラリとして2006年から始まったプロジェクトで、基本的なSVMや線形アルゴリズム、Bayesian Network等に加えて、機械学習関係で用いるような画像処理ツールやグラフツールが付属している。

https://dlib.net

この素敵な図を見たことある人も居ると思う
https://dlib.net/ml_guide.svg


dlibは画像からの物体検出として顔検出を代表としたオブジェクト検出用のクラスが用意されている。
中身はHoG+SVMとシンプルな構成だが、矩形情報と正例画像を与えるだけで、学習用サンプルを生成し、HoGフィルターのパラメータ調整や学習を行ってくれるAPIがある。
C++Pythonをサポートしており、以下からはUbuntu 14.04デフォルトのPython 2.7とdlib v19.0を用いる。


- dlibのインストール -

Ubuntu環境へのインストールならこの記事が分かりやすいらしい。

一応コマンドだけ貼っておくので上から実行してけばUbuntu 14.04ならワンパン。

sudo apt-get update
sudo apt-get upgrade

sudo apt-get install python-setuptools
sudo apt-get install python-pip

sudo pip install numpy
sudo apt-get install liblapack-dev libatlas-base-dev gfortran g++
sudo pip install scipy
sudo pip install matplotlib

sudo wget http://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/3.0.0/opencv-3.0.0.zip
sudo apt-get install unzip
sudo unzip opencv-3.0.0.zip
sudo rm opencv-3.0.0.zip

sudo apt-get install build-essential libgtk2.0-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev cmake python-dev python-numpy python-tk libtbb-dev libeigen3-dev yasm libfaac-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev libx264-dev libqt4-dev libqt4-opengl-dev sphinx-common texlive-latex-extra libv4l-dev libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev default-jdk ant libvtk5-qt4-dev

sudo apt-get install cmake
cd opencv-3.0.0
sudo cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D WITH_FFMPEG=OFF -D BUILD_opencv_python2=ON .
// ここでの16はコア数。自分のコア数はnprocコマンド辺りで確認
sudo make -j16
sudo make install

cd ..
sudo cp opencv-3.0.0/lib/cv2.so /usr/local/lib/python2.7/site-packages/

//-----------
下記シンボリックリンクを貼らないとopencvをimportする時エラーが出る
libdc1394 error: Failed to initialize libdc1394
http://stackoverflow.com/questions/29274638/opencv-libdc1394-error-failed-to-initialize-libdc1394
カメラとかのハードウェア用ドライバが邪魔するせいらしく、dlibやopencv使用自体に問題はない。
環境次第で再起動毎にシンボリックリンク貼り直す必要がある。具体的な解決策はまだないっぽい。
//-----------

sudo ln /dev/null /dev/raw1394

//-----------
python
import cv2 
でチェック
//-----------

sudo apt-get install git

sudo pip install cython
sudo pip install scikit-image

sudo apt-get install python-dev python-numpy
sudo apt-get install libboost-dev
sudo apt-get install libboost-python-dev
sudo apt-get install libboost-system-dev

sudo pip install dlib

//-----------
python
import dlib 
でチェック
//-----------


多分これでdlibのインストールは出来ると思う。Permission deniedとかはよしなに。


- 最もシンプルな顔検出機能を使う -

dlibは標準で顔検出用にfrontal_face_detector()を提供している。
こののブログの他記事でも述べているが、OpenCVの標準の顔検出カスケードよりは遥かに性能が良いと思う。

シンプルに顔検出器を使ってテストしたい場合は下のようなスクリプトで簡単にできる。

#! /usr/bin/python
# -*- coding: utf-8 -*-
u"""dlibによる顔画像検出."""
import cv2
import dlib

# 画像ファイルパスを指定
sample_img_path = 'sample.jpg'

def facedetector_dlib(img, image_path):
    try:
        detector = dlib.get_frontal_face_detector()
        # RGB変換 (opencv形式からskimage形式に変換)
        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        # frontal_face_detectorクラスは矩形, スコア, サブ検出器の結果を返す
        dets, scores, idx = detector.run(img_rgb, 0)
        # 矩形の色
        color = (0, 0, 255)
        s = ''
        if len(dets) > 0:
            # 顔画像ありと判断された場合
            for i, rect in enumerate(dets):
                # detsが矩形, scoreはスコア、idxはサブ検出器の結果(0.0がメインで数が大きい程弱い)
                # print rect, scores[i], idx[i]
                cv2.rectangle(img, (rect.left(), rect.top()), (rect.right(), rect.bottom()), color, thickness=10)
                s += (str(rect.left()) + ' ' + str(rect.top()) + ' ' + str(rect.right()) + ' ' + str(rect.bottom()) + ' ')
            s += image_path
        # 矩形が書き込まれた画像とs = 'x1 y1 x2 y2 x1 y1 x2 y2 file_name'
        # 顔が無ければ s='' が返る
        return img, s
    except:
        # メモリエラーの時など
        return img, ""

if __name__ == '__main__':
    img = cv2.imread(sample_img_path)
    img, s = facedetector_dlib(img, sample_img_path)
    cv2.imwrite('output_' + sample_img_path, img)
    f = open('./rect.txt', 'w')
    f.write(s)
    f.close()

引数には画像とupsample_numの値を与えてやる。upsample_numは多分selective searchで見る枚数か回転拡大縮小で見る枚数を増やしているんだと思う。精度は向上するが、その分探索時間とメモリをめっちゃ使う。
返り値としては顔の矩形位置座標と各スコア、サブ検出器の結果が返ってくる。スコアは割と信用できるので使い勝手は良い。

Google APIの時と同じく例によって、画像をまとめてdlibの顔検出に投げるスクリプトを下記Gitリポジトリにまとめておいた。
いつかGoogleの方とまとめるかも。

https://github.com/vaaaaanquish/dlib_detection_python_script


- 一応検証 -

例のごとく適当にテスト
入力画像は最近コミケでのコスプレが可愛かった河村友歌ちゃん。
なんかもう顔検出系のデモをやる時は毎回お世話になってるナイスフリー素材。

入力
f:id:vaaaaaanquish:20160815033016j:plain

出力
f:id:vaaaaaanquish:20160815033035j:plain


はいかわいい。


- なんかおわりに -

なんかコード貼ったら長くなったので、Simple_Object_Detectorの学習に関する内容は次の記事に書いときます。

なんかごめん。


追記:書きました。
vaaaaaanquish.hatenablog.com