04-Paddle Tensor 基础:形状、数据类型、广播与索引

概述

上一篇文章已经跑通了第一个 Paddle 程序,知道了如何创建 Tensor、做基本计算、查看设备位置。这一篇要继续往下挖:真正写模型时,绝大多数初学错误都不是模型太复杂,而是 Tensor 的形状、数据类型、广播和索引没搞清楚。

典型问题包括:

  • 矩阵乘法报错,不知道该看哪两个维度。
  • 分类损失报错,不知道标签为什么要用 int64
  • x + b 能运行,但不理解 b 是怎么自动扩展的。
  • 切片后维度变了,后面的网络层输入对不上。
  • reshape 强行改形状,结果把 batch 维和特征维搞混。
  • 高级索引能取出数据,但不知道结果 shape 为什么是那样。

本篇文章围绕四个核心问题展开:

text 复制代码
shape:Tensor 是什么结构
dtype:Tensor 中每个元素是什么类型
broadcast:形状不同为什么还能一起计算
index:如何从 Tensor 中取出或修改局部数据

这四个概念看起来基础,但它们贯穿所有深度学习任务:图像分类、文本分类、目标检测、OCR、推荐系统和大模型训练都离不开它们。

读完这篇文章,你应该能看到一个 Tensor 的 shape 就推断出它的含义,能根据任务选择合适的 dtype,能手动判断广播是否成立,并能写出常见索引和切片操作。

准备代码:统一使用一个打印函数

为了让示例更清晰,先准备一个小工具函数:

python 复制代码
import paddle


def show(name, tensor):
    print(f"\n{name}")
    print("-" * 60)
    print(tensor)
    print("shape:", tensor.shape)
    print("ndim:", tensor.ndim)
    print("dtype:", tensor.dtype)
    print("place:", tensor.place)

后面的代码都可以在同一个 Python 文件里运行。建议新建:

text 复制代码
tensor_shape_dtype_broadcast_index.py

并在文件开头写入:

python 复制代码
import paddle

paddle.seed(2026)

本文不依赖 GPU,CPU 环境即可运行。

形状基础:shape、ndim、axis、size 分别是什么

Paddle 官方文档中把 shapendimaxissize 都作为理解 Tensor 形状的重要概念。先用一个三维 Tensor 来看:

python 复制代码
import paddle

x = paddle.arange(0, 24, dtype="float32").reshape([2, 3, 4])

print(x)
print("shape:", x.shape)
print("ndim:", x.ndim)
print("numel:", x.numel())

输出含义是:

text 复制代码
shape = [2, 3, 4]
ndim = 3
numel = 24

可以这样理解:

text 复制代码
第 0 维:2 个块
第 1 维:每个块 3 行
第 2 维:每行 4 个元素

总元素个数:2 * 3 * 4 = 24

这些概念的关系如下:

概念 含义 示例
shape 每个维度上的元素数量 [2, 3, 4]
ndim Tensor 有几个维度 3
axis 指定沿哪个维度操作 axis=0/1/2
numel() Tensor 中元素总数 24

很多 Paddle 报错都会带 shape 信息。训练时遇到错误,第一反应应该是打印:

python 复制代码
print(x.shape)
print(x.dtype)

这比盲目改网络结构更有效。

形状读法:把常见任务的 shape 看懂

不同任务会使用不同的 Tensor 形状。下面是最常见的几类。

表格或向量数据

text 复制代码
[batch_size, feature_dim]

例如:

python 复制代码
x = paddle.randn([32, 10])

含义是:

  • 32 个样本。
  • 每个样本 10 个特征。

这种形状常见于多层感知机、回归模型、传统特征输入等场景。

图像数据

Paddle 图像模型中常见格式是 NCHW:

text 复制代码
[batch_size, channels, height, width]

例如:

python 复制代码
images = paddle.randn([8, 3, 224, 224])

含义是:

  • 8 张图片。
  • 每张图片 3 个通道。
  • 高度 224。
  • 宽度 224。

如果是灰度图,通道数通常是 1:

python 复制代码
gray_images = paddle.randn([8, 1, 28, 28])

文本 token 数据

文本模型中常见形状是:

text 复制代码
[batch_size, sequence_length]

例如:

python 复制代码
token_ids = paddle.randint(0, 10000, [16, 128], dtype="int64")

含义是:

  • 16 条文本。
  • 每条文本最多 128 个 token。
  • 每个位置是一个词表 ID。

如果进入 Embedding 层后,常见形状会变成:

text 复制代码
[batch_size, sequence_length, hidden_size]

例如:

text 复制代码
[16, 128, 768]

分类输出

分类模型输出通常是:

text 复制代码
[batch_size, num_classes]

例如:

python 复制代码
logits = paddle.randn([32, 10])
labels = paddle.randint(0, 10, [32], dtype="int64")

含义是:

  • logits:32 个样本,每个样本对应 10 个类别分数。
  • labels:32 个样本的真实类别编号。

这里有一个关键点:分类标签不是 [32, 10] 的 one-hot 时,常见损失函数通常期待 [32] 的类别 ID,并且 dtype 通常是 int64

reshape:改变形状但不改变元素总数

reshape 是最常用的形状变换方法之一。

python 复制代码
import paddle

x = paddle.arange(0, 12, dtype="float32")
show("x", x)

y = x.reshape([3, 4])
show("y = x.reshape([3, 4])", y)

z = x.reshape([2, 2, 3])
show("z = x.reshape([2, 2, 3])", z)

reshape 的核心规则是:变换前后元素总数必须一致。

text 复制代码
[12] -> [3, 4]      可以,因为 12 = 3 * 4
[12] -> [2, 2, 3]   可以,因为 12 = 2 * 2 * 3
[12] -> [5, 3]      不可以,因为 12 != 5 * 3

使用 -1 自动推断维度

-1 可以让 Paddle 自动推断这一维的大小:

python 复制代码
x = paddle.arange(0, 24, dtype="float32")

a = x.reshape([2, -1])
b = x.reshape([-1, 3, 4])

show("a = x.reshape([2, -1])", a)
show("b = x.reshape([-1, 3, 4])", b)

推断过程是:

text 复制代码
x 有 24 个元素

[2, -1]     -> [2, 12]
[-1, 3, 4]  -> [2, 3, 4]

注意:一个 shape 里通常只应该出现一个 -1。多个未知维度会让框架无法唯一推断。

reshape 的常见误区

reshape 只改变视图形状,不会理解你的业务含义。下面这类代码很危险:

python 复制代码
images = paddle.randn([8, 3, 224, 224])
wrong = images.reshape([3, 8, 224, 224])

这段代码可能能运行,但它把 batch 维和 channel 维混在了一起,业务含义已经错了。图像数据的 [N, C, H, W] 不是随便换顺序;如果要换维度顺序,应该使用 transpose,而不是用 reshape 硬改。

reshape 改的是形状,不是维度语义;换维度顺序用 transpose

squeeze 和 unsqueeze:处理尺寸为 1 的维度

深度学习里经常需要增加或删除大小为 1 的维度。

unsqueeze:增加一个维度

python 复制代码
import paddle

x = paddle.to_tensor([1.0, 2.0, 3.0])
show("x", x)

y = paddle.unsqueeze(x, axis=0)
z = paddle.unsqueeze(x, axis=1)

show("unsqueeze axis=0", y)
show("unsqueeze axis=1", z)

形状变化是:

text 复制代码
x: [3]
axis=0 -> [1, 3]
axis=1 -> [3, 1]

常见用途:

  • 给单个样本增加 batch 维。
  • 给向量增加可广播维度。
  • [H, W] 转成 [1, H, W][1, 1, H, W]

squeeze:删除尺寸为 1 的维度

python 复制代码
x = paddle.randn([1, 3, 1, 4])

y = paddle.squeeze(x)
z = paddle.squeeze(x, axis=0)

show("x", x)
show("squeeze all size-1 dims", y)
show("squeeze axis=0", z)

形状变化是:

text 复制代码
[1, 3, 1, 4] -> squeeze() -> [3, 4]
[1, 3, 1, 4] -> squeeze(axis=0) -> [3, 1, 4]

使用 squeeze() 时要谨慎。如果 batch_size 恰好是 1,直接 squeeze() 可能把 batch 维也删掉,导致后续代码在 batch_size 大于 1 时行为不一致。

更稳的写法是明确指定 axis

python 复制代码
y = paddle.squeeze(x, axis=2)

transpose:调整维度顺序

当你需要交换维度顺序时,使用 transpose

python 复制代码
import paddle

x = paddle.randn([2, 3, 4])
y = paddle.transpose(x, perm=[0, 2, 1])

show("x", x)
show("transpose [0, 2, 1]", y)

形状变化是:

text 复制代码
[2, 3, 4] -> [2, 4, 3]

这里 perm=[0, 2, 1] 表示:

text 复制代码
新第 0 维 = 原第 0 维
新第 1 维 = 原第 2 维
新第 2 维 = 原第 1 维

在图像任务中,经常会遇到 HWC 和 CHW 的转换:

text 复制代码
HWC: [height, width, channels]
CHW: [channels, height, width]

对应写法:

python 复制代码
image_hwc = paddle.randn([224, 224, 3])
image_chw = paddle.transpose(image_hwc, perm=[2, 0, 1])

show("image_chw", image_chw)

不要用 reshape 做 HWC 到 CHW 的转换。 reshape 不会按维度语义重新排列数据,只是换一种形状解释底层元素。

dtype 基础:数据类型决定算子行为和训练稳定性

Paddle Tensor 中所有元素的数据类型相同,可以通过 tensor.dtype 查看。官方文档列出的常见类型包括:

  • bool
  • float16
  • float32
  • float64
  • uint8
  • int8
  • int16
  • int32
  • int64
  • complex64
  • complex128

先看一个简单示例:

python 复制代码
import paddle

a = paddle.to_tensor([1, 2, 3])
b = paddle.to_tensor([1.0, 2.0, 3.0])
c = paddle.to_tensor([1.0, 2.0, 3.0], dtype="float64")

show("a from Python integers", a)
show("b from Python floats", b)
show("c specified float64", c)

常见默认规则:

  • Python 整数通常创建为 int64
  • Python 浮点数通常创建为 float32
  • 也可以通过 dtype 显式指定。

什么时候用 float32

大多数模型输入、模型参数、回归目标、损失计算都使用 float32

python 复制代码
features = paddle.to_tensor(
    [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]],
    dtype="float32",
)

float32 是深度学习中的常规选择,在精度、性能和显存之间比较均衡。

什么时候用 int64

类别标签、词表 ID、索引通常使用 int64

python 复制代码
labels = paddle.to_tensor([0, 2, 1, 3], dtype="int64")
token_ids = paddle.randint(0, 30000, [2, 8], dtype="int64")

例如分类任务中:

python 复制代码
logits = paddle.randn([4, 5], dtype="float32")
labels = paddle.to_tensor([0, 3, 1, 4], dtype="int64")

这里 logits 是浮点分数,labels 是类别编号。不要把标签写成:

python 复制代码
labels = paddle.to_tensor([0.0, 3.0, 1.0, 4.0])

这会得到浮点标签,很多分类损失函数并不接受。

什么时候用 bool

布尔类型常用于掩码:

python 复制代码
scores = paddle.to_tensor([0.1, 0.9, 0.3, 0.7], dtype="float32")
mask = scores > 0.5

show("mask", mask)

mask 的 dtype 是 bool,可以用于筛选、条件判断或后续构造 attention mask。

astype:转换数据类型

如果已经创建了 Tensor,可以使用 astype 转换类型:

python 复制代码
x = paddle.to_tensor([1, 2, 3])
y = x.astype("float32")

show("x int64", x)
show("y float32", y)

常见转换场景:

  • 图像像素从 uint8 转成 float32
  • 标签从默认整型整理成 int64
  • 某些统计结果需要转为浮点数计算。

示例:

python 复制代码
pixels = paddle.to_tensor([[0, 127, 255]], dtype="uint8")
pixels_float = pixels.astype("float32") / 255.0

show("pixels_float", pixels_float)

隐式类型提升:能自动转,不代表应该依赖它

当不同 dtype 的 Tensor 一起计算时,框架可能进行类型提升。

python 复制代码
import paddle

x = paddle.to_tensor([1, 2, 3], dtype="int64")
y = paddle.to_tensor([0.5, 0.5, 0.5], dtype="float32")

z = x + y
show("z = int64 + float32", z)

虽然这种写法可能正常运行,但不建议在关键训练逻辑中依赖隐式类型推断。更稳的写法是提前把输入整理成明确类型:

python 复制代码
x = x.astype("float32")
z = x + y

经验规则

  • 模型输入统一整理成 float32
  • 分类标签统一整理成 int64
  • 索引类 Tensor 统一整理成 int64
  • 掩码统一整理成 bool 或框架 API 明确要求的类型。

广播机制:为什么不同 shape 可以一起计算

广播是 Tensor 运算里非常重要的机制。官方文档说明:当较小形状的 Tensor 与较大形状的 Tensor 一起计算时,广播会在不进行真实数据拷贝的情况下,把较小 Tensor 按规则扩展到可匹配形状。

最简单例子:

python 复制代码
import paddle

x = paddle.ones([2, 3], dtype="float32")
b = paddle.to_tensor([10.0, 20.0, 30.0])

y = x + b

show("x", x)
show("b", b)
show("y = x + b", y)

形状关系:

text 复制代码
x: [2, 3]
b: [3]

b 会按行广播:
[[10, 20, 30],
 [10, 20, 30]]

结果 y: [2, 3]

广播判断规则

判断两个 Tensor 是否能广播,可以按下面流程:

text 复制代码
1. 从最后一个维度开始向前比较
2. 两个维度相等,可以广播
3. 其中一个维度是 1,可以广播
4. 其中一个维度不存在,可以广播
5. 否则不能广播

示例一:

text 复制代码
x: [2, 3, 4]
y: [3, 4]

对齐后:
x: [2, 3, 4]
y: [1, 3, 4]

结果:[2, 3, 4]

代码:

python 复制代码
x = paddle.ones([2, 3, 4])
y = paddle.ones([3, 4])
z = x + y

show("z", z)

示例二:

text 复制代码
x: [2, 3, 1, 5]
y: [3, 4, 1]

对齐后:
x: [2, 3, 1, 5]
y: [1, 3, 4, 1]

逐维比较:
2 vs 1 -> 可以
3 vs 3 -> 可以
1 vs 4 -> 可以
5 vs 1 -> 可以

结果:[2, 3, 4, 5]

代码:

python 复制代码
x = paddle.ones([2, 3, 1, 5])
y = paddle.ones([3, 4, 1])
z = x + y

show("broadcast result", z)

不能广播的例子

text 复制代码
x: [2, 3, 4]
y: [2, 3, 6]

最后一维:
4 vs 6

不相等,也没有任何一个是 1,因此不能广播。

代码:

python 复制代码
x = paddle.ones([2, 3, 4])
y = paddle.ones([2, 3, 6])

try:
    z = x + y
except Exception as exc:
    print(type(exc).__name__)
    print(exc)

调试广播错误时,把 shape 写成右对齐形式最有效。

text 复制代码
    [2, 3, 4]
    [2, 3, 6]
             ^

broadcast_to:显式扩展到目标形状

多数时候广播是自动发生的,但有时为了让代码更清楚,可以使用 paddle.broadcast_to()

python 复制代码
import paddle

b = paddle.to_tensor([10.0, 20.0, 30.0])
b2 = paddle.broadcast_to(b, shape=[2, 3])

show("b", b)
show("b2", b2)

这段代码表达得很明确:把 [3] 的向量广播成 [2, 3]

注意:broadcast_to 不是随便复制成任意形状,它同样要符合广播规则。

可以广播:

python 复制代码
x = paddle.ones([1, 3, 1])
y = paddle.broadcast_to(x, shape=[2, 3, 4])
show("y", y)

不能广播:

python 复制代码
x = paddle.ones([2, 3])

try:
    y = paddle.broadcast_to(x, shape=[2, 4])
except Exception as exc:
    print(type(exc).__name__)
    print(exc)

因为原始最后一维是 3,目标最后一维是 4,不相等且原始维度不是 1。

广播在模型里的真实用途

广播不是语法糖,它在模型里很常见。

给每个样本加同一个 bias

python 复制代码
x = paddle.randn([4, 3])
b = paddle.to_tensor([0.1, 0.2, 0.3], dtype="float32")

y = x + b
show("y", y)

形状:

text 复制代码
x: [4, 3]
b: [3]
y: [4, 3]

图像按通道归一化

假设图像 Tensor 是 [N, C, H, W]

python 复制代码
images = paddle.randn([8, 3, 224, 224])
mean = paddle.to_tensor([0.485, 0.456, 0.406], dtype="float32").reshape([1, 3, 1, 1])
std = paddle.to_tensor([0.229, 0.224, 0.225], dtype="float32").reshape([1, 3, 1, 1])

normalized = (images - mean) / std
show("normalized", normalized)

为什么 mean 要 reshape 成 [1, 3, 1, 1]

text 复制代码
images: [8, 3, 224, 224]
mean:   [1, 3,   1,   1]

第 0 维:1 广播到 8
第 1 维:3 对 3
第 2 维:1 广播到 224
第 3 维:1 广播到 224

这就是广播在图像任务里的典型用法。

文本 mask 扩展维度

文本 attention mask 常从 [batch_size, seq_len] 扩展成更高维:

python 复制代码
mask = paddle.to_tensor(
    [[1, 1, 1, 0], [1, 1, 0, 0]],
    dtype="float32",
)

expanded_mask = mask.reshape([2, 1, 1, 4])
show("expanded_mask", expanded_mask)

这样后续可以和 attention 分数做广播计算。

基础索引:像 Python 和 NumPy 一样取数据

Paddle 支持标准 Python 风格索引。

python 复制代码
import paddle

x = paddle.arange(0, 12, dtype="float32").reshape([3, 4])
show("x", x)

show("x[0]", x[0])
show("x[1, 2]", x[1, 2])
show("x[:, 1]", x[:, 1])
show("x[1:, 2:]", x[1:, 2:])

原始 Tensor:

text 复制代码
x =
[[ 0,  1,  2,  3],
 [ 4,  5,  6,  7],
 [ 8,  9, 10, 11]]

索引含义:

写法 含义 结果形状
x[0] 第 0 行 [4]
x[1, 2] 第 1 行第 2 列 []
x[:, 1] 所有行,第 1 列 [3]
x[1:, 2:] 第 1 行到末尾,第 2 列到末尾 [2, 2]

注意:x[1, 2] 的结果是一个 0 维 Tensor,不是 Python float。如果需要 Python 数值,可以进一步使用:

python 复制代码
value = x[1, 2].item()
print(value)

切片:start、end、step 的直觉

切片语法:

text 复制代码
start:end:step

示例:

python 复制代码
x = paddle.arange(0, 10)

show("x", x)
show("x[2:8]", x[2:8])
show("x[2:8:2]", x[2:8:2])
show("x[:5]", x[:5])
show("x[5:]", x[5:])
show("x[::-1]", x[::-1])

含义:

  • x[2:8]:从下标 2 到下标 8 之前。
  • x[2:8:2]:从 2 到 8 之前,每隔 2 个取一个。
  • x[:5]:从开头取到下标 5 之前。
  • x[5:]:从下标 5 取到末尾。
  • x[::-1]:反向取全部元素。

在二维 Tensor 中,逗号分隔不同维度:

python 复制代码
x = paddle.arange(0, 20).reshape([4, 5])

show("x", x)
show("first two rows", x[:2, :])
show("last three columns", x[:, -3:])
show("every other row", x[::2, :])

保留维度:整数索引和切片索引的差异

整数索引会减少维度,切片索引通常保留维度。

python 复制代码
x = paddle.randn([2, 3, 4])

a = x[0]
b = x[0:1]

show("x", x)
show("x[0]", a)
show("x[0:1]", b)

形状变化:

text 复制代码
x:      [2, 3, 4]
x[0]:   [3, 4]
x[0:1]: [1, 3, 4]

这在模型推理时很重要。如果网络期待输入带 batch 维 [N, C, H, W],那么对单张图片更稳的做法是保留 batch 维:

python 复制代码
single_image = images[0:1]

而不是:

python 复制代码
single_image = images[0]

后者会从 [N, C, H, W] 变成 [C, H, W],可能导致模型输入维度不匹配。

布尔索引:用条件筛选数据

布尔条件会生成 bool Tensor:

python 复制代码
import paddle

x = paddle.to_tensor([0.1, 0.8, 0.3, 0.9, 0.2])
mask = x > 0.5

show("mask", mask)
show("x[mask]", x[mask])

结果中只保留满足条件的位置。

二维示例:

python 复制代码
x = paddle.arange(0, 12, dtype="float32").reshape([3, 4])
mask = x > 5

show("mask", mask)
show("x[mask]", x[mask])

注意:布尔索引通常会把结果拉平成一维,因为它返回所有满足条件的元素,而不再保留原来的矩阵结构。

如果你希望保留结构,可以使用 paddle.where

python 复制代码
x = paddle.arange(0, 12, dtype="float32").reshape([3, 4])
y = paddle.where(x > 5, x, paddle.zeros_like(x))

show("y", y)

这段代码表示:

text 复制代码
大于 5 的位置保留原值
其他位置替换为 0

高级索引:按指定下标取元素

当你需要取指定行或指定位置时,可以使用高级索引。

按行取数据

python 复制代码
x = paddle.arange(0, 20).reshape([5, 4])
rows = paddle.to_tensor([0, 2, 4], dtype="int64")

selected = x[rows]

show("x", x)
show("selected rows", selected)

形状关系:

text 复制代码
x: [5, 4]
rows: [3]
selected: [3, 4]

按二维坐标取元素

python 复制代码
x = paddle.arange(0, 12).reshape([3, 4])

row_ids = paddle.to_tensor([0, 1, 2], dtype="int64")
col_ids = paddle.to_tensor([3, 2, 1], dtype="int64")

selected = x[row_ids, col_ids]

show("selected", selected)

它取的是:

text 复制代码
x[0, 3]
x[1, 2]
x[2, 1]

结果形状是 [3]

使用 gather_nd 表达坐标索引

当坐标已经组织成矩阵时,可以使用 paddle.gather_nd

python 复制代码
x = paddle.arange(0, 12).reshape([3, 4])
index = paddle.to_tensor(
    [[0, 3], [1, 2], [2, 1]],
    dtype="int64",
)

selected = paddle.gather_nd(x, index)
show("selected by gather_nd", selected)

index 的每一行都是一个坐标:

text 复制代码
[0, 3]
[1, 2]
[2, 1]

这类写法在目标检测、序列标注、推荐召回等任务中经常出现。

修改 Tensor 局部数据:setitem 的基本用法

Paddle 支持通过索引修改 Tensor。

python 复制代码
import paddle

x = paddle.zeros([3, 4], dtype="float32")
x[0, 0] = 1.0
x[1, :] = paddle.to_tensor([2.0, 2.0, 2.0, 2.0])
x[:, 3] = 9.0

show("x after assignment", x)

这种写法适合构造测试数据、mask 或小规模调试。训练主流程中要谨慎使用频繁的原地修改,尤其是在涉及自动微分时,要注意是否影响计算图。

更函数式的写法可以用 paddle.wherescattergather 等 API。初学阶段先掌握索引赋值即可。

完整练习:模拟一个小 batch 的数据处理

下面用一个小例子把 shape、dtype、广播和索引串起来。

假设有 4 个样本,每个样本 3 个特征:

python 复制代码
import paddle

features = paddle.to_tensor(
    [
        [10.0, 100.0, 1.0],
        [20.0, 200.0, 0.0],
        [30.0, 300.0, 1.0],
        [40.0, 400.0, 0.0],
    ],
    dtype="float32",
)

labels = paddle.to_tensor([0, 1, 0, 1], dtype="int64")

mean = paddle.mean(features, axis=0)
std = paddle.std(features, axis=0)
normalized = (features - mean) / (std + 1e-6)

positive_features = normalized[labels == 1]

show("features", features)
show("labels", labels)
show("mean", mean)
show("std", std)
show("normalized", normalized)
show("positive_features", positive_features)

这里发生了几件事:

  1. features[4, 3],表示 4 个样本、3 个特征。
  2. labels[4],dtype 是 int64,表示分类标签。
  3. meanstd[3],表示每个特征维度的统计量。
  4. (features - mean) 使用广播,将 [3] 扩展到 [4, 3]
  5. labels == 1 得到 [4] 的 bool mask。
  6. normalized[labels == 1] 选出标签为 1 的样本,结果形状是 [2, 3]

这段代码虽然简单,但已经很接近真实机器学习预处理流程。

常见错误:shape、dtype、广播、索引排查清单

错误一:reshape 后元素总数不一致

python 复制代码
x = paddle.arange(0, 10)

try:
    y = x.reshape([3, 4])
except Exception as exc:
    print(type(exc).__name__)
    print(exc)

原因:

text 复制代码
10 != 3 * 4

排查方式:

python 复制代码
print(x.numel())

错误二:矩阵乘法维度不匹配

python 复制代码
a = paddle.randn([2, 3])
b = paddle.randn([2, 4])

try:
    c = paddle.matmul(a, b)
except Exception as exc:
    print(type(exc).__name__)
    print(exc)

判断方式:

text 复制代码
[2, 3] @ [2, 4]
        3 != 2

正确写法之一:

python 复制代码
b = paddle.randn([3, 4])
c = paddle.matmul(a, b)

错误三:分类标签 dtype 错误

错误倾向:

python 复制代码
labels = paddle.to_tensor([0.0, 1.0, 2.0], dtype="float32")

更常见的分类标签写法:

python 复制代码
labels = paddle.to_tensor([0, 1, 2], dtype="int64")

排查方式:

python 复制代码
print(labels.dtype)

错误四:广播维度从左边开始对齐

很多初学者会误以为广播从第 0 维开始比较。实际应从最后一个维度开始比较。

text 复制代码
x: [2, 3, 4]
y: [3, 4]

对齐方式:
x: [2, 3, 4]
y: [1, 3, 4]

不是:

text 复制代码
x: [2, 3, 4]
y: [3, 4, ?]

错误五:整数索引导致 batch 维丢失

python 复制代码
images = paddle.randn([8, 3, 224, 224])

bad = images[0]
good = images[0:1]

print(bad.shape)
print(good.shape)

输出:

text 复制代码
[3, 224, 224]
[1, 3, 224, 224]

模型通常需要 batch 维,因此推理单样本时优先保留 [1, C, H, W]

错误六:布尔索引后结构被拉平

python 复制代码
x = paddle.arange(0, 12).reshape([3, 4])
selected = x[x > 5]

print(selected.shape)

这不是 [3, 4] 的结构,而是所有满足条件元素组成的一维结果。如果想保留原结构,使用:

python 复制代码
y = paddle.where(x > 5, x, paddle.zeros_like(x))

调试方法:看到 Tensor 错误先打印这 5 个信息

建议在调试脚本中保留一个函数:

python 复制代码
def debug_tensor(name, tensor):
    print(f"{name}:")
    print("  shape:", tensor.shape)
    print("  ndim:", tensor.ndim)
    print("  dtype:", tensor.dtype)
    print("  place:", tensor.place)
    print("  stop_gradient:", tensor.stop_gradient)

遇到错误时打印:

python 复制代码
debug_tensor("x", x)
debug_tensor("y", y)

通常能快速定位:

  • shape 是否匹配。
  • dtype 是否符合算子要求。
  • Tensor 是否在同一设备。
  • 是否意外丢失 batch 维。
  • 是否需要 unsqueezereshape

一句话经验:Paddle 初学阶段,调试不是先看模型代码,而是先看每个关键 Tensor 的结构。

总结

这一篇围绕 Paddle Tensor 的四个核心基础展开:

  • shape:描述 Tensor 的结构,排查维度错误时第一时间查看。
  • dtype :描述元素类型,模型输入常用 float32,分类标签常用 int64
  • broadcast:允许不同形状 Tensor 一起计算,判断时从最后一维向前比较。
  • index:用于取出、筛选和修改 Tensor 局部数据,注意整数索引和切片索引对维度的影响。

理解这四个核心对于写模型至关重要。

相关推荐
cui_ruicheng7 小时前
Python从入门到实战(十五):文件操作与目录管理
开发语言·python
xcLeigh7 小时前
Doubao-Seed-Evolving大模型接入教程|搭建全品类提示词+AI工具导航网页
前端·人工智能·python·ai·html·ai开发·豆包
不如语冰7 小时前
AI大模型入门-模块导入import
数据结构·人工智能·pytorch·python
投票竞赛7 小时前
书法、绘画作品投票评选,图片投票小程序作品集排版
python·小程序
学术小白人7 小时前
【倒计时4个月】-AI赋能图像处理与计算机视觉技术国际学术研讨会
网络·人工智能·神经网络·数据分析·光学
梦远青城7 小时前
Docker 部署python的paddle进行OCR文字识别身份证
python·docker·ocr·paddle·身份证识别
编码者卢布8 小时前
【Azure APIM】APIM的诊断日志与Application Insights的日志是否可以串联为一个端到端的日志链路呢?
python·flask·azure
AI人工智能+8 小时前
银行回单识别技术通过AI驱动的智能文档理解方案,实现财务处理的革命性升级
深度学习·计算机视觉·自然语言处理·ocr·银行回单识别
湘美书院--湘美谈教育8 小时前
湘美书院主理人:AI世代武侠小说的基本特征与蕴意
大数据·人工智能·深度学习·机器学习·生活