消费者人群画像—信用智能评分 :信用分预测

前言

这是关于预测分数的一个比赛,本来也想了解别人的代码来思考别人是如何做的,顺便自己也要学习一下。废话不多说,这里看下如何处理数据的。

数据 探索可视化

数据集下载可以在kaggle 里找得到,kaggle 国内可以访问,并且也可以用他的平台来跑代码,很方便的做一些事情,先来看下数据探索这一块。这里把测试集和训练集都融合在一起做特征分析,其实是因为有可能出现在测试集而不存再训练集中的特征值,这里一同处理了,

import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.kernel_ridge import KernelRidge
from sklearn.linear_model import ElasticNet, BayesianRidge
from sklearn.model_selection import KFold, StratifiedKFold, RepeatedKFold
from sklearn.metrics import mean_squared_error as mse, mean_absolute_error as mae, mean_absolute_error
import xgboost as xgb
import matplotlib.pyplot as plt
import catboost as ctb

## 读取 数据集
path = "../input/mobile-credit/"
train = pd.read_csv(path + '/train_dataset.csv')

print(train.shape)
train['type'] = 1
test = pd.read_csv(path + '/test_dataset.csv')
test['type'] = 0
data = pd.concat([train, test], ignore_index=True)
print(data.shape)

##   来看下数据大概情况

data.info()
print('-'*80)
print(data.dtypes.value_counts())


columnsList = list(data)
print(columnsList)

for col in columnsList:
    print('-'*20+'  {} '.format(col)+'-'*20)
    print(' 不同值个数 = {}  最大值 = {} , 最小值 = {}'.format(len(data[col].value_counts()),data[col].max(),data[col].min()))
    print(data[col].value_counts())

代码运行结果:

['type', '信用分', '当月旅游资讯类应用使用次数', '当月是否体育场馆消费', '当月是否到过福州山姆会员店', '当月是否景点游览', '当月是否看电影', '当月是否逛过福州仓山万达', '当月火车类应用使用次数', '当月物流快递类应用使用次数', '当月网购类应用使用次数', '当月视频播放类应用使用次数', '当月通话交往圈人数', '当月金融理财类应用使用总次数', '当月飞机类应用使用次数', '是否4G不健康客户', '是否大学生客户', '是否经常逛商场的人', '是否黑名单客户', '用户实名制是否通过核实', '用户年龄', '用户当月账户余额(元)', '用户最近一次缴费距今时长(月)', '用户编码', '用户网龄(月)', '用户话费敏感度', '用户账单当月总费用(元)', '用户近6个月平均消费值(元)', '缴费用户当前是否欠费缴费', '缴费用户最近一次缴费金额(元)', '近三个月月均商场出现次数']
--------------------  type --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
1    50000
0    50000
Name: type, dtype: int64
--------------------  信用分 --------------------
 不同值个数 = 278  最大值 = 719.0 , 最小值 = 422.0
640.0    605
635.0    597
644.0    595
634.0    594
629.0    592
        ... 
441.0      1
455.0      1
713.0      1
436.0      1
453.0      1
Name: 信用分, Length: 278, dtype: int64
--------------------  当月旅游资讯类应用使用次数 --------------------
 不同值个数 = 934  最大值 = 87681 , 最小值 = 0
0       61206
1        5340
2        4485
3        2504
4        2317
        ...  
674         1
546         1
1569        1
1313        1
735         1
Name: 当月旅游资讯类应用使用次数, Length: 934, dtype: int64
--------------------  当月是否体育场馆消费 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    62527
1    37473
Name: 当月是否体育场馆消费, dtype: int64
--------------------  当月是否到过福州山姆会员店 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    97295
1     2705
Name: 当月是否到过福州山姆会员店, dtype: int64
--------------------  当月是否景点游览 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    52454
1    47546
Name: 当月是否景点游览, dtype: int64
--------------------  当月是否看电影 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    75620
1    24380
Name: 当月是否看电影, dtype: int64
--------------------  当月是否逛过福州仓山万达 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    96077
1     3923
Name: 当月是否逛过福州仓山万达, dtype: int64
--------------------  当月火车类应用使用次数 --------------------
 不同值个数 = 180  最大值 = 775 , 最小值 = 0
0      95007
1       1542
2       1228
3        464
4        330
       ...  
147        1
234        1
212        1
170        1
92         1
Name: 当月火车类应用使用次数, Length: 180, dtype: int64
--------------------  当月物流快递类应用使用次数 --------------------
 不同值个数 = 239  最大值 = 8235 , 最小值 = 0
0      98534
1        146
2        127
3         77
4         72
       ...  
477        1
114        1
286        1
625        1
191        1
Name: 当月物流快递类应用使用次数, Length: 239, dtype: int64
--------------------  当月网购类应用使用次数 --------------------
 不同值个数 = 8382  最大值 = 417536 , 最小值 = 0
0        15937
1         1284
2          961
4          837
3          688
         ...  
10844        1
6746         1
6385         1
2940         1
8785         1
Name: 当月网购类应用使用次数, Length: 8382, dtype: int64
--------------------  当月视频播放类应用使用次数 --------------------
 不同值个数 = 16067  最大值 = 1382227 , 最小值 = 0
0        17355
1         1511
2         1418
3          843
4          828
         ...  
11365        1
60477        1
42036        1
19497        1
49528        1
Name: 当月视频播放类应用使用次数, Length: 16067, dtype: int64
--------------------  当月通话交往圈人数 --------------------
 不同值个数 = 554  最大值 = 1906 , 最小值 = 1
16      1762
14      1747
13      1737
15      1718
11      1711
        ... 
652        1
486        1
550        1
1318       1
1151       1
Name: 当月通话交往圈人数, Length: 554, dtype: int64
--------------------  当月金融理财类应用使用总次数 --------------------
 不同值个数 = 7232  最大值 = 496238 , 最小值 = 0
0        18363
1         2158
2         1620
3         1080
4          957
         ...  
9180         1
5082         1
6902         1
21394        1
20714        1
Name: 当月金融理财类应用使用总次数, Length: 7232, dtype: int64
--------------------  当月飞机类应用使用次数 --------------------
 不同值个数 = 209  最大值 = 5856 , 最小值 = 0
0       98632
1         122
7          96
2          90
8          89
        ...  
298         1
150         1
1174        1
233         1
63          1
Name: 当月飞机类应用使用次数, Length: 209, dtype: int64
--------------------  是否4G不健康客户 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    91131
1     8869
Name: 是否4G不健康客户, dtype: int64
--------------------  是否大学生客户 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    99638
1      362
Name: 是否大学生客户, dtype: int64
--------------------  是否经常逛商场的人 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    66928
1    33072
Name: 是否经常逛商场的人, dtype: int64
--------------------  是否黑名单客户 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    95150
1     4850
Name: 是否黑名单客户, dtype: int64
--------------------  用户实名制是否通过核实 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
1    99124
0      876
Name: 用户实名制是否通过核实, dtype: int64
--------------------  用户年龄 --------------------
 不同值个数 = 88  最大值 = 111 , 最小值 = 0
29     4255
36     4227
31     4168
28     4062
30     4021
       ... 
13        1
111       1
105       1
100       1
91        1
Name: 用户年龄, Length: 88, dtype: int64
--------------------  用户当月账户余额(元) --------------------
 不同值个数 = 316  最大值 = 109090 , 最小值 = 10
50       8009
30       7847
40       7777
20       7480
60       6662
         ... 
5280        1
15270       1
3100        1
2140        1
4290        1
Name: 用户当月账户余额(元), Length: 316, dtype: int64
--------------------  用户最近一次缴费距今时长(月) --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
1    70142
0    29858
Name: 用户最近一次缴费距今时长(月), dtype: int64
--------------------  用户编码 --------------------
 不同值个数 = 100000  最大值 = ffffebe8f3a74834b51e122d76a9d41a , 最小值 = 00011b2d787242499b8683f0e72ec5a2
cb1833d8de1641feb17c5d90cf6c83fb    1
138dab4ccd2f48f9b1aae72b0c14d101    1
ebc74c5d15b748f394dba5eca80e9ffb    1
704d0c39c9ee48f88299f9c269b432a1    1
c84346fc602a4913846d9ce29689be18    1
                                   ..
5a39734f0a61416b9661fa19123606ad    1
33e02edbfe2e49a79f7f00c0df69bf1a    1
eab1e711168746e183f3870611cd66aa    1
c54fb03a8dad497a884764a109164ba1    1
4cf641c938bd442994f554e9eb047ad4    1
Name: 用户编码, Length: 100000, dtype: int64
--------------------  用户网龄(月) --------------------
 不同值个数 = 283  最大值 = 288 , 最小值 = 1
5      1280
7      1141
6      1120
8      1031
10      927
       ... 
3         3
287       2
284       2
281       1
280       1
Name: 用户网龄(月), Length: 283, dtype: int64
--------------------  用户话费敏感度 --------------------
 不同值个数 = 6  最大值 = 5 , 最小值 = 0
4    29838
5    21011
2    20622
3    20578
1     7913
0       38
Name: 用户话费敏感度, dtype: int64
--------------------  用户账单当月总费用(元) --------------------
 不同值个数 = 16597  最大值 = 2117.01 , 最小值 = 0.0
18.00     2274
38.00     1816
78.00     1418
58.00     1342
98.00     1250
          ... 
96.35        1
198.21       1
166.79       1
247.56       1
43.44        1
Name: 用户账单当月总费用(元), Length: 16597, dtype: int64
--------------------  用户近6个月平均消费值(元) --------------------
 不同值个数 = 22520  最大值 = 1792.74 , 最小值 = 0.0
18.00     410
38.00     191
18.01     154
40.00     143
88.00     120
         ... 
12.86       1
251.78      1
229.92      1
211.33      1
290.20      1
Name: 用户近6个月平均消费值(元), Length: 22520, dtype: int64
--------------------  缴费用户当前是否欠费缴费 --------------------
 不同值个数 = 2  最大值 = 1 , 最小值 = 0
0    94817
1     5183
Name: 缴费用户当前是否欠费缴费, dtype: int64
--------------------  缴费用户最近一次缴费金额(元) --------------------
 不同值个数 = 532  最大值 = 1000.0 , 最小值 = 0.0
0.00      29357
99.80     22284
49.90     21066
29.94     11740
199.60     5189
          ...  
60.14         1
33.49         1
28.17         1
100.99        1
5.60          1
Name: 缴费用户最近一次缴费金额(元), Length: 532, dtype: int64
--------------------  近三个月月均商场出现次数 --------------------
 不同值个数 = 93  最大值 = 92 , 最小值 = 0
0     20755
1      7607
2      5735
92     4791
3      4190
      ...  
57      296
58      290
56      288
65      285
53      285
Name: 近三个月月均商场出现次数, Length: 93, dtype: int64

还好特征不多,不然这个就得慢慢看了,通过数据情况我们可以得到这些结论:

  • 是否 相关的特征基本就只有 0 或 1,其他没有了
    比如:当月是否景点游览、当月是否看电影、是否大学生客户
  • 是否大学生客户:大学生客户有 362个
  • 是否4G不健康客户 :是不健康的客户 8869个
  • 是否黑名单客户:黑名单用户有 4850个
  • 实名制通过 :876个未通过
  • 用户年龄:最大的是 111,最小的是0 , 0 用户应该是 脏数据
  • 用户话费敏感度 : 一共6个等级
  • 用户账单当月总费用:为 0 的数据也有,可能是脏数据,直方图来观看一下
  • 用户近6个月平均消费值(元):这个也同样有 0 的数据,也有可能是 脏数据

代码讲解(特征+算法)

上述是代码探索方面的内容,我们依旧先参考别人的代码来看下他们是如何思考的,然后我们再提出一些问题和我们的看法。

# 关键特征被fillna 0 了 这里还原回来
data.loc[data['用户年龄'] == 0, '用户年龄'] = None
data.loc[data['用户年龄'] > 100, '用户年龄'] = None
data.loc[data['用户话费敏感度'] == 0, '用户话费敏感度'] = None
data.loc[data['用户近6个月平均消费值(元)'] == 0, '用户近6个月平均消费值(元)'] = None

# 特征 名称 转换一下
data.rename(columns={'用户编码': 'id', '信用分': 'score'}, inplace=True)

origin_bool_feature = ['当月是否体育场馆消费', '当月是否景点游览', '当月是否看电影', '当月是否到过福州山姆会员店', '当月是否逛过福州仓山万达',
                       '缴费用户当前是否欠费缴费', '是否经常逛商场的人', '是否大学生客户', '是否4G不健康客户', '是否黑名单客户',
                       '用户最近一次缴费距今时长(月)', '用户实名制是否通过核实']

origin_num_feature = ['用户话费敏感度', '用户年龄', '近三个月月均商场出现次数', '当月火车类应用使用次数', '当月飞机类应用使用次数',
                      '当月物流快递类应用使用次数', '用户当月账户余额(元)', '用户网龄(月)', '缴费用户最近一次缴费金额(元)',
                      '当月通话交往圈人数', '当月旅游资讯类应用使用次数', '当月金融理财类应用使用总次数', '当月网购类应用使用次数',
                      '当月视频播放类应用使用次数', '用户账单当月总费用(元)', '用户近6个月平均消费值(元)']

count_feature_list = []

## 重点看下这些特征,把特征值的count 新增为一列,简单的一个代码就搞定了,map作用还挺大的
for i in ['用户近6个月平均消费值(元)', '用户账单当月总费用(元)', '缴费用户最近一次缴费金额(元)']:
    count_feature_list.append('count_' + i)
    data['count_' + i] = data[i].map(data[i].value_counts())

# 业务特征 - 话费特征: 
#       1、5个月的话费   
#       2、当月账单 - 平均账单   
#       3、用户最近一次缴费/ 当月账单金额
data['five_all'] = data['用户近6个月平均消费值(元)'] * data['用户网龄(月)'].apply(lambda x: min(x, 6)) - data['用户账单当月总费用(元)']
data['fee_del_mean'] = data['用户账单当月总费用(元)'] - data['用户近6个月平均消费值(元)']
data['fee_remain_now'] = data['缴费用户最近一次缴费金额(元)'] / data['用户账单当月总费用(元)']

# 把一些统计次数的特征 收纳为一个特征,但是这里没有 '近三个月月均商场出现次数'  这个特征,可能是时间维度的问题

data['次数'] = data[['当月网购类应用使用次数', '当月物流快递类应用使用次数', '当月金融理财类应用使用总次数',
                   '当月视频播放类应用使用次数', '当月飞机类应用使用次数', '当月火车类应用使用次数', '当月旅游资讯类应用使用次数']].sum(axis=1)

## 算次数占比,有一些的特征的占比情况,

for col in ['当月金融理财类应用使用总次数', '当月旅游资讯类应用使用次数']:  # 这两个比较积极向上一点
    data[col + '_百分比'] = data[col] / data['次数']
    
## 除以12 得到年限
data['regist_month'] = data['用户网龄(月)'] % 12


# 不知道这个是干嘛的 ,感觉好像是算出 一些 loss比较大的id
lgb_model = lgb.LGBMRegressor(
    num_leaves=32, reg_alpha=0., reg_lambda=0.01, objective='mse', metric='mae',
    max_depth=-1, learning_rate=0.01, min_child_samples=50,
    n_estimators=15000, subsample=0.7, colsample_bytree=0.45, subsample_freq=5,
)

# ab_id 是去除的200个样本的id。是由5折预测出来的结果的loss最大的200个构成。也可以将这200个数据权重设置0.01(线下好像更好了。没机会试了)
ab_id = []

# 设置样本权重
data['temp_label'] = data['score']
# 这里设置为None 而不是删除该数据,因为删除的话,线下一定是提升的,对于线上而言,异常数据依旧存在,所以应该关注在训练集无异常,而测试集有异常下的处理效果
data['sample_weight'] = data['temp_label'] + 200
data['sample_weight'] = data['sample_weight'] / data['sample_weight'].mean()
# 方案1 ,不训练
data.loc[data.id.isin(ab_id), 'temp_label'] = None
# 方案2,样本权重设置低一点
data.loc[data.id.isin(ab_id), 'sample_weight'] = 0.01

# 感谢大佬分享的参数
ctb_params = {
    'n_estimators': 10000,
    'learning_rate': 0.02,
    'random_seed': 4590,
    'reg_lambda': 0.08,
    'subsample': 0.7,
    'bootstrap_type': 'Bernoulli',
    'boosting_type': 'Plain',
    'one_hot_max_size': 10,
    'rsm': 0.5,
    'leaf_estimation_iterations': 5,
    'use_best_model': True,
    'max_depth': 6,
    'verbose': -1,
    'thread_count': 4
}
ctb_model = ctb.CatBoostRegressor(**ctb_params)

上述代码有点多,帮大家总结一下:

  • 将用户年龄 、用户话费敏感度、用户近6个月平均消费值(元) 一些脏数据置位None
  • 用户近6个月平均消费值(元)’, ‘用户账单当月总费用(元)’, '缴费用户最近一次缴费金额(元) :这些指标统计次数,并新增count特征
  • 新增话费特征: 1、5个月的话费 2、当月账单 - 平均账单 3、用户最近一次缴费/ 当月账单金额
  • 将一些次数相关的特征收拢为一个特征,所有次数相加作为这一个特征的特征值
  • 当月金融理财类应用使用总次数’, '当月旅游资讯类应用使用次数 :这两个特征的占比情况
  • 用户网龄(月) 除以 12 得到 年限
  • 设置了一个样本权重:用来表明样本的重要性,但是其实是对误差较大的样本较小的权重,其他的样本较大的权重,不是每一个样本一个权重。相当于将样本分两类,两个样本权重值。

这里我要改一下,使用lightgbm 来训练模型,评价指标值metrics=‘rmse’ ,代码如下:

# 把 测试数据集拿出来训练

data_train = data.drop('id', 1)
df_train = data_train.loc[data['type'] == 1]
y_train = data.loc[data['type'] == 1]['score']
print(df_train.shape)
print(y_train.shape)


params = {
    'boosting_type': 'gbdt', 
    'objective': 'regression', 

    'learning_rate': 0.005, 
    'num_leaves': 80, 
    'max_depth': 7,
    'min_data_in_leaf': 20,

    'subsample': 1, 
    'colsample_bytree': 0.7, 
    }

data_train = lgb.Dataset(df_train, y_train, silent=True)
cv_results = lgb.cv(
    params, data_train, num_boost_round=10000, nfold=5, stratified=False, shuffle=True, metrics='rmse',
    early_stopping_rounds=50, verbose_eval=100, show_stdv=True)

print('best n_estimators:', len(cv_results['rmse-mean']))
print('best cv score:', cv_results['rmse-mean'][-1])

看下部分实验结果吧:

[100]	cv_agg's rmse: 26.6712 + 0.10326
[200]	cv_agg's rmse: 17.0392 + 0.0632588
[300]	cv_agg's rmse: 11.1587 + 0.0420691
[400]	cv_agg's rmse: 7.5608 + 0.0307922
[500]	cv_agg's rmse: 5.26283 + 0.0275422
[600]	cv_agg's rmse: 3.84734 + 0.0265683


[9000]	cv_agg's rmse: 0.669732 + 0.0136983
[9100]	cv_agg's rmse: 0.669402 + 0.0136558
[9200]	cv_agg's rmse: 0.669015 + 0.0135673
[9300]	cv_agg's rmse: 0.668788 + 0.0136241
[9400]	cv_agg's rmse: 0.668525 + 0.0136477
[9500]	cv_agg's rmse: 0.668247 + 0.0136263
[9600]	cv_agg's rmse: 0.667934 + 0.0136192
[9700]	cv_agg's rmse: 0.667742 + 0.0135973
[9800]	cv_agg's rmse: 0.667451 + 0.0135567
[9900]	cv_agg's rmse: 0.667144 + 0.0135732
[10000]	cv_agg's rmse: 0.666852 + 0.0135611
best n_estimators: 10000
best cv score: 0.6668517639542803

我的问题

1、为什么要把脏数据、离散样本特征值置位None?
比如用户年龄,完全可以拆分 年龄段来做一个新特征。

2、置位None的特征是否合适?
意思就是换个模型,这样的值能不能被训练,估计不行吧,我个人认为还是都是数值化比较好,这样换模型比较好,

3、代码跟 参考博客里的PPT 对比起来差别还是挺大的,
可能代码精简了很多,只是一部分代码,但重点是思考问题的方式,根据对比赛、数据集的理解,加上实验,得到的一些结论。

参考博客

消费者人群画像-信用智能评分Top1
消费者人群画像—信用智能评分

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值