Natural Language Processing in TensorFlow(Week1/2)
這兩周沒有習題, 但是因為沒有接觸過太多NLP的東西, 光看懂code就花了不少時間
處理NLP因為不像圖像或raw data是數字的data, 必須要把文字轉換成數字
Tokenizer
因此第一步就是把文字編碼成數字, 這裡採用keras已有的API, Tokenizer
from tensorflow.keras.preprocessing.text import Tokenizer
sentences = [
'I love my dog',
'I love my cat'
]
tokenizer = Tokenizer(num_words = 100)
word_index = tokenizer.word_index
print(word_index)
#{'i': 1, 'my' : 3, 'dog' : 4, 'cat' : 5, 'love' : 2}
#加標點符號不影響, 不會因為dog!, 就增加一個index
#透過空格或, 分割語句
#不分大小寫 I = i
#tokenizer會依照出現的頻率來排序, 越前面的出現頻率越高
sequences = tokenizer.texts_to_sequences(sentences)
print(sequences)
#[[4, 2, 1, 3], [4, 2, 1, 6], [5, 2, 1, 3], [7, 5, 8, 1, 3, 9, 10]]
test_data = [
'i really love my dog',
'my dog loves my manatee'
]
test_seq = tokenizer.texts_to_sequences(sentences)
print(test_seq)
#[[4, 2, 1, 3], [1, 3, 1]
#可以看到沒看過的詞不會出現在裡面
上面可以看到當tokenizer沒有出現過的詞, 在texts_to_sequence會自動跳過那些詞, 如果要避免自動跳過這些詞有下面兩種方法
- 需要大量的訓練數據才能得到廣泛的詞彙
- 是否可以給予不知道的詞一個特殊的詞
OOV_token, Padding
除此之外, 送到model training之前還有一個問題是, data input必須要等長, 所以必須有padding的動作
加入OOV_token, 會自動把沒看過的詞用OOV_token取代
#要特別注意<OOV>不能跟真實數據的詞一樣, 否則會混淆
#num_words限定只抓100個最常見的詞出來token
tokenizer = Tokenizer(num_words = 100, oov_token="<OOV>")
from tensorflow.keras.preprocessing.sequence import pad_sequences
#會把每一段padding補0到相同長度, 會自動找語句最長的當maxlen
padded = pad_sequences(sequences)
#maxlen限制最長的語句長度, post是指把pad補0補在後面, 預設是'pre'
padded = pad_sequences(sequences, padding='post', maxlen=5)
下面這段可以讓tokenizer自動去fit給定的sentences (list)裡面有多少詞就fit出多少個token
tokenizer = Tokenizer(oov_token="<OOV>")
#會自動找出總共長度有多少
tokenizer = fit_on_texts(sentences)
Word Embeddings
詞轉換成數字之後, 目前的數字只代表常出現與否, 是否有更好的表示方式?
把單詞投影到空間中的向量, 使得這些數字變得有向量意義
舉例來說 : 可愛, 漂亮, 帥氣都是正面詞, 他們彼此在正面與反面的空間中應該比較相近, 相反的, 醜陋, 易怒, 難看這種詞就是反面詞, 會與前面的詞的距離比較遠
因此提出Word Embedding的方式將Word投影到不同意義的空間
Embedding是多維的array
詳細的Embedding code就不放上來了, 大家自己跟著課程練習
大致上的用法慢慢看都能看得懂
Quiz也不難
還有提供https://projector.tensorflow.org/
可以把embedded向量放上來看可視化
NLP很容易會發生overfit, 就是訓練上升但是validate下降, 有幾個作法
- 嘗試把句子簡短, 減少padding的數目, 通常會起到一點效果
- 更改embedding層數目, 但效果看起來還好
- 使用subword
Subword
這邊稍微google了一下subword的用意,
與傳統空格分隔tokenization技術的對比
- 傳統詞表示方法無法很好的處理未知或罕見的詞彙(OOV問題)
- 傳統詞tokenization方法不利於模型學習詞綴之間的關係
- Eg 模型學到的“old”, “older”, and “oldest”之間的關係無法泛化到“smart”, “smarter”, and “smartest”。
- Character embedding作為OOV的解決方法粒度太細 (不是很理解這句)
- Subword粒度在詞與字符之間,能夠較好的平衡OOV問題
實際subword怎麼做的還要再study
但subword這邊練習的時候有一個小坑, 在tf version 2.x好像會遇到
就是tensorflow_datasets出來的imdb data需要expand_dim
如果沒有處理直接把train_data送進去model.fit就會出現問題
print(train_data)
#<PrefetchDataset shapes: ((None,), ()), types: (tf.int64, tf.int64)>
train_data = train_data.map(lambda x_text, x_label: (x_text, tf.expand_dims(x_label, -1)))
test_data = test_data.map(lambda x_text, x_label: (x_text, tf.expand_dims(x_label, -1)))
print(train_data)
#<MapDataset shapes: ((None,), (1,)), types: (tf.int64, tf.int64)>
num_epochs = 10
心得 : NLP沒接觸過, 因此課堂影片來回看了幾次, 好在沒有習題, 但是為了熟悉度還是要跟著課堂上的範例走一遍
還是一樣的想法, model本身並不難建, 難的是要怎麼處理數據, 並讓model使用這些數據