본문으로 이동
메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
Activation-aware Weight Quantization; AWQ
활성값이 큰 입력 채널에 대응하는 소수의 중요한 가중치를 채널별 배율로 키워 보호한 뒤 낮은 비트로 반올림하는, 대형 언어 모델용 가중치 전용 양자화 기법

AWQ(Activation-aware Weight Quantization)는 MIT 한송(Song Han) 연구실이 2023년 6월 발표한 학습 후 양자화 기법이다. 가중치 중 극소수(0.1~1%)가 성능을 좌우하고, 그것이 어느 가중치인지는 가중치 값이 아니라 그 채널로 들어오는 활성값의 크기가 알려 준다는 관찰에서 출발한다. 중요한 채널을 FP16으로 남기는 혼합 정밀도 대신, 수학적으로 같은 변환인 채널별 배율 조정으로 보호해서 모든 가중치를 같은 4비트 형식으로 저장한다. 역전파나 가중치 재구성 없이 채널별 평균 활성값만 쓰므로 보정 데이터에 덜 과적합한다. MLSys 2024 최우수 논문상을 받았다.[1]

가중치만 4비트로 줄이고 활성값은 FP16으로 두는 W4A16 방식이며, 함께 쓰는 배경(왜 가중치 전용인가, 그룹 양자화)은 GPTQ 문서와 같다. 활성값까지 8비트로 줄이는 같은 연구실의 SmoothQuant와는 배율 이동이라는 발상을 공유한다.

왜 가중치 전용인가

편집 원본 편집

AWQ 논문은 Llama-2-7B를 RTX 4090에서 배치 1로 돌리는 경우를 분석했다. 문맥 처리(프롬프트 200토큰)는 10ms, 생성(20토큰)은 310ms로 생성 단계가 훨씬 느리다. 생성 단계는 산술 강도(바이트당 연산)가 약 1이라 메모리 대역폭에 묶이고, 읽는 데이터의 대부분이 활성값이 아니라 가중치다. 가중치를 4비트로 줄이면 같은 대역폭으로 4배 많은 연산을 할 수 있어 산술 강도가 약 4로 오른다.[1] 그래서 개인 기기처럼 배치가 작은 환경에서는 W8A8보다 W4A16이 더 효과적이라는 것이 논문의 주장이다.

1% 중요 가중치

편집 원본 편집

OPT-6.7B를 3비트·그룹 128로 반올림(RTN)하면 퍼플렉시티가 23.54까지 나빠진다. 이때 입력 채널의 1%만 FP16으로 남기면 결과가 채널 선택 기준에 따라 크게 달랐다.[1]

  • 활성값 크기 기준으로 1% 채널을 고르면 11.39(FP16은 10.86)
  • 가중치 크기(L2 노름) 기준이면 무작위로 고른 것과 비슷하게 별 효과가 없었다.

큰 활성값을 처리하는 채널이 더 중요하다는 뜻이다. 다만 FP16 채널을 섞으면 하드웨어 커널이 복잡해진다.

배율로 보호하기

편집 원본 편집

그룹 안의 가중치를 N비트로 양자화하는 함수를 Q(𝐰)=Δ⋅Round⁡(𝐰/Δ), Δ=max⁡(|𝐰|)/2N−1라 하자. 가중치 w 하나에 s>1을 곱하고 입력을 s로 나누면 출력은 같다. 양자화 오차는 다음처럼 바뀐다.[1]

Err⁡(Q(w)x)=Δ⋅RoundErr⁡(wΔ)⋅x,Err⁡(Q(ws)xs)=Δ′⋅RoundErr⁡(wsΔ′)⋅x⋅1s

반올림 오차의 기댓값은 약 0.25로 같고, 원소 하나를 키워도 그룹 최댓값은 대개 그대로라 Δ′≈Δ다. 그러면 오차 비율은 Δ′/Δ⋅1/s≈1/s로 줄어든다. 실제로 OPT-6.7B의 중요 채널 1%에 s=2를 곱하자 퍼플렉시티가 23.54에서 11.92로 좋아졌다. 그러나 s를 너무 키우면(s=4) 그룹 최댓값이 바뀌는 그룹이 늘어 나머지 채널의 오차가 커지므로(21.2%의 채널에서 Δ′/Δ>1), 적당한 값을 찾아야 한다.

층마다 입력 채널별 배율 벡터 𝐬를 다음 목적 함수로 고른다. X는 작은 보정 데이터에서 모은 그 층의 입력이다.[1]

𝐬*=argmin𝐬‖Q(W⋅diag⁡(𝐬))(diag⁡(𝐬)−1X)−WX‖

양자화 함수는 미분할 수 없으므로 탐색 공간을 한 변수로 줄인다. 채널별 평균 활성값 크기를 𝐬X라 할 때

𝐬=𝐬Xα,α*=argminα∈[0,1]ℒ(𝐬Xα)

α=0이면 배율 없음(RTN), 1이면 가장 공격적인 배율이다. [0,1]을 격자 탐색하고, 이어서 양자화 MSE를 줄이도록 가중치 범위를 잘라내는(clipping) 탐색도 한다. diag⁡(𝐬)−1은 앞 연산(레이어 정규화나 앞 선형 층)의 가중치에 흡수시켜 추론 비용이 없다.

선형 층 하나에서 배율을 찾는 예시다. 공식 구현처럼 배율을 max⁡(𝐬)min⁡(𝐬)로 나눠 크기를 정규화했다. 활성값이 큰 채널 10개를 섞은 무작위 입력에서 4비트·그룹 128 RTN 대비 출력 제곱 오차가 약 3분의 1로 줄었다(파이토치 2.5.1에서 확인, α*=0.35).

import torch

def quant_g(W, bits=4, group=128):
    """그룹별 비대칭 반올림 양자화 후 바로 역양자화(오차 측정용)"""
    d_out, d_in = W.shape
    Wg = W.reshape(d_out, d_in // group, group)
    wmin, wmax = Wg.amin(-1, keepdim=True), Wg.amax(-1, keepdim=True)
    maxq = 2 ** bits - 1
    scale = (wmax - wmin).clamp(min=1e-8) / maxq
    zero = torch.round(-wmin / scale)
    q = torch.clamp(torch.round(Wg / scale) + zero, 0, maxq)
    return ((q - zero) * scale).reshape(d_out, d_in)

def awq_search(W, X, n_grid=20):
    """W: [d_out, d_in], X: [n, d_in]. s = s_X^alpha를 격자 탐색"""
    s_x = X.abs().mean(0)                     # 입력 채널별 평균 활성값 크기
    Y = X @ W.T
    best = (float("inf"), None, None)
    for k in range(n_grid + 1):
        alpha = k / n_grid
        s = s_x.pow(alpha).clamp(min=1e-4)
        s = s / (s.max() * s.min()).sqrt()
        Wq = quant_g(W * s) / s               # Q(W diag(s)) diag(s)^-1
        err = (X @ Wq.T - Y).pow(2).mean().item()
        if err < best[0]:
            best = (err, alpha, s)
    return best                               # (오차, alpha, 배율)
설정 방법 Llama-2-7B Llama-2-13B Llama-2-70B LLaMA-7B LLaMA-65B
FP16 5.47 4.88 3.32 5.68 3.53
INT3, g128 RTN 6.66 5.52 3.98 7.01 4.24
GPTQ(재정렬) 6.42 5.41 3.86 6.53 4.21
AWQ 6.24 5.32 3.74 6.35 3.95
INT4, g128 RTN 5.73 4.98 3.46 5.96 3.67
GPTQ(재정렬) 5.63 4.99 3.43 5.83 3.66
AWQ 5.60 4.97 3.41 5.78 3.62

값은 WikiText-2 퍼플렉시티(낮을수록 좋음)이다.[1]

  • 일반화: 지시 튜닝 모델(Vicuna)과 멀티모달 모델(OpenFlamingo)에서도 잘 동작했다고 보고했다.
  • 보정 데이터: 같은 퍼플렉시티에 도달하는 데 GPTQ보다 10분의 1 적은 보정 데이터면 됐다. 보정 데이터와 평가 데이터 분포를 엇갈리게 하면(PubMed와 Enron) AWQ는 퍼플렉시티가 0.5~0.6 늘었고 GPTQ는 2.3~4.9 늘었다(OPT-6.7B, INT3-g128).
  • 2비트: RTN이 완전히 무너지는 INT2에서 AWQ와 GPTQ를 함께 쓰면 GPTQ 단독보다 나았다. 두 방법은 서로 독립이다.

논문은 4비트 모델을 실제 속도로 바꾸는 추론 프레임워크 TinyChat도 함께 냈다. 역양자화를 행렬 곱 커널 안에 합치고(커널 융합), ARM NEON 같은 SIMD 폭에 맞춰 가중치를 미리 재배열해 둔다. 데스크톱·노트북·모바일 GPU에서 허깅 페이스 FP16 구현 대비 평균 3.2~3.3배 빨랐고, 메모리 64GB인 젯슨 오린(Jetson Orin) 한 대에서 Llama-2-70B를 돌렸다.[1]

항목 AWQ GPTQ
무엇을 보정하나 양자화 전에 가중치 배율을 조정(오차가 덜 나게 만든다) 양자화하면서 생긴 오차를 남은 가중치에 되먹임(오차를 메운다)
필요한 통계 채널별 평균 활성값 크기 입력의 2차 통계 XX⊤(헤세 행렬)
계산 비용 층마다 배율 격자 탐색(가벼움) 층마다 촐레스키 분해와 열 단위 갱신
보정 데이터 과적합 적음 상대적으로 큼
저장 형식 같은 비트폭·그룹이면 형식이 같아 추론 커널을 공유할 수 있다

실무에서는 같은 모델을 두 방식으로 양자화해 대상 작업으로 비교하는 경우가 많다. 보정 데이터를 배포 분포에 맞추기 어렵거나 여러 도메인에서 쓸 모델이면 AWQ 쪽이 안전하다.

  • 원 구현: mit-han-lab/llm-awq(GitHub).
  • AutoAWQ: 허깅 페이스 연동 도구였으나 공식적으로 지원이 끝났고, 저장소는 vLLM 프로젝트의 llm-compressor가 이를 넘겨받았다고 안내한다.[2]
  • llm-compressor 레시피(2026-10 예제 기준)는 배율을 맞추는 AWQModifier 뒤에 실제 양자화를 하는 QuantizationModifier를 잇는다.[3]
from llmcompressor.modifiers.awq import AWQModifier
from llmcompressor.modifiers.quantization import QuantizationModifier

recipe = [
    AWQModifier(),                                   # 채널별 배율 탐색·적용
    QuantizationModifier(ignore=["lm_head"], scheme="W4A16_ASYM", targets=["Linear"]),
]
# 이후 oneshot(model=..., dataset=..., recipe=recipe, ...)으로 적용
  • 가중치 전용이라 배치가 커서 연산에 묶이는 서버 환경에서는 속도 이득이 줄어든다. 이런 환경은 FP8이나 W8A8(SmoothQuant) 같은 활성값 양자화가 더 맞을 수 있다.
  • 배율 탐색 공간을 α 하나로 줄였으므로 최적해를 보장하지 않는다.
  • 3비트 이하에서는 여전히 손실이 눈에 띄며, 2비트는 다른 기법과 결합해야 쓸 만하다.
  1. ↑ 1.0 1.1 1.2 1.3 1.4 1.5 1.6 Ji Lin, Jiaming Tang, Haotian Tang, Shang Yang, Wei-Ming Chen, Wei-Chen Wang, Guangxuan Xiao, Xingyu Dang, Chuang Gan, Song Han, 〈AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration〉, MLSys 2024. arXiv:2306.00978
  2. ↑ casper-hansen/AutoAWQ, GitHub.
  3. ↑ AWQ Quantization, llm-compressor 예제, GitHub.