目录
感知机
缺点
多层感知机
结构
数据集
目的
具体流程
代码
从零开始实现MLP代码
MLP的简洁实现代码
感知机
感知机是一个线性模型,早期提出感知机的时候,是为了用他来解决二分类的问题。
定义如下:
其分类的原理很简单
- 当wx+b>0时,激活函数(其实就是Relu)输出1,即预测当前样本为类1
- 否则,激活函数输出0(或-1也行,都无所谓的,只要能区分开两个类就行),即预测当前样本为类0(或-1)
其中,上述的向量x=[x1,x2,,,,xd]T(T代表矩阵的转置)代表一个样本的各个特征,向量w=[w1,w2,,,,wd]代表一个样本各个特征对应的权重。
为了方便新来的朋友看懂,我再讲细一点,举个例子表述一下如何将一个样本放到感知机里面得到预测的类别。
比如:
一个人代表一个样本,假设有 身高、体重、名字 这三个特征,预测性别,规定男1,女0(二分类)。
- 那么这里的向量维度d=3 ,输入的向量x=[身高、体重、名字]T,向量w=[身高权重w1、体重权重w2、名字权重w3]。
- 计算wx+b得 out=w1*身高+w2体重+w3名字+b ,如果out>0,判断为男1,否则为女0。
现在我们已经明白一个样本怎么通过感知机进行分类 ,那么关键是我们怎么训练得到合适的权重向量w和偏置b,这就要用到下面的公式更新了。
其中y为当前样本的真实标签label(是男1是女0) ,那么x和w为当前样本的输入和输入对应的权重,有如下情况:
- 当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,则说明预测正确。
- 我们的算法训练到所有的样本都能被正确分类时才退出。
这就是感知机。
缺点
但是他有很大的局限性,比如感知机是线性函数,那对于下面这种情况,他就始终没法划分正确。
为了解决这种问题,引入了多层感知机,来把感知机非线性化。
多层感知机
既然一个感知机划的一条线不能很好地区分开XOR问题,那我就用两条感知机划的线不就可以了吗?这个思想就是感知机的核心,他相当于用多个感知机叠加变成了多层感知机MLP,具有非线性的特性。
下图用了两个感知机划出来的线,最终分类的结果存在product这一行中,可以理解为对product上面两行取 同或 的操作,即相同取1,不同取0,当然1和0在这里是用+和-表示的。
上面是两个感知机叠加产生的结果,那如果用更多的感知机叠加在一起不就可以实现更好的非线性了吗?没错,所以我们可以在输入层和输出层中间加一个隐藏层,隐藏层有几层由我们自己定义,每层有几个节点也由我们定义。
直观理解,我们隐藏层越多,每层的节点数越多,那么非线性就越好,更能画出特别复杂的线,但同时可能会由于模型过于复杂产生过拟合。反之模型就越简单,可能会欠拟合。
结构
下面就来实践一下吧!
数据集
使用fashion_mnist数据集,数据集长这样,共有10类标签和对应的图片
目的
使用训练集 训练一个多层感知机(MLP)去预测 测试集对应的label
具体流程
- 加载数据集
- 初始化各层节点数、权重和偏置值
- 定义激活函数,此处用Relu。因为Relu结构简单,就一个max(0,x)。其他几个激活函数有指数,计算代价较高。因此,如果不知道用什么激活函数,那就用Relu就行!
- 定义多层感知机网络net,包含两个隐藏层,即结构为:输入层->隐藏层1->隐藏层2->输出层
- 定义损失函数,这里用交叉熵损失函数。
- 定义优化器
- 开始训练
代码
从零开始实现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)