How to setup CUDA 10.2, 11.0, and 11.5 in order to use eddy_cuda10.2 (in FSL 6.0.5.x), PyTorch, and Tensorflow 2

ATTENTION (16 Apr 2023): From 6.0.6, you can use the latest CUDA to run eddy_cuda10.2. So this post is obsolete. I wrote a new post, so please check it out.

FSL 6.0.5 ships eddy_cuda10.2 which literally uses CUDA 10.2.
I explored a way to use eddy_cuda10.2, PyTorch and Tensorflow concurrently. Below is How-To for Ubuntu 18.04/20.04.

続きを読む

Ubuntu 20.04 / 18.04 環境で eddy_cuda10.2 (in FSL 6.0.5.x), PyTorch, Tensorflow 2 を使えるようにCUDA 10.2, 11.0, 11.5をセットアップする方法

注意(16 Apr 2023): FSL 6.0.6 から、CUDA 11以降でもeddy_cuda10.2が動くようになりました。したがって、以下の内容はもう古くなっています。新しい記事をご確認ください。

私のメインマシンは Lin4Neuro 18.04 ですが、そろそろ Lin4Neuro 20.04 への移行を考えています。

今、実験機には NVIDIA GeForce RTX 2070 が備え付けられています。
これを使って、FSL 6.0.5 の eddy をGPUが使えるように設定し、なおかつ、Tensorflow, Pytorch といった Deep Learning のフレームワークも使えるようにしたいと思います。

FSL 6.0.5 にはデフォルトで CUDA 10.2 に対応した eddy_cuda10.2 が配布されています。なので、CUDA 10.2を入れることにします。

なお、これは Ubuntu 18.04 でも全く問題なくできることがわかりましたので、タイトルを変更しました。

続きを読む

【PyTorch】サンプル⑨ 〜 動的グラフ 〜


1. 目的
2. 前準備
3. 計算グラフ
4. パッケージのインポート
5. モデルの定義
6. 使用するデータ
7. ニューラルネットワークのモデルの構築
8. 損失関数の定義
9. 最適化関数の定義
10. 学習回数
11. モデルへのデータ入出力 (Forward pass)
12. 損失(loss)の計算
13. 勾配の初期化
14. 勾配の計算
15. パラメータ(Weight)の更新
16. 実行
16.1. 9_dynamic_graph.py


1. 目的

  • PyTorchの特徴の一つである動的グラフに挑戦する。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. 計算グラフ

計算グラフには、静的グラフ動的グラフがあります。

TensorFlowやTheanoといったフレームワークは静的グラフを用いていますが、それに対してPyTorchは動的グラフを採用しています。

静的グラフと動的グラフの説明についてはこちらをご覧ください。

簡単に言うと、静的グラフは、事前にニューラルネットワーク構造を決めてから学習・推定を実行します。この時、一度決めたネットワーク構造は学習や推定時に変更することができません。

これに対し、動的グラフは、ネットワーク構造を定義する時点でネットワーク構造を固定する必要はなく、ネットワークに入力されるデータの様子をみて、ネットワークを構造を適宜変えることができます。故に、動的な計算グラフになります。

この動的グラフにより、柔軟な学習や推測が可能となります。

4. パッケージのインポート

import random
import torch

5. モデルの定義

【PyTorch】サンプル⑧ 〜 複雑なモデルの構築方法 〜で紹介した方法でニューラルネットワークモデルを構築していきます。

これまでは、レイヤ(層)を積み重ねるようにして、ニューラルネットワークモデルを定義していましたが、今回はネットワークモデルをクラスを使って定義します。クラスについては、こちらが参考になりました。

簡単のために、線形結合のみのネットワークを構築していきます。

クラスの名前をDynamicNetとします。引数torch.nn.Moduleはお決まりです。

class DynamicNet(torch.nn.Module):

コンストラクタで2層の線形結合層をインスタンス化します。

コンストラクタとインスタンスについては、こちらが参考になりました。

    def __init__(self, D_in, H, D_out):
        super(DynamicNet, self).__init__()
        self.input_linear = torch.nn.Linear(D_in, H)
        self.middle_linear = torch.nn.Linear(H, H)
        self.output_linear = torch.nn.Linear(H, D_out)

コンストラクタで定義された線形結合層をつかってモデルの予測値y_predを計算します。

テンソルの演算式は、forwardオブジェクトで自由に定義できます。

このチュートリアルでは、動的グラフの特徴を感じてもらうために、不思議なモデルを作っていきます。

    def forward(self, x):
        h_relu = self.input_linear(x).clamp(min=0)
        for _ in range(random.randint(0, 3)):
            h_relu = self.middle_linear(h_relu).clamp(min=0)
        y_pred = self.output_linear(h_relu)
        return y_pred

ポイントは、この部分です。

        for _ in range(random.randint(0, 3)):
            h_relu = self.middle_linear(h_relu).clamp(min=0)

この部分でやっていることは、forループの回数を0〜3回をランダムに決めて、中間層の線形結合層の数を随時変更しています。

6. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力xと予測したいyを乱数で定義します。

# Create random input and output data
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

7. ニューラルネットワークのモデルの構築

先程作成したニューラルネットワークモデルのクラスDynamicNetをつかってモデルmodelを定義します。

model = DynamicNet(D_in, H, D_out)

8. 損失関数の定義

二乗誤差をこのモデルの損失関数とします。

reductionのデフォルトはmeanですので、何も指定しなければtorch.nn.MSELossは平均二乗誤差を返します。

reduction=sumとした場合は、累積二乗誤差を算出します。

criterion = torch.nn.MSELoss(reduction='sum')

9. 最適化関数の定義

optimパッケージを使って最適化関数としてMomentum SGDを定義します。

lrは学習率です。

optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)

10. 学習回数

学習回数は500回にします。

for t in range(500):

11. モデルへのデータ入出力 (Forward pass)

定義したニューラルネットワークモデルmodelへデータxを入力し、予測値y_predを取得します。

    y_pred = model(x)

12. 損失(loss)の計算

定義した損失関数Momentum SGDで予測値y_predと真値yとの間の損失を計算します。

    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

13. 勾配の初期化

逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。

    optimizer.zero_grad()

14. 勾配の計算

backwardメソッドでモデルパラメータ(Weight)の勾配を算出します。

    loss.backward()

15. パラメータ(Weight)の更新

stepメソッドでモデルのパラメータを更新します。

    optimizer.step()

16. 実行

以下のコードを9_dynamic_graph.pyとして保存します。

16.1. 9_dynamic_graph.py

import random
import torch


class DynamicNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(DynamicNet, self).__init__()
        self.input_linear = torch.nn.Linear(D_in, H)
        self.middle_linear = torch.nn.Linear(H, H)
        self.output_linear = torch.nn.Linear(H, D_out)

    def forward(self, x):
        h_relu = self.input_linear(x).clamp(min=0)
        for _ in range(random.randint(0, 3)):
            h_relu = self.middle_linear(h_relu).clamp(min=0)
        y_pred = self.output_linear(h_relu)
        return y_pred


# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Construct our model by instantiating the class defined above
model = DynamicNet(D_in, H, D_out)

# Construct our loss function and an Optimizer.
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
for t in range(500):
    # Forward pass: Compute predicted y by passing x to the model
    y_pred = model(x)

    # Compute and print loss
    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # Zero gradients, perform a backward pass, and update the weights.
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

コードを保存したら、実行してみましょう。

左の数字が学習回数、右の数値がモデルの推定値と実際の答えと二乗誤差です。

python3 9_dynamic_graph.py
99 3.707427978515625
199 1.2958085536956787
299 1.837145209312439
399 0.9760080575942993
499 0.43565574288368225

参考のために、静的グラフの場合の結果を載せておきます。

動的グラフの方が損失の収束が早いことがわかります。

In:

python3 9_static_graph.py

Out:

99 2.5070412158966064
199 0.04455046355724335
299 0.0011508199386298656
399 3.597284376155585e-05
499 1.361027443635976e-06

【PyTorch】サンプル⑧ 〜 複雑なモデルの構築方法 〜


1. 目的
2. 前準備
3. PyTorchのインポート
4. モデルの定義
5. 使用するデータ
6. ニューラルネットワークのモデルを構築
7. 損失関数の定義
8. 最適化関数の定義
9. 学習回数
10. モデルへのデータ入出力 (Forward pass)
11. 損失(loss)の計算
12. 勾配の初期化
13. 勾配の計算
14. 実行
14.1. 8_class_model.py
15. 終わりに


1. 目的

このチュートリアルに至るまでは、ニューラルネットワークモデルの定義を積み木を積み重ねるように単純なシーケンスtorch.nn.Sequentialで構築していました。

このtorch.nn.Sequentialを用いた方法は、モデルの定義が簡単である反面、ネットワーク構造も簡素なものしか作ることができません。

例えば、torch.nn.Sequentialでは、ResNetネットワーク構造を構築することがきません。

(Credit:Deep Residual Learning for Image Recognition. Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun. arXiv:1512.03385 [cs.CV]
(or arXiv:1512.03385v1 [cs.CV] for this version) )

このチュートリアルでは、PyTorch: Custom nn Modulesを参考に、より複雑なニューラルネットワークのモデルが構築できる方法を紹介します。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. PyTorchのインポート

import torch

4. モデルの定義

これまでは、レイヤ(層)を積み重ねるようにして、ニューラルネットワークモデルを定義していましたが、今回はネットワークモデルをクラスを使って定義します。クラスについては、こちらが参考になりました。

簡単のために、線形結合が2層のネットワークを構築していきます。

クラスの名前をTwoLayerNetとします。引数torch.nn.Moduleはお決まりです。

class TwoLayerNet(torch.nn.Module):

コンストラクタで2層の線形結合層をインスタンス化します。

コンストラクタとインスタンスについては、こちらが参考になりました。

    def __init__(self, D_in, H, D_out):
        super(TwoLayerNet, self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H)
        self.linear2 = torch.nn.Linear(H, D_out)

コンストラクタで定義された線形結合層をつかってモデルの予測値y_predを計算します。

テンソルの演算式は、forwardオブジェクトで自由に定義できます。

    def forward(self, x):
        h_relu = self.linear1(x).clamp(min=0)
        y_pred = self.linear2(h_relu)
        return y_pred

5. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力xと予測したいyを乱数で定義します。

# Create random input and output data
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

6. ニューラルネットワークのモデルを構築

先程作成したニューラルネットワークモデルのクラスTwoLayerNetをつかってモデルを定義します。

model = TwoLayerNet(D_in, H, D_out)

7. 損失関数の定義

二乗誤差をこのモデルの損失関数とします。

reductionのデフォルトはmeanですので、何も指定しなければtorch.nn.MSELossは平均二乗誤差を返します。

reduction=sumとした場合は、累積二乗誤差を算出します。

criterion = torch.nn.MSELoss(reduction='sum')

8. 最適化関数の定義

optimパッケージを使って最適化関数として確率勾配降下法(SGD: stochastic gradient descent)を定義します。

lrは学習率です。

optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)

9. 学習回数

学習回数は500回にします。

for t in range(500):

10. モデルへのデータ入出力 (Forward pass)

定義したニューラルネットワークモデルmodelへデータxを入力し、予測値y_predを取得します。

    y_pred = model(x)

11. 損失(loss)の計算

定義した損失関数SGDで予測値y_predと真値yとの間の損失を計算します。

    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

12. 勾配の初期化

逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。

    optimizer.zero_grad()

13. 勾配の計算

backwardメソッドでモデルパラメータ(Weight)の勾配を算出します。

    loss.backward()

パラメータ(Weight)の更新

stepメソッドでモデルのパラメータを更新します。

    optimizer.step()

14. 実行

以下のコードを8_class_model.pyとして保存します。

14.1. 8_class_model.py

import torch


class TwoLayerNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(TwoLayerNet, self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H)
        self.linear2 = torch.nn.Linear(H, D_out)

    def forward(self, x):
        h_relu = self.linear1(x).clamp(min=0)
        y_pred = self.linear2(h_relu)
        return y_pred


# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Construct our model by instantiating the class defined above
model = TwoLayerNet(D_in, H, D_out)

# Construct our loss function and an Optimizer.
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(500):
    # Forward pass: Compute predicted y by passing x to the model
    y_pred = model(x)

    # Compute and print loss
    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # Zero gradients, perform a backward pass, and update the weights.
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

コードを保存したら、実行してみましょう。
In:

python3 8_class_model.py 

Out:

99 2.5070412158966064
199 0.04455046355724335
299 0.0011508199386298656
399 3.597284376155585e-05
499 1.361027443635976e-06

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

15. 終わりに

今回は、クラスを使ってニューラルネットワークモデルを定義しました。

より複雑なモデルを構築する場合には、コンストラクタに線形結合層、畳み込み層、プーリング層などのコンポーネントを増やし、それらをforwardオブジェクトで、自由に組み替えてみてください。

【PyTorch】サンプル⑦ 〜 optim パッケージ 〜


1. 目的
2. 前準備
3. optim パッケージ
4. PyTorchのインポート
5. 使用するデータ
6. ニューラルネットワークのモデルを定義
7. 損失(loss)の定義
8. 学習パラメータ
9. 最適化関数(optimizer)の定義
10. 学習回数
11. モデルへのデータ入出力 (Forward pass)
12. 損失(loss)の計算
13. 勾配の初期化
14. 勾配の計算
15. パラメータ(Weight)の更新
16. 実行
16.1. 7_optim_package.py


1. 目的

  • PyTorch: optimを参考にPyTorchのoptimパッケージを使って最適化関数(optimizer)を定義する。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. optim パッケージ

optimは、最適化アルゴリズムの定義に用います。

これまでは、モデルのパラメータ(weight)を更新するために、自分の手で確率勾配降下法(SGD: stochastic gradient descent)のコードを作っていました。

今回のチュートリアルでは、optimパッケージを使ってパラメータを更新する最適化関数(optimizer)を定義していきます。

optimパッケージには、SGD+momentum、RMSProp, Adamなどディープラーニング界でよく使われる最適化アルゴリズムが複数あります。

4. PyTorchのインポート

import torch

5. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

# Create random input and output data
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

6. ニューラルネットワークのモデルを定義

前回のnnパッケージのチュートリアルと同じように、ニューラルネットワークのモデルをnnパッケージを用いて定義します。

input > Linear(線型結合) > ReLU(活性化関数) > Linear(線型結合) > outputの順に層を積み重ねます。

model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

7. 損失(loss)の定義

二乗誤差をnnパッケージを用いて計算します。

reductionのデフォルトはmeanですので、何も指定しなければtorch.nn.MSELossは平均二乗誤差を返します。

reduction=sumとした場合は、累積二乗誤差を算出します。

loss_fn = torch.nn.MSELoss(reduction='sum')

8. 学習パラメータ

学習率learning_rate1e-4とします。

learning_rate = 1e-4

9. 最適化関数(optimizer)の定義

最適化関数の定義は、torch.optimを使って簡単にできます。

このチュートリアルでは、最適化関数としてAdamを選択しています。

torch.optim.Adamの引数は、モデルのパラメータmodel.parameters()と学習率lr=learning_rateです。

optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

10. 学習回数

学習回数を500回とします。

for t in range(500):

11. モデルへのデータ入出力 (Forward pass)

定義したニューラルネットワークモデルmodelへデータxを入力し、予測値y_predを取得します。

    y_pred = model(x)

12. 損失(loss)の計算

定義した損失関数で予測値y_predと真値yとの間の損失を計算します。

    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

13. 勾配の初期化

逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。

    optimizer.zero_grad()

(参考) optimパッケージを使わない場合、以下のように記述していました。

    model.zero_grad()

14. 勾配の計算

backwardメソッドでモデルパラメータ(Weight)の勾配を算出します。

    loss.backward()

15. パラメータ(Weight)の更新

stepメソッドでモデルのパラメータを更新します。

    optimizer.step()

(参考) optimパッケージを使わない場合、以下のように記述していました。

    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate param.grad

16. 実行

以下のコードを7_optim_package.pyとして保存します。

16.1. 7_optim_package.py

import torch

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Use the nn package to define our model and loss function.
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)
loss_fn = torch.nn.MSELoss(reduction='sum')

# Use the optim package to define an Optimizer that will update the weights of
# the model for us.
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
    # Forward pass: compute predicted y by passing x to the model.
    y_pred = model(x)

    # Compute and print loss.
    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # Before the backward pass, use the optimizer object to zero all of the
    # gradients for the variables it will update (which are the learnable
    # weights of the model).
    optimizer.zero_grad()

    # Backward pass: compute gradient of the loss with respect to model
    # parameters
    loss.backward()

    # Calling the step function on an Optimizer makes an update to its
    # parameters
    optimizer.step()

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

In:

python3 7_optim_package.py 

Out:

99 54.53140640258789
199 0.6615686416625977
299 0.004912459757179022
399 3.133853169856593e-05
499 1.0647939063801459e-07

【PyTorch】サンプル⑥ 〜 nn パッケージ 〜


1. 目的
2. 前準備
3. nn パッケージ
4. PyTorchのインポート
5. 使用するデータ
6. ニューラルネットワークのモデルを定義
7. 損失(loss)の定義
8. 学習パラメータ
9. モデルへのデータ入出力 (Forward pass)
10. 損失(loss)の計算
11. 勾配の初期化
12. 勾配の計算
13. パラメータ(Weight)の更新
14. 実行
14.1. 6_nn_package.py
15. 終わりに


1. 目的

  • PyTorch: nnを参考にPyTorchのnnパッケージを扱う。
  • nnパッケージの便利さを感じる。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. nn パッケージ

nnは、ニューラルネットワークの構築に用いる。

PyTorchの自動微分autogradによって、計算グラフやパラメータの勾配を簡単に計算することができます。ですが、自動微分だけで複雑なニューラルネットワークを定義するのは困難です。そこで活躍するのがnnパッケージです。

nnパッケージを用いて、ニューラルネットワークを一つのモジュールとして定義することができます。

4. PyTorchのインポート

import torch

5. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

# Create random input and output data
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

6. ニューラルネットワークのモデルを定義

ニューラルネットワークのモデルをnnパッケージを用いて定義します。

定義の仕方は、大きく2つありますがここでは一番簡単なtorch.nn.Sequentialを使います。

作り方は簡単で、任意の層を積み重ねていくだけです。この例では、input > Linear(線型結合) > ReLU(活性化関数) > Linear(線型結合) > outputの順に層が積み重なっています。

model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

7. 損失(loss)の定義

二乗誤差もnnパッケージを用いて計算することができます。

reductionのデフォルトはmeanですので、何も指定しなければtorch.nn.MSELossは平均二乗誤差を返します。

reduction=sumとした場合は、累積二乗誤差を算出します。

loss_fn = torch.nn.MSELoss(reduction='sum')

(参考) nnパッケージを使う前は以下のように記述していた。

    loss = (y_pred - y).pow(2).sum()

8. 学習パラメータ

学習率を1e-4として、学習回数を500回とします。

learning_rate = 1e-4
for t in range(500):

9. モデルへのデータ入出力 (Forward pass)

定義したニューラルネットワークモデルへデータxを入力し、予測値y_predを取得します。

y_pred = model(x)

(参考) nnパッケージを使う前は以下のように記述していた。

    y_pred = x.mm(w1).clamp(min=0).mm(w2)

10. 損失(loss)の計算

定義した損失関数で予測値y_predと真値yとの間の損失を計算します。

    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

11. 勾配の初期化

逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。

    model.zero_grad()

12. 勾配の計算

backwardメソッドでモデルパラメータ(Weight)の勾配を算出します。

    loss.backward()

13. パラメータ(Weight)の更新

確率勾配降下法(SGD: stochastic gradient descent)で、Weightを更新する。

    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate param.grad

(参考) nnパッケージを使う前は以下のように記述していた。

    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

14. 実行

以下のコードを6_nn_package.pyとして保存します。

14.1. 6_nn_package.py

import torch

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Use the nn package to define our model as a sequence of layers. 
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

# The nn package also contains definitions of popular loss functions; in this
# case we will use Mean Squared Error (MSE) as our loss function.
loss_fn = torch.nn.MSELoss(reduction='sum')

learning_rate = 1e-4
for t in range(500):
    # Forward pass: compute predicted y by passing x to the model. 
    y_pred = model(x)

    # Compute and print loss.
    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # Zero the gradients before running the backward pass.
    model.zero_grad()

    # Backward pass: compute gradient of the loss with respect to all the learnable
    loss.backward()

    # Update the weights using gradient descent.
    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate param.grad

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 6_nn_package.py 
99 2.5003600120544434
199 0.06977272033691406
299 0.003307548351585865
399 0.00018405442824587226
499 1.1299152902211063e-05

15. 終わりに

多層のニューラルネットワークには、膨大な量のパラメータが存在しています。

nnパッケージを用いる前は、各パラメータごとに勾配計算やパラメータの更新などをしていましたが、それでは記述が困難です。

nnパッケージを用いることで、楽に勾配計算やパラメータの更新等が実行できることが感じてもらえたら嬉しいです(^^)!。

【PyTorch】サンプル⑤ 〜Static Graphs(静的グラフ)〜


1. 目的
2. 前準備
3. 静的グラフ動的グラフ
4. パッケージのインポート
5. 使用するデータとプレースフォルダplaceholdersの用意
6. 重み付けの初期化
7. データ入力 (Forward pass)
8. 損失の計算
9. 重み(Weight)の勾配計算
10. 重み(w)の更新
11. 計算グラフの実行
12. 実行
12.1. 5_static_graphs.py


1. 目的

Deep Learning(ディープラーニング)で、よく使われるTensorFlowを使って計算グラフの設計からニューラルネットワークの学習をする。

PyTorchが採用している動的グラフを理解するために、静的グラフの代表であるTensorFlowに触れる。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. 静的グラフ動的グラフ

TensorFlowとPyTorchの大きな違いは、TensorFlowが静的計算グラフ(define-by-run)を採用しているのに対し、PyTorchは動的計算グラフ(define-and-run)を使用している点です。

静的グラフと動的グラフの説明についてはこちらをご覧ください。

簡潔に言うと、静的グラフは、事前にネットワーク構造を決めてから学習・推定を実行します。

これに対し、動的グラフは、ネットワーク構造を定義する時点でネットワーク構造を固定する必要はなく、ネットワークに入力されるデータの様子をみて、ネットワークを構造を適宜変えることができます。故に、動的な計算グラフになります。

この動的グラフにより、柔軟な学習や推測が可能となります。

4. パッケージのインポート

TensorFlowとNumPyをimportする。

import tensorflow as tf
import numpy as np

5. 使用するデータとプレースフォルダplaceholdersの用意

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力値xと予測したい値yの入れ物tf.placeholdersを用意する。

x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))

6. 重み付けの初期化

乱数random_normalメソッドを使って重みwを初期化します。

w1 = tf.Variable(tf.random_normal((D_in, H)))
w2 = tf.Variable(tf.random_normal((H, D_out)))

7. データ入力 (Forward pass)

入力xと重みw1を掛け算matmulすることで重み付けをしますh

重み付けした値(h)の要素から、maximum(h,tf.zeros(1))で、0以上のものは残し、0以下のものは0とします。

最後に、重みw2を掛け合わせてmatmul重み付けします。この値がモデルの予測値y_predとなります。

h = tf.matmul(x, w1)
h_relu = tf.maximum(h, tf.zeros(1))
y_pred = tf.matmul(h_relu, w2)

8. 損失の計算

モデルが予測した値y_predと答えyとの間の二乗誤差を計算しこれを損失lossとします。

tf.reduce_sumは、テンソルの足し算総和の計算をします。

loss = tf.reduce_sum((y - y_pred) *2.0)

9. 重み(Weight)の勾配計算

これより先は、モデルが予測した値y_predと答えyを見比べて、正しく答えyを予測できるようにモデルのパラメータを更新していきます。

具体的には、重みw1, w2の勾配(grad_w1, grad_w2)を計算します。

TensorFlowでは、gradientsメソッドで勾配が計算できます。

grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])

10. 重み(w)の更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。自動微分を用いた場合のパラメータの更新は、torch.no_grad()で対象のパラメータをくくることで計算できます。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。

weight = weight - learning_rate gradient

今回は、学習率を1e-6、新しい重みw1, w2new_w1new_w2にして、SGDを用いて重みを更新します。

learning_rate = 1e-6
new_w1 = w1.assign(w1 - learning_rate grad_w1)
new_w2 = w2.assign(w2 - learning_rate grad_w2)

11. 計算グラフの実行

設計した計算グラフをこのコード部分で実行する。

with tf.Session() as sess:
    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)
    for t in range(500):
        # Execute the graph many times.
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

TensorFlowで、計算グラフを実行するには、with tf.Session() as sess:でSessionオブジェクトを作成する。

with tf.Session() as sess:

重みw1w2を初期化するため、Sessionをrunする。

    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

ニューラルネットワークに入力するデータx_valueとニューラルネットワークが予測すべき値y_valueを乱数np.random.randn`で決める。

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)

今回は、学習回数を500回とする。

各学習ごとに、損失関数loss、更新された重みw1w2から損失値(二乗誤差)を計算する。

100回学習するごとに、学習回数tと二乗誤差loss_valueを出力する。

    for t in range(500):
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

12. 実行

以下のコードを5_static_graphs.pyとして保存します。

12.1. 5_static_graphs.py

import tensorflow as tf
import numpy as np

# First we set up the computational graph:

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create placeholders for the input and target data; these will be filled
# with real data when we execute the graph.
x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))

# Create Variables for the weights and initialize them with random data.
# A TensorFlow Variable persists its value across executions of the graph.
w1 = tf.Variable(tf.random_normal((D_in, H)))
w2 = tf.Variable(tf.random_normal((H, D_out)))

# Forward pass
h = tf.matmul(x, w1)
h_relu = tf.maximum(h, tf.zeros(1))
y_pred = tf.matmul(h_relu, w2)

# Compute loss using operations on TensorFlow Tensors
loss = tf.reduce_sum((y - y_pred) *2.0)

# Compute gradient of the loss with respect to w1 and w2.
grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])

# Update the weights using gradient descent.
learning_rate = 1e-6
new_w1 = w1.assign(w1 - learning_rate grad_w1)
new_w2 = w2.assign(w2 - learning_rate grad_w2)

# actually execute the graph.
with tf.Session() as sess:
    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)
    for t in range(500):
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーモデルの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 5_static_graphs.py
99 1597.0376
199 16.325699
299 0.24026018
399 0.0046504317
499 0.00029210464

【PyTorch】サンプル④ 〜Defining New autograd Functions(自動微分関数の定義)〜


1. 目的
2. 前準備
3. PyTorchのインポート
4. クラスの定義
5. データタイプとデバイスの選択
6. 使用するデータ
7. 重み付けの初期化
8. 学習
9. データの入力 (forward)
10. 損失の計算
11. 重み(Weight)の勾配計算
12. 重みの更新
13. 実行
13.1. 4_autograd_func.py


1. 目的

PyTorchのチュートリアルPyTorch: Defining New autograd Functionsを参考にPyTorchテンソル(tensor)と自動微分(autograd)を使って、損失(loss)や重み(weight)の計算をする。

前回の、【PyTorch】サンプル③ 〜TENSORS AND AUTOGRAD(テンソルと自動微分)〜では、自動微分の基本的な扱いをご紹介しました。

今回は、前回使用した自動微分のコードを関数化してみましょう。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. PyTorchのインポート

import torch

4. クラスの定義

自動微分のコード部分を以下のようなMyReLUというクラスで定義します。

class MyReLU(torch.autograd.Function):

    @staticmethod
    def forward(ctx, input):
        ctx.save_for_backward(input)
        return input.clamp(min=0)

    @staticmethod
    def backward(ctx, grad_output):
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input

ここでは、torch.autograd.Functionを継承したサブクラスでMyReLUを定義します。

class MyReLU(torch.autograd.Function):

こちらが、パーセプトロンにデータをインプット(forward)する部分です。

@staticmethodはスタティックメソッドといって、そのクラスでしか使用しない関数を定義する時に使います。

また、スタティックメソッドの特徴として、クラスでよくあるselfを第一引数として取りません。

ctxは、逆伝播(backward)計算の際に用いる隠しオブジェクトです。

ctx.save_for_backwardメソッドを使うことで逆伝播計算に用いるオブジェクトのキャッシュを取って置くことができる。

input.clamp(min=0)で、入力されたinputの要素の中で0以下は0、0以上はそのままの要素の値として出力を返す。

    @staticmethod
    def forward(ctx, input):
        ctx.save_for_backward(input)
        return input.clamp(min=0)

この部分が、逆伝播計算の部分。

ctx.saved_tensorsで、パーセプトロンに入力された値inputを取り出します。

パーセプトロンからの出力勾配をgrad_output.clone()でコピーする。

    @staticmethod
    def backward(ctx, grad_output):
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input
`

このように、PyTorchにおける自動微分関数の定義では、`forward`と`backward`をクラスのメソッドとして定義します。

###  5. <a name='-3'></a>データタイプとデバイスの選択
これから作成するテンソルのデータ型`dtype`を`float`にします。

テンソル計算をするデバイスは、`torch.device`で指定することができます。
今回は、CPUを使用することにします。
[python]
dtype = torch.float
device = torch.device("cpu")

GPUを使う方は、deviceの部分を以下のコードに変えて下さい。

device = torch.device("cuda:0") # Uncomment this to run on GPU

6. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

PyTorchテンソルを定義する場合には、どのデバイスでdevice、どのデータ型dtypeかを指定することができます。

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

7. 重み付けの初期化

乱数を使って重みを初期化します。

自動微分をしたい場合には、初期化する時点でrequires_gradのフラグをTrueにしておきます。デフォルトはrequires_grad=Falseです。

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

8. 学習

学習率を1e-6として、学習回数を500回とします。

learning_rate = 1e-6
for t in range(500):

9. データの入力 (forward)

データをパーセプトロンに入力して、パーセプトロンの予測値y_predを計算します。

以前の自動微分のサンプルでは、

    y_pred = x.mm(w1).clamp(min=0).mm(w2)

このように記述しましたが、今回は自動微分の関数をMyReLUとしてに定義してあるので、上のコードをMyReLUを用いて以下のように書き換えます。

    # Forward pass: compute predicted y
    relu = MyReLU.apply
    y_pred = relu(x.mm(w1)).mm(w2)

10. 損失の計算

パーセプトロンが予測した値(y_pred)と答え(y)との間の二乗誤差を計算しこれを損失(loss)とします。

損失の値を、tensor配列からスカラー値で取得したい場合には、item()メソッドを用います。

各学習回数ごとに、学習回数(t)と二乗誤差(loss)を表示します。

今回は、結果を見やすくするためにif t % 100 == 99:の部分で、学習回数100回ごとに結果を出力します。
やっていることは、学習回数(t)を100で割ったときのあまりが99であるときにprint(t, loss)を実行です。

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

11. 重み(Weight)の勾配計算

これより先は、パーセプトロンが予測した値(y_pred)と答え(y)を見比べて、正しく答え(y)を予測できるようにパーセプトロンのパラメータを更新していきます。

    loss.backward()

12. 重みの更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。自動微分を用いた場合のパラメータの更新は、torch.no_grad()で対象のパラメータをくくることで計算できます。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。

weight = weight - learning_rate gradient

SGDを用いてパラメータを更新するには、このように記述します。

一度更新した、パラメータはgrad.zero()でゼロに初期化します。

    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

13. 実行

以下のコードを4_autograd_func.pyとして保存します。

13.1. 4_autograd_func.py

import torch

dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold input and outputs.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# Create random Tensors for weights.
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y using operations on Tensors
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    # Compute and print loss using operations on Tensors.
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    # Use autograd to compute the backward pass.
    loss.backward()

    # Manually update weights using gradient descent. Wrap in torch.no_grad()
    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 4_autograd_func.py 
99 763.6893310546875
199 9.174347877502441
299 0.22351056337356567
399 0.007267479319125414
499 0.0004901751526631415

【PyTorch】サンプル③ 〜TENSORS AND AUTOGRAD(テンソルと自動微分)〜


1. 目的
2. 前準備
3. 自動微分(AUTOGRAD)
4. PyTorchのインポート
5. データタイプとデバイスの選択
6. 使用するデータ
7. 重み付けの初期化
8. 学習
9. データの入力
10. 損失の計算
11. 重み(Weight)の勾配計算
12. 重みの更新
13. 実行
13.1. 3_autograd.py


1. 目的

PyTorchのチュートリアルPyTorch: Tensors and autogradを参考にPyTorchテンソル(tensor)と自動微分(autograd)を使って、損失(loss)や重み(weight)の計算をする。

これまでは、PyTorchに実装されている自動微分機能を使わずにニューラルネットワークのパラメータの勾配を計算していましたが、PyTorchの自動微分(autograd)機能を使うとパラメータの勾配計算簡単にすることができます。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. 自動微分(AUTOGRAD)

PyTorchにおけるテンソルは計算グラフでは、ノード(node)と表現されます。

例えば、テンソルxx.requires_grad=Trueとフラグを設定すると、x.gradでxの勾配を計算することができます。

4. PyTorchのインポート

import torch

5. データタイプとデバイスの選択

これから作成するテンソルのデータ型dtypefloatにします。

テンソル計算をするデバイスは、torch.deviceで指定することができます。
今回は、CPUを使用することにします。

dtype = torch.float
device = torch.device("cpu")

GPUを使う方は、deviceの部分を以下のコードに変えて下さい。

device = torch.device("cuda:0") # Uncomment this to run on GPU

6. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

PyTorchテンソルを定義する場合には、どのデバイスでdevice、どのデータ型dtypeかを指定することができます。

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

7. 重み付けの初期化

乱数を使って重みを初期化します。

注目してほしい点は、requires_grad=Trueです。
自動微分をしたい場合には、初期化する時点でrequires_gradのフラグをTrueにしておきます。デフォルトはrequires_grad=Falseです。

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

8. 学習

学習率を1e-6として、学習回数を500回とします。

learning_rate = 1e-6
for t in range(500):

9. データの入力

二次元テンソルの入力(x)と重み(w1)をmmで掛け算することで重み付けをします(h)。(一次元テンソルの積は、dotで計算できます。)

重み付けした値の要素から、clamp(min=~0)で、0以上の要素は残し、0以下のものは0とします。

最後に、重み(w2)を掛け合わせて重み付けしますmm(w2)
この値がパーセプトロンの予測値(y_pred)となります。

    # Forward pass: compute predicted y
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

10. 損失の計算

パーセプトロンが予測した値(y_pred)と答え(y)との間の二乗誤差を計算しこれを損失(loss)とします。

損失の値を、tensor配列からスカラー値で取得したい場合には、item()メソッドを用います。

各学習回数ごとに、学習回数(t)と二乗誤差(loss)を表示します。

今回は、結果を見やすくするためにif t % 100 == 99:の部分で、学習回数100回ごとに結果を出力します。
やっていることは、学習回数(t)を100で割ったときのあまりが99であるときにprint(t, loss)を実行です。

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

11. 重み(Weight)の勾配計算

これより先は、パーセプトロンが予測した値(y_pred)と答え(y)を見比べて、正しく答え(y)を予測できるようにパーセプトロンのパラメータを更新していきます。

NumPyを用いたサンプルPyTorchを用いたサンプルでは、ガリガリ勾配の計算式を書いていましたが、自動微分を使えば、一行で勾配を計算できます

    loss.backward()

12. 重みの更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。自動微分を用いた場合のパラメータの更新は、torch.no_grad()で対象のパラメータをくくることで計算できます。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。

weight = weight - learning_rate gradient

SGDを用いてパラメータを更新するには、このように記述します。

一度更新した、パラメータはgrad.zero()でゼロに初期化します。

    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

13. 実行

以下のコードを3_autograd.pyとして保存します。

13.1. 3_autograd.py

import torch

dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold input and outputs.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# Create random Tensors for weights.
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y using operations on Tensors
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    # Compute and print loss using operations on Tensors.
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    # Use autograd to compute the backward pass.
    loss.backward()

    # Manually update weights using gradient descent. Wrap in torch.no_grad()
    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 3_autograd.py 
99 743.60400390625
199 7.154838562011719
299 0.13969427347183228
399 0.0036201069597154856
499 0.000234781633480452

【PyTorch】サンプル② 〜TENSOR (テンソル) 〜


1. 目的
2. 前準備
3. TENSOR(テンソル)
4. PyTorchのインポート
5. データタイプとデバイスの選択
6. 使用するデータ
7. 重み付けの初期化
8. 学習
9. データの入力
10. 損失の計算
11. 重み(Weight)の勾配計算
12. 重みの更新
13. 実行
13.1. 2_tensor.py


### 1. 目的
PyTorchのチュートリアルPyTorch: Tensorsを参考にPyTorch Tensor(テンソル)を使って、損失(loss)や重み(weight)の計算をする。

PyTorchの特徴の一つである、テンソルとNumPyの違いを手を動かしながら感じる。

NumPyを用いた例は、こちらから。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. TENSOR(テンソル)

PyTorchのtensor(テンソル)は、基本的にはnumpy配列と同じです。

しかし、PyTorchのテンソルは、deep learning(深層学習)や計算グラフ、勾配といった概念に合わせた配列です。

PyTorchテンソルだけがCPUとGPUのどちらでも計算が実行でき、これが、numpy配列とPyTorchテンソルの最も大きな違いです。

4. PyTorchのインポート

import torch

5. データタイプとデバイスの選択

これから作成するテンソルのデータ型dtypefloatにします。

テンソル計算をするデバイスは、torch.deviceで指定することができます。
今回は、CPUを使用することにします。

dtype = torch.float
device = torch.device("cpu")

GPUを使う方は、deviceの部分を以下のコードに変えて下さい。

device = torch.device("cuda:0") # Uncomment this to run on GPU

6. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

PyTorchテンソルを定義する場合には、どのデバイスでdevice、どのデータ型dtypeかを指定することができます。

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

7. 重み付けの初期化

乱数を使って重みを初期化します。

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

8. 学習

学習率を1e-6として、学習回数を500回とします。

learning_rate = 1e-6
for t in range(500):

9. データの入力

二次元テンソルの入力(x)と重み(w1)をmmで掛け算することで重み付けをします(h)。(一次元テンソルの積は、dotで計算できます。)

重み付けした値(h)の要素から、clamp(min=~0)で、0以上のものは残し、0以下のものは0とします。numpyでは、numpy.maximum(h,0)と書きます。

最後に、重み(w2)を掛け合わせて重み付けしますmm(w2)
この値がパーセプトロンの予測値(y_pred)となります。

    # Forward pass: compute predicted y
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

10. 損失の計算

パーセプトロンが予測した値(y_pred)と答え(y)との間の二乗誤差を計算しこれを損失(loss)とします。
numpyでは、二乗計算をnumpy.squareで実行していましたが、PyTorchテンソルでの二乗計算はpow(2)と記述します。

損失の値を、tensor配列からスカラー値で取得したい場合には、item()メソッドを用います。

各学習回数ごとに、学習回数(t)と二乗誤差(loss)を表示します。

今回は、結果を見やすくするためにif t % 100 == 99:の部分で、学習回数100回ごとに結果を出力します。
やっていることは、学習回数(t)を100で割ったときのあまりが99であるときにprint(t, loss)を実行です。

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

11. 重み(Weight)の勾配計算

これより先は、パーセプトロンが予測した値(y_pred)と答え(y)を見比べて、正しく答え(y)を予測できるようにパーセプトロンのパラメータを更新していきます。

まず、重み(w1, w2)の勾配(grad_w1, grad_w2)を計算します。

numpyと違う点は、表のとおりです。

PyTorch NumPy
配列の転置 .t() .T
二次元配列の掛け算 .mm() .dot()
配列のコピー .clone() .copy()
    grad_y_pred = 2.0 (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

12. 重みの更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。
weight = weight - learning_rate gradient

SGDは、以下のコードで実行できます。
ここの部分は、numpyもPyTorchテンソルも同じコードです。

    # Update weights using gradient descent
    w1 -= learning_rate grad_w1
    w2 -= learning_rate grad_w2

13. 実行

以下のコードを2_tensor.pyとして保存します。

13.1. 2_tensor.py

import torch


dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

    # Backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 2.0 (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

    # Update weights using gradient descent
    w1 -= learning_rate grad_w1
    w2 -= learning_rate grad_w2

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 2_tensor.py 
99 306.79400634765625
199 0.4559670686721802
299 0.0014809096464887261
399 6.634114834014326e-05
499 1.7021899111568928e-05