원화의 얼굴만 다른 화풍으로 갈아끼우는 작업의 기록.

🔴 이 문서는 하다가 알게 된 것을 남기는 자리다. 계획이 아니라 실측이다. 수치와 프롬프트는 코드가 정본이다 — games/gangnam-survivors/scripts/art/.

한 줄

사장님이 고른 레퍼런스 화풍을 LoRA 로 학습해서, 얼굴 영역에만 덧입힌다. 몸·의상·머리·표정·포즈는 원화 그대로 남는다. 결과물을 디자인 B 라 부른다 (위키에 원래 올라가 있던 것이 디자인 A).

왜 얼굴만인가

전신에 걸면 무너진다. 학습셋이 상반신 위주(311장 중 upper body 52%, 전신은 1,320장 중 12장)라, 전신 구도에 강하게 걸면 모델이 남는 캔버스를 스튜디오 소품과 실내 배경으로 채운다. 얼굴 크롭에만 걸면 학습 도메인과 적용 도메인이 일치한다.

⚠️ 나중에 안 것: 원본 composite-1 워크플로에도 FaceDetailer 패스가 이미 있었다 (face_yolov8m + SAM, denoise 0.25). 같은 구조로 수렴했다.

🔴 첫 판이 실패했고, 원인은 캡션이 잘리는 것이었다

사장님 지적: “charx에 있던 얼굴 스타일이 전혀 반영이 안돼.” 맞았다.

배선은 정상이었다 — 다른 걸 고정하고 가중치만 바꿔 픽셀을 재니 98~99% 가 바뀐다. 문제는 학습이었다.

캡션 토큰 중앙값96 (최대 163)
sd-scripts 기본 창75
75 초과279 / 320 = 87%

--max_token_length 를 안 줘서 75 토큰에서 잘렸다. 거기에 shuffle_caption 이 켜져 있어 매 스텝마다 서로 다른 태그가 사라졌다. 설명이 사라진 내용은 갈 곳이 가중치뿐이라, LoRA 가 화풍 대신 의상과 배경을 배웠다.

이건 로컬 codex 교차 검수(Sol·Terra)가 잡았다. 내 가설은 “캡션이 너무 자세하다” 였는데 방향만 맞고 기전을 못 짚었다 — 길어서 문제가 아니라 길어서 잘려서 문제였다.

같이 나온 것 셋:

  • 큐레이션 필터가 solo 노골 태그(nipples·nude·masturbation)를 통과시켰다 — 320장 중 53장
  • 캐릭터 이름 태그(arlecchino (genshin impact) 등)가 캡션에 남아 있었다. 모델이 이미 아는 토큰으로 그림을 설명해 버리면 트리거가 흡수할 잔여가 없다
  • 내가 근거로 댄 “캐릭터 이름 72장” 이 과장이었다 — shrug (clothing) 같은 괄호 태그를 같이 셌다

🔴 “100%” 라고 부른 값이 100% 가 아니었다

사장님 지적: “가장 강한 적용값에서 이목구비가 완전히 바뀌어야 했는데 직전과 똑같다.”

라벨이 거짓말이었다. LoRA 가중치만 1.0 이었고 나머지가 전부 반대편에서 최대로 버티고 있었다:

항목당시 값얼굴에 미치는 영향
denoise0.62원본 얼굴 38% 가 항상 남는다. 구조적으로 완전히 못 바꾼다
작가 태그6종 ON이미 이목구비 화풍을 지시한다
748cmSDXL0.5또 다른 화풍 LoRA
원화 얼굴 서술12개눈·속눈썹·입술을 못 박는다

경합 요소를 같이 낮추는 사다리로 바꾸니 100% 에서 실제로 완전히 바뀌었다.

🔴 face 블록은 골격과 묘사로 갈라야 한다

두 부분의 운명이 정반대다:

  • 골격(턱선·얼굴형·광대·코) — 남긴다. 빼고 denoise 를 높이면 얼굴형이 제멋대로 재창작돼 윤곽이 어색해진다
  • 묘사(눈 크기·속눈썹·눈썹·입술) — 뺀다. 그게 LoRA 가 결정할 자리인데, 남겨두면 원화 지시가 LoRA 와 정면으로 다툰다

scripts/art/face-prompts.ts--face-mode structure 가 그 분리다.

가르면서 두 번 물렸다:

  • 표정 목록을 먼저 보면 thick lively eyelashes 가 ‘lively’ 때문에 살아남는다 — 묘사 판정이 먼저다
  • 눈 색을 통째로 버리면 안 된다. large round teal eyes 를 지웠더니 여섯 중 넷이 눈 색을 잃었다

🔴 남성화 요인은 눈이 아니라 골격이었다

사장님 지적: “무슨 악역 살인마 같다 · 너무 남캐 같다”(세이·미르).

처음엔 눈만 고쳤고 그건 얕은 진단이었다. 진짜 범인은 high nose bridge(높은 콧대) · angular jawline(각진 턱) · high cheekbones(광대) · straight thin eyebrows(직선 눈썹) 네 개가 겹친 것이다. 애니메 문법에서 이 조합은 남성으로 읽히고, cold stare 가 붙으면 적의가 된다.

근거는 레퍼런스 집계다. 얼굴 크롭 311장을 WD14 로 세니:

레퍼런스당시 우리 프롬프트
턱선·콧대·광대·얼굴형 태그0건셋 다 있었다
blush61%없었다
hair between eyes74%없었다
closed mouth72%없었다

이 화풍의 미형은 구조를 지정해서가 아니라 안 지정해서 나온다. 차가운 성격은 골격이 아니라 표정으로 낸다 — 레퍼런스도 그렇게 한다.

⚠️ check-invariants §9 가 이 작업을 두 번 막았고 둘 다 맞았다(눈매 지정 누락, 세이·미르 63% 겹침).

적용 방법 — 좁은 크롭이 답이다

넓은 크롭에 걸고 얼굴만 오려붙이는 방식(masked)을 먼저 시도했고 실패했다. 타원을 조여도 다시 그려진 머리카락이 어깨까지 덮어 손이 사라졌다. 크롭을 좁히면 손·의상이 아예 모델에 안 들어간다.

denoise 는 믹스 다이얼이다.

무엇이 바뀌나
0.50눈만. 손·머리·의상·표정 전부 원본
0.65눈이 확실히 커지고 속눈썹이 charx. 구조 유지 — 채택값
0.75화풍이 전반적으로 넘어감. 머리색이 밀리기 시작
0.85~0.95최대 전환. 머리 실루엣·장식이 흔들린다

🔴 하한선을 손으로 다시 적지 마라

최대강도 실험 중에 얼굴 패스의 네거티브를 짧게 새로 적었는데, 그러면서 CONTENT_FLOOR_NEGATIVE(cum·bodily fluids·stain on body)가 통째로 빠졌다. 결과가 엘라 얼굴의 흰 방울 자국이었고, 사장님이 그걸 짚었다.

하한선은 buildNegative() 에서 온다. 얼굴 패스도 예외가 아니다.

소품 환각 — 여백을 채우는 것이지 손이 비어서가 아니다

전신 컷에서 삼각대·카메라·의자가 옆에 선다. CLEAN_BG_NEGATIVEcamera·tripod이미 있는데도 나온다 — 가중치 없는 부정이 지는 것이다.

  • 가중치를 실으니((tripod:1.6) 등) 리안·미르는 잡혔다
  • 엘라는 안 잡힌다. 네 시드를 뽑아 네 번 다 소품이 붙었다

엘라는 Caster 라 손에 든 것이 없어서 여백이 가장 크다. 리안(나이프)·세이(소총)·하루(방패)에서는 한 번도 안 났다. 그래서 지금은 후처리로 지운다 — 흰 배경에서 인물과 분리된 덩어리를 연결 요소로 제거한다(declutter.py).

⚠️ 그 후처리에서도 두 번 물렸다:

  • 크기로 살리면 안 된다 — 카메라 스탠드가 문턱을 통과해 하나도 안 지워졌다. 판정은 인접성으로만
  • 잉크 문턱을 60 으로 두면 창백한 얼굴이 배경으로 분류돼 통째로 지워진다. 22 로 낮추고 덩어리 안의 구멍을 메운다

🔴 근본 해결은 엘라에게 촉매를 쥐어주는 것이다(지팡이·마법봉 같은 것). 사장님이 그 방향을 말했고, 결정은 사장님 몫이라 아직 안 넣었다.

만드는 순서

원화 생성(gen.ts) → 소품 제거(declutter) → 얼굴 패스(LoRA, dn 0.65) → webp

프롬프트 정본은 scripts/art/profiles.ts, 얼굴 패스용 앵커는 scripts/art/face-prompts.ts.

아직 안 된 것

  • 트리거 토큰이 안 붙었다. gsvface 를 넣든 빼든 결과가 거의 같다. 가중치로 적용하므로 실사용에는 무해하지만 “트리거가 작동한다” 고는 말할 수 없다
  • 미르 머리끝에 없던 청록이 조금 들어간다
  • 엘라 촉매(위 참조)