【Machine Learning in Action --5】逻辑回归(LogisticRegression)
1、概述
Logistic regression(逻辑回归)是当前业界比较常用的机器学习方法,用于估计某种事物的可能性。
在经典之作《数学之美》中也看到了它用于广告预测,也就是根据某广告被用 户点击的可能性,把最可能被用户点击的广告摆在用户能看到的地方,然后叫他“你点我啊!”用户点了,你就有钱收了。这就是为什么我们的电脑现在广告泛滥的 原因。还有类似的某用户购买某商品的可能性,某病人患有某种疾病的可能性啊等等。这个世界是随机的(当然了,人为的确定性系统除外,但也有可能有噪声或产生错误的结果,只是这个错误发生的可能性太小了,小到千万年不遇,小到忽略不计而已),所以万物的发生都可以用可能性或者几率(Odds)来表达。“几率”指的是某事物发生的可能性与不发生的可能性的比值。
Logistic regression可以用来回归,也可以用来分类,主要是二分类。
2、基本理论
2.1Logistic regression和Sigmoid函数
回归:假设现在有一些数据点,我们用一条直线对这些点进行拟合(该条称为最佳拟合直线),这个拟合过程就称作回归。利用Logistic回归进行分类的思想是:根据现有数据对分类边界线建立回归公式,以此进行分类。这里的“回归”一词源于最佳拟合,表示找到最佳拟合参数,使用的是最优化算法。
Sigmoid函数具体的计算公式如下:
z=w0x0+w1x1+w2x2+...+wnxn, z=wTx 其中w是我们要找的最佳参数(系数),x是分类器的输入数据特征。
当x为0时,Sigmoid函数值为0.5,随着x的增大,对应的Sigmoid值将逼近于1;而随着x的减小,Sigmoid值将逼近于0。如果横坐标刻度足够大(如下图所示),Sigmoid函数看起来很像一个阶跃函数。
为了实现Logistic回归分类器,我们可以在每个特征上都乘以一个回归系数,然后把所有结果值相加,将这个总和代入Sigmoid函数中,进而得到一个范围在0-1之间的数值。任何大于0.5的数据被分入1类,小于0.5即被归入0类。所以,Logistic回归也可以被看作是一种概率估计。
2.2最优化理论
由上述问题得到,我们现在的问题变成了:最佳回归系数时多少?
z=w0x0+w1x1+w2x2+...+wnxn, z=wTx
向量x是分类器的输入数据,向量w是我们要找的最佳参数(系数),从而使得分类器尽可能地精确,为了寻找最佳参数,需要用到最优化理论的一些知识。
下面首先介绍梯度上升的最优化方法,我们将学习到如何使用该方法求得数据集的最佳参数。接下来,展示如何绘制梯度上升法产生的决策边界图,该图能将梯度上升法的分类效果可视化地呈现出来。最后我们将学习随机梯度上升法,以及如何对其进行修改以获得更好的结果。
2.2.1梯度上升法
梯度上升法的基于的思想是:要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。则函数f(x,y)的梯度由下式表示:
这个梯度意味着要沿x方向移动,沿y方向移动
,其中,函数f(x,y)必须要在待计算的点上有定义并且可微。具体的函数例子如下图所示:
注释:梯度上升算法到达每个点后都会重新估计移动的方向。从P0开始,计算完该点的梯度,函数就根据梯度移动到下一点P1。在P1点,梯度再次被重新计算,并沿新的梯度方向移动到P2。如此循环迭代,直到满足停止条件。迭代过程中,梯度算子总是保证我们能选取到最佳的移动方向。
可以看到,梯度算子总是指向函数值增长最快的方向。这里所说的是移动方向,而未提到移动量的大小。该量值称为歩长,记作,用向量来表示的话,梯度上升算法的迭代公式如下:
该公式将一直被迭代执行,直到达到某个停止条件为止,比如迭代次数达到某个指定值或算法达到可以允许的误差范围。
事例(用梯度上升找到最佳参数)
该数据中有100个样本点,每个点包含两个数值型特征:X1和X2。在此数据集上,我们将通过使用梯度上升法找到最佳回归系数,也就是说拟合出Logistic回归模型的最佳参数。在以下的数据集中,每行的前两个值分别是X1和X2,它们是数据特征,第三个值是数据对应的类别标签,为了方便计算,该函数还将X0的值设为1.0。
testSet.txt数据集如下:
梯度上升算法代码如下,建立一个logRegres.py的文件,添加如下代码:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from numpy import *
#Logistic回归梯度上升优化算法
def loadDataSet():
dataMat = []; labelMat = []
fr = open('testSet.txt')
for line in fr.readlines():
lineArr = line.strip().split() #最后一行开始读取
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) #获取X0,X1,X2的值
labelMat.append(int(lineArr[2])) #获取最后一列的类别标签
return dataMat,labelMat def sigmoid(inX):
return 1.0/(1+exp(-inX)) def gradAscent(dataMatIn, classLabels): #dataMatIn是一个2维Numpy数组
dataMatrix = mat(dataMatIn) #convert to NumPy matrix
labelMat = mat(classLabels).transpose() #convert to NumPy matrix
m,n = shape(dataMatrix)
alpha = 0.001
maxCycles = 500 #迭代次数为500
weights = ones((n,1)) #回归系数初始化为1
for k in range(maxCycles): #heavy on matrix operations
h = sigmoid(dataMatrix*weights) #matrix mult
error = (labelMat - h) #vector subtraction
weights = weights + alpha * dataMatrix.transpose()* error #matrix mult
return weights
在python提示符下,写下面代码:
>>>import logRegres
>>> dataArr,labelMat=logRegres.loadDataSet()
>>> logRegres.gradAscent(dataArr,labelMat)
matrix([[ 4.12414349],
[ 0.48007329],
[-0.6168482 ]])
解释:
>>> dataMat
matrix([[ 1.00000000e+00, -1.76120000e-02, 1.40530640e+01],
[ 1.00000000e+00, -1.39563400e+00, 4.66254100e+00],
...
[ 1.00000000e+00, 1.38861000e+00, 9.34199700e+00],
[ 1.00000000e+00, 3.17029000e-01, 1.47390250e+01]])
>>> labelMat
matrix([[0],
[1],
...
[0],
[0]])
上面解出了一组回归系数,它确定了不同类别数据之间的分隔线。那么怎样画出该分隔线,从而使得优化过程便于理解,打开logRegres.py添加如下代码:
#画出数据即和Logisitic回归最佳拟合直线的函数
def plotBestFit(weights):
import matplotlib.pyplot as plt
dataMat,labelMat=loadDataSet()
dataArr = array(dataMat)
n = shape(dataArr)[0]
xcord1 = []; ycord1 = []
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)
y = (-weights[0]-weights[1]*x)/weights[2] #W0X0+W1X1+W2X2=0(X0=1),X2=(-W0-W1X1)/W2
ax.plot(x, y)
plt.xlabel('X1'); plt.ylabel('X2');
plt.show()
运行程序清单,在python提示符下输入:
>>> from numpy import *
>>> reload(logRegres)
<module 'logRegres' from 'logRegres.pyc'>
>>> logRegres.plotBestFit(wei)
'''
这个分类结果相当不错,从图上看只错分了两到四个点。但是,尽管例子简单且数据集很小,但是这个方法确需要大量的计算(300次乘法),下一节将对该算法稍作改进,从而使它能用在真实数据集上。
2.2.2随机梯度上升
梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理1001个左右的数据集时尚克,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。
一种改进方法是一次仅用一个样本点来更新回归系数,该方法称为随机梯度上升算法。由于可以在信仰本到来时对分类器进行增量式更新,因而随机梯度上升算法是一个在线学习算法。
随机梯度上升算法代码如下,将下面代码添加到logRegres.py中:
#随机梯度上升函数
def stocGradAscent0(dataMatrix, classLabels):
m,n = shape(dataMatrix)
alpha = 0.01
weights = ones(n) #initialize to all ones
for i in range(m):
h = sigmoid(sum(dataMatrix[i]*weights))
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[i]
return weights
为了验证该方法的结果,我们在python提示符中输入以下命令:
>>> from numpy import *
>>> reload(logRegres)
<module 'logRegres' from 'logRegres.py'>
>>> dataArr,labelMat=logRegres.loadDataSet()
>>> weights=logRegres.stocGradAscent0(array(dataArr),labelMat)
>>> logRegres.plotBestFit(weights)
随机梯度上升算法与梯度上升算法在代码上很相似,但有一些区别:
梯度上升算法:变量h和误差error都是向量,有矩阵的转换过程
随机梯度上升算法:变量h和误差error都是数值,没有矩阵的转换过程,所有的数据类型都是Numpy数组。
由上述图得到的最佳拟合直线图,但拟合的直线没有那么完美,这里的分类器错分了三分之一的样本。一个判断优化算法优劣的可靠方法是看它是否收敛,也就是说参数是否达到了稳定值,是否还会不断地变化。
对上述随机梯度上升算法上做了些修改,使其在整个数据集上运行200次,最终绘制的三个回归系数的变化情况如图所示:
上图展示了随机梯度上升算法在200次迭代过程中(我们的数据库有100个样本,每个样本都对系数调整一次,所以共有200*100=20000次调整)回归系数的变化情况。由图中看到X2只经过了50次迭代就达到了稳定值,但X0和X1则需要更多次的迭代。另外值得注意的是,在大的波动停止后,还有一些小的波动。产生这种现象的原因是存在一些不能正确分类的样本点(数据集并非线性可分),在每次迭代时会引发系数的剧烈改变。我们期望算法能避免来回波动,从而收敛到某个值。另外,收敛速度也要加快。
2.2.3改进的随机梯度上升算法
对上述随机梯度上升算法,我们做两处改进来避免上述的波动问题:
1) 在每次迭代时,调整更新步长alpha的值。随着迭代的进行,alpha越来越小,这会缓解系数的高频波动(也就是每次迭代系数改变得太大,跳的跨度太 大)。当然了,为了避免alpha随着迭代不断减小到接近于0(这时候,系数几乎没有调整,那么迭代也没有意义了),我们约束alpha一定大于一个稍微大点的常数项,具体见代码。
2)每次迭代,改变样本的优化顺序。也就是随机选择样本来更新回归系数。这样做可以减少周期性的波动,因为样本顺序的改变,使得每次迭代不再形成周期性。
将下述代码添加到logRegres.py中:
#改进的随机梯度上升函数
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m,n = shape(dataMatrix)
weights = ones(n) #initialize to all ones
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 4/(1.0+j+i)+0.01 #j是迭代次数,i是样本点的下标,alpha每次迭代时需要调整
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
为了验证该方法的结果,我们在python提示符中输入以下命令:
>>> reload(logRegres)
<module 'logRegres' from 'logRegres.py'>
>>> dataArr,labelMat=logRegres.loadDataSet()
>>> weights=logRegres.stocGradAscent1(array(dataArr),labelMat)
>>> logRegres.plotBestFit(weights)
改进算法增加了一个迭代次数作为第三个参数,如果该参数没有给定的话,算法将默认迭代150次,如果给定,那么算法将按照新的参数值进行迭代。与随机梯度上升算法中的回归系数类似,改进的随机梯度上升算法中的各个回归系数的变化情况如下:
比较随机梯度上升和改进后的梯度上升,可以看到两点不同:
1)系数不再出现周期性波动。
2)系数可以很快的稳定下来,也就是快速收敛。这里只迭代了20次就收敛了。而上面的随机梯度下降需要迭代200次才能稳定。
3、Logistic回归的一般过程
(1)收集数据:采用任何方法收集数据
(2)准备数据:数据类型是数值型的,另外,结构化数据格式则最佳
(3)分析数据:采用任意方法对数据进行分析
(4)训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数
(5)测试算法:一旦训练步骤完成,分类将会很快
(6)使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作。
4、Logistic回归优缺点
优点:计算代价不高,易于理解和实现
缺点:容易欠拟合,分类精度可能不高
适用数据类型:数值型和标称型数据
【Machine Learning in Action --5】逻辑回归(LogisticRegression)的更多相关文章
- Machine Learning in action --LogisticRegession 逻辑回归
本系列主要参考<机器学习实战>,使用python3编译环境,实现书中的相关代码. 1.基本算法 关于梯度上升算法和随机梯度上升算法的选择: 当数据集较小时,使用梯度上升算法: 当数据集较大 ...
- 机器学习实战(Machine Learning in Action)学习笔记————05.Logistic回归
机器学习实战(Machine Learning in Action)学习笔记————05.Logistic回归 关键字:Logistic回归.python.源码解析.测试作者:米仓山下时间:2018- ...
- 《Machine Learning in Action》—— Taoye给你讲讲Logistic回归是咋回事
在手撕机器学习系列文章的上一篇,我们详细讲解了线性回归的问题,并且最后通过梯度下降算法拟合了一条直线,从而使得这条直线尽可能的切合数据样本集,已到达模型损失值最小的目的. 在本篇文章中,我们主要是手撕 ...
- 《Machine Learning in Action》—— 浅谈线性回归的那些事
<Machine Learning in Action>-- 浅谈线性回归的那些事 手撕机器学习算法系列文章已经肝了不少,自我感觉质量都挺不错的.目前已经更新了支持向量机SVM.决策树.K ...
- 【机器学习实战】Machine Learning in Action 代码 视频 项目案例
MachineLearning 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远 Machine Learning in Action (机器学习实战) | ApacheCN(apa ...
- 学习笔记之机器学习实战 (Machine Learning in Action)
机器学习实战 (豆瓣) https://book.douban.com/subject/24703171/ 机器学习是人工智能研究领域中一个极其重要的研究方向,在现今的大数据时代背景下,捕获数据并从中 ...
- 机器学习实战(Machine Learning in Action)学习笔记————07.使用Apriori算法进行关联分析
机器学习实战(Machine Learning in Action)学习笔记————07.使用Apriori算法进行关联分析 关键字:Apriori.关联规则挖掘.频繁项集作者:米仓山下时间:2018 ...
- 《Machine Learning in Action》—— Taoye给你讲讲决策树到底是支什么“鬼”
<Machine Learning in Action>-- Taoye给你讲讲决策树到底是支什么"鬼" 前面我们已经详细讲解了线性SVM以及SMO的初步优化过程,具体 ...
- K近邻 Python实现 机器学习实战(Machine Learning in Action)
算法原理 K近邻是机器学习中常见的分类方法之间,也是相对最简单的一种分类方法,属于监督学习范畴.其实K近邻并没有显式的学习过程,它的学习过程就是测试过程.K近邻思想很简单:先给你一个训练数据集D,包括 ...
- 机器学习实战(Machine Learning in Action)学习笔记————10.奇异值分解(SVD)原理、基于协同过滤的推荐引擎、数据降维
关键字:SVD.奇异值分解.降维.基于协同过滤的推荐引擎作者:米仓山下时间:2018-11-3机器学习实战(Machine Learning in Action,@author: Peter Harr ...
随机推荐
- TortoiseSVN 1.8 关于右键的设置
以前用SVN但都是 IDE 自己集成的插件,最近使用 android studio 发现居然自己不带SVN command line插件,非得自己单独装一个,于是使用了 TortoiseSVN ,但用 ...
- Android使用Home键后应用程序重启的问题
正常情况应该是在点击 home 按键后,程序在后台暂停运行,点击 后退键 才会退出应用的,但是今天遇到个问题,点击 home 键后,重新再打开应用却每次都返回应用启动页面,有些莫名其妙,一番googl ...
- Mysq 5.7l服务无法启动,没有报告任何错误
昨天系统崩溃了,然后重装了Mysql 5.7 安装步骤和遇到问题及解决方案. 去官网下载Mysql 5.7的解压包(zip),解压到你要安装的目录. 我的安装目录是:D:\Java\Mysql 安装步 ...
- 解锁Oracle数据库用户
Oracle数据库,如何解除数据库用户账户的锁定呢???如何修改口令呢??? 请问各位高手:Oracle数据库,如何解除数据库用户账户的锁定呢???如何修改口令呢???[万分感激各位高手的帮忙] 00 ...
- Python学习笔记——进阶篇【第九周】———线程、进程、协程篇(队列Queue和生产者消费者模型)
Python之路,进程.线程.协程篇 本节内容 进程.与线程区别 cpu运行原理 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Ev ...
- asp.net时间范围查询
1.首先要查询类表中的一个时间段,要明确我的数据库中只有一个时间字段,我们先将他拆分一下. if ($("#news_OpenTime").val() != "" ...
- 系统自动生成ID(比UUID.radom().tostring()要好看)
public class test1 { public static void main(String[] args) { char[] para = {'A','B','C','D','E','F' ...
- iframe自适应高度处理方案
第一种:这个方案能解决大多数自适应高度问题,方法是在iframe所要加载的页面上添加代码,把它的高度告诉iframe所在页面.缺点显而易见,如果iframe要加载的页面非常多而且不固定那么代码加起来很 ...
- 每个Web开发者必备的9个软技能
对于一份工作,你可能专注于修炼自己的内功,会在不自觉中忽视软技能.硬技能决定你是否能得到工作,而软技能能够表明你是否适合这份工作和适应工作环境等.所有的公司都有属于自己的文化,并努力将这些文化传承下去 ...
- yii2.0 面包屑的使用
yii2中面包屑是yii2自带的小部件,类似本网站的导航栏应该就是采用面包屑来完成的 例子如下,需要引入 yii\widgets\Breadcrumbs echo Breadcrumbs::widge ...