ホーム » Word2Vec

Word2Vec」カテゴリーアーカイブ

TensorFlow : TensorLayer : チュートリアル (4) Word2Vec

TensorFlow : TensorLayer : チュートリアル (4) Word2Vec (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 12/05/2018

* 本ページは、TensorLayer の以下のドキュメントの一部を翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

チュートリアル (4)

Word2Vec サンプルを実行する

チュートリアルのこのパートでは、単語群のための行列を訓練します、そこでは各単語は行列の一意の行ベクトルにより表すことができます。最後には、類似の単語は類似のベクトルを持つでしょう。それから単語群を 2-次元平面にプロット出力するとき、類似の単語は互いに近くに群がることになります。

python tutorial_word2vec_basic.py

総てが正しくセットアップされれば、最後に出力を得るでしょう。

 

単語埋め込みを理解する

単語埋め込み

何故ベクトル表現を使用することを望むのか、そしてベクトルをどのように計算するかを理解するために Colah のブログ Word Representations を読むことを強く勧めます。word2vec についてのより詳細は Word2vec Parameter Learning Explained で見つけられます。

基本的には、埋め込み行列の訓練は教師なし学習です。総ての単語が一意の ID で表わされるとき、それは埋め込み行列の行インデックスで、単語はベクトルに変換できて、それは意味をより良く表わすことができます。例えば、一定の male-female 差分ベクトルがあるようです: woman − man = queen – king, これはベクトルの 1 次元が性別を表わすことを意味します。

モデルは次のように作成できます。

# train_inputs is a row vector, a input is an integer id of single word.
# train_labels is a column vector, a label is an integer id of single word.
# valid_dataset is a column vector, a valid set is an integer id of single word.
train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])
valid_dataset = tf.constant(valid_examples, dtype=tf.int32)

# Look up embeddings for inputs.
emb_net = tl.layers.Word2vecEmbeddingInputlayer(
        inputs = train_inputs,
        train_labels = train_labels,
        vocabulary_size = vocabulary_size,
        embedding_size = embedding_size,
        num_sampled = num_sampled,
        nce_loss_args = {},
        E_init = tf.random_uniform_initializer(minval=-1.0, maxval=1.0),
        E_init_args = {},
        nce_W_init = tf.truncated_normal_initializer(
                          stddev=float(1.0/np.sqrt(embedding_size))),
        nce_W_init_args = {},
        nce_b_init = tf.constant_initializer(value=0.0),
        nce_b_init_args = {},
        name ='word2vec_layer',
    )

 

データセット反復と損失

Word2vec は訓練のためにネガティブ・サンプリングと Skip-Gram モデルを使用します。Noise-Contrastive Estimation (NCE) 損失は損失の計算を減じるのを助けることができます。Skip-Gram はコンテキストとターゲットを反対にして、そのターゲット単語から各コンテキスト単語を予測することを試みます。次のように訓練データを生成するために tl.nlp.generate_skip_gram_batch を使用します、tutorial_generate_text.py を見てください。

# NCE cost expression is provided by Word2vecEmbeddingInputlayer
cost = emb_net.nce_cost
train_params = emb_net.all_params

train_op = tf.train.AdagradOptimizer(learning_rate, initial_accumulator_value=0.1,
          use_locking=False).minimize(cost, var_list=train_params)

data_index = 0
while (step < num_steps):
  batch_inputs, batch_labels, data_index = tl.nlp.generate_skip_gram_batch(
                data=data, batch_size=batch_size, num_skips=num_skips,
                skip_window=skip_window, data_index=data_index)
  feed_dict = {train_inputs : batch_inputs, train_labels : batch_labels}
  _, loss_val = sess.run([train_op, cost], feed_dict=feed_dict)

 

既存の埋め込み行列を restore する

埋め込み行列を訓練する最後に、行列と対応する辞書をセーブします。それから次回、行列と辞書を次のように restore できます。(tutorial_generate_text.py の main_restore_embedding_layer 参照)

vocabulary_size = 50000
embedding_size = 128
model_file_name = "model_word2vec_50k_128"
batch_size = None

print("Load existing embedding matrix and dictionaries")
all_var = tl.files.load_npy_to_any(name=model_file_name+'.npy')
data = all_var['data']; count = all_var['count']
dictionary = all_var['dictionary']
reverse_dictionary = all_var['reverse_dictionary']

tl.nlp.save_vocab(count, name='vocab_'+model_file_name+'.txt')

del all_var, data, count

load_params = tl.files.load_npz(name=model_file_name+'.npz')

x = tf.placeholder(tf.int32, shape=[batch_size])
y_ = tf.placeholder(tf.int32, shape=[batch_size, 1])

emb_net = tl.layers.EmbeddingInputlayer(
                inputs = x,
                vocabulary_size = vocabulary_size,
                embedding_size = embedding_size,
                name ='embedding_layer')

tl.layers.initialize_global_variables(sess)

tl.files.assign_params(sess, [load_params[0]], emb_net)
 

以上






TensorFlow : Guide : ML Concepts : 埋め込み (Embedding)

TensorFlow : Guide : ML Concepts : 埋め込み (Embedding) (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/14/2018
作成日時 : 09/10/2017

* 本ページは、TensorFlow 本家サイトの Guide – ML Concepts – Embedding を翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

イントロダクション

埋め込み (= embedding) は単語のような離散的な (= discrete, 別々の) オブジェクトから実数のベクトルへのマッピングです。例えば、英単語のための 300-次元埋め込みは以下を含むかもしれません :

blue:  (0.01359, 0.00075997, 0.24608, ..., -0.2524, 1.0048, 0.06259)
blues:  (0.01396, 0.11887, -0.48963, ..., 0.033483, -0.10007, 0.1158)
orange:  (-0.24776, -0.12359, 0.20986, ..., 0.079717, 0.23865, -0.014213)
oranges:  (-0.35609, 0.21854, 0.080944, ..., -0.35413, 0.38511, -0.070976)

埋め込みは離散した入力に機械学習を適用させてくれます。分類器、そしてより一般的にニューラルネットワークは密で連続的なベクトルで動作するように設計されています、そこではすべての値がオブジェクトが何であるかを定義するために寄与します。もし離散的なオブジェクトが離散的なアトム、e.g., 一意な id 数、として素朴にエンコードされるのであれば、それらは学習と一般化を阻害するでしょう。埋め込みを考える一つの方法はベクトルでないオブジェクトを機械学習のための有用な入力に変換する手段としてです。

埋め込みはまた機械学習の出力としても有用です。埋め込みはオブジェクトをベクトルにマップしますので、アプリケーションはベクトル空間における類似性 (e.g., ユークリッド距離やベクトル間の角度) をオブジェクトの類似性の堅牢で柔軟な尺度として利用できます。一つの共通的な利用方法は最近傍を見つけることです。上の同じ単語埋め込み (word embeddings) を使えば、例えば、各単語に対して3つの最近傍と相当する角度 (度単位) がここにあります :

blue:  (red, 47.6°), (yellow, 51.9°), (purple, 52.4°)
blues:  (jazz, 53.3°), (folk, 59.1°), (bluegrass, 60.6°)
orange:  (yellow, 53.5°), (colored, 58.0°), (bright, 59.9°)
oranges:  (apples, 45.3°), (lemons, 48.3°), (mangoes, 50.4°)

これはアプリケーションに、(45.3° 離れている) りんごとオレンジはある意味では (48.3° 離れている) レモンとオレンジよりも類似していることを伝えているでしょう。

 

埋め込みを訓練する

TensorFlow で単語埋め込みを訓練するためには、最初にテキストを単語(群)に分割して語彙の総ての単語に整数を割り当てる必要があります。これは既に行われたとし、word_ids はこれらの整数のベクトルだと仮定しましょう。例えば、文 “I have a cat.” は [“I”, “have”, “a”, “cat”, “.”] に分割されてそして相当する word_ids テンソルは shape [5] を持ち5つの整数から成ります。これらの word ids を埋め込むためには、次のように embedding variable を作成してtf.gather 関数を使用する必要があります :

word_embeddings = tf.get_variable(“word_embeddings”,
    [vocabulary_size, embedding_size])
embedded_word_ids = tf.gather(word_embeddings, word_ids)

この後、テンソル embedded_word_ids は私たちの例では shape [5, embedding_size] を持ち5つの単語のそれぞれに対する埋め込み (密ベクトル) を含みます。variable word_embeddings は学習されて訓練の最後には語彙の総ての単語に対する埋め込みを含みます。埋め込みは多くの方法で訓練できます、利用可能なデータに依存して。例えば、文の巨大なコーパスが与えられた場合、前の一つから次の単語を予想するためにリカレント・ニューラルネットワークを使用できるでしょう、あるいは多言語翻訳を行なうために2つのネットワークを訓練できるでしょう。これらのメソッドは 単語のベクタ表現・チュートリアル で説明されていますが、しかし総ての場合において上のような embedding variable があり、示したように、単語は tf.gather を使用して埋め込まれます。

 

埋め込みを可視化する

TensorBoard は、Embedding Projector (埋め込み投射器) と呼ばれる、埋め込みの対話的な可視化のための組み込みのビジュアライザーを持ちます。embedding projector は貴方のチェックポイント・ファイルから埋め込みを読み込みそしてそれらを 主成分分析 を使用して 3-次元に射影します。PCA の視覚的表現については、この記事 を見てください。貴方が利用できる他の非常に有用な射影は t-SNE です。

埋め込みで作業しているのであれば、データポイントにラベル/画像をアタッチすることを多分望むでしょう。各ポイントのラベルを含む メタデータ・ファイル を生成してそして Python API を使用するか手動で projector_config.pbtxt をチェックポイントと同じディレクトリに構築して保存するかをして projector を構成することによってこれを行なうことができます。

セットアップ

TensorBoard をどのように実行して必要な情報の総てをロギングしているかをどのように確かなものにするかについての徹底した情報は TensorBoard: 学習を視覚化する を見てください。

貴方の埋め込みを可視化するには、貴方が行なう必要がある3つのことがあります :

1) 貴方の埋め込みを保持する 2D テンソルをセットアップする。

embedding_var = tf.get_variable(....)

2) 定期的に貴方のモデル variable を LOG_DIR のチェックポイントに保存する。

saver = tf.train.Saver()
saver.save(session, os.path.join(LOG_DIR, "model.ckpt"), step)

3) (オプション) メタデータを貴方の埋め込みと関連付ける。

貴方の埋め込みに関連する任意のメタデータ (レベル、画像) を持つ場合、直接 LOG_DIR の projector_config.pbtxt (訳注: projector_config.proto) を保存するか、あるいは Python API を使用することで TensorBoard にそれについて知らせることができます。

例えば、次の projector_config.ptxt (訳注: 原文まま) は word_embedding テンソルを $LOG_DIR/metadata.tsv にストアされている metadata と関連付けます :

embeddings {
  tensor_name: 'word_embedding'
  metadata_path: '$LOG_DIR/metadata.tsv'
}

同じ config は次のコード・スニペットを使ってプログラムで生成できます :

from tensorflow.contrib.tensorboard.plugins import projector

# Create randomly initialized embedding weights which will be trained.
vocabulary_size = 10000
embedding_size = 200
embedding_var = tf.get_variable('word_embedding', [vocabulary_size, embedding_size])

# Format: tensorflow/tensorboard/plugins/projector/projector_config.proto
config = projector.ProjectorConfig()

# You can add multiple embeddings. Here we add only one.
embedding = config.embeddings.add()
embedding.tensor_name = embedding_var.name
# Link this tensor to its metadata file (e.g. labels).
embedding.metadata_path = os.path.join(LOG_DIR, 'metadata.tsv')

# Use the same LOG_DIR where you stored your checkpoint.
summary_writer = tf.summary.FileWriter(LOG_DIR)

# The next line writes a projector_config.pbtxt in the LOG_DIR. TensorBoard will
# read this file during startup.
projector.visualize_embeddings(summary_writer, config)

モデルを実行して埋め込みを訓練した後、TensorBoard を実行してそれにジョブの LOG_DIR をポイントさせてください。

tensorboard --logdir=LOG_DIR

それからトップ・ペインの Embeddings タブ上をクリックして適切な run を選択してください (もし一つ以上の run があれば)。

メタデータ

通常は埋め込みはそれに関連づけられたメタデータを持ちます (e.g. ラベル、画像)。メタデータはモデル・チェックポイントの外の別のファイルにストアされるべきです、何故ならばメタデータはモデルの訓練パラメータではないからです。フォーマットは最初の行がカラム・ヘッダ (ボールドで示されます) を含み続く行がメタデータ値を含む TSV ファイル (タブ・キャラクタは赤色で示されます) であるべきです :

Word\tFrequency
Airplane\t345
Car\t241

 
メインのデータファイルと共有される明示的なキーはありません; 代わりに、メタデータ・ファイルの順序は埋め込みテンソルの順序とマッチすることが仮定されています。換言すれば、最初の行はヘッダ情報でメタデータファイルの (i+1)-th 行は、チェックポイントの埋め込みテンソルの i-th 行に相当します。

Note:
TSV メタデータファイルが単一のカラムだけを持つならば、ヘッダ行を期待せずに、そして各行が埋め込みのラベルと仮定します。この例外を含むのは、一般に使用される “vocab file” フォーマットにに適合するからです。

画像

貴方の埋め込みに関連付けられた画像を持つのであれば、各データポイントの小さいサムネイルからなる単一の画像を生成する必要があるでしょう。これは スプライト画像 として知られています。スプライトは row-first 順でストアされたサムネイルで行とカラムの同じ数を持つべきです : 最初のデータポイントは左上隅に置かれて最後のデータポイントは右下隅です :

0 1 2
3 4 5
6 7  

上の例で最後の行は満たされる必要はありません。スプライトの具体的な例として、10,000 MNIST 数字 (100×100) の このスプライト画像 を見てください。

Note: 現時点では 8192px X 8192px までのスプライトをサポートしています。

(訳注: 参考のため MNIST のスプライト画像を張り付けておきます : )

スプライトを作成したら、Embedding Projector にそれをどこで見つけるか知らせる必要があります :

embedding.sprite.image_path = PATH_TO_SPRITE_IMAGE
# Specify the width and height of a single thumbnail.
embedding.sprite.single_image_dim.extend([w, h])

相互作用

Embedding Projector は3つのパネルを持ちます :

  1. 左上のデータ・パネル、ここでは彩色してポイントにラベル付けするための run (= 実行)、埋め込みテンソルそしてデータカラムを選択できます。
  2. 左下の Projections パネル、ここでは射影 (e.g. PCA, t-SNE) のタイプを選択します。
  3. 右側の Inspector パネル、ここでは特定のポイントを検索して最近傍のリストが見れます。

射影

Embedding Projector はデータセットの次元を削減する3つのメソッドを持ちます : 2つの線形と一つの非線形です。各メソッドは 2- または 3-次元ビューを作成するために使用できます。

主成分分析 次元を削減するための率直なテクニックは 主成分分析 (Principal Component Analysis, PCA) です。Embedding Projector は top 10 主成分を計算します。メニューは貴方にこれらの成分を2つまたは3つの任意の結合に射影させることを可能にします。PCA は線形射影で、大域的な幾何学を検証するのに効果的です。

t-SNE 人気のある非線形次元削減テクニックが t-SNE です。Embedding Projector は 2- と 3-次元両者の t-SNE ビューを提供します。アルゴリズムの各ステップでクライアント側アニメーティングでレイアウトが実行されます。t-SNE はしばしば何某かのローカル構造を保存するので、ローカル近傍の探索とクラスタを見つけるために有用です。高次元データの可視化に非常に有用ですが、t-SNE プロットは時々怪しげ (= mysterious) で誤解させやすいです。”how to use t-SNE effectively (t-SNE をどのように効果的に使用するか)” についてのこの 優れた記事 を見てください。

カスタム 空間における意味のある方向性を見つけるためにテキスト検索に基づく特殊な線形射影を構築することもできます。射影軸を定義するためには、2つの検索文字列または正規表現を入力してください。プログラムは (ポイントの) ラベルがこれらの検索に適合するポイントのセットの重心を計算し、射影軸としての重心間の差分ベクトルを使用します。

ナビゲーション

データセットを探求するために、2D または 3D モードで、自然なクリック & ドラッグという行動を使用してビューをズーム、回転、そしてパンにナビゲートすることができます。ポイントをクリックすると右ペインに最近傍の明示的な textual リストが現在のポイントへの距離と共に表示されます。最近傍ポイントそれ自体は射影上でハイライトされます。

クラスタにズームすることは何某かの情報を与えますが、ポイントのサブセットにビューを制限してそれらのポイント上だけで射影を実行することは時々更に有用です。それを行なうために、複数の方法でポイントを選択できます :

  1. ポイント上をクリック後、その最近傍もまた選択されます。
  2. 検索後、クエリーにマッチするポイントが選択されます。
  3. selection を有効にして、ポイント上をクリックしてドラッグすることは選択球 (= selection sphere) を定義します。

ポイントのセットの選択後、それらのポイントをそれら自身の上の更なる解析のために右側の Inspector ペインの “Isolate Points” ボタンで分離することができます。
 



単語埋め込みデータセットで “important” の最近傍の選択

カスタム射影によるフィルタリングの組み合わせはパワフルです。下で、私たちは “politics” の 100 最近傍をフィルタしてそれらを x 軸として “best” – “worst” ベクトルに射影しました。y 軸はランダムです。

右側には “ideas”, “science”, “perspective”, “journalism” を持ち、その一方左側では “crisis”, “violence” を “conflict” を持つことが見れます。

カスタム射影コントロール。

“politics” の近傍の “best” – “worst” ベクトルへのカスタム射影。

共同的な特徴

貴方の発見を共有するために、右下隅の bookmark パネルを使用して現在の (任意の射影の計算された座標を含む) 状態を小さいファイルに保存することができます。それから Projector はこれらのファイルの一つまたはそれ以上のセットにポイントされることが可能で、下のパネルを生成します。そして他のユーザはブックマークのシークエンスを通り歩くことができます。

 

Mini-FAQ

“埋め込み” は action ですか thing ですか? 両者です。人々はベクトル空間に単語を埋め込む (action) ことについてそして単語埋め込み (things) を生成することについて話します。両者に共通なのは離散したオブジェクトからベクトルへのマッピングとしての埋め込みの概念です。そのマッピングを作成または適用することは action ですが、マッピングそれ自身は thing です。

埋め込みは高次元ですか低次元ですか? 場合によります。単語と句の 300-次元ベクトル空間は、例えばですが、それが含むことができる数百万の単語と句と比較した時にはしばしば低次元 (そして密) と呼ばれます。しかし数学的にはそれは高次元で、人間の直感が 2- と 3- 次元空間について学ぶものとは劇的に異なる多くの特性を表示します。

埋め込みは埋め込み層と同じですか ? いいえ; 埋め込み層はニューラルネットワークの一部ですが、埋め込みはより一般的な概念です。

 
以上



TensorFlow Word2Vec で「源氏物語」解析

TensorFlow Word2Vec で「源氏物語」解析

TensorFlow Word2Vec による日本語テキスト解析

自然言語処理の基本と言える Word2Vec については TensorFlow のチュートリアル 単語のベクトル表現 / Word2Vec がありますが、まだ日本語テキストで試していなかったので青空文庫の源氏物語(訳: 與謝野晶子)を題材に解析してみました。桐壺から夢浮橋の帖までの全テキストを使用しています。

Wikipedia によれば …
『源氏物語』は平安時代中期に成立した日本の長編物語、小説である。文献初出は1008年(寛弘五年)でこの頃には相当な部分までが成立していたと思われる。
母系制が色濃い平安朝中期(概ね10世紀頃)を舞台に、天皇の親王として出生し、才能・容姿ともにめぐまれながら臣籍降下して源氏姓となった光源氏の栄華と苦悩の人生、およびその子孫らの人生を描く。通説とされる三部構成説に基づくと、各部のメインテーマは以下とされ、長篇恋愛小説としてすきのない首尾を整えている。

  • 第一部:光源氏が数多の恋愛遍歴を繰り広げつつ、王朝人として最高の栄誉を極める前半生
  • 第二部:愛情生活の破綻による無常を覚り、やがて出家を志すその後半生と、源氏をとりまく子女の恋愛模様
  • 第三部:源氏没後の子孫たちの恋と人生

 
日本語テキストを Word2Vec に持ち込む場合には形態素解析がつきまといます。今回は自然言語処理ツールキットの定番 NLTK をベースに改良しましたが、MeCab なども試すべきかもしれません。

取りあえず、源氏物語のテキストを形態素解析してトークン化してリストを入力として、Word2Vec の基本実装 word2vec_basic.py を実行してみました。視覚化されたグラフが以下です(画像はクリックして拡大できます)。官職名の距離が近いことなどが見て取れます。 :
w2vec_genji2

Word2Vec で Cos 距離

視覚化だけでなく単語間の具体的な cos (コサイン)距離を測るために、元祖 Word2Vec を利用しました。
以下はその例で「光源氏」「無常」「嫉妬」「寵愛」それぞれの単語と距離が近い単語を pick up しています :

 

Word: 光源氏 Position in vocabulary: 1761
単語

コサイン距離
優秀

0.837391
趣味

0.790800
貴公子

0.788980
すぐれ

0.788795
及ぶ

0.788473
最上

0.780777
人物

0.778179
方面

0.774576
最も

0.765890
競争心

0.763227
次点: 芸術、重々しい、素質、貴婦人、才気、帝王 etc.
Word: 無常 Position in vocabulary: 1443
単語

コサイン距離
人生

0.873624

0.826600
いとわしく

0.799423
体験

0.796729
はかな

0.774706
この世

0.772246
いとわしい

0.761853
あやまち

0.753774
報い

0.746610
いたさ

0741317
次点: 無限、生命、厭世、後世、信仰 etc.
 
 

Word: 嫉妬 Position in vocabulary: 885
単語

コサイン距離
気に入ら

0.823713
男性

0.820702
異性

0.801180
信頼

0.795489
軽侮

0.782988
憎悪

0.780449

0.772462
行為

0.770650
精神

0.768954
尊重

0.767844
Word: 寵愛 Position in vocabulary: 1879
単語

コサイン距離
東宮

0.806865
後宮

0.776469
内親王

0.762031
重々しく

0.758517
競争

0.758219
後見

0.757598
候補

0.750942
先帝

0.742261
大切

0.738606
後援

0.738517
 

 
光源氏がどのような人物像として描かれているか一目瞭然ですし、無常・嫉妬・寵愛のような固有名詞でない単語の場合でも性質が良く表れています。
結論として、日本語テキストの場合でも Word2Vec は機能するようです。

補記

word2vec_basic.py の embedding 層のサイズと window サイズをそれぞれ 512, 2 に変更して得られた画像は以下になります。注意深く観察すれば改良されていることが分かりますが、二次元画像に射影すると分かりにくいかもしれません :
w2vec_genji-e512_w2b

 

以上

TensorFlow : 単語のベクタ表現 / Word2Vec (コード解説)

TensorFlow : コード解説 : 単語のベクタ表現 / Word2Vec

* TensorFlow : Tutorials : 単語のベクタ表現 (翻訳/解説) に、数式は排除/コード重視の方針で詳細な解説を加筆したものです。

 

TensorFlow & Word2Vec

このチュートリアルでは Mikolov et al による word2vec モデルを眺めます。このモデルは “単語埋め込み (word embeddings)” と呼ばれる、単語のベクタ表現を学習するために使われます。

特に TensorFlow で word2vec モデルを構築するにあたり、興味ある本質的な部分を強調することを意図しています。

  • 何故単語をベクタとして表現したいのかという動機を与えることから始めます。
  • モデルの背後にある直感と、モデルがどのように訓練されるかを見ます。また TensorFlow のモデルの単純な実装も示します。
  • 最後に平凡なバージョンをより良くスケールする方法を見ます。

後でコードを通り抜けますが、直接コードを見ることを好むのであれば、
tensorflow/examples/tutorials/word2vec/word2vec_basic.py の最小限の実装を見てください。

この基本的なサンプルは何某かのデータをダウンロードし、その上で少し訓練して結果を視覚化するに必要なコードを含みます。

この基本版を読んで実行することに馴染んだら、tensorflow/models/embedding/word2vec.py へと進むことができます。これはより実践的な実装で、データをテキストモデルに移すためにどのように効率的にスレッドを使うか、訓練の間にどのようにチェックポイントを作成するか等についての幾つかのより進んだ TensorFlow の基本理念を紹介します。

最初に、単語埋め込みを何故第一に学習するのかを見ていきましょう。

 

動機 – 何故、単語埋め込みを学習するのか?

画像と音声処理システムはリッチで高次元なデータセットで動作します。物体あるいは音声認識のようなタスクのためにはタスクを成功裏に終わらせるために必要なすべての情報はデータにエンコードされていることを我々は知っています。何故なら人間はこれらのタスクを生データから遂行するからです。

 

けれども、自然言語処理システムは伝統的に単語を個別の原子的な記号として扱い、従って ‘cat’ は Id537 そして ‘dog’ は Id143 として表現できます。これらのエンコードは恣意(しい)的で、そして個々の記号間に存在するかもしれない関係に関してシステムに有用な情報はもたらしません。

これはモデルが ‘dogs’ についてのデータを処理している時に ‘cats’ について学習したこと(例えば両方とも動物であるとか、四つ足であるとか、ペットであるとか等)の非常に僅(わず)かしか利用できないことを意味しています。

単語を一意な、離散的な ID として表現することはさらにデータの疎性へとつながり、そして通常は統計モデルを成功的に訓練するためには更なるデータが必要であるかもしれないことを意味します。

ベクタ表現を使うことはこれらの障害の幾つかを乗り越えられます。

ベクタ空間モデル (VSM, Vector space models) は連続的なベクタ空間で単語を表します(埋め込みます)。そこでは意味論的に類似の単語が近いポイントにマップされています。(互いに近くに埋め込まれています。)

VSM は NLP において長い豊かな歴史を持ちますが、全てのメソッドは何らかの方法で Distributional Hypothesis(分布仮説) に依存しています。これは 同じ文脈において出現する単語は意味論的な意味を共有する、と主張するものです。

この原理を活用する異なるアプローチは2つのカテゴリーに分割されます: count-based メソッド(例えば潜在的意味解析, Latent Semantic Analysis)と、予測的 (predictive) メソッド(例えば、確率的ニューラル言語モデル, neural probabilistic language models)です。

この違いは Baroni et al. により更にたいへん詳しく説明されていますが、しかし手短に言えば:

  • Count-based メソッドは、ある単語が大規模なテキスト・コーパスで隣接語とともにどの程度の頻度で共起するかの統計情報を計算して、そしてこれらの count-統計情報を各単語について小さな、密ベクタにマップします。
  • 予測的モデルは、学習した小さな、密な埋め込みベクタ(モデルのパラメータと考えられる)の観点から隣接語から単語を直接的に予測を試みます。
 

Word2vec は生テキストから単語埋め込みを学習するための特に計算効率的な予測モデルです。それには2つの種類があります、CBOW連続 (Continuous) Bag-of-Words モデルSkip-Gram モデル です。

アルゴリズム的には、これらのモデルは似ています、CBOW がソース文脈単語(’the cat sits on the ‘)からターゲット単語(ex) ‘mat’)を予測する一方で、skip-gram は逆を行ない、ターゲット単語からソース文脈単語を予測するという点を除いてです。

逆は任意の選択のように思われますが、統計的には CBOW は(全体の文脈を一つの観測として扱うことにより)たくさんの分布情報をなだらかする効果があります。多くの場合、これは小さめのデータセットのために有用なことと判明しています。けれども、skip-gram は各文脈ターゲット・ペアを新しい観測として扱い、そしてこれはより大きなデータセットを持つ時に上手くやる傾向があります。

このチュートリアルの残りでは、skip-gram モデルに焦点を当てていきます。

 

Noise-Contrastive 訓練によるスケールアップ

ニューラル確率的言語モデルは伝統的に、softmax 関数 の観点から前の単語 \(h\) (for "history") が与えられた時に次の単語 \(w_t\) (for "target") の確率を最大化する 最尤 (maximum likelihood (ML)) 原理を使用して訓練されます,

\[
\begin{align}
P(w_t | h) &= \text{softmax}(\text{score}(w_t, h))
\end{align}
\]

ここで \(\text{score}(w\_t, h)\) は単語 \(w\_t\) の文脈 \(h\) との適合性を計算します。

訓練セット上の対数尤度 (log-likelihod) を最大化することで私たちはこのモデルを訓練します。
すなわち、以下を最大化することによります

\[
\begin{align}
J_\text{ML} &= \log P(w_t | h)
\end{align}
\]

これは言語モデリングのための正規化された確率モデルを正しく生み出します。けれどもこれは非常に高コストです、何故なら全ての訓練ステップにおいて、現在の文脈 \(h\) で他の全ての \(V\) 個の単語 \(w'\) のためのスコアを用いて各確率を計算して正規化する必要があるからです。

一方、word2vec における特徴学習のためには、完全な確率モデルは必要ありません。

CBOW と skip-gram モデルは代わりに、同じ文脈で \(k\) 個の想像上の(ノイズ)単語 \(\tilde w\) から実際のターゲット単語を \(w_t\) を識別するために二項分類目的関数(ロジスティック回帰)を使用して訓練されます。
CBOW モデルのために下に図解しました。skip-gram については方向を単に反対にするだけです。

 

数学的には、目的関数はモデルが高い確率を実際の単語に、低い確率をノイズ単語に割り当てた時に最大化されます。技術的には、これは ネガティブ・サンプリング と呼称され、この損失関数を使用するに良い数学的な動機があります: これが提示する更新は softmax 関数の更新に極限において接近します。しかし計算的にはそれは特別に魅力的です、何故なら損失関数の計算は選択したノイズ単語 (\(k\)) の数にのみ比例して語彙 (\(V\)) の全ての単語ではないからです。これは訓練を非常に速くします。

実際には非常に良く似た noise-contrastive estimation (NCE) 損失を利用します、このために TensorFlow は便利なヘルパー関数 tf.nn.nce_loss() を持っています。

これが実際にどのように動作するのかについて直感的感覚を得てみましょう!

 

Skip-Gram モデル

例として次のデータセットを考えましょう

the quick brown fox jumped over the lazy dog

最初に単語のデータセットとそれらが現れる文脈を形成します。

どのような方法でも ‘コンテキスト(context, 文脈)’ を定義できるでしょう。実際に統語論的なコンテキスト – 現在のターゲット単語の統語論的な依存性(例えば Levy et al. を参照) – が考察されてきました。ターゲットの左側の単語群 (words-to-the-left of the target,)、ターゲットの右側の単語群、等々。

取り敢えずは普通の定義にこだわることにして、 ‘コンテキスト’ をターゲット単語の左側と右側の単語群の窓 (window of words to the left and to the right of a target word) として定義します。

窓サイズ 1 を使えば、(コンテキスト, ターゲット) ペアのデータセット

([the, brown], quick), ([quick, fox], brown), ([brown, jumped], fox), …

を得ます。

skip-gram はコンテキストとターゲットを逆にして、各コンテキスト単語をそのターゲット単語から予測を試みることを思い出してください。そのため目指すタスクは ‘quick’ から ‘the’ と ‘brown’ を、’brown’ から ‘quick’ と ‘fox’ 等を予測することになります。

従ってデータセットは (入力, 出力) ペアの

(quick, the), (quick, brown), (brown, quick), (brown, fox), …

になります。

目的関数はデータセット全体に渡り定義されますが、典型的にはこれを一度に一つのサンプルを使って(あるいは batch_sizeサンプルの ‘ミニバッチ’、ここで通常は 16 <= batch_size <= 512)、確率的勾配降下 (SGD) で最適化します。

さてこのプロセスの 1 ステップを見てみましょう。

訓練ステップ \(t\) について上の最初の訓練ケースを観測することを想像しましょう。ここでの目的は quick から the を予測することです。

あるノイズ分布、典型的にはユニグラム分布、 \(P(w)\) から引き出すことにより num_noise 個のノイズの(対称)サンプルを選択します。

単純化のために num_noise=1 としてノイズのサンプルとして仮に sheep を選択してみます。次に観測されたサンプルとノイズのサンプルのこのペアのために損失を計算します。ゴールは時間ステップ \(t\) における目的関数を改善する(この場合は、最大化する)ために埋め込み (embedding) パラメータ \(\theta\) を更新することです。埋め込みパラメータ \(\theta\) に関して損失の勾配を導出することによりこれを行ないます。幸いなことに TensorFlow はこれを行なうために簡単なヘルパー関数を提供しています! そして勾配の方向に小さなステップを取ることで埋め込みへの更新を遂行します。

このプロセスが全体の訓練セットに渡って繰り返された時、モデルがノイズ単語から実際の単語を識別することに成功するまで各単語について埋め込みベクタを ‘移動’ する効果を持ちます。

学習したベクタを、例えば t-SNE 次元削減テクニック のようなものを使って2次元に射影することで視覚化できます。これらの視覚化を詳しく調べることで単語とそれの他の単語との関係性についてある一般的な、そして実際にたいへん有用な、意味的な情報をベクタが捕えていることが明らかになります。

例えば下に図解されているように、導かれたベクタ空間のある方向がある意味的な関係、male-female, gender そして country-capital 単語間の関係の方向に特化されていることを最初に発見した時は非常に興味深かったです (例として Mikolov et al., 2013 も見てください)。

 

これらの図はまた何故これらのベクタが、多くの標準的な NLP 予測タスク、例えば品詞タグ付けや固有表現認識、に対する特徴として有用なのかを説明しています。(例として Collobert et al., 2011 (pdf) によるオリジナル・ワーク、あるいは Turian et al., 2010 によるフォローアップ・ワークを見てください)。

 

グラフを構築する

理論的な説明はこれまでにして、コードレベルでグラフの構築を見ていきましょう。
以下のグラフ構築のフェーズ以前に単語データベースの構築やバッチの生成は完了しています。

graph = tf.Graph()
with graph.as_default():
  # 入力データ。
  train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
  train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])
  valid_dataset = tf.constant(valid_examples, dtype=tf.int32)

  # GPU なしの実装なので、演算と変数を CPU にピン止めしておきます。
  with tf.device('/cpu:0'):
    # Look up embeddings for inputs.
    embeddings = tf.Variable(
        tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
    embed = tf.nn.embedding_lookup(embeddings, train_inputs)

    # NCE 損失のための変数を構築。
    nce_weights = tf.Variable(
        tf.truncated_normal([vocabulary_size, embedding_size],
                            stddev=1.0 / math.sqrt(embedding_size)))
    nce_biases = tf.Variable(tf.zeros([vocabulary_size]))

埋め込みについての説明は終えているので、埋め込み行列を定義します。
これは最初は単なる大きなランダム行列で、単位立方体で一様になるように値を初期化します。

embeddings = tf.Variable(
    tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))

NCE (noise-contrastive estimation) 損失をロジスティック回帰モデルの観点から定義します。
このため、語彙の各単語のために重み(入力埋め込みへの対比として出力重みとも呼称します)とバイアスを定義する必要あります。それを定義しましょう。

nce_weights = tf.Variable(
  tf.truncated_normal([vocabulary_size, embedding_size],
                      stddev=1.0 / math.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))

然るべき場所にパラメータを持った今、skip-gram モデルグラフを定義できます。

単純化のために、テキスト・コーパスを語彙に既に整数化 (integerized) して各単語は整数として表されることを仮定しましょう。(詳細は tensorflow/examples/tutorials/word2vec/word2vec_basic.py を見てください。)

skip-gram モデルは2つの入力を取ります。一つはソース・コンテキスト単語群を表す整数で満ちたバッチで、他方はターゲット単語群のためです。これらの入力のためにプレースホルダー・ノードを作成しましょう、そうすれば後でデータを供給できます。

# 入力のためのプレースホルダー
train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])

今私たちが行なう必要があるのはバッチのソース単語の各々のためのベクタを検索する (look up) ことです。TensorFlow はこれを簡単にする便利なヘルパー関数を持ちます。

embed = tf.nn.embedding_lookup(embeddings, train_inputs)

OK、 各単語に対する埋め込みを持った今、noise-contrastive 訓練 objective を使ってターゲット単語を予測してみましょう。

# 各時のネガティブ・ラベルのサンプルを使って NCE 損失を計算します。
loss = tf.reduce_mean(
  tf.nn.nce_loss(nce_weights, nce_biases, embed, train_labels,
                 num_sampled, vocabulary_size))

損失ノードを持った今、勾配を計算してパラメータを更新する等に必要なノードを追加する必要があります。
このため確率的勾配降下を用います、TensorFlow はこれを容易にする便利なヘルパーも持ちます。

# SGD オプティマイザを使用します。
optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0).minimize(loss)
 

モデルを訓練する

そしてモデルの訓練は簡単で feed_dict を使ってデータをプレースホルダーにプッシュして
ループの中でこの新しいデータで session.run を呼び出すだけです。

with tf.Session(graph=graph) as session:
  # We must initialize all variables before we use them.
  tf.initialize_all_variables().run()
  print("Initialized")

  average_loss = 0
  for step in xrange(num_steps):
    batch_inputs, batch_labels = generate_batch(
        batch_size, num_skips, skip_window)
    feed_dict = {train_inputs : batch_inputs, train_labels : batch_labels}

    # We perform one update step by evaluating the optimizer op (including it
    # in the list of returned values for session.run()
    _, loss_val = session.run([optimizer, loss], feed_dict=feed_dict)
    average_loss += loss_val

完全なサンプルコードは tensorflow/examples/tutorials/word2vec/word2vec_basic.py を見てください。

 

学習した埋め込みを視覚化する

訓練が終了した後、t-SNE を使って学習した埋め込みを視覚化できます。

Et voila! 期待どおり類似の単語はお互いに近くにクラスタリングされる結果になりました。

【参考】 TensorFlow の進んだ特徴の多くを示す、word2vec のより本格的な実装については tensorflow/models/embedding/word2vec.py を見てください。

以下は、翻訳者が実際に実行してみた結果です :
tsne

 

埋め込みを評価する: 類推による推論 (Analogical Reasoning)

埋め込みは NLP の様々な予測タスクに有用です。

本格的な品詞モデルか固有表現モデルの訓練が不足する際、埋め込みを評価する一つの単純な方法は king が queen に対する時 father は何に対する?というような統語的そして意味的関係を予測するためにそれらを直接使用することです。

これは類推による推論 (analogical reasoning) と呼ばれタスクは Mikolov と同僚 により紹介され、そしてデータセットはここからダウンロードできます: https://word2vec.googlecode.com/svn/trunk/questions-words.txt

この評価をどのように行なうかを見るには、tensorflow/models/embedding/word2vec.py における build_eval_graph() と eval() 関数を見てください。

ハイパーパラメータの選択はこのタスクの精度に強く影響します。
このタスクで革新的な性能を達成するためには非常に大規模なデータセット上で訓練し、注意深くハイパーパラメータチューニングしそしてデータのサブサンプリングのようなトリックを使用する必要があります、これはこのチュートリアルの範囲外です。

 

実装を最適化する

上述の標準的な実装は TensorFlow の柔軟性を見せてくれます。
例えば、訓練目的(関数)の変更は単純で tf.nn.nce_loss() への呼び出しを tf.nn.sampled_softmax_loss() のような既製の他の選択肢に置き換えるだけです。もし損失関数のための新しいアイデアがあるならば、貴方は TensorFlow で新しい目的関数のための式を手作業で書くことができます。そしてその導関数をオプティマイザに計算させることができます。この柔軟性は、様々な異なるアイデアを試みて素早く反復する、機械学習モデル開発の探求フェイズで貴重です。

もし貴方が満足できるモデル構造を一度得たら、より効率的に実行するため(そしてより短時間でより多くのデータをカバーするため)に実装を最適化する価値があるかもしれません。

例えば、このチュートリアルで使用した素朴なコードは妥協した速度を経験するかもしれません、何故なら データ項目を読み供給するために Python を使用しているからです — それらの各々は TensorFlow バックエンドで非常に少ない作業しか必要としません。

もし貴方のモデルが入力データ上深刻なボトルネックがあることを見つけたのであれば、New Data Formats に記述されているように、その問題のために カスタム・データ reader を実装したいかもしれません。

Skip-Gram モデルのケースのためには、 tensorflow/models/embedding/word2vec.py のサンプルとして実際には既にこれを貴方のために行なっています。

もし貴方のモデルがもはや I/O バウンドはなくしかし依然として更なるパフォーマンスを望むのであれば、 新しい Op を追加する に記述されているように、貴方自身の TensorFlow OP を書くことで前に進むことができます。

再度 Skip-Gram ケースのためのこれのサンプル tensorflow/models/embedding/word2vec_optimized.py を提供しています。各々のステージにおけるパフォーマンス改善を計測するためにこれらを互いに対して自由にベンチマークしてみてください。

 

結論

このチュートリアルで word2vec モデルをカバーしました。
これは単語埋め込みの学習のために計算的に効率的なモデルです。私たちは何故埋め込みが有用であるかを動機付け、効率的な訓練テクニックを議論しそして TensorFlow においてこれら全てをどのように実装するかを示しました。

総括として、早期の実験のために必要な柔軟性とあつられて最適化した実装のために後で必要な制御を TensorFlow がどのように貴方に与えるかの紹介となることを望みます。

 

以上

AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com