> >
Post

[Internship] - 2달차(마지막)

[Internship] - 마지막 회고

[Internship] - 2달차(마지막)

들어가며

스타트업 인턴십 2달차(마지막)에 대한 회고


1. LLM Fine-tuning

Hugging Face Library를 사용한 LLM Fine-tuning 과정에서 마주한 문제들

문제 상황

  • BLEU Score를 사용해 100, 200 step마다 평가 진행 시도
  • 지속적인 OOM(Out of Memory) Error 발생

시도한 해결책들

  1. Model Size 축소 → 여전히 OOM 발생
  2. LoRA → QLoRA 변경 → 8bit 양자화 적용해도 OOM 지속
  3. NLTK 라이브러리로 BLEU Score 계산 → 연산량 과다로 학습 시간 급증

최종 해결책

  • 학습 중: Eval Loss로 모니터링
  • 학습 완료 후: BLEU Score 평가로 최종 성능 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 예시 code
# 기존 시도 (OOM 발생)
def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    # BLEU calculation during training
    bleu_score = calculate_bleu(predictions, labels)
    return {"bleu": bleu_score}

# 최종 해결책
def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    # Simple eval loss during training
    return {"eval_loss": eval_loss}

# Post-training evaluation
final_bleu = evaluate_bleu_after_training(model, test_dataset)


2. LAG Pipeline의 OutputFixingParse

LAG Pipeline 최적화 과정에서 사용한 OutputFixingParse에 대한 장/단점

OutputFixingParse이란?

  • LLM 출력을 특정 형식으로 자동 변환하는 LangChain의 파싱 도구
  • 출력이 잘못된 형식이어도 자동으로 수정하여 올바른 구조로 변환
  • Pydantic 모델과 연동하여 타입 안전성 보장

OutputFixingParse가 필요한 이유

  • LLM은 자연어 처리 도구지만, 그 출력은 본질적으로 비구조화된 텍스트다.
  • 같은 prompt라도 매번 미묘하게 다른 형식으로 응답할 수 있어
  • 실제 애플리케이션에서 일관성 있는 데이터 처리가 어렵다.
1
2
3
4
5
6
7
8
9
# 같은 프롬프트, 다른 출력 형식들
# 첫 번째 응답
'{"name": "John", "age": 25, "city": "Seoul"}'

# 두 번째 응답  
'{"name":"John","age":"25","city":"Seoul"}'  # 공백 차이, 타입 차이

# 세 번째 응답
'Name: John\nAge: 25\nCity: Seoul'  # 완전히 다른 형식

수동 파싱의 한계와 위험성

  • 전통적인 정규표현식이나 문자열 파싱으로는
  • LLM의 창의적이고 다양한 출력을 모두 처리하기 어려움
1
2
3
4
5
6
7
# 수동 파싱의 문제점
def manual_parse(llm_output):
    # 수십 가지 예외 케이스 처리 필요
    if "가격" in llm_output and "" in llm_output:
        # 정규표현식으로 숫자 추출 시도
        price = re.search(r'(\d+)만?원', llm_output)
        # 하지만 "천이백만원", "1,200,000원" 등 다양한 형식 존재

OutputFixingParser의 해결 방식

  • 이러한 문제들을 LLM의 자체 언어 이해 능력을 활용해 해결
  • 맥락적 이해: “120만원” → 1200000 (int 타입)
  • 형식 변환: “11월 5일 오후 2시” → datetime(2024, 11, 5, 14, 0)
  • 누락 정보 추론: 년도가 없으면 현재 년도로 자동 보완

주요 장점

  1. 자동 오류 수정: JSON 구문 오류, 누락된 필드 등을 자동으로 감지하고 수정
  2. 타입 안전성: Pydantic 모델과 연동하여 런타임 타입 검증 제공
  3. 재시도 메커니즘: 파싱 실패 시 LLM에 오류 정보를 전달하여 재생성 요청
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# OutputFixingParser 사용 예시
from langchain.output_parsers import OutputFixingParser
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel

class PersonInfo(BaseModel):
    name: str
    age: int
    email: str

# 기본 파서 + 자동 수정 기능
base_parser = PydanticOutputParser(pydantic_object=PersonInfo)
fixing_parser = OutputFixingParser.from_llm(parser=base_parser, llm=llm)

# 잘못된 형식도 자동으로 수정됨
result = fixing_parser.parse('{"name": "John", "age": "25", "email": john@test.com}')
# → PersonInfo(name="John", age=25, email="john@test.com")

주요 단점

  1. 추가 API 호출: 오류 수정 시 LLM 호출이 추가로 발생하여 비용/시간 증가
  2. 완벽하지 않은 수정: 복잡한 구조나 의미적 오류는 완전히 해결하지 못할 수 있음
  3. 의존성 증가: LLM 서비스에 대한 의존도가 높아짐



회고

인턴십 마지막 주차를 진행하며 얻은 경험과 인사이트에 대한 내용을 다룬다.


Tech

Insight

1주차: Gradient Accumulation 이해
1달차: LoRA/QLoRA Fine-tuning & GPU Memory에 대한 이해
6주차: OCR + Prompt Engineering 경험
2달차: 대략적인 스타트업의 업무환경 & 방식


스타트업에서의 실무 경험

2개월간의 스타트업에서 인턴십이 끝났다. 짧은 기간이었지만 정말 많은 걸 경험하고 배운 것 같다.
처음엔 LoRA/QLoRA에 대한 개념도 거의 몰랐는데.. (with Quantization..)
업무를 하다보니 모르면 안돼는 내용들이라 더 공부하게 되서 이해가 잘 된거 같다.
그리고 RTX4090 GPU를 실제로 사용해볼 수 있었던 것도 정말 좋은 경험이었다. (처음엔 체감이 잘 안 왔음)
아직 실무 경험이 더 필요하겠지만, 돌아보니 정말 값진 경험이라고 생각한다.

This post is licensed under CC BY 4.0 by the author.