Article Classifier は、フォームに入力された記事 URL から HTML を取得し、記事のカテゴリを判定し、画面に出力するアプリケーションである。
アプリケーション画面の中央にあるフォームに、https://gunosy.com/ から選んだ記事 URL を入力する。
Analyze ボタンを押すと、記事のカテゴリを反映し、画面に出力する。
記事 URL ではない URL を入力すると、例外処理が働く。
以下の画像のように、"Please submit a gunosy article"とエラー文が出る。
記事のカテゴリを判定する際に二種類の分類器を使用することが可能である。一つはナイーブベイズを用いた分類器、もう一つはロジスティック回帰を用いた分類器である。
また、いずれの分類器に対しても、訓練データとテストデータの比は 8:2 で学習を行なっている。
それぞれの分類器の適合率(precision)、再現率(recall)、F 値(f1-score)、テストに使われたデータ数(support)を以下に示す。
また、特定のデータセットに対して過学習を起こしていないことを示すため、交差検定による値も以下に記している。
| category | precision | recall | f1-score | support |
|---|---|---|---|---|
| IT・科学 | 0.79 | 0.94 | 0.86 | 541 |
| おもしろ | 0.75 | 0.15 | 0.25 | 101 |
| エンタメ | 0.97 | 0.94 | 0.96 | 4039 |
| グルメ | 0.87 | 0.95 | 0.91 | 611 |
| コラム | 0.81 | 0.87 | 0.83 | 1155 |
| スポーツ | 0.97 | 0.96 | 0.97 | 827 |
| 国内 | 0.87 | 0.82 | 0.84 | 671 |
| 海外 | 0.84 | 0.86 | 0.85 | 336 |
| avg / total | 0.91 | 0.91 | 0.91 | 8281 |
また、五分割交差検証により得られた値は
scores: [ 0.91063881 0.90882744 0.90725758 0.91038647 0.90736715]
で、平均値は
average value: 0.908895489763
であり、交差検証においては平均で約90.9%の精度を出した。
| category | precision | recall | f1-score | support |
|---|---|---|---|---|
| IT・科学 | 0.90 | 0.95 | 0.92 | 541 |
| おもしろ | 0.80 | 0.69 | 0.74 | 101 |
| エンタメ | 0.98 | 0.98 | 0.98 | 4039 |
| グルメ | 0.93 | 0.96 | 0.94 | 611 |
| コラム | 0.91 | 0.89 | 0.90 | 1155 |
| スポーツ | 0.98 | 0.98 | 0.98 | 827 |
| 国内 | 0.90 | 0.87 | 0.88 | 671 |
| 海外 | 0.88 | 0.91 | 0.89 | 336 |
| avg / total | 0.95 | 0.95 | 0.95 | 8281 |
また、五分割交差検証により得られた値は
scores: [ 0.94046613 0.9364811 0.94119068 0.93538647 0.93913043]
で、平均値は
average value: 0.938530962853
であり、交差検証においては平均で約93.9%の精度を出した。
実行環境は以下の通りです。
Mac OS X: Sierra 10.12.2
Python: 3.6.1
ターミナルにて、
$ brew update
$ brew install python3
$ pip install virtualenv
$ virtualenv --python=/usr/local/bin/python3 --no-site-packages env
$ source env/bin/activate
と入力して、仮想環境を起動する。
次に、
$ brew install mecab
$ brew install mecab-ipadic
$ git clone --depth 1 git@github.com:neologd/mecab-ipadic-neologd.git
$ ./mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -y -n
と入力して、MeCab の辞書として使用する mecab-ipadic-neologd をインストールする。
更に
$ git clone git@github.com:ajingu/gunosy.git
$ cd gunosy
$ pip install -r requirements.txt
と入力し、必要な python パッケージを仮想環境にインストールする。
最後に、.bash_profile に環境変数を描き込む必要がある。 今回は、データベースのパスワード等を隠蔽するために、.bash_profile に環境変数を追加し、os モジュールを使ってプログラム中で環境変数を読み込んでいる。
$ cd ~
$ vim .bash_profile
で.bash_profile を開き、テキスト内に
export GUNOSY_HOST="****"
export GUNOSY_USERNAME="****"
export GUNOSY_PASSWORD="****"
export GUNOSY_DATABASE_NAME="****"
export GUNOSY_TABLE_NAME="****"
と入力し、環境変数を追加する。
それぞれの変数の対応は以下の通りである。各々が使うデータベースを考慮して、ふさわしい値を設定する必要がある。
| 環境変数 | 値 |
|---|---|
| GUNOSY_HOST | ホスト名 |
| GUNOSY_USERNAME | ユーザー名 |
| GUNOSY_PASSWORD | パスワード |
| GUNOSY_DATABASE_NAME | データベース名 |
| GUNOSY_TABLE_NAME | テーブル名 |
※当レポジトリにはデフォルトで学習済みデータが入っているので、最初から$ python manage.py runserverと入力しても動く。
1.データ収集の際に、以前に収集したデータを消したい場合、gunosychallenge レポジトリにて
$ python manage.py initialize
というコマンドを打つことで、該当テーブルの全ての行を消去し、データベースを初期化することができる。
2.scrapy を用いたデータを収集を行う際、gunosychallenge レポジトリにて
$ python manage.py scrapy crawl gunosy
というコマンドを打って行う。データ収集の完了には約 70 分かかり、40000 記事前後のデータを取得する。
3.ナイーブベイズ分類器の学習は、gunosychallenge レポジトリにて
$ python manage.py make_clf nb
というコマンドを打って行う。学習には約 8 分かかる。
4.ウェブアプリを起動する際には、gunosychallenge レポジトリにて
$ python manage.py runserver
というコマンドを打つ。
ローカルサーバーで立ち上げるため、http://127.0.0.1:8000/ にアクセスすると、該当するウェブアプリが起動している。
上記の「概要説明」でも説明したが、中央のフォームに https://gunosy.com/ から選んだ記事 URL を入力し「Analyze」ボタンを押すと、記事のカテゴリを「エンタメ」、「スポーツ」、「おもしろ」、「国内」、「海外」、「コラム」、「IT・科学」、「グルメ」の中から推測し、画面に出力する。
1.ロジスティック回帰を用いた分類器の学習は、gunosychallenge レポジトリにて
$ python manage.py make_clf logistic
というコマンドを打って行う。学習には約 10 分かかる。
2.ウェブアプリの立ち上げと記事 URL の入力・カテゴリの推測は、Step1 と全く同じ方法で行う。
アプリケーションのテストを行うことが可能である。
・gunosy レポジトリにて
$ python gunosynews/scrapy_test.py
と入力すると、クローラーのテストを行うことができる。
・gunosychallenge レポジトリにて
$ python manage.py test
と入力すると、ウェブアプリのテストを行うことができる。
・可能な限り多くの記事の取得
該当箇所 : gunosy.py
学習・テストの際になるべく多くの記事を使うために、https://gunosy.com/tags から記事を収集した。
タグは現時点で 1~2500 の 2500 個存在し、それぞれのタグが「エンタメ」、「スポーツ」、「おもしろ」、「国内」、「海外」、「コラム」、「IT・科学」、「グルメ」の 8 つのカテゴリに割り振られている。
しかし、タグの中には、カテゴリに割り振られていないものやタグだけ存在して記事が存在しないものもある。そのようなイレギュラーなタグは無視する実装を行なった。
・pipelines.py に Mysql を設定
該当箇所 : pipelines.py
pipelines.py に、django アプリと紐付けた Mysql を設定することで、データ収集からデータベースへのアップロードまでの流れがスムーズに行われるようにした。
該当箇所 : preprocess.py
日本語の形態素解析器 MeCab を使って形態素解析を行ったが、その際に辞書として mecab-ipadic-neologd を使用した。
これによって、人物名や地名などの固有名詞が多いニュース記事から、より適切な特徴語を抽出することができている。また、特徴語は名詞と形容詞に限定し、話の文脈との関連性がより高い言葉を抽出した。
また、構築環境によって辞書の位置を指定するパスが変わるため、データを前加工する際に最初に辞書のパスを検索するように実装した。
該当箇所 : preprocess.py
日本語のストップワードを集めたslothlib をプログラム中で読み込む実装を行い、ストップワードを設定した。
該当箇所 : NaiveBayes.py, Logistic.py
分類器の学習の際に、学習した分類器を dill ライブラリを使ってシリアライズしている。
そのため、ウェブアプリで記事 URL を入力した際、すでに作った分類器を読み込むだけで新記事の解析が可能になるため、記事のカテゴリ判別にかかる時間を大幅に短縮した。
該当箇所 : NaiveBayes.py
ナイーブベイズ法における「ゼロ頻度問題」(あるカテゴリに学習時に含まれなかった単語がテスト文書に含まれていると、そのカテゴリである確率が 0 になってしまう問題)を回避するため、ラプラススムージングを実装した。
該当箇所 : Logistic.py
ナイーブベイズ法では、それぞれの単語の現れる事象は互いに独立であると前提して、単語の条件付き確率を掛け合わせている。これだと先ほどあげた「ゼロ頻度問題」のために、学習時に存在しなかった単語に結果を左右されやすい。 そのため、学習時に存在する単語のみに着目して計算を行う(学習時に存在しなかった特徴語に関しては、ロジスティック関数への入力は 0 となり実質的に影響を与えない)モデルであり、カテゴリ判別に広く使われるロジスティック回帰モデルを今回は使用した。
単語ごとの TF-IDF を計算して、それぞれの単語に対して適切な重み付けを行なった。
それぞれのカテゴリーのサンプル数に大幅な違いがある事が原因で、ナイーブベイズ分類器の時には「おもしろ」カテゴリーの再現率が0.15と大変低い数値になっている。これは「おもしろ」カテゴリーのサンプル数が他カテゴリーに比べて非常に少ないことが理由で、実際には「おもしろ」カテゴリーである記事が他カテゴリーであると推測される場合が多くなるため、偽陰性が高くなっていると考えられる。
このようなサンプル数によるカテゴリー判別の偏りを軽減するために、LogisticRegression モデルの重み付けパラメータである class_weight を"balanced"に設定し、各カテゴリーでの特徴語の重みをサンプル数に反比例させる事で、「おもしろ」カテゴリーの再現率を 50 ポイント以上改善する事ができた。
なお、データをアンダーサンプリングしてそれぞれのカテゴリのデータ数をそろえる方法も考慮したが、その場合データ数が全部で約 4000 となってサンプル数が激減し、精度が急激に下がるので、今回は class_weight を設定する手法をとった。
LogisticRegression モデルの正則化のパラメータである C の値を最適化するために、GridSearch を使用した。
該当箇所 : views.py
https://gunosy.com/ の記事 URL ではない URL を入力すると、HTML 構造を把握できず、ウェブアプリの画面ではなく、django のエラー画面が出力されてしまう。
そのため、あらかじめ例外処理を書いておき、不適当な URL が入力された場合には、ウェブアプリの画面にエラー文を出力するように設定した。
該当箇所 : settings.py(gunosychallenge ディレクトリ), database.py, pipelines.py ~/.bash_profile に環境変数を設定することで、Mysql のパスワードの公開を避けた。


