d2l-softmax实现流程

  在softmax中从新讲解一下传入传出各个输出量是什么

目录

数据读取

定义累加工具

定义预测训练精确度工具:

 定义任意数据集的精度检测工具

定义单个epoch训练流程

总训练函数(改正纯数字版本)

最终的整体运行命令行:

数据读取

  读取函数没什么好说的,就加了一个选择resize类,这个地方dataloader后是输出两个返回值,第一个为四维张量,表示为([bs,c,w,h]);第二个为一维张量标签,表示为([bs]),里面的数为对应各个图片的类别标签。这也是后续迭代中,X,y分别是什么。
for X, y in train_loader:
def load_data_fashion_mnist(batch_size, resize=None):  # @save
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
        # transforms.Resize:调整PILImage对象的尺寸。transforms.Resize([h, w])或transforms.Resize(x)等比例缩放
    trans = transforms.Compose(trans)  # 串联多个图片变换的操作
    mnist_train = torchvision.datasets.FashionMNIST(
        root="../data", train=True, transform=trans, download=False)
    mnist_test = torchvision.datasets.FashionMNIST(
        root="../data", train=False, transform=trans, download=False)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=False,
                            num_workers=get_dataloader_workers()))

定义累加工具

前面讲过了:

class Accumulator:
    """For accumulating sums over `n` variables."""

    def __init__(self, n):
        """Defined in :numref:`sec_softmax_scratch`"""
        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]

定义预测训练精确度工具:

  简单讲一下,传入预测的y_hat与标签值y,在网络中y_hat = net(X),上文可知X和y均为for loader迭代的(bs, 单个x维度cwh);(bs),这里面X每一个是一张图片,是个张量,y每个是个标签值,是个标量。

  这里y_hat不经过softmax也没影响,因为exp是单增的,这里argmax返回的是一张图片预测的10个类别中最大的数值对应的位置,也就是返回对应的类别数字。

  这里的cmp是一个bool列表,包含0(False),1(True),在此为([True, True...]),再通过.sum()函数使之数值化累加,其中‘==’运算符对dtype很敏感,所以在用之前还要统一成y的dtype!

  最终返回的是预测正确的个数(格式为float)

def accuracy(y_hat, y):  # @save
    """计算预测正确的数量"""
    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
    return float(cmp.type(y.dtype).sum())

d2l-softmax实现流程_第1张图片d2l-softmax实现流程_第2张图片

定义任意数据集的精度检测工具

在这里传入的是自己定义的检测网络,需要评估分类精度的数据集。

这里X,y与前面说的尺寸一致,在此net(X)即为一个bs中的y_hat,这里的y即为一个bs中的标签向量。再通过累加工具加起来,最终返回=预测正确的个数/数据集总个数

def evaluate_accuracy(net, data_iter):  # @save
    """计算在指定数据集上模型的精度"""
    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]

定义单个epoch训练流程

  这个train_epoch_ch3最终返回的的是训练损失值和精确度(正确预测/训练数据集总数),这里面的y.numel()表示计算y张量中所有元素个数。

  到最后这个返回的第一个值是计算出所有的loss后,除train中图片的总数,这才是整个eopch的损失平均值,如果改成float(l.mean()),然后不除metric[2],这表示的是计算每一个batch的loss平均数然后累加,是不太科学的,因为loss前面大后面小。

  loss经过交叉熵返回的是一个一维张量(bs,),每张照片对应一个loss值。其输入的y_hat是(bs,cls),y为(bs,)

def train_epoch_ch3_1(net, train_iter, loss, updater):
    """The training loop defined in Chapter 3.

    Defined in :numref:`sec_softmax_scratch`"""
    # Set the model to training mode
    if isinstance(net, torch.nn.Module):
        net.train()
    # Sum of training loss, sum of training accuracy, no. of examples
    metric = Accumulator(3)
    for X, y in train_iter:
        # Compute gradients and update parameters
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # Using PyTorch in-built optimizer & loss criterion
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # Using custom built optimizer & loss criterion
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # Return training loss and training accuracy
    return metric[0] / metric[2], metric[1] / metric[2]

d2l-softmax实现流程_第3张图片

总训练函数(改正纯数字版本)

书里面的那个画图有时候不太直观表达出具体数是多少,所以我就把画图的部分删了,换成了直接输出数字的部分。

这个函数就是输入网络,训练、测试数据集loader,epoch数与优化器,即可返回你想要的损失与精确度

def train_ch3_1(net, train_iter, test_iter, loss, num_epochs, updater):
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3_1(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        print(f'训练损失{train_metrics[0]:.5f},精确度{train_metrics[1]:.5f},测试集{test_acc:.5f}')

最终的整体运行命令行:

if __name__ == '__main__':
    batch_size = 256
    train_iter, test_iter = load_data_fashion_mnist(batch_size)

    # 初始化模型参数
    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.1)


    net.apply(init_weights)

    # 损失
    loss = nn.CrossEntropyLoss(reduction='none')

    # 优化算法
    trainer = torch.optim.SGD(net.parameters(), lr=0.1)

    # 训练
    num_epoch = 10
    train_ch3_1(net, train_iter, test_iter, loss, num_epoch, trainer)

    # # 预测
    # d2l.predict_ch3(net, test_iter)
    # plt.show()

你可能感兴趣的:(文件处理,机器学习,python,深度学习)