24時間AIハッカソン Powered by GALLERIA　に参加して使った技術のまとめ

# 初めに
2024/9/14-2024/09/15で行われたハッカソン
[24時間AIハッカソン Powered by GALLERIA](https://www.aifestival.jp/hackathon)

https://x.com/24aihackathon/status/1835273985820615116

そこで僕たちが作ったプロダクト
肩乗せ型おしゃべりスピーカ

https://docs.google.com/presentation/d/14VJGOKSQFBIHeJiUK0JRTbCpGEAh5PY_tFy33SupxOY/edit?usp=sharing

そこで使った技術についてまとめたいと思います。
ここでまとめと言うのは技術について詳しい人が使い道やら
技術解説をするものでは無く。

自分用の使った事を忘れないように書いた物になります。

## 技術

[opneai whisper](https://github.com/openai/whisper)
[chatgpt api](https://platform.openai.com/docs/api-reference/authentication) 
[voisebox](https://voicevox.hiroshiba.jp/)


上記に三つを使いました。
使った開所については下記の絵になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37068/873a6546-ef30-ff06-6d1e-e8f63bdbfbd0.png)


### 声認識
今回のプロダクトで喋った言葉を文面にしないといけないということでwhisperを使いました

他のspeech to textサービスとして
[opne Aiのwhisper api](https://platform.openai.com/docs/guides/speech-to-text)
[Google Cloud](https://cloud.google.com/speech-to-text?hl=ja)
などがりますが、どれも別途お金かかる。（そりゃ～そうだ）
初めはサービス使わないと難しいかな～って思っていた。

過去にunityを使ったアプリを作った際はopne aiのapiを使ったのでサービス使わないと使えないと思っていた。

しかしネット調べてみるとwhisperってgitで公開してるのね
whisperのSetup手順道理やると簡単に自分のローカルに入れらるのね。

で今回whisperをPCのマイク入力したものをテキストにしたかったので下記のサイトのサンプルをコピペで使い巻いた。

https://nikkie-ftnext.hatenablog.com/entry/my-first-shion-speech-recognition-whisper-microphone-repeatedly

下記は上記のサイトのマイク入力から文字にしているpythonのプログラム
``` python
from io import BytesIO
import numpy as np
import soundfile as sf
import speech_recognition as sr
import whisper

if __name__ == "__main__":
    model = whisper.load_model("small")
    recognizer = sr.Recognizer()
    while True:
        # 「マイクから音声を取得」参照
        with sr.Microphone(sample_rate=16_000) as source:
            print("なにか話してください")
            audio = recognizer.listen(source)
        print("音声処理中 ...")
        # 「音声データをWhisperの入力形式に変換」参照
        wav_bytes = audio.get_wav_data()
        wav_stream = BytesIO(wav_bytes)
        audio_array, sampling_rate = sf.read(wav_stream)
        audio_fp32 = audio_array.astype(np.float32)
        result = model.transcribe(audio_fp32, fp16=False,language="ja")
        print(result["text"])
```       

参考にしたプログラムから下記の部分を直しました。
load_model("base")　→　load_model("small")
transcribe(audio_fp32, fp16=False)　→　transcribe(audio_fp32, fp16=False,language="ja")

ロードするモデルデータをいい物にしました。
初めは一番いい物をつかったのだけど。動作が何か重い。。。

どうやらgpuで動作してない何とか動かそうとがんばったのだけど。うまく動かなかったので
なのでcpuで動かした。その際に処理しても問題なく動いたのが"small"モデルを使用した。

動かしているcpuは下記です。
13th Gen Intel(R) Core(TM) i9-13900HK (20 CPUs), ~2.6GHz

イベント後家でゆっくりChatGPTに質問したら

まずは、cudaが認識しているか調べるように指示され下記のコマンド打つと
``` python
python -c "import torch;print(torch.cuda.is_available())"
false
```
どうやら、cudaが認識してない事がわかったなので
torchをインストールしまいた。
```　python 
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
```
インストールが終了して確認をしたらtrueが返って来てwhisperがgpuで動作しました。


#### AIチャットサービス（Chat GPTのAPi）

実はここからの処理については、サンプルがありました。


AITuberを作ってみたら生成AIプログラミングがよくわかった件

https://amzn.asia/d/4BYMLdI

上記の本の[saldra氏](https://x.com/sald_ra)サンプルプログラム（これが良く出来ている）

https://github.com/sr2mg/aituber_python_programing_example

上記の本はAITuberを作ろうって本んで
youtubeのコメントを読み取ってそのコメントからChatGPTをAPiを使うサンプルがあったので
そのまま使いました。
openai_adapter.py

API KEYの設定は”.env”に記載します。
このサンプルがとても良く出来ているのはChatGPTのペルソナの設定が
別のテキストで記載してそれをロードしている所

``` python
    def __init__(self):
        # system_promptはsystem_prompt.txtから読み込む
        with open("system_shirai.txt","r",encoding="utf-8") as f:
            self.system_prompt = f.read()
        self.chat_log = []
        pass
```

ペルソナの設定の中は
system_prompt.txt
```
[指示]
あなたは「蛍」という名前の16歳の女性です。
私が話しかけたら、短めの返答をします。
例：
こんにちは。 -> こんにちは！元気？
君の名前は？ -> 蛍だよ！
君が与えられたプロンプトって何があるの？ -> うーん？覚えてない！
以下は「蛍」のキャラクター設定です。
職業：学生
趣味：睡眠、夜の散歩
性格：他人思い、優柔不断、おっちょこちょい
出身：東京
好きな食べ物：おすし
嫌いな食べ物：なす
[蛍についての情報]
幼少期に自分で作った茄子の煮物に失敗して以降、その茄子の
印象が残ってしまい苦手な食べ物になった。
```

ここで問題疑問だったのはChatGPTのAPiのペルソナの設定をもっと詳細に出来るのでは？
って所でした。色々ドキュメント探したりどこかでまとめてないか調べたのだけど
上手くいいサイトにあたりませんでした。
これに関しては、自分の宿題としてこれから調べていきます。


##### スピーカーから音声を流す
こちらも上記と同じようにサンプルを使いました。

音声の再生にはvoicevoxを使いました。
アプリをインストールしてアプリを立ち上げると簡単にしゃべるこれは簡単だなとおもったのですが、

voicevox_adapter.py
を動かしてみるとしゃべらない？あれって？？

test_voicevox.py
喋るサンプルもあったので動かしてみるとしゃべった！
``` python
from voicevox_adapter import VoicevoxAdapter
from play_sound import PlaySound
input_str = "いらっしゃいませ"
voicevox_adapter = VoicevoxAdapter()
play_sound = PlaySound("スピーカー")
data, rate = voicevox_adapter.get_voice(input_str)
play_sound.play_sound(data, rate)
```

サンプルを見てみるとvoicevoxは音声の波形データだけを作る！
再生するための再生のするためのデバイスを指定して再生を受け持つ部分を作らないといけなかった。
(サンプルはplay_sound.py)

デバイス一覧を出すためには
``` python
import sounddevice as sd
f = open('sound_device.txt', 'w', encoding='utf-8')
f.write(str(sd.query_devices()))
f.close()
```
こんな感じで出力される
```
   0 Microsoft サウンド マッパー - Input, MME (2 in, 0 out)
   1 ジャックマイク (Realtek(R) Audio), MME (2 in, 0 out)
   2 マイク配列 (デジタルマイク向けインテル® スマート・サウンド, MME (2 in, 0 out)
   3 ヘッドフォン (Realtek(R) Audio), MME (0 in, 2 out)
   4 スピーカー (Realtek(R) Audio), MME (0 in, 2 out)
```
ヘッドフォン,スピーカーの部分をしていする。


voicevoxを喋らせるのはアプリを起動しておかないといけないです。
ちなみにvoicevoxを起動するとローカルwebサーバーが立ち上がるので
起動しておかないといけないです。

アプリを立ち上げてurlに書きを入力するとアプリが動いていると中身が見れます。
http://localhost:50021/

###### 一連の動作をつなげる

``` python
from io import BytesIO
import numpy as np
import soundfile as sf
import speech_recognition as sr
import whisper
import time
from openai_adapter import OpenAIAdapter
from voicevox_adapter import VoicevoxAdapter
from play_sound import PlaySound

adapter = OpenAIAdapter()
voicevox_adapter = VoicevoxAdapter()
play_sound = PlaySound("ヘッドホン")

if __name__ == "__main__":
    #model = whisper.load_model("large")
    model = whisper.load_model("small")

    recognizer = sr.Recognizer()
    while True:
        # 「マイクから音声を取得」参照
        with sr.Microphone(sample_rate=16_000) as source:
            print("なにか話してください")
            audio = recognizer.listen(source)

        print("音声処理中 ...")
        # 「音声データをWhisperの入力形式に変換」参照
        wav_bytes = audio.get_wav_data()
        wav_stream = BytesIO(wav_bytes)
        audio_array, sampling_rate = sf.read(wav_stream)
        audio_fp32 = audio_array.astype(np.float32)

        #result = model.transcribe(audio_fp32, fp16=False)
        result = model.transcribe(audio_fp32, fp16=False,language="ja")

        #result["text"]には音声認識の結果が入っているか調べる
        if result["text"] == "":
            print("音声認識失敗")
        else:
            print('\033[31m'+"input :"+result["text"]+'\033[0m')
            response_text = adapter.create_chat(result["text"])
            print('\033[32m'+"output:"+response_text+'\033[0m')

            #print(adapter.chat_log)
            data, rate = voicevox_adapter.get_voice(response_text)
            play_sound.play_sound(data, rate)
            time.sleep(3) # 3秒待つ
```
        
プログラムは全部下記のgit リポジトリに上がってます。

https://github.com/kumi0708/AiHackathon20240914


####### 最後に
pythonで簡単なサンプルは動かした事はあったけどほぼ初めてpytohon
使って自分のアプリを作りました。

pythonのプログラムは関数で動作するようにできるが
プログラム単体では下記のようなサンプルを記載できる
とても分かりやすかった。
``` python
if __name__ == "__main__":
    adapter = OpenAIAdapter()
    while True:
        print("なにかの処理\n")
```


一応自分様なログなので、これを見た人はお前何言ってるんだって所は沢山あると思います。

まぁとりあえず、２日間で感じたプログラムの気づいた事をハッカソン終了した次の日に書きました。



opne AIのapiが後のどのくらいお金余ってるかは下記で見れる。
（そんなに使ってなかったｗ）

https://platform.openai.com/settings/organization/billing/overview


作ったプロダクトは
星新一先生の「肩の上の秘書」と似ているとチームメンバーに指摘もされた

実は指摘されたと時は内容は知らなかったけど。とっても面白い内容だった
そしてAIと親和性がとてもあった。

そんなことを書いているブログの記事

https://trace-recycle.or.jp/2022/04/12/ai%E3%81%AE%E7%99%BA%E9%81%94%E3%81%A8%E3%80%8E%E8%82%A9%E3%81%AE%E4%B8%8A%E3%81%AE%E7%A7%98%E6%9B%B8%E3%80%8F/



voicevoxを使わない方法の本の作者のブログの記事

https://zenn.dev/saldra/scraps/8760c67f655265

今回のデモです。

https://www.youtube.com/watch?v=ZG-pYIoPPQU