当前位置: 代码迷 >> 综合 >> datawhale 学习笔记——建模调参入门
  详细解决方案

datawhale 学习笔记——建模调参入门

热度:4   发布时间:2023-12-15 17:06:33.0

前言

居然又拖到了最后一天才开始打卡,。果然 ddl 是第一生产力。

放上这次的教程链接:Datawhale 零基础入门数据挖掘-Task4 建模调参

看标题就知道这次的这次的内容是建模和调参。虽然说是零基础入门系列,但是这次的教程对真的零基础的人来讲并不是很友好,还是需要很多前置知识的。主要是机器学习模型方面的教程,教程里给出了几篇作者写的文章。个人感觉需要一定门槛,如果看不懂建议参考其他的。

这次打卡还是按照教程走一遍,先梳理一下主要内容的结构。这次任务主体分为五大部分:

  • 线性回归模型
  • 模型验证
  • 嵌入式特征选择(上一次任务没讲的)
  • 模型对比
  • 模型调参

开始之前,先把数据准备好。

还是依旧要先读取数据,不过这次任务的读取数据有两点不同:

  1. 读取的数据是上一次任务中做特征工程时生成的,没有的话需要运行一下 task3 的代码生成一下
  2. 教程中作者写了一个 reduce_mem_usage() 函数。通过转换数据的存储类型来达到节约内存空间的目的。

代码就不全粘贴了,没什么可说的,后面需要用到的变量是 sample_featurecontinuous_feature_names

sample_feature = reduce_mem_usage(pd.read_csv('data_for_tree.csv'))
continuous_feature_names = [x for x in sample_feature.columns if x not in ['price','brand','model','brand']]

需要根据所选特征构造训练集。

# 特征处理,删除空的数据并重置数据下标和类型
sample_feature = sample_feature.dropna().replace('-', 0).reset_index(drop=True)
sample_feature['notRepairedDamage'] = sample_feature['notRepairedDamage'].astype(np.float32)
# 构造训练数据
train = sample_feature[continuous_feature_names + ['price']]
train_X = train[continuous_feature_names]
train_y = train['price']

线性回归模型

线性回归模型就是简单的线性关系,类似于 a 1 x 1 + a 2 x 2 + a 3 x 3 + . . . + a n x n + b = y a_1x_1 + a_2x_2 + a_3x_3 + ... + a_nx_n + b = y a1?x1?+a2?x2?+a3?x3?+...+an?xn?+b=y

就是说,每个自变量 x i x_i xi? 的单位变化都会导致因变量 y y y 的成比例的变化( a i x i a_ix_i ai?xi?

这里使用的是 sklearn 库下的 LinearRegression 函数。

# 简单的线性建模
from sklearn.linear_model import LinearRegression
model = LinearRegression(normalize=True)
model = model.fit(train_X, train_y)
print('intercept:'+ str(model.intercept_))
print(sorted(dict(zip(continuous_feature_names, model.coef_)).items(), key=lambda x:x[1], reverse=True))

运行结果:

intercept:-111820.66151155639
[(‘v_6’, 3372669.6439296836), (‘v_8’, 701432.2110340319), (‘v_9’, 169509.42711357534), (‘v_7’, 32757.63135064817), (‘v_12’, 23807.649529600818), (‘v_3’, 19739.216689565477), (‘v_11’, 13163.940983386958), (‘v_13’, 11963.46498465866), (‘v_10’, 3659.757920045777), (‘gearbox’, 878.481625388411), (‘fuelType’, 372.24870228828286), (‘bodyType’, 185.9175590029288), (‘city’, 46.98275955422143), (‘power’, 30.882991521559063), (‘brand_price_median’, 0.46125248465967494), (‘brand_amount’, 0.14337558626062372), (‘brand_price_std’, 0.13126146237474134), (‘brand_price_max’, 0.01264373865111078), (‘used_time’, 0.0006779416045904323), (‘SaleID’, 5.0161735988244534e-05), (‘train’, 3.982800990343094e-06), (‘seller’, -5.443580448627472e-06), (‘offerType’, -6.388872861862183e-06), (‘brand_price_sum’, -1.969811652902229e-05), (‘name’, -0.00024808160266330667), (‘brand_price_average’, -0.22234169615526606), (‘brand_price_min’, -1.9643720815997738),
(‘power_bin’, -56.46399500590243), (‘v_14’, -340.80766284241344), (‘kilometer’, -372.8800824482724), (‘notRepairedDamage’, -490.61611815679447), (‘v_0’, -2054.722036477167), (‘v_5’, -4343.72864602462), (‘v_4’, -15543.94705998868), (‘v_2’, -29430.03630720955), (‘v_1’, -45074.18455513423)]

这里我得到的结果跟教程稍微不同,猜测特征工程部分做的操作不完全一样。

可视化可以发现,预测目标拟合得并不好。由于很多模型都假设预测目标是正态分布的,因此后面对预测的目标 price 做了一个 log 操作。可视化后发现效果还不错。

v_9 单一特征进行预测,不取 log 和 取 log 的对比如下:

没取 log 的 v_9 特征预测图

取 log 的 v_9 特征预测图

模型验证

五折交叉验证

对模型进行五折交叉验证,这里需要说明的是 cross_val_score 函数,它可以将数据分为训练集和测试集,并返回每次验证时模型的得分情况。

# 交叉验证
from sklearn.model_selection import cross_val_score
from sklearn.metrics import mean_absolute_error,  make_scorerdef log_transfer(func):def wrapper(y, yhat):result = func(np.log(y), np.nan_to_num(np.log(yhat)))return resultreturn wrapper
# 使用线性模型对未经过 log 变换的数据进行预测
scores = cross_val_score(model, X=train_X, y=train_y_org, verbose=1, cv = 5, scoring=make_scorer(log_transfer(mean_absolute_error)))
print('AVG-org:', np.mean(scores))
# 使用线性模型对 log 变换后的数据进行预测
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))

运行结果如下:

[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
[Parallel(n_jobs=1)]: Done 5 out of 5 | elapsed: 1.1s finished
AVG-org: 1.3684950216832068
[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
[Parallel(n_jobs=1)]: Done 5 out of 5 | elapsed: 0.7s finished
AVG: 0.02487489588467453

可以看到,做 log 处理后,模型的效果提升了两个数量级。

模拟真实环境

需要说明的一点是,有时数据是存在时序性的,我们不能拥有上帝视角预测未来的数据。因此划分验证集时,需要考虑数据的时序性,模拟真实环境。即前一部分是训练集,后一部分是验证集,验证集后不应再有训练集。这是作者当时直播强调的一点,不过在这里,并没有什么影响。

绘制学习率曲线

这里利用了教程里的 plot_learning_curve 函数来绘制,其中最主要的是调用了 learning_curve 来获得测试和训练时的得分。

plot_learning_curve(LinearRegression(), 'Liner_model', train_X[:1000], train_y[:1000], ylim=(0.0, 0.5), cv=5, n_jobs=1)  
plt.show()

运行结果:

学习率曲线

嵌入式特征选择

上一次任务中讲的,特征选择一共有三种。前两种是过滤式和包裹式,他们在做特征选择时,可以明显的与训练过程分离开。但嵌入式特征选择是边训练便选择,典型的方法是L1正则化与L2正则化。(以前只知道L1、L2正则化,但并不知道这是属于嵌入式特征选择,。孤陋寡闻了)

线性回归模型加入两种正则化方法后,就分别变成了岭回归与Lasso回归。

代码粘贴一下:

from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lassomodels = [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] = scoresprint(model_name + ' is finished')result = pd.DataFrame(result)
result.index = ['cv' + str(x) for x in range(1, 6)]
print(result)

运行结果:

		LinearRegression     Ridge     Lasso
cv1          0.190851  0.194948  0.383810
cv2          0.193835  0.197714  0.382179
cv3          0.194196  0.198252  0.383611
cv4          0.191812  0.195694  0.380331
cv5          0.195868  0.199817  0.383783

这里简单介绍一下 L1 和 L2 的区别,就不粘贴教程里的可视化代码了。

L1 的惩罚项是权值的绝对值之和,因此学到的权值参数小的会尽可能趋向于0,大的会尽可能变大,最后会得到一个稀疏矩阵,0 很多;L2 的惩罚项是权值的平方和,因此权值的绝对值会尽可能小,这样平方求和后的值就会变小,最后得到一个所有权值都向 0 靠近的参数矩阵。

模型对比

这部分教程介绍的比较少,主要是做了一个简单模型性能的对比,包括线性模型、决策树模型、随机森林、梯度提升树、多层感知机、XGBoost 和 LGBM。

运行教程代码的结果:

模型对比结果

可以明显看出来,随机森林、XGBoost 和 LGBM的效果比较好。

模型调参

主要有三种方式:贪心、网格和贝叶斯。

需要提前设定好可能的参数集合。

这部分代码就不粘贴了,主要记录一下我的理解。

贪心调参

主要思路是遍历每一个参数,使用其中效果最好的参数继续训练模型,搜索下一个参数。

网格搜索

教程里使用的是 sklearn.model_selection 库里的 GridSearchCV 函数。网格调参是一种穷举搜索方法,遍历所有的参数组合,选出最好的那一组。

贝叶斯调参

教程中使用的是 BayesianOptimization 函数来做的。

贝叶斯调参的方法是一种自动调参的方式。前两种调参的方法运行效率比较低,运行时间很慢。贝叶斯的方法在于,他会建立一个概率模型,基于以前的评估结果,来最小化这个模型值。简而言之,在尝试下一组参数时,会参考以前的参数结果,避免做无用功。

最后

不得不说,还是高估了自己的自控力,拖延症真的得治呀。

不过这次学习还是学到了很多东西,虽然很多东西没有深入了解。最让人兴奋的还是大佬的各种可视化操作,看着各种数据被各种可视化出来,感觉好TM神奇。

好好学习,天天向上。又是瞎捅咕的一天,没干毕设的事内心还是有点愧疚。

  相关解决方案