Dimensionar para lidar com mais usuários inscritos
Cuidado
O acesso ao serviço de Detecção Facial é limitado com base em critérios de qualificação e uso para dar suporte aos nossos princípios de IA responsável. O serviço de Detecção Facial só está disponível para clientes e parceiros gerenciados da Microsoft. Use o Formulário de admissão de reconhecimento facial para solicitar acesso. Para obter mais informações, consulte a página Acesso limitado facial.
Este guia mostra a você como ampliar objetos de um PersonGroup e FaceList existentes para objetos de LargePersonGroup e LargeFaceList, respectivamente. PersonGroups podem conter até 1.000 pessoas no nível gratuito e 10.000 no nível pago, enquanto LargePersonGroups podem armazenar até um milhão de pessoas no nível pago.
Importante
A estrutura de dados mais recente PersonDirectory é recomendada para o novo desenvolvimento. Ele pode conter até 75 milhões de identidades e não requer treinamento manual. Para obter mais informações, consulte o Guia PersonDirectory.
Este guia demonstra o processo de migração. O guia pressupõe uma familiaridade básica com objetos de PersonGroup e FaceList, com a operação Treinar e com as funções de reconhecimento facial. Para saber mais sobre esses temas, confira o guia conceitual de reconhecimento facial.
LargePersonGroup e LargeFaceList são mencionados, coletivamente, como operações em grande escala. Um LargePersonGroup pode conter até um milhão de pessoas, cada um com no máximo 248 rostos. Um LargeFaceList pode conter até um milhão de rostos. As operações em grande escala são semelhantes aos PersonGroups e FaceLists convencionais, mas têm algumas diferenças devido à nova arquitetura.
As amostras são escritas em C#.
Observação
Para habilitar o desempenho da pesquisa de rostos para fins de Identificação e FindSimilar em grande escala, introduza uma operação Treinar para pré-processar a LargeFaceList e o LargePersonGroup. O tempo de treinamento varia de segundos a cerca de meia hora, dependendo da capacidade real. Durante o período de treinamento, é possível executar operações de Identificação e FindSimilar se uma operação de treinamento bem-sucedida tiver sido realizada anteriormente. A desvantagem é que as novas pessoas e faces adicionadas serão exibidas no resultado apenas quando for realizada uma nova migração de publicação para treinamento em grande escala.
Etapa 1: migração de código
Esta seção se concentra apenas em como migrar a implementação de um PersonGroup ou FaceList para um LargePersonGroup ou LargeFaceList. Embora um LargePersonGroup ou LargeFaceList sejam diferentes de um PersonGroup ou FaceList em termos de design e implementação interna, as interfaces de API são semelhantes para fins de compatibilidade com versões anteriores.
Não há suporte para migração de dados. Em vez disso, você recria o LargePersonGroup ou a LargeFaceList.
Migrar um objeto PersonGroup para um LargePersonGroup
A migração de um PersonGroup para um LargePersonGroup é simples. Eles compartilham exatamente as mesmas operações em nível de grupo.
Para PersonGroup ou implementação relacionada a pessoa, é necessário alterar apenas os caminhos da API ou a classe/módulo do SDK para LargePersonGroup e LargePersonGroup Person.
Adicione todos os rostos e pessoas do PersonGroup ao novo LargePersonGroup. Para saber mais, confira Adicionar faces.
Migrar um FaceList para um LargeFaceList
APIs de FaceList | APIs de LargeFaceList |
---|---|
Criar | Criar |
Excluir | Excluir |
Obter | Obter |
Lista | Lista |
Atualizar | Atualizar |
- | Treinar |
- | Obter Status de Treinamento |
A tabela anterior é uma comparação de operações em nível de lista entre FaceList e LargeFaceList. Como já mostramos, a LargeFaceList vem com novas operações, Treinar e Obter Status de Treinamento, quando comparada à FaceList. Treinar a LargeFaceList é um pré-requisito para a operação FindSimilar. O treinamento não é obrigatório para a FaceList. O snippet de código a seguir é uma função auxiliar para aguardar o treinamento de uma 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, um uso típico da FaceList com rostos adicionados e do FindSimilar se parecia com o seguinte:
// 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));
}
}
}
Ao migrá-los para a LargeFaceList, passa a se parecer com o seguinte:
// 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));
}
}
}
Como já mostramos antes, o gerenciamento de dados e a parte FindSimilar são quase iguais. A única exceção é que um novo pré-processamento da operação Treinar deve ser executado na LargeFaceList para que a operação FindSimilar funcione.
Etapa 2: treinar sugestões
Embora a operação Treinar acelere as operações FindSimilar e Identificação, o tempo de treinamento é afetado, principalmente quando é realizado em grande escala. O tempo de treinamento estimado em escalas diferentes está listado na tabela a seguir.
Escala de faces ou pessoas | Tempo de treinamento 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 usar o recurso de grande escala da maneira ideal, convém implementar as estratégias a seguir.
Etapa 2a: personalizar o intervalo de tempo
Conforme demonstrado em TrainLargeFaceList()
, há um intervalo de tempo em milissegundos para atrasar o processo de verificação de status de treinamento infinito. Para a LargeFaceList com mais rostos, usar um intervalo maior reduz o número e o custo das chamadas. Personalize o intervalo de tempo de acordo com a capacidade esperada da LargeFaceList.
A mesma estratégia também se aplica ao LargePersonGroup. Por exemplo, quando você treina um LargePersonGroup com um milhão de pessoas, o timeIntervalInMilliseconds
deverá ser 60.000, o que constitui um intervalo de um minuto.
Etapa 2b: buffer de pequena escala
As pessoas ou rostos em um LargePersonGroup ou uma LargeFaceList são pesquisáveis apenas após terem sido treinados. Em um cenário dinâmico, novas pessoas ou faces são constantemente adicionadas e devem ser imediatamente pesquisáveis, mesmo que o treinamento leve mais tempo do que o esperado.
Para atenuar esse problema, use um LargePersonGroup ou uma LargeFaceList adicionais de dimensões reduzidas como um buffer, somente para as inserções recém-adicionadas. Este buffer consome menos tempo em treinamento devido ao tamanho menor. O recurso de pesquisa imediata deve funcionar neste buffer temporário. Use esse buffer combinado com o treinamento no LargePersonGroup ou LargeFaceList mestres executando o treinamento mestre em um intervalo mais esparso. por exemplo, à meia-noite e diariamente.
Fluxo de trabalho de exemplo:
- Crie um LargePersonGroup ou uma LargeFaceList mestres, o que constitui a coleção mestre. Crie um LargePersonGroup ou uma LargeFaceList para servirem de buffer, o que constitui a coleção de buffers. A coleção de buffers se destina apenas a pessoas ou faces recém-adicionadas.
- Adicione novas pessoas ou faces à coleção mestra e à coleção de buffers.
- Treine apenas a coleção de buffers com um intervalo de tempo curto para garantir que as entradas recém-adicionadas tenham efeito.
- Chame a Identificação ou FindSimilar tanto para a coleção mestre quanto para a coleção de buffers. Mescle os resultados.
- Se o tamanho da coleção de buffers aumentar para um determinado limite ou em um tempo ocioso do sistema, crie uma nova coleção de buffers. Acione a operação Treinar na coleção mestre.
- Exclua a coleção de buffers antiga após o término da operação Treinar na coleção mestre.
Etapa 2c: treinamento autônomo
Se uma latência relativamente longa for aceitável, não será necessário acionar a operação Treinar imediatamente após adicionar novos dados. Em vez disso, a operação Treinar pode ser separada da lógica principal e acionada regularmente. Esta estratégia é adequada para cenários dinâmicos com uma latência aceitável. Pode ser aplicada a cenários estáticos para reduzir ainda mais a frequência da operação Treinar.
Digamos que haja uma função TrainLargePersonGroup
semelhante a TrainLargeFaceList
. Uma implementação típica de um treinamento autônomo em um LargePersonGroup invocando a classe Timer
em System.Timers
seria:
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 saber mais sobre gerenciamento de dados e implementações relacionadas à identificação, confira os tópicos Adicionar rostos.
Resumo
Neste guia, você aprendeu como migrar o código existente de um PersonGroup ou de uma FaceList, sem dados, para um LargePersonGroup ou uma LargeFaceList:
- Um LargePersonGroup e uma LargeFaceList funcionam de modo semelhante a um PersonGroup ou uma FaceList, com a exceção de que a operação Treinar é obrigatória para uma LargeFaceList.
- Adote a estratégia adequada da operação Treinar para uma atualização dinâmica de dados em conjuntos de dados de grandes dimensões.
Próximas etapas
Siga um guia de instruções para aprender a adicionar rostos a um PersonGroup ou escreva um script para realizar a operação Identificar em um PersonGroup.