一、逻辑回归的简介
1、逻辑回归:
Logistic回归与多重线性回归实际上有很多相同之处,最大的区别就在于它们的因变量不同,其他的基本都差不多。正是因为如此,这两种回归可以归于同一个家族,即广义线性模型(generalizedlinear model)。
逻辑回归算法本质还是回归,只是其引入了逻辑函数来帮助其分类。Logistic回归实际上是一种分类方法,主要用于两分类问题(即输出只有两种,比如0和1)。
假设数据集有n个独立的特征,x1到xn为样本的n个特征。
常规的回归算法的目标是拟合出一个多项式函数,使得预测值与真实值的误差最小:
而我们希望这样的g(x)能够具有很好的逻辑判断性质,最好是能够直接表达具有特征x的样本被分到某类的概率。比如g(x)>0.5的时候能够表示x被分为正类,g(x)<0.5表示分为反类。而且我们希望g(x)总在[0, 1]之间。所以引入Logistic函数(或称为Sigmoid函数)
函数形式为:
图像为:
当Sigmoid函数值大于0.5时,归为1类别;
当Sigmoid函数值小于0.5时,归为0类别。
2、损失函数
由多项式函数g(x)构造预测函数:
函数 h(x) 的值有特殊的含义,它表示结果取1的概率,对于输入x分类结果为类别1和类别0的概率分别为:
而对于任意y,有:
故m个样本的概率分布为(似然函数):
等式两边同时去对数,得(对数似然函数):
构造损失函数:
当损失函数最小时,数据点越吻合这个多项式函数g(x)。
也就是说,我们的目标是求得使损失函数最小的回归系数。
求一个函数的最值,这里介绍梯度上升法(求函数的最大值)
3、梯度上升法
梯度上升法:基于的思想是要找到某函数的最大值,最好的方法是沿着该函
数的梯度方向探寻。
函数f(x,y)的梯度:
沿x的方向移动 ,
沿y的方向移动 ,
最后能够到达最优点,但是f(x,y)在待计算点需要有定义并且可微。
梯度算子总是指向函数值增长最快的方向。移动方向为梯度方向,移动量大小需要乘以一个参数,称之为步长。参数迭代公式为:
该公式将一直被迭代执行,直到达到某个停止条件为止,比如迭代次数达到某个指定值或者算法达到某个可以允许的误差范围。
化简:
其中:
即:
其中:为第j个特征值的系数
α为步长(实数)
m为样本数
为第i个数据
二、python代码的实现
1、前提
条件:给出100个样本点,每个样本点包含两个特征X1和X2,再加上一个标签:1或0。
目标:利用梯度上升法求得回归系数,化为线性函数
并在图中表示出来
数据点:
数据点以文本模式存入(我给它命名为testSet),如下(部分数据点):
算法:
梯度上升法
目标:求最佳回归参数w
输入:样本特征矩阵,样本标签矩阵(m为样本数量,n为特征值数目)
初始化:循环次数t=500,步长a=0.001,初始回归参数矩阵
计算:
i从1开始循环到500,循环内容如下:
因为用梯度上升法计算量较大,现在引入随机梯度上升法
随机梯度上升法
两个算法类似,但是随机梯度上升法在计算时用的是数值,梯度上升法用的是向量,还运用了矩阵的转换,还有一点就是前者只将所有数据代入,没有再迭代,虽然计算量小,但效果不怎么好,因此在此基础上再次改进
改进随机梯度上升法
这个算法改进的地方主要在两点:
1、α的值一直在变化,而且与数据随机对应
2、在将所有数据代入后,也进行了一定次数的迭代
上面的三个算法都在下面的代码中体现
2、代码
import matplotlib.pyplot as plt
from numpy import *
def loadDataSet():
dataMat = []; labelMat = []
fr = open('testSet.txt') #打开文件,100行3列的数据
for line in fr.readlines(): #读取整行,如第一行为:-0.017612 14.053064 0
lineArr = line.strip().split() #以列表的形式读取,如第一行为:['-0.017612', '14.053064', '0']
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) #取每行数据的前两个数组成100个元素如[1.0,-0.017612, 14.053064]的列表
labelMat.append(int(lineArr[2])) #取每行数据的最后一个数组成100个元素如[1],[0]的列表
return dataMat,labelMat #返回上面两个列表
def sigmoid(inX):
return 1.0/(1+exp(-inX)) #构建sigmoid函数
def gradAscent(dataMat, labelMat):
dataMatrix = mat(dataMat) #转化为100行3列的矩阵
labelMat = mat( labelMat).transpose() #转化为100行1列的矩阵
m,n = shape(dataMatrix) #获取行数m=100,列数n=3
alpha = 0.001
maxCycles = 500
weights = ones((n,1)) #创建n行1列元素均为1.的矩阵
for k in range(maxCycles): #进行500次循环运算
h = sigmoid(dataMatrix*weights)
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose()* error #迭代w=w0+a*x*(y-h(x))
return weights #返回w的值
def stocGradAscent0(dataMat, labelMat):
m,n = shape(dataMat) #获取行数m=100,列数n=3
alpha = 0.01
weights = ones(n) #创建1行n列元素均为1.的矩阵
for i in range(m):
#h = sigmoid(sum(dataMat[i]*weights)) #dataMat[i]指第i个列表,dataMat[i]*weights为1行3列的矩阵,sum使这3个数加起来
h = sigmoid(sum(dataMat[i]*weights))
error = labelMat[i] - h
weights = weights + alpha * error * dataMat[i] #迭代w=w0+a*x*(y-h(x))
return weights
def stocGradAscent1(dataMat, labelMat):
m,n = shape(dataMat) #获取行数m=100,列数n=3
numIter=150
weights = ones(n) #创建1行n列元素均为1.的矩阵
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 4/(1.0+j+i)+0.01 #调整alpha值
randIndex = int(random.uniform(0,len(dataIndex))) #在(0,100)内取随机数randIndex
h = sigmoid(sum(dataMat[randIndex]*weights))
error = labelMat[randIndex] - h
weights = weights + alpha * error * dataMat[randIndex] #迭代w=w0+a*x*(y-h(x))
del(dataIndex[randIndex])
return weights
def plotBestFit(weights):
dataMat,labelMat=loadDataSet() #从loadDataSet()函数读取数据
dataArr = array(dataMat) #将dataMat中的100组数据转化为100行3列的矩阵
n = shape(dataArr)[0] #得到行数100
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in range(n):
if int(labelMat[i])== 1:
xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2]) #当标签为1时,将矩阵的第二三个的元素赋值给x y
else:
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) #当标签为0时,将矩阵的第二三个的元素赋值给x y
x = arange(-4.0, 4.0, 0.1) #从-4.0~4.0每隔0.1取一个数组成的列表
y = (-float(weights[0])-float(weights[1])*x)/float(weights[2]) #令 y=(-w0-w1*x)/w2
plt.plot(xcord1, ycord1,'r*') #创建标签为1的散点图,用红色的*号表示
plt.plot(xcord2, ycord2,'g+') #创建标签为0的散点图,用绿色的+号表示
plt.plot(x, y,'b') #创建目标函数曲线
plt.legend(['sort 1','sort 0']) #添加图例
plt.xlabel('X1'); plt.ylabel('X2') #横轴设为X1,纵轴设为X2
#定义函数后下面开始调用
dataMat,labelMat=loadDataSet() #从loadDataSet()函数读取数据
weights0=gradAscent(dataMat,labelMat)
weights1=stocGradAscent0(array(dataMat), labelMat)
weights2=stocGradAscent1(array(dataMat), labelMat)
print 'w0=',weights0, '\nw1=',weights1,'\nw2=',weights2 #输出w
plt.figure(1);plotBestFit(weights0)
plt.figure(2);plotBestFit(weights1)
plt.figure(3);plotBestFit(weights2) #引用函数plotBestFit
plt.show() #使图像显示在表格中
输出:
w0= [[ 4.12414349]
[ 0.48007329]
[-0.6168482 ]]
w1= [ 1.01702007 0.85914348 -0.36579921]
w2= [ 14.39064495 0.68717787 -2.00324495]
图1:结果理想,但运行过程需进行大量运算(300次乘法),不利于大数据进行运算
图2:运算较简略,但结果不稳定,与理想结果差别较大
图3:计算量较小,结果较理想
三、小结
优点: 计算代价不高,易于理解和实现。
缺点: 容易欠拟合,分类精度可能不高。
适用数据类型:数值型和标称型数据。
数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等 (数值型目标变量主要用于回归分析)
标称型目标变量的结果只在有限目标集中取值,如真与假(标称目标变量主要用于分类)