ものづくりのブログ

うちのネコを題材にしたものづくりができたらいいなと思っていろいろ奮闘してます。

fasttextでWikipediaの情報を学習させみて、今年1年を振り返る中で浮かんだ言葉の類似語を探してみる(NLP)

2020年もあともう少し、 今年は世界中が大変な1年でした。今年は新型コロナウィルスとかあつもりとかアマビエとか鬼滅の刃とかいろいろあるけど、
これらの言葉がどんな言葉と類似しているかちょっとだけ興味深いです。

fasttextの準備

a1026302.hatenablog.com

作業内容

wikipediaの情報でデータ作成

wikipediaのダウンロード
$ git clone https://github.com/attardi/wikiextractor.git
日本語版wikipediaのテキストデータを取得

日本語のwikipedia情報ダウンロードページにアクセス

$ wget https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2
wikipediaデータ整形
$ python -m wikiextractor.WikiExtractor -b 500M -o path/to/cprpus ../jawiki-latest-pages-articles.xml.bz2
INFO: Preprocessing '../jawiki-latest-pages-articles.xml.bz2' to collect template definitions: this may take some time.
INFO: Preprocessed 100000 pages
INFO: Preprocessed 200000 pages
INFO: Preprocessed 300000 pages
INFO: Preprocessed 400000 pages
INFO: Preprocessed 500000 pages
INFO: Preprocessed 600000 pages
INFO: Preprocessed 700000 pages
INFO: Preprocessed 800000 pages
INFO: Preprocessed 900000 pages
INFO: Preprocessed 1000000 pages
INFO: Preprocessed 1100000 pages
INFO: Preprocessed 1200000 pages
INFO: Preprocessed 1300000 pages
INFO: Preprocessed 1400000 pages
INFO: Preprocessed 1500000 pages
INFO: Preprocessed 1600000 pages
INFO: Preprocessed 1700000 pages
INFO: Preprocessed 1800000 pages
INFO: Preprocessed 1900000 pages
INFO: Preprocessed 2000000 pages
INFO: Preprocessed 2100000 pages
INFO: Preprocessed 2200000 pages
INFO: Preprocessed 2300000 pages
INFO: Preprocessed 2400000 pages
INFO: Preprocessed 2500000 pages
INFO: Loaded 88533 templates in 819.9s
INFO: Starting page extraction from ../jawiki-latest-pages-articles.xml.bz2.
INFO: Using 3 extract processes.
INFO: Extracted 100000 articles (1112.8 art/s)
INFO: Extracted 200000 articles (1630.0 art/s)
INFO: Extracted 300000 articles (1888.5 art/s)
INFO: Extracted 400000 articles (2153.1 art/s)
INFO: Extracted 500000 articles (2304.0 art/s)
INFO: Extracted 600000 articles (2445.7 art/s)
INFO: Extracted 700000 articles (2452.0 art/s)
INFO: Extracted 800000 articles (2439.3 art/s)
INFO: Extracted 900000 articles (2442.4 art/s)
INFO: Extracted 1000000 articles (2417.4 art/s)
INFO: Extracted 1100000 articles (2355.2 art/s)
INFO: Extracted 1200000 articles (2371.2 art/s)
INFO: Extracted 1300000 articles (2052.3 art/s)
INFO: Extracted 1400000 articles (2117.0 art/s)
INFO: Extracted 1500000 articles (2223.3 art/s)
INFO: Extracted 1600000 articles (2205.4 art/s)
INFO: Extracted 1700000 articles (2269.3 art/s)
INFO: Extracted 1800000 articles (2475.3 art/s)
INFO: Extracted 1900000 articles (2410.3 art/s)
INFO: Finished 3-process extraction of 1997224 articles in 934.8s (2136.5 art/s)

wiki.txtを作成する。(ファイルを一つにまとめます。)

$ cat wiki_0* > wiki.txt
mecabで分かち書き
mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd/ -Owakati wiki.txt -o wiki_wakati.txt -b 32768

fasttextで評価

fasttext コマンドには以下のモードがあるようです。

モード 概要
supervised ラベル付きの教師データから分類器を学習
quantize ラベル付きの教師データから分類器を学習(メモリ使用量を減らすためモデルを量子化するバージョ)
test ラベル付きの教師データから分類器を学習
test-label 学習した分類器と任意のデータに対してラベルごとに精度と感度を算出
predict 学習した分類器で予測
predict-prob 学習した分類器で予測
skipgram skipgram アルゴリズムで単語ベクトルを学習
cbow cbow アルゴリズムで単語ベクトルを学習
print-word-vectors 学習した分類器と任意のデータに対してデータに含まれる単語のベクトルを出力
print-sentence-vectors 学習した分類器と任意のデータに対してデータに含まれる文章の平均ベクトルを出力
nn 学習した分類器に対して指定した単語の nearest neighbors を表示
analogies 学習した分類器に対して指定した 単語A - 単語B + 単語C の nearest neighbors を表示
dump 学習した分類器から情報をダンプ
skipgram アルゴリズムで単語ベクトルを学習

入力ファイルはパスの通るところに配置します。

$ ./fasttext skipgram -input ../wiki_wakati.txt -output wiki.model -dim 300

テスト評価

単語と単語の近さを比較

猫は人よりも犬に近い。。。けど。。。人より草に近いのか。。。

ねこはひとよりも草に近い。。。のか。。。
>>> word_vecs.similarity('猫', '犬')
0.64618886
>>> word_vecs.similarity('猫', '人')
0.22936581
>>> word_vecs.similarity('猫', '魚')
0.3888138
>>> word_vecs.similarity('猫', '草')
0.32002676
>>> word_vecs.similarity('猫', '石')
0.17992988
特定の単語に似ているものを検索

結果はあとでのせてみよう。。。

$ python
Python 3.9.0 (default, Dec  9 2020, 00:39:07)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from gensim.models import KeyedVectors
>>> word_vecs = KeyedVectors.load_word2vec_format('~/fastText/wiki.model.vec', binary=False)
>>> [print (i) for i in word_vecs.wv.most_similar(positive=['アンジャッシュ'])]
('アンジャッシュ・', 0.9140708446502686)
('児嶋', 0.605518102645874)
('コンビ・ココリコ', 0.5987504720687866)
('ドランクドラゴン', 0.5953443050384521)
('ザコシショウ', 0.5933820009231567)
('ぎやはぎを', 0.5783674716949463)
('オアシズ', 0.5728097558021545)
('キングオブコメディ', 0.5653537511825562)
('ハリウッドザコシショウ', 0.5607682466506958)
('ケンドーコバヤシ', 0.5593159198760986)
>>> [print (i) for i in word_vecs.wv.most_similar(positive=['コロナ'])]
('ウイルス', 0.7096765041351318)
('新型', 0.7080405950546265)
('COVID', 0.6902177333831787)
('感染', 0.6101067662239075)
('インフルエンザ・パンデミック', 0.5907289385795593)
('インフルエンザ', 0.5904676914215088)
('ウイルスン', 0.5725395679473877)
('OVID', 0.5702617168426514)
('グローコロナ', 0.5675416588783264)
('2020', 0.5654521584510803)
>>> [print (i) for i in word_vecs.wv.most_similar(positive=['アマビエ'])]
('アマビコ', 0.7256805896759033)
('重る', 0.5930653214454651)
('ゃどくろ', 0.53180992603302)
('物尽し', 0.5190485715866089)
('パヨカカムイ', 0.496351957321167)
('アスキーアートキャラクター', 0.4954875111579895)
('妖怪', 0.4913427233695984)
('オヤシロ', 0.49104052782058716)
('コスプレキャラ', 0.4896015524864197)
('ゃんばろう', 0.48786661028862)

流行語大賞で類似語を探してみる

アベノマスク

>>> [print (i) for i in word_vecs.wv.most_similar(positive=['アベノマスク'])]
('布マスク', 0.8525460958480835)
('布製マスク', 0.7562093734741211)
('やるやる詐欺', 0.7026486992835999)
('だてマスク', 0.6979610919952393)
('買ってはいけない', 0.6913561820983887)
('ネット報道', 0.6889148950576782)
('桜を見る会', 0.6832566261291504)
('サージカルマスク', 0.6794693470001221)
('安倍内閣メールマガジン', 0.6772198677062988)
('定額給付金', 0.6766181588172913)

鬼滅の刃

>>> [print (i) for i in word_vecs.wv.most_similar(positive=['鬼滅の刃'])]
('かぐや様は告らせたい', 0.7571939826011658)
('竈門炭治郎', 0.7550870776176453)
('かぐや様は告らせたい~天才たちの恋愛頭脳戦~', 0.7512186765670776)
('長門有希ちゃんの消失', 0.7411877512931824)
('オトナアニメ', 0.7390203475952148)
('ゆらぎ荘の幽奈さん', 0.7386441826820374)
('NARUTO-ナルト-疾風伝', 0.7342469692230225)
('やがて君になる', 0.7341461181640625)
('TVアニメ', 0.7319309115409851)
('鬼灯の冷徹', 0.7310565710067749)

アマビエ

>>> [print (i) for i in word_vecs.wv.most_similar(positive=['アマビエ'])]
('アマビコ', 0.783353328704834)
('あま絵', 0.7148388624191284)
('妖怪', 0.6937016248703003)
('提灯お化け', 0.6908571720123291)
('萌え絵', 0.6823587417602539)
('今昔画図続百鬼', 0.6806477308273315)
('画図百鬼夜行', 0.6705069541931152)
('オジギビト', 0.6696372032165527)
('萌え寺', 0.6682267785072327)
('エボラちゃん', 0.6672184467315674)
ソロキャンプ
>>> [print (i) for i in word_vecs.wv.most_similar(positive=['ソロキャンプ'])]
('ガールズサマーキャンプ', 0.8228516578674316)
('空中キャンプ', 0.7962427139282227)
('どきどきキャンプ', 0.7840174436569214)
('スキーキャンプ', 0.7768822908401489)
('デイキャンプ', 0.7664369344711304)
('ルーキーキャンプ', 0.7661372423171997)
('ハワイキャンプ', 0.7620877027511597)
('スポーツキャンプ', 0.7614426016807556)
('西村キャンプ場', 0.7521942853927612)
('キャンプソング', 0.7495759725570679)