13 KiB
๋๊ตฌ์ RAGTools-and-RAG
[~PreTrainedTokenizerBase.apply_chat_template] ๋ฉ์๋๋ ์ฑํ
๋ฉ์์ง ์ธ์๋ ๋ฌธ์์ด, ๋ฆฌ์คํธ, ๋์
๋๋ฆฌ ๋ฑ ๊ฑฐ์ ๋ชจ๋ ์ข
๋ฅ์ ์ถ๊ฐ ์ธ์ ํ์
์ ์ง์ํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ค์ํ ์ฌ์ฉ ์ํฉ์์ ์ฑํ
ํ
ํ๋ฆฟ์ ํ์ฉํ ์ ์์ต๋๋ค.
์ด ๊ฐ์ด๋์์๋ ๋๊ตฌ ๋ฐ ๊ฒ์ ์ฆ๊ฐ ์์ฑ(RAG)๊ณผ ํจ๊ป ์ฑํ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ๋๋ฆฝ๋๋ค.
๋๊ตฌTools
๋๊ตฌ๋ ๋๊ท๋ชจ ์ธ์ด ๋ชจ๋ธ(LLM)์ด ํน์ ์์ ์ ์ํํ๊ธฐ ์ํด ํธ์ถํ ์ ์๋ ํจ์์ ๋๋ค. ์ด๋ ์ค์๊ฐ ์ ๋ณด, ๊ณ์ฐ ๋๊ตฌ ๋๋ ๋๊ท๋ชจ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ ๋ฑ์ ํตํด ๋ํํ ์์ด์ ํธ์ ๊ธฐ๋ฅ์ ํ์ฅํ๋ ๊ฐ๋ ฅํ ๋ฐฉ๋ฒ์ ๋๋ค.
๋๊ตฌ๋ฅผ ๋ง๋ค ๋๋ ์๋ ๊ท์น์ ๋ฐ๋ฅด์ธ์.
- ํจ์๋ ๊ธฐ๋ฅ์ ์ ์ค๋ช ํ๋ ์ด๋ฆ์ ๊ฐ์ ธ์ผ ํฉ๋๋ค.
- ํจ์์ ์ธ์๋ ํจ์ ํค๋์ ํ์
ํํธ๋ฅผ ํฌํจํด์ผ ํฉ๋๋ค(
Args๋ธ๋ก์๋ ํฌํจํ์ง ๋ง์ธ์). - ํจ์์๋ Google ์คํ์ผ ์ ๋ ์คํธ๋ง(docstring)์ด ํฌํจ๋์ด์ผ ํฉ๋๋ค.
- ํจ์์ ๋ฐํ ํ์
๊ณผ
Returns๋ธ๋ก์ ํฌํจํ ์ ์์ง๋ง, ๋๊ตฌ๋ฅผ ํ์ฉํ๋ ๋๋ถ๋ถ์ ๋ชจ๋ธ์์ ์ด๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ฌด์ํ ์ ์์ต๋๋ค.
์ฃผ์ด์ง ์์น์ ํ์ฌ ์จ๋์ ํ์์ ๊ฐ์ ธ์ค๋ ๋๊ตฌ์ ์์๋ ์๋์ ๊ฐ์ต๋๋ค.
def get_current_temperature(location: str, unit: str) -> float:
"""
์ฃผ์ด์ง ์์น์ ํ์ฌ ์จ๋๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
Args:
location: ์จ๋๋ฅผ ๊ฐ์ ธ์ฌ ์์น, "๋์, ๊ตญ๊ฐ" ํ์
unit: ์จ๋๋ฅผ ๋ฐํํ ๋จ์. (์ ํ์ง: ["celsius(์ญ์จ)", "fahrenheit(ํ์จ)"])
Returns:
์ฃผ์ด์ง ์์น์ ์ง์ ๋ ๋จ์๋ก ํ์๋ ํ์ฌ ์จ๋(float ์๋ฃํ).
"""
return 22. # ์ค์ ํจ์๋ผ๋ฉด ์๋ง ์ง์ง๋ก ๊ธฐ์จ์ ๊ฐ์ ธ์์ผ๊ฒ ์ฃ !
def get_current_wind_speed(location: str) -> float:
"""
์ฃผ์ด์ง ์์น์ ํ์ฌ ํ์์ km/h ๋จ์๋ก ๊ฐ์ ธ์ต๋๋ค.
Args:
location: ์จ๋๋ฅผ ๊ฐ์ ธ์ฌ ์์น, "๋์, ๊ตญ๊ฐ" ํ์
Returns:
์ฃผ์ด์ง ์์น์ ํ์ฌ ํ์(km/h, float ์๋ฃํ).
"""
return 6. # ์ค์ ํจ์๋ผ๋ฉด ์๋ง ์ง์ง๋ก ํ์์ ๊ฐ์ ธ์์ผ๊ฒ ์ฃ !
tools = [get_current_temperature, get_current_wind_speed]
NousResearch/Hermes-2-Pro-Llama-3-8B์ ๊ฐ์ด ๋๊ตฌ ์ฌ์ฉ์ ์ง์ํ๋ ๋ชจ๋ธ๊ณผ ํ ํฌ๋์ด์ ๋ฅผ ๊ฐ์ ธ์ค์ธ์. ํ๋์จ์ด๊ฐ ์ง์๋๋ค๋ฉด Command-R์ด๋ Mixtral-8x22B์ ๊ฐ์ ๋ ํฐ ๋ชจ๋ธ๋ ๊ณ ๋ คํ ์ ์์ต๋๋ค.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained( "NousResearch/Hermes-2-Pro-Llama-3-8B")
tokenizer = AutoTokenizer.from_pretrained( "NousResearch/Hermes-2-Pro-Llama-3-8B")
model = AutoModelForCausalLM.from_pretrained( "NousResearch/Hermes-2-Pro-Llama-3-8B", torch_dtype=torch.bfloat16, device_map="auto")
์ฑํ ๋ฉ์์ง๋ฅผ ์์ฑํฉ๋๋ค.
messages = [
{"role": "system", "content": "You are a bot that responds to weather queries. You should reply with the unit used in the queried location."},
{"role": "user", "content": "Hey, what's the temperature in Paris right now?"}
]
messages์ ๋๊ตฌ ๋ชฉ๋ก tools๋ฅผ [~PreTrainedTokenizerBase.apply_chat_template]์ ์ ๋ฌํ ๋ค, ์ด๋ฅผ ๋ชจ๋ธ์ ์
๋ ฅ์ผ๋ก ์ฌ์ฉํ์ฌ ํ
์คํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
inputs = tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True, return_dict=True, return_tensors="pt")
inputs = {k: v for k, v in inputs.items()}
outputs = model.generate(**inputs, max_new_tokens=128)
print(tokenizer.decode(outputs[0][len(inputs["input_ids"][0]):]))
<tool_call>
{"arguments": {"location": "Paris, France", "unit": "celsius"}, "name": "get_current_temperature"}
</tool_call><|im_end|>
์ฑํ
๋ชจ๋ธ์ ๋
์คํธ๋ง(docstring)์ ์ ์๋ ํ์์ ๋ฐ๋ผ get_current_temperature ํจ์์ ์ฌ๋ฐ๋ฅธ ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํด ํธ์ถํ์ต๋๋ค. ํ๋ฆฌ๋ฅผ ๊ธฐ์ค์ผ๋ก ์์น๋ฅผ ํ๋์ค๋ก ์ถ๋ก ํ์ผ๋ฉฐ, ์จ๋ ๋จ์๋ ์ญ์จ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค๊ณ ํ๋จํ์ต๋๋ค.
์ด์ get_current_temperature ํจ์์ ํด๋น ์ธ์๋ค์ tool_call ๋์
๋๋ฆฌ์ ๋ด์ ์ฑํ
๋ฉ์์ง์ ์ถ๊ฐํฉ๋๋ค. tool_call ๋์
๋๋ฆฌ๋ system์ด๋ user๊ฐ ์๋ assistant ์ญํ ๋ก ์ ๊ณต๋์ด์ผ ํฉ๋๋ค.
Warning
OpenAI API๋
tool_callํ์์ผ๋ก JSON ๋ฌธ์์ด์ ์ฌ์ฉํฉ๋๋ค. Transformers์์ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋์ ๋๋ฆฌ๋ฅผ ์๊ตฌํ๊ธฐ ๋๋ฌธ์, ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ฑฐ๋ ๋ชจ๋ธ์ด ์ด์ํ๊ฒ ๋์ํ ์ ์์ต๋๋ค.
tool_call = {"name": "get_current_temperature", "arguments": {"location": "Paris, France", "unit": "celsius"}}
messages.append({"role": "assistant", "tool_calls": [{"type": "function", "function": tool_call}]})
์ด์์คํดํธ๊ฐ ํจ์ ์ถ๋ ฅ์ ์ฝ๊ณ ์ฌ์ฉ์์ ์ฑํ ํ ์ ์๋๋ก ํฉ๋๋ค.
inputs = tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True, return_dict=True, return_tensors="pt")
inputs = {k: v for k, v in inputs.items()}
out = model.generate(**inputs, max_new_tokens=128)
print(tokenizer.decode(out[0][len(inputs["input_ids"][0]):]))
The temperature in Paris, France right now is approximately 12ยฐC (53.6ยฐF).<|im_end|>
Mistral ๋ฐ Mixtral ๋ชจ๋ธ์ ๊ฒฝ์ฐ ์ถ๊ฐ์ ์ผ๋ก tool_call_id๊ฐ ํ์ํฉ๋๋ค. tool_call_id๋ 9์๋ฆฌ ์์ซ์ ๋ฌธ์์ด๋ก ์์ฑ๋์ด tool_call ๋์
๋๋ฆฌ์ id ํค์ ํ ๋น๋ฉ๋๋ค.
tool_call_id = "9Ae3bDc2F"
tool_call = {"name": "get_current_temperature", "arguments": {"location": "Paris, France", "unit": "celsius"}}
messages.append({"role": "assistant", "tool_calls": [{"type": "function", "id": tool_call_id, "function": tool_call}]})
inputs = tokenizer.apply_chat_template(messages, tools=tools, add_generation_prompt=True, return_dict=True, return_tensors="pt")
inputs = {k: v for k, v in inputs.items()}
out = model.generate(**inputs, max_new_tokens=128)
print(tokenizer.decode(out[0][len(inputs["input_ids"][0]):]))
์คํค๋งSchema
[~PreTrainedTokenizerBase.apply_chat_template]์ ํจ์๋ฅผ JSON ์คํค๋ง๋ก ๋ณํํ์ฌ ์ฑํ
ํ
ํ๋ฆฟ์ ์ ๋ฌํฉ๋๋ค. LLM์ ํจ์ ๋ด๋ถ์ ์ฝ๋๋ฅผ ๋ณด์ง ๋ชปํฉ๋๋ค. ๋ค์ ๋งํด, LLM์ ํจ์๊ฐ ๊ธฐ์ ์ ์ผ๋ก ์ด๋ป๊ฒ ์๋ํ๋์ง๋ ์ ๊ฒฝ ์ฐ์ง ์๊ณ , ํจ์์ ์ ์์ ์ธ์๋ง ์ฐธ์กฐํฉ๋๋ค.
ํจ์๊ฐ ์์ ๋์ด๋ ๊ท์น์ ๋ฐ๋ฅด๋ฉด, ๋ด๋ถ์์ JSON ์คํค๋ง๊ฐ ์๋์ผ๋ก ์์ฑ๋ฉ๋๋ค. ํ์ง๋ง ๋ ๋์ ๊ฐ๋ ์ฑ์ด๋ ๋๋ฒ๊น ์ ์ํด get_json_schema๋ฅผ ์ฌ์ฉํ์ฌ ์คํค๋ง๋ฅผ ์๋์ผ๋ก ๋ณํํ ์ ์์ต๋๋ค.
from transformers.utils import get_json_schema
def multiply(a: float, b: float):
"""
๋ ์ซ์๋ฅผ ๊ณฑํ๋ ํจ์
Args:
a: ๊ณฑํ ์ฒซ ๋ฒ์งธ ์ซ์
b: ๊ณฑํ ๋ ๋ฒ์งธ ์ซ์
"""
return a * b
schema = get_json_schema(multiply)
print(schema)
{
"type": "function",
"function": {
"name": "multiply",
"description": "A function that multiplies two numbers",
"parameters": {
"type": "object",
"properties": {
"a": {
"type": "number",
"description": "The first number to multiply"
},
"b": {
"type": "number",
"description": "The second number to multiply"
}
},
"required": ["a", "b"]
}
}
}
์คํค๋ง๋ฅผ ํธ์งํ๊ฑฐ๋ ์ฒ์๋ถํฐ ์ง์ ์์ฑํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ ๋ณต์กํ ํจ์์ ๋ํ ์ ํํ ์คํค๋ง๋ฅผ ์ ์ฐํ๊ฒ ์ ์ํ ์ ์์ต๋๋ค.
Warning
ํจ์ ์๊ทธ๋์ฒ๋ฅผ ๋จ์ํ๊ฒ ์ ์งํ๊ณ ์ธ์๋ฅผ ์ต์ํ์ผ๋ก ์ ์งํ์ธ์. ์ด๋ฌํ ํจ์๋ ์ค์ฒฉ๋ ์ธ์๋ฅผ ๊ฐ์ง ๋ณต์กํ ํจ์์ ๋นํด ๋ชจ๋ธ์ด ๋ ์ฝ๊ฒ ์ดํดํ๊ณ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์๋ ์์๋ ์คํค๋ง๋ฅผ ์๋์ผ๋ก ์์ฑํ ๋ค์ [~PreTrainedTokenizerBase.apply_chat_template]์ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค.
# ์ธ์๋ฅผ ๋ฐ์ง ์๋ ๊ฐ๋จํ ํจ์
current_time = {
"type": "function",
"function": {
"name": "current_time",
"description": "Get the current local time as a string.",
"parameters": {
'type': 'object',
'properties': {}
}
}
}
# ๋ ๊ฐ์ ์ซ์ ์ธ์๋ฅผ ๋ฐ๋ ๋ ์์ ํ ํจ์
multiply = {
'type': 'function',
'function': {
'name': 'multiply',
'description': 'A function that multiplies two numbers',
'parameters': {
'type': 'object',
'properties': {
'a': {
'type': 'number',
'description': 'The first number to multiply'
},
'b': {
'type': 'number', 'description': 'The second number to multiply'
}
},
'required': ['a', 'b']
}
}
}
model_input = tokenizer.apply_chat_template(
messages,
tools = [current_time, multiply]
)
RAGRAG
๊ฒ์ ์ฆ๊ฐ ์์ฑ(Retrieval-augmented generation, RAG) ๋ชจ๋ธ์ ์ฟผ๋ฆฌ๋ฅผ ๋ฐํํ๊ธฐ ์ ์ ๋ฌธ์๋ฅผ ๊ฒ์ํด ์ถ๊ฐ ์ ๋ณด๋ฅผ ์ป์ด ๋ชจ๋ธ์ด ๊ธฐ์กด์ ๊ฐ์ง๊ณ ์๋ ์ง์์ ํ์ฅ์ํต๋๋ค. RAG ๋ชจ๋ธ์ ๊ฒฝ์ฐ, [~PreTrainedTokenizerBase.apply_chat_template]์ documents ๋งค๊ฐ๋ณ์๋ฅผ ์ถ๊ฐํ์ธ์. ์ด documents ๋งค๊ฐ๋ณ์๋ ๋ฌธ์ ๋ชฉ๋ก์ด์ด์ผ ํ๋ฉฐ, ๊ฐ ๋ฌธ์๋ title๊ณผ content ํค๋ฅผ ๊ฐ์ง ๋จ์ผ ๋์
๋๋ฆฌ์ฌ์ผ ํฉ๋๋ค.
Tip
RAG๋ฅผ ์ํ
documents๋งค๊ฐ๋ณ์๋ ํญ๋๊ฒ ์ง์๋์ง ์์ผ๋ฉฐ ๋ง์ ๋ชจ๋ธ๋ค์ดdocuments๋ฅผ ๋ฌด์ํ๋ ์ฑํ ํ ํ๋ฆฟ์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๋ชจ๋ธ์ดdocuments๋ฅผ ์ง์ํ๋์ง ํ์ธํ๋ ค๋ฉด ๋ชจ๋ธ ์นด๋๋ฅผ ์ฝ๊ฑฐ๋print(tokenizer.chat_template)๋ฅผ ์คํํ์ฌdocumentsํค๊ฐ ์๋์ง ํ์ธํ์ธ์. Command-R๊ณผ Command-R+๋ ๋ชจ๋ RAG ์ฑํ ํ ํ๋ฆฟ์์documents๋ฅผ ์ง์ํฉ๋๋ค.
๋ชจ๋ธ์ ์ ๋ฌํ ๋ฌธ์ ๋ชฉ๋ก์ ์์ฑํ์ธ์.
documents = [
{
"title": "The Moon: Our Age-Old Foe",
"text": "Man has always dreamed of destroying the moon. In this essay, I shall..."
},
{
"title": "The Sun: Our Age-Old Friend",
"text": "Although often underappreciated, the sun provides several notable benefits..."
}
]
[~PreTrainedTokenizerBase.apply_chat_template]์์ chat_template="rag"๋ฅผ ์ค์ ํ๊ณ ์๋ต์ ์์ฑํ์ธ์.
from transformers import AutoTokenizer, AutoModelForCausalLM
# ๋ชจ๋ธ๊ณผ ํ ํฌ๋์ด์ ๋ก๋
tokenizer = AutoTokenizer.from_pretrained("CohereForAI/c4ai-command-r-v01-4bit")
model = AutoModelForCausalLM.from_pretrained("CohereForAI/c4ai-command-r-v01-4bit", device_map="auto")
device = model.device # ๋ชจ๋ธ์ ๊ฐ์ ธ์จ ์ฅ์น ํ์ธ
# ๋ํ ์
๋ ฅ ์ ์
conversation = [
{"role": "user", "content": "What has Man always dreamed of?"}
]
input_ids = tokenizer.apply_chat_template(
conversation=conversation,
documents=documents,
chat_template="rag",
tokenize=True,
add_generation_prompt=True,
return_tensors="pt").to(device)
# ์๋ต ์์ฑ
generated_tokens = model.generate(
input_ids,
max_new_tokens=100,
do_sample=True,
temperature=0.3,
)
# ์์ฑ๋ ํ
์คํธ๋ฅผ ๋์ฝ๋ฉํ๊ณ ์์ฑ ํ๋กฌํํธ์ ํจ๊ป ์ถ๋ ฅ
generated_text = tokenizer.decode(generated_tokens[0])
print(generated_text)