konchangakita

KPSを一番楽しんでいたブログ 会社の看板を背負いません 転載はご自由にどうぞ

【おてがる開発環境をつくろう】さいきょうのえでぃた VS Code

ルーキー開発者が最強のえでぃたを装備した
おてがる開発環境シリーズの最後は、「VS Code」です
今までの、DockerPythonJupyter 全てにおいて、VS Code で便利に使うことができます

【おてがる開発環境をつくろう】
1.まずは Docker Desktop インストール
2.Docker であそぶ Python 入りのコンテナつくる
3.コンテナで Jupyter Lab 環境
4.さいきょうのえでぃた VS Code ←今ココ
5.VS Code はあれもこれもエクステンション!!


VS Code のオススメな理由
・エディタなので、当然ながら Python との相性は抜群
・ターミナルがついてる
・リモートSSH/コンテナがかなり便利
 - パスワードでも、鍵認証でも
 - ssh先のファイルを VSCodeエディタで開けるだとっっ!!!
 - コンテナ内のエクスプローラも楽ちん
・gitを GUI でも CLI でも
・Azure 機能との連携も豊富

VS Code をインストールしよう

ここからダウンロード
azure.microsoft.com

MS製ですが、もちろん MAC版も用意されています
f:id:konchangakita:20210605153937p:plain

インストールしたら、VS Code 画面配置でよく使うところをみてみましょう
f:id:konchangakita:20210605153429p:plain

VS Code の設定は初期のままでも結構いける

昔のエディタは、デフォルトのままでは使い物にならなくて、初期設定から結構いじる必要がありましたが
(主に文字サイズとかショートカットとか)

ちなみに固有のセッティングはここから変更できますが、ほとんどさわったことないです
f:id:konchangakita:20210605154501p:plain


設定やプラグインなど導入した場合に、自宅用のデスクトップとノートPC、WindowsMAC 間で設定を同期することもできます
プラグイン設定も同期できるのは、超便利
f:id:konchangakita:20210605155211p:plain


Githubアカウント経由で同期される
この辺はさすが MSさん
f:id:konchangakita:20210605155216p:plain

ワークスペースを作る

ワークスペースはおおざっぱにいうと、作業フォルダのショートカット集をつくるみたいな感じです
VS Codeエクスプローラをより便利に使いこなす為には、まずはじめにワークスペースを作っておくべき

プロジェクトごとや、やりたいこと別にワークスペースごとに作ってくようなイメージです

f:id:konchangakita:20210605164934p:plain

ターミナルを開く

「Ctrl + @」でターミナルをトグルできます
また、VS Code超便利な機能(パート2)として、指定のディレクトリでターミナルを開くってのがあります
プログラミングしていて、このファイルを実行したいって時に
ターミナル開いて、ディレクトリ移動して、実行ってメンドウな経験したことないでしょうか
開きたいファイルやディレクトリを右クリックして、「Open in integrated Terminal」

f:id:konchangakita:20210605165845p:plain


よく使うショートカットコマンド

Ctrl+@:ターミナルをトグル
Ctrl+Shift+p:コマンドパレットを開く

拡張機能をいれよう

VS Code拡張機能(エクステンション)を用途ごとに導入することで化けます
f:id:konchangakita:20210605170042p:plain

特に汎用性の高いリモート系のものとか紹介したいのですが、
プラグインは長くなりそうなので、次回へ続きます

さいごに

個人的にエディタとしては、秀丸サクラエディタ、vi、HTML開発用のエディタ(名前わすれた)とか使ってきましたが、初期設定のまま使えて Terminal との連携とかまさにさいきょう

【おてがる開発環境をつくろう】Jupyter Labで Python してみる

機械学習ではド定番というか、大前提?な Jupyter notebook の環境をDocker コンテナ上に Python と一緒に作ってしまいましょう

f:id:konchangakita:20210529152941p:plain:w300

【おてがる開発環境をつくろう】
1.まずは Docker Desktop インストール
2.Docker であそぶ Python 入りのコンテナつくる
3.コンテナで Jupyter Lab 環境 ←今ココ
4.さいきょうのえでぃた VS Code

Jupyter Lab の使いみち

jupyter.org
AI/MLはド定番というか、必須な代物ですが、それ以外の Python コーディングでも有用だと思います

プログラム全体を実行しないで、セルといわれる任意の行単位ごとに実行して即結果を出力できます
プログラム全体を実行しなくてよいので、Python初心者がコードの出力結果を確認できるのが便利、それでいて変数の結果などを使い回せる
 ・REST APIや、DBへのデータ出し入れなど
 ・ちょっとした変更をすぐその場で確認!
 ・最終的には、.py の実行ファイルを作る


Jupyter Lab 入りのコンテナを作る(Python 3.9.4)

Jupyter Lab は Python モジュールとして提供されていて、pip install jupyterlab でインストールできます
jupyterlabモジュール込みのコンテナを作ってみましょう

# Python 3.9.4-slimをベースに
FROM python:3.9.4-slim

# おまじない
RUN apt update -y
RUN pip install -U pip

# Jupyterモジュール
RUN pip install jupyterlab

WORKDIR /home

# デフォルトの jupyterlab 実行コマンド
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--allow-root", "--LabApp.token=''"]
コンテナイメージ作成とコンテナの起動
  • v で指定している ホスト側のマウントポイントは任意に変更しましょう
$ docker build . -t python:3.9.4-jupyter
$ docker run --rm -it -p 1234:8888 -v "D:\work\sample\app:/home/app"   --name python-jupyter python:3.9.4-jupyter



Jupyter Lab にアクセス

コンテナ内で起動している Jupyter Lab へブラウザで接続します
ブラウザからlocalhost:1234」
マウントしたディレクトリを選んで、Notebook を開く!
f:id:konchangakita:20210529154521p:plain

notebook でコマンド結果を確認するだけでなく、いろいろな機能があるようですが、まだそこには手を付けれていない。。。

python 実行してみる

こんな感じで各セルごとに区切って、Pythonコードを実行していくことができます
こんなイメージ
f:id:konchangakita:20210529221728p:plain

通常の Python のコーディングそのままに書いていけば良いです
もちろん、関数やクラスも好きに使えます


さいごに

Python の新しいモジュール導入した時や、一部部分の実行結果を確認するのにすごく重宝しています
REST API の実行とか Web Scraping とか Elasticsearch の連携とか相性がよいので、楽しく開発ができるようになるんじゃないかなぁ

【おてがる開発環境をつくろう】Docker であそぶ Python 入りのコンテナつくる

f:id:konchangakita:20210517222702p:plain:w200   f:id:konchangakita:20210517222849p:plain:w180


【おてがる開発環境をつくろう】
1.まずは Docker Desktop インストール
2.Docker であそぶ Python 入りのコンテナつくる ←今ココ
3.コンテナで Jupyter Lab 環境
4.さいきょうのえでぃた VS Code


あくまで今回の目的は、マイクロサービスを作るとかではなく
「開発環境をつくる!」です
Dockerコンテナがなんなのか、とか、アーキテクチャみたいなことは
他のサイトにおまかせして、あくまで使い方に終始していきます

コンテナをダウンロード

まずは、Dockerhub のアカウントを用意しておいて
https://hub.docker.com/

Docker ログインする

$ docker login


Dockerhub上のコンテナを、Powershell などで docker pull コマンドでダウンロードする

$ docker pull <container名>:<tag>

最近はだいたいアプリは公式が Dockerhub上にコンテナを用意していたりするので、Dockerhub をググるクセをつけておくと何かと便利、「dockerhub “ほしいもの”」でググると良いでしょう
例)docker ubuntu, docker splunkなどなど

Python であれば、Python 各バージョンのコンテナが公式で用意されています

自作コンテナを作ろう

dockerfile 作成

用途によってベースのコンテナを決めて、モジュールを加えてカスタマイズしていきます

例えば Python なら Dockerhub に用意されている tag ごとにインストールされているモジュールが違うので、ベースのコンテナを決めて追加の Pythonモジュールやパッケージインストール、設定ファイルなどをコピーしたりしてをコンテナを作っていきます
その設計図が dockerfile です
dockerfile という名前(拡張子なし)のファイルになります

dockerfile のサンプル(Dateコマンドが出力されるだけ)

# From: ベースになるコンテナを指定、tagなしだとlatestが自動的に選択される(ちゃんと指定しよう)
FROM python:3.9.4-slim

# WORKDIR ディレクトリ作って、そこに移動して以降のコマンド実行
WORKDIR /oreore

# RUN:の後にOS上で実行するコマンド
RUN apt update -y

# CMD: コンテナ起動時に実行されるコマンド
CMD ["date"]


コンテナイメージのビルド

dockerfile と同じ場所にて
 docker build . -t <container名>
を実行して dockerfile を元にコンテナイメージを作ります

(例)

$ docker build . –t test_container


コンテナイメージの確認

$ docker images
REPOSITORY                     TAG       IMAGE ID       CREATED          SIZE
test_container                 latest    80afafe350ea   55 seconds ago   132MB



コンテナの起動

コンテナを起動は
docker run
(場所はどこでもよい)

上で作った test_container を実行してみます
date コマンド出力結果だけが表示されます

$ docker run test_container
....
Sat May 22 13:16:33 UTC 2021
よく使うオプション

 --rm:コンテナ終了とともにコンテナを消す
 -it:コンテナの入出力に接続
 -p:外部からアクセスされるポート番号:コンテナ側のポート番号を指定
 -v:ディレクトリのマウント設定、ローカルのディレクトリ:マウントポイント
 -name :コンテナ名を指定

コンテナの状況を確認

docker ps コマンドで

起動中のコンテナ確認

$ docker ps

起動していないコンテナも含めて確認

$ docker ps -a
CONTAINER ID   IMAGE            COMMAND   CREATED             STATUS                         PORTS     NAMES
361bf7a7ffb9   test_container   "date"    About an hour ago   Exited (0) About an hour ago             ecstatic_solomon


検証するときや、開発環境用には、 “--rm” オプションをつけよう
作り始めはバンバン修正しながら作っていくのでコンテナ終了してもゴミが残らないように
コンテナ実行

$ docker run --rm <コンテナイメージ名>

コンテナ実行と同時にコンテナに入る(bashがある場合)
画面表示のオプション-it と、実行シェル /bin/bash を指定

$ docker run --rm –it <コンテナイメージ名> /bin/bash



ローカルの Pythonファイルを編集

おてがる開発環境を作る上での目玉の手法です
Docker のホストマウントを使います

Pythonファイルはローカルのエディタで編集しつつ
実行するのは、コンテナの中で実行
こうすることで、開発環境を作っては、FTPやSCPでファイルを送ったりSSHして vi みたいな手間が全部無くなります

コンテナからローカルフォルダをマウント方法
オプション -v "ローカルフォルダ : コンテナ内のマウントポイント"
(例)

$ docker run --rm -it  -v “C:\work\sample\app\:/home/app" --name python394 test_container /bin/sh

これで準備完了
あとは、
 ・ローカルのエディタで python ファイルを編集
 ・Docker コンテナは好きなバージョンの Python 環境を用意して、実行して確認
開発環境をいつでも潰して、作りなおしたり、持ち運びができます

コンテナイメージの更新

コンテナでは基本的に起動後はコンテナ内のファイルは固定(変更しない)、いつ起動しても同じ環境を再現できるイミュータブル性を確保します
なので、コンテナにパッケージやモジュールを追加したい場合は、dockerfile を修正してコンテナを再作成しよう


おまけ

本番環境でコンテナを作るのときのセキュリティについて

#ベースコンテナのtag(バージョン)はちゃんと指定することFROM python:x.x.x
# 設定ファイルの書き込み権限をなくすRUN chmod a-w /etc
# シェルを実行させないRUN rm –rf /bin/*
# ユーザ指定する(デフォルトは root になる)USER appuser

【おてがる開発環境をつくろう】まずは Docker Desktop インストール

プログラミング言語いれたら、得てしてアレもコレも追加モジュールいれてなんだか PC自体おかしくなって、全部消してやりなおした経験はでしょうか?
開発環境のお作法なんてのは、本職で毎日さわってないと、すぐ忘れる。。。
忘れたってええじゃない、開発環境をつくること自体が目的になってはいけません
新品のPCでもソッコーで以前までと同じ開発環境をつくれるような、「おてがる」な開発環境を作る


きっかけは AI の勉強をしていく上での勉強用に class="st" Python 環境を作って触ってきました
Python 環境もいろいろ実現方法があり

WindowsPython をそのままインストール
Windows に Anaconda をインストールして、仮想環境上に Python
Linux系 OSで Python インストール
・pyenv を使う
・Docker で python 入りコンテナを使う

すでに 5種類。。。。色々試していくうちに、どれが正しいのかわからないまま、ずいぶんぐちゃぐちゃのボロボロになりました
結果、たどり着いた開発環境を自分の備忘録も含めまとめていきます

こんな感じでまとめていこうかと
【おてがる開発環境をつくろう】
1.まずは Docker Desktop インストール←今ココ
2.Docker であそぶ Python 入りのコンテナつくる
3.コンテナで Jupyter Lab 環境
4.さいきょうのえでぃた VS Code

まずは今となっては、開発者の方にとっては常識になってるかと思う Docker から
f:id:konchangakita:20210516222119p:plain

なんでコンテナ使うのか

壊れてもすぐ作り治せる
別のPCでも共通環境をソッコーで準備
自分のPCを汚さない、開発言語はちょっとしたバージョン変更、ライブラリ依存が大変

WSL 2 をセットアップ

Windows10 で標準に入っている Hyper-Vだけでも、DockerDesktop はインストールできますが、WSL で Ubuntu を用意しておくと何かと便利なので、一緒に準備しておく

https://docs.microsoft.com/ja-jp/windows/wsl/install-win10
※バージョン 1903 以降、ビルド 18362 以上
このバージョンアップに結構時間がかかった

Hyper-Vを有効にする

f:id:konchangakita:20210516213448p:plain

Powershell

> dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
> dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

これが実行出来ない場合は、ビルドを確認
> wsl --set-default-version 2

UbuntuWindows Terminal を準備

Microsoft Storeでインストール
 - Windows Terminal
 - Ubuntu
 ※MSのアカウントが必要
f:id:konchangakita:20210516215000p:plain


Windows Terminal で WSL の Ubuntu が実行できるようになると、Windows上でも Linuxコマンドが実行できるようになるので何かと便利
f:id:konchangakita:20210516215029p:plain

Docker Desktop のインストール

ここでようやく Docker Desktop をインストール
ダウンロードして、インストール(5分くらい)
https://www.docker.com/products/docker-desktop

インストール後、一度ログアウトする

まずは dockerhub アカウントを作ろう

dockerhubサイト
https://hub.docker.com/

Windows Terminal を開いて dockerhubアカウントを config 登録しておく

$ docker login

はじめてのコンテナ起動

こんな感じでとりあえず流してみる

$ docker version
$ docker pull hello-world   # dockerhub からコンテナダウンロード
$ docker ps                 # 起動しているコンテナの状況確認
$ docker ps –a              # 起動していないコンテナも確認
$ docker rm <CONTAINER ID>  #コンテナ削除

実行例)

$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/


よくつかう Dockerコマンド

・起動中のコンテナを表示
$ docker ps
・コンテナイメージを表示
$ docker images
・コンテナ起動
$ docker run
・起動中のコンテナに入る
$ docker exec



さいごに

まずは基本中の基本の Docker を Windowsで使うための環境整備
Docker コンテナさえ扱えてしまえれば、開発環境としてはどこでも共通に扱えるようになります
Docker の準備は MAC / Linux だともっと簡単ですね

E資格対策 で丸暗記した Pythonコード

f:id:konchangakita:20210311202353p:plain

とうとうこれを堂々と使うことができます
JDLA E資格 2021#1 取得しました

喜びとともに忘れないうちに試験の振り返り第2段
Pythonコード問題は、基本的に穴埋めなので何がやりたいかを理解していれば時間さえかければ答えにたどり着くことはできるわけですが、試験時間もそんなに余裕があるものではないので公式の丸暗記と一緒で、時間短縮には結構大切かも

python コード

順伝播と誤差逆伝播

基本中の基本、コードを書いてると自然と覚えてるもんですが(デバッグもできるし)、いざ4択になると転置とかいるっけ?とか混乱するかも

アフィン変換 順伝播
f:id:konchangakita:20210227140515p:plain

y = np.dot(x, w) + b

アフィン変換 誤差逆伝播
f:id:konchangakita:20210227141607p:plain

dx = np.dot(dout, w.T)
dw = np.dot(x.T, dout)
db = np.sum(dout, axis=0)
活性化関数の勾配

シグモイドの勾配

dout = dout * (1.0 - out) * out 

tanh(ハイパボリックタンジェント

dout = (1.0 - y**2)

ReLUの勾配
順伝播で「0」にした部分を覚えておいて、0にする

dout[relu_mask] = 0
損失関数(コスト関数)の勾配

平均二乗誤差の勾配

d = -2 * (t - y) # t: 正解データ、y: 予測データ

交差エントロピーの勾配

delta = 1e-8
dout = -np.mean(t * np.log(y + delta))

交差エントロピー with ソフトマックス

dout = y - t 
最適化関数

SDG
モーメント
ネストロフのモーメント
AdaGrad
RMSprop
Adam


バッチノーマライゼーション

これが結構有用で、標準偏差を求める式も一緒に頭に叩き込めます

mu = np.mean(x, axis=0)  #平均
xc = x - mu  # 今回のミニバッチの平均との差分
var = np.mean(xc**2, axis=0)  # 分散
std = np.sqrt(var + 10e-7)   # 今回のミニバッチの標準偏差
xn = xc / std  # 正規化
CNN の im2col

これは 畳み込み処理の頻出問題のようなので、覚えておいて損はない

for y in range(filter_h):
    y_max = y + stride*out_h
    for x in range(filter_w):
        x_max = x + stride*out_w
        col[:, :, y, x, :, :] = img[:,:, y:y_max:stride, x:x_max:stride]

特に重要なのはココ

col[:, :, y, x, :, :] = img[:,:, y:y_max:stride, x:x_max:stride]

必要なのは、shape がどうなっているかを意識しておけば、im2col かかわる応用問題にも対応できるようになります
N:バッチサイズ
Ch:入力チャネル
H, W:入力高さ、 横幅
out_H, out_W:出力高さ、横幅

(N, Ch, H, W) ➔ (N*out_H*out_W, C*H*W)

LSTMの重み計算

LSTM覚えておけば、GRUも簡単に連想できます
特徴は、各ゲート分の重みをいっぺんに計算しておいて、各ゲート用にスライスして取り出すところになります

A = np.dot(h_prev, wh) + np.dot(x, wx) + b  # 各ゲート用の[N,4H] まとめて計算

# slice
f = sigmoid(A[:, :H])       # 忘却ゲート [N,H]
i = sigmoid(A[:, 2*H:3*H])  # 入力ゲート [N,H]
g = np.tanh(A[:, H:2*H])    
o = sigmoid(A[:, 3*H:])     # 出力ゲート [N,H]

おわりに

これからは実践活用のAI構築をやっていこう
KPS へ導入すっぞ!

E資格受けてみまして

合格ラインとかそんなのよくわかってないままに受けてきました
毎回こういうガチな試験を受ける時は人生で最高に勉強したーとか言ってるようなきがするけど、今回もまさしく人生で一番勉強した気がする
手応えは7-8割くらい。。。。(9割以上とる覚悟で受けたので結構凹んでる)

通常は2-3週間くらいで結果らしいので、それまで仕事が手に付きません
(テスト前も手につかないとか言ってたのでどんだけ仕事してないん)


試験内容は、守秘義務やらなんやらで語ることはできませんが
どんなことを中心に覚えておいたか備忘録(また受けることになったとき用)

丸暗記した公式をつらつらと

数式っていうのは、日常生活でみかけることがないので、知らない数式がでると若干パニック(あきらめ)になりますし、公式丸暗記の効果はそのものが選択肢出てきたら時間短縮にもなりますので、何度何度も見直して記憶定着に時間をかけました
丸暗記の労力を減らすため細かい条件とかは一旦抜きで

確率分布

一見ややこしいけど、いろんなところでよく出てくるやつ
対数尤度は特に
ベルヌーイ分布
確率関数:
f:id:konchangakita:20210222210418p:plain
期待値:𝑝
𝑝 の最尤推定
f:id:konchangakita:20210222210457p:plain
負の対数尤度:
f:id:konchangakita:20210222210521p:plain

マルチヌーイ分布
負の対数尤度:
f:id:konchangakita:20210222210707p:plain

一変量正規分布の確率密度(平均 μ 、分散 𝜎^2)
f:id:konchangakita:20210223153628p:plain

ベイズの定理
f:id:konchangakita:20210223154220p:plain

情報理論

結構忘れがち
情報量:
f:id:konchangakita:20210223160244p:plain
エントロピー
f:id:konchangakita:20210223160308p:plain
交差エントロピー
f:id:konchangakita:20210223160352p:plain
カルバックライブラーダイバージェンス
f:id:konchangakita:20210223160439p:plain

活性化関数と損失関数の微分

それぞれの関数自体は必須として、微分の式も覚えておいて損なし!
シグモイド関数
f:id:konchangakita:20210223163205p:plain
tanh(ハイパボリックタンジェント):
f:id:konchangakita:20210223163320p:plain
平均二乗誤差:
f:id:konchangakita:20210223163336p:plain
交差エントロピー誤差:
f:id:konchangakita:20210223163350p:plain
ソフトマックス交差エントロピー誤差:
f:id:konchangakita:20210223163412p:plain

GANの最適化

f:id:konchangakita:20210223171316p:plain

強化学習

ベルマン方程式
f:id:konchangakita:20210223171430p:plain
f:id:konchangakita:20210223171500p:plain

Sarsa 更新式
f:id:konchangakita:20210223171553p:plain
Q関数 更新式
f:id:konchangakita:20210223171625p:plain

勾配方策定理の勾配
f:id:konchangakita:20210223171716p:plain

丸暗記の策

覚えなければいけないことはたくさんあるわけですが、この数式というのは中途半端に覚えるとかえって混乱しがちなので丸暗記が得策
Python コードは書くしかない(写経)

【DeepLearning特訓】GAN 敵対的生成ネットワーク

E資格向けの自習アウトプット
自分用メモ

GAN 敵対的生成ネットワークは、2014年にイアン・グッドフェロー氏らが「Generative Adversarial Network」という論文で発表
生成モデルと識別モデルの組み合わせ、敵対させ競い合わせることで精度を上げていく手法

 ・生成(Generator)モデル:見破られないようなニセモノを作る
 ・識別(Discriminator)モデル:訓練データを使ってニセモノを見破ろうとする

「偽造犯と警察」とか「怪盗と探偵」とか「ルパンと銭形」とかそんなライバル同士が切磋琢磨していって、結果として騙す側(トリック)巧妙になっていくような関係

GAN のアーキテクチャ

f:id:konchangakita:20210207211937p:plain
1.ノイズを乱数からサンプリングする
2.Generator で Fakeデータを生成
3.Realデータと Fakeデータを Discriminator に識別させる
4.学習方針
  - Generator(生成モデル)は、識別判定のロスが大きくなるように ➔うまく騙せた
  - Discriminator(識別モデル)は、識別判定のロスが小さくなるように ➔みやぶった

GAN の目的関数(損失関数)

いつものごとく導出の過程の理解は一旦おいておいて

以下をセットで覚えておく
Generator ネットワーク 𝐺:𝑧→𝑥'
Discriminator ネットワーク 𝐷:𝑥→(0,1)
f:id:konchangakita:20210211163038p:plain

Discriminator:Realデータ真(1)、Fakeデータ偽(0)の時の、Discriminatorの予測に対する交差エントロピー
見破れてるのか
Generator:Realデータ真(1)、Fakeデータ真(1)の時の、Discriminatorの予測に対する交差エントロピー
うまく騙せてるのか

このあたりは、考えると沼っていくので、実装しながら理解することにする

GAN の種類

DCGAN(Deep Convolutinal GAN):CNNを使う
LAPGAN(Laplacian Pyramid):低解像度と高解像度の画像の差を比較
Conditional GAN:訓練時に教師データのラベル情報を用いて、生成するクラスを指定できる
StarGAN:マルチドメインに適用できるように拡張

この他にもたくさん研究されているらしい

TensorFlow で実装しながら頭を整理

TensorFlow公式に DCGAN のチュートリアルがあったので、こちらで参考に実装してみる
www.tensorflow.org

今回はこのあたりを使う

import tensorflow as tf
from tensorflow.keras import layers 
from tensorflow.keras.datasets import mnist, fashion_mnist
import matplotlib.pyplot as plt
データセット

データセットの準備、今回は Fashion MNIST を使ってみる

# データのロード
(x_train, t_train), (x_test, t_test) = fashion_mnist.load_data()

# 設定
BUFFER_SIZE = x_train.shape[0]  # 60000
BATCH_SIZE = 256

x_train = x_train.reshape(BUFFER_SIZE, 28, 28, 1).astype('float32')
x_train = (x_train - 127.5) / 127.5     # Normalize [-1, 1]
train_dataset = tf.data.Dataset.from_tensor_slices(x_train).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)


どんな画像か確認しておく

fig = plt.figure(figsize=(4,4))

for i in range(16):
    plt.subplot(4, 4, i+1)
    plt.imshow(x_train[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
    plt.axis('off')

plt.show()

f:id:konchangakita:20210207233715p:plain

Generator ネットワーク

ノイズを受け取って、CNN の逆畳み込みで画像を作っていく

# Generator
# noise から画像を作る
def make_generator_model():
    model = tf.keras.Sequential()
    model.add(layers.Dense(7*7*256, use_bias=False, input_shape=(100,)))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
    
    model.add(layers.Reshape((7,7,256)))
    assert model.output_shape == (None, 7,7,256)    # Noneはバッチサイズ

    model.add(layers.Conv2DTranspose(128, (5,5), strides=1, padding='same', use_bias=False))
    assert model.output_shape == (None, 7,7,128)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(64, (5,5), strides=2, padding='same', use_bias=False))
    assert model.output_shape == (None, 14,14,64)
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())

    model.add(layers.Conv2DTranspose(1, (5,5), strides=2, padding='same', use_bias=False))
    assert model.output_shape == (None, 28,28,1)

    return model


None はバッチサイズがはいる

generator = make_generator_model()
generator.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_2 (Dense)              (None, 12544)             1254400   
_________________________________________________________________
batch_normalization_3 (Batch (None, 12544)             50176     
_________________________________________________________________
leaky_re_lu_5 (LeakyReLU)    (None, 12544)             0         
_________________________________________________________________
reshape_1 (Reshape)          (None, 7, 7, 256)         0         
_________________________________________________________________
conv2d_transpose_3 (Conv2DTr (None, 7, 7, 128)         819200    
_________________________________________________________________
batch_normalization_4 (Batch (None, 7, 7, 128)         512       
_________________________________________________________________
leaky_re_lu_6 (LeakyReLU)    (None, 7, 7, 128)         0         
_________________________________________________________________
conv2d_transpose_4 (Conv2DTr (None, 14, 14, 64)        204800    
_________________________________________________________________
batch_normalization_5 (Batch (None, 14, 14, 64)        256       
_________________________________________________________________
leaky_re_lu_7 (LeakyReLU)    (None, 14, 14, 64)        0         
_________________________________________________________________
conv2d_transpose_5 (Conv2DTr (None, 28, 28, 1)         1600      
=================================================================
Total params: 2,330,944
Trainable params: 2,305,472
Non-trainable params: 25,472
_________________________________________________________________


試し画像を一枚だけ作ってみる

noise = tf.random.normal([1, 100])
generated_image = generator(noise, training=False)
plt.imshow(generated_image[0, :, :, 0], cmap='gray')

学習してないので、当然わけわからん Fake 画像が表示される
f:id:konchangakita:20210207235519p:plain

Discriminatorネットワーク

CNN で畳み込んで特徴量を抽出していく

# Discriminator
# 真の場合には正の数値を、偽の場合は負の数値を返す
def make_discriminator_model():
    model = tf.keras.Sequential()
    model.add(layers.Conv2D(64, (5,5), strides=2, padding='same', input_shape=[28,28,1]))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))

    model.add(layers.Conv2D(128, (5,5), strides=2, padding='same'))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))

    model.add(layers.Flatten())
    model.add(layers.Dense(1))

    return model
discriminator = make_discriminator_model()
discriminator.summary()
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_2 (Conv2D)            (None, 14, 14, 64)        1664      
_________________________________________________________________
leaky_re_lu_8 (LeakyReLU)    (None, 14, 14, 64)        0         
_________________________________________________________________
dropout_2 (Dropout)          (None, 14, 14, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 7, 7, 128)         204928    
_________________________________________________________________
leaky_re_lu_9 (LeakyReLU)    (None, 7, 7, 128)         0         
_________________________________________________________________
dropout_3 (Dropout)          (None, 7, 7, 128)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 6272)              0         
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 6273      
=================================================================
Total params: 212,865
Trainable params: 212,865
Non-trainable params: 0
_________________________________________________________________


適当な Fake 画像の適当な特徴量

decision = discriminator(generated_image)
decision
<tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[1.2810372]], dtype=float32)>


損失関数とオプティマイザー

損失関数はクロスエントロピーオプティマイザーにはAdam

【損失関数の考え方】
Generatorでは、fake_loss を Fakeデータを Real と判定できたか(ちゃんと騙せたか)
Discriminatorでは、Realロス と Fakeロス の和
 - real_loss は 訓練データを Real 判定できたか
 - fake_loss は Fakeデータを Fake 判定できたか(見破ったか)

# 損失関数とオプティマイザー
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

def discriminator_loss(real_output, fake_output):
    real_loss = cross_entropy(tf.ones_like(real_output), real_output)
    fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
    total_loss = real_loss + fake_loss
    return total_loss

def generator_loss(fake_output):
    return cross_entropy(tf.ones_like(fake_output), fake_output)

generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)

レーニングループ

def train_step(images):
    noise = tf.random.normal([BATCH_SIZE, noise_dim])

    with tf.GradientTape() as gen_tape, tf.GradientTape() as dis_tape:
        generated_images = generator(noise, training=True)
        real_output = discriminator(images, training=True)
        fake_output = discriminator(generated_images, training=True)

        dis_loss = discriminator_loss(real_output, fake_output)
        gen_loss = generator_loss(fake_output)

    # 勾配の保存
    gradients_of_discriminator = dis_tape.gradient(dis_loss, discriminator.trainable_variables)
    gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)

    # パラメータ更新
    discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
    generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))

# エポック数分のループ
def train(dataset, epochs):
    for epoch in range(epochs):
        for image_batch in dataset:
            gen_loss, dis_loss = train_step(image_batch)

        print('EPOCH:{}, {}, {},'.format(epoch, gen_loss, dis_loss))

        generate_and_save_images(generator, epoch+1, seed)

        # Save the model every 15 epochs
        if (epoch + 1) % 15 == 0:
            checkpoint.save(file_prefix = checkpoint_dir)

# Fake画像(生成画像)の表示と保存
def generate_and_save_images(model, epoch, test_input):
    predictions = model(test_input, training=False)
    fig = plt.figure(figsize=(4,4))

    for i in range(predictions.shape[0]):
        plt.subplot(4, 4, i+1)
        plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
        plt.axis('off')

    plt.savefig('output/dcgan_image_at_epoch_{:04d}.png'.format(epoch))
    plt.show()
# トレーニング設定
EPOCHS = 50
noise_dim = 100
num_examples_to_generate = 16

seed = tf.random.normal([num_examples_to_generate, noise_dim])

レーニング実行

train(train_dataset, EPOCHS)

エポック 10、エポック 50、エポック 100 のそれぞれの Fake画像
f:id:konchangakita:20210208004241p:plain:w180f:id:konchangakita:20210208004308p:plain:w180f:id:konchangakita:20210208004328p:plain:w180

それっぽいものが出来てきています
構造としてはシンプルなのに、オリジナルの画像が作られてくるのすごい

さいごに

構造をざっくり理解するために書いてきましたが、つまるところ Generate された分布と入力されたデータの分布を近づけていくということが重要なようです
(訓練データそのものを作るわけではない)
生成データは実用的実におもしろい、試験が終わったら実装してみたい
これからは強化学習