From 244866dc51a6b1b3db37e46fec0433a1fb6e215f Mon Sep 17 00:00:00 2001 From: betterpig Date: Fri, 11 Mar 2022 07:56:38 +0000 Subject: [PATCH 01/17] modify tensor_introduction doc --- .../basic_concept/tensor_introduction_cn.md | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 84517dee3ba..0086d920ac4 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -3,15 +3,29 @@ ## 概述:Tensor 的概念 -飞桨(PaddlePaddle,以下简称Paddle)和其他深度学习框架一样,使用**Tensor**来表示数据,在神经网络中传递的数据均为**Tensor**。 +飞桨(PaddlePaddle,以下简称Paddle)使用**Tensor**来表示数据,在神经网络中传递的数据均为**Tensor**。可以将**Tensor**理解为多维数组,其可以具有任意多的维度。不同**Tensor**可以有不同的**数据类型** (dtype) 和**形状** (shape),同一**Tensor**中所有元素的数据类型均相同。如果你对 [Numpy](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 熟悉,**Tensor**是类似于 **Numpy 数组(array)** 的概念。 -**Tensor**可以将其理解为多维数组,其可以具有任意多的维度,不同**Tensor**可以有不同的**数据类型** (dtype) 和**形状** (shape)。 +大部分Paddle API要求输入为**Tensor**类型,其输出亦是**Tensor**。原因是Paddle基于**Tensor**,从数据、网络、硬件等各方面,对神经网络模型的训练和预测速度和效果进行了优化,使用**Tensor**会获得更好的使用体验和性能。因此,推荐用户在程序中优先使用**Tensor**完成各种数据处理和组网操作。 -同一**Tensor**的中所有元素的数据类型均相同。如果你对 [Numpy](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 熟悉,**Tensor**是类似于 **Numpy 数组(array)** 的概念。 +**Tensor**作为paddle中最重要的数据结构,具有完善的API用以对**Tensor**进行创建、访问、修改、计算等一系列操作,从而满足深度学习的需要。如[使用卷积神经网络进行图像分类](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/practices/cv/convnet_image_classification.html)教程中,使用to_tensor API将标签数据转换为**Tensor**作为模型的输入;使用unsqueeze改变**Tensor**的形状用以batch对齐,使用paddle.numpy将**Tensor**转换为numpy数组,用来调用matplotlib绘制图像。接下来,从**Tensor**的创建、属性、操作、转换和广播五个方面,介绍**Tensor**相关的概念和API。 ## 一、Tensor的创建 -Paddle提供了多种方式创建**Tensor**,如:指定数据列表创建、指定形状创建、指定区间创建等。 +通常的CV、NLP任务,用户可能对数据准备操作关注不多,加上Tensor创建操作被封装,因此对于Tensor的创建感知不强。例如,在[使用卷积神经网络进行图像分类](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/practices/cv/convnet_image_classification.html)教程中,将Cifar10数据集中的图片数据转换为Tensor数据的ToTensor接口,就是在内部调用了paddle.to_tensor API。 + +```python +import paddle +from paddle.vision.transforms import ToTensor + +transform = ToTensor() +cifar10_train = paddle.vision.datasets.Cifar10(mode='train', transform=transform) +train_loader = paddle.io.DataLoader(cifar10_train, + shuffle=True, + batch_size=batch_size) +``` + + +若需要基于给定数据或图片手动创建**Tensor**,Paddle也提供了多种方式,如:指定数据列表创建、指定形状创建、指定区间创建、指定图像、NLP真实场景数据创建等。 ### 1.1 指定数据创建 通过给定Python列表数据,可以创建任意维度(也称为轴)的Tensor,举例如下: @@ -111,6 +125,27 @@ Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True, [1, 2, 3, 4]) ``` +### 1.4 指定图像、NLP真实场景数据创建Tensor +对于图片,需要用到视觉模块的ToTensor API。对于NLP文本数据,在将文本数据解码转换为数字后,通过to_tensor创建Tensor即可。下面示例为使用ToTensor,将随机生成的图片转换为Tensor。 +```python +import numpy as np +from PIL import Image +import paddle.vision.transforms as T +import paddle.vision.transforms.functional as F + +fake_img = Image.fromarray((np.random.rand(224, 224, 3) * 255.).astype(np.uint8)) +transform = T.ToTensor() +tensor = transform(fake_img) +print(tensor) +``` + +```text +Tensor(shape=[3, 224, 224], dtype=float32, place=Place(gpu:0), stop_gradient=True, + [[[0.78039223, 0.72941178, 0.34117648, ..., 0.76470596, 0.57647061, 0.94901967], + ..., + [0.49803925, 0.72941178, 0.80392164, ..., 0.08627451, 0.97647065, 0.43137258]]]) +``` + ## 二、Tensor的属性 ### 2.1 Tensor的形状 @@ -145,7 +180,7 @@ Elements number along axis 0 of Tensor: 2 Elements number along the last axis of Tensor: 5 ``` -重新设置**Tensor**的shape在实际编程中具有重要意义,Paddle提供了reshape接口来改变Tensor的shape: +重新设置**Tensor**的shape在实际编程中具有重要意义,例如在[使用卷积神经网络进行图像分类](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/practices/cv/convnet_image_classification.html)教程中,通过paddle.unsqueeze API,使y_data增加了batch维,目的是使y_data与其他数据形状对齐,满足后续Paddle API的对于输入形状的要求。相比unsqueeze,Paddle提供的reshape接口,能够直接指定Tensor的目标shape: ```python ndim_3_Tensor = paddle.to_tensor([[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]], @@ -183,6 +218,7 @@ print("Tensor flattened to Vector:", paddle.reshape(ndim_3_Tensor, [-1]).numpy() ```text Tensor flattened to Vector: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30] ``` + ### 2.2 Tensor的数据类型 **Tensor**的数据类型,可以通过 Tensor.dtype 来查看,dtype支持:'bool', 'float16', 'float32', 'float64', 'uint8', 'int8', 'int16', 'int32', 'int64'。 @@ -234,7 +270,9 @@ Tensor after cast to int64: paddle.int64 ### 2.3 Tensor的设备位置 -初始化**Tensor**时可以通过**place**来指定其分配的设备位置,可支持的设备位置有三种:CPU/GPU/固定内存,其中固定内存也称为不可分页内存或锁页内存,其与GPU之间具有更高的读写效率,并且支持异步传输,这对网络整体性能会有进一步提升,但其缺点是分配空间过多时可能会降低主机系统的性能,因为其减少了用于存储虚拟内存数据的可分页内存。当未指定place时,Tensor默认设备位置和安装的Paddle版本一致,如安装了GPU版本的Paddle,则设备位置默认为GPU。 +初始化**Tensor**时可以通过**place**来指定其分配的设备位置,可支持的设备位置有三种:CPU/GPU/固定内存,其中固定内存也称为不可分页内存或锁页内存,其与GPU之间具有更高的读写效率,并且支持异步传输,这对网络整体性能会有进一步提升,但其缺点是分配空间过多时可能会降低主机系统的性能,因为其减少了用于存储虚拟内存数据的可分页内存。 + +> 当未指定place时,Tensor默认设备位置和安装的Paddle版本一致,如安装了GPU版本的Paddle,则设备位置默认为GPU。就是说,在创建Tensor时如果未指定`place`参数,则Tensor的`place`为paddle.CUDAPlace。 以下示例分别创建了CPU、GPU和固定内存上的Tensor,并通过 `Tensor.place` 查看Tensor所在的设备位置: @@ -500,6 +538,13 @@ Tensor(shape=[2], dtype=float64, place=Place(gpu:0), stop_gradient=True, ``` 创建的 **Tensor** 与原 **Numpy array** 具有相同的形状与数据类型。 +> **注意** +> +> 虽然Paddle的Tensor可以与Numpy的数组方便的互相转换,但在实际中两者频繁转换会性能消耗。目前飞桨的Tensor已经基本覆盖Numpy的所有操作并有所加强,所以推荐用户在程序中优先使用飞桨的Tensor完成各种数据处理和组网操作。具体分为如下两种场景: + +> 场景一:在组网程序中,对网络中向量的处理,务必使用Tensor,而不建议转成Numpy的数组。如果在组网过程中转成Numpy的数组,并使用Numpy的函数会拖慢整体性能。 + +> 场景二:在数据处理和模型后处理等场景,建议优先使用Tensor,主要是飞桨为AI硬件做了大量的适配和性能优化工作,部分情况下会获得更好的使用体验和性能。 ## 五、Tensor 的广播操作 Paddle和其他框架一样,提供的一些API支持广播(broadcasting)机制,允许在一些运算时使用不同形状的Tensor。 通常来讲,如果有一个形状较小和一个形状较大的Tensor,会希望多次使用较小的Tensor来对较大的Tensor执行一些操作,看起来像是较小形状的Tensor的形状首先被扩展到和较大形状的Tensor一致,然后做运算。值得注意的是,这期间并没有对较小形状Tensor的数据拷贝操作。 From 8ce579c1276759afb119cc27285caebc843d637a Mon Sep 17 00:00:00 2001 From: betterpig Date: Thu, 31 Mar 2022 07:17:47 +0000 Subject: [PATCH 02/17] modify according to nielang's advise, change picture quote method --- .../basic_concept/images/Tensor_broadcast.png | Bin 0 -> 30432 bytes .../basic_concept/tensor_introduction_cn.md | 473 ++++++++++++------ 2 files changed, 310 insertions(+), 163 deletions(-) create mode 100644 docs/guides/01_paddle2.0_introduction/basic_concept/images/Tensor_broadcast.png diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/images/Tensor_broadcast.png b/docs/guides/01_paddle2.0_introduction/basic_concept/images/Tensor_broadcast.png new file mode 100644 index 0000000000000000000000000000000000000000..002e6faac9fecb38e66a0d5672c62f0726cdd6ca GIT binary patch literal 30432 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 literal 0 HcmV?d00001 diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 0086d920ac4..19fb91da68b 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -1,37 +1,70 @@ # Tensor介绍 -## 概述:Tensor 的概念 +## 一、Tensor 的概念介绍 + +飞桨使用张量([Tensor](../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。不同之处是 Tensor 不仅可以运行在 CPU 上,还支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;并且飞桨基于 Tensor,从数据处理、组网操作、硬件适配等各方面进行了优化,在神经网络训练、推理等任务中可获得更好的效果和使用体验。因此推荐优先使用 Tensor 完成数据处理和组网操作。 +在飞桨框架中,神经网络的输入、输出数据,以及网络中的参数均采用 Tensor 数据结构,示例如下: +```python +def train(model): + model.train() + epochs = 2 + optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters()) + # 模型训练的两层循环 + for epoch in range(epochs): + for batch_id, data in enumerate(train_loader()): + x_data = data[0] + y_data = data[1] + print("x_data: ", x_data[0][0][0][0]) # 打印神经网络的输入:批数据中的第一个数据的第一个元素 + predicts = model(x_data) + print("predicts: ", predicts[0]) # 打印神经网络的输出:批数据中的第一个数据的第一个元素 + print("weight: ", model.linear1.weight[0][0]) # 打印神经网络的权重:linear1层的weight中的第一个元素 + loss = F.cross_entropy(predicts, y_data) + acc = paddle.metric.accuracy(predicts, y_data) + loss.backward() + optim.step() + optim.clear_grad() + break + break +model = LeNet() +train(model) +``` -飞桨(PaddlePaddle,以下简称Paddle)使用**Tensor**来表示数据,在神经网络中传递的数据均为**Tensor**。可以将**Tensor**理解为多维数组,其可以具有任意多的维度。不同**Tensor**可以有不同的**数据类型** (dtype) 和**形状** (shape),同一**Tensor**中所有元素的数据类型均相同。如果你对 [Numpy](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 熟悉,**Tensor**是类似于 **Numpy 数组(array)** 的概念。 +```text +x_data: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=True, + [-1.]) +predicts: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=False, + [-0.72636688]) +weight: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=False, + [0.02227839]) +``` +以上示例代码来源 [使用LeNet在MNIST数据集实现图像分类](../practices/cv/image_classification) 任务 5.1 小节(篇幅原因仅截取部分),分别打印了神经网络的输入、输出数据和网络中的参数,可以看到均采用了 Tensor 数据结构。 -大部分Paddle API要求输入为**Tensor**类型,其输出亦是**Tensor**。原因是Paddle基于**Tensor**,从数据、网络、硬件等各方面,对神经网络模型的训练和预测速度和效果进行了优化,使用**Tensor**会获得更好的使用体验和性能。因此,推荐用户在程序中优先使用**Tensor**完成各种数据处理和组网操作。 +## 二、Tensor 的创建 -**Tensor**作为paddle中最重要的数据结构,具有完善的API用以对**Tensor**进行创建、访问、修改、计算等一系列操作,从而满足深度学习的需要。如[使用卷积神经网络进行图像分类](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/practices/cv/convnet_image_classification.html)教程中,使用to_tensor API将标签数据转换为**Tensor**作为模型的输入;使用unsqueeze改变**Tensor**的形状用以batch对齐,使用paddle.numpy将**Tensor**转换为numpy数组,用来调用matplotlib绘制图像。接下来,从**Tensor**的创建、属性、操作、转换和广播五个方面,介绍**Tensor**相关的概念和API。 +飞桨可基于给定数据手动创建 Tensor,并提供了多种方式,如: -## 一、Tensor的创建 +[1.1 指定数据创建](#newtensor1) +[1.2 指定形状创建](#newtensor2) +[1.3 指定区间创建](#newtensor3) +[1.4 通过 Numpy 数组创建](#newtensor4) -通常的CV、NLP任务,用户可能对数据准备操作关注不多,加上Tensor创建操作被封装,因此对于Tensor的创建感知不强。例如,在[使用卷积神经网络进行图像分类](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/practices/cv/convnet_image_classification.html)教程中,将Cifar10数据集中的图片数据转换为Tensor数据的ToTensor接口,就是在内部调用了paddle.to_tensor API。 +另外在常见深度学习任务中,数据样本可能是图片(image)、文本(text)、语音(audio)等多种类型,在送入神经网络训练或推理前均需要创建为 Tensor。飞桨提供了将这类数据手动创建为 Tensor 的方法,如: -```python -import paddle -from paddle.vision.transforms import ToTensor +[1.5 指定图像、文本数据创建](#newtensor5) + +由于这些操作在整个深度学习任务流程中比较常见且固定,飞桨在一些 API 中封装了 Tensor 自动创建的操作,从而无须手动转 Tensor。 + +[1.6 自动创建 Tensor 的功能介绍](#newtensor6) -transform = ToTensor() -cifar10_train = paddle.vision.datasets.Cifar10(mode='train', transform=transform) -train_loader = paddle.io.DataLoader(cifar10_train, - shuffle=True, - batch_size=batch_size) -``` +### 1.1 指定数据创建 -若需要基于给定数据或图片手动创建**Tensor**,Paddle也提供了多种方式,如:指定数据列表创建、指定形状创建、指定区间创建、指定图像、NLP真实场景数据创建等。 +与 Numpy 创建数组方式类似,通过给定 Python 序列(如列表 list、元组 tuple),可使用 [paddle.to_tensor](../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor。示例如下: -### 1.1 指定数据创建 -通过给定Python列表数据,可以创建任意维度(也称为轴)的Tensor,举例如下: +(1)创建类似向量(vector)的 1 维 Tensor: ```python -# 创建类似向量(vector)的一维 Tensor -import paddle # 后面的示例代码默认已导入paddle模块 +import paddle # 后面的示例代码默认已导入 paddle 模块 ndim_1_Tensor = paddle.to_tensor([2.0, 3.0, 4.0]) print(ndim_1_Tensor) ``` @@ -41,19 +74,18 @@ Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True, [2., 3., 4.]) ``` -特殊地,如果仅输入单个标量(scalar)数据(例如float/int/bool类型的单个元素),则会创建形状为[1]的**Tensor** +特殊地,如果仅输入单个标量(scalar)数据(例如 float/int/bool 类型的单个元素),则会创建形状为 [1] 的 Tensor,即 0 维 Tensor: ```python paddle.to_tensor(2) paddle.to_tensor([2]) ``` -上述两种创建方式完全一致,形状均为[1],输出如下: ```text +# 上述两种创建方式完全一致,形状均为 [1],输出如下: Tensor(shape=[1], dtype=int64, place=Place(gpu:0), stop_gradient=True, [2]) ``` - +(2)创建类似矩阵(matrix)的 2 维 Tensor: ```python -# 创建类似矩阵(matrix)的二维 Tensor ndim_2_Tensor = paddle.to_tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) print(ndim_2_Tensor) @@ -63,9 +95,8 @@ Tensor(shape=[2, 3], dtype=float32, place=Place(gpu:0), stop_gradient=True, [[1., 2., 3.], [4., 5., 6.]]) ``` - +(3)创建 3 维 Tensor: ```python -# 创建多维 Tensor ndim_3_Tensor = paddle.to_tensor([[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]], [[11, 12, 13, 14, 15], @@ -80,12 +111,15 @@ Tensor(shape=[2, 2, 5], dtype=int64, place=Place(gpu:0), stop_gradient=True, [[11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]]) ``` -上述不同维度的**Tensor**可以可视化的表示为: +上述不同维度的 Tensor 可视化的表示如下图所示: -
-
图1 不同维度的Tensor可视化表示
+
+ +
图1 不同维度的Tensor可视化表示
+
+ +需要注意的是,Tensor 必须形如矩形,即在任何一个维度上,元素的数量必须相等,否则会抛出异常,示例如下: -> **Tensor**必须形如矩形,也就是,在任何一个维度上,元素的数量必须**相等**,如果为以下情况将会抛出异常: ``` ndim_2_Tensor = paddle.to_tensor([[1.0, 2.0], [4.0, 5.0, 6.0]]) @@ -96,14 +130,18 @@ ValueError: Faild to convert input data to a regular ndarray : - Usually this means the input data contains nested lists with different lengths. ``` +> **说明:** +> +> * 飞桨也支持将 Tensor 转换为 Python 序列数据,可通过 [paddle.tolist](../api/paddle/tolist_cn.html) 实现,飞桨实际的转换处理过程是 **Python 序列 <-> Numpy 数组 <-> Tensor**。 +> * 基于给定数据创建 Tensor 时,飞桨是通过拷贝方式创建,与原始数据不共享内存。 -### 1.2 指定形状创建 +### 1.2 指定形状创建 -如果要创建一个指定形状的**Tensor**,可以使用以下API: +如果要创建一个指定形状的 Tensor,可以使用 [paddle.zeros](../api/paddle/zeros_cn.html)、[paddle.ones](../api/paddle/ones_cn.html)、[paddle.full](../api/paddle/full_cn.html) 实现。 ```python -paddle.zeros([m, n]) # 创建数据全为0,形状为[m, n]的Tensor -paddle.ones([m, n]) # 创建数据全为1,形状为[m, n]的Tensor -paddle.full([m, n], 10) # 创建数据全为10,形状为[m, n]的Tensor +paddle.zeros([m, n]) # 创建数据全为 0,形状为 [m, n] 的 Tensor +paddle.ones([m, n]) # 创建数据全为 1,形状为 [m, n] 的 Tensor +paddle.full([m, n], 10) # 创建数据全为 10,形状为 [m, n] 的 Tensor ``` 例如,`paddle.ones([2,3])`输出如下: ```text @@ -112,30 +150,77 @@ Tensor(shape=[2, 3], dtype=float32, place=Place(gpu:0), stop_gradient=True, [1., 1., 1.]]) ``` -### 1.3 指定区间创建 -如果要在指定区间内创建**Tensor**,可以使用以下API: +### 1.3 指定区间创建 + +如果要在指定区间内创建 Tensor,可以使用[paddle.arrange](../api/paddle/arrange_cn.html)、 [paddle.linspace](../api/paddle/linspace_cn.html) 实现。 ```python paddle.arange(start, end, step) # 创建以步长step均匀分隔区间[start, end)的Tensor paddle.linspace(start, end, num) # 创建以元素个数num均匀分隔区间[start, end)的Tensor ``` -例如,`paddle.arange(start=1, end=5, step=1)`输出如下: +示例如下: + +```python +paddle.arange(start=1, end=5, step=1) +``` ```text Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True, [1, 2, 3, 4]) ``` +> **说明:** +> +> 除了以上指定数据、形状、区间创建 Tensor 的方法,飞桨还支持如下类似的创建方式,如: +> * **创建一个空 Tensor**,即根据 shape 和 dtype 创建尚未初始化元素值的 Tensor,可通过 [paddle.empty](../api/paddle/empty_cn.html) 实现。 +> * **创建一个与其他 Tensor 具有相同 shape 与 dtype 的 Tensor**,可通过 [paddle.ones_like](../api/paddle/ones_like_cn.html) 、 [paddle.zeros_like](../api/paddle/zeros_like_cn.html) 、 [paddle.full_like](../api/paddle/full_like_cn.html) 、[paddle.empty_like](../api/paddle/empty_like_cn.html) 实现。 +> * **拷贝并创建一个与其他 Tensor 完全相同的 Tensor**,可通过 [paddle.clone](../api/paddle/clone_cn.html)实现。 +> * **创建一个满足特定分布的Tensor**,如[paddle.rand](../api/paddle/rand_cn.html), [paddle.randn](../api/paddle/randn_cn.html) , [paddle.randint](../api/paddle/randint_cn.html)等。 +> * **通过设置随机种子创建 Tensor**,可每次生成相同元素值的随机数 Tensor,可通过 [paddle.seed](../api/paddle/seed_cn.html) 和 [paddle.rand](../api/paddle/rand_cn.html) 组合实现。 + +### 1.4 通过 Numpy 数组创建 + +通过给定 Numpy 数组,可使用 [paddle.to_tensor](../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 +```python +tensor_temp = paddle.to_tensor(np.array([1.0, 2.0])) +print(tensor_temp) +``` +```text +Tensor(shape=[2], dtype=float64, place=Place(gpu:0), stop_gradient=True, + [1., 2.]) +``` +飞桨也支持将 Tensor 转换为 Numpy 数组,可通过 [Tensor.numpy](../api/paddle/Tensor_cn.html#numpy) 方法实现。 +```python +tensor_to_convert = paddle.to_tensor([1.,2.]) +tensor_to_convert.numpy() +``` +```text +array([1., 2.], dtype=float32) +``` + +> **说明:** +> +> 虽然飞桨框架中 Tensor 可以与 Numpy 数组方便地互相转换,但在实际应用中两者频繁转换会存在性能消耗,具体使用场景说明如下: +> * 场景一:在组网程序中,对网络中向量的处理,务必使用 Tensor,而不建议转成 Numpy 数组。如果在组网过程中转成Numpy 数组,并使用 Numpy 的函数会降低整体性能。 +> * 场景二:在数据处理和模型后处理等场景,也建议优先使用 Tensor,主要是飞桨为 AI 硬件做了大量的适配和性能优化工作,部分情况下会获得更好的使用体验和性能。 +> 目前飞桨的 Tensor 基本覆盖 Numpy 数组的操作并有所加强,所以推荐在程序中优先使用 Tensor 完成各种数据处理和组网操作。 + +### 1.5 指定图像、文本数据创建 + +在常见深度学习任务中,数据样本可能是图片(image)、文本(text)、语音(audio)等多种类型,在送入神经网络训练或推理前,这些数据和对应的标签均需要创建为 Tensor。以下是图像场景和 NLP 场景中手动转换 Tensor 方法的介绍。 + +* 对于图像场景,可使用 [paddle.vision.transforms.ToTensor](../api/paddle/vision/transforms/ToTensor_cn.html) 直接将 PIL.Image 格式的数据转为 Tensor,使用 [paddle.to_tensor](../api/paddle/to_tensor_cn.html) 将图像的标签(Label,通常是Python 或 Numpy 格式的数据)转为 Tensor。 +* 对于文本场景,需将文本数据解码为数字后,再通过 [paddle.to_tensor](../api/paddle/to_tensor_cn.html) 转为 Tensor。不同文本任务标签形式不一样,有的任务标签也是文本,有的则是数字,均需最终通过 paddle.to_tensor 转为 Tensor。 + +下面以图像场景为例介绍,以下示例代码中将随机生成的图片转换为 Tensor。 -### 1.4 指定图像、NLP真实场景数据创建Tensor -对于图片,需要用到视觉模块的ToTensor API。对于NLP文本数据,在将文本数据解码转换为数字后,通过to_tensor创建Tensor即可。下面示例为使用ToTensor,将随机生成的图片转换为Tensor。 ```python import numpy as np from PIL import Image import paddle.vision.transforms as T import paddle.vision.transforms.functional as F -fake_img = Image.fromarray((np.random.rand(224, 224, 3) * 255.).astype(np.uint8)) +fake_img = Image.fromarray((np.random.rand(224, 224, 3) * 255.).astype(np.uint8)) # 创建随机图片 transform = T.ToTensor() -tensor = transform(fake_img) +tensor = transform(fake_img) # 使用ToTensor()将图片转换为Tensor print(tensor) ``` @@ -145,25 +230,68 @@ Tensor(shape=[3, 224, 224], dtype=float32, place=Place(gpu:0), stop_gradient=Tru ..., [0.49803925, 0.72941178, 0.80392164, ..., 0.08627451, 0.97647065, 0.43137258]]]) ``` +> **说明:** +> +>实际编码时,由于飞桨数据加载的 [paddle.io.DataLoader](../api/paddle/io/DataLoader_cn.html) API 能够将原始 [paddle.io.Dataset](../api/paddle/io/Dataset_cn.html) 定义的数据自动转换为 Tensor,所以可以不做手动转换。具体如下节介绍。 + +### 1.6 自动创建 Tensor 的功能介绍 + +除了手动创建 Tensor 外,实际在飞桨框架中有一些 API 封装了 Tensor 创建的操作,从而无需用户手动创建 Tensor。例如 [paddle.io.DataLoader](../api/paddle/io/DataLoader_cn.html) 能够基于原始 Dataset,返回读取 Dataset 数据的迭代器,迭代器返回的数据中的每个元素都是一个 Tensor。另外在一些高层API,如 [paddle.Model.fit](../api/paddle/Model_cn.html) 、[paddle.Model.predict](../api/paddle/Model_cn.html) ,如果传入的数据不是 Tensor,会自动转为 Tensor 再进行模型训练或推理。 +> **说明:** +> +> paddle.Model.fit、paddle.Model.predict 等高层 API 支持传入 Dataset 或 DataLoader,如果传入的是 Dataset,那么会用 DataLoader 封装转为 Tensor 数据;如果传入的是 DataLoader,则直接从 DataLoader 迭代读取 Tensor 数据送入模型训练或推理。因此即使没有写将数据转为 Tensor 的代码,也能正常执行,提升了编程效率和容错性。 + +以下示例代码中,分别打印了原始数据集的数据,和送入 DataLoader 后返回的数据,可以看到数据结构由 Python list 转为了 Tensor。 +```python +import paddle + +from paddle.vision.transforms import Compose, Normalize + +transform = Compose([Normalize(mean=[127.5], + std=[127.5], + data_format='CHW')]) + +test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=transform) +print(test_dataset[0][1]) # 打印原始数据集的第一个数据的label +loader = paddle.io.DataLoader(test_dataset) +for data in enumerate(loader): + x, label = data[1] + print(label) # 打印由DataLoader返回的迭代器中的第一个数据的label + break +``` +```text +[7] # 原始数据中label为Python list +Tensor(shape=[1, 1], dtype=int64, place=Place(gpu_pinned), stop_gradient=True, + [[7]]) # 由DataLoader转换后,label为Tensor +``` + +## 三、Tensor 的属性 -## 二、Tensor的属性 +在前文中,可以看到打印 Tensor 时有 shape、dtype、place 等信息,这些都是 Tensor 的重要属性,想要了解如何操作 Tensor 需要对其属性有一定了解,接下来分别展开介绍 Tensor 的属性相关概念。 +```text +Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True, + [2., 3., 4.]) +``` -### 2.1 Tensor的形状 +### 2.1 Tensor的形状(shape) -查看一个**Tensor**的形状可以通过 **Tensor.shape**,形状是 **Tensor** 的一个重要属性,以下为相关概念: +**(1)形状的介绍** +形状是 Tensor 的一个重要的基础属性,可以通过 [Tensor.shape](../api/paddle/Tensor_cn.html#shape) 查看一个 Tensor 的形状,以下为相关概念: -1. shape:描述了Tensor每个维度上元素的数量 -2. ndim: Tensor的维度数量,例如向量的维度为1,矩阵的维度为2,Tensor可以有任意数量的维度 -3. axis或者dimension:指Tensor某个特定的维度 -4. size:指Tensor中全部元素的个数 + * shape:描述了 Tensor 每个维度上元素的数量。 + * ndim: Tensor 的维度数量,例如向量的维度为 1,矩阵的维度为2,Tensor 可以有任意数量的维度。 + * axis 或者 dimension:Tensor 的轴,即某个特定的维度。 + * size:Tensor 中全部元素的个数。 -创建1个四维 **Tensor**,并通过图形来直观表达以上几个概念之间的关系; +创建 1 个四维 Tensor ,并通过图形来直观表达以上几个概念之间的关系: ```python ndim_4_Tensor = paddle.ones([2, 3, 4, 5]) ``` -
-
图2 Tensor的shape、axis、dimension、ndim之间的关系
+
+ +
图2 Tensor的shape、axis、dimension、ndim之间的关系
+
```python print("Data Type of every element:", ndim_4_Tensor.dtype) @@ -179,67 +307,94 @@ Shape of Tensor: [2, 3, 4, 5] Elements number along axis 0 of Tensor: 2 Elements number along the last axis of Tensor: 5 ``` +**(2)重置 Tensor 形状(Reshape) 的方法** +重新设置 Tensor 的 shape 在深度学习任务中比较常见,如一些计算类 API 会对输入数据有特定的形状要求,这时可通过 [paddle.reshape](../api/paddle/reshape_cn.html) 接口来改变 Tensor 的 shape,但并不改变 Tensor 的 size 和其中的元素数据。 + -重新设置**Tensor**的shape在实际编程中具有重要意义,例如在[使用卷积神经网络进行图像分类](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/practices/cv/convnet_image_classification.html)教程中,通过paddle.unsqueeze API,使y_data增加了batch维,目的是使y_data与其他数据形状对齐,满足后续Paddle API的对于输入形状的要求。相比unsqueeze,Paddle提供的reshape接口,能够直接指定Tensor的目标shape: +以下示例代码中,创建 1 个 `shape=[3]` 的一维 Tensor,使用 reshape 功能将该 Tensor 重置为 `shape=[1, 3]` 的二维 Tensor。这种做法经常用在把一维的标签(label)数据扩展为二维,由于飞桨框架中神经网络通常需要传入一个 batch 的数据进行计算,因此可将数据增加一个 batch 维,方便后面的数据计算。 ```python -ndim_3_Tensor = paddle.to_tensor([[[1, 2, 3, 4, 5], - [6, 7, 8, 9, 10]], - [[11, 12, 13, 14, 15], - [16, 17, 18, 19, 20]], - [[21, 22, 23, 24, 25], - [26, 27, 28, 29, 30]]]) -print("the shape of ndim_3_Tensor:", ndim_3_Tensor.shape) +ndim_1_Tensor = paddle.to_tensor([1, 2, 3]) +print("the shape of ndim_1_Tensor:", ndim_1_Tensor.shape) -reshape_Tensor = paddle.reshape(ndim_3_Tensor, [2, 5, 3]) +reshape_Tensor = paddle.reshape(ndim_1_Tensor, [1, 3]) print("After reshape:", reshape_Tensor.shape) ``` ```text -the shape of ndim_3_Tensor: [3, 2, 5] -After reshape: [2, 5, 3] +the shape of ndim_1_Tensor: [3] +After reshape: [1, 3] ``` -在指定新的shape时存在一些技巧: - -**1.** -1 表示这个维度的值是从Tensor的元素总数和剩余维度推断出来的。因此,有且只有一个维度可以被设置为-1。 +在指定新的 shape 时存在一些技巧: + * `-1` 表示这个维度的值是从 Tensor 的元素总数和剩余维度自动推断出来的。因此,有且只有一个维度可以被设置为 -1。 + * `0` 表示该维度的元素数量与原值相同,因此 shape 中 0 的索引值必须小于 Tensor 的维度(索引值从 0 开始计,如第 1 维的索引值是 0,第二维的索引值是 1)。 -**2.** 0 表示实际的维数是从Tensor的对应维数中复制出来的,因此shape中0的索引值不能超过Tensor的维度。 - -有一些例子可以很好解释这些技巧: +通过几个例子来详细了解: ```text -origin:[3, 2, 5] reshape:[3, 10] actual: [3, 10] -origin:[3, 2, 5] reshape:[-1] actual: [30] -origin:[3, 2, 5] reshape:[0, 5, -1] actual: [3, 5, 2] +origin:[3, 2, 5] reshape:[3, 10] actual: [3, 10] # 直接指定目标 shape +origin:[3, 2, 5] reshape:[-1] actual: [30] # 转换为1维,维度根据元素总数推断出来是3*2*5=30 +origin:[3, 2, 5] reshape:[-1, 5] actual: [6, 5] # 转换为2维,固定一个维度5,另一个维度根据元素总数推断出来是30÷5=6 +origin:[3, 2, 5] reshape:[0, -1] actual: [3, 6] # reshape:[0, -1]中0的索引值为0,按照规则,转换后第0维的元素数量与原始Tensor第0维的元素数量相同,为3;第1维的元素数量根据元素总值计算得出为30÷3=10。 +origin:[3, 2] reshape:[3, 1, 0] error: # reshape:[3, 1, 0]中0的索引值为2,但原Tensor只有2维,无法找到与第3维对应的元素数量,因此出错。 ``` -可以发现,reshape为[-1]时,会将Tensor按其在计算机上的内存分布展平为一维。 +从上面的例子可以看到,通过 reshape:[-1] ,可以很方便地将 Tensor 按其在计算机上的内存分布展平为一维。 ```python print("Tensor flattened to Vector:", paddle.reshape(ndim_3_Tensor, [-1]).numpy()) ``` ```text Tensor flattened to Vector: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30] ``` +> **说明:** +> +> 除了 paddle.reshape 可重置 Tensor 的形状,还可通过如下方法改变 shape: +> * [paddle.squeeze](../api/paddle/squeeze_cn.html),可实现 Tensor 的降维操作,即把 Tensor 中尺寸为 1 的维度删除。 +> * [paddle.unsqueeze](../api/paddle/unsqueeze_cn.html),可实现 Tensor 的升维操作,即向 Tensor 中某个位置插入尺寸为 1 的维度。 +> * [paddle.flatten](../api/paddle/flatten_cn.html),将 Tensor 的数据在指定的连续维度上展平。 +> * [transpose](../api/paddle/transpose_cn.html),对 Tensor 的数据进行重排。 -### 2.2 Tensor的数据类型 +**(3)原位(Inplace)操作和非原位操作的区别** -**Tensor**的数据类型,可以通过 Tensor.dtype 来查看,dtype支持:'bool', 'float16', 'float32', 'float64', 'uint8', 'int8', 'int16', 'int32', 'int64'。 +飞桨框架的 API 有原位(Inplace)操作和非原位操作之分,原位操作即在原 Tensor 上保存操作结果,输出 Tensor 将与输入Tensor 共享数据,并且没有 Tensor 数据拷贝的过程。非原位操作则不会修改原 Tensor,而是返回一个新的 Tensor。通过 API 名称区分两者,如 [paddle.reshape](../api/paddle/reshape_cn.html) 是非原位操作,[paddle.reshape_](../api/paddle/reshape__cn.html) 是原位操作。 -* 通过Python元素创建的Tensor,可以通过dtype来进行指定,如果未指定: +下面以 reshape 为例说明,通过对比Tensor的 name (每个 Tensor 创建时都会有一个独一无二的 name),判断是否为同一个Tensor。 +```python +origin_tensor = paddle.to_tensor([1, 2, 3]) +new_tensor = paddle.reshape(origin_tensor, [1, 3]) # 非原位操作 +same_tensor = paddle.reshape_(origin_tensor, [1, 3]) # 原位操作 +print("origin_tensor name: ", origin_tensor.name) +print("new_tensor name: ", new_tensor.name) +print("same_tensor name: ", same_tensor.name) +``` +```text +origin_tensor name: generated_tensor_0 +new_tensor name: auto_0_ # 非原位操作后产生的Tensor与原始Tensor的名称不同 +same_tensor name: generated_tensor_0 # 原位操作后产生的Tensor与原始Tensor的名称相同 +``` - * 对于python整型数据,则会创建int64型Tensor - * 对于python浮点型数据,默认会创建float32型Tensor,并且可以通过set_default_type来调整浮点型数据的默认类型。 +### 2.2 Tensor的数据类型(dtype) +**(1)指定数据类型的介绍** +Tensor 的数据类型 dtype 可以通过 [Tensor.dtype](../api/paddle/Tensor_cn.html#dtype) 查看,支持类型包括:`bool`、`float16`、`float32`、`float64`、`uint8`、`int8`、`int16`、`int32`、`int64`、`complex64`、`complex128`。 -* 通过Numpy数组创建的Tensor,则与其原来的数据类型保持相同。 +同一 Tensor 中所有元素的数据类型均相同,通常通过如下方式指定: +* 通过给定 Python 序列创建的 Tensor,可直接使用 dtype 参数指定。如果未指定: + * 对于 Python 整型数据,默认会创建 `int64` 型 Tensor; + * 对于 Python 浮点型数据,默认会创建 `float32` 型 Tensor,并且可以通过 [paddle.set_default_dtype](../api/paddle/set_default_dtype_cn.html) 来调整浮点型数据的默认类型。 ```python +# 创建Tensor时指定dtype +ndim_1_tensor = paddle.to_tensor([2.0, 3.0, 4.0], dtype='float64') +print("Tensor dtype of ndim_1_tensor:", ndim_1_tensor.dtype) +# 创建Tensor时不指定dtype,自动选择对应的默认类型 print("Tensor dtype from Python integers:", paddle.to_tensor(1).dtype) print("Tensor dtype from Python floating point:", paddle.to_tensor(1.0).dtype) ``` ```text +Tensor dtype of ndim_1_tensor: paddle.float64 Tensor dtype from Python integers: paddle.int64 Tensor dtype from Python floating point: paddle.float32 ``` - -**Tensor**不仅支持 floats、ints 类型数据,也支持复数类型数据。如果输入为复数,则**Tensor**的dtype为 ``complex64`` 或 ``complex128`` ,其每个元素均为1个复数: +* 通过 Numpy 数组或其他 Tensor 创建的 Tensor,则与其原来的数据类型保持相同。 +* Tensor 不仅支持 float、int 类型数据,也支持 complex 复数类型数据。如果输入为复数,则 Tensor 的 dtype 为 ``complex64`` 或 ``complex128`` ,其每个元素均为 1 个复数。如果未指定,默认数据类型是``complex64``: ```python ndim_2_Tensor = paddle.to_tensor([[(1+1j), (2+2j)], @@ -252,8 +407,9 @@ Tensor(shape=[2, 2], dtype=complex64, place=Place(gpu:0), stop_gradient=True, [[(1+1j), (2+2j)], [(3+3j), (4+4j)]]) ``` +**(2)修改数据类型的方法** -Paddle提供了**cast**接口来改变dtype: +飞桨框架提供了[paddle.cast](../api/paddle/cast_cn.html) 接口来改变 Tensor 的 dtype: ```python float32_Tensor = paddle.to_tensor(1.0) @@ -267,14 +423,14 @@ print("Tensor after cast to int64:", int64_Tensor.dtype) Tensor after cast to float64: paddle.float64 Tensor after cast to int64: paddle.int64 ``` +### 2.3 Tensor的设备位置(place) +初始化 Tensor 时可以通过 [Tensor.place](../api/paddle/Tensor_cn.html#place) 来指定其分配的设备位置,可支持的设备位置有:CPU、GPU、固定内存、XPU(Baidu Kunlun)、NPU(Huawei)、MLU(寒武纪)、IPU(Graphcore)等。其中固定内存也称为不可分页内存或锁页内存,其与 GPU 之间具有更高的读写效率,并且支持异步传输,这对网络整体性能会有进一步提升,但其缺点是分配空间过多时可能会降低主机系统的性能,因为其减少了用于存储虚拟内存数据的可分页内存。 +> **说明:** +> +> * 当未指定 place 时,Tensor 默认设备位置和安装的飞桨框架版本一致。如安装了 GPU 版本的飞桨,则设备位置默认为 GPU,即 Tensor 的`place` 默认为 [paddle.CUDAPlace](../api/paddle/CUDAPlace_cn.html)。 +> * 使用 [paddle.device.set_device](../api/paddle/device/set_device_cn.html) 可设置全局默认的设备位置。Tensor.place 的指定值优先级高于全局默认值。 -### 2.3 Tensor的设备位置 - -初始化**Tensor**时可以通过**place**来指定其分配的设备位置,可支持的设备位置有三种:CPU/GPU/固定内存,其中固定内存也称为不可分页内存或锁页内存,其与GPU之间具有更高的读写效率,并且支持异步传输,这对网络整体性能会有进一步提升,但其缺点是分配空间过多时可能会降低主机系统的性能,因为其减少了用于存储虚拟内存数据的可分页内存。 - -> 当未指定place时,Tensor默认设备位置和安装的Paddle版本一致,如安装了GPU版本的Paddle,则设备位置默认为GPU。就是说,在创建Tensor时如果未指定`place`参数,则Tensor的`place`为paddle.CUDAPlace。 - -以下示例分别创建了CPU、GPU和固定内存上的Tensor,并通过 `Tensor.place` 查看Tensor所在的设备位置: +以下示例分别创建了CPU、GPU和固定内存上的 Tensor,并通过 `Tensor.place` 查看 Tensor 所在的设备位置: * **创建CPU上的Tensor** ```python @@ -306,9 +462,9 @@ print(pin_memory_Tensor.place) Place(gpu_pinned) ``` -### 2.4 Tensor的名称 +### 2.4 Tensor的名称(name) -Tensor的名称是其唯一的标识符,为python字符串类型,查看一个Tensor的名称可以通过Tensor.name属性。默认地,在每个Tensor创建时,Paddle会自定义一个独一无二的名称。 +Tensor 的名称是其唯一的标识符,为 Python 字符串类型,查看一个 Tensor 的名称可以通过 Tensor.name 属性。默认地,在每个Tensor 创建时,会自定义一个独一无二的名称。 ```python print("Tensor name:", paddle.to_tensor(1).name) @@ -316,21 +472,34 @@ print("Tensor name:", paddle.to_tensor(1).name) ```text Tensor name: generated_tensor_0 ``` +### 2.5 Tensor 的 stop_gradient 属性 +stop_gradient 表示是否停止计算梯度,默认值为 True,表示停止计算梯度,梯度不再回传。可以直接设置 stop_gradient 的值。 + +```python +eg = paddle.to_tensor(1) +print("Tensor stop_gradient:", eg.stop_gradient) +eg.stop_gradient = False +print("Tensor stop_gradient:", eg.stop_gradient) +``` +```text +Tensor stop_gradient: True +Tensor stop_gradient: False +``` -## 三、Tensor的操作 +## 四、Tensor的操作 ### 3.1 索引和切片 -您可以通过索引或切片方便地访问或修改 Tensor。Paddle 使用标准的 Python 索引规则与 Numpy 索引规则,与 [Indexing a list or a string in Python](https://docs.python.org/3/tutorial/introduction.html#strings)类似。具有以下特点: +通过索引或切片方式可访问或修改 Tensor。飞桨框架使用标准的 Python 索引规则与 Numpy 索引规则,与 [Indexing a list or a string in Python](https://docs.python.org/3/tutorial/introduction.html#strings) 类似。具有以下特点: -1. 基于 0-n 的下标进行索引,如果下标为负数,则从尾部开始计算 -2. 通过冒号 ``:`` 分隔切片参数 ``start:stop:step`` 来进行切片操作,其中 start、stop、step 均可缺省 +1. 基于 0-n 的下标进行索引,如果下标为负数,则从尾部开始计算。 +2. 通过冒号 ``:`` 分隔切片参数,``start:stop:step`` 来进行切片操作,其中 start、stop、step 均可缺省。 -#### 访问 Tensor -* 针对一维 **Tensor**,则仅有单个轴上的索引或切片: +#### 3.1.1 访问 Tensor +* 针对一维 Tensor,仅有单个维度上的索引或切片: ```python ndim_1_Tensor = paddle.to_tensor([0, 1, 2, 3, 4, 5, 6, 7, 8]) -print("Origin Tensor:", ndim_1_Tensor.numpy()) -print("First element:", ndim_1_Tensor[0].numpy()) +print("Origin Tensor:", ndim_1_Tensor.numpy()) # 原始1维Tensor +print("First element:", ndim_1_Tensor[0].numpy()) # 取Tensor第一个元素的值? print("Last element:", ndim_1_Tensor[-1].numpy()) print("All element:", ndim_1_Tensor[:].numpy()) print("Before 3:", ndim_1_Tensor[:3].numpy()) @@ -351,6 +520,7 @@ Interval of 3: [0 3 6] Reverse: [8 7 6 5 4 3 2 1 0] ``` + * 针对二维及以上的 **Tensor**,则会有多个维度上的索引或切片: ```python ndim_2_Tensor = paddle.to_tensor([[0, 1, 2, 3], @@ -378,7 +548,7 @@ All element: [[ 0 1 2 3] First row and second column: [1] ``` -索引或切片的第一个值对应第 0 维,第二个值对应第 1 维,以此类推,如果某个维度上未指定索引,则默认为 ``:`` 。例如: +索引或切片的第一个值对应第 0 维,第二个值对应第 1 维,依次类推,如果某个维度上未指定索引,则默认为 ``:`` 。例如: ```python ndim_2_Tensor[1] ndim_2_Tensor[1, :] @@ -390,13 +560,12 @@ Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True, [4, 5, 6, 7]) ``` -#### 修改 Tensor +#### 3.1.2 修改 Tensor +与访问 Tensor 类似,修改 Tensor 可以在单个或多个维度上通过索引或切片操作。同时,支持将多种类型的数据赋值给该 Tensor,当前支持的数据类型有:`int`,`float`,`numpy.ndarray`,`omplex`,`Tensor`。 > **注意:** > -> 请慎重通过索引或切片修改 Tensor,该操作会**原地**修改该 Tensor 的数值,且原值不会被保存。如果被修改的 Tensor 参与梯度计算,将仅会使用修改后的数值,这可能会给梯度计算引入风险。Paddle 之后将会对具有风险的操作进行检测和报错。 - -与访问 Tensor 类似,修改 Tensor 可以在单个或多个轴上通过索引或切片操作。同时,支持将多种类型的数据赋值给该 Tensor,当前支持的数据类型有:`int`, `float`, `numpy.ndarray`, `Tensor`。 +> 请慎重通过索引或切片修改 Tensor,该操作会**原地**修改该 Tensor 的数值,且原值不会被保存。如果被修改的 Tensor 参与梯度计算,仅会使用修改后的数值,这可能会给梯度计算引入风险。飞桨框架会自动检测不当的原位(inplace)使用并报错。 ```python import numpy as np @@ -414,7 +583,7 @@ x[1] = paddle.ones([3]) # x : [[1., 2., 3.], [1., 1., 1.]] --- -同时,Paddle 还提供了丰富的 Tensor 操作的 API,包括数学运算、逻辑运算、线性代数等100余种 API,这些 API 调用有两种方法: +同时,飞桨还提供了丰富的 Tensor 操作的 API,包括数学运算、逻辑运算、线性代数等100余种 API,这些 API 调用有两种方法: ```python x = paddle.to_tensor([[1.1, 2.2], [3.3, 4.4]], dtype="float64") y = paddle.to_tensor([[5.5, 6.6], [7.7, 8.8]], dtype="float64") @@ -433,7 +602,7 @@ Tensor(shape=[2, 2], dtype=float64, place=Place(gpu:0), stop_gradient=True, [11. , 13.20000000]]) ``` -可以看出,使用 **Tensor 类成员函数** 和 **Paddle API** 具有相同的效果,由于 **类成员函数** 操作更为方便,以下均从 **Tensor 类成员函数** 的角度,对常用 **Tensor** 操作进行介绍。 +可以看出,使用 **Tensor 类成员函数** 和 **Paddle API** 具有相同的效果,由于 **类成员函数** 操作更为方便,以下均从 **Tensor 类成员函数** 的角度,对常用 Tensor 操作进行介绍。 ### 3.2 数学运算 ```python @@ -460,7 +629,7 @@ x.prod() #指定维度上元素累乘,默认为全部维 x.sum() #指定维度上元素的和,默认为全部维度 ``` -Paddle对python数学运算相关的魔法函数进行了重写,例如: +飞桨框架对 Python 数学运算相关的魔法函数进行了重写,例如: ```text x + y -> x.add(y) #逐元素相加 x - y -> x.subtract(y) #逐元素相减 @@ -483,7 +652,7 @@ x.greater_equal(y) #判断Tensor x的元素是否大于或等于Tenso x.allclose(y) #判断Tensor x的全部元素是否与Tensor y的全部元素接近,并返回形状为[1]的布尔类Tensor ``` -同样地,Paddle对python逻辑比较相关的魔法函数进行了重写,以下操作与上述结果相同。 +同样地,飞桨框架对 Python 逻辑比较相关的魔法函数进行了重写,以下操作与上述结果相同。 ```text x == y -> x.equal(y) #判断两个Tensor的每个元素是否相等 x != y -> x.not_equal(y) #判断两个Tensor的每个元素是否不相等 @@ -493,7 +662,7 @@ x > y -> x.greater_than(y) #判断Tensor x的元素是否大于Tensor y的对 x >= y -> x.greater_equal(y) #判断Tensor x的元素是否大于或等于Tensor y的对应元素 ``` -以下操作仅针对bool型Tensor: +以下操作仅针对 bool 型Tensor: ```python x.logical_and(y) #对两个布尔类型Tensor逐元素进行逻辑与操作 x.logical_or(y) #对两个布尔类型Tensor逐元素进行逻辑或操作 @@ -512,61 +681,34 @@ x.matmul(y) #矩阵乘法 > **注意** > -> Paddle中API有原位(inplace)操作和非原位操作之分。原位操作即在原**Tensor**上保存操作结果,非原位(inplace)操作则不会修改原**Tensor**,而是返回一个新的**Tensor**来表示运算结果。在Paddle2.1后,部分API有对应的原位操作版本,在API后加上 `_` 表示,如`x.add(y)`是非原位操作,`x.add_(y)`为原位操作。 +> 以上计算 API 也有原位(inplace)操作和非原位操作之分,如`x.add(y)`是非原位操作,`x.add_(y)`为原位操作。 -更多Tensor操作相关的API,请参考 [class paddle.Tensor](../../../api/paddle/Tensor_cn.html) -## 四、Tensor 与 numpy数组 相互转换 -### 4.1 Tensor转换为numpy数组 -通过 Tensor.numpy() 方法,将 **Tensor** 转化为 **Numpy数组**: -```python -tensor_to_convert = paddle.to_tensor([1.,2.]) -tensor_to_convert.numpy() -``` -```text -array([1., 2.], dtype=float32) -``` -### 4.2 numpy数组转换为Tensor -通过paddle.to_tensor() 方法,将 **Numpy数组** 转化为 **Tensor**: -```python -tensor_temp = paddle.to_tensor(np.array([1.0, 2.0])) -print(tensor_temp) -``` -```text -Tensor(shape=[2], dtype=float64, place=Place(gpu:0), stop_gradient=True, - [1., 2.]) -``` -创建的 **Tensor** 与原 **Numpy array** 具有相同的形状与数据类型。 - -> **注意** -> -> 虽然Paddle的Tensor可以与Numpy的数组方便的互相转换,但在实际中两者频繁转换会性能消耗。目前飞桨的Tensor已经基本覆盖Numpy的所有操作并有所加强,所以推荐用户在程序中优先使用飞桨的Tensor完成各种数据处理和组网操作。具体分为如下两种场景: - -> 场景一:在组网程序中,对网络中向量的处理,务必使用Tensor,而不建议转成Numpy的数组。如果在组网过程中转成Numpy的数组,并使用Numpy的函数会拖慢整体性能。 +## 五、Tensor 的广播机制 -> 场景二:在数据处理和模型后处理等场景,建议优先使用Tensor,主要是飞桨为AI硬件做了大量的适配和性能优化工作,部分情况下会获得更好的使用体验和性能。 -## 五、Tensor 的广播操作 -Paddle和其他框架一样,提供的一些API支持广播(broadcasting)机制,允许在一些运算时使用不同形状的Tensor。 -通常来讲,如果有一个形状较小和一个形状较大的Tensor,会希望多次使用较小的Tensor来对较大的Tensor执行一些操作,看起来像是较小形状的Tensor的形状首先被扩展到和较大形状的Tensor一致,然后做运算。值得注意的是,这期间并没有对较小形状Tensor的数据拷贝操作。 +在深度学习任务中,有时需要使用较小形状的 Tensor 与较大形状的 Tensor 执行计算,广播机制就是将较小形状的 Tensor 扩展到与较大形状的 Tensor 一样的形状,便于匹配计算,同时又没有对较小形状 Tensor 进行数据拷贝操作,从而提升算法实现的运算效率。 +飞桨框架提供的一些API支持广播(broadcasting)机制,允许在一些运算时使用不同形状的 Tensor。 +飞桨 Tensor 的广播机制主要遵循如下规则(参考 [Numpy 广播机制](https://numpy.org/doc/stable/user/basics.broadcasting.html#module-numpy.doc.broadcasting)): -Paddle的广播机制主要遵循如下规则(参考 [Numpy 广播机制](https://numpy.org/doc/stable/user/basics.broadcasting.html#module-numpy.doc.broadcasting)): +* 每个 Tensor 至少为一维 Tensor +* 从最后一个维度向前开始比较两个 Tensor 的形状,需要满足如下条件才能进行广播:两个 Tensor 的维度大小相等;或者其中一个 Tensor 的维度等于 1;或者其中一个 Tensor 的维度不存在。 -1. 每个Tensor至少为一维Tensor -2. 从后往前比较Tensor的形状,当前维度的大小要么相等,要么其中一个等于一,要么其中一个不存在 - -例如: +举例如下: ```python +# 可以广播的例子1 x = paddle.ones((2, 3, 4)) y = paddle.ones((2, 3, 4)) # 两个Tensor 形状一致,可以广播 z = x + y print(z.shape) # [2, 3, 4] - +``` +```python +# 可以广播的例子2 x = paddle.ones((2, 3, 1, 5)) y = paddle.ones((3, 4, 1)) -# 从后向前依次比较: +# 从最后一个维度向前依次比较: # 第一次:y的维度大小是1 # 第二次:x的维度大小是1 # 第三次:x和y的维度大小相等 @@ -575,31 +717,36 @@ y = paddle.ones((3, 4, 1)) z = x + y print(z.shape) # [2, 3, 4, 5] - -# 相反 +``` +```python +# 不可广播的例子 x = paddle.ones((2, 3, 4)) y = paddle.ones((2, 3, 6)) -# 此时x和y是不可广播的,因为第一次比较 4不等于6 +# 此时x和y是不可广播的,因为第一次比较:4不等于6 # z = x + y # ValueError: (InvalidArgument) Broadcast dimension mismatch. ``` -现在你知道什么情况下两个Tensor是可以广播的,两个Tensor进行广播语义后的结果Tensor的形状计算规则如下: +在了解两个 Tensor 在什么情况下可以广播的规则后,两个Tensor进行广播语义后的结果Tensor的形状计算规则如下: -1. 如果两个Tensor的形状的长度不一致,那么需要在较小形状长度的矩阵向前添加1,直到两个Tensor的形状长度相等。 -2. 保证两个Tensor形状相等之后,每个维度上的结果维度就是当前维度上较大的那个。 +* 如果两个Tensor的形状的长度不一致,会在较小长度的形状矩阵前部添加1,直到两个Tensor的形状长度相等。 +* 保证两个Tensor形状相等之后,每个维度上的结果维度就是当前维度上的较大值。 -例如: +举例如下: ```python x = paddle.ones((2, 1, 4)) -y = paddle.ones((3, 1)) +y = paddle.ones((3, 1)) # y的形状长度为2,小于x的形状长度3,因此会在y的形状前部添加1,结果就是y的形状变为[1, 3, 1] z = x + y print(z.shape) -# z的形状: [2,3,4] - -x = paddle.ones((2, 1, 4)) -y = paddle.ones((3, 2)) -# z = x + y -# ValueError: (InvalidArgument) Broadcast dimension mismatch. +# z的形状: [2,3,4],z的每一维度上的尺寸,将取x和y对应维度上尺寸的较大值,如第0维x的尺寸为2,y的尺寸为1,则z的第0维尺寸为2 ``` + +
+ +
图3 Tensor 广播示例
+
+ +## 六、总结 + +Tensor 作为飞桨框架中重要的数据结构,具有丰富的 API 用以对 Tensor 进行创建、访问、修改、计算等一系列操作,从而满足深度学习任务的需要。更多 Tensor 相关的介绍,请参考 [paddle.Tensor](../../../api/paddle/Tensor_cn.html) API 文档。 From f33fc9e819a23f9f7c5ffab09fabe679365f60b8 Mon Sep 17 00:00:00 2001 From: betterpig Date: Thu, 31 Mar 2022 08:03:20 +0000 Subject: [PATCH 03/17] fix bugs --- .../basic_concept/tensor_introduction_cn.md | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 19fb91da68b..cf612d9c629 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -3,7 +3,7 @@ ## 一、Tensor 的概念介绍 -飞桨使用张量([Tensor](../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。不同之处是 Tensor 不仅可以运行在 CPU 上,还支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;并且飞桨基于 Tensor,从数据处理、组网操作、硬件适配等各方面进行了优化,在神经网络训练、推理等任务中可获得更好的效果和使用体验。因此推荐优先使用 Tensor 完成数据处理和组网操作。 +飞桨使用张量([Tensor](../../../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。不同之处是 Tensor 不仅可以运行在 CPU 上,还支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;并且飞桨基于 Tensor,从数据处理、组网操作、硬件适配等各方面进行了优化,在神经网络训练、推理等任务中可获得更好的效果和使用体验。因此推荐优先使用 Tensor 完成数据处理和组网操作。 在飞桨框架中,神经网络的输入、输出数据,以及网络中的参数均采用 Tensor 数据结构,示例如下: ```python def train(model): @@ -38,15 +38,18 @@ predicts: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=Fa weight: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=False, [0.02227839]) ``` -以上示例代码来源 [使用LeNet在MNIST数据集实现图像分类](../practices/cv/image_classification) 任务 5.1 小节(篇幅原因仅截取部分),分别打印了神经网络的输入、输出数据和网络中的参数,可以看到均采用了 Tensor 数据结构。 +以上示例代码来源 [使用LeNet在MNIST数据集实现图像分类](../../../practices/cv/image_classification) 任务 5.1 小节(篇幅原因仅截取部分),分别打印了神经网络的输入、输出数据和网络中的参数,可以看到均采用了 Tensor 数据结构。 ## 二、Tensor 的创建 飞桨可基于给定数据手动创建 Tensor,并提供了多种方式,如: [1.1 指定数据创建](#newtensor1) + [1.2 指定形状创建](#newtensor2) + [1.3 指定区间创建](#newtensor3) + [1.4 通过 Numpy 数组创建](#newtensor4) 另外在常见深度学习任务中,数据样本可能是图片(image)、文本(text)、语音(audio)等多种类型,在送入神经网络训练或推理前均需要创建为 Tensor。飞桨提供了将这类数据手动创建为 Tensor 的方法,如: @@ -60,7 +63,7 @@ weight: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=Fals ### 1.1 指定数据创建 -与 Numpy 创建数组方式类似,通过给定 Python 序列(如列表 list、元组 tuple),可使用 [paddle.to_tensor](../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor。示例如下: +与 Numpy 创建数组方式类似,通过给定 Python 序列(如列表 list、元组 tuple),可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor。示例如下: (1)创建类似向量(vector)的 1 维 Tensor: ```python @@ -132,12 +135,12 @@ ValueError: ``` > **说明:** > -> * 飞桨也支持将 Tensor 转换为 Python 序列数据,可通过 [paddle.tolist](../api/paddle/tolist_cn.html) 实现,飞桨实际的转换处理过程是 **Python 序列 <-> Numpy 数组 <-> Tensor**。 +> * 飞桨也支持将 Tensor 转换为 Python 序列数据,可通过 [paddle.tolist](../../../api/paddle/tolist_cn.html) 实现,飞桨实际的转换处理过程是 **Python 序列 <-> Numpy 数组 <-> Tensor**。 > * 基于给定数据创建 Tensor 时,飞桨是通过拷贝方式创建,与原始数据不共享内存。 ### 1.2 指定形状创建 -如果要创建一个指定形状的 Tensor,可以使用 [paddle.zeros](../api/paddle/zeros_cn.html)、[paddle.ones](../api/paddle/ones_cn.html)、[paddle.full](../api/paddle/full_cn.html) 实现。 +如果要创建一个指定形状的 Tensor,可以使用 [paddle.zeros](../../../api/paddle/zeros_cn.html)、[paddle.ones](../../../api/paddle/ones_cn.html)、[paddle.full](../../../api/paddle/full_cn.html) 实现。 ```python paddle.zeros([m, n]) # 创建数据全为 0,形状为 [m, n] 的 Tensor paddle.ones([m, n]) # 创建数据全为 1,形状为 [m, n] 的 Tensor @@ -153,7 +156,7 @@ Tensor(shape=[2, 3], dtype=float32, place=Place(gpu:0), stop_gradient=True, ### 1.3 指定区间创建 -如果要在指定区间内创建 Tensor,可以使用[paddle.arrange](../api/paddle/arrange_cn.html)、 [paddle.linspace](../api/paddle/linspace_cn.html) 实现。 +如果要在指定区间内创建 Tensor,可以使用[paddle.arrange](../../../api/paddle/arrange_cn.html)、 [paddle.linspace](../../../api/paddle/linspace_cn.html) 实现。 ```python paddle.arange(start, end, step) # 创建以步长step均匀分隔区间[start, end)的Tensor paddle.linspace(start, end, num) # 创建以元素个数num均匀分隔区间[start, end)的Tensor @@ -170,15 +173,15 @@ Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True, > **说明:** > > 除了以上指定数据、形状、区间创建 Tensor 的方法,飞桨还支持如下类似的创建方式,如: -> * **创建一个空 Tensor**,即根据 shape 和 dtype 创建尚未初始化元素值的 Tensor,可通过 [paddle.empty](../api/paddle/empty_cn.html) 实现。 -> * **创建一个与其他 Tensor 具有相同 shape 与 dtype 的 Tensor**,可通过 [paddle.ones_like](../api/paddle/ones_like_cn.html) 、 [paddle.zeros_like](../api/paddle/zeros_like_cn.html) 、 [paddle.full_like](../api/paddle/full_like_cn.html) 、[paddle.empty_like](../api/paddle/empty_like_cn.html) 实现。 -> * **拷贝并创建一个与其他 Tensor 完全相同的 Tensor**,可通过 [paddle.clone](../api/paddle/clone_cn.html)实现。 -> * **创建一个满足特定分布的Tensor**,如[paddle.rand](../api/paddle/rand_cn.html), [paddle.randn](../api/paddle/randn_cn.html) , [paddle.randint](../api/paddle/randint_cn.html)等。 -> * **通过设置随机种子创建 Tensor**,可每次生成相同元素值的随机数 Tensor,可通过 [paddle.seed](../api/paddle/seed_cn.html) 和 [paddle.rand](../api/paddle/rand_cn.html) 组合实现。 +> * **创建一个空 Tensor**,即根据 shape 和 dtype 创建尚未初始化元素值的 Tensor,可通过 [paddle.empty](../../../api/paddle/empty_cn.html) 实现。 +> * **创建一个与其他 Tensor 具有相同 shape 与 dtype 的 Tensor**,可通过 [paddle.ones_like](../../../api/paddle/ones_like_cn.html) 、 [paddle.zeros_like](../../../api/paddle/zeros_like_cn.html) 、 [paddle.full_like](../../../api/paddle/full_like_cn.html) 、[paddle.empty_like](../../../api/paddle/empty_like_cn.html) 实现。 +> * **拷贝并创建一个与其他 Tensor 完全相同的 Tensor**,可通过 [paddle.clone](../../../api/paddle/clone_cn.html)实现。 +> * **创建一个满足特定分布的Tensor**,如[paddle.rand](../../../api/paddle/rand_cn.html), [paddle.randn](../../../api/paddle/randn_cn.html) , [paddle.randint](../../../api/paddle/randint_cn.html)等。 +> * **通过设置随机种子创建 Tensor**,可每次生成相同元素值的随机数 Tensor,可通过 [paddle.seed](../../../api/paddle/seed_cn.html) 和 [paddle.rand](../../../api/paddle/rand_cn.html) 组合实现。 ### 1.4 通过 Numpy 数组创建 -通过给定 Numpy 数组,可使用 [paddle.to_tensor](../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 +通过给定 Numpy 数组,可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 ```python tensor_temp = paddle.to_tensor(np.array([1.0, 2.0])) print(tensor_temp) @@ -187,7 +190,7 @@ print(tensor_temp) Tensor(shape=[2], dtype=float64, place=Place(gpu:0), stop_gradient=True, [1., 2.]) ``` -飞桨也支持将 Tensor 转换为 Numpy 数组,可通过 [Tensor.numpy](../api/paddle/Tensor_cn.html#numpy) 方法实现。 +飞桨也支持将 Tensor 转换为 Numpy 数组,可通过 [Tensor.numpy](../../../api/paddle/Tensor_cn.html#numpy) 方法实现。 ```python tensor_to_convert = paddle.to_tensor([1.,2.]) tensor_to_convert.numpy() @@ -207,8 +210,8 @@ array([1., 2.], dtype=float32) 在常见深度学习任务中,数据样本可能是图片(image)、文本(text)、语音(audio)等多种类型,在送入神经网络训练或推理前,这些数据和对应的标签均需要创建为 Tensor。以下是图像场景和 NLP 场景中手动转换 Tensor 方法的介绍。 -* 对于图像场景,可使用 [paddle.vision.transforms.ToTensor](../api/paddle/vision/transforms/ToTensor_cn.html) 直接将 PIL.Image 格式的数据转为 Tensor,使用 [paddle.to_tensor](../api/paddle/to_tensor_cn.html) 将图像的标签(Label,通常是Python 或 Numpy 格式的数据)转为 Tensor。 -* 对于文本场景,需将文本数据解码为数字后,再通过 [paddle.to_tensor](../api/paddle/to_tensor_cn.html) 转为 Tensor。不同文本任务标签形式不一样,有的任务标签也是文本,有的则是数字,均需最终通过 paddle.to_tensor 转为 Tensor。 +* 对于图像场景,可使用 [paddle.vision.transforms.ToTensor](../../../api/paddle/vision/transforms/ToTensor_cn.html) 直接将 PIL.Image 格式的数据转为 Tensor,使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 将图像的标签(Label,通常是Python 或 Numpy 格式的数据)转为 Tensor。 +* 对于文本场景,需将文本数据解码为数字后,再通过 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 转为 Tensor。不同文本任务标签形式不一样,有的任务标签也是文本,有的则是数字,均需最终通过 paddle.to_tensor 转为 Tensor。 下面以图像场景为例介绍,以下示例代码中将随机生成的图片转换为 Tensor。 @@ -232,11 +235,11 @@ Tensor(shape=[3, 224, 224], dtype=float32, place=Place(gpu:0), stop_gradient=Tru ``` > **说明:** > ->实际编码时,由于飞桨数据加载的 [paddle.io.DataLoader](../api/paddle/io/DataLoader_cn.html) API 能够将原始 [paddle.io.Dataset](../api/paddle/io/Dataset_cn.html) 定义的数据自动转换为 Tensor,所以可以不做手动转换。具体如下节介绍。 +>实际编码时,由于飞桨数据加载的 [paddle.io.DataLoader](../../../api/paddle/io/DataLoader_cn.html) API 能够将原始 [paddle.io.Dataset](../../../api/paddle/io/Dataset_cn.html) 定义的数据自动转换为 Tensor,所以可以不做手动转换。具体如下节介绍。 ### 1.6 自动创建 Tensor 的功能介绍 -除了手动创建 Tensor 外,实际在飞桨框架中有一些 API 封装了 Tensor 创建的操作,从而无需用户手动创建 Tensor。例如 [paddle.io.DataLoader](../api/paddle/io/DataLoader_cn.html) 能够基于原始 Dataset,返回读取 Dataset 数据的迭代器,迭代器返回的数据中的每个元素都是一个 Tensor。另外在一些高层API,如 [paddle.Model.fit](../api/paddle/Model_cn.html) 、[paddle.Model.predict](../api/paddle/Model_cn.html) ,如果传入的数据不是 Tensor,会自动转为 Tensor 再进行模型训练或推理。 +除了手动创建 Tensor 外,实际在飞桨框架中有一些 API 封装了 Tensor 创建的操作,从而无需用户手动创建 Tensor。例如 [paddle.io.DataLoader](../../../api/paddle/io/DataLoader_cn.html) 能够基于原始 Dataset,返回读取 Dataset 数据的迭代器,迭代器返回的数据中的每个元素都是一个 Tensor。另外在一些高层API,如 [paddle.Model.fit](../../../api/paddle/Model_cn.html) 、[paddle.Model.predict](../../../api/paddle/Model_cn.html) ,如果传入的数据不是 Tensor,会自动转为 Tensor 再进行模型训练或推理。 > **说明:** > > paddle.Model.fit、paddle.Model.predict 等高层 API 支持传入 Dataset 或 DataLoader,如果传入的是 Dataset,那么会用 DataLoader 封装转为 Tensor 数据;如果传入的是 DataLoader,则直接从 DataLoader 迭代读取 Tensor 数据送入模型训练或推理。因此即使没有写将数据转为 Tensor 的代码,也能正常执行,提升了编程效率和容错性。 @@ -276,7 +279,7 @@ Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True, ### 2.1 Tensor的形状(shape) **(1)形状的介绍** -形状是 Tensor 的一个重要的基础属性,可以通过 [Tensor.shape](../api/paddle/Tensor_cn.html#shape) 查看一个 Tensor 的形状,以下为相关概念: +形状是 Tensor 的一个重要的基础属性,可以通过 [Tensor.shape](../../../api/paddle/Tensor_cn.html#shape) 查看一个 Tensor 的形状,以下为相关概念: * shape:描述了 Tensor 每个维度上元素的数量。 * ndim: Tensor 的维度数量,例如向量的维度为 1,矩阵的维度为2,Tensor 可以有任意数量的维度。 @@ -308,7 +311,7 @@ Elements number along axis 0 of Tensor: 2 Elements number along the last axis of Tensor: 5 ``` **(2)重置 Tensor 形状(Reshape) 的方法** -重新设置 Tensor 的 shape 在深度学习任务中比较常见,如一些计算类 API 会对输入数据有特定的形状要求,这时可通过 [paddle.reshape](../api/paddle/reshape_cn.html) 接口来改变 Tensor 的 shape,但并不改变 Tensor 的 size 和其中的元素数据。 +重新设置 Tensor 的 shape 在深度学习任务中比较常见,如一些计算类 API 会对输入数据有特定的形状要求,这时可通过 [paddle.reshape](../../../api/paddle/reshape_cn.html) 接口来改变 Tensor 的 shape,但并不改变 Tensor 的 size 和其中的元素数据。 以下示例代码中,创建 1 个 `shape=[3]` 的一维 Tensor,使用 reshape 功能将该 Tensor 重置为 `shape=[1, 3]` 的二维 Tensor。这种做法经常用在把一维的标签(label)数据扩展为二维,由于飞桨框架中神经网络通常需要传入一个 batch 的数据进行计算,因此可将数据增加一个 batch 维,方便后面的数据计算。 @@ -347,14 +350,14 @@ Tensor flattened to Vector: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 > **说明:** > > 除了 paddle.reshape 可重置 Tensor 的形状,还可通过如下方法改变 shape: -> * [paddle.squeeze](../api/paddle/squeeze_cn.html),可实现 Tensor 的降维操作,即把 Tensor 中尺寸为 1 的维度删除。 -> * [paddle.unsqueeze](../api/paddle/unsqueeze_cn.html),可实现 Tensor 的升维操作,即向 Tensor 中某个位置插入尺寸为 1 的维度。 -> * [paddle.flatten](../api/paddle/flatten_cn.html),将 Tensor 的数据在指定的连续维度上展平。 -> * [transpose](../api/paddle/transpose_cn.html),对 Tensor 的数据进行重排。 +> * [paddle.squeeze](../../../api/paddle/squeeze_cn.html),可实现 Tensor 的降维操作,即把 Tensor 中尺寸为 1 的维度删除。 +> * [paddle.unsqueeze](../../../api/paddle/unsqueeze_cn.html),可实现 Tensor 的升维操作,即向 Tensor 中某个位置插入尺寸为 1 的维度。 +> * [paddle.flatten](../../../api/paddle/flatten_cn.html),将 Tensor 的数据在指定的连续维度上展平。 +> * [transpose](../../../api/paddle/transpose_cn.html),对 Tensor 的数据进行重排。 **(3)原位(Inplace)操作和非原位操作的区别** -飞桨框架的 API 有原位(Inplace)操作和非原位操作之分,原位操作即在原 Tensor 上保存操作结果,输出 Tensor 将与输入Tensor 共享数据,并且没有 Tensor 数据拷贝的过程。非原位操作则不会修改原 Tensor,而是返回一个新的 Tensor。通过 API 名称区分两者,如 [paddle.reshape](../api/paddle/reshape_cn.html) 是非原位操作,[paddle.reshape_](../api/paddle/reshape__cn.html) 是原位操作。 +飞桨框架的 API 有原位(Inplace)操作和非原位操作之分,原位操作即在原 Tensor 上保存操作结果,输出 Tensor 将与输入Tensor 共享数据,并且没有 Tensor 数据拷贝的过程。非原位操作则不会修改原 Tensor,而是返回一个新的 Tensor。通过 API 名称区分两者,如 [paddle.reshape](../../../api/paddle/reshape_cn.html) 是非原位操作,[paddle.reshape_](../../../api/paddle/reshape__cn.html) 是原位操作。 下面以 reshape 为例说明,通过对比Tensor的 name (每个 Tensor 创建时都会有一个独一无二的 name),判断是否为同一个Tensor。 ```python @@ -373,13 +376,14 @@ same_tensor name: generated_tensor_0 # 原位操作后产生的Tensor与原始T ### 2.2 Tensor的数据类型(dtype) **(1)指定数据类型的介绍** -Tensor 的数据类型 dtype 可以通过 [Tensor.dtype](../api/paddle/Tensor_cn.html#dtype) 查看,支持类型包括:`bool`、`float16`、`float32`、`float64`、`uint8`、`int8`、`int16`、`int32`、`int64`、`complex64`、`complex128`。 +Tensor 的数据类型 dtype 可以通过 [Tensor.dtype](../../../api/paddle/Tensor_cn.html#dtype) 查看,支持类型包括:`bool`、`float16`、`float32`、`float64`、`uint8`、`int8`、`int16`、`int32`、`int64`、`complex64`、`complex128`。 同一 Tensor 中所有元素的数据类型均相同,通常通过如下方式指定: * 通过给定 Python 序列创建的 Tensor,可直接使用 dtype 参数指定。如果未指定: - * 对于 Python 整型数据,默认会创建 `int64` 型 Tensor; - * 对于 Python 浮点型数据,默认会创建 `float32` 型 Tensor,并且可以通过 [paddle.set_default_dtype](../api/paddle/set_default_dtype_cn.html) 来调整浮点型数据的默认类型。 + + * 对于 Python 整型数据,默认会创建 `int64` 型 Tensor; + * 对于 Python 浮点型数据,默认会创建 `float32` 型 Tensor,并且可以通过 [paddle.set_default_dtype](../../../api/paddle/set_default_dtype_cn.html) 来调整浮点型数据的默认类型。 ```python # 创建Tensor时指定dtype ndim_1_tensor = paddle.to_tensor([2.0, 3.0, 4.0], dtype='float64') @@ -409,7 +413,7 @@ Tensor(shape=[2, 2], dtype=complex64, place=Place(gpu:0), stop_gradient=True, ``` **(2)修改数据类型的方法** -飞桨框架提供了[paddle.cast](../api/paddle/cast_cn.html) 接口来改变 Tensor 的 dtype: +飞桨框架提供了[paddle.cast](../../../api/paddle/cast_cn.html) 接口来改变 Tensor 的 dtype: ```python float32_Tensor = paddle.to_tensor(1.0) @@ -424,11 +428,11 @@ Tensor after cast to float64: paddle.float64 Tensor after cast to int64: paddle.int64 ``` ### 2.3 Tensor的设备位置(place) -初始化 Tensor 时可以通过 [Tensor.place](../api/paddle/Tensor_cn.html#place) 来指定其分配的设备位置,可支持的设备位置有:CPU、GPU、固定内存、XPU(Baidu Kunlun)、NPU(Huawei)、MLU(寒武纪)、IPU(Graphcore)等。其中固定内存也称为不可分页内存或锁页内存,其与 GPU 之间具有更高的读写效率,并且支持异步传输,这对网络整体性能会有进一步提升,但其缺点是分配空间过多时可能会降低主机系统的性能,因为其减少了用于存储虚拟内存数据的可分页内存。 +初始化 Tensor 时可以通过 [Tensor.place](../../../api/paddle/Tensor_cn.html#place) 来指定其分配的设备位置,可支持的设备位置有:CPU、GPU、固定内存、XPU(Baidu Kunlun)、NPU(Huawei)、MLU(寒武纪)、IPU(Graphcore)等。其中固定内存也称为不可分页内存或锁页内存,其与 GPU 之间具有更高的读写效率,并且支持异步传输,这对网络整体性能会有进一步提升,但其缺点是分配空间过多时可能会降低主机系统的性能,因为其减少了用于存储虚拟内存数据的可分页内存。 > **说明:** > -> * 当未指定 place 时,Tensor 默认设备位置和安装的飞桨框架版本一致。如安装了 GPU 版本的飞桨,则设备位置默认为 GPU,即 Tensor 的`place` 默认为 [paddle.CUDAPlace](../api/paddle/CUDAPlace_cn.html)。 -> * 使用 [paddle.device.set_device](../api/paddle/device/set_device_cn.html) 可设置全局默认的设备位置。Tensor.place 的指定值优先级高于全局默认值。 +> * 当未指定 place 时,Tensor 默认设备位置和安装的飞桨框架版本一致。如安装了 GPU 版本的飞桨,则设备位置默认为 GPU,即 Tensor 的`place` 默认为 [paddle.CUDAPlace](../../../api/paddle/CUDAPlace_cn.html)。 +> * 使用 [paddle.device.set_device](../../../api/paddle/device/set_device_cn.html) 可设置全局默认的设备位置。Tensor.place 的指定值优先级高于全局默认值。 以下示例分别创建了CPU、GPU和固定内存上的 Tensor,并通过 `Tensor.place` 查看 Tensor 所在的设备位置: @@ -473,7 +477,7 @@ print("Tensor name:", paddle.to_tensor(1).name) Tensor name: generated_tensor_0 ``` ### 2.5 Tensor 的 stop_gradient 属性 -stop_gradient 表示是否停止计算梯度,默认值为 True,表示停止计算梯度,梯度不再回传。可以直接设置 stop_gradient 的值。 +stop_gradient 表示是否停止计算梯度,默认值为 True,表示停止计算梯度,梯度不再回传。在设计网络时,如不需要对某些参数进行训练更新,可以将参数的stop_gradient设置为True。可以直接设置 stop_gradient 的值。 ```python eg = paddle.to_tensor(1) From aaa6c983fec7633cad8154e0afc356217a923efc Mon Sep 17 00:00:00 2001 From: moguguo Date: Thu, 31 Mar 2022 16:53:03 +0800 Subject: [PATCH 04/17] fix some md format bugs --- .../basic_concept/tensor_introduction_cn.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index cf612d9c629..164346ce363 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -175,8 +175,8 @@ Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True, > 除了以上指定数据、形状、区间创建 Tensor 的方法,飞桨还支持如下类似的创建方式,如: > * **创建一个空 Tensor**,即根据 shape 和 dtype 创建尚未初始化元素值的 Tensor,可通过 [paddle.empty](../../../api/paddle/empty_cn.html) 实现。 > * **创建一个与其他 Tensor 具有相同 shape 与 dtype 的 Tensor**,可通过 [paddle.ones_like](../../../api/paddle/ones_like_cn.html) 、 [paddle.zeros_like](../../../api/paddle/zeros_like_cn.html) 、 [paddle.full_like](../../../api/paddle/full_like_cn.html) 、[paddle.empty_like](../../../api/paddle/empty_like_cn.html) 实现。 -> * **拷贝并创建一个与其他 Tensor 完全相同的 Tensor**,可通过 [paddle.clone](../../../api/paddle/clone_cn.html)实现。 -> * **创建一个满足特定分布的Tensor**,如[paddle.rand](../../../api/paddle/rand_cn.html), [paddle.randn](../../../api/paddle/randn_cn.html) , [paddle.randint](../../../api/paddle/randint_cn.html)等。 +> * **拷贝并创建一个与其他 Tensor 完全相同的 Tensor**,可通过 [paddle.clone](../../../api/paddle/clone_cn.html) 实现。 +> * **创建一个满足特定分布的Tensor**,如 [paddle.rand](../../../api/paddle/rand_cn.html), [paddle.randn](../../../api/paddle/randn_cn.html) , [paddle.randint](../../../api/paddle/randint_cn.html) 等。 > * **通过设置随机种子创建 Tensor**,可每次生成相同元素值的随机数 Tensor,可通过 [paddle.seed](../../../api/paddle/seed_cn.html) 和 [paddle.rand](../../../api/paddle/rand_cn.html) 组合实现。 ### 1.4 通过 Numpy 数组创建 @@ -204,6 +204,7 @@ array([1., 2.], dtype=float32) > 虽然飞桨框架中 Tensor 可以与 Numpy 数组方便地互相转换,但在实际应用中两者频繁转换会存在性能消耗,具体使用场景说明如下: > * 场景一:在组网程序中,对网络中向量的处理,务必使用 Tensor,而不建议转成 Numpy 数组。如果在组网过程中转成Numpy 数组,并使用 Numpy 的函数会降低整体性能。 > * 场景二:在数据处理和模型后处理等场景,也建议优先使用 Tensor,主要是飞桨为 AI 硬件做了大量的适配和性能优化工作,部分情况下会获得更好的使用体验和性能。 +> > 目前飞桨的 Tensor 基本覆盖 Numpy 数组的操作并有所加强,所以推荐在程序中优先使用 Tensor 完成各种数据处理和组网操作。 ### 1.5 指定图像、文本数据创建 @@ -279,6 +280,7 @@ Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True, ### 2.1 Tensor的形状(shape) **(1)形状的介绍** + 形状是 Tensor 的一个重要的基础属性,可以通过 [Tensor.shape](../../../api/paddle/Tensor_cn.html#shape) 查看一个 Tensor 的形状,以下为相关概念: * shape:描述了 Tensor 每个维度上元素的数量。 @@ -311,6 +313,7 @@ Elements number along axis 0 of Tensor: 2 Elements number along the last axis of Tensor: 5 ``` **(2)重置 Tensor 形状(Reshape) 的方法** + 重新设置 Tensor 的 shape 在深度学习任务中比较常见,如一些计算类 API 会对输入数据有特定的形状要求,这时可通过 [paddle.reshape](../../../api/paddle/reshape_cn.html) 接口来改变 Tensor 的 shape,但并不改变 Tensor 的 size 和其中的元素数据。 @@ -376,6 +379,7 @@ same_tensor name: generated_tensor_0 # 原位操作后产生的Tensor与原始T ### 2.2 Tensor的数据类型(dtype) **(1)指定数据类型的介绍** + Tensor 的数据类型 dtype 可以通过 [Tensor.dtype](../../../api/paddle/Tensor_cn.html#dtype) 查看,支持类型包括:`bool`、`float16`、`float32`、`float64`、`uint8`、`int8`、`int16`、`int32`、`int64`、`complex64`、`complex128`。 同一 Tensor 中所有元素的数据类型均相同,通常通过如下方式指定: @@ -477,7 +481,7 @@ print("Tensor name:", paddle.to_tensor(1).name) Tensor name: generated_tensor_0 ``` ### 2.5 Tensor 的 stop_gradient 属性 -stop_gradient 表示是否停止计算梯度,默认值为 True,表示停止计算梯度,梯度不再回传。在设计网络时,如不需要对某些参数进行训练更新,可以将参数的stop_gradient设置为True。可以直接设置 stop_gradient 的值。 +stop_gradient 表示是否停止计算梯度,默认值为 True,表示停止计算梯度,梯度不再回传。在设计网络时,如不需要对某些参数进行训练更新,可以将参数的stop_gradient设置为True。可参考以下代码直接设置 stop_gradient 的值。 ```python eg = paddle.to_tensor(1) From 0d82b47fbb72ffa6fbce4e8e375fc3d3435f2328 Mon Sep 17 00:00:00 2001 From: moguguo Date: Thu, 31 Mar 2022 17:37:04 +0800 Subject: [PATCH 05/17] Update tensor images links --- .../basic_concept/tensor_introduction_cn.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 164346ce363..be8daecfa9b 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -117,7 +117,7 @@ Tensor(shape=[2, 2, 5], dtype=int64, place=Place(gpu:0), stop_gradient=True, 上述不同维度的 Tensor 可视化的表示如下图所示:
- +
图1 不同维度的Tensor可视化表示
@@ -294,7 +294,7 @@ ndim_4_Tensor = paddle.ones([2, 3, 4, 5]) ```
- +
图2 Tensor的shape、axis、dimension、ndim之间的关系
@@ -751,7 +751,7 @@ print(z.shape) ```
- +
图3 Tensor 广播示例
From f455fdfe51574cce7ac194121a72f5c081ef387b Mon Sep 17 00:00:00 2001 From: moguguo Date: Thu, 31 Mar 2022 18:11:14 +0800 Subject: [PATCH 06/17] fix images link bugs --- .../basic_concept/tensor_introduction_cn.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index be8daecfa9b..211de4e4ead 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -117,7 +117,7 @@ Tensor(shape=[2, 2, 5], dtype=int64, place=Place(gpu:0), stop_gradient=True, 上述不同维度的 Tensor 可视化的表示如下图所示:
- +
图1 不同维度的Tensor可视化表示
@@ -294,7 +294,7 @@ ndim_4_Tensor = paddle.ones([2, 3, 4, 5]) ```
- +
图2 Tensor的shape、axis、dimension、ndim之间的关系
@@ -751,7 +751,7 @@ print(z.shape) ```
- +
图3 Tensor 广播示例
From e72bddc01f7b7e92b531cee2423de173ff6afc9b Mon Sep 17 00:00:00 2001 From: moguguo Date: Thu, 31 Mar 2022 20:25:31 +0800 Subject: [PATCH 07/17] fix image link bugs --- .../basic_concept/tensor_introduction_cn.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 211de4e4ead..82f34fd5bdf 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -117,7 +117,7 @@ Tensor(shape=[2, 2, 5], dtype=int64, place=Place(gpu:0), stop_gradient=True, 上述不同维度的 Tensor 可视化的表示如下图所示:
- +
图1 不同维度的Tensor可视化表示
@@ -294,7 +294,7 @@ ndim_4_Tensor = paddle.ones([2, 3, 4, 5]) ```
- +
图2 Tensor的shape、axis、dimension、ndim之间的关系
@@ -751,7 +751,7 @@ print(z.shape) ```
- +
图3 Tensor 广播示例
From b608409a35ba083cac4005702c2d119cf176be37 Mon Sep 17 00:00:00 2001 From: moguguo Date: Thu, 31 Mar 2022 20:27:21 +0800 Subject: [PATCH 08/17] fix image link bugs --- .../basic_concept/tensor_introduction_cn.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 82f34fd5bdf..d05103c71b0 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -118,7 +118,7 @@ Tensor(shape=[2, 2, 5], dtype=int64, place=Place(gpu:0), stop_gradient=True,
-
图1 不同维度的Tensor可视化表示
+
图1 不同维度的Tensor可视化表示
需要注意的是,Tensor 必须形如矩形,即在任何一个维度上,元素的数量必须相等,否则会抛出异常,示例如下: @@ -295,7 +295,7 @@ ndim_4_Tensor = paddle.ones([2, 3, 4, 5])
-
图2 Tensor的shape、axis、dimension、ndim之间的关系
+
图2 Tensor的shape、axis、dimension、ndim之间的关系
```python @@ -752,7 +752,7 @@ print(z.shape)
-
图3 Tensor 广播示例
+
图3 Tensor 广播示例
## 六、总结 From 02b606914a8671b2112a5bc75e0cbede6d4f9070 Mon Sep 17 00:00:00 2001 From: moguguo Date: Thu, 31 Mar 2022 20:30:35 +0800 Subject: [PATCH 09/17] fix image link bugs --- .../basic_concept/tensor_introduction_cn.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index d05103c71b0..00ffc919bcc 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -116,10 +116,9 @@ Tensor(shape=[2, 2, 5], dtype=int64, place=Place(gpu:0), stop_gradient=True, ``` 上述不同维度的 Tensor 可视化的表示如下图所示: -
- + +

图1 不同维度的Tensor可视化表示
-
需要注意的是,Tensor 必须形如矩形,即在任何一个维度上,元素的数量必须相等,否则会抛出异常,示例如下: @@ -293,10 +292,9 @@ Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True, ndim_4_Tensor = paddle.ones([2, 3, 4, 5]) ``` -
- + +

图2 Tensor的shape、axis、dimension、ndim之间的关系
-
```python print("Data Type of every element:", ndim_4_Tensor.dtype) @@ -750,10 +748,9 @@ print(z.shape) # z的形状: [2,3,4],z的每一维度上的尺寸,将取x和y对应维度上尺寸的较大值,如第0维x的尺寸为2,y的尺寸为1,则z的第0维尺寸为2 ``` -
- + +

图3 Tensor 广播示例
-
## 六、总结 From eebc3fd70de8622419aa26503c01e29b6301dc85 Mon Sep 17 00:00:00 2001 From: moguguo Date: Fri, 1 Apr 2022 20:18:26 +0800 Subject: [PATCH 10/17] fix image link bugs --- .../basic_concept/tensor_introduction_cn.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 00ffc919bcc..fd0b0663462 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -117,7 +117,7 @@ Tensor(shape=[2, 2, 5], dtype=int64, place=Place(gpu:0), stop_gradient=True, 上述不同维度的 Tensor 可视化的表示如下图所示: -
+

图1 不同维度的Tensor可视化表示
需要注意的是,Tensor 必须形如矩形,即在任何一个维度上,元素的数量必须相等,否则会抛出异常,示例如下: @@ -293,7 +293,7 @@ ndim_4_Tensor = paddle.ones([2, 3, 4, 5]) ``` -
+

图2 Tensor的shape、axis、dimension、ndim之间的关系
```python @@ -749,7 +749,7 @@ print(z.shape) ``` -
+

图3 Tensor 广播示例
## 六、总结 From bd1984299f7c9f84d50dc96cc4728746277e2afb Mon Sep 17 00:00:00 2001 From: betterpig Date: Thu, 7 Apr 2022 02:48:30 +0000 Subject: [PATCH 11/17] point Tensor's necessity; add the same and difference between tensor and numpy array --- .../basic_concept/tensor_introduction_cn.md | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index fd0b0663462..1864e3198b9 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -3,7 +3,8 @@ ## 一、Tensor 的概念介绍 -飞桨使用张量([Tensor](../../../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。不同之处是 Tensor 不仅可以运行在 CPU 上,还支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;并且飞桨基于 Tensor,从数据处理、组网操作、硬件适配等各方面进行了优化,在神经网络训练、推理等任务中可获得更好的效果和使用体验。因此推荐优先使用 Tensor 完成数据处理和组网操作。 +飞桨使用张量([Tensor](../../../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。与Numpy 数组相比,Tensor 支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;此外,Paddle基于Tensor,实现了深度学习所必须的反向传播功能和多种多样的算子,从而能够进行完整的深度学习任务。 + 在飞桨框架中,神经网络的输入、输出数据,以及网络中的参数均采用 Tensor 数据结构,示例如下: ```python def train(model): @@ -198,13 +199,9 @@ tensor_to_convert.numpy() array([1., 2.], dtype=float32) ``` -> **说明:** -> -> 虽然飞桨框架中 Tensor 可以与 Numpy 数组方便地互相转换,但在实际应用中两者频繁转换会存在性能消耗,具体使用场景说明如下: -> * 场景一:在组网程序中,对网络中向量的处理,务必使用 Tensor,而不建议转成 Numpy 数组。如果在组网过程中转成Numpy 数组,并使用 Numpy 的函数会降低整体性能。 -> * 场景二:在数据处理和模型后处理等场景,也建议优先使用 Tensor,主要是飞桨为 AI 硬件做了大量的适配和性能优化工作,部分情况下会获得更好的使用体验和性能。 -> -> 目前飞桨的 Tensor 基本覆盖 Numpy 数组的操作并有所加强,所以推荐在程序中优先使用 Tensor 完成各种数据处理和组网操作。 +为了方便理解和迁移,Tensor 的很多基础操作API和numpy 在功能、用法上基本保持一致。如第二节中Tensor的指定数据、形状、区间创建,第三节中Tensor的形状、数据类型,第四节中Tensor的各种操作,以及第五届中Tensor的广播,可以很方便的在numpy中找到对应操作。 + +但是,Tensor也有一些独有的属性和操作,这是为了更好地支持深度学习任务的结果。如第一节中介绍的通过numpy数据甚至图像文本等原生数据手动或自动创建Tensor的功能,能够减轻用户数据前处理的负担。而第二节中介绍的Tensor的设备位置,则方便用户决定Tensor的运行设备。至于Tensor的stop_gradient属性,更是和神经网络的训练息息相关。 ### 1.5 指定图像、文本数据创建 @@ -258,9 +255,9 @@ test_dataset = paddle.vision.datasets.MNIST(mode='test', transform=transform) print(test_dataset[0][1]) # 打印原始数据集的第一个数据的label loader = paddle.io.DataLoader(test_dataset) for data in enumerate(loader): - x, label = data[1] - print(label) # 打印由DataLoader返回的迭代器中的第一个数据的label - break + x, label = data[1] + print(label) # 打印由DataLoader返回的迭代器中的第一个数据的label + break ``` ```text [7] # 原始数据中label为Python list @@ -337,8 +334,8 @@ After reshape: [1, 3] origin:[3, 2, 5] reshape:[3, 10] actual: [3, 10] # 直接指定目标 shape origin:[3, 2, 5] reshape:[-1] actual: [30] # 转换为1维,维度根据元素总数推断出来是3*2*5=30 origin:[3, 2, 5] reshape:[-1, 5] actual: [6, 5] # 转换为2维,固定一个维度5,另一个维度根据元素总数推断出来是30÷5=6 -origin:[3, 2, 5] reshape:[0, -1] actual: [3, 6] # reshape:[0, -1]中0的索引值为0,按照规则,转换后第0维的元素数量与原始Tensor第0维的元素数量相同,为3;第1维的元素数量根据元素总值计算得出为30÷3=10。 -origin:[3, 2] reshape:[3, 1, 0] error: # reshape:[3, 1, 0]中0的索引值为2,但原Tensor只有2维,无法找到与第3维对应的元素数量,因此出错。 +origin:[3, 2, 5] reshape:[0, -1] actual: [3, 6] # reshape:[0, -1]中0的索引值为0,按照规则,转换后第0维的元素数量与原始Tensor第0维的元素数量相同,为3;第1维的元素数量根据元素总值计算得出为30÷3=10。 +origin:[3, 2] reshape:[3, 1, 0] error: # reshape:[3, 1, 0]中0的索引值为2,但原Tensor只有2维,无法找到与第3维对应的元素数量,因此出错。 ``` 从上面的例子可以看到,通过 reshape:[-1] ,可以很方便地将 Tensor 按其在计算机上的内存分布展平为一维。 From 2f8473ae7acebaef204019d93263d2815de403c5 Mon Sep 17 00:00:00 2001 From: moguguo Date: Fri, 8 Apr 2022 10:59:26 +0800 Subject: [PATCH 12/17] Modify some descriptions. --- .../basic_concept/tensor_introduction_cn.md | 103 ++++++++++-------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 1864e3198b9..6133f814657 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -3,7 +3,7 @@ ## 一、Tensor 的概念介绍 -飞桨使用张量([Tensor](../../../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。与Numpy 数组相比,Tensor 支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;此外,Paddle基于Tensor,实现了深度学习所必须的反向传播功能和多种多样的算子,从而能够进行完整的深度学习任务。 +飞桨使用张量([Tensor](../../../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。与 Numpy 数组相比,Tensor 除了支持 CPU 还支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;此外,飞桨基于 Tensor,实现了深度学习所必须的反向传播功能和多种多样的算子,从而更利于处理深度学习任务。 在飞桨框架中,神经网络的输入、输出数据,以及网络中的参数均采用 Tensor 数据结构,示例如下: ```python @@ -45,24 +45,26 @@ weight: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=Fals 飞桨可基于给定数据手动创建 Tensor,并提供了多种方式,如: -[1.1 指定数据创建](#newtensor1) +[2.1 指定数据创建](#newtensor1) -[1.2 指定形状创建](#newtensor2) +[2.2 指定形状创建](#newtensor2) -[1.3 指定区间创建](#newtensor3) - -[1.4 通过 Numpy 数组创建](#newtensor4) +[2.3 指定区间创建](#newtensor3) 另外在常见深度学习任务中,数据样本可能是图片(image)、文本(text)、语音(audio)等多种类型,在送入神经网络训练或推理前均需要创建为 Tensor。飞桨提供了将这类数据手动创建为 Tensor 的方法,如: -[1.5 指定图像、文本数据创建](#newtensor5) +[2.4 指定图像、文本数据创建](#newtensor4) 由于这些操作在整个深度学习任务流程中比较常见且固定,飞桨在一些 API 中封装了 Tensor 自动创建的操作,从而无须手动转 Tensor。 -[1.6 自动创建 Tensor 的功能介绍](#newtensor6) +[2.5 自动创建 Tensor 的功能介绍](#newtensor5) + +如果你熟悉 Numpy,已经使用 Numpy 数组创建好数据,飞桨可以很方便地将 Numpy 数组转为 Tensor,具体介绍如: + +[六、Tensor 与 Numpy 数组相互转换](#newtensor6) -### 1.1 指定数据创建 +### 2.1 指定数据创建 与 Numpy 创建数组方式类似,通过给定 Python 序列(如列表 list、元组 tuple),可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor。示例如下: @@ -138,7 +140,7 @@ ValueError: > * 飞桨也支持将 Tensor 转换为 Python 序列数据,可通过 [paddle.tolist](../../../api/paddle/tolist_cn.html) 实现,飞桨实际的转换处理过程是 **Python 序列 <-> Numpy 数组 <-> Tensor**。 > * 基于给定数据创建 Tensor 时,飞桨是通过拷贝方式创建,与原始数据不共享内存。 -### 1.2 指定形状创建 +### 2.2 指定形状创建 如果要创建一个指定形状的 Tensor,可以使用 [paddle.zeros](../../../api/paddle/zeros_cn.html)、[paddle.ones](../../../api/paddle/ones_cn.html)、[paddle.full](../../../api/paddle/full_cn.html) 实现。 ```python @@ -154,7 +156,7 @@ Tensor(shape=[2, 3], dtype=float32, place=Place(gpu:0), stop_gradient=True, ``` -### 1.3 指定区间创建 +### 2.3 指定区间创建 如果要在指定区间内创建 Tensor,可以使用[paddle.arrange](../../../api/paddle/arrange_cn.html)、 [paddle.linspace](../../../api/paddle/linspace_cn.html) 实现。 ```python @@ -179,31 +181,9 @@ Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True, > * **创建一个满足特定分布的Tensor**,如 [paddle.rand](../../../api/paddle/rand_cn.html), [paddle.randn](../../../api/paddle/randn_cn.html) , [paddle.randint](../../../api/paddle/randint_cn.html) 等。 > * **通过设置随机种子创建 Tensor**,可每次生成相同元素值的随机数 Tensor,可通过 [paddle.seed](../../../api/paddle/seed_cn.html) 和 [paddle.rand](../../../api/paddle/rand_cn.html) 组合实现。 -### 1.4 通过 Numpy 数组创建 -通过给定 Numpy 数组,可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 -```python -tensor_temp = paddle.to_tensor(np.array([1.0, 2.0])) -print(tensor_temp) -``` -```text -Tensor(shape=[2], dtype=float64, place=Place(gpu:0), stop_gradient=True, - [1., 2.]) -``` -飞桨也支持将 Tensor 转换为 Numpy 数组,可通过 [Tensor.numpy](../../../api/paddle/Tensor_cn.html#numpy) 方法实现。 -```python -tensor_to_convert = paddle.to_tensor([1.,2.]) -tensor_to_convert.numpy() -``` -```text -array([1., 2.], dtype=float32) -``` - -为了方便理解和迁移,Tensor 的很多基础操作API和numpy 在功能、用法上基本保持一致。如第二节中Tensor的指定数据、形状、区间创建,第三节中Tensor的形状、数据类型,第四节中Tensor的各种操作,以及第五届中Tensor的广播,可以很方便的在numpy中找到对应操作。 -但是,Tensor也有一些独有的属性和操作,这是为了更好地支持深度学习任务的结果。如第一节中介绍的通过numpy数据甚至图像文本等原生数据手动或自动创建Tensor的功能,能够减轻用户数据前处理的负担。而第二节中介绍的Tensor的设备位置,则方便用户决定Tensor的运行设备。至于Tensor的stop_gradient属性,更是和神经网络的训练息息相关。 - -### 1.5 指定图像、文本数据创建 +### 2.4 指定图像、文本数据创建 在常见深度学习任务中,数据样本可能是图片(image)、文本(text)、语音(audio)等多种类型,在送入神经网络训练或推理前,这些数据和对应的标签均需要创建为 Tensor。以下是图像场景和 NLP 场景中手动转换 Tensor 方法的介绍。 @@ -234,7 +214,7 @@ Tensor(shape=[3, 224, 224], dtype=float32, place=Place(gpu:0), stop_gradient=Tru > >实际编码时,由于飞桨数据加载的 [paddle.io.DataLoader](../../../api/paddle/io/DataLoader_cn.html) API 能够将原始 [paddle.io.Dataset](../../../api/paddle/io/Dataset_cn.html) 定义的数据自动转换为 Tensor,所以可以不做手动转换。具体如下节介绍。 -### 1.6 自动创建 Tensor 的功能介绍 +### 2.5 自动创建 Tensor 的功能介绍 除了手动创建 Tensor 外,实际在飞桨框架中有一些 API 封装了 Tensor 创建的操作,从而无需用户手动创建 Tensor。例如 [paddle.io.DataLoader](../../../api/paddle/io/DataLoader_cn.html) 能够基于原始 Dataset,返回读取 Dataset 数据的迭代器,迭代器返回的数据中的每个元素都是一个 Tensor。另外在一些高层API,如 [paddle.Model.fit](../../../api/paddle/Model_cn.html) 、[paddle.Model.predict](../../../api/paddle/Model_cn.html) ,如果传入的数据不是 Tensor,会自动转为 Tensor 再进行模型训练或推理。 > **说明:** @@ -273,7 +253,7 @@ Tensor(shape=[3], dtype=float32, place=Place(gpu:0), stop_gradient=True, [2., 3., 4.]) ``` -### 2.1 Tensor的形状(shape) +### 3.1 Tensor的形状(shape) **(1)形状的介绍** @@ -372,7 +352,7 @@ new_tensor name: auto_0_ # 非原位操作后产生的Tensor与原始Tensor的 same_tensor name: generated_tensor_0 # 原位操作后产生的Tensor与原始Tensor的名称相同 ``` -### 2.2 Tensor的数据类型(dtype) +### 3.2 Tensor的数据类型(dtype) **(1)指定数据类型的介绍** Tensor 的数据类型 dtype 可以通过 [Tensor.dtype](../../../api/paddle/Tensor_cn.html#dtype) 查看,支持类型包括:`bool`、`float16`、`float32`、`float64`、`uint8`、`int8`、`int16`、`int32`、`int64`、`complex64`、`complex128`。 @@ -426,7 +406,7 @@ print("Tensor after cast to int64:", int64_Tensor.dtype) Tensor after cast to float64: paddle.float64 Tensor after cast to int64: paddle.int64 ``` -### 2.3 Tensor的设备位置(place) +### 3.3 Tensor的设备位置(place) 初始化 Tensor 时可以通过 [Tensor.place](../../../api/paddle/Tensor_cn.html#place) 来指定其分配的设备位置,可支持的设备位置有:CPU、GPU、固定内存、XPU(Baidu Kunlun)、NPU(Huawei)、MLU(寒武纪)、IPU(Graphcore)等。其中固定内存也称为不可分页内存或锁页内存,其与 GPU 之间具有更高的读写效率,并且支持异步传输,这对网络整体性能会有进一步提升,但其缺点是分配空间过多时可能会降低主机系统的性能,因为其减少了用于存储虚拟内存数据的可分页内存。 > **说明:** > @@ -465,7 +445,7 @@ print(pin_memory_Tensor.place) Place(gpu_pinned) ``` -### 2.4 Tensor的名称(name) +### 3.4 Tensor的名称(name) Tensor 的名称是其唯一的标识符,为 Python 字符串类型,查看一个 Tensor 的名称可以通过 Tensor.name 属性。默认地,在每个Tensor 创建时,会自定义一个独一无二的名称。 @@ -475,7 +455,7 @@ print("Tensor name:", paddle.to_tensor(1).name) ```text Tensor name: generated_tensor_0 ``` -### 2.5 Tensor 的 stop_gradient 属性 +### 3.5 Tensor 的 stop_gradient 属性 stop_gradient 表示是否停止计算梯度,默认值为 True,表示停止计算梯度,梯度不再回传。在设计网络时,如不需要对某些参数进行训练更新,可以将参数的stop_gradient设置为True。可参考以下代码直接设置 stop_gradient 的值。 ```python @@ -491,13 +471,13 @@ Tensor stop_gradient: False ## 四、Tensor的操作 -### 3.1 索引和切片 +### 4.1 索引和切片 通过索引或切片方式可访问或修改 Tensor。飞桨框架使用标准的 Python 索引规则与 Numpy 索引规则,与 [Indexing a list or a string in Python](https://docs.python.org/3/tutorial/introduction.html#strings) 类似。具有以下特点: 1. 基于 0-n 的下标进行索引,如果下标为负数,则从尾部开始计算。 2. 通过冒号 ``:`` 分隔切片参数,``start:stop:step`` 来进行切片操作,其中 start、stop、step 均可缺省。 -#### 3.1.1 访问 Tensor +#### 4.1.1 访问 Tensor * 针对一维 Tensor,仅有单个维度上的索引或切片: ```python ndim_1_Tensor = paddle.to_tensor([0, 1, 2, 3, 4, 5, 6, 7, 8]) @@ -563,7 +543,7 @@ Tensor(shape=[4], dtype=int64, place=Place(gpu:0), stop_gradient=True, [4, 5, 6, 7]) ``` -#### 3.1.2 修改 Tensor +#### 4.1.2 修改 Tensor 与访问 Tensor 类似,修改 Tensor 可以在单个或多个维度上通过索引或切片操作。同时,支持将多种类型的数据赋值给该 Tensor,当前支持的数据类型有:`int`,`float`,`numpy.ndarray`,`omplex`,`Tensor`。 > **注意:** @@ -607,7 +587,7 @@ Tensor(shape=[2, 2], dtype=float64, place=Place(gpu:0), stop_gradient=True, 可以看出,使用 **Tensor 类成员函数** 和 **Paddle API** 具有相同的效果,由于 **类成员函数** 操作更为方便,以下均从 **Tensor 类成员函数** 的角度,对常用 Tensor 操作进行介绍。 -### 3.2 数学运算 +### 4.2 数学运算 ```python x.abs() #逐元素取绝对值 x.ceil() #逐元素向上取整 @@ -642,7 +622,7 @@ x % y -> x.mod(y) #逐元素相除并取余 x ** y -> x.pow(y) #逐元素幂运算 ``` -### 3.3 逻辑运算 +### 4.3 逻辑运算 ```python x.isfinite() #判断Tensor中元素是否是有限的数字,即不包括inf与nan x.equal_all(y) #判断两个Tensor的全部元素是否相等,并返回形状为[1]的布尔类Tensor @@ -673,7 +653,7 @@ x.logical_xor(y) #对两个布尔类型Tensor逐元素进行逻辑 x.logical_not(y) #对两个布尔类型Tensor逐元素进行逻辑非操作 ``` -### 3.4 线性代数 +### 4.4 线性代数 ```python x.t() #矩阵转置 x.transpose([1, 0]) #交换第 0 维与第 1 维的顺序 @@ -749,6 +729,35 @@ print(z.shape)

图3 Tensor 广播示例
-## 六、总结 +## 六、Tensor 与 Numpy 数组相互转换 + +如果你已熟悉 Numpy,通过以下要点,可以方便地理解和迁移到 Tensor 的使用上: +* Tensor 的很多基础操作 API 和 Numpy 在功能、用法上基本保持一致。如前文第二节中指定数据、形状、区间创建 Tensor,第三节中 Tensor 的形状、数据类型属性,第四节中 Tensor 的各种操作,以及第五节中 Tensor 的广播,可以很方便地在 Numpy 中找到相似操作。 +* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文第一节中通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,第二节中的 Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 等各种 AI 加速设备上,第二节中的 Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 + +如果已有 Numpy 数组,可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 +```python +tensor_temp = paddle.to_tensor(np.array([1.0, 2.0])) +print(tensor_temp) +``` +```text +Tensor(shape=[2], dtype=float64, place=Place(gpu:0), stop_gradient=True, + [1., 2.]) +``` +> **说明:** +> +> * 基于 Numpy 数组创建 Tensor 时,飞桨是通过拷贝方式创建,与原始数据不共享内存。 + + +相对应地,飞桨也支持将 Tensor 转换为 Numpy 数组,可通过 [Tensor.numpy](../../../api/paddle/Tensor_cn.html#numpy) 方法实现。 +```python +tensor_to_convert = paddle.to_tensor([1.,2.]) +tensor_to_convert.numpy() +``` +```text +array([1., 2.], dtype=float32) +``` + +## 七、总结 Tensor 作为飞桨框架中重要的数据结构,具有丰富的 API 用以对 Tensor 进行创建、访问、修改、计算等一系列操作,从而满足深度学习任务的需要。更多 Tensor 相关的介绍,请参考 [paddle.Tensor](../../../api/paddle/Tensor_cn.html) API 文档。 From 2d13983ad34fd0de9b5964e89669f8579c9f5980 Mon Sep 17 00:00:00 2001 From: moguguo Date: Fri, 8 Apr 2022 11:05:14 +0800 Subject: [PATCH 13/17] Update tensor_introduction_cn.md --- .../basic_concept/tensor_introduction_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index 6133f814657..a7ca2f15539 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -733,7 +733,7 @@ print(z.shape) 如果你已熟悉 Numpy,通过以下要点,可以方便地理解和迁移到 Tensor 的使用上: * Tensor 的很多基础操作 API 和 Numpy 在功能、用法上基本保持一致。如前文第二节中指定数据、形状、区间创建 Tensor,第三节中 Tensor 的形状、数据类型属性,第四节中 Tensor 的各种操作,以及第五节中 Tensor 的广播,可以很方便地在 Numpy 中找到相似操作。 -* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文第一节中通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,第二节中的 Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 等各种 AI 加速设备上,第二节中的 Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 +* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文第一节中通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,第三节中的 Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 等各种 AI 加速设备上,第三节中的 Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 如果已有 Numpy 数组,可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 ```python From 262f23ff47e8fbaeb3bb38212bc7981a77cf6acd Mon Sep 17 00:00:00 2001 From: moguguo Date: Fri, 8 Apr 2022 11:13:43 +0800 Subject: [PATCH 14/17] Update tensor_introduction_cn.md --- .../basic_concept/tensor_introduction_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index a7ca2f15539..c59e7dc52aa 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -733,7 +733,7 @@ print(z.shape) 如果你已熟悉 Numpy,通过以下要点,可以方便地理解和迁移到 Tensor 的使用上: * Tensor 的很多基础操作 API 和 Numpy 在功能、用法上基本保持一致。如前文第二节中指定数据、形状、区间创建 Tensor,第三节中 Tensor 的形状、数据类型属性,第四节中 Tensor 的各种操作,以及第五节中 Tensor 的广播,可以很方便地在 Numpy 中找到相似操作。 -* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文第一节中通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,第三节中的 Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 等各种 AI 加速设备上,第三节中的 Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 +* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文第二节中通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,第三节中的 Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 等各种 AI 加速设备上,第三节中的 Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 如果已有 Numpy 数组,可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 ```python From 639b13cf0f243145f59c39f3822d98370c4f1349 Mon Sep 17 00:00:00 2001 From: moguguo Date: Mon, 11 Apr 2022 09:43:40 +0800 Subject: [PATCH 15/17] Update tensor_introduction_cn.md --- .../basic_concept/tensor_introduction_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index c59e7dc52aa..dcc1c91238d 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -732,8 +732,8 @@ print(z.shape) ## 六、Tensor 与 Numpy 数组相互转换 如果你已熟悉 Numpy,通过以下要点,可以方便地理解和迁移到 Tensor 的使用上: -* Tensor 的很多基础操作 API 和 Numpy 在功能、用法上基本保持一致。如前文第二节中指定数据、形状、区间创建 Tensor,第三节中 Tensor 的形状、数据类型属性,第四节中 Tensor 的各种操作,以及第五节中 Tensor 的广播,可以很方便地在 Numpy 中找到相似操作。 -* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文第二节中通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,第三节中的 Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 等各种 AI 加速设备上,第三节中的 Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 +* Tensor 的很多基础操作 API 和 Numpy 在功能、用法上基本保持一致。如前文中介绍的指定数据、形状、区间创建 Tensor,Tensor 的形状、数据类型属性,Tensor 的各种操作,以及 Tensor 的广播,可以很方便地在 Numpy 中找到相似操作。 +* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文中介绍的通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 等各种 AI 加速设备上,Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 如果已有 Numpy 数组,可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 ```python From f98ec4e488ee2864df9daae0375a055406a88981 Mon Sep 17 00:00:00 2001 From: moguguo Date: Mon, 11 Apr 2022 10:20:31 +0800 Subject: [PATCH 16/17] Update tensor_introduction_cn.md --- .../basic_concept/tensor_introduction_cn.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index dcc1c91238d..c8c4fbcdb05 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -3,7 +3,7 @@ ## 一、Tensor 的概念介绍 -飞桨使用张量([Tensor](../../../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。与 Numpy 数组相比,Tensor 除了支持 CPU 还支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;此外,飞桨基于 Tensor,实现了深度学习所必须的反向传播功能和多种多样的算子,从而更利于处理深度学习任务。 +飞桨使用张量([Tensor](../../../api/paddle/Tensor_cn.html)) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 [Numpy 数组(ndarray)](https://numpy.org/doc/stable/user/quickstart.html#the-basics) 的概念。与 Numpy 数组相比,Tensor 除了支持运行在 CPU 上,还支持运行在 GPU 及各种 AI 芯片上,以实现计算加速;此外,飞桨基于 Tensor,实现了深度学习所必须的反向传播功能和多种多样的组网算子,从而可更快捷地实现深度学习组网与训练等功能。两者具体异同点可参见下文 [Tensor 与 Numpy 数组相互转换](#newtensor6)。 在飞桨框架中,神经网络的输入、输出数据,以及网络中的参数均采用 Tensor 数据结构,示例如下: ```python @@ -744,7 +744,7 @@ print(tensor_temp) Tensor(shape=[2], dtype=float64, place=Place(gpu:0), stop_gradient=True, [1., 2.]) ``` -> **说明:** +> **注意:** > > * 基于 Numpy 数组创建 Tensor 时,飞桨是通过拷贝方式创建,与原始数据不共享内存。 From 240fa422626f0d94fac3932f34dce0f44b7b37c3 Mon Sep 17 00:00:00 2001 From: moguguo Date: Tue, 12 Apr 2022 11:43:12 +0800 Subject: [PATCH 17/17] Update tensor_introduction_cn.md --- .../basic_concept/tensor_introduction_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md index c8c4fbcdb05..571c9d4b676 100644 --- a/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md +++ b/docs/guides/01_paddle2.0_introduction/basic_concept/tensor_introduction_cn.md @@ -733,7 +733,7 @@ print(z.shape) 如果你已熟悉 Numpy,通过以下要点,可以方便地理解和迁移到 Tensor 的使用上: * Tensor 的很多基础操作 API 和 Numpy 在功能、用法上基本保持一致。如前文中介绍的指定数据、形状、区间创建 Tensor,Tensor 的形状、数据类型属性,Tensor 的各种操作,以及 Tensor 的广播,可以很方便地在 Numpy 中找到相似操作。 -* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文中介绍的通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 等各种 AI 加速设备上,Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 +* 但是,Tensor 也有一些独有的属性和操作,而 Numpy 中没有对应概念或功能,这是为了更好地支持深度学习任务。如前文中介绍的通过图像、文本等原始数据手动或自动创建 Tensor 的功能,能够更便捷地处理数据,Tensor 的设备位置属性,可以很方便地将 Tensor 迁移到 GPU 或各种 AI 加速硬件上,Tensor 的 stop_gradient 属性,也是 Tensor 独有的,以便更好地支持深度学习任务。 如果已有 Numpy 数组,可使用 [paddle.to_tensor](../../../api/paddle/to_tensor_cn.html) 创建任意维度的 Tensor,创建的 Tensor 与原 Numpy 数组具有相同的形状与数据类型。 ```python