文本數(shù)據(jù)嵌入
在之前的學(xué)習(xí)之中,我們已經(jīng)簡(jiǎn)單地學(xué)習(xí)了如何進(jìn)行文本數(shù)據(jù)的處理:
- 使用 tf.data.TextLineDataset 加載文本數(shù)據(jù);
- 使用編碼將數(shù)據(jù)進(jìn)行編碼。
而在這節(jié)課之中,我們將詳細(xì)地了解將文本數(shù)據(jù)編碼的各種方法,并且對(duì)最常用的字詞嵌入方法進(jìn)行深入研究,最后我們會(huì)給出一個(gè)完整的模型來對(duì)電影評(píng)價(jià)進(jìn)行分類。
1. 文本數(shù)據(jù)編碼的方法
在機(jī)器學(xué)習(xí)領(lǐng)域,所有模型的處理數(shù)據(jù)都是數(shù)字類型的數(shù)據(jù)。因此在文本處理的過程之中,如何將文本數(shù)據(jù)轉(zhuǎn)化為數(shù)字類型數(shù)據(jù)是一個(gè)重要的研究課題??v觀所有的數(shù)據(jù)編碼方法,我們可以將常用的編碼方法大致可以分為三類:
- 簡(jiǎn)單編碼處理;
- 簡(jiǎn)單編碼后獨(dú)熱處理;
- 字詞嵌入處理。
1.1 簡(jiǎn)單編碼處理
假如我們有一個(gè)英文句子:
s = "How are you"
那么簡(jiǎn)單編碼處理就是對(duì)每個(gè)單詞賦予一個(gè)獨(dú)特的數(shù)字,用來表示該數(shù)字。比如:
"How" = 0
"are" = 1
"you" = 2
那么我們便可以將字符串s編碼為:
s_1 = [0, 1, 2]
相應(yīng)的“How you are”快就可以編碼為:
s_2 = [0, 2, 1]
這種編碼方式最大的好處是簡(jiǎn)單,非常容易理解,而且占用的空間資源很少。
但是這種方法也有缺點(diǎn):
- 那就是這種編碼方式無法識(shí)別兩個(gè)單詞之間的關(guān)系,也就是說兩個(gè)數(shù)字之間的關(guān)系不能代表實(shí)際的兩個(gè)單詞的關(guān)系,比如我們直觀理解 How 和 You 的關(guān)系更近一些,然而 How 和 You 的編碼距離是 2 ,而 are 和 you 的距離卻是 1 。
- 同時(shí)也正是因?yàn)樯厦娴脑颍瑢?dǎo)致不同的編碼規(guī)則會(huì)產(chǎn)生截然不同的效果,因?yàn)槲覀兊摹癏ow are you”完全可以編碼為 [111, 222, 333] ,而這會(huì)導(dǎo)致編碼后的數(shù)據(jù)只能依靠固定的編碼規(guī)則使用。
1.2 簡(jiǎn)單編碼的獨(dú)熱化
這種編碼和上述編碼方式一樣,只是進(jìn)行了一些獨(dú)熱化處理,因?yàn)楠?dú)熱化處理能夠讓模型在分類任務(wù)上擁有更好的性質(zhì)。
比如上述的例子:
s = "How are you"
按照獨(dú)熱編碼可以看作是:
"How" = [1, 0, 0]
"are" = [0, 1, 0]
"you" = [0, 0, 1]
雖然獨(dú)熱編碼具有更好的性值,但是它仍然沒有解決上面的問題,也就是說它依然無法表示兩個(gè)單詞之間的關(guān)系。不僅如此,它還引入了新的問題:存儲(chǔ)空間開銷巨大。因?yàn)槊總€(gè)單詞的存儲(chǔ)大小都是一個(gè)所有詞匯量大小的一個(gè)數(shù)組。
1.3 字詞嵌入
這種處理方式我們之前有過稍微的接觸,字詞嵌入會(huì)根據(jù)相關(guān)指定的參數(shù)來為每個(gè)單詞生成一個(gè)固定長(zhǎng)度的向量。
比如上面的英文句子:
s = "How are you"
編碼后可能變?yōu)椋?/p>
s_3 = [[1.9, 0.4,-0.3],[0.74, 0.23, -0.3],[0.5, 0.6, 0.7]]
通過這種形式的編碼處理,我們已經(jīng)很難通過肉眼來看出原來的句子了,但是對(duì)于機(jī)器學(xué)習(xí)的網(wǎng)絡(luò)模型來說,它卻可以進(jìn)行更快速的處理,同時(shí)它其中也包含著不同單詞之間的距離信息。
2. 使用 tf.keras.layers.Embedding 進(jìn)行字詞嵌入
該嵌入函數(shù)API的常用參數(shù)如下所示:
tf.keras.layers.Embedding(
input_dim, output_dim, embeddings_initializer='uniform',
embeddings_regularizer=None
)
這幾個(gè)參數(shù)的具體含義包括:
- input_dim: 輸入的維度,對(duì)于字詞嵌入來說就是詞匯量的大?。?/li>
- output_dim: 產(chǎn)出的維度,簡(jiǎn)單來說就是對(duì)單詞嵌入產(chǎn)生的向量的長(zhǎng)度;
- embeddings_initializer: 如何對(duì)嵌入進(jìn)行初始化;
- embeddings_regularizer: 嵌入的正則化項(xiàng),比如之前的L2正則化。
通過這些參數(shù),我們可以發(fā)現(xiàn),我們?cè)谶M(jìn)行字詞嵌入之前謂一需要做的就是找到詞匯量的大小,而這一般是人為規(guī)定的。
我們可以通過一個(gè)簡(jiǎn)單的示例來看一下它是如何工作的:
layer = tf.keras.layers.Embedding(100, 5) # 100表示詞匯量大小,5表示產(chǎn)出維度
print(layer(tf.constant([1,2,3,4,5])).numpy())
我們可以得到輸出:
[[-0.00772538 -0.00696523 -0.0306471 0.01268767 -0.0099443 ]
[-0.00331452 -0.00279518 -0.03783524 0.00927589 -0.02038437]
[ 0.03577108 0.01887624 -0.00056656 -0.00773742 0.03503906]
[ 0.02601126 0.02511038 0.01170179 -0.02206317 -0.03981184]
[-0.00608523 0.03906326 0.02454172 -0.0453696 -0.00303098]]
可以看到,我們的嵌入層已經(jīng)成功進(jìn)行了嵌入。
3. 使用字詞嵌入進(jìn)行電影評(píng)論分類
我們這里還是使用之前的電影評(píng)價(jià)進(jìn)行分類,在這里,我們不僅僅使用了嵌入層進(jìn)行處理,我們還手動(dòng)規(guī)定了最大的詞匯量為 10000 (這是為了避免一些生僻的單詞的不利影響,同時(shí)降低運(yùn)算量),同時(shí)我們將單詞數(shù)據(jù)提前填充到了相同的長(zhǎng)度 256 ,具體代碼展示如下:
import tensorflow as tf
# 使用內(nèi)置API獲取數(shù)據(jù),同時(shí)規(guī)定最大的詞匯量為10000
(train_data, train_labels), (test_data, test_labels) = tf.keras.datasets.imdb.load_data(num_words=10000)
# 文本數(shù)據(jù)對(duì)齊
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data, value=0, padding='post', maxlen=256)
test_data = tf.keras.preprocessing.sequence.pad_sequences(test_data, value=0, padding='post', maxlen=256)
# 模型構(gòu)建與編譯
model = tf.keras.Sequential([
tf.keras.layers.Embedding(10000, 32),
tf.keras.layers.GlobalAveragePooling1D(),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
model.summary()
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 訓(xùn)練與測(cè)試
history = model.fit(train_data, train_labels, epochs=30, batch_size=64)
results = model.evaluate(test_data, test_labels)
print(results)
最終,我們可以得到結(jié)果:
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_3 (Embedding) (None, None, 32) 320000
_________________________________________________________________
global_average_pooling1d_2 ( (None, 32) 0
_________________________________________________________________
dense_4 (Dense) (None, 64) 2112
_________________________________________________________________
dense_5 (Dense) (None, 1) 65
=================================================================
Total params: 322,177
Trainable params: 322,177
Non-trainable params: 0
_________________________________________________________________
Epoch 1/30
391/391 [==============================] - 3s 7ms/step - loss: 0.5101 - accuracy: 0.7729
Epoch 2/30
391/391 [==============================] - 3s 7ms/step - loss: 0.2611 - accuracy: 0.8996
..........
Epoch 29/30
391/391 [==============================] - 3s 7ms/step - loss: 0.0013 - accuracy: 1.0000
Epoch 30/30
391/391 [==============================] - 3s 7ms/step - loss: 0.0010 - accuracy: 1.0000
782/782 [==============================] - 1s 1ms/step - loss: 1.6472 - accuracy: 0.8312
[1.6471874713897705, 0.8312000036239624]
可以發(fā)現(xiàn)我們的模型準(zhǔn)確率最終達(dá)到了 83% 的準(zhǔn)確率,這是一個(gè)比較可以接收的結(jié)果。同時(shí)我們也可以發(fā)現(xiàn)模型的參數(shù)主要集中在嵌入層當(dāng)中。
4. 總結(jié)
在這節(jié)課之中,我們學(xué)習(xí)了文本嵌入的三種方法,同時(shí)我們對(duì)最常用的字詞嵌入的方法進(jìn)行了詳細(xì)的了解,最后我們根據(jù)一個(gè)電影評(píng)價(jià)分類的例子來進(jìn)行了聯(lián)系。