編者按:自然語言處理(NLP)是數(shù)據科學研究的一個溫床,它最常見的應用之一是情感分析。從民意調查到指定整個營銷策略,現(xiàn)在,這種技術已經完全重塑了企業(yè)的工作方式,這也是為什么每個數(shù)據科學家都要對它有所了解的原因之一。
本文將圍繞以twitter為代表英語文本進行分析,在文中,我們會逐步介紹常規(guī)情感分析所需的一系列步驟:從數(shù)據預處理開始,到探索文本數(shù)據,再到結合上下文產生理解,然后從數(shù)據中提取數(shù)字特征,并最終用這些特征集來訓練模型,使它具備識別情感的能力。
理解任務目標
實踐是檢驗真理的唯一標準,它同樣也是學習的一個好方法。為了讓讀者更貼近教程,這里我們選用Analytics Vidhya上的一個競賽任務:Twitter情感分析。里面包含下文使用的數(shù)據集。
在開始任務前,我們先來讀讀任務描述,理解任務目標:
這項任務的目的是檢測推文中的仇恨言論。為了簡單起見,這里我們只把包含種族主義和性別歧視的推文視為仇恨言論,因此,任務目標是從給定推文中分類出包含種族主義、性別歧視觀點的推文。
任務提供的訓練樣本有其相應的分類標簽,其中標簽“1”表示推文包含種族主義/性別歧視觀點,標簽“0”則是不包含。我們的目標是用訓練集訓練模型,讓它在測試集上準確預測推文標簽。
推文預處理和清洗
當我們進行探索時,電腦中的新問題就像現(xiàn)實生活中的陌生環(huán)境,我們對它一無所知,但它卻處處包含我們所需的信息。
上圖是兩個辦公室,一個凌亂不堪,一個整潔有序,你覺得自己在哪兒能更快找到文檔呢?這不是我們自己的房間,所以毫無疑問的,第二個。同理,如果我們的數(shù)據能以結構化的格式排列,模型也能更容易從中找到正確信息。
數(shù)據預處理是我們面對任何問題時必不可少的一步,在這個任務中,我們要做的是清除和推文情感不怎么相關的各種噪聲,比如標點符號、特殊字符、數(shù)字和表示語氣的詞。這些內容在文本上下文中沒有太大權重,再加上我們之后要提取數(shù)字特征,去除這些內容有助于之后獲得質量更高的特征空間。
首先,我們檢查數(shù)據,并加載必要的庫:
import re
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
importstring
import nltk
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
%matplotlib inline
train = pd.read_csv('train_E6oV3lV.csv')
test = pd.read_csv('test_tweets_anuFYb8.csv')
記得檢查一下訓練集的前幾行,看看有沒有問題:
train.head()
可以看到,數(shù)據一共有三列:id、標簽和推文文本。標簽是二進制目標變量,而推文包含我們將清洗和預處理的文字。以下是數(shù)據預處理的幾個要求:
為了照顧隱私,數(shù)據集把用戶名一致標記為@user,這個句柄對推文內容毫無用處,需要刪除。
文中的標點符號、特殊字符和數(shù)字也沒有多大含義,也起不到區(qū)分作用,需要刪除。
對于情感分析,一些非常短的詞也沒有實際價值,比如“pdx”“his”“all”,需要刪除。
一旦我們執(zhí)行了上述三個步驟,我們就可以將每個推文分成單個單詞或詞例,這是任何NLP任務中必須的步驟。
第4條推文中出現(xiàn)了一個“l(fā)ove”,考慮到其他推文中可能會出現(xiàn)類似的“l(fā)oves”“l(fā)oving”“l(fā)ovable”,我們可以把它們都縮成詞根“l(fā)ove”,減少數(shù)據中唯一單詞的總數(shù),同時也不會丟失大量信息。
1. 刪除推文句柄@user
如上所述,推文包含許多Twitter句柄(@user),我們需要刪掉它們。為了方便起見,這個步驟可以結合訓練集和測試集,把兩邊的句柄同時刪掉,防止之后進行重復操作。
combi = train.append(test, ignore_index=True)
下面是我們定義的函數(shù),用于刪除推文中不需要的內容。它有兩個參數(shù),一個是原始文本字符串,另一個是要從字符串中刪去的內容:
def remove_pattern(input_txt, pattern):
r = re.findall(pattern, input_txt)
for i in r:
input_txt = re.sub(i, '', input_txt)
return input_txt
接著,我們可以創(chuàng)建一個新的列tidy_tweet,用它存儲清洗后的推文:
#remove twitter handles(@user)
combi [ 'tidy_tweet' ] = np.vectorize(remove_pattern)(combi [ 'tweet' ],“@ [ w] *” )
2. 刪除標點符號、數(shù)字和特殊字符
之前提到了,標點符號、數(shù)字和特殊字符也沒有多大用處,所以在刪除句柄時,我們可以一并把它們處理了:
# remove special characters, numbers, punctuations
combi['tidy_tweet'] = combi['tidy_tweet'].str.replace("[^a-zA-Z#]", " ")
3. 刪除短詞
同理,一些過短的單詞也是我們的刪除目標,但執(zhí)行這一步時我們要小心,因為有些包含意義的單詞本身也很短。經過綜合評判,最后我們把長度為3及以下的詞語統(tǒng)統(tǒng)刪去:
combi['tidy_tweet'] = combi['tidy_tweet'].apply(lambda x: ' '.join([w for w in x.split() if len(w)>3]))
處理完畢后,我們需要回過頭來重新檢查數(shù)據集的前幾行,看看有沒有錯誤。
combi.head()
如上圖所示,我們可以明顯看出原始推文和tidy_tweet之間的區(qū)別,后者刪除了大量不必要的噪聲,只保留重要單詞。
4. 分詞
現(xiàn)在,我們已經對數(shù)據完成清洗,接下來就是在本文中標記出單獨的單詞或詞例,所謂分詞就是把文本切分成一個個單獨的詞的過程。
tokenized_tweet = combi['tidy_tweet'].apply(lambda x: x.split())
tokenized_tweet.head()
5. 提取詞干
詞干是詞的一部分,由詞根和詞綴構成,放在我們的任務中,就是把英文單詞的后綴“ing”“l(fā)y”“es”“s”等剝離。
from nltk.stem.porter import *
stemmer = PorterStemmer()
tokenized_tweet = tokenized_tweet.apply(lambda x: [stemmer.stem(i) for i in x]) # stemming
tokenized_tweet.head()
推文中單詞的可視化理解
到目前為止,我們已經完成對數(shù)據的預處理,之后就是探索這些數(shù)據,從中獲得重要理解。在開始探索前,結合任務目標,我們可能會產生一些新的問題:
整個數(shù)據集中出現(xiàn)頻率最高的單詞是哪個?
仇恨言論和非仇恨言論推文中,出現(xiàn)頻率最高的單詞又分別是哪個?
一條推文中有幾個詞例?
哪種趨勢和數(shù)據集本身有關?
哪種趨勢和這兩種言論有關?它們和相應情感相符嗎?
1. 找出推文中的常用詞:WordCloud
為了查看單體在整個訓練集中的分布情況,一種可行的方法是繪制WordCloud圖,這是一種可視化形式,能把出現(xiàn)頻率較高的詞放大,把出現(xiàn)頻率最高的較低的詞縮小。
all_words = ' '.join([text for text in combi['tidy_tweet']])
from wordcloud importWordCloud
wordcloud = WordCloud(width=800, height=500, random_state=21, max_font_size=110).generate(all_words)
plt.figure(figsize=(10, 7))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis('off')
plt.show()
如上圖所示,大多數(shù)單詞是積極的或中立的,其中“happy”“l(fā)ove”最為常見,從它們身上我們找不到和仇恨言論有關的內容。所以接下來,我們會為標簽為“1”和“0”的兩類數(shù)據單獨繪制WordCloud,并以此查看詞頻分布情況。
2. 非種族主義/性別歧視推文中的單詞
normal_words =' '.join([text for text in combi['tidy_tweet'][combi['label'] == 0]])
wordcloud = WordCloud(width=800, height=500, random_state=21, max_font_size=110).generate(normal_words)
plt.figure(figsize=(10, 7))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis('off')
plt.show()
和之前一樣,大多數(shù)詞是積極的或中立的,其中“happy”“smile”“l(fā)ove”最常見。
3. 種族主義/性別歧視推文的單詞
negative_words = ' '.join([text for text in combi['tidy_tweet'][combi['label'] == 1]])
wordcloud = WordCloud(width=800, height=500,
random_state=21, max_font_size=110).generate(negative_words)
plt.figure(figsize=(10, 7))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis('off')
plt.show()
我們可以清楚地看到,大多數(shù)詞語都有負面含義。這正好是一個非常出色的文本數(shù)據,之后我們可以基于它在twitter數(shù)據中添加主題標簽/趨勢。
4. 主題標簽對推文情感的影響
twitter里的主題標簽#就相當于國內社交平臺的話題,表示推文包含某個特定時間點的熱搜內容。這是文本中一項重要內容,我們可以利用這些標簽探索它們和推文情感的關系。
例如,下面是我們數(shù)據集中的一條推文:
這段內容涉及性別歧視,而它的主題標簽也傳達了同樣的意思。我們把這些標簽放進兩類推特文本的表格中,看看這些內容的出現(xiàn)情況。
# function to collect hashtags
def hashtag_extract(x):
hashtags = []
# Loop over the words in the tweet
for i in x:
ht = re.findall(r"#(w+)", i)
hashtags.append(ht)
return hashtags
# extracting hashtags from non racist/sexist tweets
HT_regular = hashtag_extract(combi['tidy_tweet'][combi['label'] == 0])
# extracting hashtags from racist/sexist tweets
HT_negative = hashtag_extract(combi['tidy_tweet'][combi['label'] == 1])
# unnesting list
HT_regular = sum(HT_regular,[])
HT_negative = sum(HT_negative,[])
非種族主義/性別歧視推文
a = nltk.FreqDist(HT_regular)
d = pd.DataFrame({'Hashtag': list(a.keys()),
'Count': list(a.values())})
# selecting top 10 most frequent hashtags
d = d.nlargest(columns="Count", n = 10)
plt.figure(figsize=(16,5))
ax = sns.barplot(data=d, x= "Hashtag", y = "Count")
ax.set(ylabel = 'Count')
plt.show()
非種族主義/性別歧視推文出現(xiàn)頻率較高的主題標簽都是積極的、正面的,這不難理解。那么我們來看看種族主義/性別歧視推文中的情況。
種族主義/性別歧視推文
b = nltk.FreqDist(HT_negative)
e = pd.DataFrame({'Hashtag': list(b.keys()), 'Count': list(b.values())})
# selecting top 10 most frequent hashtags
e = e.nlargest(columns="Count", n = 10)
plt.figure(figsize=(16,5))
ax = sns.barplot(data=e, x= "Hashtag", y = "Count")
ax.set(ylabel = 'Count')
plt.show()
正如預期的那樣,大多數(shù)標簽都是負面的,也有一些中性標簽。所以我們把這些主題標簽保留下來是個明確的選擇,它們確實包含區(qū)分仇恨言論和非仇恨言論的關鍵信息。
從推文中提取特征
如果要分析預處理數(shù)據,首先我們要把它們轉換為特征。現(xiàn)在,構建文本特征的方法有很多,比如詞袋模型、TF-IDF和詞嵌入。在這個問題下,我們主要介紹前兩種方法。
1. 詞袋特征
詞袋(Bag of Words,簡稱BoW)是一種統(tǒng)計某個詞在一份文檔中出現(xiàn)次數(shù)的算法,統(tǒng)計所得的詞頻數(shù)據可以用于比較文檔并測量其相似性。假設我們手頭有一個語料庫C {d1,d2…..dD}(D個文本),它包含N個唯一詞例(單詞),那么它的詞袋矩陣大小M就等于N×D。矩陣M中的每一行包含文檔D(i)中的詞例出現(xiàn)頻率。
讓我們用一個直觀的例子來理解這一點。假設我們有兩個文本:
D1:He is a lazy boy. She is also lazy.
D2:Smith is a lazy person.
它們包含的唯一詞例是[‘He’,’She’,’lazy’,’boy’,’Smith’,’person’],所以D=2,N=6,矩陣M=2×6,也就是:
上述矩陣中的列可用作構建分類模型的特征,當然,提到詞袋,首選sklearn,它的CountVectorizer函數(shù)可以直接創(chuàng)建詞袋特征。我們設max_features = 1000,取詞頻排名前1000的詞例。
from sklearn.feature_extraction.text importCountVectorizer
bow_vectorizer = CountVectorizer(max_df=0.90, min_df=2, max_features=1000, stop_words='english')
# bag-of-words feature matrix
bow = bow_vectorizer.fit_transform(combi['tidy_tweet'])
2. TF-IDF特征
這是另一種基于詞頻的方法,它和詞袋方法的不同之處在于它不僅考慮單個文檔(或推文)中單詞的出現(xiàn)次數(shù),而且考慮整個語料庫。
雖然是基于詞頻,但TF-IDF測量的是相關性,而非頻率。首先,它會統(tǒng)計某一特定文檔中的詞的出現(xiàn)次數(shù),但是,由于“and”、“the”之類的詞所有文檔中都頻繁出現(xiàn),這些詞的頻率需要調整。這就是逆文檔頻率的部分。出現(xiàn)某一個詞的文檔數(shù)量越多,這個詞作為信號的價值就越小。這樣做的目的是僅留下獨特的高頻詞用作標記。每個詞的TF-IDF相關性是一種歸一化的數(shù)據格式,總和也是1。
TF:詞例t出現(xiàn)在文檔中的次數(shù)/文檔詞例總數(shù)
IDF = log(N/n),其中N是文檔總數(shù),n是文檔中詞例t出現(xiàn)的次數(shù)
TF-IDF = TF×IDF
from sklearn.feature_extraction.text importTfidfVectorizer
tfidf_vectorizer = TfidfVectorizer(max_df=0.90, min_df=2, max_features=1000, stop_words='english')
# TF-IDF feature matrix
tfidf = tfidf_vectorizer.fit_transform(combi['tidy_tweet'])
模型構建:情感分析
現(xiàn)在,數(shù)據已經處理好了,數(shù)據的數(shù)字特征也已經被提取出來,最后就是用邏輯回歸構建模型。
1. 用詞袋特征構建模型
from sklearn.linear_model importLogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
train_bow = bow[:31962,:]
test_bow = bow[31962:,:]
# splitting data into training and validation set
xtrain_bow, xvalid_bow, ytrain, yvalid = train_test_split(train_bow, train['label'], random_state=42, test_size=0.3)
lreg = LogisticRegression()
lreg.fit(xtrain_bow, ytrain) # training the model
prediction = lreg.predict_proba(xvalid_bow) # predicting on the validation set
prediction_int = prediction[:,1] >= 0.3# if prediction is greater than or equal to 0.3 than 1 else 0
prediction_int = prediction_int.astype(np.int)
f1_score(yvalid, prediction_int) # calculating f1 score
輸出:0.525。
這個模型的F1 score是0.525,我們把它放到測試集上進行預測:
test_pred = lreg.predict_proba(test_bow)
test_pred_int = test_pred[:,1] >= 0.3
test_pred_int = test_pred_int.astype(np.int)
test['label'] = test_pred_int
submission = test[['id','label']]
submission.to_csv('sub_lreg_bow.csv', index=False) # writing data to a CSV file
得分:0.537。
2. TF-IDF特征
train_tfidf = tfidf[:31962,:]
test_tfidf = tfidf[31962:,:]
xtrain_tfidf = train_tfidf[ytrain.index]
xvalid_tfidf = train_tfidf[yvalid.index]
lreg.fit(xtrain_tfidf, ytrain)
prediction = lreg.predict_proba(xvalid_tfidf)
prediction_int = prediction[:,1] >= 0.3
prediction_int = prediction_int.astype(np.int)
f1_score(yvalid, prediction_int)
輸出:0.538,最終得分:0.547,比起詞袋特征有所改善。
以上就是twitter情感分析模型構建的全部教程,希望本文對你有幫助。
-
Twitter
+關注
關注
0文章
176瀏覽量
15773 -
數(shù)據集
+關注
關注
4文章
1197瀏覽量
24531 -
自然語言處理
+關注
關注
1文章
585瀏覽量
13418
原文標題:【入門】Twitter情感分析全面分析指南(含代碼)
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論