코딩일기

parlai internet server 코드 리뷰 ( feat. blenderbot, parlai, internet server ) 본문

Code/기타

parlai internet server 코드 리뷰 ( feat. blenderbot, parlai, internet server )

daje 2022. 11. 18. 18:55
728x90
반응형

 

안녕하세요 다제 입니다. 

 

최근 facebook에서 bb3 등이 나오면서 챗봇에 대한 관심이 급증하고 있습니다. 

 

물론, 성능적인 이슈로 많은 글들이 reddit에 올라오고 있지만, 그래도 인터넷에서 검색한 결과를 가지고 그정도 이야기를 한다는 것이 참 대단하다고 개인적으로는 생각하고 있습니다. 

 

facebook이 어떻게 bb3를 만들었는지는 추후 다룰 예정이고, 오늘은 blenderbot에서 인터넷 검색을 할 수 있도록 도와주는 server에 대해서 코드를 리뷰하는 시간을 갖도록 하겠습니다. 

 

 

해당 코드는 https://github.com/JulesGM/ParlAI_SearchEngine 여기서 바로 보실 수 있으며, 이분이 작성한 코드를 리뷰한 것입니다. 여기서 들어가시면 어떻게 설치하고 사용하는지 방법이 자세히 기재되어 있지만 그래도 한번 더 설명을 드리겠습니다. 

 

GitHub - JulesGM/ParlAI_SearchEngine: A search engine for ParlAI's BlenderBot project (and probably other ones as well)

A search engine for ParlAI's BlenderBot project (and probably other ones as well) - GitHub - JulesGM/ParlAI_SearchEngine: A search engine for ParlAI's BlenderBot project (and probably other...

github.com

 

오늘은 목차를 아래와 같습니다. 

 


 

0. 오~ 신기해!

1. 어떻게 설치를 하는지?

2. 어떻게 사용해야하는지? 

3. 어떻게 작동하는지?

 


 

0. 오~ 신기해! 

저는 손흥민을 좋아해서 손흥민을 검색해보았습니다. 

직접 촬영한 이미지

서버에서 검색한 결과가 위에 있는 것이고, 아래는 웹사이트에서 검색한 이미지 입니다. 

서버에서 전송해주는 값과 검색되는 값이 마케팅적인 요소로 인해 일부 다르긴 하지만, 그래도 비슷한 내용을 불러온다는 것을 볼 수 있습니다. 자 그렇다면 이 서버를 어떻게 설치하고 도대체 어떻게 작동을 하는지 알아보도록 하겠습니다. 

 

 

1. 어떻게 설치를 하는지?

먼저, git을 clone합니다. 이건 아실거라 믿습니다... 

# git clone
git clone https://github.com/JulesGM/ParlAI_SearchEngine.git

그래도 혹시 모르니.. 아래 이미지를 첨부하겠습니다. 

위에 녹색으로 표기된 <> code를 누르면, 위와 같은 창이 하나 뜹니다. 저기서 복사 버튼을 클릭하면 됩니다. 

그리고 커맨트 창으로 가서 위에 기재한 코드를 입력하면 폴더가 하나 생기는 것을 볼 수 있습니다. 

그럼, 해당 폴더로 들어갑니다. 

cd ParlAI_SearchEngine

그럼 깃허브에서 보이는 것처럼 파일들이 보이게 됩니다. 

그 상태에서 parlai 라이브러리를 설치하지 않으셨다면, conda 환경을 하나 생성합니다.

# 기존에 활성화된 conda를 끕니다. 
conda deactivate 

# 새로운 conda 환경을 생성합니다. 
conda create -n parlai python=3.8 -y
# parlai라는 이름을 가진 conda를 만들것이고 
# 이것의 파이썬 버전은 3.8를 사용할 것이고
# 설치 중간에 yes를 입력하는 것을 바로 하기 위해 -y 옵션을 넣어주는 것입니다.

# 새로운 conda를 실행합니다. 
conda activate parlai

 

그후 바로 pip install requirements.txt를 하면 됩니다. 

pip install -r requirements.txt

이렇게 하면 설치는 모두 끝납니다. 

 

 

2. 어떻게 사용해야하는지? 

터미널 창을 하나 더 켜주어서 같은 conda 환겨으로 접속하고, 아래 명령어를 입력하여 서버를 실행 시켜줍니다. 

python search_server.py serve --host 0.0.0.0:8080

이 명령어를 설명드리겠습니다. 

python search_server.py는 파이썬 파일을 실행하겠다라는 이야기입니다. 

여러분이 간단한 파이썬 파일을 만들고 그 안에 프린터 명령어를 기재하고 python test.py 라고 하면 프린터 되는 것을 볼 수 있습니다. 

그 뒤에 serve라는 것은 search_server.py 파일에서 Application에 있는 serve입니다. 

그리고 --host는 우리가 실행할 서버를 어디에 올려야하는지 명기해주는 것입니다. 

 

그리고 원래 실행되고 있던 터미널 창에는 아래와 같이 입력을 하게 되면 맨 처음 보신 이미지와 같이 검색이 되는 것을 확인할 수 있습니다. 

curl -X POST "http://0.0.0.0:8080" -d "q=손흥민&n=5"

curl에 대한 설명은 별도의 포스팅을 해놓았습니다. 해당 글을 참고해주시면 감사드리겠습니다. 

2022.11.18 - [Code/기타] - [Overview] curl이란? (feat.curl의 작동원리)

 

[Overview] curl이란? (feat.curl의 작동원리)

안녕하세요 오랫만에 글을 쓰게 됩니다. notion을 별도로 관리를 하다보니 블로그에 글을 쓰는 빈도가 너무 적어지게 되는데요 그래도 꾸준히 써서 좋은 글을 여러분께 공유드리도록 최선을 다하

daje0601.tistory.com

그럼 실행하는 방법도 모두 알아보았습니다. 그럼 이 서버가 어떻게 작동하는지를 알아보도록 하겠습니다. 

 

 

3. 어떻게 작동하는지?

코드를 쭉 읽어보시면, 아래 순서와 같이 작동을 하는지는 쉽게 알 수 있습니다. 

class Application : serve
↓
class GoogleSearchServer

그런데, 이 후 do_POST라는 함수가 작동이 되는데 디버그를 해보면 실제로 그냥 건너뛰게 됩니다. 

왜 이렇게 작동을 하는지 몰라서 한참을 고민하던 끝에 이유를 발견하였습니다. 

먼저, 다시 한번 실행되는 순서를 확인하기 위해 import traceback을 하여 call 되는 순서를 출력해보았습니다. 

class SearchABC(http.server.BaseHTTPRequestHandler):
    def do_POST(self):
        """ Handle POST requests from the client. (All requests are POST) """

        #######################################################################
        # Prepare and Parse
        #######################################################################
        for line in traceback.format_stack():
            print(line.strip())

위와 같이 for문을 추가하면, 아래와 같은 결과가 출력이 됩니다. 

Host: 0.0.0.0:8080
File "/home/.conda/envs/parlai/lib/python3.8/threading.py", line 890, in _bootstrap
    self._bootstrap_inner()
File "/home/.conda/envs/parlai/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
File "/home/.conda/envs/parlai/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
File "/home/.conda/envs/parlai/lib/python3.8/socketserver.py", line 683, in process_request_thread
    self.finish_request(request, client_address)
File "/home/.conda/envs/parlai/lib/python3.8/socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
File "/home/.conda/envs/parlai/lib/python3.8/socketserver.py", line 747, in __init__
    self.handle()
File "/home/.conda/envs/parlai/lib/python3.8/http/server.py", line 427, in handle
    self.handle_one_request()
File "/home/.conda/envs/parlai/lib/python3.8/http/server.py", line 415, in handle_one_request
    method()
File "search_server.py", line 92, in do_POST
    for line in traceback.format_stack():

아! threading.py라는 파일로 넘어가는구나를 확인할 수 있고, 거기에서 handle_one_request라는 코드를 보면 되겠구나를 확인하였습니다. 

    def handle_one_request(self):
        """Handle a single HTTP request.

        You normally don't need to override this method; see the class
        __doc__ string for information on how to handle specific HTTP
        commands such as GET and POST.

        """
        try:
            self.raw_requestline = self.rfile.readline(65537)
            if len(self.raw_requestline) > 65536:
                self.requestline = ''
                self.request_version = ''
                self.command = ''
                self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
                return
            if not self.raw_requestline:
                self.close_connection = True
                return
            if not self.parse_request():
                # An error code has been sent, just exit
                return
            mname = 'do_' + self.command
            if not hasattr(self, mname):
                self.send_error(
                    HTTPStatus.NOT_IMPLEMENTED,
                    "Unsupported method (%r)" % self.command)
                return
            method = getattr(self, mname)
            method()
            self.wfile.flush() #actually send the response if not already done.
        except socket.timeout as e:
            #a read or a write timed out.  Discard this connection
            self.log_error("Request timed out: %r", e)
            self.close_connection = True
            return

위 코드를 천천히 읽어보시면, command 라인에 입력된 command를 가지고 와서 앞에 do_를 붙히는 것을 볼 수 있습니다. 

그런 후 getattr로 해당 텍스트의 속성 값을 호출하게 되고, 그 호출된 것을 method로 정의한 후 실행하는 코드를 볼 수 있습니다. 

참 오래오래 디버깅한 끝에 알아냈습니다. 인공지능만 잘하면 되는 것이 아니라 이런 부분도 빠르게 읽을 수 있다면 연구하는데 속도가 날 것이라고 생각이 드는 하루였습니다. 

 

이렇게 출력된 값이 오게 되니 이후 커스터마이징하면 제가 원하는 값을 처리 할 수 있을 듯 싶습니다. 

 

SeeKeR를 사용하시는 분들을 위해 SeeKeR 샘플 코드도 함께 기재 드립니다. 

# SeeKeR
python -m parlai.scripts.interactive -mf zoo:seeker/seeker_dialogue_400M/model \
-o /Users/kds/Documents/openchat/workspace/ko_parlai/parlai/opt_presets/gen/seeker_dialogue \
--search-server 0.0.0.0:8080
# 이렇게 --search-server라는 명령어를 입력하여 사용하시면 됩니다.

 

이상 글을 마치겠습니다. 

 

감사합니다. 

728x90
반응형
Comments