Das Problem kennenlernen
In der kommerziellen Entwicklung implizieren viele Anwendungsfälle für maschinelles Lernen eine mandantenfähige Architektur und erfordern die Schulung eines separaten Modells für jeden Kunden und / oder Benutzer.
Betrachten Sie beispielsweise die Prognose von Einkäufen und Nachfrage nach bestimmten Produkten mithilfe von maschinellem Lernen. Wenn Sie eine Kette von Einzelhandelsgeschäften betreiben, können Sie anhand der Daten zur Kaufhistorie des Kunden und der Gesamtnachfrage nach diesen Produkten die Kosten und das Einkaufsvolumen für jedes Geschäft einzeln vorhersagen.
In solchen Fällen schreiben Sie zum Bereitstellen von Modellen meistens einen Flask-Service und legen ihn in einem Docker-Container ab. Es gibt viele Beispiele für maschinelle Lernserver mit einem Modell. Bei der Bereitstellung mehrerer Modelle stehen dem Entwickler jedoch nur wenige Optionen zur Verfügung, um das Problem zu lösen.
In Anwendungen mit mehreren Mandanten ist die Anzahl der Mandanten nicht im Voraus bekannt und kann praktisch unbegrenzt sein. In einem Moment haben Sie möglicherweise nur einen Client, und in einem anderen Moment können Sie Tausenden von Benutzern separate Modelle für jeden Benutzer bereitstellen. Hier beginnen sich die Einschränkungen des Standardbereitstellungsansatzes abzuzeichnen:
Wenn wir für jeden Client einen Docker-Container bereitstellen, erhalten wir eine sehr große und teure Anwendung, die nur schwer zu verwalten ist.
Ein einzelner Container, in dessen Image sich alle Modelle befinden, funktioniert auch bei uns nicht, da Tausende von Modellen auf dem Server arbeiten können und zur Laufzeit neue Modelle hinzugefügt werden.
Entscheidung
, . , Airflow S3, ML — .
ML — , : -> .
, :
Model — , ; SklearnModel, TensorFlowModel, MyCustomModel . .
ModelInfoRepository — , userid -> modelid. , SQAlchemyModelInfoRepository.
ModelRepository — , ID. FileSystemRepository, S3Repository .
from abc import ABC
class Model(ABC):
@abstractmethod
def predict(self, data: pd.DataFrame) -> np.ndarray:
raise NotImplementedError
class ModelInfoRepository(ABC):
@abstractmethod
def get_model_id_by_user_id(self, user_id: str) -> str:
raise NotImplementedError
class ModelRepository(ABC):
@abstractmethod
def get_model(self, model_id: str) -> Model:
raise NotImplementedError
, sklearn, Amazon S3 userid -> modelid, .
class SklearnModel(Model):
def __init__(self, model):
self.model = model
def predict(self, data: pd.DataFrame):
return self.model.predict(data)
class SQAlchemyModelInfoRepository(ModelInfoRepository):
def __init__(self, sqalchemy_session: Session):
self.session = sqalchemy_session
def get_model_id_by_user_id(user_id: str) -> str:
# implementation goes here, query a table in any Database
class S3ModelRepository(ModelRepository):
def __init__(self, s3_client):
self.s3_client = s3_client
def get_model(self, model_id: str) -> Model:
# load and deserialize pickle from S3, implementation goes here
:
def make_app(model_info_repository: ModelInfoRepository,
model_repsitory: ModelRepository) -> Flask:
app = Flask("multi-model-server")
@app.predict("/predict/<user_id>")
def predict(user_id):
model_id = model_info_repository.get_model_id_by_user_id(user_id)
model = model_repsitory.get_model(model_id)
data = pd.DataFrame(request.json())
predictions = model.predict(data)
return jsonify(predictions.tolist())
return app
, Flask ; sklearn TensorFlow S3 , Flask .
, , . , . cachetools:
from cachetools import Cache
class CachedModelRepository(ModelRepository):
def __init__(self, model_repository: ModelRepository, cache: Cache):
self.model_repository = model_repository
self.cache = cache
@abstractmethod
def get_model(self, model_id: str) -> Model:
if model_id not in self.cache:
self.cache[model_id] = self.model_repository.get_model(model_id)
return self.cache[model_id]
:
from cachetools import LRUCache
model_repository = CachedModelRepository(
S3ModelRepository(s3_client),
LRUCache(max_size=10)
)
- , . , , MLOps . . , . №4 Google: , - .