Apitore blog

Apitoreを運営していた元起業家のブログ

【API】日本語極性判定を作って公開した~SentencePiece版~

はじめに

ApitoreではWord2Vec+RNN+LSTMによる極性判定WebAPIを公開しています。そして先日、Googleの工藤さんが作ったSentencePieceのWebAPIも公開しました。今回は、この2つを組み合わせてみたいと思います。SentencePiece版の極性判定技術です。

デモサイト

サンプルコード

※以前の極性判定WebAPIのプログラムで、エンドポイントを変えれば動きます

解説

アルゴリズムを箇条書きにします。

  1. Tweet版SentencePiece WebAPIで形態素解析っぽく分割
  2. SentencePiece版のWord2Vecで200次元のベクトルに変換
  3. RNN+LSTMでpositive/negative/neutralの3値に分類

Tweet版のSentencePieceについて

技術内容というか実装としか、そういうのは以前ブログでも解説しました。Google工藤さんによると、SentecePieceはドメイン依存性が高いそうなのでTweet版SentencePiece WebAPIを新たに作りました。学習設定はWikipedia版のときと一緒で、出力は8000語です。 ツイートは以下の条件で集めました。だいたい200万ツイートです。

  • 任意の目的語(人手で作成した約2500語)を含むツイート
  • 20文字以上
  • URLは含まない。リツイートは含まない。
  • 投稿クライアントはTwitter公式(web、Android、iOS)

SentencePiece単語をWord2Vecで変換

SentencePieceの出力8000語でOne-Hotベクトルを作ってRNN+LSTMで学習させたかったのですが、私のPC(メモリ16GB)ではOutOfMemoryになってしまって実行できませんでした。フレームワークはdeeplearning4jの最新版を使っているのですが、もしかしたらTensorFlow/Kerasなら実行できるのかもしれません。ApitoreはJavaでWebAPIを作っている関係でdeeplearning4jを使いました(近いうちにPythonでWebAPI作れるようにして、現状システムと統合できるようにします)。 仕方がないので、いったんWord2Vecを挟むことにします。SentencePieceの学習に使った約200万ツイートにSentencePieceを適用し、Word2Vecに流しました。Word2Vecの実行方法や設定は以前ブログで紹介しました。今回の設定は以下に示します。 Word2Vecの設定

  • skip-gram
  • 次元数 200
  • window size 5
  • 階層化ソフトマックス なし
  • サンプリング 1e-3
  • ネガティブサンプリング 5
  • iteration 5
  • 最小出現数 0

SentencePieceのVocabularyは8000語なのですが、200万ツイートのSentencePiece適用結果は7600語でした。

RNN+LSTMでpositive/negative/neutralの3値に分類

学習データやアルゴリズムは以前の極性判定で使ったものとほぼ同じです。Optimizerとかパラメータは多少変えましたが、相変わらずパラメータチューニングは厳密にはやっていません。あと、本日投稿しているもう一つの記事で紹介した方法でNeutralの学習データは補強してあります。 学習データ

positive negative neutral
18,462 98,457 75,660

評価データ

positive negative neutral
9,164 33,915 25,139

性能

性能は以下になります。ただし、以下の2点がありますので参考情報です。

  1. Distant Supervisionで集めたデータが大半である
  2. 学習データ≠評価データだが、評価データのスコアが最大になるように学習している

SentencePiece版の極性判定の性能

Examples labeled as 0 classified by model as 0: 7401 times
Examples labeled as 0 classified by model as 1: 520 times
Examples labeled as 0 classified by model as 2: 1243 times
Examples labeled as 1 classified by model as 0: 881 times
Examples labeled as 1 classified by model as 1: 31534 times
Examples labeled as 1 classified by model as 2: 1500 times
Examples labeled as 2 classified by model as 0: 1005 times
Examples labeled as 2 classified by model as 1: 568 times
Examples labeled as 2 classified by model as 2: 23566 times
 Accuracy:        0.9162
 Precision:       0.8864
 Recall:          0.8916
 F1 Score:        0.889

kuromoji版の極性判定(以前公開したやつ)の性能

Examples labeled as 0 classified by model as 0: 7048 times
Examples labeled as 0 classified by model as 1: 759 times
Examples labeled as 0 classified by model as 2: 1822 times
Examples labeled as 1 classified by model as 0: 2090 times
Examples labeled as 1 classified by model as 1: 28438 times
Examples labeled as 1 classified by model as 2: 3045 times
Examples labeled as 2 classified by model as 0: 1101 times
Examples labeled as 2 classified by model as 1: 1015 times
Examples labeled as 2 classified by model as 2: 23805 times
 Accuracy:        0.8578
 Precision:       0.82
 Recall:          0.8325
 F1 Score:        0.8262

パット見た感じだと以前公開した極性判定よりも性能が上がっているかのように見えます。が、実際に使った感じだと五分五分で、むしろ以前公開した極性判定の方が性能が良い印象があります。 ※よく見ると、学習データの母体は同じものを使っているはずなのにデータ数が違います。コレガワカラナイ。たぶん、全部OOVになるデータは除外したとか(過去の賢い私が)やったんだと思います。

実際に使ってみた

色々と試してみましたが、なんかNeutralの分類が多いような・・・。あと、興味深い結果出ました笑 以下の例。合ってる。

  "text": "悲しみに暮れる ",
  "predict": {
    "sentiment": "negative",
    "score": 0.9625707864761353
  },

句点(。)ってポジティブ要素なの?

  "text": "悲しみに暮れる。",
  "predict": {
    "sentiment": "negative",
    "score": 0.5054083466529846
  },

繰り返すとニュートラルに!?

  "text": "悲しみに暮れる。悲しみに暮れる。",
  "predict": {
    "sentiment": "neutral",
    "score": 0.9570062756538391
  },

繰り返しすぎて楽しくなってきた!?

  "text": "悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。悲しみに暮れる。",
  "predict": {
    "sentiment": "positive",
    "score": 0.839227557182312
  },

あくまで一例で、全部がこういう傾向にあるわけじゃないです(「好き」とか「失敗した」とかだったら繰り返してもちゃんと出力します)。印象としてはほぼほぼニュートラルに分類してしまいますね。うーん、改善の余地ありです。

おわりに

SentencePieceを極性判定に適用してみました。本当はOne-HotでRNNやりたかったんですが、メモリ足りなくて断念。 今回やった感想としては、kuromojiなどの形態素解析技術を用いる場合と比べ、SentencePieceは語彙数を強制的に抑えられます。というわけで、未知語だろうとなんだろうととりあえず機械学習に入力できてしまいます。このあたりが極性判定というタスクにおいてプラスに働くのかマイナスに働くのかは一概には言えないですが、むずかしいところですね。ツイートなんかを対象にする場合、文章はその時代ごとに変わってくるのでSentencePieceもWord2Vecも定期的にモデルを更新してやらないといけないかなと思います。 あと、今回の場合はDistant Supervisionがベースとなっているので、どうしても顔文字を過学習してしまいます(今回の実験では顔文字はある程度フィルタリングしました)。Distant Supervisionをやるときは気をつけないといけないポイントですね。学習データも適当にサンプリングして目を通してみましたが、うーんって感じです。間違いが結構多い。Twitter社の極性判定結果はあまり使えないかもしれないですね。別の顔文字でDistant Supervisionした方が良いかもしれません。

関連情報

極性辞書(レファレンス)

  1. 小林のぞみ,乾健太郎,松本裕治,立石健二,福島俊一. 意見抽出のための評価表現の収集. 自然言語処理,Vol.12, No.3, pp.203-222, 2005. / Nozomi Kobayashi, Kentaro Inui, Yuji Matsumoto, Kenji Tateishi. Collecting Evaluative Expressions for Opinion Extraction, Journal of Natural Language Processing 12(3), 203-222, 2005.
  2. 東山昌彦, 乾健太郎, 松本裕治, 述語の選択選好性に着目した名詞評価極性の獲得, 言語処理学会第14回年次大会論文集, pp.584-587, 2008. / Masahiko Higashiyama, Kentaro Inui, Yuji Matsumoto. Learning Sentiment of Nouns from Selectional Preferences of Verbs and Adjectives, Proceedings of the 14th Annual Meeting of the Association for Natural Language Processing, pp.584-587, 2008.