构造数据迭代器:
- 传入参数:batch_size(一批数据的大小),features(特征值矩阵X),labels(标签矩阵即真实值y)
- 前三行作用:生成个数为features行数的列表,用shuffle打乱成随机数列表
- 第一个for循环:每次循环步长是一批数据的个数,循环100次
- 从上一步的乱序列表中,按顺序取出一批,如果是最后一批且不够一个列表大小,就取到列表的末尾。
- 将一批的数据作为一行,形成张量。
- 将该张量的元素作为索引,取出对应的features和labels,暂停,等待下一次被调用就从暂停的地方继续
def data_iter(batch_size,features,labels): #数据迭代器
num_examples = len(features) #features的行数
indices = list(range(num_examples)) #生成从0-999的随机数
random.shuffle(indices) #将原始列表打乱
for i in range(0,num_examples,batch_size): #从0开始,999结束,步长是batch_size
batch_indices = torch.tensor(
indices[i:min(i+batch_size,num_examples)]) #如果是最后一批,不够一个batch_size,就取列表的末尾
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是权重,由一组正态分布的随机数组成,以0为对称轴,0.01为标准差,形状是1行2列,开启梯度追踪
- b是偏置,初始化为全0矩阵,开启梯度追踪
w = torch.normal(0,0.01,size=(2,1),requires_grad = True) #w是权重
b = torch.zeros(1,requires_grad = True) #b是偏置,初始化为0
线性回归函数:
- 传入参数:特征值矩阵X,权重矩阵w,偏置矩阵b
- 返回值:Xb+w 即特征值*权重+偏置
def linreg(X,w,b): #返回线性回归的预测值
return torch.matmul(X,w) + b
均方误差函数:
- 传入参数:预测值y_hat,即linreg函数的返回值和真实值y,即labels值
- 返回值:返回预测值-真实值的平方除以2(均方误差),其中真实值y矩阵的形状应转换成与y_hat矩阵一样
def squared_loss(y_hat,y): #y_hat是linreg()函数的返回值
return (y_hat - y.reshape(y_hat.shape))**2 / 2 #y.reshape(y_hat.shape)强制把y的形状转换成y_hat一致,真实值-预测值
#均方损失 #y是data_iter返回的labels值
小批量梯度随机下降:
- 传入参数:params存储w,b的参数列表,lr学习率
- 执行过程:
- 临时关闭梯度追踪
- 分别把w,b从列表中拿出来
- param新值=param旧值-学习率*(这一小批样本的平均梯度)
- 将上一批的梯度矩阵清零
def sgd(params,lr,batch_size): #小批量梯度随机下降,params是一个存储w,b的参数列表,lr是学习率
with torch.no_grad(): #临时关闭梯度追踪
for param in params: #把w,b分别一次次拿出来
param -= lr * param.grad / batch_size #param新值=param旧值-学习率*(这一小批样本的平均梯度)
param.grad.zero_() #将上一批的梯度矩阵清零
- 设置学习率0.03,扫描次数3,将线性回归函数重命名net,损失函数loss
- 循环扫描3次:从data_iter函数中把数据一批批取出来,得到均方误差l,算出总和梯度,执行sgd更新参数,关闭梯度追踪,生成预测值与真实值之间的均方误差,最后把每一次循环的平均损失输出
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs): #把整个数据集完整扫描3遍
for X,y in data_iter(batch_size,features,labels): #从data_iter函数中把数据一批批取出来
l = loss(net(X,w,b),y)
l.sum().backward() #l.sum()形成标量用于计算backward(),算出损失对w,b的总和梯度
sgd([w,b],lr,batch_size) #sgd把总和梯度除以batch_size变成平均梯度
with torch.no_grad(): #关闭梯度追踪
train_l = loss(net(features,w,b),labels) #net()对整个1000个样本做线性回归预测,loss()生成预测值和真实值的均方误差
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}') #把每一个循环次数里面的平均损失打印出来
输出内容:
- w估计误差:将预测权重w转换成真实权重true_w的形状,计算差值,形成估计误差
- b估计误差:真实偏置-模型学到的偏置之间的误差
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}') #将预测权重w转换成真实权重true_w的形状,计算差值,形成估计误差
print(f'b的估计误差: {true_b - b}') #真实偏置-模型学到的偏置之间的误差