本文主要参考沐神的视频教程 https://www.bilibili.com/video/BV1PX4y1g7KC/?vd_source=c7bfc6ce0ea0cbe43aa288ba2713e56d
文档教程 https://zh-v2.d2l.ai/
本文的主要内容对沐神提供的代码中个人不太理解的内容进行笔记记录,内容不会特别严谨仅供参考。
1.函数目录
python | 位置 |
---|---|
range | 3.2 |
yield | 3.2 |
with | 3.6 |
torch 是 PyTorch 的核心模块,包含了张量操作、创建、数学运算、神经网络构建和许多其他功能。torch 模块提供了创建张量的多种方法,支持各种数学运算、随机数生成、线性代数操作等。它是 PyTorch 中的主要接口。
torch模块 | 位置 |
---|---|
torch.normal() | 3.1 |
torch.matmul() | 3.1 |
torch.zeros() | 3.1 |
torch.no_grad() | 3.6 |
torch.Tensor 是 PyTorch 中张量的具体数据类型和类。每个张量对象都是 torch.Tensor 类的实例。张量是多维数组,可以在 GPU 上进行高效的计算。torch.Tensor 类提供了用于操作和转换张量的方法。这些方法可以在张量对象上调用。
torch.Tensor模块 | 位置 |
---|---|
Tensor.detach() | 3.1 |
2. 基础知识
2.1 线性模型
- 线性回归是对n维输入的加权,外加偏差。
输出是输入的加权和
y =
w
1
w_{1}
w1
x
1
x_{1}
x1 +
w
2
w_{2}
w2
x
2
x_{2}
x2+…+
w
n
w_{n}
wn*
x
n
x_{n}
xn+b
向量版本: y=<w,x>+b
- 线性模型可以看做是单层神经网络。
2.2 损失函数
- 使用平方损失来衡量预测值与真实值的差异。
l
l
l(
y
y
y,
y
^
\hat{y}
y^)=
1
2
(
y
−
y
^
)
2
\frac{1}{2}(y-\hat{y})^2
21(y−y^)2
y
y
y:为真实值
y
^
\hat{y}
y^:为预测值
2.3 训练数据
假设我们有n个样本,记为:
X
n
X_{n}
Xn为列向量
X
=
[
X= [
X=[X_{1}, X_{2}, …X_{n}
]
T
]^T
]T
y
=
[
y= [
y=[y_{1}, y_{2}, …y_{n}
]
T
]^T
]T
2.4 参数学习
- 训练损失
l ( X , y , w , b ) = 1 2 n ∑ i = 1 n ( y − x i ∗ w − b ) 2 = 1 2 n ∣ ∣ y − X w − b ∣ ∣ 2 l(X, y, w, b)=\frac{1}{2n}\sum\limits_{i=1}^{n}(y-x_{i}*w-b)^2=\frac{1}{2n}||y-Xw-b||^2 l(X,y,w,b)=2n1i=1∑n(y−xi∗w−b)2=2n1∣∣y−Xw−b∣∣2 - 最小化损失来学习参数
w ∗ , b ∗ = arg min w , b l ( X , y , w , b ) w^*,b^* = \mathop{\arg\min}\limits_{w,b}l(X, y, w, b) w∗,b∗=w,bargminl(X,y,w,b)
2.5 梯度下降
-
挑选一个初始值 w 0 w_0 w0
-
重复迭代参数 t = 1 , 2 , 3 t=1,2,3 t=1,2,3
w t = w t − 1 − η ∗ ∂ l ∂ w t − 1 w_t = w_{t-1}-\eta*\frac{\partial l}{\partial w_{t-1}} wt=wt−1−η∗∂wt−1∂l -
沿着梯度方向将增加损失函数值
-
学习率:步长的超参数
2.6 小批量随机梯度下降
- 在整个训练集上计算梯度很贵
- 我们可以随机采样b个样本
i
1
,
i
2
,
.
.
.
.
.
.
i
b
i_1,i_2,......i_b
i1,i2,......ib来近似损失
1 b ∑ i ∈ I b l ( X i , y i , w ) \frac{1}{b}\sum\limits_{i∈I_b}l(X_i, y_i, w) b1i∈Ib∑l(Xi,yi,w) - b是批量大小,另外一个超参数
3. 线性回归的从零开始实现
3.1 生成数据集
3.1.1 torch.normal()
- 功能:该函数返回从单独的正态分布中提取的随机数的张量,该正态分布的均值是mean,标准差是std
- 输入参数\输出参数
torch.normal(mean, std, size, *, out=None) → Tensor
- mean(float):所有分布的均值
- std(float):所有分布的方差
- size(int):定义输出张量形状的整数序列
torch.normal(0,1,size=(2,3))
tensor([[ 0.9929, -0.9487, -0.1445],
[ 1.4671, 0.3946, 1.0862]])
3.1.2 torch.matmul()
- 作用:torch.matmul 实现了两个张量之间的矩阵乘法。
torch.matmul(input, other, *, out=None) → Tensor
- 输入参数
- input (Tensor): 第一个输入张量。
- other (Tensor): 第二个输入张量。
- 输出参数
返回一个张量,即 input 和 other 的矩阵乘积。
3.1.3 Tensor.detach()
- 作用:用于从计算图中分离一个张量。它会返回一个新的张量,该张量与原始张量共享相同的数据,但不会记录对其的任何操作,也不会对计算图产生影响。这对于控制梯度计算和管理计算图的复杂性非常有用。
- 输入输出参数
输入:torch.detach() 方法没有参数。它是在张量对象上调用的。
输出:返回一个新的张量,该张量与原始张量共享相同的数据,但没有梯度信息,不再与原始计算图关联。
import matplotlib
import random
import torch
from d2l import torch as d2l
def synthetic_data(w, b, num_examples):
x = torch.normal(0, 1, size=(num_examples, len(w)))
y = torch.matmul(x,w) + b
y += torch.normal(0, 0.01, y.shape)
return x, y.reshape(-1, 1)
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
print('features:', features[0:5,:],'\nlabel:', labels[0:5,:])
print(features.shape)
d2l.set_figsize()
d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1)
d2l.plt.show()
3.2 读取数据集
3.2.1 range
range 是 Python 的内置函数,用于生成一系列数字。这些数字通常用于循环和迭代操作。range 有三种使用方式,取决于提供的参数数量:
range(stop): 生成从 0 到 stop - 1 的整数。
range(start, stop): 生成从 start 到 stop - 1 的整数。
range(start, stop, step): 生成从 start 到 stop - 1 的整数,每次递增 step。
indices = list(range(5))
random.shuffle(indices)
print(indices)
3.2.2 yield
yield 是 Python 中的一个关键字,用于创建生成器(generator)。生成器是一种特殊的迭代器,它允许函数在执行过程中暂停和恢复,从而能够生成一个值序列,而不需要一次性生成所有值。相比于普通的返回语句,yield 可以使函数返回一个值并在下次调用时恢复函数的执行。
def simple_generator():
yield 1
yield 2
yield 3
gen = simple_generator()
for i in gen:
if i==1:
print(i)
if i==2:
print(i)
break
# 输出:
# 1
# 2
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))
#打乱顺序
random.shuffle(indices)
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(indices[i:min(i+batch_size, num_examples)])
#yield 语句不在 for 循环的范围内。它只会在最后一次迭代后执行一次
yield features[batch_indices], labels[batch_indices]
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, "\n", y)
break
3.3 初始化模型参数
3.3.1 torch.zeros()
torch.zeros(*size, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) → Tenso
- 作用:创建一个全零的张量,接受的参数为元组。
- size: 定义输出张量形状的整数序列。可以是可变数量的参数,也可以是像列表或元组这样的集合。
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
3.4 定义模型
def linreg(X, w, b):
"线性回归模型"
return torch.matmul(X, w) + b
3.5 定义损失函数
def squared_loss(y_hat, y):
return (y_hat-y.reshape(y_hat.shape))**2/2
3.6 定义优化算法
3.6.1 torch.no_grad()
禁用梯度计算的上下文管理器。
3.6.2 with
with 语句用于上下文管理,它依赖于上下文管理器协议,该协议包括 enter 和 exit 方法。
# 6. 定义优化算法
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
3.7 训练
# 7. 训练
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y) #计算损失
l.sum().backward()
sgd([w, b], lr, batch_size)
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f"epoch{epoch+1},loss {float(train_l.mean()):f}")
print('w:', w)
print('b:', b)
3.8 完整代码
import matplotlib
import random
import torch
from d2l import torch as d2l
# 1. 创建数据
def synthetic_data(w, b, num_examples):
x = torch.normal(0, 1, size=(num_examples, len(w)))
y = torch.matmul(x, w) + b
y += torch.normal(0, 0.01, y.shape)
return x, y.reshape(-1, 1)
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
# print('features:', features[0:5,:],'\nlabel:', labels[0:5,:])
# print(features.shape)
# d2l.set_figsize()
# d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1)
# d2l.plt.show()
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))
#打乱顺序
random.shuffle(indices)
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(indices[i:min(i+batch_size, num_examples)])
#yield 语句不在 for 循环的范围内。它只会在最后一次迭代后执行一次
yield features[batch_indices], labels[batch_indices]
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, "\n", y)
break
# 3.初始化模型参数
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# 4. 定义模型
def linreg(X, w, b):
"线性回归模型"
return torch.matmul(X, w) + b
# 5. 定义损失函数
def squared_loss(y_hat, y):
return (y_hat-y.reshape(y_hat.shape))**2/2
# 6. 定义优化算法
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
# 7. 训练
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y) #计算损失
l.sum().backward()
sgd([w, b], lr, batch_size)
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f"epoch{epoch+1},loss {float(train_l.mean()):f}")
print('w:', w)
print('b:', b)