张量是什么?机器学习中张量的核心概念与PyTorch实战详解

张量是什么?机器学习中张量的核心概念与PyTorch实战详解 很多刚接触机器学习的同学在看教程或者读框架源码时经常会遇到“张量”这个词比如“把输入数据转换成张量”“模型的权重是一个四维张量”。刚开始听到张量总觉得它是什么高深的数学概念好像需要很强的线性代数功底才能理解。实际上在学习机器学习的过程中张量并没有想象中那么可怕它本质上就是一种“装数据”的容器只是比我们熟悉的数组、矩阵更通用、更灵活。这篇文章会从最基础的数据形态讲起逐步拆解张量的含义、形状、维度、运算规则并且结合 NumPy 和 PyTorch 给出大量可运行的代码示例。无论你是刚入门机器学习的新手还是想系统梳理张量概念的开发者这篇文章都能帮你建立一套清晰的理解框架。学完之后你至少能看懂 PyTorch 模型里的 shape 变化能自己创建、变形、运算张量也知道了常见的广播机制和形状报错该怎么排查。1. 什么是张量从数据形态说起1.1 从标量、向量、矩阵到张量在理解张量之前我们先回顾一下数学里常见的数据组织形式。标量Scalar一个单独的数比如 5、3.14。向量Vector一组有序排列的数比如 [1, 2, 3]它是一维的。矩阵Matrix一个二维的数表比如一个 2 行 3 列的二维数组。张量Tensor维度大于等于 0 的数组统称也就是标量、向量、矩阵都是张量的特例。从这个角度来看张量是一个更大的概念。你可以把标量理解为“0 维张量”向量是“1 维张量”矩阵是“2 维张量”。当数据的维度超过 2 时比如一个形状为 (2, 3, 4) 的数据它就很难再被叫作矩阵了这时候用“张量”来描述是最合适的。换句话说张量就是“广义化的数组”它可以拥有任意多个维度每一个维度都代表数据的一种排列方式。1.2 张量的正式定义与记号在数学中张量可以理解为向量和矩阵向更高维度的推广。不过在机器学习的工程语境里我们通常不讨论太多微分几何里的张量定义而是把它当作“多维数组”来使用。一个张量有几个关键属性阶数Rank也叫维度数量。标量的阶数是 0向量的阶数是 1矩阵的阶数是 2。形状Shape每个维度的大小。比如形状为 (2, 3) 的张量表示有 2 行 3 列。数据类型Dtype元素的数据类型比如整数、浮点数、布尔值。在 PyTorch 中我们可以通过tensor.ndim查看张量的维度数量通过tensor.shape查看形状。NumPy 中也类似用array.ndim和array.shape。import numpy as np a np.array([1, 2, 3]) print(维度数量:, a.ndim) print(形状:, a.shape) print(数据类型:, a.dtype)输出维度数量: 1 形状: (3,) 数据类型: int64可以看到这个一维数组的维度数量是 1形状是 (3,)表示有 3 个元素。1.3 为什么机器学习离不开张量机器学习模型的本质就是“输入数据经过一系列数学运算输出预测结果”。而计算机处理数学运算时最有效率的数据组织形式就是数组和矩阵。神经网络里大量的矩阵乘法、逐元素运算、梯度计算都需要统一的数据结构来承载。张量之所以成为机器学习的核心数据结构原因可以概括为三点。第一统一性。图像、文本、表格、音频最终都可以转换成张量。一张彩色图片可以表示成 (高度, 宽度, 通道数) 的三维张量一段文本可以表示成 (句子长度, 词向量维度) 的二维张量。第二并行计算。GPU 擅长对大规模张量做并行运算深度学习框架把计算组织成张量运算之后可以高效利用 GPU 加速。第三自动求导。现代深度学习框架基于张量构建计算图张量在参与运算时会记录操作历史框架可以自动计算梯度这是反向传播算法的基础。因此想学好机器学习尤其是深度学习理解张量是绕不开的一步。2. 环境准备与工具选择2.1 Python 环境本文的示例代码主要使用 Python 和常见的科学计算库。建议使用 Python 3.8 或更高版本具体版本可以根据你的项目需求调整。如果你还没有配置环境推荐使用 Anaconda 或 Miniconda 创建独立的虚拟环境避免不同项目之间依赖冲突。conda create -n tensor-tutorial python3.10 conda activate tensor-tutorial2.2 NumPy 与 PyTorch 的作用本文会用到两个库NumPyPython 科学计算的基础库提供了高性能的多维数组对象ndarray也是很多数据分析、机器学习库的底层依赖。PyTorch主流的深度学习框架其核心数据结构就是torch.Tensor和 NumPy 的数组非常相似但额外支持自动求导和设备迁移CPU/GPU。安装命令如下pip install numpy pip install torch如果你的电脑有 NVIDIA 显卡并安装了 CUDA建议按照 PyTorch 官网的提示安装对应 CUDA 版本的 PyTorch如果只是为了学习张量概念CPU 版本的 PyTorch 完全够用。2.3 示例项目结构为了方便实验建议新建一个目录里面放一到两个 Python 脚本或者直接用 Jupyter Notebook 逐段运行。tensor-tutorial/ ├── tensor_basics.py └── tensor_operations.py本文的代码如果标注了tensor_basics.py就表示这部分代码可以集中放在该文件里运行。接下来的内容我会先介绍张量的核心属性再逐步展开创建、变形、运算和应用。3. 张量的核心属性形状、维度与轴3.1 轴的编号规则在张量中“轴”Axis和“维度”Dimension经常混用它们指的都是数据排列的“方向”。轴是从 0 开始编号的。看一个简单的三维张量import numpy as np x np.array([ [[1, 2], [3, 4]], [[5, 6], [7, 8]] ]) print(x.shape) # 输出 (2, 2, 2)这个张量的形状是 (2, 2, 2)含义是第 0 轴长度为 2表示外层有 2 个“块”。第 1 轴长度为 2表示每个块里有 2 行。第 2 轴长度为 2表示每行有 2 个元素。用坐标来理解x[0, 1, 0]表示第 0 个块、第 1 行、第 0 列的元素也就是数字 3。轴编号的顺序决定了你在索引时怎么定位数据。3.2 理解形状 shape形状是张量最直观的描述它用一个元组表示每个轴的长度。比如(3,)表示一维张量共 3 个元素。(2, 3)表示二维张量2 行 3 列。(4, 2, 3)表示三维张量可以理解为 4 个 2 行 3 列的矩阵堆叠在一起。在 PyTorch 中形状同样非常重要。像nn.Linear、nn.Conv2d这类网络层输入输出形状必须严格对应形状不匹配会直接报错。3.3 张量的数据类型张量里的元素有不同类型。常见的包括整数型int32、int64浮点型float32、float64布尔型bool深度学习模型默认多使用float32因为它在精度和计算速度之间取得了平衡。某些场景下比如数据处理阶段用整数索引模型计算阶段则转成浮点数。import torch a torch.tensor([1, 2, 3]) b torch.tensor([1.0, 2.0, 3.0]) print(a.dtype) # torch.int64 print(b.dtype) # torch.float32这里需要注意PyTorch 创建整数列表后默认是int64创建带小数的列表后默认是float32这和 NumPy 的默认规则略有差异。4. 张量的创建与基本操作4.1 使用 NumPy 创建张量NumPy 提供了非常丰富的数组创建函数。import numpy as np # 从 Python 列表创建 a np.array([[1, 2, 3], [4, 5, 6]]) # 全零数组 zeros np.zeros((2, 3)) # 全一数组 ones np.ones((2, 3)) # 指定数值填充 full np.full((2, 3), 7) # 随机数组 random_arr np.random.rand(2, 3) # 等差数列 arange_arr np.arange(12).reshape(3, 4) print(a:, a.shape) print(zeros:, zeros.shape) print(full:\n, full)这些函数在数据预处理中非常常用比如初始化权重、创建掩码、生成测试数据等。4.2 使用 PyTorch 创建张量PyTorch 的创建方式和 NumPy 很像常见的有import torch # 从列表创建 a torch.tensor([[1, 2], [3, 4]]) # 全零 zeros torch.zeros(2, 3) # 全一 ones torch.ones(2, 3) # 随机均匀分布 rand torch.rand(2, 3) # 随机正态分布 randn torch.randn(2, 3) # 和 NumPy 互相转换 np_arr a.numpy() back_to_torch torch.from_numpy(np_arr) print(zeros:, zeros.shape) print(rand:, rand)在 PyTorch 中还有一个非常常用的函数torch.arange它和 Python 的range类似但返回的是张量。x torch.arange(24).reshape(2, 3, 4) print(x.shape) # torch.Size([2, 3, 4])4.3 索引与切片张量的索引和 Python 列表的切片语法非常相似但因为轴变多了需要理解每个位置对应哪个轴。import numpy as np x np.arange(12).reshape(3, 4) print(x) # 取第 1 行 print(x[1]) # 取第 1 行第 2 列 print(x[1, 2]) # 取所有行的第 2 列 print(x[:, 2]) # 取前 2 行的后 2 列 print(x[:2, 2:])输出[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [ 4 5 6 7] 6 [ 2 6 10] [[2 3] [6 7]]多维切片的关键就是记住每一维都独立写一个切片范围用逗号隔开。:表示这一维全部保留:两边的数字表示起止位置和 Python 列表规则一致。5. 张量的变形与维度变换5.1 reshape 与 viewreshape是改变张量形状最常用的方法它把张量按某种顺序重新排列成新的形状前提是元素总数必须一致。import numpy as np import torch # NumPy x np.arange(12) y x.reshape(3, 4) print(y) # PyTorch xt torch.arange(12) yt xt.view(3, 4) print(yt)PyTorch 中view和reshape都用于变形但实现机制略有不同。view要求原张量在内存中是连续的而reshape在必要时会自动复制数据。如果你不确定优先使用reshape更稳妥。还有一种常见情况是通过-1让框架自动推断某一维的大小。x np.arange(12) y x.reshape(-1, 4) # 自动算出行数是 3 print(y.shape) # (3, 4)这种方式在写数据加载、批处理时非常实用不需要手动计算形状。5.2 增删维度squeeze 与 unsqueezesqueeze用于删除长度为 1 的维度unsqueeze用于在指定位置增加一个长度为 1 的维度。import torch x torch.randn(1, 3, 1, 4) # 删除所有长度为 1 的维度 y x.squeeze() print(y.shape) # torch.Size([3, 4]) # 在指定位置加维度 z x.unsqueeze(0) print(z.shape) # torch.Size([1, 1, 3, 1, 4])为什么需要增加长度为 1 的维度因为某些模型层要求输入是四维张量比如卷积层期望形状为 (批次大小, 通道数, 高度, 宽度)。当你只有一张灰度图时数据形状可能是 (高度, 宽度)就需要先unsqueeze(0)增加通道维度再unsqueeze(0)增加批次维度才能送入模型。5.3 转置 transpose 与 permutetranspose交换两个指定的维度permute可以按照任意顺序重排所有维度。import torch x torch.randn(2, 3, 4) # 交换第 0 和第 2 维 y x.transpose(0, 2) print(y.shape) # torch.Size([4, 3, 2]) # 重排所有维度 z x.permute(2, 0, 1) print(z.shape) # torch.Size([4, 2, 3])这在实际项目中很常见。比如一张图片在读取时形状可能是 (高度, 宽度, 通道数)而 PyTorch 的卷积层期望的是 (通道数, 高度, 宽度)这时候就需要用transpose或permute调整维度顺序。# 假设读入的图片张量形状为 (H, W, C) image torch.randn(224, 224, 3) # 转换为 (C, H, W) image_t image.permute(2, 0, 1) print(image_t.shape) # torch.Size([3, 224, 224])5.4 拼接与分割拼接可以用torch.cat在某个轴上把多个张量连接起来也可以用torch.stack增加一个新维度后堆叠。import torch a torch.randn(2, 3) b torch.randn(2, 3) # 沿第 0 轴拼接 cat0 torch.cat([a, b], dim0) print(cat0.shape) # torch.Size([4, 3]) # 沿第 1 轴拼接 cat1 torch.cat([a, b], dim1) print(cat1.shape) # torch.Size([2, 6]) # stack 会增加一个新维度 stacked torch.stack([a, b], dim0) print(stacked.shape) # torch.Size([2, 2, 3])这两个操作在构建批数据、合并特征时经常出现。需要强调的是cat要求除拼接轴外其他维度完全一致否则会报错。6. 张量运算与广播机制6.1 逐元素运算张量可以直接参与加减乘除运算符会对每个位置的元素独立计算。import torch a torch.tensor([1, 2, 3]) b torch.tensor([4, 5, 6]) print(a b) # tensor([5, 7, 9]) print(a * b) # tensor([4, 10, 18]) print(a ** 2) # tensor([1, 4, 9]) print(torch.exp(a)) # 指数运算这里要注意*是逐元素相乘不是矩阵乘法。矩阵乘法用或者torch.matmul。6.2 矩阵乘法矩阵乘法要求第一个矩阵的列数等于第二个矩阵的行数。import torch A torch.randn(2, 3) B torch.randn(3, 4) C A B # 等价于 torch.matmul(A, B) print(C.shape) # torch.Size([2, 4])神经网络中的全连接层本质上就是输入张量和权重矩阵做矩阵乘法再加上偏置。理解矩阵乘法的维度变化是看懂模型结构的基础。6.3 广播机制详解广播Broadcasting是 NumPy 和 PyTorch 中非常重要且容易困惑的机制。简单来说当两个张量形状不完全一致时框架会尝试自动扩张较小的张量使它们能够进行运算。import torch a torch.tensor([1, 2, 3]) b torch.tensor(10) print(a b) # tensor([11, 12, 13])10 被广播到每个元素再看一个二维的例子m torch.arange(6).reshape(2, 3) row torch.tensor([10, 20, 30]) print(m row)输出tensor([[10, 21, 32], [13, 24, 35]])这里row形状是 (3,)m形状是 (2, 3)框架把row沿第 0 轴广播相当于复制了一份和每一行相加。广播规则可以概括为从最后一个维度开始比较如果两个维度相等或者其中一个是 1就可以继续匹配如果既不等长也不是 1就无法广播会报错。# 例子形状 (3, 1) 与 (1, 4) 可以广播成 (3, 4) x torch.randn(3, 1) y torch.randn(1, 4) z x y print(z.shape) # torch.Size([3, 4])广播机制能减少很多显式的复制操作让代码更简洁、内存占用更低但如果不理解它很容易写出形状不匹配的代码或者得到意想不到的结果。7. 机器学习中的张量实战场景7.1 批处理数据一次喂入多个样本在训练模型时我们通常不会一次只输入一个样本而是把一个批次Batch的样本同时送入模型。假设每个样本是一个 64 维的特征向量批次大小为 32那么输入张量的形状就是 (32, 64)。import torch batch_size 32 feature_dim 64 inputs torch.randn(batch_size, feature_dim) print(inputs.shape) # torch.Size([32, 64])模型权重通常也是一个矩阵形状为 (输出特征数, 输入特征数)。矩阵乘法得到的结果形状为 (32, 输出特征数)每一个样本对应一行输出。output_dim 10 weight torch.randn(output_dim, feature_dim) outputs inputs weight.T print(outputs.shape) # torch.Size([32, 10])这里的weight.T是权重矩阵的转置这样矩阵乘法才能正确匹配维度。7.2 图像数据通道、高度、宽度图像数据是理解高维张量最经典的例子。一张 RGB 彩色图片如果高度为 224、宽度为 224它有 3 个颜色通道那么单个图片可以表示为形状 (3, 224, 224) 的张量。一个批次 16 张图片形状就是 (16, 3, 224, 224)。import torch batch_size 16 channels 3 height 224 width 224 images torch.randn(batch_size, channels, height, width) print(images.shape) # torch.Size([16, 3, 224, 224])卷积神经网络处理的就是这种四维张量。卷积核在高度和宽度方向上滑动同时跨通道聚合信息最终输出的特征图仍然是张量。7.3 序列文本批次、步长、特征自然语言处理中一段文本经过分词和词向量映射后会转换成一个形状为 (序列长度, 词向量维度) 的二维张量。一个批次的多条文本形状就是 (批次大小, 序列长度, 词向量维度)。batch_size 8 seq_len 50 embed_dim 128 texts torch.randn(batch_size, seq_len, embed_dim) print(texts.shape) # torch.Size([8, 50, 128])这里的第 0 轴是批次方向第 1 轴是序列的时间步方向第 2 轴是每个词的特征方向。RNN、LSTM、Transformer 等模型都会操作这种三维张量。7.4 神经网络参数权重与偏置张量神经网络的每一层参数同样以张量形式存储。比如二维卷积层的卷积核形状通常为 (输出通道数, 输入通道数, 卷积核高, 卷积核宽)全连接层的权重是二维矩阵偏置是一维向量。import torch.nn as nn linear nn.Linear(in_features64, out_features10) print(linear.weight.shape) # torch.Size([10, 64]) print(linear.bias.shape) # torch.Size([10])训练过程中PyTorch 会为这些参数张量自动计算梯度并更新它们的值。可以说模型训练本质上就是不断调整这些张量参数的过程。8. 常见问题与排查思路在实际操作张量时会遇到各种各样的报错。为了帮你快速定位问题这里整理了一张排查表。问题现象常见原因解决思路shape mismatch报错两个张量在某个维度上大小不一致无法直接运算或拼接打印两个张量的shape确认需要对齐的维度必要时使用reshape、transpose或unsqueeze调整广播时意外变成大矩阵不理解广播规则维度被自动扩展结果与预期不符打印中间结果的形状逐步确认每一步的形状变化view报错is not contiguous张量在transpose或permute后内存不连续无法直接view改用reshape或先调用contiguous()再view张量在 GPU 上运算时报设备不一致部分张量在 CPU部分在 GPU无法直接混合运算用.to(device)统一设备确保参与运算的张量在同一个设备上NumPy 张量和 PyTorch 张量混用报错两个库的数据结构不能直接做运算用torch.from_numpy()转成 PyTorch 张量或用.numpy()转回 NumPy 数组矩阵乘法维度不对左侧张量的最后一维不等于右侧张量的倒数第二维检查矩阵乘法的维度匹配条件必要时对权重做转置或重排索引结果维度减少用单个整数索引某一维时该维度会被去除如果需要保留维度使用切片写法比如x[0:1]代替x[0]排查形状类问题时最有效的办法就是在报错位置前后打印print(x.shape)。把每个关键步骤的形状都看清楚问题往往很快就能定位。9. 最佳实践与工程建议9.1 养成注释形状的习惯在写模型或数据处理代码时建议在关键张量操作附近用注释标明形状变化。这样做不仅能帮助自己理清逻辑也能让团队其他人快速理解代码。# 输入: (batch_size, 3, 224, 224) x x.permute(0, 2, 3, 1) # - (batch_size, 224, 224, 3) x x.reshape(batch_size, -1) # - (batch_size, 3*224*224)这种方式在排错时能节省大量时间。9.2 注意数据类型一致性模型输入通常要求float32而标签可能是整数类型。送入损失函数前要确认数据类型的匹配。用到 NumPy 和 PyTorch 混编时要显式转换避免运行时才暴露问题。9.3 内存与性能优化处理大张量时需要注意内存占用。批量创建的中间变量会占用大量显存因此不需要的中间结果要及时释放或者用del删除后调用torch.cuda.empty_cache()。能用inplace操作时谨慎使用因为它虽然省内存但会影响自动求导。尽量使用批量运算而不是循环循环处理张量在 Python 中性能很低。9.4 使用断言约束形状在数据加载或模型前向传播中可以用assert主动检查形状提前暴露错误而不是等到运算失败才排查。def preprocess_batch(images): assert images.ndim 4, f期望 4 维张量实际是 {images.ndim} 维 assert images.shape[1] 3, 通道数必须为 3 return images.float()9.5 安全操作与生产环境注意事项在真实项目中涉及数据变换时尽量在测试环境验证形状变化是否符合预期尤其是使用reshape时它不保证保持语义顺序。需要保持语义的业务数据建议优先使用transpose、permute等明确的维度变换操作。对大文件或大数据集做预处理时先在小规模数据上验证流程再扩展到全量数据避免算力浪费。9.6 善用官方文档与调试工具NumPy 和 PyTorch 的官方文档都非常完善。遇到不熟悉的函数可以先查看函数签名和参数说明。调试时可以用print、ipdb或者 PyTorch 的torch.set_printoptions控制输出格式让大张量更容易阅读。10. 总结与下一步学习建议本文从标量、向量、矩阵开始逐步引出了张量的定义讲解了形状、维度、轴、数据类型等核心属性并通过大量代码示例演示了张量的创建、索引切片、变形、维度重排、拼接、矩阵乘法和广播机制。最后结合图像、文本、批处理、网络参数等真实场景说明了张量在机器学习中的应用方式也整理了一份常见报错排查表。如果你能独立完成文中大部分示例并且能说出(32, 3, 224, 224)每个数字代表什么含义说明你已经掌握了张量的基本使用方法。下一步可以继续学习PyTorch 的autograd机制理解张量如何实现自动求导。Dataset和DataLoader的使用掌握真实数据如何转换成批张量。卷积层、循环神经网络等常见网络结构中的张量维度变化。使用 TensorBoard 或张量可视化工具观察模型中间层的输出。张量是机器学习中绕不开的基础概念也是连接数据与模型之间的桥梁。建议不要只看不练打开 Python 环境把文中的代码逐段运行一遍多打印几次shape很快就能建立起对张量的直觉。如果本文对你有帮助可以收藏备用也欢迎分享给正在学习机器学习的朋友。