Back to "Bygga mestadels lucid-nmt: En Production-Ready (EasyNMT kompatibel) Översättningstjänst"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

API CUDA Docker EasyNMT FastAPI Helsinki-NLP mostlylucid-nmt Neural Machine Translation Python PyTorch Transformers

Bygga mestadels lucid-nmt: En Production-Ready (EasyNMT kompatibel) Översättningstjänst

Saturday, 08 November 2025

Inledning

En FastAPI-implementation kopierar EXAKT API för EasyNMT (https://github.com/UKPLab/EasyNMT) ett utmärkt men övergivet neural-machine-translation-projekt.

Men jag har lagt till så många fina funktioner för att öka tillförlitligheten och göra den redo att användas i ett produktionssystem.Tänk snabbt översättning själv värd...).

Sedan starten av denna blogg har en stor passion varit auto-translating bloggartiklar.

JA jag vet att "google gör detta" i webbläsare etc ... etc ... etc ... men det är inte poängen.mostlylucid-nmtJag ville veta hur man gör!

Plus att det är trevligt att vara välkomnande till människor som inte läser engelska (även om de läser engelska som ett andra språk, är det FAR svårare att tolka).Så jag arbetade ut hur man gör det; samt dela hur man bygger denna typ av system.Åh och det gav mig idéer om hur man använder det i ASP.NET för automatisk lokalisering av text (inklusive dynamisk text) med hjälp av SignalR & ett slick realtidsuppdateringssystem. (

Håll ögonen öppnaÅh och jag har gjort en demo tillgänglig här; https://nmtdemo. mostlylucid.net/demo/ den kör bara i en gammal laptop utan GPU men ger dig idén (och låter mig testa lengevity).I huvudsak; människor skriver skräp text som är SUPER bullriga för maskiner att hantera effektivt.

Så mycket var att räkna ut hur man ska arbeta kring problem med EasyNMT (det var verkligen någonsin ett forskningsprojekt).

Nu är det dags.

är utformad för att vara en kamp testad (väl översätta tiotusentals ord på här!) användbart system för alla översättningar. En sorts BabelFish API. Det har också alla lärdomar jag har från tre decennier av att bygga produktionsservrar och system. Ranging från 429 koder för att tala om för kunden att backa, returnera metadata om översättningar för att hjälpa kunder, extra endpoints för att få mer data och OF KURS en demosida

vilket gör att både jag och du kan ha en pjäs.

Iskrev ett helt systemhttp://<server>:<port>/demoatt få det att hända med ett fantastiskt projekt som heter EasyNMT.

Demo

[TOC]

Men om du bara kollade det där report så vet du att det finns ett problem... det har inte rörts på flera år.

Det är ett enkelt, snabbt sätt att få en översättning API utan att behöva betala för någon tjänst eller köra en full storlek LLM för att få översättning (slowly).

I våra tidigare inlägg diskuterade vi hur man integrerar EasyNMT med ASP.NET-applikationer för bakgrundsöversättning.

**Men med tiden började sprickorna visa sig.**Det var dags för något bättre.

**Som vanligt är allt på GitHub och alla gratis för användning etc...**Dockerdrag

  • ✓ (ingår i CPMP): Reusing loaded model for en->de (3/10 models in cache)
  • ✗ Cpu-min Ordförande: Need to load model for en->fr (3/10 models in cache)
  • gpu OrdförandeGpu-min Ordförande
  • **Demo ... se senare för demo sida!**En komplett (oftast) interaktiv demosida
  • (videller bara roten)
  • **Vad är nytt?**Innan vi dyker in i snabbstart, här är vad som gör denna version spel-förändra:

Större uppdateringar (v3.1) - Intelligens och synbarhetNytt i v3.1:

  • **Den senaste versionen ger enorma förbättringar i tillförlitlighet, prestandasynlighet och intelligent modellval!**1. Vad är det som händer?
  • Smart modell Caching med sikt- Se vad som händer.
  • Cache HIT- loggningCache MISS- loggning
  • Spårning av cachestatus: Visar utnyttjandeprocent och laddade modeller
  • Varning för vräkning: Tydliga varningar när cache är full och modeller vräks
  • Ökad kapacitet:
    ====================================================================================================
      🚀 DOWNLOADING MODEL
      Model: facebook/mbart-large-50-many-to-many-mmt
      Family: mbart50
      Direction: en → bn
      Device: GPU (cuda:0)
      Total Size: 2.46 GB
      Files: 6 main files
    ====================================================================================================
    [Progress bars for each file...]
    ====================================================================================================
      ✅ MODEL READY
      Model: facebook/mbart-large-50-many-to-many-mmt
      Translation: en → bn is now available
    ====================================================================================================
    

**: Förvald cachestorlek guppad till 10 modeller (från 6)**Enhetsloggning per modell

  • : Se exakt vilken GPU/CPU varje modell använderTvå.
  • Förbättrad nedladdningsförlopp- Jag undrar inte om den är fast.
  • Storlek före nedladdning:
    [Pivot] Languages reachable from en: 85 languages
    [Pivot] Languages that can reach bn: 42 languages
    [Pivot] Found 38 possible pivot languages
    [Pivot] Selected pivot: en → hi → bn (both legs verified)
    
  • **: Visar total nedladdningsstorlek (t.ex., "Total storlek: 2,46 GB")**Filräkning
  • : Visar antal filer att ladda nerEnhetsdisplay

: Visar målenhet (GPU/CPU) i bannerFörloppsrader

  • **: Vackra tqdm framsteg för varje fil (när på TTY)**Färdigställande banner
  • : Tydligt framgångsmeddelande när modellen är klarExempel på utmatning
  • 3. Vad är det som händer?:
    Request: en→bn with opus-mt
    Trying families: ['opus-mt', 'mbart50', 'm2m100']  ✓ All three!
    opus-mt: Failed (model doesn't exist)
    mbart50: Success! (auto-fallback worked)
    

Datadriven intelligent pivotval- Inga fler blinda försök:

  • Smart skärningslogikLoading mbart50 model on GPU (cuda:0)
  • : Hittar språk där BOTH pivot ben finnsModel loaded on device: cuda:0
  • Undviker misslyckade försökSuccessfully loaded... on GPU (cuda:0)

: Kommer inte att försöka en→es→bn om es→bn inte finnsExempel på en→bn

  • Undsättningsprioriteten->hi, hi->bn
  • : engelska → spanska → franska → tyska → kinesiska → ryska
  • Genomskinlig loggning[Pivot] Both legs loaded and cached. Ready to translate.

: Se exakt varför varje pivot valdes eller hoppade överFyra.

  • Fast automatisk reserv
    • Inga fler dubbel-tries:model_familyFörsöker alltid med bakslag.
  • : Även om föredra familjen "bör" stödja paret

Engångsförsök per familj

: Inga fler försök med samma modell två gångerExempelflöde

  • För att uppnå detta mål har kommissionen beslutat att inleda ett förfarande enligt artikel 108.2 i fördraget om Europeiska unionens funktionssätt (nedan kallat EUF-fördraget).
  • GPU-tydlighet
    • Vet alltid var dina modeller är:
  • Varje modellbelastning visar:
  • Efter belastningen bekräftas:

**Framgångsmeddelandet omfattar följande:**6. Vad är det som händer?

  • Pivot förlaga för cachelagring- Effektiv återanvändning av pivot:
  • **Båda benen av pivot cachad separat:**Nästa gång en→hi eller h→bn behövs, omedelbar cache hit!
  • **Rensa loggning:**7. Vad är det som händer?
  • Urval av förlagor per begäran

**- Fungerar redan i demo:**Demo dropdown gör det möjligt att välja opus-mt, mbart50 eller m2m100

  • Gränssnittet respekterar
  • parameter per begäran
  • Modeller cached separat efter familj för omedelbar omkoppling
  • Större uppdateringar (v3.0)
    1. Vad är det som händer?requirements-prod.txtFörstärkt demosida

**- Produktionsklart interaktivt gränssnitt:**Fullständig visningslayout (100vw/100vh) för uppslukande översättningsupplevelse

  • **Rätt tema välj rullgardiner (ingen mer klumpig inmatning/datalista)**Familjebyte i realtid (Opus-MT, mBART50, M2M100)
  • Dynamisk språkbelastning baserad på vald modellRullbara utdataområden för stora översättningar
  • **Två.**Prestandaoptimerade standardvärden
    • "Snabbt som möjligt" ur lådan:
  • GPU

**: FP16 aktiverad, TILLVERKNINGSSATS_SIZE=64, MAX_INFLIGHT=1 (optimalt för enkel GPU)**Maskiner och apparater för tillverkning av massa, papper eller papp, i.a.n.

  • : WEB_CONCURRENCY=4, MAX_INFLIGHT=4, TILLVERKNINGSSATS_SIZE=16 (använd alla kärnor)
  • Snabb avstängning
  • : 5-sekunders graciös timeout (inga fler 20-sekunder hänger)
  • Behållare stannar rent utan skrämmande SIGKILL meddelanden
    1. Vad är det som händer?
  • Optimering av produktionsbyggande

**- Mindre, snabbare bilder:**Borttagna testberoenden (pytest, pytest-cov) från produktionsbyggen

  • Sparar ~200MB per bildCPU-avbildningar: ~8-10GB (fullständig), ~3-4GB (min)
  • **GPU-avbildningar: ~12-15GB (fullständig), ~6-8GB (min)**Alla användningsområden
  • för minimalt fotavtryckFyra.

Omfattande testning och belastningsprovning- Validera allt:

  • Live API testsvit
  • (30+ tester) för hälsa, översättning, upptäckt, upptäckt
  • k6 belastningsprovning

med realistiska trafikmönsterValideringsskript mellan plattformar

  • /discover/opus-mt(PowerShell + Bash)
  • /discover/mbart50Tester för modell nedladdningar och pivot översättning fallback
  • /discover/m2m100Automatiserade röktester för snabb validering

**För att uppnå detta mål har kommissionen beslutat att inleda ett förfarande enligt artikel 108.2 i fördraget om Europeiska unionens funktionssätt (nedan kallat EUF-fördraget).**Dokumentation för installation

    • Produktionsklar från första dagen:
  • 4 inställningsscenarier: Max genomströmning, låg latens, hög konkurrent, minne-Konstruerad
  • Docker Komponera exempel med GPU/CPU-konfigurationer

Kubernetes manifesterar sig med PVC, resursbegränsningar, hälsokontrollerExempel på Azure Container Prospects

  • scottgal/mostlylucid-nmt:cpuProvningsvägledning och rekommendationer för övervakning:latestKoncurrency vs throughput kompromisser förklaras
  • scottgal/mostlylucid-nmt:cpu-min6. Vad är det som händer?
  • scottgal/mostlylucid-nmt:gpuTre modeller för familjer
  • scottgal/mostlylucid-nmt:gpu-min- Välj det bästa för dina behov:

Opus-MT: 1200+ par, bästa kvalitet (separata modeller)

  • MBART50 Ordförandelatest, min, gpu, gpu-min: 50 språk, enkel 2,4 GB modell, 2 450 par
  • M2M100 Ordförande20250108.143022: 100 språk, en 2.2GB modell, 99900 par
    1. Vad är det som händer?

Auto-Fallback- Väljer intelligent bästa tillgängliga modell:

  • **Ställ in primär familj (t.ex., Opus-MT för kvalitet)**Försöker automatiskt mBART50/M2M100 om par inte finns tillgängliga
  • Maximal täckning utan att offra kvalitet8. Vad är det som händer?
  • Modell Upptäckt- Dynamiskt fråga tillgängliga modeller:
    • Alla 1200+ par från Hugging Face

- Alla mBART50 par- Alla M2M100 par

    1. Utövande av delegeringen av befogenhet
  • Minimala bilder

- Mindre och flexibel användning:

Inga förladdade modeller (nedladdning on-demand)

Volymmappad ihållande cache

Byt modellfamiljer utan ombyggnad**- Tio. - Vad är det?**Ett enda dockningsarkiv

  • Alla varianter på ett ställe: |-----|-----------------|------|-------------|----------| | cpu(ellerlatest) | scottgal/mostlylucid-nmt:cpu) - CPU | cpu-min | scottgal/mostlylucid-nmt:cpu-min- CPU minimal | gpu | scottgal/mostlylucid-nmt:gpu- GPU med CUDA 12.6 | gpu-min | scottgal/mostlylucid-nmt:gpu-min- GPU minimal

**11. Vad är det som händer?**Korrekt översättning

    • Alla bilder inkluderar datumversioner:
  • Namngivna etiketter (
  • ) alltid peka på den senaste bygget
  • Utbytbara versionstaggar (t.ex.

) för spikning av särskilda byggnader

Fullständiga OCI-etiketter för spårningsversioner, byggdatum och git begår

docker run -d \
  --name mostlylucid-nmt \
  -p 8000:8000 \
  scottgal/mostlylucid-nmt

Det finns inga bevis för att det finns en risk för att det finns en risk för att det finns en risk för att det finns en risk för att det uppstår en allvarlig störning.

curl -X POST "http://localhost:8000/translate" \
  -H "Content-Type: application/json" \
  -d '{
    "text": ["Hello, how are you?"],
    "target_lang": "de"
  }'

Senaste basbilder

{
  "translated": ["Hallo, wie geht es Ihnen?"],
  "target_lang": "de",
  "source_lang": "en",
  "translation_time": 0.34
}

- Säkerhets- och prestandaförbättringar:

Python 3.12-slim

docker run -d \
  --name mostlylucid-nmt \
  --gpus all \
  -p 8000:8000 \
  -e EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}' \
  scottgal/mostlylucid-nmt:gpu

för CPU-avbildningar (adresser Python 3.11 sårbarheter)

CUDA 12.6

med Ubuntu 24.04 för GPU-avbildningar (senaste NVIDIA stacken)

docker run -d \
  --name mostlylucid-nmt \
  -p 8000:8000 \
  -v $HOME/model-cache:/models \
  -e MODEL_CACHE_DIR=/models \
  scottgal/mostlylucid-nmt:cpu-min

PyTorch med CUDA 12.4

docker run -d `
  --name mostlylucid-nmt `
  -p 8000:8000 `
  -v ${HOME}/model-cache:/models `
  -e MODEL_CACHE_DIR=/models `
  scottgal/mostlylucid-nmt:cpu-min

(kompatibel med CUDA 12.6 körtid)

docker run -d ^
  --name mostlylucid-nmt ^
  -p 8000:8000 ^
  -v %USERPROFILE%/model-cache:/models ^
  -e MODEL_CACHE_DIR=/models ^
  scottgal/mostlylucid-nmt:cpu-min

Alla beroenden uppdaterade till de senaste säkra versionerna

- 13. - Vad är det?

curl http://localhost:8000/healthz

Varningar för fast avskrivning

- Framtidssäkrad:

Borttagen avkorrigerad TRANSFORMERS_CACHE (numera med hjälp av HF_HOME)Kompatibel med transformatorer v5Snabbstart (5 minuter)

http://localhost:8000/demo/

Demo

### Vill du bara översätta?

Här är det absolut enklaste sättet att köra mestadels lucid-nmt:

Tillgängliga dockningsbilder

  • Alla varianter finns tillgängliga från
  • ett arkiv
  • med olika etiketter:

på Tagg på full bild Namn på storlek på Beskrivning på användningsfall på plats

  • (eller
  • på ~2.5GB på CPU med källkod på Produktion CPU-utbyggnad
  • på ~1.5GB på CPU minimal, inga förladdade modeller på volym-mappad cache, flexibel
  • GPU med CUDA 12.6 + källa på produktions GPU-utplaceringar
  • GPU minimal, inga förladdade modeller på GPU med volymmappade cache

Minimala bilder

  • rekommenderas för:
  • Produktionsinstallationer med volymomfattad cache
  • Användning av mBART50 eller M2M100 (enstaka stora modeller)

Hålla behållaren liten

  • Flexibilitet för att byta modellfamiljer utan återuppbyggnadEnkel start (Opus-MT-processor)
      1. Vad är det som händer?
    • Dra och spring:
  • **Två.**Översätt någon text:
    • Svar:
    • GPU accelererad (10x snabbare)

Kräver NVIDIA Docker körtid:

  • Med ihållande modell CacheLadda ner modeller en gång och hålla dem över behållare omstarter:
  • **Linux/Mac:**Fönster (PowerShell):
  • **Fönster (CMD):**Modeller laddas ner automatiskt vid första användning och finns kvar i din lokala katalog!

Hälsokontroll:

  • Det är 5 minuters snabbstart!
    • **För produktion installation, konfiguration och avancerade funktioner, hålla läsning.**Interaktiv demosida
    • Tjänsten inkluderar en fullfjädradinteraktiv demosida
    • **som gör det enkelt att testa översättningar utan att skriva någon kod.**Få tillgång till den på:
  • Demografiska egenskaper

Demosidan ger en komplett översättningstestningsmiljö med:

  1. Vad är det som händer?
// Example: Translating a 5000-word article
Input: Long article with multiple paragraphs

Step 1: Split by paragraphs (preserves structure)
  → Paragraph 1 (800 chars)
  → Paragraph 2 (1200 chars)
  → Paragraph 3 (600 chars)
  ...

Step 2: Group into ~1000 character chunks
  → Chunk 1: Paragraphs 1-2
  → Chunk 2: Paragraph 3-4
  → Chunk 3: Paragraphs 5-6

Step 3: Translate each chunk sequentially
  → Shows progress: "Translating chunk 1/3..."
  → Shows progress: "Translating chunk 2/3..."
  → Shows progress: "Translating chunk 3/3..."

Step 4: Reassemble with paragraph breaks
  → Final output: Complete translated article with preserved formatting

Språkval

Automatiskt befolkade språkdroppar från live-tjänsten

  • Byt källkod/målspråk med ett klick
  • Stöder alla 100+ språk konfigurerade i tjänsten
  • Två.
  • Smart textknäppning

Hanterar automatiskt stora textingångar oavsett storlek

  • Intelligent uppdelning efter punkter, bevara dokumentstruktur
  • Faller tillbaka till straffdelning för mycket långa punkter
  • Visar förlopp för multi-chunk översättningar ("Översättning bit 2/5...")
  • Sömlös återmonterar bitar med rätt avstånd

3. Vad är det som händer?

  • Påvisande av språk
  • Upptäck källspråket med ett klick
  • Fyller automatiskt i dropdown för källspråk
  • Fungerar med text upp till 5000 tecken

Fyra.

  1. Avancerade alternativ:

    • Strålstorlek
    • : Kontroll översättning kvalitet (1-10)
    • Högre värden = bättre kvalitet men långsammare
    • Lägre värden = snabbare genomströmning
  2. Skadefördelning:

    • : Växla automatisk meningsdelning
    • Aktiverad (standard): Delar långa texter i meningar för bättre kvalitet
    • Inaktiverad: Översätter hela texten som ett block (snabbare för korta texter)
    • För att uppnå detta mål har kommissionen beslutat att inleda ett förfarande enligt artikel 108.2 i fördraget om Europeiska unionens funktionssätt (nedan kallat EUF-fördraget).
  3. Statistik i realtid:

    • Översättningstid
    • : Visar faktisk tid för översättning på serversidan
    • Teckenräkning
    • : Live räkna som du skriver

Statusindikator

: Idle → Translating → Klar/Error

  • **6. Vad är det som händer?**Förlaga till familjeupptäckt
  • **Utforska tillgängliga översättningspar för varje modellfamilj:**Opus-MT
  • : 1200+ språkparMBART50 Ordförande
  • : 50 språk, 2 450 parM2M100 Ordförande

: 100 språk, 9 900 par/demo/Se exakt vilka språkpar som finns tillgängliga innan du översätter

Hur textknäppning fungerar

Demo implementerar intelligent text bitning på klientsidan:Varför använda demo?SnabbtestTestöversättningar utan skrivningskodValidera språkparets tillgänglighet

Jämför översättningskvalitet med olika balkstorlekar

  1. **Provkantsfall (emoji, symboler, specialtecken)**Utvecklingsstöd
  2. Se exakt API-förfrågan/responsformatKontrollera hälso- och sjukvården innan du integrerar
  3. Provningsprestanda med olika textstorlekarUpptäck tillgängliga modellfamiljer
  4. KundreferensVisar korrekta API- användningsmönster
  5. **Visar felhantering (429, språkdetektering)**Exempel på genomförande av styckning
  6. Real-world retry logikExempel på användningEnkel översättning.
  7. **Klistra in texten: "Hej, hur mår du idag?"**Välj mål: Tyska
  8. **Klicka på "Översätt"**Resultat: "Hallo, wie geht es Ihnen heute?"

Översättning av långa dokument

Klistra in hela blogginlägget (5000 + ord)Demo skär den automatiskt i hanterbara bitarVisar förlopp när varje bit översätter

Returnerar fullständigt översatta dokument

Påvisande av språkKlistra in text på okänt språkKlicka på "Detektera språk"

Demo identifierar språk och uppdateringar dropdown

  • Redo att översätta omedelbartTekniska detaljer
  • **Demosidan är:**Delar och tillbehör till maskiner enligt nr 8456 eller till hjälpapparater för sådana maskiner eller apparater, inte nämnda eller inbegripna någon annanstans i detta kapitel, inte nämnda eller inbegripna någon annanstans i detta kapitel
  • : Enkel HTML-fil med inbäddat JavaScriptNollberoenden
  • : Inga externa bibliotek krävsMobilvänlig
  • : Responsiv design fungerar på alla enheterProduktion färdig
  • : Samma bitningslogik kan användas i dina apparFå tillgång till live demo på

På din löpande instans!

  • Problemen med EasyNMTDet här är inte att dumpa på
  • LättNMTDet gjorde så att inget annat kunde och jag har byggt
  • Ett stort antal projekt som använder den
  • **Det börjar bara bli långt i tanden så... vilka problem har vi?**Herregud, det är många.
  • **EasyNMT byggdes för nästan tio år sedan.**Tekniken har gått vidare... plus att det aldrig var tänkt att vara ett system på produktionsnivå.
  • **Här är några av frågorna:**Det kraschar... en LOT.

Den är inte utformad för att återhämta sig från problem så ofta faller bara över.

  • **Det är SUPER PICKY om dess input.**Smilisar, symboler, även siffror kan förvirra det.
  • **Den är inte avsedd för någon last.**Se ovan.
  • **Det var aldrig konstruerat för att vara.**Den är inte utformad för att uppdatera sina modeller
  • **eller vara (lätt) byggd med inbyggda modeller.**Dess GPU CUDA grejer är uråldriga
  • **Så långsammare än vad det behöver vara.**Du kan inte fixa nåt.
  • **Python-koden är på repo men igen.**Inte så bra.

**Inget mottryck eller köande.**Skicka för många förfrågningar och det bara kölar över.MODEL_FAMILYIngen observationsförmåga.

# Opus-MT (default, best quality)
MODEL_FAMILY=opus-mt

# mBART50 (50 languages, single model)
MODEL_FAMILY=mbart50

# M2M100 (100 languages, broadest coverage)
MODEL_FAMILY=m2m100

När saker går fel, du flyger blind.

Lösningen: MestadelsLucid-NMTSå...Jag bestämde mig för att bygga en ny och förbättrad EasyNMT, nuMest lucid-nmt


  1. Detta är inte bara ett lapp jobb; det är en komplett omskrivning med produktionsanvändning i åtanke.MODEL_FAMILYHär är vad som gör det bättre:opus-mtStöd för familjer i flera modeller
  2. MestadelsLucid-NMT stöder nu
  3. Tre översättningsmodellfamiljer
  4. , ger dig flexibilitet baserat på dina behov:

Opus-MT (Helsinki-NLP) - Förval

# Set primary to Opus-MT (best quality)
MODEL_FAMILY=opus-mt
AUTO_MODEL_FALLBACK=1
MODEL_FALLBACK_ORDER=opus-mt,mbart50,m2m100

# Request Ukrainian → French
# 1. Try Opus-MT first (not available)
# 2. Automatically fall back to mBART50 (available!)
# 3. Translation succeeds with mBART50

Täckning:

  • 1200+ översättningspar för 150+ språkArkitektur:
  • Separat modell per översättningsriktningKvalitet:
  • Bästa övergripande översättningskvalitetAnvändningsfall:
  • Produktionsöversättningar där kvalitetsfrågorFörlagans storlek:

300–500 MB per riktning

# Enable auto-fallback (default: enabled)
AUTO_MODEL_FALLBACK=1

# Set fallback priority (default: opus-mt → mbart50 → m2m100)
MODEL_FALLBACK_ORDER="opus-mt,mbart50,m2m100"

# Disable for strict single-family mode
AUTO_MODEL_FALLBACK=0

Exempel:

Engelska→Tyska är en annan modell än tyska→engelska

  1. **mBART50 (Facebook)**Täckning:
  2. 50 språk, all-to-all översättningArkitektur:
  3. En enda flerspråkig modellKvalitet:
  4. God kvalitet, särskilt för stora språkAnvändningsfall:
  5. Utrymmesbegränsade utplaceringar eller många språkparFörlagans storlek:
  6. **~2.4GB (en enda modell för alla 50 språk)**Fördel:
  7. En modell hanterar 2 450 översättningsparM2M100 (Facebook)
  8. **Täckning:**100 språk, all-to-all översättning
  9. **Arkitektur:**En enda flerspråkig modell
  10. **Kvalitet:**God kvalitet med bredast språktäckning
  11. **Användningsfall:**Maximal språktäckning-minFörlagans storlek:

~2.2GB (en enda modell för alla 100 språk)

Fördel:

En modell hanterar 9,900 översättningspar

Växling är lätt

    • Bara ställa in
  • miljövariabel:
  • Automatisk modell Family Fallback - NY!

En av de mest kraftfulla nya funktionerna är

  • automatisk reserv mellan modellfamiljer
  • Detta garanterar maximal språkpar täckning samtidigt prioritera översättning kvalitet.

**Hur det fungerar:**Du ställer in en primär

  • **(t.ex.**för bästa kvalitet)
  • När du begär ett översättningspar som inte är tillgängligt i den primära familjenSystemet testar automatiskt nästa familj i reservordning
  • Detta fortsätter tills en lämplig modell hittasExempel på scenario:

Fördelar:

Maximal täckning:

Stöd 100+ språk utan att hantera flera utplaceringar

  • Kvalitetsprioritering:
  • Använd alltid den bästa tillgängliga modellen för varje par
  • Nollinställning:
  • Fungerar automatiskt, ingen manuell åtgärd behövs

Transparent loggning:

  • Se vilken modellfamilj som användes för varje översättning
  • Inställning:
  • Denna funktion är perfekt för produktionsmiljöer där du vill ha maximal täckning utan att offra kvalitet!
  • Viktiga förbättringar

Flera modeller för familjestöd

# NMT: Fits on a USB stick
du -sh model-cache/
2.5G    model-cache/

# LLM: Needs serious storage
du -sh llama-models/
140G    llama-models/

- Välj mellan Opus-MT (1200+ par), mBART50 (50 språk), eller M2M100 (100 språk).

Förlaga till resultatmått för upptäckt

    • Dynamiskt fråga tillgängliga modeller från Hugging Face för varje familj.
  • Robust hantering av indata
    • Är det Emoji?
  • Siffror?

Symboler?

  • Sätt igång.
  • Tjänsten innehåller nu omfattande input sanitisering och symbol maskering.
  • Begär köning och mottryck
    • Inbyggd semaforbaserad kö med intelligenta försök efter beräkningar.

LRU-modell cachelagring

  • Hanterar automatiskt VRAM genom att vräka gamla modeller när cachen är full. |--------|------|------| Modernt stöd för CUDA
  • Använder PyTorch med CUDA 12.6, stöder FP16/BF16 för 2x hastighetsförbättringar. Produktionsklar observerbarhet
  • Hälsokontroller, beredskaps sonder, cachestatus, strukturerad loggning. Förtjusande avstängning

Inga fler övergivna förfrågningar eller korrupta stater.

EasyNMT-kompatibelt API

    • Drop-in ersätter befintliga integrationer.
  • Reträtt för översättning av pivot
    • Om ett direkt språkpar inte är tillgängligt, leder du automatiskt genom engelska (eller din valda pivot).
  • Minimala Docker-bilder
    • Ny

varianter med volymmappad cache för mindre installationer.

  • ⚠️ Sometimes adds interpretations not in original
  • ⚠️ Quality varies with prompt phrasing
  • ⚠️ Can be "creative" with technical terms
  • ⚠️ Needs careful prompt engineering
  • ⚠️ Unpredictable with edge cases

Varför NMT över LLMs för översättning?

Input: "The API returns a 429 status code when rate limited."

NMT (Opus-MT): "Die API gibt einen 429-Statuscode zurück, wenn sie ratenbegrenzt ist."
(Accurate, preserves technical terms)

LLM (might do): "Die API sendet den Fehlercode 429, wenn zu viele Anfragen gestellt werden."
(Interprets rather than translates, adds context not in original)

Du kanske undrar: "Varför använda en dedikerad NMT-tjänst när LLMs som GPT-4, Claude, eller Llama kan översätta?" Bra fråga.

Här är verklighetskontrollen baserad på produktionsanvändning:

  • Hastighet: 10-100x Snabbare
  • NMT (mestadelslylucid-nmt):
  • CPU: ~0,3-1,0 sekunder per mening
  • GPU (FP16): ~0.05-0,2 sekunder per mening
  • Partibearbetning: 50+ meningar/sekund på GPU
  • LLM:

GPT-4: 3-10 sekunder per begäran (API latens + generation)

  • Llama 3 70B: 5-15 sekunder per mening (lokal)
  • Claude 3: 2-8 sekunder per begäran (API latens)
  • Verkligt exempel:
  • Översättning av ett 1000-ords blogginlägg:
  • Mest lucid-nmt (GPU)

: 5-10 sekunder

GPT-4 API**: 30-60 sekunder**Lokala Llama 70B

  • : 2-5 minuter
  • När du automatiskt översätter hundratals blogginlägg till 12+ språk är den hastighetsskillnaden MASSIV.
  • Modellstorlek: 500MB vs 140GB
  • NMT-modeller:

Opus-MT (per riktning): 300-500MB

mBART50 (alla 50 språk): 2.4GBM2M100 (alla 100 språk): 2.2GB

Totalt för 100+ språk: ~2.2GB

flowchart LR
    A[HTTP Client] --> B[API Gateway]
    B --> C[Translation Endpoint]
    C --> D{Has Capacity?}
    D -->|Yes| E[Translation Service]
    D -->|No| F[Queue with 429]
    F --> E
    E --> G[Process Pipeline]
    G --> H[Get Model from Cache]
    H --> I[Translate]
    I --> J[Return Response]
    J --> A

LLM:

  1. Llama 3 8B: ~16GBLlama 3 70B: ~140GB
  2. Mixtral 8x7B: ~90GBGPT-4: Ej tillgängligt för självvärdande
  3. **Inverkan vid lagring:**Resurskrav: Laptop vs Server Farm
    • **Utplacering av NMT-processor:**Körs bra på: 2 processorkärnor, 4 GB RAM
    • Docker bild: 1.5-2.5GBSlutsatser: Endast CPU, ingen GPU behövsRetry-AfterKostnad: $10-20/månad VPS
  4. **LLM-krav:**Llama 3 70B: Behov 80GB+ VRAM (A100 GPU)
    • Mindre 7B-13B modeller: Fortfarande 16-32GB RAM minimum
    • API-kostnader: $0.03-0.30 per 1000 polletter (kompletterar snabbt!)
    • Självvärdighet: $1000+/månad för seriös GPU
    • Real kostnad jämförelse för 10 000 blogginlägg översättningar:
  5. på metod på bekostnad av tidtill största delen lucid-nmt (CPU) på $20/månad VPS på 2-3 timmar
    • De flesta lucid-nmt (GPU) på 50 dollar/månad GPU VPS 15-30 minuter
    • på GPT-4 API på $ 150-300 8-15 timmar
    • på Claude API på $200-400 på 6-12 timmar
  6. Lokal Llama 70B $ 1000+/månad hårdvara på 20-40 timmarKvalitet: Syfte-Built vs General-Purpose

**NMT styrkor:**Utbildad särskilt för översättningRetry-AfterKonsekvent kvalitet (samma indata = samma utdata)

Inga "hallucinationer" - ren översättning

Hanterar tekniskt innehåll, kod, formatering väl

Ingen snabb konstruktion behövs

sequenceDiagram
    participant Client
    participant API
    participant Queue
    participant Translator
    participant Cache
    participant Model

    Client->>API: POST /translate
    API->>Queue: Acquire slot

    alt Queue has space
        Queue-->>API: Slot acquired
        API->>Translator: Process translation
        Translator->>Translator: Sanitize input
        Translator->>Translator: Split sentences
        Translator->>Translator: Chunk text
        Translator->>Translator: Mask symbols
        Translator->>Cache: Get model (en→de)

        alt Cache hit
            Cache-->>Translator: Return cached model
        else Cache miss
            Cache->>Model: Load from Hugging Face
            Model-->>Cache: Pipeline loaded
            Cache->>Cache: Evict old if at capacity
            Cache-->>Translator: Return model
        end

        Translator->>Model: Translate batches
        Model-->>Translator: Translations
        Translator->>Translator: Unmask symbols
        Translator->>Translator: Post-process
        Translator-->>API: Translations
        API->>Queue: Release slot
        API-->>Client: 200 OK + translations
    else Queue full
        Queue-->>API: Overflow error
        API-->>Client: 429 Too Many Requests\nRetry-After: X seconds
    end

Utmaningar för LLM:

Exempel på scenario:

graph LR
    A[Raw Input] --> B{Sanitize?}
    B -->|Yes| C[Check Noise]
    B -->|No| D[Split Sentences]
    C -->|Is Noise| Z[Return Placeholder]
    C -->|Valid| D

    D --> E[Enforce Max Length]
    E --> F[Chunk for Batching]
    F --> G{Symbol Masking?}

    G -->|Yes| H[Mask Digits/Punct/Emoji]
    G -->|No| I[Translate]
    H --> I

    I --> J{Direct Model?}
    J -->|Available| K[Direct Translation]
    J -->|Not Available| L{Pivot Fallback?}

    L -->|Yes| M[src→en→tgt]
    L -->|No| Z
    K --> N[Unmask Syis robust input handling. Here's what happens:

**Noise Detection:**
- Strips control characters (except \t, \n, \r)
- Checks minimum character count (default: 1)
- Calculates alphanumeric ratio (default: must be ≥20%)
- Rejects pure emoji, pure punctuation, or pure whitespace

**Symbol Masking:**
Why mask symbols? Translation models are trained on text, not emoji or special symbols. These can confuse them or get mangled. So we:

1. Extract all digits, punctuation, and emoji as contiguous runs
2. Replace them with sentinel tokens: `⟪MSK0⟫`, `⟪MSK1⟫`, etc.
3. Translate the masked text
4. Restore the original symbols in their positions

Example:

Input: "Hello 👋 world! Price: $99.99" När var och en ska användas (👋) (!) (:) ($99.99)


**Post-Processing:**
After translation, we remove "symbol loops" - repeated symbols that weren't in the source:

Använd NMT (mestadelslylucid-nmt) när: Du behöver konsekvent, snabb översättning i skala Budgetfrågor (självvärdighet eller hög volym)


### Sentence Splitting & Chunking

Long texts get split intelligently:

```mermaid
graph TD
    A[Long Text] --> B[Split on . ! ? …]
    B --> C{Sentence > 500 chars?}
    C -->|Yes| D[Split on word boundaries]
    C -->|No| E[Keep sentence]
    D --> E

    E --> F[Group into chunks ≤900 chars]
    F --> G[Translate each chunk]
    G --> H[Join with space]

Du översätter tekniskt innehåll, kod, strukturerad data

  • Du behöver deterministisk utmatning (samma indata = samma utdata)
  • Du vill köra på CPU eller blygsam hårdvara
  • Du bygger automatiska översättningsrörledningar.

Använd LLMs när:

Du behöver kreativ anpassning, inte bokstavlig översättning

stateDiagram-v2
    [*] --> CheckCache
    CheckCache --> CacheHit: Model exists
    CheckCache --> CacheMiss: Model not loaded

    CacheHit --> MoveToEnd: Update LRU order
    MoveToEnd --> ReturnModel

    CacheMiss --> CheckCapacity
    CheckCapacity --> LoadModel: Space available
    CheckCapacity --> EvictOldest: Cache full

    EvictOldest --> MoveToCPU: Free VRAM
    MoveToCPU --> ClearCUDA: torch.cuda.empty_cache()
    ClearCUDA --> LoadModel

    LoadModel --> AddToCache
    AddToCache --> ReturnModel
    ReturnModel --> [*]

Sammanhang och kulturell nyansering betyder mer än snabbhet

  • Du gör översättningar med låg volym.
  • Du måste översätta + sammanfattning + skriva om i ett steg
  • Du är okej med rörliga kostnader och långsammare bearbetning
  • Bottenlinjen
  • För

automatiserad översättning av bloggar

(mitt användningsfall) NMT är den tydliga vinnaren:

# Semaphore limits concurrent translations
MAX_INFLIGHT = 1  # On GPU, 1 at a time for efficiency
MAX_QUEUE_SIZE = 1000  # Up to 1000 waiting

# When full:
# - Returns 429 Too Many Requests
# - Includes Retry-After header
# - Estimates wait time based on average duration

Översätter 100+ blogginlägg till 12 språk på ~30 minuter (GPU)

avg_duration = 2.5 seconds (tracked with EMA)
waiters = 100
slots = 1
estimated_wait = (100 / 1) * 2.5 = 250 seconds
clamped = min(250, 120) = 120 seconds
Retry-After: 120

Körs på en $ 50 / månad VPS

Konsekvent kvalitet på alla tjänster

graph LR
    A[Ukrainian Text] --> B{Direct uk→fr?}
    B -->|Exists| C[Translate Directly]
    B -->|Missing| D[Pivot via English]

    D --> E[uk→en]
    E --> F[en→fr]
    F --> G[French Result]
    C --> G

Total installation: En Docker-behållare

Prova detta med LLMs skulle kosta hundratals dollar per månad i API-avgifter eller kräva en $ 2000 + GPU-server att självvärd.

Enbart hastighetsskillnaden gör NMT till det enda praktiska valet för produktionsöversättningar.

TL;DR:

NMT är avsedd för översättning, körs på blygsam hårdvara, och är 10-100x snabbare än LLMs. Om du behöver snabb, konsekvent, kostnadseffektiv översättning i skala, NMT vinner hands down.

# src/core/cache.py
from collections import OrderedDict
import torch

class LRUPipelineCache:
    """LRU cache that automatically cleans up GPU memory when evicting models."""

    def __init__(self, capacity: int):
        self.cache = OrderedDict()  # Maintains insertion order
        self.capacity = capacity

    def get(self, key: str):
        """Get model from cache, moves it to end (most recently used)."""
        if key not in self.cache:
            return None
        self.cache.move_to_end(key)  # Mark as recently used
        return self.cache[key]

    def put(self, key: str, value):
        """Add model to cache, evicting oldest if at capacity."""
        if key in self.cache:
            self.cache.move_to_end(key)
        else:
            self.cache[key] = value

        # If cache is full, evict the oldest model
        if len(self.cache) > self.capacity:
            oldest_key, oldest_pipeline = self.cache.popitem(last=False)

            # MAGIC: Move evicted model to CPU to free GPU memory
            try:
                oldest_pipeline.model.to("cpu")
                if torch.cuda.is_available():
                    torch.cuda.empty_cache()  # Tell GPU to release memory
                logger.info(f"Evicted {oldest_key}, freed GPU memory")
            except Exception as e:
                logger.warning(f"Failed to clean GPU memory: {e}")

Översikt över arkitekturen

  • OrderedDictBegärans flöde är enkelt:
  • Kundskickar översättningsbegäran till API Gateway (Gunicorn + Uvicorn-arbetare)
  • API- gatewayNamerutter till Translation Endpoint
  • Kapacitetskontroll: Systemkontroller om den har kapacitet att hantera begäran

Ja, det är jag.

→ Begäran går till översättningstjänsten omedelbart

# src/services/model_manager.py
def get_pipeline(self, src: str, tgt: str):
    """Try to get translation model, with automatic fallback to other providers."""

    # Determine which model families support this language pair
    families_to_try = []

    if config.AUTO_MODEL_FALLBACK:
        # Try families in priority order: opus-mt → mbart50 → m2m100
        for family in config.MODEL_FALLBACK_ORDER.split(","):
            if self._is_pair_supported(src, tgt, family.strip()):
                families_to_try.append(family.strip())

    # Try each family until one succeeds
    last_error = None
    for family in families_to_try:
        try:
            model_name, src_lang, tgt_lang, _ = self._get_model_name_and_langs(src, tgt, family)

            if family != config.MODEL_FAMILY:
                logger.info(f"Using fallback '{family}' for {src}->{tgt}")

            # Load the model from HuggingFace
            pipeline = transformers.pipeline(
                "translation",
                model=model_name,
                device=device_manager.device_index,
                src_lang=src_lang,
                tgt_lang=tgt_lang
            )

            self.cache.put(f"{src}->{tgt}", pipeline)
            return pipeline

        except Exception as e:
            last_error = e
            logger.warning(f"Family '{family}' failed for {src}->{tgt}: {e}")
            continue  # Try next family

    # All families failed
    raise ModelLoadError(f"{src}->{tgt}", last_error)

Ej tillämpligt

  • → Begär köad, klienten får HTTP 429 medHuvud
  • Översättningstjänstbehandlar begäran genom rörledningen
  • Insatssanering och meningsdelningSymbolmaskering (emojis, specialtecken)
  • Översättning med cachade modellerSymbolisk avmaskning och efterbehandling

Förlaga Cache

(LRU) tillhandahåller översättningsmodeller:

# src/services/queue_manager.py
import asyncio
from contextlib import asynccontextmanager

class QueueManager:
    """Manages request queuing and backpressure."""

    def __init__(self, max_inflight: int, max_queue: int):
        self.semaphore = asyncio.Semaphore(max_inflight)  # Limit concurrent translations
        self.max_queue_size = max_queue
        self.waiting_count = 0
        self.inflight_count = 0
        self.avg_duration_sec = 5.0  # Exponential moving average

    @asynccontextmanager
    async def acquire_slot(self):
        """Try to get a translation slot, track metrics, handle queueing."""

        # Check if queue is too full
        if self.waiting_count >= self.max_queue_size:
            # Calculate how long client should wait before retrying
            retry_after = self._estimate_retry_after()
            raise QueueOverflowError(self.waiting_count, retry_after)

        self.waiting_count += 1
        try:
            # Wait for available slot (this is the queue!)
            await self.semaphore.acquire()
            self.waiting_count -= 1
            self.inflight_count += 1

            start_time = time.time()
            yield  # Let the translation happen

            # Update average duration for retry-after estimates
            duration = time.time() - start_time
            alpha = config.RETRY_AFTER_ALPHA  # Smoothing factor (0.2)
            self.avg_duration_sec = alpha * duration + (1 - alpha) * self.avg_duration_sec

        finally:
            self.inflight_count -= 1
            self.semaphore.release()

    def _estimate_retry_after(self) -> int:
        """Smart calculation: how many waiting / how many slots * avg time per request."""
        if self.inflight_count == 0:
            return config.RETRY_AFTER_MIN_SEC

        # If 10 people waiting and 2 slots available, and each takes 5 seconds:
        # retry_after = (10 / 2) * 5 = 25 seconds
        retry_sec = (self.waiting_count / self.semaphore._value) * self.avg_duration_sec

        # Clamp between min and max
        return max(
            config.RETRY_AFTER_MIN_SEC,
            min(int(retry_sec), config.RETRY_AFTER_MAX_SEC)
        )

Cache hit → Snabb respons

  • Cache miss → Ladda från HuggingFace HubCache full → Auto-evict gamla modeller, rensa CUDA minnemax_inflight)
  • Svar (@asynccontextmanageråtervänder till klienten
  • **Nyckeldesign:**Mottrycksmekanismen (queue + HTTP 429) förhindrar sammanbrott under belastning.
  • När överväldigad, tjänsten köer förfrågningar i stället för att dö, ger kunderna intelligent försök timing viaHuvuden.
  • Hur det fungerar: DjupdykningBegär flöde

När en översättning begäran kommer in, här är vad som händer:

Inmatningsbearbetningsrör

# src/utils/symbol_masking.py
import re

def mask_symbols(text: str) -> tuple[str, dict[str, str]]:
    """Replace special symbols with placeholders before translation."""

    originals = {}
    masked_text = text
    placeholder_counter = 0

    # Pattern: Match emojis, symbols, special punctuation
    # \U0001F300-\U0001F9FF = emoji range
    # [\u2600-\u26FF\u2700-\u27BF] = misc symbols
    symbol_pattern = re.compile(
        r'[\U0001F300-\U0001F9FF\u2600-\u26FF\u2700-\u27BF'
        r'\u00A9\u00AE\u2122\u2139\u3030\u303D\u3297\u3299]+'
    )

    for match in symbol_pattern.finditer(text):
        symbol = match.group()
        placeholder = f"__SYMBOL_{placeholder_counter}__"
        originals[placeholder] = symbol
        masked_text = masked_text.replace(symbol, placeholder, 1)
        placeholder_counter += 1

    return masked_text, originals

def unmask_symbols(text: str, originals: dict[str, str]) -> str:
    """Restore original symbols after translation."""
    for placeholder, original in originals.items():
        text = text.replace(placeholder, original)
    return text

Tjänsten använder en sofistikerad flerstegsrörledning för att hantera rörig riktig text:

# Before translation:
text = "Hello! 👋 Check out this cool feature 🚀"

# Mask symbols:
masked, originals = mask_symbols(text)
# masked = "Hello! __SYMBOL_0__ Check out this cool feature __SYMBOL_1__"
# originals = {"__SYMBOL_0__": "👋", "__SYMBOL_1__": "🚀"}

# Translate the masked text:
translated = translate(masked, "de")  # → "Hallo! __SYMBOL_0__ Schau dir diese coole Funktion an __SYMBOL_1__"

# Unmask symbols:
final = unmask_symbols(translated, originals)
# final = "Hallo! 👋 Schau dir diese coole Funktion an 🚀"

Maskerad: "Hej, det här är världen"

  • **Källa: "Hej världen"**Dålig översättning: "Hola mundo!!!!!!"
  • Rengörs: "Hola mundo" # Tar bort !!!! loopDetta säkerställer följande:👋Modeller kvävs inte av enorma ingångar__SYMBOL_0__Vi kan batch effektivt
  • Sammanhanget bevaras inom rimliga gränserModell Caching och minneshantering

LRU-cachen är smart om GPU-minnet:

Därför är detta viktigt:

# src/utils/text_processing.py
def chunk_sentences(sentences: list[str], max_chars: int = 900) -> list[list[str]]:
    """Group sentences into chunks that fit within model's max input length."""

    chunks = []
    current_chunk = []
    current_length = 0

    for sentence in sentences:
        sentence_len = len(sentence)

        # If this sentence alone is too long, it goes in its own chunk
        if sentence_len > max_chars:
            if current_chunk:
                chunks.append(current_chunk)
                current_chunk = []
                current_length = 0
            chunks.append([sentence])
            continue

        # If adding this sentence exceeds limit, start new chunk
        if current_length + sentence_len + 1 > max_chars:
            chunks.append(current_chunk)
            current_chunk = [sentence]
            current_length = sentence_len
        else:
            current_chunk.append(sentence)
            current_length += sentence_len + 1  # +1 for space

    # Don't forget the last chunk!
    if current_chunk:
        chunks.append(current_chunk)

    return chunks

def split_sentences(text: str, max_sentence_chars: int = 500) -> list[str]:
    """Split text into sentences, enforcing max length."""

    # Split on common sentence terminators
    sentences = re.split(r'([.!?…]+\s+)', text)

    result = []
    for sentence in sentences:
        if not sentence or sentence.isspace():
            continue

        # If sentence is too long, split on word boundaries
        if len(sentence) > max_sentence_chars:
            words = sentence.split()
            current = []
            current_len = 0

            for word in words:
                if current_len + len(word) + 1 > max_sentence_chars:
                    result.append(' '.join(current))
                    current = [word]
                    current_len = len(word)
                else:
                    current.append(word)
                    current_len += len(word) + 1

            if current:
                result.append(' '.join(current))
        else:
            result.append(sentence.strip())

    return result

GPU-minnet är värdefullt

  • Översättningsmodellerna är 300-500MB varderaLastmodellerna är långsamma (1-3 sekunder).!?…Vi håller de 6 senaste modellerna heta
  • Gamla modeller vräks automatisktKöa och mottryck
  • **Istället för att krascha under belastning, tjänsten köer begäran:**Uppskattningen är smart:
  • Översättning av Pivot FallbackAlla språkpar har inte direkt modeller på Hugging Face.

Lösning?

Pivot genom engelska:

# src/services/model_discovery.py
import httpx
from datetime import datetime, timedelta

class ModelDiscoveryService:
    """Discovers available translation models with 1-hour cache."""

    def __init__(self):
        self._cache = {}  # Cache results to avoid hammering HuggingFace API
        self._cache_ttl = timedelta(hours=1)
        self._hf_api_base = "https://huggingface.co/api/models"

    async def discover_opus_mt_pairs(self, force_refresh: bool = False):
        """Query HuggingFace for all Helsinki-NLP Opus-MT models."""

        cache_key = "opus-mt"

        # Check cache first
        if not force_refresh and cache_key in self._cache:
            cached_data, cached_time = self._cache[cache_key]
            if datetime.now() - cached_time < self._cache_ttl:
                return cached_data  # Cache hit!

        # Cache miss - query HuggingFace API
        async with httpx.AsyncClient() as client:
            response = await client.get(
                self._hf_api_base,
                params={
                    "author": "Helsinki-NLP",
                    "search": "opus-mt",
                    "limit": 1000
                },
                timeout=30.0
            )
            models = response.json()

        # Extract language pairs from model names
        # Example: "Helsinki-NLP/opus-mt-en-de" → ("en", "de")
        pairs = []
        for model in models:
            model_id = model.get("modelId", "")
            if model_id.startswith("Helsinki-NLP/opus-mt-"):
                # Extract the language codes after "opus-mt-"
                lang_part = model_id.replace("Helsinki-NLP/opus-mt-", "")
                if "-" in lang_part:
                    src, tgt = lang_part.split("-", 1)
                    pairs.append({"source": src, "target": tgt})

        # Cache the results
        self._cache[cache_key] = (pairs, datetime.now())

        return pairs

Detta dubblar latens men säkerställer täckning för alla språkpar som stöds.

  • Kod djupdykning: Coola funktioner förklaras (httpxLåt oss utforska några av de mest intressanta delarna av kodbasen!
  • **Detta är verkliga produktionsmönster som gör tjänsten robust och effektiv.**Varje snippet innehåller förklaringar lämpliga för icke-Python utvecklare.
  • **1. Vad är det som händer?**Smart LRU-cache med GPU-minneshanteringenEn av de coolaste funktionerna är den intelligenta modellen cache som vet hur man hanterar GPU minne:deVad händer här?Helsinki-NLP/opus-mt-en-de
  • : Som ett vanligt lexikon, men minns att beställningsobjekten har lagts tillLRU (minst nyligen använd)

: När cachen är full, sparka ut modellen som inte har använts på längst tid

Upprensning av GPU

# src/core/device.py
import torch

class DeviceManager:
    """Smart device selection with GPU auto-detection."""

    def __init__(self):
        self.use_gpu = self._should_use_gpu()
        self.device_index = self._resolve_device()
        self.device_str = "cpu" if self.device_index < 0 else f"cuda:{self.device_index}"

        # Auto-configure parallel translation slots based on device
        if self.device_index >= 0:
            # GPU: Run translations serially to avoid VRAM fragmentation
            self.max_inflight = 1
        else:
            # CPU: Can handle multiple translations in parallel
            self.max_inflight = config.MAX_WORKERS_BACKEND

        self._log_device_info()

    def _should_use_gpu(self) -> bool:
        """Check if GPU should be used."""
        if config.USE_GPU.lower() == "false":
            return False
        if config.USE_GPU.lower() == "true":
            return torch.cuda.is_available()
        # "auto" mode: use GPU if available
        return torch.cuda.is_available()

    def _resolve_device(self) -> int:
        """Returns device index: -1 for CPU, 0+ for CUDA."""
        if not self.use_gpu:
            return -1

        # Check if specific CUDA device requested
        if config.DEVICE and config.DEVICE.startswith("cuda:"):
            device_num = int(config.DEVICE.split(":")[1])
            return device_num

        return 0  # Use first GPU

    def _log_device_info(self):
        """Log device information at startup."""
        if self.device_index >= 0:
            gpu_name = torch.cuda.get_device_name(self.device_index)
            vram_gb = torch.cuda.get_device_properties(self.device_index).total_memory / 1e9
            logger.info(f"Using GPU: {gpu_name} ({vram_gb:.1f}GB VRAM)")
            logger.info(f"Max inflight translations: {self.max_inflight} (GPU mode)")
        else:
            cpu_count = os.cpu_count()
            logger.info(f"Using CPU ({cpu_count} cores)")
            logger.info(f"Max inflight translations: {self.max_inflight} (CPU mode)")

# Global singleton instance
device_manager = DeviceManager()

: När vi vräker en modell flyttar vi den uttryckligen till CPU-minnet och säger till GPU att släppa sina resurser

  • Varför det är viktigt: Utan detta skulle GPU-minnet fyllas upp och krascha efter att ha laddat 2-3 modeller!
  • **Två.**Automatisk modell familj fall tillbakamax_inflight=1Denna smarta funktion försöker flera AI-modellleverantörer automatiskt om den första inte har det språkpar du behöver:max_inflight=4Vad händer här?
  • Ryggkedja: Om Opus-MT inte har ukrainska→Franska, prova automatiskt mBART50, sedan M2M100DEVICE=cuda:1
  • Ingen manuell åtgärd: Användare bara begära en översättning och få den bästa tillgängliga modellen
  • Felhantering: Om alla familjer misslyckas, vi kasta ett tydligt fel med den senaste misslyckande skäl

Smart cachelagring

: Framgångsrika modeller är cachad med språkpar nyckel

# Snippet from QueueManager showing EMA calculation
def update_avg_duration(self, new_duration: float):
    """Update average duration using exponential moving average."""

    # EMA formula: new_avg = α × new_value + (1 - α) × old_avg
    # α = smoothing factor (0.0 to 1.0)
    #   - Higher α = more weight to recent values (faster adaptation)
    #   - Lower α = more weight to historical values (more stable)

    alpha = 0.2  # 20% weight to new value, 80% to historical

    self.avg_duration_sec = (
        alpha * new_duration +
        (1 - alpha) * self.avg_duration_sec
    )

3. Vad är det som händer?

# Initial average: 5.0 seconds
# New request takes: 10.0 seconds

# EMA calculation:
new_avg = 0.2 * 10.0 + 0.8 * 5.0
        = 2.0 + 4.0
        = 6.0 seconds

# Next request takes: 3.0 seconds
new_avg = 0.2 * 3.0 + 0.8 * 6.0
        = 0.6 + 4.8
        = 5.4 seconds

Begär kö med mottryck (HTTP 429)

  • **Produktionsgrad kö som förhindrar server kraschar under tung belastning:**Vad händer här?
  • Symptom på semaforer: Som en dörrvakt på en klubb - bara släpper in N människor på en gång (N =
  • Sammanhangshanterare): Spårar automatiskt mätvärden och städar upp
  • Smarta försök efter: Säger till kunderna "kom tillbaka om 25 sekunder" baserat på ködjup och genomsnittlig begäran tidRetry-AfterExponentiellt glidande medelvärde

: Jämnar ut spikar i begäran varaktighet

  • Varför det är viktigt: Under tung belastning, returnerar HTTP 429 istället för att krascha eller köa oändligt
  • **Fyra.**Symbolmaskering magi
  • **Bevarar specialtecken (emojis, symboler) som översättningsmodeller kan förstöra:**Exempel på användning:
  • **Vad händer här?**Regex- mönster
  • : Matchar emoji och specialsymbol Unicode intervallSystem för platshållare
  • : Bytedär

tillfälligt

Varför det är viktigt

: Översättningsmodeller ibland korrupta eller ta bort emojis - detta bevarar dem perfekt!

För att uppnå detta mål har kommissionen beslutat att inleda ett förfarande enligt artikel 108.2 i fördraget om Europeiska unionens funktionssätt (nedan kallat EUF-fördraget).

# Prefer GPU if available (default)
USE_GPU=auto

# Force GPU
USE_GPU=true

# Force CPU
USE_GPU=false

# Explicit device override
DEVICE=cuda:0
DEVICE=cpu

Intelligent textknäppning

# Model family selection (NEW in v2.0!)
MODEL_FAMILY=opus-mt   # Best quality (default)
MODEL_FAMILY=mbart50   # 50 languages, single model
MODEL_FAMILY=m2m100    # 100 languages, maximum coverage

# Auto-fallback between model families (NEW in v2.0!)
AUTO_MODEL_FALLBACK=1  # Enabled by default
MODEL_FALLBACK_ORDER="opus-mt,mbart50,m2m100"  # Priority order

# Volume-mapped model cache (NEW in v2.0!)
MODEL_CACHE_DIR=/models  # Persistent cache directory

# Model arguments passed to transformers.pipeline
EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}'
EASYNMT_MODEL_ARGS='{"torch_dtype":"bf16","cache_dir":"/models"}'

# Preload models at startup (reduces first-request latency)
PRELOAD_MODELS="en->de,de->en,fr->en"

# LRU cache capacity
MAX_CACHED_MODELS=6

Bryter långa texter i bitar som passar modellgränser samtidigt som meningsgränser bevaras:

  • **Vad händer här?**Skadeståndsskillnad

    • opus-mt: Använder regex för att dela på
    • mbart50med bibehållen interpunktion
    • m2m100Glädjande bitning
  • : Packar så många meningar som möjligt i varje bit utan att överskrida gränsenSpridning av ordgräns

    • 1: Om en enda mening är för lång, delas på mellanslag istället för att klippa mittord
    • 0Varför det är viktigt
  • **: Översättningsmodeller har inmatningsgränser (vanligtvis 512-1024 polletter).**Detta säkerställer att vi aldrig överskrider dem samtidigt som sammanhanget förblir intakt.

      1. Vad är det som händer?"opus-mt,mbart50,m2m100"Async modell Upptäckt med Caching
    • Dynamiskt upptäcker tillgängliga översättningsmodeller från HuggingFace:"m2m100,mbart50,opus-mt"Vad händer här?
  • Async HTTP- klientName): Gör icke-blockerande HTTP-förfrågningar till HuggingFace

    • Tidsbaserad cachelagring/models: Lagrar resultat för 1 timme för att undvika hastighetsbegränsning-v ./model-cache:/models
    • Förlaga till tolkning
    • : Extrakt

och

  • fp16från
  • bf16Varför det är viktigt
  • fp32: HuggingFace har 1200+ Opus-MT modeller.

Att fråga dem alla tar 10 sekunder.

# Batch size for translation (higher = faster but more VRAM)
EASYNMT_BATCH_SIZE=16  # CPU: 8-16, GPU: 32-64

# Maximum text length per item
EASYNMT_MAX_TEXT_LEN=1000

# Maximum beam size (higher = better quality but slower)
EASYNMT_MAX_BEAM_SIZE=5

# Worker thread pools
MAX_WORKERS_BACKEND=1    # Translation workers
MAX_WORKERS_FRONTEND=2   # Language detection workers

Caching gör det omedelbart!

# Enable request queueing (highly recommended)
ENABLE_QUEUE=1

# Max concurrent translations
# Auto: 1 on GPU, MAX_WORKERS_BACKEND on CPU
MAX_INFLIGHT_TRANSLATIONS=1

# Max queued requests before 429
MAX_QUEUE_SIZE=1000

# Per-request timeout (0 = disabled)
TRANSLATE_TIMEOUT_SEC=180

# Retry-After estimation
RETRY_AFTER_MIN_SEC=1      # Floor
RETRY_AFTER_MAX_SEC=120    # Ceiling
RETRY_AFTER_ALPHA=0.2      # EMA smoothing factor

7. Vad är det som händer?

# Enable input filtering
INPUT_SANITIZE=1

# Minimum alphanumeric ratio (0.2 = 20%)
INPUT_MIN_ALNUM_RATIO=0.2

# Minimum character count
INPUT_MIN_CHARS=1

# Language code for undetermined/noise
UNDETERMINED_LANG_CODE=und

Automatisk upptäckt av enheten

# Default sentence splitting behavior
PERFORM_SENTENCE_SPLITTING_DEFAULT=1

# Max chars per sentence before word-boundary split
MAX_SENTENCE_CHARS=500

# Max chars per chunk for batching
MAX_CHUNK_CHARS=900

# Sentence joiner
JOIN_SENTENCES_WITH=" "

Avkänner och använder automatiskt GPU om tillgängligt:

# Enable symbol masking
SYMBOL_MASKING=1

# What to mask
MASK_DIGITS=1    # Mask 0-9
MASK_PUNCT=1     # Mask .,!? etc.
MASK_EMOJI=1     # Mask 😀🎉 etc.

Vad händer här?

# Align response array length to input
ALIGN_RESPONSES=1

# Placeholder for failed items (when aligned)
SANITIZE_PLACEHOLDER=""

# Response format
EASYNMT_RESPONSE_MODE=strings    # ["translation1", "translation2"]
EASYNMT_RESPONSE_MODE=objects    # [{"text":"translation1"}, ...]

GPU-detektion

# Enable two-hop translation via pivot
PIVOT_FALLBACK=1

# Pivot language (usually English)
PIVOT_LANG=en

: Använder PyTorch för att kontrollera om CUDA är tillgängligt

# Log level
LOG_LEVEL=INFO

# Per-request logging (verbose)
REQUEST_LOG=1

# Format
LOG_FORMAT=plain    # Human-readable
LOG_FORMAT=json     # Structured JSON

# File logging with rotation
LOG_TO_FILE=1
LOG_FILE_PATH=/var/log/marian-translator/app.log
LOG_FILE_MAX_BYTES=10485760    # 10MB
LOG_FILE_BACKUP_COUNT=5

# Include raw text in logs (privacy risk!)
LOG_INCLUDE_TEXT=0

Automatisk inställning

# Periodically clear CUDA cache (seconds, 0=disabled)
CUDA_CACHE_CLEAR_INTERVAL_SEC=0

: Ställningar

# Worker count (use 1 for single GPU)
WEB_CONCURRENCY=1

# Request timeout
TIMEOUT=60

# Graceful shutdown timeout
GRACEFUL_TIMEOUT=20

# Keep-alive timeout
KEEP_ALIVE=5

om GPU (undvik VRAM-fragmentering)

På processorn (maximera parallellism)

# GET request
curl "http://localhost:8000/translate?target_lang=de&text=Hello%20world&source_lang=en"

# Response
{
  "translations": ["Hallo Welt"]
}

Enhetsval

# POST request
curl -X POST http://localhost:8000/translate \
  -H 'Content-Type: application/json' \
  -d '{
    "text": [
      "Hello world",
      "This is a test",
      "Machine translation is amazing"
    ],
    "target_lang": "de",
    "source_lang": "en",
    "beam_size": 1,
    "perform_sentence_splitting": true
  }'

# Response
{
  "target_lang": "de",
  "source_lang": "en",
  "translated": [
    "Hallo Welt",
    "Das ist ein Test",
    "Maschinenübersetzung ist erstaunlich"
  ],
  "translation_time": 0.342
}

: Kan rikta specifika GPU med

# Omit source_lang for auto-detection
curl -X POST http://localhost:8000/translate \
  -H 'Content-Type: application/json' \
  -d '{
    "text": ["Bonjour le monde"],
    "target_lang": "en"
  }'

# Response
{
  "target_lang": "en",
  "source_lang": "fr",  # Detected
  "translated": ["Hello world"],
  "translation_time": 0.156
}

Loggning

# GET
curl "http://localhost:8000/language_detection?text=Hola%20mundo"
# {"language": "es"}

# POST with batch
curl -X POST http://localhost:8000/language_detection \
  -H 'Content-Type: application/json' \
  -d '{"text": ["Hello", "Bonjour", "Hola"]}'
# {"languages": ["en", "fr", "es"]}

: Visar GPU-namn och VRAM vid start för felsökning

# Health check
curl http://localhost:8000/healthz
# {"status": "ok"}

# Readiness
curl http://localhost:8000/readyz
# {
#   "status": "ready",
#   "device": "cuda:0",
#   "queue_enabled": true,
#   "max_inflight": 1
# }

# Cache status
curl http://localhost:8000/cache
# {
#   "capacity": 6,
#   "size": 3,
#   "keys": ["en->de", "de->en", "fr->en"],
#   "device": "cuda:0",
#   "inflight": 1,
#   "queue_enabled": true
# }

# Model info
curl http://localhost:8000/model_name | jq

Mönster för enkelton

# When queue is full, you get 429
curl -X POST http://localhost:8000/translate \
  -H 'Content-Type: application/json' \
  -d '{"text": ["test"], "target_lang": "de"}'

# Response: 429 Too Many Requests
# Headers: Retry-After: 45
# Body:
{
  "message": "Too many requests; queue full",
  "retry_after_sec": 45
}

# Proper client behavior:
# 1. Read Retry-After header
# 2. Wait that long + jitter
# 3. Retry request

: En instans som delas över hela ansökan

  1. Vad är det som händer?

Exponentiell glidande medelvärde för försök-efter

Jämna försök efter uppskattning som anpassar sig till den faktiska begärans varaktighet:

Exempel:

.\build-all.ps1

Vad händer här?

chmod +x build-all.sh
./build-all.sh

EMA (Exponentiellt glidande medelvärde)

: Som ett vägt genomsnitt som ger större vikt åt de senaste värdenaUtjämningsfaktor (α):

  1. : Kontrollerar hur snabbt vi anpassar oss till förändringar (latest, min, gpu, gpu-minVarför inte ett simpelt genomsnitt?
  2. : EMA anpassar sig snabbare till förändringar och filtrerar ut spikarVarför det är viktigt20250108.143022: Ger kunderna realistiska

tider som anpassar sig till nuvarande systembelastning

# Always get the latest version
docker pull scottgal/mostlylucid-nmt:cpu
# Or use the :latest alias
docker pull scottgal/mostlylucid-nmt:latest

# Pin to a specific version for reproducibility
docker pull scottgal/mostlylucid-nmt:cpu-20250108.143022
docker pull scottgal/mostlylucid-nmt:cpu-min-20250108.143022

Dessa kodmönster visar på pythonmetoder av produktionskvalitet:

Resursförvaltning

  • : Explicit GPU minne rensningNådfull förnedring
  • : Automatisk reserv mellan modellleverantörerMottryckshantering
  • : Kö + HTTP 429 istället för krascherDataintegritet
  • : Symbol maskering bevarar specialteckenPrestandaoptimering

: Smart cachelagring, kapning och parallell bearbetning

docker inspect scottgal/mostlylucid-nmt:cpu | jq '.[0].Config.Labels'

Observationsförmåga: Detaljerad loggning och mätning spårning.

Var och en av dessa funktioner löser ett verkligt produktionsproblem som skulle orsaka krascher, fel, eller dålig användarupplevelse utan dem!

Inställningsguide

# Using pre-built image from Docker Hub (recommended)
docker run -d \
  --name translator \
  -p 8000:8000 \
  -e ENABLE_QUEUE=1 \
  -e MAX_QUEUE_SIZE=500 \
  -e EASYNMT_BATCH_SIZE=16 \
  -e TIMEOUT=180 \
  -e LOG_LEVEL=INFO \
  -e REQUEST_LOG=0 \
  scottgal/mostlylucid-nmt

# Or build locally
docker build -t mostlylucid-nmt .
docker run -d --name translator -p 8000:8000 mostlylucid-nmt

# Check logs
docker logs -f translator

Tjänsten är mycket konfigurerbar via miljövariabler.

# Using pre-built GPU image from Docker Hub (recommended)
docker run -d \
  --name translator-gpu \
  --gpus all \
  -p 8000:8000 \
  -e USE_GPU=true \
  -e DEVICE=cuda:0 \
  -e PRELOAD_MODELS="en->de,de->en,en->fr,fr->en,en->es,es->en" \
  -e EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}' \
  -e EASYNMT_BATCH_SIZE=64 \
  -e MAX_CACHED_MODELS=8 \
  -e ENABLE_QUEUE=1 \
  -e MAX_QUEUE_SIZE=2000 \
  -e WEB_CONCURRENCY=1 \
  -e TIMEOUT=180 \
  -e GRACEFUL_TIMEOUT=30 \
  -e LOG_FORMAT=json \
  -e LOG_TO_FILE=1 \
  -v /var/log/translator:/var/log/marian-translator \
  scottgal/mostlylucid-nmt:gpu

# Or build locally
docker build -f Dockerfile.gpu -t mostlylucid-nmt:gpu .
docker run -d --name translator-gpu --gpus all -p 8000:8000 mostlylucid-nmt:gpu

# Monitor cache and performance
watch -n 5 "curl -s http://localhost:8000/cache | jq"

Här är hela guiden:

version: '3.8'

services:
  translator:
    image: scottgal/mostlylucid-nmt:gpu  # Use pre-built image
    container_name: translator
    restart: unless-stopped

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

    ports:
      - "8000:8000"

    environment:
      USE_GPU: "true"
      DEVICE: "cuda:0"
      PRELOAD_MODELS: "en->de,de->en,en->fr,fr->en"
      EASYNMT_MODEL_ARGS: '{"torch_dtype":"fp16"}'
      EASYNMT_BATCH_SIZE: "64"
      MAX_CACHED_MODELS: "8"
      ENABLE_QUEUE: "1"
      MAX_QUEUE_SIZE: "2000"
      WEB_CONCURRENCY: "1"
      TIMEOUT: "180"
      LOG_FORMAT: "json"
      LOG_TO_FILE: "1"

    volumes:
      - translator-logs:/var/log/marian-translator
      - translator-cache:/root/.cache/huggingface

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

volumes:
  translator-logs:
  translator-cache:

Enhetsval

apiVersion: apps/v1
kind: Deployment
metadata:
  name: translator
spec:
  replicas: 2  # Scale horizontally for CPU, use 1 per GPU
  selector:
    matchLabels:
      app: translator
  template:
    metadata:
      labels:
        app: translator
    spec:
      containers:
      - name: translator
        image: scottgal/mostlylucid-nmt:gpu
        ports:
        - containerPort: 8000
        env:
        - name: USE_GPU
          value: "true"
        - name: EASYNMT_MODEL_ARGS
          value: '{"torch_dtype":"fp16"}'
        - name: PRELOAD_MODELS
          value: "en->de,de->en"
        - name: ENABLE_QUEUE
          value: "1"
        - name: MAX_QUEUE_SIZE
          value: "2000"

        resources:
          requests:
            memory: "4Gi"
            cpu: "2"
            nvidia.com/gpu: 1
          limits:
            memory: "8Gi"
            cpu: "4"
            nvidia.com/gpu: 1

        livenessProbe:
          httpGet:
            path: /healthz
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10

        readinessProbe:
          httpGet:
            path: /readyz
            port: 8000
          initialDelaySeconds: 20
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: translator
spec:
  selector:
    app: translator
  ports:
  - port: 80
    targetPort: 8000
  type: LoadBalancer

Modellinställning

Ny inställning förklarade:

  1. FÖRLAGA_FAMILJ

    EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}'
    
    • : Välj vilken modellfamilj som ska användas som primär
    • : Bästa kvalitet, 1200+ par, separata modeller
    • : Bra kvalitet, 50 språk, enkel 2,4 GB modell
  2. : Bra kvalitet, 100 språk, enkel 2.2GB modell

    # Start high, reduce if you get OOM
    EASYNMT_BATCH_SIZE=64  # Try 128 on large GPUs
    
  3. AUTO_MODEL_FALLBACK

    PRELOAD_MODELS="en->de,de->en,en->fr,fr->en,en->es,es->en"
    
  4. : Prova automatiskt andra familjer om par inte finns tillgängliga

    WEB_CONCURRENCY=1
    MAX_INFLIGHT_TRANSLATIONS=1
    
  5. (förval): Aktiverad - maximal täckning

    MAX_CACHED_MODELS=10  # Keep more models in VRAM
    
  6. : Handikappad - strikt enfamiljsläge

    # beam_size=1 is 3-5x faster than beam_size=5
    # Quality difference is often minimal
    curl -X POST ... -d '{"beam_size": 1, ...}'
    

FÖRLAGA_FALLBACK_ORDER

  1. : Prioriterad ordning för reserv

    EASYNMT_BATCH_SIZE=8
    
  2. Förval:

    MAX_WORKERS_BACKEND=4
    MAX_INFLIGHT_TRANSLATIONS=4
    WEB_CONCURRENCY=2
    
  3. (första kvalitet)

    PERFORM_SENTENCE_SPLITTING_DEFAULT=0
    

Alternativt:

  1. (omslag först)

    // Bad: 100 separate requests
    for (const text of texts) {
      await translate(text);
    }
    
    // Good: 1 batch request
    await translate(texts);
    
  2. FÖRLAGA_CACHE_DIR

    async function translateWithRetry(texts) {
      try {
        return await translate(texts);
      } catch (err) {
        if (err.status === 429) {
          const retryAfter = err.headers['retry-after'];
          const jitter = Math.random() * 5;
          await sleep((retryAfter + jitter) * 1000);
          return translateWithRetry(texts);
        }
        throw err;
      }
    }
    
  3. : Ihållande modelllagring via Docker-volymer

    // Reuse HTTP connections
    const agent = new https.Agent({ keepAlive: true });
    
  4. Ställ in till

    // Bad: mixed language pairs in one request
    translate([
      { text: "Hello", sourceLang: "en", targetLang: "de" },
      { text: "Bonjour", sourceLang: "fr", targetLang: "de" }
    ]);
    
    // Good: group by language pair
    translateBatch(enToDe, "en", "de");
    translateBatch(frToDe, "fr", "de");
    

och kartvolym:

Modeller finns kvar över hela behållarens omstarter

  1. Delad cache mellan flera behållareAlternativ för torch_dtyp:
  2. (flytande16): 2x snabbare på GPU, halva minnet, försumbar kvalitetsförlust(float16): Bättre numerisk stabilitet än fp16, kräver moderna GPU:er
  3. (flytande32): Full precision, långsammast men mest exaktÖversättningsinställningar
  4. Köa & prestandaInsatssanering
  5. DomsprövningSymbolmaskering
  6. SvarsbeteendeBivotnedslag
  7. LoggningSkötsel

Gunhörn (Docker)

Exempel på användning

translation_requests_total{lang_pair="en->de",status="success"} 1523
translation_requests_total{lang_pair="en->de",status="error"} 7
translation_duration_seconds{lang_pair="en->de",quantile="0.5"} 0.342
translation_duration_seconds{lang_pair="en->de",quantile="0.95"} 1.234
translation_queue_depth 23
translation_cache_size 6
translation_cache_hits_total 8234
translation_cache_misses_total 142

Grundläggande översättning

# Enable JSON logging
LOG_FORMAT=json REQUEST_LOG=1

# Output example
{
  "ts": "2025-01-08T15:30:45+0000",
  "level": "INFO",
  "name": "app",
  "message": "translate_post done items=5 dt=0.342s",
  "req_id": "a3d2f5b1-c4e6-4f7a-9d8c-1e2f3a4b5c6d",
  "endpoint": "/translate",
  "src": "en",
  "tgt": "de",
  "items": 5,
  "duration_ms": 342
}

Batchöversättning (rekommenderas)

Påvisande av automatiskt språk

Endast påvisande av språk |---------|---------|-----------------| | Slutpunkter för observerbarhetHantering av mottryck | Byggande och versionshanteringAlla Docker bilder inkluderar nu korrekt version och metadata för spårning. | Snabbt byggaBygg alla 4 varianter med automatisk datumversion: | **Fönster:**Linux/Mac: | VersionsstrategiVarje bygge skapar | två taggarNamngiven etikett | ) - alltid pekar på den senasteVersionsetikett | (t.ex.) - oföränderlig ögonblicksbild | **Exempel:**OCI- etiketter | **Varje bild innehåller metadata:**Ansökan ska innehålla följande uppgifter: | **: Bygg tidsstämpel (ÅÅÅÅMMDD.HHMMSS)**Byggdatum | : ISO 8601 tidsstämpelGit- åtagande

: Kort SHA

Variant Ordförande

: cpu-full, cpu-min, gpu-full eller gpu-minInspektera etiketterna:

För detaljerade bygginstruktioner och CI/CD-integration, se

  • BUILD.M.D.MAX_QUEUE_SIZE
  • Utplacering
  • Utplacering av processornMAX_INFLIGHT_TRANSLATIONSGPU-utplacering
  • Docker komposera

Utplacering av Kubernetes

Optimering av prestandaChecklista för GPU-optimering

Använd FP16 precision

  • 2x snabbare slutsatsENABLE_QUEUE=1
  • Hälften av VRAM-användningen

Försumbar kvalitetsförlust för översättning

Ton tillverkningssats storlekFörladda heta modeller

Ensamstående arbetstagare per GPU

  • Öka cachestorlekenEASYNMT_BATCH_SIZE
  • Lägre strålstorlek för genomströmningMAX_CACHED_MODELS
  • Checklista för processoroptimeringEASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}'
  • Lägre satsstorlekWEB_CONCURRENCY=1Öka parallellismenMAX_INFLIGHT_TRANSLATIONS=1

Inaktivera meningsdelning för korta texter

Kundens bästa metoderBegäran om tillverkningssats

Respektera försök efter

PRELOAD_MODELS="en->de,de->en"

Använd anslutningspoolning

Gruppera efter språkpar Helsinki-NLP/opus-mt-{src}-{tgt}Övervakning och observerbarhet

Nyckelmått att spåra

  • ÖversättningsgenomgångPIVOT_FALLBACK=1(begäran/sekund)
  • Genomsnittlig latenscurl http://localhost:8000/lang_pairs

(p50, p95, p99)

Ködjup(nuvarande vänteräkning)

Cache träfffrekvens

  • (% av förfrågningar som träffar cache)MASK_EMOJI=0FelfrekvensMASK_PUNCT=0
  • (5xx svar)SYMBOL_MASKING=0

Användning av GPU

(om tillämpligt)

public class MostlyLucidNmtClient
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl;

    public MostlyLucidNmtClient(HttpClient httpClient, string baseUrl)
    {
        _httpClient = httpClient;
        _baseUrl = baseUrl;
    }

    public async Task<TranslationResponse> TranslateAsync(
        List<string> texts,
        string targetLang,
        string sourceLang = "",
        int beamSize = 1,
        bool performSentenceSplitting = true,
        CancellationToken cancellationToken = default)
    {
        var request = new TranslationRequest
        {
            Text = texts,
            TargetLang = targetLang,
            SourceLang = sourceLang,
            BeamSize = beamSize,
            PerformSentenceSplitting = performSentenceSplitting
        };

        var response = await _httpClient.PostAsJsonAsync(
            $"{_baseUrl}/translate",
            request,
            cancellationToken);

        if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
        {
            // Read Retry-After header
            var retryAfter = response.Headers.RetryAfter?.Delta?.TotalSeconds ?? 30;
            var jitter = Random.Shared.Next(0, 5);
            await Task.Delay(TimeSpan.FromSeconds(retryAfter + jitter), cancellationToken);

            // Retry
            return await TranslateAsync(texts, targetLang, sourceLang, beamSize,
                performSentenceSplitting, cancellationToken);
        }

        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<TranslationResponse>(cancellationToken);
    }
}

public class TranslationRequest
{
    [JsonPropertyName("text")]
    public List<string> Text { get; set; }

    [JsonPropertyName("target_lang")]
    public string TargetLang { get; set; }

    [JsonPropertyName("source_lang")]
    public string SourceLang { get; set; }

    [JsonPropertyName("beam_size")]
    public int BeamSize { get; set; }

    [JsonPropertyName("perform_sentence_splitting")]
    public bool PerformSentenceSplitting { get; set; }
}

public class TranslationResponse
{
    [JsonPropertyName("target_lang")]
    public string TargetLang { get; set; }

    [JsonPropertyName("source_lang")]
    public string SourceLang { get; set; }

    [JsonPropertyName("translated")]
    public List<string> Translated { get; set; }

    [JsonPropertyName("translation_time")]
    public double TranslationTime { get; set; }
}

Minnesanvändning

services.AddHttpClient<MostlyLucidNmtClient>(client =>
{
    client.BaseAddress = new Uri("http://translator:8000");
    client.Timeout = TimeSpan.FromMinutes(3);
});

(VRAM för GPU, RAM för CPU)

Exempel Prometheus Metrics**Om du integrerar Prometheus (inte inbyggd, men enkel att lägga till):**Exempel på strukturerad loggning

Du kan skicka detta till Elasticsearch, CloudWatch, eller någon loggsamlare.

Jämförelse: EasyNMT vs MostlyLucid-NMT

  • på funktionen på EasyNMT på MestadelsLucid-NMT
  • Stabilitet
  • på Crashes ofta med produktion-ready, graciös felhantering

Hantering av inmatning

  • Misslyckades med emoji/symbols med Robust sanitisering + symbolmaskering
  • Mottryck
  • Ingen, OMS under belastning på Semaphore + kö med försök efter

Observationsförmåga

  • med minimala resultatmått för hälsa/ready/cache, strukturerade loggar
  • Stöd för GPU
  • Stöd inom ramen för FP16/BF16
  • Förlaga till förlaga

Manuell, ingen cachning, LRU-cache med auto-eviction

  • Rättsskipning
  • på grund av att man delar upp en smart buckling + batching
  • Pivotöversättning
  • Automatisk reserv via engelska
  • Förtjusande avstängning

Med timeout, ja, med timeout

InställningFör finjustering: 40 + env vars för finjusteringAPI-kompatibilitet

på EasyNMT endpoints på 100% kompatibel + förlängningar

  • Kod KvalitetObibehållen, monolitisk, modulär, typad, testad
  • Felsökning429 För många förfrågningar
  • **Orsak:**Kö är full.
  • **Lösning:**Ökning
  • **Lägg till fler kopior (horisontell skalning)**Ökning
  • **(om du har huvudutrymme)**Minska batchstorlekar från kunder
  • 503 Otillgänglig tjänstOrsak:
  • **Köa inaktiverade och alla slots upptagna.**Lösning:

Aktivera köning:

# Maximum coverage with auto-fallback (recommended!)
docker run -d -p 8000:8000 \
  -v ./model-cache:/models \
  -e MODEL_CACHE_DIR=/models \
  -e AUTO_MODEL_FALLBACK=1 \
  -e MODEL_FALLBACK_ORDER="opus-mt,mbart50,m2m100" \
  scottgal/mostlylucid-nmt:cpu-min

# GPU with best quality
docker run -d --gpus all -p 8000:8000 \
  -e USE_GPU=true \
  -e MODEL_FAMILY=opus-mt \
  -e EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}' \
  scottgal/mostlylucid-nmt:gpu

# Test it
curl -X POST http://localhost:8000/translate \
  -H 'Content-Type: application/json' \
  -d '{"text": ["Hello world"], "target_lang": "de"}'

Höj gränsen för inflygning om du har resurser

OOM (ut ur minnet) på GPU

Orsak:

Batch storlek för hög eller för många modeller cachad.


Lösning:

och

Långsam första begäran[Orsak:

Modellen är inte förladdad.

Translation NMT Neural Machine Translation Python FastAPI Docker CUDA PyTorch Transformers Helsinki-NLP Production Microservices API

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.