Udostępnij za pośrednictwem


Skalowanie w celu obsługi większej liczby zarejestrowanych użytkowników

Uwaga

Dostęp do usługi rozpoznawania twarzy jest ograniczony na podstawie kryteriów kwalifikowalności i użycia w celu obsługi naszych zasad odpowiedzialnej sztucznej inteligencji. Usługa rozpoznawania twarzy jest dostępna tylko dla klientów i partnerów zarządzanych przez firmę Microsoft. Użyj formularza do wprowadzania rozpoznawania twarzy, aby ubiegać się o dostęp. Aby uzyskać więcej informacji, zobacz stronę Dostęp ograniczony do twarzy.

W tym przewodniku przedstawiono sposób skalowania w górę z istniejących obiektów PersonGroup i FaceList do obiektów LargePersonGroup i LargeFaceList, odpowiednio. OsobyGroup mogą pomieścić do 1000 osób w warstwie Bezpłatna i 10 000 w warstwie płatnej, podczas gdy grupy LargePersonGroup mogą pomieścić do miliona osób w warstwie płatnej.

Ważne

Nowsza struktura danych PersonDirectory jest zalecana do tworzenia nowych rozwiązań. Może pomieścić do 75 milionów tożsamości i nie wymaga ręcznego trenowania. Aby uzyskać więcej informacji, zobacz przewodnik PersonDirectory.

W tym przewodniku przedstawiono proces migracji. Przyjęto założenie, że podstawowa znajomość obiektów PersonGroup i FaceList , operacji Train i funkcji rozpoznawania twarzy. Aby dowiedzieć się więcej o tych tematach, zobacz przewodnik koncepcyjny dotyczący rozpoznawania twarzy.

LargePersonGroup i LargeFaceList są wspólnie określane jako operacje na dużą skalę. Grupa LargePersonGroup może zawierać maksymalnie 1 milion osób, z których każda ma maksymalnie 248 twarzy. Lista LargeFaceList może zawierać maksymalnie 1 milion twarzy. Operacje na dużą skalę są podobne do konwencjonalnych obiektów PersonGroup i FaceList, ale mają pewne różnice ze względu na nową architekturę.

Przykłady są napisane w języku C#.

Uwaga

Aby włączyć wydajność wyszukiwania twarzy dla funkcji Identification i FindSimilar na dużą skalę, wprowadź operację Train w celu wstępnego przetwarzania elementów LargeFaceList i LargePersonGroup. Czas trenowania różni się od sekund do około pół godziny na podstawie rzeczywistej pojemności. W okresie treningowym możliwe jest przeprowadzenie identyfikacji i rozwiązania FindSimilar , jeśli wcześniej wykonano pomyślne szkolenie. Wadą jest to, że nowe dodane osoby i twarze nie pojawiają się w wyniku do momentu ukończenia nowego szkolenia po migracji do szkolenia na dużą skalę.

Krok 1. Migracja kodu

W tej sekcji opisano sposób migrowania implementacji PersonGroup lub FaceList do kolekcji LargePersonGroup lub LargeFaceList. Mimo że funkcja LargePersonGroup lub LargeFaceList różni się od elementu PersonGroup lub FaceList w implementacji projektowej i wewnętrznej, interfejsy API są podobne do zgodności z poprzednimi wersjami.

Migracja danych nie jest obsługiwana. Zamiast tego należy ponownie utworzyć element LargePersonGroup lub LargeFaceList .

Migrowanie obiektu PersonGroup do grupy LargePersonGroup

Migracja z obiektu PersonGroup do grupy LargePersonGroup jest prosta. Współużytkują dokładnie te same operacje na poziomie grupy.

W przypadku implementacji personGroup lub powiązanej z osobą należy zmienić tylko ścieżki interfejsu API lub klasę/moduł zestawu SDK na LargePersonGroup i LargePersonGroup Person.

Dodaj wszystkie twarze i osoby z grupy PersonGroup do nowej grupy LargePersonGroup. Aby uzyskać więcej informacji, zobacz Dodawanie twarzy.

Migrowanie listy FaceList do listy LargeFaceList

Interfejsy API facelist Interfejsy API largeFaceList
Utwórz Utwórz
Usuń Delete
Get Get
List List
Aktualizacja Aktualizacja
- Szkolenie
- Pobieranie stanu szkolenia

Poprzednia tabela jest porównaniem operacji na poziomie listy między FaceList i LargeFaceList. Jak pokazano, aplikacja LargeFaceList zawiera nowe operacje, Train and Get Training Status (Trenowanie i uzyskiwanie stanu trenowania) w porównaniu z aplikacją FaceList. Trenowanie elementu LargeFaceList jest warunkiem wstępnym operacji FindSimilar. Trenowanie nie jest wymagane w przypadku aplikacji FaceList. Poniższy fragment kodu jest funkcją pomocnika, która czeka na szkolenie elementu 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!");
        }
    }
}

Wcześniej typowe użycie funkcji FaceList z dodanymi twarzami i aplikacją FindSimilar wyglądało następująco:

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

Podczas migracji do listy LargeFaceList staje się ona następująca:

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

Jak pokazano wcześniej, zarządzanie danymi i część FindSimilar są prawie takie same. Jedynym wyjątkiem jest to, że nowa operacja wstępnego przetwarzania wstępnego musi zakończyć się w largeFaceList przed działaniem findSimilar .

Krok 2. Trenowanie sugestii

Chociaż operacja trenowania przyspiesza działanie FindSimilar i Identification, czas trenowania cierpi, zwłaszcza w przypadku zbliżania się do dużej skali. Szacowany czas trenowania w różnych skalach znajduje się w poniższej tabeli.

Skalowanie twarzy lub osób Szacowany czas trenowania
1000 1–2 s
10,000 5–10 s
100 000 1–2 min
1 000 000 10-30 min

Aby lepiej wykorzystać funkcję na dużą skalę, zalecamy następujące strategie.

Krok 2a. Dostosowywanie interwału czasu

Jak pokazano w pliku TrainLargeFaceList(), istnieje interwał czasu w milisekundach, aby opóźnić nieskończony proces sprawdzania stanu trenowania. W przypadku elementu LargeFaceList z większą liczbą twarzy użycie większego interwału zmniejsza liczbę wywołań i koszty. Dostosuj przedział czasu zgodnie z oczekiwaną pojemnością listy LargeFaceList.

Ta sama strategia dotyczy również grupy LargePersonGroup. Na przykład w przypadku trenowania grupy LargePersonGroup z 1 milionami osób timeIntervalInMilliseconds może wynosić 60 000, co jest 1-minutowym interwałem.

Krok 2b. Bufor o małej skali

Osoby lub twarze w grupie LargePersonGroup lub LargeFaceList można wyszukiwać tylko po przeszkoleniu. W scenariuszu dynamicznym nowe osoby lub twarze są stale dodawane i muszą być natychmiast wyszukiwane, ale szkolenie może trwać dłużej niż jest to wymagane.

Aby rozwiązać ten problem, użyj dodatkowej klasy LargePersonGroup lub LargeFaceList jako buforu tylko dla nowo dodanych wpisów. Ten bufor zajmuje krótszy czas trenowania z powodu mniejszego rozmiaru. Natychmiastowe wyszukiwanie w tym tymczasowym buforze powinno działać. Użyj tego buforu w połączeniu ze szkoleniem w master LargePersonGroup lub LargeFaceList , uruchamiając szkolenie główne w interwale rozrzedzanym. Przykłady znajdują się w środku nocy i codziennie.

Przykładowy przepływ pracy:

  1. Utwórz główny obiekt LargePersonGroup lub LargeFaceList, który jest kolekcją master. Utwórz bufor LargePersonGroup lub LargeFaceList, który jest kolekcją. Kolekcja dotyczy tylko nowo dodanych osób lub twarzy.
  2. Dodaj nowe osoby lub twarze zarówno do kolekcji głównej, jak i kolekcji.
  3. Wytrenuj tylko kolekcję z krótkim interwałem czasu, aby upewnić się, że nowo dodane wpisy zostaną zastosowane.
  4. Wywołaj metodę Identification lub FindSimilar zarówno dla kolekcji głównej, jak i kolekcji. Scal wyniki.
  5. Gdy rozmiar kolekcji zwiększa się do progu lub w czasie bezczynności systemu, utwórz nową kolekcję. Wyzwól operację Train (Trenowanie) w kolekcji master.
  6. Usuń starą kolekcję po zakończeniu operacji Train (Trenowanie ) w kolekcji master.

Krok 2c. Trenowanie autonomiczne

Jeśli stosunkowo długie opóźnienie jest akceptowalne, nie jest konieczne wyzwolenie operacji Trenowanie bezpośrednio po dodaniu nowych danych. Zamiast tego operację Trenowanie można podzielić z główną logiką i wyzwalać regularnie. Ta strategia jest odpowiednia dla scenariuszy dynamicznych z dopuszczalnym opóźnieniem. Można go zastosować do scenariuszy statycznych, aby jeszcze bardziej zmniejszyć częstotliwość trenowania.

Załóżmy, że istnieje funkcja podobna TrainLargePersonGroup do TrainLargeFaceList. Typowa implementacja autonomicznego szkolenia w grupie LargePersonGroup przez wywołanie Timer klasy w System.Timers programie to:

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

Aby uzyskać więcej informacji na temat zarządzania danymi i implementacji związanych z identyfikacją, zobacz Dodawanie twarzy.

Podsumowanie

W tym przewodniku przedstawiono sposób migrowania istniejącego kodu PersonGroup lub FaceList, a nie danych do kolekcji LargePersonGroup lub LargeFaceList:

  • Funkcja LargePersonGroup i LargeFaceList działają podobnie do obiektu PersonGroup lub FaceList, z tą różnicą, że operacja Train jest wymagana przez largeFaceList.
  • Weź odpowiednią strategię trenowania w celu dynamicznej aktualizacji danych dla zestawów danych na dużą skalę.

Następne kroki

Postępuj zgodnie z przewodnikiem z instrukcjami, aby dowiedzieć się, jak dodawać twarze do grupy PersonGroup lub pisać skrypt, aby wykonać operację Identyfikowanie w grupie PersonGroup.