组网、训练、评估常见问题¶
问题:stop_gradient=True
的影响范围?¶
答复:如果静态图中某一层使用
stop_gradient=True
,那么这一层之前的层都会自动stop_gradient=True
,梯度不再回传。
问题:请问paddle.matmul
和paddle.multiply
有什么区别?¶
答复:
matmul
支持的两个 tensor 的矩阵乘操作。muliply
是支持两个 tensor 进行逐元素相乘。
问题:请问paddle.gather
和torch.gather
有什么区别?¶
答复:
paddle.gather
和torch.gather
的函数签名分别为:
paddle.gather(x, index, axis=None, name=None)
torch.gather(input, dim, index, *, sparse_grad=False, out=None)
其中,paddle.gather
的参数x
,index
,axis
分别与torch.gather
的参数input
,index
,dim
意义相同。
两者在输入形状、输出形状、计算公式等方面都有区别,具体如下:
paddle.gather
输入形状:
x
可以是任意的N
维 Tensor。但index
必须是形状为[M]
的一维 Tensor,或形状为[M, 1]
的二维 Tensor。输出形状:输出 Tensor
out
的形状shape_out
和x
的形状shape_x
的关系为:shape_out[i] = shape_x[i] if i != axis else M
。计算公式:
out[i_1][i_2]...[i_axis]...[i_N] = x[i_1][i_2]...[index[i_axis]]...[i_N]
。举例说明:假设
x
的形状为[N1, N2, N3]
,index
的形状为[M]
,axis
的值为 1,那么输出out
的形状为[N1, M, N3]
,且out[i_1][i_2][i_3] = x[i_1][index[i_2]][i_3]
。
torch.gather
输入形状:
input
可以是任意的N
维 Tensor,且index.rank
必须等于input.rank
。输出形状:输出 Tensor
out
的形状与index
相同。计算公式:
out[i_1][i_2]...[i_dim]...[i_N] = input[i_1][i_2]...[index[i_1][i_2]...[i_N]]...[i_N]
。举例说明:假设
x
的形状为[N1, N2, N3]
,index
的形状为[M1, M2, M3]
,dim
的值为 1,那么输出out
的形状为[M1, M2, M3]
,且out[i_1][i_2][i_3] = input[i_1][index[i_1][i_2][i_3]][i_3]
。
异同比较
只有当
x.rank == 1
且index.rank == 1
时,paddle.gather
和torch.gather
功能相同。其余情况两者无法直接互换使用。paddle.gather
不支持torch.gather
的sparse_grad
参数。
问题:在模型组网时,inplace 参数的设置会影响梯度回传吗?经过不带参数的 op 之后,梯度是否会保留下来?¶
答复:inplace 参数不会影响梯度回传。只要用户没有手动设置
stop_gradient=True
,梯度都会保留下来。
问题:使用 CPU 进行模型训练,如何利用多处理器进行加速?¶
答复:在 2.0 版本动态图模式下,CPU 训练加速可以从以下两点进行配置:
使用多进程 DataLoader 加速数据读取:训练数据较多时,数据处理往往会成为训练速度的瓶颈,paddle 提供了异步数据读取接口 DataLoader,可以使用多进程进行数据加载,充分利用多处理的优势,具体使用方法及示例请参考 API 文档:paddle.io.DataLoader。
推荐使用支持MKL(英特尔数学核心函数库)的 paddle 安装包,MKL 相比 Openblas 等通用计算库在计算速度上有显著的优势,能够提升您的训练效率。
问题:使用 NVIDIA 多卡运行 Paddle 时报错 Nccl error,如何解决?¶
答复:这个错误大概率是环境配置不正确导致的,建议您使用 NVIDIA 官方提供的方法参考检测自己的环境是否配置正确。具体地,可以使用 NCCL Tests 检测您的环境;如果检测不通过,请登录 NCCL 官网 下载 NCCl,安装后重新检测。
问题:多卡训练时启动失败,Error:Out of all 4 Trainers
,如何处理?¶
问题描述:多卡训练时启动失败,显示如下信息:
报错分析:主进程发现一号卡(逻辑)上的训练进程退出了。
解决方法:查看一号卡上的日志,找出具体的出错原因。
paddle.distributed.launch
启动多卡训练时,设置--log_dir
参数会将每张卡的日志保存在设置的文件夹下。
问题:训练时报错提示显存不足,如何解决?¶
答复:可以尝试按如下方法解决:
检查是当前模型是否占用了过多显存,可尝试减小
batch_size
;开启以下三个选项:
#一旦不再使用即释放内存垃圾,=1.0 垃圾占用内存大小达到 10G 时,释放内存垃圾
export FLAGS_eager_delete_tensor_gb=0.0
#启用快速垃圾回收策略,不等待 cuda kernel 结束,直接释放显存
export FLAGS_fast_eager_deletion_mode=1
#该环境变量设置只占用 0%的显存
export FLAGS_fraction_of_gpu_memory_to_use=0
详细请参考官方文档存储分配与优化 调整相关配置。
此外,建议您使用AI Studio 学习与 实训社区训练,获取免费 GPU 算力,提升您的训练效率。
问题:如何提升模型训练时的 GPU 利用率?¶
答复:有如下两点建议:
如果数据预处理耗时较长,可使用 DataLoader 加速数据读取过程,具体请参考 API 文档:paddle.io.DataLoader。
如果提高 GPU 计算量,可以增大
batch_size
,但是注意同时调节其他超参数以确保训练配置的正确性。
以上两点均为比较通用的方案,其他的优化方案和模型相关,可参考官方模型库 models 中的具体示例。
问题:如何处理变长 ID 导致程序内存占用过大的问题?¶
答复:请先参考显存分配与优化文档 开启存储优化开关,包括显存垃圾及时回收和 Op 内部的输出复用输入等。若存储空间仍然不够,建议:
降低
batch_size
;对 index 进行排序,减少 padding 的数量。
问题:训练过程中如果出现不收敛的情况,如何处理?¶
答复:不收敛的原因有很多,可以参考如下方式排查:
检查数据集中训练数据的准确率,数据是否有错误,特征是否归一化;
简化网络结构,先基于 benchmark 实验,确保在 baseline 网络结构和数据集上的收敛结果正确;
对于复杂的网络,每次只增加一个改动,确保改动后的网络正确;
检查网络在训练数据上的 Loss 是否下降;
检查学习率、优化算法是否合适,学习率过大会导致不收敛;
检查
batch_size
设置是否合适,batch_size
过小会导致不收敛;检查梯度计算是否正确,是否有梯度过大的情况,是否为
NaN
。
问题:Loss 为 NaN,如何处理?¶
答复:可能由于网络的设计问题,Loss 过大(Loss 为 NaN)会导致梯度爆炸。如果没有改网络结构,但是出现了 NaN,可能是数据读取导致,比如标签对应关系错误。还可以检查下网络中是否会出现除 0,log0 的操作等。
问题:训练后的模型很大,如何压缩?¶
答复:建议您使用飞桨模型压缩工具PaddleSlim。PaddleSlim 是飞桨开源的模型压缩工具库,包含模型剪裁、定点量化、知识蒸馏、超参搜索和模型结构搜索等一系列模型压缩策略,专注于模型小型化技术。
问题:load_inference_model
在加载预测模型时能否用py_reader
读取?¶
答复:目前
load_inference_model
加载进行的模型还不支持py_reader
输入。
问题:预测时如何打印模型中每一步的耗时?¶
答复:可以在设置 config 时使用
config.enable_profile()
统计预测时每个算子和数据搬运的耗时。对于推理 api 的使用,可以参考官网文档Python 预测 API 介绍。示例代码:
# 设置 config:
def set_config(args):
config = Config(args.model_file, args.params_file)
config.disable_gpu()
# enable_profile()打开后会统计每一步耗时
config.enable_profile()
config.switch_use_feed_fetch_ops(False)
config.switch_specify_input_names(True)
config.switch_ir_optim(False)
return config
问题:模型训练时如何进行梯度裁剪?¶
答复:设置 Optimizer 中的
grad_clip
参数值。
问题:静态图模型如何拿到某个 variable 的梯度?¶
答复:飞桨提供以下三种方式,用户可根据需求选择合适的方法:
使用paddle.static.Print()接口,可以打印中间变量及其梯度。
将变量梯度名放到 fetch_list 里,通过Executor.run()获取,一般 variable 的梯度名是 variable 的名字加上 "@GRAD"。
对于参数(不适用于中间变量和梯度),还可以通过Scope.find_var()接口,通过变量名字查找对应的 tensor。
后两个方法需要使用变量名,飞桨中变量的命名规则请参见Name 。
# paddlepaddle>=2.0
import paddle
import numpy as np
paddle.enable_static()
data = paddle.static.data('data', shape=[4, 2])
out = paddle.static.nn.fc(x=data, size=1, num_flatten_dims=1, name='fc')
loss = paddle.mean(out)
loss = paddle.static.Print(loss) # 通过 Print 算子打印中间变量及梯度
opt = paddle.optimizer.SGD(learning_rate=0.01)
opt.minimize(loss)
exe = paddle.static.Executor()
exe.run(paddle.static.default_startup_program())
loss, loss_g, fc_bias_g = exe.run(
paddle.static.default_main_program(),
feed={'data': np.random.rand(4, 2).astype('float32')},
fetch_list=[loss, loss.name + '@GRAD', 'fc.b_0@GRAD']) # 通过将变量名加入到 fetch_list 获取变量
print(loss, loss_g, fc_bias_g)
print(paddle.static.global_scope().find_var('fc.b_0').get_tensor()) # 通过 scope.find_var 获取变量
问题:paddle 有对应 torch.masked_fill 函数 api 吗,还是需要自己实现?¶
答复:由于框架设计上的区别,没有对应的 api,但是可以使用 paddle.where 实现相同的功能。
# paddlepaddle >= 2.0
import paddle
paddle.seed(123)
x = paddle.rand([3, 3], dtype='float32')
# Tensor(shape=[3, 3], dtype=float32, place=CUDAPlace(0), stop_gradient=True,
# [[0.00276479, 0.45899123, 0.96637046],
# [0.66818708, 0.05855134, 0.33184195],
# [0.34202638, 0.95503175, 0.33745834]])
mask = paddle.randint(0, 2, [3, 3]).astype('bool')
# Tensor(shape=[3, 3], dtype=bool, place=CUDAPlace(0), stop_gradient=True,
# [[True , True , False],
# [True , True , True ],
# [True , True , True ]])
def masked_fill(x, mask, value):
y = paddle.full(x.shape, value, x.dtype)
return paddle.where(mask, y, x)
out = masked_fill(x, mask, 2)
# Tensor(shape=[3, 3], dtype=float32, place=CUDAPlace(0), stop_gradient=True,
# [[2. , 2. , 0.96637046],
# [2. , 2. , 2. ],
# [2. , 2. , 2. ]])
问题:在 paddle 中如何实现torch.nn.utils.rnn.pack_padded_sequence
和torch.nn.utils.rnn.pad_packed_sequence
这两个 API?¶
答复:目前 paddle 中没有和上述两个 API 完全对应的实现。关于 torch 中这两个 API 的详细介绍可以参考知乎上的文章 pack_padded_sequence 和 pad_packed_sequence :
pack_padded_sequence
的功能是将 mini-batch 数据进行压缩,压缩掉无效的填充值,然后输入 RNN 网络中;pad_packed_sequence
则是把 RNN 网络输出的压紧的序列再填充回来,便于进行后续的处理。 在 paddle 中,大家可以在 GRU、LSTM 等 RNN 网络中输入含有填充值的 mini-batch 数据的同时传入对应的sequence_length
参数实现上述等价功能,具体用法可以参考 RNN 。
问题:paddle 是否有爱因斯坦求和(einsum)这个 api?¶
答复:paddle 在 2.2rc 版本之后,新增了paddle.einsum,在 develop 和 2.2rc 之后的版本中都可以正常使用。
问题:BatchNorm在训练时加载预测时保存的模型参数时报错 AssertionError: Optimizer set error, batch_norm_1.w_0_moment_0 should in state dict.¶
答复:BatchNorm 在 train 模式和 eval 模式下需要的变量有差别,在 train 模式下要求传入优化器相关的变量,在 eval 模式下不管是保存参数还是加载参数都是不需要优化器相关变量的,因此如果在 train 模式下加载 eval 模式下保存的 checkpoint,没有优化器相关的变量则会报错。如果想在 train 模式下加载 eval 模式下保存的 checkpoint 的话,用
paddle.load
加载进来参数之后,通过set_state_dict
接口把参数赋值给模型,参考以下示例:
import paddle
bn = paddle.nn.BatchNorm(3)
bn_param = paddle.load('./bn.pdparams')
bn.set_state_dict()