# 七、自然語言處理
在本章中,我們將向您介紹如何使用 TensorFlow 中的文本。我們將首先介紹單詞嵌入如何使用詞袋方法,然后我們將繼續實現更高級的嵌入,如 word2vec 和 doc2vec。
在本章中,我們將介紹以下主題:
* 使用詞袋
* 實現 TF-IDF
* 使用 Skip-Gram 嵌入
* 使用 CBOW 嵌入
* 使用 word2vec 進行預測
* 使用 doc2vec 進行情感分析
請注意,讀者可以在 [Github](https://github.com/nfmcclure/tensorflow_cookbook) 和 [Packt 倉庫](https://github.com/PacktPublishing/TensorFlow-Machine-Learning-Cookbook-Second-Edition)中找到本章的所有代碼。
# 介紹
到目前為止,我們只考慮過主要使用數字輸入的機器學習算法。如果我們想要使用文本,我們必須找到一種方法將文本轉換為數字。有很多方法可以做到這一點,我們將在本章中探討一些常用的方法。
如果我們考慮句子`TensorFlow makes machine learning easy`,我們可以按照我們觀察它們的順序將單詞轉換為數字。這將使句子成為`1 2 3 4 5`。然后,當我們看到一個新句子`machine learning is easy`時,我們可以將其翻譯為`3 4 0 5,`,表示我們沒有看到的索引為零的單詞。通過這兩個例子,我們將詞匯量限制為六個數字。對于大塊文本,我們可以選擇我們想要保留多少單詞,并且通常保留最常用的單詞,用零索引標記其他所有單詞。
如果單詞`learning`的數值為 4,單詞`makes`的數值為 2,則自然會認為`learning`是`makes`的兩倍。由于我們不希望單詞之間存在這種類型的數字關系,我們可以假設這些數字代表的是類別,而不是關系數字。
另一個問題是這兩個句子的大小不同。我們所做的每個觀察(在這種情況下,句子)需要具有與我們希望創建的模型相同的大小輸入。為了解決這個問題,我們必須在稀疏向量中創建每個句子,如果該單詞出現在該索引中,則該特定索引中的值為 1:
| `TensorFlow` | `makes` | `machine` | `learning` | `easy` |
| --- | --- | --- | --- | --- |
| 1 | 2 | 3 | 4 | 5 |
```py
first_sentence = [0,1,1,1,1,1]
```
為了進一步解釋前面的向量,我們的詞匯由六個不同的單詞組成(五個已知單詞和一個未知單詞)。對于這些單詞中的每一個,我們要么具有零值或 1 值。零表示單詞不出現在我們的句子中,1 表示它至少出現一次。因此值為零表示該單詞不會出現,值為 1 表示它出現
| `machine` | `learning` | `is` | `easy` |
| --- | --- | --- | --- |
| 3 | 4 | 0 | 5 |
```py
second_sentence = [1,0,0,1,1,1]
```
這種方法的缺點是我們失去了任何詞序的指示。兩個句子`TensorFlow makes machine learning easy`和`machine learning makes TensorFlow easy`將產生相同的句子向量。
值得注意的是,這些向量的長度等于我們選擇的詞匯量的大小。選擇非常大的詞匯量是很常見的,因此這些句子向量可能非常稀疏。這種類型的嵌入稱為詞袋。我們將在下一節中實現這一點。
另一個缺點是單詞`is`和`TensorFlow`具有相同的數字索引值:1。有意義的是,單詞`is`可能不如單詞`TensorFlow`的出現重要。
我們將在本章中探索不同類型的嵌入,試圖解決這些問題,但首先我們將開始實現字袋算法。
# 使用詞袋嵌入
在本節中,我們將首先向您展示如何使用 TensorFlow 中的詞袋嵌入。這種映射是我們在介紹中介紹的。在這里,我們將向您展示如何使用此類嵌入進行垃圾郵件預測。
## 準備
為了說明如何在文本數據集中使用詞袋,我們將使用來自 UCI 機器學習數據倉庫的[垃圾郵件電話文本數據庫](https://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection)。這是垃圾郵件或非垃圾郵件(非垃圾郵件)的電話短信集合。我們將下載此數據,將其存儲以備將來使用,然后繼續使用詞袋方法來預測文本是否為垃圾郵件。將在詞袋算法上運行的模型將是沒有隱藏層的邏輯模型。我們將使用批量大小為 1 的隨機訓練,并在最后的保持測試集上計算精度。
## 操作步驟
對于這個例子,我們將首先獲取數據,正則化和分割文本,通過嵌入函數運行它,并訓練邏輯函數來預測垃圾郵件:
1. 第一項任務是為此任務導入必要的庫。在通常的庫中,我們需要一個`.zip`文件庫來解壓縮來自 UCI 機器學習網站的數據,我們從中檢索它:
```py
import tensorflow as tf
import matplotlib.pyplot as plt
import os
import numpy as np
import csv
import string
import requests
import io
from zipfile import ZipFile
from tensorflow.contrib import learn
sess = tf.Session()
```
1. 我們不會在每次運行腳本時下載文本數據,而是保存它并檢查文件之前是否已保存。如果我們想要更改腳本的參數,這可以防止我們反復下載數據。下載此數據后,我們將提取輸入和目標數據,并將目標更改為`1`以查找垃圾郵件,將`0`更改為非垃圾郵件:
```py
save_file_name = os.path.join('temp','temp_spam_data.csv')
if os.path.isfile(save_file_name):
text_data = []
with open(save_file_name, 'r') as temp_output_file:
reader = csv.reader(temp_output_file)
for row in reader:
text_data.append(row)
else:
zip_url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip'
r = requests.get(zip_url)
z = ZipFile(io.BytesIO(r.content))
file = z.read('SMSSpamCollection')
# Format Data
text_data = file.decode()
text_data = text_data.encode('ascii',errors='ignore')
text_data = text_data.decode().split('\n')
text_data = [x.split('\t') for x in text_data if len(x)>=1]
# And write to csv
with open(save_file_name, 'w') as temp_output_file:
writer = csv.writer(temp_output_file)
writer.writerows(text_data)
texts = [x[1] for x in text_data]
target = [x[0] for x in text_data]
# Relabel 'spam' as 1, 'ham' as 0
target = [1 if x=='spam' else 0 for x in target]
```
1. 為了減少潛在的詞匯量,我們將文本正則化。為此,我們消除了文本中大小寫和數字的影響。使用以下代碼:
```py
# Convert to lower case
texts = [x.lower() for x in texts]
# Remove punctuation
texts = [''.join(c for c in x if c not in string.punctuation) for x in texts]
# Remove numbers
texts = [''.join(c for c in x if c not in '0123456789') for x in texts]
# Trim extra whitespace
texts = [' '.join(x.split()) for x in texts]
```
1. 我們還必須確定最大句子大小。為此,我們將查看數據集中文本長度的直方圖。我們可以看到一個很好的截止可能是 25 個字左右。使用以下代碼:
```py
# Plot histogram of text lengths
text_lengths = [len(x.split()) for x in texts]
text_lengths = [x for x in text_lengths if x < 50]
plt.hist(text_lengths, bins=25)
plt.title('Histogram of # of Words in Texts')
sentence_size = 25
min_word_freq = 3
```
由此,我們將得到以下繪圖:

圖 1:數據中每個文本中單詞數的直方圖。
We use this to establish a maximum length of words to consider in each text. We set this to 25 words, but it can easily be set to 30 or 40 as well.
1. TensorFlow 有一個內置的處理工具,用于確定名為`VocabularyProcessor()`的詞匯嵌入,它位于`learn.preprocessing`庫中。請注意,您可能會使用此函數獲得已棄用的警告:
```py
vocab_processor = learn.preprocessing.VocabularyProcessor(sentence_size, min_frequency=min_word_freq)
vocab_processor.fit_transform(texts)
transformed_texts = np.array([x for x in vocab_processor.transform(texts)])
embedding_size = len(np.unique(transformed_texts))
```
1. 現在我們將數據分成 80-20 訓練和測試集:
```py
train_indices = np.random.choice(len(texts), round(len(texts)*0.8), replace=False)
test_indices = np.array(list(set(range(len(texts))) - set(train_indices)))
texts_train = [x for ix, x in enumerate(texts) if ix in train_indices]
texts_test = [x for ix, x in enumerate(texts) if ix in test_indices]
target_train = [x for ix, x in enumerate(target) if ix in train_indices]
target_test = [x for ix, x in enumerate(target) if ix in test_indices]
```
1. 接下來,我們聲明單詞的嵌入矩陣。句子詞將被翻譯成指數。這些索引將被轉換為單熱編碼的向量,我們可以使用單位矩陣創建,這將是我們的單詞嵌入的大小。我們將使用此矩陣查找每個單詞的稀疏向量,并將它們一起添加到稀疏句子向量中。使用以下代碼執行此操作:
```py
identity_mat = tf.diag(tf.ones(shape=[embedding_size]))
```
1. 由于我們最終會執行邏輯回歸來預測垃圾郵件的概率,因此我們需要聲明邏輯回歸變量。然后我們也可以聲明我們的數據占位符。值得注意的是,`x_data`輸入占位符應該是整數類型,因為它將用于查找我們的單位矩陣的行索引。 TensorFlow 要求此查找為整數:
```py
A = tf.Variable(tf.random_normal(shape=[embedding_size,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
# Initialize placeholders
x_data = tf.placeholder(shape=[sentence_size], dtype=tf.int32)
y_target = tf.placeholder(shape=[1, 1], dtype=tf.float32)
```
1. 現在我們將使用 TensorFlow 的嵌入查找函數,它將句子中單詞的索引映射到我們單位矩陣的單熱編碼向量。當我們有這個矩陣時,我們通過總結上述單詞向量來創建句子向量。使用以下代碼執行此操作:
```py
x_embed = tf.nn.embedding_lookup(identity_mat, x_data)
x_col_sums = tf.reduce_sum(x_embed, 0)
```
1. 現在我們為每個句子都有固定長度的句子向量,我們想要進行邏輯回歸。為此,我們需要聲明實際的模型操作。由于我們一次只做一個數據點(隨機訓練),我們將擴展輸入的維度并對其進行線性回歸操作。請記住,TensorFlow 具有包含 sigmoid 函數的損失函數,因此我們不需要在此輸出中包含它:
```py
x_col_sums_2D = tf.expand_dims(x_col_sums, 0)
model_output = tf.add(tf.matmul(x_col_sums_2D, A), b)
```
1. 我們現在將聲明損失函數,預測操作和優化函數來訓練模型。使用以下代碼執行此操作:
```py
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=model_output, labels=y_target))
# Prediction operation
prediction = tf.sigmoid(model_output)
# Declare optimizer
my_opt = tf.train.GradientDescentOptimizer(0.001)
train_step = my_opt.minimize(loss)
```
1. 接下來,我們將在開始訓練生成之前初始化圖變量:
```py
init = tf.global_variables_initializer()
sess.run(init)
```
1. 現在我們將開始對句子進行迭代。 TensorFlow 的`vocab_processor.fit()`函數是一次運行一個句子的生成器。我們將利用這一優勢,以便我們可以對物流模型進行隨機訓練。為了更好地了解準確率趨勢,我們將保留過去 50 個訓練步驟的平均值。如果我們只是繪制當前的一個,我們會看到 1 或 0,這取決于我們是否預測訓練數據是否正確。使用以下代碼執行此操作:
```py
loss_vec = []
train_acc_all = []
train_acc_avg = []
for ix, t in enumerate(vocab_processor.fit_transform(texts_train)):
y_data = [[target_train[ix]]]
sess.run(train_step, feed_dict={x_data: t, y_target: y_data})
temp_loss = sess.run(loss, feed_dict={x_data: t, y_target: y_data})
loss_vec.append(temp_loss)
if (ix+1)%10==0:
print('Training Observation #{}: Loss= {}'.format(ix+1, temp_loss))
# Keep trailing average of past 50 observations accuracy
# Get prediction of single observation
[[temp_pred]] = sess.run(prediction, feed_dict={x_data:t, y_target:y_data})
# Get True/False if prediction is accurate
train_acc_temp = target_train[ix]==np.round(temp_pred)
train_acc_all.append(train_acc_temp)
if len(train_acc_all) >= 50:
train_acc_avg.append(np.mean(train_acc_all[-50:]))
```
1. 這產生以下輸出:
```py
Starting Training Over 4459 Sentences.
Training Observation #10: Loss = 5.45322
Training Observation #20: Loss = 3.58226
Training Observation #30: Loss = 0.0
...
Training Observation #4430: Loss = 1.84636
Training Observation #4440: Loss = 1.46626e-05
Training Observation #4450: Loss = 0.045941
```
1. 為了獲得測試集的準確率,我們重復前面的過程,但僅限于預測操作,而不是測試集的訓練操作:
```py
print('Getting Test Set Accuracy')
test_acc_all = []
for ix, t in enumerate(vocab_processor.fit_transform(texts_test)):
y_data = [[target_test[ix]]]
if (ix+1)%50==0:
print('Test Observation #{}'.format(ix+1))
# Keep trailing average of past 50 observations accuracy
# Get prediction of single observation
[[temp_pred]] = sess.run(prediction, feed_dict={x_data:t, y_target:y_data})
# Get True/False if prediction is accurate
test_acc_temp = target_test[ix]==np.round(temp_pred)
test_acc_all.append(test_acc_temp)
print('\nOverall Test Accuracy: {}'.format(np.mean(test_acc_all)))
Getting Test Set Accuracy For 1115 Sentences.
Test Observation #10
Test Observation #20
Test Observation #30
...
Test Observation #1000
Test Observation #1050
Test Observation #1100
Overall Test Accuracy: 0.8035874439461883
```
## 工作原理
在本例中,我們使用了來自 UCI 機器學習庫的垃圾郵件文本數據。我們使用 TensorFlow 的詞匯處理函數來創建標準化詞匯表來處理和創建句子向量,這些句子向量是每個文本的單詞向量的總和。我們使用這個句子向量與邏輯回歸并獲得 80% 準確率模型來預測特定文本是否是垃圾郵件。
## 更多
值得一提的是限制句子(或文本)大小的動機。在此示例中,我們將文本大小限制為 25 個單詞。這是詞袋的常見做法,因為它限制了文本長度對預測的影響。你可以想象,如果我們找到一個單詞,例如`meeting`,它可以預測文本是非垃圾郵件(而不是垃圾郵件),那么垃圾郵件可能會通過在最后輸入該單詞的多次出現來實現。實際上,這是目標數據不平衡的常見問題。在這種情況下可能會出現不平衡的數據,因為垃圾郵件可能很難找到,而非垃圾郵件可能很容易找到。由于這個事實,我們創建的詞匯可能嚴重偏向于我們數據的非垃圾郵件部分中表示的單詞(更多非垃圾郵件意味著更多的單詞在非垃圾郵件中表示而不是垃圾郵件)。如果我們允許無限長度的文本,那么垃圾郵件發送者可能會利用這一點并創建非常長的文本,這些文本在我們的邏輯模型中觸發非垃圾郵件詞因素的概率更高。
在下一節中,我們將嘗試通過使用單詞出現的頻率來更好地解決此問題,以確定單詞嵌入的值。
# 實現 TF-IDF
由于我們可以為每個單詞選擇嵌入,我們可能會決定更改某些單詞的加權。一種這樣的策略是增加有用的單詞和減輕過度常見或罕見單詞的權重。我們將在此秘籍中探索的嵌入是嘗試實現此目的。
## 準備
TF-IDF 是一個縮寫,代表文本頻率 - 反向文檔頻率。該術語基本上是每個單詞的文本頻率和反向文檔頻率的乘積。
在前面的秘籍中,我們介紹了詞袋方法,它為句子中每個單詞的出現賦值為 1。這可能并不理想,因為每個類別的句子(前一個秘籍中的垃圾郵件和非垃圾郵件)很可能具有`the`,`and`和其他單詞的相同頻率,而諸如`Viagra`和`sale`之類的單詞]可能應該更加重視查明文本是否是垃圾郵件。
首先,我們要考慮詞頻。在這里,我們考慮單個條目中單詞出現的頻率。這部分(TF)的目的是找到在每個條目中看起來很重要的項。
但是`the`和`and`等詞可能會在每個條目中頻繁出現。我們希望減輕這些單詞的重要性,因此將前面的文本頻率(TF)乘以整個文檔頻率的倒數可能有助于找到重要的單詞。然而,由于文本集(語料庫)可能非常大,因此通常采用逆文檔頻率的對數。這為我們留下了每個文檔條目中每個單詞的 TF-IDF 的以下公式:

這里`w[tf]`是文檔中的單詞頻率,`w[df]`是所有文檔中這些單詞的總頻率。有意義的是,TF-IDF 的高值可能表示在確定文檔內容時非常重要的單詞。
創建 TF-IDF 向量要求我們將所有文本加載到內存中,并在開始訓練模型之前計算每個單詞的出現次數。因此,它沒有在 TensorFlow 中完全實現,因此我們將使用 scikit-learn 來創建我們的 TF-IDF 嵌入,但是使用 TensorFlow 來適應邏輯模型。
## 操作步驟
我們將按如下方式處理秘籍:
1. 我們將從加載必要的庫開始。這次,我們正在為我們的文本加載 scikit-learn TF-IDF 預處理庫。使用以下代碼執行此操作:
```py
import tensorflow as tf
import matplotlib.pyplot as plt
import csv
import numpy as np
import os
import string
import requests
import io
import nltk
from zipfile import ZipFile
from sklearn.feature_extraction.text import TfidfVectorizer
```
1. 我們將開始一個圖會話,并為我們的詞匯表聲明我們的批量大小和最大特征大小:
```py
sess = tf.Session()
batch_size= 200
max_features = 1000
```
1. 接下來,我們將從 Web 或我們的`temp`數據文件夾中加載數據(如果我們之前已保存過)。使用以下代碼執行此操作:
```py
save_file_name = os.path.join('temp','temp_spam_data.csv')
if os.path.isfile(save_file_name):
text_data = []
with open(save_file_name, 'r') as temp_output_file:
reader = csv.reader(temp_output_file)
for row in reader:
text_data.append(row)
else:
zip_url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip'
r = requests.get(zip_url)
z = ZipFile(io.BytesIO(r.content))
file = z.read('SMSSpamCollection')
# Format Data
text_data = file.decode()
text_data = text_data.encode('ascii',errors='ignore')
text_data = text_data.decode().split('\n')
text_data = [x.split('\t') for x in text_data if len(x)>=1]
# And write to csv
with open(save_file_name, 'w') as temp_output_file:
writer = csv.writer(temp_output_file)
writer.writerows(text_data)
texts = [x[1] for x in text_data]
target = [x[0] for x in text_data]
# Relabel 'spam' as 1, 'ham' as 0
target = [1\. if x=='spam' else 0\. for x in target]
```
1. 就像前面的秘籍一樣,我們將通過將所有內容轉換為小寫,刪除標點符號并刪除數字來減少詞匯量:
```py
# Lower case
texts = [x.lower() for x in texts]
# Remove punctuation
texts = [''.join(c for c in x if c not in string.punctuation) for x in texts]
# Remove numbers
texts = [''.join(c for c in x if c not in '0123456789') for x in texts]
# Trim extra whitespace
texts = [' '.join(x.split()) for x in texts]
```
1. 為了使用 scikt-learn 的 TF-IDF 處理函數,我們必須告訴它如何標記我們的句子。通過這個,我們只是指如何將句子分解為相應的單詞。我們已經為我們構建了一個很好的標記器:`nltk`包可以很好地將句子分解為相應的單詞:
```py
def tokenizer(text):
words = nltk.word_tokenize(text)
return words
# Create TF-IDF of texts
tfidf = TfidfVectorizer(tokenizer=tokenizer, stop_words='english', max_features=max_features)
sparse_tfidf_texts = tfidf.fit_transform(texts)
```
1. 接下來,我們將數據集分解為測試和訓練集。使用以下代碼執行此操作:
```py
train_indices = np.random.choice(sparse_tfidf_texts.shape[0], round(0.8*sparse_tfidf_texts.shape[0]), replace=False)
test_indices = np.array(list(set(range(sparse_tfidf_texts.shape[0])) - set(train_indices)))
texts_train = sparse_tfidf_texts[train_indices]
texts_test = sparse_tfidf_texts[test_indices]
target_train = np.array([x for ix, x in enumerate(target) if ix in train_indices])
target_test = np.array([x for ix, x in enumerate(target) if ix in test_indices])
```
1. 現在我們聲明我們的邏輯回歸模型變量和我們的數據占位符:
```py
A = tf.Variable(tf.random_normal(shape=[max_features,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
# Initialize placeholders
x_data = tf.placeholder(shape=[None, max_features], dtype=tf.float32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
```
1. 我們現在可以聲明模型操作和損失函數。請記住,邏輯回歸的 sigmoid 部分在我們的損失函數中。使用以下代碼執行此操作:
```py
model_output = tf.add(tf.matmul(x_data, A), b)
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=model_output, labels=y_target))
```
1. 我們將預測和精度函數添加到圖中,以便在我們的模型訓練時我們可以看到訓練和測試集的準確率:
```py
prediction = tf.round(tf.sigmoid(model_output))
predictions_correct = tf.cast(tf.equal(prediction, y_target), tf.float32)
accuracy = tf.reduce_mean(predictions_correct)
```
1. 然后我們將聲明一個優化器并初始化我們的圖變量:
```py
my_opt = tf.train.GradientDescentOptimizer(0.0025)
train_step = my_opt.minimize(loss)
# Intitialize Variables
init = tf.global_variables_initializer()
sess.run(init)
```
1. 我們現在將訓練我們的模型超過 10,000 代,并記錄每 100 代的測試/訓練損失和準確率,每 500 代打印一次。使用以下代碼執行此操作:
```py
train_loss = []
test_loss = []
train_acc = []
test_acc = []
i_data = []
for i in range(10000):
rand_index = np.random.choice(texts_train.shape[0], size=batch_size)
rand_x = texts_train[rand_index].todense()
rand_y = np.transpose([target_train[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
# Only record loss and accuracy every 100 generations
if (i+1)%100==0:
i_data.append(i+1)
train_loss_temp = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
train_loss.append(train_loss_temp)
test_loss_temp = sess.run(loss, feed_dict={x_data: texts_test.todense(), y_target: np.transpose([target_test])})
test_loss.append(test_loss_temp)
train_acc_temp = sess.run(accuracy, feed_dict={x_data: rand_x, y_target: rand_y})
train_acc.append(train_acc_temp)
test_acc_temp = sess.run(accuracy, feed_dict={x_data: texts_test.todense(), y_target: np.transpose([target_test])})
test_acc.append(test_acc_temp)
if (i+1)%500==0:
acc_and_loss = [i+1, train_loss_temp, test_loss_temp, train_acc_temp, test_acc_temp]
acc_and_loss = [np.round(x,2) for x in acc_and_loss]
print('Generation # {}. Train Loss (Test Loss): {:.2f} ({:.2f}). Train Acc (Test Acc): {:.2f} ({:.2f})'.format(*acc_and_loss))
```
1. 這產生以下輸出:
```py
Generation # 500\. Train Loss (Test Loss): 0.69 (0.73). Train Acc (Test Acc): 0.62 (0.57)
Generation # 1000\. Train Loss (Test Loss): 0.62 (0.63). Train Acc (Test Acc): 0.68 (0.66)
...
Generation # 9500\. Train Loss (Test Loss): 0.39 (0.45). Train Acc (Test Acc): 0.89 (0.85)
Generation # 10000\. Train Loss (Test Loss): 0.48 (0.45). Train Acc (Test Acc): 0.84 (0.85)
```
以下是繪制訓練和測試裝置的準確率和損耗的繪圖:

圖 2:根據 TF-IDF 值構建的物流垃圾郵件模型的交叉熵損失
訓練和測試精度圖如下:

圖 3:根據 TF-IDF 值構建的邏輯垃圾郵件模型的訓練和測試集精度
## 工作原理
使用模型的 TF-IDF 值增加了我們對先前的詞袋模型的預測,從 80% 的準確率到接近 90% 的準確率。我們通過使用 scikit-learn 的 TF-IDF 詞匯處理函數并使用這些 TF-IDF 值進行邏輯回歸來實現這一目標。
## 更多
雖然我們可能已經解決了重要性這個問題,但我們還沒有解決字序問題。詞袋和 TF-IDF 都沒有考慮句子中的單詞的順序特征。我們將在接下來的幾節中嘗試解決這個問題,這將向我們介紹 word2vec 技術。
# 使用 Skip-Gram 嵌入
在之前的秘籍中,我們在訓練模型之前決定了我們的文本嵌入。使用神經網絡,我們可以使嵌入值成為訓練過程的一部分。我們將探索的第一個這樣的方法叫做 Skip-Gram 嵌入。
## 準備
在此秘籍之前,我們沒有考慮與創建單詞嵌入相關的單詞順序。 2013 年初,Tomas Mikolov 和谷歌的其他研究人員撰寫了一篇關于創建解決這個問題的[單詞嵌入的論文](https://arxiv.org/abs/1301.3781),他們將他們的方法命名為 word2vec。
基本思想是創建捕獲單詞關系方面的單詞嵌入。我們試圖了解各種單詞如何相互關聯。這些嵌入可能如何表現的一些示例如下:
`king - man + woman = queen`
`India pale ale - hops + malt = stout`
如果我們只考慮它們之間的位置關系,我們可能會實現這樣的數字表示。如果我們能夠分析足夠大的相關文檔來源,我們可能會發現在我們的文本中,`king`,`man`和`queen`這兩個詞在彼此之間相互提及。如果我們也知道`man`和`woman`以不同的方式相關,那么我們可以得出結論,`man`是`king`,因為`woman`是`queen`,依此類推。
為了找到這樣的嵌入,我們將使用一個神經網絡來預測給定輸入字的周圍單詞。我們可以輕松地切換它并嘗試在給定一組周圍單詞的情況下預測目標單詞,但我們將從前面的方法開始。兩者都是 word2vec 過程的變體,但是從目標詞預測周圍詞(上下文)的前述方法稱為 Skip-Gram 模型。在下一個秘籍中,我們將實現另一個方法,從上下文預測目標詞,這稱為連續詞袋方法(CBOW):

圖 4:word2vec 的 Skip-Gram 實現的圖示。 Skip-Gram 預測目標詞的上下文窗口(每側窗口大小為 1)。
對于這個秘籍,我們將在康奈爾大學的一組電影評論數據上實現 [Skip-Gram 模型](http://www.cs.cornell.edu/people/pabo/movie-review-data/)。 word2vec 的 CBOW 方法將在下一個秘籍中實現。
## 操作步驟
對于這個秘籍,我們將創建幾個輔助函數。這些函數將加載數據,正則化文本,生成詞匯表并生成數據批量。只有在這之后我們才開始訓練我們的單詞嵌入。為了清楚起見,我們不是預測任何目標變量,而是我們將擬合單詞嵌入:
1. 首先,我們將加載必要的庫并啟動圖會話:
```py
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import random
import os
import string
import requests
import collections
import io
import tarfile
import urllib.request
from nltk.corpus import stopwords
sess = tf.Session()
```
1. 然后我們聲明一些模型參數。我們將一次查看 50 對單詞嵌入(批量大小)。每個單詞的嵌入大小將是一個長度為 200 的向量,我們只考慮 10,000 個最常用的單詞(每隔一個單詞將被歸類為未知單詞)。我們將訓練 5 萬代,并每 500 代打印一次。然后我們將聲明一個我們將在損失函數中使用的`num_sampled`變量(我們將在后面解釋),并且我們還聲明了我們的 Skip-Gram 窗口大小。在這里,我們將窗口大小設置為 2,因此我們將查看目標每側的周圍兩個單詞。我們將通過名為`nltk`的 Python 包設置我們的停用詞。我們還想要一種方法來檢查我們的單詞嵌入是如何執行的,因此我們將選擇一些常見的電影評論單詞并從每 2,000 次迭代中打印出最近的鄰居單詞:
```py
batch_size = 50
embedding_size = 200
vocabulary_size = 10000
generations = 50000
print_loss_every = 500
num_sampled = int(batch_size/2)
window_size = 2
stops = stopwords.words('english')
print_valid_every = 2000
valid_words = ['cliche', 'love', 'hate', 'silly', 'sad']
```
1. 接下來,我們將聲明我們的數據加載函數,該函數會檢查以確保在下載之前我們沒有下載數據。否則,如果之前保存了數據,它將從磁盤加載數據。使用以下代碼執行此操作:
```py
def load_movie_data():
save_folder_name = 'temp'
pos_file = os.path.join(save_folder_name, 'rt-polarity.pos')
neg_file = os.path.join(save_folder_name, 'rt-polarity.neg')
# Check if files are already downloaded
if os.path.exists(save_folder_name):
pos_data = []
with open(pos_file, 'r') as temp_pos_file:
for row in temp_pos_file:
pos_data.append(row)
neg_data = []
with open(neg_file, 'r') as temp_neg_file:
for row in temp_neg_file:
neg_data.append(row)
else: # If not downloaded, download and save
movie_data_url = 'http://www.cs.cornell.edu/people/pabo/movie-review-data/rt-polaritydata.tar.gz'
stream_data = urllib.request.urlopen(movie_data_url)
tmp = io.BytesIO()
while True:
s = stream_data.read(16384)
if not s:
break
tmp.write(s)
stream_data.close()
tmp.seek(0)
tar_file = tarfile.open(fileobj=tmp, mode='r:gz')
pos = tar_file.extractfile('rt-polaritydata/rt-polarity.pos')
neg = tar_file.extractfile('rt-polaritydata/rt-polarity.neg')
# Save pos/neg reviews
pos_data = []
for line in pos:
pos_data.append(line.decode('ISO-8859-1').encode('ascii',errors='ignore').decode())
neg_data = []
for line in neg:
neg_data.append(line.decode('ISO-8859-1').encode('ascii',errors='ignore').decode())
tar_file.close()
# Write to file
if not os.path.exists(save_folder_name):
os.makedirs(save_folder_name)
# Save files
with open(pos_file, 'w') as pos_file_handler:
pos_file_handler.write(''.join(pos_data))
with open(neg_file, 'w') as neg_file_handler:
neg_file_handler.write(''.join(neg_data))
texts = pos_data + neg_data
target = [1]*len(pos_data) + [0]*len(neg_data)
return(texts, target)
texts, target = load_movie_data()
```
1. 接下來,我們將為文本創建正則化函數。此函數將輸入字符串列表并使其為小寫,刪除標點,刪除數字,刪除額外的空格,并刪除停用詞。使用以下代碼執行此操作:
```py
def normalize_text(texts, stops):
# Lower case
texts = [x.lower() for x in texts]
# Remove punctuation
texts = [''.join(c for c in x if c not in string.punctuation) for x in texts]
# Remove numbers
texts = [''.join(c for c in x if c not in '0123456789') for x in texts]
# Remove stopwords
texts = [' '.join([word for word in x.split() if word not in (stops)]) for x in texts]
# Trim extra whitespace
texts = [' '.join(x.split()) for x in texts]
return(texts)
texts = normalize_text(texts, stops)
```
1. 為了確保我們所有的電影評論都能提供信息,我們應該確保它們足夠長,以包含重要的單詞關系。我們會隨意將其設置為三個或更多單詞:
```py
target = [target[ix] for ix, x in enumerate(texts) if len(x.split()) > 2]
texts = [x for x in texts if len(x.split()) > 2]
```
1. 為了構建我們的詞匯表,我們將創建一個函數來創建一個帶有計數的單詞字典。任何不常見的詞都不會使我們的詞匯量大小被截止,將被標記為`RARE`。使用以下代碼執行此操作:
```py
def build_dictionary(sentences, vocabulary_size):
# Turn sentences (list of strings) into lists of words
split_sentences = [s.split() for s in sentences]
words = [x for sublist in split_sentences for x in sublist]
# Initialize list of [word, word_count] for each word, starting with unknown
count = [['RARE', -1]]
# Now add most frequent words, limited to the N-most frequent (N=vocabulary size)
count.extend(collections.Counter(words).most_common(vocabulary_size-1))
# Now create the dictionary
word_dict = {}
# For each word, that we want in the dictionary, add it, then make it the value of the prior dictionary length
for word, word_count in count:
word_dict[word] = len(word_dict)
return(word_dict)
```
1. 我們需要一個函數將一個句子列表轉換為單詞索引列表,我們可以將它們傳遞給嵌入查找函數。使用以下代碼執行此操作:
```py
def text_to_numbers(sentences, word_dict):
# Initialize the returned data
data = []
for sentence in sentences:
sentence_data = []
# For each word, either use selected index or rare word index
for word in sentence:
if word in word_dict:
word_ix = word_dict[word]
else:
word_ix = 0
sentence_data.append(word_ix)
data.append(sentence_data)
return data
```
1. 現在我們可以實際創建我們的字典并將我們的句子列表轉換為單詞索引列表:
```py
word_dictionary = build_dictionary(texts, vocabulary_size)
word_dictionary_rev = dict(zip(word_dictionary.values(), word_dictionary.keys()))
text_data = text_to_numbers(texts, word_dictionary)
```
1. 從前面的單詞字典中,我們可以查找我們在步驟 2 中選擇的驗證字的索引。使用以下代碼執行此操作:
```py
valid_examples = [word_dictionary[x] for x in valid_words]
```
1. 我們現在將創建一個將返回 Skip-Gram 批次的函數。我們想訓練一對單詞,其中一個單詞是訓練輸入(來自我們窗口中心的目標單詞),另一個單詞是從窗口中選擇的。例如,句子`the cat in the hat`可能導致(輸入,輸出)對,如下所示:(`the`,`in`),(`cat`,`in`),(`the`,`in`),(`hat`,`in`)如果是目標詞,我們每個方向的窗口大小為 2:
```py
def generate_batch_data(sentences, batch_size, window_size, method='skip_gram'):
# Fill up data batch
batch_data = []
label_data = []
while len(batch_data) < batch_size:
# select random sentence to start
rand_sentence = np.random.choice(sentences)
# Generate consecutive windows to look at
window_sequences = [rand_sentence[max((ix-window_size),0):(ix+window_size+1)] for ix, x in enumerate(rand_sentence)]
# Denote which element of each window is the center word of interest
label_indices = [ix if ix<window_size else window_size for ix,x in enumerate(window_sequences)]
# Pull out center word of interest for each window and create a tuple for each window
if method=='skip_gram':
batch_and_labels = [(x[y], x[:y] + x[(y+1):]) for x,y in zip(window_sequences, label_indices)]
# Make it in to a big list of tuples (target word, surrounding word)
tuple_data = [(x, y_) for x,y in batch_and_labels for y_ in y]
else:
raise ValueError('Method {} not implmented yet.'.format(method))
# extract batch and labels
batch, labels = [list(x) for x in zip(*tuple_data)]
batch_data.extend(batch[:batch_size])
label_data.extend(labels[:batch_size])
# Trim batch and label at the end
batch_data = batch_data[:batch_size]
label_data = label_data[:batch_size]
# Convert to numpy array
batch_data = np.array(batch_data)
label_data = np.transpose(np.array([label_data]))
return batch_data, label_data
```
1. 我們現在可以初始化嵌入矩陣,聲明占位符,并初始化嵌入查找函數。使用以下代碼執行此操作:
```py
embeddings = tf.Variable(tf.random_uniform([vocabulary_size,
embedding_size], -1.0, 1.0))
# Create data/target placeholders
x_inputs = tf.placeholder(tf.int32, shape=[batch_size])
y_target = tf.placeholder(tf.int32, shape=[batch_size, 1])
valid_dataset = tf.constant(valid_examples, dtype=tf.int32)
# Lookup the word embedding:
embed = tf.nn.embedding_lookup(embeddings, x_inputs)
```
1. 損失函數應該是諸如`softmax`之類的東西,它計算預測錯誤單詞類別時的損失。但由于我們的目標有 10,000 個不同的類別,因此非常稀疏。這種稀疏性導致關于模型的擬合或收斂的問題。為了解決這個問題,我們將使用稱為噪聲對比誤差的損失函數。這種 NCE 損失函數通過預測單詞類與隨機噪聲預測將我們的問題轉化為二元預測。`num_sampled`參數指定批量變成隨機噪聲的程度:
```py
nce_weights = tf.Variable(tf.truncated_normal([vocabulary_size,
embedding_size], stddev=1.0 / np.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))
loss = tf.reduce_mean(tf.nn.nce_loss(weights=nce_weights,
biases=nce_biases,
inputs=embed,
labels=y_target,
num_sampled=num_sampled,
num_classes=vocabulary_size))
```
1. 現在我們需要創建一種方法來查找附近的單詞到我們的驗證單詞。我們將通過計算驗證集和所有單詞嵌入之間的余弦相似性來完成此操作,然后我們可以為每個驗證字打印出最接近的單詞集。使用以下代碼執行此操作:
```py
norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keepdims=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)
```
1. 我們現在聲明我們的優化函數并初始化我們的模型變量:
```py
optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0).minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
```
1. 現在我們可以訓練我們的嵌入并在訓練期間打印損失和最接近我們驗證集的單詞。使用以下代碼執行此操作:
```py
loss_vec = []
loss_x_vec = []
for i in range(generations):
batch_inputs, batch_labels = generate_batch_data(text_data, batch_size, window_size)
feed_dict = {x_inputs : batch_inputs, y_target : batch_labels}
# Run the train step
sess.run(optimizer, feed_dict=feed_dict)
# Return the loss
if (i+1) % print_loss_every == 0:
loss_val = sess.run(loss, feed_dict=feed_dict)
loss_vec.append(loss_val)
loss_x_vec.append(i+1)
print("Loss at step {} : {}".format(i+1, loss_val))
# Validation: Print some random words and top 5 related words
if (i+1) % print_valid_every == 0:
sim = sess.run(similarity, feed_dict=feed_dict)
for j in range(len(valid_words)):
valid_word = word_dictionary_rev[valid_examples[j]]
top_k = 5 # number of nearest neighbors
nearest = (-sim[j, :]).argsort()[1:top_k+1]
log_str = "Nearest to {}:".format(valid_word)
for k in range(top_k):
close_word = word_dictionary_rev[nearest[k]]
log_str = "%s %s," % (log_str, close_word)
print(log_str)
```
> 在前面的代碼中,我們在調用`argsort`方法之前采用相似矩陣的否定。我們這樣做是因為我們想要找到從最高相似度值到最低相似度值的索引,而不是相反。
1. 這產生以下輸出:
```py
Loss at step 500 : 13.387781143188477
Loss at step 1000 : 7.240757465362549
Loss at step 49500 : 0.9395825862884521
Loss at step 50000 : 0.30323168635368347
Nearest to cliche: walk, intrigue, brim, eileen, dumber,
Nearest to love: plight, fiction, complete, lady, bartleby,
Nearest to hate: style, throws, players, fearlessness, astringent,
Nearest to silly: delivers, meow, regain, nicely, anger,
Nearest to sad: dizzying, variety, existing, environment, tunney,
```
## 工作原理
我們通過`Skip-Gram`方法在電影評論數據集上訓練了一個 word2vec 模型。我們下載了數據,將單詞轉換為帶有字典的索引,并將這些索引號用作嵌入查找,我們對其進行了訓練,以便附近的單詞可以相互預測。
## 更多
乍一看,我們可能期望驗證集的附近單詞集合是同義詞。事實并非如此,因為很少有同義詞實際上在句子中彼此相鄰。我們真正得到的是預測我們的數據集中哪些單詞彼此接近。我們希望使用這樣的嵌入將使預測更容易。
為了使用這些嵌入,我們必須使它們可重用并保存它們。我們將通過實現 CBOW 嵌入在下一個秘籍中執行此操作。
# 使用 CBOW 嵌入
在這個秘籍中,我們將實現 word2vec 的 CBOW(連續詞袋)方法。它與`Skip-Gram`方法非常相似,除了我們預測來自環境詞周圍窗口的單個目標詞。
## 準備
在這個秘籍中,我們將實現 word2vec 的`CBOW`方法。它與`Skip-Gram`方法非常相似,只是我們預測來自環境詞周圍窗口的單個目標詞。
在前面的示例中,我們將窗口和目標的每個組合視為一組配對的輸入和輸出,但是使用 CBOW,我們將周圍的窗口嵌入添加到一起以獲得一個嵌入來預測目標字嵌入:

圖 5:如何在一個例子的窗口上創建 CBOW 嵌入數據的描述(每側窗口大小為 1)
大多數代碼都保持不變,除了我們需要改變我們創建嵌入的方式以及如何從句子生成數據。
為了使代碼更易于閱讀,我們已將所有主要函數移動到同一目錄中名為`text_helpers.py`的單獨文件中。此函數保存數據加載,文本正則化,字典創建和批量生成函數。除非另有說明,否則這些函數與使用 Skip-Gram 嵌入秘籍中顯示的完全相同。
## 操作步驟
我們將按如下方式處理秘籍:
1. 我們將首先加載必要的庫,包括前面提到的`text_helpers.py`腳本,我們將把我們的函數用于文本加載和操作。然后我們將開始一個圖會話:
```py
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import random
import os
import pickle
import string
import requests
import collections
import io
import tarfile
import urllib.request
import text_helpers
from nltk.corpus import stopwords
sess = tf.Session()
```
1. 我們要確保在開始保存之前存在臨時數據和參數保存文件夾。使用以下代碼檢查:
```py
# Make a saving directory if it doesn't exist
data_folder_name = 'temp'
if not os.path.exists(data_folder_name):
os.makedirs(data_folder_name)
```
1. 然后我們將聲明模型的參數,這與我們在上一個秘籍中對`Skip-Gram`方法所做的類似:
```py
# Declare model parameters
batch_size = 500
embedding_size = 200
vocabulary_size = 2000
generations = 50000
model_learning_rate = 0.001
num_sampled = int(batch_size/2
window_size = 3
# Add checkpoints to training
save_embeddings_every = 5000
print_valid_every = 5000
print_loss_every = 100
# Declare stop words
stops = stopwords.words('english')
# We pick some test words. We are expecting synonyms to appear
valid_words = ['love', 'hate', 'happy', 'sad', 'man', 'woman']
```
1. 我們已將數據加載和文本正則化函數移動到我們在開始時導入的單獨文件中,此文件在 [Github 倉庫](https://github.com/nfmcclure/tensorflow_cookbook/tree/master/07_Natural_Language_Processing/05_Working_With_CBOW_Embeddings)和 [Packt 倉庫](https://github.com/PacktPublishing/TensorFlow-Machine-Learning-Cookbook-Second-Edition)中都可用。現在我們可以打電話給他們我們也只想要包含三個或更多單詞的評論。使用以下代碼:
```py
texts, target = text_helpers.load_movie_data(data_folder_name) texts = text_helpers.normalize_text(texts, stops) # Texts must contain at least 3 words target = [target[ix] for ix, x in enumerate(texts) if len(x.split()) > 2] texts = [x for x in texts if len(x.split()) > 2]
```
1. 現在我們將創建我們的詞匯詞典,這將幫助我們查找單詞。當我們想要打印出最接近我們驗證集的單詞時,我們還需要一個反向字典來查找索引中的單詞:
```py
word_dictionary = text_helpers.build_dictionary(texts,
vocabulary_size)
word_dictionary_rev = dict(zip(word_dictionary.values(), word_dictionary.keys()))
text_data = text_helpers.text_to_numbers(texts, word_dictionary)
# Get validation word keys
valid_examples = [word_dictionary[x] for x in valid_words]
```
1. 接下來,我們將初始化我們想要擬合的單詞嵌入,并聲明模型數據占位符。使用以下代碼執行此操作:
```py
embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
# Create data/target placeholders
x_inputs = tf.placeholder(tf.int32, shape=[batch_size,
2*window_size])
y_target = tf.placeholder(tf.int32, shape=[batch_size, 1])
valid_dataset = tf.constant(valid_examples, dtype=tf.int32)
```
1. 我們現在可以創建一種處理嵌入一詞的方法。由于 CBOW 模型添加了上下文窗口的嵌入,我們將創建一個循環并將所有嵌入添加到窗口中:
```py
# Lookup the word embeddings and
# Add together window embeddings:
embed = tf.zeros([batch_size, embedding_size])
for element in range(2*window_size):
embed += tf.nn.embedding_lookup(embeddings, x_inputs[:, element])
```
1. 我們將使用 TensorFlow 中內置的噪聲對比誤差損失函數,因為我們的分類輸出太稀疏,無法使 softmax 收斂,如下所示:
```py
# NCE loss parameters
nce_weights = tf.Variable(tf.truncated_normal([vocabulary_size,
embedding_size], stddev=1.0 / np.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))
# Declare loss function (NCE)
loss = tf.reduce_mean(tf.nn.nce_loss(weights=nce_weights,
biases=nce_biases,
inputs=embed,
labels=y_target,
num_sampled=num_sampled,
num_classes=vocabulary_size))
```
1. 就像我們在 Skip-Gram 秘籍中所做的那樣,我們將使用余弦相似性來打印離我們的驗證字數據集最近的單詞,以了解我們的嵌入如何工作。使用以下代碼執行此操作:
```py
norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keepdims=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)
```
1. 要保存嵌入,我們必須加載 TensorFlow `train.Saver`方法。這個方法默認保存整個圖,但是我們可以給它一個參數來保存嵌入變量,我們也可以給它一個特定的名稱。在這里,我們給它的名稱與圖中的變量名稱相同:
```py
saver = tf.train.Saver({"embeddings": embeddings})
```
1. 我們現在將聲明我們的優化函數并初始化我們的模型變量。使用以下代碼執行此操作:
```py
optimizer = tf.train.GradientDescentOptimizer(learning_rate=model_learning_rate).minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
```
1. 最后,我們可以遍歷我們的訓練步驟,打印出損失,并將我們指定的嵌入和字典保存到:
```py
loss_vec = []
loss_x_vec = []
for i in range(generations):
batch_inputs, batch_labels = text_helpers.generate_batch_data(text_data, batch_size, window_size, method='cbow')
feed_dict = {x_inputs : batch_inputs, y_target : batch_labels}
# Run the train step
sess.run(optimizer, feed_dict=feed_dict)
# Return the loss
if (i+1) % print_loss_every == 0:
loss_val = sess.run(loss, feed_dict=feed_dict)
loss_vec.append(loss_val)
loss_x_vec.append(i+1)
print('Loss at step {} : {}'.format(i+1, loss_val))
# Validation: Print some random words and top 5 related words
if (i+1) % print_valid_every == 0:
sim = sess.run(similarity, feed_dict=feed_dict)
for j in range(len(valid_words)):
valid_word = word_dictionary_rev[valid_examples[j]]
top_k = 5 # number of nearest neighbors
nearest = (-sim[j, :]).argsort()[1:top_k+1]
log_str = "Nearest to {}:".format(valid_word)
for k in range(top_k):
close_word = word_dictionary_rev[nearest[k]]
print_str = '{} {},'.format(log_str, close_word)
print(print_str)
# Save dictionary + embeddings
if (i+1) % save_embeddings_every == 0:
# Save vocabulary dictionary
with open(os.path.join(data_folder_name,'movie_vocab.pkl'), 'wb') as f:
pickle.dump(word_dictionary, f)
# Save embeddings
model_checkpoint_path = os.path.join(os.getcwd(),data_folder_name,'cbow_movie_embeddings.ckpt')
save_path = saver.save(sess, model_checkpoint_path)
print('Model saved in file: {}'.format(save_path))
```
1. 這產生以下輸出:
```py
Loss at step 100 : 62.04829025268555
Loss at step 200 : 33.182334899902344
...
Loss at step 49900 : 1.6794960498809814
Loss at step 50000 : 1.5071022510528564
Nearest to love: clarity, cult, cliched, literary, memory,
Nearest to hate: bringing, gifted, almost, next, wish,
Nearest to happy: ensemble, fall, courage, uneven, girls,
Nearest to sad: santa, devoid, biopic, genuinely, becomes,
Nearest to man: project, stands, none, soul, away,
Nearest to woman: crush, even, x, team, ensemble,
Model saved in file: .../temp/cbow_movie_embeddings.ckpt
```
1. `text_helpers.py`文件中除了一個函數之外的所有函數都具有直接來自上一個秘籍的函數。我們將通過添加`cbow`方法對`generate_batch_data()`函數稍加補充,如下所示:
```py
elif method=='cbow':
batch_and_labels = [(x[:y] + x[(y+1):], x[y]) for x,y in zip(window_sequences, label_indices)]
# Only keep windows with consistent 2*window_size
batch_and_labels = [(x,y) for x,y in batch_and_labels if len(x)==2*window_size]
batch, labels = [list(x) for x in zip(*batch_and_labels)]
```
## 工作原理
此秘籍與使用 Skip-Gram 創建嵌入非常相似。主要區別在于我們如何生成數據并組合嵌入。
對于這個秘籍,我們加載數據,正則化文本,創建詞匯詞典,使用字典查找嵌入,組合嵌入,并訓練神經網絡來預測目標詞。
## 更多
值得注意的是,`CBOW`方法訓練周圍窗口的累加嵌入以預測目標字。這樣做的一個結果是來自 word2vec 的`CBOW`方法具有`Skip-Gram`方法缺乏的平滑效果,并且認為這對于較小的文本數據集可能是優選的是合理的。
# 使用 word2vec 進行預測
在本文中,我們將使用先前學習的嵌入策略來執行分類。
## 準備
現在我們已經創建并保存了 CBOW 字嵌入,我們需要使用它們來對電影數據集進行情感預測。在本文中,我們將學習如何加載和使用預先訓練的嵌入,并使用這些嵌入來通過訓練邏輯線性模型來預測好的或壞的評論來執行情感分析。
情感分析是一項非常艱巨的任務,因為人類語言使得很難掌握所謂意義的真實含義的微妙之處和細微差別。諷刺,笑話和含糊不清的引用都使這項任務成倍增加。我們將在電影評論數據集上創建一個簡單的邏輯回歸,以查看我們是否可以從我們在上一個秘籍中創建并保存的 CBOW 嵌入中獲取任何信息。由于本文的重點是加載和使用已保存的嵌入,我們不會追求更復雜的模型。
## 操作步驟
我們將按如下方式處理秘籍:
1. 我們將首先加載必要的庫并啟動圖會話:
```py
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import random
import os
import pickle
import string
import requests
import collections
import io
import tarfile
import urllib.request
import text_helpers
from nltk.corpus import stopwords
sess = tf.Session()
```
1. 現在我們將聲明模型參數。嵌入大小應與我們用于創建前面的 CBOW 嵌入的嵌入大小相同。使用以下代碼執行此操作:
```py
embedding_size = 200
vocabulary_size = 2000
batch_size = 100
max_words = 100
stops = stopwords.words('english')
```
1. 我們將從我們創建的`text_helpers.py`文件加載和轉換文本數據。使用以下代碼執行此操作:
```py
texts, target = text_helpers.load_movie_data()
# Normalize text
print('Normalizing Text Data')
texts = text_helpers.normalize_text(texts, stops)
# Texts must contain at least 3 words
target = [target[ix] for ix, x in enumerate(texts) if len(x.split()) > 2]
texts = [x for x in texts if len(x.split()) > 2]
train_indices = np.random.choice(len(target), round(0.8*len(target)), replace=False)
test_indices = np.array(list(set(range(len(target))) - set(train_indices)))
texts_train = [x for ix, x in enumerate(texts) if ix in train_indices]
texts_test = [x for ix, x in enumerate(texts) if ix in test_indices]
target_train = np.array([x for ix, x in enumerate(target) if ix in train_indices])
target_test = np.array([x for ix, x in enumerate(target) if ix in test_indices])
```
1. 我們現在加載我們在擬合 CBOW 嵌入時創建的單詞字典。重要的是我們加載它以便我們具有從單詞到嵌入索引的完全相同的映射,如下所示:
```py
dict_file = os.path.join(data_folder_name, 'movie_vocab.pkl')
word_dictionary = pickle.load(open(dict_file, 'rb'))
```
1. 我們現在可以使用我們的單詞字典將我們加載的句子數據轉換為數字`numpy`數組:
```py
text_data_train = np.array(text_helpers.text_to_numbers(texts_train, word_dictionary))
text_data_test = np.array(text_helpers.text_to_numbers(texts_test, word_dictionary))
```
1. 由于電影評論的長度不同,我們將它們標準化,因此它們的長度都相同。在我們的例子中,我們將其設置為 100 個單詞。如果評論少于 100 個單詞,我們將用零填充它。使用以下代碼執行此操作:
```py
text_data_train = np.array([x[0:max_words] for x in [y+[0]*max_words for y in text_data_train]])
text_data_test = np.array([x[0:max_words] for x in [y+[0]*max_words for y in text_data_test]])
```
1. 現在我們將聲明我們的模型變量和占位符以進行邏輯回歸。使用以下代碼執行此操作:
```py
A = tf.Variable(tf.random_normal(shape=[embedding_size,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
# Initialize placeholders
x_data = tf.placeholder(shape=[None, max_words], dtype=tf.int32)
y_target = tf.placeholder(shape=[None, 1], dtype=tf.float32)
```
1. 為了讓 TensorFlow 恢復我們預先訓練的嵌入,我們必須首先給`Saver`方法一個變量來恢復,所以我們將創建一個嵌入變量,其形狀與我們將加載的嵌入相同:
```py
embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
```
1. 現在我們將`embedding_lookup`函數放在圖上,并將句子中所有單詞的平均嵌入。使用以下代碼執行此操作:
```py
embed = tf.nn.embedding_lookup(embeddings, x_data)
# Take average of all word embeddings in documents
embed_avg = tf.reduce_mean(embed, 1)
```
1. 接下來,我們將聲明我們的模型操作和損失函數,記住我們的損失函數已經內置了 sigmoid 操作,如下所示:
```py
model_output = tf.add(tf.matmul(embed_avg, A), b)
# Declare loss function (Cross Entropy loss)
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=model_output, labels=y_target))
```
1. 現在我們將向圖添加預測和精度函數,以便我們可以在使用以下代碼訓練模型時評估精度:
```py
prediction = tf.round(tf.sigmoid(model_output))
predictions_correct = tf.cast(tf.equal(prediction, y_target), tf.float32)
accuracy = tf.reduce_mean(predictions_correct)
```
1. 我們將聲明我們的優化函數并初始化以下模型變量:
```py
my_opt = tf.train.AdagradOptimizer(0.005)
train_step = my_opt.minimize(loss)
init = tf.global_variables_initializer()
sess.run(init)
```
1. 現在我們有一個隨機初始化嵌入,我們可以告訴`Saver`方法將我們之前的 CBOW 嵌入加載到嵌入變量中。使用以下代碼執行此操作:
```py
model_checkpoint_path = os.path.join(data_folder_name,'cbow_movie_embeddings.ckpt')
saver = tf.train.Saver({"embeddings": embeddings})
saver.restore(sess, model_checkpoint_path)
```
1. 現在我們可以開始訓練幾代。請注意,我們每 100 代就可以節省訓練和測試損失和準確率。我們只會每 500 代打印一次模型狀態,如下所示:
```py
train_loss = []
test_loss = []
train_acc = []
test_acc = []
i_data = []
for i in range(10000):
rand_index = np.random.choice(text_data_train.shape[0], size=batch_size)
rand_x = text_data_train[rand_index]
rand_y = np.transpose([target_train[rand_index]])
sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y})
# Only record loss and accuracy every 100 generations
if (i+1)%100==0:
i_data.append(i+1)
train_loss_temp = sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})
train_loss.append(train_loss_temp)
test_loss_temp = sess.run(loss, feed_dict={x_data: text_data_test, y_target: np.transpose([target_test])})
test_loss.append(test_loss_temp)
train_acc_temp = sess.run(accuracy, feed_dict={x_data: rand_x, y_target: rand_y})
train_acc.append(train_acc_temp)
test_acc_temp = sess.run(accuracy, feed_dict={x_data: text_data_test, y_target: np.transpose([target_test])})
test_acc.append(test_acc_temp)
if (i+1)%500==0:
acc_and_loss = [i+1, train_loss_temp, test_loss_temp, train_acc_temp, test_acc_temp]
acc_and_loss = [np.round(x,2) for x in acc_and_loss]
print('Generation # {}. Train Loss (Test Loss): {:.2f} ({:.2f}). Train Acc (Test Acc): {:.2f} ({:.2f})'.format(*acc_and_loss))
```
1. 結果如下:
```py
Generation # 500\. Train Loss (Test Loss): 0.70 (0.71). Train Acc (Test Acc): 0.52 (0.48)
Generation # 1000\. Train Loss (Test Loss): 0.69 (0.72). Train Acc (Test Acc): 0.56 (0.47)
...
Generation # 9500\. Train Loss (Test Loss): 0.69 (0.70). Train Acc (Test Acc): 0.57 (0.55)
Generation # 10000\. Train Loss (Test Loss): 0.70 (0.70). Train Acc (Test Acc): 0.59 (0.55)
```
1. 以下是繪制訓練和測試損失和準確率的代碼,我們每 100 代保存一次:
```py
# Plot loss over time
plt.plot(i_data, train_loss, 'k-', label='Train Loss')
plt.plot(i_data, test_loss, 'r--', label='Test Loss', linewidth=4)
plt.title('Cross Entropy Loss per Generation')
plt.xlabel('Generation')
plt.ylabel('Cross Entropy Loss')
plt.legend(loc='upper right')
plt.show()
# Plot train and test accuracy
plt.plot(i_data, train_acc, 'k-', label='Train Set Accuracy')
plt.plot(i_data, test_acc, 'r--', label='Test Set Accuracy', linewidth=4)
plt.title('Train and Test Accuracy')
plt.xlabel('Generation')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.show()
```
每代交叉熵損失的圖如下:
Figure 6: Here we observe the train and test loss over 10,000 generations
上述代碼的訓練圖和測試精度如下:

圖 7:我們可以觀察到訓練和測試裝置的準確率正在緩慢提高 10,000 代。值得注意的是,該模型表現非常差,并且僅比隨機預測器略好。
## 工作原理
我們加載了我們之前的 CBOW 嵌入并對平均嵌入評論進行了邏輯回歸。這里要注意的重要方法是我們如何將模型變量從磁盤加載到當前模型中已經初始化的變量。我們還必須記住在訓練嵌入之前存儲和加載我們創建的詞匯表。使用相同的嵌入時,從單詞到嵌入索引具有相同的映射非常重要。
## 更多
我們可以看到,我們在預測情感方面幾乎達到了 60% 的準確率。例如,要知道單詞`great;`背后的含義是一項艱巨的任務,它可以在評論中用于消極或積極的背景。
為了解決這個問題,我們希望以某種方式為文檔本身創建嵌入并解決情感問題。通常,整個評論是積極的,或者整個評論是否定的。我們可以利用這個優勢,我們將在下面的使用 doc2vec 以獲取情感分析方法中查看如何執行此操作。
# 使用 doc2vec 進行情感分析
既然我們知道如何訓練單詞嵌入,我們也可以擴展這些方法以進行文檔嵌入。我們將在以下部分中探討如何執行此操作。
## 準備
在前面關于 word2vec 方法的部分中,我們設法捕獲了單詞之間的位置關系。我們沒有做的是捕捉單詞與它們來自的文檔(或電影評論)之間的關系。 word2vec 的一個擴展來捕獲文檔效果,稱為 doc2vec。
doc2vec 的基本思想是引入文檔嵌入,以及可能有助于捕獲文檔基調的單詞嵌入。例如,只知道單詞`movie`和`love`彼此接近可能無法幫助我們確定評論的情感。評論可能是談論他們如何熱愛電影或他們如何不愛電影。但是如果評論足夠長并且在文檔中找到了更多否定詞,那么我們可以采用可以幫助我們預測后續詞語的整體語氣。
Doc2vec 只是為文檔添加了一個額外的嵌入矩陣,并使用一個單詞窗口加上文檔索引來預測下一個單詞。文檔中的所有文字窗口都具有相同的文檔索引。值得一提的是,考慮如何將文檔嵌入與單詞嵌入相結合是很重要的。我們通過對它們求和來將單詞嵌入組合在單詞窗口中。將這些嵌入與文檔嵌入相結合有兩種主要方式:通常,文檔嵌入要么添加到單詞嵌入中,要么連接到單詞嵌入的末尾。如果我們添加兩個嵌入,我們將文檔嵌入大小限制為與嵌入字大小相同的大小。如果我們連接,我們解除了這個限制,但增加了邏輯回歸必須處理的變量數量。為了便于說明,我們將向您展示如何處理此秘籍中的連接。但總的來說,對于較小的數據集,添加是更好的選擇。
第一步是將文檔和單詞嵌入適用于整個電影評論集。然后我們將進行訓練測試分組,訓練邏輯模型,看看我們是否可以更準確地預測評論情感。
## 操作步驟
我們將按如下方式處理秘籍:
1. 我們將從加載必要的庫并啟動圖會話開始,如下所示:
```py
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import random
import os
import pickle
import string
import requests
import collections
import io
import tarfile
import urllib.request
import text_helpers
from nltk.corpus import stopwords
sess = tf.Session()
```
1. 我們將加載電影評論語料庫,就像我們在前兩個秘籍中所做的那樣。使用以下代碼執行此操作:
```py
texts, target = text_helpers.load_movie_data()
```
1. 我們將聲明模型參數,如下所示:
```py
batch_size = 500
vocabulary_size = 7500
generations = 100000
model_learning_rate = 0.001
embedding_size = 200 # Word embedding size
doc_embedding_size = 100 # Document embedding size
concatenated_size = embedding_size + doc_embedding_size
num_sampled = int(batch_size/2)
window_size = 3 # How many words to consider to the left.
# Add checkpoints to training
save_embeddings_every = 5000
print_valid_every = 5000
print_loss_every = 100
# Declare stop words
stops = stopwords.words('english')
# We pick a few test words.
valid_words = ['love', 'hate', 'happy', 'sad', 'man', 'woman']
```
1. 我們將正則化電影評論,并確保每個電影評論都大于所需的窗口大小。使用以下代碼執行此操作:
```py
texts = text_helpers.normalize_text(texts, stops)
# Texts must contain at least as much as the prior window size
target = [target[ix] for ix, x in enumerate(texts) if len(x.split()) > window_size]
texts = [x for x in texts if len(x.split()) > window_size]
assert(len(target)==len(texts))
```
1. 現在我們將創建我們的單詞字典。請務必注意,我們不必創建文檔字典。文件索引只是文件的索引;每個文檔都有一個唯一的索引:
```py
word_dictionary = text_helpers.build_dictionary(texts, vocabulary_size)
word_dictionary_rev = dict(zip(word_dictionary.values(), word_dictionary.keys()))
text_data = text_helpers.text_to_numbers(texts, word_dictionary)
# Get validation word keys
valid_examples = [word_dictionary[x] for x in valid_words]
```
1. 接下來,我們將定義單詞嵌入和文檔嵌入。然后我們將聲明我們的噪聲對比損失參數。使用以下代碼執行此操作:
```py
embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
doc_embeddings = tf.Variable(tf.random_uniform([len(texts), doc_embedding_size], -1.0, 1.0))
# NCE loss parameters
nce_weights = tf.Variable(tf.truncated_normal([vocabulary_size, concatenated_size],
stddev=1.0 / np.sqrt(concatenated_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))
```
1. 我們現在將聲明 doc2vec 索引和目標詞索引的占位符。請注意,輸入索引的大小是窗口大小加 1。這是因為我們生成的每個數據窗口都有一個附加的文檔索引,如下所示:
```py
x_inputs = tf.placeholder(tf.int32, shape=[None, window_size + 1])
y_target = tf.placeholder(tf.int32, shape=[None, 1])
valid_dataset = tf.constant(valid_examples, dtype=tf.int32)
```
1. 現在我們必須創建嵌入函數,它將單詞嵌入加在一起,然后在最后連接文檔嵌入。使用以下代碼執行此操作:
```py
embed = tf.zeros([batch_size, embedding_size])
for element in range(window_size):
embed += tf.nn.embedding_lookup(embeddings, x_inputs[:, element])
doc_indices = tf.slice(x_inputs, [0,window_size],[batch_size,1])
doc_embed = tf.nn.embedding_lookup(doc_embeddings,doc_indices)
# concatenate embeddings
final_embed = tf.concat(axis=1, values=)
```
1. 我們還需要聲明一組驗證詞的余弦距離,我們可以經常打印出來以觀察 doc2vec 模型的進度。使用以下代碼執行此操作:
```py
loss = tf.reduce_mean(tf.nn.nce_loss(weights=nce_weights,
biases=nce_biases,
labels=y_target,
inputs=final_embed,
num_sampled=num_sampled,
num_classes=vocabulary_size))
# Create optimizer
optimizer =
tf.train.GradientDescentOptimizer(learning_rate=model_learning_rate)
train_step = optimizer.minimize(loss)
```
1. 我們還需要從一組驗證單詞中聲明余弦距離,我們可以經常打印出來以觀察 doc2vec 模型的進度。使用以下代碼執行此操作:
```py
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)
```
1. 為了以后保存我們的嵌入,我們將創建一個模型`saver`函數。然后我們可以初始化變量,這是我們開始訓練單詞嵌入之前的最后一步:
```py
saver = tf.train.Saver({"embeddings": embeddings, "doc_embeddings":
doc_embeddings})
init = tf.global_variables_initializer()
sess.run(init)
loss_vec = []
loss_x_vec = []
for i in range(generations):
batch_inputs, batch_labels = text_helpers.generate_batch_data(text_data, batch_size,
window_size, method='doc2vec')
feed_dict = {x_inputs : batch_inputs, y_target : batch_labels}
# Run the train step
sess.run(train_step, feed_dict=feed_dict)
# Return the loss
if (i+1) % print_loss_every == 0:
loss_val = sess.run(loss, feed_dict=feed_dict)
loss_vec.append(loss_val)
loss_x_vec.append(i+1)
print('Loss at step {} : {}'.format(i+1, loss_val))
# Validation: Print some random words and top 5 related words
if (i+1) % print_valid_every == 0:
sim = sess.run(similarity, feed_dict=feed_dict)
for j in range(len(valid_words)):
valid_word = word_dictionary_rev[valid_examples[j]]
top_k = 5 # number of nearest neighbors
nearest = (-sim[j, :]).argsort()[1:top_k+1]
log_str = "Nearest to {}:".format(valid_word)
for k in range(top_k):
close_word = word_dictionary_rev[nearest[k]]
log_str = '{} {},'.format(log_str, close_word)
print(log_str)
# Save dictionary + embeddings
if (i+1) % save_embeddings_every == 0:
# Save vocabulary dictionary
with open(os.path.join(data_folder_name,'movie_vocab.pkl'), 'wb') as f:
pickle.dump(word_dictionary, f)
# Save embeddings
model_checkpoint_path = os.path.join(os.getcwd(),data_folder_name,'doc2vec_movie_embeddings.ckpt')
save_path = saver.save(sess, model_checkpoint_path)
print('Model saved in file: {}'.format(save_path))
```
1. 這產生以下輸出:
```py
Loss at step 100 : 126.176816940307617
Loss at step 200 : 89.608322143554688
...
Loss at step 99900 : 17.733346939086914
Loss at step 100000 : 17.384489059448242
Nearest to love: ride, with, by, its, start,
Nearest to hate: redundant, snapshot, from, performances, extravagant,
Nearest to happy: queen, chaos, them, succumb, elegance,
Nearest to sad: terms, pity, chord, wallet, morality,
Nearest to man: of, teen, an, our, physical,
Nearest to woman: innocuous, scenes, prove, except, lady,
Model saved in file: /.../temp/doc2vec_movie_embeddings.ckpt
```
1. 現在我們已經訓練了 doc2vec 嵌入,我們可以在邏輯回歸中使用這些嵌入來預測評論情感。首先,我們為邏輯回歸設置了一些參數。使用以下代碼執行此操作:
```py
max_words = 20 # maximum review word length
logistic_batch_size = 500 # training batch size
```
1. 我們現在將數據集拆分為訓練集和測試集:
```py
train_indices = np.sort(np.random.choice(len(target),
round(0.8*len(target)), replace=False))
test_indices = np.sort(np.array(list(set(range(len(target))) -
set(train_indices))))
texts_train = [x for ix, x in enumerate(texts) if ix in train_indices]
texts_test = [x for ix, x in enumerate(texts) if ix in test_indices]
target_train = np.array([x for ix, x in enumerate(target) if ix in train_indices])
target_test = np.array([x for ix, x in enumerate(target) if ix in test_indices])
```
1. 接下來,我們將評論轉換為數字單詞索引,并將每個評論填充或裁剪為 20 個單詞,如下所示:
```py
text_data_train = np.array(text_helpers.text_to_numbers(texts_train, word_dictionary)) text_data_test = np.array(text_helpers.text_to_numbers(texts_test, word_dictionary)) # Pad/crop movie reviews to specific length text_data_train = np.array([x[0:max_words] for x in [y+[0]*max_words for y in text_data_train]]) text_data_test = np.array([x[0:max_words] for x in [y+[0]*max_words for y in text_data_test]])
```
1. 現在我們將聲明圖中與邏輯回歸模型相關的部分。我們將添加數據占位符,變量,模型操作和損失函數,如下所示:
```py
# Define Logistic placeholders
log_x_inputs = tf.placeholder(tf.int32, shape=[None, max_words + 1])
log_y_target = tf.placeholder(tf.int32, shape=[None, 1])
A = tf.Variable(tf.random_normal(shape=[concatenated_size,1]))
b = tf.Variable(tf.random_normal(shape=[1,1]))
# Declare logistic model (sigmoid in loss function)
model_output = tf.add(tf.matmul(log_final_embed, A), b)
# Declare loss function (Cross Entropy loss)
logistic_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=model_output,
labels=tf.cast(log_y_target, tf.float32)))
```
1. 我們需要創建另一個嵌入函數。前半部分中的嵌入函數在三個單詞(和文檔索引)的較小窗口上進行訓練,以預測下一個單詞。在這里,我們將采用相同的方式進行 20 字復習。使用以下代碼執行此操作:
```py
# Add together element embeddings in window:
log_embed = tf.zeros([logistic_batch_size, embedding_size])
for element in range(max_words):
log_embed += tf.nn.embedding_lookup(embeddings, log_x_inputs[:, element])
log_doc_indices = tf.slice(log_x_inputs, [0,max_words],[logistic_batch_size,1])
log_doc_embed = tf.nn.embedding_lookup(doc_embeddings,log_doc_indices)
# concatenate embeddings
log_final_embed = tf.concat(1, [log_embed, tf.squeeze(log_doc_embed)])
```
1. 接下來,我們將在圖上創建預測和準確率函數,以便我們可以在訓練生成過程中評估模型的表現。然后我們將聲明一個優化函數并初始化所有變量:
```py
prediction = tf.round(tf.sigmoid(model_output))
predictions_correct = tf.cast(tf.equal(prediction, tf.cast(log_y_target, tf.float32)), tf.float32)
accuracy = tf.reduce_mean(predictions_correct)
# Declare optimizer
logistic_opt = tf.train.GradientDescentOptimizer(learning_rate=0.01)
logistic_train_step = logistic_opt.minimize(logistic_loss, var_list=[A, b])
# Intitialize Variables
init = tf.global_variables_initializer()
sess.run(init)
```
1. 現在我們可以開始 Logistic 模型訓練了:
```py
train_loss = []
test_loss = []
train_acc = []
test_acc = []
i_data = []
for i in range(10000):
rand_index = np.random.choice(text_data_train.shape[0], size=logistic_batch_size)
rand_x = text_data_train[rand_index]
# Append review index at the end of text data
rand_x_doc_indices = train_indices[rand_index]
rand_x = np.hstack((rand_x, np.transpose([rand_x_doc_indices])))
rand_y = np.transpose([target_train[rand_index]])
feed_dict = {log_x_inputs : rand_x, log_y_target : rand_y}
sess.run(logistic_train_step, feed_dict=feed_dict)
# Only record loss and accuracy every 100 generations
if (i+1)%100==0:
rand_index_test = np.random.choice(text_data_test.shape[0], size=logistic_batch_size)
rand_x_test = text_data_test[rand_index_test]
# Append review index at the end of text data
rand_x_doc_indices_test = test_indices[rand_index_test]
rand_x_test = np.hstack((rand_x_test, np.transpose([rand_x_doc_indices_test])))
rand_y_test = np.transpose([target_test[rand_index_test]])
test_feed_dict = {log_x_inputs: rand_x_test, log_y_target: rand_y_test}
i_data.append(i+1)
train_loss_temp = sess.run(logistic_loss, feed_dict=feed_dict)
train_loss.append(train_loss_temp)
test_loss_temp = sess.run(logistic_loss, feed_dict=test_feed_dict)
test_loss.append(test_loss_temp)
train_acc_temp = sess.run(accuracy, feed_dict=feed_dict)
train_acc.append(train_acc_temp)
test_acc_temp = sess.run(accuracy, feed_dict=test_feed_dict)
test_acc.append(test_acc_temp)
if (i+1)%500==0:
acc_and_loss = [i+1, train_loss_temp, test_loss_temp, train_acc_temp, test_acc_temp]
acc_and_loss = [np.round(x,2) for x in acc_and_loss]
print('Generation # {}. Train Loss (Test Loss): {:.2f} ({:.2f}). Train Acc (Test Acc): {:.2f} ({:.2f})'.format(*acc_and_loss))
```
1. 這產生以下輸出:
```py
Generation # 500\. Train Loss (Test Loss): 5.62 (7.45). Train Acc (Test Acc): 0.52 (0.48) Generation # 10000\. Train Loss (Test Loss): 2.35 (2.51). Train Acc (Test Acc): 0.59 (0.58)
```
1. 我們還應該注意到,我們在名為 doc2vec 的`text_helpers.generate_batch_data()`函數中創建了一個單獨的數據批量生成方法,我們在本文的第一部分中使用它來訓練 doc2vec 嵌入。以下是與該方法有關的該函數的摘錄:
```py
def generate_batch_data(sentences, batch_size, window_size, method='skip_gram'):
# Fill up data batch
batch_data = []
label_data = []
while len(batch_data) < batch_size:
# select random sentence to start
rand_sentence_ix = int(np.random.choice(len(sentences), size=1))
rand_sentence = sentences[rand_sentence_ix]
# Generate consecutive windows to look at
window_sequences = [rand_sentence[max((ix-window_size),0):(ix+window_size+1)] for ix, x in enumerate(rand_sentence)]
# Denote which element of each window is the center word of interest
label_indices = [ix if ix<window_size else window_size for ix,x in enumerate(window_sequences)]
# Pull out center word of interest for each window and create a tuple for each window
if method=='skip_gram':
...
elif method=='cbow':
...
elif method=='doc2vec':
# For doc2vec we keep LHS window only to predict target word
batch_and_labels = [(rand_sentence[i:i+window_size], rand_sentence[i+window_size]) for i in range(0, len(rand_sentence)-window_size)]
batch, labels = [list(x) for x in zip(*batch_and_labels)]
# Add document index to batch!! Remember that we must extract the last index in batch for the doc-index
batch = [x + [rand_sentence_ix] for x in batch]
else:
raise ValueError('Method {} not implmented yet.'.format(method))
# extract batch and labels
batch_data.extend(batch[:batch_size])
label_data.extend(labels[:batch_size])
# Trim batch and label at the end
batch_data = batch_data[:batch_size]
label_data = label_data[:batch_size]
# Convert to numpy array
batch_data = np.array(batch_data)
label_data = np.transpose(np.array([label_data]))
return batch_data, label_data
```
## 工作原理
在這個秘籍中,我們進行了兩個訓練循環。第一個是適合 doc2vec 嵌入,第二個循環是為了適應電影情感的邏輯回歸。
雖然我們沒有大幅度提高情感預測準確率(它仍然略低于 60%),但我們在電影語料庫中成功實現了 doc2vec 的連接版本。為了提高我們的準確率,我們應該為 doc2vec 嵌入和可能更復雜的模型嘗試不同的參數,因為邏輯回歸可能無法捕獲自然語言中的所有非線性行為。
- TensorFlow 1.x 深度學習秘籍
- 零、前言
- 一、TensorFlow 簡介
- 二、回歸
- 三、神經網絡:感知器
- 四、卷積神經網絡
- 五、高級卷積神經網絡
- 六、循環神經網絡
- 七、無監督學習
- 八、自編碼器
- 九、強化學習
- 十、移動計算
- 十一、生成模型和 CapsNet
- 十二、分布式 TensorFlow 和云深度學習
- 十三、AutoML 和學習如何學習(元學習)
- 十四、TensorFlow 處理單元
- 使用 TensorFlow 構建機器學習項目中文版
- 一、探索和轉換數據
- 二、聚類
- 三、線性回歸
- 四、邏輯回歸
- 五、簡單的前饋神經網絡
- 六、卷積神經網絡
- 七、循環神經網絡和 LSTM
- 八、深度神經網絡
- 九、大規模運行模型 -- GPU 和服務
- 十、庫安裝和其他提示
- TensorFlow 深度學習中文第二版
- 一、人工神經網絡
- 二、TensorFlow v1.6 的新功能是什么?
- 三、實現前饋神經網絡
- 四、CNN 實戰
- 五、使用 TensorFlow 實現自編碼器
- 六、RNN 和梯度消失或爆炸問題
- 七、TensorFlow GPU 配置
- 八、TFLearn
- 九、使用協同過濾的電影推薦
- 十、OpenAI Gym
- TensorFlow 深度學習實戰指南中文版
- 一、入門
- 二、深度神經網絡
- 三、卷積神經網絡
- 四、循環神經網絡介紹
- 五、總結
- 精通 TensorFlow 1.x
- 一、TensorFlow 101
- 二、TensorFlow 的高級庫
- 三、Keras 101
- 四、TensorFlow 中的經典機器學習
- 五、TensorFlow 和 Keras 中的神經網絡和 MLP
- 六、TensorFlow 和 Keras 中的 RNN
- 七、TensorFlow 和 Keras 中的用于時間序列數據的 RNN
- 八、TensorFlow 和 Keras 中的用于文本數據的 RNN
- 九、TensorFlow 和 Keras 中的 CNN
- 十、TensorFlow 和 Keras 中的自編碼器
- 十一、TF 服務:生產中的 TensorFlow 模型
- 十二、遷移學習和預訓練模型
- 十三、深度強化學習
- 十四、生成對抗網絡
- 十五、TensorFlow 集群的分布式模型
- 十六、移動和嵌入式平臺上的 TensorFlow 模型
- 十七、R 中的 TensorFlow 和 Keras
- 十八、調試 TensorFlow 模型
- 十九、張量處理單元
- TensorFlow 機器學習秘籍中文第二版
- 一、TensorFlow 入門
- 二、TensorFlow 的方式
- 三、線性回歸
- 四、支持向量機
- 五、最近鄰方法
- 六、神經網絡
- 七、自然語言處理
- 八、卷積神經網絡
- 九、循環神經網絡
- 十、將 TensorFlow 投入生產
- 十一、更多 TensorFlow
- 與 TensorFlow 的初次接觸
- 前言
- 1.?TensorFlow 基礎知識
- 2. TensorFlow 中的線性回歸
- 3. TensorFlow 中的聚類
- 4. TensorFlow 中的單層神經網絡
- 5. TensorFlow 中的多層神經網絡
- 6. 并行
- 后記
- TensorFlow 學習指南
- 一、基礎
- 二、線性模型
- 三、學習
- 四、分布式
- TensorFlow Rager 教程
- 一、如何使用 TensorFlow Eager 構建簡單的神經網絡
- 二、在 Eager 模式中使用指標
- 三、如何保存和恢復訓練模型
- 四、文本序列到 TFRecords
- 五、如何將原始圖片數據轉換為 TFRecords
- 六、如何使用 TensorFlow Eager 從 TFRecords 批量讀取數據
- 七、使用 TensorFlow Eager 構建用于情感識別的卷積神經網絡(CNN)
- 八、用于 TensorFlow Eager 序列分類的動態循壞神經網絡
- 九、用于 TensorFlow Eager 時間序列回歸的遞歸神經網絡
- TensorFlow 高效編程
- 圖嵌入綜述:問題,技術與應用
- 一、引言
- 三、圖嵌入的問題設定
- 四、圖嵌入技術
- 基于邊重構的優化問題
- 應用
- 基于深度學習的推薦系統:綜述和新視角
- 引言
- 基于深度學習的推薦:最先進的技術
- 基于卷積神經網絡的推薦
- 關于卷積神經網絡我們理解了什么
- 第1章概論
- 第2章多層網絡
- 2.1.4生成對抗網絡
- 2.2.1最近ConvNets演變中的關鍵架構
- 2.2.2走向ConvNet不變性
- 2.3時空卷積網絡
- 第3章了解ConvNets構建塊
- 3.2整改
- 3.3規范化
- 3.4匯集
- 第四章現狀
- 4.2打開問題
- 參考
- 機器學習超級復習筆記
- Python 遷移學習實用指南
- 零、前言
- 一、機器學習基礎
- 二、深度學習基礎
- 三、了解深度學習架構
- 四、遷移學習基礎
- 五、釋放遷移學習的力量
- 六、圖像識別與分類
- 七、文本文件分類
- 八、音頻事件識別與分類
- 九、DeepDream
- 十、自動圖像字幕生成器
- 十一、圖像著色
- 面向計算機視覺的深度學習
- 零、前言
- 一、入門
- 二、圖像分類
- 三、圖像檢索
- 四、對象檢測
- 五、語義分割
- 六、相似性學習
- 七、圖像字幕
- 八、生成模型
- 九、視頻分類
- 十、部署
- 深度學習快速參考
- 零、前言
- 一、深度學習的基礎
- 二、使用深度學習解決回歸問題
- 三、使用 TensorBoard 監控網絡訓練
- 四、使用深度學習解決二分類問題
- 五、使用 Keras 解決多分類問題
- 六、超參數優化
- 七、從頭開始訓練 CNN
- 八、將預訓練的 CNN 用于遷移學習
- 九、從頭開始訓練 RNN
- 十、使用詞嵌入從頭開始訓練 LSTM
- 十一、訓練 Seq2Seq 模型
- 十二、深度強化學習
- 十三、生成對抗網絡
- TensorFlow 2.0 快速入門指南
- 零、前言
- 第 1 部分:TensorFlow 2.00 Alpha 簡介
- 一、TensorFlow 2 簡介
- 二、Keras:TensorFlow 2 的高級 API
- 三、TensorFlow 2 和 ANN 技術
- 第 2 部分:TensorFlow 2.00 Alpha 中的監督和無監督學習
- 四、TensorFlow 2 和監督機器學習
- 五、TensorFlow 2 和無監督學習
- 第 3 部分:TensorFlow 2.00 Alpha 的神經網絡應用
- 六、使用 TensorFlow 2 識別圖像
- 七、TensorFlow 2 和神經風格遷移
- 八、TensorFlow 2 和循環神經網絡
- 九、TensorFlow 估計器和 TensorFlow HUB
- 十、從 tf1.12 轉換為 tf2
- TensorFlow 入門
- 零、前言
- 一、TensorFlow 基本概念
- 二、TensorFlow 數學運算
- 三、機器學習入門
- 四、神經網絡簡介
- 五、深度學習
- 六、TensorFlow GPU 編程和服務
- TensorFlow 卷積神經網絡實用指南
- 零、前言
- 一、TensorFlow 的設置和介紹
- 二、深度學習和卷積神經網絡
- 三、TensorFlow 中的圖像分類
- 四、目標檢測與分割
- 五、VGG,Inception,ResNet 和 MobileNets
- 六、自編碼器,變分自編碼器和生成對抗網絡
- 七、遷移學習
- 八、機器學習最佳實踐和故障排除
- 九、大規模訓練
- 十、參考文獻