多层感知机(multilayer perceptron,简称 MLP),多层神经网络模型。
隐藏层
多层感知机在单层神经网络的基础上引入了一到多个隐藏层(hidden layer)。隐藏层介于输入层和输出层之间。
包含单个隐藏层的多层感知机设计:
$$H = XW_h + b_h$$
$$O = HW_o + b_o$$
将隐藏层的输出直接作为输入层的输入。两个式子联立起来,得到:
$$O = (XW_h + b_h)W_o + b_o = XW_hW_0 + b_hW_o + b_o$$
可以看出,虽然引入了隐藏层,却依然等价与一个单层神经网络:其中输出层权重参数为$W_hW_o$,偏置参数为$b_hW_o + b_o$。即便添加再多的隐藏层,以上设计依然等价与一个单层神经网络。
激活函数
上述问题的根源在于全连接层只是对数据做仿射变换(affine transformation),而多个仿射变换的叠加依然是一个仿射变换。解决问题的一个方法是引入一个非线性变换,例如对隐藏层变量使用按元素操作的非线性函数进行变换,然后作为下一个全连接层的输入。这个非线性函数被称为激活函数(activation function)。
ReLU函数
ReLU(rectified linear unit)函数提供了一个很简单的非线性变换。给定元素$x$,该函数定义为:
$$ReLU(x) = max(x, 0)$$
ReLU函数只保留正数元素,并将负数元素清零。
当输入为负数时,ReLU函数的导数为0,输入为正时,ReLU的导数为1
绘制函数图像:
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
| from mxnet import autograd, nd from matplotlib import pyplot as plt
def xyplot(x_vals, y_vals, name):
plt.rcParams['figure.figsize'] = (5, 2.5) plt.plot(x_vals.asnumpy(), y_vals.asnumpy()) plt.xlabel('x') plt.ylabel(name + '(x)') plt.show()
x = nd.arange(-8.0, 8.0, 0.1)
x.attach_grad()
with autograd.record(): y = x.relu()
xyplot(x, y, "relu")
y.backward()
xyplot(x, x.grad, "grad of relu")
|
结果如图所示:

ReLU函数的导数:

Sigmoid函数
Sigmoid函数可以将元素的值变换到0和1之间:
$$sigmoid(x) = \frac{1}{1+\exp(-x)}$$
Sigmoid函数在早期神经网络中较为普遍,但是目前更多被简单的ReLU函数所替代。当输入接近0时,sigmoid函数接近线性变换。
绘制函数图像:
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
| from mxnet import autograd, nd from matplotlib import pyplot as plt
def xyplot(x_vals, y_vals, name):
plt.rcParams['figure.figsize'] = (5, 2.5) plt.plot(x_vals.asnumpy(), y_vals.asnumpy()) plt.xlabel('x') plt.ylabel(name + '(x)') plt.show()
x = nd.arange(-8.0, 8.0, 0.1)
x.attach_grad()
with autograd.record(): y = x.sigmoid()
xyplot(x, y, 'sigmoid')
y.backward() xyplot(x, x.grad, 'grad of sigmoid')
|
sigmoid函数图像:

sigmoid的导数:

当输入为0时,sigmoid函数的导数达到最大值0.25;当输入越偏离0时,sigmoid函数的导数越接近0.
Tanh函数
Tanh(双曲正切)函数可以将元素的值变换到-1和1之间:
$$tanh(x) = \frac{1-\exp(-2x)}{1+\exp(-2x)}$$
当输入接近0时,tanh函数接近线性变换。虽然函数的形状和sigmoid函数的形状很相似,但是tanh函数在坐标系的原点上对称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from mxnet import autograd, nd from matplotlib import pyplot as plt
def xyplot(x_vals, y_vals, name):
plt.rcParams['figure.figsize'] = (5, 2.5) plt.plot(x_vals.asnumpy(), y_vals.asnumpy()) plt.xlabel('x') plt.ylabel(name + '(x)') plt.show()
x = nd.arange(-8.0, 8.0, 0.1)
x.attach_grad() with autograd.record(): y = x.tanh()
xyplot(x, y, 'tanh')
y.backward() xyplot(x, x.grad, 'grad of tanh')
|
tanh的函数图像:

tanh的导数:

当输入为0时,tanh函数的导数达到最大值1;当输入偏离0时,tanh函数的导数越接近0.
多层感知机
多层感知机就是至少含有一个隐藏层的由全连接层组成的神经网络,且每个隐藏层的输出通过激活函数变换。多层感知机的层数和各个隐藏层中隐藏单元个数都是超参数。由于引入了非线性因素,多层感知机的计算方式为:
$$H=\phi(XW_h+b_h)$$
$$O = HW_o +b_o$$
其中$\phi$表示激活函数。在分类问题中,可以对输出Osoftmax运算,并使用softmax回归中的交叉熵损失函数。在回归问题中,将输出层个数设置为1,并将输出$O$直接提供给线性回归中使用的平方损失函数。
多层感知机的实现
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
| import gluonbook as gb from mxnet import nd from mxnet.gluon import loss as gloss
batch_size = 256 train_iter, test_iter = gb.load_data_fashion_mnist(batch_size)
num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = nd.random.normal(scale=0.01, shape=(num_inputs, num_hiddens)) b1 = nd.zeros(shape=(num_hiddens))
W2 = nd.random.normal(scale=0.01, shape=(num_hiddens, num_outputs)) b2 = nd.zeros(shape=(num_outputs))
params = [W1, b1, W2, b2]
for param in params: param.attach_grad()
def relu(X): return nd.maximum(X, 0)
def net(X): X = X.reshape((-1, num_inputs)) H = relu(nd.dot(X, W1) + b1)
return nd.dot(H, W2) + b2
loss = gloss.SoftmaxCrossEntropyLoss()
num_epochs, lr = 5, 0.5
gb.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params, lr)
|
多层感知机的Gluon实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import gluonbook as gb from mxnet import gluon, init from mxnet.gluon import loss as gloss, nn
batch_size = 256 train_iter, test_iter = gb.load_data_fashion_mnist(batch_size)
net = nn.Sequential() net.add(nn.Dense(256, activation='relu'), nn.Dense(10)) net.initialize(init.Normal(sigma=0.01))
loss = gloss.SoftmaxCrossEntropyLoss() trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.5})
num_epochs = 5
gb.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, trainer)
|