Logistic回归

本文代码摘自书籍《机器学习实战》,我仅稍加改正和整理,详细代码和数据集见GitHub

Logistic回归

优点:计算代价不高,易于理解和实现。
缺点:容易欠拟合,分类精度可能不高。

最佳回归系数的确定

算法思路

这里使用的梯度上升找到最佳参数。
伪代码如下:

每个回归系数初始化为1
重复R次:
	计算整个数据集的梯度
	使用alpha*gradient更新回归系数的向量
	返回回归系数

实际代码:

def loadDataSet():
    dataMat = []
    labelMat = []
    
    # 打开文本文件testSet并逐行读取
    # 每行前两个值分别是X1和X2,第三个值是对应的类别标签
    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

# sigmoid分类函数
def sigmoid(inX):
    return 1.0 / (1 + np.exp(-inX))


def gradAscent1(dataMatIn, classLabels):
    dataMatrix = np.mat(dataMatIn)
    labelMat = np.mat(classLabels).transpose()
    m, n = np.shape(dataMatrix)
    # 学习率设为0.001
    alpha = 0.001
    # 学习500次
    maxCycles = 500
    # w权重初始化为1
    weights = np.ones((n, 1))
    for k in range(maxCycles):
        h = sigmoid(dataMatrix * weights)
        error = labelMat - h
        weights = weights + alpha * dataMatrix.transpose() * error
    return weights

算法改进

梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理100个左右的数据集尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。一种改进方法是一次仅用一个样本点来更新回归系数,该方法称为随机梯度上升算法

def gradAscent2(dataMatrix, classLabels, numIter=150):
    m, n = np.shape(dataMatrix)
    weights = np.ones(n)
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
        	# alpha每次迭代时需要调整
            alpha = 4 / (1.0 + j + i) + 0.01
            
            # 随机选取一个样本更新
            randIndex = int(np.random.uniform(0, len(dataIndex)))
            
            h = sigmoid(sum(dataMatrix[randIndex] * weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha * error * dataMatrix[randIndex]
            
            # 选取后删除样本,避免重复选取
            del (dataIndex[randIndex])
            
        return weights

学习率alpha不同于之前,这里的alpha在每次迭代的时候都会调整,这会缓解求解时的波动,虽然alpha会随着迭代次数不断减小,但永远不会减小到0,因为存在一个常数项0.01。

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

准备数据

数据中的缺失值是个非常棘手的问题,一般的处理方法有:

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

本问将选择实数0来替换所有缺失值,主要原因有两点:

  1. 本文的回归系数的更新公式如下:weights = weights + alpha * error * dataMatrix[randIndex] 如果样本的某特征对应值为0,那么该特征的系数将不做更新,不会受到影响。
  2. 由于sigmoid(0) = 0.5,即它对结果的预测不具有任何倾向性,因此上述做法也不会对误差项造成任何影响。

因此,这样恰好能适用于Logistic回归。

书籍给的数据共分为horseColicTraining.txthorseColicTest.txt两个文本文件,文件中每行的前20个数字表示样本的特征,最后的数字是类标签。大概是这个样子:在这里插入图片描述

测试算法

上文我们已经给出了最主要的算法,目前还差分类函数和计算误差函数了,直接上代码。

# inX--样本,  weights --回归系数
def classifyVector(inX, weights):
    prob = sigmoid(sum(inX * weights))
	
	# 如果prob大于0.5,则认为马患病,否则认为健康
    if prob > 0.5:
        return 1.0
    else:
        return 0.0


def colicTest():

	# 整理处理数据
    frTrain = open('horseColicTraining.txt')
    frTest = open('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]))
        
	#训练    
    trainingWeights=gradAscent2(np.array(trainingSet),trainingLabels)
    
    #计算误差
    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]))
            
        # 如果预测标签与实际标签不一致,则错误次数加1
        if int(classifyVector(np.array(lineArr),trainingWeights)\
               !=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

	# 重复预测k次,取平均值
    for k in range(numTests):
        errorSum+=colicTest()
    print("after %d iterations the average error rate is: %f"\
          %(numTests,errorSum/float(numTests)))

运行结果如下:

the error rate of this test is :0.283582
the error rate of this test is :0.283582
the error rate of this test is :0.283582
the error rate of this test is :0.283582
the error rate of this test is :0.283582
the error rate of this test is :0.283582
the error rate of this test is :0.283582
the error rate of this test is :0.283582
the error rate of this test is :0.283582
the error rate of this test is :0.283582
after 10 iterations the average error rate is: 0.283582

从结果可以看到,10次迭代之后的平均错误率为28.4%。事实上,这个结果并不差,因为样本数据中有30%的数据缺失,如果调整gradAscent2()函数中的迭代次数,平均错误率可以降到20%左右。

总结

Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法来完成,最常见的算法就是梯度上升算法,而梯度上升算法又可以简化为随机梯度上升算法。

随机梯度上升算法与梯度上升算法的效果相对,但占用更少的计算资源。此外,随机梯度上升是一个在线算法,它可以在新数据到来时就完成参数更新,而不需要重新读取整个数据集来进行批处理运算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值