Safetensors
Jak działa
Safetensors zapisuje tensory w prostym binarnym formacie z nagłówkiem metadanych opisującym nazwy tensorów, ich typy danych, kształty oraz offsety w pliku. Dzięki temu biblioteka może odczytywać zawartość bez potrzeby wykonywania kodu zawartego w pliku. Format został zaprojektowany tak, aby umożliwiać szybki dostęp do danych i wspierać scenariusze zero-copy lub częściowego mapowania pliku do pamięci, zależnie od używanego frameworka i środowiska. Implementacje istnieją dla popularnych ekosystemów, takich jak PyTorch, TensorFlow, JAX, PaddlePaddle i NumPy.
Rozwiązany problem
Safetensors rozwiązuje problem niebezpiecznej deserializacji wag modeli w formatach takich jak pickle oraz powiązanych z tym ryzyk bezpieczeństwa. Tradycyjne formaty checkpointów mogą wykonywać kod przy ładowaniu, co stwarza zagrożenie podczas pobierania modeli z zewnętrznych źródeł. Dodatkowo wiele starszych formatów nie było projektowanych z myślą o szybkim, prostym i przewidywalnym dostępie do tensorów na dużą skalę. Safetensors ogranicza te ryzyka, oferując bezpieczny, prosty i wydajny format do przechowywania tensorów.
Komponenty
Pierwsze 8 bajtów pliku safetensors. Przechowuje rozmiar nagłówka JSON jako 64-bitową liczbę całkowitą bez znaku (uint64) w kolejności little-endian. Umożliwia natychmiastową lokalizację nagłówka JSON bez parsowania danych tensorów.
Zmienna długościowo sekcja UTF-8 JSON bezpośrednio po polu rozmiaru nagłówka. Zawiera słownik mapujący nazwy tensorów na ich dtype (np. F16, BF16, F32), shape (tablica wymiarów) i data_offsets ([BEGIN, END] względem początku obszaru danych). Opcjonalny klucz __metadata__ przechowuje dowolne pary string–string. Rozmiar ograniczony do 100 MB przez MAX_HEADER_SIZE.
Ciągły blok surowych bajtów przechowujący dane wszystkich tensorów w kolejności C (row-major), bez kompresji, bez paddingu między tensorami. Offsety z nagłówka JSON są relative do początku tego bufora (nie do początku pliku). Tensory muszą być upakowane (packed) przed serializacją — striding nie jest obsługiwany.
Implementacja
PyTorch pozwala na tensory współdzielące tę samą pamięć (storage). Adapter safetensors dla PyTorch zawiera specjalną logikę wykrywania i obsługi takich tensorów. Serializacja modeli z shared tensors bez tej obsługi może prowadzić do zduplikowania danych lub błędów. Po deserializacji współdzielenie pamięci jest tracone — każdy tensor jest niezależny.
Format safetensors nie stosuje kompresji. Dane tensorów są przechowywane jako surowe bajty. Dla modeli o niskiej entropii (np. bardzo rzadkich wag lub kwantyzowanych modeli z wieloma zerami) rozmiar pliku może być znacznie większy niż przy formatach ze skompresowaną serializacją. Nie ma mechanizmu selektywnej kompresji.
Specyfikacja JSON formalnie nie definiuje zachowania przy zduplikowanych kluczach. Audit Trail of Bits wykazał, że referencyjna implementacja Hugging Face odrzuca pliki ze zduplikowanymi kluczami, ale niektóre parsery JSON firm trzecich akceptują je i przetwarzają w sposób niezdefiniowany. Złośliwy plik może zatem zachowywać się inaczej w różnych implementacjach.
Format safetensors nie zawiera wbudowanego mechanizmu integralności danych (np. SHA-256 hash tensorów). Uszkodzenie pliku podczas transmisji lub przechowywania może nie zostać wykryte na etapie ładowania — format weryfikuje strukturę i offsety, ale nie sumy kontrolne danych.
Ewolucja
Nicolas Patry z Hugging Face opublikował pierwszą wersję biblioteki i specyfikacji formatu safetensors. Rdzeń w Rust, bindingi Python przez PyO3, obsługa PyTorch i NumPy. Format zaprojektowany jako bezpieczna i szybka alternatywa dla pickle.
Niezależny audit bezpieczeństwa przeprowadzony przez Trail of Bits na zlecenie Hugging Face, EleutherAI i Stability AI. Nie znaleziono krytycznych podatności prowadzących do wykonania kodu. Hugging Face Hub zaczął wyświetlać ostrzeżenia dla modeli w formacie pickle i przyjął safetensors jako preferowany format.
PyTorch scalił natywne wsparcie dla safetensors w swoim głównym API serializacji (parametr weights_only i opcja formatu safetensors w API zapisu). Oznacza to instytucjonalną aprobatę formatu przez wiodący framework deep learning, przekształcając pickle do statusu formatu legacy.
Złożoność obliczeniowa
Złożoność czasowa: O(n). Złożoność przestrzenna: O(n).
Równoległość
Tensory w bufore danych są niezależne — mogą być ładowane równolegle przez wiele wątków lub procesów. Parsowanie nagłówka JSON jest sekwencyjne, ale zajmuje << czasu ładowania danych. Format obsługuje distributed loading: każdy węzeł może załadować inny podzbiór tensorów (tensor parallelism sharding, stosowany w TGI).
Wymagania sprzętowe
Safetensors to format pliku, nie algorytm obliczeniowy. Operacja ładowania (parsowanie nagłówka JSON, memory-mapping) jest agnostyczna sprzętowo i efektywna zarówno na CPU jak i jako etap wstępny przed transferem danych na GPU/TPU. Format obsługuje framework-agnostyczne załadowanie tych samych tensorów do PyTorch, TensorFlow, JAX, MLX.