深度學習:正則化-多任務學習

摘要:首先簡單介紹多任務學習的方法,然後結合【1】給出權重自適應變化代價函數的原理與論文源碼進行實現。使用Keras框架,參考論文鏈接。

目錄

  1. 多任務學習簡介
  2. 不確定性加權的多任務學習

主要參考文獻

【1】“Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics”。

【2】“Deep learning”,花書。

1. 多任務學習簡介

多任務學習通過合併幾個任務重的樣例提高模型的泛化能力,因此可作爲一種正則化的手段。

多任務學習的模型通常包括:1. 具體任務獨立的參數;2. 所有任務共享的參數。訓練過程中兩部分參數同時更新。

底層參數通常爲共享參數,學習不同任務共有的底層表示。利用相同數據集的不同任務,底層表示可能存在某些統計關係,因此能緩解過擬合,提高泛化能力。

多任務學習常見的代價函數是不同任務的加權和
L(W)=I=1nαiLi(W) L({\bf{W}})=\sum_{I=1}^n\alpha_iL_i({\bf{W}})
其中αi\alpha_i是每個任務的權重,通常是手工選擇或者用網格搜索的方式確定,屬於額外的超參數。

2. 不確定性加權的多任務學習

文章應用在場景理解的CityScapes數據集,用不確定性加權的方式實現自適應權重的多任務學習代價函數,結論爲**方法性能優於手工權重及單任務學習。 **

  1. 研究背景

多任務學習性能受權重影響較大,但手工加權的代價太大。如下圖所示:在這裏插入圖片描述

  1. 研究問題

有效的多任務損失加權方式。

  1. 研究思路

在貝葉斯模型中,有兩類可以建模的不確定性,即認知不確定性(缺少訓練數據)、偶然不確定性(數據不能解釋信息),認知不確定性可以通過增加訓練數據緩解,偶然不確定性可以通過增加觀察所有可解釋變量的能力緩解。

偶然不確定性又可以分爲兩類,即數據依賴的異方差不確定性任務依賴的同方差不確定性,異方差不確定性取決於模型輸入並作爲模型輸出被預測,同方差不確定性對所有輸入數據保持不變而在不同任務之間變化,因此可以描述爲任務依賴的不確定性。

基於最大化同方差不確定性的高斯似然,可以推導得到多任務代價函數。

具有同方差任務不確定性的多任務代價函數最終爲:
L(W,σ1,σ2,,σn)=I=1n12σi2Li(W)+logσi2 L({\bf{W}},\sigma_1,\sigma_2,\dots,\sigma_n)=\sum_{I=1}^n\frac{1}{2\sigma_i^2}L_i({\bf{W}})+\log\sigma_i^2
其中σi\sigma_i爲可學習參數,作爲每個任務的權重。最後一項作爲正則項防止權重σ\sigma無限制增大。

  1. 方法成果

在場景理解任務中,對語義分割,實例分割,深度迴歸三個任務進行多任務學習,並與單任務,兩個任務,之間進行對比,得到優於已有方法的結果,對比統計結果如下表所示:
在這裏插入圖片描述

同時在附錄中說明該方法對權重的初始化值不敏感。

  1. 創新點

提出一種能根據任務同方差不確定性自適應變化的多任務代價函數,並用統一的網絡結構實現三種不同的任務。

  1. 源碼鏈接

給出論文作者提供的源碼,使用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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章