python学习——回归模型

发布时间 2023-10-15 17:11:56作者: 斯文小扬

从本篇开始记录一下我在研究生阶段的学习

作业之成人死亡率预测(回归模型)

1 实验介绍

1.1 实验背景

成年人死亡率指的是每一千人中15岁至60岁死亡的概率(数学期望)。这里我们给出了世界卫生组织(WHO)下属的全球卫生观察站(GHO)数据存储库跟踪的所有国家健康状况以及许多其他相关因素。要求利用训练数据建立回归模型,并预测成年人死亡率(Adult Mortality)。

1.2 实验要求

  1. 训练数据包含2336条记录和22个字段,对训练数据进行一定的可视化数据分析(章节2.2)
  2. 利用训练数据,选择合适的信息作为特征建立回归模型,并预测测试数据成年人死亡率
  3. 利用MO平台进行模型性能评估

1.3 实验环境

可以使用基于 Python 的 Pandas 库进行数据相关处理,使用 Sklearn 库进行相关模型构建。

1.4 注意事项

  1. 推荐使用基于 Python 的Sklearn库进行相关实验
  2. 数据中可能会有一些字段的值存在缺失

2 实验部分

2.1 导入相关包

import pandas as pd
import sklearn
import numpy as np

from sklearn.impute import SimpleImputer
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

from sklearn.ensemble import RandomForestRegressor, ExtraTreesRegressor
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV

import joblib

2.2 数据可视化分析

# 读取数据集
train_data = pd.read_csv('./data/train_data.csv')

#部分数据展示,以及数据集大小
train_data
train_data.shape

(2336, 22)

训练数据中包含了包含 2336 条记录和 22 个字段,每个字段含义说明如下:

  • Country:国家
  • Year:年份
  • Status:发达国家或发展中国家
  • Life expectancy:预期寿命
  • Infant deaths:每千人口中的婴儿死亡人数
  • Alcohol:人均酒精消费量(以升纯酒精为单位)
  • percentage expenditure:卫生支出占人均国内生产总值的百分比
  • Hepatitis B:一岁儿童乙型肝炎免疫疫苗接种率
  • Measles:麻疹每1000人报告的病例数
  • BMI:所有人群平均BMI指数
  • under-five deaths:每千人口中五岁以下死亡人数
  • Polio:1岁儿童脊髓灰质炎免疫覆盖率(%)
  • Total expenditure:政府卫生支出占政府总支出的百分比
  • Diphtheria:1岁儿童白喉、破伤风类毒素和百日咳免疫接种率(%)
  • HIV/AIDS:每千名活产婴儿死于艾滋病毒/艾滋病(0-4岁)
  • GDP:人均国内生产总值(美元)
  • Population:人口
  • thinness 1-19 years:10至19岁儿童和青少年的消瘦流行率
  • thinness 5-9 years:5至9岁儿童中的消瘦流行率
  • Income composition of resources:财力收入构成方面的人类发展指数(从0到1)
  • Schooling:受教育年限
  • Adult Mortality:成人死亡率(每1000人中15至60岁死亡的概率)

第22列(Adult Mortality)是本实验需要预测的数据,且上图高亮出为缺失值

# 计算各个特征之间的皮尔森相关系数
train_data.corr()

#根据皮尔森相关系数绘制热力图重点看adult mortaliyu之相关的
corr = train_data.corr()
corr.style.background_gradient(cmap='coolwarm')

# 利用seaborn检查可视化数据之间的依赖关系
import seaborn as sns
corr.style.background_gradient(cmap='coolwarm')
sns.pairplot(train_data)

从上述对数据的观察以及可视化不难得到以下几点:

  1. 数据中有一些字段的值存在缺失(NaN)
  2. 需要预测的数据成人死亡率与部分数据例如(Year,Country)等相关度不高
  3. 此外可能有一些数据的值发生异常

2.3 数据预处理

那么根据作业说明,对存在上述问题的数据进行预处理。

对于缺失值处理,可以采用对应列的均值、中位数、众数等方式填充,采用sklearn中的 SimpleImputer来填充缺失值,:

imputer = SimpleImputer(strategy='mean', missing_values=np.nan)
#采用均值
imputer = imputer.fit(data)
data = imputer.transform(data)

对于相关度不高的列,可以在处理数据时,将对应的列直接剔除,采用data.drop(["Country","Year"], axis=1),可以查看对应的皮尔森相关系数表格中,将皮尔森相关系数绝对值小于0.01的全部剔除。

同时也需要对数据进行归一化,消除特征尺度不同所带来的影响,使特征具有可比性。
采用Min-Max Scaling将特征归一化到[0,1],加速收敛。
$$
z=
\frac{x_i - min(x-i)}{max(x_i)-min(x_i)}
$$

scaler = MinMaxScaler()
scaler = scaler.fit(data)
data_norm = pd.DataFrame(scaler.transform(data), columns=data.columns)

完整代码如下:

def preprocess_data(data, imputer=None, scaler=None):
    
# -------------------------- 请调整你的数据预处理过程 ---------------------------
## 输入:
#### data 为 pandas.DataFrame类型数据
#### imputer 为缺失值填充方式
#### scaler 为数据归一化方式
## 输出:
#### data_norm 为处理后的数据,为 pandas.DataFrame类型数据
   
    column_name = ['Year', 'Life expectancy ', 'infant deaths', 'Alcohol',
               'percentage expenditure', 'Hepatitis B', 'Measles ', ' BMI ', 'under-five deaths ',
               'Polio', 'Total expenditure', 'Diphtheria ', ' HIV/AIDS', 'GDP', 'Population',
               ' thinness  1-19 years', ' thinness 5-9 years', 'Income composition of resources',
               'Schooling']
    data = data.drop(["Country", "Status"], axis=1)
    
    if imputer==None:
        imputer = SimpleImputer(strategy='mean', missing_values=np.nan)
        imputer = imputer.fit(data[column_name])
    data[column_name] = imputer.transform(data[column_name])

    if scaler==None:
        scaler = MinMaxScaler()
        scaler = scaler.fit(data)
    data_norm = pd.DataFrame(scaler.transform(data), columns=data.columns)
    
    #复制column_name里面的,空格不能省略
    data_norm = data_norm.drop(['Year', 'Measles ', 'under-five deaths ', 'Population', 'infant deaths'], axis = 1)
    return data_norm,imputer,scaler

2.4 建立模型

模型的采用上,我参考了网上部分线性回归算法,选择了sklearn包中的随机森林回归模型(RandomForestRegressor),相关参数说明

RandomForestRegressor参数说明
n_estimators:决策树个数
min_samples_leaf:叶节点所需的最小样本数。如果过小,可能导致过拟合
min_samples_split:分裂内部节点所需的最小样本数
max_depth:树的最大深度,越深越拟合
max_features:构建决策树最优模型时考虑的最大特征数。默认是"auto",表示最大特征数是N的平方根;

对于这些超参数,采用网格搜索,交叉验证

def model_fit(train_data):
    
    train_y = train_data.iloc[:, -1].values
    train_data = train_data.iloc[:, :-1]
 
    train_data_norm, imputer, scaler = preprocess_data(train_data)
 
    train_x = train_data_norm.values
 
    # 需要网格搜索的参数
    n_estimators = [i for i in range(650, 681, 5)]
    max_depth = [i for i in range(14, 18)]  # 最大深度
    min_samples_split = [i for i in range(2, 4)]  # 部节点再划分所需最小样本数
    min_samples_leaf = [i for i in range(3, 5)]  # 叶节点最小样本数
    max_samples = [i/100 for i in range(95, 97)]
    parameters = {'n_estimators': n_estimators,  # 弱学习器的最大迭代次数
                  'max_depth': max_depth,
                  'min_samples_split': min_samples_split,
                  'min_samples_leaf': min_samples_leaf,
                  'max_samples': max_samples
                  }
    
    # 随机森林回归
    regressor = RandomForestRegressor(bootstrap=True, oob_score=True, random_state=0)
    res = RandomizedSearchCV(regressor, parameters, n_iter = 100,refit=True,cv=10, verbose=1,n_jobs=-1)
 
    res.fit(train_x, train_y)
 
    joblib.dump(res, model_filename)
    joblib.dump(imputer, imputer_filename)
    joblib.dump(scaler, scaler_filename)
 
    return res

训练模型后的预测结果:

label = train_data.loc[:,'Adult Mortality']
data = train_data.iloc[:,:-1]
y_pred = predict(data)
r2 = r2_score(label, y_pred)
mse = mean_squared_error(label, y_pred)
print("MSE is {}".format(mse))
print("R2 score is {}".format(r2))

MSE is 2488.916249921622
R2 score is 0.8399071459471199

最终完整代码如下:

import pandas as pd
import sklearn
import numpy as np
import time

from sklearn.impute import SimpleImputer
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score

from sklearn.ensemble import RandomForestRegressor, ExtraTreesRegressor
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV


import joblib

model_filename = './results/model.pkl'
imputer_filename = './results/imputer.pkl'
scaler_filename = './results/scaler.pkl'

def preprocess_data(data, imputer=None, scaler=None):
    
# -------------------------- 请调整你的数据预处理过程 ---------------------------
## 输入:
#### data 为 pandas.DataFrame类型数据
#### imputer 为缺失值填充方式
#### scaler 为数据归一化方式
## 输出:
#### data_norm 为处理后的数据,为 pandas.DataFrame类型数据
   
    column_name = ['Year', 'Life expectancy ', 'infant deaths', 'Alcohol',
               'percentage expenditure', 'Hepatitis B', 'Measles ', ' BMI ', 'under-five deaths ',
               'Polio', 'Total expenditure', 'Diphtheria ', ' HIV/AIDS', 'GDP', 'Population',
               ' thinness  1-19 years', ' thinness 5-9 years', 'Income composition of resources',
               'Schooling']
    data = data.drop(["Country", "Status"], axis=1)
    
    if imputer==None:
        imputer = SimpleImputer(strategy='mean', missing_values=np.nan)
        imputer = imputer.fit(data[column_name])
    data[column_name] = imputer.transform(data[column_name])

    if scaler==None:
        scaler = MinMaxScaler()
        scaler = scaler.fit(data)
    data_norm = pd.DataFrame(scaler.transform(data), columns=data.columns)
    
    #复制column_name里面的,空格不能省略
    data_norm = data_norm.drop(['Year', 'Measles ', 'under-five deaths ', 'Population', 'infant deaths'], axis = 1)
    return data_norm,imputer,scaler

def predict(test_data):
    
# -------------------------- 请加载您最满意的模型 ---------------------------
# 加载模型(请加载你认为的最佳模型)
# 加载模型,加载请注意 filename 是相对路径, 与当前文件同级。 
# test_data 为 pandas.DataFrame类型数据  
    loaded_model = joblib.load(model_filename)
    imputer = joblib.load(imputer_filename)
    scaler = joblib.load(scaler_filename)

    test_data_norm, _, _ = preprocess_data(test_data, imputer, scaler)
    test_x = test_data_norm.values
    predictions = loaded_model.predict(test_x)
    
    return predictions

#训练模型
model = model_fit(train_data)

#验证
label = train_data.loc[:,'Adult Mortality']
data = train_data.iloc[:,:-1]
y_pred = predict(data)
r2 = r2_score(label, y_pred)
mse = mean_squared_error(label, y_pred)
print("MSE is {}".format(mse))
print("R2 score is {}".format(r2))

2.5 值得注意的细节

首先肯定是对于文件读写的路径,使用相对路径会方便很多;
注意代码的一致性,人为剔除了几个不相关的列,其列名需要与给定代码中一直,即“”引号内的所有内容一致。在修改数据预处理中,因为引号中的空格没写,一直报错卡了很久。
模型需要跑的时间比较长,提交代码只需要把跑好的模型交上去,而不是傻乎乎的重新跑一边代码,最后附上提交测试结果

3 总结

本片实验围绕线性回归模型对数据处理、模型建立等进行了一系列考察,在此之前我是没有进行过相关的实验经历,所以遇到的麻烦还比较多,在网上搜索了比较多的资料,也跟舍友同学进行了大量的讨论,最终验证了回归分析的基本方法。

(差点再第一周就忘记这件事了,还好东西已经写好了)