모델 추론 성능 최적화 - 100-hours-a-week/KTB4-1st-wiki GitHub Wiki

단계 2. 모델 추론 성능 최적화

대상

네 기능을 최적화 대상으로 삼는다. 병목의 성격이 서로 달라, 각각 다른 최적화 축을 보여준다.

기능 최적화 축
2. 텍스트검증 작업 성격별 분리(캐스케이드)
3. 가격검색 결과 캐싱
5. 스마트검색 모델 교체(LLM → 임베딩)
6. 채팅다듬기 Modal 서버리스 GPU + vLLM 통합 서빙

6번만 관리형 API가 아닌 별도 GPU 서빙(vLLM, Modal 서버리스)을 쓰는 이유: 자유 텍스트 생성이 실제로 필요하고 볼륨도 커서 self-serving 이득이 명확하기 때문이다. 나머지(2·3·5)는 LLM을 계속 API로 쓰되 호출 자체를 줄이거나(2), 캐싱하거나(3), 아예 안 쓰거나(5) 하는 방향이다.

실측 관련 제약사항 (사전 안내)

이 문서의 수치는 두 가지 서로 다른 이유로 "측정 예정"이거나 추정치다.

  1. 2·3·5번: Claude API 발급 방식이 아직 미정이라, 지금 시점에 API를 호출할 수단 자체가 없다. API 발급 방식이 정해지는 즉시 동일 스크립트로 실측할 계획이다.
  2. 6번: 위 제약과 별개로, Modal 배포 코드·모델 세팅이 아직 완료되지 않아 실측할 수 없다. Modal은 서버리스라 물리 GPU를 별도로 사전 확보할 필요는 없으며, 배포 코드가 준비되는 대로 곧바로 실측이 가능하다.

1. 기존 모델 추론의 성능 지표

가. 텍스트검증 (기능2)

[측정 예정 — API 발급 방식 확정 후 실측]

입력 유형 평균 응답시간(초)
정상 텍스트 측정 예정
연락처 포함 측정 예정
욕설/혐오 포함 측정 예정
가격 언급 포함 측정 예정
사기성 문구 측정 예정
물건 혼재 측정 예정
  • 측정 방법: API 발급 후 유형별 10회 반복 호출, 평균·중앙값 기록
  • 대상 API: POST /api/moderation/check-text
  • 지금은 유형과 무관하게 매 요청이 LLM(haiku) 1회 호출을 거치므로, 유형별 시간 차이가 크지 않을 것으로 예상 — 이 "차이 없음" 자체가 병목의 증거가 된다.

나. 가격검색 (기능3)

[측정 예정 — API 발급 방식 확정 후 실측, 단 실제 관찰 사례 1건 있음]

물건 유형 평균 응답시간(초)
흔한 공산품(브랜드·모델명 명확) 측정 예정
고유 중고물품(모호한 명칭) 45.9초 (실측, "사과" 검색 시 검색 실패 사례)
  • 측정 방법: API 발급 후 유형별 5회 반복 호출
  • 대상 API: POST /api/value/estimate-price
  • "사과" 사례는 이미 확보된 실제 관찰값이라 추정이 아닌 실측으로 기록한다. 다만 n=1이라 대표성 확보를 위해 동일 유형 반복 측정이 추가로 필요하다.

다. 스마트검색 (기능5)

[측정 예정 — API 발급 방식 확정 후 실측]

후보 물건 개수 평균 응답 시간(초) 호출당 토큰 사용량 처리량(요청/초)
10개 측정 예정 측정 예정 측정 예정
50개 측정 예정 측정 예정 측정 예정
300개(상한) 측정 예정 측정 예정 측정 예정
  • 측정 방법: API 발급 후 후보 개수별 동일 조건 10회 반복 호출, 평균·중앙값 기록
  • 대상 API: POST /api/search/rank-candidates

라. 메시지다듬기(기능6)

현재 프로덕션 상태: 6번 모두 Claude Haiku API 호출 — API 발급 방식 미정으로 측정 예정

이번 최적화 실험의 Before 기준선(naive 서빙, 아직 배포 전이라 추정)

지표 추정값 근거
처리량 측정 예정(추정 약 1.5~2 req/s) HF Transformers naive 추론 공개 벤치마크를 7B 모델 기준으로 스케일링
p50 지연시간 측정 예정(추정 약 3~4초) 출력 300토큰 기준, 배치 없는 순차 디코딩 가정
p95 지연시간 측정 예정(추정 약 7~8초) 동시 요청 큐잉 지연 반영
GPU 메모리 측정 예정(추정 약 15~16GB) 7B fp16 가중치 + KV cache

2. 식별된 성능 병목 요소와 원인 분석

가. 텍스트검증

  • 7개 검증 항목을 전부 LLM 한 번의 호출에 몰아넣음: 위반 여부와 무관하게 매 요청이 동일한 LLM 왕복 비용을 치른다.
  • 명백한 위반도 예외 없이 LLM까지 감: 전화번호가 그대로 적힌 텍스트, 욕설이 섞인 텍스트처럼 패턴·단어만으로 즉시 판별 가능한 케이스에도 조기 차단 로직이 없어 굳이 LLM까지 호출한다.

나. 가격검색

  • 반복 등록되는 인기 품목도 매번 처음부터 재검색: 동일하거나 유사한 물건이 여러 사용자에 의해 반복 등록될 가능성이 높은데, 캐싱이 없어 매번 웹서치+LLM 판단을 새로 수행한다.
  • 물건명이 모호할 때 검색 자체가 실패해 지연이 커짐: "사과" 사례처럼 특정 상품으로 좁혀지지 않으면 검색 시도와 AI 추정 폴백까지 이어지며 응답시간이 크게 늘어난다.

다. 스마트검색

  • 프롬프트 길이가 후보 수에 선형 비례: 후보가 늘어날수록 모델이 처리할 입력 토큰이 늘어난다.
  • 매 요청마다 처음부터 다시 판단: 물건 정보는 등록 이후 거의 안 바뀌는데도 매번 재계산 비용을 치른다.
  • 네트워크 왕복 고정 비용: 로컬 연산이 아닌 외부 API 호출이라 왕복 지연이 매 요청 붙는다.

라. 채팅다듬기

  • 배치 처리 없음: naive 서빙은 요청을 하나씩 순차 처리 → 요청 간 GPU idle 시간 발생
  • fp16 풀 정밀도: VRAM 점유가 커서 동시 요청이 늘어도 배치 크기를 못 키움
  • 공유 프롬프트 재계산: 6번이 매 호출마다 시스템 프롬프트를 처음부터 다시 계산(prefill)
  • 버스트 트래픽 대응 실패: 채팅·메시지 작성이 몰리는 시간대에 동시 요청이 늘면 head-of-line blocking 발생

3. 적용할 최적화 기법의 구체적 계획

가. 텍스트검증 — 캐스케이드(작업 성격별 분리)

[1단계: 규칙 기반 필터링(코드, 정규식+금칙어 템플릿)] 연락처·스팸 패턴 + 욕설/혐오 + 금지 품목 언급 → 걸리면 즉시 거부, LLM 호출 안 함
[2단계: LLM(haiku, Claude API)] 사기성 문구 + 물건 혼재 + 가격언급만 판단 (나머지 4개는 이미 걸러진 상태)
  • 조기 종료(early exit) 구조라, 명백한 위반 케이스는 LLM 호출 자체가 생략된다.
  • 정상 텍스트는 어차피 2단계까지 다 거쳐야 하므로, 이득은 위반 케이스 비율만큼만 확실하게 발생한다는 한계를 명시한다.
  • 가격언급을 2단계(LLM)에 두는 이유: "저렴하게 드릴게요", "가격은 따로 말씀드릴게요"처럼 우회적인 표현이 많아, 단순 숫자·기호 패턴 매칭(정규식)만으로는 탐지가 어렵고 문맥 이해가 필요하다.
  • 2단계가 Modal(vLLM) 자체 서빙이 아니라 Claude API로 남는 이유: (1) 규칙 기반 필터링이 대부분의 트래픽을 흡수해 2단계 호출량 자체가 적음, (2) 사기 문구·가격언급 분류기를 학습시킬 라벨링된 데이터셋이 아직 없음, (3) 사기 문구는 적대적으로 계속 변형되어 범용 추론이 가능한 큰 모델이 novel 패턴에 더 강함. 호출량이 늘거나 라벨 데이터가 쌓이면 자체 분류기 전환을 재검토한다.

나. 가격검색 — 결과 캐싱

캐시 저장 위치는 아직 미정이다 (AI 서버 자체 캐시 vs Backend 관리 캐시). 아래는 우선 검토 중인 잠정안이며, 팀 논의 후 확정 예정이다.

  • 물건명을 정규화한 키 기준으로 검색 결과를 캐싱 (TTL 예: 24시간)
  • (잠정안) AI 서버는 stateless 원칙을 유지하고, Backend가 캐시를 관리하는 안을 우선 검토 중이다. 이 경우 Backend가 요청을 받으면 먼저 캐시를 조회하고, 히트하면 AI 서버(웹서치+LLM) 호출 자체를 생략한다. 미스일 때만 기존처럼 AI 서버를 호출하고, 그 결과를 Backend가 캐시에 적재한다.
[Backend] 캐시 조회 (잠정안)
   히트 → AI 서버 호출 없이 즉시 응답
   미스 → [AI Server] 기존과 동일하게 웹서치+LLM 호출 → 결과를 Backend가 캐싱 후 응답

다. 스마트검색 — 임베딩 완전 교체

  • 검색 요청이 올 때마다, Backend가 넘겨준 후보 물건들의 제목+설명과 사용자의 취향 텍스트를 임베딩 모델(dragonkue/multilingual-e5-small-ko-v2, 한국어 특화 경량 모델)로 그 자리에서 임베딩
  • 후보(최대 50개)와 취향 텍스트를 코사인 유사도로 비교해 순위 산출 — 별도의 사전 저장·인덱싱 단계 없이 요청 단위로 즉시 계산
  • 프로덕션에서는 Backend가 후보를 50개로 제한하지만, 성능 측정은 향후 cap 조정 가능성과 스케일링 한계를 확인하기 위해 300개까지 포함한다.
  • quantization·배치처리·하드웨어가속은 Claude API·임베딩 방식 모두에서 우리가 직접 제어할 수 있는 영역이 아니라 이번 범위에서 제외한다.

라. 채팅다듬기 — vLLM 통합 서빙 (Modal 서버리스)

세 기법은 서로 비교해서 하나를 고르는 대안이 아니라, 하나의 최종 시스템에 겹쳐서 쌓는 레이어다. 최종적으로는 "AWQ로 양자화된 모델을, prefix caching을 켠 vLLM으로, Modal 서버리스 위에서 서빙"하는 단일 구성이 배포된다.

[최종 서빙 시스템 — Modal 서버리스 위에서 구동]
 ├─ ① vLLM (엔진 자체) ── continuous batching + PagedAttention 제공
 ├─ ② AWQ 양자화 모델 ── vLLM이 로드하는 가중치를 fp16 대신 4비트로 교체
 └─ ③ Prefix caching ── vLLM 내장 옵션을 켜는 것 (예: enable_prefix_caching=True)
기법 적용 방법 적용 대상
vLLM 전환 (continuous batching + PagedAttention) 6번 요청을 하나의 vLLM 인스턴스(Qwen2.5-7B-Instruct)로 통합 서빙 6번
AWQ INT4 양자화 6번이 공유하는 모델 가중치를 4비트로 양자화 6번
Prefix caching 6번의 시스템 프롬프트를 KV cache에 캐싱, 반복 호출 시 재계산 생략 6번

4. 최적화 적용 후의 기대 성능 지표

가. 텍스트검증

[측정 예정]

입력 유형 Before(초) After(초) 비고
정상 텍스트 측정 예정 측정 예정 2단계(LLM)까지 도달, 개선 폭 작을 것으로 예상
연락처 포함 측정 예정 측정 예정 1단계(규칙 기반)에서 즉시 종료 예상
욕설/혐오 포함 측정 예정 측정 예정 1단계(규칙 기반)에서 즉시 종료 예상
가격 언급 포함 측정 예정 측정 예정 2단계(LLM)까지 도달, 개선 폭 작을 것으로 예상
사기성 문구 측정 예정 측정 예정 2단계(LLM)까지 도달, 개선 폭 작을 것으로 예상
물건 혼재 측정 예정 측정 예정 2단계(LLM)까지 도달, 개선 폭 작을 것으로 예상

기대되는 방향(정성적): "모든 요청이 N배 빨라진다"가 아니라, 위반 케이스 비율만큼 LLM 호출량이 줄어든다는 것이 정확한 표현이다.

나. 가격검색

[측정 예정]

상황 Before(초) After(초)
캐시 히트 측정 예정 측정 예정(수십 ms대 추정 — 근거: 일반적인 캐시 스토리지 조회 지연)
캐시 미스 측정 예정(모호한 명칭 사례: 45.9초 실측) 기존과 동일

기대되는 방향(정성적): 인기 품목 등록 비중이 높을수록 캐시 히트율이 올라가 전체 평균 응답시간이 개선된다. 완전히 고유한 물건(중고 가구 등)은 캐시 효과가 없다는 한계를 그대로 명시한다.

다. 스마트검색

[측정 예정, 단 로컬 연산 부분은 계산 근거로 추정 가능]

후보 물건 개수 평균 응답 시간(초) 처리량(요청/초) Before 대비 개선율
10개 측정 예정 측정 예정 측정 예정
50개 측정 예정 측정 예정 측정 예정
300개(상한) 측정 예정(추정: 코사인 유사도 연산은 O(N×d), 300개 벡터라도 수 ms 이내) 측정 예정 측정 예정

기대되는 방향(정성적): 외부 LLM 호출이 사라져 네트워크 왕복 비용이 없어지고, 코사인 유사도 계산은 로컬에서 밀리초 단위로 끝나 후보 개수가 늘어나도 응답 시간 증가폭이 훨씬 완만할 것으로 예상된다. "판단 품질(추천 정확도)"은 이 측정의 범위가 아니며, 샘플 데이터는 속도 비교에만 유효하다는 한계를 유지한다.

라. 채팅다듬기

[측정 예정 — Modal 배포 후 실측으로 교체]

각 기법이 실제로 얼마나 기여했는지 검증하기 위해, 최종 구성(vLLM+AWQ+prefix caching)만 한 번에 비교하지 않고 누적 적용(ablation) 방식으로 측정한다.

단계 구성 처리량(req/s) p50 지연(초) p95 지연(초) GPU 메모리
Before naive HF transformers 측정 예정(1.52) 측정 예정(34) 측정 예정(78) 측정 예정(1516GB)
Step 1 + vLLM만 측정 예정 측정 예정 측정 예정 측정 예정
Step 2 + vLLM + AWQ 측정 예정 측정 예정 측정 예정 측정 예정(56GB)
Step 3 (최종) + vLLM + AWQ + prefix caching 측정 예정(810) 측정 예정(1.21.5) 측정 예정(2.53) 측정 예정

기대되는 방향(정성적): 6번은 2·3·5번과 달리 "우리가 직접 서빙 구성을 통제하는" 경우라, 최적화 효과의 방향성(배치로 처리량↑, 캐싱으로 지연↓)은 문헌 근거로 뒷받침되지만 정확한 수치는 Modal 배포 전까지 추정치로 남는다. 단계별 표는 vLLM·AWQ·prefix caching 세 기법이 "경쟁하는 대안"이 아니라 "누적되는 레이어"임을 수치로 보여주기 위한 것이다.