Chem Infomatics系の基礎事項まとめ(富士フィルム Brain(s)コンテスト参加記)

先日ChemInformatics系のコンペに出て、0から調べて色々知識を得たので、それについてまとめようと思います。 一応参加記なのですが、あんまり面白いことをやってないので基礎まとめの要素が強くなり、それを反映したタイトルにしました。

コンペで使ったモデル

実際にコンペで使ったモデルはmordred+LightGBMの面白みに欠けるモデルと、Transformeを事前学習してembedding生成モデル化+LIghtGBMのモデルでした。
(Privateの結果は出てませんが、後者はPublic LBでもCVでも性能が良かったのと、あんまり見ないモデルだと思うのでPrivateの結果によらずちょっとお気に入りです。)

後者のモデルはKatja Hansen氏のAmes試験データセットでの5Fold CVにおいて、AUC平均0.81を、"予測時にはSMILESのみから"得ることができました。AUC 0.81は精度単体としては大したことないんですが、SMILESだけから出たので結構嬉しいかなと思いました。

このpretrained Transoformerによる molecular embeddingはさらにチューニングとかを検証すればChemInfo系のコンペの飛び道具として使える可能性があると思います。FineTuningだけでいいので、使うのも割と楽です。こちらの手法は結構下の方のsmiles2vecのところでまとめています。

問題設定

一応問題設定に触れますが、あんまりここは以降のところには関係ないです。

mutagenicityという発がん性につながるような化合物の性質をあるかないかの2値で分類する問題でした。(mutangecityの本家の方のテストでは強/弱/無しの3段階くらいになることもあるみたいですが、今回は2値でした。)

学習用データセットは公開データセットを用いることになっており、メインとなったのがこちらの論文https://pubs.acs.org/doi/abs/10.1021/ci900161gのデータセットでした。件数としては6000件ほどで、正例負例が均衡なデータセットとなっています。

記述子とFingerprint

化学物質を機械学習で扱えるような形に変換する方法として、一般に記述子とFigerPrintの2つがあるようです。
詳細に中身を確認しているわけではないのですが、記述子は例えば分子量や、芳香族炭素の数のようなどちらかと言うと化学的性質を表すものを含み、fingerprintは純粋に構造的な物だけといった印象です。例えばFIngerPrintの一つ、Morgan Fingerprintは全原子を中心として、結合グラフにおいて距離r以下の部分構造をすべて数え上げるような操作をしています。

どちらの方が向いているかはタスクによるとおもいますが、機械学習の文脈では多くの場合記述子のほうが扱いやすいかなぁと思いました。(後述しますが、一般にfingerprintは高次元でスパースなベクトルなのでちょっと扱いに悩みます。) この記述子やFingerPrintを計算するライブラリとして、後述するRdkitというものが有名なようです。

Rdkit

非化学系の人間には全く馴染みが無かったんですが、ChemInfo的には定番of定番みたいなツールです。
これを用いて、Smile記法という化合物を文字列で表す記法から様々な化学的、物理的性質を計算したりすることができます。
その他にも便利なツールがめちゃくちゃ入っているので、Cheminfoをするならとりあえずこれというふうになりそうです。

Rdkitのインストール

RdkitのインストールはAnaconda一択です
環境を汚したくない派閥の方々はAnaconda系を入れるのを嫌うかもしれませんが、RDkitはAnaconda以外でのインストールは非常に煩雑で、やるべきではないと思います。
なんでpipで入らないかは、

RDKit: Why the RDKit isn't available on PyPi

にかかれています。抜粋すると、

The core problem is that the RDKit is not a pure python package; it's a mix of python and some compiled extension modules (shared libraries).

とのことで、python以外にも諸々必要でそのリンクの問題などを解決するのがPipだと難しいようです。
環境を汚したくない派閥のわたしはDockerでAnaconda環境を作ってその中で本コンペは作業していました。 GUIを使わず、jupyter-labで作業が済む範囲はDockerで環境作るのもそんなに大変じゃないので、結構いいと思いました。

Rdkitの使い方

については、公式の RDKitドキュメンテーション非公式日本語版サイト — RDKit_unofficial_translation_JP 2019.03.1 ドキュメント を参照して頂いたほうがいいので、ここでは深く触れません。 また、 化学の新しいカタチ | 有機合成化学者のための計算化学・ケモインフォマティクス入門 こちらのサイトも非常に参考になります。合わせて見るのをおすすめいたします。

ざっくりいえば、SMILES記法やその他によって表された分子をrdkit.Molというオブジェクトに読み込んで、そのMolオブジェクトのもとで様々な値等を計算するという流れになります。

記述子計算に役立つmordred

上記の記述子を計算するにあたって、Rdkitだけでは200個前後しか計算できません。しかし、記述子は日々様々な論文で提案されもっとたくさんあります。 そこで更に多くの種類の記述子を計算したくなることがありますが、その時の選択肢の一つに入るのがmordredです。

github.com

https://www.jstage.jst.go.jp/article/ciqs/2016/0/2016_Y4/_pdf/-char/ja

mordredは約2000もの記述子を計算することが出来る非常に便利なツールです。
2000もあるので、計算にはそこそこ時間がかかりますが、記述子をたくさん出せるので、脳死で使っておくだけで性能が向上したりします。
ただ、条件は不明ですが途中で計算が進まなくなるバグ?が発生したりするので、ちょっと注意が必要かもしれません

FingerPrintの種類

化学の新しいカタチ | 有機合成化学者のための計算化学・ケモインフォマティクス入門 おもにこちらのサイトを参考にしました。

morgan fingerprint

まず、分子をグラフ構造としてみます(原子がノード、結合がエッジ)。
その後、分子内に含まれるそれぞれの原子について、その原子からグラフとして距離がr以下の原子達によって作られる部分構造を見ていきます。
この部分構造にidを振って、OneHot Vector的にするのがMorgan FingerPrintです。
rは通常2か3を使うようです。個人的には2がいい感じでした(3になると種類が増大することに寄るデメリットの方が大きい気がしました)
当然存在しうる部分構造はものすごい数になるので、通常はスパースなベクトルか、ハッシュ化して固定長にしたベクトルを用います。
ハッシュ化はハッシュ衝突を避けるために1024bitとか2048bitを使うのが通常なようですが、512bitでもなんとかならないことはないイメージです。
(次元数の増大に弱い手法を使うときは512bitもやむなしかなと思う次第です。)

また、morgan fingerprintには、より抽象化した(ちゃんと理解してないですが、OとSとかCとSiは大体にた働きだよね?ということで同じ役割の原子として2つを区別せずまとめ上げる)バージョンも存在し、Rdkitではuse_features=Trueとすることでそちらのベクトルを使用可能です。

(use_features FalseはECFP,TrueはFCFPとか言われるFigerPrintの一種らしいです。)

アトムペアフィンガープリント(Atom pair fingerprint)

すべての原子の組について、それぞれの原子の原子タイプと、結合距離から求められるidをCount vector的にするのがAtom Pair Fingerprintです。
原子タイプはその原子の種類に加え、隣接する重原子の数とπ結合の数を加味して割り振られます。
morgan fingeprintと異なり結合距離が遠いものについても加味されるのが違いです。

morgan fingerprint同様ものすごくスパースなベクトルになります。
個人的にはlightgbm等の入力ベクトルとしてはあんまりうまく働きませんでした。(SVDとかで次元削減をしなくてはいけないせいもあるかもしれません)

トポロジカル二面角フィンガープリント

4原子によって定義されるすべての二面角について、その4原子の原子タイプによってidを振るようです。
あまり使っていないのでわかりません…
atom pairと同じような原子タイプを使っていますが、2面角の関係上遠く離れた原子については考慮していないフィンガープリントです。
これも個人的にはlightgbm等の入力ベクトルとしてはあんまりうまく働きませんでした。

ドナーアクセプターフィンガープリント(Donor Acceptor fingeprint)

基本思想はアトムペアフィンガープリントと同じですべての原子の組について、その原子タイプと結合距離からidをふって、それをcount vector的に扱います。
相違点は、原子タイプの粒度でこちらはその原子の役割(カチオン、アニオン、ドナー、アクセプター、極性原子、ハイドロフォビック、その他)の7種類で扱うようです。こちらも個人的実験の範囲ではあまりうまく働かなかったのですが、アトムペアと比較すると次元数が大分少なく取り回しが良かったです。

その他手法やツール

ここには今回はそんなに触らなかったんですが、選択肢には入ると思った手法やデータセット類をまとめます。
DeepChemのドキュメントの"Model Classes","Featurizers","MoleculeNet"を眺めると他にも色々見つかると思います。 The DeepChem Project — deepchem 2.4.0rc documentation

DeepChem

深層学習系の手法を使うときに便利なユーティリティやモデルがまとまったものです。主な機能として、

  • 有名データセットのロード
  • 各種Featurizer。Rdkitにないfingerprintなども存在します。
  • 各種モデル(smile2vec,smiles2image,GraphConvolution等)

があります。Rdkitよりも一部は多機能ですし、次cheminfo系のタスクをするときにはちゃんと使ってみたいです。
ちょっと日本語でのネット記事などが少ないので学習コストが高めかもしれないというのと、わたしがまだ理解できてないだけかもしれませんが、各種モデルの細かい設定をしようとすると難しいような気がしたのがあえて言えばマイナスかなと思います。

ただ、使いこなせればかなり強力なツールだと思います。

Graph Convolution

CNNにおける畳込みを二次元のグリッドではなくグラフ構造に拡張した手法です。
簡単なタスクでは記述子ベースのものとそんなに変わらないくらいのスコアなので、(溶解度予測とかだと本当に誤差レベルの差しか出ませんでした。) 今はまだ理解の難しさの割には積極的に採用するほどではないかなと思いました。
割と最近の手法になります。ライブラリとしてはDeepChemなどがありますが、個人的に中のグラフをいじったりが難しくちょっと使いにくい手法かなと感じました。

smile2vec

smiles記法をNLPタスクのように扱って(BERTとか,もしくはseq2seq的に)学習した中間表現をfeaturizerとして使う思想です。
LSTM系のものもあれば、Transformer系のものもあります。
嬉しさとしては、事前学習を教師なし学習にすることができれば、学習データセットが少ない場合などにほかの手法よりもスコアが出る可能性があると思っています。
ただ、普通にNLPタスクっぽく扱うのは化学的な性質を捉えた中間表現にはならない気がするので、なんらかの工夫が必要だと考えました。
今回わたしはそれまでのsmiles2vecと変えて(観測範囲では新規)、smiles→smilesを予測するのではなく、もう少し立体的な実情を表すと考えるsmiles→morgan fingerprintを事前学習することにしました。(これが最初に触れた変なモデルです。)具体的には、

transoformer encoderの最終出力をMaxpoolingしてそれを全結合層+Sigmoid-XentropyでMorganFingerPrintを予測する。
という事前学習をChemblからサンプリングしたデータセットに対して行い、そのpretrainedモデルをLightGBMに接続することで、CV0.81を出すことに成功しました。pretrainの入力は後述するrandomized smilesでAugmentationしています。
さらなる検証は必要ですが、化合物のpretrainedモデルとして意外とやれる子が誕生したかもしれません。

細かい話としては、

  • tokenizeはchar-base。元素記号を扱うので当然だけど大文字小文字は区別する。
  • Transfomerのアーキテクチャ: maxlen120,hidden 192,head 4, hopping 4(コンペのレギュでこれ以上大きくならなかった)
  • Maxpooling : (1,4)で(120,192,1)→shape(120,192/4,1)に。その後Flatten
  • ターゲットのmorgan fingerprintは512 bit,FCFP相当。あまり疎だと学習が進みにくいのとbit衝突の回避のバランスを取りたかった。
  • lossは各bitごとのXentropyの和。これ使うために最終出力はSoftMaxじゃなくてsigmoidを活性化関数に使う。
  • chemblからのサンプルはSMILESの文字数で、[0-20],[20-40]...[80-100]で均等に(これをしないと、最も多い60付近の化合物に多い構造ばっかり予測してしまうせいかイマイチうまく行かなかった。)
  • DataAugmentationでRandomize SMILESするので、Augmentation前のMaxlenは100でも、transformer入力のMaxLenは120にした。
  • LightGBMは初期パラメータ。

で行いました。

smile2image

smile2記法からRDkit等で生成した化合物の画像にCNNなどの画像系手法を噛ませて扱おうとする手法です。
個人的には化合物が大きくなった時の画像の縮尺などの問題が出るからイマイチかなぁと思っています。
実際にためしてはいないのでわかりません。

randomized smiles

smiles2vecとかだとsmilesの順序を変更することによるDataAugementationをすることができます。 Randomized SMILES strings improve the quality of molecular generative models | Journal of Cheminformatics | Full Text

このときにSMILESを並び替えるのを手動で実装するのはめちゃくちゃ苦行なので、簡易的にですがRDkitの原子のRenumberによるrandomizeを利用するとよかったです。
方法としては、

  1. SmilesをRdkitで読み込んでmolにする。
  2. 分子に含まれる原子数がN個だったとして、1〜Nをシャッフルした配列を作る。
  3. 2で作った配列を元に、Chem.RenumberAtomsで原子の通し番号を振り直す。
  4. Chem.MolToSmiles(mol,canonical=False)で新しいSMILESを出力する。

これで少し違うSMILESが出力されます。Canonical=Trueだと同じのになっちゃうのでそこミスらないように注意です…

記述子+決定木

記述子はスケールが一定じゃない(原子の個数を数えたり、電荷っぽい値だったりが混ざっている)ため、ニューラルネット系の手法やサポートベクターマシン系の手法よりは決定木系の手法の方がファーストトライとしては楽かなと思います。
ただ、人間が関わらないタスクについては(感情判定のような主観評価が混ざらないという意味です)サポートベクターマシンが強いとかいう噂もあるので、試して見る価値は大いにあると思います。

fingerprint系の手法

fingerprintの多くはかなり疎で直接扱うのがあんまり筋が良くない感じでした。
morgan fingeprint(Circular fingeprint)や、その他Deepchemでサポートされる中でもbit数を固定できるfingeprintをまずは使うのが丸いんじゃないかなぁと思いました。

Chembl

データセットです。薬系の化合物がすごいいっぱい集まっています。事前学習をするようなタスクのデータセットを集める元として優秀だと思います。
ただ、ダウンロードにすんごい時間かかります。

QM

化合物の量子力学的な値のデータセットです。 QMでtrainされたモデルをStackingすると記述子とかでは扱いきれてない量子力学的な作用とかはいったりするのかなぁとか考えてました。

Tox21

化合物の毒性に関するデータセットです。毒性の方向にも色々あってそれらがすべてまとまってるので、各種タスクに絞ると意外とデータ量が少ないです。

感想と疑問

  • conda-forgeが使えないのきつかったです。(local importに気づくまで絶望していました。)
  • 実行マシン512MB/提出10MBも結構きつかったです。(最初の方気づかずにBERT-base相当のtransformer動かしてて無駄になりました。)
  • anacondaがあるとpipしにくいので、Rdkit様にはなんとかしてpip対応してほしい…
  • 発がん性とか実際は代謝生成物とかも追わなくちゃいけない気がするので、難しそうだなと思いました。
  • 化学の専門家がSMILESだけから発がん性当てるタスクしたらどのくらい精度出るのかが素朴に気になります。とりあえず人間は越えたのだろうか。
  • 実際のところ量子力学的な性質を考えるのは意味があるんだろうか。むしろタンパク質とかとの立体的な相互作用の方が効くのかも?(そうであれば原子とその立体的位置を特徴量にして学習したらうまく行ったのかも。)
  • DescriptorってChirarlity反映できるのどのくらいあるんだろう。(生体作用の場合はChirarlityは結構Criticalな気がする(素人))