前语: 本项目来源于Kaggle平台的Elo Merchant Category Recommendation,参考了《机器学习算法比赛实战》和一些热心网友共享的材料,假如有侵权联系我删去就好,做这个项意图主意是,将这个比赛作为自己入门机器学习的学习赛,而且看到上好像没有这个比赛的相关信息,因而就整理好共享出来,感兴趣的同学可以一块学习一下机器学习比赛的完好流程,所用到的参考材料已放于终究引证

主张运用32G内存及上的装备完成,数据集略微有点大,若无条件可以到Ai Studio测验复现哇,一键运转

一、赛题背景介绍

  • 巴西付出品牌Elo
    本次比赛其实是由巴西最大的付出品牌之一的Elo和Kaggle协作举行的比赛,奖金和数据都由Elo公司提供。谈到付出品牌,国内用户首要会想到类似付出宝、PayPal这些带有浓烈互联网色彩的付出品牌,可是在巴西,线上付出更多是由本地银行主导,且线上付出的信贷产品也首要以信用卡为主。Elo便是这样的一家公司,在2011年由巴西三家首要银行合资创立,首要担任线上付出事务,而且以信用卡作为中心金融产品,现在已发放超越1.1亿张信用卡,是巴西最大的本地在线付出品牌之一。
【Kaggle】Elo 用户忠诚度预测

而且,Elo不仅是付出入口,更是一个“o2o”平台,经过App,用户可以查阅本地餐饮旅馆电影旅游机票等各项服务,并支撑信用卡在线付出。形象点了解,就好比把美团主页移到付出宝,而且付出宝没有花呗,取而代之的是自己发行的信用卡。或许愈加形象的了解,就类似国内招行信用卡掌上日子的事务形式:

【Kaggle】Elo 用户忠诚度预测
  • 事务方针:更好的进行本地服务引荐
    在官方给出的阐明中,咱们不难发现,Elo运用机器学习算法技能的中心意图,是为了更好的在App内为用户引荐当地吃穿住行的商家服务,包含热门餐厅展示、优惠折扣提醒等(十分类似上图掌上日子主页的引荐)。也便是说,其根本意图是为了引荐,或许说为每个用户进行愈加个性化的引荐,也便是赛题标题中的所写的:Merchant Category Recommendation(商户类别引荐)。可是,需求留意的是,本次比赛的建模方针却和引荐体系并不直接相关。赛题阐明中,在介绍完事务方针之后,紧接着就介绍了本次赛题的方针:对用户的忠诚度评分进行猜测。

  • 算法方针:用户忠诚度评分猜测
    所谓用户忠诚度评分,经过后续检查Evaluation不难发现,其实便是对每个用户的评分进行猜测,本质上是个回归问题。
    信任刚接触到本次赛题的小伙伴,必定会觉得赛题阐明和建模方针有些“文不对题”,究竟用户忠诚度评分貌似和个性化引荐并无直接关系,尤其此处用户忠诚度评分并不是针对某类产品或许某类商家的忠诚度评分。
    环绕这个问题,赛题阐明给出了十分笼统的解说,仅仅简略提到经过对用户忠诚度评分,可以为其提供最相关的时机(serve the most relevant opportunities to individuals),或许可以了解成是用户较为中意的线下服务,而且协助ELo节省活动成本,为用户提供更好的体会。其实也就等于是没有解说忠诚度评分和引荐体系到底是什么关系。Kaggle本赛题论坛上也有许多相关讨论帖,官方给出的解说粗心是经过忠诚度评分给用户引荐商铺和产品,这个进程并不是一个传统的协同过滤或许引荐体系进行引荐的进程,无论如何,先做好忠诚度猜测就好。

二、数据集简介

本次赛题数据较多、数据量也相对较大,部分数据乃至无法直接经过一般Excel直接打开。接下来咱们快速了解每个数据集的根本意义:

【Kaggle】Elo 用户忠诚度预测

  总的来说,上述7个数据文件大约可以分为三类,其一是根本信息类数据集,包含Data_Dictionary和sample_submission。其间Data_Dictionary数据集是一切数据的数据字典,即包含了一切数据各字段的意义,而sample_submission则是提交效果时的典范数据。

  • Data Dictionary/Data_Dictionary:数据字典

  一切其他数据表中每个字段的意义,适当于是其他各数据表的阐明书。数据字典包含多个sheet,每个sheet对应一个数据表的字段和解说:

【Kaggle】Elo 用户忠诚度预测

  终究建模效果提交格局,也便是以“一个id”+“对应猜测效果”的格局进行提交。据此咱们也能发现,实践上咱们是需求猜测每个card_id的用户忠诚度评分。

【Kaggle】Elo 用户忠诚度预测

RMSE的核算进程如下:

RMSE=1n∑i=1n(yi−yi)2RMSE= \sqrt{\frac{1}{n}\sum^n_{i=1}(y_i-\hat y_i)^2}

  然后便是完结比赛的必要数据,也便是train和test两个数据集。顾名思义,train数据集便是练习数据,test便是测验数据集,二者特征一致,极简状况下咱们可以直接在train上练习模型,在test上进行猜测。

  终究一类则是弥补数据集,也便是 ‘historical_transactions.csv’、’new_merchant_transactions.csv’和’merchants.csv’,其间前两个数据集记载了练习集和测验集信用卡的消费记载,而终究一个数据集则是前两个数据会集商铺信息(某特征)的进一步解说。在实践建模进程中,纳入更多数据进行规律发掘,则有或许到达更好的效果。

三、数据探究与清洗

  在对train和test数据集完结探究性剖析之后,接下来咱们需求进一步环绕官方给出的商户数据与信用卡买卖数据进行解读和剖析,并对其进行数据清洗,从而为后续的特征工程和算法建模做准备。

  一般来说,在数据解读、数据探究和初步数据清洗都是同步进行的,都是前期十分重要的作业事项。其间,数据解读的意图是为了快速获取数据集的根本信息,经过比对官方给出的字段解说,快速了解数据集的字段意义,这关于许多杂乱数据场景下的建模对错常有必要的。而数据探究,顾名思义,便是快速了解数据集的根本数据状况,首要作业包含数据正确性校验和数据质量剖析,中心意图是为了可以快速了解各字段的根本状况,包含默许各字段的数据类型、数据集是否存在数据不一致的状况、数据集重复值状况、缺失值状况等,当然,经过一系列的数据探究,也可以快速加深对数据集的了解。当然,数据探究结束之后,就需求进行数据清洗了,所谓数据清洗,指的是在建模/特征工程之前进行的必要的调整,以保证后续操作可履行,包含数据字段类型调整、重复值处理、缺失值处理等等,当然,有些操作或许在后续会进行少许优化,比方数据清洗阶段咱们可以先测验进行较为简略的缺失值添补,在后续的建模进程中咱们还可以依据实践建模效果来调整缺失值添补战略。

  咱们也可将数据探究与数据清洗的进程总结如下:

【Kaggle】Elo 用户忠诚度预测

  咱们将对商户数据、买卖数据的三张表进行数据探究和数据清洗。

  因为该项作业较为繁琐,咱们简略总结上述针对商户数据和买卖数据的完好步骤如下:

商户数据merchants.csv

  • 区别接连字段和离散字段;
  • 对字符型离散字段进行字典排序编码;
  • 对缺失值处理,此处一致运用-1进行缺失值填充,本质上是一种标示;
  • 对接连性字段的无量值进行处理,用该列的最大值进行替换;
  • 去除重复数据;

买卖数据new_merchant_transactions.csv和historical_transactions.csv

  • 区别字段类型,分为离散字段、接连字段和时刻字段;
  • 和商户数据的处理办法相同,对字符型离散字段进行字典排序,对缺失值进行一致填充;
  • 对新生成的Object字段进行字典排序编码;

3.1 创立清洗后数据

  结合练习集和测验集的清洗流程,咱们可以在此一致履行一切数据的数据清洗作业,并将其终究保存为本地文件,方便后续特征工程及算法建模进程运用,其流程如下:

# 读取数据
import gc
import time
import numpy as np
import pandas as pd
from datetime import datetime
train = pd.read_csv('eloData/train.csv')
test =  pd.read_csv('eloData/test.csv')
merchant = pd.read_csv('eloData/merchants.csv')
new_transaction = pd.read_csv('eloData/new_merchant_transactions.csv')
history_transaction = pd.read_csv('eloData/historical_transactions.csv')
# 字典编码函数
def change_object_cols(se):
    value = se.unique().tolist()
    value.sort()
    return se.map(pd.Series(range(len(value)), index=value)).values
# 练习集/测验集的数据预处理
# 对初次活泼月份进行编码
se_map = change_object_cols(train['first_active_month'].append(test['first_active_month']).astype(str))
train['first_active_month'] = se_map[:train.shape[0]]
test['first_active_month'] = se_map[train.shape[0]:]
# 测验集/练习集导出与内存清理
!mkdir preprocess
train.to_csv("preprocess/train_pre.csv", index=False)
test.to_csv("preprocess/test_pre.csv", index=False)
del train
del test
gc.collect()
# 商户信息预处理
# 1、依据事务意义区别离散字段category_cols与接连字段numeric_cols。
category_cols = ['merchant_id', 'merchant_group_id', 'merchant_category_id',
       'subsector_id', 'category_1',
       'most_recent_sales_range', 'most_recent_purchases_range',
       'category_4', 'city_id', 'state_id', 'category_2']
numeric_cols = ['numerical_1', 'numerical_2',
     'avg_sales_lag3', 'avg_purchases_lag3', 'active_months_lag3',
       'avg_sales_lag6', 'avg_purchases_lag6', 'active_months_lag6',
       'avg_sales_lag12', 'avg_purchases_lag12', 'active_months_lag12']
# 2、对非数值型的离散字段进行字典排序编码。
for col in ['category_1', 'most_recent_sales_range', 'most_recent_purchases_range', 'category_4']:
    merchant[col] = change_object_cols(merchant[col])
# 3、为了可以更方便核算,进行缺失值的处理,对离散字段一致用-1进行填充。
merchant[category_cols] = merchant[category_cols].fillna(-1)
# 4、对离散型字段探查发现有正无量值,这是特征提取以及模型所不能承受的,因而需求对无限值进行处理,此处选用最大值进行替换。
inf_cols = ['avg_purchases_lag3', 'avg_purchases_lag6', 'avg_purchases_lag12']
merchant[inf_cols] = merchant[inf_cols].replace(np.inf, merchant[inf_cols].replace(np.inf, -99).max().max())
# 5、平均值进行填充,后续有需求再进行优化处理。
for col in numeric_cols:
    merchant[col] = merchant[col].fillna(merchant[col].mean())
# 6、去除与transaction买卖记载表格重复的列,以及merchant_id的重复记载。
duplicate_cols = ['merchant_id', 'merchant_category_id', 'subsector_id', 'category_1', 'city_id', 'state_id', 'category_2']
merchant = merchant.drop(duplicate_cols[1:], axis=1)
merchant = merchant.loc[merchant['merchant_id'].drop_duplicates().index.tolist()].reset_index(drop=True)
# 与处理完后先不着急导出或删去,后续需求和买卖数据进行拼接。
# 买卖数据预处理
# 1、为了一致处理,首要拼接new和history两张表格,后续可以month_lag>=0进行区别。
transaction = pd.concat([new_transaction, history_transaction], axis=0, ignore_index=True)
del new_transaction
del history_transaction
gc.collect()
# 2、同样区别离散字段、接连字段以及时刻字段。
numeric_cols = [ 'installments', 'month_lag', 'purchase_amount']
category_cols = ['authorized_flag', 'card_id', 'city_id', 'category_1',
       'category_3', 'merchant_category_id', 'merchant_id', 'category_2', 'state_id',
       'subsector_id']
time_cols = ['purchase_date']
# 3、可模仿merchant的处理办法对字符型的离散特征进行字典序编码以及缺失值填充。
for col in ['authorized_flag', 'category_1', 'category_3']:
    transaction[col] = change_object_cols(transaction[col].fillna(-1).astype(str))
transaction[category_cols] = transaction[category_cols].fillna(-1)
transaction['category_2'] = transaction['category_2'].astype(int)
# 4、进行时刻段的处理,简略起见进行月份、日期的星期数(作业日与周末)、以及
# 时刻段(上午、下午、晚上、清晨)的信息提取。
transaction['purchase_month'] = transaction['purchase_date'].apply(lambda x:'-'.join(x.split(' ')[0].split('-')[:2]))
transaction['purchase_hour_section'] = transaction['purchase_date'].apply(lambda x: x.split(' ')[1].split(':')[0]).astype(int)//6
transaction['purchase_day'] = transaction['purchase_date'].apply(lambda x: datetime.strptime(x.split(" ")[0], "%Y-%m-%d").weekday())//5                                                                    
del transaction['purchase_date']
# 5、对新生成的购买月份离散字段进行字典序编码。
transaction['purchase_month'] = change_object_cols(transaction['purchase_month'].fillna(-1).astype(str))
# 完结买卖数据预处理后,即可进行买卖数据和商铺数据的表格兼并。
  • 表格兼并

  在兼并的进程中,有两种处理计划,其一是对缺失值进行-1添补,然后将一切离散型字段化为字符串类型(为了后续字典兼并做准备),其二则是新增两列,分别是purchase_day_diff和purchase_month_diff,其数据为买卖数据以card_id进行groupby并终究提取出purchase_day/month并进行差分的效果。

# 计划一代码
# 为了方便特征的一致核算将其merge兼并,从头区别相应字段品种。
cols = ['merchant_id', 'most_recent_sales_range', 'most_recent_purchases_range', 'category_4']
transaction = pd.merge(transaction, merchant[cols], how='left', on='merchant_id')
numeric_cols = ['purchase_amount', 'installments']
category_cols = ['authorized_flag', 'city_id', 'category_1',
       'category_3', 'merchant_category_id','month_lag','most_recent_sales_range',
                 'most_recent_purchases_range', 'category_4',
                 'purchase_month', 'purchase_hour_section', 'purchase_day']
id_cols = ['card_id', 'merchant_id']
transaction[cols[1:]] = transaction[cols[1:]].fillna(-1).astype(int)
transaction[category_cols] =transaction[category_cols].fillna(-1).astype(str)
# 随后将其导出为transaction_d_pre.csv
transaction.to_csv("preprocess/transaction_d_pre.csv", index=False)
# 清空开释内存
del transaction
gc.collect()
# 计划二代码
merchant = pd.read_csv('eloData/merchants.csv')
new_transaction = pd.read_csv('eloData/new_merchant_transactions.csv')
history_transaction = pd.read_csv('eloData/historical_transactions.csv')
# 1、依据事务意义区别离散字段category_cols与接连字段numeric_cols。
category_cols = ['merchant_id', 'merchant_group_id', 'merchant_category_id',
       'subsector_id', 'category_1',
       'most_recent_sales_range', 'most_recent_purchases_range',
       'category_4', 'city_id', 'state_id', 'category_2']
numeric_cols = ['numerical_1', 'numerical_2',
     'avg_sales_lag3', 'avg_purchases_lag3', 'active_months_lag3',
       'avg_sales_lag6', 'avg_purchases_lag6', 'active_months_lag6',
       'avg_sales_lag12', 'avg_purchases_lag12', 'active_months_lag12']
# 2、对非数值型的离散字段进行字典排序编码。
for col in ['category_1', 'most_recent_sales_range', 'most_recent_purchases_range', 'category_4']:
    merchant[col] = change_object_cols(merchant[col])
# 3、为了可以更方便核算,进行缺失值的处理,对离散字段一致用-1进行填充。
merchant[category_cols] = merchant[category_cols].fillna(-1)
# 4、对离散型字段探查发现有正无量值,这是特征提取以及模型所不能承受的,因而需求对无限值进行处理,此处选用最大值进行替换。
inf_cols = ['avg_purchases_lag3', 'avg_purchases_lag6', 'avg_purchases_lag12']
merchant[inf_cols] = merchant[inf_cols].replace(np.inf, merchant[inf_cols].replace(np.inf, -99).max().max())
# 5、平均值进行填充,后续有需求再进行优化处理。
for col in numeric_cols:
    merchant[col] = merchant[col].fillna(merchant[col].mean())
# 6、去除与transaction买卖记载表格重复的列,以及merchant_id的重复记载。
duplicate_cols = ['merchant_id', 'merchant_category_id', 'subsector_id', 'category_1', 'city_id', 'state_id', 'category_2']
merchant = merchant.drop(duplicate_cols[1:], axis=1)
merchant = merchant.loc[merchant['merchant_id'].drop_duplicates().index.tolist()].reset_index(drop=True)
# 1、为了一致处理,首要拼接new和history两张表格,后续可以month_lag>=0进行区别。
transaction = pd.concat([new_transaction, history_transaction], axis=0, ignore_index=True)
del new_transaction
del history_transaction
gc.collect()
# 2、同样区别离散字段、接连字段以及时刻字段。
numeric_cols = [ 'installments', 'month_lag', 'purchase_amount']
category_cols = ['authorized_flag', 'card_id', 'city_id', 'category_1',
       'category_3', 'merchant_category_id', 'merchant_id', 'category_2', 'state_id',
       'subsector_id']
time_cols = ['purchase_date']
# 3、可模仿merchant的处理办法对字符型的离散特征进行字典序编码以及缺失值填充。
for col in ['authorized_flag', 'category_1', 'category_3']:
    transaction[col] = change_object_cols(transaction[col].fillna(-1).astype(str))
transaction[category_cols] = transaction[category_cols].fillna(-1)
transaction['category_2'] = transaction['category_2'].astype(int)
# 4、进行时刻段的处理,简略起见进行月份、日期的星期数(作业日与周末)、以及
# 时刻段(上午、下午、晚上、清晨)的信息提取。
transaction['purchase_month'] = transaction['purchase_date'].apply(lambda x:'-'.join(x.split(' ')[0].split('-')[:2]))
transaction['purchase_hour_section'] = transaction['purchase_date'].apply(lambda x: x.split(' ')[1].split(':')[0]).astype(int)//6
transaction['purchase_day'] = transaction['purchase_date'].apply(lambda x: datetime.strptime(x.split(" ")[0], "%Y-%m-%d").weekday())//5                                                                    
del transaction['purchase_date']
# 5、对新生成的购买月份离散字段进行字典序编码。
transaction['purchase_month'] = change_object_cols(transaction['purchase_month'].fillna(-1).astype(str))
cols = ['merchant_id', 'most_recent_sales_range', 'most_recent_purchases_range', 'category_4']
transaction = pd.merge(transaction, merchant[cols], how='left', on='merchant_id')
numeric_cols = ['purchase_amount', 'installments']
category_cols = ['authorized_flag', 'city_id', 'category_1',
       'category_3', 'merchant_category_id','month_lag','most_recent_sales_range',
                 'most_recent_purchases_range', 'category_4',
                 'purchase_month', 'purchase_hour_section', 'purchase_day']
id_cols = ['card_id', 'merchant_id']
transaction['purchase_day_diff'] = transaction.groupby("card_id")['purchase_day'].diff()
transaction['purchase_month_diff'] = transaction.groupby("card_id")['purchase_month'].diff()
# 导出
transaction.to_csv("preprocess/transaction_g_pre.csv", index=False)
del transaction
gc.collect()

四、特征工程与模型练习

  在经历了漫长的数据解读、探究与清洗之后,接下来,咱们将进入到特征工程与算法建模的环节。并在本末节的结尾,得出终究的猜测效果。

  在此前的内容中,咱们终究得到了train.csv、test.csv和transaction.csv三张表。首要咱们简略回忆下这三张数据表的构建进程,首要,现在得到的练习集和测验集都是由原始练习集/测验集将时刻字段处理后得到:

【Kaggle】Elo 用户忠诚度预测

  而transaction数据集则相对杂乱,该数据集是有一张商户数据merchants.csv和两张买卖数据表处理后兼并得到,该进程如下所示:

【Kaggle】Elo 用户忠诚度预测

接下来,咱们就依据这三张表进行后续操作。

4.1 特征工程

  首要需求对得到的数据进一步进行特征工程处理。一般来说,关于已经清洗完的数据,特征工程部分中心需求考虑的问题便是特征创立(衍生)与特征挑选,也便是先尽或许创立/添加或许对模型效果有正面影响的特征,然后再对这些进行挑选,以保证模型运转稳定性及运转功率。当然,无论是特征衍生仍是特征挑选,其实都有十分多的办法。此处为了保证办法具有通用性,此处罗列两种特征衍生的办法,即创立通用组合特征与事务核算特征;并在特征创立结束后,介绍一种基础而通用的特征挑选的办法:基于皮尔逊相关系数的Filter办法进行特征挑选。这些办法都对错常通用且有效的办法,不仅可以协助本次建模获得较好的效果,而且也能广泛适用到其他各场景中。

4.1.1 通用组合特征创立

  首要是测验创立一些通用组合特征。

  所谓通用组合特征,指的是经过核算不同离散特征在不同取值水平下、不同接连特征取值之和创立的特征,并依据card_id进行分组求和。详细创立进程咱们可以如下简例来进行了解:

【Kaggle】Elo 用户忠诚度预测

经过该办法创立的数据集,不仅可以尽或许从更多维度表明每个card_id的消费状况,一起也可以顺利和练习集/测验集完结拼接,从而带入模型进行建模。相关进程咱们可以凭借Python中的字典目标类型来进行完成。

4.1.2 基于transaction数据集创立通用组合特征

  接下来,咱们将上述进程应用于建模真实数据,即在此前已经清洗完的transaction数据集上来完结通用组合特征的创立作业。

此处需求留意的是,因为transaction数据集本身较大,尽管特征创立作业的求和部分会必定程度削减终究带入建模的数据体量,但操作transaction数据集本身就需求耗费许多的内容及必定的时刻,假如要手动履行下述代码,主张至少装备V100 32G以上环境以满意内存需求

# 字段类型标示
# 标示离散字段or接连型字段
numeric_cols = ['purchase_amount', 'installments']
category_cols = ['authorized_flag', 'city_id', 'category_1',
       'category_3', 'merchant_category_id','month_lag','most_recent_sales_range',
                 'most_recent_purchases_range', 'category_4',
                 'purchase_month', 'purchase_hour_section', 'purchase_day']
id_cols = ['card_id', 'merchant_id']
# 特征创立
# 创立字典用于保存数据
features = {}
card_all = train['card_id'].append(test['card_id']).values.tolist()
for card in card_all:
    features[card] = {}
# 标记不同类型字段的索引
columns = transaction.columns.tolist()
idx = columns.index('card_id')
category_cols_index = [columns.index(col) for col in category_cols]
numeric_cols_index = [columns.index(col) for col in numeric_cols]
# 记载运转时刻
s = time.time()
num = 0
# 履行循环,并在此进程中记载时刻
for i in range(transaction.shape[0]):
    va = transaction.loc[i].values
    card = va[idx]
    for cate_ind in category_cols_index:
        for num_ind in numeric_cols_index:
            col_name = '&'.join([columns[cate_ind], str(va[cate_ind]), columns[num_ind]])
            features[card][col_name] = features[card].get(col_name, 0) + va[num_ind]
    num += 1
    if num%1000000==0:
        print(time.time()-s, "s")
del transaction
gc.collect()

  可以发现,全体运转所需时刻较长。此外,此处需求留意的是,card_id的提取并不是从transaction从提取,而是从练习集和测验会集提取,首要是为了保证card_id的唯一性,究竟transaction是为了train集服务的

  在提取完特征后,接下来即可将带有买卖数据特征的兼并入练习集和测验集了:

# 字典转dataframe
df = pd.DataFrame(features).T.reset_index()
del features
cols = df.columns.tolist()
df.columns = ['card_id'] + cols[1:]
# 生成练习集与测验集
train = pd.merge(train, df, how='left', on='card_id')
test =  pd.merge(test, df, how='left', on='card_id')
del df
train.to_csv("preprocess/train_dict.csv", index=False)
test.to_csv("preprocess/test_dict.csv", index=False)
gc.collect() 

  至此,咱们就完结了从transaction中提取通用特征的进程。简略检查数据集根本状况:

【Kaggle】Elo 用户忠诚度预测

4.1.3 事务核算特征创立

  当然,除了通用组合特征外,咱们还可以考虑从另一个视点进行特征提取,那便是先依据card_id来进行分组,然后核算不同字段再各组内的相关核算量,再将其作为特征,带入进行建模。其根本结构特征思路如下:

【Kaggle】Elo 用户忠诚度预测

该进程并不杂乱,可以经过pandas中的groupby进程迅速完成。和此前特征结构的思路不同,经过该办法结构的特征,不会存在许多的缺失值,而且新增的列也将相对较少。

4.1.3 数据兼并

  至此,咱们即完结了从两个不同视点提取特征的相关作业。不过到现在上述两套计划的特征依然保存在不同数据文件中,咱们需求对其进行兼并,才干进一步带入进行建模,兼并进程较为简略,只需求将train_dict(test_dict)与train_group(test_group)依据card_id进行横向拼接、然后除掉重复列即可。

4.2 随机森林模型猜测

  • 特征挑选

  因为此前创立了数千条特征,若带入全部特征进行建模,势必极大程度延长模型建模时刻,而且带入太多无关特征对模型效果提高有限,因而此处咱们凭借皮尔逊相关系数,挑选和标签最相关的300个特征进行建模。当然此处300也可以自行调整。


# 提取特征名称
features = train.columns.tolist()
features.remove("card_id")
features.remove("target")
featureSelect = features[:]
# 核算相关系数
corr = []
for fea in featureSelect:
    corr.append(abs(train[[fea, 'target']].fillna(0).corr().values[0][1]))
# 取top300的特征进行建模,详细数量可选
se = pd.Series(corr, index=featureSelect).sort_values(ascending=False)
feature_select = ['card_id'] + se[:300].index.tolist()
# 输出效果
train = train[feature_select + ['target']]
test = test[feature_select]
  • 凭借网格查找进行参数调优

  接下来,咱们将凭借sklearn中基础调参东西—网格查找(Gridsearch)进行参数查找与调优。

然后依据网格查找的要求,咱们需求依据随机森林的参数状况,有针对性的发明一个参数空间,随机森林根本参数根本状况如下:

Name Description
criterion 规矩评估目标或丢失函数,默许基尼系数,可选信息熵
splitter 树模型成长办法,默许以丢失函数取值削减最快办法成长,可选随机依据某条件进行区别
max_depth 树的最大成长深度,类似max_iter,即一共迭代几回
min_samples_split 内部节点再区别所需最小样本数
min_samples_leaf 叶节点包含最少样本数
min_weight_fraction_leaf 叶节点所需最小权重和
max_features 在进行切分时候最多带入多少个特征进行区别规矩挑选
random_state 随机数种子
max_leaf_nodes 叶节点最大个数
min_impurity_decrease 数据集再区别至少需求降低的丢失值
min_impurity_split 数据集再区别所需最低不纯度,将在0.25版本中移除
class_weight 各类样本权重

其间咱们挑选”n_estimators”、”min_samples_leaf”、”min_samples_split”、”max_depth”和”max_features”进行参数查找:

  然后是关于网格查找东西的挑选。随着sklearn不断完善,有越来越多的网格查找东西可供挑选,但全体来看其实便是在功率和精度之间做权衡,有些网格查找东西因为是全域枚举(如GridSearchCV),所以履行功率较慢、但效果精度有保证,而假如愿意献身精度换履行功率,则也有许多东西可以挑选,如RandomizedSearchCV。当然,在最新的sklearn版本中,还呈现了一种更高效的查找战略——HalvingGridSearchCV,该办法先两两比对、然后逐层挑选的办法来进行参数挑选,而且一起支撑HalvingGridSearchCV和HalvingRandomSearchCV。

  环绕本次比赛的数据,在实践履行网格查找的进程中,主张先运用RandomizedSearchCV确定大约规模,然后再运用GridSearchCV高精度查找详细参数取值,此处咱们在大致确定最优参数规模的前提下设置在一个相对较小的参数空间内来进行查找:


from sklearn.metrics import mean_squared_error
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV
features = train.columns.tolist()
features.remove("card_id")
features.remove("target")
parameter_space = {
    "n_estimators": [79, 80, 81], 
    "min_samples_leaf": [29, 30, 31],
    "min_samples_split": [2, 3],
    "max_depth": [9, 10],
    "max_features": ["auto", 80]
}
# 然后构建随机森林评估器,并输入其他超参数取值
clf = RandomForestRegressor(
    criterion="mse",
    n_jobs=15,
    random_state=22)
# 开端网格查找
# 该进程确实十分慢, 这儿大约用了V100卡大约8个h左右,可以测验切换其他办法进行参数查找
grid = GridSearchCV(clf, parameter_space, cv=2, scoring="neg_mean_squared_error")
grid.fit(train[features].values, train['target'].values)
# 检查效果
print(grid.best_params_)
# 也可以直接调用最优参数组成的评估器
print(grid.best_estimator_)
# 也可以直接检查在练习集上的终究评分:
print(np.sqrt(-grid.best_score_))
grid.best_estimator_.predict(test[features])
# 然后将效果按照所需求提交的格局写入csv文档
test['target'] = grid.best_estimator_.predict(test[features])
test[['card_id', 'target']].to_csv("submission_randomforest.csv", index=False)

数据文档写入结束后,接下来就可以直接在Kaggle上提交了。上传提交效果数据和下载数据进程类似,都可以直接运用网页功用完成,或许经过命令行的办法完成。

  • 效果提交

在Kaggle比赛主页 找到Late Submission进行效果提交,只需将效果文件在线提交即可:

【Kaggle】Elo 用户忠诚度预测
【Kaggle】Elo 用户忠诚度预测

根本能到达一个baseline的效果

【Kaggle】Elo 用户忠诚度预测

五、后续优化战略

  • 文本特征发掘

  在特征处理的进程中,可以测验运用NLP范畴的TF-IDF进行词频核算,添加离散变量特征;

  • 更多衍生特征

  除了对离散变量进行词频核算外,咱们还可以考虑构建更多特征,如全局card_id特征、最近两个月 card_id特征、二阶特征和弥补特征等,来更深程度发掘数据集信息;

  • 更多集成算法

  除了随机森林外,还有许多功用十分强壮的集成模型,包含LightGBM、XGBoost等,都是可以测验运用的算法;

  • 模型交融办法

  已然运用了多集成模型来进行建模,那么模型交融也势在必行。模型交融可以很好的综合各集成模型的输出效果,来做出终究愈加综合的判别。当然模型交融可以考虑简略加权交融或许stacking交融办法;

  • 愈加详尽的数据处理

  除了技能手段外,咱们可也可以环绕此前得出的事务剖析结论,对数据集进行愈加详尽的处理,如此前标签中呈现的异常值的处理、13家商户没有曩昔一段时刻营销信息等,经过愈加详尽的处理,可以让模型到达更好的效果。

引证

  • blog.csdn.net/qq_37039382…
  • github.com/jinchenyu/k…
  • 《机器学习算法比赛实战》