사전 모델 서빙
개념

모델 서빙

gabury1고친 사람 github-actions[bot]

모델 서빙은 훈련을 마친 머신러닝 모델을 서버에 띄워 두고 요청이 올 때마다 예측을 돌려주는 일입니다. 겉모습은 요청을 받아 응답하는 보통의 웹 서버와 같습니다. 다만 모델은 크고 계산이 무겁습니다. 그래서 따로 챙길 것이 생깁니다.

쉽고 빠른 이해

무슨 일을 하나 — 다 만든 모델을 서버에 올려 두고 요청마다 답을 계산해 돌려줍니다. 배달 앱이 주문 순간에 「도착까지 32분」을 띄울 때, 그 숫자를 계산하는 서버가 모델 서빙입니다.

왜 이렇게 하나 — 모델은 만들어 두기만 하면 아무 일도 안 합니다. 누군가 입력을 넣고 답을 받아 가야 쓸모가 생깁니다. 모델을 읽어 들이는 데 시간이 걸려서, 요청마다 새로 읽지 않고 한 번 올린 모델을 계속 씁니다.

어떻게 도나

  1. 서버가 켜질 때 모델 파일을 메모리에 올립니다
  2. 요청이 오면 입력을 모델이 받는 숫자로 바꿔 넣습니다
  3. 모델이 계산한 답을 응답에 담아 돌려줍니다

대가 — 모델 계산 시간이 응답 시간에 더해집니다. 서버가 켜지는 데도 오래 걸립니다. 세상이 바뀌면 같은 모델이 점점 더 틀리므로 운영 중에도 답을 계속 지켜봐야 합니다. 입력이 미리 정해져 있으면 요청마다 계산하지 않고 밤사이 몰아 계산해 두는 편이 싸고 단순합니다.

상세

요리사는 요리 학교에서 몇 년 동안 레시피를 익힙니다. 졸업한 뒤에는 식당 주방에 서서 주문이 들어올 때마다 요리를 냅니다. 손님은 요리사가 어디서 배웠는지 모릅니다. 주문한 접시가 빨리 나오기만 바랍니다.

머신러닝 모델도 이렇게 두 시기를 거칩니다. 데이터에서 규칙을 찾아 모델을 만드는 단계를 훈련이라고 부릅니다. 다 만든 모델을 서버에 띄워 두고 요청마다 답을 계산해 돌려주는 단계가 모델 서빙입니다.

모델에 입력을 넣고 답을 계산하는 한 번의 동작을 추론이라고 부릅니다. 그렇게 받은 답은 예측이라 합니다.

모델 파일은 혼자서는 아무 일도 하지 않습니다. 누군가 입력을 넣고 답을 받아 가야 쓸모가 생깁니다. 모델 서빙은 추론을 요청마다 되풀이할 수 있게 서버로 감싸는 일입니다.

이 절은 배달 앱의 도착 시간 예측 하나를 끝까지 예로 씁니다. 주문 버튼을 누르면 「도착까지 32분」이 뜹니다. 그 숫자를 계산하는 서버를 어떻게 띄우고 운영하는지를 차례로 봅니다.

훈련에서 서빙으로 넘어오는 모델 파일

훈련이 끝나면 모델은 숫자 묶음으로 굳습니다. 이 숫자들을 파라미터라고 부릅니다. 도착 시간 모델이라면 「거리 1km 마다 3분씩 더한다」 같은 규칙이 숫자로 담겨 있습니다.

훈련과 서빙은 대개 다른 기계에서 돕니다. 그래서 굳은 파라미터를 파일로 적어 넘깁니다. 메모리에 있는 값을 파일이나 바이트열로 적는 이 일을 직렬화라고 부릅니다.

서빙 서버는 켜질 때 이 모델 파일을 읽어 메모리에 다시 올립니다. 파라미터가 많은 모델은 파일이 커서 읽는 데 시간이 걸립니다. 그래서 요청마다 파일을 읽지 않고 한 번 올린 모델을 계속 씁니다.

flowchart TD
    subgraph 훈련
        A["지난 배달 기록"] --> B["규칙을 찾는다"]
        B --> C["모델 파일"]
    end
    subgraph 서빙
        D["서버가 켜질 때 읽는다"] --> E["메모리에 올린 모델"]
        F["요청"] --> E
        E --> G["응답 · 32분"]
    end
    C --> D

그림의 위 덩어리는 한 번 크게 돌고 끝납니다. 아래 덩어리는 서버가 켜져 있는 동안 요청마다 작게 돕니다. 둘을 잇는 것은 모델 파일 하나입니다.

훈련은 한 번으로 끝나지 않습니다. 새 데이터가 쌓이면 그 데이터로 모델을 다시 훈련합니다. 이 일을 재학습이라 합니다.

재학습을 거듭하면 모델 파일이 여럿 쌓입니다. 어느 파일이 운영 중인지 헷갈리지 않게 파일마다 버전을 붙여 한곳에 모아 둡니다. 이 저장소를 모델 레지스트리라고 부릅니다.

요청 하나가 지나가는 길

이 소절은 주문 하나가 들어와 「32분」이 나갈 때까지를 따라갑니다. 겉에서 보면 API(Application Programming Interface, 응용 프로그램 인터페이스) 호출 한 번입니다. API 는 다른 프로그램이 불러 쓰도록 열어 둔 입구입니다. 그 안에서 요청은 세 단계를 거칩니다.

첫 단계는 입력을 모델이 받는 꼴로 바꾸는 일입니다. 요청에는 배달 주소와 주문 시각이 글자로 옵니다. 모델은 숫자만 받으므로 주소는 거리로, 시각은 몇 시인지로 바꿉니다. 이 일을 전처리라고 부릅니다.

이렇게 바꿔서 모델에 넣는 값 하나하나를 특성이라고 부릅니다. 이 예에서는 거리와 시간대가 특성입니다. 모델은 요청 원문을 보지 못하고 특성만 봅니다.

둘째 단계가 추론입니다. 특성을 모델에 넣고 31.7 같은 숫자를 받습니다.

셋째 단계는 그 숫자를 응답에 맞게 다듬는 일입니다. 31.7 을 반올림해 32분으로 만들고 응답 본문에 담습니다. 이 일을 후처리라고 부릅니다.

sequenceDiagram
    participant 앱
    participant 서빙 서버
    participant 모델
    앱->>서빙 서버: 배달 주소 · 주문 시각
    서빙 서버->>서빙 서버: 전처리 · 거리와 시간대로 바꾼다
    서빙 서버->>모델: 특성
    모델-->>서빙 서버: 31.7
    서빙 서버->>서빙 서버: 후처리 · 반올림
    서빙 서버-->>앱: 32분

그림에서 모델이 맡는 것은 가운데 한 번의 왕복뿐입니다. 서빙 코드의 대부분은 그 앞뒤에서 값을 바꾸는 일입니다.

아래는 이 흐름을 줄인 코드입니다. load 는 모델 파일을 읽어 오는 함수, dist 는 주소를 거리로 바꾸는 함수, hour_of 는 시각에서 몇 시인지를 뽑는 함수라고 칩니다. 오른쪽 주석이 그 줄이 내는 값입니다.

Python
model = load("eta-v3.bin")  # 한 번만

def handle(req):
    km = dist(req["addr"])  # 2.4
    hr = hour_of(req["time"])  # 19
    y = model.predict([km, hr])  # 31.7
    return {"min": round(y)}  # {"min": 32}

첫 줄은 서버가 켜질 때 한 번만 돕니다. handle 안의 네 줄은 요청마다 돕니다. 가운데 predict 한 줄이 추론입니다. 위의 두 줄은 전처리, 마지막 줄은 후처리입니다.

전처리는 훈련 때와 같은 방식이어야 합니다. 훈련 때 거리를 km 로 넣었는데 서빙에서 m 로 넣으면 모델은 엉뚱한 답을 냅니다. 에러는 나지 않아서 쉽게 드러나지도 않습니다. 이렇게 훈련 때와 서빙 때 입력이 어긋나는 문제를 훈련-서빙 스큐라고 부릅니다.

빠르게 답하기와 많이 답하기

서빙 서버의 속도는 두 잣대로 잽니다. 이 소절은 그 둘과, 둘을 맞바꾸는 방법 하나를 봅니다.

요청 하나가 들어와 응답이 나갈 때까지 걸린 시간을 지연 시간이라고 부릅니다. 도착 시간은 주문 화면에 바로 떠야 하므로 지연 시간이 짧아야 합니다. 모델 계산이 무거울수록 지연 시간이 길어집니다.

평균만 보면 가끔 오래 걸린 요청이 묻힙니다. 그래서 백 건 가운데 가장 오래 걸린 한두 건이 얼마나 걸렸는지를 따로 봅니다. 이렇게 오래 걸린 쪽 끝을 꼬리 지연이라고 부릅니다.

정해진 시간 동안 처리한 요청 수는 처리량이라고 부릅니다. 저녁 시간에 주문이 몰리면 처리량이 모자라 요청이 줄을 섭니다. 줄을 선 시간도 지연 시간에 더해집니다.

모델 계산은 아주 많은 곱셈과 덧셈으로 이루어집니다. 그래서 이런 계산을 한꺼번에 여러 개 해내는 GPU(Graphics Processing Unit, 그래픽 처리 장치)에 모델을 올리기도 합니다. GPU 에서는 입력 몇 개를 함께 넣어도 하나를 넣을 때보다 시간이 크게 늘지 않는 일이 많습니다.

이 성질을 살려 들어온 요청을 잠깐 모았다가 한 번에 계산합니다. 눈에 띄지 않을 만큼 짧은 시간 동안 들어온 요청을 묶어 모델에 함께 넣습니다. 이 방법을 동적 배칭이라고 부릅니다.

flowchart TD
    subgraph 모으기
        A["요청 1"] --> D["짧게 기다리며 모은다"]
        B["요청 2"] --> D
        C["요청 3"] --> D
    end
    D --> E["모델이 한 번에 계산"]
    E --> F["응답 셋으로 나눈다"]

대가는 모으느라 기다린 만큼 지연 시간이 늘어난다는 것입니다. 처리량을 얻는 대신 지연 시간을 내주는 셈입니다. 모으는 시간을 얼마로 잡을지가 둘 사이를 고르는 설정값이 됩니다.

서버가 켜지고 늘어날 때

주문이 몰리면 서버를 더 띄워 요청을 나눠 받습니다. 부하에 따라 서버 수를 자동으로 늘리고 줄이는 일을 오토스케일링이라고 부릅니다.

모델 서빙에서는 서버를 늘리는 데도 시간이 걸립니다. 새로 켠 서버는 모델 파일을 다 읽기 전까지 요청을 받을 수 없습니다. 이렇게 막 켜진 서버가 준비를 마칠 때까지 요청을 못 받는 현상을 콜드 스타트라고 부릅니다.

그래서 서버마다 준비가 끝났는지 묻는 주소를 둡니다. 이 확인을 헬스 체크라고 부릅니다.

요청을 여러 서버에 나눠 주는 로드 밸런서는 모델을 다 올렸다고 답한 서버에만 요청을 보냅니다.

미리 계산해 두는 배치 추론

지금까지 본 도착 시간 서버는 요청이 온 순간에 계산했습니다. 이 방식을 온라인 추론이라고 부릅니다. 모델 서빙이라고 하면 대개 이쪽을 가리킵니다.

모든 예측이 요청 순간에 필요하지는 않습니다. 「이 손님에게 보여 줄 추천 가게 10곳」은 밤사이 미리 계산해 테이블에 넣어 둘 수 있습니다. 앱은 테이블을 조회하기만 합니다. 이렇게 몰아서 미리 계산하는 방식을 배치 추론이라고 부릅니다.

이름이 비슷한 동적 배칭과 헷갈리기 쉽습니다. 동적 배칭은 요청 순간에 계산하는 온라인 추론 안에서 요청 몇 건을 묶는 방법입니다. 배치 추론은 계산하는 때 자체가 요청과 떨어진, 미리 정해 둔 시각입니다.

두 방식을 네 가지로 견주면 이렇습니다.

온라인 추론 배치 추론
계산하는 때 요청이 온 순간 정해 둔 시각에 몰아서
응답에 더해지는 시간 모델 계산 시간 테이블 조회 시간
쓸 수 있는 입력 방금 들어온 값까지 계산한 시각까지의 값
모델을 띄워 두는 시간 늘 계산하는 동안만

가르는 질문은 입력이 언제 정해지느냐입니다. 도착 시간은 주문이 들어와야 주소를 압니다. 그래서 온라인 추론밖에 없습니다. 입력이 미리 정해져 있고 몇 시간 묵은 답이어도 괜찮으면 배치 추론이 싸고 단순합니다.

모델을 바꿔 끼우는 방법

모델은 한 번 띄우고 끝나지 않습니다. 재학습한 모델이 나오면 운영 중인 모델과 바꿔야 합니다. 이 소절은 바꾸다가 생기는 사고를 줄이는 방법 셋을 봅니다.

새 모델은 시험 점수가 좋아도 운영에서 더 틀릴 수 있습니다. 시험에 쓴 데이터와 운영에 들어오는 요청이 다르기 때문입니다. 그래서 한 번에 바꾸지 않고 조금씩 확인하며 바꿉니다.

섀도 배포는 새 모델에도 같은 요청을 복사해 보냅니다. 새 모델의 답은 버립니다. 사용자는 계속 옛 모델의 답을 받습니다. 두 모델의 답을 쌓아 두고 견줘서 새 모델이 이상한 답을 내지 않는지 확인합니다.

카나리 배포는 요청의 일부만 새 모델로 보냅니다. 처음엔 100건 중 1건 꼴로 보냅니다. 문제가 없으면 비율을 늘립니다. 새 모델의 답을 사용자가 받는다는 점이 섀도 배포와 다릅니다.

문제가 보이면 옛 모델로 되돌립니다. 이 일을 롤백이라고 부릅니다. 모델 레지스트리에 옛 버전 파일이 남아 있으므로 그 파일을 다시 읽혀 띄우면 됩니다.

운영 중에 지켜보는 지표

모델 서빙 서버도 일반 서버처럼 지연 시간과 에러 비율을 봅니다. 여기에 모델이라서 더 봐야 하는 것이 둘 있습니다. 둘 다 에러 없이 조용히 나빠지는 쪽입니다.

첫째는 들어오는 입력이 훈련 때와 닮았는가입니다. 값이 어느 범위에 얼마나 몰려 있는지를 분포라고 부릅니다. 훈련 때 거리 값은 대부분 3km 안쪽에 몰려 있었다고 칩시다. 배달 지역이 새 동네로 넓어지면 10km 넘는 주문이 늘어 분포가 바뀝니다.

이렇게 운영 입력의 분포가 훈련 데이터와 달라지는 현상을 분포 이동이라고 부릅니다. 모델은 처음 보는 범위의 값에서 더 많이 틀립니다. 서버는 멀쩡히 응답하므로 일부러 재지 않으면 모릅니다.

둘째는 답이 맞았는가입니다. 도착 시간은 배달이 끝나면 진짜 답이 나옵니다. 예측한 32분과 배달에 걸린 시간을 나중에 맞대어 틀린 정도를 계속 잽니다.

틀린 정도가 커지면 최근 데이터로 재학습합니다. 재학습한 모델은 앞 소절의 방법으로 다시 바꿔 끼웁니다.

훈련, 서빙, 지켜보기, 재학습은 이렇게 한 바퀴로 이어집니다. 이 바퀴를 사람 손을 덜 타고 돌게 만드는 일을 MLOps(Machine Learning Operations, 머신러닝 운영)라고 부릅니다.

관련 항목

모델 서빙에 앞서 거치는 단계

머신러닝 · 훈련 · 훈련 데이터 · 모델 · 파라미터 · 하이퍼파라미터 · 직렬화

요청 하나가 거치는 처리 단계

전처리 · 특성 · 추론 · 예측 · 후처리 · 훈련-서빙 스큐

모델 서빙이 답을 내는 방식

온라인 추론 · 배치 추론 · 온라인 예측 · 배치 예측 · 스트리밍 추론 · 배치 처리

모델 서빙의 속도를 재는 지표

지연 · 꼬리 지연 · 백분위수 · 처리량

모델 서빙을 빠르게 만드는 기법

동적 배칭 · GPU · 양자화 · 캐싱 · 모델 경량화

모델 서빙 서버를 띄우고 늘리는 인프라

API · REST · gRPC · 로드 밸런서 · 헬스 체크 · 콜드 스타트 · 오토스케일링 · 컨테이너 이미지 · Kubernetes

운영 중인 모델을 바꿔 끼우는 배포 방법

모델 레지스트리 · 섀도 배포 · 카나리 배포 · 블루-그린 배포 · 롤백

운영 중인 모델을 지켜보는 개념

모니터링 · 분포 · 분포 이동 · 데이터 드리프트 · 재학습 · MLOps

다른 이름: model serving · 서빙 · 모델 서버 · ML 서빙