从本篇开始记录一下我在研究生阶段的学习
作业之成人死亡率预测(回归模型)
1 实验介绍
1.1 实验背景
成年人死亡率指的是每一千人中15岁至60岁死亡的概率(数学期望)。这里我们给出了世界卫生组织(WHO)下属的全球卫生观察站(GHO)数据存储库跟踪的所有国家健康状况以及许多其他相关因素。要求利用训练数据建立回归模型,并预测成年人死亡率(Adult Mortality)。
1.2 实验要求
- 训练数据包含
2336条记录和22个字段,对训练数据进行一定的可视化数据分析(章节2.2) - 利用训练数据,选择合适的信息作为特征建立回归模型,并预测测试数据成年人死亡率
- 利用
MO平台进行模型性能评估
1.3 实验环境
可以使用基于 Python 的 Pandas 库进行数据相关处理,使用 Sklearn 库进行相关模型构建。
1.4 注意事项
- 推荐使用基于 Python 的
Sklearn库进行相关实验 - 数据中可能会有一些字段的值存在缺失
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)
从上述对数据的观察以及可视化不难得到以下几点:
- 数据中有一些字段的值存在缺失(NaN)
- 需要预测的数据成人死亡率与部分数据例如(
Year,Country)等相关度不高 - 此外可能有一些数据的值发生异常
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 总结
本片实验围绕线性回归模型对数据处理、模型建立等进行了一系列考察,在此之前我是没有进行过相关的实验经历,所以遇到的麻烦还比较多,在网上搜索了比较多的资料,也跟舍友同学进行了大量的讨论,最终验证了回归分析的基本方法。
(差点再第一周就忘记这件事了,还好东西已经写好了)