Dataset Shift in Machine Learning——第八章——KMM演算法
kmm的總體思路,既然源域和目標域的特徵分佈不一致,那麼就透過改變樣本權重的方式來使得源域的特徵分佈接近目標域的特徵分佈,比如源域的某個樣本和目標域的特徵分佈接近則權重增大,否則減少,那麼這個權重如何求解?
特徵偏移發生在很多地方,比如在一些變化很快的領域,過去三年的資料和當前的資料差異性很大,或者是某個領域國外的模型無法直接應用於國內相同領域市場的問題。
kmm的基本假設:邊緣機率分佈不同和條件機率分佈相同,具體的闡述在《遷移學習簡明手冊》裡寫的很詳細,簡單來說就是下游任務相同而特徵分佈存在差異性,也是比較符合結構化資料領域常遇到的建模過程中的問題。 邊緣機率分佈不同而條件機率分佈不同的現象被稱之為協變數偏移(covariate shift),它是領域自適應領域所研究和要解決的問題,而領域自適應又屬於遷移學習的熱門領域。
我們透過樣本加權的方式來處理結構化資料特徵發生偏移的問題是一個非常靈活高效,擴充套件性強的方式,因為主流的機器學習庫目前基本都支援sample weight的功能,即可以在模型訓練的過程中考慮樣本的權重,我們只需要提供自己估計出來的樣本權重即可。
首先,上述是建模目標的表示式,其中P指的是P(X,y),即聯合X和y的聯合機率分佈,
是模型對應的引數,這裡的
是一個形式化的表示,比如它可以表示邏輯迴歸的權重係數,也可以表示gbdt的樹結構和葉節點指,因此上式表示的是在聯合分佈P(X,y)下,我們要最小化模型的對映誤差——即從X對映到y的過程中產生的誤差,然後實際上我們只能觀察到從P(X
,y)中抽取到的部分樣本,畢竟樣本是有限的,因此,這裡就以均值的形式來作為上式期望的表達:
其中Z表示我們用於訓練的資料所尊崇的分佈,它不一定等於P,上式即為經驗風險最小化的表示式。
為了避免過擬合,我們引入結構風險最小化表示式,即在經驗風險最小化的基礎上引入對模型引數的約束,
現在我們假設訓練集的資料分佈為Ptr,測試集的分佈為Pte,損失函式的最小化是針對於Ptr的,但是我們的目的是在未來預測Pte的時候能夠儘量準確,即我們實際想要最小化損失的是未來的資料。
因此,上面的式子要轉換一下:
為什麼要引入這個比例式呢,其實直觀上也很好理解,我們可以將這個比例理解為相似度,如果test中的資料和train中的資料完全相同,則權重為1,訓練過程中該資料會被當作權重為1的正常樣本來訓練,如果二者存在差異,比差異越大則比例係數越低,則樣本權重對應的越低,透過這種“求同不存異”的方式,來將訓練集的分佈“同化”為測試集的分佈。 而這個聯合分佈我們可以轉換為條件機率分佈然後使用xgb、lgb等機器學習演算法來做非參估計。但是這種方式存在三個主要的缺點:
1、機率的估計要準確,否則計算出來的權重係數
誤差嚴重反而會影響模型的正常訓練;
2、為了計算權重係數
而去對訓練、測試集的資料進行機率估計是過火的,我們可以在不去做機率估計的情況下計算出權重係數
;(不覺得這是缺點。。。。)
3、權重係數如果估計的太多精確可能會引入方差,因為這會使得權重係數
“過擬合”於測試集,那麼下次如果還有別的測試集進行預測,可能精度還不如不做re-weight。
關於如何實現上述的過程可見,
https://
github。com/erlendd/cova
riate-shift-adaption
寫的非常詳細,擴充套件性好,但是效果不穩定的感覺,可能換個資料集就沒什麼用處了,在kesci的遷移學習練手賽裡試了一下,local cv提高一個點,線上無法測試(不知道為什麼提交之後沒反應了wtf。。。。)。
那麼kmm是如何實現的呢?
參考自上面的連結
首先我們定義一個:
變換 Φ,它使得X→F,即將原始特徵矩陣X對映到一個心的特徵空間F
,用μ:P→F表示期望運算元,也就是說我們把原始的損失函式最佳化的問題轉換到另一個特徵空間裡去了。
這裡u是一個線性運算元,將聯合分佈P對映為聯合分佈F,對映之後的資料集被稱為marginal polytope(邊緣立方體??)。而KMM的思路是找到一組權重
,使得帶權的訓練集和測試集的分佈一致,那麼如何來衡量兩個資料集的分佈是否一致呢?答案是:
這樣就好理解多了,kmm透過將訓練和測試集對映到某一個特徵空間(對映到怎樣的特徵空間取決於你使用的核函式),然後這個特徵空間可以使得二者的分佈的距離最小,以上的最佳化目標可以近似為:
這一步得變換是這樣得到得:
實際上中間做了一步小轉換,一開始還沒看明白,查閱了書上的證明才看懂。這樣就能和後面的程式碼實現對上了。
實際上,觀察上面的式子,此時問題已經轉化為一個帶約束項的二次規劃問題了,beta大於等於0並且beta之和為1。然後我們使用現成的求解二次規劃的問題的工具來求解即可。
現在我們來看看程式碼實現:
import
numpy
as
np
from
numpy
import
matrix
import
sklearn。metrics
from
cvxopt
import
matrix
,
solvers
import
matplotlib。pyplot
as
plt
import
pandas
as
pd
from
scipy。stats
import
spearmanr
#%% Kernel
def
kernel
(
ker
,
X1
,
X2
,
gamma
):
K
=
None
if
ker
==
‘linear’
:
if
X2
is
not
None
:
K
=
sklearn
。
metrics
。
pairwise
。
linear_kernel
(
np
。
asarray
(
X1
),
np
。
asarray
(
X2
))
else
:
K
=
sklearn
。
metrics
。
pairwise
。
linear_kernel
(
np
。
asarray
(
X1
))
elif
ker
==
‘rbf’
:
if
X2
is
not
None
:
K
=
sklearn
。
metrics
。
pairwise
。
rbf_kernel
(
np
。
asarray
(
X1
),
np
。
asarray
(
X2
),
gamma
)
else
:
K
=
sklearn
。
metrics
。
pairwise
。
rbf_kernel
(
np
。
asarray
(
X1
),
None
,
gamma
)
return
K
#%% Kernel Mean Matching (KMM)
class
KMM
:
def
__init__
(
self
,
kernel_type
=
‘linear’
,
gamma
=
1。0
,
B
=
1。0
,
eps
=
None
):
‘’‘
Initialization function
:param kernel_type: ’linear‘ | ’rbf‘
:param gamma: kernel bandwidth for rbf kernel
:param B: bound for beta
:param eps: bound for sigma_beta
’‘’
self
。
kernel_type
=
kernel_type
self
。
gamma
=
gamma
self
。
B
=
B
self
。
eps
=
eps
def
fit
(
self
,
Xs
,
Xt
):
‘’‘
Fit source and target using KMM (compute the coefficients)
:param Xs: ns * dim
:param Xt: nt * dim
:return: Coefficients (Pt / Ps) value vector (Beta in the paper)
’‘’
ns
=
Xs
。
shape
[
0
]
nt
=
Xt
。
shape
[
0
]
if
self
。
eps
==
None
:
self
。
eps
=
self
。
B
/
np
。
sqrt
(
ns
)
K
=
kernel
(
self
。
kernel_type
,
Xs
,
None
,
self
。
gamma
)
kappa
=
np
。
sum
(
kernel
(
self
。
kernel_type
,
Xs
,
Xt
,
self
。
gamma
)
*
float
(
ns
)
/
float
(
nt
),
axis
=
1
)
K
=
matrix
(
K
)
kappa
=
matrix
(
kappa
)
G
=
matrix
(
np
。
r_
[
np
。
ones
((
1
,
ns
)),
-
np
。
ones
((
1
,
ns
)),
np
。
eye
(
ns
),
-
np
。
eye
(
ns
)])
h
=
matrix
(
np
。
r_
[
ns
*
(
1
+
self
。
eps
),
ns
*
(
self
。
eps
-
1
),
self
。
B
*
np
。
ones
((
ns
,)),
np
。
zeros
((
ns
,))])
sol
=
solvers
。
qp
(
K
,
-
kappa
,
G
,
h
)
beta
=
np
。
array
(
sol
[
‘x’
])
return
beta
#下面是使用案例
# Reading the data
# X1 is in the source domain, and X2 is in the target domain
X1
=
pd
。
read_csv
(
“C:
\\
Users
\\
obazgir
\\
Dropbox
\\
TCGA-CCLE-TL
\\
CompareOtherTLAlgorithms
\\
MB_1_X1。csv”
)
X2
=
pd
。
read_csv
(
“C:
\\
Users
\\
obazgir
\\
Dropbox
\\
TCGA-CCLE-TL
\\
CompareOtherTLAlgorithms
\\
MB_1_X2。csv”
)
Y1
=
pd
。
read_csv
(
“C:
\\
Users
\\
obazgir
\\
Dropbox
\\
TCGA-CCLE-TL
\\
CompareOtherTLAlgorithms
\\
MB_1_Y1。csv”
)
Y2
=
pd
。
read_csv
(
“C:
\\
Users
\\
obazgir
\\
Dropbox
\\
TCGA-CCLE-TL
\\
CompareOtherTLAlgorithms
\\
MB_1_Y2。csv”
)
X1
=
X1
。
values
;
X2
=
X2
。
values
;
Y1
=
Y1
。
values
;
Y2
=
Y2
。
values
;
# X1 is the Tumor data , and X2 is the cell line data
#%% Kernel Mean Matching
kmm
=
KMM
(
kernel_type
=
‘rbf’
,
B
=
1
)
beta
=
kmm
。
fit
(
X1
,
X2
)
X1_hat
=
beta
*
X1
這裡補充一個程式碼實現上的細節:
我們之前提到過
問題的限制條件是上面的形式(最最佳化的式子是等價的,下面的式子做了矩陣的轉化而已,上下兩個式子僅僅約束條件不同)
目前github上的kmm實現基本是按照這個公式來的,引數B用於限制單個樣本的權重上限,是一個可以調整的超引數,eps是第二個約束條件右邊的:
實際上我們把第二個不等式約束兩倍除以m,就可以知道其含義是 源域的權重係數beta之均值-1的絕對值要小於等於eps,如果人工不設定eps的值,則預設為:
if self。eps == None:
self。eps = self。B / np。sqrt(ns)
這裡為了方便直觀理解,我們假設ns=10000,即源域有10000個樣本,B設為預設的1,則eps=1/10000=0。0001,實際上也就是說源域樣本的權重之和基本要等於10000,這也就意味著源域的一些樣本權重將大於1,而另一些將小於1。顯然比按照1的約束條件設定的要合理得多。
這裡用到cvxopt來求解二次規劃問題,關於其api和使用案例可見下:
solvers中的各個引數的含義可見:
K就是上式中對應的最最佳化項的第一項,kappa就是其對應的第二項,beta就是上式的x,s。t是beta大於等於0並且beta之和為1,這樣就和原始程式碼實現的對應起來了。
因此,總結,kmm假設存在某個希爾伯特空間(RHKS,關於希爾伯特空間的概念,可見
https://www。
cnblogs。com/keye/p/1074
8980。html
,不過我覺得簡單理解為在我們熟悉的歐幾里得空間之上的一個空間就行。。。是一個更加廣泛的概念),在這個空間中,加權之後的源域和目標域的距離最小,而對映到哪個空間,則取決於我們使用的核函式。這樣我們就把整個問題——1、對映——>2、透過權重係數beta使得對映之後兩個資料集的距離最小化,透過二次規劃的形式表達出來了:
這裡需要說的地方就是,我們在學習svm的時候也接觸到高斯核、線性核的概念,實際上我們使用高斯核,實際上就是選擇將原始資料透過高斯核對映到高維空間去:
但是在工程上,這一步並沒有發生,因為我們本來的目的是將原始的特徵矩陣對映到新的線性可分的維度,然後去求解最優切分平面的,但是實際上我們透過核函式可以在低維上實現——對映高維,計算最優切分平面的目的,具體的推導可見西瓜書或知乎上各路大佬的推導。因此,我們選擇什麼核函式,就是假設要將原始矩陣做什麼樣的變換,如果用線性核,就是線性變換了。
後面是關於各種理論證明誤差上限之類的,太過複雜,不翻了。。。
除此之外作者提出了使用10折交叉驗證來進行kmm的權重計算,切分成10份,每次分別計算9份和1份的權重結果,再加上最初全部資料的權重計算,一共21次kmm的權重計算。
但是原文貌似沒有提到怎麼用,盲猜就是簡單平均吧。。。。
先到這裡吧。。。後面的慢慢看