0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

parlaiとweb apiのように会話したい

Last updated at Posted at 2022-07-08

いろんな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?
image.png

http://IP/cgi-bin/parlai.py?req=Do you have a recommended movie?
image.png

以下と連携すれば、安上がりな日本語botにならないかな?
https://qiita.com/hajime_y/items/f1ca8a93d96dc3976ba1

と思ってやってみた。
image.png
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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?