gensimでword2vec

とっくに書いたと勘違いしていたのですが、まだ記事にしていなかったことに気づいたので、今更ですがgensimを使って単語の埋め込みを得る方法を紹介します。

word2vec自体の説明はそのうち書きたいですが一旦こちらをご参照ください。
wikipedia: Word2vec

gensim自体はもともとトピックモデル用のライブラリだったようで、
公式サイトのタイトルがズバリ「gensim: Topic modelling for humans」となっています。
ただ自分はもっぱらword2vec(skip-gram/CBOW)の為に使っています。

せっかくなので、このあいだのlivedoorニュースコーパスでやってみましょう。

テキストデータを単語単位で分かち書きした物を「配列で」準備し、
渡してあげればそれだけで学習してくれます。
他のライブラリはスペース区切りの文字列などを受け取ることが多いので、配列で準備する点だけは注意が必要ですね。

今回はgensimの使い方がメインなので、最低限の前処理だけして学習用データを準備します。


import MeCab
import pandas as pd

tagger = MeCab.Tagger("-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd")


def mecab_tokenizer(text):
    # テキストを分かち書きする関数を準備する
    parsed_lines = tagger.parse(text).split("\n")[:-2]
    surfaces = [l.split('\t')[0] for l in parsed_lines]
    features = [l.split('\t')[1] for l in parsed_lines]
    # 原型を取得
    bases = [f.split(',')[6] for f in features]
    # 配列で結果を返す
    token_list = [b if b != '*' else s for s, b in zip(surfaces, bases)]
    # アルファベットを小文字に統一
    token_list = [t.lower() for t in token_list]
    return token_list


# コーパスの見込み (df["text"]にニュース記事本文が入る。)
df = pd.read_csv("./livedoor_news_corpus.csv")


# 不要な文字を消す
stop_chars = "\n,.、。()()「」 『 』[]【】“”!! ??—:・■●★▲▼"
for stop_char in stop_chars:
    df["text"] = df["text"].str.replace(stop_char, " ")

# ユニコード正規化
df["text"] = df["text"].str.normalize("NFKC")
# アルファベットを小文字に統一
df["text"] = df["text"].str.lower()

# 分かち書きしたデータを作成
sentences = df["text"].apply(mecab_tokenizer)

# 作成されたデータのサンプル
print(sentences[:5])
"""
0    [2005年, 11月, から, 翌, 2006年, 7月, まで, 読売新聞, にて, 連...
1    [アンテナ, を, 張る, ながら, 生活, を, する, て, いく, ば, いい, 2月...
2    [3月2日, より, 全国ロードショー, と, なる, スティーブン, スピルバーグ, の,...
3    [女優, の, 香里奈, が, 18日, 都内, で, 行う, れる, た, 映画, ガール...
4    [5日, 東京, 千代田区, の, 内幸町, ホール, にて, 映画, キャプテン, アメリ...
Name: text, dtype: object
"""

さて、このsentencesを学習データとしてモデルを訓練します。
アルゴリズムは skip-gramとCBOWがありますが、今回はski-gramで試します。
使い方は簡単で、モデルをインポートして、インスタンス作成するときにデータを渡すだけです。
skip-gramを使いたい場合はsg=1を指定します。(0はCBOW)


from gensim.models import Word2Vec

word2vec_model = Word2Vec(
        sentences,
        sg=1,
    )

人によっては、次のようにインポート方法が違いますが、結果は同じです。


from gensim.models import word2vec

word2vec_model = word2vec.Word2Vec(
        sentences,
        sg=1,
    )

モデルの種類を指定する sg 以外にも実際には多くの引数をとるので、主なもの(自分がよく設定するもの)紹介しておきます。
=の右に書いているのは初期値です。

– size=100, # 埋め込むベクトルの次元
– window=5, # 前後何単語を予測するかの幅
– min_count=5, # 出現頻度の低い単語の足切り基準
– max_vocab_size=None, # 最大語彙数
– workers=3, # 学習の多重度
– sg=0, # skip-gram: 1 , CBOW: 0
– hs=0,
– negative=5, # negative sampling における負例の個数
– iter=5, # 学習回数

学習済みのモデルは次のように保存できます。ついでに、読み込みにコードも紹介。


# モデルの保存
word2vec_model.save("word2vec.model")

# 読み込み
# word2vec_model = Word2Vec.load("word2vec.model")

さて、モデルができたところで、使っていきましょう。
詳細全然説明してませんが、 king – man + woman = queen などの演算ができるということで、
一時非常に有名になったので、以下の例でも雰囲気伝わるのではないかなと思います。
それぞれの詳細な挙動についてはまた改めて説明記事書きたいです。


# 単語ベクトルを得る。 次の二つの書き方は結果は同じ
word2vec_model.wv["パソコン"]
word2vec_model.wv.get_vector("パソコン")

# 類似度の高い単語を得る。 topn引数で個数を指定(デフォルト10)
word2vec_model.wv.most_similar("パソコン", topn=5)
"""
[('pc', 0.7659528851509094),
 ('ノート', 0.7527473568916321),
 ('windows', 0.7253533601760864),
 ('companion', 0.7214531302452087),
 ('macos x', 0.7181501388549805)]
"""

# 単語の足し算、引き算は positive, negative で引数を指定する
# 下の例は 俳優 - 男 + 女 = 女優
word2vec_model.wv.most_similar(positive=["俳優", "女"], negative=["男"], topn=1)
# [('女優', 0.7674037218093872)]

# 二つの単語の類似度を得る
print(word2vec_model.wv.similarity("巨人", "阪神"))
# 0.8579513

# 仲間はずれ探し。
print(word2vec_model.wv.doesnt_match(["ロッテ", "オリックス", "ヤクルト", "ソニー"]))
# ソニー

# 語彙の一覧を取得する
word2vec_model.wv.vocab.keys()

# 埋め込みベクトルを全て得る。 (サイズは 語彙数*埋め込み次元)
word2vec_model.wv.vectors

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です