CTR场景数据分析建模 问题汇总5 - 复盘比赛2

创建特征(续)

简易fm

对部分相关度较高的特征做相乘相加处理,扩展特征数量,发现更好特征。

data_lxy_fa = data_lxy.copy()
float_importfa_feature = [ 'count_Product_id',
 'seller_Product_id_count',
 'seller_user_id_count',
 'Product_id_day_count',
 'Product_id_action_type_count',
 'Product_id_weekday_count',
 'user_id_nunique_under_Product_id',
 'day_before_Product_id_count',
 'user_id_seller_frequency']
for i in float_importfa_feature:
    for j in float_importfa_feature:
        if i!=j:
            data_lxy_fa[i+'_dot_'+j+'_fa'] = data_lxy_fa[i]*data_lxy_fa[j]
            data_lxy_fa[i+'_add_'+j+'_fa'] = data_lxy_fa[i]+data_lxy_fa[j]
            #data_lxy_fa[i+'_sub_'+j+'_fa'] = data_lxy_fa[i]-data_lxy_fa[j]
for col1 in combinations(float_importfa_feature, 3):
    data_lxy_fa['_'.join(col1)+'_dot_']=data_lxy_fa[col1[0]]*data_lxy_fa[col1[1]]*data_lxy_fa[col1[2]]
    data_lxy_fa['_'.join(col1)+'_add_']=data_lxy_fa[col1[0]]+data_lxy_fa[col1[1]]+data_lxy_fa[col1[2]]

归一化

归一化 减去平均值除以方差

#归一化 减去平均值除以方差
for i in float_feature:
    data_lxy_fa[i] = (data_lxy_fa[i] - data_lxy_fa[i].mean()) / (data_lxy_fa[i].std())
    
for i in data_lxy.columns:    
    if '_dot_' in i:
        data_lxy_fa[i] = (data_lxy_fa[i] - data_lxy_fa[i].mean()) / (data_lxy_fa[i].std())
    if '_add_' in i:
        data_lxy_fa[i] = (data_lxy_fa[i] - data_lxy_fa[i].mean()) / (data_lxy_fa[i].std())

训练集分割验证集

由于本题的day特征对结果影响很大,故对训练集分割时不采取随即分割的方式,而是按照day特征来分割。即将每一天内特征的百分之20化为验证集,用来初步调试模型,找到最优树结构。

train_lxy = data_lxy_fa[data_lxy_fa.purchase.notnull()]
test_lxy = data_lxy_fa[data_lxy_fa.purchase.isnull()]
train_lxy['favorite']=train_lxy['favorite'].astype('category')
train_lxy['purchase']=train_lxy['purchase'].astype('category')
train_feature = [f for f in data_lxy_fa.columns if f not in ['purchase', 'favorite']]
train_lxy_x = train_lxy[train_feature_kendall].reset_index(drop=True)
train_lxy_y_fa = train_lxy['favorite'].reset_index(drop=True)

day_unique = data_lxy.day.unique() 
valid_lxy = pd.DataFrame()
train_lxy_without_valid = pd.DataFrame()
for i in day_unique:
    temp = train_lxy[train_lxy['day'].isin([i])]
    # exec("train_lxy%s = temp" % i)
    valid_lxy_temp = temp.sample(frac = 0.05,replace = False)
    train_lxy_without_valid_temp = temp.drop(valid_lxy_temp.index)
    valid_lxy = pd.concat([valid_lxy, valid_lxy_temp], ignore_index=True)
    train_lxy_without_valid = pd.concat([train_lxy_without_valid, train_lxy_without_valid_temp], ignore_index=True)

valid_lxy_x = valid_lxy[train_feature_kendall].reset_index(drop=True)
valid_lxy_y_fa = valid_lxy['favorite'].reset_index(drop=True)

test_lxy_x = test_lxy[train_feature_kendall].reset_index(drop=True)
train_lxy_x_without_valid = train_lxy_without_valid[train_feature_kendall].reset_index(drop=True)
train_lxy_y_without_valid_fa = train_lxy_without_valid['favorite'].reset_index(drop=True)

模型选取

lightgbm

该模型效果最好,速度和性能在本题上都比xgboost快。
后悔应该尝试下catboost

categorical_feats=['seller','Product_id','user_id']
lgb_train_fa = lgb.Dataset(train_lxy_x_without_valid, train_lxy_y_without_valid_fa,categorical_feature=categorical_feats)
lgb_eval_fa = lgb.Dataset(valid_lxy_x, valid_lxy_y_fa,categorical_feature=categorical_feats, reference=lgb_train_fa)

# specify your configurations as a dict
params = {'boosting_type':"gbdt", 'num_leaves':20, 'reg_alpha':0, 'reg_lambda':0.018,
          'max_depth':5, 'n_estimators':2000, 'objective':'binary','num_class' : 1,
          'subsample':0.8, 'colsample_bytree':0.8, 'subsample_freq':1,'min_child_samples' : 40,
          'learning_rate':0.018, 'random_state':2019, 'metric':"auc",'n_jobs':-1
}

print('Starting training...')
# train
gbm_fa = lgb.train(params,
                lgb_train_fa,
                valid_sets=lgb_eval_fa,
                early_stopping_rounds=50,verbose_eval=20)

注:一开始我使用的时lgb的sklearn中的api:lgb.LGBMClassifier。但是后来上网发现lgb可以处理类别型特征,可以大大减少工作量,但是需要使用自己的api。

具体操作就是需要构建lgb.Dataset,在其中表明哪几个特征是类别型的(一定要真的转换为类别型,否则报错)。
再利用lgb.train将参数、lgb.Dataset传入,进行训练。有千分点级别的提升。

lgb叶子节点+LR

这个是facebook提出来的,应用到比赛中效果一般,比直接lgb要差、慢,很可能是我叶子节点的个数选择、及部分调参工作还有待加强。

from sklearn.linear_model import LogisticRegression

y_pred_train = lgb_model_pur.predict(train_lxy_x, pred_leaf=True)#这里是关键,保留叶子节点
#num_leaf=300 原来是300,有问题这里 应该改为20
num_leaf=20 #这里是叶子节点数量
transformed_training_matrix = np.zeros([len(y_pred_train), len(y_pred_train[0]) * num_leaf],
                                       dtype=np.int64)  # N * num_tress * num_leafs
for i in range(0, len(y_pred_train)):
    temp = np.arange(len(y_pred_train[0])) * num_leaf + np.array(y_pred_train[i])
    transformed_training_matrix[i][temp] += 1
##===================== 测试集转换
print('Writing transformed testing data')
y_pred_test = lgb_model_pur.predict(test_lxy_x, pred_leaf=True)
 
transformed_testing_matrix = np.zeros([len(y_pred_test), len(y_pred_test[0]) * num_leaf], dtype=np.int64)
for i in range(0, len(y_pred_test)):
    temp = np.arange(len(y_pred_test[0])) * num_leaf + np.array(y_pred_test[i])
    transformed_testing_matrix[i][temp] += 1
##=================================  LR ======================================
lm = LogisticRegression(penalty='l2',C=0.05) # logestic model construction
lm.fit(transformed_training_matrix,train_lxy_y_pur)  # fitting the data
y_pred_lr_test = lm.predict_proba(transformed_testing_matrix)   # Give the probabilty on each label
y_pred_lr_test

!!
复盘时发现问题:
1、num_leaves应该小于等于2^max_depth 之前实验时设置有误

max_depth : 树的深度
num_leaves : 树的最大叶子节点数
num_iterations 迭代次数

2、#num_leaf=300 原来是300,有问题这里 应该改为20
num_leaf=20 #这里是叶子节点数量

libfm

想应用libfm,但是每个fm需要有固定长的onehot样本以供其进行fm操作。
所以自然而然的想到可以使用lgb+libfm进行操作。
libfm下载我是windows,就不用进行编译了。
libfm数据格式:
在这里插入图片描述
在这里插入图片描述

使用libfm很是耗费了我一番精力,主要是自己犯懒,不想写数字转onehot以及转libfm格式的程序,
妄图使用官网提供的工具。结果实践证明不如自己干。
贴出部分我的血泪史:
在这里插入图片描述
在这里插入图片描述
最后幡然醒悟,几行代码的事,还是得靠自己

1、onehot化

#训练集gbdt叶子onehot化
y_pred_train = gbm_fa.predict(train_lxy_x, pred_leaf=True)
num_leaf=20
transformed_training_matrix = np.zeros([len(y_pred_train), len(y_pred_train[0]) * num_leaf],dtype=np.int64) 
# N * num_tress * num_leafs
for i in range(0, len(y_pred_train)):
    temp = np.arange(len(y_pred_train[0])) * num_leaf + np.array(y_pred_train[i])
    transformed_training_matrix[i][temp] += 1

#训练集gbdt叶子onehot化
y_pred_valid = gbm_fa.predict(valid_lxy_x, pred_leaf=True)
num_leaf=20
transformed_valid_matrix = np.zeros([len(y_pred_valid), len(y_pred_valid[0]) * num_leaf],dtype=np.int64) 
# N * num_tress * num_leafs
for i in range(0, len(y_pred_valid)):
    temp = np.arange(len(y_pred_valid[0])) * num_leaf + np.array(y_pred_valid[i])
    transformed_valid_matrix[i][temp] += 1
    #拼接特征与结果
#train_y_concat=np.concatenate((transformed_training_matrix, train_lxy_y_fa.values.reshape(len(train_lxy_y_fa.values),1)), axis=1)
##===================== 测试集转换
#测试集gbdt叶子onehot化
print('Writing transformed testing data')
y_pred_test = gbm_fa.predict(test_lxy_x, pred_leaf=True)
 
transformed_testing_matrix = np.zeros([len(y_pred_test), len(y_pred_test[0]) * num_leaf], dtype=np.int64)
for i in range(0, len(y_pred_test)):
    temp = np.arange(len(y_pred_test[0])) * num_leaf + np.array(y_pred_test[i])
    transformed_testing_matrix[i][temp] += 1
 

2、转libfm格式(libsvm)

def np_to_libsvm_format(filename,transformed_matrix,y_value):
    #filename 转换格式后的输出文件
    #transformed_matrix onehot后的待转矩阵
    #y_value 对应训练集的y值
    output = open(filename, 'w') #中间文件
    lenmat=len(transformed_matrix)
    linelen = 20000
    for i in range(lenmat):
        output_line = ''
        if np.isnan(transformed_matrix[i,0]):
            break
        for j in range(linelen):
             #the features cols
            if j==0:
                output_line = output_line + str(int(y_value[i]))
            if transformed_matrix[i,j]==1 :
                the_text = ' ' + str(j) + ':' + str(1)
                output_line = output_line + the_text
        output_line = output_line + '\n'       
        output.write(output_line)
    output.close()

libfm的Manual写的太简洁了,加上我理解力不足,搞定这个让我死了不少脑细胞。
以上面的例子为例
在这里插入图片描述
训练集是有标签的,但是测试集是否还需要留y值的位置?
我用实验证明,即使你的测试集你不知道y值是多少,你也得强行写上y值(哪怕全0)
测试截图:
在这里插入图片描述

3、libfm使用

最后理解了,为了追求简洁。实际上这里的-test 是既可以放验证集也可以放测试集。
如果需要线下验证模型,就在test上放入验证集,可以显示出训练和测试的loss。
如果要预测训练集,就在test放入训练集,如果你设置测试集为全0,就不要去看结果中测试集的loss了,没啥意义。

另外,这里的loss是类mse,且只有一种loss不可更换。这让用auc作为评判标准的题目很是尴尬,但是至少明白loss的意义对调参还是有用的。
命令:

./libFM -task c -train ml1m-train.libfm -test ml1m-test.libfm -dim ’1,1,8’

在这里插入图片描述
神奇的是train的loss不停上升。。但是结果还可以上0.6了,有些迷惑,望有经验的同志们解答。

最后调参调了lgb的叶子数、训练轮数、dim of 2-way interactions、分类器(MCMC、SGD、SGDA)MCMC效果最好,也最快。

其他

使用了xgboost、随机森林、LR,效果都不如lgb

相关性选择

选择相关性靠前的x个特征,输入分类器,有略微提高

#相关性判定-favorite
train_lxy['favorite']=train_lxy['favorite'].astype('float')
d={}
c={}
e={}
for i in train_feature:
    if (i!='favorite')&(i!='purchase'):
        data=pd.concat([train_lxy[i],train_lxy['favorite']],axis=1) 
        c[i]=data.corr(method='spearman').values[1,0]  # Sperman秩相关系数
        d[i]=data.corr().values[1,0]                   # Pearson相关系数 - 算法
        e[i]=data.corr(method='kendall').values[1,0]

train_feature_kendall = []
for i in train_feature:
    if abs(e[i]) > 0.01:
        train_feature_kendall.append(i)
train_feature_kendall.append('seller')
train_feature_kendall.append('Product_id')
train_feature_kendall.append('user_id')
train_feature_kendall
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值