[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 발생
시도한 해결책들
- Model Size 축소 → 여전히 OOM 발생
- LoRA → QLoRA 변경 → 8bit 양자화 적용해도 OOM 지속
- 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) - 누락 정보 추론: 년도가 없으면 현재 년도로 자동 보완
주요 장점
- 자동 오류 수정: JSON 구문 오류, 누락된 필드 등을 자동으로 감지하고 수정
- 타입 안전성: Pydantic 모델과 연동하여 런타임 타입 검증 제공
- 재시도 메커니즘: 파싱 실패 시 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")
주요 단점
- 추가 API 호출: 오류 수정 시 LLM 호출이 추가로 발생하여 비용/시간 증가
- 완벽하지 않은 수정: 복잡한 구조나 의미적 오류는 완전히 해결하지 못할 수 있음
- 의존성 증가: 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.
