目录
1 Softmax层介绍
2 选择Loss函数
2.1 NLLLoss损失函数
2.2 CrossEntropyLoss损失函数
3 MNIST训练实例代码详解
3.1 准备数据集
3.2 建立模型
3.3 构建损失函数和优化器
3.4 训练+测试
3.5 完整代码+运行结果
4 遇到问题
本节课以深度学习经典数据集MNIST数据集为例展开,如图所示是MNIST数据集的一张样例图,每个方框中是手写的数字。而我们需要做的事情就是:根据数据集中的数据,利用深度学习,让机器能够自己识别每个方框中的数字。
数字一共有0~9一共10种类别,利用深度学习来识别十种数字的类别,这就是多分类问题。
之前在糖尿病数据集的学习中,我们遇到的是二分类问题,也就是病人一共只有患病(y=1)和不患病(y=0)两种分类,知道y=1的概率,那y=0的概率就等于1-P(y=1),比较两个概率的大小,很容易就可以区分该病人属于哪一个分类。
而多分类问题不同,以MNIST数据集为例,需要计算出每个方框属于不同数字的10个概率,这10个概率需要满足:
因此多分类问题我们通常选用Softmax函数作为神经网络的最后一层。
Softmax函数公式如下:其中 表示的是线性层输出的数值;对每层的线性层求指数,之后再计算每层指数值占总层数指数值之和的比例,就是计算出的该层次的概率。
注意:
一个简单的例子如下图所示:
NLLLoss损失函数如下图所示:( 最大的对应的 值为1,其余都为0.)
对应的损失函数代码如下所示:
import numpy as np
y = np.array([1, 0, 0])
z = np.array([0.2, 0.1, -0.1])
y_pred = np.exp(z) / np.exp(z).sum()
loss = (- y * np.log(y_pred)).sum()
print(loss)
CrossEntropyLoss损失函数是将Softmax层和NLLLoss损失函数整合在一。CrossEntropyLoss损失函数如下图所示:
注意:使用CrossEntropyLoss损失函数时,神经网络的最后一层不需要做激活,(也就是不用经过Softmax层的计算),直接输入到CrossEntropyLoss损失函数中就可以,因为CrossEntropyLoss损失函数包含的Sofrmax层。
对应的损失函数代码如下所示:
import torch
y = torch.LongTensor([0])
z = torch.Tensor([[0.2, 0.1, -0.1]])
criterion = torch.nn.CrossEntropyLoss()
loss = criterion(z, y)
print(loss)
我们之前学习的案例中,输入x都是一个向量;在MNIST数据集中,我们需要输入的是一个图像,怎样,图像怎么才能输入到模型中进行训练呢?一种方法是我们可以把图像映射成一个矩向量,再输入到模型中进行训练。
怎样将一个图像映射成一个向量?
如图所示是MNIST数据集中一个方格的图像,它是由28x28=784个像素组成,其中越深的地方数值越接近0,越亮的地方数值越接近1。
因此可以将此图像按照对应的像素和数值映射成一个28x28的一个矩阵,如下图所示:
具体代码如下:
# 准备数据集
batch_size = 64
transform = transforms.Compose([
transforms.ToTensor(),
#均值、标准差
transforms.Normalize((0.1307, ), (0.3081, ))
])
train_dataset = datasets.MNIST(root='../dataset/mnist/',
train=True,
download=True,
transform=transform)
train_loader = DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist',
train=False,
download=True,
transform=transform)
test_loader = DataLoader(test_dataset,
shuffle=False,
batch_size=batch_size)
注:
全连接网络中,要求输入的是一个矩阵,因此需要将1x28x28的这个三阶的张量变成一个一阶的向量,因此将图像的每一行的向量横着拼起来变成一串,这样就变成了一个维度为1x784的向量,一共输入N个手写数图,因此,输入矩阵维度为(N,784)。这样就可以设计我们的模型,如下图所示:
具体代码:
# 设计模型
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.l1 = torch.nn.Linear(784, 512)
self.l2 = torch.nn.Linear(512, 256)
self.l3 = torch.nn.Linear(256, 128)
self.l4 = torch.nn.Linear(128, 64)
self.l5 = torch.nn.Linear(64, 10)
def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.l1(x))
x = F.relu(self.l2(x))
x = F.relu(self.l3(x))
x = F.relu(self.l4(x))
return self.l5(x)
model = Net()
具体代码:
# 构建损失函数和优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
具体代码:
# 定义训练函数
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
# 前馈+反馈+更新
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
# 每300次迭代输出一次
if batch_idx % 300 == 299:
print('[%d,%5d] loss:%.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
running_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 set:%d %%' % (100 * correct / total))
# 实例化训练和测试
if __name__ == '__main__':
# 训练10轮
for epoch in range(10):
train(epoch)
test()
完整代码:
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
# 准备数据集
batch_size = 64
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))
])
train_dataset = datasets.MNIST(root='../dataset/mnist/',
train=True,
download=True,
transform=transform)
train_loader = DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist',
train=False,
download=True,
transform=transform)
test_loader = DataLoader(test_dataset,
shuffle=False,
batch_size=batch_size)
# 设计模型
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.l1 = torch.nn.Linear(784, 512)
self.l2 = torch.nn.Linear(512, 256)
self.l3 = torch.nn.Linear(256, 128)
self.l4 = torch.nn.Linear(128, 64)
self.l5 = torch.nn.Linear(64, 10)
def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.l1(x))
x = F.relu(self.l2(x))
x = F.relu(self.l3(x))
x = F.relu(self.l4(x))
return self.l5(x)
model = Net()
# 构建损失函数和优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
# 定义训练函数
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
# 前馈+反馈+更新
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
if batch_idx % 300 == 299:
print('[%d,%5d] loss:%.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
running_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 set:%d %%' % (100 * correct / total))
# 实例化训练和测试
if __name__ == '__main__':
for epoch in range(10):
train(epoch)
test()
运行截图如下:
运行时遇到警告:The given NumPy array is not writeable,and PyTorch does not support non-writeable tensor,如图:
按照路径找到mnist.py文件:
点开修改:删除copy+False,就没有报错,程序可以继续运行了