| <!--Copyright 2023 The HuggingFace Team. All rights reserved. | |
| Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with | |
| the License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | |
| an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | |
| specific language governing permissions and limitations under the License. | |
| โ ๏ธ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be | |
| rendered properly in your Markdown viewer. | |
| --> | |
| # ์ฑํ ๋ชจ๋ธ์ ์ํ ํ ํ๋ฆฟ[[templates-for-chat-models]] | |
| ## ์๊ฐ[[introduction]] | |
| ์์ฆ LLM์ ๊ฐ์ฅ ํํ ํ์ฉ ์ฌ๋ก ์ค ํ๋๋ **์ฑํ **์ ๋๋ค. ์ฑํ ์ ์ผ๋ฐ์ ์ธ ์ธ์ด ๋ชจ๋ธ์ฒ๋ผ ๋จ์ผ ๋ฌธ์์ด์ ์ด์ด๊ฐ๋ ๋์ ์ฌ๋ฌ ๊ฐ์ **๋ฉ์์ง**๋ก ๊ตฌ์ฑ๋ ๋ํ๋ฅผ ์ด์ด๊ฐ๋๋ค. ์ด ๋ํ์๋ "์ฌ์ฉ์"๋ "์ด์์คํดํธ"์ ๊ฐ์ **์ญํ **๊ณผ ๋ฉ์์ง ํ ์คํธ๊ฐ ํฌํจ๋ฉ๋๋ค. | |
| ํ ํฐํ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ๋ค์ํ ๋ชจ๋ธ์ ์ฑํ ์ ๋ํด ๋งค์ฐ ๋ค๋ฅธ ์ ๋ ฅ ํ์์ ๊ธฐ๋ํฉ๋๋ค. ์ด๊ฒ์ด ์ฐ๋ฆฌ๊ฐ **์ฑํ ํ ํ๋ฆฟ**์ ๊ธฐ๋ฅ์ผ๋ก ์ถ๊ฐํ ์ด์ ์ ๋๋ค. ์ฑํ ํ ํ๋ฆฟ์ ํ ํฌ๋์ด์ ์ ์ผ๋ถ์ ๋๋ค. ์ฑํ ํ ํ๋ฆฟ์ ๋ํ ๋ชฉ๋ก์ ๋ชจ๋ธ์ด ๊ธฐ๋ํ๋ ํ์์ธ '๋จ์ผ ํ ํฐํ๊ฐ ๊ฐ๋ฅํ ๋ฌธ์์ด'๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ์ ์ง์ ํฉ๋๋ค. | |
| `BlenderBot` ๋ชจ๋ธ์ ์ฌ์ฉํ ๊ฐ๋จํ ์์ ๋ฅผ ํตํด ์ด๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ์ดํด๋ณด๊ฒ ์ต๋๋ค. BlenderBot์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋งค์ฐ ๊ฐ๋จํ ํ ํ๋ฆฟ์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ์ฃผ๋ก ๋ํ ๋ผ์ด๋ ์ฌ์ด์ ๊ณต๋ฐฑ์ ์ถ๊ฐํฉ๋๋ค: | |
| ```python | |
| >>> from transformers import AutoTokenizer | |
| >>> tokenizer = AutoTokenizer.from_pretrained("facebook/blenderbot-400M-distill") | |
| >>> chat = [ | |
| ... {"role": "user", "content": "Hello, how are you?"}, | |
| ... {"role": "assistant", "content": "I'm doing great. How can I help you today?"}, | |
| ... {"role": "user", "content": "I'd like to show off how chat templating works!"}, | |
| ... ] | |
| >>> tokenizer.apply_chat_template(chat, tokenize=False) | |
| " Hello, how are you? I'm doing great. How can I help you today? I'd like to show off how chat templating works!</s>" | |
| ``` | |
| ์ ์ฒด ์ฑํ ์ด ํ๋์ ๋ฌธ์์ด๋ก ์์ถ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ ์ค์ ์ธ `tokenize=True`๋ฅผ ์ฌ์ฉํ๋ฉด, ๊ทธ ๋ฌธ์์ด๋ ํ ํฐํ๋ฉ๋๋ค. ๋ ๋ณต์กํ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๊ธฐ ์ํด `mistralai/Mistral-7B-Instruct-v0.1` ๋ชจ๋ธ์ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค. | |
| ```python | |
| >>> from transformers import AutoTokenizer | |
| >>> tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1") | |
| >>> chat = [ | |
| ... {"role": "user", "content": "Hello, how are you?"}, | |
| ... {"role": "assistant", "content": "I'm doing great. How can I help you today?"}, | |
| ... {"role": "user", "content": "I'd like to show off how chat templating works!"}, | |
| ... ] | |
| >>> tokenizer.apply_chat_template(chat, tokenize=False) | |
| "<s>[INST] Hello, how are you? [/INST]I'm doing great. How can I help you today?</s> [INST] I'd like to show off how chat templating works! [/INST]" | |
| ``` | |
| ์ด๋ฒ์๋ ํ ํฌ๋์ด์ ๊ฐ [INST]์ [/INST] ์ ์ด ํ ํฐ์ ์ถ๊ฐํ์ฌ ์ฌ์ฉ์ ๋ฉ์์ง์ ์์๊ณผ ๋์ ํ์ํ์ต๋๋ค(์ด์์คํดํธ ๋ฉ์์ง ์ ์ธ). Mistral-instruct๋ ์ด๋ฌํ ํ ํฐ์ผ๋ก ํ๋ จ๋์์ง๋ง, BlenderBot์ ๊ทธ๋ ์ง ์์์ต๋๋ค. | |
| ## ์ฑํ ํ ํ๋ฆฟ์ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์?[[how-do-i-use-chat-templates]] | |
| ์์ ์์์ ๋ณผ ์ ์๋ฏ์ด ์ฑํ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๊ธฐ ์ฝ์ต๋๋ค. `role`๊ณผ `content` ํค๊ฐ ํฌํจ๋ ๋ฉ์์ง ๋ชฉ๋ก์ ์์ฑํ ๋ค์, [`~PreTrainedTokenizer.apply_chat_template`] ๋ฉ์๋์ ์ ๋ฌํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ ์ถ๋ ฅ์ด ์์ฑ๋ฉ๋๋ค! ๋ชจ๋ธ ์์ฑ์ ์ ๋ ฅ์ผ๋ก ์ฑํ ํ ํ๋ฆฟ์ ์ฌ์ฉํ ๋, `add_generation_prompt=True`๋ฅผ ์ฌ์ฉํ์ฌ [์์ฑ ํ๋กฌํํธ](#what-are-generation-prompts)๋ฅผ ์ถ๊ฐํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ ๋๋ค. | |
| ๋ค์์ `Zephyr` ์ด์์คํดํธ ๋ชจ๋ธ์ ์ฌ์ฉํ์ฌ `model.generate()`์ ์ ๋ ฅ์ ์ค๋นํ๋ ์์ ์ ๋๋ค: | |
| ```python | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| checkpoint = "HuggingFaceH4/zephyr-7b-beta" | |
| tokenizer = AutoTokenizer.from_pretrained(checkpoint) | |
| model = AutoModelForCausalLM.from_pretrained(checkpoint) # ์ฌ๊ธฐ์ bfloat16 ์ฌ์ฉ ๋ฐ/๋๋ GPU๋ก ์ด๋ํ ์ ์์ต๋๋ค. | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": "You are a friendly chatbot who always responds in the style of a pirate", | |
| }, | |
| {"role": "user", "content": "How many helicopters can a human eat in one sitting?"}, | |
| ] | |
| tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt") | |
| print(tokenizer.decode(tokenized_chat[0])) | |
| ``` | |
| ์ด๋ ๊ฒ ํ๋ฉด Zephyr๊ฐ ๊ธฐ๋ํ๋ ์ ๋ ฅ ํ์์ ๋ฌธ์์ด์ด ์์ฑ๋ฉ๋๋ค. | |
| ```text | |
| <|system|> | |
| You are a friendly chatbot who always responds in the style of a pirate</s> | |
| <|user|> | |
| How many helicopters can a human eat in one sitting?</s> | |
| <|assistant|> | |
| ``` | |
| ์ด์ ์ ๋ ฅ์ด Zephyr์ ๋ง๊ฒ ํ์์ด ์ง์ ๋์์ผ๋ฏ๋ก ๋ชจ๋ธ์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์์ ์ง๋ฌธ์ ๋ํ ์๋ต์ ์์ฑํ ์ ์์ต๋๋ค: | |
| ```python | |
| outputs = model.generate(tokenized_chat, max_new_tokens=128) | |
| print(tokenizer.decode(outputs[0])) | |
| ``` | |
| ์ด๋ ๊ฒ ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ ๋์ต๋๋ค: | |
| ```text | |
| <|system|> | |
| You are a friendly chatbot who always responds in the style of a pirate</s> | |
| <|user|> | |
| How many helicopters can a human eat in one sitting?</s> | |
| <|assistant|> | |
| Matey, I'm afraid I must inform ye that humans cannot eat helicopters. Helicopters are not food, they are flying machines. Food is meant to be eaten, like a hearty plate o' grog, a savory bowl o' stew, or a delicious loaf o' bread. But helicopters, they be for transportin' and movin' around, not for eatin'. So, I'd say none, me hearties. None at all. | |
| ``` | |
| ์ด์ ์ฌ์์ก์ฃ ! | |
| ## ์ฑํ ์ ์ํ ์๋ํ๋ ํ์ดํ๋ผ์ธ์ด ์๋์?[[is-there-an-automated-pipeline-for-chat]] | |
| ๋ค, ์์ต๋๋ค! ์ฐ๋ฆฌ์ ํ ์คํธ ์์ฑ ํ์ดํ๋ผ์ธ์ ์ฑํ ์ ๋ ฅ์ ์ง์ํ์ฌ ์ฑํ ๋ชจ๋ธ์ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด์ ์๋ "ConversationalPipeline" ํด๋์ค๋ฅผ ์ฌ์ฉํ์ง๋ง, ์ด์ ๋ ์ด ๊ธฐ๋ฅ์ด [`TextGenerationPipeline`]์ ํตํฉ๋์์ต๋๋ค. ์ด๋ฒ์๋ ํ์ดํ๋ผ์ธ์ ์ฌ์ฉํ์ฌ `Zephyr` ์์ ๋ฅผ ๋ค์ ์๋ํด ๋ณด๊ฒ ์ต๋๋ค: | |
| ```python | |
| from transformers import pipeline | |
| pipe = pipeline("text-generation", "HuggingFaceH4/zephyr-7b-beta") | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": "You are a friendly chatbot who always responds in the style of a pirate", | |
| }, | |
| {"role": "user", "content": "How many helicopters can a human eat in one sitting?"}, | |
| ] | |
| print(pipe(messages, max_new_tokens=128)[0]['generated_text'][-1]) # ์ด์์คํดํธ์ ์๋ต์ ์ถ๋ ฅํฉ๋๋ค. | |
| ``` | |
| ```text | |
| {'role': 'assistant', 'content': "Matey, I'm afraid I must inform ye that humans cannot eat helicopters. Helicopters are not food, they are flying machines. Food is meant to be eaten, like a hearty plate o' grog, a savory bowl o' stew, or a delicious loaf o' bread. But helicopters, they be for transportin' and movin' around, not for eatin'. So, I'd say none, me hearties. None at all."} | |
| ``` | |
| ํ์ดํ๋ผ์ธ์ ํ ํฐํ์ `apply_chat_template` ํธ์ถ ์ ์ธ๋ถ ์ฌํญ์ ๋ชจ๋ ์ฒ๋ฆฌํด์ฃผ๊ธฐ ๋๋ฌธ์, ๋ชจ๋ธ์ ์ฑํ ํ ํ๋ฆฟ์ด ์์ผ๋ฉด ํ์ดํ๋ผ์ธ์ ์ด๊ธฐํํ๊ณ ๋ฉ์์ง ๋ชฉ๋ก์ ์ ๋ฌํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค! | |
| ## "์์ฑ ํ๋กฌํํธ"๋ ๋ฌด์์ธ๊ฐ์?[[what-are-generation-prompts]] | |
| `apply_chat_template` ๋ฉ์๋์๋ `add_generation_prompt` ์ธ์๊ฐ ์๋ค๋ ๊ฒ์ ๋์น์ฑ์ ๊ฒ์ ๋๋ค. ์ด ์ธ์๋ ํ ํ๋ฆฟ์ ๋ด ์๋ต์ ์์์ ๋ํ๋ด๋ ํ ํฐ์ ์ถ๊ฐํ๋๋ก ์ง์ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ๋ค์๊ณผ ๊ฐ์ ์ฑํ ์ ๊ณ ๋ คํด ๋ณด์ธ์: | |
| ```python | |
| messages = [ | |
| {"role": "user", "content": "Hi there!"}, | |
| {"role": "assistant", "content": "Nice to meet you!"}, | |
| {"role": "user", "content": "Can I ask a question?"} | |
| ] | |
| ``` | |
| Zephyr ์์ ์์ ๋ณด์๋ ๊ฒ๊ณผ ๊ฐ์ด, ์์ฑ ํ๋กฌํํธ ์์ด ChatML ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ผ ๊ฒ์ ๋๋ค: | |
| ```python | |
| tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False) | |
| """<|im_start|>user | |
| Hi there!<|im_end|> | |
| <|im_start|>assistant | |
| Nice to meet you!<|im_end|> | |
| <|im_start|>user | |
| Can I ask a question?<|im_end|> | |
| """ | |
| ``` | |
| ์์ฑ ํ๋กฌํํธ๊ฐ **์๋** ๊ฒฝ์ฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ```python | |
| tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) | |
| """<|im_start|>user | |
| Hi there!<|im_end|> | |
| <|im_start|>assistant | |
| Nice to meet you!<|im_end|> | |
| <|im_start|>user | |
| Can I ask a question?<|im_end|> | |
| <|im_start|>assistant | |
| """ | |
| ``` | |
| ์ด๋ฒ์๋ ๋ด ์๋ต์ ์์์ ๋ํ๋ด๋ ํ ํฐ์ ์ถ๊ฐํ ๊ฒ์ ์ฃผ๋ชฉํ์ธ์. ์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋ธ์ด ํ ์คํธ๋ฅผ ์์ฑํ ๋ ์ฌ์ฉ์์ ๋ฉ์์ง๋ฅผ ๊ณ์ํ๋ ๋์ ๋ด ์๋ต์ ์์ฑํ๊ฒ ๋ฉ๋๋ค. ๊ธฐ์ตํ์ธ์, ์ฑํ ๋ชจ๋ธ์ ์ฌ์ ํ ์ธ์ด ๋ชจ๋ธ์ผ ๋ฟ์ด๋ฉฐ, ๊ทธ๋ค์๊ฒ ์ฑํ ์ ํน๋ณํ ์ข ๋ฅ์ ํ ์คํธ์ผ ๋ฟ์ ๋๋ค! ์ ์ ํ ์ ์ด ํ ํฐ์ผ๋ก ์๋ดํด์ผ ์ฑํ ๋ชจ๋ธ์ด ๋ฌด์์ ํด์ผ ํ๋์ง ์ ์ ์์ต๋๋ค. | |
| ๋ชจ๋ ๋ชจ๋ธ์ด ์์ฑ ํ๋กฌํํธ๋ฅผ ํ์๋ก ํ๋ ๊ฒ์ ์๋๋๋ค. BlenderBot๊ณผ LLaMA ๊ฐ์ ์ผ๋ถ ๋ชจ๋ธ์ ๋ด ์๋ต ์ ์ ํน๋ณํ ํ ํฐ์ด ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ `add_generation_prompt` ์ธ์๋ ํจ๊ณผ๊ฐ ์์ต๋๋ค. `add_generation_prompt`์ ์ ํํ ํจ๊ณผ๋ ์ฌ์ฉ ์ค์ธ ํ ํ๋ฆฟ์ ๋ฐ๋ผ ๋ค๋ฆ ๋๋ค. | |
| ## ์ฑํ ํ ํ๋ฆฟ์ ํ๋ จ์ ์ฌ์ฉํ ์ ์๋์?[[can-i-use-chat-templates-in-training]] | |
| ๋ค! ์ด ๋ฐฉ๋ฒ์ ์ฑํ ํ ํ๋ฆฟ์ ๋ชจ๋ธ์ด ํ๋ จ ์ค์ ๋ณด๋ ํ ํฐ๊ณผ ์ผ์นํ๋๋ก ํ๋ ์ข์ ๋ฐฉ๋ฒ์ ๋๋ค. ๋ฐ์ดํฐ ์ธํธ์ ๋ํ ์ ์ฒ๋ฆฌ ๋จ๊ณ๋ก ์ฑํ ํ ํ๋ฆฟ์ ์ ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ๊ทธ ํ์๋ ๋ค๋ฅธ ์ธ์ด ๋ชจ๋ธ ํ๋ จ ์์ ๊ณผ ๊ฐ์ด ๊ณ์ํ ์ ์์ต๋๋ค. ํ๋ จํ ๋๋ ์ผ๋ฐ์ ์ผ๋ก `add_generation_prompt=False`๋ก ์ค์ ํด์ผ ํฉ๋๋ค. ์ด์์คํดํธ ์๋ต์ ํ๋กฌํํธํ๋ ์ถ๊ฐ ํ ํฐ์ ํ๋ จ ์ค์๋ ๋์์ด ๋์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ์์ ๋ฅผ ๋ณด๊ฒ ์ต๋๋ค: | |
| ```python | |
| from transformers import AutoTokenizer | |
| from datasets import Dataset | |
| tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta") | |
| chat1 = [ | |
| {"role": "user", "content": "Which is bigger, the moon or the sun?"}, | |
| {"role": "assistant", "content": "The sun."} | |
| ] | |
| chat2 = [ | |
| {"role": "user", "content": "Which is bigger, a virus or a bacterium?"}, | |
| {"role": "assistant", "content": "A bacterium."} | |
| ] | |
| dataset = Dataset.from_dict({"chat": [chat1, chat2]}) | |
| dataset = dataset.map(lambda x: {"formatted_chat": tokenizer.apply_chat_template(x["chat"], tokenize=False, add_generation_prompt=False)}) | |
| print(dataset['formatted_chat'][0]) | |
| ``` | |
| ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค: | |
| ```text | |
| <|user|> | |
| Which is bigger, the moon or the sun?</s> | |
| <|assistant|> | |
| The sun.</s> | |
| ``` | |
| ์ฌ๊ธฐ์๋ถํฐ๋ ์ผ๋ฐ์ ์ธ ์ธ์ด ๋ชจ๋ธ ์์ ๊ณผ ๊ฐ์ด `formatted_chat` ์ด์ ์ฌ์ฉํ์ฌ ํ๋ จ์ ๊ณ์ํ๋ฉด ๋ฉ๋๋ค. | |
| <Tip> | |
| `apply_chat_template(tokenize=False)`๋ก ํ ์คํธ๋ฅผ ํ์ํํ ๋ค์ ๋ณ๋์ ๋จ๊ณ์์ ํ ํฐํํ๋ ๊ฒฝ์ฐ, `add_special_tokens=False` ์ธ์๋ฅผ ์ค์ ํด์ผ ํฉ๋๋ค. `apply_chat_template(tokenize=True)`๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ ์ด ๋ฌธ์ ๋ฅผ ๊ฑฑ์ ํ ํ์๊ฐ ์์ต๋๋ค! | |
| ๊ธฐ๋ณธ์ ์ผ๋ก ์ผ๋ถ ํ ํฌ๋์ด์ ๋ ํ ํฐํํ ๋ `<bos>` ๋ฐ `<eos>`์ ๊ฐ์ ํน๋ณ ํ ํฐ์ ์ถ๊ฐํฉ๋๋ค. ์ฑํ ํ ํ๋ฆฟ์ ํญ์ ํ์ํ ๋ชจ๋ ํน๋ณ ํ ํฐ์ ํฌํจํด์ผ ํ๋ฏ๋ก, ๊ธฐ๋ณธ `add_special_tokens=True`๋ก ์ถ๊ฐ์ ์ธ ํน๋ณ ํ ํฐ์ ์ถ๊ฐํ๋ฉด ์๋ชป๋๊ฑฐ๋ ์ค๋ณต๋๋ ํน๋ณ ํ ํฐ์ ์์ฑํ์ฌ ๋ชจ๋ธ ์ฑ๋ฅ์ด ์ ํ๋ ์ ์์ต๋๋ค. | |
| </Tip> | |
| ## ๊ณ ๊ธ: ์ฑํ ํ ํ๋ฆฟ์ ์ถ๊ฐ ์ ๋ ฅ ์ฌ์ฉ[[advanced-extra-inputs-to-chat-templates]] | |
| `apply_chat_template`๊ฐ ํ์ํ ์ ์ผํ ์ธ์๋ `messages`์ ๋๋ค. ๊ทธ๋ฌ๋ `apply_chat_template`์ ํค์๋ ์ธ์๋ฅผ ์ ๋ฌํ๋ฉด ํ ํ๋ฆฟ ๋ด๋ถ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ์ฑํ ํ ํ๋ฆฟ์ ๋ค์ํ ์ฉ๋๋ก ์ฌ์ฉํ ์ ์๋ ์์ ๋ฅผ ์ป์ ์ ์์ต๋๋ค. ์ด๋ฌํ ์ธ์์ ์ด๋ฆ์ด๋ ํ์์๋ ์ ํ์ด ์์ด ๋ฌธ์์ด, ๋ฆฌ์คํธ, ๋์ ๋๋ฆฌ ๋ฑ์ ์ ๋ฌํ ์ ์์ต๋๋ค. | |
| ๊ทธ๋ ๊ธด ํ์ง๋ง, ์ด๋ฌํ ์ถ๊ฐ ์ธ์์ ์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก๋ก 'ํจ์ ํธ์ถ์ ์ํ ๋๊ตฌ'๋ '๊ฒ์ ์ฆ๊ฐ ์์ฑ์ ์ํ ๋ฌธ์'๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ด ์์ต๋๋ค. ์ด๋ฌํ ์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ์ ๋ํด ์ธ์์ ์ด๋ฆ๊ณผ ํ์์ ๋ํ ๋ช ๊ฐ์ง ๊ถ์ฅ ์ฌํญ์ด ์์ผ๋ฉฐ, ์ด๋ ์๋ ์น์ ์ ์ค๋ช ๋์ด ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๋ชจ๋ธ ์์ฑ์์๊ฒ ๋๊ตฌ ํธ์ถ ์ฝ๋๋ฅผ ๋ชจ๋ธ ๊ฐ์ ์ฝ๊ฒ ์ ์กํ ์ ์๋๋ก ์ฑํ ํ ํ๋ฆฟ์ ์ด ํ์๊ณผ ํธํ๋๋๋ก ๋ง๋ค ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. | |
| ## ๊ณ ๊ธ: ๋๊ตฌ ์ฌ์ฉ / ํจ์ ํธ์ถ[[advanced-tool-use--function-calling]] | |
| "๋๊ตฌ ์ฌ์ฉ" LLM์ ๋ต๋ณ์ ์์ฑํ๊ธฐ ์ ์ ์ธ๋ถ ๋๊ตฌ๋ก์ ํจ์๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค. ๋๊ตฌ ์ฌ์ฉ ๋ชจ๋ธ์ ๋๊ตฌ๋ฅผ ์ ๋ฌํ ๋๋ ๋จ์ํ ํจ์ ๋ชฉ๋ก์ `tools` ์ธ์๋ก ์ ๋ฌํ ์ ์์ต๋๋ค: | |
| ```python | |
| import datetime | |
| def current_time(): | |
| """ํ์ฌ ํ์ง ์๊ฐ์ ๋ฌธ์์ด๋ก ๊ฐ์ ธ์ต๋๋ค.""" | |
| return str(datetime.now()) | |
| def multiply(a: float, b: float): | |
| """ | |
| ๋ ์ซ์๋ฅผ ๊ณฑํ๋ ํจ์ | |
| ์ธ์: | |
| a: ๊ณฑํ ์ฒซ ๋ฒ์งธ ์ซ์ | |
| b: ๊ณฑํ ๋ ๋ฒ์งธ ์ซ์ | |
| """ | |
| return a * b | |
| tools = [current_time, multiply] | |
| model_input = tokenizer.apply_chat_template( | |
| messages, | |
| tools=tools | |
| ) | |
| ``` | |
| ์ด๊ฒ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋ ค๋ฉด ํจ์๋ฅผ ์ ํ์์ผ๋ก ์์ฑํด์ผ ๋๊ตฌ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌ๋ฌธ ๋ถ์ํ ์ ์์ต๋๋ค. ๊ตฌ์ฒด์ ์ผ๋ก ๋ค์ ๊ท์น์ ๋ฐ๋ผ์ผ ํฉ๋๋ค: | |
| - ํจ์๋ ์ค๋ช ์ ์ธ ์ด๋ฆ์ ๊ฐ์ ธ์ผ ํฉ๋๋ค. | |
| - ๋ชจ๋ ์ธ์์๋ ํ์ ํํธ๊ฐ ์์ด์ผ ํฉ๋๋ค. | |
| - ํจ์์๋ ํ์ค Google ์คํ์ผ์ ๋ํฌ์คํธ๋ง์ด ์์ด์ผ ํฉ๋๋ค(์ฆ, ์ด๊ธฐ ํจ์ ์ค๋ช ๋ค์์ ์ธ์๋ฅผ ์ค๋ช ํ๋ `Args:` ๋ธ๋ก์ด ์์ด์ผ ํฉ๋๋ค). | |
| - `Args:` ๋ธ๋ก์๋ ํ์ ์ ํฌํจํ์ง ๋ง์ธ์. ์ฆ, `a (int): The first number to multiply` ๋์ `a: The first number to multiply`๋ผ๊ณ ์์ฑํด์ผ ํฉ๋๋ค. ํ์ ํํธ๋ ํจ์ ํค๋์ ์์ด์ผ ํฉ๋๋ค. | |
| - ํจ์์๋ ๋ฐํ ํ์ ๊ณผ ๋ํฌ์คํธ๋ง์ `Returns:` ๋ธ๋ก์ด ์์ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋๋ถ๋ถ์ ๋๊ตฌ ์ฌ์ฉ ๋ชจ๋ธ์ ์ด๋ฅผ ๋ฌด์ํ๋ฏ๋ก ์ด๋ ์ ํ ์ฌํญ์ ๋๋ค. | |
| ### ๋๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ๋ธ์ ์ ๋ฌํ๊ธฐ[[passing-tool-results-to-the-model]] | |
| ์์ ์์ ์ฝ๋๋ ๋ชจ๋ธ์ ์ฌ์ฉํ ์ ์๋ ๋๊ตฌ๋ฅผ ๋์ดํ๋ ๋ฐ ์ถฉ๋ถํ์ง๋ง, ์ค์ ๋ก ์ฌ์ฉํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ๋ ์ด๋ป๊ฒ ํด์ผ ํ ๊น์? ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ๋ค์์ ์ํํด์ผ ํฉ๋๋ค: | |
| 1. ๋ชจ๋ธ์ ์ถ๋ ฅ์ ํ์ฑํ์ฌ ๋๊ตฌ ์ด๋ฆ๊ณผ ์ธ์๋ฅผ ๊ฐ์ ธ์ต๋๋ค. | |
| 2. ๋ชจ๋ธ์ ๋๊ตฌ ํธ์ถ์ ๋ํ์ ์ถ๊ฐํฉ๋๋ค. | |
| 3. ํด๋น ์ธ์์ ๋์ํ๋ ํจ์๋ฅผ ํธ์ถํฉ๋๋ค. | |
| 4. ๊ฒฐ๊ณผ๋ฅผ ๋ํ์ ์ถ๊ฐํฉ๋๋ค. | |
| ### ๋๊ตฌ ์ฌ์ฉ ์์ [[a-complete-tool-use-example]] | |
| ๋๊ตฌ ์ฌ์ฉ ์์ ๋ฅผ ๋จ๊ณ๋ณ๋ก ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ด ์์ ์์๋ ๋๊ตฌ ์ฌ์ฉ ๋ชจ๋ธ ์ค์์ ์ฑ๋ฅ์ด ๊ฐ์ฅ ์ฐ์ํ 8B `Hermes-2-Pro` ๋ชจ๋ธ์ ์ฌ์ฉํ ๊ฒ์ ๋๋ค. ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ถฉ๋ถํ๋ค๋ฉด, ๋ ํฐ ๋ชจ๋ธ์ธ [Command-R](https://huggingface.co/CohereForAI/c4ai-command-r-v01) ๋๋ [Mixtral-8x22B](https://huggingface.co/mistralai/Mixtral-8x22B-Instruct-v0.1)๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ ๊ณ ๋ คํ ์ ์์ต๋๋ค. ์ด ๋ ๋ชจ๋ธ ๋ชจ๋ ๋๊ตฌ ์ฌ์ฉ์ ์ง์ํ๋ฉฐ ๋ ๊ฐ๋ ฅํ ์ฑ๋ฅ์ ์ ๊ณตํฉ๋๋ค. | |
| ๋จผ์ ๋ชจ๋ธ๊ณผ ํ ํฌ๋์ด์ ๋ฅผ ๋ก๋ํด ๋ณด๊ฒ ์ต๋๋ค: | |
| ```python | |
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| checkpoint = "NousResearch/Hermes-2-Pro-Llama-3-8B" | |
| tokenizer = AutoTokenizer.from_pretrained(checkpoint, revision="pr/13") | |
| model = AutoModelForCausalLM.from_pretrained(checkpoint, torch_dtype=torch.bfloat16, device_map="auto") | |
| ``` | |
| ๋ค์์ผ๋ก, ๋๊ตฌ ๋ชฉ๋ก์ ์ ์ํด ๋ณด๊ฒ ์ต๋๋ค: | |
| ```python | |
| def get_current_temperature(location: str, unit: str) -> float: | |
| """ | |
| ํน์ ์์น์ ํ์ฌ ์จ๋๋ฅผ ๊ฐ์ ธ์ต๋๋ค. | |
| ์ธ์: | |
| ์์น: ์จ๋๋ฅผ ๊ฐ์ ธ์ฌ ์์น, "๋์, ๊ตญ๊ฐ" ํ์ | |
| ๋จ์: ์จ๋ ๋จ์ (์ ํ์ง: ["celsius", "fahrenheit"]) | |
| ๋ฐํ๊ฐ: | |
| ์ง์ ๋ ์์น์ ํ์ฌ ์จ๋๋ฅผ ์ง์ ๋ ๋จ์๋ก ๋ฐํ, float ํ์. | |
| """ | |
| return 22. # ์ด ํจ์๋ ์ค์ ๋ก ์จ๋๋ฅผ ๊ฐ์ ธ์์ผ ํ ๊ฒ์ ๋๋ค! | |
| def get_current_wind_speed(location: str) -> float: | |
| """ | |
| ์ฃผ์ด์ง ์์น์ ํ์ฌ ํ์์ km/h ๋จ์๋ก ๊ฐ์ ธ์ต๋๋ค. | |
| ์ธ์: | |
| ์์น(location): ํ์์ ๊ฐ์ ธ์ฌ ์์น, "๋์, ๊ตญ๊ฐ" ํ์ | |
| ๋ฐํ๊ฐ: | |
| ์ฃผ์ด์ง ์์น์ ํ์ฌ ํ์์ km/h ๋จ์๋ก ๋ฐํ, float ํ์. | |
| """ | |
| return 6. # ์ด ํจ์๋ ์ค์ ๋ก ํ์์ ๊ฐ์ ธ์์ผ ํ ๊ฒ์ ๋๋ค! | |
| tools = [get_current_temperature, get_current_wind_speed] | |
| ``` | |
| ์ด์ ๋ด์ ์ํ ๋ํ๋ฅผ ์ค์ ํด ๋ณด๊ฒ ์ต๋๋ค: | |
| ```python | |
| 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?"} | |
| ] | |
| ``` | |
| ์ด์ ์ฑํ ํ ํ๋ฆฟ์ ์ ์ฉํ๊ณ ์๋ต์ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค: | |
| ```python | |
| inputs = tokenizer.apply_chat_template(messages, chat_template="tool_use", tools=tools, add_generation_prompt=True, return_dict=True, return_tensors="pt") | |
| inputs = {k: v.to(model.device) for k, v in inputs.items()} | |
| out = model.generate(**inputs, max_new_tokens=128) | |
| print(tokenizer.decode(out[0][len(inputs["input_ids"][0]):])) | |
| ``` | |
| ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ```text | |
| <tool_call> | |
| {"arguments": {"location": "Paris, France", "unit": "celsius"}, "name": "get_current_temperature"} | |
| </tool_call><|im_end|> | |
| ``` | |
| ๋ชจ๋ธ์ด ํจ์ ํธ์ถ์ ์ ํจํ ์ธ์๋ก ์ํํ์ผ๋ฉฐ, ํจ์ ๋ํฌ์คํธ๋ง์ ์์ฒญ๋ ํ์์ผ๋ก ํธ์ถํ์์ ์ ์ ์์ต๋๋ค. ๋ชจ๋ธ์ ์ฐ๋ฆฌ๊ฐ ํ๋์ค์ ํ๋ฆฌ๋ฅผ ์ง์นญํ๊ณ ์๋ค๋ ๊ฒ์ ์ถ๋ก ํ๊ณ , ํ๋์ค๊ฐ SI ๋จ์์ ๋ณธ๊ณ ์ฅ์์ ๊ธฐ์ตํ์ฌ ์จ๋๋ฅผ ์ญ์จ๋ก ํ์ํด์ผ ํ๋ค๊ณ ํ๋จํ์ต๋๋ค. | |
| ๋ชจ๋ธ์ ๋๊ตฌ ํธ์ถ์ ๋ํ์ ์ถ๊ฐํด ๋ณด๊ฒ ์ต๋๋ค. ์ฌ๊ธฐ์ ์์์ `tool_call_id`๋ฅผ ์์ฑํฉ๋๋ค. ์ด ID๋ ๋ชจ๋ ๋ชจ๋ธ์์ ์ฌ์ฉ๋๋ ๊ฒ์ ์๋์ง๋ง, ์ฌ๋ฌ ๋๊ตฌ ํธ์ถ์ ํ ๋ฒ์ ๋ฐํํ๊ณ ๊ฐ ์๋ต์ด ์ด๋ ํธ์ถ์ ํด๋นํ๋์ง ์ถ์ ํ ์ ์๊ฒ ํด์ค๋๋ค. ์ด ID๋ ๋ํ ๋ด์์ ๊ณ ์ ํด์ผ ํฉ๋๋ค. | |
| ```python | |
| tool_call_id = "vAHdf3" # ์์์ ID, ๊ฐ ๋๊ตฌ ํธ์ถ๋ง๋ค ๊ณ ์ ํด์ผ ํจ | |
| tool_call = {"name": "get_current_temperature", "arguments": {"location": "Paris, France", "unit": "celsius"}} | |
| messages.append({"role": "assistant", "tool_calls": [{"id": tool_call_id, "type": "function", "function": tool_call}]}) | |
| ``` | |
| ์ด์ ๋๊ตฌ ํธ์ถ์ ๋ํ์ ์ถ๊ฐํ์ผ๋ฏ๋ก, ํจ์๋ฅผ ํธ์ถํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ํ์ ์ถ๊ฐํ ์ ์์ต๋๋ค. ์ด ์์ ์์๋ ํญ์ 22.0์ ๋ฐํํ๋ ๋๋ฏธ ํจ์๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฏ๋ก, ๊ฒฐ๊ณผ๋ฅผ ์ง์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค. ๋ค์ ํ ๋ฒ, `tool_call_id`๋ ๋๊ตฌ ํธ์ถ์ ์ฌ์ฉํ๋ ID์ ์ผ์นํด์ผ ํฉ๋๋ค. | |
| ```python | |
| messages.append({"role": "tool", "tool_call_id": tool_call_id, "name": "get_current_temperature", "content": "22.0"}) | |
| ``` | |
| ๋ง์ง๋ง์ผ๋ก, ์ด์์คํดํธ๊ฐ ํจ์ ์ถ๋ ฅ์ ์ฝ๊ณ ์ฌ์ฉ์์ ๊ณ์ ๋ํํ ์ ์๋๋ก ํ๊ฒ ์ต๋๋ค: | |
| ```python | |
| inputs = tokenizer.apply_chat_template(messages, chat_template="tool_use", tools=tools, add_generation_prompt=True, return_dict=True, return_tensors="pt") | |
| inputs = {k: v.to(model.device) for k, v in inputs.items()} | |
| out = model.generate(**inputs, max_new_tokens=128) | |
| print(tokenizer.decode(out[0][len(inputs["input_ids"][0]):])) | |
| ``` | |
| ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ```text | |
| The current temperature in Paris, France is 22.0 ยฐ Celsius.<|im_end|> | |
| ``` | |
| ์ด๊ฒ์ ๋๋ฏธ ๋๊ตฌ์ ๋จ์ผ ํธ์ถ์ ์ฌ์ฉํ ๊ฐ๋จํ ๋ฐ๋ชจ์์ง๋ง, ๋์ผํ ๊ธฐ์ ์ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ์ค์ ๋๊ตฌ์ ๋ ๊ธด ๋ํ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ์ค์๊ฐ ์ ๋ณด, ๊ณ์ฐ ๋๊ตฌ ๋๋ ๋๊ท๋ชจ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ์ฌ ๋ํํ ์์ด์ ํธ์ ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์์ต๋๋ค. | |
| <Tip> | |
| ์์์ ๋ณด์ฌ์ค ๋๊ตฌ ํธ์ถ ๊ธฐ๋ฅ์ ๋ชจ๋ ๋ชจ๋ธ์์ ์ฌ์ฉ๋๋ ๊ฒ์ ์๋๋๋ค. ์ผ๋ถ ๋ชจ๋ธ์ ๋๊ตฌ ํธ์ถ ID๋ฅผ ์ฌ์ฉํ๊ณ , ์ผ๋ถ๋ ํจ์ ์ด๋ฆ๋ง ์ฌ์ฉํ์ฌ ๊ฒฐ๊ณผ์ ๋๊ตฌ ํธ์ถ์ ์์์ ๋ฐ๋ผ ๋งค์นญํ๋ฉฐ, ํผ๋์ ํผํ๊ธฐ ์ํด ํ ๋ฒ์ ํ๋์ ๋๊ตฌ ํธ์ถ๋ง ๋ฐํํ๋ ๋ชจ๋ธ๋ ์์ต๋๋ค. ๊ฐ๋ฅํ ๋ง์ ๋ชจ๋ธ๊ณผ ํธํ๋๋ ์ฝ๋๋ฅผ ์ํ๋ค๋ฉด, ์ฌ๊ธฐ์ ๋ณด์ฌ์ค ๊ฒ์ฒ๋ผ ๋๊ตฌ ํธ์ถ์ ๊ตฌ์ฑํ๊ณ , ๋ชจ๋ธ์ด ๋ฐํํ ์์๋๋ก ๋๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. ๊ฐ ๋ชจ๋ธ์ ์ฑํ ํ ํ๋ฆฟ์ด ๋๋จธ์ง ์์ ์ ์ฒ๋ฆฌํ ๊ฒ์ ๋๋ค. | |
| </Tip> | |
| ### ๋๊ตฌ ์คํค๋ง ์ดํดํ๊ธฐ[[understanding-tool-schemas]] | |
| `apply_chat_template`์ `tools` ์ธ์์ ์ ๋ฌํ๋ ๊ฐ ํจ์๋ [JSON ์คํค๋ง](https://json-schema.org/learn/getting-started-step-by-step)๋ก ๋ณํ๋ฉ๋๋ค. ์ด๋ฌํ ์คํค๋ง๋ ๋ชจ๋ธ ์ฑํ ํ ํ๋ฆฟ์ ์ ๋ฌ๋ฉ๋๋ค. ์ฆ, ๋๊ตฌ ์ฌ์ฉ ๋ชจ๋ธ์ ํจ์ ์์ฒด๋ฅผ ์ง์ ๋ณด์ง ์์ผ๋ฉฐ, ํจ์ ๋ด๋ถ์ ์ค์ ์ฝ๋๋ฅผ ๋ณด์ง ์์ต๋๋ค. ๋๊ตฌ ์ฌ์ฉ ๋ชจ๋ธ์ด ๊ด์ฌ์ ๊ฐ์ง๋ ๊ฒ์ ํจ์ **์ ์**์ **์ธ์**์ ๋๋ค. ํจ์๊ฐ ๋ฌด์์ ํ๊ณ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง์ ๊ด์ฌ์ด ์์ ๋ฟ, ์ด๋ป๊ฒ ์๋ํ๋์ง๋ ์ค์ํ์ง ์์ต๋๋ค! ๋ชจ๋ธ์ ์ถ๋ ฅ์ ์ฝ๊ณ ๋ชจ๋ธ์ด ๋๊ตฌ ์ฌ์ฉ์ ์์ฒญํ๋์ง ๊ฐ์งํ์ฌ, ์ธ์๋ฅผ ๋๊ตฌ ํจ์์ ์ ๋ฌํ๊ณ ์ฑํ ์์ ์๋ต์ ๋ฐํํ๋ ๊ฒ์ ์ฌ๋ฌ๋ถ์ ๋ชซ์ ๋๋ค. | |
| ์์ ๊ท๊ฒฉ์ ๋ฐ๋ฅธ๋ค๋ฉด, ํ ํ๋ฆฟ์ ์ ๋ฌํ JSON ์คํค๋ง ์์ฑ์ ์๋ํํ๊ณ ๋ณด์ด์ง ์๊ฒ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฑฐ๋ ๋ณํ์ ๋ ์ ์ดํ๊ณ ์ถ๋ค๋ฉด ์๋์ผ๋ก ๋ณํ์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ๋ค์์ ์๋ ์คํค๋ง ๋ณํ ์์ ์ ๋๋ค. | |
| ```python | |
| from transformers.utils import get_json_schema | |
| def multiply(a: float, b: float): | |
| """ | |
| ๋ ์ซ์๋ฅผ ๊ณฑํ๋ ํจ์ | |
| ์ธ์: | |
| a: ๊ณฑํ ์ฒซ ๋ฒ์งธ ์ซ์ | |
| b: ๊ณฑํ ๋ ๋ฒ์งธ ์ซ์ | |
| """ | |
| return a * b | |
| schema = get_json_schema(multiply) | |
| print(schema) | |
| ``` | |
| ์ด ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ```json | |
| { | |
| "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"] | |
| } | |
| } | |
| } | |
| ``` | |
| ์ํ๋ค๋ฉด ์ด๋ฌํ ์คํค๋ง๋ฅผ ํธ์งํ๊ฑฐ๋ `get_json_schema`๋ฅผ ์ ํ ์ฌ์ฉํ์ง ์๊ณ ์ฒ์๋ถํฐ ์ง์ ์์ฑํ ์๋ ์์ต๋๋ค. JSON ์คํค๋ง๋ `apply_chat_template`์ `tools` ์ธ์์ ์ง์ ์ ๋ฌํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ ๋ณต์กํ ํจ์์ ๋ํ ์ ๋ฐํ ์คํค๋ง๋ฅผ ์ ์ํ ์ ์๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ์คํค๋ง๊ฐ ๋ณต์กํ ์๋ก ๋ชจ๋ธ์ด ์ฒ๋ฆฌํ๋ ๋ฐ ํผ๋์ ๊ฒช์ ๊ฐ๋ฅ์ฑ์ด ๋์์ง๋๋ค! ๊ฐ๋ฅํ ํ ๊ฐ๋จํ ํจ์ ์๋ช ์ ์ ์งํ๊ณ , ์ธ์(ํนํ ๋ณต์กํ๊ณ ์ค์ฒฉ๋ ์ธ์)๋ฅผ ์ต์ํํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. | |
| ์ฌ๊ธฐ ์ง์ ์คํค๋ง๋ฅผ ์ ์ํ๊ณ ์ด๋ฅผ `apply_chat_template`์ ์ ๋ฌํ๋ ์์ ๊ฐ ์์ต๋๋ค: | |
| ```python | |
| # ์ธ์๋ฅผ ๋ฐ์ง ์๋ ๊ฐ๋จํ ํจ์ | |
| 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] | |
| ) | |
| ``` | |
| ## ๊ณ ๊ธ: ๊ฒ์ ์ฆ๊ฐ ์์ฑ[[advanced-retrieval-augmented-generation]] | |
| "๊ฒ์ ์ฆ๊ฐ ์์ฑ" ๋๋ "RAG" LLM์ ์ฟผ๋ฆฌ์ ์๋ตํ๊ธฐ ์ ์ ๋ฌธ์์ ์ฝํผ์ค๋ฅผ ๊ฒ์ํ์ฌ ์ ๋ณด๋ฅผ ์ป์ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ชจ๋ธ์ ์ ํ๋ ์ปจํ ์คํธ ํฌ๊ธฐ ์ด์์ผ๋ก ์ง์ ๊ธฐ๋ฐ์ ํฌ๊ฒ ํ์ฅํ ์ ์์ต๋๋ค. RAG ๋ชจ๋ธ์ ๋ํ ์ฐ๋ฆฌ์ ๊ถ์ฅ ์ฌํญ์ ํ ํ๋ฆฟ์ด `documents` ์ธ์๋ฅผ ํ์ฉํด์ผ ํ๋ค๋ ๊ฒ์ ๋๋ค. ์ด ์ธ์๋ ๊ฐ "๋ฌธ์"๊ฐ `title`๊ณผ `contents` ํค๋ฅผ ๊ฐ์ง๋ ๋จ์ผ dict์ธ ๋ฌธ์ ๋ชฉ๋ก์ด์ด์ผ ํฉ๋๋ค. ์ด ํ์์ ๋๊ตฌ์ ์ฌ์ฉ๋๋ JSON ์คํค๋ง๋ณด๋ค ํจ์ฌ ๊ฐ๋จํ๋ฏ๋ก ๋ณ๋์ ๋์ฐ๋ฏธ ํจ์๊ฐ ํ์ํ์ง ์์ต๋๋ค. | |
| ๋ค์์ RAG ํ ํ๋ฆฟ์ด ์๋ํ๋ ์์ ์ ๋๋ค: | |
| ```python | |
| document1 = { | |
| "title": "The Moon: Our Age-Old Foe", | |
| "contents": "Man has always dreamed of destroying the moon. In this essay, I shall..." | |
| } | |
| document2 = { | |
| "title": "The Sun: Our Age-Old Friend", | |
| "contents": "Although often underappreciated, the sun provides several notable benefits..." | |
| } | |
| model_input = tokenizer.apply_chat_template( | |
| messages, | |
| documents=[document1, document2] | |
| ) | |
| ``` | |
| ## ๊ณ ๊ธ: ์ฑํ ํ ํ๋ฆฟ์ ์ด๋ป๊ฒ ์๋ํ๋์?[[advanced-how-do-chat-templates-work]] | |
| ๋ชจ๋ธ์ ์ฑํ ํ ํ๋ฆฟ์ `tokenizer.chat_template` ์์ฑ์ ์ ์ฅ๋ฉ๋๋ค. ์ฑํ ํ ํ๋ฆฟ์ด ์ค์ ๋์ง ์์ ๊ฒฝ์ฐ ํด๋น ๋ชจ๋ธ ํด๋์ค์ ๊ธฐ๋ณธ ํ ํ๋ฆฟ์ด ๋์ ์ฌ์ฉ๋ฉ๋๋ค. `BlenderBot`์ ํ ํ๋ฆฟ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค: | |
| ```python | |
| >>> from transformers import AutoTokenizer | |
| >>> tokenizer = AutoTokenizer.from_pretrained("facebook/blenderbot-400M-distill") | |
| >>> tokenizer.chat_template | |
| "{% for message in messages %}{% if message['role'] == 'user' %}{{ ' ' }}{% endif %}{{ message['content'] }}{% if not loop.last %}{{ ' ' }}{% endif %}{% endfor %}{{ eos_token }}" | |
| ``` | |
| ์ฝ๊ฐ ๋ณต์กํด ๋ณด์ผ ์ ์์ต๋๋ค. ์ฝ๊ธฐ ์ฝ๊ฒ ์ ๋ฆฌํด ๋ณด๊ฒ ์ต๋๋ค. ์ด ๊ณผ์ ์์ ์ถ๊ฐํ๋ ์ค๋ฐ๊ฟ๊ณผ ๋ค์ฌ์ฐ๊ธฐ๊ฐ ํ ํ๋ฆฟ ์ถ๋ ฅ์ ํฌํจ๋์ง ์๋๋ก ํด์ผ ํฉ๋๋ค. ์๋๋ [๊ณต๋ฐฑ์ ์ ๊ฑฐํ๋](#trimming-whitespace) ํ์ ๋๋ค: | |
| ``` | |
| {%- for message in messages %} | |
| {%- if message['role'] == 'user' %} | |
| {{- ' ' }} | |
| {%- endif %} | |
| {{- message['content'] }} | |
| {%- if not loop.last %} | |
| {{- ' ' }} | |
| {%- endif %} | |
| {%- endfor %} | |
| {{- eos_token }} | |
| ``` | |
| ๋ง์ฝ ์ด์ ๊ฐ์ ํ์์ ์ฒ์ ๋ณธ๋ค๋ฉด, ์ด๊ฒ์ [Jinja ํ ํ๋ฆฟ](https://jinja.palletsprojects.com/en/3.1.x/templates/)์ ๋๋ค. | |
| Jinja๋ ํ ์คํธ๋ฅผ ์์ฑํ๋ ๊ฐ๋จํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ ํ ํ๋ฆฟ ์ธ์ด์ ๋๋ค. ๋ง์ ๋ฉด์์ ์ฝ๋์ ๊ตฌ๋ฌธ์ด ํ์ด์ฌ๊ณผ ์ ์ฌํฉ๋๋ค. ์์ ํ์ด์ฌ์์๋ ์ด ํ ํ๋ฆฟ์ด ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ผ ๊ฒ์ ๋๋ค: | |
| ```python | |
| for idx, message in enumerate(messages): | |
| if message['role'] == 'user': | |
| print(' ') | |
| print(message['content']) | |
| if not idx == len(messages) - 1: # Check for the last message in the conversation | |
| print(' ') | |
| print(eos_token) | |
| ``` | |
| ์ด ํ ํ๋ฆฟ์ ์ธ ๊ฐ์ง ์ผ์ ํฉ๋๋ค: | |
| 1. ๊ฐ ๋ฉ์์ง์ ๋ํด, ๋ฉ์์ง๊ฐ ์ฌ์ฉ์ ๋ฉ์์ง์ธ ๊ฒฝ์ฐ ๊ณต๋ฐฑ์ ์ถ๊ฐํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด ์๋ฌด๊ฒ๋ ์ถ๋ ฅํ์ง ์์ต๋๋ค. | |
| 2. ๋ฉ์์ง ๋ด์ฉ์ ์ถ๊ฐํฉ๋๋ค. | |
| 3. ๋ฉ์์ง๊ฐ ๋ง์ง๋ง ๋ฉ์์ง๊ฐ ์๋ ๊ฒฝ์ฐ ๋ ๊ฐ์ ๊ณต๋ฐฑ์ ์ถ๊ฐํฉ๋๋ค. ๋ง์ง๋ง ๋ฉ์์ง ํ์๋ EOS ํ ํฐ์ ์ถ๋ ฅํฉ๋๋ค. | |
| ์ด๊ฒ์ ๋งค์ฐ ๊ฐ๋จํ ํ ํ๋ฆฟ์ ๋๋ค. ์ ์ด ํ ํฐ์ ์ถ๊ฐํ์ง ์์ผ๋ฉฐ, ์ดํ ๋ํ์์ ๋ชจ๋ธ์ด ์ด๋ป๊ฒ ๋์ํด์ผ ํ๋์ง ์ง์ํ๋ "์์คํ " ๋ฉ์์ง๋ฅผ ์ง์ํ์ง ์์ต๋๋ค. ํ์ง๋ง Jinja๋ ์ด๋ฌํ ์์ ์ ์ํํ ์ ์๋ ๋ง์ ์ ์ฐ์ฑ์ ์ ๊ณตํฉ๋๋ค! LLaMA๊ฐ ์ ๋ ฅ์ ํ์ํํ๋ ๋ฐฉ์๊ณผ ์ ์ฌํ ํ์์ Jinja ํ ํ๋ฆฟ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค(์ค์ LLaMA ํ ํ๋ฆฟ์ ๊ธฐ๋ณธ ์์คํ ๋ฉ์์ง ์ฒ๋ฆฌ์ ์ผ๋ฐ์ ์ธ ์์คํ ๋ฉ์์ง ์ฒ๋ฆฌ๋ฅผ ํฌํจํ๊ณ ์์ต๋๋ค - ์ค์ ์ฝ๋์์๋ ์ด ํ ํ๋ฆฟ์ ์ฌ์ฉํ์ง ๋ง์ธ์!). | |
| ``` | |
| {%- for message in messages %} | |
| {%- if message['role'] == 'user' %} | |
| {{- bos_token + '[INST] ' + message['content'] + ' [/INST]' }} | |
| {%- elif message['role'] == 'system' %} | |
| {{- '<<SYS>>\\n' + message['content'] + '\\n<</SYS>>\\n\\n' }} | |
| {%- elif message['role'] == 'assistant' %} | |
| {{- ' ' + message['content'] + ' ' + eos_token }} | |
| {%- endif %} | |
| {%- endfor %} | |
| ``` | |
| ์ด ํ ํ๋ฆฟ์ ์ ์ ์ดํด๋ณด๋ฉด ๋ฌด์์ ํ๋์ง ์ดํดํ ์ ์์ต๋๋ค. ๋จผ์ , ๊ฐ ๋ฉ์์ง์ "role"์ ๋ฐ๋ผ ํน์ ํ ํฐ์ ์ถ๊ฐํ์ฌ ๋๊ฐ ๋ฉ์์ง๋ฅผ ๋ณด๋๋์ง ๋ชจ๋ธ์๊ฒ ๋ช ํํ๊ฒ ์๋ ค์ค๋๋ค. ๋ํ ์ฌ์ฉ์, ์ด์์คํดํธ ๋ฐ ์์คํ ๋ฉ์์ง๋ ๊ฐ๊ฐ ๊ณ ์ ํ ํ ํฐ์ผ๋ก ๋ํ๋์ด ๋ชจ๋ธ์ด ๋ช ํํ๊ฒ ๊ตฌ๋ถํ ์ ์์ต๋๋ค. | |
| ## ๊ณ ๊ธ: ์ฑํ ํ ํ๋ฆฟ ์ถ๊ฐ ๋ฐ ํธ์ง[[advanced-adding-and-editing-chat-templates]] | |
| ### ์ฑํ ํ ํ๋ฆฟ์ ์ด๋ป๊ฒ ๋ง๋ค ์ ์๋์?[[how-do-i-create-a-chat-template]] | |
| ๊ฐ๋จํฉ๋๋ค. Jinja ํ ํ๋ฆฟ์ ์์ฑํ๊ณ `tokenizer.chat_template`์ ์ค์ ํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ๋ค๋ฅธ ๋ชจ๋ธ์ ๊ธฐ์กด ํ ํ๋ฆฟ์ ์์์ ์ผ๋ก ์ฌ์ฉํ๊ณ ํ์์ ๋ง๊ฒ ํธ์งํ๋ ๊ฒ์ด ๋ ์ฌ์ธ ๊ฒ ์ ๋๋ค! ์๋ฅผ ๋ค์ด, ์์ LLaMA ํ ํ๋ฆฟ์ ๊ฐ์ ธ์ ์ด์์คํดํธ ๋ฉ์์ง์ "[ASST]" ๋ฐ "[/ASST]"๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค: | |
| ``` | |
| {%- for message in messages %} | |
| {%- if message['role'] == 'user' %} | |
| {{- bos_token + '[INST] ' + message['content'].strip() + ' [/INST]' }} | |
| {%- elif message['role'] == 'system' %} | |
| {{- '<<SYS>>\\n' + message['content'].strip() + '\\n<</SYS>>\\n\\n' }} | |
| {%- elif message['role'] == 'assistant' %} | |
| {{- '[ASST] ' + message['content'] + ' [/ASST]' + eos_token }} | |
| {%- endif %} | |
| {%- endfor %} | |
| ``` | |
| ์ด์ `tokenizer.chat_template` ์์ฑ์ ์ค์ ํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ค์์ [`~PreTrainedTokenizer.apply_chat_template`]๋ฅผ ์ฌ์ฉํ ๋ ์๋กญ๊ฒ ์ค์ ํ ํ ํ๋ฆฟ์ด ์ฌ์ฉ๋ฉ๋๋ค! ์ด ์์ฑ์ `tokenizer_config.json` ํ์ผ์ ์ ์ฅ๋๋ฏ๋ก, [`~utils.PushToHubMixin.push_to_hub`]๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ ํ๋ฆฟ์ ํ๋ธ์ ์ ๋ก๋ํ๊ณ ๋ชจ๋ ์ฌ์ฉ์๊ฐ ๋ชจ๋ธ์ ๋ง๋ ํ ํ๋ฆฟ์ ์ฌ์ฉํ ์ ์๋๋ก ํ ์ ์์ต๋๋ค! | |
| ```python | |
| template = tokenizer.chat_template | |
| template = template.replace("SYS", "SYSTEM") # ์์คํ ํ ํฐ ๋ณ๊ฒฝ | |
| tokenizer.chat_template = template # ์ ํ ํ๋ฆฟ ์ค์ | |
| tokenizer.push_to_hub("model_name") # ์ ํ ํ๋ฆฟ์ ํ๋ธ์ ์ ๋ก๋! | |
| ``` | |
| ์ฑํ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ [`~PreTrainedTokenizer.apply_chat_template`] ๋ฉ์๋๋ [`TextGenerationPipeline`] ํด๋์ค์์ ํธ์ถ๋๋ฏ๋ก, ์ฌ๋ฐ๋ฅธ ์ฑํ ํ ํ๋ฆฟ์ ์ค์ ํ๋ฉด ๋ชจ๋ธ์ด ์๋์ผ๋ก [`TextGenerationPipeline`]๊ณผ ํธํ๋ฉ๋๋ค. | |
| <Tip> | |
| ๋ชจ๋ธ์ ์ฑํ ์ฉ๋๋ก ๋ฏธ์ธ ์กฐ์ ํ๋ ๊ฒฝ์ฐ, ์ฑํ ํ ํ๋ฆฟ์ ์ค์ ํ๋ ๊ฒ ์ธ์๋ ์ ์ฑํ ์ ์ด ํ ํฐ์ ํ ํฌ๋์ด์ ์ ํน๋ณ ํ ํฐ์ผ๋ก ์ถ๊ฐํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ํน๋ณ ํ ํฐ์ ์ ๋๋ก ๋ถํ ๋์ง ์์ผ๋ฏ๋ก, ์ ์ด ํ ํฐ์ด ์ฌ๋ฌ ์กฐ๊ฐ์ผ๋ก ํ ํฐํ๋๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค. ๋ํ, ํ ํ๋ฆฟ์์ ์ด์์คํดํธ ์์ฑ์ ๋์ ๋ํ๋ด๋ ํ ํฐ์ผ๋ก ํ ํฌ๋์ด์ ์ `eos_token` ์์ฑ์ ์ค์ ํด์ผ ํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ํ ์คํธ ์์ฑ ๋๊ตฌ๊ฐ ํ ์คํธ ์์ฑ์ ์ธ์ ์ค์งํด์ผ ํ ์ง ์ ํํ ์ ์ ์์ต๋๋ค. | |
| </Tip> | |
| ### ์ ์ผ๋ถ ๋ชจ๋ธ์ ์ฌ๋ฌ ๊ฐ์ ํ ํ๋ฆฟ์ ๊ฐ์ง๊ณ ์๋์?[[why-do-some-models-have-multiple-templates]] | |
| ์ผ๋ถ ๋ชจ๋ธ์ ๋ค๋ฅธ ์ฌ์ฉ ์ฌ๋ก์ ๋ํด ๋ค๋ฅธ ํ ํ๋ฆฟ์ ์ฌ์ฉํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์ผ๋ฐ ์ฑํ ์ ์ํ ํ ํ๋ฆฟ๊ณผ ๋๊ตฌ ์ฌ์ฉ ๋๋ ๊ฒ์ ์ฆ๊ฐ ์์ฑ์ ๋ํ ํ ํ๋ฆฟ์ ๋ณ๋๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ `tokenizer.chat_template`๋ ๋์ ๋๋ฆฌ์ ๋๋ค. ์ด๊ฒ์ ์ฝ๊ฐ์ ํผ๋์ ์ด๋ํ ์ ์์ผ๋ฉฐ, ๊ฐ๋ฅํ ํ ๋ชจ๋ ์ฌ์ฉ ์ฌ๋ก์ ๋ํด ๋จ์ผ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. `if tools is defined`์ ๊ฐ์ Jinja ๋ฌธ์ฅ๊ณผ `{% macro %}` ์ ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ์ฝ๋ ๊ฒฝ๋ก๋ฅผ ๋จ์ผ ํ ํ๋ฆฟ์ ์ฝ๊ฒ ๋ํํ ์ ์์ต๋๋ค. | |
| ํ ํฌ๋์ด์ ์ ์ฌ๋ฌ ๊ฐ์ ํ ํ๋ฆฟ์ด ์๋ ๊ฒฝ์ฐ, `tokenizer.chat_template`๋ ํ ํ๋ฆฟ ์ด๋ฆ์ด ํค์ธ `๋์ ๋๋ฆฌ`์ ๋๋ค. `apply_chat_template` ๋ฉ์๋๋ ํน์ ํ ํ๋ฆฟ ์ด๋ฆ์ ๋ํ ํน๋ณํ ์ฒ๋ฆฌ๋ฅผ ํฉ๋๋ค: ์ผ๋ฐ์ ์ผ๋ก `default`๋ผ๋ ํ ํ๋ฆฟ์ ์ฐพ๊ณ , ์ฐพ์ ์ ์์ผ๋ฉด ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค. ๊ทธ๋ฌ๋ ์ฌ์ฉ์๊ฐ `tools` ์ธ์๋ฅผ ์ ๋ฌํ ๋ `tool_use`๋ผ๋ ํ ํ๋ฆฟ์ด ์กด์ฌํ๋ฉด ๋์ ๊ทธ๊ฒ์ ์ฌ์ฉํฉ๋๋ค. ๋ค๋ฅธ ์ด๋ฆ์ ํ ํ๋ฆฟ์ ์ ๊ทผํ๋ ค๋ฉด `apply_chat_template()`์ `chat_template` ์ธ์์ ์ํ๋ ํ ํ๋ฆฟ ์ด๋ฆ์ ์ ๋ฌํ๋ฉด ๋ฉ๋๋ค. | |
| ์ฌ์ฉ์์๊ฒ ์ฝ๊ฐ์ ํผ๋์ ์ค ์ ์์ผ๋ฏ๋ก, ํ ํ๋ฆฟ์ ์ง์ ์์ฑํ๋ ๊ฒฝ์ฐ ๊ฐ๋ฅํ ํ ๋จ์ผ ํ ํ๋ฆฟ์ ๋ชจ๋ ๊ฒ์ ๋ฃ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค! | |
| ### ์ด๋ค ํ ํ๋ฆฟ์ ์ฌ์ฉํด์ผ ํ๋์?[[what-template-should-i-use]] | |
| ์ด๋ฏธ ์ฑํ ์ฉ์ผ๋ก ํ๋ จ๋ ๋ชจ๋ธ์ ํ ํ๋ฆฟ์ ์ค์ ํ ๋๋ ํ ํ๋ฆฟ์ด ํ๋ จ ์ค ๋ชจ๋ธ์ด ๋ณธ ๋ฉ์์ง ํ์๊ณผ ์ ํํ ์ผ์นํ๋๋ก ํด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ฑ๋ฅ ์ ํ๋ฅผ ๊ฒฝํํ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค. ์ด๋ ๋ชจ๋ธ์ ์ถ๊ฐ๋ก ํ๋ จํ ๋๋ ๋ง์ฐฌ๊ฐ์ง์ ๋๋ค. ์ฑํ ํ ํฐ์ ์ผ์ ํ๊ฒ ์ ์งํ๋ ๊ฒ์ด ์ต์์ ์ฑ๋ฅ์ ์ป๋ ๋ฐฉ๋ฒ์ ๋๋ค. ์ด๋ ํ ํฐํ์ ๋งค์ฐ ์ ์ฌํฉ๋๋ค. ํ๋ จ ์ค์ ์ฌ์ฉ๋ ํ ํฐํ๋ฅผ ์ ํํ ์ผ์น์ํฌ ๋ ์ถ๋ก ์ด๋ ๋ฏธ์ธ ์กฐ์ ์์ ์ต๊ณ ์ ์ฑ๋ฅ์ ์ป์ ์ ์์ต๋๋ค. | |
| ๋ฐ๋ฉด์ ์ฒ์๋ถํฐ ๋ชจ๋ธ์ ํ๋ จ์ํค๊ฑฐ๋ ์ฑํ ์ฉ์ผ๋ก ๊ธฐ๋ณธ ์ธ์ด ๋ชจ๋ธ์ ๋ฏธ์ธ ์กฐ์ ํ๋ ๊ฒฝ์ฐ, ์ ์ ํ ํ ํ๋ฆฟ์ ์ ํํ ์ ์๋ ๋ง์ ์์ ๊ฐ ์์ต๋๋ค. LLM์ ๋ค์ํ ์ ๋ ฅ ํ์์ ์ฒ๋ฆฌํ ๋งํผ ์ถฉ๋ถํ ๋๋ํฉ๋๋ค. ์ธ๊ธฐ ์๋ ์ ํ ์ค ํ๋๋ `ChatML` ํ์์ด๋ฉฐ, ์ด๋ ๋ง์ ์ฌ์ฉ ์ฌ๋ก์ ์ ์ฐํ๊ฒ ์ฌ์ฉํ ์ ์๋ ์ข์ ์ ํ์ ๋๋ค. ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ``` | |
| {%- for message in messages %} | |
| {{- '<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n' }} | |
| {%- endfor %} | |
| ``` | |
| ์ด ํ ํ๋ฆฟ์ด ๋ง์์ ๋ ๋ค๋ฉด, ์ฝ๋์ ๋ฐ๋ก ๋ณต์ฌํ์ฌ ์ฌ์ฉํ ์ ์๋ ํ ์ค ๋ฒ์ ์ ์ ๊ณตํ๊ฒ ์ต๋๋ค. ์ด ํ ์ค ๋ฒ์ ์ [์์ฑ ํ๋กฌํํธ](#what-are-generation-prompts)์ ๋ํ ํธ๋ฆฌํ ์ง์๋ ํฌํจํ๊ณ ์์ง๋ง, BOS๋ EOS ํ ํฐ์ ์ถ๊ฐํ์ง ์๋๋ค๋ ์ ์ ์ ์ํ์ธ์! ๋ชจ๋ธ์ด ํด๋น ํ ํฐ์ ๊ธฐ๋ํ๋๋ผ๋, `apply_chat_template`์ ์ํด ์๋์ผ๋ก ์ถ๊ฐ๋์ง ์์ต๋๋ค. ์ฆ, ํ ์คํธ๋ `add_special_tokens=False`์ ์ํด ํ ํฐํ๋ฉ๋๋ค. ์ด๋ ํ ํ๋ฆฟ๊ณผ `add_special_tokens` ๋ ผ๋ฆฌ ๊ฐ์ ์ ์ฌ์ ์ธ ์ถฉ๋์ ํผํ๊ธฐ ์ํจ์ ๋๋ค. ๋ชจ๋ธ์ด ํน๋ณ ํ ํฐ์ ๊ธฐ๋ํ๋ ๊ฒฝ์ฐ, ํ ํ๋ฆฟ์ ์ง์ ์ถ๊ฐํด์ผ ํฉ๋๋ค! | |
| ```python | |
| tokenizer.chat_template = "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}" | |
| ``` | |
| ์ด ํ ํ๋ฆฟ์ ๊ฐ ๋ฉ์์ง๋ฅผ `<|im_start|>` ์ `<|im_end|>`ํ ํฐ์ผ๋ก ๊ฐ์ธ๊ณ , ์ญํ ์ ๋ฌธ์์ด๋ก ์์ฑํ์ฌ ํ๋ จ ์ ์ฌ์ฉํ๋ ์ญํ ์ ๋ํ ์ ์ฐ์ฑ์ ์ ๊ณตํฉ๋๋ค. ์ถ๋ ฅ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ```text | |
| <|im_start|>system | |
| You are a helpful chatbot that will do its best not to say anything so stupid that people tweet about it.<|im_end|> | |
| <|im_start|>user | |
| How are you?<|im_end|> | |
| <|im_start|>assistant | |
| I'm doing great!<|im_end|> | |
| ``` | |
| "์ฌ์ฉ์", "์์คํ " ๋ฐ "์ด์์คํดํธ" ์ญํ ์ ์ฑํ ์ ํ์ค์ด๋ฉฐ, ๊ฐ๋ฅํ ๋ ์ด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. ํนํ ๋ชจ๋ธ์ด [`TextGenerationPipeline`]๊ณผ ์ ์๋ํ๋๋ก ํ๋ ค๋ฉด ๊ทธ๋ ์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ฌํ ์ญํ ์๋ง ๊ตญํ๋์ง ์์ต๋๋ค. ํ ํ๋ฆฟ์ ๋งค์ฐ ์ ์ฐํ๋ฉฐ, ์ด๋ค ๋ฌธ์์ด์ด๋ ์ญํ ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. | |
| ### ์ฑํ ํ ํ๋ฆฟ์ ์ถ๊ฐํ๊ณ ์ถ์ต๋๋ค! ์ด๋ป๊ฒ ์์ํด์ผ ํ๋์?[[i-want-to-add-some-chat-templates-how-should-i-get-started]] | |
| ์ฑํ ๋ชจ๋ธ์ด ์๋ ๊ฒฝ์ฐ, ํด๋น ๋ชจ๋ธ์ `tokenizer.chat_template` ์์ฑ์ ์ค์ ํ๊ณ [`~PreTrainedTokenizer.apply_chat_template`]๋ฅผ ์ฌ์ฉํ์ฌ ํ ์คํธํ ๋ค์ ์ ๋ฐ์ดํธ๋ ํ ํฌ๋์ด์ ๋ฅผ ํ๋ธ์ ํธ์ํด์ผ ํฉ๋๋ค. ์ด๋ ๋ชจ๋ธ ์์ ์๊ฐ ์๋ ๊ฒฝ์ฐ์๋ ์ ์ฉ๋ฉ๋๋ค. ๋น ์ฑํ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ ๋ชจ๋ธ์ด๋ ์ฌ์ ํ ๊ธฐ๋ณธ ํด๋์ค ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, [ํ ๋ฆฌํ์คํธ](https://huggingface.co/docs/hub/repositories-pull-requests-discussions)๋ฅผ ๋ชจ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ ์ด์ด ์ด ์์ฑ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ค์ ํ ์ ์๋๋ก ํ์ธ์! | |
| ์์ฑ์ ์ค์ ํ๋ฉด ๋์ ๋๋ค! `tokenizer.apply_chat_template`๊ฐ ์ด์ ํด๋น ๋ชจ๋ธ์ ๋ํด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋ฏ๋ก, `TextGenerationPipeline`๊ณผ ๊ฐ์ ๊ณณ์์๋ ์๋์ผ๋ก ์ง์๋ฉ๋๋ค! | |
| ๋ชจ๋ธ์ ์ด ์์ฑ์ ์ค์ ํจ์ผ๋ก์จ, ์คํ ์์ค ๋ชจ๋ธ์ ์ ์ฒด ๊ธฐ๋ฅ์ ์ปค๋ฎค๋ํฐ๊ฐ ์ฌ์ฉํ ์ ์๋๋ก ํ ์ ์์ต๋๋ค. ํ์ ๋ถ์ผ์น๋ ์ด ๋ถ์ผ์์ ์ค๋ซ๋์ ์ฑ๋ฅ์ ์ ํ์ํค๋ ๋ฌธ์ ์์ผ๋ฏ๋ก, ์ด์ ์ด๋ฅผ ๋๋ผ ๋์ ๋๋ค! | |
| ## ๊ณ ๊ธ: ํ ํ๋ฆฟ ์์ฑ ํ[[advanced-template-writing-tips]] | |
| Jinja์ ์ต์ํ์ง ์์ ๊ฒฝ์ฐ, ์ฑํ ํ ํ๋ฆฟ์ ์์ฑํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ๋จผ์ ๋ฉ์์ง๋ฅผ ์ํ๋ ๋ฐฉ์์ผ๋ก ํ์ํํ๋ ์งง์ ํ์ด์ฌ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ ๋ค์, ํด๋น ์คํฌ๋ฆฝํธ๋ฅผ ํ ํ๋ฆฟ์ผ๋ก ๋ณํํ๋ ๊ฒ์ ๋๋ค. | |
| ํ ํ๋ฆฟ ํธ๋ค๋ฌ๋ `messages`๋ผ๋ ๋ณ์๋ก ๋ํ ๊ธฐ๋ก์ ๋ฐ์ต๋๋ค. ํ์ด์ฌ์์์ ๋ง์ฐฌ๊ฐ์ง๋ก ํ ํ๋ฆฟ ๋ด์ `messages`์ ์ ๊ทผํ ์ ์์ผ๋ฉฐ, `{% for message in messages %}`๋ก ๋ฐ๋ณตํ๊ฑฐ๋ `{{ messages[0] }}`์ ๊ฐ์ด ๊ฐ๋ณ ๋ฉ์์ง์ ์ ๊ทผํ ์ ์์ต๋๋ค. | |
| ๋ค์ ํ์ ์ฌ์ฉํ์ฌ ์ฝ๋๋ฅผ Jinja๋ก ๋ณํํ ์๋ ์์ต๋๋ค: | |
| ### ๊ณต๋ฐฑ ์ ๊ฑฐ[[trimming-whitespace]] | |
| ๊ธฐ๋ณธ์ ์ผ๋ก Jinja๋ ๋ธ๋ก ์ ํ์ ๊ณต๋ฐฑ์ ์ถ๋ ฅํฉ๋๋ค. ์ด๋ ์ผ๋ฐ์ ์ผ๋ก ๊ณต๋ฐฑ์ ๋งค์ฐ ์ ํํ๊ฒ ๋ค๋ฃจ๊ณ ์ ํ๋ ์ฑํ ํ ํ๋ฆฟ์์๋ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค! ์ด๋ฅผ ํผํ๊ธฐ ์ํด ํ ํ๋ฆฟ์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ๋ ๊ฒ์ด ์ข์ต๋๋ค: | |
| ``` | |
| {%- for message in messages %} | |
| {{- message['role'] + message['content'] }} | |
| {%- endfor %} | |
| ``` | |
| ์๋์ ๊ฐ์ด ์์ฑํ์ง ๋ง์ธ์: | |
| ``` | |
| {% for message in messages %} | |
| {{ message['role'] + message['content'] }} | |
| {% endfor %} | |
| ``` | |
| `-`๋ฅผ ์ถ๊ฐํ๋ฉด ๋ธ๋ก ์ ํ์ ๊ณต๋ฐฑ์ด ์ ๊ฑฐ๋ฉ๋๋ค. ๋ ๋ฒ์งธ ์์ ๋ ๋ฌดํดํด ๋ณด์ด์ง๋ง, ์ค๋ฐ๊ฟ๊ณผ ๋ค์ฌ์ฐ๊ธฐ๊ฐ ์ถ๋ ฅ์ ํฌํจ๋ ์ ์์ผ๋ฉฐ, ์ด๋ ์ํ์ง ์๋ ๊ฒฐ๊ณผ์ผ ์ ์์ต๋๋ค! | |
| ### ๋ฐ๋ณต๋ฌธ[[for-loops]] | |
| Jinja์์ ๋ฐ๋ณต๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ``` | |
| {%- for message in messages %} | |
| {{- message['content'] }} | |
| {%- endfor %} | |
| ``` | |
| {{ ํํ์ ๋ธ๋ก }} ๋ด๋ถ์ ์๋ ๋ชจ๋ ๊ฒ์ด ์ถ๋ ฅ์ผ๋ก ์ธ์๋ฉ๋๋ค. `+`์ ๊ฐ์ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ฌ ํํ์ ๋ธ๋ก ๋ด๋ถ์์ ๋ฌธ์์ด์ ๊ฒฐํฉํ ์ ์์ต๋๋ค. | |
| ### ์กฐ๊ฑด๋ฌธ[[if-statements]] | |
| Jinja์์ ์กฐ๊ฑด๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ``` | |
| {%- if message['role'] == 'user' %} | |
| {{- message['content'] }} | |
| {%- endif %} | |
| ``` | |
| ํ์ด์ฌ์ด ๊ณต๋ฐฑ์ ์ฌ์ฉํ์ฌ `for` ๋ฐ `if` ๋ธ๋ก์ ์์๊ณผ ๋์ ํ์ํ๋ ๋ฐ๋ฉด, Jinja๋ `{% endfor %}` ๋ฐ `{% endif %}`๋ก ๋ช ์์ ์ผ๋ก ๋์ ํ์ํด์ผ ํฉ๋๋ค. | |
| ### ํน์ ๋ณ์[[special-variables]] | |
| ํ ํ๋ฆฟ ๋ด๋ถ์์๋ `messages` ๋ชฉ๋ก์ ์ ๊ทผํ ์ ์์ ๋ฟ๋ง ์๋๋ผ ์ฌ๋ฌ ๋ค๋ฅธ ํน์ ๋ณ์์๋ ์ ๊ทผํ ์ ์์ต๋๋ค. ์ฌ๊ธฐ์๋ `bos_token` ๋ฐ `eos_token`๊ณผ ๊ฐ์ ํน๋ณ ํ ํฐ๊ณผ ์์ ๋ ผ์ํ `add_generation_prompt` ๋ณ์๊ฐ ํฌํจ๋ฉ๋๋ค. ๋ํ `loop` ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฌ ๋ฐ๋ณต์ ๋ํ ์ ๋ณด๋ฅผ ์ป์ ์ ์์ผ๋ฉฐ, ์๋ฅผ ๋ค์ด `{% if loop.last %}`๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฌ ๋ฉ์์ง๊ฐ ๋ํ์ ๋ง์ง๋ง ๋ฉ์์ง์ธ์ง ํ์ธํ ์ ์์ต๋๋ค. `add_generation_prompt`๊ฐ `True`์ธ ๊ฒฝ์ฐ ๋ํ ๋์ ์์ฑ ํ๋กฌํํธ๋ฅผ ์ถ๊ฐํ๋ ์์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค: | |
| ``` | |
| {%- if loop.last and add_generation_prompt %} | |
| {{- bos_token + 'Assistant:\n' }} | |
| {%- endif %} | |
| ``` | |
| ### ๋นํ์ด์ฌ Jinja์์ ํธํ์ฑ[[compatibility-with-non-python-jinja]] | |
| Jinja์ ์ฌ๋ฌ ๊ตฌํ์ ๋ค์ํ ์ธ์ด๋ก ์ ๊ณต๋ฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ๋์ผํ ๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ง๋ง, ์ฃผ์ ์ฐจ์ด์ ์ ํ์ด์ฌ์์ ํ ํ๋ฆฟ์ ์์ฑํ ๋ ํ์ด์ฌ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค๋ ์ ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ๋ฌธ์์ด์ `.lower()`๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ๋์ ๋๋ฆฌ์ `.items()`๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. ์ด๋ ๋นํ์ด์ฌ Jinja ๊ตฌํ์์ ํ ํ๋ฆฟ์ ์ฌ์ฉํ๋ ค๊ณ ํ ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ํนํ JS์ Rust๊ฐ ์ธ๊ธฐ ์๋ ๋ฐฐํฌ ํ๊ฒฝ์์๋ ๋นํ์ด์ฌ ๊ตฌํ์ด ํํฉ๋๋ค. | |
| ํ์ง๋ง ๊ฑฑ์ ํ์ง ๋ง์ธ์! ๋ชจ๋ Jinja ๊ตฌํ์์ ํธํ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ํ ํ๋ฆฟ์ ์ฝ๊ฒ ๋ณ๊ฒฝํ ์ ์๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ต๋๋ค: | |
| - ํ์ด์ฌ ๋ฉ์๋๋ฅผ Jinja ํํฐ๋ก ๋์ฒดํ์ธ์. ์ผ๋ฐ์ ์ผ๋ก ๊ฐ์ ์ด๋ฆ์ ๊ฐ์ง๋ฉฐ, ์๋ฅผ ๋ค์ด `string.lower()`๋ `string|lower`๋ก, `dict.items()`๋ `dict|items`๋ก ๋์ฒดํ ์ ์์ต๋๋ค. ์ฃผ๋ชฉํ ๋งํ ๋ณ๊ฒฝ ์ฌํญ์ `string.strip()`์ด `string|trim`์ผ๋ก ๋ฐ๋๋ ๊ฒ์ ๋๋ค. ๋ ์์ธํ ๋ด์ฉ์ Jinja ๋ฌธ์์ [๋ด์ฅ ํํฐ ๋ชฉ๋ก](https://jinja.palletsprojects.com/en/3.1.x/templates/#builtin-filters)์ ์ฐธ์กฐํ์ธ์. | |
| - ํ์ด์ฌ์ ํนํ๋ `True`, `False`, `None`์ ๊ฐ๊ฐ `true`, `false`, `none`์ผ๋ก ๋์ฒดํ์ธ์. | |
| - ๋์ ๋๋ฆฌ๋ ๋ฆฌ์คํธ๋ฅผ ์ง์ ๋ ๋๋งํ ๋ ๋ค๋ฅธ ๊ตฌํ์์๋ ๊ฒฐ๊ณผ๊ฐ ๋ค๋ฅผ ์ ์์ต๋๋ค(์: ๋ฌธ์์ด ํญ๋ชฉ์ด ๋จ์ผ ๋ฐ์ดํ์์ ์ด์ค ๋ฐ์ดํ๋ก ๋ณ๊ฒฝ๋ ์ ์์ต๋๋ค). `tojson` ํํฐ๋ฅผ ์ถ๊ฐํ๋ฉด ์ผ๊ด์ฑ์ ์ ์งํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค. |