\quad 这是我在学习d2l以及使用Pytorch进行机器学习课程学习的过程中整理的笔记以及一些思考,希望能对同样开始学习机器学习的你有所帮助,如有问题还请交流更正。
环境是PyCharm2022.2.1 Python 3.8(conda)
pip install d2l, torch, torchvision
\quad 线性回归的经典模型就是房价预测问题,就比如你的好朋友有一个房子要卖,而你现在知道的是交易市场上发生过的交易中,房子的占地面积以及交易价格。你要做的就是找出一条曲线对数据集进行拟合,然后在定价时给你的好朋友一些建议和指导。
\quad 线性回归问题是一个监督学习(Supervised learning) 问题。那么什么是监督学习呢?
-监督学习:利用一组已知类别的样本调整分类器的参数,使其达到所要求性能的过程,也称为监督训练或有教师学习。
\quad 通俗的来讲,就是我一直一些输入(房子的占地面积)和真是结果(放假),我需要使我的模型输出结果尽可能地接近真是结果。这样进行的就是监督学习。
定义synthetic_data(2, b, num_examples)
函数,来生成训练需要的人工数据集。函数传入的参数是真实曲线的斜率、截距以及样本数量。
def synthetic_data(w, b, num_examples):
"""生成 y = Xw + b + 噪声"""
X = torch.normal(0, 1, (num_examples, len(w)))
y = torch.matmul(X, w) + b
y += torch.normal(0, 0.01, y.shape)
return X, y.reshape((-1, 1))
定义data_iter(batch_size, features, lables)
函数,来对样本数据集进行随机访问。函数传入的参数是读取步长,样本数量和标签。这里的random.shuffle()
函数的目的是为了打乱编号,来实现随机读取的目的。而随机读取则是为了后面的小批量随机梯度下降,也就是SGD做准备。每次返回一个batch_size
大小的数据。
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_size为步长
batch_indices = torch.tensor(indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
linereg(X, w, b)
函数的功能就是实现线性回归函数的计算,就是一个简单的矩阵计算。返回值为线性回归模型的计算结果。
def linereg(X, w, b):
"""线性回归模型"""
return torch.matmul(X, w) + b
在SGD中我们使用MSELoss
,也就是均方损失,计算的是预测值和真实值之间差的平方值,再求和取平均。 1 n ∑ i = 1 n ( y ^ − y ) 2 \frac{1}{n}\sum_{i = 1}^{n}(\hat{y}-y)^{2} n1i=1∑n(y^−y)2
def squared_loss(y_hat, y):
"""均方损失"""
loss = (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
return loss #统一矩阵形状
SGD的核心。sgd(params, lr, batch_size)
函数用来实现梯度下降。函数传入的参数为**权重参数、学习率和步长。**低度下降的公式为:
W t ⃗ = W t − 1 ⃗ − η α α W t − 1 ⃗ \vec{W_{t}} = \vec{W_{t-1}}-\eta\frac{\alpha}{\alpha \vec{W_{t-1}}} Wt=Wt−1−ηαWt−1α
def sgd(params, lr, batch_size):
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
中规中矩的训练函数
if __name__ == '__main__':
true_w = torch.tensor(([2, -3.4]))
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
d2l.set_figsize()
d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1)
# plt.show()
#############################
batch_size = 10
"""for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break"""
############################
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
###########################
"""训练过程"""
lr = 0.03
num_epochs = 3
net = linereg
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}')
整体实现步骤和原理与上述过程是一致的,知识使用了现成的d2l中的函数库来简便实现了,代码如下。
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn
def load_array(data_arrays, batch_size, is_train=True):
"""构造Pytorch数据迭代器"""
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
if __name__ == '__main__':
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
"""输入输出维度"""
net = nn.Sequential(nn.Linear(2, 1))
loss = nn.MSELoss()
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
# print(next(iter(data_iter)))
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X), y)
trainer.zero_grad()
l.backward()
trainer.step()
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l : f}')
print(net[0].weight)
print(net[0].bias)
梯度下降到此就结束啦