关于最近实践 Bert 的一些坑
作者:老宋的茶書會
https://zhuanlan.zhihu.com/p/69389583
前言
最近,已經幾乎將重心完全放在了如何在 Bert 之上搞事情,主要還是探索 Bert 在分類問題以及閱讀理解問題上的一些表現,已經踩了不少的坑,想著把最近遇到的坑都記錄下來,幫助大家更好的使用 Bert。
幾個需要注意的地方
文本長度
首先注意到的一點是, 隨著文本長度的增加,所需顯存容量也會隨之呈現線性增加, 運行時間也接近線性,因此,我們往往要做一個權衡,對于不同任務而言, 文本長度所帶來的影響力并不相同.
就分類問題而言, 到一定的文本長度后,模型表現就幾乎沒有變化了,這個時候再去提升文本長度意義就不大了。
512 的魔咒
當你設置你的文本長度超過?512?時,會發生如下錯誤:
RuntimeError: Creating MTGP constants failed.在?pytorch-pretrained-BERT?項目下的Bert 實現中, 文本長度最多支持512, 這是由于Position Embedding?決定的,這意味著,如果你的文本長度很長, 你就需要采用截斷或分批讀取的方式來讀入。
不要一開始就跑整個數據集
在前期編碼測試過程中,由于數據集往往很大,加載的過程很漫長,我們就必須等到加載完成才能看看模型能不能跑起來,而實際上,往往需要不斷試錯。如果每次都跑全數據集,那么對于中大數據集,尤其是使用上 Bert 之后(分詞慢), 其效率簡直令人發指。
因此,十分推薦先分出一個demo級別的子數據集,我一般會份 1000, 1000, 1000,完整的跑一次之后再進行真正數據集的運行。
文本分類任務如何微調 Bert [2]
如何截取文本
由于 Bert 支持最大長度為 512 個token,那么如何截 取文本也成為一個很關鍵的問題。[2] 中探討了三種方式:
head-only:保存前 510 個 token (留兩個位置給 [CLS] 和 [SEP] )
tail-only:保存最后 510 個token
head + tail :選擇前128個 token 和最后382個 token
而作者在 IMDB 和 sogou 數據集上測試,發現 head + tail 效果最好,因此實際中,這三種思路都值得一試,搞不好能提高一點點呢。
多層策略
還有一種方法是將文本劃分為多段,每一段都不超過512個token, 這樣就能夠完全捕捉到全部的文本信息了。但這對于分類任務來說真的有用嗎?
我個人認為收效甚微, 就像我們讀一篇文章,基本讀個開頭,結尾基本就知道這篇文章是什么主題,屬于哪個分類了,極少數情況下會出現那種模糊不清的狀況, 而實驗也的確表明并沒有獲得很好的效果提升。
[2] 中首先將文本劃分為??段, 然后分別對每一段進行編碼,在融合信息的時候采用了三種戰略:
多層 + mean:各段求平均值
多層 + max :各段求最大值
多層 + self-att :加一層 Attention 融合
實驗表明,效果并沒有任何提升,甚至還有所下降, 我自己在 CNews 數據集上進行了多層策略的測試,發現效果提升有限,0.09個百分點,可以說幾乎沒有提升了,后續會再測試幾個數據集,可參見我的倉庫:Bert-TextClassification
Catastrophic Forgetting
Catastrophic Forgetting 指的是在遷移學習中,當學習新知識時,會忘記以前很重要的舊知識,那么 Bert 作為NLP 中遷移學習的代表,它是否有嚴重的 Catastrophic Forgetting 問題呢?
[2] 中發現,一個較低的學習率,如?2e-5?是 Bert 克服 Catastrophic Forgetting 問題的關鍵,且在?pytorch-pretrained-BERT?的實現中, 學習率是?5e-5?,也對應了這個觀點。
是否需要預訓練
微調雖然足夠強大,但是預訓練能否再次帶來效果的提升以及如何進行預訓練依舊是未知的話題。
首先第一個問題:?預訓練能否帶來效果的提升?答案是大概率會, 具體的提升依舊需要看數據集本身,從 [2] 中的實驗看出,大多數數據集都有不等的效果提升。
第二個問題:如何進行預訓練,或者說是采用哪些數據進行預訓練,主要有三個策略:
在特定數據集上做預訓練。[2] 中的實驗表明,這種方式大概率能夠提升效果。
同領域數據上預訓練。一般情況下,這種會比策略1的效果更好,且數據更容易獲取,但是如果數據來源不一,是可能帶來噪聲的。
跨領域數據上預訓練。這種策略效果提升沒有上兩種策略大,這是因為Bert 本身就已經經過高質量,大規模的跨領域數據訓練
綜合來說, 策略 2 是最佳的,前提是你需要保證數據的質量。
卡幾何,多多益善?
Pytorch 多GPU并行
我們先來談談在多GPU情況下, Pytorch 內部處理機制原理, 這對我們調參很有幫助。
Pytorch 與大多數深度學習框架一樣, 其選擇了數據并行(圖1)的方式來處理多GPU下的模型訓練, 但有其特殊之處。
具體來說, Pytorch 首先將模型加載到主GPU(一般是device_id=0), 然后再將模型復制到各個GPU上, 然后將一個 batch 的數據按照GPU個數劃分,將對應的數據輸入到各個GPU內。每個GPU都獨立的進行前向計算。而在反向傳播中, Pytorch需要匯總各個GPU上的模型輸出信息到GPU 0(個人理解,希望大佬指正,有時間會做實驗分析一下),這使得GPU 0 所占用的顯存相對較大, 且梯度計算過程會集中在GPU 0?上,梯度計算完成之后再將梯度復制到其余GPU,然后進行反向傳播更新。
實際實驗表明, 對于分類任務而言,差距并不明顯,但對于語言模型任務,由于其輸出層很大,有可能導致 GPU 爆掉。如下圖所示:
我們要用幾張卡?
對于小數據集而言, 建議使用 1 或2個GPU搞定, 多GPU情況下我們需要考慮到通信時間因素,而通信傳輸是比較慢的,親測對于小數據集而言,GPU太多反而耗時更久。對于中大數據集,需要自己判斷,多試試在幾個GPU情況下相對最省時間,又有足夠的顯存。
pytorch 中使用多卡
多卡情況下,模型相關的調用有所區別,主要在以下幾個方面:
# 模型定義 if n_gpu > 1:model=nn.DataParallel(model,device_ids=[0,1,2])# 損失計算 if n_gpu > 1:loss = loss.mean() # mean() to average on multi-gpu.# 模型保存 model_to_save = model.module if hasattr(model, 'module') else model torch.save(model_to_save.state_dict(), output_model_file)我們看到。最主要的變化依舊在模型不分, 這也是必須了解的?nn.DataParallel。
多GPU情況下負載均衡問題
具體在使用單機多GPU, 發現 GPU 0所占用的顯存要大于其余 GPU, 這是由于模型輸出以及相關梯度張量最終會匯總在GPU 0 上,前面已經有所提及,這就是 Pytorch 的負載均衡問題。
Pytorch 的負載均衡問題對于一般任務如分類來說其實并不嚴重, 而對于輸出層較大的任務如語言模型而言就十分關鍵,這也側面證明了我以前說的話:語言模型并不是一般實驗室和公司能搞的。
我個人目前并沒有受到這個問題很深的困擾,因此就沒有較深的探索,畢竟?梯度累積?這個Trick 還是十分良心的。
而對于 Pytorch 的負載均衡問題,大致有兩種解決思路:
第一種是采用分布式 DistributedDataParallel
另一種是自己寫, 可以看看 [1], 由于沒有深入就不廢話了。
最后
其實,這篇文章更像是筆記,算是對自己最近遇到的坑的一個總結,相信大家也都多多少少會遇到這些問題,因此分享出來給大家一些思考。懶得寫的更長了,就這樣吧。
如果覺得有用,點個贊再走吧,畢竟,寫文不易啊。
Reference
[1]?增大Batch訓練神經網絡:單GPU、多GPU及分布式配置的實用技巧
[2] How to Fine-Tune BERT for Text Classification?
備注:公眾號菜單包含了整理了一本AI小抄,非常適合在通勤路上用學習。
往期精彩回顧2019年公眾號文章精選適合初學者入門人工智能的路線及資料下載機器學習在線手冊深度學習在線手冊AI基礎下載(第一部分)備注:加入本站微信群或者qq群,請回復“加群”加入知識星球(4500+用戶,ID:92416895),請回復“知識星球”喜歡文章,點個在看
總結
以上是生活随笔為你收集整理的关于最近实践 Bert 的一些坑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 春节快乐!iPhone11 128G抱回
- 下一篇: EMNLP 2019中和BERT相关的一