사진 10장으로 나만의 AI 캐릭터·프로필 이미지 만들기
내 사진 10장으로 FLUX.1 dev + LoRA 학습 후 원하는 스타일의 캐릭터·프로필 이미지 무한 생성
2026.04.13
[미검증]
📌 0. 시리즈
| 응용편 | 제목 | 난이도 | 핵심 기술 |
|---|---|---|---|
| 응용 1 | 사진 10장으로 AI 캐릭터·프로필 이미지 만들기 | ⭐⭐⭐ | FLUX.1 dev · LoRA · ComfyUI |
| 응용 2 | 내 목소리 AI 클론 — 유튜브 내레이터 자동화 | ⭐⭐⭐ | F5-TTS · Kokoro · Sesame CSM-1B |
| 응용 3 | 상품 이미지 1장 → 15초 광고 영상 자동 생성 | ⭐⭐⭐⭐ | Wan2.2 · HunyuanVideo 1.5 · LTX-2 |
| 응용 4 | 공장 불량 자동 검사 — NG/OK 탐지 + 로봇 좌표 추출 | ⭐⭐~⭐⭐⭐⭐⭐ | YOLOv12 · OpenCV · RealSense |
| 응용 5 | 영상 분위기 분석 → BGM 자동 생성 & 싱크 | ⭐⭐⭐ | MusicGen · AudioCraft · Stable Audio Open |
| 응용 6 | 주제 한 줄 입력으로 유튜브 영상 완성 — AI 콘텐츠 자동화 파이프라인 | ⭐⭐⭐⭐ | LangGraph · CrewAI · AutoGen 0.4 |
| 응용 7 | 사진 보고 글 쓰는 AI — Vision LLM 상세페이지 자동 작성 | ⭐⭐⭐⭐ | Qwen2.5-VL · InternVL3 · LLaVA-Next |
| 응용 8 | 내 PDF 문서를 AI가 읽는다 — 사내 지식 RAG 챗봇 구축 | ⭐⭐⭐ | LlamaIndex · ChromaDB · Qdrant |
📌 1. 들어가며
이 포스트에서 만들 것
이 포스트를 끝까지 따라하면 내 얼굴 사진 10~30장만으로 아래 결과물을 만들 수 있습니다.
입력: 내 셀카 20장
↓
LoRA 학습 (~30분)
↓
출력:
├─ 애니메이션 캐릭터 스타일 프로필 이미지
├─ 유화 스타일 초상화
├─ 사이버펑크 캐릭터
└─ 버튜버용 일러스트 이미지
왜 LoRA인가 — DreamBooth · Full Fine-tuning과 차이
| 방식 | VRAM | 학습 시간 | 저장 용량 | 품질 |
|---|---|---|---|---|
| Full Fine-tuning | 40GB+ | 수 시간 | 수 GB | ⭐⭐⭐⭐⭐ |
| DreamBooth | 16GB+ | 30~60분 | 2~4GB | ⭐⭐⭐⭐ |
| LoRA | 8GB+ | 10~30분 | 10~150MB | ⭐⭐⭐⭐ |
💡 LoRA (Low-Rank Adaptation) 는 모델 전체를 바꾸지 않고 작은 어댑터 레이어만 학습합니다. 원본 모델 가중치는 고정한 채, 변화량(ΔW)만 저장하므로 파일 크기가 극도로 작고 VRAM 소모도 적습니다. 결과물 품질은 Full Fine-tuning과 거의 동일하기 때문에 개인 프로젝트에서는 사실상 표준 방식입니다.
📌 2. 환경 준비
2-1. 하드웨어 요구사항
최소 사양:
GPU VRAM: 8GB (RTX 3070 / 4060 등)
RAM: 16GB
저장공간: 50GB 이상 (모델 가중치 포함)
권장 사양:
GPU VRAM: 24GB (RTX 3090 / 4090 등)
RAM: 32GB
저장공간: 100GB 이상
VRAM 부족 시 대안:
- gradient_checkpointing 활성화 → VRAM 30~40% 절약
- bf16 혼합 정밀도 학습 사용
- batch_size=1 + gradient_accumulation 으로 보완
2-2. 소프트웨어 설치
Python 환경 세팅:
bash# Python 3.10 가상환경 생성
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows PowerShell
# 핵심 라이브러리 설치
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
pip install diffusers transformers peft accelerate
pip install bitsandbytes xformers
pip install Pillow datasets
ComfyUI 설치:
bashgit clone https://github.com/comfyanonymous/ComfyUI
cd ComfyUI
pip install -r requirements.txt
ComfyUI 필수 노드 설치 (ComfyUI-Manager로 설치):
- ComfyUI-FLUX-LoRA
- ComfyUI_Kontext
- ComfyUI-WD14-Tagger ← 캡션 자동 생성용
2-3. FLUX.1 dev 모델 다운로드
python# HuggingFace CLI 로그인
pip install huggingface_hub
huggingface-cli login
# → HuggingFace 계정 토큰 입력 (hf_xxxx)
# FLUX.1 dev 다운로드 (약 23GB)
from huggingface_hub import snapshot_download
snapshot_download(
repo_id="black-forest-labs/FLUX.1-dev",
local_dir="./models/flux1-dev",
ignore_patterns=["*.md", "*.txt"]
)
⚠️ FLUX.1 dev는 비상업용 라이선스입니다. 상업적 사용 시 FLUX.1 schnell (Apache 2.0) 을 사용하세요.
📌 3. 학습 데이터 준비
3-1. 사진 촬영 가이드
수량 기준:
최소: 10장 → 학습은 되지만 다양성 부족
권장: 20~30장 → 품질과 효율의 최적 균형
과잉: 50장+ → 효과 미미, 학습 시간만 증가
각도 분배 (20장 기준):
정면 바라보기: 6장 (30%)
좌측 45도: 4장 (20%)
우측 45도: 4장 (20%)
측면 (좌/우): 4장 (20%)
위에서 내려보기: 2장 (10%)
촬영 시 주의사항:
✅ 해야 할 것:
- 자연광 또는 소프트박스 조명 사용
- 다양한 표정 (미소, 무표정, 진지한 표정)
- 다양한 의상
- 배경은 단색 또는 아웃포커싱
❌ 하지 말 것:
- 선글라스, 마스크 착용 사진 포함
- 여러 사람이 함께 찍힌 사진
- 얼굴이 너무 작게 나온 사진 (전신샷)
- 흐릿하거나 역광 사진
3-2. 이미지 전처리
pythonfrom PIL import Image
import os
def preprocess_images(input_dir, output_dir, size=1024):
os.makedirs(output_dir, exist_ok=True)
for filename in os.listdir(input_dir):
if filename.lower().endswith(('.jpg', '.jpeg', '.png')):
img = Image.open(os.path.join(input_dir, filename))
# 정사각형으로 중앙 크롭
w, h = img.size
min_side = min(w, h)
left = (w - min_side) // 2
top = (h - min_side) // 2
img = img.crop((left, top, left + min_side, top + min_side))
# 리사이즈
img = img.resize((size, size), Image.LANCZOS)
# RGB 변환 (PNG 투명도 제거)
img = img.convert("RGB")
save_path = os.path.join(output_dir, filename.replace('.jpeg', '.jpg'))
img.save(save_path, quality=95)
print(f"처리 완료: {filename}")
preprocess_images("./raw_photos", "./dataset/images")
3-3. 캡션 작성법
트리거 워드 개념:
트리거 워드는 모델이 "이 사람이 나다"라고 인식하는 고유 키워드입니다. sks person 처럼 모델이 기존에 학습하지 않은 단어를 사용해야 충돌이 없습니다.
나쁜 예: "a photo of john" → john이라는 개념이 이미 모델에 존재
좋은 예: "a photo of sks person" → sks는 모델이 처음 보는 단어
캡션 파일 수동 작성 예시 (이미지당 .txt 파일 1개):
photo001.jpg → photo001.txt 내용:
"a photo of sks person, smiling, looking at camera, natural lighting"
photo002.jpg → photo002.txt 내용:
"a photo of sks person, serious expression, side view, outdoor background"
WD Tagger로 캡션 자동 생성:
python# ComfyUI WD14 Tagger 노드 사용 또는 아래 코드 활용
pip install wd14-tagger
from wd14_tagger import WD14Tagger
tagger = WD14Tagger()
tags = tagger.tag("./dataset/images/photo001.jpg", threshold=0.35)
# 자동 생성된 태그 앞에 트리거 워드 추가
trigger = "sks person"
caption = f"a photo of {trigger}, {', '.join(tags)}"
print(caption)
# 출력: "a photo of sks person, 1girl, solo, long hair, smile, ..."
📌 4. LoRA 학습
4-1. 학습 파라미터 설명
python# 핵심 파라미터 의미
learning_rate = 1e-4
# 한 스텝에서 가중치를 얼마나 크게 바꿀지
# 너무 크면 → 과적합 / 너무 작으면 → 학습 안됨
# FLUX LoRA 권장값: 1e-4 ~ 4e-4
rank = 16
# LoRA의 어댑터 행렬 크기
# 클수록 → 표현력↑ VRAM↑ 파일크기↑
# 작을수록 → 빠른학습 일반화↑
# 권장: 8 (가벼움) / 16 (균형) / 32 (고품질)
alpha = 16
# LoRA 학습의 스케일 조정값
# 보통 rank와 같은 값으로 설정
# alpha / rank 비율이 실제 학습 강도를 결정
# steps 수 기준
# 이미지 20장 기준:
# 500 steps → 학습 부족, 특징 희미
# 1000 steps → 균형 잡힌 결과 (권장)
# 2000 steps → 고품질, 과적합 주의
4-2. 학습 실행 코드
학습 설정 파일 (train_config.yaml):
yamlmodel:
base_model: "./models/flux1-dev"
model_type: "flux"
dataset:
image_dir: "./dataset/images"
caption_dir: "./dataset/images" # 같은 폴더에 txt 파일
resolution: 1024
batch_size: 1
lora:
rank: 16
alpha: 16
target_modules:
- "to_q"
- "to_k"
- "to_v"
- "to_out.0"
training:
learning_rate: 1e-4
max_train_steps: 1000
lr_scheduler: "cosine"
warmup_steps: 100
gradient_checkpointing: true
mixed_precision: "bf16"
save_every_n_steps: 250
output:
save_dir: "./output/lora"
lora_name: "my_character_v1"
학습 실행:
pythonfrom diffusers import FluxPipeline
from peft import LoraConfig, get_peft_model
from accelerate import Accelerator
from torch.utils.data import DataLoader, Dataset
from PIL import Image
import torch, os
class CharacterDataset(Dataset):
def __init__(self, image_dir, caption_dir, size=1024):
self.image_dir = image_dir
self.caption_dir = caption_dir
self.size = size
self.images = [f for f in os.listdir(image_dir) if f.endswith('.jpg')]
def __len__(self):
return len(self.images)
def __getitem__(self, idx):
img_name = self.images[idx]
img_path = os.path.join(self.image_dir, img_name)
txt_path = os.path.join(self.caption_dir, img_name.replace('.jpg', '.txt'))
image = Image.open(img_path).convert("RGB").resize((self.size, self.size))
caption = open(txt_path).read().strip()
return {"image": image, "caption": caption}
# LoRA 설정 적용
lora_config = LoraConfig(
r=16,
lora_alpha=16,
target_modules=["to_q", "to_k", "to_v", "to_out.0"],
lora_dropout=0.1,
bias="none"
)
# 학습 루프 (간략 버전)
accelerator = Accelerator(mixed_precision="bf16")
pipeline = FluxPipeline.from_pretrained("./models/flux1-dev", torch_dtype=torch.bfloat16)
model = get_peft_model(pipeline.transformer, lora_config)
dataset = CharacterDataset("./dataset/images", "./dataset/images")
dataloader = DataLoader(dataset, batch_size=1, shuffle=True)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
model, optimizer, dataloader = accelerator.prepare(model, optimizer, dataloader)
for step, batch in enumerate(dataloader):
if step >= 1000:
break
optimizer.zero_grad()
# ... 노이즈 예측 학습 ...
accelerator.backward(loss)
optimizer.step()
if step % 250 == 0:
pipeline.transformer.save_pretrained(f"./output/lora/checkpoint-{step}")
print(f"Step {step} 저장 완료")
💡 더 쉬운 방법: kohya-ss/sd-scripts GUI 툴인 Kohya GUI를 사용하면 위 코드 없이도 설정값만 입력해서 학습할 수 있습니다. FLUX LoRA를 지원하며 초보자에게 권장합니다.
4-3. 학습 중 VRAM 관리 팁
python# VRAM 절약 설정 모음
# 1. gradient checkpointing (VRAM ~35% 절약, 속도 20% 감소)
model.enable_gradient_checkpointing()
# 2. 8bit Adam 옵티마이저 (VRAM ~20% 절약)
from bitsandbytes.optim import AdamW8bit
optimizer = AdamW8bit(model.parameters(), lr=1e-4)
# 3. xformers attention (VRAM ~15% 절약)
pipeline.enable_xformers_memory_efficient_attention()
# 4. 실시간 VRAM 모니터링
import subprocess
result = subprocess.run(
['nvidia-smi', '--query-gpu=memory.used,memory.total',
'--format=csv,noheader,nounits'],
capture_output=True, text=True
)
used, total = result.stdout.strip().split(', ')
print(f"VRAM: {used}MB / {total}MB ({int(used)/int(total)*100:.1f}%)")
📌 5. 이미지 생성
5-1. ComfyUI에서 LoRA 적용법
ComfyUI 기본 FLUX 워크플로우에서:
1. Load Checkpoint 노드 → FLUX.1 dev 선택
2. [추가] Load LoRA 노드 연결
- lora_name: my_character_v1.safetensors
- strength_model: 0.8 ← 0.6~1.0 사이 조정
- strength_clip: 0.8
3. CLIP Text Encode 노드에 프롬프트 입력
4. KSampler → VAE Decode → 이미지 출력
LoRA strength 가이드:
0.4 ~ 0.6 → 닮음 낮음, 스타일 변환 자연스러움
0.7 ~ 0.8 → 균형 잡힌 닮음 (권장)
0.9 ~ 1.0 → 닮음 높음, 다양성 낮아짐
1.0 초과 → 과적합 증상 (얼굴 왜곡 가능)
5-2. 프롬프트 작성법
기본 구조:
[트리거 워드], [스타일], [품질 키워드], [구도], [조명]
스타일별 프롬프트 예시:
# 애니메이션 캐릭터
"a photo of sks person, anime style, 2D illustration,
vibrant colors, clean lineart, studio ghibli inspired,
masterpiece, best quality"
# 유화 초상화
"a photo of sks person, oil painting portrait,
renaissance style, dramatic lighting, textured brushstrokes,
museum quality, 8k resolution"
# 사이버펑크
"a photo of sks person, cyberpunk style, neon lights,
futuristic city background, holographic elements,
cinematic lighting, ultra detailed"
# 버튜버 일러스트
"a photo of sks person, vtuber avatar style,
cute anime illustration, pastel colors,
big eyes, chibi proportions, digital art"
네거티브 프롬프트 (FLUX는 선택사항):
"blurry, low quality, deformed face, extra fingers,
bad anatomy, watermark, text, signature"
5-3. FLUX.1 Kontext dev로 이미지 편집
Kontext는 레퍼런스 이미지를 보고 동일한 사람을 다른 상황에서 그려주는 기능입니다.
pythonfrom diffusers import FluxKontextPipeline
import torch
from PIL import Image
# Kontext dev 로드
pipe = FluxKontextPipeline.from_pretrained(
"black-forest-labs/FLUX.1-Kontext-dev",
torch_dtype=torch.bfloat16
).to("cuda")
# LoRA 적용
pipe.load_lora_weights("./output/lora/my_character_v1.safetensors")
# 레퍼런스 이미지 불러오기
reference_image = Image.open("./best_result.jpg")
# 이미지 편집 (배경·옷만 바꾸고 얼굴 유지)
result = pipe(
image=reference_image,
prompt="sks person wearing a business suit, office background, professional photo",
num_inference_steps=28,
guidance_scale=2.5,
).images[0]
result.save("./output/edited_result.jpg")
Kontext 활용 시나리오:
원본 레퍼런스 이미지 1장
↓
├─ 배경만 변경: "sks person, same pose, beach background"
├─ 의상만 변경: "sks person, wearing kimono"
├─ 계절 변경: "sks person, winter coat, snowing"
└─ 연령 변경: "sks person as an elderly person"
📌 6. 결과 확인 & 트러블슈팅
얼굴 유사도가 낮을 때
원인 1: 학습 steps 부족
→ max_train_steps 500→1000으로 증가
원인 2: 트리거 워드 누락
→ 프롬프트에 "sks person" 반드시 포함 확인
원인 3: LoRA strength가 너무 낮음
→ ComfyUI Load LoRA 노드에서 0.8→0.9로 증가
원인 4: 학습 데이터 품질 문제
→ 흐릿하거나 얼굴이 작은 사진 제거 후 재학습
과적합(Overfitting) 증상과 해결법
증상:
- 트리거 워드 없이도 내 얼굴이 나옴
- 다른 스타일 프롬프트 입력해도 항상 비슷한 결과
- 얼굴 이외 부분이 학습 사진과 동일하게 고정됨
해결법:
1. steps 줄이기: 2000→1000
2. learning_rate 낮추기: 1e-4 → 5e-5
3. rank 줄이기: 32 → 16
4. 중간 체크포인트 (500 steps) 사용해보기
VRAM OOM (Out of Memory) 오류 해결
python# OOM 발생 시 순서대로 적용
# 1단계: 해상도 낮추기
resolution: 1024 → 512
# 2단계: gradient checkpointing 활성화
model.enable_gradient_checkpointing()
# 3단계: 8bit 옵티마이저
from bitsandbytes.optim import AdamW8bit
# 4단계: xformers 활성화
pipeline.enable_xformers_memory_efficient_attention()
# 5단계: 캐시 비우기
import torch, gc
gc.collect()
torch.cuda.empty_cache()
# 6단계 (최후): batch_size=1 + gradient_accumulation_steps=4
# → 실질적으로 batch_size=4 효과를 VRAM 부담 없이 구현
✅ 완성 체크리스트
- 사진 20~30장 준비 및 전처리 완료
- 캡션 파일 생성 (트리거 워드 포함)
- LoRA 학습 1000 steps 완료
- ComfyUI에서 LoRA 로드 후 이미지 생성 확인
- 다양한 스타일 프롬프트로 결과물 테스트
- FLUX.1 Kontext dev로 이미지 편집 적용