动手学深度学习笔记(二)

从零开始写多层感知机

多层感知机

本节中,我们将以多层感知机(multilayer perceptron,简称 MLP)为例,介绍多层神经网络的概念。

import sys
sys.path.insert(0,'..')
import gluonbook as gb
batch_size = 256
train_data, test_data = gb.load_data_fashion_mnist(batch_size)

隐藏层

多层感知机在单层神经网络的基础上引入了一到多个隐藏层(hidden layer)。隐藏层位于输入层和输出层之间。

具体来说,给定一个小批量样本 \boldsymbol{X} \in \mathbb{R}^{n \times d},其批量大小为 n,输入个数为 d。假设多层感知机只有一个隐藏层,其中隐藏单元个数为 h。记隐藏层的输出(也称为隐藏层变量或隐藏变量)为 \boldsymbol{H},我们有 \boldsymbol{H} \in \mathbb{R}^{n \times h}。因为隐藏层和输出层均是全连接层,我们知道隐藏层的权重参数和偏差参数分别为 \boldsymbol{W}_h \in \mathbb{R}^{d \times h}\boldsymbol{b}_h \in \mathbb{R}^{1 \times h},以及输出层的权重和偏差参数分别为 \boldsymbol{W}_o \in \mathbb{R}^{h \times q}\boldsymbol{b}_o \in \mathbb{R}^{1 \times q}

我们先来看一个简单的输出 \boldsymbol{O} 的计算方法:

\begin{aligned} \boldsymbol{H} &= \boldsymbol{X} \boldsymbol{W}_h + \boldsymbol{b}_h,\\ \boldsymbol{O} &= \boldsymbol{H} \boldsymbol{W}_o + \boldsymbol{b}_o, \end{aligned}

也就是我们将这两个全连接层放置在一起,隐藏全连接层的输入直接进入输出全连接层。但如果我们将两个式子联立起来,就会发现

\boldsymbol{O} = (\boldsymbol{X} \boldsymbol{W}_h + \boldsymbol{b}_h)\boldsymbol{W}_o + \boldsymbol{b}_o = \boldsymbol{X} \boldsymbol{W}_h\boldsymbol{W}_o + \boldsymbol{b}_h \boldsymbol{W}_o + \boldsymbol{b}_o

这样等价与我们创建一个单层神经网络,它的输出层权重参数是 \boldsymbol{W}_h\boldsymbol{W}_o,且偏差参数为 \boldsymbol{b}_h \boldsymbol{W}_o + \boldsymbol{b}_o。这样,不管使用多少隐藏层,其效果等同于只有输出层的单层神经网络。

初始化

对于W1,W2参数的设置,一开始不太理解,只是简单地记忆为

W1,W2 : inputs --> hidden --->outputs

因此W1,W2的形状应该为(inputs, hidden),(hidden, outputs)

之后通过print数据在网络中的形状后才对网络运作有所理解(net函数中解释)

from mxnet import ndarray as nd

num_inputs = 28 * 28
num_outputs = 10

num_hidden = 256
weight_scale = .01

W1 = nd.random_normal(shape=(num_inputs, num_hidden), scale=weight_scale)
b1 = nd.zeros(num_hidden)
#print(W1.shape)

W2 = nd.random_normal(shape=(num_hidden, num_outputs), scale=weight_scale)
b2 = nd.zeros(num_outputs)

params = [W1, b1, W2, b2]

for param in params:
    param.attach_grad()
(784, 256)

激活函数

上述问题的根源在于全连接层只是对数据做线性变换(准确叫仿射变换(affine transformation))。但多个线性变换的叠加仍然是一个线性变化。解决问题的一个方法是引入非线性变换,例如对隐藏变量先作用一个按元素的非线性函数后再输入到后面的层中。这个非线性函数被叫做激活函数(activation function)。下面我们介绍几个常见的激活函数。

ReLU 函数

ReLU(rectified linear unit)函数提供了一个很简单的非线性变换。给定元素 x,该函数定义为

\text{relu}(x) = \max(x, 0).

可以看出,ReLU 函数只保留正数元素,并将负数元素清零。为了直观地观察这一非线性变换,我们先定义一个绘图函数xyplot

def relu(X):
    return nd.maximum(X, 0)

定义模型

对应上面讲到的初始化参数,W1的形状为(num_inputs, hidden),在net中将数据X的形状转为(-1(不确定),num_inputs),与W1相乘,将得出的结果传入激活函数Relu:

X:(-1(不确定),num_inputs) * W1:(num_inputs, hidden) --> (-1(不确定),hidden)

这里只需要用到一些简单的矩阵相乘的知识,如果不记得的话,只要记住两个二维矩阵,只要是(X,num)(num, Y)形式就能得出结果(X, Y),但如果是(num, Y)(X, num)就不能相乘(详情见线性代数)

def net(X):
    #print(X.shape)
    X = X.reshape((-1, num_inputs))
    #print(X.shape)
    h1 = relu(nd.dot(X, W1) + b1)
    output = nd.dot(h1, W2) + b2
    return output

Softmax和交叉熵损失函数

from mxnet import gluon
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()
#哇,这个函数贼难背
#计算softmax交叉熵损失(底层代码另做解释) 大体是先做softmax处理 然后再求loss值

训练

一开始,我不太理解batch_size的作用,心想如果规定了每次训练数据的数目,会不会对结果有影响,如果只是想识别一张图片会不会报错

然后,我尝试输出output的形状:print(output.shape)

得出了output.shape = (batch_size, num_ouputs) 因此batch_size只是作为一个每次训练迭代的一个单位数据长度而已(一张图片就为1),对训练无影响

from mxnet import autograd as autograd
from gluonbook import utils
learning_rate = .5

for epoch in range(5):  #迭代五次
    train_loss = 0. #初始化训练损失值
    train_acc = 0. #初始化训练准确率
    for data, label in train_data: #从训练数据迭代器中每次选取一组数据和标签进行训练
        with autograd.record():
            output = net(data) #输出值为net(data)
            #print("output")
            #print(output.shape)
            loss = softmax_cross_entropy(output, label)
            #将output值置入softmax函数,然后与label做比较,得出损失值loss
        loss.backward()
        #对loss求导
        utils.sgd(params, learning_rate, batch_size)
        #(改)对参数进行sgd优化,为了使学习率不那么敏感,学习率除以每一组数据的数量

        train_loss += nd.mean(loss).asscalar() 
        #loss的结构为数组,因此对数组的元素求平均,并且转为浮点数
        train_acc += gb.accuracy(output, label)
        #计算训练集的准确率
    
    test_acc = gb.evaluate_accuracy(test_data, net)
    #计算测试集的准确率
    print("Eopch %d. Loss: %f, Train acc %f, Test acc %f" % (
        epoch, train_loss/len(train_data),train_acc/len(train_data),test_acc))
        

Eopch 0. Loss: 0.237514, Train acc 0.911215, Test acc 0.891500
Eopch 1. Loss: 0.235197, Train acc 0.912578, Test acc 0.893800
Eopch 2. Loss: 0.229723, Train acc 0.913470, Test acc 0.887900
Eopch 3. Loss: 0.226010, Train acc 0.916329, Test acc 0.891500
Eopch 4. Loss: 0.221937, Train acc 0.917852, Test acc 0.894700

多层感知机 --- 使用Gluon

定义模型

现在使用Gluon来搭建多层感知机,更为简单方便

将使用Flatten层与Dense层搭建简单的多层网络如下:

from mxnet import gluon

net = gluon.nn.Sequential() #串型网络 按顺序排列
with net.name_scope():
    net.add(gluon.nn.Flatten()) #将输入展平为二维
    net.add(gluon.nn.Dense(256, activation="relu")) #隐藏层(全连接),256为num_output
    net.add(gluon.nn.Dense(128, activation="relu"))
    net.add(gluon.nn.Dense(10))#output层
print(net)

net.initialize()
net.collect_params()
Sequential(
  (0): Flatten
  (1): Dense(None -> 256, Activation(relu))
  (2): Dense(None -> 128, Activation(relu))
  (3): Dense(None -> 10, linear)
)





sequential3_ (
  Parameter sequential3_dense0_weight (shape=(256, 0), dtype=float32)
  Parameter sequential3_dense0_bias (shape=(256,), dtype=float32)
  Parameter sequential3_dense1_weight (shape=(128, 0), dtype=float32)
  Parameter sequential3_dense1_bias (shape=(128,), dtype=float32)
  Parameter sequential3_dense2_weight (shape=(10, 0), dtype=float32)
  Parameter sequential3_dense2_bias (shape=(10,), dtype=float32)
)

读取数据并训练

from mxnet import ndarray as nd
from mxnet import autograd
from gluonbook import utils

batch_size = 256
train_data, test_data = utils.load_data_fashion_mnist(batch_size)

softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.5} )
#设置训练器:
#net.collect_params()为网络中的权重和参数
#sgd为优化函数
#以字典的形式输入学习率

for epoch in range(5):
    train_loss = 0.
    train_acc = 0.
    for data, label in train_data:
        with autograd.record(): #设置训练范围(向前传播)
            output = net(data)
            loss = softmax_cross_entropy(output, label)
        loss.backward()
        trainer.step(batch_size)#每次读取大小为batch_size的数据训练
        
        train_loss += nd.mean(loss).asscalar()
        train_acc += utils.accuracy(output, label)
        
    test_acc = utils.evaluate_accuracy(test_data, net)
    print("Epoch %d. Loss: %f, Train acc %f, Test acc %f" % (
        epoch, train_loss/len(train_data), train_acc/len(train_data),test_acc))
            


Epoch 0. Loss: 0.841238, Train acc 0.688143, Test acc 0.825200
Epoch 1. Loss: 0.483838, Train acc 0.820107, Test acc 0.847700
Epoch 2. Loss: 0.421176, Train acc 0.843467, Test acc 0.859700
Epoch 3. Loss: 0.382882, Train acc 0.858245, Test acc 0.862000
Epoch 4. Loss: 0.357187, Train acc 0.867880, Test acc 0.877500
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容