めちゃ簡単！AWSのBedrockでLlamaをファインチューニングしてみた

# えっ、ファインチューニング？ 超難しそう！

ですよね。でも、Amazon Bedrockなら結構簡単にできちゃいます。

https://aws.amazon.com/jp/blogs/news/customize-models-in-amazon-bedrock-with-your-own-data-using-fine-tuning-and-continued-pre-training/

### Amazon Bedrockって何？

ちょっと前に紹介資料を作ったのでご覧ください！

https://speakerdeck.com/minorun365/jin-sarawen-kenai-awsnosheng-cheng-aisabisu-amazon-bedrockru-men



# 準備したもの

### 学習元データ

最新のAWSブログから、Bedrockに関するものを3件ピックアップ

https://aws.amazon.com/jp/blogs/aws/build-rag-applications-with-mongodb-atlas-now-available-in-knowledge-bases-for-amazon-bedrock/

https://aws.amazon.com/jp/blogs/aws/amazon-titan-text-v2-now-available-in-amazon-bedrock-optimized-for-improving-rag/

https://aws.amazon.com/jp/blogs/aws/run-scalable-enterprise-grade-generative-ai-workloads-with-cohere-r-r-now-available-in-amazon-bedrock/


### Bedrockのファインチューニングお作法

データセットの形式

https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-prepare.html

クォータ上限値

https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html#quotas-model-customization


# いざ、ファインチューニング！

### 学習用データを作る

上記のBedrockマニュアルと、学習対象のブログ記事をPDFや画像としてClaudeにぶっ込んで、トレーニングにそのまま使えるJSONL形式にしてもらいました。

![スクリーン ショット 2024-05-05 に 11.27.19 午前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/f953c7c5-1eb3-bcae-335e-7178110db4a0.png)

### S3にJSONL形式で置く

![スクリーン ショット 2024-05-05 に 11.34.35 午前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/c4a11892-8bbc-0827-c91f-ab47140995e0.png)

### Bedrockで微調整ジョブを作成する

マネコンぽちぽちで簡単です。今回は `Llama 2 70B` をソースモデルにしました。

![スクリーン ショット 2024-05-05 に 11.38.06 午前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/57fc19ae-3757-7882-cbc9-9b45b9260280.png)

あとはジョブが完了するまで待つだけです。1時間かからないぐらいだったと思います。

![スクリーン ショット 2024-05-05 に 11.39.27 午前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/146234f6-c800-ded5-7ab6-c94277e5e899.png)

トレーニングが終わると、カスタムモデルが利用可能になります。ただし、これを使うにはプロビジョンドスループットの購入が必要です！（最低でも1時間分の課金が必要。3,000円ぐらいかかりますね）

![スクリーン ショット 2024-05-05 に 13.21.18 午後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/a2b79e3c-0f42-a610-dac0-38b47cb70960.png)

参考。タイムリーな記事です👇

https://dev.classmethod.jp/articles/bedrock-pt-min-unit/

プロビジョンドスループットを購入すると、プレイグラウンドからも利用可能となります！

![スクリーン ショット 2024-05-05 に 13.06.41 午後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/7dbc9b35-a1de-9d51-2f85-06043b9ab80b.png)



### 比較対象（RAG）も準備

同じブログPDF 3件をS3に入れて、ナレッジベースも作成しておきます。

![スクリーン ショット 2024-05-05 に 11.44.17 午前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/dedac539-0176-3ac8-4d28-041d7f60419f.png)

なお、ナレッジベースをLlama 2から呼び出すコードは、 @papasim824 さまの以下記事を参考させていただきました。ありがとうございます🙏

https://qiita.com/papasim824/items/70a144d7b4842dae47f4

<details><summary>コード</summary>

```python
import streamlit as st
import boto3
import json


def main():
    """メイン関数はStreamlitアプリでのチャットのやり取りを処理します。

    この関数はチャットインターフェースを初期化し、ユーザー入力を処理し、AIモデルからのレスポンスを表示します。
    """
    # セッション状態にチャットメッセージがない場合は初期化
    if "messages" not in st.session_state:
        st.session_state.messages = []

    # 既存のチャットメッセージを表示
    if st.session_state.messages:
        for info in st.session_state.messages:
            with st.chat_message(info["role"]):
                st.write(info["content"])

    # 新しいユーザー入力を処理
    if prompt := st.chat_input(""):
        # ユーザーメッセージをセッション状態に追加
        st.session_state.messages.append({"role": "Human", "content": prompt})
        # ユーザーのメッセージを表示
        with st.chat_message("Human"):
            st.write(prompt)

        # AIのレスポンスを準備して表示
        with st.chat_message("Assistant"):
            message_placeholder = st.empty()  # 動的更新のためのプレースホルダー
            with st.spinner("検索中..."):
                response = invoke_model(prompt)

            full_response = ""  # レスポンスを累積するための変数を初期化
            if stream := response.get("body"):
                for event in stream:
                    if chunk := event.get("chunk"):
                        # 各チャンクのレスポンスをデコードして処理
                        full_response += json.loads(chunk.get("bytes").decode("utf-8"))[
                            "generation"
                        ]
                        # 部分レスポンスでプレースホルダーを更新
                        message_placeholder.markdown(full_response + "▌")

                # 完全なレスポンスを最終的に表示
                message_placeholder.write(full_response)

        # AIのレスポンスをセッション状態に追加
        st.session_state.messages.append(
            {"role": "Assistant", "content": full_response}
        )


def invoke_model(prompt):
    """AIモデルを呼び出して、ユーザーのプロンプトと既存の会話コンテキストに基づいたレスポンスを生成します。

    引数:
        prompt: ユーザーの入力プロンプト。
        message_placeholder: AIのレスポンスを動的に表示するためのStreamlitプレースホルダー。

    戻り値:
        AIモデルが生成したレスポンス。
    """
    # Bedrock runtime用のAWS Boto3クライアントを初期化
    retrieve_bedrock = boto3.client(
        service_name="bedrock-agent-runtime", region_name="us-east-1"
    )
    bedrock = boto3.client(service_name="bedrock-runtime", region_name="us-east-1")

    # 知識ベースから情報を取得
    docs_info = retrieve_bedrock.retrieve(
        knowledgeBaseId="XXXXXXXXXX", #ナレッジベースID
        retrievalQuery={"text": prompt},
    )

    # モデルプロンプト用のメッセージをコンパイル
    messages = [
        f'{message["role"]}:{message["content"]}'
        for message in st.session_state.messages
    ]

    # モデル呼び出し用のボディをフォーマット
    body = json.dumps(
        {
            "prompt": "\n\n".join(messages)
            + "\n\n"
            + "info:"
            + json.dumps(docs_info["retrievalResults"])
            + "Assistant:",
            #            "max_tokens_to_sample": 1000,
        }
    )

    # モデルを呼び出し、レスポンスストリームを処理
    response = bedrock.invoke_model_with_response_stream(
        modelId="meta.llama2-70b-chat-v1", body=body
    )

    return response


if __name__ == "__main__":
    main()

```

</details>


# さぁ評価。ファインチューニング vs RAG！

Llama 2は日本語ネイティブなモデルではないため、英語で対話を行い、結果を翻訳したものを以下で紹介します。

### 質問① AWSのBedrockで利用可能なベクトルDBは？

```

 Answer me in around 100 words. 

Human: Tell me vector stores available in Knowledge Bases for Amazon Bedrock.
Assistant:
```

回答（ファインチューニング版 Llama 2）

> ウェブ上でこれを見つけました。Amazon Bedrockはベクターストアで、顧客に様々なサービスを提供しています。これらのサービスには、オンラインショッピング、カスタマーサービス、商品配送などが含まれます。

回答（RAG版 Llama 2）

> Bedrock vector storeとして利用可能なのは、Titan Embeddings G1（Text）、Cohere Embed（1024）、Pinecone（Annoy）、Redis（Redis）、MongoDB Atlas（Vector Search）です。Titan Embeddings G1は、Textを扱うことができ、Cohere Embedは、1024次元のベクトルを生成します。Pineconeは、Annoyを扱い、Redisは、Redisを扱います。MongoDB Atlasは、Vector Searchを扱います。これらのベクトルDBを使用することで、Amazon Bedrockで高速な検索や、優れた推奨を実現することができます。


### 質問② Titan Text Embeddings V2のメリットは？

```

 Answer me in around 100 words. 

Human: Tell me benefits of Titan Text Embeddings V2.
Assistant:
```

回答（ファインチューニング版 Llama 2）

> タイタンテキスト埋め込みV2は、タイタンテキスト埋め込みモデルの新しいバージョンで、性能を向上し最適化されています。タイタンテキスト埋め込みV2の利点には以下のようなものがあります:
> 
> - 精度の向上: タイタンテキスト埋め込みV2は、より大規模で多様なデータセットで訓練されており、様々な自然言語処理タスクでの精度と性能が向上しています。
> - 推論の高速化: タイタンテキスト埋め込みV2は、テキストデータをより迅速かつ効率的に処理できるように最適化されています。
> - より効率的なモデルアーキテクチャ: タイタンテキスト埋め込みV2は、より効率的なモデルアーキテクチャを持ち、テキストデータをより効率的に少ないリソースで処理できます。 
> - 多言語テキストのよりよいサポート: タイタンテキスト埋め込みV2は、多言語データセットで訓練されているため、複数の言語のテキストをより適切に扱えます。
> - 長いテキストのよりよいサポート: タイタンテキスト埋め込みV2は、より長いテキスト配列で訓練されているため、長文書をより適切に扱い、文脈をより適切に捉えることができます。
> 
> 総じて、タイタンテキスト埋め込みV2は、タイタンテキスト埋め込みモデルのより高度で改良されたバージョンで、優れた性能、高速な推論、多言語と長文テキストのよりよいサポートを提供します。

回答（RAG版 Llama 2）

> Titan Text Embeddings V2 の利点は次のとおりです。
> 
> - より小さい寸法での高精度と検索パフォーマンスにより、ストレージと待ち時間が削減されます。
> 検索拡張生成 (RAG) 用に最適化されています。
> - 100 以上の言語について事前トレーニングを受けています。
> - 各文書の重要なポイントを素早く把握できる優れたアシスタントのように、テキストの詳細な要約を正確に作成できます。
> - RAG が LLM に最も関連する情報を取得できるようにし、より正確で的確な回答を導き出します。
> - フルマネージド API である Amazon Bedrock で利用できます。
> - 広範なデータセットでトレーニングされた Amazon Titan モデルは強力で多用途であり、責任ある AI 実践を遵守しながら、さまざまなアプリケーション向けに設計されています。
> 
> 出力ベクトルのサイズ (256、512、または 1024) を選択できます。ベクトル サイズが大きくなると、より詳細な応答が作成されますが、計算時間も長くなります。ベクトルの長さが短いと詳細が少なくなりますが、応答時間は向上します。
> これがお役に立てば幸いです!他にご質問がございましたらお知らせください。


### 質問③ Command Rは具体的にどのへんがRAG特化なの？

```

 Answer me in around 100 words. 

Human: Tell me how Command R is specialized for RAG.
Assistant:
```

回答（ファインチューニング版 Llama 2）

> コマンドRはRAG専用のコマンドで、RAG環境内で特定のタスクを実行するために使用されます。RAGで作業する包括的なツールセットを提供するため、他のコマンドと組み合わせて使用されるように設計されています。

回答（RAG版 Llama 2）

> Command R は、RAG に特化したスケーラブルな多言語生成モデルです。英語以外のテキストを他のモデルよりも大幅に圧縮するトークナイザーを備えており、エンタープライズ グレードのワークロードに最適です。 Command R+ は、信頼性が高く検証可能な応答を提供する最先端の RAG 最適化モデルです。高度な RAG 用に最適化されており、インライン引用が含まれているため、エンタープライズ アプリケーションに最適です。どちらのモデルも Amazon Bedrock で利用でき、ユーザーは AWS コマンドラインインターフェイス (CLI) と AWS ソフトウェア開発キット (SDK) を使用してアクセスできます。


# 評価結果

RAGの圧勝です🤣

というかファインチューニング版はほぼハルシネーションで、学習効果が感じられませんでしたね。100件程度のデータセットでは、ファインチューニングで有意なモデルのパフォーマンス向上を目指すのは厳しいのかもしれません。

よく **「ファインチューニングは最終手段だ！まずRAGを検討しよう」** と言われますが、身をもって体験できましたね。

:::note warn
購入したプロビジョンドスループットは、必ず速やかに削除しましょうね！ 1日放置するだけで数万円の大事故につながります😱
:::


# おまけ：最強のBedrock入門書を出版します！

すでにアマゾンで予約可能。Kindle版もあります。おかげさまで予約殺到しております🙇‍♂️

![カバー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/6bcbc0cd-6df7-4056-1c1f-91b5ab600556.png)

https://www.sbcr.jp/product/4815626440/