詞嵌入向量WordEmbedding的原理和生成方法

姓名:崔少杰 ? ? ? 學號:16040510021

轉載自:http://www.reibang.com/p/0bb00eed9c63=有修改

【嵌牛導讀】:詞嵌入向量(WordEmbedding)是NLP里面一個重要的概念,我們可以利用WordEmbedding將一個單詞轉換成固定長度的向量表示,從而便于進行數學處理虐急。本文將介紹WordEmbedding的使用方式,并講解如何通過神經網絡生成WordEmbedding。

【嵌牛鼻子】:WordEmbedding

【嵌牛提問】:詞嵌入向量WordEmbedding在解決問題時有什么巨大的作用囚痴?

【嵌牛正文】:WordEmbedding的使用

使用數學模型處理文本語料的第一步就是把文本轉換成數學表示撬讽,有兩種方法,第一種方法可以通過one-hot矩陣表示一個單詞缎脾,one-hot矩陣是指每一行有且只有一個元素為1祝闻,其他元素都是0的矩陣。針對字典中的每個單詞遗菠,我們分配一個編號联喘,對某句話進行編碼時,將里面的每個單詞轉換成字典里面這個單詞編號對應的位置為1的one-hot矩陣就可以了辙纬。比如我們要表達“the cat sat on the mat”豁遭,可以使用如下的矩陣表示。

one-hot矩陣表示法

one-hot表示方式很直觀贺拣,但是有兩個缺點蓖谢,第一捂蕴,矩陣的每一維長度都是字典的長度,比如字典包含10000個單詞闪幽,那么每個單詞對應的one-hot向量就是1X10000的向量啥辨,而這個向量只有一個位置為1,其余都是0盯腌,浪費空間溉知,不利于計算。第二腕够,one-hot矩陣相當于簡單的給每個單詞編了個號级乍,但是單詞和單詞之間的關系則完全體現不出來。比如“cat”和“mouse”的關聯性要高于“cat”和“cellphone”帚湘,這種關系在one-hot表示法中就沒有體現出來卡者。

WordEmbedding解決了這兩個問題。WordEmbedding矩陣給每個單詞分配一個固定長度的向量表示客们,這個長度可以自行設定崇决,比如300,實際上會遠遠小于字典長度(比如10000)底挫。而且兩個單詞向量之間的夾角值可以作為他們之間關系的一個衡量恒傻。如下表示:

WordEmbedding表示法

通過簡單的余弦函數,我們就可以計算兩個單詞之間的相關性建邓,簡單高效:

兩個向量相關性計算

因為WordEmbedding節(jié)省空間和便于計算的特點盈厘,使得它廣泛應用于NLP領域。接下來我們講解如何通過神經網絡生成WordEmbedding官边。

WordEmbedding的生成

WordEmbedding的生成我們使用tensorflow沸手,通過構造一個包含了一個隱藏層的神經網絡實現。

下面是下載數據和加載數據的代碼注簿,一看就懂契吉。訓練數據我們使用的是http://mattmahoney.net/dc/enwik8.zip數據,里面是維基百科的數據诡渴。

def maybe_download(filename, url):

"""Download a file if not present, and make sure it's the right size."""

if not os.path.exists(filename):

filename, _ = urllib.urlretrieve(url + filename, filename)

return filename

# Read the data into a list of strings.

def read_data(filename):

"""Extract the first file enclosed in a zip file as a list of words."""

with zipfile.ZipFile(filename) as f:

data = tf.compat.as_str(f.read(f.namelist()[0])).split()

return data

def collect_data(vocabulary_size=10000):

url = 'http://mattmahoney.net/dc/'

filename = maybe_download('enwik8.zip', url)

vocabulary = read_data(filename)

print(vocabulary[:7])

data, count, dictionary, reverse_dictionary = build_dataset(vocabulary, vocabulary_size)

del vocabulary? # Hint to reduce memory.

return data, count, dictionary, reverse_dictionary

接下來是如何構建訓練數據捐晶。構建訓練數據主要包括統(tǒng)計詞頻,生成字典文件妄辩,并且根據字典文件給訓練源數據中的單詞進行編號等工作惑灵。我們生成的字典不可能包含所有的單詞,一般我們按照單詞頻率由高到低排序眼耀,選擇覆蓋率大于95%的單詞加入詞典就可以了英支,因為詞典越大,覆蓋的場景越大哮伟,同時計算開銷越大干花,這是一個均衡鸯屿。下面的代碼展示了這個過程,首先統(tǒng)計所有輸入語料的詞頻把敢,選出頻率最高的10000個單詞加入字典。同時在字典第一個位置插入一項“UNK"代表不能識別的單詞谅辣,也就是未出現在字典的單詞統(tǒng)一用UNK表示修赞。然后給字典里每個詞編號,并把源句子里每個詞表示成在字典中的編號桑阶。我們可以根據每個詞的編號查找WordEmbedding中的向量表示柏副。

def build_dataset(words, n_words):

"""Process raw inputs into a dataset."""

count = [['UNK', -1]]

#? [['UNK', -1], ['i', 500], ['the', 498], ['man', 312], ...]

count.extend(collections.Counter(words).most_common(n_words - 1))

#? dictionary {'UNK':0, 'i':1, 'the': 2, 'man':3, ...}

dictionary = dict()

for word, _ in count:

dictionary[word] = len(dictionary)

data = list()

unk_count = 0

for word in words:

if word in dictionary:

index = dictionary[word]

else:

index = 0? # dictionary['UNK']

unk_count += 1

data.append(index)

count[0][1] = unk_count

reversed_dictionary = dict(zip(dictionary.values(), dictionary.keys()))

# data: "I like cat" -> [1, 21, 124]

# count: [['UNK', 349], ['i', 500], ['the', 498], ['man', 312], ...]

# dictionary {'UNK':0, 'i':1, 'the': 2, 'man':3, ...}

# reversed_dictionary: {0:'UNK', 1:'i', 2:'the', 3:'man', ...}

return data, count, dictionary, reversed_dictionary

接下來我們看一下如何將源句子轉換成訓練過程的輸入和輸出,這一步是比較關鍵的蚣录。有兩種業(yè)界常用的WordEmbedding生成方式割择,Continuous Bag Of Words (CBOW)方法和n-gram方法,我們采用n-gram方法萎河。訓練的目的是獲得能夠反映任意兩個單詞之間關系的單詞向量表示荔泳,所以我們的輸入到輸出的映射也要翻譯兩個單詞之間的關聯。n-gram的思路是將所有的源句子按固定長度(比如128個單詞)分割成很多batch虐杯。對于每個batch玛歌,從前往后每次選取長度為skip_window的窗口(我們設定skip_window=5)。對于窗口中的5個單詞擎椰,我們生成兩個source-target數據對支子,這兩個source-target對的source都是窗口中間的單詞,也就是第三個單詞达舒,然后從另外四個單詞中隨機選取兩個作為兩個target單詞值朋。然后窗口向后移動一個單詞,每次向后移動一個位置獲取下5個單詞巩搏,一共循環(huán)64次昨登,獲取到64X2=128個source-target對,作為一個batch的訓練數據贯底「萋猓總的思路就是把某個單詞和附近的單詞組對,作為輸入和輸出丈甸。這里同一個source單詞糯俗,會被映射到不同的target單詞,這樣理論上可以獲取任意兩個單詞之間的關系睦擂。

比如對于句子"cat and dog play balls on the floor"得湘,第一個窗口就是“cat and dog play balls",生成的兩個source-target對可能是下面中的任意兩個:

dog -> cat

dog -> and

dog -> balls

dog -> play

第二個窗口是"and dog play balls on"顿仇,生成的兩個source-target對可能是下面中的任意兩個:

play -> and

play -> balls

play -> dog

play -> on

下面是代碼實現:

def generate_batch(data, batch_size, num_skips, skip_window):

global data_index

assert batch_size % num_skips == 0

assert num_skips <= 2 * skip_window

batch = np.ndarray(shape=(batch_size), dtype=np.int32)

context = np.ndarray(shape=(batch_size, 1), dtype=np.int32)

span = 2 * skip_window + 1? # span含義 -> [ skip_window input_word skip_window ]

# 初始化最大長度為span的雙端隊列淘正,超過最大長度后再添加數據摆马,會從另一端刪除容不下的數據

# buffer: 1, 21, 124, 438, 11

buffer = collections.deque(maxlen=span)

for _ in range(span):

buffer.append(data[data_index])

data_index = (data_index + 1) % len(data)

for i in range(batch_size // num_skips):? # 128 / 2

# target: 2

target = skip_window? # input word at the center of the buffer

# targets_to_avoid: [2]

targets_to_avoid = [skip_window]? # 需要忽略的詞在當前span的位置

# 更新源單詞為當前5個單詞的中間單詞

source_word = buffer[skip_window]

# 隨機選擇的5個span單詞中除了源單詞之外的4個單詞中的兩個

for j in range(num_skips):

while target in targets_to_avoid:? # 隨機重新從5個詞中選擇一個尚未選擇過的詞

target = random.randint(0, span - 1)

targets_to_avoid.append(target)

# batch添加源單詞

batch[i * num_skips + j] = source_word

# context添加目標單詞,單詞來自隨機選擇的5個span單詞中除了源單詞之外的4個單詞中的兩個

context[i * num_skips + j, 0] = buffer[target]

# 往雙端隊列中添加下一個單詞鸿吆,雙端隊列會自動將容不下的數據從另一端刪除

buffer.append(data[data_index])

data_index = (data_index + 1) % len(data)

# Backtrack a little bit to avoid skipping words in the end of a batch

data_index = (data_index + len(data) - span) % len(data)

return batch, context

接下來是構建神經網絡的過程囤采,我們構建了一個包含一個隱藏層的神經網絡,該隱藏層包含300個節(jié)點惩淳,這個數量和我們要構造的WordEmbedding維度一致蕉毯。

with graph.as_default():

# 定義輸入輸出

train_sources = tf.placeholder(tf.int32, shape=[batch_size])

train_targets = tf.placeholder(tf.int32, shape=[batch_size, 1])

valid_dataset = tf.constant(valid_examples, dtype=tf.int32)

# 初始化embeddings矩陣,這個就是經過多步訓練后最終我們需要的embedding

embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))

# 將輸入序列轉換成embedding表示, [batch_size, embedding_size]

embed = tf.nn.embedding_lookup(embeddings, train_sources)

# 初始化權重

weights = tf.Variable(tf.truncated_normal([embedding_size, vocabulary_size], stddev=1.0 / math.sqrt(embedding_size)))

biases = tf.Variable(tf.zeros([vocabulary_size]))

# 隱藏層輸出結果的計算, [batch_size, vocabulary_size]

hidden_out = tf.transpose(tf.matmul(tf.transpose(weights), tf.transpose(embed))) + biases

# 將label結果轉換成one-hot表示, [batch_size, 1] -> [batch_size, vocabulary_size]

train_one_hot = tf.one_hot(train_targets, vocabulary_size)

# 根據隱藏層輸出結果和標記結果,計算交叉熵

cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=hidden_out, labels=train_one_hot))

# 隨機梯度下降進行一步反向傳遞

optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(cross_entropy)

# 計算驗證數據集中的單詞和字典表里所有單詞的相似度思犁,并在validate過程輸出相似度最高的幾個單詞

norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))

normalized_embeddings = embeddings / norm

valid_embeddings = tf.nn.embedding_lookup(normalized_embeddings, valid_dataset)

similarity = tf.matmul(valid_embeddings, normalized_embeddings, transpose_b=True)

# 參數初始化賦值

init = tf.global_variables_initializer()

我們首先隨機初始化embeddings矩陣代虾,通過tf.nn.embedding_lookup函數將輸入序列轉換成WordEmbedding表示作為隱藏層的輸入。初始化weights和biases激蹲,計算隱藏層的輸出棉磨。然后計算輸出和target結果的交叉熵,使用GradientDescentOptimizer完成一次反向傳遞学辱,更新可訓練的參數乘瓤,包括embeddings變量。在Validate過程中策泣,對測試數據集中的單詞馅扣,利用embeddings矩陣計算測試單詞和所有其他單詞的相似度,輸出相似度最高的幾個單詞着降,看看它們相關性如何差油,作為一種驗證方式无蜂。

通過這個神經網絡件相,就可以完成WordEmbedding的訓練,繼而應用于其他NLP的任務氧腰。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末交掏,一起剝皮案震驚了整個濱河市妆偏,隨后出現的幾起案子,更是在濱河造成了極大的恐慌盅弛,老刑警劉巖钱骂,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異挪鹏,居然都是意外死亡见秽,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門讨盒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來解取,“玉大人,你說我怎么就攤上這事返顺≠骺啵” “怎么了蔓肯?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長振乏。 經常有香客問我蔗包,道長,這世上最難降的妖魔是什么慧邮? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任调限,我火速辦了婚禮,結果婚禮上赋咽,老公的妹妹穿的比我還像新娘。我一直安慰自己吨娜,他們只是感情好脓匿,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宦赠,像睡著了一般陪毡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勾扭,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天毡琉,我揣著相機與錄音,去河邊找鬼妙色。 笑死桅滋,一個胖子當著我的面吹牛,可吹牛的內容都是我干的身辨。 我是一名探鬼主播丐谋,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼煌珊!你這毒婦竟也來了号俐?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤定庵,失蹤者是張志新(化名)和其女友劉穎吏饿,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體蔬浙,經...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡猪落,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了畴博。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片许布。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绎晃,靈堂內的尸體忽然破棺而出蜜唾,到底是詐尸還是另有隱情杂曲,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布袁余,位于F島的核電站擎勘,受9級特大地震影響,放射性物質發(fā)生泄漏颖榜。R本人自食惡果不足惜棚饵,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掩完。 院中可真熱鬧噪漾,春花似錦、人聲如沸且蓬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恶阴。三九已至诈胜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冯事,已是汗流浹背焦匈。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留昵仅,地道東北人缓熟。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像摔笤,于是被迫代替她去往敵國和親荚虚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內容