【Bert】自然语言(Language Model)入门之---Bert

every blog every motto: Although the world is full of suffering, it is full also of the overcoming of it

0. 前言

对bert进行梳理

论文: BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
时间: 2018.10.11
作者: Jacob Devlin, Ming-Wei Chang, Kenton Lee, Kristina Toutanova

1. 正文

1.1 整体理解

Transformer的第一版时2017.6.12

bert(用到Transformer的Encoder)的第一版arxiv上的文章时间时2018.10.11

GPT1(用到Transformer的Decoder)在arxiv上没找到对应的文章,但是第一版的bert中就有把GPT1作为参考,所以GPT1的时间应该是在2018.10.11之前

动作不得不说快阿!!!
20240727155313


下图展示了三种模型的不同

bert: 双向(Transformer Encoder)

GPT1:从左到右单向(Transformer Decoder)

ELMo:单独训练从左到右从右到左,再结合(bert双向也是借鉴于此,ELMo基础单元是LSTM,这是一个比较早的东东了)

20240727155503

具体来说,bert使用Transformer的encoder部分作为基础单元进行堆叠,而GPT使用decoder部分作为基础单元进行堆叠。

20240727161515

Bert有两个版本,一个是base (12层),一个是large(24层),base的参数量是110M,large的参数量是340M。
base的作用是为了和GPT1作对比。

base:
L:12; H:768; A:12

large:
L:24; H:1024; A:16

说明: 编码器层数L,注意力头数A,隐藏层数H.

20240727165712

1.2 和GPT1的对比

和GPT1相比的话,主要有两点不同,一个是bert是双向,另一个是预训练。

其中GPT1预训练,是预测一个句子的下一个词是什么(这个在NLP中我们也称作Language Modeling(LM)),如下:
20240727174708


而bert的预训练是以下两个:

1.2.1 任务一:“完型填空”

不同于常规思路预测下一词。

上面说了bert是双向的,如果预测下一个词,那将是没有意义。所以对输入的词进行mask,即遮住,然后让模型去预测遮住的词是什么。(是不是和我们做的完形填空一样!!!),论文中将这个称为:“masked LM” (MLM)

如下,将hairy进行Mask以后去预测:

my dog is hairy → my dog is [MASK]

然后对网络的输出结果相应位置进行softmax,得到每个词的概率分布,然后取概率最大的词作为预测结果。如下图:

20240731153330

但是存在一个问题,mask15%比例比较高,这会造成某些词在微调(fine-tuning)时候没有见过,此外,微调的时候是没有mask的,为了让预训练和微调匹配,做了一些调整。

每一个句子会预测15%token,在这其中,

  • 80%的token被替换成[MASK], my dog is hairy → my dog is [MASK]
  • 10%的token被替换成随机词, my dog is hairy → my dog is apple
  • 10%的token保持不变, my dog is hairy → my dog is hairy

20240727180536

1.2.2 任务二:预测下一个句子

在NLP中的某些任务当中,需要将两个句子作为输入(如,问答系统),所以bert中的预训练添加了一个的新的训练方式----Next Sentence Prediction,下一个句子预测。

具体的是一次输入两个句子,最后有一个输入,判断是否相似。如下图:

其中, 50%的输入数据B是A的下一个句子,50%的数据B是从语料库中随机选取的。
20240728155338

1.2.3 小结

现在我们看下面这个图应该比较好理解了。

在pre-training阶段,输出的第一位是用于判断是否是下一个句子(NSP,任务二,二分类)后续输出是做
完型填空(MLM,任务一,多分类)。

20240730144910


关于输入,需要注意的是,输入的是一个序列(sequence),一个sequence可能是一个句子(sentence)也可能是两个句子(sentence,为了适应下游的问题任务)。

而一个句子setence,更准确是一段连续的文本,不是我们常规的“句子”。

20240730150924

1.3 小结

除了论文中提到的base和large,github上还有其他版本。

  • BERT-tiny, L = 2 , H = 128 L=2,H=128L=2,H=128
  • BERT-mini, L = 4 , H = 256 L=4,H=256L=4,H=256
  • BERT-small, L = 4 , H = 512 L=4,H=512L=4,H=512
  • BERT-medium, L = 8 , H = 512 L=8,H=512L=8,H=512

20240730153820

主要贡献:

  • 引入了Masked LM,使用双向LM做模型预训练。
  • 为预训练引入了新目标NSP,它可以学习句子与句子间的关系。
  • 进一步验证了更大的模型效果更好: 12 --> 24 层。
  • 为下游任务引入了很通用的求解框架,不再为任务做模型定制。
  • 刷新了多项NLP任务的记录,引爆了NLP无监督预训练技术。

1.4 关于输入

bert的是输入是一个序列(sequence,包含多个句子(sentence)),而网络的最小处理单元是一个词,就是token。关于bert中具体的分词方式我们暂时按下不表。

我们先看一个例子。 若我们一个序列是:

Sentence A: Paris is a beautiful city. 
Sentence B: I love Paris.

1.4.1 token

先将句子进行分词,转换成一个个token以后,如下:

[CLS] Paris is a beautiful city . [SEP] I love Paris . [SEP]

其中,

  • [CLS]放在序列第一个位置,用于分类(NSP,下一个句子预测)
  • [SEP]放在每个句子(sentence)结尾,用于区分句子和句子。

20240731141555

1.4.2 segment

由于我们一次会输入两个句子(sentence),所以需要区分是句子A还是句子B,所以bert中引入了segment,用于区分句子A和句子B。

  • 句子A的segment id为0
  • 句子B的segment id为1

20240731141725

1.4.3 position

由于bert的输入是一个序列,而序列的长度是有限的,所以需要将序列进行截断,而截断以后,我们无法知道每个词在句子中的位置,所以bert中引入了position,用于表示每个词在句子中的位置。

20240731141816

1.4.4 最终的输入

最终的输入是将上面的token、segment和position相加

20240731141929

1.5 分词:WordPiece

bert中的分词采用的是WorPiece,是Google在2016年提出的,它将词拆分成更小的子词,比如,将“unhappiness”拆分成“un”和“-happy”,这样就可以避免OOV问题。

具体做法:检查单词是否在词表(vocabulary)中,如果在则标记;否则,拆分成子词,

对子词继续重复前面的过程(然后检查子词是否在词表中,如果在则标记;否则,继续拆分,直到拆分出来的子词在词表中。)

Bert的词表有30k标记。

比如:

"Let us start pretraining the model."

其中pretraining不在词表中,所以会被拆分成pre##train##ing
前面的#表示这个单词为一个子词,并且它前面有其他单词。现在我们检查子词##train和##ing是否出现在词表中。因为它们正好在词表中,所以我们不需要继续拆分。

所以上述句子会被拆分成:

tokens = [let, us, start, pre, ##train, ##ing, the, model]

增加[CLS]和[SEP]后是:

tokens = [ [CLS], let, us, start, pre, ##train, ##ing, the model, [SEP] ]

1.6 预处理代码

我们的原始数据是文本,而所谓的神经网络训练本质是对数字进行数学运算。

所以我们需要将文本转换为数字,而转换的过程就是预处理。下面我们看下代码

1.6.1 步骤

本次使用的是抱脸的transformers库

pip install transformers
1. 导入库

导入库,加载预训练的模型和分词器。

from transformers import BertModel, BertTokenizer
import torch
model = BertModel.from_pretrained('bert-base-uncased')
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

离线情况下

model_path = './model_path'
bert = BertModel.from_pretrained(pretrained_model_name_or_path=model_path)

将下图中需要的文件下载到本地即可
20240731170325

2. 分词
sentence = 'I love Paris'
tokens = tokenizer.tokenize(sentence)
print(tokens)

20240731172120

3. 添加CLS、SEP
tokens = ['[CLS]'] + tokens + ['[SEP]']
print(tokens)

20240731172404

4. 添加pad

正常的bert的输入是个固定长度,如果长度超过这个固定长度进行截断,小于该固定长度添加pad。
假设固定长度是7,现在我们的tokens长度位5,所以需要添加pad

tokens = tokens + ['[PAD]'] + ['[PAD]']
tokens

20240731172416

5. mask

bert中的encoder内部是注意力机制,我们需要传入一个mask,用于区分正常词和pad。

attention_mask = [1 if i!= '[PAD]' else 0 for i in tokens]
attention_mask

20240731172622

6. 转为id

不管是中文还是英文句子都是字符,而神经网络是对数字进行训练。所以需要将字符转化为数字。
不管是中文还是英文句子都是字符,而神经网络是对数字进行训练。所以需要将字符转化为数字。
不管是中文还是英文句子都是字符,而神经网络是对数字进行训练。所以需要将字符转化为数字。

token_ids = tokenizer.convert_tokens_to_ids(tokens)
token_ids

20240731172816

本质是从一个大的字典里面找到每次词对应的id。

20240731173125

7. 转为tensor
import torch
token_ids = torch.tensor(token_ids).unsqueeze(0)
attention_mask = torch.tensor(attention_mask).unsqueeze(0)print(token_ids.shape)
print(token_ids)

我们输入是一个句子,每个句子的长度是7。
20240731173434

8. 输入模型
hidden_rep, cls_head = bert(token_ids, attention_mask=attention_mask,return_dict=False)print(hidden_rep.shape,cls_head.shape)

hidden_rep : 是bert中最后一个encoder的输出,维度是[1,7,768]
cls_head : 是cls的输出,维度是[1,768]

对于hidden_rep,1表示一个1个句子,7表示句子的长度,768表示每个词的向量维度 (一个词用一个长度为768的向量表示)

20240731173758

1.6.2 小结

我们处理的是句子,而所谓的神经网络训练本质是对数字进行加减乘除运算。所以实际输入网络的是数字。

原始的是文本,输入网络的是经过字典映射的数字。

20240801102417

1.7 关于embedding

如果看论文,会发现bert的输入是embedding,而我们上面的预处理最终的结果好像是token_ids(只是索引而已),这二者有什么关系呢?
20240801113833

在说embedding之前,我们先看下one-hot编码。

1.7.1 one-hot编码

one-hot编码是机器学习中最常用的编码方式,对于每个词,我们用长度为n的向量表示,其中n是词表的大小,向量中只有一个1,其余都是0。

比如中文有5000个词,为了方便我们简化一下,现在词典里面有5个词。[‘我’,‘是’,‘中’,‘国’,‘人’]。

'我们人’可以用如下向量表示:
我:[1 0 0 0 0 ]
是:[0 1 0 0 0 ]
人:[0 0 0 0 1 ]

看起来也比较直观,但是别忘了我们这里词典大小是5,如果5000呢?那么这个词的向量就是5000维的,如果50000呢?50000维的向量,是不是有点太大了?

这会导致我们的结果非常的稀疏!

其次,one-hot编码之间的向量是正交的,词和词之间没有关系,比如’我’和’是’之间没有关系,'中’和’国’之间也没有关系,这显然是不合理的。

所以就出现了embedding

1.7.2 embedding

embedding是一个词典,更通俗的说一个二维向量。

我们的embedding现在是(5000,768),5000表示词表大小,768表示每个词的向量维度。

啥意思?就是我们的词表里面有5000个词,每个词用一个长度为768的向量表示。

现在我们要表示,只需要根据这个词对应的索引,在5000个词中找到对应的向量即可。而这个向量是一个长度为768的向量。

768相比之前的5000小了不少。同时词和词和词之间也有有关系的。

1.7.3 代码示例

构建一个含有10个词的词表,每个词用一个长度为3的向量表示。

import torch
import torch.nn as nn# 创建 Embedding 层
num_embeddings = 10  # 词汇表大小
embedding_dim = 3    # 嵌入向量的维度
embedding_layer = nn.Embedding(num_embeddings, embedding_dim)
embedding_layer

20240801115321

我们看下词表里面的值是个啥

embedding_layer.weight

20240801115454

现在我们有词索引如下:

# 示例输入
input_indices = torch.LongTensor([1, 2, 3, 4])
print('input.shape: ',input_indices.shape)
print("Input indices:", input_indices)

20240801115154

现在我们根据对应的词到词表中查找我们的词对应的向量。

# 获取嵌入向量
output_vectors = embedding_layer(input_indices)
print('output.shape: ',output_vectors.shape)
print("Output vectors:", output_vectors)

20240801115625

这个值是从词表中来的。
20240801115726

1.7.4 bert官方部分代码

20240801120709

1.7.5 小结

embedding正式表述是词表,或是或是词典。更本质来说是一个二维向量。

通过“查表”我们获得了每一个词的向量表示。这样的表示相比one-hot编码更稠密。同时,也能表达词和词之间的关系。

开始是我们的embedding参数是随机的,通过不断的训练,含义更加准确。

1.8 小结

bert 借鉴了GPT1和ELMo,使用Transformer的encoder部分进行堆叠。

两种预训练(MLM和NSP)能够更有效的获取语义信息。

参考

  1. https://cloud.tencent.com/developer/article/2058413
  2. https://blog.csdn.net/jiaowoshouzi/article/details/89073944
  3. https://blog.csdn.net/yjw123456/article/details/120211601
  4. https://blog.csdn.net/weixin_42029738/article/details/139578563
  5. https://helloai.blog.csdn.net/article/details/120211601
  6. https://www.cnblogs.com/JuggyZhan/p/18249075
  7. https://cloud.tencent.com/developer/article/2348457
  8. https://cloud.tencent.com/developer/article/2336439
  9. https://blog.csdn.net/magicyangjay111/article/details/132665098
  10. https://www.cnblogs.com/zackstang/p/15387549.html
  11. https://blog.csdn.net/yjw123456/article/details/120232707
  12. https://people.ee.duke.edu/~lcarin/Dixin2.22.2019.pdf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/21144.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于MATLAB的均匀面阵MUSIC算法DOA估计仿真

基于MATLAB的均匀面阵MUSIC算法DOA估计仿真 文章目录 前言一、二维MUSIC算法原理二、二维MUSIC算法MATLAB仿真三、MATLAB源代码总结 前言 \;\;\;\;\; 在波达角估计算法中,MUSIC 算法与ESPRIT算法属于特征结构子空间算法,是波达角估计算法中的基石。在前面…

Linux 命令

含义: Linux号称万物皆文件 cd 切换目录 .. 当前目录的上一级目录 ~波浪线,当前用户的home目录,比如root用户home目录是/root cd .. :进入上一级目录 pwd:查看当前位置 查看命令 ls:列出目录内容,包括参数-l&…

一周学会Flask3 Python Web开发-Debug模式开启

锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 默认情况,项目开发是普通模式,也就是你修改了代码,必须重启项目,新代码才生效&…

在VS-qt的程序中,后期增加PCH预编译功能,提高编译速度

由于前期创建qt程序的时候未勾选pch功能,导致没有启动预编译的功能. 这种情况下需要增加pch功能应该怎么做? 在项目中增加2个文件 stdafx.h和stdafx.cpp文件 stdafx.h增加qt常用头文件 #pragma once //windows #include <windows.h>//qt常用 #include <QObject&g…

天翼云910B部署DeepSeek蒸馏70B LLaMA模型实践总结

一、项目背景与目标 本文记录在天翼云昇腾910B服务器上部署DeepSeek 70B模型的全过程。该模型是基于LLaMA架构的知识蒸馏版本&#xff0c;模型大小约132GB。 1.1 硬件环境 - 服务器配置&#xff1a;天翼云910B服务器 - NPU&#xff1a;8昇腾910B (每卡64GB显存) - 系统内存&…

Python 语法及入门 (超全超详细) 专为Python零基础 一篇博客让你完全掌握Python语法

前言&#xff1a; 本篇博客超级详细&#xff0c;请尽量使用电脑端结合目录阅读 阅读时请打开右侧 “只看目录” 方便阅读 一、什么是Python 1.1 Python的诞生 1989年&#xff0c;为了打发圣诞节假期&#xff0c;Gudio van Rossum吉多 范罗苏姆&#xff08;龟叔&#xff09;决…

【架构】分层架构 (Layered Architecture)

一、分层模型基础理论 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0365cf0bfa754229bdedca6b472bffc7.png 1. 核心定义 分层架构(Layered Architecture)模型是一种常见的软件设计架构,它将软件系统按照功能划分为不同的层次,每个层次都有特定的职责和功能…

2024年国赛高教杯数学建模C题农作物的种植策略解题全过程文档及程序

2024年国赛高教杯数学建模 C题 农作物的种植策略 原题再现 根据乡村的实际情况&#xff0c;充分利用有限的耕地资源&#xff0c;因地制宜&#xff0c;发展有机种植产业&#xff0c;对乡村经济的可持续发展具有重要的现实意义。选择适宜的农作物&#xff0c;优化种植策略&…

捷米特 JM - RTU - TCP 网关应用 F - net 协议转 Modbus TCP 实现电脑控制流量计

一、项目背景 在某工业生产园区的供水系统中&#xff0c;为了精确监测和控制各个生产环节的用水流量&#xff0c;需要对分布在不同区域的多个流量计进行集中管理。这些流量计原本采用 F - net 协议进行数据传输&#xff0c;但园区的监控系统基于 Modbus TCP 协议进行数据交互&…

遥感影像目标检测:从CNN(Faster-RCNN)到Transformer(DETR)

我国高分辨率对地观测系统重大专项已全面启动&#xff0c;高空间、高光谱、高时间分辨率和宽地面覆盖于一体的全球天空地一体化立体对地观测网逐步形成&#xff0c;将成为保障国家安全的基础性和战略性资源。未来10年全球每天获取的观测数据将超过10PB&#xff0c;遥感大数据时…

iOS事件传递和响应

背景 对于身处中小公司且业务不怎么复杂的程序员来说&#xff0c;很多技术不常用&#xff0c;你可能看过很多遍也都大致了解&#xff0c;但是实际让你讲&#xff0c;不一定讲的清楚。你可能说&#xff0c;我以独当一面&#xff0c;应对自如了&#xff0c;但是技术的知识甚多&a…

【核心算法篇十三】《DeepSeek自监督学习:图像补全预训练方案》

引言:为什么自监督学习成为AI新宠? 在传统监督学习需要海量标注数据的困境下,自监督学习(Self-Supervised Learning)凭借无需人工标注的特性异军突起。想象一下,如果AI能像人类一样通过观察世界自我学习——这正是DeepSeek图像补全方案的技术哲学。根据,自监督学习通过…

轻松搭建本地大语言模型(二)Open-WebUI安装与使用

文章目录 前置条件目标一、安装 Open-WebUI使用 Docker 部署 二、使用 Open-WebUI&#xff08;一&#xff09;访问Open-WebUI&#xff08;二&#xff09;注册账号&#xff08;三&#xff09;模型选择&#xff08;四&#xff09;交互 四、常见问题&#xff08;一&#xff09;容器…

零基础学QT、C++(一)安装QT

目录 如何快速学习QT、C呢&#xff1f; 一、编译器、项目构建工具 1、编译器&#xff08;介绍2款&#xff09; 2、项目构建工具 二、安装QT 1、下载QT安装包 2、运行安装包 3、运行QT creator 4、导入开源项目 总结 闲谈 如何快速学习QT、C呢&#xff1f; 那就是项目驱动法&…

vue取消全选功能按钮注意事项

这里这个功能是通过各种条件查出数据,但只取一条数据进行后续业务,虽然每一条数据前面都有多选框,但只需要选一个,所以在业务上分析可以把这个全选按钮取消掉 这里不是简单的把多选组件的selection-change"handleSelectionChange"和handleSelectionChange方法去掉,因…

【再读】2501.12948/DeepSeek-R1通过强化学习提升大型语言模型(LLMs)的推理能力

DeepSeek-R1-Zero展示了在没有监督数据的情况下&#xff0c;通过RL可以发展出强大的推理能力。DeepSeek-R1通过引入冷启动数据和多阶段训练&#xff0c;进一步提升了推理性能&#xff0c;达到了与OpenAI-o1-1217相当的水平。此外&#xff0c;通过蒸馏技术&#xff0c;将DeepSee…

校园网架构设计与部署实战

一、学习目标 掌握校园网分层架构设计原则 理解多业务VLAN规划方法 学会部署认证计费系统 实现基础网络安全防护 二、典型校园网场景 需求分析&#xff1a;某中学需建设新型校园网络 覆盖教学楼/宿舍/图书馆三区域 区分教师/学生/访客网络权限 满足2000终端并发接入 …

leetcode:942. 增减字符串匹配(python3解法)

难度&#xff1a;简单 由范围 [0,n] 内所有整数组成的 n 1 个整数的排列序列可以表示为长度为 n 的字符串 s &#xff0c;其中: 如果 perm[i] < perm[i 1] &#xff0c;那么 s[i] I 如果 perm[i] > perm[i 1] &#xff0c;那么 s[i] D 给定一个字符串 s &#xff0…

数仓搭建(hive):DWS层(服务数据层)

DWS层示例: 搭建日主题宽表 需求 维度 步骤 在hive中建数据库dws >>建表 CREATE DATABASE if NOT EXISTS DWS; 建表sql CREATE TABLE yp_dws.dws_sale_daycount( --维度 city_id string COMMENT 城市id, city_name string COMMENT 城市name, trade_area_id string COMME…

网工项目实践2.8 IPv6设计及网络优化需求分析及方案制定

本专栏持续更新&#xff0c;整一个专栏为一个大型复杂网络工程项目。阅读本文章之前务必先看《本专栏必读》。 全网拓扑展示 一.IPV6部署规划 在北京总部&#xff0c;为了迎接未来网络的发展&#xff0c;规划在BJ_G2、BJ_G3、BJ_C1、BJ_C2之间运行IPv6协议&#xff0c;以建立I…