ai-center
2020.10
false
UiPath logo, featuring letters U and I in white

AI Center

Automation CloudAutomation SuiteStandalone
Dernière mise à jour 6 juin 2024

Construire des paquets ML

Les data scientists créent des modèles pré-entraînés à l’aide de Python ou d’une plate-forme AutoML. Ces modèles sont utilisés par les développeurs RPA dans un workflow.

Structurer les paquets ML

Tout package doit respecter un petit ensemble d'exigences. Ces exigences sont divisées entre les composants nécessaires pour servir un modèle et les composants nécessaires pour entraîner un modèle.

Composant de service

Un package doit fournir au moins les éléments suivants :
  • Un dossier contenant un fichier main.py à la racine de ce dossier.
  • Dans ce fichier, une classe appelée Main implémente au moins deux fonctions :
    • _init_(self) : ne prend aucun argument et charge votre modèle et/ou les données locales du modèle (par exemple intégrations de mots).
    • predict(self, input): une fonction à appeler au moment du service du modèle.
  • Un fichier nommé requirements.txt avec les dépendances nécessaires pour exécuter le modèle.
Considérez le composant de service d'un package comme le modèle au moment de l'inférence. Au moment du service, une image de conteneur est créée à l'aide du fichier requirements.txt fourni, et la fonction predict est utilisée comme point de terminaison du modèle.

Composant d'entraînement et d'évaluation

Outre l'inférence, un package peut éventuellement être utilisé pour entraîner un modèle d'apprentissage automatique. Cela s'effectue en fournissant les éléments suivants :

  1. Dans le même dossier racine que le fichier main.py, fournissez un fichier nommé train.py.
  2. Dans ce fichier, fournissez une classe appelée Main qui implémente au moins deux fonctions : Toutes les fonctions ci-dessous, à l'exception de _init_, sont facultatives, mais limitent le type de pipelines pouvant être exécutés avec le package correspondant.
    1. _init_(self) : ne prend aucun argument et charge votre modèle et/ou les données du modèle (par exemple intégrations de mots).
    2. train(self, training_directory) : prend en tant qu'entrée un répertoire avec des données arbitrairement structurées, et exécute tout le code nécessaire pour entraîner un modèle. Cette fonction est appelée chaque fois qu'un pipeline d'entraînement est exécuté.
    3. c. evaluate(self, evaluation_directory) : prend en tant qu'entrée un répertoire avec des données arbitrairement structurées, exécute tout le code nécessaire pour évaluer un mode, et renvoie un score unique pour cette évaluation. Cette fonction est appelée chaque fois qu'un pipeline d'évaluation est exécuté.
    4. save(self) : ne prend aucun argument. Cette fonction est appelée après chaque appel de la fonction train pour conserver votre modèle.
    5. process_data(self, input_directory) : prend une entrée input_directory avec des données arbitrairement structurées. Cette fonction n'est appelée que lorsqu'un pipeline complet est exécuté. Dans l'exécution d'un pipeline complet, cette fonction peut effectuer des transformations de données arbitraires et peut fractionner les données. Plus précisément, toutes les données enregistrées dans le chemin indiqué par la variable d'environnement training_data_directory seront l'entrée de la fonction train, et toutes les données enregistrées dans le chemin indiqué par la variable d'environnement evaluation_data_directory seront l'entrée de la fonction evaluation ci-dessus.

Gérer les types de données

Pour faciliter l'utilisation d'AI Fabric dans un workflow RPA, le package peut être défini comme ayant l'un de ces trois types d'entrée : Chaîne, Fichier et Fichiers (défini au moment du téléchargement du package).

Données de la chaîne

Il s'agit d'une séquence de caractères. Toutes les données pouvant être sérialisées peuvent être utilisées avec un package. Si elles sont utilisées dans un workflow RPA, les données peuvent être sérialisées par le Robot (par exemple à l'aide d'une activité personnalisée) et envoyées sous forme de chaîne. Le téléchargeur du package doit avoir sélectionné json comme type d'entrée du package.
La désérialisation des données se fait dans la fonction predict. Vous trouverez ci-dessous quelques exemples de désérialisation de données en Python :
<h1>Robot sends raw string to ML Skill Activity
# E.g. skill_input='a customer complaint'`
def predict(self, skill_input):
  example = skill_input  # No extra processing
    
# Robot sends json formatted string to ML Skill Activity
# E.g skill_input='{'email': a customer complaint', 'date': 'mm:dd:yy'}'
def predict(self, skill_input):
  import json
  example = json.loads(skill_input)
  
# Robot sends json formatted string with number array to ML Skill Activity
# E.g. skill_input='[10, 15, 20]'
def predict(self, skill_input):
  import json
  import numpy as np
  example = np.array(json.loads(skill_input))
  
# Robot sends json formmatted pandas dataframe
# E.g. skill_input='{"row 1":{"col 1":"a","col 2":"b"},
#                    "row 2":{"col 1":"c","col 2":"d"}}'
def predict(self, skill_input):
  import pandas as pd
  example = pd.read_json(skill_input)</h1><h1>Robot sends raw string to ML Skill Activity
# E.g. skill_input='a customer complaint'`
def predict(self, skill_input):
  example = skill_input  # No extra processing
    
# Robot sends json formatted string to ML Skill Activity
# E.g skill_input='{'email': a customer complaint', 'date': 'mm:dd:yy'}'
def predict(self, skill_input):
  import json
  example = json.loads(skill_input)
  
# Robot sends json formatted string with number array to ML Skill Activity
# E.g. skill_input='[10, 15, 20]'
def predict(self, skill_input):
  import json
  import numpy as np
  example = np.array(json.loads(skill_input))
  
# Robot sends json formmatted pandas dataframe
# E.g. skill_input='{"row 1":{"col 1":"a","col 2":"b"},
#                    "row 2":{"col 1":"c","col 2":"d"}}'
def predict(self, skill_input):
  import pandas as pd
  example = pd.read_json(skill_input)</h1>

Données du fichier

Cela indique à l'activité de compétence ML effectuant des appels vers ce modèle de s'attendre à un chemin d'accès vers un fichier. Plus précisément, l'activité lit le fichier à partir du système de fichiers et l'envoie à la fonction predict sous la forme d'une chaîne d'octets sérialisée. Ainsi, le RPA Developer peut définir un chemin d'accès vers un fichier, au lieu d'avoir à lire et à sérialiser le fichier dans le workflow même.
Dans le workflow, l'entrée de l'activité sera simplement le chemin d'accès au fichier. L'activité lit le fichier, le sérialise et envoie les octets du fichier à la fonction predict. La désérialisation des données se fait également dans la fonction predict, le cas général consistant simplement à lire les octets directement dans un objet de type fichier tel que ci-dessous :
<h1>ML Package has been uploaded with <em>file</em> as input type. The ML Skill Activity
# expects a file path. Any file type can be passed as input and it will be serialized.
def predict(self, skill_input):
  import io
  file_like = io.BytesIO(skill_input)</h1><h1>ML Package has been uploaded with <em>file</em> as input type. The ML Skill Activity
# expects a file path. Any file type can be passed as input and it will be serialized.
def predict(self, skill_input):
  import io
  file_like = io.BytesIO(skill_input)</h1>

Lire les octets sérialisés comme ci-dessus équivaut à ouvrir un fichier en activant l'indicateur de lecture binaire. Pour tester le modèle localement, lisez un fichier en tant que fichier binaire. Voici un exemple de lecture d'un fichier image et de test local de ce fichier :

<h1>main.py where model input is an image
class Main(object):
   ...
    
   def predict(self, skill_input): 
      import io
      from PIL import Image
      image = Image.open(io.BytesIO(skill_input))
   ...
  
if__name__ == '<strong>main</strong>':
   # Test the ML Package locally
   with open('./image-to-test-locally.png', 'rb') as input_file:
      file_bytes = input_file.read()
     m = Main()
     print(m.predict(file bytes))</h1><h1>main.py where model input is an image
class Main(object):
   ...
    
   def predict(self, skill_input): 
      import io
      from PIL import Image
      image = Image.open(io.BytesIO(skill_input))
   ...
  
if__name__ == '<strong>main</strong>':
   # Test the ML Package locally
   with open('./image-to-test-locally.png', 'rb') as input_file:
      file_bytes = input_file.read()
     m = Main()
     print(m.predict(file bytes))</h1>
Ce qui suit est un exemple de lecture d'un fichier csv et d'utilisation d'un cadre de données pandas dans la fonction predict :
<h1>main.py where model input is a csv file
class Main(object):
   ...
   def predict(self, skill_input): 
      import pandas as pd
      data frame = pd.read_csv(io.BytesIO(skill_input))
      ...
      
if name == '<strong>main</strong>':
   # Test the ML Package locally
   with open('./csv—to—test—locally.csv', 'rb') as input_file:
      bytes = input_file.read()
   m = Main()
   print(m.predict(bytes))</h1><h1>main.py where model input is a csv file
class Main(object):
   ...
   def predict(self, skill_input): 
      import pandas as pd
      data frame = pd.read_csv(io.BytesIO(skill_input))
      ...
      
if name == '<strong>main</strong>':
   # Test the ML Package locally
   with open('./csv—to—test—locally.csv', 'rb') as input_file:
      bytes = input_file.read()
   m = Main()
   print(m.predict(bytes))</h1>

Données du fichier

Cela informe AI Fabric que l'activité de Compétence ML effectuant des appels vers ce modèle attend une liste de chemins de fichiers. Comme dans le cas précédent, l'activité lit et sérialise chaque fichier, puis envoie une liste de chaînes d'octets à la fonction predict.

Une liste de fichiers peut être envoyée à une compétence. Dans le workflow, l'entrée de l'activité est une chaîne avec des chemins d'accès aux fichiers séparés par une virgule.

Lors du téléchargement d'un package, le scientifique des données sélectionne une liste de fichiers comme type d'entrée. Le scientifique des données doit alors désérialiser chacun des fichiers envoyés (comme expliqué ci-dessus). L'entrée de la fonction predict est une liste d'octets dans laquelle chaque élément est la chaîne d'octets du fichier.

Données arbitraires persistantes

Dans train.py, tout pipeline exécuté peut conserver des données arbitraires appelées sortie de pipeline. Toutes les données écrites dans le chemin du répertoire à partir d'artefacts de variables d'environnement sont conservées et peuvent être affichées à tout moment en accédant à la Page Détails du Pipeline. En règle générale, tout type de graphique, toute statistique des tâches d'entraînement/évaluation peut être enregistré dans le répertoireartifacts et est accessible depuis l'interface utilisateur à la fin de l'exécution du pipeline.
<h1>train.py where some historical plot are saved in ./artifacts directory during Full Pipeline execution
# Full pipeline (using process_data) will automatically split data.csv in 2/3 train.csv (which will be in the directory passed to the train function) and 1/3 test.csv
import pandas as pd
from sklearn.model_selection import train_test_split
class Main(object):
   ...
   def process_data(self, data_directory):
     d = pd.read_csv(os.path.join(data_directory, 'data.csv')) 
     d = self.clean_data(d)
     d_train, d_test = train_test_split(d, test_size=0.33, random_state=42)
     d_train.to_csv(os.path.join(data_directory , 'training', 'train.csv'), index=False)
     d_test.to_csv (os.path.join(data__directory , 'test' , 'test.csv'), index=False)
     self.save_artifacts(d_train, 'train_hist.png', os.environ["artifacts"])
     self.save_artifacts(d_test, 'test_hist.png', os.environ["artifacts"])
  ...
  
   def save_artifacts(self, data, file_name, artifact_directory):
      plot = data.hist() 
      fig = plot[0][0].get_figure()
      fig.savefig(os.path.join(artifact_directory, file_name))
...</h1><h1>train.py where some historical plot are saved in ./artifacts directory during Full Pipeline execution
# Full pipeline (using process_data) will automatically split data.csv in 2/3 train.csv (which will be in the directory passed to the train function) and 1/3 test.csv
import pandas as pd
from sklearn.model_selection import train_test_split
class Main(object):
   ...
   def process_data(self, data_directory):
     d = pd.read_csv(os.path.join(data_directory, 'data.csv')) 
     d = self.clean_data(d)
     d_train, d_test = train_test_split(d, test_size=0.33, random_state=42)
     d_train.to_csv(os.path.join(data_directory , 'training', 'train.csv'), index=False)
     d_test.to_csv (os.path.join(data__directory , 'test' , 'test.csv'), index=False)
     self.save_artifacts(d_train, 'train_hist.png', os.environ["artifacts"])
     self.save_artifacts(d_test, 'test_hist.png', os.environ["artifacts"])
  ...
  
   def save_artifacts(self, data, file_name, artifact_directory):
      plot = data.hist() 
      fig = plot[0][0].get_figure()
      fig.savefig(os.path.join(artifact_directory, file_name))
...</h1>

Utilisation de TensorFlow

Lors du développement du modèle, le graphique TensorFlow doit être chargé sur le même thread que celui utilisé pour le service. Pour ce faire, le graphique par défaut doit être utilisé.

Voici un exemple incorporant les modifications nécessaires :

import tensorflow as tf
class Main(object):
  def <strong>init</strong>(self):
    self.graph = tf.get_default_graph() # Add this line
    ...
    
  def predict(self, skill_input):
    with self.graph.as_default():
      ...import tensorflow as tf
class Main(object):
  def <strong>init</strong>(self):
    self.graph = tf.get_default_graph() # Add this line
    ...
    
  def predict(self, skill_input):
    with self.graph.as_default():
      ...

Informations sur l'utilisation du GPU

Lorsque le GPU est activé au moment de la création de la compétence, il est déployé sur une image avec le pilote NVIDIA GPU 418, CUDA Toolkit 10.0 et la bibliothèque d'exécution CUDA Deep Neural Network Library (cuDNN) 7.6.5.

Exemples

Modèle de ML simple et prêt à l'emploi sans entraînement

Dans cet exemple, le problème métier ne nécessite pas de réentraînement du modèle ; le package doit donc contenir le modèle sérialisé IrisClassifier.sav qui sera servi.
  1. Arborescence initiale du projet (sans main.py et requirements.txt) :

    IrisClassifier/
      - IrisClassifier.savIrisClassifier/
      - IrisClassifier.sav
  2. Exemple de main.py à ajouter au dossier racine :

    from sklearn.externals import joblib 
    import json
    class Main(object):
       def <strong>init</strong>(self):
          self.model = joblib.load('IrisClassifier.sav')
       def predict(self, X):
          X = json.loads(X)
          result = self.model.predict_proba(X)
          return json.dumps(result.tolist())from sklearn.externals import joblib 
    import json
    class Main(object):
       def <strong>init</strong>(self):
          self.model = joblib.load('IrisClassifier.sav')
       def predict(self, X):
          X = json.loads(X)
          result = self.model.predict_proba(X)
          return json.dumps(result.tolist())
  3. Ajouter requirements.txt :

    scikit-learn==0.19.0scikit-learn==0.19.0
Remarque : certaines contraintes doivent être respectées sur les bibliothèques pip. Assurez-vous que vous pouvez installer les bibliothèques sous les fichiers de contraintes suivants :
itsdangerous<2.1.0
Jinja2<3.0.5
Werkzeug<2.1.0
click<8.0.0itsdangerous<2.1.0
Jinja2<3.0.5
Werkzeug<2.1.0
click<8.0.0

Pour tester cela, vous pouvez utiliser la commande suivante dans un nouvel environnement et vous assurer que toutes les bibliothèques s'installent correctement :

pip install -r requirements.txt -c constraints.txtpip install -r requirements.txt -c constraints.txt

4. Structure finale des dossiers :

IrisClassifier/
  - IrisClassifier.sav
  - main.py
  - requirements.txtIrisClassifier/
  - IrisClassifier.sav
  - main.py
  - requirements.txt

Modèle simple et prêt à l'emploi avec entraînement activé

Dans cet exemple, le problème métier nécessite un réentraînement du modèle. En s'appuyant sur le package décrit ci-dessus, vous pouvez disposer des éléments suivants :

  1. Arborescence du projet initial (package de service uniquement) :

    IrisClassifier/
      - IrisClassifier.sav
      - main.py
      - requirements.txtIrisClassifier/
      - IrisClassifier.sav
      - main.py
      - requirements.txt
  2. Exemple de train.py à ajouter au dossier racine :

    import pandas as pd 
    import joblib
    class Main(object): 
       def <strong>init</strong>(self):
           self.model_path = './IrisClassifier.sav' 
           self.model = joblib.load(self.model_path)
          
       def train(self, training_directory):
           (X,y) = self.load_data(os.path.join(training_directory, 'train.csv'))
           self.model.fit(X,y)
       def evaluate(self, evaluation_directory):
           (X,y) = self.load_data(os.path.join(evaluation_directory, 'evaluate.csv'))
           return self.model.score(X,y)
       def save(self):
           joblib.dump(self.model, self.model_path)
       def load_data(self, path):
           # The last column in csv file is the target column for prediction.
           df = pd.read_csv(path)
           X = df.iloc[:, :-1].get_values()
           y = df.iloc[:, 'y'].get_values()
           return X,yimport pandas as pd 
    import joblib
    class Main(object): 
       def <strong>init</strong>(self):
           self.model_path = './IrisClassifier.sav' 
           self.model = joblib.load(self.model_path)
          
       def train(self, training_directory):
           (X,y) = self.load_data(os.path.join(training_directory, 'train.csv'))
           self.model.fit(X,y)
       def evaluate(self, evaluation_directory):
           (X,y) = self.load_data(os.path.join(evaluation_directory, 'evaluate.csv'))
           return self.model.score(X,y)
       def save(self):
           joblib.dump(self.model, self.model_path)
       def load_data(self, path):
           # The last column in csv file is the target column for prediction.
           df = pd.read_csv(path)
           X = df.iloc[:, :-1].get_values()
           y = df.iloc[:, 'y'].get_values()
           return X,y
  3. Modifiez le fichier requirements.txt si nécessaire :

    pandas==1.0.1
    scikit-learn==0.19.0pandas==1.0.1
    scikit-learn==0.19.0
  4. Structure du dossier final (package) :

    IrisClassifier/
      - IrisClassifier.sav
      - main.py
      - requirements.txt
      - train.pyIrisClassifier/
      - IrisClassifier.sav
      - main.py
      - requirements.txt
      - train.py
    Remarque : ce modèle peut désormais être servi en premier et, à mesure que de nouveaux points de données entrent dans le système via Robot ou Human-in-the-Loop, des pipelines d'entraînement et d'évaluation peuvent être créés grâce à train.py.

Cette page vous a-t-elle été utile ?

Obtenez l'aide dont vous avez besoin
Formation RPA - Cours d'automatisation
Forum de la communauté UiPath
Uipath Logo White
Confiance et sécurité
© 2005-2024 UiPath Tous droits réservés.