[动手学深度学习(PyTorch)]——图像分类数据集、Softmax实现

TorchVision库

torchvision包含一些常用的数据集、模型、转换函数等等。当前版本0.5.0包括图片分类、语义切分、目标识别、实例分割、关键点检测、视频分类等工具,它将mask-rcnn功能也都包含在内了。mask-rcnn的Pytorch版本最高支持torchvision 0.2.*,0.3.0之后mask-rcnn就包含到tensorvision之中了。

torchvision是独立于pytorch的关于图像操作的一些方便工具库。torchvision库简介(翻译) - 看看完了 - 博客园 (cnblogs.com)

torchvision主要包括一下几个包:

  • vision.datasets : 几个常用视觉数据集,可以下载和加载,这里主要的高级用法就是可以看源码如何自己写自己的Dataset的子类
  • vision.models : 流行的模型,例如 AlexNet, VGG, ResNet 和 Densenet 以及 与训练好的参数。
  • vision.transforms : 常用的图像操作,例如:随机切割,旋转,数据类型转换,图像到tensor ,numpy 数组到tensor , tensor 到 图像等。
  • vision.utils : 用于把形似 (3 x H x W) 的张量保存到硬盘中,给一个mini-batch的图像可以产生一个图像格网。

一、图像分割数据集

数据加载模板:

# ================================================================== #
#                Input pipeline for custom dataset                 #
# ================================================================== #

# You should build your custom dataset as below.
class CustomDataset(torch.utils.data.Dataset):
    def __init__(self):
        # TODO
        # 1. Initialize file paths or a list of file names. 
        pass
    def __getitem__(self, index):
        # TODO
        # 1. Read one data from file (e.g. using numpy.fromfile, PIL.Image.open).
        # 2. Preprocess the data (e.g. torchvision.Transform).
        # 3. Return a data pair (e.g. image and label).
        pass
    def __len__(self):
        # You should change 0 to the total size of your dataset.
        return 0 

# You can then use the prebuilt data loader. 
custom_dataset = CustomDataset()
train_loader = torch.utils.data.DataLoader(dataset=custom_dataset,
                                           batch_size=64, 
                                           shuffle=True)

(5条消息) PyTorch深度学习初步——如何读取你自己的数据集_白羊是小白-CSDN博客_pytorch读取自己的数据集

import time
import torch
import torchvision
from matplotlib import pyplot as plt
from torch.utils import data
from torchvision import transforms

def get_dataloader_worders():
    return 0

#MNIST数据集是图像分类中广泛使用的数据集之一,但是作为基准数据集过于简单,这里使用Fashion-MNIST数据集
#使用框架的内置API将数据集读取到内存中
def load_data_fashion_mnist(batch_size, resize=None):
    """
    :param batch_size: 批量大小
    :param resize: 图片尺寸变换
    :return: 批量数据
    """
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    # 通过ToTensor实例将图像数据集从PIL类型转换成32位浮点数格式
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root="./data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="./data", train=False, transform=trans, download=True)

    return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_worders()),
            data.DataLoader(mnist_test, batch_size, shuffle=True, num_workers=get_dataloader_worders()))

train_generator, test_generator = load_data_fashion_mnist(128)
print(type(train_generator))

#定义两个可视化函数
def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = [
        't-shirts', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt',
        'sneaker', 'bag', 'ankle boot'
    ]
    return [text_labels[int(i)] for i in labels]

def show_images(imgs, num_rows, num_cols, title=None, scale=2.5):
    """Plot a lsit of imgs"""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
        else:
            #PIL图片
            ax.imshow(img)
        ax.set_title(title[i])
    plt.show()


#取出小批量的数据,显示对应的图片和标签
# x, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
#show_images(x.reshape(18, 28, 28), 2, 9, title=get_fashion_mnist_labels(y))


#读取小批量数据
batch_size = 100


#train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_worders())

start = time.time()
for x, y in train_generator:
    continue
print('%.2f sec' % float(time.time() - start))

二、softmax回归从零开始实现

1、根据编号取出预测值的方法

import torch

#根据编号取出预测值的方法
#创建一个数据y_hat 其中包含2个样本在三个类别上的预测概率,使用y作为y_hat中改了的索引
y = torch.tensor([0, 2]) #两个真实的标号
print(y)
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
#解释:对于 y_hat[0. y[0]], y_hat[1, y[1]]
print(y_hat[[0, 1], y])

2、图片绘制函数

enumerate函数:将一个可遍历的变量转换成 "下标+数值" 形式

Python enumerate() 函数 | 菜鸟教程 (runoob.com)

#图片展示
def show_images(imgs, num_rows, num_cols, title=None, scale=2.5):
    """Plot a lsit of imgs"""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten() #将ax由n*m的Axes组展平成1*nm的Axes组,也就是说所有的编号就从 0.... nm-1,为了在for循环中索引方便
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
        else:
            #PIL图片
            ax.imshow(img)
        ax.set_title(title[i])
    plt.show()

3、小批量随机梯度更新函数

# 小批量随机梯度优化模型的损失函数
def SGD(params, lr, batch_size):  # SGD
    """SGD"""
    with torch.no_grad():  # 更新的时候不参与梯度计算
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()
def updater(batch_size):
    """小批量随机梯度下降"""
    return SGD([w, b], lr, batch_size)

4、softmax函数及交叉熵损失函数实现

【损失函数】交叉熵损失函数简介 - 知乎 (zhihu.com)

#定义softmax操作 softmax(X) = exp(xi) / sum exp(xi...)
def softmax(x):
    x_exp = torch.exp(x)
    partition = x_exp.sum(1, keepdim=True)
    return x_exp / partition #这里应用了广播机制

#实现交叉熵损失函数
def cross_entropy(y_hat, y):
    return -torch.log(y_hat[range(len(y_hat)), y])

#实现softmax回归模型
def net(x):
    return softmax(torch.matmul(x.reshape((-1, w.shape[0])), w) + b) #-1就表示根据后面的列数自动推出来行数

5、多参数累加类

class Accumulator:
    """在n个变量上累加"""
    def __init__(self, n):
        self.data = [0.0] * n #将列表中记录数扩展为n个

    def add(self, *args):
        #zip(self.data, args):将self.data和args中对应位置的元素打包
        self.data = [a + float(b) for a, b in zip(self.data, args)] #实现传进来的列表中元素分别按照编号累加的原列表(self.data)中

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

6、评估函数实现

#将预测的类别和真实的y元素进行比较
def accuracy(y_hat, y):
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)  #取出预测值最大的对应的下标
    cmp = y_hat.type(y.dtype) == y #转换成和y同类型的,并比较y_hat和y中值得一致情况,其实就是生成二值向量
    return float(cmp.type(y.dtype).sum())

#评估任意模型的准确率
def evaluate_accuracy(net, data_iter):
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module):
        net.eval() #将模型设置为评估模式
    metric = Accumulator(2) #正确预测数、预测总数 Accumulator累加器
    for x, y in data_iter:
        metric.add(accuracy(net(x), y), y.numel()) #两个数,一个是预测正确的数,另一个是样本总数
    return metric[0] / metric[1] #平均准确率

7、训练&预测

#训练模型
#训练一轮
def train_epoch(net, train_iter, loss, updater):
    if isinstance(net, torch.nn.Module):
        net.train()
    metric = Accumulator(3)
    for x,y in train_iter:
        y_hat = net(x)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            updater.zero_grad()
            l.backward()
            updater.step() #参数自更新
            metric.add(
                float(l) * len(y), accuracy(y_hat, y),
                y.size().numel()
            )
        else: #没用框架的话直接计算
            l.sum().backward()
            updater(x.shape[0])
            metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) #所有的loss的总数、分类正确的样本数,总数
    return metric[0] / metric[2], metric[1] / metric[2]

def train_all(net, train_iter, test_iter, loss, num_epochs, updater):
    for epoch in range(num_epochs):
        train_metrics = train_epoch(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        print(f'epoch: {epoch+1}, loss: {train_metrics[0]}, train acc: {train_metrics[1]:.3f}, test acc: {test_acc:.3f}')


#对图像进行预测
def predict(net, test_iter, n = 6):
    """预测标签"""
    for x, y in test_iter:
        break
    trues = get_fashion_mnist_labels(y)
    preds = get_fashion_mnist_labels(net(x).argmax(axis=1))
    titles = [true + '\n' + pred for true, pred in zip(trues, preds)]
    show_images(x[0:n].reshape((n, 28, 28)), 1, n, title=titles[0:n])

[动手学深度学习(PyTorch)]——图像分类数据集、Softmax实现_第1张图片

 三、PyTorch框架实现版

import torch
from matplotlib import pyplot as plt
from torch import nn
from torch.utils import data
import torchvision
from torchvision import transforms

def get_dataloader_worders():
    return 0

#MNIST数据集是图像分类中广泛使用的数据集之一,但是作为基准数据集过于简单,这里使用Fashion-MNIST数据集
#使用框架的内置API将数据集读取到内存中
def load_data_fashion_mnist(batch_size, resize=None):
    """
    :param batch_size: 批量大小
    :param resize: 图片尺寸变换
    :return: 批量数据
    """
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    # 通过ToTensor实例将图像数据集从PIL类型转换成32位浮点数格式
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root="./data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="./data", train=False, transform=trans, download=True)

    return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_worders()),
            data.DataLoader(mnist_test, batch_size, shuffle=True, num_workers=get_dataloader_worders()))

batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size)

#Softmax 回归的输出层是一个全连接层
#PyTorch不会隐式地调整输入的形状。因此定义展开层(flatten),在线性层之前调整网络输入的形状
net = nn.Sequential(
    nn.Flatten(),
    nn.Linear(784, 10)
)

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights)

#在交叉熵损失函数中传递未归一化的预测,并同时计算softmax及其对数
loss = nn.CrossEntropyLoss()

#使用学习率为0.1的小批量随机梯度下降作为优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

你可能感兴趣的:(动手学深度学习,pytorch,深度学习,机器学习)