📝 이 글에서는 허깅페이스의 AutoTrain을 활용한 AI 자동 매매 시스템 개발 과정을 소개합니다.
🔄 학습 완료된 모델을 가지고 ollama에 올리는 작업과 모델 병합 과정에서 발생하는 이슈를 해결해봅니다.
🔍 허깅 페이스에서 학습 완료한 어댑터 모델을 다운로드
허깅페이스에서 학습이 완료된 어댑터 모델을 다운로드하는 과정은 매우 중요합니다. 이 과정에서는 허깅페이스 API를 활용하여 학습된 어댑터 모델을 로컬 환경으로 가져오는 방법을 알아보겠습니다.
💡 어댑터 모델이란?
어댑터 모델은 대규모 언어 모델(LLM)을 특정 작업에 맞게 미세 조정한 경량화된 모델입니다. 전체 모델을 재학습하는 대신, 기본 모델의 일부 레이어만 조정하여 특정 도메인(예: 금융 데이터 분석)에 특화된 성능을 발휘할 수 있도록 합니다.
특징 | 설명 |
---|---|
🔸 경량화 | 전체 모델보다 용량이 훨씬 작음 (MB 단위) |
🔸 특화성 | 특정 태스크나 도메인에 최적화됨 |
🔸 효율성 | 미세 조정 시 적은 컴퓨팅 자원 사용 |
🔸 유연성 | 여러 어댑터를 하나의 베이스 모델에 적용 가능 |
📌 중요: 어댑터 모델만으로는 작동하지 않으며, 반드시 베이스 모델과 함께 사용해야 합니다.
🛠️ 허깅페이스에서 어댑터 모델 다운로드 방법
파이썬을 사용하여 허깅페이스에서 학습된 어댑터 모델을 다운로드하는 핵심 코드 예시입니다:
from huggingface_hub import hf_hub_download
from transformers import AutoModelForCausalLM, AutoTokenizer, AutoConfig
import os
def download_adapter(self, repo_id: str, model_name: str = None) -> dict:
"""허깅페이스에서 어댑터 모델 파일들을 다운로드합니다."""
try:
# 허깅페이스 토큰 가져오기
token = os.environ.get("HUGGINGFACE_TOKEN")
if not token:
raise ValueError("HUGGINGFACE_TOKEN이 설정되지 않았습니다.")
# 모델별 디렉토리 생성
model_name = model_name or repo_id.split('/')[-1]
adapter_path = os.path.join(ADAPTER_DIR, model_name)
os.makedirs(adapter_path, exist_ok=True)
self.logger.info(f"어댑터 다운로드 시작: {repo_id}")
self.logger.info(f"저장 위치: {adapter_path}")
# 필요한 파일들 리스트
files_to_download = [
"adapter_config.json", # 어댑터 구성 정보
"adapter_model.safetensors", # 어댑터 가중치
"special_tokens_map.json", # 특수 토큰 매핑
"tokenizer.json", # 토크나이저 정보
"tokenizer_config.json", # 토크나이저 구성
"training_args.bin", # 학습 인자
"training_params.json" # 학습 매개변수
]
downloaded_files = {}
for file in files_to_download:
try:
file_path = hf_hub_download(
repo_id=repo_id,
filename=file,
local_dir=adapter_path,
token=token # 토큰 추가
)
downloaded_files[file] = file_path
self.logger.info(f"파일 다운로드 완료: {file}")
except Exception as e:
self.logger.warning(f"파일 다운로드 실패: {file}, 오류: {str(e)}")
# 일부 파일이 없을 수 있으므로 계속 진행
continue
return {
"status": "success",
"message": "어댑터 파일 다운로드 완료",
"adapter_path": adapter_path,
"files": downloaded_files
}
except Exception as e:
self.logger.error(f"어댑터 다운로드 중 에러 발생: {str(e)}")
return {
"status": "failed",
"error": str(e)
}
✅ 작업 체크리스트
- ✓ 환경 변수에 허깅페이스 토큰 설정
- ✓ 어댑터 모델 저장 디렉토리 생성
- ✓ 필수 어댑터 파일 다운로드
- ✓ 오류 처리 및 로깅
📥 허깅페이스에 올라가 있는 베이스 모델 다운로드
베이스 모델은 어댑터 모델이 학습된 원본 모델로, 허깅페이스에서 다운로드할 수 있습니다. 이 과정은 어댑터 모델 다운로드와 유사하지만, 모델 크기가 훨씬 크기 때문에 더 많은 시간과 저장 공간이 필요합니다.
📊 베이스 모델 vs 어댑터 모델 비교
특성 | 베이스 모델 | 어댑터 모델 |
---|---|---|
파일 크기 | GB 단위 (5~70GB) | MB 단위 (10~500MB) |
다운로드 시간 | 길다 (10분~수 시간) | 짧다 (몇 초~몇 분) |
필요 저장 공간 | 많다 | 적다 |
독립 사용 가능 | ✅ | ❌ |
필수 파일 | 모델 가중치, 토크나이저, 구성 파일 | 어댑터 가중치, 구성 파일 |
파이썬을 사용하여 허깅페이스에서 학습된 베이스 모델을 다운로드하는 핵심 코드 예시입니다:
def download_base_model(self, model_path: str) -> dict:
"""허깅페이스에서 베이스 모델을 다운로드합니다."""
try:
# 환경 변수에서 설정 가져오기
base_model_id = os.environ.get("BASE_MODEL_ID")
token = os.environ.get("HUGGINGFACE_TOKEN")
revision = os.environ.get("MODEL_REVISION", "main")
if not base_model_id:
raise ValueError("BASE_MODEL_ID가 설정되지 않았습니다.")
if not token:
raise ValueError("HUGGINGFACE_TOKEN이 설정되지 않았습니다.")
self.logger.info(f"베이스 모델 다운로드 시작: {base_model_id}")
self.logger.info(f"저장 위치: {model_path}")
# 토크나이저 다운로드
self.logger.info("토크나이저 다운로드 중...")
tokenizer = AutoTokenizer.from_pretrained(
base_model_id,
token=token,
revision=revision,
trust_remote_code=True,
cache_dir=model_path
)
# 모델 설정 다운로드
self.logger.info("모델 설정 다운로드 중...")
config = AutoConfig.from_pretrained(
base_model_id,
token=token,
revision=revision,
trust_remote_code=True,
cache_dir=model_path
)
# 모델 다운로드
self.logger.info("모델 가중치 다운로드 중...")
model = AutoModelForCausalLM.from_pretrained(
base_model_id,
config=config,
token=token,
revision=revision,
torch_dtype=torch.float16, # 메모리 효율을 위해 float16 사용
trust_remote_code=True,
cache_dir=model_path
)
# 클래스 변수에 저장
self.model = model
self.tokenizer = tokenizer
self.config = config
self.logger.info("베이스 모델 다운로드 완료")
return {
"status": "success",
"message": "베이스 모델 다운로드 완료",
"model_info": {
"model_id": base_model_id,
"revision": revision,
"path": model_path,
"config": {
"model_type": config.model_type,
"vocab_size": config.vocab_size,
"hidden_size": config.hidden_size
}
}
}
except Exception as e:
self.logger.error(f"베이스 모델 다운로드 중 에러 발생: {str(e)}")
return {
"status": "failed",
"error": str(e)
}
⚠️ 주의 사항: 베이스 모델은 수 GB의 큰 용량을 가질 수 있으므로, 충분한 디스크 공간과 메모리가 확보되었는지 확인하세요.
🔄 베이스 모델에 어댑터 모델을 머지
📝 모델을 머지 하는 과정
베이스 모델과 어댑터 모델을 병합하는 과정은 두 모델의 구조를 깊이 이해하고, 이를 결합하여 새로운 기능을 추가하는 것입니다.
모델 병합의 주요 단계:
- 준비 단계: 베이스 모델과 어댑터 모델 로드
- 호환성 확인: 두 모델의 구조적 호환성 검증
- 가중치 병합: 어댑터의 가중치를 베이스 모델에 적용
- 최적화: 병합된 모델 최적화 (선택 사항)
- 검증: 병합 모델의 성능 테스트
- 저장: 최종 모델 저장
예시 코드
from transformers import AutoModel, AutoModelForCausalLM
# 베이스 모델과 어댑터 모델의 경로를 지정합니다.
base_model_path = "path/to/base/model"
adapter_model_path = "path/to/adapter/model"
# 베이스 모델을 로드합니다.
base_model = AutoModelForCausalLM.from_pretrained(base_model_path)
# 어댑터 모델을 로드하고 병합합니다.
base_model.load_adapter(adapter_model_path)
# 병합된 모델을 저장합니다
base_model.save_pretrained("path/to/merged/model")
print("모델 병합이 완료되었습니다.")
⚠️ 모델을 머지하면서 발생한 문제
베이스 모델이 증류 모델일 경우, 모델의 구조적 한계로 인해 병합이 불가능한 문제가 발생할 수 있습니다.
문제 원인:
문제 유형 | 설명 |
---|---|
구조적 불일치 | 증류 모델은 원본 모델과 다른 구조를 가질 수 있음 |
매개변수 제한 | 증류 모델은 원본보다 적은 매개변수를 가짐 |
레이어 고정 | 특정 레이어가 고정되어 수정 불가능 |
최적화 충돌 | 증류 과정에서 적용된 최적화가 어댑터와 충돌 |
🔧 해결책
베이스 모델이 증류 모델인 경우, 병합 대신 로드 방식을 사용하는 것이 효과적입니다.
로드 방식의 장점:
- 구조 보존: 각 모델의 구조를 그대로 유지
- 유연성: 필요에 따라 다양한 어댑터 전환 가능
- 메모리 효율: 어댑터만 메모리에 로드하여 효율적 관리
- 확장성: 새로운 어댑터 추가 용이
로드 방식의 파이썬 코드 예시
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
# 증류 베이스 모델과 어댑터 모델의 경로 지정
distilled_base_model_path = "path/to/distilled/base/model"
adapter_model_path = "path/to/adapter/model"
# 증류 베이스 모델과 토크나이저 로드
base_model = AutoModelForCausalLM.from_pretrained(
distilled_base_model_path,
torch_dtype=torch.float16, # 메모리 효율을 위해 float16 사용
device_map="auto" # GPU 메모리에 맞게 자동 분배
)
tokenizer = AutoTokenizer.from_pretrained(distilled_base_model_path)
# 어댑터 설정 정의
adapter_config = {
"adapter_name": "custom_adapter",
"adapter_type": "lora", # LoRA 어댑터 사용
"r": 8, # 어댑터의 랭크
"lora_alpha": 16,
"lora_dropout": 0.1
}
# 어댑터 로드 (병합이 아닌 로드 방식)
base_model.load_adapter(
adapter_model_path,
adapter_name=adapter_config["adapter_name"],
load_as=adapter_config["adapter_type"]
)
# 어댑터 활성화
base_model.set_active_adapters(adapter_config["adapter_name"])
# 모델 테스트
input_text = "이 모델은 어떤 기능을 할까요?"
input_ids = tokenizer.encode(input_text, return_tensors="pt").to(base_model.device)
output = base_model.generate(input_ids, max_length=50)
generated_text = tokenizer.decode(output[0], skip_special_tokens=True)
print(f"입력: {input_text}")
print(f"출력: {generated_text}")
# 모델 저장 (어댑터만 저장)
base_model.save_adapter("./saved_adapter", adapter_config["adapter_name"])
print("어댑터 로드 및 저장이 완료되었습니다.")
로드 방식 프로세스:
- 증류된 베이스 모델과 토크나이저를 로드합니다.
- 메모리 효율성을 위해 float16 데이터 타입 사용
- GPU 메모리에 자동 분배 설정
- 어댑터 설정을 정의합니다.
- LoRA 어댑터 사용 (Low-Rank Adaptation)
- 랭크, 알파, 드롭아웃 비율 설정
- 어댑터를 로드하고 활성화합니다.
- 병합하지 않고 별도 레이어로 로드
- 특정 어댑터를 활성화하여 사용
- 샘플 입력으로 모델을 테스트합니다.
- 모델 정상 작동 확인
- 출력 결과 검증
- 필요한 경우 어댑터만 별도로 저장합니다.
- 경량화된 어댑터만 저장하여 공간 절약
- 필요시 다른 어댑터로 교체 가능
이 방식은 베이스 모델의 구조를 변경하지 않고, 어댑터만 추가하여 필요한 기능을 구현할 수 있습니다. 특히 증류 모델과 같이 구조적 제약이 있는 경우에 유용합니다.
주의사항
- 본 글은 학습 및 연구 목적으로 작성되었으며, 실제 투자에 활용 시 발생하는 어떠한 손실에 대해서도 책임지지 않습니다.
- 투자는 항상 본인의 책임 하에 신중하게 결정하시기 바랍니다.
- 해당 글은 직접 작성한 내용을 AI를 통해 가다듬는 방식으로 작성되었습니다.
- 해당 글의 예시 중 일부는 AI를 통해서 제작되었습니다.
'IT개발 > AI 공부' 카테고리의 다른 글
10. AI 자동 매매 시스템 개발 과정: 모델 양자화 (0) | 2025.04.10 |
---|---|
8. 용어 설명: 디스크리트 액션 (1) | 2025.04.10 |
7. AI 자동 매매) 파트 2: 학습 과정 및 결과 분석 (0) | 2025.03.28 |
6. AI 자동 매매) 파트 1: AutoTrain(파인 튜닝) 및 데이터 준비 (1) | 2025.03.27 |
5. 추론 결과 가공 (4) | 2025.03.22 |