Stable DiffusionをChatGPT APIを用いたSlack botに追加してみた。

今回は、[ChatGPT APIを用いたSlack bot](https://qiita.com/katsuki_ono/items/5ba8d6b541bcbc633f56)に画像生成AIのStable diffusionの機能を追加してみました。

製作するものとしては、SlackからChatGPTと話しながら、Stable diffusionで画像生成を出来る botです。

今回もGoogle Colabを使ってpythonでコードを書いています。

製作プロセスとしては、⒈Google  colabでStable diffusionを使えるようにする、⒉ChatGPTを用いたSlack  botにStable diffusionの機能を実装する、です。

# ⒈Google  colabでStable diffusionを使えるようにする
まず、Google  colabで新規ノートブックを作成して、上部のメニューから「ランタイム」を選択して、「ランタイムのタイプを変更」を押して、ハードウェア アクセラレータをGPUに変更して保存します。![D6CF6852-219D-45DD-8F78-030C9C0A6B05.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287642/755e5fb1-3e32-c359-990e-b33786ac773b.jpeg)

次に、Stable diffusionを使えるようにコードを用意します。何故か、Stable diffusionのコードはChatGPTが書けないので、今回は、[こちらのサイト](https://zero-cheese.com/9349/)のプロセスとコードを参考にしています。

はじめに、[Hugging Face](https://huggingface.co)のアカウントを作成して、ログインして、左のメニューから「Settings」を選択して、メニューが切り替わったら「Access Tokens」を押します。
開いたページで、New tokenを押して、Roleはreadでトークンを作成します。名前はなんでも良いです。
トークンを作成したら、コピーしてメモします。

ここまできたら、必要なインストールを行います。

```
!pip install torch diffusers transformers scipy ftfy
```

最後に、下記のコードをコピペして、先程取得したHugging Faceのトークンを入力して、全てのセルを実行します。
画像が生成されたら、⒈Google  colabでStable diffusionを使えるようにするは完了です！

``` python
import torch
from torch import autocast
from diffusers import StableDiffusionPipeline

hugging_token = '<取得したトークン>'

ldm = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5",
                                              torch_dtype=torch.float32,
                                              use_auth_token=hugging_token
                                              ).to("cuda")

prompt = '<生成したい画像を表現した、文字列>'

# 5枚画像を作りたい場合
num_images = 5

i = 0  # 初期値を 0 とする

for j in range(num_images):
    with autocast("cuda"):
               image = ldm(prompt,
                    height=400,  # 縦サイズ(px)
                    width=400,  # 横サイズ(px)
                    guidance_scale=7.5,  # プロンプトの重み（生成画像の類似度（0〜20)）
                    num_inference_steps=50,  # 画像生成に費やすステップ数
                    ).images[0]

    i += 1  # ループの最後で i をインクリメントする

    image.save(f"./image_{i}.png")
```

補足
⒈生成された画像は、左のメニューのファイル内に保存されます。上のメニューのファイルとは別です。セルを再度実行したり、ランタイムを更新すると生成した画像が消えてしまうので、注意してください。![16848130-4215-4B09-A0B0-90DD316E7AC7.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287642/6a7cbf64-056b-4f68-6ed2-2af67de78e23.jpeg)

⒉Stable diffusionのバージョンの変更には、コード内のrunwayml/stable-diffusion-v1-5を変更します。変更するには、[Hugging Face](https://huggingface.co)で使用したいバージョンを検索して、使用したいバージョンのページに行き、コードをコピーします。Stable Diffusion v2-1を使用する場合には、[こちらのページ](https://huggingface.co/stabilityai/stable-diffusion-2-1)からstabilityai/stable-diffusion-2-1をコピーしてコードを変更します。![A900E15F-99B8-441F-ADE5-10D0EBED7BE6.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287642/1feb035d-bec2-48be-c53e-b5df51c9198f.jpeg)

⒊画像のサイズの指定は縦も横も8の倍数にしてください。

# ⒉ChatGPTを用いたSlack  botにStable diffusionの機能を実装する
まず、新規ノートブックを作成して、⒈の手順でGPUに設定を変更します。

次にSlack APIのスコープにfiles:writeを追加します。(ChatGPTを用いたSlack bot自体に必要なSlack APIの設定は、[こちら](https://qiita.com/katsuki_ono/items/5ba8d6b541bcbc633f56)を参照してください。)

スコープを設定したら、次はインストールです。
openai、 slack、Stable diffusionに必要なインストールを下記のように行います。

```
!pip install --upgrade pip
!pip install setuptools wheel
!pip install openai

!pip install slack-bolt

!pip install torch diffusers transformers scipy ftfy
```

インストール出来たら、openaiと slackに必要なインポートと、情報の入力などをします。

``` python
import openai
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
import os

#Bot User OAuth Token
os.environ["SLACK_BOT_TOKEN"] =  ""

#Signing Secret
os.environ["SLACK_SIGNING_SECRET"] = ""

#Token 
os.environ["SLACK_APP_TOKEN"] =  ""

#openaiの APIキー
openai_api_key = ""

app = App(
    token=os.environ.get("SLACK_BOT_TOKEN"),
    signing_secret=os.environ.get("SLACK_SIGNING_SECRET")
)

openai.api_key = openai_api_key
```

ここまで出来たら、Stable diffusionに関するコードを書きます。Hugging Faceのトークンも入力します。

``` python
import torch
from torch import autocast
from diffusers import StableDiffusionPipeline

hugging_token = '<取得したトークン>'

ldm = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5",
                                              torch_dtype=torch.float32,
                                              use_auth_token=hugging_token
                                              ).to("cuda")
```

次に botのコードを下記のように書きます。
この botでは、写真という言葉がある場合には、ChatGPTでユーザーの発言を処理して画像を生成して、ChatGPTのコメント付きでSlackからレスポンスしています。写真という言葉が含まれない場合には、普通に botと話せます。コメントと普通の会話には、assist1で一つ前の botの発言を記憶させているので、ある程度、文脈を維持するようにしています。

``` python
# botと話しながらプロンプトを作成して画像生成

assist1 = ""

@app.event("message")
def handle_message(event, say):
    global assist1
    if "写真" in event["text"]:
        text = event["text"]
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": "以下を簡潔な英単語にして英単語のみ,区切りで答えてください。"},
                {"role": "user", "content": text},
            ]
        )
        assist2 = response["choices"][0]["message"]["content"]  

        prompt = f"{assist2},任意のプロンプト"

        with autocast("cuda"):
            image = ldm(prompt,
                        height=480,  # 縦サイズ(px)
                        width=640,  # 横サイズ(px)
                        guidance_scale=7.5,  # プロンプトの重み（生成画像の類似度（0〜20)）
                        num_inference_steps=50,  # 画像生成に費やすステップ数
                        ).images[0]

        image.save(f"./image.png")

        # 写真のコメントを生成
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": "写真を送付する際のメッセージを作成して下さい。設定：任意の設定"},
                {"role": "assistant", "content": assist1},
                {"role": "user", "content": text},
            ]
        )
        assist1 = response["choices"][0]["message"]["content"]  # assist1を更新

        # 画像を送信する
        try:
            image_path = "./image.png"  # 送信する画像のパスを指定する
            response = app.client.files_upload(
                channels=event["channel"],
                file=image_path,
                initial_comment= assist1
            )
            print(response)
        except Exception as e:
            print(f"Error: {e}")


    if "写真"  not in event["text"]:
        text = event["text"]
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": "コメントと同じ設定"},
                {"role": "assistant", "content": assist1},
                {"role": "user", "content": text},
            ]
        )
        assist1 = response["choices"][0]["message"]["content"]  # assist1を更新
        say(assist1)
```
ここまで出来たら、下記のようにアプリを起動するコードを書き、全てのセルを実行します。

``` python
# 起動
if __name__ == "__main__":
    handler = SocketModeHandler(app,app_token=os.environ.get("SLACK_APP_TOKEN"))
    handler.start()
```

このプログラムは少し実行に時間がかかります。
アプリを起動するセルが実行された状態で botに話しかけて、以下のようなレスポンスがあれば、成功です！

写真という言葉有り![F9682F4E-01CB-472C-9495-7276207C1109.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287642/86c9ac6b-c025-2ccc-48e5-72ef891ee5b0.jpeg)

写真という言葉無し![CE2AA0F4-890D-4886-AE37-9CB558FCB925.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287642/0015e5f3-4652-4e24-8503-db5ac4e6dfed.jpeg)

# 最後に
私がAIにハマるきっかけになったStable diffusionを活用した botを作成出来て感慨深いです。
今回作成した botでは、画像の質を高めている関係ですぐGoogle colabが落ちてしまうので、少し改良が必要です。
また何か作ってみます。