Hébergez des modèles ML sur Amazon SageMaker en utilisant Triton Modèles ONNX
Host ML models on Amazon SageMaker using Triton ONNX models.
ONNX (Open Neural Network Exchange) est une norme open-source permettant de représenter des modèles d’apprentissage en profondeur largement pris en charge par de nombreux fournisseurs. ONNX fournit des outils pour optimiser et quantifier les modèles afin de réduire la mémoire et le calcul nécessaires à l’exécution des modèles d’apprentissage automatique (ML). L’un des plus grands avantages d’ONNX est qu’il fournit un format standardisé pour représenter et échanger des modèles de ML entre différents frameworks et outils. Cela permet aux développeurs de former leurs modèles dans un framework et de les déployer dans un autre sans avoir besoin d’une conversion de modèle ou d’une nouvelle formation approfondie. Pour ces raisons, ONNX a acquis une importance significative dans la communauté ML.
Dans cet article, nous montrons comment déployer des modèles basés sur ONNX pour des points de terminaison multi-modèles (MME) qui utilisent des GPU. Il s’agit d’une continuation de l’article “Exécutez plusieurs modèles d’apprentissage en profondeur sur GPU avec les points de terminaison multi-modèles Amazon SageMaker”, où nous avons montré comment déployer des modèles PyTorch et TensorRT de la version ResNet50 sur le serveur d’inférence Triton de Nvidia. Dans cet article, nous utilisons le même modèle ResNet50 au format ONNX ainsi qu’un exemple de modèle de traitement du langage naturel (NLP) supplémentaire au format ONNX pour montrer comment il peut être déployé sur Triton. De plus, nous avons évalué les performances du modèle ResNet50 et avons constaté les avantages de performance qu’ONNX offre par rapport aux versions PyTorch et TensorRT du même modèle, en utilisant la même entrée.
ONNX Runtime
ONNX Runtime est un moteur d’exécution pour l’inférence ML conçu pour optimiser les performances des modèles sur plusieurs plates-formes matérielles, y compris les CPU et les GPU. Il permet l’utilisation de frameworks de ML tels que PyTorch et TensorFlow. Il facilite le réglage des performances pour exécuter les modèles de manière rentable sur le matériel cible et prend en charge des fonctionnalités telles que la quantification et l’accélération matérielle, ce qui en fait l’un des choix idéaux pour le déploiement d’applications ML efficaces et à haute performance. Pour des exemples de la manière dont les modèles ONNX peuvent être optimisés pour les GPU Nvidia avec TensorRT, voir Optimisation TensorRT (ORT-TRT) et ONNX Runtime avec optimisation TensorRT.
- Affinez GPT-J en utilisant un estimateur Amazon SageMaker Hugging Face et la bibliothèque de parallélisation de modèle.
- Déployez Falcon-40B avec des DLC d’inférence de modèles volumineux sur Amazon SageMaker.
- Comment BrainPad favorise le partage interne de connaissances avec Amazon Kendra
Le flux de conteneur Amazon SageMaker Triton est représenté dans le diagramme suivant.
Les utilisateurs peuvent envoyer une demande HTTPS avec la charge utile d’entrée pour l’inférence en temps réel derrière un point de terminaison SageMaker. L’utilisateur peut spécifier un en-tête TargetModel
qui contient le nom du modèle vers lequel la demande en question doit être destinée à invoquer. En interne, le conteneur SageMaker Triton implémente un serveur HTTP avec les mêmes contrats que ceux mentionnés dans la façon dont les conteneurs servent les demandes. Il prend en charge le regroupement dynamique et prend en charge tous les backends fournis par Triton. En fonction de la configuration, l’exécution ONNX est invoquée et la demande est traitée sur CPU ou GPU tel que prédéfini dans la configuration du modèle fournie par l’utilisateur.
Vue d’ensemble de la solution
Pour utiliser le backend ONNX, suivez les étapes suivantes :
- Compiler le modèle au format ONNX.
- Configurer le modèle.
- Créer le point de terminaison SageMaker.
Prérequis
Assurez-vous que vous avez accès à un compte AWS avec les autorisations IAM AWS Identity and Access Management suffisantes pour créer un notebook, accéder à un seau Amazon Simple Storage Service (Amazon S3) et déployer des modèles sur des points de terminaison SageMaker. Voir Créer un rôle d’exécution pour plus d’informations.
Compiler le modèle au format ONNX
La bibliothèque transformers offre une méthode pratique pour compiler le modèle PyTorch au format ONNX. Le code suivant permet d’effectuer les transformations pour le modèle NLP :
onnx_inputs, onnx_outputs = transformers.onnx.export(
preprocessor=tokenizer,
model=model,
config=onnx_config,
opset=12,
output=save_path
)
L’exportation de modèles (PyTorch ou TensorFlow) est facilement réalisée grâce à l’outil de conversion fourni dans le référentiel Hugging Face transformers. Le processus suivant se déroule sous le capot :
- Allouer le modèle à partir des transformers (PyTorch ou TensorFlow).
- Transmettre des entrées fictives à travers le modèle. De cette façon, ONNX peut enregistrer l’ensemble des opérations exécutées.
- Les transformers s’occupent intrinsèquement des axes dynamiques lors de l’exportation du modèle.
- Enregistrer le graphe ainsi que les paramètres du réseau.
Un mécanisme similaire est suivi pour le cas d’utilisation de vision par ordinateur à partir du zoo de modèles torchvision :
torch.onnx.export(
resnet50,
dummy_input,
args.save,
export_params=True,
opset_version=11,
do_constant_folding=True,
input_names=["input"],
output_names=["output"],
dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}},
)
Configurer le modèle
Dans cette section, nous configurons le modèle de vision par ordinateur et de NLP. Nous montrons comment créer un modèle ResNet50 et RoBERTA large pré-entraîné pour le déploiement sur un SageMaker MME en utilisant les configurations de modèle du serveur d’inférence Triton. Le notebook ResNet50 est disponible sur GitHub . Le notebook RoBERTA est également disponible sur GitHub . Pour ResNet50, nous utilisons l’approche Docker pour créer un environnement qui possède déjà toutes les dépendances requises pour construire notre modèle ONNX et générer les artefacts de modèle nécessaires pour cet exercice. Cette approche rend beaucoup plus facile le partage de dépendances et la création de l’environnement exact nécessaire pour accomplir cette tâche.
La première étape consiste à créer le package de modèle ONNX selon la structure de répertoire spécifiée dans les modèles ONNX . Notre objectif est d’utiliser le dépôt de modèle minimal pour un modèle ONNX contenu dans un seul fichier comme suit :
<model-repository-path> /
Model_name
├── 1
│ └── model.onnx
└── config.pbtxt
Ensuite, nous créons le fichier de configuration du modèle qui décrit les entrées, les sorties et les configurations de backend pour que Triton Server puisse récupérer et invoquer les noyaux appropriés pour ONNX. Ce fichier est connu sous le nom de config.pbtxt
et est présenté dans le code suivant pour le cas d’utilisation RoBERTA. Notez que la dimension BATCH
est omise dans le fichier config.pbtxt
. Cependant, lors de l’envoi des données au modèle, nous incluons la dimension de lot. Le code suivant montre également comment vous pouvez ajouter cette fonctionnalité avec des fichiers de configuration de modèle pour définir un lot dynamique avec une taille de lot préférée de 5 pour l’inférence réelle. Avec les paramètres actuels, l’instance de modèle est invoquée instantanément lorsque la taille de lot préférée de 5 est atteinte ou que le délai de 100 microsecondes s’est écoulé depuis que la première demande a atteint le lot dynamique.
name: "nlp-onnx"
platform: "onnxruntime_onnx"
backend: "onnxruntime"
max_batch_size: 32
input {
name: "input_ids"
data_type: TYPE_INT64
dims: [512]
}
input {
name: "attention_mask"
data_type: TYPE_INT64
dims: [512]
}
output {
name: "last_hidden_state"
data_type: TYPE_FP32
dims: [-1, 768]
}
output {
name: "1550"
data_type: TYPE_FP32
dims: [768]
}
instance_group {
count: 1
kind: KIND_GPU
}
dynamic_batching {
max_queue_delay_microseconds: 100
preferred_batch_size:5
}
Le fichier de configuration similaire pour le cas d’utilisation de la vision par ordinateur est le suivant :
name: "resenet_onnx"
platform: "onnxruntime_onnx"
max_batch_size : 128
input [
{
name: "input"
data_type: TYPE_FP32
format: FORMAT_NCHW
dims: [ 3, 224, 224 ]
}
]
output [
{
name: "output"
data_type: TYPE_FP32
dims: [ 1000 ]
}
]
Créer le point de terminaison SageMaker
Nous utilisons les API Boto3 pour créer le point de terminaison SageMaker. Pour cet article, nous montrons les étapes pour le notebook RoBERTA, mais ces étapes sont communes et seront les mêmes pour le modèle ResNet50 également.
Créer un modèle SageMaker
Nous créons maintenant un modèle SageMaker . Nous utilisons l’image Amazon Elastic Container Registry (Amazon ECR) et l’artefact de modèle de l’étape précédente pour créer le modèle SageMaker.
Créer le conteneur
Pour créer le conteneur, nous extrayons l’image appropriée d’Amazon ECR pour Triton Server. SageMaker nous permet de personnaliser et d’injecter diverses variables d’environnement. Certaines des principales fonctionnalités sont la possibilité de définir la BATCH_SIZE
; nous pouvons le définir par modèle dans le fichier config.pbtxt
, ou nous pouvons définir une valeur par défaut ici. Pour les modèles qui peuvent bénéficier d’une taille de mémoire partagée plus grande, nous pouvons définir ces valeurs sous les variables SHM
. Pour activer la journalisation, définissez le niveau de journal verbose
sur true
. Nous utilisons le code suivant pour créer le modèle à utiliser dans notre point de terminaison :
mme_triton_image_uri = (
f"{account_id_map[region]}.dkr.ecr.{region}.{base}" + "/sagemaker-tritonserver:22.12-py3"
)
container = {
"Image": mme_triton_image_uri,
"ModelDataUrl": mme_path,
"Mode": "MultiModel",
"Environment": {
"SAGEMAKER_TRITON_SHM_DEFAULT_BYTE_SIZE": "16777216000", # "16777216", #"16777216000",
"SAGEMAKER_TRITON_SHM_GROWTH_BYTE_SIZE": "10485760",
},
}
from sagemaker.utils import name_from_base
model_name = name_from_base(f"flan-xxl-fastertransformer")
print(model_name)
create_model_response = sm_client.create_model(
ModelName=model_name,
ExecutionRoleArn=role,
PrimaryContainer={
"Image": inference_image_uri,
"ModelDataUrl": s3_code_artifact
},
)
model_arn = create_model_response["ModelArn"]
print(f"Created Model: {model_arn}")
Créer un point de terminaison SageMaker
Vous pouvez utiliser n’importe quelle instance avec plusieurs GPUs pour les tests. Dans cet article, nous utilisons une instance g4dn.4xlarge. Nous ne définissons pas les paramètres VolumeSizeInGB
car cette instance est livrée avec un stockage local de l’instance. Le paramètre VolumeSizeInGB
s’applique aux instances GPU prenant en charge l’attachement de volume Amazon Elastic Block Store (Amazon EBS). Nous pouvons laisser le délai de téléchargement du modèle et la vérification de santé de démarrage du conteneur aux valeurs par défaut. Pour plus de détails, consultez CreateEndpointConfig .
endpoint_config_response = sm_client.create_endpoint_config(
EndpointConfigName=endpoint_config_name,
ProductionVariants=[{
"VariantName": "AllTraffic",
"ModelName": model_name,
"InstanceType": "ml.g4dn.4xlarge",
"InitialInstanceCount": 1,
#"VolumeSizeInGB" : 200,
#"ModelDataDownloadTimeoutInSeconds": 600,
#"ContainerStartupHealthCheckTimeoutInSeconds": 600,
},
],)'
Enfin, nous créons un point de terminaison SageMaker:
create_endpoint_response = sm_client.create_endpoint(
EndpointName=f"{endpoint_name}", EndpointConfigName=endpoint_config_name)
Invoquer le point de terminaison du modèle
Il s’agit d’un modèle génératif, nous passons donc les input_ids
et attention_mask
au modèle en tant que partie de la charge utile. Le code suivant montre comment créer les tenseurs:
tokenizer("This is a sample", padding="max_length", max_length=max_seq_len)
Nous créons maintenant la charge utile appropriée en veillant à ce que le type de données corresponde à ce que nous avons configuré dans config.pbtxt
. Cela nous donne également les tenseurs avec la dimension de lot incluse, ce que Triton attend. Nous utilisons le format JSON pour invoquer le modèle. Triton fournit également une méthode d’invocation binaire native pour le modèle.
response = runtime_sm_client.invoke_endpoint(
EndpointName=endpoint_name,
ContentType="application/octet-stream",
Body=json.dumps(payload),
TargetModel=f"{tar_file_name}",
# TargetModel=f"roberta-large-v0.tar.gz",
)
Notez le paramètre TargetModel
dans le code précédent. Nous envoyons le nom du modèle à invoquer en tant qu’en-tête de demande car il s’agit d’un point de terminaison multi-modèle, nous pouvons donc invoquer plusieurs modèles en temps réel sur un point de terminaison d’inférence déjà déployé en modifiant ce paramètre. Cela montre la puissance des points de terminaison multi-modèles!
Pour afficher la réponse, nous pouvons utiliser le code suivant:
import numpy as np
resp_bin = response["Body"].read().decode("utf8")
# -- keys are -- "outputs":[{"name":"1550","datatype":"FP32","shape":[1,768],"data": [0.0013,0,3433...]}]
for data in json.loads(resp_bin)["outputs"]:
shape_1 = list(data["shape"])
dat_1 = np.array(data["data"])
dat_1.resize(shape_1)
print(f"Data Outputs recieved back :Shape:{dat_1.shape}")
ONNX pour l’optimisation des performances
Le backend ONNX utilise l’allocation de mémoire d’arène C++. L’allocation d’arène est une fonctionnalité uniquement en C++ qui vous aide à optimiser votre utilisation de la mémoire et à améliorer les performances. L’allocation et la désallocation de mémoire constituent une fraction importante du temps CPU passé dans le code de protocoles de buffers. Par défaut, la création de nouveaux objets effectue des allocations sur le tas pour chaque objet, chacun de ses sous-objets et plusieurs types de champs, tels que les chaînes. Ces allocations se produisent en masse lors de l’analyse d’un message et lors de la création de nouveaux messages en mémoire, et les libérations associées se produisent lorsque les messages et leurs arbres de sous-objets sont libérés.
L’allocation basée sur l’arène a été conçue pour réduire ce coût de performance. Avec l’allocation d’arène, de nouveaux objets sont alloués à partir d’un large morceau de mémoire pré-allouée appelé l’arène. Les objets peuvent tous être libérés en une seule fois en abandonnant toute l’arène, idéalement sans exécuter les destructeurs de tout objet contenu (bien qu’une arène puisse toujours maintenir une liste de destructeurs si nécessaire). Cela rend l’allocation d’objets plus rapide en la réduisant à une simple incrémentation de pointeur, et rend la désallocation presque gratuite. L’allocation basée sur l’arène offre également une efficacité de cache supérieure : lors de l’analyse des messages, ils sont plus susceptibles d’être alloués dans une mémoire continue, ce qui rend plus probable la frappe des lignes de cache chaudes lors de la traversée des messages. L’inconvénient de l’allocation basée sur l’arène est que la mémoire du tas C++ sera sur-allouée et restera allouée même après que les objets ont été désalloués. Cela peut entraîner une panne de mémoire ou une utilisation élevée de la mémoire CPU. Pour obtenir le meilleur des deux mondes, nous utilisons les configurations suivantes fournies par Triton et ONNX :
-
arena_extend_strategy – Ce paramètre fait référence à la stratégie utilisée pour faire croître l’arène de mémoire en fonction de la taille du modèle. Nous recommandons de définir la valeur sur 1 (=
kSameAsRequested
), qui n’est pas une valeur par défaut. Le raisonnement est le suivant : l’inconvénient de la stratégie d’extension d’arène par défaut (kNextPowerOfTwo
) est qu’elle peut allouer plus de mémoire que nécessaire, ce qui pourrait être un gaspillage. Comme son nom l’indique,kNextPowerOfTwo
(la valeur par défaut) étend l’arène d’une puissance de 2, tandis quekSameAsRequested
l’étend avec une taille identique à la demande d’allocation à chaque fois.kSameAsRequested
convient aux configurations avancées où vous connaissez à l’avance l’utilisation mémoire attendue. Lors de nos tests, parce que nous connaissons la taille des modèles qui est une valeur constante, nous pouvons choisir en toute sécuritékSameAsRequested
. -
gpu_mem_limit – Nous avons défini la valeur sur la limite de mémoire CUDA. Pour utiliser toute la mémoire possible, passez la valeur maximale de
size_t
. La valeur par défaut estSIZE_MAX
si rien n’est spécifié. Nous recommandons de la laisser par défaut. -
enable_cpu_mem_arena – Cela active l’arène de mémoire sur le CPU. L’arène peut pré-allouer de la mémoire pour une utilisation future. Définissez cette option sur
false
si vous ne le souhaitez pas. La valeur par défaut estTrue
. Si vous désactivez l’arène, l’allocation de mémoire de tas prendra du temps, de sorte que la latence d’inférence augmentera. Lors de nos tests, nous l’avons laissé par défaut. -
enable_mem_pattern – Ce paramètre fait référence à la stratégie interne d’allocation de mémoire basée sur les formes d’entrée. Si les formes sont constantes, nous pouvons activer ce paramètre pour générer un modèle de mémoire pour l’avenir et économiser du temps d’allocation, ce qui le rend plus rapide. Utilisez 1 pour activer le modèle de mémoire et 0 pour le désactiver. Il est recommandé de le définir sur 1 lorsque les caractéristiques d’entrée sont censées être identiques. La valeur par défaut est 1.
-
do_copy_in_default_stream – Dans le contexte du fournisseur d’exécution CUDA dans ONNX, un flux de calcul est une séquence d’opérations CUDA qui sont exécutées de manière asynchrone sur le GPU. ONNX exécute les opérations dans différents flux en fonction de leurs dépendances, ce qui aide à minimiser le temps d’inactivité du GPU et à obtenir de meilleures performances. Nous recommandons d’utiliser le paramètre par défaut de 1 pour utiliser le même flux pour la copie et le calcul ; cependant, vous pouvez utiliser 0 pour utiliser des flux séparés pour la copie et le calcul, ce qui pourrait entraîner la mise en pipeline des deux activités du dispositif. Lors de nos tests du modèle ResNet50, nous avons utilisé 0 et 1 mais n’avons pas trouvé de différence appréciable entre les deux en termes de performances et de consommation de mémoire du dispositif GPU.
-
Optimisation de graphique – Le backend ONNX pour Triton prend en charge plusieurs paramètres qui aident à affiner la taille du modèle ainsi que les performances d’exécution du modèle déployé. Lorsque le modèle est converti en représentation ONNX (le premier cadre dans le diagramme suivant à l’étape IR), ONNX fournit des optimisations de graphique à trois niveaux : optimisations de base, étendues et de disposition. Vous pouvez activer tous les niveaux d’optimisation de graphique en ajoutant les paramètres suivants dans le fichier de configuration de modèle :
optimization { graph : { level : 1 }}
-
cudnn_conv_algo_search – Comme nous utilisons des GPU Nvidia basés sur CUDA dans nos tests, pour notre cas d’utilisation de vision par ordinateur avec le modèle ResNet50, nous pouvons utiliser l’optimisation basée sur le fournisseur d’exécution CUDA à la quatrième couche dans le diagramme suivant avec le paramètre
cudnn_conv_algo_search
. L’option par défaut est exhaustive (0), mais lorsque nous avons modifié cette configuration en1 – HEURISTIC
, nous avons vu la latence du modèle en état stable réduire à 160 millisecondes. La raison en est que ONNX invoque la passe avant cudnnGetConvolutionForwardAlgorithm_v7 plus légère et réduit donc la latence avec des performances adéquates. -
Mode d’exécution – La prochaine étape consiste à sélectionner le mode d’exécution correct à la couche 5 dans le diagramme suivant. Ce paramètre contrôle si vous voulez exécuter les opérateurs de votre graphique séquentiellement ou en parallèle. Habituellement, lorsque le modèle a de nombreuses branches, en définissant cette option sur
ExecutionMode.ORT_PARALLEL
(1), vous obtiendrez de meilleures performances. Dans le scénario où votre modèle a de nombreuses branches dans son graphique, en définissant le mode d’exécution sur parallèle, vous obtiendrez de meilleures performances. Le mode par défaut est séquentiel, vous pouvez donc l’activer pour répondre à vos besoins.parameters { key: "execution_mode" value: { string_value: "1" } }
Pour une compréhension approfondie des opportunités d’optimisation des performances dans ONNX, reportez-vous à la figure suivante.
Source: https://static.linaro.org/connect/san19/presentations/san19-211.pdf
Chiffres de référence et optimisation des performances
En activant les optimisations de graphique, la recherche d’algorithme cudnn_conv_algo_search
et les paramètres de mode de fonctionnement parallèle lors de nos tests du modèle ResNet50, nous avons constaté que le temps de démarrage à froid du graphique du modèle ONNX est passé de 4,4 secondes à 1,61 seconde. Un exemple de fichier de configuration de modèle complet est fourni dans la section de configuration ONNX du notebook suivant.
Les résultats de benchmark de test sont les suivants :
- PyTorch – 176 millisecondes, démarrage à froid 6 secondes
- TensorRT – 174 millisecondes, démarrage à froid 4,5 secondes
- ONNX – 168 millisecondes, démarrage à froid 4,4 secondes
Les graphiques suivants visualisent ces métriques.
De plus, lors de nos tests de cas d’utilisation de vision par ordinateur, envisagez d’envoyer la charge utile de demande au format binaire à l’aide du client HTTP fourni par Triton, car cela améliore considérablement la latence d’invocation de modèle.
Les autres paramètres que SageMaker expose pour ONNX sur Triton sont les suivants :
- Batch dynamique – Le batch dynamique est une fonctionnalité de Triton qui permet de combiner les demandes d’inférence par le serveur, de sorte qu’un batch soit créé dynamiquement. La création d’un batch de demandes entraîne généralement une augmentation du débit. Le batcher dynamique doit être utilisé pour les modèles sans état. Les batches créés de manière dynamique sont distribués à toutes les instances de modèle configurées pour le modèle.
- Taille de batch maximale – La propriété
max_batch_size
indique la taille maximale de batch que le modèle prend en charge pour les types de batch qui peuvent être exploités par Triton. Si la dimension de batch du modèle est la première dimension, et que toutes les entrées et sorties du modèle ont cette dimension de batch, alors Triton peut utiliser son batcher dynamique ou séquence pour utiliser automatiquement le batching avec le modèle. Dans ce cas, la valeur demax_batch_size
doit être définie sur une valeur supérieure ou égale à 1, ce qui indique la taille maximale de batch que Triton doit utiliser avec le modèle. - Taille de batch maximale par défaut – La valeur par défaut de la taille maximale de batch est utilisée pour
max_batch_size
pendant l’autocomplétion lorsqu’aucune autre valeur n’est trouvée. Le backendonnxruntime
définira la taillemax_batch_size
du modèle sur cette valeur par défaut si l’autocomplétion a déterminé que le modèle est capable de grouper les demandes et quemax_batch_size
est 0 dans la configuration du modèle ou quemax_batch_size
est omis de la configuration du modèle. Simax_batch_size
est supérieur à 1 et qu’aucun planificateur n’est fourni, le planificateur de batch dynamique sera utilisé. La taille maximale de batch par défaut est de 4.
Nettoyage
Assurez-vous de supprimer le modèle, la configuration du modèle et le point de terminaison du modèle après avoir exécuté le notebook. Les étapes à suivre sont fournies à la fin du notebook d’exemple dans le référentiel GitHub.
Conclusion
Dans cet article, nous avons plongé en profondeur dans le backend ONNX que Triton Inference Server prend en charge sur SageMaker. Ce backend permet une accélération GPU de vos modèles ONNX. Il y a de nombreuses options à considérer pour obtenir les meilleures performances pour l’inférence, telles que les tailles de batch, les formats d’entrée de données et d’autres facteurs qui peuvent être ajustés pour répondre à vos besoins. SageMaker vous permet d’utiliser cette fonctionnalité en utilisant des points de terminaison de modèle unique et multi-modèle. Les MME permettent un meilleur équilibre entre les performances et les économies de coûts. Pour commencer avec le support MME pour GPU, voir l’hébergement de plusieurs modèles dans un conteneur derrière un point de terminaison.
Nous vous invitons à essayer les conteneurs Triton Inference Server dans SageMaker et à partager vos commentaires et questions dans les commentaires.
We will continue to update IPGirl; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles
- Comment Forethought économise plus de 66% sur les coûts des modèles d’IA générative en utilisant Amazon SageMaker
- AI Time Journal présente un eBook révolutionnaire sur les tendances de l’IA en 2023.
- Règles de base pour l’ère de la guerre de l’IA
- Robots de livraison de nourriture Uber Eats prêts à être utilisés dans plusieurs villes américaines.
- Les humains aident les ordinateurs à repérer les rafales de l’espace
- Vous pouvez fabriquer des filtres à dioxyde de carbone avec une imprimante 3D.
- Après une année difficile, Zuckerberg expose la feuille de route de Meta à ses employés.