拓冰建站拓冰建站
首页 / 资讯中心 / 正文

机器学习中的张量:从概念到 PyTorch 实战

现在很多刚接触机器学习的同学在翻开教材或者教程时第一个被卡住的概念往往不是“梯度下降”也不是“神经网络”而是一个听起来有点物理味的词——张量Tensor。尤其是当你看到“TensorFlow”这个框架名字时心里难免会想张量到底是什么为什么机器学习到处都在用它它和我们熟悉的数组、矩阵、向量又有什么关系本文将会围绕“机器学习中的张量”做一个深入但易懂的解析。我会先帮你把张量的概念拆开从标量、向量、矩阵一路推到张量再讲讲为什么深度学习框架都偏爱张量然后用 Python 代码带你实操创建和操作张量最后总结一些高频踩坑点和学习建议希望能帮你把这块基础补扎实。如果你是机器学习入门阶段的学习者或者学了一段时间但对张量仍然比较模糊那这篇文章应该能帮到你。1. 张量到底是什么从标量到张量的递进1.1 为什么一上来就要理解张量很多人学习机器学习最先接触的往往是“机器学习模型”“机器学习算法”这些宏观概念真正开始写代码后却发现输入数据、中间特征、模型参数几乎全都在用张量表示。比如一张彩色图片在计算机里不是“一张图”而是一个形状为(高, 宽, 通道数)的张量一段文本经过编码后会变成一个形状为(句子长度, 向量维度)的张量一个批量输入给神经网络的数据则往往是一个形状为(批量大小, 特征维度)的张量。如果不理解张量的维度、形状和运算规则后面看任何深度学习代码都会觉得吃力。所以把张量的概念吃透是机器学习学习路线里非常靠前的一步。1.2 对标量、向量、矩阵的回顾要理解张量可以先从我们熟悉的数学对象看起标量Scalar一个单独的数比如5、3.14。它没有方向也没有维度。在张量语境里标量可以看作“0 维张量”。向量Vector一组有序排列的数比如[1, 2, 3]。向量有方向和长度在几何上可以理解为空间中的一个点或箭头。在张量语境里向量是“1 维张量”。矩阵Matrix一个二维数组比如一个 2 行 3 列的表格。它可以看作多个向量按行或按列堆叠而成。在张量语境里矩阵是“2 维张量”。张量Tensor当维度继续增加变成 3 维、4 维甚至更高维时我们统称为张量。一个 3 维张量可以看作多个矩阵的堆叠一个 4 维张量可以看作多个 3 维张量的堆叠。用一个简单的对应表格来总结数学对象常见叫法张量维度示例形状标量数值0 维()向量一维数组1 维(3,)矩阵二维数组2 维(2, 3)3 维张量三维数组3 维(2, 3, 4)4 维张量四维数组4 维(2, 3, 4, 5)这里要注意一个容易混淆的地方在机器学习领域我们经常把“所有维度的数组”都叫张量包括 0 维的标量。但在某些数学教材里张量有更严格的变换定义。对于机器学习入门来说你完全可以先把张量理解为“多维数组的通用叫法”。1.3 张量的“维度”和“形状”在学习张量时有两个词经常混着用但含义略有不同维度Dimension / Rank指张量有多少个“轴”axis。形状Shape指每个轴上元素的数量。举个例子一个形状为(2, 3, 4)的张量它的维度是 3第一个轴上有 2 个元素第二个轴上有 3 个元素第三个轴上有 4 个元素。在 Python 生态里你通常可以通过ndim或dim()查看维度通过shape查看具体形状。这两个属性是排查问题时的第一检查点。2. 机器学习中为什么会用张量2.1 数据天然是结构化的机器学习处理的数据往往不是孤立的数值而是具有结构的高维数据。以图像为例一张 256×256 的彩色图片实际上是一个形状为(256, 256, 3)的 3 维数组。其中3表示红、绿、蓝三个颜色通道。如果不使用张量很难统一高效地表达这种多通道数据。再比如自然语言处理中的一个句子我喜欢机器学习分词后变成[我, 喜欢, 机器学习]再经过词向量映射会变成形状为(3, 向量维度)的 2 维张量。多个句子一起送入模型时还会再加一个批量维度变成 3 维张量。可以看到无论是图像、文本还是表格数据张量都能提供一种统一的结构化表达方式。这也是为什么几乎所有的深度学习框架都以张量为基本数据单位。2.2 张量运算非常适合并行计算机器学习模型的训练本质上是在做大量的矩阵乘法和逐元素运算。张量作为一个整体可以通过底层优化实现高效的向量化计算避免 Python 级别的for循环带来的性能开销。更关键的是张量运算可以很方便地映射到 GPU图形处理器上。GPU 拥有大量计算核心擅长同时处理成千上万个简单运算。把数据组织成张量后我们可以一次性把整个张量“搬”到显存中并用 GPU 并行完成计算训练速度可以比 CPU 快几个数量级。这也是为什么深度学习框架都围绕张量设计 APITensorFlow里的Tensor、PyTorch里的Tensor、JAX里的Array本质上都是张量在不同框架中的实现。2.3 张量与自动求导深度绑定现代的深度学习框架不仅把张量当作数据容器还会在张量背后记录运算历史从而支持自动求导Automatic Differentiation。以 PyTorch 为例当你把一个张量的requires_grad设置为True后框架会追踪该张量的所有运算并自动计算梯度。这对于反向传播来说至关重要模型的前向传播就是张量之间的层层运算反向传播则是沿着运算图逐层计算梯度。换句话说张量既是前向传播的载体也是反向传播的“计算图节点”。理解了张量你就同时理解了深度学习框架最核心的数据流机制。3. 用代码理解张量从 Python 列表到 PyTorch Tensor前面我们从概念上认识了张量接下来进入实操环节。我会分别用 Python 原生的嵌套列表、NumPy 数组以及 PyTorch 张量来展示张量的创建和基本操作。为了避免版本不一致带来的困惑本文示例以常见环境为例Python 3.10 及以上NumPy 1.xPyTorch 2.x如果你的版本略有不同一般不影响示例逻辑。安装命令如下pip install numpy torch3.1 用 Python 列表表示多维数据在没有任何第三方库时我们可以用嵌套列表近似表示多维数组# 标量一个数 scalar 5 # 向量一维列表 vector [1, 2, 3] # 矩阵二维嵌套列表 matrix [ [1, 2, 3], [4, 5, 6] ] # 3 维张量多个矩阵堆叠 tensor_3d [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ]这种写法虽然直观但问题是原生 Python 列表不支持批量运算、不支持 GPU 加速也没有统一的“形状”概念。你无法直接对两个嵌套列表做逐元素加法也无法高效获取某个轴的长度。因此在实际机器学习项目中我们会使用专门的库来创建和操作张量。3.2 用 NumPy 创建数组并查看形状NumPy 是 Python 数据科学生态中最基础的多维数组库。虽然严格来说 NumPy 数组和深度学习里的 Tensor 有一些区别但在概念上是完全类似的。import numpy as np # 创建一维数组 arr1 np.array([1, 2, 3]) print(arr1:, arr1) print(shape:, arr1.shape) print(ndim:, arr1.ndim) print(dtype:, arr1.dtype) print() # 创建二维数组 arr2 np.array([[1, 2, 3], [4, 5, 6]]) print(arr2:, arr2) print(shape:, arr2.shape) print(ndim:, arr2.ndim)运行结果arr1: [1 2 3] shape: (3,) ndim: 1 dtype: int64 arr2: [[1 2 3] [4 5 6]] shape: (2, 3) ndim: 2 dtype: int64从输出可以看到shape返回的是元组表示每个轴的大小ndim表示总共有几个轴。这个信息在后面排查维度匹配问题时非常重要。NumPy 也支持批量运算a np.array([[1, 2], [3, 4]]) b np.array([[5, 6], [7, 8]]) print(a b:\n, a b) print(a * b:\n, a * b) print(a b:\n, a b)其中*是逐元素乘法是矩阵乘法。初学者经常把这两者搞混后面我们会专门讲解。3.3 用 PyTorch 创建张量并理解基本属性PyTorch 是当前深度学习和机器学习实战中最流行的框架之一。它的核心数据单位就是torch.Tensor我们可以把它理解为“支持自动求导和 GPU 加速的 NumPy 数组”。3.3.1 从列表或 NumPy 数组创建张量import torch # 从 Python 列表创建 t1 torch.tensor([[1, 2, 3], [4, 5, 6]]) print(t1:\n, t1) print(shape:, t1.shape) print(ndim:, t1.ndim) print(dtype:, t1.dtype)输出t1: tensor([[1, 2, 3], [4, 5, 6]]) shape: torch.Size([2, 3]) ndim: 2 dtype: torch.int643.3.2 使用内置函数创建特殊张量# 全零张量 zeros torch.zeros(2, 3) print(zeros:\n, zeros) # 全一张量 ones torch.ones(2, 3) print(ones:\n, ones) # 随机张量数值在 [0, 1) 内 rand torch.rand(2, 3) print(rand:\n, rand) # 与现有张量相同形状的全零张量 like_zeros torch.zeros_like(t1) print(zeros_like:\n, like_zeros)这里要注意torch.rand生成的是均匀分布的随机数取值在[0, 1)之间。如果你希望生成标准正态分布的随机数可以使用torch.randn。3.3.3 张量的基本运算PyTorch 张量支持逐元素运算和矩阵运算x torch.tensor([[1.0, 2.0], [3.0, 4.0]]) y torch.tensor([[5.0, 6.0], [7.0, 8.0]]) # 逐元素加法 print(x y:\n, x y) # 逐元素乘法 print(x * y:\n, x * y) # 矩阵乘法 print(x y:\n, x y) # 求和、均值 print(x.sum():, x.sum()) print(x.mean():, x.mean()) print(x.max():, x.max())在机器学习中x y也就是矩阵乘法出现频率最高因为神经网络的每一层本质上就是一次矩阵乘法加上偏置和非线性激活。3.3.4 形状重排与转置实际处理数据时经常需要调整张量的形状。PyTorch 提供了一些非常常用的方法x torch.arange(12) # 从 0 到 11 的一维张量 print(x:, x) # 重新塑形为 3 行 4 列 x_reshaped x.reshape(3, 4) print(x_reshaped:\n, x_reshaped) # 转置 x_t x_reshaped.T print(x_reshaped.T:\n, x_t)运行结果x: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) x_reshaped: tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) x_reshaped.T: tensor([[ 0, 4, 8], [ 1, 5, 9], [ 2, 6, 10], [ 3, 7, 11]])这里需要提醒reshape的前提是元素总数不变。如果把一个包含 12 个元素的一维张量强行reshape(2, 5)就会因为元素个数不匹配而报错。4. 完整实战用张量实现一个小型线性层为了把前面的概念串起来我们来做一个小型实战手动实现一个最简单的线性层并用随机数据验证前向传播。这个例子虽然简单但它包含了机器学习和深度学习中最核心的张量操作创建输入张量。创建权重矩阵和偏置向量。执行矩阵乘法。添加偏置。使用激活函数。4.1 场景设定假设我们有一个批量大小为4的输入每个样本有3个特征。我们希望经过一个线性层将 3 维特征映射到 2 维输出。用公式表示就是output input weight.T bias其中input的形状是(4, 3)weight的形状是(2, 3)表示 2 个输出神经元每个神经元接收 3 个输入特征bias的形状是(2,)output的形状是(4, 2)这里之所以对weight做转置是因为我们希望矩阵乘法的结果是(4, 3) (3, 2) (4, 2)。4.2 完整代码import torch import torch.nn.functional as F # 1. 创建输入张量4 个样本每个样本 3 个特征 x torch.randn(4, 3) print(输入 x 的形状:, x.shape) # 2. 创建线性层的权重2 个输出神经元每个神经元连接 3 个输入特征 weight torch.randn(2, 3) bias torch.randn(2) # 3. 计算线性输出 linear_output x weight.T bias print(线性输出形状:, linear_output.shape) # 4. 应用 ReLU 激活函数 output F.relu(linear_output) print(经过 ReLU 后的输出:) print(output)代码解释torch.randn(4, 3)生成服从标准正态分布的随机数形状为(4, 3)。x weight.T是矩阵乘法。weight.T的形状是(3, 2)所以乘法的结果形状是(4, 2)。广播机制Broadcasting会自动把bias这个形状为(2,)的向量加到(4, 2)矩阵的每一行上。F.relu是 ReLU 激活函数它会把所有负数变为 0保留正数。这是神经网络中最常用的激活函数之一。4.3 预期输出说明运行这段代码你会看到类似下面的输出输入 x 的形状: torch.Size([4, 3]) 线性输出形状: torch.Size([4, 2]) 经过 ReLU 后的输出: tensor([[0.0000, 0.5134], [0.0000, 0.3121], [1.2345, 0.0000], [0.0000, 0.8765]])由于是随机初始化你的具体数值会不同但形状一定保持不变输入是(4, 3)输出是(4, 2)。4.4 扩展到真正的神经网络层实际编码中你通常不会手写线性层而是使用封装好的模块import torch.nn as nn linear_layer nn.Linear(in_features3, out_features2) output linear_layer(x)nn.Linear内部会自动创建weight和bias并按照正确的形状进行矩阵运算。也就是说我们在 4.2 中手写的代码正是nn.Linear底层逻辑的简化版。如果你想深入理解神经网络的本质强烈建议手动实现一次线性层再去看框架源码理解会完全不一样。5. 张量操作的常见错误与排查思路在实际编码中和张量相关的报错几乎都出现在“形状不匹配”和“数据类型不匹配”上。下面整理几个高频问题和排查方式。5.1 常见报错与解决方向问题现象常见原因解决思路RuntimeError: The size of tensor a (3) must match the size of tensor b (4)逐元素运算时两个张量形状不一致检查两个张量的shape调整形状或使用广播RuntimeError: mat1 and mat2 shapes cannot be multiplied矩阵乘法中前一个矩阵的列数不等于后一个矩阵的行数确认x y中x.shape[-1]是否等于y.shape[0]IndexError: dimension out of range访问了不存在的维度检查ndim确认dim参数是否越界RuntimeError: expected scalar type Float but found LongPyTorch 默认张量是int64但要求浮点运算使用.float()转换张量类型结果全为 0 或全为 NaN输入数据未归一化、学习率过大、梯度爆炸检查数据范围调整学习率检查是否出现除零设备不匹配Expected all tensors to be on the same device模型和数据分别位于 CPU 和 GPU使用model.to(device)和x.to(device)5.2 如何快速确认问题当代码报错时建议按以下顺序排查打印各张量的shape不要靠猜。打印dtype和device很多错误是类型或设备不一致导致的。从第一个报错往上找找到真正发生运算的那一行。对比公式如果是矩阵乘法确认乘法的前一个张量的最后一维和后一个张量的第一维是否一致。尝试缩小数据量用x[:2]之类的方式截取小批量数据便于观察问题。5.3 复现一个真实错误来看一个最常见的错误import torch a torch.randn(3, 4) b torch.randn(3, 4) # 错误示范试图做矩阵乘法但形状不支持 # c a b # 报错3x4 3x4 无法相乘 # 正确方式将 b 转置为 4x3 c a b.T print(c.shape) # torch.Size([3, 3])这个例子告诉我们做矩阵乘法前一定要确认形状是否满足“前一个列数 后一个行数”。6. 张量学习的关键概念广播机制、设备与自动求导6.1 广播机制Broadcasting广播是张量运算中非常实用的规则它允许形状不同的张量参与逐元素运算。PyTorch 和 NumPy 的广播规则类似从最后一个维度开始比较。满足以下任一条件即可广播两个维度相等或其中一个维度为 1或其中一个张量没有该维度。举例a torch.ones(4, 3) # 形状 (4, 3) b torch.randn(3) # 形状 (3,) c a b # 广播b 会先变成 (4, 3) 再相加 print(c.shape) # torch.Size([4, 3])理解广播可以让你写出更简洁的代码。但也要注意过度依赖广播有时会让代码可读性下降如果团队协作建议在关键位置加上形状注释。6.2 设备DeviceCPU 与 GPU在机器学习中“内存与张量对齐”是一个经常被忽略的话题。张量可以存储在 CPU 内存中也可以存储在 GPU 显存中。如果你的数据在 CPU 上但模型参数在 GPU 上就会报设备不匹配错误。常见的做法是device torch.device(cuda if torch.cuda.is_available() else cpu) x x.to(device) model model.to(device)在训练大模型时还要注意显存占用。如果你的输入批量过大即使形状正确也可能出现CUDA out of memory错误。此时可以减小batch_size或者使用梯度累积等技巧。6.3 自动求导张量的进阶能力PyTorch 张量最强大的特性之一就是自动求导。看一个简单例子x torch.tensor([2.0], requires_gradTrue) y x ** 2 3 * x 1 # 反向传播计算梯度 y.backward() # 查看梯度dy/dx 2*x 3当 x2 时梯度为 7 print(x.grad) # tensor([7.])这里的核心是requires_gradTrue告诉 PyTorch 追踪所有与x相关的运算。backward()会从y开始反向计算梯度并存入x.grad。由此可见深度学习中所有基于梯度的优化算法底层都离不开张量。如果你想深入机器学习原理张量是你绕不开的基础设施。7. 最佳实践与工程建议7.1 从第 0 维开始思考批量维在深度学习中几乎所有训练数据都会包含一个“批量大小batch size”维度并且通常位于第 0 维。例如图像数据(batch_size, height, width, channels)文本数据(batch_size, sequence_length, embedding_dim)表格数据(batch_size, num_features)这种统一习惯可以让代码更容易维护和复用。当你在框架中看到一个张量的第一个维度总是在变化时不用紧张那通常就是批量维。7.2 命名规范与形状注释张量代码最容易出问题的地方是“看不到形状”。建议在关键位置加上注释例如# x: (batch_size, seq_len, hidden_dim) # weight: (hidden_dim, num_classes) output x weight bias在工程实践中这种注释的成本极低但对于代码审阅和排错有很大帮助。7.3 控制随机种子机器学习实验需要可复现性。每次运行都生成不同随机数会降低调试效率。建议固定随机种子import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) set_seed(42)7.4 注意数据类型PyTorch 中默认的整数张量是torch.int64默认浮点张量是torch.float32。当你从 NumPy 读取数据时经常需要显式转换import numpy as np arr np.array([[1, 2], [3, 4]], dtypenp.float32) tensor torch.from_numpy(arr)使用.float()或.double()可以快速转换类型但要注意混合精度训练时框架对类型有专门要求。7.5 合理利用 GPU 但不必一开始就追求 GPU对于刚入门机器学习的同学并不建议一开始就纠结 GPU 环境。在 CPU 上用一个小数据集把张量的形状、维度、运算规则弄清楚比盲目上 GPU 更有效。只有当你开始训练稍大的模型、发现训练速度无法接受时再考虑使用 GPU 云服务或本地显卡。7.6 多参考官方文档中的张量操作前面介绍的reshape、transpose、matmul、sum等只是张量操作的冰山一角。学习张量时最可靠的资料其实是 PyTorch 官方文档中的Tensor章节以及 NumPy 官方文档中的Array章节。尤其是以下方法在机器学习项目中会反复用到torch.cat/torch.stack拼接和堆叠张量。torch.squeeze/torch.unsqueeze压缩和增加维度。torch.permute/torch.transpose维度换位。torch.mean/torch.max/torch.argmax聚合和取值。torch.clamp裁剪数值范围。把这些方法练熟再去看任何模型的源码都会轻松很多。8. 总结与后续学习路线到这里我们已经把“机器学习中的张量”做了一次比较全面的梳理从标量到向量再到矩阵然后引出张量的定义解释了为什么深度学习框架都以张量为核心用 NumPy 和 PyTorch 演示了创建张量、查看形状、执行运算的常见操作通过一个小型线性层实战把矩阵乘法、广播、激活函数串在了一起最后总结了遇到形状错误时的排查思路以及工程中的若干建议。下一步你可以继续沿着以下方向深入学习“自动求导”的底层原理理解计算结果和梯度计算的关系。学习“数据加载器”中张量的常见预处理方式例如归一化、标准化、one-hot 编码。学习卷积神经网络时重点关注图像张量形状的变化(batch_size, channels, height, width)。学习循环神经网络或 Transformer 时重点关注序列张量的维度。实践一个小型项目比如手写数字识别从数据读取到模型训练完整走一遍把张量的知识落实到实际代码中。希望这篇文章能成为你机器学习学习路线中一块稳固的垫脚石。如果你在实践过程中遇到和张量相关的报错不妨回到第 5 节按表格中的排查思路逐一检查。动手练习是掌握张量最好的方法。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门