diff --git a/docs/api/paddle/jit/save_cn.rst b/docs/api/paddle/jit/save_cn.rst index 51bbeddde63..547a8fd7d78 100644 --- a/docs/api/paddle/jit/save_cn.rst +++ b/docs/api/paddle/jit/save_cn.rst @@ -34,4 +34,96 @@ save 代码示例 ::::::::: -COPY-FROM: paddle.jit.save + ```python + # example 1: save layer + import numpy as np + import paddle + import paddle.nn as nn + import paddle.optimizer as opt + + BATCH_SIZE = 16 + BATCH_NUM = 4 + EPOCH_NUM = 4 + + IMAGE_SIZE = 784 + CLASS_NUM = 10 + + # define a random dataset + class RandomDataset(paddle.io.Dataset): + def __init__(self, num_samples): + self.num_samples = num_samples + + def __getitem__(self, idx): + image = np.random.random([IMAGE_SIZE]).astype('float32') + label = np.random.randint(0, CLASS_NUM, (1, )).astype('int64') + return image, label + + def __len__(self): + return self.num_samples + + class LinearNet(nn.Layer): + def __init__(self): + super(LinearNet, self).__init__() + self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM) + + @paddle.jit.to_static + def forward(self, x): + return self._linear(x) + + def train(layer, loader, loss_fn, opt): + for epoch_id in range(EPOCH_NUM): + for batch_id, (images, labels) in enumerate(loader()): + out = layer(images) + loss = loss_fn(out, labels) + loss.backward() + opt.step() + opt.clear_grad() + print("Epoch {} batch {}: loss = {}".format( + epoch_id, batch_id, np.mean(loss.numpy()))) + + # 1. train & save model. + + # create network + layer = LinearNet() + loss_fn = nn.CrossEntropyLoss() + adam = opt.Adam(learning_rate=0.001, parameters=layer.parameters()) + + # create data loader + dataset = RandomDataset(BATCH_NUM * BATCH_SIZE) + loader = paddle.io.DataLoader(dataset, + batch_size=BATCH_SIZE, + shuffle=True, + drop_last=True, + num_workers=2) + + # train + train(layer, loader, loss_fn, adam) + + # save + path = "example_model/linear" + paddle.jit.save(layer, path) + ``` + + ```python + # example 2: save function + import paddle + from paddle.static import InputSpec + + + def save_function(): + @paddle.jit.to_static + def fun(inputs): + return paddle.tanh(inputs) + + path = 'test_jit_save_load_function/func' + inps = paddle.rand([3, 6]) + origin = fun(inps) + + paddle.jit.save(fun, path) + load_func = paddle.jit.load(path) + + load_result = load_func(inps) + print((load_result - origin).abs().max() < 1e-10) + + save_function() + ``` diff --git a/docs/guides/jit/basic_usage_cn.md b/docs/guides/jit/basic_usage_cn.md index 7d0518c1a9a..eb80bddbe2c 100644 --- a/docs/guides/jit/basic_usage_cn.md +++ b/docs/guides/jit/basic_usage_cn.md @@ -882,6 +882,11 @@ class SimpleNet(Layer): 若被装饰函数的参数列表除了 Tensor 类型,还包含其他如 Int、 String 等非 Tensor 类型时,推荐在函数中使用 kwargs 形式定义非 Tensor 参数,如下述样例中的 ``use_act`` 参数。 ```python +import paddle +from paddle.jit import to_static +from paddle.nn import Layer +from paddle.static import InputSpec + class SimpleNet(Layer): def __init__(self, ): super(SimpleNet, self).__init__() @@ -896,11 +901,11 @@ class SimpleNet(Layer): net = SimpleNet() # 方式一:save inference model with use_act=False -net = to_static(input_spec=[InputSpec(shape=[None, 10], name='x')]) +net = to_static(net, input_spec=[InputSpec(shape=[None, 10], name='x')]) paddle.jit.save(net, path='./simple_net') # 方式二:save inference model with use_act=True -net = to_static(input_spec=[InputSpec(shape=[None, 10], name='x'), True]) +net = to_static(net, input_spec=[InputSpec(shape=[None, 10], name='x'), True]) paddle.jit.save(net, path='./simple_net') ``` diff --git a/docs/guides/jit/case_analysis_cn.md b/docs/guides/jit/case_analysis_cn.md index 81595f75b7b..42bc6b232bd 100644 --- a/docs/guides/jit/case_analysis_cn.md +++ b/docs/guides/jit/case_analysis_cn.md @@ -96,10 +96,10 @@ def forward(self, x): out = self.linear(x) # [bs, 3] - # 以下将 tensor 转为了 numpy 进行一系列操作 + # 以下将 tensor 转为了 numpy array 进行一系列操作 x_data = x.numpy().astype('float32') # [bs, 10] - weight = np.random.randn([10,3]) - mask = paddle.to_tensor(x_data * weight) # 此处又转回了 Tensor + weight = np.random.randn(10, 3) + mask = paddle.to_tensor(x_data @ weight) # 此处又转回了 Tensor out = out * mask return out @@ -113,7 +113,7 @@ def forward(self, x): out = self.linear(x) # [bs, 3] weight = paddle.randn([10,3], 'float32') - mask = x * weight + mask = paddle.matmul(x, weight) out = out * mask return out @@ -129,7 +129,7 @@ def forward(self, x): ## 四、to_tensor() 的使用 -``paddle.to_tensor()`` 接口是动态图模型代码中使用比较频繁的一个接口。 ``to_tensor`` 功能强大,将可以将一个 ``scalar`` , ``list`` ,``tuple`` , ``numpy.ndarray`` 转为 ``paddle.Tensor`` 类型。 +``paddle.to_tensor()`` 接口是动态图模型代码中使用比较频繁的一个接口。 ``to_tensor`` 功能强大,可以将一个 ``scalar`` , ``list`` ,``tuple`` , ``numpy.ndarray`` 转为 ``paddle.Tensor`` 类型。 此接口是动态图独有的接口,在动转静时,会转换为 ``assign`` 接口: @@ -193,14 +193,14 @@ class SimpleNet(paddle.nn.Layer): 动态图模型常常包含很多嵌套的子网络,建议各个自定义的子网络 ``sublayer`` **无论是否包含了参数,都继承 ``nn.Layer`` .** -从 **Parameters 和 Buffers** 章节可知,有些 ``paddle.to_tensor`` 接口转来的 ``Tensor`` 也可能参与预测逻辑分支的计算,即模型导出时,也需要作为参数序列化保存到 ``.pdiparams`` 文件中。 +从 [**Parameters 和 Buffers**](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/jit/principle_cn.html#buffer) 章节可知,有些 ``paddle.to_tensor`` 接口转来的 ``Tensor`` 也可能参与预测逻辑分支的计算,即模型导出时,也需要作为参数序列化保存到 ``.pdiparams`` 文件中。 > **原因**: 若某个 sublayer 包含了 buffer Variables,但却没有继承 ``nn.Layer`` ,则可能导致保存的 ``.pdiparams`` 文件缺失部分重要参数。 **举个例子:** ```python -class SimpleNet(object): # <---- 继承 Object +class SimpleNet(object): # <---- 继承 object def __init__(self, mask): super(SimpleNet, self).__init__() self.linear = paddle.nn.Linear(10, 3) # <---- Linear 参数永远都不会被更新 @@ -215,7 +215,7 @@ class SimpleNet(object): # <---- 继承 Object 同时,所有继承 ``nn.Layer`` 的 ``sublayer`` 都建议: -+ 重写 ``forward`` 函数,尽量避免重写 ``__call__``` 函数 ++ 重写 ``forward`` 函数,尽量避免重写 ``__call__`` 函数 > ``__call__`` 函数通常会包含框架层面的一些通用的处理逻辑,比如 ``pre_hook`` 和 ``post_hook`` 。重写此函数可能会覆盖框架层面的逻辑。 + 尽量将 ``forward`` 函数作为 sublayers 的调用入口 @@ -265,19 +265,23 @@ def forward(self, x): model = SimpleNet() model.eval() # <---- 一键切换分支,则只会导出 eval 相关的预测分支 -jit.save(mode, model_path) +paddle.jit.save(model, model_path) ``` 推荐使用 ``self.training`` 或其他非 Tensor 类型的 bool 值进行区分。 -此 flag 继承自 ``nn.Layer`` ,因此可通过 ``model.train()`` 和 ``model.eval()`` 来全局切换所有 sublayers 的分支状态。 +此 flag 继承自 ``paddle.nn.Layer`` ,因此可通过 ``model.train()`` 和 ``model.eval()`` 来全局切换所有 sublayers 的分支状态。 ## 七、非 forward 函数导出 -`@to_static` 与 `jit.save` 接口搭配也支持导出非 forward 的其他函数,具体使用方式如下: +`@paddle.jit.to_static` 与 `paddle.jit.save` 接口搭配也支持导出非 forward 的其他函数,具体使用方式如下: ```python +import paddle +from paddle.jit import to_static +from paddle.static import InputSpec + class SimpleNet(paddle.nn.Layer): def __init__(self): super(SimpleNet, self).__init__() @@ -300,24 +304,24 @@ net = SimpleNet() net.eval() # step 2: 定义 InputSpec 信息 (同上) -x_spec = InputSpec(shape=[None, 3], dtype='float32', name='x') +x_spec = InputSpec(shape=[None, 10], dtype='float32', name='x') # step 3: @to_static 装饰 static_func = to_static(net.another_func, input_spec=[x_spec]) -# step 4: 调用 jit.save 接口 -net = paddle.jit.save(static_func, path='another_func') +# step 4: 调用 paddle.jit.save 接口 +paddle.jit.save(static_func, path='another_func') ``` 使用上的区别主要在于: + **`@to_static` 装饰**:导出其他函数时需要显式地用 `@to_static` 装饰,以告知动静转换模块将其识别、并转为静态图 Program; -+ **`save`接口参数**:调用`jit.save`接口时,需将上述被`@to_static` 装饰后的函数作为**参数**; ++ **`save`接口参数**:调用`paddle.jit.save`接口时,需将上述被`@to_static` 装饰后的函数作为**参数**; 执行上述代码样例后,在当前目录下会生成三个文件: ``` another_func.pdiparams // 存放模型中所有的权重数据 -another_func.pdimodel // 存放模型的网络结构 +another_func.pdmodel // 存放模型的网络结构 another_func.pdiparams.info // 存放额外的其他信息 ``` @@ -326,7 +330,7 @@ another_func.pdiparams.info // 存放额外的其他信息 ## 八、再谈控制流 -前面[【控制流转写】(./basic_usage_cn.html#sikongzhiliuzhuanxie)]提到,不论控制流 ``if/for/while`` 语句是否需要转为静态图中的 ``cond_op/while_op`` ,都会先进行代码规范化,如 ``IfElse`` 语句会规范为如下范式: +前面[【控制流转写】](./principle_cn.html#kongzhiliuzhuanxie)提到,不论控制流 ``if/for/while`` 语句是否需要转为静态图中的 ``cond_op/while_op`` ,都会先进行代码规范化,如 ``IfElse`` 语句会规范为如下范式: ```python def true_fn_0(out): @@ -348,7 +352,7 @@ out = convert_ifelse(paddle.mean(x) > 5.0, true_fn_0, false_fn_0, (x,), (x,), (o 当控制流中,出现了 ``list.append`` 类似语法时,情况会有一点点特殊。 -Paddle 框架中的 ``cond_op`` 和 [``while_loop``](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/while_loop_cn.html#cn-api-fluid-layers-while-loop) 对输入和返回类型有一个要求: +Paddle 框架中的 [``cond_op``](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/static/nn/cond_cn.html#cond) 和 [``while_loop``](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/static/nn/while_loop_cn.html) 对输入和返回类型有一个要求: > 输入或者返回类型必须是:LoDTensor 或者 LoDTensorArray

> 即:不支持其他非 LoDTensor 类型 diff --git a/docs/guides/jit/grammar_list_cn.md b/docs/guides/jit/grammar_list_cn.md index 6963c2d2928..30ff5532d44 100644 --- a/docs/guides/jit/grammar_list_cn.md +++ b/docs/guides/jit/grammar_list_cn.md @@ -135,9 +135,9 @@ def break_usage(x): break # <------- jump out of while loop when break ; return tensor_idx ``` -当时输入 x = Tensor([1.0, 2.0 ,3.0]) 时,输出的 tensor_idx 是 Tensor([1])。 +当输入 x = Tensor([1.0, 2.0 ,3.0]) 时,输出的 tensor_idx 是 Tensor([1])。 -> 注:这里虽然 idx 是-1,但是返回值还是 Tensor。因为`tensor_idx` 在 while loop 中转化为了`Tensor`。 +> 注:这里虽然 tensor_idx 初始值是-1,但是返回值还是 Tensor。因为 `idx` 在 while loop 中转化为了只含一个索引值的 `Tensor`。 ### 3.5 与、或、非 @@ -167,9 +167,11 @@ def and(x, y): **主要逻辑:** -动态图中可以直接用 Python 的类型转化语法来转化 Tensor 类型。如若 x 是 Tensor 时,float(x)可以将 x 的类型转化为 float。 +动态图中可以直接用 Python 的类型转化语法来转化 Tensor 类型。如若 x 是 Tensor 时(只支持含有一个元素的 Tensor ), -动转静在运行时判断 x 是否是 Tensor,若是,则在动转静时使用静态图`cast`接口转化相应的 Tensor 类型。 +float(x) 可以将 x 的类型转化为 float。动转静在运行时判断 x 是否是 Tensor,若是,则在动转静时使用静态图 `cast` 接口 + +转化相应的 Tensor 类型。 **使用样例**: @@ -239,7 +241,7 @@ def recur_call(x): def list_example(x, y): a = [ x ] # < ------ 支持直接创建 a.append(x) # < ------ 支持调用 append、pop 操作 - a[1] = y # < ------ 支持下标修改 append + a[1] = y # < ------ 支持下标修改 return a[0] # < ------ 支持下标获取 ```