BLSTMを用いた文書分類でデザイナーズマンション予測に再挑戦

はじめに

仕事で深層学習を使うことはないのですが、扱える技術の幅を広げていきたいなと思い、BLSTMを用いた文書分類についてkerasでの簡単なサンプルコードをやってみようと思います。データは以前集めた、某不動産紹介サイトの賃貸マンションの設備に関する文書とそれがデザイナーズマンションかどうかのラベルを使います。そして文書内の単語からその文書がデザイナーズマンションかどうかを予測します。前回はAUCで83%だったので、それを超えれると良いですね。

目次

単純なRNNとは

  • モチベーション
    • フィードフォワード型のニューラルネットワークではうまく扱うことができない時系列データをうまく扱えるようにすること。
  •  特徴
    • 入力が互いに関係している(多層パーセプトロンの際は置かれていない仮定)
      • 直訳すると循環するニューラルネットワークとなる。
    • 最初の文の単語が2つ目、3つ目の単語に影響を与える可能性を考慮。
    具体的な関数の形としては、 $$ h_t = \tanh (W h_{t-1} + U x_t) \\\ y_t = softmax(V h_t) $$ で与えられる。
    \( h_t \)は隠れ層の状態を、\( x_t \)は入力変数を、\( y_t \)は出力ベクトルを表している。(他のアルファベットは重み行列)

LSTMとは

  • モチベーション
    • 単純なRNNの勾配消失問題を解決するために提案された手法。
  • 特徴
    • 単純なRNNに置き換えることで性能が大幅に向上することも珍しくない。
      • 時系列データ、長い文章、録音データからなる長期的なパターンを取り出すことを得意としている手法。
    • 勾配消失問題に強い
    • Long Short-Term Memory:長短期記憶
      • 長期依存性を学習できるRNNの亜種。
      • 入力ゲート、忘却ゲート、出力ゲート、内部隠れ層という4つの層が相互に関わり合う。重要な入力を認識し、それを長期状態に格納すること、必要な限りで記憶を保持すること、必要なときに記憶を取り出すことを学習する。
        • 入力ゲート:内部隠れ層のどの部分を長期状態に加えるかを決める。
        • 忘却ゲート:長期状態のどの部分を消去するか決める。
        • 出力ゲート:各タイムステップで、長期状態のどの部分を読み出し、出力するかを決める。
$$
i = \sigma (W_i h_{t-1} + U_i x_t) \\
f = \sigma (W_f h_{t-1} + U_f x_t) \\
o = \sigma (W_o h_{t-1} + U_ox_t ) \\
g = \tanh (W_g h_{t-1} + U_g x_t) \\
c_t = (c_{t-1} \otimes f ) \otimes ( g \otimes i) \\
h_t = \tanh (c_t) \otimes o
$$ i:入力ゲート(input)
f:忘却ゲート(forget)
o:出力ゲート(output)
\( \sigma \):シグモイド関数
g:内部隠れ層状態
\(c_t \):時刻tにおけるセル状態
\(h_t \):時刻tにおける隠れ状態

Bidirectional LSTMとは

  • モチベーション
    •  従来のRNNの制約を緩和するために導入。
  • 特徴
    • ある特定の時点で過去と将来の利用可能な入力情報を用いて訓練するネットワーク
      • 従来のRNNのニューロンをフォワード(未来)なものとバックワード(過去)なものとの2つに分ける。
        • 2つのLSTMを訓練している。
      • 全体のコンテキストを利用して推定することができるのが良いらしい。
        • 文章で言うと、文章の前(過去)と後(未来)を考慮して分類などを行うイメージ。
      • 人間もコンテキストを先読みしながら解釈することもある。
    • BLSTMは全てのシークエンスの予測問題において有効ではないが、適切に扱えるドメインで良い結果を残す。
    • 1997年とかなり歴史があるものらしい
出典:Deep Dive into Bidirectional LSTM

Bidirectional LSTMで文書分類

今回は、BLSTMを使って、マンションの設備に関するテキスト情報から、そのマンションがデザイナーズマンションかどうかを予測します。全体のソースコードはGoogle Colabを御覧ください。

データ

データを確認してみると、
 
今回のデータがテキストとラベルからなっていることがわかる。なお、データ数は1864件あり、そのうち16%がデザイナーズマンションです。

前処理

Google Colab上で形態素解析を行うために、MeCabをインストールする。
 
これでMaCab NeologdをGoogle ColabのPythonで実行できます。
テキストデータの前処理を行うために名詞だけを抽出してリスト形式で返す関数を定義します。
 
ここで、語彙に関するデータを作成します。
 
続いて、単語とそのインデックスからなるdict形式のデータを作成します。
 
最後に、これまでに作成した語彙とそのインデックスのデータを用いて、実際に学習に使う入力データと出力データを作成します。
 

実行

先程作成したデータを訓練データとテストデータに分けます。
ここで、AUCを計算するための関数を定義します。(まるまる拝借しました)
続いて、kerasを用いて、ネットワークを構築し、コンパイルを行います。
  テストデータでの予測精度を確認します。
実際のラベルとテキストデータを見比べてみます。
LSTMの場合は、BLSTMに関する記述(1行)をネットワークから除外すれば良いので簡単に試すことができます。 せっかくなので比較してみたところ、AUCは0.841でした。BLSTMは0.844だったので、若干上回る程度のようです。 以前扱った記事ではデザイナーズマンション分類はAUCが0.83程度だったので、LSTMを使ったほうが精度が少しだけ高いようです。

追記

TensorBoardの検証のloglossを見てみます。 エポックに対してloglossが非常に荒れています。学習曲線としてはセオリー的にアウトなようです。一方で、検証のAUCはエポックに従って高まるようです。 AUCは良くなっているけどloglossが増え続けている。loglossが下がらないと過学習している可能性が高いので、これは過学習しているだけなのだろう。

参考文献

 

[R]ボージョレ・ヌーボーのコメントに対してLDATSパッケージを使って時系列トピックモデルを扱う

はじめに

先日、某勉強会でLTをしました。その際に10秒だけ紹介したRのパッケージについて記事を書いてみようと思います。

LDATSパッケージについて

時系列でのトピックモデルを推定することができるパッケージです。
やっていることとしてはLDAでトピックを推定して次元を減らし、そのトピックの多変量時系列に関してベイズ手法による変化点検知のためのパラメータ推定を行っているようです。GitHubの該当しそうなソースコードに多変量のデータに対するsoftmax関数での回帰をやっているとの記述がある。(multinomial Bayesian Time Series analysis

元となっている論文を見る限り、BoW(Bag of Words)を想定して作っておらず、20~30程度のグループからなるデータに対して適用するのがちょうど良いです。アクセスログのページカテゴリや、マーケティングの顧客セグメントであればそんなに数は多くないので扱いやすいと思います。

データ

Webサイトから集めてきたボージョレ・ヌーボーのキャッチコピー14年分を今回は扱います。実は販売店側のキャッチコピーとワイン委員会が決めた評価が存在します。私の知っている世界は販売店側のキャッチコピーだけでした。

試してみた

今回はとにかく動くことだけを考えて、汚いコードとなっております。やっていることとしては、キャッチコピーを販売側とワイン委員会側のものを一つにつないで、数字を正規表現で「数字」に変換し、RMeCabで形態素解析をし、LDATS向けの形式のデータを作成していきます。
途中で、日本語の文字化け問題を回避するためにGoogle翻訳を使って単語名を置き換えています。
1時系列につき1文書となるようにデータを作っていく必要があるのですが、今回はボージョレ・ヌーボーのキャッチコピーなので最初から1時系列につき1文書となっているため都合が良いです。
データとソースコードはこちら

こちらは論文の図と同じものだとドキュメントの説明にあったので、論文の説明を見る限り、表すものとしては以下のようです。

  • 一番上の積み上げグラフはトピックごとの単語の割合を表しています。
  • 二番目の折れ線グラフはLDAによって推定されたトピックの時系列推移です。
  • 三番目のヒストグラムは二番目の時系列における変化点を集計したものです。
  • 四番目の折れ線グラフはモデルが推定したトピック割合の変化点の前後での推移です。

今回の図では文字が潰れていて見にくいですが、

  • トピック1はボキャブラリーが比較的リッチなコメント(「フルーティー」「フレグランス」「複雑」)
  • トピック2は数字を用いたコメント(「何年に一度の!」みたいな)
  • トピック3はボキャブラリーが貧相なコメント(「すごい!」みたいな)

のようです。
二番目の折れ線グラフを見る限り、周期的に数字を用いたコメントが現れているように思われます。四番目の折れ線グラフの変化点を見る限り、近年は数字を用いたコメントが相対的に減ってきて、リッチなボキャブラリーになってきているようです。

おわりに

時系列トピックモデルをカジュアルに試せる面白そうなパッケージだなと思い、LDATSパッケージを触ってみましたが、そもそもBoWなどを想定して作られているパッケージではないので、単語数が多いような分析ではそもそも可視化ができず使いにくいだろうなと思いました。マーケティングなどでユーザーのセグメントの推移を分析したい場合などにちょうど良いのだろうと思われます。

参考情報

[1] Long‐term community change through multiple rapid transitions in a desert rodent community
[2] Latent Dirichlet Allocation coupled with Bayesian Time Series analyses
[3] Package ‘LDATS’

R advent calendar 2019 RSelenium、jpmesh、sfパッケージで東京23区の事故物件を分析してみよう!

はじめに

今回で3回目となるR advent Calendarですが、前回は「一発屋芸人の検索トレンドのデータ」を扱い、前々回は「ポケモンのデータ」を扱いました。今回は人の命に関わるようなデータを扱ってみたいと思い、某サイトから東京都の23区内における事故物件の住所と詳細を集めてきました。どのようなエリアが事故が起きやすいのかの分析を行います。(以下では、事故物件をAP(Accident Property)と呼びます。)

分析工程

・データの収集
・データの整形
・可視化
・分析

データの収集

APに関する情報を某サイトより集める必要があります。そこで必要なライブラリとしては、RSeleniumやtwitteRがあげられます。
twitteRが必要な理由は、APに関するサイトにAPの一覧ページがなく、公式アカウントがAPに関するページのリンクを投稿しているところにあります。ただ、私が以前使っていた頃とはTwitterAPIの仕様が変わり、3ヶ月よりも前の情報にアクセスできなくなっていました。そのため、今後のデータに関してはTwitterAPIでいいのですが、過去のものに関しては別アプローチが必要となります。
また、APに関するサイトはJavaScriptで地図が表示されているだけなので、RSeleniumを使って地図をクリックさせ、表示された情報をスクレイピングするなどの処理が必要となります。
当初の想定ではTwitterのデータ収集、リンク先の情報をRSeleniumでスクレイピングするだけの簡単な仕事だと思っていたのですが、過去のデータにアクセスできないので、地図上で一つ一つ見つけていくためにRSeleniumだけで頑張ることにしました。(私の過去のアドカレ史上、一番面倒なデータとなりました。)

誰もやらないと思うのですが、一応手順を記しておきます。

RSeleniumだけでMapからAPの情報を抽出するための手順
1.都内の住所一覧を収集
2.検索窓に住所を入力
3.検索結果一覧の上位5件をクリック
4.一度地図を引くことでAPを広い範囲で捉えれるようにする
5.APのマークの要素を取得し、1件ずつクリックし、表示されたAPの情報をデータフレームに格納する

こちらが取得できたデータです。

RSeleniumでAPの
・住所
・発生時期(フリーテキスト)
・AP詳細
・投稿日
を集めることができるので、その住所データに対して、Yahoo!のジオコードに関するAPIを利用します。(利用申請が必要なはずです。4年前くらいに申請していたのでそのまま使えました。)
Yahoo!のAPIを使えば、住所から緯度経度の情報を取得することができます。

APの緯度経度がわかれば、jpmeshパッケージを用いて1kmメッシュやら10kmメッシュやらのメッシュデータに変換することができます。
jpmeshを用いてメッシュデータに変換し、メッシュ単位でAPの発生件数を集計します。

データ収集用のソースコードは思いのほか長くなってしまったので、GitHubにあげておきました。
https://github.com/KamonohashiPerry/r_advent_calendar_2019

ここで再度、手順を整理しておきましょう。

Twitterに出てきたものだけを取得(直近3ヶ月〜)する場合、
run_tweet_collect.RでTweetを収集

run_selenium.RでAPの情報をスクレイピング

run_map_api.Rで住所から緯度経度の取得

making_mesh_data_and_download_other_data.Rで緯度経度からメッシュデータへの変換、その他の人口データや地価データと接続をします。

直近3ヶ月以前のものを取得する場合、
run_selenium_from_map.Rで地図上から直接APの情報を取得する

making_mesh_data_and_download_other_data.Rで緯度経度からメッシュデータへの変換、その他の人口データや地価データと接続をします。

データの整形

APの1kmメッシュデータを手に入れたら、kokudosuuchiパッケージを使って国土地理院の収集したデータをつなぎこみます。手順としては、以下のとおりです。

まずは推計人口というそのエリアの人口の予測値です。今回は2010年のものを抽出しました。こちらは1kmメッシュのデータなので、変換することなく使えて都合が良いです。

続いて、2015年の公示地価を抽出しました。

こちらはメッシュデータではないので、緯度経度の情報から1kmメッシュのデータに変換する必要があります。後で行います。

単純集計・可視化

今回のデータセットのデータ数は3919件です。本当は7000件以上はあると思われますが、マップから取ってくるという勝手上、なかなか全てを取り切ることができませんでした。

まずは、1kmメッシュごとのAP発生件数のヒストグラムです。

1kmメッシュにおける人口のヒストグラムです。

公示地価のヒストグラムです。

1kmメッシュにおける人口あたりのAP件数のヒストグラムです。

分析

ここでは、色々な軸でAPのデータに向き合ってみようと思います。

APの発生件数の集計

人口が多いところがAPの発生件数が多いところだと思われますが、とりあえず確認します。

世田谷区は最も人口が多いことから、AP発生件数では一番となっています。続いて、歌舞伎町などがある新宿が来ています。しかしながら、人口に占めるAP発生件数で言うと、港区がかなり高く出ているのがわかります。

人口あたりのAP件数

ここでは、メッシュデータをsfパッケージ用のオブジェクトに変換して、1kmにおける人口あたりのAP発生割合を可視化しています。

こちらのmapviewパッケージで作ったマップはインタラクティブにいじることができます。ぜひ関心のあるエリアでいじってみてください。

1kmメッシュ人口あたりのAP発生件数(×100)の可視化

1kmメッシュでのAP発生件数の可視化

比率ベースで、色の明るいメッシュのところを見ると、港区、中央区、新宿区、渋谷区などがAPが発生しやすいようです。件数ベースで言うと新宿が一番多いですね。
一番色が明るい港区はてっきり六本木ではないかと思ったのですが、新橋から日比谷にかけたエリアでした。会社員による自○が多いようです。恐ろしいものです。

APの名前の集計

APの名前を集計してみます。これは別にこの名前だからAPになりやすいというわけではなく、単純に数が多いだけの可能性がありますし、実際にそうだろうと思われます。AP発生率を知るには、APではないものも含めた全物件名に占めるAP発生件名を手に入れないといけませんが、全物件名を収集するのが難しいことから単純に頻度の集計となります。今回は、wordcloud2パッケージを使って、ワードクラウドにしてみます。文字が大きいと頻度が高いものとなります。

ハイツ、荘、コーポ、マンション、号棟、アパート、ハウスなどが多く出現しているようです。ただ、物件の名前としても頻度が高いとも考えられますね。

地価と人口あたりのAP発生件数の関係

ここでは地価のデータとAP発生の関係性について見てみます。

地価の階級値(10個のパーセンタイルに分割)を横軸に、縦軸に人口あたりAP発生数をおくと、地価が上がるに従い人口あたりAP発生数が高まる傾向があります。これは、人口密度が高く地価の高いところではAPが発生しやすいということを示しているのではないでしょうか。人口密度が高いと地価があがる、人口密度が高いと治安が悪くなるという可能性が考えられます。

APの詳細の集計

ここではAPになってしまった詳細の内容について先ほどと同様に形態素解析を行いワードクラウドにしてみます。

どうやら孤独死が多いようです。高齢者の人口構成比が関係しているのだろうと思われます。

APの発生時期に関するテキストマイニング

ここでは、発生時期に含まれる四桁の数字を集計して、何年くらいのAPが多いのかをざっくりと掴みます。

どうやら昔のデータはあまり登録されていないようです。記憶が確かではないかもしれませんし、古すぎるものは消されているのかもしれませんね。あのサイトはユーザー生成コンテンツ(UGC)なので、投稿する人はそこまで昔のことをわざわざ投稿しないのかもしれないですね。

APの詳細に関するテキストマイニング

ここではトピック数10として、topicmodelsパッケージを使いLDAを行います。

なかなか恐ろしいキーワードが多いですが、なんとなくですがうまく分類されているのではないかと思われます。

トピック1は男性の不幸
トピック2は不動産屋に言われた告知事項
トピック3は孤独死
トピック4は病死
トピック5は火災・転落・事故
トピック6は事故のあった建物に関する記載
トピック7は腐乱した事例
トピック8は建物に関して不明であることの記載
トピック9は心理的瑕疵あり
トピック10は自○

となっているように思われます。まさかこのようなデータにトピックモデルを使うことになるとは。

おわりに

今回はR言語のみを用いて、APに関するデータを収集し、地図にプロットしたり他のメッシュデータとつなぎ合わせて分析をするなどしました。APが発生しやすいエリア、APと地価との関係、APのテキストマイニングなど興味深い結果が得られたと思います。
一つ残念なのは、時系列情報がフリーテキストなので、APがどのエリアでどの頻度で発生していくのかの分析のコストが高く、今回は時系列情報を用いた分析にチャレンジできませんでした。
今後はタクシーの需要推定の分析で行われているように、メッシュ単位でのAP発生確率の推定などを機械学習で行えると面白いなと思います。どなたか一緒にアノテーションしましょう!

それでは、どうか良い年末をお過ごし下さい!
メリークリスマス!

参考情報

数多くの方々の記事を見てどうにか仕上げることができました。感謝します。

[1]【追記あり】sfパッケージでシェープファイルを読み込んでmapviewパッケージで可視化するまで
[2]How to use mesh cord in R
[3]Rを使ってワードクラウドを作ってみました
[4]国土数値情報ダウンロードサービスWeb APIからデータを取得するためのRパッケージです
[5]東京の地価公示データを眺める
[6]Chapter 1 Introduction to spatial data in R
[7][翻訳] RSelenium vignette: RSeleniumの基本
[8]RからYahoo!のジオコーディングを利用する方法
[9]EMBEDDING A LEAFLET MAP ON WORDPRESS
[10]mapview advanced controls
[11]RSeleniumでChromeからファイルをダウンロードするディレクトリを指定する方法
[12]Selenium Serverが立ち上がらないときはportが被っているかも!?
[13]brew install selenium-server-standalone
[14]ナウでヤングなRの環境変数管理方法
[15]タクシードライバー向け需要予測について
[16]LDA with topicmodels package for R, how do I get the topic probability for each term?
[17]dplyr — 高速data.frame処理

[Python]機械学習などでテキストデータを特徴量にする際のソースコード集

テキストデータの特徴量化について

仕事ではテキストデータを多用するので、機械学習などで扱うためにテキストデータを特徴量にするためのアプローチを色々と整理してソースコードを残しておきたいと思います。今回はあくまでも私の知っているものだけなので、網羅性はないかもしれませんが悪しからず。
(2019/08/18 追記)Stackingをカジュアルに行えるvecstackというモジュールを用いた予測も試してみました。下の方の追記をご覧ください。

アプローチ

テキストデータを特徴量にする際のアプローチとしては、以下の3つが良く使っているものとなります。
・単語ベース
・クラスタ、トピック、分散表現ベース
・文書間の類似度ベース

今回扱うデータ

ひょんなことから、昨年10月くらいに取りためたマンションの施設情報のテキストです。

緑色が印象的な某不動産紹介サイトをクローリングしました。全部で1864件ほどの文書数となります。

加えて、デザイナーズマンションかどうかのフラグを作成しました(17%くらいがデザイナーズマンションの割合)。これでもって、マンションの施設情報からデザイナーズマンションかどうかを分類できるかチャレンジしたいと思います。
ここにデータを置いていますので、興味のある方はご利用ください。

今回扱うモデル

ランダムフォレストです。10foldsクロスバリデーションによるAUCの結果を各手法のスコアとして扱います。

こちらは、任意の手法に関して10foldsクロスバリデーションで実行し、AUCのグラフを生成してくれるソースコードです。主にscikit-learnのサイトに載っているものです。引数のclassifierをsklearnの任意のモデルのインスタンスで渡せば動きます。

単語ベース

シンプルに単語をそのまま特徴量にするというものですが、文書によっては単語数が多すぎて収集がつかないと思います。そこで単語を簡単に選択できるDocumentFeatureSelectionというパッケージを利用します。

このパッケージでは
・TF-IDFベースの特徴量選択
・PMI(Pointwise Mutual Information)ベースの特徴量選択
・SOA(Strength of association)ベースの特徴量選択
・BNS(Bi-Normal Separation)ベースの特徴量選択
を行うことができます。

まずは今回のベースラインとして、単語のカウントベースでの特徴量を扱いたいと思います。
その前に、GitHubに上がっているデータに対して以下のように簡単な前処理をしておきます。

ようやくベースラインの予測となります。以下のコードを実行すると、ROCが描かれた図がJupyter上で表示されます。

AUC82%というのはベースラインとしてはなかなか強敵なのではないでしょうか。

さて、本題の特徴量選択パッケージの適用をするためのソースコードを以下に記します。

以上のソースコードを実行すれば、tf_idf_scored_df、pmi_scored_df、soa_scored_df、bns_scored_dfにスコアを付与された単語のリストが手に入ります。

ここでは各スコアに関してアドホックに閾値を設けて、特徴量として利用することにします。

TF-IDFベースの特徴量選択

PMIベースの特徴量選択

SOAベースの特徴量選択

BNSベースの特徴量選択

クラスタ、トピック、分散表現ベース

続いて、k-meansやLDAやword2vecを用いて特徴量を作成する方法です。今回はk-means、ミニバッチk-means、LDA、FastTextによる分散表現を扱います。

k-means、ミニバッチk-means

LDA

こちらはgensimでLDAを推定し、推定したトピックの割合をデータフレームで返すコードです。

トピック数をとりあえず30個くらいに指定して推定したトピックの割合を特徴量として文書分類を行います。そのため、特徴量の数は30個になります。

FastTextによる分散表現

今回はデータ数が少ないことから、学習済みの分散表現を用います。日本語のコーパスに対して、FastTextで推定された分散表現となります。学習済み分散表現はこちらから拝借しました。

分散表現は単語に対して計算されるので、単語に対して分散表現を足し合わせたものを特徴量として扱います。ここでは分散表現の合計値、平均値、TF-IDFで重みを付けた平均値の3つのパターンを試します。

合計値ベース

平均値ベース

TF-IDFで単語を重みづけた平均値ベース

文書間の類似度ベース

今回は、デザイナーズマンションの定義文に似ているかどうかという観点で類似度ベースの特徴量を作ってみたいと思います。

今回は変数が一つだけなので、機械学習はせず、デザイナーズマンション割合との関係を図示するにとどめておきます。横軸がデザイナーズマンションの定義と施設情報の類似度で、縦軸がデザイナーズマンション割合です。

どうやら、途中でデザイナーズマンション割合がピークを迎えるようです。

おわりに

最先端の手法は調べれていないですが、テキストデータを特徴量に落とし込む手段を備忘録として残しておきました。今回あげた中では、SOAベースの特徴量選択のAUCが83%と一番高かったですが、ベースラインが82%と僅差でした。そして、分散表現形のものは80%に届いた程度です。余力があれば新しい特徴量の作り方が分かり次第アップデートしようと思います。

追記

“Automate Stacking In Python How to Boost Your Performance While Saving Time”という記事を見つけたので、紹介されているvecstackモジュールを使って今回のモデルに関して簡単にstackingしてみようと思います。
コードに関しては、こちらのGitHubに上げています。試してみた所、AUCは88%になりました。結構上がりましたね。しかもコードはめちゃ短いので楽です。

参考文献

[1]Julian Avila et al(2019), 『Python機械学習ライブラリ scikit-learn活用レシピ80+』, impress top gear
[2]Receiver Operating Characteristic (ROC) with cross validation
[3]@Kensuke-Mitsuzawa(2016), “テキストデータで特徴量選択をする”, Qiita
[4]JapaneseTokenizer 1.6
[5]DocumentFeatureSelection 1.5
[6]自然言語処理における自己相互情報量 (Pointwise Mutual Information, PMI)
[7]【Techの道も一歩から】第3回「第11回テキストアナリティクス・シンポジウム」
[8]文書分類タスクでよく利用されるfeature selection

学習済み分散表現を用いた文書分類に挑戦(一部再学習も)

はじめに

2018年9月のテキストアナリティクスシンポジウムに行った際に、学習済みの分散表現で事前学習したモデルを使って分類してうまくいく事例が紹介されていました。
全てのタスクにおいてうまくいくとは思えませんが、試すコストはあまりかからないので試してみます。

2017年のテキストアナリティクスシンポジウムにおいても、メルカリやGunosyでは分散表現を用いた手法が一番精度が高いと言われていましたし、今年の会ではNLP系の学会でも分散表現はデファクトスタンダードになっているという話も伺いました。
2013~14年はLDAを使った研究が多かった気がしますが、徐々にシフトしていっているんですね。

これまで(Word2Vecを用いて蒙古タンメン中本の口コミ評価を予測してみる)は4000件程度の蒙古タンメン中本の口コミの情報を元に分散表現を手に入れていましたが、学習済みの分散表現を用いたアプローチも有効かもしれないと思い、試してみようと思います。

分類タスク

某グルメ口コミサイトの蒙古タンメン中本の口コミのテキストから、3.5点以上の評価かどうかを予測するタスクを扱います。
本当は、ポケモン図鑑の説明文から水やら炎やらのタイプを予測するとかをしたいのですが、あいにく手元にデータがないので、以前集めた蒙古タンメン中本の口コミを使います。(実は後日、ポケモン図鑑のデータを集めたのですが、平仮名にまみれたデータな上に、データ数も800件しかなかったので、どのみち厳しかったです。)

学習済み分散表現

Word2Vecなどで大量の文書をもとに学習させた分散表現のことを指します。
大規模コーパスで分散表現を手に入れる際は、数十GBにも相当するテキストデータを数時間かけて推定するので、学習済みのモデルは非常にありがたいです。(4年前に会社のPCで計算した際は、12時間くらいかかったこともありました。)

無料で提供してくださっている分散表現については、すでにこちらのブログで紹介されています。そこで紹介されているものに少し付け足すと、日本語の分散表現に関しては以下のようなものがあります。

  • 白ヤギコーポレーションのモデル:Gensim
  • 東北大学 乾・岡崎研究室のモデル:Gensim
  • Facebookの学習済みFastTextモデル:Gensim
  • NWJC から取得した単語の分散表現データ (nwjc2vec):Gensim
  • NNLM embedding trained on Google News:TensorFlow

そこで、今回は各種学習済み分散表現と蒙古タンメン中本コーパスで求めた分散表現の文書分類の性能バトルをしてみたいと思います。
ただ、分散表現ではなく、単語の頻度をもとに特徴量を作ったものが一番精度が高いのですが、分散表現同士の比較でもってどの学習済み分散表現が中本の口コミ分類に役に立ちそうなのかを明らかにしようと思います。(本来は分析という観点から即でボツですが、見苦しくも比較していきます。)

前処理

前処理は以下の通りで、テキストデータを分かち書きして、数値や低頻度・高頻度語を除外しています。

処理を施すとこのようなデータになります。

特徴量は、scikit-learnのCountVectorizerやTfidfVectorizer、分散表現の合計・平均・TF-IDFを求めたものを用意します。

蒙古タンメン中本の口コミ4000件から作成した分散表現:Gensim

まず、以前のブログで紹介した蒙古タンメン中本の分散表現ですが、以下のように推定しています。

Pipelineを用いてExtraTreesClassifierによる学習をします。特徴量は先程あげた、テキストベースのCountVectorizerやTfidfVectorizer、分散表現の合計・平均・TF-IDFで、評価指標はAUCのクロスバリデーションスコアとします。

汗に関してコンテキストの似ている単語を抽出しています。

結果は、以下の通りで、分散表現を使わない方がAUCが高いです。ただ、w2v_tfidf(分散表現のTF-IDFを特徴量にしたもの)が分散表現の中でAUCが高いようです。今回はこの60.5%をベースラインに比較していこうと思います。

白ヤギコーポレーションのモデル:Gensim

こちらのリンク、「word2vecの学習済み日本語モデルを公開します」から、ダウンロードしてそのまま以下のコードでモデルを扱えます。

汗の関連語を抽出していますが、中国の歴史の何かですか?可汗とかいう単語は聞いたことあるかも。

まずは白ヤギさんの分散表現をそのまま使って予測してみます。(コードは先程のものとほぼ重複するので省略しています。)
残念ながら、ベースラインの60.5%には至りませんでした。

hogehoge.modelというフルモデル形式の場合は、再学習が可能です。詳しくはこちら(models.word2vec – Word2vec embeddings model)に書かれています。

今回は、白ヤギさんの分散表現に対して、追加で蒙古タンメン中本のテキストを食わせて再学習させます。

ベースラインの60.5%よりも下回り、さきほどの白ヤギさんのもともとの分散表現よりも下回りました。

再学習してもかえって精度が下がったりすることから、簡単に精度が出るわけではなさそうです。まぁ、理想はその適用領域での大量のテキストデータがあることで、Wikipediaを元に作成した分散表現に強く依存しても駄目なのだろうと思われます。

東北大学 乾・岡崎研究室のモデル:Gensim

日本語 Wikipedia エンティティベクトルからダウンロードした学習済み分散表現を用います。ダウンロード後は普通に「gzip -d file.txt.gz」みたいにターミナル上で解凍します。以下のコードを実行すればすぐに使うことができます。
ただし、KeyedVectors形式のものは白ヤギさんのように再学習ができません。(Why use KeyedVectors instead of a full model?

汗の類似語に関しては、難しい単語が高めに出ているようです。

残念ながら、ベースラインの60.5%には至りませんでした。

Facebookの学習済みFastTextモデル:Gensim

FastTextはGoogleにいたTomas Mikolov氏がFacebookに転職されて作られた分散表現を求めるためのモデルです。Gensimでも呼び出せます。学習済みのものはこちらのGitHub(Pre-trained word vectors)にあるのですが、NEologdで形態素解析したものをベースに学習し公開されている方がいるとのことで、こちら(fastTextの学習済みモデルを公開しました)からダウンロードしたものを使わせていただきました。

何だこれはレベルの結果が返ってきました。中国の歴史上の人物か何かなんでしょうか。

若干ですがベースラインの60.5%よりも良い結果が得られましたが、 誤差の範囲な気がします。

NWJC から取得した単語の分散表現データ (nwjc2vec):Gensim

国立国語研究所の収集されたテキストデータを元に学習した分散表現が提供されています。ただし、利用するためには申請する必要があります。申請が受理されたらこちら(NWJC から取得した単語の分散表現データ (nwjc2vec) を頒布)からダウンロードして使えます。

汗の関連語ですが、うまく関連付けれているように思われます。少なくとも中国史ぽくはありません。しかしながら、顔文字まで学習していたとは。

ベースラインの60.5%よりも1%ポイントほど高い結果となりました。

NNLM embedding trained on Google News:TensorFlow

こちら(tensorflow-hubで超簡単にテキスト分類モデルが作成できる)で紹介されているように、GoogleがTensorFlowでGoogleニュースのテキストをもとに学習した分散表現が提供されています。

こちらのGitHub(NNLM embedding trained on Google News)から、Japaneseのnnlm-ja-dim50、nnlm-ja-dim50-with-normalizationなどが使えます。分散表現の説明についてはこちらのドキュメント(Token based text embedding trained on Japanese Google News 6B corpus.)にあります。

AUCが65%となっているものの、先程のsklearnでのクロスバリデーションのものとの比較ではないので、なんとも言えないですが、Googleニュースのデータだし結構精度が出そうな可能性を感じますね。
今後、TensorFlowでクロスバリデーションによるAUCスコアの出し方を調べてみて、順当に比較できるようにしたいです。(Kerasを使って計算している事例は見つけた。)

比較

今回の分類タスクはそもそも分散表現では精度が出なかったのですが、学習済み分散表現の中で序列を作るとすると、梵天が一番良く、FastTextが少しだけ良かったです。
TensorFlowをほぼ業務で使わないので、Googleニュースの分散表現を今回の比較対象にできなかったのですが、後日比較できるようにしたいと思います。

あと、今回の口コミの点数を当てるタスクよりも、分散表現にとって相性がいいタスクがあるかもしれないので、今回の結果で諦めることなく色々と試して行きたいです。

おわりに

様々なシンポジウムなどでスタンダードとなってきた分散表現ですが、学習済み分散表現をそのまま使って分類問題で役に立つのかを見てきました。残念ながら、口コミの評価予測タスクにおいては全然効果がなさそうでした。ただ、分散表現の中でもタスクによって相性の良い学習済み分散表現がありそうです。
先程も述べたように、理想は大量のテキストデータで学習した分散表現を求め、それを予測に使うことなので大量のテキストデータを集めて再チャレンジしたいです。どれくらいのテキストデータがあれば十分なのかの規模感もわからないので、実践あるのみなんですかね。

参考情報

Word Embeddingだけで文書分類する
tensorflow-hubで超簡単にテキスト分類モデルが作成できる
Error: ”Word2vec’ object has no attribute index2word
Word2vec Tutorial Online training / Resuming training
Word Embeddingモデル再訪
Googleの事前学習済みモデルを手軽に利用出来るTensorFlow Hub
ゼロから作るDeep Learning ❷ ―自然言語処理編

蒙古タンメン中本コーパスに対してのLDAの適用とトピック数の探索

モチベーション

前回の記事では、Webスクレイピングにより入手した、蒙古タンメン中本の口コミデータに関して、Word2Vecを適用した特徴量エンジニアリングの事例を紹介しました。
今回はせっかく興味深いデータがあるので、どのようなトピックがあるのかをLDAを適用したいと思います。加えて、これまで記事で扱ってきたLDAの事例では評価指標であるPerplexityやCoherenceを扱ってこなかったことから、トピック数がどれくらいであるべきなのか、考察も含めて行いたいと思います。以前扱った階層ディリクレ過程であれば、トピック数を事前に決める必要が無いのですが、今回は扱わないものとします。

環境

・MacBook Pro
・Python3.5
・R version 3.4.4

Gensimで行うLDA

今回もPythonのGensimライブラリを用いて行います。

  • パープレキシティ
    • テストデータに対して計算
    • 負の対数尤度で、低いほどよい。
      • パープレキシティが低いと、高い精度で予測できるよい確率モデルと見なされる。汎化能力を表す指標。
      • トピックの数をいくらでも増やせばパープレキシティは下がる傾向が出ている。
      • 教科書でのパープレキシティの事例に関しては、トピック数を増やせば低くなるという傾向が出ている。

以下のコードでパープレキシティを計算します。

実際に、中本コーパスで計算したトピック数に対してのパープレキシティは以下のように推移しました。

Ldaのモデル選択におけるperplexityの評価によると、
”複数のトピック数で比べて、Perplexityが最も低いものを選択する。」という手法は人間にとって有益なモデルを選択するのに全く役に立たない可能性がある。”と記されています。

『トピックモデルによる統計的潜在意味解析』には、”識別問題の特徴量として使う場合は識別問題の評価方法で決定すればよい”とあるので、目的によってはパープレキシティにこだわらなくても良いと思われます。

今回のケースだと、パープレキシティだけだと、決めかねてしまいますね。

  • コヒーレンス
    • トピックごとの単語間類似度の平均
    • トピック全体のコヒーレンスが高ければ、良い学習アルゴリズムとみなす。

以下のコードでコヒーレンスを計算します。