単語分散表現 chiVe の活用方法 Part 4

**chiVeの活用方法シリーズ**
* [Part 1 「分散表現とは？chiVeとは？」](https://qiita.com/nishihara_d/items/99bc1a1185c2efd36d64)
* [Part 2 「chiVeでできること（単語ベクトルの性質）」](https://qiita.com/nishihara_d/items/d2973558ec1ee2c69555)
* [Part 3 「chiVeでできること（単語ベクトルで文書分類）」](https://qiita.com/nishihara_d/items/9e3967ad37780d93c7ac)
* Part 4 「chiVeの追加学習」（本記事）


## はじめに

2022年4月末に、[WAP NLP Tech Talk#5](https://worksapplications.connpass.com/event/244032/) にて、「単語分散表現 chiVeの活用方法」というタイトルでお話しをしました。
そのQiita版として、[スライド](https://speakerdeck.com/waptech/dan-yu-fen-san-biao-xian-chivefalsehuo-yong-fang-fa-at-wap-nlp-tech-talk-number-5) の内容を補足しながら4回に分けて紹介しています。
[chiVe](https://github.com/WorksApplications/chiVe) とは、日本語単語分散表現です。分散表現の基本事項や、chiVe の特長については、[Part 1](https://qiita.com/nishihara_d/items/99bc1a1185c2efd36d64) で触れています。

さて、前回は公開済みの chiVe を使って文書分類をしました。今回は chiVe を追加学習し、前回と同じ分類タスクで結果を比較します。


## 追加学習

公開されている chiVe は汎用的なデータセットで学習済み[^1]ですので、そのままでも利用できますが、手持ちのデータで追加学習させることによって、自分専用のドメイン（分野）に特化させることができます。
例えば、ニュースの分類タスクに専用で使う場合は、ニュースドメインのコーパスで追加学習させることで、より性能が高くなる可能性があります。

<img width="680" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513839/b401493b-68ac-7510-321c-44c8dd1fe9ab.png">

[^1]:chiVe の学習の仕組みについては[こちらの記事](https://qiita.com/masakuri/items/480c093953dc9dd7f54b)を参照してください。


## 実験

今回も [Livedoor ニュースコーパス](http://www.rondhuit.com/download.html#ldcc) でテキスト分類をします。

:::note
[前回](https://qiita.com/nishihara_d/items/9e3967ad37780d93c7ac)のコードの続きで進めます。

<details>
<summary>本記事から進めるには</summary>
前回の「<a href="https://qiita.com/nishihara_d/items/9e3967ad37780d93c7ac#%E3%83%87%E3%83%BC%E3%82%BF%E3%82%BB%E3%83%83%E3%83%88%E3%81%AE%E6%BA%96%E5%82%99">データセットの準備</a> 」節を適用してから次のコードを実行してください。

```py
import gensim
import numpy as np
import sudachipy
import sudachidict_core
from sklearn.metrics import accuracy_score
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline, Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

tokenizer = sudachipy.Dictionary().create()

def tokenize(sent: str) -> list[str]:
    morphs = tokenizer.tokenize(sent.strip())
    norms = [m.normalized_form() for m in morphs]
    return norms

def text2vec(text: str, vectorizer):
    norms = tokenize(text)
    word_vecs = np.array([vectorizer[n] for n in norms if n in vectorizer])
    text_vec = np.average(vecs, axis=0)
    return vec

def classify(train_x, train_y, test_x, test_y):
    param_grid = {
        'svc__C': [1, 10, 100, 1000],
        'svc__gamma': [1e-4, 5e-4, 1e-3],
    }
    clf = make_pipeline(StandardScaler(), SVC(random_state=0))
    search = GridSearchCV(clf, param_grid, verbose=3)
    search.fit(train_x, train_y)
    best_model = search.best_estimator_
    predicted = best_model.predict(test_x)
    accuracy = accuracy_score(test_y, predicted)
    print('accuracy = {:.4f}'.format(accuracy))
```
:::


### フルモデルのダウンロード

chiVe の追加学習を行うには、[フルモデル](https://github.com/WorksApplications/chiVe#%E8%BF%BD%E5%8A%A0%E5%AD%A6%E7%BF%92%E7%94%A8%E3%81%AE%E3%83%95%E3%83%AB%E3%83%A2%E3%83%87%E3%83%AB)を使う必要があります。

```sh:console: フルモデルのダウンロードして展開
$ wget https://sudachi.s3-ap-northeast-1.amazonaws.com/chive/chive-1.2-mc90_gensim-full.tar.gz
$ tar xzf chive-1.2-mc90_gensim-full.tar.gz
```


### chiVe の追加学習

```py:chive の追加学習
from gensim.models import Word2Vec

# chiVe を追加学習するメソッド
def finetuning(output_chive_path: str, train_texts):
    chive_finetuned = Word2Vec.load("data/chive-1.2-mc90_gensim-full/chive-1.2-mc90.bin")

    # 実験の再現性のため（並列処理すると実験が再現できない）
    chive_finetuned.workers = 1

    # 語彙の再構築
    chive_finetuned.build_vocab(train_texts, update=True)

    # 追加学習で使用するデータセットは小規模なので、min_count を小さくしておく
    chive_finetuned.vocabulary.min_count = 0

    # 学習、保存
    chive_finetuned.train(train_texts, total_examples=chive_finetuned.corpus_count,
                          epochs=chive_finetuned.epochs)
    chive_finetuned.save(output_chive_path)
```


Livedoor ニュースコーパスの学習用データでchiVeを学習します。

```py
# 学習用データを単語分割
train_toked = [tokenize(line) for article in train_df['article']
               for line in article.split('\n')]

# 追加学習した chiVe の保存先
output_chive_path='data/chive-1.2-mc90.livedoor.finetuned.bin'

# 追加学習
finetuning(output_chive_path, train_toked)
```

学習が終わると、次の3つのファイルが生成されているはずです。
```sh
$ ls
# >>> chive-1.2-mc90.livedoor.finetuned.bin
#     chive-1.2-mc90.livedoor.finetuned.bin.trainables.syn1neg.npy
#     chive-1.2-mc90.livedoor.finetuned.bin.wv.vectors.npy
```

これで、Livedoor ニュースコーパスに特化した chiVe が得られました。


### 追加学習した chiVe を使って分類

前回と同じデータセットで分類タスクの性能を確認します。

```py
# 追加学習した chiVe の読み込み
chive_finetuned = Word2Vec.load('data/chive-1.2-mc90.livedoor.finetuned.bin')

# vectorizer=chive_finetuned.wv を指定して、ベクトル化
train_x = np.array([text2vec(article, vectorizer=chive_finetuned.wv)
                    for article in train_df["article"]])
train_y = np.array(train_df["label_id"])

test_x = np.array([text2vec(article, vectorizer=chive_finetuned.wv)
                   for article in test_df["article"]])
test_y = np.array(test_df["label_id"])

# 学習、評価
classify(train_x, train_y, test_x, test_y)
# >>> accuracy = 0.9227
```

前回の正解率 91.32% に比べて、改善できたようです。


### 他のデータセットで追加学習

chiVe の性能改善にはデータセットの選択が大切です。ニュース記事とは別のデータで chiVe を追加学習し、同じ分類タスク（ニュース分類）の性能が改善するか調べます。

別のデータセットとして、Twitter のツイート文を使ってみましょう。ツイート文は、ニュース記事に比べてカジュアルな表現が多く、同じ日本語ですが性質が全く異なります。

Twitterのデータセットとして、[WRIME](https://github.com/ids-cv/wrime)を使います。

```sh:console: データセットのダウンロード
$ wget https://raw.githubusercontent.com/ids-cv/wrime/master/wrime-ver1.tsv
```

```py:python: ツイート文で chiVe を追加学習してニュース記事を分類
import pandas as pd

# データを読み込んで、単語分割
wrime_df = pd.read_csv('data/wrime/wrime-ver1.tsv', delimiter='\t')
train_toked = [tokenize(line) for tweet in wrime_df['Sentence'] for line in tweet.split('\n')]

# chiVe の追加学習
finetuning('data/chive-1.2-mc90.wrime.finetuned.bin', train_toked)

chive_finetuned_wrime = Word2Vec.load('data/chive-1.2-mc90.wrime.finetuned.bin')

# ベクトル化
train_x = np.array([text2vec(article, vectorizer=chive_finetuned_wrime.wv)
                    for article in train_df["article"]])
train_y = np.array(train_df["label_id"])
test_x = np.array([text2vec(article, vectorizer=chive_finetuned_wrime.wv)
                   for article in test_df["article"]])
test_y = np.array(test_df["label_id"])

# 分類
classify(train_x, train_y, test_x, test_y)
# >>> accuracy = 0.9091
```

追加学習をしなかった場合の正解率 91.32% に比べて、性能が下がってしまったようです。
このように、追加学習を行う時には、適切なデータセットの選択が必要です。


### まとめ

今回は chiVe の追加学習について触れました。
Livedoor ニュースコーパスの分類実験の結果は次の通りでした。

| 分類手法 | 正解率 |
|:-|:-:|
| chiVe（追加学習なし） + SVM | 0.9132  |
| chiVe（Livedoorで追加学習） + SVM  | **0.9227**  |
| chiVe（ツイート文で追加学習） + SVM  | 0.9091  |

同じドメインのデータセットで追加学習する手法（Livedoorで追加学習）が最も良い結果になりましたが、別のドメインのデータセットを使う手法（ツイート文で追加学習）は、改善が見られませんでした。
chiVe を追加学習する際には、応用先に合わせたデータセットをご検討ください。

さて、「単語分散表現 chiVe の活用方法」シリーズは今回が最後です。
ぜひ紹介した内容を応用して chiVe を使ってみてください。