Amazon SageMaker Feature Store、バッチ書き込みとレコード検索機能を強化
本文の状態
日本語全文を表示中
詳細モードで約12分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
AWS Machine Learning Blog
従来の 1 レコードごとの API 呼び出しに代わり、最大 25 件のレコードを複数機能グループ間で 1 回の呼び出しで書き込める機能を追加し、接続オーバーヘッドとスループットのボトルネックを解消する。
AI深層分析を開く2026年8月29日 04:51
AI深層分析
キーポイント
高スループット化のための BatchWriteRecord API の導入
従来の 1 レコードごとの API 呼び出しに代わり、最大 25 件のレコードを複数機能グループ間で 1 回の呼び出しで書き込める機能を追加し、接続オーバーヘッドとスループットのボトルネックを解消する。
オンラインストアの可視化と復旧可能性の向上
In-Memory ストレージ層において、レコード識別子の列挙や検索が可能になる ListRecords API を導入し、バグやパイプライン障害によるデータ喪失リスクを低減する。
既存アーキテクチャの課題解消
機能パイプラインの運用で頻発していた接続オーバーヘッドと、オンラインストアからのデータ消失時の復旧手段欠如という 2 つの主要な運用ギャップを技術的に解決する。
ListRecords API とストレージ階層
ListRecords はページネーションを使用して機能グループ内のレコード識別子を列挙するAPIである。この機能は標準(DynamoDB 依存)およびインメモリ(Redis 依存)の両方のストレージ階層で動作する。
実行に必要な IAM ポリシ
Feature Store のデータプレーン API と相互作用するには、sagemaker:BatchWriteRecord、PutRecord、ListRecords に対するアクセス権限を持つ SageMaker AI 実行ロールが必要である。
重要な引用
teams running high-throughput feature pipelines must call PutRecord (which writes a single feature record to the online store) in a loop. This means one API call per record, per feature group, which creates connection overhead and poor throughput.
If record identifiers are lost through a bug or pipeline failure, those records become permanently unrecoverable.
Write up to 25 records across multiple feature groups in a single API call, with partial-success semantics, per-record time-to-live (TTL) control
The BatchWriteRecord API tackles the throughput limits of single-record ingestion.
編集コメントを表示
編集コメント
機能ストアの運用課題を直接的に解決する API の追加は、大規模な ML システムの信頼性を高める重要な一歩である。特に In-Memory ストレージでのデータ探索機能の実装は、開発者のデバッグ時間を大幅に短縮し、運用リスクを低減する実用的な改善と言える。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
Amazon SageMaker Feature Store は、機械学習 (ML) モデルの機能(特徴量)を保存・共有・管理するために設計されたフルマネージドのリポジトリです。リアルタイム推論のための低遅延オンラインサービングや、履歴データの保持およびトレーニング用データのためのオフラインストアを提供し、ストリーミングとバッチの両方の取り込みパターンをサポートしています。
機械学習プラットフォームが成熟するにつれ、繰り返し現れる運用上の課題が二つあります。まず、高スループットのフィーチャーパイプラインを運用しているチームは、PutRecord(単一のフィーチャーレコードをオンラインストアに書き込む機能)をループ内で呼び出す必要があります。これは、1 件のレコードごとに、かつ各フィーチャーグループごとに 1 回の API 呼び出しが必要になることを意味します。その結果、接続オーバーヘッドが発生し、スループットが低下してしまいます。例えば、5 つのフィーチャーグループにわたって毎秒 10,000 件のレコードを取り込む不正検出パイプラインでは、フィーチャーを最新の状態に保つために、毎秒 50,000 回もの個別の API 呼び出しを維持しなければなりません。
2 つ目の課題は、インメモリストレージ階層を使用しているチームが、オンラインストアに保存されたレコードを検索したり列挙したりする手段を持っていない点です。バグやパイプラインの障害によってレコード識別子が失われた場合、そのレコードは永久に復元不能になります。インメモリ階層にはフォールバック用のオフラインストアが存在せず、Amazon Athena でクエリを実行することも、何が存在するかを確認するための API も用意されていません。
本日、Amazon SageMaker Feature Store 向けに 2 つの新しい API を発表いたします。
Amazon SageMaker Feature Store では、BatchWriteRecord API を使用すると、1 回の呼び出しで最大 25 件のレコードを複数の特徴グループに書き込むことができます。この API は部分成功の semantics(意味)をサポートし、各レコードごとの TTL(有効期限)制御が可能で、PutRecord と同様にイベントタイムベースの順序保証も提供します。
また、ListRecords API を利用すれば、ページネーション機能を使って特徴グループ内のレコード ID を列挙できます。これは、標準ストレージ(Amazon DynamoDB ベース)とインメモリストレージ(Redis ベース)の両方のストレージ層で動作します。
本稿では、これらの API の使い方を具体的なコード例とともに解説し、すぐに実践を開始できるようサポートします。
事前準備
本稿のサンプルを実行するには、以下の環境が必要です。
- Amazon SageMaker AI リソースを作成できる権限を持つ AWS アカウント。
- Amazon Simple Storage Service (Amazon S3) と AWS Glue へのアクセス権、および Feature Store データプレーン API を操作する権限を持つ Amazon SageMaker AI 実行ロール。
以下の AWS Identity and Access Management (IAM) ポリシーは、必要な最小限の権限を示しています:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sagemaker:BatchWriteRecord",
"sagemaker:PutRecord",
"sagemaker:ListRecords"
],
"Resource": "arn:aws:sagemaker:*:*:feature-group/*"
}
]
}・Boto3(最新バージョン)または SageMaker Python SDK v3.8.0 以降が必要です。
・レコードが取り込まれた既存のフィーチャーグループを 1 つ以上用意してください。Feature Store の使い方が初めての場合は、エンドツーエンドのワークショップノートブック をご覧ください。
BatchWriteRecord
BatchWriteRecord API は、単一レコードの取り込みにおけるスループット制限を克服するために設計されています。以下では、この API が解決する課題と動作原理について解説します。
単一レコード取り込みの課題
現在利用可能な PutRecord API では、1 つの呼び出しで 1 つのフィーチャーグループに 1 レコードのみ書き込むことができます。各呼び出しは条件付き書き込みとして機能し、リクエストに含まれる EventTime が既存レコードより新しい場合にのみ、そのレコードが「最新」バージョンとして保存されます。もしこの条件を満たさなくても、レコードはオフラインストア用の履歴バージョンとして書き込まれます。
この設計により強力な順序保証が提供されていますが、大規模運用では N×M の呼び出しパターン(N 個のレコード × M 個のフィーチャーグループ)を強制することになり、接続オーバーヘッドや遅延がボトルネックとなってスループットが制限されてしまいます。
BatchWriteRecord API を使用すると、1 回のリクエストで最大 25 エントリを処理でき、複数の特徴量グループに対して同時に書き込みが可能です。各レコードは独立して成功または失敗します。これは部分成功型の API であり、一部のレコードが失敗してもリクエスト全体が失敗することはありません。
この API は PutRecord と同様に、イベントタイム(EventTime)に基づく順序を維持します。
- 受信したレコードの EventTime が既存のものより新しい場合、そのレコードはオンラインストアで最新バージョンとして扱われます。
- 新しい場合でない場合は、オフラインストレージを持つ特徴量グループにおいて、履歴バージョンとしてオフラインストアに書き込まれます。
- 認証エラーやバリデーションエラー、サービスのスロットリングなどその他の理由で失敗したレコードは、エラー詳細と元のレコードとともにレスポンスに含まれて返されます。
- 処理されなかったリクエストは UnprocessedEntries としてレスポンスに返し、再試行が可能です。
リクエスト構造
{
"Entries": [
{
"FeatureGroupName": "click-features",
"Record": [
{"FeatureName": "user_id", "ValueAsString": "user-123"},
{"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
{"FeatureName": "click_count", "ValueAsString": "42"}
],
"TargetStores": ["OnlineStore", "OfflineStore"],
"TtlDuration": {"Unit": "Days", "Value": 7}
},
{
"FeatureGroupName": "login-features",
"Record": [
{"FeatureName": "user_id", "ValueAsString": "user-456"},
{"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:01Z"},
{"FeatureName": "login_count", "ValueAsString": "18"}
],
"TargetStores": ["OnlineStore", "OfflineStore"]
}
]
}レスポンスでは、失敗したレコードのみが返されます。
{
"Errors": [
{
"Entry": {
"FeatureGroupName": "string",
"Record": [
{
"FeatureName": "string",
"ValueAsString": "string",
"ValueAsStringList": ["string"]
}
],
"TargetStores": ["string"],
"TtlDuration": {
"Unit": "string",
"Value": number
}
},
"ErrorCode": "string",
"ErrorMessage": "string"
}
],
"UnprocessedEntries": [
{
"FeatureGroupName": "string",
"Record": [
{
"FeatureName": "string",
"ValueAsString": "string",
"ValueAsStringList": ["string"]
}
],
"TargetStores": ["string"],
"TtlDuration": {
"Unit": "string",
"Value": number
}
}
]
}エラーリストや未処理エントリに含まれていないレコードは正常に処理されました。再試行可能なエラーが発生した場合は、指数バックオフを用いて失敗したレコードのみを再試行するようにアプリケーションを実装してください。
Boto3 を使用したバッチ取り込みのコード例
import boto3
featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")
response = featurestore_runtime.batch_write_record(
Entries=[
{
"FeatureGroupName": "click-features",
"Record": [
{"FeatureName": "user_id", "ValueAsString": "user-123"},
{"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
{"FeatureName": "click_count", "ValueAsString": "42"},
],
"TargetStores": ["OnlineStore", "OfflineStore"],
},
{
"FeatureGroupName": "login-features",
"Record": [
{"FeatureName": "user_id", "ValueAsString": "user-456"},
{"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:01Z"},
{"FeatureName": "login_count", "ValueAsString": "18"},
],
"TargetStores": ["OnlineStore", "OfflineStore"],
},
]
)
if response["Errors"]:
for error in response["Errors"]:
print(f"Record {error['Entry']}, ErrorCode: {error['ErrorCode']} Failed: {error['ErrorMessage']}")
if response["UnprocessedEntries"]:
for unprocessed in response["UnprocessedEntries"]:
print(f"Unprocessed: {unprocessed['FeatureGroupName']}")
if not response["Errors"] and not response["UnprocessedEntries"]:
print("All records written successfully.")複数のフィーチャーグループへの書き込み
1 つのリクエストで複数のフィーチャーグループを対象にできます。レコードはフィーチャーグループごとにグループ化され、それぞれ独立して処理されます。
featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")
response = featurestore_runtime.batch_write_record(
Entries=[
{
"FeatureGroupName": "user-profile-features",
"Record": [
{"FeatureName": "user_id", "ValueAsString": "user-123"},
{"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
{"FeatureName": "age", "ValueAsString": "34"},
{"FeatureName": "region", "ValueAsString": "us-west-2"},
],
"TargetStores": ["OnlineStore"],
},
{
"FeatureGroupName": "click-features",
"Record": [
{"FeatureName": "user_id", "ValueAsString": "user-123"},
{"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
{"FeatureName": "click_count", "ValueAsString": "42"},
],
"TargetStores": ["OnlineStore", "OfflineStore"],
},
]
)あるフィーチャーグループでの失敗が、他のフィーチャーグループ宛てのレコードに影響することはありません。
TTL(Time-to-Live)サポート
BatchWriteRecord は、以下の優先順位で 3 つのレベルの TTL をサポートしています。
- レコードレベルの TTL — 個別のエントリに TtlDuration を設定します。最も高い優先度を持ちます。
- リクエストレベルの TTL — リクエストの最上位にデフォルトの TtlDuration を設定し、レコードレベルの TTL が指定されていないエントリに適用されます。
- フィーチャーグループレベルの TTL — レコードレベルおよびリクエストレベルの TTL のいずれも設定されていない場合に、フィーチャーグループ自体に設定された TTL が適用されます。
重要な考慮事項
リクエストごとに最大 25 エントリまでと制限されています。この上限は、1 つのリクエスト内で対象となるすべてのフィーチャーグループにまたがるエントリの総数に対して適用されます。
部分的な成功の扱い:トランザクショナル API と異なり、BatchWriteRecord は一部のレコードが失敗した場合でも、すでに書き込まれた成功したレコードをロールバックしません。エラーとして返されたレコードのみを再送信するリトライロジックを設計してください。
PutRecord と同様の IAM モデル:呼び出し元は、各ターゲットとなるフィーチャーグループの Amazon Resource Name (ARN) に対して、sagemaker:BatchWriteRecord および sagemaker:PutRecord の権限を持っている必要があります。処理前にフィーチャーグループごとの認証チェックが行われます。
EventTime の順序保持:BatchWriteRecord は条件付き書き込みを使用し、PutRecord と同じ「最新レコードが優先される」セマンティクスを維持します。古いレコードがオンラインストア内の新しいレコードを上書きすることはできません。
TargetStores の柔軟性:各エントリは、OnlineStore へ、OfflineStore へ、あるいはその両方へ独立してターゲットを設定できます(デフォルトはフィーチャーグループで有効になっているストア)。これにより、各レコードがどこに格納されるかを細かく制御できます。
ListRecords
ListRecords API は、2 つのストレージティアにおけるレコードの発見を可能にし、そのギャップを埋めます。以下のセクションでは、この API が解決する課題と動作について説明します。
レコード発見の課題
Feature Store では、PutRecord、GetRecord、DeleteRecord がサポートされていますが、いずれも呼び出し元がレコード識別子を正確に知っている必要があります。特徴グループ内のレコードを閲覧したり列挙したりするための API は用意されていません。
Standard タイプの場合の回避策は、Amazon Athena を使用してオフラインストアを検索することです。ただし、これにはオフラインストアの設定が必要で、コストが発生し、リアルタイム性もありません。
In-Memory タイプの場合は状況が深刻です。デフォルトでは対応するオフラインストアが存在しません。レコード識別子を失った場合、そのレコードは完全に復元できなくなります。発見することも削除することもできません。このため、存在しないはずのデータ(ファントムデータ)が残存し、ストレージコストが無駄になるほか、データ主体から削除を求められた際にコンプライアンス上のリスクが生じます。
How ListRecords works
ListRecords API を使用すると、ページネーション機能を使って特徴量グループ内のレコード ID を列挙できます。この API は、GetRecord や DeleteRecord で利用可能な、アクティブで削除されていない期限切れでもないレコードのみを返します。
この API は、2 つのストレージティアに対応しています。
- Standard tier (Amazon DynamoDB): オンラインストアを検索し、各レコードの最新バージョンの ID を返します。ソフト削除されたレコードや期限切れのレコードは自動的に除外されます。
- In-Memory tier (Redis): キーを検索し、ソフト削除されたレコードと内部システムキーをフィルタリングして除外します。キー名から抽出したレコード ID を返します。
リクエストとレスポンスの構造
POST /FeatureGroup/{FeatureGroupName}/ListRecordsリクエストボディ:
初回呼び出し
{
"MaxResults": 50
}または
{
"MaxResults": 50,
"NextToken": "eyJjdXJzb3IiOi4uLn0="
}レスポンス:
{
"RecordIdentifiers": [
"user-001",
"user-002",
"user-003"
],
"NextToken": "eyJuZXh0IjoiLi4ufQ=="
}レスポンスに NextToken が含まれていない場合、ページネーションは完了しています。
コード例:特徴量グループ内の全レコードを列挙する
import boto3
featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")
all_identifiers = []
next_token = None
while True:
params = {
"FeatureGroupName": "user-profile-features",
"MaxResults": 100,
}
if next_token:
params["NextToken"] = next_token
response = featurestore_runtime.list_records(**params)
all_identifiers.extend(response["RecordIdentifiers"])
next_token = response.get("NextToken")
if not next_token:
break
print(f"Found {len(all_identifiers)} active records.")コード例:孤立したレコードのクリーンアップ
不要になったレコードを特定して削除するのは、よくあるユースケースです。これは In-Memory tier の特徴量グループにおいて特に重要で、ここでは孤立したレコードが永遠に残り続ける可能性があるためです:
import boto3
featu
関連記事
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み