Pratique L’un des plus gros casse-têtes associés aux charges de travail de l’IA est de gérer tous les pilotes, environnements d’exécution, bibliothèques et autres dépendances dont elles ont besoin pour s’exécuter.
Cela est particulièrement vrai pour les tâches accélérées par le matériel où, si vous avez la mauvaise version de CUDA, ROCm ou PyTorch, il y a de fortes chances que vous vous grattiez la tête en regardant une erreur.
Comme si cela ne suffisait pas, certains projets et applications d’IA peuvent avoir des dépendances conflictuelles, tandis que différents systèmes d’exploitation peuvent ne pas prendre en charge les packages dont vous avez besoin. Cependant, en conteneurisant ces environnements, nous pouvons éviter une grande partie de ce désordre en créant des images qui ont été configurées spécifiquement pour une tâche et – peut-être plus important encore – qui peuvent être déployées de manière cohérente et reproductible à chaque fois.
Et comme les conteneurs sont largement isolés les uns des autres, vous pouvez généralement avoir des applications exécutées avec des piles logicielles conflictuelles. Par exemple, vous pouvez avoir deux conteneurs, l’un avec CUDA 11 et l’autre avec 12, exécutés en même temps.
C’est l’une des raisons pour lesquelles les fabricants de puces mettent souvent à la disposition des utilisateurs des versions conteneurisées de leurs bibliothèques de logiciels de calcul accéléré, car cela offre un point de départ cohérent pour le développement.
Conditions préalables
Dans ce didacticiel, nous examinerons différentes manières dont la conteneurisation peut être utilisée pour aider au développement et/ou au déploiement de charges de travail d’IA, qu’elles soient accélérées par le processeur ou le processeur graphique.
Ce guide suppose que vous :
- Vous utilisez Ubuntu 24.04 LTS (d’autres distributions devraient fonctionner, mais votre expérience peut varier).
- Avoir la dernière version de Docker Engine installée et une compréhension de base de l’exécution du conteneur.
- Exécutez les pilotes propriétaires de Nvidia, le cas échéant.
Bien qu’il existe un certain nombre d’environnements et d’environnements d’exécution de conteneurs, nous nous intéresserons plus particulièrement à Docker pour sa simplicité et sa large compatibilité. Cela dit, de nombreux concepts présentés ici s’appliqueront à d’autres environnements d’exécution de conteneurisation tels que Podman, même si l’exécution peut être un peu différente.
Exposer les GPU Intel et AMD à Docker
Contrairement aux machines virtuelles, vous pouvez transmettre votre GPU à autant de conteneurs que vous le souhaitez, et tant que vous ne dépassez pas la vRAM disponible, vous ne devriez pas avoir de problème.
Pour ceux qui possèdent des GPU Intel ou AMD, le processus ne pourrait pas être plus simple et consiste simplement à transmettre les bons indicateurs lors du démarrage de notre conteneur.
Par exemple, disons que nous voulons rendre votre GPU Intel disponible pour un conteneur Ubuntu 22.04. Vous ajouteriez --device /dev/dri
au docker run
commande. En supposant que vous utilisez un système bare metal avec un GPU Intel, vous exécuteriez quelque chose comme :
docker run -it --rm --device /dev/dri ubuntu:22.04
Pendant ce temps, pour les GPU AMD, vous ajouteriez --device /dev/kfd
docker run -it --rm --device /dev/kfd --device /device/dri ubuntu:22.04
Remarque : en fonction de votre système, vous devrez probablement exécuter cette commande avec des privilèges élevés en utilisant sudo docker run
ou dans certains cas doas docker run
.
Exposer les GPU Nvidia à Docker
Si vous utilisez l’une des cartes de Team Green, vous devrez installer Nvidia Container Toolkit avant de pouvoir l’exposer à vos conteneurs Docker.
Pour commencer, nous allons ajouter le référentiel logiciel de la boîte à outils à notre liste de sources et actualiser Apt. (Vous pouvez consulter la documentation de Nvidia pour obtenir des instructions sur l’installation sur les distributions RHEL et SUSE ici.)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \ && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
Nous pouvons maintenant installer le runtime du conteneur et configurer Docker pour l’utiliser.
sudo apt install -y nvidia-container-toolkit
Avec la boîte à outils du conteneur installée, nous devons simplement dire à Docker d’utiliser le runtime Nvidia en modifiant le /etc/docker/daemon.json
fichier. Pour ce faire, nous pouvons simplement exécuter ce qui suit :
sudo nvidia-ctk runtime configure --runtime=docker
La dernière étape consiste à redémarrer le démon Docker et à tester que tout fonctionne en lançant un conteneur avec le --gpus=all
drapeau.
sudo systemctl restart docker
docker run -it --rm --gpus=all ubuntu:22.04
Remarque : si vous disposez de plusieurs GPU, vous pouvez spécifier ceux à exposer à l’aide de l’ gpus=1
ou gpus '"device=1,3,4"'
drapeaux.
À l’intérieur du conteneur, vous pouvez ensuite exécuter nvidia-smi
et vous devriez voir quelque chose de similaire apparaître sur votre écran.
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.90.07 Driver Version: 550.90.07 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA RTX 6000 Ada Gene... Off | 00000000:06:10.0 Off | Off |
| 30% 29C P8 9W / 300W | 8045MiB / 49140MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| 0 N/A N/A 941 C python3 7506MiB |
| 0 N/A N/A 40598 C /usr/local/bin/python3 528MiB |
+-----------------------------------------------------------------------------------------+
Utilisation des conteneurs Docker comme environnements de développement
L’une des applications les plus utiles des conteneurs Docker lorsque vous travaillez avec des bibliothèques et des modèles de logiciels d’IA est en tant qu’environnement de développement. En effet, vous pouvez créer autant de conteneurs que vous le souhaitez et les supprimer lorsque vous avez terminé sans vous soucier de la panne de votre système.
Maintenant, vous pouvez simplement lancer une image de base de la distribution de votre choix, y exposer notre GPU et commencer à installer CUDA, ROCm, PyTorch ou Tensorflow. Par exemple, pour créer un conteneur Ubuntu de base accéléré par GPU, vous exécuteriez ce qui suit (n’oubliez pas de modifier le --gpus
ou --device
(déclarez-le de manière appropriée) pour créer puis accéder au conteneur.
docker run -itd --gpus=all -p 8081:80 -v ~/denv:/home/denv --name GPUtainer ubuntu:22.04
docker exec -it GPUtainer /bin/bash
Cela créera un nouveau conteneur Ubuntu 22.04 nommé GPUtainer qui :
- A accès à votre GPU Nvidia
- Expose le port 80 sur le conteneur comme port 8081 sur votre hôte
- Montures
/home/denv
dans un conteneur comme undenv
dossier dans le répertoire personnel de votre hôte pour un transfert de fichiers facile - Continue de fonctionner après votre sortie
Utilisation d’images prédéfinies
Bien que la création d’un conteneur à partir de zéro avec CUDA, ROCm ou OpenVINO puisse parfois être utile, elle est également plutôt fastidieuse et prend du temps, surtout lorsqu’il existe des images prédéfinies qui feront la majeure partie du travail à votre place.
Par exemple, si nous voulons mettre en place et exécuter un environnement CUDA 12.5 de base, nous pouvons utiliser un nvidia/cuda
image comme point de départ. Pour le tester, exécutez :
docker run -it --gpus=all -p 8081:80 -v ~/denv:/home/denv --name CUDAtainer nvidia/cuda:12.5.0-devel-ubuntu22.04
Ou, si vous avez une carte AMD, nous pouvons utiliser l’une des images ROCm comme celle-ci ROCm/dev-ubuntu-22.04
un.
docker run -it --device /dev/kfd --device /device/dri -p 8081 -v ~/denv:/home/denv —-name ROCmtainer ROCm/dev-ubuntu-22.04
En attendant, les propriétaires de GPU Intel devraient pouvoir créer un environnement similaire en utilisant cette image OpenVINO.
docker run -it --device /dev/dri:/dev/dri -p 8081 -v ~/denv:/home/denv —-name Vinotainer openvino/ubuntu22_runtime:latest
Convertir vos conteneurs en images
De par leur conception, les conteneurs Docker sont en grande partie éphémères par nature, ce qui signifie que les modifications qui y sont apportées ne seront pas conservées si, par exemple, vous supprimez le conteneur ou mettez à jour l’image. Cependant, nous pouvons enregistrer toutes les modifications en les validant dans une nouvelle image.
Pour valider les modifications apportées à l’environnement de développement CUDA que nous avons créé à l’étape précédente, nous exécuterons la procédure suivante pour créer une nouvelle image appelée « cudaimage ».
docker commit CUDAtainer cudaimage
Nous pourrions alors créer un nouveau conteneur basé sur celui-ci en exécutant :
docker run -itd --gpus=all -p 8082:80 -v ~/denv:/home/denv --name CUDAtainer2 cudaimage
Créer des images personnalisées
La conversion de conteneurs existants en images reproductibles peut être utile pour créer des points de contrôle et tester les modifications. Mais si vous prévoyez de partager vos images, il est généralement préférable de montrer votre travail sous la forme d’un dockerfile
.
Ce fichier est essentiellement une simple liste d’instructions qui indique généralement à Docker comment transformer une image existante en une image personnalisée. Comme pour une grande partie de ce didacticiel, si vous êtes familier avec Docker ou le docker build
commander la plupart de cela devrait être explicite.
Pour ceux qui ne connaissent pas la génération d’images Docker, nous allons passer en revue un exemple simple utilisant cette application météo IA que nous avons bricolée en Python. Elle utilise le LLM Phi3-instruct de Microsoft pour générer un rapport lisible par l’homme à partir des statistiques recueillies à partir d’Open Weather Map toutes les 15 minutes, sur le ton d’une personnalité météo de la télévision.
importer des demandes importer une torche à partir de transformateurs importer un pipeline, BitsAndBytesConfig
# Constantes ZIP_CODE = VOTRE_CODE_POSTAL API_KEY = “YOUR_OPEN_WEATHER_MAP_API_KEY” # Remplacez par votre clé API OpenWeatherMap WEATHER_URL = f”http://api.openweathermap.org/data/2.5/weather?zip={ZIP_CODE}&appid={API_KEY}” UPDATE_INTERVAL = 900 # secondes
# Initialiser le pipeline de génération de texte quantization_config = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16) pipe = pipeline(“text-generation”, “microsoft/Phi-3-mini-4k-instruct”, device_map=”auto”, model_kwargs={“quantization_config”: quantization_config})
def kelvin_to_fahrenheit(kelvin: float) -> float: “””Convertir Kelvin en Fahrenheit.””” return (kelvin – 273.15) * 9/5 + 32
def get_weather_data() -> Dict[str, Any]: “””Récupérer les données météorologiques à partir de l’API OpenWeatherMap.””” response = requests.get(WEATHER_URL) response.raise_for_status() return response.json()
def format_weather_report(données_météo : Dict[str, Any]) -> str: “””Formater les données météorologiques dans une chaîne de rapport.””” main_weather = weather_data[‘main’] emplacement = données_météo[‘name’] conditions = données_météo[‘weather’][0][‘description’] température = kelvin_à_fahrenheit(météo_principale[‘temp’]) humidité = main_weather[‘humidity’] vitesse_du_vent = données_météo[‘wind’][‘speed’]
return (f”L’heure est : {time.strftime(‘%H:%M’)}, ” f”emplacement : {location}, ” f”Conditions : {conditions}, ” f”Température : {temperature:.2f}°F, ” f”Humidité : {humidity}%, ” f”Vitesse du vent : {wind_speed} m/s”)
def generate_weather_report(weather_report: str) -> str: “””Générer un rapport météo à l’aide du pipeline de génération de texte.””” chat = [ {“role”: “assistant”, “content”: “You are a friendly weather reporter that takes weather data and turns it into short reports. Keep these short, to the point, and in the tone of a TV weather man or woman. Be sure to inject some humor into each report too. Only use units that are standard in the United States. Always begin every report with ‘in (location) the time is'”}, {“role”: “user”, “content”: f”Today’s weather data is {weather_report}”} ] réponse = pipe(chat, max_new_tokens=512) renvoie une réponse[0][‘generated_text’][-1][‘content’]
def main(): “””Fonction principale pour exécuter la boucle de rapport météo.””” try: while True: try: weather_data = get_weather_data() weather_report = format_weather_report(weather_data) generated_report = generate_weather_report(weather_report) print(generated_report) except requests.RequestException as e: print(f”Erreur lors de la récupération des données météo : {e}”) except Exception as e: print(f”Une erreur inattendue s’est produite : {e}”)
time.sleep(UPDATE_INTERVAL) except KeyboardInterrupt: print(“\nLes bulletins météo ont été arrêtés.”)
si __name__ == “__main__”: main()
Remarque : si vous suivez ces instructions, assurez-vous de définir votre code postal et votre clé API Open Weather Map de manière appropriée.
Si vous êtes curieux, l’application fonctionne en transmettant les données météorologiques et les instructions au LLM via le module de pipeline Transformers, sur lequel vous pouvez en savoir plus ici.
En elle-même, l’application est déjà assez portable avec des dépendances minimales. Cependant, elle dépend toujours de l’installation correcte du runtime CUDA, ce que nous pouvons faciliter à gérer en conteneurisant l’application.
Pour commencer, dans un nouveau répertoire, créez un fichier vide dockerfile
à côté de la weather_app.py
Script Python ci-dessus. À l’intérieur du dockerfile
nous définirons avec quelle image de base nous voulons commencer, ainsi que le répertoire de travail que nous souhaitons utiliser.
FROM nvidia/cuda:12.5.0-devel-ubuntu22.04 WORKDIR /ai_weather
En dessous, nous dirons au Dockerfile de copier le weather_app.py
script dans le répertoire de travail.
ADD weather_app.py /ai_weather/
À partir de là, nous devons simplement lui dire quelles commandes il doit exécuter. RUN
pour configurer le conteneur et installer les éventuelles dépendances. Dans ce cas, nous avons juste besoin de quelques modules Python, ainsi que de la dernière version de PyTorch pour notre GPU.
RUN apt update RUN apt upgrade -y RUN apt install python3 python3-pip -y RUN pip3 install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu124 RUN pip3 install requests accelerate transformers RUN pip3 install bitsandbytes>=0.39.0 -q
Enfin, nous allons définir le CMD
à la commande ou à l’exécutable, nous voulons que le conteneur s’exécute lors de son premier démarrage. Avec cela, notre dockerfile
est complet et devrait ressembler à ceci :
FROM nvidia/cuda:12.5.0-devel-ubuntu22.04 WORKDIR /ai_weather ADD weather_app.py /ai_weather/ RUN apt update RUN apt upgrade -y RUN apt install python3 python3-pip -y RUN pip3 install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu124 RUN pip3 install requests accelerate transformers RUN pip3 install bitsandbytes>=0.39.0 -q CMD ["/bin/bash", "-c", "python3 weather_app.py"]
Il ne nous reste plus qu’à convertir le dockerfile
dans une nouvelle image en exécutant la commande suivante, puis asseyez-vous et attendez.
docker build -t aiweather .
Après quelques minutes, l’image devrait être complète et nous pouvons l’utiliser pour faire tourner notre conteneur en mode interactif. Remarque : supprimez le --rm
un peu si vous ne voulez pas que le conteneur se détruise lorsqu’il est arrêté.
docker run -it --rm --gpus=all aiweather
Après quelques secondes, le conteneur sera lancé, téléchargera Phi3 depuis Hugging Face, le quantifiera avec une précision de 4 bits et présentera notre premier rapport météo.
"In Aurora, the time is 2:28 PM, and it's a hot one! We've got scattered clouds playing hide and seek, but don't let that fool you. It's a scorcher at 91.69°F, and the air's as dry as a bone with just 20% humidity. The wind's blowing at a brisk 6.26 m/s, so you might want to hold onto your hats! Stay cool, Aurora!"
Bien entendu, il s’agit d’un exemple volontairement simple, mais il illustre bien comment la conteneurisation peut être utilisée pour faciliter la création et le déploiement d’applications d’IA en cours d’exécution. Nous vous recommandons de consulter la documentation de Docker ici, si vous avez besoin de quelque chose de plus complexe.
Qu’en est-il des NIM ?
Comme toute autre application, la conteneurisation de vos projets d’IA présente un certain nombre d’avantages, au-delà de les rendre plus reproductibles et plus faciles à déployer à grande échelle. Elle permet également d’expédier des modèles avec des configurations optimisées pour des cas d’utilisation ou des configurations matérielles spécifiques.
C’est l’idée qui se cache derrière les microservices d’inférence Nvidia (NIM en abrégé), que nous avons examinés lors de la GTC ce printemps. Ces NIM ne sont en réalité que des conteneurs créés par Nvidia avec des versions spécifiques de logiciels tels que CUDA, Triton Inference Server ou TensorRT LLM qui ont été optimisés pour obtenir les meilleures performances possibles sur leur matériel.
Et comme ils sont conçus par Nvidia, chaque fois que le géant des GPU publie une mise à jour de l’un de ses services qui débloque de nouvelles fonctionnalités ou des performances supérieures sur du matériel nouveau ou existant, les utilisateurs pourront profiter de ces améliorations simplement en téléchargeant une nouvelle image NIM. C’est du moins l’idée.
Au cours des prochaines semaines, Nvidia devrait mettre ses NIM à disposition gratuitement via son programme de développement à des fins de recherche et de test. Mais avant de vous enthousiasmer, si vous souhaitez les déployer en production, vous aurez toujours besoin d’une licence AI Enterprise qui vous coûtera 4 500 $/an par GPU ou 1 $/heure par GPU dans le cloud.
Nous prévoyons d’examiner de plus près les NIM de Nvidia dans un avenir proche. Mais si une licence d’entreprise AI n’est pas dans votre budget, rien ne vous empêche de créer vos propres images optimisées, comme nous l’avons montré dans ce tutoriel. ®
Note de l’éditeur: Nvidia a fourni à The Register une carte graphique RTX 6000 Ada Generation pour soutenir cet article et d’autres du même genre. Nvidia n’a eu aucune influence sur le contenu de cet article.