【FAQ】本地训练与预测相关问题汇总

本文档汇总了使用PaddlePaddle过程中的常见问题,包括如何减少内存占用、如何加速训练速度、如何指定GPU设备、如何调用infer接口输出多个layer的预测结果等关键信息。

导语

在使用指南的最后一部分,我们汇总了使用PaddlePaddle过程中的常见问题,本部分推文目录如下:

2.22:【FAQ】模型配置相关问题汇总

2.23:【FAQ】参数设置相关问题汇总

2.24:【FAQ】本地训练与预测相关问题汇总

2.25:【FAQ】集群训练与预测相关问题汇总

2.26:如何贡献代码

2.27:如何贡献文档

本地训练与预测相关问题汇总

|1. 如何减少内存占用

神经网络的训练本身是一个非常消耗内存和显存的工作,经常会消耗数10GB的内存和数GB的显存。 PaddlePaddle的内存占用主要分为如下几个方面:

  • DataProvider缓冲池内存(只针对内存)

  • 神经元激活内存(针对内存和显存)

  • 参数内存 (针对内存和显存)

  • 其他内存杂项

其中,其他内存杂项是指PaddlePaddle本身所用的一些内存,包括字符串分配,临时变量等等,暂不考虑在内。

A.减少DataProvider缓冲池内存

PyDataProvider使用的是异步加载,同时在内存里直接随即选取数据来做Shuffle。即:

所以,减小这个内存池即可减小内存占用,同时也可以加速开始训练前数据载入的过程。但是,这 个内存池实际上决定了shuffle的粒度。所以,如果将这个内存池减小,又要保证数据是随机的, 那么最好将数据文件在每次读取之前做一次shuffle。可能的代码为:

#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

#

# Licensed under the Apache License, Version 2.0 (the "License");

# you may not use this file except in compliance with the License.

# You may obtain a copy of the License at

#

#     http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

@provider(min_pool_size=0, ...)

def process(settings, filename):

    os.system('shuf %s > %s.shuf' % (filename, filename))  # shuffle before.

    with open('%s.shuf' % filename, 'r') as f:

        for line in f:

            yield get_sample_from_line(line)

这样做可以极大的减少内存占用,并且可能会加速训练过程,详细文档参考 api_pydataprovider2 。

B.神经元激活内存

神经网络在训练的时候,会对每一个激活暂存一些数据,如神经元激活值等。 在反向传递的时候,这些数据会被用来更新参数。这些数据使用的内存主要和两个参数有关系, 一是batch size,另一个是每条序列(Sequence)长度。所以,其实也是和每个mini-batch中包含 的时间步信息成正比。

所以做法可以有两种:

  • 减小batch size。 即在网络配置中 settings(batch_size=1000) 设置成一个小一些的值。但是batch size本身是神经网络的超参数,减小batch size可能会对训练结果产生影响。

  • 减小序列的长度,或者直接扔掉非常长的序列。比如,一个数据集大部分序列长度是100-200, 但是突然有一个10000长的序列,就很容易导致内存超限,特别是在LSTM等RNN中。

C.参数内存

PaddlePaddle支持非常多的优化算法(Optimizer),不同的优化算法需要使用不同大小的内存。 例如使用 adadelta 算法,则需要使用等于权重参数规模大约5倍的内存。举例,如果参数保存下来的模型目录 文件为 100M, 那么该优化算法至少需要 500M 的内存。

可以考虑使用一些优化算法,例如 momentum。

|2.如何加速训练速度

加速PaddlePaddle训练可以考虑从以下几个方面:

  • 减少数据载入的耗时

  • 加速训练速度

  • 利用分布式训练驾驭更多的计算资源

A.减少数据载入的耗时

使用pydataprovider时,可以减少缓存池的大小,同时设置内存缓存功能,即可以极大的加速数据载入流程。 DataProvider 缓存池的减小,和之前减小通过减小缓存池来减小内存占用的原理一致。

#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

#

# Licensed under the Apache License, Version 2.0 (the "License");

# you may not use this file except in compliance with the License.

# You may obtain a copy of the License at

#

#     http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

@provider(min_pool_size=0, ...)

def process(settings, filename):

    os.system('shuf %s > %s.shuf' % (filename, filename))  # shuffle before.

    with open('%s.shuf' % filename, 'r') as f:

        for line in f:

            yield get_sample_from_line(line)

同时 @provider 接口有一个 cache 参数来控制缓存方法,将其设置成 CacheType.CACHE_PASS_IN_MEM 的话,会将第一个 pass (过完所有训练数据即为一个pass)生成的数据缓存在内存里,在之后的 pass 中,不会再从 python 端读取数据,而是直接从内存的缓存里读取数据。这也会极大减少数据读入的耗时。

B.加速训练速度

PaddlePaddle支持Sparse的训练,sparse训练需要训练特征是 sparse_binary_vector 、 sparse_vector 、或者 integer_value 的任一一种。同时,与这个训练数据交互的Layer,需要将其Parameter设置成 sparse 更新模式,即设置 sparse_update=True

这里使用简单的 word2vec 训练语言模型距离,具体使用方法为:使用一个词前两个词和后两个词,来预测这个中间的词。这个任务的DataProvider为:

#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

#

# Licensed under the Apache License, Version 2.0 (the "License");

# you may not use this file except in compliance with the License.

# You may obtain a copy of the License at

#

#     http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

DICT_DIM = 3000

@provider(input_types=[integer_sequence(DICT_DIM), integer_value(DICT_DIM)])

def process(settings, filename):

    with open(filename) as f:

        # yield word ids to predict inner word id

        # such as [28, 29, 10, 4], 4

        # It means the sentance is  28, 29, 4, 10, 4.

        yield read_next_from_file(f)

这个任务的配置为:

#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.

#

# Licensed under the Apache License, Version 2.0 (the "License");

# you may not use this file except in compliance with the License.

# You may obtain a copy of the License at

#

#     http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

...  # the settings and define data provider is omitted.

DICT_DIM = 3000  # dictionary dimension.

word_ids = data_layer('word_ids', size=DICT_DIM)

emb = embedding_layer(

    input=word_ids, size=256, param_attr=ParamAttr(sparse_update=True))

emb_sum = pooling_layer(input=emb, pooling_type=SumPooling())

predict = fc_layer(input=emb_sum, size=DICT_DIM, act=Softmax())

outputs(

    classification_cost(

        input=predict, label=data_layer(

            'label', size=DICT_DIM)))

C.利用更多的计算资源

利用更多的计算资源可以分为以下几个方式来进行:

  • 单机CPU训练

使用多线程训练。设置命令行参数 trainer_count。

  • 单机GPU训练

使用显卡训练。设置命令行参数 use_gpu。

使用多块显卡训练。设置命令行参数 use_gpu 和 trainer_count 。

  • 多机训练

请参考 cluster_train。

|3. 如何指定GPU设备

例如机器上有4块GPU,编号从0开始,指定使用2、3号GPU:

方式1:通过 CUDA_VISIBLE_DEVICES(链接:http://www.acceleware.com/blog/cudavisibledevices-masking-gpus) 环境变量来指定特定的GPU。

env CUDA_VISIBLE_DEVICES=2,3 paddle train --use_gpu=true --trainer_count=2

方式2:通过命令行参数 --gpu_id 指定。

paddle train --use_gpu=true --trainer_count=2 --gpu_id=2

|4.如何调用 infer 接口输出多个layer的预测结果

将需要输出的层作为 paddle.inference.Inference() 接口的 output_layer 参数输入,代码如下:

inferer = paddle.inference.Inference(output_layer=[layer1, layer2], parameters=parameters)

指定要输出的字段进行输出。以输出 value 字段为例,代码如下:

out = inferer.infer(input=data_batch, field=["value"])

需要注意的是:

  • 如果指定了2个layer作为输出层,实际上需要的输出结果是两个矩阵;

  • 假设第一个layer的输出A是一个 N1 * M1 的矩阵,第二个 Layer 的输出B是一个 N2 * M2 的矩阵;

  • paddle.v2 默认会将A和B 横向拼接,当N1 和 N2 大小不一样时,会报如下的错误:

ValueError: all the input array dimensions except for the concatenation axis must match exactly

多个层的输出矩阵的高度不一致导致拼接失败,这种情况常常发生在:

  • 同时输出序列层和非序列层;

  • 多个输出层处理多个不同长度的序列;

此时可以在调用infer接口时通过设置 flatten_result=False , 跳过“拼接”步骤,来解决上面的问题。这时,infer接口的返回值是一个python list:

  • list 中元素的个数等于网络中输出层的个数;

  • list 中每个元素是一个layer的输出结果矩阵,类型是numpy的ndarray;

  • 每一个layer输出矩阵的高度,在非序列输入时:等于样本数;序列输入时等于:输入序列中元素的总数;宽度等于配置中layer的size;    

|5. 如何在训练过程中获得某一个layer的output

可以在event_handler中,通过 event.gm.getLayerOutputs("layer_name") 获得在模型配置中某一层的name layer_name 在当前 mini-batch forward的output的值。获得的值类型均为 numpy.ndarray ,可以通过这个输出来完成自定义的评估指标计算等功能。例如下面代码:

def score_diff(right_score, left_score):

    return np.average(np.abs(right_score - left_score))

def event_handler(event):

    if isinstance(event, paddle.event.EndIteration):

        if event.batch_id % 25 == 0:

            diff = score_diff(

                event.gm.getLayerOutputs("right_score")["right_score"][

                    "value"],

                event.gm.getLayerOutputs("left_score")["left_score"][

                    "value"])

            logger.info(("Pass %d Batch %d : Cost %.6f, "

                        "average absolute diff scores: %.6f") %

                        (event.pass_id, event.batch_id, event.cost, diff))

注意:此方法不能获取 paddle.layer.recurrent_group 里step的内容,但可以获取 paddle.layer.recurrent_group 的输出。

|6.  如何在训练过程中获得参数的权重和梯度

在某些情况下,获得当前mini-batch的权重(或称作weights, parameters)有助于在训练时观察具体数值,方便排查以及快速定位问题。 可以通过在 event_handler 中打印其值(注意,需要使用 paddle.event.EndForwardBackward 保证使用GPU训练时也可以获得), 示例代码如下:

...

parameters = paddle.parameters.create(cost)

...

def event_handler(event):

    if isinstance(event, paddle.event.EndForwardBackward):

        if event.batch_id % 25 == 0:

            for p in parameters.keys():

                logger.info("Param %s, Grad %s",

                    parameters.get(p), parameters.get_grad(p))

注意:“在训练过程中获得某一个layer的output”和“在训练过程中获得参数的权重和梯度”都会造成训练中的数据从C++拷贝到numpy,会对训练性能造成影响。不要在注重性能的训练场景下使用。

 end

*原创贴,版权所有,未经许可,禁止转载

*值班小Paddle:wangp

*欢迎在留言区分享您的观点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值