나에 대한 AI 이미지 모델을 직접 훈련하는 방법
(coryzue.com)- 몇 시간 동안 나만의 AI 이미지 모델을 훈련시켜, 직접 찍은 것 같은 사진을 만들어보려는 프로젝트를 시도해봤음
- 예: "슈퍼맨" 분장을 한 내 사진을 생성
- 시도한 이유: 재미있을 것 같았고, 아이들이 함께 놀기에 좋으며, 커스텀 모델/심화 AI 부분을 좀 더 배울 수 있음
- 12~18개월 전에는 이 작업이 상당히 복잡했으나, 이제는 매우 간단해졌음
- 2시간 이내에 모델을 만들어 원하는 이미지를 얻었으며, 핵심은 올바른 도구를 빠르게 파악하는 것이었음
모델/훈련 패턴 선택
- 필요한 요소
- 기반 모델(base model)
- 훈련/파인튜닝 기법
- 훈련 데이터셋(자신의 사진 몇 장 등)
- 많은 AI들이 Stable Diffusion을 권장하지만, Pieter Levels가 사용한 Flux 모델이 더 나은 성능을 낸다고 하여 Flux를 선택
- 완전히 최신 SOTA 모델은 아니지만 충분히 좋음
- 훈련 기법으로는 LoRA(Low-Rank Adaptation)를 사용
- 모델 전체를 재학습하지 않고, 특정 "매직 단어"에 연결된 부분만 훈련함
- 예: "czue"라는 흔치 않은 단어를 모델에 학습시켜, 프롬프트에서 그 단어를 사용하면 해당 데이터셋의 특징을 반영함
훈련 세트 생성
- 학습 대상의 사진 여러 장(10~15장 정도)을 준비해야 함
- 표정, 배경, 조명, 각도 등이 다양할수록 좋음
- 한 사진에 한 사람만 있는 것이 바람직함
- 훈련 시에는 텍스트 설명이 필요하며, 여기에 매직 단어가 포함되어야 함
- 예: "a photo of czue on the beach, wearing a blue shirt"
- 하지만 최근 도구들은 자동으로 이미지 캡션을 생성해주므로, 직접 설명을 입력하지 않아도 됨
모델 훈련
- 처음에는 로컬에서 훈련할 생각이었지만, GPU와 RAM이 부족해 어려움
- GPU 클라우드 서버에서 직접 코드를 돌릴 수도 있으나, 결국 Replicate를 사용했음
- GPU 임대 서비스이며, 이미 만들어진 레시피를 바로 사용할 수 있음
- 이번 사례에서는 ostris/flux-dev-lora-trainer 레시피를 사용함
- Replicate 계정 생성 후, 청구 정보를 설정해야 함
- 주요 파라미터
- input_images: 훈련 사진(zip)
- trigger_word: 매직 단어 예) "czue"
- hf_repo_id, hf_token: Hugging Face 레포지토리/토큰
- autocaption_prefix: 자동 생성 캡션 앞에 붙일 문구 (예: "A photo of czue,")
Hugging Face에 모델 저장
- Hugging Face는 모델을 저장·공유하는 플랫폼
- Replicate도 학습된 모델을 어딘가에 저장해주지만, Hugging Face에 올리면 다른 툴과 연동하기 쉬움
- 계정 및 모델 생성 후
hf_repo_id
를 전달함- 접근 토큰은 settings/tokens에서 발급 가능함
- 학습이 끝나면 "lora.safetensors"라는 큰 파일(약 180MB)이 Hugging Face에 업로드됨
모델로 이미지 생성
- 학습이 끝나면, 모델에 텍스트를 입력해 이미지를 만드는 추론(inference) 과정을 진행함
- 로컬에서 직접 해볼 수도 있으나, 다시 Replicate를 활용했음
-
lucataco/flux-dev-lora에 hf_lora 필드만 설정하면 됨
- public Hugging Face 레포지토리 ID 혹은 Replicate에 업로드된 학습 모델 링크
-
lucataco/flux-dev-lora에 hf_lora 필드만 설정하면 됨
- 예: "a photo of czue surfing"을 입력하면 다음처럼 서핑하는 본인 이미지를 얻음
프로그래밍 방식으로 모델 실행
- 다양한 프롬프트를 시도하고 결과를 자동 저장하려면 API 호출 방식이 편리함
- 아래 Python 스크립트를 예시로 작성했음 (전체 코드는 Github에 있음)
# /// script # requires-python = ">=3.12" # dependencies = [ # "replicate", # ] # /// import argparse import os import re import replicate import uuid DEFAULT_MODEL = "czue/me-v1" DEFAULT_COUNT = 1 def get_input(prompt, model=DEFAULT_MODEL, count=DEFAULT_COUNT): return { "prompt": prompt, "hf_lora": model, "num_outputs": count } def main(): parser = argparse.ArgumentParser() parser.add_argument("prompt", help="Prompt for the photo") parser.add_argument("--model", default=DEFAULT_MODEL, help="Model to use (default: %(default)s)") parser.add_argument("--count", default=DEFAULT_COUNT, help="Number of photos to generate (default: %(default)s)", type=int) args = parser.parse_args() input = get_input(args.prompt, args.model, args.count) output = replicate.run( "lucataco/flux-dev-lora:091495765fa5ef2725a175a57b276ec30dc9d39c22d30410f2ede68a3eab66b3", input=input ) output_dir = "output" os.makedirs(output_dir, exist_ok=True) prompt_slug = "-".join(args.prompt.split(" ")[-3:]) prompt_slug = re.sub(r'[^a-zA-Z0-9\-]', '', prompt_slug).lower() for index, item in enumerate(output): file_id = uuid.uuid4().hex[:5] output_path = os.path.join(output_dir, f"{prompt_slug}-{file_id}.webp") with open(output_path, "wb") as file: file.write(item.read()) print(f"Saved photo {output_path}") if __name__ == "__main__": main()
- 예시 사용법
uv run main.py "a photo of czue, a 40 year old man, writing a blog post" \ --model="czue/me-v1" \ --count=4
결과
- 모델 성능은 들쑥날쑥함
- 사람 특징을 제법 비슷하게 잡기도 하지만, 가끔은 다른 인물을 만들어내기도 함
- 특정 나이, 성별 등을 추가로 프롬프트에 명시하면 좀 더 정확해짐
- 예를 들어, "a photo of czue, a 40 year old man, writing a blog post"는 비교적 일관된 이미지를 생성함
- 반면 "a photo of czue writing a blog post"는 훨씬 결과가 제각각이었음
- 다른 인물을 함께 넣으면 얼굴이 뒤섞이는 등의 문제가 발생함
- 버락 오바마와 같이 있는 사진을 만들어보려고 했더니, 내 얼굴의 일정 부분이 오바마 쪽에 반영되고 반대도 마찬가지
- 그래도 충분히 재밌고 유용해서 아이들과 함께 다양한 시도를 해볼 수 있었음
비용
- 무료는 아니지만 그렇게 비싸지도 않음
- 나와 아이들을 합쳐 모델 3개를 학습했는데, 각각 ~$2.50 정도였음
- 이미지 생성은 한 장당 약 $0.03 정도라서 30장을 생성해도 $1 가량임
- 전체 실험으로 $10 이하를 썼으며, 생각보다 비용 부담이 적어 만족스러웠음
- AI 모델 훈련과 이미지 생성에 관심 있다면, 생각보다 쉽게 시도할 수 있으니 도전해볼 만함
재미삼아 따라해봤는데 정말 쉽군요.
(https://www.youtube.com/watch?v=sNpQ9ULDMoo)
이것저것 만들어보며 한참 웃었습니다...
결국 우리가 죽기 전에 나 처럼 훈련된 모델을 네트워크에 업로드하고 죽으려 하지 않을까요? 생존 본능처럼... 그게 '나'는 아니지만.
기술을 보니 소설 하나가 떠올라 소개합니다. 이유리 소설가가 낸 소설집 비숫방울 퐁에 실린 '크로노스'라는 단편인데요. 사람을, 그러니까 자신의 데이터를 저장하고 학습하는 ai 를 다룬 내용입니다. 저 댓글의 고양이처럼요. 치매에 걸린 엄마가 증상이 더 심해지기 전에 그걸 써요. 그리고 자식들은 갈등하죠. 위로받기도 하고 죄책감도 느끼고. 위 기술에, 또 저 고양이의 이야기에 관심 있는 분이라면, 한번 읽어보세요.
해커뉴스 댓글 하나가 눈길을 끄네요
- 내가 사랑하는 죽은 고양이를 위해 이렇게 해봤음. 결과가 마음에 들긴 했는데, 어느 순간 갑자기 내가 하고 있는 일에 대해 소름이 돋았음
- 큰 비즈니스가 될것 같음. 나는 아마도 수십만 통의 이메일, 문자, 채팅 등을 보냈을 텐데 사랑하는 사람의 커뮤니케이션 코퍼스를 학습시켜 그들이 세상을 떠난 후에도 '그들'과 채팅할 수 있도록 하는 것은 충분히 가능한 일
- 아버지가 돌아가신 후 아버지의 목소리로 이 작업을 했고, LLM을 지원하는 비서와 대화할 수 있는 기능을 설정해 아버지의 목소리와 방식으로 응답하도록 했음. 매우 이상하게 대처하고 슬퍼하는 시기였고, 결국 내가 하는 일에 대해 정말 이상하다는 생각이 들었음
- 이거 블랙 미러의 "Be Right Back" 에피소드" 와 비슷함
정보성 댓글도 하나
- Flux의 경우, 텍스트 인코더에 훨씬 더 많은 기능이 있으며 더 의미 있고 포괄적인 문장으로 프롬프트할 수 있음
- 따라서 Stable Diffusion에서 볼 수 있었던 기존의 쉼표로 구분된 간결한 문구는 줄여도 됨
- 또한 훈련 이미지에도 동일한 작업을 수행해야 함. 모델이 '나'로 기억하지 않기를 바라는 모든 것(하고 있는 일, 입고 있는 옷, 동반한 사람, 액세서리 등)에 캡션을 달면 좋음