画像のスタイル変換に関する深層学習論文紹介【image style,texture,VGG特徴マップの最適化,pythonコード紹介】


[Machine&Deep Learning論文紹介 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/dl_survey)の一日目

スタイル変換に興味があり、まとめたので紹介

# スタイル変換とは

そもそも「スタイル変換」とは以下のような技術です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/2aee9080-9faf-889e-d0aa-e9d53fe76cd4.png)


これはゴッホの「星月夜」で、鮮やかな青色と枯山水のような筆の流れがスタイル変換の分かりやすい例になっています
このように一方の画像の「画風」をもう一方に移すことで、絵画の作者が描いたような画像を作り出す事ができます
この技術的な背景を紹介していきます





# むかしのスタイル変換を簡単に

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/f1fc2e80-d076-5ce6-b221-a444dc68e409.png)


「image quilting」という主張で名付けられた技術がありました
これは「小さなテクスチャ画像」を横に並べ、そのつなぎ目を緩やかに繋ぐ技術です

しかしこれではあまりに適応できる範囲が狭い
小さいテクスチャの範囲だけでしか移せないし、テクスチャに指定した範囲でしか変換できない
どうしても思い通りに柔軟にスタイル変換を行いたければ人手がかかってしまう


# 深層学習を使ったスタイル変換


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/94202981-68f4-58bd-1f4e-c8c155072941.png)

gatys氏が畳み込みネットワークを利用したスタイル変換が発表されました

このお話は[youtube](https://www.youtube.com/watch?v=VQEMptfWpLk)に非常に分かりやすい説明があったので紹介しておきます
私の記事よりも分かりやすいと思います


その概要は畳み込みネットワークVGGの畳み込み層の特徴マップを可視化する技術がありまして、その技術から
・畳み込みの深い層(出力側)は画像の全体を大雑把にとらえているらしい
・浅い層(入力側)は単純な線、曲線の枠だけを捉えているらしい
という事がわかってきました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/2bda3f43-821b-64b0-582e-d36d2014205b.png)


今まで画像の特徴(線や曲線)を抽出するには既知の特徴を取り出すフィルタを組み合わせていましたが、これでは自然画像に対応できませんでした。
「VGGが精度良く特徴を取り出してくれるフィルタを学習していたので、これを利用してスタイルを学習しよう」という話です

ネットワークも特にVGG19が良いらしく、他のネットワークではスタイルが正しく変換できなかったそうです。

構造は以下のようになっています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/e74c2fe9-9788-d77c-53c1-5a80013ce48e.png)

シンプルに説明しますと、
・左側がテクスチャ画像です
・テクスチャ画像を重み固定のVGGに通した時の各畳み込み層の出力を保存しておきます
・層の出力をグラム行列とします
・右側がホワイトノイズ画像です
・ノイズをVGGに通し、VGGの各層の出力を保存しグラム行列にします
・左側のグラム行列に右側のグラム行列が近くなるように最適化します

左側のテクスチャ画像からテクスチャの特徴を取得することができるので、
この特徴の**相関**を一致させよう
という話です

完全に一致させてしまうと元のテクスチャ画像に非常に似た出力だけを得ることになってしまい、
「パターンという特徴」を学習するには過剰になってしまうので
**相関(グラム行列)**という特徴量に変換して最適化の対象にしています


[なぜグラム行列にするんだ？って疑問の人はこちらが参考になるかと](![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/edb0e8e0-75b2-44a6-aa91-d22f75f5eb37.png)
https://towardsdatascience.com/neural-networks-intuitions-2-dot-product-gram-matrix-and-neural-style-transfer-5d39653e7916
)

この論文もgatys氏が論文を執筆する際の背景にしたと言っていたので参考になるかと

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/c6462d11-230d-1a58-b1b6-263542597fd6.png)



もう少し論文の中身を見ていきます


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/a093c091-6257-302b-f647-88707963a334.png)

これがグラム行列Gを表していまして、畳み込み層から出力される行列Fのグラム行列(内積,分散共分散行列のようなもの)を計算します
テクスチャ側、ノイズ側で得られたGを二乗誤差の最小化によって近づけます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/5b9cd933-e83a-6f97-86f1-e97f9ab33909.png)

各層でこの誤差を求め、層ごとに重みを付けて何層目を重視するかをコントロールできるようにします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/32ef4213-c71c-7f54-3630-976bd24d99c5.png)


そしてこの損失(誤差)は解析的に求められることを示すことができるので、
微分可能という事は最小二乗法,誤差逆伝播が使えるわけで、
ノイズ画像側をテクスチャ側のグラム行列に近くなるような画像に作り直すフィードバックが走らせられるわけです


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/89ac3919-e738-53e2-139c-f447cdd1957e.png)


論文ではL-BFGSを最適化手法に採用していますが、これは高次元の最適化に向いている手法と考えたから採用したらしい
そして必要な計算はVGGの出力とその最適化なので、普通のGPU環境があれば実行可能である


この方法によってノイズ画像は徐々にテクスチャ画像の特徴を捉え自然なテクスチャを生成します
そしてネットワークが畳み込みであるので、ノイズ画像の再構成部分を触ってやれば画像サイズを変更することもできるようになります


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/dccc1a1b-4203-fd7c-ae1b-04f2c8401a64.png)


上図はどの層の出力までを使うかによって結果が異なったことを報告しています
一番右の列を見てみます
pool4(3層目のブロック)まで使うことで非常に元画像全体を参考にしたようなテクスチャ画像になります
一番下の行の画像はグラム行列を使わなかった場合(portilla氏の論文)の結果で、特徴量というよりも全体を近似してしまうような画像になります



テクスチャ変換は局所的な情報(色パターンなど)を残しながら、全体的な情報(石の大きさや位置など)は無視してほしい
という気持ちから特徴マップを取り出してきましたが、その特徴マップのためのパラメタはかなりの量になりますので計算負荷も大きい
しかし、下図のようにパラメタを主成分分析で減らしてやった場合(一行目)でも十分に求めるテクスチャ画像を得られることが実験的に分かっっています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/07107743-2779-a787-a56e-fc4f1f777912.png)


どこまで落としてもテクスチャ画像と呼べる情報が残るのかは別の研究対象とします
もう一つ実験的にわかっているのは、レンガ壁のような規則的な構造の場合にはパラメタを減らすと失敗しやすいという事です。



最初に少し紹介しましたがVGG以外のネットワーク、ここではalex-netを使った場合の出力も紹介されています


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/dfd3ffb5-3e28-14e0-7174-2e38ca6e8a86.png)


二行目がalex-netで実行した結果ですが、画像の表面に小さな網目構造が浮かんでいてあまりいい結果にはなりませんでした。
三行目はVGGの重みをランダムにしたネットワークを使った場合です。この場合はノイズしか出力しなくなります。


最後にもう少しだけグラム行列について触れます
グラム行列がどれだけ画像の特徴量として有用なのかを実験によって比較すると
元の層の出力と比例するようにグラム行列でも分類の特徴量として有用であるような結果が出ているので、
グラム行列も十分元画像の性質を持っていることが予想できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/2777180e-1fd0-cd9e-3a16-801666098d52.png)




# 構造を残しつつスタイルだけ変換する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/bd83b971-0c58-c5f8-9c40-60423bf53917.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/db2108b7-8715-9583-1c43-250e08977f5f.png)



片方はプレプリントです
またもgatys氏の研究紹介です


簡単に全体を説明すると


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/9bcbf88e-5842-2943-6611-6216e1224228.png)


テクスチャの取得(近似させていく話)は前回の論文と同じ
同時に画像の構造(家や木や川)を残したまま、上からテクスチャを乗せたい
そのためにノイズ画像を、「構造の保存+テクスチャ」の画像へと最適化していく方法

以降から「テクスチャ」と「スタイル」という単語が入り混じりますが、
テクスチャはスタイルを表す最小の単位画像のような物と考えてください



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/ff02e911-0f80-6926-a293-e4b900efa2f3.png)



構造はこのようになっており、左側のネットワークは先に紹介した論文と同じ構造です
テクスチャを取り出したいスタイル画像(style)からスタイルの特徴をVGGでとりだします

右側が新しい考えで、VGGネットワークに構造画像(content)を通した結果のうち、
深い層の見ている全体的な構造情報を二乗誤差によって最適化する

という考えです


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/7db719d1-0f06-07e3-41e2-e95853c2a295.png)

さらに単純に話すと
上記matthew氏の論文で畳み込みネットワークが構造を深い層で学習していることが分かったため、
構造もノイズ画像にテクスチャとともに構築させたらうまくいくのでは？
という内容です

こちらが分かりやすいかと。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/dbc3a379-0db8-62a3-fef3-85cace8f03f4.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/e6086021-3dc5-70a1-b8d3-d955534f4a1a.png)

[yosinski氏のCNNが構造を出力してくれる例をyoutubeで見てみる](https://www.youtube.com/watch?v=AgkfIQ4IGaM)


コンテンツの損失はグラム行列に変換しません
そのままの構造を正しく保持したいからです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/928ec595-4b8a-1f6a-9a78-fb69c94041d7.png)

この損失も解析的に求まるため、誤差逆伝播でノイズ画像へフィードバックすることが可能です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/e5292571-c35c-ce97-548a-ab1cf4d83ae5.png)


復習になるのでスタイルの損失については画像だけ載せておきます


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/7e3801b9-a47f-6782-8544-21ef54d39350.png)


こうして計算されたstyleとcontentの情報に重みを付けてノイズ画像へフィードバックします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/dac63cea-bf8b-d63a-42ab-6eb4990e5fca.png)

当然重みの大きさによってスタイルだけが残るか、構造だけが残るかという話にもなります。
下図の数値は重みのαとβの割合であり、左上はαの割合が小さく、右下はαの割合がβに対して大きい場合です。
確かに左上は構造情報がほとんど残っておらず、右下は川などがそのまま残っていることが確認できます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/1c203259-5131-3765-906e-cb5db9a52eef.png)





前のスタイルに関する論文でも何層目までを使うか、という話がありましたがコンテンツ画像も実験した結果が紹介されています



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/c46f6884-c901-2a86-2e8e-b0e120203cb8.png)


コンテンツ画像はpoolingによって深い層になるほど輪郭が曖昧になっていきます。
浅い層ほど構造を元画像のように明確にのこしているので、二行目に見られるような二層目のコンテンツ情報を使った場合には
元画像の上に色を付けたような画像になります。
深い層になれば元画像はより抽象的になり歪みも生じてきます。
どんな画像を生成したいかにもよりますが、元画像の構造が強く残りすぎるのは、コンテンツ画像に重みを大きくつけている場合と似た結果になってしまう傾向があります。



学習のスタート画像に関する実験も載っていました
実験の内容は、今までノイズ画像を元にVGGの出力を最適化してきたが
コンテンツ画像(A)もしくはスタイル画像(B)をノイズ画像の代わりに入れて最適化したほうが、
自然なスタイル変換画像が得られるのでは？という考えです


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/087537f7-6e49-6dfa-6d90-2747a02115e8.png)


下側四つの画像(C)は異なるノイズを入力にした場合です
結果としてどれも似たような結果になりノイズから初めても問題は無さそう
ノイズの場合出発点が異なる影響で生成画像も異なる結果となるが、
元画像が固定されているA,Bの場合は再度走らせた場合にも同じ変換結果に収束することがわかっています




この研究の問題点は、コンテンツ画像が一枚一枚入力のたびに細かいピクセル情報を最適化する必要があり非常に生成が遅いことです。
生成が遅いという事は、スマホアプリに組み込んだりしたい場合、写真を撮った後にユーザーが10分とか変換のために待つ必要が出てきてしまうわけです。


#最適化をより高速にしたい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/89f2eda4-b951-3dcb-99a7-a6debf2be451.png)


変換をまたず、リアルタイムで変換するための方法です
構造全体を俯瞰してみます


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/4aa8db18-638c-9313-02ee-5ecc3622a598.png)


右側 loss networkは今までの研究と変わりはありません
入力yに対してycからcontentを、ysからstyleを取り出し、最適化して近づけます
この最適化の行為が非常に遅いので、その手前に生成ネットワークを置きました


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/559cf103-8034-3e92-eeaf-c1b672df4d57.png)


生成ネットワークは元画像とスタイル変換後画像を比較し、その差分を上記式で最小化します
スタイル変換画像を「生成」することが目的です
一度ネットワークを作っておけばあとは学習を必要とせず、順伝播するだけでスタイル変換画像が得られるため、
スマホアプリなどでも使えるようになります

多少式の記述方法が異なりますが、以下でも再度説明します
Cがチャネル、H,Wは縦横サイズです

コンテンツの情報は

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/e91dcd40-69ea-9cfe-3206-181e7b0ed6eb.png)

スタイルの情報は

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/d3acd303-f4ad-834e-e4c5-ab61ff423852.png)

でグラム行列に直してから

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/362e99ea-a050-206d-318a-b36a0cf7963e.png)

でスタイルの損失とします。

余談ですが、スタイル画像のグラム行列は計算効率を上げるため以下の式に整えてから計算しているようです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/3ea8168d-935b-b618-9447-ba1d25feb395.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/78c9f499-0230-c721-1081-18d35c22ffec.png)


いくつかの画像をスタイル変換画像に変換できるような生成ネットワークができたらこれをloss networkと切り離してスマホアプリに組み込むわけですが、
スタイル変換の生成のために色々な画像を使って学習させると失敗することが分かっており、早期停止を使ったり学習画像を減らす必要があります




順伝播するだけなので、変換速度は数百倍早くなっています
gatysらの出力時間に対してoursの速度

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/efe0e8b5-95ef-b1dc-42bc-4c1142c7cd9f.png)




その精度もgatysらと比べて特に問題ないようなスタイル変換結果が得られる


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/a9972df0-1979-4b46-4bf2-9010439157a7.png)



# より精度良く、早くスタイル変換を

[元発表のリンクを置いておきます](https://www.youtube.com/watch?v=Br9rySL7GbE)
[資料のリンクも](https://docs.google.com/presentation/d/0B_-hq6gL70bUdDBCUHVJWVlWWjQ/edit#slide=id.p6)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/47cc26e2-f15f-0144-95c5-e5b970eb7915.png)



今までからの変更点は
「インスタンス正規化」と「ジュールズサンプリング」の発想
らしいのですが

ここあたりから理解がおいついていないので、以下参考程度に読んでください。


そもそもGANやVAEと同じく、スタイル変換のアプローチはノイズからの画像生成を目指しています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/d5b9f8c1-b45d-f9e1-f852-b219be62ebbd.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/09e96e71-bb75-2e12-ea9d-2149899e50ec.png)

・手元の画像は神のみぞ知る確率分布から採取したデータである
・つまり手書き画像だったならば、再度手書きをしたら生成することができる
・この手書きによって得られるピクセルの配列パターンである母集団の分布は謎であるので
・正規乱数(ホワイトノイズ)を仮定してネットワークを使って生成しよう
という考えが背景にあります


正規乱数によるサンプリングもありますが、この手法では一様分布からのサンプリングを行っています
・そもそも非凸な目的関数なので収束は難しい
・一様なノイズから一番よさそうなシードを見つけることで画像生成にアプローチしている

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/3e4f76ac-ce3b-eff2-7595-793826eaa3b2.png)


テクスチャだけを作りたいならば今まで通り一様乱数からテクスチャ画像Xを生成するようなネットワーク(ジェネレータ)を訓練する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/d536a4c8-aa3e-7690-d4bc-74b2fa8807fb.png)

しかしコンテンツ画像も考慮したい場合にはジェネレータにコンテンツ元画像も入力することで、X側に元画像情報が残るように最適化を行わせる


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/07906a5d-2afe-06f9-4ff0-b5d816c537cf.png)


今回の研究では「インスタンス正規化」が構造に入ってきます

今までのバッチ正規化ではネットワークが画像のバッチ(特徴マップ)ごとに平均や分散を計算しておく必要がありました
正規化のパラメタも固定することで計算効率を上げ、均一な出力を得ることができるようになった

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/7cfb357d-a2e2-759f-8877-e66b7a4448ba.png)



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/4970ef2e-cc21-a8d7-d072-73ffef647527.png)



どのように生成器を最適化していくのかについては変分法などが登場してくるのですが

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/be1bc0e4-4206-2481-319c-0df9b9e18ef9.png)

これを説明できるレベルにはまだ至っていないのでこの辺でまとめを締めさせていただきます

#残っている周辺論文

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/5dde889c-f896-d2f2-dc41-8a5079ad4e2b.png)



#実装コード紹介

tensor flowから[Neural Style Transfer with tf.keras](https://github.com/tensorflow/models/blob/master/research/nst_blogpost/4_Neural_Style_Transfer_with_Eager_Execution.ipynb)としてコードが提供されています


#以上

スタイル変換の技術を調べたのでまとめ、共有しました
たのしい