|
|
|
@ -0,0 +1,201 @@
|
|
|
|
|
#########
|
|
|
|
|
参数设置
|
|
|
|
|
#########
|
|
|
|
|
|
|
|
|
|
.. contents::
|
|
|
|
|
|
|
|
|
|
1. 如何选择SGD算法的学习率
|
|
|
|
|
--------------------------
|
|
|
|
|
|
|
|
|
|
在采用sgd/async_sgd进行训练时,一个重要的问题是选择正确的learning_rate。如果learning_rate太大,那么训练有可能不收敛,如果learning_rate太小,那么收敛可能很慢,导致训练时间过长。
|
|
|
|
|
|
|
|
|
|
通常做法是从一个比较大的learning_rate开始试,如果不收敛,那减少学习率10倍继续试验,直到训练收敛为止。那么如何判断训练不收敛呢?可以估计出如果模型采用不变的输出最小的cost0是多少。
|
|
|
|
|
|
|
|
|
|
如果训练过程的的cost明显高于这个常数输出的cost,那么我们可以判断为训练不收敛。举一个例子,假如我们是三分类问题,采用multi-class-cross-entropy作为cost,数据中0,1,2三类的比例为 :code:`0.2, 0.5, 0.3` , 那么常数输出所能达到的最小cost是 :code:`-(0.2*log(0.2)+0.5*log(0.5)+0.3*log(0.3))=1.03` 。如果训练一个pass(或者更早)后,cost还大于这个数,那么可以认为训练不收敛,应该降低学习率。
|
|
|
|
|
|
|
|
|
|
2. 如何设置学习率退火(learning rate annealing)
|
|
|
|
|
------------------------------------------------
|
|
|
|
|
|
|
|
|
|
在相应的优化算法里设置learning_rate_schedule及相关参数,以使用Adam算法为例,代码如下:
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
optimizer = paddle.optimizer.Adam(
|
|
|
|
|
learning_rate=1e-3,
|
|
|
|
|
learning_rate_decay_a=0.5,
|
|
|
|
|
learning_rate_decay_b=0.75,
|
|
|
|
|
learning_rate_schedule="poly",)
|
|
|
|
|
|
|
|
|
|
PaddlePaddle目前支持8种learning_rate_schedule,这8种learning_rate_schedule及其对应学习率计算方式如下:
|
|
|
|
|
|
|
|
|
|
* "constant"
|
|
|
|
|
|
|
|
|
|
lr = learning_rate
|
|
|
|
|
|
|
|
|
|
* "poly"
|
|
|
|
|
|
|
|
|
|
lr = learning_rate * pow(1 + learning_rate_decay_a * num_samples_processed, -learning_rate_decay_b)
|
|
|
|
|
|
|
|
|
|
其中,num_samples_processed为已训练样本数,下同。
|
|
|
|
|
|
|
|
|
|
* "caffe_poly"
|
|
|
|
|
|
|
|
|
|
lr = learning_rate * pow(1.0 - num_samples_processed / learning_rate_decay_a, learning_rate_decay_b)
|
|
|
|
|
|
|
|
|
|
* "exp"
|
|
|
|
|
|
|
|
|
|
lr = learning_rate * pow(learning_rate_decay_a, num_samples_processed / learning_rate_decay_b)
|
|
|
|
|
|
|
|
|
|
* "discexp"
|
|
|
|
|
|
|
|
|
|
lr = learning_rate * pow(learning_rate_decay_a, floor(num_samples_processed / learning_rate_decay_b))
|
|
|
|
|
|
|
|
|
|
* "linear"
|
|
|
|
|
|
|
|
|
|
lr = max(learning_rate - learning_rate_decay_a * num_samples_processed, learning_rate_decay_b)
|
|
|
|
|
|
|
|
|
|
* "manual"
|
|
|
|
|
|
|
|
|
|
这是一种按已训练样本数分段取值的学习率退火方法。使用该learning_rate_schedule时,用户通过参数 :code:`learning_rate_args` 设置学习率衰减因子分段函数,当前的学习率为所设置 :code:`learning_rate` 与当前的衰减因子的乘积。以使用Adam算法为例,代码如下:
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
optimizer = paddle.optimizer.Adam(
|
|
|
|
|
learning_rate=1e-3,
|
|
|
|
|
learning_rate_schedule="manual",
|
|
|
|
|
learning_rate_args="1000:1.0,2000:0.9,3000:0.8",)
|
|
|
|
|
|
|
|
|
|
在该示例中,当已训练样本数小于等于1000时,学习率为 :code:`1e-3 * 1.0`;当已训练样本数大于1000小于等于2000时,学习率为 :code:`1e-3 * 0.9`;当已训练样本数大于2000时,学习率为 :code:`1e-3 * 0.8`。
|
|
|
|
|
|
|
|
|
|
* "pass_manual"
|
|
|
|
|
|
|
|
|
|
这是一种按已训练pass数分段取值的学习率退火方法。使用该learning_rate_schedule时,用户通过参数 :code:`learning_rate_args` 设置学习率衰减因子分段函数,当前的学习率为所设置 :code:`learning_rate` 与当前的衰减因子的乘积。以使用Adam算法为例,代码如下:
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
optimizer = paddle.optimizer.Adam(
|
|
|
|
|
learning_rate=1e-3,
|
|
|
|
|
learning_rate_schedule="manual",
|
|
|
|
|
learning_rate_args="1:1.0,2:0.9,3:0.8",)
|
|
|
|
|
|
|
|
|
|
在该示例中,当已训练pass数小于等于1时,学习率为 :code:`1e-3 * 1.0`;当已训练pass数大于1小于等于2时,学习率为 :code:`1e-3 * 0.9`;当已训练pass数大于2时,学习率为 :code:`1e-3 * 0.8`。
|
|
|
|
|
|
|
|
|
|
3. 如何初始化参数
|
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
|
|
默认情况下,PaddlePaddle使用均值0,标准差为 :math:`\frac{1}{\sqrt{d}}` 来初始化参数。其中 :math:`d` 为参数矩阵的宽度。这种初始化方式在一般情况下不会产生很差的结果。如果用户想要自定义初始化方式,PaddlePaddle目前提供两种参数初始化的方式\:
|
|
|
|
|
|
|
|
|
|
* 高斯分布。将 :code:`param_attr` 设置成 :code:`param_attr=ParamAttr(initial_mean=0.0, initial_std=1.0)`
|
|
|
|
|
* 均匀分布。将 :code:`param_attr` 设置成 :code:`param_attr=ParamAttr(initial_max=1.0, initial_min=-1.0)`
|
|
|
|
|
|
|
|
|
|
比如设置一个全连接层的参数初始化方式和bias初始化方式,可以使用如下代码。
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
hidden = fc_layer(input=ipt, param_attr=ParamAttr(initial_max=1.0, initial_min=-1.0),
|
|
|
|
|
bias_attr=ParamAttr(initial_mean=1.0, initial_std=0.0))
|
|
|
|
|
|
|
|
|
|
上述代码将bias全部初始化为1.0, 同时将参数初始化为 :code:`[1.0, -1.0]` 的均匀分布。
|
|
|
|
|
|
|
|
|
|
4. 如何共享参数
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
PaddlePaddle的参数使用名字 :code:`name` 作为参数的ID,相同名字的参数,会共享参数。设置参数的名字,可以使用 :code:`ParamAttr(name="YOUR_PARAM_NAME")` 来设置。更方便的设置方式,是使得要共享的参数使用同样的 :code:`ParamAttr` 对象。
|
|
|
|
|
|
|
|
|
|
简单的全连接网络,参数共享的配置示例为\:
|
|
|
|
|
|
|
|
|
|
.. literalinclude:: ../../python/paddle/trainer_config_helpers/tests/configs/shared_fc.py
|
|
|
|
|
|
|
|
|
|
这里 :code:`hidden_a` 和 :code:`hidden_b` 使用了同样的parameter和bias。并且softmax层的两个输入也使用了同样的参数 :code:`softmax_param`。
|
|
|
|
|
|
|
|
|
|
5. 如何加载预训练参数
|
|
|
|
|
------------------------
|
|
|
|
|
|
|
|
|
|
* 对加载预训练参数的层,设置其参数属性 :code:`is_static=True`,使该层的参数在训练过程中保持不变。以embedding层为例,代码如下:
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
emb_para = paddle.attr.Param(name='emb', is_static=True)
|
|
|
|
|
paddle.layer.embedding(size=word_dim, input=x, param_attr=emb_para)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* 从模型文件将预训练参数载入 :code:`numpy.array`,在创建parameters后,使用 :code:`parameters.set()` 加载预训练参数。PaddlePaddle保存的模型参数文件前16字节为头信息,用户将参数载入 :code:`numpy.array` 时须从第17字节开始。以embedding层为例,代码如下:
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
def load_parameter(file_name, h, w):
|
|
|
|
|
with open(file_name, 'rb') as f:
|
|
|
|
|
f.read(16) # skip header.
|
|
|
|
|
return np.fromfile(f, dtype=np.float32).reshape(h, w)
|
|
|
|
|
|
|
|
|
|
parameters = paddle.parameters.create(my_cost)
|
|
|
|
|
parameters.set('emb', load_parameter(emb_param_file, 30000, 256))
|
|
|
|
|
|
|
|
|
|
6. 存储的参数格式是什么,如何和明文进行相互转化
|
|
|
|
|
--------------------------------------------------
|
|
|
|
|
|
|
|
|
|
PaddlePaddle保存的模型参数文件内容由16字节头信息和网络参数两部分组成。头信息中,1~4字节表示PaddlePaddle版本信息,请直接填充0;5~8字节表示每个参数占用的字节数,当保存的网络参数为float类型时为4,double类型时为8;9~16字节表示保存的参数总个数。
|
|
|
|
|
|
|
|
|
|
将PaddlePaddle保存的模型参数还原回明文时,可以使用相应数据类型的 :code:`numpy.array` 加载具体网络参数,此时可以跳过PaddlePaddle模型参数文件的头信息。若在PaddlePaddle编译时,未指定按照double精度编译,默认情况下按照float精度计算,保存的参数也是float类型。这时在使用 :code:`numpy.array` 时,一般设置 :code:`dtype=float32` 。示例如下:
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
def read_parameter(fname, width):
|
|
|
|
|
s = open(fname).read()
|
|
|
|
|
# skip header
|
|
|
|
|
vec = np.fromstring(s[16:], dtype=np.float32)
|
|
|
|
|
# width is the size of the corresponding layer
|
|
|
|
|
np.savetxt(fname + ".csv", vec.reshape(width, -1),
|
|
|
|
|
fmt="%.6f", delimiter=",")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
将明文参数转化为PaddlePaddle可加载的模型参数时,首先构造头信息,再写入网络参数。下面的代码将随机生成的矩阵转化为可以被PaddlePaddle加载的模型参数。
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
def gen_rand_param(param_file, width, height, need_trans):
|
|
|
|
|
np.random.seed()
|
|
|
|
|
header = struct.pack("iil", 0, 4, height * width)
|
|
|
|
|
param = np.float32(np.random.rand(height, width))
|
|
|
|
|
with open(param_file, "w") as fparam:
|
|
|
|
|
fparam.write(header + param.tostring())
|
|
|
|
|
|
|
|
|
|
7. A protocol message was rejected because it was too big
|
|
|
|
|
------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
如果在训练NLP相关模型时,出现以下错误:
|
|
|
|
|
|
|
|
|
|
.. code-block:: bash
|
|
|
|
|
|
|
|
|
|
[libprotobuf ERROR google/protobuf/io/coded_stream.cc:171] A protocol message was rejected because it was too big (more than 67108864 bytes). To increase the limit (or to disable these warnings), see CodedInputStream::SetTotalBytesLimit() in google/protobuf/io/coded_stream.h.
|
|
|
|
|
F1205 14:59:50.295174 14703 TrainerConfigHelper.cpp:59] Check failed: m->conf.ParseFromString(configProtoStr)
|
|
|
|
|
|
|
|
|
|
可能的原因是:传给dataprovider的某一个args过大,一般是由于直接传递大字典导致的。错误的define_py_data_sources2类似:
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
src_dict = dict()
|
|
|
|
|
for line_count, line in enumerate(open(src_dict_path, "r")):
|
|
|
|
|
src_dict[line.strip()] = line_count
|
|
|
|
|
|
|
|
|
|
define_py_data_sources2(
|
|
|
|
|
train_list,
|
|
|
|
|
test_list,
|
|
|
|
|
module="dataprovider",
|
|
|
|
|
obj="process",
|
|
|
|
|
args={"src_dict": src_dict})
|
|
|
|
|
|
|
|
|
|
解决方案是:将字典的地址作为args传给dataprovider,然后在dataprovider里面根据该地址加载字典。即define_py_data_sources2应改为:
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
define_py_data_sources2(
|
|
|
|
|
train_list,
|
|
|
|
|
test_list,
|
|
|
|
|
module="dataprovider",
|
|
|
|
|
obj="process",
|
|
|
|
|
args={"src_dict_path": src_dict_path})
|
|
|
|
|
|
|
|
|
|
完整源码可参考 `seqToseq <https://github.com/PaddlePaddle/Paddle/tree/develop/demo/seqToseq>`_ 示例。
|
|
|
|
|
|
|
|
|
|
|