XGBoost线性回归工控数据分析实践案例(Sklearn接口篇)

续上一篇《XGBoost线性回归工控数据分析实践案例(原生篇)》
使用XGB Regressor Fit建模训练,本文以实践为主,原理略过,重点看代码和参数,欢迎读者反馈指导。

回顾需求,工控案例简述

由于各个工控系统分散独立控制各自设备,
其数据采集自采自用,与早起政企信息化建设原理一样,形成了大量信息孤岛,但是,存在较大差别是工控往往是实时系统,虽然产生了大量数据,同时也存在各个工控系统的时钟不一致的现象。为了解决数据相关需求,需要尽量模拟统一时钟的数据,因此,拟采用XGBoost线性回归模型预测某时刻的重要数据。

例如监控某罐体设备液位变换,每10分钟测量罐内液体容量(液位),以此回归预测某时刻,也就是可能不在正常工作测量的时刻,从取液业务时估算某时刻液位,从整体上来说,相当于解决数据缺失问题。

通过此预测,可以相互验证罐体是否漏夜等安全报警信息,从另一个角度报警。

1. XGB Regressor简介

xgboost.XGBRegressor 是XGBoost的Scikit-Learn Wrapper接口。
xgboost.XGBRegressor and xgboost.XGBClassifier 是准备 Scikit-Learn-like包装并传递相应目标函数和参数的包装器(DMatrix,因为它们称之为)。最后,fit调用简单地归结为:

 self._Booster = train(params,dmatrix,
                      self.n_estimators,evals = evals,
                      early_stopping_rounds = early_stopping_rounds,
                      evals_result = evals_result,obj = obj,feval = feval,
                      verbose_eval =详细)

这意味着可以使用everything完成的 XGBRegressor and XGBClassifier 可以通过底层的 xgboost.train 函数实现。另一种方式显然不是这样,例如, xgboost.train API中不支持XGBModel的一些有用参数。

值得注意的区别列表包括:

  • xgboost.train 允许设置在每次迭代结束时应用的callbacks。
  • xgboost.train 允许通过xgb_model parameter.
  • xgboost.train进行持续训练,不仅允许eval函数的最小化,还允许最大化。

2. 再引入两个概念

2.1. 归一化

为什么要进行归一化?

机器学习模型被互联网行业广泛应用,一般做机器学习应用的时候大部分时间是花费在特征处理上,其中很关键的一步就是对特征数据进行归一化,为什么要归一化呢?维基百科给出的解释:

  • 归一化后加快了梯度下降求最优解的速度;
    如果机器学习模型使用梯度下降法求最优解时,归一化往往非常有必要,否则很难收敛甚至不能收敛。
  • 归一化有可能提高精度;
    一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)。

哪些机器学习算法不需要(需要)做归一化?

概率模型(树形模型)不需要归一化,因为它们不关心变量的值,而是关心变量的分布和变量之间的条件概率,如决策树、RF。而像Adaboost、SVM、LR、Knn、KMeans之类的最优化问题就需要归一化。

StandardScaler原理作用:去均值和方差归一化。且是针对每一个特征维度来做的,而不是针对样本。

标准差标准化(standardScale)使得经过处理的数据符合标准正态分布,即均值为0,标准差为1,其转化函数为:

x ∗ = x − μ σ x^*=\frac{x- \mu }{\sigma } x=σxμ

其中μ为所有样本数据的均值,σ为所有样本数据的标准差。

对于RandomForest和XGBoost来说,是否进行归一化对结果影响不大。这也是树模型的一大特征。

2.2. 时序分割

由于存在时间先后的问题,对于存在时间特性的分类或者回归问题不能简单的使用StratifiekdKFold或者KFold进行交叉验证,更不能shuffle,会带来一定的时序特征交叉的问题。

class sklearn.model_selection.TimeSeriesSplit(n_splits=5, max_train_size=None)

TimeSeriesSplit是k-fold的一个变体,它首先返回k折作为训练数据集,并且 (k+1) 折作为测试数据集。请注意,与标准的交叉验证方法不同,连续的训练集是超越前者的超集。另外,它将所有的剩余数据添加到第一个训练分区,它总是用来训练模型。这个类可以用来交叉验证以固定时间间隔观察到的时间序列数据样本。

参数

  • n_splits:int,默认值= 5,表示分割的数目。 必须至少为2。在版本0.22中n_splits的默认值从3更改为5(0.21版中默认值是3。
  • max_train_size:int,可选,表示单个训练集的最大大小。

3.代码

3.1. XGB Regressor核心代码

import xgboost as xgb

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.model_selection import cross_val_score, KFold
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
from xgboost import plot_importance
from sklearn.metrics import r2_score
from sklearn.externals import joblib
from sklearn.model_selection import TimeSeriesSplit  #时序分割
from sklearn.preprocessing import StandardScaler     #归一化

# for time-series cross-validation set 5 folds
tscv = TimeSeriesSplit(n_splits=5)

def get_DataFromExcel():
    df = pd.read_excel('e:/SplitCandINCata02.xlsx')  

    return df

#拆分数据集为x,y
def feature_label_split(data):
    #获取dataFrame的名
    name_list = data.columns.values.tolist()

    label_name = 'LiquidVolume'
    del_name = ['LiquidLevel','LiquidVolume','preTime','MearsureTime','precvolume','nowTime']

    x = data.drop(del_name,axis = 1)
    y = data[label_name]

    return x,y

def feature_datatime(dat):
    dat['nowTime'] = dat['nowTime'].astype('datetime64')
    df_dt = pd.DataFrame(columns=('Year','Month','Day','Hour','Minute'))
 
    df_dt['Hour'] = dat['nowTime'].dt.hour
    df_dt['Year'] = dat['nowTime'].dt.year
    df_dt['Month'] = dat['nowTime'].dt.month
    df_dt['Day'] = dat['nowTime'].dt.day
    df_dt['Minute'] = dat['nowTime'].dt.minute
    
    return df_dt

def init_train_data(df):
    df_out=df.loc[df['OilCanStatus']!=1]     # 剔除掉进油数据
    df_out = df_out.dropna(axis = 0) #处理缺失数据,删除掉
    print(df_out)
    train_x,train_y = feature_label_split(df_out)   
    #xgboost不需要变量变为哑变量
    
    test_percent = 0.3
    x_train,x_test,y_train,y_test = train_test_split(train_x,train_y,test_size = test_percent)
    
    return x_train,x_test,y_train,y_test

#使用XGB Regressor Fit建模训练
def model_fit_regressor(x_train,x_test,y_train,y_test):
    model = xgb.XGBRegressor(max_depth=10, learning_rate=0.1, n_estimators=1000, reg_alpha=0.005, subsample=0.8,
                             gamma=0,colsample_bylevel=0.8, objective ='reg:squarederror')
    #reg:linear is now deprecated in favor of reg:squarederror.
    #'booster':'gblinear'设置,Booster.get_score() results in empty
    #数据归一化处理
    scaler = StandardScaler()
    columns = x_train.columns
    indexs_train = x_train.index
    x_train = pd.DataFrame(scaler.fit_transform(x_train),index = indexs_train, columns = columns)
    indexs_test = x_test.index
    x_test = pd.DataFrame(scaler.transform(x_test),index = indexs_test, columns = columns)

    model.fit(x_train, y_train) 
   
    score = model.score(x_train, y_train)   
    print("Training score: ", score) 
     
    # - cross validataion 
    scores = cross_val_score(model, x_train, y_train, cv=5)
    print("Mean cross-validation score: %.2f" % scores.mean())
    
    kfold = KFold(n_splits=10, shuffle=True)
    kf_cv_scores = cross_val_score(model, x_train, y_train, cv=kfold )
    print("K-fold CV average score: %.2f" % kf_cv_scores.mean())
     
    ypred = model.predict(x_test)
    mse = mean_squared_error(y_test, ypred)
    print("MSE: %.2f" % mse)
    print("RMSE: %.2f" % (mse**(1/2.0)))
    
    x_ax = range(len(y_test))
    plt.scatter(x_ax, y_test, s=5, color="blue", label="original")
    #plt.plot(x_ax, ypred, lw=0.8, color="red", label="predicted")
    plt.legend()
    plt.show()
    
    plotModelResults(model, X_train=x_train, X_test=x_test,  y_train=y_train, y_test=y_test, plot_intervals=True, plot_anomalies=True)
    #保存Model(注:save文件夹要预先建立,否则会报错)
    joblib.dump(model, 'save/OilCan.pkl')
    
    return model

3.2. 关于交叉验证与时序问题

在机器学习建模过程中,通行的做法通常是将数据分为训练集和测试集。测试集是与训练独立的数据,完全不参与训练,用于最终模型的评估。在训练过程中,经常会出现过拟合的问题,就是模型可以很好的匹配训练数据,却不能很好在预测训练集外的数据。如果此时就使用测试数据来调整模型参数,就相当于在训练时已知部分测试数据的信息,会影响最终评估结果的准确性。通常的做法是在训练数据再中分出一部分做为验证(Validation)数据,用来评估模型的训练效果。

验证数据取自训练数据,但不参与训练,这样可以相对客观的评估模型对于训练集之外数据的匹配程度。模型在验证数据中的评估常用的是交叉验证,又称循环验证。它将原始数据分成K组(K-Fold),将每个子集数据分别做一次验证集,其余的K-1组子集数据作为训练集,这样会得到K个模型。这K个模型分别在验证集中评估结果,最后的误差MSE(Mean Squared Error)加和平均就得到交叉验证误差。交叉验证有效利用了有限的数据,并且评估结果能够尽可能接近模型在测试集上的表现,可以做为模型优化的指标使用。

def mean_absolute_percentage_error(y_true, y_pred):
    return np.mean(np.abs((y_true - y_pred) / y_true)) * 100

#代码来自,https://www.cnblogs.com/bonelee/p/9902480.html
def plotModelResults(model, X_train, X_test, y_train, y_test, plot_intervals=False, plot_anomalies=False):
    """
        Plots modelled vs fact values, prediction intervals and anomalies
 
    """
    prediction = model.predict(X_test)
 
    plt.figure(figsize=(15, 7))
    plt.plot(prediction, "g", label="prediction", linewidth=2.0)
    plt.plot(y_test.values, label="actual", linewidth=2.0)
 
    if plot_intervals:
        cv = cross_val_score(model, X_train, y_train,
                             cv=tscv,
                             scoring="neg_mean_absolute_error")
        # scoring='accuracy'  accuracy:评价指标是准确度,可以省略使用默认值
        # cv:选择每次测试折数
        mae = cv.mean() * (-1)
        deviation = cv.std()
 
        scale = 20
        lower = prediction - (mae + scale * deviation)
        upper = prediction + (mae + scale * deviation)
 
        plt.plot(lower, "r--", label="upper bond / lower bond", alpha=0.5)
        plt.plot(upper, "r--", alpha=0.5)
 
        if plot_anomalies:
            anomalies = np.array([np.NaN] * len(y_test))
            anomalies[y_test < lower] = y_test[y_test < lower]
            anomalies[y_test > upper] = y_test[y_test > upper]
            plt.plot(anomalies, "o", markersize=10, label="Anomalies")
 
    error = mean_absolute_percentage_error(prediction, y_test)
    plt.title("Mean absolute percentage error {0:.2f}%".format(error))
    plt.legend(loc="best")
    plt.tight_layout()
    plt.grid(True);
    plt.savefig("linear.png")

平均绝对百分比误差,以及奇异点绘图效果如下:
XGBoost线性回归工控数据分析实践案例(Sklearn接口篇)_第1张图片
从图中,我们可以看到数据呈周期形式波动。

如果数据总量较小时,其他方法无法继续提升性能,可以尝试K-Fold。其他情况就不太建议了,例如数据量很大,就没必要更多训练数据,同时训练成本也要扩大K倍(主要指的训练时间)。

3.3. 其他代码

3.3.1. 训练代码

#训练模型
def train_model():
    #读取Excel数据
    df0 = get_DataFromExcel()
    df0 = df0.drop_duplicates(subset = ['nowTime','OilCanID','OilCode','LiquidLevel'],keep ='first',inplace = False) #数据去重
    
    df0 = pd.concat([df0,feature_datatime(df0)],axis=1)
    
    x_train,x_test,y_train,y_test = init_train_data(df0)
     
    model = model_fit_regressor(x_train,x_test,y_train,y_test)
    
    #model= model_train_reg(x_train,x_test,y_train,y_test)    
    #model.save_model('OilCanXGbLinear.model')  # 保存训练模型
    # 显示重要特征
    plot_importance(model)
    plt.show()
#模型加载与使用
def test_model():
    df0 = pd.read_excel('e:/SplitCandINCata02.xlsx')
    df0 = pd.concat([df0,feature_datatime(df0)],axis=1)

    x_train,x_test,y_train,y_test = init_train_data(df0)

    #读取Model
    model = joblib.load('save/OilCan.pkl')
    ypred = model.predict(x_test)
    #plot_tree(model)
    #plt.show()
    print(ypred)

#训练并分析模型    
train_model()

通过训练分析,得出特征重要程度。
XGBoost线性回归工控数据分析实践案例(Sklearn接口篇)_第2张图片
上图采用XGBoost 回归分析得到,与预估理论分析比较接近。
运行输出结果:

[59313 rows x 9 columns]
Training score:  0.9998090703747619
Mean cross-validation score: 0.98
K-fold CV average score: 0.98
MSE: 149006.93
RMSE: 386.01

利用XGBoost Plotting API可以实现画树这个过程,使用下plot_tree这个函数。

对于graphviz,也简单pip install graphviz后,虽然不会报错,但是在调用plot_tree时会报错。解决方案如下:graphviz需要先下载一个windows版本的graphviz安装包,下载地址如下。然后在命令行中输入以下代码将下载的graphviz添加到系统环境变量中即可。
首先要在python中安装graphviz:pip install graphviz;
然后下载graphviz这个软件,直接下载镜像文件安装就好,要记得安装路径,并将路径添加到系统path中;
最后,pip install pydot

参考:
[1]《XGBoost线性回归工控数据分析实践案例(原生篇)》 CSDN博客 , 肖永威 ,2020年8月
[2]《时间序列预测——深度好文,ARIMA是最难用的(数据预处理过程不适合工业应用),线性回归模型简单适用,预测趋势很不错,xgboost的话,不太适合趋势预测,如果数据平稳也可以使用。》博客园 , bonelee , 2018年11月
[3]《k-交叉验证KFold》 博客园 ,地球上最后一个直男 ,2019年8月

你可能感兴趣的:(人工智能及Python,大数据,python,XGBoost,XGB,Regressor,工控大数据,交叉验证)