Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

PyTorch

PyTorchはPythonのオープンソースの機械学習・深層学習ライブラリです。

  • 柔軟性を重視した設計であり、さらに、機械学習・深層学習モデルをPythonの慣用的なクラスや関数の取り扱い方で実装できるようになっています。

  • GPUを使用した計算をサポートしますので、CPU上で同じ計算を行う場合に比べて、数十倍の高速化を実現します。

1テンソル

深層学習モデルは通常、入力から出力にどのようにマッピングされるのかを対応つけるデータ構造を表します。一般的に、このようなある形式のデータから別の形式への変換は膨大な浮動小数点数の計算を通じて実現されています。

データを浮動小数点数として扱うためには、PyTorchは基本的なデータ構造として「テンソル」を導入しています。

深層学習の文脈でのテンソルとは、ベクトルや行列を任意の次元数に一般化したものを指します。つまり、多次元配列を扱います。

1.1テンソルの操作

1.1.1テンソルの作成

tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])
tensor([[0.1994, 0.0578, 0.0498],
        [0.5079, 0.3606, 0.8008],
        [0.3822, 0.2184, 0.6534],
        [0.2677, 0.1405, 0.2167],
        [0.6294, 0.2411, 0.5918]])
tensor([5.5000, 3.0000])

1.1.2テンソル要素の型

テンソル要素の型は、引数に適切なdtypeを渡すことで指定します。

1.1.3テンソルの操作(変形・変換等)

PyTorchにはテンソルに対する操作(変形・演算など)が多く用意されています。

tensor([[1.0909, 1.6082, 0.9869],
        [1.3109, 0.9099, 0.7708],
        [1.1601, 1.5726, 0.8970],
        [1.1238, 0.8603, 1.2183],
        [0.2006, 1.4446, 1.8380]])
tensor([[1.0909, 1.6082, 0.9869],
        [1.3109, 0.9099, 0.7708],
        [1.1601, 1.5726, 0.8970],
        [1.1238, 0.8603, 1.2183],
        [0.2006, 1.4446, 1.8380]])

1.1.4テンソルの一部指定や取り出し(Indexing)

PyTorchテンソルは、Numpyや他のPythonの科学計算ライブラリーと同じく、テンソルの次元ごとのレンジインデックス記法で一部指定や取り出しを行えます。

tensor([[0.9569, 0.6907, 0.3151], [0.0496, 0.9473, 0.8522]])
tensor([0.4762, 0.9864, 0.9569, 0.0496])

1.1.5テンソルの微分機能

PyTorchテンソルは、テンソルに対して実行された計算を追跡し、計算結果の出力テンソルの微分を、各テンソルの要素に対して解析的に計算することができます。

具体的には、requires_grad=True を設定したテンソルに対して行われたすべての演算が計算グラフとして記録され、.backward()メソッドを呼び出すことで、損失関数などの出力に対する各パラメータの勾配が自動的に計算されます。

tensor(4.)
実習問題

次の関数について、各変数に対する勾配を求めなさい

z=(x12+x22)×yz = (x_1^2 + x_2^2) \times y

ただし、x1=1.0x_1=1.0, x2=2.0x_2=2.0, y=3.0y=3.0 とする。

1.1.6CUDA Tensors(CUDA テンソル)

tensorは .to メソッドを使用することであらゆるデバイス上のメモリへと移動させることができます。

tensor(3., device='cuda:0', grad_fn=<AddBackward0>)
tensor(3., dtype=torch.float64, grad_fn=<ToCopyBackward0>)

2学習データセットの作成

深層学習を実装する際には、大量のデータを扱う必要があります。しかし、その際には次のような課題が生じます:

  • データの読み込みや前処理のコードが複雑になる(for文で個別処理する必要がある)

  • 学習時にデータをランダムにシャッフルしたり、ミニバッチに分割したりする必要がある

2.1Datasetクラス

PyTorchのtorch.utils.data.Datasetは、これらの課題を解決するために用意されたデータ管理の基本インターフェースです。

PyTorchで深層学習を実装する際には、特徴量行列とラベルをDatasetというクラスに渡して、特徴量行列とラベルを一つのデータベース的なものにまとめる働きをします。

一般的には、PyTorchのtorch.utils.data.Datasetクラスを継承して定義します。以下に示すメソッドを定義するように指定されています。

  • __init__(self): 初期実行関数です。Datasetを定義する際に必要な情報を受け取ります。

  • __len__(self): データ全体の数を返す関数です。

  • __getitem__(self, index): 指定されたindexに対応するデータと正解ラベル(ターゲット)を返します。これにより、データの取得方法を統一化します。

(tensor([1., 2.]), tensor(0))

2.2DataLoaderクラス

機械学習のトレーニングには、データセットのサンプリング、シャッフル、バッチ分割などの操作が必要とされます。これらの操作を効率化するためにDataLoaderクラスが用意されています。

  • バッチ処理: 指定したバッチサイズでデータを分割します

  • シャッフル: データの順序をランダムに並べ替えられます

<torch.utils.data.dataloader.DataLoader at 0x702905d0ffd0>
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Batch data shape: torch.Size([10, 3])
Batch labels shape: torch.Size([10])
Sample data: tensor([ 0.1452,  0.1315, -1.4777])
Sample label: tensor(0)

DataLoaderは反復処理が可能なので、トレーニング中のループで直接に使用することができます。

3深層学習モデルの構築

torch.nnで用意されているクラス、関数は、独自のニューラルネットワークを構築するために必要な要素を網羅しています。

PyTorchの全てのモジュールは、nn.Moduleを継承しています。

そしてニューラルネットワークは、モジュール自体が他のモジュール(レイヤー)から構成されています。

この入れ子構造により、複雑なアーキテクチャを容易に構築・管理することができます。

3.1クラスの定義

nn.Moduleを継承し、独自のネットワークモデルを定義し、その後ネットワークのレイヤーを __init__で初期化します。

nn.Module を継承した全モジュールは、入力データの順伝播関数であるforward関数を持ちます。

このクラスは、PyTorchのnn.Moduleを継承した単純なニューラルネットワークの実装を示しています。入力は固定長の512とされており、出力は3の次元を持つベクトルです。

  • self.linear_relu_stack: このシーケンシャルな層は、3つの線形層とそれぞれの後に続くReLU活性化関数から構成されています。

    • nn.Sequentialにレイヤーを順に渡すだけで、複数のレイヤーを順に積み重ねたモデルを簡単に定義できます。

    • linear layerは、線形変換を施します。linear layerは重みとバイアスのパラメータを保持しています。

    • nn.ReLUという活性化関数を設置することで、ニューラルネットワークの表現力を向上させます。

  • 順伝播メソッド (forward): 入力テンソルxを受け取り、ネットワークを通して出力を生成する機能を持ちます。

3.2GPUの利用

NeuralNetworkクラスのインスタンスを作成し、変数device上に移動させます。

以下でネットワークの構造を出力し確認します。

Using cuda device
NeuralNetwork(
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=512, out_features=128, bias=True)
    (1): ReLU()
    (2): Linear(in_features=128, out_features=128, bias=True)
    (3): ReLU()
    (4): Linear(in_features=128, out_features=3, bias=True)
    (5): ReLU()
  )
)

3.3モデルによる計算

  • ニューラルネットワークの最後のlinear layerはlogitsを出力します。このlogitsはnn.Softmaxモジュールへと渡されます。出力ベクトルの要素の値は[0,1][0, 1]の範囲となり、これは各クラスである確率を示します。

tensor([[0.0100, 0.0000, 0.0063],
        [0.0000, 0.0000, 0.0184],
        [0.0000, 0.0000, 0.0377]], device='cuda:0', grad_fn=<ReluBackward0>)
Predicted class: tensor([0, 2, 2], device='cuda:0')

4自動微分

ニューラルネットワークを訓練する際、その学習アルゴリズムとして、バックプロパゲーション(back propagation) がよく使用されます。

バックプロパゲーションでは、モデルの重みなどの各パラメータは、損失関数に対するその変数の微分値(勾配)に応じて調整されます。

これらの勾配の値を計算するために、PyTorchにはtorch.autograd という微分エンジンが組み込まれています。

autogradはPyTorchの計算グラフに対する勾配の自動計算を支援します。

シンプルな1レイヤーのネットワークを想定しましょう。

入力をx、パラメータをw と b、そして適切な損失関数を決めます。


PyTorchでは例えば以下のように実装します。

4.1勾配情報の保存

このニューラルネットワークでは、wとbが最適化したいパラメータです。

そのため、これらの変数に対する損失関数の微分値を計算する必要があります。

これらのパラメータで微分を可能にするために、requires_grad属性をこれらのテンソルに追記します。

そうすると、勾配は、テンソルの grad_fn プロパティに格納されます。

Gradient function for z = <AddBackward0 object at 0x702905d2ca90>
Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward0 object at 0x702905d2e110>

4.2勾配計算

ニューラルネットワークの各パラメータを最適化するために、入力xと出力yが与えられたもとで、損失関数の各変数の偏微分値、

すなわち

∂loss∂w\frac{\partial loss}{\partial w} 、∂loss∂b\frac{\partial loss}{\partial b}

を求める必要があります。

これらの偏微分値を求めるためにloss.backward()を実行し、w.gradとb.gradの値を導出します。

逆伝播では、.backward()がテンソルに対して実行されると、autogradは、

  • 各変数の .grad_fnを計算する

  • 各変数の.grad属性に微分値を代入する

  • 微分の連鎖律を使用して、各leafのテンソルの微分値を求める

を行います。

tensor([[0.1252, 0.1881, 0.0407],
        [0.0579, 0.0869, 0.0188],
        [0.0415, 0.0623, 0.0135],
        [0.0512, 0.0769, 0.0167],
        [0.0574, 0.0862, 0.0187]])
tensor([0.1888, 0.2836, 0.0614])

最適化ループを構築し、PyTorchによって自動的に逆伝播を行います

Epoch 100, Loss: 0.6918250918388367
Epoch 200, Loss: 0.6911764740943909
Epoch 300, Loss: 0.6903938055038452
Epoch 400, Loss: 0.6891277432441711
Epoch 500, Loss: 0.6879295110702515

4.3最適化関数

最適化は各訓練ステップにおいてモデルの誤差が小さくなるように、モデルパラメータを調整するプロセスです。

ここまでの説明は、単純な勾配降下法を最適化に使用しました。これは、シンプルなケースでは問題なく機能しますが、モデルが複雑になったときのために、パラメータ学習の収束を助ける最適化の工夫が必要とされます。

4.3.1Optimizer

optimというモジュールには、様々な最適化アルゴリズムが実装されています。

ここでは、確率的勾配降下法(Stochastic Gradient Descent)を例として使い方を説明します。

確率的勾配降下法は、ランダムに選んだ一部のデータ(ミニバッチ)で勾配を計算してパラメータを更新することを繰り返す方法です。

なお、torch.optim.SGDはパラメータの更新規則(w←w−η∇Lw \leftarrow w - \eta \nabla L)を実装したものであり、「確率的」になるかどうかは、勾配の計算にどのようなデータを使うかによって決まります。下の例ではデータ全体(10サンプル)をまとめて使っているため、実際にはバッチ学習による更新になります。

訓練したいモデルパラメータをoptimizerに登録し、合わせて学習率をハイパーパラメータとして渡すことで初期化を行います。訓練ループ内で、最適化(optimization)は3つのステップから構成されます。

  • optimizer.zero_grad()を実行し、モデルパラメータの勾配をリセットします。勾配の計算は蓄積されていくので、毎イテレーション、明示的にリセットします。

  • 続いて、loss.backward()を実行し、バックプロパゲーションを実行します。PyTorchは損失に対する各パラメータの偏微分の値(勾配)を求めます。

  • 最後に、optimizer.step()を実行し、各パラメータの勾配を使用してパラメータの値を調整します。

Epoch 100, Loss: 0.6930914521217346
Epoch 200, Loss: 0.6927065253257751
Epoch 300, Loss: 0.6925036311149597
Epoch 400, Loss: 0.6923048496246338
Epoch 500, Loss: 0.6921948790550232
Epoch 600, Loss: 0.6921080350875854
Epoch 700, Loss: 0.6920245289802551
Epoch 800, Loss: 0.6919457912445068
Epoch 900, Loss: 0.691868782043457
Epoch 1000, Loss: 0.69179368019104

5PyTorchによる深層学習の実装例

5.1データの読み込み

Loading...
Loading...
PassengerId      int64
Survived         int64
Pclass           int64
Name            object
Sex             object
Age            float64
SibSp            int64
Parch            int64
Ticket          object
Fare           float64
Cabin           object
Embarked        object
dtype: object

5.2データの前処理

  • 列の削除: 要らない列を消していきます。乗客IDや名前、チケット、港(Embarked)や部屋番号(Cabin)は生死にあまり関係がなさそうなので、削除します。

  • 欠損の補完: 平均値で欠損しているデータを補完します。

  • 文字列を数字に置換

Loading...

5.3データセットの作成

データセットとデータローダーでミニバッチ学習のためのデータセットを作成します。

(tensor([ 1.0000, 0.0000, 45.5000, 0.0000, 0.0000, 28.5000]), tensor(0.))

5.4モデルの定義

__init__で行列の掛け算の定義をして、forwardでそれをどの順番で行うかを指定する感じです。

5.5モデルの学習

損失関数は2乗誤差、最適化関数はAdamを使用します。

Epoch 1/100, Train Loss: 0.6523, Val Loss: 0.6527
Epoch 11/100, Train Loss: 0.5803, Val Loss: 0.5545
Epoch 21/100, Train Loss: 0.5136, Val Loss: 0.4905
Epoch 31/100, Train Loss: 0.4820, Val Loss: 0.4443
Epoch 41/100, Train Loss: 0.4370, Val Loss: 0.4449
Epoch 51/100, Train Loss: 0.4333, Val Loss: 0.4332
Epoch 61/100, Train Loss: 0.4191, Val Loss: 0.4213
Epoch 71/100, Train Loss: 0.4163, Val Loss: 0.4295
Epoch 81/100, Train Loss: 0.5030, Val Loss: 0.4887
Epoch 91/100, Train Loss: 0.4212, Val Loss: 0.4244

5.6モデルの評価

                 precision    recall  f1-score   support

Did not survive     0.8125    0.8667    0.8387       105
       Survived     0.7910    0.7162    0.7518        74

       accuracy                         0.8045       179
      macro avg     0.8018    0.7914    0.7952       179
   weighted avg     0.8036    0.8045    0.8028       179

実習問題

深層学習の実装を以下の指示に従って、改めて学習を行なってください。

  • バッチサイズを64に変更しなさい

  • モデルに一つ隠れ層を追加しなさい

  • ドロップアウト層を追加しなさい

  • オプティマイザはAdamWを設定しなさい

  • epochを増やしなさい