LabHub
배우기 러닝패스 코스

트랜스포머 — 어텐션을 손으로 계산한다 · KV 캐시가 줄이는 계산량 · 실습

KV 캐시가 아끼는 곱셈을 센다

LabHub 에서 이어서 보기

목표

자기회귀 생성에서 KV 캐시가 무엇을 아끼는지 곱셈 횟수를 직접 세어 확인한다. 곱셈을 세는 곱셈 함수를 만들고, 캐시 없이 도는 판과 캐시로 도는 판을 각각 만들어 두 판의 출력이 같은 값인지 허용 오차로 확인한 뒤, 문맥 길이마다 곱셈 횟수를 세어 표로 만든다. 마지막에 캐시를 낮은 정밀도로 들고 있으면 그 같음이 깨지는 것을 재고, 자기가 정한 설정으로 캐시가 먹는 원소 수와 바이트를 계산해 기록으로 남긴다.

왜 중요한가

어텐션 한 번을 손으로 계산하는 것과, 그 어텐션을 생성 반복 안에서 부르는 것은 다른 이야기다. 길이 100짜리 답을 만들려면 모델을 100번 부르고, 캐시가 없으면 부를 때마다 앞 문맥 전체의 k·v 를 처음부터 다시 만든다. 첫 토큰의 k·v 를 백 번 만들고 아흔아홉 번 버린다는 뜻이다.
이 낭비는 코드를 읽어서는 보이지 않는다. 어텐션 함수 자체는 잘못된 곳이 하나도 없기 때문이다. 보이게 하려면 세어야 한다. 그리고 시간을 재서는 안 된다 — 시간은 기계와 부하에 따라 달라지지만 곱셈 횟수는 같은 입력에서 늘 같고, 길이에 대해 어떻게 늘어나는지를 그대로 보여 준다.
이 실습은 실제 모델을 부르지 않는다. 이 파드의 시스템 파이썬에는 numpy 가 없고(/opt/onnx-lab/bin/python 안에만 있다) torch 도 transformers 도 없다. 표준 라이브러리만으로 같은 구조를 만들고, 여기서 잰 숫자만 쓴다. 그래서 "실제 모델은 몇 GB 를 먹는다" 나 "몇 배 빨라진다" 같은 말은 여기서 하지 않는다.
채점기는 여러분이 적어 둔 설명을 믿지 않는다. 여러분의 모듈을 실제로 불러 매번 다른 크기의 입력으로 함수를 두드려 보고, 값과 곱셈 횟수를 따로 계산해 대조한다. 크기가 실행마다 바뀌므로 값을 외워 넣을 수 없다.

단계

1. /root/work/tf-kv/kv.pyCONFIGreset_muls()·muls()·mul(a, b)·dot(u, v) 를 만드세요. mul 은 곱하면서 센 횟수를 하나 올리고, dot 은 그 mul 로만 곱합니다.
2. project(x, W) 를 더해 토큰 하나를 가중치 한 벌에 통과시키게 하세요. W 는 줄마다 길이가 len(x) 인 행렬이고 돌려주는 목록의 길이는 len(W) 입니다.
3. attend(q, K, V) 를 더해 질의 하나가 쌓인 K·V 전체를 보게 하세요. 점수를 √len(q) 로 나누고 소프트맥스를 씌운 뒤 V 의 가중 평균을 냅니다.
4. step_nocache(xs, Wq, Wk, Wv) 를 더해 캐시 없이 출력 하나를 내게 하세요. 앞의 모든 토큰에 대해 q·k·v 를 다시 만들고 마지막 질의로 어텐션합니다.
5. new_cache()·store(cache, k, v, digits=None)·step_cached(x, Wq, Wk, Wv, cache, digits=None) 를 더해 캐시로 같은 출력을 내게 하세요. 새 토큰 하나만 계산하고 K·V 에 한 줄 덧붙입니다.
6. ATOL·RTOL·close(got, want)·compare(xs, Wq, Wk, Wv, digits=None) 를 더해 두 방식의 출력이 같은 값인지 견주게 하세요. 등호로 견주지 않습니다.
7. mul_table(xs, Wq, Wk, Wv) 를 더해 문맥 길이 1 부터 len(xs) 까지 두 방식의 곱셈 횟수를 세게 하세요. 돌려주는 값은 (문맥길이, 캐시없음, 캐시있음) 짝의 목록입니다.
8. 토큰 12개로 표를 만들고 캐시가 먹는 메모리를 계산해 /root/work/tf-kv/kv_report.json/root/work/tf-kv/kv_report.md 에 기록하세요.

참고

단계 8개

  1. 곱셈을 세는 곱셈
  2. 토큰 하나를 투영한다
  3. 질의 하나가 캐시 전체를 본다
  4. 캐시 없이 — 앞 전체를 다시 계산한다
  5. 캐시로 — 한 줄만 덧붙인다
  6. 두 방식의 출력이 같은 값인가
  7. 길이마다 곱셈을 센다
  8. 아끼는 것과 치르는 것을 함께 적는다