敵対的ネットワークの概要

みなさんこんにちは。 この記事から、競合ネットワークに関する一連のストーリーを始めます。 前の記事と同様に、対応するdockerイメージを準備しました。ここでは、ここに記載されている内容をすべて再現する準備ができています。 ここの例からすべてのコードをコピーするのではなく、その主要部分のみをコピーします。したがって、便宜上、理解しやすいように近くに置くことをお勧めします。 Dockerコンテナはここにあり、ラップトップ、utils.pyおよびdockerfileはここにあります







敵対ネットワークのフレームワークがイアン・グッドフェローによって既に有名な作品Generative Adversarial Networksで提案されたという事実にもかかわらず、重要なアイデアはドメイン適応(ドメイン適応)の仕事から彼に来たので、このトピックで敵対ネットワークの議論を始めます。







同様のオブジェクトセットに関する2つのデータソースがあるとします。 たとえば、さまざまな社会人口統計グループ(男性/女性、大人/子供、アジア人/ヨーロッパ人など)の医療記録にすることができます。 異なるグループの代表者の典型的な血液検査は異なるため、あるサンプルの代表者について訓練された心血管疾患(CVD)のリスクを予測するモデルは、別のサンプルの代表者には適用できません。







この問題の一般的な解決策は、サンプルを識別する機能をモデルの入力に追加することですが、残念ながら、このアプローチには多くの欠点があります。







  1. 不均衡なサンプル-ヨーロッパ人よりもアジア人の方が多い
  2. 異なる統計-子供はCVDよりもはるかに少ない可能性があります
  3. サンプルの1つの不十分なマークアップ-60代の男性がアフガニスタンで死亡したため、出生地域によっては、女性よりもCVDに関するデータが少ない。
  4. データにはさまざまな兆候があります-人やマウスの血液検査など。


これらのすべての理由により、モデルトレーニングのプロセスが非常に複雑になります。 しかし、多分あなたは気にしないでください? サンプルごとに1つのモデルをトレーニングし、落ち着きます。 結局のところ-それは価値があります。 異なるトレーニングサンプルからの統計の違いを平準化できる場合、実際には、元のサンプルよりも大きいサイズのサンプルを1つ作成できます。 そして、あなたが2つのデータソースを持っているのではなく、もっとたくさんあるのであれば?







ドメインが「ネットワーク」時代に適応する問題をどのように解決したかについてはお話ししませんが、基本的なアーキテクチャをすぐに示します。







記事のネットワークアーキテクチャ



2014年、同胞のYaroslav Ganinは、Viktor Lempitskyと共同で、「バックプロパゲーションによる教師なしドメイン適応 」(教師によるエラーの逆伝播を使用しないドメイン適応)を公開しました。 この記事では、2番目のソースのラベルを使用せずに、あるデータソースから別のデータソースに分類モデルを転送する方法を示します。 提示されたモデルは、図のように相互接続された機能抽出(E)、ラベル予測(P)、およびドメイン分類子©の3つのサブネットで構成されていました。

E + Pネットワークのペアは通常の分類子であり、途中でカットされます。 カットされるレイヤーは、フィーチャレイヤーと呼ばれます。 ネットワークCはこの層から入力を受け取り、サンプルがどのソースから来たかを推測しようとします。 ネットワークEのタスクは、データからそのような兆候を抽出し、Pが例のラベルを正しく推測でき、Cがそのソースを特定できないようにすることです。







これがなぜ必要で、なぜ機能するのかをよりよく理解するために、情報について話しましょう。 各例には、ラベルに関する情報やその他の情報が含まれていると言えます。 MNISTの場合、この情報はすべて、たとえば28x28ピクセルのサイズのb / w画像の形式で記録できます。 MNISTで完璧な自動エンコーダーをトレーニングできる場合、同じ情報を別の方法で記録できます。 場合によっては、例自体のラベル情報が不完全である可能性があることは明らかです。 たとえば、画像からどの図が書き込まれたかを正確に理解できるとは限りませんが、ラベル情報の一部はまだ画像に含まれています。 しかし、ラベルに加えて、画像には、手書きの特性(太さ、傾斜、「カール」)、場所(中央またはシフトあり)、ノイズなどの多数の明白で膨大な暗黙の特性があります。 分類子をトレーニングするとき、タグに関する情報を可能な限り抽出しようとしますが、これは非常に多くの方法で実行できます。 同じMNISTで、100個の同等に効果的な分類器をトレーニングできます。各分類器には、データソースが異なる場合は言うまでもなく、画像の独自の非表示表現があります。







Ganinのアイデアは、ニューラルネットワークの助けを借りて情報を最大化できる場合、それを最小化することを妨げるものは何もないということです。 2つの異なるソース(MNISTとSVHNなど)からのデータを検討する場合、各例にはラベルとソースに関する情報が含まれていると言えます。 ニューラルネットワークEをトレーニングして、ラベルに関する情報を含む記号を抽出し、例の出所に関係なく同じ方法で抽出できる場合、1つのソースからの例のみでトレーニングされたネットワークPは、2番目のソースのラベルも予測できるはずです。







結果表



実際、ドメイン適応を使用したSVHNの例を使用してトレーニングされたニューラルネットワークは、SVHNのみでトレーニングされたネットワークよりも正確にMNISTからの画像のクラスを決定します。 同時に、両方のモデルはもちろん、トレーニング中にMNISTから単一のタグを見たことはありません。 実際、これは、2番目のサンプルについてラベルがわからなくても、訓練された分類器をあるサンプルから別のサンプルに転送できることを意味します。

番号を分類するタスクは非常に簡単ですが、この記事で使用されているネットワークのトレーニングにはかなりのリソースが必要になる場合があり、さらにこの問題を解決するコードはインターネットで簡単に見つけることができます。 したがって、この手法の別の適用例を分析し、機能層での情報の「分離」の考え方をより良く実証するのに役立つことを願っています。







多くの場合、情報を抽出したり、特別な方法で情報を提示したりすると、自動エンコーダーが登場します。 MNISTの例を圧縮形式で提示する方法を学びましょう。ただし、圧縮された表現に、どの特定の図が描かれているかに関する情報が含まれないようにします。 次に、抽出された特徴と元の数字のラベルを受け取ったネットワークのデコーダーが元の画像を復元できる場合、エンコーダーはラベル以外の情報を失わないと想定できます。 同時に、分類器が抽出された特性によってラベルを推測できない場合、ラベルに関するすべての情報は忘れられます。







これを行うには、 エンコーダー (E)、 デコーダー (D)、 分類器 (C)の3つのネットワークを作成してトレーニングする必要があります。







今回は、いくつかの畳み込み層を追加してエンコーダーを畳み込みにします。これには、 Sequentialクラスを使用します。







conv1 = nn.Sequential( nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, padding=1), nn.BatchNorm2d(num_features=16), nn.ReLU(), nn.MaxPool2d(kernel_size=2, stride=2), nn.Dropout(0.2) ) conv2 = nn.Sequential( nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, padding=1), nn.BatchNorm2d(num_features=32), nn.ReLU(), nn.MaxPool2d(kernel_size=2, stride=2), nn.Dropout(0.2) ) self.conv = nn.Sequential( conv1, conv2 )
      
      





実際、サブネットをすぐに設定できます。この場合、畳み込み層のシーケンス、ミニバッチによる正規化、アクティベーション関数、サブサンプリング、ドロップアウトです。 これらのレイヤーに関する情報は、インターネット(または、たとえば、 )で大量に入手できるため、ここでは詳細に分析しません。







フォワード機能のシーケンシャルレイヤー(またはサブネット)は、他のレイヤーと同じように使用できます。







 def forward(self, x): x = self.conv(x) x = x.view(-1, 7*7*32) x = self.fc(x) return x
      
      





デコーダーエンコーダーに似たものにするためにその最後のレイヤーは転置畳み込みを使用して設定されます







 conv1 = nn.Sequential( nn.ConvTranspose2d(in_channels=32, out_channels=16, kernel_size=3, stride=2), nn.BatchNorm2d(num_features=16), nn.ReLU(), nn.Dropout(0.2) ) conv2 = nn.Sequential( nn.ConvTranspose2d(in_channels=16, out_channels=1, kernel_size=2, padding=1, stride=2), nn.Tanh() )
      
      





デコーダーの大きな違いは、入力で、 エンコーダーから受け取ったサインだけでなく、ラベルも受け取ることです:







 def forward(self, x, y): x = torch.cat([x, y], 1) x = self.fc(x) x = x.view(-1, 32, 7, 7) x = self.deconv(x) return x
      
      





torch.catを使用する 、標識とラベルを1つのベクトルに連結し、このベクトルから画像を復元するだけです。







そして、3番目のネットワークは通常の分類 であり、 エンコーダーを使用して抽出された特性に基づいて、元の画像ラベルを予測します。

モデル全体の学習サイクルは次のようになります。







 for x, y in mnist_train: y_onehot = utils.to_onehot(y, 10) # train classifier C C.zero_grad() z = E(x) C_loss = NLL_loss(C(z), y) C_loss.backward(retain_graph=True) C_optimizer.step() # train decoder D and encoder E E.zero_grad() D.zero_grad() AE_loss = MSE_loss(D(z, y_onehot), x) C_loss = NLL_loss(C(z), y) FADER_loss = AE_loss - beta*C_loss FADER_loss.backward() D_optimizer.step() E_optimizer.step()
      
      





まず、 エンコーダーを使用して画像の特徴を抽出し 分類器 のみの重みを更新して、ラベルの予測を改善します。







 z = E(x) C_loss = NLL_loss(C(z), y) C_loss.backward(retain_graph=True) C_optimizer.step()
      
      





その際、PyTorchに計算グラフを保存して再利用するよう依頼します。 次のステップでは、 自動エンコーダーをトレーニングし、 分類器がラベルを復元するのを困難にするような記号を抽出する追加の要件を課します。







 AE_loss = MSE_loss(D(z, y_onehot), x) C_loss = NLL_loss(C(z), y) FADER_loss = AE_loss - C_loss FADER_loss.backward() D_optimizer.step() E_optimizer.step()
      
      





分類 の重みは更新れないことに注意してください。ただし、 エンコーダの重みは分類器のエラーが増加する方向に更新されます。 したがって、 分類器または自動エンコーダーのどちらかを交互に学習して、反対の目標を追求します。 これは、敵対的ネットワークの考え方です。







このようなモデルをトレーニングした結果、ラベルを除く例を復元するために必要なすべての情報を例から抽出するエンコーダーを取得する必要があります。 同時に、ラベルとともにこの情報を使用してデコーダをトレーニングし、元の例を復元できるようにします。 しかし、 デコーダに別のラベルを入力するとどうなりますか? 以下の画像では、各行は、10の可能なラベルと組み合わせて、数字の1つの符号から画像を復元することによって取得されます。 基礎となる数値は対角線上にあります(より正確には、元の例自体ではなく、復元されますが、「正しい」ラベルを使用します)。







数字間の折り返しスタイル







私の意見では、この例はラベル以外の情報を抽出するという考えを完全に示しています。なぜなら、同じ行ではすべての数字が同じスタイルで「書かれている」ことがわかるからです。 さらに、数値「1」から得られる線が不安定であることは明らかです。 これは、ユニットを記述する際に、スタイルに関する多くの情報、おそらく線の太さと傾きのみが含まれていないという事実によって説明しますが、「カール」に関する情報はまったくありません。 したがって、同じスタイルで書かれた残りの数字は非常に多様になる可能性がありますが、それぞれの場合、スタイルは行全体で1つになりますが、トレーニングのさまざまな段階で変化します。







Facebookチームの記事でNIPS'17に同様のアプローチが公開されたことを付け加えるだけです。 同様に、モデルは顔の写真から特徴を抽出し、ひげやメガネの存在などの「忘れる」ラベルを付けます。 記事で何が起こったのかの例を次に示します。







FADER Networksの記事の例



この投稿では「新しい」数字を描きましたが、このために、既存の数字を使用してスタイルを選択する必要がありました。 次の記事では、ゼロから画像を生成する方法と、この特定のモデルがこれを行う方法を知らない理由について説明します。



All Articles