大規模言語モデル/音声認識/音声合成/感情分析を使ってみます。[※1][※2]
- ※1
- 大規模言語モデルは、あくまで「与えられたテキストを補完する」ことしかできません(事実関係を参照するわけではありませんし、応答のテキストに対応する意識があるわけでもありません)。与えられたテキストの次にくる妥当そうな単語の連なりを、シード(ランダムな値)を交えつつ、学習した膨大なテキスト群から推測〜生成しているだけですーーなので相応の応答を求めるなら、与えるテキストを工夫する必要がありますーーここで言語モデルに与えるテキストを、(補完を促すという意味で)「プロンプト」といいます。
- ※2
- チャーマーズなど、(応答のテキストに対応する意識ではなく)内部の作用に対応する原意識ならあるかもしれないという主張もありますがーーいまのところそれは、哲学の範囲になります。
関連
- ◯
- キャラクタ・メイキング(容姿や体型を変える/表情を変える/髪や服を取り替える):Unity (BlendShapes, Hair, Cloth) , DAZ, VRoid
- ◯
- ChatGPT (GPT-4) にPrologで関係のルールを教えてみる:GPT-4, Prolog (SWI-Prolog)
- ◯
- ChatGPT (GPT-3.5) にPrologで関係のルールを教えてみる:GPT-3.5, Prolog (SWI-Prolog)
大規模言語モデルのための単純なAPIサーバを作る
大規模言語モデルの入力(プロンプト)〜出力をいろいろ操作したい場合、Pythonを使うのが楽ですよね(なにより関連するライブラリ群が豊富ですし)。
ただ対象のデバイスによっては、Pythonが使えないものも多くあります(Unity, etc. )。
この場合、Pythonのスクリプトをウェブサーバ上で動かし、対象のデバイスをそのクライアントにすれば、あまり労力をかけずにやり取りを実現できます(いまのところセッションの管理も不要なので)。
次のようにして、単純なウェブベースのAPIサーバを実装しますーーライブラリは最低限のものだけで済みます:
import sys import os import json
クライアントからの要求(文字列)を取り込むには、標準入力を読み、それをJSON形式に変換するだけです:
def getweb(): mtdreq = os.environ["REQUEST_METHOD"] if mtdreq == "POST": lencon = int(os.environ["CONTENT_LENGTH"]) bdycon = sys.stdin.read(lencon) jsnreq = json.loads(bdycon) return jsnreq
クライアントへの応答は、結果をふくめたJSON形式の内容を文字列にし、(ウェブのヘッダ規則に則り)標準出力に返すだけです:
def putweb(jsnres): print("Content-Type: application/json\n\n") print(json.dumps(jsnres))
サーバ側のコンテナには、たとえば次のようなライブラリ群を設置します:[※1][※2][※3]
FROM ubuntu:22.04 RUN apt-get -y update RUN apt-get -y upgrade RUN apt-get -y install python3 RUN apt-get -y install pip RUN pip install openai RUN python3 -m pip install --upgrade pip setuptools RUN pip install langchain RUN pip install torch torchvision RUN apt-get -y install rustc RUN apt-get -y install cargo RUN pip install transformers[ja] RUN pip install azure-ai-textanalytics RUN apt-get -y install apache2 RUN a2enmod cgid ...
- ※1
- この例では、大規模言語モデルと感情分析にOpenAIとAzure のAPIキット、独自の感情分析にtransformers、言語モデルとのやり取りにLangChain 、ウェブサーバにApacheを使っています(またFastCGI を使えば、スクリプトがメモリ上に置かれるので、応答はより速くなります)。
- ※2
- ウェブサーバには、一般のサーバアプリ(Apache, Nginx+fcgiwrap)のほか、Pythonのライブラリ(http.server )やフレームワーク(FastAPI, Flask, Django)も利用できます。
- ※3
- なお言うまでもないことですが……ウェブサーバを不特定多数がアクセスできるノードに置く場合、セキュリティの設定に十分注意する必要があります。
大規模言語モデルのセッションを継続して使う:ChatGPT (gpt-3.5-turbo)
ChatGPT とのAPIによるやり取りは、セッションごとに切れます。
なので継続したやり取りを実現するには、APIを利用する側でそれらをログに残す必要があります。
次は、(LangChain などは使わずに)ChatGPT の構造化に則ったかたちで、やり取りを継続させる例です:[※1][※2][※3]
import openai keyapi = "..." # 認証キー mdllng = "gpt-3.5-turbo" # 使用する言語モデル status = "..." # 前提となる条件(プロンプトの一部) pthlog = "..." # ログを記録するファイルのパス lstlog = [] # ログのリスト maxlog = 8 # ログの最大数 def getres(txtreq): openai.api_key = keyapi lstmsg = [] with open(pthlog,"r") as f: lstlog = json.load(f) prompt = ... txtreq ... # 要求テキストの修正(必要なら) lstmsg.append({"role": "system", "content": status}) lstmsg.extend(lstlog) lstmsg.append({"role": "user", "content": prompt}) jsnans = openai.ChatCompletion.create( model=mdllng, messages=lstmsg ) txtans = jsnans["choices"][0]["message"]["content"] lstlog.append({"role": "user", "content": prompt}) lstlog.append({"role": "assistant", "content": txtans}) if len(lstlog) >= maxlog+2: lstlog.pop(0) lstlog.pop(0) with open(pthlog,"w") as f: json.dump(lstlog,f) answer = ... txtans ... # 応答テキストの修正(必要なら) return answer
- ※1
- ChatGPT (gpt-3.5-turbo) のAPIがリリースされたいまは、価格面でも品質面でも、GPT-3 (text-davinci-003)のAPIを使うメリットはあまりないかもしれませんーーたしかにChatGPT のプロンプトは構造化されていますが、(従来どおりに)プロンプトを構造化せずに与えたいときは、user属性を使うことができますし(この属性はsystem属性より影響が強いので、さらにsystem属性を空にしておくことで、GPT-3 とほぼ同様の出力を得られそうです)。
- ※2
- FlexGen の登場で、(企業が提供する言語モデルを使わなくても)自分が作ったサーバ上で大規模言語モデルを動かせるようになりましたーーとはいえ、マシンに要求される仕様はそれなりに高く、そのわりに応答速度は遅く品質もあまりよくないので、使うにはまだ厳しい感じですね……
- ※3
- なおここでの会話例は、次を参考にしていますーーとてもシンプルなプロンプトの構成ですが、ここではさらに短く、「兄と妹の会話です。妹は丁寧語を使いません」「兄:……」「妹:……」のみを使っています(ただ返ってくる回答にはかならず「妹:」がつくので、音声会話ではここを削る必要がありますが):
- ・
- https://qiita.com/uezo/items/34a173787c26794e462f
感情分析の結果を、音声合成に反映する:Azure Cognitive Services (Speech SDK)
テキストの感情分析の結果を得るには、たとえば次のように実装します(Azure Cognitive Servicesの場合):[※1]
from azure.core.credentials import AzureKeyCredential from azure.ai.textanalytics import TextAnalyticsClient keyapi = "..." # 認証キー urlend = "..." # エンドポイント def getemo(txtreq): client = TextAnalyticsClient( credential=AzureKeyCredential(keyapi), endpoint=urlend ) lstreq = [txtreq] resfst = client.analyze_sentiment(documents=lstreq)[0] return resfst.sentiment
感情分析の結果を音声合成に反映させるには、Azure Cognitive ServicesならSSMLのstyle 属性(mstts:express-as @style )が使えます。
Pythonでは、このXMLをテンプレートにし、スタイルや応答のテキストを置換することができます:[※2]
from string import Template tmpxml = Template("<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"https://www.w3.org/2001/mstts\" xml:lang=\"${spklng}\"><voice name=\"${spkcha}\"><mstts:express-as role=\"${spkrol}\" style=\"${spksty}\"><prosody pitch=\"${spkpit}\" rate=\"${spkspd}\">${txtres}</prosody></mstts:express-as></voice></speak>") spklng = "ja-JP" spkcha = "ja-JP-NanamiNeural" # 現時点(2023.03.12)で、音声合成に感情を反映できる日本語対応のモデル #spkcha = "ja-JP-AoiNeural" spkrol = "Girl" spksty = "chat" spkpit = "0%" spkspd = "+25.00%" def repemo(keyemo): if keyemo == "negative": spksty = "angry" if keyemo == "neutral": spksty = "chat" if keyemo == "positive": spksty = "excited" return spksty if __name__ == "__main__": ... spksty = repemo(getemo(txtres)) txtres = tmpxml.substitute(spklng=spklng,spkcha=spkcha,spkrol=spkrol,spksty=spksty,spkpit=spkpit,spkspd=spkspd,txtres=txtres) ...
- ※1
- 感情分析については、Azure のサービスは応答が速く、ほぼ違和感なく会話に取り入れることができます(Transformersを使った感情分析は、そのモデルもフリーで使えますが、CPU だけだとけっこうタイムラグがあり厳しい感じです……)
- ※2
- ここではスタイルだけでなく、使用言語/キャラクタ/ロール/ピッチ/スピードなども併せて設定しています。
屋外で言語モデルを使う(XR空間にいるアバターとの、HMDを使った屋外での対話)
商用サービスのAPIにせよ独自開発のAPIにせよ、それらはネットワークを介して要求を送り〜応答を得ることができるものですーーなので屋外でも、(テザリングを設定した携帯端末があれば)これらの機能を使えます。[※1]
- ※1
- HMDについていえば、密閉型HMDを屋外で使うのはさすがにためらわれますけどーーMRグラスの利用が当たり前になれば、XR空間のアバターと、外で対話するのもそれほどおかしな光景ではなくなるかもしれません(ひとりで大声で話す人がいても、スマホの普及でそれほどびっくりしなくなったように)。