异或问题就是当两个输入的布尔值不一致时,输出为 True(可以用 1 代表),如果两个输入的布尔值一致的时候,输出为 False(可以用 0 代表)。
据说多层感知机(MLP)是很难处理异或问题的,就好比下面的问题。
首先由一个问题引入,来自邱锡鹏老师的书《神经网络与深度学习》第四章的习题 4-2:
习题 4-2 试设计一个前馈神经网络来解决 XOR 问题,要求该前馈神经网络具有两个隐藏神经元和一个输出神经元,并使用 ReLU 作为激活函数。
一个可行的结果如下,
W ( 1 ) = [ 1 1 1 1 ] , b ( 1 ) = [ 0 − 1 ] w ( 2 ) = [ 1 − 2 ] , b ( 2 ) = [ 0 ] (1) \boldsymbol{W}^{(1)}=\left[\begin{array}{ll}1 & 1 \\ 1 & 1\end{array}\right], \boldsymbol{b}^{(1)}=\left[\begin{array}{c}0 \\ -1\end{array}\right]\\ \boldsymbol{w}^{(2)}=\left[\begin{array}{c}1 \\ -2\end{array}\right], b^{(2)}=\left[0\right] \tag{1} W(1)=[1111],b(1)=[0−1]w(2)=[1−2],b(2)=[0](1)
故整个网络的计算为:
y = ( w ( 2 ) ) T ( ReLU ( ( W ( 1 ) ) T X + b ( 1 ) ) ) + b ( 2 ) \boldsymbol{y}=\left(\boldsymbol{w}^{(2)}\right)^{\mathrm{T}}\left(\operatorname{ReLU}\left(\left(\boldsymbol{W}^{(1)}\right)^{\mathrm{T}} \boldsymbol{X}+\boldsymbol{b}^{(1)}\right)\right)+b^{(2)} y=(w(2))T(ReLU((W(1))TX+b(1)))+b(2)
代入:
X = [ 0 0 1 1 0 1 0 1 ] \boldsymbol{X}=\left[\begin{array}{llll}0 & 0 & 1 & 1 \\ 0 & 1 & 0 & 1\end{array}\right] X=[00011011]
可以算得: y = [ 0 1 1 0 ] \boldsymbol{y}=\left[\begin{array}{llll}0 & 1 & 1 & 0\end{array}\right] y=[0110]
实际上这样的一个网络结构,如果使用随机初始化的方式去训练,是训练不好的,原因在于中间要求的是一个 ReLU 激活函数,如果换成其他的激活函数就能够训练好。
使用如下代码:
using Flux
function loss()
ŷ = mlp(data)
Flux.mse(ŷ, y)
end
cb = function ()
println(loss())
end
data = Array([[0 1 0 1];
[0 0 1 1]]);
y = Array([[0 1 1 0];]);
mlp = Chain(Dense(2, 2, relu), Dense(2, 1));
ps = Flux.params(mlp);
opt = ADAM(0.01)
@time Flux.train!(loss, ps, Iterators.repeated((), 1000), opt, cb=cb)
训练完之后会发现,损失依然很高,输出的结果会是全部都很接近 0.5 0.5 0.5.
如果我们将隐含层的参数设置为 (1) 式的结果,然后只训练输出层的权重 w ( 2 ) \boldsymbol{w}^{(2)} w(2),那么会得到相同的结果:
# 自定义权重,将权重都初始化为全 1 的矩阵
mlp = Chain(Dense(2, 2, relu, bias=[0; -1], init=ones),
Dense(2, 1, bias=zeros(1), init=ones))
# 只拿出第三个参数,即输出层的权重训练
ps = Flux.params(Flux.params(mlp)[3])
opt = ADAM(0.1)
@time Flux.train!(loss, ps, Iterators.repeated((), 1000), opt, cb=cb)
结果会得到 w ( 2 ) = [ 0.9999... − 1.9999... ] \boldsymbol{w}^{(2)}=[0.9999... -1.9999...] w(2)=[0.9999...−1.9999...],与题目设计的是一样的。
但实际上,如果我们在隐含层上使用三个神经元,就能够解决这个问题。这证明只使用两个隐含神经元,模型的能力并不够,加多了一个就能够解决了:
mlp = Chain(Dense(2, 3, relu), Dense(3, 1));
ps = Flux.params(mlp)
opt = ADAM(0.01)
@time Flux.train!(loss, ps, Iterators.repeated((), 1000), opt, cb=cb)
# loss = 0.22230548
# loss = 0.21818444
# ...
# loss = 0.0
ŷ = mlp(data)
最后的解是对的,查看各层的参数可以知道
W ( 1 ) = [ 0.574309 − 0.574309 0.92754 − 0.966212 1.12378 − 1.12138 ] , b ( 1 ) = [ 0.5743128 − 0.00046141824 − 0.0034916808 ] w ( 2 ) = [ − 1.74122 0.60053 1.2883 ] , b ( 2 ) = [ 1.0000067 ] \boldsymbol{W}^{(1)}=\left[\begin{array}{ll} 0.574309 & -0.574309\\ 0.92754 & -0.966212\\ 1.12378 & -1.12138\end{array}\right], \boldsymbol{b}^{(1)}=\left[\begin{array}{c} 0.5743128\\ -0.00046141824\\ -0.0034916808\end{array}\right]\\ \boldsymbol{w}^{(2)}=\left[\begin{array}{c} -1.74122 \\ 0.60053 \\ 1.2883\end{array}\right], b^{(2)}=\left[1.0000067\right] W(1)=⎣⎡0.5743090.927541.12378−0.574309−0.966212−1.12138⎦⎤,b(1)=⎣⎡0.5743128−0.00046141824−0.0034916808⎦⎤w(2)=⎣⎡−1.741220.600531.2883⎦⎤,b(2)=[1.0000067]
当使用两个隐含神经元的时候,使用随机初始化权重的方式去训练,会非常难以求解,但是如果使用三个隐含神经元,那么其能力就足以解决异或问题了。
不过实际上,在使用两个隐含神经元的时候,不使用 ReLU 作为激活函数,而是在隐含层使用 Sigmoid 函数,那么只有两个隐含神经元的情况下,也可以解决异或问题。
主要的问题应该还是在 ReLU 会把小于零的部分直接截断,相当于这个神经元没有被激活,会容易造成神经元「死亡」无法再继续训练。