机器学习实战-逻辑回归算法

关于逻辑回归的理解以及公式推导,可以看前面的文章
逻辑回归公式推导

逻辑回归

sigomoid function:

用梯度上升算法来求参数,求函数的最大值。

迭代步骤:

训练算法:求参数

主要步骤如下:

  • 将数据转化成矩阵
  • 求梯度
from numpy import *
def loadData(filename):
    '''
    读取文件
    '''
    datamat = []; labelmat = []
    with open(filename) as f:
        for line in f.readlines():
            line_arr = line.strip().split()
            #有1.0是因为有一个常数项
            datamat.append([1.0, float(line_arr[0]), float(line_arr[1])])
            labelmat.append(int(line_arr[2]))
    return datamat, labelmat
datamat,labelmat = loadData('testSet.txt')
def sigmoid(inX):
    '''
    sigmoid函数
    -inX:-wT·x
    '''
    return 1.0/(1+exp(-inX))
def gradAscent(dataMatIn,classLabels):
    '''
    梯度上升算法
    dataMatIn:训练样本
    classLabels:标签
    '''
    dataMatrix = mat(dataMatIn)
    labelMat = mat(classLabels).transpose()
    m,n = shape(dataMatrix)
    alpha = 0.001
    maxCycles = 500
    #weights are parameters' vector
    weights = ones((n,1))
    for k in range(maxCycles):
        h = sigmoid(dataMatrix*weights)
        error = (labelMat - h)
        #推导过程如下:
        weights = weights +alpha*dataMatrix.transpose()*error
    return weights

推导:因为代价函数的梯度已知:

weights = gradAscent(datamat,labelmat)
import matplotlib
import matplotlib.pyplot as plt
from numpy import *

def plot_fit(data, labelMat, weights):
    '''
    画图,得到分界线
    data:训练样本
    labelMat:标签矩阵
    '''
    dataArr = array(data)
    n = shape(dataArr)[0]
    
    x_cord1 = []; y_cord1 = []
    x_cord2 = []; y_cord2 = []
    for i in range(n):  
        if int(labelMat[i]) == 1:  
            x_cord1.append(dataArr[i,1]); y_cord1.append(dataArr[i,2])  
        else: x_cord2.append(dataArr[i,1]); y_cord2.append(dataArr[i,2])  
    
    fig = plt.figure()
    ax = fig.add_subplot(111)  
    ax.scatter(x_cord1, y_cord1, s = 30, c = 'red', marker='s')  
    ax.scatter(x_cord2, y_cord2, s = 30, c = 'green')  
    
    x = arange(-3.0, 3.0, 0.1)  
    y = ((-weights[0]- weights[1] * x)/weights[2]).transpose()
    ax.plot(x, y)  
    plt.xlabel('X1');  
    plt.ylabel('X2');  
    plt.show()  
plot_fit(datamat,labelmat,weights)
机器学习实战-逻辑回归算法_第1张图片
image.png

梯度下降法分类效果还可以,100个样本分类错误4个,但是计算量太大了,计算了300次乘法,需要进一步改进算法。

随机梯度上升

梯度上升每次更新回归系数,都需要遍历整个数据集,一旦特征数量很大,计算复杂度太高了,改进方法叫随机梯度上升算法,每次选择一个样本点来更新回归系数

def stocGradAscent0(dataMatrix,classLabels):
    '''
    随机梯度上升算法
    dataMatrix:训练样本
    calssLabels:标签
    output:参数值
    '''
    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*array(dataMatrix[I])
    return weights
weight2 = stocGradAscent0(datamat,labelmat)

推导过程:

plot_fit(datamat,labelmat,weight2)
机器学习实战-逻辑回归算法_第2张图片
image.png

结果显示分类效果比较差,是因为迭代次数不确定导致参数还未达到稳定。可以对上述代码进行进一步修改

def stocGradAscent1(dataMatrix,classLabels,numIter = 150):
    '''
    改进的随机梯度上升算法
    '''
    m,n = shape(dataMatrix)
    weights = ones(n)
    #迭代次数
    for j in range(numIter):
        dataIndex = range(m)
        #样本数量
        for i in range(m):
            #每次调整a,why?
            alpha = 0.01+4/(1.0+j+i)
            #uniform-->生成[x,y)之间的随机实数
            randIndex = int(random.uniform(0,len(dataIndex)))
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] -h
            weights = weights + alpha*error*array(dataMatrix[randIndex])
    return weights
'''
1.不明白为什么每次需要调整alpha
2.随机选择样本可以减少周期波动
'''
weights3 = stocGradAscent1(datamat,labelmat,10)
plot_fit(datamat,labelmat,weights3)
机器学习实战-逻辑回归算法_第3张图片
image.png

疝气病预测病马死亡率

用医院检测疝气病的指标,来预测病马的死亡率

数据预处理:缺失值处理

  • 用特征均值填补
  • 用特殊值填补,比如-1
  • 忽略这部分样本
  • 用类似样本均值填补
  • 用机器学习算法来预测

具体做法:

  1. 选一个实数值替换,选0
  2. 标签缺失,需要丢弃数据

数据来源

def getDateSet(filepath):
    '''
    获得训练集和测试集
    '''
    import numpy as np
    
    with open(filepath) as f:
        file = f.readlines()
        arr = []
        dataSet = [];Labels=[]#训练集和标签集
        for x in file:
            mid = x.strip().split('\t')
            vector = list(map(lambda x:float(x),mid))
            arr.append(vector)
            #用numpy提取数据集以及标签集,加一个x0项
            mid2 = np.array(arr)
        x0 = array([1]*len(file))
        dataSet = np.column_stack((x0,mid2[:,:-1]))
        Labels =mid2[:,-1] 
    return dataSet,Labels
trainingdataSet,trainingLabels = getDateSet('./horseColicTraining.txt')
testdataSet,testLabels = getDateSet('./horseColicTest.txt')
stocGradAscent1(trainingdataSet,trainingLabels,500)
array([ 2.88523241e+00,  4.60371241e+01, -3.44351209e+00,  1.81776542e+00,
       -1.22312262e+00, -4.51162382e-02, -1.26815180e+01, -5.19715412e+00,
       -1.62249176e+01,  1.69815852e+00, -1.42154178e+01,  2.51022948e+01,
       -1.01675395e+01,  3.62858014e+01, -5.73351142e+00, -7.52889830e+00,
        8.52370700e+00, -9.93327494e+00, -1.10860304e+00,  1.43608141e+00,
       -2.06483905e+00, -5.98332914e+00])
#得到参数
weights = stocGradAscent1(trainingdataSet,trainingLabels,500)
#测试,计算w^tx
result = mat(testdataSet)*mat(weights).T
#sigmoid参数z,是-w^Tx的计算结果,sig
z = result.tolist()
sig = list(map(lambda x:sigmoid(x[0]),result.tolist()))

def classifyVector(x):
    '''
    sigmoid函数计算结果
    '''
    if x >0.5:
        return 1.0
    else:
        return 0.0
#逻辑回归的预测结果
lg_result = [classifyVector(x) for x in sig]
#标签集转列表
testLabels.tolist()
#将预测结果和真实结果zip打包成元组n
zipped = zip(lg_result,testLabels.tolist())
n = list(zipped)
#计算分类错误率
errorRate = len([x for x in n if x[0]-x[1]!=0])/len(n)

上述代码合并如下:

def errorTest():
    '''
    错误率计算函数
    weights:逻辑回归模型参数
    '''
    #数据集准备
    trainingdataSet,trainingLabels = getDateSet('./horseColicTraining.txt')
    testdataSet,testLabels = getDateSet('./horseColicTest.txt')
    
    #得到参数
    weights = stocGradAscent1(trainingdataSet,trainingLabels,10)
    #测试,计算w^tx
    result = mat(testdataSet)*mat(weights).T
    
    #sigmoid参数z,是-w^Tx的计算结果,sig
    z = result.tolist()
    sig = list(map(lambda x:sigmoid(x[0]),result.tolist()))

    #逻辑回归的预测结果
    lg_result = [classifyVector(x) for x in sig]
    #标签集转列表
    testLabels.tolist()
    #将预测结果和真实结果zip打包成元组n
    zipped = zip(lg_result,testLabels.tolist())
    n = list(zipped)
    #计算分类错误率
    errorRate = len([x for x in n if x[0]-x[1]!=0])/len(n)
    print('错误率:%f'%errorRate)
    return errorRate
errorTest()
错误率:0.283582
0.2835820895522388
def multiTest():
    numTests = 10;errorSum=0.0
    for k in range(numTests):
        errorSum += errorTest()
    print('经过10次迭代后平均错误率:%f'%(float(errorSum/numTests)))
multiTest()
错误率:0.268657
错误率:0.492537
错误率:0.328358
错误率:0.253731
错误率:0.268657
错误率:0.417910
错误率:0.358209
错误率:0.567164
错误率:0.283582
错误率:0.328358
经过10次迭代后平均错误率:0.356716

代码整理

def sigmoid(inX):
    '''
    sigmoid函数
    -inX:-wT·x
    '''
    return 1.0/(1+exp(-inX))

def stocGradAscent1(dataMatrix,classLabels,numIter = 150):
    '''
    改进的随机梯度上升算法
    '''
    m,n = shape(dataMatrix)
    weights = ones(n)
    #迭代次数
    for j in range(numIter):
        dataIndex = range(m)
        #样本数量
        for i in range(m):
            #每次调整a,why?
            alpha = 0.01+4/(1.0+j+i)
            #uniform-->生成[x,y)之间的随机实数
            randIndex = int(random.uniform(0,len(dataIndex)))
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] -h
            weights = weights + alpha*error*array(dataMatrix[randIndex])
    return weights

def getDateSet(filepath):
    '''
    获得训练集和测试集
    '''
    import numpy as np
    
    with open(filepath) as f:
        file = f.readlines()
        arr = []
        dataSet = [];Labels=[]#训练集和标签集
        for x in file:
            mid = x.strip().split('\t')
            vector = list(map(lambda x:float(x),mid))
            arr.append(vector)
            #用numpy提取数据集以及标签集,加一个x0项
            mid2 = np.array(arr)
        x0 = array([1]*len(file))
        dataSet = np.column_stack((x0,mid2[:,:-1]))
        Labels =mid2[:,-1] 
    return dataSet,Labels


def classifyVector(x):
    '''
    sigmoid函数计算结果分类
    '''
    if x >0.5:
        return 1.0
    else:
        return 0.0
    
    
if __name__ =="__main__":
    multiTest()
错误率:0.313433
错误率:0.283582
错误率:0.462687
错误率:0.626866
错误率:0.328358
错误率:0.268657
错误率:0.343284
错误率:0.328358
错误率:0.268657
错误率:0.298507
经过10次迭代后平均错误率:0.352239

小结

学习了逻辑回归算法,愈发感受到只看一本书效果并不好,而且《机器学习实战》一书缺陷明显,代码陈旧复杂,推导过程简陋,属于名副其实的入门书籍。有时候照着书上的代码操作结果就是出不来,令人非常恼火。好了,关于这一章的内容,有以下几个注意事项:

对于逻辑回归模型的本质:是一个回归模型嵌套在sigmoid函数中,模型借助其阶跃的特点,对样本进行二分类,其问题核心在于求解模型中的参数;从概率的角度来看,由于结果只有0,1,我们认为在拥有数据集的前提下,这些数据往往被取到的概率较大,而概率大小取决于使其取到这些值的参数决定,所以选择某个参数,使得取到数据集的概率最大,这种参数估计方法叫最大似然估计法。关于参数估计,可以使用梯度下降法进行。

随着学习的深入,更加觉得机器学习并非易事,从数据清洗-特征工程-模型选择-算法优化,这些都包含着高深的学问,此外,还需要有一定的数学基础以及编程能力。不禁感慨一句:

路漫漫其修远兮,吾将上下而求索。

你可能感兴趣的