python速度比較用プログラムを書いた

題字どうり

 

pythonは高速化を求めることがちょくちょくある。
bit演算と普通の演算子どっちがいいの?とか
内包表記でのリスト作成と普通のリスト作成どっちが速いの?とか
はたまた環境によって違うんじゃないの?とか

 

そんな疑問を解消するべく、githubにコードを書いた。

github.com

Visual Attribute Transfer through Deep Image Analogyの解説

はじめに

 「Visual Attribute Transfer through Deep Image Analogyの解説」と銘打ってはいますがあくまで私が読んで理解した内容を記述いたします。
つまるところ私の誤解による間違いが混入する可能性があります。
予めご了承ください。

 

Visual Attribute Transfer through Deep Image Analogyについて

2つの似ている画像を入力として与え、互いの画風を転写しあったような画像を出力するものです。
論文はこちら

[1705.01088] Visual Attribute Transfer through Deep Image Analogy

 

処理概要
 

f:id:akikanR:20171112001900p:plain

論文より画像を参照

1. 学習済みのVGG19に画像Aと画像B'を与えて各層のreluの出力を5つずつ取得する。(画像左側のPreprocessingに相当)

2. 2枚のn層目(初回ならn=5)の出力結果にパッチマッチを行い、近いところに写像されるような対応マップを作成する。その後、処理した層が1層目なら8を実行する。

3. 作成した対応マップを用いて画像A風の画像B'(画像A'とよぶ)と画像B'風の画像A(画像Bとよぶ)を作成する。

4. 3で作成した画像をデコンボリューションして一つ上の層の画像を予測して画像を作成する。(画像B'を用いてデコンボリューションされた画像をRA, もう一方をRBとする)

5. 2で処理した層の1つ上の重みを計算する。(1つ上の画像Aを用いて計算された重みをWA,もう一方をWBとする)

6. RAとWAを用いて2で処理した層の1つ上の画像A'を作成する。(Bの方も同様)

7. 2で作成した対応マップを2で処理した層の1つ上の画像と同じ縦横が同じサイズになるように最近傍補間を使って拡大する。その後2に戻る。

8. 対応マップを用いて画像Aと画像B'から画像A'と画像Bを作成して終了する。

 

 

ざっくり言うとVGG19に画像を入れて特徴マップを持ってきた後に抽出された層ごとに似ている場所を探していって対応マップを作り、それを基に大元の画像のピクセルを対応する場所にコピーして画風を転写しあうってことを実現している。

 

処理順1 前処理(特徴マップの取得と初期対応マップ作製)

 処理概要の1にある通りVGG19に画像を入れて中間データを取得する。
この時中間データを取得する場所は5層に分かれているVGG19の各層の第1のreluをかけた直後のデータとなる。得られたデータは縦*横*チャネル数の形になっている。

 初期対応マップとはパッチマッチを行う際に必要となるものだ。初回は完全にランダムの場所に対応したものを用いる。初回以降は処理概要7で作成した対応マップを用いる。対応マップの形式はパッチマッチを適用する画像と縦横が同じサイズでチャネル数は3の一般的な画像の形式とする(描画する都合上この形)。Rに対応する座標のx値,Bに対応する座標のy値が入る。

 

処理順2 パッチマッチ

パッチマッチについては2つの画像の各ウィンドウが(近似的に)最も近い場所を探すアルゴリズムである。詳細は以下のURLを参考にしてほしい。

http://vis.berkeley.edu/courses/cs294-69-fa11/wiki/images/1/18/05-PatchMatch.pdf

ウィンドウサイズは層毎に決められており、5層から3層目まで3*3ピクセル、2,1層は5*5である。また探索する範囲は4層,3層目まで半径6*6ピクセル、2,1層は4*4である。
ここで作成される対応マップは2枚あり、画像Aから画像B'への対応マップをΦLBA、画像B'から画像Aへの対応マップをΦLABと呼ぶ。

距離の定義は以下の式のΣである。

f:id:akikanR:20171112025128p:plain

表記が難しいのでFL-A(x)と書かせてもらう。

これは画像Aを画像Aのノルムで割って正規化したものを指す。

N(p)は指定された座標pを中心として構成されるウィンドウの座標集合である。

 

処理順3 画像A'と画像Bの作成

論文中ではwarpと表記されている。

画像A’を作る例を出す。
画像B’の左上のピクセル(0,0)座標をwarpすることを考える。
この時画像B'の座標と対応するΦLABの座標を参照し、参照したピクセル内のRGB値(処理順1でRにx値、Bにy値を入れている)の(R, B)座標に現在のピクセルのデータをコピーする。これを全ての画像B'の座標に行うことで画像A'は作成される。
画像Bに関しても同様である。

 

処理順4 画像のデコンボリューション

画像を与えるとその画像が畳み込まれる前の層を復元するCNNを作成する。
初期値として値がランダムになった画像、正解データとして処理3で生成した画像を入れる。

ロス関数の定義は以下の式である。

f:id:akikanR:20171112121037p:plain

右辺第一項はデコンボリューションで生成された画像を示しており、
右辺第二項は正解データを示している。
論文中ではオプティマイザーはlbfgsを使用しているようだ。
この時作成される画像をRB', RAと呼ぶ(ここでRB'は画像B'と似た形状になっているのではなく画像B'を基にしているということを示すためにRB'となっているだけなので誤解なきよう・・・)


ここでワープ後の画像が存在するならば畳み込まれる前の画像としてどんなものがふさわしいかを出力する。本来存在しないものに言うのもおかしな話だが画像を復元している。

 

処理順5 重み計算

次の層で重要となる部分を重みをつけて取り出すための重みを出力する。

重みの定義は以下の式

f:id:akikanR:20171112122418p:plain

f:id:akikanR:20171112122436p:plain

Lは処理順2で処理した層を示している。
αは層ごとに値が決められており、4層から1層目まで順に書くと0.8,0.7,0.6,0.1となっている。
κ=300
τ = 0.05
|FL-1A(x)|^2 だがこれは0から1の間で正規化しておかなくてはいけないようだ。

画像Aの重みをWA、画像B'の重みをWB'とする

 

処理順6 重みと復元画像の合体

以下の式を用いて一つ上の層の画像A'を求める。

f:id:akikanR:20171112123552p:plain

A〇B の演算だが特徴マップFL-1Aの座標(0,0)の全てのチャネルに対してWL-1Aの座標(0,0)の値をかけるということを意味している。

なお論文中でWL-1Aは0~1の値を持つと書いているがシグモイド関数に1未満をかけている時点でそもそも1には成りえないので0~1の範囲に落とし込む操作が必要であると思われる(私は最大値で割って0~1の範囲にした)

 

処理順7 対応マップの拡大

処理順2で作成した対応マップを処理順2で処理した層の1つ上の画像と縦横が同じサイズになるように最近傍補間を使って拡大する。その後処理順2に戻る。

 

処理順8 転写しあう画像の作成

対応マップを用いて画像Aと画像B'から画像A'と画像Bを作成して終了する。

以下の式で行う。

f:id:akikanR:20171112124910p:plain

Φ1abというのは最終的に出てくる対応マップのことを指している

Nは5*5のウィンドウサイズ(パッチマッチの処理手順を参照のこと)

 

おわりに

処理手順だらけになって申し訳ないが言うは簡単だがやるとめんどくさいというのがこの論文を実装しての感想だ。
そんな感じがにじみ出てしまっていたらまことに申し訳ない。
何か疑問点があれば質問して頂けるともしかしたらこたえられるかもしれない(たぶん自分で論文読んだ方が早い)

Visual Attribute Transfer through Deep Image Analogyを実装してbot化した話

はじめに

Visual Attribute Transfer through Deep Image Analogy(https://arxiv.org/pdf/1705.01088.pdf)

これは画風を転写しあうような論文。

例えばモナリザアバターを与えるとモナリザ風のアバターアバターモナリザを出力するような奴

f:id:akikanR:20171031223904p:plain

これは実際に作ったやつで論文のはちゃんとすごい

 

今回はこれをbot化して2枚の画像を与えたら画風を転写しあった画像を返すシステムを作った

 

 

動作事例

2人やってくれた方がいたので両方とも結果を張る

1.富士山を与えた例

入力

f:id:akikanR:20171031224329j:plainf:id:akikanR:20171031224334j:plain

出力結果

f:id:akikanR:20171031224542j:plainf:id:akikanR:20171031224546j:plain

 

2.仏像と人

入力

f:id:akikanR:20171031224725j:plainf:id:akikanR:20171031224722j:plain

出力結果

f:id:akikanR:20171031224902j:plainf:id:akikanR:20171031224905j:plain

 

 

うまくいくこともある

f:id:akikanR:20171031225343j:plainf:id:akikanR:20171031225340j:plain

f:id:akikanR:20171031225510j:plainf:id:akikanR:20171031225440j:plain

 

コード

コードはgithubにあがってるので酔狂な人はやってみてほしい

なおコードのリファクタリングなどはしていない

github.com

 

 

特徴ベクトルを作成する手間を減らすプログラムを作成した

特徴量の選択は実に重要な要素である。
しかし、特徴量一つごとに特徴量化するプログラムを書くのは大変手間であるし、そんなところに時間を使うのは勿体ない。
そんな時間を減らすためにプログラムを作成した。

特徴量にしなくてはいけないものにはいくつか種類があると思う。(他にあったら順次追加していく)

  • 連続値
  • 複数選択肢がある中で一つだけを選ぶもの
  • 複数選択肢がある中で複数を選ぶもの
  • 文章などのテキストデータ


本プログラムではテキストデータ以外の3つを簡略化する。

プログラムと使用方法はgithubにあげたのでリンクを貼っておく。

github.com

 

概要としては

連続値は標準化する

複数選択肢があって一つだけ選択する場合は選択肢の分だけ次元を作って特定の部分にのみ1、それ以外は0にして返す

複数選択肢があって複数選択する場合は上記と同様に次元を作り、選択された分だけ特定の場所に1、それ以外を0で返すようにした。

 

 

バッチ正規化の効果

前書き

以前バッチ正規化の記事を書いたけど使用する場合と使用しない場合の比較を書き忘れていたので書く。

 

比較対象は

akikanr.hatenablog.com

 

でバッチ正規化をする場合としない場合で比較する。

 

 

バッチ正規化をした場合

比較対象の記事ではバッチ正規化を施した状態の正解率を書いているが比較のためここで再度記述する

 

tep 0, training accuracy 0.113636 
step 1000, training accuracy 0.997727 
step 2000, training accuracy 0.997727 
step 3000, training accuracy 0.997727 
step 4000, training accuracy 0.997727 
step 5000, training accuracy 0.997727 
step 6000, training accuracy 0.995454 
step 7000, training accuracy 0.997727 
step 8000, training accuracy 0.997727 
step 9000, training accuracy 0.997727

汎化性能は以下の通り 
test accuracy 0.878788

 

バッチ正規化をしなかった場合

step 0, training accuracy 0.0772727
step 1000, training accuracy 0.995454
step 2000, training accuracy 0.997727
step 3000, training accuracy 0.995454
step 4000, training accuracy 0.986364
step 5000, training accuracy 0.995454
step 6000, training accuracy 0.995454
step 7000, training accuracy 0.995454
step 8000, training accuracy 0.995454
step 9000, training accuracy 0.995454

汎化性能
test accuracy 0.757576

 

比較した結果

汎化性能に明らかな性能差がみられる。

またtraining accuracyについても若干の性能向上がみられており、無いよりはあった方が良い。

今回のケースに限り性能が良かったのではないかという話も無いわけではないので自分のモデルで使う場合はバッチ正規化を使用してみて性能を比較して検討してみてほしい。

 

Tensorflowでの書き方は比較対象の記事からgithubのコード置き場に行けるのでそこで読んでほしい。

もしくは

Tensorflowでbatch normlization(バッチ正規化)を使用する際の注意点(常に同じものしか出力しなくなったら) - akikanR’s blog

で紹介している記事にも書き方が載っている

 

Tensorflowでストライクウィッチーズのキャラ判別をしてみた

【はじめに】
今回の奴はストライクウィッチーズのキャラを認識する。

 


 【モデル】

今回のはTensorflowのチュートリアルにはdeep MNISTを基にして作成している。具体的にはバッチ正規化処理を追加しただけだが・・・。


【学習データ】

第一期、第二期ともにエイラーニャ回においてopencvで顔画像を切り取って学習データとしている。
11キャラ、各キャラ40枚を学習データ各キャラ10枚ずつをテストデータとしている

 

【精度】

学習データはエポック1000ぐらいで早々に収束した(99.7%)
step 0, training accuracy 0.113636
step 1000, training accuracy 0.997727
step 2000, training accuracy 0.997727
step 3000, training accuracy 0.997727
step 4000, training accuracy 0.997727
step 5000, training accuracy 0.997727
step 6000, training accuracy 0.995454
step 7000, training accuracy 0.997727
step 8000, training accuracy 0.997727
step 9000, training accuracy 0.997727

凡化性能は以下の通り
test accuracy 0.878788

 

bot化】

twitterボットとして運用していた。

→成果 

f:id:akikanR:20170923214229p:plain

まぁまぁ割とよかった
でもストライクウィッチーズの画像を入れてくれる人は少なかった

f:id:akikanR:20170923214636p:plain

 

【コード】

githubにあげている

https://github.com/akikan/Machine-Learning/tree/master/detect_witch

 

【参考サイト】

opencvのアニメ顔を切り出すのに使用させていただきました。

誠にありがとうございます。

OpenCVによるアニメ顔検出ならlbpcascade_animeface.xml - デー

フーリエ変換 メモ3 -フーリエ変換で得られるデータの意味とスペクトルを用いた逆変換-

【5】逆フーリエ変換について
まずは逆フーリエ変換の定義式です

f:id:akikanR:20170605205109p:plain

これを離散フーリエ変換の時と同様に展開していきます
※F (t) を前回離散フーリエ変換した実数部と虚数部が足し合わせたものだとして実数部をR[t] 虚数部をI[t]とします

f:id:akikanR:20170605205120p:plain


となります
ここで注意して欲しいのが「現実の音には虚数は存在しない」ということです
つまり虚数は無視します
すると上記の逆離散フーリエ変換の展開の式は

f:id:akikanR:20170605205136p:plain


ということになります

虚数が扱えないプログラミング言語の場合はこの式を用いていくことになります


【6】フーリエ変換で得られるデータ
フーリエ変換では様々なデータを得られると思うのですが、ここでは特に振幅スペクトルと位相スペクトルについて求め方を記述します

その前に離散フーリエ変換の定義式を思い出してください
Σの中身はcosとisinで記述されていましたね?
ここでcosとsinの幾何的な意味を見てみましょう

f:id:akikanR:20170605205224p:plain


k*cosθは原点から伸びるθ度に傾いた長さkの直線と半径kの円周の交点のX座標を示します
sinθの場合はcosθと同じ点のY座標を示しています


では求め方を説明します
フーリエ変換で得られた値は何かというとF(t)でした
もう少し厳密に言うと実数(=R [t]) と 虚数(=I [t])の和であるF (t) でしたね
この時のR[t] がcosθ、 I[t]がisinθに対応しており、θが位相スペクトル、半径が振幅スペクトルです
あとは単純な計算ですね
上の図の円周と直線kの交点から垂線をおろすと直角三角形が得られます
つまり振幅スペクトルは
三平方の定理より
直角三角形の斜辺の二乗 = 高さの二乗 + 底辺の二乗
よって
周波数 t の振幅スペクトル = √(R[t]の二乗 + I[t]の二乗)

次は位相スペクトルです
手始めにtanθを求め、その後アークtanで角度を求めます
周波数 t の位相スペクトル = arctan ( I[t] / R[t] )

以上で振幅スペクトルと位相スペクトルは求められました

以下これらスペクトルが示す意味です
興味の無い人は見なくても問題ありません

振幅スペクトルと位相スペクトルとはなんなのかを説明しましょう
先ほどの図に時間変化を加えてみます

f:id:akikanR:20170605205249p:plain


歪な図ですみません
この図のθが位相スペクトル、時間経過を加える前の図の円の半径が振幅スペクトルになります
位相スペクトルは周波数(時間経過を加えた時の緑の線)の位相差
振幅スペクトルは周波数(時間経過を加えた時の緑の線)の振幅を表しています
名前通りですね
まとめるとフーリエ変換で得られた各周波数に関する情報が位相スペクトルと振幅スペクトルなのです
また、含まれる周波数に関する情報があるのですから当然足し合わせて元のデータを得ることもできます

 

【7】スペクトルを用いた逆変換
このスペクトルを用いて
周波数 t の振幅スペクトル * e^周波数 t の位相スペクトル という式を立てます
これは F( t )を極座標で表現したものです
つまりは
F( t ) = 周波数 t の振幅スペクトル * e^周波数 t の位相スペクトル
という式が成り立ちます
これを逆離散フーリエ変換の式に当てはめれば無事元のデータになるというわけです
※展開するときに i がついた項は無視することを忘れないでください