ポエム判別器

# ポエム判別器

Qiita初投稿です。よろしくお願いします。
Qiitaは主に読む方で使わせていただいているんですが、統計ヤクザとか見たことがあるので、正直コメント貰っても返すの怖いし（twitterで受けつけます）、Qiitaはいいかなって思っていました。

しかし、今回はQiitaそのものを評価の対象とするので、Qiitaに投稿したいと思います。

Qiitaでは、主観や感情が入り乱れる投稿は嫌われる傾向があり、負のイメージを持ってポエムと言われることがあります。

それを機械学習で分類していれければいいね、ということで、やってきます。

## 手法選択
考えられるメソッド

- Bag of Wordsなどでlogistic-regression
- Bag of wordsなどでsvm
- Bag of wordsなどでxgboost
- doc2vec, fasttextなどのエンベッディングでテキスト分類
- Reccurent Neural Networkで分類
- Convolutional Neural NetworkでTerm粒度で分類
- Convolutional Neural NetworkでChar粒度で分類

各方面のSNSの分類の専門家によると、Char粒度のConvolutional Neural Networkがもっとも良さそうですが、解釈性の問題をはらんでおり、一般的にディープな手法は何故結果がそうなったのか判断が困難です。
いずれの手法もやったことがあるのですが、伝統と歴史のあるBag of Words(BoW) + Logistic-regression またはxgboostでいいと思います。その他手法が見たい方は、わたしのブログ等に記してあるのでよろしければ。

## 実験計画

### ポエムとはどういった文章なのか定義せよ 
  1. Qiitaでは、閲覧者に「これはポエムだ...」と言われることがある  
その評価は、はてなブックマークのコメント欄や、Qittaのコメント欄で確認できるが、取得が困難　
  2. 投稿者自らが「ポエム」のタグを付ける  
もっとも現実的な方法なので、これでいければいい。しかし、ポエムのタグをカウントしたところ、1%に満たない量しかないことがわかった。（明らかにもっとあるよね？消された？）

1,2を評価したところ、2のほうが現実性がありそうということで、2を採用しました。

### Scraperを回す
機械学習エンジニアにとってスクレイピングは基本技能であり、複雑でJavaScriptを多用したサイトでもスクレイピングできるべきです。
私がよく使用する方法は[selenium + phantomjs + python3(concurrency)](https://github.com/GINK03/poetty/blob/master/qiita.the.judge.py)の組みあわせで、コンテンツを一網打尽に取得してしまう方法ですが、行儀が悪いアクセスをするとブロックされたり下手をすると警察沙汰になったりするので、一つのドメインに対して秒間一アクセスが上限と覚えておくと良いと思います。

### 形態素解析
分かち書きにする必要があるので、[MeCab](http://taku910.github.io/mecab/)という形態素解析エンジンを用いて形態素解析します。
しかし、作られたのはだいぶ古く、このエンジンに入っている単語は非常に古いですので、[NEologd](https://github.com/neologd/mecab-ipadic-neologd)というプロジェクトで最新の辞書を組み込む必要があります。

```python
$ echo "Fate/Grand Order" | mecab
Fate/Grand Order        名詞,固有名詞,一般,*,*,*,Fate/Grand Order,フェイトグランドオーダー,フェイトグランドオーダー
EOS
```

### tf-idfのウェイティング
なぜか国内ではレレバンシーを取るのによく使われますが、重要語の強調などに使うことができます。
idf辞書があるとより高精度で動作するということだけなので、必須でないですが、あったほうが良いです。

### 学習ソフト
scikit-learnか、生liblinearを利用しようか思いましたが、次元数が多すぎると、pythonのnumpyのarrayが不安的になることが多いので、自然言語処理では生liblinearか生xgboostを使うことが多いです。

## 実験と評価
ポエムがついた520投稿を+1(ポジティブ)として、付いてない投稿をランダムで520件選んで0(ネガティブ)としました。
この内、800件を学習用データセットとして、logistic-regressionで学習、240件を評価しました。

```sh
(学習)
$ ./train -s 0 head.svm
...
iter 35 act 1.645e+00 pre 1.815e+00 delta 2.142e-01 f 1.024e+01 |g| 1.661e+02 CG  11
iter 36 act 4.756e-01 pre 3.971e-01 delta 2.142e-01 f 8.597e+00 |g| 6.762e+02 CG   2
iter 37 act 5.797e-01 pre 4.606e-01 delta 2.142e-01 f 8.121e+00 |g| 2.020e+02 CG   7
```

```sh
（評価）
$ ./predict -b 1 tail.svm head.svm.model result
Accuracy = 86% 
```
まずまずの精度が出たかと思います。ちなみに今回の使用次元数は756549次元でウルトラ高次元＆スパース構造だったので、こうなってくると人間があれこれ考えてルールベースで解くより、機械学習でちゃちゃっと済ませてしまうべきです。

## logistic-regressionの確率表現
こいつの便利なところは、predictした際にどれくらいの確率（と認識できる）でその事象になるか、表現できることです。
前のresultで出力したファイルの中を見ると、このような表現になっております。

```sh
labels 0 1
0 0.999995 4.97678e-06
1 0.0172321 0.982768
0 0.999301 0.00069908
```
小数点が確率表現ですね。
多分ですがIBM bluemixの性格判断もこれと同じ理論で、BoWで予想して連続値で表示するとあったので、同等のものだと思います。IBMを超えていけ(?)

## まとめ
 - Qiitaでは退社報告が書かれたり、一方的な上から発言があったり、読む人を不快にさせるコンテンツも多く含まれていて、そっ閉じしています。Qiitaの潜在的なSEOの強さも相まって、素晴らしいコンテンツも表示されるのですが、もうやめてくれっていうのも多く表示されるので今回これを行ってみました。（Chrome拡張でタイトルだけでフィルタするのは難しくないように思う）
 - CNNの方が精度出ましたけど、今回は素性（単語）の重要度の解釈性を高めたかったのでlogistic-regressionを用いました。
 - コードはアドホックに書き連ねていたので、再利用性はほとんどないと思いますが、一応公開しておきます。

## appendix
- コード: https://github.com/GINK03/poetty
- blog: http://catindog.hatenablog.com/
- twitter: @nardtree