日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
CNN也能用于NLP任務(wù),一文簡述文本分類任務(wù)的7個(gè)模型

本文是我之前寫過的一篇基于推特?cái)?shù)據(jù)進(jìn)行情感分析的文章(https://ahmedbesbes.com/sentiment-analysis-on-twitter-using-word2vec-and-keras.html)的延伸內(nèi)容。那時(shí)我建立了一個(gè)簡單的模型:基于 keras 訓(xùn)練的兩層前饋神經(jīng)網(wǎng)絡(luò)。用組成推文的詞嵌入的加權(quán)平均值作為文檔向量來表示輸入推文。

南明ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:028-86922220(備注:SSL證書合作)期待與您的合作!

我用的嵌入是用 gensim 基于語料庫從頭訓(xùn)練出來的 word2vec 模型。該是一個(gè)二分類任務(wù),準(zhǔn)確率能達(dá)到 79%。

本文目標(biāo)在于探索其他在相同數(shù)據(jù)集上訓(xùn)練出來的 NLP 模型,然后在給定的測試集上對(duì)這些模型的性能進(jìn)行評(píng)估。

我們將通過不同的模型(從依賴于詞袋表征的簡單模型到部署了卷積/循環(huán)網(wǎng)絡(luò)的復(fù)雜模型)了解能否得到高于 79% 的準(zhǔn)確率!

首先,將從簡單的模型開始,逐步增加模型的復(fù)雜度。這項(xiàng)工作是為了說明簡單的模型也能很有效。

我會(huì)進(jìn)行這些嘗試:

  • 用詞級(jí)的 ngram 做 logistic 回歸
  • 用字符級(jí)的 ngram 做 logistic 回歸
  • 用詞級(jí)的 ngram 和字符級(jí)的 ngram 做 Logistic 回歸
  • 在沒有對(duì)詞嵌入進(jìn)行預(yù)訓(xùn)練的情況下訓(xùn)練循環(huán)神經(jīng)網(wǎng)絡(luò)(雙向 GRU)
  • 用 GloVe 對(duì)詞嵌入進(jìn)行預(yù)訓(xùn)練,然后訓(xùn)練循環(huán)神經(jīng)網(wǎng)絡(luò)
  • 多通道卷積神經(jīng)網(wǎng)絡(luò)
  • RNN(雙向 GRU)+ CNN 模型

文末附有這些 NLP 技術(shù)的樣板代碼。這些代碼可以幫助你開啟自己的 NLP 項(xiàng)目并獲得最優(yōu)結(jié)果(這些模型中有一些非常強(qiáng)大)。

我們還可以提供一個(gè)綜合基準(zhǔn),我們可以利用該基準(zhǔn)分辨哪個(gè)模型最適合預(yù)測推文中的情緒。

在相關(guān)的 GitHub 庫中還有不同的模型、這些模型的預(yù)測結(jié)果以及測試集。你可以自己嘗試并得到可信的結(jié)果。

 
 
 
 
  1. import os 
  2. import re 
  3.  
  4. import warnings 
  5. warnings.simplefilter("ignore", UserWarning) 
  6. from matplotlib import pyplot as plt 
  7. %matplotlib inline 
  8.  
  9.  
  10. import pandas as pd 
  11. pd.options.mode.chained_assignment = None 
  12. import numpy as np  
  13. from string import punctuation 
  14.  
  15. from nltk.tokenize import word_tokenize 
  16.  
  17. from sklearn.model_selection import train_test_split 
  18. from sklearn.feature_extraction.text import TfidfVectorizer 
  19. from sklearn.linear_model import LogisticRegression 
  20. from sklearn.metrics import accuracy_score, auc, roc_auc_score 
  21. from sklearn.externals import joblib 
  22.  
  23. import scipy 
  24. from scipy.sparse import hstack 

一、數(shù)據(jù)預(yù)處理

你可以從該鏈接(http://thinknook.com/twitter-sentiment-analysis-training-corpus-dataset-2012-09-22/)下載數(shù)據(jù)集。

加載數(shù)據(jù)并提取所需變量(情感及情感文本)。

該數(shù)據(jù)集包含 1,578,614 個(gè)分好類的推文,每一行都用 1(積極情緒)和 0(消極情緒)進(jìn)行了標(biāo)記。

作者建議用 1/10 的數(shù)據(jù)進(jìn)行測試,其余數(shù)據(jù)用于訓(xùn)練。

 
 
 
 
  1. data = pd.read_csv('./data/tweets.csv', encoding='latin1', usecols=['Sentiment', 'SentimentText']) 
  2. data.columns = ['sentiment', 'text'] 
  3. datadata = data.sample(frac=1, random_state=42) 
  4. print(data.shape) 
  5. (1578614, 2) 
  6. for row in data.head(10).iterrows(): 
  7.     print(row[1]['sentiment'], row[1]['text'])  
  8. 1 http://www.popsugar.com/2999655 keep voting for robert pattinson in the popsugar100 as well!!  
  9. 1 @GamrothTaylor I am starting to worry about you, only I have Navy Seal type sleep hours.  
  10. 0 sunburned...no sunbaked!    ow.  it hurts to sit. 
  11. 1 Celebrating my 50th birthday by doing exactly the same as I do every other day - working on our websites.  It's just another day.    
  12. 1 Leah and Aiden Gosselin are the cutest kids on the face of the Earth  
  13. 1 @MissHell23 Oh. I didn't even notice.   
  14. 0 WTF is wrong with me?!!! I'm completely miserable. I need to snap out of this  
  15. 0 Was having the best time in the gym until I got to the car and had messages waiting for me... back to the down stage!  
  16. 1 @JENTSYY oh what happened??  
  17. 0 @catawu Ghod forbid he should feel responsible for anything! 

推文數(shù)據(jù)中存在很多噪聲,我們刪除了推文中的網(wǎng)址、主題標(biāo)簽和用戶提及來清理數(shù)據(jù)。

 
 
 
 
  1. def tokenize(tweet): 
  2.     tweet = re.sub(r'http\S+', '', tweet) 
  3.     tweet = re.sub(r"#(\w+)", '', tweet) 
  4.     tweet = re.sub(r"@(\w+)", '', tweet) 
  5.     tweet = re.sub(r'[^\w\s]', '', tweet) 
  6.     tweettweet = tweet.strip().lower() 
  7.     tokens = word_tokenize(tweet) 
  8.     return tokens 

將清理好的數(shù)據(jù)保存在硬盤上。

 
 
 
 
  1. data['tokens'] = data.text.progress_map(tokenize) 
  2. data['cleaned_text'] = data['tokens'].map(lambda tokens: ' '.join(tokens)) 
  3. data[['sentiment', 'cleaned_text']].to_csv('./data/cleaned_text.csv') 
  4.  
  5. data = pd.read_csv('./data/cleaned_text.csv') 
  6. print(data.shape) 
  7. (1575026, 2) 
  8. data.head() 

既然數(shù)據(jù)集已經(jīng)清理干凈了,就可以準(zhǔn)備分割訓(xùn)練集和測試集來建立模型了。

本文數(shù)據(jù)都是用這種方式分割的。

 
 
 
 
  1. x_train, x_test, y_train, y_test = train_test_split(data['cleaned_text'],  
  2.                                                     data['sentiment'],  
  3.                                                     test_size=0.1,  
  4.                                                     random_state=42, 
  5.                                                     stratify=data['sentiment']) 
  6.  
  7. print(x_train.shape, x_test.shape, y_train.shape, y_test.shape) 
  8. (1417523,) (157503,) (1417523,) (157503,) 

將測試集標(biāo)簽存儲(chǔ)在硬盤上以便后續(xù)使用。

 
 
 
 
  1. pd.DataFrame(y_test).to_csv('./predictions/y_true.csv', index=False, encoding='utf-8') 

接下來就可以應(yīng)用機(jī)器學(xué)習(xí)方法了。

1. 基于詞級(jí) ngram 的詞袋模型

那么,什么是 n-gram 呢?

如圖所示,ngram 是將可在源文本中找到的長度為 n 的相鄰詞的所有組合。

我們的模型將以 unigrams(n=1)和 bigrams(n=2)為特征。

用矩陣表示數(shù)據(jù)集,矩陣的每一行表示一條推文,每一列表示從推文(已經(jīng)經(jīng)過分詞和清理)中提取的特征(一元模型或二元模型)。每個(gè)單元格是 tf-idf 分?jǐn)?shù)(也可以用更簡單的值,但 tf-idf 比較通用且效果較好)。我們將該矩陣稱為文檔-詞項(xiàng)矩陣。

略經(jīng)思考可知,擁有 150 萬推文的語料庫的一元模型和二元模型去重后的數(shù)量還是很大的。事實(shí)上,出于計(jì)算力的考慮,我們可將這個(gè)數(shù)設(shè)置為固定值。你可以通過交叉驗(yàn)證來確定這個(gè)值。

在向量化之后,語料庫如下圖所示:

 
 
 
 
  1. I like pizza a lot 

假設(shè)使用上述特征讓模型對(duì)這句話進(jìn)行預(yù)測。

由于我們使用的是一元模型和二元模型后,因此模型提取出了下列特征:

 
 
 
 
  1. i, like, pizza, a, lot, i like, like pizza, pizza a, a lot 

因此,句子變成了大小為 N(分詞總數(shù))的向量,這個(gè)向量中包含 0 和這些 ngram 的 tf-idf 分?jǐn)?shù)。所以接下來其實(shí)是要處理這個(gè)大而稀疏的向量。

一般而言,線性模型可以很好地處理大而稀疏的數(shù)據(jù)。此外,與其他模型相比,線性模型的訓(xùn)練速度也更快。

從過去的經(jīng)驗(yàn)可知,logistic 回歸可以在稀疏的 tf-idf 矩陣上良好地運(yùn)作。

 
 
 
 
  1. vectorizer_word = TfidfVectorizer(max_features=40000, 
  2.                              min_df=5,  
  3.                              max_df=0.5,  
  4.                              analyzer='word',  
  5.                              stop_words='english',  
  6.                              ngram_range=(1, 2)) 
  7.  
  8. vectorizer_word.fit(x_train, leave=False) 
  9.  
  10. tfidf_matrix_word_train = vectorizer_word.transform(x_train) 
  11. tfidf_matrix_word_test = vectorizer_word.transform(x_test) 

在為訓(xùn)練集和測試集生成了 tf-idf 矩陣后,就可以建立第一個(gè)模型并對(duì)其進(jìn)行測試。

tf-idf 矩陣是 logistic 回歸的特征。

 
 
 
 
  1. lr_word = LogisticRegression(solver='sag', verbose=2) 
  2. lr_word.fit(tfidf_matrix_word_train, y_train) 

一旦訓(xùn)練好模型后,就可以將其應(yīng)用于測試數(shù)據(jù)以獲得預(yù)測值。然后將這些值和模型一并存儲(chǔ)在硬盤上。

 
 
 
 
  1. joblib.dump(lr_word, './models/lr_word_ngram.pkl') 
  2.  
  3. y_pred_word = lr_word.predict(tfidf_matrix_word_test) 
  4. pd.DataFrame(y_pred_word, columns=['y_pred']).to_csv('./predictions/lr_word_ngram.csv', index=False) 

得到準(zhǔn)確率:

 
 
 
 
  1. y_pred_word = pd.read_csv('./predictions/lr_word_ngram.csv') 
  2. print(accuracy_score(y_test, y_pred_word)) 
  3. 0.782042246814 

第一個(gè)模型得到了 78.2% 的準(zhǔn)確率!真不賴。接下來了解一下第二個(gè)模型。

2. 基于字符級(jí) ngram 的詞袋模型

我們從未說過 ngram 僅為詞服務(wù),也可將其應(yīng)用于字符上。

如你所見,我們將對(duì)字符級(jí) ngram 使用與圖中一樣的代碼,現(xiàn)在直接來看 4-grams 建模。

基本上這意味著,像「I like this movie」這樣的句子會(huì)有下列特征:

 
 
 
 
  1. I, l, i, k, e, ..., I li, lik, like, ..., this, ... , is m, s mo, movi, ... 

字符級(jí) ngram 很有效,在語言建模任務(wù)中,甚至可以比分詞表現(xiàn)得更好。像垃圾郵件過濾或自然語言識(shí)別這樣的任務(wù)就高度依賴字符級(jí) ngram。

與之前學(xué)習(xí)單詞組合的模型不同,該模型學(xué)習(xí)的是字母組合,這樣就可以處理單詞的形態(tài)構(gòu)成。

基于字符的表征的一個(gè)優(yōu)勢是可以更好地解決單詞拼寫錯(cuò)誤的問題。

我們來運(yùn)行同樣的流程:

 
 
 
 
  1. vectorizer_char = TfidfVectorizer(max_features=40000, 
  2.                              min_df=5,  
  3.                              max_df=0.5,  
  4.                              analyzer='char',  
  5.                              ngram_range=(1, 4)) 
  6.  
  7. vectorizer_char.fit(tqdm_notebook(x_train, leave=False)); 
  8.  
  9. tfidf_matrix_char_train = vectorizer_char.transform(x_train) 
  10. tfidf_matrix_char_test = vectorizer_char.transform(x_test) 
  11.  
  12. lr_char = LogisticRegression(solver='sag', verbose=2) 
  13. lr_char.fit(tfidf_matrix_char_train, y_train) 
  14.  
  15. y_pred_char = lr_char.predict(tfidf_matrix_char_test) 
  16. joblib.dump(lr_char, './models/lr_char_ngram.pkl') 
  17.  
  18. pd.DataFrame(y_pred_char, columns=['y_pred']).to_csv('./predictions/lr_char_ngram.csv', index=False) 
  19. y_pred_char = pd.read_csv('./predictions/lr_char_ngram.csv') 
  20. print(accuracy_score(y_test, y_pred_char)) 
  21. 0.80420055491 

80.4% 的準(zhǔn)確率!字符級(jí) ngram 模型的性能要比詞級(jí)的 ngram 更好。

3. 基于詞級(jí) ngram 和字符級(jí) ngram 的詞袋模型

與詞級(jí) ngram 的特征相比,字符級(jí) ngram 特征似乎提供了更好的準(zhǔn)確率。那么將字符級(jí) ngram 和詞級(jí) ngram 結(jié)合效果又怎么樣呢?

我們將兩個(gè) tf-idf 矩陣連接在一起,建立一個(gè)新的、混合 tf-idf 矩陣。該模型有助于學(xué)習(xí)單詞形態(tài)結(jié)構(gòu)以及與這個(gè)單詞大概率相鄰單詞的形態(tài)結(jié)構(gòu)。

將這些屬性結(jié)合在一起。

 
 
 
 
  1. tfidf_matrix_word_char_train =  hstack((tfidf_matrix_word_train, tfidf_matrix_char_train)) 
  2. tfidf_matrix_word_char_test =  hstack((tfidf_matrix_word_test, tfidf_matrix_char_test)) 
  3.  
  4. lr_word_char = LogisticRegression(solver='sag', verbose=2) 
  5. lr_word_char.fit(tfidf_matrix_word_char_train, y_train) 
  6.  
  7. y_pred_word_char = lr_word_char.predict(tfidf_matrix_word_char_test) 
  8. joblib.dump(lr_word_char, './models/lr_word_char_ngram.pkl') 
  9.  
  10. pd.DataFrame(y_pred_word_char, columns=['y_pred']).to_csv('./predictions/lr_word_char_ngram.csv', index=False) 
  11. y_pred_word_char = pd.read_csv('./predictions/lr_word_char_ngram.csv') 
  12. print(accuracy_score(y_test, y_pred_word_char)) 
  13. 0.81423845895 

得到了 81.4% 的準(zhǔn)確率。該模型只加了一個(gè)整體單元,但結(jié)果比之前的兩個(gè)都要好。

關(guān)于詞袋模型

  • 優(yōu)點(diǎn):考慮到其簡單的特性,詞袋模型已經(jīng)很強(qiáng)大了,它們訓(xùn)練速度快,且易于理解。
  • 缺點(diǎn):即使 ngram 帶有一些單詞間的語境,但詞袋模型無法建模序列中單詞間的長期依賴關(guān)系。

現(xiàn)在要用到深度學(xué)習(xí)模型了。深度學(xué)習(xí)模型的表現(xiàn)優(yōu)于詞袋模型是因?yàn)樯疃葘W(xué)習(xí)模型能夠捕捉到句子中單詞間的順序依賴關(guān)系。這可能要?dú)w功于循環(huán)神經(jīng)網(wǎng)絡(luò)這一特殊神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的出現(xiàn)了。

本文并未涵蓋 RNN 的理論基礎(chǔ),但該鏈接(http://colah.github.io/posts/2015-08-Understanding-LSTMs/)中的內(nèi)容值得一讀。這篇文章來源于 Cristopher Olah 的博客,詳細(xì)敘述了一種特殊的 RNN 模型:長短期記憶網(wǎng)絡(luò)(LSTM)。

在開始之前,要先設(shè)置一個(gè)深度學(xué)習(xí)專用的環(huán)境,以便在 TensorFlow 上使用 Keras。誠實(shí)地講,我試著在個(gè)人筆記本上運(yùn)行這些代碼,但考慮到數(shù)據(jù)集的大小和 RNN 架構(gòu)的復(fù)雜程度,這是很不實(shí)際的。還有一個(gè)很好的選擇是 AWS。我一般在 EC2 p2.xlarge 實(shí)例上用深度學(xué)習(xí) AMI(https://aws.amazon.com/marketplace/pp/B077GCH38C?qid=1527197041958&sr=0-1&ref_=srh_res_product_title)。亞馬遜 AMI 是安裝了所有包(TensorFlow、PyTorch 和 Keras 等)的預(yù)先配置過的 VM 圖。強(qiáng)烈推薦大家使用!

 
 
 
 
  1. from keras.preprocessing.text import Tokenizer 
  2. from keras.preprocessing.text import text_to_word_sequence 
  3. from keras.preprocessing.sequence import pad_sequences 
  4.  
  5. from keras.models import Model 
  6. from keras.models import Sequential 
  7.  
  8. from keras.layers import Input, Dense, Embedding, Conv1D, Conv2D, MaxPooling1D, MaxPool2D 
  9. from keras.layers import Reshape, Flatten, Dropout, Concatenate 
  10. from keras.layers import SpatialDropout1D, concatenate 
  11. from keras.layers import GRU, Bidirectional, GlobalAveragePooling1D, GlobalMaxPooling1D 
  12.  
  13. from keras.callbacks import Callback 
  14. from keras.optimizers import Adam 
  15.  
  16. from keras.callbacks import ModelCheckpoint, EarlyStopping 
  17. from keras.models import load_model 
  18. from keras.utils.vis_utils import plot_model 

4. 沒有預(yù)訓(xùn)練詞嵌入的循環(huán)神經(jīng)網(wǎng)絡(luò)

RNN 可能看起來很可怕。盡管它們因?yàn)閺?fù)雜而難以理解,但非常有趣。RNN 模型封裝了一個(gè)非常漂亮的設(shè)計(jì),以克服傳統(tǒng)神經(jīng)網(wǎng)絡(luò)在處理序列數(shù)據(jù)(文本、時(shí)間序列、視頻、DNA 序列等)時(shí)的短板。

RNN 是一系列神經(jīng)網(wǎng)絡(luò)的模塊,它們彼此連接像鎖鏈一樣。每一個(gè)都將消息向后傳遞。強(qiáng)烈推薦大家從 Colah 的博客中深入了解它的內(nèi)部機(jī)制,下面的圖就來源于此。

我們要處理的序列類型是文本數(shù)據(jù)。對(duì)意義而言,單詞順序很重要。RNN 考慮到了這一點(diǎn),它可以捕捉長期依賴關(guān)系。

為了在文本數(shù)據(jù)上使用 Keras,我們首先要對(duì)數(shù)據(jù)進(jìn)行預(yù)處理??梢杂?Keras 的 Tokenizer 類。該對(duì)象用 num_words 作為參數(shù),num_words 是根據(jù)詞頻進(jìn)行分詞后保留下來的最大詞數(shù)。

 
 
 
 
  1. MAX_NB_WORDS = 80000 
  2. tokenizer = Tokenizer(num_words=MAX_NB_WORDS) 
  3.  
  4. tokenizer.fit_on_texts(data['cleaned_text']) 

當(dāng)分詞器適用于數(shù)據(jù)時(shí),我們就可以用分詞器將文本字符級(jí) ngram 轉(zhuǎn)換為數(shù)字序列。

這些數(shù)字表示每個(gè)單詞在字典中的位置(將其視為映射)。

如下例所示:

 
 
 
 
  1. x_train[15] 
  2. 'breakfast time happy time' 

這里說明了分詞器是如何將其轉(zhuǎn)換為數(shù)字序列的。

 
 
 
 
  1. tokenizer.texts_to_sequences([x_train[15]]) 
  2. [[530, 50, 119, 50]] 

接下來在訓(xùn)練序列和測試序列中應(yīng)用該分詞器:

 
 
 
 
  1. train_sequences = tokenizer.texts_to_sequences(x_train) 
  2. test_sequences = tokenizer.texts_to_sequences(x_test) 

將推文映射到整數(shù)列表中。但是由于長度不同,還是沒法將它們?cè)诰仃囍卸询B在一起。還好 Keras 允許用 0 將序列填充至最大長度。我們將這個(gè)長度設(shè)置為 35(這是推文中的最大分詞數(shù))。

 
 
 
 
  1. MAX_LENGTH = 35 
  2. padded_train_sequences = pad_sequences(train_sequences, maxlen=MAX_LENGTH) 
  3. padded_test_sequences = pad_sequences(test_sequences, maxlen=MAX_LENGTH) 
  4. padded_train_sequences 
  5. array([[    0,     0,     0, ...,  2383,   284,     9], 
  6.        [    0,     0,     0, ...,    13,    30,    76], 
  7.        [    0,     0,     0, ...,    19,    37, 45231], 
  8.        ...,  
  9.        [    0,     0,     0, ...,    43,   502,  1653], 
  10.        [    0,     0,     0, ...,     5,  1045,   890], 
  11.        [    0,     0,     0, ..., 13748, 38750,   154]]) 
  12. padded_train_sequences.shape 
  13. (1417523, 35) 

現(xiàn)在就可以將數(shù)據(jù)傳入 RNN 了。

以下是我將使用的架構(gòu)的一些元素:

  • 嵌入維度為 300。這意味著我們使用的 8 萬個(gè)單詞中的每一個(gè)都被映射至 300 維的密集(浮點(diǎn)數(shù))向量。該映射將在訓(xùn)練過程中進(jìn)行調(diào)整。
  • 在嵌入層上應(yīng)用 spatial dropout 層以減少過擬合:按批次查看 35*300 的矩陣,隨機(jī)刪除每個(gè)矩陣中(設(shè)置為 0)的詞向量(行)。這有助于將注意力不集中在特定的詞語上,有利于模型的泛化。
  • 雙向門控循環(huán)單元(GRU):這是循環(huán)網(wǎng)絡(luò)部分。這是 LSTM 架構(gòu)更快的變體。將其視為兩個(gè)循環(huán)網(wǎng)絡(luò)的組合,這樣就可以從兩個(gè)方向同時(shí)掃描文本序列:從左到右和從右到左。這使得網(wǎng)絡(luò)在閱讀給定單詞時(shí),可以結(jié)合之前和之后的內(nèi)容理解文本。GRU 中每個(gè)網(wǎng)絡(luò)塊的輸出 h_t 的維度即單元數(shù),將這個(gè)值設(shè)置為 100。由于用了雙向 GRU,因此每個(gè) RNN 塊的最終輸出都是 200 維的。

雙向 GRU 的輸出是有維度的(批尺寸、時(shí)間步和單元)。這意味著如果用的是經(jīng)典的 256 的批尺寸,維度將會(huì)是 (256, 35, 200)。

  • 在每個(gè)批次上應(yīng)用的是全局平均池化,其中包含了每個(gè)時(shí)間步(即單詞)對(duì)應(yīng)的輸出向量的平均值。
  • 我們應(yīng)用了相同的操作,只是用最大池化替代了平均池化。
  • 將前兩個(gè)操作的輸出連接在了一起。
 
 
 
 
  1. def get_simple_rnn_model(): 
  2.     embedding_dim = 300 
  3.     embedding_matrix = np.random.random((MAX_NB_WORDS, embedding_dim)) 
  4.  
  5.     inp = Input(shape=(MAX_LENGTH, )) 
  6.     x = Embedding(input_dim=MAX_NB_WORDS, output_dim=embedding_dim, input_length=MAX_LENGTH,  
  7.                   weights=[embedding_matrix], trainable=True)(inp) 
  8.     x = SpatialDropout1D(0.3)(x) 
  9.     x = Bidirectional(GRU(100, return_sequences=True))(x) 
  10.     avg_pool = GlobalAveragePooling1D()(x) 
  11.     max_pool = GlobalMaxPooling1D()(x) 
  12.     conc = concatenate([avg_pool, max_pool]) 
  13.     outp = Dense(1, activation="sigmoid")(conc) 
  14.  
  15.     model = Model(inpinputs=inp, outpoutputs=outp) 
  16.     model.compile(loss='binary_crossentropy', 
  17.                   optimizer='adam', 
  18.                   metrics=['accuracy']) 
  19.     return model 
  20.  
  21. rnn_simple_model = get_simple_rnn_model() 

該模型的不同層如下所示:

 
 
 
 
  1. plot_model(rnn_simple_model,  
  2.            to_file='./images/article_5/rnn_simple_model.png',  
  3.            show_shapes=True,  
  4.            show_layer_names=True) 

在訓(xùn)練期間使用了模型檢查點(diǎn)。這樣可以在每個(gè) epoch 的最后將最佳模型(可以用準(zhǔn)確率度量)自動(dòng)存儲(chǔ)(在硬盤上)。

 
 
 
 
  1. filepath="./models/rnn_no_embeddings/weights-improvement-{epoch:02d}-{val_acc:.4f}.hdf5" 
  2. checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True, mode='max') 
  3.  
  4. batch_size = 256 
  5. epochs = 2 
  6.  
  7. history = rnn_simple_model.fit(x=padded_train_sequences,  
  8.                     y=y_train,  
  9.                     validation_data=(padded_test_sequences, y_test),  
  10.                     batch_sizebatch_size=batch_size,  
  11.                     callbacks=[checkpoint],  
  12.                     epochsepochs=epochs,  
  13.                     verbose=1) 
  14.  
  15. best_rnn_simple_model = load_model('./models/rnn_no_embeddings/weights-improvement-01-0.8262.hdf5') 
  16.  
  17. y_pred_rnn_simple = best_rnn_simple_model.predict(padded_test_sequences, verbose=1, batch_size=2048) 
  18.  
  19. y_pred_rnn_simple = pd.DataFrame(y_pred_rnn_simple, columns=['prediction']) 
  20. y_pred_rnn_simple['prediction'] = y_pred_rnn_simple['prediction'].map(lambda p: 1 if p >= 0.5 else 0) 
  21. y_pred_rnn_simple.to_csv('./predictions/y_pred_rnn_simple.csv', index=False) 
  22. y_pred_rnn_simple = pd.read_csv('./predictions/y_pred_rnn_simple.csv') 
  23. print(accuracy_score(y_test, y_pred_rnn_simple)) 
  24. 0.826219183127 

準(zhǔn)確率達(dá)到了 82.6%!這真是很不錯(cuò)的結(jié)果了!現(xiàn)在的模型表現(xiàn)已經(jīng)比之前的詞袋模型更好了,因?yàn)槲覀儗⑽谋镜男蛄行再|(zhì)考慮在內(nèi)了。

還能做得更好嗎?

5. 用 GloVe 預(yù)訓(xùn)練詞嵌入的循環(huán)神經(jīng)網(wǎng)絡(luò)

在最后一個(gè)模型中,嵌入矩陣被隨機(jī)初始化了。那么如果用預(yù)訓(xùn)練過的詞嵌入對(duì)其進(jìn)行初始化又當(dāng)如何呢?舉個(gè)例子:假設(shè)在語料庫中有「pizza」這個(gè)詞。遵循之前的架構(gòu)對(duì)其進(jìn)行初始化后,可以得到一個(gè) 300 維的隨機(jī)浮點(diǎn)值向量。這當(dāng)然是很好的。這很好實(shí)現(xiàn),而且這個(gè)嵌入可以在訓(xùn)練過程中進(jìn)行調(diào)整。但你還可以使用在很大的語料庫上訓(xùn)練出來的另一個(gè)模型,為「pizza」生成詞嵌入來代替隨機(jī)選擇的向量。這是一種特殊的遷移學(xué)習(xí)。

使用來自外部嵌入的知識(shí)可以提高 RNN 的精度,因?yàn)樗狭诉@個(gè)單詞的相關(guān)新信息(詞匯和語義),而這些信息是基于大規(guī)模數(shù)據(jù)語料庫訓(xùn)練和提煉出來的。

我們使用的預(yù)訓(xùn)練嵌入是 GloVe。

官方描述是這樣的:GloVe 是一種獲取單詞向量表征的無監(jiān)督學(xué)習(xí)算法。該算法的訓(xùn)練基于語料庫全局詞-詞共現(xiàn)數(shù)據(jù),得到的表征展示出詞向量空間有趣的線性子結(jié)構(gòu)。

本文使用的 GloVe 嵌入的訓(xùn)練數(shù)據(jù)是數(shù)據(jù)量很大的網(wǎng)絡(luò)抓取,包括:

  • 8400 億個(gè)分詞;
  • 220 萬詞。

下載壓縮文件要 2.03GB。請(qǐng)注意,該文件無法輕松地加載在標(biāo)準(zhǔn)筆記本電腦上。

GloVe 嵌入有 300 維。

GloVe 嵌入來自原始文本數(shù)據(jù),在該數(shù)據(jù)中每一行都包含一個(gè)單詞和 300 個(gè)浮點(diǎn)數(shù)(對(duì)應(yīng)嵌入)。所以首先要將這種結(jié)構(gòu)轉(zhuǎn)換為 Python 字典。

 
 
 
 
  1. def get_coefs(word, *arr): 
  2.     try: 
  3.         return word, np.asarray(arr, dtype='float32') 
  4.     except: 
  5.         return None, None 
  6.  
  7. embeddings_index = dict(get_coefs(*o.strip().split()) for o in tqdm_notebook(open('./embeddings/glove.840B.300d.txt'))) 
  8.  
  9. embed_size=300 
  10. for k in tqdm_notebook(list(embeddings_index.keys())): 
  11.     v = embeddings_index[k] 
  12.     try: 
  13.         if v.shape != (embed_size, ): 
  14.             embeddings_index.pop(k) 
  15.     except: 
  16.         pass 
  17.  
  18. embeddings_index.pop(None) 

一旦創(chuàng)建了嵌入索引,我們就可以提取所有的向量,將其堆疊在一起并計(jì)算它們的平均值和標(biāo)準(zhǔn)差。

 
 
 
 
  1. values = list(embeddings_index.values()) 
  2. all_embs = np.stack(values) 
  3.  
  4. emb_mean, emb_std = all_embs.mean(), all_embs.std() 

現(xiàn)在生成了嵌入矩陣。按照 mean=emb_mean 和 std=emb_std 的正態(tài)分布對(duì)矩陣進(jìn)行初始化。遍歷語料庫中的 80000 個(gè)單詞。對(duì)每一個(gè)單詞而言,如果這個(gè)單詞存在于 GloVe 中,我們就可以得到這個(gè)單詞的嵌入,如果不存在那就略過。

 
 
 
 
  1. word_index = tokenizer.word_index 
  2. nb_words = MAX_NB_WORDS 
  3. embedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size)) 
  4.  
  5. oov = 0 
  6. for word, i in tqdm_notebook(word_index.items()): 
  7.     if i >= MAX_NB_WORDS: continue 
  8.     embedding_vector = embeddings_index.get(word) 
  9.     if embedding_vector is not None: 
  10.         embedding_matrix[i] = embedding_vector 
  11.     else: 
  12.         oov += 1 
  13.  
  14. print(oov) 
  15.  
  16. def get_rnn_model_with_glove_embeddings(): 
  17.     embedding_dim = 300 
  18.     inp = Input(shape=(MAX_LENGTH, )) 
  19.     x = Embedding(MAX_NB_WORDS, embedding_dim, weights=[embedding_matrix], input_length=MAX_LENGTH, trainable=True)(inp) 
  20.     x = SpatialDropout1D(0.3)(x) 
  21.     x = Bidirectional(GRU(100, return_sequences=True))(x) 
  22.     avg_pool = GlobalAveragePooling1D()(x) 
  23.     max_pool = GlobalMaxPooling1D()(x) 
  24.     conc = concatenate([avg_pool, max_pool]) 
  25.     outp = Dense(1, activation="sigmoid")(conc) 
  26.  
  27.     model = Model(inpinputs=inp, outpoutputs=outp) 
  28.     model.compile(loss='binary_crossentropy', 
  29.                   optimizer='adam', 
  30.                   metrics=['accuracy']) 
  31.     return model 
  32.  
  33. rnn_model_with_embeddings = get_rnn_model_with_glove_embeddings() 
  34.  
  35. filepath="./models/rnn_with_embeddings/weights-improvement-{epoch:02d}-{val_acc:.4f}.hdf5" 
  36. checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True, mode='max') 
  37.  
  38. batch_size = 256 
  39. epochs = 4 
  40.  
  41. history = rnn_model_with_embeddings.fit(x=padded_train_sequences,  
  42.                     y=y_train,  
  43.                     validation_data=(padded_test_sequences, y_test),  
  44.                     batch_sizebatch_size=batch_size,  
  45.                     callbacks=[checkpoint],  
  46.                     epochsepochs=epochs,  
  47.                     verbose=1) 
  48.  
  49. best_rnn_model_with_glove_embeddings = load_model('./models/rnn_with_embeddings/weights-improvement-03-0.8372.hdf5') 
  50.  
  51. y_pred_rnn_with_glove_embeddings = best_rnn_model_with_glove_embeddings.predict( 
  52.     padded_test_sequences, verbose=1, batch_size=2048) 
  53.  
  54. y_pred_rnn_with_glove_embeddings = pd.DataFrame(y_pred_rnn_with_glove_embeddings, columns=['prediction']) 
  55. y_pred_rnn_with_glove_embeddings['prediction'] = y_pred_rnn_with_glove_embeddings['prediction'].map(lambda p:  
  56.                                                                                                     1 if p >= 0.5 else 0) 
  57. y_pred_rnn_with_glove_embeddings.to_csv('./predictions/y_pred_rnn_with_glove_embeddings.csv', index=False) 
  58. y_pred_rnn_with_glove_embeddings = pd.read_csv('./predictions/y_pred_rnn_with_glove_embeddings.csv') 
  59. print(accuracy_score(y_test, y_pred_rnn_with_glove_embeddings)) 
  60. 0.837203100893 

準(zhǔn)確率達(dá)到了 83.7%!來自外部詞嵌入的遷移學(xué)習(xí)起了作用!本教程剩余部分都會(huì)在嵌入矩陣中使用 GloVe 嵌入。

6. 多通道卷積神經(jīng)網(wǎng)絡(luò)

這一部分實(shí)驗(yàn)了我曾了解過的卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)(http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/)。CNN 常用于計(jì)算機(jī)視覺任務(wù)。但最近我試著將其應(yīng)用于 NLP 任務(wù),而結(jié)果也希望滿滿。

簡要了解一下當(dāng)在文本數(shù)據(jù)上使用卷積網(wǎng)絡(luò)時(shí)會(huì)發(fā)生什么。為了解釋這一點(diǎn),我從 wildm.com(一個(gè)很好的博客)中找到了這張非常有名的圖(如下所示)。

了解一下使用的例子:I like this movie very much!(7 個(gè)分詞)

  • 每個(gè)單詞的嵌入維度是 5。因此,可以用一個(gè)維度為 (7,5 的矩陣表示這句話。你可以將其視為一張「圖」(數(shù)字或浮點(diǎn)數(shù)的矩陣)。
  • 6 個(gè)濾波器,大小為 (2, 5) (3, 5) 和 (4, 5) 的濾波器各兩個(gè)。這些濾波器應(yīng)用于該矩陣上,它們的特殊之處在于都不是方矩陣,但它們的寬度和嵌入矩陣的寬度相等。所以每個(gè)卷積的結(jié)果將是一個(gè)列向量。
  • 卷積產(chǎn)生的每一列向量都使用了最大池化操作進(jìn)行下采樣。
  • 將最大池化操作的結(jié)果連接至將要傳遞給 softmax 函數(shù)進(jìn)行分類的最終向量。

二、背后的原理是什么?

檢測到特殊模式會(huì)激活每一次卷積的結(jié)果。通過改變卷積核的大小和連接它們的輸出,你可以檢測多個(gè)尺寸(2 個(gè)、3 個(gè)或 5 個(gè)相鄰單詞)的模式。

模式可以是像是「我討厭」、「非常好」這樣的表達(dá)式(詞級(jí)的 ngram?),因此 CNN 可以在不考慮其位置的情況下從句子中分辨它們。

 
 
 
 
  1. def get_cnn_model(): 
  2.     embedding_dim = 300 
  3.  
  4.     filter_sizes = [2, 3, 5] 
  5.     num_filters = 256 
  6.     drop = 0.3 
  7.  
  8.     inputs = Input(shape=(MAX_LENGTH,), dtype='int32') 
  9.     embedding = Embedding(input_dim=MAX_NB_WORDS, 
  10.                                 output_dim=embedding_dim, 
  11.                                 weights=[embedding_matrix], 
  12.                                 input_length=MAX_LENGTH, 
  13.                                 trainable=True)(inputs) 
  14.  
  15.     reshape = Reshape((MAX_LENGTH, embedding_dim, 1))(embedding) 
  16.     conv_0 = Conv2D(num_filters,  
  17.                     kernel_size=(filter_sizes[0], embedding_dim),  
  18.                     padding='valid', kernel_initializer='normal',  
  19.                     activation='relu')(reshape) 
  20.  
  21.     conv_1 = Conv2D(num_filters,  
  22.                     kernel_size=(filter_sizes[1], embedding_dim),  
  23.                     padding='valid', kernel_initializer='normal',  
  24.                     activation='relu')(reshape) 
  25.     conv_2 = Conv2D(num_filters,  
  26.                     kernel_size=(filter_sizes[2], embedding_dim),  
  27.                     padding='valid', kernel_initializer='normal',  
  28.                     activation='relu')(reshape) 
  29.  
  30.     maxpool_0 = MaxPool2D(pool_size=(MAX_LENGTH - filter_sizes[0] + 1, 1),  
  31.                          &
    分享標(biāo)題:CNN也能用于NLP任務(wù),一文簡述文本分類任務(wù)的7個(gè)模型
    URL標(biāo)題:http://www.dlmjj.cn/article/cojjpch.html