<< Pythonによるデータ解析4(ニューラルネット応用 1/4)
【Pythonによる機械学習4(ニューラルネット応用 2/4)の目次】
TF-IDF特徴量の抽出
今回は、比較的実装が容易なTF-IDF特徴量を用います。なお、word2vec特徴量はgensimというモジュールを利用することにより抽出することができます。以下を参考にしてください。
hirotaka-hachiya.hatenablog.com
TF-IDF特徴量を抽出するために、data.sentimentalLabelledSentencesクラスでは、以下のようにmakewordNgramDictとtfidfを実行します。makewordNgramDictのtfidf定義については、data.py
を参照してください。
※data.pyの304行目を以下のように変更してください。
変更前:
# IDF (inverse document frequency)の計算 self.idf = np.log(self.nDict) - np.log(self.wordNgramDictCnt) + 1
変更後:np.log(self.nDict)をnp.log(self.nData)に変更
# IDF (inverse document frequency)の計算 self.idf = np.log(self.nData) - np.log(self.wordNgramDictCnt) + 1
>>> import data >>> import numpy as np # データの読み込み >>> myData = data.sentimentalLabelledSentences('amazon_cells_labelled.txt') # amazon_cells_labelled.txtの全ての文章を使って、uni-gramの辞書を作成 >>> myData.makeWordNgramDict(N=1) # uni-gram辞書(uni-gramのリスト)の表示 >>> myData.wordNgramDict ['so', 'there', 'is', 'no', 'way', 'for', 'me', 'to', 'plug', 'it', 'in', 'here', 'the', 'us', 'unless', 'i', 'go', 'by', 'a', ... # uni-gram辞書の次元 >>> len(myData.wordNgramDict) 1878 # 文章「This is very good product! You should buy it.」からtfidf特徴量の抽出 >>> _, tfidf = myData.tfidf(sentence="This is very good product! You should buy it.") # tfidf特徴量の次元 >>> len(tfidf) 1878 >>> np.sum(tfidf==0) 1870 # uni-gramとtfidf特徴量の値 >>> tfidf = np.array(tfidf) >>> inds = np.where(tfidf>0)[0] >>> for ind in inds: print("{} : {}".format(myData.wordNgramDict[ind],tfidf[ind])) ... is : 0.25606477482646683 it : 0.24439234739565263 good : 0.3603690185777967 you : 0.38824035882469876 this : 0.2604450370923061 very : 0.33227878003115646 product : 0.3900422093749666 buy : 0.49633162998156966 should : 0.6521460917862246
ここでは、文章「This is very good product! You should buy it.」のtfidf特徴量は、uni-gram辞書と同じ1878次元となっていて、ほとんどの要素が0となっている、とてもスパースな特徴量ベクトルとなっていることがわかります。また、tfidfが表している各uni-gramの重要度は、「is」、「it」および「this」などのよく使われる単語では「0.2」台と低く、一方、「good」、「product」、「buy」および「should」では高い値となっていることがわかります。
演習2
以下の内容に従い、data.sentimentalLabelledSentence.createDataを用いてuni-gramの学習データと評価データを作成し、中間層のノード数が100のニューラルネットワークを学習し評価しましょう。また、可視化結果を確認しましょう。
作成したスクリプト、出力したcsvファイルおよびグラフ画像ファイルを、Moodleにて提出してください。
ニューラルネットワークの学習と評価
ニューラルネットワークを学習し評価するために、データを分割し学習データのみからn-gram辞書を作成し、学習、評価データともにtfidf特徴量に変換する必要があります。data.sentimentalLabelledSentencesクラスでは、学習と評価データを作成するcreateDataメソッドを用意しています。createDataクラスを用いて生成したuni-gramのデータを用いてニューラルネットワークを学習してみましょう。
neuralNetwork.py(または、neuralNetwork_template.py)のメインを以下のように変更してください。
【変更前の人工データを用いた場合】
# 1) 人工データの生成(難しい場合) myData = data.artificial(300,150,mean1=[1,2],mean2=[-2,-1],mean3=[4,-2],mean3multi=[-2,4],cov=[[1,0],[0,1]]) # 2) 3階層のニューラルネットワークモデルの作成 classifier = neuralNetwork(myData.xTrain, myData.tTrain,hDim=20) # 3) 学習前の事後確率と学習データの描画 myData.plotClassifier(classifier,"train",prefix="posterior_NN_before") # 7) 学習した事後確率と学習データの描画 myData.plotClassifier(classifier,"train",prefix="posterior_NN_after") myData.plotClassifier(classifier,"test",prefix="posterior_NN_after_test")
【変更後のAmazonレビューデータを用いた場合】
# 1) 人工データの生成(難しい場合) #myData = data.artificial(300,150,mean1=[1,2],mean2=[-2,-1],mean3=[4,-2],mean3multi=[-2,4],cov=[[1,0],[0,1]]) # Amazonレビューデータ myData = data.sentimentalLabelledSentences('amazon_cells_labelled.txt') #------------------------------------ # 学習と評価データの作成 # gramNum: n-gramのオーダー(スカラー) # trainRatio: 学習データ数の割合(スカラー) # isRandom: データをランダムにシャッフルするか否か myData.createData(gramNum=1, trainRatio=0.8, isRandom=True) # 2) 3階層のニューラルネットワークモデルの作成 classifier = neuralNetwork(myData.xTrain, myData.tTrain,hDim=100, batchSize=500) # 3) 学習前の事後確率と学習データの描画 #myData.plotClassifier(classifier,"train",prefix="posterior_NN_before") # 7) 学習した事後確率と学習データの描画 #myData.plotClassifier(classifier,"train",prefix="posterior_NN_after") #myData.plotClassifier(classifier,"test",prefix="posterior_NN_after_test")
中間層のノード数が1,5,10,50,100,200,300,500,800と異なるニューラルネットワークを学習し評価を行った結果は以下のようになります。
ランダムに選んだ200個の評価データに対して、正解率が約80%で精度よく分類できていることがわかります。また、人工データと同様に中間層のノード数を増やしすぎる(例えば、500超え)と正解率が悪化していることもわかります。また、線形モデルのロジスティック回帰でも、ニューラルネットワークと同等の正解率が得られていることもわかります。
感情分類の結果の可視化
正解率のプロットだけでは、実際にどれくらい感情分類ができているのかが直感的にはわかりません。そこで、ニューラルネットワークにより予測された感情カテゴリ(肯定的1、否定的0)の結果を、文章と真のカテゴリと比較できるように、商品レビュー(sentence)、真の感情カテゴリ(score)および予測(predict)列にもつccsv形式でファイルに出力します。
neuralNetwork.py(または、neuralNetwork_template.py)のメインに以下を追加してください。
#-------------------------------------------- # 9) 結果の可視化 import pandas as pd from datetime import datetime import os # 結果をdataframeに格納する predicts = np.argmax(classifier.predict(myData.xTest),axis=0) resultDF = pd.DataFrame(columns=['sentence','score','predict']) for ind in np.arange(len(predicts)): sentence = myData.data['sentence'][myData.testInd[ind]] score = myData.data['score'][myData.testInd[ind]] resultDF = resultDF.append(pd.Series([sentence,score,predicts[ind]],index=resultDF.columns),ignore_index=True) # 日時 postfix = datetime.now().strftime('%Y%m%d%H%M') # csvファイルに書き出す fullpath = os.path.join("visualization","sentimentalLabelledSentences{}.csv".format(postfix)) resultDF.to_csv(fullpath) #--------------------------------------------
実行すると、visualizationフォルダに csvファイル「sentimentalLabelledSentences日付.csv」が出力されます。
以下は、中間層のノード数が200のときの結果の例です。
出力したcsvファイルの例は以下にあります。
https://github.com/hhachiya/intelligentSystemTraining/blob/master/visualization/sentimentalLabelledSentences201712051824.csv
この結果から、多くの文章に対して、感情カテゴリに正しく分類できているのがわかります。少ない失敗事例を見ていくことにします。例えば、4つ目の「It's AGGRAVATING!」をニューラルネットは「肯定的1」と分類しています。これは、「aggravating」という単語が、amazon_cells_labelled.txtではこのレビュー以外に出現しておらず学習データに含まれていないたaggravatingに対するIF-IDF特徴量が正しく計算できていなことが原因として考えられます。
また、10番目の「I bought these hoping I could make my Bluetooth headset fit better but these things made it impossible to wear.」も誤分類していますが、これはhopingとbutで複数の文節をつなげた文章となっており、今回の単語n-gramに対するIF-IDF特徴量のように、単語の出現頻度に基づき文章の特徴量を抽出するBug-of-words特徴量では、単語の前後関係や文節間の関係を表現できていないのが原因と考えられます。このような問題を解決するためには、ニューラルネットワークを、再帰的ニューラルネットワーク(Recurrent Neural Network)などの入力ベクトルの時系列な関係を考慮したモデルに拡張する必要があります。