要查看原文,請參看:原文地址
簡介
自然語言處理是當今十分熱門的數(shù)據科學研究項目。情感分析則是自然語言處理中一個很常見的實踐。例如可以借助民意測試來構建完整的市場策略,該領域已經極大的改變了當前的商業(yè)運行模式,所以每一個數(shù)據科學家都應該熟悉該領域的內容。
成千上萬的文本數(shù)據可以在短時間內分析出情感類型(甚至是其他的特征,包括命名實體,話題,主題等等)。相比而言,如果使用人工來做這件事情,那將消耗一個團隊的人數(shù)小時的時間。

接下來,我們將按照處理一般情感分析問題的方法來開展我們的工作。首先,我們需要預處理和清理Twitter的原始數(shù)據。然后,我們需要觀察清洗好的數(shù)據,并依靠直觀感覺來做一些常識。之后,我們要量化特征,并使用量化后的數(shù)據集來訓練模型,最終實現(xiàn)標記tweet的情感。
這可以說是NLP中最有意思的挑戰(zhàn)了,我實在有點迫不及待的跟你一起開始這次探索之旅!
內容的小目錄
- 1.理解問題
- 2.預處理和數(shù)據清洗
- 3.制造故事和可視化效果
- 4.從清洗好的推文中提取特征
- 5.訓練模型:情感分析
- 6.下一步要做什么
1.理解問題
在開始我們的工作之前,讓我們再來看一遍問題,這對于確定我們的目標是非常重要的。問題是這樣的:
這項任務的目標是檢測出tweets中的負面言論。簡單起見,我們認為包含負面言論的可以認為是種族主義或者性別歧視相關的內容。所以,任務就轉化成了區(qū)分種族主義和性別歧視與其他的內容的分類任務。
在正式的情況下,給定的訓練樣本應該都已經標注好了,標簽‘1’表示種族主義/性別歧視,標簽‘0’表示不是種族主義/性別歧視。你的目標就是在給定的測試集上預測標簽。
注意:本實驗的評測標準為F1-score

從我個人來說,負面言論,網絡暴力,社交網絡霸凌這些事情已經成為了非常尖銳的問題,能夠做一個分析系統(tǒng),去檢測這些內容將會大大的發(fā)揮作用,肅清網絡空間,清除網絡暴力,還給網絡一片凈土。這也是我樂于接受這個任務的原因。現(xiàn)在就讓我們來看看詳細的步驟該怎么做吧。
2.Tweets 預處理和數(shù)據清洗
讓我們看一下下面兩張圖片,這是處于兩種不同狀態(tài)的辦公室。一個凌亂不堪,一個清理的干凈整潔。

假如你要在這個辦公室里尋找某個文件,在那個環(huán)境下你更容易找到?當然是后面那個干凈整潔的,每一個物品都歸置到了合適的位置。數(shù)據清洗跟這個過程十分相似。如果數(shù)據能夠被規(guī)整成結構化的格式,那從中找到正確的信息將輕而易舉。
預處理數(shù)據是個必要的步驟,這是為了數(shù)據挖掘做準備。這會讓提取信息和機器學習算法的處理變得簡單。如果跳過這一步,那么很大概率你會碰上充滿噪聲和偏差的數(shù)據集。這一步的目的就是把那些噪聲信息剔除掉,因為噪聲信息對于情感分析沒有什么貢獻,比如那些標點符號,特殊字符,數(shù)字,以及對文本的權重貢獻很低的內容。
在后續(xù)的步驟中,我們會從數(shù)據集中提取數(shù)字特征。這個特征空間是使用數(shù)據集中所有不重復的單詞構建的。所以如果我們對數(shù)據預處理做得好,那之后我們也會獲得一個品質更好的特征空間。
首先,讓我們讀出數(shù)據,并加載必要的依賴庫。你可以在下面的地址下載數(shù)據集:數(shù)據集下載地址
import re
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import string
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')
讓我們來看下數(shù)據
train.head()

可以看到,數(shù)據包含三列,id,label和tweet.label是一個二進制數(shù)值,tweet包含了我們需要清理的評論內容。
看了頭部數(shù)據之后,我們大概可以開始清理數(shù)據了,清理大概可以從下面幾個方面入手:
- 由于隱私保護的問題,Twitter的用戶名已經被隱去,取而代之的是‘@user’。 這個標簽沒有任何實際意義。
- 我們也考慮去掉標點符號,數(shù)字甚至特殊字符,他們也對區(qū)分不同的評論起不到任何作用。
- 大多數(shù)太短的詞起不到什么作用,比如‘pdx’,‘his’,‘all’。所以我們也把這些詞去掉。
- 執(zhí)行完上述三步之后,我們就可以把每一條評論切分成獨立的單詞或者符號了,這在所有NLP任務中都是必須的一步。
- 在第四個數(shù)據中,有一個單詞‘love’.與此同時,在余下的語料中我們可能會有更多的單詞,例如loves,loving,lovable等等。這些詞其實都是一個詞。如果我們能把這些詞都歸到它們的根源上,也就是都轉換成love,那么我們就可以大大降低不同單詞的數(shù)量,而不會損失太多信息。
A)移除Twitter標識@user
如上所述,這些評論文本包含很多Twitter標記,這些都是Twitter上面的用戶信息。我們需要把這些內容刪掉,他們對于情感分析沒有什么幫助。
方便起見,先把訓練集和測試集合起來。避免在訓練集和測試集上重復操作的麻煩。
combi = train.append(test, ignore_index=True)
下面是一個自定義的方法,用于正則匹配刪除文本中不想要的內容。它需要兩個參數(shù),一個是原始文本,一個是正則規(guī)則。這個方法的返回值是原始字符串清除匹配內容后剩下的字符。在我們的實驗中,我們將使用這個方法來去除@user標記
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
現(xiàn)在,我們新建一列tidy_tweet ,用于存放處理后的內容。就是上面說的去掉Twitter標記的內容。
# remove twitter handles (@user)
combi['tidy_tweet'] = np.vectorize(remove_pattern)(combi['tweet'], "@[\w]*")
B)去除標點符號,數(shù)字和特殊字符
這些字符都是沒有意義的。跟上面的操作一樣,我們把這些字符也都剔除掉。
使用替換方法,去掉這些非字母內容
# remove special characters, numbers, punctuations
combi['tidy_tweet'] = combi['tidy_tweet'].str.replace("[^a-zA-Z#]", " ")
C) 移除短單詞
這里要注意到底多長的單詞應該移除掉。我的選擇是小于等于三的都去掉。例如hmm,oh這樣的都沒啥用,刪掉這些內容好一些
combi['tidy_tweet'] = combi['tidy_tweet'].apply(lambda x: ' '.join([w for w in x.split() if len(w)>3]))
我們再來看一下數(shù)據集的頭幾行
combi.head()

此時,清洗前后的數(shù)據對比已經非常明顯了。重要的單詞被留了下來,噪聲內容被剔除了。
D)符號化
下面我們要把清洗后的數(shù)據集符號化。符號指的是一個個的單詞,符號化的過程就是把字符串切分成符號的過程。
tokenized_tweet = combi['tidy_tweet'].apply(lambda x: x.split())
tokenized_tweet.head()

E)提取詞干
提取詞干說的是基于規(guī)則從單詞中去除后綴的過程。例如,play,player,played,plays,playing都是play的變種。
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()

現(xiàn)在,我們把這些符號重新拼回去。使用nltk的MosesDetokenizer方法很容易做到。
from nltk.tokenize.moses import MosesDetokenizer
detokenizer = MosesDetokenizer()
for i in range(len(tokenized_tweet)):
tokenized_tweet[i] = detokenizer.detokenize(tokenized_tweet[i], return_str=True)
combi['tidy_tweet'] = tokenized_tweet
3.制造故事和可視化效果
在這一小節(jié),我們要深入了解數(shù)據。不論是文本數(shù)據還是其他數(shù)據,探索并進行數(shù)據可視化都是一個快速深入了解的必要手段。不必局限于本教程所述的幾種方式,你可以放開手腳嘗試更多的方法。
開始探索之前,我們先來思考幾個關于數(shù)據方面的問題:
- 數(shù)據集中最常見的單詞有哪些?
- 數(shù)據集上表述正面和負面言論的常見詞匯有哪些?
- 評論一般有多少主題標簽?
- 我的數(shù)據集跟哪些趨勢相關?
- 哪些趨勢跟情緒相關?他們和情緒是吻合的嗎?
A)使用 詞云 來了解評論中最常用的詞匯
現(xiàn)在,我想了解一下定義的情感在給定的數(shù)據集上是如何分布的。一種方法是畫出詞云來了解單詞分布。
詞云指的是一種用單詞繪制的圖像。出現(xiàn)頻率越高的詞在圖案中越大,出現(xiàn)頻率越低的詞在圖案中越小。
下面就來繪制基于我們的數(shù)據的詞云圖像。
all_words = ' '.join([text for text in combi['tidy_tweet']])
from wordcloud import WordCloud
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()

可以看到大部分詞匯都是正能量的或者中性的。happy和love是高頻詞匯。從這上面似乎看不出這些內容跟種族歧視或者性別歧視有什么關系。所以,我們應該分開去畫詞云,分成是種族歧視/性別歧視或者不是兩種數(shù)據。
B)非種族歧視/性別歧視的數(shù)據
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()

可以看出,這個詞云上的詞基本都是正能量或者中性詞。happy,smile,love都是高頻詞。大多數(shù)高頻詞跟我們的分類是吻合的,都屬于積極向上的情感。如法炮制,我們將畫出負能量情感詞云。我們希望能看到那些負面的,種族歧視,性別歧視相關的詞。
C)種族歧視/性別歧視數(shù)據
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ù)據集還不錯。下一步,我們將進行分析這些Twitter數(shù)據上的主題標簽。
D)理解主題標簽/流行趨勢對于評論情感的影響
主題標簽指的是Twitter中用來在一段時間內標注某些流行趨勢的詞語。我們需要對這些標簽進行檢測,看看他們是不是會對情感分析任務產生影響,是否能夠對區(qū)分評論有幫助。
帶有#前綴的詞就是主題標簽,例如,下面的情況:

這個例子似乎是帶有性別歧視的內容,而主題標簽看起來也有這個意思。
我們存下所有主題標簽,并分成兩類,一類是非歧視內容中的標簽,一類是帶有歧視內容中的標簽。
# 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,[])
現(xiàn)在,我們已經準備好了主題標簽,可以把top n的標簽畫出來。
先來看一下非歧視組的標簽吧。
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()

所有標簽都是正向的,這符合預期?,F(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ù)屬于中性的。所有,留下這些標簽用于后續(xù)的計算是個好主意。下面,我們將開始從符號化數(shù)據中提取標簽。
4.從清洗后的推文中提取特征
要分析清洗后的數(shù)據,就要把它們轉換成特征。根據用途來說,文本特征可以使用很多種算法來轉換。比如詞袋模型(Bag-Of-Words),TF-IDF,word Embeddings之類的方法。
在本文中,我使用了Bag-Of-Words和TF-IDF兩個方法。
詞袋特征
Bag-Of-Words是一種數(shù)字化表達特征的方式。假設有一個語料集合C,其中有D篇文章(d1,d2,...dD),在C中有N個不重復的符號。那么這N個符號(即單詞)構成一個列表,那么詞袋模型的矩陣M的大小就是D*N.M中的每一行記錄了一篇文章D(i)中對應符號的詞頻。
讓我們用一個簡單的例子來加強理解。假設我們只有兩篇文章
D1: He is a lazy boy. She is also lazy.
D2: Smith is a lazy person.
構建包含所有去重單詞的list
= [‘He’,’She’,’lazy’,’boy’,’Smith’,’person’]
那么,在這個語料C上,D=2,N=6
詞袋模型的矩陣M的大小就是2*6

現(xiàn)在,這個矩陣就可以作為特征矩陣來構建一個分類模型了。
使用sklearn的CountVectorizer方法可以輕松的構建詞袋模型。
設置參數(shù)max_features = 1000 ,只取詞頻前1000的詞。
from sklearn.feature_extraction.text import CountVectorizer
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'])
TF-IDF特征
這個方法也是基于詞頻的。但是它跟詞袋模型還有一些區(qū)別,主要是它還考慮了一個單詞在整個語料庫上的情況而不是單一文章里的情況。
TF-IDF方法會對常用的單詞做懲罰,降低它們的權重。同時對于某些在整個數(shù)據集上出現(xiàn)較少,但是在部分文章中表現(xiàn)較好的詞給予了較高的權重。
來深入了解一下TF-IDF:
- 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 import TfidfVectorizer
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'])
5.訓練模型:情感分析
到目前為止,所有預處理都已經完成了。下面我們就開始基于這兩個特征集合開始訓練預測模型。
我們使用邏輯回歸來構建模型。邏輯回歸大意,會擬合數(shù)據到一個邏輯方程式上,從而預測事件發(fā)生的概率。
下面這個方程就是邏輯回歸使用的式子:

要了解更多邏輯回歸的內容,可以閱讀以下內容:邏輯回歸文獻地址
注意:如果你想嘗試其他的機器學習算法,諸如隨機森林,支持向量機,XGBoot,下面這個地址可以提供一套成熟的課程來幫助你在情感分析上進行實踐。
地址:https://trainings.analyticsvidhya.com.
A)使用詞袋模型特征集合構建模型
from sklearn.linear_model import LogisticRegression
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.53
訓練出的邏輯回歸模型在驗證集上給出的f1值為0.53?,F(xiàn)在,我們使用這個模型來預測測試集數(shù)據。
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
在公開的排行榜上,f1值是0.567.現(xiàn)在我們再來訓練基于TF-IDF特征集的模型,看看表現(xiàn)如何。
B)使用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.544
這個結果跟公開排行榜上的0.564更接近了,看起來比詞袋模型有所提升。
6.下一步要做什么?
再打一遍廣告,如果你想更進一步的研究情感分析,我們?yōu)槟銣蕚淞艘幌盗械恼n程,可以在下面這個地址訪問:https://trainings.analyticsvidhya.com/.
這個課程包括了更多高級技術,比如特征提取的時候使用word2vec模型,更多機器學習算法,模型調參等等內容。
在課程中,你會學習到下面的內容:(翻譯按:反反復復?。?/p>
- 使用Embeddings(word2vec,doc2vec)來創(chuàng)建更好的特征
- 實踐先進的機器學習算法,比如SVM,RF,XGBoost
- 模型調參
- 自定義指標
結語
在本文中,我們學習了如何實際解決情感分析的問題。我們對數(shù)據進行預處理并進行了探索。然后我們使用詞袋模型,TF-IDF方法提取特征。最后構建了兩個分類模型。
你覺得這篇文章有用嗎?你有什么好的技巧嗎?你在特征提取環(huán)節(jié)使用過什么其他方法嗎?歡迎來討論和分享你的經驗在這個地址。。。,我們很期待跟你進行討論。