https://blog.csdn.net/feilong_csdn/article/details/64128443
logistic回归,又叫对数几率回归(从后文中便可此名由来)。首先给大家强调一点,这是一个分类模型而不是一个回归模型!下文开始将从不同方面讲解logistic回归的原理,随后分别使用梯度上升算法和随机梯度上升算法将logistic回归算法应用到实例中。
一、logistic回归和线性回归的关系
想必大家也早有疑惑,既然logistic回归名字中都带有“回归”二者,难道和回归模型一点关系都没有!没错,二者是有联系的,下面我们便来谈一谈!
首先给出线性回归模型:
写成向量形式为:
同时“广义线性回归”模型为:
注意,其中g(~)是单调可微函数。
下面我们便从线性回归的回归模型引出logistic回归的分类模型!!!
我们知道上诉线性回归模型只能够进行回归学习,但是若要是做分类任务如何做!答案便是在“广义线性回归”模型中:只需找一个单调可微函数将分类任务的真实标记y与线性回归模型的预测值联系起来便可以了!
logistic回归是处理二分类问题的,所以输出的标记y={0,1},并且线性回归模型产生的预测值z=wx+b是一个实值,所以我们将实值z转化成0/1值便可,这样有一个可选函数便是“单位阶跃函数”:
这种如果预测值大于0便判断为正例,小于0则判断为反例,等于0则可任意判断!
但是单位阶跃函数是非连续的函数,我们需要一个连续的函数,“Sigmoid函数”便可以很好的取代单位阶跃函数:
sigmoid函数在一定程度上近似单位阶跃函数,同时单调可微,图像如下所示:
这样我们在原来的线性回归模型外套上sigmoid函数便形成了logistic回归模型的预测函数,可以用于二分类问题:
对上式的预测函数做一个变换为:
观察上式可得: 若将y视为样本x作为正例的可能性,则1-y便是其反例的可能性。二者的比值便被称为“几率”,反映了x作为正例的相对可能性,这也是logistic回归又被称为对数几率回归的原因!
这里我们也便可以总结一下线性回归模型和logistic回归的关系:
logistic回归分类模型的预测函数是在用线性回归模型的预测值的结果去逼近真实标记的对数几率!这样也便实现了上面说的将线性回归的预测值和分类任务的真实标记联系在了一起!
二、梯度上升算法求解logistic回归模型参数w
在上一个话题中我们已经得到了logistic回归的预测函数:
这里我们将式子中的y视为类后验概率估计p(y=1|x),则上式可以重写为:
求解上式有:
先给出求解参数w的思路,下面我们便按照这个思路进行求解参数w:
1、为求解参数w,我们需要定义一个准则函数 J(w),利用准则函数求解参数w
2、我们通过最大似然估计法定义准则函数J(w)
3、接下来通过最大化准则函数J(w)便可求出参数w的迭代表达式
4、为了更好地使用数据求出参数w,我们将第三步得到的w的迭代时向量化。自此便完成了对于参数w的推导过程,接下来便可以进行实例应用了!
步骤一、求解准则函数J(w)
合并(3)(4)两个式子可得:
在(5)式中y={0,1},是个二分类问题,所以y只是取两个值0或是1。
根据(5)式可得似然函数为:
对(6)式取对数有:
因此定义准则函数为:
最终我们的目标便是最大化似然函数,也就是最大化准则函数:
步骤二、梯度上升算法求解参数w
这里我们使用梯度上升算法求解参数w,因此参数w的迭代式为:
其中α是正的比例因子,用于设定步长的“学习率”
其中对准则函数J(w)进行微分可得:
所以得到最终参数w的迭代式为:
上式将(1/m)去掉不影响结果,等价于下式:
至此我们已经得出了w的迭代公式,按说是可以在引入数据的情况下进行w的计算,进而进行分类!但是数据基本都是以矩阵和向量的形式引入的,所以我们需要对上面w的迭代时进行向量化,以方便实例应用中的使用。
步骤三、w迭代公式向量化
首先对于引入的数据集x来说,均是以矩阵的形式引入的,如下:
其中m数据的个数,n是数据的维度,也就是数据特征的数量!
再者便是标签y也是以向量的形式引入的:
参数w向量化为:
在这里定义M=x*w,所以:
定义上面说的sigmoid函数为:
所以定义估计的误差损失为:
在此基础上,可以得到步骤二中得到的参数迭代时向量化的式子为:
至此我们便完成了参数w迭代公式的推导,下面便可以在实例中应用此迭代公式进行实际的分析了!下面我们便以简单的二分类问题为例来分析logistic回归算法的使用!!
三、logistic回归算法的实例应用(pyhton)
此实例便是在二维空间中给出了两类数据点,现在需要找出两类数据的分类函数,并且对于训练出的新的模型,如果输入新的数据可以判断出该数据属于二维空间中两类数据中的哪一类!
在给出Python实现的示例代码展示之前,先介绍一下两种优化准则函数的方法:
1、梯度上升算法
2、随机梯度上升算法
梯度上升算法:
梯度上升算法和我们平时用的梯度下降算法思想类似,梯度上升算法基于的思想是:要找到某个函数的最大值,最好的方法是沿着这个函数的梯度方向探寻!直到达到停止条件为止!
梯度上升算法的伪代码:
随机梯度上升算法:
梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理小数据时还尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度太高了,改进方法便是一次仅用一个数据点来更新回归系数,此方法便称为随机梯度上升算法!由于可以在更新样本到来时对分类器进行增量式更新,因而随机梯度上升算法是一个“在线学习算法”。而梯度上升算法便是“批处理算法”!
改进的随机梯度上升算法:
随机梯度上升算法虽然大大减少了计算复杂度,但是同时正确率也下降了!所以可以对随机梯度上升算法进行改进!改进分为两个方面:
改进一、对于学习率alpha采用非线性下降的方式使得每次都不一样
改进二:每次使用一个数据,但是每次随机的选取数据,选过的不在进行选择
在知道这些信息之后下面给出示例代码,其中有详细的注释:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from numpy import *
import matplotlib.pyplot as plt
#从文件中加载数据:特征X,标签label
def loadDataSet():
dataMatrix=[]
dataLabel=[]
#这里给出了python 中读取文件的简便方式
f=open('testSet.txt')
for line in f.readlines():
#print(line)
lineList=line.strip().split()
dataMatrix.append([1,float(lineList[0]),float(lineList[1])])
dataLabel.append(int(lineList[2]))
#for i in range(len(dataMatrix)):
# print(dataMatrix[i])
#print(dataLabel)
#print(mat(dataLabel).transpose())
matLabel=mat(dataLabel).transpose()
return dataMatrix,matLabel
#logistic回归使用了sigmoid函数
def sigmoid(inX):
return 1/(1+exp(-inX))
#函数中涉及如何将list转化成矩阵的操作:mat()
#同时还含有矩阵的转置操作:transpose()
#还有list和array的shape函数
#在处理矩阵乘法时,要注意的便是维数是否对应
#graAscent函数实现了梯度上升法,隐含了复杂的数学推理
#梯度上升算法,每次参数迭代时都需要遍历整个数据集
def graAscent(dataMatrix,matLabel):
m,n=shape(dataMatrix)
matMatrix=mat(dataMatrix)
w=ones((n,1))
alpha=0.001
num=500
for i in range(num):
error=sigmoid(matMatrix*w)-matLabel
w=w-alpha*matMatrix.transpose()*error
return w
#随机梯度上升算法的实现,对于数据量较多的情况下计算量小,但分类效果差
#每次参数迭代时通过一个数据进行运算
def stocGraAscent(dataMatrix,matLabel):
m,n=shape(dataMatrix)
matMatrix=mat(dataMatrix)
w=ones((n,1))
alpha=0.001
num=20 #这里的这个迭代次数对于分类效果影响很大,很小时分类效果很差
for i in range(num):
for j in range(m):
error=sigmoid(matMatrix[j]*w)-matLabel[j]
w=w-alpha*matMatrix[j].transpose()*error
return w
#改进后的随机梯度上升算法
#从两个方面对随机梯度上升算法进行了改进,正确率确实提高了很多
#改进一:对于学习率alpha采用非线性下降的方式使得每次都不一样
#改进二:每次使用一个数据,但是每次随机的选取数据,选过的不在进行选择
def stocGraAscent1(dataMatrix,matLabel):
m,n=shape(dataMatrix)
matMatrix=mat(dataMatrix)
w=ones((n,1))
num=200 #这里的这个迭代次数对于分类效果影响很大,很小时分类效果很差
setIndex=set([])
for i in range(num):
for j in range(m):
alpha=4/(1+i+j)+0.01
dataIndex=random.randint(0,100)
while dataIndex in setIndex:
setIndex.add(dataIndex)
dataIndex=random.randint(0,100)
error=sigmoid(matMatrix[dataIndex]*w)-matLabel[dataIndex]
w=w-alpha*matMatrix[dataIndex].transpose()*error
return w
#绘制图像
def draw(weight):
x0List=[];y0List=[];
x1List=[];y1List=[];
f=open('testSet.txt','r')
for line in f.readlines():
lineList=line.strip().split()
if lineList[2]=='0':
x0List.append(float(lineList[0]))
y0List.append(float(lineList[1]))
else:
x1List.append(float(lineList[0]))
y1List.append(float(lineList[1]))
fig=plt.figure()
ax=fig.add_subplot(111)
ax.scatter(x0List,y0List,s=10,c='red')
ax.scatter(x1List,y1List,s=10,c='green')
xList=[];yList=[]
x=arange(-3,3,0.1)
for i in arange(len(x)):
xList.append(x[i])
y=(-weight[0]-weight[1]*x)/weight[2]
for j in arange(y.shape[1]):
yList.append(y[0,j])
ax.plot(xList,yList)
plt.xlabel('x1');plt.ylabel('x2')
plt.show()
if __name__ == '__main__':
dataMatrix,matLabel=loadDataSet()
#weight=graAscent(dataMatrix,matLabel)
weight=stocGraAscent1(dataMatrix,matLabel)
print(weight)
draw(weight)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
上面程序运行结果为:
其中:
图(1)是使用梯度上升算法的结果,运算复杂度高;
图(2)是使用随机梯度上升算法,分类效果略差吗,运算复杂度低;
图(3)是使用改进后的随机梯度上升算法,分类效果好,运算复杂度低。
文中如有何问题,欢迎指正,谢谢!!!