0%

Transformer 架构三剑客:Encoder、Decoder 与 Encoder-Decoder 的区别与应用

在深度学习自然语言处理(NLP)领域,Transformer 无疑是皇冠上的明珠。但Transformer 并不是只有一种形态

根据任务需求的不同,它演化出了三个各具特色的分支。今天我们来拆解 Transformer 家族的三大主力:仅编码器(Encoder-only)仅解码器(Decoder-only)编解码器(Encoder-Decoder)

一句话总结

  • Encoder-only 是“阅读理解大师”;
  • Decoder-only 是“即兴演讲天才”;
  • Encoder-Decoder 则是“同声传译专家”。

一、架构总览:一张图看懂三剑客

为了直观展示它们的区别,我们先看一张架构图:

graph TB
    subgraph "仅编码器 (Encoder-only)"
        E_IN[输入序列] --> E_ENC["编码器堆叠
(双向关注)"] E_ENC --> E_OUT[特征向量/分类结果] end subgraph "仅解码器 (Decoder-only)" D_IN[输入序列] --> D_DEC["解码器堆叠
(因果掩码/单向)"] D_DEC --> D_OUT[输出序列] end subgraph "编解码器 (Encoder-Decoder)" ED_IN[源序列] --> ED_ENC[编码器] ED_ENC --> ED_CROSS[交叉注意力] ED_DEC_IN[目标序列] --> ED_DEC[解码器] ED_CROSS --> ED_DEC ED_DEC --> ED_OUT[目标序列] end
架构 核心公式 训练方式
Encoder-only H=Encoder(X)H=Encoder(X) 掩码语言模型(MLM)
Decoder-only P(yt∥y<t,X)P(y**ty<t,X) 自回归语言模型(CLM)
Encoder-Decoder H=Encoder(X);Y=Decoder(H,Y<t)H=Encoder(X);Y=Decoder(H,Y<t) 去噪自编码器 / Seq2Seq

核心特点

  • 双向注意力机制(Bidirectional Attention):这是它的杀手锏。在处理句子中的某个词时,它能同时看到左边和右边的所有词。就像你阅读时,会结合上下文来理解一个多义词的含义。
  • 擅长“读”而非“写”:它的强项在于把一段文字压缩成高质量的向量表示(Embedding),从中提取特征。它是天生的“分析型”选手。

典型应用

阅读全文 »

知识蒸馏:让小模型拥有大模型的“智慧”

在人工智能飞速发展的今天,我们见证了参数量动辄百亿、千亿的“巨无霸”模型诞生。它们极其聪明,但代价是高昂的计算成本和漫长的推理延迟。如果想在手机或智能手表上运行这些大模型,显然不切实际。

有没有一种方法,既能保留大模型的强大能力,又能享受小模型的轻量与高效?

答案就是:知识蒸馏(Knowledge Distillation)


一、什么是知识蒸馏?

简单来说,知识蒸馏是一种模型压缩和加速技术。它的核心思想非常符合人类的教育模式——师生学习(Teacher-Student Learning)

在这个框架中:

角色 含义 特点
教师模型 庞大、复杂、高性能的大模型 不需要部署到终端,唯一任务是输出高质量预测
学生模型 体量小、结构简单的轻量模型 目标是模仿老师的行为,用更少参数达到接近的性能

一个生动的比喻

传统训练像让学生直接翻阅整座图书馆的藏书来寻找规律;而知识蒸馏则是让一位学识渊博的老教授,把自己消化理解后的精华笔记传授给学生。学生通过模仿教授的解题思路,以极低的成本掌握同样的知识。


二、为什么要做知识蒸馏?

痛点 说明
部署成本高 GPT-3 级别的模型需要多张 GPU 才能运行,无法部署到手机、智能手表等边缘设备
推理速度慢 大模型生成一个字可能需要几秒钟,实时性差
能耗大 一次推理可能消耗相当于几小时手机使用的电量

知识蒸馏的核心价值:在保持较高精度的前提下,大幅压缩模型体积和推理成本


三、知识蒸馏的完整流程

graph LR
    subgraph "阶段一:训练教师模型"
        A[海量数据] --> B[大模型训练]
        B --> C["教师模型
(精度高,体积大)"] end subgraph "阶段二:知识蒸馏训练" D["同一份数据
(可无标签)"] --> C D --> E["学生模型
(体积小)"] C -->|软标签
概率分布| F[蒸馏损失
KL散度] E -->|软预测| F D -->|硬标签| G[学生损失
交叉熵] E -->|硬预测| G F --> H[总损失] G --> H H -->|反向传播| E end subgraph "阶段三:部署" E --> I[推理部署
手机/边缘设备] end

四、核心概念详解:温度与软标签

这是知识蒸馏最精妙的部分,也是理解它的关键。

阅读全文 »

大模型第一课:手写推导线性回归的“灵魂”

在深度学习和人工智能的宏大叙事中,Transformer、大语言模型动辄拥有千亿参数,令人望而生畏。但如果我们剥去这些复杂的外衣,会发现一切神经网络的基石,其实都源于一个最简单的模型——线性回归

今天这篇博客,我们不谈复杂的框架代码,而是回归数学本质。我们将通过一份手写的学习笔记,彻底搞懂机器是如何通过“试错”来学会预测的。

核心观点:理解线性回归,就是掌握了深度学习的“第一性原理”。


一、模型的诞生:给数据画一条直线

想象一下,你是一名房产中介,手头有一堆历史成交数据(房子的面积和对应的价格)。你的目标是找到一条规律,能最好地“穿过”这些数据点,从而根据新房子的面积来预测它的价格。

这就是线性回归要解决的问题:拟合

为了实现这个目标,我们需要定义两个核心要素:

  • 输入值 (x):喂给模型的特征数据(例如:房子面积)。
  • 输出值 (z):模型给出的预测结果(例如:预测房价)。

为了让 x 变成 z,我们需要两个可调节的参数——这也是模型真正要去“学习”的东西:

  1. 权重 ($w$, Weight):决定输入 x 对结果的影响程度。在几何上,它是直线的斜率
  2. 偏置 ($b$, Bias):决定当 x=0 时的基础值。在几何上,它是直线的截距

于是,我们得到了机器学习界最经典的公式:

💡 笔记划重点:这个简单的线性公式,就是所有神经网络神经元计算的雏形。


二、怎么才算“学得好”?引入 Loss 函数

模型给出了预测值 z,但怎么知道它准不准呢?我们需要一个“裁判”来打分。这个裁判就是损失函数(Loss Function)

同时,我们需要一个参照标准——目标值 (Y, Ground Truth),也就是真实答案(例如:房子的实际成交价)。

笔记中使用了最直观的均方误差(MSE)思想。为了简化推导,我们只看单个样本的误差平方:

阅读全文 »

从静态词向量到动态理解:NLP 预训练模型的完整进化图谱

在自然语言处理(NLP)的学习过程中,理清模型的演进路线至关重要。这不仅仅是算法的更迭,更是我们对“如何让机器理解语言”这一认知不断深化的过程。回顾过去几年,我们经历了一场从简单的词嵌入到大规模预训练模型的技术革命。

今天,我们就来梳理一下这条从 Word2Vec 到 ELMo,再到 BERT 的进阶之路,看看它们是如何一步步改变我们对“语言表示”的理解的。


一、起点:Word2Vec 与静态词向量

一切的起点是从 Word2Vec 开始的。

维度 说明
核心思想 将离散的词语映射为连续的向量空间(Embedding)
核心贡献 解决了计算机无法直接处理文本的问题,并且捕捉到了词语之间的语义关系。比如:King - Man + Woman ≈ Queen
致命局限 它是静态的。无论上下文如何变化,“苹果”这个词的向量永远是一样的,无法区分是水果还是科技公司。

这种“一词多义”的困境,严重限制了模型对上下文的理解能力。


二、序列建模:RNN 及其变体

为了让机器理解句子的顺序和语境,我们引入了循环神经网络(RNN)。

原理:RNN 具有“记忆”功能,当前的输出不仅取决于当前的输入,还取决于上一时刻的状态。这使得它能够处理变长的序列数据。

痛点:标准的 RNN 存在严重的梯度消失/梯度爆炸问题,导致它很难记住长句子开头的信息(即长距离依赖问题)。

改进:为了解决这个问题,LSTM(长短期记忆网络)和 GRU(门控循环单元)应运而生。它们通过引入“门控机制”,有效地控制了信息的遗忘和保留,成为了当时处理序列任务的主流选择。

模型 核心创新 解决的问题
RNN 循环结构,具有“记忆” 处理变长序列
LSTM 遗忘门、输入门、输出门 长距离依赖(梯度消失)
GRU 更新门、重置门(LSTM 的简化版) 长距离依赖,参数更少

三、架构升级:Seq2Seq 模型

有了强大的 RNN/LSTM 作为基础组件,Seq2Seq(Sequence-to-Sequence)架构诞生了。

结构:它由两个 RNN 组成——编码器(Encoder)解码器(Decoder)

工作流

阅读全文 »

Python 多进程:8 核 CPU 只有 1 个在跑?那是 GIL 在”捣乱”

写了一个计算密集型的程序,跑起来 CPU 占用 100%,但仔细一看——8 个核心只有 1 个在满负荷运转,剩下 7 个几乎闲置。

这不是程序写得不对,是 Python 的 GIL(全局解释器锁) 在”从中作梗”。GIL 规定:同一时刻,只有一个线程能执行 Python 代码。所以多线程在计算任务上,形同虚设。

多进程是绕过 GIL 的方案——每个进程有自己独立的 Python 解释器,各自跑各自的,操作系统把它们调度到不同的 CPU 核心上,真正实现了并行。

什么场景用多进程? CPU 密集型——大量计算、加密解密、图像处理、数据压缩。这种任务”算”的时间多,”等”的时间少,多线程没用,多进程才是答案。

先看证据:多线程 vs 多进程,差距一目了然

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import multiprocessing
import threading
import time
from concurrent.futures import ThreadPoolExecutor

def cpu_work(n):
"""纯计算,不干别的"""
count = 0
for i in range(2, n):
is_prime = all(i % j != 0 for j in range(2, int(i**0.5) + 1))
if is_prime:
count += 1
return count

# 单线程
start = time.time()
cpu_work(100000)
print(f"单线程: {time.time() - start:.2f}s")

# 多线程(4个)
start = time.time()
with ThreadPoolExecutor(4) as ex:
[cpu_work(100000) for _ in range(4)]
print(f"多线程: {time.time() - start:.2f}s") # 比单线程还慢!

# 多进程(4个)
start = time.time()
with multiprocessing.Pool(4) as pool:
pool.map(cpu_work, [100000] * 4)
print(f"多进程: {time.time() - start:.2f}s") # 约单线程的 1/4

典型结果:

1
2
3
单线程: 2.35s
多线程: 2.78s ← 不仅没快,还慢了
多进程: 0.68s ← 接近 4 倍提速

多线程在计算任务上不仅无效,线程切换还带来额外开销。多进程才是真正的并行。

多进程池:别反复创建进程,用池复用

每次创建进程都要分配内存、启动解释器,开销不小。实际开发用进程池——预先创建一批进程,任务来了分配,干完了回收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import multiprocessing

def worker(n):
return n ** 2

if __name__ == "__main__":
# 默认进程数 = CPU 核心数
with multiprocessing.Pool() as pool:
# map:批量提交,保持顺序
results = pool.map(worker, range(100))

# apply_async:异步提交单个任务
async_result = pool.apply_async(worker, (10,))
result = async_result.get() # 获取结果(会阻塞)

进程数设多少?

任务类型 建议进程数
CPU 密集型 CPU 核心数
I/O 密集型(但不想用异步) CPU 核心数 × 2
内存密集型 CPU 核心数 ÷ 2

设多了没用,操作系统频繁切换进程反而拖慢速度。

阅读全文 »