日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【NLP】完全解析!Bert Transformer 阅读理解源码详解

發(fā)布時(shí)間:2025/3/12 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【NLP】完全解析!Bert Transformer 阅读理解源码详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
接上一篇:

你所不知道的 Transformer!

超詳細(xì)的 Bert 文本分類源碼解讀 | 附源碼

中文情感分類單標(biāo)簽

參考論文:

https://arxiv.org/abs/1706.03762

https://arxiv.org/abs/1810.04805

在本文中,我將以run_squad.py以及SQuAD數(shù)據(jù)集為例介紹閱讀理解的源碼,官方代碼基于tensorflow-gpu 1.x,若為tensorflow 2.x版本,會(huì)有各種錯(cuò)誤,建議切換版本至1.14。?

當(dāng)然,注釋好的源代碼在這里:

https://github.com/sherlcok314159/ML/tree/main/nlp/code

章節(jié)

  • Demo傳參

  • 數(shù)據(jù)篇

    • 番外句子分類

    • 創(chuàng)造實(shí)例

    • 實(shí)例轉(zhuǎn)換

  • 模型構(gòu)造

  • 寫入預(yù)測(cè)

Demo傳參

python bert/run_squad.py \--vocab_file=uncased_L-12_H-768_A-12/vocab.txt \--bert_config_file=uncased_L-12_H-768_A-12/bert_config.json \--init_checkpoint=uncased_L-12_H-768_A-12/bert_model.ckpt \--do_train=True \--train_file=SQUAD_DIR/train-v2.0.json \--train_batch_size=8 \--learning_rate=3e-5 \--num_train_epochs=1.0 \--max_seq_length=384 \--doc_stride=128 \--output_dir=/tmp/squad2.0_base/ \--version_2_with_negative=True

閱讀源碼最重要的一點(diǎn)不是拿到就讀,而是跑通源碼里面的小demo,因?yàn)槟闩芡╠emo就意味著你對(duì)代碼的一些基礎(chǔ)邏輯和參數(shù)有了一定的了解。

前面的參數(shù)都十分常規(guī),如果不懂,建議看我的文本分類的講解。這里講一下比較特殊的最后一個(gè)參數(shù),我們做的任務(wù)是閱讀理解,如果有答案缺失,在SQuAD1.0是不可以的,但是在SQuAD允許,這也就是True的意思。

需要注意,不同人的文件路徑都是不一樣的,你不能照搬我的,要改成自己的路徑。

數(shù)據(jù)篇

其實(shí)閱讀理解任務(wù)模型是跟文本分類幾乎是一樣的,大的差異在于兩者對(duì)于數(shù)據(jù)的處理,所以本篇文章重點(diǎn)在于如何將原生的數(shù)據(jù)轉(zhuǎn)換為閱讀理解任務(wù)所能接受的數(shù)據(jù),至于模型構(gòu)造篇,請(qǐng)看文本分類:

https://github.com/sherlcok314159/ML/blob/main/nlp/tasks/text.md

番外句子分類

想必很多人看到SquadExample類的_repr_方法都很疑惑,這里處理好一個(gè)example,為什么后面還要進(jìn)行處理?看英文注釋會(huì)發(fā)現(xiàn)這個(gè)類其實(shí)跟閱讀理解沒(méi)關(guān)系,它只是處理之后對(duì)于句子分類任務(wù)的,自然在run_squad.py里面沒(méi)被調(diào)用。_repr_方法只是在有start_position的時(shí)候進(jìn)行字符串的拼接。

創(chuàng)造實(shí)例

用于訓(xùn)練的數(shù)據(jù)集是json文件,需要用json庫(kù)讀入。

訓(xùn)練集的樣式如下,可見(jiàn)data是最外層的

{"data": [{"title": "University_of_Notre_Dame","paragraphs": [{"context": "Architecturally, the school has a Catholic character.","qas": [{"answers": [{"answer_start": 515,"text": "Saint Bernadette Soubirous"}],"question": "To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?","id": "5733be284776f41900661182"}]}]},{"title":"...","paragraphs":[{"context":"...","qas":[{"answers":[{"answer_start":..,"text":"...",}],"question":"...","id":"..."},]}]}] }


input_data是一個(gè)大列表,然后每一個(gè)元素樣式如下

{'paragraphs': [{...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}, ...], 'title': 'University_of_Notre_Dame'}

is_whitespace方法是用來(lái)判斷是否是一個(gè)空格,在切分字符然后加入doc_tokens會(huì)用到。

然后我們層層剝開(kāi),然后遍歷context的內(nèi)容,它是一個(gè)字符串,所以遍歷的時(shí)候會(huì)遍歷每一個(gè)字母,字符會(huì)被進(jìn)行判斷,如果是空格,則加入doc_tokens,char_to_word_offset表示切分后的索引列表,每一個(gè)元素表示一個(gè)詞有幾個(gè)字符組成。

切分后的doc_tokens會(huì)去掉空白部分,同時(shí)會(huì)包括英文逗號(hào)。一個(gè)單詞會(huì)有很多字符,每個(gè)字符對(duì)應(yīng)的索引會(huì)存在char_to_word_offset,例如,前面都是0,代表這些字符都是第一個(gè)單詞的,所以都是0,換句話說(shuō)就是第一個(gè)單詞很長(zhǎng)。

doc_tokens = ['Architecturally,', 'the', 'school', 'has', 'a', 'Catholic', 'character.', 'Atop', 'the',"..."]char_to_word_offset = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]

接下來(lái)進(jìn)行qas內(nèi)容的遍歷,每個(gè)元素稱為qa,進(jìn)行id和question內(nèi)容的分配,后面都是初始化一些參數(shù)

qa里面還有一個(gè)is_impossible,用于判斷是否有答案

確保有答案之后,剛剛讀入了問(wèn)題,現(xiàn)在讀入與答案相關(guān)的部分,讀入的時(shí)候注意start_position和end_position是相對(duì)于doc_tokens的


接下來(lái)對(duì)答案部分進(jìn)行雙重檢驗(yàn),actual_text是根據(jù)doc_tokens和始末位置拼接好的內(nèi)容,然后對(duì)orig_answer_text進(jìn)行空格切分,最后用find方法判斷orig_answer_text是否被包含在actual_text里面。

這個(gè)是針對(duì)is_impossible來(lái)說(shuō)的,如果沒(méi)有答案,則把始末位置全部變成-1。

然后將example變成SquadExample的實(shí)例化對(duì)象,將example加入大列表——examples并返回,至此實(shí)例創(chuàng)建完成。

實(shí)例轉(zhuǎn)換

把json文件變成實(shí)例之后,我們還差一步便可以把數(shù)據(jù)塞進(jìn)模型進(jìn)行訓(xùn)練了,那就是將實(shí)例轉(zhuǎn)化為變量。

先對(duì)question_text進(jìn)行簡(jiǎn)單的空格切分變?yōu)閝uery_tokens

如果問(wèn)題過(guò)長(zhǎng),就進(jìn)行截?cái)嗖僮?/p>

接下來(lái)對(duì)doc_tokens進(jìn)行空格切分以及詞切分,變成all_doc_tokens,需要注意的是orig_to_tok_index代表的是doc_tokens在all_doc_tokens的索引,取最近的一個(gè),而tok_to_orig_index代表的是all_doc_tokens在doc_tokens索引

對(duì)tok_start_position和tok_end_position進(jìn)行初始化,記住,這兩個(gè)是相對(duì)于all_doc_tokens來(lái)說(shuō)的,一定要與start_position和end_position區(qū)分開(kāi)來(lái),它們是相對(duì)于doc_tokens來(lái)說(shuō)的

接下來(lái)先介紹_improve_answer_span方法,這個(gè)方法是用來(lái)處理特殊的情況的,舉個(gè)例子,假如說(shuō)你的文本是"The Japanese electronics industry is the lagest in the world.",你的問(wèn)題是"What country is the top exporter of electornics?" 那答案其實(shí)應(yīng)該是Japan,可是呢,你用空格和詞切分的時(shí)候會(huì)發(fā)現(xiàn)Japanese已經(jīng)在詞表中可查,這意味著不會(huì)對(duì)它進(jìn)行再切分,會(huì)直接將它返回,這種情況下可能需要這個(gè)方法救場(chǎng)。

因?yàn)槭潜O(jiān)督學(xué)習(xí),答案已經(jīng)給出,所以呢,這個(gè)方法干的事情就是詞切分后的tokens進(jìn)行再一次切分,如果發(fā)現(xiàn)切分之后會(huì)有更好的答案,就返回新的始末點(diǎn),否則就返回原來(lái)的。

對(duì)tok_start_position和tok_end_position進(jìn)行進(jìn)一步賦值

計(jì)算max_tokens_for_doc,與文本分類類似,需要減去[CLS]和兩個(gè)[SEP]的位置,這里不同的是還要減去問(wèn)題的長(zhǎng)度,因?yàn)檫@里算的是文本的長(zhǎng)度。?

tokens = [CLS] query tokens [SEP] context [SEP]


很多時(shí)候文章長(zhǎng)度大于maximum_sequence_length的時(shí)候,這個(gè)時(shí)候我們要對(duì)文章進(jìn)行切片處理,把它按照一定長(zhǎng)度進(jìn)行切分,每一個(gè)切片稱為一個(gè)doc_span,start代表從哪開(kāi)始,length代表一個(gè)的長(zhǎng)度。

doc_spans儲(chǔ)存很多個(gè)doc_span。這里對(duì)窗口的長(zhǎng)度有所限制,規(guī)定了start_offset不能比doc_stride大,這是第二個(gè)窗口的起點(diǎn),從這個(gè)角度或許可以理解doc_stride代表平滑的長(zhǎng)度。

接下來(lái)的操作跟文本分類有些類似,添加[CLS],然后添加問(wèn)題和[SEP],這些在segment_ids里面都為0。

下面講_check_is_max_context方法,這個(gè)方法是用來(lái)判斷某個(gè)詞是否具有完備的上下文關(guān)系,源代碼給了一個(gè)例子:?

Span A: the man went to the?

Span B: to the store and bought?

Span C: and bought a gallon of ...?

那么對(duì)于bought來(lái)說(shuō),它在Span B和Span C中都有出現(xiàn),那么,哪一個(gè)上下文關(guān)系最全呢?其實(shí)我們憑直覺(jué)應(yīng)該可以猜到應(yīng)該是Span C,因?yàn)镾pan B中bought出現(xiàn)在句末,沒(méi)有下文。當(dāng)然了,我們還是得用公式計(jì)算一下

score = min(num_left_context, num_right_context) + 0.01 * doc_span.length

score_B = min(4, 0) + 0.05 = 0.05?

score_C = min(1,3) + 0.05 = 1.05?

所以,在Span C中,bought的上下文語(yǔ)義最全,最終該方法會(huì)返回True or False,在滑動(dòng)窗口這個(gè)方法中,一個(gè)詞很可能出現(xiàn)在多個(gè)span里面,所以用這個(gè)方法判斷當(dāng)前這個(gè)詞在當(dāng)前span里面是否具有最完整的上下文

回到上面,token_to_orig_map是用來(lái)記錄文章部分在all_doc_tokens的索引,而token_is_max_context是記錄文章每一個(gè)詞在當(dāng)前span里面是否具有最完整的上下文關(guān)系,因?yàn)橐婚_(kāi)始只有一個(gè)span,那么一開(kāi)始每個(gè)詞肯定都是True。split_token_index用于切分成每一個(gè)token,這樣可以進(jìn)行上下文關(guān)系判斷,至于后面添[SEP]和segment_ids添1這種操作文本分類也有。

接下來(lái)將tokens(精細(xì)化切分后的)按照詞表轉(zhuǎn)化為id,另外若不足,則把0填充進(jìn)去這種操作也是很常見(jiàn)的。

前面是進(jìn)行判斷,如果切了之后答案并不在span里面就直接舍棄,若在里面,因?yàn)橐婚_(kāi)始all_doc_tokens里面沒(méi)有問(wèn)題和[CLS],[SEP]時(shí)正文的索引是tok_start_position,然后轉(zhuǎn)換為input_ids又有問(wèn)題以及[CLS],[SEP],所以要得到正文索引需要跳過(guò)它們。

接下來(lái)大量的tf.logging只是寫入日志信息,同時(shí)也是你終端或輸出那里看到的。

最終用這些參數(shù)實(shí)例化InputFeatures對(duì)象,然后不斷重復(fù),每一個(gè)feature對(duì)應(yīng)著一個(gè)特殊的id,即為unique_id。

模型構(gòu)建

這里大致與文本分類差不多,只是文本分類在模型里面直接進(jìn)行了softmax處理,然后進(jìn)行最小交叉熵?fù)p失,而這次我們沒(méi)有直接這樣做,得到了開(kāi)頭和結(jié)尾處的未歸一化的概率logits,之后我們直接返回。

然后這次我們是在model_fn_builder方法里面的子方法model_fn里定義compute_loss,其實(shí)這里也是經(jīng)過(guò)softmax進(jìn)行歸一化,然后再計(jì)算交叉熵?fù)p失,最終返回均方誤差。

然后我們計(jì)算開(kāi)頭和結(jié)尾處的損失,總損失為二者和的平均。

最終我們進(jìn)行優(yōu)化。

寫入預(yù)測(cè)?

start_logit & end_logit 代表著未經(jīng)過(guò)softmax的概率,start_logit表示tokens里面以每一個(gè)token作為開(kāi)頭的概率,后者類似的。還有一對(duì)null_start_logit & null_end_logit,它們兩個(gè)代表的是SQuAD2.0沒(méi)有答案的那些,默認(rèn)全為0。

首先,簡(jiǎn)單介紹一下_get_best_indexes,這個(gè)方法是用來(lái)輸出由高到低前n_best_size個(gè)的概率的索引。

遍歷start_indexes,end_indexes(都是分別經(jīng)過(guò)_get_best_indexes得到),對(duì)于答案未缺失的,以具體的logit填入,另外,feature_index代表第幾個(gè)feature。

如果答案缺失,則全都為0

接下來(lái)我們進(jìn)一步轉(zhuǎn)換為具體的文本

然后進(jìn)一步清洗數(shù)據(jù)

這樣還有個(gè)問(wèn)題,詞切分會(huì)自動(dòng)小寫,與答案還存在一定的偏移,這里介紹get_final_text方法來(lái)解決這一問(wèn)題,比如:?

pred_text = steve smith?

orig_text = Steve Smith's?

這個(gè)方法通俗來(lái)講就是獲得orig_text(未經(jīng)過(guò)詞切分)上正確的截取片段。?

然后將其添加到nbest中

同樣會(huì)存在沒(méi)有答案的情況

接下來(lái)會(huì)有一個(gè)total_scores,它的元素是start_logit和end_logit相加,注意,它們不是數(shù)值,是數(shù)組,之后就計(jì)算total_scores的交叉熵?fù)p失作為概率。

剩下的部分跟文本分類差不多,這里就此略過(guò)。

往期精彩回顧適合初學(xué)者入門人工智能的路線及資料下載機(jī)器學(xué)習(xí)及深度學(xué)習(xí)筆記等資料打印機(jī)器學(xué)習(xí)在線手冊(cè)深度學(xué)習(xí)筆記專輯《統(tǒng)計(jì)學(xué)習(xí)方法》的代碼復(fù)現(xiàn)專輯 AI基礎(chǔ)下載機(jī)器學(xué)習(xí)的數(shù)學(xué)基礎(chǔ)專輯溫州大學(xué)《機(jī)器學(xué)習(xí)課程》視頻 本站qq群851320808,加入微信群請(qǐng)掃碼:

總結(jié)

以上是生活随笔為你收集整理的【NLP】完全解析!Bert Transformer 阅读理解源码详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。