【おてがる開発環境をつくろう】まずは Docker Desktop インストール
プログラミング言語いれたら、得てしてアレもコレも追加モジュールいれてなんだか PC自体おかしくなって、全部消してやりなおした経験はでしょうか?
開発環境のお作法なんてのは、本職で毎日さわってないと、すぐ忘れる。。。
忘れたってええじゃない、開発環境をつくること自体が目的になってはいけません
新品のPCでもソッコーで以前までと同じ開発環境をつくれるような、「おてがる」な開発環境を作る
きっかけは AI の勉強をしていく上での勉強用に class="st" Python 環境を作って触ってきました
Python 環境もいろいろ実現方法があり
・Windows に Python をそのままインストール
・Windows に Anaconda をインストールして、仮想環境上に Python
・Linux系 OSで Python インストール
・pyenv を使う
・Docker で python 入りコンテナを使う
すでに 5種類。。。。色々試していくうちに、どれが正しいのかわからないまま、ずいぶんぐちゃぐちゃのボロボロになりました
結果、たどり着いた開発環境を自分の備忘録も含めまとめていきます
こんな感じでまとめていこうかと
【おてがる開発環境をつくろう】
1.まずは Docker Desktop インストール←今ココ
2.Docker であそぶ Python 入りのコンテナつくる
3.コンテナで Jupyter Lab 環境
4.さいきょうのえでぃた VS Code
まずは今となっては、開発者の方にとっては常識になってるかと思う Docker から
なんでコンテナ使うのか
壊れてもすぐ作り治せる
別のPCでも共通環境をソッコーで準備
自分のPCを汚さない、開発言語はちょっとしたバージョン変更、ライブラリ依存が大変
WSL 2 をセットアップ
Windows10 で標準に入っている Hyper-Vだけでも、DockerDesktop はインストールできますが、WSL で Ubuntu を用意しておくと何かと便利なので、一緒に準備しておく
https://docs.microsoft.com/ja-jp/windows/wsl/install-win10
※バージョン 1903 以降、ビルド 18362 以上
このバージョンアップに結構時間がかかった
Hyper-Vを有効にする
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
Ubuntu と Windows Terminal を準備
Microsoft Storeでインストール
- Windows Terminal
- Ubuntu
※MSのアカウントが必要
Windows Terminal で WSL の Ubuntu が実行できるようになると、Windows上でも Linuxコマンドが実行できるようになるので何かと便利
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
E資格対策 で丸暗記した Pythonコード
とうとうこれを堂々と使うことができます
JDLA E資格 2021#1 取得しました
喜びとともに忘れないうちに試験の振り返り第2段
Pythonコード問題は、基本的に穴埋めなので何がやりたいかを理解していれば時間さえかければ答えにたどり着くことはできるわけですが、試験時間もそんなに余裕があるものではないので公式の丸暗記と一緒で、時間短縮には結構大切かも
python コード
順伝播と誤差逆伝播
基本中の基本、コードを書いてると自然と覚えてるもんですが(デバッグもできるし)、いざ4択になると転置とかいるっけ?とか混乱するかも
アフィン変換 順伝播
y = np.dot(x, w) + b
アフィン変換 誤差逆伝播
dx = np.dot(dout, w.T)
dw = np.dot(x.T, dout)
db = np.sum(dout, axis=0)
活性化関数の勾配
シグモイドの勾配
dout = dout * (1.0 - out) * out
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週間くらいで結果らしいので、それまで仕事が手に付きません
(テスト前も手につかないとか言ってたのでどんだけ仕事してないん)
試験内容は、守秘義務やらなんやらで語ることはできませんが
どんなことを中心に覚えておいたか備忘録(また受けることになったとき用)
丸暗記した公式をつらつらと
数式っていうのは、日常生活でみかけることがないので、知らない数式がでると若干パニック(あきらめ)になりますし、公式丸暗記の効果はそのものが選択肢出てきたら時間短縮にもなりますので、何度何度も見直して記憶定着に時間をかけました
丸暗記の労力を減らすため細かい条件とかは一旦抜きで
確率分布
一見ややこしいけど、いろんなところでよく出てくるやつ
対数尤度は特に
ベルヌーイ分布
確率関数:
期待値:𝑝
𝑝 の最尤推定:
負の対数尤度:
マルチヌーイ分布
負の対数尤度:
一変量正規分布の確率密度(平均 μ 、分散 𝜎^2)
ベイズの定理
活性化関数と損失関数の微分
それぞれの関数自体は必須として、微分の式も覚えておいて損なし!
シグモイド関数:
tanh(ハイパボリックタンジェント):
平均二乗誤差:
交差エントロピー誤差:
ソフトマックス交差エントロピー誤差:
GANの最適化
丸暗記の策
覚えなければいけないことはたくさんあるわけですが、この数式というのは中途半端に覚えるとかえって混乱しがちなので丸暗記が得策
Python コードは書くしかない(写経)
【DeepLearning特訓】GAN 敵対的生成ネットワーク
E資格向けの自習アウトプット
自分用メモ
GAN 敵対的生成ネットワークは、2014年にイアン・グッドフェロー氏らが「Generative Adversarial Network」という論文で発表
生成モデルと識別モデルの組み合わせ、敵対させ競い合わせることで精度を上げていく手法
・生成(Generator)モデル:見破られないようなニセモノを作る
・識別(Discriminator)モデル:訓練データを使ってニセモノを見破ろうとする
「偽造犯と警察」とか「怪盗と探偵」とか「ルパンと銭形」とかそんなライバル同士が切磋琢磨していって、結果として騙す側(トリック)巧妙になっていくような関係
GAN のアーキテクチャ
1.ノイズを乱数からサンプリングする
2.Generator で Fakeデータを生成
3.Realデータと Fakeデータを Discriminator に識別させる
4.学習方針
- Generator(生成モデル)は、識別判定のロスが大きくなるように ➔うまく騙せた
- Discriminator(識別モデル)は、識別判定のロスが小さくなるように ➔みやぶった
GAN の目的関数(損失関数)
いつものごとく導出の過程の理解は一旦おいておいて
以下をセットで覚えておく
Generator ネットワーク 𝐺:𝑧→𝑥'
Discriminator ネットワーク 𝐷:𝑥→(0,1)
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()
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 画像が表示される
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)>
損失関数とオプティマイザー
【損失関数の考え方】
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画像
それっぽいものが出来てきています
構造としてはシンプルなのに、オリジナルの画像が作られてくるのすごい
さいごに
構造をざっくり理解するために書いてきましたが、つまるところ Generate された分布と入力されたデータの分布を近づけていくということが重要なようです
(訓練データそのものを作るわけではない)
生成データは実用的実におもしろい、試験が終わったら実装してみたい
これからは強化学習
【DeepLearning特訓】VAE 変分自己符号化器
E資格向けの自習アウトプット
自分用メモ
VAE(変分自己符号化器)は、生成モデルに対するアプローチの一つ
生成モデルとは、「訓練データを生成する確率 p(x) を求めたい」という分野で、訓練データを元にして特徴を捉えて訓練デーアセットに似たデータを生成することができるようになる
あるアニメのキャラを訓練データとして与えたら、そのアニメに出てきそうなキャラを作ってくれるみたいなイメージかな、かな?
オートエンコーダ
教師なし学習のひとつ。なので学習には訓練用データだけで用意すればよい
入力データを Encoder 低次元に圧縮して、Decoder で復元して生成データを作る 潜在変数 𝑧(特徴量)を近似推論する
( seq2seq でも見かけた Encoder-Decoder手法 RNNでは系列データの隠れ層の状態を渡していった)
一般的なオートエンコーダの例
VAE: Variational Auto Encoder のアーキテクチャ
VAE では潜在変数 𝑧 にガウス分布(標準正規分布)𝑧 ~ 𝑁(0,1) を仮定する
潜在変数 𝑧 を確率分布に押し込める
VAEの順伝播
Encoder で平均 μ と分散 σ を出力、正規分布パラメータ(μ,σ)で潜在変数 𝑧 をランダムサンプリングする
Encoder:CNNを使うことが多い
潜在変数: 𝑧=𝜇+𝜎∗𝜀 (𝜀 は平均0、分散1正規分布の乱数)
Reparameterization Trick という手法で、モデルはepsilon(𝜀)を通じることで確率を維持しながら、エンコーダーの勾配をそれぞれ 平均 μ と分散 σ を介して逆伝播できる 。
Decoder:潜在変数 𝑧 から 生成データを出力、CNNで戻す
VAE の損失関数
これが非常にむつかしい。。。。
・入力データと生成データの負の対数尤度(クロスエントロピー)
・潜在変数 𝑧 の分布と符号化された潜在変数 𝑧 の分布のカルバックダイバージェンス
難しくてちょっと何言ってるわからんけど、この2つの和が総損失ということらしい
更に、確率分布をもとめるのは変分下限(わからん)の最大化に帰着するという
更に更に、変分下限の最大化はKLダイバージェンスの最小化と等しい(らしい)
対数尤度とかカルバックダイバージェンスとかから導出の式は、ちょっとややこしいので、ひとまずそこから導きだされるこの式だけは覚えておくことにする
第一項:元データと生成データの一致度、生成の精度、大きくしたい
第二項:潜在変数 𝑧 の分布(平均 0, 分散 1)と符号化された潜在変数 𝑧 の分布(平均 μ , 分散 σ )の差異、正則化項、小さくしたい
TensorFlow で実装しながら頭を整理
TensorFlow公式にチュートリアルがあるので、これに沿いながら
Convolutional Variational Autoencoder | TensorFlow Core
モジュールインポート
import numpy as np import tensorflow as tf from tensorflow.keras.datasets import mnist import matplotlib.pyplot as plt
データセットロード
いつもの MNIST を使う
train_size = 60000 batch_size = 32 test_size = 10000 (x_train, t_train), (x_test, t_test) = mnist.load_data() x_train = x_train.astype('float32') / 255. #各ピクセルの値を[0,1]に制限 x_train = x_train.reshape(x_train.shape + (1,)) #(データ数, 28, 28)→(データ数, 28, 28, 1)へ変換(チャネル数を追加) x_train = np.where(x_train > .5, 1.0, 0.0).astype('float32') x_test = x_test.astype('float32') / 255. #各ピクセルの値を[0,1]に制限 x_test = x_test.reshape(x_test.shape + (1,)) x_test = np.where(x_test > .5, 1.0, 0.0).astype('float32') train_dataset = tf.data.Dataset.from_tensor_slices(x_train).shuffle(train_size).batch(batch_size) test_dataset = tf.data.Dataset.from_tensor_slices(x_test).shuffle(train_size).batch(batch_size)
ネットワーク部分の定義クラス
Encoderネットワーク:畳み込みを2層分+1層
Decoderネットワーク:Encoderと同様に
class CVAE(tf.keras.Model): def __init__(self, latent_dim): super(CVAE, self).__init__() self.latent_dim = latent_dim self.encoder = tf.keras.Sequential([ tf.keras.layers.InputLayer(input_shape=(28,28,1)), tf.keras.layers.Conv2D(filters=32, kernel_size=3, strides=(2,2), activation='relu'), # [13,13,32] tf.keras.layers.Conv2D(64, 3, strides=(2,2), activation=tf.nn.relu), # [6,6,64] tf.keras.layers.Flatten(), # [2304] tf.keras.layers.Dense(latent_dim + latent_dim) # [4] ]) self.decoder = tf.keras.Sequential([ tf.keras.layers.InputLayer(input_shape=(latent_dim)), tf.keras.layers.Dense(units=7*7*32, activation=tf.nn.relu), tf.keras.layers.Reshape(target_shape=(7,7,32)), tf.keras.layers.Conv2DTranspose(64, 3, strides=2, padding='same', activation='relu'), tf.keras.layers.Conv2DTranspose(32, 3, strides=2, padding='same', activation='relu'), tf.keras.layers.Conv2DTranspose(1, 3, strides=1, padding='same') ]) # 学習後のデータ生成用 @tf.function def sample(self, eps=None): if eps is None: eps = tf.random.normal(shape=(100, self.latent_dim)) return self.decode(eps, apply_sigmoid=True) # q(z|x) def encode(self, x): mean, logvar = tf.split(self.encoder(x), num_or_size_splits=2, axis=1) return mean, logvar # 潜在変数の算出 def reparameterize(self, mean, logvar): eps = tf.random.normal(shape=mean.shape) return eps * tf.exp(logvar * .5) + mean # p(x|z) def decode(self, z, apply_sigmoid=False): logits = self.decoder(z) if apply_sigmoid: probs = tf.sigmoid(logits) return probs return logits
定義したネットワークを確認
latent_dim = 2 # 潜在空間の次元 model = CVAE(latent_dim) model.encoder.summary(), model.decoder.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 13, 13, 32) 320 _________________________________________________________________ conv2d_1 (Conv2D) (None, 6, 6, 64) 18496 _________________________________________________________________ flatten (Flatten) (None, 2304) 0 _________________________________________________________________ dense (Dense) (None, 4) 9220 ================================================================= Total params: 28,036 Trainable params: 28,036 Non-trainable params: 0 _________________________________________________________________ Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_1 (Dense) (None, 1568) 4704 _________________________________________________________________ reshape (Reshape) (None, 7, 7, 32) 0 _________________________________________________________________ conv2d_transpose (Conv2DTran (None, 14, 14, 64) 18496 _________________________________________________________________ conv2d_transpose_1 (Conv2DTr (None, 28, 28, 32) 18464 _________________________________________________________________ conv2d_transpose_2 (Conv2DTr (None, 28, 28, 1) 289 ================================================================= Total params: 41,953 Trainable params: 41,953 Non-trainable params: 0 _________________________________________________________________ (None, None)
オプティマイズと損失関数を定義
このチュートリアル損失関数をアレンジしている模様
これをそのまま採用してみる
optimizer = tf.keras.optimizers.Adam(1e-4) def log_normal_pdf(sample, mean, logvar, raxis=1): log2pi = tf.math.log(2. * np.pi) return tf.reduce_sum(-.5 * ((sample - mean)**2. * tf.exp(-logvar) + logvar + log2pi), axis=raxis) def compute_loss(model, x): mean, logvar = model.encode(x) z = model.reparameterize(mean, logvar) x_logit = model.decode(z) cross_loss = tf.nn.sigmoid_cross_entropy_with_logits(logits=x_logit, labels=x) logpx_z = -tf.reduce_sum(cross_loss, axis=[1,2,3]) logpz = log_normal_pdf(z, 0., 0.) logqz_x = log_normal_pdf(z, mean, logvar) return -tf.reduce_mean(logpx_z + logpz - logqz_x) @tf.function def train_step(model, x, optimizer): with tf.GradientTape() as tape: loss = compute_loss(model, x) gradients = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) return loss
生成データの画像保存関数
def generate_and_save_images(model, epoch, test_sample): mean, logvar = model.encode(test_sample) z = model.reparameterize(mean, logvar) predictions = model.sample(z) fig = plt.figure(figsize=(4,4)) for i in range(predictions.shape[0]): plt.subplot(4, 4, i+1) plt.imshow(predictions[i, :, :, 0], cmap='gray') plt.axis('off') plt.savefig('output/image_at_epoch_epoch{:04d}.png'.format(epoch)) plt.show()
学習前の生成画像を一枚保存
シャッフルされたデータセットから 16枚抜き出している
num_examples_to_generate = 16 assert batch_size >= num_examples_to_generate for test_batch in test_dataset.take(1): test_sample = test_batch[0:num_examples_to_generate, :,:,:] generate_and_save_images(model, 0, test_sample)
学習前前なので、当然わけわからん画像が生成される
10 エポック分トレーニング回してみる
epochs = 10 for epoch in range(1, epochs +1): for x_train in train_dataset: train_step(model, x_train, optimizer) loss = tf.keras.metrics.Mean() for x_test in test_dataset: loss(compute_loss(model, x_test)) elbo = -loss.result() print('Epoch: {}, Test set ELBO: {}, time elapse for current epoch: '.format(epoch, elbo)) generate_and_save_images(model, epoch, test_sample)
エポック 1回目から何かそれっぽい画像が生成され始める
エポック 10回目で生成されたデータ
さいごに
こんな感じで、与えた訓練データに近い画像が生成されてきました
試験が終わったら、もっといろいろいじってみたい
とにかく損失関数の理解がむずかしく、実装にも時間がかかった。。
TensorFlow 自体もこれでちょっと慣れたかな
次は GAN
【DeepLearning特訓】RNN応用 seq2seq編
E資格向けの自習アウトプット
自分用メモ
seq2seq は Encoder-Decoderモデルとも呼ばれている
RNNを使って、系列データを固定次元ベクトルへ変換(エンコード)、逆に固定次元ベクトルから系列を生成できる(デコード)
エンコーダ:系列データをある規則に基づいて変換する(固定長ベクトルへ)
デコーダ:系列データへ変換(可変ベクトルへ)
日本語から英語への翻訳で考えてみると
seq2seq の構造
エンコーダでは、各タイムステップの出力は使わず隠れ層の出力を伝播させる、最終単語の隠れ層の出力をデコーダに渡す
区切り文字
エンコーダとデコーダの構造が違っても良い
seq2seq の問題点
エンコードの出力は固定長ベクトル
➔入力系列が長すぎるとうまくいかない
Attention Mechanism
エンコーダの各ステップの出力を利用する
「入力と出力でどの単語が関連しているか」という対応関係を seq2seq に学習させる
Encoderの改良
最後の隠れ状態だけを Decoder に渡していたのを、入力される文章のすべてステップの出力の集合を作る
「ひとつの固定長ベクトル」という制約から開放
Decoder の改良
各ステップでRNNの出力結果を用いて重要度を算出する
終わりに
seq2seq からの、Attention Mechanism は非常に強力らしい
ただ、numpy で書いていくにはちょっと面倒なので、また TensorFlow Keras などでサクッと試してみようかな
【DeepLearning特訓】RNN応用 GRU編
E資格向けの自習アウトプット
自分用メモ
GRU(Gated Recurrent Unit)は、LSTM を単純化したモデル
LSTMはパラメータが多く学習に時間がかかので、パラメータを減らして計算量を減らす工夫
ゲートを2つに減らし、内部状態をなくした
・reset ゲート (r):過去の隠れ状態をどれだけ無視するか
・update ゲート (z):過去の隠れ状態を更新する役割
GRUの構造
行列の内積を計算するところと、要素積(アダマール積)が混ざっているの要注意
Python に落とし込むと理解しやすい(順伝播まで)
コピペで試せます
# シグモイド def sigmoid(x): return 1 / (1 + np.exp(-x)) # GRUモデル # N:バッチサイズ、D:入力単語数、H:中間層の出力次元数 class GRU: def __init__(self, wx, wh, b): self.params = wx, wh, b # # wx[D,3H], wh[H,3H], b[3H] def forward(self, x, h_prev): wx, wh, b = self.params H = wh.shape[0] wxz, wxr, wxh = wx[:, :H], wx[:, H:2*H], wx[:, 2*H:] # 入力用重み whz, whr, whh = wh[:, :H], wh[:, H:2*H], wh[:, 2*H:] # 前の時刻出力用重み bz, br, bh = b[:H], b[H:2*H], b[2*H:] # バイアス z = sigmoid(np.dot(h_prev, whz) + np.dot(x, wxz) + bz) # updateゲート r = sigmoid(np.dot(h_prev, whr) + np.dot(x, wxr) + br) # resetゲート h_hat = sigmoid(np.dot(r*h_prev, whh) + np.dot(x, wxh) + bh ) h_next = (1-z) * h_prev + z * h_hat return h_next
適当な入力とパラメータで実行
import numpy as np # 入力を適当に定義 x = np.arange(25).reshape(5,5) h_prev = np.ones((5,10)) # 重みを初期化 wx = np.random.randn(5, 30) wh = np.random.randn(10, 30) b = np.zeros(30) # モデルインスタンス gru = GRU(wx, wh, b) # 順伝播 gru.forward(x, h_prev)
array([[7.96586166e-01, 9.79622537e-03, 9.99877107e-01, 9.76598775e-01, 9.98707164e-01, 3.56091373e-01, 9.99988035e-01, 2.55284290e-01, 9.89543388e-01, 9.96706424e-01], [2.47451855e-03, 5.64910120e-03, 9.99999999e-01, 9.99999983e-01, 1.00000000e+00, 4.40713164e-03, 1.00000000e+00, 9.99995693e-01, 9.82514705e-01, 1.00000000e+00], [5.47933744e-04, 3.69982598e-03, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 3.66723137e-05, 1.00000000e+00, 1.00000000e+00, 9.70604673e-01, 1.00000000e+00], [1.66534473e-04, 2.42152843e-03, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 3.04634406e-07, 1.00000000e+00, 1.00000000e+00, 9.50828177e-01, 1.00000000e+00], [5.06729678e-05, 1.58418307e-03, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 2.53141448e-09, 1.00000000e+00, 1.00000000e+00, 9.19010082e-01, 1.00000000e+00]])