PyTorchの使い方を調べた

自分用のメモとして使い方をざっとまとめてみました


# 1. 3つのプロジェクト


PyTorchはNeural Networkがよく使われる文章、画像、音声の3つの用途それぞれについてのプロジェクトに分けられているようです。

https://pytorch.org/text/stable/index.html

https://pytorch.org/vision/stable/index.html

https://pytorch.org/audio/stable/index.html


# 2. datasets


上記の3つのプロジェクトごとにdatasetをダウンロード出来るようにしてくれています。

https://pytorch.org/vision/stable/datasets.html

https://torchtext.readthedocs.io/en/latest/datasets.html

https://pytorch.org/audio/stable/datasets.html






















# 3. インストール


公式サイトにインストールコマンドが載っています
https://pytorch.org/get-started/locally/

上記ページを開くと環境に併せたビルドをcondaやpipなどでインストールするコマンドを表示してくれます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/9401ec90-1112-7ee5-c9e0-be2baeb48392.png)

















# 4. 使い方


このページに沿ってやっていきます
https://pytorch.org/tutorials/beginner/basics/quickstart_tutorial.html


## 4-1. 準備


```python:
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose
import matplotlib.pyplot as plt
```


## 4-2. データセットの読み込み


FashionMNISTを使っていきます
train=True にするとtrainデータが得られます


```python:
# Download training data from open datasets.
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
)

# Download test data from open datasets.
test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor(),
)
```


datasets.FashionMNIST()の返り値はクラスインスタンスになっているようです


```python:
>>> training_data
Dataset FashionMNIST
    Number of datapoints: 60000
    Root location: data
    Split: Train
    StandardTransform
Transform: ToTensor()
```


入力データはdataプロパティ、出力データはtargetsプロパティに入っています



```python:入力データ
>>> test_data.data
tensor([[[0, 0, 0,  ..., 0, 0, 0],
         [0, 0, 0,  ..., 0, 0, 0],
         [0, 0, 0,  ..., 0, 0, 0],
         ...,
         [0, 0, 0,  ..., 0, 0, 0],
         [0, 0, 0,  ..., 0, 0, 0],
         [0, 0, 0,  ..., 0, 0, 0]],

         ...,

        [[0, 0, 0,  ..., 0, 0, 0],
         [0, 0, 0,  ..., 0, 0, 0],
         [0, 0, 0,  ..., 0, 0, 0],
         ...,
         [0, 0, 0,  ..., 0, 0, 0],
         [0, 0, 0,  ..., 0, 0, 0],
         [0, 0, 0,  ..., 0, 0, 0]]], dtype=torch.uint8)
```


```python:出力データ
>>> test_data.targets
tensor([9, 2, 1,  ..., 8, 1, 5])
```


入力データは28x28サイズのグレースケール画像になっています


```python
fig, ax = plt.subplots(1, 5, figsize=(22, 4))
for k in range(5):
    ax[k].imshow(test_data.data[k], cmap='gray')
    ax[k].set_xticks([])
    ax[k].set_yticks([])
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/24b4df7b-4705-746c-3351-a99a4cf65478.png)








## 4-3. DataLoaderをつくる


iterableなDataLoaderをつくって学習します


```python:
batch_size = 64

# Create data loaders.
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

for X, y in test_dataloader:
    print('Shape of data [N, H, W]: ', test_data.data.shape)
    print("Shape of X [N, C, H, W]: ", X.shape)
    print("Shape of y: ", y.shape, y.dtype)
    break
```


上記ではbatch_sizeを64に設定していますので1バッチ分のXが64枚分出てきています


```python:
Shape of data [N, H, W]:  torch.Size([10000, 28, 28])
Shape of X [N, C, H, W]:  torch.Size([64, 1, 28, 28])
Shape of y:  torch.Size([64]) torch.int64
```


DataLoaderにすると自動的にチャンネルを作ってくれるのでshapeが変わっていますね


## 4-4. GPUかCPUか


PyTorchではデータやモデルをCPUで扱うかGPUで扱うかをtoメソッドを使って明示的に指定します。to('cuda')すればGPUに、to('cpu')すればCPUにアサインされます。modelがGPU、データがCPUみたいに混在した状態で扱おうとするとエラー停止しますので注意が必要です。

PyTorchがGPUを使用可能かどうかをtorch.cuda.is_available()で調べられますので、以下を実行しておいてto(device)すればGPUが使用可能か否かで自動的に動作を変えるという書き方が出来ます。


```python:
# Get cpu or gpu device for training.
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))
```


## 4-5. モデルをつくる


この例では全結合のみで構成されるNeural Networkモデルを作成します。

以下は入力が28x28の2次元データですのでnn.Flatten()で1次元に並べ替えて、512ノードの隠れ層を2層通してから出力段に繋げる構成になっています。FashionMNISTは10クラス分類ですのでcross entropy errorを使う為に出力は10ノードになっています。


```python:
# Define model
class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
            nn.ReLU()
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)
```


上記ではsoftmaxがないのでおかしいと思われるかも知れませんが、PyTorchではsoftmaxがオーバーフローして結果が不安定になるのを防ぐ為にlossの算出時にまとめてsoftmaxするようにしています。詳しくは以下を参照してください。
https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html














## 4-6. 損失関数とoptimizerを設定する


softmaxしてcross entropy誤差を算出するnn.CrossEntropyLoss()を損失関数に、SGDをoptimizerにします。softmax済みの出力をnn.CrossEntropyLoss()に食わせるとlogが2回掛かってしまって変化が小さくなり過ぎるので意図したように学習が進まない筈ですから注意します。


```python:
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
```


使用可能な損失関数
https://pytorch.org/docs/stable/nn.html#loss-functions

使用可能なoptimizer
https://pytorch.org/docs/stable/optim.html


## 4-7. 学習用の関数と評価用の関数を作る


tensorflow.kerasの場合はモデルのインスタンスを作ってfit()するだけで学習が全自動で行われる書き方がスタンダードですが、PyTorchは以下のように自分で学習動作を書くのがスタンダードになっていて、学習プロセスの動作をちょこちょこ変えて実験するというのが気軽に出来るようになっています。


```python:学習用の関数
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)

        # Compute prediction error
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
```

torch.TensorはautoGradにより自動で微分が行われる仕様になっていて、自由度の高い書き方が出来るようになっているんだそうです。
https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html

これは学習の際には都合が良いんですが推定する際には必要ないので、その場合にはwith torch.no_grad()で明示的に無効にしてやります。


```python:評価用の関数
def test(dataloader, model):
    size = len(dataloader.dataset)
    model.eval()
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= size
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
```













## 4-8. 学習する


PyTorchはepoch管理も自分で書きます


```python:
epochs = 5
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model)
print("Done!")
```

```python:結果
Epoch 1
-------------------------------
loss: 2.308795  [    0/60000]
loss: 2.302261  [ 6400/60000]
loss: 2.289171  [12800/60000]
loss: 2.287153  [19200/60000]
loss: 2.266660  [25600/60000]
loss: 2.273998  [32000/60000]
loss: 2.258193  [38400/60000]
loss: 2.249706  [44800/60000]
loss: 2.246202  [51200/60000]
loss: 2.229442  [57600/60000]
Test Error: 
 Accuracy: 36.7%, Avg loss: 0.035078 

   ...

Epoch 5
-------------------------------
loss: 1.774351  [    0/60000]
loss: 1.843552  [ 6400/60000]
loss: 1.684049  [12800/60000]
loss: 1.775084  [19200/60000]
loss: 1.713003  [25600/60000]
loss: 1.710983  [32000/60000]
loss: 1.612547  [38400/60000]
loss: 1.593650  [44800/60000]
loss: 1.691809  [51200/60000]
loss: 1.610419  [57600/60000]
Test Error: 
 Accuracy: 46.0%, Avg loss: 0.026361 

Done!
```


## 4-9. 推定する


```python:
classes = [
    "T-shirt/top",
    "Trouser",
    "Pullover",
    "Dress",
    "Coat",
    "Sandal",
    "Shirt",
    "Sneaker",
    "Bag",
    "Ankle boot",
]

model = model.to('cpu')
model.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
    pred = model(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: "{predicted}", Actual: "{actual}"')
```


## 4-10. モデルのパラメータ


モデルの構成は以下で確認できます


```python:モデルの構成
>>> model.parameters
<bound method Module.parameters of NeuralNetwork(
  (flatten): Flatten()
  (linear_relu_stack): Sequential(
    (0): Linear(in_features=784, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=512, bias=True)
    (3): ReLU()
    (4): Linear(in_features=512, out_features=10, bias=True)
    (5): ReLU()
  )
)>
```


学習したweightやbiasは以下のようにして確認出来ます


```python:parameters()の中身を確認
>>> for param in model.parameters():
>>>    print(param)
Parameter containing:
tensor([[-0.0103, -0.0277, -0.0012,  ..., -0.0327,  0.0069,  0.0077],
        [-0.0131, -0.0320,  0.0250,  ...,  0.0132,  0.0125, -0.0227],
        ...,
        [-0.0266,  0.0209, -0.0313,  ...,  0.0115,  0.0292,  0.0354],
        [-0.0337, -0.0203,  0.0159,  ..., -0.0146, -0.0245,  0.0355]],
       device='cuda:0', requires_grad=True)
Parameter containing:
tensor([-0.0006, -0.0170, -0.0109,  0.0206, -0.0258, -0.0104,  0.0273, -0.0256,
         0.0164,  0.0151,  0.0255,  0.0077, -0.0127,  0.0343, -0.0034, -0.0269,
        -0.0269, -0.0181, -0.0285,  0.0134, -0.0067, -0.0300, -0.0027, -0.0158,
         0.0115, -0.0013, -0.0129,  0.0030,  0.0272,  0.0348, -0.0206,  0.0109,
         0.0046, -0.0308,  0.0043,  0.0196,  0.0031, -0.0061,  0.0323,  0.0238,
         0.0181, -0.0082, -0.0134, -0.0345,  0.0242, -0.0304, -0.0189,  0.0344,
        -0.0300, -0.0112, -0.0151,  0.0025, -0.0027,  0.0392, -0.0147,  0.0280,
        ...
```


同じ内容をstate_dict()から参照することも出来ます


```python:state_dict()で確認
>>> model.state_dict()
OrderedDict([('linear_relu_stack.0.weight',
              tensor([[-0.0103, -0.0277, -0.0012,  ..., -0.0327,  0.0069,  0.0077],
                      [-0.0131, -0.0320,  0.0250,  ...,  0.0132,  0.0125, -0.0227],
                      ...,
                      [-0.0266,  0.0209, -0.0313,  ...,  0.0115,  0.0292,  0.0354],
                      [-0.0337, -0.0203,  0.0159,  ..., -0.0146, -0.0245,  0.0355]],
                     device='cuda:0')),
             ('linear_relu_stack.0.bias',
              tensor([-0.0006, -0.0170, -0.0109,  0.0206, -0.0258, -0.0104,  0.0273, -0.0256,
                       0.0164,  0.0151,  0.0255,  0.0077, -0.0127,  0.0343, -0.0034, -0.0269,
                      -0.0269, -0.0181, -0.0285,  0.0134, -0.0067, -0.0300, -0.0027, -0.0158,
                       0.0115, -0.0013, -0.0129,  0.0030,  0.0272,  0.0348, -0.0206,  0.0109,
                      ...
```


## 4-11. モデルの保存と読み込み


torch.save()により上記のstate_dict()を保存すればパラメータ一式を保存することが出来ます


```python:保存
torch.save(model.state_dict(), "model.pth")
print("Saved PyTorch Model State to model.pth")
```


同じモデルを作っておいてtorch.load()で上記のpthファイルを読めば同じモデルを再現できます


```python:読み込み
model2 = NeuralNetwork()
model2.load_state_dict(torch.load("model.pth"))
```


## 4-12. 初期化


初期化に使える関数は以下にあります
https://pytorch.org/docs/master/nn.init.html


レイヤーを定義する際に引数で与えるやり方は出来ないようなので、一度定義しておいてから置き換えてやります。以下のようにすると対象の値を書き換えてくれます。


```python:Glorotの一様分布
nn.init.xavier_uniform_(model.linear_relu_stack[0].weight)
```


```python:Heの正規分布
nn.init.kaiming_normal_(model.linear_relu_stack[0].weight)
```


```python:0～1の一様分布
nn.init.uniform_(model.linear_relu_stack[0].bias, 0, 1)
```


初期化はcpuモードのtorch.Tensorで与えられるようで、GPUにアサインしたモデルを後から書き換えるとややこしいので、to('gpu')する前に書き換えちゃうのが良いと思います。


```python:
model = NeuralNetwork()

initializer1 = nn.init.xavier_uniform_  # Gromet uniform
initializer2 = nn.init.uniform_

initializer1(model.linear_relu_stack[0].weight)
initializer2(model.linear_relu_stack[0].bias)
initializer1(model.linear_relu_stack[2].weight)
initializer2(model.linear_relu_stack[2].bias)
initializer1(model.linear_relu_stack[4].weight)
initializer2(model.linear_relu_stack[4].bias)
model = model.to(device)
```


なお、nn.LinearはデフォルトでweightをHeの正規分布(init.kaiming_uniform_)で初期化、biasを一様分布(init.uniform_)で初期化しますので、これで問題ない場合はそのままでOKです。
https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/linear.py


## 4-13. パラメータを書きかえる


パラメータは各レイヤーオブジェクトが保持しています


```python:weight
>>> model.linear_relu_stack[0].weight
Parameter containing:
tensor([[ 0.0327, -0.0140,  0.0057,  ...,  0.0028, -0.0288, -0.0010],
        [-0.0104, -0.0136,  0.0318,  ...,  0.0161, -0.0176, -0.0022],
        ...,
        [ 0.0006, -0.0308,  0.0015,  ...,  0.0025,  0.0117,  0.0192],
        [ 0.0191, -0.0007, -0.0314,  ..., -0.0190,  0.0318,  0.0097]],
       device='cuda:0', requires_grad=True)
```


```python:bias
>>> model.linear_relu_stack[0].bias
Parameter containing:
tensor([-0.0231, -0.0011,  0.0320,  0.0004,  0.0071, -0.0201,  0.0232, -0.0280,
         0.0315,  0.0169,  0.0208,  0.0202,  0.0241, -0.0245, -0.0040,  0.0356,
        -0.0100, -0.0037,  0.0312, -0.0099,  0.0017, -0.0071,  0.0355, -0.0262,
        ...
```


torch.Tensorなので、これを直接書き換えることが出来ます。例えば学習済みのmodelのパラメータを新規に作成したmodel2に上書きして使うことが出来ます。


```python:
w1 = model.linear_relu_stack[0].weight
b1 = model.linear_relu_stack[0].bias
w2 = model.linear_relu_stack[2].weight
b2 = model.linear_relu_stack[2].bias
w3 = model.linear_relu_stack[4].weight
b3 = model.linear_relu_stack[4].bias

model2 = NeuralNetwork()
model2.linear_relu_stack[0].weight = w1
model2.linear_relu_stack[0].bias = b1
model2.linear_relu_stack[2].weight = w2
model2.linear_relu_stack[2].bias = b2
model2.linear_relu_stack[4].weight = w3
model2.linear_relu_stack[4].bias = b3

classes = [
    "T-shirt/top",
    "Trouser",
    "Pullover",
    "Dress",
    "Coat",
    "Sandal",
    "Shirt",
    "Sneaker",
    "Bag",
    "Ankle boot",
]

model2.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
    pred = model2(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: "{predicted}", Actual: "{actual}"')
```


```python:結果
Predicted: "Ankle boot", Actual: "Ankle boot"
```


同じ結果が得られました


# 5. datasetsとdataloaderの作り方


こちらの解説が分かりやすかったのでコードをお借りします
https://qiita.com/mathlive/items/2a512831878b8018db02


## 5-1. datasetsの作成


data要素に入力データ、label要素に出力データ、datanum要素にデータの長さを入れておいて、出力する際の挙動を__getitem__()に書けば最小構成のdatasetsが作れます。


```python:datasetsをつくる
class Mydatasets(torch.utils.data.Dataset):
    def __init__(self, transform = None):
        self.transform = transform

        self.data = [1, 2, 3, 4, 5, 6]
        self.label = [0, 1, 0, 1, 0, 1]

        self.datanum = 6

    def __len__(self):
        return self.datanum

    def __getitem__(self, idx):
        out_data = self.data[idx]
        out_label = self.label[idx]

        if self.transform:
            out_data = self.transform(out_data)

        return out_data, out_label

datasets = Mydatasets()
print('data: {}'.format(datasets.data))
print('label: {}\n'.format(datasets.label))
print(datasets.__getitem__(0))
print(datasets.__getitem__(1))
print(datasets.__getitem__(2))
```


```python:結果
data: [1, 2, 3, 4, 5, 6]
label: [0, 1, 0, 1, 0, 1]
(1, 0)
(2, 1)
(3, 0)
```


実用的にはdata, label, datanumを__init__()の引数で渡すようにした方が使いやすいかもしれません。


## 5-2. dataloaderの作成


torch.utils.data.DataLoader()が上記の__getitem__()を使ってdataloaderを作ってくれます。


```python:dataloaderをつくる
dataloader = DataLoader(datasets, batch_size=2)
for x, y in dataloader:
    print(x, y)
```

```python:結果
tensor([1, 2]) tensor([0, 1])
tensor([3, 4]) tensor([0, 1])
tensor([5, 6]) tensor([0, 1])
```


# 6. learning rate scheduler


## 6-1. 使い方


1epoch分の処理をやった後にscheduler.step()とする事でlearning rateが更新されます


```python:
>>> scheduler = ...
>>> for epoch in range(100):
>>>     train(...)
>>>     validate(...)
>>>     scheduler.step()
```


## 6-2. 実際の使い方


先程やったFashion MNISTの学習で使おうと思ったら以下のようになります


```python:
def train(dataloader, model, loss_fn, optimizer, scheduler):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)

        # Compute prediction error
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}] lr: {scheduler.get_last_lr()[0]:1.4f}")  # 変更

    scheduler.step()  # 追加

model = NeuralNetwork().to(device)
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-2)
scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.8)  # 追加

epochs = 5
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer, scheduler)  # 変更
    test(test_dataloader, model)
        
print("Done!")
```


```python:結果
Epoch 1
-------------------------------
loss: 2.300871  [    0/60000] lr: 0.0100
loss: 2.229759  [ 6400/60000] lr: 0.0100
loss: 2.042035  [12800/60000] lr: 0.0100
loss: 1.979453  [19200/60000] lr: 0.0100
loss: 1.622117  [25600/60000] lr: 0.0100
loss: 1.392005  [32000/60000] lr: 0.0100
loss: 1.534957  [38400/60000] lr: 0.0100
loss: 1.227475  [44800/60000] lr: 0.0100
loss: 1.359196  [51200/60000] lr: 0.0100
loss: 1.329959  [57600/60000] lr: 0.0100
Test Error: 
 Accuracy: 58.3%, Avg loss: 0.018586 

Epoch 2
-------------------------------
loss: 1.245046  [    0/60000] lr: 0.0095
loss: 1.233158  [ 6400/60000] lr: 0.0095
loss: 0.924072  [12800/60000] lr: 0.0095
loss: 1.149151  [19200/60000] lr: 0.0095
loss: 1.119033  [25600/60000] lr: 0.0095
loss: 0.951288  [32000/60000] lr: 0.0095
loss: 1.260948  [38400/60000] lr: 0.0095
loss: 1.062005  [44800/60000] lr: 0.0095
loss: 1.209858  [51200/60000] lr: 0.0095
loss: 1.129560  [57600/60000] lr: 0.0095
Test Error: 
 Accuracy: 66.1%, Avg loss: 0.016154 

Epoch 3
-------------------------------
loss: 1.050888  [    0/60000] lr: 0.0090
loss: 1.074153  [ 6400/60000] lr: 0.0090
loss: 0.801356  [12800/60000] lr: 0.0090
loss: 1.031100  [19200/60000] lr: 0.0090
loss: 1.044556  [25600/60000] lr: 0.0090
loss: 0.885438  [32000/60000] lr: 0.0090
loss: 1.167787  [38400/60000] lr: 0.0090
loss: 1.047352  [44800/60000] lr: 0.0090
loss: 1.199912  [51200/60000] lr: 0.0090
loss: 1.053414  [57600/60000] lr: 0.0090
Test Error: 
 Accuracy: 69.7%, Avg loss: 0.015347 
```


learning rateが指示通り減っているのが分かります


## 6-3. 挙動の確認


schedulerはいろいろあるので以下のコードで挙動を確認してみます


```python:
import torch
def check_lr(optimizer, scheduler):
    s, lr = [], []
    for epoch in range(100):
        optimizer.step()
        scheduler.step()
        s.append(scheduler.get_last_lr()[0])
        for param_group in optimizer.param_groups:
            lr.append(param_group['lr'])

    plt.figure(figsize=(4, 3))
    plt.plot(s)
    plt.plot(lr)
    plt.show()
    
model = [torch.nn.Parameter(torch.randn(2, 2, requires_grad=True))]
optimizer = torch.optim.SGD(model, 0.1)  # ここを書き換えて確認
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.5)

check_lr(optimizer, scheduler)
```


## 6-4. StepLR


step_sizeだけepochが進む度にlearning rateをgamma倍するschedulerです


```python:
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.5)
```


![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/e9e688a5-b46d-d2c2-a99a-27bc2ea18085.png)


## 6-3. ExponentialLR


epochごとにlearning rateをgamma倍するschedulerです


```python:
scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)
```


![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/d864ed68-de07-555a-ebe7-ea4b07190405.png)



## 6-4. CosineAnnealingWarmRestarts


この論文で紹介されているやり方です
https://arxiv.org/abs/1608.03983

T_0を周期とするコサインカーブで減衰して、あるところまで減衰したところで再び高いlearning rateに戻すような挙動により局所最適を脱出してもっと良いパラメータを探索します


```python:
scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2, eta_min=0.)
```


![ダウンロード (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/ce930374-ff47-9352-ca0d-fffc738f58fa.png)


## 6-5. LambdaLR


自分で書いた関数でlearning rateを操作するschedulerです


```python:
def func(epoch):
    return 0.95 ** epoch
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, func)
```


# 7. まとめ


書き方の自由度が高く、いろいろ実験できるのが良いですね
レッツトライ