注:代码参考李沐动手学深度学习,这里只是做代码注解,方便交流
我们从零开始写线性回归,虽然现在的框架,比如 pytorch 都已经帮助我们自动完成了,但为了我们以后能学习的更加深入,我们可以尝试从零开始写线性回归,思路如下:
# -*- coding: utf-8 -*-
# @Project: zc
# @Author: zc
# @File name: linear-regression
# @Create time: 2021/11/21 10:25
# 1.导入相关库
import torch
import random
# 2.生成 y = xw+b+noise,为了数据能够模拟真是情况,需要加噪音
def synthetic_data(w, b, num_examples):
x = torch.normal(0, 1, (num_examples, len(w))) # 从正太分布中找到 x ,size=(num_examples,len(w))
y = torch.matmul(x, w)+ b # y = xw + b
y += torch.normal(0, 0.01, y.shape) # y=xw + Gaussian-noise
return x, y.reshape((-1, 1))
true_w = torch.tensor([-2, 3.4])
true_b = 4.2
num_examples = 1000
# 注:此时将 x = features, y = labels
features, labels = synthetic_data(true_w,
true_b, num_examples)
# 3. 数据迭代器,将训练数据打乱后,以 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_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
# 4. 初始化模型参数,requires_grad=True表示可以求导
batch_size = 10
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
# 5.定义模型 y = x*w + b
def linreg(x, w, b):
return torch.matmul(x, w) + b
# 6.定义损失函数 squared_loss = 0.5*(y_hat-y)**2
def squared_loss(y_hat, y):
return 0.5 * (y_hat - y.reshape(y_hat.shape)) ** 2
# 7.定义小批量随机梯度下降 SGD
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_() # 需要清零
# 8.定义神经网络所需的超参数
"""
:param lr : 学习率 (超参数)
:param num_epochs :训练迭代次数 (超参数)
:param net: 网络模型
:param loss : 损失函数
"""
lr = 0.01
num_epochs = 5
net = linreg
loss = squared_loss
# 9. 开始训练数据
for epoch in range(num_epochs): # 开始训练
for x, y in data_iter(batch_size, features, labels): # 将 (x,y) 分别从 (features, labels) 中取出
l = loss(net(x, w, b), y) # 计算训练集中的 x 进过线性变换后,与训练集中的 y 计算来求得损失值
l.sum().backward() # pytorch中通常是标量对向量求导,故用 l.sum() ,求导用到 backward 得到 w,b 的导数
sgd([w, b], lr, batch_size) # 更新 [w,b] 的值
with torch.no_grad(): # 被with torch.no_grad()包住的代码,不用跟踪反向梯度计算
train_l = loss(net(features, w, b), labels)
print(f'epoch={epoch + 1},loss={float(train_l.mean()):f}')
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
epoch=1,loss=2.271274
epoch=2,loss=0.320107
epoch=3,loss=0.045157
epoch=4,loss=0.006407
epoch=5,loss=0.000953
w的估计误差: tensor([-0.0177, 0.0243], grad_fn=<SubBackward0>)
b的估计误差: tensor([0.0300], grad_fn=<RsubBackward1>)
# -*- coding: utf-8 -*-
# @Project: zc
# @Author: zc
# @File name: 线性回归简洁实现
# @Create time: 2021/11/21 16:09
# 1.导入相关库
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn
# 2.生成 y = xw+b+noise,为了数据能够模拟真是情况,需要加噪音
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
# 3.构造一个 pytorch 的数据迭代器,引用官方的 dataloader ,更专业
def load_array(data_arrays, batch_size, is_train=True):
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, is_train)
# 4. 将训练数据 (features, labels)加载到 dataloader中打散赋值给 data_iter
batch_size = 10
data_iter = load_array((features, labels), batch_size)
# 5. 定义线性模型 y = wx + b ,并赋初值
net = nn.Sequential(nn.Linear(2, 1))
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
# 6. 定义损失函数 loss
loss = nn.MSELoss()
# 7. 定义数据更新的方法,随机梯度下降
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
# 8. 开始训练
num_epochs = 10
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}')
epoch1,loss0.000248
epoch2,loss0.000100
epoch3,loss0.000100
epoch4,loss0.000100
epoch5,loss0.000101
epoch6,loss0.000100
epoch7,loss0.000100
epoch8,loss0.000100
epoch9,loss0.000100
epoch10,loss0.000101
通过我们从零实现线性回归和线性回归的简洁实现,我们需要注意以下问题:
不管是简洁实现还是从零实现,以下思路都是一样的: