概要
ラムダ技術部がYoutubeで触ってて、面白そうだったので動画とその他Qiitaの記事とかみながら同じように使ってみた
【数値化】言葉の足し算をするAIで遊んでみた
環境
項目 | スペック |
---|---|
Chip | Apple M1 |
Memory | 16 GB |
macOS | 13.1(22C65) |
wikipediaから日本語の記事のdumpファイルを取得
Wikipedia:データベースダウンロード
https://dumps.wikimedia.org/jawiki/
wikiextractorで文章を抽出
- git clone
git clone https://github.com/attardi/wikiextractor.git
- 実行
cd wikiextractor
python -m wikiextractor.WikiExtractor ../Downloads/jawiki-latest-pages-articles.xml
途中で
WARNING: Template errors in article '6月' (1710): title(0) recursion(1, 0, 0)
みたいなエラー起こったけど問題ないらしい?
The warnings are normal, due to malformed templates.
There was an error though in release 2.36 that has been fixed in release 2.37.
2015年にfixしたって言いよるけどしてないけどな..まあ一旦無視で..
動画では
INFO: Usin15 extract processes.
って書いてあって、16並列で実行してるみたいだったから
自分のは
INFO: Using 7 extract processes.
ってなってたから8並列なのかな。
なんか5時間くらい動いて途中で止まった感じしたから失敗したかなー思ったけど、
一応テキストファイルできてたのでぎりいけてるかな
テキストファイルを一つのファイルにまとめる
pwd
/Users/keiich/wikiextractor
cat text/*/* > jawiki-latest-pages-articles.txt
分かち書きを行う
mecabを使えるようにする
# mecabのインストール
brew install mecab
# IPA辞書をインストール(一番メジャーな辞書らしい)
brew install mecab-ipadic
# PythonからMeCabを呼び出せるようにする
pip3 install mecab-python3
こんな感じでmecabが使えるようになった
~/wikiextractor on master! ⌚ 1:19:53
$ echo "今日はいい天気ですね"|mecab
今日 名詞,副詞可能,*,*,*,*,今日,キョウ,キョー
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
いい 形容詞,自立,*,*,形容詞・イイ,基本形,いい,イイ,イイ
天気 名詞,一般,*,*,*,*,天気,テンキ,テンキ
です 助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
ね 助詞,終助詞,*,*,*,*,ね,ネ,ネ
EOS
分かち書きを行う(文章を単語ごとに切り離す)
mecab -Owakati jawiki-latest-pages-articles.txt -o jawiki-latest-pages-articles.text8 -b 200000
オプション | man | 説明 | 備考 |
---|---|---|---|
-O | --output-format-type=TYPE | set output format type (wakati,none,...) | アウトプット先のフォーマット |
-o | --output=FILE | set the output file name | アウトプットするファイル名 |
-b | --input-buffer-size=INT | set input buffer size (default 8192) | インプットする一行の最大サイズ |
こんな感じで分かち書きされた
before
~/wikiextractor on master! ⌚ 0:19:54
$ head jawiki-latest-pages-articles.txt
<doc id="5" url="https://ja.wikipedia.org/wiki?curid=5" title="アンパサンド">
アンパサンド
<templatestyles src="Hatnote/styles.css" />
<templatestyles src="Hatnote/styles.css" />
<templatestyles src="Hatnote/styles.css" />
<templatestyles src="Unicode/styles.css" />&
アンパサンド(&, : ampersand)は、並立助詞「…と…」を意味する記号である。ラテン語で「…と…」を表す接続詞 "et" の合字を起源とする。現代のフォントでも、Trebuchet MS など一部のフォントでは、"et" の合字であることが容易にわかる字形を使用している。
語源.
<templatestyles src="Template:Quote/styles.css" />The term ampersand is a corruption of and (&) per se and, which literally means "(the character) & by itself (is the word) and." The symbol & is derived from the ligature of ET or et, which is the Latin word for "and."<br>
after
~/wikiextractor on master! ⌚ 0:19:58
$ head jawiki-latest-pages-articles.text8
< doc id =" 5 " url =" https :// ja . wikipedia . org / wiki ? curid = 5 " title =" アンパサンド ">
アンパサンド
& lt ; templatestyles src =" Hatnote / styles . css " /& gt ;
& lt ; templatestyles src =" Hatnote / styles . css " /& gt ;
& lt ; templatestyles src =" Hatnote / styles . css " /& gt ;
& lt ; templatestyles src =" Unicode / styles . css " /& gt ;& amp ;
アンパサンド (& amp ;, : ampersand ) は 、 並立 助詞 「 … と … 」 を 意味 する 記号 で ある 。 ラテン語 で 「 … と … 」 を 表す 接続詞 " et " の 合 字 を 起源 と する 。 現代 の フォント で も 、 Trebuchet MS など 一部 の フォント で は 、 " et " の 合 字 で ある こと が 容易 に わかる 字形 を 使用 し て いる 。
語源 .
& lt ; templatestyles src =" Template : Quote / styles . css " /& gt ; The term ampersand is a corruption of and (& amp ;) per se and , which literally means "( the character ) & amp ; by itself ( is the word ) and ." The symbol & amp ; is derived from the ligature of ET or et , which is the Latin word for " and ."& lt ; br & gt ;
単語の数値化
gensimのインストール
pip install gensim
生成コードは以下。
動画のとおりだとちょこちょこエラーが発生したので、軽く修正。
$ cat train.py
import logging
from gensim.models import word2vec
def main():
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
sentences = word2vec.Text8Corpus("jawiki-latest-pages-articles.text8")
model = word2vec.Word2Vec(sentences, vector_size=200, min_count=20, window=15)
model.save("jawiki-latest-pages-articles.model")
if __name__ == "__main__":
main()
なんかいい感じに解析が進んでる気がしないでもない
$ pipenv run python train.py
2023-06-23 02:01:21,947 : INFO : collecting all words and their counts
2023-06-23 02:01:21,948 : INFO : PROGRESS: at sentence #0, processed 0 words, keeping 0 word types
2023-06-23 02:01:35,221 : INFO : PROGRESS: at sentence #10000, processed 100000000 words, keeping 681989 word types
2023-06-23 02:01:49,221 : INFO : PROGRESS: at sentence #20000, processed 200000000 words, keeping 1172151 word types
2023-06-23 02:02:03,519 : INFO : PROGRESS: at sentence #30000, processed 300000000 words, keeping 1683457 word types
2023-06-23 02:02:12,242 : INFO : collected 2020662 word types from a corpus of 361386394 raw words and 36139 sentences
2023-06-23 02:02:12,242 : INFO : Creating a fresh vocabulary
2023-06-23 02:02:12,662 : INFO : Word2Vec lifecycle event {'msg': 'effective_min_count=20 retains 166200 unique words (8.23% of original 2020662, drops 1854462)', 'datetime': '2023-06-23T02:02:12.649221', 'gensim': '4.3.1', 'python': '3.11.4 (main, Jun 23 2023, 01:43:14) [Clang 14.0.0 (clang-1400.0.29.202)]', 'platform': 'macOS-13.1-arm64-arm-64bit', 'event': 'prepare_vocab'}
2023-06-23 02:02:12,662 : INFO : Word2Vec lifecycle event {'msg': 'effective_min_count=20 leaves 356751977 word corpus (98.72% of original 361386394, drops 4634417)', 'datetime': '2023-06-23T02:02:12.662658', 'gensim': '4.3.1', 'python': '3.11.4 (main, Jun 23 2023, 01:43:14) [Clang 14.0.0 (clang-1400.0.29.202)]', 'platform': 'macOS-13.1-arm64-arm-64bit', 'event': 'prepare_vocab'}
2023-06-23 02:02:12,986 : INFO : deleting the raw counts dictionary of 2020662 items
2023-06-23 02:02:13,019 : INFO : sample=0.001 downsamples 40 most-common words
2023-06-23 02:02:13,019 : INFO : Word2Vec lifecycle event {'msg': 'downsampling leaves estimated 255732226.39166573 word corpus (71.7%% of prior 356751977)', 'datetime': '2023-06-23T02:02:13.019190', 'gensim': '4.3.1', 'python': '3.11.4 (main, Jun 23 2023, 01:43:14) [Clang 14.0.0 (clang-1400.0.29.202)]', 'platform': 'macOS-13.1-arm64-arm-64bit', 'event': 'prepare_vocab'}
2023-06-23 02:02:13,544 : INFO : estimated required memory for 166200 words and 200 dimensions: 349020000 bytes
2023-06-23 02:02:13,544 : INFO : resetting layer weights
2023-06-23 02:02:13,647 : INFO : Word2Vec lifecycle event {'update': False, 'trim_rule': 'None', 'datetime': '2023-06-23T02:02:13.647006', 'gensim': '4.3.1', 'python': '3.11.4 (main, Jun 23 2023, 01:43:14) [Clang 14.0.0 (clang-1400.0.29.202)]', 'platform': 'macOS-13.1-arm64-arm-64bit', 'event': 'build_vocab'}
2023-06-23 02:02:13,647 : INFO : Word2Vec lifecycle event {'msg': 'training model with 3 workers on 166200 vocabulary and 200 features, using sg=0 hs=0 sample=0.001 negative=5 window=15 shrink_windows=True', 'datetime': '2023-06-23T02:02:13.647143', 'gensim': '4.3.1', 'python': '3.11.4 (main, Jun 23 2023, 01:43:14) [Clang 14.0.0 (clang-1400.0.29.202)]', 'platform': 'macOS-13.1-arm64-arm-64bit', 'event': 'train'}
2023-06-23 02:02:14,655 : INFO : EPOCH 0 - PROGRESS: at 0.43% examples, 1077407 words/s, in_qsize 5, out_qsize 0
2023-06-23 02:02:15,659 : INFO : EPOCH 0 - PROGRESS: at 0.87% examples, 1098872 words/s, in_qsize 5, out_qsize 0
2023-06-23 02:02:16,659 : INFO : EPOCH 0 - PROGRESS: at 1.31% examples, 1107035 words/s, in_qsize 5, out_qsize 0
数十分ほどで解析が終わってました。
動画で試してたことと同じように遊んでみる。
・日本の数値化
~/wikiextractor on master! ⌚ 2:30:59
$ python3
Python 3.10.0 (default, Jun 23 2023, 01:29:29) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from gensim.models import word2vec
>>> model = word2vec.Word2Vec.load("jawiki-latest-pages-articles.model")
>>> model.wv["日本"]
array([-5.3468966e-01, -2.0880291e+00, -2.3025777e+00, 1.1763583e+00,
-2.6904808e-02, 3.0742314e+00, 1.9489471e+00, -4.9543033e+00,
-3.9317739e-01, -5.6353774e-02, 2.5795454e-01, 1.5430601e+00,
-1.1986245e+00, 2.3173127e+00, -3.2681861e+00, -1.6955503e+00,
8.3424285e-02, -1.8770831e+00, -3.0280814e+00, 1.2164966e+00,
2.3541834e+00, 4.0325003e+00, 2.1856875e+00, 4.2010670e+00,
-4.7692032e+00, -9.8490166e-03, 1.1412461e-02, -2.7749317e+00,
2.9831033e+00, -1.7632852e+00, 6.5999186e-01, -4.2987070e+00,
3.0999410e+00, -5.4150534e-01, -5.0368994e-01, -1.8144890e+00,
1.6156478e-01, 2.9792051e+00, -1.9222465e+00, -1.5630528e+00,
2.5720704e+00, 3.4025788e+00, -1.7461491e+00, -1.8910306e+00,
-2.2542109e+00, -1.9331188e+00, 2.6146445e+00, 9.7135574e-01,
1.3442531e+00, 2.5009036e+00, 9.3927073e-01, 2.9763885e+00,
-3.0589156e+00, 1.0975548e+00, -9.6435857e-01, 1.2285855e+00,
-1.7708542e+00, -1.5202992e+00, 1.4867097e-01, -7.2497702e-01,
1.8397453e+00, -1.5409088e+00, 7.7762669e-01, 8.6016339e-01,
3.0915241e+00, -2.9308267e+00, -4.3804317e+00, 1.5494033e+00,
5.3183832e+00, -8.0996048e-01, 5.2227459e+00, 9.1404396e-01,
1.4695956e+00, 2.3025007e+00, 1.0056815e+00, -5.6871743e+00,
-4.2530575e+00, 1.3749095e+00, 3.8670385e+00, -2.1532459e+00,
3.0861087e+00, -1.2953337e-01, 4.1080098e+00, -4.7009850e+00,
4.7578731e+00, 2.3244605e+00, 1.0505451e+00, -3.1884530e+00,
-2.8995785e-01, 1.6265123e+00, 5.6440508e-01, -6.0669250e+00,
3.6532078e+00, -2.1613202e+00, -7.9395372e-01, 2.8551087e+00,
1.2046566e+00, -1.6180586e+00, 2.9043350e-01, -2.4428318e+00,
-3.7590995e-01, -2.7069206e+00, -1.2523538e-01, 6.7488202e-03,
-3.3224568e+00, 5.3258185e+00, 1.3134362e+00, 3.0385063e+00,
1.4281878e+00, -2.2192551e-02, -2.7347583e-01, 1.0653813e+00,
-1.3599988e+00, 1.4140940e+00, 3.7209785e+00, 1.0060102e+00,
-3.4473926e-01, -3.0441198e+00, -3.7047541e+00, 1.6258577e+00,
1.4240907e+00, -9.2480677e-01, 9.2668116e-02, -8.8392859e+00,
-7.2349787e-01, -2.6077728e+00, -2.1025203e-03, -1.6684934e+00,
4.8191795e+00, 1.7806041e+00, 1.7978582e+00, 2.6835325e-01,
-4.2188540e+00, -4.5904243e-01, 3.0134189e+00, -1.7211404e+00,
8.1806943e-02, -2.5115089e+00, 9.1338664e-01, 7.2289276e-01,
-1.6027880e+00, 1.0276166e+00, 7.6018274e-01, 4.6317782e+00,
1.3763669e+00, 3.2230096e+00, 1.8509549e+00, -1.3406097e+00,
-2.0791814e+00, -4.5972414e+00, 6.4995658e-01, 6.9719183e-01,
-1.2097121e+00, 4.7637078e-01, 1.8122140e-01, -1.5820810e+00,
4.2533712e+00, -6.0426106e+00, 2.7382402e+00, 9.3920517e-01,
1.2764137e+00, -1.9453816e+00, 1.6436279e+00, 2.0296857e+00,
8.6448008e-01, 2.5855741e-01, -1.4424334e+00, 1.4021158e-01,
4.2816707e-01, 3.7061224e+00, 2.2464654e+00, -3.3896318e-01,
-1.0680170e+00, 1.2768720e+00, 5.3243690e+00, 1.5358697e+00,
5.4340678e-01, 1.1388026e+00, 1.7050402e+00, 3.2410836e-01,
-6.2944943e-01, -2.3566236e+00, -2.2727730e-02, -1.7687978e+00,
1.6992476e+00, 6.6836369e-01, -1.5976621e+00, 4.5663533e+00,
1.4152455e+00, 7.3735607e-01, 4.9971166e+00, -2.3517036e+00,
2.1868627e+00, -8.5756135e-01, -5.6144061e+00, -1.0902302e+00,
-2.0893476e+00, 2.1243804e+00, 4.8933172e-01, -4.5805365e-01],
dtype=float32)
遊ぶ
・日本に近い単語
>>> model.wv.most_similar
<bound method KeyedVectors.most_similar of <gensim.models.keyedvectors.KeyedVectors object at 0x1233a43a0>>
>>> model.wv.most_similar(positive=["日本"])
[('欧米', 0.5450484752655029), ('海外', 0.5429348349571228), ('中国', 0.534841775894165), ('韓国', 0.5329352021217346), ('米国', 0.5252956748008728), ('アメリカ', 0.507358968257904), ('国内', 0.4975028932094574), ('日本人', 0.4965011477470398), ('台湾', 0.46441274881362915), ('大日本帝国', 0.4632675051689148)]
・いろんな足し算引き算
>>> model.wv.most_similar(positive=["東京", "中国"], negative=["日本"], topn=1)
[('北京', 0.5752017498016357)]
>>> model.wv.most_similar(positive=["大阪", "中国"], negative=["日本"], topn=1)
[('泉州', 0.5714796781539917)]
>>> model.wv.most_similar(positive=["円", "中国"], negative=["日本"], topn=1)
[('ウォン', 0.5957801938056946)]
>>> model.wv.most_similar(positive=["円", "韓国"], negative=["日本"], topn=1)
[('ウォン', 0.6753687858581543)]
>>> model.wv.most_similar(positive=["身体", "ロボット"], negative=["人間"], topn=1)
[('ロボ', 0.6067451238632202)]
>>> model.wv.most_similar(positive=["身体", "ロボット"], negative=["人間"], topn=5)
[('ロボ', 0.6067451238632202), ('メカ', 0.606461226940155), ('マシーン', 0.564978837966919), ('パワードスーツ', 0.5596998929977417), ('義足', 0.5346253514289856)]
>>> model.wv.most_similar(positive=["パプリカ"], negative=["ピーマン"], topn=5)
[('ギレルモ・デル・トロ', 0.33173590898513794), ('ヴィットーリオ・エマヌエーレ', 0.3122504949569702), ('チネチッタ', 0.311750203371048), ('ウェンブリー・スタジアム', 0.3085929751396179), ('ヘルシンボリ', 0.30330243706703186)]
>>> model.wv.most_similar(positive=["ドブネズミ", "美しい"], topn=5)
[('美しく', 0.5892148613929749), ('小鳥', 0.5869338512420654), ('醜い', 0.5851070284843445), ('フクロウ', 0.5733069777488708), ('ヒキガエル', 0.5691303610801697)]
>>> model.wv.most_similar(positive=["ドブネズミ", "美しい"], negative=[], topn=5)
[('美しく', 0.5892148613929749), ('小鳥', 0.5869338512420654), ('醜い', 0.5851070284843445), ('フクロウ', 0.5733069777488708), ('ヒキガエル', 0.5691303610801697)]
>>> model.wv.most_similar(positive=["善人", "正義"], negative=[], topn=5)
[('悪人', 0.7219346761703491), ('義侠', 0.6450489163398743), ('性根', 0.6369544267654419), ('弱者', 0.6293686032295227), ('男気', 0.6282287240028381)]
>>> model.wv.most_similar(positive=["屁理屈", "論理"], negative=[], topn=5)
[('詭弁', 0.6265831589698792), ('命題', 0.5867470502853394), ('推論', 0.5705077648162842), ('直観', 0.5588919520378113), ('思考', 0.5574309825897217)]
>>> model.wv.most_similar(positive=["屁理屈"], negative=["論理"], topn=5)
[('くすぐる', 0.4393494427204132), ('コタツ', 0.43756794929504395), ('丸出し', 0.4336831867694855), ('ぶちまけ', 0.4326493740081787), ('言い合う', 0.4262131452560425)]
>>> model.wv.most_similar(positive=["人生"], negative=["金"], topn=5)
[('生き方', 0.4927005171775818), ('思索', 0.4808824062347412), ('葛藤', 0.4353131949901581), ('心境', 0.4265024662017822), ('実感', 0.4211724102497101)]
>>> model.wv.most_similar(positive=["人生"], negative=["金"], topn=10)
[('生き方', 0.4927005171775818), ('思索', 0.4808824062347412), ('葛藤', 0.4353131949901581), ('心境', 0.4265024662017822), ('実感', 0.4211724102497101), ('感動', 0.4181087017059326), ('思い出', 0.41686761379241943), ('運命', 0.4156077802181244), ('楽しい', 0.414040744304657), ('ベルクソン', 0.41058024764060974)]
>>> model.wv.most_similar(positive=[], negative=["ニート"], topn=1)
[('採火', 0.28330451250076294)]
>>>
日本の近い単語が欧米って...
多分こんな感じでいらん記号は消したほうが精度あがるっぽい
sed -e 's/<[^>]*>//g' ./wiki.txt > ./wiki_notag.txt
sed -i -e 's/(/(/g' ./wiki_notag.txt && sed -i -e 's/)/)/g' ./wiki_notag.txt
sed -i -e 's/([^)]*)//g' ./wiki_notag.txt
sed -i -e 's/ //g' ./wiki_notag.txt && sed -i -e '/^$/d' ./wiki_notag.txt
終わり。楽しかった。
追記
ゴミデータ消してもう一回回してみたけどあまり変わらず。
ゆうて結構いい感じって友だちが言ってた
$ python3
Python 3.10.0 (default, Jun 23 2023, 01:29:29) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from gensim.models import word2vec
>>> model = word2vec.Word2Vec.load("jawiki-latest-pages-articles-formatted.model")
>>> model.wv.most_similar
<bound method KeyedVectors.most_similar of <gensim.models.keyedvectors.KeyedVectors object at 0x105068940>>
>>> model.wv.most_similar(positive=["日本"])
[('海外', 0.5405691862106323), ('欧米', 0.5382519960403442), ('韓国', 0.5366967916488647), ('中国', 0.5293124318122864), ('日本人', 0.5269358158111572), ('米国', 0.5225452780723572), ('大日本帝国', 0.48113834857940674), ('東洋', 0.4748345613479614), ('台湾', 0.47319602966308594), ('国内', 0.46810826659202576)]
>>> model.wv.most_similar(positive=["円", "中国"], negative=["日本"], topn=1)
[('ウォン', 0.6088246703147888)]
>>> model.wv.most_similar(positive=["円", "中国"], negative=["日本"], topn=2)
[('ウォン', 0.6088246703147888), ('香港ドル', 0.576599657535553)]
>>> model.wv.most_similar(positive=["円", "中国"], negative=["日本"], topn=3)
[('ウォン', 0.6088246703147888), ('香港ドル', 0.576599657535553), ('彧', 0.545497477054596)]
>>> model.wv.most_similar(positive=["円", "中国"], negative=["日本"], topn=5)
[('ウォン', 0.6088246703147888), ('香港ドル', 0.576599657535553), ('彧', 0.545497477054596), ('ドル', 0.5419517755508423), ('人民元', 0.530688464641571)]
>>>
今の遊びは全部model.wv.most_similar
を使ってたけど、他にも以下関数がある
>>> model.wv.
model.wv.add_lifecycle_event( model.wv.evaluate_word_pairs( model.wv.index2word model.wv.most_similar( model.wv.save( model.wv.vector_size
model.wv.add_vector( model.wv.expandos model.wv.index_to_key model.wv.most_similar_cosmul( model.wv.save_word2vec_format( model.wv.vectors
model.wv.add_vectors( model.wv.fill_norms( model.wv.init_sims( model.wv.most_similar_to_given( model.wv.set_vecattr( model.wv.vectors_for_all(
model.wv.allocate_vecattrs( model.wv.get_index( model.wv.intersect_word2vec_format( model.wv.n_similarity( model.wv.similar_by_key( model.wv.vectors_lockf
model.wv.closer_than( model.wv.get_mean_vector( model.wv.key_to_index model.wv.next_index model.wv.similar_by_vector( model.wv.vectors_norm
model.wv.cosine_similarities( model.wv.get_normed_vectors() model.wv.load( model.wv.norms model.wv.similar_by_word( model.wv.vocab
model.wv.distance( model.wv.get_vecattr( model.wv.load_word2vec_format( model.wv.rank( model.wv.similarity( model.wv.wmdistance(
model.wv.distances( model.wv.get_vector( model.wv.log_accuracy( model.wv.rank_by_centrality( model.wv.similarity_unseen_docs( model.wv.word_vec(
model.wv.doesnt_match( model.wv.has_index_for( model.wv.log_evaluate_word_pairs( model.wv.relative_cosine_similarity( model.wv.sort_by_descending_frequency() model.wv.words_closer_than(
model.wv.evaluate_word_analogies( model.wv.index2entity model.wv.mapfile_path model.wv.resize_vectors( model.wv.unit_normalize_all()
>>>