内容提要
引子--双控开关和三控开关
|
拓展--数字电路
|
深入--神经网络
--神经网络之感知器:给定模型,通过数据训练参数,可以解决分类问题。
--神经网络之隐藏层:更强大的神经网络(更多参数)
--神经网络之激活函数:超越线性(非线性的引入)
--神经网络之反向传播:质的飞跃(性能大幅提升)
--神经网络之实用关键:算法收敛(快速有效地找到合适的参数)
双控开关和三控开关
我在进行乐高编程的时候,可以在电脑上启动,也可以在乐高机器人的可编程程序块上启动。就像家里客厅的灯,有时希望可以有两个开关来控制它。比如在楼上或楼下各安装一个,都可以独立控制灯泡的开关。初中生知道,最简单的办法是用两个单刀双掷开关来实现,如下图。
如果还想在卧室增加一个开关,实现用三个开关来控制一个灯泡呢?经典的办法是两个单刀双掷开关加一个双刀双掷开关,如下图。
如果想要四个开关,五个,甚至七八个开关来控制这个灯泡呢?电路图会是多复杂?有没有什么通用的办法来解决?
引入数字电路
我们先回到最简单的“双控开关”。开关只有2个状态,“开”和“关”,我们分布用“1”和“0”来表示。1代表开,0代表关。这样,2个开关可以用一个2位的二进制数表示,两个开关有四种组合{开开,开关,关开,关关},记做{00,01,10,11}。和上面双控开关对应起来,有{00,11}灯泡亮,{01,10}灯泡灭。
我们把它写出函数的形式:y = f(b[1],b[2])。b[1],b[2]分布代表开关1和2的状态,取值为0或1。函数值y取值也是0或1表示灯泡亮或灭。期待的结果为:
灯泡亮:y = 1 = f(0,0) = f(1,1)
灯泡灭:y = 0 = f(1,0) = f(0,1)
函数f是一个什么运算?不难发现,其实f是一个异或/模二相加运算加一个取反。即 f(b[1],b[2]) = !(b[1]+b[2]),这里+表示模二相加,!表示取反。这用一个简单的数字电路就可以实现!只要把一个标准与非门+一个异或门串联起来。
用数字电路解决多控开关
一旦写成数字电路的“数字运算”形式,就很容易打开思路拓展到三控开关。y = f(b[1],b[2],b[3])。假设初始状态{000}是亮的,拨动任意一个开关一次,将会有3种可能状态{001,010,100},这三种状态是灭的。在这3种状态下再拨动一个开关一次,再次灯泡亮,此时要么回到状态{000},要么得到状态{011,101,110}。以此类推,得到:
灯泡亮:{000,011,101,110}
灯泡灭:{111,001,010,100}
仍然是这3个开关状态的模二相加再取非!
即 f(b[1],b[2],b[3]) = !(b[1]+b[2]+b[3])
不管增加到几个开关,都可以简答地用与非门和异或门来实现!
数字电路基本概念
我们已经用数字电路实现了多控开关,用到了与非门和异或门,对应着数字电路的两个基本运算:取反NOT,异或XOR。数字电路4个基本运算还有与AND和或OR。
与门(AND):
f(0,0) = 0; f(0,1) = 0; f(1,0) = 0; f(1,1) = 1
与非门(NAND):
f(0,0) = 1; f(0,1) = 1; f(1,0) = 1; f(1,1) = 0
或门(OR):
f(0,0) = 0; f(0,1) = 1; f(1,0) = 1; f(1,1) = 1
异或门(XOR):
f(0,0) = 1; f(0,1) = 0; f(1,0) = 0; f(1,1) = 1
注意到前面一直说的是与非门而不是非门。因为与非门是一般数字电路的标准,电路实现上,非门不太稳定所以设计成与非门形式。很容易通过与非门来实现取反功能,只要把与非门的另一个输入固定成1即可。
关于数字电路的一些思考
我们已经了解到了与门、与非门、或门以及异或门的逻辑。这是构成数字电路的最基本要素。上面函数 f(b[1],b[2]) 的2个输入 (b[1],b[2]) 是独立的,很自然地想到可以看作在直角坐标系中的一个点。然后把函数值 y = f(b[1],b[2]) 看作是坐标系中点(b[1],b[2])的标签(label)。这样就把求函数值问题转化为分类问题了。对两输入问题来说,就是把4个点{00,01,10,11}分成两类{y=0, y=1}。
如下图所示。与门,与非门,或门都可以找到一个线性方程(一条直线)就能把结果0和1区分开来。不过异或无法找到这样一条直线进行区分。
然而神经网络刚好擅长解决这种分类问题。
最简单的神经网络
受生物神经结构的启发,有人提出了最简单的神经网络:感知器。
感知器的结构如下:加权求和值经过一个函数得到输出。权重是可以调节的参数,函数可能是一个简答的阈值/门限判定。
对只有2个输入 (b[1],b[2]) 的情况来说,可以把上面感知器用公式表达如下。
加权求和:z = w[1]*b[1] + w[2]*b[2]
比较判定:y = g(z),g:当z大于/小于b时,y = 1 ,否则 y = 0
很容易写出来,AND的实现之一:w[i] = 1;g:当 z = b[1] + b[2] 大于门限 b = 1.5 时取1否则取0。NAND只要把AND的判定函数取0或1反过来即可。OR把门限 b 取 0.5 即可。我们还是来写一点代码来搜索符合条件的参数吧!以AND为例。
def perceptron(x,w,b):
"""
感知器
"""
sum_val =
0
for e in zip(x,w):
sum_val += e[0]*e[1]
if sum_val>b:
return
1
else:
return
0
def prediction(input_vecs, w, b, labels):
"""
给定感知器参数,获取所有数据的输出并和标签对比,输出对比结果
"""
for e in zip(input_vecs,labels):
if perceptron(e[0],w,b)!=e[1]:
return
False
return
True
#数据
input_vecs =
[[1,1],
[0,0],
[1,0],
[0,1]]
labels =
[1,
0,
0,
0]
#参数的可能取值,[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
w_range =
[x/10.0
for x in range(0,10)]
#遍历所有参数,打印符合的系数
for b in
[0,
0.5,
1,
1.5,
2]:
for w1 in w_range:
for w2 in w_range:
w =
[w1,w2]
if prediction(input_vecs, w, b, labels)==True:
print w,b
运行这段代码可以得到几十条符合条件的参数。是不是对给定的这个数学模型(感知器),通过调节模型参数(w, b),都可以通过“数据-标签”进行训练来实现分类功能?继续尝试,NAND,OR都可以得到,可是找不到可以实现异或的参数。看来感知器模型还不够强大。