3. Python与深度学习基础速通:NumPy、Pandas基础、PyTorch张量操作、自动求导机制
说实话,很多同学一上来就急着搭模型,结果连数据怎么装、梯度怎么算都没搞明白。我当年也是这样,踩了不少坑。这一章咱们就把这些基本功打扎实,后面跑模型时才不会手忙脚乱。
3.1 NumPy:深度学习的数据基石
NumPy 是 Python 科学计算的灵魂。说白了,它就是帮你高效处理多维数组的工具。我在做缺陷检测时,一张 1024×1024 的晶圆图,用原生 Python 列表处理要好几秒,换成 NumPy 数组,几十毫秒就搞定。
3.1.1 创建数组
import numpy as np
# 从列表创建
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 全零数组,常用于初始化权重
zeros = np.zeros((3, 4))
# 随机数组,模拟噪声数据
noise = np.random.randn(2, 3)
np.random.seed(42) 固定随机种子,这样每次跑结果都一样,方便调试。
3.1.2 数组运算与广播
NumPy 的广播机制,说白了就是让不同形状的数组也能做运算。举个例子:
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([10, 20, 30])
# 广播:b 自动扩展成 [[10,20,30], [10,20,30]]
result = a + b
3.2 Pandas:表格数据的瑞士军刀
做深度学习,数据预处理占掉 80% 的时间。Pandas 就是帮你处理 CSV、Excel 这些表格数据的利器。
3.2.1 DataFrame 基础操作
import pandas as pd
# 读取缺陷样本的 CSV 文件
df = pd.read_csv('defect_samples.csv')
# 查看前5行,快速了解数据长什么样
df.head()
# 统计缺失值
df.isnull().sum()
# 按缺陷类型分组统计
df.groupby('defect_type').size()
| 操作 | 代码 | 说明 |
|---|---|---|
| 筛选行 | df[df['label'] == 1] |
选出正样本 |
| 删除空值 | df.dropna() |
去掉缺失行 |
| 填充空值 | df.fillna(0) |
用0填充 |
| 列重命名 | df.rename(columns={'old':'new'}) |
改列名 |
实战经验:我在处理工业缺陷数据时,经常遇到标签不平衡——好样本几万张,坏样本才几十张。这时候用 df.sample(frac=1) 打乱数据,再用分层抽样保证训练集和验证集的比例一致。
3.3 PyTorch 张量操作:从 NumPy 到 GPU
PyTorch 的张量(Tensor)和 NumPy 数组长得像,但多了两个杀手锏:GPU 加速和自动求导。
3.3.1 创建张量
import torch
# 从 NumPy 转过来
np_arr = np.array([[1, 2], [3, 4]])
tensor = torch.from_numpy(np_arr)
# 直接在 GPU 上创建
if torch.cuda.is_available():
gpu_tensor = torch.randn(3, 3).cuda()
# 需要梯度的张量(后面自动求导用)
x = torch.tensor([2.0, 3.0], requires_grad=True)
3.3.2 张量变形与运算
你想想看,一张图片在 PyTorch 里通常表示成 (batch, channel, height, width) 的形状。我刚开始总搞混维度顺序,后来养成了一个习惯:每次 reshape 前先打印 .shape 看一眼。
# 模拟一张 3 通道的 224x224 图片
img = torch.randn(3, 224, 224)
# 增加 batch 维度
img_batch = img.unsqueeze(0) # 变成 (1, 3, 224, 224)
# 矩阵乘法(全连接层核心)
W = torch.randn(128, 256)
x = torch.randn(256)
out = W @ x # 等价于 torch.matmul(W, x)
torch.Tensor 和 torch.nn.functional 的常用函数打印出来贴在工位上。我当年就是这么干的,一个月后基本就烂熟于心了。
3.4 自动求导机制:深度学习引擎的核心
自动求导,说白了就是 PyTorch 帮你自动算梯度。你只需要定义好前向计算,反向传播的导数它全包了。
3.4.1 计算图与反向传播
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1
# 反向传播,计算 dy/dx
y.backward()
# 查看梯度
print(x.grad) # 输出: tensor([7.]),因为 dy/dx = 2x + 3 = 7
为什么会这样?PyTorch 内部维护了一张计算图,记录了每一步操作。调用 .backward() 时,它沿着图反向传播,用链式法则算出每个参数的梯度。
3.4.2 梯度清零与优化器
这里有个新手必踩的坑:梯度会累积!
# 错误示范:梯度会累加
for i in range(3):
y = (x ** 2).sum()
y.backward()
print(x.grad) # 每次都会加上之前的梯度
# 正确做法:每次迭代前清零
optimizer = torch.optim.SGD([x], lr=0.01)
for i in range(3):
optimizer.zero_grad() # 梯度清零
y = (x ** 2).sum()
y.backward()
optimizer.step() # 更新参数
optimizer.zero_grad()。梯度一直在累加,参数更新完全乱套了。从那以后,我写训练循环的第一件事就是把 zero_grad() 写上。
3.5 本章知识体系
下面这张图把 NumPy、Pandas、PyTorch 三者的关系串起来了。你仔细看看,它们各自负责什么,又是怎么衔接的。
嗯,这一章的内容就到这里。NumPy 和 Pandas 是数据预处理的两把刷子,PyTorch 张量和自动求导是模型训练的核心。把这些基础打牢了,后面搭 CNN、训练分类器的时候,你就能把精力放在模型设计上,而不是被这些底层操作绊住脚。