0x00 感知器

感知器这个玩意其实蛮有意思,他能用来学习实现其他函数。一个感知器有3个部分组成:

  • 输入权值和偏置项:学过机器学习的你一看可能就知道,这就是那个w和b。一个感知器可以接收多个输入,每个输入上都有一个权值
  • 激活函数:比如机器学习中常用的sigmoid函数,一般记作f
  • 输出

感知器的输出可以使用一个通用的函数来计算,如下:

y=f(wx+b)y=f(wx+b)

看到这儿不要慌,下面我们通过一个简单的例子来简述感知器的训练过程。

0x01 感知器的训练

感知器的训练过程,其实就是设法通过不断地调整w和b的值,以求拟合我们输入和输出的数据。而调整的方式也很简单,如下:

wiwi+Δwibb+Δb\begin{align*} w_i&\gets w_i+\Delta w_i \\ b&\gets b+\Delta b \end{align*}

其中:

Δwi=η(ty)xiΔb=η(ty)\begin{align*} \Delta w_i&=\eta(t-y)x_i \\ \Delta b&=\eta(t-y) \end{align*}

上式中η\eta即为学习率,t即为true表示实际值,y即为预测值。

现在我们假设激活函数f为如下这个跃迁函数:

f(z)={1z>00otherwisef(z)=\begin{equation}\begin{cases}1\qquad z>0\\ 0\qquad \text{otherwise}\end{cases}\end{equation}

此后我们使用η=0.2\eta=0.2的学习率来训练一个感知器,以实现AND(与)运算的功能。我们以如下矩阵的形式来表示运算x1 AND x2x_1 \text{ AND } x_2的输入值:

x1 AND x2[x1x2]x_1 \text{ AND } x_2\rightarrow \left[ \begin{matrix} x_1 \\ x_2 \end{matrix} \right]

首先我们假设初始值矩阵w和b如下:

W=[00],b=0W=\left[ \begin{matrix} 0&0 \end{matrix} \right], b=0

则当输入值为1 AND 1时有:

z=Wx+b=[00][11]+0=0y=f(z)=f(0)=0z=Wx+b=\left[ \begin{matrix} 0&0 \end{matrix} \right]\left[ \begin{matrix} 1 \\ 1 \end{matrix} \right]+0=0\\ y=f(z)=f(0)=0

此时我们来计算变化后的W和b,众所周知1 AND 1=1,则有:

Δw1=0.2×(10)×1=0.2Δw2=0.2×(10)×1=0.2Δb=0.2×(10)=0.2\Delta w_1=0.2\times (1-0)\times 1=0.2\\ \Delta w_2=0.2\times (1-0)\times 1=0.2\\ \Delta b=0.2\times (1-0)=0.2

由此,即可得出变化后的W和b分别为:

W=[0.20.2],b=0.2W=\left[ \begin{matrix} 0.2&0.2 \end{matrix} \right], b=0.2

此时对于输入1 AND 1,重新计算感知器的输出,我们会发现感知器已经可以输出正确的结果了。由此,我们继续训练我们的感知器,当输入值为0 AND 1时,依据新的W和b重新计算感知器的输出,有:

z=Wx+b=[0.20.2][01]+0.2=0.4y=f(z)=f(0.4)=1z=Wx+b=\left[ \begin{matrix} 0.2&0.2 \end{matrix} \right]\left[ \begin{matrix} 0 \\ 1 \end{matrix} \right]+0.2=0.4\\ y=f(z)=f(0.4)=1

此时其返回了错误的结果,我们再来计算w和b的变化量,然后进行更新,则有:

Δw1=0.2×(01)×0=0Δw2=0.2×(01)×1=0.2Δb=0.2×(01)=0.2\Delta w_1=0.2\times (0-1)\times 0=0\\ \Delta w_2=0.2\times (0-1)\times 1=-0.2\\ \Delta b=0.2\times (0-1)=-0.2

使用上面的变化量再对W和b进行更新可有:

W=[0.20],b=0W=\left[ \begin{matrix} 0.2&0 \end{matrix} \right], b=0

此时我们发现这组W和b已经可以完全适应输入1 AND 1和0 AND 1的情况了,我们再引入下一组训练数据1 AND 0,重新计算上述流程,并对W和b进行更新,在此我们不再写出相关计算步骤,而是直接给出更新后的W和b即为:

W=[00],b=0.2W=\left[ \begin{matrix} 0&0 \end{matrix} \right], b=-0.2

这组W和b适应了输入1 AND 0和0 AND 1的情况,却遗忘了1 AND 1的学习用例,于是我们不断地使用训练数据反复对模型进行训练,最终所得的结果如下:

W=[0.40.2],b=0.4W=\left[ \begin{matrix} 0.4&0.2 \end{matrix} \right], b=-0.4

在上述的W和b下,与运算所用的4组测试用例——1 AND 1, 0 AND 0, 0 AND 1, 1 AND 0——已经实现了完全匹配。

对于上述反复训练的过程,我们用python代码实现了出来,仅供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import numpy as np

# 设置初始的参数W和b
W = np.array([0, 0])
b = 0
# 与运算的4组输入数据,0表示false,1表示true
data = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
# 学习率为0.2
learn_rate = 0.2

# 以上述的跃迁函数作为激活函数
def f(x):
if x > 0:
return 1
return 0

# 用我们训练的模型W和b来计算预测值
def y(x):
z = np.dot(W, x.T) + b
return f(z)

# 判断4组输入数据的预测值与答案值是否完全匹配,其返回2个参数
# 如果完全匹配的话,返回True, -1
# 如果不完全匹配的,返回False, 不匹配的用例下标
def all_matched():
for i in range(4):
x = data[i]
if y(x) != (x[0] & x[1]):
return False, i
return True, -1

# 学习第i组输入数据
def study(i):
x = data[i]
error = (x[0] & x[1]) - y(x)
# 计算上述的更新值
delta_w1 = learn_rate * error * x[0]
delta_w2 = learn_rate * error * x[1]
delta_b = learn_rate * error
# 更新模型参数W和b
global W, b
W = np.array([W[0] + delta_w1, W[1] + delta_w2])
b = b + delta_b


totally_matched, failed = all_matched()

# 所有输入的数据的预测值与答案值不完全匹配的话进入循环
while not totally_matched:
study(failed)
totally_matched, failed = all_matched()

# 输出最终训练所得的W和b
print(W)
print(b)