zoukankan      html  css  js  c++  java
  • 如何入门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之三:如何优化神经网络

  • 相关阅读:
    uva 10369 Arctic Network
    uvalive 5834 Genghis Khan The Conqueror
    uvalive 4848 Tour Belt
    uvalive 4960 Sensor Network
    codeforces 798c Mike And Gcd Problem
    codeforces 796c Bank Hacking
    codeforces 768c Jon Snow And His Favourite Number
    hdu 1114 Piggy-Bank
    poj 1276 Cash Machine
    bzoj 2423 最长公共子序列
  • 原文地址:https://www.cnblogs.com/jimchen1218/p/12021682.html
Copyright © 2011-2022 走看看