Upgrade to Pro — share decks privately, control downloads, hide ads and more …

生成 AI の基礎 〜 サンプル実装で学ぶ基本原理

生成 AI の基礎 〜 サンプル実装で学ぶ基本原理

Avatar for Etsuji Nakai

Etsuji Nakai

August 09, 2026

More Decks by Etsuji Nakai

Other Decks in Technology

Transcript

  1. 目次 第1部:前提知識 第3部:自然言語モデルの基礎 ◦ Keras による分類モデルの実装 ◦ LSTM によるテキスト分類 ◦

    多層ニューラルネットワーク ◦ LSTM によるテキスト生成 ◦ 畳み込みフィルターと転置畳み込みフィ ルター 第2部:画像生成モデルの基礎 ◦ オートエンコーダと潜在空間 ◦ 変分オートエンコーダ ◦ マルチタスク学習 ◦ 条件付き変分オートエンコーダ 第4部:より高性能な生成モデル ◦ Transformer モデル ◦ DCGAN ◦ Diffusion モデル ◦ VQ-VAE ◦ マルチモーダル生成モデル
  2. サンプルコードの入手について • Google Colaboratory に Google アカウントでログインして、新しいノートブックを開きます。 ◦ • https://colab.research.google.com/

    新しく開いたノートブック上で次のコマンドを実行すると、本講義のサンプルノートブックがダウンロー ドできます。 ◦ 次のコマンドを実行すると、ユーザー認証が行われて、Colab の実行環境に該当ユーザーの Google Drive がマウントされます。 from google.colab import drive drive.mount('/content/gdrive') ◦ 次のコマンドを実行すると、サンプルノートブックが Google Drive に保存されます。 %%bash cd '/content/gdrive/My Drive/Colab Notebooks' git clone https://github.com/enakai00/colab_GenAI_lecture 4
  3. GPU ランタイムを使用する際の注意 • 各ノートブックの先頭部分に、使用するランタイムの種類(GPU を使用するかどうか)が記載されてい ます。 • Google Colaboratory を無償枠で使用している場合、GPU

    ランタイムの使用時間には制限があり、長時 間使用を続けると、一定期間(半日〜1日程度)GPU ランタイムが使用できなくなることがあります。 ◦ GPU ランタイムを使用するノートブックは、実行が終わったらすぐに、画面上部のメニューから 「ランタイム」→「ランタイムを接続解除して削除」を選択して、ランタイムを停止しておくこと をお勧めします。 5
  4. 線形多項分類器 • 各ラベルに対する「可能性の高さ」を 1 次関数で計算します。 ◦ 例えば、28 × 28 (=

    784) ピクセルの画像データであれば、1 つの画像は 784 個の実数 値からなるデータと見なせるので、784 変数の 1 次関数を使用します。 ◦ ラベルごとに異なる 1 次関数を用意して、それぞれのラベルに対する計算値を得ます。 MNIST の画像データを 10 種類に分類する場合 それぞれの画像に対して 10 個の出力値が得られる ・・・ 9
  5. 分類モデルの誤差関数 • n 番目のデータ の正解ラベルを次の One-hot 表現で与えます。(正解を k として、k 番

    目の要素 のみが 1 になっている。) • 今のモデルで正解 k に対する確率は します。 ※ One-hot 表現の正解ラベルを用いると なので、これが大きくなるようにモデルを学習 は次式で表すことができます。 11
  6. Keras による線形多項分類器の実装例 • 最適化アルゴリズム、誤差関数、性能指標を指定した後に、学習処理を実施します。 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc']) history = model.fit(train_images,

    train_labels, validation_data=(test_images, test_labels), batch_size=128, epochs=10) • テストデータの指定 トレーニングデータとは別の「テストデータ」を指定すると、性能指標の計算は、テスト データに対しても行わます。 ◦ トレーニングデータに対する性能指標だけを見ていると、トレーニングデータには高い 正解率を示すにもかかわらず、その他のデータには正解率があがらない「過学習 (オー バーフィッティング)」の発生を見落とす危険性があるので注意が必要です。 14
  7. Keras による線形多項分類器の実装例 Epoch 1/10 469/469 [==============================] - 5s 8ms/step -

    loss: 0.6776 - acc: 0.8321 val_loss: 0.3868 - val_acc: 0.9012 Epoch 2/10 469/469 [==============================] - 3s 5ms/step - loss: 0.3638 - acc: 0.9018 val_loss: 0.3226 - val_acc: 0.9120 Epoch 3/10 469/469 [==============================] - 2s 5ms/step - loss: 0.3207 - acc: 0.9117 val_loss: 0.2985 - val_acc: 0.9168 Epoch 4/10 469/469 [==============================] - 3s 6ms/step - loss: 0.3002 - acc: 0.9169 val_loss: 0.2868 - val_acc: 0.9210 Epoch 5/10 469/469 [==============================] - 2s 4ms/step - loss: 0.2882 - acc: 0.9204 val_loss: 0.2783 - val_acc: 0.9243 Epoch 6/10 469/469 [==============================] - 2s 3ms/step - loss: 0.2799 - acc: 0.9222 val_loss: 0.2748 - val_acc: 0.9238 Epoch 7/10 469/469 [==============================] - 2s 3ms/step - loss: 0.2737 - acc: 0.9237 val_loss: 0.2710 - val_acc: 0.9240 Epoch 8/10 469/469 [==============================] - 1s 2ms/step - loss: 0.2693 - acc: 0.9250 val_loss: 0.2692 - val_acc: 0.9257 Epoch 9/10 469/469 [==============================] - 1s 3ms/step - loss: 0.2652 - acc: 0.9263 val_loss: 0.2674 - val_acc: 0.9244 Epoch 10/10 469/469 [==============================] - 1s 2ms/step - loss: 0.2626 - acc: 0.9266 val_loss: 0.2680 - val_acc: 0.9250 テストデータに 対する正解率 92.5% 15
  8. Keras による多層ニューラルネットワークの実装例 • レイヤーを順に追加する形で定義します。 model = models.Sequential(name='multilayer_model') model.add(layers.Input(shape=(784,))) model.add(layers.Dense(512, activation='relu',

    name='feedforward1')) model.add(layers.Dense(256, activation='relu', name='feedforward2')) model.add(layers.Dense(128, activation='relu', name='feedforward3')) model.add(layers.Dense(10, activation='softmax', name='softmax')) • モデルの出力は(線形多項分類器と同様の)確率値なので学習処理は変わりません。 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc']) history = model.fit(train_images, train_labels, validation_data=(test_images, test_labels), batch_size=128, epochs=10) 19
  9. Keras による多層ニューラルネットワークの実装例 Epoch 1/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.4483 - val_acc: 0.9683

    - val_loss: 0.1004 Epoch 2/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0942 - val_acc: 0.9761 - val_loss: 0.0804 Epoch 3/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0562 - val_acc: 0.9778 - val_loss: 0.0823 Epoch 4/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0419 - val_acc: 0.9783 - val_loss: 0.0823 Epoch 5/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0339 - val_acc: 0.9768 - val_loss: 0.0840 Epoch 6/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0249 - val_acc: 0.9774 - val_loss: 0.0896 Epoch 7/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0221 - val_acc: 0.9732 - val_loss: 0.1149 Epoch 8/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0215 - val_acc: 0.9788 - val_loss: 0.0936 Epoch 9/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0187 - val_acc: 0.9753 - val_loss: 0.1155 Epoch 10/10 469/469 ━━━━━━━━━━━━━━━━━━━━ 0.0145 - val_acc: 0.9831 - val_loss: 0.0842 テストデータに 対する正解率 98.3% 11s 19ms/step - acc: 0.8669 - loss: 9s 17ms/step - acc: 0.9723 - loss: 9s 19ms/step - acc: 0.9825 - loss: 8s 16ms/step - acc: 0.9868 - loss: 8s 16ms/step - acc: 0.9895 - loss: 11s 18ms/step - acc: 0.9919 - loss: 9s 16ms/step - acc: 0.9930 - loss: 8s 17ms/step - acc: 0.9929 - loss: 過学習が発生 9s 18ms/step - acc: 0.9937 - loss: 7s 15ms/step - acc: 0.9954 - loss: 20
  10. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part01/2. Multilayer model MNIST classification.ipynb ▪

    多層ニューラルネットワークによる多項分類器を Keras で実装して、MNIST データセットの 分類を行います。 ▪ モデルの構成(レイヤー数、レイヤー内のノード数)を変更して、結果がどのように変わる か観察してください。 model = models.Sequential(name='multilayer_model') model.add(layers.Input(shape=(784,))) model.add(layers.Dense(512, activation='relu', name='feedforward1')) model.add(layers.Dense(256, activation='relu', name='feedforward2')) model.add(layers.Dense(128, activation='relu', name='feedforward3')) model.add(layers.Dense(10, activation='softmax', name='softmax')) 21
  11. 畳み込みフィルターによる特徴量の抽出 定数 b (< 0) を加えて ReLU を適用した結果 • 実際の畳み込みフィルターでは、フィルター

    適用後に「定数を加えて、活性化関数を適用 する」という追加処理を行います。 • 下図は、フィルター適用後に定数 -0.2 を加え て、ReLU を適用しています。(フィルター適 用後にピクセル値が 0.2 以下の部分は強制的 に 0 に変換される) 25
  12. 畳み込みフィルターの動的な学習 • 一般の画像データの場合、どのようなフィルターが適切なのかは、すぐには分からないの で、フィルターも学習対象のパラメーターとして、適切なフィルターそのものを学習させま す。 • 下記のモデルでは、16 種類のフィルターを適用した後、全結合層を介して、線形多項分類器 に入力しています。 model

    = models.Sequential(name='CNN_model') model.add(layers.Input(shape=(784,))) model.add(layers.Reshape((28, 28, 1), name='reshape')) model.add(layers.Conv2D(16, (5, 5), padding='same', strides=(2, 2), use_bias=True, activation='relu', name='conv_filter')) model.add(layers.Flatten(name='flatten')) model.add(layers.Dense(512, activation='relu', name='hidden')) model.add(layers.Dense(10, activation='softmax', name='softmax')) 27
  13. 学習後のフィルターの様子 畳み込みフィルターの動的な学習 Epoch 1/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9752 -

    val_loss: 0.0788 Epoch 2/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9793 - val_loss: 0.0594 Epoch 3/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9828 - val_loss: 0.0492 Epoch 4/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9834 - val_loss: 0.0547 Epoch 5/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9847 - val_loss: 0.0540 Epoch 6/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9863 - val_loss: 0.0496 Epoch 7/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9830 - val_loss: 0.0671 Epoch 8/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9837 - val_loss: 0.0620 Epoch 9/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9841 - val_loss: 0.0724 Epoch 10/10 469/469 ━━━━━━━━━━━━━━━━━━━━ - val_acc: 0.9870 - val_loss: 0.0498 テストデータに 対する正解率 98.7% 5s 6ms/step - acc: 0.8820 - loss: 0.4215 1s 2ms/step - acc: 0.9771 - loss: 0.0754 2s 3ms/step - acc: 0.9856 - loss: 0.0457 2s 3ms/step - acc: 0.9912 - loss: 0.0289 2s 3ms/step - acc: 0.9942 - loss: 0.0193 1s 3ms/step - acc: 0.9962 - loss: 0.0133 1s 3ms/step - acc: 0.9976 - loss: 0.0092 1s 3ms/step - acc: 0.9968 - loss: 0.0098 1s 3ms/step - acc: 0.9969 - loss: 0.0089 1s 3ms/step - acc: 0.9972 - loss: 0.0086 28
  14. 2 層目のフィルターの適用方法 • 1 層目のフィルターから得られ た n 枚の画像データを「n 個の レイヤーからなる画像」とし

    て、2層目のフィルターを適用 します。 • 右図は 1 層目に 32 個、2 層目 に 64 個のフィルターを用意し た例です。2 層目の 64 個の フィルターは、それぞれ内部的 に 32 個のサブフィルターを持 ちます。 30
  15. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part01/3. Static filter example.ipynb ▪ ◦

    縦/横のエッジを取り出す固定的なフィルターを適用します。 Part01/4. Dynamic filter MNIST classification.ipynb ▪ 畳み込みニューラルネットワーク(CNN)で MNIST データセットの分類を行います。(16 個のフィルターを持つ畳み込み層を使用しています。) ▪ フィルター数を変更して、結果がどのように変わるか観察してください。 model = models.Sequential(name='CNN_model') model.add(layers.Input(shape=(784,))) model.add(layers.Reshape((28, 28, 1), name='reshape')) model.add(layers.Conv2D(16, (5, 5), padding='same', strides=(2, 2), use_bias=True, activation='relu', フィルター数 name='conv_filter')) model.add(layers.Flatten(name='flatten')) model.add(layers.Dense(512, activation='relu', name='hidden')) model.add(layers.Dense(10, activation='softmax', name='softmax')) 34
  16. オートエンコーダの学習例 32 × 32 × 1 • 32 × 32

    ピクセルの画像を 2 次元の潜 在空間に埋め込むオートエンコーダを 定義します。 • つまり、1 つの画像は、2 次元平面の 1 つの点に対応づけられます。 • エンコーダ部分は畳み込みフィル ター、デコーダ部分は転置畳み込み フィルターを使用します。 16 × 16 × 32 8 × 8 × 64 4 × 4 × 128 2 次元の 潜在空間 38
  17. オートエンコーダの実装例(エンコーダ) • エンコーダは の 2 つの値を出力します。 32 × 32 ×

    1 encoder = models.Sequential(name='encoder') encoder.add(layers.Input(shape=(32*32,), name='encoder_input')) encoder.add(layers.Reshape((32, 32, 1), name='reshape')) encoder.add(layers.Conv2D(32, (3, 3), strides=2, padding='same', activation='relu', name='conv_filter1')) # (16, 16, 32) encoder.add(layers.Conv2D(64, (3, 3), strides=2, padding='same', activation='relu', name='conv_filter2')) # (8, 8, 64) encoder.add(layers.Conv2D(128, (3, 3), strides=2, padding='same', activation='relu', name='conv_filter3')) # (4, 4, 128) encoder.add(layers.Flatten(name='flatten')) encoder.add(layers.Dense(2, name='embedding_space')) 16 × 16 × 32 8 × 8 × 64 潜在空間 4 × 4 × 128 39
  18. オートエンコーダの実装例(デコーダ) • エンコーダが出力した潜在空間の点から画像を生成します。 decoder = models.Sequential(name='decoder') decoder.add(layers.Input(shape=(2,), name='decoder_input')) decoder.add(layers.Dense(4 *

    4 * 128, name='expand')) decoder.add(layers.Reshape((4, 4, 128), name='reshape')) decoder.add(layers.Conv2DTranspose(64, (3, 3), strides=2, padding='same', activation='relu', name='conv_transpose1')) # (8, 8, 64) decoder.add(layers.Conv2DTranspose(32, (3, 3), strides=2, padding='same', activation='relu', name='conv_transpose2')) # (16, 16, 32) decoder.add(layers.Conv2DTranspose(1, (3, 3), strides=2, padding='same', activation='sigmoid', name='conv_transpose3')) # (32, 32, 1) decoder.add(layers.Flatten(name='flatten')) 潜在空間 40
  19. オートエンコーダの本質的な課題 学習データ • 現実世界の学習データ(整った画像)は、潜在空間上 でまばらに存在します。 • 2 つの学習データの「隙間」部分からは、2 つの画像 の中間状態の画像が生成されると期待されますが、こ

    のような画像は人間の目から見て「自然な画像」とは 限りません。 • エンコーダ 「飛び飛びに存在する現実世界のデータ」を連続的な 潜在空間にマッピングするという点にオートエンコー ダーの本質的な課題があります。 この隙間の部分を学習する データが存在しない 潜在空間 44
  20. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part02/1. AutoEncoder MNIST example.ipynb ▪ フィードフォワード・ネットワークによるオートエンコーダのモデルを

    MNIST データセット で学習します。 ▪ ◦ さまざまな手書き文字を入力して、どのような出力結果になるか観察してください。 Part02/2. AutoEncoder Fashion MNIST.ipynb ▪ 畳み込みフィルターと転置畳み込みフィルターによるオートエン コーダのモデルを Fashion MNIST データセットで学習します。 46
  21. 正規分布について • 平均 μ 、分散 σ2(標準偏差 σ)の正規分布に従う乱数を • 特に ◦

    の場合 で表します。 を「標準正規分布」と言います。 標準正規分布の乱数は、次の式で、正規分布 に変換できます。 2次元の 正規分布 正規分布 48
  22. 変分オートエンコーダの仕組み • 2 次元の潜在空間を用いるものとして、次の手続きを実行します。 ◦ エンコーダは、入力画像に対して、正規分布の平均 と各軸方向の標準偏差 を出力します。 ◦ 得られた正規分布からサンプリングした値をデコーダに入力します。

    ◦ デコーダの出力画像が入力画像に近づくようにモデルのパラメーターを更新します。 ◦ エンコーダが出力する正規分布が標準正規分布か 潜在空間 ら大きく外れない様に、誤差関数に罰則項を追加 します。 • この範囲の乱数で得られた 値をデコーダに入力する これらにより、潜在空間の原点の近くに「整った画像 が得られる点」が集まるものと期待できます。 49
  23. 変分オートエンコーダの実装例(エンコーダ) • エンコーダは ◦ 次式で の 4 つの値を出力します。 32 ×

    32 × 1 に変換して利用します。 16 × 16 × 32 encoder = models.Sequential(name='encoder') encoder.add(layers.Input(shape=(32*32,), name='encoder_input')) encoder.add(layers.Reshape((32, 32, 1), name='reshape')) encoder.add(layers.Conv2D(32, (3, 3), strides=2, padding='same', activation='relu', name='conv_filter1')) # (16, 16, 32) encoder.add(layers.Conv2D(64, (3, 3), strides=2, padding='same', activation='relu', name='conv_filter2')) # (8, 8, 64) encoder.add(layers.Conv2D(128, (3, 3), strides=2, padding='same', activation='relu', name='conv_filter3')) # (4, 4, 128) encoder.add(layers.Flatten(name='flatten')) encoder.add(layers.Dense(4, name='mean_and_log_var')) 8 × 8 × 64 4 × 4 × 128 50
  24. 変分オートエンコーダの実装例(サンプラー) • サンプラーは次の関係を利用して、平均 、各軸方向の標準偏差 布から、デコーダに入力する値をランダムに選択します。 の正規分 標準正規分布 標準正規分布の乱数を 目的の正規分布に変換 def

    get_samples(x): # x: encoder output num_examples = tf.shape(x)[0] means, log_vars = x[:, 0:2], x[:, 2:4] std_samples = tf.random.normal(shape=(num_examples, 2)) samples = means + tf.exp(0.5 * log_vars) * std_samples return samples sampler = models.Sequential(name='sampler') sampler.add(layers.Input(shape=(4,), name='sampler_input')) sampler.add(layers.Lambda(get_samples, name='sampled_embedding')) × 潜在空間 デコーダに入力する 値をランダムに選択 51
  25. 変分オートエンコーダの実装例(デコーダ) • デコーダは、サンプラーが選択した潜在空間の点から画像を生成 します。(オートエンコーダと同じコードです。) decoder = models.Sequential(name='decoder') decoder.add(layers.Input(shape=(2,), name='decoder_input')) decoder.add(layers.Dense(4

    * 4 * 128, name='expand')) decoder.add(layers.Reshape((4, 4, 128), name='reshape')) decoder.add(layers.Conv2DTranspose(64, (3, 3), strides=2, padding='same', activation='relu', name='conv_transpose1')) # (8, 8, 64) decoder.add(layers.Conv2DTranspose(32, (3, 3), strides=2, padding='same', activation='relu', name='conv_transpose2')) # (16, 16, 32) decoder.add(layers.Conv2DTranspose(1, (3, 3), strides=2, padding='same', activation='sigmoid', name='conv_transpose3')) # (32, 32, 1) decoder.add(layers.Flatten(name='flatten')) × 潜在空間 サンプラーが選んだ値 52
  26. 変分オートエンコーダの実装例(誤差関数) • 「KL ダイバージェンス」は、2 つの乱数分布がどの程度似ているかを表す計算式です。 (KL ダイバージェンスの値が小さいほど、2 つの分布はより似ています。) • エンコーダの出力から得られる正規分布

    の標準正規分布 KL ダイバージェンスを誤差関数に罰則項として加えます。 ◦ に対する 罰則項の重み(下の例では 0.001)は、ハイパーパラメータとしてチューニングが必 要です。 def custom_loss(y_true, y_pred): mean, log_var, pred = y_pred[:, 0:2], y_pred[:, 2:4], y_pred[:, 4:1028] reconstruction_loss = losses.mse(pred, y_true) kl_loss = tf.reduce_mean(tf.reduce_sum( -0.5 * (1 + log_var - tf.square(mean) - tf.exp(log_var)), axis=1)) loss = reconstruction_loss + 0.001 * kl_loss return loss KL ダイバージェンス 53
  27. 変分オートエンコーダの実装例 • KL ダイバージェンスの計算に必要なエンコーダの出力値と、デコーダからの出力イメージ を同時に出力するモデルを定義して、このモデルに対して学習処理を行います。 エンコーダの出力 model_inputs = encoder.inputs[0] model_outputs

    = layers.Concatenate(name='prediction_with_mean_log_var')( [encoder(model_inputs), decoder(sampler(encoder(model_inputs)))]) model = models.Model(inputs=model_inputs, outputs=model_outputs, name='Variational_AutoEncoder') デコーダの出力 model.compile(optimizer='adam', loss=custom_loss) 正解ラベル 誤差関数 エンコーダ サンプラー デコーダ 54
  28. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part02/3. Variational AutoEncoder Fashion MNIST.ipynb ▪

    変分オートエンコーダのモデルを Fashion MNIST データセットで学習します。 ▪ 誤差関数における KL ダイバージェンスの重みを変化させて、結果がどのように変わるか観 察してください。 def custom_loss(y_true, y_pred): mean, log_var, pred = y_pred[:, 0:2], y_pred[:, 2:4], y_pred[:, 4:1028] reconstruction_loss = losses.mse(pred, y_true) kl_loss = tf.reduce_mean(tf.reduce_sum( -0.5 * (1 + log_var - tf.square(mean) - tf.exp(log_var)), axis=1)) loss = reconstruction_loss + 0.001 * kl_loss return loss KL ダイバージェンスの重み 59
  29. マルチタスク学習の実装例 • エンコーダ、サンプラー、デコーダは変分オートエンコーダと同じものを再利用します。 • サンプラーの出力を受け取って、ラベルを予測する線形多項分類器を追加で用意します。 discriminator = models.Sequential(name='discriminator') discriminator.add(layers.Input(shape=(2,), name='discriminator_input'))

    discriminator.add(layers.Dense(10, activation='softmax', name='softmax')) • エンコーダの出力値、デコーダからの出力イメージ、分類の予測値を同時に出力するモデル を定義して、このモデルに対して学習処理を行います。 model_inputs = encoder.inputs[0] model_outputs = layers.Concatenate(name='multi_task_output')( [encoder(model_inputs), # mean and log_var decoder(sampler(encoder(model_inputs))), # reconstructed image discriminator(sampler(encoder(model_inputs))) # label prediction ]) model = models.Model(inputs=model_inputs, outputs=model_outputs, name='multi_task_model') 62
  30. マルチタスク学習の実装例 • 学習時の正解ラベルとして使用するため、画像データにラベル値を付与したデータを用意し ます。各画像データの末尾に(One-hot エンコーディングの)10 要素のラベル値が追加さ れた形になります。 labeled_images = np.hstack([train_images,

    train_labels]) • 誤差関数は、VAE の誤差関数に、分類モデルの誤差関数(カテゴリカル・クロスエントロ ピー)を加えます。 def custom_loss(y_true, y_pred): mean, log_var, image, label_pred = y_pred[:, 0:2], y_pred[:, 2:4], y_pred[:, 4:4+32*32], y_pred[:, 4+32*32:] image_true, label_true = y_true[:, :32*32], y_true[:, 32*32:] reconstruction_loss = losses.mse(image, image_true) discriminator_loss = losses.CategoricalCrossentropy()(label_true, label_pred) kl_loss = tf.reduce_mean(tf.reduce_sum( -0.5 * (1 + log_var - tf.square(mean) - tf.exp(log_var)), axis=1)) loss = reconstruction_loss + 0.001 * kl_loss + 0.05 * discriminator_loss return loss 63
  31. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part02/4. VAE multitask learning Fashion MNIST.ipynb

    ▪ 変分オートエンコーダに分類モデルを組み合わせたマルチタスク学習を適用します。 ▪ 誤差関数における分類モデルに対する誤差(カテゴリカル・クロスエントロピー)の重みを 変化させて、結果がどのように変わるか観察してください。 def custom_loss(y_true, y_pred): mean, log_var, image, label_pred = y_pred[:, 0:2], y_pred[:, 2:4], y_pred[:, 4:4+32*32], y_pred[:, 4+32*32:] image_true, label_true = y_true[:, :32*32], y_true[:, 32*32:] reconstruction_loss = losses.mse(image, image_true) discriminator_loss = losses.CategoricalCrossentropy()(label_true, label_pred) kl_loss = tf.reduce_mean(tf.reduce_sum( -0.5 * (1 + log_var - tf.square(mean) - tf.exp(log_var)), axis=1)) loss = reconstruction_loss + 0.001 * kl_loss + 0.05 * discriminator_loss return loss カテゴリカル・クロスエントロピーの重み 66
  32. ラベル付き画像データの準備 • 画像データの直後に One-hot 表現のラベルデータを付加したデータを用意します。 labeled_images = np.hstack([train_images, train_labels]) image

    = labeled_images[0][:32*32] # Image part label = labeled_images[0][32*32:] # Label part ラベル:2 前半の 32×32 個の数値が画像データで その後の 10 個の数値がラベルデータ 69
  33. 条件付き変分オートエンコーダの実装例 • デコーダには、サンプラー の出力値に加えて、ラベル 値を入力するようにモデル を構成します。 model_inputs = tf.keras.Input(shape=(32*32+10,), name='model_inputs')

    input_images, input_labels = model_inputs[:, 0:1024], model_inputs[:, 1024:1034] decoder_inputs = layers.Concatenate(name='decoder_inputs')( [sampler(encoder(input_images)), input_labels]) model_outputs = layers.Concatenate(name='prediction_with_mean_log_var')( [encoder(input_images), decoder(decoder_inputs)]) model = models.Model(inputs=model_inputs, outputs=model_outputs, name='conditional_VAE') decoder = models.Sequential(name='conditional_decoder') decoder.add(layers.Input(shape=(12,), name='decoder_input')) decoder.add(layers.Dense(4 * 4 * 128, name='expand')) decoder.add(layers.Reshape((4, 4, 128), name='reshape')) decoder.add(layers.Conv2DTranspose(64, (3, 3), strides=2, padding='same', activation='relu', name='conv_transpose1')) # (8, 8, 64) decoder.add(layers.Conv2DTranspose(32, (3, 3), strides=2, padding='same', activation='relu', name='conv_transpose2')) # (16, 16, 32) decoder.add(layers.Conv2DTranspose(1, (3, 3), strides=2, padding='same', activation='sigmoid', name='conv_transpose3')) # (32, 32, 1) decoder.add(layers.Flatten(name='flatten')) 70
  34. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part02/5. Conditional VAE Fashion MNIST.ipynb ▪

    条件付き変分オートエンコーダのモデルを Fashion MNIST データセットで学習します。 ▪ 学習後のデコーダにさまざまなラベル値(複数のラベルを同時に指定)を与えて、生成され る画像がどのように変化するか観察してください。 ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] fig = plt.figure(figsize=(6, 6)) label = [0] * 10 label[1], label[6] = 0.5, 0.5 # Trouser + Shirt add_grid_images(fig, label) ラベル値の設定 One-hot エンコーディングの フラグと画像の種類の対応 75
  35. LSTM の仕組み • LSTM のセルは、「隠れ状態 h」と「セル状態 c」の 2 種類の情報を伝達します。隠れ状態 は、直前の出力値に一致します。

    ◦ • 隠れ状態とセル状態は同じ次元のベクトル値で、具体的な次元数はハイパーパラメーターとして利 用者が指定します。 大雑把には、次の様に理解する事ができます。 ◦ セル状態に、これまでの処理結果の情報が保存さ れます。 ◦ 隠れ状態と新しい入力値を用いて、セル状態の情 報を更新します。(不要な情報を削除して、新し い情報を追加します。) ◦ 更新後のセル状態の情報を用いて、「隠れ状態+ 入力値」から出力値を生成します。 ◦ 隠れ状態は短期的な(=直前の)情報、セル状態 は長期的な情報を伝達すると考えられます。 長期的な 情報 不要な情報 を削除 「直前+現在」 の情報 新しい情報 を追加 更新されたセル状態 から出力値を生成 80
  36. LSTM の仕組み • シグモイド関数を除いてみると、LSTM の基本的な情報の流れが理解できます。 ◦ 2 つの tanh は、入力情報・出力情報に対する活性化関数として機能します。

    ◦ 3 つのシグモイド関数は、削除・追加・出力する情報を選定するゲートになります。 長期的な 情報 新しい情報 を追加 不要な情報 を削除 新しい情報 を追加 削除する情報を 選定するゲート 「直前+現在」 の情報 更新されたセル状態 から出力値を生成 追加する情報を 選定するゲート 出力する情報を 選定するゲート 81
  37. ゲート機構の仕組み • ゲート機構は、次の計算処理を行います。 ◦ ◦ で c の各成分の「重要度」(0 〜 1

    の値)を計算します。 c の各成分に対応する重要度を個別に掛けます。これにより、重要度の低い成分は値が小さくなり ます。 c と同じ次元のベクトル x に基づいて c から 残すべき情報を選定 82
  38. LSTM の利用方法 • 時系列データ全体から 1 つの予測値が必要な場合は、最後のセル の出力値のみを利用します。一方、各時刻に対する予測値が必要 な場合は、すべてのセルの出力値を利用します。 • 複数の

    LSTM を多段に重ねて利用することもできます。 出力値 多項分類器 テキスト分類を行う例 LSTM レイヤー2 セル B セル B ・・・ セル B LSTM レイヤー1 セル A セル A ・・・ セル A t=1 t=2 入力値 t=T 83
  39. テキストのトークン化について • テキストデータを扱う場合、あらかじめ、それぞれの単語を対応する整数値(トークン ID) に置き換えて、数値リストのデータに変換します。事前に用意されたトークン化ツール (トークナイザー)を用いて変換します。 ◦ アポストロフィー(')などの記号は、 トークナイザーが自動判別して処理し ます。

    I didn't feel humiliated. トークナイザーに よる処理の例 トークン ID のリストに変換 ◦ ◦ テキストの先頭と末尾を示すトークン ([CLS] / [SEP])が付与されます。 固定長のリストにするため残りの部分 は 0([PAD]) で埋められます。 [101, 1045, 2134, 2102, 2514, 26608, 102, 0, 0, ..., 0] トークン ID をテキストに再変換 [CLS] i didn ##t feel humiliated [SEP] [PAD] [PAD] ... [PAD] 86
  40. トークンの埋め込み表現について • テキストデータをニューラルネットワークで処理する場合、最初の「埋め込み層」でそれぞ れのトークン ID を高次元空間の埋め込みベクトルに変換します。 ◦ トークン ID とベクトル値は、単純に

    1 対 1 で対応させます。この際の「対応表」が埋 め込み層のウェイト(チューニング対象のパラメータ)になります。 ◦ モデルの学習結果として、意味が近い単語は、埋め込みベクトルも類似した値になるも のと期待できます。 埋め込みベクトルの次元 ID = 1 に対応するベクトル 埋め込み層のウェイト ID = 2 に対応するベクトル トークン ID の値の範囲 ID = 3 に対応するベクトル ・・・ 87
  41. 双方向 LSTM による分類モデルの実装例 トークン数 N • 埋め込み層と双方向 LSTM では次のオプションを指定します。 ◦

    ◦ • 埋め込み層:トークン ID の値の範囲(VOCAB_SIZE)、埋め込み ベクトルの次元(512) 双方向 LSTM: 隠れ状態の次元(512、および、256)、すべての セルの出力値を使用するかどうか(return_sequences) [101, 1045, 2134, ..., 0] [N] 埋め込み層 [N, 512] ・・・ 双方向 LSTM 過学習を防止するためのドロップアウト層が挟まれています。 [N, 512*2] lstm_model = models.Sequential(name='Bidirectional_LSTM_classifier') lstm_model.add(layers.Input(shape=(None,), name='input_ids')) lstm_model.add(layers.Embedding(VOCAB_SIZE, 512, name='text_embedding')) lstm_model.add(layers.Bidirectional(layers.LSTM(512, return_sequences=True), name='bidirectional_LSTM1')) lstm_model.add(layers.Dropout(rate=0.4, name='dropout1')) lstm_model.add(layers.Bidirectional(layers.LSTM(256, return_sequences=False), name='bidirectional_LSTMs')) lstm_model.add(layers.Dropout(rate=0.4, name='dropout2')) lstm_model.add(layers.Dense(256, activation='relu', name='feedforward')) lstm_model.add(layers.Dropout(rate=0.4, name='dropout3')) lstm_model.add(layers.Dense(6, activation='softmax', name='softmax')) ・・・ ・・・ 双方向 LSTM [256*2] 全結合層 [256] 線形多項分類器 88
  42. (参考)ドロップアウト層について • ドロップアウト層は、モデルの学習時に、2つのレイヤー間の接続の一部をランダムに切断 します。 ◦ • 例えば、512 次元のベクトル値(= 512 個の実数値)を受け渡した際に、40%

    の値が 強制的に 0 に書き換えられます。 ドロップアウト層の直後のレイヤーは、一部の情報が欠落した状態で予測をする必要がある ため、学習データに過度に依存することなく、汎化性能が維持できると期待されます。 89
  43. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part03/1. LSTM emotion recognizer.ipynb ▪ LSTM

    によるテキスト分類モデルを emotion データセットで学習します。 ▪ さまざまなテキストに対する予測結果を確認してみてください。 input_text = [ "I lost my words as he nudged me.", "It always makes my day.", "I'm caring about you as your mother.", "I am not happy at all. It's opposite!", "The movie gave me a chill in my spine.", "I am stunned at the heartfelt welcome." ] ▪ モデルの定義に含まれる layers.LSTM を layers.SimpleRNN に変えると、 LSTM のセルが「最もシンプルな RNN」のセルに変わります。LSTM の場 合と精度を比較してください。 91
  44. RNN による「次の単語」の予測 • RNN で「次の単語」を予測する場合、トークン ID の値をカテゴリーラベルとして、多項分 類器でトークン ID ごとの確率値を出力します。

    • 各セルは、入力データに対して、「自身が受け取るトークンよりも前にある情報」を隠れ状 態から受け取って利用して予測します。 t = 2 の単語 t = 3 の単語 を予測 出力値 出力値 確率 トークン ID すべてのトークン ID に ついて確率値を出力 を予測 入力値 多項分類器 多項分類器 セル セル t=1 t=2 多項分類器 ・・・ セル t=T 93
  45. RNN による「次の単語」の予測 • 学習に使用するデータは、下図のように、トークン ID に変換したテキストに対して、最後の トークンを除いたものを入力データ、最初のトークンを除いたものを正解ラベルとします。 入力データ [SEP] [CLS]

    time flies like an arrow [101, 2051, 10029, 2066, 2019, 8612, 102, 0, 0, 0] トークン ID に変換 正解ラベル [CLS] time 入力データ flies like an arrow [SEP] [PAD] [PAD] [101, 2051, 10029, 2066, 2019, 8612, 102, 0, 0] 0, 0] ・・・ 正解ラベル [2051, 10029, 2066, 2019, 8612, 102, time flies like an 0, arrow [SEP] [PAD] [PAD] [PAD] 94
  46. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part03/2. LSTM recipe generator training.ipynb ▪

    ◦ LSTM によるテキスト生成モデルを「レシピ」のデータセットで学習します。 Part03/3. LSTM recipe generator analysis.ipynb ▪ 学習後のテキスト生成モデルを用いて、レシピのテキストを生成します。 ▪ 温度パラメーターによる生成テキストの変化を観察してください。 gen_text('Recipe for beef', temp=0.5, lang='ja') 温度パラメーターの指定 98
  47. Transformer モデルの考え方 • • テキストにおける単語の「意味」は、まわりにある単語との関係で変化します。例えば、次 の 2 つのテキストの「flies」の品詞・意味は、最後にある単語(arrow / banana)を見て

    初めて決定する事ができます。 ◦ Time flies like an arrow.(時間は矢のように飛ぶ) ◦ Time flies like a banana.(「時間バエ」はバナナを好む) 一般に、トークンを埋め込み空間のベクトルに変換した場合、「意味が近いトークンはベク トル値も近くなる」と期待されますが、テキストに依存しない一般的な「意味」と、特定の テキストに置かれた場合の「意味」は異なります。 101
  48. Attention Head の仕組み • トークンごとに、現在の埋め込みベクトルの値から、 Query, Key, Value の 3

    種類のベクトル値を生成します。 ◦ ◦ • Query, Key, Value は、それぞれ(トークンの位置に依存しな い)共通のアフィン変換(1 次関数の集まり=活性化関数を持 たない隠れ層)で生成します。 Value Value Key Key Query Query Value ・・・ Key Query 「トークン A の意味に、トークン B が及ぼす影響度」は、 「トークン A の Query」と「トークン B の Key」の内積値 で決まると考えます。(内積値が大きいと影響が大きい。) ◦ • これらは同じ次元のベクトルで、この次元を「Attention Head のサイズ」と言います。 先の例では、「flies の Query」と「banana の Key」は内積値が大きくなると期待されます。 内積値に基づいた重みで、すべてのトークンの Value を合成したものを新しい埋め込みベク トルの値として出力します。 103
  49. Attention Head の仕組み • 右図は「flies」について、新しい埋め込み ベクトルを出力する流れです。 • すべてのトークンについて同様の処理を行 います。 "flies"

    の新しい 埋め込みベクトル weight の重みで Value を合成 Query と Key の内積で weight を計算 Value Value Value Value Value weight weight weight weight weight Key Key Key Key Key Query Query Query Query Query Query, Key, Value を生成 "flies" の現在の 埋め込みベクトル time flies like a banana 104
  50. Multi-Head Attention • 複数の Attention Head を並列に使用すること で、異なる観点での「意味」が抽出できます。 • それぞれの

    Attention Head の出力を 1 列につ なげたものを新しい埋め込みベクトルの値としま す。 ◦ • 各 Attention Head のサイズは、「元の次元 / Attention Head の個数」にします。これによ り、結合後のベクトルが元の次元に戻ります。 各アテンションヘッド からの出力を結合 Attention Head アテンションヘッド 複数の Attention Head からなる、右図のブロッ クを「Multi-Head Attention」と呼びます。 time flies like a banana 105
  51. (参考)RNN と Attention Head の比較 • 埋め込みベクトルから Query, Key, Value

    を生成する仕組みは、トークンの位置に依存しないので、その 意味では、同一のセルを繰り返し使用する RNN に類似した仕組みと言えます。 • RNN は隣接するトークンの相互作用のみを考えたのに対して、Attention Head はすべてのトークンの相 互作用を取り入れた点が大きな違いになります。(この結果、Attention Head の処理結果は、トークン の順序に依存しないことになります。) セル t=1 ・・・ セル t=2 セル ウェイトの値は すべてのセルで共通 Value Value Key Key Query Query Value ・・・ Key Query t=T RNN アフィン変換 (トークンの位置に依存しない) Attention Head 106
  52. Transformer エンコーダの構造 • Multi-Head Attention の後段に、「レイヤー正規化+全 結合層+レイヤー正規化」を繋げたものが 「Transformer エンコーダ」です。 レイヤー正規化

    全結合層 • ◦ レイヤー正規化と全結合層もトークンの位置に依存しない、 すべてのトークンに共通の計算処理を適用します。 ◦ 過学習を防止するために、必要に応じてドロップアウト層を 追加します。 一般には、複数の Transormer エンコーダを多段に積み 重ねて利用します。 レイヤー正規化 マルチヘッド・アテンション スキップ接続 Transformer エンコーダ 107
  53. (参考)スキップ接続について • レイヤーに対して、入力 x に対する「変化分」を計算させたい場合に使用します。 • 学習の初期においても(ウェイトを小さい値で初期化すれば)、出力 y が入力 x

    に近い値に なるため、後続のレイヤーに意味のある情報を伝えることができて、学習がスムーズに進む と期待できます。 出力 y このレイヤーは x と y の 差分 y - x を計算すればよい スキップ接続 入力 x 108
  54. (参考)レイヤー正規化について • 埋め込みベクトルを として、すべての成分についての平均と分散が 0, 1 になるように正規化した上で、成分ごとにスケーリング(定数倍して定数値を加える) します。 ◦ スケーリングの定数

    は学習対象のパラメータです。これは、成分ごとに適したス ケールがあるという想定に基づいた仕組みです。 トークンごとに 個別に正規化 σ ≒ 0 の時にエラーにならない ように小さな値を加える レイヤー正規化 埋め込みベクトル 109
  55. アテンションマスクについて • Transformer で「次の単語の予測」を行う場合、学習時に、入力トークンよりも後ろのトー クンの情報を参照しないように Attention Head の処理を制限する必要があります。 • Attention

    Head はオプションで「アテンションマスク」を受け取り、それぞれのトークン に対して、処理対象とするトークンを限定することができます。 t = 2 の単語 を予測 t = 3 の単語 を予測 出力データ t = 1 のトークンに 対する処理対象 多項分類器 多項分類器 ・・・ 多項分類器 Transformer エンコーダ 入力データ t=1 t=2 ・・・ t=T t = 2 のトークンに 対する処理対象 アテンションマスクの例 [[1, 0, 0, 0, 0], [1, 1, 0, 0, 0], [1, 1, 1, 0, 0], [1, 1, 1, 1, 0], [1, 1, 1, 1, 1]] 110
  56. Transformer モデルによるテキスト分類 • • • Transformer モデルでテキスト分類を 実装する場合、最初のトークンに対す る出力値を多項分類器に入力します。 多項分類器

    最初のトークンの埋め込みベクトル に、分類に必要な情報が集約するよう にモデル全体が学習されるものと期待 します。 アテンションマスクによる参照制限は 行いません。(固定長リストにするた めの末尾の Padding 部分は参照しない ようにアテンションマスクを設定しま す。) 入力データ Transformer エンコーダ Transformer エンコーダ Positional エンコーディング t=1 t=2 ・・・ t=T 113
  57. (参考)Keras のカスタムレイヤーの定義方法 全結合層とレイヤー正規化からなる カスタムレイヤー DenseLN を定義する例 カスタムレイヤーの @saving.register_keras_serializable() オプションを指定 class

    DenseLN(layers.Layer): def __init__(self, ff_dim, ff_activation, **kwargs): super(DenseLN, self).__init__(**kwargs) self.ff_dim = ff_dim レイヤー内部で使用する self.ff_activation = ff_activation サブレイヤーを定義 self.ff = layers.Dense(ff_dim, activation=ff_activation) self.ln = layers.LayerNormalization(epsilon=1e-6) # 入力データを inputs で受け取って、処理結果を返却する関数 def call(self, inputs): x = self.ff(inputs) カスタムレイヤーの計算処理を outputs = self.ln(x) ファンクショナル API で記述 return outputs def build(self, input_shape): pass def get_config(self): config = super().get_config() config.update({ "ff_dim": self.ff_dim, "ff_activation": sef.ff_activation }) return config DenseLN layers.LayerNormalization 利用可能なオプション の辞書を定義 layers.Dense レイヤー正規化 全結合層 114
  58. Transformer モデルによるテキスト分類 • 下図は、1 層目と 2 層目の Transformer エンコーダの Attention

    Heads が計算したトーク ン間の Weights(4 つの Attention Heads の最大値を 2 レイヤーで平均)の例です。 115
  59. Transformer モデルによるテキスト生成 • • • RNN による実装と同様に、各トークン について「次の単語」の確率分布を予 測するようにモデルを学習します。 学習後のモデルにテキストの冒頭部分

    を入力すると、最後の出力から次の単 語の確率分布が得らるので、ここから 単語をサンプリングして、末尾に付け 加える作業を繰り返します。 学習時はアテンションマスクにより、 「自分より後ろのトークン」は参照し ないように制限します。 入力データ すべてのトークン ID に ついて確率値を出力 多項分類器 多項分類器 多項分類器 Transformer エンコーダ Positional エンコーディング t=1 t=2 ・・・ t=T 117
  60. (参考)Transformer を用いた LLM の例 • 2022 年に論文公開されたモデル(PaLM)では、モデルのサイズに関連した下記の値が 公開されています。 ◦ モデルに入力可能なトークン数は

    2048 です。 Transformer エンコーダの段数 Attention Head の個数 埋め込みベクトルの次元 Attention Head のサイズ https://arxiv.org/abs/2204.02311 119
  61. (参考)Vision Transformer による画像の意味抽出 • Vision Transformer (ViT) は、画像データに Transformer を適用するモデルです。

    • 画像を小さなパッチの列に分解して、モデルに入力します。 • それぞれのパッチは、最初のレイヤー で、対応する「埋め込みベクトル」に変 換されます。 • その後は、LLM と同様の Transformer エンコーダで処理を行います。 ◦ 複数のパッチの位置関係から「画像 の意味」を表す情報が得られると期 待されます。 https://arxiv.org/abs/2010.11929 122
  62. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part04/1. Transformer emotion recognizer.ipynb ▪ Transformer

    によるテキスト分類モデルを emotion データセットで学習します。 ▪ 埋め込みベクトルの次元、Attention Head の個数とサイズ、全結合層のノード数がそれぞれ 下記のパラメータで指定できます。パラメータの値によってモデルの精度がどのように変化 するか、観察してください。 EMBEDDING_DIM = 512 N_HEADS = 4 KEY_DIM = EMBEDDING_DIM // N_HEADS FEED_FORWARD_DIM = 2048 124
  63. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part04/2. Transformer recipe generator training.ipynb ▪

    Transformer によるテキスト生成モデルを「レシピ」のデータセットで学習します。 ▪ 「Part04/1. Transformer emotion recognizer.ipynb」と同様に下記のパラメータが指定で きます。 EMBEDDING_DIM = 512 N_HEADS = 4 KEY_DIM = EMBEDDING_DIM // N_HEADS FEED_FORWARD_DIM = 2048 ◦ Part04/3. Transformer recipe generator analysis.ipynb ▪ 学習後のテキスト生成モデルを用いて、レシピのテキストを生成します。 125
  64. DCGAN の仕組み 潜在空間からランダムに 入力値をサンプリング 識別モデルが出力する P(A) が大きくなる方向に パラメーターを修正 生成器 画像セット

    A P(A) : A が本物である確率 識別器 学習データ B P(B) : B が本物である確率 P(A) が小さく、P(B) が大きくなる 方向にパラメーターを修正 128
  65. DCGAN による画像生成モデルの実装例 • 生成器は、転置畳み込みフィルターによる画像生成モデルを使用します。 • 活性化関数に LeakyReLU を使用すると、学習がよりうまく進むことが知られています。 latent_dim =

    64 潜在空間の次元 generator = models.Sequential(name='generator') generator.add(layers.Input(shape=(latent_dim,), name='generator_input')) generator.add(layers.Dense(4 * 4 * 128, name='expand')) generator.add(layers.Reshape((4, 4, 128), name='reshape')) generator.add(layers.Conv2DTranspose(64, (3, 3), strides=2, padding='same', name='conv_transpose1')) # (8, 8, 64) generator.add(layers.LeakyReLU(negative_slope=0.2, name='leaky_relu1')) generator.add(layers.Conv2DTranspose(32, (3, 3), strides=2, padding='same', name='conv_transpose2')) # (16, 16, 32) generator.add(layers.LeakyReLU(negative_slope=0.2, name='leaky_relu2')) generator.add(layers.Conv2DTranspose(1, (3, 3), strides=2, padding='same', activation='sigmoid', name='conv_transpose3')) # (32, 32, 1) generator.add(layers.Flatten(name='flatten')) 129
  66. DCGAN による画像生成モデルの実装例 • 識別器は CNN(畳み込みニューラルネットワーク)による分類モデルを使用します。 ◦ • 本物 / 偽物を識別する二項分類器を実装します。

    生成器と同様に、活性化関数に LeakyReLU を使用するほか、ドロップアウト層で識別性能 を調整します。 識別性能を調整 discriminator = models.Sequential(name='discriminator') discriminator.add(layers.Input(shape=(32*32,), name='discriminator_input')) discriminator.add(layers.Reshape((32, 32, 1), name='reshape')) discriminator.add(layers.Conv2D(64, (5, 5), strides=2, padding='same', name='conv1')) discriminator.add(layers.LeakyReLU(negative_slope=0.2, name='leaky_relu1')) discriminator.add(layers.Conv2D(128, (5, 5), strides=2, padding='same', name='conv2')) discriminator.add(layers.LeakyReLU(negative_slope=0.2, name='leaky_relu2')) discriminator.add(layers.Flatten(name='flatten')) discriminator.add(layers.Dropout(rate=0.4, name='dropout')) discriminator.add(layers.Dense(1, activation='sigmoid', name='sigmoid')) 「本物である確率」を出力 130
  67. DCGAN による画像生成モデルの実装例 • 「勾配降下法を 1 回実行だけ適用する処理」を識別器と生成器に対して交互に実行します。 ◦ • ここでは Tensorflow

    の Low-level API で実装しています。 生成器の学習では、ランダムに選択した潜在空間の値から「生成器」→「識別器」→「予測 値(本物である確率)」を出力して、これが正解ラベルに一致するように生成器のパラメー ターを修正します。 生成器で生成した画像に 「本物」のラベルを付ける 予測値を取得 Low-level API で勾配降下法 を 1 回だけ適用 # Train generator random_inputs = tf.random.normal(shape=(batch_size, self.latent_dim)) fake_labels = tf.ones((batch_size, 1)) # labels are real images with tf.GradientTape() as tape: predictions = self.discriminator(self.generator(random_inputs)) g_loss = self.loss_fn(fake_labels, predictions) grads = tape.gradient(g_loss, self.generator.trainable_weights) self.g_optimizer.apply_gradients( zip(grads, self.generator.trainable_weights)) 131
  68. DCGAN による画像生成モデルの実装例 • 識別器の学習では、生成器で生成した画像と学習用画像をあわせて学習データを用意しま す。 生成器で画像を生成 学習用画像とあわせて 学習データを用意 識別器が強くなりすぎない ように、正解ラベルに

    ノイズを加える Low-level API で勾配降下法 を 1 回だけ適用 # Train discriminator random_inputs = tf.random.normal(shape=(batch_size, self.latent_dim)) fake_images = self.generator(random_inputs) combined_images = tf.concat([fake_images, real_images], axis=0) labels = tf.concat( [tf.zeros((batch_size, 1)), tf.ones((batch_size, 1))], axis=0) labels += 0.05 * tf.random.uniform(tf.shape(labels)) # Add random noise with tf.GradientTape() as tape: predictions = self.discriminator(combined_images) d_loss = self.loss_fn(labels, predictions) grads = tape.gradient(d_loss, self.discriminator.trainable_weights) self.d_optimizer.apply_gradients( zip(grads, self.discriminator.trainable_weights)) 132
  69. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part05/1. DCGAN MNIST example.ipynb ▪ DCGAN

    による画像生成モデルを MNIST データセットで学習します。 ▪ MNIST データセットをダウンロードする部分を次のように修正して、Fashin MNIST のデー タセットで学習した結果を確認してください。 from tensorflow.keras.datasets import fashion_mnist (train_images, train_labels), _ = fashion_mnist.load_data() train_images = np.pad(train_images, ((0, 0), (2, 2), (2, 2))) train_images = train_images.reshape( (len(train_images), 32*32)).astype('float32') / 255 136
  70. 「データ分布」の再現機能としての生成モデル • 32 × 32 ピクセルの画像データは、1,024 次元空間の点にマッピングできます。この際、 「整った画像」に対応するいくつかのクラスターができていると想像できます。 • VAE

    のデコーダは、潜在空間の原点付近の点を「整った画像」の点にマッピングします。こ れは、潜在空間上の「標準正規分布のデータ」を 1,024 次元空間内の「整った画像のクラ スター」に変換する機能と考えられます。 潜在空間 画像空間 138
  71. Diffusion モデルの元になるアイデア • 潜在空間を介さずに、画像データの空間(画像空間) 内で直接に、「ノイズ画像のクラスター」を「整った 画像のクラスター」に変換するモデルができれば、生 成モデルとして利用できます。 ◦ • 画像空間における標準正規分布のクラスター

    (各ピクセル値が標準正規分布で分布した画像 の集合)を「ノイズ画像のクラスター」と考え ます。 「整った画像」 のクラスター 「ノイズ画像」 のクラスター 「ノイズ画像」は簡単に用意できる一方、「整った画 像」は簡単には用意できない点がポイントになりま す。 画像空間のイメージ図 143
  72. Diffusion モデルの考え方 • 簡単のために画像空間を2次元と仮定して説明します。平面上の 1 つの点が 1 つの画像デー タに対応すると考えてください。 •

    学習用画像データの集合を標準正規分布のノイズ画像に変換する「Diffusion プロセス」を 用意して、これと逆向きの変換(再構成プロセス)を機械学習モデルで実現します。 Moons 分布 標準正規分布 Diffusion プロセス 再構成プロセス ノイズ画像の集合 学習用の画像の集合 144
  73. Diffusion プロセスの構成 • 個々の学習用の画像 に標準正規 分布のノイズ画像の 1 つ を割り 当てます。

    • 次式で、元の画像をノイズ画像に置 き換えていきます。 標準正規分布 ノイズ画像の集合 t = 0.0 t = 0.1 t = 0.2 Diffusion プロセス t = 0.3 ・・・ 学習用の画像の集合 t = 1.0 145
  74. ノイズ予測モデルの学習 オフセット・コサインスケジュール • Signal rate と Noise rate は、右図の「オフセット ・コサインスケジュール」がよく用いられます。

    • 各タイムステップで 測モデル」を学習します。 を用意して、「ノイズ予 ノイズ予測モデル t = 0.0 t = 0.1 t = 0.2 Diffusion プロセス t = 0.3 ・・・ を予測 t = 1.0 146
  75. 学習後のモデルによる画像生成の流れ • 一例として、Diffusion プロセスのステップを t = 0, 0.1, 0.2, …,

    1.0 の 10 ステップとします。 • ノイズ画像(t = 1.0)からノイズ成分を予測し て、オリジナル画像の方向に 1 ステップ分だ け修正します。 • 得られた画像(t = 0.9)から同様の予測を行 い、オリジナルデータの方向に 1 ステップ分 だけ修正します。 • これを 10 ステップ分繰り返して、オリジナ ル画像を近似的に再現します。 オリジナル t = 0.8 モデルの予測 t = 0.9 t = 1.0 予測の方向に 1 ステップ分修正 148
  76. Diffusion モデルの実装 — ノイズ予測モデル • ノイズ予測モデルは、ノイズを含むデータ を予測します(*)。 と Noise rate

    を元にして、ノイズ成分 ノイズ予測モデル ノイズを含む データ を予測 diffusion_model = models.Sequential(name='diffusion_model') ノイズを含むデータと diffusion_model.add(layers.Input(shape=(3,))) Noise rate を受け取る diffusion_model.add(layers.Dense(64, name='expand')) diffusion_model.add(layers.Dense(64, activation='relu', name='feedforward1')) diffusion_model.add(layers.Dense(64, activation='relu', name='feedforward2')) diffusion_model.add(layers.Dense(64, activation='relu', name='feedforward3')) diffusion_model.add(layers.Dense(64, activation='relu', name='feedforward4')) diffusion_model.add(layers.Dense(2, name='estimated_noise')) (*) t = 1 の最終状態でも SR = 0.02 なので、オリジナルデータの 痕跡はわずかに残っています。モデルは、t = 1 の場合でも、 このわずかな痕跡との違いを発見するように学習されます。 データに含まれる ノイズ成分を出力する 149
  77. Diffusion モデルの実装 — 学習データの集め方 • Diffusion プロセスを用意して、そこか らさまざまなタイムステップ t の三つ組

    を集めます。 • 学習画像とノイズ画像の対応づけは任 意なので、偏らないように複数の対応 付けによる、複数の Diffusion プロセス を用意します。 • それぞれの対応付けについて、複数の ランダムな t についてのデータを用意し ます。 Diffusion プロセスの用意 学習データ 標準正規分布 入力データ ノイズ画像の集合 学習用の画像の集合 三つ組データの用意 ノイズ予測モデル 正解ラベル を予測 150
  78. Diffusion モデルの実装 — 学習データの集め方 16 種類の対応付け でデータを用意 num_examples = len(train_data)

    inputs, labels = [], [] for _ in range(16): noise_data = np.random.multivariate_normal( [0, 0], [[1, 0], [0, 1]], size=num_examples) 標準正規分布 タイムステップを ランダムに選択 for _ in range(32): # select 32 random time steps 32 個のタイム t = np.random.uniform(0, 1, size=1) ステップを選択 noise_rate, signal_rate = diffusion_schedule(t) noisy_data = signal_rate * train_data + noise_rate * noise_data noise_rate_stack = np.vstack(np.array([noise_rate]*num_examples)) 入力データ inputs.append(np.hstack((noisy_data, noise_rate_stack))) labels.append(noise_data) # predict noise_data 正解ラベル inputs, labels = np.vstack(inputs), np.vstack(labels) 三つ組データの用意 noisy_data ノイズ予測モデル noise_rate nise_data を予測 151
  79. 学習後のモデルによるオリジナルデータの再現 • 一例として、Diffusion プロセスのステップを t = 0, 0.1, 0.2, …,

    1.0 の 10 ステップとしま す。t = 1.0 のノイズデータから t = 0.9 のデータを再現して、さらにこれから t = 0.8 のデータ を再現する、という処理を t = 0 まで繰り返します。 • 1 ステップ前のデータは次の様に計算します。 ◦ 現在のデータ(noisy_data)に対してモデルが予測したノイズ(noise_data)を用いて、オリジナ ルデータを次の計算で再現します。 estimated_original_data = (noisy_data - noise_rate * noise_data) / signal_rate ◦ 再現した(近似的な)オリジナルデータに対して、1 ステップ前のノイズを再度加えます。 noisy_data = signal_rate * estimated_original_data + noise_rate * noise_data 1 ステップ前の 予測データが得られる 1 ステップ前の値を使う 1 ステップ前の値を使う 152
  80. 学習後のモデルによるオリジナルデータの再現 • オリジナルデータを再現するコードの例です。 def reverse_diffusion(initial_data, steps=100): 指定したステップ数(steps)によって、ステップ値 t の取り得る denoise_history

    = [initial_data] num_examples = len(initial_data) 値が変わりますが、学習時は 0 < t < 1 の範囲のさまざまな t に対 step_size = 1 / steps するデータで学習しているので問題ありません。 noisy_data = initial_data for step in range(steps): # Estimate noise_data from the current noisy_data 現在のステップ値 t に対応する t = 1 - step * step_size Noise rate / Signal rate を取得 noise_rate, signal_rate = diffusion_schedule(t) 現在のデータと Noise rate noise_rate_stack = np.array([noise_rate]*num_examples).reshape(num_examples, 1) のペアからノイズを予測 input_data = np.hstack((noisy_data,noise_rate_stack)) noise_data = diffusion_model.predict(input_data, verbose=0) # Reconstruct the estimated original data estimated_original_data = (noisy_data - noise_rate * noise_data) / signal_rate # Get new noisy_data (1-step prior) next_noise_rate, next_signal_rate = diffusion_schedule(t-step_size) noisy_data = next_signal_rate * estimated_original_data + next_noise_rate * noise_data denoise_history.append(noisy_data) return denoise_history ノイズを取り除いた オリジナルデータを計算 1 つ前のステップ値に対応する Noise rate / Signal rate を取得して 1 つ前のステップのデータを再構成 153
  81. Diffusion モデルが優れている理由 • • DCGAN は、デコーダと識別器を並列に学習する必要があり、学習の進捗のバランスが重要 になります。 ◦ 識別器が弱すぎるとデコーダは整った画像を生成する必要がありません。 ◦

    識別器が強すぎるとデコーダは整った画像を生成する方法が発見できません。 ◦ 識別器に偏りがある(特定の特徴をもった画像を「本物」と判別する)とデコーダは特定の画像ば かり生成します。 変分オートエンコーダも内部的には2つのモデル(エンコーダとデコーダ)を学習してお り、類似の課題があります。 ◦ 最終的な潜在空間の状態がエンコーダの学習過程に依存しており、特に、誤差関数における KL ダ イバージェンスの重みによって、結果が大きく変わります。 ◦ 潜在空間の分布が標準正規分布からずれると、整った画像が再現されにくくなります。 156
  82. オートエンコーダの隠れた課題 学習データ • オートエンコーダから変分オートエンコーダに拡張する目的 の 1 つとして、「クラスター間の隙間を埋める」という説明 をしました。 • サンプラーでデコーダへの入力値を動かす事には、「学習

    データ間の隙間を埋める」という役割もありました。 ◦ 多くの場合、2 つの自然な画像の「中間画像」 は、自然な画像にはなりません。つまり、現実 世界の画像データは本質的に「飛び飛びに存在 する」という性質があります。 エンコーダ この隙間の部分を学習する データが存在しない 潜在空間 159
  83. VQ-VAE のアイデア • 「現実世界の画像データは飛び飛びに存在する」という課題を根本的に解決するアイデアと して、Vector Quantized VAE(VQ-VAE)があります。 • VQ-VAE では、潜在空間を「画像の各部位の役割を示すベクトル値の集合」に置き換えるこ

    とで、画像の特徴をダイレクトに表現します。 ◦ ◦ 「各部位の役割を示すベクトル値」は一定数の ベクトル値を事前に用意します。これを「コー ドブック」と呼びます。コードブックの個々の ベクトル値は、直感的には、「空」「雲」 「海」などの意味を表すと考えます。 コードブックの値のみを組み合わせることで、 「整った画像に対応する潜在空間の値」が構成 しやすくなります。 空 雲 海 島 空 空 空 雲 雲 海 海 海 海 海 海 船 船 島 島 海 海 海 島 島 海 海 島 島 島 ・・・ コードブック 1 つの画像に対応した 「潜在空間の値」のイメージ 160
  84. VQ-VAE による学習例 • 入力画像:128 × 128 ピクセルのカラー画像 ◦ 画像は 256

    階調(RGB の各レイヤーのピクセル値は 256 = 28 通りの値を取る)なので、1 つの画 像の情報量は 128 × 128 × 3 × 8 ビット • コードブック:512 個の 1 次元ベクトル(つまり、512 = 29 種類の離散値) • 潜在空間のサイズ:32 × 32 ピクセル ◦ 潜在空間の情報量は 32 × 32 × 9 ビットなので、画像の情報量は約 1/40 に削減 デコーダからの出力 エンコーダへの入力 https://arxiv.org/abs/1711.00937 162
  85. 演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part05/2. VAE distribution conversion example.ipynb ▪

    変分オートエンコーダでデータ分布の変換モデルを実装した例です。 ▪ 誤差関数における KL ダイバージェンスの重みを変化させて、結果がどのように変わるか観 察してください。 def custom_loss(y_true, y_pred): mean, log_var, pred = y_pred[:, 0:2], y_pred[:, 2:4], y_pred[:, 4:6] reconstruction_loss = losses.mse(y_true, pred) kl_loss = tf.reduce_mean(tf.reduce_sum( -0.5 * (1 + log_var - tf.square(mean) - tf.exp(log_var)), axis=1)) loss = reconstruction_loss + 0.8 * kl_loss return loss KL ダイバージェンスの重み ◦ Part05/3. Diffusion model example.ipynb ▪ 2 次元空間のデータに Diffusion モデルを適用した例です。 163
  86. CLIP による埋め込みベクトルの生成 • 実際にこれを実現するには、テキストエンコーダは、入力テキストから「画像の種類を表す 情報」を抽出する様に学習しておく必要があります。 ◦ • これにより、入力テキストの文面が学習に使ったテキストと異なっていても、「画像の 種類」として同じ意味内容であれば、類似の画像が生成できるようになります。 これを実現するモデルの一例として、CLIP

    がありま す。「画像とその説明文」のペアを学習データとして 用意しておき、画像とテキストを個別にエンコーダで 変換して、得られる埋め込みベクトルの類似度が大き くなるようにエンコーダを学習します。 ◦ 類似した画像は類似した埋め込みベクトルにな るので、対応するテキストの埋め込みベクトル も類似したものになります。 https://github.com/openai/CLIP 166
  87. CLIP による埋め込みベクトルの特徴 • 画像 A と画像 B が画像データとして似ていると埋め込みベクトルは似ている ◦ •

    学習の結果、画像とペアになるテキストの埋め込みベクルは似ている ◦ • これらの結果、類似画像のテキストは埋め込みベクトルが似ている ◦ 167
  88. Diffusion モデルとテキストエンコーダの組み合わせ • Diffusion モデルにテキストエンコーダからの入力を組み合わせることで、自然言語テキス トから画像を生成するさまざまなモデルが提案されており、一例として、次の様なバリエー ションがあります。 ◦ テキストエンコーダを使わずにテキスト(トークン ID

    の列)をそのまま入力 ◦ CLIP のテキストエンコーダからの出力(テキストの埋め込みベクトル I)を利用する ◦ CLIP のテキストエンコーダからの出力をさらにイメージエンコーダ側の出力(対応す る画像の埋め込みベクトル T)に置き換えるモデルを追加 ▪ なので、 よりも の方が類似度が高い 168
  89. Stable Diffusion について • オリジナルの Diffusion モデルは、高次元の画像空間で直接に「整った画像」と「ノイズ画 像」の分布を変換しました。 • Stable

    Diffusion の Latent Diffusion Model(LDM)は、潜在空間の中で、「乱数で分布 した集合」と「整った画像に対応する点の集合」を変換します。 ◦ • 潜在空間は、オートエンコーダで事前に用 意しておきます。LDM で得られた「整った 画像に対応する点」をデコーダに入力して 実際の画像を出力します。 さらに、「画像の種類を表す埋め込みベクトル」 を入力することで、生成する画像を操作できるよ うに設計されています。 https://arxiv.org/abs/2112.10752 171
  90. PaLI のアーキテクチャー • PaLI は、テキストと画像のペアからテキストを出力するモデルです。 • テキストは通常の Transformer エンコーダに入力します。画像は Vision

    Transformer で 「画像の意味を表す埋め込みベクトル」に変換した後に、テキスト用の Transoformer エン コーダに入力します。 ◦ • これでテキストと画像を組み合わせた表現の「意味」が抽出できると期待します。 後段に Transformer デコーダを接続して、その意味に基づいた出力テキストを生成しま す。 https://arxiv.org/pdf/2209.06794.pdf 172