Les processus et les informations sont au cœur de chaque entreprise. La vulnérabilité et l'opportunité de ce moment sont la question de savoir si votre entreprise peut automatiser vos processus en utilisant l'IA et récolter les récompenses de le faire. Chatgpt, un usage général de l'IA, nous a ouvert les yeux sur ce que l'IA peut faire. Ce qui compte maintenant, c'est diriger la puissance de l'IA vers vos problèmes commerciaux et déverrouiller la valeur de vos données propriétaires. Dans ce document, je vais vous montrer comment.
Vous ne voulez pas une IA qui peut simplement discuter; Ce que vous voulez vraiment, ce sont des automatisations qui effectuent le travail qui maintient votre entreprise en marche - alimentant les processus métier à grande précision et à l'échelle. La façon éprouvée de personnaliser l'IA vers vos processus métier est d'accorder un LLM sur vos données et de l'action que vous souhaitez que l'IA fonctionne.
Parlons spécifiquement du réglage fin que nous allons faire dans ce document et la technologie derrière. Voici les cinq outils que nous utiliserons largement:
À la fin de la journée, vous devriez retirer deux choses de ce document:
Je vais vous décrire un problème dur et réel, ce que les chercheurs de l'apprentissage automatique ont déjà fait et la nouvelle frontière que nous avons pu pousser à utiliser des outils Sota puissants. Nous allons former des modèles sur des GPU dans le cloud. Nous allons également mettre en action de puissantes pratiques MLOPS - en utilisant MLFlow pour organiser nos expériences et nos paramètres. Et en cours de route, je vais souligner le modèle de conception de ce projet afin que vous puissiez personnaliser la base de code pour vos propres projets d'apprentissage en profondeur.
Commençons par l'arrière-plan du problème.
Un bon processus pour trouver des problèmes appropriés pour l'apprentissage automatique et pour les ensembles de données de qualité consiste à commencer par parcourir des sites avec des repères. Les repères fournissent un cadre de référence pour le niveau de difficulté du problème pour l'apprentissage automatique, que nous utilisons pour mesurer nos progrès pendant le développement du modèle. Un ensemble de données particulier avec des références bien établies est l'ensemble de données de service injuste (TOS injuste); Voici une déclaration de problème intrigante pour cela: utilisez l'IA pour trouver toutes les clauses déloyales en termes de contrats de service. Le contexte est que la loi européenne sur les consommateurs sur les contrats déloyaux établit ce que sont les clauses déloyales et les différents types de clauses déloyales. Ce qui rend les Tos injustes parfaits pour la classification du texte, c'est qu'il a été étiqueté manuellement conformément à celui qui a été établi en droit européen.
Chalkidis et al. (2021) a appliqué huit méthodes d'apprentissage automatique différentes aux TO déloyaux et obtenus macro F1 allant de 75 à 83, et sur la figure 1 juste en dessous, nous extraitons de leurs résultats publiés.
| Méthode | Tos déloyal | |
|---|---|---|
| micro F1 | macro f1 | |
| Tfidf-svm | 94.7 | 75.0 |
| Bert | 95.6 | 81.3 |
| Roberta | 95.2 | 79.2 |
| Deberta | 95.5 | 80.3 |
| Forgeron | 95.5 | 80.9 |
| Bigbird | 95.7 | 81.3 |
| Bertide | 96.0 | 83.0 |
| Caselaw-Bert | 96.0 | 82.3 |
Les choses intéressantes que nous pouvons déduire de ce tableau sont:
En regardant les données, le déséquilibre des classes est certainement présent, ce qui est une bonne raison pour le premier et le deuxième point ci-dessus.
Il existe huit types différents de clauses déloyales. Les auteurs de cet article ont développé des modèles de classification multi-étiquettes pour les huit types, mais nous allons simplement construire un modèle de classification binaire qui classe une clause comme juste ou injuste.
Concevons comment nous allons le faire.
__init__.py expose les API publiques du module dans lequel il se trouve afin que nous puissions raccourcir facilement les importations pour des fonctionnalités profondément sous-modulées comme cet exemple: from . fine_tune_clsify_head import TransformerModule L'importation ci-dessus dans architectures/__init__.py permet architectures extérieures de code d'importer TransformerModule sans avoir à se souvenir de la chapelure conduisant à l'endroit où cette fonction se situe, comme ceci:
from architectures import TransformerModuleVoici à quoi ressemble la structure du projet de la racine du module Lightning_Mlflow:
.
├── architectures
│ ├── __init__.py
│ └── fine_tune_clsify_head.py
├── config
│ ├── __init__.py
│ └── train_config.py
├── data
│ ├── __init__.py
│ └── lex_glue.py
└── train.py
La question clé est de savoir pourquoi le projet est structuré de cette façon. Et la réponse est que la séparation des préoccupations est établie entre les 3 sous-modules et 1 point d'entrée. Voyons chacun de ces modules à tour de rôle:
architectures : Spécifie essentiellement tout ce que Pytorch doit savoir, sauf pour les donnéesconfig : contient tous les paramètres et peut être subdivisé en train_config.py et inference_config.py et n'importe quoi d'autredata : contient toute la logique et les paramètres de traitement des donnéestrain.py : le point de contrôle central; Il s'agit du seul morceau de code qui peut voir architectures , config et data ; Les appels SDK mlflow vont iciAvec cette brève introduction à l'écart, déplions nos manches et essayons de vraiment comprendre le code dans chacun des modules. Commençons par Architectures / Fine_Tune_Clsify_head.py. Ce code est organisé comme la figure 3 juste en dessous.

Il est clair que les principales méthodes à l'intérieur de cette classe à comprendre sont: le constructeur, le passage avant par le réseau, les mesures et les étapes. Nous en discuterons à leur tour. Commençons par le constructeur. Cet extrait est à quoi ressemble le code:
Ce qui est vraiment crucial ici, c'est que le constructeur charge un modèle pré-entraîné de la face de l'étreinte et met en place un réglage fin efficace des paramètres (PEFT). Le PEFT est le moyen de saignement pour amener les modèles de grands langues, et est une mise en œuvre du visage étreint de Qlora, qui est la combinaison de trois stratégies d'efficacité différentes: quantification, faible rang et adaptateur. La quantification est l'idée qu'un entier à un octet est d'une précision numérique suffisante pour les calculs de gradient dans l'apprentissage en profondeur. Le rang bas provient du concept d'algèbre linéaire selon lequel le rang de matrice est le nombre de dimensions non redondantes de l'espace vectoriel. Le réglage de l'adaptateur peut être compris comme une sorte de généralisation du réglage de la tête du réseau, en réalisant une partie des poids dans de nombreuses couches.
Regardons la méthode forward :
En tant que formation, un réseau neuronal implique l'alternance entre le rétropropal et Feed Forward, cela spécifie la partie avant, et la chose notable est que les arguments de forward sont: input_ids , attention_mask et label . Ce sont les sorties d'un tokenizer étreint, et ils nous permettent de lier le visage étreint et la foudre pytorch.
Ensuite, jetons un coup d'œil à la méthode _compute_metrics :
Il y a quelques choses à noter ici. Tout d'abord, observez comment les logits sont déballés à partir de l'évaluation de la fonction directe, puis convertis en probabilités via la fonction Softmax. Cela, à son tour, conduit à la prédiction binaire. Nous utilisons la prédiction et le réel ( batch["label"] ) pour calculer la précision, F1, la précision et le rappel.
Enfin, parlons de training_step , validation_step et test_step .
Ils se ressemblent presque, avec une différence importante étant training_step Retour outputs["loss"] au lieu de metrics . Cela a du sens parce que le rétropropre minimise la perte de manière itérative; training_step n'est que l'abstraction par Pytorch Lightning de la boucle d'entraînement.
Ok, nous avons maintenant terminé avec architectures , plongeons dans config / trains_config.py, qui ressemble à la figure 7 ci-dessous.
Ce code est explicite, mais il y a des choix à appeler. Le pretrained_model est défini sur roberta-base , et si nous devions le changer en tout nom de modèle de visage étreint qui prend en charge Autotokenizer, tout devrait encore fonctionner (testé). La précision du modèle s'est avérée la plus sensible à max_length et batch_size , donc ce sont les principaux hyperparamètres à jouer, et ils se comprobment tous les deux entre la précision et la charge de calcul. Sur Azure ML, j'ai utilisé l'instance standard_nc24ads_a100_v4. Le VRAM sur le GPU A100 s'est avéré être le facteur limitant de ces deux hyperparamètres. Ce que j'ai trouvé, c'est que 256 était le plus grand batch_size qui ne ferait pas que Cuda lance une erreur hors de la mémoire.
La détermination de max_length de 128 était plus facile car la grande majorité des clauses étaient sous cette limite de jeton.
Enfin, comme nous le verrons plus tard, max_epochs de 10 ont été choisies par essais et erreurs, car la métrique de validation F1 que nous suivons dans MLFlow était aplatie à ce moment-là.
Ok, parlons des données. Tout ce que vous devez savoir est dans la classe LexGlueDataModule dans data / lex_glue.py, et c'est sa structure:
Il y a pas mal de détails sur LexGlueDataModule , mais l'idée principale est que la méthode setup récupère les données directement à partir du visage étreint, puis une sorte de tokenisation doit se produire. Les détails de cette tokenisation se trouvent dans la méthode _shared_transform , qui n'est que l'interface de haut niveau pour la méthode de rappel _preprocess .
Enfin, nous sommes prêts pour Train.py, le point de contrôle central de tout. Rappelez-vous quand nous avons commencé cette section avec la structure du répertoire du projet? Cette structure communique un message important: que les dépendances sont résolues vers le bas dans l'arborescence du répertoire. En d'autres termes, nous avons restreint le code à appeler uniquement un autre code au même niveau ou plus bas. Cette restriction ne vient certainement pas de Python; C'est auto-imposé, et pourquoi? Pour éliminer les conjectures. Il ne fait aucun doute que la confusion des chaînes de dépendance est un problème majeur dans les grandes bases de code. Ce que tout cela signifie, c'est que train.py va importer les fonctionnalités de chacun des autres modules, puis mettre en mouvement une course d'entraînement. Regardons le morceau de code au cœur de celui-ci:
Nous instancions une classe d'entraîneur de Lightning Pytorch, activons l'arrêt anticipé et la définition de la précision en fonction de la machine sur laquelle nous sommes via precision="bf16-mixed" if torch. cuda.is_available() else "32-true" . Nous configurons mlflow pour tout suivre. Pour tout assembler, nous importons les fonctionnalités des trois autres modules que nous avions décrits plus tôt:
from architectures . fine_tune_clsify_head import TransformerModule
from config import TrainConfig
from data import LexGlueDataModuleLes fonctionnalités de base sont enveloppées dans nos sous-classes de superclasses Pytorch Lightning, et nous les instancons donc:
model = TransformerModule (
pretrained_model = config . pretrained_model , num_classes = config . num_classes ,
lr = config . lr , )
datamodule = LexGlueDataModule (
pretrained_model = config . pretrained_model , max_length = config . max_length ,
batch_size = config . batch_size , num_workers = config . num_workers ,
debug_mode_sample = config . debug_mode_sample , ) Et enfin, nous invoquons les méthodes fit et test de l'objet trainer :
trainer . fit ( model = model , datamodule = datamodule )
best_model_path = checkpoint_callback . best_model_path
# Evaluate the last and the best models on the test sample.
trainer . test ( model = model , datamodule = datamodule )
trainer . test (
model = model , datamodule = datamodule , ckpt_path = best_model_path , ) Nous avions appelé test deux fois pour que nous testons à la fois le dernier modèle au temps d'arrêt et le meilleur modèle selon notre métrique. Ceci est un exemple parfait de la riche fonctionnalité qui vient avec Pytorch Lightning.
Et cela enroule la procédure pas à pas de code. Plongeons maintenant dans les résultats du modèle.
Voici un tableau de bord qui met tout cela ensemble:
En profitant de l'intégration serrée entre Azure et MLFlow, les métriques de notre course d'entraînement ont été mises à disposition pour une consommation et une visualisation faciles dans la console Web Azure ML. MLFlow a capturé bien plus que les métriques, mais aller plus loin dans sa pleine fonctionnalité et les cas d'utilisation plus larges de MLOPS est pour un autre jour. Pour l'instant, concentrons-nous sur l'histoire que notre visualisation des données nous révèle.
Une chose qui sort du tableau supérieur gauche est que la formation a sans aucun doute amélioré la perte sur l'échantillon de validation. De plus, entre 125 et 190 étapes, la courbe commence à s'aplatir, une indication potentielle selon laquelle une formation supplémentaire pourrait être inutile. Mais étant monotone, nous voyons que le sur-ajustement n'a pas encore eu lieu, et de plus, la courbe s'est de nouveau ralentie de 190 à 210, alors nous aurions peut-être dû le laisser courir pour 5 autres époques.
L'intrigue supérieure droite, intéressant, raconte une histoire similaire. La caractéristique la plus notable est de la hauteur des valeurs de précision et de l'unité ici est en pourcentage. Le fait que les lignes atteignent 90% dès le départ est logique car l'ensemble de données est déséquilibré à un rapport 1: 9 entre l'injuste et le juste. C'est exactement pourquoi la métrique préférée est F1, ce que nous affichons dans le graphique inférieur gauche.
Les courbes F1 présentent ici le modèle classique où le modèle s'améliore rapidement au début mais atteint finalement le point de rendement décroissant. Une question intéressante provoquée par les courbes F1 est de savoir comment réconcilier entre la perte et la F1, lorsque l'un suggère de se rétrécir tandis que l'autre laisse de la place pour aller plus loin? Le morceau de théorie suivant cassera cette énigme.
Nous observons le monde sous forme d'événements discrets, mais les constructions continues (par exemple la probabilité) sont souvent plus utiles en mathématiques. Les résultats dans l'ensemble de données TOS injustes sont codés "équitables" ou "injustes", et il existe une distribution de probabilité latente qui a généré ces résultats. Cette distribution de probabilité latente est ce à quoi la fonction de perte entre l'entropie croisée peut fonctionner. Ainsi, dans l'apprentissage en profondeur, nous optimisons pour la métrique continue, mais nous jugeons à quel point notre modèle est bon par la métrique discrète, car continu (latent) et discret (observé) sont des approximations les uns des autres. C'est l'idée.
Ainsi, lorsque le F1 fait allusion à une histoire différente de la perte, il n'y a que deux explications possibles: le hasard et le déséquilibre des classes. En effet, c'est pourquoi la précision et la F1 sont tellement plus volatiles que la perte.
L'aléatoire semble également être l'explication probable de l'écart entre le meilleur score F1 de validation de 70% et le score de test F1 de 78% (à partir du tableau inférieur droit).
Cette analyse de jeu par jeu des mesures d'évaluation du modèle met fin à cette section. Ensuite, soyez pratique avec la création de ce projet. Le guide d'installation ci-dessous vous aidera à commencer à bricoler avec le code.
Étape 1. Clone ce référentiel dans un répertoire de travail local:
$ git clone https://github.com/zjohn77/lightning-mlflow-hf.git
$ cd lightning-mlflow-hf Étape 2. Toutes les dépendances du projet sont dans environment.yml , et vous allez créer un environnement virtuel pour cela. Les instructions ci-dessous sont pour conda , mais rien dans ces dépendances empêche venv ou poetry .
$ conda env create -n lightning-mlflow-hf -f environment.yml Étape 3. À la fin de l'exécution de la formation, la fonction copy_dir_to_abs copiera les sorties dans Azure Blob Storage. Si Azure est également ce que vous utilisez, transmettez simplement les informations d'identification à cette fonction et que vous êtes réglé. Sinon, remplacez par votre propre flux de travail.
Étape 4. Dans votre environnement virtuel, vous changerez ou indiquerez votre IDE vers Where train.py est et exécutez:
$ python train.pyS'il n'y a pas de bogues, il imprimera pour consoler un message de visage étreint et beaucoup de messages Pytorch Lightning. Après quelques minutes, il devrait commencer à imprimer une barre de progression. Asseyez-vous serré et laissez-le faire son truc. Lorsque la course se termine enfin, une table ASCII résumant les mesures d'évaluation du modèle final sera imprimée à la console. C'est tout ce qu'il y a!
Toute contribution est la bienvenue. Nous utilisons les demandes GitHub Pull pour l'examen du code, et nous utilisons le formateur noir pour assurer la cohérence du style de code.
Les tests unitaires et les tests DOC sont également très bienvenus.
Une feuille de route est un travail en cours. Revenez bientôt.
Ilias Chalkidis, Abhik Jana, Dirk Hartung, Michael Bommarito, Ion Androutsopoulos, Daniel Martin Katz, Nikolaos Aletras. (2021). Lexglue: un ensemble de données de référence pour la compréhension juridique de la langue en anglais . Extrait d'Arxiv: https://arxiv.org/abs/2110.00976