李哥深度学习 第二节 线性回归模型
手写一个简单的 线性回归模型
一、引入包
import torch
import matplotlib.pyplot as plt
import random
二、创建数据函数
def create_data(w, b, data_num):
x = torch.normal(0, 1, (data_num, len(w)))
y = torch.matmul(x, w) + b
noise = torch.normal(0, 0.01, y.shape) # y 同型
y += noise
return x, y
num = 500
true_w = torch.tensor([0.1, 2, 2, 4])
true_b = torch.tensor(1.1)
X, Y = create_data(true_w, true_b, num) # 创建数据 X为500x4 Y为500x1 的tensor类型数据
# plt.scatter(X[:, 1], Y, 1)
# plt.show()
数据X使用torch生成服从N(0,1)的标准正态分布,这里规模用的是500x4。
标签Y=X*w+b,x为500x4的矩阵,w为4x1的矩阵,所以y为500x1的矩阵,为了模仿真实世界的随机性,生成了一个与y同型的服从N(0,0.01)矩阵noise,然后加到y上。
X,Y为实际数据
三、打乱数据并按组取出函数
# 每次访问提供 batchsize 个乱序数据
def data_provider(data, label, batchsize):
length = len(label)
indices = list(range(length))
random.shuffle(indices) # 打乱数据
for each in range(0, length, batchsize):
get_indices = indices[each: each+batchsize] # 取一组batchsize个乱序序号
get_data = data[get_indices]
get_label = label[get_indices]
yield get_data, get_label
batchsize = 16
# for batch_x, batch_y in data_provider(X, Y, batchsize):
# print(batch_x, batch_y)
# break
data就是X,label就是Y,batchsize=16
获取Y的长度,然后生成一个[0,len(label))的数组indices,然后打乱这个数组,相当于打乱数据的序号。
然后写一个for循环便利indices数组,步长为batchsize,每次返回一组16个数据和标签。
四、一些其他函数
def fun(x, w, b):
perd_y = torch.matmul(x, w) + b
return perd_y
def maeLoss(pred_y, y):
loss = torch.sum(abs(pred_y - y)) / len(y)
return loss
# 梯度下降, 反向传播不计算梯度
def sgd(paras, lr):
with torch.no_grad(): # 接下来不计算梯度
for para in paras:
para -= para.grad*lr
para.grad.zero_() # 把梯度归零
fun函数会用于在训练时计算预测值
maeLoss函数计算损失值,loss是一个标量
sgd函数用于更新参数,lr会调节更新的幅度。并将梯度归零
五、开始训练
lr = 0.03
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True) # w_0需要被计算梯度
b_0 = torch.tensor(0.01, requires_grad=True)
epochs = 50 # 训练轮数
for epoch in range(epochs):
data_loss = 0
for batch_x, batch_y in data_provider(X, Y, batchsize):
pred = fun(batch_x, w_0, b_0) # 前向传播
loss = maeLoss(pred, batch_y) # 计算损失
loss.backward() # 反向传播计算梯度
sgd([w_0, b_0], lr) # 更新参数
data_loss += loss
print("epoch %03d: loss%.6f"%(epoch, data_loss))
print("原来的函数值:", true_w, true_b)
print((w_0, b_0))
idx = 3
plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy(), label="pred")
plt.scatter(X[:, idx], Y, 1)
plt.show()
lr是学习率,决定了参数变化的大小,lr为超参数
w_0是权重的初值,同样随机地设置为正态分布,注意一定要设置requires_grad=True(需要被计算梯度), 这样才会被识别为参数
b_0为初始的偏置,同样requires_grad=True
训练轮数为50轮,因为一轮训练可能不够找到最小的loss值
然后写一个for循环迭代50轮,每一轮取16个数据,首先计算当前参数下的预测值pred,然后计算真实值和预测值的损失loss,然后计算loss的梯度并反向传播给w_0和b_0,最后用sgd更新参数
下面是一些我个人产生过的疑问:
1、为什么是计算loss的梯度?
梯度是一个由偏导数组成的向量,它暗示了下降最快的方向,例如本例中:

那么loss下降最快的方向就是w沿
,b沿
方向下降的情况。
当然,这种普通的梯度下降法只能用于无约束条件下
2、loss是一个标量,哪里来的梯度?
loss 是标量,但它是参数 w_0 和 b_0 的函数,这并不冲突
尽管计算结果是一个标量,但作为pytorch的一个类,在计算梯度时pytorch仍可以知道它的函数形式,可以正确地计算偏导数
更多推荐
所有评论(0)