著者は、 Write for DOnations プログラムの一環として、 DevColorを選択して寄付を受け取りました。

動物分類のためのニューラルネットワークはだまされる可能性がありますか? 動物の分類子をだますことはほとんど影響を与えないかもしれませんが、私たちの顔認証システムがだまされる可能性があるとしたらどうでしょうか? または、自動運転車のプロトタイプのソフトウェアですか? 幸いなことに、多くのエンジニアと研究者が、プロトタイプのコンピュータービジョンモデルとモバイルデバイスまたは自動車の生産品質モデルの間に立っています。 それでも、これらのリスクには重大な影響があり、機械学習の実践者として考慮することが重要です。

このチュートリアルでは、動物の分類子を「だます」またはだましてみます。 チュートリアルを進める際には、コンピュータービジョンライブラリである OpenCV と、ディープラーニングライブラリであるPyTorchを使用します。 敵対的機械学習の関連フィールドで次のトピックを取り上げます。

  • ターゲットの敵対的な例を作成します。 たとえば、犬の画像を選択します。 target クラス、たとえば猫を選びます。 あなたの目標は、ニューラルネットワークをだまして、写真に写っている犬が猫であると信じ込ませることです。
  • 敵対的防御を作成します。 つまり、トリックが何であるかを知らなくても、これらのトリッキーな画像からニューラルネットワークを保護します。

チュートリアルの終わりまでに、ニューラルネットワークをだますためのツールと、だましから身を守る方法を理解できるようになります。

前提条件

このチュートリアルを完了するには、次のものが必要です。

ステップ1—プロジェクトの作成と依存関係のインストール

このプロジェクトのワークスペースを作成し、必要な依存関係をインストールしましょう。 ワークスペースをAdversarialMLと呼びます。

  1. mkdir ~/AdversarialML

AdversarialMLディレクトリに移動します。

  1. cd ~/AdversarialML

すべてのアセットを保持するディレクトリを作成します。

  1. mkdir ~/AdversarialML/assets

次に、プロジェクトの新しい仮想環境を作成します。

  1. python3 -m venv adversarialml

環境をアクティブ化します。

  1. source adversarialml/bin/activate

次に、このチュートリアルで使用するPythonのディープラーニングフレームワークであるPyTorchをインストールします。

macOSで、次のコマンドを使用してPytorchをインストールします。

  1. python -m pip install torch==1.2.0 torchvision==0.4.0

LinuxおよびWindowsでは、CPUのみのビルドに次のコマンドを使用します。

  1. pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
  2. pip install torchvision

次に、OpenCVnumpyのパッケージ済みバイナリをインストールします。これらは、それぞれコンピュータビジョンと線形代数のライブラリです。 OpenCVは画像の回転などのユーティリティを提供し、numpyは行列反転などの線形代数ユーティリティを提供します。

  1. python -m pip install opencv-python==3.4.3.18 numpy==1.14.5

Linuxディストリビューションでは、libSM.soをインストールする必要があります。

  1. sudo apt-get install libsm6 libxext6 libxrender-dev

依存関係をインストールした状態で、次に説明するResNet18という動物分類子を実行してみましょう。

ステップ2—事前に訓練された動物分類器を実行する

PyTorchの公式コンピュータービジョンライブラリであるtorchvisionライブラリには、一般的に使用されるコンピュータービジョンニューラルネットワークの事前トレーニング済みバージョンが含まれています。 これらのニューラルネットワークはすべて、 ImageNet 2012 でトレーニングされています。これは、1000クラスの120万のトレーニング画像のデータセットです。 これらのクラスには、乗り物、場所、そして最も重要なことに動物が含まれます。 このステップでは、ResNet18と呼ばれるこれらの事前トレーニング済みニューラルネットワークの1つを実行します。 ImageNetでトレーニングされたResNet18を「動物分類器」と呼びます。

ResNet18とは何ですか? ResNet18は、 MSR (He et al。)によって開発された残余ニューラルネットワークと呼ばれるニューラルネットワークファミリーの中で最小のニューラルネットワークです。 つまり、ニューラルネットワーク(関数f、入力x、出力f(x))は、「残余接続」x + f(x)。 この残余の接続は、今日でも最先端のニューラルネットワークで多用されています。 たとえば、 FBNetV2 FBNetV3です。

次のコマンドを使用して、この犬の画像をダウンロードします。

  1. wget -O assets/dog.jpg https://assets.digitalocean.com/articles/trick_neural_network/step2a.png

Image of corgi running near pond

次に、JSONファイルをダウンロードして、ニューラルネットワークの出力を人間が読める形式のクラス名に変換します。

  1. wget -O assets/imagenet_idx_to_label.json https://raw.githubusercontent.com/do-community/tricking-neural-networks/master/utils/imagenet_idx_to_label.json

次に、犬の画像で事前トレーニング済みのモデルを実行するスクリプトを作成します。 step_2_pretrained.pyという名前の新しいファイルを作成します。

  1. nano step_2_pretrained.py

まず、必要なパッケージをインポートし、main関数を宣言して、Pythonボイラープレートを追加します。

step_2_pretrained.py
from PIL import Image
import json
import torchvision.models as models
import torchvision.transforms as transforms
import torch
import sys

def main():
    pass

if __name__ == '__main__':
    main()

次に、ニューラルネットワーク出力から人間が読めるクラス名へのマッピングをロードします。 インポートステートメントの直後で、main関数の前にこれを追加します。

step_2_pretrained.py
. . .
def get_idx_to_label():
    with open("assets/imagenet_idx_to_label.json") as f:
        return json.load(f)
. . .

入力画像のサイズが最初に正しく、次に正しく正規化されるようにする画像変換関数を作成します。 最後の直後に次の関数を追加します。

step_2_pretrained.py
. . .
def get_image_transform():
    transform = transforms.Compose([
      transforms.Resize(224),
      transforms.CenterCrop(224),
      transforms.ToTensor(),
      transforms.Normalize(mean=[0.485, 0.456, 0.406],
                           std=[0.229, 0.224, 0.225])
    ])
    return transform
. . .

get_image_transformでは、ニューラルネットワークに渡される画像に適用するさまざまな変換を定義します。

  • transforms.Resize(224):画像の小さい側のサイズを224に変更します。 たとえば、画像が448 x 672の場合、この操作は画像を224×336にダウンサンプリングします。
  • transforms.CenterCrop(224):画像の中央からサイズ224×224の切り抜きを取ります。
  • transforms.ToTensor():画像をPyTorchテンソルに変換します。 すべてのPyTorchモデルでは、入力としてPyTorchテンソルが必要です。
  • transforms.Normalize(mean=..., std=...):平均を減算し、標準偏差で除算することにより、入力を標準化します。 これについては、torchvisionのドキュメントで詳しく説明されています。

画像を指定して、動物のクラスを予測するユーティリティを追加します。 この方法では、以前の両方のユーティリティを使用して動物の分類を実行します。

step_2_pretrained.py
. . .
def predict(image):
    model = models.resnet18(pretrained=True)
    model.eval()

    out = model(image)

    _, pred = torch.max(out, 1)  
    idx_to_label = get_idx_to_label()  
    cls = idx_to_label[str(int(pred))]  
    return cls
. . .

ここで、predict関数は、事前にトレーニングされたニューラルネットワークを使用して提供された画像を分類します。

  • models.resnet18(pretrained=True):ResNet18と呼ばれる事前トレーニング済みニューラルネットワークをロードします。
  • model.eval():「評価」モードで実行するようにモデルをインプレースで変更します。 他の唯一のモードは「トレーニング」モードですが、このチュートリアルではモデルをトレーニングしていない(つまり、モデルのパラメーターを更新していない)ため、トレーニングモードは必要ありません。
  • out = model(image):提供された変換された画像でニューラルネットワークを実行します。
  • _, pred = torch.max(out, 1):ニューラルネットワークは、可能なクラスごとに1つの確率を出力します。 このステップでは、最も高い確率でクラスのインデックスを計算します。 たとえば、out = [0.4, 0.1, 0.2]の場合、pred = 0です。
  • idx_to_label = get_idx_to_label():クラスインデックスから人間が読めるクラス名へのマッピングを取得します。 たとえば、マッピングは{0: cat, 1: dog, 2: fish}のようになります。
  • cls = idx_to_label[str(int(pred))]:予測されたクラスインデックスをクラス名に変換します。 最後の2つの箇条書きで示した例では、cls = idx_to_label[0] = 'cat'が生成されます。

次に、最後の関数に続いて、画像をロードするユーティリティを追加します。

step_2_pretrained.py
. . .
def load_image():
    assert len(sys.argv) > 1, 'Need to pass path to image'
    image = Image.open(sys.argv[1])

    transform = get_image_transform()
    image = transform(image)[None]
    return image
. . .

これにより、スクリプトの最初の引数で指定されたパスから画像が読み込まれます。 transform(image)[None]は、前の行で定義された一連の画像変換を適用します。

最後に、main関数に次の情報を入力して、画像を読み込み、画像内の動物を分類します。

step_2_pretrained.py
def main():
    x = load_image()
    print(f'Prediction: {predict(x)}')

ファイルがGitHubのstep_2_pretrained.pyにある最後のステップ2スクリプトと一致することを再確認してください。 スクリプトを保存して終了し、動物分類子を実行します。

  1. python step_2_pretrained.py assets/dog.jpg

これにより、次の出力が生成され、動物分類子が期待どおりに機能することが示されます。

Output
Prediction: Pembroke, Pembroke Welsh corgi

これで、事前にトレーニングされたモデルで推論を実行することはできます。 次に、画像にわずかな違いがあるニューラルネットワークをだまして、敵対的な例が実際に動作しているのを確認します。

ステップ3—敵対的な例を試す

次に、敵対的な例を合成し、その例でニューラルネットワークをテストします。 このチュートリアルでは、x + rの形式の敵対的な例を作成します。ここで、xは元の画像であり、rはいくつかの「摂動」です。 最終的には摂動rをダウンロードします。 摂動rをダウンロードすることから始めます。

  1. wget -O assets/adversarial_r.npy https://github.com/do-community/tricking-neural-networks/blob/master/outputs/adversarial_r.npy?raw=true

次に、画像を摂動と合成します。 step_3_adversarial.pyという名前の新しいファイルを作成します。

  1. nano step_3_adversarial.py

このファイルでは、次の3つのステップのプロセスを実行して、敵対的な例を作成します。

  1. 画像を変換する
  2. 摂動を適用しますr
  3. 摂動画像を逆変換します

ステップ3の終わりに、敵対的なイメージがあります。 まず、必要なパッケージをインポートし、main関数を宣言します。

step_3_adversarial.py
from PIL import Image
import torchvision.transforms as transforms
import torch
import numpy as np
import os
import sys

from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image


def main():
    pass


if __name__ == '__main__':
    main()

次に、以前の画像変換を反転する「画像変換」を作成します。 インポート後、main関数の前にこれを配置します。

step_3_adversarial.py
. . .
def get_inverse_transform():
    return transforms.Normalize(
        mean=[-0.485/0.229, -0.456/0.224, -0.406/0.255],  # INVERSE normalize images, according to https://pytorch.org/docs/stable/torchvision/models.html
        std=[1/0.229, 1/0.224, 1/0.255])
. . .

前と同じように、transforms.Normalize操作は平均を減算し、標準偏差で除算します(つまり、元の画像xの場合、y = transforms.Normalize(mean=u, std=o) = (x - u) / o)。 いくつかの代数を実行し、この正規化関数(transforms.Normalize(mean=-u/o, std=1/o) = (y - -u/o) / 1/o = (y + u/o) o = yo + u = x)を逆にする新しい操作を定義します。

逆変換の一部として、PyTorchテンソルをPIL画像に変換するメソッドを追加します。 最後の関数の後にこれを追加します。

step_3_adversarial.py
. . .
def tensor_to_image(tensor):
    x = tensor.data.numpy().transpose(1, 2, 0) * 255.  
    x = np.clip(x, 0, 255)
    return Image.fromarray(x.astype(np.uint8))
. . .
  • tensor.data.numpy()は、PyTorchテンソルをNumPy配列に変換します。 .transpose(1, 2, 0)は、(channels, width, height)(height, width, channels)に再配置します。 このNumPyアレイは、おおよそ[X47X]