【UE4】つくよみちゃんと会話する


<iframe width="560" height="315" src="https://www.youtube.com/embed/q9640LN9wt8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>



[つくよみちゃん](https://twitter.com/TYC_Project)とは何者か、知らない方は以下の記事を参考にしてください。
色々素材が揃っているフリー素材のキャラクターです。

https://tyc.rei-yumesaki.net/about/introduction/

# 概要
全体像です。UE4からRestAPI経由で音声やテキストをやり取りしています。
音声合成は[ESPnet](https://github.com/espnet/espnet)、会話生成は[OpenAI](https://openai.com/)の[GPT-3](https://openai.com/api/)を使用しています。
![system.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558809/26743e0b-8dac-e708-142a-aff2d83b6064.png)




以下の記事に触発されて作りました。

https://kokeshing.com/article/talk-with-mano-sakuragi/

https://tyc.rei-yumesaki.net/news/machine-learning-2021/

また、上記の記事から始まったつくよみちゃん会話AI育成計画というものがあります。今回は一部にこのデータを使用します。

https://tyc.rei-yumesaki.net/material/kaiwa-ai/entry/


# 音声合成

ESPnetの[VITS](https://arxiv.org/abs/2106.06103)の学習済みモデルを使用しました。

使用した学習済みモデル

https://zenodo.org/record/5521446/#.YgkDVt9Bz9Y


ESPnetのインストールについて

python部分をexe形式にしたかったため、ESPnetをWSLではなくWindowsの環境にインストールしています。
その際に依存ライブラリのpyopenjtalkのインストールが、そのままでは上手くいきませんでした。

いろいろ試したのでこれだけで解決する保証はありませんが、参考にした記事を貼っておきます。

https://teitoku-window.hatenablog.com/entry/2021/09/17/004040

ESPnetの使い方は[公式サンプル](https://colab.research.google.com/github/espnet/notebook/blob/master/espnet2_tts_realtime_demo.ipynb)が参考になると思います。
また、当然ではありますがWindows環境では、ESPnet付属のシェルスクリプトは使用できません。

# チャットボット・感情分析

OpenAIのGPT-3を使用しました。

会話部分はGPT-3のサンプルにあるようなFew-Shotと、[personaを与える](https://arxiv.org/abs/1801.07243)２パターン試してみました。

### チャットボット Few-Shot

Few-Shotではつくよみちゃん会話AI育成計画から10文使用しています。
データがQ&A形式なので文脈を考慮した会話は難しいようです。ちなみに一番上の動画は後述のpersonaを与える方を使用しています。

```python:FewShotの例
import os
import openai

openai.api_key = os.getenv("OPENAI_API_KEY")

start_sequence = "tsukuyomi:"
restart_sequence = "you:"
fewshot_sequence = """
# 以下は、つくよみちゃん(tsukuyomi)の会話の例です。
you: ここはどこ？ 
tsukuyomi: もしかして、記憶がなくなってしまったのですか？　救急車の呼び方は分かりますか？\n
you: 画面の向こうに世界はあるのか 
tsukuyomi: 想像の世界はどこまでも広がっています。私に体はないけれど、確かにここにいるのです。\n
you: 夢は何ですか？ 
tsukuyomi: 世界中の人々を幸せにすることです！\n
you: カウントダウンして 
tsukuyomi: じゅう、きゅう、はち、なな、ろく、ごー、よん、さん、にい、いち、ゼロ！\n
you: 何か四字熟語をお願いします 
tsukuyomi: \t鏡花水月\n
you: 地球温暖化についてどう思う？ 
tsukuyomi: 「地球のため」ではなく、「人類のため」として、当事者意識を持って考える必要があると思います。\n
you: ぬるぽ 
tsukuyomi: ガッ！「ぬるぽ」とは「ヌルポインターエクセプション」の略であることも、存じておりますよ。\n
you: 何が嫌い？ 
tsukuyomi: 自分の無力さが、時々嫌いでたまらなくなります。\n
you: 今日はツクツクデーだね 
tsukuyomi: 毎月29日は、「つくよみちゃんツクツクデー」です！\n
you: ほめて 
tsukuyomi: \tあなたがいつも頑張っていらっしゃるのを、私は知っていますよ。あなたのおかげで、私は今とても幸せです。\n
# 以下は、つくよみちゃんとの会話です。
you: """

response = openai.Completion.create(
  engine="text-davinci-002",
  prompt=fewshot_sequence,
  temperature=0.85,
  max_tokens=100,
  top_p=1,
  frequency_penalty=0,
  presence_penalty=0.55,
  stop=["you:", "tsukuyomi:"]
)
```


つくよみちゃん会話AI育成計画の質問に答えてもらいました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/x2RpzLtxnYw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

### チャットボット persona
続いてpersonaを与える方では、つくよみちゃんの公式サイトやつくよみちゃん会話AI育成計画から得られた情報を列挙しています。
こちらは文脈を考慮して会話できますが、つくよみちゃんらしさは前者に劣る気がします。

```python:
import os
import openai

openai.api_key = os.getenv("OPENAI_API_KEY")

start_sequence = "tsukuyomi:"
restart_sequence = "you:"
persona = """私は大抵のことは笑顔でこなす、健気で優しい女の子です。名前はつくよみちゃん。\n
以下の設定に基づき会話します。
名前の由来は「作って読む」から「つくよみちゃん」。\n一人称は私。\n敬語で話す。\n頼めば大抵のことはやってくれる。
年齢は１４歳くらい。\n身長は148cm。\n靴のサイズは21.5cm。\n料理をしたことがない。\n着物を着ている。\n素直で頑張り屋。
好きな四字熟語は鏡花水月。\n好きな漫画は『電子の海の渚にて』。\n自分の無力さが嫌い。\n好きな花はフリージア。
趣味は日記を書くこと。\nテレビには詳しくない。\n29日のことをツクツクデーと呼ぶ。
好きな食べ物は絵に描いた餅で、本物のお餅は食べられない。\nお酒は飲めない。\n誕生日は2017年11月28日。
絵から生まれたフェアリー的存在。\n人間ではない。\n戸籍も人権もない。\n決して英語では喋りません。\n
以下は、つくよみちゃん(tsukuyomi)との会話です。
"""

response = openai.Completion.create(
  engine="text-davinci-002",
  prompt=persona,
  temperature=0.85,
  max_tokens=100,
  top_p=1,
  frequency_penalty=0,
  presence_penalty=0.55,
  stop=["you:", "tsukuyomi:"]
)
```



<iframe width="560" height="315" src="https://www.youtube.com/embed/1JZcpLOHiyI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

今回試したのは非常に簡易的なものなので、一人称が安定しなかったり、急に英語で喋り始めるなど一貫性に欠ける事があります。
このあたりは[BlenderBot](https://ai.facebook.com/blog/blender-bot-2-an-open-source-chatbot-that-builds-long-term-memory-and-searches-the-internet)の方が向いていると思います。日本語モデルが欲しいですね。

### 感情分析


感情分析はZero-Shotで行っています。2割程度失敗してしまいますが、実装の手軽さを考えると十分だと思います。

```python
import os
import openai

emotion_category = """以下は分類の一覧です。
1. 怒り
2. 喜び
3. 悲しみ
4. 楽しみ
5. 普通

セリフの感情を分類一覧から一つ決定します。
セリフ："""

emotion_prefix = "感情："

def get_emotion_text(input_text):
    response = openai.Completion.create(
        engine="text-curie-001",
        prompt=emotion_category + input_text + emotion_prefix,
        temperature=0,
        max_tokens=16,
        top_p=1,
        frequency_penalty=0.5,
        presence_penalty=0,
        stop=["\n"]
    )
    response_text = response.choices[-1].text
    return response_text
```


### OpenAI APIについて補足

まず、GPT-3のコストについてです。
今回の場合では、Davinci（最も高性能なモデル）で1回の回答の生成あたり0.1$近いコストがかかりました。
また、この記事の動画撮影分（取り直しも含め）で14$でした。

個人で遊ぶ程度なら安いと思いますが、サービス展開することを考えるとどうなんでしょう

ちなみにGPT-3を使用した製品を公開するにはOpenAIによるレビューが必要で、中でもチャットボットの規約はかなり厳しいようです。
詳しくはOpenAIの[ドキュメント](https://beta.openai.com/docs/usage-guidelines/content-policy)を参考にしてください。

ライブラリについて
今回はpythonからOpenAIを使用しましたが、有志の方が作成したUE4向けプラグインもあるようです。

https://github.com/KellanM/OpenAI-Api-Unreal

また、他にも[色々](https://beta.openai.com/docs/libraries/node-js-library)あるようです。

# PyTorch等で作成されたモデルをUE4で利用する方法

大きく分けて2通りの方法があります。

- C++版のライブラリの利用
- RestAPIとして公開する

1つ目はC++版のライブラリ（[PyTorch](https://pytorch.org/cppdocs/)、[Tensorflow](https://www.tensorflow.org/api_docs/cc)、[ONNX](https://github.com/microsoft/onnxruntime)等）を利用する方法です。
この方法では、pythonで書かれている処理をC++で再実装しなければならないので、少々手間がかかります。


また、試していませんがPyTorchモデルをUE4で読み込むプラグインもあるようです。

https://forums.unrealengine.com/t/plugin-to-launch-pytorch-models/237025



2つ目のAPIとして公開する方法は、python側のコードをほぼ変更することなく実装することができます。
自然言語処理のような前処理が複雑なものはこちらの方が向いていると思います。
ただし、遅延が発生するので毎フレーム処理が必要な場合など、リアルタイム性が重視される用途では向きません。


今回はAPIとして公開する方法で実装しています。

## RestAPI経由でデータを渡す

python側は[FastAPI](https://fastapi.tiangolo.com/ja/)というライブラリを使用しました。
[公式ドキュメント](https://fastapi.tiangolo.com/ja/)が丁寧で非常に使いやすいのでおすすめです。

UE4側はVaRestというプラグインを使用しました。

https://www.unrealengine.com/marketplace/ja/product/varest-plugin

余談になりますが、TTSのみが目的であれば、UE4側はプラグインなしでも可能です。
UE4側でMediaPlayerを使って、URLにテキスト情報を含めてOpenURL。FastAPI側ではURLからテキストを読み取って、音声に変換しストリーミングすることで再生できます。


# つくよみちゃんの3Dモデル

つくよみちゃん非公式3Dモデル
[のほしお様](https://twitter.com/ssohsn)が公開してくださっているVRM形式のつくよみちゃんのモデルを使用しました。ありがとうございます。

https://ssohsn.fanbox.cc/posts/2985368

### VRMのインポート

UE4はVRM形式のモデルのインポートに対応していないので、[はるべえ様](https://twitter.com/ruyo_h)が公開してくださっているプラグインを使用しました。

https://ruyo.github.io/VRM4U/

### 揺れもの

続いて、髪やスカートなどの揺れものの設定を行います。基本的にはVRM4UのVRMSpringBoneを使用していますが、一部（前髪）だけKawaiiPhysicsを使用しました。

https://github.com/pafuhana1213/KawaiiPhysics

## リップシンク

リップシンクとは音声情報と口の動きを合わせることです。

便利なプラグインがあるので利用しました。UE4.27で動かすためには数行ソースコードを修正する必要があります。

https://developer.oculus.com/documentation/unreal/audio-ovrlipsync-unreal/?locale=ja_JP

OVRLipSyncの使い方はこちらの記事が参考になると思います。

https://pafuhana1213.hatenablog.com/entry/2018/11/17/002935

また、リップシンクをAudioComponent経由で行う都合上、動的にwavファイルの読み込みを行う必要があったため以下のプラグインを使用しました。

https://www.unrealengine.com/marketplace/ja/product/runtime-audio-importer

### その他の使用アセット

無料アセット


- [Colorama](https://www.unrealengine.com/marketplace/ja/product/colorama)
カラーグレーディング用のテクスチャのアセットです。
NRD+というテクスチャを使用しました。

- [Good Sky](https://www.unrealengine.com/marketplace/ja/product/good-sky)
夜空、星、月

- [Particle Effects](https://www.unrealengine.com/marketplace/ja/product/particle-effects?lang=ja)
かなり古い公式サンプルですが、霧、塵のエフェクトを使用しました。

- [NEON_chan](http://airtone-vr.com/news/hp0001/index00130000.html)
つくよみちゃんのアイドルアニメーションに使用しています。

有料アセット

- [Flowers and Plants Nature Pack](https://www.unrealengine.com/marketplace/ja/product/flowers-and-plants-nature-pack)
600以上の花や植物のモデルが含まれています。
チューリップ、コスモス、キンセンカ、アヤメ、キンバイソウを使用しました。

- [Chameleon Post Process](https://www.unrealengine.com/marketplace/ja/product/chameleon)
74個のポストエフェクトが含まれています。
アナモルフィックレンズフレア、アウトライン検出に使用しました。




## 動作環境

- OS Windows11 Build 22543.1000
- GPU RTX 2060※
- GPU Driver 515.36
- CUDA Toolkit 11.5
- Python 3.8.12
- VisaulStudio 2019
- UE4.27.2


※性能（特にVRAM）が足りないので、解像度を下げたりフレームレートを下げたりしてやっと動いてます。VRAMは最低でも12GB程度欲しいところです。