Logistic回归分类算法原理分析与代码实现
前言
本文将介绍机器学习分类算法中的Logistic回归分类算法并给出伪代码,Python代码实现。
(说明:从本文开始,将接触到最优化算法相关的学习。旨在将这些最优化的算法用于训练出一个非线性的函数,以用于分类。)
算法原理
首先要提到的概念是回归。
对于回归这个概念,在以后的文章会有系统而深入的学习。简单的说,回归就是用一条线对N多数据点进行一个拟合,这个拟合的过程就叫做回归。
Logistic回归分类算法就是对数据集建立回归公式,以此进行分类。
而至于如何寻找最佳回归系数,或者说是分类器的训练,就需要使用到最优化算法了。
回归分类器的形式
基本形式是用每个特征都乘以一个回归系数,然后把所有的结果值相加。
这样算出的很多结果都是连续的,不利于分类,故可以将结果再带入到一个Sigmoid函数以得到一些比较离散的分类结果。
Sigmoid函数的轮廓如下:
这样,计算的结果会是一个0-1的值。进而0.5以上归为一类,以下归为一类即可。(一般的逻辑回归只能解决两个分类的问题)
接下来的工作重点就转移到了最佳回归系数的确定了。
最佳回归系数的确定
确定最佳回归系数的过程,也就是对数据集进行训练的过程。
求最佳回归系数的步骤如下:
1. 列出分类函数:
(θ 指回归系数,在实践中往往会再对结果进行一个Sigmoid转换)
2. 给出分类函数对应的错误估计函数:
(m为样本个数)
只有当某个θ向量使上面的错误估计函数J(θ)取得最小值的时候,这个θ向量才是最佳回归系数向量。
3. 采用梯度下降法或者最小二乘法求错误函数取得最小值的时候θ的取值:
为表述方便,上式仅为一个样本的情况,实际中要综合多个样本的情况需要进行一个求和 (除非你使用后面会介绍的随机梯度上升算法),具体请参考下面的代码实现部分。
将步骤 2 中的错误函数加上负号,就可以把问题转换为求极大值,梯度下降法转换为梯度上升法。
更详尽的推导部分,在以后专门分析回归的文章中给出。
基于梯度上升法的最佳回归参数拟合
梯度上升法求最大值的核心思想是将自变量沿着目标函数的梯度方向移动,一直移动到指定的次数或者说某个允许的误差范围。
基于梯度上升法的分类伪代码:
每个回归系数初始化为1
重复R次:
计算整个数据集的梯度
使用 alpha * gradient 更新回归系数向量
返回回归系数
下面给出完整的实现及测试代码(用到的数据集文件共 4 列,前 3 列为特征,最后一列为分类结果):
#!/usr/bin/env python
# -*- coding:UTF-8 -*- '''
Created on 2014-12-29 @author: fangmeng
''' import numpy #=====================================
# 输入:
# 空
# 输出:
# dataMat: 测试数据集
# labelMat: 测试分类标签集
#=====================================
def loadDataSet():
'创建测试数据集,分类标签集并返回。' # 测试数据集
dataMat = [];
# 测试分类标签集
labelMat = []
# 文本数据源
fr = open('/home/fangmeng/testSet.txt') # 载入数据
for line in fr.readlines():
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2])) return dataMat,labelMat #=====================================
# 输入:
# inX: 目标转换向量
# 输出:
# 1.0/(1+numpy.exp(-inX)): 转换结果
#=====================================
def sigmoid(inX):
'sigmoid转换函数' return 1.0/(1+numpy.exp(-inX)) #=====================================
# 输入:
# dataMatIn: 数据集
# classLabels: 分类标签集
# 输出:
# weights: 最佳拟合参数向量
#=====================================
def gradAscent(dataMatIn, classLabels):
'基于梯度上升法的logistic回归分类器' # 将数据集,分类标签集存入矩阵类型。
dataMatrix = numpy.mat(dataMatIn)
labelMat = numpy.mat(classLabels).transpose() # 上升步长度
alpha = 0.001
# 迭代次数
maxCycles = 500
# 初始化回归参数向量
m,n = numpy.shape(dataMatrix)
weights = numpy.ones((n,1)) # 对回归系数进行maxCycles次梯度上升
for k in range(maxCycles):
h = sigmoid(dataMatrix*weights)
error = (labelMat - h)
weights = weights + alpha * dataMatrix.transpose()* error return weights def test():
'测试' dataArr, labelMat = loadDataSet()
print gradAscent(dataArr, labelMat) if __name__ == '__main__':
test()
测试结果:
拟合结果展示
可使用matplotlib画决策边界,用作分析拟合结果是否达到预期。
绘制及测试部分代码如下所示:
#======================================
# 输入:
# weights: 回归系数向量
# 输出:
# 图形化的决策边界演示
#======================================
def plotBestFit(weights):
'决策边界演示' import matplotlib.pyplot as plt
# 获取数据集 分类标签集
dataMat,labelMat=loadDataSet()
dataArr = numpy.array(dataMat) # 两种分类下的两种特征列表
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in range(numpy.shape(dataArr)[0]):
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 = numpy.arange(-3.0, 3.0, 0.1)
y = (-weights[0]-weights[1]*x)/weights[2]
ax.plot(x, y) plt.xlabel('X1'); plt.ylabel('X2');
plt.show() def test():
'测试' dataArr, labelMat = loadDataSet()
weights = gradAscent(dataArr, labelMat)
plotBestFit(weights.getA())
测试结果:
更好的求最值方法 - 随机梯度上升
Logistic回归的本质其实就是求拟合参数,而求拟合参数最核心的就是求错误估计函数的最小值。
仔细分析上面的代码,会发现该分类的效率做多的耗费也是在求最值上面。由于每次都要用所有数据来计算梯度,导致数据集非常大的时候,该算法很不给力。
实践表明,每次仅仅用一个样本点来更新回归系数,当所有样本算完,也能达到相似的效果(仅仅是相似,或者说接近)。
由于可以在每个样本到达的时候对分类器进行增量式更新,因此随机梯度上升算法其实是一个在线学习算法。
基于随机梯度上升的分类伪代码:
所有回归系数初始化为1
对数据集中的每个样本:
计算该样本的梯度
使用 alpha * gradient 更新回归系数值
返回回归系数值
请对比上面的基本梯度上升算法进行理解学习。
优化之后的分类算法函数如下:
#=====================================
# 输入:
# dataMatIn: 数据集(注意是向量类型)
# classLabels: 分类标签集
# 输出:
# weights: 最佳拟合参数向量
#=====================================
def stocGradAscent0(dataMatrix, classLabels):
'基于随机梯度上升法的logistic回归分类器' m,n = numpy.shape(dataMatrix)
# 上升步长度
alpha = 0.01
# 初始化回归参数向量
weights = numpy.ones(n) # 对回归系数进行样本数量次数的梯度上升,每次上升仅用一个样本。
for i in range(m):
h = sigmoid(sum(dataMatrix[i]*weights))
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[i]
return weights
你也许会疑惑 "不是说随机梯度上升算法吗?随机呢?",不用急,很快就会提到这个。
分析优化后的代码可以看到两个区别:一个是当前分类结果变量h和误差变量都是数值类型(之前为向量类型),二是无矩阵转换过程,数据集为numpy向量的数组类型。
测试结果:
对比优化前的算法结果,可以看出分类错误率更高了。
优化后的效果反而更差了?这样说有点不公平,因为优化后的算法只是迭代了100次,而优化前的足足有500次。
那么接下来可以进一步优化,理论依据为:增加迭代计算的次数,当达到一个接近收敛或者已经收敛的状态时,停止迭代。
那么如何做到这点呢?
第一是要动态的选定步长,第二,就是每次随机的选定样本,这就是为什么叫做随机梯度上升算法了。
最终修改后的分类器如下:
#=====================================
# 输入:
# dataMatIn: 数据集(注意是向量类型)
# classLabels: 分类标签集
# 输出:
# weights: 最佳拟合参数向量
#=====================================
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
'基于随机梯度上升法的logistic回归分类器' m,n = numpy.shape(dataMatrix)
weights = numpy.ones(n) # 迭代次数控制
for j in range(numIter):
# 样本选择集
dataIndex = range(m)
# 随机选取样本遍历一轮
for i in range(m):
# 动态修正步长
alpha = 4/(1.0+j+i)+0.0001
# 随机选取变量进行梯度上升
randIndex = int(numpy.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
运行结果:
这次,和最原始的基于梯度上升法分类器的结果就差不多了。但是迭代次数大大的减少了。
网上也有一些非常严格的证明随机梯度上升算法的收敛速度更快(相比基础梯度上升算法)的证明,有兴趣的读者可以查找相关论文。
小结
1. 逻辑回归的计算代价不高,是很常用的分类算法。集中基于随机梯度上升的逻辑回归分类器能够支持在线学习。
2. 但逻辑回归算法缺点很明显 - 一般只能解决两个类的分类问题。
3. 另外逻辑回归容易欠拟合,导致分类的精度不高。
Logistic回归分类算法原理分析与代码实现的更多相关文章
- 第七篇:Logistic回归分类算法原理分析与代码实现
前言 本文将介绍机器学习分类算法中的Logistic回归分类算法并给出伪代码,Python代码实现. (说明:从本文开始,将接触到最优化算法相关的学习.旨在将这些最优化的算法用于训练出一个非线性的函数 ...
- 第一篇:K-近邻分类算法原理分析与代码实现
前言 本文介绍机器学习分类算法中的K-近邻算法并给出伪代码与Python代码实现. 算法原理 首先获取训练集中与目标对象距离最近的k个对象,然后再获取这k个对象的分类标签,求出其中出现频数最大的标签. ...
- logistic回归介绍以及原理分析
1.什么是logistic回归? logistic回归虽然说是回归,但确是为了解决分类问题,是二分类任务的首选方法,简单来说,输出结果不是0就是1 举个简单的例子: 癌症检测:这种算法输入病理图片并且 ...
- Apriori 关联分析算法原理分析与代码实现
前言 想必大家都听过数据挖掘领域那个经典的故事 - "啤酒与尿布" 的故事. 那么,具体是怎么从海量销售信息中挖掘出啤酒和尿布之间的关系呢? 这就是关联分析所要完成的任务了. 本文 ...
- 第十四篇:Apriori 关联分析算法原理分析与代码实现
前言 想必大家都听过数据挖掘领域那个经典的故事 - "啤酒与尿布" 的故事. 那么,具体是怎么从海量销售信息中挖掘出啤酒和尿布之间的关系呢? 这就是关联分析所要完成的任务了. 本文 ...
- K-Means 聚类算法原理分析与代码实现
前言 在前面的文章中,涉及到的机器学习算法均为监督学习算法. 所谓监督学习,就是有训练过程的学习.再确切点,就是有 "分类标签集" 的学习. 现在开始,将进入到非监督学习领域.从经 ...
- 第十三篇:K-Means 聚类算法原理分析与代码实现
前言 在前面的文章中,涉及到的机器学习算法均为监督学习算法. 所谓监督学习,就是有训练过程的学习.再确切点,就是有 "分类标签集" 的学习. 现在开始,将进入到非监督学习领域.从经 ...
- Lineage逻辑回归分类算法
Lineage逻辑回归分类算法 线性回归和逻辑回归参考文章: http://blog.csdn.net/viewcode/article/details/8794401 http://www.cnbl ...
- Logistic Algorithm分类算法的Octave仿真
本次Octave仿真解决的问题是,根据两门入学考试的成绩来决定学生是否被录取,我们学习的训练集是包含100名学生成绩及其录取结果的数据,需要设计算法来学习该数据集,并且对新给出的学生成绩进行录取结果预 ...
随机推荐
- JavaEE Log4j
1.Log4j是Apache的一个开放源代码项目,可以通过一个配置文件来灵活的进行配置,而不需要修改应用的代码. 2.Log4j有三个主要部件组成: 记录器:按照布局中指定的格式把日志信息写入一个或多 ...
- javascript类继承的一些实验
其实一开始编js没怎么用过对象,一般都用func,func,func···但是用多了,感觉代码一点都不美观,还要这里包一个函数,那里包一个函数,或者一直都是函数调用,不好看,而且一些重用的都要重写的话 ...
- ICEM(2)—机翼翼稍网格绘制
有时我们需要观察翼尖涡,这就需要将机翼全部被网格包围.但是网上比较多的教程都是机翼边缘即为网格边缘,机翼位于网格内部的不多.若是直接将网格拉伸,则会产生结构和非结构网格交错的情况.下面是绘制步骤 1. ...
- C#自定义属性(跟成员变量的区别)
属性声明 public int age { get; set; } 从功能上等价于 private int m_age; public int age {get { return m_age; }se ...
- I/O Directory类
Directory类 Directory类位于System.IO 命名空间.Directory类提供了在目录和子目录中进行创建移动和列举操作的静态方法.此外,你还可以访问和操作各种各样的目录属性. 1 ...
- 分析自定义view的实现过程-实现雪花飞舞效果(转载有改动)
声明:本文源码出自实现雪花飞舞效果(有改动)主要通过这篇文来分析自定义view的实现过程. 没事时,比较喜欢上网看看一些新的东西,泡在网上的日子就是一个很不错的网站. 下面开始了,哈哈.^_^ 大家都 ...
- linux下如何使用sftp命令
sftp 是一个交互式文件传输程式.它类似于 ftp, 但它进行加密传输,比FTP有更高的安全性.下边就简单介绍一下如何远程连接主机,进行文件的上传和下载,以及一些相关操作. 举例,如远程主机的 IP ...
- java nio 与io区别
转自:http://blog.csdn.net/keda8997110/article/details/19549493 当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使 ...
- 7月17日——高校就业信息网站功能及数据获取之python爬虫
本周我们小组在分析上周用户需求之后,确定了网站的主要框架和功能.数据收集和存储方式,以及项目任务分配. 一.网站的主要框架和功能. 网站近期将要实现的主要功能有,先重点收集高校(华东五校)就业宣讲会的 ...
- 我的PHP编程环境变迁:notepad -> notepad++ -> Sublime Text2 -> PhpStorm
10多年前最一开始写PHP程序的时候是用windows自带的notepad,现在想来真的很屌丝. 后来经人推荐换成了notepad++,感觉还是相当不错的(中间还用过一阵子editplus). 比较喜 ...