机器学习:逻辑斯特回归
前面的分类都是以标签为离散值为前提进行的,在这种情况下我们引入了回归分析来处理标签为连续值的情况。
假设我们有一系列数据点希望用一条直线对其进行建模那么这个建模过程即为回归分析而这条直线则代表了最佳拟合。
其核心思想在于通过现有数据建立分类边界线对应的回归方程从而实现分类任务。
而回归分析的本质即寻求最佳匹配参数集这一步骤通常会采用最优化算法来进行。
一旦确定了这些参数就可以直接完成分类任务。
1基于logistic回归与sigmoid函数的应用
在logistic回归模型中我们需要设计一个函数既能接受所有输入又能输出相应的类别预测结果而sigmoid函数因其S形曲线的特点非常适合用于这一类问题它类似于单位阶跃函数的形式。

当x等于零时,在该点处的函数输出是零点五;随着x的增大趋近于一;而随着x的减小则趋近于零。
为了构建sigmoid回归分类器,在每个特征维度上赋予其相应的权重系数后进行求和计算;接着将该总和输入至sigmoid函数中进行映射处理;最终得到的结果是一个介于零到一之间的数值。
如果计算出的结果超过零点五,则判定该样本属于类别一;反之,则判定为类别零。
Logistic回归模型本质上是一种基于概率的二元分类方法。

g(z)的曲线是

此时就可以对标签y进行分类了,

其中θTx=0 即θ0+θ1x1+θ2x2=0 称为决策边界即boundarydecision。
2:线性回归模型的成本函数基于最小二乘法原理旨在减少到最低水平以减少预测值与实际观测值之间的误差平方和。

然而,在Logistic回归中,并非可以简单地最小化观察值与估计值之间的差平方和。其损失函数J(θ)被明确定义为一种非凸函数的形式。这样一来,则会导致存在多个局部极小值点的存在性问题。由于这种特性,在应用梯度下降法时难以收敛至全局最优参数的位置。因此,在此我们重新定义了一种新的损失函数:

这个函数的作用是,在y等于1的情况下输出值设为1;在y等于0的情况下输出值设为0;从而实现了精准预测目标。然而,在预测结果与实际不符的情况下(即出现错误),输出值将趋于无穷大。

这样总体损失函数就是:

3:sigmoid函数的作用域定义为实数域R,则其输入记作z=σ(w·x),其中σ代表sigmoid激活函数;根据定义可得:
z = w_0 x_0 + w_1 x_1 + w_2 x_2 + \dots + w_n x_n
将其表示为向量形式即:
z = \mathbf{w}^\top \mathbf{x}
其中\mathbf{x}是由多个样本实例构成的数据矩阵;\mathbf{w}则代表最佳参数权重向量。
对于寻找目标函数极值的问题而言,则可以通过沿负梯度方向逐步逼近全局最小值来实现最小化损失的目标。
其梯度可通过以下数学表达式计算出:
\nabla f(\mathbf{w}) = \frac{2}{m} \sum_{i=1}^m (\hat{y}_i - y_i) \cdot \mathbf{x}_i

求和后得到:

对于随机化的梯度迭代每次只使用一个样本进行参数更新,就为:

这段代码直接采用了该公式的应用。已知当hθ(x)≥0.5时,在这种情况下对应地,在这种情况下y=1;而当hθ(x)<0.5时,则对应地,在这种情况下y=0。因此我们选择将hθ(x)作为y=1发生时的概率估计值;然而当y=0时(即hθ(x)<0.5),我们不能直接使用hθ(x)作为y=0发生的概率值(因为最大似然估计的思想要求我们使已有数据发生的概率最大化;如果使用过小的值来表示这种情况下的发生概率,则会违背这一原则)。因此我们可以采用1-hθ(x)作为y=0发生的概率估计值;这样一来我们就建立了两个相对应的概率估计模型:P(y=1|x)=hθ(x),P(y=0|x)=1−hθ(x);然后只需要最大化这两个条件下的联合概率密度函数就可以了。

再转换成对数似然函数,就和上面给出的似然函数一致了。

4:梯度上升算法沿着其计算出的梯度方向持续变化;梯度算子主要指向目标函数值增长最快的方向。该优化算法的具体实现:
#-*- coding:utf-8 -*-
from numpy import *
#计算代价不高,易于理解和实现,容易欠拟合,分类精度不高,数据类型为数值型和标称型数据
def loadDataSet():
dataMat = []; labelMat = []
fr = open('testSet.txt')#打开文本
for line in fr.readlines():#逐行读取,并保存成矩阵,x0为1
lineArr = line.strip().split()
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
labelMat.append(int(lineArr[2]))#读取标签
return dataMat,labelMat#返回数据矩阵和标签
def sigmoid(inX):#函数实现,核心函数
return 1.0/(1+exp(-inX))
def gradAscent(dataMatIn,classLabels):#
dataMatrix = mat(dataMatIn)#转换成numpy矩阵数据类型
labelMat = mat(classLabels).transpose()#把标签转换成行向量
m,n = shape(dataMatrix)#m是行,n是列
alpha = 0.001#向目标移动的步长
maxCycles = 500#迭代次数
weights = ones((n,1))#初始化矩阵向量,n行一列的单位矩阵
for k in range(maxCycles):#每一次迭代
h = sigmoid(dataMatrix*weights)#把z=wx带入函数,求出一个列向量,代表计算出的标签
error = (labelMat - h)#与实际的标签进行比较,得误差
weights = weights + alpha * dataMatrix.transpose() * error#回归系数为原来的回归系数加上步长乘以数据集行列变换后乘以误差列向量,得w1,w2,w3三个回归系数
return weights

通过一组数据的提供, 可以从而得到回归系数. 进行Logistic回归分析. 第5步: 在确定了回归系数后, 获得不同类别数据之间的分割线, 并绘制该分割线.
def plotBestFit(weights):#画出回归线
import matplotlib.pyplot as plt#
dataMat,labelMat = loadDataSet()#导入数据集
dataArr = array(dataMat)#转换成array阵列
n = shape(dataArr)[0]#得到行数
xcord1 = []; ycord1 = []#设置数据点的x,y坐标
xcord2 = []; ycord2 = []
for i in range(n):#按标签类别进行分颜色显示
if int(labelMat[i]) == 1:
xcord1.append(dataArr[i,1]);ycord1.append(dataArr[i,2])
else:
xcord2.append(dataArr[i,1]);ycord2.append(dataArr[i,2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1,ycord1,s=30,c='red',marker='s')#红色显示
ax.scatter(xcord2,ycord2,s=30,c='green')#绿色显示
x = arange(-3.0,3.0,0.1)#x的取值范围和步长间隔
y = (-weights[0]-weights[1]*x)/weights[2]#针对x和回归系数的y的取值
ax.plot(x,y)
plt.xlabel('X1');plt.ylabel('X2');
plt.show()


该方法的误差率较低但计算规模却较大需要执行300次乘法运算。
6: 随机梯度上升: 梯度上升算法在每次迭代回归系数参数时都需要遍历全部数据集 该优化方案由于计算规模过大而效率不高 改进为随机梯度上升法: 它每次仅采用单个样本点来更新回归系数参数 在新样本输入时通过增量式更新的方式对分类器进行优化 从而实现了在线学习算法 这种方法与一次性处理所有数据的批处理优化方式存在本质区别。
#随机梯度上升算法,区别是这个的h和error是数值,而之前的是向量,这个的数据类型是numpy数组,没有矩阵的转换过程,可以在新数据到来时就完成参数更新,不需要读取整个数据集进行批处理,占用更少的资源,是在线算法
def stocGradAscent0(dataMatrix,classLabels):
m,n = shape(dataMatrix)#得到数据集的行列数量
alpha = 0.01#步长
weights = ones(n)#单位行向量,行数=数据集列数
for j in range(200):#200次迭代
for i in range(m):#对每一行
h = sigmoid(sum(dataMatrix[i]*weights))#求出这一行向量乘以回归系数,得到一个数值,带入函数中
error = classLabels[i] - h#分别与每行的标签相比较,求误差
weights = weights + alpha*error*dataMatrix[i]#回归系数等于原来的加上步长乘以误差乘以一行向量。
return weights


一种可靠的评估优化算法性能的方法是观察其是否收敛于稳定状态。具体来说,在这种情况下参数应当已经达到了一个固定值,并且不会持续变动。
7:随机上升算法在参数规模较大的波动停止后,则会伴随有较小幅度的周期性振荡。由于数据集中存在无法正确分类的一些样本点(数据集并非线性可分),因此每次迭代都会导致系数发生剧烈变动。我们希望该算法能够避免振荡现象的发生,并使收敛过程更加迅速地稳定趋近于某个特定数值。
def stocGradAscent1(dataMatrix,classLabels,numIter=150):#第三个参数是迭代次数,默认是150次,用了两种机制,步长动态减少和样本随机选择,将会收敛很快,没有高频波动。
m,n = shape(dataMatrix)
weights = ones(n)
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 4/(1.0+j+i)+0.01#这个步长在每次迭代的时候都会调整减小,但因为有常数项,则不会减小到0,保证在多次迭代后新数据仍有影响,学习下模拟退火算法
randIndex = int(random.uniform(0,len(dataIndex)))#从行数数字中随机算出一行进行计算,随机选取样本来更新回归系数,减少周期波动。
h = sigmoid(sum(dataMatrix[randIndex]*weights))
error = classLabels[randIndex] - h
weights = weights + alpha*error*dataMatrix[randIndex]
del(dataIndex[randIndex])#删除该行
return weights
模拟退火算法防止参数急剧减少。


具体来说,在疝气病症的数据集中
def classifyVector(inX,weights):#分类函数,给出20个特征和一个标签,包括回归系数,将会得到分类值
prob = sigmoid(sum(inX*weights))
if prob > 0.5:return 1.0
else:return 0.0
def colicTest():#如何处理缺失数据是个难题
frTrain = open('horseColicTraining.txt')#打开这个文本
frTest = open('horseColicTest.txt')
trainingSet = [];trainingLabels = []
for line in frTrain.readlines():#逐行读取并把每个特征值列表化
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
trainingLabels.append(float(currLine[21]))
trainWeights = stocGradAscent1(array(trainingSet),trainingLabels,500)#进行训练
errorCount = 0;numTestVec = 0.0
for line in frTest.readlines():#进行测试并计算错误率
numTestVec += 1.0
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
if int(classifyVector(array(lineArr),trainWeights)) != int(currLine[21]):
errorCount += 1
errorRate = (float(errorCount)/numTestVec)
print "the error rate of this test is: %f" %errorRate
return errorRate
#分类时不需做太多工作,把测试集的特征向量乘以最优化方法得来的回归系数,求和,输入到函数中大于0.5就把标签设为1
def multiTest():#调用10次colicTest并求结果的平均值
numTests = 10;errorSum = 0.0
for k in range(numTests):
errorSum += colicTest()
print "after %d iterrations the average error rate is:%f" %(numTests,errorSum/float(numTests))

总结:Logistic回归的主要目标是确定非线性映射Sigmoid函数模型的最佳参数配置以实现通过最优化算法求解问题的过程。在最优化算法中梯度上升算法是其中最为常用的并且可以通过简化的方式得到随机梯度上升算子其资源消耗较低的特点在于它是一种在线学习方法可以在新样本到达时立即更新模型参数而无需一次性处理全部样本进行批处理运算。
