房屋信贷违约风险竞争--kaggle

数据由Home Credit提供,这是一项专门为非银行人口提供信贷额度(贷款)的服务。预测客户是否会偿还贷款或遇到困难是一个关键的商业需求,而Home Credit在Kaggle上举办了这个竞赛。

这个任务是一个标准的监督分类任务:
监督:标签包含在训练数据中,目标是训练一个模型来学习从特征中预测
标签分类:标签为二元变量,0(将按时偿还贷款),1(将难以偿还贷款)

数据:

  • application_train/application_test:家庭信贷中关于每个贷款申请的主要训练和测试数据。每个贷款都有自己的行,由“SK_ID_CURR”特性标识。训练申请数据带有“目标”,表示0:贷款已偿还或1:贷款未偿还。
  • bureau:客户以前在其他金融机构的信用数据。每个以前的信贷在局里有自己的行,但是申请数据中的一个贷款可以有多个以前的信贷。
  • bureau_balance:关于以前在bureau的学分的月度数据。每行是前一个信用记录的一个月,一个前一个信用记录可以有多个行,每个月一个信用记录的长度。
  • previous_application:在申请数据中有贷款的客户之前的家庭信贷贷款申请。应用程序数据中的每个当前贷款都可以有多个以前的贷款。以前的每个应用程序都有一行,由“SK_ID_PREV”特性标识。
  • POS_CASH_BALANCE:每月的数据,关于以前的销售点或现金贷款客户有住房信贷。每行是前一个销售点或现金贷款的一个月,而前一个贷款可以有许多行。
  • credit_card_balance:每月关于以前信用卡客户使用家庭信用的数据。每行是信用卡余额的一个月,而一张信用卡可以有很多行。
  • installments_payment:先前在Home Credit的贷款付款历史。每一笔已付款都有一行,每一笔未付款都有一行。

以下这个图片是解释这些数据的相关:

《房屋信贷违约风险竞争--kaggle》 数据相关

1.ROC(受试者工作特征曲线) 和 AUC(ROC曲线下的面积):

ROC和AUC的解释可以参见
机器学习(周志华) –第二章的内容
ROC AUC是模型性能的较好表征。

2.读取数据

首先,我们可以列出所有可用的数据文件。总共有9个文件:1个训练主文件(有目标)1个测试主文件(没有目标),1个提交示例文件,以及6个其他文件,其中包含每笔贷款的附加信息。

# 导入基本处理模块
import numpy as np
import pandas as pd 
from sklearn.preprocessing import LabelEncoder
import os
import warnings
warnings.filterwarnings('ignore')
import matplotlib.pyplot as plt
import seaborn as sns

(1).显示可用列表

print(os.listdir("../input/"))

《房屋信贷违约风险竞争--kaggle》 可用列表内容

(2).训练dafaframe

app_train = pd.read_csv('../input/application_train.csv')
print('Training data shape: ', app_train.shape)
app_train.head()

《房屋信贷违约风险竞争--kaggle》 输出数据长度

训练数据有307511个观察数据(每一个单独的贷款)和122个特征(变量),包括目标(我们想预测的标签)

(3).测试dafaframe特征

app_test = pd.read_csv('../input/application_test.csv')
print('Testing data shape: ', app_test.shape)
app_test.head()

《房屋信贷违约风险竞争--kaggle》 测试dafaframe

由此可见测试集比训练集小很多,而且缺少目标列

3.探索性数据分析:

我个人理解,探究性数据分析第一步将数据可视化,然后从图中理解数据的趋势,数据异常等问题,再对数据进行处理,例如数据清洗等操作。

1).检查目标列的分布

目标是我们被要求预测的:要么贷款的0被及时偿还,要么1表明客户有付款困难。我们可以先检查一下属于每个类别的贷款数量。
app_train['TARGET'].value_counts()

《房屋信贷违约风险竞争--kaggle》

app_train['TARGET'].astype(int).plot.hist();

《房屋信贷违约风险竞争--kaggle》 image.png

从结果图中,我们可以看出0比1的结果多很多,这样会导致我们的学习器出现错误,有可能在真实数据训练中出现过拟合的问题,然后把多数数据都看成了0不偿还,这样会降低准确度,损失用户,所以我们需要将数据进行均衡操作。

(2).检查缺失值

接下来,我们可以查看每列中缺失值的数量和百分比

# 计算丢失值的函数 
def missing_values_table(df):
        # 总缺失值
        mis_val = df.isnull().sum()
        
        # 计算缺失值的百分比
        mis_val_percent = 100 * df.isnull().sum() / len(df)
        
        # 显示结果的表格
        mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1)
        
        # 重命名列
        mis_val_table_ren_columns = mis_val_table.rename(
        columns = {0 : 'Missing Values', 1 : '% of Total Values'})
        
        # 按下降百分比对表进行排序
        mis_val_table_ren_columns = mis_val_table_ren_columns[
            mis_val_table_ren_columns.iloc[:,1] != 0].sort_values(
        '% of Total Values', ascending=False).round(1)
        
        # 显示部分信息
        print ("Your selected dataframe has " + str(df.shape[1]) + " columns.\n"      
            "There are " + str(mis_val_table_ren_columns.shape[0]) +
              " columns that have missing values.")
        
        # 返回缺少的信息值
        return mis_val_table_ren_columns

        # 统计缺失值
        missing_values = missing_values_table(app_train)
        missing_values.head(20)

在处理这些缺失值有两种方法,第一种是将缺失值进行删除,但是这些缺失值代表有很多,而且我们并不知道这些缺失值对最终结果是否有影响,所以这时候要使用第二种方法,利用Xgboox这种学习模型来对缺失值进行处理(处理缺失值而且不影响缺失值的数值)。

上面代码处理后输出的结果:

《房屋信贷违约风险竞争--kaggle》

《房屋信贷违约风险竞争--kaggle》

(4).列类型

让我们看看每个数据类型的列数。int64和float64是数值变量(可以是离散的,也可以是连续的)。对象列包含字符串,是类特征。

每类列的编号:
app_train.dtypes.value_counts()

《房屋信贷违约风险竞争--kaggle》

查看看每个对象列中的惟一条目的数量。

app_train.select_dtypes('object').apply(pd.Series.nunique, axis = 0)

《房屋信贷违约风险竞争--kaggle》

由结果可以看到,大部分分类变量都有相对缺少唯一的条目,现在我们需要找到一种方法去处理这些数据。

(5)编码分类变量:

在我们进一步深入之前,我们需要处理麻烦的分类变量。但是机器学习模型不能处理分类变量(除了LightGBM等一些模型)。因此,在将这些变量传递给模型之前,我们必须找到一种方法将这些变量编码(表示)为数字。这个过程主要有两种方式:

  • 标签编码:在一个具有整数的类别变量中分配每个唯一的类别。没有创建新的列。下面是一个示例
    图像

    《房屋信贷违约风险竞争--kaggle》

  • 独热编码:为类别变量中的每个惟一类别创建一个新列。每个观察值在相应的类别中在列中接受1,在所有其他新列中接受0,独热编码的唯一缺点是,特性(数据的维数)的数量可能会随着具有多个类别的分类变量而激增。为了解决这个问题,我们可以执行一次热编码,然后执行PCA或其他降维方法,以减少维数(同时仍然试图保留信息。

标签编码和独热编码

让我们实现上面描述的策略:对于具有2个唯一类别的任何类别变量(dtype == object),我们将使用标签编码,对于具有2个以上唯一类别的任何类别变量,我们将使用one-hot编码。

# 创建标签编码对象
le = LabelEncoder()
le_count = 0

# 迭代这些列
for col in app_train:
    if app_train[col].dtype == 'object':
        # 如果2个唯一类别的列或更少的类别的列使用标签编码:
        if len(list(app_train[col].unique())) <= 2:
            # 训练训练集
            le.fit(app_train[col])
            # 标签化训练集和测试集
            app_train[col] = le.transform(app_train[col])
            app_test[col] = le.transform(app_test[col])
            
            # 计算标签编码作用了多少列
            le_count += 1
            
print('%d columns were label encoded.' % le_count)

标签编码作用列数量结果:

《房屋信贷违约风险竞争--kaggle》

接下来进行独热编码

# 对分类变量执行独热编码
app_train = pd.get_dummies(app_train)
app_test = pd.get_dummies(app_test)

print('Training Features shape: ', app_train.shape)
print('Testing Features shape: ', app_test.shape)

![(https://upload-images.jianshu.io/upload_images/9137004-18ec016a3c412081.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

(7).调整训练和测试数据

在训练和测试数据中需要有相同的特性(列)。One-hot编码在训练数据中创建了更多的列,因为有些类别变量在测试数据中没有表示。要删除不在测试数据中的训练数据中的列,我们需要对齐dataframes。首先,我们从训练数据中提取目标列(因为这不在测试数据中,但是我们需要保留这些信息)。当我们进行对齐时,我们必须确保将axis = 1设置为基于列而不是行对齐数据.

train_labels = app_train['TARGET']

# 对齐训练和测试数据,只保留两个数据格式中的列
app_train, app_test = app_train.align(app_test, join = 'inner', axis = 1)

# 将目标加入
app_train['TARGET'] = train_labels

print('Training Features shape: ', app_train.shape)
print('Testing Features shape: ', app_test.shape

《房屋信贷违约风险竞争--kaggle》

训练和测试dafaframe现在具有机器学习所需的相同特性。由于独热编码,特性的数量显著增加。在某些时候,我们可能想尝试降维(删除不相关的特性)来减小dafaframe的大小

探索性数据分析

异常

在做EDA时,我们总是希望注意的一个问题是数据中的异常。这些可能是由于输入错误的数字、测量设备的错误,或者它们可能是有效的但极端的测量。定量支持异常的一种方法是使用描述方法查看列的统计数据。DAYS_BIRTH列中的数字是负数,因为它们是相对于当前的贷款申请记录的。要查看这些数年的统计数据,我们可以将其倍数乘以-1,然后除以一年的天数:
(app_train['DAYS_BIRTH'] / -365).describe()

《房屋信贷违约风险竞争--kaggle》

这些年龄比值看起来没有问题了,大致均衡。那工作值呢?

app_train['DAYS_EMPLOYED'].describe()

《房屋信贷违约风险竞争--kaggle》

这个看起来就不对了!最大值(除了正的值)大约是1000年!

app_train['DAYS_EMPLOYED'].plot.hist(title = 'Days Employment Histogram');
plt.xlabel('Days Employment');

《房屋信贷违约风险竞争--kaggle》

出于好奇,让我们对异常客户进行子集划分,看看他们的违约率是否比其他客户高或低

anom = app_train[app_train['DAYS_EMPLOYED'] == 365243]
non_anom = app_train[app_train['DAYS_EMPLOYED'] != 365243]
print('The non-anomalies default on %0.2f%% of loans' % (100 * non_anom['TARGET'].mean()))
print('The anomalies default on %0.2f%% of loans' % (100 * anom['TARGET'].mean()))
print('There are %d anomalous days of employment' % len(anom))

《房屋信贷违约风险竞争--kaggle》

事实证明,异常现象的违约率较低。
处理异常取决于确切的情况,没有固定的规则。最安全的方法之一就是将异常值设置为缺失值,然后在机器学习之前将其填充(使用赋值)。在本例中,由于所有的异常值都完全相同,我们希望用相同的值来填充它们,以防所有这些贷款都有共同之处。异常值似乎有一定的重要性,所以我们想告诉机器学习模型如果我们真的填了这些值。作为解决方案,我们将使用非数字填充异常值(np.nan),然后创建一个新的布尔列,指示该值是否为异常。

# 创建一个异常的标志列
app_train['DAYS_EMPLOYED_ANOM'] = app_train["DAYS_EMPLOYED"] == 365243

# 将异常值替换为nan
app_train['DAYS_EMPLOYED'].replace({365243: np.nan}, inplace = True)

app_train['DAYS_EMPLOYED'].plot.hist(title = 'Days Employment Histogram');
plt.xlabel('Days Employment');

《房屋信贷违约风险竞争--kaggle》

这个分布看起来更符合我们的期望,我们还创建了一个新的列来告诉模型这些值最初是反常的(因为我们将不得不用一些值来填充nans,可能是列的中值)。在dataframe中有天数的其他列看起来与我们所期望的没什么明显的异常值。
作为一个非常重要的提示,我们对训练数据所做的任何事情我们也必须对测试数据所做的。让我们确保创建新列并使用np填充现有列。在楠测试数据中。

app_test['DAYS_EMPLOYED_ANOM'] = app_test["DAYS_EMPLOYED"] == 365243
app_test["DAYS_EMPLOYED"].replace({365243: np.nan}, inplace = True)

print('There are %d anomalies in the test data out of %d entries' % (app_test["DAYS_EMPLOYED_ANOM"].sum(), len(app_test)))

《房屋信贷违约风险竞争--kaggle》

相关性

现在我们已经处理了分类变量和离群值,让我们继续处理EDA。一种尝试和理解数据的方法是在特征和目标之间寻找相关性。我们可以使用。校正 dafaframe法计算每个变量与目标之间的Pearson相关系数
相关系数并不是表示特征“相关性”的最佳方法,但它确实让我们了解了数据中可能存在的关系。相关系数绝对值的一般解释有:
-19“很弱”
-.20 -。39“软弱”
-.40 -。59“温和”
-.60 -。79年“强大”
-.80 – 1.0“很强”

# 找到与目标的关联和排序
correlations = app_train.corr()['TARGET'].sort_values()

# 显示相关性
print('Most Positive Correlations:\n', correlations.tail(15))
print('\nMost Negative Correlations:\n', correlations.head(15))

《房屋信贷违约风险竞争--kaggle》

让我们来看看一些更重要的相关性:DAYS_BIRTH是最积极的相关性。(TARGET除外,因为变量与自身的相关性总是1!)查看文档,DAYS_BIRTH是客户在贷款处于负值时(无论出于什么原因!)的天数。相关性是正的,但实际上这个功能的价值是负的,这意味着当客户渐渐长大,他们不太可能拖欠贷款(即目标= = 0)。这是一个让人有些迷惑,所以我们将特性然后相关性的绝对值将是负的。

年龄对还款的影响

# 找出出生后的积极天数与目标的相关性
app_train['DAYS_BIRTH'] = abs(app_train['DAYS_BIRTH'])
app_train['DAYS_BIRTH'].corr(app_train['TARGET'])

《房屋信贷违约风险竞争--kaggle》

随着客户年龄的增长,与目标客户之间存在负线性关系,即随着客户年龄的增长,他们更倾向于按时还贷。
我们来看看这个变量。首先,我们可以做一个年龄的直方图。我们将把x轴放在几年后,使这个图更容易理解。

# 设置平面图的风格
plt.style.use('fivethirtyeight')

# 以年为单位绘制年龄分布
plt.hist(app_train['DAYS_BIRTH'] / 365, edgecolor = 'k', bins = 25)
plt.title('Age of Client'); plt.xlabel('Age (years)'); plt.ylabel('Count');

《房屋信贷违约风险竞争--kaggle》

年龄分布本身并没有告诉我们太多,除了没有异常值,因为所有的年龄都是合理的。为了将年龄对目标的影响形象化,我们接下来将使用目标的值来绘制核密度估计图(KDE)。核密度估计图显示了单个变量的分布,可以认为它是平滑的直方图(它是通过计算每个数据点上的核(通常是高斯核),然后对所有单个核进行平均,得到一条平滑的曲线而创建的)。对于这个图,我们将使用seaborn kdeplot。

plt.figure(figsize = (10, 8))

# KDE是按时偿还的贷款
sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, 'DAYS_BIRTH'] / 365, label = 'target == 0')

# 未按时偿还的贷款
sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, 'DAYS_BIRTH'] / 365, label = 'target == 1')

# 平面图的标签
plt.xlabel('Age (years)'); plt.ylabel('Density'); plt.title('Distribution of Ages');

《房屋信贷违约风险竞争--kaggle》

目标== 1曲线向较年轻的一端倾斜。
虽然这不是一个显著的相关(-0.07相关系数),但这个变量可能会在机器学习模型中有用,因为它确实会影响目标。
让我们以另一种方式来看待这种关系:未能按年龄组别偿还贷款的平均情况。

为了制作这个图表,我们首先把年龄分类切成每个5岁的箱子。
然后,对于每个箱子,我们计算目标的平均值,它告诉我们在每个年龄段没有偿还的贷款比例。

# 年龄信息分为一个单独的dafaframe
age_data = app_train[['TARGET', 'DAYS_BIRTH']]
age_data['YEARS_BIRTH'] = age_data['DAYS_BIRTH'] / 365

# 箱子年龄的dafaframe
age_data['YEARS_BINNED'] = pd.cut(age_data['YEARS_BIRTH'], bins = np.linspace(20, 70, num = 11))
age_data.head(10)

《房屋信贷违约风险竞争--kaggle》

# 将箱子分组并计算其平均值
age_groups  = age_data.groupby('YEARS_BINNED').mean()
age_groups

《房屋信贷违约风险竞争--kaggle》

plt.figure(figsize = (8, 8))

# 将年龄箱和目标的平均值绘制成条形图
plt.bar(age_groups.index.astype(str), 100 * age_groups['TARGET'])

# 平面图的标签
plt.xticks(rotation = 75); plt.xlabel('Age Group (years)'); plt.ylabel('Failure to Repay (%)')
plt.title('Failure to Repay by Age Group');

《房屋信贷违约风险竞争--kaggle》

有一个明显的趋势:年轻的申请者更有可能无法偿还贷款!最年轻的三个年龄段的还款失败率超过10%,最年长的三个年龄段的还款失败率超过5%。
这是银行可以直接使用的信息:因为年轻的客户不太可能偿还贷款,也许他们应该得到更多的指导或理财建议。这并不意味着银行应该歧视年轻客户,但明智的做法是采取预防措施,帮助年轻客户按时付款。

外部来源

与目标具有最强负相关性的三个变量分别是EXT_SOURCE_1、EXT_SOURCE_2和EXT_SOURCE_3。根据文档,这些特性表示“来自外部数据源的规范化分数”。我不确定这到底意味着什么,但它可能是一种利用大量数据来源得出的累积信用评级。
让我们看看这些变量。
首先,我们可以显示EXT_SOURCE特性与目标以及彼此之间的相关性。

# 提取EXT_SOURCE变量并显示相关性
ext_data = app_train[['TARGET', 'EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']]
ext_data_corrs = ext_data.corr()
ext_data_corrs

《房屋信贷违约风险竞争--kaggle》

plt.figure(figsize = (8, 6))

# 相关性的热图
sns.heatmap(ext_data_corrs, cmap = plt.cm.RdYlBu_r, vmin = -0.25, annot = True, vmax = 0.6)
plt.title('Correlation Heatmap');

所有这三个EXT_SOURCE特性都与目标函数有负相关,表明随着EXT_SOURCE的值的增加,客户更有可能偿还贷款。
我们还可以看到DAYS_BIRTH与EXT_SOURCE_1呈正相关,表明这个评分中的一个因素可能是客户年龄。

接下来,我们可以看看由目标值着色的每个特征的分布。
这将使我们可视化这个变量对目标的影响。

plt.figure(figsize = (10, 12))

#  遍历source
for i, source in enumerate(['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3']):
    
    # 为每个source创建新的子图
    plt.subplot(3, 1, i + 1)
    # 偿还贷款的平面图
    sns.kdeplot(app_train.loc[app_train['TARGET'] == 0, source], label = 'target == 0')
    # 未偿还的贷款的平面图
    sns.kdeplot(app_train.loc[app_train['TARGET'] == 1, source], label = 'target == 1')
    
    # 平面图的标签
    plt.title('Distribution of %s by Target Value' % source)
    plt.xlabel('%s' % source); plt.ylabel('Density');
    
plt.tight_layout(h_pad = 2.5)

《房屋信贷违约风险竞争--kaggle》

EXT_SOURCE_3显示了目标值之间最大的差异。我们可以清楚地看到,这个特性与申请人偿还贷款的可能性有一定的关系。这种关系并不很强(事实上,它们都被认为很弱,但这些变量对于机器学习模型仍然有用,可以用来预测申请人是否会按时偿还贷款。

最后平面图

作为最后的探索性绘图,我们可以对EXT_SOURCE变量和DAYS_BIRTH变量进行配对。
对图是一个很好的探索工具,因为它让我们看到多对变量之间的关系以及单个变量的分布。
这里我们使用seaborn可视化库和配对网格函数创建一对图,上面三角形上有散点图,对角线上有直方图,下面三角形上有2D核密度图和相关系数。

如果您不理解这段代码,没关系!
在Python中绘图可能过于复杂,对于最简单的图形之外的任何内容,我通常都会找到一个现有的实现并修改代码(不要重复自己的操作)!

# 复制数据用于绘图
plot_data = ext_data.drop(columns = ['DAYS_BIRTH']).copy()

# 加上客户的年龄
plot_data['YEARS_BIRTH'] = age_data['YEARS_BIRTH']

# 删除na值并限制为前100000行
plot_data = plot_data.dropna().loc[:100000, :]

# 函数计算两列之间的相关系数
def corr_func(x, y, **kwargs):
    r = np.corrcoef(x, y)[0][1]
    ax = plt.gca()
    ax.annotate("r = {:.2f}".format(r),
                xy=(.2, .8), xycoords=ax.transAxes,
                size = 20)

# 创建pairgrid对象
grid = sns.PairGrid(data = plot_data, size = 3, diag_sharey=False,
                    hue = 'TARGET', 
                    vars = [x for x in list(plot_data.columns) if x != 'TARGET'])

# 顶层是一个散点图
grid.map_upper(plt.scatter, alpha = 0.2)

# 对角线是一个直方图
grid.map_diag(sns.kdeplot)

# 底是密度图
grid.map_lower(sns.kdeplot, cmap = plt.cm.OrRd_r);

plt.suptitle('Ext Source and Age Features Pairs Plot', size = 32, y = 1.05);

《房屋信贷违约风险竞争--kaggle》

在这个图中,红色表示未偿还贷款,蓝色表示已偿还贷款。我们可以看到数据中不同的关系。EXT_SOURCE_1和DAYS_BIRTH(或者相当于YEARS_BIRTH)之间似乎存在适度的正线性关系,这表明这个特性可能会考虑客户的年龄。

特征工程

特征工程赢得了Kaggle竞赛:那些获胜的是那些能够从数据中创建最有用的功能的人。(这在很大程度上是正确的,因为获胜的模型,至少对于结构化数据来说,都倾向于梯度提升的变体)。这代表了机器学习中的一种模式:特征工程比模型构建和超参数调优有更大的投资回报。这是一篇关于这个主题的好文章)。正如Andrew Ng喜欢说的那样:“应用机器学习基本上就是特征工程。”

在选择合适的模型和最优的设置是很重要的,模型只能从给定的数据中学习。确保这些数据尽可能与任务相关是数据科学家的工作(可能还有一些自动化工具来帮助我们)。

特征工程指的是一个遗传过程,它既包括特征构造:从现有数据中添加新特征,也包括特征选择:只选择最重要的特征或其他降维方法。我们可以使用许多技术来创建特性和选择特性。

当我们开始使用其他数据源时,我们将做大量的特性工程,但是在这个笔记本中,我们将只尝试两种简单的特性构造方法:

  • 多项式特征
  • 领域知识的特点
# 为多项式特征建立一个新的数据dataframe
poly_features = app_train[['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH', 'TARGET']]
poly_features_test = app_test[['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH']]

# 输入处理缺失值
from sklearn.preprocessing import Imputer
imputer = Imputer(strategy = 'median')

poly_target = poly_features['TARGET']

poly_features = poly_features.drop(columns = ['TARGET'])

# 需要估算缺失值
poly_features = imputer.fit_transform(poly_features)
poly_features_test = imputer.transform(poly_features_test)

from sklearn.preprocessing import PolynomialFeatures
                                  
# 创建具有指定程度的多项式对象
poly_transformer = PolynomialFeatures(degree = 3)

# 训练多项式特征
poly_transformer.fit(poly_features)

# 转化特征值
poly_features = poly_transformer.transform(poly_features)
poly_features_test = poly_transformer.transform(poly_features_test)
print('Polynomial Features shape: ', poly_features.shape)

《房屋信贷违约风险竞争--kaggle》

这创建了大量的新特性。要获得名称,我们必须使用多项式特性get_feature_names方法。

poly_transformer.get_feature_names(input_features = ['EXT_SOURCE_1', 'EXT_SOURCE_2', 'EXT_SOURCE_3', 'DAYS_BIRTH'])[:15]

《房屋信贷违约风险竞争--kaggle》

有35个特性,每个特性都被提升到3级和交互项。

现在,我们可以看到这些新特性是否与目标相关联。

# 创建特性的dataframe
poly_features = pd.DataFrame(poly_features, 
                             columns = poly_transformer.get_feature_names(['EXT_SOURCE_1', 'EXT_SOURCE_2', 
                                                                           'EXT_SOURCE_3', 'DAYS_BIRTH']))

# 增加目标值
poly_features['TARGET'] = poly_target

# 找到目标相关性
poly_corrs = poly_features.corr()['TARGET'].sort_values()

# 显示最不相关和最相关的一面 
print(poly_corrs.head(10))
print(poly_corrs.tail(5))

《房屋信贷违约风险竞争--kaggle》

一些新变量与目标的相关性(就绝对值大小而言)比原始特征更大。当我们构建机器学习模型时,我们可以尝试使用和不使用这些特性来确定它们是否真的有助于模型学习。
我们将把这些特性添加到训练和测试集的副本中,然后评估有和没有这些特性的模型。很多时候,在机器学习中,知道一种方法是否有效的唯一方法就是去尝试它!

# 将测试特征值放入dafaframe中
poly_features_test = pd.DataFrame(poly_features_test, 
                                  columns = poly_transformer.get_feature_names(['EXT_SOURCE_1', 'EXT_SOURCE_2', 
                                                                                'EXT_SOURCE_3', 'DAYS_BIRTH']))

# 将多项式特征合并到训练集中
poly_features['SK_ID_CURR'] = app_train['SK_ID_CURR']
app_train_poly = app_train.merge(poly_features, on = 'SK_ID_CURR', how = 'left')

# 将多项式特性合并到测试dafaframe中
poly_features_test['SK_ID_CURR'] = app_test['SK_ID_CURR']
app_test_poly = app_test.merge(poly_features_test, on = 'SK_ID_CURR', how = 'left')

# 对齐dafaframe
app_train_poly, app_test_poly = app_train_poly.align(app_test_poly, join = 'inner', axis = 1)

# 显示新的矩阵信息
print('Training data with polynomial features shape: ', app_train_poly.shape)
print('Testing data with polynomial features shape:  ', app_test_poly.shape)

《房屋信贷违约风险竞争--kaggle》

领域知识的特点

因为我不是信用专家,也许称这种“领域知识”并不完全正确,但或许我们可以称之为“应用有限金融知识的尝试”。
在这种思路下,我们可以创建一些功能,试图捕捉我们认为可能对判断客户是否会拖欠贷款很重要的内容。
在这里,我将使用由Aguiar的这个脚本启发的5个特性:

CREDIT_INCOME_PERCENT:信用金额相对于客户的收入的百分比
ANNUITY_INCOME_PERCENT:贷款年金相对于客户收入的百分比
CREDIT_TERM:以月份为单位的付款期限(因为年金是每月应付金额
DAYS_EMPLOYED_PERCENT:相对于客户的年龄,被雇佣的天数的百分比

app_train_domain = app_train.copy()
app_test_domain = app_test.copy()

app_train_domain['CREDIT_INCOME_PERCENT'] = app_train_domain['AMT_CREDIT'] / app_train_domain['AMT_INCOME_TOTAL']
app_train_domain['ANNUITY_INCOME_PERCENT'] = app_train_domain['AMT_ANNUITY'] / app_train_domain['AMT_INCOME_TOTAL']
app_train_domain['CREDIT_TERM'] = app_train_domain['AMT_ANNUITY'] / app_train_domain['AMT_CREDIT']
app_train_domain['DAYS_EMPLOYED_PERCENT'] = app_train_domain['DAYS_EMPLOYED'] / app_train_domain['DAYS_BIRTH']
app_test_domain['CREDIT_INCOME_PERCENT'] = app_test_domain['AMT_CREDIT'] / app_test_domain['AMT_INCOME_TOTAL']
app_test_domain['ANNUITY_INCOME_PERCENT'] = app_test_domain['AMT_ANNUITY'] / app_test_domain['AMT_INCOME_TOTAL']
app_test_domain['CREDIT_TERM'] = app_test_domain['AMT_ANNUITY'] / app_test_domain['AMT_CREDIT']
app_test_domain['DAYS_EMPLOYED_PERCENT'] = app_test_domain['DAYS_EMPLOYED'] / app_test_domain['DAYS_BIRTH']

可视化新变量

我们应该在图中直观地探讨这些领域知识变量。对于所有这些,我们将使相同的KDE图由目标值

plt.figure(figsize = (12, 20))
# 迭代新特征
for i, feature in enumerate(['CREDIT_INCOME_PERCENT', 'ANNUITY_INCOME_PERCENT', 'CREDIT_TERM', 'DAYS_EMPLOYED_PERCENT']):
    
    # 为每个soruce创建一个新的子图
    plt.subplot(4, 1, i + 1)
    # 偿还贷款平面图
    sns.kdeplot(app_train_domain.loc[app_train_domain['TARGET'] == 0, feature], label = 'target == 0')
    # 未偿还贷款的平面图
    sns.kdeplot(app_train_domain.loc[app_train_domain['TARGET'] == 1, feature], label = 'target == 1')
    
    # 平面图的标签
    plt.title('Distribution of %s by Target Value' % feature)
    plt.xlabel('%s' % feature); plt.ylabel('Density');
    
plt.tight_layout(h_pad = 2.5)

《房屋信贷违约风险竞争--kaggle》

《房屋信贷违约风险竞争--kaggle》

很难说这些新特性是否有用。唯一确定的方法就是尝试一下!

基线

原始的基准,我们可以猜测所有例子测试相同的值集。我们被要求预测的概率不偿还贷款,如果我们完全不确定,我们猜0.5测试集上的所有观测。这将给我们一个接收器的操作特性曲线下的面积(AUC ROC)0.5的竞争(随机猜测分类任务得分0.5)。

因为我们已经知道我们会得到什么分数,我们真的不需要做一个天真的基线猜测。
让我们用一个稍微复杂一点的模型来描述我们的实际基线:逻辑回归。

逻辑回归的实现

在这里,我将侧重于实现模型而不是解释细节,但是对于那些想要更多地了解机器学习算法理论的人,我建议对统计学习和使用scikit – learning和TensorFlow的实际机器学习进行介绍。
这两本书都介绍了制作模型所需的理论和代码(分别用R和Python编写)。
他们都认为最好的学习方法就是实践,而且他们都非常有效!

为了获得基线,我们将在编码分类变量之后使用所有的特性。
我们将通过填充缺失值(赋值)和规范化特征的范围(特征缩放)对数据进行预处理。
下面的代码执行这两个预处理步骤。

from sklearn.preprocessing import MinMaxScaler, Imputer

# 从训练数据中删除目标
if 'TARGET' in app_train:
    train = app_train.drop(columns = ['TARGET'])
else:
    train = app_train.copy()
    
# 特征名称
features = list(train.columns)

# 复制测试集
test = app_test.copy()

# 缺失值的中值估算
imputer = Imputer(strategy = 'median')

# 将每个特性缩放到0-1
scaler = MinMaxScaler(feature_range = (0, 1))

# 符合训练数据
imputer.fit(train)

# 转换训练和测试数据
train = imputer.transform(train)
test = imputer.transform(app_test)

# 重复的标量
scaler.fit(train)
train = scaler.transform(train)
test = scaler.transform(test)

print('Training data shape: ', train.shape)
print('Testing data shape: ', test.shape)

《房屋信贷违约风险竞争--kaggle》

我们将在第一个模型中使用来自Scikit-Learn的logisticregressionlearn。我们将对默认模型设置做的唯一更改是降低正则化参数C,该参数控制过拟合的数量(一个较低的值应该会减少过拟合)。这将使我们得到比默认逻辑回归略好的结果,但它仍然会为未来的任何模型设置一个较低的标准。
这里我们使用熟悉的Scikit-Learn建模语法:首先创建模型,然后使用.fit训练模型,然后使用.predict_proba对测试数据进行预测(记住,我们想要的是概率而不是0或1)。

from sklearn.linear_model import LogisticRegression

# 使模型具有指定的正则化参数
log_reg = LogisticRegression(C = 0.0001)

# 训练训练集
log_reg.fit(train, train_labels)

《房屋信贷违约风险竞争--kaggle》

既然这个模型已经被训练过了,我们就可以用它来做预测。我们想预测不还贷的概率,所以我们用模型预测。proba方法。这将返回一个mx2数组,其中m是观察数。第一列是目标的概率是0,第二列是目标的概率是1(对于单个行,两列总和必须为1)。我们希望概率不偿还贷款,所以我们将选择第二列。
下面的代码进行预测并选择正确的列。

  • 作出预测
  • 确保只选择第二列
    log_reg_pred = log_reg.predict_proba(test)[:, 1]
    预测必须采用sample_submission中显示的格式。csv文件,其中只有两个列:SK_ID_CURR和TARGET。我们将以这种格式从测试集和名为submit的预测中创建一个dafaframe。
# 提交dafaframe
submit = app_test[['SK_ID_CURR']]
submit['TARGET'] = log_reg_pred

submit.head()

《房屋信贷违约风险竞争--kaggle》

这些预测表示贷款不会被偿还的概率在0到1之间。如果我们用这些预测来对申请者进行分类,我们可以设置一个确定贷款风险的概率阈值。

  • 将提交保存到csv文件中
    submit.to_csv('log_reg_baseline.csv', index = False)
    提交现在已经保存到运行笔记本的虚拟环境中。要访问提交,在笔记本的末尾,我们将点击内核右上角的蓝色Commit & Run按钮。这将运行整个笔记本,然后让我们下载运行期间创建的所有文件。
    一旦我们运行了笔记本,创建的文件就可以在Output子选项卡下的Versions选项卡中使用。从这里,提交文件可以提交到比赛或下载。因为这个笔记本里有几个型号,所以会有多个输出文件。
逻辑回归基线在提交时应得分在0.671左右。

改进模型:随机森林

为了尝试并改良我们基线的不好的性能,我们可以更新算法。让我们尝试在相同的训练数据上使用一个随机森林,看看它如何影响性能。随机森林是一个更强大的模型特别是当我们使用数百棵树的时候。我们将在随机森林中使用100棵树。

from sklearn.ensemble import RandomForestClassifier

# 建立随机森林分类器
random_forest = RandomForestClassifier(n_estimators = 100, random_state = 50, verbose = 1, n_jobs = -1)
# Train on the training data
random_forest.fit(train, train_labels)

# 提取重要特征
feature_importance_values = random_forest.feature_importances_
feature_importances = pd.DataFrame({'feature': features, 'importance': feature_importance_values})

# 对测试数据进行预测
predictions = random_forest.predict_proba(test)[:, 1]

《房屋信贷违约风险竞争--kaggle》

# 做一个提交用的dataframe
submit = app_test[['SK_ID_CURR']]
submit['TARGET'] = predictions

# 保存提交用的dataframe
submit.to_csv('random_forest_baseline.csv', index = False)

这些预测也将在我们运行整个代码时可用。

该模型提交时得分应在0.678左右。

使用特征工程进行预测

检验多项式特征和领域知识是否改进模型的唯一方法是对这些特征进行测试和模型测试!然后,我们可以将提交性能与没有这些特性的模型进行比较,以衡量我们的特性工程的效果

poly_features_names = list(app_train_poly.columns)

# 输入多项式特征
imputer = Imputer(strategy = 'median')

poly_features = imputer.fit_transform(app_train_poly)
poly_features_test = imputer.transform(app_test_poly)

#多项式特征的比例
scaler = MinMaxScaler(feature_range = (0, 1))

poly_features = scaler.fit_transform(poly_features)
poly_features_test = scaler.transform(poly_features_test)

random_forest_poly = RandomForestClassifier(n_estimators = 100, random_state = 50, verbose = 1, n_jobs = -1)
# 训练训练集
random_forest_poly.fit(poly_features, train_labels)

# 做出对数据的预测
predictions = random_forest_poly.predict_proba(poly_features_test)[:, 1]

《房屋信贷违约风险竞争--kaggle》

# 提交dataframe的预测
submit = app_test[['SK_ID_CURR']]
submit['TARGET'] = predictions

# 保存提交的dataframe
submit.to_csv('random_forest_baseline_engineered.csv', index = False)

当提交给竞赛时,这个模型得分为0.678,与没有工程特性的模型完全一样。考虑到这些结果,在这种情况下,我们的特性构造似乎没有帮助。

测试领域特征

现在我们可以测试自己制作的领域特征。

app_train_domain = app_train_domain.drop(columns = 'TARGET')

domain_features_names = list(app_train_domain.columns)

# 输入domainnomial特征
imputer = Imputer(strategy = 'median')

domain_features = imputer.fit_transform(app_train_domain)
domain_features_test = imputer.transform(app_test_domain)

# 测量domainnomial特性
scaler = MinMaxScaler(feature_range = (0, 1))

domain_features = scaler.fit_transform(domain_features)
domain_features_test = scaler.transform(domain_features_test)

random_forest_domain = RandomForestClassifier(n_estimators = 100, random_state = 50, verbose = 1, n_jobs = -1)

# 训练训练数据
random_forest_domain.fit(domain_features, train_labels)

# 提取重要特征
feature_importance_values_domain = random_forest_domain.feature_importances_
feature_importances_domain = pd.DataFrame({'feature': domain_features_names, 'importance': feature_importance_values_domain})

# 对测试数据进行预测
predictions = random_forest_domain.predict_proba(domain_features_test)[:, 1]

《房屋信贷违约风险竞争--kaggle》

# 做一个提交用的dataframe
submit = app_test[['SK_ID_CURR']]
submit['TARGET'] = predictions

# 保存提交用的dataframe
submit.to_csv('random_forest_baseline_domain.csv', index = False)

这个分数在提交的时候是0.679,这可能表明特征工程在这个模型中没有帮助(但是它们在本文末尾的梯度提升模型中确实有帮助)。

模型解释:功能重要性

作为一种查看哪些变量最相关的简单方法,我们可以查看随机森林的特征输入。
鉴于我们在探索性数据分析中看到的相关性,我们应该预期最重要的特性是EXT_SOURCE和DAYS_BIRTH。
在未来的工作中,我们可能会使用这些特征输入作为降维的方法。

def plot_feature_importances(df):
    """
    由平面图测量模型的重要性。这可以用任何测量
如果重要性越高越好,那么特征的重要性就越大。
   
参数:
df(dataframe):功能重要性。必须在列中包含特征吗
在一个名为“重要性”的专栏中,它被称为“特征”和“重要性”
     
返回:
显示了15个最重要的特征     
df (dataframe):按特征的重要性进行排序(最高到最低)
带有一列表示归一化重要性
        """
    
    # 按特征的重要性进行排序
    df = df.sort_values('importance', ascending = False).reset_index()
    
    # 对特性的重要性进行规范化,使其加起来为1
    df['importance_normalized'] = df['importance'] / df['importance'].sum()

    # 制作测量特征的水平条形图
    plt.figure(figsize = (10, 6))
    ax = plt.subplot()
    
    # 需要反向索引绘制最重要的顶部
    ax.barh(list(reversed(list(df.index[:15]))), 
            df['importance_normalized'].head(15), 
            align = 'center', edgecolor = 'k')
    
    # 设置y轴刻度和标签
    ax.set_yticks(list(reversed(list(df.index[:15]))))
    ax.set_yticklabels(df['feature'].head(15))
    
    # 平面图的标签
    plt.xlabel('Normalized Importance'); plt.title('Feature Importances')
    plt.show()
    
    return df
#  将特征重要性显示为默认的
feature_importances_sorted = plot_feature_importances(feature_importances)

《房屋信贷违约风险竞争--kaggle》

正如预期的那样,最重要的特征是处理EXT_SOURCE和DAYS_BIRTH的特征。

我们发现只有少数几个特征对模型具有重要意义,这表明我们可以在不降低性能的情况下删除许多特征(我们甚至可以看到性能的提高)。

特征输入并不是解释模型或进行降维的最复杂的方法,但它们让我们开始理解模型在进行预测时考虑的因素。

feature_importances_domain_sorted = plot_feature_importances(feature_importances_domain)

《房屋信贷违约风险竞争--kaggle》

我们看到,我们所有的四个自己设计的功能都使它进入了前15个最重要的!这能使我们确信,我们的领域知识至少有部分走上了正轨。

结论

在这文章中,我们看到了如何开始一个Kaggle机器学习竞赛。
我们首先确保理解了数据、我们的任务以及我们的提交将被评判的度量。
然后,我们执行了一个相当简单的EDA来尝试和确定可能有助于我们建模的关系、趋势或异常。
在此过程中,我们执行了必要的预处理步骤,如编码分类变量、估算缺失值和将特性缩放到一定范围。
然后,我们从现有数据中构建了新的特征,看看这样做是否有助于我们的模型。

一旦数据探索、数据准备和特性工程完成,我们就实现了一个希望改进的基线模型。
然后我们建立了第二个稍微复杂一点的模型,以超过我们的第一个分数。
我们还进行了一个实验来确定添加特征工程变量的效果。

我们遵循一个机器学习项目的概要:

1.了解问题和数据
2.数据清理和格式化(这主要是为我们做的)
3.探索性数据分析
4.基准模型
5.改进的模型
6.模型解释(只有一点点)
机器学习竞赛确实与典型的数据科学问题稍有不同,因为我们只关心在单个指标上获得最佳性能,而不关心解释。
然而,通过尝试理解我们的模型如何做出决策,我们可以尝试改进它们或检查错误以纠正错误。

datafram
随机森林
独热编码
生成多项式特征

    原文作者:DouMarK
    原文地址: https://www.jianshu.com/p/c494a3a92af5
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞