批量大小和训练时间
Batch size and Training time
感谢@P运行e 对我的问题的批评意见。
我正在尝试使用 MNIST 数据集找出批量大小和训练时间之间的关系。
通过阅读 Whosebug 中的大量问题,例如这个问题:
How does batch size impact time execution in neural networks?
人们说当我使用小批量时训练时间会减少。
然而,通过尝试这两个,我发现批量大小 == 1 的训练比批量大小 == 60,000 花费更多的时间。我将纪元设置为 10.
我将我的 MMIST 数据集分成 60k 用于训练,10k 用于测试。
下面是我的代码和结果。
mnist_trainset = torchvision.datasets.MNIST(root=root_dir, train=True,
download=True,
transform=transforms.Compose([transforms.ToTensor()]))
mnist_testset = torchvision.datasets.MNIST(root=root_dir,
train=False,
download=True,
transform=transforms.Compose([transforms.ToTensor()]))
train_dataloader = torch.utils.data.DataLoader(mnist_trainset,
batch_size=1,
shuffle=True)
test_dataloader = torch.utils.data.DataLoader(mnist_testset,
batch_size=50,
shuffle=False)
# Define the model
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.linear_1 = torch.nn.Linear(784, 256)
self.linear_2 = torch.nn.Linear(256, 10)
self.sigmoid = torch.nn.Sigmoid()
def forward(self, x):
x = x.reshape(x.size(0), -1)
x = self.linear_1(x)
x = self.sigmoid(x)
pred = self.linear_2(x)
return pred
# trainer
no_epochs = 10
def my_trainer(optimizer, model):
criterion = torch.nn.CrossEntropyLoss()
train_loss = list()
test_loss = list()
test_acc = list()
best_test_loss = 1
for epoch in range(no_epochs):
# timer starts
start = timer()
total_train_loss = 0
total_test_loss = 0
# training
# set up training mode
model.train()
for itr, (image, label) in enumerate(train_dataloader):
optimizer.zero_grad()
pred = model(image)
loss = criterion(pred, label)
total_train_loss += loss.item()
loss.backward()
optimizer.step()
total_train_loss = total_train_loss / (itr + 1)
train_loss.append(total_train_loss)
# testing
# change to evaluation mode
model.eval()
total = 0
for itr, (image, label) in enumerate(test_dataloader):
pred = model(image)
loss = criterion(pred, label)
total_test_loss += loss.item()
# we now need softmax because we are testing.
pred = torch.nn.functional.softmax(pred, dim=1)
for i, p in enumerate(pred):
if label[i] == torch.max(p.data, 0)[1]:
total = total + 1
# caculate accuracy
accuracy = total / len(mnist_testset)
# append accuracy here
test_acc.append(accuracy)
# append test loss here
total_test_loss = total_test_loss / (itr + 1)
test_loss.append(total_test_loss)
print('\nEpoch: {}/{}, Train Loss: {:.8f}, Test Loss: {:.8f}, Test Accuracy: {:.8f}'.format(epoch + 1, no_epochs, total_train_loss, total_test_loss, accuracy))
if total_test_loss < best_test_loss:
best_test_loss = total_test_loss
print("Saving the model state dictionary for Epoch: {} with Test loss: {:.8f}".format(epoch + 1, total_test_loss))
torch.save(model.state_dict(), "model.dth")
# timer finishes
end = timer()
print(end - start)
return no_epochs, test_acc, test_loss
model_sgd = Model()
optimizer_SGD = torch.optim.SGD(model_sgd.parameters(), lr=0.1)
sgd_no_epochs, sgd_test_acc, sgd_test_loss = my_trainer(optimizer=optimizer_SGD, model=model_sgd)
我计算了每个 epoch 花费了多少时间。
下面是结果。
Epoch: 1/10, Train Loss: 0.23193890, Test Loss: 0.12670580, Test Accuracy: 0.96230000
63.98903721500005 seconds
Epoch: 2/10, Train Loss: 0.10275097, Test Loss: 0.10111042, Test Accuracy: 0.96730000
63.97179028100004 seconds
Epoch: 3/10, Train Loss: 0.07269370, Test Loss: 0.09668248, Test Accuracy: 0.97150000
63.969843954 seconds
Epoch: 4/10, Train Loss: 0.05658571, Test Loss: 0.09841745, Test Accuracy: 0.97070000
64.24135530400008 seconds
Epoch: 5/10, Train Loss: 0.04183391, Test Loss: 0.09828428, Test Accuracy: 0.97230000
64.19695308500013 seconds
Epoch: 6/10, Train Loss: 0.03393899, Test Loss: 0.08982467, Test Accuracy: 0.97530000
63.96944059600014 seconds
Epoch: 7/10, Train Loss: 0.02808819, Test Loss: 0.08597597, Test Accuracy: 0.97700000
63.59837343000004 seconds
Epoch: 8/10, Train Loss: 0.01859330, Test Loss: 0.07529452, Test Accuracy: 0.97950000
63.591578820999985 seconds
Epoch: 9/10, Train Loss: 0.01383720, Test Loss: 0.08568452, Test Accuracy: 0.97820000
63.66664020100029
Epoch: 10/10, Train Loss: 0.00911216, Test Loss: 0.07377760, Test Accuracy: 0.98060000
63.92636473799985 seconds
在此之后我将批量大小更改为 60000 并再次 运行 相同的程序。
train_dataloader = torch.utils.data.DataLoader(mnist_trainset,
batch_size=60000,
shuffle=True)
test_dataloader = torch.utils.data.DataLoader(mnist_testset,
batch_size=50,
shuffle=False)
print("\n===== Entering SGD optimizer =====\n")
model_sgd = Model()
optimizer_SGD = torch.optim.SGD(model_sgd.parameters(), lr=0.1)
sgd_no_epochs, sgd_test_acc, sgd_test_loss = my_trainer(optimizer=optimizer_SGD, model=model_sgd)
我得到了批量大小 == 60000 的结果
Epoch: 1/10, Train Loss: 2.32325006, Test Loss: 2.30074144, Test Accuracy: 0.11740000
6.54154992299982 seconds
Epoch: 2/10, Train Loss: 2.30010080, Test Loss: 2.29524792, Test Accuracy: 0.11790000
6.341824101999919 seconds
Epoch: 3/10, Train Loss: 2.29514933, Test Loss: 2.29183527, Test Accuracy: 0.11410000
6.161918789000083 seconds
Epoch: 4/10, Train Loss: 2.29196787, Test Loss: 2.28874513, Test Accuracy: 0.11450000
6.180891567999879 seconds
Epoch: 5/10, Train Loss: 2.28899717, Test Loss: 2.28571669, Test Accuracy: 0.11570000
6.1449509030003355 seconds
Epoch: 6/10, Train Loss: 2.28604794, Test Loss: 2.28270152, Test Accuracy: 0.11780000
6.311743144000047 seconds
Epoch: 7/10, Train Loss: 2.28307867, Test Loss: 2.27968731, Test Accuracy: 0.12250000
6.060618773999977 seconds
Epoch: 8/10, Train Loss: 2.28014660, Test Loss: 2.27666961, Test Accuracy: 0.12890000
6.171511712999745 seconds
Epoch: 9/10, Train Loss: 2.27718973, Test Loss: 2.27364607, Test Accuracy: 0.13930000
6.164125173999764 seconds
Epoch: 10/10, Train Loss: 2.27423453, Test Loss: 2.27061504, Test Accuracy: 0.15350000
6.077817454000069 seconds
正如您所看到的,很明显当 batch_size == 1 时每个 epoch 花费了更多时间,这与我所看到的不同。
也许我对每个时期的训练时间与收敛前的训练时间感到困惑?通过查看此网页,我的直觉似乎是正确的:https://medium.com/deep-learning-experiments/effect-of-batch-size-on-neural-net-training-c5ae8516e57
有人可以解释一下发生了什么吗?
这是一个边缘问题;你应该仍然能够从基础文献中提取这种理解......最终。
您的见解完全正确:您测量的是每个时期的执行时间,而不是总训练时间 (TTT)。您还采纳了通用的“小批量”建议荒谬:批量大小为 1 几乎肯定是次优的。
宏观层面的机制非常简单。
批量大小为 60k(整个训练集)时,您 运行 通过模型对所有 60k 图像进行平均,然后对该平均结果进行一次反向传播。这往往会失去从关注鲜为人知的功能中获得的知识。
批量大小为 1 时,您 运行 每个图像单独通过模型,对一个结果求平均(一个非常简单的操作 :-)),然后进行反向传播。这往往会过分强调个别效果,尤其是保留每个图像的迷信效果。它还对前几张图像的初始假设给予了过多的权重。
小批量最明显的影响是你正在做 60k back-props 而不是 1,所以每个 epoch 需要更长的时间。
这两种方法中的任何一种都是极端情况,在应用中通常是荒谬的。
您需要通过试验找到“最佳点”,让您以最快的速度收敛到可接受的(接近最佳的)准确度。在选择实验设计时有几个注意事项:
- 内存大小:您希望能够一次将整个批次摄取到内存中。这允许您的模型流水线读取和处理。如果超出可用内存,您将浪费大量时间进行交换。如果内存使用不足,则会留下一些未开发的潜在性能。
- 处理器:如果您使用的是多处理器芯片,您希望让它们都处于忙碌状态。如果您希望通过 OS 控件分配处理器,您还需要考虑分配给模型计算的处理器数量,以及分配给 I/O 和系统使用的处理器数量。例如,在我做的一个项目中,我们小组发现我们的 32 个内核最适合使用,其中 28 个分配给计算,4 个保留用于 I/O 和其他系统功能。
- 缩放:某些特征在 2 的幂中效果最好。您可能会发现批处理大小为 2^n 或 3 * 2^n 对于某些 n,效果最好,仅仅是因为块大小和其他系统分配。
多年来对我最有效的实验设计是从 2 的幂开始,它大致是训练集大小的平方根。对您来说,有一个明显的起始猜测值 256。因此,您可能 运行 在 64、128、256、512 和 1024 处进行实验。看看哪些给您带来最快的收敛。
然后使用因子 3 进行一步优化。例如,如果您发现 128 时性能最佳,也可以尝试 96 和 192。
您可能会发现您的“最佳点”与相邻批次大小之间的差别很小;这是大多数复杂信息系统的本质。
感谢@P运行e 对我的问题的批评意见。
我正在尝试使用 MNIST 数据集找出批量大小和训练时间之间的关系。
通过阅读 Whosebug 中的大量问题,例如这个问题: How does batch size impact time execution in neural networks? 人们说当我使用小批量时训练时间会减少。
然而,通过尝试这两个,我发现批量大小 == 1 的训练比批量大小 == 60,000 花费更多的时间。我将纪元设置为 10.
我将我的 MMIST 数据集分成 60k 用于训练,10k 用于测试。
下面是我的代码和结果。
mnist_trainset = torchvision.datasets.MNIST(root=root_dir, train=True,
download=True,
transform=transforms.Compose([transforms.ToTensor()]))
mnist_testset = torchvision.datasets.MNIST(root=root_dir,
train=False,
download=True,
transform=transforms.Compose([transforms.ToTensor()]))
train_dataloader = torch.utils.data.DataLoader(mnist_trainset,
batch_size=1,
shuffle=True)
test_dataloader = torch.utils.data.DataLoader(mnist_testset,
batch_size=50,
shuffle=False)
# Define the model
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.linear_1 = torch.nn.Linear(784, 256)
self.linear_2 = torch.nn.Linear(256, 10)
self.sigmoid = torch.nn.Sigmoid()
def forward(self, x):
x = x.reshape(x.size(0), -1)
x = self.linear_1(x)
x = self.sigmoid(x)
pred = self.linear_2(x)
return pred
# trainer
no_epochs = 10
def my_trainer(optimizer, model):
criterion = torch.nn.CrossEntropyLoss()
train_loss = list()
test_loss = list()
test_acc = list()
best_test_loss = 1
for epoch in range(no_epochs):
# timer starts
start = timer()
total_train_loss = 0
total_test_loss = 0
# training
# set up training mode
model.train()
for itr, (image, label) in enumerate(train_dataloader):
optimizer.zero_grad()
pred = model(image)
loss = criterion(pred, label)
total_train_loss += loss.item()
loss.backward()
optimizer.step()
total_train_loss = total_train_loss / (itr + 1)
train_loss.append(total_train_loss)
# testing
# change to evaluation mode
model.eval()
total = 0
for itr, (image, label) in enumerate(test_dataloader):
pred = model(image)
loss = criterion(pred, label)
total_test_loss += loss.item()
# we now need softmax because we are testing.
pred = torch.nn.functional.softmax(pred, dim=1)
for i, p in enumerate(pred):
if label[i] == torch.max(p.data, 0)[1]:
total = total + 1
# caculate accuracy
accuracy = total / len(mnist_testset)
# append accuracy here
test_acc.append(accuracy)
# append test loss here
total_test_loss = total_test_loss / (itr + 1)
test_loss.append(total_test_loss)
print('\nEpoch: {}/{}, Train Loss: {:.8f}, Test Loss: {:.8f}, Test Accuracy: {:.8f}'.format(epoch + 1, no_epochs, total_train_loss, total_test_loss, accuracy))
if total_test_loss < best_test_loss:
best_test_loss = total_test_loss
print("Saving the model state dictionary for Epoch: {} with Test loss: {:.8f}".format(epoch + 1, total_test_loss))
torch.save(model.state_dict(), "model.dth")
# timer finishes
end = timer()
print(end - start)
return no_epochs, test_acc, test_loss
model_sgd = Model()
optimizer_SGD = torch.optim.SGD(model_sgd.parameters(), lr=0.1)
sgd_no_epochs, sgd_test_acc, sgd_test_loss = my_trainer(optimizer=optimizer_SGD, model=model_sgd)
我计算了每个 epoch 花费了多少时间。
下面是结果。
Epoch: 1/10, Train Loss: 0.23193890, Test Loss: 0.12670580, Test Accuracy: 0.96230000
63.98903721500005 seconds
Epoch: 2/10, Train Loss: 0.10275097, Test Loss: 0.10111042, Test Accuracy: 0.96730000
63.97179028100004 seconds
Epoch: 3/10, Train Loss: 0.07269370, Test Loss: 0.09668248, Test Accuracy: 0.97150000
63.969843954 seconds
Epoch: 4/10, Train Loss: 0.05658571, Test Loss: 0.09841745, Test Accuracy: 0.97070000
64.24135530400008 seconds
Epoch: 5/10, Train Loss: 0.04183391, Test Loss: 0.09828428, Test Accuracy: 0.97230000
64.19695308500013 seconds
Epoch: 6/10, Train Loss: 0.03393899, Test Loss: 0.08982467, Test Accuracy: 0.97530000
63.96944059600014 seconds
Epoch: 7/10, Train Loss: 0.02808819, Test Loss: 0.08597597, Test Accuracy: 0.97700000
63.59837343000004 seconds
Epoch: 8/10, Train Loss: 0.01859330, Test Loss: 0.07529452, Test Accuracy: 0.97950000
63.591578820999985 seconds
Epoch: 9/10, Train Loss: 0.01383720, Test Loss: 0.08568452, Test Accuracy: 0.97820000
63.66664020100029
Epoch: 10/10, Train Loss: 0.00911216, Test Loss: 0.07377760, Test Accuracy: 0.98060000
63.92636473799985 seconds
在此之后我将批量大小更改为 60000 并再次 运行 相同的程序。
train_dataloader = torch.utils.data.DataLoader(mnist_trainset,
batch_size=60000,
shuffle=True)
test_dataloader = torch.utils.data.DataLoader(mnist_testset,
batch_size=50,
shuffle=False)
print("\n===== Entering SGD optimizer =====\n")
model_sgd = Model()
optimizer_SGD = torch.optim.SGD(model_sgd.parameters(), lr=0.1)
sgd_no_epochs, sgd_test_acc, sgd_test_loss = my_trainer(optimizer=optimizer_SGD, model=model_sgd)
我得到了批量大小 == 60000 的结果
Epoch: 1/10, Train Loss: 2.32325006, Test Loss: 2.30074144, Test Accuracy: 0.11740000
6.54154992299982 seconds
Epoch: 2/10, Train Loss: 2.30010080, Test Loss: 2.29524792, Test Accuracy: 0.11790000
6.341824101999919 seconds
Epoch: 3/10, Train Loss: 2.29514933, Test Loss: 2.29183527, Test Accuracy: 0.11410000
6.161918789000083 seconds
Epoch: 4/10, Train Loss: 2.29196787, Test Loss: 2.28874513, Test Accuracy: 0.11450000
6.180891567999879 seconds
Epoch: 5/10, Train Loss: 2.28899717, Test Loss: 2.28571669, Test Accuracy: 0.11570000
6.1449509030003355 seconds
Epoch: 6/10, Train Loss: 2.28604794, Test Loss: 2.28270152, Test Accuracy: 0.11780000
6.311743144000047 seconds
Epoch: 7/10, Train Loss: 2.28307867, Test Loss: 2.27968731, Test Accuracy: 0.12250000
6.060618773999977 seconds
Epoch: 8/10, Train Loss: 2.28014660, Test Loss: 2.27666961, Test Accuracy: 0.12890000
6.171511712999745 seconds
Epoch: 9/10, Train Loss: 2.27718973, Test Loss: 2.27364607, Test Accuracy: 0.13930000
6.164125173999764 seconds
Epoch: 10/10, Train Loss: 2.27423453, Test Loss: 2.27061504, Test Accuracy: 0.15350000
6.077817454000069 seconds
正如您所看到的,很明显当 batch_size == 1 时每个 epoch 花费了更多时间,这与我所看到的不同。
也许我对每个时期的训练时间与收敛前的训练时间感到困惑?通过查看此网页,我的直觉似乎是正确的:https://medium.com/deep-learning-experiments/effect-of-batch-size-on-neural-net-training-c5ae8516e57
有人可以解释一下发生了什么吗?
这是一个边缘问题;你应该仍然能够从基础文献中提取这种理解......最终。
您的见解完全正确:您测量的是每个时期的执行时间,而不是总训练时间 (TTT)。您还采纳了通用的“小批量”建议荒谬:批量大小为 1 几乎肯定是次优的。
宏观层面的机制非常简单。
批量大小为 60k(整个训练集)时,您 运行 通过模型对所有 60k 图像进行平均,然后对该平均结果进行一次反向传播。这往往会失去从关注鲜为人知的功能中获得的知识。
批量大小为 1 时,您 运行 每个图像单独通过模型,对一个结果求平均(一个非常简单的操作 :-)),然后进行反向传播。这往往会过分强调个别效果,尤其是保留每个图像的迷信效果。它还对前几张图像的初始假设给予了过多的权重。
小批量最明显的影响是你正在做 60k back-props 而不是 1,所以每个 epoch 需要更长的时间。
这两种方法中的任何一种都是极端情况,在应用中通常是荒谬的。
您需要通过试验找到“最佳点”,让您以最快的速度收敛到可接受的(接近最佳的)准确度。在选择实验设计时有几个注意事项:
- 内存大小:您希望能够一次将整个批次摄取到内存中。这允许您的模型流水线读取和处理。如果超出可用内存,您将浪费大量时间进行交换。如果内存使用不足,则会留下一些未开发的潜在性能。
- 处理器:如果您使用的是多处理器芯片,您希望让它们都处于忙碌状态。如果您希望通过 OS 控件分配处理器,您还需要考虑分配给模型计算的处理器数量,以及分配给 I/O 和系统使用的处理器数量。例如,在我做的一个项目中,我们小组发现我们的 32 个内核最适合使用,其中 28 个分配给计算,4 个保留用于 I/O 和其他系统功能。
- 缩放:某些特征在 2 的幂中效果最好。您可能会发现批处理大小为 2^n 或 3 * 2^n 对于某些 n,效果最好,仅仅是因为块大小和其他系统分配。
多年来对我最有效的实验设计是从 2 的幂开始,它大致是训练集大小的平方根。对您来说,有一个明显的起始猜测值 256。因此,您可能 运行 在 64、128、256、512 和 1024 处进行实验。看看哪些给您带来最快的收敛。
然后使用因子 3 进行一步优化。例如,如果您发现 128 时性能最佳,也可以尝试 96 和 192。
您可能会发现您的“最佳点”与相邻批次大小之间的差别很小;这是大多数复杂信息系统的本质。