OpenAI API를 이용할 때 모델별로 최대 토큰 수가 정해져 있습니다. API를 호출하기 전에 토큰 수를 미리 알 수 있으면 좋겠죠? 파이썬에서 텍스트를 토큰으로 변환해주는 tiktoken 패키지를 이용하면 토큰 수를 알 수 있습니다.
tiktoken 설치
tiktoken은 pip를 이용해 설치할 수 있습니다.
pip install tiktoken
인코딩 방법(토크나이저) 결정
OpenAI에 여러 종류의 토크나이저들이 있기 때문에 토큰화하기 전에 먼저 토크나이저를 지정해줍니다. 지정 방법에는 두 가지가 있는데, 토크나이저 이름을 이용하는 방법과 LLM 이름을 이용하는 방법 중 하나를 선택해 사용합니다.
import tiktoken
# 토크나이저 이름을 이용하는 방법
encoder = tiktoken.get_encoding("cl100k_base")
# LLM 이름을 이용하는 방법
encoder = tiktoken.encoding_for_model("gpt-3.5-turbo")
인코딩
토크나이저를 정한 후에는 다음과 같이 텍스트를 인코딩(토큰화)할 수 있습니다.
text = "I love Python!"
result = encoder.encode(text)
print(result) # 결과: [40, 3021, 13325, 0]
토큰 번호로 결과가 출력되네요. 토큰의 길이를 알고 싶다면 토큰 리스트의 길이를 이용하면 됩니다.
len(result) # 결과: 4
디코딩
다시 텍스트로 변환하려면 decode를 사용합니다.
encoder.decode(result) # 결과: 'I love Python!'
처음 입력했던 텍스트가 반환되는 것을 볼 수 있습니다. 토큰별로 확인하고 싶다면 decode_single_token_bytes를 사용합니다.
[encoder.decode_single_token_bytes(token) for token in result]
# 결과: [b'I', b' love', b' Python', b'!']
환경 변수는 컴퓨터 운영체제 또는 쉘 차원에서 관리하는 변수로, 환경 변수를 이용하면 서로 다른 프로세스 간에 변수를 공유할 수 있습니다. 환경 변수는 언어, 시간대 같은 시스템 설정, 프로그램의 동작을 변경하는 설정, API 키, 프로그램 경로 등 다양한 목적으로 사용할 수 있습니다. 윈도우즈 운영체제에서는 환경 변수 설정 메뉴를 이용해, 리눅스나 맥OS에서는 쉘에서 export 명령을 이용해 설정할 수 있습니다.
파이썬에서 환경 변수 다루기
파이썬에서는 os 모듈의 environ 또는 getenv를 사용해 환경 변수에 접근할 수 있습니다.
import os
# 읽기
val = os.environ["MY_VARIABLE"]
val = os.environ.get("MY_VARIABLE")
val = os.getenv("MY_VARIABLE")
# 쓰기
os.environ["MY_VARIABLE"] = val
os.environ을 이용하면 환경 변수를 딕셔너리처럼 다룰 수 있습니다. 존재하지 않는 환경 변수에 접근하려고 하면 에러가 발생합니다. 이 때 get 메소드를 이용하면 변수가 존재하지 않을 때 사용할 default 값을 지정해줄 수 있습니다. 별도로 default 값을 지정하지 않으면 환경 변수가 존재하지 않을 때 None을 반환합니다. os.getenv는 os.environ.get과 동일하게 동작합니다. 보통 좀 더 간결한 os.getenv를 사용합니다.
os.environ을 이용해 설정한 환경 변수는 현재 실행 중인 프로세스에만 영향을 미치고, 시스템 전체 환경 변수를 변경하지는 않습니다.
Colab에서 환경 변수
API 키와 같은 비밀번호의 경우 코드 내에 저장하는 것은 보안 문제로 인해 보통 추천하지 않습니다. API 키는 환경 변수에 저장하고 사용하는 것이 좋습니다. 하지만, Colab과 같이 직접 환경 변수를 지정하기 어려운 상황도 있습니다. 이런 경우에는 환경 변수에 저장하는 대신 설정 파일에 API 키를 저장해놓고 코드에서 읽어서 쓸 수 있습니다. 파이썬에는 ini, cfg와 같은 설정 파일을 다루는 configparser 모듈이 있습니다. YAML, TOML이나 JSON 파일을 설정 파일로 쓰는 경우도 있습니다. 아래 살펴볼 dotenv 패키지는 설정 파일에서 읽어들인 값을 환경 변수처럼 이용할 수 있도록 만드는 패키지입니다.
Colab 노트북에서 설치하려면 위의 명령 앞에 느낌표(!)를 붙이면 됩니다. 기본 설정 파일은 .env 파일입니다. 파일 내용은 다음과 같이 적습니다.
TEMPERATURE=0.8
OPENAI_API_KEY=sk-my-key
위 변수들은 같은 디렉토리에서 다음의 코드로 읽어들일 수 있습니다.
import os
from dotenv import load_dotenv
load_dotenv()
temperature = float(os.getenv("TEMPERATURE"))
openai_api_key = os.getenv("OPENAI_API_KEY")
만약 .env 파일이 다른 경로에 있다면 load_dotenv(dotenv_path="/path/to/dotenv")와 같이 절대 또는 상대 경로를 넣어주면 됩니다. 이미 동일한 이름의 시스템 환경 변수가 존재하는 경우 .env 파일에 적은 환경 변수는 무시되는데, 시스템 환경 변수를 덮어쓰고 싶으면 override=True 옵션을 주면 됩니다. 단순히 API 키나 변수를 읽어들이는 경우 위의 코드로 충분합니다. 다른 기능이 필요할 경우 여기에서 load_dotenv 함수의 다른 인자와 패키지 내의 다른 함수 설명을 찾을 수 있습니다.
참고로, dotenv로 읽어들인 환경 변수는 모두 문자열(str) 자료형이 됩니다. 따라서 필요할 경우 적절히 형변환을 해서 사용해야 합니다. 불리언 값의 경우 .env 파일에 적은 TRUE, FALSE 값은 파이썬 문자열로 변환된 후 bool("FALSE")와 같이 쓰면 모두 True 값이 되니 주의해야 합니다.
OpenAI API를 이용할 때 ChatCompletion.create 함수에 functions라는 인자(argument)가 있습니다. LLM에게 사용할 수 있는 함수 정보를 알려줄 때 사용합니다. 이를 이용하면 LLM이 질문에 응답하기 위해 함수 호출이 필요할 경우 함수를 호출해달라는 출력(function_call)을 내놓습니다. 실제 호출은 파이썬 프로그램에서 하고 LLM은 어떤 함수를 어떤 인자들을 사용해 호출해야 하는지 알려주는 것이죠. 사용자는 함수 호출 결과를 기존 질문과 함께 다시 LLM에 전달하고 LLM은 결과를 바탕으로 질문에 응답합니다.
예제: Wikipedia 함수
먼저, LLM이 호출할 수 있는 함수를 하나 만들어보겠습니다. 검색 문자열을 받아 Wikipedia를 검색하고 첫 번째로 검색된 페이지의 내용을 반환하는 함수입니다. 페이지 내용은 다시 LLM에 보내 LLM이 답변하는데 참고할 수 있도록 합니다. 이를 위해 파이썬 wikipedia 패키지를 설치하겠습니다.
pip install wikipedia
패키지를 설치했으면 파이썬에서 다음과 같이 사용할 수 있습니다.
import wikipedia
wikipedia.set_lang("en")
r = wikipedia.search("Large language model")
print(r)
## 출력 결과
['Large language model',
'Language model',
'BERT (language model)',
'BLOOM (language model)',
'LLaMA',
'Prompt engineering',
'Modeling language',
'Generative pre-trained transformer',
'Transformer (machine learning model)',
'GPT-3']
한글 검색을 위해서는 언어를 한글로 바꾼 후 검색하면 됩니다.
wikipedia.set_lang("ko")
r = wikipedia.search("광안대교 정식 개통일")
print(r)
## 출력 결과
['광안대교',
'수영강변대로',
'남항대교',
'인천대교',
'해운대',
'세토 대교',
'만덕대로',
'교량',
'부산광역시',
'남구 (부산광역시)']
위의 결과들을 보면, 검색 결과는 검색 관련 내용이 있는 Wikipedia 페이지 제목 10개입니다. 페이지의 내용은 다음과 같이 받아올 수 있습니다.
p = wikipedia.page(r[0])
print(p.content)
위의 내용을 종합해서 다음과 같은 함수를 만들었습니다. 검색 내용과 언어를 입력받으면 Wikipedia를 검색해서 가장 먼저 나오는 페이지의 내용을 반환하는 함수입니다.
def search_wikipedia(query, lang):
wikipedia.set_lang(lang)
r = wikipedia.search(query, results=1)
p = wikipedia.page(r[0])
return p.content
여기서는 하나의 함수, 두 개의 인자를 사용했는데, 함수가 여러 개 있으면 functions 리스트에 같은형식으로 추가하면 됩니다. 함수 정의 부분을 살펴보면, name에 함수 이름이 들어가고, description 부분에 함수 설명이 들어갑니다. parameters – properties에 인자 설명이 들어가는데, 입력 자료형은 type으로 지정해주고, 설명은 description에 들어가게 됩니다. 선택 가능한 경우가 정해져 있으면 enum으로 지정해주면 됩니다. 필수 인자는 required로 지정해줍니다. 위 내용을 추가해서 OpenAI API를 호출해보겠습니다.
import os
import openai
openai.api_key = os.getenv("OPENAI_API_KEY")
messages = [{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "광안대교 정식 개통일이 언제야?"}]
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=messages,
functions=functions
)
출력: function_call
광안대교 정식 개통일이 언제인지 질문했죠. 참고로, 제가 ChatGPT에게 같은 질문을 했을 때는 틀린 답을 내놓았습니다. LLM은 질문을 바탕으로 주어진 함수 호출이 필요한지 아닌지 판단합니다. 함수 호출이 필요하다면 response['choices'][0]['message'] 내에 function_call 항목이 생기고, 필요 없다면 이 항목이 생기지 않습니다. 따라서 함수를 실제로 호출할 것인지는 function_call이 존재하는지 여부로 판단할 수 있습니다. 위 코드의 실행 결과로 얻은 response는 다음과 같습니다.
function_call이 존재하고, 호출할 함수의 이름과 인자 정보가 있는 것을 알 수 있습니다. 참고로, query 부분이 알아보기 어렵게 나오는데, json 인코딩 때문에 그렇습니다. \n은 개행, \"는 큰 따옴표로 감싼 문자열 안에 큰 따옴표 문자를 넣기 위한 표시죠. print 해보면 한글이 제대로 나옵니다.
print(response["choices"][0]['message']["function_call"]["arguments"])
## 아래는 출력 결과
{
"query": "광안대교 개통일",
"lang": "ko"
}
실제 함수 호출
이 정보를 이용해 함수를 호출하고 그 결과를 다시 LLM에게 전달해주면 됩니다. 함수 호출을 위해서는 아래 함수를 이용하겠습니다.
response["choices"][0]['message']["function_call"]["arguments"]에 있는 인자는 문자열로 되어 있으므로 dictionary로 바꾸기 위해 json.loads를 이용했고, 읽어들인 args를 search_wikipedia함수의 keyword arguments로 넘겼습니다.
함수 호출 결과를 LLM에 전달하기
위 함수를 실행해 얻은 결과는 LLM에 보내는 messages에 추가한 후 OpenAI API를 다시 호출합니다. 이 때 role에는 function이 들어가고, 기존에 없던 name에 호출한 함수 이름, content에 함수의 결과가 들어갑니다.
msg = response["choices"][0]["message"]
content = execute_function_call(msg)
messages.append(
{
"role":"function",
"name":"search_wikipedia",
"content":content
}
)
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=messages,
functions=functions
)
print(response["choices"][0]["message"]["content"])
## 출력 결과
광안대교의 정식 개통일은 2003년 1월 6일입니다.
이제 맞는 답을 얻었습니다. 참고로, wikipedia 페이지 중에는 길이가 긴 것들도 있으므로 페이지 내용을 전달할 때 content를 모두 전달하면 token 수 제한에 걸릴 수 있습니다. functions에 들어가는 함수 설명은 system 메시지에 들어가 token 수를 차지하므로 너무 많은 함수를 넣으면 역시 token 수 제한에 걸릴 수 있습니다. Retrieval Augmented Generation에서처럼 페이지 내용을 임베딩 벡터로 만들고 벡터 검색을 이용해 질문과 관련된 내용만 추출하면 token 수를 줄일 수 있습니다.
JSON (JavaScript Object Notation) 파일은 인터넷에서 서버와 클라이언트 사이 통신에 많이 사용하는 텍스트 표기 형식입니다. REST API에서도 많이 사용하고, 설정 파일로 사용하기도 하죠. 다음과 같이 사람과 컴퓨터가 읽을 수 있는 텍스트 형식입니다.
JSON에는 { }로 표기하는 객체(object), [ ]로 표기하는 배열(array), 큰 따옴표로 감싸는 문자열(string), 숫자(number), 불리언(boolean), null 자료형이 있습니다. 객체는 키와 값으로 이루어지는데, 키는 문자열이고, 값은 위의 6가지 자료형 중 하나가 될 수 있습니다. 객체 안에 객체를 넣는 것도 가능합니다. 배열에도 여러 자료형들이 함께 들어갈 수 있습니다.
파이썬에서 JSON 읽기
파이썬에서 JSON 파일을 읽으려면 다음과 같이 json 모듈의 load 함수를 사용하면 됩니다.
import json
with open('data.json') as fj:
data = json.load(fj)
문자열(str 또는 bytes, bytearray)에 json 내용이 들어있을 경우 loads를 이용합니다.
import json
js = '{"key": "value"}'
data = json.loads(js)
이렇게 읽어들인 data는 파이썬에서 dict 자료형이 됩니다. JSON 자료형은 읽었을 때 아래와 같이 파이썬 자료형으로 변환됩니다.
JSON
Python
object
dict
array
list
string
str
number (integer)
int
number (real)
float
true
True
false
False
null
None
JSON to Python
파이썬에서 JSON 쓰기
파이썬에서 JSON 파일을 쓰려면 dump, 문자열에 쓰려면 dumps 함수를 이용합니다.
import json
data = ["list", {'key': 'value'}]
with open('data.json', 'w') as fj:
json.dump(data, fj)
js = json.dumps(data, indent=4)
dumps에서 indent 옵션을 넣으면 출력시 보기 좋게 만들어줍니다. 파이썬 자료형은 아래와 같이 JSON 자료형으로 변환됩니다.
Python
JSON
dict
object
list, tuple
array
str
string
int, float, (int, float 열거형)
number
True
true
False
false
None
null
Python to JSON
JSON 파일에 한글 쓰기
파이썬 문자열에 한글이 포함되어 있을 경우 위의 dump, dumps를 이용해 JSON 파일이나 문자열을 쓰면 한글이 바로 표시되지 않습니다. 그래도 load, loads로 읽으면 제대로 읽어지기는 합니다. 파일이나 문자열로 썼을 때 한글이 바로 표시되게 만들고 싶으면 dump, dumps에 ensure_ascii=False 옵션을 주면 됩니다.
import json
data = ["list", {'key': '한글'}]
with open('data.json', 'w') as fj:
json.dump(data, fj, ensure_ascii=False)
js = json.dumps(data, indent=4, ensure_ascii=False)
제어 문자가 들어간 문자열
\n, \t, \r, \0과 같은 제어 문자가 들어간 문자열을 json.loads로 읽어들이려고 하면 JSONDecodeError가 발생합니다. 제어 문자를 포함하고 싶으면 strict=False 옵션을 주면 됩니다.
js = """
["list",
{"key": "
한글
"}]
"""
data = json.loads(js, strict=False)
이해를 돕기 위해 제목에 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에 전달할 입력입니다. 입력값을 보면 role과 content로 이루어진 딕셔너리들의 배열임을 알 수 있습니다.
Role
role에는 “system”, “user”, “assistant” 중 하나가 오는데, “system”과 “user”는 사용자가 LLM에 요청하는 내용이고, “assistant”는 LLM의 응답입니다. 앞에서 API를 통해 사용하는 LLM은 이전 대화 내용을 기억하지 못한다고 했죠? 위 코드는 이전 대화를 기억할 수 있도록 전달하는 코드임을 알 수 있습니다. “system” 메시지는 LLM에게 대화 전체에 적용될 역할 등을 지정해주는 메시지로, 보통 대화 처음에 한 번 나오는데, 꼭 필요한 것은 아닙니다. 생략하면 “You are a helpful assistant.”라고 주는 것과 유사합니다. 이후에는 보통 “user”와 “assistant”가 번갈아 나옵니다.
위 예제에서는 model과 messages를 입력했는데, 이 두 항목은 필수로 입력해야 하고, 그 외에도 하이퍼파라미터들과 기타 여러 가지 항목들이 있습니다.
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를 이용해 사용할 수 있도록 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-16k, gpt-4-32k 모델도 있습니다. LLM 모델 정보는 여기에서 확인할 수 있습니다.
Model
Max Tokens
Input Price (per 1K tokens)
Output Price (per 1K tokens)
gpt-3.5-turbo
4,097
$0.0015
$0.002
gpt-3.5-turbo-16k
16,385
$0.003
$0.004
gpt-4
8,192
$0.03
$0.06
gpt-4-32k
32,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에 비해 임베딩 가격은 무시할만하다고 할 수 있습니다.
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를 호출할 수도 있습니다.
부동 소수는 실수를 표현하는 방법 중 하나입니다. 고정 소수와 달리 소숫점 자리가 움직인다고 부동 소수라고 부릅니다.
고정 소수: 123.45
부동 소수: , , 등
부동 소수는 라고 쓸 수 있는데 각 부분의 명칭은 다음과 같습니다.
: 부호 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 바이트
표준에는 더 많은 비트를 사용한 부동 소수도 있는데 보통 위의 세 가지 자료형을 많이 사용합니다.
정밀도
부호 비트
지수 비트
가수 비트
최소 양수 (근사치)
최대 양수 (근사치)
반정도
1
5
10
단정도
1
8
23
배정도
1
11
52
부동 소수 비트
위 표를 보면 기수(base) 저장에는 비트를 사용하지 않는 것을 알 수 있습니다. 항상 2이기 때문에 저장할 필요가 없죠. 지수 비트를 더 많이 사용할수록 더 넓은 범위를 표현할 수 있고, 가수 비트를 더 많이 사용할수록 소숫점 아래 더 많은 자리까지 정밀한 값을 표현할 수 있습니다. 많은 비트를 사용할수록 더 정확하지만 메모리를 많이 차지하고 계산 속도가 느려지게 됩니다.
자료형 변환
자료형 사이에는 변환이 가능합니다. 낮은 비트의 자료형에서 높은 비트의 자료형으로 변환할 때는 저장된 숫자의 정밀도에 변함이 없지만, 높은 비트의 자료형에서 낮은 비트의 자료형으로 변환하면 저장된 숫자의 정밀도를 일부 희생하게 됩니다.
양자화 Quantization
딥러닝에서는 신경망 매개변수(가중치) 저장에 기본적으로 단정도 부동 소수를 사용합니다. 거대 언어 모델과 같이 큰 모델의 경우 수많은 매개변수(보통 수십억 개가 넘어갑니다)가 존재하기 때문에 메모리와 계산량 부담을 줄이기 위해 반정도나 더 낮은 정밀도(8비트 정수)의 자료형으로 변환을 하는 경우가 많습니다. 이를 양자화(Quantization)라고 합니다.
양자화에는 신경망 훈련 중 양자화, 훈련을 마친 후 양자화, 동적 양자화, 정적 양자화 등 다양한 기법이 존재합니다.
정리하는 뇌라는 책에 보면 여러 개의 프로젝트를 진행하고 있을 경우 프로젝트별로 작업 공간을 분리하는 것이 두뇌 활동에 도움이 된다는 이야기가 나옵니다. 2018년 이 책을 읽고 학교 연구실에서 수업 준비 및 행정 업무용 책상과 연구용 책상(그리고 컴퓨터)을 분리했던 적이 있습니다. 수업 준비를 하다가 연구를 하려면 자리를 옮겨야 했고, 이를 통해 두뇌에 작업이 달라졌다는 것을 확실히 알려주기 위해서였죠.
연구용 책상을 따로 분리할 수는 있었지만 공간 제약으로 인해 여러 연구 프로젝트들끼리의 물리적 공간 분리는 할 수 없었습니다. 대신 Tmux를 이용해 클러스터 서버에서 터미널을 분리해서 작업할 수 있었고, 실제로 이 프로젝트 작업하다가 저 프로젝트 작업하면서 뒤죽박죽되는 상황이 많이 줄었습니다. Tmux에서 프로젝트별로 터미널 세션을 만들어 작업했했는데, 세션 전환을 통해 이제 다른 프로젝트 작업을 시작한다는 것을 두뇌에 알려준 셈이죠.
이후 도커 사용법을 배워 컨테이너 가상화도 프로젝트 작업 공간 분리에 함께 사용하기 시작했습니다. 이전에는 같은 호스트 운영체제에서 터미널만 분리해 작업했던 반면, 컨테이너를 사용하면 프로젝트별로 서로 다른 운영체제를 사용하는 셈이기에 작업 공간 분리에 더 좋습니다.
세컨드 브레인 책에도 프로젝트별 파일 정리에 대한 언급이 나옵니다. 트와일라 타프라는 창의적인 안무가도 새로운 작품을 시작하면 새로운 상자를 준비해 작품과 관련된 모든 자료를 상자에 보관했다고 합니다. 프로젝트를 잠시 보류했다가도 나중에 다시 상자를 쉽게 찾아 진행할 수 있었고, 내용물을 공유하기도 좋았다고 합니다.