ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

sentencepieceとgensimとツイートデータを使ってword2vecを訓練

ランダムに収集したツイートデータが6000万件あったようなので、そのデータを使ってgensimのword2vecモデルを作りました。このモデルは、次回の記事でツイート分類タスクに使おうと思いますが、どんな感じのword2vecになったのか、その生成過程も含めて書いておきます。

TL;DR

訓練済みモデルは以下からダウンロードできます: https://github.com/sugiyamath/word2vec_japanese_twitter

これを使えば、以下のように「ツイッターらしいword2vec」でいろいろできます。

オタク + 政治 = ネトウヨ

model.wv.most_similar(["オタク", "政治"])[0]
('ネトウヨ', 0.6881198883056641)

マスコミ + 嘘 - 真実 = [パヨク, ネトウヨ]

model.wv.most_similar(["マスコミ", "嘘"], ["真実"])[:2]
[('パヨク', 0.501679539680481), ('ネトウヨ', 0.49308836460113525)]

パイプライン

  1. Twitter Stream APIなどを使って常時データを取得し、jsonをファイルへ保存していく。
  2. 保存しているjsonファイルからツイートのみを取得し、1ツイート1行になるように整形してtxtファイルへ出力。
  3. txtファイルからsentencepieceを学習する。
  4. 学習したsentencepieceを使ってtxtファイルをtext8形式にトークナイズ。
  5. text8形式のデータをgensimのWord2Vecで訓練。
  6. 訓練したモデルを使う。

コード

まず、保存してあるjsonファイルからtxtファイルを生成するスクリプトを書きます。

import pandas as pd
from multiprocessing import Pool

def replace_nl(string):
    return ' '.join(string.split())

def write_them(d, outfile="tweet.txt"):
    with open(outfile, "a") as f:
        f.write('\n'.join(list(map(replace_nl, d['text']))))
        
def run_it(df_iter):
    while(True):
        try:
            for d in df_iter:
                try:
                    write_them(d)
                except:
                    pass
        except:
            print("iter_error")
            continue
        
    #pool = Pool(8)
    #pool.map(write_them, df_iter)
            
if __name__ == "__main__":
    df_iter = pd.read_json("../tweet.dat", chunksize=100, lines=True)
    run_it(df_iter)

tweet.txtが生成されます。

次に、sentencepieceをインストールして以下のコマンドを実行します。

mkdir sp
mv tweet.txt sp
spm_train --input=sp/tweet.txt --model_prefix=sp/sp --vocab_size=32000

sentencepieceのモデルが生成されるので、それを用いてtext8形式のデータを生成します。

if __name__ == "__main__":
    import sentencepiece as spm
    import re
    from tqdm import tqdm

    sp = spm.SentencePieceProcessor()
    sp.Load('sp/sp.model')
    
    regex_url = re.compile('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')
    regex_user = re.compile('@(\w){1,15}')

    with open("sp/tweet.txt", "r") as f:
        for i, line in tqdm(enumerate(f)):
            try:
                line = line.strip()
                line = re.sub(regex_url, "", line)
                line = re.sub(regex_user, "", line)
                data = ' '.join([l.replace("▁", "").replace("#","") for l in sp.EncodeAsPieces(line)])
                with open("tweet_ja.text8", "a") as g:
                    g.write(data+"\n")
            except:
                print("error: fail line {}".format(i))
                
    print("Done!")

tweet_ja.text8をgensimのWord2Vecで訓練します。

import logging
from gensim.models import word2vec

if __name__ == "__main__":
    logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
    sentences = word2vec.Text8Corpus('data/tweet_ja.text8')
    model = word2vec.Word2Vec(sentences, size=200, workers=8)
    model.save("word2vec_tweet.model")

Note: workers=8は使うcpu数を指定します。

モデルの使い方

以下のようにモデルを読み込んでください。

from gensim.models import word2vec
model = word2vec.Word2Vec.load("word2vec_japanese_twitter/model/w2v_gensim/word2vec_tweet.model")

あとはgensimのマニュアルでもみたらよろしいかと。 https://radimrehurek.com/gensim/models/word2vec.html

以下のQiita記事を見ればマニュアル読めないアホでも理解できるっぽいです。 https://qiita.com/kenta1984/items/93b64768494f971edf86

補足

ツイート6000万件というとデータがたくさんありそうな気がしますが、word2vecとsentencepieceがあまり精度が良くないので、もっとデータがあったほうがより良いモデルができると思います。

例えば、sentencepieceを使った影響だと思いますが、このword2vecのモデルは一般的な語を学習しませんでした(例えば、"人工知能"という語)

参考

[0] https://www.madopro.net/entry/sentencepiece_rnn_lm