线性回归-从零开始

文章目录

  • 1. 线性回归-从零实现
    • 1.1 代码-从零实现
    • 1.2.结果-从零实现
  • 2.线性回归-简洁实现
    • 2.1 代码-简洁实现
    • 2.2 结果-简洁实现
  • 3. 小结
    • 3.1 数据加载类
    • 3.2 线性回归网络
    • 3.3 损失函数类
    • 3.4 数据更新优化器(随机梯度下降)
    • 3.5 训练常用思路如下

1. 线性回归-从零实现

注:代码参考李沐动手学深度学习,这里只是做代码注解,方便交流

我们从零开始写线性回归,虽然现在的框架,比如 pytorch 都已经帮助我们自动完成了,但为了我们以后能学习的更加深入,我们可以尝试从零开始写线性回归,思路如下:

  • 训练数据集的收集(synthetic_data)
  • 定义网络线性回归(linreg)
  • 定义损失函数(squared_loss)
  • 参数更新器小批量随机梯度下降(sgd)

1.1 代码-从零实现

# -*- 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}')

1.2.结果-从零实现

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>)

2.线性回归-简洁实现

2.1 代码-简洁实现

# -*- 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}')

2.2 结果-简洁实现

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

3. 小结

通过我们从零实现线性回归和线性回归的简洁实现,我们需要注意以下问题:

3.1 数据加载类

  • data.TensorDataset()
  • data.DataLoader()

3.2 线性回归网络

  • nn.Linear()
  • nn.Sequential()

3.3 损失函数类

  • nn.MSELoss()

3.4 数据更新优化器(随机梯度下降)

  • torch.optim.SGD

3.5 训练常用思路如下

不管是简洁实现还是从零实现,以下思路都是一样的:

  • 通过调⽤net(X)⽣成预测并计算损失(正向传播)
  • 优化器梯度清零
  • 通过进⾏反向传播来计算梯度
  • 优化器梯度更新

你可能感兴趣的:(pytorch,python,开发语言,后端)