Technical Hedgehog

機械学習と気になる技術を語るはりねずみ

Googleの事前学習済みモデルを手軽に利用出来るTensorFlow Hub

自然言語処理におけるword2vecや画像処理におけるInceptionなど、一般的に広く用いられているモデルを上流で用いる事は多くあります。汎用的な知識を扱えるメリットがある一方、学習には大量のデータセットの準備と膨大な学習時間がかかってしまいます。

f:id:kamujun:20180810181316p:plain

この問題に対して、あらかじめ学習させた状態のモデル(事前学習済みモデル)を用意しておき上流に転移させる方法があります。本記事ではその事前学習済みモデルについて、Googleが提供するのライブラリであるTensorFlow Hubを紹介します。

TensorFlow HubはGoogleの大量リソースを用いて学習したモデルを手軽に実装できるほか、自作したモデルを別環境で利用しやすいように自作することも可能です。本記事では概要と特徴、利用方法を紹介します。

今回説明するTensorFlow Hubの利用方法、作成方法について実験したコードはGitHubにあります。利用方法は生TensorFlowとKerasにおいてFine-tuningを行う実験を行っています。作成を行う例はネット上でもなかなか無かったので、誰かのお役に立てたら幸いです。(チュートリアルでは大雑把にしか書いてなかった)スターしていただけると嬉しいです。

TensorFlow Hubとは

f:id:kamujun:20180810181604p:plain 画像:google Developers Blog より

TensorFlow HubはGoogleが提供している事前学習済みモデルです。以前から事前学習済みモデルは様々なフレームワークでも提供されており、TensorFlowにおいてもグラフと学習済みチェックポイントが公開されています(現在も更新されているようです)。ただ、利用する際にはグラフを定義したファイルの読み込みやチェックポイントの解凍やロードが必要であるなど手軽ではない印象を受けます。

TensorFlow Hubの特徴はなんといっても手軽に実装できる点が特徴です(しかもGoogleが大量リソースを用いて学習したモデル)。事前学習済させるモデルの多くは大量なコーパスをもとにしているので自作すると大変ですが、TensorFlow HubはGoogleの潤沢なリソースを用いて学習させたモデルが無料で提供されています。例としては画像系の事前学習済みモデルであるNASNetは62,000時間以上のGPU処理時間を投じて計算されたモデルであったり、テキスト系のNeural-Net Language Modelsでは2,000億単語を用いて学習したモデルなどがあります。これらのモデルは後述するモジュールと言う形で数行で実装することが可能なくらい簡単に利用出来ます。

概要・用語説明

TensorFlow Hubにおいてグラフと重みはひとまとめに提供されており、モジュールと呼ばれます。モジュールという単位にすることによってグラフの読み込みや重みのロードなどそれぞれの操作を意識することなく利用することができます。現時点では提供されているモジュールのグラフ構造を読み解くことはできないようになっています。(GitHub Issueより)

ざっくりとしたイメージではモジュールはクラスのようなものと考えることができ、インプットを与えれば求めた処理を行い、アウトプットを返却するものです。モジュール内部のグラフなどを考える必要はありません。

f:id:kamujun:20180810161928p:plain

TensorFlow Hubのモジュールの用語・機能について説明します。

  • シグネチャ
    モジュールが持つメソッドのような機能です。モジュールは複数のシグネチャを持つことができ、指定したシグネチャによってインプットや処理内容などが変わります。指定しない場合はdefaultが指定されます。

  • アウトプット
    複数のアウトプットを持つモジュールがあります。何も指定しない場合はdefaultのアウトプットのみが返却されますが、as_dict=Trueを指定することで定められたoutputがリストで返却されます。

提供モジュール

TensorFlow Hubでは様々な分野の事前学習済みモデルが用意されています。同一のモデルであっても返却される次元数や学習させる言語について異なるモジュールが用意されている場合があります。2018年8月現在では以下の通りですが、今後映像や音声系のモジュールの提供も予定しているとのこと。

画像系

  • Inception (V1, V2, V3)
  • Inception-ResNet (V2)
  • MobileNet (V1, V1 with TF-Lite, V2。それぞれmultiplierが25,50,75,100%のものを用意。)
  • NASNet-A (large, mobile)
  • PNASNet-5 (large)
  • ResNet (V1, V2)

テキスト系

  • NNLM (Chinese, English, German,
 Indonesian, Japanese, Korean, Spanish)
  • Universal Sentence Encoder (default, large, lite)
  • word2vec
  • ELMo

その他

  • Generative Adversarial Networks
    progan-128
  • DEep Local Features (DELF)
    delf
  • Inflated 3D Convnet (I3D)
    i3d-kinetics-400 i3d-kinetics-600

実装

次にTensorFlow Hubを用いた実装例を紹介します。利用するのは難しくないのですが、モジュールの作成は少し面倒なところがあります。以下で説明する機能の実装例はGitHubにあります。

モジュール利用・再学習

以下は公式サイトに記載されているサンプルコードです。前回の記事で紹介したELMoを例に紹介します。与えた文について単語ごとにELMoの分散表現を得る事を目的とした例です。

import tensorflow as tf
import tensorflow_hub as hub

with tf.Graph().as_default():
    elmo = hub.Module("https://tfhub.dev/google/elmo/2", trainable=True) # モジュール用意
    embeddings = elmo(
        ["the cat is on the mat", "dogs are in the fog"],
        signature="default",
        as_dict=True)["elmo"] # モジュール適用

    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        sess.run(tf.tables_initializer())

        print(sess.run(embeddings)) # 実行

モジュール用意

TensorFlow Hubを利用するにはモジュールのURLを指定してモジュールのダウンロードし、インスタンス生成を行います。その際にFine-tuning行わせたい場合は"trainable"をTrueにしておきます。これだけでグラフと重みのロードが終わっています。

モジュール適用

インスタンスに対してモジュールで定められた形式で引数を与えます。Googleが提供する各モジュールは仕様が公開されており、今回のELMoの場合はこちらのページに記載されています。ここでは分散表現を得る対象の文、シグネチャ、返り値の形式を指定します。

ここではシグネチャを"default"にしています。仕様ではこのモジュールは2つのシグネチャを持っており、一文を与えて処理させる"default"と、トークナイズした単語配列を与えて処理させる"tokens"が用意されています。

アウトプットについては"elmo"を指定しています。これは"default"のアウトプットでは文中単語の分散表現の平均を返すようになっており、目的とした単語ごとの分散表現は"elmo"で返却されるためです。"default"以外のアウトプットを用いるので"as_dict=True"としています。

実行

セッションで実行すればOKです。もちろん他の計算を足すことも可能です。Fine-tuningしたい場合は通常通りloss関数やオプティマイザを設定する必要があります。

モジュール作成

作成についてはコードを書くと長くなるので大まかな流れを図とともに説明します。本記事では概要の説明だけを行いますが、実装はGitHub上にあります。

モジュール作成で特にポイントとなるのはグラフの定義と重みを分けて扱う必要がある点です。これはエクスポート時にグラフ定義の関数と重みの割当は別々に行う必要があるためです。以下説明で順に説明していきます。

f:id:kamujun:20180810171347p:plain

①作成モデル準備

作成したいモデルを構築し、学習を行わせます。目標の性能が発揮することが確認できたら一度モデルを保存します。 もし重みをFine-tuning(再学習)させる想定が無いのであれば、convert_variables_to_constantsを用いてVariableをconstantに変換させておくと後の手順が一部スキップできます。

②モジュールスペック定義

モジュール内のグラフである"Module_Spec"を定義する関数を作成します。「①作成モデル準備」で保存したモデルをロードし、インプットとアウトプットのテンソルの変数を取得します。取得したインプットとアウトプットを用いてモジュールのシグネチャを追加することでスペック定義に用いる関数を用意します。

③モジュール作成

スペック定義を用いてモジュールのインスタンス生成を行います。この時点で作成したモデルと同じように利用することができますが、この時点ではVariableの値つまり重みは初期化されたままです。

④重み割り当て

「①作成モデル準備」で保存しておいたモデルの重みをロードして割り当てることで、事前学習した状態のモジュールとすることができます。ただし、Fine-tuningさせる予定がなく、constantに変換済みであればここの手順は不要です。(重みはグラフ中のconstantとして既に含まれているため)

⑤エクスポート

最後にこのモジュールを"export"したら事前学習済みモデルのモジュール完成です。指定したフォルダにモジュール一式であるpbファイルと重みであるVariableの値が出力されます。

自作モジュールの利用

作成したモデルは2つの方法で別環境で利用することができます。

モジュールファイルを直接受け渡し

exportしたモジュールを利用したい環境へ配置し、モジュールのダウンロードURLの部分でディレクトリを指定すれば利用かのうです。作成したモジュールが以下通りexportされた場合、ディレクトリmnist_moduleを指定すればOKです。

 .
 └mnist_module
    ├── assets
    ├── saved_model.pb
    ├── tfhub_module.pb
    └── variables

クラウドストレージを用いたホスティング

モジュールを圧縮した上でクラウドストレージに配置したものを利用することが可能です。その際は「.tar.gz」に圧縮すれば良いと説明があります。圧縮方法は公式の説明に任せますが、利用する際はダウンロードURLにクエリ「?tf-hub-format=compressed」を追加する必要があります。

mnist_module = hub.Module('https://your_hosting_address/mnist_module.tgz?tf-hub-format=compressed')

Google Cloud Storage と AWS S3 で試した結果、両者とも問題無く使用することができました。

TensorFlow Hubを使用した感想

実際に使用している中で感じた良い点とあと一歩の点を挙げて見ようと思います。

良い点

・大量リソースで学習された事前学習済みモデルが無料で利用出来る。しかも簡単に。
・各分野で有名なモデルを多数用意
・モデルによっては前処理まで含まれている(ボキャブラリ作成やトークナイズも含まれるので生の文を投げるだけで良い!)

難しい点

・KerasのLambdaレイヤにおいてTensorFlow hubを用いた場合、Fine−Tuningさせた結果の保存が不可能?
生のTenforFlowでは再学習結果も含めモデルを保存できたが、KerasのLambdaレイヤ内の重みの保存は実験する限りでは無理そうだった。カスタムレイヤで実装するしか無いかもしれない。

・モジュールの作成がやや煩雑
保存してあるモデルをサクッと読ませるだけでモジュール作成出来るようになると便利だと思います。

TensorFlow Hubによって事前学習済みモデルの恩恵を手軽に受けれるため、自分が取り組んでいるタスクに対して注力できるのはとても良いと感じました。有益な事前学習済みモデルを自作した場合にも他者が利用しやすい形で公開できるのは魅力ですね。

文脈を考慮した単語表現を獲得するELMo

文脈を考慮した単語表現を獲得する深層学習手法のELMoを紹介します。「アメ」は「Rain」と「Candy」どちらの意味か?それを文脈から考慮させるのがこの手法です。

f:id:kamujun:20180706171450p:plain

機械学習で取り組まれている複雑なタスクは、部分問題を解決する技術を組み合わせて実現されます。例えば文書分類というタスクに取り組む場合、文書特徴を獲得するために単語分割や単語表現などの手法を用いた上で、SVMやナイーブベイズといった分類器を用いる方法があります。部分問題を解決する技術は特定のタスクだけではなく、様々な問題で利用することができます。単語分割は文書分類にだけではなく質問応答など自然言語処理において幅広く利用されます。

上記のように部分問題は様々なタスクで用いられるため、部分問題の性能向上は様々なタスクの性能向上に寄与できる可能性があります。部分問題がうまく解決できていないと、それらを組み合わせて実現する応用タスクも望んだ結果が得られない恐れがあるためです。単語分割でいうと、単語がうまく区切れないまま文書分類を行ったとしても結果は芳しくないであろうという事は直感的に理解してもらえると思います。

今回は単語表現という部分問題において、文脈を考慮した表現を獲得するELMoと呼ばれる深層学習手法を紹介します。近年、単語を分散で表現する方法である単語埋め込み(word embedding)が注目されていますが、手法の多くは1単語毎に1つの表現しか得ることができません。これは「bank」という単語は「銀行」と「土手」という複数の意味を持ちますが、どちらか一方しか表現できていない可能性があります。ELMoではどの意味で用いられているのかを文脈から汲み取った上で、単語の表現を獲得することができていると主張されています。

本記事ではELMoの手法と、論文中で行われている複数の実験のうち「他手法との性能比較」と「文脈を考慮した表現獲得の確認」について紹介します。

ELMo

ELMoは事前学習によって文脈を考慮した単語表現方法を獲得する手法です。Allen Institute for Artificial IntelligenceMatthewらがNAACL 2018において発表し、ベストペーパー の一つにも選ばれています。

この手法の注目すべき点は、文脈を考慮した単語表現が獲得できる点です。このことは語義曖昧性解消(Word Sense Disambiguation)の助けとなると言えると思います。word embeddingは様々なタスクや手法でよく用いられるため活用シーンは多くあります。論文では6つのタスクにおいてELMoを既存手法で用いた結果、性能を上乗せできることを確認しています。

手法概要

この手法は双方向言語モデル(bidirectional language model。以降biLMと呼ぶ)を大量なコーパスを用いて獲得する事で実現されます。論文では1B Word Benchmarkを用いて事前学習させたそうです。

f:id:kamujun:20180706154428p:plain

双方向を用いる理由としては、順方向だけでなく文を逆方向からも読み進めることで語義を正しく捉えられる可能性があるためだと思います。例として「A bank is a financial institution that accepts deposits from the public and creates credit.」という文において「bank」の表現を獲得しようとした場合、順方向からは「A」だけの情報しか用いることができませんが、逆方向からだと「financial」や「deposit」、「credit」など金融に係る情報を利用することができます。

ELMoのアーキテクチャとしてはbi-directional LSTM(biLSTM)を複数層重ねた物となっています。論文の実験では2層のもので行われています。この手法の工夫としては各層の重みを調整(学習)させることでタスク特有の問題に適合させていると説明されています。

獲得された表現は他の単語表現と組み合わせることによって利用すると論文では説明されています。他の単語表現ベクトルと接続(concatenate)してモデルのインプットとします。このことは一般的な表現(ELMo)を活かしつつ、タスク依存となるドメイン固有の表現(word embedding)を利用することを目指しているのではないかと思います。

f:id:kamujun:20180706154530p:plain

6つのタスクにおける性能評価

論文中では様々なタスクに対して性能評価を行っています。取り組むタスクは質問応答(Question answering)、テキスト含意(Textual entailment)、意味役割付与(Semantic role labeling) 、(Coreference resolution)、固有表現抽出(Named entity extraction)、感情分析(Sentiment analysis)の6種類です。

実験では各タスク毎に用意したベースライン手法に対してELMoの有無で性能差がどれくらいであるか評価を行っています。また、ELMoを適用したベースライン手法と最先端手法(state-of-the-art:SOTA)の比較も行っています。

f:id:kamujun:20180706154649p:plain

ELMo適用有無による性能比較はいずれのタスクにおいても適用後のほうが優れています。また、SOTAとの比較においてもすべて上回っています。この実験によりELMoは多様性を含み幅広いタスクにおいて有用であるモデルだと論文で主張されています。

ELMoは文脈を考慮した表現を獲得できたのか?

各タスクにおいて性能向上に寄与していることは上記の通り確認しています。一方で、論文中では以下の3つの実験から文脈を考慮した単語表現を獲得していることを確認しています。

f:id:kamujun:20180706154709p:plain

1つ目の実験はELMoと他の単語埋め込み手法であるGloVeにおいて、似ている単語表現を持つ単語の比較です。ここでは「play」という単語が例として挙げられています。GloVeではざっくりと意味が似ている単語(「playing」や「game」など)が似た表現として取れています。一方で、ELMoでは文脈を考慮してスポーツを競技する「play」と、劇を演じる「play」は分けて表現できていると提示されています。

GloVeでは1単語から1つの単語表現を得るので、意味が似ている単語が多くなるのは想定通りなのかなと感じます。一方でELMoは文を与えて各単語の表現を得ており、用いる情報が多いためアドバンテージがあるためのではと思います。

f:id:kamujun:20180706154749p:plain

2つ目の実験では語義曖昧性解消(Word sense disambiguation)の実験です。ここではELMoではなく用いる言語モデル(2層biLM)の各層別々を対象として、他の手法と比較を行っています。結果(Table 5)を見ると2層目のbiLMが性能良く、教師あり学習手法のSOTAであるIacobacciらに迫っていると確認されています。

3つ目の実験は品詞タグ付け(POS tagging)の実験です。こちらの実験結果(Table 6)も同様に言語モデル(2層biLM)の各層別々を対象としており、1層目のbiLMがSOTAであるLingらに近づいています。

これらの実験によりELMoは文脈を考慮した表現が獲得できるていると説明されています。

個人的疑問点

ここで個人的な疑問点として、上記の実験だけでELMoは文脈を考慮した表現を獲得できたと言えるのかが気になりました。1つ目の実験ではELMoとGloVeの比較を例に文脈を考慮した単語が獲得できた例が挙げられましたが、なぜか2,3つ目の実験ではELMoではなく中で用いている言語モデルであるbiLMの各層を用いて実験していました。ELMoは各層の重みをタスクごとに学習することができるので各層の性能は参考にすることはできると思いますが、ELMo全体としての結果を見てみたいと思いました。

また、性能向上したのは文脈を考慮した表現が獲得したからと言えるのかという点も気になります。チームで話題にした際、ELMoは10億単語を超える大量のコーパスを用いて学習しているため、タスクで利用する際には未知語が少なくなっているのではないかという仮説が挙げられました。未知語が少ないほど性能が向上する可能性があるため、他の手法においても同程度の学習データを用意して実験したほうが良かったかもしれません。「文脈を考慮できた=各タスクで性能向上に寄与できた」というのは少し論拠が弱いかもと感じました。

おわりに

文脈を考慮した単語表現であるELMoを紹介しました。実験でも確認できたように性能向上の助けとすることができる事が確認できました。ELMoが良いなと思った点としては、大規模なコーパスから汎用的で文脈を考慮した表現を獲得できる点ではないかと思います。学習させるデータを毎回用意するのは手間がかかるため、事前に他のコーパスで事前学習しておくことができる点は利用シーンを広げることができるのではないかと思います。

次回はELMoを使った簡単な実験を記事にしようかと思います!
よろしければTwitterのフォローやいいねをお願いします。

参考

数えきれないほどの分類を行うExtreme Classification

本記事では分類タスクの一種であるExtreme Classificationの代表的な手法と特徴を紹介します。機械学習においてアヤメの分類など10数個までのラベルやクラスへの分類タスクはチュートリアルなどで多く取り上げられています。

一方で商品をカテゴリに分類したい場合など大量のラベルやクラスで分類したい場合、既存手法では計算量が膨大になるなど様々な問題に直面します。そこで大量のラベルやクラスを用いて分類を行うタスクをExtreme Classificationと呼び研究が進められています。

Extreme Classificationとは?

f:id:kamujun:20180622152814p:plain

Extreme Classificationは10万〜100万にも及ぶ膨大なラベルやクラスを用いて対象を分類するタスクです。このタスクは少なくとも10年以上前から研究が行われており、学会のワークショップなどでも取り組まれています。直近ではNIPS Extreme Classification 2017 が開催されています。日本からはYahooの田頭さんが既存手法を改善させたAnnexMLを発表されています。また、AnnexMLの論文紹介として、u++さんのこちらの記事が参考になります。

Extreme Classificationはマルチクラス(multi-class)とマルチラベル(multi-label)の2つの問題に分けて考えられます。

前者はExtreme Multi-class Classificationと呼ばれ、対象に対して一つのクラスを割り当てる問題を想定しています。後者の複数のラベルを割り当てるタスクはExtreme Multi-label Classification (XMCと略されることが多い)と呼ばれ、近年の研究ではこちらの問題に取り組んだものが多く見受けられます。これはマルチラベル問題はマルチクラス問題を包含しているためだと思います(マルチラベルでの推定結果1番目だけ採用すればマルチクラスと同等とみなせる)。

Extreme Multi-label Classification について興味があれば各手法の性能やデータセットについて紹介しているThe Extreme Classification Repositoryが参考になると思います。

以降はXMCの代表的なアプローチ方法であるEmbedding BasedとTree Based、Deep Basedの3つについて、それぞれ代表的な手法を交えながら紹介します。

Embedding Based

このアプローチの基本的な考え方は、分類したいラベルを減らして既存手法を適用可能にさせることをベースとしています。XMCが難しい原因はラベル数が多すぎるためであり、1-vs-Allなどの既存の手法が扱えないためです。そこでラベルを低ランクに近似させることで、扱えるラベル数にしようというわけです。このアプローチにおける手法の違いは、低ランクにするcompression(圧縮)処理や推定結果を得るために圧縮前の次元にdecompression(本記事では解凍と呼称)処理などに現れます。

このアプローチの利点としては仕組みとしてシンプルかつ理論的に説明が可能であり、実装が比較的容易である点です。問題点としては現実世界のデータにおいて、ラベル毎の訓練データが不均衡である場合など低ランクに仮定するのが困難な(圧縮の際に情報が落ちる)場合があるという点があります。

f:id:kamujun:20180622162311p:plain

代表的な手法としてはSLEECがあります。学習フェーズでは高次元(各値は0or1)で表現されるラベルベクトルを低次元へ圧縮(と同時に解凍)する方法を学習させます。次に入力である特徴ベクトルから低次元ラベルを推定させるモデルを学習させます。推論させる場合は低次元ラベルを求めた後、その結果を解凍することによって分類結果を得ることができます。

性能向上と計算量を減らすための工夫として、入力である特徴ベクトルをk-means法で大まかに分割させ、それぞれのクラスタで圧縮(解凍)方法を学習させています。推論時にはkNN(k近傍法)で所属するクラスタを求めてから低次元ラベルの推定と解凍を行います。

Tree Based

この方法では大量ラベルでの分類を決定木などの木構造を用いて推定させます。このアプローチではラベル数が多いことによる計算量の増加や性能の低下という問題に対して、既存手法を適応させるために改良したものが多く存在します。

このアプローチの利点は木構造アルゴリズムと同様に、バランスのとれた構造を作れた場合に推定時の計算量がある程度で抑えられる点です。また、アンサンブルにしたり多くの特徴量と重みを用いることでロバスト性を獲得することができます。

f:id:kamujun:20180622162419p:plain

代表的なものとしてFastXMLがあります。これはランダムフォレストをマルチラベルに適応させた手法です。既存手法では各決定木を成長させる際に情報利得の目的関数としてジニ不純度(Gini impurity)やエントロピー(entropy)を用いていましたが、ラベルが多すぎるために計算量が膨大になってしまう問題があります。そこで目的関数にランキングの評価指標であるnDCG@kを用いる事で、計算量を抑えつつ大量ラベル分類の学習することができます。nDCG@kやこのタスクで良く用いられている評価指標についてはこちらで紹介しています。

Deep Based

近年注目を集めているDeep Learningを用いた手法も提案されており、NAACLなどの学会などでも採択されてきています。アプローチ方法は様々で、入力である特徴ベクトルをもとに複数層重ねたNeural Networkで学習させる方法や、分類ラベルをグラフとみなしてEmbeddingを求めて目標関数に用いる方法など研究が盛んになってきています。

f:id:kamujun:20180622162554p:plain

今回紹介するのは、文書特徴を畳み込んで分類を行うXML-CNNです。文書分類のタスクに対してCNNを用いる事は珍しくありませんが大量ラベルを扱うための工夫として、プーリングの方法、損失関数、次元数の少ない隠れ層の追加などでより対処しています。 それぞれの工夫については論文輪講にて紹介しました。

これから

これまでもExtreme Classificationは検索や広告などでも応用されてきたように、活用されるシーンが増えてくるのではないかと感じています。今後も研究は活発に行われると思いますので、引き続き注目していきたいと思います。

参考