PyTorch Tensor入门:核心属性、创建方式与高频操作详解

PyTorch Tensor入门:核心属性、创建方式与高频操作详解 很多人在学习 PyTorch 时会陷入一个误区第一课就想直接搭建神经网络。结果torch.nn.Linear、torch.nn.Conv2d还没用热就被各种 shape 报错、device 报错、dtype 报错打回原形。回头再看问题往往出在最基本的数据结构上——Tensor。如果说神经网络是 PyTorch 这栋大楼的墙体那 Tensor 就是一块块的砖。砖的尺寸、类型、摆放位置不对墙就砌不起来。与其一脸懵地抄模型代码不如先花一篇文章把 Tensor 的创建、属性、基本操作讲透。这节课不追求“跑通一个模型”追求的是“你亲手写出的每一行 Tensor 代码都能预测结果”。文章会从三个核心属性讲起接着演示 5 类常见创建方式再拆解 4 组高频操作最后补充 PyTorch 安装验证和真实项目中容易踩的坑。内容偏基础但顺序和判断标准是按工程实践来的不是按 API 文档抄的。1. 为什么第一课应该学 Tensor而不是直接学神经网络先回答一个很实际的问题为什么几乎所有 PyTorch 入门路线都会把 Tensor 放在最前面因为 PyTorch 里所有计算包括标量、向量、矩阵、图片甚至是模型参数在底层都以 Tensor 形式存在。你调用model(x)时x是 Tensormodel内部的权重是 Tensor输出的预测结果也是 Tensor。如果不理解 Tensor那你写神经网络就只是在“套模板”遇到报错时根本没有能力定位问题。Tensor 和 NumPy 数组很像很多人会直接把它理解成“可以跑在 GPU 上的 NumPy 数组”。这个类比方向是对的但不完整。Tensor 比 NumPy 数组多了三个关键能力第一它可以自动记录计算图并计算梯度这是深度学习训练的基础第二它可以指定运行在cuda设备上让矩阵运算利用 GPU 并行加速第三它在生态上无缝衔接了 PyTorch 的 Dataset、DataLoader、nn.Module 等模块。所以Tensor 不只是“数据容器”它是 PyTorch 计算体系的最小单元。把 Tensor 的属性和操作搞明白后面学自动求导、模型搭建、训练循环你会觉得很顺如果跳过这一课你会反复在错误中打转。2. Tensor 的三个核心属性shape、dtype、device在动手创建 Tensor 之前先建立一个判断框架。每次你拿到一个 Tensor都应该能回答三个问题它的形状是什么它里面存的是什么类型的数据它运行在 CPU 还是 GPU 上这三个属性分别对应shape、dtype、device。绝大多数新手报错都跟这三个属性有关。2.1 shape张量的形状与维度shape描述的是 Tensor 每个维度的大小。比如torch.zeros(2, 3)产生一个 2 行 3 列的张量它的shape是torch.Size([2, 3])。理解 shape 是理解 PyTorch 的基础门槛尤其是它在卷积、Transformer、时间序列模型里反复出现。新手最容易犯的错误是把一维向量和二维矩阵搞混。torch.tensor([1, 2, 3])的 shape 是[3]它只有一个维度而torch.tensor([[1, 2, 3]])的 shape 是[1, 3]它是二维的。一个是“3 个数字排成一排”一个是“1 行 3 列的表格”。import torch a torch.tensor([1, 2, 3]) b torch.tensor([[1, 2, 3]]) print(a.shape:, a.shape) # torch.Size([3]) print(b.shape:, b.shape) # torch.Size([1, 3]) print(a.ndim:, a.ndim) # 1 print(b.ndim:, b.ndim) # 2这段代码中的a.ndim和b.ndim会输出不同值正是因为维度不同。建议你在初学阶段每创建一个 Tensor 都打印一次shape慢慢形成对维度的直觉。2.2 dtype数据类型决定精度和存储dtype表示 Tensor 中元素的数据类型。PyTorch 中常见的有torch.float32、torch.float64、torch.int64、torch.bool等。模型训练中最常用的是torch.float32也就是单精度浮点数。需要特别注意的坑是从 Python 内置类型创建 Tensor 时PyTorch 会推断默认类型。比如torch.tensor([1, 2, 3])会得到torch.int64而torch.tensor([1.0, 2.0, 3.0])会得到torch.float32。大部分神经网络操作要求输入是浮点数如果你拿一个int64的张量直接喂给模型很可能会报数据类型不匹配的错误。所以在需要浮点计算的场景最好显式指定dtype。x_int torch.tensor([1, 2, 3]) x_float torch.tensor([1.0, 2.0, 3.0]) x_float32 torch.tensor([1, 2, 3], dtypetorch.float32) print(x_int dtype:, x_int.dtype) # torch.int64 print(x_float dtype:, x_float.dtype) # torch.float32 print(x_float32 dtype:, x_float32.dtype) # torch.float32这里真正容易踩坑的地方是torch.tensor和torch.Tensor的区别。torch.tensor会根据传入数据推断dtype而torch.Tensor等价于torch.FloatTensor默认创建torch.float32的张量。如果你对默认行为没有把握显式传dtype是最稳妥的做法。2.3 device数据在 CPU 还是 GPUdevice表示 Tensor 所在的设备。cpu表示内存中的 CPU 张量cuda:0表示第一块 NVIDIA GPU 上的张量。CPU 和 GPU 上的张量不能直接做运算新手最常见的报错之一就是 “Expected all tensors to be on the same device”。在现代 PyTorch 2.x 中更推荐的写法是用torch.device统一管理设备而不是到处写.cuda()。device torch.device(cuda if torch.cuda.is_available() else cpu) x torch.randn(3, 4, devicedevice) print(x.device:, x.device)从机器学习工程的角度看把设备选择写在代码入口处避免在模型、损失函数、数据加载器里到处判断设备会大幅减少低级错误。2.4 requires_grad开启梯度追踪在深度学习训练中Tensor 还有一个特殊属性requires_grad。当它设置为True时PyTorch 会记录这个 Tensor 参与的所有运算以便后续调用backward()时自动计算梯度。x torch.tensor([2.0, 3.0], requires_gradTrue) y x ** 2 y.sum().backward() print(x.grad:, x.grad) # tensor([4., 6.])这个例子中y x^2对x的梯度是2x所以x2时梯度为 4x3时梯度为 6。初学阶段你可以先记住结论后面讲自动求导时会深入展开。现在只需要知道requires_grad是连接 Tensor 与神经网络训练的桥梁。3. Tensor 的创建方式从简单到实用PyTorch 创建 Tensor 的方式非常多但核心规律只有一条要么从已有数据转换要么用工厂函数按规则生成。下面按使用频率从高到低整理。3.1 从 Python 列表或 NumPy 数组创建最直观的方式是直接用torch.tensor包裹 Python 列表或者用torch.as_tensor包装 NumPy 数组。两者都会复制数据或共享内存具体行为有细微差别但初学阶段你只需要记住torch.tensor最常用torch.as_tensor在 NumPy 和 PyTorch 互转时效率更高。import numpy as np # 从列表创建 from_list torch.tensor([[1, 2], [3, 4]]) # 从 NumPy 数组创建 np_array np.array([[1.0, 2.0], [3.0, 4.0]]) from_numpy torch.as_tensor(np_array) print(from_list:\n, from_list) print(from_numpy:\n, from_numpy)这里有一个常见误区很多老教程会让你用torch.from_numpy(np_array)。这个函数本身没问题但它返回的 Tensor 与 NumPy 数组共享底层内存修改任意一方都会影响另一方。如果你不希望数据被意外修改用torch.as_tensor配合显式复制会更安全。3.2 用工厂函数创建规则数据创建全 0、全 1、指定数值、等间隔序列时使用工厂函数。zeros torch.zeros(2, 3) ones torch.ones(2, 3) full torch.full((2, 3), 7) range_tensor torch.arange(0, 10, 2) linspace_tensor torch.linspace(0, 1, steps5) print(zeros:\n, zeros) print(ones:\n, ones) print(full:\n, full) print(arange:, range_tensor) print(linspace:, linspace_tensor)torch.arange类似 Python 内置的range左闭右开torch.linspace是闭区间生成steps个均匀分布的数。在需要生成坐标、权重初始化、测试数据时这些函数非常常用。3.3 创建随机张量深度学习中模型权重通常需要随机初始化。PyTorch 提供多组随机生成函数分别对应不同分布。rand_tensor torch.rand(2, 3) # [0, 1) 均匀分布 randn_tensor torch.randn(2, 3) # 标准正态分布 randint_tensor torch.randint(0, 10, (2, 3)) # [0, 10) 整数随机 print(rand:\n, rand_tensor) print(randn:\n, randn_tensor) print(randint:\n, randint_tensor)初学阶段torch.randn更值得关注因为很多模型的权重初始化遵循正态分布。每次运行结果都不同是正常现象如果希望实验可复现需要设置随机种子。这一点会在最佳实践部分详细说。3.4 创建时统一指定 dtype、device、requires_grad创建 Tensor 时这三个参数可以一次性传进去避免后续转换。device torch.device(cuda if torch.cuda.is_available() else cpu) x torch.randn(2, 3, dtypetorch.float32, devicedevice, requires_gradTrue) print(x.shape:, x.shape) print(x.dtype:, x.dtype) print(x.device:, x.device) print(x.requires_grad:, x.requires_grad)这种“创建即指定”的写法能在源头规避大量运行时错误。尤其是在项目代码里比创建后再用.to(device)、.type(torch.float32)反复转换要清晰得多。3.5 创建方式对比表为了方便查询我把常用创建函数整理成一张表创建函数作用示例返回 shapetorch.tensor(data)从数据创建torch.tensor([1,2])[2]torch.zeros(shape)全 0 张量torch.zeros(2,3)[2,3]torch.ones(shape)全 1 张量torch.ones(2,3)[2,3]torch.full(shape, val)全指定值张量torch.full((2,3),7)[2,3]torch.arange(start,end,step)等间隔整数序列torch.arange(0,10,2)[5]torch.linspace(start,end,steps)闭区间等分数列torch.linspace(0,1,5)[5]torch.rand(shape)均匀分布随机数torch.rand(2,3)[2,3]torch.randn(shape)标准正态随机数torch.randn(2,3)[2,3]torch.randint(low,high,shape)整数随机数torch.randint(0,10,(2,3))[2,3]这张表不需要背收藏起来当查询手册用就行。实际写代码时看到函数名基本能猜出用途真正需要上心的是维度参数的位置和默认 dtype。4. Tensor 的基本操作一组一组拆解创建 Tensor 只是开始真正的高频操作是索引、形状变换、数学运算、拼接和归约。下面按场景分组讲解每组都附带可运行代码。4.1 索引与切片Tensor 的索引和 NumPy 几乎完全一致。你可以用整数下标取其中一个元素用冒号切片取一段数据也可以用布尔掩码做条件筛选。x torch.arange(12).reshape(3, 4) # tensor([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]]) print(x[0]) # 第 0 行 print(x[1, 2]) # 第 1 行第 2 列值为 6 print(x[:, 1]) # 所有行的第 1 列 print(x[1:, :2]) # 从第 1 行开始前 2 列这里容易混淆的是x[1, 2]是一个零维张量标量x[1:2, 2:3]则是一个 1×1 的二维张量。二者在形状上不一样后续做拼接或广播时可能产生差异。建议在关键逻辑中打印shape确认。4.2 形状变换view、reshape、transpose、squeeze、unsqueeze形状变换是深度学习代码里出现频率最高的操作之一。CNN 的卷积和全连接层之间需要view打平Transformer 里需要transpose调整维度批量数据中经常用squeeze和unsqueeze增删维度。view和reshape都可以用来改变形状。它们的差别在于view要求张量在内存中是连续的reshape在需要时会自动进行数据复制。初学阶段建议优先使用reshape它更稳健如果在view上报错提示contiguous问题可以用x.contiguous().view(...)先保证内存连续。a torch.arange(6) b a.reshape(2, 3) print(b:\n, b) # 转置 x torch.randn(2, 3, 4) y x.transpose(0, 1) # 交换第 0 和第 1 维 z x.permute(2, 0, 1) # 按指定顺序重排所有维度 print(x.shape:, x.shape) print(y.shape:, y.shape) print(z.shape:, z.shape) # 增删维度 s torch.randn(1, 3, 1) print(squeeze:, s.squeeze().shape) # [3] print(unsqueeze:, s.unsqueeze(0).shape) # [1,1,3,1]transpose交换两个维度permute可以让所有维度按任意顺序重排。对于注意力机制中[batch, seq_len, feature]与[batch, feature, seq_len]的转换transpose是主力工具。而squeeze会删除所有长度为 1 的维度unsqueeze在指定位置增加一个长度为 1 的维度常用于给数据加 batch 维度。4.3 数学运算与矩阵乘法Tensor 支持、-、*、/这类逐元素运算也支持矩阵乘法。重点区分两个概念逐元素乘法*是每个位置对应相乘要求两个张量形状可广播矩阵乘法或torch.matmul执行的是线性代数中的矩阵乘法。a torch.tensor([[1.0, 2.0], [3.0, 4.0]]) b torch.tensor([[5.0, 6.0], [7.0, 8.0]]) print(a b:\n, a b) print(a * b:\n, a * b) # 逐元素相乘 print(a b:\n, a b) # 矩阵乘法 print(torch.mm(a, b):\n, torch.mm(a, b))新手最常犯的错误是把*当成矩阵乘法。对于两个二维张量a * b结果是对应元素相乘而不是矩阵点积。返回真正的矩阵乘法结果。如果只想算点积也可以使用torch.dot但它只适用于一维向量。4.4 拼接与分割cat、stack、split、chunk拼接操作在构造数据集和模型特征融合时很常见。cat沿已有维度连接stack创建一个新维度来堆叠。a torch.randn(2, 3) b torch.randn(2, 3) # 沿第 0 维拼接结果 shape [4, 3] c torch.cat([a, b], dim0) # 堆叠成新维度结果 shape [2, 2, 3] d torch.stack([a, b], dim0) print(cat shape:, c.shape) print(stack shape:, d.shape)cat和stack的区别可以从 shape 变化上直观看出cat不增加新的维度只是把已有维度变大stack增加一个新的维度常用于把多个样本堆成 batch。分割操作split是按固定长度切分chunk是切分成固定份数实际使用频率稍低但理解它们能帮你在查看模型 intermediate 输出时有更多工具。4.5 归约操作sum、mean、max、min归约操作会把 Tensor 的某个维度“消掉”变成汇总统计量。x torch.randn(2, 3) print(全局求和:, x.sum()) print(沿第0维求和:, x.sum(dim0)) print(全局均值:, x.mean()) print(第0维最大值:, x.max(dim0))x.sum(dim0)表示沿第 0 维求和结果是第 1 维大小对应的向量。max(dim...)返回两个值最大值和对应的索引。在分类模型里max(dim1)常用来取出每个样本预测概率最高的类别。5. 环境准备与 PyTorch 安装先跑起来再谈原理虽然这一课的核心是 Tensor但没有环境代码只能在纸上运行。这里补充 PyTorch 环境安装和验证方法重点解决“下载慢”“装了不能用 GPU”“依赖冲突”三个问题。5.1 安装方式选择PyTorch 安装主要有pip和conda两种方式。如果你已经在用 Anaconda建议为 PyTorch 单独创建一个虚拟环境避免污染基础环境。conda create -n pytorch-env python3.10 conda activate pytorch-env创建虚拟环境是工程上最稳妥的做法。不同项目的 PyTorch 版本、CUDA 版本、Python 版本可能互相冲突独立环境能让你在项目间快速切换也不会因为一次安装失败把整个开发环境弄坏。5.2 GPU 版本的安装命令GPU 版本安装的关键是 CUDA 版本匹配。PyTorch 官方安装页面会根据你的系统和 CUDA 版本生成命令常见构建有cu118对应 CUDA 11.8cu121对应 CUDA 12.1 等。安装前先确认本机显卡驱动支持的 CUDA 版本再选择对应 PyTorch 构建。pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121没有 NVIDIA GPU或者暂时只是学习 Tensor 基础可以先安装 CPU 版本后续需要 GPU 时再重建环境。CPU 版不需要关心 CUDA 匹配安装门槛更低。pip install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple如果你关心 AMD 显卡需要特别注意AMD 平台在 PyTorch 中的 GPU 支持主要依赖 ROCm且 Windows 下的支持有限。稳妥的做法是先去官方文档确认当前版本是否覆盖你的显卡型号不要盲目复制命令。5.3 PyTorch 下载很慢怎么解决下载慢是安装阶段最常见的痛点。解决办法优先级如下第一使用国内 pip 镜像。把默认 pip 源切换到清华、阿里云等镜像能明显提升 CPU 版下载速度。pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple第二如果 GPU 版需要从官方源下载而速度不理想可以先在浏览器或下载工具中下载 whl 文件再本地安装。这种方式支持断点续传不会因为网络中断导致重新下载。pip install torch-2.x.xcu121-cp310-cp310-win_amd64.whl文件名里的cp310表示 Python 3.10win_amd64表示 Windows 64 位。注意选择与你的 Python 版本匹配的文件。从材料看很多读者在安装时感到困惑的正是“手机开热点也慢”的问题这种情况下先下载 whl 再安装是最靠谱的路径。5.4 验证安装是否成功安装完成后用一段代码验证核心依赖是否可用。import torch print(PyTorch 版本:, torch.__version__) print(是否可用 CUDA:, torch.cuda.is_available()) if torch.cuda.is_available(): print(GPU 名称:, torch.cuda.get_device_name(0))如果你的机器没有 NVIDIA GPUtorch.cuda.is_available()返回False是正常现象CPU 版本也能完成 Tensor 学习和基础模型训练。但如果你的机器有 NVIDIA GPU并且驱动和 CUDA 版本匹配这里应该返回True。返回False时优先检查驱动是否安装、CUDA 版本和 PyTorch 构建是否匹配。6. 运行结果与验证一个最小示例把前面知识合并成一段完整代码建议你在自己的环境里跑通并逐行观察输出。import torch device torch.device(cuda if torch.cuda.is_available() else cpu) # 创建两个随机张量 x torch.randn(2, 3, dtypetorch.float32, devicedevice) w torch.ones(3, 1, dtypetorch.float32, devicedevice) # 矩阵乘法 y x w print(x shape:, x.shape, dtype:, x.dtype, device:, x.device) print(w shape:, w.shape) print(y shape:, y.shape) print(y 值:\n, y)这段代码完成了三件事创建随机输入、创建权重、执行矩阵乘法。x是2×3w是3×1相乘后得到yshape 是2×1。如果你能看到这些输出说明 PyTorch 的环境、Tensor 创建、基本属性、数学运算已经全部跑通这节课的核心内容就掌握了一大半。如果运行失败第一反应应该是按顺序排查导入 PyTorch 是否成功device 设置是否正确两个参与乘法的张量 shape 是否匹配。其中任何一步出问题报错信息会明确指出。7. 常见问题与排查思路问题现象可能原因排查方式解决方案pip install torch下载很慢或超时默认 PyPI 源在境外网络不稳定检查下载进度观察卡在哪个依赖配置国内 pip 镜像或先下载 whl 再本地安装torch.cuda.is_available()返回 False没有 NVIDIA GPU或驱动、CUDA 版本与 PyTorch 构建不匹配运行nvidia-smi查看驱动支持的 CUDA 版本根据实际 CUDA 版本选择对应 PyTorch 构建CPU 和 GPU 张量直接运算报错device不一致打印参与运算张量的device用tensor.to(device)统一设备模型输入报 dtype 不匹配整数张量和浮点张量混用查看报错信息中提到的实际 dtype创建时显式指定dtypetorch.float32torch.load加载旧检查点报错从 PyTorch 2.6 开始weights_only默认值为True包含自定义对象的旧检查点会被拦截查看异常信息是否与 pickle 反序列化有关对可信模型可显式设置weights_onlyFalse更推荐只保存和加载state_dictconda卡在 Solving environmentconda 依赖解析过慢观察是否长时间停在 solving 阶段改用mamba或直接用 pip 安装view报错提示不连续转置、切片等操作可能使内存不连续查看是否使用了transpose、permute使用reshape或先调用contiguous()矩阵乘法结果不符合预期把*当成矩阵乘法使用检查代码中是*还是矩阵乘法使用或torch.matmul表格里值得单独展开的是 PyTorch 2.6 的weights_only变化。从 2.6 开始torch.load的默认参数从weights_onlyFalse调整为weights_onlyTrue这个改动提升了安全性避免了加载恶意 pickle 文件的风险。但如果你用旧版本 PyTorch 保存的检查点里包含自定义类实例直接加载会报错。工程上最安全的做法是保存模型时只保存state_dict加载时只加载state_dict而不是把整个模型对象序列化。8. 最佳实践与工程建议到这里Tensor 的创建、属性、基本操作已经讲完。但“能运行”和“能在项目里用得好”之间还有一段距离。下面几条工程建议来自实际项目中反复踩坑后的总结。第一统一管理 device。不要把cuda字符串散落在代码各处。在项目入口定义一次device所有模型和 Tensor 都通过to(device)迁移。这样当环境从 GPU 切到 CPU 时只需要改一处而不是全文搜索修改。device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) x x.to(device)第二创建 Tensor 时显式指定 dtype。尤其是在做科学计算、模型推理时依赖默认 dtype 很危险。一个从列表创建的整数 Tensor可能会让后续的浮点运算直接报错。写dtypetorch.float32只多打几个字却能节省大量排查时间。第三注重实验可复现性。涉及随机张量的深度学习实验需要在代码入口固定随机种子否则不同次运行的初始化不同实验结果难以对比。import random import numpy as np import torch def set_seed(seed: int 42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed)第四在边界处多打印 shape。深度学习调试最大的技巧就是在数据流的关键节点打印 Tensor 的shape。不要凭感觉认为某个中间结果是二维还是三维直接用一行print(x.shape)确认。很多模型结构错误都是靠这种“shape 卫生习惯”提前发现的。第五不要盲目追求最新版本。PyTorch 版本更新较快新版本往往伴随行为变化比如前面提到的weights_only默认值调整。在企业项目里建议锁定已验证过的版本并在升级前先阅读官方 breaking change 说明和迁移指南。第六保存模型参数时优先使用 state_dict。检查点文件只保存模型参数字典而不是整个模型对象。这样做的好处是文件更小、加载更安全并且可以灵活地配合不同模型定义代码使用。# 保存 torch.save(model.state_dict(), model.pth) # 加载 model MyModel() model.load_state_dict(torch.load(model.pth, weights_onlyTrue))9. 下一步可以怎么练这一课只是 PyTorch 学习路线的一个起点。Tensor 相当于深度学习世界的“数据类型”下一步建议按这样的顺序继续第一学习自动求导。Tensor 的requires_grad和backward()是理解神经网络训练的关键只有掌握了梯度如何从损失函数传播回模型参数才能真正理解反向传播。第二学习 Dataset 和 DataLoader。真实项目中的数据不会一次性全部塞进 GPU 显存而是通过 DataLoader 按 batch 读取。你会发现DataLoader 返回的每个 batch本质上就是一个 Tensor。第三学习用 nn.Module 搭建模型。从线性回归开始慢慢到 MLP、CNN、RNN。这个阶段你会频繁用到view、transpose、unsqueeze、cat等操作那时再回头看这篇文章会有种“原来当时学的都是为了这里”的感觉。第四可以做一个小型实战项目。比如用 PyTorch 实现手写数字识别或者尝试时间卷积网络与 Transformer 结合的时序预测任务。这类项目能把 Tensor 的 shape 操作、模型搭建、训练循环串联起来对综合能力的提升很有帮助。Tensor 的学习不需要一次性背下所有 API也不建议死记硬背函数签名。更好的方式是把这篇文章当作地图先跑通示例再在遇到问题时按图索骥。收藏备用等你开始写模型时再回来对照每一类操作的 shape 变化你会发现自己已经能看懂 PyTorch 里的大多数代码了。