看完了李沐老师的动手学深度中线性回归,对单层神经网络有了进一步的认识,针对老师上课的代码,我进行了复现并对代码进行了详细的注释。
# 线性回归从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)])# 返回随机特征和标签,注意不可以使用returnyield features[batch_indices], labels[batch_indices]# return batch_indicesbatch_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_lossfor 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}')