GPT in action - 1. chatGPT 로 만드는 지능형 챗봇

Dongyoung Kim·2023년 3월 23일
8

GPT

목록 보기
9/12
post-thumbnail

최근 저희 팀에서는 GPT 의 fine tuning 에 앞서 GPT 의 In-context learning 을 활용하여 별도의 fine tuning 없이 활용하는 방법도 시도해보고 있습니다. 이러한 방법은 여러모로 장점도 많고, 실제로 잘 동작하는것으로 평가하고 있습니다.

GPT fine tuning 외에도 GPT 를 사용하는 입장에서 GPT in action 이라는 시리즈로 GPT API 를 기반으로 도구를 제작하는것에 대해 설명해보고자 합니다. 여러 경우의 업무 적용 가능성을 타진해보았고, 이번 포스트에서는 챗봇 구축에 대한 이야기를 해보고자 합니다.

chatbot

이루다나 chatGPT 서비스와 같이 open domain 에대한 챗봇을 제외한다면, 일반적으로 기업이 원하는 챗봇은 컨트롤이 가능해야 합니다. 여기서 컨트롤이라 함은 K은행 챗봇이 W은행 상품을 홍보해선 한되는 등 입니다. 즉 어느정도 정해진 루틴 내에서 대화할수 있는 챗봇을 만들어야 하며, 이는 일반적으로 다이얼로그 플로우가 존재하는 선 내에서 대화를 진행하게 됩니다. 특히 특정 업무를 수행해야 하는 챗봇, 예를 들어 금융회사의 예금 송금 등 과 같은, 이라면 더더욱 규칙기반의 작업을 수행해야 하겠습니다.

이러한 방법은 답변하는 챗봇입장에서는 문제가 없지만 질문하는 사람 입장에서는 문제가 많습니다. 뭘 물어봐도 재대로 이해도 못하고 답도 못하는 챗봇이라서지요. 이는

  1. 자연어로 입력된 쿼리에 대해 챗봇이 그 의미를 이해하지 못함
  2. 의도는 알아들었지만 의도에 대한 액션 (답변) 의 매칭이 실패
  3. 준비된 다이얼로그가 없어 "죄송합니다" 만 이야기
  4. 질문도 잘 알아듣고 답도 한것 같은데 out-dated 된 내용 (잘못된 내용)
  5. 다 재대로 답하긴 했는데, 로봇같은 똑같은 답변

이를 해결하려면

  1. 사용자의 의도를 파악할수 있는 충분한(?) NLP 엔진 개발 팀
  2. 무조건 맞추는 최고의 검색 엔진 개발 팀
  3. 모든 경우의 수를 커버할 수 있는 다이얼로그 제작 팀
  4. 변경되는 정보에 맞추어 up-to-date 다이얼로그 제작 팀
  5. 변형 답변들을 준비하는 다이얼로그 제작 팀

을 운영하면 될지도 모르겠습니다. 다만 하나의 챗봇을 만들자고 이 많은 노력과 인력이 투입되어야 한다면 CS 팀이 효과적일지도 모르겠습니다.

GPT-3 의 등장과 함께 위의 문제중 5번은 해결이 되었고, 다이얼로그 플로우와 함께 주어진 문장을 변형해서 답변하게 하므로 좀더 자연스러운 챗봇을 구현하는 등의 시도등이 있었습니다. 하지만 이는 여전히 의도를 파악하고 이에 대한 적절한 답변을 구사하는 챗봇과는 거리가 있습니다.

그리고 '23년 현재, gpt-4 및 chatGPT (gpt-3.5) 의 등장과 함께, 완벽할순 없지만 상당부분 위 문제가 해결이 되었고 정말 말귀를 알아듣고 적절한 답변을 하는 챗봇이 나온것 같습니다. 특히 이 chatGPT, gpt-4 가 매우 똑똑해서 의도를 적절히 설명하고 명령하는것만으로도 우리가 원하는 custom chatbot 을 구성해 볼 수 있습니다. 이를위한 별도의 fine-tuning 은 필요하지 않습니다.

custom chatbot with GPT-3.5/4 APIs

이하의 내용은 매우 개인적인 의견들입니다. 참고만 부탁드립니다.

포스트를 작성하는 현재 시점으로, chatbot 을 만들수 있는 api 는 openai 의 gpt-3.5 및 gpt-4 뿐으로 판단됩니다. 우선 국내외 다른 gpt 도 많지만 타사의 모델에서는 chat api 를 지원하지 않을 뿐더러, 아래 설명하는 수준의 고난도 언어 테스크의 수행은 gpt-3 가 아닌 gpt-3.5, gpt-4 의 매우 고성능을 필요로 합니다.

chat

openai 에서는 chat task 에 대한 api 를 별도로 지원합니다 (https://platform.openai.com/docs/guides/chat/introduction). 이 api 에서 주요한 점은 message 에 3가지의 role, i.e. system, user, assistant 가 나누어 져 있고, system 을 통해 in-context learning 을 수행할 수 있습니다. 예를 들면 아래와 같습니다.

openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "너는 K은행의 친절한 은행원이야."},
        {"role": "user", "content": "사용자 질문 1"},
        {"role": "assistant", "content": "답변 1"},
        {"role": "user", "content": "사용자 질문 2"}
    ]
)

대화의 구현은 해당 api 에 대화를 추가하는 형식으로 진행하면 됩니다. 단 대화가 길어질수록 토큰이 길어지니 비용이 비싸지고, 넘우 길어질 경우 길이제한에 overflow 가 일어나므로 적절히 과거 대화를 지워야 합니다.

위 방법을 통해 자유대화를 할 수 있는 챗봇을 만들 수 있습니다. 이 챗봇은 이미 사용자의 의도를 충분히 잘 파악할 수 있고, 어느정도 지식 (parametric memory) 를 갖추고 있어 웬만한 대화가 가능하며, 형식적인 답변이 아닌 다양한 답변을 할 수 있는 챗봇입니다. 즉 앞에서 문제가 되었던 부분의 대부분을 해결할 수 있습니다만, 우리의 의도 또는 특정 도메인 (회사) 의 답변을 하지는 못합니다.

위 예시에서 system 메시지를 통해 in-context learning 을 수행할 수 있다고 하였습니다. 즉 우린 system message 의 내용을 통해 이 챗봇을 친절한 K은행 챗봇으로도, 무서운 X사채의 챗봇으로도 만들수 있습니다. 뿐만 아니라, 우리가 원하는 의도 및 특정 도메인의 정보를 기반으로 답변할 수 있도록 이 system message 넣고 nonparametric 정보를 통해 답변을 도출시킬수 있습니다. 즉 우린 이 api system message 에 user query 에 적당한 문서를 찾고, 이를 넣어 적당히 답변하도록 명령하는것입니다.

user query 에 맞는 context 의 문서를 찾기위해서 일반적인 검색엔진을 사용할 수 있겠습니다. 또는 gpt-3 를 검색용으로 사용할 수도 있습니다. api 로 공개된 모델 중 text-embedding-ada-002 은 주어진 텍스트에 대한 임베딩을 만들어주는 모델로, 두 문장의 임배딩 유사도를 계산함으로서 문맥적 검색을 수행할 수 있습니다. 즉 챗봇이 대답하고자 하는 내용을 포함한 FAQ 가 있다고 가정하면, 이를 임베딩 벡터 데이터베이스로 만들어두고, 사용자의 요청을 벡터화 해서 임베딩 벡터 데이터베이스의 벡터들과 유사도를 측정, 가장 유사한 내용을 가지고 오는것입니다. 참고로 gpt-3 의 embedding 을 통한 검색은 엉망입니다. 공식문서 (https://openai.com/blog/new-and-improved-embedding-model) 에 설명하고 있지만, 웬만한 encoder 모델의 embedding 성능에 비하면 한참 떨어집니다.

embedding api 사용법에 대해서는 openai 공식문서에 잘 설명되어 있습니다.(https://platform.openai.com/docs/guides/embeddings/what-are-embeddings). 유사도 측정은 faiss 라이브러리의 cosine similarity 를 이용하면 빠르고 편리합니다 (https://github.com/facebookresearch/faiss).

chat gpt + search gpt = custom chatbot

위 설명 내용을 그림으로 그려보면 다음과 같습니다.

위 구성에서는 두개의 모델, gpt-3 및 gpt-3.5/gpt-4 가 사용됩니다. gpt-3 는 embedding 수행을 통해 FAQ 문서중 질문의 맥락적으로 가장 유사한 문서를 찾습니다. gpt-3.5/gpt-4 는 유사한 문서와 사용자의 질문, 시스템 명령 (회사규정이나 답변 스타일 등) 그리고 이전의 사용자와의 질문답변 내용을 모두 종합하여 적절한 답변을 하게 됩니다.

엄청 단순한 구성입니다. 이게 진짜 작동할까요? gpt-3 의 검색도 시원찮은데도요? 네 정말 오롯히 gpt-3.5 및 gpt-4 의 성능에 의존하여 잘 작동됩니다. 위 시스템의 구현은 사실상 두번의 api call 로 이루어지기에 그 코드도 몇줄 되지 않습니다.

참고로 아래 코드는 gpt-4 가 짜준 코드입니다. 작동시켜보진 않았는데, 일단 제가 짠 코드보다 퀄리티는 좋아 보입니다 :) 코드 작성에 있어서는 gpt-4 가 확실히 잘하는것 같습니다.

🐰 Dongyoung:
다음은 챗봇 시스템 설명이야. 이 챗봇은 사용자의 입력을 받고, 관련된 내용을 사전에서 찾고, 그 내용을 기반으로 대답하는 챗봇이야.
우선 사용자의 입력이 오면 그 입력을 embedding api 를 콜해서 embedding 을 만들고, 기존에 가지고 있는 임베딩 사전을 이용, cosine similarity 를 최대한 빠르게 (faiss 이용) 계산해서 유사한 문서를 사전에서 찾아내고, 그 문서의 내용과 사용자의 입력, 그리고 시스템 메시지를 더해서 채팅 api 를 호출해서 답변을 만들어. 이 과정은 반복되고 채팅 api 에 추가되어 호출되게 되.
embedding api 는 다음과 같아

def get_embedding(text, model="text-embedding-ada-002"):
   text = text.replace("\n", " ")
   return openai.Embedding.create(input = [text], model=model)['data'][0]['embedding']

채팅 api 는 다음과 같아

import openai
openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Who won the world series in 2020?"},
        {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
        {"role": "user", "content": "Where was it played?"}
    ]
)

채팅 api 에 들어가는 시스템 메시지는 다음과 같아 "너는 친절한 K사 은행원이야. K은행이라고 소개하고 모르는 답변이 있으면 고객센터 1234-5678 로 연락달라 그래." + 유사 문서 내용
채팅 api 에 들어가는 user 메시지는 사용자의 입력이야
만약 이전에 대화 내용이 있다면 user 메시지와 assistant 메시지를 이전 내용으로 채워서 채팅 api 에 넣어줘.
위 내용을 바탕으로 챗봇 백엔드 시스템을 python 으로 만들어줘

🦄 ChatGPT:
아래는 챗봇 백엔드 시스템을 Python 클래스로 구현한 예제입니다. 필요한 라이브러리들을 설치하고 코드를 실행해보세요.

import openai
import faiss
import numpy as np

class Chatbot:
    def __init__(self, index, embeddings, documents, system_message):
        self.index = index
        self.embeddings = embeddings
        self.documents = documents
        self.system_message = system_message
        self.chat_history = []

    def get_embedding(self, text, model="text-embedding-ada-002"):
        text = text.replace("\n", " ")
        return openai.Embedding.create(input=[text], model=model)["data"][0]["embedding"]

    def find_similar_document(self, user_embedding):
        _, top_indices = self.index.search(np.array([user_embedding]), 1)
        top_index = top_indices[0][0]
        return self.documents[top_index]

    def chat(self, user_input):
        user_embedding = self.get_embedding(user_input)
        similar_document = self.find_similar_document(user_embedding)
        system_message = self.system_message + " " + similar_document

        messages = [{"role": "system", "content": system_message}]
        for message in self.chat_history:
            messages.append(message)
        messages.append({"role": "user", "content": user_input})

        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=messages
        )
        assistant_message = response.choices[0].message.content

        self.chat_history.append({"role": "user", "content": user_input})
        self.chat_history.append({"role": "assistant", "content": assistant_message})

        return assistant_message

# 데이터 및 사전 생성 예시
documents = ["문서1 내용", "문서2 내용", "문서3 내용"]
embeddings = [get_embedding(doc) for doc in documents]
embedding_matrix = np.array(embeddings)
index = faiss.IndexFlatL2(embedding_matrix.shape[1])
index.add(embedding_matrix)

system_message = "너는 친절한 K사 은행원이야. K은행이라고 소개하고 모르는 답변이 있으면 고객센터 1234-5678 로 연락달라 그래."

# 챗봇 객체 생성
chatbot = Chatbot(index, embeddings, documents, system_message)

# 사용자 입력 처리 예시
user_input = "예금 계좌 개설 방법이 어떻게 되나요?"
response = chatbot.chat(user_input)
print(response)

위 코드는 챗봇 시스템을 구현한 예시입니다. 사용자 입력을 받아 적절한 응답을 생성하는 방식으로 작동합니다. 이 코드를 실행하려면 먼저 필요한 라이브러리들을 설치해야 합니다.

operation

아래 그림은 이런 방식으로 구현한 챗봇의 실제 동작 예시 입니다. 아래와 같은 작업을 다이얼로그 플로우 등 기존의 챗봇으로 구현하려면 상당히 난이도가 높다는걸, 해보신분들은 더 잘 아실것 같습니다. 여기서 놀라운건 이런 챗봇의 구현이 단순히 gpt-3.5 api call 로만 가능하다는 것입니다.

  1. 위 대화에 있어서 사용자는 본인 이름과 나이를 밝혔습니다. 나이 만으로 미성녕자인것을 우선 search gpt 에서 맥락적으로 찾아주었구요, chat gpt 는 이 내용을 기반으로 적절히 답했습니다.
  2. 사용자가 부모님이 와서 해주면 되냐는 질문에 대해서 search gpt 는 동일한 문서를 찾아서 리턴하고, chat gpt 는 주어진 정보 및 이전 채팅을 참조해서, 사용자의 이름 및 그 내용을 적절하게 말해줍니다.
  3. 마지막에 인사에서 search gpt 는 사실 잘못된 문서를 리턴했습니다 (유사한 문서가 없었던것이죠). 하지만 chat gpt 는 사용자의 의도가 작별인사임을 알고 주어진 문서와 관계없이 친절히 답했습니다.

custom chatbot and more

gpt 의 성능을 보면, 이런 챗봇 테스크는 쉬운것 같습니다. 사실 이미 자유대화와 인지능력이 있는 모델로 특정 도메인으로 제한하는건은 단순히 명령을 내리는 수준에서 충분히 가능합니다.

chatbot 을 만드는 입장에서, 단순 질문 답변 및 대화 외에도, backend system 과 연계시켜 업무를 처리하는 등의 시나리오 작업이 가능합니다. 이런경우에도 search gpt 와 적절히 연계하여 그 답변을 업무로 나가게끔 한다면 업무처리가 가능한 챗봇으로도 사용 가능할 것 같습니다. 다만 이와 같은 방법은 여전히 다이얼로그 플로우 기반으로 작동되는 챗봇같습니다.

대화는 gpt 가 스스로 판단해서 잘 하는데 업무라고 못할까요? gpt 가 어떤 업무를 할지 판단하고, 업무 방법을 배워서 수행하게 하면 되지 않을까요? 저는 개인적으로 충분히 가능하다고 봅니다. 본 포스트 작성일 기준 어제 일자로 Microsoft 에서 gpt-4 에 대한 일련의 실험결과를 작성했고, 해당 보고서 5장에 이러한 접근이 가능함을 보여주는 내용이 있습니다. (Bubeck, S., Chandrasekaran, V., Eldan, R., Gehrke, J., Horvitz, E., Kamar, E., Lee, P., Lee, Y. T., Li, Y., Lundberg, S., Nori, H., Palangi, H., Ribeiro, M. T., & Zhang, Y. (2023). Sparks of Artificial General Intelligence: Early experiments with GPT-4. https://arxiv.org/abs/2303.12712v1).

이렇게 되면, 정말 인공지능이 사람을 대체하는건 얼마 남지 않은것 처럼 보입니다. 또는 벌써 시작된것 같습니다.

profile
AI professional for science and industry

9개의 댓글

comment-user-thumbnail
2023년 3월 25일

시리즈 전부 정말 잘 읽었습니다. 궁금한 부분들을 푸는 데에 큰 도움이 되었습니다. 감사합니다.

1개의 답글
comment-user-thumbnail
2023년 3월 28일

정말로 시간가는 줄 모르고 대단히 재밌게 잘 읽었습니다.
다음 포스트도 기대하고 기다리고 있겠습니다.
감사합니다.

1개의 답글
comment-user-thumbnail
2023년 4월 20일

댓글을 안 남길 수가 없네요.. 정말 유용한 정보 쉽게 기술해 주셔서 감사합니다~ ^^

1개의 답글
comment-user-thumbnail
2023년 4월 28일

시리즈 정말 잘 읽었습니다 .
혹시 저도 출처를 남기고 블로그에 내용을 인용해도 괜찮을까요...?

1개의 답글
comment-user-thumbnail
2023년 8월 2일

재밌고 유익하네요~! 술술 읽히기도하구요! 좋은 글 감사합니다!

답글 달기