目录
1.逻辑回归介绍
逻辑回归是监督学习,主要解决二分类问题。
逻辑回归虽然有回归字样,但是它是一种被用来解决分类的模型,为什么叫逻辑回归是因为它是利用回归的思想去解决了分类的问题。
逻辑回归和线性回归都是一种广义的线性模型,只不过逻辑回归的因变量(Y)服从伯努利分布(离散分布),而线性回归的因变量(Y)满足的是高斯分布(正态分布),因此他们两个是很相似的(PS:线性回归是拟合一条直线,而逻辑回归是根据sigmoid将线性变成非线性,所以去掉sigmoid,他们是一样的)。
所以理论上线性回归也可以用于做分类预测,但是准确率很低,效果很差,所以我们引用sigmoid函数将线性变成非线性来解决该类问题。
2.逻辑回归原理
常用的非线性激活函数有sigmoid、tanh、relu等等,前两者sigmoid/tanh比较常见于全连接层,后者relu常见于卷积层。这里先简要介绍下最基础的sigmoid函数。
sigmoid函数公式如下:
其中z是一个线性组合。通过代入很大的正数或很小的负数到g(z)函数中可知,其结果趋近于0或1。因此,sigmoid函数g(z)的图形表示如下(横轴表示定义域z,纵轴表示值域g(z))。
压缩至0到1有何用处呢?用处是这样一来便可以把激活函数看作一种“分类的概率”,比如激活函数的输出为0.9的话便可以解释为90%的概率为正样本。
1.选定阈值
其实使用逻辑回归来预测分类问题,最后结果不是0或者是1,而是0-1之间的数,sigmoid默认情况是将0.5以上的都归为正例,0.5以下都归为负例,但是这样是不准确的。因为0.49它还是有49%的概率为正例的。就算我们认为0.1是负类,0.1还是有10% 的可能是正类,所以无论怎么选择,都有误差的,我们要做的就是尽量的减少误差。
因为我们不管使用什么方式 求解,最后的结果都是0-1之间数,而不是0或1。有的人认为只需要确定好阈值那么我们就能找到最优解,但是实际上不应该这么想,我们要做的在这个方程中
求出最优的W值,然后在根据最优W值的方法来确定确定阈值。
2.似然函数
最大似然估计就是通过已知结果去反推最大概率导致该结果的参数。极大似然估计是概率论在统计学中的应用,它提供了一种给定观察数据来评估模型参数的方法,即 “模型已定,参数未知”,通过若干次试验,观察其结果,利用实验结果得到某个参数值能够使样本出现的概率为最大,则称为极大似然估计。逻辑回归是一种监督式学习,是有训练标签的,就是有已知结果的,从这个已知结果入手,去推导能获得最大概率的结果参数,只要我们得出了这个参数,那我们的模型就自然可以很准确的预测未知的数据了。
3.梯度下降
当我们确定了目标之后,我们就需要一个算法来解决问题,现在最常用也比较好用的就是梯度下降。
梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
我们对一个多元函数求偏导,会得到多个偏导函数.这些导函数组成的向量,就是梯度.我们用梯度下降是用来求解一个损失函数的最小值,所谓下降实际上是这个损失函数的值在下降。
3.为什么逻辑回归用于分类问题
从原理的角度来看,逻辑回归被用于解决分类问题的主要原因是其模型假设和输出特性。
-
模型假设:逻辑回归假设因变量(输出)服从伯努利分布,即属于某一类别的概率服从一个逻辑函数(或称为 sigmoid 函数)。这种假设适用于分类问题,因为分类问题的输出通常是离散的类别,而逻辑回归能够将输入特征映射到离散的类别概率上。
-
输出特性:逻辑回归的输出是一个在0到1之间的概率值,表示样本属于某一类别的概率。通常情况下,将概率大于0.5的样本分类为正类别,概率小于等于0.5的样本分类为负类别。这种特性使得逻辑回归在解决二分类问题时非常方便。
4.代码实现
1.获取数据集
import numpy as np
import matplotlib.pyplot as plt
def get_dataset(mean, cov, N = 100, pi = 0.3, cov2 = None):
mean = np.array(mean, dtype = 'float')
cov = np.array(cov, dtype = 'float')
assert mean.shape[0] == 2 and mean.shape[1] == cov.shape[0] and cov.shape[0] == cov.shape[1], '参数不合法!'
positive = int(N *pi)
negative = N - positive
pdata = np.random.multivariate_normal(mean[0], cov, positive)
ndata = np.random.multivariate_normal(mean[1], cov if cov2 is None else cov2, negative)
return np.concatenate([pdata, ndata]), np.concatenate([np.ones(positive), np.zeros(negative)])
代码中np.concatenate
用于把正例和反例拼接起来。最后返回的数据应该类似于(data, target)
,其中data是一个n*n的矩阵
2.梯度下降法
def GD(data, target, max_iteration = 10000, lr = 0.05):
# 判断收敛的阈值
epsilon = 1e-8
# 意义如上文所述
N, d = data.shape
# 意义如上文所述,随机初始化后开始梯度下降
beta = np.random.randn(d + 1)
# 因为我们在前面加了一列1
X = np.concatenate([np.ones((N, 1)), data], axis = 1)
for i in range(max_iteration):
term1 = -np.sum(X * target.reshape(N, 1), axis = 0)
term2 = np.sum((1 / (np.e ** -np.dot(beta, X.T) + 1)).reshape(N, 1) * X, axis = 0)
grad = term1 + term2
if np.linalg.norm(grad) < epsilon:
break
beta -= lr * grad
return beta
数据集利用numpy的*运算将对应行相乘,但在这之前我们需要把Y YY变成一个二维数组(利用reshape(N,1)),最后需要对相乘结果按行相加,这在numpy中用np.sum(A, axis=0)表示,这就是代码中term1的来历。
3.主函数
if __name__ == '__main__':
mean = [[1, 1], [4, 4]]
cov = np.diag([1, 1])
data, target = get_dataset(mean, cov)
beta = GD(data, target, max_iteration = 100000)
for (x, y), label in zip(data, target):
plt.scatter(x, y, c = 'red' if label else 'black')
x = np.linspace(-1, 4)
y = (-beta[1] * x - beta[0]) / beta[2]
plt.plot(x, y)
plt.show()
5.运行结果分析·
运行结果如下
1. 数据集生成效果
生成的数据集与期望的一致,数据点的分布符合预期。正例和反例能够清晰地区分开来,呈现出明显的聚类特征。通过调整均值和协方差参数,成功生成了满足实验需求的数据集。
2. 模型参数收敛情况
通过梯度下降法求解逻辑回归模型的参数,在合理的迭代次数内成功收敛。模型参数在迭代过程中逐渐变化,最终收敛于稳定值。通过调整学习率等超参数,确保了梯度下降算法的有效性和收敛性。
3. 分类边界效果
绘制的分类边界能够很好地将正例和反例分开,符合实际情况。分类边界在数据集上的表现与预期相符,能够有效地区分两类数据点。通过逻辑回归模型得到的分类边界在数据集上展现了良好的分类性能。
6.总结
通过本次实验,成功实现了逻辑回归模型的训练和分类边界的绘制。生成的数据集符合预期,模型参数在合理的迭代次数内成功收敛,分类边界能够有效地将正例和反例分开。实验结果表明,所实现的逻辑回归模型在该数据集上表现良好,具有一定的分类能力。逻辑回归算法的原理和实现过程得到了验证,为进一步研究和应用机器学习算法提供了基础。
后续改进与展望
在实验过程中,可以进一步尝试不同的参数设置和算法优化方法,以提高模型的性能和收敛速度。此外,可以将逻辑回归算法应用于更复杂的数据集和问题中,探索其在实际应用中的效果和局限性。