第一章:数学基础——理解神经网络的数学工具箱
当我们打开大语言模型的源代码,首先映入眼帘的并非优雅的英语句子或流畅的对话,而是一行行精密的数学运算。从输入文本的向量化表示,到注意力机制的矩阵乘法计算,再到最终概率分布的输出,每一个环节都离不开数学的支撑。这并非故作深奥的炫技,而是由大语言模型的本质所决定的——它们本质上是一系列从数据中自动学习而来的数学函数,能够将人类的语言映射为计算机可以理解和运算的数值表示。理解这些数学工具,不仅能帮助我们理解大语言模型的工作原理,更能让我们具备分析问题、诊断模型、乃至改进创新的能力。
让我们从一个简单的例子开始。假设我们希望训练一个模型来判断一段评论是正面还是负面的,这个看似简单的二分类任务,背后却涉及了多项数学技术的综合运用:我们需要将文字转换为模型能够处理的向量(这涉及线性代数的知识);我们需要计算模型预测与真实标签之间的差距(这涉及概率论中损失函数的概念);我们还需要根据这个差距调整模型参数,使下一次预测更加准确(这涉及微积分中梯度的概念)。接下来的几节,我们将逐一深入探讨这些数学工具,为后续理解Transformer架构和GPT模型奠定坚实的基础。
标量、向量与矩阵:数据的不同形态
数学中,我们用标量(Scalar)来表示单独一个数值,比如温度37.5、股票价格150.32,或者神经网络中某个神经元的激活值。标量是最基本的数据单位,它只有大小,没有方向。在程序中,我们通常用普通变量来存储标量值。在NumPy库中,标量就是一个零维数组。
```python import numpy as np
标量示例
temperature = 37.5 price = 150.32
NumPy标量
np_scalar = np.array(42) print(f"标量维度: {np_scalar.ndim}") # 输出: 0 print(f"标量形状: {np_scalar.shape}") # 输出: () ```
然而,现实世界中的数据往往更加复杂。我们需要同时处理多个相关的数值,这时候就需要用到向量(Vector)。向量本质上是按顺序排列的一组标量,可以看作是一维数组。从几何角度来看,向量既有大小又有方向,这使得它非常适合表示具有多个特征的数据点。例如,在自然语言处理中,一个单词可以被表示为一个向量,这个向量的每个维度可能代表不同的语义特征。
```python
向量示例:单词"king"的分布式表示
假设我们使用一个简化的词向量空间(实际中维度会高得多)
word_embedding = np.array([0.2, -0.5, 0.8, 0.1, -0.3]) print(f"向量维度: {word_embedding.ndim}") # 输出: 1 print(f"向量形状: {word_embedding.shape}") # 输出: (5,) ```
在深度学习框架中,向量无处不在。当我们输入一句话“人工智能正在改变世界”时,每个单词都会被转换为一个向量,所有单词的向量组合在一起,就形成了一个矩阵(Matrix)。矩阵是二维数组,具有行和列两个维度,非常适合表示批量数据或者数据之间的对应关系。在神经网络中,矩阵被广泛用于表示权重、偏置以及层与层之间的连接关系。
```python
矩阵示例:一批句子的词向量表示
假设我们有3个句子,每个句子包含4个单词,每个单词是5维向量
sentence_embeddings = np.array([ [0.2, -0.5, 0.8, 0.1, -0.3], # 句子1的4个词向量 [0.1, 0.3, -0.2, 0.7, 0.4], # 句子2的4个词向量 [-0.4, 0.2, 0.5, -0.1, 0.6], # 句子3的4个词向量 ]) print(f"矩阵形状: {sentence_embeddings.shape}") # 输出: (3, 4, 5) ```
理解这些数据结构之间的转换关系至关重要。在神经网络的前向传播过程中,数据会经历一系列的变换:从标量到向量,从向量到矩阵,从低维到高维。GPT等大语言模型更是将这些概念发挥到了极致,其中最常见的是张量(Tensor)的概念——张量是向量和矩阵向更高维度的推广,一维张量是向量,二维张量是矩阵,三维及以上的张量则可以表示更复杂的数据结构。例如,在处理一批文本时,我们通常使用三维张量:第一个维度是句子数量,第二个维度是每个句子的词数,第三个维度是词向量的维度。
矩阵运算:神经网络的信息传递
如果说向量和矩阵是数据的容器,那么矩阵运算就是在这个容器中进行信息处理和变换的核心操作。神经网络之所以强大,很大程度上是因为它能够通过精心设计的矩阵运算,将输入数据转化为有意义的输出表示。
矩阵加法是最基础的运算,两个形状相同的矩阵对应位置的元素相加即可。在神经网络中,矩阵加法通常用于添加偏置项。假设我们有一个隐藏层的输出矩阵$\mathbf{H}$和一个偏置向量$\mathbf{b}$,那么加上偏置后的结果就是$\mathbf{H} + \mathbf{b}$。这里需要注意的是,偏置向量通常会被广播(Broadcasting)到与矩阵相同的形状后再进行加法运算。
```python
矩阵加法示例
H = np.array([[1, 2], [3, 4], [5, 6]]) b = np.array([1, 2])
广播机制:b被扩展为 [[1,2], [1,2], [1,2]] 后再相加
H_with_bias = H + b print(f"原始矩阵 H:\n{H}") print(f"偏置 b: {b}") print(f"加上偏置后:\n{H_with_bias}") ```
矩阵乘法是神经网络中最核心的运算。与逐元素相乘不同,矩阵乘法遵循特定的规则:设矩阵$\mathbf{A}$的形状为$(m \times n)$,矩阵$\mathbf{B}$的形状为$(n \times k)$,则它们的乘积$\mathbf{C} = \mathbf{A} \times \mathbf{B}$的形状为$(m \times k)$,其中$\mathbf{C}$的第$i$行第$j$列元素等于$\mathbf{A}$的第$i$行与$\mathbf{B}$的第$j$列的点积。用数学语言可以表示为:
$$C_{ij} = \sum_{k=1}^{n} A_{ik} \times B_{kj}$$
这个运算规则看似简单,却蕴含着巨大的表达能力。当我们用一个大矩阵乘以一个向量时,本质上是在对向量进行线性变换;当用两个大矩阵相乘时,则是在进行更复杂的空间变换。在神经网络中,每一层的计算本质上都是一次矩阵乘法:$\mathbf{Y} = \mathbf{X} \times \mathbf{W} + \mathbf{b}$,其中$\mathbf{X}$是输入,$\mathbf{W}$是权重矩阵,$\mathbf{b}$是偏置,$\mathbf{Y}$是输出。
```python
矩阵乘法示例
X = np.array([[1, 2, 3], # 输入:2个样本,每个样本3个特征 [4, 5, 6]]) W = np.array([[0.1, 0.2], # 权重:3个输入特征映射到2个输出 [0.3, 0.4], [0.5, 0.6]]) b = np.array([0.1, 0.2])
Y = np.dot(X, W) + b # 等价于 X @ W + b print(f"输入矩阵形状: {X.shape}") print(f"权重矩阵形状: {W.shape}") print(f"输出矩阵形状: {Y.shape}") print(f"输出结果:\n{Y}") ```
哈达玛积(Hadamard Product),也称为逐元素乘法,是另一种重要的矩阵运算。与矩阵乘法不同,哈达玛积要求两个矩阵形状完全相同,对应位置的元素直接相乘即可。在神经网络的某些层中,如残差连接或门控机制,哈达玛积被广泛使用。
```python
哈达玛积示例
A = np.array([[1, 2], [3, 4]]) B = np.array([[0.5, 1], [1.5, 2]])
C = A * B # 或 np.multiply(A, B) print(f"哈达玛积结果:\n{C}") ```
转置(Transpose)操作会将矩阵的行和列互换。如果原矩阵$\mathbf{A}$的形状是$(m \times n)$,那么$\mathbf{A}^\top$的形状就是$(n \times m)$。在神经网络的计算中,转置操作常用于调整数据的维度以满足矩阵乘法的形状要求。例如,在计算损失函数对权重的梯度时,就会用到转置操作。
```python
转置示例
A = np.array([[1, 2, 3], [4, 5, 6]]) print(f"原矩阵:\n{A}") print(f"转置后:\n{A.T}") ```
理解这些矩阵运算的形状变化规律对于debug神经网络代码至关重要。一个常见的错误是矩阵形状不匹配导致的运算失败。记住一个简单的原则:在神经网络中,数据从输入到输出的过程中,维度的变化通常遵循某种规则——要么保持,要么扩展,要么压缩,而这些变化都是通过精心设计的矩阵乘法实现的。
向量空间与嵌入表示:从文字到数字
在继续深入之前,我们需要理解一个对于大语言模型至关重要的概念:向量空间(Vector Space)与嵌入(Embedding)。当我们将文本数据输入神经网络时,首先需要将离散的符号(文字)转换为连续的数值表示,这就是嵌入技术的核心思想。
传统的文本表示方法如词袋模型(Bag of Words)将文本表示为稀疏的高维向量,每个维度对应一个词汇表中的单词,出现则为1,不出现则为0。这种表示方法的问题显而易见:它丢失了词语之间的语义关系,“猫”和“狗”在这种表示下可能毫无区别。而分布式假设(Distributional Hypothesis)提出了一个革命性的观点:一个词的含义由它所处的上下文决定。因此,语义相似的词应该具有相似的向量表示。
词嵌入就是基于这一思想的技术。以Word2Vec为例,训练完成后的词向量具有一个神奇的特性:语义关系可以通过向量运算来表示。最著名的例子是:$\text{king} - \text{man} + \text{woman} \approx \text{queen}$。这个等式从数学上精确地捕捉了性别关系的语义。又如:$\text{Paris} - \text{France} + \text{Italy} \approx \text{Rome}$,展示了首都与国家的关系。这种被称为词向量算术的特性,是分布式表示之美的集中体现。
```python
使用预训练词向量演示词向量算术(概念示例)
实际使用时需要加载真实的词向量模型
import numpy as np
def cosine_similarity(v1, v2): """计算两个向量的余弦相似度""" dot_product = np.dot(v1, v2) norm_product = np.linalg.norm(v1) * np.linalg.norm(v2) return dot_product / norm_product
假设我们有以下词向量(简化示例)
king = np.array([0.8, 0.2, 0.1]) man = np.array([0.7, 0.3, 0.2]) woman = np.array([0.3, 0.8, 0.4]) queen = np.array([0.4, 0.9, 0.3])
演示 king - man + woman ≈ queen
result = king - man + woman similarity = cosine_similarity(result, queen) print(f"运算结果与queen的余弦相似度: {similarity:.4f}") ```
在大语言模型中,嵌入技术得到了进一步的扩展和深化。GPT系列模型使用的嵌入不仅包括词嵌入(Token Embedding),还包括了位置嵌入(Positional Embedding),用于表示词语在序列中的位置信息。这两种嵌入通常会被相加,形成最终的输入表示。这种设计使得模型既能理解“说了什么”(语义信息),也能理解“在哪说的”(位置信息)。
更深层次地看,词向量实际上定义了一个高维空间中的语义地图。在这个空间中,语义相似的概念聚集在一起,形成“语义簇”;而不同维度则编码了不同的语义特征,如性别、时态、情感极性等。理解这一概念对于后续学习注意力机制至关重要——因为注意力机制本质上是在计算 Query、Key、Value 向量之间的相似度,而这些向量都源自于嵌入空间。
概率分布:衡量不确定性的数学语言
如果说线性代数让我们能够表示和处理数据,那么概率论则让我们能够处理不确定性,并对不确定性进行建模和推理。在大语言模型中,概率论无处不在:从模型的输出分布到文本生成的采样策略,从困惑度评估到贝叶斯优化,概率论是理解这些技术的数学基础。
让我们从概率分布(Probability Distribution)开始。在自然界和人类社会中,很多现象都遵循一定的随机规律。例如,抛掷一枚均匀硬币,正面朝上的概率是0.5;投掷一个均匀骰子,每个面朝上的概率都是1/6。这种将随机变量的每个可能取值与其概率相关联的函数,就是概率分布。对于离散型随机变量,我们用概率质量函数(Probability Mass Function, PMF)来描述;对于连续型随机变量,则用概率密度函数(Probability Density Function, PDF)来描述。
在机器学习中,最常用的概率分布包括均匀分布(Uniform Distribution)、正态分布(Normal Distribution)和伯努利分布(Bernoulli Distribution)。正态分布(也称为高斯分布)是其中最为重要的一个,其概率密度函数为:
$$f(x) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)$$
其中$\mu$是均值,$\sigma^2$是方差。正态分布之所以重要,是因为中心极限定理(Central Limit Theorem)告诉我们:大量独立随机变量的和趋向于正态分布,无论这些变量本身服从什么分布。在神经网络的初始化、梯度分布分析等领域,正态分布都扮演着核心角色。
```python import numpy as np import matplotlib.pyplot as plt
绘制不同参数的正态分布
x = np.linspace(-5, 5, 1000) variances = [0.5, 1.0, 2.0] means = [-1, 0, 1]
plt.figure(figsize=(10, 6)) for var in variances: pdf = (1 / np.sqrt(2 * np.pi * var)) * np.exp(-x**2 / (2 * var)) plt.plot(x, pdf, label=f'$\\sigma^2$ = {var}')
plt.xlabel('x') plt.ylabel('概率密度') plt.title('不同方差参数的正态分布') plt.legend() plt.grid(True, alpha=0.3) plt.savefig('normal_distribution.png', dpi=150, bbox_inches='tight') plt.close() ```
期望(Expectation)和方差(Variance)是描述概率分布的两个关键统计量。期望可以理解为随机变量的平均值,是对随机变量长期表现的一种预测。对于离散型随机变量$X$,其期望定义为:
$$E[X] = \sum_{i} x_i \cdot P(X = x_i)$$
方差则衡量随机变量与其期望之间的偏离程度,方差越小,说明随机变量的取值越集中在期望附近。方差定义为:
$$\text{Var}(X) = E[(X - E[X])^2] = E[X^2] - (E[X])^2$$
```python
计算期望和方差的示例
假设我们有一批考试成绩
scores = np.array([85, 90, 78, 92, 88, 76, 95, 83, 89, 91])
mean_score = np.mean(scores) variance_score = np.var(scores) std_score = np.std(scores) # 标准差
print(f"平均分: {mean_score:.2f}") print(f"方差: {variance_score:.2f}") print(f"标准差: {std_score:.2f}") ```
在大语言模型的语境下,概率分布的应用极为广泛。模型的输出本质上是一个概率分布:给定前面的文本,下一个词可能是词汇表中任何一个词,而每个词都有一个对应的出现概率。GPT模型正是通过学习训练数据中词语的分布规律,才能够生成看起来“自然”的文本。从数学上看,语言模型的目标是学习一个条件概率分布$P(\text{下一个词} | \text{上下文})$,使得生成的文本具有尽可能高的概率。
条件概率(Conditional Probability)和贝叶斯定理(Bayes' Theorem)是概率论中另一个重要工具。条件概率$P(A|B)$表示在事件$B$发生的条件下,事件$A$发生的概率。贝叶斯定理建立了先验概率和后验概率之间的联系:
$$P(A|B) = \frac{P(B|A) \cdot P(A)}{P(B)}$$
这个看似简单的公式在机器学习中有着深刻的应用。在大语言模型中,我们可以将$A$理解为某个假设(如“这句话是正面评价”),将$B$理解为观测到的数据(如文本内容)。贝叶斯定理告诉我们如何根据观测数据来更新对假设的信念。虽然现代神经网络并不直接使用贝叶斯推断,但贝叶斯思维贯穿于许多算法的设计理念中。