前言

本文将介绍机器学习分类算法中的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 20**-**-** @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回归分类算法原理分析与代码实现的更多相关文章

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

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

  2. 第一篇:K-近邻分类算法原理分析与代码实现

    前言 本文介绍机器学习分类算法中的K-近邻算法并给出伪代码与Python代码实现. 算法原理 首先获取训练集中与目标对象距离最近的k个对象,然后再获取这k个对象的分类标签,求出其中出现频数最大的标签. ...

  3. 第十三篇:K-Means 聚类算法原理分析与代码实现

    前言 在前面的文章中,涉及到的机器学习算法均为监督学习算法. 所谓监督学习,就是有训练过程的学习.再确切点,就是有 "分类标签集" 的学习. 现在开始,将进入到非监督学习领域.从经 ...

  4. logistic回归介绍以及原理分析

    1.什么是logistic回归? logistic回归虽然说是回归,但确是为了解决分类问题,是二分类任务的首选方法,简单来说,输出结果不是0就是1 举个简单的例子: 癌症检测:这种算法输入病理图片并且 ...

  5. 第十四篇:Apriori 关联分析算法原理分析与代码实现

    前言 想必大家都听过数据挖掘领域那个经典的故事 - "啤酒与尿布" 的故事. 那么,具体是怎么从海量销售信息中挖掘出啤酒和尿布之间的关系呢? 这就是关联分析所要完成的任务了. 本文 ...

  6. K-Means 聚类算法原理分析与代码实现

    前言 在前面的文章中,涉及到的机器学习算法均为监督学习算法. 所谓监督学习,就是有训练过程的学习.再确切点,就是有 "分类标签集" 的学习. 现在开始,将进入到非监督学习领域.从经 ...

  7. Apriori 关联分析算法原理分析与代码实现

    前言 想必大家都听过数据挖掘领域那个经典的故事 - "啤酒与尿布" 的故事. 那么,具体是怎么从海量销售信息中挖掘出啤酒和尿布之间的关系呢? 这就是关联分析所要完成的任务了. 本文 ...

  8. Lineage逻辑回归分类算法

    Lineage逻辑回归分类算法 线性回归和逻辑回归参考文章: http://blog.csdn.net/viewcode/article/details/8794401 http://www.cnbl ...

  9. Logistic Algorithm分类算法的Octave仿真

    本次Octave仿真解决的问题是,根据两门入学考试的成绩来决定学生是否被录取,我们学习的训练集是包含100名学生成绩及其录取结果的数据,需要设计算法来学习该数据集,并且对新给出的学生成绩进行录取结果预 ...

随机推荐

  1. mysql linux 区分大小写

    查看大小写区分 mysql> show variables like "%case%"; +------------------------+-------+ | Varia ...

  2. 线程安全,有状态,无状态的对象<转>

    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.另外 ...

  3. protobuf--数据序列化及反序列化

    ProtoBuf是一种灵活高效的独立于语言平台的结构化数据表示方法,可用于表示通信协议和数据存储等各方面,与XML相比,ProtoBuF更小更快更简单.你可以用定义自己ProtoBuf的数据结构,用P ...

  4. 用 phpize 编译共享 PECL 扩展库

    有时候不能用 pecl 安装命令.这可能是因为在防火墙后面,或者是因为想要安装的扩展库还没有 PECL 兼容的包,例如 SVN 中尚未发布的扩展库.如果要编译这种扩展库,可以用更底层的编译工具来手工进 ...

  5. Framework 7 之 给Picker Modal 添加半透明背景

    官网的效果图如下: 效果图如下: 我们需要在显示下面浮层的时候显示后面的半透明背景,Framework 7 里面默认有个半透明背景,如下图: 解决方案: 1.在</body>标签之前添加& ...

  6. js学习笔记17----元素的各种位置,尺寸

    1. offsetLeft[Top] 当前元素到定位父级(即offsetParent)的距离(偏移值 ). 父级没有定位: offsetParent -> body offsetLeft[Top ...

  7. 10 个强大的JavaScript / jQuery 模板引擎推荐

    模板引擎是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档.由于在开发过程中,网站或应用程序的界面与数据实现分离,大大提升了开发效率,良好的设计也使得代码重用变得更加容易. 本文 ...

  8. Python 随机数,break,continue

    #-*- coding:utf-8 -*- #导入模块 import random #打印10以内的随机数 num = 5 while num > 0: #random.randint(0,10 ...

  9. 多媒体开发之h264中的sps---sps信息提取之分辨率宽高提取2

    -------------------author:pkf -----------------------------time:2015-8-20 -------------------------- ...

  10. perl chomp 函数的真正作用

    之前一直以为chomp函数只是去掉字符串末尾的\n, 但是今天写程序遇到一个bug,最后的原因就处在chomp上: 读取fasta文件,内容如下: >1 ATGCTAGCTACGTACGTACG ...