【模型学习之路】手写+分析bert
手写+分析bert
目录
前言
架构
embeddings
Bertmodel
预训练任务
MLM
NSP
Bert
后话
netron可视化
code2flow可视化
fine tuning
前言
Attention is all you need!
读本文前,建议至少看懂【模型学习之路】手写+分析Transformer-CSDN博客。
毕竟Bert是transformer的变种之一。
架构
embeddings
Bert可以说就是transformer的Encoder,就像训练卷积网络时可以利用现成的网络然后fine tune就投入使用一样,Bert的动机就是训练一种预训练模型,之后根据不同的场景可以做不同的fine tune。
这里我们还是B代表批次(对于Bert,一个Batch可以输入一到两个句子,输入两个句子时,两个直接拼接就好了),m代表一个batch的单词数,n表示词向量的长度。
Bert的输入是三种输入之和(维度设定我们与本系列上一篇文章保持相同):
token_embeddings 和Transformer完全一样。
segment_embeddings 用来标记句子。第一个句子每个单词标0,第二个句子的每个单词标1。
pos_embeddings 用来标记位置,维度和Transformer中的一样,但是Bert的pos_embeddings是训练出来的(这意味它成为了神经网络里要训练的参数了)。
def get_token_and_segments(tokens_a, tokens_b=None):"""bert的输入之一:token embeddingsbert的输入之二:segment embeddingspos_embeddings在后面的模型里面"""tokens = ['<cls>'] + tokens_a + ['<sep>']segments = [0] * (len(tokens_a) + 2)if tokens_b is not None:tokens += tokens_b + ['<sep>']segments += [1] * (len(tokens_b) + 1)return tokens, segments
Bertmodel
Bert的单个EncpderLayer和Transformer是一样的,我们直接把上一节的代码复制过来就好。
组装好。
class BertModel(nn.Module):def __init__(self, vocab, n, d_ff, h, n_layers,max_len=1000, k=768, v=768):super(BertModel, self).__init__()self.token_embeddings = nn.Embedding(vocab, n) # [B, m]->[B, m, vocab]->[B, m, n]self.segment_embeddings = nn.Embedding(2, n) # [B, m]->[B, m, 2]->[B, m, n]self.pos_embeddings = nn.Parameter(torch.randn(1, max_len, n)) # [1, max_len, n]self.layers = nn.ModuleList([EncoderLayer(n, h, k, v, d_ff)for _ in range(n_layers)])def forward(self, tokens, segments, m): # m是句子长度X = self.token_embeddings(tokens) + \self.segment_embeddings(segments)X += self.pos_embeddings[:, :X.shape[1], :]for layer in self.layers:X, attn = layer(X)return X
简单测试一下。
# 弄一点数据测试一下tokens = torch.randint(0, 100, (2, 10)) # [B, m]segments = torch.randint(0, 2, (2, 10)) # [B, m]m = 10bert = BertModel(100, 768, 3072, 12, 12)out = bert(tokens, segments, m)print(out.shape) # [2, 10, 768]
预训练任务
Bert在训练时要做两种训练,这里先画个图表示架构,后面给出分析和代码。
MLM
Maked language model,是指在训练的时候随即从输入预料上mask掉一些单词,然后通过的上下文预测该单词,该任务非常像我们在中学时期经常做的完形填空。
在BERT的实验中,15%的WordPiece Token会被随机Mask掉。在训练模型时,一个句子会被多次喂到模型中用于参数学习,但是Google并没有在每次都mask掉这些单词,而是在确定要Mask掉的单词之后,80%的时候会直接替换为[Mask],10%的时候将其替换为其它任意单词,10%的时候会保留原始Token。(这里就不深入了)
class MLM(nn.Module):def __init__(self, vocab, n, mlm_hid):super(MLM, self).__init__()self.mlp = nn.Sequential(nn.Linear(n, mlm_hid),nn.ReLU(),nn.LayerNorm(mlm_hid),nn.Linear(mlm_hid, vocab))def forward(self, X, P):# X: [B, m, n]# P: [B, p]# 这里P指的是记录了要mask的元素的矩阵,若P(i,j)==k,表示X(i,k)被mask了p = P.shape[1]P = P.reshape(-1)batch_size = X.shape[0]batch_idx = torch.arange(batch_size)batch_idx = torch.repeat_interleave(batch_idx, p)X = X[batch_idx, P].reshape(batch_size, p, -1) # [B, p, n]out = self.mlp(X)return out
这里的forward的逻辑有点麻烦,要读懂的话可以要手推一下。p是每一个Batch中mask的词的个数。(即在一个Batch中,m个词挑出了p个)
NSP
Next Sentence Prediction的任务是判断句子B是否是句子A的下文。训练数据的生成方式是从平行语料中随机抽取的连续两句话,其中50%保留抽取的两句话,它们符合IsNext关系,另外50%的第二句话是随机从预料中提取的,它们的关系是NotNext的。这个关系由每个句子的第一个token——<cls>捕捉。
class NSP(nn.Module):def __init__(self, n, nsp_hid):super(NSP, self).__init__()self.mlp = nn.Sequential(nn.Linear(n, nsp_hid),nn.Tanh(),nn.Linear(nsp_hid, 2))def forward(self, X):# X: [B, m, n]X = X[:, 0, :] # [B, n]out = self.mlp(X) # [B, 2]return out
Bert
下面拼装Bert。
class Bert(nn.Module):def __init__(self, vocab, n, d_ff, h, n_layers,max_len=1000, k=768, v=768, mlm_feat=768, nsp_feat=768):super(Bert, self).__init__()self.encoder = BertModel(vocab, n, d_ff, h, n_layers, max_len, k, v)self.mlm = MLM(vocab, n, mlm_feat)self.nsp = NSP(n, nsp_feat)def forward(self, tokens, segments, m, P=None):X = self.encoder(tokens, segments, m)mlm_out = self.mlm(X, P) if P is not None else Nonensp_out = self.nsp(X)return X, mlm_out, nsp_out
后话
netron可视化
利用netron可视化。
test_tokens = torch.randint(0, 100, (2, 10)) # [B, m]
test_segments = torch.randint(0, 2, (2, 10)) # [B, m]
test_P = torch.tensor([[1, 2, 4, 6, 8], [1, 3, 4, 5, 6]])
test_m = 10
test_bert = Bert(100, 768, 3072, 12, 12)
test_X, test_mlm_out, test_nsp_out = test_bert(test_tokens, test_segments, test_m, test_P)modelData = "./demo.pth"
torch.onnx.export(test_bert, (test_tokens, test_segments), modelData)
netron.start(modelData)
截取部分看一下。
code2flow可视化
code2flow可以可视化代码函数和类的相互调用关系。
code2flow.code2flow([r'代码路径.py'], '输出路径.svg')
这里生成的png,其实svg清晰得多。
fine tuning
Bert的精髓在于,Bert只是一个编码器(Encoder),经过MLM和NSP两个任务的训练之后,可以自己在它的基础上训练一个Decoder来输出特定的值、得到特定的效果。这也是Bert的神奇和魅力所在!通过两个任务训练出一个编码器,然后可以通过不同的Decoder达到各种效果!
持续探索Bert......
相关文章:

【模型学习之路】手写+分析bert
手写分析bert 目录 前言 架构 embeddings Bertmodel 预训练任务 MLM NSP Bert 后话 netron可视化 code2flow可视化 fine tuning 前言 Attention is all you need! 读本文前,建议至少看懂【模型学习之路】手写分析Transformer-CSDN博客。 毕竟Bert是tr…...
Redis学习文档(常见面试题)
目录 Redis回收使用的是什么算法? Redis如何做大量数据插入? 为什么要做Redis分区? 你知道有哪些Redis分区实现方案? Redis分区有什么缺点? Redis持久化数据和缓存怎么做扩容? 分布式Redis是前期做还…...
【C++刷题】力扣-#594-最长和谐子序列
题目描述 和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 给你一个整数数组 nums ,请你在所有可能的子序列中找到最长的和谐子序列的长度。 数组的 子序列是一个由数组派生出来的序列,它可以通过删除一些元素或不删除元素、且不改变…...

MoveIt 控制自己的真实机械臂【2】——编写 action server 端代码
完成了 MoveIt 这边 action client 的基本配置,MoveIt 理论上可以将规划好的 trajectory 以 action 的形式发布出来了,浅浅尝试一下,在 terminal 中运行 roslaunch xmate7_moveit_config_new demo.launch 报错提示他在等待 xmate_arm_control…...
C#制作学生管理系统
定义学生类 定义一个简单的类来表示学生,包括学号、姓名、性别、年龄、电话、地址。再给其添加一个方法利于后续添加方法查看学生信息。 //定义学生类 public class student {public int ID { get; set; }//开放读写权限public string Name { get; set; }public i…...

python Pandas合并(单元格、sheet、excel )
安装 Pandas 和 openpyxl 首先,确保已经安装了 Pandas 和 openpyxl。可以通过 pip 安装: pip install pandas openpyxl 创建 DataFrame import pandas as pd # 创建 DataFrame df1 pd.DataFrame({ 姓名: [张三, 李四, 王五], 年龄: [25, 30, 35]…...

OJ在线编程常见输入输出练习【JavaScript】
(注:本文是对【JavaScript Node 】 ACM模式,常见输入输出练习相关内容的介绍!!!) 牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 一、ACM模式下的编辑页面 二、ACM模式下&a…...
新能源汽车空调系统:绿色出行的舒适保障
在新能源汽车迅速发展的今天,空调系统作为提升驾乘舒适度的重要组成部分,发挥着不可或缺的作用。新能源汽车空调系统主要由压缩机、冷凝器、节流装置和蒸发器四大件组成,它们协同工作,为车内提供适宜的温度和湿度环境。 一、压缩…...

Date工具类详细汇总-Date日期相关方法
# 1024程序员节 | 征文 # 目录 简介 Date工具类单元测试 Date工具类 简介 本文章是个人总结实际工作中常用到的Date工具类,主要包含Java-jdk8以下版本的Date相关使用方法,可以方便的在工作中灵活的应用,在个人工作期间频繁使用这些时间的格…...

TMUX1308PWR规格书 数据手册 具有注入电流控制功能的 5V 双向 8:1单通道和 4:1 双通道多路复用器芯片
TMUX1308 和 TMUX1309 为通用互补金属氧化物半导体 (CMOS) 多路复用器 (MUX)。TMUX1308 是 8:1单通道(单端)多路复用器,而 TMUX1309 是 4:1 双通道(差分)多路复用器。这些器件可在源极 (Sx) 和漏极 (Dx) 引脚上支持从 …...

证件照怎么换底色?简单又快速!不看后悔
一、引言 证件照在我们的生活中有着广泛的应用,无论是求职、考试还是办理各种证件,都需要用到不同底色的证件照。传统的换底色方法往往比较复杂,需要一定的专业技能和软件操作经验。但是现在,有了更简单快捷的方法,让你…...
Rust 基础语法与常用特性
Rust 跨界:全面掌握跨平台应用开发 第一章:快速上手 Rust 1.2 基础语法与常用特性 1.2.1 数据类型与控制流 数据类型 Rust 提供了丰富的内置数据类型,主要分为标量类型和复合类型。 标量类型 标量类型表示单一的值,Rust 中…...

一、开发环境的搭建
环境搭建步骤: 下载软件安装软件运行软件 其他: Visual studio 安装包文件:https://www.alipan.com/s/nd5RgzD4e3b 下载软件 在浏览器中搜索Visual studio,选择如图的选项 点击该区域,进入该页面,【或…...

Docker:存储原理
Docker:存储原理 镜像联合文件系统overlay镜像存储结构容器存储结构 存储卷绑定挂载存储卷结构 镜像 联合文件系统 联合文件系统Union File System是一种分层,轻量且高效的文件系统。其将整个文件系统分为多个层,层与层之间进行覆盖&#x…...

ts:数组的常用方法(push、pop、shift、unshift、splice、slice)
前端css中filter的使用 一、主要内容说明二、例子(一)、push方法(尾添加)1.源码1 (push方法)2.源码1运行效果 (二)、pop方法(尾删除)1.源码2(pop方…...
物联网网关确保设备安全
物联网(IoT)网关在确保设备安全方面扮演着至关重要的角色。 作为连接物联网设备和云端或企业系统的中介,物联网网关可以实施多种安全措施来保护设备和数据。 是物联网网关确保设备安全的关键方法: 1. 设备认证和授权 认证&…...

Vue学习笔记(五)
Class绑定 数据绑定的一个常见需求场景式操纵元素的CSS class列表,因为class是attribute,我们可以和其他attribute一样使用v-bind将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此…...
Nestjs返回格式小结
在 NestJS 中,除了 text/event-stream(用于 Server-Sent Events)之外,还有多种格式的返回方式,具体取决于你的应用需求。以下是一些常见的返回格式及其示例: 1. JSON 格式 Get(json) getJsonResponse(Res…...

【力扣刷题实战】相同的树
大家好,我是小卡皮巴拉 文章目录 目录 力扣题目: 相同的树 题目描述 示例 1: 示例 2: 示例 3: 解题思路 题目理解 算法选择 具体思路 解题要点 完整代码(C语言) 兄弟们共勉 &#…...

Golang | Leetcode Golang题解之第515题在每个树行中找最大值
题目: 题解: func largestValues(root *TreeNode) (ans []int) {if root nil {return}q : []*TreeNode{root}for len(q) > 0 {maxVal : math.MinInt32tmp : qq nilfor _, node : range tmp {maxVal max(maxVal, node.Val)if node.Left ! nil {q …...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...