逻辑回归:Logistic

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/keyue123/article/details/82965914

  逻辑回归简单说就是将数据拟合到一个 l o g i s t i c logistic 函数中,从而能够完成对事件发生的概率进行预测。虽然名字叫做回归,但是其实质上却是一个分类问题,主要适用于二分类。
  逻辑回归算法速度快,适合二分类问题,容易理解,可直接看到各特征的权重,而且很容易更新模型吸收新的数据,但是对数据和场景的适应能力有局限性,不如决策树算法适应性那么强。
  注:这里我就不讲逻辑回归具体的推导过程,感兴趣的可以自己下去查一下,可以参考逻辑回归算法原理

1. S i g m o i d Sigmoid 函数
  逻辑回归的核心思想是,如果线性回归的结果输出是一个连续值,而值的范围是无法限定的,那我们就想办法把这个结果值映射为可以帮助我们判断的结果。 s i g m o i d sigmoid 函数主要就时帮助我们实现这个功能,具体实现参考Sigmoid函数
g ( x ) = 1 1 + e x {g(x)} = \frac{1}{{1 + {e^{ - x}}}}   函数图像表示为:

在这里插入图片描述

  从函数图上可以看出,函数 y = g ( x ) y=g(x) x = 0 x=0 的时候取值为1/2,而随着 x x 逐渐变小,函数值趋于0, x x 逐渐变大的同时函数值逐渐趋于1,而这正是一个概率的范围。

2. 判定边界
  这里我们引入一个概念,叫做判定边界,可以理解为是用以对不同类别的数据分割的边界,边界的两旁应该是不同类别的数据。正是因为这个原因,逻辑回归才能解决分类的问题。如下图就是蓝色直线就是我们的判定边界:

<align=center>在这里插入图片描述

逻辑回归是如何根据样本点获得这些判定边界的,我们举个例子:对于 g ( θ T x ) 0.5 g({\theta ^T}x)≥0.5 ,则 θ T x 0 {\theta ^T}x≥0 ,此时的预估是 y = 1 y=1 ,反之,当预测 y = 0 y = 0 时, θ T x &lt; 0 {\theta ^T}x&lt;0
所以我们认为 θ T x = 0 {\theta ^T}x =0 是一个决策边界,当它大于0或小于0时,逻辑回归模型分别预测不同的分类结果。

3. 梯度上升
  要找到某函数的最大值,最好的方法是沿着该函数的梯度方向寻找。如果梯度记为 ,则函数 f ( x , y ) f(x, y) 的梯度由下式表示
在这里插入图片描述
  这个梯度意味着要沿 x x 的方向移动 x f ( x , y ) x \frac{{\partial xf(x,y)}}{{\partial x}} ,沿 y y 的方向移动 x f ( x , y ) y \frac{{\partial xf(x,y)}}{{\partial y}} 。其中,函数 f ( x , y ) f(x, y) 必须要在待计算的点上有定义并且可微(参考机器学习实战)。
在这里插入图片描述
  上图展示的,梯度上升算法到达每个点后都会重新估计移动的方向。从 P 0 P0 开始,计算完该点的梯度,函数就根据梯度移动到下一点 P 1 P1 。在 P 1 P1 点,梯度再次被重新计算,并沿着新的梯度方向移动到 P 2 P2 。如此循环迭代,直到满足停止条件。迭代过程中,梯度算子总是保证我们能选取到最佳的移动方向。

4. 实例分析
  了解了那么多理论,这里我们举个经典案例,从疝气病症预测病马的死亡率,数据如下:

2.000000	1.000000	38.500000	66.000000	28.000000	3.000000	3.000000	0.000000	2.000000	5.000000	4.000000	4.000000	0.000000	0.000000	0.000000	3.000000	5.000000	45.000000	8.400000	0.000000	0.000000	0.000000
1.000000	1.000000	39.200000	88.000000	20.000000	0.000000	0.000000	4.000000	1.000000	3.000000	4.000000	2.000000	0.000000	0.000000	0.000000	4.000000	2.000000	50.000000	85.000000	2.000000	2.000000	0.000000
2.000000	1.000000	38.300000	40.000000	24.000000	1.000000	1.000000	3.000000	1.000000	3.000000	3.000000	1.000000	0.000000	0.000000	0.000000	1.000000	1.000000	33.000000	6.700000	0.000000	0.000000	1.000000
1.000000	9.000000	39.100000	164.000000	84.000000	4.000000	1.000000	6.000000	2.000000	2.000000	4.000000	4.000000	1.000000	2.000000	5.000000	3.000000	0.000000	48.000000	7.200000	3.000000	5.300000	0.000000
2.000000	1.000000	37.300000	104.000000	35.000000	0.000000	0.000000	6.000000	2.000000	0.000000	0.000000	0.000000	0.000000	0.000000	0.000000	0.000000	0.000000	74.000000	7.400000	0.000000	0.000000	0.000000
2.000000	1.000000	0.000000	0.000000	0.000000	2.000000	1.000000	3.000000	1.000000	2.000000	3.000000	2.000000	2.000000	1.000000	0.000000	3.000000	3.000000	0.000000	0.000000	0.000000	0.000000	1.000000
1.000000	1.000000	37.900000	48.000000	16.000000	1.000000	1.000000	1.000000	1.000000	3.000000	3.000000	3.000000	1.000000	1.000000	0.000000	3.000000	5.000000	37.000000	7.000000	0.000000	0.000000	1.000000
1.000000	1.000000	0.000000	60.000000	0.000000	3.000000	0.000000	0.000000	1.000000	0.000000	4.000000	2.000000	2.000000	1.000000	0.000000	3.000000	4.000000	44.000000	8.300000	0.000000	0.000000	0.000000
2.000000	1.000000	0.000000	80.000000	36.000000	3.000000	4.000000	3.000000	1.000000	4.000000	4.000000	4.000000	2.000000	1.000000	0.000000	3.000000	5.000000	38.000000	6.200000	0.000000	0.000000	0.000000
2.000000	9.000000	38.300000	90.000000	0.000000	1.000000	0.000000	1.000000	1.000000	5.000000	3.000000	1.000000	2.000000	1.000000	0.000000	3.000000	0.000000	40.000000	6.200000	1.000000	2.200000	1.000000
  • 准备数据
      因为数据是传感器采集,所以可能会出现缺失或者不正确的情况,处理方法可以参考我之前的博客或者是机器学习实战(数据处理),在预处理需要做两件事: 所有的缺失值必须用一个实数值来替换,因为我们使用的NumPy数据类型不允许包含缺失值。我们这里选择实数 0 来替换所有缺失值,恰好能适用于 L o g i s t i c Logistic 回归。
      回归系数的更新公式如下:

       w e i g h t s = w e i g h t s + a l p h a e r r o r d a t a M a t r i x [ d a t a I n d e x [ r a n d I n d e x ] ] weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]

  如果 d a t a M a t r i x dataMatrix 的某个特征对应值为0,那么该特征的系数将不做更新,即:

       w e i g h t s = w e i g h t s weights = weights

   S i g m o i d ( 0 ) = 0.5 由于 Sigmoid(0) = 0.5 ,即它对结果的预测不具有任何倾向性,因此我们上述做法也不会对误差造成任何影响。基于上述原因,将缺失值用 0 代替既可以保留现有数据,也不需要对优化算法进行修改。

def loadDataSet(fileName):
    frTrain = open(fileName)	#打开数据集
    
    trainingSet = []		#数据集列表
    trainingLabels = []		#标签列表

    for line in frTrain.readlines():
        currLine = line.strip().split('\t')	#切割数据
        lineArr = []
        for i in range(21):			#20个特征
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[21]))
    
   return trainingSet, trainingLabels		#返回特征列表和标签列表
  • s i g m o i d sigmoid 函数
def sigmoid(inX):
    return 1.0/(1+exp(-inX))
  • 梯度上升算法
def gradAscent(dataMatIn, classLabels):		#梯度上升算法
    dataMatrix = mat(dataMatIn)             	#转换为矩阵
    labelMat = mat(classLabels).transpose() 	#将数组转换为矩阵,并将行向量转置为列向量

    m, n = shape(dataMatrix)    		# m数据量  n特征数
    alpha = 0.001   				#向目标移动的步长
    maxCycles = 500 				#迭代次数
    weights = ones((n, 1))  			#生成一个全为1的矩阵
    for k in range(maxCycles):
        h = sigmoid(dataMatrix*weights)     	#矩阵乘法
        error = (labelMat - h)              	#向量相减
        weights = weights + alpha * dataMatrix.transpose() * error #矩阵乘法,最后得到回归系数

    return array(weights)			#返回回归系数

  梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理小的数据集时尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。这是就有人想出一种改进方法,一次仅用一个样本点(样本点每次随机挑选一批数据集)来更新回归系数,该方法称为随机梯度上升算法。

def stocGradAscent(dataMatrix, classLabels, numIter=150):	#随机梯度上升算法
    m, n = shape(dataMatrix)	#获取维度
    weights = ones(n)		#生成一个全为1的矩阵

    for j in range(numIter):
        dataIndex = range(m)
        for i in range(m):
            alpha = 4/(1.0+j+i) + 0.0001    #alpha会随着迭代不断减小,但永远不会减小到0,因为后边还有一个常数项0.0001
            randIndex = int(random.uniform(0, len(dataIndex)))	#获取随机数据索引

            h = sigmoid(sum(dataMatrix[dataIndex[randIndex]]*weights))
            error = classLabels[dataIndex[randIndex]] - h
            weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]
            #del(dataIndex[randIndex])

    return weights

  随机梯度上升算法与梯度上升算法在代码上很相似,但也有一些区别: 第一,后者的变量 h 和误差 error 都是向量,而前者则全是数值;第二,前者没有矩阵的转换过程,所有变量的数据类型都是 NumPy 数组。

  • 分类
      得到回归系数之后,我们可以将带入一组数据,根据回归系数,算出标签值。
def classifyVector(inX, weights):
    prob = sigmoid(sum(inX*weights))
    if prob > 0.5:
        return 1.0
    else:
        return 0.0
  • 测试
      根据上面的各函数功能,我们可以带入一组数据集来测试效果,下面是我的测试函数:
def testResult(fileName):
    errorCount = 0
    numTestVec = 0.0

    frTest = open(fileName)			#打开测试数据集
    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
  • 结果
if __name__ == '__main__':
    trainFilePath = 'C:\\Users\\John\\Desktop\\DataBooks\\MachineLearning\\Ch05\\horseColicTraining.txt'	#训练集
    testFilePath = 'C:\\Users\\John\\Desktop\\DataBooks\\MachineLearning\\Ch05\\horseColicTest.txt'	#测试集
    numTests = 10
    errorSum = 0.0
    for k in range(numTests):	#每组数据计算十次
        trainingSet, trainingLabels = loadDataSet(trainFilePath)		#获取测试集
        trainWeights = stocGradAscent(array(trainingSet), trainingLabels, 500)	#逻辑回归训练
        errorSum += testResult(testFilePath)		

    print ("after %d iterations the average error rate is: %f" % (numTests, errorSum / float(numTests)))		#计算错误率

在这里插入图片描述
4. 实例分析
  逻辑回归

  
  
注:这里引用了一些博主的博客,如果博主反对。可联系本人删除,谢谢。

猜你喜欢

转载自blog.csdn.net/keyue123/article/details/82965914