
上一篇我们已经知道深度学习模型真正处理的是一块一块的 Tensor。但仅仅知道 Tensor 是什么还远远不够。真正开始接触 PyTorch 后很快就会遇到x.shape x.ndim x.reshape(...) x.unsqueeze(...) x.permute(...)以及各种看起来有点奇怪的数字[3] [32, 3] [3, 224, 224] [32, 3, 224, 224]问题来了这些数字到底代表什么为什么同样是 Tensor有的是一维有的是二维、三维甚至四维为什么有时候只是把几个数字换个位置程序就可能无法正常处理这篇我们就把 Tensor 最核心的几个概念一次讲清Shape、Dimension、Axis、Batch以及常见的 Tensor 变形操作。而且从这一篇开始要慢慢培养一个之后会反复用到的习惯看到 Tensor第一反应先看 Shape。01Shape 到底是什么先看一个最简单的 Tensorimport torch x torch.tensor([ [1, 2, 3], [4, 5, 6] ])它其实就是这样一张数字表1 2 3 4 5 6有2 行 3 列所以print(x.shape)会得到torch.Size([2, 3])也就是Shape [2, 3]可以先把 Shape 理解成这个 Tensor 在每一个维度上分别有多大。Shape 不等于元素数量这里很容易出现一个误区。例如[2, 3]一共有2 × 3 6个元素。而[6]也有 6 个元素。但两者明显不是同一种结构[2, 3] [ [1, 2, 3], [4, 5, 6] ]而[6] [1, 2, 3, 4, 5, 6]所以元素数量相同不代表 Shape 相同。Shape 真正描述的是这些数字是怎样组织起来的。02Dimension 和 Axis 又是什么有了 Shape接下来两个词就很好理解了Dimension Axis还是Shape [2, 3]Shape 里一共有两个数字所以这是一个2D Tensor也就是说Dimension 2与此同时这两个维度分别有自己的编号Axis 0 Axis 1对应Shape [2, 3] ↑ ↑ Axis0 Axis1因此Axis 0 的大小 2 Axis 1 的大小 3可以用一句话区分Shape 看大小Dimension 看总数Axis 看编号。下面这张图把三者放到了一起为什么 Axis 从 0 开始因为 Python 的索引通常就是从 0 开始的。例如Shape [32, 3, 224, 224]那么Axis 0 → 32 Axis 1 → 3 Axis 2 → 224 Axis 3 → 224所以以后看到x.shape[0] x.shape[1]本质上就是查看某一个 Axis 的大小。03Shape 里的数字其实都有自己的“语义”真正重要的不是2 3 32 224这些数字本身。而是每一个位置上的数字到底表示什么。比如我们想描述一个学生。记录三个信息身高 175 cm 体重 65 kg 年龄 20 岁可以写成[175, 65, 20]它的 Shape[3]这里的 3 表示一个样本有 3 个特征。那么如果现在有 32 个学生呢就可以把它们放到一起[ [175, 65, 20], [168, 55, 19], [182, 70, 21], ... ]Shape 就会变成[32, 3]此时Axis 0 → 样本 Axis 1 → 特征也就是说32 个样本每个样本有 3 个特征。而最前面的这个 32就引出了深度学习中另一个非常常见的概念Batch04Batch 到底是什么Batch 中文通常翻译成批次但它本身一点都不复杂。一个学生[175, 65, 20]Shape 是[3]32 个学生放在一起[32, 3]其中32 Batch Size所以可以先用一个非常直观的方式理解Batch 就是把多个样本放到一起处理。对应关系一个样本 [N] ↓ 多个样本 [B, N]其中B Batch N Feature这张图把“一个样本”和“一批样本”的区别画得很直观现在先知道 Batch 是“多个样本放在一起”就足够了。至于为什么模型训练时通常喜欢一批一批处理而不是一条一条处理我们等真正开始学习模型训练时再详细展开。05图片的数据又是怎么组织的前面的学生数据比较简单[32, 3]但图片比它多了一层空间结构。先从灰度图开始。假设一张图片28 × 28每个位置都是一个像素值。那么它可以表示成[28, 28]也就是[H, W]其中H Height W Width所以灰度图本质上可以看成一张二维数字表。06RGB 图片为什么多一个维度彩色图片通常由三个颜色通道组成R Red G Green B Blue因此一张 RGB 图片可以理解为红色信息 绿色信息 蓝色信息所以它的 Shape 不再只是[H, W]而可能是[C, H, W]例如一张224 × 224的 RGB 图片[3, 224, 224]其中3 → Channel 224 → Height 224 → Width07再加一个 Batch 会怎样一张 RGB 图片[3, 224, 224]如果一次放入 32 张[32, 3, 224, 224]也就是[B, C, H, W]对应B → Batch C → Channel H → Height W → Width所以[32, 3, 224, 224]可以直接读成32 张图片每张有 3 个颜色通道每个通道大小为 224 × 224。从灰度图到 RGB再到一批图片可以用下面这张图一次看懂08为什么 Shape 的位置不能随便换来看[32, 3, 224, 224]和[32, 224, 224, 3]里面的元素总数其实完全相同。但是它们表达的数据组织方式并不一样。如果我们约定[B, C, H, W]那么[32, 3, 224, 224]表示32 个样本 3 个 Channel 224 高 224 宽但[32, 224, 224, 3]如果还按照同样的规则解释就会变成32 个样本 224 个 Channel 224 高 3 宽含义已经完全变了。所以一定要建立这个意识Shape 不只是“几个数字”数字所在的位置同样重要。09为什么 Tensor 经常需要“变形”真实的数据并不一定总是以我们想要的形式出现。有时候我们需要[2, 3]变成[6]有时候需要[28, 28]变成[1, 28, 28]还有时候数据本来是[224, 224, 3]我们却需要[3, 224, 224]因此 PyTorch 中有很多专门调整 Shape 的操作。最常见的包括reshape view squeeze unsqueeze transpose permute flatten其实不用一个一个死记。它们大致就在做三件事情重新组织、增加/删除维度、交换维度。10reshape重新组织 Shape例如x torch.tensor([ [1, 2, 3], [4, 5, 6] ]) y x.reshape(6)原来的 Shape[2, 3]现在变成[6]但是数字依然是1 2 3 4 5 6所以可以先把 reshape 理解为元素本身没有凭空增加或减少只是重新组织。例如[2, 3] ↓ [3, 2] ↓ [6]它们的元素总数都是6view 呢PyTorch 中还经常看到x.view(...)入门阶段可以把它和 reshape 理解为同一类操作都是改变 Tensor 的 Shape。它们更细的区别以后真正遇到时再讲不需要现在增加额外负担。11unsqueeze 和 squeeze假设现在[28, 28]想变成[1, 28, 28]可以使用x x.unsqueeze(0)也就是增加一个大小为 1 的维度。反过来[1, 28, 28]变回[28, 28]可以理解成 squeeze去掉大小为 1 的维度。所以unsqueeze → 加一个大小为 1 的 Axis squeeze → 去掉一个大小为 1 的 Axis12transpose 和 permute有时候数据内容没有变化我们只是想交换不同 Axis 的位置。例如[H, W, C]变成[C, H, W]也就是[224, 224, 3] ↓ [3, 224, 224]这时候经常会用到transpose(...)或者permute(...)可以先把它们理解成重新排列维度的顺序。13flatten把多维数据“摊平”比如[2, 3]可以变成[6]原来的[ [1, 2, 3], [4, 5, 6] ]被展开成[1, 2, 3, 4, 5, 6]这就是Flatten。以后学习全连接神经网络时我们会真正看到它为什么有用。现在只需要知道Flatten 的核心就是把多个维度合并起来。如果觉得这些操作容易混可以直接保存下面这张速查图14除了 ShapeTensor 还有 dtypeTensor 不仅要知道长什么样还需要知道里面的数字是什么类型。这就是dtypePyTorch 中可以查看x.dtype常见的例如float32 int64简单理解float32 → 浮点数 int64 → 整数例如1.2 3.5 -0.7属于浮点数。而0 1 2 3可以用整数表示。以后开始训练模型时我们会遇到“为什么某些数据必须是 float某些标签却经常是 int64”这样的问题。现在先知道 dtype 是 Tensor 的一个重要属性即可。15Tensor 还会存在哪里除了Shape dtypeTensor 还有一个很现实的问题它现在存在哪台计算设备上例如CPU或者GPUPyTorch 中可以通过x.device查看。也经常会看到x x.to(device)意思就是把 Tensor 移动到指定设备上。所以到这里可以先建立一个比较完整的 Tensor 认识Tensor │ ├─ 数值 ├─ Shape ├─ dtype └─ device至于为什么深度学习喜欢 GPU、CUDA 又是什么我们等真正开始训练网络时再讲。16真正重要的是建立“Shape 思维”到这里比记住几个 API 更重要的是开始学会读一个 Tensor。例如看到[32, 3]不要只看到32 和 3而是应该问Axis 0 是什么 Axis 1 是什么假如这是[B, N]那么32 → Batch 3 → Feature再看到[32, 3, 224, 224]也应该尝试读成Axis 0 → Batch Axis 1 → Channel Axis 2 → Height Axis 3 → Width这就从“看一串数字”慢慢变成了“看数据结构”。17以后看到一个 Tensor先问这 4 个问题这是我非常建议以后一直保留的习惯。第一步Shape 是什么例如[32, 10]第二步一共有几个 DimensionShape 里有两个数字2D Tensor第三步每个 Axis 分别是什么意思比如Axis 0 → Batch Axis 1 → Feature第四步下一步操作改变了哪个 Axis例如[32, 10] ↓ [32, 64]可以先观察到32 没变 10 → 64也就是说Batch 没变 Feature 发生变化下一篇开始学习神经元和全连接网络之后我们就会真正遇到这种变化。下面这张图可以作为这一篇最后的总结记住一句话不要只看 Tensor 里有几个数字更要看这些数字在描述怎样的数据结构。18这一篇真正需要记住什么这一篇知识点很多但真正需要带走的其实只有下面几件事。① Shape 描述 Tensor 的结构[2, 3]表示第一个维度大小为 2 第二个维度大小为 3② Dimension 看一共有几个维度例如[32, 3, 224, 224]有四个数字所以是4D Tensor③ Axis 是具体某一个维度Axis 0 Axis 1 Axis 2 Axis 3④ Batch 就是把多个样本放到一起一个样本 [N] ↓ 一批样本 [B, N]⑤ 图片通常具有空间维度和 Channel灰度图 [H, W] RGB 图片 [C, H, W] 一批 RGB 图片 [B, C, H, W]⑥ Tensor 的 Shape 可以改变常见操作reshape / view squeeze / unsqueeze transpose / permute flatten但本质都围绕怎样重新组织数据。⑦ 真正应该养成的是 Shape 思维以后拿到一个 Tensor先看 Shape再看每一个 Axis 代表什么。这会成为后面理解神经网络最重要的基本功之一。接下来会学什么到这里我们已经知道 Tensor 是怎样组织数据的也开始建立了 Shape、Axis 和 Batch 的基本认识。接下来我们会正式进入神经网络本身看看一个最基础的神经元到底怎样接收输入、使用权重和偏置并最终得到一个输出。也就是从“数据是什么样的”逐渐走向“这些数据进入神经网络以后到底发生了什么”从这里开始Tensor 就不再只是被我们观察的数据结构而会真正参与到神经网络的计算过程中。