๊ธฐ๋Šฅ ์ƒ์„ธ ๋ณด๊ณ 

07. ํƒ์ƒ‰ ์ตœ์ ํ™” โ€” Thompson Bandit + ๋ฒ ์ด์ง€์•ˆ ์ตœ์ ํ™” + ์ˆ˜๋ ด ๊ฐ์ง€

๋Œ€์ƒ ๋ชจ๋“ˆ - pyrosetta_flow/bandit.py โ€” Thompson Sampling ์œ„์น˜ ๋ฐด๋”ง (PositionBandit) - pyrosetta_flow/bayesian_optimizer.py โ€” GP ๋ฒ ์ด์ง€์•ˆ ์ตœ์ ํ™” (BayesianPeptideOptimizer) - pyrosetta_flow/convergence.py โ€” Mann-Whitney U ์ˆ˜๋ ด ๊ฐ์ง€ (ConvergenceDetector) - pyrosetta_flow/adapter.py โ€” warm-start ๋ž˜ํผ (get_bandit_guidance / initialize_from_history)

์„ฑ๊ฒฉ: ๋ณธ ํด๋Ÿฌ์Šคํ„ฐ๋Š” ํ›„๋ณด๋ฅผ "๋” ๋นจ๋ฆฌยท๋” ๋˜‘๋˜‘ํ•˜๊ฒŒ ๊ณ ๋ฅด๋„๋ก" ๋•๋Š” ํƒ์ƒ‰ ๊ฐ€์†(์ธํ”„๋ผ) ๊ณ„์ธต์ด๋‹ค. ๊ฒฐ๊ณผ์˜ ๊ณผํ•™์  ์ •๋‹น์„ฑ(ddGยท์„ ํƒ์„ฑยท๋…์„ฑ)์€ ๋ณ„๋„ ์Šค์ฝ”์–ด๋ง/๊ฐ€๋“œ ๊ณ„์ธต์ด ์ฑ…์ž„์ง„๋‹ค. ์ฆ‰ ์ด ๋ชจ๋“ˆ๋“ค์€ Action Item(๊ณผํ•™์  ๊ฒฐ๋ก )์— ์ง์ ‘ ๋ฌด๊ด€ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค โ€” ์ž์„ธํ•œ ๊ทผ๊ฑฐ๋Š” ยง3, ยง7.


โ‘  ๋™์ž‘ ์›๋ฆฌ

1-A. Thompson Sampling ์œ„์น˜ ๋ฐด๋”ง (bandit.py)

์‹ค์ œ ๊ตฌํ˜„ (stub ์•„๋‹˜). ๊ฐ ๋ณ€์ด ๊ฐ€๋Šฅ ์œ„์น˜๋ฅผ ํ•˜๋‚˜์˜ "์•”(arm)"์œผ๋กœ ๋ณด๊ณ  Beta(ฮฑ, ฮฒ) ๋ถ„ํฌ๋ฅผ ์œ ์ง€ํ•œ๋‹ค.

  • ๋ณ€์ด ๊ฐ€๋Šฅ ์œ„์น˜๋Š” 1-indexed๋กœ [1, 2, 4, 5, 6, 11, 12, 13] ๊ณ ์ • (bandit.py:24). ์ฆ‰ Cys3ยทCys14(SS bond)์™€ Trp8ยทLys9ยทThr10(FWKT pharmacophore ํ•ต์‹ฌ๋ถ€ ์ผ๋ถ€)์€ ์•” ์ง‘ํ•ฉ์—์„œ ์ œ์™ธ โ€” ๋„๋ฉ”์ธ ์ œ์•ฝ์ด ์œ„์น˜ ํ›„๋ณด ์ž์ฒด์— ๋ฐ˜์˜๋จ.
  • ํžˆ์Šคํ† ๋ฆฌ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ (bandit.py:49-100): record_type=="candidate", status=="success", ddG๊ฐ€ [-60, 200] plausible ๋ฒ”์œ„ ๋‚ด(bandit.py:26-27, 60-61)์ธ ๋ ˆ์ฝ”๋“œ๋งŒ ์‚ฌ์šฉ. WT(AGCKNFFWKTFTSC) ๊ด€์ธก์ด ์žˆ์œผ๋ฉด ๊ทธ ํ‰๊ท ์„, ์—†์œผ๋ฉด ์ „์ฒด ddG์˜ ์ค‘์•™๊ฐ’์„ baseline์œผ๋กœ ์‚ผ์Œ(bandit.py:74-78). ๊ฐ ํ›„๋ณด์—์„œ WT ๋Œ€๋น„ ๋ฐ”๋€ ์œ„์น˜๋ฅผ ์‹๋ณ„(bandit.py:86-90)ํ•˜๊ณ , ddG๊ฐ€ baseline๋ณด๋‹ค ๋‚ฎ์œผ๋ฉด(๊ฐœ์„ ) ํ•ด๋‹น ์œ„์น˜๋“ค์˜ ฮฑ๋ฅผ +1, ์•„๋‹ˆ๋ฉด ฮฒ๋ฅผ +1 (bandit.py:95-100).
  • ์œ„์น˜ ์„ ํƒ (sample_focus_positions, bandit.py:102-115): ๊ฐ ์•”์˜ Beta ๋ถ„ํฌ์—์„œ random.betavariate(ฮฑ, ฮฒ)๋กœ ํ‘œ๋ณธ์„ ๋ฝ‘๊ณ (bandit.py:110) ํ‘œ๋ณธ๊ฐ’ ์ƒ์œ„ n๊ฐœ ์œ„์น˜๋ฅผ ๋ฐ˜ํ™˜. โ†’ ์ •ํ†ต Thompson Sampling (posterior sampling ๊ธฐ๋ฐ˜ ํƒ์ƒ‰-ํ™œ์šฉ ๊ท ํ˜•). rng ์ฃผ์ž… ๊ฐ€๋Šฅํ•ด ์žฌํ˜„์„ฑ ํ™•๋ณด.
  • ์˜จ๋ผ์ธ ๊ฐฑ์‹  update(bandit.py:117-124)์™€ ์ง„๋‹จ์šฉ get_arm_stats(๊ธฐ๋Œ“๊ฐ’ ฮฑ/(ฮฑ+ฮฒ), ๊ด€์ธก์ˆ˜)(bandit.py:126-137) ์ œ๊ณต.

์ฃผ์˜: ๊ฐœ์„ /์•…ํ™”๋ฅผ ์ด์ง„(Bernoulli) ์œผ๋กœ๋งŒ ๋ณธ๋‹ค. ddG๊ฐ€ 1์  ์ข‹์•„์ง„ ๊ฒƒ๊ณผ 50์  ์ข‹์•„์ง„ ๊ฒƒ์„ ๋™์ผํ•˜๊ฒŒ ฮฑ+1 ์ฒ˜๋ฆฌ โ€” ๊ฐœ์„  ํญ(magnitude)์€ ๋ฌด์‹œ. ๋˜ํ•œ ๋‹ค์ค‘ ์œ„์น˜๊ฐ€ ๋™์‹œ์— ๋ฐ”๋€ ํ›„๋ณด๋Š” credit assignment๊ฐ€ ๋ชจ๋“  ๋ณ€์ด ์œ„์น˜์— ๊ท ๋“ฑ ๋ถ„๋ฐฐ๋˜์–ด, ์–ด๋А ์œ„์น˜๊ฐ€ ์‹ค์ œ ๊ธฐ์—ฌํ–ˆ๋Š”์ง€ ๊ตฌ๋ถ„ ๋ถˆ๊ฐ€(bandit.py:96-100). ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ž์ฒด๋Š” ์ •์ƒ์ด๋‚˜ ์‹ ํ˜ธ ํ•ด์ƒ๋„๋Š” ๊ฑฐ์นœ ํŽธ.

1-B. GP ๋ฒ ์ด์ง€์•ˆ ์ตœ์ ํ™” (bayesian_optimizer.py)

์‹ค์ œ ๊ตฌํ˜„ (stub ์•„๋‹˜). ๋‹จ, BoTorch ์œ ๋ฌด์— ๋”ฐ๋ผ 2๋‹จ ํด๋ฐฑ.

  • ์ž„๋ฒ ๋”ฉ ๊ณ„์ธต (pluggable): PeptideEmbedder ABC(bayesian_optimizer.py:61-90) ์•„๋ž˜
  • OneHotEmbedder โ€” ์ž”๊ธฐ๋‹น 20-dim one-hot ์—ฐ๊ฒฐ, max_len ํŒจ๋”ฉ(:96-130). ์™ธ๋ถ€ ์˜์กด ์—†์Œ. ์‹ค์ œ๋กœ runner๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์ž„๋ฒ ๋”(runner.py:479).
  • ESM2Embedder โ€” transformers/torch ํ•„์š”, mean-pool last hidden state(:136-183). ์‹ค์ œ ๊ตฌํ˜„์ด๋‚˜ runner์—์„œ ๋ฏธ์‚ฌ์šฉ(์„ค์น˜ ์˜์กด).
  • GP ๋Œ€๋ฆฌ๋ชจ๋ธ:
  • BoTorch ๊ฐ€์šฉ ์‹œ SingleTaskGP ร— ๋ชฉ์ ํ•จ์ˆ˜ ๊ฐœ์ˆ˜ โ†’ ModelListGP, fit_gpytorch_mll๋กœ marginal likelihood ์ตœ์ ํ™”(:362-372).
  • ๋ฏธ๊ฐ€์šฉ ์‹œ _FallbackGP โ€” numpy๋งŒ์œผ๋กœ RBF ์ปค๋„ GP๋ฅผ ์ง์ ‘ ๊ตฌํ˜„(์ปค๋„ ํ–‰๋ ฌ ์—ญํ–‰๋ ฌ np.linalg.inv, ์˜ˆ์ธก ํ‰๊ท /๋ถ„์‚ฐ)(:189-246). ์ •์‹ GP ํšŒ๊ท€์‹ ๊ทธ๋Œ€๋กœ โ€” stub์ด ์•„๋‹ˆ๋ผ ์ถ•์†ŒํŒ ์ •์‹ ๊ตฌํ˜„.
  • ํš๋“ํ•จ์ˆ˜(acquisition):
  • BoTorch ๊ฒฝ๋กœ: qNEHVI๋ฅผ import๋Š” ํ•˜๋‚˜(:33-35), ์‹ค์ œ _acquisition_botorch๋Š” posterior ํ‰๊ท ์˜ ref point ๋Œ€๋น„ ๊ฐœ์„ ๋Ÿ‰ ๊ณฑ(product-of-improvements) ์„ ์“ฐ๋Š” ๊ฒฝ๋Ÿ‰ ํ”„๋ก์‹œ๋‹ค(:516-531). ์ฃผ์„์—๋„ "full qNEHVI is expensive for large sets"๋ผ๊ณ  ๋ช…์‹œ(:516-517). โ†’ ์ฆ‰ ์ง„์งœ qNEHVI hypervolume ๊ณ„์‚ฐ์€ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Œ.
  • ํด๋ฐฑ ๊ฒฝ๋กœ: ๋ชฉ์ ๋ณ„ UCB(mean + 2.0ยทโˆšvar) ํ•ฉ์‚ฐ(:533-550).
  • ์ œ์•ˆ ์ƒ์„ฑ suggest(:386-428): reference_seq์— ๋Œ€ํ•ด ํ—ˆ์šฉ ์œ„์น˜๋งˆ๋‹ค 19๊ฐœ ๋‹จ์ผ์  ๋ณ€์ด๋ฅผ ์ „๋ถ€ enumerate(_enumerate_mutations, :430-464)ํ•˜๊ณ  ํš๋“๊ฐ’ ์ƒ์œ„ n๊ฐœ ๋ฐ˜ํ™˜.
  • ๋ชฉ์ ํ•จ์ˆ˜: runner์—์„œ ["ddg"(์ตœ์†Œํ™”), "ecr_score"(์ตœ๋Œ€ํ™”)]๋กœ ์„ค์ •(runner.py:480-482); ์ตœ์†Œํ™” ๋ชฉ์ ์€ ๋ถ€ํ˜ธ ๋ฐ˜์ „์œผ๋กœ ํ•ญ์ƒ ์ตœ๋Œ€ํ™” ๋ฌธ์ œ๋กœ ๋ณ€ํ™˜(:316-320).

ํ†ตํ•ฉ ๊นŠ์ด ์ฃผ์˜: scoring_pipeline์—์„œ BO๋Š” fitโ†’suggest๋งŒ ํ˜ธ์ถœํ•˜๊ณ  ๊ทธ ์ œ์•ˆ์„ ๋‹ค์Œ ํ›„๋ณด ์ƒ์„ฑ์— ํ”ผ๋“œ๋ฐฑํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฝ”๋“œ ์ฃผ์„์ด ๋ช…์‹œ: "Step 4: Bayesian Optimization suggest (๋ถ€์ˆ˜ํšจ๊ณผ ์—†์Œ โ€” ๋กœ๊ทธ๋งŒ)"(scoring_pipeline.py:246), ๊ฒฐ๊ณผ๋Š” BO suggest top-3 positions: ... ์ถœ๋ ฅ์— ๊ทธ์นจ(scoring_pipeline.py:270-274). ์ฆ‰ BO๋Š” ํ˜„์žฌ ๊ด€์ฐฐ์ž/๋กœ๊น… ๋ชจ๋“œ๋กœ๋งŒ ์ž‘๋™ํ•˜๋ฉฐ ํƒ์ƒ‰ ๋ฃจํ”„๋ฅผ ์‹ค์ œ๋กœ ์กฐํ–ฅํ•˜์ง€ ์•Š๋Š”๋‹ค.

1-C. Mann-Whitney U ์ˆ˜๋ ด ๊ฐ์ง€ (convergence.py)

์‹ค์ œ ๊ตฌํ˜„ (stub ์•„๋‹˜), scipy ๋ฌด์˜์กด.

  • _rank_data(convergence.py:14-27): ๋™์  ํ‰๊ท  ์ˆœ์œ„ ์ฒ˜๋ฆฌ ํฌํ•จํ•œ ์ˆœ์œ„ ๋ถ€์—ฌ โ€” ์ •์„.
  • _mann_whitney_u(:30-59): U ํ†ต๊ณ„๋Ÿ‰ = R1 โˆ’ n1(n1+1)/2, n_total โ‰ฅ 8์ผ ๋•Œ ์—ฐ์†์„ฑ ๋ณด์ • ์—†๋Š” ์ •๊ทœ๊ทผ์‚ฌ(ฮผ=n1ยทn2/2, ฯƒ=โˆš(n1ยทn2ยท(N+1)/12)), ์–‘์ธก p๊ฐ’ = 2ยท(1โˆ’ฮฆ(z))(:50-58). n_total<8์ด๋ฉด ๋ณด์ˆ˜์ ์œผ๋กœ 1.0 ๋ฐ˜ํ™˜(๋ฐ์ดํ„ฐ ๋ถ€์กฑ)(:47-48). ฮฆ๋Š” math.erf ๊ธฐ๋ฐ˜ ์ •๊ทœ CDF(:62-64). โ†’ ๊ต๊ณผ์„œ ์ •๊ทœ๊ทผ์‚ฌ ์ •ํ™•. (๋™์  ๋ถ„์‚ฐ ๋ณด์ • ํ•ญ์€ ์ƒ๋žต โ€” ๋™์  ๋งŽ์„ ๋•Œ ์•ฝ๊ฐ„ ๋ณด์ˆ˜์ .)
  • ConvergenceDetector.is_converged(:79-143): ์ตœ๊ทผ window์™€ ์ง์ „ window์˜ top-k ddG๋ฅผ ๋น„๊ต. ์ˆ˜๋ ด ํŒ์ • = (p > ์œ ์˜์ˆ˜์ค€, ์ฆ‰ ๋”์ด์ƒ ์œ ์˜ํ•œ ๊ฐœ์„  ์—†์Œ) AND (๋ณ€๋™๊ณ„์ˆ˜ CV < 0.15) ๋™์‹œ ์ถฉ์กฑ(:126-129). ์ตœ์†Œ 2ยทwindow_size iteration ํ•„์š”(:94-99). ์‚ฌ๋žŒ์ด ์ฝ์„ ๊ถŒ๊ณ  ๋ฌธ์ž์—ด ์ƒ์„ฑ.

์ค‘์š”(ํ†ตํ•ฉ): runner๋Š” ๋งค iteration add_iterationโ†’is_converged๋กœ ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ  emitter๋กœ ๋ณด๊ณ (runner.py:906-924, 1287-1293)ํ•˜์ง€๋งŒ, ๋ฉ”์ธ iteration ๋ฃจํ”„(runner.py:508)์—๋Š” conv_flag ๊ธฐ๋ฐ˜ break๊ฐ€ ์—†๋‹ค. ์ฆ‰ ์ˆ˜๋ ด ๊ฐ์ง€๋Š” ์ž๋ฌธ(advisory)ยทUI ํ‘œ์‹œ์šฉ์ผ ๋ฟ ๋ฃจํ”„๋ฅผ ์กฐ๊ธฐ ์ข…๋ฃŒ์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค. (๋ณ„๋„์˜ validation ๋‹จ๊ณ„ early-stop์€ CV ๊ธฐ๋ฐ˜์œผ๋กœ ์กด์žฌํ•˜๋‚˜ โ€” runner.py:1368-1376 โ€” ์ด๋Š” ์ตœ์ข… ํ›„๋ณด ๋‹ค์ค‘์‹œํ–‰ ๊ฒ€์ฆ์šฉ์ด์ง€ ๋ณธ ์ˆ˜๋ ด๊ฐ์ง€๊ธฐ์™€ ๋ฌด๊ด€.)


โ‘ก ์˜ํ–ฅ (ํƒ์ƒ‰ ํšจ์œจ / warm-start)

  • warm-start: get_bandit_guidance(adapter.py:113-128)๊ฐ€ prior_records๋กœ ๋ฐด๋”ง์„ ๋ถ€ํŠธ์ŠคํŠธ๋žฉํ•ด focus_positions๋ฅผ ์‚ฐ์ถœ. runner๋Š” Planner๊ฐ€ focus_positions๋ฅผ ๋น„์›Œ๋‘” ๊ฒฝ์šฐ์—๋งŒ ๋ฐด๋”ง ์ถ”์ฒœ์œผ๋กœ ์ฑ„์šด๋‹ค(runner.py:592-593, if bandit_guidance and not guidance.get("focus_positions")). โ†’ LLM Planner ์šฐ์„ , ๋ฐด๋”ง์€ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ํด๋ฐฑ. ๋ฌดํ•œ ๋ฐœ๊ตด ์—”์ง„์˜ ๊ธ€๋กœ๋ฒŒ ๋ฆฌ๋”๋ณด๋“œ warm-start(runner.py:498)์™€ ํ•จ๊ป˜ "์ด์ „ ์‹คํ–‰ ์ง€์‹ ์žฌ์‚ฌ์šฉ" ์ถ•์„ ํ˜•์„ฑ.
  • ํƒ์ƒ‰ ํšจ์œจ: ๋ฐด๋”ง์€ ๊ณผ๊ฑฐ์— ๊ฐœ์„ ์„ ๋งŽ์ด ๋‚ธ ์œ„์น˜๋กœ ๋ณ€์ด ์˜ˆ์‚ฐ์„ ํŽธํ–ฅ โ†’ ๋ฌด์ž‘์œ„ ์œ„์น˜ ๋Œ€๋น„ ์ˆ˜๋ ด ๊ฐ€์† ๊ธฐ๋Œ€. ๋‹ค๋งŒ ยง1-A์˜ ๊ฑฐ์นœ credit assignmentยท์ด์ง„ํ™”๋กœ ์‹ ํ˜ธ๊ฐ€ ์•ฝํ•ด์งˆ ์ˆ˜ ์žˆ์Œ.
  • BO ์˜ํ–ฅ์€ ํ˜„์žฌ ๋ฏธ๋ฏธ: ยง1-B๋Œ€๋กœ ์ œ์•ˆ์ด ๋ฃจํ”„์— ํ”ผ๋“œ๋ฐฑ๋˜์ง€ ์•Š์•„(๋กœ๊น…๋งŒ), ์‹ค์ธก ํƒ์ƒ‰ ํšจ์œจ ๊ธฐ์—ฌ๋Š” ์‚ฌ์‹ค์ƒ 0์— ๊ฐ€๊น๋‹ค. ํ–ฅํ›„ suggest ๊ฒฐ๊ณผ๋ฅผ ํ›„๋ณด ํ’€์— ์ฃผ์ž…ํ•˜๋ฉด ์˜ํ–ฅ์ด ์ƒ๊น€.
  • ์ˆ˜๋ ด ๊ฐ์ง€ ์˜ํ–ฅ: ์กฐ๊ธฐ ์ข…๋ฃŒ๊ฐ€ ์•„๋‹ˆ๋ผ "๊ทธ๋งŒ๋‘˜ ๋งŒํ•˜๋‹ค"๋Š” ์‹ ํ˜ธ ์ œ๊ณต โ†’ ์‚ฌ๋žŒ/์ƒ์œ„ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ์˜ ์˜์‚ฌ๊ฒฐ์ • ๋ณด์กฐ. ๊ณ„์‚ฐ ์˜ˆ์‚ฐ ์ ˆ๊ฐ ํšจ๊ณผ๋Š” ํ˜„์žฌ ์ž๋™ํ™”๋˜์–ด ์žˆ์ง€ ์•Š์Œ.

โ‘ข ๊ด€๋ จ Action Item

์ง์ ‘ ๋ฌด๊ด€ (ํƒ์ƒ‰ ๊ฐ€์†์€ ์ธํ”„๋ผ ๊ณ„์ธต) โ€” ๊ทผ๊ฑฐ:

  1. ์ด ๋ชจ๋“ˆ๋“ค์€ ์–ด๋–ค ํ›„๋ณด๊ฐ€ SSTR2 ์„ ํƒ์ ยท์ €๋…์„ฑ์ธ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ ํŒ์ •์€ ddG ๊ณ„์‚ฐ(PyRosetta)ยท์„ ํƒ์„ฑ ๋ฃจํ”„(selectivity_loop)ยท์•ฝ๋ฆฌํ•™ ๊ฐ€๋“œ(pharmacology_guards.py)๊ฐ€ ํ•œ๋‹ค. ๋ฐด๋”ง/BO/์ˆ˜๋ ด์€ "๊ทธ ํŒ์ • ๋Œ€์ƒ์„ ์–ด๋–ค ์ˆœ์„œ๋กœ ์–ผ๋งˆ๋‚˜ ๋นจ๋ฆฌ ๊ณ ๋ฅผ์ง€"๋งŒ ๋‹ค๋ฃฌ๋‹ค.
  2. ์ฝ”๋“œ ์ž์ฒด๊ฐ€ ์ž์‹ ์„ ๋ณด์กฐ ์—ญํ• ๋กœ ์„ ์–ธ: ๋ฐด๋”ง์€ Planner ๋ฏธ์ง€์ • ์‹œ ํด๋ฐฑ(runner.py:592), BO๋Š” "๋ถ€์ˆ˜ํšจ๊ณผ ์—†์Œ โ€” ๋กœ๊ทธ๋งŒ"(scoring_pipeline.py:246), ์ˆ˜๋ ด์€ break ์—†๋Š” advisory(runner.py:508 ๋ฃจํ”„์— ๋ฏธ๋ฐ˜์˜).
  3. ๋”ฐ๋ผ์„œ ๊ณผํ•™์  ๊ฒฐ๋ก (Action Item)์„ ๋ฐ”๊พธ์ง€ ์•Š์œผ๋ฉฐ, ์ œ๊ฑฐํ•ด๋„ ๊ฒฐ๊ณผ์˜ ์ •ํ™•์„ฑ์€ ๋ณ€ํ•˜์ง€ ์•Š๊ณ  ์†๋„/์˜ˆ์‚ฐ๋งŒ ์˜ํ–ฅ ๋ฐ›๋Š”๋‹ค.

โ†’ Action Item๊ณผ์˜ ์—ฐ๊ฒฐ๊ณ ๋ฆฌ๋Š” ๊ฐ„์ ‘์ : "๋™์ผ ์˜ˆ์‚ฐ์œผ๋กœ ๋” ์ข‹์€ ํ›„๋ณด์— ๋„๋‹ฌํ•  ํ™•๋ฅ ์„ ๋†’์ธ๋‹ค"๋Š” ํšจ์œจ ์ถ•์—์„œ๋งŒ ๊ธฐ์—ฌ.


โ‘ฃ ์™„์„ฑ๋„ % + ๊ทผ๊ฑฐ

๋ชจ๋“ˆ ์™„์„ฑ๋„ ๊ทผ๊ฑฐ
bandit.py (Thompson) 90% ๋ถ€ํŠธ์ŠคํŠธ๋žฉยทํ‘œ๋ณธยท๊ฐฑ์‹ ยท์ง„๋‹จ ๋ชจ๋‘ ์‹ค๊ตฌํ˜„, rng ์ฃผ์ž… ์žฌํ˜„์„ฑ, ๋„๋ฉ”์ธ ์œ„์น˜ ์ œ์•ฝ ๋ฐ˜์˜. ํ†ตํ•ฉ๋„ ํ™œ์„ฑ(runner.py:592). ๊ฐ์ : ์ด์ง„ํ™”๋กœ ๊ฐœ์„ ํญ ๋ฌด์‹œ, ๋‹ค์ค‘๋ณ€์ด credit assignment ๋ฏธ๋ถ„๋ฆฌ(bandit.py:96-100). ์ „์šฉ ํ…Œ์ŠคํŠธ ๋‹ค์ˆ˜ ์กด์žฌ(tests/test_bandit.py).
bayesian_optimizer.py (GP/BO) 65% GP(BoTorch+numpy ํด๋ฐฑ)ยท์ž„๋ฒ ๋”ยทenumerateยทsuggest ์‹ค๊ตฌํ˜„. ๋‹จ (a) qNEHVI๋Š” import๋งŒ, ์‹ค์ œ๋Š” product-of-improvements ํ”„๋ก์‹œ(:516-531); (b) suggest ๊ฒฐ๊ณผ๊ฐ€ ๋ฃจํ”„์— ๋ฏธํ”ผ๋“œ๋ฐฑ(scoring_pipeline.py:246, ๋กœ๊น…๋งŒ). ์ฆ‰ "๊ตฌํ˜„๋์œผ๋‚˜ ๋ฏธ๋ฐฐ์„ ".
convergence.py (Mann-Whitney) 85% ์ˆœ์œ„ยทUยท์ •๊ทœ๊ทผ์‚ฌยทCV ๊ฒŒ์ดํŠธ ์ •์„ ๊ตฌํ˜„, scipy ๋ฌด์˜์กด. ๊ฐ์ : ๋™์  ๋ถ„์‚ฐ๋ณด์ • ํ•ญ ์ƒ๋žต, ๊ทธ๋ฆฌ๊ณ  ์กฐ๊ธฐ์ข…๋ฃŒ ๋ฏธ๋ฐฐ์„ (advisory only, runner.py:508 ๋ฃจํ”„์— break ์—†์Œ). ์ „์šฉ ํ…Œ์ŠคํŠธ ์กด์žฌ(tests/test_convergence.py).
adapter.get_bandit_guidance 95% ์–‡์€ ๋ž˜ํผ, ์˜ˆ์™ธ ๋น„์น˜๋ช… ์ฒ˜๋ฆฌ(runner.py:467-468), Planner ํฌ๋งท ํ˜ธํ™˜.

ํด๋Ÿฌ์Šคํ„ฐ ์ข…ํ•ฉ โ‰ˆ 80% (์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„ ํ’ˆ์งˆ์€ ๋†’์Œ, ์ผ๋ถ€๋Š” "๊ตฌํ˜„ ํ›„ ๋ฏธ๋ฐฐ์„ "์ด๋ผ ์šด์˜ ๊ธฐ์—ฌ๊ฐ€ ์ž ์žฌ ์ƒํƒœ).


โ‘ค ํ•™์ˆ  ๊ฐ€์น˜ (BO/bandit์˜ ๋‹จ๋ฐฑ์งˆ ์„ค๊ณ„ ์ ์šฉ ์˜์˜)

  • Thompson Sampling์„ ์ž”๊ธฐ ์œ„์น˜ ์„ ํƒ์— ์ ์šฉ: ์„œ์—ด ๊ณต๊ฐ„์€ ์กฐํ•ฉํญ๋ฐœ(14aa ร— 20AA)์ด๋ผ ์ „์ˆ˜ ํƒ์ƒ‰ ๋ถˆ๊ฐ€. ์œ„์น˜๋ณ„ Beta-Bernoulli ๋ฐด๋”ง์€ "์–ด๋А ์œ„์น˜๋ฅผ ๊ฑด๋“œ๋ฆด ๋•Œ ๊ฐœ์„  ํ™•๋ฅ ์ด ๋†’์€๊ฐ€"๋ฅผ posterior๋กœ ํ•™์Šต โ€” ์œ„์น˜ ์ˆ˜์ค€ ํƒ์ƒ‰-ํ™œ์šฉ ๊ท ํ˜•์„ ๊ฒฝ๋Ÿ‰์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ํ•ฉ๋ฆฌ์  ๋ชจ๋ธ๋ง. ๋‹จ๋ฐฑ์งˆ ๋ณ€์ด ์„ค๊ณ„์—์„œ ๋ฐด๋”ง ๊ธฐ๋ฐ˜ ์œ„์น˜ ์šฐ์„ ์ˆœ์œ„ํ™”๋Š” ๋ฌธํ—Œ์ ์œผ๋กœ ํƒ€๋‹นํ•œ ์ ‘๊ทผ.
  • GP ๊ธฐ๋ฐ˜ ๋‹ค๋ชฉ์  BO (qNEHVI ์ง€ํ–ฅ): ddG-์„ ํƒ์„ฑ ๋“ฑ ์ƒ์ถฉ ๋ชฉ์ ์˜ Pareto ์ „์„ ์„ surrogate๋กœ ํƒ์ƒ‰ํ•˜๋Š” ๊ฒƒ์€ wet-lab/๋„ํ‚น ๋น„์šฉ์ด ํฐ ํŽฉํƒ€์ด๋“œ ์„ค๊ณ„์—์„œ sample-efficiency๋ฅผ ๋†’์ด๋Š” ํ‘œ์ค€ ํŒจ๋Ÿฌ๋‹ค์ž„. ESM-2 ์ž„๋ฒ ๋”ฉ ์˜ต์…˜์€ ์„œ์—ด ์œ ์‚ฌ๋„๋ฅผ ์ž ์žฌ๊ณต๊ฐ„ ๊ฑฐ๋ฆฌ๋กœ ํ™˜์›ํ•ด GP ์ปค๋„์— ์˜๋ฏธ ๋ถ€์—ฌ โ€” ์ตœ์‹  PLM+BO ๊ฒฐํ•ฉ ํ๋ฆ„๊ณผ ์ •ํ•ฉ.
  • scipy ๋ฌด์˜์กด Mann-Whitney U + CV ์ด์ค‘ ๊ฒŒ์ดํŠธ: ๋‹จ์ˆœ best๊ฐ’ ์ถ”์ ์ด ์•„๋‹ˆ๋ผ ๋ถ„ํฌ ์ˆ˜์ค€(์ค‘์•™ ๊ฒฝํ–ฅ ๋ณ€ํ™” ์—†์Œ + ์ €๋ณ€๋™)์—์„œ plateau๋ฅผ ํ†ต๊ณ„์ ์œผ๋กœ ํŒ์ • โ€” ๋…ธ์ด์ฆˆ ํฐ ddG ์‹œ๊ณ„์—ด์— ๋Œ€ํ•ด ๊ณผ์ ํ•ฉ ์—†๋Š” ์ •์ง€ ๊ทœ์น™. ์žฌํ˜„์„ฑยท์ด์‹์„ฑ(์™ธ๋ถ€ ์˜์กด 0) ์ธก๋ฉด์—์„œ ์‹ค์šฉ์ .
  • ์ข…ํ•ฉ ์˜์˜: "ํฐ LLM Planner + ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ๋ฐด๋”ง/BO ํด๋ฐฑ + ํ†ต๊ณ„์  ์ˆ˜๋ ด"์˜ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ๋Š”, ๋น„์‹ผ ๋ฌผ๋ฆฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜(PyRosetta) ํ˜ธ์ถœ ์˜ˆ์‚ฐ์„ ํ†ต๊ณ„์ ์œผ๋กœ ์ ˆ์•ฝํ•˜๋ ค๋Š” active-learningํ˜• ์„ค๊ณ„๋กœ์„œ ๋ฐฉ๋ฒ•๋ก ์  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค. ๋‹ค๋งŒ ๊ฐ€์น˜ ์‹คํ˜„์€ BO ํ”ผ๋“œ๋ฐฑยท์ˆ˜๋ ด ์กฐ๊ธฐ์ข…๋ฃŒ์˜ ๋ฐฐ์„  ์™„์„ฑ์— ๋‹ฌ๋ ค ์žˆ์Œ.

โ‘ฅ ์‚ฌ์šฉ๋ฒ•

๋ฐด๋”ง (์ง์ ‘)

from pyrosetta_flow.bandit import PositionBandit
bandit = PositionBandit()                 # ๊ธฐ๋ณธ ์œ„์น˜ [1,2,4,5,6,11,12,13]
bandit.initialize_from_history(records)   # experiment_log ๋ ˆ์ฝ”๋“œ ๋ฆฌ์ŠคํŠธ
focus = bandit.sample_focus_positions(n=3)

๋ฐด๋”ง (runner ํ†ตํ•ฉ ๋ž˜ํผ) โ€” adapter.get_bandit_guidance(records, n_focus=3) โ†’ {"focus_positions": [...], "source": "bandit_thompson", "arm_stats": {...}}. runner๋Š” Planner๊ฐ€ focus๋ฅผ ๋น„์› ์„ ๋•Œ๋งŒ ์‚ฌ์šฉ(runner.py:592-593).

๋ฒ ์ด์ง€์•ˆ ์ตœ์ ํ™”

from pyrosetta_flow.bayesian_optimizer import BayesianPeptideOptimizer, OneHotEmbedder
bo = BayesianPeptideOptimizer(
    embedder=OneHotEmbedder(max_len=14),
    objectives=["ddg", "ecr_score"],
    maximize=[False, True],
)
bo.fit(obs_dicts)                          # [{"sequence","ddg","ecr_score"}, ...] (>=2๊ฑด)
sugg = bo.suggest(n=3, reference_seq="AGCKNFFWKTFTSC")

runner๋Š” _HAS_BO ์‹œ ์ž๋™ ์ƒ์„ฑ(runner.py:476-486)ํ•˜๊ณ  scoring_pipeline๊ฐ€ fitโ†’suggest(ํ˜„์žฌ ๋กœ๊น… ์ „์šฉ).

์ˆ˜๋ ด ๊ฐ์ง€

from pyrosetta_flow.convergence import ConvergenceDetector
det = ConvergenceDetector(window_size=3, significance_level=0.05)
det.add_iteration(it, top_k_ddgs)
converged, details = det.is_converged()    # details: p_value, cv, recommendation

runner๋Š” ๋งค iteration ํ˜ธ์ถœยทemitter ๋ณด๊ณ (runner.py:906-924). config ํ‚ค: convergence_window_size, convergence_significance, bandit_n_focus(schema.py:37-39).


โ‘ฆ ์™œ ํ•„์š”ํ•œ๊ฐ€ (Action Item ๋ฌด๊ด€์ž„์—๋„)

์ด ํด๋Ÿฌ์Šคํ„ฐ๋Š” ๊ฒฐ๋ก ์„ ๋ฐ”๊พธ์ง€ ์•Š์œผ๋ฏ€๋กœ "์—†์–ด๋„ ๋˜๋Š” ๊ฒƒ ์•„๋‹ˆ๋ƒ"๋Š” ์งˆ๋ฌธ์ด ์ž์—ฐ์Šค๋Ÿฝ๋‹ค. ๊ทธ๋Ÿผ์—๋„ ํ•„์š”ํ•œ ์ด์œ ๋Š” ๋น„์šฉ ๊ตฌ์กฐ ๋•Œ๋ฌธ์ด๋‹ค.

  1. ๋ฌผ๋ฆฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์˜ˆ์‚ฐ์ด ๋ณ‘๋ชฉ. PyRosetta ddGยท๋„ํ‚น์€ ํ›„๋ณด๋‹น ์ˆ˜์ดˆ~์ˆ˜๋ถ„. ์„œ์—ด ๊ณต๊ฐ„์€ ์กฐํ•ฉํญ๋ฐœ์ด๋ผ ๋ฌด์ž‘์œ„/์ „์ˆ˜ ํƒ์ƒ‰์€ ๊ฐ™์€ ์˜ˆ์‚ฐ์œผ๋กœ ํ›จ์”ฌ ์ ์€ ์ข‹์€ ํ›„๋ณด์— ๋„๋‹ฌํ•œ๋‹ค. ๋ฐด๋”ง์€ ๋ณ€์ด ์˜ˆ์‚ฐ์„ "์—ญ์‚ฌ์ ์œผ๋กœ ์ž˜ ๋˜๋Š” ์œ„์น˜"๋กœ ํŽธํ–ฅํ•ด ๋™์ผ ์˜ˆ์‚ฐ๋‹น ๊ธฐ๋Œ€ ์ˆ˜ํ™•์„ ๋†’์ธ๋‹ค. ๊ฒฐ๋ก ์˜ ์ •ํ™•์„ฑ์ด ์•„๋‹ˆ๋ผ ๊ฒฐ๋ก ์— ๋„๋‹ฌํ•˜๋Š” ์†๋„ยทํ™•๋ฅ ์„ ๋‹ค๋ฃจ๋ฏ€๋กœ ์ธํ”„๋ผ์ด๋ฉฐ, ๋ฌดํ•œ ๋ฐœ๊ตด ์—”์ง„์ฒ˜๋Ÿผ ์žฅ๊ธฐ ๊ฐ€๋™ ์‹œ ๊ทธ ๋ˆ„์  ํšจ๊ณผ๊ฐ€ ํ•ต์‹ฌ ์ž์‚ฐ์ด ๋œ๋‹ค.
  2. warm-start = ์ง€์‹์˜ ๋ง๊ฐ ๋ฐฉ์ง€. initialize_from_history/๊ธ€๋กœ๋ฒŒ ๋ฆฌ๋”๋ณด๋“œ๋Š” ์ด์ „ ์‹คํ–‰์—์„œ ๋ฐฐ์šด ์œ„์น˜ ์„ ํ˜ธ๋ฅผ ๋‹ค์Œ ์‹คํ–‰์ด ์ƒ์†ํ•˜๊ฒŒ ํ•œ๋‹ค. ์ด๊ฒƒ์ด ์—†์œผ๋ฉด ๋งค ์‹คํ–‰์ด cold-start๋กœ ๋™์ผ ํ•™์Šต์„ ๋ฐ˜๋ณต โ€” ๋ฌดํ•œ ์—”์ง„ ์„ค๊ณ„์˜ ์ „์ œ(์—ญ๋Œ€ ์ง€์‹ ์žฌ์‚ฌ์šฉ)์™€ ์ง๊ฒฐ.
  3. ์ •์ง€ ๊ทœ์น™ = ์˜ˆ์‚ฐ ๋‚ญ๋น„ ์ฐจ๋‹จ. ์ˆ˜๋ ด ๊ฐ์ง€๋Š” plateau๋ฅผ ํ†ต๊ณ„์ ์œผ๋กœ ์•Œ๋ ค, ๊ฐœ์„  ์—†๋Š” iteration์— ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์˜ˆ์‚ฐ์„ ๋” ์“ฐ๋Š” ๊ฒƒ์„ ๋ง‰๋Š” ์‹ ํ˜ธ๋ฅผ ์ค€๋‹ค(ํ˜„์žฌ๋Š” advisory์ง€๋งŒ ์ž๋™ ์ข…๋ฃŒ๋กœ ์Šน๊ฒฉ ์‹œ ์ฆ‰์‹œ ์˜ˆ์‚ฐ ์ ˆ๊ฐ).
  4. Planner ํ™˜๊ฐ/๊ณต๋ฐฑ์˜ ์•ˆ์ „๋ง. ๋ฐด๋”ง์€ LLM Planner๊ฐ€ focus_positions๋ฅผ ๋ชป ์ค„ ๋•Œ์˜ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ํด๋ฐฑ(runner.py:592)์œผ๋กœ, ํƒ์ƒ‰์ด ๋ฌด๋ฐฉํ–ฅ์œผ๋กœ ๋น ์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•œ๋‹ค.

์š”์•ฝ: ๊ณผํ•™์  ํŒ์ •์€ ์Šค์ฝ”์–ด๋ง/๊ฐ€๋“œ๊ฐ€, ๊ทธ ํŒ์ •์„ ์–ธ์ œยท๋ฌด์—‡์— ๋Œ€ํ•ด ํšจ์œจ์ ์œผ๋กœ ๋‚ด๋ฆด์ง€๋Š” ๋ณธ ํด๋Ÿฌ์Šคํ„ฐ๊ฐ€ ์ฑ…์ž„์ง„๋‹ค. Action Item์— ์ง์ ‘ ๊ธฐ์—ฌํ•˜์ง€ ์•Š์ง€๋งŒ, ํ•œ์ •๋œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์˜ˆ์‚ฐ์„ ๊ฐ€์ง„ ๋ฐœ๊ตด ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ฒ˜๋ฆฌ๋Ÿ‰ยท์ง€์†๊ฐ€๋Šฅ์„ฑ์„ ๊ฒฐ์ •ํ•˜๋Š” ๋ณด์กฐ ์ธํ”„๋ผ๋กœ์„œ ํ•„์š”ํ•˜๋‹ค. ๋‹ค๋งŒ BO ํ”ผ๋“œ๋ฐฑ ๋ฐฐ์„ ๊ณผ ์ˆ˜๋ ด ๊ธฐ๋ฐ˜ ์ž๋™ ์ข…๋ฃŒ๊ฐ€ ๋ฏธ์™„์ด๋ฏ€๋กœ, ํ˜„์žฌ ์‹คํšจ ๊ธฐ์—ฌ๋Š” "๋ฐด๋”ง warm-start" ์ถ•์— ์ง‘์ค‘๋˜์–ด ์žˆ๊ณ  BO/์ˆ˜๋ ด์€ ์ž ์žฌ๋ ฅ ์ƒํƒœ์ž„์„ ๋ฐํ˜€๋‘”๋‹ค.


๊ฒ€์ฆ ์ธ์šฉ ๋ชฉ๋ก (file_path:line)

  • bandit.py:24 โ€” ๋ณ€์ด ๊ฐ€๋Šฅ ์œ„์น˜ ๊ณ ์ • [1,2,4,5,6,11,12,13] (SS bond/pharmacophore ์ผ๋ถ€ ์ œ์™ธ)
  • bandit.py:26-27, 60-61 โ€” ddG plausible ๋ฒ”์œ„ ํ•„ํ„ฐ [-60, 200]
  • bandit.py:49-100 โ€” initialize_from_history ๋ถ€ํŠธ์ŠคํŠธ๋žฉ (baselineยทcreditยทฮฑ/ฮฒ ๊ฐฑ์‹ )
  • bandit.py:74-78 โ€” WT ํ‰๊ท  ๋˜๋Š” ์ค‘์•™๊ฐ’ baseline
  • bandit.py:95-100 โ€” ๊ฐœ์„ /์•…ํ™” ์ด์ง„ โ†’ ฮฑ/ฮฒ +1 (magnitude ๋ฌด์‹œ, ๋‹ค์ค‘๋ณ€์ด ๊ท ๋“ฑ credit)
  • bandit.py:102-115 โ€” sample_focus_positions (random.betavariate Thompson ํ‘œ๋ณธ)
  • bandit.py:117-137 โ€” update/get_arm_stats
  • bayesian_optimizer.py:25-45 โ€” BoTorch ์„ ํƒ์  import ๊ฐ€๋“œ
  • bayesian_optimizer.py:96-130 โ€” OneHotEmbedder ์‹ค๊ตฌํ˜„
  • bayesian_optimizer.py:136-183 โ€” ESM2Embedder ์‹ค๊ตฌํ˜„(๋ฏธ๋ฐฐ์„ )
  • bayesian_optimizer.py:189-246 โ€” _FallbackGP numpy RBF GP ์ •์‹ ๊ตฌํ˜„
  • bayesian_optimizer.py:362-372 โ€” BoTorch SingleTaskGP/ModelListGP fit
  • bayesian_optimizer.py:430-464 โ€” ๋‹จ์ผ์  ๋ณ€์ด enumerate
  • bayesian_optimizer.py:516-531 โ€” qNEHVI ๋Œ€์‹  product-of-improvements ํ”„๋ก์‹œ
  • bayesian_optimizer.py:533-550 โ€” ํด๋ฐฑ UCB ํš๋“ํ•จ์ˆ˜
  • convergence.py:14-27 โ€” _rank_data ๋™์  ํ‰๊ท ์ˆœ์œ„
  • convergence.py:30-59 โ€” _mann_whitney_u ์ •๊ทœ๊ทผ์‚ฌ p๊ฐ’
  • convergence.py:62-64 โ€” math.erf ์ •๊ทœ CDF (scipy ๋ฌด์˜์กด)
  • convergence.py:94-99 โ€” ์ตœ์†Œ 2ยทwindow_size iteration ์š”๊ตฌ
  • convergence.py:126-129 โ€” ์ˆ˜๋ ด = (p>์œ ์˜์ˆ˜์ค€) AND (CV<0.15)
  • adapter.py:113-128 โ€” get_bandit_guidance warm-start ๋ž˜ํผ
  • runner.py:462-472 โ€” ๋ฐด๋”ง/์ˆ˜๋ ด detector ์ดˆ๊ธฐํ™”
  • runner.py:476-486 โ€” BayesianPeptideOptimizer ์ž๋™ ์ƒ์„ฑ(OneHotEmbedder, ddg/ecr_score)
  • runner.py:508 โ€” ๋ฉ”์ธ iteration ๋ฃจํ”„ (conv_flag ๊ธฐ๋ฐ˜ break ๋ถ€์žฌ)
  • runner.py:592-593 โ€” Planner ๋ฏธ์ง€์ • ์‹œ์—๋งŒ ๋ฐด๋”ง focus ์ ์šฉ (ํด๋ฐฑ)
  • runner.py:906-924, 1287-1293 โ€” ์ˆ˜๋ ด ๊ณ„์‚ฐยทemitter ๋ณด๊ณ  (advisory)
  • scoring_pipeline.py:246 โ€” "Step 4: BO suggest (๋ถ€์ˆ˜ํšจ๊ณผ ์—†์Œ โ€” ๋กœ๊ทธ๋งŒ)"
  • scoring_pipeline.py:249-279 โ€” BO fitโ†’suggest, ๊ฒฐ๊ณผ ๋กœ๊น…๋งŒ
  • schema.py:37-39 โ€” config ํ‚ค convergence_window_size/convergence_significance/bandit_n_focus
  • tests/test_bandit.py, tests/test_convergence.py โ€” ์ „์šฉ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์กด์žฌ