word2vec と doc2vec

単語や文章を分散表現(意味が似たような単語や文章を似たようなベクトルとして表現)を取得します。

github

  • jupyter notebook形式のファイルはこちら

google colaboratory

  • google colaboratory で実行する場合はこちら

筆者の環境

筆者のOSはmacOSです。LinuxやUnixのコマンドとはオプションが異なります。

!sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.6
BuildVersion:	18G6032
!python -V
Python 3.8.5

基本的なライブラリをインポートしそのバージョンを確認しておきます。tensorflowとkerasuのversionも確認します。

%matplotlib inline
%config InlineBackend.figure_format = 'svg'

import matplotlib
import matplotlib.pyplot as plt
import scipy
import numpy as np

import tensorflow as tf
from tensorflow import keras

print('matplotlib version :', matplotlib.__version__)
print('scipy version :', scipy.__version__)
print('numpy version :', np.__version__)
print('tensorflow version : ', tf.__version__)
print('keras version : ', keras.__version__)
matplotlib version : 3.3.2
scipy version : 1.5.2
numpy version : 1.18.5
tensorflow version :  2.3.1
keras version :  2.4.0

テキストデータの取得

著作権の問題がない青空文庫からすべての作品をダウンロードしてきます。gitがかなり重いので、最新の履歴だけを取得します。

git clone --depth 1 https://github.com/aozorabunko/aozorabunko.git

実際のファイルはcardsにzip形式として保存されているようです。ディレクトリの個数を確認してみます。

!ls ./aozorabunko/cards/* | wc -l
   19636

zipファイルだけzipsに移動させます。

find ./aozorabunko/cards/ -name *.zip | xargs -I{} cp {} -t ./zips/
!ls ./zips/ | head -n 5
1000_ruby_2956.zip
1001_ruby_2229.zip
1002_ruby_20989.zip
1003_ruby_2008.zip
1004_ruby_2053.zip
!ls ./zips/ | wc -l
   16444

となり、16444個のzipファイルがある事が分かります。こちらをすべて解凍し、ディレクトリを移動させます。

for i in `ls`; do [[ ${i##*.} == zip ]] && unzip -o $i -d ../texts/; done

これで、textsというディレクトリにすべての作品のテキストファイルがインストールされました。

!ls ./texts/ | grep miyazawa
miyazawa_kenji_zenshu.txt
miyazawa_kenji_zenshuno_kankoni_saishite.txt
miyazawa_kenjino_sekai.txt
miyazawa_kenjino_shi.txt
!ls ./texts/ | grep ginga_tetsudo
ginga_tetsudono_yoru.txt

となり、宮沢賢治関連の作品も含まれていることが分かります。銀河鉄道の夜もあります。

銀河鉄道の夜を使ったword2vec

今回はすべてのテキストファイルを対象にするには時間がかかるので、同じ岩手県出身の、高校の先輩でもある宮沢賢治の作品を例に取りword2vecを試してみます。 しかし、ファイルの中身を見てみると、

%%bash
head ./texts/ginga_tetsudono_yoru.txt
��͓S���̖�
�{�򌫎�

-------------------------------------------------------
�y�e�L�X�g���Ɍ����L���ɂ‚��āz

�s�t�F���r
�i��j�k�\���s�������ӂ��t

�m���n�F���͎Ғ��@��ɊO���̐�����A�T�_�̈ʒu�̎w��
!nkf --guess ./texts/ginga_tetsudono_yoru.txt
Shift_JIS (CRLF)

となりshift_jisで保存されていることが分かります。

!nkf -w ./texts/ginga_tetsudono_yoru.txt > ginga.txt

と、ディレクトリを変更し、ファイル名も変更します。

%%bash
cat ginga.txt | head -n 25
銀河鉄道の夜
宮沢賢治

-------------------------------------------------------
【テキスト中に現れる記号について】

《》:ルビ
(例)北十字《きたじふじ》

[#]:入力者注 主に外字の説明や、傍点の位置の指定
   (数字は、JIS X 0213の面区点番号またはUnicode、底本のページと行数)
(例)※[#小書き片仮名ヰ、155-15]

 [#(…)]:訓点送り仮名
 (例)僕[#(ん)]とこ
-------------------------------------------------------

[#7字下げ]一、午后の授業[#「一、午后の授業」は中見出し]

「ではみなさんは、さういふふうに川だと云はれたり、乳の流れたあとだと云はれたりしてゐたこのぼんやりと白いものがほんたうは何かご承知ですか。」先生は、黒板に吊した大きな黒い星座の図の、上から下へ白くけぶった銀河帯のやうなところを指しながら、みんなに問をかけました。
カムパネルラが手をあげました。それから四五人手をあげました。ジョバンニも手をあげやうとして、急いでそのまゝやめました。たしかにあれがみんな星だと、いつか雑誌で読んだのでしたが、このごろはジョバンニはまるで毎日教室でもねむく、本を読むひまも読む本もないので、なんだかどんなこともよくわからないといふ気持ちがするのでした。
ところが先生は早くもそれを見附けたのでした。
「ジョバンニさん。あなたはわかってゐるのでせう。」
ジョバンニは勢よく立ちあがりましたが、立って見るともうはっきりとそれを答へることができないのでした。ザネリが前の席からふりかへって、ジョバンニを見てくすっとわらひました。ジョバンニはもうどぎまぎしてまっ赤になってしまひました。先生がまた云ひました。
「大きな望遠鏡で銀河をよっく調べると銀河は大体何でせう。」
%%bash
cat ginga.txt | tail -n 25
ジョバンニはそのカムパネルラはもうあの銀河のはづれにしかゐないといふやうな気がしてしかたなかったのです。
けれどもみんなはまだ、どこかの波の間から、
「ぼくずゐぶん泳いだぞ。」と云ひながらカムパネルラが出て来るか或ひはカムパネルラがどこかの人の知らない洲にでも着いて立ってゐて誰かの来るのを待ってゐるかといふやうな気がして仕方ないらしいのでした。けれども俄かにカムパネルラのお父さんがきっぱり云ひました。
「もう駄目です。落ちてから四十五分たちましたから。」
ジョバンニは思はずか〔け〕よって博士の前に立って、ぼくはカムパネルラの行った方を知ってゐますぼくはカムパネルラといっしょに歩いてゐたのですと云はうとしましたがもうのどがつまって何とも云へませんでした。すると博士はジョバンニが挨拶に来たとでも思ったものですか しばらくしげしげジョバンニを見てゐましたが
「あなたはジョバンニさんでしたね。どうも今晩はありがたう。」と叮ねいに云ひました。
 ジョバンニは何も云へずにたゞおじぎをしました。
「あなたのお父さんはもう帰ってゐますか。」博士は堅く時計を握ったまゝまたきゝました。
「いゝえ。」ジョバンニはかすかに頭をふりました。
「どうしたのかなあ、ぼくには一昨日大へん元気な便りがあったんだが。今日あ〔〕たりもう着くころなんだが。船が遅れたんだな。ジョバンニさん。あした放課后みなさんとうちへ遊びに来てくださいね。」
さう云ひながら博士は〔〕また川下の銀河のいっぱいにうつった方へじっと眼を送りました。ジョバンニはもういろいろなことで胸がいっぱいでなんにも云へずに博士の前をはなれて早くお母さんに牛乳を持って行ってお父さんの帰ることを知らせやうと思ふともう一目散に河原を街の方へ走りました。



底本:「【新】校本宮澤賢治全集 第十一巻 童話※[#ローマ数字4、1-13-24] 本文篇」筑摩書房
   1996(平成8)年1月25日初版第1刷発行
※底本のテキストは、著者草稿によります。
※底本では校訂及び編者による説明を「〔 〕」、削除を「〔〕」で表示しています。
※「カムパネルラ」と「カンパネルラ」の混在は、底本通りです。
※底本は新字旧仮名づかいです。なお拗音、促音の小書きは、底本通りです。
入力:砂場清隆
校正:北川松生
2016年6月10日作成
青空文庫作成ファイル:
このファイルは、インターネットの図書館、青空文庫(http://www.aozora.gr.jp/)で作られました。入力、校正、制作にあたったのは、ボランティアの皆さんです。

となり、ファイルの先頭と、末尾に参考情報が載っているほかは、ちゃんとテキストとしてデータが取れている模様です。 先ず、この辺の前処理を行います。

import re

with open('ginga.txt', mode='r') as f:
  all_sentence = f.read()

全角、半角の空白、改行コード、縦線(|)をすべて削除します。正規表現を利用します。

all_sentence = all_sentence.replace(" ", "").replace(" ","").replace("\n","").replace("|","")

《》で囲まれたルビの部分を削除します。正規表現を利用します。

all_sentence = re.sub("《[^》]+》", "", all_sentence)

———-の部分で分割を行い、2番目の要素を取得します。

all_sentence = re.split("\-{8,}", all_sentence)[2]

次に、「。」で分割し、文ごとにリストに格納します。

sentence_list = all_sentence.split("。")
sentence_list = [ s + "。" for s in sentence_list]
sentence_list[:5]
['[#7字下げ]一、午后の授業[#「一、午后の授業」は中見出し]「ではみなさんは、さういふふうに川だと云はれたり、乳の流れたあとだと云はれたりしてゐたこのぼんやりと白いものがほんたうは何かご承知ですか。',
 '」先生は、黒板に吊した大きな黒い星座の図の、上から下へ白くけぶった銀河帯のやうなところを指しながら、みんなに問をかけました。',
 'カムパネルラが手をあげました。',
 'それから四五人手をあげました。',
 'ジョバンニも手をあげやうとして、急いでそのまゝやめました。']

最初の文は不要なので削除します。

sentence_list = sentence_list[1:]
sentence_list[:5]
['」先生は、黒板に吊した大きな黒い星座の図の、上から下へ白くけぶった銀河帯のやうなところを指しながら、みんなに問をかけました。',
 'カムパネルラが手をあげました。',
 'それから四五人手をあげました。',
 'ジョバンニも手をあげやうとして、急いでそのまゝやめました。',
 'たしかにあれがみんな星だと、いつか雑誌で読んだのでしたが、このごろはジョバンニはまるで毎日教室でもねむく、本を読むひまも読む本もないので、なんだかどんなこともよくわからないといふ気持ちがするのでした。']

となり、不要な部分を削除し、一文ごとにリストに格納できました。前処理は終了です。

janomeによる形態素解析

janomeは日本語の文章を形態素ごとに分解する事が出来るツールです。同じようなツールとして、MecabやGinzaなどがあります。一長一短があると思いますが、ここではjanomeを利用します。

word2vecには文ごとに単語分割した行列が必要なので、それをword_per_sentenceとして取得します。また、全単語をリスト化したword_listも作っておきます。

また、何も考えずに形態素解析を行うと、「の」や「は」などの助詞が多く含まれてしまうので、「名詞」と「動詞」だけに限定します。

from janome.tokenizer import Tokenizer

t = Tokenizer()

word_list = []
word_per_sentence_list = []

# 名詞と動詞だけを取得する
def get_words_by_janome(sentence):
  tokens = t.tokenize(sentence)
  return [token.base_form for token in tokens if token.part_of_speech.split(',')[0] in['動詞', '名詞']]

for sentence in sentence_list:
  word_list.extend(get_words_by_janome(sentence))
  word_per_sentence_list.append(get_words_by_janome(sentence))

中身を少し見てみます。想定通りそれぞれの配列に単語が格納されているのが分かります。

word_per_sentence_list[:5]
[['先生',
  '黒板',
  '吊す',
  '星座',
  '図',
  '上',
  '下',
  'けぶる',
  '銀河',
  '帯',
  'やう',
  'ところ',
  '指す',
  'みんな',
  '問',
  'かける'],
 ['カムパネルラ', '手', 'あげる'],
 ['四', '五', '人', '手', 'あげる'],
 ['ジョバンニ', '手', 'あげる', 'やう', '急ぐ', 'やめる'],
 ['あれ',
  'みんな',
  '星',
  'いつか',
  '雑誌',
  '読む',
  'の',
  'このごろ',
  'ジョバンニ',
  '毎日',
  '教室',
  '本',
  '読む',
  'ひま',
  '読む',
  '本',
  'こと',
  'わかる',
  '気持ち',
  'する',
  'の']]
word_list[:10]
['先生', '黒板', '吊す', '星座', '図', '上', '下', 'けぶる', '銀河', '帯']

単語のカウント

単語のカウントを行い、出現頻度の高いベスト10を抽出してみます。名詞のみに限定した方が良かったかもしれません。

import collections

count = collections.Counter(word_list)
count.most_common()[:10]
[('する', 258),
 ('ゐる', 248),
 ('やう', 222),
 ('の', 209),
 ('ジョバンニ', 191),
 ('ひる', 137),
 ('見る', 114),
 ('なる', 112),
 ('人', 102),
 ('カムパネルラ', 100)]

「銀河」と「ジョバンニ」がどれぐらい含まれているかカウントしてみます。

dict(count.most_common())['銀河']
25
dict(count.most_common())['ジョバンニ']
191

gensimに含まれるword2vecを用いた学習

word2vecを用いて、word_listの分散表現を取得します。使い方はいくらでも検索できますので、ここでは割愛します。文章ごとの単語のリストを渡せば、ほぼ自動的に分散表現を作ってくれます。

from gensim.models import word2vec

model = word2vec.Word2Vec(word_per_sentence_list, size=100, min_count=5, window=5, iter=1000, sg=0)

分散行列

得られた分散表現を見てみます。

model.wv.vectors
array([[-0.1085711 , -0.7295571 ,  0.12008803, ..., -0.54713595,
         0.25876907, -0.99029714],
       [ 0.84538716, -0.07945182,  0.585292  , ..., -0.54458576,
        -1.3785691 , -0.7405585 ],
       [-0.3068329 ,  0.9396992 ,  0.20060548, ..., -1.2855539 ,
        -0.2027892 , -0.24656042],
       ...,
       [ 1.154139  , -1.807011  , -3.8160653 , ..., -1.1014802 ,
        -1.8305504 , -1.1464196 ],
       [-2.7170303 ,  1.7595738 , -2.5706198 , ..., -4.1118627 ,
        -1.3364341 , -1.5060377 ],
       [ 0.22748926,  2.3822339 ,  0.08786247, ...,  0.1526236 ,
         2.072494  ,  0.04411798]], dtype=float32)

分散行列の形状確認

408個の単語について、100次元のベクトルが生成されました。

model.wv.vectors.shape
(408, 100)

この分散表現の中で、「銀河」がどういう表現になっているか確認します。

model.wv.__getitem__("銀河")
array([-0.60647625,  2.0173557 , -0.12664434, -1.4640857 , -0.17778993,
        1.6508715 , -2.7586727 , -2.3078275 , -2.424162  ,  0.20204756,
        3.3374844 ,  1.2588876 ,  2.4461162 , -3.1263301 , -2.4787934 ,
       -0.35571855, -0.49893048, -0.5236631 , -0.13757144,  2.1490831 ,
        0.3766181 ,  2.45091   , -0.05705294,  2.0727618 , -1.4372207 ,
       -0.40425998, -0.02195518, -0.04408328, -2.8960073 ,  0.5383666 ,
       -2.9297552 , -2.540225  ,  1.6428277 , -0.08052313, -1.3399279 ,
       -0.77334726,  1.9164326 ,  2.2334647 ,  3.0947473 ,  2.263199  ,
       -2.2468755 ,  1.1897175 , -2.7903354 , -0.20981236,  2.3498356 ,
        3.364441  ,  1.5565808 , -0.9524172 , -1.021568  , -3.0035462 ,
        2.9526875 ,  3.3491218 , -2.04989   , -0.11580631,  3.1596048 ,
        0.53184134, -0.5069683 ,  2.0982676 ,  1.6006128 ,  0.13060321,
       -1.7611482 , -2.0391207 ,  1.9682236 ,  0.5594682 , -0.24885197,
        1.978357  , -2.0844686 ,  0.03066224,  2.698693  , -1.7572972 ,
        1.2543527 ,  1.7763172 ,  0.29018798,  0.45022094,  1.3718299 ,
       -1.1919422 ,  1.1058612 , -3.5015378 , -0.6096107 ,  1.5871202 ,
        0.02774499,  1.4241607 , -1.2940854 ,  0.794197  ,  2.7273803 ,
        5.944245  ,  1.9882361 ,  1.5752172 ,  1.2644691 , -0.06811355,
        1.0618243 ,  0.5446952 , -2.636908  ,  1.2896786 , -1.123268  ,
       -0.59733117,  1.1447626 ,  3.1397603 ,  1.8445805 , -2.2756069 ],
      dtype=float32)

cos類似度による単語抽出

ベクトルの内積を計算することにより、指定した単語に類似した単語をその$\cos$の値と一緒に抽出する事ができます。

model.wv.most_similar("銀河")
[('ステーション', 0.37244850397109985),
 ('けむる', 0.2560712695121765),
 ('くら', 0.23860865831375122),
 ('草', 0.23003369569778442),
 ('一つ', 0.22825516760349274),
 ('これ', 0.22280968725681305),
 ('波', 0.22131246328353882),
 ('森', 0.2179364413022995),
 ('何', 0.2119046151638031),
 ('ザネリ', 0.20720958709716797)]
model.wv.most_similar("ジョバンニ")
[('云', 0.34611761569976807),
 ('カムパネルラ', 0.2926814556121826),
 ('博士', 0.2849109172821045),
 ('思ふ', 0.279746413230896),
 ('ゐる', 0.257301390171051),
 ('足', 0.2518441081047058),
 ('する', 0.24245725572109222),
 ('ぼんやり', 0.23133447766304016),
 ('車掌', 0.22958970069885254),
 ('屋', 0.2216600775718689)]

単語ベクトルによる演算

足し算するにはpositiveメソッドを引き算にはnegativeメソッドを利用します。

まず、「銀河+ジョバンニ」を計算します。

model.wv.most_similar(positive=["銀河", "ジョバンニ"])
[('ステーション', 0.35201749205589294),
 ('ぼんやり', 0.26429280638694763),
 ('けむる', 0.25039511919021606),
 ('何', 0.2481682002544403),
 ('あげる', 0.2395499348640442),
 ('歩く', 0.23418119549751282),
 ('云', 0.23226001858711243),
 ('はる', 0.22393637895584106),
 ('地図', 0.22199329733848572),
 ('誰', 0.22106748819351196)]

次に「銀河+ジョバンニー家」を計算します。

model.wv.most_similar(positive=["銀河", "ジョバンニ"], negative=["家"])
[('すき', 0.28036734461784363),
 ('けむる', 0.2678922414779663),
 ('ぼんやり', 0.2661510407924652),
 ('あたる', 0.23386208713054657),
 ('ステーション', 0.2286910116672516),
 ('眼', 0.22204485535621643),
 ('はる', 0.21921753883361816),
 ('方', 0.21217162907123566),
 ('見える', 0.21174871921539307),
 ('あれ', 0.207304909825325)]

高校の先輩ではありながら、私は宮沢賢治の作品は読んだ事がないので、単語の演算の結果は感覚と合っていますでしょうか?

doc2vec

次に文章ごとに分散表現を作成できるdoc2vecを利用して、文章語との類似度を計算してみます。文章毎にタグ付けされたTaggedDocumentを作成します。

from gensim.models.doc2vec import Doc2Vec
from gensim.models.doc2vec import TaggedDocument

tagged_doc_list = []

for i, sentence in enumerate(word_per_sentence_list):
  tagged_doc_list.append(TaggedDocument(sentence, [i]))

print(tagged_doc_list[0])
TaggedDocument(['先生', '黒板', '吊す', '星座', '図', '上', '下', 'けぶる', '銀河', '帯', 'やう', 'ところ', '指す', 'みんな', '問', 'かける'], [0])

doc2vecもgensimのメソッドを呼び出すだけです。

model = Doc2Vec(documents=tagged_doc_list, vector_size=100, min_count=5, window=5, epochs=20, dm=0)

このモデルを利用して、入力した文章の分散表現を取得することが出来ます。以下では、word_per_sentence_list[0]のベクトルを取得しています。

word_per_sentence_list[0]
['先生',
 '黒板',
 '吊す',
 '星座',
 '図',
 '上',
 '下',
 'けぶる',
 '銀河',
 '帯',
 'やう',
 'ところ',
 '指す',
 'みんな',
 '問',
 'かける']
model.docvecs[0]
array([-0.06444998, -0.03596994,  0.0227333 ,  0.07276238, -0.00363343,
       -0.04119626,  0.02736741,  0.02613232,  0.04667201, -0.06793695,
       -0.0116343 ,  0.06310435, -0.00173872, -0.03437345,  0.00554367,
        0.0212866 ,  0.04885085, -0.04512009,  0.00058356, -0.1749456 ,
       -0.05576846,  0.01806886,  0.05424768, -0.11315122,  0.01383568,
        0.01009082, -0.07640994, -0.0451671 ,  0.02734458,  0.0323933 ,
       -0.0051905 ,  0.02434624,  0.16169837, -0.1276576 , -0.10116552,
       -0.04519976,  0.02329508,  0.14243364, -0.10175437, -0.11279111,
       -0.02039773,  0.01978061, -0.04506303,  0.03735163,  0.06236678,
       -0.02610403,  0.08709168,  0.04676996, -0.05390077,  0.03777384,
        0.09418248, -0.01249803,  0.05934777,  0.03127318, -0.03615933,
       -0.02909052, -0.03908448, -0.01020512, -0.01477503,  0.05161656,
        0.07686085,  0.03361001,  0.03777939, -0.04005695, -0.09531841,
        0.03033048, -0.09612833,  0.02519404, -0.03649573, -0.0126434 ,
       -0.0775796 ,  0.0704511 ,  0.0151057 , -0.0610601 ,  0.04034737,
        0.05117826,  0.06670614,  0.03593067, -0.03789723,  0.0014505 ,
       -0.1049965 , -0.02339783, -0.01323754, -0.04068379,  0.02191896,
       -0.08311725,  0.0096609 , -0.10904255, -0.00327289, -0.02505866,
       -0.02644212,  0.14420451, -0.06790182,  0.08442814,  0.01060481,
       -0.03577584,  0.02106708,  0.02913176,  0.03764822,  0.0865751 ],
      dtype=float32)

また、word2vecと同様、most_similarで類似度が高い文章のIDと類似度を取得することが出来ます。

# 文章IDが0の文章と似た文章とその内積を得ることが出来る。
model.docvecs.most_similar(0)
[(1117, 0.9988083243370056),
 (223, 0.9987501502037048),
 (570, 0.9987390041351318),
 (206, 0.998712956905365),
 (143, 0.9986640214920044),
 (226, 0.9986582398414612),
 (141, 0.9986466765403748),
 (47, 0.9986271262168884),
 (189, 0.9986019134521484),
 (808, 0.9985982179641724)]
for p in model.docvecs.most_similar(0):
  print(word_per_sentence_list[p[0]])
['ジョバンニ', 'いろいろ', 'こと', '胸', '云', '博士', '前', 'はなれる', 'お母さん', '牛乳', '持つ', '行く', 'お父さん', '帰る', 'こと', '知る', 'せる', 'やう', '思ふ', '一目散', '河原', '街', '方', '走る']
['#', '7', '字', '下げ', '六', '銀河', 'ステーション', '[#「〔', '六', '銀河', 'ステーション', '見出し', 'ジョバンニ', 'うし', 'ろ', '天気', '輪', '柱', 'いつか', 'ぼんやり', 'する', '三角', '標', '形', 'なる', '蛍', 'やう', 'ぺかぺか', '消える', 'ともる', 'する', 'ゐる', 'の', '見る']
['窓', '外', '足', 'ふんばる', '見上げる', '鷺', '捕る', '支度', 'する', 'ゐる', 'の', '思う', '急ぐ', 'そっち', '見る', '外', 'はいち', 'めん', '砂子', 'すゝ', 'きの', '波', '鳥', '捕る', 'いせる', 'なか', '尖る', '帽子', '見える']
['#', '7', '字', '下げ', '五', '天気', '輪', '柱', '[#「〔', '五', '天気', '輪', '柱', '見出し', '牧場', 'うし', 'ろ', '丘', 'なる', '平ら', '頂上', '北', '大熊', '星', '下', 'ふだん', '連', '見える']
['ジョバンニ', '電', '燈', '方', '下りる', '行く', 'いま', 'ばける', 'もの', 'やう', 'ぼんやり', 'うし', 'ろ', '引く', 'ゐる', 'ジョバンニ', '影', 'うし', 'なる', '足', 'あげる', '手', '振る', 'ジョバンニ', '横', '方', 'はる', '来る', 'の']
['どこ', 'ふしぎ', '声', '銀河', 'ステーション', '銀河', 'ステー', 'ション', '云', 'ふる', '声', 'する', '思ふ', '眼', '前', 'なる', '億', '万', '蛍烏賊', '火', '一', 'ぺん', '化石', 'する', 'せる', '中', '沈める', '工合', 'ダイアモンド', '会社', 'ねだる', 'なる', 'ため', '穫る', 'れる', 'ふり', 'する', '置く', '金剛石', '誰か', 'ひる', 'くり', 'する', 'ばら', '撒く', '風', '眼', '前', 'する', 'あう', 'なる', 'ジョバンニ', '思ふ', '何', 'ん', '眼', '擦る', 'しまふ']
['#', '7', '字', '下げ', '四', 'ケンタウル', '祭', '夜', '#「〔', '四', 'ケンタウル', '祭', '夜', '見出し', 'ジョバンニ', '口笛', '吹く', 'ゐる', 'やう', '口', '付き', '檜', 'まっ黒', 'ならぶ', '町', '坂', '下りる', '来る', 'の']
['#', '7', '字', '下げ', '二', '活版', '所', '#「〔', '二', '活版', '所', '見出し', 'ジョバンニ', '学校', '門', '出る', 'とき', '組', '七', '八', '人', '家', '帰る', 'カムパネルラ', 'まん中', 'する', '校庭', '隅', '桜', '木', 'ところ', '集まる', 'ゐる']
['十字', 'なる', '町', 'の', 'まがる', 'する', 'ふる', '橋', '行く', '方', '雑貨', '店', '前', '影', 'ぼんやり', 'シャツ', '入り乱れる', '六', '七', '人', '生徒', 'ら', '口笛', '吹く', '笑う', 'する', 'めいめい', '烏瓜', '燈火', '持つ', 'やる', '来る', 'の', '見る']
['世界', '交響楽', '地平線', 'はて', '湧く', 'まっ黒', '野原', 'なか', '一', '人', 'インデアン', '鳥', '羽根', '頭', 'たくさん', '石', '腕', '胸', 'かざる', '弓', '矢', '番', '一目散', '汽車', '追う', '来る', 'の']

[‘先生’,‘黒板’,‘吊す’,‘星座’,‘図’,‘上’,‘下’,‘けぶる’,‘銀河’,‘帯’,‘やう’,‘ところ’,‘指す’,‘みんな’,‘問’,‘かける’] という文章と、同じ文章を抽出していますが、どうでしょうか?

一通り、word2vecを用いた分散表現の取得から、doc2vecまでやってみました。言葉で数学的な演算が出来るというのは、やはり画期的な事なんだと思います。考えた人はすごいです。実際の業務に利用するには、wikipediaなどの巨大なデータセットから既に学習済みのモデルを利用する事が多いと思いますが、カスタムしたい場合など一から自前で作成する場合もあります。