深度學(xué)習(xí)模型不會(huì)接受原始文本數(shù)據(jù)作為輸入,它只能處理數(shù)值張量。因此需要文本向量化。
文本向量化是指將原始文本轉(zhuǎn)化為數(shù)值張量的過程抹凳,有多種實(shí)現(xiàn)方式:
????1.將文本分割為單詞,并將每個(gè)單詞轉(zhuǎn)化為一個(gè)向量
????2. 將文本分割為字符伦腐,并將每個(gè)字符轉(zhuǎn)化為一個(gè)向量
????3. 提取單詞或字符的n-gram(多個(gè)連續(xù)的單詞或字符)赢底,將每個(gè)n-gram轉(zhuǎn)化為一個(gè)向量。
將文本分割后的單詞/字符/n-gram稱為token柏蘑,將tokens轉(zhuǎn)化為向量有兩種方法:
????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)#創(chuàng)建一個(gè)分詞器,只保留前1000個(gè)最常見的單詞 tokenizer.fit_on_texts(samples)#構(gòu)建單詞索引 sequence=tokenizer.texts_to_sequences(samples)#將字符串轉(zhuǎn)換為單詞的整數(shù)索引組成的列表?
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.無法捕捉詞之間的語義關(guān)系
對于第一個(gè)問題咳焚,可以使用one-hot散列技巧來緩解洽损,也就是對word做hash。問題是會(huì)存在沖突革半。
import numpy as np
#將單詞保存為長度為1000 的向量碑定。如果單詞數(shù)量接近1000 個(gè)(或更多),那么會(huì)遇到很多散列沖突又官,這會(huì)降低這種編碼方法的準(zhǔn)確性
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詞嵌入
詞嵌入是從數(shù)據(jù)中學(xué)習(xí)得到的延刘。常見的詞向量維度是256、512 或1024(處理非常大的詞表時(shí))六敬。
與此相對碘赖,onehot編碼的詞向量維度通常為20 000 或更高(對應(yīng)包含20 000 個(gè)標(biāo)記的詞表)。因此外构,詞向量可以將更多的信息塞入更低的維度中普泡。
詞向量之間的幾何關(guān)系應(yīng)該表示這些詞之間的語義關(guān)系。詞嵌入的作用應(yīng)該是將人類的語言映射到幾何空間中审编。
獲取詞嵌入主要有兩種方法:
????1.在完成主任務(wù)(文本分類/情感預(yù)測)的同時(shí)學(xué)習(xí)詞嵌入撼班。
????在這種情況下,詞嵌入一開始是隨機(jī)值垒酬,在訓(xùn)練的過程中對詞嵌入進(jìn)行學(xué)習(xí)砰嘁。學(xué)習(xí)方式與神經(jīng)網(wǎng)絡(luò)中的權(quán)重相同眯亦。
????2.在不同于待解決問題的機(jī)器學(xué)習(xí)任務(wù)中計(jì)算好詞嵌入,將其直接加載進(jìn)模型中般码。這些詞嵌入叫做預(yù)訓(xùn)練詞嵌入。
在Tensorflow中乱顾,可以通過Embedding層來生成詞向量
Embedding層至少需要兩個(gè)參數(shù):(1)token的個(gè)數(shù)(最大單詞索引+1)(2)嵌入的維度
如:embedding_layer=keras.layers.Embedding(1000,64)
可以將Embedding層看作一個(gè)字典:將整數(shù)索引(表示指定單詞)映射為稠密向量。
它接受整數(shù)作為輸入走净,并在內(nèi)部字典中查找這些整數(shù)券时,然后返回相關(guān)聯(lián)的向量。
單詞索引->Embedding層->對應(yīng)的詞向量
Embedding層的輸入是一個(gè)二維整數(shù)張量伏伯,其形狀為(samples,sequence_length), 其中每個(gè)元素是一個(gè)整數(shù)序列橘洞。
序列必須具有相同的長度(因?yàn)樾枰獙⑺鼈兇虬梢粋€(gè)張量),所以較短的序列應(yīng)該用0填充说搅,較長的序列應(yīng)該被截?cái)唷?/b>
Embedding 層返回一個(gè)形狀為(samples, sequence_length, embedding_dimensionality) 的三維浮點(diǎn)數(shù)張量炸枣。然后可以用RNN 層或一維卷積層來處理這個(gè)三維張量。
將一個(gè)Embedding 層實(shí)例化時(shí)弄唧,它的權(quán)重(即標(biāo)記向量的內(nèi)部字典)最開始是隨機(jī)的适肠。與其他層一樣,在訓(xùn)練過程中候引,利用反向傳播來逐漸調(diào)節(jié)這些詞向量侯养,改變空間結(jié)構(gòu)以便下游模型可以利用。
代碼示例:使用keras自帶的imdb數(shù)據(jù)集
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.datasets import imdb
max_feature=1000 #每條文本只保留最常見的1000個(gè)詞
max_len=20 #每條文本單詞個(gè)數(shù)最多為20
(x_train,y_train),(x_test,t_test)=imdb.load_data(num_words=max_feature)
#將整數(shù)列表轉(zhuǎn)換成形狀為(samples,maxlen) 的二維整數(shù)張量
x_train=keras.preprocessing.sequence.pad_sequences(x_train,max_len)
x_test=keras.preprocessing.sequence.pad_sequences(x_test,max_len)
構(gòu)建模型:
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')
訓(xùn)練模型:
history=model.fit(x_train,y_train,epochs=10,batch_size=32,validation_split=0.2)
上述在訓(xùn)練模型的過程中澄干,同時(shí)訓(xùn)練詞向量逛揩。若想使用預(yù)訓(xùn)練的詞向量:
加載數(shù)據(jù)集:使用原始imdb數(shù)據(jù)集(即英文語句序列)
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
#對文本數(shù)據(jù)進(jìn)行分詞,使用預(yù)訓(xùn)練的詞向量
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])
打亂訓(xùn)練數(shù)據(jù)并將其劃分為訓(xùn)練集麸俘,驗(yàn)證集:
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 年英文維基百科的預(yù)計(jì)算嵌入。
這是一個(gè)822 MB 的壓縮文件疾掰,文件名是glove.6B.zip搂誉,里面包含400 000 個(gè)單詞(或非單詞的標(biāo)記)的100 維嵌入向量。解壓文件静檬。
?對解壓后的文件(一個(gè).txt 文件)進(jìn)行解析炭懊,構(gòu)建一個(gè)將單詞(字符串)映射為其向量表示(數(shù)值向量)的索引。
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()
構(gòu)建可以加載到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
與上例相同拂檩,構(gòu)建模型:
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'))
關(guān)鍵步驟侮腹,使用預(yù)訓(xùn)練的詞向量:
model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False
訓(xùn)練模型:
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))