线性回归简易实例
文章目录
导入模块
python
import torch
import matplotlib.pyplot as plt #画图
import random #随机
torch
库帮助进行梯度计算和参数更新,实现训练过程(随机梯度下降、损失函数的计算等),提供了高效的张量计算和自动求导功能,使得训练机器学习模型更加简便和高效。matplotlib.pyplot
库用于绘制图像,使运行结果可视化。random
的作用是引入随机性。
所模拟的模型
假设数据符合一个如下图的线性回归模型:
- 输入数据x有四个维度。分别是外貌、性格、财富、内涵。
- 输出数据y为恋爱次数。
- w的四个维度为x的真实权值分为为8.1,2,2,4。分别对应外貌、性格、财富、内涵。
- b的真实值为1.1。
神经网络的目的就是使用随机的w和b通过深度学习的方法找出真实的w和b的值。
生成数据
python
x = torch.normal(0, 1, (data_num, len(w)))
y = torch.matmul(x, w) + b #矩阵相乘
从正态分布中随机生成平均数为0,方差为1,数量为data_num
,维度为len(w)
的数据集x。对生成的数据集x与真实的w进行矩阵相乘加上真实的b得到一个符合假设模型的数据集。
计算过程如下图:
python
noise = torch.normal(0, 0.01, y.shape) #噪声加到y上
y += noise
由于实际情况下,真实的数据不会完全符合模型,因此加入一些噪声更好的模拟真实的数据情况。
完整的生成数据代码:
python
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 #生成的输入数据个数为500个
true_w = torch.tensor([8.1, 2, 2, 4]) #设定真实的参数用于生成数据
true_b = torch.tensor(1.1)
X, Y = create_data(true_w, true_b, num)
plt.scatter(X[:, 0], Y, 1) #打印输入数据第一列和输出数据的关系
plt.show()
结果如下:
获取数据
由于实际情况下,数据是收集得到的,因此实际的第一步操作是要获取数据。
python
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] #每次取batchsieze个数据
get_data = data[get_indices]
get_label = label[get_indices]
yield get_data, get_label #有存档点的return
batchsize = 16
-
data
参数为输入数据集。 -
label
参数为输出数据集。 -
使用
random.shuffle
随机打乱indices
列表中的元素顺序,这样每次训练时访问的数据顺序都不会固定,从而避免模型在训练过程中出现过拟合。 -
yield get_data, get_label
语句的作用是将当前批次的数据(get_data
)和标签(get_label
)返回给调用方,并且使得函数的执行暂停,待下一次调用时继续从当前位置执行。这使得data_provider
函数成为一个生成器。具体来说过程如下:第一次调用
yield
后的执行流程:- 当你第一次迭代
data_provider(data, label, batchsize)
时:- 生成器函数从头开始执行。
- 执行到
for
循环的第一步,获取当前批次的数据(get_data
)和标签(get_label
)。 - 然后执行
yield get_data, get_label
,这时函数暂停并返回get_data
和get_label
。
- 函数的状态 :
- 此时,生成器函数的执行暂停,所有局部变量(如
indices
,get_indices
,get_data
,get_label
)都被保存下来,以便下一次继续使用。 - 程序控制流回到调用
for
循环的地方,返回数据。
- 此时,生成器函数的执行暂停,所有局部变量(如
第二次及以后调用时函数后的执行流程
- 恢复执行 :生成器函数不会从头开始执行,而是从上次暂停的地方继续执行。也就是说,函数会从上次执行
yield
后的地方继续(在本例中是从for
循环的下一个迭代开始,因为每循环一次,就遇到yield
进行了暂停)。 - 继续执行 :
- 在上次暂停后,生成器已经完成了
yield get_data, get_label
,此时函数的执行状态被保留。 - 生成器函数继续执行下一个批次的数据生成,并再次遇到
yield
。 - 返回下一个批次的
get_data
和get_label
。
- 在上次暂停后,生成器已经完成了
- 暂停并返回数据 :每次执行到
yield
时,函数会暂停并返回相应的批次数据,直到下一次调用。
- 当你第一次迭代
-
batchsize
设定每次所取的数据个数。
定义模型
python
def fun(x, w, b): #模型
pred_y = torch.matmul(x, w) + b
return pred_y
定义线性回归模型,使用该模型拟合数据的关系,获取预测值。
公式如下图:
定义LOSS
python
def maeLoss(pre_y, y):
return torch.sum(abs(pre_y-y))/len(y)
定义LOSS函数,用于计算预测值和真实数据的误差。
公式如下图:
使用模型拟合出真实参数
实现梯度下降函数,用于更新参数
梯度下降,更新参数的方法如下图:
首先,设置一个初始的权重 w 0 w^0 w0(w的第0个版本),然后计算损失函数 L(衡量模型预测结果与真实结果之间差异的函数),然后计算损失函数相对于参数的梯度。梯度表示了损失函数在某个点的斜率,指示了在当前参数下,损失函数增加或减少的方向和大小。例如,对于权重 w 0 w^0 w0,其梯度是损失函数对权重的导数。然后使用如下公式更新参数:
知道参数的损失函数值符合我们的需求。其中学习率learning rate
决定了优化算法(如梯度下降)在每一步中调整模型权重的幅度。所有参数包括偏置在梯度下降过程中操作相当。
梯度下降代码的具体代码实现:
python
def sgd(paras, lr): #随机梯度下降,更新参数
with torch.no_grad(): #属于这句代码的部分,不计算梯度
for para in paras:
para -= para.grad*lr #不能写成para = para - para.grad*lr
para.grad.zero_() #使用过的梯度,归零
lr = 0.03 #学习度
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True) #这个需要计算梯度
b_0 = torch.tensor(0.01, requires_grad=True)
with torch.no_grad()
表示在此代码块中的操作不会计算梯度,防止在执行参数更新操作时计算不必要的梯度。因为推理参数阶段,不需要计算梯度,只进行前向传播来获取输出,只有在进行优化更新参数时需要使用梯度。para.grad.zero_()
用来将当前参数的梯度归零的。因为在PyTorch中,每次反向传播时,梯度会默认被累加。为了防止梯度不断累加导致的错误,我们需要在每次更新参数之后,将这些梯度清零。lr = 0.03
,设置学习度w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True)
来从正态分布中随机初始化权重,其中:0是均值,0.01是标准差,true_w.shape
表示权重 的形状和真实权重true_w
相同,requires_grad=True
表示该张量需要计算梯度。b_0 = torch.tensor(0.01, requires_grad=True)
定义了初始的偏置b_0
。它使用torch.tensor()
创建了一个数值为0.01
的标量,并且同样设置了requires_grad=True
,表示这个值需要计算梯度。
训练函数
使用定义的模型和LOSS函数,进行训练,得到参数,具体代码如下:
python
epochs = 50 #训练的次数
for epoch in range(epochs):
data_loss = 0
for batch_x, batch_y in data_provider(X, Y, batchsize):
pred_y = fun(batch_x, w_0, b_0)
loss = maeLoss(pred_y, 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 = 0
plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy())
plt.scatter(X[:, idx], Y, 1) #用来选择第idx个特征(即 X[:, idx])来绘制模型的预测结果和真实数据。
plt.show() #打印
epochs = 50
设定训练的次数。for batch_x, batch_y in data_provider(X, Y, batchsize):
使用data_provider
获取batchsize
个数的数据。pred_y = fun(batch_x, w_0, b_0)
,使用当前的权重 和偏置计算出预测的输出值。loss = maeLoss(pred_y, batch_y)
,计算真实值和预测值的误差。loss.backward()
是用来执行反向传播过程的,它会根据损失函数loss
计算所有可训练参数(如w_0
和b_0
)的梯度。。sgd([w_0, b_0], lr)
这是一个基于 随机梯度下降(SGD) 的优化步骤,更新模型参数。sgd
函数根据计算出的梯度更新w_0
和b_0
的值,学习率由lr
指定。具体来说,sgd
会使用每个参数的梯度来调整它们的值,从而最小化损失函数。
输出结果如下图:
完整代码
python
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([8.1, 2, 2, 4])
true_b = torch.tensor(1.1)
X, Y = create_data(true_w, true_b, num)
# plt.scatter(X[:, 0], Y, 1)
# plt.show()
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] #每次取batchsieze个数据
get_data = data[get_indices]
get_label = label[get_indices]
yield get_data, get_label #有存档点的return
batchsize = 16
# for batch_x, batch_y in data_provider(X, Y, batchsize):
# print(batch_x, batch_y)
def fun(x, w, b): #模型
pred_y = torch.matmul(x, w) + b
return pred_y
def maeLoss(pre_y, y):
return torch.sum(abs(pre_y-y))/len(y)
def sgd(paras, lr): #随机梯度下降,更新参数
with torch.no_grad(): #属于这句代码的部分,不计算梯度
for para in paras:
para -= para.grad*lr #不能写成para = para - para.grad*lr
para.grad.zero_() #使用过的梯度,归零
lr = 0.03
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True) #这个需要计算梯度
b_0 = torch.tensor(0.01, requires_grad=True)
# print(w_0, b_0)
epochs = 50
for epoch in range(epochs):
data_loss = 0
for batch_x, batch_y in data_provider(X, Y, batchsize):
pred_y = fun(batch_x, w_0, b_0)
loss = maeLoss(pred_y, 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 = 0
plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy())
plt.scatter(X[:, idx], Y, 1)
plt.show()