擴大以處理更多已註冊的使用者
警告
臉部辨識服務存取受限於資格和使用準則,以支援我們的「負責任的 AI 原則」。 臉部辨識服務僅供 Microsoft 受管理的客戶和合作夥伴使用。 請使用臉部辨識受理表單以申請存取。 如需詳細資訊,請參閱臉部的有限存取權頁面。
本指南說明如何分別從現有的 PersonGroup 和 FaceList 物件擴大至 LargePersonGroup 和 LargeFaceList 物件。 PersonGroup 在免費層最多可以保留 1000 個人員,在付費層可以保留 10,000 個人員,而 LargePersonGroup 在付費層最多可以保留一百萬個人員。
重要
建議使用較新的資料結構 PersonDirectory 進行新的開發。 它最多可以保存 7500 萬個身分識別,而且不需要手動訓練。 如需詳細資訊,請參閱 PersonDirectory 指南 (部分機器翻譯)。
本指南示範移轉流程。 它假設您對於 PersonGroup 和 FaceList 物件、定型作業和臉部辨識功能已有基本了解。 若要深入了解這些物件,請參閱臉部辨識概念指南。
LargePersonGroup 和 LargeFaceList 統稱為大規模作業。 LargePersonGroup 最多可包含 1 百萬個人員,每個人員最多 248 張臉。 LargeFaceList 最多可包含 1 百萬張臉。 大規模作業與傳統的 PersonGroup 和 FaceList 類似,但由於採用新結構,因此有一些差異。
範例是以 C# 撰寫。
注意
若要大規模針對 Identification 和 FindSimilar 啟用臉部搜尋效能,請導入定型作業以預先處理 LargeFaceList 和 LargePersonGroup。 根據實際容量而定,定型時間從數秒到大約半小時不等。 如果先前已成功進行定型作業,則在定型期間可以執行 Identification 和 FindSimilar。 缺點則是在完成新的大規模定型後移轉之前,新增的人員和臉部不會出現在結果中。
步驟 1:程式碼移轉
本節著重於如何將 PersonGroup 或 FaceList 實作移轉至 LargePersonGroup 或 LargeFaceList。 雖然 LargePersonGroup 或 LargeFaceList 與 PersonGroup 或 FaceList 在設計及內部實作上有所差異,但 API 介面類似,均支援回溯相容性。
不支援資料移轉。 您可以改為重新建立 LargePersonGroup 或 LargeFaceList。
將 PersonGroup 移轉至 LargePersonGroup
從 PersonGroup 移轉至 LargePersonGroup 很簡單。 它們會共用完全相同的群組層級作業。
針對 PersonGroup 或人員相關的實作,只需將 API 路徑或 SDK 類別/模組變更為 LargePersonGroup 和 LargePersonGroup Person 即可。
將 PersonGroup 中所有臉部和人員新增至新的 LargePersonGroup。 如需詳細資訊,請參閱新增臉部。
將 FaceList 移轉至 LargeFaceList
FaceList API | LargeFaceList API |
---|---|
建立 | 建立 |
刪除 | 刪除 |
Get | Get |
清單 | 清單 |
更新 | 更新 |
- | 定型 |
- | 取得定型訓練 |
上表是 FaceList 與 LargeFaceList 之間的清單層級作業比較。 如上所示,相較於 FaceList,LargeFaceList 附帶了定型和取得定型狀態這兩項新作業。 將 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:定型建議
雖然定型作業可加快 FindSimilar 和 Identification 的速度,但定型時間會受到影響,尤其是在大規模進行時。 下表列出針對各種不同規模預估的定型時間。
臉部或人員的規模 | 預估的定型時間 |
---|---|
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:小規模緩衝區
LargePersonGroup 或 LargeFaceList 中的人員或臉部只有在定型後才可供搜尋。 在動態案例中,新的人員或臉部會持續新增且必須立即可供搜尋,而定型所花費的時間可能超出預期。
為了緩解此問題,請只針對剛新增的項目使用額外的小規模 LargePersonGroup 或 LargeFaceList 作為緩衝區。 由於大小較小,因此,此緩衝區會採用較短的時間來定型。 此暫存緩衝區上的即時搜尋功能應會運作。 透過以更疏鬆的間隔來執行主要定型,將此緩衝區與主要 LargePersonGroup 或 LargeFaceList 上的定型一起使用。 範例包括午夜和每日。
範例工作流程:
- 建立主要 LargePersonGroup 或 LargeFaceList,此為主要集合。 建立緩衝區 LargePersonGroup 或 LargeFaceList,此為緩衝區集合。 緩衝區集合僅適用於新增加的人員或臉部。
- 將新的人員或臉部同時新增至主要集合和緩衝區集合。
- 只以短時間間隔來將緩衝區集合定型,以確保新增加的項目會生效。
- 同時針對主要集合和緩衝區集合呼叫 Identification 或 FindSimilar。 合併結果。
- 在緩衝區集合大小增加達到閾值,或處於系統閒置時間時,建立新的緩衝區集合。 觸發主要集合上的定型作業。
- 當主要集合上的定型完成之後,刪除舊的緩衝區集合。
步驟 2c:獨立定型
如果可以接受相對較長的延遲,就不需要在新增資料後立即觸發定型作業。 可以改為將定型作業從主要邏輯中分割出來,以定期方式觸發。 此策略適用於可接受延遲的動態案例。 它可以套用至靜態案例,以進一步降低定型頻率。
假設有一個類似於 TrainLargeFaceList
的 TrainLargePersonGroup
函式。 透過叫用 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();
}
如需資料管理和識別相關實作的詳細資訊,請參閱新增臉部。
摘要
在本指南中,您已了解如何將現有的 PersonGroup 或 FaceList 程式碼 (非資料) 移轉至 LargePersonGroup 或 LargeFaceList:
- LargePersonGroup 和 LargeFaceList 的運作方式與 PersonGroup 或 FaceList 類似,差別在於 LargeFaceList 需要定型作業。
- 採用適當的定型策略,以針對大規模資料集進行動態資料更新。
下一步
請遵循操作指南,了解如何將臉部新增至 PersonGroup,或撰寫指令碼以在 PersonGroup 上執行識別作業。