开发者指南 / 从头开始编写 PyTorch 的训练循环

从头开始编写 PyTorch 的训练循环

作者: fchollet
创建日期: 2023/06/25
最后修改日期: 2023/06/25
描述: 在 PyTorch 中编写低级别的训练和评估循环。

在 Colab 中查看 GitHub 源代码


设置

import os

# 本指南只能使用 torch 后端运行。
os.environ["KERAS_BACKEND"] = "torch"

import torch
import keras
import numpy as np

介绍

Keras 提供了默认的训练和评估循环,fit()evaluate()。 它们的用法在指南 使用内置方法的训练与评估 中有介绍。

如果您想在利用 fit() 的便利性的同时自定义模型的学习算法 (例如,使用 fit() 训练 GAN),您可以继承 Model 类并 实现自己的 train_step() 方法,该方法在 fit() 期间会被反复调用。

现在,如果您想对训练和评估进行非常低级的控制,您应该从头编写自己的训练和评估循环。这就是本指南的内容。


第一个端到端示例

要编写自定义训练循环,我们需要以下成分:

  • 当然,我们需要一个要训练的模型。
  • 一个优化器。您可以使用 keras.optimizers 优化器, 或者使用来自 torch.optim 的本地 PyTorch 优化器。
  • 一个损失函数。您可以使用 keras.losses 的损失, 或者使用来自 torch.nn 的本地 PyTorch 损失。
  • 一个数据集。您可以使用任何格式:tf.data.Dataset, PyTorch 的 DataLoader,Python 生成器等等。

让我们将它们排列好。我们将在每种情况下使用本地 torch 对象 – 当然,Keras 模型除外。

首先,让我们获取模型和 MNIST 数据集:

# 我们考虑一个简单的 MNIST 模型
def get_model():
    inputs = keras.Input(shape=(784,), name="digits")
    x1 = keras.layers.Dense(64, activation="relu")(inputs)
    x2 = keras.layers.Dense(64, activation="relu")(x1)
    outputs = keras.layers.Dense(10, name="predictions")(x2)
    model = keras.Model(inputs=inputs, outputs=outputs)
    return model


# 创建并加载 MNIST 数据集并放入 torch DataLoader
# 准备训练数据集。
batch_size = 32
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = np.reshape(x_train, (-1, 784)).astype("float32")
x_test = np.reshape(x_test, (-1, 784)).astype("float32")
y_train = keras.utils.to_categorical(y_train)
y_test = keras.utils.to_categorical(y_test)

# 保留 10,000 个样本用于验证。
x_val = x_train[-10000:]
y_val = y_train[-10000:]
x_train = x_train[:-10000]
y_train = y_train[:-10000]

# 创建 torch 数据集
train_dataset = torch.utils.data.TensorDataset(
    torch.from_numpy(x_train), torch.from_numpy(y_train)
)
val_dataset = torch.utils.data.TensorDataset(
    torch.from_numpy(x_val), torch.from_numpy(y_val)
)

# 为数据集创建 DataLoader
train_dataloader = torch.utils.data.DataLoader(
    train_dataset, batch_size=batch_size, shuffle=True
)
val_dataloader = torch.utils.data.DataLoader(
    val_dataset, batch_size=batch_size, shuffle=False
)

接下来,这是我们的 PyTorch 优化器和损失函数:

# 实例化一个 torch 优化器
model = get_model()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

# 实例化一个 torch 损失函数
loss_fn = torch.nn.CrossEntropyLoss()

让我们使用自定义训练循环通过小批量梯度训练我们的模型。

在损失张量上调用 loss.backward() 会触发反向传播。 完成后,您的优化器会神奇地知道每个变量的梯度 并可以更新其变量,通过调用 optimizer.step() 完成。 张量、变量、优化器通过隐藏的全局状态相互连接。 此外,别忘了在 loss.backward() 之前调用 model.zero_grad(),否则您将无法 获得变量的正确梯度。

以下是我们的训练循环,逐步进行:

  • 我们打开一个 for 循环以遍历纪元
  • 对于每个纪元,我们打开一个 for 循环以遍历数据集,按批处理
  • 对于每个批次,我们在输入数据上调用模型以获取预测,然后使用它们计算损失值
  • 我们调用 loss.backward()
  • 在作用域外,我们获取模型权重的梯度
  • 最后,我们使用优化器根据 梯度更新模型的权重
epochs = 3
for epoch in range(epochs):
    for step, (inputs, targets) in enumerate(train_dataloader):
        # 正向传播
        logits = model(inputs)
        loss = loss_fn(logits, targets)

        # 反向传播
        model.zero_grad()
        loss.backward()

        # 优化器变量更新
        optimizer.step()

        # 每100个批次记录一次日志。
        if step % 100 == 0:
            print(
                f"训练损失(每批次)在步骤 {step}: {loss.detach().numpy():.4f}"
            )
            print(f"迄今为止看到: {(step + 1) * batch_size} 个样本")
训练损失(对于1个批次)在步骤0: 110.9115
已查看: 32个样本
训练损失(对于1个批次)在步骤100: 2.9493
已查看: 3232个样本
训练损失(对于1个批次)在步骤200: 2.7383
已查看: 6432个样本
训练损失(对于1个批次)在步骤300: 1.6616
已查看: 9632个样本
训练损失(对于1个批次)在步骤400: 1.5927
已查看: 12832个样本
训练损失(对于1个批次)在步骤500: 1.0992
已查看: 16032个样本
训练损失(对于1个批次)在步骤600: 0.5425
已查看: 19232个样本
训练损失(对于1个批次)在步骤700: 0.3308
已查看: 22432个样本
训练损失(对于1个批次)在步骤800: 0.8231
已查看: 25632个样本
训练损失(对于1个批次)在步骤900: 0.5570
已查看: 28832个样本
训练损失(对于1个批次)在步骤1000: 0.6321
已查看: 32032个样本
训练损失(对于1个批次)在步骤1100: 0.4962
已查看: 35232个样本
训练损失(对于1个批次)在步骤1200: 1.0833
已查看: 38432个样本
训练损失(对于1个批次)在步骤1300: 1.3607
已查看: 41632个样本
训练损失(对于1个批次)在步骤1400: 1.1250
已查看: 44832个样本
训练损失(对于1个批次)在步骤1500: 1.2562
已查看: 48032个样本
训练损失(对于1个批次)在步骤0: 0.5181
已查看: 32个样本
训练损失(对于1个批次)在步骤100: 0.3939
已查看: 3232个样本
训练损失(对于1个批次)在步骤200: 0.3406
已查看: 6432个样本
训练损失(对于1个批次)在步骤300: 0.1122
已查看: 9632个样本
训练损失(对于1个批次)在步骤400: 0.2015
已查看: 12832个样本
训练损失(对于1个批次)在步骤500: 0.1184
已查看: 16032个样本
训练损失(对于1个批次)在步骤600: 1.0702
已查看: 19232个样本
训练损失(对于1个批次)在步骤700: 0.4062
已查看: 22432个样本
训练损失(对于1个批次)在步骤800: 0.4570
已查看: 25632个样本
训练损失(对于1个批次)在步骤900: 1.2490
已查看: 28832个样本
训练损失(对于1个批次)在步骤1000: 0.0714
已查看: 32032个样本
训练损失(对于1个批次)在步骤1100: 0.3677
已查看: 35232个样本
训练损失(对于1个批次)在步骤1200: 0.8291
已查看: 38432个样本
训练损失(对于1个批次)在步骤1300: 0.8320
已查看: 41632个样本
训练损失(对于1个批次)在步骤1400: 0.1179
已查看: 44832个样本
训练损失(对于1个批次)在步骤1500: 0.5390
已查看: 48032个样本
训练损失(对于1个批次)在步骤0: 0.1309
已查看: 32个样本
训练损失(对于1个批次)在步骤100: 0.4061
已查看: 3232个样本
训练损失(对于1个批次)在步骤200: 0.2734
已查看: 6432个样本
训练损失(对于1个批次)在步骤300: 0.2972
已查看: 9632个样本
训练损失(对于1个批次)在步骤400: 0.4282
已查看: 12832个样本
训练损失(对于1个批次)在步骤500: 0.3504
已查看: 16032个样本
训练损失(对于1个批次)在步骤600: 0.3556
已查看: 19232个样本
训练损失(对于1个批次)在步骤700: 0.7834
已查看: 22432个样本
训练损失(对于1个批次)在步骤800: 0.2522
已查看: 25632个样本
训练损失(对于1个批次)在步骤900: 0.2056
已查看: 28832个样本
训练损失(对于1个批次)在步骤1000: 0.3259
已查看: 32032个样本
训练损失(对于1个批次)在步骤1100: 0.5215
已查看: 35232个样本
训练损失(对于1个批次)在步骤1200: 0.8051
已查看: 38432个样本
训练损失(对于1个批次)在步骤1300: 0.4423
已查看: 41632个样本
训练损失(对于1个批次)在步骤1400: 0.0473
已查看: 44832个样本
训练损失(对于1个批次)在步骤1500: 0.1419
已查看: 48032个样本

作为替代,让我们看看使用Keras优化器和Keras损失函数时循环的样子。

重要区别:

  • 你通过 v.value.grad 来获取变量的梯度,调用在每个可训练变量上。
  • 你通过 optimizer.apply() 来更新变量,这必须在 torch.no_grad() 范围内调用。

另外一个大陷阱: 尽管所有的NumPy/TensorFlow/JAX/Keras API以及Python unittest API都使用参数顺序约定 fn(y_true, y_pred)(参考值在前,预测值在后),PyTorch 实际上为其损失使用 fn(y_pred, y_true)。因此,请确保反转 logitstargets 的顺序。

model = get_model()
optimizer = keras.optimizers.Adam(learning_rate=1e-3)
loss_fn = keras.losses.CategoricalCrossentropy(from_logits=True)

for epoch in range(epochs):
    print(f"\nStart of epoch {epoch}")
    for step, (inputs, targets) in enumerate(train_dataloader):
        # 前向传播
        logits = model(inputs)
        loss = loss_fn(targets, logits)

        # 反向传播
        model.zero_grad()
        trainable_weights = [v for v in model.trainable_weights]

        # 调用 torch.Tensor.backward() 来计算权重的梯度
        loss.backward()
        gradients = [v.value.grad for v in trainable_weights]

        # 更新权重
        with torch.no_grad():
            optimizer.apply(gradients, trainable_weights)

        # 每100个批次记录一次日志。
        if step % 100 == 0:
            print(
                f"Training loss (for 1 batch) at step {step}: {loss.detach().numpy():.4f}"
            )
            print(f"Seen so far: {(step + 1) * batch_size} samples")
开始第 0 轮
步骤 0 的训练损失(1 批次):98.9569
目前已看到:32 个样本
步骤 100 的训练损失(1 批次):5.3304
目前已看到:3232 个样本
步骤 200 的训练损失(1 批次):0.3246
目前已看到:6432 个样本
步骤 300 的训练损失(1 批次):1.6745
目前已看到:9632 个样本
步骤 400 的训练损失(1 批次):1.0936
目前已看到:12832 个样本
步骤 500 的训练损失(1 批次):1.4159
目前已看到:16032 个样本
步骤 600 的训练损失(1 批次):0.2796
目前已看到:19232 个样本
步骤 700 的训练损失(1 批次):2.3532
目前已看到:22432 个样本
步骤 800 的训练损失(1 批次):0.7533
目前已看到:25632 个样本
步骤 900 的训练损失(1 批次):1.0432
目前已看到:28832 个样本
步骤 1000 的训练损失(1 批次):0.3959
目前已看到:32032 个样本
步骤 1100 的训练损失(1 批次):0.4722
目前已看到:35232 个样本
步骤 1200 的训练损失(1 批次):0.3851
目前已看到:38432 个样本
步骤 1300 的训练损失(1 批次):0.8599
目前已看到:41632 个样本
步骤 1400 的训练损失(1 批次):0.1237
目前已看到:44832 个样本
步骤 1500 的训练损失(1 批次):0.4919
目前已看到:48032 个样本
开始第 1 轮
步骤 0 的训练损失(1 批次):0.8972
目前已看到:32 个样本
步骤 100 的训练损失(1 批次):0.5844
目前已看到:3232 个样本
步骤 200 的训练损失(1 批次):0.1285
目前已看到:6432 个样本
步骤 300 的训练损失(1 批次):0.0671
目前已看到:9632 个样本
步骤 400 的训练损失(1 批次):0.4296
目前已看到:12832 个样本
步骤 500 的训练损失(1 批次):0.1483
目前已看到:16032 个样本
步骤 600 的训练损失(1 批次):0.0230
目前已看到:19232 个样本
步骤 700 的训练损失(1 批次):0.1368
目前已看到:22432 个样本
步骤 800 的训练损失(1 批次):0.1531
目前已看到:25632 个样本
步骤 900 的训练损失(1 批次):0.0472
目前已看到:28832 个样本
步骤 1000 的训练损失(1 批次):0.2343
目前已看到:32032 个样本
步骤 1100 的训练损失(1 批次):0.4449
目前已看到:35232 个样本
步骤 1200 的训练损失(1 批次):0.3942
目前已看到:38432 个样本
步骤 1300 的训练损失(1 批次):0.3236
目前已看到:41632 个样本
步骤 1400 的训练损失(1 批次):0.0717
目前已看到:44832 个样本
步骤 1500 的训练损失(1 批次):0.9288
目前已看到:48032 个样本
开始第 2 轮
步骤 0 的训练损失(1 批次):0.9393
目前已看到:32 个样本
步骤 100 的训练损失(1 批次):0.2383
目前已看到:3232 个样本
步骤 200 的训练损失(1 批次):0.1116
目前已看到:6432 个样本
步骤 300 的训练损失(1 批次):0.6736
目前已看到:9632 个样本
步骤 400 的训练损失(1 扁次):0.6713
目前已看到:12832 个样本
步骤 500 的训练损失(1 批次):0.3394
目前已看到:16032 个样本
步骤 600 的训练损失(1 批次):0.2385
目前已看到:19232 个样本
步骤 700 的训练损失(1 批次):0.4248
目前已看到:22432 个样本
步骤 800 的训练损失(1 批次):0.0200
目前已看到:25632 个样本
步骤 900 的训练损失(1 批次):0.1259
目前已看到:28832 个样本
步骤 1000 的训练损失(1 批次):0.7566
目前已看到:32032 个样本
步骤 1100 的训练损失(1 批次):0.0594
目前已看到:35232 个样本
步骤 1200 的训练损失(1 批次):0.2821
目前已看到:38432 个样本
步骤 1300 的训练损失(1 批次):0.2088
目前已看到:41632 个样本
步骤 1400 的训练损失(1 批次):0.5654
目前已看到:44832 个样本
步骤 1500 的训练损失(1 批次):0.0512
目前已看到:48032 个样本

指标的低级处理

让我们在这个基本训练循环中添加指标监控。

你可以在从头编写的这样的训练循环中轻松重用内置的 Keras 指标(或你编写的自定义指标)。流程如下:

  • 在循环开始时实例化指标
  • 在每个批次后调用 metric.update_state()
  • 当你需要显示当前指标的值时调用 metric.result()
  • 当你需要清除指标的状态时调用 metric.reset_state() (通常在一个轮次结束时)

让我们利用这些知识在每个轮次结束时计算训练和验证数据的 CategoricalAccuracy

# 获取一个新的模型
model = get_model()

# 实例化一个优化器以训练模型。
optimizer = keras.optimizers.Adam(learning_rate=1e-3)
# 实例化一个损失函数。
loss_fn = keras.losses.CategoricalCrossentropy(from_logits=True)

# 准备指标。
train_acc_metric = keras.metrics.CategoricalAccuracy()
val_acc_metric = keras.metrics.CategoricalAccuracy()

这是我们的训练和评估循环: for epoch in range(epochs): print(f"\nStart of epoch {epoch}") for step, (inputs, targets) in enumerate(train_dataloader): # 前向传播 logits = model(inputs) loss = loss_fn(targets, logits)

    # 后向传播
    model.zero_grad()
    trainable_weights = [v for v in model.trainable_weights]

    # 对损失调用 torch.Tensor.backward() 以计算权重的梯度。
    loss.backward()
    gradients = [v.value.grad for v in trainable_weights]

    # 更新权重
    with torch.no_grad():
        optimizer.apply(gradients, trainable_weights)

    # 更新训练指标。
    train_acc_metric.update_state(targets, logits)

    # 每100个批次记录一次。
    if step % 100 == 0:
        print(
            f"Training loss (for 1 batch) at step {step}: {loss.detach().numpy():.4f}"
        )
        print(f"Seen so far: {(step + 1) * batch_size} samples")

# 在每个纪元结束时显示指标。
train_acc = train_acc_metric.result()
print(f"Training acc over epoch: {float(train_acc):.4f}")

# 在每个纪元结束时重置训练指标
train_acc_metric.reset_state()

# 在每个纪元结束时运行验证循环。
for x_batch_val, y_batch_val in val_dataloader:
    val_logits = model(x_batch_val, training=False)
    # 更新验证指标
    val_acc_metric.update_state(y_batch_val, val_logits)
val_acc = val_acc_metric.result()
val_acc_metric.reset_state()
print(f"Validation acc: {float(val_acc):.4f}")
第0个纪元开始
步骤0的训练损失(1个批次):59.2206
目前看到:32个样本
步骤100的训练损失(1个批次):8.9801
目前看到:3232个样本
步骤200的训练损失(1个批次):5.2990
目前看到:6432个样本
步骤300的训练损失(1个批次):3.6978
目前看到:9632个样本
步骤400的训练损失(1个批次):1.9965
目前看到:12832个样本
步骤500的训练损失(1个批次):2.1896
目前看到:16032个样本
步骤600的训练损失(1个批次):1.2416
目前看到:19232个样本
步骤700的训练损失(1个批次):0.9403
目前看到:22432个样本
步骤800的训练损失(1个批次):0.1838
目前看到:25632个样本
步骤900的训练损失(1个批次):0.5884
目前看到:28832个样本
步骤1000的训练损失(1个批次):0.7836
目前看到:32032个样本
步骤1100的训练损失(1个批次):0.7015
目前看到:35232个样本
步骤1200的训练损失(1个批次):0.3335
目前看到:38432个样本
步骤1300的训练损失(1个批次):0.2763
目前看到:41632个样本
步骤1400的训练损失(1个批次):0.4787
目前看到:44832个样本
步骤1500的训练损失(1个批次):0.2562
目前看到:48032个样本
整个纪元的训练准确率:0.8411
验证准确率:0.8963
第1个纪元开始
步骤0的训练损失(1个批次):0.3417
目前看到:32个样本
步骤100的训练损失(1个批次):1.1465
目前看到:3232个样本
步骤200的训练损失(1个批次):0.7274
目前看到:6432个样本
步骤300的训练损失(1个批次):0.1273
目前看到:9632个样本
步骤400的训练损失(1个批次):0.6500
目前看到:12832个样本
步骤500的训练损失(1个批次):0.2008
目前看到:16032个样本
步骤600的训练损失(1个批次):0.7483
目前看到:19232个样本
步骤700的训练损失(1个批次):0.5821
目前看到:22432个样本
步骤800的训练损失(1个批次):0.5696
目前看到:25632个样本
步骤900的训练损失(1个批次):0.3112
目前看到:28832个样本
步骤1000的训练损失(1个批次):0.1761
目前看到:32032个样本
步骤1100的训练损失(1个批次):0.1811
目前看到:35232个样本
步骤1200的训练损失(1个批次):0.2736
目前看到:38432个样本
步骤1300的训练损失(1个批次):0.3848
目前看到:41632个样本
步骤1400的训练损失(1个批次):0.4627
目前看到:44832个样本
步骤1500的训练损失(1个批次):0.3934
目前看到:48032个样本
整个纪元的训练准确率:0.9053
验证准确率:0.9221
第2个纪元开始
步骤0的训练损失(1个批次):0.5743
目前看到:32个样本
步骤100的训练损失(1个批次):0.4448
目前看到:3232个样本
步骤200的训练损失(1个批次):0.9880
目前看到:6432个样本
步骤300的训练损失(1个批次):0.2268
目前看到:9632个样本
步骤400的训练损失(1个批次):0.5607
目前看到:12832个样本
步骤500的训练损失(1个批次):0.1178
目前看到:16032个样本
步骤600的训练损失(1个批次):0.4305
目前看到:19232个样本
步骤700的训练损失(1个批次):0.1712
目前看到:22432个样本
步骤800的训练损失(1个批次):0.3109
目前看到:25632个样本
步骤900的训练损失(1个批次):0.1548
目前看到:28832个样本
步骤1000的训练损失(1个批次):0.1090
目前看到:32032个样本
步骤1100的训练损失(1个批次):0.5169
目前看到:35232个样本
步骤1200的训练损失(1个批次):0.3791
目前看到:38432个样本
步骤1300的训练损失(1个批次):0.6963
目前看到:41632个样本
步骤1400的训练损失(1个批次):0.6204
目前看到:44832个样本
步骤1500的训练损失(1个批次):0.1111
目前看到:48032个样本
整个纪元的训练准确率:0.9216
验证准确率:0.9356

模型跟踪的损失的低级处理

层和模型通过调用 self.add_loss(value) 的层递归地跟踪在前向传递过程中创建的任何损失。
在前向传递结束时,通过属性 model.losses 可以获取到结果标量损失值的列表。

如果你想使用这些损失组件,你应该将它们相加并在训练步骤中将它们添加到主要损失中。

考虑这个层,它创建了一个活动正则化损失:

class ActivityRegularizationLayer(keras.layers.Layer):
    def call(self, inputs):
        self.add_loss(1e-2 * torch.sum(inputs))  # 添加正则化损失
        return inputs

让我们构建一个使用它的非常简单的模型:

inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu")(inputs)
# 将活动正则化插入为一层
x = ActivityRegularizationLayer()(x)
x = keras.layers.Dense(64, activation="relu")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)

model = keras.Model(inputs=inputs, outputs=outputs)

Here's what our training loop should look like now:

# 获取一个全新的模型
model = get_model()

# 实例化一个优化器来训练模型。
optimizer = keras.optimizers.Adam(learning_rate=1e-3)
# 实例化一个损失函数。
loss_fn = keras.losses.CategoricalCrossentropy(from_logits=True)

# 准备指标。
train_acc_metric = keras.metrics.CategoricalAccuracy()
val_acc_metric = keras.metrics.CategoricalAccuracy()

for epoch in range(epochs):
    print(f"\n开始第 {epoch} 轮")
    for step, (inputs, targets) in enumerate(train_dataloader):
        # 前向传播
        logits = model(inputs)
        loss = loss_fn(targets, logits)
        if model.losses:
            loss = loss + torch.sum(*model.losses)

        # 后向传播
        model.zero_grad()
        trainable_weights = [v for v in model.trainable_weights]

        # 计算损失的梯度
        # 对于权重。
        loss.backward()
        gradients = [v.value.grad for v in trainable_weights]

        # 更新权重
        with torch.no_grad():
            optimizer.apply(gradients, trainable_weights)

        # 更新训练指标。
        train_acc_metric.update_state(targets, logits)

        # 每 100 批次记录一次。
        if step % 100 == 0:
            print(
                f"第 {step} 步的训练损失(对于 1 批次):{loss.detach().numpy():.4f}"
            )
            print(f"至今已见:{(step + 1) * batch_size} 个样本")

    # 在每轮结束时显示指标。
    train_acc = train_acc_metric.result()
    print(f"整轮训练准确率:{float(train_acc):.4f}")

    # 在每轮结束时重置训练指标
    train_acc_metric.reset_state()

    # 在每轮结束时运行验证循环。
    for x_batch_val, y_batch_val in val_dataloader:
        val_logits = model(x_batch_val, training=False)
        # 更新验证指标
        val_acc_metric.update_state(y_batch_val, val_logits)
    val_acc = val_acc_metric.result()
    val_acc_metric.reset_state()
    print(f"验证准确率:{float(val_acc):.4f}")
开始第 0 轮
第 0 步的训练损失(对于 1 批次):138.7979
至今已见:32 个样本
第 100 步的训练损失(对于 1 批次):4.4268
至今已见:3232 个样本
第 200 步的训练损失(对于 1 批次):1.0779
至今已见:6432 个样本
第 300 步的训练损失(对于 1 批次):1.7229
至今已见:9632 个样本
第 400 步的训练损失(对于 1 批次):0.5801
至今已见:12832 个样本
第 500 步的训练损失(对于 1 批次):0.4298
至今已见:16032 个样本
第 600 步的训练损失(对于 1 批次):0.4717
至今已见:19232 个样本
第 700 步的训练损失(对于 1 批次):1.3369
至今已见:22432 个样本
第 800 步的训练损失(对于 1 批次):1.3239
至今已见:25632 个样本
第 900 步的训练损失(对于 1 批次):0.5972
至今已见:28832 个样本
第 1000 步的训练损失(对于 1 批次):0.1983
至今已见:32032 个样本
第 1100 步的训练损失(对于 1 批次):0.5228
至今已见:35232 个样本
第 1200 步的训练损失(对于 1 批次):1.0025
至今已见:38432 个样本
第 1300 步的训练损失(对于 1 批次):0.3424
至今已见:41632 个样本
第 1400 步的训练损失(对于 1 批次):0.5196
至今已见:44832 个样本
第 1500 步的训练损失(对于 1 批次):0.4287
至今已见:48032 个样本
整轮训练准确率:0.8089
验证准确率:0.8947
开始第 1 轮
第 0 步的训练损失(对于 1 批次):0.2903
至今已见:32 个样本
第 100 步的训练损失(对于 1 批次):0.4118
至今已见:3232 个样本
第 200 步的训练损失(对于 1 批次):0.6533
至今已见:6432 个样本
第 300 步的训练损失(对于 1 批次):0.0402
至今已见:9632 个样本
第 400 步的训练损失(对于 1 批次):0.3638
至今已见:12832 个样本
第 500 步的训练损失(对于 1 批次):0.3313
至今已见:16032 个样本
第 600 步的训练损失(对于 1 批次):0.5119
至今已见:19232 个样本
第 700 步的训练损失(对于 1 批次):0.1628
至今已见:22432 个样本
第 800 步的训练损失(对于 1 批次):0.4793
至今已见:25632 个样本
第 900 步的训练损失(对于 1 批次):0.2726
至今已见:28832 个样本
第 1000 步的训练损失(对于 1 批次):0.5721
至今已见:32032 个样本
第 1100 步的训练损失(对于 1 批次):0.5783
至今已见:35232 个样本
第 1200 步的训练损失(对于 1 批次):0.2533
至今已见:38432 个样本
第 1300 步的训练损失(对于 1 批次):0.2218
至今已见:41632 个样本
第 1400 步的训练损失(对于 1 批次):0.1232
至今已见:44832 个样本
第 1500 步的训练损失(对于 1 批次):0.6805
至今已见:48032 个样本
整轮训练准确率:0.8970
验证准确率:0.9097
开始第2个周期
第0步的训练损失(1个批次): 0.4553
到目前为止:32个样本
第100步的训练损失(1个批次): 0.3975
到目前为止:3232个样本
第200步的训练损失(1个批次): 1.2382
到目前为止:6432个样本
第300步的训练损失(1个批次): 0.0927
到目前为止:9632个样本
第400步的训练损失(1个批次): 0.3530
到目前为止:12832个样本
第500步的训练损失(1个批次): 0.3842
到目前为止:16032个样本
第600步的训练损失(1个批次): 0.6423
到目前为止:19232个样本
第700步的训练损失(1个批次): 0.1751
到目前为止:22432个样本
第800步的训练损失(1个批次): 0.4769
到目前为止:25632个样本
第900步的训练损失(1个批次): 0.1854
到目前为止:28832个样本
第1000步的训练损失(1个批次): 0.3130
到目前为止:32032个样本
第1100步的训练损失(1个批次): 0.1633
到目前为止:35232个样本
第1200步的训练损失(1个批次): 0.1446
到目前为止:38432个样本
第1300步的训练损失(1个批次): 0.4661
到目前为止:41632个样本
第1400步的训练损失(1个批次): 0.9977
到目前为止:44832个样本
第1500步的训练损失(1个批次): 0.3392
到目前为止:48032个样本
整个周期的训练准确率: 0.9182
验证准确率: 0.9200

就这样!