第 1 章 认识 PyTorch
学习目标
- 知道 PyTorch 是什么、解决什么问题
- 完成 PyTorch 的安装并验证版本
- 会用
import torch导入并创建第一个张量 - 理解
torch.Tensor的三个基本属性:维数、形状、数据类型 - 理解「向量化」:张量运算作用于每个元素,不需要写循环
1.1 PyTorch 是什么
PyTorch 是 Facebook AI Research(现 Meta AI)开源的深度学习框架,核心是一个 Python 库。它的名字由两部分组成:Py(Python)和 Torch(火炬,一种早期科学计算框架的名字)。
PyTorch 提供三件套:
- 张量(tensor):一个存储同类型元素、支持整体运算的多维容器,用法与 NumPy 的数组类似,但默认数据类型是 32 位浮点数,且可以运行在 GPU 上。
- 自动求导(autograd):自动计算「损失对参数」的梯度——这是训练神经网络的核心机制,第 6 章展开。
- 神经网络库(
torch.nn):搭建网络、损失函数、优化器的现成组件,第 7~11 章展开。
它解决一个具体问题:用 Python 从零写深度学习又慢又啰嗦。比如把 100 万个数字每个加 1,用列表要写循环:
# 列表写法:必须逐个元素处理
nums = list(range(1_000_000))
result = [x + 1 for x in nums]用张量,一次运算作用于整个张量:
import torch
nums = torch.arange(1_000_000)
result = nums + 1nums + 1 内部用编译好的代码批量执行。在本机实测,100 万个元素各加 1,张量用时约 0.002 秒,列表循环约 0.045 秒——快 20 倍以上(具体数值随机器波动)。这种「对整块数据做运算,而不是逐个元素循环」的方式,叫向量化(vectorization)。
本书所有示例基于 PyTorch 2.x(写作时最新为 2.13.0)。代码统一在交互环境(REPL)或脚本文件中运行,约定运行方式用 uv run python(见 1.2)。
1.2 安装 PyTorch
1.2.1 用 uv 安装(推荐)
本书配套仓库使用 uv 管理 Python 环境。在项目根目录:
$ uv sync完成后验证:
$ uv run python -c "import torch; print(torch.__version__)"
2.13.0+cu130版本号末尾的 +cu130 表示这是带 CUDA 13.0 支持的构建。是否真的能用 GPU,以 1.3 的 torch.cuda.is_available() 为准。
1.2.2 用 pip 安装
如果你已有自己的 Python 环境,直接:
$ pip install torch
$ python3 -c "import torch; print(torch.__version__)"输出 2.x.x 即安装成功。后续所有示例,把 uv run python 换成你自己的 python3 即可。
1.3 导入 PyTorch 与创建第一个张量
导入是用 import 语句把库加载进当前程序。PyTorch 社区约定统一简写为 torch:
import torch张量用 torch.tensor 从列表创建。torch.tensor(列表) 把列表转换成一个 torch.Tensor:
import torch
scores = torch.tensor([85, 92, 78, 90, 88])
print(scores)输出:
tensor([85, 92, 78, 90, 88])注意 tensor(...) 是张量的打印格式,与列表的 [...] 不同。
张量可以和数直接做算术,结果仍是张量:
print(scores + 10) # 每个分数加 10
print(scores * 2) # 每个分数乘 2输出:
tensor([ 95, 102, 88, 100, 98])
tensor([170, 184, 156, 180, 176])+、* 逐元素作用于整个张量,这就是 1.1 说的向量化。对比列表:列表的 + 是拼接,* 是重复——语义完全不同,这是新手最容易混淆的地方。
1.4 张量的三个基本属性
每个 torch.Tensor 都有三个必知属性:ndim(维数)、shape(形状)、dtype(数据类型)。
维数(ndim)是张量有几层方括号:一维是向量,二维是矩阵。
形状(shape)是一个 torch.Size 对象,每个元素是该维度上的长度。
数据类型(dtype)是张量里元素的类型。torch.Tensor 要求所有元素类型一致。
import torch
a = torch.tensor([1, 2, 3]) # 一维
b = torch.tensor([[1, 2], [3, 4]]) # 二维
print("a:", a.ndim, a.shape, a.dtype)
print("b:", b.ndim, b.shape, b.dtype)输出:
a: 1 torch.Size([3]) torch.int64
b: 2 torch.Size([2, 2]) torch.int64逐项解读:
a.ndim是1、b.ndim是2:分别是一维、二维。a.shape是torch.Size([3]):长度为 3 的一维张量。b.shape是torch.Size([2, 2]):2 行 2 列。dtype是torch.int64:64 位整数。元素里只要有一个小数,整个张量就是浮点型:
import torch
c = torch.tensor([1.5, 2.5])
print(c.dtype)
print(c)输出:
torch.float32
tensor([1.5000, 2.5000])这里有一个与 NumPy 的关键区别:PyTorch 默认浮点类型是 float32(32 位),不是 float64。深度学习用 32 位浮点就够,而且省一半内存、在 GPU 上更快。代价是精度略低,1.6 会看到实例。
也可以在创建时用 dtype 参数指定类型:
import torch
d = torch.tensor([1, 2, 3], dtype=torch.float64)
print(d)
print(d.dtype)输出:
tensor([1., 2., 3.], dtype=torch.float64)
torch.float64注意两点:打印浮点张量时每个数带一个小数点(1.);当 dtype 不是默认的 float32 时,打印会额外标注 dtype=torch.float64。
1.5 二维张量的打印格式
二维张量打印成矩阵形状,自动换行对齐:
import torch
grades = torch.tensor([[85, 90], [78, 88], [92, 95]])
print(grades)
print("形状:", grades.shape)输出:
tensor([[85, 90],
[78, 88],
[92, 95]])
形状: torch.Size([3, 2])形状 torch.Size([3, 2]) 读作「3 行 2 列」:3 个学生,每人 2 门成绩。这个「先认识数据长什么样,再学怎么操作它」的顺序,贯穿全书。
1.6 张量的简单统计
torch.Tensor 自带常用的统计方法,无需循环。统计平均值时,整数张量不能直接求 mean(PyTorch 会报错),所以这里用浮点张量:
import torch
scores = torch.tensor([85.0, 92.0, 78.0, 90.0, 88.0], dtype=torch.float64)
print("总和:", scores.sum().item())
print("平均:", scores.mean().item())
print("最大:", scores.max().item())
print("最小:", scores.min().item())
print("个数:", scores.numel())输出:
总和: 433.0
平均: 86.6
最大: 92.0
最小: 78.0
个数: 5.item() 把 0 维张量(标量)转成 Python 数字。没有 .item() 时,scores.sum() 打印成 tensor(433.)——它仍然是张量,不是普通数字,直接拿去做字符串拼接会出错。
如果不指定 dtype=torch.float64,默认的 float32 算平均值会有可见误差:
import torch
scores32 = torch.tensor([85.0, 92.0, 78.0, 90.0, 88.0])
print(scores32.mean().item())输出:
86.5999984741211精确值应该是 86.6,float32 只保留约 7 位有效十进制数字。深度学习场景通常不在乎这种误差,但做数值计算时要意识到它。
1.7 读懂第一条报错
PyTorch 报错和 Python 报错格式一样,是 traceback。常见的第一条报错是形状不匹配。
在交互环境中输入:
>>> import torch
>>> torch.tensor([1, 2, 3]) + torch.tensor([1, 2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 0解读:错误类型是 RuntimeError(运行时错误),信息是 The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 0(张量 a 的大小 3 必须与张量 b 的大小 2 在非单例维度 0 上匹配)。两个张量长度不同(3 和 2),不能逐元素相加。某些形状不同的张量其实可以相加——这叫「广播」,是第 4 章的主题,现在只需记住:两个形状不兼容的张量做运算会报 RuntimeError。
读 PyTorch 报错三步:
- 看最后一行:错误类型 + 一句话说明,形状错误的信息里一定带有
size ... must match或shapes ...。 - 看中间的行:出错的是哪条语句、哪个文件。
- 核对参与运算的张量形状:用
print(张量.shape)确认。
动手实践
- 在项目根目录运行
uv run python进入交互环境,验证torch.__version__和torch.cuda.is_available()。 - 创建你自己的身高张量
torch.tensor([...]),打印它,再打印ndim、shape、dtype。 - 把张量每个元素减 5 并打印,验证结果仍是张量;再用 Python 列表做同样的
- 5,观察报错——这直观展示了张量与列表运算语义的区别。 - 故意让
torch.tensor([1, 2, 3])与torch.tensor([1, 2])相加,用「读报错三步」说出:错误类型是什么、两个操作数的形状各是什么。 - 对整型分数张量
torch.tensor([85, 92, 78])调用.mean(),观察报错,再用dtype=torch.float64重试——亲眼确认「整数张量不能直接求平均」。
常见错误
| 错误写法 | 现象 | 原因 |
|---|---|---|
torch.tensor(1, 2, 3) | TypeError: tensor() takes 1 or 2 positional arguments but 3 were given | tensor 只接受一个序列参数,应写成 torch.tensor([1, 2, 3]) |
[1, 2, 3] + [4, 5, 6] | 结果是 [1, 2, 3, 4, 5, 6] | 列表的 + 是拼接,不是逐元素相加;要用 torch.tensor |
torch.tensor([1, 2, 3]) + torch.tensor([1, 2]) | RuntimeError: The size of tensor a (3) must match ... | 形状不兼容,不能逐元素运算 |
import torch 后直接用 tensor([1, 2]) | NameError: name 'tensor' is not defined | 导入简写后要用 torch.tensor(...);或写成 from torch import tensor |
torch.tensor([85, 92]).mean() | RuntimeError: mean(): could not infer output dtype. Input dtype must be either a floating point or complex dtype. Got: Long | 整数张量不能求平均,要先转浮点(如 dtype=torch.float64) |
print("平均:", scores.mean()) | 打印 平均: tensor(86.6) | mean() 返回 0 维张量,要用 .item() 转成 Python 数字 |
章末练习
基础
- 写一段代码:创建张量
[10, 20, 30, 40, 50],分别打印它的ndim、shape、dtype。 - 创建二维张量
[[1, 2, 3], [4, 5, 6]],打印形状并说明它代表几行几列。 - 验证安装:用一行命令输出你本机 PyTorch 的版本号,并输出
torch.cuda.is_available()。
提高
- 用列表
[1, 2, 3]分别做[1, 2, 3] * 2和torch.tensor([1, 2, 3]) * 2,打印两个结果,并解释为什么不同。 - 创建张量
torch.tensor([1.0, 2, 3])(混入一个小数),打印它的dtype,再解释为什么整个张量变成浮点型;接着打印torch.tensor([1, 2, 3], dtype=torch.float64)并说明打印格式与默认浮点的差异。
挑战
- 不运行代码,预测
torch.tensor([1, 2, 3], dtype=torch.float64).mean().item()的输出,再运行验证;然后用列表完成同样任务(需要循环或sum(lst)/len(lst)),比较两种写法。 - 用
torch.arange(1_000_000)创建 100 万个整数,执行+1运算;用列表推导式完成同样的+1,用time模块分别计时,感受向量化的速度差异(提示:列表版本大约慢 20 倍以上)。
章末自测
每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。
- PyTorch 的核心数据结构是什么?
- A.
list - B.
ndarray - C.
Tensor - D.
Series
- A.
- PyTorch 社区约定的导入写法是?
- A.
import pytorch as pt - B.
import torch - C.
from torch import * - D.
import torch as pytorch
- A.
torch.tensor([1, 2, 3]).shape的结果是?- A.
3 - B.
torch.Size([3]) - C.
torch.Size([1, 3]) - D.
[3]
- A.
torch.tensor([1.5, 2]).dtype的结果是?- A.
torch.int64 - B.
torch.float64 - C.
torch.float32 - D.
torch.float16
- A.
torch.tensor([[1, 2], [3, 4]])的ndim是?- A.
1 - B.
2 - C.
4 - D.
torch.Size([2, 2])
- A.
torch.tensor([1, 2, 3]) + 10的结果是?- A.
[11, 12, 13](列表) - B.
[1, 2, 3, 10] - C.
tensor([11, 12, 13]) - D. 报错
- A.
- PyTorch 默认浮点数据类型是?
- A.
float64 - B.
float32 - C.
int64 - D.
float16
- A.
torch.tensor([1, 2, 3]) + torch.tensor([1, 2])会?- A. 返回
tensor([2, 4, 3]) - B. 返回
tensor([2, 4]) - C. 报
RuntimeError - D. 返回列表
- A. 返回
- 下列哪个是查看张量元素总数的属性或方法?
- A.
len()(内置函数) - B.
shape - C.
ndim - D.
numel()
- A.
- 关于向量化,正确的说法是?
- A. 必须用 for 循环处理张量
- B. 一次运算作用于整个张量,内部用编译代码批量执行
- C. 只有列表才支持向量化
- D. 向量化会让程序更慢
