回归是指将一对数据拟合为一条直线的过程,而Logistics回归则是将回归用于分类,其主要思想为:根据现有的数据对分类边界线建立回归公式,依次为依据进行分类,在这里最关键的一步是寻找最佳的拟合参数,这一步将会用到一些最优化的方法,如梯度上升等。
本文以二分类为例,说到二分类问题,我们首先想到的是0,1的问题,那么首先要找到一个函数,一个值为0或1的函数,这里想到了一个高等数学中遇到的单位阶跃函数:它的值只有0和1,但是这个函数存在0到1之间的跳跃,这个跳跃很难处理,庆幸的是,数学上还有另一个函数,和他有类似的性质,那就是Sigmoid函数,Sigmoid函数公式如下:
Sigmoid的输入记为z,z由以下公式得出:
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])])
labelMat.append(int(lineArr[2]))
return dataMat,labelMat
def sigmoid(inX): #sigmoid函数
return 1.0/(1+exp(-inX))
def gradAscent(dataMatIn,classLabels):#梯度上升
dataMatrix = mat(dataMatIn) #转换成矩阵
labelMat = mat(classLabels).transpose() #转置
m,n = shape(dataMatrix) #矩阵的维度
alpha = 0.001 #步长
maxCycles = 500 #循环次数
weights = ones((n,1)) #n*1的矩阵
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights) # m*1的矩阵
error = (labelMat - h) # m*1的矩阵
weights = weights + alpha * dataMatrix.transpose() * error
# n*m的矩阵乘以m*1的矩阵得到n*1的矩阵,然后以weights相加
return weights
dataArr,LabelMat = loadDataSet()
绘图代码部分:
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]
ax.plot(x,y)
plt.xlabel('X1');plt.ylabel('X2');
plt.show()
梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理100个左右的数据时尚可,但是当数据集超过一定的数量时(如有十亿样本和成千上万的特征时),那么该方法的计算复杂度就太高了。一种改进方法是依次仅才采用一个样本点来更新回归系数,该方法称为随机梯度上升法。是一种在线学习方法,即可以再新样本到来时对分类器进行增量式更新。
随机梯度上升算法代码如下:
def stocGradAscent0(dataMatrix,classLabels):#随机梯度上升
m,n = shape(dataMatrix)
alpha = 0.01
weights = 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和误差error都是向量,而随机梯度上升算法则是数值;第二随机梯度上升算法没有矩阵转换的过程,所有变量的数据类型都是NumPy数组,而梯度上升算法数据类型则是矩阵。
对该算法绘图如下:
可以看出在迭代次数少的情况下(随机梯度算法迭代了200次,而梯度上升迭代了500次),随机梯度上升算法的效果是不如梯度上升的
随机梯度上升算法对于3个不同的系数迭代200次,回归系数和迭代次数的关系如下:
可见回归系数的值存在周期性的波动,原因是存在一些不能正确分类的样本点,在迭代的时候会引发系数剧烈的改变,另外算法的收敛速度也较慢。
在这里对随机梯度上升算法进行了两处改进:一是时步长alpha在每次迭代的时候都调整一次,这样做会缓解波动;第二个地方是通过随机选取样本来更新回归系数。这样做将会减少周期性的波动,具体的实现代码如下:
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
m,n = shape(dataMatrix)
weights = ones(n)
for j in range(numIter):
dataIndex = list(range(m)) #python 3 改法
for i in range(m):
alpha = 4/(1.0+j+i)+0.01 #步长的计算公式
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]) #每次迭代都要删除随机选出的值
#按书上的写 python 3 在这里会报错
return weights
回归系数的变化如下:
可见改进算法的系数并没有出现周期性的波动,而且收敛速度也快了很多。
对结果绘图如下:
在算法精度上,迭代了150次的改进算法的精度和迭代500次的梯度上升算法相当,这个算法大大地减少了计算量。