多层感知机

上图所示的多层感知机中,输入和输出个数分别为4和3,中间的隐藏层中包含了5个隐藏单元(hidden unit)。由于输入层不涉及计算,图3.3中的多层感知机的层数为2。由图3.3可见,隐藏层中的神经元和输入层中各个输入完全连接,输出层中的神经元和隐藏层中的各个神经元也完全连接。因此,多层感知机中的隐藏层和输出层都是全连接层。

具体来说,给定一个小批量样本\(\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} \in \mathbb{R}^{n \times q}\)的计算为

\[\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\)。不难发现,即便再添加更多的隐藏层,以上设计依然只能与仅含输出层的单层神经网络等价。

激活函数

上面问题的根源就在于每一层的变换都是线性变换.线性变换的叠加依然是线性变换,所以,我们需要引入非线性.即对隐藏层的输出经过激活函数后,再作为输入输入到下一层.  

几种常见的激活函数:

  • relu
  • sigmoid
  • tanh

relu

\[\text{ReLU}(x) = \max(x, 0).
\]

其曲线及导数的曲线图绘制如下:



sigmoid

其曲线及导数的曲线图绘制如下:

\[\text{sigmoid}(x) = \frac{1}{1 + \exp(-x)}.
\]



tanh

\[\text{tanh}(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)}.
\]

其曲线及导数的曲线图绘制如下:




从头实现多层感知机

必要的模块导入

  1. import torch
  2. import numpy as np
  3. import matplotlib.pylab as plt
  4. import sys
  5. import torchvision
  6. import torchvision.transforms as transforms

获取和读取数据

依然是之前用到的FashionMNIST数据集

  1. batch_size = 256
  2. num_workers = 4 # 多进程同时读取
  3. def load_data(batch_size,num_workers):
  4. mnist_train = torchvision.datasets.FashionMNIST(root='/home/sc/disk/keepgoing/learn_pytorch/Datasets/FashionMNIST',
  5. train=True, download=True,
  6. transform=transforms.ToTensor())
  7. mnist_test = torchvision.datasets.FashionMNIST(root='/home/sc/disk/keepgoing/learn_pytorch/Datasets/FashionMNIST',
  8. train=False, download=True,
  9. transform=transforms.ToTensor())
  10. train_iter = torch.utils.data.DataLoader(
  11. mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
  12. test_iter = torch.utils.data.DataLoader(
  13. mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
  14. return train_iter,test_iter
  15. train_iter,test_iter = load_data(batch_size,num_workers)

模型参数初始化

我们的神经网络有2层,所以相应的参数变成[W1,b1,W2,b2]

  1. ##
  2. num_inputs, num_outputs, num_hiddens = 784, 10, 256 #假设隐藏层有256个神经元
  3. W1 = torch.tensor(np.random.normal(0, 0.01, (num_inputs, num_hiddens)), dtype=torch.float)
  4. b1 = torch.zeros(num_hiddens, dtype=torch.float)
  5. W2 = torch.tensor(np.random.normal(0, 0.01, (num_hiddens,num_outputs)), dtype=torch.float)
  6. b2 = torch.zeros(num_outputs, dtype=torch.float)
  7. params = [W1,b1,W2,b2]
  8. for param in params:
  9. param.requires_grad_(requires_grad=True)

模型定义

模型需要用到激活函数relu以及将输出转换为概率的函数softmax.

所以首先定义好relu和softmax.

relu定义:

  1. def relu(X):
  2. #print(X.shape)
  3. return torch.max(input=X,other=torch.zeros(X.shape))

torch.max用法

softmax定义:

  1. def softmax(X): # X.shape=[样本数,类别数]
  2. _exp = X.exp()
  3. partion = X_exp.sum(dim=1, keepdim=True) # 沿着列方向求和,即对每一行求和
  4. #print(partion.shape)
  5. return X_exp/partion # 广播机制,partion被扩展成与X_exp同shape的,对应位置元素做除法

模型结构定义:

  1. def net(X):
  2. X = X.view((-1,num_inputs))
  3. #print(X.shape)
  4. H = relu(torch.matmul(X,W1) + b1)
  5. #print(H.shape)
  6. output = torch.matmul(H,W2) + b2
  7. return softmax(output)

损失函数定义

  1. def cross_entropy(y_hat, y):
  2. y_hat_prob = y_hat.gather(1, y.view(-1, 1)) # ,沿着列方向,即选取出每一行下标为y的元素
  3. return -torch.log(y_hat_prob)

优化器定义

  1. def sgd(params, lr, batch_size):
  2. for param in params:
  3. param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data

模型训练

定义精度评估函数

  1. def evaluate_accuracy(data_iter, net):
  2. acc_sum, n = 0.0, 0
  3. for X, y in data_iter:
  4. acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
  5. n += y.shape[0]
  6. return acc_sum / n

训练:

  • 数据加载
  • 前向传播
  • 计算loss
  • 反向传播,计算梯度
  • 根据梯度值,更新参数
  • 清空梯度

    加载下一个batch的数据,循环往复.
  1. num_epochs, lr = 5, 0.1
  2. def train():
  3. for epoch in range(num_epochs):
  4. train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
  5. for X, y in train_iter:
  6. #print(X.shape,y.shape)
  7. y_hat = net(X)
  8. l = cross_entropy(y_hat, y).sum() # 求loss
  9. l.backward() # 反向传播,计算梯度
  10. sgd(params, lr, batch_size) # 根据梯度,更新参数
  11. 1.grad.data.zero_() # 清空梯度
  12. b1.grad.data.zero_()
  13. 2.grad.data.zero_() # 清空梯度
  14. b2.grad.data.zero_()
  15. train_l_sum += l.item()
  16. train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
  17. n += y.shape[0]
  18. test_acc = evaluate_accuracy(test_iter, net)
  19. print('epoch %d, loss %.4f, train_acc %.3f,test_acc %.3f'
  20. % (epoch + 1, train_l_sum / n, train_acc_sum/n, test_acc))
  21. train()

输出如下:

  1. epoch 1, loss 1.0535, train_acc 0.629test_acc 0.760
  2. epoch 2, loss 0.6004, train_acc 0.789test_acc 0.788
  3. epoch 3, loss 0.5185, train_acc 0.819test_acc 0.824
  4. epoch 4, loss 0.4783, train_acc 0.833test_acc 0.830
  5. epoch 5, loss 0.4521, train_acc 0.842test_acc 0.832

多层感知机的简单实现

必要的模块导入

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.init as init
  4. import numpy as np
  5. #import matplotlib.pylab as plt
  6. import sys
  7. import torchvision
  8. import torchvision.transforms as transforms

获取和读取数据

  1. batch_size = 256
  2. num_workers = 4 # 多进程同时读取
  3. def load_data(batch_size,num_workers):
  4. mnist_train = torchvision.datasets.FashionMNIST(root='/home/sc/disk/keepgoing/learn_pytorch/Datasets/FashionMNIST',
  5. train=True, download=True,
  6. transform=transforms.ToTensor())
  7. mnist_test = torchvision.datasets.FashionMNIST(root='/home/sc/disk/keepgoing/learn_pytorch/Datasets/FashionMNIST',
  8. train=False, download=True,
  9. transform=transforms.ToTensor())
  10. train_iter = torch.utils.data.DataLoader(
  11. mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
  12. test_iter = torch.utils.data.DataLoader(
  13. mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
  14. return train_iter,test_iter
  15. train_iter,test_iter = load_data(batch_size,num_workers)

模型定义及参数初始化

这里我们使用torch.nn中自带的实现. 由于后续要定义的损失函数nn.nn.CrossEntropyLoss中包含了softmax的操作,所以这里不再需要定义relu和softmax.

  1. class Net(nn.Module):
  2. def __init__(self,num_inputs, num_outputs, num_hiddens):
  3. super(Net,self).__init__()
  4. self.l1 = nn.Linear(num_inputs,num_hiddens)
  5. self.relu1 = nn.ReLU()
  6. self.l2 = nn.Linear(num_hiddens,num_outputs)
  7. def forward(self,X):
  8. X=X.view(X.shape[0],-1)
  9. o1 = self.relu1(self.l1(X))
  10. o2 = self.l2(o1)
  11. return o2
  12. def init_params(self):
  13. for param in self.parameters():
  14. #print(param.shape)
  15. init.normal_(param,mean=0,std=0.01)
  16. num_inputs, num_outputs, num_hiddens = 28*28,10,256
  17. net = Net(num_inputs,num_outputs,num_hiddens)
  18. net.init_params()

定义损失函数

  1. loss = nn.CrossEntropyLoss()

定义优化器

  1. optimizer = torch.optim.SGD(net.parameters(),lr=0.5)

训练模型

  1. def evaluate_accuracy(data_iter, net):
  2. acc_sum, n = 0.0, 0
  3. for X, y in data_iter:
  4. acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
  5. n += y.shape[0]
  6. return acc_sum / n
  7. num_epochs=5
  8. def train():
  9. for epoch in range(num_epochs):
  10. train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
  11. for X, y in train_iter:
  12. y_hat=net(X) #前向传播
  13. l = loss(y_hat,y).sum()#计算loss
  14. l.backward()#反向传播
  15. optimizer.step()#参数更新
  16. optimizer.zero_grad()#清空梯度
  17. train_l_sum += l.item()
  18. train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
  19. n += y.shape[0]
  20. test_acc = evaluate_accuracy(test_iter, net)
  21. print('epoch %d, loss %.4f, train_acc %.3f,test_acc %.3f'
  22. % (epoch + 1, train_l_sum / n, train_acc_sum/n, test_acc))
  23. train()

输出如下:

  1. epoch 1, loss 0.0031, train_acc 0.709test_acc 0.785
  2. epoch 2, loss 0.0019, train_acc 0.823test_acc 0.831
  3. epoch 3, loss 0.0016, train_acc 0.844test_acc 0.830
  4. epoch 4, loss 0.0015, train_acc 0.855test_acc 0.854
  5. epoch 5, loss 0.0014, train_acc 0.866test_acc 0.836

可以看到这里的loss相比我们自己实现的loss小了很多,是因为torch里在计算loss的时候求的是这个batch的平均loss.我们自己实现的损失函数并没有求平均.

从头学pytorch(五) 多层感知机及其实现的更多相关文章

  1. 从头学pytorch(一):数据操作

    跟着Dive-into-DL-PyTorch.pdf从头开始学pytorch,夯实基础. Tensor创建 创建未初始化的tensor import torch x = torch.empty(5,3 ...

  2. 从头学pytorch(七):dropout防止过拟合

    上一篇讲了防止过拟合的一种方式,权重衰减,也即在loss上加上一部分\(\frac{\lambda}{2n} \|\boldsymbol{w}\|^2\),从而使得w不至于过大,即不过分偏向某个特征. ...

  3. 从头学pytorch(十五):AlexNet

    AlexNet AlexNet是2012年提出的一个模型,并且赢得了ImageNet图像识别挑战赛的冠军.首次证明了由计算机自动学习到的特征可以超越手工设计的特征,对计算机视觉的研究有着极其重要的意义 ...

  4. 从头学pytorch(三) 线性回归

    关于什么是线性回归,不多做介绍了.可以参考我以前的博客https://www.cnblogs.com/sdu20112013/p/10186516.html 实现线性回归 分为以下几个部分: 生成数据 ...

  5. 从头学pytorch(六):权重衰减

    深度学习中常常会存在过拟合现象,比如当训练数据过少时,训练得到的模型很可能在训练集上表现非常好,但是在测试集上表现不好. 应对过拟合,可以通过数据增强,增大训练集数量.我们这里先不介绍数据增强,先从模 ...

  6. 从头学pytorch(二) 自动求梯度

    PyTorch提供的autograd包能够根据输⼊和前向传播过程⾃动构建计算图,并执⾏反向传播. Tensor Tensor的几个重要属性或方法 .requires_grad 设为true的话,ten ...

  7. 从头学pytorch(九):模型构造

    模型构造 nn.Module nn.Module是pytorch中提供的一个类,是所有神经网络模块的基类.我们自定义的模块要继承这个基类. import torch from torch import ...

  8. 从头学pytorch(十二):模型保存和加载

    模型读取和存储 总结下来,就是几个函数 torch.load()/torch.save() 通过python的pickle完成序列化与反序列化.完成内存<-->磁盘转换. Module.s ...

  9. 从头学pytorch(十七):网络中的网络NIN

    网络中的网络NIN 之前介绍的LeNet,AlexNet,VGG设计思路上的共同之处,是加宽(增加卷积层的输出的channel数量)和加深(增加卷积层的数量),再接全连接层做分类. NIN提出了一个不 ...

随机推荐

  1. 【css】用css巧妙实现移动端横向滑动展示功能

    前言:记得以前处理移动端横向滑动展示都是去用js去解决的,要用js进行蛮多处理,要算li的宽度,然后还要用js设置ul盒子的宽度,又要设置最大滑动距离,最小滑动距离等等.......但是现在发现用cs ...

  2. pycharm连接mysql5.7

    由于加密方式改变 需要在url后增加 ?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC

  3. 小议WebRTC拥塞控制算法:GCC介绍

    网络拥塞是基于IP协议的数据报交换网络中常见的一种网络传输问题,它对网络传输的质量有严重的影响,网络拥塞是导致网络吞吐降低,网络丢包等的主要原因之一,这些问题使得上层应用无法有效的利用网络带宽获得高质 ...

  4. 用python实现密码校验程序

    密码需要符合下面的要求: 8个字符以上,包含数字,大小写,开头不能为特殊字符. #! /usr/bin/pythonimport re password = str(input()) def lenO ...

  5. Android开发实战——记账本(2)

    开发日志(2)——Bean目录以及数据库 首先编写一些自己生成的数据进行测试,看一下能否显示在模拟器上.那前提就是先写出bean目录,这和之前学的Javaweb步骤差不多.bean目录有三个变量事件. ...

  6. Linux下制作和使用静态库和动态库

    概述 Linux操作系统支持的函数库分为静态库和动态库,动态库又称共享库.linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib. 静态函数库: 这类库的名字一般是libxxx. ...

  7. MySQL执行外部sql脚本文件的命令

    sql脚本是包含一到多个sql命令的sql语句,我们可以将这些sql脚本放在一个文本文件中(我们称之为“sql脚本文件”),然后通过相关的命令执行这个sql脚本文件.基本步骤如下:1.创建包含sql命 ...

  8. 【visio】数据可视化 - 链接到外部数据

    编辑数据的时候,纯粹一个一个地手动输入,效率过低,visio提供了连接外部数据的功能,以加快数据导入,该功能也同时能够针对外部数据的改变,更新visio内部数据. 1. 创建外部源数据 visio支持 ...

  9. CGMH:Constrained Sentence Generation by Metropolis-Hastings Sampling解读

    根据关键字生成句子: 读进关键字,随机选择处理手段(增删改)以及待处理word的位置,然后计算接受/拒绝概率,根据概率生成一个新的序列,再循环这一过程,循环次数是500,每次都将困惑度最低的生成句子放 ...

  10. 微信小程序UDP通信,注意点 接收 onMessage 收到的message是ArrayBuffer缓冲,不能直接输出,要另转String处理

    1.WXML 页面代码 <!--index.wxml--> <view class="container"> <view class="us ...