1. 程式人生 > >基於Xgboost + LR + Keras 建模評估使用者信用狀態

基於Xgboost + LR + Keras 建模評估使用者信用狀態

  1. 專案背景
    拍拍貸“魔鏡風控系統”基於400多個數據維度來對當前使用者的信用狀態進行評估,通過歷史資料每個借款人的性別、年齡、籍貫、學歷資訊、通訊方式、網站登入資訊、第三方時間資訊等使用者資訊以及對應的分類標籤,在此基礎上結合新發標的使用者資訊,得到使用者六個月內逾期率的預測,為金融平臺提供關鍵的決策支援。

  2. 資料格式
    資料下載–點這裡
    這裡麵包含三期資料,每期資料內容和格式相同,這裡麵包括兩部分資訊:
    一部分是Master
    PPD_dat_1.csv
    PPD_dat_2.csv
    PPD_dat_3.csv
    這裡寫圖片描述
    一部分是Log info
    PPD_daht_1_LogInfo.csv PPD_daht_2_LogInfo.csv PPD_daht_3_LogInfo.csv
    這裡寫圖片描述


    一部分是Update info
    PPD_daht_1_Userupdate.csv
    PPD_daht_2_Userupdate.csv
    PPD_daht_3_Userupdate.csv
    這裡寫圖片描述

3. 問題思路

資料清洗

  • 對資料的合併:要把幾次的資料合併到一起;要把主表和日誌表合併在一起;要把訓練集和測試集合並在一起。
  • 對字元空格的轉換:存在著漢字和英文字元,需要轉換成數值形式;存在著資料表達不統一的情況,比如北京和北京市,QQ和Qq,以及多空格等情況。
    • 對LogInfo與UserupdateInfo 日期資訊的處理等:歷史記錄相對於主表的主要差異在於對於每個index的各項資訊,主表是按列彙總,而歷史記錄是按行堆疊,因此將歷史記錄按index 分組,將各行資訊彙總到各列上,使得各個index 對應唯一一行以與主表連線。此外,對每筆貸款的歷史記錄中的時間資訊,通常其起始時間和登陸/更新資訊的總頻率對衡量借款人的行為較為重要。

資料摘要

  • 它的作用是簡化並理解資料特徵,主要包括了變數的型別、變數空值/非空值資料、變數頻數前五的值與對應數量、其他值的數量、數字變數的統計量(均值、方差、四分位數)

特徵工程

  • 數值特徵的保留與非數值特徵的轉換:有額外資訊的非數值變數轉化為對應的數值:時間–>年月日周、相對天數,地名–>經緯度和城市等級,定序變數–>序數;其他非數值變數全部0-1啞變數處理。
  • 選取統計量概況一系列相似變數:取中位數、方差、求和、最值、空值樹等概況各時期第三方資訊、幾個城市變數資訊等,統計量儘量要相互獨立
  • 刪除稀疏特徵:空值/同一值佔絕大比例的列
  • 刪除共線特徵:相關矩陣的嚴格下三角陣有接近正負1的列
  • 使用中位數填充空值,通常資料分佈不對稱時,中位數比平均數更能保持排序關係
  • 最後正態標準化:rank與正態分佈的百分位函式複合。之所以考慮正態標準化,是為了應對實際資料的大量有偏分佈和極端值,在正態標準化的情況下,資料只保留排序關係,徹底去除了有偏分佈和極端值,在大樣本下能滿足眾多模型假設,在本次資料集下能明顯提高邏輯迴歸和神經網路的效果。

模型選擇

  • Logistic Regression 簡單、快捷、穩健、可解釋性強,工業界最常用的模型之一。雖然LR模型對變數關係的線性限制,使得其難以達到最優,但可以在建模時通過增加L2罰函式 來減少過擬合;此外,作為基準,能夠對資料清洗效果和模型表現作出快速評估。最後,與樹模型、神經網路模型等模型差異度較大,適合進行模型的加權組合,補充模型精度。
  • XGBoost 適合處理非線性、變數成分多元化、樣本和變數間無固定模式關聯(影象、語音),在KDDCup等競賽中表現優秀的模型之一。如果以精度為目標,綜合穩健性、速度、通用性等因素可以首選XGBoost
  • Keras ,深度學習框架,分為線性模型和泛化模型,其中裡面各層獨立,靈活性高。深度學習的出發點是各變數充滿複雜的非線性關係,通過不斷優化網路權值向真實關聯趨近;而XGBoost 的出發點是認為各變數獨立,從決策樹的二分關聯疊加向真實關聯趨近。所以兩者各有特點,有較高的互補性。

交叉檢驗

  • 相比於使用單訓練預測集建模,交叉檢驗的優勢在於:1.更準確的估計模型預測精度(均值)2,預估模型預測效果範圍(標準差) 3,減少過擬合
  • 實現步驟:1.將樣本行的index 隨機拆成10份儲存,2. 每次取一份作為驗證集,其他九份一起作為訓練集,進行訓練,得到一個模型,3. 依次取不同的一份作為驗證集,一共得到10個模型,4. 通過將10個模型取平均進行預測。

變數評估和處理

  • XGBoost 在建模過程中同時可以得到模型中各個特徵的重要程度,可以作為特徵重要性的判斷標準
  • LR 模型訓練完成後每個特徵都有一個權值,權值的大小和正負反映了該特徵的重要程度和方向、
  • 通過以上方法可以得到判斷出最重要的特徵集合,進行可以對這些特徵再進行一定的特徵工程,實現資訊挖掘最大化;同時也能判斷出相對影響力極小的特徵,需要情況下可以進行清除。

引數優化

在特徵工程和模型選擇之後,有一項重要且考驗耐心的工作,那就是調參。通常情況下,要做以下步驟:
1. 調參之前,理解模型和引數的含義。(否則你很可能不知道調參的粒度和調參的方向)
2. 先用單資料集,從預設值開始,手工逐個調整,對於引數範圍大的採取等比數列的方式增加/減少粒度,對於引數範圍小的採取等差數列的方式增加/減少粒度。這樣做的結果是對各個引數確定了一個合理的範圍。
3. 然後可以採用交叉驗證和組合搜尋的方法來自動得到最優引數,這個過程可能較長,所以這裡交叉驗證的折數不要太大。另外折數小除了節約時間以外,同時也因為資料集的不同,避免在最後的結果上造成過擬合

模型融合

  • 一種方法是加權融合,
  • 一種方法是基於rank 融合。

4 演算法實現過程及其程式碼細節

4.1 資料清理
引入的包

"""
Created on Sat Jun 18 10:06:28 2016
@author: Yes,boy! 
"""
import pandas as pd
import numpy  as np

path = "D:/InAction/PPDS/data"
title = "PPD"

第一部分是處理主表:
我們首先構造一個函式Read_concat_csv,來實現幾份資料的合併,通過pandas.concat 來實現。

#輸入:檔名列表,read_csv方法中的引數字典
#輸出:合併後的資料集
def Read_concat_csv(file,par_csv={}):
    da = pd.concat(map(lambda x:pd.read_csv(x,**par_csv),file))
    return da

這裡面有三處語法細節:
1. pd.read_csv()
2. map()
3. concat

接著構造一個對資料不統一情況的處理,比如北京和北京市,河南和河南省,以及多餘的空格

#輸入:一個字串
#輸出:處理掉空格、市、省的字串
def Del_string(xstr):
    xstrc = xstr.strip().strip(u"市").strip(u"省")
    if xstrc=="":
        xstrc = np.nan
    return xstrc

這裡面有一個語法細節:strip()

接下來是對主表開始處理:

par_csv = dict(index_col = 0, encoding = "GB18030", parse_dates = ["ListingInfo"], na_values = [-1], 
               converters = dict(zip(*[["UserInfo_{}".format(i) for i in [9, 2, 4, 8, 20, 7, 19]], [Del_string]*7])))#六列地名,一列通訊

file_dat = ["{}/{}_dat_{}.csv".format(path, title, 1+x) for x in range(3)]
file_dav = ["{}/{}_dav.csv".format(path, title)]
dat = Read_concat_csv(file_dat, par_csv)
dav = Read_concat_csv(file_dav, par_csv)
#np.save("{}/{}_irt.npy".format(path, title), list(dat.index))
#np.save("{}/{}_irv.npy".format(path, title), list(dav.index))
da = pd.concat([dat, dav]) 

第二部分是處理Log 和 Update 表

def Read_History(file, icid, ictime, par_csv = {}):
    '''Organize Time-Dependent Historical Records

    Parameters
    ----------
    file: a list of file name
    icid: column name of id
    ictime: a list of 2 column names: [basetime, recordtime]
    par_csv: other parameters for pd.read_csv
    set_index(list) 把list 中的列都變成index 
    sort_index() 對 index 進行升序排列
    '''
    par = {"parse_dates": ictime}
    par.update(par_csv)
    dah = Read_concat_csv(file, par)
    dahb = (dah.assign(Id = dah[icid], Time = (dah[ictime[1]] - dah[ictime[0]]).astype('timedelta64[D]')).set_index(["Id", "Time"])
            .drop([icid]+ictime, axis = 1).sort_index())
    return(dahb)   

接下來對兩個表呼叫函式進行處理
““
dah1 = Read_History(file = [“{}/{}_dah{}_LogInfo.csv”.format(path, title, x) for x in [“t_1”, “t_2”, “t_3”, “v”]],
icid = ‘Idx’, ictime = [‘Listinginfo1’, ‘LogInfo3’])

dah2 = Read_History(file = [“{}/{}_dah{}_Userupdate.csv”.format(path, title, x) for x in [“t_1”, “t_2”, “t_3”, “v”]],
icid = ‘Idx’, ictime = [‘ListingInfo1’, ‘UserupdateInfo2’],
par_csv = {“converters”: {“UserupdateInfo1”: lambda x: x.lower()}})
“`