李沐深度学习-d2lzh_pytorch模块实现

发布时间:2024年01月19日

d2lzh_pytorch 模块

import random
import torch
import matplotlib_inline
from matplotlib import pyplot as plt
import torchvision
import torchvision.transforms as transforms
import torchvision.datasets
import sys
from collections import OrderedDict


# ---------------------------------------------------------------------------------------------
# 图表展示
def use_svg_display():
    # 用矢量图表示
    matplotlib_inline.backend_inline.set_matplotlib_formats('svg')


def set_figsize(figsize=(3.5, 2.5)):
    use_svg_display()
    # 设置图的尺寸
    plt.rcParams['figure.figsize'] = figsize


# ---------------------------------------------------------------------------------------------
# 读取数据
# 获取总的样本数量,然后打乱顺序,用batch-size获取每一部分索引去索引对应样本中的数据,使用yield返回
'''
函数详解:
torch.linspace(start, end, steps, dtype) → Tensor  从start开始到end结束,生成steps个数据点,数据类型为dtype
torch.index_select(input, dim, index)   索引张量中的子集
**
    input:需要进行索引操作的输入张量
    dim:张量维度  0,1
    index:索引号,是张量类型
**
yield: 使用yield的函数返回迭代器对象,每次使用时会保存变量信息,使用next()或者使用for可以循环访问迭代器中的内容
'''


def data_iter(batch_size, features, labels):
    num_examples = len(features)  # features   nxm
    indices = list(range(num_examples))  # 借助range生成索引序列
    random.shuffle(indices)  # 把list列表中的值打乱顺序
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i:min(i + batch_size, num_examples)])  # 这里的i是对标乱序表中的下标索引号
        yield features.index_select(0, j), labels.index_select(0, j)  # 0维度,有1000个样本,j就是他们的下标


# ---------------------------------------------------------------------------------------------

# 定义模型
def linreg(X, w, b):
    return torch.mm(X, w) + b  # 传进来的参数和样本特征都符合矩阵形式 w,b都是列矩阵  X:1000x2  w:2x1  b:1x1
    # 这里使用了广播


# ---------------------------------------------------------------------------------------------

# 定义损失函数
def square_loss(y_hat, y):
    # 保证y_hat和y同型,pytorch中的MSELoss没有除以2的操作
    return (y_hat - y.view(y_hat.size())) ** 2 / 2


# 这里的得到的也是一个小批量的样本的损失张量

# ---------------------------------------------------------------------------------------------
# 定义优化算法
# 这里使用的是sgd算法,使用小批量梯度和(参数求导后的和:梯度会自动累加,不用自己加和梯度)除以小批量样本个数来求小批量平均值
def sgd(params, lr, batch_size):
    for param in params:
        param.data -= lr * param.grad / batch_size  # 这里更改param时使用的是param.data,这样就不会影响反向梯度
        # 这里的param指的是w1,w2,b


# 这里应该是小批量中的每个loss运行完,得到小批量每个样本的梯度然后pytorch自动进行了梯度累加,之后一个小批量得到一个累加和后的
# 梯度w1,w2,b
# ---------------------------------------------------------------------------------------------

'''
FashionMNIST 数据集
'''


# ----------------------------------------------------------将数值标签转换成文本标签
def get_fashion_mnist_labels(labels):
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal',
                   'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]


# -----------------------------------------------------在一行里画出多张图像和对应标签的函数
def show_fashion_mnist(images, labels):
    use_svg_display()
    # 这里的_表示忽略(不使用)的变量
    _, figs = plt.subplots(1, len(images), figsize=(12, 12))  # 设置一行 len(images)个数量,每个figsize大小的画布
    # figs 返回的是一个画布对象,这个对象有imshow,set_tittle,axes_get_xasis().set_visible,
    # axes.get_yaxis().set_visible()这几种函数调用方式,用来给figs里面添加图像
    for f, img, lbl, in zip(figs, images, labels):  # 这个画布对象循环往里面添加图像信息
        f.imshow(img.view((28, 28)).numpy())  # img承接图像信息,将tensor转化为numpy  这里参数为数组元素
        f.set_title(lbl)
        f.axes.get_xaxis().set_visible(False)
        f.axes.get_yaxis().set_visible(False)
    plt.savefig("路径")


# ----------------------------------------------------------------获取并读取FashionMNIST数据集函数,返回小批量train,test
def load_data_fashion_mnist(batch_size):
    mnist_train = torchvision.datasets.FashionMNIST(root='路径',
                                                    train=True, download=True, transform=transforms.ToTensor())
    mnist_test = torchvision.datasets.FashionMNIST(root='路径',
                                                   train=False, download=True, transform=transforms.ToTensor())
    '''
    上面的mnist_train,mnist_test都是torch.utils.data.Dataset的子类,所以可以使用len()获取数据集的大小
    训练集和测试集中的每个类别的图像数分别是6000,1000,两个数据集分别有10个类别
    '''
    # mnist是torch.utils.data.dataset的子类,因此可以将其传入torch.utils.data.DataLoader来创建一个DataLoader实例来读取数据
    # 在实践中,数据读取一般是训练的性能瓶颈,特别是模型较简单或者计算硬件性能比较高的时候
    # DataLoader一个很有用的功能就是允许多进程来加速读取  使用num_works来设置4个进程读取数据
    if sys.platform.startswith('win'):
        num_workers = 0
    else:
        num_workers = 4
    train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True,
                                             num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False,
                                            num_workers=num_workers)
    return train_iter, test_iter


# -------------------------------------------------------------查看mnist前10个图像和标签
def check_mnist():
    mnist_train = torchvision.datasets.FashionMNIST(root='路径',
                                                    train=True, download=True, transform=transforms.ToTensor())
    mnist_test = torchvision.datasets.FashionMNIST(root='路径',
                                                   train=False, download=True, transform=transforms.ToTensor())
    X, y = [], []
    for i in range(10):
        X.append(mnist_train[i][0])  # 循环获取图像张量矩阵
        y.append(mnist_train[i][1])  # 循环获取图像对应数值标签
    show_fashion_mnist(X, get_fashion_mnist_labels(y))
    # feature, label = mnist_train[0]
    # print(feature.shape, label)  CxHxW
    # feature对应高和宽均为28像素的图像,因为使用了transforms.ToTensor(),所以每个像素的数值对应于【0.0,1.0】的32位浮点数
    # C 是通道数,RGB,灰色图像,通道数为1,H,W分别为高,宽
    # mnist_train[0] 是一个元祖,它包含两部分,图像数据结构和图像标签值,图像的数据结构是1x28x28结构,是一个浮点数矩阵,代表一个图像


# -------------------------------------------------------------------------评价模型net在数据集data_iter上的准确率
def evaluate_accuracy(test_iter, net):
    acc_sum, n, x = 0.0, 0, 0.0
    for X, y in test_iter:  # 返回一个批量的数据元组迭代对象
        acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()  # 将net模型的预测y与标签y进行了准确率比较
        n += y.shape[0]  # 累加获得样本个数
        x = acc_sum / n
    return x


# -------------------------------------------------------------------------训练模型函数
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params=None, lr=None, optimizer=None):
    for epochs in range(num_epochs):  # 循环周期
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0  # 预先定义 训练损失,训练精度,批量个数
        for X, y in train_iter:  # 批量更新
            y_hat = net(X)
            l = loss(y_hat, y).sum()  # 损失计算

            # 梯度清零
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:  # 权重存在并且权重的梯度存在
                for param in params:
                    param.grad.data.zero_()
            l.backward()  # 反向传播
            # 梯度更新操作
            if optimizer is None:
                sgd(params, lr, batch_size)  # 调用sgd进行梯度下降操作
            else:
                optimizer.step()  # softmax回归的简洁实现将要用到

            train_l_sum += l.item()  # 损失累加
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()  # (y_hat.argmax(dim=1) == y)
            # 取出y_hat每一行中最大的概率索引和y比较,结果为tensor,元素值为0/1
            n += y.shape[0]  # 计算一个批量中标签的个数
        test_acc = evaluate_accuracy(test_iter, net)  # 一个循环之后进行测试集的准确度计算
        print(f'epoch %d,loss %.4f,train_acc %.3f,test_acc %.3f'
              % (epochs + 1, train_l_sum / n, train_acc_sum / n, test_acc))


# x = torch.tensor([[0.1, 0.4, 0.2], [1, 0.06, 0.5]])
# print((x.argmax(dim=1)==torch.tensor([[1,1]])).float())

# -------------------------------------------------------------------------x的形状转换功能函数
class FlattenLayer(torch.nn.Module):
    def __init__(self):
        super(FlattenLayer, self).__init__()  # 初始化函数,自动调用forward函数

    def forward(self, x):  # x shape: (batch,*,*,....)
        return x.view(x.shape[0], -1)  # 转换成(batch_size,特征数)形状


# 这样就方便定义模型
net = torch.nn.Sequential(
    # FlattenLayer()
    # torch.nn.Linear(num_inputs,num_outputs)
    OrderedDict([
        ('flatten', FlattenLayer()),
        ('linear', torch.nn.Linear(2, 3))
    ])
)

文章来源:https://blog.csdn.net/qq_43401942/article/details/135696889
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。