• 如何入门Pytorch之二:如何搭建实用神经网络


    上一节中,我们介绍了Pytorch的基本知识,如数据格式,梯度,损失等内容。

    在本节中,我们将介绍如何使用Pytorch来搭建一个经典的分类神经网络。

    搭建一个神经网络并训练,大致有这么四个部分:

    1、准备数据;

    2、搭建模型;

    3、评估函数;

    4、优化网络权重。

    先上一张模型结构图,基本包含了一个网络模型所包含的内容了。

     接下来依次介绍。

    一.数据准备

        这一部分内容在上一篇中已详细讲过,这里就不多赘述了。

    二.搭建模型

     1、层的概念(神经网络的基本组建单元)

    针对y=wx+b,搭建了一个简易的线性模型,如下:

    from torch.nn import Linear
    inp = Variable(torch.randn(1,10))
    myLayer = Linear(in_features=10,out_features=5,bias=True)
    myLayer(inp)

    myLayer.weight  #通过权重来访问
    Output :
    Parameter containing:

    -0.2386 0.0828 0.2904 0.3133 0.2037 0.1858 -0.2642 0.2862 0.2874 0.1141
    0.0512 -0.2286 -0.1717 0.0554 0.1766 -0.0517 0.3112 0.0980 -0.2364 -0.0442
    0.0776 -0.2169 0.0183 -0.0384 0.0606 0.2890 -0.0068 0.2344 0.2711 -0.3039
    0.1055 0.0224 0.2044 0.0782 0.0790 0.2744 -0.1785 -0.1681 -0.0681 0.3141
    0.2715 0.2606 -0.0362 0.0113 0.1299 -0.1112 -0.1652 0.2276 0.3082 -0.2745
    [torch.FloatTensor of size 5x10]

    myLayer.bias  #通过偏置来访问
    Output :
    Parameter containing:
    -0.2646
    -0.2232
    0.2444
    0.2177
    0.0897
    [torch.FloatTensor of size 5

    在不同的框架里,线性层有着不一样的名字,如:Dense或fully connected layers.

    一般情况下,为了解决实际问题,需要同时搭建多个线性层,就象下面这样:

    myLayer1 = Linear(10,5)
    myLayer2 = Linear(5,2)
    myLayer2(myLayer1(inp))

    其中,每层的参数都是不一样:

    Layers Weight1
    Layer1 3.0
    Layer2 2.0

    不过,只是简单的堆叠线性层并不能有效帮助网络学习到更多新的东西。这里举一个例子,如:

    Y=2(3X1)-2LinearLayers

    Y=6(X1)-1 LinearLayers

    为了解决上面这个问题,就需要引入非线性函数的概念,也就是激活函数。

    以下是一些常用的激活函数:

    Sigmoid :f(x)=1 /(1+e^(-x))
    Tanh:f(x)=(e^x-e^(-x)/(e^x+e^(-x)))
    ReLU:f(x)=max(0,x)
    Leaky ReLU:f(x)=x  x>=0  ;f(x)=ax  x<0 {a<0,1}

    sample_data = Variable(torch.Tensor([[1,2,-1,-1]]))
    myRelu = ReLU()
    myRelu(sample_data)
    Output:
    Variable containing:
    1 2 0 0
    [torch.FloatTensor of size 1x4]

    2、搭建一个深度学习算法

    以下是基于nn.Module搭建的简易模型类

    class MyFirstNetwork(nn.Module):
        def __init__(self,input_size,hidden_size,output_size):
            super(MyFirstNetwork,self).__init__()
            self.layer1 = nn.Linear(input_size,hidden_size)
            self.layer2 = nn.Linear(hidden_size,output_size)
        def __forward__(self,input):
            out = self.layer1(input)
            out = nn.ReLU(out)
            out = self.layer2(out)
            return out

    三.损失函数

    机器学习主要解决的问题有:二分类,多分类。,回归

    1、回归:使用线性网络的最近一层的其中一个输出值。

     一般情况下,回归的损失函数使用MSE(Mean Square Error)来描述

    loss = nn.MSELoss()
    input = Variable(torch.randn(3, 5), requires_grad=True)
    target = Variable(torch.randn(3, 5))
    output = loss(input, target)
    output.backward()

    2、分类:使用sigmoid激活函数(近0或1)作为最终输出值。也即2分类问题。

    对于分类,使用交叉熵损失函数,算法基本实现如下:

    def cross_entropy(true_label, prediction):
        if true_label == 1:
            return -log(prediction)
        else:
            return -log(1 - prediction)

    3、多分类:使用softmax层作为最终输出。

    loss = nn.CrossEntropyLoss()
    input = Variable(torch.randn(3, 5), requires_grad=True)
    target = Variable(torch.LongTensor(3).random_(5))
    output = loss(input, target)
    output.backward()

    常用损失函数:

    L1 loss   :正则化时使用   loss(input, target)=|input-target|
    loss = torch.nn.L1Loss(reduce=True,size_average=False)

    SmoothL1Loss:平滑L1Loss:在(-1,1)上平方Loss,其他情况是L1Loss
    loss = torch.nn.SmoothL1Loss(reduce=False,size_average=False)
    MSE Loss:回归时使用 
    loss = torch.nn.MSELoss(reduce=False,size_average=False)
    BCELoss
        loss = torch.nn.BCELoss(reduce=False,size_average=False)
    CrossEntropy loss:多分类时使用 
    loss = torch.nn.CrossEntropyLoss(reduce=False,size_average=False,weight=None)
    NLL Loss:非平衡数据集分类时使用   Log likelihood loss损失 ,用于训练一个n类分类器
    m=
    torch.nn.LogSoftmax()
    loss = torch.nn.NLLLoss()
    input=Variable(torch.randn(3,5),requires_grad=True)
    target=Variable(torch.LongTensor([1,0,4]))
    output=loss(m(input),target)
    NLL Loss2d:像素级分类时使用(语义分割)
    m=nn.Conv2d(16,32,(3,3)).float()
    loss = nn.NLLLoss2d()
    input = Variable(torch.randn(3,16,10,10)
    target = Variable(torch.LongTensor(3,8,8).random_(0,4))
    output = loss(m(input),target)
    MultiMarginLoss :适用于多分类模型
    x = Variable(torch.randn(3,10))
    y = Variable(torch.LongTensor(3).random_(10))
    loss = torch.nn.MultiMarginLoss()

    四.优化器

    常用优化器如下:

    ADADELTA
    Adagrad
    Adam
    SparseAdam
    Adamax
    ASGD
    LBFGS
    RMSProp
    Rprop
    SGD
    
    举例如下:
    optimizer = optim.SGD(model.parameters(), lr = 0.01)

    优化器的使用方法如下:

    for input, target in dataset:
    optimizer.zero_grad()
    output = model(input)
    loss = loss_fn(output, target)
    loss.backward()
    optimizer.step()

    五、搭建一个图像分类的深度学习模型

    解决任何问题前很重要的一步是获取数据,Kaggle上提供了大量竞赛用数据用于图像识别。

    1、这里制作了一套简单的数据集:dogsandcats,格式如下:

    dogsandcats/
      train/
        dog/
          dog.183.jpg
          dog.184.jpg
        cat/
          cat.17.jpg
          cat.2.jpg
       valid/
         dog/
           dog.192.jpg
           dog.132.jpg
         cat/
           cat.132.jpg
           cat.23.jpg

    2、读取数据集的脚本文件:

    path = './dogsandcats/'
    #Read all the files inside our folder.
    files = glob(os.path.join(path,'*/*.jpg'))  #获取文件夹中文件
    print(f'Total no of images {len(files)}')
    no_of_images = len(files)
    #Create a shuffled index which can be used to create a validation data set
    shuffle = np.random.permutation(no_of_images)  #打乱数据顺序
    #Create a validation directory for holding validation images.
    os.mkdir(os.path.join(path,'valid'))
    #Create directories with label names
    for t in ['train','valid']:   #新建两个文件夹,分别存放两个不同的类别
        for folder in ['dog/','cat/']:
            os.mkdir(os.path.join(path,t,folder))
    #Copy a small subset of images into the validation folder.
    for i in shuffle[:2000]:
        folder = files[i].split('/')[-1].split('.')[0]
        image = files[i].split('/')[-1]
        os.rename(files[i],os.path.join(path,'valid',folder,image))
    #Copy a small subset of images into the training folder.
    for i in shuffle[2000:]:
        folder = files[i].split('/')[-1].split('.')[0]
        image = files[i].split('/')[-1]
        os.rename(files[i],os.path.join(path,'train',folder,image))

    3、加载数据集进Tensor

    在Pytorch中,torchvision.datasets提供了一个接口ImageFolder可用来加载图片。

    基本功能三步曲:裁剪成统一尺寸,标准化数据集,转换成Tensor。

    simple_transform=transforms.Compose([transforms.Scale((224,224)),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],[0.229,0.224,0.225])
    train = ImageFolder('dogsandcats/train/',simple_transform) valid = ImageFolder('dogsandcats/valid/',simple_transform)

    train.class_to_idx - {'cat': 0, 'dog': 1}
    train.classes - ['cat', 'dog']

    对Tensor中图片进行可视化显示,是调试中不可缺少的一环。

    def imshow(inp):
    """Imshow for Tensor."""
        inp = inp.numpy().transpose((1, 2, 0))
        mean = np.array([0.485, 0.456, 0.406])
        std = np.array([0.229, 0.224, 0.225])
        inp = std * inp + mean
        inp = np.clip(inp, 0, 1)
        plt.imshow(inp)

    imshow(train[50][0]) #可以用来显示图片内容

    4、批量加载Tensor

    使用GPU训练深度学习模型时,为了提高芯片的利用率,一般一次性会导入多张图片。

    train_data_gen = torch.utils.data.DataLoader(train,batch_size=64,num_workers=3)
    valid_data_gen = torch.utils.data.DataLoader(valid,batch_size=64,num_workers=3)  #num_workers 表示并行处理数

    5、搭建一个网络模型

    在处理真实问题时,通常情况下,我们很少搭建全新的模型。目前已有很多成熟的方案。如ResNet,在目前的Pytorch中已经完全实现,只能简单调用就可以了。

    model_ft = models.resnet18(pretrained=True)
    num_ftrs = model_ft.fc.in_features
    model_ft.fc = nn.Linear(num_ftrs, 2)
    if is_cuda:
        model_ft = model_ft.cuda()

    ResNet在PyTorch实现:

    ResNet(
        (conv1):Conv2d(3,64,kernel_size=(7,7),stride=(2,2),padding=(3,3),bias=False)
        (bn1):BatchNorm2d(64,eps=1e-5,momentum=0.1,affine=True)
        (relu):ReLU(inplace)
        (maxpool):MaxPool2d(size=(3,3),stride-(2,2),padding=(1,1),dilation=(1,1))
        (layer1):Sequential(
           (0):BasicBlock(
               (conv1):Conv2d(64,64,kernel_size=(3,3),stride=(1,1),padding=(1,1),bias=False)
               (bn1):BatchNorm2d(64,eps=1e-5,momentum=0.1,affine=True)
               (relu):ReLU(inplace)
               (conv2):Conv2d(64,64,kernel_size=(3,3),stride=(1,1),padding=(1,1),bias=False)
               (bn2):BatchNorm2d(64,eps=1e-5,momentum=0.1,affine=True)
           (1):BasicBlock(
               (conv1):Conv2d(64,64,kernel_size=(3,3),stride=(1,1),padding=(1,1),bias=False)
               (bn1):BatchNorm2d(64,eps=1e-5,momentum=0.1,affine=True)
               (relu):ReLU(inplace)
               (conv2):Conv2d(64,64,kernel_size=(3,3),stride=(1,1),padding=(1,1),bias=False)
               (bn2):BatchNorm2d(64,eps=1e-5,momentum=0.1,affine=True)
     (layer2):Sequential(
           (0):BasicBlock(
               (conv1):Conv2d(64,128,kernel_size=(3,3),stride=(2,2),padding=(1,1),bias=False)
               (bn1):BatchNorm2d(128,eps=1e-5,momentum=0.1,affine=True)

    ResNet是基于数据集ImageNet来预测1000个分类的网络。所以,我们不能直接使用,为了能够用来处理我们的数据集,需要在最后一层把1000个分类改成这里的2类,

    model_ft.fc = nn.Linear(num_ftrs, 2)

    如果当前有可用的GPU,当前已成功安装了CUDA,使用前需要在代码里指定,否则默认会使用CPU。

    if is_cuda:
         model_ft = model_ft.cuda()

    6、训练模型

    # Loss and Optimizer
    learning_rate = 0.001
    criterion = nn.CrossEntropyLoss()
    optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)
    exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7,
    gamma=0.1)

    下面是完整的训练模型接口,加载一个模型并运行加个周期来提高我们算法。

    def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
        since = time.time()
        best_model_wts = model.state_dict()
        best_acc = 0.0
        for epoch in range(num_epochs):
            print('Epoch {}/{}'.format(epoch, num_epochs - 1))
            print('-' * 10)
            # Each epoch has a training and validation phase
            for phase in ['train', 'valid']:
                if phase == 'train':
                    scheduler.step()
                    model.train(True) # Set model to training mode
                else:
                    model.train(False) # Set model to evaluate mode
                running_loss = 0.0
                running_corrects = 0
                # Iterate over data.
                for data in dataloaders[phase]:
                    # get the inputs
                    inputs, labels = data
                    # wrap them in Variable
                    if is_cuda:
                        inputs = Variable(inputs.cuda())
                        labels = Variable(labels.cuda())
                    else:
                        inputs, labels = Variable(inputs), Variable(labels)
                    # zero the parameter gradients
                    optimizer.zero_grad()
                    # forward
                    outputs = model(inputs)
                    _, preds = torch.max(outputs.data, 1)
                    loss = criterion(outputs, labels)
                    # backward + optimize only if in training phase
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()
                    # statistics
                    running_loss += loss.data[0]
                    running_corrects += torch.sum(preds == labels.data)
               epoch_loss = running_loss / dataset_sizes[phase]
               epoch_acc = running_corrects / dataset_sizes[phase]
               print('{} Loss: {:.4f} Acc: {:.4f}'.format(
               phase, epoch_loss, epoch_acc))
               # deep copy the model
               if phase == 'valid' and epoch_acc > best_acc:
                   best_acc = epoch_acc
                   best_model_wts = model.state_dict()
            print()
        time_elapsed = time.time() - since
        print('Training complete in {:.0f}m {:.0f}s'.format(
        time_elapsed // 60, time_elapsed % 60))
        print('Best val Acc: {:4f}'.format(best_acc))
        # load best model weights
        model.load_state_dict(best_model_wts)
        return model

    一段时间后,训练LOG显示如下:

    Epoch 18/24
    ----------
    train Loss: 0.0044 Acc: 0.9877
    valid Loss: 0.0059 Acc: 0.8740
    Epoch 19/24
    ----------
    train Loss: 0.0043 Acc: 0.9914
    valid Loss: 0.0059 Acc: 0.8725
    Epoch 20/24
    ----------
    train Loss: 0.0041 Acc: 0.9932
    valid Loss: 0.0060 Acc: 0.8725
    Epoch 21/24
    ----------
    train Loss: 0.0041 Acc: 0.9937
    valid Loss: 0.0060 Acc: 0.8725
    Epoch 22/24
    ----------
    train Loss: 0.0041 Acc: 0.9938
    valid Loss: 0.0060 Acc: 0.8725
    Epoch 23/24
    ----------
    train Loss: 0.0041 Acc: 0.9938
    valid Loss: 0.0060 Acc: 0.8725
    Epoch 24/24
    ----------
    train Loss: 0.0040 Acc: 0.9939
    valid Loss: 0.0060 Acc: 0.8725
    Training complete in 27m 8s
    Best val Acc: 0.874000

    这是在Titan X GPU上训练了30分钟后的结果。后续会使用不同的技术可以更快地训练模型。

    上一篇:

         如何入门Pytorch之一:Pytorch基本知识介绍

    下一篇:

         如何入门Pytorch之三:如何优化神经网络

  • 相关阅读:
    POJ3613 k边最短路
    洛谷4014最大/小费用最大流
    POJ1734无向图求最小环
    洛谷4013数字梯形
    洛谷4147玉蟾宫
    洛谷4145上帝造题的七分钟2
    洛谷4092 树
    Nginx动静分离-tomcat
    nginx之Geoip读取地域信息模块
    Nginx与Lua开发
  • 原文地址:https://www.cnblogs.com/jimchen1218/p/12021682.html
Copyright © 2020-2023  润新知