动手学深度学习
大约 3 分钟
跟沐神学 吼吼吼,上学不跟沐神好好学,上班之后来换债了
原文里的conda,这里会换成uv,如果遇到问题再说吧,uv实在是太快了
1. 深度学习基础
1.1 环境安装
这里推荐是用GPU,我用的MAC,所以先用CPU顶一下,后面遇到需要GPU的地方再去租云GPU吧
# 新建项目
mkdir ml_learn
cd ml_learn
# uv初始化
uv init
# 添加环境
uv add jupyter
uv add torch
uv add pandas
uv add matplotlib
uv add d2l
uv add torchvision
# 下载D2L
curl https://zh-v2.d2l.ai/d2l-zh-2.0.0.zip -o d2l-zh.zip
unzip d2l-zh.zip && rm d2l-zh.zip
1.2 数据操作
- 张量的原地更新: 计算
tensor的时候,Y、X会先计算,并分配一个内存,最后将Y指向新的内存。
非原地更新
before = id(Y)
Y = Y + X
id(Y) == before
# False
原地更新
Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
# True
1.3 数据预处理
1.3.1 读取数据集
# 构造数据集
import os
os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:
f.write('NumRooms,Alley,Price\n') # 列名
f.write('NA,Pave,127500\n') # 每行表示一个数据样本
f.write('2,NA,106000\n')
f.write('4,NA,178100\n')
f.write('NA,NA,140000\n')
# pandas读取
import pandas as pd
data = pd.read_csv(data_file)
print(data)
# NumRooms Alley Price
# 0 NaN Pave 127500
# 1 2.0 NaN 106000
# 2 4.0 NaN 178100
# 3 NaN NaN 140000
1.3.2 处理缺失值
处理原数据集的NaN部分,用平均值替换
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs["NumRooms"] = inputs["NumRooms"].fillna(inputs["NumRooms"].mean())
print(inputs)
1.3.3 One-Hot编码
将非数值列,转换为One-Hot编码,便于转换为张量
- 机器学习兼容性:大多数机器学习算法只能处理数值数据
- 避免顺序误解:防止算法误以为类别之间有大小关系
- 保留缺失值信息:将缺失值作为有用信息而非直接丢弃
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
# (tensor([[3., 1., 0.],
# [2., 0., 1.],
# [4., 0., 1.],
# [3., 0., 1.]], dtype=torch.float64),
# tensor([127500, 106000, 178100, 140000]))
1.3.4 转换为张量
将数值转换为张量,便于后续张量计算
import torch
x = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
x, y
2. 预备知识
2.1 自动微分
2.1.1 标量的反向传播
对
- 使用
requires_grad_来设置张量计算梯度 - 使用
backward,会从调用的张量开始,沿着计算图反向传播,计算所有requires_grad=True的张量的梯度
import torch
x = torch.arange(4.0)
# tensor([0., 1., 2., 3.])
x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True)
x.grad # 默认值是None
y = 2 * torch.dot(x, x)
# tensor(28., grad_fn=<MulBackward0>)
y.backward()
x.grad
# tensor([ 0., 4., 8., 12.])
2.1.2 非标量的反向传播
如果y不是一个标量(1*1),需要先将它转换成标量,可以用sum直接求和,也可以成一个ones相加。
x.grad.zero_()
y = x * x
# tensor([0., 1., 4., 9.], grad_fn=<MulBackward0>)
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
# tensor([0., 2., 4., 6.])