精华内容
下载资源
问答
  • 优化参数规范
    千次阅读
    2020-02-18 20:42:02

    PyTorch | (1)初识PyTorch

    PyTorch | (2)PyTorch 入门-张量

    PyTorch | (3)Tensor及其基本操作

    PyTorch | (4)神经网络模型搭建和参数优化


    基于PyTorch深度学习框架用简单快捷的方式搭建出复杂的神经网络模型,同时让模型参数的优化方法趋于高效。如同使用PyTorch中的自动梯度方法一样,在搭建复杂的神经网络模型的时候,我们也可以使用PyTorch中已定义的类和方法,这些类和方法覆盖了神经网络中的线性变换、激活函数、卷积层、全连接层、池化层等常用神经网络结构的实现。在完成模型的搭建之后,我们还可以使用PyTorch提供的类型丰富的优化函数来完成对模型参数的优化,除此之外,还有很多防止模型在模型训练过程中发生过拟合的类。

    深度学习中的常见概念

    1:批量

      批量,即Batch,是深度学习中的一个重要概念。批量通常是指两个不同的概念——如果对应的是模型训练方法,那么批量指的是将所有数据处理完以后一次性更新权重或者参数的估计,如果对应的是模型训练中的数据,那么对应的是一次输入供模型计算用的数据量。这两个概念有着紧密的关系。

      基于批量概念的模型训练通常按照如下步骤:

      (1) 初始化参数

      (2) 重复以下步骤:处理所有数据 ,更新参数

      和批量算法相对应的是递增算法,其步骤如下:

      (1) 初始化参数

      (2) 重复以下步骤:处理一个或者一组数据点,更新参数

      我们看到,这里的主要区别是批量算法一次处理所有的数据;而在递增算法中,每处理一个或者数个观测值就要更新一次参数。这里“处理”和“更新”二词根据算法的不同有不同的含义。在后向传播算法中,“处理”对应的具体操作就是在计算损失函数的梯度变化曲线。如果是批量算法,则计算平均或者总的损失函数的梯度变化曲线;而如果是递增算法,则计算损失函数仅在对应于该观测值或者数个观测值时的梯度变化曲线。“更新”则是从已有的参数值中减去梯度变化率和学习速率的乘积。

    2:在线学习和离线学习

      在深度学习中,另外两个常见的概念是在线学习(Online Learning)和离线学习(Offline Learning)。在离线学习中,所有的数据都可以被反复获取,比如上面的批量学习就是离线学习的一种。而在在线学习中,每个观测值在处理以后会被遗弃,同时参数得到更新。在线学习永远是递增算法的一种,但是递增算法却既可以离线学习也可以在线学习。

      离线学习有如下几个优点:

    1. 对于任何固定个数的参数,目标函数都可以直接被计算出来,因此很容易验证模型训练是否在朝着所需要的方向发展。
    2. 计算精度可以达到任意合理的程度。
    3. 可以使用各种不同的算法来避免出现局部最优的情况。
    4. 可以采用训练、验证、测试三分法对模型的普适度进行验证。
    5. 可以计算预测值及其置信区间。

       在线学习无法实现上述功能,因为数据并没有被存储,不能反复获取,因此对于任何固定的参数集,无法在训练集上计算损失函数,也无法在验证集上计算误差。这就造成在线算法一般来说比离线算法更加复杂和不稳定。但是离线递增算法并没有在线算法的问题,因此有必要理解在线学习和递增算法的区别。

    3:偏移/阈值

      在深度学习中,采用sigmoid激活函数的隐藏层或者输出层的神经元通常在计算网络输入时加入一个偏移值,称为Bias。对于线性输出神经元,偏移项就是回归中的截距项。
      跟截距项的作用类似,偏移项可以被视为一个由特殊神经元引出的链接权重,这是因为偏移项通常链接到一个取固定单位值的偏移神经元。比如在一个多层感知器(MLP)神经网络中,某一个神经元的输入变量为N维,那么这个神经元在这个高维空间中根据参数画一个超平面,一边是正值,一边为负值。所使用的参数决定了这个超平面在输入空间中的相对位置。如果没有偏移项,这个超平面的位置就被限制住了,必须通过原点;如果多个神经元都需要其各自的超平面,那么就严重限制了模型的灵活性。这就好比一个没有截距项的回归模型,其斜率的估计值在大多数情况下会大大偏移最优估计值,因为生成的拟合曲线必须通过原点。因此,如果缺少偏移项,多层感知器的普适拟合能力就几乎不存在了。
      通常来说,每个隐藏层和输出层的神经元都有自己的偏移项。但是如果输入数据已经被等比例转换到一个有限值域中,比如[0,1]区间,那么第一个隐藏层的神经元设置了偏移项以后,后面任何层跟这些具备偏移项的神经元有链接的其他神经元就不需要再额外设置偏移项了。

    4:标准化数据

      在机器学习和深度学习中,常常会出现对数据标准化这个动作。那么什么是标准化数据呢?其实这里是用“标准化”这个词代替了几个类似的但又不同的动作。下面详细讲解三个常见的“标准化”数据处理动作。

    1. 重放缩(Rescaling):通常指将一个向量加上或者减去一个常量,再乘以或者除以一个常量。比如将华氏温度转换为摄氏温度就是一个重放缩的过程。
    2. 规范化(Normalization):通常指将一个向量除以其范数,比如采用欧式空间距离,则用向量的方差作为范数来规范化向量。在深度学习中,规范化通常采用极差为范数,即将向量减去最小值,并除以其极差,从而使数值范围在0到1之间。
    3. 标准化(Standardization):通常指将一个向量移除其位置和规模的度量。比如一个服从正态分布的向量,可以减去其均值,并除以其方差来标准化数据,从而获得一个服从标准正态分布的向量。

       那么在深度学习中是否应该进行以上任何一种数据处理呢?答案是依照情况而定。一般来讲,如果激活函数的值域在0到1之间,那么规范化数据到[0,1]的值域区间是比较好的。另外一个考虑是规范化数据能使计算过程更加稳定,特别是在数据值域范围区别较大的时候,规范化数据总是相对稳健的一个选择。而且很多算法的初始值设定也是针对使规范化以后的数据更有效来设计的。

    一:PyTorch之torch.nn

      PyTorch中的 torch.nn包提供了很多与实现神经网络中的具体功能相关的类,这些类涵盖了深度神经网络模型在搭建和参数优化过程中的常用内容,比如神经网络中的卷积层、池化层、全连接层这类层次构造的方法、防止过拟合的参数归一化方法、Dropout 方法,还有激活函数部分的线性激活函数、非线性激活函数相关的方法,等等。在学会使用PyTorch的 torch.nn进行神经网络模型的搭建和参数优化后,我们就会发现实现一个神经网络应用并没有我们想象中那么难。

    1.1 导入包

      下面使用PyTorch的torch.nn包来简化我们之前的代码,开始部分的代码变化不大,如下所示:

    #_*_coding:utf-8_*_
    import torch
    from torch.autograd import Variable
     
    # 批量输入的数据量
    batch_n = 100
    # 通过隐藏层后输出的特征数
    hidden_layer = 100
    # 输入数据的特征个数
    input_data = 1000
    # 最后输出的分类结果数
    output_data = 10
     
    x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
    y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)


    之前的代码如下:   和之前一样,这里首先导入必要的包、类并定义了4个变量,不过这里仅定义了输入和输出的变量,之前定义神经网络模型中的权重参数的代码被删减了,这和我们之后在代码中使用的torch.nn包中的类有关,因为这个类能够帮助我们自动生成和初始化对应维度的权重参数。

    #_*_coding:utf-8_*_
    import torch
    from torch.autograd import Variable
     
    # 批量输入的数据量
    batch_n = 100
    # 通过隐藏层后输出的特征数
    hidden_layer = 100
    # 输入数据的特征个数
    input_data = 1000
    # 最后输出的分类结果数
    output_data = 10
     
    x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
    y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)
     
    w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad = True)
    w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad = True)

     

    1.2 模型搭建

    models = torch.nn.Sequential(
        # 首先通过其完成从输入层到隐藏层的线性变换
        torch.nn.Linear(input_data,hidden_layer),
        # 经过激活函数
        torch.nn.ReLU(),
        # 最后完成从隐藏层到输出层的线性变换
        torch.nn.Linear(hidden_layer,output_data)
    )

    1.2.1 torch.nn.Sequential   torch.nn.Sequential括号内的内容就是我们搭建的神经网络模型的具体结构,这里首先通过torch.nn.Linear(input_data, hidden_layer)完成从输入层到隐藏层的线性变换,然后经过激活函数及torch.nn.Linear(hidden_layer, output_data)完成从隐藏层到输出层的线性变换。下面分别对在以上代码中使用的torch.nn.Sequential、torch.nn.Linear和torch.nn.RelU这三个类进行详细介绍

      torch.nn.Sequential类是torch.nn中的一种序列容器,通过在容器中嵌套各种实现神经网络中具体功能相关的类,来完成对神经网络模型的搭建,最主要的是,参数会按照我们定义好的序列自动传递下去。我们可以将嵌套在容器中的各个部分看作各种不同的模块,这些模块可以自由组合。模块的加入一般有两种方式,一种是在以上代码中使用的直接嵌套,另一种是以orderdict有序字典的方式进行传入,这两种方式的唯一区别是,使用后者搭建的模型的每个模块都有我们自定义的名字,而前者默认使用从零开始的数字序列作为每个模块的名字。下面通过示例来直观地看一下使用这两种方式搭建的模型之间的区别。

      首先,使用直接嵌套搭建的模型代码如下:

    #_*_coding:utf-8_*_
    import torch
    from torch.autograd import Variable
     
    # 批量输入的数据量
    batch_n = 100
    # 通过隐藏层后输出的特征数
    hidden_layer = 100
    # 输入数据的特征个数
    input_data = 1000
    # 最后输出的分类结果数
    output_data = 10
     
    x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
    y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)
     
    models = torch.nn.Sequential(
        # 首先通过其完成从输入层到隐藏层的线性变换
        torch.nn.Linear(input_data,hidden_layer),
        # 经过激活函数
        torch.nn.ReLU(),
        # 最后完成从隐藏层到输出层的线性变换
        torch.nn.Linear(hidden_layer,output_data)
    )
    print(models)

    这里对模型的结构进行打印输出,结果如下:

    Sequential(
      (0): Linear(in_features=1000, out_features=100, bias=True)
      (1): ReLU()
      (2): Linear(in_features=100, out_features=10, bias=True)
    )

       使用orderdict有序字典进行传入来搭建的模型代码如下:

    #_*_coding:utf-8_*_
    import torch
    from torch.autograd import Variable
    from collections import OrderedDict
     
    # 批量输入的数据量
    batch_n = 100
    # 通过隐藏层后输出的特征数
    hidden_layer = 100
    # 输入数据的特征个数
    input_data = 1000
    # 最后输出的分类结果数
    output_data = 10
     
    models = torch.nn.Sequential(OrderedDict([
        ("Linel",torch.nn.Linear(input_data,hidden_layer)),
        ("ReLU1",torch.nn.ReLU()),
        ("Line2",torch.nn.Linear(hidden_layer,output_data))
    ])
    )
    print(models)

    这里对该模型的结构进行打印输出,结果如下:

    Sequential(
      (Linel): Linear(in_features=1000, out_features=100, bias=True)
      (ReLU1): ReLU()
      (Line2): Linear(in_features=100, out_features=10, bias=True)
    )

       通过对这两种方式进行比较,我们会发现,对模块使用自定义的名称可让我们更便捷地找到模型中相应的模块并进行操作。

     

    1.2.2 torch.nn.Linear

      torch.nn.Linear类用于定义模型的线性层,即完成前面提到的不同的层之间的线性变换。torch.nn.Linear类接收的参数有三个,分别是输入特征数、输出特征数和是否使用偏置,设置是否使用偏置的参数是一个布尔值,默认为True,即使用偏置。在实际使用的过程中,我们只需将输入的特征数和输出的特征数传递给torch.nn.Linear类,就会自动生成对应维度的权重参数和偏置,对于生成的权重参数和偏置,我们的模型默认使用了一种比之前的简单随机方式更好的参数初始化方法。
      根据我们搭建模型的输入、输出和层次结构需求,它的输入是在一个批次中包含100个特征数为1000的数据,最后得到100个特征数为10的输出数据,中间需要经过两次线性变换,所以要使用两个线性层,两个线性层的代码分别是torch.nn.Linear(input_data,hidden_layer)和torch.nn.Linear(hidden_layer, output_data)。可看到,其代替了之前使用矩阵乘法方式的实现,代码更精炼、简洁。

    1.2.3 torch.nn.RelU

       torch.nn.ReLU类属于非线性激活分类,在定义时默认不需要传入参数。当然,在 torch.nn包中还有许多非线性激活函数类可供选择,比如之前讲到的PReLU、LeakyReLU、Tanh、Sigmoid、Softmax等。

      在掌握torch.nn.Sequential、torch.nn.Linear和torch.nn.RelU的使用方法后,快速搭建更复杂的多层神经网络模型变为可能,而且在整个模型的搭建过程中不需要对在模型中使用到的权重参数和偏置进行任何定义和初始化说明,因为参数已经完成了自动生成。

    1.3 优化模型

    epoch_n = 10000
    learning_rate = 1e-4
    loss_fn = torch.nn.MSELoss()

      下面简单介绍在torch.nn包中常用的损失函数的具体用法,如下所述:   前两句代码和之前的代码没有多大区别,只是单纯地增加了学习速率和训练次数,学习速率现在是0.0001,训练次数增加到了10000次,这样做是为了让最终得到的结果更好。不过计算损失函数的代码发生了改变,现在使用的是在torch.nn包中已经定义好的均方误差函数类torch.nn.MSELoss来计算损失值,而之前的代码是根据损失函数的计算公式来编写的。

    1.3.1 torch.nn.MSELoss

      torch.nn.MSELoss类使用均方误差函数对损失值进行计算,在定义类的对象时不用传入任何参数,但在使用实例时需要输入两个维度一样的参数方可进行计算。示例如下

    import torch
    from torch.autograd import Variable
     
    loss_f = torch.nn.MSELoss()
    x = Variable(torch.randn(100,100))
    y = Variable(torch.randn(100,100))
    loss = loss_f(x,y)
    print(loss)

       以上代码首先通过随机方式生成了两个维度都是(100,100)的参数,然后使用均方误差函数来计算两组参数的损失值,打印输出的结果如下:

    tensor(2.0121)

    1.3.2 torch.nn.L1Loss

      torch.nn.L1Loss类使用平均绝对误差函数对损失值进行计算,同样,在定义类的对象时不用传入任何参数,但在使用实例时需要输入两个维度一样的参数进行计算。示例如下:

    import torch
    from torch.autograd import Variable
     
    loss_f = torch.nn.L1Loss()
    x = Variable(torch.randn(100,100))
    y = Variable(torch.randn(100,100))
    loss = loss_f(x,y)
    print(loss)

       以上代码也是通过随机方式生成了两个维度都是(100,100)的参数,然后使用平均绝对误差函数来计算两组参数的损失值,打印输出的结果如下:

    tensor(1.1294)

    1.3.3 torch.nn.CrossEntropyLoss

     torch.nn.CrossEntropyLoss类用于计算交叉熵,在定义类的对象时不用传入任何参数,在使用实例时需要输入两个满足交叉熵的计算条件的参数,代码如下:

    import torch
    from torch.autograd import Variable
     
    loss_f = torch.nn.CrossEntropyLoss()
    x = Variable(torch.randn(3,5))
    y = Variable(torch.LongTensor(3).random_(5))
    loss = loss_f(x,y)
    print(loss)

     

    这里生成的第1组参数是一个随机参数,维度为(3,5);第2组参数是3个范围为0~4的随机数字。计算这两组参数的损失值,打印输出的结果如下

    	
    tensor(1.6983)
    
    

     在学会使用PyTorch中的优化函数之后,我们就可以对自己建立的神经网络模型进行训练并对参数进行优化了,代码如下:   这里生成的第1组参数是一个随机参数,维度为(3,5);第2组参数是3个范围为0~4的随机数字。计算这两组参数的损失值,打印输出的结果如下

    for epoch in range(epoch_n):
        y_pred = models(x)
        loss = loss_fn(y_pred,y)
        if epoch%1000 == 0:
            print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data[0]))
        models.zero_grad()
     
        loss.backward()
     
        for param in models.parameters():
            param.data -= param.grad.data*learning_rate

     

    以上代码中的绝大部分和之前训练和优化部分的代码是一样的,但是参数梯度更新的方式发生了改变。因为使用了不同的模型搭建方法,所以访问模型中的全部参数是通过对“models.parameters()”进行遍历完成的,然后才对每个遍历的参数进行梯度更新。其打印输入结果的方式是每完成1000次训练,就打印输出当前的loss值.

    1.4 结果及分析  

    Epoch:0,Loss:1.0140
    Epoch:1000,Loss:0.9409
    Epoch:2000,Loss:0.8776
    Epoch:3000,Loss:0.8216
    Epoch:4000,Loss:0.7716
    Epoch:5000,Loss:0.7263
    Epoch:6000,Loss:0.6850
    Epoch:7000,Loss:0.6468
    Epoch:8000,Loss:0.6109
    Epoch:9000,Loss:0.5773
    
    Process finished with exit code 0

      从结果可以看出,参数的优化效果比较理想,loss值被控制在相对较小的范围之内,这和我们增强了训练次数有很大关系。

    完整代码如下:

    #_*_coding:utf-8_*_
    import torch
    from torch.autograd import Variable
     
    # 批量输入的数据量
    batch_n = 100
    # 通过隐藏层后输出的特征数
    hidden_layer = 100
    # 输入数据的特征个数
    input_data = 1000
    # 最后输出的分类结果数
    output_data = 10
     
    x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
    y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)
     
    models = torch.nn.Sequential(
        # 首先通过其完成从输入层到隐藏层的线性变换
        torch.nn.Linear(input_data,hidden_layer),
        # 经过激活函数
        torch.nn.ReLU(),
        # 最后完成从隐藏层到输出层的线性变换
        torch.nn.Linear(hidden_layer,output_data)
    )
    # print(models)
     
    epoch_n = 10000
    learning_rate = 1e-4
    loss_fn = torch.nn.MSELoss()
     
    for epoch in range(epoch_n):
        y_pred = models(x)
        loss = loss_fn(y_pred,y)
        if epoch%1000 == 0:
            print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data[0]))
        models.zero_grad()
     
        loss.backward()
     
        for param in models.parameters():
            param.data -= param.grad.data*learning_rate

    二:PyTorch之torch.optim

      到目前为止,代码中的神经网络权重的参数优化和更新还没有实现自动化,并且目前使用的优化方法都有固定的学习速率,所以优化函数相对简单,如果我们自己实现一些高级的参数优化算法,则优化函数部分的代码会变得较为复杂。在PyTorch的torch.optim包中提供了非常多的可实现参数自动优化的类,比如SGD、AdaGrad、RMSProp、Adam等,这些类都可以被直接调用,使用起来也非常方便。

    2.1 优化模型

    我们使用自动化的优化函数实现方法对之前的代码进行替换,新的代码如下:

    #_*_coding:utf-8_*_
    import torch
    from torch.autograd import Variable
     
    # 批量输入的数据量
    batch_n = 100
    # 通过隐藏层后输出的特征数
    hidden_layer = 100
    # 输入数据的特征个数
    input_data = 1000
    # 最后输出的分类结果数
    output_data = 10
     
    x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
    y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)
     
    models = torch.nn.Sequential(
        # 首先通过其完成从输入层到隐藏层的线性变换
        torch.nn.Linear(input_data,hidden_layer),
        # 经过激活函数
        torch.nn.ReLU(),
        # 最后完成从隐藏层到输出层的线性变换
        torch.nn.Linear(hidden_layer,output_data)
    )
    # print(models)
     
    epoch_n = 20
    learning_rate = 1e-4
    loss_fn = torch.nn.MSELoss()
     
    optimzer = torch.optim.Adam(models.parameters(),lr = learning_rate)

      这里使用了torch.optim包中的torch.optim.Adam类作为我们的模型参数的优化函数,在torch.optim.Adam类中输入的是被优化的参数和学习速率的初始值,如果没有输入学习速率的初始值,那么默认使用0.001这个值。因为我们需要优化的是模型中的全部参数,所以传递给torch.optim.Adam类的参数是models.parameters。另外,Adam优化函数还有一个强大的功能,就是可以对梯度更新使用到的学习速率进行自适应调节,所以最后得到的结果自然会比之前的代码更理想。

    2.2 训练模型 

    进行模型训练的代码如下:

    #进行模型训练
    for epoch in range(epoch_n):
        y_pred = models(x)
        loss = loss_fn(y_pred,y)
        print("Epoch:{}, Loss:{:.4f}".format(epoch, loss.data[0]))
        optimzer.zero_grad()
     
        loss.backward()
     
        #进行梯度更新
        optimzer.step()

     在以上代码中有几处代码和之前的训练代码不同,这是因为我们引入了优化算法,所以通过直接调用optimzer.zero_grad来完成对模型参数梯度的归零;并且在以上代码中增加了optimzer.step,它的主要功能是使用计算得到的梯度值对各个节点的参数进行梯度更新。

    2.3 打印结果

      这里只进行20次训练并打印每轮训练的loss值,结果如下:

    Epoch:0, Loss:1.1289
    Epoch:1, Loss:1.1073
    Epoch:2, Loss:1.0862
    Epoch:3, Loss:1.0656
    Epoch:4, Loss:1.0455
    Epoch:5, Loss:1.0258
    Epoch:6, Loss:1.0065
    Epoch:7, Loss:0.9877
    Epoch:8, Loss:0.9692
    Epoch:9, Loss:0.9513
    Epoch:10, Loss:0.9338
    Epoch:11, Loss:0.9166
    Epoch:12, Loss:0.8997
    Epoch:13, Loss:0.8833
    Epoch:14, Loss:0.8672
    Epoch:15, Loss:0.8514
    Epoch:16, Loss:0.8360
    Epoch:17, Loss:0.8209
    Epoch:18, Loss:0.8061
    Epoch:19, Loss:0.7917
     
    Process finished with exit code 0

      在看到这个结果后我们会很惊讶,因为使用torch.optim.Adam类进行参数优化 后仅仅进行了20次训练,得到的loss值就已经远远低于之前进行10000次优化训练的 结果。所以,如果对torch.optim中的优化算法类使用得当,就更能帮助我们优化好 模型中的参数。

    2.4 完整的代码如下:

    #_*_coding:utf-8_*_
    import torch
    from torch.autograd import Variable
     
    # 批量输入的数据量
    batch_n = 100
    # 通过隐藏层后输出的特征数
    hidden_layer = 100
    # 输入数据的特征个数
    input_data = 1000
    # 最后输出的分类结果数
    output_data = 10
     
    x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
    y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)
     
    models = torch.nn.Sequential(
        # 首先通过其完成从输入层到隐藏层的线性变换
        torch.nn.Linear(input_data,hidden_layer),
        # 经过激活函数
        torch.nn.ReLU(),
        # 最后完成从隐藏层到输出层的线性变换
        torch.nn.Linear(hidden_layer,output_data)
    )
    # print(models)
     
    epoch_n = 20
    learning_rate = 1e-4
    loss_fn = torch.nn.MSELoss()
     
    optimzer = torch.optim.Adam(models.parameters(),lr = learning_rate)
     
    #进行模型训练
    for epoch in range(epoch_n):
        y_pred = models(x)
        loss = loss_fn(y_pred,y)
        print("Epoch:{}, Loss:{:.4f}".format(epoch, loss.data[0]))
        optimzer.zero_grad()
     
        loss.backward()
     
        #进行梯度更新
        optimzer.step()

     三:torch和torchvision

      在PyTorch中有两个核心的包,分别是torch和torchvision。我们之前已经接触了torch包的一部分内容,比如使用了torch.nn中的线性层加激活函数配合torch.optim完成了神经网络模型的搭建和模型参数的优化,并使用了 torch.autograd实现自动梯度的功能,接下来会介绍如何使用torch.nn中的类来搭建卷积神经网络。
      torchvision包的主要功能是实现数据的处理、导入和预览等,所以如果需要对计算机视觉的相关问题进行处理,就可以借用在torchvision包中提供的大量的类来完成相应的工作。下面可以看看都导入了什么包

    import torch
    # torchvision包的主要功能是实现数据的处理,导入和预览等
    import torchvision
    from torchvision import datasets
    from torchvision import transforms
    from torch.autograd import Variable

      在前面讲到过,在torch.transforms中提供了丰富的类对载入的数据进行变换,现在让我们看看如何进行变换。我们知道,在计算机视觉中处理的数据集有很大一部分是图片类型的,而在PyTorch中实际进行计算的是Tensor数据类型的变量,所以我们首先需要解决的是数据类型转换的问题,如果获取的数据是格式或者大小不一的图片,则还需要进行归一化和大小缩放等操作,庆幸的是,这些方法在torch.transforms中都能找到。

    3.1 PyTorch中的torch.transforms

      在torch.transforms中有大量的数据变换类,其中有很大一部分可以用于实现数据增强(Data Argumentation)。若在我们需要解决的问题上能够参与到模型训练中的图片数据非常有限,则这时就要通过对有限的图片数据进行各种变换,来生成新的训练集了,这些变换可以是缩小或者放大图片的大小、对图片进行水平或者垂直翻转等,都是数据增强的方法。不过在手写数字识别的问题上可以不使用数据增强的方法,因为可用于模型训练的数据已经足够了。对数据进行载入及有相应变化的代码如下:

    transform = transforms.Compose([transforms.ToTensor(),
                                    transforms.Normalize(mean=[0.5,0.5,0.5],
                                    std=[0.5,0.5,0.5])])

      在以上的代码中,在torchvision.transforms.Compose类中只是用了一个类型的转换变化transfroms.ToTensor和一个数据标准化变换transforms.Normalize。这里使用的是标准化变换也叫标准差变换法,这种方法需要使用原始数据的均值(Mean)和标准差(Standard Deviation)来进行数据的标准化,在经过标准化变换之后,数据全部符合均值为0,标准差为1的标准正态分布,计算公式入选:   我们可以将上面代码中的torchvision.transforms.Compose类看作是一种容器,它能够同时对多种数据变换进行组合。传入的参数是一个列表,列表中的元素就是对载入的数据进行的各种变换操作。

       不过这里我们偷了一个懒,均值和标准差的值并非来自原始数据的,而是自行定义了一个,不过仍然能够达到我们的目的。

      下面看看在torchvision.transforms中常用的数据变换操作。

    3.1.1  torchvision.transforms.Resize

      用于对载入的图片数据按我们需求的大小进行缩放。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h ,w )的序列,其中,h 代表高度,w 代表宽度,但是如果使用的是一个整型数据,那么表示缩放的宽度和高度都是这个整型数据的值。

    3.1.2  torchvision.transforms.Scale

      用于对载入的图片数据按我们需求的大小进行缩放,用法和torchvision.transforms.Resize类似。

    3.1.3  torchvision.transforms.CenterCrop

      用于对载入的图片以图片中心为参考点,按我们需要的大小进行裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h ,w )的序列。

    3.1.4  torchvision.transforms.RandomCrop

      用于对载入的图片按我们需要的大小进行随机裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h ,w )的序列。

    3.1.5  torchvision.transforms.RandomHorizontalFlip

      用于对载入的图片按随机概率进行水平翻转。我们可以通过传递给这个类的参数自定义随机概率,如果没有定义,则使用默认的概率值0.5。

    3.1.6  torchvision.transforms.RandomVerticalFlip

      用于对载入的图片按随机概率进行垂直翻转。我们可以通过传递给这个类的参数自定义随机概率,如果没有定义,则使用默认的概率值0.5。

    3.1.7  torchvision.transforms.ToTensor

      用于对载入的图片数据进行类型转换,将之前构成PIL图片的数据转换成Tensor数据类型的变量,让PyTorch能够对其进行计算和处理。

    3.1.8  torchvision.transforms.ToPILImage

      用于将Tensor变量的数据转换成PIL图片数据,主要是为了方便图片内容的显示

    3.2 PyTorch中的torch.nn

      首先我们看一个卷积神经网络模型搭建的代码:

    #模型搭建和参数优化
    # 在顺利完成数据装载后,我们可以开始编写卷积神经网络模型的搭建和参数优化的代码
    #卷积层使用torch.nn.Conv2d类来搭建
    # 激活层使用torch.nn.ReLU 类方法来搭建
    # 池化层使用torch.nn.MaxPool2d类方法来搭建
    # 全连接层使用 torch.nn.Linear 类方法来搭建
     
    class Model(torch.nn.Module):
        def __init__(self):
            super(Model,self).__init__()
            self.conv1 = torch.nn.Sequential(
                torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1),
                torch.nn.ReLU(),
                torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
                torch.nn.ReLU(),
                torch.nn.MaxPool2d(stride=2,kernel_size=2))
     
            self.dense = torch.nn.Sequential(
                torch.nn.Linear(14*14*128,1024),
                torch.nn.ReLU(),
                torch.nn.Dropout(p = 0.5),
                torch.nn.Linear(1024,10)
            )
     
        def forward(self, x):
            x = self.conv1(x)
            x = x.view(-1,14*14*128)
            x = self.dense(x)
            return x

    上面我们选择搭建了一个在结构层次上有所简化的卷积神经网络模型,在结构上使用了两个卷积层:一个最大池化层和两个全连接层,这里对具体的使用方法进行补充说明。

    3.2.1 torch.nn.Conv2d   

    用于搭建卷积神经网络的卷积层,主要的输入参数有输入通道数、输出通道数、卷积核大小、卷积核移动步长和Paddingde值。其中,输入通道数的数据类型是整型,用于确定输入数据的层数;输出通道数的数据类型也是整型,用于确定输出数据的层数;卷积核大小的数据类型是整型,用于确定卷积核的大小;卷积核移动步长的数据类型是整型,用于确定卷积核每次滑动的步长;Paddingde 的数据类型是整型,值为0时表示不进行边界像素的填充,如果值大于0,那么增加数字所对应的边界像素层数。

    3.2.2  torch.nn.MaxPool2d

      用于实现卷积神经网络中的最大池化层,主要的输入参数是池化窗口大小、池化窗口移动步长和Padding的值。同样,池化窗口大小的数据类型是整型,用于确定池化窗口的大小。池化窗口步长的数据类型也是整型,用于确定池化窗口每次移动的步长。Padding的值和在torch.nn.Conv2d中定义的Paddingde值的用法和意义是一样的。

    3.2.3  torch.nn.Dropout

      torch.nn.Dropout类用于防止卷积神经网络在训练的过程中发生过拟合,其工作原理简单来说就是在模型训练的过程中,以一定的随机概率将卷积神经网络模型的部分参数归零,以达到减少相邻两层神经连接的目的。下图显示了 Dropout方法的效果。

     

      在上图中的打叉的神经节点就是被随机抽中并丢弃的神经连接,正是因为选取的方式的随机性,所以在模型的每轮训练中选择丢弃的神经连接也是不同的,这样做是为了让我们最后训练出来的模型对各部分的权重参数不产生过度依赖,从而防止过拟合,对于torch.nn.Dropout类,我们可以对随机概率值的大小进行设置,如果不足任何设置,我们就使用默认的概率值0.5。

      最后说一下代码中前向传播forward函数中的内容,首先经过self.conv1进行卷积处理,然后进行x.view(-1,14*14*128),对参数实现扁平化,因为之后紧接着的就是全连接层,所以如果不进行扁平化,则全连接层的实际输出的参数维度和其定义输入的维度将不匹配,程序将会报错,最后通过self.dense定义的全连接层进行最后的分类。

     


     

    参考资料

    1. https://www.cnblogs.com/wj-1314/p/9838866.html 

    更多相关内容
  • 分析了数控加工工艺生成方式,阐述了数控加工工艺决策过程中相关原理以及切削参数规范化,同时说明了由于相关的各种先进制造技术的迅速发展,开发了数控加工切削参数管理和优化系统,从而为企业创造了良好的经济效益。
  • GSM爱立信参数规范

    2014-07-20 22:37:55
    GSM爱立信的参数规范,用于G网优化调整参考的指导性文件
  • 为了解决新屯矿14271工作面出现的顶板下沉及断裂的问题,对14271工作面在掘进阶段顶板围岩的受力、变形与破坏特征进行研究,规范优化了锚杆、锚索的支护参数,并在此基础上提出此类巷道的围岩稳定控制对策。...
  • 为了规范综采工作面作业循环过程中割...对相关作业参数进行优化,细化考核标准,提高现场管理水平,并在陈四楼煤矿综采工作面进行了应用。实践表明,综采设备损坏以及故障频率均有明显减少,实现了综采工作面安全快速推进。
  • CFG桩复合地基多参数优化设计研究,李兴毓,申翃,CFG桩复合地基技术在我国是20世纪80年代末发展起来的,到目前为止,计算理论已经比较成熟,《建筑地基处理技术规范》JGJ79-2002为CFG桩�
  • QoS优化是LTE SON的重要组成部分,但尚未在规范中定义。 我们讨论了QoS优化问题的建模,改进了适应度函数,然后提供了一种基于MPSO的算法来搜索LTE网络的最佳QoS参数值集。 仿真结果表明,该算法比可应用于LTE SON的...
  • Hyperopt 参数优化

    万次阅读 多人点赞 2018-08-28 20:02:03
    Hyperopt(Hyper-parameter Optimization)用于模型选择和参数优化参数选择在训练模型时是一个很关键的部分。然而存在这样的问题,一方面参数选择背后包含着一定的数学原理,对于新手来说难上手;另一方面,一个...

    翻译自https://districtdatalabs.silvrback.com/parameter-tuning-with-hyperopt
    Parameter Tuning with Hyperopt –Kris Wright

    概述

    Hyperopt可以帮助快速进行机器学习模型参数调试。通常情况下有两种类型的参数调试方法,网格搜索(grid search)和随机搜索(random search)。网格搜索速度慢但是适用于需要随整个参数空间进行搜索的情况;随机搜索速度很快但是容易遗漏一些重要信息。幸运的是,我们有第三个选择:贝叶斯优化(Bayesian optimization)。这里我们关注贝叶斯优化在Python中的实现:Hyperopt模块。

    使用贝叶斯优化进行参数调试允许我们获得给定模型的最优参数,如,逻辑回归( logistic regression),因此我们可以进行最优模型选择。通常情况下,机器学习工程师或数据科学家会对一些模型进行一些形式的手动参数调试(如网格搜索或随机搜索),如决策树(decision tree)、支持向量机(support vector machine)、k最近邻(k nearest neighbors)-然后比较准确度得分(accuracy score)进而选择性能表现最好的一组参数给模型使用。这种方法可能会产生次优模型(sub-optimal models)。数据科学家可能会给决策树选择一组最优参数,但是对SVM却不是最优的,这就意味着模型比较是有缺陷的。KNN每次都可以打败SVM,如果SVM的参数选择很差的话。贝叶斯优化允许数据科学家找到所有模型的最优参数,然后比较所有使用了最佳参数的模型。这样的话,在模型选择中,就可以比较最优的决策树和最优的支持向量机。只有这样才能保证我们选择和使用了实际最好的模型。

    本文涉及的主题包括:
    - 评估函数(Objective functions)
    - 搜索空间(Search spaces)
    - 存储评价试验(Storing evaluation trials)
    - 可视化(Visualization)
    - 鸢尾花数据集实例(Full example on a classic dataset: Iris)

    目标函数(Objective Functions - A Motivating Example)

    假设有一个定义在摸个范围内的函数,你想要使这个函数最小化,也就是说,你想要找到一个输入值,使得函数的输出结果是最小的。如下面的例子,找到一个x,使得线性函数y(x)=x取得最小值。

    from hyperopt import fmin, tpe, hp
    best = fmin(
        fn=lambda x: x,
        space=hp.uniform('x', 0, 1),
        algo=tpe.suggest,
        max_evals=100)
    print best

    下面进行分解说明。
    函数fmin首先要接收一个函数fn来最小化,这里我们指定fn为一个匿名函数lambda x: x,实际上这个函数可以是任何有效的带有返回值的函数(valid value-returning function),如回归中的平均绝对误差(mean absolute error)。
    第二个参数space指定了搜索空间(search space),在这个例子中为在0到1范围之间的数字。hp.uniform是一个内置的( built-in)hyperopt函数,包括三个参数:name:x,范围的上界和下届:0和1。
    参数algo接收一个搜索算法(serach algorithm),这个例子中tpe代表tree of Parzen estimators。这个话题超出了这个博客文章的范围,更多关于tpe的内容请参考这里。algo参数也可以设置成hyperopt.random,但我们在此不做介绍,因为这是众所周知的搜索策略。

    最后,我们指定fmin函数执行的最大次数max_evals。fmin函数返回一个python字典。函数输出的一个例子:{‘x’: 0.000269455723739237}。
    下面是一张函数图像,红色的点是我们要找的x的位置。
    y=x

    另一个例子(More Complicated Examples)

    这里有个更复杂的目标函数:lambda x: (x-1)**2,这次我们试着最小化一个二次方程 y(x)=(x1)2 y ( x ) = ( x − 1 ) 2 。因此,我们将搜索空间更改为包括我们已经知道的最优值x=1以及加上两侧的一些次优范围:hp.uniform(‘x’, -2, 2)。
    我们可以得到:

    best = fmin(
        fn=lambda x: (x-1)**2,
        space=hp.uniform('x', -2, 2),
        algo=tpe.suggest,
        max_evals=100)
    print best

    输出为:

    {'x': 0.997369045274755}

    函数如下图:
    y=(x-1)^2
    除了最小化目标函数,也许我们想使函数值最大化。这种情况下我们只需要返回函数的负值,如函数y(x) = -(x**2):
    y=-x^2
    如何解决这个问题呢?我们只需要将目标函数改为lambda x: (x**2),求y(x) = -(x**2)得最小值即为求y(x) = -(x**2)的最大值。
    同理对于刚开始的线性函数的例子,我们将求最小化改为求最大化,将将目标函数改为lambda x: -x即可。
    下面是一个有用许多(给定无穷范围的无穷多个)局部最小值得函数,也可以求最大值。
    y=sin(x)/x

    搜索空间(Search spaces)

    Hyperopt模块包含了一些方便的函数(handy functions)来指定输入参数的范围。我们已经见过hp.uniform。最初,这些是随机搜索空间,但是随着hyperopt的学习(随着从目标函数获得更多的feedback),它会对初始搜索空间的不同部分进行调整和采样,并认为这些部分会给它提供最有意义的反馈。
    以下函数将在本文使用:

    • hp.choice(label, options) ,其中options为 python list 或tuple
    • hp.normal(label, mu, sigma) ,其中mu 和 sigma 分别为均值和标准差
    • hp.uniform(label, low, high),其中low和 high分别为范围的上界和下界

    其他还有hp.normal, hp.lognormal, hp.quniform,因为本文不会使用就没有写,可自行了解。
    下面定义搜索空间:

    import hyperopt.pyll.stochastic
    
    space = {
        'x': hp.uniform('x', 0, 1),
        'y': hp.normal('y', 0, 1),
        'name': hp.choice('name', ['alice', 'bob']),
    }
    
    print hyperopt.pyll.stochastic.sample(space)

    例子输出:

    {'y': -1.4012610048810574, 'x': 0.7258615424906184, 'name': 'alice'}

    获取信息和试验(Capturing Info with Trials)

    如果能看到hyperopt黑盒里到底发生了什么,那就太好了。Trials对象允许我们这样做。我们只需要再导入几个项目

    from hyperopt import fmin, tpe, hp, STATUS_OK, Trials
    
    fspace = {
        'x': hp.uniform('x', -5, 5)
    }
    
    def f(params):
        x = params['x']
        val = x**2
        return {'loss': val, 'status': STATUS_OK}
    
    trials = Trials()
    best = fmin(fn=f, space=fspace, algo=tpe.suggest, max_evals=50, trials=trials)
    
    print 'best:', best
    
    print 'trials:'
    for trial in trials.trials[:2]:
        print trial

    STATUS_OK 和 Trials是新导入的模块,Trials允许我们存储每一时间步长(time step)所存储的信息。然后我们可以输出这些函数在给定时间步长上对给定参数的求值。
    输出:

    best: {'x': 0.014420181637303776}
    trials:
    {'refresh_time': None, 'book_time': None, 'misc': {'tid': 0, 'idxs': {'x': [0]}, 'cmd': ('domain_attachment', 'FMinIter_Domain'), 'vals': {'x': [1.9646918559786162]}, 'workdir': None}, 'state': 2, 'tid': 0, 'exp_key': None, 'version': 0, 'result': {'status': 'ok', 'loss': 3.8600140889486996}, 'owner': None, 'spec': None}
    {'refresh_time': None, 'book_time': None, 'misc': {'tid': 1, 'idxs': {'x': [1]}, 'cmd': ('domain_attachment', 'FMinIter_Domain'), 'vals': {'x': [-3.9393509404526728]}, 'workdir': None}, 'state': 2, 'tid': 1, 'exp_key': None, 'version': 0, 'result': {'status': 'ok', 'loss': 15.518485832045357}, 'owner': None, 'spec': None}

    Trials对象将数据存储为BSON对象,类似于JSON对象一样。BSON来自pymongo模块,着这里我们不讨论细节,但是对于hyperopt有一些高级选项需要使用MongoDB进行分布式计算,因此导入pymongo。
    回到上面的输出,’tid’表示时间id,也就是时间步长,范围0到max_evals-1,每次迭代加1;’x’在’vals’键中,也就是每次迭代参数存储的地方;’loss’在’result’键中,是每次迭代目标函数的值。

    可视化(Visualization)

    这里介绍两种类型可视化,val vs. time和loss vs. val。
    首先val vs. time。下面是trials.trials数据描述可视化的代码和样本输出。

    f, ax = plt.subplots(1)
    xs = [t['tid'] for t in trials.trials]
    ys = [t['misc']['vals']['x'] for t in trials.trials]
    ax.set_xlim(xs[0]-10, xs[-1]+10)
    ax.scatter(xs, ys, s=20, linewidth=0.01, alpha=0.75)
    ax.set_title('$x$ $vs$ $t$ ', fontsize=18)
    ax.set_xlabel('$t$', fontsize=16)
    ax.set_ylabel('$x$', fontsize=16)

    输出如下(假设我们将max_evals设置为1000):
    val vs. time
    我们可以看到,最初算法从整个范围中均匀取值,但是随着时间的增加以及对于参数在目标函数上效果的学习,算法搜索范围越来越集中到最可能取得最优值的范围-0附近。它仍然探索整个解决方案空间,但不太频繁。

    loss vs. val的可视化:

    f, ax = plt.subplots(1)
    xs = [t['misc']['vals']['x'] for t in trials.trials]
    ys = [t['result']['loss'] for t in trials.trials]
    ax.scatter(xs, ys, s=20, linewidth=0.01, alpha=0.75)
    ax.set_title('$val$ $vs$ $x$ ', fontsize=18)
    ax.set_xlabel('$x$', fontsize=16)
    ax.set_ylabel('$val$', fontsize=16)

    loss vs. val
    这就是我们期望的,因为函数y(x) = x**2是确定性的。
    最后,让我们尝试一个更复杂的示例,使用更多的随机性和更多的参数。

    鸢尾花数据集(The Iris Dataset)

    在本节中,我们将介绍在经典数据集Iris上使用hyperopt进行参数调优的4个完整示例。我们将介绍k近邻(KNN)、支持向量机(SVM)、决策树和随机森林。注意,由于我们试图最大化交叉验证的准确性(下面代码中的acc),我们必须为hyperopt对这个值进行取负数,因为hyperopt只知道如何最小化函数。最小化函数f等于最大化函数f的复数。
    对于这项任务,我们将使用经典的Iris数据集,并进行一些有监督的机器学习。有4个输入特性和3个输出类。这些数据被标记为属于0类、1类或2类,它们映射到不同种类的鸢尾花。输入有4列:萼片长度(sepal length)、萼片宽度(sepal width)、花瓣长度(petal length)和花瓣宽度(pedal width)。输入单位是厘米。我们将使用这4个特性来学习预测三个输出类之一的模型。由于数据是由sklearn提供的,因此它有一个很好的DESCR属性,提供了关于数据集的详细信息。

    print iris.feature_names # input names
    print iris.target_names # output names
    print iris.DESCR # everything else

    让我们通过可视化特性和类来更好地了解数据,使用下面的代码。如果还没有安装seaborn,请不要忘记安装pip install seaborn。

    import seaborn as sns
    sns.set(style="whitegrid", palette="husl")
    
    iris = sns.load_dataset("iris")
    print iris.head()
    
    iris = pd.melt(iris, "species", var_name="measurement")
    print iris.head()
    
    f, ax = plt.subplots(1, figsize=(15,10))
    sns.stripplot(x="measurement", y="value", hue="species", data=iris, jitter=True, edgecolor="white", ax=ax)

    如图:
    iris

    K最近邻(K-Nearest Neighbors)

    我们现在使用hyperopt找到k近邻(KNN)机器学习模型的最佳参数。KNN模型根据训练数据集中k个最近的数据点的多数类,将测试集中的数据点进行分类。
    关于这个算法的更多信息可以在这里找到。下面的代码包含了我们已经讨论过的所有内容。

    from sklearn import datasets
    iris = datasets.load_iris()
    X = iris.data
    y = iris.target
    
    def hyperopt_train_test(params):
        clf = KNeighborsClassifier(**params)
        return cross_val_score(clf, X, y).mean()
    
    space4knn = {
        'n_neighbors': hp.choice('n_neighbors', range(1,100))
    }
    
    def f(params):
        acc = hyperopt_train_test(params)
        return {'loss': -acc, 'status': STATUS_OK}
    
    trials = Trials()
    best = fmin(f, space4knn, algo=tpe.suggest, max_evals=100, trials=trials)
    print 'best:'
    print best

    现在我们来看看输出图。y轴是交叉验证得分,x轴是k-最近邻中的k值。下面是代码及其图像:

    f, ax = plt.subplots(1)#, figsize=(10,10))
    xs = [t['misc']['vals']['n_neighbors'] for t in trials.trials]
    ys = [-t['result']['loss'] for t in trials.trials]
    ax.scatter(xs, ys, s=20, linewidth=0.01, alpha=0.5)
    ax.set_title('Iris Dataset - KNN', fontsize=18)
    ax.set_xlabel('n_neighbors', fontsize=12)
    ax.set_ylabel('cross validation accuracy', fontsize=12)

    knn
    当k大于63时,准确率急剧下降。这是由于数据集中每个类的数量。这三个类中的每个类只有50个实例。因此,让我们通过将’n_neighbors’的值限制为较小的值进行深入研究。

    from sklearn import datasets
    iris = datasets.load_iris()
    X = iris.data
    y = iris.target
    
    def hyperopt_train_test(params):
        clf = KNeighborsClassifier(**params)
        return cross_val_score(clf, X, y).mean()
    
    space4knn = {
        'n_neighbors': hp.choice('n_neighbors', range(1,50))
    }
    
    def f(params):
        acc = hyperopt_train_test(params)
        return {'loss': -acc, 'status': STATUS_OK}
    
    trials = Trials()
    best = fmin(f, space4knn, algo=tpe.suggest, max_evals=100, trials=trials)
    print 'best:'
    print best

    下面是当我们运行相同的可视化代码时得到的结果:
    knn-2
    现在我们可以清楚地看到k在k = 4处有一个最佳值。

    上面的模型不做任何预处理。让我们对我们的特性进行规范化和缩放看看这是否有帮助。使用这段代码:

    # now with scaling as an option
    from sklearn import datasets
    iris = datasets.load_iris()
    X = iris.data
    y = iris.target
    
    def hyperopt_train_test(params):
        X_ = X[:]
    
        if 'normalize' in params:
            if params['normalize'] == 1:
                X_ = normalize(X_)
                del params['normalize']
    
        if 'scale' in params:
            if params['scale'] == 1:
                X_ = scale(X_)
                del params['scale']
    
        clf = KNeighborsClassifier(**params)
        return cross_val_score(clf, X_, y).mean()
    
    space4knn = {
        'n_neighbors': hp.choice('n_neighbors', range(1,50)),
        'scale': hp.choice('scale', [0, 1]),
        'normalize': hp.choice('normalize', [0, 1])
    }
    
    def f(params):
        acc = hyperopt_train_test(params)
        return {'loss': -acc, 'status': STATUS_OK}
    
    trials = Trials()
    best = fmin(f, space4knn, algo=tpe.suggest, max_evals=100, trials=trials)
    print 'best:'
    print best

    像这样画出参数:

    parameters = ['n_neighbors', 'scale', 'normalize']
    cols = len(parameters)
    f, axes = plt.subplots(nrows=1, ncols=cols, figsize=(15,5))
    cmap = plt.cm.jet
    for i, val in enumerate(parameters):
        xs = np.array([t['misc']['vals'][val] for t in trials.trials]).ravel()
        ys = [-t['result']['loss'] for t in trials.trials]
        xs, ys = zip(\*sorted(zip(xs, ys)))
        ys = np.array(ys)
        axes[i].scatter(xs, ys, s=20, linewidth=0.01, alpha=0.75, c=cmap(float(i)/len(parameters)))
        axes[i].set_title(val)

    knn-3
    我们发现,数据的缩放和/或规范化并不能提高预测的准确性。k的最佳值为4,准确率为98.6%。

    这对于一个简单模型KNN的参数调优非常有用。让我们看看支持向量机(SVM)能做些什么。

    支持向量机 (SVM)

    由于这是一个分类任务,我们将使用sklearn的SVC类。这是代码:

    iris = datasets.load_iris()
    X = iris.data
    y = iris.target
    
    def hyperopt_train_test(params):
        X_ = X[:]
    
        if 'normalize' in params:
            if params['normalize'] == 1:
                X_ = normalize(X_)
                del params['normalize']
    
        if 'scale' in params:
            if params['scale'] == 1:
                X_ = scale(X_)
                del params['scale']
    
        clf = SVC(**params)
        return cross_val_score(clf, X_, y).mean()
    
    space4svm = {
        'C': hp.uniform('C', 0, 20),
        'kernel': hp.choice('kernel', ['linear', 'sigmoid', 'poly', 'rbf']),
        'gamma': hp.uniform('gamma', 0, 20),
        'scale': hp.choice('scale', [0, 1]),
        'normalize': hp.choice('normalize', [0, 1])
    }
    
    def f(params):
        acc = hyperopt_train_test(params)
        return {'loss': -acc, 'status': STATUS_OK}
    
    trials = Trials()
    best = fmin(f, space4svm, algo=tpe.suggest, max_evals=100, trials=trials)
    print 'best:'
    print best
    
    parameters = ['C', 'kernel', 'gamma', 'scale', 'normalize']
    cols = len(parameters)
    f, axes = plt.subplots(nrows=1, ncols=cols, figsize=(20,5))
    cmap = plt.cm.jet
    for i, val in enumerate(parameters):
        xs = np.array([t['misc']['vals'][val] for t in trials.trials]).ravel()
        ys = [-t['result']['loss'] for t in trials.trials]
        xs, ys = zip(\*sorted(zip(xs, ys)))
        axes[i].scatter(xs, ys, s=20, linewidth=0.01, alpha=0.25, c=cmap(float(i)/len(parameters)))
        axes[i].set_title(val)
        axes[i].set_ylim([0.9, 1.0])

    下面是我们得到的:
    svm
    同样,缩放和规范化也没有帮助。核函数的首选为最佳(linear),最佳C值为1.4168540399911616,最佳伽玛值为15.04230279483486。该参数集的分类准确率达到99.3%。

    决策树(Decision Trees)

    我们将只尝试对决策树的几个参数进行优化。这是代码。

    iris = datasets.load_iris()
    X_original = iris.data
    y_original = iris.target
    
    def hyperopt_train_test(params):
        X_ = X[:]
        if 'normalize' in params:
            if params['normalize'] == 1:
                X_ = normalize(X_)
                del params['normalize']
    
        if 'scale' in params:
            if params['scale'] == 1:
                X_ = scale(X_)
                del params['scale']
        clf = DecisionTreeClassifier(**params)
        return cross_val_score(clf, X, y).mean()
    
    space4dt = {
        'max_depth': hp.choice('max_depth', range(1,20)),
        'max_features': hp.choice('max_features', range(1,5)),
        'criterion': hp.choice('criterion', ["gini", "entropy"]),
        'scale': hp.choice('scale', [0, 1]),
        'normalize': hp.choice('normalize', [0, 1])
    }
    
    def f(params):
    acc = hyperopt_train_test(params)
    return {'loss': -acc, 'status': STATUS_OK}
    
    trials = Trials()
    best = fmin(f, space4dt, algo=tpe.suggest, max_evals=300, trials=trials)
    print 'best:'
    print best

    输出如下,准确率为97.3%:

    {'max_features': 1, 'normalize': 0, 'scale': 0, 'criterion': 0, 'max_depth': 17}

    如图我们可以看到,在不同的尺度值、标准化值和标准值下,性能几乎没有差别。

    parameters = ['max_depth', 'max_features', 'criterion', 'scale', 'normalize'] # decision tree
    cols = len(parameters)
    f, axes = plt.subplots(nrows=1, ncols=cols, figsize=(20,5))
    cmap = plt.cm.jet
    for i, val in enumerate(parameters):
        xs = np.array([t['misc']['vals'][val] for t in trials.trials]).ravel()
        ys = [-t['result']['loss'] for t in trials.trials]
        xs, ys = zip(\*sorted(zip(xs, ys)))
        ys = np.array(ys)
        axes[i].scatter(xs, ys, s=20, linewidth=0.01, alpha=0.5, c=cmap(float(i)/len(parameters)))
        axes[i].set_title(val)
        #axes[i].set_ylim([0.9,1.0])

    dt

    随机森林(Random Forests)

    让我们看看集成分类器Random Forest发生了什么,它只是一组针对不同大小的数据分区训练的决策树,每个分区对一个输出类进行投票,并选择多数派类作为预测。

    iris = datasets.load_iris()
    X_original = iris.data
    y_original = iris.target
    
    def hyperopt_train_test(params):
        X_ = X[:]
        if 'normalize' in params:
            if params['normalize'] == 1:
                X_ = normalize(X_)
                del params['normalize']
    
        if 'scale' in params:
            if params['scale'] == 1:
                X_ = scale(X_)
                del params['scale']
        clf = RandomForestClassifier(**params)
        return cross_val_score(clf, X, y).mean()
    
    space4rf = {
        'max_depth': hp.choice('max_depth', range(1,20)),
        'max_features': hp.choice('max_features', range(1,5)),
        'n_estimators': hp.choice('n_estimators', range(1,20)),
        'criterion': hp.choice('criterion', ["gini", "entropy"]),
        'scale': hp.choice('scale', [0, 1]),
        'normalize': hp.choice('normalize', [0, 1])
    }
    
    best = 0
    def f(params):
        global best
        acc = hyperopt_train_test(params)
        if acc > best:
        best = acc
        print 'new best:', best, params
        return {'loss': -acc, 'status': STATUS_OK}
    
    trials = Trials()
    best = fmin(f, space4rf, algo=tpe.suggest, max_evals=300, trials=trials)
    print 'best:'
    print best

    同样,我们只得到97.3%的准确率,和决策树一样。

    下面是绘制参数的代码:

    parameters = ['n_estimators', 'max_depth', 'max_features', 'criterion', 'scale', 'normalize']
    f, axes = plt.subplots(nrows=2, ncols=3, figsize=(15,10))
    cmap = plt.cm.jet
    for i, val in enumerate(parameters):
        print i, val
        xs = np.array([t['misc']['vals'][val] for t in trials.trials]).ravel()
        ys = [-t['result']['loss'] for t in trials.trials]
        xs, ys = zip(\*sorted(zip(xs, ys)))
        ys = np.array(ys)
        axes[i/3,i%3].scatter(xs, ys, s=20, linewidth=0.01, alpha=0.5, c=cmap(float(i)/len(parameters)))
        axes[i/3,i%3].set_title(val)
        #axes[i/3,i%3].set_ylim([0.9,1.0])

    rf

    所有模型(All Together Now)

    虽然自动调优一个模型的参数(例如SVM或KNN)既有趣又有指导意义,但更有用的是一次调优所有的参数并得到一个总体上最好的模型。这使我们能够同时比较所有的参数和所有的模型,这给了我们最好的模型。这是代码。

    digits = datasets.load_digits()
    X = digits.data
    y = digits.target
    print X.shape, y.shape
    
    def hyperopt_train_test(params):
        t = params['type']
        del params['type']
        if t == 'naive_bayes':
            clf = BernoulliNB(**params)
        elif t == 'svm':
            clf = SVC(**params)
        elif t == 'dtree':
            clf = DecisionTreeClassifier(**params)
        elif t == 'knn':
            clf = KNeighborsClassifier(**params)
        else:
            return 0
        return cross_val_score(clf, X, y).mean()
    
    space = hp.choice('classifier_type', [
        {
            'type': 'naive_bayes',
            'alpha': hp.uniform('alpha', 0.0, 2.0)
        },
        {
            'type': 'svm',
            'C': hp.uniform('C', 0, 10.0),
            'kernel': hp.choice('kernel', ['linear', 'rbf']),
            'gamma': hp.uniform('gamma', 0, 20.0)
        },
        {
            'type': 'randomforest',
            'max_depth': hp.choice('max_depth', range(1,20)),
            'max_features': hp.choice('max_features', range(1,5)),
            'n_estimators': hp.choice('n_estimators', range(1,20)),
            'criterion': hp.choice('criterion', ["gini", "entropy"]),
            'scale': hp.choice('scale', [0, 1]),
            'normalize': hp.choice('normalize', [0, 1])
        },
        {
            'type': 'knn',
            'n_neighbors': hp.choice('knn_n_neighbors', range(1,50))
        }
    ])
    
    count = 0
    best = 0
    def f(params):
        global best, count
        count += 1
        acc = hyperopt_train_test(params.copy())
        if acc > best:
            print 'new best:', acc, 'using', params['type']
            best = acc
        if count % 50 == 0:
            print 'iters:', count, ', acc:', acc, 'using', params
        return {'loss': -acc, 'status': STATUS_OK}
    
    trials = Trials()
    best = fmin(f, space, algo=tpe.suggest, max_evals=1500, trials=trials)
    print 'best:'
    print best

    这段代码需要一段时间才能运行,因为我们增加了计算次数:max_evals=1500。当发现新的最佳精度时,还会增加输出以更新。奇怪的是,为什么使用这种方法没有找到我们在上面找到的最佳模型:SVM的kernel=linear,C=1.416,和gamma=15.042。

    总结

    我们已经介绍了一些简单的例子,比如最小化确定性线性函数,以及一些复杂的例子,比如调整随机森林参数。hyperopt的文档在这里。另一个关于hyperopt的好博客是FastML的。hyperopt作者撰写的SciPy会议论文是Hyperopt: A Python Library for Optimizing the Hyperparameters of Machine Learning Algorithms,附带一个视频教程。对工程细节的一种更科学的处理方法是Making a Science of Model Search
    这篇文章中的技术可以应用于除机器学习之外的许多领域,例如在epsilon的epsilon-greedy multi-armed bandit调优参数,或传递给图形生成器的参数,以形成具有某些特性的合成网络。稍后我们将对此进行更多的讨论。

    展开全文
  • 使用TensorBoard进行超参数优化

    千次阅读 2020-07-04 09:08:07
    在本文中,我们将介绍超参数优化,然后使用TensorBoard显示超参数优化的结果。 深度神经网络的超参数是什么? 深度学习神经网络的...超参数的一个通俗的解释是:用来优化参数的参数。 神经网络中的一些超参数是: 隐

    在本文中,我们将介绍超参数优化,然后使用TensorBoard显示超参数优化的结果。

    深度神经网络的超参数是什么?

    深度学习神经网络的目标是找到节点的权重,这将帮助我们理解图像、文本或语音中的数据模式。

    要做到这一点,可以使用为模型提供最佳准度和精度的值来设计神经网络参数。

    那么,这些被称为超参数的参数是什么呢?

    用于训练神经网络模型的不同参数称为超参数。这些超参数像旋钮一样被调优,以提高神经网络的性能,从而产生一个优化的模型。超参数的一个通俗的解释是:用来优化参数的参数。

    神经网络中的一些超参数是:

    1. 隐藏层的数量
    2. 隐含层中单位或节点的集合的数量
    3. 学习速率
    4. DropOut比例
    5. 迭代次数
    6. 优化器的选择如SGD, Adam, AdaGrad, Rmsprop等
    7. 激活函数选择如ReLU, sigmoid, leaky ReLU等
    8. 批次大小

    如何实现超参数优化?

    超参数优化是寻找深度学习算法的优化器、学习率、等超参数值,从而获得最佳模型性能的过程。

    可以使用以下技术执行超参数优化。

    • 手动搜索
    • 网格搜索:对指定超参数的所有可能组合进行穷举搜索,从而得到笛卡尔积。
    • 随机搜索:超参数是随机选择的,不是每一个超参数的组合都被尝试。随着超参数数量的增加,随机搜索是一个更好的选择,因为它可以更快地得到超参数的良好组合。
    • 贝叶斯优化:整合关于超参数的先验数据,包括模型的准确性或损失。先验信息有助于确定模型超参数选择的更好近似。

    为了在TensorBoard中可视化模型的超参数并进行调优,我们将使用网格搜索技术,其中我们将使用一些超参数,如不同的节点数量,不同的优化器,或学习率等看看模型的准确性和损失。

    为什么使用TensorBoard进行超参数优化?

    一幅图片胜过千言万语,这也适用于复杂的深度学习模型。深度学习模型被认为是一个黑盒子,你发送一些输入数据,模型做一些复杂的计算,输出结果。

    TensorBoard是Tensorflow的一个可视化工具包,用于显示不同的指标、参数和其他可视化,帮助调试、跟踪、微调、优化和共享深度学习实验结果

    TensorBoard可以跟踪模型在每个训练迭代的精度和损失;

    还有不同的超参数值。不同超参数值的跟踪精度将帮助您更快地微调模型。

    我们将使用猫和狗数据集使用TensorBoard可视化标量、图形和分布。

    https://www.kaggle.com/c/dogs-vs-cats/data

    导入所需的库

    导入TensorFlow和TensorBoard HParams插件以及Keras库来预处理图像和创建模型。

    import tensorflow as tf
    from tensorboard.plugins.hparams import api as hp
    import datetime
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
    from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img
    import numpy as np
    

    加载TensorBoard notebook扩展

    # Load the TensorBoard notebook extension
    %load_ext tensorboard
    

    创建图像分类的深度学习模型

    为训练设置关键参数

    BASE_PATH = 'Data\\dogs-vs-cats\\train\\'
    TRAIN_PATH='Data\\dogs-vs-cats\\train_data\\'
    VAL_PATH='Data\\dogs-vs-cats\\validation_data\\'batch_size = 32 
    epochs = 5
    IMG_HEIGHT = 150
    IMG_WIDTH = 150
    

    对训练图像进行缩放和不同的增强

    train_image_generator = ImageDataGenerator(                                               
    rescale=1./255,
    rotation_range=45, 
    width_shift_range=.15,
    height_shift_range=.15,
    horizontal_flip=True,
    zoom_range=0.3)
    

    重新调节验证数据

    validation_image_generator = ImageDataGenerator(rescale=1./255)
    

    为训练和验证生成成批的规范化数据

    train_data_gen = train_image_generator.flow_from_directory(
    batch_size = batch_size,                      
    directory=TRAIN_PATH,
    shuffle=True,
    target_size=(IMG_HEIGHT, IMG_WIDTH),
    class_mode='categorical')
    val_data_gen = validation_image_generator.flow_from_directory(batch_size = batch_size,
    directory=VAL_PATH,
    target_size=(IMG_HEIGHT, IMG_WIDTH), 
    class_mode='categorical')
    

    为网格搜索(Grid Search)设置超参数

    我们通过列出超参数的不同值或取值范围,使用了四个超参数来运行我们的实验。

    对于离散超参数,将尝试所有可能的参数组合,对于实值参数,只使用下界和上界。

    第一层的单元数量:256和512

    dropout比例:范围在0.1到0.2之间。所以dropout比例是0。1和0。2。

    优化器:adam, SGD, rmsprop

    优化器的学习率:0.001,0.0001和0.0005,

    我们还将准确率显示在TensorBoard 上

    ## Create hyperparameters
    HP_NUM_UNITS=hp.HParam('num_units', hp.Discrete([ 256, 512]))
    HP_DROPOUT=hp.HParam('dropout', hp.RealInterval(0.1, 0.2))
    HP_LEARNING_RATE= hp.HParam('learning_rate', hp.Discrete([0.001, 0.0005, 0.0001]))
    HP_OPTIMIZER=hp.HParam('optimizer', hp.Discrete(['adam', 'sgd', 'rmsprop']))METRIC_ACCURACY='accuracy'
    

    创建和配置日志文件

    log_dir ='\\logs\\fit\\' + datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
    with tf.summary.create_file_writer(log_dir).as_default():
        hp.hparams_config(
        hparams=
        [HP_NUM_UNITS, HP_DROPOUT,  HP_OPTIMIZER, HP_LEARNING_RATE],
        metrics=[hp.Metric(METRIC_ACCURACY, display_name='Accuracy')],
        )
    

    创建、编译和训练模型

    超参数不是硬编码的,但从hparams字典为不同的参数:HP_DROPOUT , HP_NUM_UNITS ,HP_OPTIMIZER ,HP_LEARNING_RATE。

    函数返回最后一个批次的验证准确性。

    def create_model(hparams):
        model = Sequential([
        Conv2D(64, 3, padding='same', activation='relu', 
               input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
        MaxPooling2D(),
        #setting the Drop out value based on HParam
        Dropout(hparams[HP_DROPOUT]),
        Conv2D(128, 3, padding='same', activation='relu'),
        MaxPooling2D(),
        Dropout(hparams[HP_DROPOUT]),
        Flatten(),
        Dense(hparams[HP_NUM_UNITS], activation='relu'),
        Dense(2, activation='softmax')])
        
        #setting the optimizer and learning rate
        optimizer = hparams[HP_OPTIMIZER]
        learning_rate = hparams[HP_LEARNING_RATE]
        if optimizer == "adam":
            optimizer = tf.optimizers.Adam(learning_rate=learning_rate)
        elif optimizer == "sgd":
            optimizer = tf.optimizers.SGD(learning_rate=learning_rate)
        elif optimizer=='rmsprop':
            optimizer = tf.optimizers.RMSprop(learning_rate=learning_rate)
        else:
            raise ValueError("unexpected optimizer name: %r" % (optimizer_name,))
        
        # Comiple the mode with the optimizer and learninf rate specified in hparams
        model.compile(optimizer=optimizer,
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
        
        #Fit the model 
        history=model.fit_generator(
        train_data_gen,
        steps_per_epoch=1000,
        epochs=epochs,
        validation_data=val_data_gen,
        validation_steps=1000,
        callbacks=[
            tf.keras.callbacks.TensorBoard(log_dir),  # log metrics
            hp.KerasCallback(log_dir, hparams),# log hparams
            
        ])
        return history.history['val_accuracy'][-1]
    

    对于模型的每次运行,使用超参数和最终批次精度记录hparams都会被纪律。我们需要将最后一个批次的验证精度转换为标量值。

    def run(run_dir, hparams):
      with tf.summary.create_file_writer(run_dir).as_default():
        hp.hparams(hparams)  # record the values used in this trial
        accuracy = create_model(hparams)
        #converting to tf scalar
        accuracy= tf.reshape(tf.convert_to_tensor(accuracy), []).numpy()
        tf.summary.scalar(METRIC_ACCURACY, accuracy, step=1)
    

    用不同的超参数值运行模型

    这里的实验使用网格搜索,并测试第一层单元数的所有可能的超参数组合,Dropout比例、优化器及其学习率,以及准确度用于准确性。

    session_num = 0for num_units in HP_NUM_UNITS.domain.values:
      for dropout_rate in (HP_DROPOUT.domain.min_value, HP_DROPOUT.domain.max_value):
        for optimizer in HP_OPTIMIZER.domain.values:
            for learning_rate in HP_LEARNING_RATE.domain.values:
              hparams = {
                  HP_NUM_UNITS: num_units,
                  HP_DROPOUT: dropout_rate,
                  HP_OPTIMIZER: optimizer,
                  HP_LEARNING_RATE: learning_rate,
              }
              run_name = "run-%d" % session_num
              print('--- Starting trial: %s' % run_name)
              print({h.name: hparams[h] for h in hparams})
              run('logs/hparam_tuning/' + run_name, hparams)
              session_num += 1
    

    在HParams中可视化结果

    python -m tensorboard.main --logdir="logs/hparam_tuning"
    

    当按精度降序排序时,可以看到最优化的模型是256台,dropout比例为0.2,rmsprop优化器学习率为0.0005。

    在jupyter notebook中可以使用以下命令查看

    %tensorboard --logdir='\logs\hparam_tuning'
    

    在Tensorboard中使用Parallel Coordinates视图,显示每个超参数的单独运行情况并显示精度,查找最优化的超参数,以获得最佳的模型精度

    总结

    Tensorboard为超参数调优提供了一种可视化的方式来了解哪些超参数可以用于微调深度学习模型以获得最佳精度,更多的操作可以查看官方文档:

    https://www.tensorflow.org/tensorboard/hyperparameter_tuning_with_hparams

    作者:Renu Khandelwal

    deephub翻译组

    展开全文
  • 介绍了ANSYS优化设计的过程,论述了如何将ANSYS强大的有限元分析、优化设计功能和我国设计规范相结合,用APDL进行用户化程序的开发,并运用到工程结构的优化设计中。实例中对一个十二杆桁架结构和江西国际汽车城GJ-3...
  • 积神经网络(CNN)的参数优化方法

    千次阅读 2020-12-22 12:42:57
    积神经网络(CNN)的参数优化方法from:http://blog.csdn.net/u010900574/article/details/51992156著名:本文是从 Michael Nielsen的电子书Neural Network and Deep Learning的深度学习那一章的卷积神经网络的参数...

    积神经网络(CNN)的参数优化方法

    from:http://blog.csdn.net/u010900574/article/details/51992156

    著名: 本文是从 Michael Nielsen的电子书Neural Network and Deep Learning的深度学习那一章的卷积神经网络的参数优化方法的一些总结和摘录,并不是我自己的结论和做实验所得到的结果。我想Michael的实验结果更有说服力一些。本书在github上有中文翻译的版本,

    前言

    最近卷积神经网络(CNN)很火热,它在图像分类领域的卓越表现引起了大家的广泛关注。本文总结和摘录了Michael Nielsen的那本Neural Network and Deep Learning一书中关于深度学习一章中关于提高泛化能力的一些概述和实验结果。力争用数据给大家一个关于正则化,增加卷积层/全连接数,弃权技术,拓展训练集等参数优化方法的效果。

    本文并不会介绍正则化,弃权(Dropout), 池化等方法的原理,只会介绍它们在实验中的应用或者起到的效果,更多的关于这些方法的解释请自行查询。

    mnist数据集介绍

    本文的实验是基于mnist数据集合的,mnist是一个从0到9的手写数字集合,共有60,000张训练图片,10,000张测试图片。每张图片大小是28*28大小。我们的实验就是构建一个神经网络来高精度的分类图片,也就是提高泛化能力。

    提高泛化能力的方法

    一般来说,提高泛化能力的方法主要有以下几个:

    正则化

    增加神经网络层数

    使用正确的代价函数

    使用好的权重初始化技术

    人为拓展训练集

    弃权技术

    下面我们通过实验结果给这些参数优化理论一个直观的结果

    1. 普通的全连接神经网络的效果

    我们使用一个隐藏层,包含100个隐藏神经元,输入层是784,输出层是one-hot编码的形式,最后一层是Softmax层。训练过程采用对数似然代价函数,60次迭代,学习速率η=0.1,随机梯度下降的小批量数据大小为10,没有正则化。在测试集上得到的结果是97.8%,代码如下:

    >>> import network3

    >>> from network3 import Network

    >>> from network3 import ConvPoolLayer, FullyConnectedLayer, SoftmaxLayer

    >>> training_data, validation_data, test_data = network3.load_data_shared()

    >>> mini_batch_size = 10

    >>> net = Network([

    FullyConnectedLayer(n_in=784, n_out=100),

    SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

    >>> net.SGD(training_data, 60, mini_batch_size, 0.1,

    validation_data, test_data)

    2.使用卷积神经网络 — 仅一个卷积层

    输入层是卷积层,5*5的局部感受野,也就是一个5*5的卷积核,一共20个特征映射。最大池化层选用2*2的大小。后面是100个隐藏神经元的全连接层。结构如图所示

     

    在这个架构中,我们把卷积层和chihua层看做是学习输入训练图像中的局部感受野,而后的全连接层则是一个更抽象层次的学习,从整个图像整合全局信息。也是60次迭代,批量数据大小是10,学习率是0.1.代码如下,

    >>> net = Network([

    ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),

    filter_shape=(20, 1, 5, 5),

    poolsize=(2, 2)),

    FullyConnectedLayer(n_in=20*12*12, n_out=100),

    SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

    >>> net.SGD(training_data, 60, mini_batch_size, 0.1,

    validation_data, test_data)

    经过三次运行取平均后,准确率是98.78%,这是相当大的改善。错误率降低了1/3,。卷积神经网络开始显现威力。

    3.使用卷积神经网络 — 两个卷积层

    我们接着插入第二个卷积-混合层,把它插入在之前的卷积-混合层和全连接层之间,同样的5*5的局部感受野,2*2的池化层。

    >>> net = Network([

    ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),

    filter_shape=(20, 1, 5, 5),

    poolsize=(2, 2)),

    ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),

    filter_shape=(40, 20, 5, 5),

    poolsize=(2, 2)),

    FullyConnectedLayer(n_in=40*4*4, n_out=100),

    SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

    >>> net.SGD(training_data, 60, mini_batch_size, 0.1,

    validation_data, test_data)

    这一次,我们拥有了99.06%的准确率。

    4.使用卷积神经网络 — 两个卷积层+线性修正单元(ReLU)+正则化

    上面我们使用的Sigmod激活函数,现在我们换成线性修正激活函数ReLU

    f(z)=max(0,z)

    ,我们选择60个迭代期,学习速率η=0.03, ,使用L2正则化,正则化参数λ=0.1

    ,代码如下

    >>> from network3 import ReLU

    >>> net = Network([

    ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),

    filter_shape=(20, 1, 5, 5),

    poolsize=(2, 2),

    activation_fn=ReLU),

    ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),

    filter_shape=(40, 20, 5, 5),

    poolsize=(2, 2),

    activation_fn=ReLU),

    FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),

    SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

    >>> net.SGD(training_data, 60, mini_batch_size, 0.03,

    validation_data, test_data, lmbda=0.1)

    这一次,我们获得了99.23%的准确率,超过了S型激活函数的99.06%. ReLU的优势是max(0,z)

    中z取最大极限时不会饱和,不像是S函数,这有助于持续学习。

    5.使用卷积神经网络 — 两个卷基层+线性修正单元(ReLU)+正则化+拓展数据集

    拓展训练集数据的一个简单方法是将每个训练图像由一个像素来代替,无论是上一个像素,下一个像素,或者左右的像素。其他的方法也有改变亮度,改变分辨率,图片旋转,扭曲,位移等。

    我们把50,000幅图像人为拓展到250,000幅图像。使用第4节一样的网络,因为我们是在训练5倍的数据,所以减少了过拟合的风险。

    >>> expanded_training_data, _, _ = network3.load_data_shared(

    "../data/mnist_expanded.pkl.gz")

    >>> net = Network([

    ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),

    filter_shape=(20, 1, 5, 5),

    poolsize=(2, 2),

    activation_fn=ReLU),

    ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),

    filter_shape=(40, 20, 5, 5),

    poolsize=(2, 2),

    activation_fn=ReLU),

    FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),

    SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

    >>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03,

    validation_data, test_data, lmbda=0.1)

    这次的到了99.37的训练正确率。

    6.使用卷积神经网络 — 两个卷基层+线性修正单元(ReLU)+正则化+拓展数据集+继续插入额外的全连接层

    继续上面的网络,我们拓展全连接层的规模,300个隐藏神经元和1000个神经元的额精度分别是99.46%和99.43%.

    我们插入一个额外的全连接层

    >>> net = Network([

    ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),

    filter_shape=(20, 1, 5, 5),

    poolsize=(2, 2),

    activation_fn=ReLU),

    ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),

    filter_shape=(40, 20, 5, 5),

    poolsize=(2, 2),

    activation_fn=ReLU),

    FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),

    FullyConnectedLayer(n_in=100, n_out=100, activation_fn=ReLU),

    SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

    >>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03,

    validation_data, test_data, lmbda=0.1)

    这次取得了99.43%的精度。拓展后的网络并没有帮助太多。

    7.使用卷积神经网络 — 两个卷基层+线性修正单元(ReLU)+拓展数据集+继续插入额外的全连接层+弃权技术

    弃权的基本思想就是在训练网络时随机的移除单独的激活值,使得模型对单独的依据丢失更为强劲,因此不太依赖于训练数据的特质。我们尝试应用弃权技术到最终的全连接层(不是在卷基层)。这里,减少了迭代期的数量为40个,全连接层使用1000个隐藏神经元,因为弃权技术会丢弃一些神经元。Dropout是一种非常有效有提高泛化能力,降低过拟合的方法!

    >>> net = Network([

    ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28),

    filter_shape=(20, 1, 5, 5),

    poolsize=(2, 2),

    activation_fn=ReLU),

    ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12),

    filter_shape=(40, 20, 5, 5),

    poolsize=(2, 2),

    activation_fn=ReLU),

    FullyConnectedLayer(

    n_in=40*4*4, n_out=1000, activation_fn=ReLU, p_dropout=0.5),

    FullyConnectedLayer(

    n_in=1000, n_out=1000, activation_fn=ReLU, p_dropout=0.5),

    SoftmaxLayer(n_in=1000, n_out=10, p_dropout=0.5)],

    mini_batch_size)

    >>> net.SGD(expanded_training_data, 40, mini_batch_size, 0.03,

    validation_data, test_data)

    使用弃权技术,的到了99.60%的准确率。

    8.使用卷积神经网络 — 两个卷基层+线性修正单元(ReLU)+正则化+拓展数据集+继续插入额外的全连接层+弃权技术+组合网络

    组合网络类似于随机森林或者adaboost的集成方法,创建几个神经网络,让他们投票来决定最好的分类。我们训练了5个不同的神经网络,每个都大到了99.60%的准去率,用这5个网络来进行投票表决一个图像的分类。

    采用这个方法,达到了99.67%的准确率。

    总结

    卷积神经网络 的一些技巧总结如下:

    1. 使用卷积层极大地减小了全连接层中的参数的数目,使学习的问题更容易

    2. 使用更多强有力的规范化技术(尤其是弃权和卷积)来减小过度拟合,

    3. 使用修正线性单元而不是S型神经元,来加速训练-依据经验,通常是3-5倍,

    4. 使用GPU来计算

    5. 利用充分大的数据集,避免过拟合

    6. 使用正确的代价函数,避免学习减速

    7. 使用好的权重初始化,避免因为神经元饱和引起的学习减速

    CNN超参数优化和可视化技巧详解

    转自:https://zhuanlan.zhihu.com/p/27905191

    在深度学习中,有许多不同的深度网络结构,包括卷积神经网络(CNN或convnet)、长短期记忆网络(LSTM)和生成对抗网络(GAN)等。

    在计算机视觉领域,对卷积神经网络(简称为CNN)的研究和应用都取得了显著的成果。CNN网络最初的诞生收到了动物视觉神经机制的启发,目前已成功用于机器视觉等领域中。

    技术博客Towards Data Science最近发布了一篇文章,作者Suki Lau。文章讨论了在卷积神经网络中,该如何调整超参数以及可视化卷积层。

    为什么用卷积神经网络?

    首先,我们想要计算机具有什么能力呢?

    当我们看到一只猫跳上窗台或在沙发上睡觉时,我们的潜意识会认出它是一只猫。

    我们希望计算机也能完成这项任务,即将图像输入后,找出其独有的特征,最终输出该图像的类别信息。

    卷积神经网络可以完成这项任务。

    何为卷积神经网络?

    先谈定义,卷积神经网络是一种特殊的神经网络,其中至少包含一个卷积层。在典型的CNN网络结构中,输入一张图像,经由一系列卷积层、非线性激活层、池化层和全连接层后,可输出相应的类别标签。

    卷积神经网络的特别之处在于加入了卷积层。

    在经典的神经网络中,整张图片会被传入网络中来训练各网络层权值。当输入为简单居中的图像时,如Mnist手写数字图,网络识别效果较优,但是当输入变为更为复杂多变的图像时,如跳上窗户的小猫,此时网络识别效果不佳甚至无法辨识。

    加入更多隐含层学习输入图像的抽象特征,可能会有所帮助,但是这样会增加神经元的数目,大大增加训练所需的计算资源和占用过多的内存,这是不切实际的。

    而CNN识别目标的过程,是先寻找诸如边缘、线段和曲线等相关低级特征,然后使用多个卷积层来构建更多抽象的高级特征。

    在卷积层的学习过程中,CNN网络通过共享多个卷积核(或特征检测器)的权值,来学习每张图片的局部信息,并用于构建抽象特征图谱。卷积核共享特性大大降低了训练网络所需的参数量。

    由于经过训练的检测器可以通过卷积层重复用来组合地检测图片中的抽象特征,因此卷积神经网络更适用于复杂的图像识别任务。

    超参数调整

    在深度神经网络中,调整超参数组合并非易事,因为训练深层神经网络十分耗时,且需要配置多个参数。

    接下来,我们简单列举几个影响CNN网络的关键超参数。

    学习率

    学习率是指在优化算法中更新网络权重的幅度大小。

    学习率可以是恒定的、逐渐降低的、基于动量的或者是自适应的,采用哪种学习率取决于所选择优化算法的类型,如SGD、Adam、Adagrad、AdaDelta或RMSProp等算法。

    优化策略这方面的内容可参阅量子位之前编译过的“一文看懂各种神经网络优化算法:从梯度下降到Adam方法”。

    迭代次数

    迭代次数是指整个训练集输入到神经网络进行训练的次数。当测试错误率和训练错误率相差较小时,可认为当前的迭代次数是合适的,否则需继续增大迭代次数,或调整网络结构。

    批次大小

    在卷积神经网络的学习过程中,小批次会表现得更好,选取范围一般位于区间[16,128]内。

    还需要注意的是,CNN网络对批次大小的调整十分敏感。

    激活函数

    激活函数具有非线性,理论上可以使模型拟合出任何函数。通常情况下,rectifier函数在CNN网络中的效果较好。当然,可以根据实际任务,选择其他类型的激活函数,如Sigmoid和Tanh等等。

    隐含层的数目和单元数

    增加隐含层数目以加深网络深度,会在一定程度上改善网络性能,但是当测试错误率不再下降时,就需要寻求其他的改良方法。增加隐含层数目也带来一个问题,即提高了训练该网络的计算成本。

    当网络的单元数设置过少时,可能会导致欠拟合,而单元数设置过多时,只要采取合适的正则化方式,就不会产生不良影响。

    权重初始化

    在网络中,通常会使用小随机数来初始化各网络层的权重,以防止产生不活跃的神经元,但是设置过小的随机数可能生成零梯度网络。一般来说,均匀分布方法效果较好。

    Dropout方法

    作为一种常用的正则化方式,加入Dropout层可以减弱深层神经网络的过拟合效应。该方法会按照所设定的概率参数,在每次训练中随机地不激活一定比例的神经单元。该参数的默认值为0.5。

    手动调整超参数是十分费时也不切实际。接下来介绍两种搜索最优超参数的常用方法。

    网格搜索和随机搜索

    网格搜索是通过穷举法列出不同的参数组合,确定性能最优的结构。随机搜索是从具有特定分布的参数空间中抽取出一定数量的候选组合。

    网格搜索方法也需要制定策略,在初始阶段最好先确定各超参数值的大概范围。可以先尝试在较小迭代次数或较小规模的训练集上进行大步幅的网格搜索。然后在下个阶段中,设置更大的迭代次数,或是使用整个训练集,实现小幅精确定位。

    虽然在许多机器学习算法中,通常会使用网格搜索来确定超参数组合,但是随着参数量的增大,训练网络所需的计算量呈指数型增长,这种方法在深层神经网络的超参数调整时效果并不是很好。

    有研究指出,在深度神经网络的超参数调整中,随机搜索方法比网格搜索的效率更高,具体可参考文末中的“随机搜索在超参数优化中的应用”。

    当然,可根据神经网络的理论经验,进行超参数的手动调整在一些场景下也是可行的。

    可视化

    我们可以通过可视化各个卷积层,来更好地了解CNN网络是如何学习输入图像的特征。

    可视化有两种直接方式,分别是可视化激活程度和可视化相关权重。在网络训练过程中,卷积层的激活情况通常会变得更为稀疏和具有局部特性。当不同输入图像的激活图都存在大片未激活的区域,那么可能是设置了过高的学习率使得卷积核不起作用,导致产生零激活图像。

    性能优良的神经网络通常含有多个明显而平滑的卷积器,且没有任何干扰特征。若在权重中观察到相关干扰特征,可能原因是网络未被充分训练,或是正则化强度较低导致了过拟合效应。

    神经网络参数优化--基于CNN的验证

    转自:https://ziyubiti.github.io/2016/11/20/cnnpara/

    当使用多层更深的隐藏层全连接网络时,参数量会变得非常巨大,达到数十亿量级;而采用CNN结构,则可以层间共享权重,极大减小待训练的参数量;同时可采用二维卷积,保留图像的空间结构信息;采用池化层,进一步减少参数计算。

    一般来说,提高泛化能力的方法主要有: 正则化、增加神经网络层数、改变激活函数与代价函数、使用好的权重初始化技术、人为扩展训练集、弃权技术。

    下面以MNIST为例,结合CNN、Pooling、Fc结构,通过不同的网络结构变化,给这些参数优化理论一个直观的验证结果。

    CNN不同网络结构性能比较

    可以看出:

    1、使用L2正则化,dropout技术,扩展数据集等,有效缓解过拟合,提升了性能;

    2、使用ReLU,导数为常量,可以缓解梯度下降问题,并加速训练;

    3、增加Conv/Pooling与Fc层,可以改善性能。(我自己实测也是如此)

    Note:

    1、网络并非越深越好,单纯的Conv/Pooling/Fc结构,增加到一定深度后由于过拟合性能反而下降。

    2、网络结构信息更重要,如使用GoogleNet、ResNet等。

    知乎上的讨论:

    转自:https://www.zhihu.com/question/41631631

    训练技巧对深度学习来说是非常重要的,作为一门实验性质很强的科学,同样的网络结构使用不同的训练方法训练,结果可能会有很大的差异。这里我总结了近一年来的炼丹心得,分享给大家,也欢迎大家补充指正。

    参数初始化。

    下面几种方式,随便选一个,结果基本都差不多。但是一定要做。否则可能会减慢收敛速度,影响收敛结果,甚至造成Nan等一系列问题。

    下面的n_in为网络的输入大小,n_out为网络的输出大小,n为n_in或(n_in+n_out)*0.5

    uniform均匀分布初始化: w = np.random.uniform(low=-scale, high=scale, size=[n_in,n_out])

    Xavier初始法,适用于普通激活函数(tanh,sigmoid):scale = np.sqrt(3/n)

    He初始化,适用于ReLU:scale = np.sqrt(6/n)

    normal高斯分布初始化: w = np.random.randn(n_in,n_out) * stdev # stdev为高斯分布的标准差,均值设为0

    Xavier初始法,适用于普通激活函数 (tanh,sigmoid):stdev = np.sqrt(n)

    He初始化,适用于ReLU:stdev = np.sqrt(2/n)

    数据预处理方式

    zero-center ,这个挺常用的. X -= np.mean(X, axis = 0) # zero-center X /= np.std(X, axis = 0) # normalize

    PCA whitening,这个用的比较少.

    训练技巧

    要做梯度归一化,即算出来的梯度除以minibatch size

    clip c(梯度裁剪): 限制最大梯度,其实是value = sqrt(w1^2+w2^2….),如果value超过了阈值,就算一个衰减系系数,让value的值等于阈值: 5,10,15

    dropout对小数据防止过拟合有很好的效果,值一般设为0.5,小数据上dropout+sgd在我的大部分实验中,效果提升都非常明显(实测sgd比adam好).因此可能的话,建议一定要尝试一下。 dropout的位置比较有讲究, 对于RNN,建议放到输入->RNN与RNN->输出的位置.关于RNN如何用dropout,可以参考这篇论文:http://arxiv.org/abs/1409.2329

    adam,adadelta等,在小数据上,我这里实验的效果不如sgd, sgd收敛速度会慢一些,但是最终收敛后的结果,一般都比较好。如果使用sgd的话,可以选择从1.0或者0.1的学习率开始,隔一段时间,在验证集上检查一下,如果cost没有下降,就对学习率减半. 我看过很多论文都这么搞,我自己实验的结果也很好. 当然,也可以先用ada系列先跑,最后快收敛的时候,更换成sgd继续训练.同样也会有提升.据说adadelta一般在分类问题上效果比较好,adam在生成问题上效果比较好。

    除了gate之类的地方,需要把输出限制成0-1之外,尽量不要用sigmoid,可以用tanh或者relu之类的激活函数.1. sigmoid函数在-4到4的区间里,才有较大的梯度。之外的区间,梯度接近0,很容易造成梯度消失问题。2. 输入0均值,sigmoid函数的输出不是0均值的。

    rnn的dim和embdding size,一般从128上下开始调整. batch size,一般从128左右开始调整.batch size合适最重要,并不是越大越好.

    word2vec初始化,在小数据上,不仅可以有效提高收敛速度,也可以可以提高结果.

    尽量对数据做shuffle

    LSTM 的forget gate的bias,用1.0或者更大的值做初始化,可以取得更好的结果,来自这篇论文:http://jmlr.org/proceedings/papers/v37/jozefowicz15.pdf, 我这里实验设成1.0,可以提高收敛速度.实际使用中,不同的任务,可能需要尝试不同的值.

    Batch Normalization据说可以提升效果,不过我没有尝试过,建议作为最后提升模型的手段,参考论文:Accelerating Deep Network Training by Reducing Internal Covariate Shift

    如果你的模型包含全连接层(MLP),并且输入和输出大小一样,可以考虑将MLP替换成Highway Network,我尝试对结果有一点提升,建议作为最后提升模型的手段,原理很简单,就是给输出加了一个gate来控制信息的流动,详细介绍请参考论文: http://arxiv.org/abs/1505.00387

    来自@张馨宇的技巧:一轮加正则,一轮不加正则,反复进行。

    Ensemble

    Ensemble是论文刷结果的终极核武器,深度学习中一般有以下几种方式

    同样的参数,不同的初始化方式

    不同的参数,通过cross-validation,选取最好的几组

    同样的参数,模型训练的不同阶段,即不同迭代次数的模型。

    不同的模型,进行线性融合. 例如RNN和传统模型.

    其实我发现现在深度学习越来越成熟,调参工作比以前少了很多,绝大多数情况自己设计的参数都不如教程和框架的默认参数好,不过有一些技巧我一直都在用的

    (1)relu+bn。这套好基友组合是万精油,可以满足95%的情况,除非有些特殊情况会用identity,比如回归问题,比如resnet的shortcut支路,sigmoid什么的都快从我世界里消失了

    (2)dropout 。分类问题用dropout ,只需要最后一层softmax 前用基本就可以了,能够防止过拟合,可能对accuracy提高不大,但是dropout 前面的那层如果是之后要使用的feature的话,性能会大大提升(例如max pool进入fc,实测发现加BN效果非常明显)

    (3)数据的shuffle 和augmentation 。这个没啥好说的,aug也不是瞎加,比如行人识别一般就不会加上下翻转的,因为不会碰到头朝下的异型种

    (4)降学习率。随着网络训练的进行,学习率要逐渐降下来,如果你有tensorboard,你有可能发现,在学习率下降的一瞬间,网络会有个巨大的性能提升,同样的fine-tuning也要根据模型的性能设置合适的学习率,比如一个训练的已经非常好的模型你上来就1e-3的学习率,那之前就白训练了,就是说网络性能越好,学习率要越小

    (5)tensorboard。以前不怎么用,用了之后发现太有帮助,帮助你监视网络的状态,来调整网络参数

    (6)随时存档模型,要有validation 。这就跟打游戏一样存档,把每个epoch和其对应的validation 结果存下来,可以分析出开始overfitting的时间点,方便下次加载fine-tuning

    (7)网络层数,参数量什么的都不是大问题,在性能不丢的情况下,减到最小

    (8)batchsize通常影响没那么大,塞满卡就行,除了特殊的算法需要batch大一点

    (9)输入减不减mean归一化在有了bn之后已经不那么重要了

    上面那些都是大家所知道的常识,也是外行人觉得深度学习一直在做的就是这些很low的东西,其实网络设计(关键!!!实测发现对于acc影响极大!)上博大精深,这也远超过我的水平范畴,只说一些很简单的

    (1)卷积核的分解。从最初的5×5分解为两个3×3,到后来的3×3分解为1×3和3×1,再到resnet的1×1,3×3,1×1,再xception的3×3 channel-wise conv+1×1,网络的计算量越来越小,层数越来越多,性能越来越好,这些都是设计网络时可以借鉴的

    (2)不同尺寸的feature maps的concat,只用一层的feature map一把梭可能不如concat好,pspnet就是这种思想,这个思想很常用

    (3)resnet的shortcut确实会很有用,重点在于shortcut支路一定要是identity,主路是什么conv都无所谓,这是我亲耳听resnet作者所述

    (4)针对于metric learning,对feature加个classification 的约束通常可以提高性能加快收敛

    补充一点,adam收敛虽快但是得到的解往往没有sgd+momentum得到的解更好,如果不考虑时间成本的话还是用sgd吧。

    再补充一个rnn trick,仍然是不考虑时间成本的情况下,batch size=1是一个很不错的regularizer, 起码在某些task上,这也有可能是很多人无法复现alex graves实验结果的原因之一,因为他总是把batch size设成1。。。

    没做过CNN,RNN,调过连续值DNN,以下经验仅限于CTR

    1.样本要足够随机

    2.样本要做归一化

    3.激活函数要视样本输入选择

    4.minibatch很重要,几百到几千是比较合适的(很大数据量的情况下)

    5.learning rate很重要,可以直接用adagrad or adadelta,省去一些麻烦,然后把冲量调到0.9以上

    6.权重初始化,可用高斯分布乘上一个很小的数

    小白一枚,在这里总结一下我在试验中观察到的现象(必然有理解错误的地方):

    1. Adam收敛速度的确要快一些,可是结果总是不如其他优化算法,如果很看重结果不在乎速度还是用其他的试试。

    2. Dropout的放置位置以及大小非常重要,求大神能分享经验.....

    3. Relu并不是一定比Tanh好,如果不太懂的话,用的不合适,可能会导致梯度消失?(不知道是不是网络结构问题,为什么一用relu梯度一会儿就变成Nan)

    4. pretrain 的 Embedding在训练中不调优泛化能力要更好一些,调优的话参数会增加好多啊。

    另:心得体会

    1. 深度学习真是一门实验科学,很多地方解释不了为什么好,为什么不好。

    2.如果你机器配置很不到位,也没有人带你,毕业设计千万别选深度学习,天天愁,好坑啊。

    最近在看 Karpathy 的 cs231n, 还没看完, 不过过程中总结了一下他提到的一些技巧:

    关于参数:

    通常情况下, 更新参数的方法默认用 Adam 效果就很好

    如果可以载入全部数据 (full batch updates), 可以使用 L-BFGS

    Model Ensembles:

    训练多个模型, 在测试时将结果平均起来, 大约可以得到 2% 提升.

    训练单个模型时, 平均不同时期的 checkpoints 的结果, 也可以有提升.

    测试时可以将测试的参数和训练的参数组合起来:

    分享几个常用的trick:

    1.增加每个step的轮数

    2.early stop

    3.用小一些的学习率warmup

    4.回退到更大的学习率

    5.nesterov momentum sgd

    6.搜索初始学习率

    1.better initialization helps a lot

    2.use minibatch and choose batch_size(must)

    3.use batch_norm &dropout

    4.use adam

    5.plot the learning rate curve

    6.plot the loss curve.

    7.lstm &gru are almost always better than sample RNN

    8.use better framework(like tensorflow with tensorboard)

    9.find hyper parameters used most often in paper

    10 pray

    cnn的调参主要是在优化函数、embedding的维度还要残差网络的层数几个方面。

    优化函数方面有两个选择:sgd、adam,相对来说adam要简单很多,不需要设置参数,效果也还不错。

    embedding随着维度的增大会出现一个最大值点,也就是开始时是随维度的增加效果逐渐变好,到达一个点后,而后随维度的增加,效果会变差。

    残差网络的层数与embedding的维度有关系,随层数的增加,效果变化也是一个凸函数。

    另外还有激活函数,dropout层和batchnormalize层的使用。激活函数推荐使用relu,dropout层数不易设置过大,过大会导致不收敛,调节步长可以是0.05,一般调整到0.4或者0.5就可找到最佳值。

    以上是个人调参的一些经验,可供参考。

    1.无论是cnn还是rnn,batch normalization都有用,不一定结果提高几个点,收敛快多了

    2.数据初始时normalize得好,有时候直接提高2个点,比如cifar10,转到yuv下normalize再scn

    3.loss不降了lr就除10

    4. google的inception系列按它论文里说的永远无法复现

    如何训练深度神经网络?老司机的 15 点建议

    导语:印度深度学习专家 Rishabh Shukla 对开发深度神经网络的经验总结。

    本文为印度深度学习专家、创业者 Rishabh Shukla 在 GitHub 上发表的长博文,总结了他过去的开发经验,旨在给新入门的开发者提供指导。雷锋网做了不改变原意的编译。

    在深度学习领域,为了高效训练深度神经网络,有些实践方法被过来人强烈推荐。

    在这篇博文中,我会覆盖几种最常使用的实践方法,从高品质训练数据的重要性、超参数(hyperparameters)到更快创建 DNN(深度神经网络) 原型模型的一般性建议。这些推荐方法中的大多数,已被学术界的研究所证实,并在论文中展示了相关实验、数学证据,比如 Efficient BackProp(Yann LeCun et al.)和 Practical Recommendations for Deep Architectures(Yoshua Bengio)。

    1. 训练数据

    许多 ML 开发者习惯把原始训练数据直接扔给 DNN——为什么不这么做呢?既然任何 DNN (大多数人的假设)仍然能够给出不错的结果,不是吗?但是,有句老话叫“给定恰当的数据类型,一个简单的模型能比复杂 DNN 提供更好、更快的结果”。虽然这有一些例外,但在今天,这句话仍然没有过时。因此,不管你是在计算机视觉( CV),自然语言处理(NLP)还是统计建模(Statistical Modelling)等领域,想要对原始数据预处理,有几个方法可以得到更好的训练数据:

    获取越大的数据库越好。DNN 对数据很饥渴,越多越好。

    去除所有包含损坏数据的训练样本,比如短文字,高度扭曲的图像,假输出标签,包含许多虚值(null values)的属性。

    Data Augmentation(数据扩张)——生成新样例。以图像为例,重新调节,增加噪声等等。

    2. 选择恰当的激励函数(activation function)

    激励函数是所有神经网络的核心部分之一。

    激励函数把渴望已久的非线性(non-linearity)加入了模型。多年来,Sigmoid 函数 一直是多数人倾向的选择。但是,Sigmoid 函数不可避免地存在两个缺陷:1. 尾部  sigmoids 的饱和,进一步导致梯度消失。2. 不以 0 为中心(输出在 0 到 1 之间)。

    一个更好的替代选择是 Tanh 函数。数学上来说,Tanh 只是调整、平移过的 Sigmoid 函数:tanh(x) = 2*sigmoid(x) - 1。虽然 Tanh 仍旧存在梯度消失的缺陷,但好消息是:Tanh 以 0 为中心。因此,把 Tanh 作为激励函数能更快地收敛(converge)。我发现使用 Tanh 通常比 Sigmoid 效果更好。

    你还可以探索其他选择,比如 ReLU, SoftSign 等等。对于一些特定任务, 它们能够改善上述问题。

    3. 隐藏单元和隐层(Hidden Units and Layers)的数量

    保留超出最优数量的隐藏单元,一般是比较保险的做法。这是因为任何正则化方法( regularization method)都会处理好超出的单元,至少在某种程度上是这样。在另一方面,保留比最优数量更少的隐藏单元,会导致更高的模型欠拟合(underfitting)几率。

    另外,当采用无监督预训练的表示时(unsupervised pre-trained representations,下文会做进一步解释),隐藏单元的最优数目一般会变得更大。因此,预训练的表示可能会包含许多不相关信息(对于特定任务)。通过增加隐藏单元的数目,模型会得到所需的灵活性,以在预训练表示中过滤出最合适的信息。

    选择隐层的最优数目比较直接。正如 Yoshua Bengio 在  Quora 中提到的:

    “你只需不停增加层,直到测试误差不再减少。”

    4. 权重初始化 (Weight Initialization)

    永远用小的随机数字初始化权重,以打破不同单元间的对称性(symmetry)。但权重应该是多小呢?推荐的上限是多少?用什么概率分布产生随机数字?

    当使用 Sigmoid 激励函数时,如果权重初始化为很大的数字,那么 sigmoid 会饱和(尾部区域),导致死神经元(dead neurons)。如果权重特别小,梯度也会很小。因此,最好是在中间区域选择权重,比如说那些围绕平均值均衡分布的数值。

    幸运的是,已经有许多关于初始权重合适取值的研究。这对于高效的收敛非常重要。为初始化均衡分布的权重,均匀分布(uniform distribution )或许是最好的选择之一。另外,就像论文中所展示的(Glorot and Bengio, 2010),有更多输入连接(fan_in)的单位,应该有相对更小的权重。

    多亏这些十分透彻的试验,现在我们已经有了经过检验的公式,可以直接用来权重的初始化。

    比如说在  ~ Uniform(-r, r) 提取的权重,对于 tanh 激励  r=sqrt(6/(fan_in+fan_out));对于 sigmoid 激励 r=4*(sqrt(6/fan_in+fan_out)) 。fan_in 是上一层的大小, 而 fan_out 是下一层的。

    5. 学习率

    这或许是最重要的超参数之一,调节着学习过程。如果学习率设置得太小,你的模型很可能需要 n 年来收敛。设置得太大,再加上不多的初始训练样本,你的损失可能会极高。一般来说,0.01 的学习率比较保险

    相比固定学习率,在每个周期、或每几千个样例后逐渐降低学习率是另一个选择。虽然这能更快地训练,但需要人工决定新的学习率。一般来说,学习率可以在每个周期后减半。几年前,这种策略十分普遍。

    幸运的是,我们现在有了更好的、基于动能(momentum based)的方法,来调整学习率。这取决于误差函数的曲率。另外,既然有些参数有更快、或更慢的学习速率;它或许能帮助我们针对模型中的单独参数,设定不同的学习率。

    最近有大量关于优化方法的研究,导致了自适应学习率(adaptive learning rates)。目前我们有许多选择,从老式动能方法( Momentum Method ),到  Adagrad、Adam (个人最爱)、 RMSProp 等等。;类似于 Adagrad 或 Adam 的方法,能替我们省去人工选择初始学习率的麻烦;给定合适的时间,模型会开始平滑地收敛。当然,选择一个特别合适的初始学习率仍然能起到帮助作用。

    6. 超参数调参:扔掉网格搜索,拥抱随机搜索

    网格搜索(Grid Search )在经典机器学习中十分普遍。但它在寻找 DNN 的最优超参数方面一点也不高效。这主要是由于 DNN 尝试不同超参数组合所耗费的时间。随着超参数不断增长,网格搜索需要的计算性能会指数级增长。

    有两种解决办法:

    取决于你之前的经验,你可以人工对部分常见超参数调参,比如学习率、隐层数目。

    采用随机搜索(random search),或者随机采样代替网格搜索,来选择最优超参数。

    超参数组合通常在期望范围之内、从均匀分布中被选择出来。加入之前获得的知识来进一步缩小搜寻空间,也是有可能的(比如,学习率不应该太大也不应该太小)。大家发现,随机搜索比网格搜索高效地多。

    7. 学习方法

    随机梯度下降( Stochastic Gradient Descent )的老方法也许对于 DNN 不是那么有效率(有例外)。最近,有许多研究聚焦于开发更灵活的优化算法,比如 Adagrad、Adam,、AdaDelta,、RMSProp 等等。在提供自适应学习率之外,这些复杂的方法还对于模型的不同参数使用不同的学习率,通常能有更平滑的收敛。把这些当做超参数是件好事,你应该每次都在训练数据的子集上试试它们。

    8. 权重的维度保持为 2 的幂

    即便是运行最先进的深度学习模型,使用最新、最强大的计算硬件,内存管理仍然在字节(byte)级别上进行。所以,把参数保持在 64, 128, 512, 1024 等 2 的次方永远是件好事。这也许能帮助分割矩阵和权重,导致学习效率的提升。当用 GPU 运算,这变得更明显。

    9. 无监督预训练(Unsupervised Pretraining )

    不管你进行的是 NLP(自然语言处理)、计算机视觉还是语音识别等任务,无监督预训练永远能帮助你训练监督、或其他无监督模型:NLP 中词向量就(Word Vectors)无所不在;你可以用 ImageNet 的数据库,使用无监督方式对你的模型预训练,或是对于两个类别的监督分类;或是更大频域的音频样本,来在扬声器消崎模型(speaker disambiguation model)中使用该信息。

    10. Mini-Batch(小批量) 对比随机学习(Stochastic Learning)

    训练一个模型的主要目的是学习合适的参数,即产生输入到输出的最优映射。这些参数利用每个训练样本进行调参,不管你决定使用 batch, mini-batch 还是随机学习。当采用随机学习方法时,学习每个训练样本后权重的梯度都会进行调参,向梯度加入噪音(随机学习中“随机”的由来)。这样做的结果十分理想,比如说,训练中加入的噪音使得模型更不容易过拟合。

    但是,随机学习方法也许效率不高。如今的计算设备有非常可观的运算能力,随机学习很可能会浪费其中的一大部分。如果我们能计算矩阵相乘,那么为什么要限制自己,重复单个矢量组之间的乘法呢?因此,为了更高的吞吐率和更快的学习,我推荐使用 mini-batch 而不是随机学习。

    但是,选择适当的 batch 规模同样重要。所以我们能保留一些噪音(相比大规模 batch),与此同时更高效地利用计算性能。一般来说,包含  16 个到 128 个样例的 batch(2 的幂)是不错的选择。通常,一旦你发现了更重要的超参数(通过随机搜索或是人工搜索),batch 规模就会确性下来。但是,有些场景中模型得到训练数据流(比如网络学习),那么采用随机学习就是不错的选择。

    11. 打乱训练样本

    这来自于信息理论(Information Theory)——“学习到一件不太可能发生的事却发生了,比学习一件很可能发生的事已经发生,包含更多的信息。”同样的,把训练样例的顺序随机化(在不同周期,或者 mini-batch),会导致更快的收敛。如果模型看到的很多样例不在同一种顺序下,运算速度会有小幅提升。

    12. 使用 Dropout 正则化

    如果有数百万的参数需要学习,正则化就是避免 DNN 过拟合的必须手段。你也可以继续使用 L1/L2 正则化,但 Dropout 是检查 DNN 过拟合的更好方式(雷锋网按:Dropout 是指随机让网络某些隐层节点的权重不工作,不工作的那些节点可以暂时认为不是网络结构的一部分,但是它的权重会保留下来)。执行 Dropout 很容易,并且通常能带来更快地学习。0.5 的默认值是一个不错的选择,当然,这取决于具体任务。如果模型不太复杂,0.2 的 Dropout 值或许就够了。

    在测试阶段,Dropout 应该被关闭,权重要调整到相应大小。只要对一个模型进行 Dropout 正则化,多一点训练时间,误差一定会降低。

    13. 周期 / 训练迭代次数

    “对深度学习模型进行多个周期的训练,会得到更好的模型”——我们经常听到这句话。但多少周期才是“多”呢?其实,这里有一个简单的策略:继续按照一个固定的样例数或者周期训练模型,比如两万个样例或者一个周期。在每批样例之后,比较测试误差(test error)和训练误差(train error),如果它们的差距在缩小,那么继续训练。另外,记得在每批训练之后,保存模型的参数,所以训练好之后你可以从多个模型中做选择。

    14. 可视化

    训练深度学习模型有上千种出差错的方式。我猜大家都遇到过这样的场景:模型已经训练了几个小时或者好几天,然而在训练完成之后,才意识到某个地方出问题了。为了不让你自己神经错乱,一定要对训练过程作可视化处理。比较显而易见的措施是保存或打印损失值、训练误差、测试误差等项目的日志。

    在此之外,一个很好的措施是采用可视化库(visualization library ),在几个训练样例之后、或者周期之间,生成权重柱状图。这或许能帮助我们追踪深度学习模型中的一些常见问题,比如梯度消失与梯度爆发(Exploding Gradient)。

    15. 使用支持 GPU 和自动微分法 (Automatic Differentiation)的库

    谢天谢地,对于快速创建原型模型,我们已经有了相当不错的库,比如 Theano, Tensorflow, Keras 等等。几乎所有这些深度学习库支持 GPU 计算和自动微分法。所以,你不需要深入研究核心 GPU 编程技术(除非你想——这绝对很有意思)。你也不需要写自己的微分代码——在非常复杂的模型上这相当费劲(但若需要,你应该有能力去做)。 Tensorflow还提供了分布式计算的支持——如果你是土豪的话.

    展开全文
  • 该工具试图弥合相对复杂的 LTI 控制设计和易用性之间的差距。 通过将控制设计问题表述为非线性优化问题,该工具可以获得强制闭环系统满足稳定时间和阻尼比目标规范参数或解决方案。 它不限于任何一种控制结构。
  • arm优化编译参数选项解释

    千次阅读 2019-11-15 13:50:16
    CFLAGS += -Wall -O2 -mcpu=cortex-a53 -mfloat-abi=softfp -mfpu=neon-vfpv4 -mno-unaligned-access -fno-...其中-Otime参数可能会报错,改为-Ofast,表示在-O2/-O3优化时,优先考虑执行速度进行优化  
  • 研究能够实现较大传动比、较大功率、较高效率且结构简单、紧凑的非H封闭式周转轮系的建模、多目标多约束模糊优化设计、相配套的装配图及主要零件图的参数化设计。在对非H封闭式周转轮系的结构类型和特点进行分析的...
  • 神经网络的参数优化方法

    万次阅读 多人点赞 2018-10-23 14:05:42
    著名: 本文是从 Michael Nielsen的电子书Neural Network and Deep Learning的深度学习那一章的卷积神经网络的参数优化方法的一些总结和摘录,并不是我自己的结论和做实验所得到的结果。我想Michael的实验结果更有...
  • 数据库规范优化方案(一)

    万次阅读 2020-01-10 21:28:29
    点击上方“码农进阶之路”,选择“设为星标”回复“面经”获取面试资料一、数据库设计方面1、对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立...
  • 卷积神经网络(CNN)的参数优化方法

    万次阅读 多人点赞 2019-06-08 19:32:17
    积神经网络的参数优化方法——调整网络结构是关键!!!你只需不停增加层,直到测试误差不再减少. 著名:本文是从 Michael Nielsen的电子书Neural Network and Deep Learning的深度学习那一章的卷积神经网络的参数...
  • 参数优---贝叶斯优化及其改进(PBT优化

    万次阅读 多人点赞 2020-01-08 21:14:34
    自动机器学习超参数调整(贝叶斯优化)—介绍了例子 贝叶斯优化(Bayesian Optimization)深入理解 贝叶斯优化(BayesianOptimization) 简介 所谓优化,实际上就是一个求极值的过程,数据科学的很多时候就是求极值...
  • 代码优化思路 前提 编程军规 编程时,需要遵循营地法则:保证你离开时,代码库比你来时更健康;(重构,改善代码的既有设计) 好代码的检验标准 人们是否能轻而易举的修改它(重构,改善代码的既有设计) 注释 特别...
  • 华为C语言编程规范(精华总结)

    万次阅读 多人点赞 2020-03-24 09:48:55
    一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。 2、简洁为美 简洁就是易于理解并且易于实现。代码越长越难以看懂,也就越容易在修改时引入错误。写的代码越多,意味着出错的地方越多...
  • 本节,我们会讨论一种最为常用的规范化(regularization)手段——有时候被称为权重衰减(weight-decay)或者L2规范化(regularization)。L2规范化的想法是增加一个额外的项到代价函数上,这个项叫做规范化项。下面...
  • 卷积神经网络参数优化策略(一)

    千次阅读 2020-05-14 11:20:53
    力争用数据给大家一个关于正则化,增加卷积层/全连接数,弃权技术,拓展训练集等参数优化方法的效果。 提高泛化能力的方法,一般来说,提高泛化能力的方法主要有以下几个: 正则化 增加神经网络层数 使用正确的代价...
  • 史上最强Tomcat8性能优化

    万次阅读 多人点赞 2019-10-25 15:33:32
    文章目录授人以鱼不如授人以渔目的服务器资源Tomcat配置优化Linux环境安装运行Tomcat8AJP连接执行器(线程池)3种运行模式部署测试用的web项目...测试报告调整Tomcat参数进行优化禁用AJP连接设置线程池最大线程数为150...
  • 资源名称:深入JVM内核—原理、诊断与优化 教程内容:课程简介:第一周初识JVMJVM分类Java语言规范JVM规范介绍JVM的基本知识和发展历史,并介绍了Java语言规范和JVM规范。第二周JVM运行机制简介堆、栈、方法区等JVM...
  • gcc编译优化参数

    千次阅读 2012-08-11 17:46:07
    ...GCC编译器选项及优化提示 ...大多数程序和库在编译时默认的优化级别是"2"(使用gcc选项:"-...如果你只想让编译出来的程序运行在特定的平台上,就需要执行更高级的编译器优化选项,以产生只能运行于特定平台的代码
  • 此篇文章将简单讲解游戏中,场景的优化,以及制作规范,简单技巧和这样做的原因。 【前言】 优化是影响游戏体验的一个重要因素,好的优化,能够让玩家手感,体验达到最佳,而优化太差,会导致很多问题,比如内存...
  • #优化启动:非阻塞执行,牺牲CPU换取快速启动 CMD java -Djava.security.egd=file:/dev/./urandom -jar baye-eureka.jar 2、替换boot中的tomcat容器 <!--web 模块--> <dependency&...
  • 为处理地铁侵限问题,采用实际掘进中线坐标估计优化线路参数,以地铁实际掘进中线为基础,整体上对线路参数进行优化,以满足设计约束及侵限要求,可最大限度地提升对复杂侵限情况处理的能力。阐述了调整设计中线以...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 206,824
精华内容 82,729
关键字:

优化参数规范