본문 바로가기
IT개발/AI 공부

9. AI 자동 매매 시스템 개발 과정: 모델 병합

by jusyBear 2025. 4. 10.
반응형

📝 이 글에서는 허깅페이스의 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의 큰 용량을 가질 수 있으므로, 충분한 디스크 공간과 메모리가 확보되었는지 확인하세요.


🔄 베이스 모델에 어댑터 모델을 머지

📝 모델을 머지 하는 과정

베이스 모델과 어댑터 모델을 병합하는 과정은 두 모델의 구조를 깊이 이해하고, 이를 결합하여 새로운 기능을 추가하는 것입니다.

모델 병합의 주요 단계:

  1. 준비 단계: 베이스 모델과 어댑터 모델 로드
  2. 호환성 확인: 두 모델의 구조적 호환성 검증
  3. 가중치 병합: 어댑터의 가중치를 베이스 모델에 적용
  4. 최적화: 병합된 모델 최적화 (선택 사항)
  5. 검증: 병합 모델의 성능 테스트
  6. 저장: 최종 모델 저장

예시 코드

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("모델 병합이 완료되었습니다.")

⚠️ 모델을 머지하면서 발생한 문제

베이스 모델이 증류 모델일 경우, 모델의 구조적 한계로 인해 병합이 불가능한 문제가 발생할 수 있습니다.

문제 원인:

문제 유형 설명
구조적 불일치 증류 모델은 원본 모델과 다른 구조를 가질 수 있음
매개변수 제한 증류 모델은 원본보다 적은 매개변수를 가짐
레이어 고정 특정 레이어가 고정되어 수정 불가능
최적화 충돌 증류 과정에서 적용된 최적화가 어댑터와 충돌

🔧 해결책

베이스 모델이 증류 모델인 경우, 병합 대신 로드 방식을 사용하는 것이 효과적입니다.

로드 방식의 장점:

  1. 구조 보존: 각 모델의 구조를 그대로 유지
  2. 유연성: 필요에 따라 다양한 어댑터 전환 가능
  3. 메모리 효율: 어댑터만 메모리에 로드하여 효율적 관리
  4. 확장성: 새로운 어댑터 추가 용이

로드 방식의 파이썬 코드 예시

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("어댑터 로드 및 저장이 완료되었습니다.")

로드 방식 프로세스:

  1. 증류된 베이스 모델과 토크나이저를 로드합니다.
    1. 메모리 효율성을 위해 float16 데이터 타입 사용
    2. GPU 메모리에 자동 분배 설정
  2. 어댑터 설정을 정의합니다.
    1. LoRA 어댑터 사용 (Low-Rank Adaptation)
    2. 랭크, 알파, 드롭아웃 비율 설정
  3. 어댑터를 로드하고 활성화합니다.
    1. 병합하지 않고 별도 레이어로 로드
    2. 특정 어댑터를 활성화하여 사용
  4. 샘플 입력으로 모델을 테스트합니다.
    1. 모델 정상 작동 확인
    2. 출력 결과 검증
  5. 필요한 경우 어댑터만 별도로 저장합니다.
    1. 경량화된 어댑터만 저장하여 공간 절약
    2. 필요시 다른 어댑터로 교체 가능

이 방식은 베이스 모델의 구조를 변경하지 않고, 어댑터만 추가하여 필요한 기능을 구현할 수 있습니다. 특히 증류 모델과 같이 구조적 제약이 있는 경우에 유용합니다.


주의사항

  • 본 글은 학습 및 연구 목적으로 작성되었으며, 실제 투자에 활용 시 발생하는 어떠한 손실에 대해서도 책임지지 않습니다.
  • 투자는 항상 본인의 책임 하에 신중하게 결정하시기 바랍니다.
  • 해당 글은 직접 작성한 내용을 AI를 통해 가다듬는 방식으로 작성되었습니다.
  • 해당 글의 예시 중 일부는 AI를 통해서 제작되었습니다.
반응형