参考B站尚硅谷bert论文链接李哥深度学习概述BERTBidirectional Encoder Representations from Transformers是由 Google 于 2018 年提出的一种语言预训练模型。其核心创新在于采用 Transformer 的编码器Encoder结构通过双向自注意力机制在建模每个 token 表示时同时整合左右两个方向的上下文信息从而获得更准确、更丰富的语义表示。bert和g p t的区别在于是否能观察到全局信息还是只能观察到上文的信息具体实现细节预处理输入序号掩码我们输入的是固定长的句子但是掩码表示有效的句子长度sequence ids用于分割句子ids需要通过一个embedding输入层表示这个和tranfo,er不太一样tranformer输入的有两个部分 一个是embedding 一个是postional encoding 变成 token embedding模型参数本次实验做的是bert-base模型版本层数 (Layers)模型维度 (d_model)注意力头数 (Heads)参数量BERT-base12768121.1 亿BERT-large241024163.4 亿Embedding类型描述/作用维度/形状备注Token Embedding词元嵌入表示输入序列中的每个token(句子长度, 768)768为模型隐藏层维度 (d_model)Position Embedding位置嵌入最大限制(512, 768)512为模型支持的最大输入长度Segment Embedding段嵌入用于区分不同的句子片段如NSP任务(2, 768)2代表两句话严谨起见如果单纯了解可忽略还需要经过dense层进一步提取图片解析左边的Dense层结构在功能上对应右边FFN层的整体架构但FFN层通过升维再降维的方式增强了模型的表达能力。Dense层全连接层的核心作用是将前一层提取的特征进行整合与转化最终输出任务所需的预测结果。它通过权重和偏置参数对输入特征进行线性变换并结合非线性激活函数增强模型的表达能力使其能够拟合复杂的决策边界。FFN层升维度再降维[ 输入 x ] shape: (L, 768) | v | FEED FORWARD NETWORK (FFN) | | | | ------------- | | | Linear 1 | -- 权重 W1: (768, 3072) | | (Dense) | 把门打开让信息量变大4倍 | ------------- | | | | | v | | shape: (L, 3072) -- 最宽处 | | | | | [ GELU/ReLU ] -- 激活函数 | | | | | v | | shape: (L, 3072) | | | | | v | | ------------- | | | Linear 2 | -- 权重 W2: (3072, 768) | | (Dense) | 把门关上恢复原状 | ------------- | | | | v [ 输出 y ] shape: (L, 768)所以模型的参数分为3部分直接运行理解参数配置运行环境conda create-nbertpython3.11-yconda activate bert pipinstalltorch2.6.0 --index-url https://download.pytorch.org/whl/cpu pipinstalltransformers-ihttps://pypi.tuna.tsinghua.edu.cn/simple前置知识model.parameters() 返回一个生成器生成参数p.numl()pytorch计算张量的方法一个形状为 (768, 3072) 的权重矩阵调用 .numel() 会返回 768 * 3072 2,359,296。一个形状为 (768,) 的偏置向量调用 .numel() 会返回 768。bert的代码 非常长有时候能达到1000行所以我们直接懂原理然后调用就可以代码importos# 设置 Hugging Face 镜像源加速下载fromtransformersimportBertModel,BertTokenizer# 1. 加载预训练模型从 Hugging Face 镜像加载中文 BERTbertBertModel.from_pretrained(rE:\25.第二十五章 ⾃然语⾔处理通⽤框架-BERT实战\课件、源码\BERT开源项目及数据\bert-base-chinese)# 2. 定义一个函数来统计模型参数defget_parameter_number(model):# 计算总参数量 (Total Parameters)total_numsum(p.numel()forpinmodel.parameters())# 计算可训练参数量 (Trainable Parameters)trainable_numsum(p.numel()forpinmodel.parameters()ifp.requires_grad)# 返回包含两个统计值的字典return{Total:total_num,Trainable:trainable_num}print(get_parameter_number(bert))# 嵌入层三个输入 不用说了转换emb_num21128*7682*768512*768# 忽略了bias# 参数说明 输入每个词先通过嵌入层变成768维向量W_Q 768×768 的矩阵 Query权重#768 * 768 输出线性层# 最后两个参数是前馈神经网络的升维和降维self_att_num768*768*3768*768768*30723072*768all_att_num12*self_att_num# 12层Transformerpooler_num768*768#经过12层Transformer后每个词都变成了768维向量#取[CLS] token BERT会在句首添加一个特殊标记 [CLS]print(emb_numall_att_numpooler_num)# for name, para in bert.named_parameters():# print(name, para.shape)结果和原理我们的验证参数是对的PS E:\25.第二十五章 ⾃然语⾔处理通⽤框架-BERT实战\课件、源码\BERT开源项目及数据D:/Software/Programming/Anaconda/envs/bert/python.exee:/25.第二十五章 ⾃然语⾔处理通⽤框架-BERT实战/课件、源码/BERT开源项目及数据/get_parm.pyLoading weights:100%|████████████████████████████████████████████████|199/199[00:0000:00,23911.83it/s][transformers]BertModel LOAD REPORT from: E:\25.第二十五章 ⾃然语⾔处理通⽤框架-BERT实战\课件、源码\BERT开 源项目及数据\bert-base-chinese Key|Status||---------------------------------------------------------- cls.seq_relationship.weight|UNEXPECTED||cls.predictions.bias|UNEXPECTED||cls.predictions.transform.dense.weight|UNEXPECTED||cls.predictions.decoder.weight|UNEXPECTED||cls.predictions.transform.LayerNorm.bias|UNEXPECTED||cls.seq_relationship.bias|UNEXPECTED||cls.predictions.transform.LayerNorm.weight|UNEXPECTED||cls.predictions.transform.dense.bias|UNEXPECTED||Notes: - UNEXPECTED: can be ignored when loading from different task/architecture;not okifyouexpectidentical arch.{Total:102267648,Trainable:102267648}embeddings.word_embeddings.weight torch.Size([21128,768])embeddings.position_embeddings.weight torch.Size([512,768])embeddings.token_type_embeddings.weight torch.Size([2,768])embeddings.LayerNorm.weight torch.Size([768])embeddings.LayerNorm.bias torch.Size([768])encoder.layer.0.attention.self.query.weight torch.Size([768,768])encoder.layer.0.attention.self.query.bias torch.Size([768])encoder.layer.0.attention.self.key.weight torch.Size([768,768])encoder.layer.0.attention.self.key.bias torch.Size([768])encoder.layer.0.attention.self.value.weight torch.Size([768,768])encoder.layer.0.attention.self.value.bias torch.Size([768])encoder.layer.0.attention.output.dense.weight torch.Size([768,768])encoder.layer.0.attention.output.dense.bias torch.Size([768])encoder.layer.0.attention.output.LayerNorm.weight torch.Size([768])encoder.layer.0.attention.output.LayerNorm.bias torch.Size([768])encoder.layer.0.intermediate.dense.weight torch.Size([3072,768])encoder.layer.0.intermediate.dense.bias torch.Size([3072])encoder.layer.0.output.dense.weight torch.Size([768,3072])encoder.layer.0.output.dense.bias torch.Size([768])encoder.layer.0.output.LayerNorm.weight torch.Size([768])encoder.layer.0.output.LayerNorm.bias torch.Size([768])encoder.layer.1.attention.self.query.weight torch.Size([768,768])encoder.layer.1.attention.self.query.bias torch.Size([768])encoder.layer.1.attention.self.key.weight torch.Size([768,768])encoder.layer.1.attention.self.key.bias torch.Size([768])encoder.layer.1.attention.self.value.weight torch.Size([768,768])encoder.layer.1.attention.self.value.bias torch.Size([768])encoder.layer.1.attention.output.dense.weight torch.Size([768,768])encoder.layer.1.attention.output.dense.bias torch.Size([768])encoder.layer.1.attention.output.LayerNorm.weight torch.Size([768])encoder.layer.1.attention.output.LayerNorm.bias torch.Size([768])encoder.layer.1.intermediate.dense.weight torch.Size([3072,768])encoder.layer.1.intermediate.dense.bias torch.Size([3072])encoder.layer.1.output.dense.weight torch.Size([768,3072])encoder.layer.1.output.dense.bias torch.Size([768])encoder.layer.1.output.LayerNorm.weight torch.Size([768])encoder.layer.1.output.LayerNorm.bias torch.Size([768])encoder.layer.2.attention.self.query.weight torch.Size([768,768])encoder.layer.2.attention.self.query.bias torch.Size([768])encoder.layer.2.attention.self.key.weight torch.Size([768,768])encoder.layer.2.attention.self.key.bias torch.Size([768])encoder.layer.2.attention.self.value.weight torch.Size([768,768])encoder.layer.2.attention.self.value.bias torch.Size([768])encoder.layer.2.attention.output.dense.weight torch.Size([768,768])encoder.layer.2.attention.output.dense.bias torch.Size([768])encoder.layer.2.attention.output.LayerNorm.weight torch.Size([768])encoder.layer.2.attention.output.LayerNorm.bias torch.Size([768])encoder.layer.2.intermediate.dense.weight torch.Size([3072,768])encoder.layer.2.intermediate.dense.bias torch.Size([3072])encoder.layer.2.output.dense.weight torch.Size([768,3072])encoder.layer.2.output.dense.bias torch.Size([768])encoder.layer.2.output.LayerNorm.weight torch.Size([768])encoder.layer.2.output.LayerNorm.bias torch.Size([768])encoder.layer.3.attention.self.query.weight torch.Size([768,768])encoder.layer.3.attention.self.query.bias torch.Size([768])encoder.layer.3.attention.self.key.weight torch.Size([768,768])encoder.layer.3.attention.self.key.bias torch.Size([768])encoder.layer.3.attention.self.value.weight torch.Size([768,768])encoder.layer.3.attention.self.value.bias torch.Size([768])encoder.layer.3.attention.output.dense.weight torch.Size([768,768])encoder.layer.3.attention.output.dense.bias torch.Size([768])encoder.layer.3.attention.output.LayerNorm.weight torch.Size([768])encoder.layer.3.attention.output.LayerNorm.bias torch.Size([768])encoder.layer.3.intermediate.dense.weight torch.Size([3072,768])encoder.layer.3.intermediate.dense.bias torch.Size([3072])encoder.layer.3.output.dense.weight torch.Size([768,3072])encoder.layer.3.output.dense.bias torch.Size([768])encoder.layer.3.output.LayerNorm.weight torch.Size([768])encoder.layer.3.output.LayerNorm.bias torch.Size([768])encoder.layer.4.attention.self.query.weight torch.Size([768,768])encoder.layer.4.attention.self.query.bias torch.Size([768])encoder.layer.4.attention.self.key.weight torch.Size([768,768])encoder.layer.4.attention.self.key.bias torch.Size([768])encoder.layer.4.attention.self.value.weight torch.Size([768,768])encoder.layer.4.attention.self.value.bias torch.Size([768])encoder.layer.4.attention.output.dense.weight torch.Size([768,768])encoder.layer.4.attention.output.dense.bias torch.流程我主要挑1些对我自己来说需要强调的就行其他的自己了解输入和embedding假设输入一句话[我, 爱, 中国]第一步每个词先通过嵌入层变成768维向量我 → [0.1, 0.2, ..., 0.9] (768维) 爱 → [0.3, 0.5, ..., 0.1] (768维) 中国 → [0.7, 0.2, ..., 0.4] (768维)自注意力QKV运算bert也是利用到自注意力计算的2.第二步用三个权重矩阵分别计算 Q、K、VW_Q768×768的矩阵# Query权重W_K768×768的矩阵# Key权重W_V768×768的矩阵# Value权重Q输入 × W_Q# 每个词的Query向量K输入 × W_K# 每个词的Key向量V输入 × W_V# 每个词的Value向量**pooler_num 768×768的含义**这是池化层Pooler的参数用来把整个句子的表示浓缩成一个固定向量。池化层输入我 爱 中 国 ↓ BERT编码器 ↓ 输出每个词都有一个768维向量 ↓ 池化层 ↓ 输出一个768维的句子向量 ← 用于分类等任务输入经过12层Transformer后每个词都变成了768维向量我 → [768维] 爱 → [768维] 中国 → [768维]取[CLS] tokenBERT会在句首添加一个特殊标记[CLS][[CLS], 我, 爱, 中, 国] ↑ 取这个位置的向量全连接层处理# 池化层768 → 768 的线性变换句子向量全连接层([CLS]的768维向量)# 参数 768 × 768为什么叫池化“池化”Pooling来源于 CNN 的概念但这里的 BERT Pooler 其实是classPooler(nn.Linear):def__init__(self):super().__init__(768,768)# 768×768它本质是一个全连接层不是真正的池化如Max Pooling。但习惯上叫它池化层。动手实现tokenizerimportos# 设置 Hugging Face 镜像源加速下载fromtransformersimportBertModel,BertTokenizer# 1. 加载预训练模型从 Hugging Face 镜像加载中文 BERTbertBertModel.from_pretrained(rE:\25.第二十五章 ⾃然语⾔处理通⽤框架-BERT实战\课件、源码\BERT开源项目及数据\bert-base-chinese)# 2. 定义一个函数来统计模型参数defget_parameter_number(model):# 计算总参数量 (Total Parameters)total_numsum(p.numel()forpinmodel.parameters())# 计算可训练参数量 (Trainable Parameters)trainable_numsum(p.numel()forpinmodel.parameters()ifp.requires_grad)# 返回包含两个统计值的字典return{Total:total_num,Trainable:trainable_num}# print(get_parameter_number(bert))# # 嵌入层三个输入 不用说了转换# emb_num 21128*768 2*768 512*768 # 忽略了bias# # 参数说明 输入每个词先通过嵌入层变成768维向量W_Q 768×768 的矩阵 Query权重# #768 * 768 输出线性层# # 最后两个参数是前馈神经网络的升维和降维# self_att_num 768*768*3 768*768 768*3072 3072*768# all_att_num 12 * self_att_num # 12层Transformer# pooler_num 768*768#经过12层Transformer后每个词都变成了768维向量# #取[CLS] token BERT会在句首添加一个特殊标记 [CLS]# print(emb_num all_att_num pooler_num)# # for name, para in bert.named_parameters():# # print(name, para.shape)# 1. 加载预训练的中文 BERT tokenizertokenizerBertTokenizer.from_pretrained(bert-base-chinese)# 2. 定义输入文本input我爱你# 3. 对文本进行分词处理outtokenizer(input,)# 4. 打印分词结果print(out)结果和分析[CLS]我爱你[SEP]加一些参数# 1. 加载预训练的中文 BERT tokenizertokenizerBertTokenizer.from_pretrained(bert-base-chinese)# 2. 定义输入文本input我爱你# 3. 对文本进行分词处理outtokenizer(input,truncationTrue,paddingmax_length,max_length128)# 4. 打印分词结果print(out)电话