深度学习:正则化-多任务学习

摘要:首先简单介绍多任务学习的方法,然后结合【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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章