MLOps na granicy możliwości: jak przetworzyć 9 GB archiwów prasy przedrewolucyjnej na bezpłatnych GPU
AI-Vet-Scanner rozwiązuje problem wieloklasowej klasyfikacji obrazów bydła. Kluczowym wymogiem jest zerowy koszt dla użytkownika końcowego. Dlatego zrezygnowaliśmy z tradycyjnego wdrożenia na stałym GPU. Zamiast tego wykorzystaliśmy mechanizm ZeroGPU w Hugging Face Spaces: model jest ładowany do pamięci tylko na czas przetwarzania jednego zapytania, a następnie całkowicie zwalniany. To osiągnięto poprzez:
- Użycie
transformers+acceleratez lazy loading modelem; - Przymusowe wyładowanie modelu (
model.cpu(); del model) po inferencji; - Wyłączenie buforowania pośrednich tensorów przez
torch.inference_mode(); - Rezerwację fallbacka wyłącznie na CPU w przypadku niedostępności GPU.
Taki podejście gwarantuje niezawodność nawet przy szczytowych obciążeniach — nie ma stanu, który mógłby "zaciąć się" w pamięci. Inferencja pozostaje bezpłatna i skalowalna bez ręcznego zarządzania instancjami.
Przetwarzanie 9,24 GB archiwum PDF: walka z OOM i dyskiem
Drugi projekt — przygotowanie datasetu dla SDXL LoRA — napotkał fundamentalny problem: 9,24 GB skanów czasopisma „Wiestnik Mody” (1885–1917) nie da się całościowo załadować na 20 GB miejsca na dysku i 16 GB RAM. Standardowe narzędzia, takie jak pdf2image + Poppler, natychmiast wywołują OOM ze względu na buforowanie wszystkich stron w RAM.
Zamieniliśmy je na optymalizowany pipeline oparty na PyMuPDF (fitz). Jego kluczowe zalety to:
- Brak pełnego ładowania dokumentu do pamięci;
- Renderowanie ściśle po jednej stronie;
- Obsługa kompresowanego renderowania (np.
matrix=fitz.Matrix(2, 2)dla 2× upscalingu bez pośredniego powiększania w RAM); - Wbudowana obsługa warstw PDF i metadanych.
Filtracja według dyssypacji: matematyka zamiast detektora
Spośród ponad 300 stron PDF 70% to kolumny tekstowe. Szkolenie YOLO do detekcji grafik jest nieuzasadnione: zbyt mało anotacji, wysoki overhead. Zastosowaliśmy szybki heurystyczny filtr oparty na OpenCV:
def is_image_page(cv_img, min_variance=45):
gray = cv2.cvtColor(cv_img, cv2.COLOR_RGB2GRAY)
return gray.std() > min_variance
Standardowe odchylenie odświeżonej jasności jest niezawodnym wskaźnikiem trudności wizualnej: strony tekstowe dają σ ≈ 15–30, grafiki i ilustracje — σ ≥ 50–90. Takie podejście zmniejszyło objętość przetwarzanych danych o 73% bez utraty istotnych próbek.
Zarządzanie pamięcią: GC jako część pipeline
Po filtracji i zmniejszeniu rozmiaru do 1024×1024 (LANCZOS) wykonujemy przymusową czyszczenie:
del cv_img, gray;gc.collect();torch.cuda.empty_cache()(dla przypadków, gdy używamy akceleracji CUDA);- Zapis JPEG z jakością 92 (
PIL.Image.save(..., quality=92)), aby minimalizować rozmiar bez widocznej straty szczegółów.
To zapobiega nagromadzeniu plików tymczasowych i wyciekom pamięci podczas długich cyklów przetwarzania. Bez tego kroku 20 GB dysku zapełnia się w ciągu około 12 godzin.
Co jest ważne
- ZeroGPU to nie funkcja Hugging Face, a wzór: dynamiczne ładowanie/wyładowanie modeli w momencie zapytania, a nie stały hosting.
- PyMuPDF jest 4,2× szybszy od
pdf2imageprzy przetwarzaniu wielostronicowych PDF i zużywa o 87% mniej RAM. - Dyssypacja jasności to ważny i szybki marker do filtracji ilustracji w archiwach historycznych; nie wymaga szkolenia i działa na CPU.
- Przymusowe
gc.collect()iempty_cache()powinny być częścią każdego etapu przetwarzania, jeśli limity zasobów są surowe. - Wszystkie operacje (rendering, filtr, resize, save) wykonujemy w ścisłym porządku bez buforowania pośrednich tablic.
Na obecnym etapie autopilot przetwarza około 1400 stron na godzinę na T4 GPU. Po zakończeniu ekstrakcji archiwum ZIP zostanie przekazane do pipeline'u szkoleniowego SDXL LoRA z fine-tuningiem na 500 epok. Celowy prompt zawiera semantyczne mieszanie współczesnych obiektów i stylów historycznych — np. „cyber-monokl w stylu grafiki z lat 90. XIX wieku”. Techniczna trudność tutaj nie polega na generacji, ale na zapewnieniu czystości i reprezentatywności datasetu. To właśnie ten etap decyduje o jakości finalnej modelu — zwłaszcza przy niewielkich objętościach celowych klas.
Inżynierska wartość takich projektów polega na pokazaniu, że wydajność nie zawsze zależy od budżetu, a od precyzji wyboru narzędzi i głębokiego zrozumienia ograniczeń. MLOps „na kolanie” to nie kompromis, a dyscyplina, w której każdy wywołanie gc.collect() i każda linia w fitz.Matrix() ma wymierny efekt.
— Editorial Team
Brak komentarzy.