契机

多任务学习一开始比较流行的模型是share-bottom模型,即不同的任务共享一份底层特征(如下图所示),这样做能够令不同任务之间共享信息,但如果任务之间相关度不高,不同塔相似度较低,这样做的效果就会大打折扣。MMOE模型能通过gates和experts的配合来解决上述问题。




ios mob推送配置p12 moe推送_算法


模型结构

MMOE结构是由MOE衍化而来的,因而下文会先讲解MOE,然后再具体解释MMOE的工作原理。

MOE

MOE的全称为Mixture of Experts,其底层由一个长度为N的gate向量ios mob推送配置p12 moe推送_ios mob推送配置p12_02和N个expert向量ios mob推送配置p12 moe推送_相似度_03组成,运行流程如下:

  1. 每个expert共享底层输入ios mob推送配置p12 moe推送_ios mob推送配置p12_04,其中第ios mob推送配置p12 moe推送_相似度_05个expert经过若干全连接层后得到对应的ios mob推送配置p12 moe推送_相似度_06
  2. ios mob推送配置p12 moe推送_相似度_06与gate向量的第ios mob推送配置p12 moe推送_相似度_05维向量相乘得到加权输出ios mob推送配置p12 moe推送_ios mob推送配置p12_09
  3. 将所有的加权输出相加得到最终的输出,ios mob推送配置p12 moe推送_ios mob推送配置p12_10
  4. ios mob推送配置p12 moe推送_深度学习_11输入到指定塔,经过若干全连接层后得到塔的最终输出。


ios mob推送配置p12 moe推送_ios mob推送配置p12_12


这里需要注意的是,某一个ios mob推送配置p12 moe推送_深度学习_13计算的结果只会输出到某一个塔上(例如towerA),且不同的ios mob推送配置p12 moe推送_深度学习_13会对应不同的ios mob推送配置p12 moe推送_ios mob推送配置p12_02ios mob推送配置p12 moe推送_相似度_16

MOE模型中,由gate来控制experts的输出,从而动态地决定哪些experts对某一个塔比较重要,这样即使不同塔的相似较低,experts也能够学习地较好,因为每个expert的学习是相互独立的,这一点很重要。

MMOE

MMOE的全称为Multi-gate Mixture-of-Experts,从名字上可以看出,MMOE是MOE的升级版。它的改进手段相对直观,具体来说,把原先单一gate向量变为M个gate向量,M为塔的个数,示意图如下所示。

ios mob推送配置p12 moe推送_深度学习_17

这里需要注意的是,上图中towerA和towerB所用到的experts是同一套experts。可以看出,相比于MOE,MMOE对gates和experts做了进一步的解耦,模型对于不同相似度塔的协同学习处理的更好,而且更重要的一点在于MMOE能够得到ios mob推送配置p12 moe推送_深度学习_13相对于每一个塔的得分,这是一个很大的进步。

模型代码

具体应用时,MMOE的自由度会比较大,最一开始gates和experts的输入都是全部底层特征,有时gates底层输入为item侧特征,experts底层输入为全量特征,这种组合下模型的指标会有进一步提升,不过这些都是视业务场景而定的,我们的代码实现如下所示。

# coding=utf-8
import tensorflow as tf


class MMOE(object):
    def __call__(self, expert_input, gate_input):
        self.target_num = 2
        self.expert_num = 4  # expert个数
        self.es = 32  # embedding_size

        # experts
        expert_list = []
        for i in range(self.expert_num):
            expert = expert_input
            for n in [128, 64]:
                expert = tf.layers.dense(expert, units=n,
                                         kernel_initializer=tf.truncated_normal_initializer(stddev=0.1),
                                         bias_initializer=tf.constant_initializer(0.1), activation=tf.nn.relu)
            expert = tf.layers.dense(expert, units=self.es,
                                     kernel_initializer=tf.truncated_normal_initializer(stddev=0.1),
                                     bias_initializer=tf.constant_initializer(0.1))
            expert_list.append(expert)
        experts = tf.reshape(tf.stack(expert_list, axis=1), [-1, self.es, self.expert_num])
        print("experts: %s" % experts)

        # gates
        gate_list = []
        for i in range(self.target_num):
            gate_net = gate_input
            for n in [128, 32]:
                gate_net = tf.layers.dense(gate_net, units=n,
                                           kernel_initializer=tf.truncated_normal_initializer(stddev=0.1),
                                           bias_initializer=tf.constant_initializer(0.1), activation=tf.nn.relu)
            gate_net = tf.nn.softmax(tf.layers.dense(gate_net, self.expert_num,
                                                     kernel_initializer=tf.truncated_normal_initializer(stddev=0.1),
                                                     bias_initializer=tf.constant_initializer(0.1)))
            gate_list.append(gate_net)
        gates = tf.reshape(tf.stack(gate_list, axis=1), [-1, self.expert_num, len(gate_list)])

        # merge
        merge = tf.reshape(tf.matmul(experts, gates), [-1, self.target_num, self.es])

        # towers
        towers = merge
        for n in [32, 16]:
            towers = tf.layers.dense(towers, units=n,
                                     kernel_initializer=tf.truncated_normal_initializer(stddev=0.1),
                                     bias_initializer=tf.constant_initializer(0.1), activation=tf.nn.relu)
        towers = tf.nn.softmax(tf.layers.dense(towers, units=1,
                                               kernel_initializer=tf.truncated_normal_initializer(stddev=0.1),
                                               bias_initializer=tf.constant_initializer(0.1)))

        return tf.squeeze(towers, axis=2)

参考

  1. mmoe paper
  2. moe paper