深度學習模型不會接受原始文本數據作為輸入,它只能處理數值張量。因此需要文本向量化。
文本向量化是指將原始文本轉化為數值張量的過程,有多種實現方式:
????1.將文本分割為單詞,并將每個單詞轉化為一個向量
????2. 將文本分割為字符,并將每個字符轉化為一個向量
????3. 提取單詞或字符的n-gram(多個連續的單詞或字符),將每個n-gram轉化為一個向量。
將文本分割后的單詞/字符/n-gram稱為token,將tokens轉化為向量有兩種方法:
????1.one-hot 編碼????
????2.Embedding(通常只用于單詞,叫作詞嵌入(word embedding))
一. one-hot
import tensorflow as tf?
from tensorflow import keras?
from tensorflow.keras.preprocessing.text import Tokenizer
samples=['The cat is very cute.','The girl is so beautiful.'] tokenizer=Tokenizer(num_words=1000)#創建一個分詞器,只保留前1000個最常見的單詞 tokenizer.fit_on_texts(samples)#構建單詞索引 sequence=tokenizer.texts_to_sequences(samples)#將字符串轉換為單詞的整數索引組成的列表?
one_hot_results=tokenizer.texts_to_matrix(samples,mode='binary')
word_index=tokenizer.word_index#單詞索引
print(sequence):
print(one_hot_results):
print(word_index):
One-hot存在的問題:
? ? 1.維度爆炸
? ? 2.無法捕捉詞之間的語義關系
對于第一個問題,可以使用one-hot散列技巧來緩解,也就是對word做hash。問題是會存在沖突。
import numpy as np
#將單詞保存為長度為1000 的向量。如果單詞數量接近1000 個(或更多),那么會遇到很多散列沖突,這會降低這種編碼方法的準確性
dimensionality=1000
max_length=10
results=np.zeros((len(samples),max_length,dimensionality))
for i,sample in enumerate(samples):
? ? for j,word in list(enumerate(sample.split()))[:max_length]:
? ? ? ? index=abs(hash(word))%dimensionality
? ? ? ? results[i][j][index]=1
二. Embedding詞嵌入
詞嵌入是從數據中學習得到的。常見的詞向量維度是256、512 或1024(處理非常大的詞表時)。
與此相對,onehot編碼的詞向量維度通常為20 000 或更高(對應包含20 000 個標記的詞表)。因此,詞向量可以將更多的信息塞入更低的維度中。
詞向量之間的幾何關系應該表示這些詞之間的語義關系。詞嵌入的作用應該是將人類的語言映射到幾何空間中。
獲取詞嵌入主要有兩種方法:
????1.在完成主任務(文本分類/情感預測)的同時學習詞嵌入。
????在這種情況下,詞嵌入一開始是隨機值,在訓練的過程中對詞嵌入進行學習。學習方式與神經網絡中的權重相同。
????2.在不同于待解決問題的機器學習任務中計算好詞嵌入,將其直接加載進模型中。這些詞嵌入叫做預訓練詞嵌入。
在Tensorflow中,可以通過Embedding層來生成詞向量
Embedding層至少需要兩個參數:(1)token的個數(最大單詞索引+1)(2)嵌入的維度
如:embedding_layer=keras.layers.Embedding(1000,64)
可以將Embedding層看作一個字典:將整數索引(表示指定單詞)映射為稠密向量。
它接受整數作為輸入,并在內部字典中查找這些整數,然后返回相關聯的向量。
單詞索引->Embedding層->對應的詞向量
Embedding層的輸入是一個二維整數張量,其形狀為(samples,sequence_length), 其中每個元素是一個整數序列。
序列必須具有相同的長度(因為需要將它們打包成一個張量),所以較短的序列應該用0填充,較長的序列應該被截斷。
Embedding 層返回一個形狀為(samples, sequence_length, embedding_dimensionality) 的三維浮點數張量。然后可以用RNN 層或一維卷積層來處理這個三維張量。
將一個Embedding 層實例化時,它的權重(即標記向量的內部字典)最開始是隨機的。與其他層一樣,在訓練過程中,利用反向傳播來逐漸調節這些詞向量,改變空間結構以便下游模型可以利用。
代碼示例:使用keras自帶的imdb數據集
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.datasets import imdb
max_feature=1000 #每條文本只保留最常見的1000個詞
max_len=20 #每條文本單詞個數最多為20
(x_train,y_train),(x_test,t_test)=imdb.load_data(num_words=max_feature)
#將整數列表轉換成形狀為(samples,maxlen) 的二維整數張量
x_train=keras.preprocessing.sequence.pad_sequences(x_train,max_len)
x_test=keras.preprocessing.sequence.pad_sequences(x_test,max_len)
構建模型:
dimonsion=8
model=keras.models.Sequential()
# Embedding 層激活的形狀為(samples, maxlen, 8)
model.add(keras.layers.Embedding(max_feature,dimonsion,input_length=max_len))
# 將三維的嵌入張量展平成形狀為(samples, maxlen * 8) 的二維張量
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(1,activation='sigmoid'))
model.compile(loss='binary_crossentropy',metrics=['accuracy'],optimizer='rmsprop')
訓練模型:
history=model.fit(x_train,y_train,epochs=10,batch_size=32,validation_split=0.2)
上述在訓練模型的過程中,同時訓練詞向量。若想使用預訓練的詞向量:
加載數據集:使用原始imdb數據集(即英文語句序列)
import pandas as pd
train=pd.read_csv("D://data/imdb/train.csv",sep="\t",header=None)
train.columns=['label','text']
train.head()
text=train.text
label=train.label
#對文本數據進行分詞,使用預訓練的詞向量
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
max_len=100
max_words=1000
tokenizer=Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(text)
sequence=tokenizer.texts_to_sequences(text)
word_index=tokenizer.word_index
print('found %s unique tokens'% len(word_index))
data=pad_sequences(sequence,max_len)
print(data[:2])
打亂訓練數據并將其劃分為訓練集,驗證集:
import numpy as np
indices=np.arange(data.shape[0])
np.random.shuffle(indices)
data=data[indices]
label=label[indices]
x_val=data[:5000]
y_val=label[:5000]
x_train=data[5000:]
y_train=label[5000:]
下載Glove詞嵌入
打開https://nlp.stanford.edu/projects/glove,下載2014 年英文維基百科的預計算嵌入。
這是一個822 MB 的壓縮文件,文件名是glove.6B.zip,里面包含400 000 個單詞(或非單詞的標記)的100 維嵌入向量。解壓文件。
?對解壓后的文件(一個.txt 文件)進行解析,構建一個將單詞(字符串)映射為其向量表示(數值向量)的索引。
embedding_index={}
f=open("D:/Glove/glove.6B.100d.txt",'r',encoding='mac_roman')
for line in f:
? ? ? ? values=line.split()
? ? ? ? word=values[0]
? ? ? ? coefs=np.array(values[1:],dtype='float32')
? ? ? ? embedding_index[word]=coefs
f.close()
構建可以加載到Embedding層的嵌入矩陣
expanding_dim=100
embedding_matrix=np.zeros((max_words,expanding_dim))
for word,i in word_index.items():
? ? if i<max_words:
? ? ? ? embedding_vector=embedding_index.get(word)
? ? ? ? if embedding_vector is not None:
? ? ? ? ? ? embedding_matrix[i]=embedding_vector
與上例相同,構建模型:
model=keras.models.Sequential()
model.add(keras.layers.Embedding(1000,100,input_length=max_len))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(1,activation='sigmoid'))
關鍵步驟,使用預訓練的詞向量:
model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False
訓練模型:
model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['acc'])
history=model.fit(x_train,y_train,epochs=10,batch_size=32,validation_data=(x_val,y_val))