赞
踩
目录
目录
4.1 MLM(Masked Language Model)
4.2 NSP(Next Sentence Prediction)
BERT的全称为Bidirectional Encoder Representation from Transformers,是一个预训练的语言 表征模型,它强调不再像以往一样采用传统的单向语言模型或者把两个单向语言模型进行浅层拼接的方法进行预训练。它旨在通过在所有层中对左右上下文进行联合调节,采用新的masked language model(MLM),用于语言理解的深度双向转换器的预训练,从未标记的文本中预训练深度双向表示。因此,只需一个额外的输出层即可对预训练的 BERT 模型进行微调,从而为各种任务(例如问答和语言推理)创建较为先进的模型,而无需对特定于任务的架构进行大量修改。
· BERT在 11 项自然语言处理任务上获得了最先进的新结果,包括将 GLUE 分数提高到 80.5% (绝对提高 7.7%),将 MultiNLI 准确率提高到 86.7%(绝对提高 4.6%),将 SQuAD v1.1 问答测试 F1 提高到 93.2(绝对提高 1.5 分),将 SQuAD v2.0 测试 F1 提高到 83.1(绝对提高5.1 分)。
· BERT的网络架构主要使用的是《Attention is all you need》中提出的多层Transformer结构,Transformer结构在NLP领域中已经得到了广泛应用,其最大的特点是抛弃了传统的RNN和CNN。通过Self-Attention机制将任意位置的两个单词的距离进行特定转换,有效的解决了NLP 中棘手的长期依赖问题。
BERT利用MLM进行预训练并且采用深层的双向Transformer组件进行构建模型,总体结构是将多个Transformer Encoder一层一层地堆叠起来。在论文中,作者分别用12层和24层Transformer Encoder组装出两套BERT模型,两套模型的参数总计分别为110M和340M。(Transformer模型详解(图解最完整版) - 知乎 (zhihu.com))
图2.1 Transformer整体结构
隐藏Transformer详细结构,表示如下:
图2.2 Transformer黑箱图
Transformer结构进行堆叠,形成更深的神经网络(可理解为将Transformer encoder进行堆叠):
图2.3 多层堆叠Transformer
经过多层Transformer结构的堆叠后,形成BERT的主体结构(可视化:大雄007):
图2.4 Bert整体结构
图2.5 BERT的总体预训练和微调程序
图3.1 token表征的组成
输入向量由三层组合:
3.1.Token Embeddings 即词向量。
· 要将各个词转换成固定维度的向量。中文目前尚未对输入文本进行分词,而是直接对单子构成为本的输入单位。特别的,英文词汇会做更细粒度的切分,比如上图中的playing 切割成 play 和 ##ing;将词切割成更细粒度的 Word Piece 是为了解决未登录词的常见方法。
· [CLS] 表示开始标志,同时[CLS]表示该特征用于分类模型,对非分类模型,该符号可以省去。[SEP]表示分句符号,用于断开输入语料中的多个句子。
· Bert 在处理英文文本时只需 30522 个词,Token Embeddings 层会将每个词转换成 768 维向量,例如:‘I like dog’ ,3个Token 会被转换成一个 (5, 768) 的矩阵或 (1, 5, 768) 的张量。
3.2. Segment Embeddings 段落向量。
· BERT 能够处理对输入句子对的分类任务,这类任务就像判断两个文本是否语义相似。句子对中的两个句子被简单的拼接在一起后送入到模型中,BERT通过segment embeddings去区分一个句子对中的两个句子,因为预训练不单单做LM,还得做以两个句子为输入的分类任务。
· Segement Embeddings 层有两种向量表示,前一个向量是把 0 赋值给第一个句子的各个 Token,后一个向量是把1赋值给各个 Token,问答系统等任务要预测下一句,因此输入是有关联的句子。而文本分类只有一个句子,那么 Segement embeddings 就全部是 0。
3.3. Position Embeddings 位置向量。
· 一般认为,Transformers无法编码输入的序列的顺序性,加入position embeddings会让BERT理解不同位置的words,Bert的位置向量不使用三角函数,而是需要进行训练学习。
· 例如:出现在文本不同位置的字/词所携带的语义信息存在差异(如 ”你爱爸爸“ 和 ”爸爸爱你“),你、爸爸虽然都和爱字很接近,但是位置不同,表示的含义不同。
· BERT 中处理的最长序列是 512 个 Token,长度超过 512 会被截取,BERT 在各个位置上学习一个向量来表示序列顺序的信息编码进来,这意味着 Position Embeddings 实际上是一个 (512, 768) 的 lookup 表,表第一行是代表第一个序列的每个位置,第二行代表序列第二个位置。
最后,BERT 模型将 Token Embeddings (1, n, 768) + Segment Embeddings(1, n, 768) + Position Embeddings(1, n, 768) 求和的方式得到一个 Embedding(1, n, 768) 作为模型的输入。
· 预训练的概念在CV(Computer Vision,计算机视觉)中的应用十分广泛,CV中采用的预训练任务一般是ImageNet图像分类任务,完成图像分类任务的前提是必须能抽取出良好的图像特征,而ImageNet数据集有规模大、质量高的优点,因此常常能够获得很好的效果。
· 尽管NLP领域没有类似于ImageNet这样质量高的人工标注数据,但是可以利用大规模文本数据的自监督性质来构建预训练任务。因此,BERT模型的预训练(Pre-training)任务是由两个自监督任务组成,即MLM(Masked Language Model)和NSP(Next Sentence Prediction)。
· 对于预训练语料库,我们使用BooksCorpus(8亿个单词)(Zhu et al.,2015)和英语维基百科(2500万个单词)。对于维基百科,我们只提取文本段落并忽略列表、表和标头。为了提取长连续序列,使用文档级语料库而不是打乱的句子级语料库(如Billion Word Benchmark(Chelba et al.,2013))是至关重要的
· MLM是BERT能够不受单向语言模型所限制的原因,简单来说就是用mask token([MASK])随机地以15%的概率对每一个训练序列中的token进行替换,然后预测出[MASK]位置原有的单词,这种实验类似于我们高中时期做过的完形填空。
· 在BERT的实验中,训练模型时,一个句子会被多次喂到模型中用于参数学习,但是Google并没有在每次都mask掉这些单词,而是在确定要Mask掉的单词之后,做以下处理:
1)80%是[MASK]。如,my dog is cute——>my dog is [MASK]
2)10%是随机的其他token。如,my dog is cute——>my dog is apple
3)10%是原来的token。如,my dog is cute——>my dog is cute
再用该位置对应的 去预测出原来的token(输入到全连接,然后用softmax输出每个token的概率,最后用交叉熵计算loss)。该策略令到BERT不再只对[MASK]敏感,而是对所有的token都敏感,以致能抽取出任何token的表征信息。
· 基于以上思考:如果句子中的某个Token 100%都会被mask掉,那么在fine-tuning的时候模型就会有一些没有见过的单词。加入随机Token的原因是因为Transformer要保持对每个输入token的分布式表征,否则模型就会记住这个[mask]是某个出现的具体token。至于单词带来的负面影响,因为一个单词被随机替换掉的概率只有15%*10% =1.5%,这个负面影响其实是可以忽略不计的。 文章也指出每次只预测15%的单词,因此模型收敛的比较慢。
论文中关于该策略的实验数据:
图4.1 Ablation over different masking strategies
· 一些任务如问答、自然语言推断等需要理解两个句子之间的关系,而MLM任务倾向于抽取token层次的表征,因此不能直接获取句子层次的表征。为了使模型能够有能力理解句子间的关系,BERT使用了NSP任务来预训练,简单来说就是预测两个句子是否连在一起。具体的做法是:我们在语料库中挑选出句子A和句子B来组成一个训练样例,50%的概率句子B就是句子A的下一句(是句子A的下文则标注为IsNext),剩下50%的概率句子B是语料库中的随机句子(不是句子A的下文则标注为NotNext)。之后把训练样例输入到BERT模型中,用[CLS]对应的C信息去进行二分类的预测。训练样例如下:
Input1=[CLS] He likes the [MASK] tree [SEP] the tree can bear [MASK] fruit [SEP]
Label1=IsNext
Input2=[CLS] He likes the [MASK] tree [SEP] penguin [MASK] are flight ##less birds [SEP]
Label2=NotNext
之后把每一个训练样例输入到BERT中可以相应获得两个任务对应的loss,再把这两个loss加在一起就是整体的预训练loss。我们明显看到,这两个任务所需的数据均可以从无标签的文本数据中构建,这是自监督性质的体现,比CV中需要人工标注的ImageNet数据集简单多了。
· NSP任务可能并不是必要的,消除NSP损失在下游任务的性能上能够与原始BERT持平或略有提高。针对“以单句子为单位输入,模型无法学习到词之间的远程依赖关系”,后续的RoBERTa、ALBERT、spanBERT都移去了NSP任务。
图5.1 BERT在不同任务上的微调
图5.1展示了BERT在不同任务上的微调情况。我们的特定任务模型是通过将BERT与一个额外的输出层结合而形成的,因此只需要从头开始学习极少量的参数。在这些任务中,(a)和(b)是序列级任务,而(c)和(d)是令牌级任务。在图中,E代表输入嵌入,Ti代表令牌i的上下文表示,[CLS]是用于分类输出的特殊符号,[SEP]是用于分隔非连续令牌序列的特殊符号。
· 微调是直接的,因为Transformer中的自注意力机制允许BERT通过交换适当的输入和输出来建模许多下游任务——无论它们涉及单文本还是文本对。对于涉及文本对的应用,一种常见的模式是在应用双向交叉注意力之前独立地对文本对进行编码,如Parikh等(2016);Seo等(2017)。BERT使用自注意力机制来统一这两个阶段,因为使用自注意力对连接的文本对进行编码实际上包括两个句子之间的双向交叉注意力。对于每个任务,我们只需将特定于任务的输入和输出插入到BERT中,并进行端到端的微调。在输入方面,预训练中的句子A和句子B类似于(1)释义中的句子对,(2)蕴含中的假设前提对,(3)问答中的问题段落对,以及(4)文本分类或序列标记中的退化文本对。在输出方面,将令牌表示形式输入到输出层以进行令牌级任务,如序列标记或问答,并将[CLS]表示形式输入到输出层以进行分类,如蕴含或情感分析。与预训练相比,微调相对便宜。使用相同的预训练模型,本文中的所有结果都可以在单个Cloud TPU上最多复制1小时,或在GPU上复制几个小时。
· 对于微调,大多数模型超参数与预训练时相同,但批处理大小、学习率和训练周期数除外。dropout概率始终保持在0.1。最佳超参数值是特定于任务的,但我们发现以下范围的可能值在所有任务中表现良好:
我们还观察到,大型数据集(例如,100k+标记的训练示例)对超参数选择的敏感度远低于小型数据集。微调通常非常快,因此合理地运行上述参数的穷举搜索并选择在开发集上表现最好的模型是合理的。
· 在海量的语料上训练完BERT之后,便可以将其应用到NLP的各个任务中了。 微调(Fine-Tuning)可实现的任务包括:基于句子对的分类任务,基于单个句子的分类任务,问答任务,命名实体识别等:
· 基于句子对的分类任务:QNLI:用于判断文本是否包含问题的答案,类似于我们做阅读理解定位问题所在的段落。STS-B:预测两个句子的相似性,包括5个级别。MRPC:也是判断两个句子是否是等价的。RTE:类似于MNLI,但是只是对蕴含关系的二分类判断,而且数据集更小。SWAG:从四个句子中选择为可能为前句下文的那个。
· 基于单个句子的分类任务:SST-2:电影评价的情感分析。CoLA:句子语义判断,是否是可接受的(Acceptable)。
· 问答任务:SQuAD v1.1:给定一个句子(通常是一个问题)和一段描述文本,输出这个问题的答案,类似于做阅读理解的简答题。
· 命名实体识别:CoNLL-2003 NER:判断一个句子中的单词是不是Person,Organization(组织),Location(位置),Miscellaneous(杂项)或者other(无命名实体)。
图6.1 预训练模型架构的差异
· 上图所示,Trm代表的是Transformer层,E代表的是Token Embedding,即输入Token映射成的向量,T代表的是模型输出的每个Token的特征向量表示。
· 在这里,我们研究了最近流行的表示学习模型的差异,包括ELMo、OpenAI GPT和BERT。BERT使用双向Transformer,OpenAI GPT 使用从左到右的 Transformer,ELMo 使用独立训练的从左到右和从右到左的 LSTM 的串联来为下游任务生成特征。在这三者中,只有 BERT 表示在所有层中同时以左上下文和右上下文为条件。除了架构差异之外,BERT 和 OpenAI GPT 是微调方法,而 ELMo 是一种基于功能的方法。
· 与BERT最具可比性的现有预训练方法是OpenAI GPT,它在大型文本编辑器上训练从左到右的Transformer LM。事实上,BERT中的许多设计决策都是有意使其尽可能接近GPT,以便将这两种方法进行最小程度的比较。这项工作的核心论点是,模型的双向性和两个预训练任务占了经验改进的大部分,但我们确实注意到,BERT和GPT的训练方式还有其他几个差异:
•GPT是在BooksCorpus上训练的(800M字);BERT在BooksCor pus(8亿字)和维基百科(2.5亿字)上接受培训。
•GPT使用仅在微调时引入的句子分隔符([SEP])和分类器标记([CLS];BERT在预训练期间学习[SEP]、[CLS]和句子A/B嵌入。
•GPT被训练了1M个步骤,批量大小为32000个单词;BERT接受了1Msteps的训练,批量大小为128000个单词。
•GPT对所有微调实验使用5e-5的相同学习率;BERT选择特定任务的微调学习率,该学习率在开发集上表现最好。
下面我们展示如何使用 Hugging Face 的 Transformers 库对文本数据进行预处理,并使用 BERT 模型进行情感分类任务。我们将逐步解析这段代码,并确保其各个部分都清晰明了
1.确保安装了必要的库:我们使用 Hugging Face的BertTokenizer、 BertModel模型和Pytorch等;BertTokenizer将输入的评论语句转化为输入Bert模型的向量信息,BertModel根据输入信息输出结果。(私信领取 requirements.txt 文件)。
- import pandas as pd
- from datasets import Dataset, DatasetDict
- from transformers import BertTokenizer, BertForSequenceClassification, Trainer, TrainingArguments, DataCollatorWithPadding
- from sklearn.model_selection import train_test_split
这里导入了 pandas 用于数据处理,datasets 用于数据集管理,transformers 用于加载和训练 BERT 模型,sklearn 用于数据集的分割。
- # 加载TXT文件并处理数据
- label_map = {
- "anger": 0,
- "disgust": 1,
- 'sadness': 2,
- "happiness": 3,
- 'like': 4
- }
-
- data = []
- with open("./simplifyweibo_5_moods.txt", "r", encoding="utf-8") as file:
- for line in file:
- label, text = line.strip().split(":", 1) # 只分割第一个冒号
- data.append({"text": text.strip(), "label": label_map[label.strip()]})
这里将 simplifyweibo_5_moods.txt
文件中的数据按行读取,并将每行的数据按第一个冒号分割为标签和文本。然后,将标签映射为数值,并将数据存储在一个字典列表中。
- # 转换为DataFrame
- data_df = pd.DataFrame(data)
-
- # 分割数据集为训练集和测试集
- train_df, test_df = train_test_split(data_df, test_size=0.2, random_state=42)
将数据转换为 pandas DataFrame 格式,并使用 train_test_split
将数据集分割为训练集和测试集,比例为 80% 训练集和 20% 测试集。
- # 转换为datasets格式
- train_dataset = Dataset.from_pandas(train_df)
- test_dataset = Dataset.from_pandas(test_df)
- dataset = DatasetDict({"train": train_dataset, "test": test_dataset})
将 pandas DataFrame 转换为 datasets 格式,以便与 Transformers 库兼容。
- # 加载BERT分词器和模型
- tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
- model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=len(label_map))
加载预训练的 BERT 分词器和模型,并指定模型的输出标签数为情感标签的数量
- # 预处理数据集
- def preprocess_function(examples):
- return tokenizer(examples['text'], truncation=True, padding=True, max_length=512)
-
- encoded_dataset = dataset.map(preprocess_function, batched=True)
定义一个预处理函数,将文本数据分词并进行截断和填充。然后使用 map
方法对数据集进行批量预处理。
- # 使用 DataCollatorWithPadding 处理数据填充
- data_collator = DataCollatorWithPadding(tokenizer)
创建一个数据填充器,以确保批处理时输入序列的长度一致。
- # 设置训练参数
- training_args = TrainingArguments(
- output_dir='./results',
- evaluation_strategy="epoch",
- learning_rate=2e-5,
- per_device_train_batch_size=8,
- per_device_eval_batch_size=8,
- num_train_epochs=10,
- weight_decay=0.01,
- )
定义训练参数,包括输出目录、评估策略、学习率、批处理大小、训练轮数和权重衰减。
- # 创建Trainer实例
- trainer = Trainer(
- model=model,
- args=training_args,
- train_dataset=encoded_dataset["train"],
- eval_dataset=encoded_dataset["test"],
- data_collator=data_collator,
- tokenizer=tokenizer
- )
创建一个 Trainer
实例,用于管理训练过程。
- # 训练模型
- trainer.train()
-
- # 保存模型和分词器
- model.save_pretrained('./sentiment_model')
- tokenizer.save_pretrained('./sentiment_model')
开始训练模型,并在训练完成后保存模型和分词器。
该过程是使用 Hugging Face 的 Transformers 库进行模型训练的输出日志。进度条:
- ”Map: 100%“ 和 ”Map: 100%“表示数据预处理的进度条,显示数据预处理已经完成。
- “0%”表示训练过程的进度条,当前显示训练刚刚开始。
我们进行文本的测试:
- # 示例文本
- texts = ["I am very happy today!", "This is so sad."]
- predictions = predict(texts)
- print(predictions)
得到结果:
参考文献:
1.《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》
特别鸣谢:
知乎网:大雄007、Jeffery
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。