All Articles

初心者がword2vecの入門をいくつかやってみた

wikiのワード

【Python】Word2Vecの使い方 を参考に進めました

準備

wikiのデータダウンロードから加工までが想像以上に時間がかかります。。。

curl https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2 -o input/jawiki-latest-pages-articles.xml.bz2
git clone https://github.com/attardi/wikiextractor.git
cd wikiextractor
python WikiExtractor.py ../input/jawiki-latest-pages-articles.xml.bz2
find text/ | grep wiki | awk '{system("cat "$0" >> ../input/wiki.txt")}'
cd ..
mecab -Owakati ./input/wiki.txt -o ./input/wiki_wakati.txt
nkf -w --overwrite ./input/wiki_wakati.txt

実行コマンド

以下のコマンドを実行することで 鉄道 に近いワードを出すことができます。 make_model(False) の箇所を True に変えることで、 モデルの作成が行えます(こちらも時間がかかります)。

from gensim.models import word2vec
import logging
  
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
  
  
def make_model(flg=False):
    if flg:
        sentences = word2vec.Text8Corpus('./input/wiki_wakati.txt')
        model = word2vec.Word2Vec(sentences, size=200, min_count=20, window=15)
        model.save("./models/wiki.model")
  
  
if __name__ == "__main__":
    make_model(False)
    model = word2vec.Word2Vec.load("./models/wiki.model")
    results = model.wv.most_similar(positive=['鉄道'])
    for result in results:
        print(result)
$ pytton train1.py
...
('軽便鉄道', 0.6504012942314148)
('国鉄', 0.6245941519737244)
('PRR', 0.60747230052948)
('路線', 0.5936535596847534)
('支線', 0.5888773202896118)
('ローカル線', 0.5758099555969238)
('インターアーバン', 0.5740399956703186)
('狭軌', 0.5734384059906006)
('省線', 0.5698161721229553)
('運輸', 0.5681511163711548)

三四郎

15分でできる日本語Word2Vec たを参考に進めました

準備

wget http://www.aozora.gr.jp/cards/000148/files/794_ruby_4237.zip
unzip 794_ruby_4237.zip
mkdir input
mv sanshiro.txt input
pip install -r requirements.txt

requirements.txt

janome
gensim

実行コマンド

train.py

import codecs
import re
from janome.tokenizer import Tokenizer
from gensim.models import word2vec
    
# Tokenneizerインスタンスの生成
t = Tokenizer()
    
    
def shaping():
    # ファイル読込み、内部表現化
    f = codecs.open('input/sanshiro.txt', "r", "sjis")
    text = f.read()
    f.close()
    # ファイル整形
    # ヘッダ部分の除去
    text = re.split('\-{5,}', text)[2]
    # フッタ部分の除去
    text = re.split('底本:', text)[0]
    # | の除去
    text = text.replace('|', '')
    # ルビの削除
    text = re.sub('《.+?》', '', text)
    # 入力注の削除
    text = re.sub('[#.+?]', '', text)
    # 空行の削除
    text = re.sub('\n\n', '\n', text)
    text = re.sub('\r', '', text)
    return text
    
def extract_words(text):
    tokens = t.tokenize(text)
    return [token.base_form for token in tokens
            if token.part_of_speech.split(',')[0] in ['名詞', '動詞']]
    
if __name__ == "__main__":
    text = shaping()
    sentences = text.split('。')
    word_list = [extract_words(sentence) for sentence in sentences]
    model = word2vec.Word2Vec(word_list, size=100, min_count=5, window=5, iter=100)
    
    # size: 圧縮次元数
    # min_count: 出現頻度の低いものをカットする
    # window: 前後の単語を拾う際の窓の広さを決める
    # iter: 機械学習の繰り返し回数(デフォルト:5)十分学習できていないときにこの値を調整する
    # model.wv.most_similarの結果が1に近いものばかりで、model.dict['wv']のベクトル値が小さい値ばかりの
    # ときは、学習回数が少ないと考えられます。
    # その場合、iterの値を大きくして、再度学習を行います。
    
    ret = model.wv.most_similar(positive=['世間'])
    for item in ret:
        print(item[0], item[1])
$ python train.py
自己 0.5558735728263855
喝采 0.5495895147323608
外国 0.5363900661468506
決心 0.5256530046463013
聞こえる 0.5240170359611511
社会 0.5110801458358765
堪える 0.5018360614776611
える 0.49730929732322693
賛成 0.49427175521850586
今日 0.4933317303657532

wiki(特定のページ)

gensimのWord2Vecを使ってみる。 を参考に進めました

準備

brew install mecab
brew install swig
pip install -r requirements.txt

requirements.txt

lxml
mecab-python3
bs4

実行コマンド

初回実行時のみ make_file の変数 make_file_flgTrueにしてください。 (引数で帰るパターンにしようと思って途中でやめてます。。。笑)

train.py

import MeCab
import codecs
import urllib.parse as parser
import urllib.request as request
from bs4 import BeautifulSoup
from gensim.models import word2vec

tagger = MeCab.Tagger("-Owakati")
link = "https://ja.wikipedia.org/wiki/"
keyword = ["織田信長", "徳川家康", "豊臣秀吉",
           "伊達政宗", "武田信玄", "上杉謙信",
           "明智光秀", "島津義弘", "北条氏康",
           "長宗我部元親", "毛利元就", "真田幸村",
           "立花宗茂", "石田三成", "浅井長政"]

corpus = []


def make_file(make_file_flg=False):
    if not make_file_flg:
        return
    for word in keyword:
        # 戦国大名の記事をダウンロード
        with request.urlopen(link + parser.quote_plus(word)) as response:
            # responseはhtmlのformatになっている
            html = response.read().decode('utf-8')

            soup = BeautifulSoup(html, "lxml")
            # <p>タグを取得
            p_tags = soup.find_all('p')
            for p in p_tags:
                corpus.append(tagger.parse(p.text).strip())

    with codecs.open("input/pwiki.txt", "w", "utf-8") as f:
        f.write("\n".join(corpus))


if __name__ == "__main__":
    make_file()
    with codecs.open("input/pwiki.txt", "r", "utf-8") as f:
        corpus = f.read().splitlines()

    corpus = [sentence.split() for sentence in corpus]
    # モデルを作る
    model = word2vec.Word2Vec(corpus, size=200, min_count=20, window=10)
    # model.save("models/pwiki.model")
    # model = word2vec.Word2Vec.load("models/pwiki.model")

    similar_words = model.wv.most_similar(positive=["尾張"], topn=9)
    print(*[" ".join([v, str("{:.2f}".format(s))]) for v, s in similar_words], sep="\n")
    print(model.wv.similarity(w1="尾張", w2="天正"))
$ python train3.py
直 1.00
諏訪 1.00
勝 1.00
秀 1.00
信孝 1.00
一益 1.00
柴田 1.00
昌 1.00
続い 1.00
0.5121256

やって見た感想

それぞれデータの前処理が違うがやり方は同じで、 しかもwikiのデータダウンロードが遅さを除けばかなりお手軽に実装することができた。 何を学習データとして使うかがポイントになりそう。 あと使う前のイメージとの相違でいうと、 学習データにある単語しか扱えないこと。 考えてみれば当然だがなんでも単語の足し算ができる訳ではなかった。