Pythonでword2vecを使ってみる（Windows11）

# まえおき
この記事は[wikipediaを使ったword2vecコーパスの作り方をまとめてみた](https://qiita.com/Zect/items/d106d46fc94eaa2ed361)、[【Python】Word2Vecの使い方](https://qiita.com/kenta1984/items/93b64768494f971edf86)を参考にして作られています。
SSH接続したPCで動かすためにコマンドラインのみで完結するようにしました。（インストール手順は割愛します。）
とにかく動くものが欲しい方は[おまけ](#おまけ)を見てください。

# 開発環境
- Windows11
- python: 3.10
- gensim: 4.1.2
- wsl2（これを使えばLinuxのように動かせるので楽ですが今回は極力使いません）

# wikipediaのデータをもってくる
現ディレクトリ内に`jawiki-latest-pages-articles.xml.bz2`をダウンロードします。
~~~
curl https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2 -o jawiki-latest-pages-articles.xml.bz2
~~~

# もってきたテキストをきれいにする
`wikiextractor`を使用します。
~~~
pip install git+https://github.com/prokotg/wikiextractor
python -m wikiextractor.WikiExtractor jawiki-latest-pages-articles.xml.bz2
~~~
:::note warn
最新バージョンver 3.0.6では現在Windowsでエラーが起きるため、ver 3.0.4を使用しています。
ver 3.0.6を使う場合には、wsl2等を利用する必要があります。
:::
`wikiextractor`のテキストを収集します。
~~~
chcp 65001
type nul > wiki.txt
for /d %%d in (./text/*) do copy /b /y wiki.txt+.\text\%%d\* wiki.txt
~~~
:::note warn
catのwindows版で調べて出てきたtypeを使用してみましたが、文字化けが起きたのでcopyを使用します。
/bオプションを付けないとEOSタグが余分に出てきてしまいます。
:::
テキスト内の`<doc>`タグを消す前処理をします。
コマンドでやろうとすると面倒くさそうだった（正規表現が使えない？）ので、アルゴリズムだけ書いておきます。
自分はVS Code 標準の置換機能を使って前処理しました。
- 正規表現`<.*>`を削除（<doc>タグの削除）

wsl2の場合は以下のコマンドで実行可能です。
~~~
sed -e 's/<[^>]*>//g' ./wiki.txt > ./wiki_notag.txt
~~~

pythonでやる場合は以下のプログラムで実行可能です。
~~~trans.py
import re
with open("wiki.txt","r",encoding="utf8")as f:
    texts=f.read()
notag=re.("<.*>","",texts)
with open("wiki_notag","w",encoding="utf8")as f:
    f.write(notag)
~~~

# テキストを単語にわける
MeCabで分かち書きします。
~~~
chcp 65001
mecab -Owakati wiki_notag.txt -o wiki_wakati.txt -b 40960 -d ./mecab-ipadic-neologd
~~~
:::note warn
chcp 65001でコマンドラインをutf-8に変更する必要があります。
-bオプションを付けないとMeCabは8192文字で自動で分割してしまいます。
-dオプションで辞書を変更できます。（つけなくてもいいです。）
:::
mecab-ipadic-neologdは使わなくても良いですが、これを使うと語彙が多くなるのでベクトル変換できないことが少なくなります。
ですが、Linux仮想環境のwsl2,BoW等を使わないと辞書が作れないです。
インストールしたい方は以下を参考にしてください。説明は日本語なので読めると思います。
[mecab-ipadic-NEologdのインストール手順](https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md)

# 学習させる
`gensim`を使ってword2vecを学習します。
ハイパーパラメータに関しては参照したサイトと同じにしてあります。
~~~run.py
from gensim.models import word2vec
import logging

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentences = word2vec.Text8Corpus('./wiki_wakati.txt')

model = word2vec.Word2Vec(sentences, vector_size=200, min_count=20, window=15)
model.wv.save_word2vec_format("./wiki.model", binary=True)
~~~
これでモデルの準備は完了です。

# 使ってみる
`t-SNE`で可視化したり、文章の類似度を取ったり
~~~show.py
from gensim.models import KeyedVectors
from sklearn.manifold import TSNE
import MeCab
import matplotlib.pyplot as plt
import numpy as np

dic_path="../../mecab-ipadic-neologd"
model = KeyedVectors.load_word2vec_format('wiki.model', binary=True)
mecab=MeCab.Tagger("-Owakati -d {}".format(dic_path))
tsne = TSNE() # n_componentsは低次元データの次元数
def cossim(v1,v2):
    return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))

text1="え！？わたしの給料、安すぎ？"
text2="おぼろげに46という数字が浮かんできた"
vec1=[model[s] for s in mecab.parse(text1).split()]
vec2=[model[s] for s in mecab.parse(text1).split()]
X1=tsne.fit_transform(vec1)
X2=tsne.fit_transform(vec2)

plt.plot(X1[:,0],X1[:,1],".")
plt.plot(X2[:,0],X2[:,1],".")
plt.savefig("sexy.png")

print("\"{}\"と\"{}\"の類似度：{}".format(text1,text2,cossim(np.mean(vec1,axis=0),np.mean(vec2,axis=0)))
~~~

# おまけ
実行したら学習モデルを作れます。Windows上でmecab-ipadic-neologdが取れないのでmecabは標準辞書です。

:::note warn
下準備が必要です。
- 上の`trans.py`を使用しています。コピペして同じディレクトリ内に保存してください。[もってきたテキストをきれいにする](#もってきたテキストをきれいにする)
- `mecab`をインストールしてください。[mecabインストーラー](https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7WElGUGt6ejlpVXc)
- `gensim`をインストールしてください。`pip install gensim`
:::
~~~make_w2vmodel.bat
chcp 65001
curl https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2 -o jawiki-latest-pages-articles.xml.bz2
pip install git+https://github.com/prokotg/wikiextractor
python -m wikiextractor.WikiExtractor jawiki-latest-pages-articles.xml.bz2
type nul > wiki.txt
for /d %%d in (./text/*) do copy /b /y wiki.txt+.\text\%%d\* wiki.txt
python trans.py
mecab -Owakati wiki_notag.txt -o wiki_wakati.txt -b 40960
~~~