単語のベクトル演算で「捕手-野球+サッカー」をやってみた
はじめに
こんにちは自動化アーキテクトの森川です。
自然言語処理のライブラリを使って、単語のベクトル演算なるものをやってみました。
単語の分散表現
単語埋め込みとも呼ばれます。
他の単語との関係性を解析しながら、単語の概念を固定長のベクトルにすることで数値化できる仕組みのようなものだと思っています。
詳しくは、自然言語処理の書籍をご覧ください。
単語のベクトル演算
ベクトル化することで、計算が可能になります。
王様という概念から男性を引いて、女性を足す。期待する答えは女王です。
それではやってみましょう。
FastTextをさわってみる
単語の分散表現で有名なライブラリは"Word2Vec"ですが、調べてみるとFacebook(現Meta)さんが開発したFastTextというライブラリは、更に性能が改善されているそうです。今回はこれを触ってみます。
環境構築
Dockerで手早く環境をつくります。オリジナルはc++ですが手軽に行きたいのでPythonで操作をします。
※ 手元のWSL2(Ubuntu20.04LTS)上でやっています。
├ Dockerfile
├ script/test_fs.py
└ model/wiki_ja__my_model.bin
DockerFileからイメージをビルド
# Dockerfile
FROM python:3.9
WORKDIR /app
RUN git clone https://github.com/facebookresearch/fastText.git && \
cd fastText && \
pip install .
CMD ["python", "app.py"]
docker build -t fasttext .
そこそこ時間を要します。
教師なしモデルの入手
Wikipedia日本語ページをfasttextでトレーニングしたデータを事前に作成してmodelに置きます。
大きさは約4GBと、かなりデカい
トレーニング済モデルはFastText公式に公開されているので、こちらを流用しても良いです。
※ ご利用の際はルールを守りましょう
https://fasttext.cc/docs/en/pretrained-vectors.html
コンテナ実行
docker run --rm -it -v ~/model:/model -v ~/script:/script fasttext /bin/bash
コンテナの中に入ってpythonを起動して、コマンドを打っていきましょう。
$ python
Python 3.9.13 (main, Jul 12 2022, 12:04:02)
...
>>> import fasttext
>>> from pprint import pprint
>>> model = fasttext.load_model('/model/wiki_ja__my_model.bin')
実行結果
>>> pprint(model.get_word_vector("自然言語処理"))
array([ 2.77438134e-01, -8.66088197e-02, -3.12941849e-01, -8.79815221e-02,
1.07949361e-01, -1.84897318e-01, -3.29486020e-02, -2.19183698e-01,
-2.22590283e-01, 2.98827551e-02, -8.47320631e-03, 1.79108500e-01,
...
dtype=float32)
少しだけ解説
fasttextのインスタンスを初期化して、モデルをロードしています。
「自然言語処理」という単語のベクトルを出力しています。
今回のモデルはdimension=300なので300個の意味深な数字が出力されました。
さて、ここから本題です。
単語ベクトルの演算
単語ベクトルを羅列されても生身の人間にはさっぱり理解できません。
しかしながら数値化されることで単語同士を演算することができます。
さっそくやってみましょう。
単語の類推をしてくれるFastTextのAPIget_analogiesを使います。
3つの引数を渡すと "A - B + C" をベクトル演算して類似する単語を出力します。
>>> pprint(model.get_analogies("王様", "男性","女性",k=10))
[(0.5490007996559143, '王女'),
(0.5433363914489746, 'シンデレラ'),
(0.534498929977417, 'マレフィセント'),
(0.5192593932151794, 'お姫様'),
(0.5052384734153748, 'ジミニー・クリケット'),
(0.49663999676704407, '王さま'),
(0.4936622083187103, '女王'),
(0.4888650178909302, '魅惑'),
(0.4867748022079468, 'オコノミミ'),
(0.486691415309906, 'マリー・クリスティーヌ')]
"王女"が1番目に来てしまいました。"女王"は8位、がんばれクイーン。
気を取り直して、本記事タイトルの単語ベクトル演算 "捕手 - 野球 + サッカー" をやってみます。
期待する答えは"ゴールキーパー"です。
>>> pprint(model.get_analogies("捕手","野球","サッカー", k=4))
[(0.7237740159034729, 'ゴールキーパー'),
(0.6964200735092163, 'GK'),
(0.6818143725395203, 'サイドバック'),
(0.6719012260437012, 'DF')]
見事に"ゴールキーパー"が1位です。さらに"GK"まで出してくれました!
捕手とゴールキーパーは厳密な意味では同義ではないのですが、なんとなくそうだよね?な答えを機械が導き出すところが素敵です。
調子に乗って、もう一つやってみましょう。
"荒川 - 東京 + 大阪"
期待する答えは"淀川"、そうですYODO Riverです。
>>> pprint(model.get_analogies("荒川", "東京","大阪",k=5))
[(0.6349592208862305, '淀川'),
(0.6153256893157959, '大和川'),
(0.567109227180481, '木津川'),
(0.560580313205719, '寝屋川'),
(0.535304069519043, '八尾')]
バッチリですね。関西出身としては、母校の近くを流れる大和川が2位に入っているところが嬉しいです。
最後に、少し難しいのをやってみましょう。
"責務 - 利益 + 尊い"
期待する答えは"ノブレスオブリージュ"です。
>>> pprint(model.get_analogies("責務", "利益","尊い",k=5))
[(0.4072819650173187, '厳か'),
(0.4040670394897461, '使命'),
(0.4022969603538513, 'ノブレスオブリージュ'),
(0.3977338373661041, '大切'),
(0.39596885442733765, '大事'),
惜しい!3番目でした。
テストファイルはこちら
# train/test_fs.py
import fasttext
from pprint import pprint
model = fasttext.load_model('/train/model.bin')
pprint(model.get_analogies("責務", "利益","尊い",k=5))
まとめ
DockerでFastTextを起動
捕手 - 野球 + サッカー = ゴールキーパー となった。
トレーニングされたモデルを使って単語ベクトル演算をサクッとやってみました。
今日では広く使われる技術の基礎になっている考え方だと思いますが、手元で叩いてみると納得感と面白みがありました。
そして、MLツールを実行するとき「よくやってくれた!」という気持ちが少し湧くのは何故でしょう、不思議です。
FastTextには類似語検出や文章のベクトル出力のAPIも用意されています。
ご興味あるかたは触ってみてください。
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/