概述
安装 PaddlePaddle 之后,很多初学者会直接搜索"如何训练模型"。这一步不算错,但有点早。深度学习模型训练的底层对象是 Tensor,模型输入、模型参数、预测结果、损失值和梯度,最终都会落到 Tensor 上。
所以第一个 Paddle 程序不应该急着堆网络结构,而应该先回答几个基础问题:
- 如何把 Python 列表、标量、NumPy 数组变成 Paddle
Tensor? - 如何查看
Tensor的形状、数据类型、设备位置和梯度开关? - 如何做基本数学计算、矩阵乘法和统计计算?
- 如何把
Tensor转回 NumPy,方便打印、调试或和其他 Python 库交互? - 如何判断当前程序运行在 CPU 还是 GPU?
- 如何写出一段 CPU/GPU 都能运行的最小 Paddle 程序?
这篇文章会围绕一个完整脚本展开。你可以先完整运行,再逐段拆开理解。
text
Python 数据
|
paddle.to_tensor()
|
Paddle Tensor
|
数学计算 / 矩阵计算 / 统计计算
|
查看 shape / dtype / place / numpy
|
CPU 或 GPU 设备执行
读完这篇文章,你应该能独立写出一个 Paddle Tensor 程序,并能判断它在什么设备上执行、每个 Tensor 的基本属性是什么。
前置条件:确认 Paddle 已经可用
在写第一个程序之前,建议先执行一次环境检查:
bash
python -c "import paddle; paddle.utils.run_check()"
如果输出类似下面的信息,说明 Paddle 基础安装可用:
text
PaddlePaddle is installed successfully!
你还可以查看当前版本:
bash
python -c "import paddle; print(paddle.__version__)"
本文示例按 PaddlePaddle 3.x 的常用写法组织。如果你使用的是旧版本,个别输出格式可能略有差异,但核心概念一致。
完整示例:先跑通第一个 Paddle 脚本
新建文件 first_paddle_program.py:
python
import numpy as np
import paddle
def print_tensor(name, tensor):
print(f"\n{name}")
print("-" * 60)
print(tensor)
print("shape:", tensor.shape)
print("dtype:", tensor.dtype)
print("place:", tensor.place)
print("stop_gradient:", tensor.stop_gradient)
def choose_device():
if paddle.device.is_compiled_with_cuda():
paddle.device.set_device("gpu:0")
else:
paddle.device.set_device("cpu")
return paddle.device.get_device()
def main():
paddle.seed(2026)
np.random.seed(2026)
device = choose_device()
print("Paddle version:", paddle.__version__)
print("Current device:", device)
x = paddle.to_tensor(
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]],
dtype="float32",
)
print_tensor("x: created from Python list", x)
y_np = np.array([[10, 20, 30], [40, 50, 60]], dtype="float32")
y = paddle.to_tensor(y_np)
print_tensor("y: created from NumPy array", y)
z_add = x + y
z_mul = x * 2
z_matmul = paddle.matmul(x, y, transpose_y=True)
print_tensor("z_add: x + y", z_add)
print_tensor("z_mul: x * 2", z_mul)
print_tensor("z_matmul: x @ y.T", z_matmul)
mean_value = paddle.mean(x)
row_sum = paddle.sum(x, axis=1)
col_max = paddle.max(x, axis=0)
print_tensor("mean_value: mean(x)", mean_value)
print_tensor("row_sum: sum(x, axis=1)", row_sum)
print_tensor("col_max: max(x, axis=0)", col_max)
zeros = paddle.zeros([2, 3], dtype="float32")
ones = paddle.ones([2, 3], dtype="float32")
random_normal = paddle.randn([2, 3], dtype="float32")
sequence = paddle.arange(0, 6, dtype="float32").reshape([2, 3])
print_tensor("zeros", zeros)
print_tensor("ones", ones)
print_tensor("random_normal", random_normal)
print_tensor("sequence", sequence)
result_np = z_add.numpy()
print("\nz_add converted to NumPy:")
print(type(result_np))
print(result_np)
if __name__ == "__main__":
main()
运行:
bash
python first_paddle_program.py
如果你的环境只有 CPU,输出中的设备位置通常会出现 Place(cpu)。如果你安装的是 GPU 版,并且 GPU 可用,输出中会出现类似 Place(gpu:0) 的设备信息。
这个脚本虽然不训练模型,但已经覆盖了 Paddle 入门的关键链路:
text
创建 Tensor -> 查看属性 -> 做计算 -> 做统计 -> 管理设备 -> 转 NumPy
下面逐段拆解。
Tensor 创建:从 Python 数据进入 Paddle 世界
Paddle 中最常见的 Tensor 创建方式是 paddle.to_tensor()。官方 API 当前形式如下:
python
paddle.to_tensor(data, dtype=None, place=None, stop_gradient=True)
它可以把下面几类数据变成 paddle.Tensor:
- Python 标量,例如
1、3.14 - Python
list - Python
tuple numpy.ndarray- 已有的
paddle.Tensor
从 Python 列表创建
python
import paddle
x = paddle.to_tensor(
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]],
dtype="float32",
)
print(x)
这里创建的是一个二维 Tensor,形状是 [2, 3]。你可以把它理解成一个有 2 行、3 列的矩阵。
从 NumPy 数组创建
python
import numpy as np
import paddle
arr = np.array([[1, 2], [3, 4]], dtype="float32")
x = paddle.to_tensor(arr)
print(x)
这在真实项目中非常常见。很多数据清洗、读取、预处理流程会先用 NumPy 或 Pandas 完成,进入模型前再转换成 Paddle Tensor。
从标量创建 0 维 Tensor
python
import paddle
x = paddle.to_tensor(3.14)
print(x)
print(x.shape)
标量会变成 0 维 Tensor。它不是 Python 的 float,而是 Paddle 能参与计算图、设备调度和自动微分的 Tensor。
paddle.to_tensor() 是把普通 Python/NumPy 数据接入 Paddle 计算体系的入口。
Tensor 属性:shape、dtype、place、stop_gradient
看到一个 Tensor 时,初学者不要只看它的数值,还要看它的四个关键属性。
| 属性 | 含义 | 为什么重要 |
|---|---|---|
shape |
Tensor 的形状 | 决定矩阵计算、广播、模型输入是否匹配 |
dtype |
数据类型 | 影响精度、显存、算子支持和损失计算 |
place |
设备位置 | 判断 Tensor 在 CPU、GPU 或其他设备上 |
stop_gradient |
是否阻断梯度 | 决定后续自动微分是否追踪它 |
示例:
python
import paddle
x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]], dtype="float32")
print("shape:", x.shape)
print("dtype:", x.dtype)
print("place:", x.place)
print("stop_gradient:", x.stop_gradient)
通常情况下,刚创建的普通 Tensor 默认 stop_gradient=True。这表示它默认不需要计算梯度。后续讲自动微分时,我们会专门使用 stop_gradient=False 来让某些 Tensor 参与梯度计算。
shape:先看维度再写计算
shape 是理解 Tensor 的第一入口。
python
x = paddle.to_tensor([[1, 2, 3], [4, 5, 6]])
print(x.shape)
输出形状是 [2, 3],表示 2 行 3 列。
在模型训练中,常见形状包括:
| 数据类型 | 常见 shape | 含义 |
|---|---|---|
| 单个向量 | [feature_dim] |
一个样本的特征 |
| 一批向量 | [batch_size, feature_dim] |
一批样本 |
| 灰度图像 | [batch_size, 1, height, width] |
NCHW 格式 |
| 彩色图像 | [batch_size, 3, height, width] |
RGB 三通道 |
| 文本 token | [batch_size, seq_len] |
一批文本序列 |
初学时,只要计算报错,第一反应应该是打印 shape。
dtype:不要忽略数据类型
深度学习里最常见的数据类型是 float32。标签、索引、类别 ID 常用 int64。
python
features = paddle.to_tensor([[1.0, 2.0]], dtype="float32")
labels = paddle.to_tensor([1], dtype="int64")
print(features.dtype)
print(labels.dtype)
如果把类别标签误写成浮点数,某些损失函数会直接报错;如果把模型输入误写成整数,很多连续数学运算也会不符合预期。
经验规则:
- 模型输入特征:优先用
float32 - 回归目标:通常用
float32 - 分类标签:通常用
int64 - 布尔掩码:使用
bool
place:确认数据在哪里计算
place 表示 Tensor 的设备位置。
python
import paddle
paddle.device.set_device("cpu")
x = paddle.ones([2, 3])
print(x.place)
如果当前使用 CPU,通常会看到 Place(cpu)。如果当前使用 GPU,可能看到 Place(gpu:0)。
设备位置很重要,因为不同设备上的 Tensor 不能随意混在一起计算。真实训练中,如果模型参数在 GPU,而输入数据在 CPU,就可能触发设备不一致错误或隐式拷贝开销。
常用创建方式:不只是 to_tensor
除了 paddle.to_tensor(),Paddle 还提供了一组常用的初始化函数。
| 函数 | 作用 | 示例 |
|---|---|---|
paddle.zeros() |
创建全 0 Tensor | paddle.zeros([2, 3]) |
paddle.ones() |
创建全 1 Tensor | paddle.ones([2, 3]) |
paddle.full() |
创建指定填充值 Tensor | paddle.full([2, 3], 7) |
paddle.arange() |
创建等差序列 | paddle.arange(0, 6) |
paddle.linspace() |
创建指定数量的等间隔序列 | paddle.linspace(0, 1, 5) |
paddle.randn() |
创建正态分布随机 Tensor | paddle.randn([2, 3]) |
paddle.randint() |
创建随机整数 Tensor | paddle.randint(0, 10, [2, 3]) |
示例:
python
import paddle
zeros = paddle.zeros([2, 3], dtype="float32")
ones = paddle.ones([2, 3], dtype="float32")
sevens = paddle.full([2, 3], 7, dtype="int64")
seq = paddle.arange(0, 6, dtype="float32").reshape([2, 3])
grid = paddle.linspace(0, 1, 5)
noise = paddle.randn([2, 3], dtype="float32")
print(zeros)
print(ones)
print(sevens)
print(seq)
print(grid)
print(noise)
这些函数在模型开发中很常见。例如:
- 用
zeros初始化缓存。 - 用
ones构造掩码。 - 用
arange构造位置索引。 - 用
randn生成测试输入。 - 用
full构造固定填充值。
to_tensor 适合把已有数据转成 Tensor,zeros/ones/randn/arange 适合直接按规则生成 Tensor。
基本计算:逐元素运算和矩阵乘法
Tensor 创建之后,就可以进行计算。先看逐元素运算:
python
import paddle
x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]])
y = paddle.to_tensor([[10.0, 20.0], [30.0, 40.0]])
print(x + y)
print(x - y)
print(x * y)
print(y / x)
print(x ** 2)
这些运算会逐位置进行:
text
[[1, 2], [[10, 20], [[11, 22],
[3, 4]] + [30, 40]] = [33, 44]]
标量与 Tensor 运算
python
x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]])
print(x + 1)
print(x * 10)
标量会自动广播到 Tensor 的每个位置。广播机制后面会单独讲,这里先记住:形状不同也可能能运算,但必须符合广播规则。
矩阵乘法
矩阵乘法使用 paddle.matmul():
python
import paddle
a = paddle.to_tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
b = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
c = paddle.matmul(a, b)
print(c)
print(c.shape)
这里:
a.shape是[2, 3]b.shape是[3, 2]- 结果
c.shape是[2, 2]
如果矩阵乘法报错,优先检查内侧维度是否一致:
text
[2, 3] @ [3, 2] -> [2, 2]
^ ^
这两个维度必须匹配
神经网络中的线性层、注意力计算、特征变换,本质上都会大量使用类似矩阵乘法的操作。
统计计算:mean、sum、max 和 axis
统计计算是训练中经常出现的操作。例如求损失平均值、按 batch 聚合指标、找最大类别概率。
python
import paddle
x = paddle.to_tensor(
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]],
dtype="float32",
)
print("mean:", paddle.mean(x))
print("sum all:", paddle.sum(x))
print("sum rows:", paddle.sum(x, axis=1))
print("max columns:", paddle.max(x, axis=0))
这里最值得注意的是 axis。
对于一个形状为 [2, 3] 的 Tensor:
text
axis=0:沿着第 0 维聚合,结果保留列方向,形状变成 [3]
axis=1:沿着第 1 维聚合,结果保留行方向,形状变成 [2]
可以这样理解:
text
x = [[1, 2, 3],
[4, 5, 6]]
paddle.sum(x, axis=0) -> [5, 7, 9]
paddle.sum(x, axis=1) -> [6, 15]
遇到 axis,不要死记参数,先把 Tensor 的 shape 写出来,再判断沿哪一维聚合。
NumPy 转换:调试时非常实用
Paddle Tensor 可以用 .numpy() 转为 NumPy 数组:
python
import paddle
x = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0]])
arr = x.numpy()
print(type(arr))
print(arr)
这在调试中很有用:
- 需要用 NumPy 检查结果。
- 需要把结果交给 Matplotlib 画图。
- 需要打印更熟悉的数组格式。
- 需要和其他 Python 数据处理库交互。
不过要注意:训练主流程中不要频繁 .numpy()。如果 Tensor 在 GPU 上,转 NumPy 通常意味着要把数据拷回 CPU,这会带来额外开销。调试可以用,性能敏感代码中要谨慎。
设备管理:CPU、GPU 与全局设备
Paddle 支持在 CPU、GPU、XPU、NPU、MLU 等多种设备上执行。入门阶段最常见的是 CPU 和 GPU。
查看当前设备
python
import paddle
print(paddle.device.get_device())
官方文档说明,如果没有显式设置全局设备,CUDA 可用时通常返回 gpu:0,CUDA 不可用时返回 cpu。
设置当前设备
python
import paddle
paddle.device.set_device("cpu")
print(paddle.device.get_device())
如果你安装的是 GPU 版,并且 GPU 可用:
python
import paddle
paddle.device.set_device("gpu:0")
print(paddle.device.get_device())
官方 set_device 支持类似下面的字符串:
cpugpugpu:0xpuxpu:0npunpu:0mlumlu:0
初学阶段通常只需要 cpu 和 gpu:0。
写一个 CPU/GPU 自适应选择函数
为了让代码在不同机器上都能运行,可以这样写:
python
import paddle
def choose_device():
if paddle.device.is_compiled_with_cuda():
paddle.device.set_device("gpu:0")
else:
paddle.device.set_device("cpu")
return paddle.device.get_device()
device = choose_device()
print("Current device:", device)
这段代码的逻辑是:
- 如果当前 Paddle 是 CUDA 编译版本,就使用第 0 张 GPU。
- 否则使用 CPU。
注意,is_compiled_with_cuda() 判断的是当前 Paddle 包是否具备 CUDA 能力。实际 GPU 是否能稳定运行,还依赖驱动、硬件和安装包匹配情况。第二篇安装文章中的 paddle.utils.run_check() 仍然是必要的。
全局设备只影响后续创建的 Tensor
这是一个很容易踩坑的点。
python
import paddle
paddle.device.set_device("cpu")
x = paddle.ones([2, 2])
if paddle.device.is_compiled_with_cuda():
paddle.device.set_device("gpu:0")
y = paddle.ones([2, 2])
print("x place:", x.place)
print("y place:", y.place)
set_device("gpu:0") 会影响后续创建的 Tensor,但不会自动把已经存在的 x 从 CPU 移到 GPU。也就是说:
text
先创建的 Tensor 保持原设备
后创建的 Tensor 使用新的全局设备
这也是为什么训练代码通常会在程序开始阶段就先设置设备,然后再创建模型和数据。
设备管理要尽早做,避免模型、输入和中间结果出现在不同设备上。
创建时指定 place:更精确地控制设备
除了设置全局设备,也可以在创建 Tensor 时通过 place 指定设备。
CPU 示例:
python
import paddle
x = paddle.to_tensor([1, 2, 3], place=paddle.CPUPlace())
print(x.place)
GPU 示例:
python
import paddle
if paddle.device.is_compiled_with_cuda():
x = paddle.to_tensor([1, 2, 3], place=paddle.CUDAPlace(0))
print(x.place)
一般建议:
- 简单脚本:使用
paddle.device.set_device()设置全局设备。 - 特殊场景:创建 Tensor 时用
place精确指定。 - 初学阶段:不要在同一段代码里随意混用多个设备,先保证逻辑清晰。
第一个小练习:手写一个线性计算
为了让 Tensor 计算更接近模型训练,我们写一个简单的线性函数:
y=xW+b y = xW + b y=xW+b
代码如下:
python
import paddle
paddle.device.set_device("cpu")
x = paddle.to_tensor(
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]],
dtype="float32",
)
w = paddle.to_tensor(
[[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]],
dtype="float32",
)
b = paddle.to_tensor([0.01, 0.02], dtype="float32")
y = paddle.matmul(x, w) + b
print("x shape:", x.shape)
print("w shape:", w.shape)
print("b shape:", b.shape)
print("y shape:", y.shape)
print(y)
形状关系是:
text
x: [2, 3]
w: [3, 2]
b: [2]
x @ w: [2, 2]
(x @ w) + b: [2, 2]
这里的 b 会被广播到每一行:
text
[[... , ...],
[... , ...]]
+
[0.01, 0.02]
这个例子已经非常接近神经网络里的 Linear 层。后面学习 paddle.nn.Linear 时,你会发现框架只是把参数管理、初始化和调用方式封装得更方便。
常见错误
错误一:装了多个 Python,运行环境不是安装环境
现象:
text
ModuleNotFoundError: No module named 'paddle'
排查:
bash
python -c "import sys; print(sys.executable)"
python -m pip show paddlepaddle
python -m pip show paddlepaddle-gpu
确认 python 和 pip 指向同一个虚拟环境。
错误二:矩阵乘法维度不匹配
错误写法:
python
a = paddle.randn([2, 3])
b = paddle.randn([2, 3])
c = paddle.matmul(a, b)
[2, 3] @ [2, 3] 的内侧维度 3 和 2 不一致,所以不能直接矩阵乘法。
正确写法之一:
python
c = paddle.matmul(a, b, transpose_y=True)
此时相当于:
text
[2, 3] @ [3, 2] -> [2, 2]
错误三:dtype 不符合算子要求
例如分类标签通常应该是整数类型:
python
labels = paddle.to_tensor([0, 1, 2], dtype="int64")
模型输入通常用浮点类型:
python
features = paddle.to_tensor([[0.1, 0.2]], dtype="float32")
当损失函数或模型层报 dtype 相关错误时,先打印:
python
print(tensor.dtype)
错误四:以为 set_device 会移动已有 Tensor
错误理解:
python
paddle.device.set_device("cpu")
x = paddle.ones([2, 2])
paddle.device.set_device("gpu:0")
print("x 仍然在原来的设备上")
实际情况是:x 仍然在 CPU。set_device 影响的是后续 OP 运行的全局设备,以及后续创建 Tensor 时默认使用的设备。
错误五:训练代码里频繁 numpy
.numpy() 对调试很方便,但训练循环里频繁调用会带来性能开销,尤其是 GPU 训练场景。除非确实要打印、记录或交给外部库处理,否则尽量让数据保持 Tensor 形式。
总结
这篇文章没有训练模型,但已经覆盖了 Paddle 编程的最小基础:
paddle.to_tensor():把 Python/NumPy 数据变成 Paddle Tensor。shape:描述 Tensor 的结构,是排查维度问题的第一信息。dtype:描述数据类型,影响精度、算子和损失函数。place:描述设备位置,帮助判断 CPU/GPU 执行情况。stop_gradient:描述是否阻断梯度,是后续自动微分的关键属性。paddle.matmul():完成矩阵乘法,是神经网络计算的重要基础。paddle.mean/sum/max():完成常用统计聚合。paddle.device.set_device():设置全局设备。paddle.device.get_device():查看当前全局设备。.numpy():把 Tensor 转回 NumPy,适合调试和外部库交互。