프로젝트

[프로젝트] CivitAI Safetensors를 활용하여 생성 이미지를 만들어보자

rahites 2024. 9. 28. 13:34

CivitAI를 로컬로 사용해보고 싶다... 는 생각에서 시작한 나만의 프로젝트..!!


 

1. CivitAI 모델 소개

CivitAI

https://civitai.com/

 

Civitai: The Home of Open-Source Generative AI

Explore thousands of high-quality Stable Diffusion & Flux models, share your AI-generated art, and engage with a vibrant community of creators

civitai.com

 

CivitAI는 대표적인 Stable Diffusion 기반의 AI 이미지 생성 플랫폼이다. 페이지를 둘러보다 유저들이 업로드해둔 생성 이미지들 뿐만 아니라 Checkpoint, LoRA, Embedding 등 정말 많은 모델들이 공개되어 있는 것을 확인할 수 있다.

그 중 대표적으로 사용되는 모델들은 다음과 같다.

 

  • Checkpoint : 해당 모델 전체를 특정 데이터에 대해 학습하여 나온 모델(일반적으로 인공지능 모델을 학습할 때 저장되는 형태라고 이해하면 편하다. ex. 100epoch에 저장된 xxx.checkpoint)
    • checkpoint에는 다시 trained와 merge 모델이 존재하는데 trained는 하나의 모델로 학습한 것, merge는 학습한 여러 모델을 합친 것을 의미한다(아마 가중치를 가중합해주는 것을 말하는 것으로 보임).
  • LoRA(Low Rank Adaptation) : 기존 LLM을 효율적으로 학습하기 위해 제안된 방법론으로 생성 모델에서는 전체 모델을 학습하기 어렵기 때문에 기존 모델의 가중치를 Low Rank로 분해하여 모델의 표현력을 유지하고 효율성을 높인 방법.
    • 생성 모델에서 LoRA는 적은 파라미터를 적용시키는 것 만으로 LoRA 모델이 가지는 정보가 전체 모델에 영향을 미치기 쉽다고 생각하면 된다.
    • 실제로 checkpoint 모델은 몇GB정도 하는 반면 LoRA 모델은 몇MB 정도의 크기를 가진다. 관련 정보를 찾다 Reddit에서 checkpoint는 이미지의 그림체를 만들고 LoRA는 캐릭터를 만들어 준다는 글을 봤다. 직접 모델을 돌려보다 보니 반은 맞고 반은 틀린 말 같긴 한데, 쉽게 이해하기 위해서는 이 문장이 제법 말은 된다고 생각한다.(LoRA기법이 Dreambooth에서 나와 캐릭터 위주로 학습되기 때문에 이런 말이 나온 것 같지만 실제 Inference는 기대와 다르게 나올때가 많다…)
  • Embedding : 데이터를 저차원의 벡터 공간으로 변환한 것. 생성 모델에서는 Text Inversion 개념으로 사용되어 특정 Prompt에 대한 Embedding을 학습한다.
    • 주로 하나의 Trigger Word에 관련된 여러 Text Embedding을 학습하고 이를 positive/negative prompt에 넣어 사용하는 방식으로 사용된다.
  • VAE : 우리가 아는 그 VAE 모델이 맞다. StableDiffusion에서 VAE는 이미지의 품질을 보정하는 용도로 주로 사용이 되는데, 여기부터는 추측이지만 아마 기존 배포되는 checkpoint에 VAE란이 비어있어 이미지의 색감이나 풍부함을 더해줄 수 있는 학습된 VAE 가중치를 따로 배포하는 것으로 생각된다.
    • CivitAI를 돌아다니다 종종 보이는 Baked VAE는 VAE가 checkpoint 안에 포함되어 있다는 것을 의미한다.

 

많은 모델들이 safetensors 파일 형태로 올라와 있고 safetensors란 huggingface에서 제작한 tensor를 저장하는 format이다(속도가 매우 빠르다고 한다).

https://huggingface.co/docs/safetensors/index

 

Safetensors

 

huggingface.co

 

2. 모델 사용하기

자 그럼 올라와 있는 이 모델들을 어떻게 사용할 수 있을까?

처음에는 나도 단순히 모델 safetensor를 받아 inference를 하는 방법을 찾아 보다 많이 헤멨는데 여러 구글 블로그나 유튜브 예시를 보면 Automatic 1111같이 WebUI를 사용해서 모델을 학습 및 추론하는 예시가 나와있지만, 그래도 인공지능 공부하는 사람으로서 만들어진 UI말고 내가 짠 코드만으로 생성된 결과를 볼 수 있게 만들어보고 싶었다…

https://github.com/AUTOMATIC1111/stable-diffusion-webui

 

GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI

Stable Diffusion web UI. Contribute to AUTOMATIC1111/stable-diffusion-webui development by creating an account on GitHub.

github.com

 

결국 활용가능한 패키지를 찾은 것이 Diffusers 였다.

 

Diffusers는 HuggingFace에서 제공하는 Diffusion 모델을 쉽게 사용할 수 있도록 도와주는 라이브러리로 다양한 모델의 학습 및 추론, 파이프라인 기능을 제공한다.

 

이번에는 Diffuseres에 존재하는 파이프라인(Pipeline)을 사용해 보았다. 우선 Diffusers 파이프라인을 사용하기 전에 Civitai에 올라와있는 모델들의 base model을 확인해야 한다.

 

위 사진에서 볼 수 있듯이 civitai에 올라와 있는 모델들은 모두 학습에 사용한 Base 모델을 표기해두었다. 따라서 내가 이 모델로 학습한 safetensor를 사용하기 위해서는 해당 모델을 load해서 사용해 주어야 하는데 앞서 설명한 모델 타입(checkpoint, lora …)에 따라 각기 다른 사용법을 가진다.

 

우선 전체 모델의 뼈대가 되는 checkpoint 모델은 크게 3가지로 불러올 수 있다.

 

1. Huggingface에 올라와 있는 모델을 사용하는 경우

Diffusers 라이브러리에는 기본 StableDffusion과 Diffusion에 대한 Pipeline을 제공한다. 따라서 HuggingFace hub에 올라와 있는 모델을 다운받아 바로 로드할 수 있으며, 대표적으로 사용하는 모델은 다음과 같다.

  • StableDiffusion1.5 : runwayml/stable-diffusion-v1-5
  • SDXL : stabilityai/stable-diffusion-xl-base-1.0
from diffusers import StableDiffusionPipeline, DiffusionPipeline
model_id = "runwayml/stable-diffusion-v1-5"
pipeline = DiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16, use_safetensors = True).to("cuda")

 

2. Huggingface에 올라와 있는 모델을 로컬로 다운 받아 사용하는 경우

모델을 로드할 때마다 Huggingface hub에 접속한다면, 인터넷이 없는 경우나 배포를 해야할 경우 문제가 생길 수 있다. 따라서 Huggingface-cli를 이용하여 미리 모델을 내가 원하는 경로에 다운받아 놓은 뒤 사용할 수 있다.

 

Huggingface-cli를 사용하여 모델을 로컬로 다운받는 방법은 다음과 같다.

huggingface-cli download <repository> —local-dir

사용 방법은 크게 어렵지 않게 로컬로 다운받은 경로를 넣어주면 된다.

from diffusers import StableDiffusionPipeline, DiffusionPipeline
model_id = "./runwayml/stable-diffusion-v1-5"
pipeline = DiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16, use_safetensors = True).to("cuda")

 

3. CivitAI에서 safetensors를 받아 사용하는 경우(다른 모델 공유 사이트에서 safetensors를 받는 경우 포함)

checkpoint를 공유하는 하나의 safetensors에는 해당 base 모델이 가지는 구조의 모든 가중치를 가지고 있다. 따라서 해당 base 모델을 따로 불러올 필요 없이 load_single_file 메소드를 사용해 해당 모델을 불러올 수 있다.

# https://civitai.com/models/36520/ghostmix
pipeline = StableDiffusionPipeline.from_single_file("ghostmix.safetensors").to("cuda")

 

3. LoRA & Embeddings 사용하기

앞서서는 Checkpoint 파일을 활용하여 모델을 Load 하는 방법에 대해 설명하였다. 하지만 우리가 이미지를 생성하다보면 하나의 그림체뿐만이 아니라 조금 더 다양한 형태의, 다양한 스타일이 적용된 이미지를 만들고 싶어진다. 그럴 때 사용하는 것이 LoRA 파일이다. 

 

LoRA 모델은 Checkpoint와 다르게 해당 base 모델이 가지는 구조의 모든 가중치를 가지고 있지 않다. 따라서 해당 base 모델을 먼저 Load 한 뒤 load_lora 메소드를 사용해 Lora 모델을 적용하는 과정을 거쳐야 한다.

 

이 때 다운로드한 LoRA 모델의 base 모델을 확인하여 해당 base 모델을 먼저 Load 해 주어야 한다(SD1.5, SDXL, Pony 등).

 

이 때 꼭 base모델이 아니더라도 해당 모델 구조를 동일하게 가지는 checkpoint위에 LoRA를 적용할 수 있으며 StableDiffusion 구조를 확장한 SDXL는 SD LoRA를 적용할 수 있지만 그 역은 불가능한 특징을 가진다.

(예를 들어 pony 모델은 Base가 SD 1.5이기 때문에 SDXL 기반으로 만들어진 LoRA 파일은 사용할 수없다)

 

적용하는 방법으로는 Pipeline에서 load_lora_weights라는 메소드를 활용하는 것이고 아래와 같이 Weight를 적용할 수 있다.

# https://civitai.com/models/14313/maplestory-style
# https://civitai.com/models/58390/detail-tweaker-lora-lora
pipeline.load_lora_weights(".", weight_name="maplestory.safetensors", adapter_name='maplestory')
pipeline.load_lora_weights(".", weight_name="add_detail.safetensors", adapter_name='Detail Tweaker')

 

위 예시에서는 2가지 LoRA 파일을 적용하였는데 이렇게 되면 처음 적용시키려는 LoRA 파일이 뒤의 LoRA로 인해 덮어쓰기가 되어 버린다. 따라서 한번에 여러 weight를 적용하고 싶다면 set_adapter 메소드를 사용해 그 정도를 조절할 수 있다.

# set_adapter를 사용하지 않고 load_lora_weight를 순서대로 실행하면 wegiht가 덮어 써진다.(0.0, 1.0 효과)
pipeline.set_adapters(['maplestory', 'Detail Tweaker'], adapter_weights=[0.9, 1.2])

 

LoRA는 Prompt에 녹여 lora:filename:multiplier 형태로 사용한다. multiplier는 0~1로 지정하고 filename뒤에 숫자는 seed의 역할을 수행한다.

# Maplestory 그림체를 적용
prompt = '<lora:maplestory20:1>"

 

다음은 Embeddings이다. Embeddings는 주로 캐릭터의 특징을 살리거나 잘 나오지 않는 부분을 보정하는 역할로 많이 사용하며, load_textual_inversion 메소드를 사용한다.

 

이번 실험에서는 고질적인 문제인 손문제을 완화하고 전체적으로 낮은 퀄리티를 방지하도록 학습된 embeddings를 사용하였다.

# https://civitai.com/models/7808
# https://civitai.com/models/16993/badhandv4
pipeline.load_textual_inversion(".", weight_name="easynegative.safetensors", token="easynegative") # SD1.5
pipeline.load_textual_inversion(".", weight_name="badhandv4.pt", token="badhandv4") # SD1.5

 

위와 같이 LoRA 파일과 Embeddings를 사용할 때에는 설명서에 나와있는 Prompt or Negative Prompt Keyword를 꼭 해당 Prompt 위치에 넣어주어야 한다(그렇게 학습이 되었기 때문에).

 

4. Prompt를 넣어 이미지를 생성하기

Prompt의 종류에는 Prompt와 Negative Prompt가 존재한다. Prompt에는 내가 만들고 싶은 이미지에 대한 정보를, Negative Prompt에는 내가 만들고 싶지 않은 이미지에 대한 정보를 넣어주면 된다.

 

일반적으로 많이 사용되는 Prompt들은 구글 검색을 하면 많이 나오기 때문에 해당 프롬프트들을 참고하여 작성해주면 좋다(CivitAI에 유저들이 올린 퀄리티 좋은 이미지들을 보면 어떤 키워드로 만들었는지 볼 수 있다).

 

간단하게 작성해본 Prompt

prompt = "best quality, masterpiece, absurbres, super-resolution, chibi, teacher, class, school, <lora:maplestory20:1>"
negative_prompt = "badhandv4, easynegative, worst quality, watermarks, signature"

 

Prompt 작성 외에도 Pipeline Inference에 들어가는 num_inference_steps, guidance_scale, clip_skip 등 중요한 파라미터 들이 많지만 추후에 더 디테일하게 작성하도록 하겠다.

 

결과

maplestory 그림체로 만든 생성 이미지

 

간단하지만 WebUI 없이 학습된 Safetensors를 사용하여 생성 모델을 Inference 하는 과정을 알아 보았다. 호치민 아저씨가 그려진 Stable Diffusion UI 툴은 기존에 너무 연동이 잘 되도록 만들어졌기 때문에 다른 방식으로 생성할 수 있는 나만의 Framework를 만들어보고 싶어 진행하였고 많은 모델을 한번에 다루지는 못하지만, "어떻게 하면 어떤 결과가 나오는구나~" 하는 걸 느낄 수 있었던 프로젝트였던 것 같다.