迁移学习:DAN深度适配网络在风控冷启动中的应用

DAN原理简介

DAN(Deep Adaptation Netowrk)深度领域适配是迁移学习中基于特征变换迁移的一种实现,它期望通过深度神经网络为载体,训练一个网络对源域数据和目标域数据做变换使得变换后的两域分布差异(MMD)最小,同时变换后的特征能在源域上完成不错的分类效果,实现即能充分利用源域的数据和标签进行训练,也能不断弥合源域和目标领数据分布差异大的问题。直接看公式

基于特征变换的迁移学习一般范式

基于特征变换的迁移优化源域的分类器预测损失和特征变换后源域和目标域的差异损失,而DAN在此基础上将传统仅对最后一层变换的输出计算差异损失改为了最后6-8层,把MMD差异度量方式改为了MK-MMD

DAN网络结构


MMD和MK-MMD
MMD的基本定义

迁移学习中采用MMD距离度量源域和目标域的分布差异,即两样本被映射到可再生核希尔伯特空间后的均值之差,公式如下

MMD公式

其中f(x)表示对原始特征向量做一个函数变换,对于x,y两个分布数据经过同一个函数变换之后期望之差的最大值,如果x,y数据分布完全一致,mmd等于0。
sup表示上确界,sup下面f属于函数域F表示MMD是所有可能的变换函数f得到每一个期望之差的集合里面,期望之差最大的那个结果值。MMD的基本思想就是,如果两个随机变量的任意阶都相同的话,那么两个分布就是一致的。而当两个分布不相同的话,那么使得两个分布之间差距最大的那个矩应该被用来作为度量两个分布的标准
一个任意函数f由于样本采样问题也会把完全相同分布的两个数据x,y的MMD距离计算地不限大,因此函数域F限制为再生希尔伯特空间中单位球内的一个任意向量,进一步f(x)表示再生希尔伯特空间中的向量特征向量经过核函数映射到再生希尔伯特空间中的向量的点积。
MMD经过一系列的推导,最终计算方式如下

MMD的计算

tr表示矩阵的迹,是矩阵主对角线上元素之和,在式子中是核矩阵K和M矩阵相乘的结果的迹。


高斯核函数

核函数是用来计算映射到高维空间之后的内积的一种简便方法,MMD通常采用高斯核函数对原始数据映射到高维,高斯核函数的公式如下

高斯核函数

||x-x’||表示两个数据点的欧式距离,α表示带宽,带宽是一个超参数,它决定了高斯核函数的平滑程度。


MK-MMD多核MMD

核函数的选取直接决定了MMD距离的大小,因此引入MK-MMD(多核MMD距离),通过选取多个核函数计算MMD距离,然后将这些距离以某种方式线性组合在一起作为最终的MMD结果,相当于就是核函数这个环节,用一堆核函数组合成了最终的核函数,其他步骤不变


通过MMD代码了解MMD公式

网上找的MMD tensorflwo代码实现如下

import tensorflow as tf


def guassian_kernel(source, target, kernel_mul=2.0, kernel_num=5, fix_sigma=None):
    n_s = tf.shape(source)[0]
    n_t = tf.shape(target)[0]
    n_samples = n_s + n_t
    total = tf.concat([source, target], axis=0)
    total0 = tf.expand_dims(total, axis=0)
    total1 = tf.expand_dims(total, axis=1)
    # 高斯核函数的分子
    L2_distance = tf.reduce_sum(((total0 - total1) ** 2), axis=2)
    if fix_sigma:
        bandwidth = fix_sigma
    else:
        bandwidth = tf.reduce_sum(L2_distance) / tf.cast(n_samples ** 2 - n_samples, tf.float32)
    bandwidth /= kernel_mul ** (kernel_num // 2)
    # 高斯核函数的分母
    bandwidth_list = [bandwidth * (kernel_mul ** i) for i in range(kernel_num)]
    # 高斯核函数的数学表达式
    kernel_val = [tf.exp(-L2_distance / bandwidth_temp) for bandwidth_temp in bandwidth_list]
    return sum(kernel_val)


def MMD(source, target, kernel_mul=2.0, kernel_num=5, fix_sigma=None):
    kernels = guassian_kernel(source, target,
                              kernel_mul=kernel_mul, kernel_num=kernel_num, fix_sigma=fix_sigma)
    n_s = tf.shape(source)[0]
    n_t = tf.shape(target)[0]
    XX = tf.reduce_sum(kernels[:n_s, :n_s]) / tf.cast(n_s ** 2, tf.float32)
    YY = tf.reduce_sum(kernels[-n_t:, -n_t:]) / tf.cast(n_t ** 2, tf.float32)
    XY = tf.reduce_sum(kernels[:n_s, -n_t:]) / tf.cast(n_s * n_t, tf.float32)
    YX = tf.reduce_sum(kernels[-n_t:, :n_s]) / tf.cast(n_s * n_t, tf.float32)
    loss = XX + YY - XY - YX
    return loss

以上代码包含两个部分,guassian_kernel用来计算特征向量经过多个高斯核函数组合之后最终的高斯核函数核矩阵,MMD函数是最终矩阵迹的计算过程。
先看guassian_kernel

    n_s = tf.shape(source)[0]
    n_t = tf.shape(target)[0]
    n_samples = n_s + n_t
    total = tf.concat([source, target], axis=0)
    total0 = tf.expand_dims(total, axis=0)
    total1 = tf.expand_dims(total, axis=1)
    # 高斯核函数的分子
    # 每个特征向量和其他特征向量的欧式距离矩阵
    L2_distance = tf.reduce_sum(((total0 - total1) ** 2), axis=2)

以上内容计算高斯核函数的分母,计算每个样本特征向量和其他特征向量的欧式距离,形成一个欧式距离对称阵。

    if fix_sigma:
        bandwidth = fix_sigma
    else:
        # 初始化带宽
        bandwidth = tf.reduce_sum(L2_distance) / tf.cast(n_samples ** 2 - n_samples, tf.float32)
    bandwidth /= kernel_mul ** (kernel_num // 2)

以上内容在对高斯核函数的带宽做初始化,然后分别通过构造5个带宽来构造5个核函数

    # 生成5个带宽代表5个核函数
    bandwidth_list = [bandwidth * (kernel_mul ** i) for i in range(kernel_num)]
    # 高斯核函数的数学表达式
    kernel_val = [tf.exp(-L2_distance / bandwidth_temp) for bandwidth_temp in bandwidth_list]

最终以各个核函数的加和作为总的核函数

    return sum(kernel_val)

这个函数相当于得到了核矩阵,完成了这个公式

核矩阵

这个矩阵是一个方阵,shape=[源域样本数+目标域样本数, 源域样本数+目标域样本数]。
再看MMD函数,下面代码在进行矩阵相乘

    XX = tf.reduce_sum(kernels[:n_s, :n_s]) / tf.cast(n_s ** 2, tf.float32)
    YY = tf.reduce_sum(kernels[-n_t:, -n_t:]) / tf.cast(n_t ** 2, tf.float32)
    XY = tf.reduce_sum(kernels[:n_s, -n_t:]) / tf.cast(n_s * n_t, tf.float32)
    YX = tf.reduce_sum(kernels[-n_t:, :n_s]) / tf.cast(n_s * n_t, tf.float32)

相当于公式的这一步

K*M

对照M矩阵,两个矩阵相乘再求主对角线的和如下

M矩阵

主对角线左上:Ks,s / (ns * ns) - Ks,t / (ns * t)
主对角线右下:- Kt,s * (ns * nt) + Kt,t / (nt * nt) 

上面代码计算XX,YY,XY,YX就是在进行矩阵相乘,得到对角线的元素,最后直接加和就是矩阵的迹,得到MMD。


在风控场景代码实战
问题描述

设有以下样本

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

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


网络构建

采用tensorflow1的静态图构建一个DAN网络,source和target共享底部,最终损失以source的预测损失和target的mmd损失的组合作为最终优化目标,底部网络采用两层的全连接,其中最后一层的全连接将target和source变换后的状态向量进行mmd计算。

class DAN:
    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], 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"):
            output = tf.squeeze(tf.layers.dense(self.source_dense_2_out_act, 1, activation=None))
            self.prob = tf.nn.sigmoid(output, name="prob")

        with tf.name_scope("loss"):
            self.cls_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
                labels=self.source_input_y, logits=output)) + tf.losses.get_regularization_loss()
            self.mmd_loss = MMD(self.source_dense_2_out_act, self.target_dense_2_out_act)
            self.lambd = 2 / (1 + tf.exp(-10 * self.global_step / 1000)) - 1
            self.loss = self.cls_loss + self.mmd_loss * tf.cast(self.lambd, tf.float32)

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

target和source完全使用同一个底部网络进行前向传播,使用reuse进行网络层共享,在loss组合的时候使用给mmd loss赋予权重

self.lambd = 2 / (1 + tf.exp(-10 * self.global_step / 1000)) - 1


训练过程

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

    tf.reset_default_graph()
    model = DAN(feature_size=291, hidden_1_size=256, hidden_2_size=64, l2_regularizer_scale=0.00, learning_rate=0.005)
    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, mmd_loss_val, lambd = sess.run(
                [model.train_step, model.global_step, model.loss, model.cls_loss, model.mmd_loss, model.lambd],
                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:", mmd_loss_val, "lambd:", lambd)
                train_loss_list.append(loss_val)
                loss_mmd_list.append(mmd_loss_val)
                loss_cls_list.append(cls_loss_val)
                steps.append(step)

            if step % 3 == 0:
                val_prob = sess.run(model.prob, 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_loss_list.append(loss_val)
                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

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

loss收敛

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

验证集auc,ks


测试

测试代码如下

def predict_pb(input_x, pb_file_no=None):
    """从pb导入模型"""
    max_time = pb_file_no
    if max_time is None:
        max_time = max(os.listdir(os.path.join(BASIC_PATH, "./tfserving")))
    print("读取pb版本:", max_time)
    with tf.Session(graph=tf.Graph()) as sess:
        tf.saved_model.loader.load(sess, [tag_constants.SERVING], os.path.join(BASIC_PATH, "./tfserving", max_time))
        graph = tf.get_default_graph()
        input_self = graph.get_operation_by_name("source_input_x").outputs[0]
        probs = graph.get_tensor_by_name("output/prob:0")
        pred = sess.run(probs, feed_dict={input_self: input_x})

    return pred

分别查看是验证集和全量target样本上的auc和ks

    pred = predict_pb(target_test_x)
    pred_2 = predict_pb(target_train_x)

    auc = roc_auc_score(target_test_y, pred)
    fpr, tpr, threshold = roc_curve(target_test_y, pred)
    ks = abs(fpr - tpr).max()
    print("test: auc:{} ks:{}".format(auc, ks))

    auc = roc_auc_score(target_train_y + target_test_y, list(pred_2) + list(pred))
    fpr, tpr, _ = roc_curve(target_train_y + target_test_y, list(pred_2) + list(pred))
    ks = abs(fpr - tpr).max()
    print("total: auc:{} ks:{}".format(auc, ks))

输出如下

读取pb版本: 1682593104
test: auc:0.7811415362731151 ks:0.43520625889046943
total: auc:0.7961154500140641 ks:0.4780751657919877

和在source上使用树模型直接在target上预测的平均效果对比如下

DAN对比source直接tree

DAN在指标上都略好于只用source训练随机森林的结果。

零基础如何学习大模型 AI

领取方式在文末

为什么要学习大模型?

学习大模型课程的重要性在于它能够极大地促进个人在人工智能领域的专业发展。大模型技术,如自然语言处理和图像识别,正在推动着人工智能的新发展阶段。通过学习大模型课程,可以掌握设计和实现基于大模型的应用系统所需的基本原理和技术,从而提升自己在数据处理、分析和决策制定方面的能力。此外,大模型技术在多个行业中的应用日益增加,掌握这一技术将有助于提高就业竞争力,并为未来的创新创业提供坚实的基础。

大模型实际应用案例分享

①智能客服:某科技公司员工在学习了大模型课程后,成功开发了一套基于自然语言处理的大模型智能客服系统。该系统不仅提高了客户服务效率,还显著降低了人工成本。
②医疗影像分析:一位医学研究人员通过学习大模型课程,掌握了深度学习技术在医疗影像分析中的应用。他开发的算法能够准确识别肿瘤等病变,为医生提供了有力的诊断辅助。
③金融风险管理:一位金融分析师利用大模型课程中学到的知识,开发了一套信用评分模型。该模型帮助银行更准确地评估贷款申请者的信用风险,降低了不良贷款率。
④智能推荐系统:一位电商平台的工程师在学习大模型课程后,优化了平台的商品推荐算法。新算法提高了用户满意度和购买转化率,为公司带来了显著的增长。

这些案例表明,学习大模型课程不仅能够提升个人技能,还能为企业带来实际效益,推动行业创新发展。

学习资料领取

如果你对大模型感兴趣,可以看看我整合并且整理成了一份AI大模型资料包,需要的小伙伴文末免费领取哦,无偿分享!!!
vx扫描下方二维码即可
加上后会一个个给大家发

在这里插入图片描述

部分资料展示

一、 AI大模型学习路线图

整个学习分为7个阶段
在这里插入图片描述

二、AI大模型实战案例

涵盖AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,皆可用。
在这里插入图片描述

三、视频和书籍PDF合集

从入门到进阶这里都有,跟着老师学习事半功倍。
在这里插入图片描述

在这里插入图片描述

如果二维码失效,可以点击下方链接,一样的哦
【CSDN大礼包】最新AI大模型资源包,这里全都有!无偿分享!!!

😝朋友们如果有需要的话,可以V扫描下方二维码联系领取~
在这里插入图片描述

以下是使用TensorFlow实现DAN(Domain-Adversarial Neural Networks)的代码示例: 首先,我们需要导入相关的库和模块: ```python import tensorflow as tf import numpy as np ``` 接下来,我们定义一个数据集类,用于加载我们的数据: ```python class DAN_Dataset: def __init__(self, X, y, domain): self.X = X self.y = y self.domain = domain def __len__(self): return len(self.X) def __getitem__(self, index): return self.X[index], self.y[index], self.domain[index] ``` 然后,我们定义一个MLP作为我们的特征提取器: ```python class MLP: def __init__(self, input_dim, hidden_dim, output_dim): self.input_dim = input_dim self.hidden_dim = hidden_dim self.output_dim = output_dim self.W1 = tf.Variable(tf.random_normal([self.input_dim, self.hidden_dim])) self.b1 = tf.Variable(tf.zeros([self.hidden_dim])) self.W2 = tf.Variable(tf.random_normal([self.hidden_dim, self.output_dim])) self.b2 = tf.Variable(tf.zeros([self.output_dim])) def forward(self, x): hidden = tf.nn.relu(tf.matmul(x, self.W1) + self.b1) output = tf.matmul(hidden, self.W2) + self.b2 return output ``` 接下来,我们定义一个域分类器(domain classifier): ```python class Domain_Classifier: def __init__(self, input_dim, hidden_dim): self.input_dim = input_dim self.hidden_dim = hidden_dim self.W1 = tf.Variable(tf.random_normal([self.input_dim, self.hidden_dim])) self.b1 = tf.Variable(tf.zeros([self.hidden_dim])) self.W2 = tf.Variable(tf.random_normal([self.hidden_dim, 1])) self.b2 = tf.Variable(tf.zeros([1])) def forward(self, x): hidden = tf.nn.relu(tf.matmul(x, self.W1) + self.b1) output = tf.matmul(hidden, self.W2) + self.b2 return output ``` 接下来,我们定义一个DAN模型: ```python class DAN: def __init__(self, input_dim, hidden_dim, output_dim): self.input_dim = input_dim self.hidden_dim = hidden_dim self.output_dim = output_dim self.feature_extractor = MLP(self.input_dim, self.hidden_dim, self.output_dim) self.domain_classifier = Domain_Classifier(self.output_dim, self.hidden_dim) def forward(self, x, alpha): features = self.feature_extractor.forward(x) reverse_features = self.reverse_gradient(features, alpha) domain_output = self.domain_classifier.forward(reverse_features) return features, domain_output def reverse_gradient(self, X, alpha): gradient_reversed_tensor = ReverseGradient(alpha) return gradient_reversed_tensor(X) class ReverseGradient(tf.keras.layers.Layer): def __init__(self, alpha): super(ReverseGradient, self).__init__() self.alpha = alpha def call(self, inputs, **kwargs): return ReverseGradientFunc(self.alpha)(inputs) class ReverseGradientFunc(tf.keras.backend.Function): def __init__(self, alpha): super(ReverseGradientFunc, self).__init__() self.alpha = alpha def call(self, inputs, **kwargs): return inputs def gradient(self, gradients): return -self.alpha * gradients ``` 现在,我们可以使用上述定义的类和模型来训练我们的DAN模型
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值