BERT多言語モデルで日本語文章の二値分類を試す

BERTの多言語モデルを用いて二値分類を試してみました。既に[先駆者がいらっしゃいます](https://techblog.nhn-techorus.com/archives/12978)ので、二番煎じになります。

# ソースコード

[https://github.com/knok/exp-ml-bert](https://github.com/knok/exp-ml-bert)に用意してあります。

READMEにも一通りの説明は記載しました。

## 対象データ

3種類のデータセットで比較、検討しました。

1. [ライブドアニュースコーパス](https://www.rondhuit.com/download.html)のうち、「ITライフハック」と「家電チャンネル」の記事
2. 同じくライブドアニュースコーパスのうち、「Sports Watch」と「Peachy」の記事
3. [https://www.aozora.gr.jp/](青空文庫)で公開されている、「芥川龍之介」の著作と「宮沢賢治」の著作

### 下処理

[0-setup.ipynb](https://github.com/knok/exp-ml-bert/blob/master/0-setup.ipynb)を実行することで、以下の処理を行います。

* ライブドアニュースコーパスのダウンロード
* CoLAデータセットに合わせたtsvへの変換
  * 不要な文字列の除去
* データのシャッフル、分割
* BERTのソースコード取得
* BERT多言語事前訓練モデルの取得と展開

これにより、1で示したデータの下準備を完了します。他2つのデータセットについては、以下を実行してください。

* [0.1-setup.ipynb](https://github.com/knok/exp-ml-bert/blob/master/0.1-setup.ipynb)
* [aozora/0-preprocess.ipynb](https://github.com/knok/exp-ml-bert/blob/master/aozora/0-preprosess.ipynb)

## 訓練と評価

[run_cola.sh](https://github.com/knok/exp-ml-bert/blob/master/run_cola.sh)スクリプトを実行することで、1番目のデータセットに対する訓練と評価を行います。

### トークナイザの出力例

```
INFO:tensorflow:tokens: [CLS] ハ ##イ ##オ ##ニア ##から ##3 ##d 映 像 に 対 応 した 簡 単 操 作 の ##フル ##ー ##レイ ##ティ ##ス ##ク ##フ ##レー ##ヤー 新 発 売 [SEP]
```

モデルに含まれるvocab.txtにある文字列単位でトークナイズされます。

#### WordPieceについて

改めて論文[[1609.08144] Google's Neural Machine Translation System: Bridging the Gap between Human and Machine Translation](https://arxiv.org/abs/1609.08144)上でのWordPieceに関する説明を読んでみました。
もととなる考え方は[Japanese and Korean Voice Search – Google AI](https://ai.google/research/pubs/pub37842)にあるとのことですが、このときはWordPieceという名称は使われていませんでした。

トークンを単語単位より細かくすることで、低頻度語は短いトークンの組み合わせによって表現できるという考え方です。コーパスから言語モデルを構築し、尤度が最大化されるよう訓練したモデルでトークン分割を行うのだと論文にはあります。
ただ、BERT内でのトークナイザの実装はもっと単純で、サブワードの探索は最長一致によって決定されます。空白、句読点などで区切られたトークン中、"##"から始まるサブワードで最も長いものを順に探索し、トークンをサブワードの集合に置き換えています。

自前のコーパスでBERTの事前訓練をした場合、既にWordPieceに基づいた語彙表を必要としています。残念ながら、WordPieceの実装自体はGoogle内部にしかない([SentencePieceのREADMEより](https://github.com/google/sentencepiece)とのことなので、WordPiceの語彙表を生成するためには頑張って自力で実装するしかなさそうです。
あるいはBERTのトークナイザを修正して、SentencePieceを扱えるようにするという方法が考えられます。こちらのほうは簡単に実現できそうです。

##### 濁点の扱い

これ以降に示した例でもわかりますが、BERTのトークナイザはかな文字の濁点を除去してしまいます。これは以下のような処理によって行われます。

* BasicTokenizer内、_run_strip_accentsメソッドにて 
 1. unicodedata.normalize("NFD", text)により文字列はNormalization Form Canonical Decompressionに変換される (参考: [Mac OS X の NFD 問題での対策諸々 - Qiita])(https://qiita.com/knaka/items/48e1799b56d520af6a09)
 2. その後の処理で、unicodedata.category(char)によってカテゴリーが"Mn"(Mark, Nonspacing)と判定された文字を削除する

日本語における濁点がこれに該当するため、削除されてしまいます。現状、これは仕様として受け入れるしかありません。

### 評価結果

各データセットとモデル、訓練条件に対するdev.tsvでのaccuracyを以下の表に示します。

|                        |LDCC(IT/家電)|LDCC(Sports/Peachy)|青空文庫|
|------------------------|------------|-------------------|------|
|BERT epoch 3            |0.819       |0.955|0.986|
|BERT epoch 3(事前訓練なし)|0.463       |0.472|0.978|
|BERT epoch 10           |0.881       |0.966|0.991|
|BERT epoch 10(事前訓練なし)|0.785      |0.944|0.985|
|TfidfVec+MultinominalNB |0.757       |0.781|0.886|



## 感想

もとのデータのドメインが割と近いライブドアニュースコーパスの「ITライフハック」と「家電ウォッチ」では、事前訓練の有無が大きく影響しています。事前訓練がないと、10 epoch学習させてもTfidf/MultinominalNBより若干マシな結果しか得られません。

もっと違いの明確な「Sports Watch」と「Peachy」で分類器の訓練をした場合は、より高いスコアを得られました。しかし、10 epochの学習であれば事前訓練がなくともかなり良い結果を得られています。これは12レイヤーtransformerによる構造が持つ強力さを示しているように思います。

コメントで指摘のあった、本来のCoLAタスクに近いと思われる、異なる2人の著者による著作の本文をデータセットとした分類タスクでは、前者2つのタスクよりもさらに高い結果を得られました。
CoLAに近いタスクであるから高い結果が得られたとも考えられますが、事前訓練なしのモデルやナイーブベイズ手法でも高い結果が出ているので、実際には一般的な分類タスクとして特徴がはっきりしているデータを対象にしたので高い精度が出た、という感触を持っています。

分類タスクと事前訓練の関係についての論文として、[Semi-supervised Sentiment Classification with Dialog Data]( https://www.jstage.jst.go.jp/article/pjsai/JSAI2018/0/JSAI2018_4G204/_article/-char/ja/)があります。
こちらではseq2seqで対話データを学習させ、そのパラメータを転移学習して感情分析に適用することで精度を向上させたとのことです。
今回行っていることは割とこれに近い気がします。

### テストデータによる検討

1番めの評価データ(test.tsv)に対し推論を行うスクリプトも用意して[pred_cola.sh](https://github.com/knok/exp-ml-bert/blob/master/pred_cola.sh)、実際の推論結果を検討してみました。
その一部を以下に示します。

```
0.97653985	0.023460178	0	何かとネットが騒がしかった！　不審なAndroidアプリ騒動までを振り返る
0.9854231	0.014576941	0	一連の流れを紹介！ Google playで出回った不審なAndroidアプリ問題
0.01633481	0.98366517	1	連載●After Effects 天国への階段 第9回
0.98625237	0.013747647	0	Mac必須アプリの使いこなし術！パソコンの最新情報をチェック
0.02397995	0.97602004	1	任天堂「Ｗｉｉ　Ｕ」は年末商戦期に発売決定　−　販売エリアは日米欧豪
0.9603782	0.0396218	0	スマホもバッテリーパックもコレ1台でOK！欲張りなマルチ充電器
0.026460296	0.9735397	1	元旦深夜０時に「ミラーファイト」新作がYouTubeで公開！
0.44018716	0.5598128	0	ストップ違法コピー！ BSA世界ソフトウェア違法コピー調査2011の結果発表
0.9839643	0.016035693	0	必要なファイルを素早く取り出すマル秘ワザ
0.013715204	0.98628485	1	スタイリッシュな男性と、きらめく女性向け！　パナソニックグループ、単３形・単４形「エネループ　トーンズ　ウォモ／ルージュ」を限定発売
```

「ストップ違法コピー！ BSA世界ソフトウェア違法コピー調査2011の結果発表」というセンテンスでは2ラベルの確率がかなり拮抗した値になっています。
「違法」という単語は「ITライフハック」にのみ4件存在する一方で、「2011」は「家電チャンネル」にのみ3件出現します。
1406件という少ない訓練データでは、判別するには十分な特徴を見いだせなかったのではないかと考えられます。

## 今後

もう少し異なるモデルや手法とも比較をしてみたいところです。[PyTorch実装、WordPieceでなくneologdで処理した日本語Wikipediaを対象にしたモデルを作っている方はいる](https://qiita.com/Kosuke-Szk/items/4b74b5cce84f423b7125)ので、これを用いた比較もできると良さそうです。

あとは、直接BERTのTransformerを使うのでなく、extract_features.pyで得られる特徴ベクトルをベースに分類タスクを行ってみるのも試してみたいところです。これが広く応用できれば本当にNLP界のVGG16的なものになりそうです。

そもそも、BERTのモデルは2つの文章の関係を解くタスク向けがメインと思われるので、そういう種類のデータでの検討もしてみたいところです。手っ取り早くやるには、既存のデータセットを機械翻訳にかけるという方法を取ることが考えられます。
あとは、[雑談対話コーパス - 対話破綻検出チャレンジ](https://sites.google.com/site/dialoguebreakdowndetection/chat-dialogue-corpus)を用いる方法が考えられます。これは2つの文章の関係性を見るタスクについてのデータとしては適切だと考えられます。

## 参考

* [BERTの学習済みモデルを使ってみる | NHN テコラス Tech Blog | AWS、機械学習、IoTなどの技術ブログ](https://techblog.nhn-techorus.com/archives/12978)
* [汎用言語表現モデルBERTを日本語で動かす(PyTorch) - Qiita](https://qiita.com/Kosuke-Szk/items/4b74b5cce84f423b7125)
* [[1609.08144] Google's Neural Machine Translation System: Bridging the Gap between Human and Machine Translation](https://arxiv.org/abs/1609.08144)
* [Semi-supervised Sentiment Classification with Dialog Data](https://www.jstage.jst.go.jp/article/pjsai/JSAI2018/0/JSAI2018_4G204/_article/-char/ja/)
* [sklearnでナイーブベイズによるテキスト分類にチャレンジ - Qiita](https://qiita.com/kotaroito/items/76a505a88390c5593eba)