深度学习与Pytorch入门实战(六)定义MLP&GPU加速&测试【数字识别实例】 1. nn.Linear方式 2. 继承nn.Module方式 3. GPU加速 4. 多分类测试

笔记摘抄

  • Pytorch定义网络结构识别手写数字,可以对网络中的参数w和b进行手动定义的(参考上一节)

  • 也可以直接用 nn.Linear 定义层的方式来定义

  • 更加方便的方式是直接继承 nn.Module 来定义自己的网络结构。

import torch
import  torch.nn as nn
import  torch.nn.functional as F

# 模拟 一张 28*28 的图片摊平
x = torch.randn(1, 784)            #shape=[1,784]

# 定义三个全连接层
layer1 = nn.Linear(784, 200)         # (in, out)
layer2 = nn.Linear(200, 200)
layer3 = nn.Linear(200, 10)

x = layer1(x)                        # shape=[1,200]
x = F.relu(x, inplace=True)          # inplace=True在原对象基础上修改,可以节省内存
print(x.shape)

x = layer2(x)                        # shape=[1,200]
x = F.relu(x, inplace=True)

x = layer3(x)                        # shape=[1,10]
x = F.relu(x, inplace=True)

2. 继承nn.Module方式

import  torch
import  torch.nn as nn
import  torch.nn.functional as F
import  torch.optim as optim
from    torchvision import datasets, transforms

#超参数
batch_size=200
learning_rate=0.01
epochs=10

#获取训练数据
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=True, download=True,          #train=True则得到的是训练集
                   transform=transforms.Compose([                 #transform进行数据预处理
                       transforms.ToTensor(),                     #转成Tensor类型的数据
                       transforms.Normalize((0.1307,), (0.3081,)) #进行数据标准化(减去均值除以方差)
                   ])),
    batch_size=batch_size, shuffle=True)                          #按batch_size分出一个batch维度在最前面,shuffle=True打乱顺序

#获取测试数据
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=False, transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])),
    batch_size=batch_size, shuffle=True)


class MLP(nn.Module):

    def __init__(self):
        super(MLP, self).__init__()
        
        # 定义网络的每一层,nn.ReLU可以换成其他激活函数,比如nn.LeakyReLU()
        self.model = nn.Sequential(     
            nn.Linear(784, 200),
            nn.ReLU(inplace=True),
            nn.Linear(200, 200),
            nn.ReLU(inplace=True),
            nn.Linear(200, 10),
            nn.ReLU(inplace=True),
        )

    def forward(self, x):
        x = self.model(x)
        return x

net = MLP()
# 定义sgd优化器,指明优化参数、学习率
# net.parameters()得到这个类所定义的网络的参数[[w1,b1,w2,b2,...]
optimizer = optim.SGD(net.parameters(), lr=learning_rate)
criteon = nn.CrossEntropyLoss()

for epoch in range(epochs):

    for batch_idx, (data, target) in enumerate(train_loader):
        data = data.view(-1, 28*28)          # 将二维的图片数据摊平[样本数,784]

        logits = net(data)                   # 前向传播
        loss = criteon(logits, target)       # nn.CrossEntropyLoss()自带Softmax

        optimizer.zero_grad()                # 梯度信息清空
        loss.backward()                      # 反向传播获取梯度
        optimizer.step()                     # 优化器更新

        if batch_idx % 100 == 0:             # 每100个batch输出一次信息
            print('Train Epoch: {} [{}/{} ({:.0f}%)]	Loss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))


    test_loss = 0
    correct = 0                                         # correct记录正确分类的样本数
    for data, target in test_loader:
        data = data.view(-1, 28 * 28)
        logits = net(data)
        test_loss += criteon(logits, target).item()     # 其实就是criteon(logits, target)的值,标量

        pred = logits.data.max(dim=1)[1]                # 也可以写成pred=logits.argmax(dim=1)
        correct += pred.eq(target.data).sum()

    test_loss /= len(test_loader.dataset)
    print('
Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)
'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
view result
Train Epoch: 0 [0/60000 (0%)] Loss: 2.307840
Train Epoch: 0 [20000/60000 (33%)] Loss: 2.022810
Train Epoch: 0 [40000/60000 (67%)] Loss: 1.342542

Test set: Average loss: 0.0038, Accuracy: 8374/10000 (84%)

Train Epoch: 1 [0/60000 (0%)] Loss: 0.802759
Train Epoch: 1 [20000/60000 (33%)] Loss: 0.627895
Train Epoch: 1 [40000/60000 (67%)] Loss: 0.482087

Test set: Average loss: 0.0020, Accuracy: 8926/10000 (89%)

Train Epoch: 2 [0/60000 (0%)] Loss: 0.496279
Train Epoch: 2 [20000/60000 (33%)] Loss: 0.420009
Train Epoch: 2 [40000/60000 (67%)] Loss: 0.429296

Test set: Average loss: 0.0017, Accuracy: 9069/10000 (91%)

Train Epoch: 3 [0/60000 (0%)] Loss: 0.304612
Train Epoch: 3 [20000/60000 (33%)] Loss: 0.356296
Train Epoch: 3 [40000/60000 (67%)] Loss: 0.405541

Test set: Average loss: 0.0015, Accuracy: 9149/10000 (91%)

Train Epoch: 4 [0/60000 (0%)] Loss: 0.304062
Train Epoch: 4 [20000/60000 (33%)] Loss: 0.406027
Train Epoch: 4 [40000/60000 (67%)] Loss: 0.385962

Test set: Average loss: 0.0014, Accuracy: 9201/10000 (92%)

Train Epoch: 5 [0/60000 (0%)] Loss: 0.186269
Train Epoch: 5 [20000/60000 (33%)] Loss: 0.196249
Train Epoch: 5 [40000/60000 (67%)] Loss: 0.228671

Test set: Average loss: 0.0013, Accuracy: 9248/10000 (92%)

Train Epoch: 6 [0/60000 (0%)] Loss: 0.364886
Train Epoch: 6 [20000/60000 (33%)] Loss: 0.295816
Train Epoch: 6 [40000/60000 (67%)] Loss: 0.244240

Test set: Average loss: 0.0012, Accuracy: 9290/10000 (93%)

Train Epoch: 7 [0/60000 (0%)] Loss: 0.228807
Train Epoch: 7 [20000/60000 (33%)] Loss: 0.192547
Train Epoch: 7 [40000/60000 (67%)] Loss: 0.223399

Test set: Average loss: 0.0012, Accuracy: 9329/10000 (93%)

Train Epoch: 8 [0/60000 (0%)] Loss: 0.176273
Train Epoch: 8 [20000/60000 (33%)] Loss: 0.346954
Train Epoch: 8 [40000/60000 (67%)] Loss: 0.253838

Test set: Average loss: 0.0011, Accuracy: 9359/10000 (94%)

Train Epoch: 9 [0/60000 (0%)] Loss: 0.246411
Train Epoch: 9 [20000/60000 (33%)] Loss: 0.201452
Train Epoch: 9 [40000/60000 (67%)] Loss: 0.162228

Test set: Average loss: 0.0011, Accuracy: 9377/10000 (94%)

区别nn.ReLU()F.relu()

  • nn.ReLU()是类风格的API(大写开头,必须 先实例化再调用;参数w、b是内部成员,通过.parameters来访问)

  • F.relu()是函数风格的API(自己管理)

import  torch
import  torch.nn as nn
import  torch.nn.functional as F

x = torch.randn(1,10)

# 方法一
# F.relu: 直接调用函数 并传入参数
print(F.relu(x, inplace=True))   
# tensor([[0.2846, 0.6158, 0.0000, 0.0000, 0.0000, 1.7980, 0.6466, 0.4263, 0.0000, 0.0000]])

# 方法二
# nn.Relu: 必须先实例化,如:创建layer, 
layer = nn.ReLU(inplace=True)
print(layer(x))    
# tensor([[0.2846, 0.6158, 0.0000, 0.0000, 0.0000, 1.7980, 0.6466, 0.4263, 0.0000, 0.0000]])

3. GPU加速

  • Pytorch中使用torch.device()选取并返回抽象出的设备

  • 然后,在定义的网络模块 或者 Tensor后面加上 .to(device变量)就可以将它们搬到设备上了。

  • .cuda()默认用gpu,不推荐.

device = torch.device('cpu:0')                     #使用第一张显卡

net = MLP().to(device)                             # 定义的网络

criteon = nn.CrossEntropyLoss().to(device)         # 损失函数
  • 每次取出的训练集 和 验证集的 batch数据放到GPU上:
data, target = data.to(device), target.cuda()     # 两种方式, .cuda()不推荐
  • 应用上面的案例 添加GPU加速,完整代码:
view code
import  torch
import  torch.nn as nn
import  torch.nn.functional as F
import  torch.optim as optim
from    torchvision import datasets, transforms

# 超参数
batch_size=200
learning_rate=0.01
epochs=10

# 获取训练数据
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=True, download=True,         # train=True则得到的是训练集
                   transform=transforms.Compose([                 # transform进行数据预处理
                       transforms.ToTensor(),                     # 转成Tensor类型的数据
                       transforms.Normalize((0.1307,), (0.3081,)) # 进行数据标准化(减去均值除以方差)
                   ])),
    batch_size=batch_size, shuffle=True)                          # 按batch_size分出一个batch维度在最前面,shuffle=True打乱顺序

#获取测试数据
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=False, transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])),
    batch_size=batch_size, shuffle=True)


class MLP(nn.Module):

    def __init__(self):
        super(MLP, self).__init__()

        self.model = nn.Sequential(         # 定义网络的每一层,
            nn.Linear(784, 200),
            nn.ReLU(inplace=True),
            nn.Linear(200, 200),
            nn.ReLU(inplace=True),
            nn.Linear(200, 10),
            nn.ReLU(inplace=True),
        )

    def forward(self, x):
        x = self.model(x)
        return x

device = torch.device('cuda:0')  # 使用第一张显卡
net = MLP().to(device)
# 定义sgd优化器,指明优化参数、学习率,net.parameters()得到这个类所定义的网络的参数[[w1,b1,w2,b2,...]
optimizer = optim.SGD(net.parameters(), lr=learning_rate)
criteon = nn.CrossEntropyLoss().to(device)


for epoch in range(epochs):

    for batch_idx, (data, target) in enumerate(train_loader):
        data = data.view(-1, 28*28)          # 将二维的图片数据摊平[样本数,784]
        data, target = data.to(device), target.cuda()

        logits = net(data)                   # 前向传播
        loss = criteon(logits, target)       # nn.CrossEntropyLoss()自带Softmax

        optimizer.zero_grad()                # 梯度信息清空
        loss.backward()                      # 反向传播获取梯度
        optimizer.step()                     # 优化器更新

        if batch_idx % 100 == 0:             # 每100个batch输出一次信息
            print('Train Epoch: {} [{}/{} ({:.0f}%)]	Loss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))


    test_loss = 0
    correct = 0                                         # correct记录正确分类的样本数
    for data, target in test_loader:
        data = data.view(-1, 28 * 28)
        data, target = data.to(device), target.cuda()

        logits = net(data)
        test_loss += criteon(logits, target).item()     # 其实就是criteon(logits, target)的值,标量

        pred = logits.data.max(dim=1)[1]                # 也可以写成pred=logits.argmax(dim=1)
        correct += pred.eq(target.data).sum()

    test_loss /= len(test_loader.dataset)
    print('
Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)
'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

4. 多分类测试

  • 下面的例子中logits是一个 4*10 的Tensor

    • 可以理解成4张图片,每张图片有10维向量的预测

    • 然后对每一张照片的输出值执行softmaxargmax(dim=1),得出预测标签,与真实标签比较,得出准确率。

import  torch
import  torch.nn.functional as F

logits = torch.rand(4, 10)
pred = F.softmax(logits, dim=1)
print(pred.shape)     # torch.Size([4, 10])

pred_label = pred.argmax(dim=1)     # 每行最大值的下标      
print(pred_label)  
# tensor([8, 0, 7, 2]) 和 logits.argmax(dim=1)结果一样

# 定义标签(真实值)
label = torch.tensor([5, 0, 2, 7])

# 与真实标签比较
correct = torch.eq(pred_label, label)               
print(correct)
# tensor([False,  True, False, False]) 和 pred_label.eq(label)结果一样

# 准确率
print(correct.sum().float().item() / len(logits))   # 0.25