动手学深度学习第三章--线性回归——笔记
3.线性神经网络
介绍神经网络的整个训练过程, 包括:定义简单的神经网络架构、数据处理、指定损失函数和如何训练模型。经典统计学习技术中的线性回归和softmax回归可以视为线性神经网络。
3.1 线性回归
在机器学习领域中的大多数任务通常都与预测(prediction)有关。 当我们想预测一个数值时,就会涉及到回归问题。
线性模型:中的仿射变换,指的是是通过加权和对特征进行线性变换(linear transformation), 并通过偏置项来进行平移(translation)。输出的预测值由输入特征通过线性模型的仿射变换决定,仿射变换由所选权重和偏置确定。其中,偏置b是当所有特征值都为0的时候,预测值应为多少,权重w决定了每个特征对我们预测值的影响。
1.损失函数:
拟合程度的度量。 损失函数(loss function)能够量化目标的实际值与预测值之间的差距。通常我们会选择非负数作为损失,且数值越小表示损失越小,完美预测时的损失为0。 回归问题中最常用的损失函数是平方误差函数。
其中,有小尖角的是预测值,没有的是真实值。

常数1/2不会带来本质的差别,但这样在形式上稍微简单一些 (因为当我们对损失函数求导后常数系数为1)
为了度量模型在整个数据集上的质量,我们需计算在训练集n个样本上的损失均值(也等价于求和)。


2.随机梯度下降:
梯度下降(gradient descent)的方法, 这种方法几乎可以优化所有深度学习模型。 它通过不断地在损失函数递减的方向上更新参数来降低误差。
但实际中的执行可能会非常慢:因为在每一次更新参数之前,我们必须遍历整个数据集。 因此,我们通常会在每次需要计算更新的时候随机抽取一小批样本, 这种变体叫做小批量随机梯度下降。
更新参数的公式如下:同时更新w和b,其中α为学习率,决定参数每一步更新的大小。

若是在一个小批量的样本数上更新参数,乘于学习率的那一项还有除于一个批量的大小。
超参数:批量大小和学习率的值通常是手动预先指定,而不是通过模型训练得到的。 超参数通常是我们根据训练迭代结果来调整的, 而训练迭代结果是在独立的验证数据集上评估得到的。

Tip:矢量化可以加速模型的运行速度。
3.正态分布和平方误差

4.神经网络图
由于模型重点在发生计算的地方,所以通常我们在计算层数时不考虑输入层。 也就是说, 图3.1.2中神经网络的层数为1。 我们可以将线性回归模型视为仅由单个人工神经元组成的神经网络,或称为单层神经网络。

对于线性回归,每个输入都与每个输出(在本例中只有一个输出)相连, 我们将这种变换( 图3.1.2中的输出层) 称为全连接层(fully-connected layer)或称为稠密层(dense layer)。
3.2 线性回归从零开始实现
1.生成数据集代码:
生成一个带有噪声的线性模型,torch.normal 方法是生成正态分布,0是均值,1是标准差,num_examples是样本的数量。 X生成的是特征矩阵,行为每一个样本,列为对应的特征。函数返回值为特征矩阵和标签列表。

生成第二个特征features[:, 1]和labels的散点图, 可以直观观察到两者之间的线性关系。

- d2l.set_figsize() - 设置绘图的尺寸
- d2l.plt.scatter(...) - 绘制散点图:
- x 轴数据:features[:, (1)].detach().numpy() - 从特征张量的第 2 列(索引为 1)中提取数据,并从计算图中分离后转换为 NumPy 数组
- y 轴数据:labels.detach().numpy() - 从标签张量中提取数据,并从计算图中分离后转换为 NumPy 数组
- 1 - 散点的大小
这里使用.detach()是因为需要将 PyTorch 张量从计算图中分离出来,才能转换为 NumPy 数组进行可视化。这通常用于在训练过程中可视化数据或中间结果。
2.读取数据集:

Indices生成的是从0到样本数量减一的列表,当作索引。然后把索引随机打乱。for循环表示每次从batch_size的整数倍的索引开始,取一个大小为批大小为batch_size的数据。batch_indices表示的是此批数据的索引。设置min函数来处理末尾剩下的不足一个批次大小的数据。
torch.tensor(...)将本次批次的索引转换为 PyTorch 张量,以便用于张量索引。
yield关键字使函数成为生成器,每次返回一个批次的特征和标签。
打印第一个批次大小为10的特征矩阵和标签:

3.初始化参数:
W从均值为 0、标准差为 0.01 的正态分布中随机采样,为两行一列的参数,表示输入的特征是二维的,b是一个一维的为0 的数,requires_grad等于true表示该张量需要计算梯度,以便后续反向传播更新。
4.定义线性模型

5.定义均方损失函数:

6.定义优化算法:小批量梯度下降算法

- 除以batch_size是因为损失函数通常是批次中所有样本损失的平均值,是为了让学习率不受批次大小影响。
- with torch.no_grad()::创建一个上下文环境,在该环境中不会追踪梯度计算,节省内存并加速计算。
param.grad.zero_(),每次参数更新后必须清零梯度,否则 PyTorch 会默认累积梯度。
7.训练:
在每次迭代中,我们读取一小批量训练样本,并通过我们的模型来获得一组预测。 计算完损失后,我们开始反向传播,存储每个参数的梯度。 最后,我们调用优化算法sgd来更新模型参数。


小L是当前批次的预测值与真实值的损失,是形状为(batch_size, 1)的张量。通过sum()将其聚合为标量,再调用backward()计算梯度。
3.3使用深度学习框架来简洁地实现 3.2节中的线性回归模型。
1.生成数据集

2.读取数据集:
这个函数实际上是使用 PyTorch 内置的TensorDataset和DataLoader实现了与之前手写的data_iter类似的功能,但更强大、更稳定。
- data.TensorDataset(*data_arrays):将输入的特征和标签组合成 PyTorch 的TensorDataset
- data_arrays:包含特征和标签的数组集合(通常是一个元组,如(features, labels))
-
- TensorDataset是一种将多个张量按样本维度组合的数据集格式
- *data_arrays用于解包元组,将特征和标签分别传入
data.DataLoader(...):创建数据加载器,用于批量迭代数据

iter() 是 Python 的内置函数,作用是:将 “可迭代对象” 转换为 “迭代器(Iterator)”

next() 也是 Python 内置函数,作用是:从迭代器中获取 “下一个元素”。

与 3.2节不同,这里我们使用iter构造Python迭代器,并使用next从迭代器中获取第一项。
![]()
3 定义模型:

4.初始化参数:

5.定义损失函数

6.定义优化算法:

7.训练模型:


完整代码如下,有需要自取:
# 从零实现线性神经网络
import random
import torch
from d2l import torch as d2l
# 生成数据集
def synthetic_data(w,b,num_examples):
# 生成 y = wx+b+噪声
X = torch.normal(0,1,(num_examples,len(w))) #特征矩阵
y = torch.matmul(X,w)+b
y += torch.normal(0,0.001,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],'\nlabel:', labels[0])
d2l.set_figsize()
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);
# 读取数据集
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
# 初始化参数
w = torch.normal(0,0.01,size=(2,1),requires_grad=True)
b = torch.zeros(1,requires_grad=True)
# 定义模型
def liner_model(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_()
lr = 0.03
num_epochs = 3
net = liner_model
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_loss = loss(net(features,w,b),labels)
print(f'epoch:{epoch+1},train_loss:{float(train_loss.mean())}')
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
# 简洁实现线性回归网络
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
from torch import nn
# 生成数据集
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):
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size=batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size, is_train=True)
next(iter(data_iter))
# 定义模型
net = nn.Sequential(nn.Linear(2,1))
# 初始化模型参数
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
# 定义损失函数
loss = nn.MSELoss()
# 定义优化算法
sgd = torch.optim.SGD(net.parameters(),lr=0.03)
# 训练模型
num_epochs = 3
for epoch in range(num_epochs):
for X,y in data_iter:
l = loss(net(X),y)
sgd.zero_grad() #梯度清零
l.backward() #反向传播计算梯度
sgd.step() #优化器更新参数
l = loss(net(features),labels)
print(f'epoch:{epoch+2},loss:{float(l.mean())}')
w = net[0].weight.data
b = net[0].bias.data
print(f'参数w的误差为:{true_w-w},参数b的误差为:{true_b-b}')
更多推荐




所有评论(0)