如何用 Python 和 BERT 做中文文本二元分類?

如何用 Python 和 BERT 做中文文本二元分類?

興奮

去年, Google 的 BERT 模型一發布出來,我就很興奮。

因為我當時正在用 fast.ai 的 ULMfit 做自然語言分類任務(還專門寫了《如何用 Python 和深度遷移學習做文本分類?》一文分享給你)。ULMfit 和 BERT 都屬於預訓練語言模型(Pre-trained Language Modeling),具有很多的相似性。

所謂語言模型,就是利用深度神經網絡結構,在海量語言文本上訓練,以抓住一種語言的通用特徵

上述工作,往往只有大機構才能完成。因為花費實在太大了。

這花費包括但不限於:

  • 存數據
  • 買(甚至開發)運算設備
  • 訓練模型(以天甚至月計)
  • 聘用專業人員
  • ……

預訓練就是指他們訓練好之後,把這種結果開放出來。我們普通人或者小型機構,也可以借用其結果,在自己的專門領域文本數據上進行微調,以便讓模型對於這個專門領域的文本有非常清晰的認識

所謂認識,主要是指你遮擋上某些詞彙,模型可以較準確地猜出來你藏住了什麼。

甚至,你把兩句話放在一起,模型可以判斷它倆是不是緊密相連的上下文關係。

如何用 Python 和 BERT 做中文文本二元分類?

這種“認識”有用嗎?

當然有。

BERT 在多項自然語言任務上測試,不少結果已經超越了人類選手。

如何用 Python 和 BERT 做中文文本二元分類?

BERT 可以輔助解決的任務,當然也包括文本分類(classification),例如情感分類等。這也是我目前研究的問題。

痛點

然而,為了能用上 BERT ,我等了很久。

Google 官方代碼早已開放。就連 Pytorch 上的實現,也已經迭代了多少個輪次了。

但是我只要一打開他們提供的樣例,就頭暈。

如何用 Python 和 BERT 做中文文本二元分類?

單單是那代碼的行數,就非常嚇人。

而且,一堆的數據處理流程(Data Processor) ,都用數據集名稱命名。我的數據不屬於上述任何一個,那麼我該用哪個?

如何用 Python 和 BERT 做中文文本二元分類?

還有莫名其妙的無數旗標(flags) ,看了也讓人頭疼不已。

如何用 Python 和 BERT 做中文文本二元分類?

讓我們來對比一下,同樣是做分類任務,Scikit-learn 裡面的語法結構是什麼樣的。

from sklearn.datasets import load_iris
from sklearn import tree
iris = load_iris()
clf = tree.DecisionTreeClassifier()
clf = clf.fit(iris.data, iris.target)

即便是圖像分類這種數據吞吐量大,需要許多步驟的任務,你用 fast.ai ,也能幾行代碼,就輕輕鬆鬆搞定。

!git clone https://github.com/wshuyi/demo-image-classification-fastai.git
from fastai.vision import *
path = Path("demo-image-classification-fastai/imgs/")
data = ImageDataBunch.from_folder(path, test='test', size=224)
learn = cnn_learner(data, models.resnet18, metrics=accuracy)
learn.fit_one_cycle(1)
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_top_losses(9, figsize=(8, 8))

別小瞧這幾行代碼,不僅幫你訓練好一個圖像分類器,還能告訴你,那些分類誤差最高的圖像中,模型到底在關注哪裡。

如何用 Python 和 BERT 做中文文本二元分類?

對比一下,你覺得 BERT 樣例和 fast.ai 的樣例區別在哪兒?

我覺得,後者是給人用的

教程

我總以為,會有人把代碼重構一下,寫一個簡明的教程。

畢竟,文本分類任務是個常見的機器學習應用。應用場景多,也適合新手學習。

但是,這樣的教程,我就是沒等來。

當然,這期間,我也看過很多人寫的應用和教程。

有的就做到把一段自然語言文本,轉換到 BERT 編碼。戛然而止。

有的倒是認真介紹怎麼在官方提供的數據集上,對 BERT 進行“稍微修改”使用。所有的修改,都在原始的 Python 腳本上完成。那些根本沒用到的函數和參數,全部被保留。至於別人如何複用到自己的數據集上?人家根本沒提這事兒。

我不是沒想過從頭啃一遍代碼。想當年讀研的時候,我也通讀過仿真平臺上 TCP 和 IP 層的全部 C 代碼。我確定眼前的任務,難度更低一些。

但是我真的懶得做。我覺得自己被 Python 機器學習框架,特別是 fast.ai 和 Scikit-learn 寵壞了。

後來, Google 的開發人員把 BERT 弄到了 Tensorflow Hub 上。還專門寫了個 Google Colab Notebook 樣例。

看到這個消息,我高興壞了。

我嘗試過 Tensorflow Hub 上的不少其他模型。使用起來很方便。而 Google Colab 我已在《如何用 Google Colab 練 Python?》一文中介紹給你,是非常好的 Python 深度學習練習和演示環境。滿以為雙劍合璧,這次可以幾行代碼搞定自己的任務了。

且慢。

如何用 Python 和 BERT 做中文文本二元分類?

真正打開一看,還是以樣例數據為中心。

普通用戶需要什麼?需要一個接口。

你告訴我輸入的標準規範,然後告訴我結果都能有什麼。即插即用,完事兒走人。

一個文本分類任務,原本不就是給你個訓練集和測試集,告訴你訓練幾輪練多快,然後你告訴我準確率等結果嗎?

你至於讓我為了這麼簡單的一個任務,去讀幾百行代碼,自己找該在哪裡改嗎?

好在,有了這個樣例做基礎,總比沒有好。

我耐下心來,把它整理了一番。

聲明一下,我並沒有對原始代碼進行大幅修改。

所以不講清楚的話,就有剽竊嫌疑,也會被鄙視的。

這種整理,對於會 Python 的人來說,沒有任何技術難度。

可正因為如此,我才生氣。這事兒難做嗎?Google 的 BERT 樣例編寫者怎麼就不肯做?

從 Tensorflow 1.0 到 2.0,為什麼變動會這麼大?不就是因為 2.0 才是給人用的嗎?

你不肯把界面做得清爽簡單,你的競爭者(TuriCreate 和 fast.ai)會做,而且做得非常好。實在坐不住了,才肯降尊紆貴,給普通人開發一個好用的界面。

教訓啊!為什麼就不肯吸取呢?

我給你提供一個 Google Colab 筆記本樣例,你可以輕易地替換上自己的數據集來運行。你需要去理解(包括修改)的代碼,不超過10行

我先是測試了一個英文文本分類任務,效果很好。於是寫了一篇 Medium 博客,旋即被 Towards Data Science 專欄收錄了。

如何用 Python 和 BERT 做中文文本二元分類?

Towards Data Science 專欄編輯給我私信,說:

Very interesting, I like this considering the default implementation is not very developer friendly for sure.

有一個讀者,居然連續給這篇文章點了50個贊(Claps),我都看呆了。

如何用 Python 和 BERT 做中文文本二元分類?

看來,這種忍受已久的痛點,不止屬於我一個人。

估計你的研究中,中文分類任務可能遇到得更多。所以我乾脆又做了一箇中文文本分類樣例,並且寫下這篇教程,一併分享給你。

咱們開始吧。

代碼

請點擊這個鏈接(http://t.cn/E6twZEG),查看我在 Github 上為你做好的 IPython Notebook 文件。

如何用 Python 和 BERT 做中文文本二元分類?

Notebook 頂端,有個非常明顯的 "Open in Colab" 按鈕。點擊它,Google Colab 就會自動開啟,並且載入這個 Notebook 。

如何用 Python 和 BERT 做中文文本二元分類?

我建議你點一下上圖中紅色圈出的 “COPY TO DRIVE” 按鈕。這樣就可以先把它在你自己的 Google Drive 中存好,以便使用和回顧。

這件事做好以後,你實際上只需要執行下面三個步驟:

  1. 你的數據,應該以 Pandas 數據框形式組織。如果你對 Pandas 不熟悉,可以參考我的這篇文章。
  2. 如有必要,可以調整訓練參數。其實主要是訓練速率(Learning Rate)和訓練輪數(Epochs)。
  3. 執行 Notebook 的代碼,獲取結果。

當你把 Notebook 存好之後。定睛一看,或許會覺得上當了。

如何用 Python 和 BERT 做中文文本二元分類?

老師你騙人!說好了不超過10行代碼的!

別急

在下面這張圖紅色圈出的這句話之前,你不用修改任何內容

如何用 Python 和 BERT 做中文文本二元分類?

請你點擊這句話所在位置,然後從菜單中如下圖選擇 Run before 。

如何用 Python 和 BERT 做中文文本二元分類?

下面才都是緊要的環節,集中注意力。

第一步,就是把數據準備好。

!wget https://github.com/wshuyi/demo-chinese-text-binary-classification-with-bert/raw/master/dianping_train_test.pickle
with open("dianping_train_test.pickle", 'rb') as f:
train, test = pickle.load(f)

這裡使用的數據,你應該並不陌生。它是餐飲點評情感標註數據,我在《如何用Python和機器學習訓練中文文本情感分類模型?》和《如何用 Python 和循環神經網絡做中文文本分類?》中使用過它。只不過,為了演示的方便,這次我把它輸出為 pickle 格式,一起放在了演示 Github repo 裡,便於你下載和使用。

其中的訓練集,包含1600條數據;測試集包含400條數據。標註裡面1代表正向情感,0代表負向情感。

利用下面這條語句,我們把訓練集重新洗牌(shuffling),打亂順序。以避免過擬合(overfitting)。

train = train.sample(len(train))


這時再來看看我們訓練集的頭部內容。

train.head()
如何用 Python 和 BERT 做中文文本二元分類?

如果你後面要替換上自己的數據集,請注意格式。訓練集和測試集的列名稱應該保持一致。

第二步,我們來設置參數。

myparam = {
"DATA_COLUMN": "comment",
"LABEL_COLUMN": "sentiment",
"LEARNING_RATE": 2e-5,
"NUM_TRAIN_EPOCHS":3,
"bert_model_hub":"https://tfhub.dev/google/bert_chinese_L-12_H-768_A-12/1"
}

前兩行,是把文本、標記對應的列名,指示清楚。

第三行,指定訓練速率。你可以閱讀原始論文,來進行超參數調整嘗試。或者,你乾脆保持默認值不變就可以。

第四行,指定訓練輪數。把所有數據跑完,算作一輪。這裡使用3輪。

如果你希望學習如何使用這些參數,推薦下面這本教材。

廣告

Python深度學習作者:[美]弗朗索瓦·肖萊(Francois Chollet)

京東

最後一行,是說明你要用的 BERT 預訓練模型。咱們要做中文文本分類,所以使用的是這個中文預訓練模型地址。如果你希望用英文的,可以參考我的 Medium 博客文章以及對應的英文樣例代碼。

最後一步,我們依次執行代碼就好了。

result, estimator = run_on_dfs(train, test, **myparam)


注意,執行這一句,可能需要花費一段時間。做好心理準備。這跟你的數據量和訓練輪數設置有關。

在這個過程中,你可以看到,程序首先幫助你把原先的中文文本,變成了 BERT 可以理解的輸入數據格式。

如何用 Python 和 BERT 做中文文本二元分類?

當你看到下圖中紅色圈出文字時,就意味著訓練過程終於結束了。

如何用 Python 和 BERT 做中文文本二元分類?

然後你就可以把測試的結果打印出來了。

pretty_print(result)
如何用 Python 和 BERT 做中文文本二元分類?

跟咱們之前的教程(使用同一數據集)對比一下。

如何用 Python 和 BERT 做中文文本二元分類?

當時自己得寫那麼多行代碼,而且需要跑10個輪次,可結果依然沒有超過 80% 。這次,雖然只訓練了3個輪次,但準確率已經超過了 88% 。

在這樣小規模數據集上,達到這樣的準確度,不容易。

BERT 性能之強悍

,可見一斑。

小結

講到這裡,你已經學會了如何用 BERT 來做中文文本二元分類任務了。希望你會跟我一樣開心。

如果你是個資深 Python 愛好者,請幫我個忙。

還記得這條線之前的代碼嗎?

如何用 Python 和 BERT 做中文文本二元分類?

能否幫我把它們打個包?這樣咱們的演示代碼就可以更加短小精悍和清晰易用了。

歡迎在咱們的 Github 項目上提交你的代碼。如果你覺得這篇教程對你有幫助,歡迎給這個 Github 項目加顆星。謝謝!

祝深度學習愉快!

延伸閱讀


分享到:


相關文章: