簡化NLP:TensorFlow中tf.strings的使用

  • 2020 年 2 月 14 日
  • 筆記

簡化NLP:TensorFlow中tf.strings的使用

TensorFlow中很早就包含了tf.strings這個模組,不過實話說,在tf 1.x的固定計算圖的情況下,各種操作頗為複雜,我們在迎來了2.0中才更好可以看出tf.strings的威力。

tf.strings的其中一個重要的作用是可以使字元串成為TensorFlow的第一公民,可以直接加入到模型的輸入中,在上一篇最簡單的BERT調用中,就用到了類似技術 (https://github.com/qhduan/bert-model

詳細來說,我們之前在NLP中如果要將字元串進行計算,需要進行下面幾步:

  1. 首先需要將字元串分詞,例如英文常見用空格、標點分詞,中文使用分詞器或者乾脆按字分詞
  2. 其次需要計算一個詞表,能將每個詞對應到一個對應的數字上,一般還要加入一些例如[pad],[unk]等特殊符號
  3. 在訓練前將訓練集的所有字元串經過上面的結果,都轉換為數字元號。或者使用generator等技術在訓練中流式轉換

那麼tf.strings的目的,就是我們為什麼不能直接將字元串輸入,避免上面的幾步?這樣做有幾個好處:

  1. 避免了很多多餘的程式碼,比如額外的分詞、計算詞表等
  2. 保證模型的統一性,例如模型本身就包含了分詞和符號轉換,就可以直接把模型打包、發布(例如用tensorflow hub),這樣別人可以不載入或使用任何第三方程式碼和程式也能直接用你的模型了
  3. 模型發布也可以直接用tensorflow serve等完成,避免第三方介入

具體來說怎麼做呢,需要以下幾步:

代替分詞

上圖可以看到,英文直接使用tf.strings.split就可以按照空格和標點符號分詞,下圖中文的分詞相對tricky,我們先用字元串替換方法tf.strings.regex_replace來把中文符號的後面都加一個空格,然後就可以用split了。

代替詞表

TensorFlow提供了一個簡單的函數tf.strings.to_hash_bucket_fast可以用來代替詞表,從其中hash的字樣也可以看出,這是通過字元串hashing的方法來直接映射到對應的數字上。這個函數接受兩個參數,一個是字元串後者字元串數組,另一個是詞表大小。詞表大小是整個模型所要接受的詞表大小,當然既然是hashing,就有可能導致碰撞衝突,所以這個詞表大小應該設置的比實際單詞量大來盡量保證衝突不發生。

實際程式碼

當我們說我們需要構建一個文本分類模型的時候,例如簡單的Bi-LSTM,只需要這樣:

就可以完成一個包含tokenizer、字典映射到模型訓練的模型。其中的幾個細節:

tf.strings.split:分詞

tf.strings.to_hash_bucket_fast:詞表映射(字典映射,單個字元到整形)

tf.squeeze:用這個是因為我們的輸入會產生一個額外的維度,我們需要去掉

tf.to_tensor:實際上我們之前的維度輸入的是不定長的(ragged tensor),因為每個句子的詞數不一樣,我們需要將它轉換為定長的普通的tensor,所以這裡使用了to_tensor函數並填充0做padding

再之後的程式碼就與其他所有的TensorFlow/Keras程式碼無異了。

想要訓練時和其他普通的模型一樣:

想要預測時更簡單:

TensorFlow設計的很多工具也是希望我們能用最簡單的程式碼,最快完成工程實踐,提高最多的效率。