《零基础入门AI:深度学习入门(从PyTorch安装到自动微分)》
一、PyTorch安装与环境配置
PyTorch是由Facebook AI Research开发的深度学习框架,以其动态计算图和Pythonic设计而广受欢迎。安装前需确认:
-
系统要求:
- 支持Windows/Linux/macOS
- Python 3.7+(推荐3.8+)
- 可选NVIDIA GPU(需安装CUDA)
-
安装步骤:
- 访问PyTorch官网
- 选择配置(操作系统、包管理器、Python版本、CUDA版本)
- 执行安装命令(无GPU示例):
pip install torch torchvision torchaudio
-
验证安装:
import torch print(torch.__version__) # 查看版本(如2.1.0) print(torch.cuda.is_available()) # 检查GPU支持(返回True/False)
常见问题:若安装失败,尝试使用Python虚拟环境或检查pip版本。GPU用户需确保CUDA版本与PyTorch要求匹配。
二、认识人工智能
1. 人工智能(AI)是什么?
AI : 人工智能是使机器模拟人类智能行为**的科学与技术。核心是通过算法让计算机:
- 感知环境(如摄像头识别物体)
- 学习知识(如从数据中发现规律)
- 做出决策(如自动驾驶车辆转弯)
AI本质
- 本质是数学计算
- 数学是理论关键
- 计算机是实现关键:算力
- 新的有效算法,需要更大的算力
2. AI实现过程
三要素:数据、网络、算力
① 神经网络:找到合适的数学公式;
② 训练:用已有数据训练网络,目标是求最优解;
③ 推理:用模型预测新样本;
示例:图像识别
3. 术语关系图
人工智能(AI)
├─ 机器学习(ML) - 让计算机从数据中学习
│ └─ 深度学习(DL) - 使用多层神经网络
├─ 计算机视觉(CV) - 图像/视频理解
└─ 自然语言处理(NLP) - 语言理解与生成
4. AI产业大生态
- 基础层:硬件(GPU/TPU芯片)、云计算平台
- 技术层:框架(PyTorch/TensorFlow)、算法模型
- 应用层:智能医疗、自动驾驶、智能客服
三、初识PyTorch
PyTorch是一个基于Python的深度学习框架,它提供了一种灵活、高效、易于学习的方式来实现深度学习模型。PyTorch最初由Facebook开发,被广泛应用于计算机视觉、自然语言处理、语音识别等领域。
PyTorch使用张量(tensor)来表示数据,可以轻松地处理大规模数据集,且可以在GPU上加速。
PyTorch提供了许多高级功能,如**自动微分(automatic differentiation)、自动求导(automatic gradients)**等,这些功能可以帮助我们更好地理解模型的训练过程,并提高模型训练效率。
PyTorch三大核心优势:
-
动态计算图(Define-by-Run)
- 传统框架(如TensorFlow)需先定义静态计算图
- PyTorch在执行时动态构建计算图
- 优势:更灵活调试,支持Python控制流
-
Python原生体验
- 与NumPy相似的API设计
- 无缝集成Python科学生态(Pandas/Matplotlib)
-
丰富的工具链
- TorchVision:图像处理
- TorchText:文本处理
- TorchAudio:音频处理
主流深度学习框架对比:
框架 | 特点 | 适用场景 |
---|---|---|
PyTorch | 动态图,易调试 | 研究、原型开发 |
TensorFlow | 静态图,部署成熟 | 工业级部署 |
Keras | 高层API,上手简单 | 快速原型设计 |
四、Tensor概述
PyTorch会将数据封装成张量(Tensor)进行计算,所谓张量就是元素为相同类型的多维矩阵。
张量可以在 GPU 上加速运行。
1. 什么是Tensor?
Tensor是多维数组的泛化形式:
- 0维:标量(Scalar) → 温度值 25.3
- 1维:向量(Vector) → [1.2, 3.4, 5.6]
- 2维:矩阵(Matrix) → 图片像素矩阵
- 3维+:高阶张量 → 视频数据(宽×高×时间×通道)
2. Tensor核心特点
- 统一数据容器:所有深度学习数据(图像/文本/音频)最终都转为Tensor
- 自动微分支持:记录操作历史用于梯度计算
- 硬件加速:可无缝切换CPU/GPU计算
- 动态计算图:PyTorch 支持动态计算图,这意味着在每一次前向传播时,计算图是即时创建的。
3. 常见数据类型(dtype)
类型 | 说明 | 示例 |
---|---|---|
torch.float32 | 32位浮点数(最常用) | 0.12345 |
torch.int64 | 64位整数 | 42 |
torch.bool | 布尔值 | True |
为什么数据类型重要? 不同操作对数据类型敏感,错误类型会导致计算失败或精度损失。
五、Tensor创建:四种核心方法
1. 基础创建
import torch# 从Python列表创建 (自动推断类型)
t1 = torch.tensor([[1, 2], [3, 4]])# 显式指定类型(重要!)
labels = torch.tensor([0, 1], dtype=torch.int64)# :创建特殊值张量
zeros = torch.zeros(2, 3) # 2×3全零矩阵
ones = torch.ones(3) # 长度为3的全1向量
2. 函数生成法(预定义模式)
# 单位矩阵(恒等变换)
I = torch.eye(3) # [[1,0,0],[0,1,0],[0,0,1]]# 对角矩阵(特征值)
D = torch.diag(torch.tensor([1, 2, 3]))
3. 序列张量
# 等差数列:start=0, end=10, step=2
range_tensor = torch.arange(0, 10, 2) # [0,2,4,6,8]# 线性间隔:start=0, end=1, 分成5份
linspace_tensor = torch.linspace(0, 1, 5) # [0.0, 0.25, 0.5, 0.75, 1.0]
4. 随机张量(重要!)
# 均匀分布:[0,1)区间
rand_tensor = torch.rand(2, 2) # 标准正态分布(均值μ,标准差σ):均值0,标准差1
randn_tensor = torch.randn(3) # 特定范围整数:[low, high)
randint_tensor = torch.randint(1, 10, (3,)) # 3个1-9的随机整数# 均匀分布(区间[a,b])
biases = torch.FloatTensor(50).uniform_(-0.1, 0.1)# 特殊初始化(Xavier方法)
torch.nn.init.xavier_uniform_(weights)
随机种子的作用:
torch.manual_seed(42)
确保每次生成相同随机数,保证实验可复现
六、Tensor常见属性
1. 关键属性获取
x = torch.rand(2, 3, dtype=torch.float32)print(x.shape) # 形状:torch.Size([2, 3])
print(x.dtype) # 数据类型:torch.float32
print(x.device) # 存储位置:cpu 或 cuda:0
print(x.requires_grad) # 是否需梯度计算:False
2. 设备切换(CPU ↔ GPU)
# 检查GPU可用性
if torch.cuda.is_available():device = torch.device("cuda")y = x.to(device) # 转移到GPUz = y.cpu() # 转回CPU
3. 类型转换(重要!)
# 显式转换
a = torch.tensor([1.5, 2.7])
b = a.int() # 转为整数 → [1, 2](截断小数)
c = a.float() # 转为单精度浮点# 自动转换(操作中混合类型时)
d = a + torch.tensor(3) # float + int → float
七、Tensor与NumPy互操作
1.张量转Numpy
- 浅拷贝
调用numpy()方法可以把Tensor转换为Numpy,此时内存是共享的。
import torchdef test003():# 1. 张量转numpydata_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])data_numpy = data_tensor.numpy()print(type(data_tensor), type(data_numpy))# 2. 他们内存是共享的data_numpy[0, 0] = 100print(data_tensor, data_numpy)if __name__ == "__main__":test003()
- 深拷贝
使用copy()方法可以避免内存共享:
import torchdef test003():# 1. 张量转numpydata_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])# 2. 使用copy()避免内存共享data_numpy = data_tensor.numpy().copy()print(type(data_tensor), type(data_numpy))# 3. 此时他们内存是不共享的data_numpy[0, 0] = 100print(data_tensor, data_numpy)if __name__ == "__main__":test003()
2.Numpy转张量
也可以分为内存共享和不共享
- 浅拷贝
from_numpy方法转Tensor默认是内存共享的
import numpy as np
import torchdef test006():# 1. numpy转张量data_numpy = np.array([[1, 2, 3], [4, 5, 6]])data_tensor = torch.from_numpy(data_numpy)print(type(data_tensor), type(data_numpy))# 2. 他们内存是共享的data_tensor[0, 0] = 100print(data_tensor, data_numpy)if __name__ == "__main__":test006()
- 深拷贝
使用传统的torch.tensor()则内存是不共享的~
import numpy as np
import torchdef test006():# 1. numpy转张量data_numpy = np.array([[1, 2, 3], [4, 5, 6]])data_tensor = torch.tensor(data_numpy)print(type(data_tensor), type(data_numpy))# 2. 内存是不共享的data_tensor[0, 0] = 100print(data_tensor, data_numpy)if __name__ == "__main__":test006()
为什么需要转换? NumPy在数据预处理中更高效,而Tensor是深度学习计算的标准格式。
八、Tensor常见操作
1.获取元素值
我们可以把单个元素tensor转换为Python数值,这是非常常用的操作
import torchdef test002():data = torch.tensor([18])print(data.item())passif __name__ == "__main__":test002()
注意:
-
和Tensor的维度没有关系,都可以取出来!
-
如果有多个元素则报错;
-
仅适用于CPU张量,如果张量在GPU上,需先移动到CPU
-
gpu_tensor = torch.tensor([1.0], device='cuda') value = gpu_tensor.cpu().item() # 先转CPU再提取
2. 元素值运算
常见的加减乘除次方取反开方等各种操作,带有_的方法则会替换原始值。
import torchdef test001():# 生成范围 [0, 10) 的 2x3 随机整数张量data = torch.randint(0, 10, (2, 3))print(data)# 元素级别的加减乘除:不修改原始值print(data.add(1))print(data.sub(1))print(data.mul(2))print(data.div(3))print(data.pow(2))# 元素级别的加减乘除:修改原始值data = data.float()data.add_(1)data.sub_(1)data.mul_(2)data.div_(3.0)data.pow_(2)print(data)if __name__ == "__main__":test001()
3. 阿达玛积
阿达玛积是指两个形状相同的矩阵或张量对应位置的元素相乘。它与矩阵乘法不同,矩阵乘法是线性代数中的标准乘法,而阿达玛积是逐元素操作。假设有两个形状相同的矩阵 A和 B,它们的阿达玛积 C=A∘B定义为:
Cij=Aij×BijC_{ij}=A_{ij}×B_{ij} Cij=Aij×Bij
其中:
- Cij 是结果矩阵 C的第 i行第 j列的元素。
- Aij和 Bij分别是矩阵 A和 B的第 i行第 j 列的元素。
在 PyTorch 中,可以使用mul函数或者*来实现;
import torchdef test001():data1 = torch.tensor([[1, 2, 3], [4, 5, 6]])data2 = torch.tensor([[2, 3, 4], [2, 2, 3]])print(data1 * data2)def test002():data1 = torch.tensor([[1, 2, 3], [4, 5, 6]])data2 = torch.tensor([[2, 3, 4], [2, 2, 3]])print(data1.mul(data2))if __name__ == "__main__":test001()test002()
4. Tensor相乘
矩阵乘法是线性代数中的一种基本运算,用于将两个矩阵相乘,生成一个新的矩阵。
假设有两个矩阵:
- 矩阵 A的形状为 m×n(m行 n列)。
- 矩阵 B的形状为 n×p(n行 p列)。
矩阵 A和 B的乘积 C=A×B是一个形状为 m×p的矩阵,其中 C的每个元素 Cij,计算 A的第 i行与 B的第 j列的点积。计算公式为:
Cij=∑k=1nAik×BkjC_{ij}=∑_{k=1}^nA_{ik}×B_{kj} Cij=k=1∑nAik×Bkj
矩阵乘法运算要求如果第一个矩阵的shape是 (N, M),那么第二个矩阵 shape必须是 (M, P),最后两个矩阵点积运算的shape为 (N, P)。
在 PyTorch 中,使用@或者matmul完成Tensor的乘法。
import torchdef test006():data1 = torch.tensor([[1, 2, 3], [4, 5, 6]])data2 = torch.tensor([[3, 2], [2, 3], [5, 3]])print(data1 @ data2)print(data1.matmul(data2))if __name__ == "__main__":test006()
5. 元素访问与切片
x = torch.tensor([[1, 2, 3], [4, 5, 6]])# 单元素访问
print(x[0, 1]) # 第0行第1列 → 2# 切片(与Python语法一致)
row = x[1, :] # 第1行 → [4,5,6]
col = x[:, 1] # 第1列 → [2,5]
block = x[0:2, 1:3] # 子矩阵 [[2,3],[5,6]]
6. 数学运算
a = torch.tensor([1, 2])
b = torch.tensor([3, 4])# 逐元素运算
add = a + b # [4, 6]
sub = a - b # [-2, -2]
mul = a * b # [3, 8](阿达玛积)
div = b / a # [3.0, 2.0]# 矩阵乘法
mat_a = torch.tensor([[1, 2], [3, 4]])
mat_b = torch.tensor([[5, 6], [7, 8]])
matmul = mat_a @ mat_b # [[19,22],[43,50]]
7. 形状操作
x = torch.rand(2, 3)# view:改变形状(元素总数不变)
y = x.view(3, 2) # 2×3 → 3×2# unsqueeze:增加维度(常用于广播)
z = x.unsqueeze(0) # [2,3] → [1,2,3]# squeeze:降低维度
torch.squeeze(input, dim=None)# transpose:维度交换
w = x.transpose(0, 1) # 行变列 → [3,2]
8. 广播机制(Broadcasting)
当两个Tensor形状不同时,自动扩展小张量:
A = torch.ones(3, 2) # 3×2
B = torch.tensor([1, 2]) # 形状(2,)# 广播过程:
# B自动扩展为 [[1,2],
# [1,2],
# [1,2]]
C = A + B # 结果形状(3,2)
广播规则:从后往前逐维比较,维度需相等或其中一个为1
九、自动微分
自动微分模块torch.autograd负责自动计算张量操作的梯度,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效。
核心:
- 计算图(Computational Graph):记录Tensor操作的有向无环图
- 梯度(Gradient):函数输出相对于输入的变化率
- 反向传播(Backpropagation):从输出到输入逐层计算梯度
1. 基础概念
张量
Torch中一切皆为张量,属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。
计算图:
torch.autograd通过创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。
在 PyTorch 中,当张量的 requires_grad=True 时,PyTorch 会自动跟踪与该张量相关的所有操作,并构建计算图。每个操作都会生成一个新的张量,并记录其依赖关系。当设置为 True
时,表示该张量在计算图中需要参与梯度计算,即在反向传播(Backpropagation)过程中会自动计算其梯度;当设置为 False
时,不会计算梯度。
例如:
z=x∗yloss=z.sum()z = x * y\\loss = z.sum() z=x∗yloss=z.sum()
在上述代码中,x 和 y 是输入张量,即叶子节点,z 是中间结果,loss 是最终输出。每一步操作都会记录依赖关系:
z = x * y:z 依赖于 x 和 y。
loss = z.sum():loss 依赖于 z。
这些依赖关系形成了一个动态计算图,如下所示:
x y\ /\ /\ /z||vloss
叶子节点:
在 PyTorch 的自动微分机制中,叶子节点(leaf node) 是计算图中:
- 由用户直接创建的张量,并且它的 requires_grad=True。
- 这些张量是计算图的起始点,通常作为模型参数或输入变量。
特征:
- 没有由其他张量通过操作生成。
- 如果参与了计算,其梯度会存储在 leaf_tensor.grad 中。
- 默认情况下,叶子节点的梯度不会自动清零,需要显式调用 optimizer.zero_grad() 或 x.grad.zero_() 清除。
如何判断一个张量是否是叶子节点?
通过 tensor.is_leaf 属性,可以判断一个张量是否是叶子节点。
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) # 叶子节点
y = x ** 2 # 非叶子节点(通过计算生成)
z = y.sum()print(x.is_leaf) # True
print(y.is_leaf) # False
print(z.is_leaf) # False
叶子节点与非叶子节点的区别
特性 | 叶子节点 | 非叶子节点 |
---|---|---|
创建方式 | 用户直接创建的张量 | 通过其他张量的运算生成 |
is_leaf 属性 | True | False |
梯度存储 | 梯度存储在 .grad 属性中 | 梯度不会存储在 .grad,只能通过反向传播传递 |
是否参与计算图 | 是计算图的起点 | 是计算图的中间或终点 |
删除条件 | 默认不会被删除 | 在反向传播后,默认被释放(除非 retain_graph=True) |
detach():张量 x 从计算图中分离出来,返回一个新的张量,与 x 共享数据,但不包含计算图(即不会追踪梯度)。
特点:
- 返回的张量是一个新的张量,与原始张量共享数据。
- 对 x.detach() 的操作不会影响原始张量的梯度计算。
- 推荐使用 detach(),因为它更安全,且在未来版本的 PyTorch 中可能会取代 data。
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x.detach() # y 是一个新张量,不追踪梯度y += 1 # 修改 y 不会影响 x 的梯度计算
print(x) # tensor([1., 2., 3.], requires_grad=True)
print(y) # tensor([2., 3., 4.])
反向传播
使用tensor.backward()方法执行反向传播,从而计算张量的梯度。这个过程会自动计算每个张量对损失函数的梯度。例如:调用 loss.backward() 从输出节点 loss 开始,沿着计算图反向传播,计算每个节点的梯度。
梯度
计算得到的梯度通过tensor.grad访问,这些梯度用于优化模型参数,以最小化损失函数。
2. 梯度计算示例
# 步骤1:创建需追踪梯度的Tensor
x = torch.tensor(2.0, requires_grad=True)# 步骤2:构建计算图
y = x**2 + 3*x + 1 # y = x² + 3x + 1# 步骤3:反向传播计算梯度
y.backward()# 步骤4:获取梯度
print(x.grad) # dy/dx = 2x + 3 = 2*2 + 3 = 7.0
3. 梯度上下文控制
# 禁止梯度跟踪(推理阶段使用)
with torch.no_grad():inference = x * 2 # 不记录计算图# 梯度清零(防止梯度累加)
x.grad.zero_() # 下次backward前必须清零# 分离计算图(保留值但断开连接)
detached_y = y.detach()
4. 向量梯度计算
def test003():# 1. 创建张量:必须为浮点类型x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)# 2. 操作张量y = x ** 2# 3. 计算梯度,也就是反向传播y.backward()# 4. 读取梯度值print(x.grad)if __name__ == "__main__":test003()
5. 多向量梯度计算
import torchdef test004():# 创建两个张量,并设置 requires_grad=Truex = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)y = torch.tensor([4.0, 5.0, 6.0], requires_grad=True)# 前向传播:计算 z = x * yz = x * y# 前向传播:计算 loss = z.sum()loss = z.sum()# 查看前向传播的结果print("z:", z) # 输出: tensor([ 4., 10., 18.], grad_fn=<MulBackward0>)print("loss:", loss) # 输出: tensor(32., grad_fn=<SumBackward0>)# 反向传播:计算梯度loss.backward()# 查看梯度print("x.grad:", x.grad) # 输出: tensor([4., 5., 6.])print("y.grad:", y.grad) # 输出: tensor([1., 2., 3.])if __name__ == "__main__":test004()
6. 数学原理:链式法则
对于复合函数 z=f(g(x))z = f(g(x))z=f(g(x)):
∂z∂x=∂z∂g⋅∂g∂x\frac{\partial z}{\partial x} = \frac{\partial z}{\partial g} \cdot \frac{\partial g}{\partial x}∂x∂z=∂g∂z⋅∂x∂g
为什么需要梯度清零? 默认情况下梯度会累加,训练循环中需手动清零防止批次间干扰