【FAQ】本地訓練與預測相關問題彙總

導語python

在使用指南的最後一部分,咱們彙總了使用PaddlePaddle過程當中的常見問題,本部分推文目錄以下:
算法


2.22:【FAQ】模型配置相關問題彙總express

2.23:【FAQ】參數設置相關問題彙總apache

2.24:【FAQ】本地訓練與預測相關問題彙總api

2.25:【FAQ】集羣訓練與預測相關問題彙總緩存

2.26:如何貢獻代碼網絡

2.27:如何貢獻文檔多線程





640?wx_fmt=png

本地訓練與預測相關問題彙總app

640?wx_fmt=png



|1. 如何減小內存佔用less


神經網絡的訓練自己是一個很是消耗內存和顯存的工做,常常會消耗數10GB的內存和數GB的顯存。 PaddlePaddle的內存佔用主要分爲以下幾個方面:


  • DataProvider緩衝池內存(只針對內存)

  • 神經元激活內存(針對內存和顯存)

  • 參數內存 (針對內存和顯存)

  • 其餘內存雜項

其中,其餘內存雜項是指PaddlePaddle自己所用的一些內存,包括字符串分配,臨時變量等等,暫不考慮在內。


A.減小DataProvider緩衝池內存


PyDataProvider使用的是異步加載,同時在內存裏直接隨即選取數據來作Shuffle。即:


640?wx_fmt=png 640?wx_fmt=png


因此,減少這個內存池便可減少內存佔用,同時也能夠加速開始訓練前數據載入的過程。可是,這 個內存池實際上決定了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開始,指定使用二、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

*歡迎在留言區分享您的觀點


640?wx_fmt=png


本文分享 CSDN - 飛槳PaddlePaddle。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索