目录
1.感知机
2.多层感知机
3.代码实现
3.1多层感知机的从0开始实现
3.2多层感知机的简洁实现
感知机其实就是2分类的问题
(1)yi是个标号(+1,-1),小于0表感知机把分类样本预测错了,因为如果是大于0的,意味着要分类成一个正类,假设yi是+1的,那么乘积就是一个大于等于0的数,若乘积小于0说明yi是-1,表示分类分错了。(yi为真实标号,为预测标号)。
(标号乘以样本作权重更新,偏差等于偏差+标号)。由感知机的损失公式:
,可以看出yixi是损失对w求偏导得出的,而yi是损失对偏差b求偏导得出的。
(3)重复(1),(2)直到所有的类都分类正确。
(4)等价于使用批量大小为1的梯度下降,含义是每次拿一个样本算梯度然后更新,没有用随机梯度下降是因为感知机最原始的模型就是,感知机一遍一遍的扫数据没有说要随机去弄。感知机的损失函数等于。max()其实就对应了感知机训练过程的if语句,因为如果分类正确的话,这一项是大于0的,前面再加个符号整个第二项的参数就为负,所以max()取的是0,损失函数为0,梯度是常数就不再更新参数了。就对应上面的if语句不成立,如果分类错误就要进入if语句更新参数。
理解一下收敛:r看作是数据的大小,也就是说当r很大时,数据的收敛就会变慢,还有个因素就是,表示数据是不是很好,很好的意思是是否能把两个点真的分的特别开(也是余量较大的含义),分得很开的话很快就收敛了,如果分隔面特别小,那么找到这个面就会花很多时间。
感知机不能拟合xor(异或)函数,如上图紫色是坐标轴,当x和y同号时,同红点表示,当x和y异号时用绿点表示,蓝色是分割线,始终不能把红点和绿点分开。感知机只能产生线性分隔面。
解释一下:蓝色分隔面就是把x分成了正和负,黄色分割线就是把y分成了正和负,根据1,2,3,4个点的位置,整理成表格。同号得正,异号得负。对这四个点作分类的问题就可以看作右下角的流程图,把每个点分别经过两个感知机就能得到正确的结果,就是组合几个函数,一层感知机就变成了多层感知机。这就是多层感知机做的事情。
输出就是有多少类了,我们能做的就是设置隐藏层有多大。
输入是一个n维的向量,假设隐藏层的大小是m,权重就是m*n的矩阵,偏移就是有多少个隐藏层就有多少个标量偏移,长为m的向量。因为是单分类的问题所以只输出一个,那么输出层就是一个长为m的向量(这里单分类的时候把图中o2,o3去掉来看就明白了),h是一个长为m的向量,作为输入进入到输出层,再经过权重和偏置的运算得到的输出是一个标量。
为什么需要非线性激活函数?
答:因为加入激活函数是线性的,输出层输出的结果仍然是线性的,那么多层感知机就等价于单层的感知机(这也是我们常犯的错误)。
sigmoid函数就是根据x的值投影到(0,1)开区间。分母做的事情就是exp把x变成一个正数,然后1加正数分支1一定是在(0,1)区间里的一个值。
tanh就是根据输入投影到(-1,1)的开区间。
ReLU函数就是把x,0求最大值。x的导数是+1,0的导数是0。
softmax做的事情就是把所有的输入拉到0和1 之间的区域,使得y1+...+yk=1。多分类问题就是在softmax回归里面加了一层隐藏层。
与单分类问题不同的是输出层的输出变成了K个,因为输出要有k个单元。所以w2就是(m*k)的矩阵,偏移是一个长为k的向量。因此输出o就变成了一个向量,不再是标量了。
记住激活函数不能少,少了就等于这层没有作用,相当于隐藏层的层数减1。输出不需要激活函数。超参数的配置很重要。比如,输入的数据很复杂的情况下,第一个选择就是用单隐藏层,把m1设的大一点,输入为维度128,隐藏层做64,128,256都行。第二个选择是把模型做的深一点,增加m2,m3。相对于单隐藏层的m1很大,做深的话,m1就不要太大,m2,m3大小递减。比如128到64到32到8依次递减。但是第一个隐藏层稍微胖一点也没关系,比如128的输入先做到256再慢慢缩回去,比如图上4个输入,5个隐藏层。要慢慢压缩,不然损失的信息太多最后难以还原。
import torch
from torch import nn
from d2l import torch as d2l
batch_size = 256
#批量大小等于256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
"""初始化模型参数"""
num_inputs, num_outputs, num_hiddens = 784, 10, 256
#假设输入784(28*28的图片),输出是10,输入输出不可改,隐藏层是256
W1 = nn.Parameter(torch.randn(
num_inputs, num_hiddens, requires_grad=True) * 0.01)
#nn.Parameter声明是torch的一个参数(不加也没关系),第一个隐藏层是行数784,列数256,
#因为要更新所以需要梯度,torch.randn()返回返回一个包含了从标准正态分布中抽取的一组
#随机数的张量乘0.01我的理解:randn是正态(0,1)分布,乘0.01使得分布为正态(0,0.1)分布,
#数据方差更小,把方差变为了0.01
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
#偏差就是隐藏层的个数,是一个向量,偏差设成0
W2 = nn.Parameter(torch.randn(
num_hiddens, num_outputs, requires_grad=True) * 0.01)
#第二个隐藏层的输入就是256(前一个隐藏层的输出),输出是10,需要梯度。
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
#偏置是10,因为要分10类
params = [W1, b1, W2, b2]
"""激活函数"""
def relu(X):
a = torch.zeros_like(X)
return torch.max(X, a)
#zeros_like函数使得数据类型和形状都和X一样但是值为0
#torch.zeros_like() 是 PyTorch 中的一个函数,它返回一个与给定输入张量形状和数据类型
#相同,但所有元素都被设置为零的新张量。torch.zeros_like(input, dtype=None, layout=None,
# device=None, requires_grad=False)
"""实现模型"""
def net(X):
X = X.reshape((-1, num_inputs))
#使X变成一个2维的矩阵,因为输入是784,-1代表计算得出的batchsize256
H = relu(X@W1 + b1) # 这里“@”代表矩阵乘法
#根据理论公式转不转置取决于x是怎么创建的,因为这里的x是256*784大小,而w(784*256)
#所以不用转置
return (H@W2 + b2)
"""损失函数"""
loss = nn.CrossEntropyLoss(reduction='none')
#损失函数是交叉熵损失,当reduction='none'时,函数会输出一个形状为(batch_size, num_classes)
#的矩阵,表示每个样本的每个类别的损失。nn.CrossEntropyLoss() 内置了softmax操作,因此input
#只需要是网络输出的logits即可,不需要自己用softmax进行归一化,也不需要保证是正数。
"""训练"""
num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
#经过训练发现现在的损失较之前的只用softmax相比,损失已经降到0.4以下了。精度肉眼看不出来变化
"""预测"""
#在一些测试数据上应用这个模型
d2l.predict_ch3(net, test_iter)
(以上是多层感知机的分类)
(以上是多层感知机的分类预测结果)
(以上是softmax分类结果)
import torch
from torch import nn
from d2l import torch as d2l
"""模型"""
#隐藏层,它包含256个隐藏单元,并使用了ReLU激活函数。
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights);
"""训练"""
batch_size, lr, num_epochs = 256, 0.1, 10
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=lr)
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
补充一点感知机的损失:
感知机的损失函数只计算被误分类的点,意味着yi与(w.xi+b)是异号的,所以损失L一定大于等于0。(w.xi+b)表征了误分类点到超平面的距离
感知机的目的就是求一个L等于0的超平面,也就是使得损失函数最小的超平面
参考:
torch.zeros_like有什么作用_torch.zero_like-CSDN博客