AIがリアルタイムでコメント返信するバーチャル視聴者を作りました

# 概要
人からコメントもらえるのって嬉しいですよね.
なので, AIに簡単にコメントを貰えるプログラムを書きました.

マイク上で言葉を喋ると, 某動画サイトのように画面上にコメントが流れます

具体的に言うと, ｢おはようございます｣と喋った時↓のようなコメントが
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323935303937332f30363537636665372d613138382d333566372d343537612d6362636332616466353437322e70.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950973/829320cf-f69e-0a20-3a27-d4c42c887b7c.png)
｢マイクテスト聞こえていますか｣と喋った時↓のようなコメントが
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323935303937332f64643434316636362d393035362d646134372d633433382d3533336233626463323761342e70.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950973/0be4bf50-8b1f-1442-e43c-b094bf296a8c.png)
画面上に流れます


# 全体の流れ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950973/d6338589-aa85-7ec0-8b24-37c66e8275f0.png)

コード全体の流れについて説明します
1. PCのマイクで喋った内容をwhisperを使いリアルタイム文字起こしを行います
1. 次に, その内容を深層学習モデルにいれることでそれっぽいコメントを出力
1. 最後にそれをtkinterで画面上にコメント表示しました

# ①whisper : 音声データのリアルタイム文字起こし

whisperとはopenAIが提供している文字起こし用のモデルです
whisperを用いる場合, [API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis)経由か[モデル](https://github.com/openai/whisper)を直接動かすかの２通りの方法があります
今回はリアルタイムで文字起こしを行いたかったためモデルを使用しました.

コードは[このサイト](https://github.com/mallorbc/whisper_mic)の内容を参考に書き換えました.

https://github.com/mallorbc/whisper_mic

# ②T5 : 喋った内容→コメント

## 学習データのダウンロードについて

｢文字起こしデータ｣と｢チャット欄｣がテキストデータとして残っているyoutube-liveを学習データとしました
各動画に対する, ｢文字起こしデータ｣や｢チャット欄｣のダウンロードとして[yt-dlp](https://github.com/yt-dlp/yt-dlp)を使用しました

https://github.com/yt-dlp/yt-dlp

具体的に｢文字起こしデータ｣と｢チャット欄｣をダウンロードするコードは下記のようになります

```python
from yt_dlp import YoutubeDL

def get_youtube_subtitles(url):
    ydl_video_opts = {
        'outtmpl' : '%(id)s'+'_.mp3',
        'format' : 'bestaudio',
        'writesubtitles' : True,
        'skip_download' : True,
        "ignoreerrors":True,
        "overwrite":False,
        "writeautomaticsub":True,
        "subtitleslangs":['ja', 'live_chat']
    }

    with YoutubeDL(ydl_video_opts) as ydl:
        result = ydl.download([url])

urls = [ ここにダウンロードするurl書く ]
[get_youtube_subtitles(url) for url in urls]
```
## 学習データの整形について

ダウンロードした｢チャット欄｣のjsonファイルは, 各行がjsonファイルとなっているので注意

ダウンロードした｢文字起こしデータ｣は実際に表示される字幕形式となっているため冗長です
[このサイト](https://note.com/shironoy/n/ndaef7e0a628c)を参考に冗長さを取り除きました

https://note.com/shironoy/n/ndaef7e0a628c

そして, ｢チャット欄｣の投稿とその30秒前の｢文字起こしデータ｣の内容を学習用のデータセットとしてひとまとめにしました.

## モデルについて

入れた文字を元にそれっぽいコメントを出力するモデルを作成しました.
学習済み自然言語処理モデルをファインチューニングすることをします

深層学習をつかった学習済み自然言語処理モデルは
1. エンコーダーデコーダーモデル
    * 入力が文字列 出力が文字列 
    * 例:transformer T5
1. エンコーダーモデル
    * 入力が文字列 出力がベクトル
    * 例:BERT
1. デコーダーモデル 例:GPT
    * 入力文の続きを書く
    * 例:GPT

の三種類があります
今回は, ｢文字起こしデータ｣=入力と｢チャット欄｣=出力の関係に当たるので｢エンコーダーデコーダーモデル｣を選ぶのが筋が良さそうです
そのため, エンコーダーデコーダーモデルの一つであるT5をモデルとして選びました.

https://huggingface.co/sonoisa/t5-base-japanese-adapt

ファインチューニングは以下のサイトを参考に行いました

https://github.com/sonoisa/t5-japanese

# ③tkinter : コメント表示

喋った内容を画面上に描画するためにtkinterというライブラリを使用しました.
コメントを描画するところだけ抜粋してコードを書きます

```python
class TextObj():
    def __init__(self, root, i):
        self.itr = i
        self.text = tkinter.StringVar()
        self.text.set("")
        self.is_live = False
        self.label = ttk.Label(
            master=root, 
            textvariable=self.text, 
            font=("メイリオ", FONTSIZE, "bold"), 
            foreground=COLOR, 
            background="grey",
        )
        self.x = WW
        self.y = FONTSIZE*2*self.itr
        # self.label.place(x=0, y=FONTSIZE*2*self.itr)
    
    def setComment(self, comment):
        self.is_live = True
        self.x = WW
        self.text.set(comment)
        self.label.place(x=self.x, y=self.y)
        self.label.after(int(2000*random.random()), self.moveComment)
    
    def moveComment(self):
        self.x -= VELOCITY/FPS*(WW+self.label.winfo_reqwidth())/WW
        self.label.place(x=self.x, y=self.y)
        if self.x > -self.label.winfo_reqwidth():  # スクロール中
            self.label.after(int(1000/FPS+0.5), self.moveComment)
        else:  # 左端
            self.is_live = False

def main(model, energy, pause, dynamic_energy, device):
    # model設定
    # ここでバックグラウンドでリアルタイム文字起こし&コメント取得する処理を書く
    # result_queue=コメント内容

    # gui設定
    root = tkinter.Tk()
    root.title("CommentScroll")
    root.wm_attributes("-topmost", True)  # 最前面表示
    root.wm_attributes("-fullscreen", True)

    root.attributes("-alpha", 1)  # 透明度(機能してない?)
    root.wm_attributes("-transparentcolor", "grey")  # 透過色設定
    ttk.Style().configure("TP.TFrame", background="grey")

    frame = ttk.Frame(master=root, style="TP.TFrame", width=WW, height=WH)
    frame.pack()

    text_objs = [TextObj(root, n) for n in range(N_COMMENT_MAX)]

    def refreshComment():
        try:
            comments = result_queue.get(block=False)
            for comment in comments:
                for text_obj in text_objs:
                    if (text_obj.is_live)==False:
                        text_obj.setComment(comment)
                        break
        except:
            # コメントがない場合
            pass
        root.after(int(1000/FPS+0.5), refreshComment)
    root.after(int(1000/FPS+0.5), refreshComment)
    root.mainloop()

```

# 感想

簡単な挨拶などはちゃんとコメントを貰えるのですが, ちょっと難しい話をすると全部｢草｣とか｢ww｣とかになってしまいます
かなしい
極力, レスポンスがしっかりしがちな雑談系の動画に絞って学習すればもっと質が良くなったのではないかと反省してます