Stellen Sie mehrere Modelle für maschinelles Lernen auf einem einzigen Server bereit

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: , - .








All Articles