【指南】微型神经网络如何实现基本功能

2024年09月14日 由 alex 发表 147 0

简介

本文展示了小型人工神经网络 (NN) 如何表示基本功能。其目的是提供有关 NN 工作原理的基本直觉,并作为对机制可解释性的温和介绍--机制可解释性是一个寻求对 NN 进行逆向工程的领域。


我将举出三个基本函数的例子,用简单的算法来描述每个函数,并展示如何将算法 “编码 ”到神经网络的权重中。然后,我将探讨神经网络能否利用反向传播学习算法。我鼓励读者把每个例子当作一个谜语,并在阅读解答之前花上一分钟。


三个基本函数

在以下所有示例中,我使用术语 “神经元 ”来表示神经网络计算图中的单个节点。每个神经元只能使用一次(没有循环;例如,不是 RNN),并按以下顺序执行 3 个操作:

  1. 与输入向量内积。
  2. 添加偏置项。
  3. 运行一个(非线性)激活函数。


2


我只提供极少量的代码片段,以便阅读流畅。


< 运算符

学习函数 “x < 10 ”需要多少个神经元?编写一个神经元网络,当输入小于 10 时返回 1,否则返回 0。


解决方案

首先,让我们按照我们想要学习的模式创建样本数据集


X = [[i] for i in range(-20, 40)]
Y = [1 if z[0] < 10 else 0 for z in X]


3


这项分类任务可以使用逻辑回归和 Sigmoid 作为输出激活来解决。b 是偏置项,可视为神经元的阈值。直观地说,我们可以设置 b = 10 和 a = -1 得到 F=Sigmoid(10-x)


让我们用 PyTorch 来实现并运行 F


model = nn.Sequential(nn.Linear(1,1), nn.Sigmoid())
d = model.state_dict()
d["0.weight"] = torch.tensor([[-1]]).float()
d['0.bias'] = torch.tensor([10]).float()
model.load_state_dict(d)
y_pred = model(x).detach().reshape(-1)


4


似乎是正确的模式,但我们能否做一个更严格的近似值?例如,F(9.5) = 0.62,我们希望它更接近 1。


对于 Sigmoid 函数,当输入接近 -∞ / ∞ 时,输出分别接近 0 / 1。因此,我们需要让我们的 10 - x 函数返回大数,这可以通过乘以一个更大的数来实现,比如 100,得到 F=Sigmoid(100(10-x)),现在我们将得到 F(9.5) =~1。


5


事实上,当训练一个只有一个神经元的网络时,它会收敛到 F=Sigmoid(M(10-x)),其中 M 是一个标量,它在训练过程中会不断增长,以使近似更紧密。


6


需要说明的是,我们的单神经元模型只是“<10 ”函数的近似值。我们永远无法达到零损失,因为神经元是一个连续函数,而“<10 ”不是一个连续函数。


最小值(a,b)

编写一个神经网络,获取两个数字并返回它们之间的最小值。


解决方案

和之前一样,我们先创建一个测试数据集,并将其可视化


X_2D = [
[random.randrange(-50, 50),
 random.randrange(-50, 50)]
 for i in range(1000)
]
Y = [min(a, b) for a, b in X_2D]


7


在这种情况下,ReLU 激活是一个很好的选择,因为它本质上是一个最大值函数(ReLU(x) = max(0,x))。事实上,使用 ReLU 可以将最小函数写成如下形式


min(a, b) = 0.5 (a + b -|a - b|) = 0.5 (a + b - ReLU(b - a) - ReLU(a - b))


[等式 1]

现在,让我们构建一个能够学习等式 1 的小型网络,并尝试使用梯度下降法对其进行训练


class MinModel(nn.Module):
  def __init__(self):
      super(MinModel, self).__init__()
      # For ReLU(a-b)
      self.fc1 = nn.Linear(2, 1)
      self.relu1 = nn.ReLU()
      # For ReLU(b-a)
      self.fc2 = nn.Linear(2, 1)
      self.relu2 = nn.ReLU()
      # Takes 4 inputs
      # [a, b, ReLU(a-b), ReLU(b-a)]
      self.output_layer = nn.Linear(4, 1)
  def forward(self, x):
      relu_output1 = self.relu1(self.fc1(x))
      relu_output2 = self.relu2(self.fc2(x))
      return self.output_layer(
          torch.cat(
             (x, Relu_output1, relu_output2),
             dim=-1
          )
      )


8


训练 300 个历元就足以收敛。让我们看看模型的参数


>> for k, v in model.state_dict().items():
>>   print(k, ": ", torch.round(v, decimals=2).numpy())
fc1.weight :  [[-0. -0.]]
fc1.bias :  [0.]
fc2.weight :  [[ 0.71 -0.71]]
fc2.bias :  [-0.]
output_layer.weight :  [[ 1.    0.    0.   -1.41]]
output_layer.bias :  [0.]


许多权重都归零了,剩下的是看起来很不错的


model([a,b]) = a - 1.41 * 0.71 ReLU(a-b) ≈ a - ReLU(a-b)


这不是我们预期的解法,但它是一个有效的解法,甚至比公式 1 更简洁!通过观察网络,我们发现了一个新的漂亮公式!证明:


证明:

  • 如果 a <= b:model([a,b]) = a - ReLU(a-b) = a - 0 = a
  • 如果 a > b:a - ReLU(a-b) = a - (a-b) = b


是偶数吗?

创建一个神经网络,将整数 x 作为输入,并返回 x mod 2。也就是说,如果 x 是偶数,则返回 0;如果 x 是奇数,则返回 1。


这个问题看起来很简单,但令人惊讶的是,要创建一个有限大小的网络,并能正确分类(-∞,∞)中的每个整数(使用标准的非周期性激活函数,如 ReLU),是不可能的。


定理:is_even 至少需要对数神经元

使用 ReLU 激活函数的网络至少需要 n 个神经元才能将 2^n 个连续自然数中的每个数正确地分类为偶数或奇数(即求解 is_even)。


证明: 使用归纳法

基础:n == 2:直观地说,单个神经元(ReLU(ax + b)形式)无法求解 S = [i + 1, i + 2, i + 3, i + 4],因为它不是线性可分的。例如,在不失一般性的前提下,假设 a > 0 且 i + 2 为偶数。如果 ReLU(a(i + 2) + b) = 0,那么 ReLU(a(i + 1) + b) = 0(单调函数),但 i + 1 是奇数。


假设为 n,再看 n+1: 让 S = [i + 1, ..., i + 2^(n+1)],为了避免矛盾,假设 S 可以用 n 大小的网络求解。取第一层的一个输入神经元 f(x) = ReLU(ax+b),其中 x 是网络的输入。根据 ReLU 的定义,存在一个这样的 j:

S' = [i + 1, ..., i + j], S'' = [i + j + 1, ..., i + 2^(n + 1)] S'' = [i + j + 1, ..., i + 2^(n + 1)].

f(x ≤ i) = 0

f(x ≥ i) = ax + b


有两种情况需要考虑:

  • 情况 |S'| ≥ 2^n:放弃 f 及其所有边不会改变网络在 S' 上的分类结果。因此,有一个大小为 n-1 的网络可以解决 S'。
  • 情况 |S''|≥ 2^n: 对于每个以 f 为输入的神经元 g,g(x) = ReLU(cf(x) + d + ...)=ReLU(c ReLU(ax + b) + d + ...),去掉神经元 f,将 x 直接连接到 g,得到 ReLU(cax + cb + d + ...)。大小为 n - 1 的网络可以求解 S''。


对数算法

多少个神经元足以对 [1, 2^n] 进行分类?我已经证明了 n 个神经元是必要的。接下来,我将证明 n 个神经元也足够了。


一个简单的实现方法是一个网络不断加减 2,并检查是否在某一点上达到 0。更有效的算法是加减 2 的幂次,这只需要 O(n) 个神经元。更正式的算法是:

f_i(x) := |x - i|

f(x) := f_1∘ f_1∘ f_2 ∘ f_4∘ ... ∘ f_(2^(n-1)) (|x|)


证明:

  • 根据定义:∀ x ϵ[0, 2^i]:f_(2^(i-1)) (x) ≤ 2^(i-1)。即,将区间缩短一半。
  • 递推 f_1∘ f_1∘ f_2 ∘ ... ∘ f_(2^(n-1)) (|x|) ≤ 1
  • 对于每个偶数 i: is_even(f_i(x)) = is_even(x)
  • 类似地 is_even(f_1( f_1(x))) = is_even(x)
  • 我们得到 f(x) ϵ {0,1} 和 is_even(x) = is_even(f(x))。QED.


算法实现

让我们尝试在一个小范围内使用神经网络来实现这一算法。我们从定义数据开始。


X = [[i] for i in range(0, 16)]
Y = [z[0] % 2 for z in X]


9


由于域包含 2⁴ 个整数,我们需要使用 6 个神经元。5 个神经元用于 f_1∘ f_1∘ f_2 ∘ f_4∘ f_8, + 1 个输出神经元。让我们构建网络并硬连接权重


def create_sequential_model(layers_list = [1,2,2,2,2,2,1]):
  layers = []
  for i in range(1, len(layers_list)):
      layers.append(nn.Linear(layers_list[i-1], layers_list[i]))
      layers.append(nn.ReLU())
  return nn.Sequential(*layers)
# This weight matrix implements |ABS| using ReLU neurons.
# |x-b| = Relu(-(x-b)) + Relu(x-b)
abs_weight_matrix = torch_tensor([[-1, -1],
                                  [1, 1]])
# Returns the pair of biases used for each of the ReLUs.
get_relu_bias = lambda b: torch_tensor([b, -b])
d = model.state_dict()
d['0.weight'], d['0.bias'] = torch_tensor([[-1],[1]]), get_relu_bias(8)
d['2.weight'], d['2.bias'] = abs_weight_matrix, get_relu_bias(4)
d['4.weight'], d['4.bias'] = abs_weight_matrix, get_relu_bias(2)
d['6.weight'], d['6.bias'] = abs_weight_matrix, get_relu_bias(1)
d['8.weight'], d['8.bias'] = abs_weight_matrix, get_relu_bias(1)
d['10.weight'], d['10.bias'] = torch_tensor([[1, 1]]), torch_tensor([0])
model.load_state_dict(d)
model.state_dict()


不出所料,我们可以看到该模型对 [0,15] 进行了完美预测


10


而且,不出所料,它并不能概括新的数据点


11


我们看到,我们可以硬连接模型,但使用梯度下降法,模型会收敛到相同的解吗?


12


答案是--没那么容易!相反,它陷入了局部最小值--预测平均值。


这是一个已知的现象,梯度下降会卡在局部最小值。对于高度非线性函数(如 is_even)的非光滑误差曲面,这种现象尤其普遍。


结论

希望本文能帮助你了解小型神经网络的基本结构。分析大型语言模型要复杂得多,但这是一个进展迅速、充满挑战的研究领域。


在使用大型语言模型时,我们很容易将注意力集中在提供数据和计算能力上,以获得令人印象深刻的结果,而不去了解它们是如何运行的。然而,可解释性提供了至关重要的见解,有助于解决公平性、包容性和准确性等问题。

文章来源:https://towardsdatascience.com/how-tiny-neural-networks-represent-basic-functions-8a24fce0e2d5
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消