当前位置: 首页 > article >正文

多层感知机 MLP

多层感知机 MLP

引言

多层感知机(Multi-Layer Perception)由感知机推广而来,最主要的特点是具有多个神经元层,因此也叫深度神经网络(DNN)。
在这里插入图片描述

感知机由两层神经元组成,输入层接收外界输入信号后传递给输出层,输出层是M-P神经元。也称为阈值逻辑单元(threshold logic unit)
一个感知机的模型示意图如下:
在这里插入图片描述

感知机只具有输入层和输出层,通过对输入信号进行加权,使用激活功能,产生输出。可以表示为
u = ∑ i = 1 n w i x i + b u = \sum_{i=1}^n w_ix_i + b u=i=1nwixi+b
y = s i g n ( u ) = { + 1 , u > 0 − 1 , u ≤ 0 y = sign(u) = \begin{cases} +1, \quad u>0 \\ -1, \quad u\leq0 \end{cases} y=sign(u)={+1,u>01,u0
很显然,感知机仅仅只是一个线性的模型,如果遇到非线性数据就很难进行分类(感知机无法解决异或问题),那么可以通过加深神经元的网络层次来进行拟合,理论上来说,多层网络可以拟合任何复杂的函数
在这里插入图片描述

人工智能奠基人之一的Marvin Minsky与1969年出版了《感知机》一书,书中指出,单层神经网络无法解决非线性问题,而多层神经网络的训练算法尚看不到希望,这个论断直接使神经网络研究进入了“冰河期”,这就是神经网络的第一次低谷。直到后来BP算法的走红,才掀起了神经网络的第二次高潮。

多层感知机能够解决异或问题主要是通过升维,原本的样本是二维的,但中间层有两个节点,每个样本通过中间层的结点计算得到两个值,那么就对原来的样本进行了升维操作。
在这里插入图片描述

采用多层感知机后:
在这里插入图片描述

随着维度的增高,原本线性不可分的样本,就可以变得可分。引用自知乎答主Yiwen

原理

在这里插入图片描述

通过矩阵 X ∈ R n × d \mathbf{X} \in \mathbb{R}^{n \times d} XRn×d
来表示 n n n个样本的小批量,
其中每个样本具有 d d d个输入特征。
对于具有 h h h个隐藏单元的单隐藏层多层感知机,
H ∈ R n × h \mathbf{H} \in \mathbb{R}^{n \times h} HRn×h表示隐藏层的输出,
称为隐藏表示(hidden representations)。
在数学或代码中, H \mathbf{H} H也被称为隐藏层变量(hidden-layer variable)
隐藏变量(hidden variable)。
因为隐藏层和输出层都是全连接的,
所以我们有隐藏层权重 W ( 1 ) ∈ R d × h \mathbf{W}^{(1)} \in \mathbb{R}^{d \times h} W(1)Rd×h
和隐藏层偏置 b ( 1 ) ∈ R 1 × h \mathbf{b}^{(1)} \in \mathbb{R}^{1 \times h} b(1)R1×h
以及输出层权重 W ( 2 ) ∈ R h × q \mathbf{W}^{(2)} \in \mathbb{R}^{h \times q} W(2)Rh×q
和输出层偏置 b ( 2 ) ∈ R 1 × q \mathbf{b}^{(2)} \in \mathbb{R}^{1 \times q} b(2)R1×q
形式上,我们按如下方式计算单隐藏层多层感知机的输出
O ∈ R n × q \mathbf{O} \in \mathbb{R}^{n \times q} ORn×q

H = X W ( 1 ) + b ( 1 ) , O = H W ( 2 ) + b ( 2 ) . \begin{aligned} \mathbf{H} & = \mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)}, \\ \mathbf{O} & = \mathbf{H}\mathbf{W}^{(2)} + \mathbf{b}^{(2)}. \end{aligned} HO=XW(1)+b(1),=HW(2)+b(2).

注意在添加隐藏层之后,模型现在需要跟踪和更新额外的参数。
可我们能从中得到什么好处呢?在上面定义的模型里,我们没有好处!
原因很简单:上面的隐藏单元由输入的仿射函数给出,
而输出(softmax操作前)只是隐藏单元的仿射函数。
仿射函数的仿射函数本身就是仿射函数,
但是我们之前的线性模型已经能够表示任何仿射函数。

我们可以证明这一等价性,即对于任意权重值,
我们只需合并隐藏层,便可产生具有参数
W = W ( 1 ) W ( 2 ) \mathbf{W} = \mathbf{W}^{(1)}\mathbf{W}^{(2)} W=W(1)W(2)
b = b ( 1 ) W ( 2 ) + b ( 2 ) \mathbf{b} = \mathbf{b}^{(1)} \mathbf{W}^{(2)} + \mathbf{b}^{(2)} b=b(1)W(2)+b(2)
的等价单层模型:

O = ( X W ( 1 ) + b ( 1 ) ) W ( 2 ) + b ( 2 ) = X W ( 1 ) W ( 2 ) + b ( 1 ) W ( 2 ) + b ( 2 ) = X W + b . \mathbf{O} = (\mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)})\mathbf{W}^{(2)} + \mathbf{b}^{(2)} = \mathbf{X} \mathbf{W}^{(1)}\mathbf{W}^{(2)} + \mathbf{b}^{(1)} \mathbf{W}^{(2)} + \mathbf{b}^{(2)} = \mathbf{X} \mathbf{W} + \mathbf{b}. O=(XW(1)+b(1))W(2)+b(2)=XW(1)W(2)+b(1)W(2)+b(2)=XW+b.

为了发挥多层架构的潜力,
我们还需要一个额外的关键要素:
在仿射变换之后对每个隐藏单元应用非线性的激活函数(activation function) σ \sigma σ
激活函数的输出(例如, σ ( ⋅ ) \sigma(\cdot) σ())被称为活性值(activations)。
一般来说,有了激活函数,就不可能再将我们的多层感知机退化成线性模型:

H = σ ( X W ( 1 ) + b ( 1 ) ) , O = H W ( 2 ) + b ( 2 ) . \begin{aligned} \mathbf{H} & = \sigma(\mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)}), \\ \mathbf{O} & = \mathbf{H}\mathbf{W}^{(2)} + \mathbf{b}^{(2)}.\\ \end{aligned} HO=σ(XW(1)+b(1)),=HW(2)+b(2).

由于 X \mathbf{X} X中的每一行对应于小批量中的一个样本,
出于记号习惯的考量,
我们定义非线性函数 σ \sigma σ也以按行的方式作用于其输入,
即一次计算一个样本。

为了构建更通用的多层感知机,
我们可以继续堆叠这样的隐藏层,
例如 H ( 1 ) = σ 1 ( X W ( 1 ) + b ( 1 ) ) \mathbf{H}^{(1)} = \sigma_1(\mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)}) H(1)=σ1(XW(1)+b(1)) H ( 2 ) = σ 2 ( H ( 1 ) W ( 2 ) + b ( 2 ) ) \mathbf{H}^{(2)} = \sigma_2(\mathbf{H}^{(1)} \mathbf{W}^{(2)} + \mathbf{b}^{(2)}) H(2)=σ2(H(1)W(2)+b(2))
一层叠一层,从而产生更有表达能力的模型。

通用近似定理

多层感知机可以通过隐藏神经元,捕捉到输入之间复杂的相互作用,
这些神经元依赖于每个输入的值。
我们可以很容易地设计隐藏节点来执行任意计算。
例如,在一对输入上进行基本逻辑操作,多层感知机是通用近似器。
即使是网络只有一个隐藏层,给定足够的神经元和正确的权重,
我们可以对任意函数建模,尽管实际中学习该函数是很困难的。
神经网络有点像C语言。
C语言和任何其他现代编程语言一样,能够表达任何可计算的程序。
但实际上,想出一个符合规范的程序才是最困难的部分。

而且,虽然一个单隐层网络能学习任何函数,
但并不意味着我们应该尝试使用单隐藏层网络来解决所有问题。
事实上,通过使用更深(而不是更广)的网络,我们可以更容易地逼近许多函数。

算法优化原理

针对样本,误差可以定义为
L o s s = ( y − y ^ ) 2 Loss = (y-\hat y )^2 Loss=(yy^)2
带入 y ^ \hat y y^
L o s s = ( y − ( W T X + b ) ) 2 Loss = (y - (W^TX + b))^2 Loss=(y(WTX+b))2
如果训练集中有N个样本,需要将误差累加,并使得误差最小化
m i n ∑ i = 1 N L o s s i min \sum_{i=1}^N Loss_i mini=1NLossi
待优化变量为W,b,为无约束问题,可以通过梯度下降算法进行求解。
基本迭代公式为:
θ i = θ i − α ∂ ∂ θ i J ( θ 0 , θ 1 . . . , θ n ) \theta_i = \theta_i - \alpha\frac{\partial}{\partial \theta_i}J(\theta_0,\theta_1...,\theta_n) θi=θiαθiJ(θ0,θ1...,θn)

代码

import torch
from torchvision import datasets
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F

#一次取出的训练样本数
batch_size = 16
# epoch 的数目
n_epochs = 10
# 模型保存位置
path = "./saved_model/MLP.bin"

#读取数据
train_data = datasets.MNIST(root="./data", train=True, download=True,transform=transforms.ToTensor())
test_data = datasets.MNIST(root="./data", train=False, download=True, transform=transforms.ToTensor())
#创建数据加载器
train_loader = torch.utils.data.DataLoader(train_data, batch_size = batch_size, num_workers = 0)
test_loader = torch.utils.data.DataLoader(test_data, batch_size = batch_size, num_workers = 0)

class MLP(nn.Module):
    def __init__(self):
        #继承自父类
        super(MLP, self).__init__()
        #创建一个三层的网络
        #输入的28*28为图片大小,输出的10为数字的类别数
        hidden_first = 512
        hidden_second = 512
        self.first = nn.Linear(in_features=28*28, out_features=hidden_first)
        self.second = nn.Linear(in_features=hidden_first, out_features=hidden_second)
        self.third = nn.Linear(in_features=hidden_second, out_features=10)

    def forward(self, data):
        #先将图片数据转化为1*784的张量
        data = data.view(-1, 28*28)
        data = F.relu(self.first(data))
        data = F.relu((self.second(data)))
        data = F.log_softmax(self.third(data), dim = 1)

        return data

def train():
    # 定义损失函数和优化器
    lossfunc = torch.nn.CrossEntropyLoss().cuda()
    #lossfunc = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(params=model.parameters(), lr=0.01)
    # 开始训练
    for epoch in range(n_epochs):
        train_loss = 0.0
        for data, target in train_loader:
            optimizer.zero_grad()
            #将数据放至GPU并计算输出
            data = data.cuda()
            target = target.cuda()
            output = model(data)
            #计算误差
            loss = lossfunc(output, target)
            #反向传播
            loss.backward()
            #将参数更新至网络中
            optimizer.step()
            #计算误差
            train_loss += loss.item() * data.size(0)
        train_loss = train_loss / len(train_loader.dataset)
        print('Epoch:  {}  \tTraining Loss: {:.6f}'.format(epoch + 1, train_loss))
        # 每遍历一遍数据集,测试一下准确率
        test()
    #最后将模型保存
    torch.save(model, path)

#测试程序
def test():
    correct = 0
    total = 0
    with torch.no_grad():  # 训练集中不需要反向传播
        for data in test_loader:
            images, labels = data
            #将数据放到GPU上
            images = images.cuda()
            labels = labels.cuda()
            output = model(images)
            _, predicted = torch.max(output.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    print('Accuracy of the network on the test images: %d %%' % (
            100 * correct / total))
    return 100.0 * correct / total

try:
    model = torch.load(path).cuda()
    test()
except FileNotFoundError:
    model = MLP()
    # 将模型放到GPU上
    model = model.cuda()
    train()

参考

AD稳稳CSDN博客

柚子味的羊CSDN博客

知乎博主我在开水团做运筹

知乎博主HappyWang

李沐——动手学深度学习

code


http://www.kler.cn/news/353542.html

相关文章:

  • 【优选算法篇】编织算法的流动诗篇:滑动窗口的轻盈之美
  • Golang | Leetcode Golang题解之第477题汉明距离总和
  • mqtt客户端订阅一直重复连接?
  • 详解SSH和bash
  • 【Linux】嵌入式Linux系统的组成、u-boot编译
  • 灵当CRM data/pdf.php 任意文件读取漏洞复现
  • 计算机网络(五)—— 运输层
  • Springboot +Mybatis项目用log4j2打印SQL语句
  • 推动AI技术研发与应用,景联文科技提供专业高效图像采集服务
  • Spring Cloud 组件的使用
  • 新手爬虫DAY1
  • exchange邮件系统ADFS双因素认证技术方案
  • [移植] tgi 编译
  • element-ui的树形结构样式调整,添加线条和边框样式
  • Git中从dev分支恢复master分支
  • 爬虫逆向学习(十二):一个案例入门补环境
  • perl文件测试操作符及其意义
  • 线性代数知识学习
  • JAVA 中的 String 类
  • 【C++笔记】类和对象(上)