这里就有一些麻烦的地方:为什么梯度是:error*y*x
这其中是用到了likelihood的但是证明实在太麻烦了,我引用一下台北大学林轩田老师的证明方法:
证明这里其实就出来了,这个梯度结合一下PLA,发现如下
这就是 : 学习效率 * error(是否等于标注) * y * n。下面是代码
#coding=gbk
from numpy import *
'''
Created on 2018年8月1日
@author: 张怡
'''
from statsmodels.sandbox.nonparametric.tests.ex_smoothers import weights
'''
logistic回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,以此进行分类。
逻辑斯蒂回归主要针对的是0-1数值,二分类预测,用到的函数是sigmoid函数,傻子都应该记住的一个函数
当该值大于0.5归类为1,否则归类为0,这样就完成了二分类的任务。
所以也算是一个概率估计
二.关于最佳回归系数的确定
w就是我们要拟合的最佳参数,
1,。梯度上升法、
2.使用梯度上升发寻找最佳参数
'''
#预处理数据
def loadDataSet():
#创建两个列表
dataMat = []; labelMat = []
#打开文本数据集
fr = open('testSet.txt')
#遍历文本的每一行
for line in fr.readlines():
#对当前行去除收尾空格,并按空格进行分离
lineArr = line.strip().split()
#讲每一行的两个特征x1,x2,加上x0=1,组成列表并添加到数据集列表中
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
#将当前行的标签添加到标签列表中
labelMat.append(int(lineArr[2]))
#返回数据列表、标签列表
return dataMat,labelMat
#定义sigmoid函数
def sigmoid(inx):
return 1/(1+exp(-inx))
#梯度上升法更新最有拟合参数
#@dataMatIn : 数据集
#@classLabels : 数据标签
def gradAscent(dataMatIn , classLabels):
#将数据集列表转换为Numpy矩阵
dataMatrix = mat(dataMatIn)
#将数据集标签列表转换Numpy矩阵,并转置
labelMat = mat(classLabels).transpose()
#获取数据集矩阵的行数和猎术
m,n = shape(dataMatrix)
#学习步长
alpha = 0.001
#最大迭代次数
maxCycles = 500
#初始化权值参数向量的每个维度均为1.0
#是个向量,毕竟维度嘛
weights = ones((n,1))
#循环迭代次数
for k in range(maxCycles):
#求当前的sigmoid函数预测概率
#每次都要遍历数据一次
h = sigmoid(dataMatrix*weights)
#*******************************************
#此处计算真是类别和预测类别的差值
#对logistic回归函数的对数释然函数的参数项求偏导
error = (labelMat-h)
#更新权值参数s
#梯度是对参数进行求偏导数
#w = w + x * error(y)这是梯度上升
#梯度下降就是减法,但是PLA中是w=w-y*x
weights = weights+alpha*dataMatrix.transpose()*error
return weights
#还可以通过matpotlib画出决策的边界。集体代码是
def plotBestFit(wei):
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]
ax.plot(x, y)
plt.xlabel('X1');
plt.ylabel('X2');
plt.show()
'''
我们知道梯度上升法每次更新回归系数都需要遍历整个数据集,当样本数量较小时,该方法尚可,但是当样本数据集非常大且特征非常多时,那么随机梯度下降法的计算复杂度就会特别高。一种改进的方法是一次仅用一个样本点来更新回归系数,即岁集梯度上升法。由于可以在新样本到来时对分类器进行增量式更新,因此随机梯度上升法是一个在线学习算法。随机梯度上升法可以写成如下伪代码:
所有回归系数初始化为1
随机梯度上升是一个在线学习算法,与在线学习相对应,一次处理所有数据被称作是’批处理‘
对数据集每个样本
计算该样本的梯度
使用alpha*gradient更新回顾系数值
返回回归系数值
前者对参数采用精确解析的方式,计算时间场但模型性能较高,后者采用随机梯度上升估算模型参数,计算时间短但产出的模型性能略低
'''
def stocGradAscent(dataMatrix , classLabels):
#获取数据集的行数和列数
m,n = shape(dataMatrix)
#为了方便计算转换为numpy数组,也就是矩阵
dataMat = mat(dataMatrix)
#初始化权值向量各个参数为1.0
#而且是一个矩阵或者向量
weights= ones(n)
#设置学习效率为0.01
alpha = 0.01
#我们设置循环m次数,每次选取数据集一个样本更新参数
for i in range(m):
#计算当前样本的sigmoid函数值
h = sigmoid(dataMatrix[i]*weights)
#计算当前样本的残差(代替梯度)
error = (classLabels[i]-h)
#更新权值参数
weights = weights + alpha*error*dataMatrix[i]
return weights
'''
评判一个优化算法的优劣的可靠方法是看其是否收敛,也就是说参数的值是否达到稳定值。此外,当参数值接近稳定时,仍然可能会出现一些小的周期性的波动。这种情况发生的原因是样本集中存在一些不能正确分类的样本点(数据集并非线性可分),所以这些点在每次迭代时会引发系数的剧烈改变,造成周期性的波动。显然我们希望算法能够避免来回波动,从而收敛到某个值,并且收敛速度也要足够快。
'''
#因此进行适当的修改
#@dataMatriX: 数据集列表
#@classLabels : 标签类别o
#@numIter :迭代次数,默认150
def stocGradAscent1(dataMatriX,classLabels,numTter = 150):
#为了方便计算转换为numpy数组,也就是矩阵
dataMat = array(classLabels)
#获取数据集的行数和列数
m,n = shape(dataMat)
#初始化权值向量各个参数为1.0
#而且是一个矩阵或者向量
weights= ones(n)
#循环150次数
for j in range(numTter):
#获取数据集行下标列表
dataIndex = range(m)
#遍历行列表
for i in range(m):
#每次更新参数时候设置动态的学习效率,且为保证多次迭代后对数据仍有一定的影响
#添加了固定的效率.1
alpha = 4/(1.0+j+i)+0.1
#随机获得样本
randomIndex = int(random.nuiform(0,len(dataIndex)))
#计算当前sigmoid函数值
h = sigmoid(dataMat[randomIndex]*weights)
#计算权值更新
#***********************************************
error = classLabels - h
weights = weights + alpha*error*dataMat[randomIndex]
#***********************************************
#选取该样本后,该样本下表删除,确保每次迭代时候只使用一次
del(dataIndex[randomIndex])
'''
上述代码有两点改进:
1.一个是学习效率,每次迭代时候都会调整,这样就不会有高频波动,而且有一个固定的常数0.1.也不会为0
2.随机选取样本更新回归系数,减少了周期性的波动,用完就删除这个值
3.s手链的更快,因为遍历的次数不再是500次,而是20次
'''
#*********************************************
'''
从疝气病症预测病马死亡率
1.现有数据集有100个样本和20个特征,但是数据存在一定的问题,即数据集有30%的缺失,因此,我们在对病马进行预测死亡率前,首先要解决数据的缺失问题。
遇到数据缺失的情况,但有时候数据相当昂贵,扔掉和重新获取均不可取,这显然是会带来更多的成本负担,所以我们可以选取一些有效的方法来解决该类问题。比如:
1 使用可用特征的均值填补缺失值
2 使用特殊值来填补缺失的特征,如-1
3 忽略有缺失值的样本
4 使用相似样本的平均值填补缺失值
5 使用另外的机器学习算法预测缺失值
这里我们根据logistic回归的函数特征,选择实数0代替所有缺失值,这恰好使用logistics回归,因此他在参数更新时候不会影响参数的值
也就是:如果某个特征对应值为0.公式w也不会发生改变
'''
#------------------------------实例:从疝气病预测病马的死亡率----------------------------
#1.准备数据:处理数据的缺失值
#这里讲特征的缺失值补为0,从而在更新时候不影响系数的值
#2 分类决策函数
def clasifyVector(inX , weights):
#计算logistics回归预测概率
prob = sigmoid(inX*weights)
#大于0.5预测为1
if prob > 0.5:
return 1.0
else:
return 0.0
#logistic回归预测算法
def colicTest():
#打开训练数据集
frTrain = open('horseColicTraining.txt')
#打开测试数据集
frTest = open('horseColicTest.txt')
#新建两个空列表,保存训练集和测试集
trainingSet = [];trainingLabels = []
#读取训练集文档的每一行
for line in frTrain.readlines():
#对当前行进行特征分割
#去掉'','\t',\n\
currLine = line.strip().split()
#新建列表存储每个样本的特征向量
lineArr = []
#遍历每个样本的特征
#range(21)是0-20
for i in range(21):
#讲这个样本的特征存入lineArr列表
lineArr.append(float(currLine(i)))
#讲这个样本的标签存入标签列表
trainingLabels.append(currLine[21])
#将这个样本的特征向量添加到数据集列表
trainingSet.append(lineArr)
#调动随机梯度上升发更新logistics回归的权值参数
trainWeights = stocGradAscent1(trainingSet, trainingLabels, 500)
#统计测试数据集预测错误样本数量和样本总数
errorCount = 0 ; numTestVec = 0.0
#遍历测试数据集的每个样本
for line in frTest.readlines():
#样本总数加1
numTestVec += 1.0
#对当前行进行处理,分割出各个特征以及样本标签
currLine = line.strip().split()
#新建特征向量
lineArr = []
#将各个特征构成特征向量
for i in range(21):
lineArr.append(float(currLine[i]))
#利用分类预测函数对这个样本进行预测,并且与样本标签比较
if(clasifyVector(lineArr, trainWeights) != currLine[21]):
errorCount +=1
#计算测试机总的预测错误率
errorRate = float(errorCount)/numTestVec
#打印
print('error is : %f'%'errorRate')
return errorRate
#多次测试算法求平均值
def multTest():
#设计为10次
numTest = 10 ; errorRateSum = 0.0
#每一次测试算法并统计错误率
for k in range(numTest):
errorRateSum += colicTest()
#打印10次平均值
print('after %d iterations the average error rate is: %f''%(numTests,errorRateSum/float(numTests)')
'''
这样,经过10次的迭代,平均误差为35%,显然这跟30%的数据缺失有一定的关系,当然,如果我们调整合适的步长和迭代次数,相信错误率会有所下降。
四,小结
logistic回归的目的是寻找一个非线性函数sigmoid的最佳拟合参数,从而来相对准确的预测分类结果。为了找出最佳的函数拟合参数,最常用的优化算法为梯度上升法,当然我们为了节省计算损耗,通常选择随机梯度上升法来迭代更新拟合参数。并且,随机梯度上升法是一种在线学习算法,它可以在新数据到来时完成参数的更新,而不需要重新读取整个数据集来进行批处理运算。
总的来说,logistic回归算法,其具有计算代价不高,易于理解和实现等优点;此外,logistic回归算法容易出现欠拟合,以及分类精度不太高的缺点。
'''