摘要:首先簡單介紹多任務學習的方法,然後結合【1】給出權重自適應變化代價函數的原理與論文源碼進行實現。使用Keras框架,參考論文鏈接。
目錄
- 多任務學習簡介
- 不確定性加權的多任務學習
主要參考文獻
【1】“Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics”。
【2】“Deep learning”,花書。
1. 多任務學習簡介
多任務學習通過合併幾個任務重的樣例提高模型的泛化能力,因此可作爲一種正則化的手段。
多任務學習的模型通常包括:1. 具體任務獨立的參數;2. 所有任務共享的參數。訓練過程中兩部分參數同時更新。
底層參數通常爲共享參數,學習不同任務共有的底層表示。利用相同數據集的不同任務,底層表示可能存在某些統計關係,因此能緩解過擬合,提高泛化能力。
多任務學習常見的代價函數是不同任務的加權和
其中是每個任務的權重,通常是手工選擇或者用網格搜索的方式確定,屬於額外的超參數。
2. 不確定性加權的多任務學習
文章應用在場景理解的CityScapes數據集,用不確定性加權的方式實現自適應權重的多任務學習代價函數,結論爲**方法性能優於手工權重及單任務學習。 **
- 研究背景
多任務學習性能受權重影響較大,但手工加權的代價太大。如下圖所示:
- 研究問題
有效的多任務損失加權方式。
- 研究思路
在貝葉斯模型中,有兩類可以建模的不確定性,即認知不確定性(缺少訓練數據)、偶然不確定性(數據不能解釋信息),認知不確定性可以通過增加訓練數據緩解,偶然不確定性可以通過增加觀察所有可解釋變量的能力緩解。
偶然不確定性又可以分爲兩類,即數據依賴的異方差不確定性、任務依賴的同方差不確定性,異方差不確定性取決於模型輸入並作爲模型輸出被預測,同方差不確定性對所有輸入數據保持不變而在不同任務之間變化,因此可以描述爲任務依賴的不確定性。
基於最大化同方差不確定性的高斯似然,可以推導得到多任務代價函數。
具有同方差任務不確定性的多任務代價函數最終爲:
其中爲可學習參數,作爲每個任務的權重。最後一項作爲正則項防止權重無限制增大。
- 方法成果
在場景理解任務中,對語義分割,實例分割,深度迴歸三個任務進行多任務學習,並與單任務,兩個任務,之間進行對比,得到優於已有方法的結果,對比統計結果如下表所示:
同時在附錄中說明該方法對權重的初始化值不敏感。
- 創新點
提出一種能根據任務同方差不確定性自適應變化的多任務代價函數,並用統一的網絡結構實現三種不同的任務。
- 源碼鏈接
給出論文作者提供的源碼,使用Keras框架,在實驗中使用了相關的方法,可以直接調用類CustomMultiLossLayer
。
import sys
import numpy as np
np.random.seed(0)
from keras import backend as K
from keras.layers import Input, Dense, Lambda, Layer
from keras.models import Model
from keras.initializers import Constant
class CustomMultiLossLayer(Layer):
"""Multi-task losses. Paper: https://arxiv.org/pdf/1705.07115v3.pdf. """
def __init__(self, n_tasks=2, **kwargs):
self.n_tasks = n_tasks
self.is_placeholder = True
super(CustomMultiLossLayer, self).__init__(**kwargs)
def build(self, input_shape=None):
"""initialize log_var value for each task."""
self.log_vars = []
for i in range(self.n_tasks):
self.log_vars += [self.add_weight(name='log_var' + str(i), shape=(1,),
initializer=Constant(0.), trainable=True)]
super(CustomMultiLossLayer, self).build(input_shape)
def multi_loss(self, ys_true, ys_pred):
"""Def multi-task loss as shown in paper."""
assert len(ys_true) == self.n_tasks and len(ys_pred) == self.n_tasks
loss = 0
for y_true, y_pred, log_val in zip(ys_true, ys_pred, self.log_vars):
precision = K.exp(-log_val[0])
loss += K.sum(precision * (y_true - y_pred) ** 2. + log_val[0], -1)
return K.mean(loss)
def call(self, inputs):
ys_true = inputs[:self.n_tasks]
ys_pred = inputs[self.n_tasks:]
loss = self.multi_loss(ys_true, ys_pred)
self.add_loss(loss, inputs=inputs)
return K.concatenate(inputs, -1)
def get_trainable_model(prediction_model):
""" Trainable model
# Arguments
prediction_model: Model, standard multi-task model
# Returns
Model for training
"""
inp = Input(shape=(Q, ), name='inp')
y1_pred, y2_pred = prediction_model(inp)
y1_true = Input(shape=(1,), name='y1_true')
y2_true = Input(shape=(1,), name='y2_true')
out = CustomMultiLossLayer(n_tasks=2)([y1_true, y2_true, y1_pred, y2_pred])
return Model([inp, y1_true, y2_true], out)
def get_prediction_model():
inp = Input(shape=(Q,), name='inp')
x = Dense(nb_features, activation='relu', name='x')(inp)
y1_pred = Dense(D1, name='y1_pred')(x)
y2_pred = Dense(D2, name='y2_pred')(x)
return Model(inp, [y1_pred, y2_pred])
N = 100
nb_epoch = 2000
batch_size = 20
nb_features = 1024
Q = 1
D1 = 1 # first output
D2 = 1 # second outpu
prediction_model = get_prediction_model()
trainable_model = get_trainable_model(prediction_model)
trainable_model.compile(optimizer='adam', loss=None)
assert len(trainable_model.layers[-1].trainable_weights) == 2
assert len(trainable_model.losses) == 1
def gen_data(N):
X = np.random.randn(N, Q)
w1 = 2.
b1 = 8.
sigma1 = 1e1 # ground truth
Y1 = X.dot(w1) + b1 + sigma1 * np.random.randn(N, D1)
w2 = 3
b2 = 3.
sigma2 = 1e0 # ground truth
Y2 = X.dot(w2) + b2 + sigma2 * np.random.randn(N, D2)
return X, Y1, Y2
import pylab
%matplotlib inline
X, Y1, Y2 = gen_data(N)
pylab.figure(figsize=(3, 2))
pylab.scatter(X[:, 0], Y1[:, 0])
pylab.scatter(X[:, 0], Y2[:, 0])
pylab.show()
hist = trainable_model.fit([X, Y1, Y2], nb_epoch=nb_epoch, batch_size=batch_size, verbose=0)
# Found standard deviations (ground truth is 10 and 1):
[np.exp(K.get_value(log_var[0]))**0.5 for log_var in trainable_model.layers[-1].log_vars]