看完了李沐老师的动手学深度中线性回归,对单层神经网络有了进一步的认识,针对老师上课的代码,我进行了复现并对代码进行了详细的注释。
# 线性回归从0开始实现
import random
import torch
from d2l import torch as d2l
# 根据带有噪声的线性模型构造一个人工数据集,使用线性模型参数w=[2,-3.4].T,b=4.2和噪声项epslion生成数据集及标签
def synthetic_data(w,b,num_examples):
# 生成 y=Xw+b+噪声
# 均值为0方差为1的正态随机数,样本大小为num_xampls(行数),列数与w的维度相同
X = torch.normal(0,1,(num_examples,len(w)))
# 真实值
y = torch.matmul(X,w) + b
# 加入噪音
y +=torch.normal(0,0.01,y.shape)
# 返回特征和标签并把标签变为二维张量(1000,1)
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)
# 散点图展示
d2l.set_figsize()
# detach将张量转变为array数组进行画散点图
d2l.plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1)
# 定义一个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即随机索引
batch_indices = torch.tensor(indices[i:min(i+batch_size,num_examples)])
# 返回随机特征和标签,注意不可以使用return
yield features[batch_indices], labels[batch_indices]
# return batch_indices
batch_size = 10
# 查看构造的数据
for X,y in data_iter(batch_size,features,labels):
print(X,'\n',y)
break
# 定义初始化参数
# 初始化权重:均值为0方差为1的正太随机数,由于需要计算梯度,因此将requires_grad设置为True
w = torch.normal(0,0.01,size=(2,1),requires_grad=True)
# 初始化偏置为0,由于也要计算梯度因此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
# 定义优化算法,采用随机梯度下降法
# params表示一个张量列表,包含w和b,lr是学习率
def sgd(params,lr,batch_size):
"""小批量随机梯度下降"""
# 表示更新的时候不参与梯度计算
with torch.no_grad():
for param in params:
# 随机梯度下降法,注意要除以均值
param -= lr * param.grad / batch_size
# 将梯度设为0,确保下一次计算与上一次结果无关,理论可以看看前面的求导视频
param.grad.zero_()
# 学习率
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):
# 计算X和y的小批量损失
l = loss(net(X,w,b),y)
# 求和之后计算梯度
l.sum().backward()
# 梯度下降法更新参数
sgd([w,b],lr,batch_size)
# 使用最后得到的w,b去预测全部数据并计算与真实标签之间的误差
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}')