由于一段时间的断更,关于工业蒸汽预测的赛题,只记录到了模型训练部分,今天来看下模型训练后常用的模型检验,评估方法,以及如何使用特征组合去提升特征的质量
文章开始之前,有必要了解下相关的概念
模型评估的概念和方法
欠拟合和过拟合
数据关系是样本的分布规律,或者是特征与对应样本之间的关系,如果一个模型恰到好处的表达了数据之间的关系时,我们就认为这个模型拟合效果好,欠拟合(underfitting)也叫做高偏差(bias),是指算法训练的模型不能完整的表达数据关系,在这种情况下需要增加额外的特征,增加多项式特征等方法来优化模型,过拟合(overfitting)也叫做高偏差(variance),指的是算法训练出的模型过多的表达了数据关系,此时很有可能是数据间的噪声关系,这种情况下一般需要通过收集更多的数据,使用更少的特征来优化模型
下图分别表示了欠拟合,正常拟合,过拟合的三种拟合结果
举个例子,来进一步说明欠拟合和过拟合,假设现在手动模拟出一个二维线性数据集并显示
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
class node():
def __init__(self,x,y):
self.x = x
self.y = y
l = list()
np.random.seed(666)
x = np.random.uniform(-3.0,3.0,size=100)
X = x.reshape(-1,1)
y = 0.5 * x ** 2 + x + 2 + np.random.normal(0,1,size=100)
plt.scatter(x,y)
plt.show()
for i in range(0,len(x)):
t = node(x[i],y[i])
l.append(t)
l.sort(key=lambda t:t.x)
for i in range(0,len(x)):
print(l[i].x,l[i].y)
接下来用前面学过的线性回归模型对数据进行拟合
# 使用线性模型对数据进行拟合
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X,y)
lin_reg.score(X,y)
可以看到,准确率很低,效果不是很好,可见我们用直线拟合数据的程度很低,绘制一下结果看看
# 绘制拟合结果
y_predict = lin_reg.predict(X)
print(y_predict)
plt.scatter(x,y)
plt.plot(np.sort(x),y_predict[np.argsort(x)],color='r')
plt.show()
接下来使用多项式回归拟合,创建一个pipline管道,便于灵活调整参数
# 多项目拟合
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
def PolynomialRegression(degree):
return Pipeline([('poly',PolynomialFeatures(degree=degree)),('std_scaler',StandardScaler()),('lin_reg',LinearRegression())])
# 使用Pipeline拟合数据,多项式参数设置为 degree=2 进行预测
poly2_reg = PolynomialRegression(degree=2)
poly2_reg.fit(X,y)
y2_predict = poly2_reg.predict(X)
# 将模型结果进行拟合
plt.scatter(x,y)
plt.plot(np.sort(x),y2_predict[np.argsort(x)],color='r')
plt.show()
当把多项式参数设置为degree=10进行预测
当多项式参数设置为degree=100时进行预测
综合前面的成果,可以看出随着多项式参数degree的不断增多,拟合的效果越好,因为样本点是一定的,我们总能找到将曲线所有的样本点拟合,也就是说所有样本点都落在这条曲线上,使得整体的误差为0,曲线并不是计算出的拟合曲线,只是原有的数据点对应的y值的预测结果,而且有的地方没有数据点,因此连接的结果和原来的曲线不一样,而在未知的待预测的过程中,过量拟合的训练集会造成泛化能力的降低,预测偏差增大
说到这里,我们就要学习一下模型的泛化和正则化的概念
模型的泛化,正则化与评估方法
泛化和正则化的概念
泛化指的是机器学习模型学习到的概念在处理训练未遇到的样本时的表现,即模型处理新样本的能力
正则化指的是给需要训练的目标函数加上一些规则,目的是防止过拟合,常见的L1,L2正则化使用的正则化项分别是L1范数,L2范数
本题作为一个回归模型,常见的评估有平均绝对误差,均方误差,均方根误差和R平方值四种方法
- MAE平均绝对误差 是绝对误差的平均值,可以更好地反映预测值误差的实际情况。
-
MSE均方误差 MSE是真实值与预测值的差值的平方然后求和平均,通过平方的形式便于求导,所以常被用作线性回归的损失函数。
-
RMSE均方根误差
衡量观测值与真实值之间的偏差,常用来作为机器学习模型预测结果衡量的标准。
- R平方 也称为决定系数,反映因变量的全部变异能通过回归关系被自变量解释的比例,该值越高说明模型越好
交叉验证
交叉验证是验证分类器性能的一种统计分析方法,意义在于将原始数据分组,一部分为训练集,一部分为验证集,以此来评价分类器的性能指标,常用的交叉检验方法包括简单交叉检验,K折交叉验证,留一法交叉验证和留P法交叉验证
简单交叉检验
简单交叉检验(Cross Validation)
就是将原始数据分为训练集和验证集,利用验证集训练分类器,然后利用验证集验证模型,将最后的准确率作为此分类器的指标,通常划分30%的数据作为验证集
K折交叉检验
K折交叉检验(KFold Cross Validation)
,是将原始的数据分为K组(一般是均分),然后将每个子集数据分别做一次验证集,其余K-1组子集数据作为训练集,这样就会得到K个模型,将K个模型最终的验证集的分类准确率取平均值,作为K折交叉检验分类器的性能指标,通常设置K大于或等于3
留一法交叉验证
留一法交叉检验(LOO-CV)
是指每个训练集由除一个样本之外的其余样本组成,留下一个样本组成验证集,这样,对于N个样本的数据集,可以组成N个不同的训练集和N个不同的检验集,因此LOO-CV会得到N个模型,用这N个模型最终的验证集的分类准确率的平均数作为分类器的性能指标
留P法交叉检验
留P法交叉检验(LPO-CV)
,与留一法交叉验证类似,是从完整的数据集中删除P个样本,产生所有可能的训练集和检验集,对于N个样本,能产生(N,p)个训练-检验对
学习曲线
学习曲线是在训练集大小不同时,通过绘制模型训练集和交叉验证集上的准确率来观察模型在新数据上的表现,进而判断模型的方差或偏差是否过高,以及增大训练集是否可以减少过拟合
验证曲线
与学习曲线不同,验证曲线的横轴为某个超参数的一系列值,由此可以比较不同的参数设置下(而非不同训练集大小)的模型的准确率
特征优化
好的特征对于模型来说很重要,目前可以通过合成特征,对特征做简单变换,用决策树创建新特征,特征组合等方法对特征进行优化
合成特征
合成特征是指不再输入特征列,而是从一个或者多个输入特征衍生而来的特征,通过标准化或者缩放单独创建的特征不属于合成特征,合成特征包括以下几种类型:
-
将一个特征与其本身或者其他特征相乘
-
两个特征相除
-
对连续特征进行分桶,分为多个区间
特征的简单变换
1.特征的线性组合仅仅适用于决策树以及基于决策树的集成学习算法,如(Gradient Boosting,随机森林),因为树模型不擅长捕获不同特征之间的相关性,但是SVM,线性回归,神经网络等模型自身可以线性组合,常用的数值特征的变换和组合如下
-
多项式特征
-
比例特征
-
绝对值
-
max(x1,x2)
类别特征与数值特征的组合
用N1,N2表示数值特征,用C1,C2表示类别特征,利用pandas的groupby操作可以创造出以下几种有意义的新特征,其中,C2还可以是离散化的N1
-
中位数: median(N1) _by (C1)
-
算术平均数:mean(N1) _by (C1)
-
众数: mode(N1) _by (C1)
-
最小值: min(N1) _by (C1)
-
最大值: max(N1) _by (C1)
-
标准差: std(N1) _by (C1)
-
方差: var(N1) _by (C1)
-
频数: freq(C2)_(C1)
通过将这些特征进行有效组合,就能增加大量优秀的特征
在工业蒸汽量预测数据集上,重新构建特征,并且使用LightGBM模型对新构造的特征进行模型训练与评估的代码如下:
import pandas as pd
# 导入数据
train_data_file = '../zhengqi_train.txt'
test_data_file = '../zhengqi_test.txt'
train_data = pd.read_csv(train_data_file,sep='\t',encoding='utf-8')
test_data = pd.read_csv(test_data_file,sep='\t',encoding='utf-8')
# 特征构造方法
epsilon = 1e-5
func_dict = {
"add" : lambda x,y:x+y,
"mins" : lambda x,y:x-y,
"div" : lambda x,y:x / (y + epsilon),
"multi" : lambda x,y : x*y
}
# 特征构造函数
def auto_features_make(train_data,test_data,func_dict,col_list):
train_data,test_data = train_data.copy(),test_data.copy()
for col_i in col_list:
for col_j in col_list:
for func_name,func in func_dict.items():
for data in [train_data,test_data]:
func_features = func(data[col_i],data[col_j])
col_func_features = '-'.join([col_i,func_name,col_j])
data[col_func_features] = func_features
return train_data,test_data
# 特征降维
train_data2,test_data2 = auto_features_make(train_data,test_data,func_dict,col_list=test_data.columns)
test_data2.head(10)
from sklearn.decomposition import PCA # 主成分分析
# PCA 降维
pca = PCA(n_components=500)
train_data2_pca = pca.fit_transform(train_data2.iloc[:,0:-1])
test_data2_pca = pca.transform(test_data2)
train_data2_pca = pd.DataFrame(train_data2_pca)
test_data2_pca = pd.DataFrame(test_data2_pca)
train_data2_pca['target'] = train_data2['target']
X_train2 = train_data2[test_data2.columns].values
Y_train = train_data2['target']
print(X_train2)
# lightGBM
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error
import lightgbm as lgb
import numpy as np
# 5折交叉验证
Folds=5
kf = KFold( n_splits=Folds, random_state=2019, shuffle=True)
# 记录训练和预测MSE
MSE_DICT = {
'train_mse':[],
'test_mse':[]
}
# 线下训练预测
for i, (train_index, test_index) in enumerate(kf.split(X_train2)):
# lgb树模型
lgb_reg = lgb.LGBMRegressor(
learning_rate=0.01,
max_depth=-1,
n_estimators=5000,
boosting_type='gbdt',
random_state=2019,
objective='regression',
)
# 切分训练集和预测集
X_train_KFold, X_test_KFold = X_train2[train_index], X_train2[test_index]
y_train_KFold, y_test_KFold = y_train[train_index], y_train[test_index]
# 训练模型
lgb_reg.fit(
X=X_train_KFold,y=y_train_KFold,
eval_set=[(X_train_KFold, y_train_KFold),(X_test_KFold, y_test_KFold)],
eval_names=['Train','Test'],
early_stopping_rounds=100,
eval_metric='MSE',
verbose=50
)
# 训练集预测 测试集预测
y_train_KFold_predict = lgb_reg.predict(X_train_KFold,num_iteration=lgb_reg.best_iteration_)
y_test_KFold_predict = lgb_reg.predict(X_test_KFold,num_iteration=lgb_reg.best_iteration_)
print('第{}折 训练和预测 训练MSE 预测MSE'.format(i))
train_mse = mean_squared_error(y_train_KFold_predict, y_train_KFold)
print('------\n', '训练MSE\n', train_mse, '\n------')
test_mse = mean_squared_error(y_test_KFold_predict, y_test_KFold)
print('------\n', '预测MSE\n', test_mse, '\n------\n')
MSE_DICT['train_mse'].append(train_mse)
MSE_DICT['test_mse'].append(test_mse)
print('------\n', '训练MSE\n', MSE_DICT['train_mse'], '\n', np.mean(MSE_DICT['train_mse']), '\n------')
print('------\n', '预测MSE\n', MSE_DICT['test_mse'], '\n', np.mean(MSE_DICT['test_mse']), '\n------')
经过K折交叉检验和特征组合两个步骤,可以进一步降低MSE,提升模型的预测精度
如果觉得对小编的文章感兴趣或是喜欢的,欢迎点赞评论,我们下期见😄
推荐阅读
欢迎关注我的公众号“James的黑版报”,原创技术文章第一时间推送。
James的黑板报
一生只有一个职业:学生
29篇原创内容
公众号