持续创作,加快成长!这是我参与「日新计划 10 月更文挑战」的第31天,点击查看活动详情


写在最前边

这篇文章要写的内容看封面,就是要用一篇文章讲解一下,怎样用Fashion-MNIST数据集,咱们自己建一个神经网络,练习好之后用它做图片分类。

这篇文章更适合有一点点基础的人看。每一部分的细节之前都写过具体解读,看不懂的能够去看专栏:pytorch tutorial – LolitaAnn在的专栏 – ()


import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor

老规矩,一切开端之前咱们先浅浅导个包。

数据集

Pytorch梳理数据集有两个首要的方法:torch.utils.data.DataLoadertorch.utils.data.Dataset

  • Dataset:存储数据集的样本及其标签

  • DataLoader将数据集包裹到迭代器中,便利模型读取

Pytorch提供了人工智能三大领域(文本、图画、语音)首要的数据集,运用TorchText, TorchVision, TorchAudio咱们能够拿到对应的一些主流数据集。

今日咱们要做一个图片分类网络,所以咱们在这儿用TorchVision。

咱们运用如下代码将FashionMNIST的练习集和测试集依次加载进来。

# 从FashionMNIST加载练习集
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
)
# 从FashionMNIST加载测试集
test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor(),
)
batch_size = 64
# 运用DataLoader创立好迭代器加载数据,便利模型读取
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)
for X, y in test_dataloader:
    print(f"Shape of X [N, C, H, W]: {X.shape}")
    print(f"Shape of y: {y.shape} {y.dtype}")
    break

咱们用for循环读了一步测试集DataLoader的内容,能够看到如下输出:

Shape of X [N, C, H, W]: torch.Size([64, 1, 28, 28])
Shape of y: torch.Size([64]) torch.int64

X是样本,咱们能够看到样本的张量形状为[64, 1, 28, 28],意思是一个batch有64张图,单通道(黑白图),分辨率为2828.

y是标签,FashionMNIST是分十类的,标签运用整数表示的,能够看到这儿y是一个长为64的向量。

创立模型

咱们创立自界说模型要作为nn.Module的子类,在__inti__中写网络结构,在forward中写核算图前馈核算的进程。如果有GPU咱们就将代码丢到GPU上运转。

# 看看你有无GPU,有GPU就用GPU,没有就用CPU。
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using {device} device")
# 界说模型结构
class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )
    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits
model = NeuralNetwork().to(device)
print(model)

# 这儿我是有GPU的,所以显现运用cuda Using cuda device
NeuralNetwork(
\quad \quad (flatten): Flatten(start_dim=1, end_dim=-1)
\quad \quad (linear_relu_stack):
\quad \quad Sequential(
\quad \quad \quad (0): Linear(in_features=784, out_features=512, bias=True)
\quad \quad \quad (1): ReLU()
\quad \quad \quad (2): Linear(in_features=512, out_features=512, bias=True)
\quad \quad \quad (3): ReLU()
\quad \quad \quad (4): Linear(in_features=512, out_features=10, bias=True)
\quad \quad \quad )
\quad \quad)

优化网络

练习网络之前先界说好loss优化目标和优化器。

loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)

练习网络咱们要许多epochs,在每次循环中,模型都会运用练习集猜测成果,运用猜测成果和实在成果进行比较,通过反向传达不断批改模型的参数。

def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)
        # 运用loss算猜测值和实在值的差错
        pred = model(X)
        loss = loss_fn(pred, y)
        # 反向传达更新网络
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        # 每100个batch打印一下当时练习进度
        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")

咱们也要不断查看模型的体现,以便知道他是在往正确的方向上学习的。

def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval() # 将模型转化为点评形式
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")

练习进程就是跑多个循环,咱们这儿设置epochs=15就是进行15次参数优化。每个epoch中模型都会更新参数以让自己的猜测成果更为准确。咱们在这儿是每轮epoch都打印一下当时的猜测的准确率和loss。loss越低,准确率越高,模型越好。

epochs = 15
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model, loss_fn)
print("Done!")

我先是跑了5个epoch,感觉准确率有点低,所以加到15了。当然这儿是简略数据集,练习过多可能呈现过拟合的现象,不能盲目增大epoch提高准确率。

  • epochs=5

    用一个图像分类实例拿捏Pytorch使用方法

  • epoch=15

    用一个图像分类实例拿捏Pytorch使用方法

模型训好了,做个猜测

classes = [
    "T-shirt/top",
    "Trouser",
    "Pullover",
    "Dress",
    "Coat",
    "Sandal",
    "Shirt",
    "Sneaker",
    "Bag",
    "Ankle boot",
]
# 将模型改为点评形式
model.eval()
x, y = test_data[0][0], torch.tensor(test_data[0][1])
# 把x,y挪到GPU上
x = x.to(device)
y = y.to(device)
with torch.no_grad():
    pred = model(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: "{predicted}", Actual: "{actual}"')

关于x和y的取法x, y = test_data[0][0], torch.tensor(test_data[0][1])test_data长下图这样,test_data[0]就是第一个样本,test_data[0][0]是取样本,test_data[0][1]是取标签。

用一个图像分类实例拿捏Pytorch使用方法

至于.to(device),x,y都是在CPU上的,但是模型是在GPU上的,咱们要将其挪到GPU上,否则会报错RuntimeError:Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!

模型保存和加载

模型练习好了,以后能够直接拿来用的。所以咱们要将其保存一下:

torch.save(model.state_dict(), "model.pth")
print("Saved PyTorch Model State to model.pth")

Saved PyTorch Model State to model.pth

需要的时分重新加载模型:

model = NeuralNetwork()
model.load_state_dict(torch.load("model.pth"))

由于咱们前边建好模型, 咱们是接着前边代码运转的,所以这儿不需要加载模型。

运用模型猜测

这儿是把类别都放进来了,给模型一个样本,看看它的成果。

classes = [
    "T-shirt/top",
    "Trouser",
    "Pullover",
    "Dress",
    "Coat",
    "Sandal",
    "Shirt",
    "Sneaker",
    "Bag",
    "Ankle boot",
]
model.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
    pred = model(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: "{predicted}", Actual: "{actual}"')

Predicted: “Ankle boot”, Actual: “Ankle boot”

能够看到在这儿是猜测对了的。

在这咱们不需要.to(device)了,由于这儿是运用前边建好的模型+咱们自己读进来的参数,这是一个新的网络,咱们没有把这个新网络放到GPU上。也就是说你现在这个代码里有两个如出一辙的模型。

  • 你在GPU上练习好了这个模型,然后将参数保存到了本地。

  • 你又从本地读进来参数,在CPU上重现了这个模型。