笔记来源于B站up主,@刘二大人
感谢老师和其他社区创作者
前面提到的二分类问题是简单的判断是/否,通过Linear Layer将多特征的输入进行线性变化,最终变为一个输出,然后使用sigmoid函数将其变为一个分布在0~1之间的概率分布y_hat,可以以0.5为分界,将y_hat的结果分为是(>0.5)/否(<0.5)
本次课程探讨多分类问题
以MNIST数据集为例,里面有手写数字0-9,共10个分类,那么随机输入一个手写数字,如何判断其是0-9中的哪一类呢?
输入层的数据经过Linear Layer进行线性变化,再经过Sigmoid层变为0-1之间的数值,但此时各个输出之前各自独立,我们希望他们的概率能同属于一个分布,所有P(y=i)≥0且所有的P(y=i)求和能等于1,那么所有概率中哪一类的概率最大,就可以说y_hat判断是哪一类,此时用到Softmax函数。
Softmax函数如下:
通过将Sigmoid输出的结果z求e的指数,使得所有的P都实现了P(y=i)≥0
又通过将所有求过e指数的结果累加作为分母,实现了所有P(y=i)求和等于1
具体例子说明:
假设输出经过某个激活函数后输出为[0.2, 0.1, -0.1],经过Softmax函数后变为[0.38, 0.34, 0.28]
合计概率为1,这三个输出结果就是一个分布,能够更方便的做损失函数。
对标签采用独热编码,即标签指示的分类项为1,其余全为0,采用损失函数NLLLoss计算损失。
如此一来就只会计算标签项与对应的y_hat的损失,其余项损失全为0;
如图所示为例子:
y_hat求出0.38表示第一项的概率最大,那么对应标签正好为第一项为1,计算
假设标签对应的是第二项,即0 1 0,那么计算
对比和可知,如果预测的越准确,那么loss值将会越小。
=================================================
由log曲线可知,当y_hat<0时,y_hat越大,-log(y_hat)越小
=================================================
交叉熵损失函数CrossEntropyLoss就是Softmax+log+NLLLoss
所以使用交叉熵损失的时候,网络最后一层的输出项无需采用激活函数
对MINIST数据集进行分类,代码如下:
# 多分类问题交叉熵损失
import torch
from torch.utils.data import DataLoader
from torchvision import transforms, datasets
batch_size = 64
# 对输入的图像进行PIL转Tensor,并进行归一化处理
transform = transforms.Compose([
transforms.ToTensor(),
# 归一化处理,(均值,方差) (输入-均值)/方差
transforms.Normalize(0.1307, 0.3081)
])
# 数据集准备
train_dataset = datasets.MNIST(root=r'H:\chenpytorch\data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_dataset = datasets.MNIST(root=r'H:\chenpytorch\data', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
# 设计网络模型
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.model = torch.nn.Sequential(
torch.nn.Linear(784, 512),
torch.nn.ReLU(),
torch.nn.Linear(512, 256),
torch.nn.ReLU(),
torch.nn.Linear(256, 128),
torch.nn.ReLU(),
torch.nn.Linear(128, 64),
torch.nn.ReLU(),
torch.nn.Linear(64, 10)
)
def forward(self, x):
# 使用view将图片进行变形
# 输入的图片是(batch_size,1,28,28),要将像素全部展开成一行,1*28*28=784
# (-1,784)值根据输入,将数据转为784行后自动计算batch_size,batch_size*1*28*28/784=batch_size
x = x.view(-1, 784)
x = self.model(x)
return x
model = Net()
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
# 定义训练函数
def train(epoch):
train_loss = 0.0
for batch_idx, data in enumerate(train_loader,0):
inputs, labels = data
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs,labels)
loss.backward()
optimizer.step()
train_loss += loss.item()
if batch_idx %300 == 299:
print('epoch:{} batch_idx:{} loss:{}'.format(epoch+1, batch_idx, train_loss/300))
train_loss = 0.0
def test():
correct = 0
total = 0
with torch.no_grad():
for data in test_loader:
images,labels = data
outputs = model(images)
_,predicted = torch.max(outputs.data, dim=1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('accuracy on test_dataset:{}'.format(correct/total))
if __name__ == '__main__':
for epoch in range(10):
train(epoch)
test()
结果:
部分代码解释:
一、
对于神经网络而言,希望输入的数据越小越好,希望的像素值能满足[0,1]分布,并且最好是均值为0方差为1的正态分布
transforms.Totensor()
将读入的PIL图像转化为Tensor类型的图像
PIL数据为WHC(宽 高 通道数),转为Tensor的CWH(通道 宽 高)
原来的像素为0-255,是三通道彩色的RGB图,压缩到像素分布为0-1的浮点数
transforms.Normalize()归一化处理
如果是Normalize((0.5,0.5,0.5),(0.5,0.5,0.5)),是归一化到【-1,1】
如果是Normalize(channel_mean, channel_std),才是均值为0,标准差为1的正态分布
# 对输入的图像进行PIL转Tensor,并进行归一化处理
transform = transforms.Compose([
transforms.ToTensor(),
# 归一化处理,(均值,方差) (输入-均值)/方差
transforms.Normalize(0.1307, 0.3081)
])
二、
x = x.view(-1, 784)
x = x.view(-1, 784) 将输入数据x转化为64行784列的矩阵
因为已知原来的像素为28*28,将它展为一条后即为784列,前面的-1求出来的是batch_size为64
view()的作用相当于numpy中的reshape,重新定义矩阵的形状
view中一个参数定为-1,代表动态调整这个维度上的元素个数,以保证元素的总数不变。
python中view()函数怎么用?_view函数-CSDN博客
三、
torch.max()的使用 output = torch.max(input, dim)
分别用下划线“_空”来接收outputs中最大的数据,用predicted接收最大数据对应的索引
因为outputs输出的就是y_hats预测值,是一个64(batch_size)行,10(class类别)列的矩阵,通过dim=1对行求最大值索引,可以得到预测可能性最大的类。
_, predicted = torch.max(outputs.data, dim=1)