好久没写数据挖掘这块的内容了,这一期就接着来讲讲。
学习一下逻辑回归模型。
从上图我们可知,逻辑回归模型多用于因变量为分类变量的情况。
所以本次的数据预测,也选取的是一个二分类变量(是否违约)。
/ 01 / Logistic回归
Logistic回归通过logit转换将取值为正负无穷的线性方程的值域转化为(0,1),正好与概率的取值范围一致。
具体公式就不列举了,此处点到为止。
想了解更多,可以查阅相关资料或书籍。
Logistic回归是通过构建logit变换,从而进行概率预测。
线性回归同样也是一种预测方法。
但是Logistic回归适合预测分类变量,而且预测的是一个区间0到1的概率。
而线性回归则适合的是预测连续型变量。
此外如果遇到多元目标变量时,Logistic回归也能够进行预测。
但更多的时候,分析师更倾向于根据业务的理解将多元目标变量整合为二元目标变量,然后进行Logistic回归(如若可行)。
Logistic回归预测的是事件的概率,使用最大似然估计对概率进行参数估计。
/ 02/ Python实现
惯例,继续使用书中提供的数据。
一份汽车违约贷款数据集。
涉及到的变量有「是否违约」「曾经破产标识」「五年内信用不良事件数量」「最久账户存续时间」「可循环贷款账户使用比例」「FICO打分」「贷款金额/建议售价*100」「行驶里程」。
读取数据,并对数据进行抽样,训练集和测试集比例为7:3。
代码语言:javascript复制import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
import sklearn.metrics as metrics
import statsmodels.formula.api as smf
# 消除pandas输出省略号情况
pd.set_option('display.max_columns', None)
# 读取数据,skipinitialspace:忽略分隔符后的空白
accepts = pd.read_csv('accepts.csv', skipinitialspace=True)
# dropna:对缺失的数据进行删除
accepts = accepts.dropna(axis=0, how='any')
# frac抽样比例,为70%
train = accepts.sample(frac=0.7, random_state=1234).copy()
test = accepts[~ accepts.index.isin(train.index)].copy()
print('训练集样本量: %i n测试集样本容量: %i' % (len(train), len(test)))
经过我的一番实践,发现抽取比例不同,会导致最后的结果也有所不同。
此外上述使用的是随机抽样,会出现抽取的训练集和测试集当中的违约比例不一样的情况。
所以还可以考虑一下分层抽样,保证固定比例抽取样本。
接下来使用广义线性回归,且指定使用logit变换对数据进行处理。
代码语言:javascript复制lg = smf.glm('bad_ind ~ fico_score', data=train, family=sm.families.Binomial(sm.families.links.logit)).fit()
print(lg.summary())
针对FICO评分得到的逻辑回归结果。
得到回归方程的系数和截距。
其中e的-0.0151次方的值约为0.985。
这就意味着FICO评分每增加一个单位,违约发生的可能性就为原来的0.985倍,违约可能性降低。
多元逻辑回归的实现如下。
代码语言:javascript复制formula = """bad_ind ~ fico_score bankruptcy_ind tot_derog age_oldest_tr rev_util ltv veh_mileage"""
lg_m = smf.glm(formula=formula, data=train, family=sm.families.Binomial(sm.families.links.logit)).fit()
print(lg_m.summary())
得到的逻辑回归结果如下。
得到各变量的系数,其中「可循环贷款账户使用比例」和「行驶里程」这两个变量的系数相对来说较不显著,可以选择删除。
当然还可以结合线性回归时使用的,基于AIC准则的向前法,对变量进行筛选。
代码语言:javascript复制# 向前回归法
def forward_select(data, response):
"""data是包含自变量及因变量的数据,response是因变量"""
# 获取自变量列表
remaining = set(data.columns)
remaining.remove(response)
selected = []
# 定义数据类型(正无穷)
current_score, best_new_score = float('inf'), float('inf')
# 自变量列表含有自变量时
while remaining:
aic_with_candidates = []
# 对自变量列表进行循环
for candidates in remaining:
# 构建表达式,自变量会不断增加
formula = "{} ~ {}".format(response, ' '.join(selected [candidates]))
# 生成自变量的AIC解释力度
aic = smf.glm(formula=formula, data=data, family=sm.families.Binomial(sm.families.links.logit)).fit().aic
# 得到自变量的AIC解释力度列表
aic_with_candidates.append((aic, candidates))
# 对解释力度列表从大到小排序
aic_with_candidates.sort(reverse=True)
# 得到解释力度最大值(AIC值最小)及自变量
best_new_score, best_candidate = aic_with_candidates.pop()
# 1.正无穷大大于解释力度最大值 2.上一期实验的AIC值需大于下一期的AIC实验值,即添加变量越多,AIC值应该越小,模型效果越好
if current_score > best_new_score:
# 移除影响最大的自变量
remaining.remove(best_candidate)
# 添加影响较大的自变量
selected.append(best_candidate)
# 赋值本次实验的AIC值
current_score = best_new_score
print('aic is {},continue!'.format(current_score))
else:
print('forward selection over!')
break
# 采用影响较大的自变量列表,对数据做线性回归
formula = "{} ~ {}".format(response, ' '.join(selected))
print('final formula is {}'.format(formula))
model = smf.glm(formula=formula, data=data, family=sm.families.Binomial(sm.families.links.logit)).fit()
return model
# 采用向前回归法筛选变量,利用筛选的变量构建回归模型
data_for_select = train[['bad_ind', 'fico_score', 'bankruptcy_ind', 'tot_derog', 'age_oldest_tr', 'rev_util', 'ltv', 'veh_mileage']]
lg_m1 = forward_select(data=data_for_select, response='bad_ind')
print(lg_m1.summary())
输出结果如下。
发现变量并没有被筛选掉。
但是观察到之前提到的两个变量,他们对于AIC值的改变,微乎其微。
虽然AIC值是降低了,但是基于就变化这么点点,也是可以选择删除的。
这里就和书中,有所不一样了...
接下来使用线性回归中的方差膨胀因子计算函数,完成对逻辑回归中自变量的多重共线性判断。
代码语言:javascript复制def vif(df, col_i):
# 获取变量
cols = list(df.columns)
# 去除因变量
cols.remove(col_i)
# 获取自变量
cols_noti = cols
# 多元线性回归模型建立及获取模型R²
formula = col_i '~' ' '.join(cols_noti)
r2 = smf.ols(formula, df).fit().rsquared
# 计算方差膨胀系数
return 1. / (1. - r2)
# 获取自变量数据
exog = train[['fico_score', 'tot_derog', 'age_oldest_tr', 'rev_util', 'ltv', 'veh_mileage']]
# 遍历自变量,获取其VIF值
for i in exog.columns:
print(i, 't', vif(df=exog, col_i=i))
输出结果如下。
发现都小于10这个阈值,说明自变量没有显著的多重共线性。
下面利用训练好的模型对测试进行预测。
代码语言:javascript复制train['proba'] = lg_m1.predict(train)
test['proba'] = lg_m1.predict(test)
print(test['proba'].head())
# 大于0.5的为违约
test['prediction'] = (test['proba'] > 0.5).astype('int')
# print(test['prediction'])
输出结果如下。
/ 03 / 模型评估
Logistic回归模型多用于做排序类模型。
而评估排序模型的指标则有ROC曲线、K-S统计量、洛伦兹曲线等。
本次以ROC曲线来说明。
ROC曲线又称接收者操作特征曲线,用来描述模型分辨能力,对角线以上的图形越高,则模型越好。
在ROC曲线中,主要涉及到灵敏度与特异度两个指标。
灵敏度表示模型预测响应的覆盖程度。
特异度表示模型预测不响应的覆盖程度。
覆盖度表示预测准确地观测占实际观测的比例。
ROC曲线则是以特异度为X轴,以灵敏度为Y轴的散点曲线图。
ROC曲线越陡峭,表示预测概率高的观测里响应的覆盖率越强,虚报的响应占比少,说明模型效果越好。
最后可用AUC值(曲线下方的面积)判断模型的好坏。
「0.5,0.7」-效果较低,「0.7,0.85」-效果一般,「0.85,0.95」-效果良好,「0.95,1」-效果非常好。
本例中ROC曲线的实现代码如下。
代码语言:javascript复制acc = sum(test['prediction'] == test['bad_ind']) / np.float(len(test))
# 预测模型准确率
print('The accurancy is %.2f' % acc)
# 输出0.5阈值时的交叉汇总表
print(pd.crosstab(test.bad_ind, test.prediction, margins=True))
for i in np.arange(0, 1, 0.1):
prediction = (test['proba'] > i).astype('int')
confusion_matrix = pd.crosstab(test.bad_ind, prediction, margins=True)
precision = confusion_matrix.ix[1, 1] / confusion_matrix.ix['All', 1]
recall = confusion_matrix.ix[1, 1] / confusion_matrix.ix[1, 'All']
f1_score = 2 * (precision * recall) / (precision recall)
print('threshold: %s, precision: %.2f, recall: %.2f, f1_score: %.2f' % (i, precision, recall, f1_score))
# 绘制ROC曲线
fpr_test, tpr_test, th_test = metrics.roc_curve(test.bad_ind, test.proba)
fpr_train, tpr_train, th_train = metrics.roc_curve(train.bad_ind, train.proba)
plt.figure(figsize=[3, 3])
plt.plot(fpr_test, tpr_test, 'b--')
plt.plot(fpr_train, tpr_train, 'r-')
plt.title('ROC curve')
plt.show()
# 计算AUC值
print('AUC = %.4f' % metrics.auc(fpr_train, tpr_train))
print(metrics.auc(fpr_train, tpr_train))
输出ROC曲线图如下。
预测模型准确率输出为0.81。
输出AUC值为0.7732,模型效果一般。