Ridimensionare per gestire più utenti registrati
Attenzione
L'accesso al servizio Viso è limitato in base ai criteri di idoneità e utilizzo al fine di supportare i nostri principi di intelligenza artificiale responsabile. Il servizio Viso è disponibile solo per i clienti e i partner gestiti da Microsoft. Usare il modulo di acquisizione riconoscimento volto per richiedere l'accesso. Per altre informazioni, vedere la pagina Accesso limitato dalla funzione Viso.
Questa guida illustra come aumentare le prestazioni da oggetti PersonGroup e FaceList esistenti agli oggetti LargePersonGroup e LargeFaceList, rispettivamente. PersonGroups può contenere fino a 1000 persone nel livello gratuito e 10.000 nel livello a pagamento. LargePersonGroups può contenere fino a un milione di persone nel livello a pagamento.
Importante
La struttura dei dati più recente PersonDirectory è consigliata per il nuovo sviluppo. Può contenere fino a 20 milioni di identità e non richiede il training manuale. Per altre informazioni, vedere la Guida a PersonDirectory.
Questa guida illustra il processo di migrazione. Presuppone una familiarità di base con gli oggetti PersonGroup e FaceList, con l'operazione Train e con le funzioni di riconoscimento volto. Per altre informazioni su questi argomenti, vedere la guida concettuale al riconoscimento volto.
LargePersonGroup e LargeFaceList vengono collettivamente definite operazioni su larga scala. LargePersonGroup può contenere fino a 1 milione di persone, ognuna con un massimo di 248 visi. LargeFaceList può contenere fino a 1 milione di visi. Le operazioni su larga scala sono simili a quelle convenzionali eseguite per PersonGroup e FaceList, ma presentano alcune differenze dovute alla nuova architettura.
Gli esempi sono scritti in C#.
Nota
Per abilitare le prestazioni di ricerca dell'API Viso per le operazioni Identification e FindSimilar su larga scala, introdurre un'operazione Train per eseguire l'analisi preliminare degli oggetti LargeFaceList e LargePersonGroup. Il tempo di training varia da alcuni secondi a circa mezz'ora, a seconda della capacità effettiva. Durante il periodo di training, è possibile eseguire le operazioni Identification e FindSimilar se in precedenza è stato eseguito un training corretto. Nuove persone e nuovi visi aggiunti non vengono visualizzati nel risultato fino a quando non viene completata una nuova post-migrazione al training su larga scala.
Passaggio 1: Migrazione del codice
Questa sezione illustra come eseguire la migrazione dell'implementazione di PersonGroup o FaceList a LargePersonGroup o LargeFaceList. Anche se LargePersonGroup e LargeFaceList differiscono da PersonGroup e FaceList nella progettazione e nell'implementazione interna, le interfacce API sono simili per la compatibilità con le versioni precedenti.
La migrazione dei dati non è supportata. È invece possibile ricreare l'oggetto LargePersonGroup o LargeFaceList.
Eseguire la migrazione da un oggetto PersonGroup a un oggetto LargePersonGroup
La migrazione da un oggetto PersonGroup a un oggetto LargePersonGroup è semplice. Questi oggetti condividono esattamente le stesse operazioni a livello di gruppo.
Per l'implementazione correlata a PersonGroup o Person è necessario solo modificare i percorsi dell'API o il modulo o la classe SDK in LargePersonGroup e LargePersonGroupPerson.
Aggiungere tutti i visi e tutte le persone dall'oggetto PersonGroup al nuovo oggetto LargePersonGroup. Per altre informazioni, vedere Aggiungere visi.
Eseguire la migrazione da un oggetto FaceList a un oggetto LargeFaceList
API di FaceList | API di LargeFaceList |
---|---|
Creazione | Creazione |
Eliminare | Elimina |
Recupero | Recupero |
List | List |
Aggiornamento | Aggiornamento |
- | Esecuzione del training |
- | Get Training Status |
La tabella precedente contiene un confronto delle operazioni a livello di elenco tra FaceList e LargeFaceList. Come illustrato, LargeFaceList include nuove operazioni, Train e Get Training Status, rispetto a FaceList. Il training dell'oggetto LargeFaceList è una condizione preliminare dell'operazione FindSimilar. Il training non è necessario per FaceList. Il frammento di codice seguente è una funzione helper per eseguire il training di un oggetto 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!");
}
}
}
In precedenza, un uso tipico di FaceList con l'aggiunta di visi e FindSimilar era il seguente:
// 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));
}
}
}
Quando si esegue la migrazione a LargeFaceList, il frammento di codice diventa il seguente:
// 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));
}
}
}
Come illustrato in precedenza, la gestione dei dati e la parte relativa a FindSimilar sono quasi identiche. L'unica eccezione è data dal fatto che, per il funzionamento di FindSimilar, è necessario completare un'operazione Train di analisi preliminare nell'oggetto LargeFaceList.
Passaggio 2: Suggerimenti per l'operazione Train
Anche se l'operazione Train velocizza FindSimilar e Identification, l'impatto sui tempi di training è negativo, soprattutto su larga scala. I tempi di training stimati per dimensioni diverse sono elencati nella tabella seguente.
Dimensione per visi o persone | Tempo di training stimato |
---|---|
1.000 | 1-2 secondi |
10,000 | 5-10 secondi |
100,000 | 1-2 minuti |
1\.000.000 | 10-30 minuti |
Per usare al meglio la funzionalità su larga scala, è consigliabile adottare le strategie seguenti.
Passaggio 2a: Personalizzare l'intervallo di tempo
Come illustrato in TrainLargeFaceList()
, è presente un intervallo di tempo, espresso in millisecondi, per ritardare il processo di verifica dello stato di training infinito. Per l'elemento LargeFaceList con più visi, l'uso di un intervallo maggiore riduce il numero e il costo delle chiamate. È quindi necessario personalizzare l'intervallo di tempo in base alla capacità prevista di LargeFaceList.
La stessa strategia si applica anche a LargePersonGroup. Quando, ad esempio, si esegue il training di un oggetto LargePersonGroup con 1 milione di persone, il valore di timeIntervalInMilliseconds
può essere 60.000, ovvero un intervallo di 1 minuto.
Passaggio 2b: Buffer su scala ridotta
È possibile eseguire la ricerca di persone o visi in un oggetto LargePersonGroup o LargeFaceList solo dopo l'esecuzione del training. In uno scenario dinamico, nuovi visi o persone vengono continuamente aggiunti e deve essere immediatamente possibile eseguirne la ricerca. Il training può pertanto richiedere più tempo del previsto.
Per attenuare questo problema, usare un oggetto LargePersonGroup o LargeFaceList di dimensioni molto piccole come buffer solo per le voci appena aggiunte. Grazie alle dimensioni ridotte, questo buffer richiede un tempo di training minore. La funzionalità di ricerca immediata in questo buffer temporaneo dovrebbe funzionare in modo corretto. Usare il buffer in combinazione con il training nell'oggetto LargePersonGroup o LargeFaceList principale eseguendo il training principale a intervalli meno frequenti. Ad esempio, a mezzanotte e con frequenza giornaliera.
Di seguito viene indicato un flusso di lavoro di esempio:
- Creare un oggetto LargePersonGroup o LargeFaceList principale, che rappresenta la raccolta principale. Creare un buffer LargePersonGroup o LargeFaceList, che rappresenta la raccolta buffer. La raccolta buffer è relativa solo alle persone o ai visi nuovi aggiunti.
- Aggiungere nuovi visi o persone sia alla raccolta principale che alla raccolta buffer.
- Eseguire solo il training della raccolta buffer con un breve intervallo di tempo per verificare che le voci appena aggiunte diventino effettive.
- Chiamare l'operazione Identification o FindSimilar sia sulla raccolta principale che sulla raccolta buffer. Unire i risultati.
- Quando le dimensioni della raccolta buffer aumentano fino a una soglia stabilita o causano un tempo di inattività del sistema, creare una nuova raccolta buffer. Attivare l'operazione Train nella raccolta principale.
- Eliminare la raccolta buffer precedente dopo il completamento dell'operazione Train nella raccolta principale.
Passaggio 2c: Training autonomo
Se una latenza relativamente lunga è accettabile, non è necessario attivare l'operazione Train subito dopo l'aggiunta di nuovi dati. L'operazione Train può essere invece divisa dalla logica principale e attivata regolarmente. Questa strategia è adatta per scenari dinamici con una latenza accettabile. È applicabile a scenari statici per ridurre ulteriormente la frequenza dell'operazione Train.
Si supponga che sia disponibile una funzione TrainLargePersonGroup
analoga alla funzione TrainLargeFaceList
. Un'implementazione tipica del training autonomo su un oggetto LargePersonGroup richiamando la classe Timer
in System.Timers
è la seguente:
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();
}
Per altre informazioni sulla gestione dei dati e sulle implementazioni correlate all'identificazione, vedere Aggiungere visi.
Riepilogo
In questa guida si è appreso come eseguire la migrazione del codice PersonGroup o FaceList esistente (non di dati) a un oggetto LargePersonGroup o LargeFaceList:
- LargePersonGroup e LargeFaceList funzionano in modo analogo a PersonGroup o FaceList, ad eccezione del fatto che per LargeFaceList è richiesta l'operazione Train.
- Per aggiornare i dati dinamici per set di dati su larga scala, adottare la strategia Train corretta.
Contenuto correlato
Seguire le procedure per aggiungere visi a PersonGroup o scrivere uno script per eseguire l'operazione Identify su PersonGroup.