くろたんく雑記帳

日常とか、わんちゃんとか、機械学習とか、競プロとか、

MENU

【書評】機械学習を解釈する技術〜予測力と説明力を両立する実践テクニック

森下さんの機械学習を解釈する技術〜予測力と説明力を両立する実践テクニック」を読んだので、まだ隅々までは読めていないが、感想を書きます。

Interpretable Machine Learningってとても話題で、最近だとHACARUSさんがInterpretable Machine Learningを翻訳して話題になっていたけどもこれともまた違った内容です。 hacarus.github.io どちらかというと、より実践的で、4つの機械学習の解釈手法をPythonで実装して、アルゴリズムを理解し分析から急所を捉えるといった構成になっていると感じました。


本書の概要

感想

解釈手法をマクロな解釈手法からミクロな解釈手法と使い分けで段階としては

  • Permutation Feature Importance
  • Partial Dependence
  • Individual Conditional Expectation

という流れは非常にわかりやすかったです。またその解釈手法の使い方を安全か注意が必要かという点で区別しており、

  • モデルのデバッグ
  • モデルの振る舞いを解釈
  • 因果関係の探索

という点で段階を分けて考えるというのは、実際の業務にも活きてくる内容と思いました。

書籍全体の流れは

  1. 線形回帰モデル
  2. Permutation Feature Importance
  3. Partial Dependence
  4. Individual Conditional Expectation
  5. SHapley Additive exPlanations

という流れで構成されており、私は特に後半の Individual Conditional ExpectationとSHapley Additive exPlanationsの章が秀逸と思いました。というのもこれらをここまで丁寧に説明している書籍が殆どないので、とても理解が深まりました(Permutation Feature Importance, Partial Dependenceに関しても同様です)

コードはこちらでサポートされています。

github.com

特に、交互作用はよく使い割に説明性が欠けることが多く、理論実装ともにわかりやすかったです。

SHapley Additive exPlanationsの説明はとりあえず僕が知る限り一番わかりやすかったので、これまで理解できなかった方も理解が進むのではないでしょうか?

私にとっては、このIndividual Conditional ExpectationとSHapley Additive exPlanationsの実装を試せたことが最大の収穫です。

あと何気に、付録のRの実装があったのがなにげに嬉しかった。 そして、付録Bで「線形回帰モデルが元来備えている解釈」と「機械学習の解釈手法を通した解釈」が整合的であることを示しているのはとてもおもしろかった。むしろこれで完全に納得したといった感じです。


終わりに

すべての章に通じて言えますが、 数学的な内容は最低限に抑え、高度な内容を平易な文章と簡易なコードでとてもわかり易く解説されていると思いました。

「解釈可能な機械学習」を題材に非常に勉強になる書籍でした。普段から意識はしているので、実践にすでに使っているものもあれば、これは使えるなという新しい発見もできました。このような技術はきちんと実践に活かせるようにならなくては意味がないので得た知識を実践に活かせるようになりたいものです。

参考図書

同時に出版されている齋藤さんと安井さんの「施策デザインのための機械学習入門〜データ分析技術のビジネス活用における正しい考え方」 こちらも書評を書いてあるが、すでに業務等の実践で機械学習をガンガン使っている人向けで特に「バイアス」「反実仮想」を考慮した機械学習フレームワークを提唱しており、私としては「「推薦」の内容でバイアスも考慮した具体的な解説と実践的なコードがここままでわかりやすく書かれている書籍は他にはないと思います

blacktanktop.hatenablog.com

安井さんの「効果検証入門」、反実仮想についてわかりやすく記述されているのでオススメ

【書評】施策デザインのための機械学習入門〜データ分析技術のビジネス活用における正しい考え方

齋藤さんと安井さんの「施策デザインのための機械学習入門〜データ分析技術のビジネス活用における正しい考え方」を読んだので、まだ隅々までは読めていないが、感想を書きます。

今回の書籍は初学者向けというよりは、すでに業務等の実践で機械学習をガンガン使っている人向け、もちろんこれからという人も事前に読むと実践の場面でとてもスキルが活きると思います。これまで、共著の安井さんは、「効果検証入門〜正しい比較のための因果推論/計量経済学の基礎 」という書籍を以前書かれていて、合わせて読むと効果的とも思いました。

基本的に機械学習をベースに「推薦」の内容でバイアスも考慮した具体的な解説と実践的なコードがここままでわかりやすく書かれている書籍は他にはないと思います。


本書の概要

感想

まずいきなり、「機械学習を使ってまで解くべき問題はあくまで意思決定の最適化問題であって、予測誤差の最小化問題を解くべきではありません」というのはそのとおりで、データサイエンティストが陥りがちな急所をよく突いているなと思いました。

1章 機械学習実践のためのフレームワーク

1章ではフレームワークについて言及されており

  1. KPI を設定する
  2. データの観測構造をモデル化する
  3. 解くべき問題を特定する
  4. 観測データのみを用いて問題を解く方法を考える
  5. 機械学習モデルを学習する
  6. 施策を導入する

このフレームワークを実践することで「機械学習モデルを学習する」の部分がとても活きていくるということを述べられております。

2章 機械学習実践のための基礎技術

2章以降は上記のフレームワーク則り、正確な予測と高性能な意思決定を導く流れが具体的に記述されていて、なるほどなと思わされました。また私が一番うれしかったのはOpen Bandit Piplineについての言及があったことです

github.com

以前発表があったときにちょっとやってみようかなぁと思ったのですが、当時私の理解が浅かったこともありうまく手をつけられなかったのですが、本書では詳しく解説されているし、サポートページでJupyter Notebookもあるので、あーこういうことねっていう使い方がすぐわかります。

github.com

Notebookや書籍にも書かれている通り、全データは11G程度あるので、まずはサンプルでやるのをおすすめします。

ここのことですね、data_path=Noneでいけます

https://github.com/st-tech/zr-obp/blob/3cb08b507e6c1b351d94e22b6a9d9f88d6f1cc88/obp/dataset/real.py#L42

全データはメッチャ重いが現実のデータを想定するとこのくらいでも不思議では無いので感覚を掴むには良いかと思います。 また、多くの方がこのようなlogデータを取り扱うと思うのでどのようなデータがアレばこのフレームワークに収められるのかということを理解するためにもぜひ実行しながら読みすすめることをおすすめします。

3章 Explicit Feedbackを用いた推薦システム構築の実践

ここからは思っきり推薦の話です。推薦には、☆ x 3のように「明示的」に評価がされているものとログデータにおけるコンバーションポイントのクリックのように「暗黙的」に評価がさえているものに分かれます。3章はExplicit Feedback(明示的の方)を扱っています。私は、MFを業務でガンガン使っておりますが、バイアスの考え方を含め今後の参考になることがたくさん記述されおりました。また、Yahoo! R3データは使ったことがなかったのですが、とても興味深い結果になっており実践のデータに適用してみたいなと思いました(MFのコードは巨大なデータを使うのでCythonで実装されたものを使っているのですが傾向スコアやナイーブ推定量を考慮した実装を書かなきゃいけないなぁ・・・なんておもったり)

4章 Implicit Feedbackを用いた推薦システムの構築

実践の場ではExplicit Feedbackはほぼ無いので、どちらかといえば私の興味はこちらの、 Implicit Feedbackの推薦システムです。こちもフレームワークに則り、ランキング学習に焦点を絞って語られており、Pytorchでの実装もとてもわかり易く結果もとてもおもしろい結果が見えています。

5章 因果効果を考慮したランキングシステムの構築

こちらは更に発展的な内容で、4章においては、「真の嗜好度合いの総量の最大化」を問題設定としていたが、 「推薦枠経由で観測されるコンバーション数や収益、コンテンツ視聴時間等のKPIの最大化」 「推薦経由ではなく、プラットフォーム全体で観測されるコンバーション数や収益、コンテンツ視聴時間等のKPIの最大化」 を問題設定としており、こちらは実際の実務に直結する内容で、速攻取り入れていきたいなというような内容でした。

あと、付録がとても充実していて、追加の解説や演習問題があるので理解が深まります。(演習問題の略解でもいいのでほしいなぁと思いました。合ってるのかどうか判断できないし)


終わりに

すべての章に通じて言えますが、具体的な図表が書かれているのでとても状況がわかりやすく、理解が促されます。

数学的な議論はとても多く数式は省略せずきちんと説明されているため理解が促されました。特に随所に登場する「バイアス」を考慮するための考え方はとてもわかり易く解説されていると思いました。

「反実仮想機械学習」を題材に非常に勉強になる書籍でした。今回はザーッと読んで手を動かした程度の理解ですので、きちんと実践に活かせるようにするには実戦投入していくのが一番かと思いました。

CounterFactual Machine Learning勉強会には、何度か参加させていただいたが、今後も是非開催していただきたいと思います。 cfml.connpass.com

参考図書

安井さんの「効果検証入門」、反実仮想についてわかりやすく記述されているのでオススメ

同時に出版されている森下さんの「機械学習を解釈する技術〜予測力と説明力を両立する実践テクニック」 こちらも書評を書いてあるが、現在実践で多く求められる説明可能 AI について解説している書籍

blacktanktop.hatenablog.com

星野本「調査観察データの統計科学」 私はここから先に勉強していたので、比較的すっと入ってこられたので、こちらも一読すると良いかもです。

論文執筆TeX環境

経緯

社会人ドクター生活も、4ヶ月経って結構バタバタしていたため完全ブログストップ状態。

blacktanktop.hatenablog.com

2021年4月に入ってすぐ、論文を書く流れになったもののTeXはよくわからんけどOverleafなら問題なく出来るよっていうことで、TeXを使い始めました。

www.overleaf.com

結果的に便利で、作法もなれては来たが、差分管理とかぐちゃぐちゃになりがちで(GitHub連携はカネがかかるし)仕方なくTeX環境をローカルに作ってGitHubで管理する。必要に応じてというか最終段階に入る頃にOverleafを使うのがいいんじゃんっていうところに行き着きました。

これは@hyper_pigeonさんも言っていることであながち間違いじゃないんだなぁというか共通認識を持っている方がいるということで、さっと環境を準備するかとなったという経緯です。 hyper-pigeon.hatenablog.com

環境構築

TeXの知識吸収

TeXの知識が殆どないので奥村先生のLaTeX2ε美文書作成入門読めよっていう話。こちらを読めば、今抱えている問題も解決しそう。マダ読み中

どうやるか

  • 絶対にローカルに環境をセットアップしたくないのでDocker一択
  • まぁ他に同じようにやっている人いるでしょう
  • というわけで、結論から言うと、 先程紹介した@hyper_pigeonさんのレポジトリ github.comTwitterでepsが変換できないとぼやいていたら神の一言で、「テンプレートあるよ」ってな感じで@meta1127さんのレポジトリを参考にしました。(ご本人からもeps自体の解決にはならないかもだけど、参考までにっていうことで。)

結論

ここでつぶやいている通り、以下のようになりました。

Overleafみたいにどんなフォーマットでも平気だぜみたいな感じにはならんので、

  • Docker imageは大きく2つ使い分ける

    • pman0214/docker-alpine-texlive-ja-epspdf
    • Paperist/docker-alpine-texlive-ja github.com github.com

    • 主に、英語はpman0214/docker-alpine-texlive-ja-epspdfを日本語はPaperist/docker-alpine-texlive-jaを使う。どちらでも平気なものもある。

    • 先程言及したepsをfigureやtemplate自体に使われている場合は前者を使うとできます。
  • .latexmkrcでいい感じに設定する

    • これは変換コマンドがテンプレートと合っていないといい感じに変換できないということのようで、きちんとは理解できていないですが、エラーが出たら適宜対応するとなんとかなりそうということがわかりました。
      • とりあえずエラーを読む。(よくわからん事が多い)
      • .latexmkrc の
      • $latex のpdflatexをupdflatex, platex, uplatexに変えたりする。(そんなようなコメントが出ているときがあるので気付けるはず)
      • $latex_silent 上と同じものに変更
      • bblとかリファレンス周りで変なことがおきていそうなら
      • $bibtex のupbibtexを pbibtexに変えたりする。
  • latexmkでpdf化まで持っていく

    • 複数のコマンドでゴニョゴニョやるのはむずい(めんどくさかった)ので latexmkコマンドで一発(ただし、.latexmkrcをつかって設定はコントロールする)
  • dockerコマンド長いからMakefileでタスクっぽく実行する
    • これは@hyper_pigeonさんのアイディアをそのまま使わせていただきました。後で示す私のレポジトリの場合はもっと簡略化してmake latexmkで動くようにしました。
  • GitHub Actionsも設定してバーション管理
  • これはどちらでも良かったのですが、せっかくなので@meta1127さんが教えてくれたものを参考にさせていただきました。ただGitHub Actionsやtagなどは使ったことがほぼなかったのでこちらも勉強しました。

成果物

こちらに最終的に挙動を一応確認したものをpushしておきました。

github.com

解決しました。単純に.gitignoreに*.pdfとしていてpushされてないということでした。!/figure/*.pdfなどとすることでpushできて問題なくbuildできました。

ただ、これには若干問題があり、figureのpdfなどをfig/というディレクトリに入れた状態で実行するとローカルではビルドできるけどGitHub Actionsではコケるという状態を確認しています。

.
├── Definitions
├── Makefile
├── figure
│   ├── fig1_XXX.pdf
│   ├── fig2_YYY.pdf
└── main.tex

イメージとしてはこんな状態のものです。解決法がわかる方がいたら教えてほしいです。

終わりに

この作業に2日位は費やしてしまったが、博士論文のフォーマットもその他のジャーナルのフォーマットも、とりあえず挙動が確認できたので、一旦、書くことに集中出来るという状態になりました。

環境系は疲れる。

GraphQL-based APIによるPDBのデータ取得(Ligand 情報編)

はじめに

はじめて(PDB)https://www.rcsb.org/というタンパク質のDBを触ったが、GUIではなくAPIを利用したデータ取得に少し手間取ったのでメモ。参考は以下

今回は主に、GraphQLによる取得について書く

GraphQL-based API

GraphQLは使ったことがなかったが、JSON形式でqueryを記述することで複雑なデーター構造の所望のデータを取得できるようで、今回の需要にあっていると感じたし、そもそもこっちで取得しろよっていう雰囲気を感じた。

https://data.rcsb.org/#data-api https://data.rcsb.org/migration-guide.html#legacy-fetch-api

経緯

今回は以下のような状況でLigand情報を取得しようと試みた。

目的はLigandの分子量、ALOGPなどの各種特性を計算したいが、 そのLigandの

  • ID
  • 名前
  • SMILES

が不明である。

ただし、

  • 論文上で複数のProtein+Ligand複合体のPDB IDがわかっている。
  • そのLigandとされる構造式も書かれている。

PDB IDからのLigand情報の取得

ドキュメントを読む限り、

Entry:特定のPDB構造関するデータ。

  • 4文字の英数字による識別子(PDB IDで例えば、1Q2W)
  • タイトル、寄託者のリスト、登録日、公開日、実験の詳細

Entity : PDBに存在する化学的な分子の内容。大きく分けて3種類ある

  • polymer_entity - タンパク質、DNA、RNA
  • Branched_entity - 直鎖状または分岐状の炭水化物(糖類およびオリゴ糖とか)
  • nonpolymer_entity - 低分子化学物質(酵素補酵素、Ligand、ionとか)

Entity Instance : PDBに存在するEntityのコピーでEntityと同様3種類ある

  • polymer_entity_instance
  • branched_entity_instance
  • nonpolymer_entity_instance

Assembly : 生物学的ユニットを形成する構造要素

例えば、以下のようなものが記述されている

Chemical Component : PDBエントリーに含まれる全ての残基や低分子のデータ

例えば、以下のようなものが記述されている

  • 化学記述子(SMILES & InChI)
  • 化学式
  • 系統的な化学名

ドキュメントを頼りに色々やったが、クエリ上で、フィルターするやり方がいまいちわからない。今回で言えば、pdbx_chem_comp_descriptor.type == 'SMILES_CANONICAL' & pdbx_chem_comp_descriptor.program == 'OpenEye OEToolkits' この様に絞りたいが、どの様にクエリを書けばいいのだろうか・・・それに、階層構造もスキーマを見てもよくわからん

しかし幸い、サンプルにLigandのSMILESを取得するQueryが書かれていたのでそちらを参考にPythonで取得できるようにした。

import requests

query = '''
{
  entry(entry_id:"1AZ1") {
    nonpolymer_entities {
      rcsb_nonpolymer_entity_container_identifiers {
        entry_id
      }
      nonpolymer_comp {
        chem_comp {
          id
          type
        }
        pdbx_chem_comp_descriptor {
          descriptor
          type
          program
        }
      }
    }
  }
}
'''

url = "https://data.rcsb.org/graphql?query=" + query
response = requests.get(url)
json_data = response.json()
json_data

とすると以下のような結果が返ってくる

{'data': {'entry': {'nonpolymer_entities': [{'nonpolymer_comp': {'chem_comp': {'id': 'NAP',
       'type': 'non-polymer'},
      'pdbx_chem_comp_descriptor': [{'descriptor': 'NC(=O)c1ccc[n+](c1)[C@@H]2O[C@H](CO[P]([O-])(=O)O[P@@](O)(=O)OC[C@H]3O[C@H]([C@H](O[P](O)(O)=O)[C@@H]3O)n4cnc5c(N)ncnc45)[C@@H](O)[C@H]2O',
        'program': 'CACTVS',
        'type': 'SMILES_CANONICAL'},
       {'descriptor': 'NC(=O)c1ccc[n+](c1)[CH]2O[CH](CO[P]([O-])(=O)O[P](O)(=O)OC[CH]3O[CH]([CH](O[P](O)(O)=O)[CH]3O)n4cnc5c(N)ncnc45)[CH](O)[CH]2O',
        'program': 'CACTVS',
        'type': 'SMILES'},
       {'descriptor': 'c1cc(c[n+](c1)[C@H]2[C@@H]([C@@H]([C@H](O2)CO[P@@](=O)([O-])O[P@](=O)(O)OC[C@@H]3[C@H]([C@H]([C@@H](O3)n4cnc5c4ncnc5N)OP(=O)(O)O)O)O)O)C(=O)N',
        'program': 'OpenEye OEToolkits',
        'type': 'SMILES_CANONICAL'},
       {'descriptor': 'c1cc(c[n+](c1)C2C(C(C(O2)COP(=O)([O-])OP(=O)(O)OCC3C(C(C(O3)n4cnc5c4ncnc5N)OP(=O)(O)O)O)O)O)C(=O)N',
        'program': 'OpenEye OEToolkits',
        'type': 'SMILES'},
       {'descriptor': 'InChI=1S/C21H28N7O17P3/c22-17-12-19(25-7-24-17)28(8-26-12)21-16(44-46(33,34)35)14(30)11(43-21)6-41-48(38,39)45-47(36,37)40-5-10-13(29)15(31)20(42-10)27-3-1-2-9(4-27)18(23)32/h1-4,7-8,10-11,13-16,20-21,29-31H,5-6H2,(H7-,22,23,24,25,32,33,34,35,36,37,38,39)/t10-,11-,13-,14-,15-,16-,20-,21-/m1/s1',
        'program': 'InChI',
        'type': 'InChI'},
       {'descriptor': 'XJLXINKUBYWONI-NNYOXOHSSA-N',
        'program': 'InChI',
        'type': 'InChIKey'}]},
     'rcsb_nonpolymer_entity_container_identifiers': {'entry_id': '1AZ1'}},
    {'nonpolymer_comp': {'chem_comp': {'id': 'ALR', 'type': 'non-polymer'},
      'pdbx_chem_comp_descriptor': [{'descriptor': 'O=C2c1c3c(ccc1)cccc3C(=O)N2CC(=O)O',
        'program': 'ACDLabs',
        'type': 'SMILES'},
       {'descriptor': 'OC(=O)CN1C(=O)c2cccc3cccc(C1=O)c23',
        'program': 'CACTVS',
        'type': 'SMILES_CANONICAL'},
       {'descriptor': 'OC(=O)CN1C(=O)c2cccc3cccc(C1=O)c23',
        'program': 'CACTVS',
        'type': 'SMILES'},
       {'descriptor': 'c1cc2cccc3c2c(c1)C(=O)N(C3=O)CC(=O)O',
        'program': 'OpenEye OEToolkits',
        'type': 'SMILES_CANONICAL'},
       {'descriptor': 'c1cc2cccc3c2c(c1)C(=O)N(C3=O)CC(=O)O',
        'program': 'OpenEye OEToolkits',
        'type': 'SMILES'},
       {'descriptor': 'InChI=1S/C14H9NO4/c16-11(17)7-15-13(18)9-5-1-3-8-4-2-6-10(12(8)9)14(15)19/h1-6H,7H2,(H,16,17)',
        'program': 'InChI',
        'type': 'InChI'},
       {'descriptor': 'GCUCIFQCGJIRNT-UHFFFAOYSA-N',
        'program': 'InChI',
        'type': 'InChIKey'}]},
     'rcsb_nonpolymer_entity_container_identifiers': {'entry_id': '1AZ1'}}]}}}

対応するLigandは複数存在することもあるようだ。 今回はひとまずすべて取得し後に構造式から判断する。

以下のような関数を作成

  • PDB IDを引数とする
  • 特定のSMILESも絞れていないので取得後に、pdbx_chem_comp_descriptor.type == 'SMILES_CANONICAL' & pdbx_chem_comp_descriptor.program == 'OpenEye OEToolkits' となるデータのみ抽出する
import requests
def entry_to_ligand(entry_id):
    ligand_list = []
    query = '''
    {
    entry(entry_id:"'''  + entry_id + '''") {
        nonpolymer_entities {
        rcsb_nonpolymer_entity_container_identifiers {
            entry_id
        }
        nonpolymer_comp {
            chem_comp {
            id
            type
            }
            pdbx_chem_comp_descriptor {
            descriptor
            type
            program
            }
        }
        }
    }
    }
    '''
    url = "https://data.rcsb.org/graphql?query=" + query
    response = requests.get(url)
    json_data = response.json()
    for i in json_data.get('data').get('entry').get('nonpolymer_entities'):
        entry_id = i.get('rcsb_nonpolymer_entity_container_identifiers')
        nonpolymer_comp = i.get('nonpolymer_comp')
        ligand_id = nonpolymer_comp.get('chem_comp').get('id')
        for data in nonpolymer_comp.get('pdbx_chem_comp_descriptor'):
            if (data.get('type') == "SMILES_CANONICAL") and (data.get('program') == "OpenEye OEToolkits"):
                smiles = [data.get('descriptor')]
                type = [data.get('type')]
                program = [data.get('program')]
                d = {'entry':entry_id, 'ligand_id':ligand_id, 'smiles':smiles, 'type': type, 'program': program}
                ligand_list.append(d)
    return ligand_list


entry_to_ligand("1AZ1")

# [{'entry': {'entry_id': '1AZ1'},
#   'ligand_id': 'NAP',
#   'program': ['OpenEye OEToolkits'],
#   'smiles': ['c1cc(c[n+](c1)[C@H]2[C@@H]([C@@H]([C@H](O2)CO[P@@](=O)([O-])O[P@](=O)(O)OC[C@@H]3[C@H]([C@H]([C@@H](O3)n4cnc5c4ncnc5N)OP(=O)(O)O)O)O)O)C(=O)N'],
#   'type': ['SMILES_CANONICAL']},
#  {'entry': {'entry_id': '1AZ1'},
#   'ligand_id': 'ALR',
#   'program': ['OpenEye OEToolkits'],
#   'smiles': ['c1cc2cccc3c2c(c1)C(=O)N(C3=O)CC(=O)O'],
#   'type': ['SMILES_CANONICAL']}
# ]

DataFrameで扱いたいなら適宜pandasで

pd.DataFrame(entry_to_ligand("1AZ1"))

#  entry_id    ligand_id   smiles  type    program
#  1AZ1    NAP [c1cc(c[n+](c1)[C@H]2[C@@H]([C@@H]([C@H](O2)CO...   [SMILES_CANONICAL]  [OpenEye OEToolkits]
#  1AZ1    ALR [c1cc2cccc3c2c(c1)C(=O)N(C3=O)CC(=O)O]  [SMILES_CANONICAL]  [OpenEye OEToolkits]

あとは適宜、ProteinとLigand PDB IDを適宜流し込んであげれば欲しいデータは取得できた。(ただし、NAP, CL, URE, PO4などのようなLigandを弾きたければ適宜弾く必要はある。)

df = pd.DataFrame()
for entry_id in entry_ids:
    tmp = pd.DataFrame(entry_to_ligand(entry_id))
    df = pd.concat([df, tmp])
df.reset_index().drop('index', axis=1)

# entry_id ligand_id   smiles  type    program
# 1AZ1 NAP [c1cc(c[n+](c1)[C@H]2[C@@H]([C@@H]([C@H](O2)CO...   [SMILES_CANONICAL]  [OpenEye OEToolkits]
# 1AZ1 ALR [c1cc2cccc3c2c(c1)C(=O)N(C3=O)CC(=O)O]  [SMILES_CANONICAL]  [OpenEye OEToolkits]
# 1DDR CL  [[Cl-]] [SMILES_CANONICAL]  [OpenEye OEToolkits]
# 1DDR MTX [CN(Cc1cnc2c(n1)c(nc(n2)N)N)c3ccc(cc3)C(=O)N[C...   [SMILES_CANONICAL]  [OpenEye OEToolkits]
# 1DDR URE [C(=O)(N)N] [SMILES_CANONICAL]  [OpenEye OEToolkits]
# ...  ... ... ... ...
# 2BGD CL  [[Cl-]] [SMILES_CANONICAL]  [OpenEye OEToolkits]
# 2BGD PO4 [[O-]P(=O)([O-])[O-]]   [SMILES_CANONICAL]  [OpenEye OEToolkits]
# 2BGD NA  [[Na+]] [SMILES_CANONICAL]  [OpenEye OEToolkits]
# 7STD CA  [[Ca+2]]    [SMILES_CANONICAL]  [OpenEye OEToolkits]
# 7STD CRP [CC[C@]1([C@H](C1(Cl)Cl)C)C(=O)N[C@H](C)c2ccc(...   [SMILES_CANONICAL]  [OpenEye OEToolkits]

終わりに

正直、Protein:Ligandの組が1:1対応と思っていたのだが、いわゆる低分子だけでなく、NAPとかイオンとかその他諸々も含むので複数もあり得るのかと思った。

仕方がないので一旦すべて取得して、不要なものはルールで除去し、差雌雄的にRDKitで構造式に直して、目視で論文に書かれている構造式と照らし合わせて、目的のLigandのSMILESを得ることができた。

GraphQLでの取得はDBの構造というかKeyの単語がスキーマをみてもよくわからなかったりするのでちょっと困る。ちょっとずつおぼえていくしかないか

社会人ドクター生活スタート

とてもバタバタしていて、ブログは放置していましたが、理由があって、

結論から言うと、博士後期課程の学生として、東京工業大学 情報理工学院 情報工学系の大上研究室に所属することになりました。

www.li.c.titech.ac.jp


経緯

以前より、創薬分野に関わる研究がしたいと思っていて、現職は続けながら、社会人として学位取得を目指し頑張っていことと思い立ったのが2020年の8-9月

Twitter上でいろんな方が教えてくれて、色々検討しつつ、所属先の指導教員である大上先生が声をかけてくれたのがきっかけで、受け入れについて相談にのってもらいました。

上司と家族に説明し、去年からTOEIC受けたり、10年くらい前の修士論文を引っ張り出して取りまとめたり、今年の2月の入試に向けての準備や実際の入試はとても大変だったけれど、3月に無事に合格発表を受けてホッとしました。

今後

これまでの経験は

これらの経験も活かしつつ、 コンピュータサイエンスの応用として、おもしろい生命科学の課題解決ができたらいいなと思っています。

2020年を振り返る

今年はコロナ情勢の中、いろんな事をやった一年だった。 結果的に、AtCoderにチャレンジしたのが大きかった。その他にも試験を何個か受けたり、本読んで感想書いたりして、まぁまぁ充実していた。

簿記(2020-02)

XBRLから財務分析を行う話があったのだがXBRLの取り扱い自体はできても、そもそも財務に関するドメイン知識が皆無だったので、基礎を学ぶためにまずは簿記の試験を1ヶ月程度かけて行った。無事合格。3級は基礎中の基礎でこれはこれで勉強になったが、いわゆる財務分析についても勉強していきたい。

blacktanktop.hatenablog.com

はてぶ(2020-06~)

とりあえず、自分がやってることというのはだいたい忘れていくし、なにかの折に何者なのかもわかりやすいように、当時興味あったことをログとして残すことにした。

blacktanktop.hatenablog.com

AtCoder (2020-05~)

時間もあったのと数学的思考をプログラミング化するいい題材がないか考えて、AtCoderを始めた。

blacktanktop.hatenablog.com

なんだかんだ半年くらいは続いて、今もコンテストには参加しているけど、とっさのひらめき・知識・実装力不足を実感した。そして半年もやってるのにまだ沼から抜け出せてないOrz

初めて慣れてきた頃に、良い書籍が出て勉強になった。 blacktanktop.hatenablog.com

今年は合計で数百問は解いたけど、コンテストの復習も兼ねて、再度解き直しを行った。

blacktanktop.hatenablog.com

書評 (2020-07~)

今年は、本を読むだけでなく、とりあえずまとめてみるっていうことを何回かやった。ただただ読むだけよりも、文章にすることで、細かいところをきちんと調べるようになったので、よりinputが捗った。これら以外の本も色々読んだけど、まとめられたのは、このくらいで、これからも続けていきたい。

blacktanktop.hatenablog.com

blacktanktop.hatenablog.com

blacktanktop.hatenablog.com

blacktanktop.hatenablog.com

blacktanktop.hatenablog.com

blacktanktop.hatenablog.com

blacktanktop.hatenablog.com

blacktanktop.hatenablog.com

移住 (2020-07~)

なにげに、結構でかい話で一生に1−2度くらいしかな異買い物をすることに。去年秋くらいから家庭の事情もあり、移住計画スタート。いい場所に家を建てられそうだったので、速攻土地をおさえ、なんだかんだ、去年末から家全体の設計を考えて、インテリアはどうするだの、ZEHにするだのと進んで、諸々終えたのが今年の2−3月。4月の緊急事態宣言直前に地鎮祭。ものの数ヶ月建築工事が終わって、引き渡しってな感じである意味激動の数ヶ月が裏では動いてたっていう。まぁ計画当初は去年秋で、まさかコロナの騒ぎがここまで大きくなるとは想像もしていなかったけど、結果的には無事に移住もできてフルリモートで働けるしイイコトづくし。

スタサプ・TOEIC(2020-09-10)

諸事情でTOEICを受けざるを得ず、1ヶ月くらい真剣にスタサプと向き合った。これまでTOEICは受けたことがなくどんなものかもよくわかっていなかったが、結果はギリ700超えでまぁ初手ではこんなもんかと、一旦納得した(分布的には平均よりちょっといいってことになってる)。家で模試を解いてたときは素点計算で800位だったので、もう少し試験慣れが重要かなと思ったけど、ここ数年割と離れていた割には、やはりリスニングの方ができるのはNetflixを英語字幕で観まくったり、友人と会話しているおかげかな・・・っていうかReadingが悪すぎ。まぁ機会があればもう少し対策して実際に800くらいはいけそうと思ったので、まぁもうすこし頑張ってもいいかな。英語の試験対策はつまらない・・・TOEICできなくてもある程度の英語でのコミュニケーションはできるしね。まぁ逆にTOEICできても話す場面で全く話せないっていうのだけは避けたくて、日常会話に力を入れてきたのがとりあえず受けて成果を出すって言うところに単純に活きた感じだった。

ちなみに、スタサプは月3000円位だったけど割とコスパがいいと思った。集中して1−2ヶ月やるだけで感触はつかめる。ただ試験対策は模試を時間通りに解いてやり直してを繰り返さないと、試験の感覚を養えないのと、試験は音が聞きづらい(ホールに響く感じになる)のでそこらへんの慣れも重要な気がした。 f:id:black_tank_top:20201231074750j:plainf:id:black_tank_top:20201231074852j:plain

業績まとめ

諸事情で、過去の業績をまとめる必要があり、大学から大学院・その後理研・現職までをザーッとまとめるっていう作業をしていた。修論を整理しながら改めて、自分のやりたいことの方向性も固まってきたので、来年以降その方向で進んでいけるといいなと思っている。

終わりに

今年は、思いったら始めるっていう感じで通したので、来年はもう少し計画的にやりたい。来年からは何ヶ年計画で色々とやりたいことがあるので、年明けから結構忙しくなりそうだ。

Python3で解いた AtCoderの勉強になった問題 2020

サマリー

  • 6月くらいから、AtCoderやってみようと、ふと思い立って初めて約半年。
  • 年末時間ができたから復習していてそこで面白かった問題を上げていく。
  • 基本過去記事の再掲しつつ、もう一度振り返る。

AtCoder Beginner Contest

169 C-Multiplication 3 (2020-05-31)

問題は以下 atcoder.jp 初めての参戦で、いきなりこの問題でWAを超絶だした記憶。オーバーフローや計算精度に関する問題も出るのかと痛感した。

どういう時に問題が起きるのかをそもそも理解できずに、試行錯誤しつつ、以下のうように、浮動小数は実際には正しい値を保持していないことを改めて知って勉強になった。

0.07 * 100
# 7.000000000000001
0.29 * 100
# 28.999999999999996

でもDecimalモジュール使えばいいやと思って通した。

a, b = map(str, input().split())
res = int(a) * Decimal(b)
print(int(res))

けど本来は、数値をそのまま掛け合わせて後で割って整数部分だけ取得するとやってほしかったんだろうなぁ

a, b = map(str, input().split())
c,d = b.split('.')
# int(c+d)とするとbを文字通り100倍された結果になる。
res = a * int(c+d) 
print(res//100)

169 D - Div Game (2020-05-31)

問題は以下 atcoder.jp この当時は、素因数分解をする方法もコードに落としてなくて1から実装しなきゃで大変だったが、それさえできればあと一工夫でできる。


170 D - Not Divisible (2020-06-14)

問題は以下

atcoder.jp

この当時は、エラトステネスの篩的な考えもコードに落としてないし、発想としても持っていなかった。

  • リストに含まれるある値の定数倍の値をふるい落とすという考えを学んだ
  • また、inで探すときは対象をlistではなくsetにしておくことが速度として重要というのも学んだ
n = int(input())
a = list(map(int, input().split()))
a_s = sorted(a)
a_set = set(a_s)
maxv = max(a)
res = [0] * (maxv+1)
rep = 0
for i in a_s:
    # iを評価してない時はiを含むの定数倍のところは+1
    if res[i] == 0:
        for j in range(i, maxv+1, i):
            res[j] += 1
    # iを評価してあればiを+1
    # すでに定数倍として評価されていたら2以上にしたい
    else:
        res[i] += 1
cnt = 0
# 最終的に検索対象が定数倍リスト内で1になっているものだけをカウントする
for i in range(n):
    if res[a[i]] == 1:
        cnt += res[a[i]]
print(cnt)

172 C - Tsundoku (2020-06-27)

問題は以下 atcoder.jp

ここらへんから記事書き始めた。

  • しゃくとり
  • 累積和と二分探索

この時、これらのイメージを掴んだ感じ

blacktanktop.hatenablog.com

172 D - Sum of Divisors (2020-06-27)

問題は以下 atcoder.jp

Pythonだと{\displaystyle O(NlogN)}でも{\displaystyle N \geq 10^8)}だと通らないということを感じた回だった。

工夫すれば、

約数 {\displaystyle \times} 等差数列の和で計算できる。

blacktanktop.hatenablog.com


173 C - H and V (2020-07-05)

問題は以下 atcoder.jp

はじめてbitをコードで起こした。bit演算子である、'&'や '>'の意味を学んだ。

  • bit全探索
  • itertoolsのproduct の両者を学んだ。bitは未だに苦手でbitで全パターン検索するような場合は先にproductで作ってしまうほうが考えやすい。

blacktanktop.hatenablog.com


174 C - Repsept (2020-08-02)

問題は以下 atcoder.jp

愚直に'7'をいくつも並べたものを順に割っていくっていうのはだめ。基本的に{\displaystyle x * 10^6}という様にたくさん文字列を羅列する形は遅い。

純粋に'7', '77', '777' ... なので初項7, 公比10の等比数列と考える。

  • 等比数列の和
  • 余りで考えれば良いのでモジュラの性質をうまく使って10倍されていく時にあまりを10倍して考えればオーバーフローなどを木にする必要がなくなる。

blacktanktop.hatenablog.com


176 D - Wizard in Maze (2020-08-22)

問題は以下 atcoder.jp

結構重くて、なかなか通らなかった。 対策としては、外側へのはみ出しを防止。結構むずくて、今解き直しても素直に実装できなそう。

  • 上下左右を'##'で埋める。
  • あとは歩いていけなくなるまで普通の迷路探索 。
  • ワープのパターン出いけるところに歩いていける範囲としてキューに加える。

blacktanktop.hatenablog.com


178 C - Ubiquity (2020-09-13)

問題は以下 atcoder.jp

愚直に条件に当てはまるものを求めるのではなく、包括原理より、全パターンから条件に合わないものを引くとしたほうがいいパターン。

ポイントとして、{\displaystyle x^n}という形は愚直に計算すると遅いので計算過程ではくり返し二乗法を使う

blacktanktop.hatenablog.com


180 C - Cream puff (2020-11-01)

問題は以下 atcoder.jp

単純な約数列挙だが、いちからコード書くと結構大変だった。考え方としては素因数分解と同じようなイメージでいけた。{\displaystyle 1)}から{\displaystyle \sqrt{n}))}まで試し割りして、 割り切れたものと、割り切れたときの商を別々に保持して後でリストを連結

blacktanktop.hatenablog.com


181 D - Hachi (2020-10-17)

問題は以下 atcoder.jp

単純な倍数判定だが、入れ替えを考慮するため侮れない。 結果から言うと、Counterで8の倍数の下3桁の文字列のパターンと一致するかどうかで判断。Counterの結果は引き算できることがポイント

blacktanktop.hatenablog.com


182 D - Wandering (2020-11-08)

問題は以下 atcoder.jp

その時までの値の累積和とその時までの値の最大値の和を考える。 図示しながら考えるとわかり良い。

blacktanktop.hatenablog.com


183 D - Water Heater (2020-11-15)

問題は以下 atcoder.jp

持ち方がポイントで、使うスケジュールの最初の時刻でプラス、終わりでマイナスしたリストを頭から順に足し合わせていくようにして、スケジュールの累積和として考える

blacktanktop.hatenablog.com


184 D - increment of coins (2020-11-22)

問題は以下 atcoder.jp

メモ化を自動的にやってくれるlru_cacheというデコレータを知った。 メモ化と比較するとスッキリすることがわかる。

blacktanktop.hatenablog.com


186 D - Sum of difference (2020-12-19)

問題は以下 atcoder.jp

久しぶりにコンテストの最中にDまで解けた。
引き算の絶対値がポイントで、事例を書き出して、ソートして考えても良いと考えられれば単純な数え上げとなる。

blacktanktop.hatenablog.com


AtCoder Regular Contest

106 B - Value (2020-10-24)

問題は以下

atcoder.jp

Union-Find問題。連結しているグループの値の和が一致するかを検討するだけ。このとき注意しなくてはいけないのは、find()でrootを求めていく。

blacktanktop.hatenablog.com


109 B - log (2020-11-28)

問題は以下

atcoder.jp

これまで、リストが存在する二分探索はやったことがあったが、この問題ではリストを作ってから行うと、TLEとなってしまうため、midの値を適切に変形し 探索したい値{\displaystyle n+1}と比較する。

blacktanktop.hatenablog.com


110 B - many (2020-12-05)

問題は以下

atcoder.jp

最初'0'にアサインできたら、最後、左から数えて何番目の'0'にアサインできるかを考える問題

blacktanktop.hatenablog.com


最後に

結局だいたい実際にコンテストで解いた問題全部あげただけみたいになってしまったがまとめながら、半日かけて、ここに書き出した問題をもう一度解き直しして良い復習ができた。

ブログにまとめながらやっていたおかげで数ヶ月前に書いたコードを少し見直しただけで思い出せたし、復習が比較的楽にできてよかった。

2020年06月以前の問題も実際には精進として解いていて、その中でも良さげな問題はあったが、とりあえず今回は取り扱わないでおいた。


参考書籍

けんちょんさんの本。非常に参考になる。行き詰まった時にもう一度読み直したりしてる。

こちらで、書籍を7章まで進めている。

blacktanktop.hatenablog.com