より多くの登録済みユーザーを処理するようにスケーリングする
注意事項
Microsoft の責任ある AI の原則をサポートするために、Face サービスの利用は、適格性と使用基準に基づいて制限されています。 Face サービスは、Microsoft が管理する顧客とパートナーのみが利用できます。 顔認識受付フォームを使用して利用申請を行ってください。 詳細については、「Face の制限付きアクセス」ページを参照してください。
このガイドでは既存の PersonGroup および FaceList オブジェクトから、それぞれ LargePersonGroup および LargeFaceList オブジェクトにスケールアップする方法について説明します。 PersonGroups は Free レベルで最大 1000 人、有料レベルでは最大 10,000 人を保持でき、LargePersonGroups は有料レベルで最大 100 万人を保持できます。
重要
新しい開発には、新しいデータ構造 である PersonDirectory をお勧めします。 最大 7,500 万個の ID を保持でき、手動トレーニングは必要ありません。 詳細については、「PersonDirectory のガイド」を参照してください。
このガイドではこの移行プロセスについて説明します。 ここでは、PersonGroup オブジェクトと FaceList オブジェクト、トレーニング操作、および顔認識機能に関する基本的な知識があることを前提としています。 これらの内容の詳細については、顔認識に関する概念ガイドを参照してください。
LargePersonGroup と LargeFaceList は、まとめて大規模操作と呼びます。 LargePersonGroup には、最大 100 万人、その 1 人あたり最大 248 個の顔を保持できます。 LargeFaceList には、最大 100 万個の顔を保持できます。 大規模操作は、従来の PersonGroup と FaceList に似ていますが、新しいアーキテクチャなのでいくつかの違いがあります。
サンプルは C# で記述されています。
Note
Identification と FindSimilar に対して大規模な顔検索パフォーマンスを有効にするには、トレーニング操作を導入して LargeFaceList と LargePersonGroup を事前に処理します。 トレーニング時間は実際の容量に応じて変わり、数秒から約 30 分かかります。 以前にトレーニング操作に成功している場合、トレーニング期間中に Identification と FindSimilar を実行できます。 欠点は、大規模なトレーニングへの新しい移行後処理が完了するまで、新しく追加された人物や顔が結果に表示されないことです。
手順 1: コードを移行する
このセクションでは、PersonGroup または FaceList の実装を LargePersonGroup または LargeFaceList に移行する方法に焦点を当てています。 LargePersonGroup または LargeFaceList は、設計と内部実装の点で PersonGroup または FaceList とは異なりますが、API インターフェイスは下位互換性のために似ています。
データ移行はサポートされていません。 代わりに LargePersonGroup または LargeFaceList を再作成します。
PersonGroup を LargePersonGroup に移行する
PersonGroup から LargePersonGroup への移行は簡単です。 これらはまったく同じグループ レベルの操作を共有します。
PersonGroup または Person 関連の実装では、API パスまたは SDK クラス/モジュールを LargePersonGroup と LargePersonGroup Person に変更するだけで済みます。
すべての顔と人を PersonGroup から新しい LargePersonGroup に追加します。 詳細については、「顔を追加する方法」を参照してください。
FaceList を LargeFaceList に移行する
FaceList API | LargeFaceList API |
---|---|
作成 | 作成 |
削除 | 削除 |
取得 | 取得 |
List | List |
更新 | 更新 |
- | トレーニング |
- | トレーニング状態の取得 |
上の表は、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 部分はほぼ同じです。 唯一の例外は、FindSimilar が動作する前に、LargeFaceList で新しい事前処理のトレーニング操作が完了する必要があることです。
手順 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 にも適用されます。 たとえば、100 万人の人物が含まれる LargePersonGroup をトレーニングする場合、timeIntervalInMilliseconds
は 60,000 (1 分間隔) になります。
手順 2b: 小規模なバッファー
LargePersonGroup または LargeFaceList の Persons または Faces は、トレーニングが完了した後にのみ検索できます。 動的なシナリオでは、新しい人または顔が絶えず追加され、すぐに検索できるようにする必要がありますが、望ましい時間よりもトレーニング時間が長くなる可能性があります。
この問題を軽減するには、新しく追加されたエントリに対してのみ、追加の小規模な 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 は、LargeFaceList にトレーニング操作が必要であることを除けば、PersonGroup または FaceList と同様に機能します。
- 大規模なデータセットの動的なデータ更新には、適切なトレーニング戦略を実行してください。
次のステップ
ハウツー ガイドに従って、PersonGroup に顔を追加する方法、または PersonGroup で識別操作を実行するスクリプトを作成する方法を学習します。