【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.pswp.cn/bicheng/71539.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/71539.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/71539.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux中使用Docker安裝DIFY搭建本地支持庫和Agent

Dify 是一款開源的大語言模型(LLM) 應用開發平臺。它融合了后端即服務(Backend as Service)和 LLMOps 的理念,使開發者可以快速搭建生產級的生成式 AI 應用。即使你是非技術人員,也能參與到 AI 應用的定義和數據運營過程中。 然而…

開源工具推薦--思維導圖、流程圖等繪制

1. 前言 在工作中,經常要用到各種不同的工具,隨著系統的升級,有些工具也在不斷更新升級。這里收集整理一些好用的開源工具推薦,遵循以下一些基本原則:開源免費,商業工具的有效平替,輕量級&…

Ubuntu 下 nginx-1.24.0 源碼分析 - ngx_create_pool函數

ngx_create_pool 聲明在 src\core\ngx_palloc.h 中 ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log); 實現在 src\core\ngx_palloc.c 中 ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log) {ngx_pool_t *p;p ngx_memalign(NGX_POOL_ALIGNMENT, size, lo…

ac的dhcp池里option43配錯導致ap無法上線問題排查過程

dhcp池里ac地址配錯,導致ap無法上線問題排查過程 問題:ap手動設置ac的ip正常注冊在線,但dhcp獲得ip和ac地址發現無法在ac上注冊成功。 組網: ac旁路結構,路由器lan口地址172.16.1.1,開dhcp服務&#xff0…

IntelliJ IDEA中Maven配置全指南

一、環境準備與基礎配置 1.1 Windows 環境下載并配置 Maven 見此篇博文:環境配置 1.2 IDEA配置步驟 打開設置面板:File → Settings → Build → Build Tools → Maven 關鍵配置項: Maven home path E:\apache-maven-3.9.9 (…

存儲區域網絡(SAN)管理

存儲區域網絡(Storage Area Network,SAN)采用網狀通道(Fibre Channel ,簡稱FC)技術,通過FC交換機連接存儲陣列和服務器主機,建立專用于數據存儲的區域網絡。SAN提供了一種與現有LAN連…

使用vue-office報錯TypeError: ft.createElementVNode is not a function

支持多種文件(.docx、.xlsx、.xls、.pdf、.pptx)預覽的vue組件庫,支持vue2/3。也支持非Vue框架的預覽。 不支持.doc、.ppt(2003年及以前的版本) 官網:https://www.npmjs.com/package/vue-office/excel?activeTabreadme 官方有實…

Ubuntu部署ktransformers

準備工作 一臺服務器 CPU:500G GPU:48G(NVIDIA4090) 系統:Ubuntu20.04(github的文檔好像用的是22.04) 第一步:下載權重文件 1.下載hfd wget https://hf-mirror.com/hfd/hfd.s…

C++初階——簡單實現vector

目錄 1、前言 2、Vector.h 3、Test.cpp 1、前言 簡單實現std::vector類模板。 相較于前面的string,vector要注意: 深拷貝,因為vector的元素可能是類類型,類類型元素可以通過賦值重載,自己實現深拷貝。 迭代器失效…

全志A133 android10 適配SLM770A 4G模塊

一,模塊基本信息 1.官方介紹 SLM770A是美格智能最新推出的一款LTE Cat.4無線通訊模組,最大支持下行速率150Mbps及上行速率50Mbps。同時向下兼容現有的3G和2G網絡,以確保即使在偏遠地區也可以進行網絡通信。 SLM770A模組支持分集接收和MIMO技…

微信小程序:多菜單欄設計效果

一、實現效果 二、代碼 wxml 編輯前端界面,步驟 菜單邏輯: 逐步取出數組中的項,首先取出頂部菜單項,然后選中后取出選中的底部數據(左側菜單+右側內容),然后點擊左側菜單取出選中的左側菜單對應的右側內容 ①這里我的數據是全部封裝到一個數組對象的,首先我的循環…

C++基礎知識學習記錄—string類

string實際上是C內置的一個類,內部對char *進行了封裝,不用擔心數組越界問題,string類中,除了上課講解的函數外,還有很多函數可以使用,可以自行查閱文檔。 構造函數原型: string(); //創建一個…

Ollama+DeepSeek+Open-WebUi

環境準備 Docker Ollama Open-WebUi Ollama 下載地址:Ollama docker安裝ollama docker run -d \ -v /data/ollama/data:/root/.ollama \ -p 11434:11434 \ --name ollama ollama/ollama 下載模型 Ollama模型倉庫 # 示例:安裝deepseek-r1:7b doc…

設計模式--訪問者模式【行為型模式】

設計模式的分類 我們都知道有 23 種設計模式,這 23 種設計模式可分為如下三類: 創建型模式(5 種):單例模式、工廠方法模式、抽象工廠模式、建造者模式、原型模式。結構型模式(7 種)&#xff1…

前端循環全解析:JS/ES/TS 循環寫法與實戰示例

循環是編程中控制流程的核心工具。本文將詳細介紹 JavaScript、ES6 及 TypeScript 中各種循環的寫法、特性,并通過實際示例幫助你掌握它們的正確使用姿勢。 目錄 傳統三劍客 for 循環 while 循環 do...while 循環 ES6 新特性 forEach for...of for...in 數組…

數據中心儲能蓄電池狀態監測管理系統 組成架構介紹

安科瑞劉鴻鵬 摘要 隨著數據中心對供電可靠性要求的提高,蓄電池儲能系統成為關鍵的后備電源。本文探討了蓄電池監測系統在數據中心儲能系統中的重要性,分析了ABAT系列蓄電池在線監測系統的功能、技術特點及其應用優勢。通過蓄電池監測系統的實施&#…

Mac端homebrew安裝配置

拷打了一下午o3-mini-high,不如這位博主的超強帖子,10分鐘結束戰斗 跟隨該文章即可,2025/2/19親測可行 mac 安裝HomeBrew(100%成功)_mac安裝homebrew-CSDN博客文章瀏覽閱讀10w次,點贊258次,收藏837次。一直覺得自己寫…

機器學習實戰(8):降維技術——主成分分析(PCA)

第8集:降維技術——主成分分析(PCA) 在機器學習中,降維(Dimensionality Reduction) 是一種重要的數據處理技術,用于減少特征維度、去除噪聲并提高模型效率。主成分分析(Principal C…

windows環境下用docker搭建php開發環境dnmp

安裝WSL WSL即Linux子系統,比虛擬機占用資源少,安裝的前提是系統必須是win10以上。 WSL的安裝比較簡單,網上有很多教程,例如:WSL簡介與安裝流程(Windows 下的 Linux 子系統)_wsl安裝-CSDN博客&…

Python網絡爬蟲技術詳解文檔

Python網絡爬蟲技術詳解文檔 目錄 網絡爬蟲概述爬蟲核心技術解析常用Python爬蟲庫實戰案例演示反爬蟲機制與應對策略爬蟲法律與道德規范高級爬蟲技術資源推薦與學習路徑1. 網絡爬蟲概述 1.1 什么是網絡爬蟲 網絡爬蟲(Web Crawler)是一種按特定規則自動抓取互聯網信息的程序…