前言:
1.lstm并不能代替传统的逻辑回归评分卡,本文意在探讨lstm在nlp之外的作用,以及对用户行为数据的应用。
2.知识与创意源自梅子行老师的 《智能风控-原理,算法与工程实践》。
3.使用的数据无实际意义,用户信息神圣不可侵犯。
数据与变量
以客户为维度构建时间序列,时间刻度可以是月份也可以是期数,分别计算每月(每期)的额度使用率,逾期率,还款次数,罚息等聚合变量。
构建特征矩阵,例如24期的7个变量组成24*7的矩阵带入LSTM模型。
LSTM优缺点
优点:
1.RNN的隐藏层是由本单元的与上一单元的组合决定的,而LSTM可以对历史输入选择性的遗忘,只保留重要的信息。
2.时间序列对于特征工程的要求偏低,可使用目标时间段内的全部信息。
3.模型效果好,KS目测比一般机器学习使用时间序列聚合的衍生特征要高。
缺点
1.深度学习都依靠大量的数据维持稳定性(百万级)
2.变量上升到高纬度,没有解释性可言。
3.无法监控变量稳定性,一旦效果出现偏差无法得知原因。
时间序列建模使用场景
1.对下一阶段行为进行预测,或用做贷后管理,智能催收,反欺诈等。
2.将输出分数/等级/标签(或隐藏层参数),作为标准评分卡的特征(在作为特征iv合适的情况下)。
3.对输出分数/等级/标签单独进行监控(在作为特征iv过高的情况下)。
以下是代码小例子
import pandas as pd
import numpy as np
from keras.layers import Dense, LSTM
from keras.utils import to_categorical
from keras.models import Sequential
#读取数据
df=pd.read_csv('data.csv')
#变量
features=["v{}".format(i) for i in range(1,8)]
##观测样本
#样本数量
sample=len(df.uid.unique())
print("sample:",sample)
#变量个数
input_dim=len(features)
print("input_dim:",input_dim)
#时间步长
timestep=df.groupby('uid')['y'].count().unique()[0]
print("timestep :",timestep )
#将数据转为张量
#(样本数,时间长度,变量数)
data=np.reshape(df[features].values,(sample,timestep,input_dim))
#获取标签,每个用户只有一个标签
target=df.groupby('uid')['y'].mean().values
#划分训练集与测试集,比例为8:2
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(data,target,test_size=0.2)
#训练集形状
print("trainset shape:",x_train.shape)
#测试集形状
print("testset shape:",x_test.shape)
#二分类常用评估函数
import tensorflow as tf
from keras import backend as K
def binary_PFA(y_true, y_pred, threshold=K.variable(value=0.5)):
y_pred = K.cast(y_pred >= threshold, 'float32')
# N = 反例总数
N = K.sum(1 - y_true)
# FP = #反例被预测为正例
FP = K.sum(y_pred - y_pred * y_true)
return FP/N
def binary_PTA(y_true, y_pred, threshold=K.variable(value=0.5)):
y_pred = K.cast(y_pred >= threshold, 'float32')
# P = 正例总数
P = K.sum(y_true)
# TP = #正例被预测为正例
TP = K.sum(y_pred * y_true)
return TP/P
#计算auc
def auc(y_true, y_pred):
ptas = tf.stack([binary_PTA(y_true,y_pred,k) for k in np.linspace(0, 1, 1000)],axis=0)
pfas = tf.stack([binary_PFA(y_true,y_pred,k) for k in np.linspace(0, 1, 1000)],axis=0)
pfas = tf.concat([tf.ones((1,)) ,pfas],axis=0)
binSizes = -(pfas[1:]-pfas[:-1])
s = ptas*binSizes
return K.sum(s, axis=0)
#计算真正率
def tpr(y_true, y_pred,threshold=K.variable(value=0.5)):
y_pred = K.cast(y_pred >= threshold, 'float32')
TP=tf.reduce_sum(y_true*tf.round(y_pred))
TN=tf.reduce_sum((1-y_true)*(1-tf.round(y_pred)))
FP=tf.reduce_sum((1-y_true)*tf.round(y_pred))
FN=tf.reduce_sum(y_true*(1-tf.round(y_pred)))
TPR = TP/(TP+FN)
return TPR
#计算假正率
def fpr(y_true, y_pred,threshold=K.variable(value=0.5)):
y_pred = K.cast(y_pred >= threshold, 'float32')
TP=tf.reduce_sum(y_true*tf.round(y_pred))
TN=tf.reduce_sum((1-y_true)*(1-tf.round(y_pred)))
FP=tf.reduce_sum((1-y_true)*tf.round(y_pred))
FN=tf.reduce_sum(y_true*(1-tf.round(y_pred)))
FPR = FP/(TN+FP)
return FPR
#计算ks
def ks(y_true, y_pred):
TPR=tf.stack([tpr(y_true,y_pred,k) for k in np.linspace(0, 1, 1000)],axis=0)
FPR=tf.stack([fpr(y_true,y_pred,k) for k in np.linspace(0, 1, 1000)],axis=0)
ks=K.max(TPR-FPR)
return ks
#构建lstm神经网络,样本量较小,维度少,层数与单元数不宜过多
model = Sequential()
model.add(LSTM(16,input_dim=7, input_length=24, return_sequences=True))
model.add(LSTM(16,activation="sigmoid"))
model.add(Dense(1,activation="sigmoid"))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['binary_accuracy',auc,ks])
#训练
model.fit(x_train, y_train, epochs=100, batch_size=128,verbose=0,validation_data=(x_test,y_test))
print("训练完成")
#实际预测,返回概率
y_predict_train=model.predict_proba(x_train)
y_predict_test=model.predict_proba(x_test)
#评估指标
from sklearn.metrics import roc_curve
def get_ks(y_true,y_pred):
fpr,tpr,thresholds=roc_curve(y_true,y_pred)
ks=max(tpr-fpr)
return ks
from sklearn.metrics import roc_auc_score
def get_auc(y_true,y_pred):
auc=roc_auc_score(y_true,y_pred)
return auc
#预测结果评估
print('train auc:',get_auc(y_train,y_predict_train))
print('train ks:',get_ks(y_train,y_predict_train))
print('test auc:',get_auc(y_test,y_predict_test))
print('test ks:',get_ks(y_test,y_predict_test))
sample: 7292
input_dim: 7
timestep : 24
trainset shape: (5833, 24, 7)
testset shape: (1459, 24, 7)
训练完成
train auc: 0.8274663441550225
train ks: 0.5010717750341944
test auc: 0.8125805687066807
test ks: 0.47002410557896745