BERT 模型详解:架构、原理与实战
引言
科学突破很少发生在真空中,它们往往是建立在积累的人类知识之上的阶梯。要了解当前大型语言模型(LLM)的成功,我们需要回到过去并回顾 BERT。BERT 由 Google 研究人员于 2018 年开发,是首批基于 Transformer 架构的大型语言模型之一。凭借其惊人的结果,它迅速成为自然语言处理(NLP)任务中无处不在的基线,包括一般语言理解、问答和命名实体识别。
可以说,BERT 为我们如今目睹的生成式 AI 革命铺平了道路。尽管 BERT 是最早的 LLM 之一,但它仍然被广泛使用,有数千个开源、免费和预训练的 BERT 模型可用于特定用例,例如情感分析、临床笔记分析和有害评论检测。
本文将深入探讨 BERT 的架构、内部工作原理、实际应用及其局限性,并提供实战代码示例。
什么是 BERT?
BERT(Bidirectional Encoder Representations from Transformers)是 Google 于 2018 年发布的开源模型。这是一项雄心勃勃的实验,旨在测试所谓的 Transformers(一种创新的神经架构,由谷歌研究人员在 2017 年的著名论文《Attention Is All You Need》中提出)在自然语言处理任务上的性能。
BERT 成功的关键在于其变压器架构。在 Transformer 出现之前,对自然语言进行建模是一项非常具有挑战性的任务。尽管复杂的神经网络(即递归神经网络 RNN 或卷积神经网络 CNN)兴起,但结果仅部分成功。
主要的挑战在于神经网络用于预测句子中缺失单词的机制。当时,最先进的神经网络依赖于编码器 - 解码器架构,这是一种功能强大但耗时耗资源的机制,不适合并行计算。考虑到这些挑战,谷歌研究人员开发了转换器,这是一种基于注意力机制的创新神经架构。
BERT 的核心架构与工作原理
双向编码能力
递归神经网络和卷积神经网络通常使用顺序计算来生成预测。也就是说,一旦在大型数据集上进行训练,它们可以预测哪个单词将跟随给定的单词序列。从这个意义上说,它们被认为是单向或上下文无关的算法。
相比之下,像 BERT 这样基于 Transformer 驱动的模型是双向的,因为它们根据前一个单词和后一个单词来预测单词。这是通过自注意力机制实现的,自注意力机制是编码器中都包含的一层。注意力层的目标是捕获输入句子中不同单词之间存在的上下文关系。
预训练与微调
Transformer 在庞大的数据语料库上从头开始训练,遵循一个耗时且昂贵的过程(只有包括 Google 在内的少数公司才能负担得起)。
就 BERT 而言,它在维基百科(约 25 亿字)和 Google BooksCorpus(约 8 亿字)上进行了为期四天的预训练。这使得该模型不仅可以获得英语知识,还可以获得来自世界各地的许多其他语言的知识。
为了优化训练过程,谷歌开发了新的硬件,即所谓的 TPU(张量处理单元),专为机器学习任务而设计。
为了避免训练过程中不必要且成本高昂的交互,谷歌研究人员使用迁移学习技术将(预)训练阶段与微调阶段分开。这允许开发人员选择预训练模型,细化目标任务的输入输出对数据,并使用特定于领域的数据重新训练预训练模型的头部。这个特性使像 BERT 这样的 LLM 成为建立在它们之上的无穷无尽的应用程序的基础模型。
掩码语言建模(MLM)
在 BERT(以及每个基于 transformers 的 LLM)中实现双向学习的关键因素是注意力机制。此机制基于掩码语言建模(MLM)。通过屏蔽句子中的单词,此技术迫使模型在句子两个方向上分析剩余的单词,以增加预测被屏蔽单词的机会。MLM 基于在计算机视觉领域已经尝试过的技术,非常适合需要对整个序列有良好的上下文理解的任务。
BERT 是第一个应用这种技术的 LLM。特别是,随机 15% 的标记化单词在训练期间被屏蔽。结果表明,BERT 能够较准确地预测隐藏词。
此外,BERT 还引入了下一个句子预测(NSP)任务,用于判断两个句子是否连续,这有助于理解句子间的逻辑关系。
分词策略:WordPiece
BERT 使用 WordPiece 分词方法,这是一种子词分词技术。它将单词拆分为更小的片段,从而有效处理词汇表之外的单词。这种方法平衡了词汇覆盖率和计算效率,使得模型能够更好地处理罕见词和拼写错误。
实战:如何使用 BERT
在实际开发中,我们通常使用 Hugging Face 的 transformers 库来加载和使用 BERT 模型。以下是一个简单的 Python 代码示例,展示如何加载预训练模型并进行推理。
from transformers import BertTokenizer, BertModel
import torch
# 1. 加载预训练的分词器和模型
tokenizer = BertTokenizer.from_pretrained()
model = BertModel.from_pretrained()
text =
inputs = tokenizer(text, return_tensors=, truncation=, padding=)
torch.no_grad():
outputs = model(**inputs)
last_hidden_state = outputs.last_hidden_state
(last_hidden_state.shape)


