参与拿奖:本文已参与「新人创作礼」活动,一起开启掘金创作之路
1.概念
回归:用一条直线对数据点进行拟合(该线称为最佳拟合直线),拟合过程就是回归
基于逻辑回归和sigmoid函数的分类
sigmoid函数:s(z)=1/(1+e^{-z})
sigmoid函数随着z值得增大逼近于1,而随着z值减小逼近于0,像一个阶跃函数
可以通过在每个特征上乘一个回归系数,然后将所有得结果相加,
将总和带入sigmoid函数,可以得到一个[0,1]之间得数值,大于0.归为一类,剩下的归为另一类
最优化算法:梯度上升法
算法思想:找到某函数得最大值,最好办法是沿着函数梯度方向寻找,则f(x,y)梯度为分别对x,y求偏导后的值,所形成的矩阵
梯度上升算法上升到每一个点都会重新估计移动的方向,每次移动时,梯度算子总是指向数值增长最快的地方
梯度迭代公式:w=w0+a*df:df表示该函数f在w0情况下的梯度,a表示移动量的大小,即步长
梯度上升算法用于求解最大值,梯度下降用于求解最小值
2.一个简单案例代码
2.1 testSet数据内容
2.2 代码
from numpy import *
##### 简单代码实现
##### 文件内容,每行三列数据,分别为特征值0,1和标签2
def loadDataSet():
dataMat=[]
labelMat=[]
fr=open(r'testSet.txt','r+')
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
# sigmoid函数
def sigmoid(inX):
return 1.0/(1.0+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))
for k in range(maxCycles):
# h为一个列向量(m,1)
h=sigmoid(dataMatrix*weights)
# 计算类别与预测类别的误差
error=(labelMat-h)
# 调整回归系数
weights=weights+alpha*dataMatrix.transpose()*error
return weights
dataSet,dataLabels=loadDataSet()
weights=gradAscent(dataSet,dataLabels)
# 分析数据:画出决策边界
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='r',marker='s')
ax.scatter(xcord2,ycord2,s=30,c='g')
# 得到最佳拟合直线
# 此处设置了sigmoid函数为0
# 0=sum(wx for w,x in [(w1,x1),(w2,x2),(w3,x3)])
x=arange(-3.0,3.0,0.1)
y=(-weights[0]-weights[1]*x)/weights[2]
# print(shape(x),shape(y))
ax.plot(x,y.T)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
plotBestFit(weights)
# 训练算法:随机梯度上升
### 梯度上升算法在每次更新回归系数都需要遍历整个数据集
### 在处理大量数据集时可以仅用一个样本点来更新回归系数,随机梯度上升算法
### 可以在新样本到来时对分类器进行增量式更新,其为在线学习算法,一次处理所有数据叫批处理
### 随机梯度上升算法
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
# print(weights+error*mat(dataMatrix[i]))
weights=weights+alpha*error*array(dataMatrix[i])
return weights
weights=stocGradAscent0(dataSet,dataLabels)
plotBestFit(weights)
# 判断一个优化算法的优劣的可靠办法是看其是否收敛,也就是参数是否达到了稳定值
# 改进随机梯度算法
# 第三个参数为迭代次数
def stocGradAscent1(dataMatrix, classLabels,numIter=40):
m,n = shape(dataMatrix)
alpha = 0.4
weights = ones(n) #initialize to all ones
weightsHistory=zeros((40*m,n))
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
# alpha每次迭代时都进行调整,会随迭代次数增大而减小,但永远大于0
# j表示迭代次数,i表示样本点下标,即第i个选出来的样本
# 当j远小于max(i)时,alpha就不是严格下降的
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
#print(error)
weights = weights + alpha * error * array(dataMatrix[randIndex])
weightsHistory[j*m + i,:] = weights
del(dataIndex[randIndex])
# print(weights)
return weights,weightsHistory
weights,weightsHistory=stocGradAscent1(dataSet,dataLabels)
plotBestFit(weights)
2.3 运行结果
3.使用图示方法展示梯度方向
3.1 运行效果
3.2 代码
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
leafNode = dict(boxstyle="round4", fc="0.8")
arrow_args = dict(arrowstyle="<-")
matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'
delta = 0.025
x = np.arange(-2.0, 2.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = -((X-1)**2)
Z2 = -(Y**2)
#Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
#Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 1.0 * (Z2 + Z1)+5.0
# Create a simple contour plot with labels using default colors. The
# inline argument to clabel will control whether the labels are draw
# over the line segments of the contour, removing the lines beneath
# the label
plt.figure()
CS = plt.contour(X, Y, Z)
plt.annotate('', xy=(0.05, 0.05), xycoords='axes fraction',
xytext=(0.2,0.2), textcoords='axes fraction',
va="center", ha="center", bbox=leafNode, arrowprops=arrow_args )
plt.text(-1.9, -1.8, 'P0')
plt.annotate('', xy=(0.2,0.2), xycoords='axes fraction',
xytext=(0.35,0.3), textcoords='axes fraction',
va="center", ha="center", bbox=leafNode, arrowprops=arrow_args )
plt.text(-1.35, -1.23, 'P1')
plt.annotate('', xy=(0.35,0.3), xycoords='axes fraction',
xytext=(0.45,0.35), textcoords='axes fraction',
va="center", ha="center", bbox=leafNode, arrowprops=arrow_args )
plt.text(-0.7, -0.8, 'P2')
plt.text(-0.3, -0.6, 'P3')
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Gradient Ascent')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
4.小结
逻辑回归是寻找一个非线性函数sigmoid的最佳拟合参数,求解算法可以使用最大化算法来完成。
最优化算法中,梯度上升算法比较常用,其又可以简化为随机梯度上升算法。
随机梯度算法较于梯度上升算法,占用更少计算资源,并且还是一个在线算法,可以在新数据到来时完成参数更新。
5.参考资料
[1] 机器学习实战
[2] 书籍源码
[3] jupyter版本