Logistic回归是一种简单的分类算法,提到“回归”,很多人可能觉得与分类没什么关系,Logistic回归通过对数据分类边界的拟合来实现分类。而“回归”也就意味着最佳拟合。要进行最佳拟合,则需要寻找到最佳的拟合参数,一些最优化方法就可以用于最佳回归系数的确定。

Logistic回归

对书中代码做的修改:

  • 1.修改plotBestFit(wei)参数名称plotBestFit(weights)
  • 2.移除plotBestFit()中weights=wei.getA()
  • 3.分析数据画决策边界调用plotBestFit(weights)时传入weights.getA(),其中weight是gredAscent(dataArr,labelMat)方法的返回值

LogistiC 回归梯度上升优化算法

加载数据

from numpy import *
def loadDataSet():
    dataMat = []
    labelMat = []
    fr = open('F:\\study\\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

Sigmoid函数

"""
sigmoid函数
"""
def sigmoid(inX):
    return (1.0/(1+exp(-inX)))

LogistiC 梯度上升算法

def gredAscent(dataMatIn, classLabels):
    #[[x,x,x],...]
    dataMatrix = mat(dataMatIn)
    #将列表转换为矩阵再倒置
    labelMat = mat(classLabels).transpose()
    #获取行列
    m,n = shape(dataMatrix)
    #向目标移动的步长
    alpha = 0.001
    #迭代次数
    maxCycles = 500
    #回归系数 n=3行1列的单位数组
    weights = ones((n,1))
    for k in range(maxCycles):
        #h>0.5 在右边 dataMatrix*weights矩阵相乘100行一列
        #每次计算所有样本点
        h = sigmoid(dataMatrix*weights)
        error = (labelMat-h);
        #w = w + α▽w f(w)
        #dataMatrix.transpose() 3行100列  error 100行1列  相乘后3,1
        weights = weights + alpha * dataMatrix.transpose()*error
    return weights
  • 1.解析文本,文本中有100个样本点,前两列代表点的数值型特征X1,X2,最后一列为分类标签
  • 2.设置回归系数初始值为1(为单位矩阵,每个样本点对应一个回归系数,设置回归系数行数等于样本点的列数,实现倒置),步长为0.001,训练次数500
  • 3.计算z=wTx,带入sigmod获取结果,将结果与分类标签计算误差值
  • 4.根据误差值方向调整回归系数(梯度迭代公式,梯度简化为数据样本与错误量相乘[见上篇博客公式推导])
"""
查看效果
"""
dataArr,labelMat = loadDataSet()
weights = gredAscent(dataArr,labelMat)
"""
分析数据:画出决策边界
"""
import matplotlib.pyplot as plt
def plotBestFit(weights):
    dataMat,labelMat = loadDataSet()
    dataArr = array(dataMat)
    # n = 100
    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)
    # 画标签为1的点
    ax.scatter(xcord1, ycord1, s=30, c='red', marker='p')
    # 画标签为0的点
    ax.scatter(xcord2, ycord2, s=30, c='green')
    #创建决策边界线
    x = arange(-3.0, 3.0, 0.1)
    """
    设置sigmod函数值为0,0是两个分类的分界处,可解出x,y的关系式
    """
    # 0 = w0*x0 + w1*x + w2*y  =>  y = -(w0 + w1*x)/w2
    y = (-weights[0]-weights[1]*x)/weights[2] #最佳拟合线
    ax.plot(x,y)
    plt.xlabel('X1');plt.ylabel('X2');
    plt.show()
"""
查看效果
"""
from numpy import *
# 传入返回的回归系数(书中有误,因此修改了plotBestFit的传参,也为了后面随机梯度可共用
#另外需要定义weights将gredAscent的值赋给weights)
%matplotlib inline
# getA()将自身矩阵转化为ndarray类型的变量,等价于asarray(self)
plotBestFit(weights.getA())

分析数据

随机梯度上升算法

"""
一次仅用一个样本点来更新回归系数
"""
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
"""
查看效果
"""
dataArr,labelMat = loadDataSet()
weights = stocGradAscent0(array(dataArr),labelMat)
plotBestFit(weights)

分析数据

改进的随机梯度上升算法

"""
改进的随机梯度上升算法
alpha在每次迭代的时候都会调整,随着迭代次数不断减小,但永远不会到0
每次从样本中随机取出一个样本更新回归系数,之后将改值从列表中删除,进行下次迭代
"""
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    m,n = shape(dataMatrix)
    weights = ones(n)
    for j in range(numIter):
        # 每个样本的编号[0,..,99]
        dataIndex = range(m)
        for i in range(m):
            """
            步长alpha每次迭代时需要调整,每次减少1/(i+j)
            档j<<max(i),alpha就不是严格下降的。
            避免参数严格下降也常见于模拟退火算法(SAA)等其他优化算法中
            """
            alpha = 4/(1.0+j+i)+0.01
            # numpy.random.uniform(low,high,size)在给定区间[0,100)内随机取样,注意是左闭右开
            # size输出样本数目,int或元组,缺省时输出一个值
            randIndex = int(random.uniform(0,len(dataIndex)))
            # 求和 即计算z = w0x0+w1*x1+w2*x2  随机选取样本计算h
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha * error * dataMatrix[randIndex]
            # 样本被使用后去除 在此次迭代内dataIndex长度每次减1
            del(dataIndex[randIndex])
    return weights
"""
查看效果
"""
dataArr,labelMat = loadDataSet()
weights = stocGradAscent1(array(dataArr),labelMat)
plotBestFit(weights)

分析数据

从疝气病症预测病马的死亡率

处理数据中的缺失值

可选的做法

使用可用特征的均值来填补缺失值;
使用特殊值来填补缺失值,如-1;
忽略有缺失值的样本;
使用相似样本的均值添补缺失值;
使用另外的机器学习算法预测缺失值。

numpy不允许包含缺失值,选择实数0来替换所有缺失值,恰好能适用于Logistic回归
我们需要的是一个在更新是不会影响系数的值,根据回归系数的更新公式
w = w + a e dataMatrix[randIndex],若dataMatrix的某特征对应值为0,则系数不做更新w=w,另外sigmoid(0)=0.5,对结果的预测不具有任何的倾向性

Logistic回归分类函数

def classifyVector(inX, weights):
    prob = sigmoid(sum(inX*weights))
    if prob > 0.5: return 1.0
    else: return 0.0
def colicTest():
    frTrain = open('F:\study\horseColicTraining.txt')
    frTest = open('F:\study\horseColicTest.txt')
    trainingSet = []; trainingLabels = []
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[21]))
    trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 500)
    errorCount = 0; numTestVec = 0.0
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        if int(classifyVector(array(lineArr), trainWeights)) != int(currLine[21]):
            errorCount += 1
    errorRate = (float(errorCount)/numTestVec)
    print "the error rate of this test is: %f" % errorRate
    return errorRate
def multiTest():
    numTests = 10; errorSum=0.0
    for k in range(numTests):
        errorSum += colicTest()
    print "after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests))
"""
运行时报D:\Anaconda2\lib\site-packages\ipykernel\__main__.py:5: 
RuntimeWarning: overflow encountered in exp警告
说明计算的数据结果溢出了,忽略也无妨,查过说可以调整sigmoid函数,
使用longfloat()来解决溢出,但没有解决,还在寻找解决办法。。
"""
multiTest()
D:\Anaconda2\lib\site-packages\ipykernel\__main__.py:5: RuntimeWarning: overflow encountered in exp


the error rate of this test is: 0.298507
the error rate of this test is: 0.402985
the error rate of this test is: 0.268657
the error rate of this test is: 0.298507
the error rate of this test is: 0.283582
the error rate of this test is: 0.402985
the error rate of this test is: 0.388060
the error rate of this test is: 0.328358
the error rate of this test is: 0.388060
the error rate of this test is: 0.417910
after 10 iterations the average error rate is: 0.347761

总结(来自书中)

Logistic回归的目的是寻找一个非线性函数sigmoid的最佳拟合参数,求解过程可以由最优化
算法来完成。在最优化算法中,最常用的就是梯度上升算法,而梯度上升算法又可以简化为随机
梯度上升算法。
随机梯度上升算法与梯度上升算法的效果相当,但占用更少的计算资源。此外,随机梯度上
升是一个在线算法,它可以在新数据到来时就完成参数更新,而不需要重新读取整个数据集来进
行批处理运算。
机器学习的一个重要问题就是如何处理缺失数据。这个问题没有标准答案,取决于实际应用
中的需求。现有一些解决方案,每种方案都各有优缺点。