前言
思索了很久到底要不要出深度学习内容,毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新,很多坑都没有填满,而且现在深度学习的文章和学习课程都十分的多,我考虑了很久决定还是得出神经网络系列文章,不然如果以后数学建模竞赛或者是其他更优化模型如果用上了神经网络(比如利用LSTM进行时间序列模型预测),那么就更好向大家解释并且阐述原理了。但是深度学习的内容不是那么好掌握的,包含大量的数学理论知识以及大量的计算公式原理需要推理。且如果不进行实际操作很难够理解我们写的代码究极在神经网络计算框架中代表什么作用。不过我会尽可能将知识简化,转换为我们比较熟悉的内容,我将尽力让大家了解并熟悉神经网络框架,保证能够理解通畅以及推演顺利的条件之下,尽量不使用过多的数学公式和专业理论知识。以一篇文章快速了解并实现该算法,以效率最高的方式熟练这些知识。
现在很多竞赛虽然没有限定使用算法框架,但是更多获奖的队伍都使用到了深度学习算法,传统机器学习算法日渐式微。比如2022美国大学生数学建模C题,参数队伍使用到了深度学习网络的队伍,获奖比例都非常高,现在人工智能比赛和数据挖掘比赛都相继增多,对神经网络知识需求也日渐增多,因此十分有必要掌握各类神经网络算法。
博主专注建模四年,参与过大大小小数十来次数学建模,理解各类模型原理以及每种模型的建模流程和各类题目分析方法。此专栏的目的就是为了让零基础快速使用各类数学模型、机器学习和深度学习以及代码,每一篇文章都包含实战项目以及可运行代码。博主紧跟各类数模比赛,每场数模竞赛博主都会将最新的思路和代码写进此专栏以及详细思路和完全代码。希望有需求的小伙伴不要错过笔者精心打造的专栏。
前一篇文章我们具体讲述了神经网络神经元的基本构造,以及引入了神经网络一些概念性质,有了这些基础我们就能更好的理解每一层神经网络究竟要做什么,如何工作的。
激活函数定义与功能
举个例子,我们可以将激活函数比作人类的情绪,比如愉快、中立和生气。如果一个神经元的情绪是愉快的,那么它在收到信息后可能更容易激活,即传递信息。相反,如果它的情绪是生气的,那么即使收到的信息很强烈,它也可能不会激活,即不传递信息。
想象一下神经元是大脑中的小工人,他们负责接受信息并决定是否传递这些信息给下一个神经元。但是这些神经元并不是简单地将信息原封不动地传递,它们还会加上一些个人的“情绪”来影响信息的传递。
这种“情绪”就好比神经网络中的激活函数。激活函数就像神经元的情绪开关,当神经元收到信息时,它会根据这些信息的强弱和自己的“情绪”来决定是否激活(传递信息)。
总的来说,激活函数的作用就是在神经网络中引入非线性性质,使得神经网络可以学习更加复杂和抽象的模式。它们帮助神经元决定是否传递信息,从而让整个神经网络能够更好地适应和学习输入数据的特征。就像人类情绪决定了我们如何对待收到的信息一样,激活函数也决定了神经元如何处理输入信息。
我们来更直观的感受一下激活函数的作用:在现实中,存在非常复杂的线性不可分的情况。
激活函数的设计灵感来自于生物神经元。生物神经元不仅仅是线性的输入输出关系,它们在达到一定电位阈值后会激活,产生脉冲。激活函数模拟了这种生物神经元的激活过程,使得神经网络更贴近生物神经系统的工作方式。
激活函数的作用是为神经网络引入非线性性,提高网络的表达能力,模拟生物神经元的工作方式,解决优化问题,以便网络可以更好地学习和表示各种数据的复杂模式和特征。
激活函数分类
每个激活函数都有自己的优点和适用场景。在选择激活函数时,需要根据具体任务、网络结构和训练效果进行选择。激活函数可以分为以下几类:
-
线性激活函数: 线性激活函数是最简单的激活函数,其输出与输入成正比。然而,由于线性函数的叠加仍然是线性的,所以线性激活函数的叠加多层无法表达复杂的非线性模式,因此在深度神经网络中很少使用。
-
非线性激活函数: 非线性激活函数是神经网络中常用的激活函数,它们引入了非线性变换,使得网络能够学习复杂的数据模式和特征。常见的非线性激活函数包括 ReLU、Leaky ReLU、PReLU、Tanh 和 Sigmoid 等。
-
输出激活函数: 输出激活函数位于神经网络的输出层,通常用于处理不同类型的任务,如分类、回归、生成等。常见的输出激活函数有 Sigmoid(二元分类问题)、Softmax(多类别分类问题)、线性激活函数(回归问题)等。
-
归一化激活函数: 归一化激活函数在一定范围内对输入进行缩放和平移,以便使输出位于特定的范围内。例如,Sigmoid 函数和 Tanh 函数就是归一化激活函数。
-
带参数的激活函数: 有些激活函数具有可调节的参数,允许网络自适应地学习激活函数的形状。例如,Leaky ReLU 和 PReLU 就是带参数的激活函数,其中参数可以在训练中进行调整。
-
自适应激活函数: 这些激活函数根据输入数据的性质自动调整激活函数的形状。例如,自适应的激活函数可以根据数据分布动态地选择合适的非线性变换。
常用的激活函数
1.Sigmoid函数
关于Sigmoid函数,与人工智能算法有过接触的小伙伴估计都有使用到过这个函数,无论是作为启发函数还是作为数据处理的归一化函数,该函数的功能都十分好用。Sigmoid非线性激活函数的数学表达式为 :
其中e是纳皮尔常数,其值为2.7182...
优点
-
输出范围有界: Sigmoid 函数的输出范围在 0 到 1 之间,这使得它在表示概率或概率分布时很有用。在二元分类问题中,Sigmoid 函数的输出可以被解释为样本属于某一类别的概率。
-
可导性: Sigmoid 函数在其定义范围内是连续可导的,这对于使用梯度下降等优化算法进行模型训练是有帮助的。导数的计算也相对简单。
-
阈值控制: Sigmoid 函数在接近原点时,输出变化比较敏感。这意味着它可以帮助网络学习对输入的微小变化做出较大的响应,有助于模型进行精细的调整。
缺点
-
梯度消失问题: 当输入值很大或很小时,Sigmoid 函数的导数接近于零,这会导致梯度消失的问题。在深度神经网络中,这可能导致训练过程变得困难,甚至无法进行有效的训练。
-
不以零为中心: Sigmoid 函数的输出不以零为中心,这可能导致神经网络在训练过程中的权重更新存在偏移,影响学习的效果。
-
计算成本较高: 在计算 Sigmoid 函数时,需要进行指数运算,这在某些情况下会增加计算成本,尤其是当神经元数量很多时。
Python代码
import numpy as np
import matplotlib.pyplot as plt
# 定义 Sigmoid 激活函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 生成一组输入值
x = np.linspace(-10, 10, 100)
# 计算对应的 Sigmoid 输出
y = sigmoid(x)
# 绘制输入和输出曲线
plt.figure(figsize=(8, 6))
plt.plot(x, y, label='Sigmoid Function')
plt.title('Sigmoid Activation Function')
plt.xlabel('Input')
plt.ylabel('Output')
plt.legend()
plt.grid(True)
plt.show()
2. ReLU 函数
ReLU(Rectified Linear Activation)函数是深度学习中常用的非线性激活函数之一。它在神经网络中广泛应用,因为它简单有效,能够解决梯度消失问题,并且在实际应用中取得了良好的结果。
ReLU 函数的定义很简单:对于任何输入值 x
,输出等于输入 x
(如果 x
大于等于零),或者输出为零(如果 x
小于零)。数学表达式如下:
优点
-
非线性性质: ReLU 函数引入了非线性变换,使得神经网络可以学习更加复杂的数据模式和特征,从而提升网络的表达能力。
-
避免梯度消失问题: 在反向传播过程中,ReLU 的导数在正区间是常数 1,这意味着梯度不会随着层数增加而消失。相比于一些 Sigmoid 和 Tanh 等函数,在深度网络中使用 ReLU 函数更不容易出现梯度消失的情况。
-
计算高效: ReLU 函数的计算非常简单,只需要判断输入是否大于零,不涉及复杂的数学运算。
缺点
-
神经元死亡问题: 当输入为负值时,ReLU 函数的导数为零,这可能导致一些神经元在训练过程中“死亡”,即永远不会被激活。这通常发生在学习率过大时。
-
不以零为中心: ReLU 函数在输入为负时输出为零,这使得神经网络中的某些权重在训练过程中可能出现偏移,影响网络的学习。
为了克服 ReLU 函数的缺点,也有一些改进版本,如 Leaky ReLU、Parametric ReLU(PReLU)等,它们在一定程度上解决了神经元死亡问题并且可以学习不同的斜率。
Python代码
import numpy as np
import matplotlib.pyplot as plt
# 定义 ReLU 激活函数
def relu(x):
return np.maximum(0, x)
# 生成一组输入值
x = np.linspace(-10, 10, 100)
# 计算对应的 ReLU 输出
y = relu(x)
# 绘制输入和输出曲线
plt.figure(figsize=(8, 6))
plt.plot(x, y, label='ReLU Function')
plt.title('ReLU Activation Function')
plt.xlabel('Input')
plt.ylabel('Output')
plt.legend()
plt.grid(True)
plt.show()
3.Tanh 函数
Tanh 函数(双曲正切函数)是一种常用的激活函数,它在神经网络中用于引入非线性性,并可以将输入映射到一个在 -1 到 1 之间的输出范围。Tanh 函数类似于 Sigmoid 函数,但在输出值的范围上更广泛,因此在某些情况下更有用。
Tanh 函数的数学表达式如下:
Tanh 函数的图像呈现出 S 形曲线,类似于 Sigmoid 函数,但 Tanh 函数的输出范围在 -1 到 1 之间。当输入接近正无穷时,Tanh 函数的输出趋近于 1,而当输入接近负无穷时,输出趋近于 -1。当输入接近零时,输出在 0 附近。
优点
-
非线性性质: Tanh 函数引入了非线性变换,使得神经网络可以学习更加复杂的数据模式和特征,提高网络的表达能力。
-
输出范围有界: Tanh 函数的输出范围在 -1 到 1 之间,这使得它在一些情况下适用于数据标准化和缩放。
-
以零为中心: Tanh 函数的输出在零点附近有对称性,这使得神经网络中的权重更新相对均衡,避免偏移。
缺点
-
梯度消失问题: 当输入接近正无穷或负无穷时,Tanh 函数的导数接近于零,这可能导致梯度消失的问题,特别是在深度神经网络中。
-
计算成本较高: 计算 Tanh 函数需要进行指数运算,这在某些情况下可能会增加计算成本。
Python代码
import numpy as np
import matplotlib.pyplot as plt
# 定义 Tanh 激活函数
def tanh(x):
return np.tanh(x)
# 生成一组输入值
x = np.linspace(-10, 10, 100)
# 计算对应的 Tanh 输出
y = tanh(x)
# 绘制输入和输出曲线
plt.figure(figsize=(8, 6))
plt.plot(x, y, label='Tanh Function')
plt.title('Tanh Activation Function')
plt.xlabel('Input')
plt.ylabel('Output')
plt.legend()
plt.grid(True)
plt.show()
4.Softmax函数
Softmax特别适用于多类别分类问题。它将一组实数作为输入,将其转换为表示概率分布的输出,使得所有输出的和为 1。Softmax 函数广泛应用于神经网络中的分类问题,例如图像分类、自然语言处理中的文本分类等。
Softmax 函数的数学表达式如下,假设有一个包含 n
个元素的输入向量 x
:
其中,是输入向量中的第 i
个元素,而分母部分是所有输入元素的指数和。这样,Softmax 函数将每个输入值映射到一个 [0, 1] 范围内的概率值,并保证所有概率值的和为 1。
在多类别分类问题中,假设有 k
个类别,Softmax 函数的输出可以解释为输入属于每个类别的概率。输出向量的第 i
个元素就是输入属于第 i
个类别的概率。
优点
-
概率分布: Softmax 函数生成一个概率分布,使得网络的输出可以解释为属于每个类别的概率,有助于对分类问题进行解释和分析。
-
多类别分类: Softmax 函数适用于多类别分类问题,能够同时处理多个类别的输出。
-
可导性: Softmax 函数是可导的,这对于使用梯度下降等优化算法进行模型训练是有帮助的。
缺点
-
数值稳定性: 当输入值较大或较小时,Softmax 函数中的指数运算可能导致数值溢出或下溢。为了解决这个问题,通常会在计算 Softmax 时进行数值稳定性处理,例如通过减去输入向量中的最大值。
-
标签不平衡问题: 如果某个类别的训练样本数量远大于其他类别,Softmax 函数可能会倾向于预测数量更多的类别。
Python代码
import numpy as np
# 定义 Softmax 函数
def softmax(x):
e_x = np.exp(x - np.max(x)) # 为了数值稳定性,减去最大值
return e_x / e_x.sum()
# 生成一组输入向量
x = np.array([2.0, 1.0, 0.1])
# 计算 Softmax 输出
softmax_output = softmax(x)
print("Input vector:", x)
print("Softmax output:", softmax_output)
print("Sum of probabilities:", np.sum(softmax_output))
生成一个输入向量 x
,并使用 Softmax 函数计算对应的输出向量 y
。为了保证数值稳定性,我们在计算时减去了输入向量中的最大值。运行代码后,你将看到输入向量经过 Softmax 函数转换后得到的概率分布,所有输出值都在 [0, 1] 范围内,且和为 1。
总结
大家使用激活函数的时候如果有条件的话,尽量都选择用一遍,选择效果最好的函数再进行微调。如果拿不定主意的话就先选ReLU 或其变种。避免使用 Sigmoid 和 Tanh,尽管 Sigmoid 和 Tanh 激活函数在一些情况下仍然有用,但它们容易引发梯度消失问题,并且在深度网络中不如 ReLU 及其变种效果好。
对于二元分类问题,常用 Sigmoid 激活函数;对于多类别分类问题,常用 Softmax 激活函数。对于回归问题,通常不需要使用激活函数。某些情况下,激活函数可能导致梯度爆炸。如果你遇到梯度爆炸问题,可以考虑使用梯度裁剪等方法来控制梯度大小。所以说每个问题和数据集都可能有不同的特点,因此最好通过实验验证不同激活函数的效果,找到最适合的激活函数。