线性回归
? 线性模型可以看做是单层神经网络。
? 定义什么是平方损失,1/2存在的原因是求导时可以很好的去除。
?
? 训练损失是损失函数,,其中的1/n是求取平均。注意y-Xw都是张量相减,y和x都包含n个样本。通过找到这个最小的损失函数的值可以得出最优的w和b。
? 这是唯一拥有最优解的模型,可以通过X和y得到最优的w*。
? 超参数就是需要人为来指定的一个值。
. 对所有样本进行梯度下降的成本太大,时间太长,所以需要进行小批量样本的梯度下降。这里的b就是batch_size。这个值不能太小,太小的话不适合并行来最大利用计算资源。也不适合太大,内存消耗增加浪费计算,例如如果所有样本都是相同的。
线性回归从0开始实现
? 将从0开始实现整个方法,包括数据流水线、模型、损失函数和小批量随机梯度下降优化器。
%matplotlib inline
import random
import torch
from d2l import torch as d2l
? 首先导入random包,作用是随机化权重。
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))
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
? 这是一个数据生成函数,num_examples是n个样本。X是均值为0,方差为1的随机数,大小是n,列数是权重w的列数。同时添加一个噪声,均值为0,方差为1,大小和y一样:y += torch.normal(0, 0.01, y.shape)。最后return中的reshape中的-1表示自动计算,1表示1列。通过这样的操作就获得了一百个训练样本。其中X是features输入,y是labels标签。
? 通过print查看feature[0]的值:
print('features:', features[0], '\nlabel:', labels[0])
features: tensor([-0.2699, 1.0983])
label: tensor([-0.0847])
? features中的每一行都包含一个二维数据样本,labels中的每一行都包含一个一位标签值(一个标量)。
? 接下来定义一个data_iter函数,该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为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]
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
? 通过random.shuffle函数去打乱下标。for循环当中的意思是从0开始,到num_examples结束,步长为batch_size,然后构造batch_indices,每次取出第i个到i+batch_size个下标的内容。最后产生这些随机的特征和随机的标号。yield作用是不断返回数值。
? 然后初始化模型参数w和b
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
? 然后定义线性回归模型
def linreg(X, w, b):
"""线性回归模型。"""
return torch.matmul(X, w) + b
? 然后定义损失函数
def squared_loss(y_hat, y):
"""均方损失。"""
return (y_hat - y.reshape(y_hat.shape))**2 / 2
? 然后定义优化算法
def sgd(params, lr, batch_size):
"""小批量随机梯度下降。"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
? params是一个list,里面包含了w和b,lr代表学习率。 在for循环中,param -= lr * param.grad / batch_size的含义就是梯度下降的公式表达,其中param可能是w也可能是b,最后手动将梯度设为0。
? 接下来就到训练过程了
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}')
? net参数就是之前定义过的线性回归模型,之后通过l.sum().backward()进行求梯度,再通过sgd函数更新梯度,最后计算出loss。
线性回归的简洁实现
? 通过使用深度学习框架来简介线性回归模型,生成数据集
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
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)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
next(iter(data_iter))
? tensordataset的作用是把输入的两类数据进行一一对应,然后通过dataloader随机的挑选出一个样本出来,shuffle的含义是是否需要随机打乱他的顺序。
? 然后是模型的定义,使用框架的预定义好的层。
from torch import nn
net = nn.Sequential(nn.Linear(2, 1))
? nn是神经网络的缩写。对于线性回归来说只需要全连接层,只需要指定输入的维度是2,输出的维度是1。
? 然后是初始化模型参数
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
? 通过net[0]可以访问到第一层,也就是上述的线性层(因为就一层),然后通过weight可以访问到w,然后通过normal替换掉他的值为正态分布的值,同理以0来填充bias。
? 然后计算均方误差,使用的是MSELoss类
loss = nn.MSELoss()
? 然后实例化SGD实例
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
? 其中包含两个参数,一个是parameters,意思是包含全部的参数,第二个是学习率。
? 然后可以开始训练
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}')
? 三部曲: trainer.zero_grad() l.backward() trainer.step()。首先清零梯度,然后通过backward计算梯度,然后通过step更新梯度
|