深度学习入门

匿名 (未验证) 提交于 2019-12-03 00:15:02

Deep Learning with Pytorch_002

chapter03_深入研究神经网络的构建块

  1. 在上一章中,我们使用Py Torch的低级操作来构建模块,如网络体系结构、损失函数和优化器。在本章中,我们将探讨解决现实世界问题所需的神经网络的一些重要组成部分,以及PyTorch如何通过提供大量的高级函数来抽象出大量的复杂性。
    本章将讨论以下主题:
  • 深入研究神经网络的各种构建块
  • 探索PyTorch中的高级功能来构建深度学习体系结构
  • 将深度学习应用在一个真实图像分类问题
  1. 任何深入的学习训练都需要获取数据,构建一个总体上是将一堆层聚集在一起的体系结构。
  2. 理解PyTorch为构建层、损失函数和优化器提供的更高层次的抽象。
  • 层次――神经网络的基本块

    • 最重要的层之一――线性层

      对输入的数据应用线性变换:y=xAT+byy = xA^T + by

       torch.randn(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) → Tensor 

      返回一个由均值为0、方差为1的正态分布(标准正态分布)中的随机数组成的张量。
      outiN(0,1)
      torch.from_numpy(ndarray) → Tensor是将numpy类型转换为Tensor的函数

       # from_numpy 测试 ,从numpy.ndarray中创建一个张量,  # 返回的张量和ndarray共享相同的内存。张量的变化将反映在ndarray中,反之亦然;  # 返回的张量是不可调整的(这句话是什么意思)  a = numpy.array([1, 2, 3])  t = torch.from_numpy(a)  print(t)  # tensor([ 1,  2,  3])  print(a)  t[0] = -1  print(a)  # array([-1,  2,  3])  print(t)  # tensor([-1,  2,  3]) 

      完整的线性变换实现代码(还没用linear):

       import torch  torch.__version__  import numpy as np  import matplotlib.pyplot as plt  from torch.autograd import Variable  #   Creating data for our neural network  def get_data():      train_X = np.asarray([3.3, 4.4, 5.5, 6.71, 6.93, 4.168, 9.779, 6.182, 7.59, 2.167, 7.042, 10.791, 5.313, 7.997, 5.654, 9.27, 3.1])      train_Y = np.asarray([1.7, 2.76, 2.09, 3.19, 1.694, 1.573, 3.366, 2.596, 2.53, 1.221, 2.827, 3.465, 1.65, 2.904, 2.42, 2.94, 1.3])      dtype = torch.FloatTensor      X = Variable(torch.from_numpy(train_X).type(dtype), requires_grad=False).view(17, 1)    # 对X进行转置      y = Variable(torch.from_numpy(train_Y).type(dtype), requires_grad=False)      return X, y  # Creating learnable parameters  def get_weights():      w = Variable(torch.randn(1), requires_grad=True)      b = Variable(torch.zeros(1), requires_grad=True)      return w, b  #   Network implementation  def simple_network(x):      y_pred = torch.matmul(x, w) + b      return y_pred  #   loss function  def loss_fn(y, y_pred):      loss = torch.mean((y - y_pred) ** 2)      for param in [w, b]:          if not param.grad is None:              param.grad.data.zero_()  # 第一次时,需要将梯度清零将梯度             loss.backward()  # 计算可学习参数w和b的梯度      return loss.data  # Optimize the neural network  def optimize(learning_rate):      w.data -= learning_rate * w.grad.data      b.data -= learning_rate * b.grad.data  learning_rate = 0.005  x,y = get_data()  w,b = get_weights()  num_epochs = 100    # 这个数据不能太大,太大会加大误差  for epoch in range(num_epochs):      inputs = x      targets = y      # Forward pass      outputs = simple_network(inputs)      # Backward and optimize      loss = loss_fn(outputs, targets)      optimize(learning_rate)      if (epoch + 1) % 10 == 0:         print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch + 1, num_epochs, loss.item()))         print('outputs:')         print(outputs)         print(loss.item())  plt.plot(x.detach().numpy(), y.detach().numpy(), 'ro', label='Original Data')  plt.plot(x.detach().numpy(), outputs.detach().numpy(), label='Fitting Line')  plt.legend()  plt.show() 

      但在Pytorch中,它的强大之处在于, 对于线性层的线性变换:Y=Wx+b,在上面编写的整个函数可以用一行代码编写,如下所示:

      from torch.nn import Linear, ReLU myLayer = Linear(in_features=10,out_features=5,bias=True)  # 输  入张量大小 为10,输出为5 
      torch.nn.Linear(in_features, out_features, bias=True)   #in_feature ――每个输入样本的大小 ;out_feature――每个输出样本的大小;bias――如果设置为False,则该层将不会学习加性偏差,默认值:True。 

      具体实现代码如下(包含所需的导入包):

      import torch from torch.autograd import Variable from torch.nn import Linear, ReLU inp = Variable(torch.randn(1,10))    #创建输入数据 print('inp:') print(inp) myLayer = Linear(in_features=10,out_features=5,bias=True)  # 输入张量大小为10,输出为5 myLayer(inp) print('myLayer(inp):') print(myLayer(inp)) print('myLayer.weight:') print(myLayer.weight) print('myLayer.bias:') print(myLayer.bias) 

      实现线性变换,使用Pytorch的框架Linear:

      # 使用Pytorch中的Linear实现线性变换(能实现的代码) import torch import torch.nn as nn import numpy as np import matplotlib.pyplot as plt # Hyper-parameters input_size = 1 output_size = 1 num_epochs = 60 learning_rate = 0.001 # Toy dataset x_train = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],[9.779], [6.182], [7.59], [2.167], [7.042],[10.791], [5.313], [7.997], [3.1]], dtype=np.float32) y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573],[3.366], [2.596], [2.53], [1.221], [2.827],[3.465], [1.65], [2.904], [1.3]], dtype=np.float32) # Linear regression model model = nn.Linear(input_size, output_size) # Loss and optimizer criterion = nn.MSELoss() optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) # Train the model for epoch in range(num_epochs):     # Convert numpy arrays to torch tensors     inputs = torch.from_numpy(x_train)     targets = torch.from_numpy(y_train)     # Forward pass     outputs = model(inputs)     loss = criterion(outputs, targets)     # Backward and optimize     optimizer.zero_grad()     loss.backward()     optimizer.step()     if (epoch + 1) % 5 == 0:        print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch + 1, num_epochs, loss.item())) # Plot the graph predicted = model(torch.from_numpy(x_train)).detach().numpy() plt.plot(x_train, y_train, 'ro', label='Original data') plt.plot(x_train, predicted, label='Fitted line') plt.legend() plt.show() 

      线性层由不同的名称调用,例如跨不同框架的密集层或完全连接层。用于解决真实世界用例的深层学习体系结构通常包含一个以上的层,在PyTorch中,我们可以通过多种方式来实现它,如下所示:

      • 一种简单的方法是将一个层的输出传递给另一个层
        # 将一个层的输出传递给另一个层 myLayer1 = Linear(10,5) myLayer2 = Linear(5,2) myLayer2(myLayer1(inp)) print('myLayer1(inp):') print(myLayer1(inp)) print('myLayer2(myLayer1(inp)):') print(myLayer2(myLayer1(inp))) 

      线性层的缺点:具有两个不同层的体系结构可以简单地表示为具有不同层的单层。因此,仅仅叠加多个线性层将无助于算法学习任何新的东西。

      • 示例:
        Y=2(3X1)              #  2 Linear Layers Y=6(X1)               #1 Linear Layer 
  • 非线性激活
    非线性激活是接受输入,然后应用数学变换并产生输出的函数。为了解决线性变换中的问题,引进不同的非线性函数来帮助学习不同的关系,而不是只关注线性关系。PyTorch将这些非线性功能作为层提供,能够像使用线性层一样使用它们。
    一些常用的非线性函数如下:

    • Sigmoid

      • Sigmoid函数的表达式如下:
        σ(x)=1/(1+e(x))σ(x)=1/(1+e^(-x^) )
      • Sigmoid函数直观地获取一个实数,并输出在0到1之间的一个数字;对于较大的负数,它返回接近于零的值;对于一个较大的正数,它返回接近1的值。
      • Sigmoid函数的缺点:当Sigmoid函数的输出接近于零或1时,Sigmoid函数之前各层的梯度接近于零。前一层的可学习参数使得梯度接近于零,权重不被调整,导致神经元死亡。
    • Tanh

      • tanh非线性函数将实数压缩在-1和1的范围内,但当tanh输出接近-1 和1的极值时,tanh也面临同样的饱和梯度问题;然而,它比Sigmoid更 好,因为tanh的输出是以零为中心的。
    • ReLU

      • 在最近几年里,ReLU变得越来越流行;它有一个简单的数学公式:
        f(x)=max(0,x)f(x)=max(0,x)
      • ReLU将所有输入为负值的数压缩为0,并留下正数。
      • ReLU帮助优化器更快地找到正确的权重集;更严格地说,它使随机梯度下降的收敛速度更快。
      • ReLU在计算上很便宜,因为只是在进行阈值处理,而不是像对Sigmoid和切线函数那样计算。
      • ReLU有一个缺点:当一个大的梯度在反向传播过程中通过它时。常常变得没有反应;它们被称为神经元死亡,它可以通过仔细选择学习率来控制。
    • Leaky ReLU

      • Leaky ReLU试图解决一个濒死的问题,而不是饱和到零,我们饱和到一个非常小的数字,例如0.001;对于某些使用情况,该激活功能为其他应用提供了卓越的性能,但这并不一致。
    • ReLU(线性整流函数)、Leaky ReLU(带泄露线性整流函数)、PReLU(参数化修正线性单元)和RReLU(随机纠正线性单元)的比较:

  • PyTorch 非线性激活

    代码:

    #  A quick example of how to use the ReLU function in PyTorch sample_data = Variable(torch.Tensor([[1,2,-1,-1]])) myReLU = ReLU() print('myReLU(sample_data):') print(myReLU(sample_data)) print('myReLU(sample_data).size():') print(myReLU(sample_data).size()) 

    在上面的代码中,取一个具有两个正值和两个负值的张量,并在其上应用一个ReLU函数处理,它将负数阈值化为0并保留原来的正数。
    输出结果如下:

    myReLU(sample_data): tensor([[1., 2., 0., 0.]]) myReLU(sample_data).size(): torch.Size([1, 4]) 
  1. 构建深度学习算法的PyTorch方法
  • Pytorch中的所有网络都是由类实现的,而Pytorch中的子类由nn.Module调用
  • 类中应该实现__init__方法和__forward__方法
  • 在__init__方法中初始化所有层,例如线性层
  • 在__forward__方法中,将输入数据传递到在__init__方法中初始化的层中,并返回最终输出。
  • 非线性函数通常直接应用于forward函数中,也有一些在init方法中使用。
  • 下面的代码片段展示了如何在PyTorch中实现深度学习架构:
    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 
    上面所示代码中做的是继承父类并在该类中实现两种方法,在Python中,我们将父类作为参数传递给类名,从而实现子类;init方法充当构造函数,super用于将子类的参数传递给父类,在上面的例子中是nn.Module
  1. 不同机器学习问题的模型结构
  • 我们正在解决的问题将主要决定我们将使用哪些层,从线性层到用于顺序数据的长短期存储器(LSTM),根据所要解决的问题的类型,最后一层是确定的;我们通常使用机器学习或深度学习算法来解决三个问题。
    • 1.对于回归问题,例如预测t恤的价格,我们使用最后一层作为一个线性层,具有一个输出,并且输出一个连续的值。
    • 2.要将给定的图像分类为t恤或非t恤,可以使用Sigmoid激活函数,因为它输出的值要么接近1,要么接近于0,这通常称为二进制分类问题。
    • 3.对于多类别分类,我们必须将给定图像分类为T恤、牛仔裤、衬衫或连衣裙;我们将在网络的末尾使用Softmax层,例如,它接受前一个线性层的输入,并为给定数量的示例输出概率,在我们的例子中,它将被训练来预测每种类型的图像的四个概率,并且这四个概率和为1。
  1. Loss functions
  • 一旦我们定义了我们的网络架构,我们就剩下两个重要的步骤:一个是计算我们的网络在执行特定的回归、分类;下一步就是优化权重。

  • 优化器(梯度下降)通常接受一个标量值,所以loss函数应该生成一个标量值,并且该值在训练中必须最小化。而对于某些用例,如预测道路上的障碍物并将其划分为行人或非行人,将需要两个或更多的损失函数。但即使在这种情况下,也需要将损失合并为一个标量,以便优化器最小化。

  • PyTorch中用于回归和分类的loss函数,应用示例:

    # Loss functions loss = nn.MSELoss() input = Variable(torch.randn(3,5),requires_grad=True) target = Variable(torch.randn(3,5)) output = loss(input,target) output.backward() print('input:') print(input) print('target:') print(target) print('output:') print(output) 
  • 交叉熵损失:它计算了一个预测概率的分类网络的损失,这个概率应该加起来总和为1,就像Softmax层一样;当预测概率偏离正确概率时,交叉熵损失增加;例如,如果我们的分类算法预测某图像的0.1概率是猫,但实际上是熊猫,那么交叉熵损失将更高;如果它预测与实际标签相似,那么交叉熵损失就会降低。

    • 交叉熵损失函数的原理
      在二分类问题模型:例如逻辑回归「Logistic Regression」、神经网络「Neural Network」等,真实样本的标签为 [0,1],分别表示负类和正类。交叉熵损失模型的最后通常会经过一个 Sigmoid 函数,输出一个概率值,这个概率值反映了预测为正类的可能性:概率越大,可能性越大。
      预测输出即 Sigmoid 函数的输出表征了当前样本标签为 1 的概率可表达为:
      y^=P(y=1x)\hat y=P(y=1|x)
      当前样本标签为 0 的概率就可以表达成:
      1y^=P(y=0x)1-\hat y=P(y=0|x)
    • 单个样本交叉熵损失的公式:
      L=[ylogy^+(1y)log(1y^)]L=-[ylog\ \hat y+(1-y)log\ (1-\hat y)]
    • 交叉熵损失函数定义:
    # cross_entropy loss def cross_entropy(true_label,prediction):     if true_label == 1:         return -log(prediction)    # log这块会提示出错,导入了from math import log     else:         return -log(1 - prediction) 

    在分类问题中使用交叉熵损失:

    # Use a cross-entropy loss in a classification problem 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() print('output.backward():') print(output.backward()) 

    但是在上面的代码中会报如下错误:

    解决方法是:将output.backward()改为output.backward(retain_graph=True),该问题是指在默认情况下,网络在反向传播中不允许多个backward()。需要在第一个backward设置retain_graph=True
    正确输出如下:

    • Pytorch中的一些loss函数及其应用范围:
      • L1 loss――主要用作正则化器
      • MSE loss――回归问题的损失函数
      • Cross-entropy loss――用于二进制和多级分类问题
      • NLL Loss――用于分类问题,并允许我们使用特定的权重来处理不平衡的数据集。
      • NLL Loss2d――用于逐像素分类,主要用于与图像分割有关的问题。
  1. 优化网络体系结构
    一旦我们计算出我们的网络损失,我们将优化权重以减少损失,从而提高算法的精度,为了简单起见,让我们将这些优化器看作黑匣子,它们接受丢失函数和所有可学习的参数,并稍微移动它们以提高性能。Pytorch所提供的一些优化器如下所示:
  • ADADELTA
  • Adagrad――自适应梯度算法
  • Adam
  • SparseAdam
  • Adamax
  • ASGD
  • LBFGS
  • RMSProp
  • Rprop
  • SGD――随机梯度下降
  1. 创建一个SGD优化器,它将网络的所有可学习参数作为第一个参数,并创建了一个学习速率,它决定可以对可学习的参数进行多大比例的更改:

     optimizer = optim.SGD(model.parameters(),lr = 0.01)  # 这处的调试为什么不会继续往下执行,而且model不管导入哪个包都出错,所有关于model的包都没有这个attribute 
     loss = nn.MSELoss()  for input,target in dataset:      optimizer.zero_grad()      output = model(input)      losss = loss(output,target)      loss.backward()      optimizer.step()      print('loss.backward():')      print(loss.backward()) 

    一旦创建了优化器对象,就要在循环中调用zero_grad(),因为参数将累积在上一次优化器调用期间创建的梯度;当调用了loss函数中的backward函数(计算梯度更新权值)中后,就需要调用optimizer.step(),对可学习参数进行实际更改。

  2. 基于深度学习的图像分类
    Dogs vs. Cats数据集

  • 调试出现的错误:

    import os dir = 'F:\\inner\\kaggle'             # 数据集路径 list_img = [] list_label = [] data_size = 0 dir = dir + '/train/' for file in os.listdir(dir):  # 遍历dir文件夹     list_img.append(dir + file)  # 将图片路径和文件名添加至image list     data_size += 1  # 数据集增1     name = file.split(sep='.')  # 分割文件名,"cat.0.jpg"将分割成"cat",".","jpg"3个元素     # label采用one-hot编码,"1,0"表示猫,"0,1"表示狗,任何情况只有一个位置 为"1",在采用CrossEntropyLoss()计算Loss情况下,label只需要输入"1"的索引,即猫应输入0,狗应输入1     if name[0] == 'cat':         list_label.append(0)  # 图片为猫,label为0     else:         list_label.append(1)  # 图片为狗,label为1,注意:list_img和list_label中的内容是一一配对的 print('data_size:') print(data_size) 

    同一段代码,在两个项目里运行结果不一样

    • 在本地运行时结果:
    • 服务器运行结果:
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!