揉扁搓圆Transformer架构: 激活函数说明

在充分了解Transformer架构前,我们需要知道它的一些前置知识,如果对前置知识了解不清楚,我们深入transformer架构时就会愈发糊涂最后走不下去。Transformer其实是有早期深度学习网络进化而来,他是基于早起深度学习网络架构上进行的一次进化。

Transformer最早的祖先就是最简单形式的深度学习网络,简称"向前传导网络", Feed Forward Network,它的基本结构如下:

它本质上是使用基于计算机的数据结构形式来对人脑用于思考推理的脑神经活动过程进行模拟。人脑中大量的神经元以层级结构进行组织,每一个神经元就像一个cpu,它的职能包含输入,处理,输出三部分。输入对应的是其他神经元给它传递的电信号,它把所有输入的电信号集合起来,进行相应处理后,把处理结果同样以电信号的方式传递给其他神经元。

从数据结构上为了模拟上面描述的神经元运行过程就形成了一种网状结构,网络以层为单位,每一层包含若干个节点,每个节点的输入对应上一层节点的输出。从上图可以看出,右边的网络结构中最左边一层叫做输入层,它不用接收其他神经元的输出,而是直接接收大脑传递的电信号,对应到数据结构上,最左边一层的每个节点在代码上对应一个向量,向量其实是多个浮点数构成的一维数组,上图中最左边的输入层有三个绿色节点,那意味着网络对应的输入数据就是3个向量,当然这里的三个节点只是示例,具体输入层需要多少个节点要根据具体的应用情况来决定。同时上层右边图形中,最右边的一层叫输出层,大脑做出决策时,依赖的正是它的输出。位于最左边和最右边的中间部分,我们同一称为中间层(hidden layers),他们对应的每一层中的节点分别接收上一层节点的输出作为它的输入,通过节点自身对输入的数据进行处理后,将处理结果再传递给他下一层的节点。

我们先看输入层的具体实例。如果我们创建的网络用于处理文本输入,那么输入层对应的数据通常会是一个句子,例如"How are your",在代码上会将句子里面的单词转化为token,一般情况下一个单词可以看作是一个token,但有些情况,一个单词会分解成两个token,例如he's 这种情况就会拆解成两个token,一个对应单词he,另一个对应's,每个token会通过一些特定算法转化成一个向量,于是"How are your"在传递给网络时,它会转化成一个包含3个向量的数组,至于每个向量的长度如何决定就要取决于具体的应用需求。

如果输入给网络的是图片,那么图片的每个像素点就可以先转化为向量,然后再把向量的集合作为输入。例如对于32 X 32的图形,我们需要将图形转化为一个二维数组,数组包含32 X32行,每一行对应一个向量,每一行包含的元素个数取决于具体应用。

网络的学习能力取决于中间层的数量,以及中间层中每个节点如何处理它所获得的输入。上面图形中遗漏了一个非常重要的细节,那就是节点将它的处理结果如何移交给它下一层的节点。一个节点处理完输出得到一个输出,它就需要将输出的内容提交给他下一层的节点。假设当前节点输出的电信号取值为3,同时他需要将该电信号传递给其他10个节点,那么它不是简单的把数值3依次传递给10个节点,而是将数值3切分成10份,例如第一份数值为0.2,第二份数值为0.8,以此类推,只要保证这十份数值加总起来为3即可。这就意味这上一层节点跟下一层有连接的节点之间的连线上还有一个系数,这个系数乘以节点的输出值就得到该节点传递给对应节点的数值,这个系数叫做权重。另外下一层的节点在接收上一层的输入时,它自己也会做一些调整,通常情况下会把上一层输入的结果上加上一个固定数值b,这个数值叫做接收节点的偏好,也叫bias:

一个节点在接收到上一层节点传入的数值,将数值根据自身偏好调整后,它需要把这些数值收集起来做特定处理。这里我们需要了解"收集"和"特定处理"的含义,"收集"其实很简单,通常是把上一层输入的数值加总,对于"特定处理"就需要好好说道说道。所谓"特定处理"其实是将加总的数值传给一个特定函数进行计算,对于神经网络而言,常用的特定函数如下:

sigmod = 1 / (1 + e ^-z)

Tanh = (e ^ z - e ^-z)/(e ^ z + e ^ -z)

ReLU = max(0, z)

Leaky ReLU = max(a * z, z), 0 < a <= 1

GELU = z * P(X <= z), 这里X是基于N(0, 1)分别的随机变量

ELU = max(a * ( e ^ z - 1), z), a 约等于1的常量。

我们用代码来实现一下上面的函数,并绘制函数图形,这样我们能加深理解:

py 复制代码
import numpy as np
import matplotlib.pyplot as plt
import math

# 定义激活函数
def sigmoid(x):
    """Sigmoid激活函数"""
    return 1 / (1 + np.exp(-x))

def tanh(x):
    """Tanh激活函数"""
    return np.tanh(x)

def relu(x):
    """ReLU激活函数"""
    return np.maximum(0, x)

def leaky_relu(x, alpha=0.01):
    """Leaky ReLU激活函数,默认负斜率为0.01"""
    return np.where(x > 0, x, alpha * x)

def gelu(x):
    """GELU激活函数(精确版本,使用erf)"""
    return 0.5 * x * (1 + np.vectorize(math.erf)(x / np.sqrt(2)))

def elu(x, alpha=1.0):
    """ELU激活函数,默认alpha=1.0"""
    return np.where(x > 0, x, alpha * (np.exp(x) - 1))

# 生成输入数据
x = np.linspace(-5, 5, 500)

# 计算各激活函数的输出
y_sigmoid = sigmoid(x)
y_tanh = tanh(x)
y_relu = relu(x)
y_leaky_relu = leaky_relu(x)
y_gelu = gelu(x)
y_elu = elu(x)

# 绘制图像
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
fig.suptitle('常用激活函数图像', fontsize=16)

# Sigmoid
axes[0, 0].plot(x, y_sigmoid, 'b-', linewidth=2)
axes[0, 0].set_title('Sigmoid')
axes[0, 0].grid(True)
axes[0, 0].axhline(y=0, color='k', linestyle='--', linewidth=0.5)
axes[0, 0].axvline(x=0, color='k', linestyle='--', linewidth=0.5)

# Tanh
axes[0, 1].plot(x, y_tanh, 'r-', linewidth=2)
axes[0, 1].set_title('Tanh')
axes[0, 1].grid(True)
axes[0, 1].axhline(y=0, color='k', linestyle='--', linewidth=0.5)
axes[0, 1].axvline(x=0, color='k', linestyle='--', linewidth=0.5)

# ReLU
axes[0, 2].plot(x, y_relu, 'g-', linewidth=2)
axes[0, 2].set_title('ReLU')
axes[0, 2].grid(True)
axes[0, 2].axhline(y=0, color='k', linestyle='--', linewidth=0.5)
axes[0, 2].axvline(x=0, color='k', linestyle='--', linewidth=0.5)

# Leaky ReLU
axes[1, 0].plot(x, y_leaky_relu, 'm-', linewidth=2)
axes[1, 0].set_title('Leaky ReLU (α=0.01)')
axes[1, 0].grid(True)
axes[1, 0].axhline(y=0, color='k', linestyle='--', linewidth=0.5)
axes[1, 0].axvline(x=0, color='k', linestyle='--', linewidth=0.5)

# GELU
axes[1, 1].plot(x, y_gelu, 'c-', linewidth=2)
axes[1, 1].set_title('GELU')
axes[1, 1].grid(True)
axes[1, 1].axhline(y=0, color='k', linestyle='--', linewidth=0.5)
axes[1, 1].axvline(x=0, color='k', linestyle='--', linewidth=0.5)

# ELU
axes[1, 2].plot(x, y_elu, 'y-', linewidth=2)
axes[1, 2].set_title('ELU (α=1.0)')
axes[1, 2].grid(True)
axes[1, 2].axhline(y=0, color='k', linestyle='--', linewidth=0.5)
axes[1, 2].axvline(x=0, color='k', linestyle='--', linewidth=0.5)

plt.tight_layout()
plt.show()

上面代码运行后得到结果如下:

上面代码中比较难理解的其实是GELU函数,我们对他的原理再做进一步分解。GELU对应的代码中有如下部分:

"""

np.vectorize(math.erf)(x / np.sqrt(2))

"""

它其实模拟的是下面的积分运算:

上面公式中的系数2/(sqrt(pi))可以放到积分符号的右边,那就是应用及其广泛的标准正太分布概率函数,那么这个积分实际上就是计算概率函数下方[0,z]这个区间的阴影面积,我们用一段具体的代码来加深理解:

py 复制代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
from scipy.special import erf

# 设置z值
z = 1.5

# 标准正态分布的概率密度函数
def pdf(x):
    return norm.pdf(x)  # 直接使用scipy的pdf函数

# 生成数据
x = np.linspace(-4, 4, 1000)
y = pdf(x)

# 计算从0到z的面积
area = norm.cdf(z) - norm.cdf(0)  # norm.cdf(0) = 0.5

# 绘制曲线
plt.figure(figsize=(8, 5))
plt.plot(x, y, 'b-', linewidth=2, label=r'$\phi(x)$')

# 填充从0到z的区域
x_fill = np.linspace(0, z, 500)
y_fill = pdf(x_fill)
plt.fill_between(x_fill, y_fill, alpha=0.4, color='red', label=f'Area from 0 to {z}')

# 绘制垂直线和水平线
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='gray', linestyle='--', linewidth=1, label='x = 0')
plt.axvline(z, color='red', linestyle='--', linewidth=1, label=f'x = {z}')

# 在图上标注面积
plt.text(z + 0.1, 0.08, f'Area = {area:.4f}', fontsize=10, color='red')
plt.text(0.1, 0.35, f'z = {z}', fontsize=10, color='red')

# 设置坐标轴刻度,明确显示0和z的位置
plt.xticks([-4, -2, 0, z, 2, 4])
plt.yticks(np.arange(0, 0.5, 0.1))

plt.title('Standard Normal PDF: Shaded Area from 0 to z')
plt.xlabel('x')
plt.ylabel('Density')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()

# 打印面积验证
print(f"Area from 0 to {z} = {area:.6f}")
print(f"0.5 * erf({z}/√2) = {0.5 * erf(z / np.sqrt(2)):.6f}")

上面代码运行后所得结果如下:

上图中蓝色曲线就是标准正态分布密度函数对应的曲线,当给定z=1.5时,红色部分的面积就是前面代码:

py 复制代码
np.vectorize(math.erf)(x / np.sqrt(2))

x取值1.5时对应的数值。它整条曲线下方对应的面积大小为1,那么红色区域面积表示对于随机变量X,它的取值落入区间[0,1.5]的概率就是红色区域面积对应的大小。

相关推荐
HarryPoint2 小时前
Claude Code Auto Mode:不用点"同意",也能保证安全
人工智能
孤烟2 小时前
伯克利研究:AI 未减负反加压,77% 职场人工作量飙升
人工智能·ai编程
jinanwuhuaguo2 小时前
OpenClaw全网使用人群全景深度分析报告
网络·人工智能·网络协议·rpc·openclaw
踩着两条虫2 小时前
AI驱动的Vue3应用开发平台 深入探究(十四):扩展与定制之插件系统开发指南
vue.js·人工智能·低代码·重构·架构
spider'3 小时前
ROS2开发环境搭建
人工智能
Yiyaoshujuku3 小时前
医院API接口,从医院真实世界数据HIS、LJS、EMR、PACS系统到医院药品流向数据....
大数据·前端·人工智能
STLearner3 小时前
AI论文速读 | 元认知监控赋能深度搜索:认知神经科学启发的分层优化框架
大数据·论文阅读·人工智能·python·深度学习·学习·机器学习
AI浩3 小时前
第 2 章:Claude Code 环境搭建与API配置。
人工智能·目标检测
不一样的故事1263 小时前
抓重点、留弹性、重节奏
大数据·网络·人工智能·安全