Amazon Bedrock のバッチ推論を試してみた

# 背景・目的

自分が関わる業務のユースケースから、Bedrockのバッチ推論のほうが適していると感じたため
簡単に特徴を整理し、試します。

# まとめ
下記に特徴を整理します。

|特徴|説明|
|:--|:--|
|バッチ推論|複数のプロントを送信し、非同期で応答を生成できる<BR><BR>バッチ推論を使用すると、大規模なデータセットでのモデル推論のパフォーマンスを向上させることが可能|
|ユースケース|単一のリクエストを送信し、S3バケットに応答を生成することで、大量のリクエストを効率的に処理するのに役立つ|
|流れ|1. 作成したファイルでモデル入力を定義し、そのファイルをS3にアップロードする<BR>2. バッチ推論リクエストを送信し、S3バケットを指定する<BR>3. ジョブが完了したらS3から出力ファイルを取得できる|
|料金|バッチ推論は、オンデマンド推論の50%の料金で使用できる|
|入力ファイル|jsonlフォーマット。<BR>recordIdとmodelInputのフィールドを持つ|

# 概要
下記を基に整理します。

https://docs.aws.amazon.com/bedrock/latest/userguide/batch-inference.html

> バッチ推論を使用すると、複数のプロンプトを送信し、非同期で応答を生成できます。バッチ推論は、単一のリクエストを送信し、Amazon S3 バケットに応答を生成することで、大量のリクエストを効率的に処理するのに役立ちます。作成したファイルでモデル入力を定義したら、そのファイルを S3 バケットにアップロードします。次に、バッチ推論リクエストを送信し、S3 バケットを指定します。ジョブが完了したら、S3 から出力ファイルを取得できます。バッチ推論を使用すると、大規模なデータセットでのモデル推論のパフォーマンスを向上させることができます。

- 複数のプロントを送信し、非同期で応答を生成できる
- バッチ推論は、単一のリクエストを送信し、S3バケットに応答を生成することで、大量のリクエストを効率的に処理するのに役立つ
- バッチ推論を使用すると、大規模なデータセットでのモデル推論のパフォーマンスを向上させることが可能
- 簡単な流れ
    1. 作成したファイルでモデル入力を定義し、そのファイルをS3にアップロードする
    1. バッチ推論リクエストを送信し、S3バケットを指定する
    1. ジョブが完了したらS3から出力ファイルを取得できる

## 料金
下記を基に整理します。
https://aws.amazon.com/jp/bedrock/pricing/

### オンデマンドとバッチ
> オンデマンドモードでは、使用した分のみのお支払いとなり、時間ベースの契約はありません。テキスト生成モデルでは、入力トークンの処理および出力トークンの生成ごとに課金されます。埋め込みモデルでは、入力トークンの処理ごとに課金されます。トークンは数文字で構成されており、ユーザー入力と、結果を生成するためのプロンプトを理解するためにモデルが学習する基本単位です。画像生成モデルでは、生成されたすべての画像について課金されます。クロスリージョン推論: オンデマンドモードは、一部のモデルのクロスリージョン推論もサポートしています。これにより、デベロッパーはさまざまな AWS リージョンでコンピューティングを活用してトラフィックバーストをシームレスに管理し、より高いスループット制限と強化された回復力を活用できます。クロスリージョン推論の使用には追加料金は発生せず、料金はリクエストを実行したリージョン (ソースリージョン) に基づいて計算されます。


- オンデマンド
    - 使用した分の支払い
    - テキスト生成では、入力トークンの処理、出力トークンの生成ごとに課金
    - 埋め込みモデルでは、入力トークンの処理ごと
    - 画像生成でモルでは、生成されたすべての画像について課金
    - クロスリージョン推論
        - 一部のクロスリージョン推論もサポートしている
        - 様々なリージョンでコンピューティングを活用してトラフィックバーストをシームレスに管理し、より高いスループット制限と強化された回復力を活用できる
        - 追加料金はかからない。実行したリージョンに基づいて計算される


> バッチモードでは、一連のプロンプトを単一の入力ファイルとして提供し、応答を単一の出力ファイルとして受け取ることができるため、大規模な予測を同時に取得できます。レスポンスは処理され、Amazon S3 バケットに保存されるため、後でアクセスできます。Amazon Bedrock は、Anthropic、Meta、Mistral AI、Amazon などの主要な AI プロバイダーから提供される厳選した基盤モデル (FM) を、オンデマンド推論料金と比較して 50% 低い料金でバッチ推論のために提供しています。こちらのモデルリストをご覧ください。

- 大規模な予測を同時に取得できる
- レスポンスは処理され、Amazon S3 バケットに保存されるため、後でアクセスでる
- 主要なAIプロバイダーから提供されるFMをオンデマンド推論と比較して50%低い料金でバッチ推論のために提供している

## バッチ推論の入力ファイル
下記を基に整理します。

https://docs.aws.amazon.com/bedrock/latest/userguide/batch-inference-data.html

下記の形式の.jsonl（JSONLine）を作成する

```json
{ "recordId" : "11 character alphanumeric string", "modelInput" : {JSON body} }
...
```

- 各行には、下記が必要
    - recordId
    - modelInput：送信する入力のリクエスト本文を含むフィールドを含むJSONオブジェクト


### Anthropic Claude 3 Haikuのバッチ推論の例
```json
{
    "recordId": "CALL0000001", 
    "modelInput": {
        "anthropic_version": "bedrock-2023-05-31", 
        "max_tokens": 1024,
        "messages": [ 
            { 
                "role": "user", 
                "content": [
                    {
                        "type": "text", 
                        "text": "Summarize the following call transcript: ..." 
                    } 
                ]
            }
        ]
    }
}
```

# 実践
## 前提

### モデルのリクエスト
事前にモデルが利用できるようにします。

1. Bedrockに移動します
1. ナビゲーションペインの「モデルアクセス」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/299d9b0b-6d2b-7c30-66a2-0baf400cd754.png)

1. 「特定のモデルを有効にする」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6e9dc2e9-4b5a-2af9-be33-979648268df6.png)

1. EULAを読みます
1. 対象のモデルにチェックを付けて、「次へ」をクリックします（今回は、Claude 3.5 Sonnet）を利用します
1. ユースケースの詳細を入力し、「次へ」をクリックします
1. 最後に、「送信」をクリックします
1. ステータスが、「進行中」になりますが、数分以内に「アクセスが付与されました」に変わります
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/4ec6367c-2c41-d372-5b6d-64871cf53e56.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/67ba00b0-b04c-c94b-583d-25868280ecbb.png)


### バッチ推論に必要な権限
下記を元に試します。

https://docs.aws.amazon.com/bedrock/latest/userguide/batch-inference-prereq.html

1. IAMに移動します
1. ナビゲーションペインで「ロール」をクリックします
1. 「ロールの作成」をクリックします
1. 信頼されたエンティティでは、「作成カスタム信頼ポリシー」を選択します
1. 下記の信頼ポリシーには下記を指定し、「次へ」をクリックします
    ```json
    {
    	"Version": "2012-10-17",
    	"Statement": [
    		{
    			"Sid": "Statement1",
    			"Effect": "Allow",
    			"Principal": {
    			    "Service": "bedrock.amazonaws.com"
    			},
    			"Action": "sts:AssumeRole"
    		}
    	]
    }
    ```
1. 許可を追加で「AmazonBedrockFullAccess」をアタッチします
1. ロール名を指定して、「作成」をクリックします



### Format and upload your batch inference data
1. S3バケットを作成します
1. [こちら](https://qiita.com/drafts#anthropic-claude-3-haiku%E3%81%AE%E3%83%90%E3%83%83%E3%83%81%E6%8E%A8%E8%AB%96%E3%81%AE%E4%BE%8B)のファイルをアップロードします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/dea3c255-55a8-21e2-7773-acd8d2eef8fc.png)

## Create a batch inference job
下記を元に試します。

https://docs.aws.amazon.com/bedrock/latest/userguide/batch-inference-create.html

1. Bedrockに移動します
1. ナビゲーションペインで、「Batch inference」をクリックします
1. 「Create job」をクリックします
1. 下記を入力し、「Create batch inference job」をクリックします
    - job name : 任意
    - Model : 上記でモデルリクエストしたもの（Claude 3.5 Sonnetを使用）
    - Input data : 上記で作成したinputファイルのS3パス
    - Output data : 出力先のS3パス
    - Service role：上記で作成したIAMロールを指定
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5bfc410f-f9f1-2037-d0f3-75fe3fb48303.png" width=500 />

### ステータスの確認
1. 一覧で確認できます。作成と同時に実行されたようです
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/97f57ebe-5084-1cd2-1b14-d94c9c37ac5d.png)

1. しばらくすると「Failed」になりました
1. job nameをクリックします
1. エラーメッセージが表示されていました。S3バケットへの権限をつけ忘れていました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/875526d7-fa37-a416-c2c7-c17a93ce9bbf.png)


### 修正
#### 権限の付与
1. IAMに移動します

##### IAMポリシーの作成
1. ナビゲーションペインで「ポリシー」をクリックします
1. 「ポリシーを作成」をクリックします
1. JSONに切り替え、下記のポリシーを貼り付けます
    ```json
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": [
                    "s3:GetObject",
                    "s3:PutObject",
                    "s3:ListBucket"
                ],
                "Resource": [
                    "arn:aws:s3:::${{s3-bucket-input}}", 
                    "arn:aws:s3:::${{s3-bucket-input}}/*",
                    "arn:aws:s3:::${{s3-bucket-output}}",
                    "arn:aws:s3:::${{s3-bucket-output}}/*"
                ],
                "Effect": "Allow"
            }
        ]
    }
    ```
1. ポリシー名を入力し、「ポリシーの作成」をクリックします

##### IAMロールへアタッチ
1. 上記で作成した、IAMポリシーをBedrockで実行するIAMロールにアタッチします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/70f81543-9773-ea57-804b-2f17b2080175.png)

#### ファイルフォーマット
1. そういえば、jsonlになっていませんでした
    ```json
    % cat test.jsonl 
    {
        "recordId": "CALL0000001", 
        "modelInput": {
            "anthropic_version": "bedrock-2023-05-31", 
            "max_tokens": 1024,
            "messages": [ 
                { 
                    "role": "user", 
                    "content": [
                        {
                            "type": "text", 
                            "text": "Summarize the following call transcript: ..." 
                        } 
                    ]
                }
            ]
        }
    }
    % 
    ```
1. 下記のように修正し、再アップします
    ```json
    % cat test.jsonl 
    {"recordId": "CALL0000001","modelInput": {"anthropic_version": "bedrock-2023-05-31","max_tokens": 1024,"messages": [{ "role": "user", "content": [{"type": "text", "text": "Summarize the following call transcript: ..." } ]}]}}
    % 
    ```

### 再実行
1. Bedrock inferenceのBatch Inferenceに移動します
1. ダッシュボードを見ると、リランができないようだ
1. あらためて、「Create job」をクリックし、再度同じ内容を入力します
1. December 30, 2024, 15:42にSubmittedになりました（エビデンスな取り忘れました）
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/803f2abb-1d7a-1ed0-40c6-9c2574638a48.png)
1. しばらくすると、Validatingに変わりました。結構長い
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/658e5ed4-d1ae-8611-0c4d-042cd19c9243.png)

1. 5分くらいで、Failedになりました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/8210f4d3-bf14-8ef0-d8e6-b58fd534f65c.png)

1. どうやら、100件は必要なようです
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/c05c0651-082e-6afa-5248-17e7e3275d30.png)


### 再々実行
1. 100件のファイルを作成しました
    ```bash
    % wc -l test2.jsonl
         100 test2.jsonl
    % 
    ```
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/304264d8-5d11-b23a-8097-97a161e5d2dd.png)

1. 実行します。（Submitted）になりました。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/8d62f32f-25c4-6db7-5f6a-81150e1e4767.png)

1. しばらくすると、「Validating」になりました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/a70e86d9-be78-b9b1-237b-67906d0ae51e.png)

1. 「Scheduled」になりました。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/42d4d6f5-deac-1199-192a-9d87c20bc8ef.png)

1. 全然終わらないので、止めます。ジョブを選択し、「Stop job」をクリックします
1. 「Stopping」>「Stopped」になりました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/59a9de42-4112-96ab-37cc-64fef07d1431.png)


### ファイルの中身を入れ替えて再実行
1. ファイルの中身を変えてみます
    ```json
    % tail -1 test3.jsonl 
    {"recordId":"CALL100","modelInput": {"anthropic_version": "bedrock-2023-05-31","max_tokens": 100,"messages": [{ "role": "user", "content": [{"type": "text", "text": "あなたは誰ですか？" } ]}]}}
    % 
    ```

1. 「In progress」になりました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/d85a3d10-fc05-ce67-c017-467281a09264.png)

1. 「Complated」になりました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/51836382-5694-6046-6a53-78261bfcef00.png)

1. Output locationを確認します
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/74ba58bc-6b99-b8c4-8c47-1223960531b4.png)

1. 結果を確認します。それぽく確認できました
    ```json
    % tail -3 test3.jsonl.out
    {"modelInput":{"anthropic_version":"bedrock-2023-05-31","max_tokens":100,"messages":[{"role":"user","content":[{"type":"text","text":"あなたは誰ですか？"}]}]},"modelOutput":{"id":"msg_bdrk_017TSZASMdKgGMgjKT6Jhxhw","type":"message","role":"assistant","model":"claude-3-5-sonnet-20241022","content":[{"type":"text","text":"私はAnthropicが作ったAIアシスタントのClaudeです。誠実に、倫理的にお手伝いさせていただきます。"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":17,"output_tokens":49}},"recordId":"CALL098"}
    {"modelInput":{"anthropic_version":"bedrock-2023-05-31","max_tokens":100,"messages":[{"role":"user","content":[{"type":"text","text":"あなたは誰ですか？"}]}]},"modelOutput":{"id":"msg_bdrk_01WiiDjTXgv5rWYpbbuBP2Ty","type":"message","role":"assistant","model":"claude-3-5-sonnet-20241022","content":[{"type":"text","text":"私はClaudeです。人工知能アシスタントとして、倫理的で誠実な対話を心がけています。Anthropicによって作られました。"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":17,"output_tokens":49}},"recordId":"CALL099"}
    {"modelInput":{"anthropic_version":"bedrock-2023-05-31","max_tokens":100,"messages":[{"role":"user","content":[{"type":"text","text":"あなたは誰ですか？"}]}]},"modelOutput":{"id":"msg_bdrk_01E433ZN9YRjehEMNQuBZdAp","type":"message","role":"assistant","model":"claude-3-5-sonnet-20241022","content":[{"type":"text","text":"私はAnthropicが作ったAIアシスタントのClaudeです。人々との対話を通じて様々なタスクをサポートすることを目的としています。"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":17,"output_tokens":54}},"recordId":"CALL100"}
    % 
    ```

1. manifestを確認します。レコード数と成功数、トークン数がわかります
    ```json
    % cat manifest.json.out 
    {"totalRecordCount":100,"processedRecordCount":100,"successRecordCount":100,"errorRecordCount":0,"inputTokenCount":1700,"outputTokenCount":5216}
    % 
    ```

# 考察
一時的にジョブが「Scheduled」のまま進行せず、その挙動が不明でしたが、バッチ処理に組み込むのであれば、価格も安価で適しているのではないかと感じました。ただし、マネジメントコンソールはリランができないなど、使い勝手はあまりよろしくなく改善してほしいと思いました。次回は、プログラムからの実行を試みたいと考えています。


# 参考
https://docs.aws.amazon.com/bedrock/latest/userguide/batch-inference.html

https://aws.amazon.com/jp/bedrock/pricing/

https://docs.aws.amazon.com/bedrock/latest/userguide/batch-inference-data.html