DataFountain2021丨系统认证风险预测 TOP方案(附代码)

前段时间组队参加了DataFountain上"系统认证风险预测"挑战赛, 最后排名A榜第2, B榜第14, 遗憾未能进入决赛, 在这里分享下比赛过程, 最后有代码供各位参考.

比赛链接

系统认证风险预测

比赛任务

参赛团队将基于用户认证行为数据及风险异常标记结构,构建用户认证行为特征模型和风险异常评估模型,利用风险评估模型去判断当前用户认证行为是否存在风险。

比赛为二分类预测问题, 对于竞赛初学者, 比较适合从这样的结构化比赛入手.

方案

最后提交的方案为lightgbm + catboost 融合, A榜得分为 0.53016672.
在这里插入图片描述
大家的得分都不高, 而且从数据上看, 所有分类特征, 不同类之间风险均值都很接近, 可能和这个数据由人工标记的不大准有关. 本方案上分思路大致如下:

  • 最开始, 在baseline基础上细化特征, 主要是时间特征,得分在0.51397588932
  • 调参数: 在构造了一些特征后, 发现再增加特征, 得分基本都下降, 就开始调参方向. 主要对num_leaves, max_depth, n_estimators, learning_rate进行调整, 因为本赛题数据计算不大, 时间也充足, 调参比较方便, 这时得分在0.52059315975
  • 细调参数: 调参过程中发现, 参数有一点点变化, 结果差异都很大. 接着对n_splits, random_state, verbose等等参数都进行了调整, 最终分数在0.52587843799
  • 模型融合: 比赛最后阶段(暂列第5), 大家开始组队, 第1, 第2, 第4三个组成了一队后, 分数有了明显提升. 我也开始考虑组队, 找到队友后(catboost模型), 将两个人的模型最好成绩做了简单平均, 得分居然到0.52975581963(惊喜), 之后修改模型权重, 将预测值改为rank排序值得分都有稍微提升, 最终得分0.53016671554. 再之后尝试用更多模型(树模型), 都没有好的效果
  • 没想到: 切换B榜后, 我们的得分一下子掉到了十几名(意外), 而有的队在A榜是十几名, 在B榜一下子到了前三.

思考:

  1. 我们模型不够稳定, 参数调的太细, 感觉是B榜掉分的原因之一, 尤其是模型和k折的2个随机种子, 最好和最差的得分能有2%, 虽然上分, 但也是坑
  2. 可能有好的特征,没有发现. 本题风险预测+时序数据, 每条数据和它最近的几条数据应该有特征可以挖掘
  3. NN模型没有尝试
  4. 运气: A榜运气不错, B榜就…

比赛快结束了, 等大佬分享他们的思路, 最后附上代码(lightgbm部分, 线上得分0.52587843799):

导入数据与特征工程

# -*- coding: utf-8 -*-

import warnings
warnings.simplefilter('ignore')

import os
import re
import gc
import json
import numpy as np
import pandas as pd
pd.set_option('max_columns', None)
pd.set_option('max_rows', 200)
pd.set_option('float_format', lambda x: '%.3f' % x)
from tqdm.notebook import tqdm
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import KFold, StratifiedKFold
from sklearn.metrics import roc_auc_score
import lightgbm as lgb

import os
os.chdir('C:/Users/yyz/Desktop/系统认证风险预测/')

# 读取数据
train = pd.read_csv('data/train_dataset.csv', sep='\t')
test = pd.read_csv('data/test_dataset.csv', sep='\t')
data = pd.concat([train,test],axis=0)

# location列转成多列
data['location_first_lvl'] = data['location'].astype(str).apply(lambda x: json.loads(x)['first_lvl'])
data['location_sec_lvl'] = data['location'].astype(str).apply(lambda x: json.loads(x)['sec_lvl'])
data['location_third_lvl'] = data['location'].astype(str).apply(lambda x: json.loads(x)['third_lvl'])


# 删除值相同的列
data.drop(['client_type', 'browser_source'], axis=1, inplace=True)
data['auth_type'].fillna('__NaN__', inplace=True)

# 日期数据处理 
data['op_date'] = pd.to_datetime(data['op_date'])
data['op_ts'] = data["op_date"].values.astype(np.int64) // 10 ** 9
data = data.sort_values(by=['user_name', 'op_ts']).reset_index(drop=True)
data['last_ts'] = data.groupby(['user_name'])['op_ts'].shift(1)
data['ts_diff1'] = data['op_ts'] - data['last_ts']  

data['op_date_month'] = data['op_date'].dt.month  # 月份放到年份的前面居然上分
data['op_date_year'] = data['op_date'].dt.year 
data['op_date_day'] = data['op_date'].dt.day
data['op_date_dayofweek'] = data['op_date'].dt.dayofweek
data['op_date_ymd'] = data['op_date'].dt.year*100 + data['op_date'].dt.month 
data['op_date_hour'] = data['op_date'].dt.hour

period_dict ={
    23: 0, 0: 0, 1: 0,
    2: 1, 3: 1, 4: 1,
    5: 1, 6: 1, 7: 2,
    8: 2, 9: 2, 10: 2, 11: 2,
    12: 2, 13: 3,
    14: 3, 15: 3, 16: 3, 17: 3,
    18: 3,
    19: 4, 20:4, 21: 4, 22: 4,
}
data['hour_cut']=data['op_date_hour'].map(period_dict)
# 一年中的哪个季度
season_dict = {
    1: 1, 2: 1, 3: 1,
    4: 2, 5: 2, 6: 2,
    7: 3, 8: 3, 9: 3,
    10: 4, 11: 4, 12: 4,
}
data['month_cut']=data['op_date_month'].map(season_dict)
data['dayofyear']=data['op_date'].apply(lambda x: x.dayofyear)  # 一年中的第几天
data['weekofyear']=data['op_date'].apply(lambda x: x.week)  # 一年中的第几周
data['是否周末'] = data['op_date'].apply(lambda x: True if x.dayofweek in [4,5, 6] else False)  # 是否周末
data.loc[((data['op_date_hour'] >= 7) & (data['op_date_hour'] < 22)), 'isworktime'] = 1

# 特征编码
data['ip_risk_level'] = data['ip_risk_level'].map({'1级':1,'2级':2,'3级':3})

for f in ['ip', 'location', 'device_model', 'os_version', 'browser_version']:
    data[f'user_{f}_nunique'] = data.groupby(['user_name'])[f].transform('nunique')
 
for method in ['mean', 'max', 'min', 'std']:
    data[f'ts_diff1_{method}'] = data.groupby('user_name')['ts_diff1'].transform(method)
    
for i in[ 'os_type']: 
    data[i+'_n'] = data.groupby(['user_name','op_date_ymd','op_date_hour'])[i].transform('nunique') 

lis =   ['user_name', 'action', 
         'auth_type',
         'ip', 
                 'ip_location_type_keyword',  'device_model',
                 'os_type', 'os_version', 'browser_type', 'browser_version',
                 'bus_system_code', 'op_target', 'location_first_lvl', 'location_sec_lvl',
                 'location_third_lvl']  
# one_hot
data_re = data[lis]
df_processed = pd.get_dummies(data_re, prefix_sep="_", columns=data_re.columns)
lis_sx = [i for i in data.columns if i not in lis]
data = pd.concat([data[lis_sx],df_processed],axis=1)

train = data[data['risk_label'].notna()]
test = data[data['risk_label'].isna()]

建模

ycol = 'risk_label'
feature_names = list(
    filter(lambda x: x not in [ycol, 'session_id', 'op_date', 'location','last_ts'], train.columns))
model = lgb.LGBMClassifier(objective='binary',
                           boosting_type='gbdt',
                           tree_learner='serial',
                           num_leaves=29,
                           max_depth=7,
                           learning_rate=0.07,
                           n_estimators=1590,
                           subsample=0.7,
                           feature_fraction=0.95,
                           reg_alpha=0.,
                           reg_lambda=0.,
                           random_state=1973,  
                           is_unbalance=True,
                           metric='auc')


oof = []
prediction = test[['session_id']]
prediction[ycol] = 0
df_importance_list = []

kfold = StratifiedKFold(n_splits=10, shuffle=True, random_state=1950) 
for fold_id, (trn_idx, val_idx) in enumerate(kfold.split(train[feature_names], train[ycol])):
    X_train = train.iloc[trn_idx][feature_names]
    Y_train = train.iloc[trn_idx][ycol]

    X_val = train.iloc[val_idx][feature_names]
    Y_val = train.iloc[val_idx][ycol]

    print('\nFold_{} Training ================================\n'.format(fold_id+1))

    lgb_model = model.fit(X_train,
                          Y_train,
                          eval_names=['train', 'valid'],
                          eval_set=[(X_train, Y_train), (X_val, Y_val)],
                          verbose=250,
                          eval_metric='auc',
                          early_stopping_rounds=400)

    pred_val = lgb_model.predict_proba(
        X_val, num_iteration=lgb_model.best_iteration_)
    df_oof = train.iloc[val_idx][['session_id', ycol]].copy()
    df_oof['pred'] = pred_val[:, 1]
    oof.append(df_oof)

    pred_test = lgb_model.predict_proba(
        test[feature_names], num_iteration=lgb_model.best_iteration_)
    prediction[ycol] += pred_test[:, 1] / kfold.n_splits

    df_importance = pd.DataFrame({
        'column': feature_names,
        'importance': lgb_model.feature_importances_,
    })
    df_importance_list.append(df_importance)

    del lgb_model, pred_val, pred_test, X_train, Y_train, X_val, Y_val
    gc.collect()
    
    
df_importance = pd.concat(df_importance_list)
df_importance = df_importance.groupby(['column'])['importance'].agg(
    'mean').sort_values(ascending=False).reset_index()
df_importance    

df_oof = pd.concat(oof)
print('roc_auc_score', roc_auc_score(df_oof[ycol], df_oof['pred']))

prediction['id'] = range(len(prediction))
prediction['id'] = prediction['id'] + 1
prediction = prediction[['id', 'risk_label']].copy()
prediction.columns = ['id', 'ret']

prediction.to_csv('result/sub40_lgb.csv', index=False)

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

python技巧(数据分析及可视化)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值