1. 数据集

1.1 背景介绍

火力发电的基本原理是:燃料在燃烧时加热水生成蒸汽,蒸汽压力推动汽轮机旋转,然后汽轮机带动发电机旋转,产生电能。在这一系列的能量转化中,影响发电效率的核心是锅炉的燃烧效率,即燃料燃烧加热水产生高温高压蒸汽。锅炉的燃烧效率的影响因素很多,包括锅炉的可调参数,如燃烧给量,一二次风,引风,返料风,给水水量;以及锅炉的工况,比如锅炉床温、床压,炉膛温度、压力,过热器的温度等。我们如何使用以上的信息,根据锅炉的工况,预测产生的蒸汽量,来为我国的工业届的产量预测贡献自己的一份力量呢?
所以,该案例是使用以上工业指标的特征,进行蒸汽量的预测问题。由于信息安全等原因,我们使用的是经脱敏后的锅炉传感器采集的数据(采集频率是分钟级别)。
其实处理起来感觉和这个背景并没有什么关系,因为是脱敏后的数据,我们并不知道每个属性的真实含义是什么,也就不需要人为的对每个属性进行处理,不需要添加和人工属性和交叉属性等等。

1.2 数据集

数据分成训练数据(train.txt)和测试数据(test.txt),其中字段”V0”-“V37”,这38个字段是作为特征变量,”target”作为目标变量。我们需要利用训练数据训练出模型,预测测试数据的目标变量。

1.3 评价指标:

最终的评价指标为均方误差MSE,即:
image.png

2. 数据预处理

2.1 导入依赖包

  1. import warnings
  2. warnings.filterwarnings("ignore")
  3. import matplotlib.pyplot as plt
  4. import seaborn as sns
  5. # 模型
  6. import pandas as pd
  7. import numpy as np
  8. from scipy import stats
  9. from sklearn.model_selection import train_test_split
  10. from sklearn.model_selection import GridSearchCV, RepeatedKFold, cross_val_score,cross_val_predict,KFold
  11. from sklearn.metrics import make_scorer,mean_squared_error
  12. from sklearn.linear_model import LinearRegression, Lasso, Ridge, ElasticNet
  13. from sklearn.svm import LinearSVR, SVR
  14. from sklearn.neighbors import KNeighborsRegressor
  15. from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor,AdaBoostRegressor
  16. from xgboost import XGBRegressor
  17. from sklearn.preprocessing import PolynomialFeatures,MinMaxScaler,StandardScaler

2.2 读取数据

  1. data_train = pd.read_csv('train.txt',sep = '\t')
  2. data_test = pd.read_csv('test.txt',sep = '\t')
  3. #合并训练数据和测试数据
  4. #在合并训练数据与测试数据之前,为其添加标签,标明来源
  5. data_train["oringin"]="train"
  6. data_test["oringin"]="test"
  7. data_all=pd.concat([data_train,data_test],axis=0,ignore_index=True)
  8. #显示前5条数据
  9. data_all.head()

集成学习案例二 (蒸汽量预测) - 图2

2.3 数据探索性分析

这里使用 kdeplot(核密度估计图) 进行数据的初步分析,即EDA。
核密度估计
核密度估计:https://www.zhihu.com/question/27301358
核密度估计根据我的理解就是,已知散点图,做回归,要求连线尽可能平滑,大致观察数据的分布情况。在本例中,通过核密度估计,观察训练集与测试集数据的分布情况,从而删除不具有相似分布的属性值。

  1. for column in data_all.columns[0:-2]:
  2. #核密度估计(kernel density estimation)是在概率论中用来估计未知的密度函数,属于非参数检验方法之一。通过核密度估计图可以比较直观的看出数据样本本身的分布特征。
  3. #kdeplot手册:https://www.cntofu.com/book/172/docs/25.md
  4. #ax: 要绘图的坐标轴,若为空,则使用当前轴
  5. g = sns.kdeplot(data_all[column][(data_all["oringin"] == "train")], color="Red", shade = True)
  6. g = sns.kdeplot(data_all[column][(data_all["oringin"] == "test")], ax =g,color="Blue", shade= True)
  7. g.set_xlabel(column)
  8. g.set_ylabel("Frequency")
  9. g = g.legend(["train","test"])
  10. plt.show()

集成学习案例二 (蒸汽量预测) - 图3
集成学习案例二 (蒸汽量预测) - 图4
集成学习案例二 (蒸汽量预测) - 图5
集成学习案例二 (蒸汽量预测) - 图6
集成学习案例二 (蒸汽量预测) - 图7
集成学习案例二 (蒸汽量预测) - 图8
集成学习案例二 (蒸汽量预测) - 图9
集成学习案例二 (蒸汽量预测) - 图10
集成学习案例二 (蒸汽量预测) - 图11
集成学习案例二 (蒸汽量预测) - 图12
集成学习案例二 (蒸汽量预测) - 图13
集成学习案例二 (蒸汽量预测) - 图14
集成学习案例二 (蒸汽量预测) - 图15
集成学习案例二 (蒸汽量预测) - 图16
集成学习案例二 (蒸汽量预测) - 图17
集成学习案例二 (蒸汽量预测) - 图18
集成学习案例二 (蒸汽量预测) - 图19
集成学习案例二 (蒸汽量预测) - 图20
集成学习案例二 (蒸汽量预测) - 图21
集成学习案例二 (蒸汽量预测) - 图22
集成学习案例二 (蒸汽量预测) - 图23
集成学习案例二 (蒸汽量预测) - 图24
集成学习案例二 (蒸汽量预测) - 图25
集成学习案例二 (蒸汽量预测) - 图26
集成学习案例二 (蒸汽量预测) - 图27
集成学习案例二 (蒸汽量预测) - 图28
集成学习案例二 (蒸汽量预测) - 图29
集成学习案例二 (蒸汽量预测) - 图30
集成学习案例二 (蒸汽量预测) - 图31
集成学习案例二 (蒸汽量预测) - 图32
集成学习案例二 (蒸汽量预测) - 图33
集成学习案例二 (蒸汽量预测) - 图34
集成学习案例二 (蒸汽量预测) - 图35
集成学习案例二 (蒸汽量预测) - 图36
集成学习案例二 (蒸汽量预测) - 图37
集成学习案例二 (蒸汽量预测) - 图38
集成学习案例二 (蒸汽量预测) - 图39

集成学习案例二 (蒸汽量预测) - 图40
从以上的图中可以看出特征”V5”,“V9”,“V11”,“V17”,“V22”,”V28”中训练集数据分布和测试集数据分布不均,所以我们删除这些特征数据。

  1. for column in ["V5","V9","V11","V17","V22","V28"]:
  2. g = sns.kdeplot(data_all[column][(data_all["oringin"] == "train")], color="Red", shade = True)
  3. g = sns.kdeplot(data_all[column][(data_all["oringin"] == "test")], ax =g, color="Blue", shade= True)
  4. g.set_xlabel(column)
  5. g.set_ylabel("Frequency")
  6. g = g.legend(["train","test"])
  7. plt.show()
  8. data_all.drop(["V5","V9","V11","V17","V22","V28"],axis=1,inplace=True)

查看特征之间的相关性(相关程度)
集成学习案例二 (蒸汽量预测) - 图41
进行降维操作,即将相关性的绝对值小于阈值的特征进行删除。

  1. threshold = 0.1
  2. corr_matrix = data_train1.corr().abs()
  3. drop_col=corr_matrix[corr_matrix["target"]<threshold].index
  4. data_all.drop(drop_col,axis=1,inplace=True)
  5. # 归一化
  6. cols_numeric=list(data_all.columns)
  7. cols_numeric.remove("oringin")
  8. def scale_minmax(col):
  9. return (col-col.min())/(col.max()-col.min())
  10. scale_cols = [col for col in cols_numeric if col!='target']
  11. data_all[scale_cols] = data_all[scale_cols].apply(scale_minmax,axis=0)
  12. data_all[scale_cols].describe()

集成学习案例二 (蒸汽量预测) - 图42
绘图显示Box-Cox变换对数据分布影响,Box-Cox用于连续的响应变量不满足正态分布的情况。在进行Box-Cox变换之后,可以一定程度上减小不可观测的误差和预测变量的相关性。

特征工程

绘图显示Box-Cox变换对数据分布影响,Box-Cox用于连续的响应变量不满足正态分布的情况。在进行Box-Cox变换之后,可以一定程度上减小不可观测的误差和预测变量的相关性。

对于quantitle-quantile(q-q)图,可参考: https://blog.csdn.net/u012193416/article/details/83210790

  1. fcols = 6
  2. frows = len(cols_numeric)-1
  3. plt.figure(figsize=(4*fcols,4*frows))
  4. i=0
  5. for var in cols_numeric:
  6. if var!='target':
  7. dat = data_all[[var, 'target']].dropna()
  8. i+=1
  9. plt.subplot(frows,fcols,i)
  10. sns.distplot(dat[var] , fit=stats.norm);
  11. plt.title(var+' Original')
  12. plt.xlabel('')
  13. i+=1
  14. plt.subplot(frows,fcols,i)
  15. _=stats.probplot(dat[var], plot=plt)
  16. plt.title('skew='+'{:.4f}'.format(stats.skew(dat[var])))
  17. plt.xlabel('')
  18. plt.ylabel('')
  19. i+=1
  20. plt.subplot(frows,fcols,i)
  21. plt.plot(dat[var], dat['target'],'.',alpha=0.5)
  22. plt.title('corr='+'{:.2f}'.format(np.corrcoef(dat[var], dat['target'])[0][1]))
  23. i+=1
  24. plt.subplot(frows,fcols,i)
  25. trans_var, lambda_var = stats.boxcox(dat[var].dropna()+1)
  26. trans_var = scale_minmax(trans_var)
  27. sns.distplot(trans_var , fit=stats.norm);
  28. plt.title(var+' Tramsformed')
  29. plt.xlabel('')
  30. i+=1
  31. plt.subplot(frows,fcols,i)
  32. _=stats.probplot(trans_var, plot=plt)
  33. plt.title('skew='+'{:.4f}'.format(stats.skew(trans_var)))
  34. plt.xlabel('')
  35. plt.ylabel('')
  36. i+=1
  37. plt.subplot(frows,fcols,i)
  38. plt.plot(trans_var, dat['target'],'.',alpha=0.5)
  39. plt.title('corr='+'{:.2f}'.format(np.corrcoef(trans_var,dat['target'])[0][1]))

下载.png

  1. # 进行Box-Cox变换
  2. cols_transform=data_all.columns[0:-2]
  3. for col in cols_transform:
  4. # transform column
  5. data_all.loc[:,col], _ = stats.boxcox(data_all.loc[:,col]+1)
  6. print(data_all.target.describe())
  7. plt.figure(figsize=(12,4))
  8. plt.subplot(1,2,1)
  9. sns.distplot(data_all.target.dropna() , fit=stats.norm);
  10. plt.subplot(1,2,2)
  11. _=stats.probplot(data_all.target.dropna(), plot=plt)

:::success count 2888.000000
mean 0.126353
std 0.983966
min -3.044000
25% -0.350250
50% 0.313000
75% 0.793250
max 2.538000
Name: target, dtype: float64 ::: image.png

使用对数变换target目标值提升特征数据的正太性 可参考:https://www.zhihu.com/question/22012482

  1. sp = data_train.target
  2. data_train.target1 =np.power(1.5,sp)
  3. print(data_train.target1.describe())
  4. plt.figure(figsize=(12,4))
  5. plt.subplot(1,2,1)
  6. sns.distplot(data_train.target1.dropna(),fit=stats.norm);
  7. plt.subplot(1,2,2)
  8. _=stats.probplot(data_train.target1.dropna(), plot=plt)

image.png
image.png

模型构建以及集成学习

构建训练集和测试集

  1. # function to get training samples
  2. def get_training_data():
  3. # extract training samples
  4. from sklearn.model_selection import train_test_split
  5. df_train = data_all[data_all["oringin"]=="train"]
  6. df_train["label"]=data_train.target1
  7. # split SalePrice and features
  8. y = df_train.target
  9. X = df_train.drop(["oringin","target","label"],axis=1)
  10. X_train,X_valid,y_train,y_valid=train_test_split(X,y,test_size=0.3,random_state=100)
  11. return X_train,X_valid,y_train,y_valid
  12. # extract test data (without SalePrice)
  13. def get_test_data():
  14. df_test = data_all[data_all["oringin"]=="test"].reset_index(drop=True)
  15. return df_test.drop(["oringin","target"],axis=1)

rmse、mse的评价函数

  1. from sklearn.metrics import make_scorer
  2. # metric for evaluation
  3. def rmse(y_true, y_pred):
  4. diff = y_pred - y_true
  5. sum_sq = sum(diff**2)
  6. n = len(y_pred)
  7. return np.sqrt(sum_sq/n)
  8. def mse(y_ture,y_pred):
  9. return mean_squared_error(y_ture,y_pred)
  10. # scorer to be used in sklearn model fitting
  11. rmse_scorer = make_scorer(rmse, greater_is_better=False)
  12. #输入的score_func为记分函数时,该值为True(默认值);输入函数为损失函数时,该值为False
  13. mse_scorer = make_scorer(mse, greater_is_better=False)

寻找离群值,并删除

  1. # function to detect outliers based on the predictions of a model
  2. def find_outliers(model, X, y, sigma=3):
  3. # predict y values using model
  4. model.fit(X,y)
  5. y_pred = pd.Series(model.predict(X), index=y.index)
  6. # calculate residuals between the model prediction and true y values
  7. resid = y - y_pred
  8. mean_resid = resid.mean()
  9. std_resid = resid.std()
  10. # calculate z statistic, define outliers to be where |z|>sigma
  11. z = (resid - mean_resid)/std_resid
  12. outliers = z[abs(z)>sigma].index
  13. # print and plot the results
  14. print('R2=',model.score(X,y))
  15. print('rmse=',rmse(y, y_pred))
  16. print("mse=",mean_squared_error(y,y_pred))
  17. print('---------------------------------------')
  18. print('mean of residuals:',mean_resid)
  19. print('std of residuals:',std_resid)
  20. print('---------------------------------------')
  21. print(len(outliers),'outliers:')
  22. print(outliers.tolist())
  23. plt.figure(figsize=(15,5))
  24. ax_131 = plt.subplot(1,3,1)
  25. plt.plot(y,y_pred,'.')
  26. plt.plot(y.loc[outliers],y_pred.loc[outliers],'ro')
  27. plt.legend(['Accepted','Outlier'])
  28. plt.xlabel('y')
  29. plt.ylabel('y_pred');
  30. ax_132=plt.subplot(1,3,2)
  31. plt.plot(y,y-y_pred,'.')
  32. plt.plot(y.loc[outliers],y.loc[outliers]-y_pred.loc[outliers],'ro')
  33. plt.legend(['Accepted','Outlier'])
  34. plt.xlabel('y')
  35. plt.ylabel('y - y_pred');
  36. ax_133=plt.subplot(1,3,3)
  37. z.plot.hist(bins=50,ax=ax_133)
  38. z.loc[outliers].plot.hist(color='r',bins=50,ax=ax_133)
  39. plt.legend(['Accepted','Outlier'])
  40. plt.xlabel('z')
  41. return outliers
  42. # get training data
  43. X_train, X_valid,y_train,y_valid = get_training_data()
  44. test=get_test_data()
  45. # find and remove outliers using a Ridge model
  46. outliers = find_outliers(Ridge(), X_train, y_train)
  47. X_outliers=X_train.loc[outliers]
  48. y_outliers=y_train.loc[outliers]
  49. X_t=X_train.drop(outliers)
  50. y_t=y_train.drop(outliers)

image.png
进行模型的训练

def get_trainning_data_omitoutliers():
    #获取训练数据省略异常值
    y=y_t.copy()
    X=X_t.copy()
    return X,y

def train_model(model, param_grid=[], X=[], y=[], 
                splits=5, repeats=5):

    # 获取数据
    if len(y)==0:
        X,y = get_trainning_data_omitoutliers()

    # 交叉验证
    rkfold = RepeatedKFold(n_splits=splits, n_repeats=repeats)

    # 网格搜索最佳参数
    if len(param_grid)>0:
        gsearch = GridSearchCV(model, param_grid, cv=rkfold,
                               scoring="neg_mean_squared_error",
                               verbose=1, return_train_score=True)

        # 训练
        gsearch.fit(X,y)

        # 最好的模型
        model = gsearch.best_estimator_        
        best_idx = gsearch.best_index_

        # 获取交叉验证评价指标
        grid_results = pd.DataFrame(gsearch.cv_results_)
        cv_mean = abs(grid_results.loc[best_idx,'mean_test_score'])
        cv_std = grid_results.loc[best_idx,'std_test_score']

    # 没有网格搜索  
    else:
        grid_results = []
        cv_results = cross_val_score(model, X, y, scoring="neg_mean_squared_error", cv=rkfold)
        cv_mean = abs(np.mean(cv_results))
        cv_std = np.std(cv_results)

    # 合并数据
    cv_score = pd.Series({'mean':cv_mean,'std':cv_std})

    # 预测
    y_pred = model.predict(X)

    # 模型性能的统计数据        
    print('----------------------')
    print(model)
    print('----------------------')
    print('score=',model.score(X,y))
    print('rmse=',rmse(y, y_pred))
    print('mse=',mse(y, y_pred))
    print('cross_val: mean=',cv_mean,', std=',cv_std)

    # 残差分析与可视化
    y_pred = pd.Series(y_pred,index=y.index)
    resid = y - y_pred
    mean_resid = resid.mean()
    std_resid = resid.std()
    z = (resid - mean_resid)/std_resid    
    n_outliers = sum(abs(z)>3)
    outliers = z[abs(z)>3].index

    return model, cv_score, grid_results

# 定义训练变量存储数据
opt_models = dict()
score_models = pd.DataFrame(columns=['mean','std'])
splits=5
repeats=5


# 定义训练变量存储数据
opt_models = dict()
score_models = pd.DataFrame(columns=['mean','std'])
splits=5
repeats=5

image.png
下载.png

# 预测函数
def model_predict(test_data,test_y=[]):
    i=0
    y_predict_total=np.zeros((test_data.shape[0],))
    for model in opt_models.keys():
        if model!="LinearSVR" and model!="KNeighbors":
            y_predict=opt_models[model].predict(test_data)
            y_predict_total+=y_predict
            i+=1
        if len(test_y)>0:
            print("{}_mse:".format(model),mean_squared_error(y_predict,test_y))
    y_predict_mean=np.round(y_predict_total/i,6)
    if len(test_y)>0:
        print("mean_mse:",mean_squared_error(y_predict_mean,test_y))
    else:
        y_predict_mean=pd.Series(y_predict_mean)
        return y_predict_mean

进行模型的预测以及结果的保存

y_ = model_predict(test)
y_.to_csv('predict.txt',header = None,index = False)