天天看點

銀行客戶逾期還款業務(邏輯回歸)業務描述:(P2P平台)預測貸款申請是否會違約,進而決定是否向申請人發放貸款 (39522,52)

業務描述:(P2P平台)預測貸款申請是否會違約,進而決定是否向申請人發放貸款 (39522,52)

邏輯回歸模型的一般步驟

一.資料的整理

1.根據業務對字段進行删除、清洗等預處理

2.建構資料集:Y的屬性—>【違約,不違約,不清楚】

用于模組化的資料集:違約和不違約【70%為訓練集】

用于測試的資料集:違約和不違約【30%為測試集】

3.解釋變量的篩選(X): statsmodels 向前逐漸法,以aic作為篩選的标準(越小越好)

二.建立模型
三.模型的檢驗

用測試集進行檢驗,畫ROC曲線,求AUC值

四.預測
銀行客戶逾期還款業務(邏輯回歸)業務描述:(P2P平台)預測貸款申請是否會違約,進而決定是否向申請人發放貸款 (39522,52)

資料源結構:資料集是Lending Club平台發生借貸的業務資料,共有52個變量,39522條記錄。

1.1 資料預處理

  • Step.1 去掉一些明顯沒用的特征,如’desc’,'url’,并将剩下特征儲存到一個新的csv檔案中
import warnings
warnings.filterwarnings('ignore') #忽視
import pandas as pd
loans_2020 = pd.read_csv("./LoanStats3a.csv", skiprows=1) #第一行是字元串,是以要skiprows=1跳過第一行
half_count = len(loans_2020) / 2 # 4萬行除以2 = 19767.5行
loans_2020 = loans_2020.dropna(thresh=half_count, axis=1)#2萬行中剔除空白值超過一半的列,thresh:剔除 ,還剩下54行
loans_2020 = loans_2020.drop(['desc', 'url'],axis=1) #按照列中,删除描述和URL連結,還剩52行
loans_2020.to_csv('loans_2020.csv', index=False) #追加到“loans_2020.csv”檔案 , index=False表示不加索引
#儲存在與代碼同一個檔案夾裡
loans_2020 = pd.read_csv("loans_2020.csv")
print("第一行的資料展示 \n",loans_2020.iloc[0])  #第一行的資料
loans_2020.shape #(39522,52)
           
  • Step.2 輸出資料标簽,初判斷無用特征
#id:使用者ID
#member_id:會員編号
#funded_amnt:承諾給該貸款的總金額
#funded_amnt_inv:投資者為該貸款承諾的總金額
#grade:貸款等級。貸款利率越高,則等級越高
#sub_grade:貸款子等級
#emp_title:工作名稱
#issue_d:貸款月份

loans_2020 = loans_2020.drop(["id", "member_id", "funded_amnt", "funded_amnt_inv", "grade", "sub_grade", "emp_title", "issue_d"], axis=1)
#zip_code:常用的郵編
#out_prncp和out_prncp_inv都是一樣的:總資金中剩餘的未償還本金
#out_prncp_inv:實際未償還的本金
#total_rec_prncp:迄今收到的本金

loans_2020 = loans_2020.drop(["zip_code", "out_prncp", "out_prncp_inv", "total_pymnt", "total_pymnt_inv", "total_rec_prncp"], axis=1)

#total_rec_int:迄今收到的利息
#recoveries:是否收回本金
#collection_recovery_fee:收集回收費用
#last_pymnt_d:最近一次收到還款的時間
#last_pymnt_amnt:全部的還款的時間


#保留候選特征
loans_2020 = loans_2020.drop(["total_rec_int", "total_rec_late_fee", "recoveries", "collection_recovery_fee", "last_pymnt_d", "last_pymnt_amnt"], axis=1)
           
  • Step.3 确定目前貸款狀态(label值)
  • 要做一個二分類,用0 1 表示
loans_2020 = loans_2020[(loans_2020['loan_status'] == "Fully Paid") |
                        (loans_2020['loan_status'] == "Charged Off")]#隻要這兩個屬性
bad_good = {'Fully Paid':1, 'Charged Off':0}
loans_2020['loan_status'] = loans_2020.loan_status.map(bad_good)
loans_2020['loan_status']

           
  • Step.4 去掉特征中隻有一種屬性的列
#在原始資料中的特征值或者屬性裡都是一樣的,對于分類模型的預測是沒有用的
#某列特征都是n n n  NaN  n n ,有缺失的,唯一的屬性就有2個,用pandas空值給去掉

orig_columns = loans_2020.columns  #展現出所有的列

drop_columns = []  #初始化空值

for col in orig_columns:
    #   dropna()先删除空值,再去重算唯一的屬性
    col_series = loans_2020[col].dropna().unique()  #去重唯一的屬性
    if len(col_series) == 1:  #如果該特征的屬性隻有一個屬性,就給過濾掉該特征
        drop_columns.append(col)

loans_2020 = loans_2020.drop(drop_columns, axis=1)
print(drop_columns)
print("--------------------------------------------")
print(loans_2020.shape)
loans_2020.to_csv('filtered_loans_2020.csv', index=False)#将資料儲存為filtered_loans_2020
#還剩下24個候選特征

           
  • Step.5 處理缺失值
loans = pd.read_csv('filtered_loans_2020.csv')
null_counts = loans.isnull().sum()  #用pandas的isnull統計一下每列的缺失值,給累加起來
print(null_counts) 

#對于每列中缺失的情況不是很大,大多數是很好的 ,那就删掉幾個列也無可厚非(對于樣本大),或者是隻删除缺失值,或者用均值、中位數和衆數補充
           
loans = loans.drop("pub_rec_bankruptcies", axis=1)
loans = loans.dropna(axis=0) 
#用dtypes類型統計有多少個是object、int、float類型的特征
print(loans.dtypes.value_counts())
loans.shape
           
  • Step.6 資料類型的轉換

    由于sk-learn庫不接受字元型的資料,是以還需将上面特征中12個字元型的資料進行處理。

#Pandas裡select_dtypes隻標明“ohbject”的類型str,隻標明字元型的資料

object_columns_df = loans.select_dtypes(include=["object"])
print(object_columns_df.iloc[0])

#term:分期多少個月啊
#int_rate:利息,10.65%,後面還要把%去掉
#emp_length:10年的映射成10,9年的映射成9
#home_ownership:房屋所有權,是租的、還是自己的、還是低壓出去了,那就用0 1 2來代替

           
#"purpose"和"title"表達的意思相近,且從輸出結果可以看出"title"所含的屬性較多,可以将其舍棄掉
print(loans["purpose"].value_counts())#purpose:你貸款時的目的是什麼,買房還是買車,還是其他消費

print("------------------------------------------------")

print(loans["title"].value_counts())#title:跟purpose一樣,貸款的目的,選一個就行了

           
#labelencoder
# jemp_length做成字典,emp_length當做key ,value裡還是字典 ,"10+ years": 10...
# 再在後面調用replace函數,剛才利息這列特征,是不是有%啊,再用astype()處理一下

mapping_dict = {
    "emp_length": {
        "10+ years": 10,
        "9 years": 9,
        "8 years": 8,
        "7 years": 7,
        "6 years": 6,
        "5 years": 5,
        "4 years": 4,
        "3 years": 3,
        "2 years": 2,
        "1 year": 1,
        "< 1 year": 0,
        "n/a": 0
    }
}

# 删除:last_credit_pull_d:LC撤回最近的月份   
#earliest_cr_line:第一次借貸時間
#addr_state:家庭郵編
#title:URL的标題
loans = loans.drop(
    ["last_credit_pull_d", "earliest_cr_line", "addr_state", "title"], axis=1)
#rstrip:删除 string 字元串末尾的指定字元
loans["int_rate"] = loans["int_rate"].str.rstrip("%").astype("float")
#revol_util:透支額度占信用比例
loans["revol_util"] = loans["revol_util"].str.rstrip("%").astype("float")
loans = loans.replace(mapping_dict)
loans.iloc[0]
# loans.shape #(38174, 19)

           
  • 剩餘的其他字元型特征,此處選擇使用pandas的get_dummies()函數,直接映射為數值型。
#檢視指定标簽的屬性,并記數
#home_ownership:房屋所有權
#verification_status:身份保持證明
#emp_length:客戶公司名稱
#purpose:貸款的意圖
#term:貸款分期的時間

cat_columns = ["home_ownership", "verification_status", "emp_length", "purpose", "term"]
dummy_df = pd.get_dummies(loans[cat_columns])
#concat() 方法用于連接配接兩個或多個數組,
dummy_df
loans = pd.concat([loans, dummy_df], axis=1)
loans = loans.drop(cat_columns, axis=1)
#pymnt_plan 訓示是否已為貸款實施付款計劃 ,裡面都為N,删掉這一列
loans = loans.drop("pymnt_plan", axis=1)
loans
loans.to_csv('cleaned_loans_2020.csv', index=False)
           
loans = pd.read_csv("cleaned_loans_2020.csv") # 清洗完的資料拿過來,現在的資料要麼是float類型和int類型
print(loans.info())
#獨熱編碼,使得一個特征的多個屬性變為了多個列

           

1.2 模型訓練

from sklearn.linear_model import LogisticRegression # 分類
lr = LogisticRegression() # 調用邏輯回歸的算法包



cols = loans.columns # 4萬行 * 24列的樣本
train_cols = cols.drop("loan_status") # 删除loan_status這一列作為目标值

features = loans[train_cols] # 23列的特征矩陣
target = loans["loan_status"] # 作為标簽矩陣

lr.fit(features, target) #開始訓練
predictions = lr.predict(features) # 開始預測
lr.predict_proba(features)#lr的機率模型
           

1.3 建立混淆矩陣

# 假正類(False Positive,FP):将負類預測為正類
fp_filter = (predictions == 1) & (loans["loan_status"] == 0)
fp = len(predictions[fp_filter])
print(fp)
print("----------------------------------------")


# 真正類(True Positive,TP):将正類預測為正類
tp_filter = (predictions == 1) & (loans["loan_status"] == 1)
tp = len(predictions[tp_filter])
print(tp)
print("----------------------------------------")


# 假負類(False Negative,FN):将正類預測為負類
fn_filter = (predictions == 0) & (loans["loan_status"] == 1)
fn = len(predictions[fn_filter])
print(fn)
print("----------------------------------------")

# 真負類(True Negative,TN):将負類預測為負類
tn_filter = (predictions == 0) & (loans["loan_status"] == 0)
tn = len(predictions[tn_filter])
print(tn)
           
銀行客戶逾期還款業務(邏輯回歸)業務描述:(P2P平台)預測貸款申請是否會違約,進而決定是否向申請人發放貸款 (39522,52)
# Rates:就可以用剛才的名額進行衡量了呀
tpr = tp / float((tp + fn))
fpr = fp / float((fp + tn))
print(tpr)#真正率
print(fpr)#假正率
"""
tpr:比較高,我們非常喜歡,給他貸款了,而且這些人能還錢了
fpr:比較高,這些人不會還錢,但還是貸給他了吧
為什麼這個2個值都那麼高呢?把所有人來了,都借給他錢呀,列印出前20行都為1,為什麼會出現這種情況?
絕對是前面的資料出現問題了,比如說資料是6:1,絕大多數是1,小部分是0,樣本不均衡的情況下,導緻分類器錯誤的認為把所有的樣本預測為1,因為負樣本少,咱們就“資料增強”,
把負樣本1增強到4份兒,是不是可以啊,要麼收集資料 ,資料已經定值了,沒辦法收集,要麼是造資料,你知道什麼樣的人會還錢嗎?也不好造吧,怎麼解決樣本不均衡的問題呢?
接下來要考慮權重的東西了,一部分是6份,另一部分是1份,把6份的權重設定為1,把1份的權重設定為6,設定權重項來進行衡量,把不均衡的樣本變得均衡,加了權重項,讓正樣本對結果的影響小一些,
讓負樣本對結果的影響大一些,通過加入權重項,模型對結果變得均衡一下,有一個參數很重要
"""
           
銀行客戶逾期還款業務(邏輯回歸)業務描述:(P2P平台)預測貸款申請是否會違約,進而決定是否向申請人發放貸款 (39522,52)
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_predict
"""
權重項可以自己定義的
0代表5倍的
1代表10倍的
"""
penalty = {
    0: 5,
    1: 1
}

lr = LogisticRegression(class_weight=penalty)
# kf = KFold(features.shape[0], random_state=1)
kf = 10
predictions = cross_val_predict(lr, features, target, cv=kf)
predictions = pd.Series(predictions)

# False positives.
fp_filter = (predictions == 1) & (loans["loan_status"] == 0)
fp = len(predictions[fp_filter])

# True positives.
tp_filter = (predictions == 1) & (loans["loan_status"] == 1)
tp = len(predictions[tp_filter])

# False negatives.
fn_filter = (predictions == 0) & (loans["loan_status"] == 1)
fn = len(predictions[fn_filter])

# True negatives
tn_filter = (predictions == 0) & (loans["loan_status"] == 0)
tn = len(predictions[tn_filter])

# Rates
tpr = tp / float((tp + fn))
fpr = fp / float((fp + tn))

print(tpr)
print(fpr)
           
銀行客戶逾期還款業務(邏輯回歸)業務描述:(P2P平台)預測貸款申請是否會違約,進而決定是否向申請人發放貸款 (39522,52)
  • 那麼為什麼會出現上面極其離譜的現象呢?這是由于我們的樣本是很不均衡的,這就容易導緻我們建構的分類器把所有樣本都歸為樣本量較大的那一個類。解決的方法有很多,其中一個是進行資料增強,就是把少的樣本增多,但是要添加的資料要麼是收集的,要麼是自己造的,是以這項工作還是挺難的。是以将考慮權重,将少的樣本的權重增大,期望模型能夠達到比較均衡的狀态。
  • 以上案例不是着重給出一個正确率的預測模型,隻是給出使用機器學習模組化的一般流程,分為兩大部分:資料處理和模型學習,第一部分需要大量的業務知識對原始資料進行清理及特征提取,第二部分模型學習,涉及長時間的模型參數調整,調整方向和政策需要大家進一步的研究。模型效果不理想時,可以考慮的調整政策:

    1.調節正負樣本的權重參數。

    2.更換模型算法。

    3.同時幾個使用模型進行預測,然後取去測的最終結果。

    4.使用原資料,生成新特征。

    5.調整模型參數

繼續閱讀