JSON модели для Data Science: сохранение и загрузка ML объектов
Работа с JSON моделями в Python для Data Science
Как создать и сериализовать модель данных ML с валидацией?
Наиболее эффективный способ - использование библиотеки pydantic. Она позволяет определить классы данных с автоматической валидацией и сериализацией в JSON. Модели pydantic подходят для описания конфигураций, гиперпараметров, архитектур нейросетей и сохранения результатов предсказаний.
from pydantic import BaseModel
from typing import List, Optional
import json
class ModelConfig(BaseModel):
model_name: str
learning_rate: float = 0.001
layers: List[int]
activation: str = 'relu'
dropout: Optional[float] = None
config = ModelConfig(
model_name='cnn',
learning_rate=0.0005,
layers=[32, 64, 128],
activation='tanh',
dropout=0.2
)
json_str = config.model_dump_json(indent=2)
print(json_str)
Json model python (модель json в python)
{
"model_name": "cnn",
"learning_rate": 0.0005,
"layers": [32, 64, 128],
"activation": "tanh",
"dropout": 0.2
}
При загрузке JSON данные автоматически валидируются: неправильные типы вызывают ошибку ValidationError. Это предотвращает использование повреждённых конфигураций в ML пайплайне.
Типичная ошибка: сериализация объектов, которые не являются примитивами JSON (например, numpy массивы). Pydantic не поддерживает их напрямую. Решение - использовать кастомные валидаторы или преобразовывать массивы в списки перед созданием модели.
Как сохранить состояние обученной ML модели в JSON с помощью стандартного модуля json?
Библиотека json в Python не умеет сериализовывать сложные объекты, такие как модели scikit-learn или TensorFlow. Но можно сохранить гиперпараметры и веса, преобразовав их в словарь. Для этого пишется кастомный JSONEncoder.
import json
import numpy as np
class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.ndarray):
return obj.tolist()
return super().default(obj)
weights = {'layer1': np.random.rand(3,3)}
with open('model_weights.json', 'w') as f:
json.dump(weights, f, cls=NumpyEncoder)
При загрузке массивы восстанавливаются из списков обратно в numpy через np.array(). Метод подходит для простых ML алгоритмов, где все параметры умещаются в словарь.
Проблема: потеря информации о типе данных (например, float32 вместо float64). Чтобы избежать потерь, можно сохранять в JSON дополнительное поле dtype.
Как обеспечить обратную совместимость схем JSON для ML моделей?
Когда модель обновляется, а старые JSON‑конфиги должны работать - помогает использование JSON Schema и библиотеки jsonschema. Определите схему, а при загрузке валидируйте её. Допускаются умолчания для отсутствующих полей.
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"properties": {
"model_name": {"type": "string"},
"layers": {"type": "array", "items": {"type": "integer"}},
"activation": {"type": "string", "default": "relu"}
},
"required": ["model_name", "layers"]
}
data = {"model_name": "lstm", "layers": [50]}
try:
validate(instance=data, schema=schema)
print("JSON корректен")
except ValidationError as e:
print(f"Ошибка: {e.message}")
Этот подход применяется в MLOps для инвариантной загрузки параметров между версиями моделей.
Сложность: поддержка схемы вручную при изменении структуры модели. Рекомендуется генерировать схему автоматически из pydantic модели методом model_json_schema().
Расширенные примеры работы с JSON моделями ML
1. Сериализация полной модели scikit-learn с помощью jsonpickle
Библиотека jsonpickle позволяет сериализовать произвольные Python-объекты, включая обученные модели, с сохранением классов. Это удобно для быстрого прототипирования, но не рекомендуется для продакшна из-за рисков безопасности.
import jsonpickle
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=100, random_state=42)
model = RandomForestClassifier(n_estimators=10, random_state=42)
model.fit(X, y)
# Сериализация
model_json = jsonpickle.encode(model, indent=2)
with open('rf_model.json', 'w') as f:
f.write(model_json)
# Десериализация
with open('rf_model.json', 'r') as f:
loaded_model = jsonpickle.decode(f.read())
# Проверка
predictions = loaded_model.predict(X[:5])
print(predictions) # [0 1 0 1 0]
[0 1 0 1 0]
Проблемы: десериализация может выполнить произвольный код, если JSON был подделан. Используйте только для доверенных файлов.
2. Преобразование датакласса в JSON с помощью dataclasses + json
Стандартный модуль dataclasses в Python 3.7+ даёт чистый синтаксис для хранения данных ML (например, результатов экспериментов).
from dataclasses import dataclass, asdict
import json
@dataclass
class Metrics:
accuracy: float
f1_score: float
timestamp: str
metrics = Metrics(accuracy=0.94, f1_score=0.92, timestamp='2025-03-25T12:00:00')
json_str = json.dumps(asdict(metrics), indent=2)
print(json_str)
{
"accuracy": 0.94,
"f1_score": 0.92,
"timestamp": "2025-03-25T12:00:00"
}
Недостаток: нет валидации. Для больших проектов используйте pydantic.
3. Загрузка JSON конфига нейронной сети с автоматическим восстановлением объектов TensorFlow/Keras
Сложные ML модели часто хранят архитектуру в виде JSON (например, формат model.to_json() в Keras). Покажем, как объединить конфиг с весами.
import tensorflow as tf
import json
# Создаём простую модель
model = tf.keras.Sequential([
tf.keras.layers.Dense(10, activation='relu', input_shape=(5,)),
tf.keras.layers.Dense(1, activation='sigmoid')
])
# Сохраняем архитектуру в JSON
arch_json = model.to_json()
with open('model_arch.json', 'w') as f:
f.write(arch_json)
# Сохраняем веса отдельно (сериализация в JSON через numpy)
weights = [layer.get_weights() for layer in model.layers]
serialized_weights = {
str(i): [w.tolist() if hasattr(w, 'tolist') else w for w in layer_weights]
for i, layer_weights in enumerate(weights)
}
with open('model_weights.json', 'w') as f:
json.dump(serialized_weights, f, indent=2)
# Восстановление
with open('model_arch.json', 'r') as f:
new_model = tf.keras.models.model_from_json(f.read())
with open('model_weights.json', 'r') as f:
loaded_weights = json.load(f)
# Приводим обратно к numpy
import numpy as np
for i, layer in enumerate(new_model.layers):
w = [np.array(arr) for arr in loaded_weights[str(i)]]
layer.set_weights(w)
print("Модель восстановлена", new_model.summary())
Модель восстановлена ...
Такой подход позволяет хранить архитектуру в читаемом JSON, а веса - в том же формате, но преобразованные в списки.
4. Генерация JSON Schema из pydantic модели для документирования API ML
from pydantic import BaseModel, Field
from typing import List, Optional
class PredictionRequest(BaseModel):
features: List[float] = Field(..., description="Входные признаки модели")
model_version: Optional[str] = "v1.0"
schema = PredictionRequest.model_json_schema()
print(json.dumps(schema, indent=2))
{
"$defs": {},
"properties": {
"features": {
"items": {"type": "number"},
"title": "Features",
"type": "array",
"description": "Входные признаки модели"
},
"model_version": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"default": "v1.0",
"title": "Model Version"
}
},
"required": ["features"],
"title": "PredictionRequest",
"type": "object"
}
Схему можно использовать для автоматической валидации входящих запросов к ML‑сервису.