admin

  • This content is password-protected. To view it, please enter the password below.

  • 在最初尝试了解 diffusion 模型的时候,原本是打算跳过 VAE (Variational Autoencoder 变分自编码 )模型的,后面发现有点跳不过去。再花了一些时间去了解 VAE,才意识到其实不应该跳过去,相反的,了解 VAE 实现的一些架构、原理、直觉、初衷、数学原理则可以大大帮助理解后续的生成模型。

    1. 为什么现在你依旧需要了解 VAE

    “潜空间”(latent space)的处理依旧是现代(SOTA)生成模型最为核心的组件。而从autoencoder模型,扩展到 VAE 模型,则是生成模型走出的关键步骤之一。将潜空间限制在一个正态分布的空间内,然后,在这个空间进行采样后进行 decoder 的生成思想,则是现代生成模型很多思想来时的路。

    如果跳过这一段,很多的概念则会显得非常突然。

    2. VAE 关键直觉与主要思想

    2.1 关键直觉

    在计算机与数学科学中有几个概念是反复出现的,其中之一就是“高斯分布”(或者叫“正态分布”)。在图像生成模型中非常关键的,则是将潜空间限制在了一个正态分布之中,为什么会这样?似乎并没有人去说明这一点,这里做一个简单的阐述和理解。高斯分布可以理解为大量伯努利分布的极限形式,现实世界的分布我们通常会假设其为大量微小因子的共同作用下的宏观表现,故通常假设其满足高斯分布。

    那这与 VAE 有什么关系呢?在经典的 Guassian VAE 中,一个关键假设是:“潜空间” \(z \) 符合高斯分布的(通过训练来将其拉向高斯分布)。然后再对潜空间采样后,就可以生成较为“逼真”的图像了。那么假设“潜空间” \(z \) 符合高斯分布有什么深意吗?还是只是为了方便采样?

    从直觉构建的角度,我们可以这样认为:潜空间的每个“维度”都有某这非常强的意义,例如色彩、风格、类型等,那么则有理由相信,一个对象在每一个“维度”上的分布也是符合正态分布的。即“潜空间”是一个符合正态分布的空间,那么从这个样本空间进行采样时,也就更容易得到一个有代表意义的点。

    2.2 VAE 的主要思想

    如何使用“神经网络”生成“逼真”的数据(例如图片、视频等)呢?在机器学习领域,一个比较自然的探索是从“autoencoder”架构去做一些尝试:“autoencoder” 由一个确定的encoder 将输入压缩到一个低维的“潜编码”(latent code)空间中,然后再由decoder根据“latent code”重建数据。

    如右图所示,对于这样的神经网络设计其优势是非常明显的,这可以是非常好的“无监督学习”的神经网络,对比最终输出和输入的数据即可以作为损失函数。

    但,在数据生成(例如新的图像生成)上,这样的设计在实践中也有着非常明显的限制:即对于如果随机选取一个“latent code”,通常只能生成一些无意义的数据。

    2013年的 “Variational Autoencoder (VAE)” (Kingma and Welling, 2013) 架构则尝试解决这个问题:将“潜空间”(latent space)设计为某个符合某个概率分布结构。终于,可以生成出色的、逼真的数据(例如“图像”)。

    上述的描述可能还是比较抽象的,下面将实现一个基础的“VAE”模型,从而观测该模型的各个模块。

    3. 一个简单的 VAE 模型的观测

    3.1 模型架构概述

    这里实现了(主要靠Gemini)一个由多个全连接层构成 VAE 模型。输入是 MNIST 数据集,输入层维度是 \(28 \times 28 = 784 \),中间的潜空间(Latent Space)维度为 20 ,包括 20 个均值和20个方差,然后是一个基本对称的decoder,包括一个400个神经元的全连接层和一个784个神经元的输出层,手绘描述如右图。

    相关的代码实现参考:VAE-my-practice.ipynb

    这个神经网络结构之简单、生成效果之惊人,我自己是有点被震惊了的,相比于Autoencoder ,这里将“潜空间”限制在了某个概率分布之中,更为具体的是将每一个样本都“尝试”映射到正态分布中(注:\(p(z|x) \)是正态分布)。这里的“尝试”的做法是,先让神经网络输出“均值”和“方差”,然后再做一次重参数化的“采样”(Sampling)得到 \(z \)。

    说明:\(p(z|x) \)是正态分布;从模型架构上看,\(p(z) \) 似乎并不是正态分布,但最终由训练的目标函数将\(p(z) \) “拉向”一个正态分布。

    3.2 模型效果观测:某个点附近的数据

    这里使用上述模型,将 MNIST 数据集中的一张原始图片计算出对应的正态分布,即均值和方差,然后使用该均值和方差进行了 11 一次采样,再生成 11 张图片,可以看到这些生成的图片和原始图片有非常高的相似度,同时又与原始图片不同。

    即达到了生成“逼真”图片的效果。这种实现范式,展现出了非常强的图片生成潜力。也是后续 diffusion 模型的重要基础之一。

    再从 \(z \sim \mathcal{N}(0,I) \) 随机采样一些值,再看看这些随机生成的效果,如右图。

    可以看到,基本上能够生成一些有意义的图。这里只是使用了一些全连接层,即可以产生非常好的效果,可以预期,如果使用卷积神经网络,是可以有更好的效果的。

    3.3 模型效果观测:空间中交接处的情况

    从构建直觉的角度我们可以这样理解,encoder 层将原始的“向量空间”映射到了一个“潜空间”(latent space),该潜空间中的数据符合标准正态分布。所以,这就使得我们在“潜空间”变量 \(z \) 中做一个标准正态分布的采样,就可以使用 decoder 生成一个有意义的图片。并且,从构建直觉的角度,我们可以认为decoder 将正态分布中的点映射到“10个”数字的“空间”(784维)中,甚至可以认为(直觉角度)目标空间中有一片“连续”的空间即为“像空间”,这些空间某种程度可以“聚类”为“10类”,这些聚类的边缘则是一些介于不同数字之间的难以辨认的数字。

    例如,我们考虑这样观测这些“聚类”中数字“1”和数字“0”之间的图像。我们先使用encoder计算(找一张图片作为输入)出数字“1”、“0”对应的均值,并以此作为对应数字在“潜空间”中的中心(更为严格的可以多计算一些相同的数字,再计算均值),然后在“1”、“1”对应的质心之间进行插值,然后观测这些“插值”经过encoder后的图片。

    在放大33% ~ 66% 这个关键阶段:

    3.4 模型小结

    如上所述,encoder 最终把“原始的数据”映射到一个“潜空间”(latent space),“潜空间”中的数据(在线性代数中类似的概念称为“像空间”)符合正态分布。从从构建直觉角度我们可以有如下的一些理解和疑问。

    VAE 模型尝试把“潜空间”限制在一个非常小区域,即以“原点”为中心的一个小“球”中(想想 3 \(\sigma \) 内就可能覆盖了 99.73% 的点),更为准确的是一片标准正态分布的概率云当中。问题:这种把“像空间”限制在小区域内,是否是实现“逼真生成”的关键因素?尝试回答:像空间的大小也许并不是关键的,毕竟,从数学角度来看,单位“球”可能与整个空间是一样大的,只是看起来小了而已;那么,关键可能在于把“像空间”现在在了某种结构上,这里的结构是标准正态分布空间。问题:为什么把“像空间”限制在某种结构上,就能够达到这样的效果呢?

    在上述示例的“概率云”中,所有的点并不是“对等的”,概率密度相等的点,其意义也有着非常大的区别。从直觉的角度,例如最某个数字1的点 \(z_1 \in z \),在点 \(z_1 \) 附近的点则映射到的“像”也是更为接近的与数字“1”的点(这里的 \(z_1 = q_{\theta}(x) \quad \text{where} \, x \in \text{Samples} \))。

    4. VAE 实现的一些细节

    4.1 VAE的训练目标

    VAE 模型将潜空间限制在了一个“正态分布”之中,通过在正态分布中采样的方式用来生成新的图片。这个过程并不是很好理解,这里介绍一下 VAE 的数学建模思路。

    模型的训练目标(ELBO):

    $$
    \min_{\theta,\phi} \mathbb{E}_{q_{\theta}(z|x)}[\frac{1}{2\sigma^2}|| x-\mu_{\phi}(z) ||^2] + \mathcal{D}_{KL}(q_{\theta}(z|x)||p(z) ) \tag{1}
    $$

    关于这个式子的完整的数学推导,在网上比较好找到,有一些复杂、也不是很好理解。但是,这个式子的意义却比较明确:

    • 前一部分,对于某个输入 \(x \),需要神经网络的参数 \(\theta , \phi \) 生成的图像\(x’ \)能够与原始图像接近
    • 后半部分,则让神经网络参数\(\theta \) 是的分布 \(q_{\theta}(z|x) \) 靠近 \(p(z) \)即标准正态分布

    4.2 VAE的数学模型

    理解 VAE 比较困难的大概是这背后的数学模型,而理解了这个“数学模型”才可能进一步理解上述的公式,以及更多的VAE以及后续生成模型的思想。所以这里总结一些我对这个“数学模型”的理解:

    Encoder 部分 \(q_{\theta} \) 是将一个图片 \(x \) 映射到一个概率分布(而不是某个具体的值),通常是 \(\mathcal{N}(\mu,\sigma^2) \)。那么,在神经网络中,如何将一个具体的样本/数据 \(x \) 映射到一个概率分布呢?是这样的:神经网络则需要输出/拟合/回归出该分布的关键参数即可,例如对于正态分布只需要给均值和方差即可。这也是为什么在上述的“示例VAE”中 Encoder 部分输出即为分布 \(z \sim p(z) \) 的均值与方差。那么,要从潜空间 \(z \) 中取一个值(即采样一个值)的时候,则需要“随机”的做一次生成,这里的“随机”则使用了“重参数化”技巧进行处理从而解决训练时向后传播的随机性问题。更为一般的即:$$ q_{\theta}(z|x) := \mathcal{N}(z;\mu_{\theta}(x),\text{diag}(\sigma_{\theta}^2(x))) \tag{2} $$

    Decoder 部分 \(p_{\phi} \) 则是将潜空间的采样 \(z \) 映射到一个概率分布,而不是一个具体的值,但这里可能感觉是一个具体指的原因是,通常输出就是这个分布的均值(或期望),而不再另外进行采样操作。在常见的 Gaussian VAE 中,更为一般的有:

    $$ p_{\phi}(x|z) := \mathcal{N}(x;\mu_{\phi}(z),\sigma^2 I) \tag{3} $$

    最后,有了这两个定义公式(2)、(3),就可以计算上述的损失函数公式(1)了。

    5. VAE 中的数学推导

    这大概是最难理解的部分,甚至注意到很多领域内的专家(更加注重实践能力),对于这部分也很头疼,这里做一些尝试。

    5.1 VAE 基本数学概率模型与训练目标

    我们有一个上述的 VAE 模型,其中:

    • \(\theta \) 为encoder部分参数,\(\phi \) 为decoder 的参数
    • \(p_{\theta}(z|x) \) 表示对于给定的输入 \(x \),encoder 将输入映射为分布\(p_{\theta}(z|x) \) ,通常按照上述的公式(2)定义
    • \(p_{\phi}(x|z) \) 表示对于给定的潜空间采样值 \(z \),decoder 将其映射为分布\(p_{\phi}(x|z) \) ,通常按照上述的公式(3)定义

    在数学形式上,就可以有目标端(神经网络的输出)\(x \) 的分布:

    $$
    p_{\phi}(x) = \int p_{\phi}(x|z)p(z)dz
    $$

    这是一个形式上的表达,并不能直接计算,这是因为 \(p_{\phi}(x|z) \) 是一个由神经网络定义的映射或分布,对于每一个 \(z \),并没有一个简单的表达式给出\(p_{\phi}(x|z) \)的形式,或者更为直接的,在公式(3)中的 \(\mu_{\phi}(z) \) 由神经网络定义,并没有数学形式的表达。

    这个式子确实不可计算,在实际训练中,则做了一些数学“推导”,训练另一个与此相关的式子(ELBO),这个我们后续再看。

    所以,对于 VAE 模型的训练,即需要最大化:

    $$
    \max_{\phi}\sum_\limits{i} \log p_{\phi}(x_i) \quad \forall x_i \in \text{Sample Data} \tag{4}
    $$

    即对于最终的概率分布(PDF)\(p_{\phi}(x) \),任何的真实的图像(对应的 \(\forall x \in {\text{data}} \))所对应的密度值取值都需要很大,才能最终使得上述式子取值最大。

    这似乎还比较抽象,这里我们进一步解释一下上述的式子(4)。考虑真实分布为 \(p_{\text{data}}(x) \),通常我们可以使用 KL 散度衡量两个分布的距离,那么,就需要最小化这个散度值:

    $$
    \begin{aligned}
    \mathcal{D}_{\text{KL}} &= \int p_{\text{data}}(x) \log \frac{p_{\text{data}}(x)}{ p_{\phi}(x)}dx \\[0.8em]
    &= \int p_{\text{data}}(x) \log p_{\text{data}}(x)dx – \int p_{\text{data}}(x) \log p_{\phi}(x)dx
    \end{aligned}
    $$

    上述式子中的前项是一个常数(认为真实世界的分布是确定的,是拟合的目标,其实就是这个分布的“熵”),那么要这个散度值最小,就是让上述式子中后一项最大,即 \(\int p_{\text{data}}(x)p_{\phi}(x)dx \) 最大;在离散场景,积分则变成求和,并且 \(p_{\text{data}}(x) \)通常不可知,则通常使用\(\frac{1}{N} \)替代或者直接忽略,即有上面的式子(4)。

    5.2 使用 ELBO 对上述目标进行近似

    关于“Evidence Lower Bound” (简称 ELBO )虽然网上有很多的说明,但总是不够细致(或者要求对概率论有非常深的理解),这里做一个最为详细的推导和说明如下(应该是互联网上能够找到的最为详细的说明了)。

    ELBO的定义为:

    $$
    \mathcal{L}_{\text{ELBO}}
    =
    \underbrace{
    \mathbb{E}_{z \sim q_\theta(z|x)}
    \left[ \log p_\phi(x|z) \right]
    }_{\text{Reconstruction Term}}

    \underbrace{
    D_{\mathrm{KL}}\!\left( q_\theta(z|x) \,\|\, p(z) \right)
    }_{\text{Latent Regularization}}
    $$

    可以通过严格的证明,如下不等式成立:

    $$
    \begin{equation}
    \log p_\phi(x) \ge \mathcal{L}_{\text{ELBO}}(\theta, \phi; x)
    \end{equation}
    $$

    证明如下:

    $$
    \begin{aligned}
    \log p_\phi(x) &\stackrel{(1)}{=} \log \int p_\phi(x, z) \, dz \\[0.8em]
    &\stackrel{(2)}{=} \log \int q_\theta(z|x) \frac{p_\phi(x, z)}{q_\theta(z|x)} \, dz \\[0.8em]
    &\stackrel{(3)}{=} \log \mathbb{E}_{z \sim q_\theta(z|x)} \left[ \frac{p_\phi(x, z)}{q_\theta(z|x)} \right] \\[0.8em]
    &\stackrel{(4)}{\geq} \mathbb{E}_{z \sim q_\theta(z|x)} \left[ \log \frac{p_\phi(x, z)}{q_\theta(z|x)} \right] \\[0.8em]
    &\stackrel{(5)}{=} \mathbb{E}_{z \sim q_\theta(z|x)} \left[ \log \frac{p_\phi(x, z) p(z)}{q_\theta(z|x)p(z)} \right] \\[0.8em]
    &\stackrel{(6)}{=} \mathbb{E}_{z \sim q_\theta(z|x) p(z)} \left[ \log \frac{p_{\phi}(x|z)p(z)}{q_\theta(z|x)} \right] \\[0.8em]
    &\stackrel{(7)}{=} \mathbb{E}_{z \sim q_\theta(z|x)} \left[ \log {p_{\phi}(x|z)} – \log { \frac{q_\theta(z|x)}{p(z)} } \right] \\[0.8em]
    &\stackrel{(8)}{=} \mathbb{E}_{z \sim q_\theta(z|x)} \left[ \log {p_{\phi}(x|z)} \right] – \mathbb{E}_{z \sim q_\theta(z|x)} \left[ \log { \frac{q_\theta(z|x)}{p(z)} } \right] \\[0.8em]
    &\stackrel{(9)}{=} \mathbb{E}_{z \sim q_\theta(z|x)} \left[ \log {p_{\phi}(x|z)}] – \mathcal{D}_{\mathrm{KL}}(q_\theta(z|x)||p(z)) \right] \\[0.8em]
    \end{aligned}
    $$

    各步骤的说明:

    • 步骤 (1) 为边缘概率公式
    • 步骤(2) 分子、分母都乘以了 \(q_\theta(z|x) \)
    • 步骤(3) 使用期望的形式改写上述公式(可以想一想连续形式的概率期望计算),这一步是为了转化为下一步可以使用 “Jensen” 不等式
    • 步骤(4) 这里使用 “Jensen” 不等式,对于凹函数 \(\varphi \)有 \(\varphi(\mathbb{E}[X]) \geq \mathbb{E}[\varphi(X)] \) ,函数 \(f(x) = \log (x) \) 即为凹函数
    • 步骤(5) 分子、分母都乘以 \(p(z) \)
    • 步骤(6) 使用基础的条件概率公式 \(p_{\phi}(x|z)p(z) = p_{\phi}(x,z) \)
    • 步骤(7) 使用对数函数基本性质 \(\log \frac{ab}{c} = \log {a} – \log \frac{c}{b} \)
    • 步骤(8) 使用期望公式的线性特性 \(\mathbb{E}[X+Y] = \mathbb{E}[X] + \mathbb{E}[Y] \)
    • 步骤(9) 使用了KL散度的定义,简单的说,即概率密度对数的差的期望即为散度

    步骤(9) 也最终与前述的 \(\mathcal{L}_{\mathrm{ELBO}} \) 定义完全相同,故得证。

  • 最近 midjourney 用的比较多,很好奇,这背后的技术大概是怎样的。好了,那就去做一些了解吧(补充注:后来我才发现这个坑有多大…)。

    除了大语言模型之外,另一个非常活跃的领域就是图片处理技术,例如文生图(Text-to-Image)、多模态模型等。除了在原有的 CNN 技术架构上,也出现了很多新的突破。虽然同样使用的是深度神经网络,但图像生成技术与大语言模型技术(LLM)是非常不同的。其模型架构不同 ,底层的数学、物理原理也非常不一样。在对大语言模型有一个框架性的了解 之后,现在打算开一个新的“坑”,当然,也就没有打算爬出来,可能就“浅尝辄止”做个最为基础的了解。一般学东西,都是从 “Why/How/What” 的顺序,但在尝试了一下,无法理解“Why”后,于是就打算绕道,看看 How 和 What 了。那么,“What”打算就从“Foward Diffusion Process”开始吧。

    整个文生图(Text-to-Image)的架构是比较复杂的,其中较为“简单”、基础 的一步即位“Forward Diffusion Process”,本文从“Denoising Diffusion Probabilistic Models”论文中为例,说明这个过程。

    1. 概述:“FDP” 是一个加随机噪声的过程

    很多地方都会说:“Forward Diffusion Process” 就是一个添加随机噪声的过程。这话对、也不对。首先,“FDP” 确实是一个添加随机噪声的过程,但是这个随机噪声添加得非常有“讲究”。我们就从“添加随机噪声”和“讲究”两个角度去介绍。

    “Forward Diffusion Process” 过程的数据是用于训练神经网络(通常是一个U-Net架构)的参数的,从而最终实现其逆过程(即“Reverse Diffusion Process”),而生成图片。

    1.1 添加随机噪声

    “FDP” 做如下的事情:

    • 先读取一张图片(这里使用的一张博客图片)
    • 将图片的每个像素点取值,随机“迭代加上”一个随机值 \(N(0,1) \)

    例如,我们使用论文 DDPM 中的设定对如下图片添加噪声,就可以观察到如下过程:

    更为详细的:

    • 1. 先读取一张图片,并将其 RGB 通道的数据读取出来
    • 2. 将像素值从 [0, 255] 的整数缩放到 [0.0, 1.0] 的浮点数
    • 3. 每个通道每个点随机“迭代加上”一个随机值,按照 \(N(0,1) \) 分布生成该随机值

    2. “线性”添加噪声

    Denoising Diffusion Probabilistic Models 论文中使用了“线性调度”的方式添加噪声。即添加噪声的强度“线性”的逐渐增强,这里的“线性”是指增加的噪声的“方差”线性增加。

    先用更加形式化的数学语言描述上述的噪声添加,即:

    $$ x_t = A x_{t-1} + B \epsilon \quad \text{where} \quad \epsilon \sim \mathcal{N}(0,1) $$

    2.1 调整权重系数

    直觉上,可以这样理解,在添加噪声的过程中,刚开始是清晰图片,所以噪声添加的较少,而后,随着图片变得模糊,也逐步增加了添加噪声的强度(方差)。

    论文中 “线性调度” 模式做了如下设计:

    $$
    x_t = \sqrt{1 – \beta_t} x_{t-1} + \sqrt{\beta_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I}) \tag{1}
    $$

    这里的 \(\beta_t \) 是一个随着时间序列推进逐渐增大的值,从而在迭代过程中(或者说这个马尔科夫链中),逐步增加噪声在图片中的影响。论文中,\(\beta_t \) 是一个线性变换的序列,从 \(10^{-4} \),通过1000步迭代,增加到\(0.02 \)。

    2.2 计算的“简化”

    上述的“公式(1)”是一个迭代计算的“数列”(或者说是“马尔科夫链”),在实际的计算中,会经常使用如下的“通项公式”计算上述的迭代“数列”:

    $$
    x_t = \sqrt{\bar{\alpha}_t} x_0 + \sqrt{1-\bar{\alpha}_t}\epsilon \quad \epsilon \sim \mathcal{N}(0,1) \\
    \text{Where} \quad \alpha_t := 1-\beta_t ,\, \bar{\alpha}_t = \prod_{s=1}^{t}\alpha_s \tag{2}
    $$

    关于详细的如何从“公式(1)”严格的推导到上述表达式(2),参考本文小结“4.5 “调度公式”的推导”。

    上述的表达式,在论文中出现的形式则是:

    $$
    q(x_t|x_0) = \mathcal{N}(x_t; \sqrt{\bar{\alpha}}x_0,(1-\bar{\alpha_t}\mathbf{I}) ) \tag{3}
    $$

    这里的“公式(2)、(3)”所表达的意思是等价的。简单的说明:

    • (a) \(\mathcal{N}(x; \mu \, , \sigma^2) \) 表示正态分布的随机变量 \(x \),均值为\(\mu \) 方差为 \(\sigma^2 \)
    • (b) 上述的表达式中的 \(I \) 表示单位矩阵。这是因为公式中的 \(x_t \) 是一个表示所有像素值的向量(例如,128×128的向量,即可能有16384个随机变量),\(I \) 表示协方差矩阵是一个对角矩阵,即所有随机变量都是完全独立的。

    3. 代码实现

    完整的代码参考:Forward-Diffusion-Process.ipynb.ipynb

    3.1 读取并预处理图片

    该函数输入原始图片、迭代次数、初始噪声,即 (x_0, t,noise) 。原始图片在读取后,需要做几个处理:

    • 统一 Resize 到 128 x 128 像素来出来
    • 按 RGB 三通道转成一个 1 x 3 x 128 x 128 的张量/数组
    • 像素值从 [0, 255] 的整数缩放到 [0.0, 1.0] 的浮点数

    对应代码:

    # 3. 加载测试图片
    url = "https://www.orczhou.com/wp-content/uploads/2025/12/IMG_1710-scaled.jpg"
    img = Image.open(requests.get(url, stream=True).raw).convert("RGB")
    transform = transforms.Compose([transforms.Resize((128, 128)), transforms.ToTensor()])
    x_0 = transform(img).unsqueeze(0) # 变为 (1, 3, 128, 128)

    3.2 生成随机噪声

    生成一个随机按 \(N(0,1) \) 分布的对象,与上述图片相同,即:1 x 3 x 128 x 128

    noise = torch.randn_like(x_0) # 采样纯噪声 epsilon

    3.3 原始图片叠加噪声

    噪声的叠加并不是简单的直接相加( \(x_0 + \text{noise} \) ),而是一个迭代式的,并考虑原始图片影响的方式(线性采样考虑):

    $$
    \begin{aligned}
    x_t &= \sqrt{1 – \beta_t} x_{t-1} + \sqrt{\beta_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I}) \\[0.5em]
    x_t &= \sqrt{\bar{\alpha}} x_0 + \sqrt{1-\bar{\alpha}}\epsilon
    \end{aligned}
    $$

    4. 公式(1)的说明

    我们再来看看 “公式(1)” 的设计:

    $$
    x_t = \sqrt{1 – \beta_t} x_{t-1} + \sqrt{\beta_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I}) \tag{1}
    $$

    为什么不用最为直观、自然的 \(1-\beta_t \) 与 \(\beta_t \) 作为上述表达式中的系数,而是使用了他们的平方根?

    这个“设计”思路的根源是因为:高斯分布乘以一个常数后,其方差则为该常数的平方再乘以原来的方差。即: \(X \sim \mathcal{N}(\mu,\sigma^2) \),那么 \(aX \sim \mathcal{N}(a\mu,a^2\sigma^2) \)。

    整体上,考虑是希望在传播过程中,方差不要偏离太大。并且随着时间的推进,最终的数值是一个标准正态分布的,如果来看推迟到出来的“公式3”:

    $$
    q(x_t|x_0) = \mathcal{N}(x_t; \sqrt{\bar{\alpha}}x_0,(1-\bar{\alpha_t})\mathbf{I}) \tag{3}
    $$

    可以看到在这样的“设计”(即使用“根号”)下,最终迭代的\(x_t \) 的均值是 \(\sqrt{\bar{\alpha}}x_0 \),方差为 \(1-\bar{\alpha_t} \),随着\(t \)的增加,就逐步趋向于 \(\mathcal{N}(0,1) \)了。

    5. 一些数学公式与推导

    Diffusion 相关的数学基础还是非常、非常复杂的,而这里的公式推导看起来虽然有点复杂,但可能是整个Diffusion模型的数学基础中最为简单的部分了。这里,勉强祭出右边的图片。

    我们这里还是来做一些尝试吧。

    5.1 Forward Diffusion Process 公式

    $$x_t = \sqrt{1 – \beta_t} x_{t-1} + \sqrt{\beta_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I}) \tag{1} $$

    这个公式本身已经有一定的复杂度了,要搞清楚大概需要关注如下点:

    • 这里的 \(\epsilon \) 是什么意思
    • \(\beta_t \) 的计算设计
    • 从“迭代公式(1)”到“通项公式(2)”

    5.2 唬人的 \(\epsilon \)

    多维变量的概率已经忘得差不多了…,好在这里是一些完全“独立”的随机变量,还比较好理解。我们先看看这里的: \(\epsilon \sim \mathcal{N}(0, \mathbf{I}) \)。

    第一次看到这些个符号的时候,也是被“怔”了一下的,仔细一看还好。

    首先,这里公式中的 \(x_t \) 是一张图片所有的像素信息,例如,如果是一张 128×128 的图片,那么所有的像素信息则是一个长度为 1 x 3 x 128 x 128 的向量。即,\(x_t \) 是一个 3 x 128 x 128 的向量。

    对应的,这里的 \(\epsilon \) 也是一个这样的向量(例如, 3 x 128 x 128 的向量,而不是数学中常见的表示一个很小的值),可以这样理解,这个向量的每一个取值都是一个随机变量(例如一共 3 x 128 x 128 个随机变量),每一个随机变量都是独立的,即协方差矩阵为单位矩阵(这里的 \(\mathbf{I} \)),并且每个随机变量符合标准正态分布,即 \(\mathcal{N}(0, 1) \)。

    再回头看看原公式,是不是简单了很多:

    $$x_t = \sqrt{1 – \beta_t} x_{t-1} + \sqrt{\beta_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I}) $$

    5.3 线性调度

    再来看公式中的 \(\beta_{t} \):

    $$x_t = \sqrt{1 – \beta_t} x_{t-1} + \sqrt{\beta_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I}) $$

    在原始的 Denoising Diffusion Probabilistic Models 论文中,取值如下:\( \beta_1 = 10^{-4} ,\, \beta_{T} = 0.02 \),即均匀线性的在1000次噪声添加中,\(\beta \)均匀的从\(0.0001 \) 增长到 \(0.02 \),即:

    $$ \beta_1 = 0.0001, \beta_2 = 0.0001199, \beta_3 = 0.0001398 , … , \beta_{1000} = 0.02 $$

    在 Python 中就是如下代码:

    T = 1000  # 总步数
    betas = torch.linspace(0.0001, 0.02, T) # 线性调度:噪声方差逐渐增大

    5.4 “线性调度”的真实计算式

    但是,在实际的运算不会用上面的公式。而是,使用了这个版本的推导,从而更加高效,更加直觉,同时,看起来更加“抽象”,即在论文中的如下公式:

    $$ q(x_t|x_0) = \mathcal{N}(x_t; \sqrt{\bar{\alpha}}x_0,(1-\bar{\alpha_t}\mathbf{I}) ) \tag{3}$$

    这个版本看起来就很“唬人” ,但理解了其意思还是感觉比较“简洁”的,再理解之后,就觉得也还比较“简单”。

    本质上,这公式是前面“公式(1)”的推导与快速计算版本,该公式提供了一个无需迭代计算,而直接根据\(x_0 \) 计算 \(x_t \) 的方法。其中的 \(\alpha_t := 1-\beta_t ,\, \bar{\alpha}_t = \prod_{s=1}^{t}\alpha_s \) 。

    5.5 “调度公式”的推导

    这里来尝试做一下“公式(1)”到“公式(2)”的推导,尝试理解以下研究者们这部分工作(也可以参考这里:Diffusion Models: A Mathematical Introduction的第14页)。

    $$
    \begin{aligned}
    x_t &= \sqrt{1 – \beta_t} x_{t-1} + \sqrt{\beta_t} \epsilon \\[0.5em]
    x_{t-1} &= \sqrt{1 – \beta_{t-1}} x_{t-2} + \sqrt{\beta_{t-1}} \epsilon \\[0.5em]
    x_{t-2} &= \sqrt{1 – \beta_{t-2}} x_{t-3} + \sqrt{\beta_{t-2}} \epsilon \\[0.5em]
    \quad &\vdots & \\[0.5em]
    x_{1} &= \sqrt{1 – \beta_{1}} x_{0} + \sqrt{\beta_{1}} \epsilon \\[0.5em]
    \end{aligned}
    $$

    所以:

    $$
    \begin{aligned}
    x_t &= \sqrt{1 – \beta_t} (\sqrt{1 – \beta_{t-1}} x_{t-2} + \sqrt{\beta_{t-1}} \epsilon) + \sqrt{\beta_t} \epsilon \\[0.5em]
    &= ( \sqrt{1 – \beta_t}\sqrt{1 – \beta_{t-1}} )x_{t-2} + ( \sqrt{1 – \beta_t}\sqrt{\beta_{t-1}} )\epsilon + \sqrt{\beta_t} \epsilon
    \end{aligned} \tag{4}
    $$

    接下去看看上面等式的后面两部分:

    $$ ( \sqrt{1 – \beta_t}\sqrt{\beta_{t-1}} )\epsilon + \sqrt{\beta_t} \epsilon \tag{5}$$

    这里并不是简单的加法,而是两个独立概率分布的加法:如果概率基础还在的话,就有如下的公式,两个独立的高斯分布(例如:\(X \sim \mathcal{N} (\mu_X ,\sigma^2_X ) \quad Y \sim \mathcal{N} (\mu_Y ,\sigma^2_Y ) \))的随机变量之和,其结果依旧是高斯分布,并且均值依据是两个均值的和、方差也是两个方差的和( \(X+Y \sim \mathcal{N} (\mu_X + \mu_Y ,\sigma^2_X + \sigma^2_Y ) \) )。

    注意上面的表示 \(\sqrt{\beta_t}\epsilon \) 中,\(\sqrt{\beta_t} \) 是标准差,方差即 \(\beta_t \)。

    所以上面“公式(5)”两个分布的和,依旧是高斯分布,且均值依旧是 0,方差则为 \((1-\beta_t)\beta_{t-1}+\beta_t \),即有了如下看似错误的,但是却是正确的推导:

    $$
    ( \sqrt{1 – \beta_t}\sqrt{\beta_{t-1}} )\epsilon + \sqrt{\beta_t} \epsilon = \sqrt{(1-\beta_t)\beta_{t-1}+\beta_t} \epsilon
    $$

    其实上面并不是一个一般意义的“等式”,而是表达了如下的含义:

    $$
    ( \sqrt{1 – \beta_t}\sqrt{\beta_{t-1}} )\epsilon + \sqrt{\beta_t} \epsilon \sim \mathcal{N}(0,(1-\beta_t)\beta_{t-1}+\beta_t)
    $$

    有了这里的理解,就以继续上面公式(4)的推导就非常容易有如下的结论了:

    $$
    \begin{aligned}
    x_t &= \sqrt{1 – \beta_t} (\sqrt{1 – \beta_{t-1}} x_{t-2} + \sqrt{\beta_{t-1}} \epsilon) + \sqrt{\beta_t} \epsilon \\[0.5em]
    &= ( \sqrt{1 – \beta_t}\sqrt{1 – \beta_{t-1}} )x_{t-2} + ( \sqrt{1 – \beta_t}\sqrt{\beta_{t-1}} )\epsilon + \sqrt{\beta_t} \epsilon \\[0.5em]
    &= \sqrt{\alpha_t\alpha_{t-1}}x_{t-2} + \sqrt{1-\alpha_t\alpha_{t-1}} \epsilon \\[0.5em]
    &\vdots \\[0.5em]
    &=\sqrt{\alpha_t\alpha_{t-1}\cdots\alpha_1}x_{0} + \sqrt{1-\alpha_t\alpha_{t-1}\cdots\alpha_{1}} \epsilon \\[0.5em]
    &= \sqrt{\bar{\alpha_t}}x_0 + \sqrt{1-\bar{\alpha_t}} \epsilon \\[0.5em]
    \text{where}\, \alpha_t &:= 1-\beta_t ,\, \bar{\alpha_t} = \prod_{s=1}^{t}\alpha_s
    \end{aligned}
    $$

    即有了最终的公式:

    $$
    x_t = \sqrt{\bar{\alpha_t}}x_0 + \sqrt{1-\bar{\alpha_t}} \epsilon \\
    \text{where}\, \alpha_t := 1-\beta_t ,\, \bar{\alpha_t} = \prod_{s=1}^{t}\alpha_s
    $$

    5.6 在Python中的实现

    了解了上面这些,再看这些代码就很简单了:

    T = 1000  # 总步数
    betas = torch.linspace(0.0001, 0.02, T) # 线性调度:噪声方差逐渐增大
    
    # 计算中间变量
    alphas = 1. - betas
    alphas_cumprod = torch.cumprod(alphas, axis=0) # 对应公式中的 alpha_bar

    以及最后的 Forward Diffusion Process 计算:

        sqrt_alphas_cumprod_t = torch.sqrt(alphas_cumprod[t]) # 信号系数
        sqrt_one_minus_alphas_cumprod_t = torch.sqrt(1. - alphas_cumprod[t]) # 噪声系数
    
        # 核心公式实现
        return sqrt_alphas_cumprod_t * x_0 + sqrt_one_minus_alphas_cumprod_t * noise, noise

    6. 微分方程角度的考虑

    从随机微分方程的角度考虑 “DDPM” 模型是该模型发布之后的事情了,我们这里先看看这个随机微分方程的“漂移/Drift”部分与上述迭代式子的关系。

    先不考虑“随机项”的增加,那么在设计时,希望随着时间步骤的迭代,变化的速率逐渐增加,即迁移变化慢,后期变化快,即考虑在微分方程的右侧增加一下 \(\beta(t) \),该函数随着时间增加而增加,最为常见的即为线性增长(对应于“线性调度”)。此外,该变化率应该与当前值有关,当前值越大,则变化率应该越大;并且期望最终迭代结果趋向于零(即均值最终为零的正态分布),就有最终的微分方程设计:

    $$ \frac{dx}{dt} = -\frac{1}{2}\beta(t)x \tag{5} $$

    说明:

    • 方程右侧的负号,表示总是朝着x取值相反的方向移动,即总是朝着原点方向移动
    • \(\beta(t)x \)则表达了上述的两个关于“变化率”大小的意图

    该微分方程的迭代解就有如下的迭代表达式:

    $$
    \begin{aligned}
    &x_{t} – x_{t-1} = -\frac{1}{2}\beta(t)x_{t-1} \\[0.5em]
    &x_{t} = x_{t-1} -\frac{1}{2}\beta(t)x_{t-1} \\[0.5em]
    &x_{t} = (1 -\frac{1}{2}\beta(t))x_{t-1} \\[0.5em]
    \end{aligned}
    $$

    很神奇的是,在\(\beta \)很小的时候,根据泰勒展开有:

    $$
    \sqrt{1-\beta} = 1 – \frac{1}{2}\beta – \frac{1}{8}\beta^2 + \cdots
    $$

    最终就有:

    $$
    x_{t} = \sqrt{1-\beta_t}x_{t-1}
    $$

    这里就可以从“微分方程”的角度去理解上述的 stable diffusion 中 Forward Diffusion Process 中前半部分了。

    7. 小结 FDP

    在了解 What 中,也在慢慢理解 How 以及Why 。这里再次从宏观上概述 FDP 的过程,从而跳出上述的 What 细节,再次审视这个过程。

    7.1 首先,为什么需要 “Forward Diffusion Process” ?

    简单回答:给样本添加噪声,构建训练数据。

    “Forward Diffusion Process” 过程的数据主要用于训练,对于一个给定的图片,逐步添加噪声,最后让其变成一张纯粹的、高斯分布的噪声。而这个过程的数据,则可以用于训练 U-Net 的神经网络,让该U-Net具备一个神奇的能力:即给出一张图片(带有噪声的),该 U-Net 可以预测出这张图片中有哪些是噪声。

    7.2 “Forward Diffusion Process” 操作的数学计算

    其核心公式如下:

    $$
    x_t = \sqrt{1 – \beta_t} x_{t-1} + \sqrt{\beta_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I})
    $$

    经过推导,等价与如下公式(关于公式的推导:Forward Diffusion Process):

    $$
    x_t = \sqrt{\bar{\alpha_t}} x_0 + \sqrt{1-\bar{\alpha_t}}\epsilon \quad \epsilon \sim \mathcal{N}(0,1) \\
    \text{Where} \quad \alpha_t := 1-\beta_t ,\, \bar{\alpha_t} = \prod_{s=1}^{t}\alpha_s \tag{a}
    $$

    在论文中可能看到的形式:

    $$
    q(x_t|x_0) = \mathcal{N}(x_t; \sqrt{\bar{\alpha}}x_0,(1-\bar{\alpha_t}\mathbf{I}) )
    $$

    对于一张照片实际做上述操作则有如下效果:

    关于上述公式的一些重要特性:

    • 经过若干次迭代后,一张清晰的图片最终变成一张“随机”噪声图片,这里的“随机”是指的正态分布
    • 从“公式(a)”可以看到,逐步迭代和多步合并迭代有一样的效果。当然,为了获得训练数据,总是逐步迭代

  • 了解 CLIP 模型

    ·

    深度学习领域除了大语言模型之外,另一个极其活跃的领域即为视频、图像处理领域(或者叫“多模”数据)。随着 Stable Diffusion 取得成功,这个领域也在非常快速的发展。这里也打算极其粗浅的了解一下这个领域。打算,从最为基础“组件”之一 “CLIP” 开始。

    “CLIP”的全称是“Contrastive Language–Image Pre-training”,描述的是一种训练方法,可以把“图片”、“文字”嵌入(Embedding)到同一个空间(例如 512 维的向量空间),并保持非常好的相关性。利用这个特点,则可以非常好的进行诸如:图片相似性、文本图片相关性的人物;也可以作为其他模型的一部分,提供非常好的文本与图片一致性的关系。这里以典型的“clip-vit-base-patch32”模型为例,去简单实践并了解该模型。

    1. 实验设计

    这里选择一张图片,如右图,使用该模型对图片进行 Embedding;然后再选择一组文本,如下,使用该模型对其进行 Embedding ,然后观察这些向量之间的余弦距离关系。

    texts = [
        "a dog",
        "a cat",
        "a car",
        "a landscape",
        "a person",
        "a person behind a statue",
        "a person riding a bicycle",
        "tourist attraction",
        "a vacation"
    ]

    2. 实验数据与结果分析

    在使用模型进行Embedding后,然后把所有文本向量与图片计算余弦相似度后有右侧数据:

    可以看到,图片和文本之间表现出了非常强的相关度。相关度最高的文本为:“a person behind a statue”,也是所有文本中对图片最为准确的描述。“a person” 也比 “a person riding a bicycle” 有着更高的相似度,更比诸如 “a dog”、“a cat” 要高非常多。

    0.2707  |  a person behind a statue
    0.2508  |  tourist attraction
    0.2074  |  a person
    0.2063  |  a vacation
    0.1974  |  a person riding a bicycle
    0.1875  |  a dog
    0.1821  |  a landscape
    0.1754  |  a cat
    0.1655  |  a car

    2.1 可视化结果

    这里在一个数轴上,简单对上述结果进行可视化,从而建立一个更为直观一点的认识:

    在数轴上将上述文本与图片的相似度取值绘制出来,可以看到,“a person behind a statue”远比其他的文本相似度要高。

    3. 其他

    对于 Embedding 出来的向量,这里也使用诸如“T-SNE”、“PCA”、“UMAP”、“MDS”等方式去降维到二维空间分析,但是并没有观察余弦相似度的保持,与上述计算结果不符合。也许是点太少、也许是用法不对。这也让对诸如“T-SNE”等算法在可视化上效果,提高了警惕。关于这部分计算的数据和结果可以参考:SD-Forward-Diffusion-Process.ipynb@Colab,这里不再展示。

  • 线性代数回顾(二)

    ·

    到目前为止,前面的线性方程组的解,还有一些问题没有彻底回答(例如,解空间的描述),在回答这个问题之前,我们需要先了解一下“向量空间”。“向量空间”的严格定义是有些枯燥的,这里暂时把“向量空间”的限制为大家所熟悉的、最为典型的“ \(n \) 维欧氏空间”。

    1. 向量空间

    1.1 基

    要描述一个向量空间中的元素,则首先需要一组基(坐标)。在\(n\)维欧氏空间,最为常见的一组基,即为多个“垂直”(“正交”)的单位向量,即:

    $$ \begin{align}
    \alpha_1 &= (1,0,\dots) \\
    & \vdots \\
    \alpha_i &= (\dots,0,1,0,\dots) \\
    & \vdots \\
    \alpha_n &= (0,\dots,1)
    \end{align}
    $$

    在二维平面空间中,则为:\( \alpha_1 = (1,0) \quad \alpha_2 = (0,1)\);三维空间则为:\( \alpha_1 = (1,0,0) \quad \alpha_2 = (0,1,0) \quad \alpha_3 = (0,0,1)\)。

    既然有“正交”基,那么当然有不那么“正交”的基,而此类“基”则是更为普遍的。事实上,更为普遍的,任何 \( n \) 个线性无关的向量都可以作为向量空间的基。

    1.2 线性相关与线性无关

    考虑一组向量\( \alpha_1,\dots , \alpha_n \),如果当且仅当所有\( a_i = 0 \quad i=1,\dots,n\)时如下的等式才成立:

    $$ a_1\alpha_1 + a_2\alpha_2 + \dots + a_n\alpha_n = 0 $$

    那么,就说 这组向量 \( \alpha_1,\dots , \alpha_n \) 是线性无关的。反之,则称这组向量是线性相关的。

    或者这么说,对于一组线性无关的向量\( \alpha_1,\dots , \alpha_n \):任何一个向量都不能用剩余的向量做“线性表示”。

    1.3 一些重要的结论

    结论:设\( \{ \alpha_1,\dots , \alpha_n \}\)是向量空间\( V \)的一组基,那么\( V \)空间中的每一个向量都可以唯一的表示为这组基的线性组合。这个线性组合的系数,就叫“坐标”(注:相对于这组基)。

    结论:\( W_1 \)、\( W_2 \)是\( V \)的有限子空间,那么有:

    $$ dim(W_1+W_2) = dim(W_1) + dim(W_2) – dim(W_1\cap W_2) $$

    结论:\( n \)维向量空间中,任意\( n \)个线性无关的向量都可以取做基。

    2. 线性变换与矩阵

    2.1 概述

    “线性变换”是指向量空间中一类特殊的映射 \( \sigma : R^n \to R^m \) ,需要满足条件是:

    • \( \sigma(\xi + \eta) = \sigma(\xi) + \sigma(\eta) \)
    • \( \sigma(a\xi) = a\sigma(\xi) \)

    “线性变换” 描述了向量空间之间的映射。后续所有的内容大概都是围绕此而展开,后续所有的内容都会尝试通过各种方式将 “线性变换” 的特性研究清楚。这里写出部分结论,后面再慢慢展开:

    • 线性变换之下,原点保持不变。即 \( \sigma( \vec{0} ) = \vec{0} \)
    • 几何意义下,通常,线性变换包括了:旋转、镜像、拉伸/压缩(特别的,有时候会压缩到零)、剪切

    为了研究清楚一个线性变换上述的特点,通常需要选取一组“基”,然后使用这组“基”的“坐标”来描述空间中的点,进而再描述对应的线性变换。最为常见的基为“正交单位基”。

    从方法上来看,研究清楚“线性变换”最为关键的是研究清楚对应的“变换矩阵”。所以,“线性代数”的核心后面就变成了对矩阵特性的研究,但是,也不要忘记了初衷,否则很快就迷失了。

    2.2 线性变换

    我在大学期间对于线性变换、矩阵有什么作用,是完全没有概念的。所以对于他们的特性研究也没有掌握的很深,基本上是停留在能够把一些联系题做对这个层面。而现在,注意线性变换的广泛应用之后,尝试去理解去本质之后,就会寻根问底的去理解清楚什么是线性变换、什么是矩阵。这里再次说说我的理解。

    在一个向量空间中,最为常见的是 \(n \) 维欧氏空间,会有很多的向量,例如每个 Embedding 就可以理解是在一个线性空间中,“线性变换”表述了是空间中的一类映射,该映射满足上述“小结2.1”中的两个要求,即原点依旧到原点、映射保持所谓的“线性”(例如,向量和的映射等于映射的和等)。

    在一个向量空间中,一个线性变换就是一个符合一定条件的映射。与向量空间的基的选取是没有关系的。自此,与矩阵也是没有关系的。所以,线性变换本身是更为底层、更为基础的概念。

    2.3 欧氏空间的线性变换与矩阵

    现在我们把问题限定在 \(n \) 维欧氏空间中。那么,这时候,我们如何描述一个线性变换呢?是的,就是“基”与“矩阵”。

    通常,\(n \) 维欧氏空间,我们会先选取一组基,然后使用一个矩阵去描述这个线性变换。并且非常幸运的,一旦这组基 选定了,这个矩阵是唯一的。

    结论:在\(n \) 维欧氏空间(这个条件似乎可以去掉)中,对于线性变换 \(\sigma \),如果选定一组“基”,那么就存在唯一的“矩阵”描述该线性变换。

    上述的结论,是比较明显的。我们考虑对于线性变换中的上述选定的基向量 \(\alpha_i,\quad \text{where } i = 1,\ldots,n \),线性变换将其映射到 \(\beta_i,\quad \text{where } i = 1,\ldots,n \),那么根据“基”的基本性质,对于这里的任何 \(\beta_i \)都可以表示成\(\alpha_i \)的线性组合,所有的这些系数构成的矩阵,就是上述描述的唯一的“矩阵”。具体的:

    $$
    \begin{aligned}
    \beta_1 &= a_{11}\alpha_1 + a_{21}\alpha_2 + \cdots + a_{n1}\alpha_n \\[0.5em]
    \beta_2 &= a_{12}\alpha_1 + a_{22}\alpha_2 + \cdots + a_{n2}\alpha_n \\[0.5em]
    &\ \vdots \\[0.5em]
    \beta_n &= a_{1n}\alpha_1 + a_{2n}\alpha_2 + \cdots + a_{nn}\alpha_n
    \end{aligned}
    $$

    即:

    $$
    \begin{aligned}
    \begin{bmatrix}
    \beta_1 & \beta_2 & \cdots & \beta_n
    \end{bmatrix}
    =
    \begin{bmatrix}
    \alpha_1 & \alpha_2 & \cdots & \alpha_n
    \end{bmatrix}
    \begin{bmatrix}
    a_{11} & a_{12} & \cdots & a_{1n} \\
    a_{21} & a_{22} & \cdots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{n1} & a_{n2} & \cdots & a_{nn}
    \end{bmatrix}
    \end{aligned}
    $$

    即在这个线性变换\(\sigma \) 在选定基 \(\alpha_i,\quad \text{where } i = 1,\ldots,n \) 对应的矩阵为:

    $$
    \begin{aligned}
    A =
    \begin{bmatrix}
    a_{11} & a_{12} & \cdots & a_{1n} \\
    a_{21} & a_{22} & \cdots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{n1} & a_{n2} & \cdots & a_{nn}
    \end{bmatrix}
    \end{aligned}
    $$

    从这段简洁的“证明”(或“说明”)来看,我们很自然有如下结论,根据空间中的“基”的选取不同,我们会得到不同的矩阵。因为我们反复会提到,我们经常会通过研究矩阵的特性来研究线性变换。那么,同一个线性变换在不同的“基”下的不同“矩阵”,很自然的能够想到,这些“矩阵”是有某些共性的,是的,我们称这些矩阵为“相似矩阵”,相关特性,暂不展开。

    3. 特征向量与特征值

    3.1 为什么

    为什么我们需要关注“特征值与特征向量”呢?为什么我们要去了解奇异值分解(SVD)呢?

    原因是“线性变换”是一个映射,是非常抽象的。而特征值 、特征向量、SVD分解可以把线性变换最为关键的特性,以非常“直观”的形式表达出来。当然,这里的“直观”并不是简单意义上能够一眼就看出什么来,事实上,“线性变换”本身就有很强的抽象性,这里的“直观”只是相对的,是否直观,完全依赖于各位看客自己的“悟性”了 。

    特征向量的基本定义:如果有 \( \sigma(\xi) = \lambda \xi \) ,那么这里的 \( \xi \) 就是特征向量,对应的 \(\lambda \) 就是对应的特征值。

    要想真正说清楚特征向量与特征值是需要非常多篇幅的,而且关于对特征向量的理解对于理解线性变化也是非常关键的,所以,建议花些时间较为系统的做一些理解。如果你已经建立的基础概念,这里的一篇文章可能是帮助你增强一些理解:特征向量与特征值。

    3.2 关于对特征向量的理解

    完整的讨论特征向量与特征值是复杂的,这里将其限定在一些较为简单的情况,作为一个入门。我们这里考虑最为简单的情况,即对于一个 \(n \times n \)的矩阵,其秩为 \(n \),并且在计算特征值时,有 \(n \) 是不重复的实数解,即没有任何根式重根。如果,恰好 \( n = 2 \)这大概是最为简单的情况了,不过理解这种情况,再进一步拓展,则学习曲线会平滑很多。

    我们来看一个实例,在二维空间中,在标准基下,我们有如下的线性变换矩阵:

    $$
    W = \begin{bmatrix}
    2 & 1 \\
    1 & 2
    \end{bmatrix}
    $$

    根据上述特征向量特征值的定义进行求解,我们可以有如下的特征值与特征向量:

    • \( \lambda_1 = 3 \) 特征向量 \( (1,1) \)
    • \( \lambda_2 = 1 \) 特性向量 \( (-1,1) \)

    从特征向量角度理解线性变换:那么上述的矩阵A对应的线性变换 \(\sigma \) 有如下特性,在这个二维空间任何向量 \(\beta \),都可以分解(投影)为上述两个特征向量方向的向量: \(\beta_1 \,, \beta_2 \),且有:\(\beta = \beta_1 + \beta_2 \)。那么,则有:\(\sigma(\beta) = \lambda_1 \beta_1 + \lambda_2 \beta_2 \)。即,这个线性变换可以这样描述:先将任何向量沿着特征向量方向分解,然后再按照特征值的大小进行拉伸或压缩,然后再把向量合并起来。

    上述的解释,可以对照着右图去理解。特征向量分别为 \((1,1) \) 和 \((-1,1) \) ,即图中浅绿色、浅蓝色方向。该矩阵作用在向量 \((0,1) \) 上,即图中的红色向量。先将红色向量沿着浅绿色、浅蓝色方向分解,然后按照特征值进行拉伸,即图中的绿色、蓝色向量,最后合并为图中的紫色最终向量。

    上述的场景是线性变换中,最为简单的一类。而实际的线性变换,则更为复杂,可能还涉及到对于向量的旋转、镜像、剪切等变换。关于更多场景可以自己探索,或者阅读相关书籍,也可以看看这篇文章中的更多直观的例子:特征值与特征向量

    “特征向量”可以很好的帮助理解“方阵”变换,还有一类变换时非方阵的情况,通常这时候可以借助于奇异值分解的方式去理解,关于奇异值分解可以参考:奇异值分解–深度学习的数学基础

    4. 最后

    初等的线性代数核心部分大概是这些内容,出于完整性的考虑,可以再进一步了解“Jordan 块”相关的内容,从而把相关理论补充完整,这里不再详述。

    如果再回到最初的线性方程组解的问题,我们这里就可以回答最后一个问题:对于一个线性方程组,如果有解,那么所有的解空间是怎样的?

    结论:如果方程组的系数矩阵的秩为\( r \),那么解空间的维度为\( n-r \)。解空间的“基”则可以通过初等变换求得。这里不再详述。

  • 线性代数回顾

    ·

    如果要理解大模型内部是如何工作的,良好的数学基础是必须的,而线性代数又是所有这些的基础。例如我们来看 \(\text{Attention} \) 机制中的如下问题。

    1. 为什么要重温线性代数

    考虑第 \(j \) 个 \( \text{Layer} \) 中的第 \( i \) 个 \( \text{Head} \) ,则有如下的计算(为了简化,下述的角标省略了 \( \text{Layer} \) 部分):

    $$
    \begin{aligned}
    Q_i &= XW_i^Q \\[0.3em]
    K_i &= XW_i^K \\[0.3em]
    \text{Therefore:} \\[0.3em]
    \text{Attention Score}_i &= Q_iK_i^T \\[0.3em]
    &= XW_i^Q(XW_i^K)^T \\[0.3em]
    &= XW_i^Q\big((W_i^K)^T X^T\big) \\[0.3em]
    &= XW_i^Q(W_i^K)^T X^T \\[0.3em]
    &= X\big(W_i^Q (W_i^K)^T\big) X^T \\[0.3em]
    \text{Let:} \quad \\[0.3em]
    W_i^{QK} &= W_i^Q (W_i^K)^T \\[0.3em]
    \text{Therefore:} \\[0.3em]
    \text{Attention Score}_i &= XW_i^{QK} X^T \\[0.3em]
    \end{aligned}
    $$

    注:上述的推导使用矩阵的一些简单特性,包括转置计算、结合律等。其中,在开源的GPT2模型中,\(W_i^Q \,, W_i^K \) 都是 \(64 \times 768 \)的矩阵。

    这里一个简单、又不太简单的问题是:那为什么每一个 \( \text{Head} \) 中不使用一个权重 \( W_i^{QK} \) 就可以了?这个问题从我第一次看明白 \( \text{Attention} \) 的计算后,困扰了我一会儿,直到重温了线性代数,才算是理解了上述的计算。

    大学时学习线性代数学得非常痛苦,而现在带着问题再去看这本书,竟然花了两个晚上就看完了。这里对里面的基本概念和结论做个梳理,以更好的理解什么是“线性变换”、与矩阵的关系是什么、如何研究一个线性变换或矩阵的性质等。

    本系列主要以介绍线性代数的“直觉”为主,不会做任何证明,为了更好的阐述“直觉”,甚至牺牲了很多的数学严谨,看客也需要从构建“直觉”与“联系”的角度阅读。本文的阅读前提是已经有一定的线性代数的基础、也对神经网络/LLM技术有一定了解,那么本文则尝试通过较小的篇幅去构建两者的联系,看看如何使用线性代数的技术去研究神经网络的中的问题。

    2. 线性代数讨论的主要问题

    通常“线性代数”课程会从 \(n\) 元一次线性方程组引入,并使用行列式理论,去较为彻底的回答如何解决 \(n\) 元一次线性方程组。更进一步的,为了更好/更完整的研究 \(n\) 元一次线性方程组的“解空间”,则需要引入一个新的研究对象:“向量空间”。而向量空间本身所具备的普遍性,已经远超出 \(n\) 元一次线性方程组本身。而后,“向量空间”、“线性变换”就变成了新的研究对象,因为现实问题中,我们经常会尝试通过“线性变换”来洞察向量空间中向量的关系。

    是不是感觉上面描述漏了什么?是的,漏了“矩阵”。无论是讨论 \(n\) 元一次线性方程组还是“向量空间”或“线性变换”,矩阵都是“核心”工具。这个工具“核心”或者说重要到什么程度呢?甚至很多问题,只需要研究清楚对应“矩阵”的特性,原来的问题就研究清楚了。所以,你会注意到,线性代数的书中,几乎全都在介绍“矩阵”的各种特性。

    而各种 \( \text{Embedding} \) 就可以理解为是在最为典型的欧氏空间中的向量。

    3. \(n \) 元一次方程组的解

    线性代数通常都会以解“\( n \)元一次方程组”为切入点,这个问题看似很简单,但是最终要完全讨论清楚,则需要很多篇幅。这也是“线性代数”前半部分比较枯燥的原因。整体上来看,关于“\( n \)元一次方程组”的解需要讨论清楚几个问题:

    • (a) \( n \)元一次方程组的解是否存在?
    • (b) 如果存在,如何求解
    • (c) 如果解不存在,充要条件是什么
    • (d) 如果有解,那么所有的解如何表达

    在探讨上述问题的时候,先是引入了“行列式”、“矩阵”的概念与理论,并通过矩阵的“初等变换”实现对上述问题的求解。这里涉及到的概念延伸出了:

    • (a) 矩阵的初等变换
    • (b) 矩阵的秩等

    这里我们列举一些主要的结论(并不做推倒,推倒过程还是非常复杂的,这也正是线性代数书比较枯燥的原因之一)。这里考虑如下的线性方程组:

    $$
    a_{11}x_1 + a_{12}x_2 + \cdots + a_{1n}x_n = b_1, \\
    a_{21}x_1 + a_{22}x_2 + \cdots + a_{2n}x_n = b_2, \\
    \quad\vdots \\
    a_{m1}x_1 + a_{m2}x_2 + \cdots + a_{mn}x_n = b_m.
    $$

    结论: 线性方程组有解的充分必要条件是:它的系数矩阵与增广矩阵有相同的秩。

    结论: 线性方程组系数矩阵和增广矩阵的秩都是\( r \),方程组的未知数的个数是\( n \),如果:

    • \( r = n \) 则线性方程组有唯一解
    • \( r < n \) 则线性方程组有无穷组解

    上述两个定理较为彻底的回答了解存在性的问题。那么,解的公式化表达是怎样的呢?为了略微简化问题,这里考虑仅考虑\( n \)个方程、\( n \)个未知数,且解唯一的情况:

    $$
    \begin{cases}
    a_{11}x_1 + a_{12}x_2 + \cdots + a_{1n}x_n = b_1, \\
    a_{21}x_1 + a_{22}x_2 + \cdots + a_{2n}x_n = b_2, \\
    \quad\vdots \\
    a_{n1}x_1 + a_{n2}x_2 + \cdots + a_{nn}x_n = b_n
    \end{cases}
    \quad\Longleftrightarrow\quad
    \begin{bmatrix}
    a_{11} & a_{12} & \cdots & a_{1n} \\
    a_{21} & a_{22} & \cdots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{n1} & a_{n2} & \cdots & a_{nn}
    \end{bmatrix}
    \begin{bmatrix}
    x_1 \\ x_2 \\ \vdots \\ x_n
    \end{bmatrix}
    =
    \begin{bmatrix}
    b_1 \\ b_2 \\ \vdots \\ b_n
    \end{bmatrix}
    $$

    形式化的解,则可以由两种方式给出:

    使用矩阵的表达:

    $$
    \vec{x} = A^{-1}\vec{b}
    $$

    克莱默法则”(Cramer’s rule / formula):

    $$
    x_1 = \frac{D_1}{D},x_2 = \frac{D_2}{D},\dots , x_n = \frac{D_n}{D},
    $$

    注意:上述的两种表达,无论哪种,限制条件都非常苛刻,即要求:矩阵\( A \)可逆或者行列列式\(D \neq 0\),当然,这两个条件式等价的。并且,这里的\(D\)也经常写作:\( det(A) \)。

    4. 矩阵基础与部分结论

    虽然是为了求解线性方程组才引入矩阵的,但很快就会意识到,对矩阵本身特性的研究有着更为广泛的应用。

    首先,在定义了矩阵的运算之后,很快就会有一些矩阵的运算律。例如,加法的结合律、交换律、分配律等。这里,矩阵的乘法是重点,且略微复杂一些:

    • 首先,首先矩阵的乘法是不满足交换律的
    • 很幸运,结合律和分配律都是满足的
    • 再次,对于转置运算,是满足如下形式的:\( (AB)^T = B^TA^T \)

    结论: 线性方程组的初等变换,对应着三个初等变换矩阵:\( P_{ij} \)、\( D_i(k) \)、\( T_{ij}(k) \),且这三个初等变换矩阵都是可逆的。

    结论: 一个\( m \times n\)的矩阵\( A \)总是可以通过初等变换化为以下形式的矩阵:

    $$ \bar{A} = \begin{bmatrix}
    I_r & O_{r,\,n-r} \\
    O_{m-r,\,r} & O_{m-r,\,n-r}
    \end{bmatrix} $$

    这里,\(I_r\)是\( r \)阶单位矩阵,\(O_{st}\)表示\( s\times t\)的零矩阵,\( r \)等于矩阵\(A\)的秩。

    结论:\(n \) 阶矩阵\( A \)可逆,当且仅当\( A \)的秩等于\( n \)。

    如何求解矩阵的逆:有了这些结论,那么对于一个可逆矩阵要求其逆矩阵,则可以有些办法:即对一个矩阵实施一系列的初等变换,将其变为单位矩阵。而同时,在开始的时候,就将所有的这些初等变换作用在一个单位矩阵上。最后,当原矩阵变为单位矩阵的时候,后面的单位矩阵就变成原矩阵的逆了。

    结论: 两个矩阵乘积的秩,不大于任何一个矩阵的秩。特别的,如果有一个矩阵是可逆的,则乘积的秩则等于另一个矩阵的秩。

    结论: 一个矩阵的行空间的维数等于列空间的维数,等于这个矩阵的秩。

    5. 向量空间

    到目前为止,前面的线性方程组的解,还有一些问题没有彻底回答(例如,解空间的描述),在回答这个问题之前,我们需要先了解一下“向量空间”。“向量空间”的严格定义是有些枯燥的,这里暂时把“向量空间”的限制为大家所熟悉的、最为典型的“ \(n \) 维欧氏空间”。

    5.1 基

    要描述一个向量空间中的元素,则首先需要一组基(坐标)。在\(n\)维欧氏空间,最为常见的一组基,即为多个“垂直”(“正交”)的单位向量,即:

    $$ \begin{align}
    \alpha_1 &= (1,0,\dots) \\
    & \vdots \\
    \alpha_i &= (\dots,0,1,0,\dots) \\
    & \vdots \\
    \alpha_n &= (0,\dots,1)
    \end{align}
    $$

    在二维平面空间中,则为:\( \alpha_1 = (1,0) \quad \alpha_2 = (0,1)\);三维空间则为:\( \alpha_1 = (1,0,0) \quad \alpha_2 = (0,1,0) \quad \alpha_3 = (0,0,1)\)。

    既然有“正交”基,那么当然有不那么“正交”的基,而此类“基”则是更为普遍的。事实上,更为普遍的,任何 \( n \) 个线性无关的向量都可以作为向量空间的基。

    5.2 线性相关与线性无关

    考虑一组向量\( \alpha_1,\dots , \alpha_n \),如果当且仅当所有\( a_i = 0 \quad i=1,\dots,n\)时如下的等式才成立:

    $$ a_1\alpha_1 + a_2\alpha_2 + \dots + a_n\alpha_n = 0 $$

    那么,就说 这组向量 \( \alpha_1,\dots , \alpha_n \) 是线性无关的。反之,则称这组向量是线性相关的。

    或者这么说,对于一组线性无关的向量\( \alpha_1,\dots , \alpha_n \):任何一个向量都不能用剩余的向量做“线性表示”。

    5.3 一些重要的结论

    结论:设\( \{ \alpha_1,\dots , \alpha_n \}\)是向量空间\( V \)的一组基,那么\( V \)空间中的每一个向量都可以唯一的表示为这组基的线性组合。这个线性组合的系数,就叫“坐标”(注:相对于这组基)。

    结论:\( W_1 \)、\( W_2 \)是\( V \)的有限子空间,那么有:

    $$ dim(W_1+W_2) = dim(W_1) + dim(W_2) – dim(W_1\cap W_2) $$

    结论:\( n \)维向量空间中,任意\( n \)个线性无关的向量都可以取做基。

    6. 线性变换与矩阵

    6.1 概述

    “线性变换”是指向量空间中一类特殊的映射 \( \sigma : R^n \to R^m \) ,需要满足条件是:

    • \( \sigma(\xi + \eta) = \sigma(\xi) + \sigma(\eta) \)
    • \( \sigma(a\xi) = a\sigma(\xi) \)

    “线性变换” 描述了向量空间之间的映射。后续所有的内容大概都是围绕此而展开,后续所有的内容都会尝试通过各种方式将 “线性变换” 的特性研究清楚。这里写出部分结论,后面再慢慢展开:

    • 线性变换之下,原点保持不变。即 \( \sigma( \vec{0} ) = \vec{0} \)
    • 几何意义下,通常,线性变换包括了:旋转、镜像、拉伸/压缩(特别的,有时候会压缩到零)、剪切

    为了研究清楚一个线性变换上述的特点,通常需要选取一组“基”,然后使用这组“基”的“坐标”来描述空间中的点,进而再描述对应的线性变换。最为常见的基为“正交单位基”。

    从方法上来看,研究清楚“线性变换”最为关键的是研究清楚对应的“变换矩阵”。所以,“线性代数”的核心后面就变成了对矩阵特性的研究,但是,也不要忘记了初衷,否则很快就迷失了。

    6.2 线性变换

    我在大学期间对于线性变换、矩阵有什么作用,是完全没有概念的。所以对于他们的特性研究也没有掌握的很深,基本上是停留在能够把一些联系题做对这个层面。而现在,注意线性变换的广泛应用之后,尝试去理解去本质之后,就会寻根问底的去理解清楚什么是线性变换、什么是矩阵。这里再次说说我的理解。

    在一个向量空间中,最为常见的是 \(n \) 维欧氏空间,会有很多的向量,例如每个 Embedding 就可以理解是在一个线性空间中,“线性变换”表述了是空间中的一类映射,该映射满足上述“小结2.1”中的两个要求,即原点依旧到原点、映射保持所谓的“线性”(例如,向量和的映射等于映射的和等)。

    在一个向量空间中,一个线性变换就是一个符合一定条件的映射。与向量空间的基的选取是没有关系的。自此,与矩阵也是没有关系的。所以,线性变换本身是更为底层、更为基础的概念。

    6.3 欧氏空间的线性变换与矩阵

    现在我们把问题限定在 \(n \) 维欧氏空间中。那么,这时候,我们如何描述一个线性变换呢?是的,就是“基”与“矩阵”。

    通常,\(n \) 维欧氏空间,我们会先选取一组基,然后使用一个矩阵去描述这个线性变换。并且非常幸运的,一旦这组基 选定了,这个矩阵是唯一的。

    结论:在\(n \) 维欧氏空间(这个条件似乎可以去掉)中,对于线性变换 \(\sigma \),如果选定一组“基”,那么就存在唯一的“矩阵”描述该线性变换。

    上述的结论,是比较明显的。我们考虑对于线性变换中的上述选定的基向量 \(\alpha_i,\quad \text{where } i = 1,\ldots,n \),线性变换将其映射到 \(\beta_i,\quad \text{where } i = 1,\ldots,n \),那么根据“基”的基本性质,对于这里的任何 \(\beta_i \)都可以表示成\(\alpha_i \)的线性组合,所有的这些系数构成的矩阵,就是上述描述的唯一的“矩阵”。具体的:

    $$
    \begin{aligned}
    \beta_1 &= a_{11}\alpha_1 + a_{21}\alpha_2 + \cdots + a_{n1}\alpha_n \\[0.5em]
    \beta_2 &= a_{12}\alpha_1 + a_{22}\alpha_2 + \cdots + a_{n2}\alpha_n \\[0.5em]
    &\ \vdots \\[0.5em]
    \beta_n &= a_{1n}\alpha_1 + a_{2n}\alpha_2 + \cdots + a_{nn}\alpha_n
    \end{aligned}
    $$

    即:

    $$
    \begin{aligned}
    \begin{bmatrix}
    \beta_1 & \beta_2 & \cdots & \beta_n
    \end{bmatrix}
    =
    \begin{bmatrix}
    \alpha_1 & \alpha_2 & \cdots & \alpha_n
    \end{bmatrix}
    \begin{bmatrix}
    a_{11} & a_{12} & \cdots & a_{1n} \\
    a_{21} & a_{22} & \cdots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{n1} & a_{n2} & \cdots & a_{nn}
    \end{bmatrix}
    \end{aligned}
    $$

    即在这个线性变换\(\sigma \) 在选定基 \(\alpha_i,\quad \text{where } i = 1,\ldots,n \) 对应的矩阵为:

    $$
    \begin{aligned}
    A =
    \begin{bmatrix}
    a_{11} & a_{12} & \cdots & a_{1n} \\
    a_{21} & a_{22} & \cdots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{n1} & a_{n2} & \cdots & a_{nn}
    \end{bmatrix}
    \end{aligned}
    $$

    从这段简洁的“证明”(或“说明”)来看,我们很自然有如下结论,根据空间中的“基”的选取不同,我们会得到不同的矩阵。因为我们反复会提到,我们经常会通过研究矩阵的特性来研究线性变换。那么,同一个线性变换在不同的“基”下的不同“矩阵”,很自然的能够想到,这些“矩阵”是有某些共性的,是的,我们称这些矩阵为“相似矩阵”,相关特性,暂不展开。

    7. 特征向量与特征值

    7.1 为什么

    为什么我们需要关注“特征值与特征向量”呢?为什么我们要去了解奇异值分解(SVD)呢?

    原因是“线性变换”是一个映射,是非常抽象的。而特征值 、特征向量、SVD分解可以把线性变换最为关键的特性,以非常“直观”的形式表达出来。当然,这里的“直观”并不是简单意义上能够一眼就看出什么来,事实上,“线性变换”本身就有很强的抽象性,这里的“直观”只是相对的,是否直观,完全依赖于各位看客自己的“悟性”了 。

    特征向量的基本定义:如果有 \( \sigma(\xi) = \lambda \xi \) ,那么这里的 \( \xi \) 就是特征向量,对应的 \(\lambda \) 就是对应的特征值。

    要想真正说清楚特征向量与特征值是需要非常多篇幅的,而且关于对特征向量的理解对于理解线性变化也是非常关键的,所以,建议花些时间较为系统的做一些理解。如果你已经建立的基础概念,这里的一篇文章可能是帮助你增强一些理解:特征向量与特征值。

    7.2 关于对特征向量的理解

    完整的讨论特征向量与特征值是复杂的,这里将其限定在一些较为简单的情况,作为一个入门。我们这里考虑最为简单的情况,即对于一个 \(n \times n \)的矩阵,其秩为 \(n \),并且在计算特征值时,有 \(n \) 是不重复的实数解,即没有任何根式重根。如果,恰好 \( n = 2 \)这大概是最为简单的情况了,不过理解这种情况,再进一步拓展,则学习曲线会平滑很多。

    我们来看一个实例,在二维空间中,在标准基下,我们有如下的线性变换矩阵:

    $$
    W = \begin{bmatrix}
    2 & 1 \\
    1 & 2
    \end{bmatrix}
    $$

    根据上述特征向量特征值的定义进行求解,我们可以有如下的特征值与特征向量:

    • \( \lambda_1 = 3 \) 特征向量 \( (1,1) \)
    • \( \lambda_2 = 1 \) 特性向量 \( (-1,1) \)

    从特征向量角度理解线性变换:那么上述的矩阵A对应的线性变换 \(\sigma \) 有如下特性,在这个二维空间任何向量 \(\beta \),都可以分解(投影)为上述两个特征向量方向的向量: \(\beta_1 \,, \beta_2 \),且有:\(\beta = \beta_1 + \beta_2 \)。那么,则有:\(\sigma(\beta) = \lambda_1 \beta_1 + \lambda_2 \beta_2 \)。即,这个线性变换可以这样描述:先将任何向量沿着特征向量方向分解,然后再按照特征值的大小进行拉伸或压缩,然后再把向量合并起来。

    上述的解释,可以对照着右图去理解。特征向量分别为 \((1,1) \) 和 \((-1,1) \) ,即图中浅绿色、浅蓝色方向。该矩阵作用在向量 \((0,1) \) 上,即图中的红色向量。先将红色向量沿着浅绿色、浅蓝色方向分解,然后按照特征值进行拉伸,即图中的绿色、蓝色向量,最后合并为图中的紫色最终向量。

    上述的场景是线性变换中,最为简单的一类。而实际的线性变换,则更为复杂,可能还涉及到对于向量的旋转、镜像、剪切等变换。关于更多场景可以自己探索,或者阅读相关书籍,也可以看看这篇文章中的更多直观的例子:特征值与特征向量

    “特征向量”可以很好的帮助理解“方阵”变换,还有一类变换时非方阵的情况,通常这时候可以借助于奇异值分解的方式去理解,关于奇异值分解可以参考:奇异值分解–深度学习的数学基础

    8. 一些补充说明

    初等的线性代数核心部分大概是这些内容,出于完整性的考虑,可以再进一步了解“Jordan 块”相关的内容,从而把相关理论补充完整,这里不再详述。

    如果再回到最初的线性方程组解的问题,我们这里就可以回答最后一个问题:对于一个线性方程组,如果有解,那么所有的解空间是怎样的?

    结论:如果方程组的系数矩阵的秩为\( r \),那么解空间的维度为\( n-r \)。解空间的“基”则可以通过初等变换求得。这里不再详述。

    9. 再看看前面的问题

    $$
    \begin{aligned}
    \text{Attention Score}_i = XW_i^{QK} X^T \quad \text{where} \,,W_i^{QK} = W_i^Q (W_i^K)^T
    \end{aligned}\tag{1}
    $$

    $$
    \text{Attention Score}_i = Q_iK_i^T = XW_i^Q(XW_i^K)^T \tag{2}
    $$

    在上面的计算(1) 和 计算(2),那么在模型中训练 \(W_i^{QK} \) 和 在模型中训练单独训练 \(W_i^Q \,,W_i^K \) 是否是等价的?

    答案是否定的。

    这里以 GTP2 模型为例,原因在于如果单独训练 \(W_i^{QK} \) ,那么这个矩阵的秩,则很可能是 768 ;而单独训练 \(W_i^Q \,,W_i^K \),这两个矩阵的秩则一定小于 64,这两个矩阵的乘积的秩也一定是 64 (严格来说是小于等于)。所以最终训练获得的效果一定是不同的。当然,哪个更好,这倒不一定,但他们并不是等价的。

    一般意义来说,使用 \(W_i^{QK} \) 可能有着更强的表达能力,只是意义没有那么明确,并且训练的参数要更多。