8.2 KiB
์น ์๋ฒ๋ฅผ ์ํ ํ์ดํ๋ผ์ธ ์ฌ์ฉํ๊ธฐusing_pipelines_for_a_webserver
์ถ๋ก ์์ง์ ๋ง๋๋ ๊ฒ์ ๋ณต์กํ ์ฃผ์ ์ด๋ฉฐ, "์ต์ ์" ์๋ฃจ์ ์ ๋ฌธ์ ๊ณต๊ฐ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค. CPU ๋๋ GPU๋ฅผ ์ฌ์ฉํ๋์ง์ ๋ฐ๋ผ ๋ค๋ฅด๊ณ ๋ฎ์ ์ง์ฐ ์๊ฐ์ ์ํ๋์ง, ๋์ ์ฒ๋ฆฌ๋์ ์ํ๋์ง, ๋ค์ํ ๋ชจ๋ธ์ ์ง์ํ ์ ์๊ธธ ์ํ๋์ง, ํ๋์ ํน์ ๋ชจ๋ธ์ ๊ณ ๋๋ก ์ต์ ํํ๊ธธ ์ํ๋์ง ๋ฑ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค. ์ด ์ฃผ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ผ๋ฏ๋ก, ์ด ์ฅ์์ ์ ์ํ๋ ๊ฒ์ ์ฒ์ ์๋ํด ๋ณด๊ธฐ์ ์ข์ ์ถ๋ฐ์ ์ผ ์๋ ์์ง๋ง, ์ด ์ฅ์ ์ฝ๋ ์ฌ๋ฌ๋ถ์ด ํ์๋ก ํ๋ ์ต์ ์ ์๋ฃจ์ ์ ์๋ ์ ์์ต๋๋ค.ํต์ฌ์ ์ผ๋ก ์ดํดํด์ผ ํ ์ ์ dataset๋ฅผ ๋ค๋ฃฐ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ฐ๋ณต์๋ฅผ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ๋๋ค. ์๋ํ๋ฉด, ์น ์๋ฒ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์์ฒญ์ ๊ธฐ๋ค๋ฆฌ๊ณ ๋ค์ด์ค๋ ๋๋ก ์ฒ๋ฆฌํ๋ ์์คํ ์ด๊ธฐ ๋๋ฌธ์ ๋๋ค.
๋ณดํต ์น ์๋ฒ๋ ๋ค์ํ ์์ฒญ์ ๋์์ ๋ค๋ฃจ๊ธฐ ์ํด ๋งค์ฐ ๋ค์คํ๋ ๊ตฌ์กฐ(๋ฉํฐ ์ค๋ ๋ฉ, ๋น๋๊ธฐ ๋ฑ)๋ฅผ ์ง๋๊ณ ์์ต๋๋ค. ๋ฐ๋ฉด์, ํ์ดํ๋ผ์ธ(๋๋ถ๋ถ ํ์ดํ๋ผ์ธ ์์ ์๋ ๋ชจ๋ธ)์ ๋ณ๋ ฌ์ฒ๋ฆฌ์ ๊ทธ๋ค์ง ์ข์ง ์์ต๋๋ค. ์๋ํ๋ฉด ํ์ดํ๋ผ์ธ์ ๋ง์ RAM์ ์ฐจ์งํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ๋ฐ๋ผ์, ํ์ดํ๋ผ์ธ์ด ์คํ ์ค์ด๊ฑฐ๋ ๊ณ์ฐ ์ง์ฝ์ ์ธ ์์ ์ค์ผ ๋ ๋ชจ๋ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฆฌ์์ค๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ์ฐ๋ฆฌ๋ ์น ์๋ฒ๊ฐ ์์ฒญ์ ๋ฐ๊ณ ๋ณด๋ด๋ ๊ฐ๋ฒผ์ด ๋ถํ๋ฅผ ์ฒ๋ฆฌํ๊ณ , ์ค์ ์์
์ ์ฒ๋ฆฌํ๋ ๋จ์ผ ์ค๋ ๋๋ฅผ ๊ฐ๋ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐํ ๊ฒ์
๋๋ค. ์ด ์์ ๋ starlette ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์ค์ ํ๋ ์์ํฌ๋ ์ค์ํ์ง ์์ง๋ง, ๋ค๋ฅธ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๋์ผํ ํจ๊ณผ๋ฅผ ๋ณด๊ธฐ ์ํด์ ์ฝ๋๋ฅผ ์กฐ์ ํ๊ฑฐ๋ ๋ณ๊ฒฝํด์ผ ํ ์ ์์ต๋๋ค.
server.py๋ฅผ ์์ฑํ์ธ์:
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from transformers import pipeline
import asyncio
async def homepage(request):
payload = await request.body()
string = payload.decode("utf-8")
response_q = asyncio.Queue()
await request.app.model_queue.put((string, response_q))
output = await response_q.get()
return JSONResponse(output)
async def server_loop(q):
pipe = pipeline(model="google-bert/bert-base-uncased")
while True:
(string, response_q) = await q.get()
out = pipe(string)
await response_q.put(out)
app = Starlette(
routes=[
Route("/", homepage, methods=["POST"]),
],
)
@app.on_event("startup")
async def startup_event():
q = asyncio.Queue()
app.model_queue = q
asyncio.create_task(server_loop(q))
์ด์ ๋ค์ ๋ช ๋ น์ด๋ก ์คํ์ํฌ ์ ์์ต๋๋ค:
uvicorn server:app
์ด์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ค๋ณผ ์ ์์ต๋๋ค:
curl -X POST -d "test [MASK]" http://localhost:8000/
#[{"score":0.7742936015129089,"token":1012,"token_str":".","sequence":"test."},...]
์, ์ด์ ์น ์๋ฒ๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ํ ์ข์ ๊ฐ๋ ์ ์๊ฒ ๋์์ต๋๋ค!
์ค์ํ ์ ์ ๋ชจ๋ธ์ ํ ๋ฒ๋ง ๊ฐ์ ธ์จ๋ค๋ ๊ฒ์ ๋๋ค. ๋ฐ๋ผ์ ์น ์๋ฒ์๋ ๋ชจ๋ธ์ ์ฌ๋ณธ์ด ์์ต๋๋ค. ์ด๋ฐ ๋ฐฉ์์ ๋ถํ์ํ RAM์ด ์ฌ์ฉ๋์ง ์์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ ํ ๋ฉ์ปค๋์ฆ์ ์ฌ์ฉํ๋ฉด, ๋ค์๊ณผ ๊ฐ์ ๋์ ๋ฐฐ์น๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ์ถ๋ก ์ ๋จ๊ณ์ ๋ช ๊ฐ์ ํญ๋ชฉ์ ์ถ์ ํ๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฉ์ง ์์ ์ ํ ์ ์์ต๋๋ค:
์ฝ๋๋ ์๋์ ์ผ๋ก ๊ฐ๋ ์ฑ์ ์ํด ์์ฌ ์ฝ๋์ฒ๋ผ ์์ฑ๋์์ต๋๋ค! ์๋ ์ฝ๋๋ฅผ ์๋์ํค๊ธฐ ์ ์ ์์คํ ์์์ด ์ถฉ๋ถํ์ง ํ์ธํ์ธ์!(string, rq) = await q.get()
strings = []
queues = []
while True:
try:
(string, rq) = await asyncio.wait_for(q.get(), timeout=0.001) # 1ms
except asyncio.exceptions.TimeoutError:
break
strings.append(string)
queues.append(rq)
strings
outs = pipe(strings, batch_size=len(strings))
for rq, out in zip(queues, outs):
await rq.put(out)
๋ค์ ๋ง์ ๋๋ฆฌ์๋ฉด, ์ ์๋ ์ฝ๋๋ ๊ฐ๋ ์ฑ์ ์ํด ์ต์ ํ๋์์ผ๋ฉฐ, ์ต์์ ์ฝ๋๋ ์๋๋๋ค. ์ฒซ์งธ, ๋ฐฐ์น ํฌ๊ธฐ ์ ํ์ด ์์ผ๋ฉฐ ์ด๋ ์ผ๋ฐ์ ์ผ๋ก ์ข์ ๋ฐฉ์์ด ์๋๋๋ค. ๋์งธ, ๋ชจ๋ ํ ๊ฐ์ ธ์ค๊ธฐ์์ ํ์์์์ด ์ฌ์ค์ ๋๋ฏ๋ก ์ถ๋ก ์ ์คํํ๊ธฐ ์ ์ 1ms๋ณด๋ค ํจ์ฌ ์ค๋ ๊ธฐ๋ค๋ฆด ์ ์์ต๋๋ค(์ฒซ ๋ฒ์งธ ์์ฒญ์ ๊ทธ๋งํผ ์ง์ฐ์ํด).
๋จ์ผ 1ms ๊ธธ์ด์ ๋ฐ๋๋ผ์ธ์ ๋๋ ํธ์ด ๋ ์ข์ต๋๋ค.
์ด ๋ฐฉ์์ ์ฌ์ฉํ๋ฉด ํ๊ฐ ๋น์ด ์์ด๋ ํญ์ 1ms๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ฒ ๋ ๊ฒ์ ๋๋ค. ํ์ ์๋ฌด๊ฒ๋ ์์ ๋ ์ถ๋ก ์ ์ํ๋ ๊ฒฝ์ฐ์๋ ์ต์ ์ ๋ฐฉ๋ฒ์ด ์๋ ์ ์์ต๋๋ค. ํ์ง๋ง ๋ฐฐ์น ์์ ์ด ์ฌ์ฉ๋ก์ ๋ฐ๋ผ ์ ๋ง๋ก ์ค์ํ๋ค๋ฉด ์๋ฏธ๊ฐ ์์ ์๋ ์์ต๋๋ค. ๋ค์ ๋งํ์ง๋ง, ์ต์์ ์๋ฃจ์ ์ ์์ต๋๋ค.
๊ณ ๋ คํด์ผ ํ ๋ช ๊ฐ์ง ์ฌํญfew_things_you_might want_to_consider
์๋ฌ ํ์ธerror_checking
ํ๋ก๋์ ํ๊ฒฝ์์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ฌ์ง๊ฐ ๋ง์ต๋๋ค. ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ชจ์๋ผ๊ฑฐ๋, ๊ณต๊ฐ์ด ๋ถ์กฑํ๊ฑฐ๋, ๋ชจ๋ธ์ ๊ฐ์ ธ์ค๋ ๋ฐ์ ์คํจํ๊ฑฐ๋, ์ฟผ๋ฆฌ๊ฐ ์๋ชป๋์๊ฑฐ๋, ์ฟผ๋ฆฌ๋ ์ ํํด๋ ๋ชจ๋ธ ์ค์ ์ด ์๋ชป๋์ด ์คํ์ ์คํจํ๋ ๋ฑ๋ฑ ๋ง์ ๊ฒฝ์ฐ๊ฐ ์กด์ฌํฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์๋ฒ๊ฐ ์ฌ์ฉ์์๊ฒ ์ค๋ฅ๋ฅผ ์ถ๋ ฅํ๋ ๊ฒ์ด ์ข์ผ๋ฏ๋ก
์ค๋ฅ๋ฅผ ํ์ํ๊ธฐ ์ํด try...except ๋ฌธ์ ๋ง์ด ์ถ๊ฐํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
ํ์ง๋ง ๋ณด์ ์ํฉ์ ๋ฐ๋ผ ๋ชจ๋ ์ค๋ฅ๋ฅผ ํ์ํ๋ ๊ฒ์ ๋ณด์์ ์ํํ ์๋ ์๋ค๋ ์ ์ ๋ช
์ฌํด์ผํฉ๋๋ค.
์ํท ๋ธ๋ ์ดํนcircuit_breaking
์น ์๋ฒ๋ ์ผ๋ฐ์ ์ผ๋ก ์ํท ๋ธ๋ ์ดํน์ ์ํํ ๋ ๋ ๋์ ์ํฉ์ ์ง๋ฉดํฉ๋๋ค. ์ฆ, ์ด๋ ์๋ฒ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๋ฌด๊ธฐํ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๊ณผ๋ถํ ์ํ์ผ ๋ ์ ์ ํ ์ค๋ฅ๋ฅผ ๋ฐํํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์๋ฒ๊ฐ ๋งค์ฐ ์ค๋ ์๊ฐ ๋์ ๋๊ธฐํ๊ฑฐ๋ ์ ๋นํ ์๊ฐ์ด ์ง๋ ํ์ 504 ์๋ฌ๋ฅผ ๋ฐํํ๋ ๋์ 503 ์๋ฌ๋ฅผ ๋น ๋ฅด๊ฒ ๋ฐํํ๊ฒ ํ๋ ๊ฒ์ ๋๋ค.
์ ์๋ ์ฝ๋์๋ ๋จ์ผ ํ๊ฐ ์์ผ๋ฏ๋ก ๊ตฌํํ๊ธฐ๊ฐ ๋น๊ต์ ์ฝ์ต๋๋ค. ํ ํฌ๊ธฐ๋ฅผ ํ์ธํ๋ ๊ฒ์ ์น ์๋ฒ๊ฐ ๊ณผ๋ถํ ์ํญ ํ์ ์์ ๋ ์๋ฌ๋ฅผ ๋ฐํํ๊ธฐ ์ํ ๊ฐ์ฅ ๊ธฐ์ด์ ์ธ ์์ ์ ๋๋ค.
๋ฉ์ธ ์ฐ๋ ๋ ์ฐจ๋จblocking_the_main_thread
ํ์ฌ PyTorch๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ง์ํ์ง ์์ผ๋ฉฐ, ์คํ ์ค์๋ ๋ฉ์ธ ์ค๋ ๋๊ฐ ์ฐจ๋จ๋ฉ๋๋ค. ๋ฐ๋ผ์ PyTorch๋ฅผ ๋ณ๋์ ์ค๋ ๋/ํ๋ก์ธ์ค์์ ์คํํ๋๋ก ๊ฐ์ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ฌ๊ธฐ์๋ ์ด ์์ ์ด ์ํ๋์ง ์์์ต๋๋ค. ์๋ํ๋ฉด ์ฝ๋๊ฐ ํจ์ฌ ๋ ๋ณต์กํ๊ธฐ ๋๋ฌธ์ ๋๋ค(์ฃผ๋ก ์ค๋ ๋, ๋น๋๊ธฐ ์ฒ๋ฆฌ, ํ๊ฐ ์๋ก ์ ๋ง์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค). ํ์ง๋ง ๊ถ๊ทน์ ์ผ๋ก๋ ๊ฐ์ ์์ ์ ์ํํ๋ ๊ฒ์ ๋๋ค.
๋จ์ผ ํญ๋ชฉ์ ์ถ๋ก ์ด ์ค๋ ๊ฑธ๋ฆฐ๋ค๋ฉด (> 1์ด), ๋ฉ์ธ ์ฐ๋ ๋๋ฅผ ์ฐจ๋จํ๋ ๊ฒ์ ์ค์ํ ์ ์์ต๋๋ค. ์๋ํ๋ฉด ์ด ๊ฒฝ์ฐ ์ถ๋ก ์ค ๋ชจ๋ ์ฟผ๋ฆฌ๋ ์ค๋ฅ๋ฅผ ๋ฐ๊ธฐ ์ ์ 1์ด๋ฅผ ๊ธฐ๋ค๋ ค์ผ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
๋์ ๋ฐฐ์นdynamic_batching
์ผ๋ฐ์ ์ผ๋ก, ๋ฐฐ์น ์ฒ๋ฆฌ๊ฐ 1๊ฐ ํญ๋ชฉ์ ํ ๋ฒ์ ์ ๋ฌํ๋ ๊ฒ์ ๋นํด ๋ฐ๋์ ์ฑ๋ฅ ํฅ์์ด ์๋ ๊ฒ์ ์๋๋๋ค(์์ธํ ๋ด์ฉ์ batching details์ ์ฐธ๊ณ ํ์ธ์).
ํ์ง๋ง ์ฌ๋ฐ๋ฅธ ์ค์ ์์ ์ฌ์ฉํ๋ฉด ๋งค์ฐ ํจ๊ณผ์ ์ผ ์ ์์ต๋๋ค.
API์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ ์ ํ์ ๊ฐ๋ฅ์ฑ์ด ๋งค์ฐ ๋๊ธฐ ๋๋ฌธ์ ๋์ ๋ฐฐ์น ์ฒ๋ฆฌ๊ฐ ์์ต๋๋ค.
ํ์ง๋ง ๋งค์ฐ ํฐ ๋ชจ๋ธ์ธ BLOOM ์ถ๋ก ์ ๊ฒฝ์ฐ ๋์ ๋ฐฐ์น ์ฒ๋ฆฌ๋ ๋ชจ๋ ์ฌ๋์๊ฒ ์ ์ ํ ๊ฒฝํ์ ์ ๊ณตํ๋ ๋ฐ ํ์์
๋๋ค.