深度学习---从入门到放弃简单线性神经网络

Posted 佩瑞

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度学习---从入门到放弃简单线性神经网络相关的知识,希望对你有一定的参考价值。

深度学习—从入门到放弃(二)简单线性神经网络

1.基本结构

就像昨天说的,我们构建深度学习网络一般适用于数据大,处理难度也大的任务,因此对于网络的结构需要有一个非常深入的了解。这里以一个分类猫狗的线性神经网络分类器作为例子:
1.目标函数
想象一下,如果是想要一个能够分类出猫和狗的网络,我们的最终目的是什么?应该是使用最短的时间,最好的方法来完成任务。具象的来说就是在崎岖的山上找一条最优的下山路径。在神经网络中就是指最大限度降低损失函数的路径。


2.学习规则
可以继续联想刚刚的下山路径,我们的目标函数是如何最优下山,而学习规则则是我们该如何下山,换言之也就是我们如何通过不断的学习和迭代获得最优路径,最优模型参数。
3.网络架构
什么是神经网络?就如同它的字面意思,是由一层层神经元构成的网络结构,因此我们如何设计神经元间的连接以及网络层数都会对结果产生影响。我们今天的大标题叫简单线性神经网络,也就是说层和层之间是以线性关系连接。
4.初始化
又回到我们的下山问题,下山的目标(目标函数)和如何下山(学习规则)都找到了,那么还有一个问题,我们该从哪里出发,而这就是初始化。起点决定成败,一个好的起点会让我们节省很多力气。
5.环境
既然是分类问题,那么肯定要给我们的网络一些实例去学习,但对于深度学习网络庞大数据量的需求,我们又该怎么办呢?如果我说有这么一个开放数据集,里面有成千上万的图片数据,且都是预处理好了的,是不是解决了一大难题!在猫狗分类的问题里我们选用了IMAGENET里的图片数据。
有了一个基本的认知之后我们再来引入一个简单线性神经网络,可以看到这个神经网络是多层结构的,每一层都有自己的权重w,而我们之前所说的“下山”便是找到损失函数最小的最优参数w,

2.梯度下降

由于大多数学习算法的目标是最小化风险(也称为成本或损失)函数,因此优化通常是大多数机器学习算法的核心!梯度下降算法及其变体(例如随机梯度下降)是用于深度学习的最强大和最流行的优化方法之一。

2.1梯度向量


这里有关梯度向量大家只需要有这么一个了解就够了:梯度向量总是指向局部损失函数增大的方向,可以这么理解:下山过程中如果跟着梯度向量的方向走就可能会变成如何更好攀登顶峰而不是下山。为了解决这个问题,我们在之后引入负的梯度向量的概念。

2.2梯度下降算法


这里就用到了上一节提到的负梯度向量的概念,而这里的学习率可以理解为我们下山过程中每迈一步的步长,通过不断的迭代更新(计算损失函数相对于可学习参数(即权重)的梯度),神经网络中的权重最终返回我们想要的“下山最优解”

2.3计算图和反向传播

对于梯度下降算法的结构分析后我们不难发现:随着变量和嵌套函数数量的增加,梯度的推导将变得十分艰难(梯度向量的计算需要对函数微分),这违背了我们设置梯度下降的初衷—不仅没有找到最优参数还会加大计算负担。
这里我们以一个嵌套函数为例:

我们可以构建一个所谓的计算图(如下所示),将原始函数分解为更小、更易于理解的表达式。

我们将上图称为前向传播,经过前向传播后的函数变得十分简洁。随后我们从前向传播末端出发逐步向前微分,这个过程为反向传播。

通过将计算分解为对中间变量的简单操作,我们可以使用链式法则来计算任何梯度。

3.PyTorch 梯度下降

AutoGrad 是 PyTorch 的自动微分引擎。在Pytorch里梯度下降也是从前向传播开始的,当我们声明变量和操作时,PyTorch 会跟踪所有指令,并在我们调用.backward(),即反向传播时构建计算图。PyTorch 每次迭代或更改图时都会重建计算图。
总结来说Pytorch梯度下降分为以下几个步骤:

1.重置梯度
2.计算图前向传播
3.计算损失函数
4.计算图反向传播,计算损失函数相对于可学习参数(即权重)的梯度
5.梯度下降

# Reset all gradients to zero
sgd_optimizer.zero_grad()

# Forward pass (Compute the output of the model on the features (inputs))
prediction = wide_net(inputs)

# Compute the loss
loss = loss_function(prediction, targets)
print(f'Loss: {loss.item()}')

# Perform backpropagation to build the graph and compute the gradients
loss.backward()

# Optimizer takes a tiny step in the steepest direction (negative of gradient)
# and "updates" the weights and biases of the network
sgd_optimizer.step()

4.nn.Moudle

PyTorch 为我们提供了现成的神经网络构建块,例如层(例如线性、循环等)、不同的激活和损失函数等等,都打包在torch.nn模块中.
对于训练,我们需要知道三点:
1.模型参数
模型参数是指模型的所有可学习参数,可通过调用.parameters()模型访问。
2.损失函数
梯度下降的优化对象
3.优化器
PyTorch 为我们提供了许多优化方法(不同版本的梯度下降)。优化器保存模型的当前状态,并通过调用该step()方法,将根据计算出的梯度更新参数

4.1 定义一个简单的神经网络

## A Wide neural network with a single hidden layer
class WideNet(nn.Module):

  def __init__(self):
    """Initializing the WideNet"""
   
    n_cells = 512
    super().__init__()
    self.layers = nn.Sequential(
        nn.Linear(1, n_cells),
        nn.Tanh(),
        nn.Linear(n_cells, 1),
    )

  def forward(self, x):
    """Forward pass

    Args:
      x (torch.Tensor): 2D tensor of features

    Returns:
      torch.Tensor: model predictions
    """
    return self.layers(x)
    
# Create a mse loss function
loss_function = nn.MSELoss()

# Stochstic Gradient Descent optimizer (you will learn about momentum soon)
lr = 0.003  # learning rate
#这里使用了随机梯度下降中的momentum方法
sgd_optimizer = torch.optim.SGD(wide_net.parameters(), lr=lr, momentum=0.9)

网络结构为:

WideNet(
(layers): Sequential(
(0): Linear(in_features=1, out_features=512, bias=True)
(1): Tanh()
(2): Linear(in_features=512, out_features=1, bias=True)
)
)

4.2 训练网络

def train(features, labels, model, loss_fun, optimizer, n_epochs):

  """Training function
  Args:
    features (torch.Tensor): features (input) with shape torch.Size([n_samples, 1])
    labels (torch.Tensor): labels (targets) with shape torch.Size([n_samples, 1])
    model (torch nn.Module): the neural network
    loss_fun (function): loss function
    optimizer(function): optimizer
    n_epochs (int): number of training iterations
  Returns:
    list: record (evolution) of training losses
  """
  loss_record = []  # keeping recods of loss
### 梯度下降
  for i in range(n_epochs):
    optimizer.zero_grad()  # set gradients to 0
    predictions = model(features)  # Compute model prediction (output)
    loss = loss_fun(predictions, labels)  # Compute the loss
    loss.backward()  # Compute gradients (backward pass)
    optimizer.step()  # update parameters (optimizer takes a step)

    loss_record.append(loss.item())
  return loss_record


set_seed(seed=2021)
epochs = 1847 # Cauchy, Exercices d'analyse et de physique mathematique (1847)
losses = train(inputs, targets, wide_net, loss_function, sgd_optimizer, epochs)
with plt.xkcd():
  ex3_plot(wide_net, inputs, targets, epochs, losses)

对应一开始深度学习网络的五大成分,我们可以这么理解:

1.目标函数–MSE损失
loss_function = nn.MSELoss()
2.学习规则–随机梯度下降和momentum方法
sgd_optimizer = torch.optim.SGD(wide_net.parameters(), lr=lr, momentum=0.9)
3.网络结构–512个神经元,1个隐藏层,1个Tanh激活层
def init(self):
n_cells = 512
super().init()
self.layers = nn.Sequential(
nn.Linear(1, n_cells),
nn.Tanh(),
nn.Linear(n_cells, 1),
)
4.初始化–随机
5.环境

5.超参数

5.1 网络深度


在了解”深度“这个概念前,我们先来看看神经网络的结构:输入层,隐藏层,输出层。因此深度在这里指的就是隐藏层的数量,隐藏层数量越多,网络深度越大。

那么让我们来看看深度在训练神经网络时带来的挑战。想象一个具有 50 个隐藏层且每层只有一个神经元的单输入单输出线性网络。网络的输出很容易计算:

如果所有权重的初始值为 w i = 2 wi=2 wi=2, y ( p ) = 2 50 ≈ 1.1256 × 1 0 15 y(p)=2^{50}≈1.1256×10^{15} y(p)=2501.1256×1015,这时候的预测值趋近于无限大,此时这种情况称为梯度爆炸
如果所有权重的初始值为 w i = 0.5 wi=0.5 wi=0.5, y ( p ) = 0. 5 50 ≈ 8.88 × 1 0 − 16 y(p)=0.5^{50}≈8.88×10^{-16} y(p)=0.5508.88×1016,这时候的预测值趋近于无限小,此时这种情况称为梯度消失
因此为了有效的避免这两种情况发生,我们需要了解深度对于网络训练带来的影响:

不难看出,浅层网络的学习是一个循序渐进的过程,而对于深层网络而言它的学习过程更像sigmoid函数,即通过一定时间的学习后突然掌握数据集的某些特征,这两种学习方式是截然不同的。

5.2 学习率

学习率是大多数优化算法的常见超参数。我们可以把学习率想成梯度下降过程中每一步的步长。

1.学习率过小
学习率过小将会导致损失函数收敛过慢,即梯度下降迈步太小,花费很长时间都不能走到损失函数最小的最优参数的位置

2.学习率过大
学习率过大会导致梯度下降时跳过损失函数最小值,造成损失函数的波动

上图为适宜学习率下的神经网络损失随训练时间的变化

5.3 深度,学习率的相互影响

一般来说,深度越深的网络需要的是更小的学习率,可以想象成它的处理过程更为复杂,因而也就更需要小心翼翼的走好每一步。而浅层神经网络则正好相反,它可以承受较大学习率带来的影响。

5.4 初始化


详见之前说到的梯度爆炸和梯度消失。

欢迎大家关注公众号奇趣多多一起交流!

深度学习—从入门到放弃(一)pytorch基础

以上是关于深度学习---从入门到放弃简单线性神经网络的主要内容,如果未能解决你的问题,请参考以下文章

深度学习---从入门到放弃pytorch基础

深度学习:从入门到放弃

从入门到精通:机器学习算法与应用

[资源]深度学习从入门到放弃

深度学习入门:线性回归模型

入门实战《深度学习技术图像处理入门》+《视觉SLAM十四讲从理论到实践》