20190226のDeepLearningに関する記事は5件です。

最新最強の物体検出技術M2Det

はじめに

AAAI19で北京大学、アリババ、テンプル大学の合同チームにより発表された物体検出技術M2Detについての解説です。

  • Qijie Zhao et al., M2Det: A Single-Shot Object Detector based on Multi-Level Feature Pyramid Network.

下記は論文から引用した他手法との性能比較図ですが、水色の★がM2Detです。横軸が処理時間、縦軸が検出精度なので、左上にいくほど性能が高い(高速かつ高精度)ことになりますが、M2DetはRetinaNetやRefineDet、SSDやYOLOなどの有名な既存手法を凌駕していることがわかります。
fig1.jpg

ざっくりとしたアーキテクチャは以下のように図示されていますが、これだけではよくわからないので細かく中身を見ていきたいと思います。
fig2.jpg

アーキテクチャ概要

fig3.jpg
M2Detのアーキテクチャの概要は上図のようになります。大きく分けてBackbone networkとMulti-Level Feature Pyramid Network (MLFPN)、Prediction layersから成ります。それぞれの詳細について以下で見ていきます。

Backbone network

fig1-1.jpg
ここは単なる特徴抽出器なので、画像分類向けの任意のアーキテクチャが利用できます。論文ではVGG-16とResNet-101が用いられています。

Multi-Level Feature Pyramid Network (MLFPN)

MLFPNは以下3つのモジュールで構成されます。

  • Feature Fusion Module (FFM)
  • Thinned U-shape Module (TUM)
  • Scale-wise Feature Aggregation Module (SFAM)

Feature Fusion Module v1 (FFMv1)

fig1-2.jpg
FFMは複数回登場しますが、まず最初のFFM(これをFFMv1と呼びます)は、Backbone networkで得られる特徴ピラミッドのうち異なる解像度の特徴マップをフュージョンすることでBase featureと呼ぶ特徴マップを生成します。以下はBackboneにVGG-16を利用し、conv4_3とconv5_3の特徴マップをFFMv1がフュージョンする様子を示しています。最初のConv層でチャネル方向を圧縮し、かつ解像度が小さい特徴マップについてはアップサンプルして解像度をそろえたうえでConcatすることでフュージョンを実現します。
fig4.jpg

Thinned U-shape Module

fig1-3.jpg
TUMはエンコーダ-デコーダ構成となっており、FFMからの出力を受け取って再度マルチスケールな特徴ピラミッドを生成します。デコーダの各レイヤからの出力をアップサンプルしたうえでエンコーダの対応するレイヤ出力と足し合わせ、さらに1x1 Convを適用してから出力します。
fig5.jpg

Feature Fusion Module v2 (FFMv2)

fig1-4.jpg
TUMは多段に接続され、その間にFFMが再び挿入されます(これをFFMv2と呼びます)。このとき、TUMが生成する特徴ピラミッドのうち、最大解像度の特徴マップだけが次のTUMの入力として利用されます。FFMv2は、このTUM出力とBase featureに1x1 Convを適用したものをConcatすることでフュージョンし、次のTUMの入力とします。スタックされたTUMのうち、初期のTUMはShallowな特徴を抽出しますが、後段に行くにつれDeepな特徴を抽出できるようになります。
fig6.jpg

Scale-wise Feature Aggregation Module (SFAM)

fig1-5.jpg
多段に接続した各TUMから得られる特徴ピラミッドを最後に統合するのがSFAMです。SFAMでは、それぞれの特徴ピラミッドから同じ解像度の特徴マップを取り出してチャネル方向にConcatし、さらにSqueeze-and-Excitation Netsで用いられているアテンション機構を導入しています。まず、各チャネルに対してGlobal average poolingを適用して各チャネルの情報を圧縮し(Squeeze)、それを2つの全結合層に通すことで各チャネルに対する重み係数に変換します(Excitation)。これを各チャネルにかけることで最終的な出力とします。
fig7.jpg

Prediction layers

fig1-6.jpg
SFAMから得られる最終的な特徴ピラミッドに対し、SSD等と同様にConv層をくっつけて物体クラスの予測と物体位置の回帰を行います。特徴マップの各点に対して6種類のアンカーボックスを3種類のアスペクト比で適用し、信頼度が0.05以上となるものだけを残します。そしてSoft-NMSを適用してさらに精度を高めます。

性能評価実験

論文中ではMS COCOにおけるSOTAとの比較が示されています。以下はその結果の一部をプロットしたものです(冒頭の図と凡例が違っています、すいません)。▲印がFaster R-CNNに代表されるtwo-stage手法、●印がM2Detを含むone-stage手法です。M2Detは精度と速度の両面で他のone-stage手法を上回っており、場合によってはtwo-stage手法よりも高い精度を示しています。詳細な数値については論文中のTable 1を参照いただければと思います。
fig8.jpg
論文中では各モジュールの有無や個数でどのように性能が変わるかといったAblation studyも細かく報告されておりますので、興味がある方はそちらを参照ください。

考察

著者らが述べているM2Detにおける性能向上要因は大きく以下の2つです。

  • 既存のone-stage手法ではBackboneで生成される特徴ピラミッドをそのまま使うか、あるいは数層程度のレイヤを追加しているだけに過ぎない。M2DetではFFMでBase featureを生成した後、それをさらに多段のTUMに入力することで結果的にBackboneよりも大幅にDeepかつ物体検出に適した特徴表現を獲得できる。
  • SFAMで生成されるマルチレベルな特徴ピラミッドの各レベルは、複数レベルのTUMの出力を統合したものであり、これにより物体ごとの見た目の複雑さの多様性をうまく扱うことができている。

つまり、MLFPNによって物体のサイズ変化に対応するためのマルチスケールな特徴、かつ見た目の複雑さの変化に対応するためのマルチレベル(Shallow → Deep)な特徴が獲得できるということがポイントです。下図はこの様子を確認するためにM2DetのClassification Conv層のアクティベーションをスケールとレベルごとに可視化したものです。入力画像には大きさの異なる複数の人物、車が写っていますが、同じ物体でもその大きさに応じて異なるスケールで強く反応が出ています(下図上段)。一方、信号や車、人物など異なる物体であっても、その大きさがほぼ同じであれば反応するスケールは同じです(下図下段)。ただし、スケールは同じですが見た目が単純な信号機はShallowなレベルで反応が強く出ているのに対し、見た目が複雑な人物はよりDeepなレベルで強く反応しています。
fig9.jpg

まとめ

SSDやYOLOに代わる物体検出技術として有望そうなM2Detについて解説しました。論文を見る限りは精度、速度共に申し分なく、ぜひ使ってみたい技術ではあります。ただ、アーキテクチャがかなり複雑なので、なぜこれでそんなに早くなるのかはよくわかりません。実装にはPyTorchが使われているようですが、残念ながらコードはまだ公開されていません。ただ著者のGitHubページを見ると3/1にコード公開予定とのことなので、近いうちに試してみることができそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GridSearchCVの評価指標にユーザ定義関数を使用する方法

はじめに

関連の記事が少なく、公式の説明も自分には分かり辛かったので書いてみました。
公式: https://scikit-learn.org/stable/modules/model_evaluation.html#scoring

手順概要

  1. ユーザ定義関数を定義する。
  2. sklearn.metrics.scorer.make_scorerにユーザ定義関数を渡してスコアラーを生成する(評価関数として使えるようにする)。
  3. 2.のmake_scorerをGridSearchCVのパラメータ「scoring」に設定する。

(ユーザ定義関数の内容に関して、今回は私のコードをそのまま貼りましたが、当然個々に寄ると思うので本記事のユーザ定義関数の内容を熟読してもあまり参考にはならないと思います。上記2点を満たしていれば問題ないです。)

ユーザ定義関数に関する注意点

下記2点を満たすように定義・実装すること。

  1. y_test(正解データ)とy_pred(推論結果データ)を引数として渡すことができる。
  2. 戻り値としてLoss(もしくは評価値)を返す。

make_scorerの設定に関する注意点

make_scorerのパラメータ「greater_is_better」の設定

  • ユーザ定義関数の戻り値を評価値にする場合: True
  • ユーザ定義関数の戻り値をLossにする場合: False

実装

「手順概要」の1.

ユーザ定義関数
def calc_score(y_train: np.array, y_pred: np.array):

  threshold = 1
  ignore = 10

  # 比較結果格納用配列の作成
  flg = np.zeros((y_train.shape[0],1))

  y_train = y_train.reshape([y_train.shape[0], 1])
  y_pred = y_pred.reshape([y_train.shape[0], 1])

  # 正解値と予測値の差分データを作成
  y_diff = y_train - y_pred

  for cnt_row in range(y_diff.shape[0]-DIFF):
      # 差分データの絶対値が閾値を超えていなければTrue
      if abs(y_diff[cnt_row]) < threshold:
        flg[cnt_row] = True
      else:
        flg[cnt_row] = False

  # 正解率を計算
  cnt_true = 0
  for cnt_row in range(flg.shape[0]):
    if flg[cnt_row] == 1:
      cnt_true += 1
  score = cnt_true / flg.shape[0]

  return score
main()
...

# 例
params = {'hidden_layer_sizes': [(100,), (100, 10), (100, 100, 10), (100, 100, 100, 10), (100, 100, 100, 100, 10),  (100, 100, 100, 100, 100, 10)],
             'max_iter': [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000], 
             'learning_rate_init': [0.001, 0.0001, 0.00001], 
             'early_stopping': [True, False], 
             'tol': [0.0001, 0.00001], 
             'batch_size': [100, 200, 300], 
             'verbose': [True], 
             'random_state': [42]}

# 例
mlpr = MLPRegressor()

from sklearn.metrics.scorer import make_scorer
# 「手順概要」の2.
my_scorer = make_scorer(calc_score, greater_is_better=True)
# 「手順概要」の3.
gs = GridSearchCV(mlpr, param_grid=params, cv=10, scoring=my_scorer)

...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Deep LearningでバーチャルYouTuber作ってみた

概要

  • GANを使ってVTuber作ってみた
  • 人間と猫のモデルを作った
  • リアルタイムで動物のモデルを動かす試みはまだあまりないと思う

※注意
背景では、バーチャルYouTuberについて熱く語っているので、興味のない方は飛ばして下さい。
GANについて軽く技術の紹介をしていますが、すでにご存じの方はDeep Leaning based Virtual YouTuberまで飛んで下さい。

背景

バーチャルYouTuberをご存知でしょうか?バーチャルYouTuberとは、モーションキャプチャを用いてキャラクターモデルの操作を行い、YouTubeやMirrativ等のプラットフォームで配信を行う、言わばYouTuberをキャラクターにしたエンターテイメントの1つです。

バーチャルYouTuberランキングを運営する株式会社ユーザーローカル様の調査によれば1 、2018/1/31時点で活動しているのは181名でしたが、同年12月には6000人以上に増加、そして2019/2/21には7000人を突破したとの報告もあり2、バーチャルYouTuber市場が今後も拡大していくことが予想されます。かく言う私も沼にはまったファンの1人で、最推しの樋口楓さんとニコニコ町会議でお話しをしたり、18時間かけて東北から大阪までライブを見に行く程のハマり具合です。

これだけの人数が活動している中で、「YouTubeの収益化」をクリアするのは大きな問題の1つだと私は考えています。様々な基準がありますが、大きく分けて①チャンネル登録者数1000人以上、②過去12ヶ月の総再生時間が4000時間以上という2つを満たす必要があります。多くの方が活動し、企業も参戦する中、この基準を満たすのは容易ではありません。

そこで、これまでキャラクターベースのモデルではなく、GANを使った「Deep Learning系Vtuber」という新しい形態でのモデルを提供することができれば、話題性を生み、チャンネル登録者数増加に繋げられるのではないかと考えました。以降、大学の講義のPBLとして取り組んだことについて説明をしていきたいと思います。

(という、背景や問題提起は後付です。ただ単純に、GANでVtuberできんじゃね?と思ったのが始まりです。)

GAN

機械学習を用いて作るモデルは、Discriminative modelとGenerative modelという2つに分類されます。それぞれの詳細な説明は省略しますが、今回使用するGAN3はGenerative modelに属する手法です。(一部構造にDiscriminative modelを使用していますが)

下図がGANのネットワーク構造です。

GANはDiscriminatorとGeneratorと呼ばれる2つのネットワークから構成されます。それぞれの目的は以下のとおりです。

Network 目的
Generator 画像の生成
Discriminator 本物の画像と偽物の画像の識別

minimax gameを想像して頂ければ分かりやすいかもしれませんが、GANは2つのネットワークを競わせる形で学習を行っていきます。Generatorは本物に近い画像を生成できるように(Discriminatorを欺けるように)学習をします。Discriminatorでは生成された画像と本物の画像を正しく識別できるように学習をしていきます。これを繰り返し、最終的にDiscriminatorで識別することができなくなるまで学習を行ったGeneratorを用いてFake imagesを作成するのがGANの目的です。

数式等や詳細なアルゴリズムはこちらを見て頂ければ分かりやすいかと思います。
はじめてのGAN
Pokemon_GAN

Pix2Pix

今回使用した手法がPix2Pix4と呼ばれる、GANの拡張手法です。

Pix2Pixではある入力をされた画像を、本物っぽい画像に変換することが可能です。例えば上図の場合、カバンの線画を入力として与えた場合、それに着色を行い本物のカバンのような画像を出力します。

GANと異なる点としては、2つの画像のペアを用いて学習するということです。例として、線画のキティちゃんの着色を考えます。下図のように、線画をGeneratorに入力し、出力として色付きキティちゃんを得るのが目的です。

Discriminatorでは、「入力 + 正しい色のキティちゃん」と「入力 + 生成された色付きキティちゃん」の識別を行います。

ペア画像に対しこれらを繰り返し学習していくことで、片方の入力を得た際に、もう一方の画像を出力するのがPix2Pixです。

Deep Leaning based Virtual YouTuber

ここからが本題です。今回のプロジェクトでは、先に紹介したPix2Pixを用いています。「Face landmark + 実際の画像」をペアとして学習を行い、作成したモデルに、リアルタイムで取得されたlandmarkを入力し、あたかもベースとなった人間が動いているかのような出力をすることで、Vtuberのような形式を目指します。

今回は以下のリポジトリをForkし開発を行いました。Dat Tran様に感謝致します。
Thank you, datitran (Dat Tran) ! I really appreciate it.
datitran/face2face-demo

とりあえず色々調整して学習して動かしてみた

結果がこちらです。
mel.gif

左側の線画がリアルタイムで取得した私の動きで、右側が出力となっています。
(本当は一番左に私の顔が写っているのですが、恥ずかしいのでカットしています。)

結構ちゃんと動いてます。
データを変更し学習しても、そこそこの物が出来上がりました。WebカメラとGPUだけでここまでできるなんて凄いなーというのが私の感想です。他人の顔を動かすような研究はたくさん行われていそうなので、こんなものかと思われる方もいるかもしれません。実際、世界初のAIニュースキャスターが中国で誕生するなど、GANの精度は日に日に向上しています。


法律的な問題点

GANを使う以上、元となる人物のデータが必要となります。先ほどの例では、ドイツのメルケル首相を使用しました。また、遊びではじめしゃちょーのモデルも作成しました。ここで、問題になってくるのが法律的な問題です。

実は日本には著作権法には47条の7という、世界的に見ても珍しい条文があります。簡単に言えば「情報解析が目的であれば、著作権者の承諾なしに自由に使用ができる」といった内容です。しかも、非営利目的の利用に限定されていません。我々AI開発者・研究者にとっては天国のような内容です。

しかし、情報解析に機械学習が含まれているのかどうかといった解釈や、他の法律(他国含む)等を考慮すると怪しい気がしてきます。そこで、「動物のデータなら大丈夫ではないか?」と考えました。以降は動物のデータを用いた取り組みについてご紹介していきます。

Animal Model

前処理

人間でも動物でも、Pix2Pixを使う以上ペアの画像を作成する必要があります。前処理の流れは大まかに、このようになっています。

  1. データ(動画)入手
  2. フレーム分解
  3. 分解した画像からLandmarkを取得
  4. フレーム分解した画像とLandmarkをペアとして学習

今回、最も苦戦したのが「3. 分解した画像からLandmarkを取得」です。人間のlandmark検出であれば、OpenCVやdlibといったライブラリを使えば簡単かつ、高精度に検出することが可能です。しかし、動物のlandmark検出となると話しは変わってきます。

Object detectionでは「どこに、何の動物がいるか」の検出は可能です。しかし、それぞれの動物の顔の特徴までは検出できません。調べた限り、動物の顔の特徴抽出器も多くは存在しません。(需要ないだろうし)

Landmark detectorを作成するには、顔がもつ特徴点を画像ごとに記録し学習を行う必要があります。人間であれば、画像のように60個以上ある特徴点の座標を1枚ごとに記録する必要があります。学習には数千枚を超えるデータが必要であるため、これを1から作るのは現実的ではありません。

猫の特徴抽出

そこで、頑張って探し辿り着いたpycatfdを一部改変し、使用することにしました。
marando/pycatfd

抽出された特徴点の座標をもとに、以下のような画像を生成するプログラムを作成しました。

pycatfdですが、猫が少しでも横を向くと検出ができなくなってしまう(または、誤検出がおこる)という問題があります。また、そもそもの検出精度も悪く化物のようなデータになってしまいます。そこで、取得した特徴点から、様々な処理を行ったデータを用意し、実験を行いました。行った処理内容は以下の通りです。

データ(動画) 処理内容
①某CMに登場する猫 無処理
②某CMに登場する猫 データ選定
③某CMに登場する猫 耳なし
④某CMに登場する猫 擬似耳
⑤YouTubeに投稿された猫 データ拡張 反転
⑥YouTubeに投稿された猫 データ拡張 反転 + 縮尺変更
  • ②服を着ていたり、帽子を被っていたりする画像を削除
  • ③耳の検出が不安定なため、耳がないlandmark画像を作成
  • ④耳の検出が不安定なため、座標を指定して擬似的な耳を作成
  • ⑤⑥使いやすそうなデータが見つかったため、データを変更
  • ⑤データ数が少ないため、水平方向に反転した画像を追加
  • ⑥反転に加え、縮尺を変更することでノイズに強くなるのではないかと予想

色んな試行錯誤をした結果

最も悪かった結果 (前処理①)
badneko.gif

最も良かった結果 (前処理⑤)
goodneko.gif

考察

遠くから見れば猫っぽいかな... くらいの精度でした。
原因としては大きく分けて、以下の2つだと考えています。

  1. 特徴の違い
    猫の特徴を学習したモデルに、人間の特徴を入力しているため、精度に影響したと考えられれます。今回は人間の目と顔の輪郭のみを入力としました。(他の特徴も入力すると精度が下がったため)そのため、特徴の変換器みたいなものを作成する必要がありそうです。

  2. データ
    猫の特徴抽出の説明において、検出精度が低いと記述しました。下の画像の耳が垂れている猫のように、ありえない画像を学習に使ってしまったのも原因と考えられます。

終わりに

精度向上の余地はありそうですが、一旦開発はここで終了としています。実は開発メンバーの中にGANを専門に勉強、研究をしている人間はいませんでした。そのため、感想やコメント、アドバイスを頂ければ幸いです。最後まで読んでいただきありがとうございました。

Github

整理中(2019/02/26)
gojirokuji/dtuber


  1. 株式会社ユーザーローカル, バーチャルYouTuber、本日6000人を突破(ユーザーローカル調べ), https://www.userlocal.jp/news/20181219vs/ 

  2. 株式会社ユーザーローカル, バーチャルYouTuber、本日7000人を突破(ユーザーローカル調べ), https://www.userlocal.jp/news/20190221vn/ 

  3. Ian J. Goodfellow, Jean Pouget-Abadie, Mehdi Mirza, Bing Xu, David Warde-Farley, Sherjil Ozair, Aaron Courville, Yoshua Bengio, Generative Adversarial Networks, arXiv:1406.2661, https://arxiv.org/abs/1406.2661 

  4. Phillip Isola, Jun-Yan Zhu, Tinghui Zhou, Alexei A. Efros, Image-to-Image Translation with Conditional Adversarial Networks, arXiv:1611.07004, https://arxiv.org/abs/1611.07004 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GANを学びたいけれど、価値関数の数式に困惑している人へ

GANの価値関数(目的関数)

image.png

本記事ではGANを学び始めたら見かけてしまう、一見複雑そうな上記の式を解説していく

GANのモデル

まずはGANのモデルと学習の流れを軽く見てみる
image.png

順伝播(Discriminatorに判定させてみる)

・Generatorがノイズ(例えば100次元のベクトル)から画像っぽいものを生成する
もちろん最初はでたらめな画像しか生成できない

・Discriminatorには本物の画像、もしくはGeneratorが生成した画像のどちらかをランダムに入れる

・Discriminatorがスコアを出すときはシグモイド関数を使うため、
本物だと思ったら1を出力、Generatorが生成した画像(偽物)だと思ったら0を出力する

正確にいうと「Discriminatorに来た画像が本物である確率」

逆伝播(Discriminatorの重みを更新するとき)

Discriminatorの理想は本物が来たら1を出力して、偽物が来たら0を出力

・ここはシンプルに画像分類をしたときのCNNの重みの更新と同じ
GANだからと言って新しい何かがあるわけではない
正しく判定(本物が来たら1を出力して、偽物が来たら0を出力)できるように重みを更新する

逆伝播(Generatorの重みを更新するとき)

Generatorの理想はDiscriminatorが間違って判定(偽物の画像に対して1を出力)すること

・これは実装上のポイントにもなるが
Generatorの重みを更新するときは、誤差がDiscriminatorを通ってくるため
Discriminatorの重みの更新を止めなければならない

もしDiscriminatorの重みの更新を止めなかったら、Discriminatorの重みが偽物に対して1を出力するようになってしまう(今はGeneratorの理想に近づくような更新がされているため)

本題へ

GANの価値関数(再掲)
image.png

右辺について

$E$:期待値
$x$~$p_{data}(x) $:本物の画像がDiscriminatorに来たとき
$D(x)$:Discriminatorに本物の画像が来たときのスコア

$x$~$p_z(x)$:Generatorが生成した画像(偽物)がDiscriminatorに来たとき
$G(z)$:Generatorがノイズzから生成した偽物画像
$D(G(z))$:Discriminatorに(Generatorが生成した)偽物の画像が来たときのスコア

logのグラフ

log(x)のxの値が大きいほどlog(x)は大きくなる
image.png

Discriminatorを評価するとき

・Discriminatorの理想は本物が来たら1、偽物が来たら0を出力すること

右辺第一項

image.png
上記の式を言語化すると、本物の画像がDiscriminatorに来たときのDiscriminatorのスコアである

本物の画像のため、シグモイド関数の出力は0~1の内、を出力してほしい

理想的な出力である$D(x)=1$を出力できれば、結果的に上記の式(右辺第一項)を最大化できる

右辺第二項

image.png
上記の式を言語化すると
Generatorが生成した画像(偽物)がDiscriminatorに来たときのDiscriminatorのスコアである

偽物の画像のため、シグモイド関数の出力は0~1の内、を出力してほしい

理想的な出力である$D(G(z)))=0$を出力できれば、$log(1-0)$になり
結果的に上記の式(右辺第二項)を最大化できる

Discriminatorまとめ

上述のようにDiscriminatorの理想通り(本物の画像には1を出力、偽物の画像には0を出力)になれば、
第一項と第二項はどちらも最大になる(大きい値と大きい値を足し合わせているので右辺トータルも最大)

このことを表しているのが、価値関数の左辺にある$max_D$である

Generatorを評価するとき

・Generatorの理想はDiscriminatorが間違って判定(偽物の画像に対して1を出力)すること

・右辺第一項はGeneratorに関係ないので無視する

右辺第一項

image.png
本物の画像がDiscriminatorに来たときのDiscriminatorのスコアを表している

これはGeneratorとは関係がないのでGeneratorを評価するときは無視する

右辺第二項

image.png
Generatorが生成した画像(偽物)がDiscriminatorに来たときのDiscriminatorのスコアを表している

GeneratorはDiscriminatorが間違ってほしい
Generatorにとっては、偽物画像に対してDiscriminatorが1を出力するのが理想

理想的な出力である$D(G(z))=1$を出力できれば、$log(1-1)$になり
結果的に右辺第二項を最小化できる

Generatorまとめ

上述のようにGeneratorの理想通り(Generatorの生成した画像に対してDiscriminatorが1を出力)になれば、第二項は最小になる(第一項は無視、第二項最小化で右辺トータルは最小)

このことを表しているのが、価値関数の左辺にある$min_G$である

全体まとめ

文字が多くて圧倒されてしまいがちな式だが、
「GeneratorとDiscriminatorのそれぞれの理想の状況ってどうなんだっけ?」と落ち着いて考えていけば、「そんな複雑なことを表している式ではないんだな」ということが理解できると思う

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Keras (Cifar10)

Library

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

from tqdm import tqdm

from sklearn.model_selection import train_test_split 

import tensorflow as tf

from keras.models import Sequential
from keras.layers import InputLayer, Activation ,Dropout ,Flatten, Dense
from keras.layers import LeakyReLU, Conv2D, MaxPooling2D, BatchNormalization
from keras.layers import GlobalAveragePooling2D

from keras.layers.noise import GaussianNoise

from keras.optimizers import Adam
from keras.callbacks import ReduceLROnPlateau, EarlyStopping

Data

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
n_classes = 10
pos = 1

plt.figure(figsize=(10, 10))

for targetClass in tqdm(range(n_classes)):
  targetIdx = []
  # クラスclassIDの画像のインデックスリストを取得
  for i in range(len(y_train)):
    if y_train[i] == targetClass:
      targetIdx.append(i)

  # 各クラスからランダムに選んだ最初の10個の画像を描画
  np.random.shuffle(targetIdx)
  for idx in targetIdx[:10]:
    img = x_train[idx]
    plt.subplot(10, 10, pos)
    plt.imshow(img)
    plt.axis('off')
    pos += 1

plt.show()

image.png

n_classes = 10

x_train = x_train.astype(np.float)/255.0
y_train_oh = np.identity(n_classes)[y_train]

x_train, x_valid, y_train, y_valid = train_test_split(x_train, y_train_oh, 
                                                       test_size=0.20, 
                                                       stratify=y_train,
                                                       random_state=100)

print (x_train.shape)
print (y_train.shape)
print (x_valid.shape)
print (y_valid.shape)

image.png

Model 1 (no data augmentation)

input_shape = x_train.shape[1:]

def create_model():
  model = Sequential()
  model.add(InputLayer(input_shape=input_shape))
  model.add(GaussianNoise(0.15))

  model.add(Conv2D(128, (3, 3), padding='same'))
  model.add(LeakyReLU(0.1))
  model.add(Conv2D(128, (3, 3), padding='same'))
  model.add(LeakyReLU(0.1))
  model.add(Conv2D(128, (3, 3), padding='same'))
  model.add(LeakyReLU(0.1))           
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Dropout(0.5))

  model.add(Conv2D(256, (3, 3), padding='same'))
  model.add(LeakyReLU(0.1))
  model.add(Conv2D(256, (3, 3), padding='same'))
  model.add(LeakyReLU(0.1))
  model.add(Conv2D(256, (3, 3), padding='same'))
  model.add(LeakyReLU(0.1))           
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Dropout(0.5))

  model.add(Conv2D(512, (3, 3), padding='valid'))
  model.add(LeakyReLU(0.1))
  model.add(Conv2D(256, (1, 1)))
  model.add(LeakyReLU(0.1))
  model.add(Conv2D(128, (1, 1)))
  model.add(LeakyReLU(0.1))          

  model.add(GlobalAveragePooling2D())
  model.add(Dense(n_classes, activation='softmax'))

  return model
model = create_model()

model.summary()

image.png

image.png

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics =['accuracy'])
early_stopping = EarlyStopping(monitor='val_acc', patience=5, mode='max', 
                              verbose=1)
lr_reduction = ReduceLROnPlateau(monitor='val_acc', patience=5, 
                                 factor=0.5, min_lr=0.00001, verbose=1)
callbacks = [early_stopping, lr_reduction]

batch_size = 32
epochs = 10

history = model.fit(x_train, y_train, 
                    batch_size=batch_size, 
                    epochs=epochs, 
                    validation_data=(x_valid, y_valid),
                    callbacks=callbacks,
                    verbose=1)
plt.figure(figsize =(5,3))
plt.plot(history.history['loss'], marker='.', label='train')
plt.plot(history.history['val_loss'], marker='.', label='validation')
plt.title('Loss')
plt.grid(True)
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(loc='best')
plt.show()

plt.figure(figsize =(5,3))
plt.plot(history.history['acc'], marker='.', label='train')
plt.plot(history.history['val_acc'], marker='.', label='validation')
plt.title('Accuracy')
plt.grid(True)
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.legend(loc='best')
plt.show()

Model 2 (data augmentation)

datagen = ImageDataGenerator(
    #featurewise_center=True,
    #featurewise_std_normalization=True,
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    horizontal_flip=True)

datagen.fit(x_train)
epochs = 10
batch_size = 32

model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),
                    steps_per_epoch=len(x_train)//batch_size, 
                    epochs=epochs,
                    validation_data=(x_valid, y_valid), 
                    validation_steps=len(x_valid)//batch_size,
                    callbacks=callbacks,
                    verbose=1)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む