摘要:本文通過Keras實現了一個RNN文本分類學習的案例,並詳細介紹了循環神經網絡原理知識及與機器學習對比。
一.RNN文本分類
1.RNN
循環神經網絡英文是Recurrent Neural Networks,簡稱RNN。RNN的本質概念是利用時序信息,在傳統神經網絡中,假設所有的輸入(以及輸出)都各自獨立。但是,對於很多任務而言,這非常局限。舉個例子,假如你想根據一句沒說完的話,預測下一個單詞,最好的辦法就是聯繫上下文的信息。而RNN(循環神經網絡)之所以是「循環」,是因為它們對序列的每個元素執行相同的任務,而每次的結果都獨立於之前的計算。
假設有一組數據data0、data1、data2、data3,使用同一個神經網絡預測它們,得到對應的結果。如果數據之間是有關係的,比如做菜下料的前後步驟,英文單詞的順序,如何讓數據之間的關聯也被神經網絡學習呢?這就要用到——RNN。
比如存在ABCD數字,需要預測下一個數字E,會根據前面ABCD順序進行預測,這就稱為記憶。預測之前,需要回顧以前的記憶有哪些,再加上這一步新的記憶點,最終輸出output,循環神經網絡(RNN)就利用了這樣的原理。
首先,讓我們想想人類是怎麼分析事物之間的關聯或順序的。人類通常記住之前發生的事情,從而幫助我們後續的行為判斷,那麼是否能讓計算機也記住之前發生的事情呢?
在分析data0時,我們把分析結果存入記憶Memory中,然後當分析data1時,神經網絡(NN)會產生新的記憶,但此時新的記憶和老的記憶沒有關聯,如上圖所示。在RNN中,我們會簡單的把老記憶調用過來分析新記憶,如果繼續分析更多的數據時,NN就會把之前的記憶全部累積起來。
下面是一個典型的RNN結果模型,按照時間點t-1、t、t+1,每個時刻有不同的x,每次計算會考慮上一步的state和這一步的x(t),再輸出y值。在該數學形式中,每次RNN運行完之後都會產生s(t),當RNN要分析x(t+1)時,此刻的y(t+1)是由s(t)和s(t+1)共同創造的,s(t)可看作上一步的記憶。多個神經網絡NN的累積就轉換成了循環神經網絡,其簡化圖如下圖的左邊所示。例如,如果序列中的句子有5個單詞,那麼,橫向展開網絡後將有五層神經網絡,一層對應一個單詞。
總之,只要你的數據是有順序的,就可以使用RNN,比如人類說話的順序,電話號碼的順序,圖像像素排列的順序,ABC字母的順序等。RNN常用於自然語言處理、機器翻譯、語音識別、圖像識別等領域。
2.文本分類
文本分類旨在對文本集按照一定的分類體系或標準進行自動分類標記,屬於一種基於分類體系的自動分類。文本分類最早可以追溯到上世紀50年代,那時主要通過專家定義規則來進行文本分類;80年代出現了利用知識工程建立的專家系統;90年代開始藉助於機器學習方法,通過人工特徵工程和淺層分類模型來進行文本分類。現在多採用詞向量以及深度神經網絡來進行文本分類。
牛亞峰老師將傳統的文本分類流程歸納如下圖所示。在傳統的文本分類中,基本上大部分機器學習方法都在文本分類領域有所應用。主要包括:
- Naive Bayes
- KNN
- SVM
- 集合類方法
- 最大熵
- 神經網絡
利用Keras框架進行文本分類的基本流程如下:
- 步驟 1:文本的預處理,分詞->去除停用詞->統計選擇top n的詞做為特徵詞
- 步驟 2:為每個特徵詞生成ID
- 步驟 3:將文本轉化成ID序列,並將左側補齊
- 步驟 4:訓練集shuffle
- 步驟 5:Embedding Layer 將詞轉化為詞向量
- 步驟 6:添加模型,構建神經網絡結構
- 步驟 7:訓練模型
- 步驟 8:得到準確率、召回率、F1值
注意,如果使用TFIDF而非詞向量進行文檔表示,則直接分詞去停後生成TFIDF矩陣後輸入模型。本文將採用詞向量、TFIDF兩種方式進行實驗。
深度學習文本分類方法包括:
- 卷積神經網絡(TextCNN)
- 循環神經網絡(TextRNN)
- TextRNN+Attention
- TextRCNN(TextRNN+CNN)
推薦牛亞峰老師的文章:基於 word2vec 和 CNN 的文本分類 :綜述 & 實踐
二.基於傳統機器學習貝葉斯算法的文本分類
1.MultinomialNB+TFIDF文本分類
數據集採用基基偉老師的自定義文本,共21行數據,包括2類(小米手機、小米粥)。其基本流程是:
- 獲取數據集data和target
- 調用Jieba庫實現中文分詞
- 計算tf-idf值,將詞頻矩陣轉換為TF-IDF向量矩陣
- 調用機器學習算法進行訓練和預測
- 實驗評估及可視化分析
完整代碼如下:
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 28 22:10:20 2020
@author: Eastmount CSDN
"""
from jieba import lcut
#--------------------------------載入數據及預處理-------------------------------
data = [
[0, '小米粥是以小米作為主要食材熬製而成的粥,口味清淡,清香味,具有簡單易制,健胃消食的特點'],
[0, '煮粥時一定要先燒開水然後放入洗淨後的小米'],
[0, '蛋白質及胺基酸、脂肪、維生素、礦物質'],
[0, '小米是傳統健康食品,可單獨燜飯和熬粥'],
[0, '蘋果,是水果中的一種'],
[0, '粥的營養價值很高,富含礦物質和維生素,含鈣量豐富,有助於代謝掉體內多餘鹽分'],
[0, '雞蛋有很高的營養價值,是優質蛋白質、B族維生素的良好來源,還能提供脂肪、維生素和礦物質'],
[0, '這家超市的蘋果都非常新鮮'],
[0, '在北方小米是主要食物之一,很多地區有晚餐吃小米粥的習俗'],
[0, '小米營養價值高,營養全面均衡 ,主要含有碳水化合物'],
[0, '蛋白質及胺基酸、脂肪、維生素、鹽分'],
[1, '小米、三星、華為,作為安卓三大手機旗艦'],
[1, '別再管小米華為了!魅族手機再曝光:這次真的完美了'],
[1, '蘋果手機或將重陷2016年困境,但這次它無法再大幅提價了'],
[1, '三星想要繼續壓制華為,僅憑A70還不夠'],
[1, '三星手機屏占比將再創新高,超華為及蘋果旗艦'],
[1, '華為P30、三星A70爆賣,斬獲蘇寧最佳手機營銷獎'],
[1, '雷軍,用一張圖告訴你:小米和三星的差距在哪裡'],
[1, '小米米聊APP官方Linux版上線,適配深度系統'],
[1, '三星剛剛更新了自家的可穿戴設備APP'],
[1, '華為、小米跨界並不可怕,可怕的打不破內心的「天花板」'],
]
#中文分析
X, Y = [' '.join(lcut(i[1])) for i in data], [i[0] for i in data]
print(X)
print(Y)
#['煮粥 時 一定 要 先燒 開水 然後 放入 洗淨 後 的 小米', ...]
#--------------------------------------計算詞頻------------------------------------
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
#將文本中的詞語轉換為詞頻矩陣
vectorizer = CountVectorizer()
#計算個詞語出現的次數
X_data = vectorizer.fit_transform(X)
print(X_data)
#獲取詞袋中所有文本關鍵詞
word = vectorizer.get_feature_names()
print('【查看單詞】')
for w in word:
print(w, end = " ")
else:
print("\n")
#詞頻矩陣
print(X_data.toarray())
#將詞頻矩陣X統計成TF-IDF值
transformer = TfidfTransformer()
tfidf = transformer.fit_transform(X_data)
#查看數據結構 tfidf[i][j]表示i類文本中的tf-idf權重
weight = tfidf.toarray()
print(weight)
#--------------------------------------數據分析------------------------------------
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(weight, Y)
print(len(X_train), len(X_test))
print(len(y_train), len(y_test))
print(X_train)
#調用MultinomialNB分類器
clf = MultinomialNB().fit(X_train, y_train)
pre = clf.predict(X_test)
print("預測結果:", pre)
print("真實結果:", y_test)
print(classification_report(y_test, pre))
#--------------------------------------可視化分析------------------------------------
#降維繪製圖形
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
pca = PCA(n_components=2)
newData = pca.fit_transform(weight)
print(newData)
L1 = [n[0] for n in newData]
L2 = [n[1] for n in newData]
plt.scatter(L1, L2, c=Y, s=200)
plt.show()
輸出結果如下所示:
- 6個預測數據的accuracy ===> 0.67
繪製圖形如下圖所示:
2.GaussianNB+Word2Vec文本分類
該方法與前面不同之處是採用Word2Vec進行詞向量計算,將每行數據集分詞,並計算每個特徵詞的詞向量,接著轉換為詞向量矩陣,比如15行數據,每行數據40個特徵詞,每個特徵詞用20維度的詞向量表示,即(15, 40, 20)。同時,由於詞向量存在負數,所以需要使用GaussianNB算法替代MultinomialNB算法。
Word2Vec詳見作者前文:[Python人工智慧] 九.gensim詞向量Word2Vec安裝及《慶餘年》中文短文本相似度計算
- sentences:傳入的數據集序列(list of lists of tokens),默認值為None
- size:詞向量維數,默認值為100
- window:同句中當前詞和預測詞的最大距離,默認值為5
- min_count:最低詞頻過濾,默認值為5
- workers:線程數,默認值為3
- sg:模型參數,其值為0表示CBOW,值為1表示skip-gram,默認值為0
- hs:模型參數,其值為0表示負例採樣,值為1表示層次softmax,默認值為0
- negative:負例樣本數,默認值為5
- ns_exponent:用於形成負例樣本的指數,默認值為0.75
- cbow_mean:上下文詞向量參數,其值為0表示上下文詞向量求和值,值為1表示上下文詞向量平均值,默認值為1
- alpha:初始學習率,默認值為0.025
- min_alpha:最小學習率,默認值為0.0001
完整代碼如下:
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 28 22:10:20 2020
@author: Eastmount CSDN
"""
from jieba import lcut
from numpy import zeros
from gensim.models import Word2Vec
from sklearn.model_selection import train_test_split
from tensorflow.python.keras.preprocessing.sequence import pad_sequences
max_features = 20 #詞向量維度
maxlen = 40 #序列最大長度
#--------------------------------載入數據及預處理-------------------------------
data = [
[0, '小米粥是以小米作為主要食材熬製而成的粥,口味清淡,清香味,具有簡單易制,健胃消食的特點'],
[0, '煮粥時一定要先燒開水然後放入洗淨後的小米'],
[0, '蛋白質及胺基酸、脂肪、維生素、礦物質'],
[0, '小米是傳統健康食品,可單獨燜飯和熬粥'],
[0, '蘋果,是水果中的一種'],
[0, '粥的營養價值很高,富含礦物質和維生素,含鈣量豐富,有助於代謝掉體內多餘鹽分'],
[0, '雞蛋有很高的營養價值,是優質蛋白質、B族維生素的良好來源,還能提供脂肪、維生素和礦物質'],
[0, '這家超市的蘋果都非常新鮮'],
[0, '在北方小米是主要食物之一,很多地區有晚餐吃小米粥的習俗'],
[0, '小米營養價值高,營養全面均衡 ,主要含有碳水化合物'],
[0, '蛋白質及胺基酸、脂肪、維生素、鹽分'],
[1, '小米、三星、華為,作為安卓三大手機旗艦'],
[1, '別再管小米華為了!魅族手機再曝光:這次真的完美了'],
[1, '蘋果手機或將重陷2016年困境,但這次它無法再大幅提價了'],
[1, '三星想要繼續壓制華為,僅憑A70還不夠'],
[1, '三星手機屏占比將再創新高,超華為及蘋果旗艦'],
[1, '華為P30、三星A70爆賣,斬獲蘇寧最佳手機營銷獎'],
[1, '雷軍,用一張圖告訴你:小米和三星的差距在哪裡'],
[1, '小米米聊APP官方Linux版上線,適配深度系統'],
[1, '三星剛剛更新了自家的可穿戴設備APP'],
[1, '華為、小米跨界並不可怕,可怕的打不破內心的「天花板」'],
]
#中文分析
X, Y = [lcut(i[1]) for i in data], [i[0] for i in data]
#劃分訓練集和預測集
X_train, X_test, y_train, y_test = train_test_split(X, Y)
#print(X_train)
print(len(X_train), len(X_test))
print(len(y_train), len(y_test))
"""['三星', '剛剛', '更新', '了', '自家', '的', '可', '穿戴', '設備', 'APP']"""
#--------------------------------Word2Vec詞向量-------------------------------
word2vec = Word2Vec(X_train, size=max_features, min_count=1) #最大特徵 最低過濾頻次1
print(word2vec)
#映射特徵詞
w2i = {w:i for i, w in enumerate(word2vec.wv.index2word)}
print("【顯示詞語】")
print(word2vec.wv.index2word)
print(w2i)
"""['小米', '三星', '是', '維生素', '蛋白質', '及', 'APP', '胺基酸',..."""
"""{',': 0, '的': 1, '小米': 2, '、': 3, '華為': 4, ....}"""
#詞向量計算
vectors = word2vec.wv.vectors
print("【詞向量矩陣】")
print(vectors.shape)
print(vectors)
#自定義函數-獲取詞向量
def w2v(w):
i = w2i.get(w)
return vectors[i] if i else zeros(max_features)
#自定義函數-序列預處理
def pad(ls_of_words):
a = [[w2v(i) for i in x] for x in ls_of_words]
a = pad_sequences(a, maxlen, dtype='float')
return a
#序列化處理 轉換為詞向量
X_train, X_test = pad(X_train), pad(X_test)
print(X_train.shape)
print(X_test.shape)
"""(15, 40, 20) 15個樣本 40個特徵 每個特徵用20詞向量表示"""
#拉直形狀 (15, 40, 20)=>(15, 40*20) (6, 40, 20)=>(6, 40*20)
X_train = X_train.reshape(len(y_train), maxlen*max_features)
X_test = X_test.reshape(len(y_test), maxlen*max_features)
print(X_train.shape)
print(X_test.shape)
#--------------------------------建模與訓練-------------------------------
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
#調用GaussianNB分類器
clf = GaussianNB().fit(X_train, y_train)
pre = clf.predict(X_test)
print("預測結果:", pre)
print("真實結果:", y_test)
print(classification_report(y_test, pre))
輸出結果如下所示:
- 6個預測數據的accuracy ===> 0.83
三.Keras實現RNN文本分類
1.IMDb數據集和序列預處理
(1) IMDB數據集Keras框架為我們提供了一些常用的內置數據集。比如,圖像識別領域的手寫識別MNIST數據集、文本分類領域的電影影評imdb數據集等等。這些資料庫可以用一條代碼就可以調用:
- (trainX, trainY), (testX, testY) = imdb.load_data(path=「imdb.npz」, num_words=max_features)
這些數據集是通過s3.amazonaws.com進行下載的,但有時該網站不能使用,需要下載數據至本地,再進行調用分析。Keras數據集百度雲連結:
- pan.baidu.com/s/1aZRp0uMk…,提取碼: 3a2u
作者將下載後的數據放在C:\Users\Administrator.keras\datasets文件夾下,如下圖所示。
該數據集是網際網路電影資料庫(Internet Movie Database,簡稱IMDb),它是一個關於電影演員、電影、電視節目、電視明星和電影製作的在線資料庫。
imdb.npz文件中數據和格式如下:
[list([1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, ...])
list([1, 194, 1153, 194, 8255, 78, 228, 5, 6, 1463, 4369, 5012, 134, 26, 4, 715, 8, 118, 1634, 14, 394, 20, 13, 119, 954, 189, 102, 5, 207, 110, 3103, 21, 14, 69, ...])
list([1, 14, 47, 8, 30, 31, 7, 4, 249, 108, 7, 4, 5974, 54, 61, 369, 13, 71, 149, 14, 22, 112, 4, 2401, 311, 12, 16, 3711, 33, 75, 43, 1829, 296, 4, 86, 320, 35, ...])
...
list([1, 11, 6, 230, 245, 6401, 9, 6, 1225, 446, 2, 45, 2174, 84, 8322, 4007, 21, 4, 912, 84, 14532, 325, 725, 134, 15271, 1715, 84, 5, 36, 28, 57, 1099, 21, 8, 140, ...])
list([1, 1446, 7079, 69, 72, 3305, 13, 610, 930, 8, 12, 582, 23, 5, 16, 484, 685, 54, 349, 11, 4120, 2959, 45, 58, 1466, 13, 197, 12, 16, 43, 23, 2, 5, 62, 30, 145, ...])
list([1, 17, 6, 194, 337, 7, 4, 204, 22, 45, 254, 8, 106, 14, 123, 4, 12815, 270, 14437, 5, 16923, 12255, 732, 2098, 101, 405, 39, 14, 1034, 4, 1310, 9, 115, 50, 305, ...])] train sequences
每個list是一個句子,句子中每個數字表示單詞的編號。那麼,怎麼獲取編號對應的單詞?此時需要使用imdb_word_index.json文件,其文件格式如下:
{"fawn": 34701, "tsukino": 52006,..., "paget": 18509, "expands": 20597}
共有88584個單詞,採用key-value格式存放,key代表單詞,value代表(單詞)編號。詞頻(單詞在語料中出現次數)越高編號越小,例如, 「the:1」出現次數最高,編號為1。
(2) 序列預處理在進行深度學習向量轉換過程中,通常需要使用pad_sequences()序列填充。其基本用法如下:
keras.preprocessing.sequence.pad_sequences(
sequences,
maxlen=None,
dtype='int32',
padding='pre',
truncating='pre',
value=0.
)
參數含義如下:
- sequences:浮點數或整數構成的兩層嵌套列表
- maxlen:None或整數,為序列的最大長度。大於此長度的序列將被截短,小於此長度的序列將在後部填0
- dtype:返回的numpy array的數據類型
- padding:pre或post,確定當需要補0時,在序列的起始還是結尾補0
- truncating:pre或post,確定當需要截斷序列時,從起始還是結尾截斷
- value:浮點數,此值將在填充時代替默認的填充值0
- 返回值是個2維張量,長度為maxlen
基本用法如下所示:
from keras.preprocessing.sequence import pad_sequences
print(pad_sequences([[1, 2, 3], [1]], maxlen=2))
"""[[2 3] [0 1]]"""
print(pad_sequences([[1, 2, 3], [1]], maxlen=3, value=9))
"""[[1 2 3] [9 9 1]]"""
print(pad_sequences([[2,3,4]], maxlen=10))
"""[[0 0 0 0 0 0 0 2 3 4]]"""
print(pad_sequences([[1,2,3,4,5],[6,7]], maxlen=10))
"""[[0 0 0 0 0 1 2 3 4 5] [0 0 0 0 0 0 0 0 6 7]]"""
print(pad_sequences([[1, 2, 3], [1]], maxlen=2, padding='post'))
"""結束位置補: [[2 3] [1 0]]"""
print(pad_sequences([[1, 2, 3], [1]], maxlen=4, truncating='post'))
"""起始位置補: [[0 1 2 3] [0 0 0 1]]"""
在自然語言中一般和分詞器一起使用。
>>> tokenizer.texts_to_sequences(["下 雨 我 加班"])
[[4, 5, 6, 7]]
>>> keras.preprocessing.sequence.pad_sequences(tokenizer.texts_to_sequences(["下 雨 我 加班"]), maxlen=20)
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7]],dtype=int32)
2.詞嵌入模型訓練
此時我們將通過詞嵌入模型進行訓練,具體流程包括:
- 導入IMDB數據集
- 數據集轉換為序列
- 創建Embedding詞嵌入模型
- 神經網絡訓練
完整代碼如下:
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 28 17:08:28 2020
@author: Eastmount CSDN
"""
from keras.datasets import imdb #Movie Database
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Flatten, Embedding
#-----------------------------------定義參數-----------------------------------
max_features = 20000 #按詞頻大小取樣本前20000個詞
input_dim = max_features #詞庫大小 必須>=max_features
maxlen = 80 #句子最大長度
batch_size = 128 #batch數量
output_dim = 40 #詞向量維度
epochs = 2 #訓練批次
#--------------------------------載入數據及預處理-------------------------------
#數據獲取
(trainX, trainY), (testX, testY) = imdb.load_data(path="imdb.npz", num_words=max_features)
print(trainX.shape, trainY.shape) #(25000,) (25000,)
print(testX.shape, testY.shape) #(25000,) (25000,)
#序列截斷或補齊為等長
trainX = sequence.pad_sequences(trainX, maxlen=maxlen)
testX = sequence.pad_sequences(testX, maxlen=maxlen)
print('trainX shape:', trainX.shape)
print('testX shape:', testX.shape)
#------------------------------------創建模型------------------------------------
model = Sequential()
#詞嵌入:詞庫大小、詞向量維度、固定序列長度
model.add(Embedding(input_dim, output_dim, input_length=maxlen))
#平坦化: maxlen*output_dim
model.add(Flatten())
#輸出層: 2分類
model.add(Dense(units=1, activation='sigmoid'))
#RMSprop優化器 二元交叉熵損失
model.compile('rmsprop', 'binary_crossentropy', ['acc'])
#訓練
model.fit(trainX, trainY, batch_size, epochs)
#模型可視化
model.summary()
輸出結果如下所示:
(25000,) (25000,)
(25000,) (25000,)
trainX shape: (25000, 80)
testX shape: (25000, 80)
Epoch 1/2
25000/25000 [==============================] - 2s 98us/step - loss: 0.6111 - acc: 0.6956
Epoch 2/2
25000/25000 [==============================] - 2s 69us/step - loss: 0.3578 - acc: 0.8549
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_2 (Embedding) (None, 80, 40) 800000
_________________________________________________________________
flatten_2 (Flatten) (None, 3200) 0
_________________________________________________________________
dense_2 (Dense) (None, 1) 3201
=================================================================
Total params: 803,201
Trainable params: 803,201
Non-trainable params: 0
_________________________________________________________________
顯示矩陣如下圖所示:
3.RNN文本分類
RNN對IMDB電影數據集進行文本分類的完整代碼如下所示:
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 28 17:08:28 2020
@author: Eastmount CSDN
"""
from keras.datasets import imdb #Movie Database
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Flatten, Embedding
from keras.layers import SimpleRNN
#-----------------------------------定義參數-----------------------------------
max_features = 20000 #按詞頻大小取樣本前20000個詞
input_dim = max_features #詞庫大小 必須>=max_features
maxlen = 40 #句子最大長度
batch_size = 128 #batch數量
output_dim = 40 #詞向量維度
epochs = 3 #訓練批次
units = 32 #RNN神經元數量
#--------------------------------載入數據及預處理-------------------------------
#數據獲取
(trainX, trainY), (testX, testY) = imdb.load_data(path="imdb.npz", num_words=max_features)
print(trainX.shape, trainY.shape) #(25000,) (25000,)
print(testX.shape, testY.shape) #(25000,) (25000,)
#序列截斷或補齊為等長
trainX = sequence.pad_sequences(trainX, maxlen=maxlen)
testX = sequence.pad_sequences(testX, maxlen=maxlen)
print('trainX shape:', trainX.shape)
print('testX shape:', testX.shape)
#-----------------------------------創建RNN模型-----------------------------------
model = Sequential()
#詞嵌入 詞庫大小、詞向量維度、固定序列長度
model.add(Embedding(input_dim, output_dim, input_length=maxlen))
#RNN Cell
model.add(SimpleRNN(units, return_sequences=True)) #返回序列全部結果
model.add(SimpleRNN(units, return_sequences=False)) #返回序列最尾結果
#輸出層 2分類
model.add(Dense(units=1, activation='sigmoid'))
#模型可視化
model.summary()
#-----------------------------------建模與訓練-----------------------------------
#激活神經網絡
model.compile(optimizer = 'rmsprop', #RMSprop優化器
loss = 'binary_crossentropy', #二元交叉熵損失
metrics = ['accuracy'] #計算誤差或準確率
)
#訓練
history = model.fit(trainX,
trainY,
batch_size=batch_size,
epochs=epochs,
verbose=2,
validation_split=.1 #取10%樣本作驗證
)
#-----------------------------------預測與可視化-----------------------------------
import matplotlib.pyplot as plt
accuracy = history.history['accuracy']
val_accuracy = history.history['val_accuracy']
plt.plot(range(epochs), accuracy)
plt.plot(range(epochs), val_accuracy)
plt.show()
輸出結果如下所示,三個Epoch訓練。
- 訓練數據的accuracy ===> 0.9075
- 評估數據的val_accuracy ===> 0.7844
Epoch可以用下圖進行形象的表示。
(25000,) (25000,)
(25000,) (25000,)
trainX shape: (25000, 40)
testX shape: (25000, 40)
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_2 (Embedding) (None, 40, 40) 800000
_________________________________________________________________
simple_rnn_3 (SimpleRNN) (None, 40, 32) 2336
_________________________________________________________________
simple_rnn_4 (SimpleRNN) (None, 32) 2080
_________________________________________________________________
dense_2 (Dense) (None, 1) 33
=================================================================
Total params: 804,449
Trainable params: 804,449
Non-trainable params: 0
_________________________________________________________________
Train on 22500 samples, validate on 2500 samples
Epoch 1/3
- 11s - loss: 0.5741 - accuracy: 0.6735 - val_loss: 0.4462 - val_accuracy: 0.7876
Epoch 2/3
- 14s - loss: 0.3572 - accuracy: 0.8430 - val_loss: 0.4928 - val_accuracy: 0.7616
Epoch 3/3
- 12s - loss: 0.2329 - accuracy: 0.9075 - val_loss: 0.5050 - val_accuracy: 0.7844
繪製的accuracy和val_accuracy曲線如下圖所示:
- loss: 0.2329 - accuracy: 0.9075 - val_loss: 0.5050 - val_accuracy: 0.7844
四.RNN實現中文數據集的文本分類
1.RNN+Word2Vector文本分類
第一步,導入文本數據集並轉換為詞向量。
data = [
[0, '小米粥是以小米作為主要食材熬製而成的粥,口味清淡,清香味,具有簡單易制,健胃消食的特點'],
[0, '煮粥時一定要先燒開水然後放入洗淨後的小米'],
[0, '蛋白質及胺基酸、脂肪、維生素、礦物質'],
[0, '小米是傳統健康食品,可單獨燜飯和熬粥'],
[0, '蘋果,是水果中的一種'],
[0, '粥的營養價值很高,富含礦物質和維生素,含鈣量豐富,有助於代謝掉體內多餘鹽分'],
[0, '雞蛋有很高的營養價值,是優質蛋白質、B族維生素的良好來源,還能提供脂肪、維生素和礦物質'],
[0, '這家超市的蘋果都非常新鮮'],
[0, '在北方小米是主要食物之一,很多地區有晚餐吃小米粥的習俗'],
[0, '小米營養價值高,營養全面均衡 ,主要含有碳水化合物'],
[0, '蛋白質及胺基酸、脂肪、維生素、鹽分'],
[1, '小米、三星、華為,作為安卓三大手機旗艦'],
[1, '別再管小米華為了!魅族手機再曝光:這次真的完美了'],
[1, '蘋果手機或將重陷2016年困境,但這次它無法再大幅提價了'],
[1, '三星想要繼續壓制華為,僅憑A70還不夠'],
[1, '三星手機屏占比將再創新高,超華為及蘋果旗艦'],
[1, '華為P30、三星A70爆賣,斬獲蘇寧最佳手機營銷獎'],
[1, '雷軍,用一張圖告訴你:小米和三星的差距在哪裡'],
[1, '小米米聊APP官方Linux版上線,適配深度系統'],
[1, '三星剛剛更新了自家的可穿戴設備APP'],
[1, '華為、小米跨界並不可怕,可怕的打不破內心的「天花板」'],
]
#中文分析
X, Y = [lcut(i[1]) for i in data], [i[0] for i in data]
#劃分訓練集和預測集
X_train, X_test, y_train, y_test = train_test_split(X, Y)
#print(X_train)
print(len(X_train), len(X_test))
print(len(y_train), len(y_test))
"""['三星', '剛剛', '更新', '了', '自家', '的', '可', '穿戴', '設備', 'APP']"""
#--------------------------------Word2Vec詞向量-------------------------------
word2vec = Word2Vec(X_train, size=max_features, min_count=1) #最大特徵 最低過濾頻次1
print(word2vec)
#映射特徵詞
w2i = {w:i for i, w in enumerate(word2vec.wv.index2word)}
print("【顯示詞語】")
print(word2vec.wv.index2word)
print(w2i)
"""['小米', '三星', '是', '維生素', '蛋白質', '及', 'APP', '胺基酸',..."""
"""{',': 0, '的': 1, '小米': 2, '、': 3, '華為': 4, ....}"""
#詞向量計算
vectors = word2vec.wv.vectors
print("【詞向量矩陣】")
print(vectors.shape)
print(vectors)
#自定義函數-獲取詞向量
def w2v(w):
i = w2i.get(w)
return vectors[i] if i else zeros(max_features)
#自定義函數-序列預處理
def pad(ls_of_words):
a = [[w2v(i) for i in x] for x in ls_of_words]
a = pad_sequences(a, maxlen, dtype='float')
return a
#序列化處理 轉換為詞向量
X_train, X_test = pad(X_train), pad(X_test)
此時輸出結果如下所示:
15 6
15 6
Word2Vec(vocab=120, size=20, alpha=0.025)
【顯示詞語】
[',', '的', '、', '小米', '三星', '是', '維生素', '蛋白質', '及',
'脂肪', '華為', '蘋果', '可', 'APP', '胺基酸', '在', '手機', '旗艦',
'礦物質', '主要', '有', '小米粥', '作為', '剛剛', '更新', '設備', ...]
{',': 0, '的': 1, '、': 2, '小米': 3, '三星': 4, '是': 5,
'維生素': 6, '蛋白質': 7, '及': 8, '脂肪': 9, '和': 10,
'華為': 11, '蘋果': 12, '可': 13, 'APP': 14, '胺基酸': 15, ...}
【詞向量矩陣】
(120, 20)
[[ 0.00219526 0.00936278 0.00390177 ... -0.00422463 0.01543128
0.02481441]
[ 0.02346811 -0.01520025 -0.00563479 ... -0.01656673 -0.02222313
0.00438196]
[-0.02253242 -0.01633896 -0.02209039 ... 0.01301584 -0.01016752
0.01147605]
...
[ 0.01793107 0.01912305 -0.01780855 ... -0.00109831 0.02460653
-0.00023512]
[-0.00599797 0.02155897 -0.01874896 ... 0.00149929 0.00200266
0.00988515]
[ 0.0050361 -0.00848463 -0.0235001 ... 0.01531716 -0.02348576
0.01051775]]
第二步,建立RNN神經網絡結構,使用Bi-GRU模型,並進行訓練與預測。
#--------------------------------建模與訓練-------------------------------
model = Sequential()
#雙向RNN
model.add(Bidirectional(GRU(units), input_shape=(maxlen, max_features)))
#輸出層 2分類
model.add(Dense(units=1, activation='sigmoid'))
#模型可視化
model.summary()
#激活神經網絡
model.compile(optimizer = 'rmsprop', #RMSprop優化器
loss = 'binary_crossentropy', #二元交叉熵損失
metrics = ['acc'] #計算誤差或準確率
)
#訓練
history = model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs,
verbose=verbose, validation_data=(X_test, y_test))
#----------------------------------預測與可視化------------------------------
#預測
score = model.evaluate(X_test, y_test, batch_size=batch_size)
print('test loss:', score[0])
print('test accuracy:', score[1])
#可視化
acc = history.history['acc']
val_acc = history.history['val_acc']
# 設置類標
plt.xlabel("Iterations")
plt.ylabel("Accuracy")
#繪圖
plt.plot(range(epochs), acc, "bo-", linewidth=2, markersize=12, label="accuracy")
plt.plot(range(epochs), val_acc, "gs-", linewidth=2, markersize=12, label="val_accuracy")
plt.legend(loc="upper left")
plt.title("RNN-Word2vec")
plt.show()
輸出結果如下圖所示,發現accuracy和val_accuracy值非常不理想。怎麼解決呢?
神經網絡模型和Epoch訓練結果如下圖所示:
- test loss: 0.7160684466362
- test accuracy: 0.33333334
這裡補充一個知識點——EarlyStopping。EarlyStopping是Callbacks的一種,callbacks用於指定在每個epoch開始和結束的時候進行哪種特定操作。Callbacks中有一些設置好的接口,可以直接使用,如acc、val_acc、loss和val_loss等。EarlyStopping則是用於提前停止訓練的callbacks,可以達到當訓練集上的loss不在減小(即減小的程度小於某個閾值)的時候停止繼續訓練。上面程序中,當我們loss不在減小時就可以調用Callbacks停止訓練。推薦文章:[深度學習] keras的EarlyStopping使用與技巧 - zwqjoy
最後給出該部分的完整代碼:
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 28 22:10:20 2020
@author: Eastmount CSDN
"""
from jieba import lcut
from numpy import zeros
import matplotlib.pyplot as plt
from gensim.models import Word2Vec
from sklearn.model_selection import train_test_split
from tensorflow.python.keras.preprocessing.sequence import pad_sequences
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Dense, GRU, Bidirectional
from tensorflow.python.keras.callbacks import EarlyStopping
#-----------------------------------定義參數----------------------------------
max_features = 20 #詞向量維度
units = 30 #RNN神經元數量
maxlen = 40 #序列最大長度
epochs = 9 #訓練最大輪數
batch_size = 12 #每批數據量大小
verbose = 1 #訓練過程展示
patience = 1 #沒有進步的訓練輪數
callbacks = [EarlyStopping('val_acc', patience=patience)]
#--------------------------------載入數據及預處理-------------------------------
data = [
[0, '小米粥是以小米作為主要食材熬製而成的粥,口味清淡,清香味,具有簡單易制,健胃消食的特點'],
[0, '煮粥時一定要先燒開水然後放入洗淨後的小米'],
[0, '蛋白質及胺基酸、脂肪、維生素、礦物質'],
[0, '小米是傳統健康食品,可單獨燜飯和熬粥'],
[0, '蘋果,是水果中的一種'],
[0, '粥的營養價值很高,富含礦物質和維生素,含鈣量豐富,有助於代謝掉體內多餘鹽分'],
[0, '雞蛋有很高的營養價值,是優質蛋白質、B族維生素的良好來源,還能提供脂肪、維生素和礦物質'],
[0, '這家超市的蘋果都非常新鮮'],
[0, '在北方小米是主要食物之一,很多地區有晚餐吃小米粥的習俗'],
[0, '小米營養價值高,營養全面均衡 ,主要含有碳水化合物'],
[0, '蛋白質及胺基酸、脂肪、維生素、鹽分'],
[1, '小米、三星、華為,作為安卓三大手機旗艦'],
[1, '別再管小米華為了!魅族手機再曝光:這次真的完美了'],
[1, '蘋果手機或將重陷2016年困境,但這次它無法再大幅提價了'],
[1, '三星想要繼續壓制華為,僅憑A70還不夠'],
[1, '三星手機屏占比將再創新高,超華為及蘋果旗艦'],
[1, '華為P30、三星A70爆賣,斬獲蘇寧最佳手機營銷獎'],
[1, '雷軍,用一張圖告訴你:小米和三星的差距在哪裡'],
[1, '小米米聊APP官方Linux版上線,適配深度系統'],
[1, '三星剛剛更新了自家的可穿戴設備APP'],
[1, '華為、小米跨界並不可怕,可怕的打不破內心的「天花板」'],
]
#中文分析
X, Y = [lcut(i[1]) for i in data], [i[0] for i in data]
#劃分訓練集和預測集
X_train, X_test, y_train, y_test = train_test_split(X, Y)
#print(X_train)
print(len(X_train), len(X_test))
print(len(y_train), len(y_test))
"""['三星', '剛剛', '更新', '了', '自家', '的', '可', '穿戴', '設備', 'APP']"""
#--------------------------------Word2Vec詞向量-------------------------------
word2vec = Word2Vec(X_train, size=max_features, min_count=1) #最大特徵 最低過濾頻次1
print(word2vec)
#映射特徵詞
w2i = {w:i for i, w in enumerate(word2vec.wv.index2word)}
print("【顯示詞語】")
print(word2vec.wv.index2word)
print(w2i)
"""['小米', '三星', '是', '維生素', '蛋白質', '及', 'APP', '胺基酸',..."""
"""{',': 0, '的': 1, '小米': 2, '、': 3, '華為': 4, ....}"""
#詞向量計算
vectors = word2vec.wv.vectors
print("【詞向量矩陣】")
print(vectors.shape)
print(vectors)
#自定義函數-獲取詞向量
def w2v(w):
i = w2i.get(w)
return vectors[i] if i else zeros(max_features)
#自定義函數-序列預處理
def pad(ls_of_words):
a = [[w2v(i) for i in x] for x in ls_of_words]
a = pad_sequences(a, maxlen, dtype='float')
return a
#序列化處理 轉換為詞向量
X_train, X_test = pad(X_train), pad(X_test)
#--------------------------------建模與訓練-------------------------------
model = Sequential()
#雙向RNN
model.add(Bidirectional(GRU(units), input_shape=(maxlen, max_features)))
#輸出層 2分類
model.add(Dense(units=1, activation='sigmoid'))
#模型可視化
model.summary()
#激活神經網絡
model.compile(optimizer = 'rmsprop', #RMSprop優化器
loss = 'binary_crossentropy', #二元交叉熵損失
metrics = ['acc'] #計算誤差或準確率
)
#訓練
history = model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs,
verbose=verbose, validation_data=(X_test, y_test))
#----------------------------------預測與可視化------------------------------
#預測
score = model.evaluate(X_test, y_test, batch_size=batch_size)
print('test loss:', score[0])
print('test accuracy:', score[1])
#可視化
acc = history.history['acc']
val_acc = history.history['val_acc']
# 設置類標
plt.xlabel("Iterations")
plt.ylabel("Accuracy")
#繪圖
plt.plot(range(epochs), acc, "bo-", linewidth=2, markersize=12, label="accuracy")
plt.plot(range(epochs), val_acc, "gs-", linewidth=2, markersize=12, label="val_accuracy")
plt.legend(loc="upper left")
plt.title("RNN-Word2vec")
plt.show()
2.LSTM+Word2Vec文本分類
接著我們使用LSTM和Word2Vec進行文本分類。整個神經網絡的結構很簡單,第一層是嵌入層,將文本中的單詞轉化為向量;之後經過一層LSTM層,使用LSTM中最後一個時刻的隱藏狀態;再接一個全連接層,即可完成整個網絡的構造。
注意矩陣形狀的變換。
- X_train = X_train.reshape(len(y_train), maxlen*max_features)
- X_test = X_test.reshape(len(y_test), maxlen*max_features)
完整代碼如下所示:
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 28 22:10:20 2020
@author: Eastmount CSDN
"""
from jieba import lcut
from numpy import zeros
import matplotlib.pyplot as plt
from gensim.models import Word2Vec
from sklearn.model_selection import train_test_split
from tensorflow.python.keras.preprocessing.sequence import pad_sequences
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Dense, LSTM, GRU, Embedding
from tensorflow.python.keras.callbacks import EarlyStopping
#-----------------------------------定義參數----------------------------------
max_features = 20 #詞向量維度
units = 30 #RNN神經元數量
maxlen = 40 #序列最大長度
epochs = 9 #訓練最大輪數
batch_size = 12 #每批數據量大小
verbose = 1 #訓練過程展示
patience = 1 #沒有進步的訓練輪數
callbacks = [EarlyStopping('val_acc', patience=patience)]
#--------------------------------載入數據及預處理-------------------------------
data = [
[0, '小米粥是以小米作為主要食材熬製而成的粥,口味清淡,清香味,具有簡單易制,健胃消食的特點'],
[0, '煮粥時一定要先燒開水然後放入洗淨後的小米'],
[0, '蛋白質及胺基酸、脂肪、維生素、礦物質'],
[0, '小米是傳統健康食品,可單獨燜飯和熬粥'],
[0, '蘋果,是水果中的一種'],
[0, '粥的營養價值很高,富含礦物質和維生素,含鈣量豐富,有助於代謝掉體內多餘鹽分'],
[0, '雞蛋有很高的營養價值,是優質蛋白質、B族維生素的良好來源,還能提供脂肪、維生素和礦物質'],
[0, '這家超市的蘋果都非常新鮮'],
[0, '在北方小米是主要食物之一,很多地區有晚餐吃小米粥的習俗'],
[0, '小米營養價值高,營養全面均衡 ,主要含有碳水化合物'],
[0, '蛋白質及胺基酸、脂肪、維生素、鹽分'],
[1, '小米、三星、華為,作為安卓三大手機旗艦'],
[1, '別再管小米華為了!魅族手機再曝光:這次真的完美了'],
[1, '蘋果手機或將重陷2016年困境,但這次它無法再大幅提價了'],
[1, '三星想要繼續壓制華為,僅憑A70還不夠'],
[1, '三星手機屏占比將再創新高,超華為及蘋果旗艦'],
[1, '華為P30、三星A70爆賣,斬獲蘇寧最佳手機營銷獎'],
[1, '雷軍,用一張圖告訴你:小米和三星的差距在哪裡'],
[1, '小米米聊APP官方Linux版上線,適配深度系統'],
[1, '三星剛剛更新了自家的可穿戴設備APP'],
[1, '華為、小米跨界並不可怕,可怕的打不破內心的「天花板」'],
]
#中文分析
X, Y = [lcut(i[1]) for i in data], [i[0] for i in data]
#劃分訓練集和預測集
X_train, X_test, y_train, y_test = train_test_split(X, Y)
#print(X_train)
print(len(X_train), len(X_test))
print(len(y_train), len(y_test))
"""['三星', '剛剛', '更新', '了', '自家', '的', '可', '穿戴', '設備', 'APP']"""
#--------------------------------Word2Vec詞向量-------------------------------
word2vec = Word2Vec(X_train, size=max_features, min_count=1) #最大特徵 最低過濾頻次1
print(word2vec)
#映射特徵詞
w2i = {w:i for i, w in enumerate(word2vec.wv.index2word)}
print("【顯示詞語】")
print(word2vec.wv.index2word)
print(w2i)
"""['小米', '三星', '是', '維生素', '蛋白質', '及', 'APP', '胺基酸',..."""
"""{',': 0, '的': 1, '小米': 2, '、': 3, '華為': 4, ....}"""
#詞向量計算
vectors = word2vec.wv.vectors
print("【詞向量矩陣】")
print(vectors.shape)
print(vectors)
#自定義函數-獲取詞向量
def w2v(w):
i = w2i.get(w)
return vectors[i] if i else zeros(max_features)
#自定義函數-序列預處理
def pad(ls_of_words):
a = [[w2v(i) for i in x] for x in ls_of_words]
a = pad_sequences(a, maxlen, dtype='float')
return a
#序列化處理 轉換為詞向量
X_train, X_test = pad(X_train), pad(X_test)
print(X_train.shape)
print(X_test.shape)
"""(15, 40, 20) 15個樣本 40個特徵 每個特徵用20詞向量表示"""
#拉直形狀 (15, 40, 20)=>(15, 40*20) (6, 40, 20)=>(6, 40*20)
X_train = X_train.reshape(len(y_train), maxlen*max_features)
X_test = X_test.reshape(len(y_test), maxlen*max_features)
#--------------------------------建模與訓練-------------------------------
model = Sequential()
#構建Embedding層 128代表Embedding層的向量維度
model.add(Embedding(max_features, 128))
#構建LSTM層
model.add(LSTM(128, dropout=0.2, recurrent_dropout=0.2))
#構建全連接層
#注意上面構建LSTM層時只會得到最後一個節點的輸出,如果需要輸出每個時間點的結果需將return_sequences=True
model.add(Dense(units=1, activation='sigmoid'))
#模型可視化
model.summary()
#激活神經網絡
model.compile(optimizer = 'rmsprop', #RMSprop優化器
loss = 'binary_crossentropy', #二元交叉熵損失
metrics = ['acc'] #計算誤差或準確率
)
#訓練
history = model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs,
verbose=verbose, validation_data=(X_test, y_test))
#----------------------------------預測與可視化------------------------------
#預測
score = model.evaluate(X_test, y_test, batch_size=batch_size)
print('test loss:', score[0])
print('test accuracy:', score[1])
#可視化
acc = history.history['acc']
val_acc = history.history['val_acc']
# 設置類標
plt.xlabel("Iterations")
plt.ylabel("Accuracy")
#繪圖
plt.plot(range(epochs), acc, "bo-", linewidth=2, markersize=12, label="accuracy")
plt.plot(range(epochs), val_acc, "gs-", linewidth=2, markersize=12, label="val_accuracy")
plt.legend(loc="upper left")
plt.title("LSTM-Word2vec")
plt.show()
輸出結果如下所示,仍然不理想。
- test loss: 0.712007462978363
- test accuracy: 0.33333334
對應的圖形如下所示。
3.LSTM+TFIDF文本分類
同時,補充LSTM+TFIDF文本分類代碼。
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 28 22:10:20 2020
@author: Eastmount CSDN
"""
from jieba import lcut
from numpy import zeros
import matplotlib.pyplot as plt
from gensim.models import Word2Vec
from sklearn.model_selection import train_test_split
from tensorflow.python.keras.preprocessing.sequence import pad_sequences
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Dense, LSTM, GRU, Embedding
from tensorflow.python.keras.callbacks import EarlyStopping
#-----------------------------------定義參數----------------------------------
max_features = 20 #詞向量維度
units = 30 #RNN神經元數量
maxlen = 40 #序列最大長度
epochs = 9 #訓練最大輪數
batch_size = 12 #每批數據量大小
verbose = 1 #訓練過程展示
patience = 1 #沒有進步的訓練輪數
callbacks = [EarlyStopping('val_acc', patience=patience)]
#--------------------------------載入數據及預處理-------------------------------
data = [
[0, '小米粥是以小米作為主要食材熬製而成的粥,口味清淡,清香味,具有簡單易制,健胃消食的特點'],
[0, '煮粥時一定要先燒開水然後放入洗淨後的小米'],
[0, '蛋白質及胺基酸、脂肪、維生素、礦物質'],
[0, '小米是傳統健康食品,可單獨燜飯和熬粥'],
[0, '蘋果,是水果中的一種'],
[0, '粥的營養價值很高,富含礦物質和維生素,含鈣量豐富,有助於代謝掉體內多餘鹽分'],
[0, '雞蛋有很高的營養價值,是優質蛋白質、B族維生素的良好來源,還能提供脂肪、維生素和礦物質'],
[0, '這家超市的蘋果都非常新鮮'],
[0, '在北方小米是主要食物之一,很多地區有晚餐吃小米粥的習俗'],
[0, '小米營養價值高,營養全面均衡 ,主要含有碳水化合物'],
[0, '蛋白質及胺基酸、脂肪、維生素、鹽分'],
[1, '小米、三星、華為,作為安卓三大手機旗艦'],
[1, '別再管小米華為了!魅族手機再曝光:這次真的完美了'],
[1, '蘋果手機或將重陷2016年困境,但這次它無法再大幅提價了'],
[1, '三星想要繼續壓制華為,僅憑A70還不夠'],
[1, '三星手機屏占比將再創新高,超華為及蘋果旗艦'],
[1, '華為P30、三星A70爆賣,斬獲蘇寧最佳手機營銷獎'],
[1, '雷軍,用一張圖告訴你:小米和三星的差距在哪裡'],
[1, '小米米聊APP官方Linux版上線,適配深度系統'],
[1, '三星剛剛更新了自家的可穿戴設備APP'],
[1, '華為、小米跨界並不可怕,可怕的打不破內心的「天花板」'],
]
#中文分詞
X, Y = [' '.join(lcut(i[1])) for i in data], [i[0] for i in data]
print(X)
print(Y)
#['煮粥 時 一定 要 先燒 開水 然後 放入 洗淨 後 的 小米', ...]
#--------------------------------------計算詞頻------------------------------------
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
#將文本中的詞語轉換為詞頻矩陣
vectorizer = CountVectorizer()
#計算個詞語出現的次數
X_data = vectorizer.fit_transform(X)
print(X_data)
#獲取詞袋中所有文本關鍵詞
word = vectorizer.get_feature_names()
print('【查看單詞】')
for w in word:
print(w, end = " ")
else:
print("\n")
#詞頻矩陣
print(X_data.toarray())
#將詞頻矩陣X統計成TF-IDF值
transformer = TfidfTransformer()
tfidf = transformer.fit_transform(X_data)
#查看數據結構 tfidf[i][j]表示i類文本中的tf-idf權重
weight = tfidf.toarray()
print(weight)
#數據集劃分
X_train, X_test, y_train, y_test = train_test_split(weight, Y)
print(X_train.shape, X_test.shape)
print(len(y_train), len(y_test))
#(15, 117) (6, 117) 15 6
#--------------------------------建模與訓練-------------------------------
model = Sequential()
#構建Embedding層 128代表Embedding層的向量維度
model.add(Embedding(max_features, 128))
#構建LSTM層
model.add(LSTM(128, dropout=0.2, recurrent_dropout=0.2))
#構建全連接層
#注意上面構建LSTM層時只會得到最後一個節點的輸出,如果需要輸出每個時間點的結果需將return_sequences=True
model.add(Dense(units=1, activation='sigmoid'))
#模型可視化
model.summary()
#激活神經網絡
model.compile(optimizer = 'rmsprop', #RMSprop優化器
loss = 'binary_crossentropy', #二元交叉熵損失
metrics = ['acc'] #計算誤差或準確率
)
#訓練
history = model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs,
verbose=verbose, validation_data=(X_test, y_test))
#----------------------------------預測與可視化------------------------------
#預測
score = model.evaluate(X_test, y_test, batch_size=batch_size)
print('test loss:', score[0])
print('test accuracy:', score[1])
#可視化
acc = history.history['acc']
val_acc = history.history['val_acc']
# 設置類標
plt.xlabel("Iterations")
plt.ylabel("Accuracy")
#繪圖
plt.plot(range(epochs), acc, "bo-", linewidth=2, markersize=12, label="accuracy")
plt.plot(range(epochs), val_acc, "gs-", linewidth=2, markersize=12, label="val_accuracy")
plt.legend(loc="upper left")
plt.title("LSTM-TFIDF")
plt.show()
輸出結果如下所示:
- test loss: 0.7694947719573975
- test accuracy: 0.33333334
對應圖形如下:
4.機器學習和深度學習對比分析
最終結果我們進行簡單對比,發現機器學習比深度學習好,這是為什麼呢?我們又能做哪些提升呢?
- MultinomialNB+TFIDF:test accuracy = 0.67
- GaussianNB+Word2Vec:test accuracy = 0.83
- RNN+Word2Vector:test accuracy = 0.33333334
- LSTM+Word2Vec:test accuracy = 0.33333334
- LSTM+TFIDF:test accuracy = 0.33333334
作者結合大佬們的文章及自己的經驗對其進行簡單分析,原因如下:
- 一是 數據集預處理的原因,上述代碼沒有進行停用詞過濾,大量標點符號和停用詞影響了文本分類效果。同時詞向量的維度設置也需要進行調試。
- 二是 數據集大小的原因。數據量少的情況下,推薦使用CNN,RNN的過擬合會讓你欲哭無淚。如果數據量多,也許RNN效果會更好。如果為了創新,RLSTM和RCNN等都是近幾年不錯的選擇。但如果僅僅是為了應用,用普通的機器學習方法已經足夠優秀了(特別是新聞數據集),如果考慮上時間成本,貝葉斯無疑才是真正的最好選擇。
- 三是 CNN和RNN適用性不同。CNN擅長空間特徵的學習和捕獲,RNN擅長時序特徵的捕獲。從結構來講,RNN更勝一籌。主流的NLP問題,比如翻譯、生成文本,seq2seq(倆獨立RNN)的引入突破了很多之前的benchmark。Attention的引入是為了解決長句問題,其本質就是外掛了額外的一個softmax去學詞和詞的映射關係,有點像外掛存儲,其根源來自一篇名為「neural turing machine」的paper。
- 四是 不同的數據集適應不同的方法,各種方法各有所長。有的情感分析GRU好於CNN,而新聞分類、文本分類競賽CNN可能會有優勢。CNN具有速度優勢,基本比較大的數據上CNN能加大參數,擬合更多種類的local phrase frequency,獲得更好的效果。如果你是想做系統,兩個算法又各有所長,就是ensemble登場的時候了。
- 五是 在文本情感分類領域,GRU是要好於CNN,並且隨著句子長度的增長,GRU的這一優勢會進一步放大。當句子的情感分類是由整個句子決定的時候,GRU會更容易分類正確, 當句子的情感分類是由幾個局部的key-phrases決定的時候,CNN會更容易分類正確。
總之,我們在真實的實驗中,儘量選擇適合我們數據集的算法,這也是實驗中的一部分,我們需要對比各種算法、各種參數、各種學習模型,從而找到一個更好的算法。後續作者會進一步學習TextCNN、Attention、BiLSTM、GAN等算法,希望能與大家一起進步。
參考及推薦文章:請問對於中文長文本分類,是CNN效果好,還是RNN效果好?- 知乎
五.總結
寫道這裡,這篇文章就結束了。希望對您有所幫助,同時文章中不足或錯誤的地方,歡迎讀者提出。這些實驗都是我在做論文研究或項目評價常見的一些問題,希望讀者帶著這些問題,結合自己的需求進行深入的思考,更希望大家能學以致用。
總之,本文通過Keras實現了一個RNN文本分類學習的案例,並詳細介紹了循環神經網絡原理知識及與機器學習對比。最後,作為人工智慧的菜鳥,我希望自己能不斷進步並深入,後續將它應用於圖像識別、網絡安全、對抗樣本等領域,指導大家撰寫簡單的學術論文,一起加油!
作者:華為雲開發者社區
連結:https://juejin.cn/post/7036192064375095332
來源:稀土掘金