赞
踩
早上没睡醒!又是在梦中被坏女人骗走感情的一天。
线性回归是一种最基本的回归分析方法,旨在将一个或多个自变量与一个连续的因变量之间的关系建模为线性方程。该方法可以用于预测因变量的值,也可用于描述自变量和因变量之间的关系。
在线性回归中,我们假设自变量和因变量之间存在线性关系。这意味着,如果我们增加自变量的一个单位,那么因变量的值也将相应地增加一个固定的量。线性回归模型的基本形式如下:
y = β 0 + β 1 x 1 + β 2 x 2 + . . . + β n x n + ε y = β0 + β1x1 + β2x2 + ... + βnxn + ε y=β0+β1x1+β2x2+...+βnxn+ε
其中,y是因变量的值,x1、x2、…、xn是自变量的值,β0、β1、β2、…、βn是模型的参数,ε表示误差项。
线性回归的目标是找到最佳的参数值,使得模型的预测值与实际值之间的差异最小化。通常使用最小二乘法来估计参数值,即通过最小化残差平方和来确定最佳的参数值。线性回归有如下特点:
总之,线性回归是一种基础的回归分析方法,可以用于建立自变量和因变量之间的线性关系模型,并对未来的数据进行预测。
当我们使用线性回归模型建立自变量和因变量之间的关系时,我们需要做以下几个步骤:
线性回归的定义:线性回归是一种通过自变量(一个或多个特征值)与因变量(目标值)之间的关系来进行建模的回归分析。
一元线性回归:即只用一个x来预测y,就是一元线性回归,一元线性回归的任务即找到一条直线(
y
=
w
x
+
b
y=wx+b
y=wx+b)来尽量的拟合图中的所有数据点。
损失函数:损失函数是机器学习中一个重要的概念,它用于度量模型在训练数据上的性能。损失函数是一个将预测值和真实值作为输入的函数,它的输出是一个标量,表示模型在该预测值下的误差大小。损失函数越小,说明模型的预测结果越接近真实值。是用来评价直线对于图中所有数据点的拟合程度, 一般情况下,我们使用均方误差来评价直线的拟合程度,即均方误差的值越小,说明直线越能拟合我们的数据。
在训练模型时,我们的目标是最小化损失函数。通常采用梯度下降等优化算法来寻找最小化损失函数的模型参数。
常见的损失函数包括:
下边以均方误差为例:
MSE(Mean Square Error)均方误差:即真实值与预测值的差值的平方然后求和再平均
M
S
E
=
1
m
∑
i
=
1
m
(
y
i
−
f
(
x
i
)
)
2
MSE = \frac1m\sum_{i=1}^m{(y_i-f(x_i))}^2
MSE=m1i=1∑m(yi−f(xi))2
注:其中的黄色线代表真实值与预测值的差。
梯度下降的定义:梯度下降是机器学习中的一种优化算法,用于寻找函数的最小值。在机器学习中,我们通常使用梯度下降算法来寻找损失函数的最小值,以便优化模型的参数。
梯度下降的基本思想是,从一个随机的起始点开始,沿着负梯度方向(导数值最小的方向)逐步迭代更新参数,直到达到损失函数的最小值。在梯度下降中,我们通过计算损失函数对每个参数的导数来确定下一步应该向哪个方向移动参数,以最小化损失函数。这个导数被称之为梯度,通过更新参数的值,我们可以逐步降低损失函数,直到达到一个局部最小值。
梯度下降的目的:不断学习改进,即梯度下降的目的,就是使得损失函数最小化。
梯度下降的原理:使用微积分里的导数,通过求出函数导数的值,从而找到函数下降的方向或者是最低点。
梯度下降有两种形式:批量梯度下降(BGD)和随机梯度下降(SGD)。在批量梯度下降中,每次迭代时,我们使用所有训练样本来计算损失函数和梯度,并更新参数。这种方法可以获得全局最优解,但是对于大规模数据集来说,计算成本很高。
相比之下,随机梯度下降每次只使用一个样本来计算梯度,并更新参数。这种方法计算成本低,但容易收敛到局部最优解。为了平衡这两个方法的优缺点,还有一种折中方法,称为小批量随机梯度下降(mini-batch SGD),它使用一小批数据来计算梯度,并更新参数
梯度下降的过程:
1、for 循环 to 训练次数(一般来说训练1000次,即较多次数,设置条件只要满足损失小于多少就停止):
2、随机生成权重w和偏差b,要求符合正态分布。(直接设置两个1也可以,后续会w和b会更新)。设置学习率,一般设置为(0.01, 0.1, 1)
3、计算每一个训练数据的预测值(
y
=
w
x
+
b
y=wx+b
y=wx+b),求得损失函数,并且计算每一个训练数据的权重和偏差相对于损失函数的梯度,即我们最终会得到每一个训练数据的权重和偏差的梯度值。
4、计算所有训练数据权重w的梯度的总和。
5、计算所有训练数据偏差b的梯度的总和。
6、求得所有样本的权重和偏差的梯度的平均值。
7、根据公式来更新权重值和偏差值,即使用设置好的学习率来进行更新。
(
w
t
−
1
.
g
r
a
d
是损失函数对
w
t
−
1
求偏导得到的梯度
)
(w_{t-1}.grad是损失函数对w_{t-1}求偏导得到的梯度)
(wt−1.grad是损失函数对wt−1求偏导得到的梯度)
w
t
=
w
t
−
1
−
L
R
∗
w
t
−
1
.
g
r
a
d
w_t = w_{t-1}- LR*w_{t-1}.grad
wt=wt−1−LR∗wt−1.grad
b
t
=
b
t
−
1
−
L
R
∗
b
t
−
1
.
g
r
a
d
b_t = b_{t-1}- LR*b_{t-1}.grad
bt=bt−1−LR∗bt−1.grad
8、循环,直到满足损失小于多少为止。
import torch
import matplotlib.pyplot as plt
# 设置CPU生成随机数的种子,方便下次复现实验结果。
torch.manual_seed(9)
# 设置学习率为0.1
lr = 0.05
# 创建训练数据
# 创建二维列表,
x = torch.rand(20, 1)*10
y = 2*x + (5 + torch.randn(20, 1))
输出x和y:
# 随机参数w和b
w = torch.randn((1), requires_grad=True)
b = torch.randn((1), requires_grad=True)
输出w和b:
for i in range(1000): # 前向传播 # torch.mul作element-wise的矩阵点乘,维数不限,可以矩阵乘标量 # 当a, b维度不一致时,会自动填充到相同维度相点乘。 wx = torch.mul(w, x) # 支持广播相加 y_pred = torch.add(wx, b) # 计算MSE Loss # 反向传播, ✖2分之一是为了方便求导 loss = (0.5 * (y - y_pred) ** 2).mean() # 反向传播, 计算当前梯度 loss.backward() # 更新参数 # w = w- LR*w.grad # b = b- LR*w.grad # 函数形式:torch.sub(input, other, *, alpha=1, out=None) # 参数解读: # input: 输入的被减数,格式为tensor格式 # other:输入的减数 # alpha:与上面other参数搭配使用,用来与other相乘,当使用torch.sub()函数时不指定alpha的值时,alpha默认为1 # out: 指定torch.sub()输出值被赋给的变量,可不指定。 # 然而 # torch.sub_()功能与torch.sub()相同,区别在与torch.sub_()是torch.sub()的in-place操作版本。 b.data.sub_(lr * b.grad) w.data.sub_(lr * w.grad) # 绘图 if i % 20 == 0: plt.cla() # 防止社区版可视化时模型重叠2020-12-15 plt.scatter(x.data.numpy(), y.data.numpy()) plt.plot(x.data.numpy(), y_pred.data.numpy(), 'r-', lw=5) plt.text(2, 20, 'Loss=%.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'}) plt.xlim(1.5, 10) plt.ylim(8, 28) plt.title("Iteration: {}\nw: {} b: {}".format(i, w.data.numpy(), b.data.numpy())) plt.pause(0.5) if loss.data.numpy() < 1: break plt.show()
迭代的最终图:
import random import torch from d2l import torch as d2l import plotly.express as px import pandas as pd from sklearn.model_selection import train_test_split def synthetic_data(w, b, num_examples): #@save """生成y=Xw+b+噪声""" # torch.normal 是 PyTorch 中的一个函数,用于从正态(高斯)分布中生成随机数。 # 具体来说,它接受两个参数:均值(mean)和标准差(standard deviation),并返回一个张量,其中的元素是从指定分布中随机生成的。 # 最后一个参数为size,生成数据的量以及维度。 # 生成num_examples组,每组的长度和w的长度相等。 X = torch.normal(0, 1, (num_examples, len(w))) # 在PyTorch中,torch.matmul 函数用于执行矩阵乘法运算。它接受两个张量作为输入,例如:torch.matmul(a, b), # 其中 a 和 b 可以是2D、3D或高维张量。两个输入张量必须满足一定的维度要求,以便矩阵乘法能够执行, # 否则会引发错误。比如说,当 a 是形状为 (n, m) 的 2D 张量,b 是形状为 (m, p) 的 2D 张量时, # torch.matmul(a, b) 将返回一个形状为 (n, p) 的 2D 张量,这个张量包含了矩阵乘法的结果。 y = torch.matmul(X, w) + b y += torch.normal(0, 0.01, y.shape) return X, y.reshape((-1, 1)) # 初始的w和b true_w = torch.tensor([2, -3.4]) true_b = 4.2 # 调用 # 得到我们要使用的特征和标签 features, labels = synthetic_data(true_w, true_b, 1000)
概述:每次抽取一小批量样本,并使用它们来更新我们的模型。定义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 = torch.tensor( indices[i: min(i + batch_size, num_examples)]) # 在 Python 中,"yield" 是一个关键字,用于定义生成器函数。生成器函数是一种特殊的函数, # 可以在函数执行期间产生多个值,而不是一次性返回所有的值。当生成器函数执行到 "yield" 语句时, # 它会暂停执行并将一个值返回给调用者。下次调用生成器函数时,它会从上一次暂停的位置继续执行,直到再次遇到 "yield" 语句。 yield features[batch_indices], labels[batch_indices]
当取批量为10时,预览一批次数据(即该函数可以连续获得不同的小批量,直到遍历完整个数据集):
# 定义我们的线性回归模型 def linreg(X, w, b): #@save """ 线性回归模型 将模型的输入和参数同模型的输出关联起来。 输入特征X和模型权重w的矩阵-向量乘法后加上偏置b,这里为广播机制。 """ return torch.matmul(X, w) + b def squared_loss(y_hat, y): #@save """ 均方损失 将真实值y的形状转换为和预测值y_hat的形状相同。 """ return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2 def sgd(params, lr, batch_size): #@save """ 小批量随机梯度下降 从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。接下来 朝着减小损失的方向更新我们的参数。 """ with torch.no_grad(): for param in params: param -= lr * param.grad / batch_size param.grad.zero_()
Notice:随机化w和b,通过训练, 计算小批量损失,反向传播,通过参数的梯度来更新参数,最终训练得到的w和b,与实际的w和b作差,得到w、b的估计误差。
# 设置批次为10 batch_size = 10 # 我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重, 并将偏置初始化为0。 w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True) b = torch.zeros(1, requires_grad=True) # 开始训练: # 初始化参数 # 计算梯度并且更新参数。 lr = 1 num_epochs = 10 # 使用到的模型 net = linreg # 损失函数 loss = squared_loss for epoch in range(num_epochs): for X, y in data_iter(batch_size, features, labels): l = loss(net(X, w, b), y) # X和y的小批量损失 # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起, # 并以此计算关于[w,b]的梯度 l.sum().backward() sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数 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}')
注意:这里的其中几个函数被设计为静态方法是避免不必要的实例创建,进而优化内存使用和计算速度,也方便调用,不需要实例即可调用。
import torch import random class LinearRegressionModel: def __init__(self, num_features, learning_rate=0.01, batch_size=10, num_epochs=10): self.num_features = num_features self.learning_rate = learning_rate self.batch_size = batch_size self.num_epochs = num_epochs # Initialize weights and bias self.w = torch.normal(0, 0.01, size=(num_features, 1), requires_grad=True) self.b = torch.zeros(1, requires_grad=True) @staticmethod def synthetic_data(w, b, num_examples): X = torch.normal(0, 1, (num_examples, len(w))) y = torch.matmul(X, w) + b y += torch.normal(0, 0.01, y.shape) return X, y.reshape((-1, 1)) @staticmethod 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 = torch.tensor(indices[i: min(i + batch_size, num_examples)]) yield features[batch_indices], labels[batch_indices] @staticmethod def linreg(X, w, b): return torch.matmul(X, w) + b @staticmethod def squared_loss(y_hat, y): return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2 @staticmethod def sgd(params, lr, batch_size): with torch.no_grad(): for param in params: param -= lr * param.grad / batch_size param.grad.zero_() def train(self, features, labels): for epoch in range(self.num_epochs): for X, y in self.data_iter(self.batch_size, features, labels): l = self.squared_loss(self.linreg(X, self.w, self.b), y) l.sum().backward() self.sgd([self.w, self.b], self.learning_rate, self.batch_size) with torch.no_grad(): train_l = self.squared_loss(self.linreg(features, self.w, self.b), labels) print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}') return self.w, self.b # Example usage: true_w = torch.tensor([2, -3.4]) true_b = 4.2 num_examples = 1000 # Instantiate your model model = LinearRegressionModel(num_features=2, learning_rate=0.01, batch_size=10, num_epochs=20) features, labels = model.synthetic_data(true_w, true_b, num_examples) w, b = model.train(features, labels) print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}') print(f'b的估计误差: {true_b - b}') print(f'w的估计值为:{w}') print(f'b的估计值为:{b}')
输出:
epoch 1, loss 2.390462
epoch 2, loss 0.377770
epoch 3, loss 0.060474
epoch 4, loss 0.009834
epoch 5, loss 0.001657
epoch 6, loss 0.000318
epoch 7, loss 0.000097
epoch 8, loss 0.000061
epoch 9, loss 0.000055
epoch 10, loss 0.000054
epoch 11, loss 0.000054
epoch 12, loss 0.000054
epoch 13, loss 0.000054
epoch 14, loss 0.000054
epoch 15, loss 0.000054
epoch 16, loss 0.000054
epoch 17, loss 0.000054
epoch 18, loss 0.000054
epoch 19, loss 0.000054
epoch 20, loss 0.000054
w的估计误差: tensor([-0.0005, 0.0002], grad_fn=)
b的估计误差: tensor([-0.0004], grad_fn=)
w的估计值为:tensor([[ 2.0005],
[-3.4002]], requires_grad=True)
b的估计值为:tensor([4.2004], requires_grad=True)
概述:使用PyTorch的高级API更简洁地实现模型,data模块提供了数据处理工具,而nn模块定义了大量的神经网络层和常见损失函数。
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
def load_array(data_arrays, batch_size, is_train=True): #@save
"""构造一个PyTorch数据迭代器"""
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
# nn是神经网络的缩写
from torch import nn
# 我们将两个参数传递到nn.Linear中。 第一个指定输入特征形状,即2,第二个指定输出特征形状,输出特征形状为单个标量,因此为1。
net = nn.Sequential(nn.Linear(2, 1))
# 使用net之前,我们需要初始化模型参数。如回归模型中的权重和偏置。我们通过net[0]选择网络中的第一个图层, 然后使用weight.data和bias.data方法访问参数。 我们还可以使用替换方法normal_和fill_来重写参数值。
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
# 计算均方误差使用的是MSELoss类,也称为平方
# 范数。 默认情况下,它返回所有样本损失的平均值。
loss = nn.MSELoss()
# 小批量随机梯度下降算法是一种优化神经网络的标准工具, PyTorch在optim模块中实现了该算法的许多变种。 当我们实例化一个SGD实例时,我们要指定优化的参数 (可通过net.parameters()从我们的模型中获得)以及优化算法所需的超参数字典。 小批量随机梯度下降只需要设置lr值,这里设置为0.03。
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X) ,y)
trainer.zero_grad()
l.backward()
trainer.step()
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')
w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)
参考文章:
用人话讲明白线性回归LinearRegression.
线性回归,损失的定义,损失函数与优化方法,用统计学习方法来理解线性回归、损失函数和优化方法,Sklearn使用方法.
梯度下降算法(Gradient Descent)的原理和实现步骤.
年纪大了,推个简单公式得写好久。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。