共用方式為


擴大以處理更多已註冊的使用者

警告

臉部辨識服務存取受限於資格和使用準則,以支援我們的「負責任的 AI 原則」。 臉部辨識服務僅供 Microsoft 受管理的客戶和合作夥伴使用。 請使用臉部辨識受理表單以申請存取。 如需詳細資訊,請參閱臉部的有限存取權頁面

本指南說明如何分別從現有的 PersonGroupFaceList 物件擴大至 LargePersonGroupLargeFaceList 物件。 PersonGroup 在免費層最多可以保留 1000 個人員,在付費層可以保留 10,000 個人員,而 LargePersonGroup 在付費層最多可以保留一百萬個人員。

重要

建議使用較新的資料結構 PersonDirectory 進行新的開發。 它最多可以保存 7500 萬個身分識別,而且不需要手動訓練。 如需詳細資訊,請參閱 PersonDirectory 指南 (部分機器翻譯)。

本指南示範移轉流程。 它假設您對於 PersonGroupFaceList 物件、定型作業和臉部辨識功能已有基本了解。 若要深入了解這些物件,請參閱臉部辨識概念指南。

LargePersonGroupLargeFaceList 統稱為大規模作業。 LargePersonGroup 最多可包含 1 百萬個人員,每個人員最多 248 張臉。 LargeFaceList 最多可包含 1 百萬張臉。 大規模作業與傳統的 PersonGroupFaceList 類似,但由於採用新結構,因此有一些差異。

範例是以 C# 撰寫。

注意

若要大規模針對 IdentificationFindSimilar 啟用臉部搜尋效能,請導入定型作業以預先處理 LargeFaceListLargePersonGroup。 根據實際容量而定,定型時間從數秒到大約半小時不等。 如果先前已成功進行定型作業,則在定型期間可以執行 IdentificationFindSimilar。 缺點則是在完成新的大規模定型後移轉之前,新增的人員和臉部不會出現在結果中。

步驟 1:程式碼移轉

本節著重於如何將 PersonGroupFaceList 實作移轉至 LargePersonGroupLargeFaceList。 雖然 LargePersonGroupLargeFaceListPersonGroupFaceList 在設計及內部實作上有所差異,但 API 介面類似,均支援回溯相容性。

不支援資料移轉。 您可以改為重新建立 LargePersonGroupLargeFaceList

將 PersonGroup 移轉至 LargePersonGroup

PersonGroup 移轉至 LargePersonGroup 很簡單。 它們會共用完全相同的群組層級作業。

針對 PersonGroup 或人員相關的實作,只需將 API 路徑或 SDK 類別/模組變更為 LargePersonGroupLargePersonGroup Person 即可。

PersonGroup 中所有臉部和人員新增至新的 LargePersonGroup。 如需詳細資訊,請參閱新增臉部

將 FaceList 移轉至 LargeFaceList

FaceList API LargeFaceList API
建立​​ 建立​​
刪除 刪除
Get Get
清單​​ 清單​​
更新 更新
- 定型
- 取得定型訓練

上表是 FaceListLargeFaceList 之間的清單層級作業比較。 如上所示,相較於 FaceListLargeFaceList 附帶了定型取得定型狀態這兩項新作業。 將 LargeFaceList 定型是 FindSimilar 作業的先決條件。 FaceList 不需要定型。 以下程式碼片段是一個等候 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!");
        }
    }
}

先前,FaceList (搭配新增臉部) 和 FindSimilar 的一般用法如下所示:

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

將其移轉至 LargeFaceList 時,即會變成:

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

如前所示,資料管理和 FindSimilar 部分幾乎相同。 唯一例外是必須先在 LargeFaceList 中完成全新的預先處理定型作業,FindSimilar 才能運作。

步驟 2:定型建議

雖然定型作業可加快 FindSimilarIdentification 的速度,但定型時間會受到影響,尤其是在大規模進行時。 下表列出針對各種不同規模預估的定型時間。

臉部或人員的規模 預估的定型時間
1,000 1-2 秒
10,000 5-10 秒
100,000 1-2 分
1,000,000 10-30 分

若要充分利用大規模功能,我們建議使用下列策略。

步驟 2a:自訂時間間隔

TrainLargeFaceList() 中所示,有一個會延遲無限定型狀態檢查程序的時間間隔 (以毫秒為單位)。 針對臉部數量較多的 LargeFaceList,使用較長的間隔將可降低呼叫計數和成本。 根據預期的 LargeFaceList 容量來自訂時間間隔。

相同的策略也適用於 LargePersonGroup。 例如,當您將含有 1 百萬個人員的 LargePersonGroup 定型時,timeIntervalInMilliseconds 可能為 60,000 (此為 1 分鐘的間隔)。

步驟 2b:小規模緩衝區

LargePersonGroupLargeFaceList 中的人員或臉部只有在定型後才可供搜尋。 在動態案例中,新的人員或臉部會持續新增且必須立即可供搜尋,而定型所花費的時間可能超出預期。

為了緩解此問題,請只針對剛新增的項目使用額外的小規模 LargePersonGroupLargeFaceList 作為緩衝區。 由於大小較小,因此,此緩衝區會採用較短的時間來定型。 此暫存緩衝區上的即時搜尋功能應會運作。 透過以更疏鬆的間隔來執行主要定型,將此緩衝區與主要 LargePersonGroupLargeFaceList 上的定型一起使用。 範例包括午夜和每日。

範例工作流程:

  1. 建立主要 LargePersonGroupLargeFaceList,此為主要集合。 建立緩衝區 LargePersonGroupLargeFaceList,此為緩衝區集合。 緩衝區集合僅適用於新增加的人員或臉部。
  2. 將新的人員或臉部同時新增至主要集合和緩衝區集合。
  3. 只以短時間間隔來將緩衝區集合定型,以確保新增加的項目會生效。
  4. 同時針對主要集合和緩衝區集合呼叫 Identification 或 FindSimilar。 合併結果。
  5. 在緩衝區集合大小增加達到閾值,或處於系統閒置時間時,建立新的緩衝區集合。 觸發主要集合上的定型作業。
  6. 當主要集合上的定型完成之後,刪除舊的緩衝區集合。

步驟 2c:獨立定型

如果可以接受相對較長的延遲,就不需要在新增資料後立即觸發定型作業。 可以改為將定型作業從主要邏輯中分割出來,以定期方式觸發。 此策略適用於可接受延遲的動態案例。 它可以套用至靜態案例,以進一步降低定型頻率。

假設有一個類似於 TrainLargeFaceListTrainLargePersonGroup 函式。 透過叫用 System.Timers 中的 Timer 類別,在 LargePersonGroup 上進行獨立定型的一般實作如下:

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

如需資料管理和識別相關實作的詳細資訊,請參閱新增臉部

摘要

在本指南中,您已了解如何將現有的 PersonGroupFaceList 程式碼 (非資料) 移轉至 LargePersonGroupLargeFaceList

  • LargePersonGroupLargeFaceList 的運作方式與 PersonGroupFaceList 類似,差別在於 LargeFaceList 需要定型作業。
  • 採用適當的定型策略,以針對大規模資料集進行動態資料更新。

下一步

請遵循操作指南,了解如何將臉部新增至 PersonGroup,或撰寫指令碼以在 PersonGroup 上執行識別作業。