金融风控实战——迁移学习
遷移學習
為什么做遷移?
源域樣本和目標域樣本分布有區別,目標域樣本量又不夠。
場景
思考我們平時建模會使用到遷移學習的一些場景:
1)新開了某個消費分期的場景只有少量樣本,需要用其他場景的數據進行建模;
2)業務被迫停止3個月后項目重啟,大部分訓練樣本比較老舊,新的訓練樣本又不夠;
3)在某個新的國家開展了類似國內的業務,因為國情不同,顯然部分特征分布是不同的;
主要任務
縮小邊緣分布之間和條件分布下的差異。
幾個基本概念:
-
Domain(域):包括兩部分:feature space(特征空間)和probability(概率)。所以當domain不同的時候,分兩種情況。可能是feature space不同,也可能是feature space一樣但probability不同;
-
Task(任務):包括兩部分: label space(標記空間)和objective predictive function(目標預測函數)。同理,當task不同的時候,也分兩種情況。可能是label space不同,也可能是label space一樣但function不同;
-
Source(源)是用于訓練模型的域/任務;
-
Targe(任務)是要用前者的模型對自己的數據進行預測/分類/聚類等機器學習任務的域/任務。
通常我們說的遷移學習就是指將知識從源域遷移到目標域的過程。
比如我們有大量英短銀漸層的圖片,和少量美短起司的照片,想訓練一個判別當前的貓是不是美短起司的學習器。如果我們用英短銀漸層圖片來作為樣本,顯然訓練的模型是不能用來判別美短起司的,用美短起司的樣本來訓練,樣本量又太小。這時候我們可能會使用英短銀漸層來訓練一個卷積神經網絡,然后將這個網絡的中間結構取出來作為目標模型的前半部分,然后在少量的美短起司的樣本上再繼續學習后面的幾層網絡(再訓練、再學習)。熟悉卷積神經網絡的同學可能知道,CNN的前幾層主要學習的是輪廓和局部形狀等共性特征。這樣通過前面的學習,我們就知道了貓咪的共性,再通過對起司的學習得到細節上的差異。
然后在提遷移學習前,首先說另一種學習方法,叫做增量學習。
增量學習主要關注的是災難性遺忘(Catastrophic forgetting),平衡新知識與舊知識之間的關系。但我們可以用它來模擬神經網絡中的finetune.
Xgboost提供兩種增量訓練的方式:
- 一種是在當前迭代樹的基礎上增加新樹,原樹不變;(層遷移,兩只小貓咪的例子)
- 另一種是當前迭代樹結構不變,重新計算葉節點權重,同時也可增加新樹。 (fine—tuning)
增量學習有什么用呢?
我們可以仿照神經網絡中的基于模型的遷移學習,先用一部分樣本訓練前幾棵樹,然后用新的樣本學習后面的樹,通常我們可能用源域與目標域的混合數據訓練前幾棵樹,以得到更好的表達能力,最后用目標域的數據訓練后面幾棵樹。
import xgboost as xgb from sklearn.datasets import load_digits # 訓練數據xgb_params_01 = {}digits_2class = load_digits(2) X_2class = digits_2class['data'] y_2class = digits_2class['target']dtrain_2class = xgb.DMatrix(X_2class, label=y_2class)gbdt_03 = xgb.train(xgb_params_01, dtrain_2class, num_boost_round=3) # 訓練三棵樹的模型gbdt_03a = xgb.train(xgb_params_01, dtrain_2class, num_boost_round=7, xgb_model=gbdt_03) # 在原模型基礎上繼續訓練深度學習常見的遷移方法
- 模型再優化(fine—tuning,小量樣本調優,會造成過擬合,我們就會做一些遷移的手段,比如說保守訓練,先用原模型的參數作為基礎繼續再用新的數據集做訓練,這樣我們就能在這些參數上面加一些正則化,保證參數的改變不會太大)
- 層遷移(兩只小貓咪)
- 域對抗遷移(兩個domain,M1國內(大量的樣本)、M2國外(少量樣本),我們希望模型最大化label準確率,最小化domain分類準確率,我希望模型能區分好人還是壞人,而不能區分這個人屬于M1的域還是M2的域)
M1國內有label,M2國外無label。M1+M2=TM,TM打來自于M1還是M2的標簽
目前有突破的遷移學習算法基本上可以概括這幾類:
基于實例的遷移學習方法 (可以保留模型的解釋性,本門課的重點)
- 代表有Dai等人提出的基于實例的 TrAdaBoost 遷移學習算法。當目標域中的樣本被錯誤地分類之后,可以認為這個樣本是很難分類的,因此增大這個樣本的權重,在下一次的訓練中這個樣本所占的比重變大 。如果源域中的一個樣本被錯誤地分類了,可以認為這個樣本對于目標數據是不同的,因此降低這個樣本的權重,降低這個樣本在分類器中所占的比重。
基于特征的遷移學習方法
可以分為基于特征選擇的遷移學習方法和基于特征映射的遷移學習方法。
-
基于特征選擇的遷移學習方法是識別出源領域與目標領域中共有的特征表示,然后利用這些特征進行知識遷移。
-
基于特征映射的遷移學習方法是把各個領域的數據從原始高維特征空間映射到低維特征空間,在該低維空間下,源領域數據與目標領域數據擁有相同的分布。這樣就可以利用低維空間表示的有標簽的源領域樣本數據訓練分類器,對目標測試數據進行預測。
基于模型的遷移學習方法
- 由源域學習到的模型應用到目標域上,再根據目標域學習新的模型。該方法首先針對已有標記的數據,利用決策樹構建魯棒性的行為識別模型,然后針對無標定數據,利用K-Means聚類等方法尋找最優化的標定參數。比如 TRCNN等
我們平時用的最多的就是基于實例的遷移學習方法,今天主要圍繞樣本的選擇上做展開。
下面介紹幾個統計概念
聯合分布(joint distribution)
很多情況下,我們對于幾個變量同時的取值有關問題感興趣,例如我們需要知道事件“ lntellegence = high 且Grade= A”的概率。分析這樣的事件,則需要考慮兩個隨機變量的聯合分布(joint distribution)。下圖為聯合分布的一個例子。
from IPython.display import Image Image(filename='./image/1.png', width=600)
上圖表示了隨機變量 I,D,GI,D,GI,D,G的一個聯合分布,其中包含3個變量,分別是:III(學生智力,有0和1兩個取值)、DDD(試卷難度,有0和1兩個取值)、GGG(成績等級,有1、2、3三個取值)。故而這三個離散的隨機變量共有 2×2×3=122×2×3=122×2×3=12種聯合分布狀態。
上表中我們可以讀出系統取值為這 12 個聯合分布狀態中任一個的概率,例如:P(I=0,D=0,G=1)=0.126.P(I=0,D=0,G=1)=0.126.P(I=0,D=0,G=1)=0.126.
條件分布
當對于一組隨機變量,考慮其中某些變量取值特定值時,其余變量的分布是一種條件分布問題。
可以看到,條件分布率就是在邊緣分布率的基礎上都加上“另一個隨機變量取定某值”這個條件。
簡單來說,對于二緯離散隨機變量有:
P(X=xi∣Y=yj)=P(X=xi,Y=yj)P(Y=yj)P(X=x_i|Y=y_j)=\frac{P(X=x_i,Y=y_j)}{P(Y=y_j)}P(X=xi?∣Y=yj?)=P(Y=yj?)P(X=xi?,Y=yj?)?
為在 Y=yjY=y_jY=yj? 條件下 X 的條件分布率. (其中iii為固定的),也稱作該聯合分布在YYY上的條件分布。
回到 3.2 中例子來看,下圖中表是概率的聯合分布,表中隨便去掉所有包含某個值的行,就能對分布表進行縮減。
例如可以去掉所有 GGG 不為 1 的行,這樣就只剩下了 1、4、7、10 行,這樣他們的概率之和就不為 1 了,所以需要重新標準化(Renormalization),從而推得原聯合分布在 GGG 上的條件分布4。如圖為推導過程。
Image(filename='./image/2.png', width=600)
剔除無關取值(GGG 不為 1 的行)
標準化得到的值
即得到之前的聯合分布在變量 Grade(g)Grade(g)Grade(g)上的條件分布為上圖右邊的表格。
反之也可以把所有含有某個值得行相加,這就是接下來要講的邊緣化(Marginalization)。由此可得上圖中聯合分布在變量 D 上的邊緣分布如下圖右表。
Image(filename='./image/5.png', width=600)邊緣分布
一旦定義了隨機變量,我們就可以在能夠用 XXX 描述的事件上考慮分布。這個分布通常稱為隨機變量 XXX 的邊緣分布(marginal distribution) ,記為 P(X) . 這時單獨只考慮 XXX 的取值,與其它隨機變量取什么值的概率無關了。
例如,3.2 中聯合分布例子里,III 的邊緣分布為:
P(I=0)=0.126+0.168+0.126+0.009+0.045+0.126P(I=0)=0.126+0.168+0.126+0.009+0.045+0.126 P(I=0)=0.126+0.168+0.126+0.009+0.045+0.126
P(I=1)=0.252+0.0224+0.0056+0.06+0.036+0.024P(I=1)=0.252+0.0224+0.0056+0.06+0.036+0.024 P(I=1)=0.252+0.0224+0.0056+0.06+0.036+0.024
獨立同分布
我們建模的時候一直在強調獨立同分布,那么什么叫 獨立同分布?
獨立同分布即指變量均服從同一種分布,并且變量之間是相互獨立的(在多數情況下其實是不滿足的,但往往選擇忽略并不緊密的聯系)。例如隨機變量X1和X2,兩個變量獨立即指X1的出現并不影響X2,同理X2的出現并不影響X1,并且X1和X2所在的樣本集具有相同的分布形狀和分布參數。
對離散隨機變量具有相同的分布律,對連續隨機變量則有相同的概率密度函數,有著相同的分布函數,相同的期望和方差。
P(I=1)=0.252+0.0224+0.0056+0.06+0.036+0.024P(I=1)=0.252+0.0224+0.0056+0.06+0.036+0.024 P(I=1)=0.252+0.0224+0.0056+0.06+0.036+0.024
再回想一下我們的主要任務:
縮小邊緣分布之間和條件分布下的差異。
實現方法
剛剛說了我們的主要任務是 縮小邊緣分布和條件分布下的差異。
那么如何實現這兩個目標呢?
1)縮小訓練集與測試集的邊緣分布的距離,通常的做法是清洗訓練樣本,去除一些異常點或者減少他們的權重。這樣可以將訓練樣本的分布與測試樣本的分布保持一致。
2)如果想減少條件分布的差異呢?用決策樹舉例子,我們還需要在決策樹劃分的每一層的樣本中,重復上述過程,才可以保證條件概率分布也是相近的。
我們這里不得不介紹一下另一個Boosting的樹模型–AdaBoost
這是它的迭代過程。
Image(filename='./image/6.png', width=500)
如果不搞清楚他的原理,看Tradaboost代碼可能會一臉問號。
TrAdaboost
簡述
它是Adaboost學習方法發展而來,作者是Wenyuan Dai。TrAdaboost算法是用來解決訓練集和測試集分布不同的問題。在遷移學習的某些情況下,一個訓練集中會包含大量的輔助訓練樣本和少量的源訓練樣本,我們會將兩個不同分布的訓練集放在一起訓練,這種方法也稱為基于實例的遷移學習方法。
原理
1)Tradaboost是由Adaboost算法演變而來的,我們先來看Adaboost算法的基本思想:當一個訓練樣本被錯誤分類,算法就會認為這個樣本是難分類的,就相應地給此樣本增加樣本權重,下次訓練時這個樣本被分錯的概率就會降低。
2)類似地,在一個包含源訓練數據和輔助訓練數據的訓練集中,TrAdaboost算法也會對訓練樣本進行權重調整,對于源數據樣本,權重調整策略跟Adaboost差不多:如果一個源訓練樣本被錯誤分類,根據這一次源樣本訓練時的錯誤率進行調整,增加權重,降低下次訓練時的分類誤差;對于輔助訓練樣本:當它們被誤分類后,算法則認為它們是與目標數據很不同的,于是降低它們的權重,權重調整的依據是Hedge(b);
3)Tradaboost通過提升多個弱分類器,對后半(N/2~N)個弱分類器進行綜合投票,得出最后的決策。
Image(filename='./image/10.png', width=500)
TrAdaBoost 和 AdaBoost 主要區別在于:
1)TrAdaBoost 的輸入是 Ds (源域)和 Dt (目標域)對應的兩個數據集,并從 Ds 中只選取對學習任務 Tt 最有用的知識;
2)TrAdaBoost 在計算模型誤差時,僅考慮在 Dt 上的誤差;
3)TrAdaBoost 在 Ds 和 Dt 中使用不同的樣本調權方式;
4)TrAdaBoost 僅使用學習到的所有基學習器中,后訓練的半數基學習器來預測模型效果。
樣本的初始權重設置和基分類器選取比較關鍵。初始權重設置是較強的先驗信息,而且,如果初始權重設置不當,也會影響計算穩定性。我們可以通過不同領域的樣本比例,或根據不同類別樣本對應的比例,或綜合考慮前二者來設置初始權重。另外,基分類器的選取也會影響迭代輪數、計算穩定性和模型最終效果。
案例:跨國家跨場景遷移模型
某知名金融公司在印度新展開的小額現金貸產品,積累了少量Label樣本(約1200條),用于建模樣本量顯然不夠,考慮到雖然國家不同,但借款用戶的本質大體相似。考慮從國內大額產品的存量客戶上面做遷移,首先制作四個變量。制作變量時要保證國內與印度客群都有這個字段并且字段的含義能保證一致。所以當前的問題就變成了:將知識從國內樣本(源域)遷移至只有少量樣本的印度客群(目標域)。
import pandas as pd from sklearn.metrics import roc_auc_score,roc_curve,auc from sklearn.model_selection import train_test_split from sklearn import metrics from sklearn.linear_model import LogisticRegression from sklearn.svm import LinearSVC import numpy as np import random import math from sklearn.calibration import CalibratedClassifierCV data = pd.read_excel("/Users/zhucan/Desktop/tra_sample.xlsx") data.head() data.shape #(95806, 6) data.type.unique() #array(['target', 'origin', 'offtime'], dtype=object) #offtime國外的樣本的測試集feature_lst = ['zx_score','msg_cnt','phone_num_cnt','register_days'] train = data[data.type == 'target'].reset_index().copy() diff = data[data.type == 'origin'].reset_index().copy() val = data[data.type == 'offtime'].reset_index().copy()print(train.shape) print(diff.shape) val.shape #(14527, 7) #(65304, 7) #(15975, 7)#trans_S, trans_A, label_S, label_A, test train = train.loc[:1200]trans_S = train[feature_lst].copy() #目標域 label_S = train['bad_ind'].copy() trans_S.shape #(1201, 4) trans_A = diff[feature_lst].copy() #源域 label_A = diff['bad_ind'].copy() trans_A.shape #(65304, 4) val_x = val[feature_lst].copy() val_y = val['bad_ind'].copy() test = val_x.copy() test.shape #(15975, 4) lr_model = LogisticRegression(C=0.1,class_weight = 'balanced',solver = 'liblinear') lr_model.fit(trans_S,label_S)y_pred = lr_model.predict_proba(trans_S)[:,1] fpr_lr_train,tpr_lr_train,_ = roc_curve(label_S,y_pred) train_ks = abs(fpr_lr_train - tpr_lr_train).max() print('train_ks : ',train_ks)y_pred = lr_model.predict_proba(test)[:,1] fpr_lr,tpr_lr,_ = roc_curve(val_y,y_pred) val_ks = abs(fpr_lr - tpr_lr).max() print('val_ks : ',val_ks)from matplotlib import pyplot as plt plt.plot(fpr_lr_train,tpr_lr_train,label = 'train LR') plt.plot(fpr_lr,tpr_lr,label = 'evl LR') plt.plot([0,1],[0,1],'k--') plt.xlabel('False positive rate') plt.ylabel('True positive rate') plt.title('ROC Curve') plt.legend(loc = 'best') plt.show() #train_ks : 0.48500238435860754 #val_ks : 0.3887057754389137 trans_data = np.concatenate((trans_A, trans_S), axis=0) trans_label = np.concatenate((label_A, label_S), axis=0)lr_model = LogisticRegression(C=0.3,class_weight = 'balanced',solver = 'liblinear') lr_model.fit(trans_A,label_A)y_pred = lr_model.predict_proba(trans_data)[:,1] fpr_lr_train,tpr_lr_train,_ = roc_curve(trans_label,y_pred) train_ks = abs(fpr_lr_train - tpr_lr_train).max() print('train_ks : ',train_ks)y_pred = lr_model.predict_proba(test)[:,1] fpr_lr,tpr_lr,_ = roc_curve(val_y,y_pred) val_ks = abs(fpr_lr - tpr_lr).max() print('val_ks : ',val_ks)from matplotlib import pyplot as plt plt.plot(fpr_lr_train,tpr_lr_train,label = 'train LR') plt.plot(fpr_lr,tpr_lr,label = 'evl LR') plt.plot([0,1],[0,1],'k--') plt.xlabel('False positive rate') plt.ylabel('True positive rate') plt.title('ROC Curve') plt.legend(loc = 'best') plt.show() #train_ks : 0.4910909493184976 #val_ks : 0.33077621830414 import numpy as np from sklearn import tree#邏輯回歸的學習率、權重的大小,影響整體收斂的快慢 #初始權重很重要# H 測試樣本分類結果 # TrainS 目標域樣本 # TrainA 源域樣本 # LabelS 目標域標簽 # LabelA 源域標簽 # Test 測試樣本 # N 迭代次數#計算weight def calculate_P(weights, label):total = np.sum(weights)return np.asarray(weights / total, order='C')#用邏輯回歸作為基分類器,輸出概率 def train_classify(trans_data, trans_label, test_data, P):clf = LogisticRegression(C=0.3,class_weight = 'balanced',solver='liblinear')clf.fit(trans_data, trans_label, sample_weight=P[:, 0])return clf.predict_proba(test_data)[:,1],clf#計算在目標域上面的錯誤率 def calculate_error_rate(label_R, label_H, weight):total = np.sum(weight)return np.sum(weight[:, 0] / total * np.abs(label_R - label_H)) #預測-真實#根據邏輯回歸輸出的score的得到標簽,注意這里不能用predict直接輸出標簽 def put_label(score_H,thred):new_label_H = []for i in score_H:if i <= thred:new_label_H.append(0)else:new_label_H.append(1)return new_label_H#指定迭代次數,相當于集成模型中基模型的數量 N=500trans_data = np.concatenate((trans_A, trans_S), axis=0) trans_label = np.concatenate((label_A, label_S), axis=0)row_A = trans_A.shape[0] row_S = trans_S.shape[0] row_T = test.shape[0]test_data = np.concatenate((trans_data, test), axis=0)# 初始化權重 weights_A = np.ones([row_A, 1])/row_A weights_S = np.ones([row_S, 1])/row_S*2 weights = np.concatenate((weights_A, weights_S), axis=0)bata = 1 / (1 + np.sqrt(2 * np.log(row_A / N)))# 存儲每次迭代的標簽和bata值? bata_T = np.zeros([1, N]) # 存每一次迭代的 error_rate / (1 - error_rate) result_label = np.ones([row_A + row_S + row_T, N])predict = np.zeros([row_T])trans_data = np.asarray(trans_data, order='C') trans_label = np.asarray(trans_label, order='C') test_data = np.asarray(test_data, order='C')best_ks = -1 #最優KS best_round = -1 #最優基模型數量 best_model = -1 #最優模型# 初始化結束for i in range(N):P = calculate_P(weights, trans_label)result_label[:, i],model = train_classify(trans_data, trans_label,test_data, P)score_H = result_label[row_A:row_A + row_S, i]pctg = np.sum(data.bad_ind)/len(data.bad_ind)thred = pd.DataFrame(score_H).quantile(1-pctg)[0]label_H = put_label(score_H,thred)error_rate = calculate_error_rate(label_S, label_H,weights[row_A:row_A + row_S, :])if error_rate > 0.5:error_rate = 0.5if error_rate == 0:N = ibreak # 防止過擬合# error_rate = 0.001bata_T[0, i] = error_rate / (1 - error_rate)# 調整目標域樣本權重for j in range(row_S):weights[row_A + j] = weights[row_A + j] * np.power(bata_T[0, i],(-np.abs(result_label[row_A + j, i] - label_S[j])))# 調整源域樣本權重for j in range(row_A):weights[j] = weights[j] * np.power(bata, np.abs(result_label[j, i] - label_A[j]))y_pred = result_label[(row_A + row_S):,i]fpr_lr_train,tpr_lr_train,_ = roc_curve(val_y,y_pred)train_ks = abs(fpr_lr_train - tpr_lr_train).max()print('test_ks : ',train_ks,'當前第',i+1,'輪')if train_ks > best_ks :best_ks = train_ksbest_round = ibest_model = model #test_ks : 0.15578884442899515 當前第 1 輪 #test_ks : 0.14516536326608226 當前第 2 輪 #test_ks : 0.15162300261719303 當前第 3 輪 #test_ks : 0.1558032631518237 當前第 4 輪 # . . . #test_ks : 0.38762651455043984 當前第 498 輪 #test_ks : 0.38762651455043984 當前第 499 輪 #test_ks : 0.38762651455043984 當前第 500 輪y_pred = best_model.predict_proba(trans_S)[:,1] fpr_lr_train,tpr_lr_train,_ = roc_curve(label_S,y_pred) train_ks = abs(fpr_lr_train - tpr_lr_train).max() print('train_ks : ',train_ks)y_pred = best_model.predict_proba(test)[:,1] fpr_lr,tpr_lr,_ = roc_curve(val_y,y_pred) val_ks = abs(fpr_lr - tpr_lr).max() print('val_ks : ',val_ks)from matplotlib import pyplot as plt plt.plot(fpr_lr_train,tpr_lr_train,label = 'train LR') plt.plot(fpr_lr,tpr_lr,label = 'evl LR') plt.plot([0,1],[0,1],'k--') plt.xlabel('False positive rate') plt.ylabel('True positive rate') plt.title('ROC Curve') plt.legend(loc = 'best') plt.show() #train_ks : 0.4629947544110634 #val_ks : 0.39846160021324123總結
以上是生活随笔為你收集整理的金融风控实战——迁移学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lesson 16.4 卷积遇见深度学习
- 下一篇: 金融风控实战——有监督分箱