fasttextでwebサイト分類したかった

[Wano株式会社](https://wano.co.jp/)のfushimiです。
先日、弊社の2020年の開発合宿(一泊二日)でやったネタを投稿します。  

合宿日記も兼ねてるので完全にとりとめのない時系列順の出来事の羅列になりますが、ご容赦ください。


# 未知のwebサイトの自動カテゴリ分けをしよう
自然言語処理や機械学習は全然わからん勢なのですが、せっかくの合宿なのでwebサイトのコンテンツ解析をして遊んでみました。

作るものとしては表題通り、**webサイトのカテゴリ分類器** です。

あらかじめ決めたカテゴリ分類に応じて、入力された未知のWebサイトが適切にカテゴライズされることを目的とします。


## アプローチ

### 学習
* カテゴリごとのメディア記事をクロールして大量に集める
* 記事をきれいにする
* 記事を形態素解析する
* fasttextで学習済みモデルを作る

### テスト
* 入力されたwebサイトのコンテンツをいくつか集める
* コンテンツをきれいにする
* コンテンツを形態素解析する
* 学習済みモデルに食わせて思った通りにカテゴリ分けできたら成功

## fasttext

AWSのML系サービスで遊んでみることも考えましたが、今回はfasttextによる単語ベクトルの算出というアプローチをとってみました。

fasttextは、facebook製の自然言語処理ライブラリです。word2vecと同じく単語ベクトルを算出するライブラリと理解しました。
[word2vec（Skip-Gram Model）の仕組みを恐らく日本一簡潔にまとめてみたつもり](https://www.randpy.tokyo/entry/word2vec_skip_gram_model)

## 環境構築

以下のようなDockerfileを構築しました。
今回は慣れてるgoとdockerで作業を始めてしまったので、その影響でいろいろ入っています。

```
FROM ubuntu:18.04

ENV HOME /root
WORKDIR $HOME
ENV DEBIAN_FRONTEND noninteractive
SHELL ["/bin/bash", "-c" ]

RUN apt-get update && \
    apt-get -y install wget python3 python3-pip curl groff  gcc make cmake g++ openssl git tree  ca-certificates --no-install-recommends unzip
#build-essential
RUN echo "stty rows 50 cols 200" >> ~/.bashrc

RUN wget https://github.com/facebookresearch/fastText/archive/v0.9.1.zip
RUN unzip v0.9.1.zip
RUN cd fastText-0.9.1 && make && cp  ./fasttext $HOME/
RUN cp $HOME/fasttext /usr/bin/ &&  which fasttext

WORKDIR $HOME

# mecab
RUN apt-get install -y mecab libmecab-dev mecab-ipadic mecab-ipadic-utf8

# 辞書
RUN apt-get install  -y xz-utils patch file sudo
RUN cd $HOME && git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
RUN cd mecab-ipadic-neologd && ls && ./bin/install-mecab-ipadic-neologd -n -y
RUN echo `mecab-config --dicdir`"/mecab-ipadic-neologd"

#### any env
RUN git clone https://github.com/anyenv/anyenv $HOME/.anyenv && \
    echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >>  $HOME/.bashrc && \
    echo 'eval "$(anyenv init -)"' >>  $HOME/.bashrc
ENV ANYENV_HOME $HOME/.anyenv
ENV ANYENV_ENV  $ANYENV_HOME/envs
ENV PATH $ANYENV_HOME/bin:$PATH

ENV ANYENV_DEFINITION_ROOT $HOME/.config/anyenv/anyenv-install
RUN mkdir -p $ANYENV_DEFINITION_ROOT && \
    git clone https://github.com/anyenv/anyenv-install $ANYENV_DEFINITION_ROOT &&\
    which anyenv && ls $ANYENV_DEFINITION_ROOT


### go
ENV GO111MODULE on
RUN anyenv install goenv
ENV PATH $ANYENV_ENV/goenv/bin:$ANYENV_ENV/goenv/shims:$PATH
ENV GOENV_ROOT $ANYENV_ENV/goenv
ENV GOPATH /root/go
RUN goenv install 1.14.0 && \
    goenv global 1.14.0 && \
    goenv rehash && \
    echo 'eval "$(goenv init -)"' >> ~/.bashrc
ENV PATH $PATH:/usr/local/go/bin:$GOPATH/bin
ENV GOBIN $GOROOT/bin
RUN echo 'export GOBIN=$GOROOT/bin' >> ~/.bashrc
RUN echo 'export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin' >> ~/.bashrc


##### aws-cli
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" &&  \
    unzip awscliv2.zip && \
    ./aws/install


#### 文字コードの設定
ENV LANG=C.UTF-8
ENV LANGUAGE=en_US:


##### after build
RUN apt-get clean


```

## 学習
### 学習元となるメディアカテゴリを決めよう

そもそもはfasttextを選んだのは、[機械学習で大量のテキストをカテゴリ別に分類してみよう！](http://tech.wonderpla.net/entry/2017/10/10/110000) を見ていて、「おもしろそう!」と思ったからです。なので先にカテゴリーを決めておくというアイディアもここから来ています。

まず、分類するメディアのカテゴリを以下のように決定します。

* IT系
* 医療系
* ママ/子育て系
* 金融系
* 音楽系

粒度がバラバラじゃん!って話なのですが、個人的にサンプルが思いつきそうなメディアがざっくりこのへんだったのでこのままいきます(笑)

### カテゴリごとの記事を集めよう

![Artboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85375/67acebd8-8e39-e31d-d208-9e8b48ff945c.png)


カテゴリごとに7-10サイト、aタグで同ドメインのものを漁って各100-300ページくらい集めることにしました。
学習対象としてはドメインレベルで専門情報を扱ってるサイト(ex . ongaku.news.jp) のみにして、複数のカテゴリ記事を持っている統合情報メディアみたいのは今回は使いません。
aタグベースで関連記事を漁っていくのに当たり、いろんなカテゴリの記事があると数時間で学習元として使うには大変そうだったからですね。

ちなみにGoで書いてます。goroutineはすごい。


### 集めた記事をきれいにしよう

合宿一日目の夕方くらいから方針決めてはじめてみたのはいいんですが、当たり前ですが**そもそもこの作業自体が大変でした...**

* 大変だったこと
 * SJISのサイト -> utf8にする
 * scriptだのstyleだのiframeだのいらないタグを排除する
 * 排除するにしてもDOMのセレクタなんか忘れたわ...  
* goroutineはすごい

このように、「きれいな学習用データを集める」という作業だけで一日目の深夜までいってしまいました。
このへんの質があとで響いてきます...。


### ちょっと記事に重み付け

本文のみでなく、いくつかのタグは「そのサイトにとって大事なもの」とみなして、抽出分をさらに記事にくっつけて強化しています。
どこぞのRTBのブログでこれをコンテンツ解析に使ってるっていってたので。

* title
* meta[name=description]
* meta[name=keywords]


### 形態素解析する
おなじみのmecabを使って形態素解析をしました。
辞書データとして[mecab-ipadic-neologd](https://github.com/neologd/mecab-ipadic-neologd.git)のお世話になりました。
そのままだと余計な品詞でゴミがのりそうだったので、**試しに名詞形の影響力を強くしています。** (といっても複数回くっつけ直しただけ)

goで作業してたので[mecabバインディング](https://github.com/bluele/mecab-golang)を使いましたが、やはりこの手のツールはpythonが一番豊富でしたね...


ここまでの作業で作ったファイルがこんな感じの1枚のテキストです。
(108MBほどになりました)


* IT系 => __label_it
* 医療系 => __label_medical
* ママ/子育て系 => __label_mama
* 金融系 => __label_money
* 音楽系 => __label_music

としてラベリングしています。

```txt:src.txt
__label__music, All All Digital Digital Music Music と は 「 All All Digital Digital Music Music 」 は 、 世界 世界 最先端 最先端 の 音楽 音楽 ビジネス ビジネス と エンタテインメント・テクノロジー エンタテインメント・テクノロジー にで は 、 インディペンデント インディペンデント な アーティスト アーティスト や 作曲 家 、 プロデューサー プロデューサー 、 音楽 音楽 クリエイター クリエイター から 、 レコード レコード 会社 会社 を はじめ と する 音楽 音楽 業界 業界場 分析 、 音楽 音楽 ビジネス ビジネス に 影響 を 与える 戦略 戦略 や アイデア アイデア 、 未来 未来 に 向け て 考える べき ビジネス ビジネス モデル モデル や マーケティング マーケティング 、 テクノロジー テクノロジー 、 データ デー続 的 な

....
__label__mama, 妊娠 が 成立 する と  体 に 様々 な 変化 が 現れ ます が 、 その 代表 的 な もの が 「 つわり つわり 」 です 。 吐き気 吐き気 や 眠気 眠気 、 頭痛 頭痛 など 様々 な 不快 症状 症状 が 現れる ので 、 妊娠 初期 初期 は妊婦 さん にとって は 本当に 辛い 時期 です ね 。 気持ち 気持ち 悪く て 何 も 食べ たく なくなる かも しれ ませ ん 。 しかし 、 つわり つわり の 症状 症状 を 軽減 し て くれる 効果 効果 が 期待 できる 栄養素 栄養素 も ある ので 、  妊娠 する
```


### fasttextで学習済みモデルを作る

以上で作ったsrc.txtからおもむろに学習済みモデルを作ります。

```
fasttext supervised -input src.txt -output output.model
```
``` output.model.bin ``` と　``` output.model.vec ``` が生成されました。


## できあがったモデルで遊ぶ

取得してきた「未知の」webサイトに対して何ページかのスクレイプと「きれいにする」までの作業をほどこし、1枚のテキストにします。

ここでは弊社の開発者ブログを「未知の」Webサイトとし、wano.txtを生成しました。

```wano.txt
Wano Wano Group Group Developers Developers Blog Blog Wano Wano グループ グループ エンジニア エンジニア による 開発 ブログ ブログ New New Posts Posts 1 … About About Wano Wano グループ グループ の エンジニア エンジニア や デザイナー デザイナー が 使っ て いる 技術 技術 、 興味 興味 の ある 技術 技術 、 ( 色んな 意味 で ) はまっ て いる 技術 技術 など を お伝え し て いき ます 。 KeywordsWano KeywordsWano Group Group Developers Developers Blog Blog ｜ Wano Wano グループ グループ エンジニア エンジニア による 開発 ブログ ブログ Wano Wano Group Group Developers Developers Blog Blog ｜ Wano Wano グループ グループ エンジニア 
...
```

学習済みモデルに喰わせます。

```
fasttext predict-prob output.model.bin /wano.txt 5
```

```
__label__it, 0.761388 
__label__music, 0.11441 
__label__mama, 0.059265 
__label__money, 0.0472763 
__label__medical, 0.0177098
```
ITというカテゴリワードがドンピシャだったせいか、``` __label__it ``` (ITカテゴリ)のスコアが一番高く反映されました。いいかんじですね。


## 感想/課題つらつら

もちろん判定がうまくいかないサイトもあって、やはりスクレイプとカテゴリ分けの健全度が全て...と言う感想でした。
合宿の深夜でどんなソースをいれても金融メディア判定になることがあって、スクレイプを見直す羽目に。
ただ、付け焼き刃のアプローチでもこのようになかなかおもしろい分類器ができあがったので、もうすこし深めてみたいと考えています。