深度學習-Embedding(詞嵌入)

深度學習模型不會接受原始文本數據作為輸入,它只能處理數值張量。因此需要文本向量化

文本向量化是指將原始文本轉化為數值張量的過程,有多種實現方式:

????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))

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容