遺伝的アルゴリズムで「好みの顔」を探して失敗した
自分の好みを言葉で説明するのは難しい。「タイプ」という言葉は出てくるが、では顔のどの要素がそう感じさせているのかと問われると答えられない。私も答えられなかった。
そこで画像生成AIと遺伝的アルゴリズムを組み合わせ、この言語化できない好みを機械的に探索してみた。結果から書くと、失敗した。4世代80個体を生成して、探していた顔は最終世代まで現れなかった。
うまくいった話より、失敗の原因を掘ったほうが役に立つと思うので、世代ごとの実物を並べながら検証録として残しておく。
やったこと
通常の遺伝的アルゴリズムは適応度を計算する評価関数をプログラムとして書く。だが「好みかどうか」は数式で書けない。そこで人間自身を評価関数として使う。対話型遺伝的アルゴリズムと呼ばれる手法で、1990年代から音楽やデザインの分野で使われてきた。
手順はシンプルだ。
・ランダムな遺伝子の組み合わせで個体群を生成する ・人間が good と bad を選ぶ ・選ばれた個体の遺伝子を交叉させ、突然変異を加えて次世代を作る ・繰り返す
顔の特徴を8つの遺伝子に分解した。系統、肌の色、目の形、眉、髪型、唇、体型、表情の雰囲気。それぞれに3から5の対立形質を用意し、ランダムに組み合わせて第1世代20体を生成した。生成には Higgsfield の soul_2 を使い、1枚あたり0.12クレジット、20体で2.4クレジットである。背景と衣装は固定し、顔まわりだけが変数になるようにした。
どう回したか
環境は Claude Code で、Higgsfield の MCP サーバーを接続し、画像生成ツールを会話から直接呼んでいる。遺伝子の組み合わせを決めてチャットで指示すると、それがそのままプロンプトに変換されて生成が走り、世代の管理も会話の中で完結する。ブラウザのUIを触る場面は一度もなかった。
使ったモデルは soul_2 で、内部的には text2image_soul_v2 という名前になっている。出力はアスペクト比3対4、品質2k、実寸1536かける2048である。
プロンプトは固定部と可変部に分けた。固定部は撮影場所、照明、衣装、カメラの寄りで、全個体で完全に同じ文字列を使う。可変部が遺伝子で、系統、肌、目、眉といった対立形質を順に並べただけの単純な列である。比較したい要素以外を揃えるのは実験の基本だが、これを怠ると「衣装が好みだっただけ」といった交絡が入る。
バッチ投入には2つの制約があった。1回のリクエストで投げられるのは12件まで、さらに同時実行数にプランごとの上限があり、契約していた Plus では8ジョブである。20体を一気に投げると9件目以降が rate limit で弾かれたので、7体、7体、6体と分割し、完了を待ってから次を送る形にした。
そしてこれが後で効いてくるのだが、soul_2 が公開しているパラメータは品質とSoul-IDの2つだけで、シードを指定する口がない。生成が終わったあとの結果にシード値が記録されて返るだけである。
いくらかかったか
生成前にコストを見積もる機能があるので、実行のたびに消費量を確認しながら進めた。画像1枚あたりの単価は一定である。
| 単位 | クレジット |
|---|---|
| 画像1枚 | 0.12 |
| 1世代(20体) | 2.4 |
| 全4世代(80体) | 9.6 |
| 遺伝子座1つあたり | 0.64 |
| 確定した遺伝子1つあたり | 0.96 |
遺伝子座は15、最終的に確定した遺伝子は10個だったので、後ろの2行はそれで割った値である。1つの遺伝子を確定させるのに1クレジット弱、という感覚になる。
比較のために書いておくと、同じサービスの動画生成は10秒で20クレジットかかる。動画1本の予算で画像なら167枚生成できる計算で、探索のように数を打つ用途では画像のほうが2桁安い。今回の実験が4世代で済んだのはコストの制約ではなく、単に方向が見えなくなったからである。
第1世代、眉だけが完全に分離した
G1 / 第1世代 20体のうち判定した9体









左の4体が好み、右の5体が非好み。決定的だったのは眉で、好み側は全員が細眉、非好み側は全員が太眉と、8遺伝子のうち眉だけが完全に分離した。
8個の遺伝子のうち、眉だけがきれいに割れた。好み側は4体すべてが細眉、非好み側は5体すべてが太眉である。目の形も強い信号を示し、非好み5体すべてが大きく丸い目で、好み側には1体も含まれていない。
逆に系統は、好み側が4種類ばらばらだった。顔立ちのルーツは好みにまったく寄与していない。この時点で、確定した遺伝子と中立な遺伝子がはっきり分かれた。
第2世代、解釈を誤って停滞した
G2 / 保留3体と淘汰5体








淘汰された5体は全員が頬ぷっくり。好まれていたのは体型のふくよかさであって、顔の丸さではなかった。
good はゼロ、まあまあが3体という結果だった。原因は私の解釈ミスである。
第1世代で「ぽっちゃり」が好まれたのを、顔まで含めた全体の話だと読んでしまい、頬をふっくらさせた個体ばかり作った。だが淘汰された5体は全員が頬ぷっくりで、保留になった3体のうち2体は中庸頬である。好まれていたのは体型のふくよかさであって、顔の丸さではなかった。
体型と顔の輪郭を別々の遺伝子として分けていなければ、この取り違えには気づけなかった。遺伝子の粒度が粗いと、誤った方向へ収束していく。
第3世代、収束したように見えた
G3 / 初めて good が出た5体





20体中5体が good。ここで肌は色白、目の間隔は広め、髪はウェーブを排除して直毛、と3つの遺伝子が確定した。この時点では収束が始まったように見えた。
20体中5体が good。第2世代の停滞から明確に前進したように見えた。この世代で肌は色白、目の間隔は広め、髪は直毛と、3つの遺伝子が追加で確定している。
特に髪型は、good の5体にウェーブが1体も含まれないという negative 側の情報として強く出た。この時点で私は「方向は正解」と判断した。この判断が誤りだったことは、次の世代でわかる。
第4世代、そして到達しなかった
G4 / 最終世代






確定遺伝子10個を全個体が共有している。それでも、探していた顔は1体も現れなかった。
確定遺伝子10個を全個体が共有した状態で、探していた顔は現れなかった。
収束ではなかった
第3世代の good 率25パーセントを、私は収束の兆しとして読んだ。だが最終世代まで到達しなかった以上、あれは収束ではない。
good と判定された個体は、おそらく「明確に嫌ではない」個体だったのだと思う。80枚も顔を見せられれば、相対的にましなものを選ぶことはできる。だがそれは絶対的な好みへの接近を意味しない。負の情報だけが蓄積して、正の情報がゼロのまま探索が終わった。これが今回の失敗の形である。
なぜ届かなかったか
原因を疑わしい順に並べる。上位2つはアルゴリズムの問題ではなく、実験設計そのものの欠陥だった。
シードが固定されていなかった
これが最大の欠陥である。各個体は遺伝子とランダムシードの積として生成されていた。同じ遺伝子を渡しても、シードが違えば別の顔が出る。
つまり、ある個体が選ばれたとき、それが遺伝子のおかげなのかシードの偶然なのかを分離できていない。遺伝的アルゴリズムは選択圧が遺伝子にかかることを前提とするが、今回は選択圧のかなりの部分がノイズにかかっていた。世代を重ねても情報が蓄積しない構造になっており、手法として成立していない。
質が悪いのは、これに気づいたのが80枚を生成し終えた後だったことだ。前述のとおり、使ったモデルが公開しているパラメータは品質とSoul-IDだけで、シードの指定口が見当たらない。生成結果にはシード値が記録されて返ってくるので、渡す方法があるかどうかを最初に調べるべきだった。調べないまま、シードが暗黙に固定されている前提で回し始めていた。
顔の印象は要素の足し算ではない
顔の魅力は配置的に知覚されることが知られている。目や鼻といったパーツ単体ではなく、パーツ間の距離や比率という全体の布置で決まる。
私が組んだ遺伝子は「太眉か細眉か」といったカテゴリの集合で、比率という連続量を表現していない。目と目の間隔を広いか標準かの2値にしたところで、実際に効いているのが顔幅に対する比なら、その情報は落ちる。探索空間そのものが、好みの存在する空間とずれていた疑いが強い。
プロンプトが書き換えられた
生成の途中で、モデル側の自動補完が指定を勝手に詳細化していた。衣装が全個体で同じ色に寄ったことで気づいたが、指定した遺伝子が画像に反映されていない個体が混ざっている。遺伝子型と表現型の対応が壊れており、その個体への評価は系統に正しく伝わらない。
モデルの事前分布の外にあった
生成モデルには特定の顔立ちへの強い偏りがある。探していた顔がその分布の外にあるなら、どれだけ世代を回しても到達しない。これはアルゴリズムではなく探索範囲の限界で、モデルを変えるしか手がない。
翻訳者が毎回はさまる
評価するのは人間、その評価を遺伝子に翻訳するのも人間である。第2世代の頬の取り違えは自覚できたが、同種の誤読が他の遺伝子座で起きていない保証はない。人間が評価関数である以上、そのフィードバックの解釈が最大のボトルネックになる。
信号が薄すぎた
15の遺伝子座が張る空間に対して、1世代20個体を4世代、しかもカテゴリ判定だけでは情報量が足りない。次元の呪いに対して、サンプル数が桁で不足していた。
次に回すなら
同じことをやるなら、遺伝的アルゴリズムはやめる。
まずシードを固定する。そのうえで、一度に1つの遺伝子だけを変えた対照実験にする。眉だけが違う2枚を並べて選ばせれば、その差は確実に眉に由来する。地味だが、ノイズと遺伝子の効果を分離できる。遺伝子座が15あっても、1座あたり数枚で済む。
連続量を扱いたいなら、カテゴリではなく画像そのものを補間したほうがいい。2枚の顔の潜在表現を混ぜて中間を作り、どちら寄りが好みかを二分探索する。これなら比率のような言語化できない情報も探索対象に入る。
そしてもうひとつ。今回かかったのは80枚で9.6クレジットである。安さに油断して、設計を詰めないまま回し始めたのが根本的な失敗だと思う。安いからこそ雑に試せるのは事実だが、雑に回した実験は雑な結論しか返さない。シードを渡す方法があるかどうかを最初に確かめていれば、この9.6クレジットは別の設計に使えた。
失敗から残ったもの
好みの顔は見つからなかったが、副産物として「明確に好みではない条件」は10個ほど確定した。太眉、大きく丸い目、あざとい笑顔、頬のふくらみ、濃い肌、ウェーブの髪。これらは繰り返し negative 側に現れ、一度も positive 側に来なかった。
負の情報は正の情報より安定して取れる。「これは違う」という判定は迷いがないぶんノイズが少なく、遺伝子がきれいに分離する。もし次に設計するなら、good を選ばせるのではなく bad だけを選ばせ続けて、消去法で領域を狭める方式のほうが機能するかもしれない。
失敗ではあるが、どこで失敗したかがはっきりしている失敗ではあった。シードを固定して、もう一度やってみようと思う。