Technical Hedgehog

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

Kaggle創薬コンペにおけるDeep Learningの適用

創薬においてコンピュータの活用はますます盛んになってきており、2012年にはKaggleでコンペも開催されました。このコンペは標的に対する分子の活性を推定するというタスクでした。

用いられた手法としては1位はDeep Learning、2位は非Deepな機械学習手法でありスコアにこそ大差はありませんでしたが、創薬においてDeep Learningの適用可能性を示したことで当時は話題になったそうです。だいぶ前のコンペなの解説記事はすでに多くありますが、コンペを通じて創薬の概要とDeep Learningがどのようなアプローチで適用されたのかを紹介してみます。

f:id:kamujun:20190423182833p:plain:w450

!Caution!
できる限りの調査をしましたが、私は製薬や医療に詳しい人ではないので誤った解釈をしてしまっている可能性があります。「ここ間違っている」と言う点がありましたら指摘いただけると幸いです。

では、はじめにコンペのタスク背景となる創薬について概要を説明してみます。

創薬の大きな流れ

創薬における作業は作業は大きく分けて基礎研究、試験、承認といったステップで進んでいきます。

f:id:kamujun:20190423144916p:plain
創薬プロセス(製薬の未来より)

探索研究

探索研究や基礎研究と呼ばれるフェーズでは一般的に以下の様な流れとなるそうです。

ターゲットの選定

体の中のタンパク質等の働きをもとに、病気の改善につながるターゲットを選定します。例えば炎症を引き起こす物質としてプロスタグランジンの一種があるそうですが、これは酵素「プロスタグランジンH合成酵素」によって合成されるそうです。この酵素をターゲットとして働きを弱める化合物が発見できれば、それはすなわち抗炎症薬と言われるものとなります。このように、ターゲットを探すことから薬作りが始まります。

リード化合物の探索

ターゲットに対して反応を示す化合物であるリード化合物を探します。リード化合物を探す理由は全くヒントがない状態から薬となる化合物を作るのでは無く、効果がある化合物をベースに発展させていくほうが現実的なためです。

リード化合物を探す方法の一つとしては、経験的に効果があると知られている物質から求める方法です。19世紀に生まれた炎症薬であるアスピリンは、ヒポクラテスが熱や痛みに対して処方したとされるヤナギからリード化合物を見つけ、発展させていったそうです。

2つ目の方法としては、数千、数十万以上の化合物に対して活性の反応を得る方法でスクリーニングと呼ばれる手法です。候補とする化合物は分子量や形など一定の条件をもとに用意し、様々な活性を一度に調べる中からリード化合物となるものを見つける方法です。

以上の方法から薬のタネを探すのがこのフェーズの目的です。

リード化合物の最適化

リード化合物に対して様々な合成を行い、薬候補となる化合物を作成します。発見したリード化合物そのままでは効果が限定的であったり、副作用がある場合もあります。そこで少し手直しを行うことで効能をあげたり、副作用を低減させることが最適化の目的です。

試験・承認・市販

その後のフェーズでは動物や人間を用いた試験(前臨床・臨床(1相、2相、3相))において効果や安定性が確認された後、国に承認されて晴れて市販されるようになります。

基礎研究から承認まで平均で13~14年もかかり、候補として研究を始めた分子が新薬として世に出る確率は3万分の1とも言われているようです。各フェーズにおいて、効果があまり出ない、副作用が強すぎる、製造工程を安価にできないなどで研究が中止されることも多々あるそうです。お金も莫大に掛かるでしょうから厳しい世界ですね…

Kaggle創薬コンペ

ここで話題を変えてKaggleに目を向けると、このコンペはリード化合物の探索をコンピュータ上で行うバーチャルスクリーニングを想定しているのでは無いかと思われます。コンペの目的は15種類のターゲットに対して各分子の活性がどれくらい示すかを推定するタスクであり、これはまさにタンパク質に対してどの化合物が反応を示すかを調べることを意識していそうです。

これからは与えられたデータセットの解説とコンペの結果、そして1位となったDeep Learningの手法を見ていきます。

Data Set

データセットはすべてコード化されているため何を意味しているかは分かりません。つまりドメイン知識を用いることができません。

15個のターゲット毎にファイルが分かれており「ACT1」のように表現されています。コンペ後に出された論文を見るに、ACT1は「3A4」と呼ばれるデータセットに該当すると思われ、生体異物を代謝する酵素シトクロムP450 3A4」に関する各化合物の反応を記したものでありそうです。ちなみに、この酵素は主に肝臓や小腸に分布しており、薬物治療の最適化(薬が代謝排出されずに吸収されやすいようにするなど)を考える上で重要な酵素であるとみなされることが多いそうです。

Training Set

以下の通り情報が与えられています。

  • Column 1
    分子ID。「ACT1_M_80」のように、「ターゲットコード+分子コード」で表現されています。 分子コードはすべてのターゲットで共通であり一意につけられているようです。(つまりACT1_M_80とACT15_M_80は同一の分子)

  • Column 2
    活性値。ターゲットごとに単位が異なっているため同一のスケールでは無いことは留意しておく必要があります。

  • Column 3〜
    分子の記述子。 コンペ中ではこれ以上の説明がなくラベルの意味は不明となっていましたが、この論文中でatomic pair(AP)やdonor−acceptor pair(DP)であると明かされました。いずれも原子同士の組み合わせを表現しているようです。
    APでは元素や結合している非水素元素数、π電子数をもとにラベル付けされているようです。
    DPではカチオン、アニオン、中性ドナー、中性アクセプター、極性、疎水性物質、その他で分類しているそうです。
    (ここまで来るとちょっと追いきれないのでそれぞれの意味は他に譲ります…)

中身を見てみると0以上の数値データで表現されている非常にスパースなデータであり、5%のみが0以外と論文では説明されています。記述子のラベルそれぞれがどんな意味を持つのかは論文でも言及されておらず最後まで不明なままでした。

Test Set

レーニングセットから活性値を除いたフォーマットになっています。コンペでは15種類のテスト用データセットに記載されている分子について、それぞれの活性値を推定して提出することとなります。

コンペの評価は相関係数Rの二乗で求められます。詳細はこちらです。

コンペ結果

コンペの最終結果であるPrivate Leaderboardは以下の表となっています。

f:id:kamujun:20190423160033p:plain:w600
Merck Molecular Activity Challenge Private Leaderboard

1位はGeorge Dahl率いるトロント大学のチームによるDeep Learningを用いた手法でした。このチームにはDeep Learningにまつわる研究に対してチューリング賞を受賞したGeoff Hintonもアドバイサーとして参加していたようです。

2位はData Robotチームによる機械学習(Random Forests, PCA, GBM, KNN, Neural Nets, Elastic Net, GAM, SVMなど)を用いた手法でした。機会学習自動化プラットフォームを提供している事で有名なData Robot社ですが、この年の年初に企業してコンペに参加したようです。

このコンペにおいてはわずかにDeep Learning手法が上回った結果となりました。これからは1位を獲得したDeep Learning手法を見ていきます。

Deep Learningを用いたモデル

主催であるMerckと1位チームの共著で発表された論文Deep Neural Nets as a Method for Quantitative Structure−Activity Relationshipsをもとに手法を見ていきます。これは、1位になった手法をベースに少し改良を加えたモデルのようです。

この論文のcontributeはQSAR(Quantitative Structure-Activity Relationships)と呼ばれる、化合物の構造と生物学的な活性の量的関係を分析するタスクにおいてDeep Leariningを適用し、他の手法と同等以上の性能を示すことができた点です。これまではSVMやrandom forestといった手法が用いられてきたようですが、Deep系の手法が実問題に適用できたということが注目されたようです。

ネットワークを見ると基本的には全結合層を重ねただけのシンプルなネットワークです。論文ではハイパーパラメータ(隠れ層の数やニューロン数、ドロップアウト、事前学習など)によって性能がどう変化するかを様々実験し、考察されています。

f:id:kamujun:20190423160657p:plain:w450
全結合層によるDeep Neural Network

次に論文中の2つの実験について紹介します。

Individual vs Joint

筆者達は15個のターゲットに対するデータセットについて、別々のデータセットで学習すべきか、全てのデータセットを用いて学習するべきかを実験しています。言い換えるとACT1用、ACT2用、…と一個づつモデルを用意するか、ACT1〜ACT15まで全てのターゲットに対して推定できるモデルを1つ用意するかで性能がどのように異なるかを確認したということです。

この論文においては前者はindivisual DNN、後者はjoint DNNと名付けられています。ネットワーク図のイメージとしては以下です。

f:id:kamujun:20190423173314p:plain:w450
Individual DNN と Joint DNN

この実験を行った理由は各データセットでサイズが異なっており、データセット毎にモデルを作って学習を行うindivisual DNNでは学習データが足りずに性能が向上しない可能性があると筆者達は考えたようです。

joint DNNは問題に対して、全てのデータセットを用いて1つのモデルとして学習することでデータ不足を補います。それぞれのターゲットを予測する部分はアウトプット層で分けたニューロンを分ける事で出力されています。全てのデータセットを用いることで大量のデータから獲得した分子構造の知識のようなものが隠れ層に蓄積され、性能向上に役立てることを期待しています。

これら2つのモデルの性能を比較した結果は以下の通りと報告されています。上に分布しているほどJoing DNNの性能が良いことを示しています。

f:id:kamujun:20190423161755p:plain:w600
Joint DNNs vs Individual DNNs

15個のデータセットに対する推定結果を平均で見ると(左)、Joint DNNの方が良いことを示しています。

13個のデータセットにおいて(右)、性能が良い理由としては学習に用いた分子のうち85%が単一のデータセット、97%が2つ以下のデータセットでしか出現していないため、他のデータセットの特徴をうまく活用できたからではないかと考察されています。

逆に大きいデータ量を持つ2個のデータセットおいて(中央)性能が悪い原因としては、重複する分子はこの2つのデータセット間によるものであったため悪くなったではないかと述べられています。ちょっと個々の部分の説明はあまりしっくり来ていませんが…。

joint DNNの疑問点としてデータセット間で記述子と分子が揃っておらずどのように扱っているのか、またそれによる性能への影響があります。例えばACT1にD_1はあるが、ACT2にはD_1が存在しないといった風です。想像するに記述子も活性も単純に0としていると思うのですが実装が見つからなかったので不明でした。

DNN(parameter調整済み) vs RF

この論文では最終的に以下ハイパーパラメータでモデルで性能が良かったと述べられています。このモデルはjoint DNNで学習されたものとなっています。

・前処理
    対数変換
・隠れ層
    4層で各層のニューロン数は4000, 2000, 1000, 1000
・ドロップアウト    
    (input) 0 (hidden) 25% (hidden) 25% (hidden) 25% (hidden) 10% (output)
・活性化関数
    ReLU
・事前学習
    なし。パラメータ初期値はランダム。
・エポック数
    コンピュータ資源が許す限り多く。(ドロップアウトが効いているため過学習はしないとのこと)
・最適化
    モメンタム。パラメータは学習率0.05、モメンタム0.9、重み減衰0.0001。

上記モデルとrandom forestについて、データセット毎に比較した結果が以下となります。

f:id:kamujun:20190423163014p:plain:w450
パラメータチューニングされたjoint DNNとrandom forestの性能比較

15個のデータセットのうち13個においてjoint DNN(DNN with recommended parameter values)が上回っています(太字部分)。また、ハイパーパラメータの選択が最も悪かった場合においても0.412となり、randam forestより性能が良かったと筆者らは言っています。

終わりに

創薬の特にバーチャルスクリーニングのタスクにおいてDeep Learningがどのように適用され、実用可能性があるかをKaggleのコンペを通して見てみました。

私はこのタスクに対してDeep Learningを用いる事について、筋は悪くないのではと感じました。現状のDeep Learningの課題として解釈が難しいという点があり、研究対象にもなっています。一方で、数多ある候補からリード化合物となる可能性が高いものを見つけるこのタスクにおいては、なぜそのように推定したかよりも1%でも性能が高い方が優先されるのではないでしょうか。よって、Deep Learningは選択肢の一つとしては十分にあげても良いのではないかと思います。この点については専門家の方にどこを重要視するのか聞いてみたいです。

また、データセットについても考えさせられました。コンペで用いたデータセットの記述子が実際の活性予測する上で妥当であるのか特徴量の学習は可能なのか、よく用いられるフィンガープリントではどうなるのかなど、データセットに起因する懸念点もあるようです。一方で、分子のグラフ構造を利用するGraph Convolutionなど、新たな手法によって利用できるデータセットも変わってきているとも言えると思います。

参照資料

Embeddingを高速に取り出すMagnitude

word2vecなど分散表現が活躍するシーンは多いですが、実行速度やメモリなど利用する上で気がかりになる面もあります。この記事では分散表現をすばやく便利に使うためのパッケージであるMagnitudeの説明と実行速度について実験した結果を紹介します。

What's Magnitude?

Magnitude は簡単にすばやく単語ベクトルを扱うためのライブラリです。

github.com

EMNLP2018で発表された Magnitude: A Fast, Efficient Universal Vector Embedding Utility Package の著者実装となります。論文中で挙げられているように、Gensim の代替を想定しているようです。

Magnitude Object について

Magnitudeではベクトル取得を高速化するために、事前に分散表現を変換したものを用います。それぞれの分散表現ごとに独自のフォーマット".magnitude"のオブジェクトとして用意しています。初期ロード時は遅延ロードするようになっているためオブジェクトの読み込みに時間がかからないようにし、一度呼び出された単語はメモリ上にキャッシュすることで再取得時には高速に取り出すことができます。SQLiteを用いることにより実現されているようです。

こちらに事前学習済みのMagnitude Objectが公開されており、後述する未知語に対する処理が異なるLight, Medium, Heavey の3種類が用意されています。用意されているモデルはword2vecやGlove、ELMoなど様々であり、今後は話題のBERTも追加される予定のようです。

Out-of-Vocabulary について

ここではMagnitudeの特徴的な機能(だと私は思っている)である、未知語(Out-of-Vocabulary)に関する機能を紹介します。

基本的な機能を含めて利用方法自体は簡単であり、githubのREADMEを見ていただければ困らないと思います。

Basic Out-of-Vocabulary Keys

新しい単語やスペルミスなど、分散表現を構築する際の学習データに含まれない未知語に対し、Magnitudeは適当であるランダムなベクトル値を割り振ります。ざっくり言うと、同じ単語であれば2回目以降は同じベクトルを返すようにすること(辞書に新規登録するイメージ)と、n-gramが近しい未知語はベクトルも近しくなるように割り振るようにしているそうです。Lightモデルで利用することが可能です。

Advanced Out-of-Vocabulary Keys

こちらも未知語に対する処理方法ですが、ランダムにベクトルを割り当てるのではなく、辞書に登録されている近しい単語に似るようにベクトルを割り当てます。スペルミスやtypoも近似できるようになっています。MediumやHeavyモデルで利用することができます。

内部でどのような処理を行っているのかの説明と、日本語に対しても処理できるようにした実装が以下の記事で紹介されています。とてもおもしろいです。

yag-ays.github.io

Speed について

論文ではGoogle Newsコーパスで学習したword2vecについてMagnitudeとGensimの実行時間についての比較がされており、初期ロードでは97倍、1単語の初期呼び出しキー(cold key)については等倍なものの、再呼び出しキー(warm key)では110倍高速に処理することができるとされています。その他のベンチマーク結果はこちらから確認することができます。

以下からはMagunitudeを用いることで学習速度を向上させることができるのか、実験で確かめていきます。

速度実験

Magnitudeが他のライブラリに比べて学習に掛かる時間を短縮させることができるのか確認してみます。今回は速度比較を主眼とするため、いずれもライブラリの場合でも全結合層を1層だけ用いただけのモデルであり、違いは分散表現の取得処理だけが違いとなるように実装しています。私のgithubにおいてますのでよければ見てみてください。

  • 比較対象
    Gensimとの比較ではword2vecを、TensorFlow Hubとの比較ではELMoを用いて比較しました。

  • データセット・タスク
    MovieLens の映画ジャンルにおけるマルチラベル分類。 Building a text classification model with TensorFlow Hub and Estimators で紹介されているものを利用。これはMovieLens のうち特定の9種類ジャンルに関するデータだけを抽出した14,993件を用いています。このうち80%を学習データとして、20%をテストデータとして利用します。

  • ハイパーパラメータ

    • 隠れ層ユニット数 : 64
    • バッチサイズ : 32
    • ステップ数 : 375
    • エポック数 : 5
  • マシンスペック

Magnitude vs Gensim

word2vecを利用した場合以下の通りになりました。Magnitudeはword2vecのMediumを利用しています。結果を確認してみましょう。

Magnitude with word2vec

Epoch 1/5
375/375 [==============================] - 5455s 15s/step - loss: 0.4881 - acc: 0.8169 - val_loss: 0.4049 - val_acc: 0.8411
Epoch 2/5
375/375 [==============================] - 4s 12ms/step - loss: 0.4279 - acc: 0.8209 - val_loss: 0.3760 - val_acc: 0.8497
Epoch 3/5
375/375 [==============================] - 2s 6ms/step - loss: 0.4057 - acc: 0.8312 - val_loss: 0.3557 - val_acc: 0.8588
Epoch 4/5
375/375 [==============================] - 1s 4ms/step - loss: 0.3843 - acc: 0.8386 - val_loss: 0.3360 - val_acc: 0.8654
Epoch 5/5
375/375 [==============================] - 1s 4ms/step - loss: 0.3662 - acc: 0.8453 - val_loss: 0.3239 - val_acc: 0.8699

Gensim with word2vec

375/375 [==============================] - 13s 36ms/step - loss: 0.4396 - acc: 0.8246 - val_loss: 0.3887 - val_acc: 0.8565
Epoch 2/5
375/375 [==============================] - 11s 29ms/step - loss: 0.3607 - acc: 0.8502 - val_loss: 0.3194 - val_acc: 0.8759
Epoch 3/5
375/375 [==============================] - 9s 24ms/step - loss: 0.3254 - acc: 0.8630 - val_loss: 0.2967 - val_acc: 0.8825
Epoch 4/5
375/375 [==============================] - 9s 24ms/step - loss: 0.3084 - acc: 0.8702 - val_loss: 0.2813 - val_acc: 0.8867
Epoch 5/5
375/375 [==============================] - 10s 25ms/step - loss: 0.2984 - acc: 0.8743 - val_loss: 0.2764 - val_acc: 0.8877

すべてのエポックをトータルで見るとGensimのほうが速いですね…ただGithubにあげているソースを見ると分かるように、Gensimはword2vecのモデルをロードする際に2,3分時間がかかっていましたが、Magunitudeは一瞬でロードが終わっています。加えて、2エポック目以降はMagnitudeのほうが速かったです。

また、実装上の問題で全く同じ処理にできているかと言うと異なります。Gensimでは未知語をすべて同一の特定単語に置き換えている一方、Magnitudeでは未知語の推測を行っているためです。この当たりが速度に影響を及ぼしているかは今回は確認できていません。

Magnitude vs TensorFlow Hub

ELMoを利用した場合の結果は以下の通りになりました。MagnitudeはELMoのMediumを利用しています。ELMoの概要については以前に以下の記事で紹介しました。

kamujun.hatenablog.com

Magnitude with ELMo

Epoch 1/5
375/375 [==============================] - 8449s 23s/step - loss: 0.2545 - acc: 0.8932 - val_loss: 0.2394 - val_acc: 0.9064
Epoch 2/5
375/375 [==============================] - 147s 391ms/step - loss: 0.2501 - acc: 0.8950 - val_loss: 0.2361 - val_acc: 0.9067
Epoch 3/5
375/375 [==============================] - 169s 450ms/step - loss: 0.2461 - acc: 0.8970 - val_loss: 0.2338 - val_acc: 0.9079
Epoch 4/5
375/375 [==============================] - 185s 494ms/step - loss: 0.2419 - acc: 0.8985 - val_loss: 0.2338 - val_acc: 0.9072
Epoch 5/5
375/375 [==============================] - 163s 436ms/step - loss: 0.2385 - acc: 0.9001 - val_loss: 0.2325 - val_acc: 0.9082

TensoFlow Hub with ELMo

Epoch 1/5
375/375 [==============================] - 13045s 35s/step - loss: 0.3439 - acc: 0.8597 - val_loss: 0.2966 - val_acc: 0.8945
Epoch 2/5
375/375 [==============================] - 10262s 27s/step - loss: 0.2938 - acc: 0.8787 - val_loss: 0.3074 - val_acc: 0.8933
Epoch 3/5
375/375 [==============================] - 10268s 27s/step - loss: 0.2845 - acc: 0.8830 - val_loss: 0.2725 - val_acc: 0.8972
Epoch 4/5
375/375 [==============================] - 10247s 27s/step - loss: 0.2746 - acc: 0.8872 - val_loss: 0.2568 - val_acc: 0.8966
Epoch 5/5
375/375 [==============================] - 10244s 27s/step - loss: 0.2654 - acc: 0.8888 - val_loss: 0.2553 - val_acc: 0.8963

こちらは明確にMagnitudeの方が速くなりました。あまりにも速くなっているので心配になるのですが、学習が進んでいるところを見ると実装が明らかに間違えているわけではなさそうです。(Sequenceを作る際のシードを固定し、TensorFlow Hubと比較してやってもよかったかも)

速度以外に確認した点として、MagnitudeでどのようにELMoを実装しているのかソースを確認しました。ELMoは文脈を考慮した単語ベクトルを得るため文毎に計算が必要ですが、Magnitude内部で本家であるAllen NLPのELMoを用いて計算しているようでした。

まとめ

Magnitudeを用いることでEmbeddingを高速に取り出すことができることを確認しました。word2vecにおいてはGensimも相当早いので、短文では明確なアドバンテージがあるとは言えなかったものの、ELMoではTensorFlow Hubより高速に取り出すことができました。Magnitudeは速さ以外にも未知語の処理やメモリ使用量なども強みがあるので、公開されている事前学習済みモデルを用いる際は利用する価値があるのではないかと思います。自分で作った分散表現もMagnitude Objectに変換できるようなので、自作に挑戦しても良いですね。

フォローやコメントなどで応援して頂けると励みになります。いつもありがとうございます!

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

参考