《人工智能》机器学习 - 第5章 逻辑回归(四 多元逻辑回归实战)

199 阅读7分钟

5.4多元逻辑回归实战

5.4.1多元逻辑回归实战之预测病马的死亡率

本次实战内容,将使用Logistic回归来预测患疝气病的马的存活问题。原始数据集下载地址:
archive.ics.uci.edu/ml/datasets…

这里的数据包含368个样本和28个特征。我并非育马专家,从一些文献中了解到,疝病是描述马胃肠痛的术语。然而,这种病不一定源自马的胃肠问题,其他问题也可能引发马疝病。该数据集中包含了医院检测马疝病的一些指标,有的指标比较主观,有的指标难以测量,例如马的疼痛级别。

另外需要说明的是,除了部分指标主观和难以测量外,该数据还存在一个问题,数据集中有30%的值是缺失的。下面将首先介绍如何处理数据集中的数据缺失问题,然后再利用Logistic回归和随机梯度上升算法来预测病马的生死。

数据中的缺失值是个非常棘手的问题,有很多文献都致力于解决这个问题。那么,数据缺失究竟带来了什么问题?假设有100个样本和20个特征,这些数据都是机器收集回来的。若机器上;的某个传感器损坏导致一个特征无效时该怎么办?此时是否要扔掉整个数据?这种情况下,另外19个特征怎么办?它们是否还可用?答案是肯定的。因为有时候数据相当昂贵,扔掉和重新获取都是不可取的,所以必须采用一些方法来解决这个问题。

下面给出了一些可选的做法:
 使用可用特征的均值来填补缺失值;
 使用特殊值来填补缺失值,如1;
 忽略有缺失值的样本;
 使用相似样本的均值添补缺失值;
 使用另外的机器学习算法预测缺失值。

原始的数据集经过处理,保存为两个文件:horseColicTest.txt和horseColicTraining.txt。参见实例。现在我们有一个“干净”可用的数据集和一个不错的优化算法,下面将把这些部分融合在一起训练出一个分类器,然后利用该分类器来预测病马的生死问题。

有了这些数据集,我们只需要一个Logistic分类器,就可以利用该分类器来预测病马的生死问题了。看代码吧。

# -*- coding:utf-8 -*-
import numpy as np
import time
import matplotlib.pyplot as plt

import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['font.family'] = 'sans-serif'
matplotlib.rcParams['axes.unicode_minus'] = False

"""
函数说明:读取数据

Parameters:
    filename - 文件名
Returns:
    dataSet_Arr - 数据集
    Labels_Arr - 标签
"""
def loadDataSet_old(filename):
        
    fr = open(filename)		#打开训练集

    dataSet = []
    Labels = []
    for line in fr.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        
        dataSet.append(lineArr)
        Labels.append(float(currLine[-1]))
    
    # 将X,Y列表转化成矩阵
    dataSet_Arr = np.array(dataSet)
    Labels_Arr = np.array(Labels)
    
    return dataSet_Arr,Labels_Arr

"""
函数说明:读取数据

Parameters:
    filename - 文件名
Returns:
    xArr - x数据集
    yArr - y数据集
"""
def loadDataSet(filename):
    X = []
    Y = []
    
    with open(filename, 'rb') as f:
        for idx, line in enumerate(f):
            line = line.decode('utf-8').strip()
            if not line:
                continue
            eles = line.split()
            eles = list(map(float, eles))
            
            if idx == 0:
                numFea = len(eles)
            #去掉每行的最后一个数据再放入X中
            X.append(eles[:-1])
            #获取每行的租后一个数据
            Y.append([eles[-1]])
     
    # 将X,Y列表转化成矩阵
    xArr = np.array(X)
    yArr = np.array(Y)
    
    return xArr,yArr

"""
函数说明:定义sigmoid函数

Parameters:
    z
Returns:
    返回
"""
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

"""
函数说明:定义代价函数

Parameters:
    theta, X, Y, theLambda
Returns:
    返回
"""
def J(theta, X, Y, theLambda=0):
    m, n = X.shape
    h = sigmoid(np.dot(X, theta))
    J = (-1.0/m)*(np.dot(np.log(h).T,Y)+np.dot(np.log(1-h).T,1-Y)) + (theLambda/(2.0*m))*np.sum(np.square(theta[1:]))
    if np.isnan(J[0]):
        return np.inf
    # 其实J里面只有一个数值,需要取出该数值
    return J.flatten()[0]

"""
函数说明:梯度上升求解参数

Parameters:
    X, Y, options
Returns:
    返回参数
"""
def gradient(X, Y, options):
    '''
    options.alpha 学习率
    options.theLambda 正则参数λ
    options.maxLoop 最大迭代轮次
    options.epsilon 判断收敛的阈值
    options.method
        - 'sgd' 随机梯度上升
        - 'bgd' 批量梯度上升
    '''
    
    m, n = X.shape
    
    # 初始化模型参数,n个特征对应n个参数
    theta = np.zeros((n,1))
    
    cost = J(theta, X, Y)  # 当前误差
    costs = [cost]
    thetas = [theta]
    
    # Python 字典dict.get(key, default=None)函数返回指定键的值,如果值不在字典中返回默认值
    alpha = options.get('alpha', 0.01)
    epsilon = options.get('epsilon', 0.00001)
    maxloop = options.get('maxloop', 1000)
    theLambda = float(options.get('theLambda', 0)) # 后面有 theLambda/m 的计算,如果这里不转成float,后面这个就全是0
    method = options.get('method', 'bgd')
    
    # 定义随机梯度上升
    def _sgd(theta):
        count = 0
        converged = False
        while count < maxloop:
            if converged :
                break
            # 随机梯度上升,每一个样本都更新
            for i in range(m):
                h =sigmoid(np.dot(X[i].reshape((1,n)), theta))
                
                theta = theta + alpha*((1.0/m)*X[i].reshape((n,1))*(Y[i]-h) + (theLambda/m)*np.r_[[[0]], theta[1:]])
                
                thetas.append(theta)
                cost = J(theta, X, Y, theLambda)
                costs.append(cost)
                if abs(costs[-1] - costs[-2]) < epsilon:
                    converged = True
                    break
            count += 1
        return thetas, costs, count
    
    # 定义批量梯度上升
    def _bgd(theta):
        count = 0
        converged = False
        while count < maxloop:
            if converged :
                break
            
            h = sigmoid(np.dot(X, theta))
            theta = theta + alpha*((1.0/m)*np.dot(X.T, (Y-h)) + (theLambda/m)*np.r_[[[0]],theta[1:]])
            
            thetas.append(theta)
            cost = J(theta, X, Y, theLambda)
            costs.append(cost)
            if abs(costs[-1] - costs[-2]) < epsilon:
                converged = True
                break
            count += 1
        return thetas, costs, count
    
    methods = {'sgd': _sgd, 'bgd': _bgd}
    return methods[method](theta)  

"""
函数说明:绘制代价函数图像

Parameters:
    costs
Returns:
    返回参数
"""
def plotCost(costs):
    # 绘制误差曲线
    plt.figure(figsize=(8,4))
    plt.plot(range(len(costs)), costs)
    plt.xlabel(u'迭代次数')
    plt.ylabel(u'代价J')    

"""
函数说明:绘制参数曲线

Parameters:
    thetass - 参数
Returns:
    返回参数
"""
def plotthetas(thetas):
    # 绘制参数theta变化
    thetasFig, ax = plt.subplots(len(thetas[0]))
    thetas = np.asarray(thetas)
    for idx, sp in enumerate(ax):
        thetaList = thetas[:, idx]
        sp.plot(range(len(thetaList)), thetaList)
        sp.set_xlabel('Number of iteration')
        sp.set_ylabel(r'$\theta_%d$'%idx)

"""
函数说明:分类函数

Parameters:
	inX - 特征向量
	weights - 回归系数
Returns:
	分类结果
"""
def classifyVector(inX, weights):
    
    prob = sigmoid(sum(inX*weights))
    
    if prob > 0.5: 
        return 1.0
    else: 
        return 0.0

"""
函数说明:测试数据

Parameters:
    Weights - 回归系数
    dataSet - 数据集
    Labels - 标签
Returns:
    predict - 预测结果
    errorRate - 错误率
"""
def test(Weights,dataSet,Labels):
    
    errorCount = 0
    numTestVec = 0.0
    predict = []
    for i in range(len(test_Y)):
        numTestVec += 1.0
        result = classifyVector(dataSet[i], Weights)
        
        predict.append(result)
        
        if int(result)!= int(Labels[i]):
            errorCount += 1
    
    errorRate = (float(errorCount)/numTestVec) * 100 	#错误率计算
    
    return predict , errorRate


"""
函数说明:绘制预测值与真实值

Parameters:
    X_test,Y_test,y_predict
Returns:
    无
"""
def plotfit(X_test,Y_test,y_predict):
    #画图对预测值和实际值进行比较
    p_x = range(len(X_test))
    plt.figure(figsize=(18,4),facecolor="w")
    plt.ylim(0,6)
    plt.plot(p_x,Y_test,"ro",markersize=8,zorder=3,label=u"真实值")
    plt.plot(p_x,y_predict,"go",markersize=14,zorder=2,label=u"预测值")
    plt.legend(loc="upper left")
    plt.xlabel(u"编号",fontsize=12)
    plt.ylabel(u"类型",fontsize=12)
    plt.title(u"Logistic算法对数据进行分类",fontsize=12)
    #plt.savefig("Logistic算法对数据进行分类.png")
    plt.show()

if __name__ == '__main__':
    ## Step 1: load data...
    print("Step 1: load data...")

    origin_train_X, train_Y = loadDataSet('horseColicTraining.txt')
    origin_test_X, test_Y = loadDataSet('horseColicTest.txt')
    
    train_m,train_n = origin_train_X.shape
    test_m,test_n = origin_test_X.shape
    
    train_X = np.concatenate((np.ones((train_m,1)), origin_train_X), axis=1)
    test_X = np.concatenate((np.ones((test_m,1)), origin_test_X), axis=1)
    
    #print(train_X)
    #print(train_Y)
        
    ## Step 2: training sgd...
    print("Step 2: training sgd...")
    
    # sgd随机梯度上升
    options = {
        'alpha':0.5,
        'epsilon':0.00000001,
        'maxloop':100000,
        'method':'sgd' 
    }
    
    # 训练模型
    start = time.time()
    sgd_thetas, sgd_costs, sgd_iterationCount = gradient(train_X, train_Y, options)
    print("sgd_thetas:")
    print(sgd_thetas[-1])
    end = time.time()
    sgd_time = end - start
    print("sgd_time:"+str(sgd_time))
    
    ## Step 3: testing...
    print("Step 3: testing...")
    predict_Y , errorRate = test(sgd_thetas[-1].all(),test_X,test_Y)

    ## Step 4: show the result...
    print("Step 4: show the result...")
    print('预测值:',predict_Y)
    print("测试集错误率为: %.2f%%" % errorRate)
    
    ## Step 5: show the plotfit...
    print("Step 5: show the plotfit...")
    plotfit(test_X,test_Y,predict_Y)
    
    ## Step 6: show costs sgd...
    print("Step 6: show costs sgd...")
    plotCost(sgd_costs)

    ## Step 7: show thetas sgd...
    print("Step 7: show thetas sgd...")
    plotthetas(sgd_thetas)

结果如下所示。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
【完整代码参考3.Multi_Logistic_Regression_Classify\Multi_Logistic_Regression_Classify_v1\ Multi_Logistic_Regression_Classify_v1.0\Multi_Logistic_Regression_Classify_v1.0.py】

5.4.2多元逻辑回归实战之预测病马的死亡率调用sklearn

和前面一样还是要使用sklearn来实现的,直接看代码吧,注意和前文的例子比较。Python代码如下。

# @Language : Python3.6
"""
# -*- coding:UTF-8 -*-
import numpy as np
import random
import matplotlib.pyplot as plt
import matplotlib
import time 
from sklearn.linear_model import LogisticRegression

matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['font.family'] = 'sans-serif'
matplotlib.rcParams['axes.unicode_minus'] = False

"""
函数说明:读取数据

Parameters:
    filename - 文件名
Returns:
    dataSet_Arr - 数据集
    Labels_Arr - 标签
"""
def loadDataSet_old(filename):
        
    fr = open(filename)		#打开训练集

    dataSet = []
    Labels = []
    for line in fr.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        
        dataSet.append(lineArr)
        Labels.append(float(currLine[-1]))
    
    # 将X,Y列表转化成矩阵
    dataSet_Arr = np.array(dataSet)
    Labels_Arr = np.array(Labels)
    
    return dataSet_Arr,Labels_Arr

"""
函数说明:读取数据

Parameters:
    filename - 文件名
Returns:
    xArr - x数据集
    yArr - y数据集
"""
def loadDataSet(filename):
    X = []
    Y = []
    
    with open(filename, 'rb') as f:
        for idx, line in enumerate(f):
            line = line.decode('utf-8').strip()
            if not line:
                continue
            eles = line.split()
            eles = list(map(float, eles))
            
            if idx == 0:
                numFea = len(eles)
            #去掉每行的最后一个数据再放入X中
            X.append(eles[:-1])
            #获取每行的租后一个数据
            Y.append([eles[-1]])
     
    # 将X,Y列表转化成矩阵
    xArr = np.array(X)
    yArr = np.array(Y)
    
    return xArr,yArr

"""
函数说明:绘制预测值与真实值

Parameters:
    X_test,Y_test,y_predict
Returns:
    无
"""
def plotfit(X_test,Y_test,y_predict):
    #画图对预测值和实际值进行比较
    p_x = range(len(X_test))
    plt.figure(figsize=(18,4),facecolor="w")
    plt.ylim(0,6)
    plt.plot(p_x,Y_test,"ro",markersize=8,zorder=3,label=u"真实值")
    plt.plot(p_x,y_predict,"go",markersize=14,zorder=2,label=u"预测值")
    plt.legend(loc="upper left")
    plt.xlabel(u"编号",fontsize=12)
    plt.ylabel(u"类型",fontsize=12)
    plt.title(u"Logistic算法对数据进行分类",fontsize=12)
    #plt.savefig("Logistic算法对数据进行分类.png")
    plt.show()
    
if __name__ == '__main__':
    ## Step 1: load data...
    print("Step 1: load data...")
    
    origin_train_X, train_Y = loadDataSet('horseColicTraining.txt')
    origin_test_X, test_Y = loadDataSet('horseColicTest.txt')
    
    train_m,train_n = origin_train_X.shape
    test_m,test_n = origin_test_X.shape
    
    train_X = np.concatenate((np.ones((train_m,1)), origin_train_X), axis=1)
    test_X = np.concatenate((np.ones((test_m,1)), origin_test_X), axis=1)
    
    #print(train_X)
    #print(train_Y)
    
        
    ## Step 2: init lr...
    print("Step 2: init lr...")
    mlr = LogisticRegression(solver = 'sag',max_iter = 5000)
    
    
    ## Step 3: training...
    print("Step 3: training...")
 
    start = time.time()
    #使用改进的随即上升梯度训练
    #输出y做出ravel()转换。不然会有警告
    mlr.fit(train_X,train_Y.ravel())	
    
    end = time.time()
    
    mlr_time = end - start
    print('mlr_time:',mlr_time)
    
    ## Step 4: show weights...
    print("Step 4: show weights...")
    #模型效果获取
    r = mlr.score(train_X,train_Y)
    print("R值(准确率):",r)
    
    #print("参数:",mlr.coef_)
    #print("截距:",mlr.intercept_)
    w0 = np.array(mlr.intercept_)
    w1 = np.array(mlr.coef_.T[1:22])
    
    #合并为一个矩阵
    weights = np.vstack((w0,w1))
    #print('weights:')
    #print(weights)

    ## Step 5: predict ...
    print("Step 5: predict...")
    predict_Y = mlr.predict(test_X) 
    print('预测值:',predict_Y)

    ## Step 6: show the plotfit...
    print("Step 6: show the plotfit...")
    plotfit(test_X,test_Y,predict_Y)

结果如下所示。
在这里插入图片描述

在这里插入图片描述
【完整代码参考3.Multi_Logistic_Regression_Classify\Multi_Logistic_Regression_Classify-sklearn_v2\Multi_Logistic_Regression_Classify-sklearn_v2.0.py】

本章参考代码
点击进入