Kaggleword2vecNLP教程第一部分:寫給入門者的詞袋

apachecn_飛龍發表於2018-09-27

原文:Bag of Words Meets Bags of Popcorn

譯者:飛龍

協議:CC BY-NC-SA 4.0

自豪地採用谷歌翻譯

第一部分:寫給入門者的詞袋

什麼是 NLP

NLP(自然語言處理)是一組用於處理文字問題的技術。這個頁面將幫助你從載入和清理IMDB電影評論來起步,然後應用一個簡單的詞袋模型,來獲得令人驚訝的準確預測,評論是點贊還是點踩。

在你開始之前

本教程使用 Python。如果你之前沒有使用過 Python,我們建議你前往泰坦尼克號競賽 Python 教程,熟悉一下(檢視隨機森林介紹)。

如果你已熟悉 Python 並使用基本的 NLP 技術,則可能需要跳到第 2 部分。

本教程的這一部分不依賴於平臺。在本教程中,我們將使用各種 Python 模組進行文字處理,深度學習,隨機森林和其他應用。詳細資訊請參閱“配置你的系統”頁面。

有很多很好的教程,以及實際上用 Python 寫的關於 NLP 和文字處理的整本書。本教程絕不是詳盡無遺的 – 只是為了幫助你以電影評論起步。

程式碼

第 1 部分的教程程式碼就在這裡

讀取資料

可以從“資料”頁面下載必要的檔案。你需要的第一個檔案是unlabeledTrainData,其中包含 25,000 個 IMDB 電影評論,每個評論都帶有正面或負面情感標籤。

接下來,將製表符分隔檔案讀入 Python。為此,我們可以使用泰坦尼克號教程中介紹的pandas包,它提供了read_csv函式,用於輕鬆讀取和寫入資料檔案。如果你之前沒有使用過pandas,則可能需要安裝它。

# 匯入 pandas 包,然後使用 "read_csv" 函式讀取標記的訓練資料
import pandas as pd       
train = pd.read_csv("labeledTrainData.tsv", header=0, 
                    delimiter="	", quoting=3)

這裡,header=0表示檔案的第一行包含列名,delimiter= 表示欄位由製表符分隔,quoting=3讓 Python 忽略雙引號,否則試圖讀取檔案時,可能會遇到錯誤。

我們可以確保讀取 25,000 行和 3 列,如下所示:

>>> train.shape
(25000, 3)

>>> train.columns.values
array([id, sentiment, review], dtype=object)

這三列被稱為"id""sentiment""array"。 現在你已經讀取了培訓集,請檢視幾條評論:

print train["review"][0]

提醒一下,這將顯示名為"review"的列中的第一個電影評論。 你應該看到一個像這樣開頭的評論:

"With all this stuff going down at the moment with MJ i`ve started listening to his music, watching the odd documentary here and there, watched The Wiz and watched Moonwalker again. Maybe i just want to get a certain insight into this guy who i thought was really cool in the eighties just to maybe make up my mind whether he is guilty or innocent. Moonwalker is part biography, part feature film which i remember going to see at the cinema when it was originally released. Some of it has subtle messages about MJ`s feeling towards the press and also the obvious message of drugs are bad m`kay. <br/><br/>..."

有 HTML 標籤,如"<br/>",縮寫,標點符號 – 處理線上文字時的所有常見問題。 花一些時間來檢視訓練集中的其他評論 – 下一節將討論如何為機器學習整理文字。

資料清理和文字預處理

刪除 HTML 標記:BeautifulSoup

首先,我們將刪除 HTML 標記。 為此,我們將使用BeautifulSoup。 如果你沒有安裝,請從命令列(不是從 Python 內部)執行以下操作:

$ sudo pip install BeautifulSoup4

然後,從 Python 中載入包並使用它從評論中提取文字:

# Import BeautifulSoup into your workspace
from bs4 import BeautifulSoup             

# Initialize the BeautifulSoup object on a single movie review     
example1 = BeautifulSoup(train["review"][0])  

# Print the raw review and then the output of get_text(), for 
# comparison
print train["review"][0]
print example1.get_text()

呼叫get_text()會為你提供不帶標籤的評論文字。如果你瀏覽BeautifulSoup文件,你會發現它是一個非常強大的庫 – 比我們對此資料集所需的功能更強大。但是,使用正規表示式刪除標記並不是一種可靠的做法,因此即使對於像這樣簡單的應用程式,通常最好使用像BeautifulSoup這樣的包。

處理標點符號,數字和停止詞:NLTK 和正規表示式

在考慮如何清理文字時,我們應該考慮我們試圖解決的資料問題。對於許多問題,刪除標點符號是有意義的。另一方面,在這種情況下,我們正在解決情感分析問題,並且有可能"!!!"或者":-("可以帶有情感,應該被視為單詞。在本教程中,為簡單起見,我們完全刪除了標點符號,但這是你可以自己玩的東西。

與之相似,在本教程中我們將刪除數字,但還有其他方法可以處理它們,這些方法同樣有意義。例如,我們可以將它們視為單詞,或者使用佔位符字串(例如"NUM")替換它們。

要刪除標點符號和數字,我們將使用一個包來處理正規表示式,稱為re。Python 內建了該軟體包;無需安裝任何東西。對於正規表示式如何工作的詳細說明,請參閱包文件。現在,嘗試以下方法:

import re
# 使用正規表示式執行查詢和替換
letters_only = re.sub("[^a-zA-Z]",           # 要查詢的模式串
                      " ",                   # 要替換成的模式串
                      example1.get_text() )  # 要從中查詢的字串
print letters_only

正規表示式的完整概述超出了本教程的範圍,但是現在知道[]表示分組成員而^表示“不”就足夠了。 換句話說,上面的re.sub()語句說:“查詢任何不是小寫字母(a-z)或大寫字母(A-Z)的內容,並用空格替換它。”

我們還將我們的評論轉換為小寫並將它們分成單個單詞(在 NLP 術語中稱為“分詞”):

lower_case = letters_only.lower()        # 轉換為小寫
words = lower_case.split()               # 分割為單詞

最後,我們需要決定如何處理那些沒有多大意義的經常出現的單詞。 這樣的詞被稱為“停止詞”;在英語中,它們包括諸如“a”,“and”,“is”和“the”之類的單詞。方便的是,Python 包中內建了停止詞列表。讓我們從 Python 自然語言工具包(NLTK)匯入停止詞列表。 如果你的計算機上還沒有該庫,則需要安裝該庫;你還需要安裝附帶的資料包,如下所示:

import nltk
nltk.download()  # 下載文字資料集,包含停止詞

現在我們可以使用nltk來獲取停止詞列表:

from nltk.corpus import stopwords # 匯入停止詞列表
print stopwords.words("english") 

這將允許你檢視英語停止詞列表。 要從我們的電影評論中刪除停止詞,請執行:

# 從 "words" 中移除停止詞
words = [w for w in words if not w in stopwords.words("english")]
print words

這會檢視words列表中的每個單詞,並丟棄在停止詞列表中找到的任何內容。 完成所有這些步驟後,你的評論現在應該是這樣的:

[u`stuff`, u`going`, u`moment`, u`mj`, u`ve`, u`started`, u`listening`, u`music`, u`watching`, u`odd`, u`documentary`, u`watched`, u`wiz`, u`watched`, u`moonwalker`, u`maybe`, u`want`, u`get`, u`certain`, u`insight`, u`guy`, u`thought`, u`really`, u`cool`, u`eighties`, u`maybe`, u`make`, u`mind`, u`whether`, u`guilty`, u`innocent`, u`moonwalker`, u`part`, u`biography`, u`part`, u`feature`, u`film`, u`remember`, u`going`, u`see`, u`cinema`, u`originally`, u`released`, u`subtle`, u`messages`, u`mj`, u`feeling`, u`towards`, u`press`, u`also`, u`obvious`, u`message`, u`drugs`, u`bad`, u`m`, u`kay`,.....]

不要擔心在每個單詞之前的u;它只是表明 Python 在內部將每個單詞表示為 unicode 字串

我們可以對資料做很多其他的事情 – 例如,Porter Stemming(詞幹提取)和 Lemmatizing(詞形還原)(都在 NLTK 中提供)將允許我們將"messages""message""messaging"視為同一個詞,這當然可能很有用。 但是,為簡單起見,本教程將就此打住。

把它們放在一起

現在我們有了清理評論的程式碼 – 但我們需要清理 25,000 個訓練評論! 為了使我們的程式碼可重用,讓我們建立一個可以多次呼叫的函式:

def review_to_words( raw_review ):
    # 將原始評論轉換為單詞字串的函式
    # 輸入是單個字串(原始電影評論),
    # 輸出是單個字串(預處理過的電影評論)
    # 1. 移除 HTML
    review_text = BeautifulSoup(raw_review).get_text() 
    #
    # 2. 移除非字母        
    letters_only = re.sub("[^a-zA-Z]", " ", review_text) 
    #
    # 3. 轉換為小寫,分成單個單詞
    words = letters_only.lower().split()                             
    #
    # 4. 在Python中,搜尋集合比搜尋列表快得多,
    #    所以將停止詞轉換為一個集合
    stops = set(stopwords.words("english"))                  
    # 
    # 5. 刪除停止詞
    meaningful_words = [w for w in words if not w in stops]   
    #
    # 6. 將單詞連線成由空格分隔的字串,
    #    並返回結果。
    return( " ".join( meaningful_words ))   

這裡有兩個新元素:首先,我們將停止詞列表轉換為不同的資料型別,即集合。 這是為了速度;因為我們將呼叫這個函式數萬次,所以它需要很快,而 Python 中的搜尋集合比搜尋列表要快得多。

其次,我們將這些單詞合併為一段。 這是為了使輸出更容易在我們的詞袋中使用,在下面。 定義上述函式後,如果你為單個評論呼叫該函式:

clean_review = review_to_words( train["review"][0] )
print clean_review

它應該為你提供與前面教程部分中所做的所有單獨步驟完全相同的輸出。 現在讓我們遍歷並立即清理所有訓練集(這可能需要幾分鐘,具體取決於你的計算機):

# 根據 dataframe 列大小獲取評論數
num_reviews = train["review"].size

# 初始化空列表來儲存清理後的評論
clean_train_reviews = []

# 遍歷每個評論;建立索引 i
# 範圍是 0 到電影評論列表長度
for i in xrange( 0, num_reviews ):
    # 為每個評論呼叫我們的函式,
    # 並將結果新增到清理後評論列表中
    clean_train_reviews.append( review_to_words( train["review"][i] ) )

有時等待冗長的程式碼的執行會很煩人。 編寫提供狀態更新的程式碼會很有幫助。 要讓 Python 在其處理每 1000 個評論後列印狀態更新,請嘗試在上面的程式碼中新增一兩行:

print "Cleaning and parsing the training set movie reviews...
"
clean_train_reviews = []
for i in xrange( 0, num_reviews ):
    # 如果索引被 1000 整除,列印訊息
    if( (i+1)%1000 == 0 ):
        print "Review %d of %d
" % ( i+1, num_reviews )                                                                    
    clean_train_reviews.append( review_to_words( train["review"][i] ))

從詞袋建立特徵(使用sklearn

現在我們已經整理了我們的訓練評論,我們如何將它們轉換為機器學習的某種數字表示?一種常見的方法叫做詞袋。詞袋模型從所有文件中學習詞彙表,然後通過計算每個單詞出現的次數對每個文件進行建模。例如,考慮以下兩句話:

句子1:"The cat sat on the hat"

句子2:"The dog ate the cat and the hat"

從這兩個句子中,我們的詞彙如下:

{ the, cat, sat, on, hat, dog, ate, and }

為了得到我們的詞袋,我們計算每個單詞出現在每個句子中的次數。在句子 1 中,“the”出現兩次,“cat”,“sat”,“on”和“hat”每次出現一次,因此句子 1 的特徵向量是:

{ the, cat, sat, on, hat, dog, ate, and }

句子 1:{ 2, 1, 1, 1, 1, 0, 0, 0 }

同樣,句子 2 的特徵是:{ 3, 1, 0, 0, 1, 1, 1, 1}

在 IMDB 資料中,我們有大量的評論,這將為我們提供大量的詞彙。要限制特徵向量的大小,我們應該選擇最大詞彙量。下面,我們使用 5000 個最常用的單詞(記住已經刪除了停止詞)。

我們將使用 scikit-learn 中的feature_extraction模組來建立詞袋特徵。如果你學習了泰坦尼克號競賽中的隨機森林教程,那麼你應該已經安裝了 scikit-learn;否則你需要安裝它

print "Creating the bag of words...
"
from sklearn.feature_extraction.text import CountVectorizer

# 初始化 "CountVectorizer" 物件,
# 這是 scikit-learn 的一個詞袋工具。
vectorizer = CountVectorizer(analyzer = "word",   
                             tokenizer = None,    
                             preprocessor = None, 
                             stop_words = None,   
                             max_features = 5000) 

# fit_transform() 有兩個功能:
# 首先,它擬合模型並學習詞彙;
# 第二,它將我們的訓練資料轉換為特徵向量。
# fit_transform 的輸入應該是字串列表。
train_data_features = vectorizer.fit_transform(clean_train_reviews)

# Numpy 陣列很容易使用,因此將結果轉換為陣列
train_data_features = train_data_features.toarray()

要檢視訓練資料陣列現在的樣子,請執行以下操作:

>>> print train_data_features.shape
(25000, 5000)

它有 25,000 行和 5,000 個特徵(每個詞彙一個)。

請注意,CountVectorizer有自己的選項來自動執行預處理,標記化和停止詞刪除 – 對於其中的每一個,我們不指定None,可以使用內建方法或指定我們自己的函式來使用。 詳細資訊請參閱函式文件。 但是,我們想在本教程中編寫我們自己的資料清理函式,來向你展示如何逐步完成它。

現在詞袋模型已經訓練好了,讓我們來看看詞彙表:

# 看看詞彙表中的單詞
vocab = vectorizer.get_feature_names()
print vocab

如果你有興趣,還可以列印詞彙表中每個單詞的計數:

import numpy as np

# 求和詞彙表中每個單詞的計數
dist = np.sum(train_data_features, axis=0)

# 對於每個詞,列印它和它在訓練集中的出現次數
for tag, count in zip(vocab, dist):
    print count, tag

隨機森林

到了這裡,我們有詞袋的數字訓練特徵和每個特徵向量的原始情感標籤,所以讓我們做一些監督學習! 在這裡,我們將使用我們在泰坦尼克號教程中介紹的隨機森林分類器。 隨機森林演算法包含在 scikit-learn 中(隨機森林使用許多基於樹的分類器來進行預測,因此是“森林”)。 下面,我們將樹的數量設定為 100 作為合理的預設值。 更多樹可能(或可能不)表現更好,但肯定需要更長時間來執行。 同樣,每個評論所包含的特徵越多,所需的時間就越長。

print "Training the random forest..."
from sklearn.ensemble import RandomForestClassifier

# 使用 100 棵樹初始化隨機森林分類器
forest = RandomForestClassifier(n_estimators = 100) 

# 使用詞袋作為特徵並將情感標籤作為響應變數,使森林擬合訓練集
# 這可能需要幾分鐘來執行
forest = forest.fit( train_data_features, train["sentiment"] )

建立提交

剩下的就是在我們的測試集上執行訓練好的隨機森林並建立一個提交檔案。 如果你還沒有這樣做,請從“資料”頁面下載testData.tsv。 此檔案包含另外 25,000 條評論和標籤;我們的任務是預測情感標籤。

請注意,當我們使用詞袋作為測試集時,我們只呼叫transform,而不是像訓練集那樣呼叫fit_transform。 在機器學習中,你不應該使用測試集來擬合你的模型,否則你將面臨過擬合的風險。 出於這個原因,我們將測試集保持在禁止狀態,直到我們準備好進行預測。

# 讀取測試資料
test = pd.read_csv("testData.tsv", header=0, delimiter="	", 
                   quoting=3 )

# 驗證有 25,000 行和 2 列
print test.shape

# 建立一個空列表並逐個附加乾淨的評論
num_reviews = len(test["review"])
clean_test_reviews = [] 

print "Cleaning and parsing the test set movie reviews...
"
for i in xrange(0,num_reviews):
    if( (i+1) % 1000 == 0 ):
        print "Review %d of %d
" % (i+1, num_reviews)
    clean_review = review_to_words( test["review"][i] )
    clean_test_reviews.append( clean_review )

# 獲取測試集的詞袋,並轉換為 numpy 陣列
test_data_features = vectorizer.transform(clean_test_reviews)
test_data_features = test_data_features.toarray()

# 使用隨機森林進行情感標籤預測
result = forest.predict(test_data_features)

# 將結果複製到帶有 "id" 列和 "sentiment" 列的 pandas dataframe
output = pd.DataFrame( data={"id":test["id"], "sentiment":result} )

# 使用 pandas 編寫逗號分隔的輸出檔案
output.to_csv( "Bag_of_Words_model.csv", index=False, quoting=3 )

恭喜,你已準備好第一次提交! 嘗試不同的事情,看看你的結果如何變化。 你可以以不同方式清理評論,為詞袋錶示選擇不同數量的詞彙表單詞,嘗試 Porter Stemming,不同的分類器或任何其他的東西。 要在不同的資料集上試用你的 NLP 招式,你還可以參加我們的爛番茄比賽。 或者,如果你為完全不同的東西做好了準備,請訪問深度學習和詞向量頁面。


相關文章