使用Fashion-MNIST数据集完成Softmax回归模型搭建

import matplotlib.pyplot as plt
import torch
import torchvision
from d2l import torch as d2l
from IPython import display


d2l.use_svg_display()

"""
# 1, 读取数据集
# 1.1,通过ToTensor实例将图像数据从PIL类型变换为32位浮点数格式。
# 并除以255使得所有像素数值均在0~1之间
trans = torchvision.transforms.ToTensor()
mnist_train_dataset = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
mnist_test_dataset = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)

print(mnist_train_dataset[0][0].shape)

def get_fashion_mnist_labels(labels):
    
    text_labels = ['t-shrit', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]

# 1.2, 创建一个函数来可视化这些样本
def show_dataset_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    
    figsize = (num_cols*scale, num_rows*scale)
    _, axes = d2l.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())
            plt.imshow(img.numpy())
        else:
            # PIL图片
            #ax.imshow(img)
            plt.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if titles:
            ax.set_title(titles[i])
    return axes

X, y = next(iter(torch.utils.data.DataLoader(mnist_train_dataset, batch_size=18)))
show_dataset_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y));

batch_size1 = 256
"""

# 2, 读取小批量
def get_dataloader_workers():
    """使用4个进程来读取数据"""
    return 4

# 2.1, 为了使我们在读取训练集和测试集时更容易,我们使用内置的数据迭代器,
# train_iter = torch.utils.data.DataLoader(mnist_train_dataset, batch_size1, shuffle=True, num_workers=get_dataloader_workers())

def load_data_fashion_mnist(batch_size, resize=None): #@save
    
    trans = [torchvision.transforms.ToTensor()]
    if resize:
        trans.insert(0, torchvision.transforms.Resize(resize))
    trans = torchvision.transforms.Compose(trans)
    mnist_train_dataset = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
    mnist_test_dataset = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)
    return (torch.utils.data.DataLoader(mnist_train_dataset, batch_size, shuffle=True, num_workers=get_dataloader_workers()),
            torch.utils.data.DataLoader(mnist_test_dataset, batch_size, shuffle=False, num_workers=get_dataloader_workers()))

# 2.2 测试加载batch_size=32的样本。
train_iter1, test_iter1 = load_data_fashion_mnist(32, resize=64)
for X1, Y1 in train_iter1:
    print(X1.shape, X1.dtype, Y1.shape, Y1.dtype)
    break

# 3, Softmax回归从零开始实现
# 3.1 使用Fashion-MNIST数据集,并设置数据迭代器的批量大小为256.
batch_size2 = 256
train_dataset_iterator, test_dataset_iterator = d2l.load_data_fashion_mnist(batch_size2, resize=None)

# 4, 初始化模型参数
# 4.1 数据集中每个样本是28x28的图像,将该图像展平为一维向量为784长度,
#     由于有10个类别,那么权重将是784x10的矩阵,偏置将构成一个1x10的行向量,
#     我们仍然使用正态分布初始化权重Weight,偏置bais初始化为0.
num_inputs = 784
num_outputs = 10

weight = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
bais = torch.zeros(num_outputs, requires_grad=True)

# 5, 定义softmax操作
# 5.1 使用sum运算符沿着Tensor的指定维度计算和。
t1 = torch.tensor([[1,2,3],[4,5,6]])
print(t1.sum(0, keepdim=True))
print(t1.sum(1, keepdim=True))

# 5.2 实现softmax函数,规范化结果。
def softmax(Predicted_value):
    exp_value = torch.exp(Predicted_value)
    SUM_partition = exp_value.sum(1, keepdim=True)
    return exp_value / SUM_partition

# 5.3 随机输入,验证计算结果
predict_value1 = torch.normal(0, 1, (2,10))
normalize_value = softmax(predict_value1)
print(normalize_value)
print(normalize_value.sum(1, keepdim=True))

# 6,定义模型, 将输入层数据input_X,使用reshape函数将每张原始图像展平为向量。
def network(input_X):
    return softmax(torch.matmul(input_X.reshape((-1, weight.shape[0])), weight) + bais)

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

# 8, 分类精度,我们使用argmax获得每行中最大元素的索引来获得预测类别。然后我们将预测类别与真实y元素进行比较。由
# 于等式运算符“==”对数据类型很敏感,因此我们将y_hat的数据类型转换为与y的数据类型一致。结果是一
# 个包含0(错)和1(对)的张量。最后,我们求和会得到正确预测的数量
def accuracy(y_hat, y):
    print(y_hat.shape)
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_predicted_indices = y_hat.argmax(axis=1)
        print(y_predicted_indices)
    compare_result = y_predicted_indices.type(y.dtype) == y
    print(compare_result)
    return float(compare_result.type(y.dtype).sum())

# 8.1, 使用两个张量简单测试一下accuracy()函数。
test_y_hat = torch.tensor([(0.05, 0.05, 0.1, 0.15, 0.05, 0.45, 0.05, 0.02, 0.03, 0.05),
                           (0.05, 0.45, 0.1, 0.15, 0.05, 0.05, 0.05, 0.02, 0.03, 0.05),
                           (0.05, 0.05, 0.1, 0.15, 0.05, 0.15, 0.05, 0.37, 0.03, 0.05),
                           (0.05, 0.05, 0.1, 0.15, 0.05, 0.15, 0.05, 0.01, 0.39, 0.05)])

test_y = torch.tensor([(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0),
                       (0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
                       (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0),
                       (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)])
                       
test_y_index = torch.tensor([(5, 2, 7, 9)])
print(accuracy(test_y_hat, test_y_index))

def evaluate_accuracy(net, data_iter):
    if isinstance(net, torch.nn.Module):
        net.eval()
    metric = Accumulator(2)
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
    return metric[0]/metric[1]

# 8.2, 构建多个变量累加的Accumulator类,在上面的evaluate_accuracy函数中,我们在
#       Accumulator实例中创建了2个变量,分别用于存储正确预测的数量和预测的总数量,当我们
#       遍历数据集时,两者都将随着时间的推移而累加。
list1 = [x + y for x, y in [(1, 2),(3, 4),(5, 6)]]
print(list1)

list2 = [4, 5, 6, 7]
list1 = [m + n for m, n in zip(list1, list2)]
print(list1)

class Accumulator: #@save
    def __init__(self, n):
        self.data = [0.0] * n
    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]
    def reset(self):
        self.data = [0.0] * len(self.data)
    def __getitem__(self, idx):
        return self.data[idx]

# 9, 训练, 定义一个函数来训练一个迭代周期,updater是更新模型参数的常用函数,它接受
#    批量大小作为参数。它可以是d2l.sgd函数,也可以是框架的内置优化函数。
# 9.1 使用numel()函数统计张量Tensor中的元素element的个数。
x = torch.randn(3,3)
print("number elements of x is ",x.numel())
y = torch.randn(3,10,5)
print("number elements of y is ",y.numel())

def train_epoch_ch3(net, train_iter, updater):
    if isinstance(net, torch.nn.Module):
        net.train()
    metric = Accumulator(3)
    for X, y in train_iter:
        # 计算梯度并更新参数
        y_hat = net(X)
        loss_value = cross_entropy(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            loss_value.mean().backward()
            updater.step()
        else:
            # 使用定制的优化器和损失函数
            loss_value.sum().backward()
            updater(X.shape[0])
        metric.add(float(loss_value.sum()), accuracy(y_hat, y), y.numel())
        print("number elements of y is ", y.numel())
    # 返回训练损失和训练精度
    print("number is ", metric[2])
    return metric[0] / metric[2], metric[1] / metric[2]

# 9.2, 定义一个在动画中绘制数据的实用程序类Animator.
class Animator: #@save
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                    ylim=None, xscale='linear', yscale='linear',
                    fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                figsize=(3.5, 2.5)):
        # 增量地绘制多条线
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函数捕获参数
        self.config_axes = lambda: d2l.set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # Add multiple data points into the figure
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        plt.draw()
        plt.pause(0.001)
        display.clear_output(wait=True)


# 9.3, 实现训练函数,它会在train_iter访问的训练数据集上训练一个模型net,该训练函数
#      将会运行多个迭代周期(由num_epochs指定),在每个迭代周期接受时,利用test_iter访问到
#      的测试数据集对模型进行评估,然后利用Animator类来可视化训练进度。
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], legend=['train loss', 'train acc', 'test acc'])

    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        animator.add(epoch+1, train_metrics+(test_acc,))
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc



# 9.4, 定义小批量随机梯度下降函数来优化模型的损失函数,注意updater是更新模型参数的常用函数,它接受
#      批量大小作为参数,它可以是d2l.sgd,也可以是Pytorch框架自带的内置优化函数。
"""
def sgd(params, lr, batch_size):
    # 为了和原书保持一致,这里除以了batch_size,但是应该是不用除的,因为一般用PyTorch计算loss时就默认已经
    # 沿batch维求了平均了。
    for param in params:
        param.data = param.data - lr * param.grad / batch_size # 注意这里更改param时用的param.data
"""
learning_rate = 0.1
def updater(batch_size):
    return d2l.sgd([weight, bais], learning_rate, batch_size)

num_epochs = 10
train_ch3(network, train_dataset_iterator, test_dataset_iterator, cross_entropy, num_epochs, updater)
plt.show()

# 10, 使用训练好的模型对图像进行分类预测。
def predict_ch3(net, test_iter, n=6):
    print("use model to predict data:")
    for X, y in test_iter:
        break
    y_predicted = net(X)
    y_predicted_indices = y_predicted.argmax(axis=1)
    print(y_predicted_indices)
    compare_result = y_predicted_indices.type(y.dtype) == y
    print(compare_result)
    trues = d2l.get_fashion_mnist_labels(y)
    preds = d2l.get_fashion_mnist_labels(y_predicted_indices)
    titles = [true + '\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(network, test_dataset_iterator)

----------------------------------------------------------------------

一,训练过程epoch的损失函数变化和训练准确度变化:

二,使用训练后模型预测结果

tensor([9, 2, 1, 1, 6, 1, 4, 6, 5, 7, 4, 5, 5, 3, 4, 1, 2, 6, 8, 0, 6, 7, 7, 7,
        1, 2, 6, 0, 9, 6, 8, 8, 3, 3, 8, 0, 7, 5, 7, 9, 6, 1, 6, 9, 6, 7, 6, 1,
        2, 6, 6, 6, 5, 6, 2, 2, 8, 4, 8, 0, 7, 7, 8, 5, 1, 1, 6, 4, 7, 8, 7, 0,
        6, 6, 2, 3, 1, 2, 8, 4, 1, 8, 5, 9, 5, 0, 3, 2, 0, 6, 5, 3, 6, 7, 1, 8,
        0, 1, 6, 2, 3, 6, 7, 6, 7, 8, 5, 9, 9, 4, 2, 5, 7, 0, 5, 2, 8, 4, 7, 8,
        0, 0, 9, 9, 3, 0, 8, 4, 1, 5, 4, 1, 9, 1, 8, 6, 6, 1, 2, 5, 1, 6, 0, 0,
        1, 6, 1, 3, 2, 6, 6, 2, 1, 3, 5, 0, 4, 7, 9, 3, 7, 2, 3, 9, 0, 9, 4, 7,
        4, 2, 6, 5, 2, 1, 2, 1, 3, 0, 9, 1, 0, 9, 3, 8, 7, 9, 9, 4, 4, 7, 1, 2,
        1, 6, 3, 2, 8, 3, 6, 1, 1, 0, 2, 9, 2, 6, 0, 7, 9, 8, 4, 1, 8, 4, 1, 3,
        1, 6, 7, 4, 8, 5, 6, 0, 7, 7, 6, 6, 7, 0, 7, 8, 9, 2, 9, 0, 5, 1, 4, 2,
        5, 4, 9, 6, 2, 8, 6, 4, 6, 4, 9, 7, 4, 5, 5, 4])


tensor([ True,  True,  True,  True,  True,  True,  True,  True,  True,  True,
         True,  True, False,  True,  True,  True,  True, False,  True,  True,
        False, False,  True, False,  True, False,  True,  True,  True, False,
         True,  True,  True,  True,  True,  True,  True,  True,  True,  True,
         True,  True, False, False,  True,  True, False,  True,  True, False,
        False, False,  True, False,  True,  True,  True,  True,  True,  True,
         True,  True,  True,  True,  True,  True, False, False, False,  True,
         True,  True, False,  True,  True,  True,  True,  True,  True,  True,
         True,  True,  True,  True,  True,  True,  True,  True,  True,  True,
         True,  True,  True,  True,  True,  True,  True,  True, False,  True,
         True,  True,  True, False,  True,  True,  True,  True,  True,  True,
         True,  True,  True,  True,  True,  True,  True, False,  True,  True,
         True,  True,  True,  True,  True,  True,  True,  True,  True,  True,
         True,  True,  True,  True,  True,  True, False,  True,  True,  True,
         True, False,  True,  True,  True,  True,  True, False,  True, False,
        False, False,  True, False,  True,  True,  True,  True,  True,  True,
         True,  True,  True,  True,  True,  True,  True,  True,  True,  True,
        False,  True,  True,  True,  True,  True,  True,  True,  True,  True,
         True,  True,  True, False,  True,  True,  True,  True,  True,  True,
         True,  True,  True,  True,  True,  True,  True,  True,  True,  True,
         True,  True,  True,  True,  True, False,  True,  True,  True,  True,
         True,  True,  True,  True,  True,  True,  True,  True,  True, False,
         True,  True, False,  True,  True,  True,  True, False,  True,  True,
         True,  True,  True,  True,  True,  True,  True,  True,  True, False,
         True, False,  True, False, False,  True,  True,  True, False, False,
         True,  True, False,  True,  True, False])

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值