Comparteix a través de


Escala para manejar más usuarios inscritos

Precaución

El acceso al servicio Face está limitado en función de los criterios de idoneidad y uso para apoyar nuestros principios de inteligencia artificial responsable. El servicio Face solo está disponible para clientes y asociados administrados por Microsoft. Use el formulario de admisión de reconocimiento facial para solicitar acceso. Para obtener más información, consulte la página Acceso limitado de Face.

En esta guía aprenderá a escalar verticalmente a partir de los objetos existentes PersonGroup y FaceList a los objetos LargePersonGroup y LargeFaceList respectivamente. PersonGroups puede contener hasta 1000 personas en el nivel gratis y 10 000 en el nivel de pago, mientras que LargePersonGroups puede contener hasta un millón de personas en el nivel de pago.

Importante

Se recomienda la estructura de datos más reciente PersonDirectory para el nuevo desarrollo. Puede contener hasta 75 millones de identidades y no requiere entrenamiento manual. Para obtener más información, consulte la guía PersonDirectory.

En esta guía se muestra el proceso de migración. Se presuponen conocimientos básicos de los objetos PersonGroup y FaceList, de la operación Train y de las funciones de reconocimiento facial. Para más información sobre estos temas, consulte la guía conceptual de reconocimiento facial.

LargePersonGroup y LargeFaceList se conocen colectivamente como operaciones a gran escala. LargePersonGroup puede contener hasta un millón de personas, cada una de las cuales con un máximo de 248 caras. LargeFaceList puede contener hasta un millón de caras. Las operaciones a gran escala son similares a las de PersonGroup y FaceList convencionales, pero tienen algunas diferencias debido a la arquitectura nueva.

Los ejemplos están escritos en C#.

Nota:

Para habilitar la búsqueda de caras para Identification y FindSimilar a gran escala, incorpore una operación Train para preprocesar LargeFaceList y LargePersonGroup. El tiempo de entrenamiento varía de unos segundos a una media hora de acuerdo con la capacidad real. Durante el período de entrenamiento, es posible realizar las operaciones Identification y FindSimilar si antes se realizó correctamente un entrenamiento. El inconveniente es que las personas y caras nuevas agregadas no aparecen en el resultado hasta que se complete una nueva migración posterior al entrenamiento a gran escala.

Paso 1: Migración del código

Esta sección se centra en cómo migrar la implementación de PersonGroup o FaceList a LargePersonGroup o LargeFaceList. Aunque LargePersonGroup o LargeFaceList difieren de PersonGroup o FaceList en el diseño y la implementación interna, las interfaces de la API son similares por motivos de compatibilidad con versiones anteriores.

No se admite la migración de datos. En su lugar, puede volver a crear LargePersonGroup o LargeFaceList.

Migración de PersonGroup a LargePersonGroup

Migrar de PersonGroup a LargePersonGroup es una operación sencilla. Comparten exactamente las mismas operaciones de nivel de grupo.

Para la implementación de PersonGroup (o una implementación relacionada con las personas), solo es necesario cambiar las rutas de las API o la clase/módulo del SDK a LargePersonGroup y LargePersonGroup Person.

Agregue todas las caras y las personas de PersonGroup al nuevo LargePersonGroup. Para más información, consulte Agregar caras.

Migración de FaceList y LargeFaceList

API de FaceList API de LargeFaceList
Crear Crear
Eliminar Eliminar
Obtener Obtener
List List
Actualizar Actualizar
- Train
- Get Training Status

La tabla anterior es una comparación de las operaciones en el nivel de lista entre FaceList y LargeFaceList. Tal y como se muestra, LargeFaceList incluye nuevas operaciones (Train y Get Training Status) en comparación con FaceList. Entrenar LargeFaceList es una condición previa de la operación FindSimilar. El entrenamiento no es necesario para FaceList. El fragmento de código siguiente es una función auxiliar para esperar el entrenamiento de 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!");
        }
    }
}

Anteriormente, un uso típico de FaceList con caras agregadas y FindSimilar tenía este aspecto:

// 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));
        }
    }
}

En la migración a LargeFaceList, se convierte en lo siguiente:

// 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));
        }
    }
}

Tal como se mostró anteriormente, la administración de datos y la parte FindSimilar son prácticamente iguales. La única excepción es que debe realizarse una operación Train de procesamiento previo totalmente nueva en LargeFaceList para que FindSimilar funcione.

Paso 2: Entrenamiento de sugerencias

Aunque la operación Train acelera las operaciones FindSimilar e Identification, el tiempo de entrenamiento se ve afectado, especialmente cuando se aplica a gran escala. En la tabla siguiente se indica el tiempo de entrenamiento estimado de las diferentes escalas.

Escala para caras o personas Tiempo de entrenamiento estimado
1,000 1 a 2 segundos
10 000 5 a 10 segundos
100 000 1 a 2 minutos
1 000 000 10 a 30 minutos

Para aprovechar más la característica a gran escala, se recomiendan las estrategias siguientes.

Paso 2a: Personalización del intervalo de tiempo

Como se muestra en TrainLargeFaceList(), hay un intervalo de tiempo en milisegundos para retrasar el proceso de comprobación del estado del entrenamiento infinito. Cuando LargeFaceList tiene más caras, el uso de un intervalo mayor reduce el número de llamadas y el costo. Personalice el intervalo de tiempo según la capacidad esperada de LargeFaceList.

La misma estrategia se aplica también a LargePersonGroup. Por ejemplo, al entrenar un LargePersonGroup con un millón de personas, timeIntervalInMilliseconds puede ser 60 000, que es un intervalo de un minuto.

Paso 2b: Búfer a pequeña escala

Las búsquedas de personas o caras en LargePersonGroup o LargeFaceList solo se pueden realizar después del entrenamiento. En un escenario dinámico, se agregan constantemente nuevas personas o caras que deben poder buscarse de inmediato, aunque el entrenamiento podría tardar más tiempo del deseado.

Para mitigar este problema, use una colección LargePersonGroup o LargeFaceList a pequeña escala adicional como búfer solo para las entradas recién agregadas. Este búfer tarda menos tiempo en el entrenamiento porque es más pequeño. La funcionalidad de búsqueda inmediata en este búfer temporal debería funcionar. Use este búfer en combinación con el entrenamiento en la colección LargePersonGroup o LargeFaceList maestra mediante la ejecución del entrenamiento maestro en un intervalo más escaso. Algunos ejemplos son en medio de la noche y a diario.

Flujo de trabajo de ejemplo:

  1. Cree un LargePersonGroup o un LargeFaceList maestro, es decir, una colección maestra. Cree un LargePersonGroup o un LargeFaceList de búfer, es decir, una colección búfer. La colección búfer es solo para las personas o caras recién agregadas.
  2. Agregue las nuevas personas o caras a la colección maestra y a la colección búfer.
  3. Entrene solo la colección búfer a intervalos más cortos para garantizar que las entradas recién agregadas surtan efecto.
  4. Llame a Identification o FindSimilar en la colección maestra y la colección búfer. Combine los resultados.
  5. Cuando el tamaño de la colección búfer aumenta hasta un umbral o en un tiempo de inactividad del sistema, cree una nueva colección búfer. Desencadene la operación Train en la colección maestra.
  6. Elimine la colección búfer anterior una vez que finalice la operación Train de la colección maestra.

Paso 2c: Entrenamiento independiente

Si es aceptable una latencia relativamente larga, no es necesario desencadenar la operación Train justo después de agregar datos nuevos. En su lugar, la operación Train se puede separar de la lógica principal y desencadenar periódicamente. Esta estrategia es adecuada para escenarios dinámicos con una latencia aceptable. Se puede aplicar a escenarios estáticos para reducir aún más la frecuencia de la operación Train.

Supongamos que hay una función TrainLargePersonGroup similar a TrainLargeFaceList. Una implementación típica del entrenamiento independiente en LargePersonGroup mediante la invocación de la clase Timer en System.Timers es:

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();
}

Para más información sobre implementaciones relacionadas con la identificación y la administración de datos, consulte Agregar caras.

Resumen

En esta guía, aprendió a migrar el código existente de PersonGroup o FaceList, no los datos, a LargePersonGroup o LargeFaceList:

  • LargePersonGroup y LargeFaceList funcionan de manera similar a PersonGroup o FaceList, excepto en que LargeFaceList requiere la operación Train.
  • Adopte la estrategia de Train adecuada para la actualización de datos dinámicos en conjuntos de datos a gran escala.

Pasos siguientes

Siga una guía de procedimientos para obtener información sobre cómo agregar caras a un elemento PersonGroup o escribir un script para efectuar la operación de identificación en un elemento PersonGroup.