该论文是关于神经网络鲁棒性理论类的文章。类似有Sigmoid激活函数的神经网络,由于其非线性,使得在进行神经网络鲁棒验证评估时,不可避免地会引入了不精确性。
当前的一个研究方向是寻找更严格的近似值以获得更精确的鲁棒验证结果。然而,现有的紧密度定义是启发式的,缺乏理论基础。在该论文中,作者对现有的神经元紧密度表征进行了全面的实证分析,并揭示它们仅在特定的神经网络上具有优势。
另外,作者基于神经网络紧密度的概念重新提出了一个统一的神经网络紧密度定义,并表明计算神经网络紧密度是一个复杂的非凸优化问题。为了能够更好地理解该论文原理,文末给出了论文中一些原理示例的相关代码。
论文链接: https://arxiv.org/abs/2208.09872
神经网络是遵循逐层传播的,输入层上的每个神经元都接受一个输入值,该输入值乘以权重系数,然后传递给下一层的后续神经元。所有传入的数字相加。总和被馈送到激活函数
,并且
的输出与偏差
相加。然后将结果传播到下一层,直到到达输出层。
给定一个
层神经网络
,即
,其中
是一个第
层非线性可微激活函数。
可以是仿射变换或卷积操作中的其中一个:
其中
,
和
分别表示权重矩阵,偏置向量和卷积操作。在该论文中,作者主要关注的是
,
和
的激活函数如下所示:
神经网络
的输出是一个
维的值为
到
之间的向量,每一个维度其对应的是属于该类别的概率。令
是一个
类分类标签集合,
表示的是输入
的输出标签
上公式直观地发现,
返回了一个集合
中的标签
,
是个输出向量中最大值。
因为神经网络本质上是由计算机通过根据训练数据微调网络中的权重而组成的“程序”。与程序员开发的手工程序不同,神经网络缺乏形式化表示,几乎无法解释,这使得形式化和验证其属性非常具有挑战性。如果对神经网络的输入的合理扰动不会改变分类结果,则神经网络该扰动是鲁棒的。扰动通常通过扰动输入
和原始输入
之间的距离来测量,使用
范数来表示,其中
可以是
、
或
。一般的情况情况下,使用的度量范数是
。
神经网络的鲁棒性可以通过界
进行量化截断,
是一个安全的扰动距离,使得任何低于
的扰动都具有与神经网络的原始输入相同的分类结果。
定义1(局部鲁棒性): 给定一个神经网络,一个输入和一个在下的边界。关于是鲁棒的,当且仅当对每个成立,使得。这样的被称为认证下界。
验证
的鲁棒性有如下两个问题:
满足
,则有
其中对于每个
成立,其中
,且
返回概率
,即将
分类到类别
中;
很困难,大多数最先进的方法都采用高效的二分搜索算法。
由于包含非线性激活函数,所以神经网络
是高度非线性的。证明公式
的计算成本很高,即使对于最简单的全连接
网络也是NP-hard的。目前已经研究了许多方法来提高验证效率的,但同时也牺牲了完整性。具有代表性的方法包括区间分析、解释中的抽象和输出范围估计等。这些方法的基础技术是使用线性约束来过度逼近非线性激活函数,这可以比原始约束更有效地解决计算复杂度的问题。
基于近似的方法不是直接证明公式
,而是通过两个线性约束过度近似
和
,并证明
的线性下界
是大于
的线性上界
。显然,
是公式
的充分条件,证明或反证的效率明显更高。
定义2(上下线性界): 令是在区间的非线性函数。已知系数,则有
当以下条件成立时
和
分别是
的上下线性界。
使用线性上下界过度逼近非线性激活函数是神经网络逼近的关键。对于区间
上的每个激活函数
,作者定义了一个线性上界
和一个线性下界
以确保对于区间
中的所有
,使得
包含在
。给定定义 1中的输入范围,网络的输出范围是通过传播每个网络的输出区间来计算的定义2中的神经元到输出层。
如下图所示考虑一个简单基于近似验证神经网络的示例,最初的验证问题是证明对于任何输入
,其中
和
,它总是被分类为神经元标签
。这相当于证明辅助神经元的输出
总是大于
。作者定义线性上/下界
和
分别近似神经元
和
。
由上图可知,
的输出区间为
,从而证明网络对所有输入在
区间中是鲁棒的。
需要注意的是也可以考虑由一系列线段组成的分段线性边界来更紧密地逼近激活函数。然而,这种分段方式会导致约束的数量在逐层传播时呈指数级增长。这将大大降低验证的可扩展性。因此,使用一个线性上界和一个线性下界逼近激活函数是基于逼近的鲁棒性验证方法的最有效和广泛采用的选择。
在更严格的近似会产生更精确的验证结果的假设下,现有的紧密度表征是一种启发式的方法。但现有例子表明这个假设并不总是成立。这意味着定义每个单独神经元的紧密度对于实现紧密近似既不充分也不必要。那是因为神经元上的紧密度不能保证神经网络的输出间隔总是精确的。
但是,输出区间是判断网络是否鲁棒的基础。为了表征神经网络中激活函数的近似紧密度,作者引入了神经网络紧密度的概念,确保通过对激活函数的网络方式更紧密的逼近,使得神经网络产生更精确的输出间隔,从而产生更精确的验证结果。
定义3(神经网络紧密度): 给定神经网络,。令是的线性近似,且和分别表示的上下界。如果是神经网络的最紧密逼近,则对于任意不同的线性逼近有
其中
和
分别表示
和
的第
个元素。
定理1: 由定义3可知,如果比更紧密,则神经网络的近似比有更精确的鲁棒性。
证明: 令
有固定的扰动
。需要验证对于任意
有以下不等式成立
根据定义3可知,可得以下不等式
显然可知
是比
更紧密的近似。
给定一个
层神经网络
,使用
来表示在应用第
个激活函数之前
层的复合函数,即
第
层有
个神经元,
表示输出的第
个元素。对于每个激活函数
,其上和下线性界分别为
和
。然后可以将计算神经网络最紧密近似的问题形式化为以下优化问题:
其中
和
分别表示
的上下界,且
,
,
和
表示定义在权重
和偏置
的常数值,具体的定义如下所示:
需要注意的是,以上优化形式可能无法保证单个激活函数的近似值相对于现有的紧密度定义是最紧密的。
对于单层网络,优化问题可以进一步简化如下:
其中
,
,
表示的是隐层的数量。
优化问题是一个凸变体,因此可以通过利用基于梯度下降的搜索算法来求解该问题,以下算法流程图为计算单隐层近似相关算法的伪代码。对于隐层神经元上的每个激活函数,首先确定穿过两个端点的线
可以是上线性界还是下线性界。然后选择激活函数的切线作为上线性界或下线性界,其截断点可以是优化变量。最后用梯度下降法去优化目标函数,并进一步更新系数
,
,
和
。
对于具有两个或更多隐层的神经网络,由于其非凸性使得求解相关的优化问题而变得不切实际。对于任何隐藏层,激活函数的输入间隔都受到前一个隐藏层激活函数的近似值的约束。在该论文中,作者提出了可计算的神经元最紧密近似,并确定了当神经网络中的所有权重都为非负时,神经元最紧密近似导致网络最紧密。
如下表格所示为没有非负权重的Sigmoid模型的对比结果。在关于精度验证结果中,论文中提出的NEWISE计算可验证下界方法要比所有其它方法都要出色。尤其是针对于在Fashion Mnist数据集的训练出的模型,该方法甚至一度实现了验证结果高达 96.22%的精度。另外,NEWISE也相应对标准差的精度提高了129.33%。这也表明,跟其它方法相比,论文的方法可以对输入更加敏感。
如下表格所示为不同评估方法在具有混合权重的Sigmoid激活关于全连接神经网络和卷积神经网络对比结果。依然可以发现论文中提出的NEWISE计算可验证下界方法要比所有其它方法在提高评估精度和标准差方面都要出色。
以下两个程序分别画出了单个sigmoid函数上下线性界以及论文中实例2的示意图。
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(-10,10,10000)
Sigmoid_x = 1/(1 + np.exp(-x))
X_U3 = 0.104 * x + 0.670
X_L3 = 0.104 * x + 0.329
plt.plot(x, Sigmoid_x, label='Sigmoid(x)')
plt.plot(x, X_U3, label='X_U3')
plt.plot(x, X_L3, label='X_L3')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc='best')
# # plt.text(-0.5,3,r"$Loss=(w^8 - 1)^2$",fontsize=20,color="red")
plt.show()
以下两个程序分别画出了单个arctan函数上下线性界以及论文中关于arctan实例的示意图。
import numpy as np
import mpl_toolkits.axisartist as ast
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
data = np.linspace(-1, 1, 20)
x1, x2 = np.meshgrid(data, data)
x3_sigmoid = 1 / (1 + np.exp(-x1 - x2))
xu3 = 0.104 * (x1 + x2) + 0.670
xl3 = 0.104 * (x1 + x2) + 0.329
x4_sigmoid = 1 / (1 + np.exp(-x1 + x2))
xu4 = 0.104 * (x1 - x2) + 0.670
xl4 = 0.104 * (x1 - x2) + 0.329
x5 = 2 * x3_sigmoid + 2 * x4_sigmoid
xu5 = 2 * xu3 + 2 * xu4
xl5 = 2 * xl3 + 2 * xl4
x5 = 2 * x3_sigmoid + 2 * x4_sigmoid
x6 = 3 * x3_sigmoid - 5 * x4_sigmoid
xu6 = 3 * xu3 - 5 * xl4
xl6 = 3 * xl3 - 5 * xu4
x7 = xl5 - xu6
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x1, x2, x7)
# ax.plot_surface(x1, x2, xu6)
# ax.plot_surface(x1, x2, xl6)
# ax.plot_surface(x1, x2, xl5)
# ax.plot_surface(x1, x2, x5)
plt.show()
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(-3,-1,10000)
Arctan_x = np.arctan(x)
X_U3 = 0.232 * x - 0.554
X_L3 = 0.200 * x - 0.707
plt.plot(x, Arctan_x, label='Arctan(x)')
plt.plot(x, X_U3, label='X_U3')
plt.plot(x, X_L3, label='X_L3')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc='best')
# # plt.text(-0.5,3,r"$Loss=(w^8 - 1)^2$",fontsize=20,color="red")
plt.show()
import numpy as np
import mpl_toolkits.axisartist as ast
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
data1 = np.linspace(-2, -1, 20)
data2 = np.linspace(-1, 0, 20)
x1, x2 = np.meshgrid(data1, data2)
x3_arctan = np.arctan(x1 + x2)
xu3 = 0.232 * (x1 + x2) - 0.554
# xl3 = 0.200 * (x1 + x2) - 0.707
xl3 = 0.100 * (x1 + x2) - 0.949
x4_arctan = np.arctan(x1 - x2)
xu4 = 0.554 * (x1 - x2)
# xl4 = 0.500 * (x1 - x2) - 0.285
xl4 = 0.200 * (x1 - x2) - 0.707
x5 = x3_arctan + 2 * x4_arctan
xu5 = xu3 + 2 * xu4
xl5 = xl3 + 2 * xl4
x6 = - x3_arctan + 2 * x4_arctan
xu6 = - xl3 + 2 * xu4
xl6 = - xu3 + 2 * xl4
x7 = xl5 - xu6
print(x7.max())
print(x7.min())
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x8 = 0 *(x1 + x2)
ax.plot_surface(x1, x2, x7)
ax.plot_surface(x1, x2, x8)
# ax.plot_surface(x1, x2, xl6)
# ax.plot_surface(x1, x2, xl5)
# ax.plot_surface(x1, x2, x5)
plt.show()