《Machine Learning in Action》| 第5章 Logistic回归

绘制数据集图像
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
"""
@函数说明:加载数据集
"""
def loadDataSet():
    dataMat = [] # 创建数据列表
    labelMat = [] # 创建标签列表  
    fr = open(r'D:\dataset\inaction\logistic\testSet.txt') # 打开文件
    for line in fr.readlines(): # 逐行读取
        #print(line) # 如-0.017612       14.053064       0
        lineArr = line.strip().split() # 按照空格划分成单个数据并返回一个列表
        #print(lineArr) # 如['-0.017612', '14.053064', '0']
        # 为了方便计算,将 X0 的值设为 1.0 ,也就是在每一行的开头添加一个 1.0 作为 X0
        # 这里增加一维元素为1,主要是为了截距项b
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) # 添加数据
        labelMat.append(int(lineArr[2])) # 添加标签
        #print(dataMat) # 数据列表,如[[1.0, -0.017612, 14.053064], [1.0, -1.395634, 4.662541],...,[1.0, -0.196949, 0.444165]]
        #print(labelMat) # 标签列表,如[0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1]
    return dataMat, labelMat
"""
@函数说明:绘制数据集图像
"""
def plotDataSet():
    dataMat,labelMat = loadDataSet() # 加载数据集
    dataArr = np.array(dataMat) # 转换成numpy的array数组,以取出样本值dataArr[i,1],dataArr[i,2]
    n = np.shape(dataMat)[0] # 数据个数
    xcode1 = [];ycode1 = [] # 正样本
    xcode2 = [];ycode2 = [] # 负样本
    for i in range(n):
        if int(labelMat[i]) == 1:
            xcode1.append(dataArr[i,1]);ycode1.append(dataArr[i,2]) # 1为正样本
        else:
            xcode2.append(dataArr[i,1]);ycode2.append(dataArr[i,2]) # 0为负样本
    fig = plt.figure() # 绘制一个figure对象,生成空的figure窗口
    # 添加subplot子图进行绘图,空figure不能绘图
    ax = fig.add_subplot(111) # 1*1第一个,又如(2,2,3)2*2的第三个
    ax.scatter(xcode1, ycode1, s=20, c='red', marker='s', alpha=.5, label='class 1') # 绘制正样本
    ax.scatter(xcode2, ycode2, s=20, c='green', marker='*', alpha=.5, label='class 0') # 绘制正样本
    plt.legend()#使label生效
    plt.title('DataSet')
    plt.xlabel('x1');plt.ylabel('x2')
    plt.show()

执行代码,运行效果
这里写图片描述

训练算法:使用梯度上升找到最佳参数

  • 梯度上升法的伪代码如下:
    每个回归系数初始化为 1
    重复 R 次:
    ————计算整个数据集的梯度
    ————使用 alpha × gradient 更新回归系数的向量
    返回回归系数
程序清单5-1: Logistic回归梯度上升优化算法

"""
@函数说明:sigmoid函数(S形函数,用作二分类)
"""
def sigmoid(inX):
    return 1.0 / (1+np.exp(-inX))

"""
@函数说明:全批量梯度上升算法
""" 
def gradAscent(dataMatIn, classLabels):
    dataMatrix = np.mat(dataMatIn) # 转换成numpy的mat矩阵100*3,做矩阵运算
    labelMat = np.mat(classLabels).transpose() # 转换成numpy的mat矩阵100*1,并进行转置1*100
    m, n = np.shape(dataMatrix) # 返回dataMatrix的大小100*3。m为行数,n为列数。
    #print(m,n) # 100行3列
    alpha = 0.001 # 移动步长,也就是学习速率,控制更新的幅度。
    maxCycles = 500 # 最大迭代次数
    weights = np.ones((n,1))
    for k in range(maxCycles):
        h = sigmoid(dataMatrix * weights) # 梯度上升矢量化公式
        error = labelMat - h
        weights = weights + alpha * dataMatrix.transpose() * error
#    全批量梯度上升回归系数:
#    [[ 4.12414349]
#     [ 0.48007329]
#     [-0.6168482 ]]
    return weights.getA() # 将矩阵matrix转换为数组array(),返回权重数组,只有数组才能取出weights[0],weights[1],weights[2]

求出最优参数
全批量梯度上升回归系数:
[[ 4.12414349]
[ 0.48007329]
[-0.6168482 ]]

分析数据:画出决策边界

程序清单5-2 :画出数据集和Logistic回归最佳拟合直线的函数
"""
@函数说明:绘制数据集图像,画出决策边界
"""
def plotBestFit(weights):
    dataMat, labelMat = loadDataSet()
    dataArr = np.array(dataMat)
    n = np.shape(dataArr)[0]
    # 将标签为1的数据元素和为0的分别放在(xcode1,ycode1)、(xcord2,ycord2)
    xcode1 = [];ycode1 = [] # 正样本横纵坐标
    xcode2 = [];ycode2 = [] # 负样本横纵坐标
    for i in range(n):
        if int(labelMat[i]) == 1: # 1为真样本
            xcode1.append(dataArr[i,1]);ycode1.append(dataArr[i,2])
        else: # 0为负样本
            xcode2.append(dataArr[i,1]);ycode2.append(dataArr[i,2])
    # 绘制数据集图像
    fig = plt.figure()
    ax = fig.add_subplot(111) # 添加subplot子图
    ax.scatter(xcode1, ycode1, s=30, c='red', marker='s', alpha=.5, label='class 1')
    ax.scatter(xcode2, ycode2, s=30, c='green', alpha=.5, label='class 0')
    # 最佳拟合直线,绘制出w0 + w1*x + w2*y = 0的边界直线
    x = np.arange(-3.0, 3.0, 0.1)
    y = (-weights[0] - weights[1]*x)/weights[2]
    ax.plot(x, y)
    plt.legend()#使label生效
    plt.title('BestFit')
    # x,y轴显示的文字
    plt.xlabel('x1');plt.ylabel('x2')
    plt.show()

执行代码,运行效果如下:
这里写图片描述

训练算法:随机梯度上升

  • 随机梯度上升算法的伪代码如下:
    所有回归系数初始化为 1
    对数据集中每个样本
    ————计算该样本的梯度
    ————使用 alpha × gradient 更新回归系数值
    返回回归系数值

梯度上升法每次更新回归系数都需要便利整个数据集,计算复杂度高,改进的方法一次只用一个样本更新回归系数

程序清单5-3:随机梯度上升算法
"""
@函数说明:随机梯度上升算法:只使用一个样本点来更新回归系数
#与梯度上升代码类似,区别:1.h和error不是向量,全为数值;2.没有矩阵转换过程,所有变量的数据类型全是numpy数组
"""
def stocGradAscent0(dataMatrix, classLabels): # 参数dataMatrix是numpy数组类型数据,传入矩阵,需要np.array(matrix)转换一下
    m,n = np.shape(dataMatrix) # m为样本数100,n为特征数3
    alpha = 0.01 # 步长,学习速率
    weights = np.ones(n) # 初始化回归系数,一维数组[1,1,1]
    for i in range(m): # 遍历整个样本,一次只用一个样本
        # sum(data_mat[i]*weights)为了求 f(x)的值, f(x)=a1*x1+b2*x2+..+nn*xn,
        # 此处求出的 h 是一个具体的数值,而不是一个矩阵
        h = sigmoid(sum(dataMatrix[i]*weights)) # 如:dataMatrix[0]: [1.0, -0.017612, 14.053064]*[1. 1. 1.]
        # error是一个数,dataMatrix[i]的维度是(3,)
        error = classLabels[i] - h
        # 每个样本计算完差值就更新weights
        weights = weights + alpha * error * dataMatrix[i]
#随机梯度上升回归系数:
# [ 1.01702007  0.85914348 -0.36579921]
    return weights

求出最优参数
随机梯度上升回归系数:
[ 1.01702007 0.85914348 -0.36579921]

用随机梯度上升算法求出的回归系数拟合的决策边界,如下:
这里写图片描述

程序清单5-4:改进的随机梯度上升算法

改进的地方有两处
一、alpha在每次迭代是都会调整,alpha随着每次迭代都会减小,但永远不会减小到0,保证多次迭代之后新数据仍然有一定影响力
二、随机选取样本更新回归系数

"""
@函数说明:改进的随机梯度上升算法
#1. alpha在每次迭代的时候都会调整,并且,虽然alpha会随着迭代次数不断减小,但永远不会减小到0
#2. 通过随机选取样本来更新回归系数,改进算法还增加了一个迭代次数作为第三参数
""" 
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    m,n = np.shape(dataMatrix) # m为样本数100,n为特征数3
    weights = np.ones(n) # 参数初始化,一维数组[1,1,1]
    for j in range(numIter):
        dataIndex = list(range(m)) # 这里必须要用list,不然后面的del没法使用
        for i in range(m): 
            # alpha每次迭代时需要调整,缓解数据波动或者高频振动
            # i和j的不断增大,导致alpha的值不断减少,但是不为0,保证多次迭代之后新数据仍然具有一定的影响
            # alpha大,学习的快,越接近结果,降低学习率
            alpha = 4/(1.0 + j + i)+0.01 # 降低alpha的大小,每次减小1/(j+i)
            # 随机产生一个 0~len()之间的一个值
            # random.uniform(x, y) 方法将随机生成下一个实数,它在[x,y]范围内,x是这个范围内的最小值,y是这个范围内的最大值。
            # 随机选取样本来更新回归系数
            randIndex = int(np.random.uniform(0, len(dataIndex))) # 随机选取样本
            h = sigmoid(sum(dataMatrix[randIndex]*weights)) # 选择随机选取的一个样本,计算h
            error = classLabels[randIndex] - h # 计算误差
            weights = weights + alpha * error * dataMatrix[randIndex] # 更新回归系数
            del(dataIndex[randIndex]) # 删除已经使用的样本
#改进梯度上升回归系数:
# [14.53305126  0.70520079 -1.91569198]
    return weights

求出最优参数
改进的随机梯度上升回归系数:
[14.53305126 0.70520079 -1.91569198]

用随机梯度上升算法求出的回归系数拟合的决策边界,如下:
这里写图片描述

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

使用 Logistic 回归估计马疝病的死亡率

  • 收集数据:给定数据文件。
  • 准备数据:用 Python 解析文本文件并填充缺失值。
  • 分析数据:可视化并观察数据。
  • 训练算法:使用优化算法,找到最佳的系数。
  • 测试算法:为了量化回归的效果,需要观察错误率。根据错误率决定是否回退到训练阶段,通过改变迭代的次数和步长等参数来得到更好的回归系数。
  • 使用算法:实现一个简单的命令行程序来收集马的症状并输出预测结果。

使用 Logistic 回归方法进行分类所需做的是把测试集上每个特征向量乘以最优化方法得来的回归系数,再将该乘积结果求和,最后输入到 sigmoid 函数即可。如果对应的 sigmoid 值大于 0.5 就预测类别标签为 1,否则为 0。

程序清单5-5:Logistic回归分类函数
# ------------------------从疝气病症预测病马的死亡率---------------------------
"""
@函数说明:Logistic回归分类函数:根据特征向量和回归系数来计算 Sigmoid 的值,大于0.5函数返回类别1,否则返回类别0
Parameters:
    inX - 特征向量
    weights - 根据梯度下降/改进随机梯度下降计算得到的回归系数
Returns:
    分类结果
"""
def classifyVector(inX, weights):
    prob = sigmoid(sum(inX * weights))
    if prob > 0.5:
        return 1.0
    else:
        return 0.0

"""
#@函数说明:Logistic回归分类器(改进的随机梯度上升求最优参数)进行预测
# 打开测试集和训练集(患疝病的马的存活问题),使用测试集进行500迭代的Logistic回归,
# 计算出回归参数,并根据测试集,得出训练模型的错误率
"""
def colicTest0():
    frTrain = open(r'D:\dataset\inaction\logistic\horseColicTraining.txt') # 打开训练集
    frTest = open(r'D:\dataset\inaction\logistic\horseColicTest.txt') # 打开测试集
    # trainingSet 中存储训练数据集的特征,trainingLabels 存储训练数据集的样本对应的类别标签
    trainingSet = []; trainingLabels = [] 
    for line in frTrain.readlines(): # 逐行读取
        currLine = line.strip().split('\t') # 以制表符划分并存入列表
#        print(np.shape(currLine)) #(22,) 共22列,前21列特征,['2.000000',...,'0.000000']
#        print(len(currLine)) # 长度22
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
#        lineArr = [float(currLine[i]) for i in range(len(currLine)-1)] # 列表生成器一行搞定
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[-1])) # 训练标签列表,-1倒数第一个
    trainweights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500) # 使用改进的随机梯度上升算法进行训练
#    print(trainweights) #<class 'numpy.ndarray'>21条特征,有21个回归系数,[ 22.61292494 ... -4.92743622]
    errorCount = 0; numTestVec = 0.0 # 分类错误的样本条数,样本总条数
    # 读取测试集进行测试,计算分类错误的样本条数和最终的错误率
    for line in frTest.readlines():
        numTestVec += 1.0 # 总条数计数 
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
#        # 优化算法得到的分类函数标签与读取的测试集的类别标签做对比,如1 1,1 1,0 1,0 0
#        print('%d %d'%(int(classifyVector(np.array(lineArr), trainweights)) ,int(currLine[-1])))
        if int(classifyVector(np.array(lineArr), trainweights)) != int(currLine[-1]):
            errorCount += 1 # 分类错误条数计数
#    print(numTestVec) # 测试样本共67条实例
    errorRate = (float(errorCount)/numTestVec) * 100 # 计算错误率
    print("改进的随机梯度上升求最优参数测试集错误率为: %.2f%%"%errorRate)
    return errorRate # 返回float类型的错误率
    

"""
@函数说明:Logistic回归分类器(梯度上升求最优参数)进行预测
"""
def colicTest1():          
    frTrain = open(r'D:\dataset\inaction\logistic\horseColicTraining.txt') # 打开训练集
    frTest = open(r'D:\dataset\inaction\logistic\horseColicTest.txt') # 打开测试集
    trainingSet = []; trainingLabels = []
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[-1]))
    trainWeights = gradAscent(np.array(trainingSet), trainingLabels) # 使用梯度上升算法进行训练
#    print(trainWeights) #21*1向量[[  7.35249885],...,[ -4.19707172]]
#    print(np.shape(trainWeights)) #(21, 1)21行1列ndarray数组
#    print(trainWeights[:,0]) # 切片,取第一维的所有元素组成一维数组,21个元素[  7.35249885   ,...,   -4.19707172]
#    print(np.shape(trainWeights[:,0])) #(21,) 一维数组
    errorCount = 0; numTestVec = 0.0
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        # trainWeights[:,0]把21*1的ndarray数组转化成一维数组,等价于np.mat(trainWeights).getA1()【getA1()把矩阵转化成一维数组】
        if int(classifyVector(np.array(lineArr), np.mat(trainWeights).getA1())) != int(currLine[-1]):
            errorCount += 1
    errorRate = (float(errorCount)/numTestVec) * 100 # 计算错误率
    print("梯度上升求最优参数测试集错误率为: %.2f%%"%errorRate)

"""
@函数说明:调用colicTest0()函数10次并求结果的平均值
"""
def multiTest():
    numtTests = 10; errorSum = 0.0
    for k in range(numtTests):
        errorSum += colicTest0()
    print("迭代%d次的平均错误率是:%.2f%%" % (numtTests, errorSum/float(numtTests)))

if __name__ == '__main__': 
    multiTest()
    colicTest1()

执行代码,运行结果如下:
这里写图片描述

使用sklearn库构建的Logistic回归分类器对病马的死亡率进行预测

"""
@函数说明:使用sklearn库中的LogisticRegression算法对病马存活率进行预测
"""
def colicSklearn():
    frTrain = open(r'D:\dataset\inaction\logistic\horseColicTraining.txt') # 打开训练集
    frTest = open(r'D:\dataset\inaction\logistic\horseColicTest.txt') # 打开测试集
    trainingSet = []; trainingLabels = []
    testSet = [];testLabels = []
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[-1]))
    for line in frTest.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        testSet.append(lineArr)
        testLabels.append(float(currLine[-1]))
    # 创建LogisticRegression模型
    # solver='liblinear':优化算法选择参数,选择逻辑回归损失函数的优化方法
    # liblinear:使用了开源的liblinear库实现,内部使用了坐标轴下降法来迭代优化损失函数
    # max_iter=10:算法收敛最大迭代次数
    classifier = LogisticRegression(solver='liblinear', max_iter=10)
    classifier.fit(trainingSet, trainingLabels) # 训练模型
    test_accurcy = classifier.score(testSet,testLabels) 
    print("sklearn中Logistic回归分类器求最优参数测试集错误率为:%f%%" %((1-test_accurcy)*100))
    print("正确率:%f%%" % (test_accurcy*100))

执行代码,运行结果及与梯度上升改进的随机梯度上升优化方法比较结果:

这里写图片描述

LogisticRegression模型一共有14个参数,默认配置:

这里写图片描述

具体参数说明:点此链接

参数表格如下:

这里写图片描述
这里写图片描述
这里写图片描述
附所有代码:

# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
"""
@函数说明:加载数据集
"""
def loadDataSet():
    dataMat = [] # 创建数据列表
    labelMat = [] # 创建标签列表  
    fr = open(r'D:\dataset\inaction\logistic\testSet.txt') # 打开文件
    for line in fr.readlines(): # 逐行读取
        #print(line) # 如-0.017612       14.053064       0
        lineArr = line.strip().split() # 按照空格划分成单个数据并返回一个列表
        #print(lineArr) # 如['-0.017612', '14.053064', '0']
        # 为了方便计算,将 X0 的值设为 1.0 ,也就是在每一行的开头添加一个 1.0 作为 X0
        # 这里增加一维元素为1,主要是为了截距项b
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) # 添加数据
        labelMat.append(int(lineArr[2])) # 添加标签
        #print(dataMat) # 数据列表,如[[1.0, -0.017612, 14.053064], [1.0, -1.395634, 4.662541],...,[1.0, -0.196949, 0.444165]]
        #print(labelMat) # 标签列表,如[0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1]
    return dataMat, labelMat
"""
@函数说明:绘制数据集图像
"""
def plotDataSet():
    dataMat,labelMat = loadDataSet() # 加载数据集
    dataArr = np.array(dataMat) # 转换成numpy的array数组,以取出样本值dataArr[i,1],dataArr[i,2]
    n = np.shape(dataMat)[0] # 数据个数
    xcode1 = [];ycode1 = [] # 正样本
    xcode2 = [];ycode2 = [] # 负样本
    for i in range(n):
        if int(labelMat[i]) == 1:
            xcode1.append(dataArr[i,1]);ycode1.append(dataArr[i,2]) # 1为正样本
        else:
            xcode2.append(dataArr[i,1]);ycode2.append(dataArr[i,2]) # 0为负样本
    fig = plt.figure() # 绘制一个figure对象,生成空的figure窗口
    # 添加subplot子图进行绘图,空figure不能绘图
    ax = fig.add_subplot(111) # 1*1第一个,又如(2,2,3)2*2的第三个
    ax.scatter(xcode1, ycode1, s=20, c='red', marker='s', alpha=.5, label='class 1') # 绘制正样本
    ax.scatter(xcode2, ycode2, s=20, c='green', marker='*', alpha=.5, label='class 0') # 绘制正样本
    plt.legend()#使label生效
    plt.title('DataSet')
    plt.xlabel('x1');plt.ylabel('x2')
    plt.show()

"""
@函数说明:sigmoid函数(S形函数,用作二分类)
"""
def sigmoid(inX):
    return 1.0 / (1+np.exp(-inX))

"""
@函数说明:梯度上升算法
""" 
def gradAscent(dataMatIn, classLabels):
    dataMatrix = np.mat(dataMatIn) # 转换成numpy的mat矩阵100*3,做矩阵运算
    labelMat = np.mat(classLabels).transpose() # 转换成numpy的mat矩阵100*1,并进行转置1*100
    m, n = np.shape(dataMatrix) # 返回dataMatrix的大小100*3。m为行数,n为列数。
    #print(m,n) # 100行3列
    alpha = 0.001 # 移动步长,也就是学习速率,控制更新的幅度。
    maxCycles = 500 # 最大迭代次数
    weights = np.ones((n,1))
    for k in range(maxCycles):
        h = sigmoid(dataMatrix * weights) # 梯度上升矢量化公式
        error = labelMat - h
        weights = weights + alpha * dataMatrix.transpose() * error
#    全批量梯度上升回归系数:
#    [[ 4.12414349]
#     [ 0.48007329]
#     [-0.6168482 ]]
    return weights.getA() # 将矩阵matrix转换为数组array(),返回权重数组,只有数组才能取出weights[0],weights[1],weights[2]

"""
@函数说明:绘制数据集图像,画出决策边界
"""
def plotBestFit(weights):
    dataMat, labelMat = loadDataSet()
    dataArr = np.array(dataMat)
    n = np.shape(dataArr)[0]
    xcode1 = [];ycode1 = [] # 正样本横纵坐标
    xcode2 = [];ycode2 = [] # 负样本横纵坐标
    for i in range(n):
        if int(labelMat[i]) == 1: # 1为真样本
            xcode1.append(dataArr[i,1]);ycode1.append(dataArr[i,2])
        else: # 0为负样本
            xcode2.append(dataArr[i,1]);ycode2.append(dataArr[i,2])
    # 绘制数据集图像
    fig = plt.figure()
    ax = fig.add_subplot(111) # 添加subplot子图
    ax.scatter(xcode1, ycode1, s=30, c='red', marker='s', alpha=.5, label='class 1')
    ax.scatter(xcode2, ycode2, s=30, c='green', alpha=.5, label='class 0')
    # 最佳拟合直线
    x = np.arange(-3.0, 3.0, 0.1)
    y = (-weights[0] - weights[1]*x)/weights[2]
    ax.plot(x, y)
    plt.legend()#使label生效
    plt.title('BestFit')
    plt.xlabel('x1');plt.ylabel('x2')
    plt.show()
  
"""
@函数说明:随机梯度上升算法:只使用一个样本点来更新回归系数
#与梯度上升代码类似,区别:1.h和error不是向量,全为数值;2.没有矩阵转换过程,所有变量的数据类型全是numpy数组
"""
def stocGradAscent0(dataMatrix, classLabels):
    m,n = np.shape(dataMatrix) # m为样本数100,n为特征数3
    alpha = 0.01 # 步长,学习速率
    weights = np.ones(n) # 初始化回归系数,一维数组[1,1,1]
    for i in range(m): # 遍历整个样本,一次只用一个样本
        # sum(data_mat[i]*weights)为了求 f(x)的值, f(x)=a1*x1+b2*x2+..+nn*xn,
        # 此处求出的 h 是一个具体的数值,而不是一个矩阵
        h = sigmoid(sum(dataMatrix[i]*weights)) # 如:dataMatrix[0]: [1.0, -0.017612, 14.053064]*[1. 1. 1.]
        # error是一个数,dataMatrix[i]的维度是(3,)
        error = classLabels[i] - h
        # 每个样本计算完差值就更新weights
        weights = weights + alpha * error * dataMatrix[i]
#随机梯度上升回归系数:
# [ 1.01702007  0.85914348 -0.36579921]
    return weights

"""
@函数说明:改进的随机梯度上升算法
#1. alpha在每次迭代的时候都会调整,并且,虽然alpha会随着迭代次数不断减小,但永远不会减小到0
#2. 通过随机选取样本来更新回归系数,改进算法还增加了一个迭代次数作为第三参数
""" 
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    m,n = np.shape(dataMatrix) # m为样本数100,n为特征数3
    weights = np.ones(n) # 参数初始化,一维数组[1,1,1]
    for j in range(numIter):
        dataIndex = list(range(m)) # 这里必须要用list,不然后面的del没法使用
        for i in range(m): 
            # i和j的不断增大,导致alpha的值不断减少,但是不为0,保证多次迭代之后新数据仍然具有一定的影响
            # alpha大,学习的快,越接近结果,降低学习率
            alpha = 4/(1.0 + j + i)+0.01 # 降低alpha的大小,每次减小1/(j+i)
            # 随机产生一个 0~len()之间的一个值
            # random.uniform(x, y) 方法将随机生成下一个实数,它在[x,y]范围内,x是这个范围内的最小值,y是这个范围内的最大值。
            # 随机选取样本来更新回归系数
            randIndex = int(np.random.uniform(0, len(dataIndex))) # 随机选取样本
            h = sigmoid(sum(dataMatrix[randIndex]*weights)) # 选择随机选取的一个样本,计算h
            error = classLabels[randIndex] - h # 计算误差
            weights = weights + alpha * error * dataMatrix[randIndex] # 更新回归系数
            del(dataIndex[randIndex]) # 删除已经使用的样本
#改进梯度上升回归系数:
# [14.53305126  0.70520079 -1.91569198]
    return weights

"""
@函数说明:测试函数,测试各个算法并拟合直线
"""
def test():
    dataMat, labelMat = loadDataSet()    
    print('全批量梯度上升回归系数:\n',gradAscent(dataMat, labelMat))
    weights = gradAscent(dataMat, labelMat)
    plotBestFit(weights)
    print('随机梯度上升回归系数:\n',stocGradAscent0(np.array(dataMat), labelMat))
    weights = stocGradAscent0(np.array(dataMat), labelMat)
    plotBestFit(weights)
    print('改进梯度上升回归系数:\n',stocGradAscent1(np.array(dataMat), labelMat))
    weights = stocGradAscent1(np.array(dataMat), labelMat)
    plotBestFit(weights)
    
# ------------------------从疝气病症预测病马的死亡率---------------------------
"""
@函数说明:Logistic回归分类函数:根据特征向量和回归系数来计算 Sigmoid 的值,大于0.5函数返回类别1,否则返回类别0
Parameters:
    inX - 特征向量
    weights - 根据梯度下降/改进随机梯度下降计算得到的回归系数
Returns:
    分类结果
"""
def classifyVector(inX, weights):
    prob = sigmoid(sum(inX * weights))
    if prob > 0.5:
        return 1.0
    else:
        return 0.0

"""
@函数说明:Logistic回归分类器(改进的随机梯度上升求最优参数)进行预测
"""
def colicTest0():
    frTrain = open(r'D:\dataset\inaction\logistic\horseColicTraining.txt') # 打开训练集
    frTest = open(r'D:\dataset\inaction\logistic\horseColicTest.txt') # 打开测试集
    # trainingSet 中存储训练数据集的特征,trainingLabels 存储训练数据集的样本对应的类别标签
    trainingSet = []; trainingLabels = [] 
    for line in frTrain.readlines(): # 逐行读取
        currLine = line.strip().split('\t') # 以制表符划分并存入列表
#        print(np.shape(currLine)) #(22,) 共22列,前21列特征,['2.000000',...,'0.000000']
#        print(len(currLine)) # 长度22
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
#        lineArr = [float(currLine[i]) for i in range(len(currLine)-1)] # 列表生成器一行搞定
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[-1])) # 训练标签列表,-1倒数第一个
    trainweights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500) # 使用改进的随机梯度上升算法进行训练
#    print(trainweights) #<class 'numpy.ndarray'>21条特征,有21个回归系数,[ 22.61292494 ... -4.92743622]
    errorCount = 0; numTestVec = 0.0 # 分类错误的样本条数,样本总条数
    # 读取测试集进行测试,计算分类错误的样本条数和最终的错误率
    for line in frTest.readlines():
        numTestVec += 1.0 # 总条数计数 
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
#        # 优化算法得到的分类函数标签与读取的测试集的类别标签做对比,如1 1,1 1,0 1,0 0
#        print('%d %d'%(int(classifyVector(np.array(lineArr), trainweights)) ,int(currLine[-1])))
        if int(classifyVector(np.array(lineArr), trainweights)) != int(currLine[-1]):
            errorCount += 1 # 分类错误条数计数
#    print(numTestVec) # 测试样本共67条实例
    errorRate = (float(errorCount)/numTestVec) * 100 # 计算错误率
    print("改进的随机梯度上升求最优参数测试集错误率为: %.2f%%"%errorRate)
    return errorRate # 返回float类型的错误率
    

"""
@函数说明:Logistic回归分类器(梯度上升求最优参数)进行预测
"""
def colicTest1():          
    frTrain = open(r'D:\dataset\inaction\logistic\horseColicTraining.txt') # 打开训练集
    frTest = open(r'D:\dataset\inaction\logistic\horseColicTest.txt') # 打开测试集
    trainingSet = []; trainingLabels = []
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[-1]))
    trainWeights = gradAscent(np.array(trainingSet), trainingLabels) # 使用梯度上升算法进行训练
#    print(trainWeights) #21*1向量[[  7.35249885],...,[ -4.19707172]]
#    print(np.shape(trainWeights)) #(21, 1)21行1列ndarray数组
#    print(trainWeights[:,0]) # 切片,取第一维的所有元素组成一维数组,21个元素[  7.35249885   ,...,   -4.19707172]
#    print(np.shape(trainWeights[:,0])) #(21,) 一维数组
    errorCount = 0; numTestVec = 0.0
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        # trainWeights[:,0]把21*1的ndarray数组转化成一维数组,等价于np.mat(trainWeights).getA1()【getA1()把矩阵转化成一维数组】
        if int(classifyVector(np.array(lineArr), np.mat(trainWeights).getA1())) != int(currLine[-1]):
            errorCount += 1
    errorRate = (float(errorCount)/numTestVec) * 100 # 计算错误率
    print("梯度上升求最优参数测试集错误率为: %.2f%%"%errorRate)

"""
@函数说明:调用colicTest0()函数10次并求结果的平均值
"""
def multiTest():
    numtTests = 10; errorSum = 0.0
    for k in range(numtTests):
        errorSum += colicTest0()
    print("改进的随机梯度上升求最优参数测试集迭代%d次的平均错误率是:%.2f%%" % (numtTests, errorSum/float(numtTests)))
    
# ------------------------使用Sklearn构建Logistic回归分类器---------------------
"""
@函数说明:使用sklearn库中的LogisticRegression算法对病马存活率进行预测
"""
def colicSklearn():
    frTrain = open(r'D:\dataset\inaction\logistic\horseColicTraining.txt') # 打开训练集
    frTest = open(r'D:\dataset\inaction\logistic\horseColicTest.txt') # 打开测试集
    trainingSet = []; trainingLabels = []
    testSet = [];testLabels = []
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[-1]))
    for line in frTest.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        testSet.append(lineArr)
        testLabels.append(float(currLine[-1]))
    # 创建LogisticRegression模型
    # solver='liblinear':优化算法选择参数,选择逻辑回归损失函数的优化方法
    # liblinear:使用了开源的liblinear库实现,内部使用了坐标轴下降法来迭代优化损失函数
    # max_iter=10:算法收敛最大迭代次数
    classifier = LogisticRegression(solver='liblinear', max_iter=10)
    classifier.fit(trainingSet, trainingLabels) # 训练模型
    test_accurcy = classifier.score(testSet,testLabels) 
    print("sklearn中Logistic回归分类器求最优参数测试集错误率为:%f%%" %((1-test_accurcy)*100))
    print("正确率:%f%%" % (test_accurcy*100))
    
#----------------------------主函数--------------- 
if __name__ == '__main__': 
    test() # 优化算法测试
    multiTest() # 测试改进的随机梯度上升求最优参数测试集错误率
    colicTest1() # 测试梯度上升求最优参数测试集错误率
    colicSklearn()

附执行结果截图:
这里写图片描述
这里写图片描述

参考链接:
logistic回归预测病马死亡率

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值