Transformers documentation
Web sunucusu çıkarımı
Web sunucusu çıkarımı
Bir web sunucusu, gelen istekleri bekleyen ve geldikçe onları karşılayan bir sistemdir. Bu, bir web sunucusunda Pipeline'ı bir çıkarım motoru olarak kullanabileceğiniz anlamına gelir; çünkü her gelen isteği işlemek için bir yineleyici (bir veri seti üzerinde yineleme yapar gibi) kullanabilirsiniz.
Pipeline ile bir web sunucusu tasarlamak benzersizdir, çünkü ikisi temelde farklıdır. Web sunucuları, birden fazla isteği eşzamanlı olarak işlemek için çoklu demetleme (çok iş parçacıklı, asenkron vb.) kullanır. Öte yandan Pipeline ve altındaki model, çok fazla bellek kullandıkları için paralellik için tasarlanmamıştır. Pipeline çalışırken veya yoğun hesaplama gerektiren bir iş sırasında ona tüm kullanılabilir kaynakları ayırmak en iyisidir.
Bu kılavuz, Pipeline çalıştırmanın daha ağır yükünü tek bir iş parçacığıyla ele alırken, istekleri alma ve göndermenin daha hafif yükünü bir web sunucusuyla yöneterek bu farkı nasıl aşacağınızı gösterir.
Sunucu oluşturma
Starlette, web sunucuları oluşturmak için kullanılan hafif bir çerçevedir. İstediğiniz başka bir çerçeveyi de kullanabilirsiniz ancak aşağıdaki kodda bazı değişiklikler yapmanız gerekebilir.
Başlamadan önce, Starlette ve uvicorn paketlerinin yüklü olduğundan emin olun.
!pip install starlette uvicorn
Şimdi server.py dosyasında basit bir web sunucusu oluşturabilirsiniz. Modelin gereksiz kopyalarının bellek tüketmesini önlemek için modelin sadece bir kez yüklenmesi gerekir.
Maskelenmiş token'ı doldurmak için bir pipeline oluşturun: [MASK].
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(task="fill-mask",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))Sunucuyu aşağıdaki komutla başlatın.
uvicorn server:app
Sunucuya bir POST isteği göndererek sorgulayın.
curl -X POST -d "Paris is the [MASK] of France." http://localhost:8000/Bu, aşağıdaki çıktıyı döndürmelidir.
[{'score': 0.9969332218170166,
'token': 3007,
'token_str': 'capital',
'sequence': 'paris is the capital of france.'},
{'score': 0.0005914849461987615,
'token': 2540,
'token_str': 'heart',
'sequence': 'paris is the heart of france.'},
{'score': 0.00043787318281829357,
'token': 2415,
'token_str': 'center',
'sequence': 'paris is the center of france.'},
{'score': 0.0003378340043127537,
'token': 2803,
'token_str': 'centre',
'sequence': 'paris is the centre of france.'},
{'score': 0.00026995912776328623,
'token': 2103,
'token_str': 'city',
'sequence': 'paris is the city of france.'}]İstekleri kuyruğa alma
Sunucunun kuyruklandırma mekanizması, dinamik toplu işleme (dynamic batching) gibi ilginç uygulamalar için kullanılabilir. Dinamik toplu işleme, istekleri Pipeline ile işlemeye başlamadan önce birkaç isteği biriktirir.
Aşağıdaki örnek, performans yerine okunabilirlik amacıyla sözde kod olarak yazılmıştır; özellikle şunu fark edeceksiniz:
Bir parti boyutu sınırı yoktur.
Zaman aşımı, her kuyruk alımında sıfırlanır; bu nedenle bir isteği işlemeye başlamadan önce
timeoutdeğerinden çok daha uzun süre bekleyebilirsiniz. Bu durum, ilk çıkarım isteğini de aynı miktarda geciktirir. Web sunucusu, kuyruk boş olsa bile her zaman 1 ms bekler; bu verimsizdir, çünkü bu süre çıkarımı başlatmak için kullanılabilir. Ancak toplu işleme (batching) kullanım senaryonuz için zorunluysa bu davranış mantıklı olabilir.Her getirme işleminde sıfırlamak yerine, aşağıda gösterildiği gibi tek bir 1 ms'lik son kullanma süresine sahip olmak daha iyi olacaktır.
async def server_loop(q):
pipe = pipeline(task="fill-mask", model="google-bert/bert-base-uncased")
while True:
(string, rq) = await q.get()
strings = []
queues = []
strings.append(string)
queues.append(rq)
while True:
try:
(string, rq) = await asyncio.wait_for(q.get(), timeout=1)
except asyncio.exceptions.TimeoutError:
break
strings.append(string)
queues.append(rq)
outs = pipe(strings, batch_size=len(strings))
for rq, out in zip(queues, outs):
await rq.put(out)Hata denetimi
Üretim ortamında birçok şey ters gidebilir. Bellek yetersizliği, alan yetersizliği, modelin yüklenememesi, hatalı model yapılandırması, hatalı sorgu ve daha pek çok sorunla karşılaşabilirsiniz.
try...except ifadeleri eklemek, bu hataları hata ayıklama amacıyla kullanıcıya geri döndürmek için faydalıdır. Belirli bilgileri ifşa etmemeniz gereken durumlarda bunun bir güvenlik riski oluşturabileceğini unutmayın.
Devre kesme
Sunucu aşırı yüklendiğinde kullanıcıların süresiz beklemesine neden olmak yerine 503 veya 504 hatası döndürmeye çalışın.
Bu hata türlerini uygulamak nispeten basittir, çünkü yalnızca tek bir kuyruk söz konusudur. Sunucunuzun yük altında çökmeden önce hata döndürmeye ne zaman başlayacağınızı belirlemek için kuyruk boyutuna bakın.
Ana iş parçacığını engelle
PyTorch asenkron farkındalığına sahip değildir, bu nedenle hesaplama ana iş parçacığının çalışmasını engeller.
Bu nedenle, PyTorch'un kendi ayrı iş parçacığında veya sürecinde çalıştırılması daha iyidir. Tek bir isteğin çıkarımı özellikle uzun sürdüğünde (1 sn'den fazla), bu durum daha da önemli hale gelir; çünkü bu, çıkarım sırasında gelen her sorgunun hata bile almadan önce 1 sn beklemek zorunda kaldığı anlamına gelir.
Dinamik toplu işleme
Doğru ortamda kullanıldığında dinamik toplu işleme (dynamic batching) çok etkili olabilir, ancak her seferinde yalnızca 1 istek geçiriyorsanız bu gerekli değildir (daha fazla ayrıntı için toplu çıkarım bölümüne bakın).
Update on GitHub