• 用pytorch实现神经网络


    章节设置:
    一、实现自带数据并自定义神经网络进行训练
    二、使用自带的模型进行训练以及测试
    三、用自己定义神经网络

    全局设定,指定了设备

    事实证明,即使是1650这样的显卡也会比cpu运行得快很多,只要运行的时候显存够就行,在使用的时候有两种方式(注意——还有其他指定显卡的方式,比如cuda(),只是这种更通用,更熟悉而已):

    1. model.to(device)
    2. tensor变量.to(device)
    # 如果显卡可用,则用显卡进行训练
    device = "cuda" if torch.cuda.is_available() else "cpu"
    # device='cpu'
    print(f"Using {device} device")
    
    • 1
    • 2
    • 3
    • 4

    全局设定GPU的设备使用

    一、实现自带数据并自定义神经网络进行训练

    放在这个示例的理解:获得dataset数据–>利用加载器进行数据加载–>设计神经网络的结构–>定义优化器以及损失函数–>遍历数据进行梯度下降迭代求解最佳的梯度值–>用模型进行预测得到预测值
    通用理解:数据加载–>设计网络结构–>遍历数据进行梯度下降求解到损失值的最小值或者指定次数–>保存对应的梯度参数或者直接计算test情况
    通过pytorch设计网络结构,需要完成以下几点:

    1. 定义网络结构,初始化函数init
    2. 定义网络节点连接方式forward函数

    1. 获得dataset数据

    通过从内置的数据集中获取数据,和之前的方式一样,只不过内置的给你写好了torchvision.datasets.mnist.MNIST.py,以下解释相关变量的定义

    解释torchvision.datasets.mnist.MNIST.init.data相关定义以及是怎么来的

    test_data = torchvision.datasets.MNIST('./mnist',train=False)
    # test_x= Variable(torch.unsqueeze(test_data.test_data,dim=1),volatile=True).type(torch.FloatTensor)[:2000]/255.
    # 压缩纬度,将第一个纬度给压没了(可以理解为x的纬度)
    test_x = torch.unsqueeze(test_data.data, dim=1).type(torch.FloatTensor)[:2000] / 255. #取前2000个数据
    
    • 1
    • 2
    • 3
    • 4

    对于这里面的test_data.data的理解,torchvision.datasets.mnist.MNIST.__init__.data可以看他们的调用关系,它其实是通过MNIST类的__init__进行对象的建立,所以获得的是最终的对象,如下所示:

    对象=torchvision.datasets.mnist.MNIST.__init__()
    对象具有data属性,所以就可以调用对象.data
    综合起来就是: torchvision.datasets.mnist.MNIST.__init__.data
    
    • 1
    • 2
    • 3

    在python的类只要函数里面调用了self.属性=相关定义赋值,那么之后python的类就具有这样的功能。

    之所以要进行维度的拓展就是因为cnn的输入是1*28*28,所以数据需要转换为batch_size*1*28*28,而扩展纬度就是通过unsqueeze,而有时候需要进行维度的统一就是通过unsqueeze以及expand,所以下面对unsqueeze以及expand的使用方法进行探究

    • unsqueeze可以增加一个纬度,但是维度的siz只是1而已,而expand就可以将数据进行复制,将数据变为n
    import torch
    # 获得一开始的初始化数值:tensor([[a1,a2,a3]])
    nn1=torch.rand(1,3)
    print(nn1)
    # unsqueeze是解压的意思,在第i个维度上进行扩展,将其扩展为tensor([[[a1,a2,a3]]])
    nn1=nn1.unsqueeze(0)
    print("*"*100)
    print(nn1)
    #利用expand对数据进行扩展
    nn1=nn1.expand(1,3,3)
    print("*"*100)
    print(nn1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    扩展维度输出的结果

    2. 利用加载器进行数据加载

    分为训练数据以及测试,由于训练数据需要分批次以及打乱操作等。所以加载方式会有所不同,或者可以可以测试数据的加载更加简单粗暴一些:

    1. 训练集数据的加载:通过dataset加载数据集–>将数据集加载进加载器中(加载中可以设置一批次迭代的次数还有是否打乱)
    2. 测试集数据的加载:可以直接加载数据以及对应的目标值就行
    # 1. 训练数据的加载
    train_data=torchvision.datasets.MNIST(
        root='./mnist',
        train=True,
        transform=torchvision.transforms.ToTensor(),#从下载数据改变数据形式 #(0,1)   (0,255)
        download=DOWNLOAD_MNIST
    )
    train_loader=Data.DataLoader(dataset=train_data,batch_size=BATCH_SIZE,shuffle=True,num_workers=0) 
    
    # 2. 测试数据的加载
    #test不用像train一样分批次加载,同时也不用需要打乱,所以直接获得相关数据就行
    test_data = torchvision.datasets.MNIST('./mnist',train=False)
    # 压缩纬度,将第一个纬度给压没了(可以理解为x的纬度)
    test_x = torch.unsqueeze(test_data.data, dim=1).type(torch.FloatTensor)[:2000] / 255. #取前2000个数据 转换为batch_size*1*28*28
    test_y=test_data.targets[:2000]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3. 设计神经网络的结构

    将神经网络中核心函数init()以及forward()函数实现就行

    1. init()是实现网络中有哪些层的;
    2. forward()是实现网络中不同连接的层的连接关系的;
    class CNN(nn.Module):
        def __init__(self):
            super(CNN, self).__init__()
            self.conv1=nn.Sequential(
                nn.Conv2d(#(1*28*28) #这里的1表示通道
                    in_channels=1,
                    out_channels=16,
                    kernel_size=5,
                    stride=1,
                    padding=2, #如果 stride=1,padding=(kernel_size-1)/2=(5-1)/2
                ),# -->(16,28*28)
                nn.ReLU(),# -->(16,28*28)
                nn.MaxPool2d(kernel_size=2),# -->(16,14*14)
            )
            self.conv2=nn.Sequential(# (16,14*14)
                nn.Conv2d(16,32,5,1,2),#加工32层-->(32 14*14)
                nn.ReLU(),#-->(32 14*14)
                nn.MaxPool2d(2),#-->(32 7*7)
            )
            self.out=nn.Linear(32*7*7,10) #把前面的纬度32*7*7变成10
    
        def forward(self,x):
            x=self.conv1(x)
            x=self.conv2(x)  #(batch, 32 ,7,7)
            x=x.view(x.size(0),-1)  #(batch,32*7*7)
            output=self.out(x)
            return output
    
    • 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

    4. 定义优化器以及损失函数

    optimizer = torch.optim.Adam(cnn.parameters(),lr=LR) #Adam是梯度下降的一种方法,将神经网络的参数以及传播率传进去
    print(cnn.parameters())
    loss_func = nn.CrossEntropyLoss()  #损失函数的定义
    
    • 1
    • 2
    • 3

    5. 遍历数据进行梯度下降

    梯度下降+每到50次进行相关信息的输出或者一些处理

    梯度下降

    1. 确定函数中优化器以及损失函数都已经设置好了
    2. 损失函数定义以及调用loss = loss_func(output, y)
    3. 优化器的置零:optimizer.zero_grad()
    4. 反向传播:loss.backward()
    5. 梯度迭代更新:optimizer.step()

    批量输出相关信息或者批量处理数据

    批量处理数据,也就是epoch=50,这里没有设置相关的变量而是用if step % 50 == 0:#表示已经进行了50的倍数了进行判断了,并且这里只做了相关的输出,并没有其他的操作(比如有时候可以对方差做一个汇总使得信息的整体性更强)

    for epoch in range(EPOCH):
        for step, (x, y) in enumerate(train_loader):
            # 50*10  最后输出是分类的概率大小
            output = cnn(x)  # 得到网络中的输出数据
            loss = loss_func(output, y)  # 计算每一个网络的损失值
            optimizer.zero_grad()  # 在下一次求导之前将保留的grad清空
            loss.backward()  # 反向传播,计算梯度
            optimizer.step()  # 应用求导到优化器上去
    
            if step % 50 == 0:#表示已经进行了50的倍数了
                test_output = cnn(test_x)
                # 在分类问题中,通常需要使用max()函数对softmax函数的输出值进行操作,求出预测值索引,然后与标签进行比对,计算准确率。其中1表示纬度
                # 函数会返回两个tensor,第一个tensor是每行的最大值;第二个tensor是每行最大值的索引。我们需要的是第二个的索引,本来是tensor的格式,这里需要化为numpy格式
                pred_y = torch.max(test_output, 1)[1].data.numpy()
                # size()考虑到可能大小也是有纬度的,所以取的是第一个纬度
                # 这里的loss.data.numpy()和accuracy都是一纬的,如果是数组就直接把数据给输出了,这里也是可以的
                accuracy = sum(pred_y == test_y.data.numpy()) / test_y.size(0)
                print('Epoch: ', epoch, '| train loss: %.4f' % loss.data.numpy(), '| test accuracy: %.2f' % accuracy)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    6. 用模型进行预测得到预测值

    torch.max的使用见:https://blog.csdn.net/weixin_42295969/article/details/126352240?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22126352240%22%2C%22source%22%3A%22weixin_42295969%22%7D

    # 从测试数据中计算10个预测,[:10]表示列表中第一项到第10项
    test_output = cnn(test_x[:10])
    # 这里为什么是max从而获得预测值
    pred_y = torch.max(test_output, 1)[1].data.numpy() #返回的是最大值的索引,最大的概率值
    print(pred_y, 'prediction number')
    print(test_y[:10].numpy(), 'real number')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    代码:
    链接:https://pan.baidu.com/s/1m6GE2Qkx6YZEYUcCnHn0ow?pwd=cu0s
    提取码:cu0s

    二、使用自带的模型进行训练以及测试

    备注:与《一》使用相同的数据,代码在代码
    与前面的对比(括号内是这个进行的改动):1、获得dataset数据–>2、利用加载器进行数据加载–>3、设计神经网络的结构(直接导入已有的神经网络结构)–>4、定义优化器以及损失函数–>5、遍历数据进行梯度下降求解–>6、用模型进行预测得到预测值
    注意:核心关注3,这是与前面不同的地方,并且增加了6预测的对比,如果对预测感兴趣,可以参考6(文中已高亮标记)

    1-2 获得dataset数据–>利用加载器进行数据加载

    与之前的一样进行加载数据

    3. 神经网络的设计

    可以直接导入自带的神经网络,这里以导入ResNet网络为例:

    '''
        随着模型的加深,需要训练的模型参数量增加,相同的训练次数下模型训练准确率起来得更慢
    '''
    
    # model = alexnet(pretrained=False, num_classes=5).to(device) # 29.3%
    
    '''        VGG系列    '''
    # model = vgg11(pretrained=False, num_classes=5).to(device)   #  23.1%
    # model = vgg13(pretrained=False, num_classes=5).to(device)   # 30.0%
    # model = vgg16(pretrained=False, num_classes=5).to(device)
    
    
    '''        ResNet系列    '''
    # model = resnet18(pretrained=False, num_classes=5).to(device)    # 43.6%
    # model = resnet34(pretrained=False, num_classes=5).to(device)
    # model = resnet50(pretrained= False, num_classes=5).to(device)
    model = resnet101(pretrained=False, num_classes=5).to(device)   #  26.2%
    # model = resnet152(pretrained=False, num_classes=5).to(device)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    示例说明

    from torchvision.models import resnet18, resnet34,resnet50, resnet101, resnet152    # ResNet系列
    model = resnet101(pretrained=False, num_classes=5).to(device)   #  26.2%
    print(model)
    
    • 1
    • 2
    • 3

    模型输出结果

    4、定义优化器以及损失函数

    与前面一样的定义方法,先定义后调用

    5、遍历数据进行梯度下降求解

    迭代进行训练以及测试,其中训练的函数train里就保存了进行梯度下降求解的方法

    包含梯度下降求解法的train

    这里就将所有的数据训练了一轮就行,复杂的就会涉及到epoch以及batch_size的概念

    • 当一个完整的数据集通过了神经网络一次并且返回了一次,这个过程称为一个 epoch。
    • 在不能将数据一次性通过神经网络的时候,就需要将数据集分成几个 batch,所以下面的batch仅仅只是表示批次。
    # 定义训练函数,需要
    def train(dataloader, model, loss_fn, optimizer):
        size = len(dataloader.dataset)
        # 从数据加载器中读取batch(一次读取多少张,即批次数),X(图片数据),y(图片真实标签)。
        for batch, (X, y) in enumerate(dataloader):
            # 将数据存到显卡
            X, y = X.to(device), y.to(device)
    
            # 得到预测的结果pred
            pred = model(X)
    
            # 计算预测的误差
            # print(pred,y)
            loss = loss_fn(pred, y)
    
            # 反向传播,更新模型参数
            optimizer.zero_grad() #梯度清零
            loss.backward() #反向传播
            optimizer.step() #更新参数
    
            # 每训练10次,输出一次当前信息
            if batch % 10 == 0:
                loss, current = loss.item(), batch * len(X)
                print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    6、用模型进行预测得到预测值

    设置为测试模型并设置不计算梯度,进行测试数据集的加载,判断预测值与实际标签是否一致,统一正确信息个数

    设置为测试模型并设置不计算梯度

    # 将模型转为验证模式
    model.eval()
    # 测试时模型参数不用更新,所以no_gard()
    with torch.no_grad():
        # 加载数据加载器,得到里面的X(图片数据)和y(真实标签)
        for X, y in dataloader:
            加载数据
            pred = model(X)#进行预测
            # 预测值pred和真实值y的对比
            test_loss += loss_fn(pred, y).item()
            # 统计预测正确的个数
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()#返回相应维度的最大值的索引
    test_loss /= size
    correct /= size
    print(f"correct = {correct}, Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    7. 保存好训练好的模型

    torch.save(model.state_dict(), "model_resnet101.pth")
    print("Saved PyTorch Model Success!")
    
    • 1
    • 2

    pth文件保存
    代码:https://gitee.com/sxh_and_ll/AI-CV/tree/master/proj/%E4%BD%BF%E7%94%A8pytorch%E8%87%AA%E5%B8%A6%E6%A8%A1%E5%9E%8B%E8%AE%AD%E7%BB%83%E4%BB%A5%E5%8F%8A%E6%B5%8B%E8%AF%95

    三、使用自己定义神经网络

    使用自己定义的神经网络仅仅只是要写神经网络结构就是,只需要将模型继承nn.moudle类,另外实现init以及forward方法就行

  • 相关阅读:
    React搭建完整项目配置插件
    c++工程的一般命名规则
    zynq开发中的文件系统
    GeForce RTX 2080显卡驱动安装(linux系统)
    autopilot news
    CMU15445 (Fall 2019) 之 Project#1 - Buffer Pool 详解
    Android系统升级A/B分区OTA升级应用层调用UpdateEngine代码
    【数据结构】排序--选择排序(堆排序)
    LeetCode 2530. 执行 K 次操作后的最大分数:优先队列(贪心)
    人工智能深度学习框架六核64位3399开发板支持5G模块Android8.1
  • 原文地址:https://blog.csdn.net/weixin_42295969/article/details/126352278