4月18日
比赛第一天,刚拿到数据,简单粗略的看了一遍数据,已将uer,ad_operation,ad_feature三个文件的里的杂乱数据转换成csv下的数据.
发现的一些问题:
User文件
这里user_id 其实是乱排的,所以它不是连续的整型数据.所以如果用字符串处理它也是可以的,只要之后用sklean再重新编码就行了
性别里是由1,2,3的数值来表示,也就是说存在不确定性别这一说
这里婚恋状况(Status)是取多值的,暂时将其保存成了字符串的形式. 常见的数值是0-18,不知道为什么会有那么多状态.
Education很规矩,1-8取其中的任意一个值.消费能力(Consuption)也很规矩,1-3取其中的任意一个值
device:这个特征很调皮,取了0,2,3,4,所以之后也要用sklearn重新编码一下
work:是可以取多值的,0-6的取值互相组合,统计下来有以下几种组合:
[0]、[1]、[2]、[2,1]、[2,4]、[2,5]、[2,6]、[2,6,1]、[2,6,4]、[2,6,5]、[3]、[3,2]、[3,2,6]、[3,6]、[4]、[5]、[6]、[6,1]、[6,4]、[6,5]
之后也可以用sklearn重新编码一下连接类型:很规矩,1-5,都是单值
behavior:暂时存成了字符串形式,暂时没有想法
ad_feature
ad_id都是唯一的,但发现日志里的广告数少于这里的广告数,说明有一些广告是没有在日志里曝光过的,并且说明书里也有提及测试数据里会有新的广告,这时我想到的是推荐系统里的协同过滤,细节还没有想清楚
create_time:是创造的时间,以时间戳的形式存储,后续应该怎么处理,可以参考一下以前比赛的处理方式
account_id: 从1开始一直到29737,只取单值.看到有不同广告同一个account_id,很有可能是一家公司,或是什么的.这个到时候可以进行补全缺失值用。还有就是暂时没弄懂账户结构分为4级的意思.
merch_id:这个说明书上写的是唯一标识,但却找到了不唯一的ad,并且里面的格式貌似也是不统一的,要到时候修正一下(重点处理该特征)
merch_type:很干净,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18(注意少了9,糟老头坏的很)。
ad_industry:可以取多值,暂时保存成了字符串形式.
ad_size:存在缺省值:说是可以取多值,但是暂时没看到多个值,还要再观察观察.
ad_operation
- ad_id:会出现多次
- time:这个时间是创建时间或者是修改时间,当数值为0表示创建时间,你可以在上面的ad_feature里找到具体的创建时间,非零的情况是修改时间,这个时间并不是时间戳的形式,而是20170217000000这样的形式(个人觉得没什么用)
- op_type:操作的类型,创建是2,修改是1,创建时time就为0
- modify:修改字段,只出现1,2,3,4,指明要修改的特征
- change_value:是改后的值,现在保存成了字符串的形式,之后可以考虑多弄几列分别表示更改广告状态,出价,人群定向和广告时段设置.注意广告人群定向里有all.
其他
看了第一天的日志(2.16),
- 统计了一下ad_position_id,有些位置的曝光次数特别的多.可以认为是常用广告位.
- 同一时间的请求,同一个广告位上可能存在多个广告.
- 之前说过要改user_id,注意一定要与日志里的同时改.
- 同一天里同一个广告,很有可能专门出现在一个位置,大小也倾向相同,当然这也不绝对,还是到时候要画图一下看看
- 另外,同一天的同一个曝光广告的bid可能是不同,个人认为不会是因为广告频繁被修改价格,而是因为GSP竞价机制,最终赢的广告的价格是由第二名决定的.我看GSP的概念的时候,认为这时与时间强相关的一种竞价方式,不知道这里会不会用到时间序列.
- 有些广告曝光次数很多,有些很少,应该也是符合长尾分布.具体还要画图才能知道.
TODO
- 把user,ad_feature,ad_op里的独立的特征都进行编码,所谓独立的特征就是与其他表不相关的特征
- 对ad_operation里的change_value处理.ad_operation里的time改成时间戳的形式
- 考虑一些需要显示的图:
a. 日志里31天每天的请求次数(不重复)可以绘制成图,可以反映,这些用户的用手机的习惯
b. 看个别几条广告31天的曝光次数变化,参照着它的操作.
c. 也需要考虑用户这31天的请求次数(不重复)的图,可以反映个人习惯.
d. 考虑单天里位置,尺寸的影响.
….待补充….
一些计算广告专业数值与曝光的关系需要好好研究研究.4月19日
发现ad_op中其实创建修改一般都是同时进行的.
绝大部分创建时就修改了属性
有1522个数据是创建时就设置了失活.
有231493次操作要设置为正常.
有269309次操作要设置为失活.
而暂时未知,创建了广告之后状态是算失活还是正常.
(个人倾向于一创建就算正常,否则创建设置失活这一操作就毫无意义.但不确定需要与log中进行比较查看才能确定.)
Done
user
device:从[0,2,3,4]映射到了[0,1,2,3]
work:[‘0’,’1’,’2’,’2,1’,’2,4’,’2,5’,’2,6’,’2,6,1’,’2,6,4’,’2,6,5’,’3’,’3,2’,’3,2,6’,’3,6’,’4’,’5’,’6’,’6,1’,’6,4’,’6,5’]映射到[0,…19]
ad_feature
将merch_type与test里的merch_type统一
[1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18]映射到[0,….,16]
ad_id为388994,219111,56723的广告,商品id不唯一
388994:(6199,28123)
219111:(6199,28123)
56723:(19196,26277)
暂且只有一个广告的size是两个值的.
ad_op
在将time转化成时间戳的过程中发现了一些无效的时间,比如,20190230,这个日期会存在我准备将其保存成-1(大概有1292个)
将chang_value那一列扩展成了4列,每列分别对应于修改字段修改的值.因为每行每列都要用数字填充,所以用-1填充.
TODO
要给日志里的时间找到对应的周几.
以及对ad_op文件里的投放时间段进行处理,以便以后方便找到投放时间.
4月20日
ad_op
已经确定ad_op的数据里time=0且op_type=1的数据为脏数据。这些数据没有后续操作了
测试数据有20000条。
不知道人群定向以外的人会收到该广告的曝光么?
广告之间是会相互影响的,不能一条一条的输入.
(广告竞争!!)
现已将ad_op里的time=0且op_type=2的数据的time=0改成其创造时间,有些操作的广告没有静态数据,则已经被用0来填充了.time里的-1表示时间戳有问题.
ad_op里进行过操作的不同广告数为37000多(已经剃除了脏数据)
ad_feature
静态数据里创建时间存在0,缺省值(9290个)
account_id不存在缺省值
merch_id存在落地页,也许不算缺省值
ad_industry不存在缺省值
ad_size有缺失
用户画像的方法
用户画像构建方法分成三类:
第一类就是查户口。直接使用原始数据作为用户画像的内容,如注册资料等人口统计学信息,或者购买历史,阅读历史等,除了数据清洗等工作,数据本身并没有做任何抽象和归纳。这就跟查户口一样,没什么技术含量,但通常对于用户冷启动等场景非常有用。
第二类就是堆数据。方法就是堆积历史数据,做统计工作,这是最常见的用户画像数据,常见的兴趣标签,就是这一类,就是从历史行为数据中去挖掘出标签,然后在标签维度上做数据统计,用统计结果作为量化结果。这一类数据贡献了常见的酷炫用户画像
- 第三类就是黑盒子。就是用机器学习方法,学习出人类无法直观理解的稠密向量,也最不被非技术人员重视,但实际上在推荐系统中承担的作用非常大。比如使用潜语义模型构建用户阅读兴趣,或者使用矩阵分解得到的隐因子,或者使用深度学习模型学习用户的 Embedding 向量。这一类用户画像数据因为通常是不可解释,不能直接被人看懂。
TODO
- 得到User的候选广告
4.25
每天的数据已经得到
目前已经得到多个统计数据
暂时的发现,日志里的曝光广告的ad_size都不为-1,且曝光广告位的大小与广告大小严格一致
且统计了每天的各个大小的曝光广告位的个数,应该是一致的分布
找到各个广告位的id所匹配的广告位大小
TODO
pctr与广告位有关
各个广告位的个数在每天是差不多的,也就是说可以的到预测一天广告位的个数
广告位与广告尺寸的大小关系是固定的,广告位只与特定的几个广告尺寸相关
4.27
log
quality_ecpm为0的个数,在一天中各个时段有周期性变化,到午后达到峰值,再到9点多达到另一个峰值
5月1日
比赛模型一般框架
Preprocess
1 | # 通用的预处理框架 |
LogisticRegression
1 | # 通用的LogisticRegression框架 |
LightGBM
二分类
1
2
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73import lightgbm as lgb
import pandas as pd
import numpy as np
import pickle
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
print("Loading Data ... ")
# 导入数据
train_x, train_y, test_x = load_data()
# 用sklearn.cross_validation进行训练数据集划分,这里训练集和交叉验证集比例为7:3,可以自己根据需要设置
X, val_X, y, val_y = train_test_split(
train_x,
train_y,
test_size=0.05,
random_state=1,
stratify=train_y ## 这里保证分割后y的比例分布与原数据一致
)
X_train = X
y_train = y
X_test = val_X
y_test = val_y
# create dataset for lightgbm
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
# specify your configurations as a dict
params = {
'boosting_type': 'gbdt',
'objective': 'binary',
'metric': {'binary_logloss', 'auc'},
'num_leaves': 5,
'max_depth': 6,
'min_data_in_leaf': 450,
'learning_rate': 0.1,
'feature_fraction': 0.9,
'bagging_fraction': 0.95,
'bagging_freq': 5,
'lambda_l1': 1,
'lambda_l2': 0.001, # 越小l2正则程度越高
'min_gain_to_split': 0.2,
'verbose': 5,
'is_unbalance': True
}
# train
print('Start training...')
gbm = lgb.train(params,
lgb_train,
num_boost_round=10000,
valid_sets=lgb_eval,
early_stopping_rounds=500)
print('Start predicting...')
preds = gbm.predict(test_x, num_iteration=gbm.best_iteration) # 输出的是概率结果
# 导出结果
threshold = 0.5
for pred in preds:
result = 1 if pred > threshold else 0
# 导出特征重要性
importance = gbm.feature_importance()
names = gbm.feature_name()
with open('./feature_importance.txt', 'w+') as file:
for index, im in enumerate(importance):
string = names[index] + ', ' + str(im) + '\n'
file.write(string)多分类
1
2
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72import lightgbm as lgb
import pandas as pd
import numpy as np
import pickle
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
print("Loading Data ... ")
# 导入数据
train_x, train_y, test_x = load_data()
# 用sklearn.cross_validation进行训练数据集划分,这里训练集和交叉验证集比例为7:3,可以自己根据需要设置
X, val_X, y, val_y = train_test_split(
train_x,
train_y,
test_size=0.05,
random_state=1,
stratify=train_y ## 这里保证分割后y的比例分布与原数据一致
)
X_train = X
y_train = y
X_test = val_X
y_test = val_y
# create dataset for lightgbm
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
# specify your configurations as a dict
params = {
'boosting_type': 'gbdt',
'objective': 'multiclass',
'num_class': 9,
'metric': 'multi_error',
'num_leaves': 300,
'min_data_in_leaf': 100,
'learning_rate': 0.01,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_freq': 5,
'lambda_l1': 0.4,
'lambda_l2': 0.5,
'min_gain_to_split': 0.2,
'verbose': 5,
'is_unbalance': True
}
# train
print('Start training...')
gbm = lgb.train(params,
lgb_train,
num_boost_round=10000,
valid_sets=lgb_eval,
early_stopping_rounds=500)
print('Start predicting...')
preds = gbm.predict(test_x, num_iteration=gbm.best_iteration) # 输出的是概率结果
# 导出结果
for pred in preds:
result = prediction = int(np.argmax(pred))
# 导出特征重要性
importance = gbm.feature_importance()
names = gbm.feature_name()
with open('./feature_importance.txt', 'w+') as file:
for index, im in enumerate(importance):
string = names[index] + ', ' + str(im) + '\n'
file.write(string)
XGBoost
- 二分类
1
2
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72import numpy as np
import pandas as pd
import xgboost as xgb
import time
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import train_test_split
train_x, train_y, test_x = load_data()
# 构建特征
# 用sklearn.cross_validation进行训练数据集划分,这里训练集和交叉验证集比例为7:3,可以自己根据需要设置
X, val_X, y, val_y = train_test_split(
train_x,
train_y,
test_size=0.01,
random_state=1,
stratify=train_y
)
# xgb矩阵赋值
xgb_val = xgb.DMatrix(val_X, label=val_y)
xgb_train = xgb.DMatrix(X, label=y)
xgb_test = xgb.DMatrix(test_x)
# xgboost模型 #####################
params = {
'booster': 'gbtree',
# 'objective': 'multi:softmax', # 多分类的问题、
# 'objective': 'multi:softprob', # 多分类概率
'objective': 'binary:logistic',
'eval_metric': 'logloss',
# 'num_class': 9, # 类别数,与 multisoftmax 并用
'gamma': 0.1, # 用于控制是否后剪枝的参数,越大越保守,一般0.1、0.2这样子。
'max_depth': 8, # 构建树的深度,越大越容易过拟合
'alpha': 0, # L1正则化系数
'lambda': 10, # 控制模型复杂度的权重值的L2正则化项参数,参数越大,模型越不容易过拟合。
'subsample': 0.7, # 随机采样训练样本
'colsample_bytree': 0.5, # 生成树时进行的列采样
'min_child_weight': 3,
# 这个参数默认是 1,是每个叶子里面 h 的和至少是多少,对正负样本不均衡时的 0-1 分类而言
# ,假设 h 在 0.01 附近,min_child_weight 为 1 意味着叶子节点中最少需要包含 100 个样本。
# 这个参数非常影响结果,控制叶子节点中二阶导的和的最小值,该参数值越小,越容易 overfitting。
'silent': 0, # 设置成1则没有运行信息输出,最好是设置为0.
'eta': 0.03, # 如同学习率
'seed': 1000,
'nthread': -1, # cpu 线程数
'missing': 1,
'scale_pos_weight': (np.sum(y==0)/np.sum(y==1)) # 用来处理正负样本不均衡的问题,通常取:sum(negative cases) / sum(positive cases)
# 'eval_metric': 'auc'
}
plst = list(params.items())
num_rounds = 2000 # 迭代次数
watchlist = [(xgb_train, 'train'), (xgb_val, 'val')]
# 交叉验证
result = xgb.cv(plst, xgb_train, num_boost_round=200, nfold=4, early_stopping_rounds=200, verbose_eval=True, folds=StratifiedKFold(n_splits=4).split(X, y))
# 训练模型并保存
# early_stopping_rounds 当设置的迭代次数较大时,early_stopping_rounds 可在一定的迭代次数内准确率没有提升就停止训练
model = xgb.train(plst, xgb_train, num_rounds, watchlist, early_stopping_rounds=200)
model.save_model('../data/model/xgb.model') # 用于存储训练出的模型
preds = model.predict(xgb_test)
# 导出结果
threshold = 0.5
for pred in preds:
result = 1 if pred > threshold else 0
lightGBM
使用Dataset构建数据到lgb中1
2
3
4
5
6
7
8
9
10import lightgbm as lgb
import numpy as np
# 训练数据,500个样本,10个维度
train_data = np.random.rand(500, 10)
# 构建二分类数据
label = np.random.randint(2, size=500)
# 放入到dataset中
train = lgb.Dataset(train_data, label=label)
print(train)
还有下列清晰构建数据方式,指定 feature names(特征名称)和 categorical features(分类特征):1
train_data = lgb.Dataset(data, label=label, feature_name=['c1', 'c2', 'c3'], categorical_feature=['c3'])
分类特征可以人为制定,使用categorical_feature选取你制定的名称
XGBoost
在用XGBoost模型进行预测之前先对XGBoost进行简单的介绍
XGBoost模型有两种使用方式,一种是原生版本,一种是实现了sklearn接口的版本。
XGBoost的原生版本参数介绍
General Parameters
- booster [default=gbtree]:可选项为gbtree,gblinear或dart;其中gbtree和dart是使用基于树模型的,而gblinear是使用基于线性模型的;
- silent [default=0]:0表示输出运行信息,1表示不输出;
- nthread [如果不进行设置,默认是最大线程数量]:表示XGBoost运行时的并行线程数量;
- disable_default_eval_metric [default=0]:标记以禁用默认度量标准。设置 >0 表示禁用;
- num_pbuffer [通过XGBoost自动设置,不需要用户来设置]:预测缓冲区的大小,通常设置为训练实例的数量;
- num_feature [通过XGBoost自动设置,不需要用户来设置]:被使用在boosting中的特征维度,设置为最大化的特征维度;
Parameters for Tree Booster:
- eta (default=0.3, 别名: learning_rate) :eta表示学习率:range:[0, 1] ,作用:防止过拟合;
- gamma [default=0, 别名: min_split_loss]: 在树的叶节点上进一步分区所需的最小化损失减少,gamma越大算法越保守 range:[0, ∞];
- max_depth [default=6]:表示树的深度,值越大模型越复杂,越容易过拟合。0表示不限制;
- min_child_weight [default=1]:子节点所需要的最小样本权重之和。如果一个叶子节点的样本权重和小于min_child_weight结束节点进一步的切分。在线性回归模型中,这个参数是指建立每个模型所需要的最小样本数。该值越大,算法越保守;
- max_delta_step [default=0]:我们允许每个叶子输出的最大的delta step,该值为0,表示不限制。该值为正数,可以帮助使更新步骤更加保守。通常该参数不需要设置,但是在logistic回归中,分类类别极度不平衡的时候,将该值设置在1_10之间可以帮助控制更新步骤;
- subsample [default=1]:训练数据的子样本,subsample=n,表示在训练数据中随机采样n%的样本,可以防止过拟合。 range:(0, 1] ;
- lambda [default=1, 别名: reg_lambda]: L2正则化项系数;
- alpha [default=0, 别名: reg_alpha]: L1正则化项系数;
- tree_method string [default= auto]:在分布式和外存的版本中,仅支持 tree_method=approx;可选项为:auto, exact, approx, hist, gpu_exact, gpu_hist
- auto:表示使用启发式的方法来选择使运行速度最快的算法,如下:
1.1 对于小到中等的数据集,Exact Greedy Algorithm将被使用;
1.2 对于大数据集,Approximate Algorithm将被使用;
1.3 因为以前的行为总是在单个机器中使用Exact Greedy Algorithm,所以当选择Approximate Algorithm来通知该选择时,用户将得到消息。 - Exact Greedy Algorithm
- approx:Approximate Algorithm
- hist:快速直方图优化近似贪心算法。它使用了一些可以改善性能的方法,例如bins caching;
- gpu_exact:在GPU上执行Exact Greedy Algorithm;
- gpu_hist:在GPU上执行hist算法;
- max_leaves [default=0]:设置叶节点的最大数量,仅仅和当row_policy=lossguide才需要被设置;
- max_bin, [default=256]:仅仅tree_method=hist时,该方法需要去设置。bucket连续特征的最大离散bins数量;
学习任务参数(Learning Task Parameters)
- objective [default=reg:linear]
- reg:linear:线性回归;
- reg:logistic:逻辑回归;
- binary:logistic: 二分类逻辑回归,输出概率;
- binary:logitraw: 二分类逻辑回归,在logistic transformation之前输出score;
- binary:hinge: 二分类的hinge损失,让预测为0或1,而不是概率;
- multi:softmax:多分类的使用softmax目标函数,使用此含参数时需要指定多分类分为几类,设置num_class=n;
- multi:softprob: 和softmax相同,但是输出的是每个样本点属于哪个类的预测概率值;
- rank:pairwise:使用XGBoost做排序任务使用的。
- base_score [default=0.5]:所有实例的初始预测分数,全局偏差。对于有足够的迭代数目,改变该值将不会太多的影响;
- eval_metric [default according to objective] :默认:根据objective参数(回归:rmse, 分类:error)。还有许多可以自己查官方API。
使用XGBoost原生版本模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19X_train,X_test,y_train,y_test = train_test_split(df_train,target,test_size = 0.3,random_state = 1)
data_train = xgb.DMatrix(X_train, y_train) # 使用XGBoost的原生版本需要对数据进行转化
data_test = xgb.DMatrix(X_test, y_test)
param = {'max_depth': 5, 'eta': 1, 'objective': 'binary:logistic'}
watchlist = [(data_test, 'test'), (data_train, 'train')]
n_round = 3
booster = xgb.train(param, data_train, num_boost_round=n_round, evals=watchlist)
# 计算错误率
y_predicted = booster.predict(data_test)
y = data_test.get_label()
accuracy = sum(y == (y_predicted > 0.5))
accuracy_rate = float(accuracy) / len(y_predicted)
print ('样本总数:{0}'.format(len(y_predicted)))
print ('正确数目:{0}'.format(accuracy) )
print ('正确率:{0:.3f}'.format((accuracy_rate)))
XGBoost的sklearn接口版本参数介绍
因为XGBoost是使用的是一堆CART树进行集成的,而CART(Classification And Regression Tree)树即可用作分类也可用作回归,这里仅仅介绍XGBoost的分类,回归问题类似,有需要请访问XGBoost API的官网进行查看。
class xgboost.XGBClassifier(max_depth=3, learning_rate=0.1, n_estimators=100, silent=True, objective=’binary:logistic’, booster=’gbtree’, n_jobs=1, nthread=None, gamma=0, min_child_weight=1, max_delta_step=0, subsample=1, colsample_bytree=1, colsample_bylevel=1, reg_alpha=0, reg_lambda=1, scale_pos_weight=1, base_score=0.5, random_state=0, seed=None, missing=None, ** kwargs)
- max_depth : int 表示基学习器的最大深度;
- learning_rate : float 表示学习率,相当于原生版本的 “eta”;
- n_estimators: int 表示去拟合的boosted tree数量;
- silent:boolean 表示是否在运行boosting期间打印信息;
- objective:string or callable 指定学习任务和相应的学习目标或者一个自定义的函数被使用,具体看原生版本的objective;
- booster:string 指定要使用的booster,可选项为:gbtree,gblinear 或 dart;
- n_jobs:int 在运行XGBoost时并行的线程数量。
- gamma:float 在树的叶节点上进行进一步分区所需的最小损失的减少值,即加入新节点进入的复杂度的代价;
- min_child_weight : int 在子节点中实例权重的最小的和;
- max_delta_step : int 我们允许的每棵树的权重估计最大的delta步骤;
- subsample :float 训练样本的子采样率;
- colsample_bytree :float 构造每个树时列的子采样率。
- colsample_bylevel :float 在每一层中的每次切分节点时的列采样率;
- reg_alpha :float 相当于原生版本的alpha,表示L1正则化项的权重系数;
- reg_lambda: float 相当于原生版本的lambda,表示L2正则化项的权重系数;
- scale_pos_weight:float 用来平衡正负权重;
- base_score: 所有实例的初始预测分数,全局偏差;
- random_state:int 随机种子;
- missing:float,optional 需要作为缺失值存在的数据中的值。 如果为None,则默认为np.nan。
XGBoost的sklearn的接口版本用法与sklearn中的模型的用法相同,这里简单的进行使用
1 | X_train,X_test,y_train,y_test = train_test_split(df_train,target,test_size = 0.3,random_state = 1) |
Randomforest
在scikit-learn中,RandomForest的分类类是RandomForestClassifier,回归类是RandomForestRegressor,需要调参的参数包括两部分,第一部分是Bagging框架的参数,第二部分是CART决策树的参数。
sklearn官网地址(RandomForestClassifier):http://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html#sklearn.ensemble.RandomForestClassifier
1 | class sklearn.ensemble.RandomForestClassifier(n_estimators=10, criterion='gini', max_depth=None,min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0,max_features='auto', max_leaf_nodes=None, min_impurity_split=1e-07,bootstrap=True, oob_score=False, n_jobs=1, random_state=None, verbose=0,warm_start=False, class_weight=None) |
- 参数解读
1.1 Bagging框架的参数
和GBDT对比,GBDT的框架参数比较多,重要的有最大迭代器个数,步长和子采样比例,调参起来比较费力。但是RF则比较简单,这是因为bagging框架里的各个弱学习器之间是没有依赖关系的,这减小的调参的难度。换句话说,达到同样的调参效果,RF调参时间要比GBDT少一些。下面我来看看RF重要的Bagging框架的参数,由于RandomForestClassifier和RandomForestRegressor参数绝大部分相同,这里会将它们一起讲,不同点会指出。
- n_estimators: 也就是弱学习器的最大迭代次数,或者说最大的弱学习器的个数,默认是10。一般来说n_estimators太小,容易欠拟合,n_estimators太大,又容易过拟合,一般选择一个适中的数值。对Random Forest来说,增加“子模型数”(n_estimators)可以明显降低整体模型的方差,且不会对子模型的偏差和方差有任何影响。模型的准确度会随着“子模型数”的增加而提高,由于减少的是整体模型方差公式的第二项,故准确度的提高有一个上限。在实际应用中,可以以10为单位,考察取值范围在1至201的调参情况。
- bootstrap:默认True,是否有放回的采样。
- oob_score :默认识False,即是否采用袋外样本来评估模型的好坏。有放回采样中大约36.8%的没有被采样到的数据,我们常常称之为袋外数据(Out Of Bag, 简称OOB),这些数据没有参与训练集模型的拟合,因此可以用来检测模型的泛化能力。个人推荐设置为True,因为袋外分数反应了一个模型拟合后的泛化能力。对单个模型的参数训练,我们知道可以用cross validation(cv)来进行,但是特别消耗时间,而且对于随机森林这种情况也没有大的必要,所以就用这个数据对决策树模型进行验证,算是一个简单的交叉验证,性能消耗小,但是效果不错。
- criterion: 即CART树做划分时对特征的评价标准,分类模型和回归模型的损失函数是不一样的。分类RF对应的CART分类树默认是基尼系数gini,另一个可选择的标准是信息增益entropy,是用来选择节点的最优特征和切分点的两个准则。回归RF对应的CART回归树默认是均方差mse,另一个可以选择的标准是绝对值差mae。一般来说选择默认的标准就已经很好的。
从上面可以看出, RF重要的框架参数比较少,主要需要关注的是 n_estimators,即RF最大的决策树个数。
1.2 RF决策树的参数
RF的决策树参数,它要调参的参数基本和GBDT相同,如下: - max_features: RF划分时考虑的最大特征数。可以使用很多种类型的值,默认是”None”,意味着划分时考虑所有的特征数;如果是”log2”意味着划分时最多考虑log2N个特征;如果是”sqrt”或者”auto”意味着划分时最多考虑N−−√N个特征。如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比xN)取整后的特征数,其中N为样本总特征数。一般来说,如果样本特征数不多,比如小于50,我们用默认的”None”就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。
- max_depth: 决策树最大深度。默认为”None”,决策树在建立子树的时候不会限制子树的深度这样建树时,会使每一个叶节点只有一个类别,或是达到min_samples_split。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。
- min_samples_split: 内部节点再划分所需最小样本数,默认2。这个值限制了子树继续划分的条件,如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。 默认是2.如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
- min_samples_leaf:叶子节点最少样本数。 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
- min_weight_fraction_leaf:叶子节点最小的样本权重和。这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。 默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
- max_leaf_nodes: 最大叶子节点数。通过限制最大叶子节点数,可以防止过拟合,默认是”None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。
- min_impurity_split: 节点划分最小不纯度。这个值限制了决策树的增长,如果某节点的不纯度(基于基尼系数,均方差)小于这个阈值,则该节点不再生成子节点,即为叶子节点 。一般不推荐改动默认值1e-7。
上面决策树参数中最重要的包括最大特征数max_features, 最大深度max_depth, 内部节点再划分所需最小样本数min_samples_split和叶子节点最少样本数min_samples_leaf。 - splitter: 随机选择属性”random”还是选择不纯度最大”best”的属性,建议用默认 best。
- presort:是否对数据进行预分类,以加快拟合中最佳分裂点的发现。默认False,适用于大数据集。小数据集使用True,可以加快训练。是否预排序,预排序可以加速查找最佳分裂点,对于稀疏数据不管用,Bool,auto:非稀疏数据则预排序,若稀疏数据则不预排序
1.3 进行预测的几种常用方法 - predict_proba(x):给出带有概率值的结果。每个点在所有label(类别)的概率和为1.
- predict(x):直接给出预测结果。内部还是调用的predict_proba(),根据概率的结果看哪个类型的预测值最高就是哪个类型。
- predict_log_proba(x):和predict_proba基本上一样,只是把结果给做了log()处理。
1.4 RandomForest调参实例1
2
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172#导入需要的库
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.grid_search import GridSearchCV
from sklearn import cross_validation, metrics
import matplotlib.pylab as plt
%matplotlib inline
#导入数据,顺便看看数据的类别分布
train= pd.read_csv('C:\\Users\\86349\\Desktop\\train_modified\\train_modified.csv')
target='Disbursed' # Disbursed的值就是二元分类的输出
IDcol= 'ID'
train['Disbursed'].value_counts()
#可以看到类别输出如下,也就是类别0的占大多数:
0 19680
1 320
Name:Disbursed, dtype: int64
#接着选择好样本特征和类别输出,样本特征为除去ID和输出类别的列
x_columns = [x for x in train.columns if x not in [target,IDcol]]
X = train[x_columns]
y = train['Disbursed']
#不管任何参数,都用默认的,拟合下数据看看
rf0 = RandomForestClassifier(oob_score=True, random_state=10)
rf0.fit(X,y)
print rf0.oob_score_
y_predprob = rf0.predict_proba(X)[:,1]
print "AUC Score (Train): %f" % metrics.roc_auc_score(y,y_predprob)
#输出如下:0.98005 AUC Score (Train): 0.999833
#可见袋外分数已经很高(理解为袋外数据作为验证集时的准确率,也就是模型的泛化能力),而且AUC分数也很高(AUC是指从一堆样本中随机抽一个,抽到正样本的概率比抽到负样本的概率 大的可能性)。相对于GBDT的默认参数输出,RF的默认参数拟合效果对本例要好一些。
#首先对n_estimators进行网格搜索
param_test1= {'n_estimators':range(10,71,10)}
gsearch1= GridSearchCV(estimator = RandomForestClassifier(min_samples_split=100,
min_samples_leaf=20,max_depth=8,max_features='sqrt' ,random_state=10),
param_grid =param_test1, scoring='roc_auc',cv=5)
gsearch1.fit(X,y)
gsearch1.grid_scores_,gsearch1.best_params_, gsearch1.best_score_
#输出结果如下:
([mean:0.80681, std: 0.02236, params: {'n_estimators': 10},
mean: 0.81600, std: 0.03275, params:{'n_estimators': 20},
mean: 0.81818, std: 0.03136, params:{'n_estimators': 30},
mean: 0.81838, std: 0.03118, params:{'n_estimators': 40},
mean: 0.82034, std: 0.03001, params:{'n_estimators': 50},
mean: 0.82113, std: 0.02966, params:{'n_estimators': 60},
mean: 0.81992, std: 0.02836, params:{'n_estimators': 70}],
{'n_estimators':60},
0.8211334476626017)
#这样我们得到了最佳的弱学习器迭代次数,接着我们对决策树最大深度max_depth和内部节点再划分所需最小样本数min_samples_split进行网格搜索。
param_test2= {'max_depth':range(3,14,2), 'min_samples_split':range(50,201,20)}
gsearch2= GridSearchCV(estimator = RandomForestClassifier(n_estimators= 60,
min_samples_leaf=20,max_features='sqrt' ,oob_score=True,random_state=10),
param_grid = param_test2,scoring='roc_auc',iid=False, cv=5)
gsearch2.fit(X,y)
gsearch2.grid_scores_,gsearch2.best_params_, gsearch2.best_score_
#输出如下:
([mean:0.79379, std: 0.02347, params: {'min_samples_split': 50, 'max_depth': 3},
mean: 0.79339, std: 0.02410, params:{'min_samples_split': 70, 'max_depth': 3},
mean: 0.79350, std: 0.02462, params:{'min_samples_split': 90, 'max_depth': 3},
mean: 0.79367, std: 0.02493, params:{'min_samples_split': 110, 'max_depth': 3},
mean: 0.79387, std: 0.02521, params:{'min_samples_split': 130, 'max_depth': 3},
mean: 0.79373, std: 0.02524, params:{'min_samples_split': 150, 'max_depth': 3},
mean: 0.79378, std: 0.02532, params:{'min_samples_split': 170, 'max_depth': 3},
mean: 0.79349, std: 0.02542, params:{'min_samples_split': 190, 'max_depth': 3},
mean: 0.80960, std: 0.02602, params:{'min_samples_split': 50, 'max_depth': 5},
mean: 0.80920, std: 0.02629, params:{'min_samples_split': 70, 'max_depth': 5},
mean: 0.80888, std: 0.02522, params:{'min_samples_split': 90, 'max_depth': 5},
mean: 0.80923, std: 0.02777, params:{'min_samples_split': 110, 'max_depth': 5},
mean: 0.80823, std: 0.02634, params:{'min_samples_split': 130, 'max_depth': 5},
mean: 0.80801, std: 0.02637, params:{'min_samples_split': 150, 'max_depth': 5},
mean: 0.80792, std: 0.02685, params:{'min_samples_split': 170, 'max_depth': 5},
mean: 0.80771, std: 0.02587, params:{'min_samples_split': 190, 'max_depth': 5},
mean: 0.81688, std: 0.02996, params:{'min_samples_split': 50, 'max_depth': 7},
mean: 0.81872, std: 0.02584, params:{'min_samples_split': 70, 'max_depth': 7},
mean: 0.81501, std: 0.02857, params:{'min_samples_split': 90, 'max_depth': 7},
mean: 0.81476, std: 0.02552, params:{'min_samples_split': 110, 'max_depth': 7},
mean: 0.81557, std: 0.02791, params:{'min_samples_split': 130, 'max_depth': 7},
mean: 0.81459, std: 0.02905, params:{'min_samples_split': 150, 'max_depth': 7},
mean: 0.81601, std: 0.02808, params:{'min_samples_split': 170, 'max_depth': 7},
mean: 0.81704, std: 0.02757, params:{'min_samples_split': 190, 'max_depth': 7},
mean: 0.82090, std: 0.02665, params:{'min_samples_split': 50, 'max_depth': 9},
mean: 0.81908, std: 0.02527, params:{'min_samples_split': 70, 'max_depth': 9},
mean: 0.82036, std: 0.02422, params:{'min_samples_split': 90, 'max_depth': 9},
mean: 0.81889, std: 0.02927, params:{'min_samples_split': 110, 'max_depth': 9},
mean: 0.81991, std: 0.02868, params:{'min_samples_split': 130, 'max_depth': 9},
mean: 0.81788, std: 0.02436, params:{'min_samples_split': 150, 'max_depth': 9},
mean: 0.81898, std: 0.02588, params:{'min_samples_split': 170, 'max_depth': 9},
mean: 0.81746, std: 0.02716, params:{'min_samples_split': 190, 'max_depth': 9},
mean: 0.82395, std: 0.02454, params:{'min_samples_split': 50, 'max_depth': 11},
mean: 0.82380, std: 0.02258, params:{'min_samples_split': 70, 'max_depth': 11},
mean: 0.81953, std: 0.02552, params:{'min_samples_split': 90, 'max_depth': 11},
mean: 0.82254, std: 0.02366, params:{'min_samples_split': 110, 'max_depth': 11},
mean: 0.81950, std: 0.02768, params:{'min_samples_split': 130, 'max_depth': 11},
mean: 0.81887, std: 0.02636, params:{'min_samples_split': 150, 'max_depth': 11},
mean: 0.81910, std: 0.02734, params:{'min_samples_split': 170, 'max_depth': 11},
mean: 0.81564, std: 0.02622, params:{'min_samples_split': 190, 'max_depth': 11},
mean: 0.82291, std: 0.02092, params:{'min_samples_split': 50, 'max_depth': 13},
mean: 0.82177, std: 0.02513, params:{'min_samples_split': 70, 'max_depth': 13},
mean: 0.82415, std: 0.02480, params:{'min_samples_split': 90, 'max_depth': 13},
mean: 0.82420, std: 0.02417, params:{'min_samples_split': 110, 'max_depth': 13},
mean: 0.82209, std: 0.02481, params:{'min_samples_split': 130, 'max_depth': 13},
mean: 0.81852, std: 0.02227, params:{'min_samples_split': 150, 'max_depth': 13},
mean: 0.81955, std: 0.02885, params:{'min_samples_split': 170, 'max_depth': 13},
mean: 0.82092, std: 0.02600, params:{'min_samples_split': 190, 'max_depth': 13}],
{'max_depth':13, 'min_samples_split': 110},
0.8242016800050813)
#已经取了三个最优参数,看看现在模型的袋外分数:
rf1= RandomForestClassifier(n_estimators= 60, max_depth=13, min_samples_split=110,
min_samples_leaf=20,max_features='sqrt' ,oob_score=True,random_state=10)
rf1.fit(X,y)
printrf1.oob_score_
#输出结果为:0.984
#可见此时我们的袋外分数有一定的提高。也就是时候模型的泛化能力增强了。对于内部节点再划分所需最小样本数min_samples_split,我们暂时不能一起定下来,因为这个还和决策树其他的参数存在关联。下面我们再对内部节点再划分所需最小样本数min_samples_split和叶子节点最少样本数min_samples_leaf一起调参。
#再对内部节点再划分所需最小样本数min_samples_split和叶子节点最少样本数min_samples_leaf一起调参
param_test3= {'min_samples_split':range(80,150,20), 'min_samples_leaf':range(10,60,10)}
gsearch3= GridSearchCV(estimator = RandomForestClassifier(n_estimators= 60,max_depth=13,
max_features='sqrt' ,oob_score=True, random_state=10),
param_grid = param_test3,scoring='roc_auc',iid=False, cv=5)
gsearch3.fit(X,y)
gsearch3.grid_scores_,gsearch2.best_params_, gsearch2.best_score_
#输出如下:
([mean:0.82093, std: 0.02287, params: {'min_samples_split': 80, 'min_samples_leaf':10},
mean: 0.81913, std: 0.02141, params:{'min_samples_split': 100, 'min_samples_leaf': 10},
mean: 0.82048, std: 0.02328, params:{'min_samples_split': 120, 'min_samples_leaf': 10},
mean: 0.81798, std: 0.02099, params:{'min_samples_split': 140, 'min_samples_leaf': 10},
mean: 0.82094, std: 0.02535, params:{'min_samples_split': 80, 'min_samples_leaf': 20},
mean: 0.82097, std: 0.02327, params:{'min_samples_split': 100, 'min_samples_leaf': 20},
mean: 0.82487, std: 0.02110, params:{'min_samples_split': 120, 'min_samples_leaf': 20},
mean: 0.82169, std: 0.02406, params:{'min_samples_split': 140, 'min_samples_leaf': 20},
mean: 0.82352, std: 0.02271, params:{'min_samples_split': 80, 'min_samples_leaf': 30},
mean: 0.82164, std: 0.02381, params:{'min_samples_split': 100, 'min_samples_leaf': 30},
mean: 0.82070, std: 0.02528, params:{'min_samples_split': 120, 'min_samples_leaf': 30},
mean: 0.82141, std: 0.02508, params:{'min_samples_split': 140, 'min_samples_leaf': 30},
mean: 0.82278, std: 0.02294, params:{'min_samples_split': 80, 'min_samples_leaf': 40},
mean: 0.82141, std: 0.02547, params:{'min_samples_split': 100, 'min_samples_leaf': 40},
mean: 0.82043, std: 0.02724, params:{'min_samples_split': 120, 'min_samples_leaf': 40},
mean: 0.82162, std: 0.02348, params:{'min_samples_split': 140, 'min_samples_leaf': 40},
mean: 0.82225, std: 0.02431, params:{'min_samples_split': 80, 'min_samples_leaf': 50},
mean: 0.82225, std: 0.02431, params:{'min_samples_split': 100, 'min_samples_leaf': 50},
mean: 0.81890, std: 0.02458, params:{'min_samples_split': 120, 'min_samples_leaf': 50},
mean: 0.81917, std: 0.02528, params:{'min_samples_split': 140, 'min_samples_leaf': 50}],
{'min_samples_leaf':20, 'min_samples_split': 120},
0.8248650279471544)
#最后我们再对最大特征数max_features做调参:
param_test4= {'max_features':range(3,11,2)}
gsearch4= GridSearchCV(estimator = RandomForestClassifier(n_estimators= 60,max_depth=13, min_samples_split=120,
min_samples_leaf=20 ,oob_score=True, random_state=10),
param_grid = param_test4,scoring='roc_auc',iid=False, cv=5)
gsearch4.fit(X,y)
gsearch4.grid_scores_,gsearch4.best_params_, gsearch4.best_score_
#输出如下:
([mean:0.81981, std: 0.02586, params: {'max_features': 3},
mean: 0.81639, std: 0.02533, params:{'max_features': 5},
mean: 0.82487, std: 0.02110, params:{'max_features': 7},
mean: 0.81704, std: 0.02209, params:{'max_features': 9}],
{'max_features':7},
0.8248650279471544)
#用我们搜索到的最佳参数,我们再看看最终的模型拟合:
rf2= RandomForestClassifier(n_estimators= 60, max_depth=13, min_samples_split=120,
min_samples_leaf=20,max_features=7 ,oob_score=True, random_state=10)
rf2.fit(X,y)
printrf2.oob_score_
#此时的输出为:0.984
#可见此时模型的袋外分数基本没有提高,主要原因是0.984已经是一个很高的袋外分数了,如果想进一步需要提高模型的泛化能力,我们需要更多的数据。
参考文献
- https://blog.csdn.net/qq_32805671/article/details/84147415
- https://www.kesci.com/home/project/5ae969440739c42faa1eab95
- https://www.jianshu.com/p/204fc7bdd077(https://blog.csdn.net/ssswill/article/details/85235074)
- http://lightgbm.apachecn.org/#/(http://www.zeroyx.com/index.php?r=site/art&id=17&title_id=105)
- https://blog.csdn.net/sinat_35512245/article/details/79700029(XGboost数据比赛实战之调参篇(完整流程))
- https://blog.csdn.net/m_buddy/article/details/79341058(XGBoost数据训练小例子)
- https://blog.csdn.net/Eddy_zheng/article/details/50496186(机器学习xgboost实战—手写数字识别)
- https://blog.csdn.net/qq_27469517/article/details/76570168(XGBoost 与 信用卡诈骗数据集 三)
- https://blog.csdn.net/sinat_35512245/article/details/79668363(Scikit中的特征选择,XGboost进行回归预测,模型优化的实战)
- https://blog.csdn.net/ChenVast/article/details/82107490(【机器学习】一些常用的回归模型实战(9种回归模型))
- https://blog.csdn.net/u010462995/article/details/70312702(随机森林回归应用中遇到的问题)
- https://cloud.tencent.com/developer/news/319575(XGBoost+LightGBM+LSTM:一次机器学习比赛中的高分模型方案)
- https://www.jianshu.com/p/49ab87122562(catboost 实战)
- https://www.cnblogs.com/webRobot/p/9249906.html(sklearn实战-乳腺癌细胞数据挖掘)
- https://redstonewill.com/1565/
- https://blog.csdn.net/linxid/article/details/80723811