【自然言語処理】gensimを使った単語の分散表現|python
今回は自然言語処理の本質となる「単語の分散表現」について解説したいと思います。
gensimというパッケージの使い方も説明しておりますので、初心者の方でも気軽に勉強できると思われます。
まずは、自然言語処理の基礎から学習したいという方は、以下のコンテンツなどをご覧くださいませ。
【自然言語処理】形態素解析で文章を単語に分ける | python
【自然言語処理】単語の出現頻度を可視化させてみましょう | python
Word2Vec
単語の分散表現とは、「単語を数値表現に処理すること」です。具体的には、単語を数百次元のベクトルで表すということです。
理由は、自然言語のままだとプログラムは単語が理解できないからです。
(意味を理解しているかのように処理できる→例えば、類似文書を出力できたりするというだけであり、本質的に「理解」できているわけではありません。理解しているように振る舞うことができている状態です。)
上のように、単語を低次元のベクトルで表すことで、単語の内積をとったときに内積が大きいほど単語が類似していると言えます。
正直と誠実の内積は大きく、類似している言えますね。逆に誠実と愚かの内積は小さく、あまり似ているとは言えません。
特徴的なのは、固定長のベクトルで表すということです。単語数が増えたりしても途中でベクトルの長さを変えなくて良いです。
では、どのようにしてベクトル表現を作ることができるのでしょうか?
必要なのはword2vecというソフトウェアとW2Vで使うモデルです。
word to vector 「単語をベクトルに」という意味ですね。
word2vecは、CBOWとSkip-gramというニューラルネットワークのモデルを持っています。
ニューラルネットワークの特徴を活かし、少量の学習で重みづけをしながら推論ベースで単語の分散表現を使います。
CBOWとSkip-gramがそれぞれどんなアルゴリズムなのかは、今回割愛し、gensimというパッケージを使って簡単に分散表現を実装します。
gensim
実際にモデルを実装するのは時間がかかるので、gensimというパッケージを使います。
ごちゃごちゃ説明する前に、まずは手を動かして実装します。
*gensimのバージョン4以降のコードです。4以前だとエラーになる場合がございます。
pip install gensim
まずは、ターミナル等でgensimパッケージをインストールします。
#ログ出力用のloggingをインポート
#学習モデルのword2vecをインポート
#コーパス読み取り用のText8Corpusをインポート
import logging
from gensim.models.word2vec import Word2Vec, Text8Corpus
次に、事前準備を行います。
Text8Corpusは、「word2vecのモデルが学習するための材料を読み込む準備をしている」という意味あいです。
ちなみにコーパスとは、学習用に準備された自然言語の集まりです。 ツイートの寄せ集めや、口コミなどが挙げられます。 多くのコーパスは、ネット上で公開されており、拡張子はcsv,tsv,jsonのいずれかです。
とはいえ、今回はtext8というコーパスを使っております。
text8とは、wikipediaから100MBほどの情報をとってきたコーパスであり、事前に前処理がされており、とても便利です。
引用や脚注、マークアップが削除されておりチュートリアルに最適のコーパスです。
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
学習の進捗を出力するためにlogginngを有効にしています。
次にコーパスを学習するコードを実行するとき、進捗状況がログとして残ります。
#sentencesという変数に、dataというフォルダのja.text8というコーパスを入れます
sentences = Text8Corpus('data/ja.text8')
#modelという変数に、word2vecというメソッドの内容を入れる
model = Word2Vec(sentences,vector_size=200,window = 5,sg = 1)
Word2Vecのメソッドの引数については以下の通りです。
sentences:学習したコーパス
vector_size:単語のベクトルの大きさ、200次元で表す。
window:ある単語からどれだけ離れた単語までを、ベクトルの値を決めるのに使うか
sg:Skip-gramのこと。sgを引数に設定しなければ、CBOWというモデルが自動的に使われます。
windowについて。 文脈を捉えようとする試みです。 skip-gramとは、ある単語を入力したときにその単語の周辺に出現しやすい単語を予測するアルゴリズムです。 例えば、More information about the competition law infringement which is being alleged.(訳:主張されている競争法違反についてのより多くの情報)という英文があったとき、「law」が入力されとしましょう。 window=1:lawの1単語横のcompetitionとinfringementのみを周辺語とする。 window=2:lawの2単語の横の、theとwhichまでを周辺語として認識する ‥のように、ある単語の近くに使われる周辺後の範囲を指定できる引数がwindowです。 ここでいう入力はlaw、出力はtheやcompetitionなどの周辺語です。
この学習をおこなった後に、単語の分散表現を行います。
教師あり学習ですね。
では、実際に単語がベクトル表現で表されている様子を見てみましょう。
#ここの単語はなんでもいいです。
model.wv["怪物"]
array([-5.74578755e-02, -1.19124033e-01, -1.81787744e-01, 1.86903462e-01, 1.65381879e-01, 2.36798257e-01, -1.30107075e-01, 3.90152395e-01, 1.01852752e-01, -8.27169567e-02, -6.67056069e-02, -5.73966444e-01, -1.36171535e-01, 1.13452889e-01, 1.75852852e-03, -3.45277429e-01, -1.00736953e-01, 2.91915268e-01, 5.56889474e-01, -1.98455632e-01, 1.00835897e-01, -6.33992180e-02, -3.19765136e-02, 1.01708815e-01, 2.36660004e-01, 1.12614043e-01, 5.85892461e-02, 8.11173618e-02, -9.91275385e-02, -3.17364126e-01, -6.93919435e-02, -4.95574117e-01, -3.08841437e-01, -2.40222767e-01, 2.21948028e-01, 1.11959100e-01, 4.99229282e-01, -7.00845718e-02, -1.19736470e-01, 1.01726227e-01, -1.47525758e-01, -2.46771365e-01, -1.37070790e-01, -1.50946453e-02, -1.06790014e-01, 3.09541076e-03, -1.69193015e-01, -1.86622903e-01, -4.02938165e-02, 7.58389458e-02, -3.47803712e-01, 3.10465805e-02, 1.76589325e-01, -3.53197515e-01, -1.80332750e-01, -4.03138064e-02, -3.73911649e-01, -6.13464564e-02, -1.52033523e-01, -3.16934586e-01, -2.34764814e-01, 3.93359751e-01, 5.86312532e-01, -3.41633081e-01, -2.46376306e-01, 1.26989171e-01, -9.40915346e-02, 3.99139374e-01, -1.65089726e-01, 4.19072151e-01, 6.07829876e-02, 1.06179565e-01, 4.72472236e-02, 1.99828684e-01, 6.52605072e-02, -1.73842371e-01, -1.37173027e-01, 1.89701304e-01, -5.92277050e-02, 1.22872457e-01, -1.80371001e-01, -5.07625818e-01, -1.37325585e-01, 5.96180484e-02, -2.78004229e-01, 5.77348378e-03, 7.11323619e-02, -2.67148893e-02, -3.67268711e-01, 1.68771341e-01, 1.54808447e-01, 1.81707501e-01, 4.54377621e-01, 9.34635103e-02, 3.23046535e-01, 4.73329842e-01, -1.42930478e-01, 1.23264633e-01, 3.01070839e-01, 3.45786810e-01, -3.91956151e-01, 4.61933732e-01, -3.72882754e-01, 1.67713445e-02, -5.25482714e-01, -1.14966765e-01, 1.74623236e-01, 1.35068953e-01, -1.61789119e-01, 7.90113360e-02, -2.00289473e-01, 6.86529733e-04, 6.93527341e-01, -1.36972427e-01, 2.62813240e-01, 2.07301527e-01, 1.94205627e-01, -2.12078780e-01, 2.55560368e-01, -1.82609782e-01, 8.27648938e-02, 4.66460139e-01, 3.18437457e-01, 4.16225851e-01, -2.05453694e-01, 3.59682620e-01, -3.96077670e-02, -3.20932359e-01, 2.56230286e-03, -4.56318706e-02, 1.00905336e-01, -9.58420113e-02, 7.88971707e-02, 1.28744289e-01, -1.64711624e-01, -1.10812716e-01, 2.99102161e-02, -3.05148184e-01, -2.09239557e-01, -3.22534107e-02, 4.67742473e-01, -1.44400299e-01, -6.30869269e-02, 8.20780024e-02, -2.45412365e-01, 2.94849128e-01, -3.49732965e-01, 3.70511785e-02, -2.10068405e-01, 2.61649966e-01, 1.86655343e-01, -4.90679234e-01, -2.46286392e-03, 4.26966995e-01, -1.41283661e-01, 3.87581527e-01, 9.11838189e-02, 4.76159483e-01, -3.73843729e-01, 1.61803663e-01, 5.25904596e-02, 2.04351395e-01, -1.55313119e-01, 2.07683161e-01, -8.89163315e-02, 5.64798154e-02, 2.78836310e-01, -1.51857331e-01, 6.23572059e-03, -1.40753210e-01, 4.47412394e-02, 2.64004946e-01, -1.23464219e-01, -2.25946411e-01, 5.99781722e-02, 2.68170714e-01, -3.28233808e-01, -2.37356871e-03, 2.20807701e-01, 1.01419672e-01, -6.47760108e-02, 6.10329323e-02, -4.84440848e-03, -7.82773644e-03, 1.82474703e-01, 3.66159141e-01, -2.34974295e-01, 7.10406452e-02, 1.00471126e-02, -6.01655506e-02, 3.18622947e-01, 1.03070073e-01, -2.12216407e-01, -2.31640190e-01, 2.92406708e-01, 1.32831737e-01, 4.96210121e-02, 7.60043487e-02, 2.77582139e-01, 5.50935082e-02], dtype=float32)
このように表すことができました。
最後に「怪物」と似ている単語はなんなのかgensimに聞いてみましょう。
model.wv.most_similar("怪物",topn=10)
[('幻影', 0.7531608939170837), ('雪男', 0.7460487484931946), ('異形', 0.7430112957954407), ('魔物', 0.7407946586608887), ('亡霊', 0.7351414561271667), ('怪異', 0.7347351312637329), ('悪夢', 0.7292711734771729), ('神通力', 0.7274906039237976), ('野良猫', 0.7251668572425842), ('アルテミス', 0.7198331952095032)]
似てるランキングトップ10をみたところ上の様になりました。
確かに我々人類が見てもそんなに違和感のない結果ですね。
単語の横にあるのは、冒頭でお話しした内積の値です。1に近いほど類似度が高いです。
文書に拡張すると文書自体の類似度が測れるというわけです。
この辺は、【python】コサイン類似度は高校数学の知識で理解できます!をご覧ください。