IMAGE: AI生成ビジュアル
RESEARCH / TRL
Hugging Face、コードで水彩画を描くモデルをTRLとOpenEnvで再現 — 1.5M回再生のRL水彩を完全オープンに
p5.brushでJavaScriptを書いて描く水彩。TRL+OpenEnvで報酬を“美の好み”に置き換え、手作業で厳選した178枚のプールを基準にGRPOで学習。H200 Jobsで110ステップ、推論はQwen3.5-35B-A3BにLoRAを適用。
Hugging FaceのSergio Paniego氏は9月3日、言語モデルにJavaScriptを書かせて水彩画を描かせる試みを、TRLとOpenEnvで再現したブログ「Training a coding model to paint watercolours with TRL and OpenEnv」を公開した。元となったのは、Surya Narreddi氏が8月23日に投稿し150万回以上再生された水彩生成の動画だ。同氏はp5.jsに自然な描画ツールを与えるライブラリp5.brushを使い、モデルが約150行のJavaScriptで一枚を描く手法で注目を集めた。今回の再現は、そのアイデアをエンジニアリング側からオープンに再実装し、学習データ、RL環境、学習コード、学習済みモデルまでを全てHub上で公開した点が特徴だ。
出典: Training a coding model to paint watercolours with TRL and OpenEnv — Hugging Face Blog
何を作ったか — コードが絵を描くRLの全体像
- 媒体はコード: モデルは画像を直接生成しない。p5.brushの47メソッドのうち、作者が厳選した10メソッド(
scaleBrushesnoStrokefillnoFillfillBleedfillTexturebeginShapevertexendShapecircle)だけを使ってdraw()を含む約150行のJSを出力する。brush.fillBleed(0.25)のように顔料のにじみ自体をコードで制御し、紙の質感や筆のストロークはライブラリ側がシミュレートする。 - パイプラインはHugging Faceで完結: 学習はHF Jobs、RL環境とスコアラーモデルはSpaces、pairwise judgeはInference Providers、成果物はHubのコレクションに集約。環境とスコアラーのSpaceを複製し、2つの環境変数で報酬配分を切り替えた上で、以下の1コマンドで実行できる。
- 学習設定(一次情報のレシピ): ベースは
Qwen/Qwen3.5-35B-A3B、LoRAall-linear・bf16・gradient checkpointing、学習率5e-5(constant_with_warmup, warmup 5)、scale-rewards none、steps 110・episodes 240・generations 8・batch 1・accum 8・max completion 8192。flavorはH200、timeout 48h、subjectはa peach hibiscus、references 4。生成はtop-p 0.95 / top-k 20。 - プロンプトの工夫: 長いAPIリファレンスは存在しないメソッドの幻覚を招くため、200回の自動プロンプト最適化(GEPA)で収束した「10メソッドのみ許可・説明なし」のallowlistを採用。筆者独自の追記は「各花弁を2〜3回重ね塗り(最初は大きく、次は小さく不透明に)」の一文で、色彩の改善に寄与したとしている。
- 検証用の制御タスク: 初期は報酬が平坦で学習が進まなかったが、ブラウザもjudgeも使わない簡易タスクで学習率不足(2e-5→5e-5)とスケジューラ(linear→constant_with_warmup)等の4点修正により、初めて報酬が上昇したという。
報酬設計 — 「好み」で学ばせる
出典: Training a coding model to paint watercolours with TRL and OpenEnv — Hugging Face Blog
- 4項の加重和(Narreddi氏が収束させた配分):
gate 0.05(コンパイル成功・実体のある描画・チート検出)+length 0.05(長いコードへの弱い誘導)+pairwise judge 0.60(プールに対するスタイル勝率)+HPSv3 0.30(画像の美的嗜好)。3本の実験ではjudgeとHPSv3の配分だけを入れ替えた。
| run | pairwise judge | HPSv3 | 役割 |
|---|---|---|---|
| judge-led | 0.60 | 0.30 | オリジナル配分・110 steps |
| hps-led | 0.30 | 0.60 | 中間配分・110 steps |
| hps-only | 0.00 | 0.90 | 検証用・60 stepsで早期停止 |
- HPSv3: MizzenAIの公開7B嗜好モデル。画像とテキスト記述から人が好む度合いのスコアを返す。多数の人手によるペア比較で学習された「みんなの平均的な好み」の代理。
- pairwise judge:
Qwen/Qwen3-VL-30B-A3B-InstructをHF Inference Providers経由で呼び出し。候補の描画と、プールからランダムに選んだ4枚の参照画像を並べて比較し、提示順を入れ替えた両方向での勝率をスコア化する。判断基準はプールのみで、著者の手作業レーティング(“私の好み”)を符号化している。 - ゲートとレンダリング: ヘッドレスChromiumで各スケッチをレンダリングし、ライブラリ外の直接描画やキャンバスへのテキスト書き込み等のチートをゲートが弾く。
プールが報酬関数になる — 178枚を手で選ぶ
- プールの規模と作り方: 178枚を
loveとokayの2ティアに分割。全てモデル生成で、iNaturalistのオープンライセンスのハイビスカス写真を基に、4つのオープンモデルがp5.brushスケッチを執筆、ビジョンモデルが3回の反復でフィードバック、最後に著者が1枚ずつレーティングして178枚を残した。
| 生成モデル | 枚数 |
|---|---|
| GLM-5.2 | 64 |
| Kimi-K3 | 57 |
| Qwen3-Coder-Next | 35 |
| Qwen3.5-122B-A10B | 22 |
- 選定の意図: オープンで毎回有効なスケッチを返すことを確認できた4系統を採用し、2候補は信頼性不足で除外。プールには人手のp5.brush作品を含めていない(ニッチなライブラリゆえ十分なコード付き作品が存在しないため)。本文ではこの点を明確な制約としている。
- 報酬への効き方: pairwise judgeが引く4枚の参照は
loveとokayから半数ずつ。弱い初期ポリシーでも勝てる余地を残しつつ、どちらのティアに勝っても同じ報酬が得られる設計で、初期の学習シグナルを確保した。オリジナルはトップティアのみとの比較だった点をあえて変更している。 - 学習への含意: プールを差し替えればコードを触らずに報酬が変わる。出力の多様性もプールに依存し、単一主題(peach hibiscus)に絞ったプールでは、GRPOの収束とともに各ステップの中央値の絵が互いに似てくることが動画と数値で示されている。
何が学ばれたか — 数値と観察
- 学習曲線: いずれのrunも報酬は上昇。1ステップ15〜18分、judgeを含む2本は200 steps予定を比較が安定した段階で110で停止。グループ平均報酬(前1/3 vs 後1/3)は
hps-only 0.58→0.71 (+0.13)、judge-led 0.45→0.72 (+0.27)、hps-led 0.57→0.82 (+0.24)。judgeの比重が大きいほど初期は低くノイズが大きいが、最終的な伸びは大きい。 - 最初に起きること: どのrunでも最初に消えるのは低報酬(0.3未満)の不良描画(空白や不定形の洗い)。
hps-onlyではグループ平均の上昇の4分の3がこの不良の減少で説明でき、judge-ledでは0.3未満が99→16、hps-ledでは37→4に減少した。 - 上位を伸ばすのはjudge:
hps-onlyは信頼性は上がるが上位の質は+0.03しか伸びず、中心と茎があればHPSv3は満足してしまう。一方judgeありでは各ステップの最良も伸び、塗りカバレッジは0.11→0.23(judge-led)、0.13→0.30(hps-led)と倍増し、hps-onlyはほぼ不変。良い絵でも参照に勝てばさらに報酬が得られるため、顔料を多く使う方向に学習が進んだ。 - 指示の無視も合理: システムプロンプトは「15〜30個の塗り形状」を求めるが、実際の平均は7〜9個で、形状数と報酬の相関は
+0.000 / -0.14 / +0.07とほぼ無相関。報酬が与えない文はポリシーが無視するという観察が報告されている。 - 天井と重複化:
hps-onlyの良いサンプルの水準に全ロールアウトが到達すれば平均は0.771で頭打ちになる可能性が指摘されている。また同一run内では報酬の分散が縮み、出力が単一の花に収束する。より多様な出力にはより多様なプール(=キュレーション作業)が必要だと結論づけている。 - 修正した4点(TRLのGRPOTrainer): 学習率
2e-5→5e-5、スケジューラlinear→constant_with_warmup、scale_rewards group→none(gateの1リジェクトがグループ全体のadvantageを潰していた)、target_modules 手動リスト→all-linear(MoEの命名差で10層しか学習していなかった)。