コンテンツにスキップ

Top

PyTorch 入門

PyTorchをとりま使ってみる的な。

ただ、WSL2+Ubuntu20.04環境だとDetectron2のコンパイル時にエラーがでてどうしようもなくなったので、もうこの環境で学習することはない。

今後は普通のUbuntuでやる。ここではWSL2上での記事だが。

開発環境

Windows 10 Pro
WSL2 + Ubuntu 20.04
Anaconda 4.12.0
CUDA Version: 11.6

環境構築

Anacondaの環境を作成

$ conda create -n PyTorch python=3.10

なお、削除したくなったら、

$ conda remove -n PyTorch --all

で削除できる

Anaconda上にPyTorchをインストール

$ conda activate PyTorch
$ conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch

いろいろ必要なものをpipで入れる。(condaで入れるんじゃないの?これ。あってる?環境の作り方)

pip install numpy
pip install opencv-python
pip install click
pip install cvui
pip install tqdm
pip install matplotlib

でPyTorchの環境構築完了。

なお終わったらdeactivateすること

$ conda deactivate

WSL2上のUbuntuのGUI表示

WSL2のUbuntu上でGUIを出そうとしても当然GUIは表示されない。

まず、Windows 側にXmingをインストール後、コマンドプロンプト上から "-ac" の引数をつけて起動する(なんでかはしらん)

> "C:\Program Files (x86)\Xming\Xming.exe" :0 -clipboard -multiwindow -ac

次にUbuntu側はDisplay変数が必要なので、

$ export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0
を。

これでGUIが表示されるようになる。
(動確コマンド)

$ xeyes

ただ、なんかわからんけど表示されない場合、tkinterをインストールすると出たりする。

$ sudo apt install -y python3-tk

なんでかは知らん。

手書き文字の画像分類の流れ

①MNISTデータ(手書き文字データ群)をダウンロードする

②MNISTデータを用いて手書き文字を画像分類するモデルを学習する

③学習したモデルを利用して手書き文字を推論する

MNISTのダウンロード

MNISTという手書き文字データを画像分類するモデルを学習して作成する一連の流れを記す。

まずはMNISTのデータをダウンロードしなきゃいけないのだが、PyTorchはMNISTデータを自動でダウンロードできる。
なんか違和感あるけど、そういう機能がニューラルネットワークモデルを作成するフレームワークにはデフォルトでついていることが多い。

以下のソースでMNISTのデータがダウンロードできる。

from torchvision.datasets import MNIST
from torchvision import transforms

transform = transforms.Compose([
    transforms.ToTensor()
])

dataset_train = MNIST('data/', train=True, download=True, transform=transform)

そのまんまMNISTというクラスが用意されているのでそれを使えばデータをダウンロードする。
なお、transformsというわけがわからないのが急に出てきたが、MNISTは別にPyTorchに適したデータ形式でデータを提供してくれているわけではない。
ので、ダウンロードしたデータをそのままPyTorchでは使えない。

PyTorch は Tensor(テンソル)という形のデータを用いる。
ので、この transforms で ToTensor() 、すなわちダウンロードしたMNISTデータをテンソル化するよ、と指示を出しているのである。

ちなみにこのMNISTのデータ、0~9の画像ファイルのzipかなんかだと思っていたが全然違って複数のバイナリファイル。
実際に1枚づつ画像として読みだしたい場合、以下のコードでできる。
OpenCVを使って画像を表示するため、いったん、numpy形式に変換している。

import cv2

from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision import transforms

# データをダウンロードする(この時Tensor型に変換する)
transform = transforms.Compose([
    transforms.ToTensor(),
])
dataset_train = MNIST('data/', train=True, download=True, transform=transform)

# 画像の先頭イテレーターを取得(画像だけじゃなくラベルも取れるが)
train_loader = DataLoader(dataset_train, batch_size=10, shuffle=False)
data_iter = iter(train_loader)
images, labels = data_iter.next()

for index, (image, label) in enumerate(zip(images, labels)):
    # tensor型をnumpyにする(OpenCVで扱うために)
    npimg = image.numpy()

    # チャンネル数、高さ、幅の順でデータが入っているのでOpenCV用に高さ、幅、チャンネル数に変換
    npimg = npimg.transpose((1, 2, 0))

    # 値が0〜1なので0〜255の範囲にしてモノクロ画像にする
    npimg *= 255

    # 表示
    print("label={}".format(label))
    cv2.imshow("imshow", npimg)
    k = cv2.waitKey(-1)

取得したデータはTensor型なのでOpenCVで使えるようにnumpy化する。
OpenCVの画像は高さ、幅、チャンネル数なのに、MNISTのデータはチャンネル数、高さ、幅の順で入っているので入れ替え。
そこまでしてやっとこさOpenCVのimshowで表示できる形式になる。

で、実行したら画像が表示されるはず。
(28x28のモノクロ画像)

MNISTを画像分類するモデルの学習をする

①ネットワークモデルの定義

MNISTのデータを手に入れたので、こんどはこのデータを元に学習をして、画像分類ができるモデルを作成する。

そのためにはまずネットワークモデルを作成する必要がある。

画像分類に適したネットワークモデルはいくつがあるが、極めてシンプルなものとして、全結合層にLinearと中間層の活性化関数にReLUを組み合わせ、出力の活性化関数にはSigmoidを用いたモデルを作成する。
(ここではPyTorchの勉強であって、ネットワークモデルの勉強ではないので全結合層なり活性化関数なりなぜそれらを組み合わせたら画像分類できるのか、などは一切割愛)

PyTorchでネットワークモデルを作るには以下のように、nn.Moduleを継承したクラスを作成する。

import torch
from torch import nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()

        self.l1 = nn.Linear(28 * 28, 200)
        self.l2 = nn.Linear(200, 100)
        self.l3 = nn.Linear(100, 50)
        self.l4 = nn.Linear(50, 10)  # class = 10

    def forward(self, x):
        x = torch.reshape(x, (-1, 28 * 28))  # 入力:(N, 1, 28, 28) を (N, 784)にreshape
        x = F.relu(self.l1(x))
        x = F.relu(self.l2(x))
        x = F.relu(self.l3(x))
        x = nn.Sigmoid()(self.l4(x))
        return x

クラス名がNetなのはなんか教科書的なやつでだいたいNetという名前がつけられてるから真似してるだけでどんな名前でも問題ない。
nn.Moduleクラスを継承する必要がある。
__init__内で使用する線形関数(Linear)を定義する。といっても引数の値が違うだけ。
最初のself.l1は入力が28x28、で出力が200、ということを定義している。
28x28はMNISTの画像のサイズ。
で、次は200を100に、そして次は100を50に、最終的には50を10にしている。
なんで最後が10かというと、MNISTは0~9の文字、すなわち10種類だからである。

forwardは

relu関数の引数にLinear関数が入れられていますが、Linear関数だけを連続して呼び出しても意味がないことは確認されています。
途中で活性化関数をかけないと学習にならないのです。
ので、Linearの呼び出しと活性化関数呼び出しはセットとなるため、
x = F.relu(self.l1(x))
な感じの呼び出しになります。
最後のSigmoidで10種類に分けて結果が出力されます。
なぜ出力層はReLUではなくSigmoidなのか、などニューラルネットワークの基礎的な部分はここでは割愛します。

②学習する

さて、データもダウンロードし、モデルも定義し、学習率も設定したので学習してみましょう。


③推論する

学習モデルができたのでこれを使って推論してみましょう。


結果は...ひどいですね。ぜんぜん当たりません。

所詮この程度のネットワークモデルでは話にならん、といったところですか。

以上がPyTorchを使った画像分類のざっとした流れです。

ニューラルネットワークについて勉強したことがコードとして現実化するのは感慨深いですが結構な面倒臭さです。

環境の構築も面倒だったり、PyTorchはどんどんバージョンアップして行き、少し前まで動いていたプログラムが動かないというジレンマ。

まだまだ過渡期だなぁという気がしています。

以上!