讓你聽見你想要聽見的,讓你看見你想要看見的。Joi能滿足你的一切幻想。 --《銀翼殺手2049》
在這部電影中,用程式設定的虛擬人物,時時刻刻的與主角進行情感溝通,甚至產生了親密的感情,或許這個在未來真的能夠實現。現在轉到現實中來,近兩年AI的熱度暴漲,其中自然語言處理也是研究的重點,現在顯然已經有非常顯著的效果。
情感在人類資訊溝通的意義
情感對於人類的社交、表達、記憶、決策和感知上都有很重要的作用,有研究顯示人類交流中的80%資訊都是帶有情感資訊的。情感狀態不同會影響人的思維和行動,特別是積極或消極所帶來的影響有很大的不同。由於情感在人類溝通的重大意義,所以在設計人與計算機交流的時候也是必不可少的部分。通過和人的聊天,AI可用情感分析對實體進行高效率的自動化標註,學習到使用者的性格變得更加智慧。像微軟的小冰,聊的越多越“懂”自己。
一般情況下,我們探討的情感分析多指文字中的情感分析,也有更多維的情感分析,比如文字+圖片+標籤+語音+面部表情、肢體動作等等,就像前一段時間被授予合法公民身份的AI機器人索菲亞可以通過攝像聲音和周圍的環境來通過面部表情來傳達情感。人類能表達的情感的資訊量的大小和維度。比如面對面交流時,情感性的資訊往往是從語音語調、面部表情、肢體等多個維度表達出來的。然而到了人機互動中,情感寬頻的整個正規化會發生較大的變化,如通過人機對話系統交流時,少了肢體這個維度,人類的情感頻寬似乎瞬間驟降了。但實際上也增加了幾個新的輸出維度,如圖片、表情包、回覆時間的長短等。
情感分析之文字情感傾向分析
顯然,面對多維更加智慧的場景,目前的技術實現起來難度很大,但是針對大多數的文字語意的表達還是有比較成熟的解決方案,現在就來說一下深度學習在自然語言處理的表現吧。
深度學習在情感分析中的應用已經較為普遍了,如利用 LSTM 結合句法分析樹、基於卷積神經網路和支援向量機等。一般情況下,對於各種方法的綜合創新應用,能達到取長補短的效果,進而能夠提高情感分析的準確率,另外還能從無標註的文字里學習到其中的隱藏特徵,以實現端到端的分類。
關於更多的深度學習相比於詞典以及傳統的機器學習的方式的優勢,大家可以自行查閱相關文章,這篇文章只將用深度學習來簡單的訓練一個樣本,並預測任意一個句子的情感傾向。
情感分析工具keras,語言包括中文和英文
開始之前,你需要對深度學習原理有比較深刻的瞭解,lstm的原理,調參優化細節,keras基本知識的掌握。專案github地址戳這裡。
1. 準備語料
本次收集的語料不是太多。中文大概2w多條(淘寶評論),英文1w多條(電影評論),以後有時間會繼續補充語料。其中有5%作為驗證集,10%為測試集合。檔案已經切分好,但是因為需要訓練所有資料集,所以這個提前切分到不同資料夾並沒有什麼作用(後面講到會重新合併再切分)。
2.選擇語言版本,分別設定訓練集、測試集、驗證集和維度
因為後面的程式要訓練中文或英文,所以在這裡提前選擇語言版本和不同的語言版本訓練相關的引數。
# 選擇語言中文還是英文
languageType = ''
while (languageType != 'c' and languageType != 'e'):
languageType = input("Please enter a train type(chinese enter lower: c , english enter lower: e): ")
max_length = '' #一句話最大長度
load_path = '' #檔案載入路徑
language = '' #語言型別
tr_num = 17000 #訓練集
va_num = 2000 #訓練集
if languageType == 'c':
max_length = 100
load_path = 'data/chinese'
language = 'chinese'
tr_num = 17000
va_num = 2000
elif languageType == 'e':
max_length = 40
load_path = 'data/english'
language = 'english'
tr_num = 8000
va_num = 600
複製程式碼
3.載入資料集
這裡把中文和英文放在不同的資料夾下,利用 pandas
中的read_csv()
讀取資料集合併到一起,如果這裡本來就是一個整的資料集。則直接讀取就好。
# 獲取csv檔案:內容放到陣列裡面 分別是訓練集、驗證集、測試集,最後合併到一起
def sst_binary(data_dir='data/chinese'):
tr_data = pd.read_csv(os.path.join(data_dir, 'train_binary_sent.csv'))
va_data = pd.read_csv(os.path.join(data_dir, 'valid_binary_sent.csv'))
te_data = pd.read_csv(os.path.join(data_dir, 'test_binary_sent.csv'))
all_data = tr_data.append(va_data).append(te_data)
return all_data
複製程式碼
4.資料預處理
這一步是針對中文和英文的特點來處理掉對分析無用的詞提升精度。比如停用詞、標點符號、特殊字元、轉義字元等等。因為語料比較少,這個程式還沒有針對這一塊做處理。
5.將詞語轉化為向量
這裡是最核心的地方,深度學習在訓練資料的時候要求輸入的資料是一個向量,這樣才能進行矩陣運算,也是多層感知器的輸入。所以如果直接將一組句子是無法識別的。所以最重要的一步就是將詞語轉化為詞向量,可是如何才能得到向量呢?
這裡用到的是詞嵌入的方法,大概步驟是:
- 中文最小統計粒度是詞,所以要先切詞(
jieba
)將一句話按照詞語切分開來而非字。 - 將所有詞放在一起,統計每個詞出現的次數按照重大到小的排序,然後加上索引。
- 將句子中的詞語全部替換成相應的索引,這樣一個句子中的每個詞語就用一個數字去表示了。
- 呼叫keras model第一層
Embedding()
,該層會利用詞嵌入將句子數字陣列轉化為詞向量。
需要注意的是,jieba分詞雖然是分中文的,但是也可以處理英文(英文是按照空格切分的),這樣可以得到比較統一的陣列shape。
#定義模型
class Model(object):
def __init__(self, sentence_max_length=100):
sentence_max_length = sentence_max_length #截斷詞數 cut texts after this number of words (among top max_features most common words)
sentence_drop_length = 5 #出現次數少於該值的詞扔掉。這是最簡單的降維方法
#將每個句子裡的詞轉化成詞頻索引值
def transform(data):
#如果是中文呼叫結巴分詞
xs = data['sentence'].apply(lambda s: list(jieba.cut(s)))
#將所有詞放到一個陣列中
word_all = []
for i in xs:
word_all.extend(i)
#統計詞頻並排序建索引
global word_frequency, word_set
word_frequency = pd.Series(word_all).value_counts() #統計詞頻,從大到小排序
word_frequency = word_frequency[word_frequency >=
sentence_drop_length] #出現次數小於5的丟棄
word_frequency[:] = list(range(
1,
len(word_frequency) + 1)) #將詞頻排序的結果加索引
word_frequency[''] = 0 #新增空字串用來補全,之前丟棄的後面的找不到的會用0代替
word_set = set(
word_frequency.index) #經過處理之後的所有詞的陣列集合,並且去掉可能存在的重複元素
#將詞語替換成按照所有訓練集詞頻排序後的索引
xt = xs.apply(lambda s: word2num(s, sentence_max_length))
xt = np.array(list(xt))
yt = np.array(list(data['label'])).reshape(
(-1, 1)) #此處用來調整標籤形狀n行1列 (-1是模糊控制即有不定多少行,1是1列)
#當前訓練集合詞的索引長度
wi = len(word_frequency)
return xt, yt, wi
self.transform = transform
複製程式碼
6.keras 訓練資料集
這一部分就交給keras處理了,具體用法可以參見keras中文文件,可以自定義一些引數,比如訓練輪數、啟用函式、加入驗證集等等。當然核心的還是lstm了,相對於RNN,在訓練長文字有更好的效果。訓練完了之後可以選擇儲存模型。方便下次直接呼叫。
#將詞轉化為數字向量 即一個句子裡的每個詞都有用上面生成的索引值代替
def word2num(s, sentence_max_length):
s = [i for i in s if i in word_set]
s = s[:sentence_max_length] + [''] * max(0, sentence_max_length - len(s))
return list(word_frequency[s])
# krea 訓練資料集
def model_train(x, y, wi, language, sentence_max_length=100, tr_num=17000, va_num=2000):
global model
model = Sequential()
model.add(Embedding(wi, 256, input_length=sentence_max_length))
model.add(LSTM(128))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(
loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(
x[:tr_num],
y[:tr_num],
batch_size=128,
nb_epoch=30,
validation_data=(x[tr_num:tr_num + va_num], y[tr_num:tr_num + va_num]))
score = model.evaluate(
x[tr_num + va_num:], y[tr_num + va_num:], batch_size=128)
model.save('model_' + language + '.h5')
return score[1]
#載入已經訓練好的模型
def model_load(language):
global model
model = load_model('model_' + language + '.h5')
複製程式碼
7.預測單個句子
預測單個句子依然需要將這個句子分詞,然後將詞轉化為數字,所以還是用到訓練模型時用到的處理方式。
#單個句子的預測函式
def model_predict(s, sentence_max_length=100):
s = np.array(word2num(list(jieba.cut(s)), sentence_max_length))
s = s.reshape((1, s.shape[0]))
return model.predict_classes(s, verbose=0)[0][0]
複製程式碼
好了,大功告成,我們已經可以直接測試訓練的結果了。
總結
現在,我們已經能夠利用keras裡的lstm對文字進行預測情感傾向了。但是這裡完美還是有很大距離,需要不斷的調參來提高準確度。能夠提升的地方比如加大訓練集數量、對資料預處理、調整訓練次數等等。當然還有更酷的方案比如雙向lstm這樣的訓練方式,綜合對比,或許都是改良的途徑。
目前一套模型同時解決中文和英文的例子還是非常少,畢竟語言的差異還是非常大,接下來會繼續對其中進行一些探索,這個也是我對自然語言處理中的情感分析一點小小的嘗試。
Github地址:github.com/Elliottssu/…
轉載著名出處