前言


logistic回归的主要思想:根据现有数据对分类边界建立回归公式,以此进行分类
所谓logistic,无非就是True or False两种判断,表明了这其实是一个二分类问题
我们又知道回归就是对一些数据点拟合成线性函数,但是线性函数的值域是无穷的
所以logistic和回归加在一起,就是要把取值范围从无穷映射到(0,1)上,使之成为一个二分类器
所以本文会介绍怎么拟合一个回归函数,然后再把它作为自变量输入丢进一个阶跃函数,然后输出一个(0,1)的二值结果
这就是所谓的logistic回归

本文的参考书是《机器学习实战》

sigmoid函数


由前言中知道,我们需要一个阶跃函数,不管接受什么输入,输出的都是0或1
sigmoid函数刚好满足这样的特性:

它的图形如下:

当z=0时,函数值为0.5。
把自变量带入函数会得到一个0~1之间的数值,这时就可以把大于0.5的数据分为1类,小于0.5的归为0类。
所以logistic回归可以被看成是一种概率估计。
这样就完成了把无穷取值范围映射到0和1的使命。
接下来需要做的就是就是对数据点进行回归,使回归得到的结果成为sigmoid函数的输入

最佳回归系数


线性回归的形式是:

其中W就是回归系数向量,向量的每个元素对应数据的一个维度也就是一种特征

现在的任务就是确定最佳回归系数,常用的方法有最小二乘法、梯度上升法等最优化方法。

本文主要是使用梯度上升法作为讨论的基础

梯度上升法

梯度上升基于的思想是:要找函数的最大值,最好的方法是沿着该函数的梯度方向探寻,因为梯度总是指向函数增长最快的方向
有了方向,那么还要有步长才能朝着最优值移动,这个步长可以自己指定,这里设步长为alpha,那么算法的迭代公式为:

顺便提一下,相应的有个叫梯度下降的算法,用于求函数的最小值,只用把中间的加号变成减号就行了
梯度上升的伪代码如下:
  1. 每个回归系数初始化为1
  2. 重复R次:
  3. 计算整个数据集的梯度
  4. 使用 alpha * gradient 更新回归系数向量
  5. 返回回归系数

代码实现如下:
  1. # 载入测试数据,返回测试数据集和类别标签
  2. def loadDataSet():
  3. dataMat = []
  4. labelMat = []
  5. fr = open(r'E:\ml\machinelearninginaction\Ch05\testSet.txt')
  6. dataFromFile = fr.readlines()
  7. print len(dataFromFile)
  8. for line in dataFromFile:
  9. lineArr = line.strip().split()
  10. dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
  11. labelMat.append(int(lineArr[2]))
  12. return dataMat, labelMat
  13. # sigmoid 函数
  14. def sigmoid(inX):
  15. return 1.0/(1+exp(-inX))
  16. # =====================================
  17. # 梯度上升算法
  18. # 输入:
  19. # dataMatIn: 2维数组,每列代表一种特征
  20. # classLabels: 类别标签
  21. # 返回:逻辑回归参数
  22. # =====================================
  23. def gradAscent(dataMatIn, classLabels):
  24. dataMat = mat(dataMatIn)# 装换成numpy矩阵
  25. labelMat = mat(classLabels).transpose()# 转置
  26. m, n = shape(dataMat)
  27. weights = ones((n, 1))# 初始化权重都为1
  28. alpha = 0.001
  29. maxCycles = 500# 迭代次数
  30. for i in range(maxCycles):
  31. # 以下三行代码是梯度上升算法的具体实现
  32. h = sigmoid(dataMat * weights)# 矩阵乘法
  33. error = (labelMat - h)
  34. weights = weights + alpha * dataMat.transpose() * error
  35. return weights
关于代码有两点需要指出,第10行中loadData函数在特征列加了一列全是1的特征。
21~23行代码是梯度上升的具体实现,由上面提到的迭代公式到这里的实现需要一些数学推导,大概来说如下:


本文的数据集用的是《机器学习实战》的testSet.txt数据集,概览图如下:

导入数据集到上面的函数得到如下结果:

这就是回归系数

画出决策函数

为了使优化过程便于理解,我们可以把这个数据集已经刚才已经得到的回归线可视化
代码如下:
  1. # 画出数据集合logistic回归最佳拟合直线的函数
  2. # 输入wei是系数向量
  3. def plotBestFit(wei):
  4. import matplotlib.pyplot as plt
  5. weight = wei.getA() # 矩阵转换成数组
  6. dataMat, labelMat = loadDataSet()
  7. dataArray = array(dataMat)
  8. n = shape(dataArray)[0] # 获得数组的行数
  9. xcord1 = []
  10. ycord1 = []
  11. xcord2 = []
  12. ycord2 = []
  13. for i in range(n):
  14. if labelMat[i] == 1:
  15. xcord1.append(dataArray[i, 1])
  16. ycord1.append(dataArray[i, 2])
  17. else:
  18. xcord2.append(dataArray[i, 1])
  19. ycord2.append(dataArray[i, 2])
  20. fig = plt.figure()
  21. ax = fig.add_subplot(111)
  22. ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
  23. ax.scatter(xcord2, ycord2, s=30, c='green')
  24. x = arange(-3.0, 3.0, 0.1)
  25. y = (-weight[0] - weight[1] * x) / weight[2]
  26. ax.plot(x, y)
  27. plt.show()
这就是使用梯度上升500次得到的结果,效果还不错

随机梯度上升

从上面的代码可以看出梯度上升法每次更新回归系数的时候都要遍历整个数据集,计算复杂度太高
一种改进的方法是一次只用一个样本点来更新回归系数,这种方法叫做随机梯度上升算法
这样的话,每输入一个新的样本就可以对分类器进行一次更新,这种方式叫做增量式更新,所以这个算法是一个在线学习算法
与在线学习算法相对应的,一次处理所有数据被称作“批处理

改进后代码如下:
  1. # =========================================
  2. # 随机梯度上升算法
  3. # =========================================
  4. def stoGradAsent0(dataMatrix, classLabels):
  5. m, n = shape(dataMatrix)
  6. weights = ones(n)
  7. alpha = 0.001
  8. for i in range(m):
  9. h = sigmoid(sum(dataMatrix[i] * weights))
  10. err = h - classLabels[i]
  11. weights = weights + alpha * err * dataMatrix[i]
  12. return weights
用下面的代码调用上面的函数:
  1. data, label = loadDataSet()
  2. wei = stocGradAscent0(array(data), label)
  3. plotBestFit(wei)
得到如下分类结果:

可以看到这个分类效果没有之前的分类效果好。但是之前的效果是迭代500次才得到的,所以不具有可比性。

改进的随机梯度上升

  1. def stocGradAscent1(dataMatrix, classLabels, numIter=150):
  2. m,n = shape(dataMatrix)
  3. weights = ones(n)
  4. for j in range(numIter):
  5. dataIndex = range(m)
  6. for i in range(m):
  7. # alpha随着迭代次数减小,但是有常数项保证永远不会小到0
  8. alpha = 4/(1.0+j+i)+0.0001
  9. # 随机选取样本来更新回归系数
  10. randIndex = int(random.uniform(0,len(dataIndex)))
  11. h = sigmoid(sum(dataMatrix[randIndex]*weights))
  12. error = classLabels[randIndex] - h
  13. weights = weights + alpha * error * dataMatrix[randIndex]
  14. del(dataIndex[randIndex])
  15. return weights
从代码中我们可以看到主要做出了两个改进:
  1. 第八行代码表示alpha随着迭代次数减小,这是为了缓解回归系数的波动,尽快达到收敛状态(收敛是判断一个优化算法是否可靠的重要方法)。并且alpha有常数项保证永远不会小到0
  2. 第十行表示随机选取样本来更新回归系数,这样是为了减少周期波动
对数据集作20次遍历stocGradAscent1(array(data), label, 20)得到的分类效果如下:

很明显效果比没有改进前的随机梯度上升算法要好多了。

例子:从疝气病预测病马的死亡率


缺失值处理

在正式开始这个例子之前我们想先讨论一下数据中的缺失值处理
因为即将用到这个数据集的原始数据集是有缺失值的,而且缺失值的处理的数据的预处理中非常重要
常用的缺失值处理方法:
  • 使用可用特征的均值填补
  • 使用特殊值来填补缺失值,比如0或-1
  • 忽略有缺失值的样本
  • 使用其他机器学习算法来预测缺失值
  • 使用相似样本对应特征的均值俩填补
以下两种情况的值缺失处理方法是不同的
  1. 特征缺失:可以丢弃此样本;否则的话,由于numpy不支持包含缺失值,所以必须要填补上
  2. 标签缺失:基本上只能直接丢弃,因为和特征值不同,它很难使用某个合适值来替换

用logistic回归进行分类

  1. # 用于为每个输入样本分类
  2. # inX为待分类的样本的特征值
  3. # weights 为训练好的权重
  4. def classifyVector(inX, weights):
  5. prob = sigmoid(sum(inX * weights))
  6. if prob > 0.5:
  7. return 1
  8. else:
  9. return 0
  10. def colicTest():
  11. frTrain = open(r'E:\ml\machinelearninginaction\Ch05\horseColicTraining.txt')
  12. frTest = open(r'E:\ml\machinelearninginaction\Ch05\horseColicTest.txt')
  13. trainData = []
  14. trainLabels = []
  15. for line in frTrain.readlines():
  16. currLine = line.strip().split('\t')
  17. lineArr = []
  18. for i in range(21):#这个数据集有21个特征
  19. lineArr.append(float(currLine[i]))
  20. trainData.append(lineArr)
  21. trainLabels.append(float(currLine[21]))
  22. # 用训练集作500次迭代得到权重
  23. trainWeights = stocGradAscent1(array(trainData), trainLabels, 500)
  24. numTestVec = 0 # 记录测试样本的数量
  25. errorCount = 0 # 预测错误的数量
  26. for line in frTest.readlines():
  27. numTestVec += 1
  28. currLine = line.strip().split('\t')
  29. lineArr = []
  30. for i in range(21):
  31. lineArr.append(float(currLine[i]))
  32. # 把训练好的权重应用到测试集上
  33. if int(classifyVector(array(lineArr), trainWeights)) != int(currLine[21]):
  34. errorCount += 1
  35. errorRate = float(errorCount)/numTestVec
  36. print 'The error rate of this test is %f' %errorRate
  37. return errorRate
  38. def multiTest():
  39. numTests = 10; errorSum=0.0
  40. for k in range(numTests):
  41. errorSum += colicTest()
  42. print "after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests))
测试效果如下:

每次的错误率不同是因为随机策略导致的。

小结


 LR优点:计算代价不高,易于理解和实现
LR缺点:容易欠拟合,分类精度可能不高
使用数据类型:数值型和标称型

梯度上升是非常常用的最优化方法
随机梯度上升算法降低了计算复杂度,而且这是一个在线学习算法

缺失数据处理是数据分析乃至机器学习的重要组成部分,它没有标准的做法,取决于具体情况的不同


【4】Logistic回归的更多相关文章

  1. 神经网络、logistic回归等分类算法简单实现

    最近在github上看到一个很有趣的项目,通过文本训练可以让计算机写出特定风格的文章,有人就专门写了一个小项目生成汪峰风格的歌词.看完后有一些自己的小想法,也想做一个玩儿一玩儿.用到的原理是深度学习里 ...

  2. 机器学习——Logistic回归

    1.基于Logistic回归和Sigmoid函数的分类 2.基于最优化方法的最佳回归系数确定 2.1 梯度上升法 参考:机器学习--梯度下降算法 2.2 训练算法:使用梯度上升找到最佳参数 Logis ...

  3. logistic回归

    logistic回归 回归就是对已知公式的未知参数进行估计.比如已知公式是$y = a*x + b$,未知参数是a和b,利用多真实的(x,y)训练数据对a和b的取值去自动估计.估计的方法是在给定训练样 ...

  4. Logistic回归 python实现

    Logistic回归 算法优缺点: 1.计算代价不高,易于理解和实现2.容易欠拟合,分类精度可能不高3.适用数据类型:数值型和标称型 算法思想: 其实就我的理解来说,logistic回归实际上就是加了 ...

  5. Logistic回归的使用

    Logistic回归的使用和缺失值的处理 从疝气病预测病马的死亡率 数据集: UCI上的数据,368个样本,28个特征 测试方法: 交叉测试 实现细节: 1.数据中因为存在缺失值所以要进行预处理,这点 ...

  6. 如何在R语言中使用Logistic回归模型

    在日常学习或工作中经常会使用线性回归模型对某一事物进行预测,例如预测房价.身高.GDP.学生成绩等,发现这些被预测的变量都属于连续型变量.然而有些情况下,被预测变量可能是二元变量,即成功或失败.流失或 ...

  7. SPSS数据分析—配对Logistic回归模型

    Lofistic回归模型也可以用于配对资料,但是其分析方法和操作方法均与之前介绍的不同,具体表现 在以下几个方面1.每个配对组共有同一个回归参数,也就是说协变量在不同配对组中的作用相同2.常数项随着配 ...

  8. SPSS数据分析—多分类Logistic回归模型

    前面我们说过二分类Logistic回归模型,但分类变量并不只是二分类一种,还有多分类,本次我们介绍当因变量为多分类时的Logistic回归模型. 多分类Logistic回归模型又分为有序多分类Logi ...

  9. SPSS数据分析—二分类Logistic回归模型

    对于分类变量,我们知道通常使用卡方检验,但卡方检验仅能分析因素的作用,无法继续分析其作用大小和方向,并且当因素水平过多时,单元格被划分的越来越细,频数有可能为0,导致结果不准确,最重要的是卡方检验不能 ...

  10. Logistic回归分类算法原理分析与代码实现

    前言 本文将介绍机器学习分类算法中的Logistic回归分类算法并给出伪代码,Python代码实现. (说明:从本文开始,将接触到最优化算法相关的学习.旨在将这些最优化的算法用于训练出一个非线性的函数 ...

随机推荐

  1. 证明线程池ThreadPoolExecutor的核心线程数,最大线程数,队列长度的关系

    关于线程池的几个参数,很多人不是很清楚如何配置,他们之间是什么关系,我用代码来证明一下. package www.itbac.com; import java.util.concurrent.*; p ...

  2. 简洁明了的Noip考场策略 / 平时做题也适用

    1.选择策略: 评估的标准得分的难度不是AC的难度 2.思考问题: 怀疑的眼光审视自己 3.写代码前: 想想可不可以换一种代码实现会好写很多 把自己的思路再理一遍,可以写到纸上,记下来大致关键顺序 4 ...

  3. 你所不知道的 CSS 负值技巧与细节

    写本文的起因是,一天在群里有同学说误打误撞下,使用负的 outline-offset 实现了加号.嗯?好奇的我马上也动手尝试了下,到底是如何使用负的 outline-offset 实现加号呢? 使用负 ...

  4. springboot-jsp打jar问题

    [**前情提要**]最近做了一个项目,项目是springboot+jsp结构的,但是在发布生产环境的时候又需要用maven打成jar包,但是一开始的默认配置都不成功.下面的文章就是具体的解决过程. - ...

  5. CentOS yum 源修改

    修改 CentOS 默认 yum 源为 mirrors.163.com 首先备份系统自带yum源配置文件/etc/yum.repos.d/CentOS-Base.repo [root@localhos ...

  6. Java——检测其他线程的状态以及启动已死亡的线程

    这次这个的思路是在主类中维护一个map,map的key是线程名,value是线程的状态,然后创建周期执行的线程通过检测这个map来判断进程的状态,如果有死亡的进程就把该进程启动. 首先是主类,这里的m ...

  7. Javarscipt中数组或者字符串的随机排序方法

    在日常开发中,经常会遇到随机排序的需求,思路就是利用Math.random()方法,抽取随机数,让数组中的元素进行对调: 话不多说直接上代码,方法一:基本思路就是将a中随机抽取一个元素,放入b中,再从 ...

  8. 最短路径Dijkstra算法模板题---洛谷P3371 【模板】单源最短路径(弱化版)

    题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779. 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入格式 第一行 ...

  9. SQL TRUNCATE TABLE 命令

    SQL TRUNCATE TABLE 命令 SQL TRUNCATE TABLE 命令用于删除现有数据表中的所有数据. 你也可以使用 DROP TABLE 命令来删除整个数据表,不过 DROP TAB ...

  10. Eclipse导入spring-boot-plus(三)

    Eclipse导入spring-boot-plus 安装lombok插件 !!!请先确保Eclipse已安装lombok插件!!!