Advertisement

深度学习细分领域_深度学习在金融领域的应用

阅读量:

关注上方“Datawhale ”,选择“星标”公众号

第一时间获取价值内容

aeb62c473eac65fb31cbc8230263458d.gif
b52b16a2ace712253c28189cbf489b5a.png

作者丨梅子行

来源丨风控算法工程师(ID:RC_algorithms)

当下人工智能技术的快速发展正在重塑各个行业。特别是在计算机视觉(CV)领域已基本被神经网络(NN)主导,并且自然语言处理(NLP)以及推荐系统等领域也已实现了大规模的应用。值得注意的是,在现有情况来看,在风控领域我们认为深度学习的应用还处于早期阶段。主要原因在于这类模型具有很强的黑箱特性,其解释性相对不足。然而,在尝试将线性回归(LR)与提升树算法(XGBoost)结合的过程中,这种组合方式的解释性已经显著下降。回顾当前机器学习的发展历程,则可观察到其经历了从传统统计方法逐步向神经网络架构演进的发展阶段。这表明深度学习将在未来广泛应用于多个行业领域。

那么当前深度学习在风控场景都有哪些应用呢?

我觉得基本可以总结为以下这三个子场景:

1)序列数据建模:代表算法 LSTM

2)图谱建模:代表算法 GCN

3)传统特征衍生:代表算法 CNN、XDeepFM

循环神经网络

从实际应用角度来看,在处理复杂动态变化的数据方面具备显著优势的一类模型是基于RNN的序列预测模型;这类模型的独特之处在于未采用传统的onehot编码或基于时间窗口的时间序列统计描述方法;相反地,在建模过程中采用了循环神经网络对时序数据进行深度学习拟合的方式;这种深度学习方法能够有效捕捉用户的动态行为演变规律。

71254f213e323d865c7b9c2eaf9aceae.png

代表场景主要是拥有时间顺序的序列数据:

  • B卡

  • 盗号检测

  • 失联模型

  • 文本分类

理论上来说用户在app上的点击数据都可以拿来使用。

双向LSTM的pytorch实现

复制代码
    from torch import nnclass BLSTM(nn.Module):"""        Implementation of BLSTM Concatenation for sentiment classification task"""def __init__(self, embeddings, input_dim, hidden_dim, num_layers, output_dim, max_len=40, dropout=0.5):        super(BLSTM, self).__init__()        self.emb = nn.Embedding(num_embeddings=embeddings.size(0),                                embedding_dim=embeddings.size(1),                                padding_idx=0)        self.emb.weight = nn.Parameter(embeddings)        self.input_dim = input_dim        self.hidden_dim = hidden_dim        self.output_dim = output_dim# sen encoder        self.sen_len = max_len        self.sen_rnn = nn.LSTM(input_size=input_dim,                               hidden_size=hidden_dim,                               num_layers=num_layers,                               dropout=dropout,                               batch_first=True,                               bidirectional=True)        self.output = nn.Linear(2 * self.hidden_dim, output_dim)def bi_fetch(self, rnn_outs, seq_lengths, batch_size, max_len):        rnn_outs = rnn_outs.view(batch_size, max_len, 2, -1)# (batch_size, max_len, 1, -1)        fw_out = torch.index_select(rnn_outs, 2, Variable(torch.LongTensor([0])).cuda())        fw_out = fw_out.view(batch_size * max_len, -1)        bw_out = torch.index_select(rnn_outs, 2, Variable(torch.LongTensor([1])).cuda())        bw_out = bw_out.view(batch_size * max_len, -1)        batch_range = Variable(torch.LongTensor(range(batch_size))).cuda() * max_len        batch_zeros = Variable(torch.zeros(batch_size).long()).cuda()        fw_index = batch_range + seq_lengths.view(batch_size) - 1        fw_out = torch.index_select(fw_out, 0, fw_index)  # (batch_size, hid)        bw_index = batch_range + batch_zeros        bw_out = torch.index_select(bw_out, 0, bw_index)        outs = torch.cat([fw_out, bw_out], dim=1)return outsdef forward(self, sen_batch, sen_lengths, sen_mask_matrix):"""        :param sen_batch: (batch, sen_length), tensor for sentence sequence        :param sen_lengths:        :param sen_mask_matrix:        :return: """''' Embedding Layer | Padding | Sequence_length 40'''        sen_batch = self.emb(sen_batch)        batch_size = len(sen_batch)''' Bi-LSTM Computation '''        sen_outs, _ = self.sen_rnn(sen_batch.view(batch_size, -1, self.input_dim))        sen_rnn = sen_outs.contiguous().view(batch_size, -1, 2 * self.hidden_dim)  # (batch, sen_len, 2*hid)''' Fetch the truly last hidden layer of both sides'''        sentence_batch = self.bi_fetch(sen_rnn, sen_lengths, batch_size, self.sen_len)  # (batch_size, 2*hid)        representation = sentence_batch        out = self.output(representation)        out_prob = F.softmax(out.view(batch_size, -1))return out_prob
卷积神经网络

在CNN结构中,卷积操作主要依赖于共享参数化的滤波器(kernel)。通过对中心像素及其周边像素进行加权求和以构建feature map的方式,则可有效地完成图像的空间特征提取。其中所采用的加权系数即为卷积核内部所定义的权重参数。

ab5284bab156b726dc12661d81b22241.png

代表场景主要是用于拥有拓扑关系的数据上:

通过将可求和的数据以feature-map的形式呈现后即可进行卷积操作;进而完成特征之间的交叉连接并提取更为深层的特征信息

深度学习的另一个非常重要的领域就是在我们的知识图谱中。

图卷积神经网络的前奏-Node2Vec

学习GCN之前,则必须先掌握这一基本概念被称为Node2Vec;在深入讨论Node2Vec的过程中,则通常会涉及另一个相关的概念Word2Vec

9dab1b9c0de1568b56fb9353a9e1a12f.png

Word2Vec的基本流程如下

305482a5083bbf0097bed5eb739b6165.png

那我们怎么在图中找到“临近”的节点?如何生成节点序列?

1)广度优先遍历(BFS)

从根节点出发,即下图中的0节点出发

首先访问它的子节点1,之后再访问它的子节点2。

当根节点0的所有子节点访问完了,再访问节点1的子节点,即节点3和节点4

当节点1的所有子节点访问完了,再访问节点2的所有子节点,即节点5和节点6

访问顺序为0-1-2-3-4-5-6

通过广度优先搜索能够揭示(unveil)的是网络中节点间的结构性等价关系(structural equivalence)。这种等价关系表征了网络中节点在位置和结构上的相似程度。

16cdf20e131bc17bf6347cfabb256039.png
2)深度优先遍历(DFS)

从根节点0出发

首先访问它的子节点1, 然后再访问节点1的子节点,节点3和节点4

再返回访问子节点2,然后再访问节点2的子节点,节点5和节点6

访问顺序为 0-1-3-4-2-5-6

深度优先遍历的结果表现为一种叫做homophily的特征:衡量两个节点之间相似性的依据是它们之间的距离。当两个节点的距离较近时,则其同质性将提升相应的比例。

bfd2c099b262f8124f07d596bf55dce3.png

那么,在某种生成节点序列的过程中是否有可能综合考虑homophily和structural equivalence?

Random Walk 随机游走

该理论Random Walk的概念由英国统计学家Karl Pearson于1905年首次提出,并以其开创性的研究奠定了现代概率论的基础。该理论 Random Walk 的核心特征在于其表现为一种无规律的运动模式,在运动过程中每个阶段的变化均遵循随机性特征。若我们考虑一个相对简单的网络拓扑结构示例,则可以更好地理解这一机制的应用场景。

40c988aa01cc3795f2b7d9b9ada9e6b8.png

在加权网络拓扑结构图中允许我们配置某节点转移至另一个节点的概率

随机行走在生成的节点序列中,在某种程度上既可以兼顾homophily的特点,又可以兼顾structural equivalence的优势。然而,在homophily和structural equivalence这两种相似性方面的分配比例难以得到有效控制。

node2vec

node2vec 在某种程度上提高了random walk的表现,在一定程度上更能体现同质性和结构相似性。举例而言,在图中选取t作为起始节点,并引入两个参数p和q。

Return probability parameter p (Return parameter p), corresponding to BFS, controls the probability of returning to the original node. As shown in the graph, after moving from t to v, there is a 1/p probability of returning to t at node v.
In-out probability parameter q (In outparameter q), corresponding to DFS, controls the probability of moving to other nodes.

d8e5647f423fb5cc6863735c64593cd2.png

假设现在已经从节点 t 走到节点 v,那么边的权重如所示::

5298b0d88bbce3fec581467b8713a13f.png

其中dtx表示节点t到节点x之间的最短路径

通过上面我们可以发现:

  • q 越大,p越小,结构相似性所占比重越高

  • p 越大,q越小,同质性所占比重越高

Node2Vec的pytorch实现
复制代码
    #preprocess_transition_probs(初始生成节点到节点的概率)def preprocess_transition_probs(self):'''    Preprocessing of transition probabilities for guiding the random walks.    '''####get_alias_edge这个函数是对每条边设定为二阶randomwalk的概率形式###这个函数的作用是生成每个边界的概率,同时会有alias_setup这个函数将概率进行转换,方便后面抽样    G = self.G    is_directed = self.is_directed    alias_nodes = {}for node in G.nodes():      unnormalized_probs = [G[node][nbr]['weight'] for nbr in sorted(G.neighbors(node))]#读取每个邻点权重      norm_const = sum(unnormalized_probs)###权重求和,作为公式中正则项常数的那个分母      normalized_probs =  [float(u_prob)/norm_const for u_prob in unnormalized_probs]###除以分母      alias_nodes[node] = alias_setup(normalized_probs)    alias_edges = {}    triads = {}if is_directed:for edge in G.edges():        alias_edges[edge] = self.get_alias_edge(edge[0], edge[1])else:for edge in G.edges():        alias_edges[edge] = self.get_alias_edge(edge[0], edge[1])        alias_edges[(edge[1], edge[0])] = self.get_alias_edge(edge[1], edge[0])    self.alias_nodes = alias_nodes    self.alias_edges = alias_edgesreturn#get_alias_edge是得到节点到节点的概率def get_alias_edge(self, src, dst):####二阶ramdom walk#src是随机游走序列中的上一个节点,dst是当前节点'''    Get the alias edge setup lists for a given edge.'''    G = self.G    p = self.p    q = self.q    unnormalized_probs = []for dst_nbr in sorted(G.neighbors(dst)):if dst_nbr == src:        unnormalized_probs.append(G[dst][dst_nbr]['weight']/p)elif G.has_edge(dst_nbr, src):        unnormalized_probs.append(G[dst][dst_nbr]['weight'])else:        unnormalized_probs.append(G[dst][dst_nbr]['weight']/q)    norm_const = sum(unnormalized_probs)    normalized_probs =  [float(u_prob)/norm_const for u_prob in unnormalized_probs]return alias_setup(normalized_probs)#alias_setup :输入概率,得到对应的两组数,方便后面的抽样调用def alias_setup(probs):'''    alias_setup的作用是根据二阶random walk输出的概率变成每个节点对应两个数,被后面的alias_draw函数所进行抽样  '''  K = len(probs)  q = np.zeros(K)  J = np.zeros(K, dtype=np.int)  smaller = []  larger = []for kk, prob in enumerate(probs):      q[kk] = K*probif q[kk] < 1.0:          smaller.append(kk)else:          larger.append(kk)##kk是下标,表示哪些下标小while len(smaller) > 0 and len(larger) > 0:      small = smaller.pop()##smaller自己也会减少最右边的值      large = larger.pop()      J[small] = large      q[large] = q[large] + q[small] - 1.0if q[large] < 1.0:          smaller.append(large)else:          larger.append(large)return J, q#alias_draw 抽样函数def alias_draw(J, q):'''  Draw sample from a non-uniform discrete distribution using alias sampling.  '''  K = len(J)  kk = int(np.floor(np.random.rand()*K))if np.random.rand() < q[kk]:return kkelse:return J[kk]#node2vec_walk就是对于给定的长度,对于开始节点开始模拟这个节点的路径,涉及的函数都在上面提及def node2vec_walk(self, walk_length, start_node):'''    Simulate a random walk starting from start node.    '''    G = self.G    alias_nodes = self.alias_nodes    alias_edges = self.alias_edges    walk = [start_node]######alias_draw这个函数是等于是根据二阶random walk概率选择下一个点while len(walk) < walk_length:      cur = walk[-1]      cur_nbrs = sorted(G.neighbors(cur))###G.neighbors(cur)得到cur一级关联的节点if len(cur_nbrs) > 0:if len(walk) == 1:####cur[0]          walk.append(cur_nbrs[alias_draw(alias_nodes[cur][0], alias_nodes[cur][1])])else:          prev = walk[-2]          next = cur_nbrs[alias_draw(alias_edges[(prev, cur)][0],             alias_edges[(prev, cur)][1])]          walk.append(next)else:breakreturn walk
图卷积神经网络(Graphs Convolutional Neural Networks )

在CNN处理的图像或视频数据中,图像或视频中的像素单元以非常有序的方式排列而成二维网格状结构。与此同时,在科学研究领域中还存在大量具有非欧几何特性的数据集存在其中例如社交网络和信息网络同样具有这种特征的结构模式

从本质上讲,这种网络结构(非欧几何学特征)对应于图论中抽象意义下的拓扑学模型.由此导出GCN.

afbf94d28f1297ad0d986cbaf127a082.png

为什么要研究GCN?

  • CNN难以处理具有非欧几何特性的数据,在遵循传统离散卷积架构(如问题1所述)时会导致平移不变性的丧失。直观地说,在拓扑图中各个节点周围的邻居数量可能存在差异因此自然不可能使用相同尺寸的卷积核来进行运算

考虑到现有深度神经网络难以有效处理具有非欧几何特性的数据

  • 从广义上讲,在赋范空间中各种数据都可以形成拓扑关系。
    因此,在谱聚类(Spectral Clustering)原理中总结道。
    这表明拓扑连接作为一种广泛的数据结构。
    其应用范围非常广泛。

代表场景是金融知识图谱:

  • 提取客户的关系特征

  • 构建反欺诈模型

因子分解机

对于预测性系统而言,在其中进行的任何尝试都必须建立在充分的理论分析基础之上

在现有风控模型中,实现交叉特征挖掘的过程通常依赖于人工特征提取技术,并且这种方法存在以下三个方面的不足:

关键属性都紧密关联于具体应用场景,在每种场景下都必须先投入大量时间和精力去深入分析数据模式之后才能成功地设计和提取高阶交叉特征;因此人力成本过高

通常情况下,原始数据中常常包含大量稀少的特征;交互特征所占据的空间维数范围等于原始各特征维数乘积的结果;由此可知,在实际应用中很容易导致维度灾难的问题。

  • 人工提取的交叉特征无法泛化到未曾在训练样本中出现过的模式中

因此自动学习特征间的交互关系是十分有意义的。

FM算法的pytorch实现:

复制代码
    class FM_model(nn.Module):def __init__(self, n, k):super(FM_model, self).__init__()self.n = n # len(items) + len(users)self.k = kself.linear = nn.Linear(self.n, 1, bias=True)self.v = nn.Parameter(torch.randn(self.k, self.n))def fm_layer(self, x):# x 属于 R^{batch*n}        linear_part = self.linear(x)# 矩阵相乘 (batch*p) * (p*k)        inter_part1 = torch.mm(x, self.v.t())  # out_size = (batch, k)# 矩阵相乘 (batch*p)^2 * (p*k)^2        inter_part2 = torch.mm(torch.pow(x, 2), torch.pow(self.v, 2).t()) # out_size = (batch, k)         output = linear_part + 0.5 * torch.sum(torch.pow(inter_part1, 2) - inter_part2) # 这里torch求和一定要用sumreturn output  # out_size = (batch, 1)def forward(self, x):        output = self.fm_layer(x)return output
DeepFM

左边deep network,右边FM,所以叫deepFM。图示就是这样:

7535226f6a08eb3edd78d09b0b39078d.png

其中deep部分并不怎么deep,才2个全连接层(FC)。

目前大多数相关研究工作均建立在因子分解机的基础之上,并通过多层全连接神经网络自动提取并学习特征间的复杂交互关系(如FNN、PNN和DeepFM等模型)。

该模型所提取的交互特征表现为隐式形式,并呈现出非显式且难以掌控的特点;然而其交互机制限定在元素级别(bit-wise)层面,并未达到因子分解机所期望的基于向量级(vector-wise)交互的目的。

xDeepFM算法

压缩交互网络(Compressed Interaction Network)

在CIN架构中,单个神经元所对应的接收域与特征维度D构成正交关系,并延伸至整个平面.相比之下,在CNN结构中,每个神经元的接收域局限于其周围的小范围区域.由此可见,在经过卷积操作后生成的特征图(Feature Map)呈现出向量形式而非矩阵形式.

通过将CIN与线性回归单元和全连接神经网络单元集成起来,并构建出完整的模型,并将其命名为极深因子分解机**xDeepFM.

代表场景和CNN差不多:

  • 对变量矩阵做特征交叉,挖掘更深层次的特征
1b5e865b760093fb5fc77290298ca7be.png

在前面的部分提到这些关键模块,这些模块是基于这几年处理过的所有深度学习相关的风控项目经过归纳总结得到的.旨在帮助那些对深度学习感兴趣并从事风控行业的从业者们更好地理解和应用相关技术.

b2d00ab1a6277aff6de7021a391b792a.png

全部评论 (0)

还没有任何评论哟~