🎸

Streamlit×ChatGPT:Webアプリケーションを作成

2024/10/25に公開

仮想環境の構築

まず、Webアプリケーションを作成するにあたって仮想環境を構築するところから始めましょう。仮想環境の構築にはAnaconda等がありますが、今回はPythonにすでに備わっているvenvを利用して簡単に仮想環境を構築していきます。シェル上で以下のコマンドを打ち込むだけで、仮想環境を作成することができます。

python3 -m venv app

仮想環境をアクティベートモードに切り替えるために以下のコマンドを実行します。

 . app/bin/activate

https://qiita.com/shun_sakamoto/items/7944d0ac4d30edf91fde

ChatGPTのAPIキーをゲットしよう

ChatGPTのAPIキーをゲットするためにまずはOpenAI Platformにログインしましょう。
ChatGPTのAPIキーの取得は以下の記事通りにやればできたので、記事を参考しながらAPIキーをゲットして下さい。(特段の理由がなければ、クレジットの自動リチャージはOFFにしておいた方がいいかもしれません...)
https://qiita.com/kurata04/items/a10bdc44cc0d1e62dad3

パッケージのインストール

Webアプリ開発で必要なパッケージをインストールしていきます。今回は、簡単にWebアプリを作成するためにStreamlitというライブラリを使用します。Streamlitは、Pythonを使用してWebアプリ開発の専門知識がなくても使えるので非常にありがたいツールとなっています。また、今回はChatGPTのAPIを使ったWebアプリを作成するのでOpenAIのパッケージもインストールします。

pip install streamlit
pip install openai

今回は、小説の内容を要約する機能を持ったアプリケーションを作成します。この時にLangChainを利用することで、長文の入力操作を簡単に記述することができるのでインストールしていきます。

pip install langchain
pip install langchain-community

Webアプリケーションの作成

今回は、テキストを入力してテキストを出力(Text-to-Text)、テキストファイルを読み取ってその内容を要約、画像を入力してテキストを出力(Image-to-Text)する3つの機能を持ったWebアプリケーションを作成していきます。

今回作成したサンプルコードはGitHubに公開していますので、参考にしてみてください。
https://github.com/SugiyamaSeiji/PBL2024_sample-code

Text-to-Text

Text-to-Textでは、ユーザーが入力した文章をChatGPTが要約する機能を持ったWebアプリケーションを作成します。

streamlitのtext_inputメソッドを使用してユーザーが入力したテキストを読み込みます。「送信」ボタンが押されると入力したテキスト引数としてtext_to_text関数を使って入力テキストを要約した結果を返却します。

src/app.py
input = st.text_input("テキストを入力してください")
if st.button("送信"):
    result = text_to_text(input)
    st.text_area("出力結果", result)

では、text_to_text関数の中身について説明していきます。chat.completions.createを使ってChatGPTのモデルにリクエストを送信します。その引数は、modelとmessagesです。

src/utils.py
def text_to_text(text):
    client = OpenAI()
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
        {
            "role": "system",
            "content": "あなたは、要約者です。",
        },
        {
                "role": "user",
                "content": [
                    {"type": "text", "text": f"次の文章を500字程度に要約してください。「{text}」"},
                ],
            }
        ]
    )
    response_text = response.choices[0].message.content

    return response_text

今回は、GPT-3.5-turboモデルを指定していますが、GPT4やGPT4oなど使用したいモデルに書き換えるだけで使用するモデルを変更することができます。

messagesには、システムメッセージとユーザーメッセージを定義します。システムメッセージは、モデルがどのような振る舞いをするべきを定義します。今回はモデルに「要約者である」という役割を与えています。ユーザーメッセージには、モデルが実際に処理すべきテキストを入力します。

最後にモデルにリクエストした結果を返却しています。

要約

テキストファイルにある文章を読み込んでその内容を要約するWebアプリケーションを作成します。

テキストファイルを読み込む際は、streamlitのfile_uploaderメソッドを使用します。テキストファイルがアップロードされたら、uploaded_file.read().decode("utf-8")で読み込みます。
読み込んだ内容を要約していきます。

src/app.py
uploaded_file = st.file_uploader("ファイルを選択してください")
if uploaded_file is not None:
    # ファイルの読み込み
    text = uploaded_file.read().decode("utf-8")

summarization関数では、テキストの分割とMap-Reduce法に要約文の作成を実装します。

モデルの字数制限によって入力できない場合などに、RecursiveCharacterTextSplitterを使用し入力文を一定数の文字数(チャンク)ごとに区切ることでモデルに文章を入力することができます。
chunk_sizeは区切る文字数のことを示し、chunk_overlapはチャンクごとに重複させたい文字数のことを示します。

src/utils.py
    # テキストを分割する
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size = 5000,   # チャンクの文字数
        chunk_overlap  = 0,  # チャンクオーバーラップの文字数  
    )

要約する方法として今回はMap-Reduce法を採用します。この他にもRefine法などがありますが目的に合った要約方法をとることをおすすめします!
要約には、langchainのメソッドであるload_summarize_chainを使用します。
Map-Reduce法で使用するモデルやプロンプトの指定を簡単に実装することができます。

llmは各チャンクごとに要約するためのモデルを示し、reduce_llmは各チャンクの要約を最終的に統合するモデルを示します。

map_promptは各テキストチャンクを要約する際に使用するプロンプトであり、combine_promptは各チャンクの要約を最終的に統合する際に使用するプロンプトです。

今回は簡易的なプロンプトを入力していますが、プロンプトに要約の具体例を付け足したりことでより精度の高い要約結果を得ることができるかもしれません。

src/utils.py
map_chain = load_summarize_chain(
        llm = ChatOpenAI(temperature=0,model_name=llm), # 分割ドキュメントを要約
        reduce_llm = ChatOpenAI(temperature=0,model_name=reduce_llm), # 最終の要約モデル
        collapse_llm = ChatOpenAI(temperature=0,model_name=reduce_llm), # 入力制限時に使用される
        chain_type = "map_reduce",
        map_prompt = map_first_prompt,
        combine_prompt = map_combine_prompt,
        collapse_prompt = map_combine_prompt,
        token_max = 5000,
        verbose = True
    )

以下のサイトにはMap-Reduce法の詳しい実装方法やRefine法の解説もあるので、ぜひ参考にしてみてください。
https://zenn.dev/tsuzukia/articles/05bfdcfcf5bd68

Image-to-Text

Image-to-Textでは、画像を読み込んでその画像に対して大喜利をするWebアプリケーションを作成します。

この関数は、画像をBase64形式にエンコードする関数です。画像を文字列に変換してChatGPTに入力できる文字列に変換していきます。

src/utils.py
def encode_image_to_base64(image):
    buffered = io.BytesIO()
    image.save(buffered, format=image.format or "PNG")
    return base64.b64encode(buffered.getvalue()).decode("utf-8")

Text-to-Textと同じように、chat.completions.createメソッドを使ってChatGPTのモデルにリクエストを送信します。contentのtypeにimage_urlを追加し、文字列に変換した画像を入力します。
この時、画像を低画像にすることでAPIの利用料金を削減することができます。detailをlowにすることで設定できます。

src/utils.py
def image_to_text(image):
    # `encode_image_to_base64`で PIL 形式の画像を base64 エンコードした文字列に変換
    image_base64 = encode_image_to_base64(image)
    client = OpenAI()
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
        {
            "role": "system",
            "content": "あなたはユーモアに溢れた大喜利 AI です。与えられたお題に対して、面白い回答をお願いします。",
        },
        {
            "role": "user",
            "content": [
                {
                "type": "text",
                "text": "次の画像を見て、何か面白い一言をお願いします。",
                },
                {
                "type": "image_url",
                "image_url": {
                    "url":  f"data:image/jpeg;base64,{image_base64}",
                    "detail": "low"
                },
                },
            ],
        }
        ]
    )
    response_text = response.choices[0].message.content
    
    return response_text

おわりに

今回は、ChatGPTのAPIを利用してWebアプリケーションを作成しました。ChatGPTのAPI利用方法については理解できたでしょうか。画像を入力としてAPIを利用する方法の記事が少なかったので、参考にしていただければと思います。

Discussion