[笔记]飞浆PaddlePaddle-百度架构师手把手带你零基础实践深度学习-21日学习打卡(Day 5)
(Credit: https://gitee.com/paddlepaddle/Paddle/raw/develop/doc/imgs/logo.png)
资源配置
使用GPU进行训练
- CPU:
fluid.CPUPlace()
设置 - GPU:
fluid.CUDAPlace(0)
设置,参数0是GPU编号,比如拥有四个GPU卡的机器编号为0,1,2,3 - 以
dygraph.guard
函数的参数传入声明的place资源,with语句作用域下的代码在该资源上运行# 仅前3行代码有所变化,在使用GPU机器时,可以将use_gpu变量设置成True use_gpu = True place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace() with fluid.dygraph.guard(place): model = MNIST('mnist') model.train() # 调用加载数据的函数 train_loader = laod_data('train')
分布式训练(多卡)
- 两种并行计算方式
- 模型并行:节省内存,应用较为首先
- 数据并行:飞桨框架采用的实现方式
- CPU: Pserver通信方式
- GPU: NCCL通行方式
- 训练节点上运行同样的程序,以不同的数据做训练
- 不同训练节点计算的梯度需要聚合
- 实现“多GPU卡”的分布式训练
- 程序修改
- 命令行调用
分布式训练(多卡),程序修改
程序修改(四点)
- 启动训练前
- 获取环境变量定义的GPU序号,感知自己
- 对原模型做并行化预处理
- 定义过GPU训练的reader,不同ID的GPU加载不同的数据集
# 修改1 - 从环境变量获取使用GPU的序号 place = fluid.CUDAPlace(fliud.dygraph.parallel.Env().dev_id) with fluid.dygraph.guard(place): # 修改2 - 对原模型做并行化预处理 strategy = fluid.dygraph.paralle.prepare_context() model = MNIST('mnist') model = fluid.dygraph.paralle.DataParallel(model, strategy) model.train() # 调用加载数据的函数 train_loader = load_data('train') # 修改3 - 多GPU数据读取,必须确保每个进程读取的数据是不同的 train_loader = fluid.contrib.reader.distributed_batch_reader(train_loader)
- 训练过程中
- 对计算的loss做调整,并收集参数的梯度
# 修改4 - 多GPU训练需要对Loss做出调整,并聚合不同设备上的参数梯度 avg_loss = mnist.scale_loss(avg_loss) avg_loss.backward() model.apply_collective_grads()
分布式训练(多卡),命令行调用
命令行调用L制定具体在哪些GPU上运行
$ python -m paddle.distributed.launch --selected_gpus=0,1,2,3 --log_dir ./mylog train_multi_gpu.py
paddle.distributed.launch
: 启动分布式运行selected_gpus
: 设置使用的GPU的序号(需要是多GPU卡的机器,命令watch nvidia-smi
查看)log_dir
: 存放训练Log,如果不设置,每个GPU寻默认输出到屏幕train_multi_gpu.py
: 多GPU训练的程序,包含修改过的train_multi_gpu()
函数
训练过程:关键问题
- 加入更多的评估指标:分类准确率
实现方案:- Forward函数加入acc计算并返回结果
- 训练过程中去的该批次样本的acc(与loss不同,无需在做平均)
- 打印acc(分类准确率)
- 加入对训练过程的检查,确保正确执行
实现方案:- 在Forward函数中,打印模型每一层的参数和输出
- check_shape控制打印“尺寸”
- check_content控制打印“内容值”
- 通过“尺寸”验证网络结构是否正确,通过“内容值”验证数据分布是否合理
- 在Forward函数中,打印模型每一层的参数和输出
- 在测试数据集上评测,更好评价模型效果
实现方案:- 加载参数,模型设置成eval状态
- 读取校验的样本集
- 根据模型预测计算评估指标,注意需要将不同批次的评估结果取平均
- 在优化目标中加入正则化项,避免模型过拟合
实现方案:- 在优化目标中整体加入正则化项
- 对某一层的参数加入正则化项
- 加入作图,更好的分析训练效果
实现方案:- 引入PLT库
- 彼此编号作为X轴,记录在列表iters
- 该批次的训练损失作为Y轴,记录在列表loses
- 训练后将数据以参数灌入plt.plot作图
训练过程:飞桨可视化分析工具VisualDL
VisualDL:飞桨原装,辅助模型优化
模式:生产数据文件,启动Web服务展现
- 创建LogWriter对象,设置试验结果(数据)存放路径
# coding=utf-8 from visualdl import LogWriter # 创建LogWriter对象,实验结果存放在'./log/train'路径下 log_writer_1 = LogWriter('./log/train')
- 训练过程中插入作图语句
# 每训练了100批次的数据,打印下当前Loss的情况 if batch_id % 100 == 0: print ("epoch: {}, batch: {}, loss is: {}, acc is {}".format( epoch_id, batch_id, avg_loss.numpy(), avg_acc.numpy() )) log_writer_1.add_scalar(tag='acc', step=iter, value=avg_acc.numpy()) log_writer_1.add_scalar(tag='loss', step=iter, value=avg_loss.numpy()) iter = iter + 100
- 命令行输入:
$ visualdl --logdir ./log
上述命令执行后会给出可查的网址:http://0.0.0.0:8040 (默认本机),可通过参数修改网卡的配置:--host 168.225.0.1 --port 8080
- 打开浏览器输入网址即可查看
保存和加载模型:从预测场景到恢复训练场景
-
预测场景:
只保存模型参数 -
保存训练场景:
保存模型参数和优化器参数- 使用存在参数变化的Adam优化器(学习率以多项式曲线从0.01 -> 0.001衰减),方便校验恢复训练的过程是否正常
- 每一轮训练保存一次,轮次号区分保存的模型状态
# 定义学习率 total_steps = (int(60000/BATCH_SIZE) + 1)*EPOCH_NUM lr = fluid.dygraph.PolynomialDecay(0.01, total_steps, 0.001) optimizer = fluid.optimizer.Adam(learning_rate=lr) for epoch_id in range(EPOCH_NUM): pass # 保存模型参数和优化器的参数 fluid.save_dygraph(model.state_dict(), './checkpoint/mnist_epoch{}'.format(epoch_id)) fluid.save_dygraph(optimizer.state_dict(), './checkpoint/mnist_epoch{}'.format(epoch_id))
-
恢复训练场景
- 声明相同模型,加载模型参数
- 声明相同优化器,加载优化器参数
- 模型设置成“训练”状态
- 加载第0轮训练后的状态,从第1轮回复训练(效果与未中断完全一致)
# 加载模型参数到模型中 params_dict, opt_dict = fluid.load_dygraph(params_path) model = MNIST('mnist') model.load_dict(params_dict) # 定义学习率,并加载优化器参数到模型中 total_steps = (int(60000/BATCH_SIZE) + 1)*EPOCH_NUM lr = fluid.dygraph.PolynomialDecay(0.01, total_steps, 0.001) optimizer = fluid.optimizer.Adam(learning_rate=lr) optimizer.set_dict(opt_dict) # 从第一个epoch开始重新训练 for epoch_id in range(1, EPOCH_NUM): pass