如何解决批次大小和训练时间
感谢@Prune 对我的问题的批评性评论。
我试图通过使用 MNIST 数据集来找出批量大小和训练时间之间的关系。
通过阅读stackoverflow中的许多问题,例如这个: 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,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,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=False)
print("\n===== Entering SGD optimizer =====\n")
model_sgd = Model()
optimizer_SGD = torch.optim.SGD(model_sgd.parameters(),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 个反向道具而不是 1 个,所以每个 epoch 需要更长的时间。
这两种方法都属于极端情况,在应用中通常很荒谬。
您需要通过试验找到“最佳点”,使您能够以最快的速度收敛到可接受的(接近最佳的)精度。在选择实验设计时需要考虑以下几点:
- 内存大小:您希望能够一次将整个批次提取到内存中。这允许您的模型流水线读取和处理。如果超过可用内存,您将失去大量的交换时间。如果您未充分利用内存,就会留下一些潜在的性能未开发。
- 处理器:如果您使用的是多处理器芯片,您希望它们都处于忙碌状态。如果您想通过操作系统控件分配处理器,您还需要考虑分配给模型计算的处理器数量,以及分配给 I/O 和系统使用的处理器数量。例如,在我做过的一个项目中,我们的团队发现最好使用 32 个内核,其中 28 个分配给计算,4 个保留用于 I/O 和其他系统功能。
- 缩放:某些特征在 2 的幂时效果最佳。您可能会发现,对于某些 n,批量大小为 2^n 或 3 * 2^n 效果最佳,这仅仅是因为块大小和其他系统分配。
多年来对我来说效果最好的实验设计是从 2 的幂开始,大约是训练集大小的平方根。对您来说,显然有一个 256 的起始猜测。因此,您可能会在 64、128、256、512 和 1024 处运行实验。看看哪些能让您的收敛速度最快。
然后使用 3 的因子进行一步细化。例如,如果您发现最佳性能出现在 128,那么也尝试使用 96 和 192。
您可能会发现“最佳位置”与相邻批次大小之间的差异很小;这是大多数复杂信息系统的本质。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。