单调神经网络《 Monotonic Networks》及代码实现

在金融风控领域当中,模型可解释性相当重要。我对nips的论文《Monotonic Networks》当中的单调神经网络进行了复现,在权重为正的情况下,我们就称该神经网络为单调神经网络,因为不管如何进行输入,我们输出都会呈现出单调性,也就是输入越大,输出越大。或者输入越大,输出越小。传统的单调神经网络设计十分简单,而最简单的则是仅有一层全连接神经网络,且神经元的权重全部为正/负,这样就能够保证神经网络的单调性。同时,在论文《 Monotonic Networks》当中,提出了一种更为创新的办法,在神经网络能够保证所有的权重为正/负的情况下,同时能够实现模型更高的准确性。我们来看看他是怎么实现的。

1.论文概述

在这里展示了这样一个模型,我们将其称为单调网络。单调网络通过对超平面组进行最大和最小操作来实现分段线性曲面。通过约束超平面权重的符号来强制执行单调性约束。可以使用通常与其他模型(例如前馈神经网络)一起使用的基于梯度的优化方法来训练单调网络。 Armstrong (Armstrong et. al. 1996) 开发了一种称为自适应逻辑网络的模型,它能够强制执行单调性,并且似乎与这里介绍的方法有一些相似之处。然而,自适应逻辑网络只能通过商业软件包获得。训练算法是专有的,尚未在学术期刊上完全披露。因此,单调网络代表(据我们所知)在学术环境中呈现的第一个模型,该模型具有强制单调性的能力。

我们来看看这个神经网络的架构如何:

该单调网络具有前馈、三层(两个隐藏层)架构。第一层单元计算输入向量的不同线性组合。如果特定输入需要增加单调性,则连接到该输入的所有权重都被约束为正。类似地,连接到需要降低单调性的输入的权重被限制为负数。第一层单元被分成若干组(每组的单元数量不一定相同)。对应于每个组的是第二层单元,它计算组内所有第一层单元的最大值。最终输出单元计算所有组的最小值。该神经网络的设计图如下:

单调神经网络设计图

该神经网络第一层是我们的输入,第二层将我们输入放到一个全连接层当中,同时,分为3组(我们可以分为k组,看k的取值,怎样达到最好的模型识别准确率,你也可以尝试4,5,6......)。在这三组当中,我们分别找到每一组当中神经元输出最大的神经元,再将这些输出最大的3个神经元进行比较,将这3个神经元当中输出值最小的神经元作为我们的输出,最后通过sigmoid function,得到我们模型最好的输出。下面是该神经网络的数学表达式:

神经网络的第一层

在以上公式当中, [公式] 表示第k组神经元的输出。如果我们分为了3组神经元,则k=3.

其中 [公式] 表示第k组神经元当中第j个神经元的权重大小。

而后面的 [公式] 则代表了偏执项。

假设我们令 [公式] 为模型最后的输出,则有公式:

如果我们面对的是一个分类问题,则可以将最后的输入放进sigmoid function,公式如下:

以上就是这个神经网络的整体实现。

2.代码实现

代码实现这一块我使用了pytorch来做的实现,神经网络架构可以编写如下:

import torch 
from torch import nn
class MNET(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer_one = nn.Linear(32, 64) #将64个神经元,分为4组,每一组有16个神经元
        self.sigmoid = nn.Sigmoid()

    #forward这里就是神经网络的输入了
    def forward(self, input_x):
        length=len(input_x)
        input_x = self.layer_one(input_x)
    
        group_one=input_x[:,:,:16] #shape:[10,1,16],一共有16组神经元
        group_two=input_x[:,:,:32]
        group_three=input_x[:,:,:48]
        group_four=input_x[:,:,:64]
        
        #然后取每一个group当中,输出值最大的神经元

        max_one=self.find_max(group_one)
        max_two=self.find_max(group_two)
        max_three=self.find_max(group_three)
        max_four=self.find_max(group_four)
        
        #然后找到这4组数据的最小值.每一个数据,只选取4个数据当中的一个
        max_group=torch.cat((max_one,max_two),dim=1)#横着拼
        max_group=torch.cat((max_group,max_three),dim=1)
        max_group=torch.cat((max_group,max_four),dim=1)
        
        return self.find_mini(max_group)
        
        
    def find_max(self,group):
        #这个找最大,的确应该在每一个数据当中找最大,如果每次在喂进的所有数据当中找最大,还是不靠谱的
        max_ret=torch.max(group,dim=2)
        return max_ret.values
    
    def find_mini(self,group):
        mini_ret=torch.min(group,dim=1)
        return mini_ret.values.unsqueeze(-1)

由于在训练的过程当中,需要将所有的权重调整为正,因此需要如下代码调整权重的大小:

loss = criterion(out, var_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()

for p in net.parameters():
    p.data.clamp_(0, 99)

好啦,今天的分享就到这里啦!如果有收获的话,不要忘了点赞和收藏呀!

posted @ 2022-02-11 17:33  Geeksongs  阅读(1796)  评论(0编辑  收藏  举报

Coded by Geeksongs on Linux

All rights reserved, no one is allowed to pirate or use the document for other purposes.