1. 实验数据

根据美国疾病控制预防中心的数据,现在美国1/7的成年人患有糖尿病。但是到2050年,这个比例将会快速增长至高达1/3。在UCL机器学习数据库里一个糖尿病数据集,通过这一数据集,建立一个数据分析模型实现对病人是否患病进行预测。
数据地址:https://github.com/susanli2016/Machine-Learning-with-Python/blob/master/diabetes.csv

数据集位768 × \times × 9的矩阵,其中前8列为数据特征(怀孕次数,血糖,血压,皮脂厚度,胰岛素,BMI身体质量指数,糖尿病遗传函数,年龄),最后一列为标签(结果),0意味着未患糖尿病,1意味着患有糖尿病。在768个样本钟,500个被标记为0,268个标记为1。数据可视化如下:

在这里插入图片描述

2. 实验展示

在本次实验中,我们打算比较多种经典机器学习模型。为方便比较,我们首先搭建统一的机器学习框架,建立统一的评价指标。下面我们将逐一介绍。

2.1 实验环境

本次实验我们将用到下个库(函数):

import numpy as np
import pandas as pd
from imblearn.over_sampling import SMOTE

from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, StratifiedKFold
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.metrics import precision_score, recall_score, roc_auc_score, average_precision_score, f1_score

2.2 加载数据

我们首先读入整个数据集,通过上述可是化结果我们可以观察到数据中共有8个属性,:

具体实现代码如下:

def LoadData():
    rawDataset = pd.read_csv('diabetes.txt')
    return rawDataset

我们统计算出数据的多种统计值来观察数据的分布,结果如下:

在这里插入图片描述

我们可以发现:

  • 数据中不存在缺失值
  • 数据的各个属性值均大于零,且均为连续值
  • 数据的各个属性分布较为不均衡,体现为不同属性的均值和标准差相差较大
  • 通过观察数据的同一个属性下的均值和方差,我们任务部分数据存在异常值
  • 样本正负比例不均衡,多数样本为负样本

因此,我们需要对数据进行预处理。

2.3 异常点去除

我们首先考虑 3 σ 3\sigma 3σ准则来去除异常点:

3 σ 3\sigma 3σ准则

借助正态分布的优良性质, 3 σ 3\sigma 3σ准则常用来判定数据是否异常。由于正态分布关于均值 μ \mu μ对称,数值分布在 ( μ − σ , μ + σ ) (μ-\sigma,μ+\sigma) (μσμ+σ)中的概率为0.6827,数值分布在 ( μ − 3 σ , μ + 3 σ ) (μ-3\sigma,μ+3\sigma) (μ3σμ+3σ)中的概率为0.9973。也就是说只有0.3%的数据会落在均值的 ± 3 σ ±3\sigma ±3σ之外,这是一个小概率事件,如下图所示:

在这里插入图片描述

但是正态分布的参数 μ \mu μ σ \sigma σ极易受到个别异常值的影响,从而影响判定的有效性。因此又产生了Tukey箱型图法。

IQR准则

上图中IQR,即四分位间距 Q 3 − Q 1 Q3-Q1 Q3Q1 ( Q 1 , Q 3 ) (Q1, Q3) (Q1,Q3)涵盖了数据分布最中间的50%的数据,具有稳健性。数据落在 ( Q 1 − 1.5 ∗ I Q R , Q 3 + 1.5 ∗ I Q R ) (Q1-1.5*IQR, Q3+1.5*IQR) (Q11.5IQR,Q3+1.5IQR) 范围内,则认为是正常值,在此范围之外的即为异常值。

在这里插入图片描述

具体实现代码如下:给定数据集,我们采用IQR准则剔除异常值:

def Preprocess(rawDataset, threshold):

    outlierIndices = []
    features = list(rawDataset)[:-1]

    for feature in features:

        Q1 = np.percentile(rawDataset[feature], 25)
        Q3 = np.percentile(rawDataset[feature],75)
        IQR = Q3 - Q1
        outlierStep = 1.5 * IQR
        
        outlierIndices.extend(rawDataset[(rawDataset[feature] < Q1 - outlierStep) 
        | (rawDataset[feature] > Q3 + outlierStep )].index)

    outlierIndices = Counter(outlierIndices)
    outlierIndices = list(k for k, v in outlierIndices.items() if v > threshold )

    rawDataset.drop(outlierIndices, axis = 0).reset_index(drop=True)
    dataset = rawDataset.values[:,:-1]
    label = rawDataset.values[:,-1]
 
    return dataset, label

注意:在上述代码中,首先求出了所有异常点的编号位置,而后对异常值进行统计,对超出阈值次数的异常值予以剔除。

2.4 数据标准化

给定数据的特征,我们考虑采用和正态尺度标准化和最小最大尺度标准化来对数据进行处理,不同的标准化策略通过关键字processKey传入,具体代码如下:

if processKey == 'standard':
	scaler = StandardScaler()
elif processKey == 'minMax':
    scaler = MinMaxScaler()

trainingData = scaler.fit_transform(trainingData)
validationData = scaler.transform(validationData)

注意:在数据标准化时,我们只能在训练集上进行拟合,在测试数据上进行直接转化

2.5 采样处理

针对于数据集正负样本不均衡的问题,我们采用SMOTE采样方法,对正样本进行上采样,由此获得均衡数据集:

smo = SMOTE(random_state=0)
trainingData, trainingLabel = smo.fit_resample(trainingData, trainingLabel)

注意:在上采样数据集之前,我们首先划分了训练集和测试集,然后仅仅对训练集的数据进行上采样。这样可以消除由于上采样和数据划分带来的数据依赖性,是我们的实验结果更加准确。

2.6 建立模型

我们使用sklearn库构建包括:逻辑回归、支持向量机、随机森林和多层感知机在内的多种模型,这里,为了方便调整参数和后续复现我们的代码,我们将随机种子random_state固定为0,具体代码如下:

def BuidModel(modelKey):
    if modelKey == 'svm':

        return SVC(probability=True, random_state=0)
    
    elif modelKey == 'rf':
        
        return RandomForestClassifier(n_estimators=512, random_state=0)

    elif modelKey == 'mlp':

        return MLPClassifier(max_iter=10000, random_state=0)
    
    elif modelKey == 'logistic':
        
        return LogisticRegression(random_state=0)

2.7 训练&测试模型

  • 我们模型和数据采用交叉验证框架方式对上述模型进行评估。

  • 我们采用精准率(precision),召回率(recall),受试者操作特征曲线下的面积(the area under the receiver operating characteristic curve),平均精度(average precision)(也就是PR曲线下方的面积AUPR)进行模型评估;

  • 在交叉验证实验的每一折中,我们首先对数据进行3.4部分和3.5部分中的预处理,然后在训练集上建立我们的模型,在验证集上测试我们的模型。

具体实现代码如下:

def TrainTestModel(dataset, label, processKey, modelKey, K):
    
    trainingPrecision = np.zeros(K)
    trainingRecall = np.zeros(K)
    traingAuc = np.zeros(K)
    traingAupr = np.zeros(K)
    traingF1 = np.zeros(K)

    validationPrecision = np.zeros(K)
    validationRecall = np.zeros(K)
    validationAuc = np.zeros(K)
    validationAupr = np.zeros(K)
    validationF1 = np.zeros(K)

    kFold = StratifiedKFold(n_splits=K)
    
    for (k, (trainingIndex, validationIndex)) in enumerate(kFold.split(dataset, label)):

        trainingData, validationData = dataset[trainingIndex], dataset[validationIndex]
        trainingLabel, validationLabel = label[trainingIndex], label[validationIndex]

        if processKey == 'standard':
            scaler = StandardScaler()
        elif processKey == 'minMax':
            scaler = MinMaxScaler()

        trainingData = scaler.fit_transform(trainingData)
        validationData = scaler.transform(validationData)
        smo = SMOTE(random_state=0)
        trainingData, trainingLabel = smo.fit_resample(trainingData, trainingLabel)

        model = BuidModel(modelKey)
        model.fit(trainingData, trainingLabel)

        traingingProbablity = model.predict_proba(trainingData)[:, 1]
        traingPredictLabel = model.predict(trainingData)

        validationProbablity = model.predict_proba(validationData)[:, 1]
        validationPredictLabel = model.predict(validationData)

        trainingPrecision[k] = precision_score(trainingLabel, traingPredictLabel)
        trainingRecall[k] = recall_score(trainingLabel, traingPredictLabel)
        traingAuc[k] = roc_auc_score(trainingLabel, traingingProbablity)
        traingAupr[k] = average_precision_score(trainingLabel, traingingProbablity)
        traingF1[k] = f1_score(trainingLabel, traingPredictLabel)

        print('*****************k={0}*****************'.format(k))
        print('Training Precision:', trainingPrecision[k])
        print('Training Recall:', trainingRecall[k])
        print('Training auc:', traingAuc[k])
        print('Training aupr:', traingAupr[k])
        print('Training f1:', traingF1[k])

        validationPrecision[k] = precision_score(validationLabel, validationPredictLabel)
        validationRecall[k] = recall_score(validationLabel, validationPredictLabel)
        validationAuc[k] = roc_auc_score(validationLabel, validationProbablity)
        validationAupr[k] = average_precision_score(validationLabel, validationProbablity)
        validationF1[k] = f1_score(validationLabel, validationPredictLabel)

        print('Validation Precision:', validationPrecision[k])
        print('Validation Recall:', validationRecall[k])
        print('Validation auc:', validationAuc[k])
        print('Validation aupr:', validationAupr[k])
        print('Validation f1:', validationF1[k])
    

    print('**********************************')
    print('**********************************')
    print('**********************************')
    print('Mean Training Precision:', trainingPrecision.mean())
    print('Mean Training Recall:', trainingRecall.mean())
    print('Mean Training auc:', traingAuc.mean())
    print('Mean Training aupr:', traingAupr.mean())
    print('Mean Training f1:', traingF1.mean())

    print('Mean Validation Precision:', validationPrecision.mean())
    print('Mean Validation Recall:', validationRecall.mean())
    print('Mean Validation auc:', validationAuc.mean())
    print('Mean Validation aupr:', validationAupr.mean())
    print('Mean Validation f1:', validationF1.mean())
    

3. 实验结果

3.1 样例展示

下面我们按照第3部分的框架实现一个简单的样例,流程如下所示:

  • 我们首先载入数据
  • 之后对数据进行预处理,我们选取离群点的阈值为1,这意味着任何存在两个异常属性及以上的记录将会被移除
  • 我们采取最小最大尺度对数据进行标准化
  • 我们选择10折交叉验证框架对模型进行评估

具体代码如下:

rawDataset = LoadData()
dataset, label = Preprocess(rawDataset, 1)
TrainTestModel(dataset, label, 'minMax', 'logistic', K=10)

我们运行上述寻优代码,得到以下结果:

*****************k=0*****************
Training Precision: 0.7788018433179723
Training Recall: 0.7511111111111111
Training auc: 0.8526370370370371
Training aupr: 0.8297431003127342
Training f1: 0.7647058823529411
Validation Precision: 0.5714285714285714
Validation Recall: 0.7407407407407407
Validation auc: 0.8037037037037037
Validation aupr: 0.7256850228837795
Validation f1: 0.6451612903225806
*****************k=1*****************
Training Precision: 0.7795454545454545
Training Recall: 0.7622222222222222
Training auc: 0.8499703703703704
Training aupr: 0.8290171420566803
Training f1: 0.7707865168539326
Validation Precision: 0.6923076923076923
Validation Recall: 0.6666666666666666
Validation auc: 0.8148148148148148
Validation aupr: 0.7587969155910737
Validation f1: 0.6792452830188679
*****************k=2*****************
Training Precision: 0.7741935483870968
Training Recall: 0.7466666666666667
Training auc: 0.8468345679012346
Training aupr: 0.8293515281952981
Training f1: 0.7601809954751131
Validation Precision: 0.6060606060606061
Validation Recall: 0.7407407407407407
Validation auc: 0.8466666666666667
Validation aupr: 0.725264491270496
Validation f1: 0.6666666666666666
*****************k=3*****************
Training Precision: 0.7780373831775701
Training Recall: 0.74
Training auc: 0.853130864197531
Training aupr: 0.8336999242021322
Training f1: 0.7585421412300682
Validation Precision: 0.5277777777777778
Validation Recall: 0.7037037037037037
Validation auc: 0.7540740740740741
Validation aupr: 0.6274968659420228
Validation f1: 0.6031746031746033
*****************k=4*****************
Training Precision: 0.7757009345794392
Training Recall: 0.7377777777777778
Training auc: 0.8498567901234567
Training aupr: 0.8269032603251051
Training f1: 0.7562642369020501
Validation Precision: 0.5714285714285714
Validation Recall: 0.5925925925925926
Validation auc: 0.7962962962962963
Validation aupr: 0.7197997266426064
Validation f1: 0.5818181818181818
*****************k=5*****************
Training Precision: 0.7638888888888888
Training Recall: 0.7333333333333333
Training auc: 0.8440345679012347
Training aupr: 0.8211510489938374
Training f1: 0.7482993197278911
Validation Precision: 0.6451612903225806
Validation Recall: 0.7407407407407407
Validation auc: 0.8548148148148148
Validation aupr: 0.7816674617757291
Validation f1: 0.689655172413793
*****************k=6*****************
Training Precision: 0.7517564402810304
Training Recall: 0.7133333333333334
Training auc: 0.8457728395061729
Training aupr: 0.8225843455688012
Training f1: 0.7320410490307868
Validation Precision: 0.6896551724137931
Validation Recall: 0.7407407407407407
Validation auc: 0.8088888888888888
Validation aupr: 0.6660335118185174
Validation f1: 0.7142857142857143
*****************k=7*****************
Training Precision: 0.7464788732394366
Training Recall: 0.7066666666666667
Training auc: 0.8370123456790124
Training aupr: 0.813932796528752
Training f1: 0.7260273972602739
Validation Precision: 0.7419354838709677
Validation Recall: 0.8518518518518519
Validation auc: 0.9059259259259259
Validation aupr: 0.8419001878854815
Validation f1: 0.7931034482758621
*****************k=8*****************
Training Precision: 0.7523364485981309
Training Recall: 0.7155555555555555
Training auc: 0.8405728395061729
Training aupr: 0.8209595861106735
Training f1: 0.733485193621868
Validation Precision: 0.6666666666666666
Validation Recall: 0.7692307692307693
Validation auc: 0.8546153846153846
Validation aupr: 0.6580292698414197
Validation f1: 0.7142857142857142
*****************k=9*****************
Training Precision: 0.7534562211981567
Training Recall: 0.7266666666666667
Training auc: 0.8400740740740741
Training aupr: 0.8161989532377201
Training f1: 0.7398190045248869
Validation Precision: 0.6
Validation Recall: 0.6923076923076923
Validation auc: 0.8346153846153846
Validation aupr: 0.731246489597415
Validation f1: 0.6428571428571429
**********************************
**********************************
**********************************
Mean Training Precision: 0.7654196036213177
Mean Training Recall: 0.7333333333333333
Mean Training auc: 0.8459896296296296
Mean Training aupr: 0.8243541685531734
Mean Training f1: 0.7490151736979812
Mean Validation Precision: 0.6312421832277227
Mean Validation Recall: 0.7239316239316239
Mean Validation auc: 0.8274415954415956
Mean Validation aupr: 0.7235919943248541
Mean Validation f1: 0.6730253217119128

3.2 模型比较

按照4.1部分中的参数设定,我们运行10次10交叉实验(10-CV)以避免偏差,系统性的评估不同的机器学习模型。

MinMax-Scale

我们采用最小最大尺度进行归一化,得到的结果如下:

在这里插入图片描述

更进一步的,我们画出上述结果的柱状图:

在这里插入图片描述

从上述结果中我们可以发现:

  • 所有的模型都在我们的数据集上均产生了大体令人满意的结果。
  • 不同的模型出现了不同程度的过拟合,表现为在训练集上的性能高于测试集上的性能。其中逻辑回归过拟合成都较小,随机森林过拟合程度较大。这是由于相比于随机森林,逻辑回归的模型参数量较小,拟合能力有限,不容易发生过拟合。
  • 逻辑回归以较少的参数量,较短的训练时间产生了较为满意的结果。这一点体现了奥卡姆剃刀定律。
  • 在测试数据上,四种模型都实现了相近的效果,整体的性能不会因为采用了不同的模型存在较大的变化;要想获得较好的性能需要选择一个模型然后不断地尝试和优化。这一点体现了没有免费午餐定理(NFL)。
Standard-Scale

同样的,我们采用正态尺度,对数据进行变换,得到以下结果:

在这里插入图片描述

在这里插入图片描述

这里我们观察上述数据可以得到类似于最小最大尺度标准化相同的结论,这里我们就不再赘述了。不过值得注意到是,我们发现有些模型对于数据不同的尺度的放缩平移变换更为敏感,比如这里的多层感知机模型。

至此,我们的任务就完成了。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐