逻辑回归
一.逻辑回归概述
逻辑回归是一种用于解决二分类问题的统计学习方法。它的基本思想是通过对数据进行逻辑函数(也称为Sigmoid函数)的拟合来进行分类预测。
二.基本原理
2.1 Sigmoid函数
Sigmoid函数(逻辑函数):逻辑回归使用Sigmoid函数(也称为逻辑函数)将线性函数的输出映射到0到1之间,用于表示某个样本属于某一类的概率。Sigmoid函数的数学形式如下:
g
(
z
)
=
1
1
+
e
−
z
g(z) = \frac{1}{1 + e^{-z}}
g(z)=1+e−z1
其中,
z
=
w
T
x
+
b
z = w^T x + b
z=wTx+b
w是特征权重向量,x是特征向量,b是偏置项。
sigmoid函数图像:
2.2 模型假设
模型假设:逻辑回归假设样本的输出服从伯努利分布,即对于给定的特征向量 x,样本属于某一类的概率 𝑃(𝑦=1∣𝑥)由逻辑函数 𝑔(𝑧) 给出,进而得到对应类别的概率分布。
2.3 损失函数
损失函数:逻辑回归通常使用对数似然损失函数(Log Loss)来衡量模型预测值与真实标签之间的差距。对于二分类问题,损失函数可以定义为:
J
(
w
)
=
−
1
m
∑
i
=
1
m
[
y
(
i
)
log
(
g
(
z
(
i
)
)
)
+
(
1
−
y
(
i
)
)
log
(
1
−
g
(
z
(
i
)
)
)
]
J(w) = -\frac{1}{m} \sum_{i=1}^{m} [y^{(i)} \log(g(z^{(i)})) + (1 - y^{(i)}) \log(1 - g(z^{(i)}))]
J(w)=−m1i=1∑m[y(i)log(g(z(i)))+(1−y(i))log(1−g(z(i)))]
其中,m 是样本数量,y (i) 是第 𝑖个样本的真实标签,𝑧(𝑖) 是第 𝑖个样本的预测值,𝑔(𝑧(𝑖))是逻辑函数的输出。
2.4 参数优化
参数优化:通过最小化损失函数来优化模型参数 𝑤 和 𝑏常用的方法包括梯度下降、牛顿法等。通常使用梯度下降(Gradient Descent)或其变体(如随机梯度下降Stochastic Gradient Descent, SGD)来最小化代价函数,从而找到最优的权重 w 和偏置 b。
2.5模型预测
模型预测:训练完成后,利用学习到的参数对新样本进行预测。通常将逻辑函数的输出值大于0.5的样本预测为正类,小于0.5的样本预测为负类。
三.逻辑回归的优缺点
优点:
简单而有效:逻辑回归是一种简单的线性分类算法,易于实现和理解。
计算效率高:训练速度相对较快,适用于处理大型数据集。
结果具有可解释性:逻辑回归模型输出的概率可以直观地解释为各个特征对分类结果的影响程度。
可以处理多类别分类问题:通过一对多(OvR)或一对一(OvO)等方法,逻辑回归可以处理多类别分类任务。
缺点:
线性假设限制:逻辑回归假设特征之间的关系是线性的,当数据集中存在非线性关系时,表现可能不佳。
容易受到特征相关性的影响:如果特征之间存在高度相关性,逻辑回归模型可能会出现过拟合,导致性能下降。
不适用于复杂数据集:当数据集的分类边界非常复杂时,逻辑回归可能无法很好地拟合数据。
对异常值敏感:逻辑回归模型对异常值敏感,异常值的存在可能会对模型产生较大影响。
四.损失函数
逻辑回归的损失,称为对数似然损失:
J(θ) = -1/m * Σ [y * log(hθ(x)) + (1 - y) * log(1 - hθ(x))]
在这个公式中:J(θ) 是损失函数,用于衡量模型预测值和真实值之间的差距。m 是样本数量。θ 是模型参数,hθ(x) 是模型预测的输出值(也称为假设函数)。y 是真实的标签值,取值为0或1。
将公式矢量化:θ=θ+αXT( y−g(Xθ))
五.优化之梯度上升
梯度上升是一种优化算法,用于最大化一个函数。它是梯度下降算法的反向操作。梯度下降是通过沿着函数梯度的反方向逐步更新参数来最小化一个损失函数,而梯度上升则是通过沿着函数梯度的方向逐步更新参数来最大化一个目标函数。
基本原理如下:
确定目标函数: 首先确定一个需要最大化的目标函数。这个函数通常表示为关于参数的某个表达式,例如在监督学习中可能是似然函数或者是某种评估指标。
初始化参数: 初始化参数的值,通常可以随机选择一个初始值。
计算梯度: 对目标函数关于参数的梯度进行计算。梯度表示了函数在当前参数值处的变化率和方向。
更新参数: 根据梯度的方向,逐步更新参数。参数的更新方向是为了使目标函数值增加,因此在梯度上升中,参数会朝着梯度的方向进行更新。
重复迭代: 重复执行步骤3和步骤4,直到满足停止条件。停止条件可以是达到一定的迭代次数,或者目标函数值的变化不再显著。
六.算法实现
6.1逻辑回归基本实现
(1)收集数据
(2)准备数据:需要进行距离计算,因此要求数据类型为数值型。结构化数据格式最佳
(3)分析数据
(4)训练算法:训练的目的是为了找到最佳的分类回归系数
(5)测试算法:一旦训练步骤完成,分类将会很快
(6)使用算法:通过训练好的回归系数进行简单的回归计算
(2)直接第二步——准备数据:
数据展示:
数据太多,截屏并不完整,后面数据省略。
(3)分析数据:
通过准备的数据,分解为自己想要的数据,代码:
from numpy import *
import numpy as np
import matplotlib.pyplot as plt
def loadDataSet():
dataMat=[]; labelMat=[]
fr=open("data.txt")
for line in fr.readlines():#逐行读取
lineArr=line.strip().split()
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])#默认X0=1.0,文件中每行的前两个值X1和X2
labelMat.append(int(lineArr[2])) #每行的第三个值(数据的类别标签)
return dataMat,labelMat
为何要在特征集第一列都加入一个1.0,后面会说,对于求后面的偏置b有极大的作用。
运行结果
第一行为将准备的数据拆分为特征集和标签集的特征集,分别是准备的数据的前两列数据,第二行则是标签集,只包含了0,1两个标签。
(4)训练算法:
本实验采用梯度上升算法实现对参数的最优解求解,梯度上升法求解参数步骤:
梯度上升算法伪代码:
每个回归系数初始化为1
重复需要迭代的次数:
计算整个数据集的梯度
使用alpha*gradient更新回归系数的向量
返回回归系数,即为每个特征的权重值
代码:
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
def gradAscent(dataMatIn, classLabels):
dataMatrix = np.mat(dataMatIn)
##print(dataMatrix)
labelMat = np.mat(classLabels).transpose()
m, n = np.shape(dataMatrix) ## m为行数,n为列数
##print('输出值:',m,n)
alpha = 0.001 ##学习步长
maxCycles = 500 ##迭代次数
weights = np.ones((n, 1))
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
error = (labelMat - h)
weights = weights + alpha * dataMatrix.transpose() * error
return weights
运行结果:由于dataMatrix是一个3100的矩阵,labelMat被转换成了一组1100的矩阵,weights是对应特征集的权重,即为一组回归系数对应着特征集的x0,x1,x2的权重。前面已经说过,在两个特征的前面加了一个1.0的列,就是为了计算偏置b的值,若引入新的测试,此时新数据的w0 * x0就可作为偏置b来作为自变量带入到sigmoid函数当中。
(5)样本测试
准备测试集:
testset = [1, -0.165494, 0.456127]
将得到的权重集转为列表,方便提取:
##w0*1即作为偏置b带入到sigmoid函数
def result(weights , testset):
list = weights.tolist() #矩阵转列表
w0 = list[0]; w1 = list[1]; w2 = list[2] #获取列表中的每一个元素
z = w0[0]*1 + w1[0]*testset[0] + w2[0]*testset[1]
final = sigmoid(z)
print('计算的sigmoid函数值:',final)
return final
算出待测数据的sigmoid函数值,进行预测,如果大于0.5,则属于1类,反之属于0类。
sort = result(weights, testset)
if sort > 0.5:
print("属于第1类")
else:
print("属于第0类")
(6)绘制决策边界
def plotDataSet():
dataMat, labelMat ,testset= loaddataset() # 加载数据集
dataArr = np.array(dataMat) # 转换成numpy的array数组
n = np.shape(dataMat)[0] # 数据个数
xcord1 = []
ycord1 = [] # 正样本
xcord2 = []
ycord2 = [] # 负样本
for i in range(n): # 根据数据集标签进行分类
if int(labelMat[i]) == 1:
xcord1.append(dataArr[i, 1])
ycord1.append(dataArr[i, 2]) # 1为正样本
else:
xcord2.append(dataArr[i, 1])
ycord2.append(dataArr[i, 2]) # 0为负样本
x_values = np.array([dataArr[:, 1].min() - 1, dataArr[:, 1].max() + 1])
y_values = 1.109 * x_values + 6.543 # 根据斜率和偏移量计算y值
fig = plt.figure()
ax = fig.add_subplot(111) # 添加subplot
ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5) # 绘制正样本
ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5) # 绘制负样本
ax.plot(x_values, y_values, 'k-')
plt.title('DataSet') # 绘制title
plt.xlabel('x')
plt.ylabel('y') # 绘制label
plt.show()
plotBestFit()函数通过使用Matplotlib画出数据集和Loistic回归最佳拟合直线的函数,这里设置了Sigmoid函数为0(0是两个分类<类别1和类别0>的分界处)。
结果:
实验小结
数据预处理的重要性:在进行逻辑回归实验之前,对数据进行适当的预处理是至关重要的。包括处理缺失值、异常值和特征缩放等步骤,可以提高模型的性能和稳定性。
特征选择的影响:选择合适的特征对模型的性能有着重要的影响。通过特征选择技术,可以排除对模型预测没有帮助的特征,提高模型的泛化能力。
参数调优的挑战:逻辑回归模型通常有一些超参数需要调优,例如正则化参数。通过交叉验证等技术,可以找到最优的参数组合,但这通常需要花费一定的时间和计算资源。
类别不平衡问题:在实际的分类问题中,类别不平衡是一个常见的挑战。逻辑回归模型可能会倾向于预测数量较多的类别,因此需要采取一些方法来处理类别不平衡,如过采样、欠采样或使用带权重的损失函数等。
模型评估和解释:在实验结束后,评估模型的性能是必不可少的。除了常见的指标如准确率、精确率和召回率外,还可以使用ROC曲线和AUC等指标来评估模型的性能。此外,了解模型的预测结果如何解释也是很重要的,可以帮助理解模型的决策过程和改进模型的可解释性。