Déployez plusieurs modèles de machine learning sur un seul serveur

Connaître le problème

Dans le développement commercial, de nombreux cas d'utilisation de l'apprentissage automatique impliquent une architecture multi-locataires et nécessitent la formation d'un modèle distinct pour chaque client et / ou utilisateur.





À titre d'exemple, envisagez de prévoir les achats et la demande pour certains produits à l'aide de l'apprentissage automatique. Si vous gérez une chaîne de magasins de vente au détail, vous pouvez utiliser les données de l'historique des achats des clients et la demande totale pour ces produits pour prévoir les coûts et les volumes d'achat pour chaque magasin individuellement.





Le plus souvent, dans de tels cas, pour déployer des modèles, vous écrivez un service Flask et le placez dans un conteneur Docker. Il existe de nombreux exemples de serveurs d'apprentissage automatique à modèle unique, mais lorsqu'il s'agit de déployer plusieurs modèles, le développeur dispose de peu d'options pour résoudre le problème.





Dans les applications multi-locataires, le nombre de locataires n'est pas connu à l'avance et peut être pratiquement illimité - à un moment donné, vous ne pouvez avoir qu'un seul client, et à un autre moment, vous pouvez servir des modèles distincts pour chaque utilisateur à des milliers d'utilisateurs. C'est là que les limites de l'approche de déploiement standard commencent à émerger:





  • Si nous déployons un conteneur Docker pour chaque client, nous nous retrouvons avec une application très volumineuse et coûteuse qui sera assez difficile à gérer.





  • Un seul conteneur, à l'image duquel il y a tous les modèles, ne fonctionne pas non plus pour nous, car des milliers de modèles peuvent fonctionner sur le serveur, et de nouveaux modèles sont ajoutés au moment de l'exécution.





Décision

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