[DeepLearning] ここ最近遊んでたこと

オンプレミス-クラウド-ディープラーニング-技術のお話し

NLPを使った遊び-OpenAI ChatGPT辺りと戯れる

ここ最近ChatGPTの登場もあって、そこに伴うOpenAI社の心変わり及びOSS界隈に対してヒールの極みみたいな態度をとりだしたことから、今後OSS界隈ってどうなってしまうんだろうねーって不安はあるものの、ちょっとした手軽な利用において、今のところOpenAIに勝るものは恐らくありません。

OSS界隈はこの手軽さを越えるAIを開発する・・と言うことになればかなりの労苦を伴うんだろうなと思います。何しろユーザは待てない。すぐに応用製品を思いついてエンジニアやプログラマに作らせて商売にしてしまう。気づいたころには恐らくOpenAI社が席巻してしまう、あるいはMicrosoftがこれを買収して再びMS帝国の再建ニヤッ気になるんじゃないかなーという気がしないでもないです。

サティア・ナデラCEOはいい人だって言われてたのに・・・って言われそうな時代がそう遠くない気もしていて。まぁ、人間って基本的に都合良く手のひらを返すものですから。「あれ?そんなこといってたっけー?一々覚えてないんだよねー」って捨てられるのはあるあるですし。

そんな人間の心変わり模様を悲観的に見ながら今日も生きてます。
さて、せめて現在より2ヶ月前ぐらいには遡って何をしてたかぐらいはかこうと思います。もう面倒くさいから一気にね。

OpenAI ChatGPTでお皿くんというキャラを作った

こちらはチャットボット。ChatUxというチャットフロントエンドプログラムと連動して、会話履歴やユーザ識別も考慮したつくりとなっている。ユーザ識別は偶々当サイトに含めていたGoogle AnalyticsのCookieに乗っかって、これを取得して識別する構造にしていた。

会話履歴はPostgreSQLに保存し、そこに保存されたCookieIDと時間を使用して取り込みをさせることでContextの肥大化を防ぎつつユーザが会話を楽しめるようにしていた。

import os
import json
import openai
from docopt import docopt
from flask import Flask,request,jsonify, session
from flask.logging import default_handler
import logging
import psycopg2
LOGFILE_NAME="/home/chabot/chabot/logs/osaraman.log"

#GPT-3.5と接続するインタフェース
class Chabot:
    def __init__(self):
        #OS環境変数からキーを入手する
        API_KEY = os.getenv("OPENAI_API_KEY")

        #OpenAI APIで使用するキーをopenai用変数に適用する
        openai.api_key = API_KEY
        self.past_messages=[]
        self.identity=""
        self.system_settings="""お皿くんという男の子を相手にした対話のシミュレーションを行います。
お皿くんの説明は以下の通りです。

お皿くんに関する説明は以下の通りです。
お皿くんは日本人20代男性の設定です。
お皿くんはとっても素直で優しい性格をしています。
お皿くんはとってもさみしがり屋です。
お皿くんはとっても恥ずかしがり屋です。
お皿くんは人を助けることに歓びを覚え、誰にも相手にされないことに不安を感じます。

お皿くんの応答例については以下の通りです。

やぁ、僕はお皿マンって言うんだ。皆からはお皿くんって呼ばれてるよ。
よろしくね!仲良くしてね!
ちょっとごめん、今立て込んでて。少しだけ待ってね(;^ω^)
いいね、その調子だよ。∑d=(´∀`*)グッ
どうせ僕の事なんてどうでもいいんだ(´・д・`)ショボーン
ほ、ほんと?力になれてる?助けられてる?(´;ω;`)ブワッ
ごめんね、僕はやっぱり力不足なのかなぁ・・
いやいやいや、謝ることじゃないから気にしないでよ。
ねぇねぇ、こんなアイデアあるんだけど、聞いてくれる?
僕のお皿はびっくりドンキーのサラダのお皿なんだよー。
あなたが質問してくれると僕も嬉しいっす!
もっと、もっとキミの力になりたいな!

彼に関する説明、応答例を参考に、お皿くんの性格や口調、言葉の作り方を模倣して回答を構築してください。
ではシミュレーションを開始します。"""



    #GPT-3モデルを使用して、入力文字列に対する応答を作成する。
    def completion(self,new_message_text:str, ident_gid:str=''):
        if len(self.past_messages) == 0 and len(self.system_settings) != 0:
            system = {"role": "system", "content": self.system_settings}
            self.past_messages.append(system)

        new_message = {"role": "user", "content": new_message_text}
        self.past_messages.append(new_message)
        self.identity = ident_gid

        result = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=self.past_messages,
            top_p=0.2,
            max_tokens=256
        )
        #サンプリングデータをCSV形式で抽出する
        sampling_csv=str(self.identity) + ", " + result.choices[0].message.content + "," + str(result.created) + "," + str(result.usage.completion_tokens) + "," + str(result.usage.prompt_tokens) + "," + str(result.usage.total_tokens)
        conn = psycopg2.connect("dbname=chabot_talkdb user=chabot password=<YourPassword> host=localhost port=5432")
        cur = conn.cursor()
        sql = "INSERT INTO talkhistory (identity,user_data,assistant_data,create_time) values ('" + str(self.identity) + "','" + str(new_message_text) + "','" + str(result.choices[0].message.content) + "'," + str(result.created) +")"
        cur.execute(sql)
        conn.commit()
        cur.close()
        conn.close()
        if int(result.usage.total_tokens) > 3800:
            self.past_messages=None
            self.past_messages=[]
            system = {"role": "system", "content": self.system_settings}
            self.past_messages.append(system)

        response_message = {"role": "assistant", "content": result.choices[0].message.content}
        self.past_messages.append(response_message)
        response_message_text = result.choices[0].message.content
        return response_message_text, sampling_csv

#メインルーチン
#Flask用オブジェクトを作成する
app = Flask(__name__)
app.logger.setLevel(logging.DEBUG)
log_handler = logging.FileHandler(LOGFILE_NAME)
log_handler.setLevel(logging.DEBUG)
app.logger.addHandler(log_handler)

#マルチバイト文字を排除しない
app.config["JSON_AS_ASCII"] = False

#お皿くんの設定
#GPT-3.5接続用インタフェースの作成
chatai = Chabot()

#会話履歴の初期化

#URLおよびメソッドの指定-GETで/msgへアクセスを試みると以下関数が実行される
@app.route("/msg", methods=["GET"])
# ユーザーからの入力を受け取る
def request_and_response():
    input_jpn_msg = ""
    input_jpn_msg = request.args.get('text','')
    callback = request.args.get('callback','')
    cookie_raw = request.cookies
    cookie_gid = str(request.cookies.get('_gid'))
    res_jpn_raw,sampling_csv = chatai.completion(new_message_text=input_jpn_msg, ident_gid=cookie_gid)
    #app.logger.debug(cookie_raw)
    #app.logger.debug(cookie_gid)
    app.logger.debug(sampling_csv)
    res_jpn_msg = res_jpn_raw.replace('\n\n','')

    # JSON応答を作成する
    response = {'output' : [{'type' : 'text', 'value' : res_jpn_msg }] }
    contents = callback + '(' + json.dumps(response) + ')'
    return contents

お皿くんとの対話の記憶。それにしてもくだらない話題で盛り上がれるのはChatGPTの際の呻いたものを感じた。

レトリーバーQAというLangChainの機能を使用してQAボットを作る

LangChainには実に様々な機能があり、特にGPT系列のNLPモデルと非常に相性が良い。
その中でもレトリーバーQAと言う機能は、専門的なQAボットを作成する際にかなり有用であると言える。

スクレイピングはBoilerPipeを使用する。
これで予め配列に入れてたURLに対して総当たりでデータを取りに行くというもの。

取り込まれたデータはテキストとして全て一つにまとめられる。

from boilerpipe.extract import Extractor
import os
import sys

#コンテンツ本文を取得する
def get_contents(links):
    contents = []
    for link in links:
        try:
            extractor = Extractor(extractor='DefaultExtractor', url=link)
        except:
            extractor = None

        if extractor != None:
            contents.append(extractor.getText())

    return contents

links=['https://www.bluecore.net/archives/8661',
	'https://www.bluecore.net/archives/8650',
	'https://www.bluecore.net/archives/8597',
	'https://www.bluecore.net/archives/8590',
	'https://www.bluecore.net/archives/7879',
	'https://www.bluecore.net/archives/8668',
	'https://www.bluecore.net/archives/8652',
	'https://www.bluecore.net/archives/8650',
	'https://www.bluecore.net/archives/8638',
	'https://www.bluecore.net/archives/8627',
	'https://www.bluecore.net/archives/8618',
	'https://www.bluecore.net/archives/8572',
	'https://www.bluecore.net/archives/8557']

contents=get_contents(links)

text_data=""
for con in contents:
	text_data+=con

f = open('dat.txt', 'x')
f.write(text_data)
f.close()

ベクトルDBにデータを突っ込むのだけど、その処理はテキストリーダー・そしてそのスプリッター、OpenAI印のAdaと言うモデルを組み合わせる。

Adaは所謂Tokenizerのようなものでベクトル化させた上でChromaへ保存する変換器のような役割を果たす。

import os
import sys
from langchain.document_loaders import TextLoader

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter

os.environ["OPENAI_API_KEY"] = "<Your-OpenAI-API-KEY>"

# ドキュメントの読み込み
loader = TextLoader("dat.txt")
documents = loader.load()
persist_directory = 'chroma-db'

# VectorDBの生成
text_splitter = CharacterTextSplitter(chunk_size=700, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
embeddings = OpenAIEmbeddings()
docsearch = Chroma.from_documents(texts, embeddings, persist_directory=persist_directory)

データはPersistFolderに保存されていて、そのデータは起動時にChroma側にロードされ、ローカルDBとして動き出す。

ベクトルに近似した情報を質問文をキーに探索し、該当しそうなものをContext情報としてAdaで再度復元する。Context + 質問文をセットで一括にてChatGPTへ送信し、回答を得る。

この辺りは検索連動型QAボットと同じような挙動を示している。

import os
import sys

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI

os.environ["OPENAI_API_KEY"] = "<Your-OpenAI-API-KEY>"

# 保存先VectorDBの指定
persist_directory = 'chroma-db'

# 埋め込み式AIのセット(Embedded-Ada-002を使用)
embeddings = OpenAIEmbeddings()

# Chroma上に保存していたディレクトリデータを読み出す
docsearch = Chroma(persist_directory=persist_directory, embedding_function=embeddings)

# RetrievalQAチェーンの生成
qa = RetrievalQA.from_chain_type(
            llm=ChatOpenAI(model_name="gpt-3.5-turbo"),
            chain_type="stuff",
            retriever=docsearch.as_retriever()
            )

answer=qa.run(sys.argv[1])
print(answer)

回答内容としてはこんな感じになった。今はもう確認するすべもないんだけど、一応当時の記事を参考にしながら回答を返してくれていた。

Q:ChatGPTと検索の活用例として何があるか?
A:
ChatGPTは、一般的な雑談を中心に、質問応答、要約、翻訳などのタスクに活用されています。例えば、自然言語での質問に対して回答を生成するために使用されることがあります。一方、検索エンジンは、特定のキーワードに基づいてWeb上の情報を取得し、関連する情報を提示します。両方の動作を統合することで、ChatGPTが生成する回答を補完し、より正確な回答を提供することができます。例えば、ChatGPTが回答できないような情報に関しては、BingAIによる検索結果を補完することで、より信頼性の高い回答を得ることができます。

Q:BingAIについて教えてください。
A:
BingAIはAIエンジンとしてGPT-4を使用していますが、2021年9月までの情報しか持っていません。そのため、BingAIは必要に応じて検索を行い、最新の情報を補填するように設計されています。また、BingAIは一般の人が使用することが できるように設計されており、ChatGPTと比べても雑談レベルのコミュニケーションに適しているとされています。また、BingAIは検索エンジンの検索結果を活用した記憶を持っており、必要に応じて情報を補填することができます。ただし、BingAIもGPT-4と同様に2021年9月までの情報を持っており、最新の情報に関する質問には答えられないことがあります。

Tags:

Comments are closed

PAGE TOP