机器学习之逻辑回归算法

169 阅读5分钟
参与拿奖:本文已参与「新人创作礼」活动,一起开启掘金创作之路
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数据内容

image.png

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 运行结果

image.png

3.使用图示方法展示梯度方向

3.1 运行效果

image.png

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版本