DataWhale-数据挖掘学习-ModelingParameters

拟合模型

Liner Regression-线性回归

f ( x ) = w x + b f(x) = wx+b f(x)=wx+b
这是对输入和输出一个最简单的拟合,认为输入和输出之间是一个线性关系,每个输入分量在输出结果中起到一定比例的作用。
其中需要拟合的参数有 w , b w,b w,b,目前有很多方法解决这个问题,比如最小二乘法,梯度下降法等等
但需要注意的是,线性回归模型有5个前提假设

  • 线性性 & 可加性
  • 误差项(ε)之间应相互独立
  • 自变量(X1,X2)之间应相互独立
  • 误差项(ε)的方差应为常数
  • 误差项(ε)应呈正态分布
    尤其是最后一条,在一些情况下是不满足的,需要对输出数据进行一些变形。

决策树模型

决策树模型的讲解

GBDT模型

GBDT模型的讲解

XGBoost模型

XGBoost模型的讲解

LightGBM模型

LightGBM模型的讲解

这里直接附上python调用sklearn进行拟合的代码

from sklearn.linear_model import LinearRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.neural_network import MLPRegressor
from xgboost.sklearn import XGBRegressor
from lightgbm.sklearn import LGBMRegressor

models = [LinearRegression(),
          DecisionTreeRegressor(),
          RandomForestRegressor(),
          GradientBoostingRegressor(),
          MLPRegressor(solver='lbfgs', max_iter=100), 
          XGBRegressor(n_estimators = 100, objective='reg:squarederror'), 
          LGBMRegressor(n_estimators = 100)]
          
result = dict()
for model in models:
    model_name = str(model).split('(')[0]
    scores = cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error))
    result[model_name] = scores
    print(model_name + ' is finished')

嵌入式特征选择

嵌入式特征选择也就是不人工干预特征的选取,在优化时加入关于特征的选择项,让算法自己选择使用哪些特征。

最常见的是在线性回归模型里加入的系数惩罚项,L1正则化与L2正则化

在将线性回归看为一个优化问题,目标是最小化 ∑ ( y i − ( w x i + b ) ) 2 \sum (y_i-(wx_i+b))^2 (yi(wxi+b))2

而对于lasso回归和岭回归,它们在这个能量函数里加入了对系数的惩罚项

lasso要求 ∑ ( y i − ( w x i + b ) ) 2 + α ∣ ∣ w ∣ ∣ 1 1 \sum (y_i-(wx_i+b))^2 + \alpha ||w||_1^1 (yi(wxi+b))2+αw11

岭回归要求 ∑ ( y i − ( w x i + b ) ) 2 + α ∣ ∣ w ∣ ∣ 2 2 \sum (y_i-(wx_i+b))^2 + \alpha ||w||_2^2 (yi(wxi+b))2+αw22

这也就是所说的L1、L2正则化

可以在直观的来看,这两个能量函数都要求模型的系数尽可能的小,而在数学上可以证明,Lasso会是模型系数变得稀疏。一个直观的解释如图在这里插入图片描述
这里正方形就是L1模下的单位圆

from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso

models = [LinearRegression(),
          Ridge(),
          Lasso()]
          
result = dict()
for model in models:
    model_name = str(model).split('(')[0]
    scores = cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error))
    result[model_name] = scores
    print(model_name + ' is finished')

线性回归的系数如下
在这里插入图片描述
岭回归的系数如下
在这里插入图片描述
L2正则化在拟合过程中通常都倾向于让权值尽可能小,最后构造一个所有参数都比较小的模型。因为一般认为参数值小的模型比较简单,能适应不同的数据集,也在一定程度上避免了过拟合现象。可以设想一下对于一个线性回归方程,若参数很大,那么只要数据偏移一点点,就会对结果造成很大的影响;但如果参数足够小,数据偏移得多一点也不会对结果造成什么影响,专业一点的说法是『抗扰动能力强』

Lasso回归的系数如下
在这里插入图片描述
L1正则化有助于生成一个稀疏权值矩阵,进而可以用于特征选择。如下图,我们发现power与userd_time特征非常重要。

模型性能验证

模型的性能验证和过拟合问题是紧密联系在一起的。
过拟合一个直观的解释就是模型忽视了数据的规律,将训练数据背了下来。
在这里插入图片描述
在模型训练里测试集的作用就是检测最后的结果达到什么效果,在训练集上效果很好是不是因为过拟合了

但一个致命的问题是,理论上测试集要与训练集无关。假设训练了一个模型,在训练集上结果很好,高高兴兴地去测试集上跑一跑,发现结果不好,于是修改模型参数啦什么的,得到一个新的结果,继续去测试集上试一下,发现结果还不好,你继续去修改模型。如此循环往复,得到了一个很好的结果,试问选择模型是不是过拟合了?

我认为这也算一种过拟合,因为在训练时利用了测试集的信息(结果不好)。
于是一个直观的想法是再分一个集合出来,用来检测模型是不是过拟合了,这就是验证集。

在使用训练集对参数进行训练的时候,经常会发现人们通常会将一整个训练集分为三个部分(比如mnist手写训练集)。一般分为:训练集(train_set),评估集(valid_set),测试集(test_set)这三个部分。这其实是为了保证训练效果而特意设置的。其中测试集很好理解,其实就是完全不参与训练的数据,仅仅用来观测测试效果的数据。而训练集和评估集则牵涉到下面的知识了。

因为在实际的训练中,训练的结果对于训练集的拟合程度通常还是挺好的(初始条件敏感),但是对于训练集之外的数据的拟合程度通常就不那么令人满意了。因此我们通常并不会把所有的数据集都拿来训练,而是分出一部分来(这一部分不参加训练)对训练集生成的参数进行测试,相对客观的判断这些参数对训练集之外的数据的符合程度。这种思想就称为交叉验证(Cross Validation)

from sklearn.model_selection import cross_val_score
from sklearn.metrics import mean_absolute_error,  make_scorer

def log_transfer(func):
    def wrapper(y, yhat):
        result = func(np.log(y), np.nan_to_num(np.log(yhat)))
        return result
    return wrapper
    
scores = cross_val_score(model, X=train_X, y=train_y, verbose=1, cv = 5, scoring=make_scorer(log_transfer(mean_absolute_error)))

print('AVG:', np.mean(scores))

scores = pd.DataFrame(scores.reshape(1,-1))
scores.columns = ['cv' + str(x) for x in range(1, 6)]
scores.index = ['MAE']
scores

模型调参

贪心调参方法

贪心法也就是将一个多步问题一步步求解,每次找一个当前最优解。在调参时就是每次固定其他地方,调整一个参数,调整到当前最优后固定这个参数,继续调整下一个参数

best_obj = dict()
for obj in objective:
    model = LGBMRegressor(objective=obj)
    score = np.mean(cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)))
    best_obj[obj] = score
    
best_leaves = dict()
for leaves in num_leaves:
    model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0], num_leaves=leaves)
    score = np.mean(cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)))
    best_leaves[leaves] = score
    
best_depth = dict()
for depth in max_depth:
    model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0],
                          num_leaves=min(best_leaves.items(), key=lambda x:x[1])[0],
                          max_depth=depth)
    score = np.mean(cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)))
    best_depth[depth] = score
    
sns.lineplot(x=['0_initial','1_turning_obj','2_turning_leaves','3_turning_depth'], y=[0.143 ,min(best_obj.values()), min(best_leaves.values()), min(best_depth.values())])

在这里插入图片描述

网格调参方法

其实网格调参法就是个枚举,将参数域划分为网格,尝试枚举每个格点参数的模型优劣。

from sklearn.model_selection import GridSearchCV
parameters = {'objective': objective , 'num_leaves': num_leaves, 'max_depth': max_depth}
model = LGBMRegressor()
clf = GridSearchCV(model, parameters, cv=5)
clf = clf.fit(train_X, train_y)
clf.best_params_

{'max_depth': 15, 'num_leaves': 55, 'objective': 'regression'}

model = LGBMRegressor(objective='regression',
                          num_leaves=55,
                          max_depth=15)
                          
np.mean(cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)))

贝叶斯调参方法

贝叶斯方法也是在参数域里寻找一个最优解,但网格法和随机法是漫无目的的寻找,贝叶斯方法是根据之前试过的参数配置估计可能出现优质解的位置,有目的试验参数。

from bayes_opt import BayesianOptimization
def rf_cv(num_leaves, max_depth, subsample, min_child_samples):
    val = cross_val_score(
        LGBMRegressor(objective = 'regression_l1',
            num_leaves=int(num_leaves),
            max_depth=int(max_depth),
            subsample = subsample,
            min_child_samples = int(min_child_samples)
        ),
        X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)
    ).mean()
    return 1 - val
rf_bo = BayesianOptimization(
    rf_cv,
    {
    'num_leaves': (2, 100),
    'max_depth': (2, 100),
    'subsample': (0.1, 1),
    'min_child_samples' : (2, 100)
    }
)
rf_bo.maximize()
|   iter    |  target   | max_depth | min_ch... | num_le... | subsample |
-------------------------------------------------------------------------
|  1        |  0.8591   |  45.17    |  28.53    |  34.28    |  0.5116   |
|  2        |  0.8662   |  72.17    |  94.75    |  68.84    |  0.6485   |
|  3        |  0.863    |  8.271    |  88.89    |  55.83    |  0.5158   |
|  4        |  0.8651   |  8.242    |  46.95    |  78.31    |  0.2923   |
|  5        |  0.8676   |  57.41    |  62.01    |  79.51    |  0.9199   |
|  6        |  0.8692   |  97.09    |  96.4     |  98.48    |  0.1618   |
|  7        |  0.869    |  90.15    |  2.932    |  99.83    |  0.9159   |
|  8        |  0.8695   |  91.79    |  96.49    |  99.6     |  0.8113   |
|  9        |  0.8504   |  5.098    |  87.54    |  99.91    |  0.4858   |
|  10       |  0.7719   |  2.276    |  90.83    |  2.803    |  0.8641   |
|  11       |  0.869    |  95.79    |  99.28    |  96.3     |  0.5634   |
|  12       |  0.8555   |  98.19    |  3.927    |  26.92    |  0.8837   |
|  13       |  0.8063   |  2.775    |  2.932    |  43.19    |  0.8099   |
|  14       |  0.7719   |  99.81    |  99.21    |  2.276    |  0.9053   |
|  15       |  0.8669   |  98.83    |  31.76    |  72.88    |  0.8974   |
|  16       |  0.867    |  64.5     |  2.082    |  73.23    |  0.2481   |
|  17       |  0.8667   |  97.24    |  4.207    |  70.31    |  0.8119   |
|  18       |  0.8507   |  5.837    |  2.611    |  99.76    |  0.4554   |
|  19       |  0.8669   |  33.84    |  97.23    |  74.94    |  0.1564   |
|  20       |  0.8644   |  38.38    |  66.84    |  54.39    |  0.9654   |
|  21       |  0.8693   |  45.07    |  23.99    |  98.81    |  0.3903   |
|  22       |  0.8694   |  98.17    |  53.53    |  99.11    |  0.3457   |
|  23       |  0.865    |  72.71    |  32.72    |  59.46    |  0.1247   |
|  24       |  0.8673   |  41.96    |  22.85    |  75.48    |  0.9121   |
|  25       |  0.8681   |  97.57    |  76.33    |  81.99    |  0.677    |
|  26       |  0.8691   |  80.88    |  22.38    |  97.99    |  0.9399   |
|  27       |  0.869    |  58.01    |  3.743    |  99.88    |  0.918    |
|  28       |  0.8685   |  68.81    |  98.43    |  89.11    |  0.1334   |
|  29       |  0.867    |  21.72    |  70.88    |  74.84    |  0.1054   |
|  30       |  0.8691   |  99.92    |  15.64    |  99.21    |  0.872    |
=========================================================================

小结

在本章中,我们完成了建模与调参的工作,并对我们的模型进行了验证。此外,我们还采用了一些基本方法来提高预测的精度,提升如下图所示。

plt.figure(figsize=(13,5))
sns.lineplot(x=['0_origin','1_log_transfer','2_L1_&_L2','3_change_model','4_parameter_turning'], y=[1.36 ,0.19, 0.19, 0.14, 0.13])

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值