지난주에 만든 농업 sLLM은 현장 장비에서 돌아가야 해요. 비닐하우스 제어기나 농기계 단말 같은 곳인데, 인터넷이 끊기기 일쑤고 GPU는 없거나 아주 작아요. 모델을 거기 맞게 줄이는 작업, 그러니까 양자화와 추론 엔진 이야기예요.
목표 장비
구체적으로 쓸게요. 저희가 맞춘 장비는 ARM 기반 보드에 메모리 8GB, 작은 내장 GPU가 있거나 없는 경우예요. 목표는 짧은 질문에 몇 초 안에 첫 토큰이 나오고, 읽는 속도보다 빠르게 생성하는 거예요. 메모리는 모델 외에 다른 프로세스도 써야 해서 모델에 4GB 정도만 쓸 수 있어요.
양자화가 뭐냐면요
모델 가중치는 보통 16비트 실수예요. 이걸 8비트나 4비트 정수로 바꾸면 메모리가 절반, 1/4로 줄고 계산도 빨라져요. 대신 정밀도가 떨어져서 답이 조금 달라져요. 얼마나 달라지는지가 관건이에요.
| 정밀도 | 메모리(수십억 파라미터 모델 기준) | 품질 |
|---|---|---|
| FP16 | 기준 | 기준 |
| INT8 | 약 1/2 | 거의 차이 없음 |
| 4비트 | 약 1/4 | 약간 저하, 방식에 따라 차이 큼 |
| 3비트 이하 | 더 작음 | 눈에 띄게 저하 |
저희 메모리 예산에서는 4비트가 필요했어요.
4비트 방식 비교
4비트 안에서도 방법이 여러 가지예요. 저희가 해 본 건 크게 두 부류예요.
단순 그룹 양자화. 가중치를 작은 그룹으로 나눠 그룹마다 스케일을 두고 반올림해요. 빠르고 간단한데, 중요한 가중치도 똑같이 반올림돼서 품질 손실이 상대적으로 커요.
보정 데이터 기반 양자화(GPTQ·AWQ 계열). 소량의 대표 데이터를 흘려 보면서 "어떤 가중치가 출력에 민감한지"를 보고, 민감한 건 더 정밀하게 남겨요. 품질이 확실히 나았어요. 여기서 중요한 게 보정 데이터를 농업 질의응답으로 쓰는 거예요. 일반 텍스트로 보정하면 농업 용어 쪽 정밀도가 먼저 깎여요.
평가는 지난주 평가셋으로 했어요. 4비트 보정 양자화에서 태그별 점수가 FP16 대비 조금 떨어졌는데, 거절 태그가 제일 많이 떨어졌어요. 양자화하면 모델이 "모르겠다"를 덜 하더라고요. 그래서 거절 데이터 비중을 높여 보정하고, 그래도 부족한 건 거절 규칙을 모델 밖에 둬서 보완했어요(질문 분류기가 "예측 불가" 유형을 먼저 거르는 식).
추론 엔진
양자화된 모델을 실제로 돌리는 건 추론 엔진이에요. 선택 기준은 이랬어요.
- ARM CPU에서 돌아갈 것 (GPU가 없을 수 있어요)
- 내장 GPU가 있으면 쓸 것
- 메모리 매핑으로 로딩이 빠를 것 (장비가 재부팅되면 바로 떠야 해요)
- 의존성이 적을 것 (장비에 파이썬 환경 깔기 어려워요)
결과적으로 C/C++ 기반의 경량 추론 엔진을 쓰고, 모델은 그 엔진의 양자화 포맷으로 변환해서 넣어요. 파이썬 없이 단일 바이너리로 뜨는 게 현장에서 제일 편했어요.
속도를 더 짜낸 것들
- 프롬프트 캐시. 시스템 프롬프트(역할, 답변 형식)는 매번 같으니까 그 부분의 KV 캐시를 저장해 두고 재사용해요. 첫 토큰 시간이 크게 줄었어요.
- 컨텍스트 길이 제한. 긴 문서를 넣을 일이 없어서 컨텍스트를 짧게 잡으면 메모리가 남고 빨라져요.
- 스레드 수 조정. CPU 코어를 다 쓰면 다른 프로세스가 굶어요. 장비별로 스레드 수를 설정으로 열어 뒀어요.
RAG는 장비 안에서
사실을 모델에 외우게 하지 않고 문서에서 가져온다고 했는데, 장비에는 인터넷이 없어요. 그래서 문서 색인도 장비 안에 넣어요. 작물 가이드, 병해충 도감, 농약 기준을 청크로 만들어 작은 벡터 색인으로 넣고, 임베딩 모델도 양자화해서 같이 올려요. 전부 합쳐서 메모리 예산 안에 들어가야 해서 색인 크기를 조정했어요. 업데이트는 인터넷이 연결될 때 차분으로 받아요.
정리
- 메모리 예산을 먼저 정하고 그에 맞는 비트 수를 고르세요.
- 4비트는 보정 데이터 기반 방식으로, 보정 데이터는 도메인 데이터로.
- 양자화하면 "모르겠다"를 덜 해요. 거절은 모델 밖 규칙으로도 보강하세요.
- 엔진은 장비 환경(ARM, 의존성, 로딩 속도)에 맞춰서.
- RAG 색인도 장비 안에.
다음 주는 스마트팜 로봇의 눈, 생육 단계 인식 데이터셋을 어떻게 만들었는지예요.