import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt
from torch import nn
from torch import optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report

import warnings
warnings.filterwarnings('ignore')

batch_size = 256
epochs = 10
learning_rate = 0.01
train_dataset = datasets.MNIST('./data', train=True, download=False, transform=transforms.Compose([
    transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]))
test_dataset = datasets.MNIST('./data', train=False, download=False, transform=transforms.Compose([
    transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]))

train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

常规操作就不说了,导库加载数据,说明一下warnings,这里就是去除一些不会影响运行的警告。

def plot_curve(data, name):
    plt.plot(range(len(data)), data, color='blue')
    plt.legend([name], loc='upper right')
    plt.xlabel('step')
    plt.ylabel('value')

画图工具

class Net(nn.Module):
    '''公式 out = (i-k+2p+1)/s, conv满5往上取整, pooling往下取整'''
    def __init__(self):
        super(Net, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=0),  # [256, 1, 28, 28] => [256, 32, , ]
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=0),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
        )
        self.fc = nn.Sequential(
            nn.Linear(64*5*5, 32),
            nn.ReLU(),
            nn.Linear(32, 16),
            nn.ReLU(),
            nn.Linear(16, 10),
            # nn.Softmax(dim=1)
        )

    def forward(self, x):
        x = self.conv(x)
        # x = torch.nn.functional.adaptive_avg_pool2d(x, (1, 1))
        # x = x.view(x.size(0), -1)
        x = x.view(x.size(0), 64*5*5)
        x = self.fc(x)

        return x

这是一个简单的卷积网络,很简单,一层卷积一层池化,这里也就两层卷积,计算卷积后图像的大小公式:公式 out = (i-k+2p+1)/s, conv满5往上取整, pooling往下取整,就找这个算,得到的输出就是下面全连接的输入,在传入前馈后,经过了卷积后的图像依然是[b, , , ]四维的,而全连接要的是二维,所以还是要转换一下,不然要报错。就像我上面的参数,卷积后为[256, 64, 5, 5],转二维后[256, 64*5*5]

net = Net()#.to(device)
criteon = nn.CrossEntropyLoss()#.to(device)
opt = optim.Adam(net.parameters(), lr=learning_rate)
print(net)
train_loss = []
all_train_loss = []
num = 0.0
for epoch in range(epochs):
    for batch_idx, (x, y) in enumerate(train_dataloader):
        # x, y = x.to(device), y.to(device)
        out = net(x)
        # loss = criteon(out, y)
        loss = criteon(out.to(float), F.one_hot(y).to(float))

        opt.zero_grad()
        loss.backward()
        opt.step()

        all_train_loss.append(loss.item())
        num += 1
        if batch_idx % 10 == 0:
            print(epoch, batch_idx, loss.item())

    train_loss.append(loss.item() / num)

接下来就是实例化模型了,这里训练模型如果有GPU的可以传到GPU上,不过运算完记得传回CPU,跟之前全连接没多大区别,只不过这里我用了nn.CrossEntropyLoss,这是交叉熵函数,看过之前全连接的应该能注意到上面全连接最后一层我没有加softmax,这个nn.CrossEntropyLoss相当于torch.nn.LogSoftmax和torch.nn.NLLLoss,源码里的解释有说明

而为什么又要这样用呢,首先使用softmax()最后计算精度上会有偏差,softmax会进行指数操作,而x值过大或者过小时会造成exp(x)超过float的范围,LogSoftmax解决了上溢或者下溢的问题,而torch. nn.CrossEntropyLoss()包含这两种函数,这样既解决了上下溢,又不用再使用one_hot编码。

剩下的代码就更全连接大差不差了,相信认真看的肯定能懂,毕竟还是很好理解的。

不多说了,还有下一个实现,期待下次更新吧。。。