批次大小和训练时间

如何解决批次大小和训练时间

感谢@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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-