ChatGPT 하이퍼파라미터

OpenAI의 GPT 모델들의 API 호출시 사용 가능한 하이퍼파라미터들이 있습니다. 하이퍼파라미터는 훈련되는 신경망 가중치가 아니라 사용자가 직접 입력하는 매개변수로, 모델의 응답에 영향을 미치게 됩니다. 주요 하이퍼파라미터들은 다음과 같습니다.

하이퍼파라미터기본값범위
temperature10.0~2.0
top-p10.0~1.0
max_tokensinf정수
frequency_penalty0-2.0~2.0
presence_penalty0-2.0~2.0
Hyperparameters

각 하이퍼파라미터를 통해 얻을 수 있는 결과를 살펴봅시다.

temperature

결과의 다양성과 예측 가능성을 조절합니다. 값이 클수록 더 무작위적인 응답(더 창의적인 응답)을 얻을 수 있습니다. 예측 가능하고 일관된 응답을 얻어야 할 경우에는 0에 가까운 값을 사용합니다. 보통 아래 나오는 top_p나 temperature 중 하나를 조정합니다.

top_p

단어 샘플링시 사용하는 확률 분포의 상위 부분을 의미합니다. 예를 들어, top_p=0.9라면 확률 분포 상위 90% 내에서만 단어를 샘플링합니다. 이렇게 되면 이상치를 제거하는 효과를 얻을 수 있겠죠. top_p=0.1이라면 상위 10% 내의 확률 분포 내에서만 단어를 샘플링하므로 좀 더 예측 가능한 결과를 얻을 수 있습니다.

max_tokens

답변의 최대 토큰 수를 제한할 때 사용합니다.

presence_penalty

0보다 큰 값을 사용할 경우 앞에서 나왔던 단어(토큰)가 또 나오는 것을 억제합니다. 즉, 새로운 내용을 이야기할 가능성이 높아집니다.

frequency_penalty

0보다 큰 값을 사용할 경우 특정 단어(토큰)의 출현 빈도가 높아지는 것을 억제합니다. 즉, 같은 말을 문자 그대로 반복하는 것을 막습니다.

예시

OpenAI Playground에서는 직접 API를 호출하지 않고도 위의 하이퍼파라미터들을 바꿔가며 테스트해볼 수 있습니다. ChatGPT에서도 메시지 입력시 아래와 같이 하이퍼파라미터를 입력하여 응답 결과를 조정할 수 있습니다.

예시: temperature=0.1

나는 대학생이야. 다음 괄호에 들어갈만한 장소를 10군데 적어줘. temperature=0.1

나는 내일 ( )에 갈 것이다. 
=====
주어진 문장에서 괄호에 들어갈 수 있는 장소를 10군데 제안해 드립니다:

나는 내일 (도서관)에 갈 것이다.
나는 내일 (학교)에 갈 것이다.
나는 내일 (카페)에 갈 것이다.
나는 내일 (식당)에 갈 것이다.
나는 내일 (공원)에 갈 것이다.
나는 내일 (영화관)에 갈 것이다.
나는 내일 (체육관)에 갈 것이다.
나는 내일 (박물관)에 갈 것이다.
나는 내일 (친구 집)에 갈 것이다.
나는 내일 (도시 쇼핑몰)에 갈 것이다.

예시: temperature=1.0

나는 대학생이야. 다음 괄호에 들어갈만한 장소를 10군데 적어줘. temperature=1.0

나는 내일 ( )에 갈 것이다. 
=====
온도 (temperature)가 높은 경우, 모델은 더 다양한 창조적인 답변을 생성할 수 있습니다. 다음은 온도가 높을 때 가능한 10개의 장소 예시입니다:

나는 내일 (자연휴양림)에 갈 것이다.
나는 내일 (물놀이공원)에 갈 것이다.
나는 내일 (미술갤러리)에 갈 것이다.
나는 내일 (음악 페스티벌)에 갈 것이다.
나는 내일 (카페트매장)에 갈 것이다.
나는 내일 (과학 박물관)에 갈 것이다.
나는 내일 (헬스 클럽)에 갈 것이다.
나는 내일 (해변)에 갈 것이다.
나는 내일 (캠핑장)에 갈 것이다.
나는 내일 (동물원)에 갈 것이다.
이것들은 더 다양한 장소를 나타내는 창조적인 예시입니다.

예시: top_p=0.1

나는 대학생이야. 다음 괄호에 들어갈만한 행동을 10가지 적어줘. top_p=0.1

나는 1시간 후 ( )을/를 할 것이다. 
=====
top_p (penalty) 값이 낮은 경우, 모델은 더 확실한 예상 답변에 가까운 답변을 생성합니다. 따라서 가능한 10가지 행동 예시는 다음과 같습니다:

나는 1시간 후 (공부)를 할 것이다.
나는 1시간 후 (운동)을 할 것이다.
나는 1시간 후 (음악 듣기)를 할 것이다.
나는 1시간 후 (친구와 만남)을 할 것이다.
나는 1시간 후 (요리)를 할 것이다.
나는 1시간 후 (영화 보기)를 할 것이다.
나는 1시간 후 (독서)를 할 것이다.
나는 1시간 후 (산책)을 할 것이다.
나는 1시간 후 (미술 활동)을 할 것이다.
나는 1시간 후 (봉사 활동)을 할 것이다.
이러한 답변은 top_p 값이 낮아 더 일반적이고 확실한 행동을 나타냅니다.

예시: top_p=1.0

나는 대학생이야. 다음 괄호에 들어갈만한 행동을 10가지 적어줘. top_p=1.0

나는 1시간 후 ( )을/를 할 것이다. 
=====
top_p (penalty) 값이 높은 경우, 모델은 더 다양하고 창조적인 답변을 생성할 수 있습니다. 따라서 가능한 10가지 행동 예시는 다음과 같습니다:

나는 1시간 후 (하늘을 바라볼 것이다).
나는 1시간 후 (별을 관찰할 것이다).
나는 1시간 후 (음악을 연주할 것이다).
나는 1시간 후 (그림을 그릴 것이다).
나는 1시간 후 (배낭을 싸고 여행을 떠날 것이다).
나는 1시간 후 (심리적인 독서를 할 것이다).
나는 1시간 후 (새로운 요리를 시도할 것이다).
나는 1시간 후 (사진을 찍으러 나갈 것이다).
나는 1시간 후 (봉사 활동을 할 것이다).
나는 1시간 후 (친구와 대화를 나눌 것이다).
이러한 답변은 top_p 값이 높아 더 창조적이고 다양한 행동을 나타냅니다.

프롬프트 엔지니어링: 좋은 프롬프트 작성하기

OpenAI 사이트에 소개된 챗GPT 프롬프트 엔지니어링 Best practices를 소개해드립니다.

1. 최신 모델 사용하기

현재 챗GPT 최신 모델은 gpt-3.5-turbo와 gpt-4입니다.

2. 앞부분에 Instruction 쓰고 ###, “”” 같은 기호를 이용해 Context와 분리하기

안 좋은 예

Summarize the text below as a bullet point list of the most important points.

{text input here}

좋은 예

Summarize the text below as a bullet point list of the most important points.

Text: """
{text input here}
"""

3. 구체적으로 쓰기, 자세히 설명하기(상황, 원하는 결과, 출력 형식, 길이, 스타일 등)

안 좋은 예

Write a poem about OpenAI.

좋은 예

Write a short inspiring poem about OpenAI, focusing on the recent DALL-E product launch (DALL-E is a text to image ML model) in the style of a {famous poet}

4. 출력 형식 자세히 알려주기

안 좋은 예

Extract the entities mentioned in the text below. Extract the following 4 entity types: company names, people names, specific topics and themes.

Text: {text}

좋은 예

Extract the important entities mentioned in the text below. First extract all company names, then extract all people names, then extract specific topics which fit the content and finally extract general overarching themes

Desired format:
Company names: <comma_separated_list_of_company_names>
People names: -||-
Specific topics: -||-
General themes: -||-

Text: {text}

5. Zero-shot으로 안 되면 few-shot, few-shot으로 안 되면 fine-tune

Zero-shot

Extract keywords from the below text.

Text: {text}

Keywords:

Few-shot

Extract keywords from the corresponding texts below.

Text 1: Stripe provides APIs that web developers can use to integrate payment processing into their websites and mobile applications.
Keywords 1: Stripe, payment processing, APIs, web developers, websites, mobile applications
##
Text 2: OpenAI has trained cutting-edge language models that are very good at understanding and generating text. Our API provides access to these models and can be used to solve virtually any task that involves processing language.
Keywords 2: OpenAI, language models, text processing, API.
##
Text 3: {text}
Keywords 3:

6. 애매모호한 설명 쓰지 않기

안 좋은 예

The description for this product should be fairly short, a few sentences only, and not too much more.

좋은 예

Use a 3 to 5 sentence paragraph to describe this product.

7. 하지 말라는 명령 대신 하라는 명령 사용하기

안 좋은 예

The following is a conversation between an Agent and a Customer. DO NOT ASK USERNAME OR PASSWORD. DO NOT REPEAT.

Customer: I can’t log in to my account.
Agent:

좋은 예

The following is a conversation between an Agent and a Customer. The agent will attempt to diagnose the problem and suggest a solution, whilst refraining from asking any questions related to PII. Instead of asking for PII, such as username or password, refer the user to the help article www.samplewebsite.com/help/faq

Customer: I can’t log in to my account.
Agent:

8. 코드 생성시 코드 시작 구문을 보여줘서 LLM이 해당 구문으로 시작하는 패턴을 따르도록 하기

안 좋은 예

# Write a simple python function that
# 1. Ask me for a number in mile
# 2. It converts miles to kilometers

좋은 예

# Write a simple python function that
# 1. Ask me for a number in mile
# 2. It converts miles to kilometers
 
import

기타

위 내용들 외에도 영어로 명령하기(보통 영어로 명령했을 때 더 좋은 결과가 나옵니다), 역할 부여하기, 자기 목적에 맞는 프롬프트를 여러 가지 실험해보기, 다른 사람의 좋은 프롬프트 보고 배우기 등의 방법이 있습니다.

좀 더 복잡한 문제 해결을 위한 Chain-of-Thought, Tree-of-Thought, Self-Consistency, ReAct 등의 프롬프팅 프레임워크도 계속 발표되고 있습니다.

ChatGPT API 사용하기

ChatGPT vs GPT

이해를 돕기 위해 제목에 ChatGPT라고 썼지만, 엄밀히 말하면 대화형 ChatGPT와 OpenAI API를 통해 사용하는 GPT 모델은 다른 모델입니다. 대화형 ChatGPT는 자연스러운 대화를 위해 GPT 모델을 별도로 훈련시킨 모델입니다. 특히, 이전 대화 내용을 기억하는 기능이 추가되어 있죠. API를 통해 이용하는 GPT 모델들은 대화의 맥락을 기억하는 기능이 없기 때문에 API를 이용해 대화를 이어가기 위해서는 사용자가 이전 대화의 내용을 입력으로 전달해야 합니다.

파이썬으로 API 사용하기

파이썬에서는 openai 패키지를 설치하면 OpenAI API를 쉽게 사용할 수 있습니다. 명령줄에서 다음을 실행합니다.

pip install openai

설치 후에는 openai 패키지를 import하고 API Key를 지정해줍니다. 아래 예제에서는 환경변수로 지정해놓은 API Key를 가지고 왔습니다.

import os
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")

ChatCompletion

OpenAI 문서에 있는 예제를 보겠습니다. ChatCompletion.create를 이용해 LLM 모델을 사용할 수 있습니다.

response = 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?"}
    ]
)

위 코드에서 model에는 OpenAI에서 제공하는 LLM 모델들 중 원하는 것을 적습니다. messages는 LLM에 전달할 입력입니다. 입력값을 보면 rolecontent로 이루어진 딕셔너리들의 배열임을 알 수 있습니다.

Role

role에는 “system”, “user”, “assistant” 중 하나가 오는데, “system”과 “user”는 사용자가 LLM에 요청하는 내용이고, “assistant”는 LLM의 응답입니다. 앞에서 API를 통해 사용하는 LLM은 이전 대화 내용을 기억하지 못한다고 했죠? 위 코드는 이전 대화를 기억할 수 있도록 전달하는 코드임을 알 수 있습니다. “system” 메시지는 LLM에게 대화 전체에 적용될 역할 등을 지정해주는 메시지로, 보통 대화 처음에 한 번 나오는데, 꼭 필요한 것은 아닙니다. 생략하면 “You are a helpful assistant.”라고 주는 것과 유사합니다. 이후에는 보통 “user”와 “assistant”가 번갈아 나옵니다.

위 예제에서는 modelmessages를 입력했는데, 이 두 항목은 필수로 입력해야 하고, 그 외에도 하이퍼파라미터들과 기타 여러 가지 항목들이 있습니다.

Response

결과는 다음과 같이 확인합니다. 여러분이 실행하면 결과가 약간씩 다르게 나올 수 있습니다. 아래 있는 제 실행 결과도 OpenAI 문서에 나온 결과와 단어 순서에 약간 차이가 있습니다.

print(response['choices'][0]['message']['content'])
# 실행 결과
# The World Series in 2020 was played at Globe Life Field in Arlington, Texas.

결과 확인하기가 좀 복잡하죠? response 전체를 출력해보면 다음과 같이 다른 정보들을 포함하고 있는 것을 알 수 있습니다. 사실 response는 chat completion 객체로, 세부 항목 설명은 여기에 있습니다.

{
  "id": "chatcmpl-7wi85qbDjFeDclPTjepMccfCbiWHa",
  "object": "chat.completion",
  "created": 1694227013,
  "model": "gpt-3.5-turbo-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The World Series in 2020 was played at Globe Life Field in Arlington, Texas."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 53,
    "completion_tokens": 18,
    "total_tokens": 71
  }
}

API를 이용한 대화

LLM을 한 번만 호출한다면 위와 같이 실행하면 되고, LLM과 대화를 이어가고 싶다면 반복문 내에서 이전 대화 내용을 누적해서 다음 입력으로 전달해주면 됩니다.

대화가 길어지면 입력 토큰 수가 크게 증가하겠죠? 비용도 문제지만, 토큰 수 제한에 걸릴 수도 있습니다.

그럴 경우 이전 대화 내용을 요약해서 전달하면 됩니다. 요약하는 것 역시 LLM을 이용하죠. 현재 대화와는 별도로 API를 호출하면서 이전 대화 내용을 전달하고 요약해달라고 하면 됩니다.

이 때 이전 대화 전체를 전달하며 요약할 수도 있고, 기존 요약된 내용에 새로운 대화를 추가해서 전달하며 요약할 수도 있습니다. 이전 대화 전체를 전달하면 과거 내용을 유지할 수 있지만 토큰이 많이 필요하고, 요약된 내용에 새로운 대화를 추가해서 요약하면 토큰 수를 줄일 수 있지만, 과거 대화 내용이 여러 번 요약되며 사라질 수 있습니다.

OpenAI API (GPT) 사용 준비

OpenAI에서는 다양한 서비스들을 API를 이용해 사용할 수 있도록 REST API를 제공하고 있습니다. 사용할 수 있는 모델들에는 GPT-3.5, GPT-4 외에도 DALL-E (그림 그리기), Whisper (음성 인식), Embeddings (텍스트 임베딩) 등이 있습니다. API를 사용하기 위해서는 OpenAI API 사이트에 회원 가입sk-로 시작하는 API Key를 받아야 합니다. API는 사용료가 있는데, 처음 가입하면 3개월간 쓸 수 있는 $5를 제공하므로, 여러 가지 테스트를 해볼 수 있습니다.

LLM 모델 – 성능, 가격, 토큰 수

LLM 모델은 gpt-3.5-turbo나 gpt-4를 사용하면 됩니다. 물론 gpt-4의 성능이 더 좋지만 비용이 더 비싸므로 gpt-3.5-turbo의 성능으로 충분한 경우에는 해당 모델을 사용하면 됩니다. 아래 표에 이 글을 작성하는 시점의 가격을 적었지만 앞으로 바뀔 수 있으므로 최신 가격은 OpenAI 사이트에서 확인하시기 바랍니다.

LLM 모델에는 입출력 토큰 수 제한이 있습니다. 토큰 수는 단어 수보다 많죠. 입력과 출력 토큰의 합계로 제한하는데, gpt-3.5-turbo 모델의 경우 4K, gpt-4의 경우 8K 토큰을 지원합니다. 더 많은 토큰이 필요한 경우에 사용할 수 있는 gpt-3.5-turbo-16kgpt-4-32k 모델도 있습니다. LLM 모델 정보는 여기에서 확인할 수 있습니다.

ModelMax TokensInput Price
(per 1K tokens)
Output Price
(per 1K tokens)
gpt-3.5-turbo4,097$0.0015$0.002
gpt-3.5-turbo-16k16,385$0.003$0.004
gpt-48,192$0.03$0.06
gpt-4-32k32,768$0.06$0.12
OpenAI LLM Models

참고로, gpt-3.5-turbo 모델의 경우 자신의 데이터를 이용해 Fine Tuning할 수 있습니다. 훈련 가격은 $0.008/1K, 입력 가격은 $0.012/1K, 출력 가격은 $0.016/1K로, 기본 gpt-3.5-turbo 모델보다 8배 비싸네요.

임베딩 모델

텍스트 임베딩이 필요할 경우 text-embedding-ada-002 모델을 사용하면 됩니다. 이 때 임베딩 모델에서 사용하는 토크나이저는 cl100k_base, 최대 입력 토큰수는 8192, 출력 벡터는 1536 차원입니다. 임베딩 모델 정보는 여기에서 확인할 수 있습니다. API 사용 가격은 $0.0001/1K로, 1달러를 쓰기 위해서는 10,000K 토큰이 필요하네요. LLM에 비해 임베딩 가격은 무시할만하다고 할 수 있습니다.

마인드맵 Mindmap

마인드맵은 토니 부잔이 개발한 노트법입니다. 제가 학생 때 시험 공부하며 마인드맵의 도움을 많이 받았습니다. 물리탐사 수업을 들은 학생들은 몇 번 그려봤죠? 컴퓨터 마인드맵 프로그램도 있지만 마인드맵을 처음 접한다면 먼저 손으로 그려보는 것을 추천합니다. 마인드맵 작성 방법은 위 그림에 나와 있습니다. 책에 나온 작성 규칙을 옮기면 아래와 같습니다.

강조 기법

  • 항상 중심 이미지 사용, 중심 이미지에 3가지 이상의 색상 사용
  • 마인드맵 전체에 걸쳐 이미지 사용
  • 이미지와 단어를 입체화
  • 공감각 사용
  • 글자, 가지, 이미지를 다양한 크기로 사용
  • 적절한 공간 배분

연상 결합

  • 가지끼리 연결할 때 화살표 사용
  • 색상, 부호 사용

명료화 기법

  • 하나의 가지 위에 하나의 키워드만 사용
  • 모든 단어는 알아보기 좋게 쓰기
  • 가지의 길이는 단어 길이와 비슷하게
  • 주가지는 중심 이미지에 연결, 두껍게 그리기
  • 가지와 가지는 연결해서 그리기
  • 필요시 가지 외곽을 둘러싸는 경계선 그리기
  • 용지는 가로로

레이아웃

  • 위계적 조직화
  • 번호 사용

규칙이 많은데 외울 필요는 없고, 몇 번 연습해보면서 익히면 됩니다. 제 경우 암기할 것이 많은 과목에 특히 유용했습니다.

위 내용중 키워드가 중요합니다. 공부하는 내용 중 핵심 키워드들을 뽑아서 마인드맵에 적습니다. 핵심 키워드를 뽑기 위해서는 공부하는 내용을 잘 알아야겠죠? 그래서 마인드맵을 만들면 마인드맵을 보며 빠르게 복습하기에도 좋지만, 마인드맵을 만드는 과정에서 공부하는 내용을 더 잘 이해할 수 있습니다. 중심으로부터 위계적으로 연결되어 있으니 큰 그림을 보며 세부 지식의 연관성을 파악하기도 좋습니다.

아래는 위키피디아에서 손으로 그린 그림만 가져 왔습니다. 그림을 잘 못 그린다고 부담을 가질 필요는 없습니다. 그림을 못 그려도, 안 그려도 충분히 유용합니다.

RESTful API 설명

API (Application Programming Interface)

API는 어떤 프로그램의 함수를 호출하고 결과를 받는 대화 형식을 정해놓은 것입니다. API에는 SOAP API, GraphQL API, gRPC API 등 여러 종류들이 있는데 인터넷에서 가장 많이 사용하고 있는 API 중 하나가 REST API 입니다. REST API 규약을 잘 따르면 RESTful 하다고 합니다.

Representation State Transfer (REST) API

REST API는 웹 상의 자원(Resource)을 고유한 URL을 통해 나타내고, HTTP 메소드를 사용하여 해당 자원과 관련된 작업을 수행하는 API입니다. 사용할 수 있는 메소드(행위)는 다음과 같습니다.

  • GET: 데이터를 조회하거나 검색 (Read)
  • POST: 새로운 데이터를 생성 (Create)
  • PUT: 기존 데이터를 업데이트하거나 대체 (Update)
  • PATCH: 기존 데이터의 일부를 업데이트 (Update)
    • DELETE: 데이터를 삭제 (Delete)

이러한 Create-Read-Update-Delete 작업을 CRUD 작업이라고도 합니다.

REST API는 자원 식별자, 메소드, 내용(입력값, Pay Load)으로 이루어집니다. 메소드는 위에 주어져 있고, 내용은 데이터에 관한 정보가 되겠습니다. 입출력 내용에 고정된 형식이 있는 것은 아니지만 JSON (JavaScript Object Nation)과 XML (eXtensible Markup Language) 형식을 많이 사용합니다. 자원 식별자는 뭘까요?

자원 식별자

고유한 URL을 이용해 웹 상에서 내가 원하는 자원에 접근할 수 있는데, 자원 식별을 위해 사용하는 고유한 식별자를 자원 식별자 URI (Uniform Resource Identifier)라고 합니다. URL과 URN은 모두 URI의 일종입니다.

  • URL (Uniform Resource Locator): 인터넷 상에서 자원의 위치를 나타내는 주소
  • URN (Uniform Resource Name): 자원에 대한 유일한 이름으로, 위치와 상관없이 식별 가능

상태 코드

자원 식별자, 메소드, 내용이 있으면 REST API를 호출해서 결과를 받아볼 수 있습니다. 구체적인 호출 방법은 사용하는 언어나 프로그램에 따라 달라집니다. GET 메소드의 경우 요청한 정보를 결과물로 받게 되지만, 다른 메소드(생성, 업데이트, 삭제)의 경우 서버 내에서 처리하면 되기 때문에 반환하는 데이터가 없을 수 있습니다. 그럼 요청이 정상적으로 처리되었는지 알 수가 없겠죠? 그래서 REST API에서는 항상 숫자로 된 상태 코드를 반환합니다. 숫자 범위에 따른 상태 코드입니다.

  • 2xx: 성공적인 응답 – 200은 성공적인 응답, 201은 자원이 성공적으로 생성되었음
  • 3xx: 리다이렉션 – 301은 자원 위치가 영구적으로 변경되었음, 302는 자원 위치 임시 변경
  • 4xx: 클라이언트 오류 – 400은 잘못된 요청, 404는 찾을 수 없는 자원
  • 5xx: 서버 오류 – 500은 서버 내부 오류(서버에서 요청을 처리할 수 없는 상태)

URL 주소로 웹 페이지를 열면 GET 메서드를 호출하는 셈입니다. 찾는 페이지가 없을 때 404 Error – Page not found 오류를 보신 적이 있을겁니다.

인공지능 비서와 외부 API

인공지능 비서를 만들 때 LLM은 두뇌라 할 수 있습니다. 두뇌만 있는 것보다는 손발이 있어야 더 많은 일들을 할 수 있겠죠? LLM에게 각종 함수나 API들을 손발로 쓸 수 있도록 만들어줄 수 있습니다. 파이썬에서 LLM의 API를 이용해 인공지능 비서를 만든다고 해봅시다. 다음은 LLM에게 API 사용 기술을 전수하는 과정입니다.

  • LLM에게 명령과 함께 REST API와 같은 외부 API 사용법을 알려주고 명령 수행을 위해 필요하면 API 호출을 요청하라고 알려줍니다.
  • LLM이 필요할 경우 REST API를 특정 메소드와 내용으로 호출하라고 프로그램에 요청합니다.
  • 파이썬에서는 LLM의 요청대로 REST API를 호출한 후 얻은 결과를 LLM에 알려줍니다.
  • LLM은 API 호출 결과를 가지고 계속해서 명령을 수행합니다. 필요하면 여러 개의 API를 호출할 수도 있습니다.
  • 이 과정을 주어진 명령을 완수할 때까지 반복합니다.

부동 소수와 딥러닝에서의 양자화

부동 소수 Floating point numbers

부동 소수는 실수를 표현하는 방법 중 하나입니다. 고정 소수와 달리 소숫점 자리가 움직인다고 부동 소수라고 부릅니다.

  • 고정 소수: 123.45
  • 부동 소수: 0.12345\times10^3, 1.2345\times 10^2, 등

부동 소수는 \pm m\times n^e 라고 쓸 수 있는데 각 부분의 명칭은 다음과 같습니다.

  • \pm: 부호 sign
  • m: 가수 significand
  • n: 기수 base=2 (항상 2)
  • e: 지수 exponent

정밀도 Precision

부동 소수는 숫자를 표현(저장)하기 위해 사용하는 비트 수에 따라 반정도, 단정도, 배정도 자료형 등으로 나눌 수 있습니다(IEEE754 표준). 비트 수를 많이 사용할수록 정밀도가 높죠.

  • 반정도 FP16, half-precision: 16 비트 = 2 바이트
  • 단정도 FP32, single-precision: 32 비트 = 4 바이트
  • 배정도 FP64, double-precision: 64 비트 = 8 바이트

표준에는 더 많은 비트를 사용한 부동 소수도 있는데 보통 위의 세 가지 자료형을 많이 사용합니다.

정밀도부호 비트지수 비트가수 비트최소 양수 (근사치)최대 양수 (근사치)
반정도15105.96\times10^{-8}6.55\times10^4
단정도18231.40\times10^{-45}3.40\times 10^{38}
배정도111524.941\times10^{-324}1.798\times10^{308}
부동 소수 비트

위 표를 보면 기수(base) 저장에는 비트를 사용하지 않는 것을 알 수 있습니다. 항상 2이기 때문에 저장할 필요가 없죠. 지수 비트를 더 많이 사용할수록 더 넓은 범위를 표현할 수 있고, 가수 비트를 더 많이 사용할수록 소숫점 아래 더 많은 자리까지 정밀한 값을 표현할 수 있습니다. 많은 비트를 사용할수록 더 정확하지만 메모리를 많이 차지하고 계산 속도가 느려지게 됩니다.

자료형 변환

자료형 사이에는 변환이 가능합니다. 낮은 비트의 자료형에서 높은 비트의 자료형으로 변환할 때는 저장된 숫자의 정밀도에 변함이 없지만, 높은 비트의 자료형에서 낮은 비트의 자료형으로 변환하면 저장된 숫자의 정밀도를 일부 희생하게 됩니다.

양자화 Quantization

딥러닝에서는 신경망 매개변수(가중치) 저장에 기본적으로 단정도 부동 소수를 사용합니다. 거대 언어 모델과 같이 큰 모델의 경우 수많은 매개변수(보통 수십억 개가 넘어갑니다)가 존재하기 때문에 메모리와 계산량 부담을 줄이기 위해 반정도나 더 낮은 정밀도(8비트 정수)의 자료형으로 변환을 하는 경우가 많습니다. 이를 양자화(Quantization)라고 합니다.

양자화에는 신경망 훈련 중 양자화, 훈련을 마친 후 양자화, 동적 양자화, 정적 양자화 등 다양한 기법이 존재합니다.

Retrieval Augmented Generation & Fine Tuning

기초 모델(Foundation Model)의 한계

ChatGPT나 Bard와 같은 거대 언어 모델(LLM)을 다양한 분야의 일반적인 지식에 대해 훈련된 기초 모델(Foundation Model)이라고 합니다. 기초 모델은 일반적인 작업을 대체로 잘 수행합니다. LLM은 질문에 따라 응답이 달라지기 때문에 응답의 질을 향상시키기 위해 어떻게 메시지를 보내야 하는지 연구하는 것을 프롬프트 엔지니어링이라고 합니다. 프롬프트 엔지니어링을 통해 훌륭한 프롬프트를 찾아냈다고 하더라도 LLM에는 한계가 존재합니다.

  • 특정 시기까지의 데이터만 가지고 훈련되었기 때문에 최신 정보를 반영하지 못합니다. 최신 정보를 물어보면 엉터리 대답(hallucination)을 하기도 하죠.
  • 다양한 분야의 일반적인 자료를 이용해 훈련시켰기 때문에 특정 세부 분야의 지식이 부족합니다.

이러한 한계를 극복하기 위해 Retrieval Augmented GenerationFine Tuning을 많이 사용합니다.

Retrieval Augmented Generation (RAG)

벡터 데이터베이스를 이용하면 문서에서 질문과 관련된 부분을 추출하여 LLM 프롬프트에 Context로 전달할 수 있습니다. LLM은 Context의 내용을 바탕으로 질문에 맞는 답을 내놓을 수 있죠. 특정 분야의 최신 정보를 반영한 문서를 사용하면 위에서 말한 LLM의 한계를 극복할 수 있습니다.

Fine Tuning

Fine Tuning은 이미 훈련되어 있는 기초 모델에 목적에 맞는 자료를 이용해 추가 훈련을 시키는 전이학습 과정입니다. 이를 통해 LLM이 특정 분야의 최신 정보를 반영하도록 만들 수 있죠. 이렇게 훈련된 LLM에 질문을 던지면 추가 훈련 정보를 반영한 응답을 얻을 수 있습니다.

RAG or Fine Tuning?

두 가지 기법은 모두 LLM의 한계를 극복하여 성능을 향상시키기 위한 기법입니다. 하지만 서로 다른 기법으로, 장단점 차이가 있기 때문에 목적에 맞는 기법을 이용하는게 좋습니다.

RAG 장점, Fine Tuning 단점

  • 새로운 정보 업데이트: RAG에서 최신 정보나 새로운 분야의 정보를 반영하려면 새 문서를 이용해 벡터 데이터베이스를 업데이트하면 됩니다. LLM은 건드릴 필요가 없습니다. 하지만 Fine Tuning에서는 새로운 정보를 추가할 때마다 전이학습이 필요하고, 거대한 LLM일수록 추가 학습 비용이 높아집니다.
  • 추가 정보 준비: RAG에서는 추가 정보를 입력하는데 특별한 형식이 필요하지 않습니다. 주어진 문서를 일정 길이로 나누어 임베딩 모델을 통해 벡터로 만들고 벡터 데이터베이스에 입력하면 됩니다. 하지만 Fine Tuning을 위해서는 바람직한 질문-응답(Label) 쌍을 충분히 준비해야 합니다.
  • 해석 가능성: RAG에서는 LLM이 벡터 검색 결과를 바탕으로 응답하기 때문에 벡터 검색 결과를 확인하면 LLM이 왜 그렇게 대답했는지 알 수 있습니다. Fine Tuning의 경우 기초 모델과 같이 왜 그런 대답을 했는지 명확히 알기 어렵습니다.

Fine Tuning 장점, RAG 단점

  • 응답 속도: Fine Tuning은 훈련을 통해 가중치가 업데이트된 LLM을 이용하기 때문에 실제 사용시 기초 모델과 응답 속도 차이가 거의 없습니다. 하지만 RAG는 기초 모델에 벡터 검색 과정이 추가되므로 응답 속도가 느려지게 됩니다.
  • 실제 사용시 계산 비용: Fine Tuning은 추가 훈련 비용이 들지만 훈련 후 실제 사용시 계산 비용은 기초 모델과 동일합니다. RAG는 추가 훈련 비용은 안 들지만 벡터 검색 비용이 추가됩니다. 단, 전체 계산 비용은 추가 훈련을 얼마나 많이 하느냐에 따라 달라질 수 있습니다.
  • 특정 스타일 반영: Fine Tuning은 훈련 자료를 이용해 LLM이 특정 스타일(문어체, 구어체 등)로 응답하도록 훈련시킬 수 있습니다. RAG에서도 Few-shot learning을 이용해 스타일을 학습시킬 수 있지만, 그렇게 되면 토큰 수가 증가하고, 이는 비용 증가로 이어집니다.

물론, Fine Tuning한 모델에 RAG를 추가하는 것도 가능합니다. 대신 비용이 더 증가하죠.

벡터 데이터베이스

벡터 임베딩과 벡터 검색

텍스트는 토크나이저와 임베딩을 거쳐 벡터가 됩니다. 토큰화와 임베딩을 합해서 그냥 임베딩이라고 이야기하기도 합니다. 벡터를 이용한 검색 과정에 대해 생각해봅시다. 문서를 문단, 문장 등 작은 단위(chunk)로 분리하고 각각을 벡터로 만들어 저장해 놓습니다. 질문이 들어왔을 때 질문도 임베딩 벡터로 만들면 저장된 벡터와 질문의 임베딩 벡터 사이의 거리를 계산할 수 있습니다. 거리가 가까운 벡터는 질문과 관련된 내용을 포함할 가능성이 높습니다. 이렇게 질문과 관련된 벡터를 찾는 과정이 벡터 검색입니다.

검색 후에는 거리가 가장 가까운 벡터 K개를 추출하여 거대 언어 모델의 프롬프트에 포함시킵니다. 이 방법을 Retrieval-Augmented Generation (RAG)라고 합니다.

벡터 거리 측정

검색을 위해 벡터 사이의 거리를 측정하는 방법에는 여러 가지가 있습니다.

  • 코사인 유사도
  • 해밍
  • 유클리드 거리 (L2)
  • 맨하탄 거리 (L1)
  • 내적

이 외에도 여러 가지가 있는데, LLM과 관련해서 코사인 유사도를 많이 사용합니다. 보통 질문의 임베딩 벡터와 가장 가까운 벡터를 하나만 찾는게 아니라 K개를 찾습니다. K-Nearest Neighbor (KNN)라고 하죠.

이 때 문서가 짧으면 벡터도 많지 않고 거리를 구하는 계산이 금방 끝나지만 문서가 길어지게 되면 벡터 검색 시간이 크게 증가하게 됩니다. 그래서 보통 Approximate Nearest Neighbor (ANN) 기법을 사용해 근사적으로 가장 가까운 벡터들을 찾습니다. 정확도를 약간 희생하고 검색 속도를 높이는 방법입니다. ANN 관련 기술도 여러 가지가 있습니다.

  • Locality Sensitive Hashing (LSH)
  • Random Projection
  • Hierarchical Navigable Small World (HNSW) Graphs
  • Product Quantization

벡터 라이브러리

ANN 기법은 검색 범위를 좁히거나 벡터의 길이를 짧게 만들어 벡터 검색 계산량을 줄이는데, 그렇게 하기 위해서는 미리 벡터를 인덱싱하는 과정이 필요합니다. 이렇게 인덱스를 만들고 벡터간의 거리를 계산해서 검색하는 프로그램을 벡터 라이브러리라고 합니다. 다음은 많이 사용하는 벡터 라이브러리들입니다.

  • 메타에서 개발한 FAISS (Facebook AI Similarity Search)
  • 스포티파이에서 개발한 Annoy (Approximate Nearest Neighbors Oh Yeah)
  • 구글에서 개발한 ScaNN (Scalable Nearest Neighbors)
  • NMSLIB
  • HNSWLIB

벡터 데이터베이스

실제 LLM에서 벡터 임베딩을 이용한 검색을 위해서는 벡터 라이브러리의 기능 외에도 추가 기능들이 필요합니다. 벡터 외에 벡터의 원본 텍스트와 같은 메타 정보를 포함하는 기능, 메타 정보를 이용한 필터링, 벡터의 손쉬운 추가/제거/업데이트, 병렬 연산 지원, 백업이나 보안 지원 등과 같은 기능을 추가한 벡터 라이브러리가 벡터 데이터베이스입니다. 최근에는 기존 데이터베이스에서 벡터 검색을 지원하기도 하는데, 많이 사용하는 벡터 전용 데이터베이스는 다음과 같습니다.

  • Pinecone
  • Milvus
  • Chroma
  • Weaviate
  • Deep Lake
  • Qdrant
  • Vespa

LLM을 이용해 RAG를 구현할 때 위 내용들을 몰라도, 벡터 데이터베이스 사용법만 알면 구현이 가능합니다^^ 하지만, 어느 정도의 배경 지식은 알고 만들면 더 좋겠죠?

토큰과 임베딩 벡터

거대 언어 모델(LLM)에 우리는 텍스트 문자열을 입력해 답을 얻습니다. 그러나 컴퓨터에서 실제 계산은 숫자로 이뤄지죠. 우리가 입력한 글자들은 토크나이저(Tokenizer)를 거쳐 토큰으로 분리되고, 각각의 토큰은 임베딩(Embedding) 모델을 거쳐 숫자들로 이루어진 벡터가 됩니다. 거대 언어 모델에는 이 벡터들이 순차적으로 전달되고, 연산을 통해 응답이 나오게 됩니다.

토크나이저와 토큰

토크나이저는 텍스트를 단어나 더 작은 단위(subwords)로 나누는 프로그램입니다. 나눠진 결과를 토큰이라고 하고, 이 과정을 토큰화(tokenize)라고 합니다. 토큰은 아직 문자죠. 토크나이저에는 단어 기반, 서브워드 기반, 문자 기반 토크나이저가 있는데, 거대 언어 모델들은 주로 서브워드 기반 토크나이저를 사용합니다.

  • 단어 기반: [인공지능을]
  • 서브워드 기반: [인공, ##지능, ##을]
  • 문자 기반: [인, 공, 지, 능, 을]

임베딩

토큰이 임베딩 층을 거치면 벡터가 되고, 이 벡터를 임베딩 벡터라 합니다. 거대 언어 모델에서 임베딩 벡터는 보통 수백~수천 차원의 밀집 벡터(0이 별로 없는)를 이용합니다. 이렇게 토큰별로 만들어진 벡터들은 차례대로 거대 언어 모델에 입력으로 전달됩니다.

예시

이해를 돕기 위해 간략한 예시를 봅시다(실제 결과와는 차이가 있습니다). “인공지능을 활용합시다”라는 텍스트를 입력으로 사용했다고 해봅시다. 그럼 텍스트는 토크나이저를 거쳐 토큰으로 분리가 됩니다.

  • 입력: 인공지능을 활용합시다
  • 토큰화: [인공, ##지능, ##을, 활용, ##합, ##시다]

결과 토큰은 사용하는 토크나이저에 따라 달라지게 됩니다. 각 토큰은 임베딩을 거쳐 벡터가 됩니다. 실제는 고차원 벡터이지만 아래에는 간략하게 3차원으로 표시했습니다.

  • 인공: [0.32, -0.21, 0.59]
  • ##지능: [0.12, 0.45, -0.22]
  • ##시다: [0.15, -0.33, 0.72]

거대 언어 모델은 실제로 이런 숫자를 받아들여 자연어 처리 작업을 수행합니다. 요즘 많이 사용하는 트랜스포머 신경망에서 각각의 벡터는 순차적으로 입력으로 사용되는데, 이 벡터들에 위치 정보가 추가되고 신경망의 여러 층을 거치면서 토큰들 사이의 관계와 문맥이 반영된 출력이 계산됩니다.

문장, 문서 수준의 임베딩

앞에서 다룬 임베딩은 거대 언어 모델의 입력으로 사용하기 위한 임베딩이고, 주어진 텍스트의 전체적인 의미와 문맥을 벡터로 표현하기 위한 임베딩 모델들도 있습니다. 벡터 데이터베이스 검색에 사용하는 임베딩이죠. 이러한 모델을 사용하면 임베딩 벡터를 이용해 문장의 유사도를 계산하거나 정보 검색, 클러스터링, 분류 등을 수행할 수 있습니다.

이러한 모델에서는 주어진 입력을 먼저 토크나이저를 이용해 토큰으로 분리하고, 각 토큰들의 벡터 임베딩을 생성한 후, 이 벡터들을 조합하여 전체 문서를 대표하는 하나의 벡터를 만드는 과정을 거칩니다. 대표 벡터를 만드는 방법에는 벡터의 평균을 이용하는 평균 임베딩 방법, 단어의 등장 빈도를 이용하는 TF-IDF 가중치 방법, 딥 러닝 모델을 사용하는 방법 등이 있습니다.