๊ธฐ๋ฅ ์์ธ ๋ณด๊ณ
06. ๋ค๋ชฉ์ ์ค์ฝ์ด๋ง (NSGA-II Pareto + scalar, GNINA, ECR) ๊ธฐ๋ฅ ๋ถ์
๋์ ๋ชจ๋ -
pyrosetta_flow/multiobjective.pyโ ObjectiveWeights, cheap_objectives, multiobjective_scalar, screen_selectivity, ๋ ์ฑ ํ๋ํฐ -pyrosetta_flow/pareto_ranking.pyโ NSGA-II ๋น์ง๋ฐฐ ์ ๋ ฌ + crowding distance -pyrosetta_flow/gnina_rescoring.pyโ GNINA CNN rescoring + ECR consensus -pyrosetta_flow/ranking.pyโ ์คํ ๋ก๊ทธ ๊ธฐ๋ฐ ์ง๊ณ ๋ญํน(๋ณด์กฐ) -pyrosetta_flow/scoring_pipeline.pyโ 4๋ชฉ์ ํตํฉ ์ฒด์ธ(ECR consensus ํฌํจ)๋ณธ ๋ณด๊ณ ์์ ๋ชจ๋ ์ฌ์ค ์ฃผ์ฅ์
file_path:line์ผ๋ก ์ธ์ฉํ๋ค. ๋ฏธํ์ธ ํญ๋ชฉ์ ๋ฏธ๊ฒ์ฆ์ผ๋ก ๋ช ์ํ๋ค.
โ ๋์ ์๋ฆฌ
1.1 4๊ฐ ๋ชฉ์ ์ ์ ์ (ฮG + ๋ฐ๊ฐ๊ธฐ + ์ ํ์ฑ + ADMET)
๋ค๋ชฉ์ ํ๋ ์์ SST-14(AGCKNFFWKTFTSC) ๋ณ์ด์ฒด๋ฅผ 4์ถ์ผ๋ก ํ๊ฐํ๋ค (multiobjective.py:5-9):
| ๋ชฉ์ | ๋ฐฉํฅ | ์ฐ์ถ ์ถ์ฒ | ์ฑ๊ฒฉ |
|---|---|---|---|
ddg |
โ (๋ฎ์์๋ก ๊ฒฐํฉ ๊ฐํจ) | PyRosetta FlexPepDock (์ค์ธก REU/kcalยทmol) | ์ค์ธก |
selectivity_margin / delta_margin |
โ (์์=SSTR2 ์ ํ์ ) | off-target PyRosetta ๋ํน | ์ค์ธก |
half_life_h โ stability_norm |
โ | ์์ด ๊ธฐ๋ฐ ์์๋ธ surrogate | surrogate |
admet_score |
โ | ๋ฌผ์ฑ(GRAVY/Boman/Instability/pI) surrogate | surrogate |
honest disclaimer (multiobjective.py:15-18): half_life_h, admet_score ๋ ๋ญํน์ฉ surrogate ์ด๋ฉฐ ์์ ๋ฐ๊ฐ๊ธฐยท์์ ADMET ์์น๊ฐ ์๋๋ค. ddgยทselectivity_margin ์ ์ค์ FlexPepDock ๊ฒฐ๊ณผ์ง๋ง ์ ๋ ์นํ๋(Ki/Kd)๊ฐ ์๋๋ค. ์ด๋ VR-cycle-09 / H-06("๊ณ์ฐ ๋ถ๊ฐ๋ฅ์ ๊ณ์ฐ ๊ฐ๋ฅํ ์ฒ ํ์ง ์๊ธฐ") ์์น์ ์ฝ๋ ๋ฐ์์ด๋ค.
1.2 ๋น์ฉ ๊ณ์ธตํ (cost-tiered)
์ค์ ๋ํน์ด ๋น์ธ๋ฏ๋ก 2๊ณ์ธต์ผ๋ก ๋๋๋ค (multiobjective.py:11-13):
- Layer 0 (๋ชจ๋ ํ๋ณด, ์์ด๋ง, ฮผs๊ธ): ๋ฐ๊ฐ๊ธฐ + ADMET surrogate โ
cheap_objectives()(multiobjective.py:101-155) - Layer 1 (top-K, ์ค์ PyRosetta, ์๋ถ๊ธ): off-target ์ ํ์ฑ ๋ํน โ
screen_selectivity()(multiobjective.py:361-458)
Layer 0 โ cheap_objectives
- ๋ฐ๊ฐ๊ธฐ:
halflife_ensemble.ensemble_halflife(ํด๋ฆฌ์คํฑ A + RandomForest C ๊ฒฐํฉ)์ ํธ์ถ, ์คํจ ์ ๋ ๊ฑฐ์ ํด๋ฆฌ์คํฑ ๋จ๋ ์ผ๋ก ํด๋ฐฑ (multiobjective.py:113-129).stability_norm์ log10 ์ ๊ทํ๋ก 0~1 ๋ณํ (multiobjective.py:127-129). - ADMET:
PharmaProperties๋ก GRAVY/Boman/Instability/aliphatic/pI ๊ณ์ฐ (multiobjective.py:135-153). Cys3-Cys14 SS bond ๋ฅผ pI ๊ณ์ฐ์์ ์ ์ธํ๋ค (multiobjective.py:139-140, 0-indexed ์ต์ยท์ต๋ Cys). admet_reasonableness()๊ฐ 4๊ฐ ๋ฌผ์ฑ์ 0~1 ๋ถ๋ถ์ ์๋ก ๋ณํ ํ ๊ฐ์ค ํ๊ท (multiobjective.py:64-90):- ๊ฐ์ค์น: Instability 0.35 + GRAVY 0.30 + Boman 0.15 + pI 0.20 (
multiobjective.py:89) - Instability <40 ๋ง์ , 40~80 ์ ํ ๊ฐ์ (
multiobjective.py:75) - GRAVY โค-1 ๋ง์ (์น์), โฅ1 0์ (
multiobjective.py:77) - Boman <1.0 ์ฝํจ, 1.0~4.0 ์ ํ, >4.0 ๋ง์ (
multiobjective.py:79-84) - pI 6~8 ๋ง์ (
multiobjective.py:86)
Layer 0.5 โ pepADMET ๋ ์ฑ ํ๋ํฐ
predict_toxicity_for_sequences()๊ฐ pepADMET GNN ์ ๋ณ๋ conda env subprocess ๋ก ๋ฐฐ์น ์ถ๋ก (multiobjective.py:188-203). ๋ฏธ์ค์น/์คํจ ์ ๋น dict ๋ฐํ(graceful).apply_toxicity_to_extra()๊ฐ ๊ฒฐ๊ณผ๋ฅผadmet_score์ ๊ณฑ์ ํ๋ํฐ๋ก ๋ฐ์ (multiobjective.py:206-241):- binary
is_toxic๋ ๋น๋ณ๋ณ์ (oxytocinยทnative ๊น์ง ์ ๋ถ toxic ํ์ )์ด๋ผ ๊ฒ์ดํธ์ ์ฐ์ง ์๊ณ ๊ธฐ๋ก๋ง ํจ (multiobjective.py:211-219) - ๋์ hc50 ์ฐ์๊ฐ์ native SST-14 ๊ธฐ์ค์ ๋๋น(home-advantage) ๋ก ํ๊ฐ (
multiobjective.py:222-241). native ยฑ5.0 ๋ฐด๋๋ "๋๊ธ" ๋ฌดํ๋ํฐ (_HC50_NATIVE_TOLERANCE=5.0,multiobjective.py:166), ์ด๊ณผ๋ถ์ ์ ํ ๋น๋ก ํ๋ํฐ(ํํ_TOXIC_ADMET_PENALTY=0.4,multiobjective.py:163,238). - 2026-06-17 (VR-A2) ์ถ๊ฐ: SMILES ํ์ฑ ์คํจ๋ก ์ ํ ํด๋ฐฑ๋ ๊ฒฝ์ฐ cyclic ๊ตฌ์กฐ ์์ค๋ก hc50 ์ ๋ขฐ ๋ถ๊ฐ โ ๊ฒ์ดํธ skip(fail-open), ํ๋๊ทธ๋ง ๊ธฐ๋ก (
multiobjective.py:225-228). available=False(์ถ๋ก ๋ถ๊ฐ)๋ฉดadmet_score๋ฅผ ๊ฑด๋๋ฆฌ์ง ์์ โ fail-closed, ๊ฐ์ง ์์ ํ์ ๋ฐฉ์ง (multiobjective.py:209,217).
Layer 1 โ screen_selectivity
- top-K ํ๋ณด์ SSTR2 ์ ๋ฐํ ๋ณตํฉ์ฒด๋ฅผ SSTR1/3/4/5 ์ off-target ๋ํน (
multiobjective.py:361-458). - SSTR2(๋์ผ ํ๋กํ ์ฝ baseline) + off-target 4์ข
์
ThreadPoolExecutor๋ก ๋ณ๋ ฌ ๋ํน (์์ฐจ ~25๋ถ โ ๋ณ๋ ฌ ~6๋ถ/ํ๋ณด) (multiobjective.py:401-425). selectivity_margin = min(offtarget_ddg) - baseline(์์=SSTR2 ๋ ๊ฐํจ=์ ํ์ , G-2 SSOT) (multiobjective.py:434-435).- home-advantage ๋ณด์ : native ๋ ๋์ผ ํ๋กํ ์ฝ์์ +margin ํธํฅ์ด ์์ผ๋ฏ๋ก native baseline ๋๋น
delta_margin = margin - nat_margin์ด ์ง์ง ์ ํ์ฑ ์ ํธ (multiobjective.py:436-447).more_selective_than_native = delta_margin > 0(multiobjective.py:448). - NaN ๋ํน ๊ฒฐ๊ณผ๋ fail-closed ๋ก None ์ฒ๋ฆฌ (
multiobjective.py:419), ์ ๋ถ ์คํจ ์selectivity_margin=None(multiobjective.py:429-430). - top-K ์ ๋ณ: clash ๊ฒ์ดํธ(
clash_max=10.0) ํต๊ณผ ํ ddg ์ค๋ฆ์ฐจ์ ์์ K (select_topk_for_selectivity,multiobjective.py:308-323).
1.3 NSGA-II Pareto front (pareto_ranking.py)
๋ ๊ฑฐ์ ๊ฐ์คํฉ(0.45/0.20/0.15/0.10/0.10)์ pymoo ๊ธฐ๋ฐ ๋น์ง๋ฐฐ ์ ๋ ฌ + crowding distance ๋ก ๋์ฒด (pareto_ranking.py:3-4).
- 4๋ชฉ์ ๋ชจ๋ ์ต์ํ๋ก ๋ณํ (
_extract_objectives,pareto_ranking.py:35-53):[ddG, -stability, -druggability, -diversity]โ stability/druggability/diversity ๋ ๋ถํธ ๋ฐ์ (pareto_ranking.py:53). - ์ ์ฝ (
_extract_constraints,pareto_ranking.py:56-77):hard_violations โค 0,clash_score - threshold โค 0(๊ธฐ๋ณธ ์๊ณ 10.0,pareto_ranking.py:32). pareto_rank_candidates()ํ๋ฆ (pareto_ranking.py:125-190): 1. ๋ชฉ์ ํ๋ ฌ F(nร4) + ์ ์ฝ ์๋ฐ ๋ฒกํฐ cv(์์ ์๋ฐ ํฉ) ๊ตฌ์ฑ (pareto_ranking.py:158-166) 2.NonDominatedSorting().do(F)๋ก front ๋ถ๋ฆฌ (pareto_ranking.py:169-170) 3._penalise_infeasible()๋ก infeasible ํ๋ณด๋ฅผ ๋ชจ๋ feasible front ๋ค๋ก ๋ฐ์ด๋ด๊ณ , ์๋ฐ๋ ์ค๋ฆ์ฐจ์ ๋จ์ผ ํ๋ํฐ front ๋ก ๋ถ์ฐฉ (pareto_ranking.py:80-117,173) 4. front๋ณcalc_crowding_distance๊ณ์ฐ(๊ฒฝ๊ณ์ =inf), ํ๋ณด๋น 2๊ฐ ๊ธธ์ด โค2 ์ธ front ๋ ์ ๋ถ inf (pareto_ranking.py:180-188) 5.pareto_rank(0=์ต์ฐ์ front),crowding_distance(๋์์๋ก ๊ณ ๋ฆฝ/๋ค์) ๋ถ์ฌ (pareto_ranking.py:186-188)select_from_pareto_front(): rank ์ค๋ฆ์ฐจ์ โ crowding distance ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ ํ ์์ n (pareto_ranking.py:193-231).- ์ฃผ์:
diversity๋ ํ์ฌ ํญ์ 0.0 ์ผ๋ก ์ ๋ ฅ๋์ด(scoring_pipeline.py:218) 4๋ชฉ์ ์ค 1๊ฐ๊ฐ ์ฌ์ค์ ๋นํ์ฑ ์ํ๋ค(๋ฏธ๊ฒ์ฆ: ๋ค๋ฅธ ํธ์ถ ๊ฒฝ๋ก์์ ์ฑ์์ง ๊ฐ๋ฅ์ฑ).
1.4 GNINA CNN rescoring (gnina_rescoring.py)
FlexPepDock ์ถ๋ ฅ PDB ๋ฅผ GNINA --score_only ๋ชจ๋๋ก ์ฌ์ฑ์ (gnina_rescoring.py:1-9).
- dry-run mock ์ฌ๋ถ โ ํ์ธ๋จ:
gnina๋ฐ์ด๋๋ฆฌ๊ฐ PATH ์ ์์ผ๋ฉด ๊ฒฝ๊ณ ๋ก๊ทธ + ๊ฒฐ์ ๋ก ์ mock ์ ์ ๋ฐํ (gnina_rescoring.py:7-9,30,45-47,205-210). mock ๊ฐ์gnina_cnn_score/affinity/vina_score = 0.0,gnina_dry_run = 1.0(gnina_rescoring.py:32-37). - ํ ํ๊ฒฝ์์ ์ค์ธก ํ์ธ:
which gninaโ not found. ๋ฐ๋ผ์ ํ์ฌ GNINA ๋จ๊ณ๋ dry-run mock ์ผ๋ก ๋์ ์ค์ด๋ฉฐ ์ค์ CNN ์ ์๊ฐ ์๋๋ค. - ๋ณตํฉ์ฒด PDB ๋ฅผ chain ๊ธฐ์ค์ผ๋ก receptor(A)/peptide(B) ์์ํ์ผ ๋ถ๋ฆฌ (
split_receptor_peptide,gnina_rescoring.py:97-176). - ๋จ์ผ rescore: subprocess ํธ์ถ, rcโ 0/timeout ์ NaN + ์๋ฌ ํ๋๊ทธ ๋ฐํ (
gnina_rescore,gnina_rescoring.py:179-265). - ๋ฐฐ์น:
ThreadPoolExecutor๋ณ๋ ฌ, ์ ๋ ฅ ์์ ๋ณด์กด (batch_gnina_rescore,gnina_rescoring.py:268-324). - stdout ํ์ฑ:
CNNscoreํค๋ ๋ค์ ์ค์์ 3ํ ํฐ ์ถ์ถ, ์คํจ ์ NaN (_parse_gnina_output,gnina_rescoring.py:50-89).
1.5 ECR (Exponential Rank Consensus)
์ฌ๋ฌ ์ ์ํญ์ ์์ ๊ธฐ๋ฐ์ผ๋ก ํตํฉ (exponential_rank_consensus, gnina_rescoring.py:327-395).
- ๊ณต์:
ECR_i = ฮฃ_k exp(-rank_{i,k} / N), N=ํ๋ณด ์, ๋์์๋ก ์ข์ (gnina_rescoring.py:333-337,388). - ๋ชจ๋ ์ง์ ์ ์ํญ์ ๋ฎ์์๋ก ์ข์ โ ์ค๋ฆ์ฐจ์ 1-based ์์ ๋ถ์ฌ (
gnina_rescoring.py:367-382). NaN/๋น์์น๋ inf ๋ก ๋ฐ์ด ์ต์ ์์ (gnina_rescoring.py:374-375). - ์
๋ ฅ dict ๋ฅผ ๋ณํํ์ง ์๊ณ
ecr_score+ecr_ranks๋ฅผ ์ถ๊ฐํ ์ฌ๋ณธ ๋ฐํ, ecr_score ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ (gnina_rescoring.py:385-395). - ๊ธฐ๋ณธ score_keys:
gnina_cnn_score, gnina_cnn_affinity, gnina_vina_score(gnina_rescoring.py:359-360). ๋จ, ํ์ดํ๋ผ์ธ์์๋ddg๋ฅผ ์ถ๊ฐํด 4๊ฐ ํญ์ผ๋ก ํธ์ถ (scoring_pipeline.py:177).
1.6 ์ค์นผ๋ผ ๊ฐ์ค (multiobjective_scalar)
UI ์ ๋ ฌ ๋ณด์กฐ์ฉ ๋จ์ผ ์ ์ (multiobjective.py:281-305).
- ๊ฐ์ค์น (์ฝ๋ ์ง์ ํ์ธ,
ObjectiveWeights,multiobjective.py:273-278): ddg = 0.40(๊ฒฐํฉ, ์ต์ฐ์ )selectivity = 0.25stability = 0.20admet = 0.15- ํฉ = 1.00
- ์ ๊ทํ (
multiobjective.py:293-298): ddg ๋(ddg_ref - ddg)/ddg_scale(๊ธฐ๋ณธ ref=0, scale=50,multiobjective.py:284-285), selectivity_margin ์/20.0ํฌํ, stability/admet ์ ์ด๋ฏธ 0~1. - ์ต์ข
์ ์ = ๊ฐ์ค ์ ํํฉ (
multiobjective.py:299-305). runner ๋_mo_scalar()๋ก UI ํ์์ฉ์ผ๋ก๋ง ์ฌ์ฉ (runner.py:64-73).
1.7 ํตํฉ ์ฒด์ธ ๋ฐฐ์ (scoring_pipeline.py)
_apply_alternative_scoring() ๊ฐ FlexPepDock ๊ฒฐ๊ณผ์ 5๋จ๊ณ๋ฅผ ์์ฐจ ์ ์ฉ, ๊ฐ ๋จ๊ณ graceful skip (scoring_pipeline.py:42-281):
| ๋จ๊ณ | ๋ด์ฉ | ์์น | skip ์กฐ๊ฑด |
|---|---|---|---|
| 0 | cheap objectives (๋ฐ๊ฐ๊ธฐ+ADMET) | scoring_pipeline.py:79-95 |
์์ธ ์ non-fatal |
| 0.5 | pepADMET ๋ ์ฑ ํ๋ํฐ | scoring_pipeline.py:102-120 |
SST_DISABLE_PEPADMET_TOX env / ๋ฏธ์ค์น |
| 1 | GNINA rescore (dry-run fallback) | scoring_pipeline.py:125-157 |
_HAS_GNINA + PDB ์กด์ฌ |
| 2 | ECR consensus (ddg+GNINA 4ํญ) | scoring_pipeline.py:162-189 |
GNINA ๊ฒฐ๊ณผ ์์ ๋๋ง |
| 3 | Pareto ranking (NSGA-II) | scoring_pipeline.py:201-243 |
_HAS_PARETO(pymoo) |
| 4 | BO suggest (๋ก๊ทธ๋ง, ๋ถ์ํจ๊ณผ ์์) | scoring_pipeline.py:249-279 |
bo_optimizer ์ ๋ฌ + ๊ด์ธก โฅ2 |
Pareto ์
๋ ฅ ํด๋ฐฑ (scoring_pipeline.py:204-221): stability_norm ๋ถ์ฌ ์ clash ๊ธฐ๋ฐ proxy((40-clash)/40), admet_score ๋ถ์ฌ ์ ECR ํด๋ฐฑ, diversity=0.0 ๊ณ ์ .
โก ์ํฅ
- ์ ํ ํ์ง: ๊ฐ์คํฉ ๋จ์ผ ์ ์์ ์์ ๊ฐ์ค์น ์์กด์ NSGA-II Pareto front ๋ก ๋์ฒดํด, ๋ชฉ์ ๊ฐ trade-off ๋ฅผ ๋ช
์์ ์ผ๋ก ๋ณด์กดํ๋ค(front-0 ํ๋ณด ์งํฉ). ๋จ์ผ ์ค์นผ๋ผ๋ UI ์ ๋ ฌ ๋ณด์กฐ๋ก ๊ฒฉํ (
runner.py:64-73). - ๋น์ฉ ํจ์จ: cost-tiered ๊ตฌ์กฐ๋ก ๋น์ผ off-target ๋ํน์ top-K ์๋ง ์ ์ฉ(
multiobjective.py:11-13,runner.py:1414-1418), ์์ฉ์ฒด ๋ณ๋ ฌํ๋ก ํ๋ณด๋น ~25๋ถโ~6๋ถ (multiobjective.py:401-403). - ์ ์ง์ฑ(๊ฐ๋): surrogate/์ค์ธก ๊ตฌ๋ถ ๋ช
์(
multiobjective.py:15-18), fail-closed ๋ ์ฑ ๊ฒ์ดํธ(multiobjective.py:209), home-advantage ๋ณด์ ์ผ๋ก native ํธํฅ ์ ๊ฑฐ(multiobjective.py:436-447). ํ๊ฐ ์ ์ ์ฐจ๋จ์ด ์ค๊ณ์ ๋ด์ฅ. - ๊ฒฌ๊ณ ์ฑ: ๋ชจ๋ ์ต์
๋ ์์กด(GNINA/pymoo/pepADMET)์ graceful skip ์ผ๋ก ์ฒ๋ฆฌ, ๋ถ์ฌ ํ๊ฒฝ์์๋ ํ์ดํ๋ผ์ธ ์งํ(
scoring_pipeline.py์ ๋ฐ).
โข ๊ด๋ จ Action Item (๋ค๋ชฉ์ ํตํฉ)
CLAUDE.md ์ Stage ์ ์ฉ ์ด๋ ฅ ๋ฐ ์งํ ๋ฉ๋ชจ(MEMORY.md)์ ์ฐ๊ณ๋๋ ํญ๋ชฉ:
- ๋ค๋ชฉ์ ํตํฉ ๋ณธ์ฒด: ฮG+๋ฐ๊ฐ๊ธฐ+์ ํ์ฑ+ADMET 4์ถ ํตํฉ (
multiobjective.py:5-9) โ sstr2-selectivity-goal(2026-06-10) ์ ฮmargin home-advantage ๋ณด์ ๊ณผ ์ง๊ฒฐ (multiobjective.py:436-447). - ๋ฐ๊ฐ๊ธฐ ์์๋ธ ํตํฉ (2026-06-09): ํด๋ฆฌ์คํฑ A + RF C ๊ฒฐํฉ (
multiobjective.py:111-121) โ MEMORY ์ "ensemble" ์งํ ํญ๋ชฉ. - pepADMET ๋
์ฑ ํตํฉ (B, 2026-06-09 / VR-A2 2026-06-17): binary ๋น๋ณ๋ณ์ฑ ๋ฐ๊ฒฌ โ hc50 home-advantage ๊ฒ์ดํธ ์ ํ (
multiobjective.py:164-241). - VR-cycle-09 / H-06 ๊ฐ๋: surrogate honest disclaimer ์ ์ฝ๋ ๋ฐ์ (
multiobjective.py:15-18), CLAUDE.md Stage 8h ์ ์ง๊ฒฐ. - god-object ๋ถ๋ฆฌ (P1, 2026-06-09): runner.py ์ ์ค์ฝ์ด๋ง ์ฒด์ธ์ scoring_pipeline.py ๋ก ์ถ์ถ (
scoring_pipeline.py:3). - ์์ฌ ๊ฐญ(๊ถ๊ณ ):
diversity๋ชฉ์ ์ด ํญ์ 0.0 ์ผ๋ก ๋นํ์ฑ (scoring_pipeline.py:218) โ NSGA-II 4๋ชฉ์ ์ค 1๊ฐ ๋ฏธํ์ฉ. ๋ค์์ฑ ๋ฉํธ๋ฆญ(์์ด ๊ฑฐ๋ฆฌ ๋ฑ) ์ฐ๊ฒฐ์ด ๋ฏธ์ Action Item ํ๋ณด.
โฃ ์์ฑ๋ ๋ฐ ๊ทผ๊ฑฐ
์์ฑ๋: ์ฝ 80%
| ์์ญ | ์์ฑ๋ | ๊ทผ๊ฑฐ |
|---|---|---|
| cheap_objectives / ADMET surrogate | 95% | ์์ ๊ตฌํ + ํด๋ฐฑ + SS bond ์ฒ๋ฆฌ (multiobjective.py:64-155), ํ
์คํธ ๋ณด์ (tests/test_multiobjective.py) |
| ๋ ์ฑ ํ๋ํฐ (hc50 home-adv) | 90% | fail-closed/fail-open ๋ถ๊ธฐ ์๋น (multiobjective.py:206-241), ๋จ pepADMET ๋ฏธ์ค์น ์ skip |
| screen_selectivity (Layer 1) | 85% | ๋ณ๋ ฌ ๋ํน+home-adv ๋ณด์ ์๋น (multiobjective.py:361-458), config-gated(enable_selectivity) |
| NSGA-II Pareto | 90% | ๋น์ง๋ฐฐ ์ ๋ ฌ+crowding+์ ์ฝ ์ฒ๋ฆฌ ์๋น (pareto_ranking.py ์ ์ฒด), ๋จ diversity ๋ฏธ์
๋ ฅ |
| GNINA rescoring | 60% | ํ ํ๊ฒฝ dry-run mock ์ผ๋ก๋ง ๋์(๋ฐ์ด๋๋ฆฌ ๋ถ์ฌ ํ์ธ), ์ค์ธก ๊ฒฝ๋ก ๋ฏธ๊ฒ์ฆ (gnina_rescoring.py:205-210) |
| ECR consensus | 95% | ๊ณต์ ๊ตฌํ + NaN ์ฒ๋ฆฌ ์๋น (gnina_rescoring.py:327-395) |
| scalar ํตํฉ | 90% | ๊ฐ์ค์น ์ ์+์ ๊ทํ ์๋น (multiobjective.py:273-305), UI ๋ณด์กฐ ์ญํ ๋ช
ํ |
| diversity ๋ชฉ์ | 20% | ํญ์ 0.0 ์
๋ ฅ์ผ๋ก ๋นํ์ฑ (scoring_pipeline.py:218) |
๋ฏธ์/๋ฏธ๊ฒ์ฆ:
- GNINA ์ค์ธก(live) ๊ฒฝ๋ก๋ ๋ฐ์ด๋๋ฆฌ ๋ถ์ฌ๋ก ๋ณธ ํ๊ฒฝ์์ ๋ฏธ๊ฒ์ฆ โ dry-run ๋ง ํ์ธ.
- diversity ๋ชฉ์ ๋ฏธ์ฐ๊ฒฐ.
- pepADMET/halflife RF ์ ์ค์ ๋ชจ๋ธ ์ ํ๋๋ ๋ณธ ๋ณด๊ณ ๋ฒ์ ๋ฐ(surrogate ๋ฉด์ฑ
๋ช
์๋จ).
โค ํ์ ๊ฐ์น (NSGA-II ๋ค๋ชฉ์ ์ต์ ํ์ ์์)
- Pareto-optimal trade-off ๋ณด์กด: ์ฝ๋ฌผ ํ๋ณด๋ ๊ฒฐํฉ๋ ฅโยท๋ฐ๊ฐ๊ธฐโยท์ ํ์ฑโยทADMETโ ๊ฐ ์๋ก ์์ถฉํ๋ ์ ํ์ ๋ค๋ชฉ์ ๋ฌธ์ ๋ค. ๊ฐ์คํฉ์ ๊ฐ์ค์น ์ ํ์ ๋ฐ๋ผ ๋น๋ณผ๋ก(non-convex) front ์ ์ผ๋ถ ํด๋ฅผ ์๊ตฌํ ๋ฐฐ์ ํ์ง๋ง, NSGA-II ๋น์ง๋ฐฐ ์ ๋ ฌ์ trade-off surface ์ ์ฒด๋ฅผ ๋ณด์กดํ๋ค (
pareto_ranking.py:3-11). ์ด๋ ์์ ๊ฐ์ค์น ๊ฐ์ ์์ด ์์ฌ๊ฒฐ์ ์์๊ฒ ๋ค์ํ ํ๋ณด ์งํฉ์ ์ ๊ณตํ๋ค. - Crowding distance ๋ก ๋ค์์ฑ ์ ์ง: ๋์ผ front ๋ด์์ ๊ณ ๋ฆฝ๋(๋ค์ํ) ํด๋ฅผ ์ฐ์ ์ ํํด ํํ์ ๊ณต๊ฐ ํ์์ ์กฐ๊ธฐ ์๋ ด์ ๋ฐฉ์ง (
pareto_ranking.py:180-188,224-228). - ์ ์ฝ ์ฒ๋ฆฌ: clash/hard_violation infeasible ํด๋ฅผ feasible front ๋ค๋ก relegate ํ๋ constraint-domination ๋ฐฉ์์ Deb ์ NSGA-II ํ์ค ์ ์ฝ ์ฒ๋ฆฌ์ ์ ํฉ์ (
pareto_ranking.py:80-117). - ECR consensus: ์ ๋ ์ ์ ์ค์ผ์ผ์ด ์ด์ง์ ์ธ ๋ค์ํ ์ฑ์ ๊ธฐ(ddG REU vs GNINA CNN vs Vina kcal)๋ฅผ ์์ ๊ณต๊ฐ์์ ํตํฉํ๋ rank-based consensus ๋ ์ค์ผ์ผ ๋ถ๋ณ์ฑ์ ํ๋ณด, ๋จ์ผ ์ฑ์ ๊ธฐ ํธํฅ์ ์ํ (
gnina_rescoring.py:333-337). - ์ ์งํ surrogate ๋ถ๋ฆฌ: surrogate(๋ฐ๊ฐ๊ธฐ/ADMET)์ ์ค์ธก(ddG/์ ํ์ฑ)์ ๋ช
์ ๊ตฌ๋ถํ๊ณ home-advantage ๋ก baseline ํธํฅ์ ๋ณด์ ํ๋ ๋ฐฉ๋ฒ๋ก ์ AI ํ๋ณด ๋ฐ๊ตด์ ์ฌํ์ฑยท์ ๋ขฐ์ฑ ์ธก๋ฉด์์ ํ์ ์ ์ผ๋ก ์๋ฏธ ์๋ ๊ฐ๋๋ค (
multiobjective.py:15-18,436-447).
โฅ ์ฌ์ฉ๋ฒ
ํตํฉ ์ฒด์ธ (์๋, runner ๊ฒฝ์ )
runner.py ๊ฐ FlexPepDock ๊ฒฐ๊ณผ์ ์๋ ์ ์ฉ (runner.py:190,1123, scoring_pipeline.py:42):
from pyrosetta_flow.scoring_pipeline import _apply_alternative_scoring
candidates = _apply_alternative_scoring(candidates, iter_dir, iteration, bo_optimizer)
# ๊ฐ cand.extra_scores ์ half_life_h, admet_score, gnina_*, ecr_score,
# pareto_rank, crowding_distance ์ฑ์์ง
cheap objectives ๋จ๋
from pyrosetta_flow.multiobjective import cheap_objectives, enrich_candidates
obj = cheap_objectives("AGCKNFFWKTFTSC") # half_life_h, admet_score, stability_norm ...
enrich_candidates(cands) # cand dict ์ stability/druggability ๋งคํ ์ฃผ์
NSGA-II Pareto ๋ญํน ๋จ๋
from pyrosetta_flow.pareto_ranking import pareto_rank_candidates, select_from_pareto_front
ranked = pareto_rank_candidates(cands, clash_threshold=10.0) # pareto_rank, crowding_distance ๋ถ์ฌ
best = select_from_pareto_front(ranked, n=5) # front-0 ์ฐ์ , crowding ๋ด๋ฆผ์ฐจ์
GNINA + ECR ๋จ๋
from pyrosetta_flow.gnina_rescoring import batch_gnina_rescore, exponential_rank_consensus
scores = batch_gnina_rescore(pdb_paths, max_workers=4) # ๋ฐ์ด๋๋ฆฌ ์์ผ๋ฉด dry-run mock
consensus = exponential_rank_consensus(cands, score_keys=["ddg","gnina_cnn_score",...])
์ค์นผ๋ผ ์ ์ (UI ์ ๋ ฌ)
from pyrosetta_flow.multiobjective import multiobjective_scalar, ObjectiveWeights
s = multiobjective_scalar(cand, ObjectiveWeights(ddg=0.40, selectivity=0.25,
stability=0.20, admet=0.15))
์ ํ์ฑ (top-K, ๋น์, config-gated)
config.enable_selectivity=True + config.selectivity_top_k ์ค์ ์ runner ๊ฐ top-K ์ ์๋ ํธ์ถ (runner.py:1414-1445). pepADMET ๋
์ฑ์ SST_DISABLE_PEPADMET_TOX=1 ๋ก ๋นํ์ฑ ๊ฐ๋ฅ (scoring_pipeline.py:104-105).
โฆ ํ์ํ ์ด์
- ๋จ์ผ ์ ์์ ํ๊ณ ๊ทน๋ณต: SSTR2 ๋ฐฉ์ฌ์ฑ์์ฝํ ํ๋ณด๋ ๊ฒฐํฉยท๋ฐ๊ฐ๊ธฐยท์ ํ์ฑยท์์ ์ฑ์ด ๋์ ์ต์ ์ผ ์ ์๋ค(์์ถฉ). ๊ฐ์คํฉ ๋จ์ผ ๋ญํน์ ๊ฐ์ค์น ์ ํ์์ ์ฃผ๊ด์ ๊ฒฐ๊ณผ๊ฐ ์ข
์๋๋ฏ๋ก, Pareto front ๋ก ๊ฐ๊ด์ trade-off ์งํฉ์ ํ๋ณดํด์ผ ํ๋ค (
pareto_ranking.py:3-11). - ๊ณ์ฐ ๋น์ฉ ํต์ : ์ค์ PyRosetta off-target ๋ํน์ ํ๋ณด๋น ์๋ถ์ด ๋ค์ด ์ ์ ์ ์ฉ์ด ๋ถ๊ฐ๋ฅํ๋ค. cost-tiered ๊ตฌ์กฐ๋ก ์ ๋น์ฉ surrogate ๊ฐ ์ ์ 1์ฐจ ํํฐ, ๋น์ผ ๋ํน์ top-K ์๋ง ์ ์ฉํด์ผ ์ฒ๋ฆฌ๋์ ํ๋ณดํ๋ค (
multiobjective.py:11-13). - ์ด์ง์ ์ฑ์ ๊ธฐ ํตํฉ: ddG(REU)ยทGNINA(CNN ํ๋ฅ )ยทVina(kcal)๋ ์ค์ผ์ผ์ด ๋ฌ๋ผ ์ง์ ํฉ์ฐ์ด ๋ถ๊ฐ๋ฅํ๋ค. ECR ์์ ํฉ์๊ฐ ์ค์ผ์ผ ๋ถ๋ณ ํตํฉ์ ์ ๊ณตํ๋ค (
gnina_rescoring.py:333-337). - ํ๊ฐ ์ฐจ๋จ / ์ ์ง์ฑ: AI ๋ฐ๊ตด ํ์ดํ๋ผ์ธ์ ์ ๋ขฐ์ฑ์ "๋ชจ๋ฅด๋ ๊ฒ์ ์๋ ์ฒํ์ง ์์"์ ๋ฌ๋ ค์๋ค. surrogate ๋ฉด์ฑ
๋ช
์, fail-closed ๋
์ฑ ๊ฒ์ดํธ, home-advantage baseline ๋ณด์ ์ด ๊ฐ์ง ์ ์์ ์์ฌ๊ฒฐ์ ์ค์ผ์ ๋ง๋๋ค (
multiobjective.py:15-18,209,436-447). - ์ ํ์ฑ ์ฐ์ ๋ชฉํ: SSTR1/3/4/5 ๋๋น SSTR2 ์ ํ์ฑ์ off-target ๋ถ์์ฉ ํํผ์ ํต์ฌ์ด๋ค. delta_margin(home-advantage ๋ณด์ ) ์ด native ๋๋น ์ง์ง ์ ํ์ฑ ์ ํธ๋ฅผ ์ ๊ณตํ๋ค (
multiobjective.py:447-448).
๊ฒ์ฆ ์ธ์ฉ ๋ชฉ๋ก
| ์ฃผ์ฅ | ์ธ์ฉ |
|---|---|
| 4๋ชฉ์ ์ ์(ddgโ/half_lifeโ/selectivityโ/admetโ) | multiobjective.py:5-9 |
| surrogate vs ์ค์ธก honest disclaimer | multiobjective.py:15-18 |
| cost-tiered 2๊ณ์ธต ๊ตฌ์กฐ | multiobjective.py:11-13 |
| admet_reasonableness ๊ฐ์ค์น(0.35/0.30/0.15/0.20) | multiobjective.py:89 |
| cheap_objectives ๋ฐ๊ฐ๊ธฐ ์์๋ธ+ํด๋ฐฑ | multiobjective.py:111-129 |
| SS bond pI ์ ์ธ ์ฒ๋ฆฌ | multiobjective.py:139-140 |
| ๋ ์ฑ ํ๋ํฐ ์์(0.4 / ยฑ5.0 / 200.0) | multiobjective.py:163-168 |
| binary is_toxic ๋น๋ณ๋ณ โ hc50 ๊ฒ์ดํธ | multiobjective.py:211-219 |
| hc50 home-advantage ํ๋ํฐ | multiobjective.py:232-241 |
| VR-A2 ์ ํ ํด๋ฐฑ hc50 skip | multiobjective.py:225-228 |
| fail-closed(available=False ๋ฌด์ฒ๋ฆฌ) | multiobjective.py:209,217 |
| ObjectiveWeights ๊ฐ(0.40/0.25/0.20/0.15) | multiobjective.py:273-278 |
| multiobjective_scalar ์ ๊ทํยท์ ํํฉ | multiobjective.py:293-305 |
| select_topk_for_selectivity clash ๊ฒ์ดํธ | multiobjective.py:308-323 |
| screen_selectivity ๋ณ๋ ฌ ๋ํน | multiobjective.py:401-425 |
| selectivity_margin ์ ์ | multiobjective.py:434-435 |
| delta_margin home-advantage ๋ณด์ | multiobjective.py:436-448 |
| NSGA-II ๊ฐ์คํฉ ๋์ฒด | pareto_ranking.py:3-4 |
| 4๋ชฉ์ ์ต์ํ ๋ณํ | pareto_ranking.py:35-53 |
| ์ ์ฝ(hard_violations/clash) | pareto_ranking.py:56-77 |
| infeasible relegation | pareto_ranking.py:80-117 |
| ๋น์ง๋ฐฐ ์ ๋ ฌ+crowding | pareto_ranking.py:169-188 |
| select_from_pareto_front ์ ๋ ฌํค | pareto_ranking.py:224-231 |
| GNINA dry-run mock ๋์ | gnina_rescoring.py:7-9,30,45-47,205-210 |
| dry-run mock ๊ฐ | gnina_rescoring.py:32-37 |
| GNINA ๋ฐ์ด๋๋ฆฌ ๋ถ์ฌ(ํ๊ฒฝ ์ค์ธก) | which gnina โ not found |
| ECR ๊ณต์ | gnina_rescoring.py:333-337,388 |
| ECR NaNโinf ์ฒ๋ฆฌ | gnina_rescoring.py:374-375 |
| ECR ๊ธฐ๋ณธ score_keys | gnina_rescoring.py:359-360 |
| ํ์ดํ๋ผ์ธ 5๋จ๊ณ ์ฒด์ธ | scoring_pipeline.py:42-281 |
| ECR ํธ์ถ ์ ddg ์ถ๊ฐ(4ํญ) | scoring_pipeline.py:177 |
| Pareto diversity=0.0 ๊ณ ์ | scoring_pipeline.py:218 |
| Pareto ์ ๋ ฅ ํด๋ฐฑ(clash proxy/ECR) | scoring_pipeline.py:204-212 |
| pepADMET env ๋นํ์ฑ ํ๋๊ทธ | scoring_pipeline.py:104-105 |
| runner scalar UI ๋ณด์กฐ ํธ์ถ | runner.py:64-73 |
| runner ์ ํ์ฑ top-K config-gated | runner.py:1414-1445 |