自然语言处理基础:语言模型BERT

一、语言模型BERT介绍

1、BERT概述

BERT(Bidirectional Encoder Representations from Transformers)是由谷歌在 2018 年提出的一种预训练语言模型。它基于 Transformer 的编码器部分,通过双向预训练的方式来学习文本的上下文表示,能够更好地理解自然语言的语义和语境信息。

在 BERT 出现之前,自然语言处理领域的预训练模型大多采用单向语言模型进行预训练,比如 ELMo 虽然考虑了双向信息,但本质上是分别训练两个单向 LSTM 再拼接,无法真正实现双向理解。这些模型在处理需要结合上下文双向信息的任务时,表现往往不够理想。

随着深度学习的发展,Transformer 模型凭借其自注意力机制在处理序列数据上展现出巨大优势,能够更好地捕捉长距离依赖关系。同时,大规模文本语料的积累也为预训练模型提供了充足的数据支撑,在这样的背景下,BERT 应运而生,旨在通过双向预训练提升模型对自然语言的理解能力。

BERT 的出现是自然语言处理领域的一个重要里程碑。它打破了以往单向预训练的局限,通过双向预训练让模型能够更全面、深入地理解文本语义,极大地提升了多种自然语言处理任务的性能。此后,预训练 + 微调的模式成为自然语言处理的主流范式,推动了该领域的快速发展,为后续一系列更先进的语言模型奠定了基础,激发了研究者们对预训练模型的深入探索和创新。

2、BERT的特点

双向性:BERT 采用双向 Transformer 编码器,能够同时已关注句子中每个词的左侧和右侧上下文信息,从而更准确地理解词在特定语境下的含义。例如,在 “他拿着苹果” 这句话中,“苹果” 可能指水果,而在 “他使用苹果手机” 中,“苹果” 指品牌,BERT 能通过双向上下文准确区分。

预训练与微调结合:先在大规模无标注文本语料上进行预训练,学习通用的语言表示,然后针对具体任务,在少量标注数据上进行微调,使模型能够快速适应不同任务,大大减少了对特定任务标注数据的依赖。

基于 Transformer:充分利用 Transformer 的自注意力机制,能够灵活地捕捉文本中不同位置词之间的依赖关系,无论是短距离还是长距离依赖,都能较好地处理,相比 RNN 等模型在并行计算和长文本处理上更具优势。

3、BERT的功能

文本表示学习:将输入的文本转换为包含丰富语义信息的向量表示,这些向量能够很好地反映文本的上下文含义,为后续的自然语言处理任务提供高质量的输入特征。

支持多种自然语言处理任务:通过微调,BERT 可以应用于文本分类、命名实体识别、问答系统、语义相似度计算、情感分析等多种任务,并在这些任务中取得优异的性能。

4、BERT现状

目前,BERT 仍然是自然语言处理领域中被广泛使用和研究的模型之一。虽然后续出现了 GPT 系列、XLNet、RoBERTa 等更先进的模型,但 BERT 的核心思想和架构对它们产生了深远影响。

在实际应用中,针对不同的场景和需求,研究者们对 BERT 进行了各种改进和优化,比如提出了更小的模型版本(如 DistilBERT)以提高运行效率,或者在特定领域的语料上进行二次预训练,使其更适应特定领域的任务。同时,BERT 在工业界也有大量的应用,为各种自然语言处理产品提供技术支持。

5、BERT未来展望

模型效率提升:随着应用场景的不断扩展,对模型的运行速度和资源消耗提出了更高要求。未来,研究者们可能会进一步优化 BERT 的结构,提出更轻量、高效的版本,使其能够在移动设备等资源受限的环境中更好地应用。

多模态融合:将 BERT 与图像、音频等其他模态的信息进行融合,构建多模态预训练模型,以处理更复杂的多模态任务,比如图文问答、视频内容分析等。

更好的上下文理解能力:虽然 BERT 已经具备一定的上下文理解能力,但在处理超长文本、复杂语义推理等方面仍有提升空间。未来可能会通过改进模型结构或预训练任务,进一步增强其对复杂语境的理解能力。

领域适应性增强:针对不同的专业领域,如医疗、法律、金融等,开发更具针对性的 BERT 变体模型,通过在特定领域的大规模语料上进行深度预训练,提高模型在该领域任务中的性能。

6、BERT应用场景

搜索引擎:帮助搜索引擎更好地理解用户的查询意图和网页内容,提高搜索结果的准确性和相关性。例如,当用户搜索 “苹果的价格” 时,BERT 能区分 “苹果” 是指水果还是手机,从而返回更符合用户需求的结果。

智能问答系统:在客服问答、知识问答等场景中,BERT 能够理解用户的问题,并从海量的知识库中找到准确的答案进行回复,提升问答系统的交互体验。

文本分类:用于新闻分类、垃圾邮件识别、情感分析等任务。比如在情感分析中,BERT 可以分析用户对产品的评价文本,判断其情感倾向是正面、负面还是中性。

命名实体识别:从文本中识别出人名、地名、组织机构名等实体信息,广泛应用于信息抽取、知识图谱构建等领域。例如,从新闻报道中提取出事件涉及的人物、地点等关键信息。

机器翻译:辅助提升机器翻译的质量,使翻译结果更符合目标语言的语法和语义习惯,增强不同语言之间的沟通效果。

二、BERT的双向性

BERT(Bidirectional Encoder Representations from Transformers)的核心创新之一就是它的双向性(Bidirectional),这使得它在理解上下文时比之前的单向模型(如GPT)更强大。BERT的双向性就像阅读理解时先通读全文再答题,而不是边读边猜。这种能力让它更擅长需要全局理解的任务(如问答、语义消歧)。

1、什么是双向性?

传统的语言模型(如GPT)是单向的,即只能从左到右(或从右到左)顺序处理文本。例如,预测句子中的某个词时,只能依赖它左侧的上下文(或右侧),无法同时看到整个句子的信息。

而BERT是双向的:它在处理每个词时,能同时看到左右两侧的全部上下文。这是通过一种叫掩码语言模型(Masked Language Model, MLM)的训练任务实现的。

2、双向性的实现原理

BERT在训练时,会随机遮盖(Mask)句子中的某些词(例如遮盖15%的词),然后让模型根据周围所有词来预测被遮盖的词。例如:

    原始句子:”猫躺在沙发上睡觉”

    遮盖后:”猫躺在[MASK]上睡觉”

模型需要同时利用左侧的”猫躺在”和右侧的”上睡觉”来预测[MASK]的位置可能是”沙发”。这种训练迫使模型学会从双向上下文中理解词义。

3. 例子

假设你是一个猜词游戏的主持人,给朋友以下提示:

    “小明今天[MASK]得很开心,因为他考试得了满分。”

(1) 单向模型(如GPT)的行为:

朋友只能听你从左到右逐字念提示(单向),听到”小明今天”时就要猜[MASK],可能猜”起”或”醒”(因为没有后面的信息)。

(2) BERT的双向行为:

朋友能同时看到整句话的所有字(双向),发现后半句有”考试得了满分”,结合”很开心”,更准确地猜出[MASK]是”笑”或”玩”。

4. 为什么双向性重要?

 一词多义:比如”苹果”在”吃苹果”和”苹果手机”中含义不同,双向上下文能帮助区分。

长距离依赖:比如句子”虽然他昨天淋了雨,但今天[MASK]很高兴”,[MASK](如”仍然”)需要联系开头的”虽然”`。

5、对比单向模型的局限性

以ELMo为例(虽然是双向,但本质是两个单向模型的拼接),例如有个句子:”银行[MASK]的存款”

左到右模型:看到”银行”,可能猜”抢劫”(负面)。

右到左模型:看到”存款”,可能猜”里”(中性)。

BERT:同时看到”银行”和”存款”,更可能猜”里”或”的”。

三、BERT 的预训练与微调的结合

1、概述

BERT 的预训练与微调的结合是 BERT 模型能够在多种自然语言处理任务中表现出色的核心机制,分为预训练和微调两个关键阶段。

(一)预训练阶段

在这个阶段,BERT 会在大规模的无标注文本语料(比如海量的书籍、网页文章等)上进行训练。训练任务主要有两个:一个是 “掩码语言模型(MLM)”,即随机掩盖掉文本中的部分词语,让模型根据上下文预测被掩盖的词语是什么;另一个是 “下一句预测(NSP)”,判断两个句子在语义上是否是连续的。

通过这两个任务,BERT 就像在 “博览群书”,从海量文本中学习到通用的语言知识,包括词语的含义、语法规则、上下文之间的逻辑关系等,形成一个具有强大语言理解能力的基础模型。此时的模型就如同一个掌握了丰富基础知识的 “通才”,能够理解各种通用的语言表达。

(二)微调阶段

当面对具体的自然语言处理任务(如文本分类、命名实体识别、问答等)时,就需要对预训练好的 BERT 模型进行微调。

在微调阶段,会使用该具体任务的标注数据对模型进行训练。此时,模型会在已经掌握的通用语言知识基础上,学习针对该特定任务的规则和模式。微调过程中,模型的大部分参数会保留预训练阶段学到的知识,只通过少量的任务相关数据进行调整,使其能够精准适配当前任务。就像 “通才” 根据具体工作岗位的要求,进行针对性的技能培训,从而成为该岗位的 “专才”。

这种 “预训练 + 微调” 的结合模式,既利用了大规模无标注数据让模型学到扎实的通用语言基础,又通过少量标注数据快速适配具体任务,大大提高了模型在各种任务上的性能和泛化能力。

例子:

我们可以把 BERT 的预训练与微调过程类比为学生的学习和考试准备过程。

预训练阶段就如同学生在整个求学阶段(从小学到高中)的广泛学习。在这个过程中,学生学习语文、数学、英语、物理、历史等各种基础学科知识,了解各种概念、原理和逻辑关系,积累了大量的通用知识,就像 BERT 在海量文本上学习通用语言知识一样。这个阶段的学习不针对某一个具体的考试,而是为了构建一个全面的知识体系。

而微调阶段就好比学生在准备一场特定的考试(比如高考中的语文作文考试)。此时,学生已经具备了扎实的基础知识,在准备作文考试时,会专门针对作文的写作要求、评分标准、常见题型等进行练习。他们会分析优秀范文的结构和写法,进行针对性的写作训练,调整自己的知识运用方式以适应作文考试的需求。这就像 BERT 在具体任务的标注数据上进行微调,让已经掌握通用语言知识的模型,适配具体任务的要求。

通过广泛的基础学习(预训练),学生有了应对各种考试的潜力;再通过针对特定考试的准备(微调),学生就能在该考试中取得好成绩。BERT 也是如此,通过预训练获得强大的通用语言理解能力,再通过微调精准适配具体任务,从而在各种自然语言处理任务中表现出色。

2、BERT预训练

BERT的预训练(Pre-training)是让它通过大量文本数据学习语言通用规律的过程,相当于给模型“上小学和中学”,之后再通过微调(Fine-tuning)适应具体任务(比如“上大学学专业”)。预训练的核心作用是让模型掌握语言的基础能力,比如理解词义、语法、上下文关系等。

3、预训练的作用

BERT预训练主要完成两个任务:

(1) 掩码语言模型(MLM):随机遮盖句子中的词,让模型根据上下文预测被遮住的词(学懂词和上下文的关系)。

(2) 下一句预测(NSP):判断两个句子是否连续(学懂句子间逻辑)。

通过这两个任务,BERT学会了:

(1) 单词在不同上下文中的含义(比如“苹果”是水果还是公司?)。

(2) 语法和语义规则(比如“他踢足球”和“足球踢他”的区别)。

(3) 句子间的逻辑(比如“因为下雨,所以带伞”)。

假设你教一个小朋友学语言:

场景:预训练(打基础)

你给小朋友看很多句子,偶尔挖空让他猜:

句子:“猫喜欢喝[MASK]。”

小朋友通过其他句子(如“猫喝牛奶”“狗喝水”)的规律,猜出[MASK]可能是“牛奶”。

句子:“天空是[MASK]的。”

他可能猜“蓝”或“阴”(取决于其他学过的句子)。

通过大量这样的练习,小朋友学会了:

(1) 常见词的用法(“喝”通常搭配液体)。

(2) 常识(“天空”常是“蓝色”)。

(3) 排除错误选项(“猫喝咖啡”不太可能)。

为什么预训练有效?

数据效率高:预训练时模型从海量文本(如维基百科)中学到通用知识,微调时只需少量标注数据。

解决冷启动问题:如果没有预训练,模型在特定任务(如医疗问答)上需要大量专业标注数据,而BERT通过预训练已经懂了基础语言,微调更快。

任务:判断“苹果”在句子中的含义

    句子1:“他买了一个苹果。”(水果)

    句子2:“苹果发布了新手机。”(公司)

BERT通过预训练学到:

    在句子1中,“买”“一个”暗示“苹果”是水果。

    在句子2中,“发布”“手机”暗示“苹果”是公司。

如果没有预训练,模型可能需要大量标注数据才能区分这两种情况。

4、微调的作用

BERT的微调(Fine-tuning)是指在预训练好的BERT模型基础上,用特定任务的数据对它进行“二次训练”,让它从“通才”变成“专才”。这个过程类似于:

预训练:让BERT读完所有中小学课本,学会通用语言规则(如语法、词义)。

微调:再给它看某个专业的资料(如医学论文、法律条文),让它成为该领域的专家。

 微调的作用:

 (1) 适应具体任务

BERT预训练后虽然懂语言,但不知道具体要解决什么问题(比如情感分析、问答)。微调就是教它“任务该怎么做”。

 (2) 提升精度

用任务相关的数据调整模型参数,让它更精准(比如让医疗问答BERT更熟悉医学术语)。

 (3) 节省资源

不需要从头训练模型(省时间、省算力)。

场景:教BERT当“客服”

(1) 预训练后的BERT

已经懂语言基础,比如知道“退款”“不满意”“故障”等词的含义。

但它不知道具体如何回答客户问题。

(2) 微调过程

你给BERT看大量标注好的客服对话:

    用户问:“订单没收到,怎么办?” → 标准回答:“请提供订单号,我们帮您查询物流。”

    用户问:“产品坏了,能退货吗?” → 标准回答:“可以,请拍照上传凭证。”

BERT通过微调学习到:

   当问题涉及“没收到”时,应该索要“订单号”。

   当问题涉及“退货”时,应该要求“凭证”。

(3) 微调后的效果

用户问:“手机屏幕碎了,能换吗?”

BERT自动回答:“请提供购买凭证和照片,我们将为您处理售后。”(因为它从微调数据中学到了这类问题的处理流程)

四、BERT预训练的例子

(1) 安装依赖

pip install torch transformers

(2) 示例代码

import torch
from transformers import BertTokenizer, BertForMaskedLM

# 1. 加载BERT的tokenizer和小型预训练模型(英文)
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForMaskedLM.from_pretrained('bert-base-uncased')

# 2. 输入句子,并遮盖一个词(用[MASK]标记)
text = “The cat sits on the [MASK].”  # 遮盖”mat”或”bed”等词
inputs = tokenizer(text, return_tensors=”pt”)

# 3. 让BERT预测被遮盖的词
with torch.no_grad():
    outputs = model(**inputs)

# 4. 获取预测结果(最可能的候选词)
predictions = outputs.logits[0, 4]  # 定位到[MASK]的位置(第4个token)
predicted_index = torch.argmax(predictions).item()
predicted_word = tokenizer.convert_ids_to_tokens([predicted_index])[0]

print(f”Original sentence: {text}”)
print(f”Predicted word: {predicted_word}”)

代码解释:

【1】模型和Tokenizer

bert-base-uncased是一个小型BERT模型(区分大小写)。

Tokenizer将句子拆分成BERT能理解的词片段(如sits → [“sit”, “##s”])。

【2】遮盖单词

句子中的[MASK]对应实际单词(如mat),模拟预训练时的遮盖任务。

【3】预测过程

 模型输出每个位置的词概率分布,我们取[MASK]位置最可能的词。

【4】输出结果

 可能输出:Predicted word: mat(或floor、bed等合理词)。

如果想模拟BERT的预训练(而不仅仅是预测),需要以下扩展:

from transformers import DataCollatorForLanguageModeling

# 1. 定义数据(实际预训练需海量文本)
sentences = [“The cat sits on the mat.”, “Dogs love to play fetch.”]

# 2. 对数据分词并动态遮盖(DataCollator自动处理)
data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer,
    mlm=True,
    mlm_probability=0.15  # BERT默认遮盖15%的词
)

# 3. 模拟一个batch(实际训练需循环大量数据)
batch = [tokenizer(sentence) for sentence in sentences]
batch = data_collator(batch)  # 自动随机遮盖词

# 4. 训练步骤(简化版,实际需定义优化器、循环等)
outputs = model(**batch)
loss = outputs.loss  # 通过损失函数优化模型
print(f”Loss: {loss.item()}”)

五、BERT预训练和微调的例子

【1】 预训练(MLM任务)输出

===== 预训练(Masked Language Model)=====
预训练损失(MLM Loss): 2.1043
预测被遮盖的词: tumor

 MLM Loss 越小,说明模型预测被遮盖词的能力越强。

预测被遮盖的词:”The patient has a [MASK] in the lung.” → 预测 “tumor”(合理)。

【2】微调(疾病分类)输出

===== 微调(疾病分类)=====
测试文本: 'The biopsy shows malignant tumor' -> 预测类别: 癌症

import torch
from transformers import (
    BertTokenizer,
    BertForMaskedLM,        # 用于预训练
    BertForSequenceClassification,  # 用于微调
    DataCollatorForLanguageModeling,
    Trainer,
    TrainingArguments,
)

# 设置随机种子(保证实验可复现)
torch.manual_seed(42)

### ——————– 1. 预训练(MLM任务) ——————– ###
print(”
===== 预训练(Masked Language Model)=====”)

# (1) 加载BERT模型和Tokenizer
tokenizer = BertTokenizer.from_pretrained(“bert-base-uncased”)
mlm_model = BertForMaskedLM.from_pretrained(“bert-base-uncased”)

# (2) 模拟3条医疗文本数据(遮盖部分词)
medical_texts = [
    “The patient has a [MASK] in the lung.”,  # 可能填 “tumor” 或 “infection”
    “Take 200mg [MASK] every 6 hours.”,      # 可能填 “aspirin” 或 “ibuprofen”
    “Symptoms include [MASK] and headache.”  # 可能填 “fever” 或 “nausea”
]

# (3) 动态遮盖并计算MLM损失
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm_probability=0.15)
inputs = tokenizer(medical_texts, return_tensors=”pt”, padding=True)
inputs = data_collator([{“input_ids”: inputs[“input_ids”][i]} for i in range(3)])

# (4) 前向传播计算MLM损失
outputs = mlm_model(**inputs)
mlm_loss = outputs.loss
print(f”预训练损失(MLM Loss): {mlm_loss.item():.4f}”)

# (5) 预测被遮盖的词(示例)
masked_index = torch.where(inputs[“input_ids”][0] == tokenizer.mask_token_id)[0][0]
predicted_token_id = torch.argmax(outputs.logits[0, masked_index]).item()
predicted_word = tokenizer.decode(predicted_token_id)
print(f”预测被遮盖的词: {predicted_word}”)

### ——————– 2. 微调(疾病分类任务) ——————– ###
print(”
===== 微调(疾病分类)=====”)

# (1) 准备3条医疗文本 + 分类标签(0: 癌症, 1: 感染, 2: 其他)
texts = [
    “Lung cancer confirmed by biopsy”,  # 癌症 (0)
    “Bacterial infection detected”,     # 感染 (1)
    “Patient has high blood pressure”   # 其他 (2)
]
labels = [0, 1, 2]

# (2) 分词并转换为模型输入
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors=”pt”)
inputs[“labels”] = torch.tensor(labels)

# (3) 加载分类模型(基于BERT)
classifier_model = BertForSequenceClassification.from_pretrained(
    “bert-base-uncased”,
    num_labels=3  # 3个类别
)

# (4) 微调训练(仅1个epoch示意)
training_args = TrainingArguments(
    output_dir=”./output”,
    per_device_train_batch_size=2,
    num_train_epochs=1,
    logging_steps=1,
)

trainer = Trainer(
    model=classifier_model,
    args=training_args,
    train_dataset=torch.utils.data.Dataset.from_dict(inputs),
)

trainer.train()

# (5) 测试分类效果
test_text = “The biopsy shows malignant tumor”
test_input = tokenizer(test_text, return_tensors=”pt”)
with torch.no_grad():
    output = classifier_model(**test_input)
predicted_class = torch.argmax(output.logits).item()
disease_classes = [“癌症”, “感染”, “其他”]
print(f”测试文本: '{test_text}' -> 预测类别: {disease_classes[predicted_class]}”)

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
SweetAugust0601的头像 - 宋马
评论 抢沙发

请登录后发表评论

    暂无评论内容