diff --git a/doc_cn/concepts/use_concepts.rst b/doc_cn/concepts/use_concepts.rst index f97fcc74bb0d5ce67e9076c0bc84e9a237dbcf3e..ea7a5c7d1860b8bffca954e6a76291dab5d72e95 100644 --- a/doc_cn/concepts/use_concepts.rst +++ b/doc_cn/concepts/use_concepts.rst @@ -15,7 +15,7 @@ PaddlePaddle是一个深度学习框架,支持单机模式和多机模式。 系统框图 ======== -下图描述了用户使用框图,PaddlePaddle的trainer进程里内嵌了Python解释器,trainer进程可以利用这个解释器执行Python脚本,Python脚本里定义了模型配置、训练算法、以及数据读取函数。其中,数据读取程序往往定义在一个单独Python脚本文件里,被称为DataProvider,通常是一个Python函数。模型配置、训练算法通常定义在另一单独Python文件中。下面将分别介绍这两部分。 +下图描述了用户使用框图,PaddlePaddle的trainer进程里内嵌了Python解释器,trainer进程可以利用这个解释器执行Python脚本,Python脚本里定义了模型配置、训练算法、以及数据读取函数。其中,数据读取程序往往定义在一个单独Python脚本文件里,被称为DataProvider,通常是一个Python函数。模型配置、训练算法通常定义在另一单独Python文件中, 称为训练配置文件。下面将分别介绍这两部分。 .. graphviz:: @@ -42,12 +42,12 @@ DataProvider是PaddlePaddle系统的数据提供器,将用户的原始数据 在不同的应用里,训练数据的格式往往各不相同。因此,为了用户能够灵活的处理数据,我们提供了Python处理数据的接口,称为 `PyDataProvider`_ 。在 ``PyDataProvider`` 中,系统C++模块接管了shuffle、处理batch、GPU和CPU通信、双缓冲、异步读取等问题,一些情况下(如:``min_pool_size=0``)需要Python接口里处理shuffle,可以参考 `PyDataProvider`_ 的相关文档继续深入了解。 -模型配置文件 +训练配置文件 ============ -模型配置文件主要包括数据传入接口定义(DataConfig)、优化算法(OptimizationConfig)、网络结构(ModelConfig)。 其中数据传入接口定义与DataProvider的关系是:DataProvider里定义数据读取函数,配置文件的DataConfig里指定DataProvider文件名字、生成数据函数接口,请不要混淆。 +训练配置文件主要包括数据传入接口定义(DataConfig)、优化算法(OptimizationConfig)、网络结构(ModelConfig)。 其中数据传入接口定义与DataProvider的关系是:DataProvider里定义数据读取函数,配置文件的DataConfig里指定DataProvider文件名字、生成数据函数接口,请不要混淆。 -一个简单的模型配置文件为: +一个简单的训练配置文件为: .. literalinclude:: trainer_config.py :linenos: diff --git a/doc_cn/ui/data_provider/dataprovider.rst b/doc_cn/ui/data_provider/dataprovider.rst new file mode 100644 index 0000000000000000000000000000000000000000..e6796429a78801eba5e5fb776dd6fbe3413115ea --- /dev/null +++ b/doc_cn/ui/data_provider/dataprovider.rst @@ -0,0 +1,13 @@ +DataProvider的介绍 +================== + +DataProvider是PaddlePaddle负责提供数据的模块。其作用是将数据传入内存或显存,让神经网络可以进行训练或预测。用户可以通过简单使用Python接口 `PyDataProvider2 `_ ,来自定义传数据的过程。如果有更复杂的使用,或者需要更高的效率,用户也可以在C++端自定义一个 ``DataProvider`` 。 + +PaddlePaddle需要用户在网络配置(trainer_config.py)中定义使用哪种DataProvider,并且在DataProvider中实现如何访问训练文件列表(train.list)或测试文件列表(test.list)。 + +- train.list和test.list存放在本地(推荐直接存放到训练目录,以相对路径引用)。一般情况下,两者均为纯文本文件,其中每一行对应一个数据文件地址: + + - 如果数据文件存于本地磁盘,这个地址则为它的绝对路径或相对路径(相对于PaddlePaddle程序运行时的路径)。 + - 地址也可以为hdfs文件路径,或者数据库连接路径等。 + - 由于这个地址会被DataProvider使用,因此,如何解析该地址也是用户自定义DataProvider时需要考虑的地方。 +- 如果没有设置test.list,或设置为None,那么在训练过程中不会执行测试操作;否则,会根据命令行参数指定的测试方式,在训练过程中进行测试,从而防止过拟合。 diff --git a/doc_cn/ui/data_provider/index.rst b/doc_cn/ui/data_provider/index.rst deleted file mode 100644 index ec8f8e5dc5b29e3504d0087e844c1f14436919d9..0000000000000000000000000000000000000000 --- a/doc_cn/ui/data_provider/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -PaddlePaddle的数据提供(DataProvider)介绍 -======================================== - -数据提供(DataProvider)是PaddlePaddle负责提供数据的模块。其作用是将训练数据传入内存或者显存,让神经网络可以进行训练。简单的使用,用户可以使用Python的 :code:`PyDataProvider` 来自定义传数据的过程。如果有更复杂的使用,或者需要更高的效率,用户也可以在C++端自定义一个 :code:`DataProvider` 。 - -PaddlePaddle需要用户在网络配置(trainer_config.py)中定义使用哪种DataProvider及其参数,训练文件列表(train.list)和测试文件列表(test.list)。 - -其中,train.list和test.list均为本地的两个文件(推荐直接放置到训练目录,以相对路径引用)。如果test.list不设置,或者设置为None,那么在训练过程中,不会执行测试操作。否则,会根据命令行参数指定的测试方式,在训练过程中进行测试,从而防止过拟合。 - -一般情况下,train.list和test.list为纯文本文件,一行对应一个数据文件,数据文件存放在本地磁盘中。将文件的绝对路径或相对路径(相对于PaddlePaddle程序运行时的路径)写在train.list和test.list中。当然,train.list和test.list也可以放置hdfs文件路径,或者数据库连接地址等等。 - -用户在DataProvider中需要实现如何访问其中每一个文件。DataProvider的具体用法和如何实现一个新的DataProvider,请参考下述文章: - -.. toctree:: - - pydataprovider2.rst - write_new_dataprovider.rst diff --git a/doc_cn/ui/data_provider/mnist_config.py b/doc_cn/ui/data_provider/mnist_config.py index 39becff03b08f5e75b8503aaf01e782d2b0fb3be..429338c57f8f865f0c5835d933445b65ee2ea7aa 100644 --- a/doc_cn/ui/data_provider/mnist_config.py +++ b/doc_cn/ui/data_provider/mnist_config.py @@ -5,5 +5,6 @@ define_py_data_sources2( test_list=None, module='mnist_provider', obj='process') + img = data_layer(name='pixel', size=784) label = data_layer(name='label', size=10) diff --git a/doc_cn/ui/data_provider/mnist_provider.py b/doc_cn/ui/data_provider/mnist_provider.py deleted file mode 100644 index 8b828641d55735e67ca634107d5b239150649651..0000000000000000000000000000000000000000 --- a/doc_cn/ui/data_provider/mnist_provider.py +++ /dev/null @@ -1,22 +0,0 @@ -from paddle.trainer.PyDataProvider2 import * - - -# Define a py data provider -@provider(input_types=[dense_vector(28 * 28), integer_value(10)]) -def process(settings, filename): # settings is not used currently. - f = open(filename, 'r') # open one of training file - - for line in f: # read each line - label, pixel = line.split(';') - - # get features and label - pixels_str = pixel.split(' ') - - pixels_float = [] - for each_pixel_str in pixels_str: - pixels_float.append(float(each_pixel_str)) - - # give data to paddle. - yield pixels_float, int(label) - - f.close() # close file diff --git a/doc_cn/ui/data_provider/pydataprovider2.rst b/doc_cn/ui/data_provider/pydataprovider2.rst index 80b40084d8f5037a76df0b3e01ed5742d8476bd0..dce373118c5ae01c7ecf9afc15e1d9af9bf4ebe4 100644 --- a/doc_cn/ui/data_provider/pydataprovider2.rst +++ b/doc_cn/ui/data_provider/pydataprovider2.rst @@ -1,257 +1,227 @@ -PyDataProvider2的使用 -===================== - -PyDataProvider是PaddlePaddle使用Python提供数据的推荐接口。使用该接口用户可以只关注如何 -从文件中读取每一条数据,而不用关心数据如何传输给PaddlePaddle,数据如何存储等等。该数据 -接口使用多线程读取数据,并提供了简单的Cache功能。 - - -简单的使用场景 --------------- - -这里以MNIST手写识别为例,来说明简单的PyDataProvider如何使用。MNIST是一个包含有 -70,000张灰度图片的数字分类数据集。对于MNIST而言,标签是0-9的数字,而特征即为 -28*28的像素灰度值。这里我们使用简单的文本文件表示MNIST图片,样例数据如下。 - -.. literalinclude:: mnist_train.txt - -其数据使用;间隔,第一段数据为这张图片的label,第二段数据为这个图片的像素值。 -首先我们将这个数据文件(例如文件名是'mnist_train.txt')写入train.list。那么 -train.list即为 - -.. literalinclude:: train.list - -那么对应的dataprovider既为 - -.. literalinclude:: mnist_provider.py - :linenos: - -其中第一行是引入PaddlePaddle的PyDataProvider2包。主要函数是process函数。process函数 -具有两个参数,第一个参数是 settings 。这个参数在这个样例里没有使用,具 -体可以参考 settings 。第二个参数是filename,这个参数被PaddlePaddle进程传入,为 -train.list中的一行(即train.list若干数据文件路径的某一个路径)。 - -:code:`@provider` 是一个Python的 `Decorator `_ -。这行的作用是设置DataProvider的一些属性,并且标记process函数是一个DataProvider。 -如果不了解 `Decorator `_ 是什么也没关系, -只需要知道这只是一个标记属性的方法就可以了。 - -属性 `input_types`_ 是设置这个DataProvider返回什么样的数据。这里设置的是返回一个 -28*28的稠密向量和一个[0-9],10维的整数值。 `input_types`_ 具体可以设置成什么其他格 -式,请参考 `input_types`_ 的文档。 - -process函数是实现数据输入的主函数,在这个函数中,实现了打开文本文件,从文本文件中读取 -每一行,并将每行转换成和 `input_types`_ 一致的特征,并在23行返回给PaddlePaddle进程。需要注意 -的是, 返回的顺序需要和 `input_types`_ 中定义的顺序一致。 - -同时,返回数据在PaddlePaddle中是仅仅返回一条完整的训练样本,并且使用关键词 :code:`yield` 。 -在PyDataProvider中,可以为一个数据文件返回多条训练样本(就像这个样例一样),只需要在 -process函数调用多次 :code:`yield` 即可。 :code:`yield` 是Python的一个关键词,相关的概 -念是 :code:`generator` 。使用这个关键词,可以在一个函数里,多次返回变量。 - -在训练配置里,只需要使用一行代码即可以设置训练引用这个DataProvider。这个设置为 - -.. literalinclude:: mnist_config.py - -这里说明了训练数据是 'train.list',而没有测试数据。引用的DataProvider是 'mnist_provider' -这个模块中的 'process' 函数。 - -同时,根据模型配置文件中 :code:`data_layer` 的名字,用户也可以显式指定返回的数据对应关系。例如: - -.. literalinclude:: mnist_provider.dict.py - :linenos: - -如果用户不指定返回数据的对应关系,那么PaddlePaddle会粗略的根据layer的声明顺序, -来确定对应关系。这个对应关系可能不正确。所以推荐使用显式指定返回值和数据对应关系。 - -至此,简单的PyDataProvider样例就说明完毕了。对于用户来说,讲数据发送给PaddlePaddle,仅仅需要 -知道如何从 **一个文件** 里面读取 **一条** 样本。而PaddlePaddle进程帮助用户做了 - -* 将数据组合成Batch训练 -* Shuffle训练数据 -* 多线程数据读取 -* 缓存训练数据到内存(可选) -* CPU->GPU双缓存 - -是不是很简单呢? - -序列模型数据提供 ----------------- - -序列模型是指数据的某一维度是一个序列形式,即包含时间步信息。所谓时间步信息, -不一定和时间有关系,只是说明数据的顺序是重要的。例如,文本信息就是一个序列 -数据。 - -这里举例的数据是英文情感分类的数据。数据是给一段英文文本,分类成正面情绪和 -负面情绪两类(用0和1表示)。样例数据为 - -.. literalinclude:: sentimental_train.txt - -这里,DataProvider可以是 - -.. literalinclude:: sentimental_provider.py - -这个序列模型比较复杂。主要是增加了初始化机制。其中 :code:`on_init` 函数是使用 -`@provider`_ 中的 `init_hook`_ 配置参数配置给DataProvider的。这个函数会在 -DataProvider创建的时候执行。这个初始化函数具有如下参数: - -* 第一个参数是 settings 对象。 -* 其他参数均使用key word argument形式传入。有部分参数是Paddle自动生成的, - 参考 `init_hook`_ 。这里的 :code:`dictionary` 是从训练配置传入的dict对象。 - 即从单词字符串到单词id的字典。 - -传入这个变量的方式为 - -.. literalinclude:: sentimental_config.py - -这个声明基本上和mnist的样例一致。除了 - -* 在配置中读取了字典 -* 在声明DataProvider的时候传入了dictionary作为参数。 - -在 :code:`on_init` 函数中,配置了 `input_types` 。这个和在 `@provider`_ 中配置 -`input_types` 效果一致,但是在 `on_init` 中配置 `input_types` 是在运行时执行的,所以 -可以根据不同的数据配置不同的输入类型。这里的输入特征是词id的序列,所以将 :code:`seq_type` -设置成了序列(同时,也可以使用 :code:`integer_sequence` 类型来设置)。 - -同时,将字典存入了settings 对象。这个字典可以在 :code:`process` 函数中使用。 :code:`process` -函数中的 settings 和 :code:`on_init` 中的settings 是同一个对象。 - -而在 :code:`process` 函数中,基本的处理逻辑也和mnist逻辑一致。依次返回了文件中的每条数据。 - -至此,基本的PyDataProvider使用介绍完毕了。具体DataProvider还具有什么功能,请参考下节reference。 - -参考(Reference) ---------------- - -@provider -+++++++++ - -:code:`@provider` 是一个Python的 `Decorator`_ ,他可以将某一个函数标记成一个PyDataProvider。它包含的参数有: - -* `input_types`_ 是数据输入格式。具体有哪些格式,参考 `input_types`_ 。 -* should_shuffle 是个DataProvider是不是要做shuffle,如果不设置的话,训练的时候默认shuffle, - 测试的时候默认不shuffle。 -* min_pool_size 是设置DataProvider在内存中最小暂存的数据条数。这个也是PaddlePaddle所能够保证的shuffle粒度。 - 设置成-1的话,会预先读取全部数据到内存中。 -* pool_size 是设置DataProvider在内存中暂存的数据条数。设置成-1的话,即不在乎内存暂存多少条数据。 -* can_over_batch_size 表示是否允许Paddle暂存略微多余pool_size的数据。这样做可以避免很多死锁问题。 - 一般推荐设置成True -* calc_batch_size 传入的是一个函数,这个函数以一条数据为参数,返回batch_size的大小。默认情况下一条数据 - 是一个batch size,但是有时为了计算均衡性,可以将一条数据设置成多个batch size -* cache 是数据缓存的策略,参考 `cache`_ -* init_hook 是初始化时调用的函数,参考 `init_hook`_ -* check 设置成true的话,会根据input_types检查数据的合法性。 -* check_fail_continue 如果设置成true的话,即使在check中数据不合法,也会扔到这条数据,继续训练。 如果 - check是false的话,没有作用。 - -input_types -+++++++++++ - -PaddlePaddle的数据包括四种主要类型,和三种序列模式。其中,四种数据类型是 - -* dense_vector 表示稠密的浮点数向量。 -* sparse_binary_vector 表示稀疏的零一向量,即大部分值为0,有值的位置只能取1 -* sparse_float_vector 表示稀疏的向量,即大部分值为0,有值的部分可以是任何浮点数 -* integer 表示整数标签。 - -而三种序列模式为 - -* SequenceType.NO_SEQUENCE 即不是一条序列 -* SequenceType.SEQUENCE 即是一条时间序列 -* SequenceType.SUB_SEQUENCE 即是一条时间序列,且序列的每一个元素还是一个时间序列。 - -不同的数据类型和序列模式返回的格式不同,列表如下 - -+----------------------+---------------------+-----------------------------------+------------------------------------------------+ -| | NO_SEQUENCE | SEQUENCE | SUB_SEQUENCE | -+======================+=====================+===================================+================================================+ -| dense_vector | [f, f, ...] | [[f, ...], [f, ...], ...] | [[[f, ...], ...], [[f, ...], ...],...] | -+----------------------+---------------------+-----------------------------------+------------------------------------------------+ -| sparse_binary_vector | [i, i, ...] | [[i, ...], [i, ...], ...] | [[[i, ...], ...], [[i, ...], ...],...] | -+----------------------+---------------------+-----------------------------------+------------------------------------------------+ -| sparse_float_vector | [(i,f), (i,f), ...] | [[(i,f), ...], [(i,f), ...], ...] | [[[(i,f), ...], ...], [[(i,f), ...], ...],...] | -+----------------------+---------------------+-----------------------------------+------------------------------------------------+ -| integer_value | i | [i, i, ...] | [[i, ...], [i, ...], ...] | -+----------------------+---------------------+-----------------------------------+------------------------------------------------+ - -其中,f代表一个浮点数,i代表一个整数。 - -init_hook -+++++++++ - -init_hook可以传入一个函数。这个函数在初始化的时候会被调用。这个函数的参数是: - -* 第一个参数是 settings 对象。这个对象和process的第一个参数一致。具有的属性有 - * settings.input_types 设置输入类型。参考 `input_types`_ - * settings.logger 一个logging对象 -* 其他参数都使用key word argument传入。这些参数包括paddle定义的参数,和用户传入的参数。 - * Paddle定义的参数包括: - * is_train bool参数,表示这个DataProvider是训练用的DataProvider或者测试用的 - DataProvider - * file_list 所有文件列表。 - * 用户定义的参数使用args在训练配置中设置。 - -注意,PaddlePaddle保留添加参数的权力,所以init_hook尽量使用 :code:`**kwargs` , 来接受不使用的 -函数来保证兼容性。 - -cache -+++++ - -DataProvider提供了两种简单的Cache策略。他们是 - -* CacheType.NO_CACHE 不缓存任何数据,每次都会从python端读取数据 -* CacheType.CACHE_PASS_IN_MEM 第一个pass会从python端读取数据,剩下的pass会直接从内存里 - 读取数据。 - - -注意事项 --------- - -可能的内存泄露问题 -++++++++++++++++++ - -PaddlePaddle将train.list中的每一行,都传递给process函数,从而生成多个generator。 -即如果train.list中,有100个训练文件,即会生成100个generator。这个本身不是一个很 -严重的问题。 - -但是,如果在训练时,每一条训练数据都是一个文件,并且,训练数据非常多的情况下,就 -会生成多个generator。每个generator在没有调用的时候,是几乎不占内存的。但是,当调 -用过一次的时候,generator便会存下当前的上下文(Context)。而这个Context可能会非常 -大。并且,generator至少调用两次才会知道是否停止。所以,即使在process里面只会有一 -个yield,也需要两次随机选择到同样的generator的时候,才会释放该段内存。 - -.. code-block:: python - - def func(): - yield 0 - - f = func() # 创建generator - tmp = next(f) # 调用一次,返回0 - tmp = next(f) # 调用第二次的时候,才会Stop Iteration - -而如果按顺序调用这些generator就不会出现这个问题。 - -所以最佳实践推荐不要将每一个样本都放入train.list。而是将样本的地址放入另一个文本 -文件,train.list写入那个文本文件的地址。 或者在python generator的上下文中尽量留 -下非常少的变量引用。例如 - -.. code-block:: python - - def real_process(fn): - # ... read from fn - return result # 当函数返回的时候,python可以解除掉内部变量的引用。 - - def process(fn): - yield real_process(fn) - -这个问题是PyDataProvider读数据时候的逻辑问题,基本上不能整体修正。 - - -内存不够用的情况 -++++++++++++++++ - -PyDataProvider2会尽量使用内存。所以如果对于内存比较小的机器,推荐设置 -:code:`pool_size` 变量,而这个变量推荐大于训练的batch size,并且在内存足够 -的情况下越大越好。 - +PyDataProvider2的使用 +===================== + +PyDataProvider2是PaddlePaddle使用Python提供数据的推荐接口。该接口使用多线程读取数据,并提供了简单的Cache功能;同时可以使用户只关注如何从文件中读取每一条数据,而不用关心数据如何传输,如何存储等等。 + +.. contents:: + +MNIST的使用场景 +--------------- + +我们以MNIST手写识别为例,来说明PyDataProvider2的简单使用场景。 + +样例数据 +++++++++ + +MNIST是一个包含有70,000张灰度图片的数字分类数据集。样例数据 ``mnist_train.txt`` 如下: + +.. literalinclude:: mnist_train.txt + +其中每行数据代表一张图片,行内使用 ``;`` 分成两部分。第一部分是图片的标签,为0-9中的一个数字;第二部分是28*28的图片像素灰度值。 对应的 ``train.list`` 即为这个数据文件的名字: + +.. literalinclude:: train.list + +dataprovider的使用 +++++++++++++++++++ + +.. literalinclude:: mnist_provider.dict.py + +- 首先,引入PaddlePaddle的PyDataProvider2包。 +- 其次,定义一个Python的 `Decorator `_ `@provider`_ 。用于将下一行的数据输入函数标记成一个PyDataProvider2,同时设置它的input_types属性。 + + - `input_types`_:设置这个PyDataProvider2返回什么样的数据。本例根据网络配置中 ``data_layer`` 的名字,显式指定返回的是一个28*28维的稠密浮点数向量和一个[0-9]的10维整数标签。 + + .. literalinclude:: mnist_config.py + :lines: 9-10 + + - 注意:如果用户不显示指定返回数据的对应关系,那么PaddlePaddle会根据layer的声明顺序,来确定对应关系。但这个关系可能不正确,所以推荐使用显式指定的方式来设置input_types。 +- 最后,实现数据输入函数(如本例的 ``process`` 函数)。 + + - 该函数的功能是:打开文本文件,读取每一行,将行中的数据转换成与input_types一致的格式,然后返回给PaddlePaddle进程。注意, + + - 返回的顺序需要和input_types中定义的顺序一致。 + - 返回时,必须使用Python关键词 ``yield`` ,相关概念是 ``generator`` 。 + - 一次yield调用,返回一条完整的样本。如果想为一个数据文件返回多条样本,只需要在函数中调用多次yield即可(本例中使用for循环进行多次调用)。 + + - 该函数具有两个参数: + + - settings:在本例中没有使用,具体可以参考 `init_hook`_ 中的说明。 + - filename:为 ``train.list`` 或 ``test.list`` 中的一行,即若干数据文件路径的某一个。 + +网络配置中的调用 +++++++++++++++++ + +在网络配置里,只需要一行代码就可以调用这个PyDataProvider2,如, + +.. literalinclude:: mnist_config.py + :lines: 1-7 + +训练数据是 ``train.list`` ,没有测试数据,调用的PyDataProvider2是 ``mnist_provider`` 模块中的 ``process`` 函数。 + +小结 ++++++ + +至此,简单的PyDataProvider2样例就说明完毕了。对用户来说,仅需要知道如何从 **一个文件** 中读取 **一条样本** ,就可以将数据传送给PaddlePaddle了。而PaddlePaddle则会帮用户做以下工作: + +* 将数据组合成Batch进行训练 +* 对训练数据进行Shuffle +* 多线程的数据读取 +* 缓存训练数据到内存(可选) +* CPU->GPU双缓存 + +是不是很简单呢? + +时序模型的使用场景 +------------------ +样例数据 +++++++++ + +时序模型是指数据的某一维度是一个序列形式,即包含时间步信息。所谓时间步信息,不一定和时间有关系,只是说明数据的顺序是重要的。例如,文本信息就是一个序列数据。 + +本例采用英文情感分类的数据,即将一段英文文本数据,分类成正面情绪和负面情绪两类(用0和1表示)。样例数据 ``sentimental_train.txt`` 如下: + +.. literalinclude:: sentimental_train.txt + +dataprovider的使用 +++++++++++++++++++ + +相对MNIST而言,这个dataprovider较复杂,主要原因是增加了初始化机制 `init_hook`_。本例的 ``on_init`` 函数就是根据该机制配置的,它会在dataprovider创建的时候执行。 + +- 其中 ``input_types`` 和在 `@provider`_ 中配置的效果一致。本例中的输入特征是词ID的序列,因此使用 ``integer_value_sequence`` 类型来设置。 +- 将 ``dictionary`` 存入settings对象,在 ``process`` 函数中使用。 dictionary是从网络配置中传入的dict对象,即一个将单词字符串映射到单词ID的字典。 + +.. literalinclude:: sentimental_provider.py + +网络配置中的调用 +++++++++++++++++ + +调用这个PyDataProvider2的方法,基本上和MNIST样例一致,除了 + +* 在配置中需要读取外部字典。 +* 在声明DataProvider的时候传入dictionary作为参数。 + +.. literalinclude:: sentimental_config.py + :emphasize-lines: 12-14 + +参考(Reference) +--------------- + +@provider ++++++++++ + +``@provider`` 是一个Python的 `Decorator`_ ,可以将某一个函数标记成一个PyDataProvider2。如果不了解 `Decorator`_ 是什么也没关系,只需知道这是一个标记属性的方法就可以了。它包含的属性参数如下: + +* input_types:数据输入格式。具体的格式说明,请参考 `input_types`_ 。 +* should_shuffle:是不是要对数据做Shuffle。训练时默认shuffle,测试时默认不shuffle。 +* min_pool_size:设置内存中最小暂存的数据条数,也是PaddlePaddle所能够保证的shuffle粒度。如果为-1,则会预先读取全部数据到内存中。 +* pool_size: 设置内存中暂存的数据条数。如果为-1(默认),则不在乎内存暂存多少条数据。如果设置,则推荐大于训练时batch size的值,并且在内存足够的情况下越大越好。 +* can_over_batch_size:是否允许暂存略微多余pool_size的数据。由于这样做可以避免很多死锁问题,一般推荐设置成True。 +* calc_batch_size:可以传入一个函数,用于自定义每条数据的batch size(默认为1)。 +* cache: 数据缓存的策略,具体请参考 `cache`_ 。 +* init_hook:初始化时调用的函数,具体请参考 `init_hook`_ 。 +* check:如果为true,会根据input_types检查数据的合法性。 +* check_fail_continue:如果为true,那么当check出数据不合法时,会扔到这条数据,继续训练或预测。(对check=false的情况,没有作用) + +input_types ++++++++++++ + +PaddlePaddle的数据包括四种主要类型,和三种序列模式。 + +四种数据类型: + +* dense_vector:稠密的浮点数向量。 +* sparse_binary_vector:稀疏的01向量,即大部分值为0,但有值的地方必须为1。 +* sparse_float_vector:稀疏的向量,即大部分值为0,但有值的部分可以是任何浮点数。 +* integer:整数标签。 + +三种序列模式: + +* SequenceType.NO_SEQUENCE:不是一条序列 +* SequenceType.SEQUENCE:是一条时间序列 +* SequenceType.SUB_SEQUENCE: 是一条时间序列,且序列的每一个元素还是一个时间序列。 + +不同的数据类型和序列模式返回的格式不同,列表如下: + ++----------------------+---------------------+-----------------------------------+------------------------------------------------+ +| | NO_SEQUENCE | SEQUENCE | SUB_SEQUENCE | ++======================+=====================+===================================+================================================+ +| dense_vector | [f, f, ...] | [[f, ...], [f, ...], ...] | [[[f, ...], ...], [[f, ...], ...],...] | ++----------------------+---------------------+-----------------------------------+------------------------------------------------+ +| sparse_binary_vector | [i, i, ...] | [[i, ...], [i, ...], ...] | [[[i, ...], ...], [[i, ...], ...],...] | ++----------------------+---------------------+-----------------------------------+------------------------------------------------+ +| sparse_float_vector | [(i,f), (i,f), ...] | [[(i,f), ...], [(i,f), ...], ...] | [[[(i,f), ...], ...], [[(i,f), ...], ...],...] | ++----------------------+---------------------+-----------------------------------+------------------------------------------------+ +| integer_value | i | [i, i, ...] | [[i, ...], [i, ...], ...] | ++----------------------+---------------------+-----------------------------------+------------------------------------------------+ + +其中,f代表一个浮点数,i代表一个整数。 + +注意:对sparse_binary_vector和sparse_float_vector,PaddlePaddle存的是有值位置的索引。例如, + +- 对一个5维非序列的稀疏01向量 ``[0, 1, 1, 0, 0]`` ,类型是sparse_binary_vector,返回的是 ``[1, 2]`` 。 +- 对一个5维非序列的稀疏浮点向量 ``[0, 0.5, 0.7, 0, 0]`` ,类型是sparse_float_vector,返回的是 ``[(1, 0.5), (2, 0.7)]`` 。 + +init_hook ++++++++++ + +init_hook可以传入一个函数。该函数在初始化的时候会被调用,其参数如下: + +* 第一个参数是settings对象,它和数据传入函数的第一个参数(如本例中 ``process`` 函数的 ``settings`` 参数)必须一致。该对象具有以下两个属性: + * settings.input_types:数据输入格式,具体请参考 `input_types`_ 。 + * settings.logger:一个logging对象。 +* 其他参数使用 ``kwargs`` (key word arguments)传入,包括以下两种: + * PaddlePaddle定义的参数: 1)is_train:bool型参数,表示用于训练或预测;2)file_list:所有文件列表。 + * 用户定义的参数:使用args在网络配置中设置。 + +注意:PaddlePaddle保留添加参数的权力,因此init_hook尽量使用 ``**kwargs`` 来接受不使用的函数以保证兼容性。 + +cache ++++++ + +PyDataProvider2提供了两种简单的Cache策略: + +* CacheType.NO_CACHE:不缓存任何数据,每次都会从python端读取数据 +* CacheType.CACHE_PASS_IN_MEM:第一个pass会从python端读取数据,剩下的pass会直接从内存里 + 读取数据。 + + +注意事项 +-------- + +可能的内存泄露问题 +++++++++++++++++++ + +PaddlePaddle将train.list中的每一行都传递给process函数,从而生成多个generator。当训练数据非常多时,就会生成非常多的generator。 + +虽然每个generator在没有调用的时候,是几乎不占内存的;但当调用过一次后,generator便会存下当前的上下文(Context),而这个Context可能会非常大。并且,generator至少需要调用两次才会知道是否停止。所以,即使process函数里面只有一个yield,也需要两次随机选择到相同generator的时候,才会释放该段内存。 + +.. code-block:: python + + def func(): + yield 0 + + f = func() # 创建generator + tmp = next(f) # 调用一次,返回0 + tmp = next(f) # 调用第二次的时候,才会Stop Iteration + +由于顺序调用这些generator不会出现上述问题,因此有两种解决方案: + +1. **最佳推荐**:将样本的地址放入另一个文本文件,train.list写入那个文本文件的地址。即不要将每一个样本都放入train.list。 +2. 在generator的上下文中尽量留下非常少的变量引用,例如 + +.. code-block:: python + + def real_process(fn): + # ... read from fn + return result # 当函数返回的时候,python可以解除掉内部变量的引用。 + + def process(fn): + yield real_process(fn) + +注意:这个问题是PyDataProvider读数据时候的逻辑问题,很难整体修正。 + +内存不够用的情况 +++++++++++++++++ + +PyDataProvider2会尽可能多的使用内存。因此,对于内存较小的机器,推荐使用 ``pool_size`` 变量来设置内存中暂存的数据条。具体请参考 `@provider`_ 中的说明。 + diff --git a/doc_cn/ui/data_provider/sentimental_provider.py b/doc_cn/ui/data_provider/sentimental_provider.py index 0fb0bb88e95a230f01f18b78ebb37b659c3768f1..14bd0e05a921dbfd5212d8483524d3af3e4ae98f 100644 --- a/doc_cn/ui/data_provider/sentimental_provider.py +++ b/doc_cn/ui/data_provider/sentimental_provider.py @@ -8,19 +8,16 @@ def on_init(settings, dictionary, **kwargs): # set input types in runtime. It will do the same thing as # @provider(input_types) will do, but it is set dynamically during runtime. - settings.input_types = [ + settings.input_types = { # The text is a sequence of integer values, and each value is a word id. # The whole sequence is the sentences that we want to predict its # sentimental. - integer_value( - len(dictionary), seq_type=SequenceType), # text input + 'data': integer_value_sequence(len(dictionary)), # text input + 'label': integer_value(2) # label positive/negative + } - # label positive/negative - integer_value(2) - ] - - # save dictionary as settings.dictionary. It will be used in process - # method. + # save dictionary as settings.dictionary. + # It will be used in process method. settings.dictionary = dictionary diff --git a/doc_cn/ui/data_provider/write_new_dataprovider.rst b/doc_cn/ui/data_provider/write_new_dataprovider.rst deleted file mode 100644 index a2495fe66371eb0cf678434f43feb6f91d93f3cf..0000000000000000000000000000000000000000 --- a/doc_cn/ui/data_provider/write_new_dataprovider.rst +++ /dev/null @@ -1,4 +0,0 @@ -自定义一个DataProvider -==================== - -TBD \ No newline at end of file diff --git a/doc_cn/ui/index.rst b/doc_cn/ui/index.rst index d871ad805ff7cd37fb83f24024003e54bce77f42..ff36c9adb690f4126cf6ee332a9f0b09648270bd 100644 --- a/doc_cn/ui/index.rst +++ b/doc_cn/ui/index.rst @@ -8,8 +8,8 @@ .. toctree:: :maxdepth: 1 - data_provider/index.rst - + data_provider/dataprovider.rst + data_provider/pydataprovider2.rst 命令及命令行参数 ================ @@ -23,9 +23,8 @@ * `参数分类 <../../doc/ui/cmd_argument/argument_outline.html>`_ * `参数描述 <../../doc/ui/cmd_argument/detail_introduction.html>`_ - 预测 -==== +======= .. toctree:: :maxdepth: 1