PyTorch を使用したモデルのトレーニング

完了

PyTorch は、ディープ ラーニング モデルをトレーニングするために一般的に使用される機械学習フレームワークです。 Azure Databricks では、PyTorch は ML クラスターにプレインストールされています。

このユニットのコード スニペットは、重要な点を強調する例として提供されています。 このモジュールの後半の演習では、完全な実際の例のコードを実行できます。

PyTorch ネットワークを定義する

PyTorch では、モデルは定義したネットワークに基づいています。 ネットワークは複数のレイヤーで構成され、それぞれ入力と出力が指定されています。 さらに、データがネットワーク経由で渡されるときに、各レイヤーに関数を適用する 前方 関数を定義します。

次のコード例では、ネットワークを定義します。

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

class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.layer1 = nn.Linear(4, 5)
        self.layer2 = nn.Linear(5, 5)
        self.layer3 = nn.Linear(5, 3)

    def forward(self, x):
        layer1_output = torch.relu(self.layer1(x))
        layer2_output = torch.relu(self.layer2(layer1_output))
        y = self.layer3(layer2_output)
        return y

コードは最初は複雑に見えるかもしれませんが、このクラスは 3 つのレイヤーを持つ比較的単純なネットワークを定義します。

  • 4 つの入力値を受け取り、次のレイヤーに対して 5 つの出力値を生成する入力レイヤー。
  • 5 つの入力を受け入れ、5 つの出力を生成するレイヤー。
  • 5 つの入力を受け入れ、3 つの出力を生成する最終的な出力レイヤー。

forward 関数は、レイヤーを入力データ (x) に適用し、各レイヤーからの出力を次のレイヤーに渡し、最後に最後のレイヤー (ラベル予測ベクトル y を含む) から出力を返します。 レイヤー 1 と 2 の出力に 修正された線形単位 (ReLU) アクティブ化関数を適用して、出力値を正の数に制限します。

使用する損失条件の種類に応じて、戻り値に log_softmax などのアクティブ化関数を適用して、0 から 1 の範囲に強制することができます。 ただし、一部の損失条件 (多クラス分類でよく使用される CrossEntropyLoss など) では、適切な関数が自動的に適用されます。

トレーニング用のモデルを作成するには、次のようにネットワーク クラスのインスタンスを作成するだけで済みます。

myModel = MyNet()

モデリング用のデータを準備する

PyTorch レイヤーは、 テンソル (行列のような構造) として書式設定されたデータに対して機能します。 他の一般的なデータ形式をテンソルに変換するさまざまな関数があり、PyTorch データ ローダー を定義して、データ テンソルをトレーニングまたは推論用のモデルに読み取ることができます。

ほとんどの教師あり機械学習手法と同様に、トレーニングと検証用に個別のデータセットを定義する必要があります。 この分離により、トレーニングされていないデータが表示されたときに、モデルが正確に予測されることを検証できます。

次のコードでは、2 つのデータ ローダーを定義します。1 つはトレーニング用、もう 1 つはテスト用です。 この例の各ローダーのソース データは、特徴値の Numpy 配列と、対応するラベル値の Numpy 配列であると見なされます。

# Create a dataset and loader for the training data and labels
train_x = torch.Tensor(x_train).float()
train_y = torch.Tensor(y_train).long()
train_ds = td.TensorDataset(train_x,train_y)
train_loader = td.DataLoader(train_ds, batch_size=20,
    shuffle=False, num_workers=1)

# Create a dataset and loader for the test data and labels
test_x = torch.Tensor(x_test).float()
test_y = torch.Tensor(y_test).long()
test_ds = td.TensorDataset(test_x,test_y)
test_loader = td.DataLoader(test_ds, batch_size=20,
    shuffle=False, num_workers=1)

この例のローダーは、データを 30 のバッチに分割します。このバッチは、トレーニングまたは推論中に 前方 関数に渡されます。

損失条件とオプティマイザー アルゴリズムを選択する

モデルは、トレーニング データをネットワークにフィードし、損失 (予測値と実際の値の集計された差) を測定し、損失を最小限に抑えるために重みとバランスを調整してネットワークを最適化することによってトレーニングされます。 損失の計算と最小化の具体的な詳細は、選択した損失条件とオプティマイザー アルゴリズムによって制御されます。

損失基準

PyTorch では、以下を始め、複数の損失条件関数がサポートされています。

  • cross_entropy: 複数の変数の予測値と実際の値の集計差を測定する関数 (通常は、多クラス分類のクラス確率の損失を測定するために使用されます)。
  • binary_cross_entropy: 予測された確率と実際の確率の差を測定する関数 (通常、二項分類でのクラス確率の損失を測定するために使用されます)。
  • mse_loss: 予測された数値と実際の数値の平均二乗誤差損失を測定する関数 (通常は回帰に使用されます)。

モデルのトレーニング時に使用する損失条件を指定するには、適切な関数のインスタンスを作成します。このように:

import torch.nn as nn

loss_criteria = nn.CrossEntropyLoss

ヒント

PyTorch で使用できる損失条件の詳細については、PyTorch ドキュメントの 損失関数 を参照してください。

オプティマイザー アルゴリズム

損失を計算した後、オプティマイザーは、それを最小限に抑えるために重みとバランスを調整する最善の方法を決定するために使用されます。 オプティマイザーは、関数を最小化するための 勾配降下 法の特定の実装です。 PyTorch で使用できるオプティマイザーには、次のようなものがあります。

  • Adadelta: アダプティブ学習率 アルゴリズムに基づくオプティマイザー。
  • Adam: Adam アルゴリズムに基づく計算効率の高いオプティマイザー。
  • SGD: 確率的勾配降下 アルゴリズムに基づくオプティマイザー。

これらのアルゴリズムのいずれかを使用してモデルをトレーニングするには、オプティマイザーのインスタンスを作成し、必要なパラメーターを設定する必要があります。 特定のパラメーターは選択したオプティマイザーによって異なりますが、ほとんどの場合、各最適化で行われる調整のサイズを制御する 学習率 を指定する必要があります。

次のコードは、 Adam オプティマイザーのインスタンスを作成します。

import torch.optim as opt

learning_rate = 0.001
optimizer = opt.Adam(model.parameters(), lr=learning_rate)

ヒント

PyTorch で使用可能なオプティマイザーの詳細については、PyTorch ドキュメントの アルゴリズム を参照してください。

トレーニング関数とテスト関数を作成する

ネットワークを定義してデータを準備したら、トレーニング データをネットワーク経由で渡し、損失を計算し、ネットワークの重みとバイアスを最適化し、テスト データを使用してネットワークのパフォーマンスを検証することで、データを使用してモデルのトレーニングとテストを行うことができます。 ネットワークを介してデータを渡してトレーニング データを使用してモデルを トレーニング する関数と、テスト データを使用してモデルを テスト する別の関数を定義するのが一般的です。

トレーニング関数を作成する

次の例は、モデルをトレーニングする関数を示しています。

def train(model, data_loader, optimizer):

    # Use GPU if available, otherwise CPU
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model.to(device)
    
    # Set the model to training mode (to enable backpropagation)
    model.train()
    train_loss = 0
    
    # Feed the batches of data forward through the network
    for batch, tensor in enumerate(data_loader):
        data, target = tensor # Specify features and labels in a tensor
        optimizer.zero_grad() # Reset optimizer state
        out = model(data) # Pass the data through the network
        loss = loss_criteria(out, target) # Calculate the loss
        train_loss += loss.item() # Keep a running total of loss for each batch

        # backpropagate adjustments to weights/bias
        loss.backward()
        optimizer.step()

    #Return average loss for all batches
    avg_loss = train_loss / (batch+1)
    print('Training set: Average loss: {:.6f}'.format(avg_loss))
    return avg_loss

次の例は、モデルをテストする関数を示しています。

def test(model, data_loader):
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model.to(device)
    # Switch the model to evaluation mode (so we don't backpropagate)
    model.eval()
    test_loss = 0
    correct = 0

    # Pass the data through with no gradient computation
    with torch.no_grad():
        batch_count = 0
        for batch, tensor in enumerate(data_loader):
            batch_count += 1
            data, target = tensor
            # Get the predictions
            out = model(data)

            # calculate the loss
            test_loss += loss_criteria(out, target).item()

            # Calculate the accuracy
            _, predicted = torch.max(out.data, 1)
            correct += torch.sum(target==predicted).item()
            
    # Calculate the average loss and total accuracy for all batches
    avg_loss = test_loss/batch_count
    print('Validation set: Average loss: {:.6f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        avg_loss, correct, len(data_loader.dataset),
        100. * correct / len(data_loader.dataset)))
    return avg_loss

複数のエポックでモデルをトレーニングする

ディープ ラーニング モデルをトレーニングするには、通常、トレーニング関数を複数回 (エポックと呼ばれます) 実行し、各 エポックのトレーニング データから計算される損失を減らすことを目的としています。 テスト関数を使用して、(モデルがトレーニングされなかった) テスト データからの損失もトレーニング損失に合わせて減少していることを検証できます。つまり、モデル トレーニングによって、トレーニング データに 過剰に適合 するモデルが生成されていないことを確認できます。

ヒント

エポックごとにテスト関数を実行する必要はありません。 1 秒ごとに実行するか、最後に 1 回実行するかを選択できます。 ただし、トレーニング中にモデルをテストすると、モデルがオーバーフィットし始めたエポックの数を判断するのに役立ちます。

次のコードは、50 エポックを超えるモデルをトレーニングします。

epochs = 50
for epoch in range(1, epochs + 1):

    # print the epoch number
    print('Epoch: {}'.format(epoch))
    
    # Feed training data into the model to optimize the weights
    train_loss = train(model, train_loader, optimizer)
    print(train_loss)
    
    # Feed the test data into the model to check its performance
    test_loss = test(model, test_loader)
    print(test_loss)

トレーニング済みのモデルの状態を保存する

モデルのトレーニングが正常に完了したら、次のように重みとバイアスを節約できます。

model_file = '/dbfs/my_model.pkl'
torch.save(model.state_dict(), model_file)

後でモデルを読み込んで使用するには、モデルの基になるネットワーク クラスのインスタンスを作成し、保存された重みとバイアスを読み込みます。

model = myNet()
model.load_state_dict(torch.load(model_file))