
양자화 과정
weights = [0.235, 0.562, -0.341, 0.892, 0.156, -0.423, 0.671, -0.234]
# 통계 계산
mean = 0.127 # 평균
std = 0.482 # 표준편차
# 4σ 범위 계산
range_min = mean - 4 * std # -1.801
range_max = mean + 4 * std # 2.055
# 4비트(16레벨) 양자화를 위한 구간 크기 계산
num_levels = 16
step_size = (range_max - range_min) / (num_levels - 1) # 0.257
def quantize_4bit(value, range_min, step_size):
# 0-15 사이의 정수값으로 변환
level = round((value - range_min) / step_size)
# 범위 제한
level = max(0, min(15, level))
# 다시 실제 값으로 변환
return level * step_size + range_min
# 예시 값들의 1차 양자화 결과
first_quantized = {
0.235: 0.250, # 8번 레벨
0.562: 0.500, # 9번 레벨
-0.341: -0.250, # 6번 레벨
0.892: 0.750 # 10번 레벨
}
# 양자화 오차 계산
quantization_errors = {
0.235: 0.015, # (0.250 - 0.235)
0.562: -0.062, # (0.500 - 0.562)
-0.341: 0.091, # (-0.250 - -0.341)
0.892: -0.142 # (0.750 - 0.892)
}
error_threshold = 0.1 # 오차 임계값 설정

def secondary_quantize(original, first_quantized, error):
if abs(error) > 0.1: # 임계값보다 오차가 큰 경우
# 추가 비트를 사용하여 더 세밀한 양자화
fine_step = step_size / 4 # 더 작은 스텝 사이즈 사용
adjusted = round(original / fine_step) * fine_step
return adjusted
return first_quantized
# 2차 양자화 결과
second_quantized = {
0.235: 0.234, # 오차가 작아 거의 원본값 유지
0.562: 0.563, # 오차가 커서 세밀하게 조정
-0.341: -0.340, # 오차가 작아 거의 원본값 유지
0.892: 0.891 # 오차가 커서 세밀하게 조정
}
# 4비트 양자화 테이블 예시
quantization_table = {
0: -1.801,
1: -1.544,
2: -1.287,
...
14: 1.798,
15: 2.055
}
# 가중치 저장 형식
stored_format = {
'scale_factor': step_size,
'zero_point': range_min,
'quantized_weights': [8, 9, 6, 10, ...], # 4비트 인덱스값
'high_precision_indices': [1, 3], # 2차 양자화가 적용된 인덱스
'high_precision_values': [0.563, 0.891] # 2차 양자화된 실제 값
}
# 원본 가중치 (32비트 float)
original_memory = 32 bits * 8 values = 256 bits
# 양자화 후 (4비트 + 일부 높은 정밀도 값)
quantized_memory = (4 bits * 8 values) + (32 bits * 2 values) = 96 bits
# 메모리 절감: 약 62.5%
이러한 방식으로 QLoRA는:
1. 대부분의 가중치를 4비트로 압축하여 기본적인 메모리 절감을 달성
2. 중요한 가중치는 높은 정밀도를 유지하여 성능 저하 방지
3. 정규분포 기반의 구간 설정으로 안정적인 양자화 보장
4. 이차 양자화를 통한 세밀한 오차 보정
이를 통해 메모리 사용량을 크게 줄이면서도 모델의 성능을 거의 그대로 유지