<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 七、自然語言處理 在本章中,我們將向您介紹如何使用 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 ``` 由此,我們將得到以下繪圖: ![](https://img.kancloud.cn/2f/b8/2fb82bd5a8a20cce5677ca162e329e4f_378x266.png) 圖 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 的以下公式: ![](https://img.kancloud.cn/03/c5/03c58c64c8b25d454d6b4e00eae0bb6b_2090x480.png) 這里`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) ``` 以下是繪制訓練和測試裝置的準確率和損耗的繪圖: ![](https://img.kancloud.cn/c7/d7/c7d709a3ec7e91e6619551e8c9badd5c_400x281.png) 圖 2:根據 TF-IDF 值構建的物流垃圾郵件模型的交叉熵損失 訓練和測試精度圖如下: ![](https://img.kancloud.cn/fb/f8/fbf8f4e5333fdb48a9c9d0d5eee61f7b_400x281.png) 圖 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): ![](https://img.kancloud.cn/be/a7/bea739abe908d7fbaa904fb98e0beb21_867x663.png) 圖 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,我們將周圍的窗口嵌入添加到一起以獲得一個嵌入來預測目標字嵌入: ![](https://img.kancloud.cn/81/5d/815db8df1e42aa393a29fa17a6b20d70_868x663.png) 圖 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() ``` 每代交叉熵損失的圖如下: ![](https://img.kancloud.cn/3d/38/3d38b35cb1e606240766976aa26cf4e7_406x281.png)Figure 6: Here we observe the train and test loss over 10,000 generations 上述代碼的訓練圖和測試精度如下: ![](https://img.kancloud.cn/0b/6c/0b6ce21cb11a1df5bf85d14e4a98000d_406x281.png) 圖 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 嵌入和可能更復雜的模型嘗試不同的參數,因為邏輯回歸可能無法捕獲自然語言中的所有非線性行為。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看