問題:如何做到對目標值的區間范圍的預測
使用神經網絡做回歸任務,我們使用MSE、MAE作為損失函數,最終得到的輸出y通常會被近似為y的期望值,例如有兩個樣本:(x=1, y=3)和(x=1, y=2),那只用這兩個樣本訓練模型,預測x=1時y的值就是2.5。
但有些情況下目標值y的空間可能會比較大,只預測一個期望值并不能幫助我們做進一步的決策。我們想知道x=1時,y的值最小會是多少,最大會是多少,使用MSE、MAE這些損失函數來構建預測輸出區間模型時候,往往需要對樣本進行非常復雜的處理才能達到目的,而且因為數據的預處理需要加入很強的先驗信息,建模效果肯定會打折扣,再一個如果數據規模比較大,那將會在數據預處理上浪費大量的時間。
這里介紹一個特殊的損失函數——分位數損失,利用分位數損失我們不需要對數據進行任何先驗的處理,就可以輕松做到預測輸出y的某一分位數水平值,例如5%分位數或95%分位數,利用這個輸出很自然就完成預測輸出范圍的回歸模型。
分位數損失函數的表達式如下圖:
其中γ是損失函數的參數,從實際意義上可以理解為是我們需要的分位數,這個損失函數從結構上看,就是以一定的概率γ懲罰預測值大于實際值,同時鼓勵預測值小于實際值,這樣的效果就是學得了目標y的γ分位數期望值。對于分位數損失函數的具體應用可以參考下邊的例子。
我們這里以波士頓房價數據集為例理解一下分位數損失函數的效果。
首先加載數據并進行分割,另外為了可視化方便,我們將x_test進行PCA降維并排序:
boston_data = load_boston()
x = boston_data['data']
y = boston_data['target']
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=77)
# PCA對數據進行降維處理,方便可視化
pca = PCA(n_components=1)
x_test_after_pca = pca.fit_transform(x_test, y_test)
# 對數據進行排序
x_sort_id = np.argsort(x_test, axis=None)
x_test = x_test[x_sort_id]
y_test = y_test[x_sort_id]
x_for_visual = x_test_after_pca[x_sort_id]
第二步就是損失函數的定義,在keras中沒有分位數函數的定義,可以根據公式來進行定義:
def quantile_loss(y_pred, y_true, r=0.5):
greater_mask = K.cast((y_true <= y_pred), 'float32')
smaller_mask = K.cast((y_true > y_pred), 'float32')
return K.sum((r-1)*K.abs(smaller_mask*(y_true-y_pred)), -1)+K.sum(r*K.abs(greater_mask*(y_true-y_pred)), -1)
還有一種更簡潔的定義方式可以參考這里:
def tilted_loss(q, y_true, y_pred):
e = (y_true-y_pred)
return K.mean(K.maximum(q*e, (q-1)*e), axis=-1)
構建一個最簡單的模型:
def gen_model(ipt_dim):
l1 = Input(shape=(ipt_dim,))
l2 = Dense(10, activation='relu', kernel_initializer=glorot_normal(), bias_initializer=zeros())(l1)
l3 = Dense(5, activation='relu', kernel_initializer=glorot_normal(), bias_initializer=zeros())(l2)
l4 = Dense(1, activation='relu', kernel_initializer=glorot_normal(), bias_initializer=zeros())(l3)
m_model = Model(inputs=l1, outputs=l4)
return m_model
調用模型,并且對結果進行可視化:
plt.figure()
plt.scatter(x_for_visual, y_test, label='actual')
q_list = [0.1, 0.5, 0.9]
for quantile in q_list:
model = gen_model(in_dims)
model.compile(loss=lambda y_t, y_p: tilted_loss(quantile, y_t, y_p), optimizer='adam')
model.fit(x_train, y_train, epochs=5, batch_size=8, verbose=1)
y_ = model.predict(x_test)
plt.plot(x_for_visual, y_, label=quantile)
plt.legend()
plt.show()
可視化的最終結果如下圖,我們可以看到學習到的三個檔位的分位數回歸模型。其中0.5的與普通MAE回歸結果是等價的,只不過0.5預測的是中位數而MAE預測的是期望值;0.1和0.9的兩條曲線可以作為預測結果的上下界,能夠包含其中80%的數據結果,如果我們追求更高的區間置信度,可以選擇更低的下界分位數和更高的上界分位數。
總結
- 在回歸任務中,我們可以輕松構建能夠預測輸出值范圍的模型,并且不依賴對數據的先驗處理,是一種非常高效的方法;
- 分位數損失函數的實現,推薦使用文中的第二種方法,涉及的計算步驟更少,效率更高;
- 分位數損失函數與MAE損失類似,是一種線性的損失函數,在loss在0附近的區間內同樣存在導數不連續的問題,如果能有簡單的方法可以規避這個缺點,則會更加好用。