일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- 우분투
- 멀티모달
- AI
- 오픈AI
- 메타
- 인공지능
- 다국어 지원
- 이미지 생성
- LLM
- 강화 학습
- ChatGPT
- 생성형AI
- 티스토리챌린지
- 트랜스포머
- PYTHON
- 오블완
- 휴머노이드 로봇
- 실시간 렌더링
- 확산 모델
- AI 기술
- 일론 머스크
- 오픈소스
- OpenAI
- tts
- 딥러닝
- Stable Diffusion
- 강화학습
- 자연어 처리
- XAI
- 감정 표현
- Today
- Total
AI 탐구노트
자신만의 폰트 제작 - 2) MX-Font를 이용해 자신만의 손글씨 만들기 본문
지난 글에서는 자신만의 손글씨 폰트 만들기에 대해 알아 봤습니다. 이제 실제로 만들어 보는 단계가 남았죠. 사실 GAN을 이용해 진행하는 모델들이 일부 있었는데, 제가 기본기가 허술하다보니 어디서 어떻게 접근해야 하는지를 몰라서 시도를 못하고 있던 터였습니다. 이제 AI의 도움을 받아가며 하나씩 진행해 보렵니다.
1. 구현하려는 내용 개요 및 요구사항
이번에 하려는 작업은 다음과 같습니다.
- 목표 : 사용자 맞춤형 손글씨 폰트 자동 생성
- 언어 : 한글, 영어
- 요구 품질 : 적은 수의 샘플로 높은 품질의 폰트 생성
- 기술 : 최신 AI 기술 활용
2. 적용 기술 선정
폰트 제작에 활용할 수 있는 다양한 AI 모델이 있습니다. 대표적인 모델로는 다음과 같은 것들이 있습니다.
- GAN(Generative Adversarial Networks) : 생성자와 판별자로 구성된 모델로, 새로운 글자 이미지를 생성하는 데 유용합니다.
- VAE(Variational Autoencoders) : 데이터의 잠재 공간을 학습하여 새로운 글자 형태를 생성할 수 있습니다.
- Transformer 기반 모델 : 시퀀스 데이터를 처리하는 데 강력한 모델로, 글자의 일관성을 유지하면서 생성할 수 있습니다.
예전에 폰트 제작할 때는 대부분 GAN을 이용한 것들이 가장 많이 사용되었습니다. 나름 결과물의 품질도 괜찮았구요. GAN은 생성자와 판별자가 서로 경쟁하며 고품질의 이미지를 생성할 수 있기 때문에, 섬세한 폰트 디자인에 유리합니다. 또한, GAN은 다양한 스타일의 글자를 생성하는 데 강력한 성능을 보입니다.
그래서 GAN 기반의 Neural Fonts라는 것을 이용하려고 했습니다. 그런데, 문제가 사전 학습 모델의 가중치 파일의 다운로드 링크가 끊어져 있었습니다. 오래전 올라온 모델이었는데 그 사이 관리가 안 된 모양이었습니다. 그렇다고 데이터셋을 가지고 직접 학습을 시키기엔 역량 부족... 아쉽지만 이건 패스 하기로 했습니다.
최근 가장 주목받고 있는 기술은 Few-shot Font Generation이며 찾아본 바로는 다음과 같은 것들이 있었습니다. 참고로 이 둘은 Naver 클로바AI 쪽에서 공개한 기술들입니다.
- DG-Font++ (2024년) : 최신 폰트 생성 기술로, 매우 적은 수의 문자(10~20자 정도)로도 높은 품질의 폰트를 생성 가능
- MX-Font (2023년) : 매우 적은 글자수로 높은 품질의 한글 폰트 생성 가능
DG-Font++은 가장 구미가 당겼지만, 아직 코드가 공개되어 있지 않았습니다. 그래서, 어쩔 수 없이 2023년 클로바AI에서 발표하 MX-Font라는 것을 이용하기로 했습니다.
3. MX-Font 의 개요 및 장점
🔖 MX-Font란?
- Clova AI에서 개발한 최신 폰트 생성 기술
- GAN(Generative Adversarial Networks) 기반의 모델
- 소수의 손글씨 샘플을 입력해 전체 문자집합(예: 한글 11,172자)를 생성
- 한글의 초성, 중성, 종성 구조를 고려해 조합형 폰트를 생성하는데 최적화되어 있음
- 장점 : 적은 데이터로도 일관된 스타일의 폰트를 생성할 수 있음
- 단점 : 복잡한 손글씨 스타일에서는 구조 왜곡이 발생할 수 있으며, 전문적인 환경 설정이 필요함
4. 개발 단계 별 로드맵
1) 데이터 수집 및 처리
- 사용자로부터 손글씨 이미지를 입력 받습니다.
이 부분은 GAN을 이용한 다른 모델을 해 보려고 했을 때 미리 진행한 것이 있어서 그것을 이용합니다.
2) MXFont 학습 및 미세조정
MXFont 사전 학습 모델이 있으면 이를 이용해 사용자 입력 데이터로 Few Shot 학습을 진행합니다. ->공개된 것이 없네요...- 제공되는 코드와 샘플 폰트 등을 이용해 모델 학습을 진행합니다.
- 이후 사용자 입력 데이터를 이용한 Few-shot 학습을 진행합니다.
3) 폰트 생성
- 미학습 문자에 대한 폰트 스타일 자동 확장 생성을 진행하고
- 생성된 문자 이미지들을 이용해 TTF 폰트를 생성합니다.
5. 개발 환경 구성
1️⃣ 개발 진행 환경
- 운영체제 : 우분투 24.04 LTS
- GPU : RTX 3090
- CUDA : 12.4, cuDNN 8
- Python : 3.10 (3.7 이상)
- framework : Pytorch
- 기타 라이브러리 : OpenCV, Pillow 등 이미지 처리 라이브러리
2️⃣ 필수 라이브러리 설치
conda create -n mxfont python=3.10
conda activate mxfont
# PyTorch (CUDA 버전 확인 필수)
conda install pytorch torchvision pytorch-cuda=12.4 -c pytorch -c nvidia
# 기타 필수 패키지
pip install opencv-python pillow matplotlib tqdm fonttools
3️⃣ MX-Font 소스 코드 다운로드 및 설치
git clone https://github.com/clovaai/fewshot-font-generation.git
cd fewshot-font-generation
pip install -r requirements.txt
4️⃣ 데이터셋
1) 데이터셋 준비
데이터셋은 트루타입 글꼴파일(TTF 파일) 또는 이미지 파일 둘 다 가능합니다. (아래 그림 참조) 저는 한글 트루타입 폰트를 이용하는 방식으로 진행했습니다. TTF 파일에서 필요한 데이터를 직접 추출하는 방식이라 훨씬 편하고 간편하기 때문입니다.
훈련용 글꼴 파일과 검증용 글꼴 파일을 별도의 폴더에 넣게 되는데 위의 그림을 보면 *.ttf 파일과 함께 *.txt 파일이 있습니다. 텍스트 파일의 경우, 사용 가능한 문자 목록을 정의하고 있는데 이는 코드에서 제공되는 get_chars_from_ttf.py 프로그램을 이용해서 생성할 수 있습니다. 즉, 학습과 검증에 이용한 TTF 파일만 구해서 폴더에 넣고 위의 코드를 돌리기만 하면 된다는거죠.
$ python get_chars_from_ttf.py {ttf 파일 폴더}
2) 훈련 및 검증 세트 구성
학습과 검증에 사용할 문자 목록를 각각 JSON 파일로 저장하라고 되어 있습니다.
- 학습 : data/chn/train_chars.json(중국어), data/kor/train_chars.json(한국어)
- 검증 : data/chn/val_unseen_chars.json(중국어), data/kor/val_unseen_chars.json(한국어)
기본적으로 해당 폴더에 이미 파일들이 제공되고 있습니다. 저는 이걸 그대로 이용하겠습니다.
3) 분해 정보 준비
문자를 구성하는 구성 요소들의 목록 정보가 JSON 파일 형태로 저장된 것입니다. 소스에서는 이미 분해 규칙과 원시 파일 형식이 다음과 같이 제공되고 있습니다.
- 중국어 : data/chn/decomposition.json, data/chn/primals.json
- 한국어 : data/kor/decomposition.json, data/kor/primals.json
이 부분도 별도로 생성하지 않고 제공되는 것을 그대로 사용하겠습니다.
4) 소스 글꼴 또는 소스 이미지 준비
소스는 제작하고자 하는 사용자의 손글씨 데이터를 의미합니다. 그러므로 이 부분은 글꼴이 아니라 직접 써서 분류한 글자 이미지를 이용해야 할 것 같습니다. 하지만, 일단은 제대로 동작하는지, 작동 여부를 확인하는 것이 필요한 상황이라 샘플로 제공된 폰트를 이용하는 방식으로 진행했습니다. 이게 제대로 나온다면 그 때는 실제 스캔한 파일을 이용하는 방식으로 전환할 예정입니다.
5) 데이터 구성 파일 (cfgs/data/train/custom.yaml) 수정
작업 환경에 맞춰 내용을 다음과 같이 수정합니다.
- 데이터 폴더 : 한국어 데이터이므로 'chn' -> 'kor' 로 변경
- 데이터 로더 설정 :
- batch_size : 6 # 기본 4였음. 좀 더 크게 조정 가능 (GPU 메모리 여유 용량 고려)
- num_worker : 8 # CPU 코어 수에 따라 적절히 설정
- pin_memory : true # GPU 전송 속도 향상
위 내용이 반영된 파일 내용은 다음과 같습니다.
dset: # leave blank
train: # leave blank
data_dir: data_example/kor/ttf
chars: data/kor/train_chars.json
extension: ttf
val:
unseen_chars:
data_dir: data_example/kor/ttf
extension: ttf
n_gen: 20
n_font: 5
chars: data/kor/val_unseen_chars.json
source_path: data/kor/source.ttf
source_ext: ttf
seen_chars:
data_dir: data_example/kor/ttf
extension: ttf
n_gen: 20
n_font: 5
chars: data/kor/val_seen_chars.json
source_path: data/kor/source.ttf
source_ext: ttf
5️⃣ 모델 학습
1) 학습 구성 파일 (cfgs/MX/train.yaml) 수정
이 파일은 학습률, 반복 수, 저장 디렉토리 등이 설정되어 있습니다. 여기서는 'chn'으로 되어 있는 것을 'kor'로만 수정하고 진행합니다.
2) 학습 실행
옵션으로 되어 있는 항목들은 멀티 GPU나 멀티 노드를 이용한 학습 때 필요한 것들이라 다 제외하고 아래와 같이 간소화해서 실행합니다.
# 형식
$ python train_MX.py cfgs/MX/train.yaml cfgs/data/train/custom.yaml -g(optional) 2 -n(optional) 2 -nr(optional) -p(optional) 12241 0 --work_dir(optional) path/to/save/outputs
# 실제 실행 코드
$ python train_MX.py cfgs/MX/train.yaml cfgs/data/train/custom.yaml
3) 코드 오류 해결
학습을 진행시키는 과정에서 2가지 종류의 오류가 발생해 다음과 같이 조치했습니다.
* base/dataset/ttf_utils.py 파일 수정
AttributeError: 'FreeTypeFont' object has no attribute 'getsize'
이는 Pillow 10.0.0 버전부터 getsize() 메소드가 제거되었기 때문에 발생한 것이었습니다. Pillow를 다운그레이드하거나 코드의 내용을 수정해야 했습니다. 패키지 간의 의존성이 문제가 될 수 있기에 저는 후자를 택했고 수정한 부분은 다음과 같습니다.
# 기존 코드
width, height = font.getsize(char)
# 수정된 코드
bbox = font.getbbox(char)
width, height = bbox[2] - bbox[0], bbox[3] - bbox[1]
* MX/trainer.py 파일 수정
RuntimeError: indices should be either on cpu or on the same device as the indexed tensor (cpu)
찾아보니 원인은 Pytorch에서 인덱싱 수행 시, 인덱스 텐서와 대상 텐서가 서로 다른 디바이스(CPU, GPU)에 있을 때 발생한다고 합니다. 그래서, 이를 같은 디바이스로 이동시키는 작업이 필요했고 관련 코드는 다음과 같습니다.
# 수정 전
acc = T_probs[cids, eids].sum() / n_experts
# 수정 후
# T_probs의 디바이스 확인
device = T_probs.device
# 인덱스 텐서를 T_probs와 동일한 디바이스로 이동
cids = cids.to(device)
eids = eids.to(device)
# 인덱싱 수행
acc = T_probs[cids, eids].sum() / n_experts
4) 학습 진행 결과
실행하면 학습이 진행되며 다음과 같이 진행 현황을 로그에서 확인할 수 있습니다.
$ python train_MX.py cfgs/MX/train.yaml cfgs/data/train/custom.yaml
INFO::05/09 21:30:51 | Run Argv:
> train_MX.py cfgs/MX/train.yaml cfgs/data/train/custom.yaml
INFO::05/09 21:30:51 | Args:
config_paths = ['cfgs/MX/train.yaml', 'cfgs/data/train/custom.yaml']
nodes = 1
gpus_per_node = 1
nr = 0
port = 13481
verbose = True
world_size = 1
INFO::05/09 21:30:51 | Configs:
seed: 2
model: mx
decomposition: data/kor/decomposition.json
primals: data/kor/primals.json
max_iter: 800000
g_lr: 0.0002
d_lr: 0.001
ac_lr: 0.0002
adam_betas:
- 0.0
- 0.9
trainer:
resume: None
force_resume: False
work_dir: result/mx
pixel_loss_type: l1
pixel_w: 0.1
gan_w: 1.0
fm_layers: all
fm_w: 1.0
ac_w: 1.0
ac_gen_w: 1.0
ac_cross_w: 0.0
indp_exp_w: 1.0
indp_fact_w: 1.0
save: all-last
print_freq: 1000
val_freq: 10000
save_freq: 50000
tb_freq: 100
gen:
n_experts: 6
n_emb: 2
dset:
loader:
batch_size: 8
num_workers: 16
train:
n_in_s: 3
n_in_c: 3
data_dir: data_example/kor/ttf
chars: data/kor/train_chars.json
extension: ttf
val:
unseen_chars:
data_dir: data_example/kor/ttf
extension: ttf
n_gen: 20
n_font: 5
chars: data/kor/val_unseen_chars.json
source_path: data/kor/source.ttf
source_ext: ttf
seen_chars:
data_dir: data_example/kor/ttf
extension: ttf
n_gen: 20
n_font: 5
chars: data/kor/val_seen_chars.json
source_path: data/kor/source.ttf
source_ext: ttf
use_ddp: False
INFO::05/09 21:30:51 | [0] Get dataset ...
INFO::05/09 21:30:51 | [0] Build model ...
INFO::05/09 21:30:51 | Start training ...
INFO::05/09 21:30:56 | Step 0
|D 3.879 |G -0.354 |FM 0.206 |R_font 0.875 |F_font 0.125 |R_uni 0.625 |F_uni 0.250
|AC_s 1.172 |AC_c 3.628 |cr_AC_s 0.000 |cr_AC_c 0.000 |AC_acc_s 48.6% |AC_acc_c 10.3%
|AC_g_s 1.716 |AC_g_c 3.872 |cr_AC_g_s 0.000 |cr_AC_g_c 0.000 |AC_g_acc_s 33.3% |AC_g_acc_c 10.1%
|L1 0.129 |INDP_EXP 0.4757 |INDP_FACT 0.7441
/hdd/git/fewshot-font-generation/MX/trainer.py:180: FutureWarning: `torch.cuda.max_memory_cached` has been renamed to `torch.cuda.max_memory_reserved`
torch.cuda.max_memory_cached() / 1000 / 1000)
INFO::05/09 21:48:21 | Step 1000
|D 3.564 |G 0.569 |FM 0.098 |R_font 0.569 |F_font 0.801 |R_uni 0.550 |F_uni 0.567
|AC_s 0.453 |AC_c 3.047 |cr_AC_s 0.000 |cr_AC_c 0.000 |AC_acc_s 91.3% |AC_acc_c 16.9%
|AC_g_s 0.482 |AC_g_c 3.387 |cr_AC_g_s 0.000 |cr_AC_g_c 0.000 |AC_g_acc_s 88.6% |AC_g_acc_c 13.0%
|L1 0.033 |INDP_EXP 0.0241 |INDP_FACT 0.1240
INFO::05/09 22:05:45 | Step 2000
|D 3.691 |G 0.438 |FM 0.082 |R_font 0.528 |F_font 0.752 |R_uni 0.552 |F_uni 0.585
|AC_s 0.035 |AC_c 2.166 |cr_AC_s 0.000 |cr_AC_c 0.000 |AC_acc_s 99.0% |AC_acc_c 24.0%
|AC_g_s 0.036 |AC_g_c 2.376 |cr_AC_g_s 0.000 |cr_AC_g_c 0.000 |AC_g_acc_s 98.9% |AC_g_acc_c 19.5%
|L1 0.027 |INDP_EXP 0.0092 |INDP_FACT 0.0573
참고로 위의 상황은 학습이 열심히 진행되고 있는 것을 보여줍니다. 각 로그의 항목은 다음 의미를 갖습니다.
- Step: 현재 학습 단계(예: Step 0, Step 1000 등)를 나타냅니다.
- D (Discriminator Loss): 판별자의 손실 값으로, 낮을수록 판별자가 실제와 생성된 데이터를 잘 구분하고 있음을 의미합니다.
- G (Generator Loss): 생성자의 손실 값으로, 낮을수록 생성된 데이터가 실제와 유사함을 의미합니다.
- FM (Feature Matching Loss): 특징 매칭 손실로, 낮을수록 생성된 데이터의 특징이 실제 데이터와 유사함을 나타냅니다.
- R_font / F_font: 폰트 수준에서의 진짜/가짜 판별 정확도를 나타냅니다.
- R_uni / F_uni: 문자 수준에서의 진짜/가짜 판별 정확도를 나타냅니다.
- AC_s / AC_c: 스타일/내용 인식기의 손실 값으로, 낮을수록 인식기가 해당 정보를 잘 추출하고 있음을 의미합니다.
- AC_acc_s / AC_acc_c: 스타일/내용 인식기의 정확도로, 높을수록 인식 성능이 우수함을 나타냅니다.
- AC_g_s / AC_g_c: 생성된 이미지에 대한 스타일/내용 인식기의 손실 값입니다.
- AC_g_acc_s / AC_g_acc_c: 생성된 이미지에 대한 스타일/내용 인식기의 정확도입니다.
- L1: 생성된 이미지와 실제 이미지 간의 픽셀 단위 차이를 나타내며, 낮을수록 유사함을 의미합니다.
- INDP_EXP / INDP_FACT: 독립적인 전문가 및 요인 간의 손실 값으로, 낮을수록 독립성이 잘 유지되고 있음을 나타냅니다.
5) GPU 사용량 확인
학습 중 GPU 사용율 현황을 보면 다음과 같습니다. 거의 쉬지 않고 열일하고 있는 셈입니다. GPU가 사용하는 전기량이 350W 정도이니 PC 자체의 전기 소모를 합치면 거의 전열기를 쉬지 않고 돌리는 것과 비슷할 것 같습니다.
6) 학습 진행에 따른 폰트 생성 샘플 확인
학습이 진행되면서 점점 더 제대로 된 글자체가 나오게 될텐데, 중간에 저장된 샘플링 이미지를 통해 어느 정도까지 되었는지 확인할 수 있습니다. Step 1000 단위로만 저장되는 것이어서 몇 개 정도만 가능했습니다. 다음은 그 예입니다.
7) 학습 중단...
일단 진행되는 추이를 보고 약 35 분 정도가 지난 후에 학습을 중단했습니다. 1000 Step을 처리하는데 17분 25초가 소요되었는데 학습에 총 62,000 스텝이 진행되어야 하므로 전체를 계산해 보면 18시간 동안 진행되어야 한다는 계산이 나왔기 때문입니다.
학습 속도를 높이기 위해 Mixed Precision 학습이나 배치 크기를 조정하거나 num_workers를 조정해서 데이터 로딩 속도를 높일 수도 있겠지만 그래도 많이 소요될 것은 분명해 보였습니다. 게다가 GPU의 팬 동작 소리가 계속 거슬릴 정도로 크게 들리는게 걸리기도 했습니다. 컴퓨터를 살려야겠다는 마음으로 중단 결정을 내린 겁니다.
일단은 가이드대로 따라하면 일단 동작하고 학습도 진행된다는 것은 확인한 셈입니다. 다만 아쉬운 것은 최종 완성된 결과물을 확인하지 못했다는 것과 진행한 과정이 맞는지 확신이 없다는 것입니다. 워낙 세부적으로 설정할 것들이 많다보니... -_-;
다음 번에 기회가 되면, 개인 컴퓨터 말고 클라우드나 원격 서버에서 동작시킬 수 있을 때 이번에 하려고 했던 작업을 한번 더 진행해야 할 것 같습니다. :-(
참고자료
- 블로그) 손글씨 필기체 폰트 생성 AI 모델 개발 의뢰 사례 (링크)
'DIY 테스트' 카테고리의 다른 글
[Python] 영상 다 보지 말고, 핵심만 뽑아보자! -Youtube 영상에서 스크립트 추출하기 (1) | 2025.06.05 |
---|---|
WhisperX를 이용한 간이 회의 녹취 프로그램 만들어보기 (0) | 2025.05.23 |
영상 기반 강물의 표면 유속 측정 (0) | 2025.05.04 |
뉴스 타이틀 추출 및 워드 클라우드 생성기 만들기 (0) | 2025.04.28 |
감성과 기능을 더한 틀린 그림 찾기 게임, 바이브 코딩으로 구현하다 (2) | 2025.04.25 |