いろんなbotを作って遊んでいるが、pariaiがやはり凄いので、もっと遊びたい。
parlai(Blenderbot2)にはインタラクティブなインタフェースが用意されている。
Enter Your Message: What is your hobby?
/opt/conda/lib/python3.10/site-packages/parlai/core/torch_generator_agent.py:1728: UserWarning: __floordiv__ is deprecated, and its behavior will change in a future version of pytorch. It currently rounds toward 0 (like the 'trunc' function NOT 'floor'). This results in incorrect rounding for negative values. To keep the current behavior, use torch.div(a, b, rounding_mode='trunc'), or for actual floor division, use torch.div(a, b, rounding_mode='floor').
hyp_ids = best_idxs // voc_size
[BlenderBot2Fid]: I like to play video games. What about you? _POTENTIALLY_UNSAFE__
Enter Your Message: Who are you?
[BlenderBot2Fid]: Hi, I'm a computer programmer. What do you like to do in your spare time?
Enter Your Message: Who is the Prime Minister of Japan?
[BlenderBot2Fid]: I'm not sure, but I do know that Shinzo Abe is the prime minister of Japan.
Enter Your Message: Do you have any recommended manga?
[BlenderBot2Fid]: I don't have any recommendations, but there are a lot of anime and manga out there.
コマンドで遊ぶ分には良いが、ほかアプリと連携させるには不便なので、Web経由でparlaiとやり取りできるようにした。
まず、サーバ用のプログラムを用意。
parlaiのインタラクティブに起動しつつ、pexpectで"Enter Your Message:"のプロンプトが来るまで待ち受けて入力を受け付ける。
応答には余計な文字列が含まれているので、'[BlenderBot2Fid]'の文字列を基準に必要なものだけ削って返す。
今後、入出力をいろいろ増やして遊びたいので、文字列ではなくjsonで応答できるようにする。
なお、--search_serverとあるようにserarch_serverを分離している。
(というか、同じ環境でsearch_serverも動かそうとするとpipがdependency resolver does not currently~を吐くのは自分だけ?無視しても動くけれど、気持ち悪いので分離)
safe_interactiveを使わない場合に初回応答時に出てくる「POTENTIALLY_UNSAFE_」のメッセージも、ここで除外している。
#!/opt/conda/bin/python
# -*- coding: utf-8 -*-
import socket, pickle, pexpect, sys
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), 54320))
s.listen(5)
p = pexpect.spawn('parlai interactive --model-file zoo:blenderbot2/blenderbot2_400M/model --search_server 10.100.0.95:10187')
p.expect("Enter Your Message:", timeout=300)
print("Ready to respond")
while True:
csocket, address = s.accept()
req=pickle.loads(csocket.recv(1024))
p.sendline(req["msg"])
print("input: " + req["msg"])
p.expect("Enter Your Message:", timeout=300)
out=p.before.decode('utf-8')
out=out[ out.find('[BlenderBot2Fid]') +28:-12].replace("_POTENTIALLY_UNSAFE__","")
print("output: " + out)
csocket.send(pickle.dumps([out]))
csocket.close()
そしてcgi。
上記プロセスに接続して応答を受け取る。
#!/opt/conda/bin/python
# -*- coding: utf-8 -*-
import cgi, socket, pickle, json, sys
params = cgi.FieldStorage()
print('Content-Type: text/html; charset=UTF-8\n')
if 'req' not in params: print("リクエスト「req」がありません")
else:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostname(), 54320))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sendpacket=pickle.dumps( {"msg":params['req'].value.replace("\n","。").replace("\t","")} )
s.send(sendpacket)
# print(str(params),file=sys.stderr)
print(pickle.loads(s.recv(1024))[0])
Dockerfileは以下。
FROM jupyter/datascience-notebook
USER root
ENV PYTHONUNBUFFERED True
ENV LANG C.UTF-8
ENV LANGUAGE en_US:
RUN pip install torch parlai transformers faiss-gpu
RUN pip install fairseq
RUN cp /etc/localtime /etc/localtime.org
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN echo "Asia/Tokyo" >/etc/timezone
WORKDIR /app
COPY app /app
CMD /app/boot.sh
あまり行儀が良くないが、1コンテナ上でいろいろ動かしている。
server.pyは、先のparlaiを起動しているヤツ。
[root@ip-10-100-0-11 parlai]# cat ./app/boot.sh
nohup /app/server.py >server.log 2>&1 &
exec /app/app.py
[root@ip-10-100-0-11 parlai]#
[root@ip-10-100-0-11 parlai]# cat ./app/app.py
#!/opt/conda/bin/python
# -*- coding: utf-8 -*-
import os, http.server, socketserver
PORT = 80
os.chdir('/app')
class handler(http.server.CGIHTTPRequestHandler):
cgi_directories = ["/cgi-bin"]
httpd = socketserver.TCPServer(("0.0.0.0", PORT), handler)
httpd.server_name = "test"
httpd.server_port = PORT
httpd.serve_forever()
[root@ip-10-100-0-11 parlai]#
なお、zoo:blenderbot2/blenderbot2_400M/modelの初回起動時に13GB程度のデータダウンロードが走るが、サーバが重いのか16時間ほどを要した。
コンテナ削除で一緒に消えると悲しいので、別途ダウンロードしてローカルに保存しておくのが無難。
容量が容量なのでimageに含めたくはないし、以下のスクリプトで大きめのファイルを予め/parlai/data以下にダウンロードしておき、/opt/conda/lib/python3.10/site-packages/data以下をまるごとマウントする。
function parlai_setfile() {
dname=`dirname ${2}`
if [ ! -e /parlai/${2} ]; then
mkdir -p /parlai/${dname}/
wget -P /parlai/${dname}/ ${1}
fi
}
parlai_setfile http://parl.ai/downloads/_models/blenderbot2/blenderbot2_400M/model.tgz data/models/blenderbot2/blenderbot2_400M/model.tgz
parlai_setfile https://dl.fbaipublicfiles.com/fairseq/gpt2_bpe/encoder.json data/gpt2/encoder.json
parlai_setfile http://parl.ai/downloads/_models/hallucination/bart_rag_token/model.tgz data/models/hallucination/bart_rag_token/model.tgz
parlai_setfile https://dl.fbaipublicfiles.com/dpr/wikipedia_split/psgs_w100.tsv.gz data/models/hallucination/wiki_passages/psgs_w100.tsv.gz
parlai_setfile https://dl.fbaipublicfiles.com/dpr/checkpoint/retriver/multiset/hf_bert_base.cp data/models/hallucination/multiset_dpr/hf_bert_base.cp
parlai_setfile http://parl.ai/downloads/_models/blenderbot2/memory_decoder/model.tgz data/models/blenderbot2/memory_decoder/model.tgz
parlai_setfile http://parl.ai/downloads/_models/blenderbot2/query_generator/model.tgz data/models/blenderbot2/query_generator/model.tgz
parlai_setfile http://dl.fbaipublicfiles.com/fairseq/models/bart.large.tar.gz data/models/bart/bart.large.tar.gz
上記でダウンロードしたファイルを、docker run時に「-v /parlai/data:/opt/conda/lib/python3.10/site-packages/data」でマウントすればOK
これで、web経由でやりとりできる。RTX 3070でも1秒程度で応答帰ってくるので上々。
http://IP/cgi-bin/parlai.py?req=are you happy?
http://IP/cgi-bin/parlai.py?req=Do you have a recommended movie?
以下と連携すれば、安上がりな日本語botにならないかな?
https://qiita.com/hajime_y/items/f1ca8a93d96dc3976ba1
と思ってやってみた。
2つ目の書き込みには「映画」という単語が出てきていないのに、「インターセプター」という単語からか、あるいは会話の流れからか、それが映画であると認識しているの凄い。
なんJコーパス学習させて頭おかしくなったずんだもん(中身Rinna-1b)と会話されて、その履歴で学習しなおせば、あっちも少しマシになるだろうか?
一応、ImageServerの方のDockerfileも載せておく。
SearchEngineはgitからとってきて置いておく必要あり。
FROM jupyter/datascience-notebook
USER root
ENV PYTHONUNBUFFERED True
ENV LANG C.UTF-8
ENV LANGUAGE en_US:
RUN pip install torch
RUN cp /etc/localtime /etc/localtime.org
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN echo "Asia/Tokyo" >/etc/timezone
COPY SearchEngine /SearchEngine
WORKDIR /SearchEngine
RUN pip install -r requirements.txt
CMD exec python /SearchEngine/search_server.py serve --host 0.0.0.0:80