ZeroRPC szolgáltatás használata ML-modell üzembe helyezéséhez

Ez a blogbejegyzés a három korábbi blogbejegyzésben elkezdett ötletekre épít.

Ebben a blogbejegyzésben bemutatom, hogyan telepíthetjük ugyanazt az ML-modellt, amelyet kötegelt munkaként ebben a blogbejegyzésben, feladatsorként ebben a blogbejegyzésben, egy AWS lambdán belül ebben a blogbejegyzésben , Kafka streaming alkalmazásként ebben a blogbejegyzésben, gRPC szolgáltatásként ebben a blogbejegyzésben, MapReduce munkaként ebben a blogbejegyzésben és Websocket szolgáltatásként ebben a blogbejegyzésben.

A blogbejegyzésben szereplő kód megtalálható ebben a github repo-ban.

Bevezetés

Két szoftverfolyamat sokféleképpen kommunikálhat egymással. Gépi tanulási modell telepítésekor gyakran egyszerűbb elkülöníteni a modellkódot a saját folyamatán belül. Minden olyan kódnak, amelynek használnia kell a modellt az előrejelzések készítéséhez, kommunikálnia kell a modellkódot futtató folyamattal, hogy előrejelzéseket készítsen. Ez a megközelítés egyszerűbb, mint beágyazni a modellkódot az előrejelzéseket igénylő folyamatba, mert megkímél minket attól a fáradságtól, hogy újra létrehozzuk a modell algoritmusát az előrejelzéseket igénylő folyamat programozási nyelvén. Az RPC-hívásokat széles körben használják a különböző folyamatokban futó kódok összekapcsolására is. Az elmúlt néhány évben a mikroszolgáltatási architektúrák népszerűségének növekedése az RPC népszerűségének növekedését is okozta az integráló rendszerekben.

Az RPC a Remote Procedure Call rövidítése. A távoli eljárás csak egy függvényhívás, amely a hívást kezdeményező folyamattól eltérő folyamatban kerül végrehajtásra. A hívás bemeneti paraméterei a hívási folyamatból származnak, és a hívás eredménye visszakerül a hívási folyamatba. A függvényhívás úgy néz ki, mintha helyileg hajtották volna végre. Az RPC ezért kérés-válasz protokollként fut. A hívást kezdeményező folyamatot kliensnek, a hívást végrehajtó folyamatot pedig szervernek nevezzük. Az RPC akkor hasznos, ha olyan függvényt szeretne meghívni, amely nem a helyi folyamatban van implementálva, és nem szeretne aggódni a folyamatok közötti kommunikáció bonyolultsága miatt. Az RPC hasonló, de sokkal egyszerűbb, mint a REST és HTTP alapú folyamatok közötti kommunikáció.

Az RPC hívás egy sor lépést követ a hívás befejezéséhez. Először az ügyfélkód hívja meg a „csonknak” nevezett kódrészletet az ügyfélfolyamatban. A csonk normál függvényként viselkedik, de valójában meghívja a távoli eljárást a szerveren. A csonk ezután átveszi a kliens kódja által megadott paramétereket, és sorba rendezi azokat, hogy a kommunikációs csatornán továbbíthatók legyenek. A csonk a kommunikációs csatornát használja a távoli folyamattal való kommunikációhoz, és elküldi az eljárás végrehajtásához szükséges információkat. A kiszolgáló csonkja megkapja az információkat, deszerializálja a paramétereket, majd végrehajtja az eljárást. A lépések sorozatát ezután fordított sorrendben hajtják végre, hogy az eljárás eredményeit visszaküldjék az ügyfélkódba.

A korábbi blogbejegyzésekben bemutattuk, hogyan lehet RPC-t csinálni RESTful szolgáltatással és gRPC szolgáltatással. Ebben a blogbejegyzésben folytatjuk a számunkra elérhető lehetőségek feltárását a folyamatok közötti kommunikációhoz egy olyan ZeroRPC szolgáltatással, amely képes a gépi tanulási modellek tárolására.

ZeroRPC

A ZeroRPC egy egyszerű RPC keretrendszer, amely számos különböző nyelven működik. A ZeroRPC a MessagePack-et használja a paraméterek szerializálására és deszerializálására, a ZeroMQ-t pedig a folyamatok közötti adatátvitelre. A ZeroRPC támogatja a fejlett funkciókat, például a streamelt válaszokat, a szívveréseket és az időtúllépéseket. A keretrendszer támogatja a szolgáltatás és a kivételek önvizsgálatát is.

A ZeroRPC keretrendszer a ZeroMQ üzenetküldő keretrendszert használja az üzenetek folyamatok közötti továbbítására. A ZeroMQ egy nagy teljesítményű, alacsony szintű üzenetküldő keretrendszer, amely sokféle kommunikációs mintában használható. A ZeroRPC keretrendszer a ZeroMQ keretrendszert használja kérés-válasz mintában az RPC-hívások végrehajtásához. A ZeroMQ a közzétételi-feliratkozási mintát is támogatja más mintákkal együtt. A ZeroMQ-t úgy tervezték, hogy támogassa a nagymértékben elosztott és párhuzamos alkalmazásokat. A ZeroMQ számos különböző programozási nyelven és számos operációs rendszerben működik.

A ZeroRPC keretrendszer a MessagePack formátumot használja a szerializáláshoz. Ez a formátum hasonló a JSON-hoz, de bináris, ami helytakarékosabbá teszi, és gyorsabb szerializálást és deszerializálást tesz lehetővé. A MessagePack formátum hasonló a gRPC által használt Protocol Buffer formátumhoz, de lehetővé teszi tetszőleges adatszerkezetek sorba rendezését. Ez különbözik a protokollpufferektől, amelyek sémát igényelnek az adatok sorba rendezéséhez. A MessagePack szintén dinamikusan begépelt, ami gyorsabbá és egyszerűbbé teszi a kódfejlesztést vele, de hiányzik a protokollpufferek dokumentációs és kódgenerálási funkciói.

Csomag felépítése

A szolgáltatás kódbázisa a következőképpen épül fel:

- model_zerorpc_service (python package for the zerorpc service)
    - __init__.py
    - config.py
    - ml_model_zerorpc_endpoint.py
    - ml_model_manager.py
    - service.py
- scripts (scripts for testing the service)
- tests (unit tests) 
- Dockerfile (used to build a docker image of the service)
- Makefle  
- README.md
- requirements.txt (dependencies of the service)
- setup.py
- test_requirements.txt

Ez a struktúra a „github repository”-ban látható.

A modell telepítése

Ezzel a blogbejegyzéssel az a célunk, hogy bemutassuk, hogyan lehet felépíteni egy olyan ZeroRPC szolgáltatást, amely képes bármilyen ML-modellt fogadni, amely az MLModel alaposztállyal működik. Annak bemutatására, hogyan lehet ezt megtenni, ugyanazt a modellt fogjuk használni, amelyet a korábbi blogbejegyzésekben is bemutattunk. A modell Python környezetbe történő telepítéséhez hajtsa végre ezt a parancsot:

pip install git+https://github.com/schmidtbri/ml-model-abc-improvements

Ez a parancs telepíti a modellkódot és a paramétereket a modell git tárolójából. A modellkód működésének megértéséhez tekintse meg ezt a blogbejegyzést. A modell telepítése után tesztelhetjük a Python kód végrehajtásával egy interaktív munkamenetben:

>>> from iris_model.iris_predict import IrisModel
>>> model = IrisModel()
>>> model.predict({“sepal_length”:1.1, “sepal_width”: 1.2, “petal_width”: 1.3, “petal_length”: 1.4})
{‘species’: ‘setosa’}

A fenti kód importálja az MLModel felületet megvalósító osztályt, példányosítja azt, és előrejelzési kérést küld a modellobjektumnak. A modell sikeresen válaszol a virágfajra vonatkozó előrejelzéssel.

Annak érdekében, hogy a ZeroRPC szolgáltatás megtalálja a telepíteni kívánt modellt, létrehozunk egy konfigurációs modult, amely a modell csomagjára és moduljára mutat:

class Config(dict):
    models = [{
        “module_name”: “iris_model.iris_predict”,
        “class_name”: “IrisModel”
    }]

A fenti kód "itt" található.

Ez a konfiguráció rugalmasságot biztosít számunkra, hogy dinamikusan adjunk hozzá és távolítsunk el modelleket a szolgáltatásból. Egy szolgáltatás tetszőleges számú modellt tárolhat, ha telepítve vannak a környezetben, és hozzáadják a konfigurációhoz. A konfigurációs modul_name és class_name mezői egy olyan osztályra mutatnak, amely megvalósítja az MLModel felületet, amely lehetővé teszi a szolgáltatás számára, hogy előrejelzéseket készítsen a modell használatával.

A korábbi blogbejegyzésekhez hasonlóan egy egyedi objektumot fogunk használni az előrejelzések készítéséhez használt ML-modellobjektumok kezelésére. Azt az osztályt, amelyből az egytagú objektum példányosodik, ModelManagernek hívják. Az osztály felelős az MLModel objektumok példányosításáért, a példányok kezeléséért, az MLModel objektumokkal kapcsolatos információk visszaadásáért, és szükség esetén az objektumokra való hivatkozások visszaadásáért. A ModelManager osztály kódja itt található. A ModelManager osztály teljes leírása megtalálható ebben a blogbejegyzésben.

ZeroRPC végpont

A gépi tanulási modell hosztolásához kezelnünk kell a bejövő előrejelzési kéréseket, válaszokat kell készítenünk rájuk, és integrálnunk kell a ZeroRPC keretrendszerrel. Az ebben a részben leírt osztály kezeli a szolgáltatás ezen szempontjait.

Először deklaráljuk az osztályt:

class MLModelZeroRPCCEndpoint(object):

A fenti kód "itt" található.

Ezután hozzáadjuk a kódot, amely inicializálja az objektumot, amikor az osztály példányosodik:

def __init__(self, model_qualified_name):
    model_manager = ModelManager()
    model_instance = model_manager.get_model(model_qualified_name)
    
    if model_instance is None:
        raise ValueError(“‘{}’ not found in ModelManager   
            instance.”.format(model_qualified_name))
    self._model = model_manager.get_model(model_qualified_name)
    self.__doc__ = “Predict with the 
        {}.”.format(self._model.display_name)

A fenti kód "itt" található.

Az __init__ metódusnak van egy „modell_qualified_name” nevű argumentuma, amely megmondja a végpontosztálynak, hogy melyik modellt fogja tárolni. Az __init__ metódus először hivatkozást kap a ModelManager egyszemélyes példányára, amely a szolgáltatás indulásakor inicializálódik. Ezután hivatkozást kapunk arra a konkrét modellre, amelyet az MLModelZeroRPCCEndpoint osztály ezen példánya tárol. Ezután ellenőrizzük, hogy a modellhivatkozás egyenlő-e a None értékkel, ami akkor történik, ha a ModelManager nem találja a kért névvel rendelkező modellt, ha pedig nincs a név alapján, kivételt teszünk. Ha a modell létezik, elmentünk egy hivatkozást a saját változóba, ami megkönnyíti a hozzáférést a jövőben. Végül módosítjuk a saját változó docstring tulajdonságát, ami miatt a szolgáltatás visszaadja azt az önvizsgálat során, majd meglátjuk, hogyan működik ez később.

Most, hogy megvan a végpont példánya, kezelnünk kell a bejövő előrejelzési kéréseket:

def __call__(self, data):
    prediction = self._model.predict(data=data)
    return prediction

A fenti kód "itt" található.

A metódusban szereplő kód nagyon egyszerű, megkapja az „adat” nevű paramétert a klienstől, elküldi a modell predikciós metódusának, és visszaadja a modell által visszaadott predikciós objektumot. A színfalak mögött a ZeroRPC keretrendszer kezeli a szerializálást és a deszerializálást, a kliens és a szerver közötti kapcsolatot, valamint a szerver által felvetett kivételeket.

Ez az osztály a Python egy speciális funkcióját használja, amely a „hívható mágikus módszer”. A __call__ metódus egy speciális metódus, amely az MLModelZeroRPCCEndpoint osztály bármely példányát hívhatóvá alakítja, amely lehetővé teszi az osztály példányainak függvényként vagy metódusként történő használatát. Ez később hasznos lesz, amikor dinamikus számú végpontot kell inicializálnunk a gRPC szolgáltatásban.

ZeroRPC szolgáltatás

Most, hogy van egy modellünk és egy módunk arra, hogy a modellt egy végponton belül hosztoljuk, megírhatjuk a szolgáltatást létrehozó kódot. Mielőtt ezt megtehetnénk, be kell töltenünk a konfigurációt:

configuration = getattr(config, os.environ[“APP_SETTINGS”])

A fenti kód "itt" található.

A konfiguráció dinamikusan töltődik be egy osztály importálásával a config.py modulból. Az osztály nevét egy APP_SETTINGS nevű környezeti változó fogadja.

A ZeroRPC szolgáltatás olyan osztályként épül fel, amely olyan metódusokat biztosít, amelyek RPC-hívásként vannak kitéve a külvilágnak. A szolgáltatássá váló osztály a következőképpen van meghatározva:

class ModelZeroRPCService(object):

A fenti kód "itt" található.

A modellszolgáltatás indításakor az __init__ metódus kerül végrehajtásra:

def __init__(self):
    self.model_manager = ModelManager() 
    self.model_manager.load_models(configuration=
        configuration.models)
    for model in self.model_manager.get_models():
        endpoint = MLModelZeroRPCCEndpoint(model_qualified_name=
            model[“qualified_name”])
    operation_name = “{}_predict”.format(model[“qualified_name”])
    setattr(self, operation_name, endpoint)

A fenti kód "itt" található.

A szolgáltatás a ModelManager singleton példányosításával és a modellek konfigurációból történő betöltésével kezdődik. Ezután a szolgáltatás példányosít egy MLModelZeroRPCCEndpoint osztályt minden modellhez a ModelManagerben, és csatolja azt a „self” paraméterhez egy dinamikusan létrehozott műveletnévvel. A szolgáltatási metódust a modell „predict” metódusára képezi le az azt becsomagoló végpont objektum. Ennek az az oka, hogy tetszőleges számú MLModel objektumot tudunk hosztolni a szolgáltatásban, ez a kód lehetővé teszi, hogy dinamikusan csatoljuk őket a szolgáltatásobjektumhoz. Az inicializálási módszer végén minden, a szolgáltatás által üzemeltetett modellhez van egy szolgáltatási módszerünk.

A szolgáltatás mostantól képes előrejelzési kéréseket fogadni a modellekhez, és visszaküldeni az előrejelzéseket az ügyfeleknek, de hozzáadhatunk néhány funkciót a tárolt modellek metaadatainak közzétételével, a get_models metódus ezt teszi:

def get_models(self):
    models = self.model_manager.get_models()
    return models

A fenti kód "itt" található.

A get_models eljárás a használható modellek listáját adja vissza, de nem adja vissza a modellhez rendelkezésre álló összes metaadatot. A modell összes metaadatának biztosításához hozzáadjuk a get_model_metadata metódust:

def get_model_metadata(self, qualified_name):
    model_metadata = self.model_manager.get_model_metadata(  
        qualified_name=qualified_name)
    
    if model_metadata is not None:
        return model_metadata
    else:
        raise ValueError(“Metadata not found for this model.”)

A fenti kód "itt" található.

A Szolgáltatás használata

A szolgáltatás használatának bemutatásához írtunk néhány szkriptet a projekt "scripts mappájába". A szkriptek végrehajtásához először el kell indítanunk a szolgáltatást a következő parancsokkal:

export PYTHONPATH=./
export APP_SETTINGS=ProdConfig
python model_zerorpc_service/service.py

A ZeroRPC Python csomag rendelkezik egy segédprogrammal, amely lehetővé teszi a kommunikációt a ZeroRPC szolgáltatással a parancssorból. Most, hogy fut egy ZeroRPC szolgáltatás, végrehajthatjuk ezt a parancsot, hogy megkapjuk a szolgáltatásban elérhető eljárások listáját:

zerorpc tcp://127.0.0.1:4242
connecting to “tcp://127.0.0.1:4242”
[ModelZeroRPCService]
get_model_metadata  Return metadata about a model hosted by the  
                    service.
get_models          Return list of models hosted in this service.
iris_model_predict  Predict with the Iris Model.

A ZeroRPC eszköz visszaadja a szolgáltatásban elérhető módszerek leírását. Az iris_model_predict eljárás dokumentációs karakterlánca akkor jött létre, amikor a modell végpontját példányosítottuk.

Ezután meghívunk egy eljárást a szolgáltatásban Python kóddal. Az elérhető modellek listája a „get_models” eljárás meghívásával nagyon egyszerű:

client = zerorpc.Client()
client.connect(“tcp://127.0.0.1:4242”)
result = client.get_models()
print(“Result: {}”.format(result))

A fenti kód "itt" található.

A kód futtatásakor ki kell nyomtatnia a szolgáltatás által üzemeltetett modellek listáját:

Result: [{‘display_name’: ‘Iris Model’, ‘qualified_name’: ‘iris_model’, ‘description’: ‘A machine learning model for predicting the species of a flower based on its measurements.’, ‘major_version’: 0, ‘minor_version’: 1}]

A szolgáltatással előrejelzést készíteni ugyanolyan egyszerű:

client = zerorpc.Client()
client.connect(“tcp://127.0.0.1:4242”)
result = client.iris_model_predict(
    {“sepal_length”: 1.1, 
     “sepal_width”: 1.2,
     “petal_length”: 1.4, 
     “petal_width”: 1.5})
print(“Result: {}”.format(result))

A fenti kód "itt" található.

Annak megtekintéséhez, hogy a ZeroRPC szolgáltatás hogyan kezeli a kivételeket, megváltoztatjuk az ügyfél kódját, hogy szándékosan okozzon kivételt az MLModel osztályban:

client = zerorpc.Client()
client.connect(“tcp://127.0.0.1:4242”)
result = client.iris_model_predict(
    {“sepal_length”: 1.1, 
     “sepal_width”: 1.2,
     “petal_length”: 1.4, 
     “petal_width”: “abc”})
print(“Result: {}”.format(result))

Amikor végrehajtjuk az ügyfélkódot, ezt a kivételt kapjuk:

python scripts/predict_with_model.py
Traceback (most recent call last):
File “scripts/predict_with_model.py”, line 15, in <module>
…
File /Users/brian/Code/zerorpc-ml-model-deployment/venv/lib/python3.7/site-packages/iris_model/iris_predict.py”, line 51, in predict
raise MLModelSchemaValidationException(“Failed to validate input data: {}”.format(str(e)))
ml_model_abc.MLModelSchemaValidationException: Failed to validate input data: Key ‘petal_width’ error: ‘abc’ should be instance of ‘float’

Záró

Ebben a blogbejegyzésben bemutattuk, hogyan lehet gépi tanulási modellt üzembe helyezni a ZeroRPC keretrendszer használatával. A szolgáltatás tetszőleges számú, az MLModel felületet megvalósító modellt képes fogadni. A szolgáltatás kódbázisa egyszerűbb, mint egy RESTful szolgáltatás, és könnyebb, mint a REST webszolgáltatások által általában használt JSON szerializációs formátum. Az RPC-szolgáltatások is könnyebben érthetők, mint a REST-szolgáltatások, mivel az ügyféloldalon egy normál függvényhívást utánoznak.

A ZeroRPC szolgáltatásnak van néhány előnye, de vannak hátrányai is a gRPC-hez képest. A ZeroRPC keretrendszernek nincs módja sémainformációk biztosítására a szolgáltatás kérését és válaszait alkotó adatstruktúrákhoz. Összehasonlításképpen a gRPC protokollpufferek megkövetelik, hogy a szolgáltatás fejlesztője teljes körű adatszolgáltatási szerződést biztosítson a szolgáltatásra, és a REST-szolgáltatások rendelkeznek JSON-sémával és OpenAPI-specifikációval, amely képes biztosítani ezeket az információkat a szolgáltatásról. A get_model_metadata végpont felépítésével ezeket az információkat a szolgáltatásban tárolt minden modellhez tudtuk biztosítani, de nem a teljes szolgáltatásra vonatkozóan.

A ZeroRPC keretrendszer több funkcionalitást biztosít az RPC-hívásokhoz azáltal, hogy lehetővé teszi a szerver számára, hogy a válaszokat visszaküldje az ügyfélnek. Ez lehetővé teszi a szerver számára, hogy visszaküldje az előrejelzési válaszokat, amint azok elérhetővé válnak a szerveren, és egyszerű felületet biztosít. A jövőben érdekes lenne kihasználni a ZeroRPC ezen funkcióját az előrejelzési válaszok streamelésére az ügyfélnek.