• pytorch教程


    1 pytorch的安装

    库名作用
    torchvision图像视频处理
    torchaudio音频处理
    torchtext自然语言处理
    pip install -i https://mirrors.aliyun.com/pypi/simple/ torch torchvision torchaudio torchtext
    
    • 1
    • 出现以下界面说明安装成功
      在这里插入图片描述
    • 验证是否安装成功
      在这里插入图片描述

    2 PyTorch基础知识

    2.1 张量简介

    • 分类:0维张量(标量)、1维张量(向量)、2维张量(矩阵)、3维张量(时间序列)、4维张量(图像)、5维张量(视频)
    • 概念:一个数据容器,可以包含数据、字符串等
    • 常见的构造Tensor的函数
    函数功能
    Tensor(*sizes)基础构造函数
    tensor(data)类似于np.array
    ones(*sizes)全1
    zeros(*sizes)全0
    eye(*sizes)对角为1,其余为0
    arange(s,e,step)从s到e,步长为step
    linspace(s,e,steps)从s到e,均匀分成step份
    rand/randn(*sizes)rand是[0,1)均匀分布;randn是服从N(0,1)的正态分布
    normal(mean,std)正态分布(均值为mean,标准差是std)
    randperm(m)随机排列
    • Tensor的运算
    函数作用
    torch.abs(A)绝对值
    torch.add(A,B)相加,A和B既可以是Tensor也可以是标量
    torch.clamp(A,max,min)裁剪,A中的数据若小于min或大于max,则变成min或max,即保证范围在[min,max]
    torch.div(A,B)相除,A%B,A和B既可以是Tensor也可以是标量
    torch.mul(A,B)点乘,A*B,A和B既可以是Tensor也可以是标量
    torch.pow(A,n)求幂,A的n次方
    torch.mm(A,B.T)矩阵叉乘,注意与torch.mul之间的区别
    torch.mv(A,B)矩阵与向量相乘,A是矩阵,B是向量,这里的B需不需要转置都是可以的
    A.item()将Tensor转化为基本数据类型,注意Tensor中只有一个元素的时候才可以使用,一般用于在Tensor中取出数值
    A.numpy()将Tensor转化为Numpy类型
    A.size()查看尺寸
    A.shape查看尺寸
    A.dtype查看数据类型
    A.view()重构张量尺寸,类似于Numpy中的reshape
    A.transpose(0,1)行列交换
    A[1:]A[-1,-1]=100切面,类似Numpy中的切面
    A.zero_()归零化
    torch.stack((A,B),sim=-1)拼接,升维
    torch.diag(A)取A对角线元素形成一个一维向量
    torch.diag_embed(A)将一维向量放到对角线中,其余数值为0的Tensor

    2.2 初始化

    • 张量可以直接从数据中创建。数据类型是自动推断的
    import torch
    
    # 直接从数据创建张量
    data = [[1, 2], [3, 4]]
    x_data = torch.tensor(data)
    print(f"Tensor from Data:\n {x_data} \n")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 从 NumPy 数组创建
    import numpy as np
    
    # 从numpy创建张量
    data = [[1, 2], [3, 4]]
    np_array = np.array(data)
    x_np = torch.from_numpy(np_array)
    print(f"Tensor from Numpy:\n {x_np} \n")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 根据另一个张量创建
    import torch
    
    # 根据另一个张量创建
    data = [[1, 2], [3, 4]]
    x_data = torch.tensor(data)
    
    x_ones = torch.ones_like(x_data)  # 保留原有张量的形状和数据类型
    print(f"Ones Tensor: \n {x_ones} \n")
    
    x_rand = torch.rand_like(x_data, dtype=torch.float)  # 显式更改张量的数据类型
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 使用随机或恒定值创建.
    import torch
    
    shape = (2, 3,)  # 创建2行3列的张量
    rand_tensor = torch.rand(shape)
    ones_tensor = torch.ones(shape)
    zeros_tensor = torch.zeros(shape)
    
    print(f"Random Tensor: \n {rand_tensor} \n")
    print(f"Ones Tensor: \n {ones_tensor} \n")
    print(f"Zeros Tensor: \n {zeros_tensor}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.3 张量的属性

    • 张量属性包括形状、数据类型和存储设备等
    import torch
    
    tensor = torch.rand(3,4)
    print(f"Shape of tensor: {tensor.shape}")
    print(f"Datatype of tensor: {tensor.dtype}")
    print(f"Device tensor is stored on: {tensor.device}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.4 ndarray与tensor互转

    import numpy as np
    import torch
    
    print(np.__version__)
    print(torch.__version__)
    
    print("tensor转ndarray")
    a = torch.ones(5)
    print(type(a))
    b = a.numpy()
    print(type(b))
    
    print("ndarray转tensor")
    a1 = np.ones(5)
    print(type(a1))
    b2 = torch.from_numpy(a1)
    print(type(b2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.5 索引、切片、变形、聚合、矩阵拼接、切割、转置

    import torch
    import numpy as np
    
    # 随机生成0-10的形状为4行5列的tensor
    t = torch.randint(0, 10, (4, 5))
    print(t)
    
    # 查看形状
    print(t.shape)
    
    # 一 索引和切片
    # 取第0行0列的数据
    print(t[0, 0])
    
    # 取第0行的数据
    print(t[0])
    
    # 取中间两行的数据
    print(t[1:3])
    
    # 取中间两列的数据
    print(t[:, 1:3])
    
    # 取中间两行和两列的数据
    print(t[1:3, 1:3])
    
    # 增加维度 变为 (4,5,1)
    # 方法一
    print(t.reshape(4, 5, 1))
    # 方法二 None表示占个位置
    print(t[:, :, None])
    # 方法三 ...代表前面所有维度,None代表最后一个维度
    print(t[..., None])
    # 方法四 在中间插一个维度 (4,1,5)
    print(t[:, None, :])
    
    # 去除空白的维度
    print(t.reshape(4, 5, 1).squeeze())
    print(t[:, None, :].squeeze())
    
    # 拉伸维度
    print(t.unsqueeze(dim=0).shape)
    
    tensor = torch.tensor([[1], [2], [3]])
    print(tensor.expand(3, 4))
    
    # 模拟卷积神经网络只取宽度和高度
    # 32个通道,224宽,224高,3个卷积核(batch_size,W,H,C)
    # numpy操作
    n = np.random.random((32, 224, 224, 3))
    print(n[0, :, :, 0].shape)
    # torch操作
    t = torch.tensor(n)
    print(t.shape)
    print(t[0, :, :, 0].shape)
    
    # 二、变形 reshape和view
    t = torch.randint(0, 10, (4, 5))
    print(t)
    # print(t.reshape(5,4))
    # print(t.view(5,4))
    
    # 三、聚合
    # pytorch聚合的时候不指定维度和numpy表现是一样的,回把所有的维度聚合成一个数字
    print(t.sum())
    # dim(dimension)指定维度
    # 行相加
    print(t.sum(dim=0))
    # 保持聚合掉的维度继续存在
    print(t.sum(dim=0,keepdim=True))
    # 列相加
    print(t.sum(dim=1))
    # 保持聚合掉的维度继续存在
    print(t.sum(dim=1,keepdim=True))
    # 聚合函数 max,min,mean,median,sum,argmin(最小值索引),argmax(最大值索引),std(标准偏差)
    # 第一个维度最大值得索引(每列的最大值的索引)
    print(t.argmax(dim=0))
    # 第二个维度最大值得索引(每行的最大值的索引)
    print(t.argmax(dim=1))
    
    # 四、矩阵的拼接
    t1 = torch.randint(0,10,size=(4,5))
    t2 = torch.randint(0,10,size=(4,5))
    print(t1)
    print(t2)
    # pytorch也有dot,但是,仅限于向量(一维)之间的运算。(numpy中不限于一维)
    # 一维向量的点乘
    # t1[0,0]*t2[0,0] + t1[0,1]*t2[0,1] + t1[0,2]*t2[0,2] + t1[0,3]*t2[0,3] + t1[0,4]*t2[0,4]
    print(t1[0].dot(t2[0]))
    # 默认也是按照dim=0,增加了行数
    print(torch.concat((t1,t2)))
    # 同样增加行数
    print(torch.vstack((t1,t2)))
    # 增加列
    print(torch.concat((t1,t2),dim=1))
    # 同样增加列数
    print(torch.hstack((t1,t2)))
    
    # 五、切割
    print("--------------切割----------------")
    t = torch.randint(0,10,size=(4,5))
    print(t)
    # 1+2+1=4,必须等于维度
    print(t.split([1,2,1]))
    
    # 六、转置
    # 0维度和1维度互换
    print(torch.permute(t,[1,0]).shape)
    print(t.transpose(1,0).shape)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    3 pytorch自动微分

    • 自动微分其实就是求导
    import torch
    
    # requires_grad=True 表示对x求导
    x = torch.ones(1, 1, requires_grad=True)
    
    # 写一个计算公式,倒数是2
    y = 2 * x + 2
    
    # 反向传播求导
    y.backward()
    
    # 这里也是2
    print(x.grad)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4 线性回归

    from torch import nn, optim, tensor
    
    # y = 2*x + 1
    X = tensor([[1.0], [2.0], [3.0], [4.0]])
    Y = tensor([[3.0], [5.0], [7.0], [9.0]])
    
    # 训练模型(线性),可以获得预测结果。
    model = nn.Linear(1, 1)
    
    # 定义损失函数(均方误差损失(Mean Square Error Loss)),传入实际值和预测值,就可以获得损失函数。
    # 这是常用于回归问题的损失函数
    loss_fn = nn.MSELoss()
    
    # 需要更新的参数-这里是一个生成器,可以节约内存
    # model.parameters()
    
    # 优化器(训练器) lr是学习率,可以梯度清零和更新参数。
    optimizer = optim.SGD(model.parameters(), 0.001)
    
    # 训练
    for epoch in range(1000):
        for x, y in zip(X, Y):
            # 获得预测结果
            y_pred = model(x)
            # 获得损失函数,使用损失函数做反向传播
            loss = loss_fn(y, y_pred)
            # 梯度清零
            optimizer.zero_grad()
            # 反向传播
            loss.backward()
            # 更新参数
            optimizer.step()
    
    # w的值
    weight = model.weight
    print(weight)
    # b的值
    bias = model.bias
    print(bias)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    5 分类

    5.1 写法一

    import torch
    from torch import nn,float
    
    # 1代表基数 2代表偶数
    data = [[1, 3, 5, 7, 9, 1],
            [2, 4, 6, 8, 10, 0],
            [11, 13, 15, 17, 19, 1],
            [12, 14, 16, 18, 20, 0],
            [21, 23, 25, 27, 29, 1],
            [22, 24, 26, 28, 30, 0],
            [31, 33, 35, 37, 39, 1],
            [32, 34, 36, 38, 40, 0],
            [41, 43, 45, 47, 49, 1],
            [42, 44, 46, 48, 50, 0], ]
    t_data = torch.tensor(data,dtype=float)
    
    # 行都要,列不要最后一行
    X = t_data[:, :-1]
    print(type(X))
    print(X)
    # 行都要,列只要最后一样
    Y = t_data[:, -1]
    Y = Y.reshape(-1, 1)
    print(type(X))
    print(Y)
    
    # 查看特征数,也就是有多少列
    print(X.shape)
    
    # 建一个两层的模型
    model = nn.Sequential(
        # 输入是5个特征(X的列数),输出50个神经元
        nn.Linear(5, 50),
        # 输如的神经元数就是上一层的输出50个,输出就是Y的列数
        nn.Linear(50, 1),
        # 激活函数(分类),将这个输出转化为一个在0和1之间的值。这个值可以看作是模型对输入数据的预测结果。
        nn.Sigmoid()
    )
    
    state_dict = model.state_dict()
    print(state_dict)
    
    # 二分类交叉熵(适用于二分类问题的一种损失函数)
    loss_fn = nn.BCELoss()
    
    # 优化器-梯度下降的优化算法,传入模型的参数为权重(w)和偏执(b),学习率为0.001
    optim_sgd = torch.optim.SGD(model.parameters(), 0.001)
    
    # 正常神经网络是一批一批跑的
    batch_size = 2
    # 整除 根据批次大小计算步长,一共10行数据,每次跑两行,正好步长是5
    steps = 10 // 2
    
    # 开始训练
    # 训练100次
    for epoch in range(100):
        # 防止数据量过大,按照步长跑,把大数据切成一段一段的跑。
        for batch in range(steps):
            # 每一批的起始位置
            start = batch * batch_size
            end = start + batch_size
            x = X[start:end]
            y = Y[start:end]
    
            # 拿到预测结果
            y_pred = model(x)
            # 获取损失函数
            loss = loss_fn(y_pred, y)
            # 梯度清零
            optim_sgd.zero_grad()
            # 反向传播
            loss.backward()
            # 更新
            optim_sgd.step()
    
    # 查看权重
    print(model.state_dict())
    
    # 计算准确率
    # >=0.5 就是正样本
    acc_rate = ((model(X).data.numpy() >= 0.5) == Y.numpy()).mean()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    5.2 写法二

    import torch
    from torch import nn, float
    import numpy as np
    
    # 1代表基数 2代表偶数
    data = [[1, 1],
            [2, 0],
            [11, 1],
            [12, 0],
            [21, 1],
            [22, 0],
            [31, 1],
            [32, 0],
            [41, 1],
            [42, 0], ]
    t_data = torch.tensor(data, dtype=float)
    
    # 行都要,列不要最后一行
    X = t_data[:, :-1]
    
    # 行都要,列只要最后一样
    Y = t_data[:, -1]
    Y = Y.reshape(-1, 1)
    
    # 查看特征数,也就是有多少列
    print(X.shape)
    
    
    class DemoModl(nn.Module):
        def __init__(self):
            super().__init__()
            # 定义网络中会用到的东西
            # 输入是5个特征(X的列数),输出50个神经元
            self.lin_1 = nn.Linear(1, 50)
            # 输如的神经元数就是上一层的输出50个,输出就是Y的列数
            self.lin_2 = nn.Linear(50, 1)
            # 激活函数(分类),将这个输出转化为一个在0和1之间的值。这个值可以看作是模型对输入数据的预测结果。
            self.sigmod = nn.Sigmoid()
            # 拐弯函数
            self.activate = nn.ReLU()
    
        def forward(self, input):
            # forward中写前向传播
            x = self.lin_1(input)
            x = self.activate(x)
            x = self.lin_2(x)
            x = self.sigmod(x)
            return x
    
    
    # 学习率
    lr = 0.001
    
    # 获取模型函数
    def get_model():
        model = DemoModl()
        # 返回模型和优化器
        return model, torch.optim.Adam(model.parameters(),lr=lr)
    
    # 损失函数
    loss_fn = nn.BCELoss()
    
    # 获取模型和优化器
    model,opt = get_model()
    # 超参数初始化
    batch_size = 2
    steps = 10 // 2
    
    # 训练100次
    for epoch in range(1000):
        # 防止数据量过大,按照步长跑,把大数据切成一段一段的跑。
        for batch in range(steps):
            # 每一批的起始位置
            start = batch * batch_size
            end = start + batch_size
            x = X[start:end]
            y = Y[start:end]
    
            # 拿到预测结果
            y_pred = model(x)
            # 获取损失函数
            loss = loss_fn(y_pred, y)
            # 梯度清零
            opt.zero_grad()
            # 反向传播
            loss.backward()
            # 更新
            opt.step()
    
    print('loss=========',loss_fn(model(X),Y))
    
    acc_rate = ((model(X).data.numpy() >= 0.5) == Y.numpy()).mean()
    print(acc_rate)
    
    print(np.unique(model(X).data.numpy()))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    6 CNN手写数字识别

    import matplotlib.pyplot as plt
    import torch
    from torchvision import datasets, transforms
    from torch import nn
    import torch.nn.functional as F
    from tqdm import tqdm
    
    # ToTensor有三个作用
    # 1、把数据转化为tensor
    # 2、把数据转化成0到1之间的小数
    # 3、会把图片你的通道数放到第一个维度
    transformation = transforms.Compose([transforms.ToTensor(), ])
    
    # 训练数据
    train_ds = datasets.MNIST(root="./../data/", train=True, download=True, transform=transformation)
    print(f"训练数据条数{len(train_ds)}")
    # 测试数据
    test_ds = datasets.MNIST(root="./../data/", train=False, download=True, transform=transformation)
    print(f"测试数据条数{len(test_ds)}")
    
    x, y = train_ds[0]
    print(f"y值是{y}")
    # 对图片进行数据处理
    print(x.shape)
    # squeeze变成二维的(1, 28, 28)-->(28,28)
    plt.imshow(x.squeeze().numpy(), cmap='gray')
    # 显示图片  图片显示也是5
    plt.show()
    
    # 变成data loadr
    train_dl = torch.utils.data.DataLoader(train_ds, batch_size=64, shuffle=True)
    test_dl = torch.utils.data.DataLoader(test_ds, batch_size=256)
    
    
    # 创建模型
    class Model(nn.Module):
        def __init__(self):
            super().__init__()
            # 写init按照模型结果顺序去写
            # 黑白图片,输入通道为1,输出通道为32,卷积核3*3
            self.conv1 = nn.Conv2d(1, 32, 3)  # in: 64(batch_size),1(黑白),28(width),28(height) -> out: 64,32,26,26
            # 池化 卷积2*2,步长2
            self.pool = nn.MaxPool2d(2, 2)  # out: 64,32,13,13
            # 第二个卷积核
            self.conv2 = nn.Conv2d(32, 64,
                                   3)  # out: 64,64,11,11 -> maxpool: 64,64,5,5   (W(宽)-F(核数)+1)/S(步长) = 11-3+1/2=5.5(向上取整)
            # 全链接
            self.linear_1 = nn.Linear(64 * 5 * 5, 256)
            self.linear_2 = nn.Linear(256, 10)
            # 将二维或三维的张量展平为一维的向量。
            # 例如,如果你有一个 2D 张量,其形状为 [batch_size, height * width],那么使用 nn.Flatten() 后,它将变成一个 1D 张量,形状为 [batch_size, height * width]。
            # 这个函数通常在将卷积层的输出送入全连接层或其他需要将数据展平为一维向量的层之前使用
            self.flatten = nn.Flatten()
    
        def forward(self, input1):
            # conv1卷积 + relu激活(引入非线性,解决梯度消失,提高模型性能)
            x = F.relu(self.conv1(input1))
            # 池化 (降低特征图的维度,减少信息冗余,提高训练速度,提高所提取特征的鲁棒性)
            x = self.pool(x)
            # 卷积
            x = F.relu(self.conv2(x))
            # 池化
            x = self.pool(x)
            # 批次保持不变,转变为一维向量 ,变成二维 [batch_size, height * width]
            x = self.flatten(x)
            # 全连接
            x = F.relu(self.linear_1(x))
            x = self.linear_2(x)
            return x
    
    
    # 创建模型
    model = Model()
    # 损失函数(多分类的损失函数)
    loss_fn = torch.nn.CrossEntropyLoss()
    # 优化器
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    
    
    def train_one_epoch(epoch, model, train_dl, test_dl):
        correct = 0
        total = 0
        running_loss = 0
        for x, y in tqdm(train_dl,desc="训练"):
            # 预测值
            y_pred = model(x)
            # 损失
            loss = loss_fn(y_pred, y)
            # 梯度清零
            optimizer.zero_grad()
            # 反向传播
            loss.backward()
            # 调整
            optimizer.step()
    
            # 计算一下训练的损失和准确率
            with torch.no_grad():
                y_ = torch.argmax(y_pred, dim=1)
                correct += (y_ == y).sum().item()
                total += y.size(0)
                running_loss += loss.item()
    
        epoch_loss = running_loss / len(train_dl.dataset)
        epoch_acc = correct / total
    
        # 测试部分代码
        test_correct = 0
        test_total = 0
        test_running_loss = 0
    
        with torch.no_grad():
            for x, y in tqdm(test_dl,desc="测试"):
                # 预测值
                y_pred = model(x)
                # 损失
                loss = loss_fn(y_pred, y)
                y_ = torch.argmax(y_pred, dim=1)
                test_correct += (y_ == y).sum().item()
                test_total += y.size(0)
                test_running_loss += loss.item()
    
        test_epoch_loss = test_running_loss / len(test_dl.dataset)
        test_epoch_acc = test_correct / test_total
    
        print("epoch===", epoch,"loss===", round(epoch_loss, 3),"accuracy===", round(epoch_acc, 3),"test_loss===", round(test_epoch_loss, 3),"test_accuracy===", round(test_epoch_acc, 3))
        return epoch_loss, epoch_acc, test_epoch_loss, test_epoch_acc
    
    
    epochs = 20
    train_loss = []
    train_acc = []
    test_loss = []
    test_acc = []
    
    for epoch in range(epochs):
        epoch_loss, epoch_acc, test_epoch_loss, test_epoch_acc = train_one_epoch(epoch, model, train_dl, test_dl)
        train_loss.append(epoch_loss)
        train_acc.append(epoch_acc)
        test_loss.append(test_epoch_loss)
        test_acc.append(test_epoch_acc)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140

    7 自定义数据集

    自定义数据集要求实现两个方法

    • _len_返回数据集的长度
    • _getitem_返回数据集中的一项内容
    • 自定义的数据集需要返回图片,以及图片对应的标记
    from torchvision import transforms
    import torch.nn.functional as F
    import torch
    from torch import nn
    import torchvision
    import os
    import numpy as np
    from sklearn.model_selection import train_test_split
    from PIL import Image
    import matplotlib.pyplot as plt
    
    # 自定义的数据集的图片标记
    species = ["男", "女"]
    # 使用字典推导式,建立类别与索引之间的映射关系
    species_to_idx = dict((i, c) for i, c in enumerate(species))
    print(species_to_idx)
    # 获取指定路径下的文件名称
    filenames = os.listdir("./../dataset")
    print(filenames)
    
    # 生成所有图片的label
    all_labels = []
    for img in filenames:
        for i, c in enumerate(species):
            # 如果包含label
            if c in img:
                all_labels.append(i)
    print(all_labels)
    
    # 打乱数据
    index = np.random.permutation(len(all_labels))
    filenames = np.array(filenames)[index]
    all_labels = np.array(all_labels)[index]
    print(filenames)
    print(all_labels)
    
    # (方法一)手动划分训练集和测试集 (80%为训练集20%为测试集)
    # split = int((len(filenames) * 0.8))
    # print(split)
    # 训练集
    # train_imgs = filenames[:split]
    # train_labels = all_labels[:split]
    # print(train_imgs)
    # 测试集
    # test_imgs = filenames[split:]
    # test_labels = all_labels[split:]
    # print(test_imgs)
    # (方法二) 使用train_test_split(参数一:X,参数二:Y,参数三:分割值)
    x_train, x_test, y_train, y_test = train_test_split(filenames, all_labels, test_size=0.25)
    print(x_train, x_test, y_train, y_test)
    
    # 对图片的操作
    transform = transforms.Compose([
        transforms.Resize((96, 96)),
        transforms.ToTensor(),
        # 正则化
        transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
    ])
    
    
    class MyDataset(torch.utils.data.Dataset):
        def __init__(self, img_paths, labels, transform):
            # super.__init__()
            self.imgs = img_paths
            self.labels = labels
            # transform处理的图片要求是正常的颜色空间即RGB颜色空间的图片
            self.transforms = transform
    
        def __getitem__(self, index):
            # 返回指定index的样本
            base_dir = "./../dataset"
            img_path = os.path.join(base_dir, self.imgs[index])
            label = self.labels[index]
    
            # 通过路径获取图像(类型RGB)
            img = Image.open(img_path)
            # 检查一下图片的通道数
    
            # 自定义数据集的时候,需要对图片做一些检查.有可能读进来的图片,不是符合要求.
            # #检查一下图片的通道数
            if np.array(img).shape[-1] == 3:
                data = self.transforms(img)
                return data, label
            else:
                # 图片有问题.去访问下一张图片
                # return self.__getitem__(index + 1)
                return None
            # if np.array(img).shape[-1] != 3:
            #     print("图片有损坏")
            #     print(img_path)
            # 对图片处理(变成tensor,变换大小,正则化)
            # data = self.transforms(img)
            # print(data)
            # print(data.shape)
            # print(label)
            # return data, label
    
        def __len__(self):
            # 返回样本长度
            return len(self.imgs)
    
        # collate_fn是个独立的函数,一般写成静态函数,放到自定义的dataset类中
        # 实例函数,类函数,静态函数
        @staticmethod
        def collate_fn(batch):
            # batch是个列表,长度是batch_size
            # 列表里面每一个元素是一个元组(x,y)
            # [(x1,y1),(x2,y2)... (xn,yn)]
            # 需要把所有的x取出来,拼到一起,需要把所有的y取出来,拼接到一起.#[(x1, y1).. None,..(xn,yn)]
            # 对batch中的None做了过滤.
            batch = [sample for sample in batch if sample is not None]
            from torch.utils.data.dataloader import default_collate
            return default_collate(batch)
    
    
    train_ds = MyDataset(x_train, y_train, transform)
    test_ds = MyDataset(x_test, y_test, transform)
    # img, label = train_ds[0]
    
    # 查看图片是否正确,permute变换维度,从(3,96,96)-->(96,96,3)
    # plt.imshow(img.permute(1, 2, 0).numpy())
    # plt.show()
    
    # 将数据集放到加载器中
    train_dl = torch.utils.data.DataLoader(train_ds, batch_size=1, shuffle=True, drop_last=True,
                                           collate_fn=MyDataset.collate_fn)
    test_dl = torch.utils.data.DataLoader(test_ds, batch_size=1, shuffle=True, drop_last=True,
                                          collate_fn=MyDataset.collate_fn)
    
    for x, y in train_dl:
        print(x.shape, y.shape)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131

    8 学习迁移

    • 这里利用了上面的自定义数据集
    from numpy.compat import long
    from torchvision import transforms
    import torch.nn.functional as F
    import torch
    from torch import nn
    import torchvision
    import os
    import numpy as np
    from sklearn.model_selection import train_test_split
    from PIL import Image
    import matplotlib.pyplot as plt
    from tqdm import tqdm
    
    # 自定义的数据集的图片标记
    species = ["nan", "nv"]
    # 使用字典推导式,建立类别与索引之间的映射关系
    species_to_idx = dict((i, c) for i, c in enumerate(species))
    print(species_to_idx)
    # 获取指定路径下的文件名称
    filenames = os.listdir("./../dataset")
    print(filenames)
    
    # 生成所有图片的label
    all_labels = []
    for img in filenames:
        for i, c in enumerate(species):
            # 如果包含label
            if c in img:
                all_labels.append(i)
    print(all_labels)
    
    # 打乱数据
    index = np.random.permutation(len(all_labels))
    filenames = np.array(filenames)[index]
    all_labels = np.array(all_labels)[index]
    print(filenames)
    print(all_labels)
    
    # (方法一)手动划分训练集和测试集 (80%为训练集20%为测试集)
    # split = int((len(filenames) * 0.8))
    # print(split)
    # 训练集
    # train_imgs = filenames[:split]
    # train_labels = all_labels[:split]
    # print(train_imgs)
    # 测试集
    # test_imgs = filenames[split:]
    # test_labels = all_labels[split:]
    # print(test_imgs)
    # (方法二) 使用train_test_split(参数一:X,参数二:Y,参数三:分割值)
    x_train, x_test, y_train, y_test = train_test_split(filenames, all_labels, test_size=0.25)
    print(x_train, x_test, y_train, y_test)
    
    # 对图片的操作
    transform = transforms.Compose([
        transforms.Resize((96, 96)),
        transforms.ToTensor(),
        # 正则化
        transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
    ])
    
    
    class MyDataset(torch.utils.data.Dataset):
        def __init__(self, img_paths, labels, transform):
            # super.__init__()
            self.imgs = img_paths
            self.labels = labels
            # transform处理的图片要求是正常的颜色空间即RGB颜色空间的图片
            self.transforms = transform
    
        def __getitem__(self, index):
            # 返回指定index的样本
            base_dir = "./../dataset"
            img_path = os.path.join(base_dir, self.imgs[index])
            label = self.labels[index]
    
            # 通过路径获取图像(类型RGB)
            img = Image.open(img_path)
            # 检查一下图片的通道数
    
            # 自定义数据集的时候,需要对图片做一些检查.有可能读进来的图片,不是符合要求.
            # #检查一下图片的通道数
            if np.array(img).shape[-1] == 3:
                data = self.transforms(img)
                # 转换为int64类型
                label = label.astype(np.int64)
                return data, label
            else:
                # 图片有问题.去访问下一张图片
                return self.__getitem__(index + 1)
                # return None
            # if np.array(img).shape[-1] != 3:
            #     print("图片有损坏")
            #     print(img_path)
            # 对图片处理(变成tensor,变换大小,正则化)
            # data = self.transforms(img)
            # print(data)
            # print(data.shape)
            # print(label)
            # return data, label
    
        def __len__(self):
            # 返回样本长度
            return len(self.imgs)
    
        # collate_fn是个独立的函数,一般写成静态函数,放到自定义的dataset类中
        # 实例函数,类函数,静态函数
        @staticmethod
        def collate_fn(batch):
            # batch是个列表,长度是batch_size
            # 列表里面每一个元素是一个元组(x,y)
            # [(x1,y1),(x2,y2)... (xn,yn)]
            # 需要把所有的x取出来,拼到一起,需要把所有的y取出来,拼接到一起.#[(x1, y1).. None,..(xn,yn)]
            # 对batch中的None做了过滤.
            batch = [sample for sample in batch if sample is not None]
            from torch.utils.data.dataloader import default_collate
            return default_collate(batch)
    
    
    train_ds = MyDataset(x_train, y_train, transform)
    test_ds = MyDataset(x_test, y_test, transform)
    # img, label = train_ds[0]
    
    # 查看图片是否正确,permute变换维度,从(3,96,96)-->(96,96,3)
    # plt.imshow(img.permute(1, 2, 0).numpy())
    # plt.show()
    
    # 将数据集放到加载器中
    train_dl = torch.utils.data.DataLoader(train_ds, batch_size=1, shuffle=True, drop_last=True)
    test_dl = torch.utils.data.DataLoader(test_ds, batch_size=1, shuffle=True, drop_last=True)
    
    for x, y in train_dl:
        print(x.shape, y.shape)
    
    # pytorch放模型的地方
    # pretained=True,代表使用迁移学习,progress=True,下载模型(进度条)
    # model = torchvision.models.resnet34(pretrained=True, progress=True)
    model = torchvision.models.vgg16(pretrained=True, progress=True)
    print(model)
    # 把features层的参数全部固定住.
    for param in model.features.parameters():
        param.requires_grad = False
    # 改造最后一层,输入特征还是原来的,输出特征为2(男,女)
    model.classifier[-1] = nn.Linear(model.classifier[-1].in_features, 2)
    # 另一种改法,这是针对1000个类别的,不适用
    # model.classifier[-1].out_features = 4
    
    # 创建损失函数
    loss_fn = torch.nn.CrossEntropyLoss()
    # 优化器
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    
    
    # 训练
    def train_one_epoch(epoch, model, train_dl, test_dl):
        correct = 0
        total = 0
        running_loss = 0
        for x, y in tqdm(train_dl, desc="训练"):
            # 预测值
            y_pred = model(x)
            # 损失
            loss = loss_fn(y_pred, y)
            # 梯度清零
            optimizer.zero_grad()
            # 反向传播
            loss.backward()
            # 调整
            optimizer.step()
    
            # 计算一下训练的损失和准确率
            with torch.no_grad():
                y_ = torch.argmax(y_pred, dim=1)
                correct += (y_ == y).sum().item()
                total += y.size(0)
                running_loss += loss.item()
    
        epoch_loss = running_loss / len(train_dl.dataset)
        epoch_acc = correct / total
    
        # 测试部分代码
        test_correct = 0
        test_total = 0
        test_running_loss = 0
    
        with torch.no_grad():
            for x, y in tqdm(test_dl, desc="测试"):
                # 预测值
                y_pred = model(x)
                # 损失
                loss = loss_fn(y_pred, y)
                y_ = torch.argmax(y_pred, dim=1)
                test_correct += (y_ == y).sum().item()
                test_total += y.size(0)
                test_running_loss += loss.item()
    
        test_epoch_loss = test_running_loss / len(test_dl.dataset)
        test_epoch_acc = test_correct / test_total
    
        print("epoch===", epoch, "loss===", round(epoch_loss, 3), "accuracy===", round(epoch_acc, 3), "test_loss===",
              round(test_epoch_loss, 3), "test_accuracy===", round(test_epoch_acc, 3))
        return epoch_loss, epoch_acc, test_epoch_loss, test_epoch_acc
    
    
    epochs = 20
    train_loss = []
    train_acc = []
    test_loss = []
    test_acc = []
    
    for epoch in range(epochs):
        epoch_loss, epoch_acc, test_epoch_loss, test_epoch_acc = train_one_epoch(epoch, model, train_dl, test_dl)
        train_loss.append(epoch_loss)
        train_acc.append(epoch_acc)
        test_loss.append(test_epoch_loss)
        test_acc.append(test_epoch_acc)
    
    # 查看参数
    params = model.state_dict()
    print(params)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220

    9 保存模型和加载模型

    9.1 保存模型

    # 保存模型
    save_path = "./../model/my_vgg16.pth"
    torch.save(model.state_dict(), save_path)
    
    • 1
    • 2
    • 3

    9.2 加载模型

    # 加载模型
    # 得先把网络结构定义好.
    # 然后把网络的实例对象创建出来.
    # pretrained=False,表示只要模型,不要参数,不会下载
    vgg16 = torchvision.models.vgg16(pretrained=False, progress=True)
    #没有训练过的参数
    vgg16.state_dict()
    # 修改最后一层
    vgg16.classifier[-1] = nn.Linear(vgg16.classifier[-1].in_features,2)
    # 加载自己的模型参数
    vgg16.load_state_dict(torch.load('./../model/my_vgg16.pth'))
    print("已完成")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    9.3 保存最佳模型

    import copy
    
    # 最佳参数
    best_model_weight = model.state_dict()
    epochs = 2
    train_loss = []
    train_acc = []
    test_loss = []
    test_acc = []
    # 最佳准确率
    best_acc = 0.0
    
    for epoch in range(epochs):
        epoch_loss, epoch_acc, test_epoch_loss, test_epoch_acc = train_one_epoch(epoch, model, train_dl, test_dl)
        train_loss.append(epoch_loss)
        train_acc.append(epoch_acc)
        test_loss.append(test_epoch_loss)
        test_acc.append(test_epoch_acc)
    
        if test_epoch_acc > best_acc:
            best_acc = test_epoch_acc
            # 深拷贝
            best_model_weight = copy.deepcopy(model.state_dict())
            
    model.load_state_dict(best_model_weight)     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    9.4 保存完整模型

    #完整模型包括模型结构和参数
    torch. save (model,'./../model/whole_model.pth')
    
    • 1
    • 2

    9.5 加载完整模型

    #加载完整模型
    model2 = torch.load('./../model/whole_model.pth')
    
    • 1
    • 2

    10 数据增强

    transforms.CenterCrop 中间位置裁剪
    transforms.RandomCrop 随机位置裁剪
    transforms.RandomHorizontalFlip 水平翻转
    transforms.RandomVerticalFlip垂直翻转
    transforms. RandomRotation旋转
    transforms.ColorJitter(brigtness,contrast对比度,saturation饱和度, hue色调)(调整颜色的相关属性.transforms.RandomGrayscale随机灰度化)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    11 学习率衰减

    # 学习率衰减
    from torch.optim import lr_scheduler
    # setp_size步长为7,每7步学习率减少0.1
    step_lr_scheduler = lr_scheduler.StepLR(optimizer,step_size=7,gamma=0.1)
    
    # 在每批循环后添加
    step_lr_scheduler.step()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    更新SQLite数据库数据
    处理尚不存在的 DOM 节点
    【从零开始学习 UVM】2.2、UVM 基础功能 —— UVM utility & field macros(UVM实用宏和字段宏)
    《跟小海龟学Python》图书介绍、代码下载、视频教程
    【LeetCode】每一轮都要把输入数组看一遍的二分
    redis命令学习
    GitHub 最新发布的这份 Java 面试导图 + 面试手册,真不是吹的
    docker安装jenkins最新版
    糖尿病新世界杂志糖尿病新世界杂志社糖尿病新世界编辑部2022年第12期目录
    实现H5网页授权
  • 原文地址:https://blog.csdn.net/weixin_43684214/article/details/133485148