Share
Embed
生成 AI の基礎 〜 サンプル実装で学ぶ基本原理
Etsuji Nakai
August 09, 2026
Transcript
175 slides
-
生成モデルの基礎 〜 サンプル実装で学ぶ基本原理 中井悦司 / Etsuji Nakai 2026/06/06 ver3.3 Copyright
(C) 2026 National Institute of Informatics, All rights reserved. -
目次 第1部:前提知識 第3部:自然言語モデルの基礎 ◦ Keras による分類モデルの実装 ◦ LSTM によるテキスト分類 ◦
多層ニューラルネットワーク ◦ LSTM によるテキスト生成 ◦ 畳み込みフィルターと転置畳み込みフィ ルター 第2部:画像生成モデルの基礎 ◦ オートエンコーダと潜在空間 ◦ 変分オートエンコーダ ◦ マルチタスク学習 ◦ 条件付き変分オートエンコーダ 第4部:より高性能な生成モデル ◦ Transformer モデル ◦ DCGAN ◦ Diffusion モデル ◦ VQ-VAE ◦ マルチモーダル生成モデル -
サンプルコードの入手について • 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 -
GPU ランタイムを使用する際の注意 • 各ノートブックの先頭部分に、使用するランタイムの種類(GPU を使用するかどうか)が記載されてい ます。 • Google Colaboratory を無償枠で使用している場合、GPU
ランタイムの使用時間には制限があり、長時 間使用を続けると、一定期間(半日〜1日程度)GPU ランタイムが使用できなくなることがあります。 ◦ GPU ランタイムを使用するノートブックは、実行が終わったらすぐに、画面上部のメニューから 「ランタイム」→「ランタイムを接続解除して削除」を選択して、ランタイムを停止しておくこと をお勧めします。 5 -
線形多項分類器 • 各ラベルに対する「可能性の高さ」を 1 次関数で計算します。 ◦ 例えば、28 × 28 (=
784) ピクセルの画像データであれば、1 つの画像は 784 個の実数 値からなるデータと見なせるので、784 変数の 1 次関数を使用します。 ◦ ラベルごとに異なる 1 次関数を用意して、それぞれのラベルに対する計算値を得ます。 MNIST の画像データを 10 種類に分類する場合 それぞれの画像に対して 10 個の出力値が得られる ・・・ 9 -
分類モデルの誤差関数 • n 番目のデータ の正解ラベルを次の One-hot 表現で与えます。(正解を k として、k 番
目の要素 のみが 1 になっている。) • 今のモデルで正解 k に対する確率は します。 ※ One-hot 表現の正解ラベルを用いると なので、これが大きくなるようにモデルを学習 は次式で表すことができます。 11 -
Keras による線形多項分類器の実装例 • 「784 個の値を受取る 1 次関数」を10 個用意した後に、それらにソフトマックス関数を適 用することで、前述の分類モデルが構成できます。 model
= models.Sequential(name='linear_model') model.add(layers.Input(shape=(784,))) model.add(layers.Dense(10, activation='softmax', name='softmax')) 13 -
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 -
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 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part01/1. Linear model MNIST classification.ipynb ▪
線形多項分類器を Keras で実装して、MNIST データセットの分類を行います。 16 -
多層ニューラルネットワーク • 入力データをそのまま線形多項分類器に入力するのではなく、事前に多層ニューラルネット ワークを介して、分類に有用な情報を抽出します。 • 1 つのノードは、「1 次関数」+「活性化関数」を表します。 ◦ 活性化関数には、ReLU
がよく用いられます。 ・・・ ・・・ ・・・ ・・・ 全結合層 線形多項分類器 18 -
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 -
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 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
畳み込みフィルターによる特徴量の抽出 定数 b (< 0) を加えて ReLU を適用した結果 • 実際の畳み込みフィルターでは、フィルター
適用後に「定数を加えて、活性化関数を適用 する」という追加処理を行います。 • 下図は、フィルター適用後に定数 -0.2 を加え て、ReLU を適用しています。(フィルター適 用後にピクセル値が 0.2 以下の部分は強制的 に 0 に変換される) 25 -
畳み込みフィルターの動的な学習 • 一般の画像データの場合、どのようなフィルターが適切なのかは、すぐには分からないの で、フィルターも学習対象のパラメーターとして、適切なフィルターそのものを学習させま す。 • 下記のモデルでは、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 -
学習後のフィルターの様子 畳み込みフィルターの動的な学習 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 -
複数レイヤーに対する畳み込みフィルター適用の仕組み • 1 つのフィルターは、各レイヤー に適用するサブフィルターを内部 的に保持しており、各サブフィル ターの出力を合成したものが出力 画像になります。 • RGB
画像ファイルを入力しても、 その出力は RGB 画像ファイルに なるわけではありません。 29 -
2 層目のフィルターの適用方法 • 1 層目のフィルターから得られ た n 枚の画像データを「n 個の レイヤーからなる画像」とし
て、2層目のフィルターを適用 します。 • 右図は 1 層目に 32 個、2 層目 に 64 個のフィルターを用意し た例です。2 層目の 64 個の フィルターは、それぞれ内部的 に 32 個のサブフィルターを持 ちます。 30 -
転置畳み込みフィルターの仕組み • 元画像にストライドの隙間を空けてから畳み込みフィルターを適用することで、画像サイズ を拡大します。 • 複数レイヤーの画像については、レイヤーごとのサブフィルターで処理した結果を合成した 1 レイヤーの画像を生成します。 ストライド (2,
2) で 3 × 3 ピクセルの画像を 6 × 6 ピクセルに拡大する例 https://github.com/vdumoulin/conv_arithmetic 33 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
オートエンコーダ • エンコーダとデコーダを接続して、 同じデータを再現する様に学習した モデルをオートエンコーダと言いま す。 • エンコーダとデコーダの接続部分の 低次元空間を「潜在空間」、もしく は、「埋め込み空間」と言います。
• 潜在空間には、画像の「種類」を表 す抽象化された情報が埋め込まれて いると考えることができます。 潜在空間 37 -
オートエンコーダの学習例 32 × 32 × 1 • 32 × 32
ピクセルの画像を 2 次元の潜 在空間に埋め込むオートエンコーダを 定義します。 • つまり、1 つの画像は、2 次元平面の 1 つの点に対応づけられます。 • エンコーダ部分は畳み込みフィル ター、デコーダ部分は転置畳み込み フィルターを使用します。 16 × 16 × 32 8 × 8 × 64 4 × 4 × 128 2 次元の 潜在空間 38 -
オートエンコーダの実装例(エンコーダ) • エンコーダは の 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 -
オートエンコーダの実装例(デコーダ) • エンコーダが出力した潜在空間の点から画像を生成します。 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 -
オートエンコーダの実装例(学習用モデル) • エンコーダとデコーダを結合したモデルを定義して、学習処理を行います。 • 学習が終わった後は、エンコーダとデコーダは個別に使用することができます。 model = models.Model(inputs=encoder.inputs, outputs=decoder(encoder(encoder.inputs)), name='CNN_AutoEncoder')
エンコーダとデコーダを 結合した学習用モデル model.compile(optimizer='adam', loss='mse') 正解ラベル エンコーダ デコーダ 誤差関数 41 -
オートエンコーダの学習例 • Fashion MNIST の画像データで学習した結果、 潜在空間には、10 種類の画像に対応したクラス ターができていることが分かります。 • デコーダを使用すると、潜在空間の点から新し
い画像を生成することができます。 学習データに対する エンコーダの出力値 42 -
オートエンコーダの本質的な課題 学習データ • 現実世界の学習データ(整った画像)は、潜在空間上 でまばらに存在します。 • 2 つの学習データの「隙間」部分からは、2 つの画像 の中間状態の画像が生成されると期待されますが、こ
のような画像は人間の目から見て「自然な画像」とは 限りません。 • エンコーダ 「飛び飛びに存在する現実世界のデータ」を連続的な 潜在空間にマッピングするという点にオートエンコー ダーの本質的な課題があります。 この隙間の部分を学習する データが存在しない 潜在空間 44 -
オートエンコーダを改善するアイデア • オートエンコーダを学習する際に、エンコーダによって学習データから得られた点をそのま まデコーダに入れるのではなく、乱数で一定範囲に広げてからデコーダに入力します。 • これにより、デコーダは、潜在空間のより広い範囲において、学習データに近い「整った画 像」を再現するように学習されます。 潜在空間 エンコーダ エンコーダから得られた点を乱数で
移動してからデコーダに入力する デコーダ デコーダは、広がった範囲 の点に対して学習データを 再現する様に学習される 45 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ Part02/1. AutoEncoder MNIST example.ipynb ▪ フィードフォワード・ネットワークによるオートエンコーダのモデルを
MNIST データセット で学習します。 ▪ ◦ さまざまな手書き文字を入力して、どのような出力結果になるか観察してください。 Part02/2. AutoEncoder Fashion MNIST.ipynb ▪ 畳み込みフィルターと転置畳み込みフィルターによるオートエン コーダのモデルを Fashion MNIST データセットで学習します。 46 -
正規分布について • 平均 μ 、分散 σ2(標準偏差 σ)の正規分布に従う乱数を • 特に ◦
の場合 で表します。 を「標準正規分布」と言います。 標準正規分布の乱数は、次の式で、正規分布 に変換できます。 2次元の 正規分布 正規分布 48 -
変分オートエンコーダの仕組み • 2 次元の潜在空間を用いるものとして、次の手続きを実行します。 ◦ エンコーダは、入力画像に対して、正規分布の平均 と各軸方向の標準偏差 を出力します。 ◦ 得られた正規分布からサンプリングした値をデコーダに入力します。
◦ デコーダの出力画像が入力画像に近づくようにモデルのパラメーターを更新します。 ◦ エンコーダが出力する正規分布が標準正規分布か 潜在空間 ら大きく外れない様に、誤差関数に罰則項を追加 します。 • この範囲の乱数で得られた 値をデコーダに入力する これらにより、潜在空間の原点の近くに「整った画像 が得られる点」が集まるものと期待できます。 49 -
変分オートエンコーダの実装例(エンコーダ) • エンコーダは ◦ 次式で の 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 -
変分オートエンコーダの実装例(サンプラー) • サンプラーは次の関係を利用して、平均 、各軸方向の標準偏差 布から、デコーダに入力する値をランダムに選択します。 の正規分 標準正規分布 標準正規分布の乱数を 目的の正規分布に変換 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 -
変分オートエンコーダの実装例(デコーダ) • デコーダは、サンプラーが選択した潜在空間の点から画像を生成 します。(オートエンコーダと同じコードです。) 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 -
変分オートエンコーダの実装例(誤差関数) • 「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 -
変分オートエンコーダの実装例 • 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 -
ラベルデータの利用 • 変分オートエンコーダは、ラベルデータを使わず に、画像の特徴だけから、潜在空間に画像の種類 ごとのクラスターが生成できました。 • ラベルデータを明示的に使用する事で、次の様な 応用が可能になります。 ◦ マルチタスク学習
⇨ ラベルごとのより明確なクラスターを生成 ◦ 条件付き変分オートエンコーダ ⇨ ラベル指定で特定の画像を生成 58 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
分類モデルとの同時学習 • 潜在空間の値からラベルを予測する分類モデルを同時に学習します。 • 分類モデルの立場からは、潜在空間の中でラベルごとにデータが分離されていることが重要 なので、ラベルごとのより明確なクラスターが得られると期待できます。 デコーダ 潜在空間 画像を比較 ラベル付き
画像データ 画像データ サンプラー ラベル:2 × エンコーダ ラベルを比較 分類モデル 61 -
マルチタスク学習の実装例 • エンコーダ、サンプラー、デコーダは変分オートエンコーダと同じものを再利用します。 • サンプラーの出力を受け取って、ラベルを予測する線形多項分類器を追加で用意します。 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 -
マルチタスク学習の実装例 • 学習時の正解ラベルとして使用するため、画像データにラベル値を付与したデータを用意し ます。各画像データの末尾に(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 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
ラベル付き画像データの準備 • 画像データの直後に 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 -
条件付き変分オートエンコーダの実装例 • デコーダには、サンプラー の出力値に加えて、ラベル 値を入力するようにモデル を構成します。 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 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
リカレント・ニューラルネットワークの仕組み • 時系列データに対して、同じ構造の「セル」を繰り返し適用することで、各時刻における出 力データを得る形式のモデルをリカレント・ニューラルネットワーク(RNN)と言います。 • RNN のセルは、出力値と同時に「隠れ状態」の値 h を出力します。この値は、次の時刻の データを処理する際に利用されます。隠れ状態には、これまでの処理結果の情報が埋め込ま
れているものと期待されます。 出力値 パラメーターの値は すべてのセルで共通 隠れ状態 入力値 セル セル t=1 t=2 ・・・ セル t=T 78 -
リカレント・ニューラルネットワークの仕組み • 図は、最もシンプルな RNN の例です。隠れ状態と入力値をハイパボリック・タンジェント を活性化関数とする全結合層に入力して、得られた結果を新しい隠れ状態、および、出力値 とします。 • 直前の出力値を新しい隠れ状態とするため、長期的な情報の保持が困難になります。(次の セルは直前の出力のみを参照します。)
◦ 数学的には、ハイパボリック・タンジェントが繰り返し適用される結果、勾配消失が発生して学習 が進まなくなります。 ハイパボリック・タンジェントのグラフ 79 -
LSTM の仕組み • LSTM のセルは、「隠れ状態 h」と「セル状態 c」の 2 種類の情報を伝達します。隠れ状態 は、直前の出力値に一致します。
◦ • 隠れ状態とセル状態は同じ次元のベクトル値で、具体的な次元数はハイパーパラメーターとして利 用者が指定します。 大雑把には、次の様に理解する事ができます。 ◦ セル状態に、これまでの処理結果の情報が保存さ れます。 ◦ 隠れ状態と新しい入力値を用いて、セル状態の情 報を更新します。(不要な情報を削除して、新し い情報を追加します。) ◦ 更新後のセル状態の情報を用いて、「隠れ状態+ 入力値」から出力値を生成します。 ◦ 隠れ状態は短期的な(=直前の)情報、セル状態 は長期的な情報を伝達すると考えられます。 長期的な 情報 不要な情報 を削除 「直前+現在」 の情報 新しい情報 を追加 更新されたセル状態 から出力値を生成 80 -
LSTM の仕組み • シグモイド関数を除いてみると、LSTM の基本的な情報の流れが理解できます。 ◦ 2 つの tanh は、入力情報・出力情報に対する活性化関数として機能します。
◦ 3 つのシグモイド関数は、削除・追加・出力する情報を選定するゲートになります。 長期的な 情報 新しい情報 を追加 不要な情報 を削除 新しい情報 を追加 削除する情報を 選定するゲート 「直前+現在」 の情報 更新されたセル状態 から出力値を生成 追加する情報を 選定するゲート 出力する情報を 選定するゲート 81 -
ゲート機構の仕組み • ゲート機構は、次の計算処理を行います。 ◦ ◦ で c の各成分の「重要度」(0 〜 1
の値)を計算します。 c の各成分に対応する重要度を個別に掛けます。これにより、重要度の低い成分は値が小さくなり ます。 c と同じ次元のベクトル x に基づいて c から 残すべき情報を選定 82 -
LSTM の利用方法 • 時系列データ全体から 1 つの予測値が必要な場合は、最後のセル の出力値のみを利用します。一方、各時刻に対する予測値が必要 な場合は、すべてのセルの出力値を利用します。 • 複数の
LSTM を多段に重ねて利用することもできます。 出力値 多項分類器 テキスト分類を行う例 LSTM レイヤー2 セル B セル B ・・・ セル B LSTM レイヤー1 セル A セル A ・・・ セル A t=1 t=2 入力値 t=T 83 -
双方向 LSTM について • 予測内容によっては、時系列データを必ずしも時間順に処理する必要はありません。このよ うな場合、正方向と逆方向の処理を組み合わせた、双方向 LSTM も使用できます。 出力値 それぞれのセルからの
出力を結合したものを 出力値とする セル B セル B セル A 入力値 t=1 セル A t=2 セル B ・・・ ・・・ セル A t=T 84 -
双方向 LSTM によるテキスト分類 • 双方向 LSTM でテキスト分類を行う場合、各方向の最後のセルの出力を結合して、これを多 項分類器に入力します。 最後のセルからの 出力を結合したものを
出力値とする 多項分類器 セル B セル B セル A 入力値 t=1 セル A t=2 セル B ・・・ ・・・ セル A t=T 85 -
テキストのトークン化について • テキストデータを扱う場合、あらかじめ、それぞれの単語を対応する整数値(トークン 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 -
トークンの埋め込み表現について • テキストデータをニューラルネットワークで処理する場合、最初の「埋め込み層」でそれぞ れのトークン ID を高次元空間の埋め込みベクトルに変換します。 ◦ トークン ID とベクトル値は、単純に
1 対 1 で対応させます。この際の「対応表」が埋 め込み層のウェイト(チューニング対象のパラメータ)になります。 ◦ モデルの学習結果として、意味が近い単語は、埋め込みベクトルも類似した値になるも のと期待できます。 埋め込みベクトルの次元 ID = 1 に対応するベクトル 埋め込み層のウェイト ID = 2 に対応するベクトル トークン ID の値の範囲 ID = 3 に対応するベクトル ・・・ 87 -
双方向 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 -
(参考)ドロップアウト層について • ドロップアウト層は、モデルの学習時に、2つのレイヤー間の接続の一部をランダムに切断 します。 ◦ • 例えば、512 次元のベクトル値(= 512 個の実数値)を受け渡した際に、40%
の値が 強制的に 0 に書き換えられます。 ドロップアウト層の直後のレイヤーは、一部の情報が欠落した状態で予測をする必要がある ため、学習データに過度に依存することなく、汎化性能が維持できると期待されます。 89 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
RNN による「次の単語」の予測 • RNN で「次の単語」を予測する場合、トークン ID の値をカテゴリーラベルとして、多項分 類器でトークン ID ごとの確率値を出力します。
• 各セルは、入力データに対して、「自身が受け取るトークンよりも前にある情報」を隠れ状 態から受け取って利用して予測します。 t = 2 の単語 t = 3 の単語 を予測 出力値 出力値 確率 トークン ID すべてのトークン ID に ついて確率値を出力 を予測 入力値 多項分類器 多項分類器 セル セル t=1 t=2 多項分類器 ・・・ セル t=T 93 -
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 -
RNN によるテキスト生成 • 学習後のモデルにテキストの冒頭部分を入力すると、最後の出力から次の単語の確率分布が 得られます。ここから単語をサンプリングして、末尾に付け加える作業を繰り返します。 多項分類器 多項分類器 多項分類器 セル セル
セル t=1 t=2 t=3 Recipe for spicy 確率分布に従って単語をサンプ リングして、末尾に付け加える 95 -
温度パラメーターについて • モデルが出力した確率分布を次の様に変換 してからサンプリングすることで、生成さ れるテキストの変化を調整できます。 • 温度パラメーター T が小さいほど実行ごと の変動が小さくなりますが、同じ文章が繰
り返される問題が発生しやすくなります。 温度パラメーター T による分布の変化 96 -
LSTM によるテキスト生成モデルの学習結果 トークン数 N • 料理レシピのデータセットでテキスト生成モデルを学習す る例がノートブックに用意されています。実際に生成され るテキストの例は、ノートブックで確認してください。 [101, 1045,
2134, ..., 0] ・・・ [N] 埋め込み層 学習データの例 [N, 512] ・・・ LSTM [N, 512] ・・・ 線形多項分類器 [N, 30522] ・・・ https://huggingface.co/datasets/Shengtao/recipe 97 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
Transformer モデルの考え方 • • テキストにおける単語の「意味」は、まわりにある単語との関係で変化します。例えば、次 の 2 つのテキストの「flies」の品詞・意味は、最後にある単語(arrow / banana)を見て
初めて決定する事ができます。 ◦ Time flies like an arrow.(時間は矢のように飛ぶ) ◦ Time flies like a banana.(「時間バエ」はバナナを好む) 一般に、トークンを埋め込み空間のベクトルに変換した場合、「意味が近いトークンはベク トル値も近くなる」と期待されますが、テキストに依存しない一般的な「意味」と、特定の テキストに置かれた場合の「意味」は異なります。 101 -
Transformer モデルの考え方 • そこで、テキスト内のトークンの関係を見て、埋め込みベクトルの値を「そのテキストにお ける意味」にあう様に更新する処理(Attention レイヤー)を実装します。 • 画像データにおける畳み込みフィルターは、あるピクセルと周囲のピクセルの関係から、画 像の特徴を抽出しましたが、これに類似した処理と考えられます。 このテキストに
応じた意味を表現 Attention レイヤー テキストに依存しない 一般的な意味を表現 埋め込みベクトル time flies like a banana 102 -
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 -
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 -
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 -
(参考)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 -
Transformer エンコーダの構造 • Multi-Head Attention の後段に、「レイヤー正規化+全 結合層+レイヤー正規化」を繋げたものが 「Transformer エンコーダ」です。 レイヤー正規化
全結合層 • ◦ レイヤー正規化と全結合層もトークンの位置に依存しない、 すべてのトークンに共通の計算処理を適用します。 ◦ 過学習を防止するために、必要に応じてドロップアウト層を 追加します。 一般には、複数の Transormer エンコーダを多段に積み 重ねて利用します。 レイヤー正規化 マルチヘッド・アテンション スキップ接続 Transformer エンコーダ 107 -
(参考)スキップ接続について • レイヤーに対して、入力 x に対する「変化分」を計算させたい場合に使用します。 • 学習の初期においても(ウェイトを小さい値で初期化すれば)、出力 y が入力 x
に近い値に なるため、後続のレイヤーに意味のある情報を伝えることができて、学習がスムーズに進む と期待できます。 出力 y このレイヤーは x と y の 差分 y - x を計算すればよい スキップ接続 入力 x 108 -
(参考)レイヤー正規化について • 埋め込みベクトルを として、すべての成分についての平均と分散が 0, 1 になるように正規化した上で、成分ごとにスケーリング(定数倍して定数値を加える) します。 ◦ スケーリングの定数
は学習対象のパラメータです。これは、成分ごとに適したス ケールがあるという想定に基づいた仕組みです。 トークンごとに 個別に正規化 σ ≒ 0 の時にエラーにならない ように小さな値を加える レイヤー正規化 埋め込みベクトル 109 -
アテンションマスクについて • 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 -
Positional エンコーディングについて • ここまでの実装では、「テキスト内でのトークンの位置」を意識した処理が含まれていませ ん。トークン同士の関係は Query と Key の内積値だけで決まるので、図のようにトークン の位置を入れ替えても出力値は変わりません。
出力値 <A> <B> <C> 出力値 <D> <E> アテンションヘッド time flies like a banana <E> <B> <C> <D> <A> アテンションヘッド banana flies like a time 111 -
Positional エンコーディングについて • 実際には、トークンの位置関係も各トークンの「意味」に影響するので、何らかの方法で位 置情報を組み込む必要があります。 • そこで、最初の埋め込み層で、各トークンを埋め込みベクトルに変換した後、トークンの位 置情報を埋め込みベクトルに変換したものを加えます。 トークンと位置情報の 埋め込みベクトルを合計
トークン用の埋め込み層 位置情報用の埋め込み層 time flies 1 like 2 a 3 banana 4 5 112 -
Transformer モデルによるテキスト分類 • • • Transformer モデルでテキスト分類を 実装する場合、最初のトークンに対す る出力値を多項分類器に入力します。 多項分類器
最初のトークンの埋め込みベクトル に、分類に必要な情報が集約するよう にモデル全体が学習されるものと期待 します。 アテンションマスクによる参照制限は 行いません。(固定長リストにするた めの末尾の Padding 部分は参照しない ようにアテンションマスクを設定しま す。) 入力データ Transformer エンコーダ Transformer エンコーダ Positional エンコーディング t=1 t=2 ・・・ t=T 113 -
(参考)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 -
Transformer モデルによるテキスト分類 • 下図は、1 層目と 2 層目の Transformer エンコーダの Attention
Heads が計算したトーク ン間の Weights(4 つの Attention Heads の最大値を 2 レイヤーで平均)の例です。 115 -
Transformer モデルによるテキスト生成 • • • RNN による実装と同様に、各トークン について「次の単語」の確率分布を予 測するようにモデルを学習します。 学習後のモデルにテキストの冒頭部分
を入力すると、最後の出力から次の単 語の確率分布が得らるので、ここから 単語をサンプリングして、末尾に付け 加える作業を繰り返します。 学習時はアテンションマスクにより、 「自分より後ろのトークン」は参照し ないように制限します。 入力データ すべてのトークン ID に ついて確率値を出力 多項分類器 多項分類器 多項分類器 Transformer エンコーダ Positional エンコーディング t=1 t=2 ・・・ t=T 117 -
Transformer モデルによるテキスト生成 • 下図は、Transformer エンコーダの Attention Heads が計算したトークン間の Weights (4つの
Attention Heads の平均値)の例です。 118 -
(参考)Transformer を用いた LLM の例 • 2022 年に論文公開されたモデル(PaLM)では、モデルのサイズに関連した下記の値が 公開されています。 ◦ モデルに入力可能なトークン数は
2048 です。 Transformer エンコーダの段数 Attention Head の個数 埋め込みベクトルの次元 Attention Head のサイズ https://arxiv.org/abs/2204.02311 119 -
Transformer のエンコーダとデコーダの違い • Attention 機構の論文では、原文の意味を抽出する エンコーダと、原文の意味を補助情報として訳文を 生成するデコーダを組み合わせた、翻訳モデルが提 案されました。 • その後、エンコーダだけを用いた
LLM として、 PaLM が開発されました。 https://arxiv.org/abs/1706.03762 121 -
(参考)Vision Transformer による画像の意味抽出 • Vision Transformer (ViT) は、画像データに Transformer を適用するモデルです。
• 画像を小さなパッチの列に分解して、モデルに入力します。 • それぞれのパッチは、最初のレイヤー で、対応する「埋め込みベクトル」に変 換されます。 • その後は、LLM と同様の Transformer エンコーダで処理を行います。 ◦ 複数のパッチの位置関係から「画像 の意味」を表す情報が得られると期 待されます。 https://arxiv.org/abs/2010.11929 122 -
(参考)Vision Transformer による画像の意味抽出 • 一例として、後段に分類モデルを接続して、画像 を分類するモデルを作ると、分類に有用な情報を 持つ部分に Attention の Weight
が掛かると期 待されます。 https://arxiv.org/abs/2010.11929 123 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
DCGAN の仕組み • DCGAN では、エンコーダを用いずに、デコーダ部分だけを直接に学習します。 • デコーダが生成した画像と学習用の画像を識別するモデルを別途用意しておき、これらを交 互に学習することで、互いの性能を高めていきます。 • デコーダと識別モデルをそれぞれ「生成器」「識別器」と呼びます。
識別モデル (識別器) 本物?偽物? デコーダ (生成器) 学習データ 127 -
DCGAN の仕組み 潜在空間からランダムに 入力値をサンプリング 識別モデルが出力する P(A) が大きくなる方向に パラメーターを修正 生成器 画像セット
A P(A) : A が本物である確率 識別器 学習データ B P(B) : B が本物である確率 P(A) が小さく、P(B) が大きくなる 方向にパラメーターを修正 128 -
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 -
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 -
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 -
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 -
学習後の潜在空間の様子 • DCGAN の論文では、学習後の潜在空間には、画像の種類を表現する「方向」が存在するこ とが指摘されています。 Smile Smiling Woman Smiling Man
Woman Man Neutral Woman Neutral Man Neutral http://arxiv.org/abs/1511.06434 135 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
「データ分布」の再現機能としての生成モデル • 32 × 32 ピクセルの画像データは、1,024 次元空間の点にマッピングできます。この際、 「整った画像」に対応するいくつかのクラスターができていると想像できます。 • VAE
のデコーダは、潜在空間の原点付近の点を「整った画像」の点にマッピングします。こ れは、潜在空間上の「標準正規分布のデータ」を 1,024 次元空間内の「整った画像のクラ スター」に変換する機能と考えられます。 潜在空間 画像空間 138 -
「データ分布」の再現機能としての生成モデル • 右図のように、Moons 分布の各点を学習 データとして、変分オートエンコーダを 学習します。 • エンコーダは、学習データ全体を潜在空 間上の標準正規分布に近くなるように マッピングします。
• その結果、デコーダは、潜在空間の標準 正規分布を元の Moons 分布に変換すると 期待されます。 Moons 分布 エンコーダ デコーダ サンプラー 潜在空間 140 -
Diffusion モデルの元になるアイデア • 潜在空間を介さずに、画像データの空間(画像空間) 内で直接に、「ノイズ画像のクラスター」を「整った 画像のクラスター」に変換するモデルができれば、生 成モデルとして利用できます。 ◦ • 画像空間における標準正規分布のクラスター
(各ピクセル値が標準正規分布で分布した画像 の集合)を「ノイズ画像のクラスター」と考え ます。 「整った画像」 のクラスター 「ノイズ画像」 のクラスター 「ノイズ画像」は簡単に用意できる一方、「整った画 像」は簡単には用意できない点がポイントになりま す。 画像空間のイメージ図 143 -
Diffusion モデルの考え方 • 簡単のために画像空間を2次元と仮定して説明します。平面上の 1 つの点が 1 つの画像デー タに対応すると考えてください。 •
学習用画像データの集合を標準正規分布のノイズ画像に変換する「Diffusion プロセス」を 用意して、これと逆向きの変換(再構成プロセス)を機械学習モデルで実現します。 Moons 分布 標準正規分布 Diffusion プロセス 再構成プロセス ノイズ画像の集合 学習用の画像の集合 144 -
Diffusion プロセスの構成 • 個々の学習用の画像 に標準正規 分布のノイズ画像の 1 つ を割り 当てます。
• 次式で、元の画像をノイズ画像に置 き換えていきます。 標準正規分布 ノイズ画像の集合 t = 0.0 t = 0.1 t = 0.2 Diffusion プロセス t = 0.3 ・・・ 学習用の画像の集合 t = 1.0 145 -
ノイズ予測モデルの学習 オフセット・コサインスケジュール • Signal rate と Noise rate は、右図の「オフセット ・コサインスケジュール」がよく用いられます。
• 各タイムステップで 測モデル」を学習します。 を用意して、「ノイズ予 ノイズ予測モデル t = 0.0 t = 0.1 t = 0.2 Diffusion プロセス t = 0.3 ・・・ を予測 t = 1.0 146 -
ノイズ予測モデルによる逆変換 • ノイズ予測モデルによる予測結果 す。 を用いると、次式で元の画像の予測 が得られま • タイムステップが最終時刻に近いほど、予測誤差 ε の影響が大きくなります。
• そこで、さまざまなタイムステップの予測を組み合わせることで、より正確な予測を行いま す。 147 -
学習後のモデルによる画像生成の流れ • 一例として、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 -
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 -
Diffusion モデルの実装 — 学習データの集め方 • Diffusion プロセスを用意して、そこか らさまざまなタイムステップ t の三つ組
を集めます。 • 学習画像とノイズ画像の対応づけは任 意なので、偏らないように複数の対応 付けによる、複数の Diffusion プロセス を用意します。 • それぞれの対応付けについて、複数の ランダムな t についてのデータを用意し ます。 Diffusion プロセスの用意 学習データ 標準正規分布 入力データ ノイズ画像の集合 学習用の画像の集合 三つ組データの用意 ノイズ予測モデル 正解ラベル を予測 150 -
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 -
学習後のモデルによるオリジナルデータの再現 • 一例として、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 -
学習後のモデルによるオリジナルデータの再現 • オリジナルデータを再現するコードの例です。 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 -
Diffusion モデルが優れている理由 • • DCGAN は、デコーダと識別器を並列に学習する必要があり、学習の進捗のバランスが重要 になります。 ◦ 識別器が弱すぎるとデコーダは整った画像を生成する必要がありません。 ◦
識別器が強すぎるとデコーダは整った画像を生成する方法が発見できません。 ◦ 識別器に偏りがある(特定の特徴をもった画像を「本物」と判別する)とデコーダは特定の画像ば かり生成します。 変分オートエンコーダも内部的には2つのモデル(エンコーダとデコーダ)を学習してお り、類似の課題があります。 ◦ 最終的な潜在空間の状態がエンコーダの学習過程に依存しており、特に、誤差関数における KL ダ イバージェンスの重みによって、結果が大きく変わります。 ◦ 潜在空間の分布が標準正規分布からずれると、整った画像が再現されにくくなります。 156 -
Diffusion モデルが優れている理由 • 一方、Diffusion モデルには、次のような特性があります。 ◦ 単一のモデルを学習するため、複数モデルのバランスを考える必要がありません。 ◦ 学習データと標準正規分布ノイズを明示的に紐づけており、標準正規分布から安定的に整った画像 が再現できます。
◦ 画像生成のステップを複数に分けることで、安定的に整った画像が生成できます。 157 -
オートエンコーダの隠れた課題 学習データ • オートエンコーダから変分オートエンコーダに拡張する目的 の 1 つとして、「クラスター間の隙間を埋める」という説明 をしました。 • サンプラーでデコーダへの入力値を動かす事には、「学習
データ間の隙間を埋める」という役割もありました。 ◦ 多くの場合、2 つの自然な画像の「中間画像」 は、自然な画像にはなりません。つまり、現実 世界の画像データは本質的に「飛び飛びに存在 する」という性質があります。 エンコーダ この隙間の部分を学習する データが存在しない 潜在空間 159 -
VQ-VAE のアイデア • 「現実世界の画像データは飛び飛びに存在する」という課題を根本的に解決するアイデアと して、Vector Quantized VAE(VQ-VAE)があります。 • VQ-VAE では、潜在空間を「画像の各部位の役割を示すベクトル値の集合」に置き換えるこ
とで、画像の特徴をダイレクトに表現します。 ◦ ◦ 「各部位の役割を示すベクトル値」は一定数の ベクトル値を事前に用意します。これを「コー ドブック」と呼びます。コードブックの個々の ベクトル値は、直感的には、「空」「雲」 「海」などの意味を表すと考えます。 コードブックの値のみを組み合わせることで、 「整った画像に対応する潜在空間の値」が構成 しやすくなります。 空 雲 海 島 空 空 空 雲 雲 海 海 海 海 海 海 船 船 島 島 海 海 海 島 島 海 海 島 島 島 ・・・ コードブック 1 つの画像に対応した 「潜在空間の値」のイメージ 160 -
VQ-VAE の学習処理 • エンコーダは、元の画像に対して「画像サイズを縮小して(縦横のピクセル数を減らして)各ピクセル値 をベクトル値に置き換える」という処理を行います。さらに、各ベクトル値をコードブックに載っている ベクトル値(値が一番近いもの)に置き換えてからデコーダに入力します。 • 勾配ベクトルの計算時は「コードブックに載っている値への置き換え(不連続関数)」はスキップしま す。(不連続関数部分を近似的に恒等関数に置き換えて勾配ベクトルを計算します。) •
画像生成時は、「潜在空間の各点 にコードブックの値を割り当てる モデル」を別途学習して使いま す。(整った画像から得られる潜 在空間の値を教師データとして、 PixelCNN を学習します。) https://arxiv.org/abs/1711.00937 161 -
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 -
演習 • 以下のノートブックを見て、コードの実装を理解してみてください。 ◦ 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 -
ラベル情報を用いたデコーダの学習 • 条件付き変分オートエンコーダでは、デコーダにラベル情報を入力することで、生成する画 像を指定できました。 • この際、単純なラベル値ではなく、「画像の説明文を Transformer モデルのテキストエン コーダで埋め込みベクトルに変換した値」を入力すれば、自然言語テキストで生成画像を指 定できる様になると期待できます。
エンコーダ 潜在空間 デコーダ 画像データ "a photograph of an astronaut riding a horse" × テキストの 埋め込みベクトル テキスト エンコーダ 165 -
CLIP による埋め込みベクトルの生成 • 実際にこれを実現するには、テキストエンコーダは、入力テキストから「画像の種類を表す 情報」を抽出する様に学習しておく必要があります。 ◦ • これにより、入力テキストの文面が学習に使ったテキストと異なっていても、「画像の 種類」として同じ意味内容であれば、類似の画像が生成できるようになります。 これを実現するモデルの一例として、CLIP
がありま す。「画像とその説明文」のペアを学習データとして 用意しておき、画像とテキストを個別にエンコーダで 変換して、得られる埋め込みベクトルの類似度が大き くなるようにエンコーダを学習します。 ◦ 類似した画像は類似した埋め込みベクトルにな るので、対応するテキストの埋め込みベクトル も類似したものになります。 https://github.com/openai/CLIP 166 -
CLIP による埋め込みベクトルの特徴 • 画像 A と画像 B が画像データとして似ていると埋め込みベクトルは似ている ◦ •
学習の結果、画像とペアになるテキストの埋め込みベクルは似ている ◦ • これらの結果、類似画像のテキストは埋め込みベクトルが似ている ◦ 167 -
Diffusion モデルとテキストエンコーダの組み合わせ • Diffusion モデルにテキストエンコーダからの入力を組み合わせることで、自然言語テキス トから画像を生成するさまざまなモデルが提案されており、一例として、次の様なバリエー ションがあります。 ◦ テキストエンコーダを使わずにテキスト(トークン ID
の列)をそのまま入力 ◦ CLIP のテキストエンコーダからの出力(テキストの埋め込みベクトル I)を利用する ◦ CLIP のテキストエンコーダからの出力をさらにイメージエンコーダ側の出力(対応す る画像の埋め込みベクトル T)に置き換えるモデルを追加 ▪ なので、 よりも の方が類似度が高い 168 -
Diffusion モデルとテキストエンコーダの組み合わせ • 右図は、前ページの 3 つの 方法を比較した例です。下 にいくほど、入力テキスト の意味によりマッチした画 像が生成されていることが
わかります。 https://arxiv.org/abs/2204.06125 169 -
Stable Diffusion について • オリジナルの Diffusion モデルは、高次元の画像空間で直接に「整った画像」と「ノイズ画 像」の分布を変換しました。 • Stable
Diffusion の Latent Diffusion Model(LDM)は、潜在空間の中で、「乱数で分布 した集合」と「整った画像に対応する点の集合」を変換します。 ◦ • 潜在空間は、オートエンコーダで事前に用 意しておきます。LDM で得られた「整った 画像に対応する点」をデコーダに入力して 実際の画像を出力します。 さらに、「画像の種類を表す埋め込みベクトル」 を入力することで、生成する画像を操作できるよ うに設計されています。 https://arxiv.org/abs/2112.10752 171 -
PaLI のアーキテクチャー • PaLI は、テキストと画像のペアからテキストを出力するモデルです。 • テキストは通常の Transformer エンコーダに入力します。画像は Vision
Transformer で 「画像の意味を表す埋め込みベクトル」に変換した後に、テキスト用の Transoformer エン コーダに入力します。 ◦ • これでテキストと画像を組み合わせた表現の「意味」が抽出できると期待します。 後段に Transformer デコーダを接続して、その意味に基づいた出力テキストを生成しま す。 https://arxiv.org/pdf/2209.06794.pdf 172 -
Gemini のアーキテクチャー • モデルの詳細は公開されていませんが、PaLI のアーキテクチャーを拡張したものと想像され ます。テキストと画像に加えて、音声と動画のデータも入力可能で、それぞれのデータ形式 に応じたエンコーダが用意されています。 • 後段にテキスト生成用のデコーダと画像生成用のデコーダを接続して、テキストと画像の両 方を出力します。
https://storage.googleapis.com/deepmind-media/gemini/gemini_1_report.pdf 173