from dataclasses import dataclass from enum import Enum from itertools import chain from pandas import DataFrame from pickle import load from prometheus_client import Counter, Histogram def open_model_file(file, *, buffering=-1, opener=None, **kwargs_extra): open_kwargs_extra = {} if 'closefd' in kwargs_extra: open_kwargs_extra['closefd'] = kwargs_extra.pop('closefd') if len(kwargs_extra) > 0: raise TypeError( 'Unexpected keyword arguments given: {}' .format(', '.join(map(repr, kwargs_extra.keys()))) ) return open(file, 'rb', buffering=buffering, opener=opener) def load_model_from_file(file): return load(file) def load_model_from_path(path, *, buffering=-1, opener=None, **kwargs_extra): open_kwargs_extra = {} for k in ('closefd',): if k in kwargs_extra: open_kwargs_extra[k] = kwargs_extra.pop(k) if len(kwargs_extra) > 0: raise TypeError( 'Unexpected keyword arguments given: {}'.format(', '.join(kwargs_extra.keys())) ) with open_model_file( path, buffering=buffering, opener=opener, **open_kwargs_extra, ) as model_file: return load_model_from_file(model_file) class FuelType(Enum): PETROL = 'petrol' DIESEL = 'diesel' CNG = 'cng' class SellingType(Enum): DEALER = 'dealer' INDIVIDUAL = 'individual' class TransmissionType(Enum): MANUAL = 'manual' AUTOMATIC = 'automatic' @dataclass class PricePredictionFeatures: selling_price: float driven_kms: float age: float fuel_type: FuelType selling_type: SellingType transmission_type: TransmissionType metric_prediction_latency = Histogram( 'model_prediction_seconds', 'Время вычислений в модели', buckets=( list(chain.from_iterable((v * (10 ** p) for v in (1, 2, 5)) for p in range(-4, (1 + 1)))) + [float('+inf')] ), ) metric_prediction_errors = Counter( 'model_prediction_errors_total', 'Ошибки вычислений в модели по типу', ('error_type',), ) metric_prediction_value = Histogram( 'model_prediction_value', 'Предсказанное значение цены', buckets=( list(chain.from_iterable((v * (10 ** p) for v in (1, 2, 5)) for p in range(-1, (2 + 1)))) + [float('+inf')] ), ) class PricePredictor: def __init__(self, model_path): self._model = load_model_from_path(model_path) def predict(self, features): # WARN: порядок столбцов вроде имеет значение features_df = DataFrame([{ 'selling_price': features.selling_price, 'driven_kms': features.driven_kms, 'fuel_type': features.fuel_type.value, 'selling_type': features.selling_type.value, 'transmission': features.transmission_type.value, 'age': features.age, }]) try: with metric_prediction_latency.time(): predictions = self._model.predict(features_df) except Exception as err: metric_prediction_errors.labels(error_type=type(err).__name__).inc() raise assert len(predictions) == 1 value = float(predictions[0]) metric_prediction_value.observe(value) return value