当前位置:   article > 正文

迁移学习:子领域自适应DSAN和LMMD原理深析和tensorflow实战_dsan和dan优势在哪里

dsan和dan优势在哪里
DSAN简单介绍

DSAN(Deep Subdomain Adaptation Network),深度子领域自适应网络,相比传统的深度领域适配网络引入了子领域的概念,一个领域下可以依据一些条件,将相似的样本划分到一个子领域,比如使用类标签作为划分依据,同一类放到一个子领域,不再进行全局对齐,而是分别对相关的子领域进行对齐

子领域对齐

DSAN使用LMMD(Local MMD)在不同的层进行对齐来达到子领域对齐的效果,除此之外和传统的深度适配网络DDC,DAN没啥区别,主模型可以切换成适合自己业务的。

DSAN网络结构

从图上LMMD的指向来看一个LMMD由4个元素计算得到,分别是高层映射层的源域特征目标域特征源域的y目标域的预测y,优化目标如下,损失是分类器损失加上lmmd损失。

dsan优化目标

在原论文中dsan被用作无监督使用,即目标领域没有y值,论文采用分类器在目标域上x的预测值作为伪标签,从而进行分类计算lmmd,如果目标域有少量标签,实际也可以采用目标域的标签进行有监督dsan。dsan是一种条件分布子适应,即要的是每一个类别数据分布相似,而不是整体数据相似


LMMD原理解析

因此dsan的核心是搞懂LMMD的计算方式,公式如下

dsan公式1

这个公式简单而言就是计算源域和目标域在每个y值类别C下的映射到高维空间后的加权特征分布距离,首先最左边代表从第一类c=1开始到所有其他类别c,一一计算后边每个类的距离,最后处于总c得到最终平均距离。右边是源域经过映射后的分布减去目标域经过映射后哦分布的空间距离,注意相比于一般的MMD,这个式子中没有**期望E**,mmd是期望相减每个样本权重是1/m,1/n,而lmmd采用的是加权,每个样本有自己的权重,不论是源域权重W(sc)还是目标域W(tc),每个类C下每个样本w的总和都是1,为啥?看下面w的公式

样本权重w公式

这个W©是部分源域和目标域的,代表在这个类别c下,标签的onehot向量上第c的位置的值除以这个位置上全部值的和,本质上是对每个位置的值做归一化,举个例子

[[1, 0, 0],                [[1/1, 0/2, 0/0], 
 [0, 1, 0],    =>         [0/1, 1/2, 0/0],
 [0, 1, 0]]                 [0/1, 1/2, 0/0]]

  • 1
  • 2
  • 3
  • 4

在每一列上(每一个类别)w相加为1,在源域上,采用的是硬标签,onehot的值是离散的,如果该样本不是这个标签则这个位置是0,权重也是0,在目标领上,采用的是软标签,就是分类器预测的softmax稠密向量,在原论文中有说如果采用分类器预测的硬标签,如果预测错误对整体有负面影响,因此使用软标签减少这种负面影响。
说白了,针对每个类别,如果源域是这个类别,则将该样本纳入空间距离计算,若不属于这个类别,权重是0直接不纳入计算,对于目标域该样本的权重则直接看softmax的输出,目的就是每个类别之间计算空间距离(就是mk-mmd),通过权重挑出来源域和目标域中属于该类别的样本计算mk-mmd当在训练过程中的某一个batch时,可能出现source的y和target的y在类别集合上对不上的情况,此时只取两者的交集类别,即每个batch只计算样本类别都存在于source和target的样本权重,防止出现无减数或者被减数的情况。当目标域有y时,目标域也采用硬标签,此时双方数据都是硬标签,该套权重方案也是适用的。


tensorflow实战
问题描述

设有以下样本

群体A坏客户: 350 群体A好客户: 4800 
群体B坏客户: 1000 群体B好客户: 5500

  • 1
  • 2
  • 3

目标是对群体A进行坏客户预测,但是A的坏客户样本明显不足,而B样本的坏客户和A客户有非常大的相似,两者的本质是相似的,只不过是因为业务范围导致不能合并建模,因此识别B中的坏客户的知识可以迁移到A任务上,模型在保证对B有不错分类效果的前提下,还能弥合AB客群的特征分布差异是本次建模的初衷。
最终模型的训练输入是全量source的x和y,70%的target的x,验证使用30%的target的x和y,测试使用全部target的x和y,是一种半监督学习方式。


网络构建

采用tensorflow1的静态图构建一个DSAN网络,source和target共享底部,最终损失以source的预测损失和lmmd损失的组合作为最终优化目标,底部网络采用两层的全连接,其中最后一层的全连接将target和source变换后的状态向量以及source的标签和target的伪标签进行lmmd计算。
先看lmmd在tensorflow下的实现,LMMD只要实现了其他复用DAN等传统迁移网络即可。

def LMMD(n_class, source_x, source_y, target_x, target_y, kernel_mul=2.0, kernel_num=5, fix_sigma=None):
    """
    通过计算权重挑选出源域和目标域要进入空间距离计算的样本
    :param source_y: [[1,0],[0,1],...]
    :param target_y: [[0.2,0.7],[0.3,0.65]]
    :param batch_size: 64
    :param n_class: 2
    :return:
    """
    batch_size = tf.shape(source_y)[0]
    # 对source的y做每个类别位置的归一化
    source_y_sum = tf.reduce_sum(source_y, axis=0)
    source_y_normalize = source_y / source_y_sum
    source_y_normalize = tf.where(tf.is_nan(source_y_normalize), tf.zeros_like(source_y_normalize),
                                  source_y_normalize)
    # 对target的y做每个类别位置的归一化
    target_y_sum = tf.reduce_sum(target_y, axis=0)
    target_y_normalize = target_y / target_y_sum
    target_y_normalize = tf.where(tf.is_nan(target_y_normalize), tf.zeros_like(target_y_normalize),
                                  target_y_normalize)

    # 取都在source和target中的类别
    source_y_value = tf.expand_dims(tf.argmax(source_y, axis=1), 0)
    target_y_value = tf.expand_dims(tf.argmax(target_y, axis=1), 0)
    # [[0, 1]]
    index = tf.reshape(tf.sparse.to_dense(tf.sets.intersection(source_y_value, target_y_value)), [-1, 1])
    mask_arr = tf.zeros([batch_size, n_class])
    # 把对应的列改为1
    mask_arr = tf.transpose(
        tf.tensor_scatter_nd_update(tf.transpose(mask_arr), index, tf.ones([tf.shape(index)[0], batch_size])))
    target_y_normalize = target_y_normalize * mask_arr
    source_y_normalize = source_y_normalize * mask_arr

    # 展开公式
    weight_ss = tf.matmul(source_y_normalize, tf.transpose(source_y_normalize)) / n_class
    weight_tt = tf.matmul(target_y_normalize, tf.transpose(target_y_normalize)) / n_class
    weight_st = tf.matmul(source_y_normalize, tf.transpose(target_y_normalize)) / n_class
    # 核矩阵
    kernels = guassian_kernel(source_x, target_x,
                              kernel_mul=kernel_mul, kernel_num=kernel_num, fix_sigma=fix_sigma)
    SS = kernels[:batch_size, :batch_size]
    TT = kernels[batch_size:, batch_size:]
    ST = kernels[:batch_size, batch_size:]
    loss = tf.reduce_sum(weight_ss * SS + weight_tt * TT - 2 * weight_st * ST)

    return loss

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

LMMD代码先根据source_y和target_y求出样本权重,将各个类目下不相关,以及不同时在source_y和target_y中出现的类目过滤,其他和mmd一样,在最后进行loss计算的时候带上每个样本权重即可。
其他网络部分随意,这边只用了两层全连接作为共享层,代码如下

class DSAN:
    def __init__(self, feature_size, hidden_1_size, hidden_2_size, learning_rate=0.01, l2_regularizer_scale=0.01):
        self.source_input_x = tf.placeholder(tf.float32, [None, feature_size], name="source_input_x")
        self.target_input_x = tf.placeholder(tf.float32, [None, feature_size], name="target_input_x")
        self.source_input_y = tf.placeholder(tf.float32, [None, 2], name="source_input_y")
        self.global_step = tf.Variable(0, name="global_step", trainable=False)
        self.feature_size = feature_size
        self.hidden_1_size = hidden_1_size
        self.hidden_2_size = hidden_2_size
        self.l2_regularizer_scale = l2_regularizer_scale
        self.learning_rate = learning_rate

        with tf.name_scope("share_net"):
            # dense 1
            source_dense_1_out = tf.layers.dense(self.source_input_x, self.hidden_1_size,
                                                 kernel_regularizer=tf.contrib.layers.l2_regularizer(
                                                     self.l2_regularizer_scale), name="dense_1")
            target_dense_1_out = tf.layers.dense(self.target_input_x, self.hidden_1_size,
                                                 kernel_regularizer=tf.contrib.layers.l2_regularizer(
                                                     self.l2_regularizer_scale), name="dense_1", reuse=True)
            source_dense_1_out_act = tf.nn.sigmoid(source_dense_1_out)
            target_dense_1_out_act = tf.nn.sigmoid(target_dense_1_out)
            # dense 2
            source_dense_2_out = tf.layers.dense(source_dense_1_out_act, self.hidden_2_size,
                                                 kernel_regularizer=tf.contrib.layers.l2_regularizer(
                                                     self.l2_regularizer_scale), name="dense_2")
            target_dense_2_out = tf.layers.dense(target_dense_1_out_act, self.hidden_2_size,
                                                 kernel_regularizer=tf.contrib.layers.l2_regularizer(
                                                     self.l2_regularizer_scale), name="dense_2", reuse=True)
            self.source_dense_2_out_act = tf.nn.sigmoid(source_dense_2_out)
            self.target_dense_2_out_act = tf.nn.sigmoid(target_dense_2_out)

        with tf.name_scope("output"):
            source_output = tf.layers.dense(self.source_dense_2_out_act, 2, activation=None)
            target_output = tf.layers.dense(self.target_dense_2_out_act, 2, activation=None)
            self.source_probs = tf.nn.softmax(source_output, dim=1, name="source_probs")
            self.target_probs = tf.nn.softmax(target_output, dim=1, name="target_probs")

        with tf.name_scope("loss"):
            self.cls_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(
                logits=source_output, labels=self.source_input_y)) + tf.losses.get_regularization_loss()
            self.lmmd_loss = LMMD(n_class=2, source_x=self.source_dense_2_out_act, source_y=self.source_input_y,
                                  target_x=self.target_dense_2_out_act, target_y=self.target_probs)
            self.lambd = 2 / (1 + tf.exp(-10 * self.global_step / 200)) - 1
            self.loss = self.cls_loss + self.lmmd_loss * tf.cast(self.lambd, tf.float32) * 0.5

        optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate)
        self.train_step = optimizer.minimize(self.loss, global_step=self.global_step)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

训练过程

训练过程将全部source输入得到分类器损失,70% target输入作为计算lmmd的依据,验证集以30%的target的特征输入,输出预测的target的label和实际的label进行比对得到auc,ks指标

tf.reset_default_graph()
    model = DSAN(feature_size=291, hidden_1_size=256, hidden_2_size=64, l2_regularizer_scale=0.00, learning_rate=0.01)
    saver = tf.train.Saver(tf.global_variables(), max_to_keep=1)
    with tf.Session() as sess:
        init_op = tf.group(tf.global_variables_initializer())
        sess.run(init_op)
        source_batches = get_batch(100, 512, source_x, source_y)
        target_batches = get_batch(100, 512, target_train_x, target_test_y)
        # 验证
        val_feed_dict = {model.source_input_x: target_test_x}

        train_loss_list = []
        val_loss_list = []
        val_auc_list = []
        val_ks_list = []
        loss_cls_list = []
        loss_mmd_list = []
        steps = []

        for source_batch, target_batch in zip(source_batches, target_batches):
            epoch, source_batch_x, source_batch_y = source_batch
            _, target_batch_x, _ = target_batch
            feed_dict = {model.source_input_x: source_batch_x, model.source_input_y: source_batch_y,
                         model.target_input_x: target_batch_x}
            _, step, loss_val, cls_loss_val, lmmd_loss_val = sess.run(
                [model.train_step, model.global_step, model.loss, model.cls_loss, model.lmmd_loss],
                feed_dict=feed_dict)

            if step % 1 == 0:
                print("epoch:", epoch + 1, "step:", step, "loss:", round(loss_val, 4), "cls_loss:", cls_loss_val,
                      "mmd_loss_val:", lmmd_loss_val)
                train_loss_list.append(loss_val)
                loss_mmd_list.append(lmmd_loss_val)
                loss_cls_list.append(cls_loss_val)
                steps.append(step)

            if step % 3 == 0:
                val_prob = sess.run(model.source_probs, feed_dict=val_feed_dict)
                batch_metrics = get_metrics(val_prob, target_test_y)
                print("{:-^30}".format("evaluation"))
                print("[evaluation]", "target_test_auc:", round(batch_metrics["auc"], 4), "target_test_ks:",
                      round(batch_metrics["ks"], 4), "\n")

                diff_auc = (batch_metrics["auc"] - max(val_auc_list)) if len(val_auc_list) else 0
                val_auc_list.append(batch_metrics["auc"])
                val_ks_list.append(batch_metrics["ks"])
                print("本轮auc比之前最大auc{}:{}, 当前最大auc: {}".format("上升" if diff_auc > 0 else "下降", abs(diff_auc),
                                                               max(val_auc_list)))
                if diff_auc > 0:
                    saver.save(sess, os.path.join(BASIC_PATH, "./ckpt/ckpt"))
                    print("[save checkpoint]")
                print("-" * 40)
                if early_stop_auc(val_auc_list, windows=15):
                    print("{:-^30}".format("early stop!"))
                    break

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

训练日志如下

本轮auc比之前最大auc下降:0.06467132246979268, 当前最大auc: 0.8160107896389905
----------------------------------------
epoch: 6 step: 64 loss: 0.1399 cls_loss: 0.1373772 mmd_loss_val: 0.0054984335
epoch: 6 step: 65 loss: 0.3462 cls_loss: 0.17473626 mmd_loss_val: 0.37216622
epoch: 6 step: 66 loss: 0.2066 cls_loss: 0.18266876 mmd_loss_val: 0.051726535
----------evaluation----------
[evaluation] target_test_auc: 0.7547 target_test_ks: 0.4726 

本轮auc比之前最大auc下降:0.061323578317259875, 当前最大auc: 0.8160107896389905
----------------------------------------
---------early stop!----------

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

训练中记录总loss,mmd loss,分类器loss随着迭代步长的收敛图如下

loss收敛

在验证集上的auc和ks情况如下

验证集auc,ks


测试

在测试集和全量target样本的ks和auc结果如下

读取pb版本: 1683396035
test: auc:0.8160107896389905 ks:0.5165502715885157
total: auc:0.8067782353634174 ks:0.4930029933785909

  • 1
  • 2
  • 3
  • 4

综合对比随机森林,DAN,DSAN的效果如下,DSAN取得了最佳效果

模型对比


有监督DSAN

将target的真实y输入给dsan,而不是采用伪标签,修改网络代码如下

class SDSAN:
    def __init__(self, feature_size, hidden_1_size, hidden_2_size, learning_rate=0.01, l2_regularizer_scale=0.01):
        self.source_input_x = tf.placeholder(tf.float32, [None, feature_size], name="source_input_x")
        self.target_input_x = tf.placeholder(tf.float32, [None, feature_size], name="target_input_x")
        self.source_input_y = tf.placeholder(tf.float32, [None, 2], name="source_input_y")
        # 增加target的y输入
        self.target_input_y = tf.placeholder(tf.float32, [None, 2], name="target_input_y")
        self.global_step = tf.Variable(0, name="global_step", trainable=False)
        self.feature_size = feature_size
        self.hidden_1_size = hidden_1_size
        self.hidden_2_size = hidden_2_size
        self.l2_regularizer_scale = l2_regularizer_scale
        self.learning_rate = learning_rate

        with tf.name_scope("share_net"):
            # dense 1
            source_dense_1_out = tf.layers.dense(self.source_input_x, self.hidden_1_size,
                                                 kernel_regularizer=tf.contrib.layers.l2_regularizer(
                                                     self.l2_regularizer_scale), name="dense_1")
            target_dense_1_out = tf.layers.dense(self.target_input_x, self.hidden_1_size,
                                                 kernel_regularizer=tf.contrib.layers.l2_regularizer(
                                                     self.l2_regularizer_scale), name="dense_1", reuse=True)
            source_dense_1_out_act = tf.nn.sigmoid(source_dense_1_out)
            target_dense_1_out_act = tf.nn.sigmoid(target_dense_1_out)
            # dense 2
            source_dense_2_out = tf.layers.dense(source_dense_1_out_act, self.hidden_2_size,
                                                 kernel_regularizer=tf.contrib.layers.l2_regularizer(
                                                     self.l2_regularizer_scale), name="dense_2")
            target_dense_2_out = tf.layers.dense(target_dense_1_out_act, self.hidden_2_size,
                                                 kernel_regularizer=tf.contrib.layers.l2_regularizer(
                                                     self.l2_regularizer_scale), name="dense_2", reuse=True)
            self.source_dense_2_out_act = tf.nn.sigmoid(source_dense_2_out)
            self.target_dense_2_out_act = tf.nn.sigmoid(target_dense_2_out)

        with tf.name_scope("output"):
            source_output = tf.layers.dense(self.source_dense_2_out_act, 2, activation=None)
            target_output = tf.layers.dense(self.target_dense_2_out_act, 2, activation=None)
            self.source_probs = tf.nn.softmax(source_output, dim=1, name="source_probs")

        with tf.name_scope("loss"):
            self.cls_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(
                logits=source_output, labels=self.source_input_y)) + tf.losses.get_regularization_loss()
            self.lmmd_loss = LMMD(n_class=2, source_x=self.source_dense_2_out_act, source_y=self.source_input_y,
                                  target_x=self.target_dense_2_out_act, target_y=self.target_input_y)
            self.lambd = 2 / (1 + tf.exp(-10 * self.global_step / 200)) - 1
            self.loss = self.cls_loss + self.lmmd_loss * tf.cast(self.lambd, tf.float32) * 0.5

        optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate)
        self.train_step = optimizer.minimize(self.loss, global_step=self.global_step)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

很简单加一个输入即可,另外在lmmd计算的时候输入该target_y,在训练过程中将target_y引入。最终结果和无监督DSAN对比如下

最终模型对比

有监督DSAN取得了最佳结果。

最后的最后

感谢你们的阅读和喜欢,我收藏了很多技术干货,可以共享给喜欢我文章的朋友们,如果你肯花时间沉下心去学习,它们一定能帮到你。

因为这个行业不同于其他行业,知识体系实在是过于庞大,知识更新也非常快。作为一个普通人,无法全部学完,所以我们在提升技术的时候,首先需要明确一个目标,然后制定好完整的计划,同时找到好的学习方法,这样才能更快的提升自己。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

img

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

img

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

img

四、AI大模型商业化落地方案

img
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/762894
推荐阅读
相关标签
  

闽ICP备14008679号