서비스 인프라

GPU 서버, AI 모델을 직접 띄우기 전에 알아 둘 것

AI 모델을 직접 돌리려면 모델이 GPU 메모리에 들어가야 한다.
GPU가 AI 계산에 쓰이는 이유부터 인스턴스를 고를 때 볼 사양, 모델을 바꾸는 동안 생기는 빈 구간을 대체 경로로 메우는 방법까지 이어서 설명한다.

이 글의 목차CPU와 GPU는 잘하는 일이 다르다5
  1. CPU와 GPU는 잘하는 일이 다르다
  2. 모델은 GPU 메모리에 들어가야 돌아간다
  3. GPU 한 장에 안 들어가면
  4. 모델을 바꾸는 동안 요청이 갈 곳
  5. 처음이라면 어디서 시작할까

서비스에 AI 기능을 붙이는 방법은 크게 두 가지다.
하나는 AI 회사가 열어 둔 API에 요청을 보내 답을 받는 방법이고, 다른 하나는 공개된 모델을 받아 내 서버에서 직접 돌리는 방법이다.

두 번째 길을 가려고 EC2 인스턴스 유형 목록을 열면 g5, p5처럼 G나 P로 시작하는 이름이 보인다.
GPU가 달린 인스턴스다.
AI 모델은 왜 GPU에서 돌리고, GPU 서버를 고를 때는 무엇부터 봐야 할까.

CPU와 GPU는 잘하는 일이 다르다

CPU(중앙 처리 장치)⁠는 프로그램의 명령을 실행하는 중심 칩이다.
앞 계산의 결과를 보고 다음에 할 일을 정하는 것처럼, 복잡한 판단을 순서대로 빠르게 처리하는 데 강하다.

GPU(그래픽 처리 장치)⁠는 원래 화면을 그리려고 만든 칩이다.
화면은 픽셀이라는 작은 점 수백만 개로 이뤄져 있고, 점마다 색을 정해야 한다.
계산 하나하나는 단순하고 서로를 기다릴 필요가 없다.

이렇게 서로 독립적인 계산을 동시에 아주 많이 처리하는 것을 병렬 처리라고 한다.
GPU는 이 일에 맞춰 만든 장치다.

작업 의존성에 따른 병렬 처리 방식 앞 계산을 기다리는 판단은 순차 처리에 맞고, 서로 독립적인 같은 계산은 많은 장치에 나눌수록 처리량이 커진다. 개념 도해 작업 의존성에 따른 병렬 처리 방식 작업 분해와 실행 경로 CPU · 의존 작업 ① 복잡한 판단 ② 다음 명령 선택 ③ 순차 완료 앞 결과 대기 GPU · 독립 작업 ① 같은 계산 분해 ② 여러 장치 실행 ③ 결과 결합 동시 처리 병렬화 가능 범위 병렬 분할 가능선 앞 결과 필요 순서 의존 순차 실행 서로 독립 동시 실행 폭 처리량 활용 독립 작업 증가 · 작업 간 의존성 앞 계산을 기다리는 판단은 순차 처리에 맞고, 서로 독립적인 같은 계산은 많은 장치에 나눌수록 처리량이 커진다. SOSHIN · DEV NOTES
계산이 앞 결과를 기다려야 하는지에 따라 CPU와 GPU 중 맞는 쪽이 달라진다.

2006년 NVIDIA가 CUDA를 공개하면서 GPU의 병렬 처리 능력을 과학과 연구 계산에도 쓸 수 있게 됐다.
CUDA는 그래픽이 아닌 계산을 GPU에서 돌리게 해 주는 NVIDIA의 개발 플랫폼이다.

AI 모델의 바탕인 신경망 계산은 대부분 큰 행렬의 곱셈이다.
행렬은 숫자를 가로세로로 늘어놓은 것이고, 행렬 곱셈에서는 단순한 곱셈과 덧셈이 서로 독립적으로 아주 많이 일어난다.
픽셀 계산과 모양이 닮았다.

모델은 GPU 메모리에 들어가야 돌아간다

GPU로 신경망을 돌리는 방식이 널리 알려진 계기 가운데 하나가 2012년의 AlexNet이다.
이 신경망은 그해 이미지 인식 대회 ILSVRC에서 우승했는데, 신경망의 크기를 제한한 것은 주로 GPU 메모리와 학습 시간이었다.

연구진이 쓴 GPU 한 장의 메모리는 3GB였다.
모델이 한 장에 다 들어가지 않아 두 장에 나눠 실었고, 두 GPU는 일부 층에서만 데이터를 주고받게 했다.

AI 모델은 결국 커다란 숫자 묶음이다.
학습으로 정해진 숫자를 파라미터라고 하는데, 요청을 처리하는 동안 이 숫자를 전부 GPU 메모리에 올려 두고 계산한다.

예를 들어 파라미터가 80억 개인 모델을 숫자 하나에 2바이트로 저장하면 파라미터만 160억 바이트, 약 16GB다.
여기에 계산하는 동안 생기는 중간값을 둘 공간이 따로 필요하다.

그래서 GPU 인스턴스를 고를 때는 메모리 칸을 잘 봐야 한다.
AWS의 가속 컴퓨팅 인스턴스 사양표에는 메모리와 가속기 메모리가 따로 적혀 있다.
모델이 올라가는 곳은 뒤쪽, GPU에 달린 메모리다.

g5 인스턴스에 달린 GPU는 NVIDIA A10G이고, 사양표에 적힌 가속기 메모리는 한 장에 22GiB다.
1GiB는 2의 30제곱 바이트여서 앞의 160억 바이트는 약 14.9GiB에 해당한다.

인스턴스 크기와 GPU 메모리는 따로 움직인다.
사양표를 보면 일반 메모리는 g5.xlarge가 16GiB, g5.16xlarge가 256GiB로 16배 차이가 나지만, GPU는 둘 다 A10G 한 장이다.
모델이 GPU 메모리에 들어가지 않는 문제는 인스턴스 크기만 올려서는 풀리지 않을 수 있다.

인스턴스 크기에 따른 가속기 메모리 모델의 파라미터는 GPU에 달린 가속기 메모리에 올라가고, g5에서는 인스턴스를 키워도 GPU가 한 장이면 그 크기가 22GiB 그대로다. 개념 도해 인스턴스 크기에 따른 가속기 메모리 모델이 올라가는 곳 80억 개 · 16비트 모델 예 모델 파일 디스크 읽어 올림 g5.xlarge 인스턴스 GPU · A10G 가속기 메모리 22GiB 파라미터 14.9GiB 중간값 자리 약 7GiB CPU · 메모리 메모리 16GiB 크기별 사양과 모델 파라미터 모델 칸 · 16비트 파라미터만 셈 메모리 가속기 메모리 80억 개 · 14.9GiB 140억 개 · 26.1GiB g5.xlarge 16GiB 22GiB · A10G 1장 들어감 넘침 g5.16xlarge 256GiB 22GiB · A10G 1장 들어감 넘침 모델의 파라미터는 GPU에 달린 가속기 메모리에 올라가고, g5에서는 인스턴스를 키워도 GPU가 한 장이면 그 크기가 22GiB 그대로다. SOSHIN · DEV NOTES
인스턴스를 키웠을 때 늘어나는 것은 메모리 칸뿐이라서, 140억 개 모델은 두 크기 모두에서 넘친다.

GPU 한 장에 안 들어가면

모델이 GPU 한 장에 들어가지 않을 때 쓰는 방법이 몇 가지 있다.
vLLM은 모델을 서버로 띄워 요청을 받게 해 주는 오픈소스 도구인데, 이 도구의 메모리 절약 문서는 큰 모델이 메모리를 바닥낼 수 있다며 모델을 여러 GPU에 나눠 싣는 방법부터 소개한다.

나눠 실으면 GPU끼리 주고받는 데이터가 늘어난다.
프로세스마다 모델 전체를 읽어 쪼개야 해서, 디스크에서 모델을 읽는 시간도 길어진다.

숫자를 더 적은 비트로 저장해 모델을 줄이는 양자화도 있다.
숫자 하나에 16비트(2바이트)⁠를 쓰던 것을 8비트나 4비트로 줄이면 파라미터가 차지하는 메모리도 절반이나 4분의 1 가까이로 준다.
그 대신 정밀도를 내주므로 답의 품질이 얼마나 달라지는지는 확인하고 써야 한다.

이 모델의 파라미터는 GPU 한 장에 들어갈까

파라미터가 차지하는 메모리는 파라미터 수와 숫자 하나에 쓰는 비트 수로 정해진다. 기준은 g5 인스턴스에 달린 A10G 한 장의 가속기 메모리 22GiB다.

파라미터 메모리 · 추정약 14.9GiB

저장 크기

160억 바이트

80억 개 × 2바이트

A10G 한 장 22GiB 기준

한 장에 들어감

남는 몫 약 7.1GiB

파라미터 메모리는 파라미터 수에 숫자 하나의 바이트 수를 곱한 값이다. 80억 개를 16비트로 저장하면 약 14.9GiB로 22GiB GPU 한 장에 들어가고, 140억 개는 약 26.1GiB라 두 장이 필요하다. 같은 140억 개도 8비트로 저장하면 약 13GiB로 한 장에 들어간다.

파라미터만 셈한 추정치이며 1GiB는 2의 30제곱 바이트로 셌다. 요청을 처리하는 동안 쌓이는 KV 캐시 같은 중간 계산값, 실행 도구가 쓰는 메모리, 여러 장에 나눌 때 더 드는 몫은 빠져 있어서 실제로는 이보다 더 필요하다.

모델을 바꾸는 동안 요청이 갈 곳

모델은 디스크에서 읽어 GPU 메모리에 올리고 실행 준비를 마쳐야 요청을 받는다.
큰 모델일수록 이 준비에 시간이 걸린다.

문제는 새 버전으로 바꿀 때다.
옛 모델과 새 모델을 함께 띄울 GPU 메모리가 있으면, 새 모델이 준비된 뒤에 요청을 넘기면 된다.

메모리가 모자라면 옛 모델을 먼저 내려야 한다.
그러면 새 모델이 준비될 때까지 요청을 받을 곳이 없다.
모델 설정 하나를 바꾸려고 다시 띄우는 일도 다운타임이 될 수 있다.
다운타임은 서비스가 요청에 응답하지 못하는 시간이다.

그래서 직접 돌리는 모델을 1차 경로로 두고, 그쪽이 응답하지 못하면 외부 AI 서비스의 API로 요청을 넘기는 구성을 쓰기도 한다.
이렇게 다른 경로로 넘기는 것을 폴백(fallback)⁠이라고 한다.

1차 경로의 전환 조건과 우회 시점 무응답·제한 시간 초과 때 우회하고, 연속 실패가 기준을 넘으면 1차 호출을 멈췄다가 회복 확인에 성공해야 다시 보낸다. 개념 도해 1차 경로의 전환 조건과 우회 시점 이번 요청의 전환 조건 ① 요청 ② 1차 호출 ③ 응답 기한 판정 제한 시간 안 1차 응답 무응답 · 시간 초과 ④ 대체 경로 응답 연속 실패 뒤의 다음 요청 회로 닫힘 · 1차 시도 연속 실패 전환 기준 회로 열림 · 1차 호출 생략 회복 확인 확인 실패 · 열림 유지 확인 성공 · 회로 닫힘 무응답·제한 시간 초과 때 우회하고, 연속 실패가 기준을 넘으면 1차 호출을 멈췄다가 회복 확인에 성공해야 다시 보낸다. SOSHIN · DEV NOTES
1차 경로가 응답하지 못할 때 무슨 일이 일어날지 미리 정해 두면, 그 경로를 손볼 자유가 생긴다.

까다로운 것은 언제 넘기느냐다.
응답이 아예 없을 때뿐 아니라 정해 둔 시간 안에 답이 오지 않을 때도 실패로 봐야 한다.
그러지 않으면 사용자는 응답 없는 1차 경로를 계속 기다린다.

실패가 이어지는데 매번 1차 경로로 요청을 보내 기다리는 것도 낭비다.
회로 차단기(circuit breaker) 패턴은 실패가 기준을 넘으면 회로를 열어 호출을 보내지 않고 곧바로 실패를 돌려준다.
그리고 주기적으로 1차 경로가 살아났는지 확인한다.

폴백에는 함정이 하나 있다.
대체 경로가 조용히 잘 동작하면 1차 경로가 며칠째 죽어 있어도 아무도 모를 수 있다.

그래서 얼마나 자주, 어떤 이유로 넘어갔는지를 지표로 남겨 둔다.
무엇을 어떻게 남길지는 관측 글에 따로 정리했다.

방식얻는 것대신 감수할 것
외부 API에 요청장비도 모델 준비도 필요 없다비용이 사용량을 따라 늘고, 고를 수 있는 모델이 정해져 있다
직접 운영모델과 실행 위치를 직접 고른다장비가 멈추거나 모델을 바꾸는 동안 기능이 멈춘다
둘을 함께한쪽이 멈춰도 응답이 이어진다두 경로와 전환 조건을 모두 관리해야 한다

처음이라면 어디서 시작할까

처음 AI 기능을 붙인다면 외부 API로 시작하는 경우가 많다.
장비를 고르고 모델을 띄우지 않아도 기능부터 만들어 볼 수 있기 때문이다.

직접 운영은 보통 사용량이 커져 비용을 다시 따져야 할 때, 원하는 모델이 API로 제공되지 않을 때, 데이터를 외부 서비스로 보내기 어려울 때 검토한다.

직접 돌린다고 비용이 저절로 줄지는 않는다.
EC2 온디맨드 요금은 인스턴스를 띄운 때부터 중지하거나 종료할 때까지 매겨진다.
요청이 없는 시간에도 GPU 요금이 나간다.

데이터도 마찬가지다.
모델을 내 서버에서 돌려도 로그를 어디에 남기는지, 다른 외부 서비스와 연동하는지에 따라 데이터가 나가는 곳은 달라진다.

GPU 인스턴스를 고를 때는 사양표의 가속기 메모리부터 본다.
쓰려는 모델과 중간값이 들어가는지, 새 버전을 띄우는 동안 옛 버전을 함께 둘 여유가 있는지를 먼저 확인하고, 계산 속도는 그다음에 비교한다.

소신을 상징하는 까마귀 목판화 일러스트
작성자소신 · Soshin

웹과 서버를 만들고, 개발하며 배운 것들을 글로 남깁니다.

좌우로 움직여 보세요