태그 보관물: LLM

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에 비해 임베딩 가격은 무시할만하다고 할 수 있습니다.

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 가중치 방법, 딥 러닝 모델을 사용하는 방법 등이 있습니다.

거대 언어 모델(Large Language Model, LLM)과 프롬프트 엔지니어링

프롬프트와 프롬프트 엔지니어링

ChatGPT와 같은 거대 언어 모델(Large Language Model, LLM)을 사용할 때 모델에 전달하는 메시지를 프롬프트(Prompt)라고 하죠. 프롬프트를 잘 만들어서 원하는 결과물을 얻기 위해 연구하는 것을 프롬프트 엔지니어링이라고 합니다.

프롬프트는 한 문장이 될 수도 있고 여러 문단이 될 수도 있죠. 프롬프트를 몇 가지 주요 구성 요소들로 나눠보면 아래와 같이 정리할 수 있습니다.

  • Goal: 달성해야 하는 목표
  • Instruction: 구체적인 지시, 요구 사항 또는 질문
  • Role: 모델에게 조언자나 편집자, 작가 등의 역할을 지정해주는 부분
  • Context: 작업의 배경 지식이나 상황 제공
  • Output Format: 원하는 응답 형식을 지정해주는 부분
  • Systematic Approach: 특정 시스템이나 알고리즘을 따르도록 지시
  • Step-by-step: 복잡한 작업을 단계별로 처리하라고 지시
  • Clarifying Questions: 불확실한 부분은 모델이 사용자에게 질문하도록 권장
  • Examples: 질문과 응답 예제

이러한 분류는 정해져 있는 것은 아니고, 사람에 따라 다르게 분류할 수 있습니다. 또 매번 위의 모든 요소가 다 들어갈 필요도 없죠. 사용 목적에 따라 들어가는 요소들이 달라지게 됩니다. LLM은 예제로부터 학습할 수 있는데, 예제가 없으면 Zero-shot, 하나 있으면 One-shot, 두 개 이상 있으면 Few-shot learning이라고 합니다.

아래 나오는 내용은 주로 대화형 환경 보다는 LLM의 API (Application Programming Interface)를 이용해 사용자에게 드러나지 않게 구현하게 됩니다.

환각 현상의 완화

거대 언어 모델에서 나타나는 환각(hallucination, 거짓 응답) 현상을 줄이는 방법 중 하나가 Context를 제공하고 주어진 정보를 이용해 답하라고 지시하는 방법입니다. 물론 Context에는 사실을 담아야겠죠. 그러려면 Context 정보를 어디선가 가지고 와야 합니다.

Bing Chat, Bard

질문을 가지고 인터넷을 검색해서 Context에 들어갈 내용을 가져오면 Microsoft의 Bing Chat이나 Google의 Bard와 같이 최신 정보를 반영한 모델이 됩니다. 검색한 웹문서를 직접 열어서 필요한 부분을 찾을 필요 없이 질문에 대한 답을 알아서 정리해주죠.

문서에 대한 질의 응답: ChatPDF

Context를 PDF 파일과 같은 문서에서 가져오면 문서의 내용에 대해 질문할 수 있는 챗봇, ChatPDF가 됩니다. 인터넷 검색의 경우 이미 있는 Google이나 Bing과 같은 검색 엔진의 검색 결과에서 가장 먼저 나오는 몇 개의 인터넷 문서에서 정보를 가져오면 됩니다. PDF 파일은 어떻게 할까요?

PDF나 워드 문서의 경우 문서에서 텍스트 정보를 추출하여 텍스트를 컴퓨터에서 처리할 수 있는 숫자들의 벡터로 변환합니다. 이 과정을 벡터 임베딩(Vector Embedding)이라고 하죠. 문서 전체의 텍스트를 일정 길이(Chunk)로 나눠서 벡터로 변환한 후 벡터들을 벡터 데이터베이스(VectorDB)에 저장해 놓습니다. 질문이 들어오면 질문도 벡터로 변환합니다. 벡터들끼리는 거리를 측정할 수 있죠? 질문 벡터와 문서의 벡터들 사이의 거리를 측정하여 거리가 가까운 벡터들을 몇 개 추출하면 그 벡터에 해당하는 원본 텍스트가 Context에 들어가게 됩니다. 그럼 LLM은 질문에 대한 답을 포함할 가능성이 높은 Context의 정보를 이용해서 대답을 해주죠. 정리해보면, 다음과 같습니다.

  1. 답을 찾고자 하는 문서(문서들)에서 텍스트 추출
  2. 텍스트를 일정 길이로 나누기
  3. 나눈 내용을 벡터 임베딩을 이용해 벡터로 변환
  4. 변환한 벡터들을 벡터 데이터베이스에 저장
  5. 사용자 질문 (사용자 질문이 제일 처음에 문서와 함께 주어질 수도 있습니다)
  6. 사용자 질문을 벡터 임베딩을 이용해 벡터로 변환
  7. 질문 벡터와 벡터 데이터베이스에 저장된 벡터들 사이의 거리 계산
  8. 거리가 가장 가까운 벡터들 추출
  9. 추출한 벡터들의 원본 텍스트를 LLM에 질문에 대한 Context로 제공
  10. LLM은 질문에 대한 답을 Context에서 찾아서 사용자에게 응답

일반 검색에서는 검색하는 단어나 문장과 글자가 일치하는 것을 찾지만 벡터 검색은 의미가 유사한 내용을 찾는 의미 검색(semantic search)입니다. 벡터 데이터베이스에 회사의 제품에 관한 여러 가지 문서들을 넣으면 회사의 제품 상담 챗봇이 됩니다.

문서에는 PDF 뿐 아니라 Word, Excel, Power Point, Gmail, Notion 등 다양한 문서들이 있습니다. 이러한 문서들에서 텍스트를 편리하게 추출해주는 라이브러리로 Llama-Index가 있습니다.

인공지능 비서: AutoGPT, BabyAGI

LLM의 API를 호출하는 프로그램에서 Instruction에 특정 함수의 사용법을 알려줄 수 있습니다. 예를 들면 위키피디아 검색을 하고 싶으면 wikipedia(query)라고 출력하라고 알려줄 수 있습니다. 수학 계산을 하고 싶으면 calculate(equation)이라고 출력하라고 알려줄 수도 있죠. 질문과 함께 함수 사용법을 알려주고 함수를 사용할 수 있다고 LLM에 알려주면 LLM은 최종 대답을 하기 전에 필요한 정보를 얻기 위해 함수를 사용하게 됩니다. 직접 함수를 호출하는 것은 아니고 함수 호출에 필요한 명령을 출력하게 되는거죠. 그럼 프로그램에서 해당 함수를 호출하여 결과를 LLM에 전달해줍니다.

  1. 질문과 함께 사용할 수 있는 함수들을 LLM에 제시
  2. LLM에서 필요한 함수를 호출하는 명령 출력
  3. LLM을 호출하는 프로그램에서 함수 실행
  4. 함수 실행 결과를 LLM에 알려주기
  5. LLM에서 최종 응답

예를 들어, 광안대교를 언제 개통했는지 질문하고 위의 함수를 사용할 수 있다고 알려주면, wikipedia(광안대교 개통일)과 같은 출력을 내놓게 됩니다. 그럼 이 함수 호출 내용을 프로그램에서 분석해서 위키피디아 검색을 수행하고 결과 페이지(위키피디아 광안대교 페이지)의 내용을 LLM에 질문과 함께 Context로 전달하게 됩니다. 그럼 LLM은 Context에서 답을 찾아 대답하게 되죠.

LLM에서 Python 코드를 출력하면 코드를 실행해서 결과를 알려주겠다고 지시할 수도 있습니다. LLM에 지시를 내리면 LLM에서 지시를 수행하기 위한 코드를 작성해서 출력하고, 프로그램에서 코드를 실행한 후 실행 결과를 다시 LLM에 전달합니다. 그럼 LLM은 결과를 이용해 이후의 응답을 생성할 수 있죠. ChatGPT Plus의 Advanced Data Analysis (Code Interpreter의 새 이름)의 기본 원리입니다.

이렇게 LLM에 알려주는 함수는 LLM의 Tool 또는 Skill이라고 부릅니다. 많은 함수를 알려줄수록 LLM이 할 수 있는 일도 많아지죠. 복잡한 문제의 경우 문제 풀이 단계를 논리적으로 분석해서 제시하고 하나씩 차례대로 수행하라고 지시할 수도 있습니다. 각 단계를 수행할 때 사용할 수 있는 함수들도 같이 알려주죠. AutoGPTBabyAGI의 기본 원리입니다. 이런 LLM 프로그래밍을 편리하게 만들어주는 라이브러리로 LangChain이 있습니다.

J.A.R.V.I.S.

말로 지시를 내리고 소리로 응답을 듣고 싶다면? 음성 인식(Speech Recognition) 기술을 이용해 말을 글로 바꿔서 지시를 내릴 수 있습니다. LLM의 출력은 글을 읽어주는 음성 합성(Text-to-Speech) 기술을 이용하면 됩니다.

  1. 사용자가 음성으로 명령
  2. 음성 인식: 음성을 글자로 변환
  3. 글자를 LLM (AutoGPT)에 전달
  4. LLM (AutoGPT)에서 응답
  5. 음성 합성: 응답 결과를 읽어주기

간단한 자비스(JARVIS)가 됩니다. 토니 스타크의 자비스에 비하면 아직 한참 멀었지만, 점점 향상되고 있습니다.