AIが描いた絵を、別のAIに検品させています。仕上がった絵本のページを1枚ずつ見せて、「主人公の見た目がおかしくないか」「腕が3本生えていないか」を答えさせる、という素朴な仕組みです。

その検品が「問題なし」と言い、実物は明らかに壊れている、ということが二度ありました。外から見た症状は、二度ともまったく同じでした。 ログには clean とだけ残ります。

そして原因は正反対で、当然ながら効く対策も正反対でした。以下はその二件の記録です。数字はすべて自分たちの実測値です。


一件目:髪が肩より長い子が、短髪で描かれた

10歳の女の子の絵本で、3ページ続けて髪型が短く描かれました。参照写真も、他のページも、髪は肩より下です。検品はその3ページを全部 clean で通しました。

最初に立てた仮説は、いま思えばいちばん自然なものでした。検査に渡している画像は長辺1024pxに縮めたもので、髪の長さを見るには小さすぎるのではないか。

良いページ3枚・悪いページ3枚(同じ子・同じ構図)を用意して測りました。結果は、その仮説を二重に否定しました。

まず、1024pxでもちゃんと見えていました。 「この子の髪はどう見えるか」とだけ聞くと、悪いページでは毎回「短い(short)」、良いページでは毎回「肩より下(past the shoulders)」と答えます。12/12で正解です。 画像を2048pxに上げても、判定は1つも変わりませんでした。

次が問題でした。 同じモデルに「この髪は元と大きく違うか?」と判断として聞くと、悪いページ6枚すべてで NO と答えます。しかも同じ返答の中で、その髪を「短い」と描写しながらです。

つまり、知覚は無傷で、判断が甘い。 見えているものを、自分で「まあ許容範囲だ」と丸めていました。

効いたのは解像度でも、言葉の強さでもありませんでした。順序です。比較を求める前に、まず観察した長さを言わせる。それだけで裏返りました。

だからこの検査は、長いチェックリストの中の1項目ではなく、二段構えの独立した呼び出しとして置いてあります。「まず記述させ、そのあとで比較させる」という手順そのものが本体なので、他の項目と一緒くたにすると成立しません。


二件目:こちらは本当に解像度だった

別の本で、主人公だけがアニメのセル画のような描き方(輪郭線があって、べた塗りで、陰影が「描かれて」いる)になり、水彩で描かれた背景の上に貼り付いたようになりました。これも clean で通っていました。

同じ疑いをかけて、同じように測りました。今度は結果が逆でした。

検査に渡した画像 8枚中の検出
4Kの原画から頭部を切り出したもの 8/8
2048pxに縮めたページ全体 7/8
1024pxに縮めたページ全体(当時の本番) 7/8

モデルも同じ、指示の文言も一字も変えていません。変えたのはピクセル数だけです。 そして外した1枚は、縮小した2条件でどちらも同じページでした。引きの構図(WIDE)のページで、そこでは顔がおよそ55pxしかありません。

この描き分けは、55pxでは物理的に見えません。一件目とは違って、ここは本当に「渡している画像に、その欠陥がもう存在しない」でした。

対策は、先に頭の位置を探してから、原画のほうをその周りで切り出して渡す、というものです。ひとつだけ注意点があって、顔だけにきつく寄せてはいけません。 この判定は「子どもと背景の描き方が食い違っているか」という比較なので、比較対象である背景を切り落とすと、質問そのものが意味を失います。頭の大きさの2.6倍を残しています。


「問題なし」には少なくとも3つの原因がある

二件並べて、ようやく分かりました。同じ clean でも、原因はこう分かれます。

  1. 見えていない — 解像度、切り出し方、圧縮。渡した時点で欠陥が消えている
  2. 見えているが通している — 知覚はできていて、判断が甘い。聞く順序や聞き方の問題
  3. そもそも聞いていない — 契約上その検査の対象外だったり、長いチェックリストの中で実行されていなかったり

対策は3つとも違います。 1は画像の作り方を直す。2は聞く順序を直す。3は独立した呼び出しに切り出す。だから最初に切り分けないと、効かない対策を延々と磨くことになります。 わたしたちは一件目で、まず解像度を上げにいって空振りしました。

切り分け自体は、驚くほど簡単でした。「何が見えるか」と「それは問題か」を、分けて聞くだけです。

  • 「見える」と答えられないなら → 1(渡している画像の問題)
  • 正しく描写できるのに「問題ない」と言うなら → 2(判断の問題)

この2問を先にやっていれば、一件目で解像度を疑って回り道することはありませんでした。1分の実験です。


測ったあとの話:その数字は何点分の価値があるのか

もうひとつ、自戒として書き残しています。

一件目の文言は、その6枚を見ながら選びました。 そして採点も同じ6枚でやっています。コードのコメントには、そのことを大文字でそのまま書いてあります。

TREAT THOSE AS IN-SAMPLE — the same six images were used to pick this wording,
so the false-positive figure in particular is optimistic.

同じ画像で文言を調整して、同じ画像で採点したら、良い数字が出るのは当たり前です。とくに誤検出(本当は正常なのに問題と言う)の少なさは、いくらでも見かけ上よくできてしまいます。

なので、無関係な本での成績を別に測っています。関係のない6冊で 0/16、別の9冊で 0/33。そのうえで再現した唯一の誤検出は1歳児のページでした。赤ちゃんの髪を「短い」と判定するのは、検査としては正しくても、事故としては無意味です。そこは検査の文言ではなく、呼び出し側で対象から外しました。


補足:再現しない失敗は、プロンプトを直す対象ではない

最後に、直し方の順番の話を少しだけ。

わたしたちが使っている画像モデルは、seed を受け付けません(入力スキーマにないので、渡すと422で弾かれます)。つまり1ページごとに独立した引き直しで、同じ絵は二度と出せません。

先ほどの引きの構図のページも、そのページ自身の本番プロンプトで6回引き直して、0/6で再現しませんでした。 これは低頻度のランダムな失敗です。

ここでいちばんやりがちなのは、プロンプトを触ることです。でも再現しないのだから、触ったあとで良くなったのか、たまたま出なかったのかを区別できません。 しかも、全ページ共通の文言を触れば、いま正常に出ている全部のページに影響します。

なので、共通の文言は一切触らず、検出して引き直すほうに倒しました。測定できるベースラインがないうちは、全体に効く設定に手を入れない。 これはモデルの話というより、ランダムな失敗の扱い方の話です。


まとめ

  • 検査が「問題なし」と言うとき、見えていないのか、見えていて通しているのかを先に分ける。症状は同じで、対策は正反対
  • 切り分けは2問でできる。「何が見えるか」と「それは問題か」を分けて聞く
  • 見えていない場合:その欠陥は、渡している解像度でまだ存在するのか。 引きの構図では顔が55pxになることがある
  • 見えている場合:効くのは言葉の強さではなく順序。比較させる前に、観察を言わせる
  • 切り出すときは比較対象を切り落とさない。「AとBが食い違っているか」を聞くなら、Bも画面に残す
  • 文言を選ぶのに使った画像で採点した数字は楽観的。無関係なサンプルで別に測る
  • 再現しない失敗にプロンプトで応じない。 検出して引き直す

検査の精度が上がらないとき、疑う先はだいたい「もっと強く言えばいいのでは」に向かいます。実際に効いたのは、二件ともモデルに何をどの順で見せるかのほうでした。


この記事について

はじめに書いておくと、これはわたしたちが自分たちのプロダクトで踏んだ失敗の記録で、第三者のレビューではありません。上に出てくる数字はすべて自分たちの実測で、コメントの引用も自分たちのコードからそのまま持ってきています。

わたしたちは Lumora という、写真を1枚アップロードすると子どもが主人公になる絵本をAIが書いて描くサービスを作っています。全ページを通して主人公の見た目が変わらないようにするのが技術的な肝で、この記事の「検品」は、そのために動いている仕組みの一部です。

同じような自動チェックを作っている方の役に立てば嬉しいですし、「うちはこう切り分けている」という話があればぜひ教えてください。

Jason Cheng(Lumora チーム)