Mettre à l’échelle pour gérer davantage d’utilisateurs inscrits
Attention
L’accès au service visage est limité en fonction des critères d’éligibilité et d’utilisation afin de prendre en charge nos principes d’IA responsable. Le service visage est disponible uniquement pour les clients et partenaires gérés par Microsoft. Utilisez le Formulaire d’admission de la reconnaissance faciale pour demander l’accès. Pour plus d’informations, consultez la page Accès limité visage.
Ce guide montre comment effectuer un scale-up pour passer des objets PersonGroup et FaceList à des objets LargePersonGroup et LargeFaceList, respectivement. PersonGroups peut contenir jusqu’à 1000 personnes dans le niveau gratuit et 10 000 dans le niveau payant, tandis que LargePersonGroups peut contenir jusqu’à un million de personnes dans le niveau payant.
Important
La structure de données PersonDirectory plus récente est recommandée pour les nouveaux développements. Elle peut conserver jusqu’à 75 millions d’identités et ne nécessite aucune formation manuelle. Pour obtenir plus d’informations, consultez le Guide sur PersonDirectory.
Ce guide décrit le processus de migration. Il requiert des connaissances de base sur les objets PersonGroup et FaceList, l’opération Entraîner et les fonctions de reconnaissance faciale. Pour en savoir plus sur ces sujets, consultez le guide conceptuel Reconnaissance faciale.
Les objets LargePersonGroup et LargeFaceList sont collectivement désignés sous le nom d’« opérations à grande échelle ». L’objet LargePersonGroup peut contenir 1 million de personnes, avec un maximum de 248 visages par personne. L’objet LargeFaceList peut contenir 1 million de visages. Les opérations à grande échelle sont semblables aux objets PersonGroup et FaceList classiques, mais présentent quelques différences liées à la nouvelle architecture.
Les exemples sont écrits en C#.
Remarque
Pour activer les performances de recherche de visage dans le cadre des opérations Identification et FindSimilar à grande échelle, introduisez une opération Entraîner afin de prétraiter les objets LargeFaceList et LargePersonGroup. La durée de la formation varie de quelques secondes à environ une demi-heure en fonction de la capacité réelle. Pendant l’entraînement, il est possible d’exécuter des opérations Identification et FindSimilar si un entraînement a eu lieu au préalable. L'inconvénient est que les nouvelles personnes et les nouveaux visages ajoutés n'apparaîtront pas dans le résultat tant qu'une nouvelle formation post-migration à grande échelle n'aura pas été effectuée.
Étape 1 : migration du code
Cette section porte sur la migration d’une implémentation PersonGroup ou FaceList vers une implémentation LargePersonGroup ou LargeFaceList. Bien que les objets LargePersonGroup et LargeFaceList diffèrent des objets PersonGroup et FaceList en termes de conception et d’implémentation interne, les interfaces API sont similaires pour faciliter la compatibilité descendante.
La migration des données n'est pas prise en charge. Vous devez recréer l’objet LargePersonGroup ou LargeFaceList.
Migrer un objet PersonGroup vers un objet LargePersonGroup
La migration d’un objet PersonGroup vers un objet LargePersonGroup est simple. Au niveau groupe, ceux-ci partagent exactement les mêmes opérations.
Pour une implémentation liée à PersonGroup ou à Person, seuls les chemins d’API ou la classe/le module du Kit de développement logiciel (SDK) doivent être changés en LargePersonGroup et LargePersonGroup Person.
Ajoutez tous les visages et toutes les personnes de l’objet PersonGroup dans le nouvel objet LargePersonGroup. Pour plus d'informations, consultez Ajouter des visages.
Migrer un objet FaceList vers un objet LargeFaceList
API FaceList | API LargeFaceList |
---|---|
Créer | Créer |
DELETE | DELETE |
Obtenir | Obtenir |
List | List |
Update | Update |
- | Former |
- | Obtenir l’état de l’entraînement |
Le tableau précédent est une comparaison des opérations au niveau de la liste entre FaceList et LargeFaceList. Comme nous pouvons l’observer, LargeFaceList comporte de nouvelles opérations, Entraîner et Obtenir l’état de l’entraînement, comparé à FaceList. L’entraînement de l’objet LargeFaceList est une condition préalable à l’opération FindSimilar. Aucun entraînement n’est requis pour l’objet FaceList. L’extrait de code suivant est une fonction d’assistance pour attendre l’entraînement d’un objet LargeFaceList :
/// <summary>
/// Helper function to train LargeFaceList and wait for finish.
/// </summary>
/// <remarks>
/// The time interval can be adjusted considering the following factors:
/// - The training time which depends on the capacity of the LargeFaceList.
/// - The acceptable latency for getting the training status.
/// - The call frequency and cost.
///
/// Estimated training time for LargeFaceList in different scale:
/// - 1,000 faces cost about 1 to 2 seconds.
/// - 10,000 faces cost about 5 to 10 seconds.
/// - 100,000 faces cost about 1 to 2 minutes.
/// - 1,000,000 faces cost about 10 to 30 minutes.
/// </remarks>
/// <param name="largeFaceListId">The Id of the LargeFaceList for training.</param>
/// <param name="timeIntervalInMilliseconds">The time interval for getting training status in milliseconds.</param>
/// <returns>A task of waiting for LargeFaceList training finish.</returns>
private static async Task TrainLargeFaceList(
string largeFaceListId,
int timeIntervalInMilliseconds = 1000)
{
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", SUBSCRIPTION_KEY);
// Trigger a train call.
await httpClient.PostAsync($"{ENDPOINT}/face/v1.0/largefacelists/{largeFaceListId}/train", null);
// Wait for training finish.
while (true)
{
await Task.Delay(timeIntervalInMilliseconds);
string? trainingStatus = null;
using (var response = await httpClient.GetAsync($"{ENDPOINT}/face/v1.0/largefacelists/{largeFaceListId}/training"))
{
string contentString = await response.Content.ReadAsStringAsync();
trainingStatus = (string?)(JsonConvert.DeserializeObject<Dictionary<string, object>>(contentString)?["status"]);
}
if ("running".Equals(trainingStatus))
{
continue;
}
else if ("succeeded".Equals(trainingStatus))
{
break;
}
else
{
throw new Exception("The train operation is failed!");
}
}
}
Auparavant, une utilisation standard de l’objet FaceList, avec ajout de visages et FindSimilar, se présentait comme suit :
// Create a FaceList.
const string FaceListId = "myfacelistid_001";
const string FaceListName = "MyFaceListDisplayName";
const string ImageDir = @"/path/to/FaceList/images";
using (var content = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new Dictionary<string, object> { ["name"] = FaceListName, ["recognitionModel"] = "recognition_04" }))))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
await httpClient.PutAsync($"{ENDPOINT}/face/v1.0/facelists/{FaceListId}", content);
}
// Add Faces to the FaceList.
Parallel.ForEach(
Directory.GetFiles(ImageDir, "*.jpg"),
async imagePath =>
{
using (Stream stream = File.OpenRead(imagePath))
{
using (var content = new StreamContent(stream))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
await httpClient.PostAsync($"{ENDPOINT}/face/v1.0/facelists/{FaceListId}/persistedfaces?detectionModel=detection_03", content);
}
}
});
// Perform FindSimilar.
const string QueryImagePath = @"/path/to/query/image";
var results = new List<HttpResponseMessage>();
using (Stream stream = File.OpenRead(QueryImagePath))
{
var response = await faceClient.DetectAsync(BinaryData.FromStream(stream), FaceDetectionModel.Detection03, FaceRecognitionModel.Recognition04, returnFaceId: true);
var faces = response.Value;
foreach (var face in faces)
{
using (var content = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new Dictionary<string, object> { ["faceId"] = face.FaceId, ["faceListId"] = FaceListId }))))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
results.Add(await httpClient.PostAsync($"{ENDPOINT}/face/v1.0/findsimilars", content));
}
}
}
Lorsque vous le migrez vers l’objet LargeFaceList, le code devient :
// Create a LargeFaceList.
const string LargeFaceListId = "mylargefacelistid_001";
const string LargeFaceListName = "MyLargeFaceListDisplayName";
const string ImageDir = @"/path/to/FaceList/images";
using (var content = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new Dictionary<string, object> { ["name"] = LargeFaceListName, ["recognitionModel"] = "recognition_04" }))))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
await httpClient.PutAsync($"{ENDPOINT}/face/v1.0/largefacelists/{LargeFaceListId}", content);
}
// Add Faces to the LargeFaceList.
Parallel.ForEach(
Directory.GetFiles(ImageDir, "*.jpg"),
async imagePath =>
{
using (Stream stream = File.OpenRead(imagePath))
{
using (var content = new StreamContent(stream))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
await httpClient.PostAsync($"{ENDPOINT}/face/v1.0/largefacelists/{LargeFaceListId}/persistedfaces?detectionModel=detection_03", content);
}
}
});
// Train() is newly added operation for LargeFaceList.
// Must call it before FindSimilar to ensure the newly added faces searchable.
await TrainLargeFaceList(LargeFaceListId);
// Perform FindSimilar.
const string QueryImagePath = @"/path/to/query/image";
var results = new List<HttpResponseMessage>();
using (Stream stream = File.OpenRead(QueryImagePath))
{
var response = await faceClient.DetectAsync(BinaryData.FromStream(stream), FaceDetectionModel.Detection03, FaceRecognitionModel.Recognition04, returnFaceId: true);
var faces = response.Value;
foreach (var face in faces)
{
using (var content = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new Dictionary<string, object> { ["faceId"] = face.FaceId, ["largeFaceListId"] = LargeFaceListId }))))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
results.Add(await httpClient.PostAsync($"{ENDPOINT}/face/v1.0/findsimilars", content));
}
}
}
Comme illustré précédemment, la gestion des données et la partie FindSimilar sont quasiment identiques. La seule exception est qu’une nouvelle opération Entraîner de prétraitement doit être exécutée dans l’objet LargeFaceList avant que l’opération FindSimilar puisse fonctionner.
Étape 2 : suggestions d’entraînement
Bien que l’opération Entraîner accélère les opérations FindSimilar et Identification, la durée de celle-ci augmente, en particulier à grande échelle. Le tableau suivant fournit une estimation de la durée de formation à différentes échelles.
Échelle - Visages ou personnes | Estimation de la durée de formation |
---|---|
1 000 | 1 à 2 secondes |
10 000 | 5 à 10 secondes |
100 000 | 1 à 2 minutes |
1 000 000 | 10 à 30 minutes |
Pour tirer le meilleur parti de la fonctionnalité de grande échelle, nous recommandons les stratégies suivantes.
Étape 2a : personnaliser un intervalle de temps
Comme indiqué dans TrainLargeFaceList()
, un intervalle (exprimé en millisecondes) permet de retarder le processus infini de vérification de l'état de la formation. Pour LargeFaceList avec davantage de visages, l’utilisation d’un intervalle plus élevé permet de réduire le nombre et le coût des appels. Personnalisez l’intervalle en fonction de la capacité attendue de l’objet LargeFaceList.
La même stratégie s’applique également à LargePersonGroup. Par exemple, lorsque vous entraînez un objet LargePersonGroup contenant 1 million de personnes, timeIntervalInMilliseconds
peut s’élever à 60 000, ce qui correspond à un intervalle d’une minute.
Étape 2b : mémoire tampon à petite échelle
Il est possible d’effectuer des recherches de personnes ou de visages dans un objet LargePersonGroup ou LargeFaceList à condition de l’avoir entraîné au préalable. Dans un scénario dynamique, de nouvelles personnes et de nouveaux visages sont constamment ajoutés et ceux-ci doivent être immédiatement interrogeables, mais la formation peut être plus longue qu'on ne le souhaiterait.
Pour atténuer ce problème, utilisez un objet LargePersonGroup ou LargeFaceList à petite échelle en guise de mémoire tampon pour les entrées nouvellement ajoutées. La formation de cette mémoire tampon prend moins de temps en raison de sa petite taille. La recherche immédiate sur cette mémoire tampon temporaire doit fonctionner. Utilisez cette mémoire tampon en combinaison avec un entraînement de l’objet LargePersonGroup ou LargeFaceList principal en exécutant l’entraînement principal sur un intervalle plus court. Par exemple, au milieu de la nuit ou quotidiennement.
Exemple de flux de travail :
- Créez un objet LargePersonGroup ou LargeFaceList principal correspondant à la collection principale. Créez une mémoire tampon LargePersonGroup ou LargeFaceList correspondant à la collection de mémoire tampon. La collection de mémoire tampon ne concerne que les personnes ou visages nouvellement ajoutés.
- Ajoutez de nouvelles personnes ou de nouveaux visages à la collection principale et à la collection de mémoire tampon.
- Formez la collection de mémoire tampon avec un intervalle court afin de garantir la prise en compte des nouvelles entrées.
- Appelez l’opération Identification ou FindSimilar sur la collection principale et sur la collection de mémoire tampon. Fusionnez les résultats.
- Lorsque la taille de la collection de mémoire tampon atteint un seuil ou lors d'une période d'inactivité du système, créez une nouvelle collection de mémoire tampon. Déclenchez l’opération Entraîner sur la collection principale.
- Supprimez l’ancienne collection de mémoire tampon à l’issue de l’opération Entraîner exécutée sur la collection principale.
Étape 2c : formation autonome
Si une latence relativement longue est acceptable, il n’est pas nécessaire de déclencher l’opération Entraîner juste après l’ajout de nouvelles données. Au lieu de cela, elle peut être séparée de la logique principale et déclenchée régulièrement. Cette stratégie convient aux scénarios dynamiques présentant une latence acceptable. Elle peut être appliquée à des scénarios statiques pour réduire la fréquence d’entraînement.
Supposons qu'il existe une fonction TrainLargePersonGroup
semblable à TrainLargeFaceList
. Voici un exemple d’implémentation standard de l’entraînement autonome sur un objet LargePersonGroup en appelant la classe Timer
dans System.Timers
:
private static void Main()
{
// Set up standalone training at regular intervals.
const int TimeIntervalForStatus = 1000 * 60; // 1-minute interval for getting training status.
const double TimeIntervalForTrain = 1000 * 60 * 60; // 1-hour interval for training.
var trainTimer = new Timer(TimeIntervalForTrain);
trainTimer.Elapsed += (sender, args) => TrainTimerOnElapsed("mylargepersongroupid_001", TimeIntervalForStatus);
trainTimer.AutoReset = true;
trainTimer.Enabled = true;
// Other operations like creating persons, adding faces, and identification, except for Train.
// ...
}
private static void TrainTimerOnElapsed(string largePersonGroupId, int timeIntervalInMilliseconds)
{
TrainLargePersonGroup(largePersonGroupId, timeIntervalInMilliseconds).Wait();
}
Pour plus d’informations sur la gestion des données et les implémentations relatives à l’identification, consultez Ajouter des visages.
Résumé
Dans ce guide, vous avez appris à migrer le code PersonGroup ou FaceList existant (et non les données) vers un objet LargePersonGroup ou LargeFaceList :
- Les objets LargePersonGroup et LargeFaceList fonctionnent comme les objets PersonGroup et FaceList, sauf qu’une opération Entraîner est requise par LargeFaceList.
- Adoptez la stratégie d’entraînement appropriée pour la mise à jour dynamique des données des jeux de données à grande échelle.
Étapes suivantes
Suivez un guide pratique pour savoir comment ajouter des visages à un groupe PersonGroup ou écrire un script pour exécuter l’opération Identifier sur un PersonGroup.