【AI学习从零至壹】Pytorch逻辑回归
Pytorch逻辑回归
- 线性回归
- 简单线性回归的参数估计
- 概率和似然的区别
- 最⼤似然估计
- 似然函数
- 对数似然函数
- 逻辑回归
- 梯度下降法
- 下⼭问题
- 梯度与学习率
- 学习率
- 梯度下降法的模拟与可视化
- 学习率对梯度的影响
- 学习率的最佳取值
- 梯度更新
- 逻辑回归模型构建及训练流程
线性回归
线性回归的⽬标就是找到⼀个线性函数来拟合数据,使得预测值与真实值之间的误差尽可能⼩,⽽参数估计就是确定这个线性函数中参数的过程。
简单线性回归的参数估计
简单线性回归模型的表达式为: y = β0 + β1x + ϵ,其中 是因变量, X是⾃变量, B0是截距, B1是斜率, ϵ是误差项(服从均值为 0 的正态分布)。
斜率 表⽰直线的倾斜程度,它衡量了⾃变量每变动⼀个单位时,因变量的平均变动量。在机器学习和优化算法中,为了更简洁通⽤地表⽰这些参数,常常⽤ 来统⼀表⽰模型的参数向量。
误差真实值和预测值之间肯定是要存在差异的(用ϵ来表示该误差)
对于每个样本:
概率和似然的区别
概率(Probably),是在已知⼀些概率分布参数的情况下,预测观测值的结果;
似然(Likelihood),则是⽤于在已知某些观测值所得到的结果时,对观测结果所属的概率分布参数进⾏估计。
X表⽰某⼀个具体的数据; 表⽰模型的参数。
- 如果0是已知确定的,X 是变量,这个函数叫做概率函数(probability function),它描述对于不同的样本点 ,其出现概率是多少。
- 如果X是已知确定的, 0是变量,这个函数叫做似然函数(likelihood function),它描述对于不同的模型参数,出现 这个样本点的概率是多少。
最⼤似然估计
最⼤似然估计(Maximum Likelihood Estimation)就是⼀种可以⽣成拟合数据任何分布的参数的最可能估计的技术。
简单解释就是:最⼤似然估计的⽬的就是找到⼀个最符合当前观测数据的概率分布。
似然函数
对数似然函数
我们对似然函数取对数, 就是对数似然函数
为什么要取对数?
原始的似然函数是很多条件概率的乘积, 在计算极⼤值的时候需要求似然函
数的导数。 ⽽乘积的导数计算很⿇烦, 所以取对数可以把乘法变成加法。
逻辑回归
⽽这其中的转换,我们使⽤的就是sigmoid函数
回过头来看之前说过的
梯度下降法
在机器学习中,梯度下降法的作⽤就是:最⼩化⼀个损失函数。
下⼭问题
- 梯度下降的思想其实就是⼀个下⼭的过程,假设我们爬到了⼭顶,然后感觉很累想尽快下⼭休息。但是这个时候起雾了,⼭上都是浓雾,能⻅度很低。那么我们下⼭的路径是⽆法确定的,这时候我们要怎么下⼭呢?聪明的你肯定能想到利⽤周围能看⻅的环境去找下⼭的路径,这个时候梯度下降的思想就能体现了,我们⾸先要找到周围最陡峭的地⽅,然后朝着地势往下的⽅向⾛,⾛完⼀段距离发现最陡峭的⽅向变了,那么我们调整⼀下⽅向接着沿地势往下的⽅位⾛。这样不断的进⾏下去,理论上最后⼀定会到达⼭脚
- 假设这座⼭有的坡特别平缓,那么⼏个⽅向的陡峭程度很接近,我们⽆法⽤⾁眼测量出最陡峭的,假如恰好我们带了⼀个测量仪,然后每⾛⼀段距离我们就会测量⼀次,这时候就会出现⼀个问题,如果我们每⾛⼏步就测量⼀次,那么可能⾛到明天我们都下不去⼭,但是我们如果减少测量次数,那么可能就会偏离下⼭的最快的路径。这⾥我们就会需要⼀个合适的测量频率,保证我们下⼭路径⾜够快,然后⼜不需要太多时间去测量。
- 因此,我们不断的使⽤这种⽅法,反复的求取梯度,函数就能达到相对的最⼩值。为什么是相对⽽不是绝对呢? 我们下⼭过程中可能会遇到这种情况,根据地势往下⾛,不⼩⼼进⼊了⼀个很深的洼地,这时四周都是地势向上了,⼈是很聪明的,我们可以爬出来继续找个其它地势向下的⽅向,但是计算机可没有那么聪明,它可能会深陷其中⽆论怎么求梯度都爬不出来了。这就是函数可能只能够达到局部的最⼩值,⽽不是全局的最⼩值。
梯度与学习率
梯度
梯度下降的梯度是什么?函数在某⼀点的梯度是这样⼀个向量,它的⽅向与取得最⼤⽅向导数的⽅向⼀致,⽽它的模为⽅向导数的最⼤值。在存在多个变量的函
数中,梯度是⼀个向量,向量有⽅向,梯度的⽅向就指出了函数在给定点的上升最快的⽅向。这就意味着我们需要到达⼭底,那么在我们下⼭测量中,梯度就告诉我们下⼭的⽅向。梯度的⽅向是函数在给定点上升最快的⽅向,那么反⽅向就是函数在给定点下降最快的⽅向,所以我们只要沿着梯度的反⽅向⼀直⾛,就能⾛到局部的最低点!
由于神经⽹络模型中有众多的参数,也称为权重参数(weight parameter)所以我们常常需要处理的是多元复合函数,要想知道某⼀个权重参数对损失函数的影响,那么就要求它的偏导数。因此对于权重参数,我们是可以确定向量的⽅向的,就是求它的导数值就可以了
学习率
学习率看起来很陌⽣,但结合着下⼭问题来解释就很好理解了。⽐如说,我们说要找⼀个合适的测量频率,保证我们下⼭⼜快,测量次数⼜少。那么这个学习率就是影响我们测量频率的因素。可以将其理解为梯度下降过程中的步⻓。
在我们训练模型的时候,学习率是⾥⾯很重要的⼀个超参数
(Hyperparameters),它决定着我们的损失函数能否收敛到最⼩值,还有需要多⻓时间才能收敛到最⼩值。⼀个合适的学习率能够让我们的损失函数在合适的时间内收敛到局部最⼩值。
如果学习率过⼤的时候,函数收敛过程如下图所⽰:
可以看出来,当学习率设定太⼩时,收敛过程将变得⼗分缓慢并且可能不收敛。反⽽学习率设置的过⼤时,梯度可能会在最⼩值附近来回震荡,甚⾄可能⽆法收敛。
梯度下降法的模拟与可视化
上图描述的是⼀个损失函数在函数不同参数取值的情况下(x轴),对应变化值(y轴)。
对于损失函数,应该有⼀个最⼩值。对于最⼩化损失函数这样⼀个⽬标,实际上就是在上图所⽰的坐标系中,寻找合适的参数,使得损失函数的取值最⼩。
我们⽤代码来绘制⼀张这样的图
import numpy as np
import matplotlib.pyplot as plt
plot_x =np.linspace(-1,6,150)
print(plot_x)
plot_y = (plot_x-2.5)**2 -1
plt.show()
学习率对梯度的影响
可以看到theta在下降过程中,每⼀步都在逐渐变⼩(逐渐逼近),直到满⾜⼩于epsilon的条件
学习率的最佳取值
梯度更新
在得到了逻辑回归的损失函数后,我们就可以使⽤梯度下降法来寻找损失函数极⼩值。直⽩的说,就是要求出, 当 0取什么值时, 损失函数可以到达极⼩值。
逻辑回归模型构建及训练流程
首先,我们先数据准备,参数初始化
Scikit-learn 是⽤ Python 开发的开源机器学习库,⼴泛⽤于数据挖掘和数据分析。
特点:易⽤、⾼效,具有丰富的⼯具,涵盖分类、回归、聚类等多种机器学习算法。
功能:提供数据预处理、模型选择、评估等功能,便于构建完整的机器学习⼯作流。
优势:有详细⽂档和⽰例,社区活跃,能降低开发成本、提⾼效率。
https://scikit-learn.org/stable/api/index.html
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import numpy as np
#生成训练数据
X,y = make_classification(n_samples=150,n_features=10) #shape (150,10)
#数据拆分
#局部样本训练模型
#新样本数据模型表现不好
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3)
接着我们开始训练
#权重参数
theta = np.random.randn(1,10) #shape (1,10)
bias = 0
#超参数
lr = 0.01
epochs = 3000 #训练次数
#模型计算参数
def forward(theta,X_train):
Z = np.dot(theta,X_train.T) + bias
#将预测值转化为概率
y_hat= 1/(1 + np.exp(-Z))
return y_hat
#损失函数
def loss(y,y_hat):
return -y * np.log(y_hat + 1e-8) - (1-y) * np.log(1 - y_hat + 1e-8)
#计算梯度
def cal_gradient(y_hat,y,X):
m = X.shape[-1]
delta_theta = np.dot((y_hat - y),X)/m
delta_bias = np.mean(y_hat - y)/m
return delta_theta, delta_bias
#模型训练
for i in range(epochs):
#前向计算
y_hat = forward(theta , X_train)
#计算损失
loss_value = loss(y_train,y_hat)
#计算梯度
delta_theta, delta_bias = cal_gradient(y_hat,y_train,X_train)
#更新梯度
theta = theta - lr * delta_theta
bias = bias - lr * delta_bias
if i % 100 == 0:
# 计算准确率
acc = np.mean(np.round(y_hat) == y_train) # [False,True,...,False] -> [0,1,...,0]
print(f"epoch: {i}, loss: {np.mean(loss_value)}, acc: {acc}")