Skip to content

Latest commit

 

History

History
126 lines (98 loc) · 4.7 KB

File metadata and controls

126 lines (98 loc) · 4.7 KB

3.9 多层感知机的从零开始实现

我们已经从上一节里了解了多层感知机的原理。下面,我们一起来动手实现一个多层感知机。首先导入实现所需的包或模块。

import tensorflow as tf
import numpy as np
import sys
print(tf.__version__)

3.9.1 获取和读取数据

这里继续使用Fashion-MNIST数据集。我们将使用多层感知机对图像进行分类。

from tensorflow.keras.datasets import fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
batch_size = 256
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)
x_train = x_train/255.0
x_test = x_test/255.0
train_iter = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(batch_size)
test_iter = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(batch_size)

3.9.2 定义模型参数

我们在3.6节(softmax回归的从零开始实现)里已经介绍了,Fashion-MNIST数据集中图像形状为 $28 \times 28$,类别数为10。本节中我们依然使用长度为 $28 \times 28 = 784$ 的向量表示每一张图像。因此,输入个数为784,输出个数为10。实验中,我们设超参数隐藏单元个数为256。

num_inputs, num_outputs, num_hiddens = 784, 10, 256

w1 = tf.Variable(tf.random.truncated_normal([num_inputs, num_hiddens], stddev=0.1))
b1 = tf.Variable(tf.random.truncated_normal([num_hiddens], stddev=0.1))
w2 = tf.Variable(tf.random.truncated_normal([num_hiddens, num_outputs], stddev=0.1))
b2=tf.Variable(tf.random.truncated_normal([num_outputs], stddev=0.1))

3.9.3 定义激活函数

这里我们使用基础的max函数来实现ReLU,而非直接调用relu函数。

def relu(x):
    return tf.math.maximum(x,0)

3.9.4 定义模型

同softmax回归一样,我们通过view函数将每张原始图像改成长度为num_inputs的向量。然后我们实现上一节中多层感知机的计算表达式。

def net(X):
    def net(x,w1,b1,w2,b2):
    x = tf.reshape(x,shape=[-1,num_inputs])
    h = relu(tf.matmul(x,w1) + b1 )
    y = tf.math.softmax( tf.matmul(h,w2) + b2 )
    return y

3.9.5 定义损失函数

为了得到更好的数值稳定性,我们直接使用Tensorflow提供的包括softmax运算和交叉熵损失计算的函数。

def loss(y_hat,y_true):
    return tf.losses.sparse_categorical_crossentropy(y_true,y_hat)

3.9.6 训练模型

训练多层感知机的步骤和3.6节中训练softmax回归的步骤没什么区别。我们直接调用d2l包中的train_ch3函数,它的实现已经在3.6节里介绍过。我们在这里设超参数迭代周期数为5,学习率为0.5。

注:由于原书的mxnet中的SoftmaxCrossEntropyLoss在反向传播的时候相对于沿batch维求和了,而PyTorch默认的是求平均,所以用PyTorch计算得到的loss比mxnet小很多(大概是maxnet计算得到的1/batch_size这个量级),所以反向传播得到的梯度也小很多,所以为了得到差不多的学习效果,我们把学习率调得成原书的约batch_size倍,原书的学习率为0.5,这里设置成100.0。(之所以这么大,应该是因为d2lzh_pytorch里面的sgd函数在更新的时候除以了batch_size,其实PyTorch在计算loss的时候已经除过一次了,sgd这里应该不用除了)

def acc(y_hat,y):
    return np.mean((tf.argmax(y_hat,axis=1) == y))

num_epochs, lr = 5, 0.5

for epoch in range(num_epochs):
    loss_all = 0
    for x,y in train_iter:
        with tf.GradientTape() as tape:
            y_hat = net(x,w1,b1,w2,b2)
            l = tf.reduce_mean(loss(y_hat,y))
            loss_all += l.numpy()
            grads = tape.gradient(l, [w1, b1, w2, b2])
            w1.assign_sub(grads[0])
            b1.assign_sub(grads[1])
            w2.assign_sub(grads[2])
            b2.assign_sub(grads[3])
    print(epoch, 'loss:', l.numpy())
    total_correct, total_number = 0, 0

    for x,y in test_iter:
        with tf.GradientTape() as tape:
            y_hat = net(x,w1,b1,w2,b2)
            y=tf.cast(y,'int64')
            correct=acc(y_hat,y)
    print(epoch,"test_acc:", correct)

输出:

0 loss: 1.0416569
0 test_acc: 0.75
1 loss: 1.0674641
1 test_acc: 0.75
2 loss: 0.90997523
2 test_acc: 0.875
3 loss: 0.8479213
3 test_acc: 0.9375
4 loss: 0.84292793
4 test_acc: 0.9375

小结

  • 可以通过手动定义模型及其参数来实现简单的多层感知机。
  • 当多层感知机的层数较多时,本节的实现方法会显得较烦琐,例如在定义模型参数的时候。

注:本节除了代码之外与原书基本相同,原书传送门