本章通过介绍构建神经网络所涉及的基本思想,如激活函数,损失函数,优化器和监督训练设置,为后面的章节打基础。我们从一个简单的神经网络Perceptron开始,将各种概念联系在一起。Perceptron本身是更复杂的神经网络中的基本模块。这是一个常见的模式,将在整本书中重复出现 - 我们讨论的每个架构或网络可以单独使用,也可以在其他复杂网络中使用。当我们讨论计算图和本书的其余部分时,这种组合性将变得清晰。
1 感知器:最简单的神经网络
最简单的神经网络单元是Perceptron。在生物神经元之后,感知器在历史上很有名,与生物神经元一样,有输入和输出,“信号”从输入流向输出,如图3-1所示。
Perceptron
每个感知器单元具有输入(x),输出(y)和三个“旋钮”:一组权重(w),偏置(b)和激活函数(f)。从数据中学习权重和偏差,并根据网络设计者对网络及其目标输出的直觉来手工挑选激活功能。在数学上,我们可以表达如下:
y = f(wx + b)
通常情况下,Perceptron有多个输入。我们可以使用向量来表示这种一般情况; 也就是说,x和w是向量,w和x的乘积用点积替换:
y = f(wx + b)
这里用f表示的激活函数通常是非线性函数。给出了PyTorch中的Perceptron实现,它采用任意数量的输入,仿射变换,应用激活函数,并产生单个输出。
Example 3-1. Perceptron的pytorch实现
# Example 3-1. Implementing a Perceptron using PyTorchimport torchimport torch.nn as nnclass Perceptron(nn.Module): """ A Perceptron is one Linear layer """ def __init__(self, input_dim): """ Args: input_dim (int): size of the input features """ super(Perceptron, self).__init__() self.fc1 = nn.Linear(input_dim, 1) def forward(self, x_in): """The forward pass of the Perceptron Args: x_in (torch.Tensor): an input data tensor. x_in.shape should be (batch, num_features) Returns: the resulting tensor. tensor.shape should be (batch,) """ return torch.sigmoid(self.fc1(x_in)).squeeze()
线性运算wx + b称为仿射变换。PyTorch方便地使用在torch.nn
模块中提供了一个类Linear()
,它可以进行权重和偏差计算,并进行所需的仿射变换。在“深入监督训练”中,您将了解如何从数据中“学习”权重w和b的值。前面例子中使用的激活函数是sigmoid函数。在下一节中,我们将回顾一些常见的激活函数,包括sigmoid函数。
2 激活函数
激活函数是在神经网络中重要的非线性部件,用于捕获数据中的复杂关系。在“深入监督训练”和“多层感知器”中,我们深入探讨了学习中需要非线性的原因,但首先,让我们看一些常用的激活函数。
Sigmoid
神经网络历史中最早使用的激活函数之一。它输入可以是任意实数,把它压缩在0和1之间。在数学上,sigmoid表示如下:
σ(x) = 1 / (1 + exp(−x))
Sigmoid激活
import torchimport matplotlib.pyplot as plt x = torch.range(-5。,5.,0.1) y = torch.sigmoid(x) plt.plot(x.numpy(),y.numpy()) plt.show ()
Sigmoid
从图中可以看出,Sigmoid函数对于大多数输入而言,非常快速地饱和。这可能会成为一个问题,因为它可能导致梯度变为零或发散到溢出的浮点值。这些现象也分别称为消失梯度问题和爆炸梯度问题。因此,很少看到除了输出之外的神经网络中使用的sigmoid单位,其中压缩属性允许将输出解释为概率。
Tanh
tanh函数是Sigmoid不同的变体。当你写下表达式时,这一点就变得清晰
tanh(x) = 2σ(2x) − 1
就像sigmoid一样,也是一个“压缩”函数,除了它将实际值集合从(-∞,+∞)映射到范围[-1, +1]。
import torchimport matplotlib.pyplot as plt x = torch.range(-5., 5., 0.1) y = torch.tanh(x) plt.grid() plt.plot(x.numpy(), y.numpy()) plt.show()
Tanh
RELU
ReLU(发音为ray-luh)代表整流线性单元。这可以说是最重要的激活功能。事实上,如果没有使用ReLU,很可能会说很多最近的深度学习创新是不可能的。对于一些如此基础的东西,就神经网络激活功能而言,它也是一个令人惊讶的新东西。它的形式也非常简单:
f(x)=max(0,x)
因此,所有ReLU单元正在做的是将负值剪辑为零,如例3-4所示。
import torchimport matplotlib.pyplot as plt relu = torch.nn.ReLU() x = torch.range(-5., 5., 0.1) y = relu(x) plt.plot(x.numpy(), y.numpy()) plt.show()
ReLu
ReLU的削波效应有助于消除梯度问题,随着时间的推移,网络中的某些输出可能会简单地变为零并且永远不会再次恢复。这被称为“垂死的ReLU”问题。为了改变这种影响,提出了诸如Leaky ReLU或Parametric ReLU(PReLU)之类的变体,其中泄漏系数a是学习参数:
f(x)=max(x,ax)
import torchimport matplotlib.pyplot as plt prelu = torch.nn.PReLU(num_parameters=1) x = torch.range(-5., 5., 0.1) y = prelu(x) plt.plot(x.numpy(), y.numpy()) plt.show()
PRelu
SOFTMAX
激活功能的另一个选择是softmax。像Sigmoid形函数,该函数SOFTMAX各单元的输出是间0
和1
。然而,softmax操作还将每个输出除以所有输出的总和,这给出了k个可能类的离散概率分布。结果分布中的概率总和为1。这对于解释分类任务的输出非常有用,因此这种转换通常与概率训练目标配对,例如分类交叉熵,这在“深入深度监督训练”中有所涉及。
softmax(xi)=exi/(Σexj)
import torch.nn as nnimport torch softmax = nn.Softmax(dim=1) x_input = torch.randn(1, 3) y_output = softmax(x_input) print(x_input) print(y_output) print(torch.sum(y_output, dim=1))
tensor([[-1.2468, -0.3236, 1.8509]])tensor([[0.0390, 0.0981, 0.8629]])tensor([1.])
在本节中,我们研究了四个重要的激活函数:Sigmoid,Tanh,ReLU和softmax。这些只是您可以用于构建神经网络的许多可能激活中的四个。随着我们逐步完成本书,将会清楚应该使用哪些激活函数以及在哪里,但一般指南只是简单地遵循过去的工作。
损失函数
在第1章中,我们看到了一般监督机器学习架构以及损失函数或目标函数如何帮助指导训练算法通过查看数据来选择正确的参数。回想一下,损失函数将真值(y)和预测(ŷ)作为输入并产生实值得分。该分数越高,模型预测越差。PyTorch在其nn
包中实现了许多损失函数,这些函数太全面,无法在此查看,但我们将回顾一些常用的损失函数。
均方误差损失
对于网络输出(ŷ)和目标(y)是连续值的回归问题,一个常见的损失函数是均方误差(MSE)。
MSE
MSE只是预测值和目标值之差的平方平均值。还有一些其他损失函数可用于回归问题,例如平均绝对误差(MAE)和均方根误差(RMSE),但它们都涉及计算输出和目标之间的实值距离。例3-6显示了如何使用PyTorch实现MSE损失。
import torchimport torch.nn as nn mse_loss = nn.MSELoss() outputs = torch.randn(3, 5, requires_grad=True) targets = torch.randn(3, 5) loss = mse_loss(outputs, targets) print(loss)
分类交叉熵损失
分类交叉熵损失通常用于多类分类设置,其中输出被解释为类成员概率的预测。目标(y)是n个元素的向量,表示所有类的真正多项式分布。如果只有一个类是正确的,那么这个向量就是一个热点。网络的输出(ŷ)也是n个元素的向量,但表示网络对多项分布的预测。分类交叉熵将比较这两个向量(y,ŷ)来衡量损失:
CrossEntropy
交叉熵及其表达式起源于信息论,但出于本节的目的,将其视为计算不同两种分布的方法是有帮助的。我们希望正确类的概率接近1
,而其他类的概率接近于零。
要正确使用PyTorch CrossEntropyLoss
,重要的是要有点理解网络输出之间的关系,如何计算损失函数,以及源于真正表示浮点数的计算约束的种类。具体而言,有四条信息确定网络输出和损失函数之间的细微差别关系。首先,数量有多小或多大是有限的。其次,如果对softmax公式中使用的指数函数的输入是负数,则结果是指数小的数,如果是正数,则结果是指数大数。接下来,假设网络的输出是应用softmax函数之前的向量。最后,日志function是指数函数的倒数,和log(exp(x))恰好等于x。根据这四条信息,假设作为softmax函数核心的指数函数和交叉熵计算中使用的对数函数进行数学简化,以便在数值上更稳定并避免真正小或真的大数。这些简化的结果是不使用softmax函数的网络输出可以与PyTorch一起使用CrossEntropyLoss
以优化概率分布。然后,当网络训练完成后,softmax函数可用于创建概率分布,如例3-7所示。
import torchimport torch.nn as nn ce_loss = nn.CrossEntropyLoss() outputs = torch.randn(3, 5, requires_grad=True) targets = torch.tensor([1, 0, 3], dtype=torch.int64) loss = ce_loss(outputs, targets) print(loss)
在此代码示例中,首先使用随机值向量来模拟网络输出。然后,称为目标的地面实况向量被创建为整数向量,因为PyTorch的实现CrossEntropyLoss假定每个输入都有一个特定的类,并且每个类都有一个唯一的索引。这就是为什么targets有三个元素:表示每个输入的正确类的索引。根据这个假设,它执行计算更高效的索引到模型输出的操作
二进制交叉熵
我们在上一节中看到的分类交叉熵损失函数在我们有多个类时在分类问题中非常有用。有时,我们的任务涉及区分两个类 - 也称为二进制分类。对于这种情况,使用二进制交叉熵(BCE)损失是有效的。我们在“示例:餐厅评论的情感分类”中查看此损失功能,以用于我们的示例任务。
在例3-8中,我们使用sigmoid激活函数在表示网络输出的随机向量上创建二进制概率输出向量概率。接下来,将基础事实实例化为0和1的向量。最后,我们计算使用二进制概率向量和实况矢量二元交叉熵损失。
bce_loss = nn.BCELoss() sigmoid = nn.Sigmoid() probabilities = sigmoid(torch.randn(4, 1, requires_grad=True)) targets = torch.tensor([1, 0, 1, 0], dtype=torch.float32).view(4, 1) loss = bce_loss(probabilities, targets)print(probabilities)print(loss) tensor([[0.4689], [0.2005], [0.2437], [0.5810]], grad_fn=<SigmoidBackward>) tensor(0.8157, grad_fn=<BinaryCrossEntropyBackward>)
3 深入监督学习
监督学习是学习如何在给定标记示例的情况下将观察结果映射到指定目标的问题。在本节中,我们将详细介绍。具体来说,我们明确地描述了如何使用模型预测和损失函数来对模型参数进行基于梯度的优化。这是一个重要的部分,因为本书的其余部分依赖于它,所以即使您对监督学习有点熟悉,也值得详细阅读。
回忆第1章,监督学习需要以下内容:模型,损失函数,训练数据和优化算法。用于监督学习的训练数据是成对的观察和目标,该模型根据观察结果计算预测,并且损失测量预测的误差与目标相比。培训的目标是使用基于梯度的优化算法来调整模型的参数,以使损失尽可能低。
在本节的其余部分,我们将讨论一个经典的玩具问题:将二维点分类为两个类中的一个。直观地说,这意味着学习一条线,称为决策边界或超平面,以区分一个类与另一个类的点。我们逐步介绍并描述数据结构,选择模型,选择损失,设置优化算法,最后将它们一起运行。
构建数据
在机器学习中,通常的做法是在尝试理解算法时创建具有良好理解属性的合成数据。对于本节,我们将合成数据用于“玩具”任务 - 将二维点分类为两个类中的一个。为了构建数据,我们从xy平面的两个不同部分采样个点,为模型创建一个易于学习的情况。样品显示在图3-2中所示的图中。该模型的目标是将恒星(⋆)分类为一类,)作为另一类。这在图的右侧可视化,其中线上方的所有内容都不同于线下方的所有内容。生成数据的代码位于名为的函数中get_toy_data()
在本章附带的Python笔记本中。
图3-2。创建可线性分离的数据集。数据集是来自两个正态分布的采样,每个类一个。分类任务成为区分数据点是属于一个分布还是属于另一个分布的任务
作者:readilen
链接:https://www.jianshu.com/p/e7431bcff3a6
x