目录 

感知机

缺点

多层感知机

结构

数据集 

目的

具体流程

代码

从零开始实现MLP代码

MLP的简洁实现代码



感知机

        感知机是一个线性模型,早期提出感知机的时候,是为了用他来解决二分类的问题。

定义如下:

多层感知机人脸识别 多层感知机原理_感知机

 其分类的原理很简单

  • wx+b>0时,激活函数(其实就是Relu)输出1,即预测当前样本为类1
  • 否则,激活函数输出0(或-1也行,都无所谓的,只要能区分开两个类就行),即预测当前样本为类0(或-1)

        其中,上述的向量x=[x1,x2,,,,xd]T(T代表矩阵的转置)代表一个样本的各个特征,向量w=[w1,w2,,,,wd]代表一个样本各个特征对应的权重。

        为了方便新来的朋友看懂,我再讲细一点,举个例子表述一下如何将一个样本放到感知机里面得到预测的类别。

比如:

        一个代表一个样本,假设有 身高、体重、名字 这三个特征,预测性别,规定男1,女0(二分类)。

  1. 那么这里的向量维度d=3 ,输入的向量x=[身高、体重、名字]T,向量w=[身高权重w1、体重权重w2、名字权重w3]。
  2. 计算wx+b得 out=w1*身高+w2体重+w3名字+b ,如果out>0,判断为男1,否则为女0。

        现在我们已经明白一个样本怎么通过感知机进行分类 ,那么关键是我们怎么训练得到合适的权重向量w和偏置b,这就要用到下面的公式更新了。

多层感知机人脸识别 多层感知机原理_多层感知机人脸识别_02

        其中y为当前样本的真实标签label(是男1是女0) ,那么xw为当前样本的输入和输入对应的权重,有如下情况:

  • 当out=<w,x>+b > 0时,根据激活函数得Relu(out)=1 ,说明我们预测当前样本为男人。但如果y*[<w,x>+b] <= 0 ,说明y<=0,那就说明真实label为女0。这就说明我们预测的和真实label不一样,预测错了,就需要通过w=w+yx 和 b=b+y分别更新w和b。
  • 同理,当out= <w,x>+b <= 0时, Relu(out)=0,但如果y*[<w,x>+b] <= 0,说明y不可能小于0,只有两类,所以y只能为1。也需要通过公式w=w+yx 和 b=b+y更新w和b。
  • y*[<w,x>+b] > 0,则说明预测正确。
  • 我们的算法训练到所有的样本都能被正确分类时才退出。

这就是感知机。

缺点

        但是他有很大的局限性,比如感知机是线性函数,那对于下面这种情况,他就始终没法划分正确。

多层感知机人脸识别 多层感知机原理_多层感知机_03

 为了解决这种问题,引入了多层感知机,来把感知机非线性化。

多层感知机

        既然一个感知机划的一条线不能很好地区分开XOR问题,那我就用两条感知机划的线不就可以了吗?这个思想就是感知机的核心,他相当于用多个感知机叠加变成了多层感知机MLP,具有非线性的特性。

        下图用了两个感知机划出来的线,最终分类的结果存在product这一行中,可以理解为对product上面两行取 同或 的操作,即相同取1,不同取0,当然1和0在这里是用+和-表示的。

多层感知机人脸识别 多层感知机原理_人工智能_04

        上面是两个感知机叠加产生的结果,那如果用更多的感知机叠加在一起不就可以实现更好的非线性了吗?没错,所以我们可以在输入层和输出层中间加一个隐藏层,隐藏层有几层由我们自己定义,每层有几个节点也由我们定义。

        直观理解,我们隐藏层越多,每层的节点数越多,那么非线性就越好,更能画出特别复杂的线,但同时可能会由于模型过于复杂产生过拟合。反之模型就越简单,可能会欠拟合。

结构

多层感知机人脸识别 多层感知机原理_人工智能_05

下面就来实践一下吧!

数据集 

使用fashion_mnist数据集,数据集长这样,共有10类标签和对应的图片

多层感知机人脸识别 多层感知机原理_人工智能_06

目的

使用训练集 训练一个多层感知机(MLP)去预测 测试集对应的label

具体流程

  1. 加载数据集
  2. 初始化各层节点数、权重和偏置值
  3. 定义激活函数,此处用Relu。因为Relu结构简单,就一个max(0,x)。其他几个激活函数有指数,计算代价较高。因此,如果不知道用什么激活函数,那就用Relu就行!
  4. 定义多层感知机网络net,包含两个隐藏层,即结构为:输入层->隐藏层1->隐藏层2->输出层
  5. 定义损失函数,这里用交叉熵损失函数。
  6. 定义优化器
  7. 开始训练

代码

从零开始实现MLP代码

"""
单隐藏层的MLP神经网络实现
"""
from d2l import torch as d2l
import torch
from torch import nn

# 取数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)

# 初始化参数
input_num, output_num, hidden_num = 784, 10, 500
W1 = nn.Parameter(torch.randn((input_num, hidden_num), requires_grad=True)*0.01)   #使用X@W1 + b1计算出隐藏层的输出,因此W1形状为(input,hidden)
b1 = nn.Parameter(torch.zeros((1,hidden_num), requires_grad=True))
W2 = nn.Parameter(torch.randn((hidden_num, output_num), requires_grad=True)*0.01)   #使用relu(H)@H + b2计算出隐藏层的输出,因此W1形状为(hidden,output)
b2 = nn.Parameter(torch.zeros((1,output_num), requires_grad=True))
params = [W1, b1, W2, b2]

# 定义激活函数(用来引入非线性,防止层数塌陷,常用relu,因为简单)
def relu(X):
    X_zeros = torch.zeros_like(X)
    return torch.max(X_zeros, X)

# 定义MLP多层感知机网络
def net(X):
    X = X.reshape((-1, input_num))
    H = X@W1 + b1
    return relu(H)@W2 + b2

# 定义损失函数(这里用交叉熵损失函数)
loss = nn.CrossEntropyLoss()

# 开始训练
epoch_nums = 10
lr = 0.1
# 定义优化器(这里使用小批量随机梯度下降)
trainer = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, epoch_nums, trainer)
d2l.plt.show()

MLP的简洁实现代码

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :深度学习入门
@File :MLP简洁实现.py
@Author :little_spice
@Date :2022/4/27 13:39
"""
from d2l import torch as d2l
import torch
from torch import nn

# 取数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)

# 定义网络结构
net = nn.Sequential(
    nn.Flatten(),
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 10)
)

# 初始化参数
def init_weights(model):
    if type(model)==nn.Linear:
        # print("-" * 100)
        # print("model: ", model)
        # print("model.weights: ", model.weight)
        nn.init.normal_(model.weight, std=0.1)
        # print("*"*100)
        # print("model: ", model)
        # print("model.weights: ", model.weight)
net.apply(init_weights)
# 定义损失函数(这里用交叉熵损失函数)
loss = nn.CrossEntropyLoss()

# 开始训练
epoch_nums = 10
lr = 0.1
# 定义优化器
trainer = torch.optim.SGD(net.parameters(), lr)
d2l.train_ch3(net, train_iter, test_iter, loss, epoch_nums, trainer)