宽客秀

宽客秀

Quant.Show的Web3站点,Archives from quant.show

基於神經網絡的情感分析

1. 概述#

情感分析是自然語言處理中常見的案例,多為對語言文字所表達的積極或消極情感做判斷。之前,讀過雲帆先生寫的一篇 《自然語言處理用於量化分析》,受益匪淺,再加上近期在自然語言處理的項目上盤桓了一段時間,本文就當是對自然語言處理中的情感分析話題做了簡單的心得總結。

本文仍然採用雲帆先生用的 Keras 框架,神經網絡上以 Embedding+LSTM 或者 Embedding+CNN+Pooling 為主。(本文對作為自然語言處理特有的 Embedding 會著重做一些說明。)

訓練數據集及驗證數據集採用 Sarcasm_Headlines_Dataset(Misra,Rishabh and Arora, Prahal,2019),
該數據將文章標題分類為諷刺性或非諷刺性,具體而言包含 3 個屬性(本文主要使用前 2 個屬性):
1)is_sarcastic: 1 if the record is sarcastic otherwise 0
2)headline: the headline of the news article
3)article_link: link to the original news article. Useful in collecting supplementary data

預訓練詞向量數據採用 GloVe(Jeffrey Pennington,Richard Socher, and Christopher D. Manning,2014),該詞向量是在 2014 年的英文維基百科上訓練的,有 400k 個不同的詞,每個詞用 100 維向量表示。除 GloVe 以外,word2vec-api也提供了很多預訓練詞向量庫。中文詞向量也提供了不少基於中文的預訓練詞向量庫。

2. 數據獲取#

2.1 從文件獲取數據#

def parse_data(file):
for l in open(file,'r'):
yield json.loads(l)

data = list(parse_data('./Sarcasm_Headlines_Dataset_v2.json'))

2.2 將數據劃分為訓練集和驗證集#

X = []
y = []
for s in data:
X.append(s['headline'])
y.append(s['is_sarcastic'])

訓練集(包含驗證集):測試集 = 9:1#

trainval_size = int(len(X) * train_ratio)

訓練集及驗證集#

X_trainval = X[_size]
y_trainval = y[_size]

訓練集:驗證集 = 8:2#

X_train, X_valid,y_train, y_valid = train_test_split(X_trainval,
y_trainval,
test_size = valid_ratio,
random_state=12)

測試集#

X_test = X[trainval_size:]
y_test = y[trainval_size:]

2.3 生成訓練及驗證用的序列#

1、生成 tokenizer
tokenizer 的主要作用是把所有詞語變成索引。

# 定義 tokenizer
tokenizer = Tokenizer(num_words = num_words ,oov_token=oov_token)

訓練 tokenizer#

tokenizer.fit_on_texts(trainning_sentences)

tokenizer.word_index#

word_index = tokenizer.word_index

2、由詞語組成的句子轉換成由索引組成的序列
注意此處,上述代碼的 num_words 參數的效果就顯現出來了。每個序列中,不一定每個詞語都有索引對應,只有那些頻率最高的(Top num_words)的詞才會有索引對應,而低頻詞此時就直接被忽略。所以,理論上 num_words<=word_index。

sequences = token.texts_to_sequences(sentences)

3、對齊序列
雖然通過 tokenizer,讓句子變成了數字序列,但由於每個句子的詞語數不同,所以句子長短不一,從而導致序列為非整齊的序列。此時還需要做對齊序列處理。
對齊序列的方法是按照最長句子的長度(或指定的長度)對齊,空缺地方補 0,補 0 的方式可分為前補 0 或後補 0。本文採用 16 位作為句子的最長長度,不足 16 位的句子採用前補 0 的方式,來對齊序列。

paddedSequecnes = pad_sequences(sequences,
padding=padding,
maxlen=MAX_SEQUENCE_LENGTH,
truncating=trunc_type)

3. 模型構建及訓練#

3.1 模型構建#

1,Embedding 層 + Flatten 層
目前為止,我們得到了一个整數序列,這個序列是一個 2D 張量,其 shape 為(batch_size,sequence_length)(其中 batch_size 如果沒有特別指定的話,就是 None)。但是這種做法並沒有抓住詞與詞的意思之間的聯繫。

常見的做法是把整數用 One-hot 轉成高維稀疏向量再轉成低維密集向量,因此一個詞可能用 100D, 300D 或 500D 的向量來表示,而詞與詞之間的相似度就用向量之間內積表示。這個過程就叫做 word2vec,即把單詞轉成換向量的過程,就是用 Embedding () 來實現。

首先我們要把序列中的每個詞進行 One-hot 編碼轉換,讓其成為一個只有 0-1 的稀疏矩陣。這麼做的目的是什么呢?答案:計算簡單。稀疏矩陣做矩陣計算的時候,只需要把 1 對應位置的數相乘求和就行。而之前由 0-9 數字組成的 1D 列表顯然計算是慢了。

接下來,讓稀疏矩陣 (m,n) 乘以一個詞向量矩陣 (n, output_dim),形成一個新的詞向量矩陣(m, output_dim)。

最後,把所有輸入的序列中的整數,替換為相對應的詞向量,從而形成新的張量,這個張量擴展為 3D 張量,其 shape 為(batch_size,sequence_length,output_dim),其中的 output_dim 指的是詞向量的維度。

對於自然語言處理來說,Embedding 層是必須的,且必須作為第一層。詳細的 Embedding 原理參考如下:

02e73a94d4cd2815079ccb22ab798af0

Embedding(num_words,
EMBEDDING_DIM,
input_length=MAX_SEQUENCE_LENGTH,
trainable=True),
Flatten(),

2,LSTM 層
LSTM 層可以布一層,也可以布多層,唯一要注意的如果是布多層的時候,第一層的參數 return_sequences 要設定為 True。

Bidirectional(LSTM(64,return_sequences=True)),
Bidirectional(LSTM(32)),

3,Conv1D 層 + GlobalMaxPooling1D 層
卷積層 + 池化層。

Conv1D(128, 5, activation='relu'),
GlobalMaxPooling1D(),

3.2 預訓練詞向量的引入#

還是回到 Embedding 層。可以看出,之前的代碼在使用 Keras 的中 Embedding 層時候,並沒有引入預訓練詞向量,其詞向量先是隨機初始化,然後,在訓練數據的過程中自行訓練完成。

現在我們嘗試一下,引入預訓練詞向量 GloVe。
首先,構造 embedding_matrix,其中該 matrix 的維度應與 GloVe 的維度保持一致。
然後,將預訓練詞向量的值映射到 embedding_matrix 中。
最後,在 Embedding 層中指定引入 embedding_matrix。指定 embeddings_initializer=[embeddings_matrix] ,並且 trainable=False(表示在訓練的過程中不更新詞向量)。

Embedding(num_words,
EMBEDDING_DIM,
embeddings_initializer=Constant(embedding_matrix),
input_length=MAX_SEQUENCE_LENGTH,
trainable=False),

3.3 模型訓練#

我們使用不同的 layer 設計,結合對預訓練詞向量引入與否,進行編譯與訓練。

4. 模型預測#

根據 Layer 設計的不同及是否引入預訓練詞向量,模型預測結果參考如下:

29705b1f81f94f5d81f4b59b15063b29

以 case6 為例,acc 和 loss 的圖示參考如下:

84d4014fa62f4765627bdfd318ee5217

ca34c73ba4a2a3f7dbd689b0c96f94f3

由上可知,
1)在目前的參數設置前提下,CNN+Polling 的設計和 LSTM 不相伯仲還要好。。。不過這說明不了什麼問題,關鍵還是要看參數的設計。但 LSTM 確實很消耗資源。
2)預訓練詞向量的引入可以減少訓練的時間,但並不一定能保證模型更優。

5. 中文情感分析應用#

目前為止,我們都是在英文的基礎上進行研究。那么,中文該如何做呢?
其實中文和英文最大的差別在於英文的詞語是自然切分的,而中文是連在一起的。所以,對於中文而言,首先要做的是在 tokenizer 之前,做中文分詞。目前常用的分詞方法,比如說 jieba 分詞,便能很好地解決這一問題。

其次,要解決的就是預訓練詞向量。從上述的結果可以看出,使用預訓練的詞向量作為特徵是非常有效的。所以,要儘可能引入預訓練詞向量。一般情況下預訓練詞向量有兩種獲取方式:一是別人訓練好的百科數據。優勢:包含詞語多,符合日常用語的語義;劣勢:專有名詞不足,占用空間大;二是自己訓練。優勢:專有名詞,針對具體任務語義更準確; 劣勢:泛化性差。

語義識別目前比較好的 Python 庫有 gensim,其中也包含了很多詞向量導入和訓練的方法。

解決分詞和詞向量庫的問題後,剩下的思路基本上一樣了。


本文用到的文件的百度網盤下載地址如下:
鏈接:https://pan.baidu.com/s/1jH\_4Vzj1kh4af6domUCObQ
提取碼:cr0t

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。