初探 MindSpore(四):把最小训练单元放进数据迭代
初探 MindSpore四把最小训练单元放进数据迭代第三篇已经把 MindSpore 的最小训练闭环搭出来了NetWithLossCellOptimizerTrainOneStepCell但这还只是“一步训练”。真正进入训练过程还需要回答两个问题数据从哪里来。一步训练如何重复执行。这一篇只处理这两件事不展开完整工程模板不引入复杂数据集也不讨论回调、Checkpoint 和评估体系。目标只有一个把最小训练单元放进最简单的数据迭代里看清 MindSpore 训练过程的基本连接方式。训练过程再往前只多出一层数据MindSpore 官方教程把模型训练概括为四步构建数据集定义网络定义超参数、损失函数和优化器输入数据进行训练与评估前面三篇已经把第 2 步和第 3 步压缩成了最小形式。第四篇补上的就是第 1 步和第 4 步里最核心的部分把数据喂给一步训练单元。最小训练闭环先保留不动先沿用第三篇里的最小训练单元importnumpyasnpimportmindsporeasmsimportmindspore.nnasnnclassNet(nn.Cell):def__init__(self):super().__init__()self.fcnn.Dense(10,2)defconstruct(self,x):returnself.fc(x)netNet()loss_fnnn.MSELoss()net_with_lossnn.WithLossCell(net,loss_fn)optimizernn.Adam(net.trainable_params(),learning_rate1e-3)train_stepnn.TrainOneStepCell(net_with_loss,optimizer)这部分不再解释。它的作用只是提供一个已经能执行单步训练的对象train_step。接下来要做的不是改训练骨架而是把数据按批次送进去。数据集最小入口GeneratorDataset如果只是为了说明训练过程如何接起来最轻量的入口不是直接加载真实数据集而是先使用GeneratorDataset。MindSpore 官方GeneratorDataset文档对它的定义很直接它是一个源数据集可以通过 Python 数据源在每个 epoch 生成数据。生成数据的列名和列类型由用户定义的数据决定。对这一篇来说GeneratorDataset的价值在于两点足够轻不需要额外准备真实数据文件已经属于 MindSpore 正式的数据集接口而不是临时 Python 列表一个最小例子如下importnumpyasnpimportmindspore.datasetasdsdefdata_generator():for_inrange(8):xnp.random.randn(10).astype(np.float32)ynp.random.randn(2).astype(np.float32)yieldx,y datasetds.GeneratorDataset(data_generator,column_names[data,label])datasetdataset.batch(4)这里的结构很简单原始生成器每次产出一条样本GeneratorDataset把它包装成 MindSpore 数据集batch(4)把单条样本合成 batch到这一步训练过程需要的数据入口就有了。先用最直接的方式跑一个训练循环如果只从理解训练过程的角度出发最直接的方式不是先引入Model.train而是先手动遍历数据集把 batch 逐个送进TrainOneStepCell。这样做的好处是结构最透明。foritemindataset.create_dict_iterator():losstrain_step(item[data],item[label])print(loss)这段代码只做了一件事从数据集中取出一个 batch把data和label送进train_step返回当前这一步的 loss对 PyTorch 用户来说这一层理解非常重要。因为这里清楚地展示了 MindSpore 最小训练过程的真实边界数据迭代在外层一步训练封装在TrainOneStepCell里。也就是说第四篇真正要让读者看清的不是某个具体 API而是“数据”和“一步训练”是怎样接起来的。把前面的部分拼成一个完整最小例子把网络、训练单元和数据集拼在一起就是一个最小可读的训练示例importnumpyasnpimportmindsporeasmsimportmindspore.nnasnnimportmindspore.datasetasdsclassNet(nn.Cell):def__init__(self):super().__init__()self.fcnn.Dense(10,2)defconstruct(self,x):returnself.fc(x)defdata_generator():for_inrange(8):xnp.random.randn(10).astype(np.float32)ynp.random.randn(2).astype(np.float32)yieldx,y datasetds.GeneratorDataset(data_generator,column_names[data,label])datasetdataset.batch(4)netNet()loss_fnnn.MSELoss()net_with_lossnn.WithLossCell(net,loss_fn)optimizernn.Adam(net.trainable_params(),learning_rate1e-3)train_stepnn.TrainOneStepCell(net_with_loss,optimizer)foritemindataset.create_dict_iterator():losstrain_step(item[data],item[label])print(loss)这段代码里最关键的不是随机数据本身而是训练结构终于完整了数据集负责提供 batchTrainOneStepCell负责执行一步训练外层循环负责重复调用训练步骤这就是最小训练过程。为什么先讲手动迭代而不是先讲Model.trainMindSpore 官方教程当然提供了更高层的训练入口。官方Model training教程本身就展示了Model、数据集、训练和评估这些更完整的接口组织方式。官方mindspore.train.Model文档也明确给出了train、eval、predict等高层入口。但第四篇不先从Model.train开始有一个非常实际的原因对于 PyTorch 用户先看懂训练是如何一步步拼起来的比先记住高层接口更重要。如果一开始就直接上Model.train(...)初学者当然能看到一个更短的训练入口但也更容易忽略真正的结构关系loss 是怎么接进网络的优化器是怎么接进训练单元的数据集最终是怎么送到训练步骤里的而手动迭代数据集再调用train_step恰好把这些关系全部露出来了。GeneratorDataset在这里的意义不是“真实”而是“最小”这一篇用GeneratorDataset不是因为它代表典型业务数据而是因为它足够小适合把训练连接过程讲清楚。官方教程中当然还有更完整的数据集写法包括使用已有标准数据集、做数据处理、做 shuffle 和 batch 等。但这一篇不需要这些内容。当前目标不是写一个完整训练项目而是验证一件事MindSpore 的训练过程至少需要两层显式结构数据集迭代以及一步训练封装。只要这一点看清后面再换成 MNIST、CIFAR-10 或其他真实数据集训练主干其实不会变。到这里最小训练过程已经完整了如果把前三篇和这一篇连起来看结构已经非常清楚第一篇解决框架基本结构nn.Module - nn.Cellforward - construct第二篇解决最小网络改写从一个最小 PyTorch 网络改写到 MindSpore第三篇解决一步训练封装Net - WithLossCell - Optimizer - TrainOneStepCell第四篇解决数据如何接进训练Dataset - batch - iterator - train_step也就是说到这一章为止已经能把一个最小网络真正跑起来了。虽然它还不是一个“完整项目”但训练主干已经具备。本文结论第四篇只建立一个结论在 MindSpore 里完整的最小训练过程不是单独写一个网络然后直接开训而是把数据集迭代和TrainOneStepCell显式接起来。如果压缩成三条就是数据先组织成Dataset数据按 batch 迭代每个 batch 交给TrainOneStepCell这也是 PyTorch 用户在进入 MindSpore 训练部分后接下来最应该建立的直觉。下一篇最自然的主题不是继续扩展更多数据接口而是引入更高一层的训练入口Model。到那时再回头看就会更容易理解Model.train()到底帮用户封装了什么。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2422627.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!