๐ป ์ฝ๋ฉ ์
๋๋๊ธฐ ๊ฐ์ด๋
[1๋ฒ ์
] ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น (๊ฐ์ฅ ๋จผ์ ์คํ, 1~2๋ถ ์์)
```bash
!pip install -q transformers datasets bitsandbytes peft trl accelerate
```
์ค์น๊ฐ ๋๋๋ฉด ๋ค์ ์
๋ก ๋์ด๊ฐ๋๋ค.
[2๋ฒ ์
] ๋ชจ๋ธ ๋ก๋ (ํ๊น
ํ์ด์ค์์ ๋ค์ด๋ก๋, ์ฝ 2~3๋ถ ์์)
```python
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
model_id = "Qwen/Qwen2.5-1.5B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=bnb_config,
device_map="auto"
)
print("✅ ๋ชจ๋ธ๊ณผ ํ ํฌ๋์ด์ ๋ก๋ ์๋ฃ!")
```
[3๋ฒ ์
] ๋ฐ์ดํฐ์
์ค๋น (1์ด ๋ง์ ์๋ฃ)์ฑ๋ด์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊พธ๊ณ ์ถ์ ๋ ์ด ์
๋ง ์์ ํด์ ๋ค์ ์คํํ๋ฉด ๋๋ฏ๋ก ๋ฌด์กฐ๊ฑด ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
```python
from datasets import Dataset
dataset_data = [
{
"messages": [
{"role": "system", "content": "๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค."},
{"role": "user", "content": "์ค๋ ๋ ์จ ์ด๋?"},
{"role": "assistant", "content": "์ค๋ ๋ ์จ๋ ์์ฃผ ํ์ฐฝํด์ ์ฐ์ฑ
ํ๊ธฐ ๋ฑ ์ข์ ๋ ์จ๋ค ๋ฉ๋ฉ!"}
]
},
{
"messages": [
{"role": "system", "content": "๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค."},
{"role": "user", "content": "๋๋ ๋๊ตฌ์ผ?"},
{"role": "assistant", "content": "๋๋ ์ฃผ์ธ์ ์ธ์์์ ์ ์ผ ์ฌ๋ํ๋ ์ธ๊ณต์ง๋ฅ ๊ฐ์์ง ์ฑ๋ด์ด๋ค ๋ฉ๋ฉ!"}
]
},
{
"messages": [
{"role": "system", "content": "๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค."},
{"role": "user", "content": "๋ฐฐ๊ณ ํ๋ฐ ๋ญ ๋จน์๊น?"},
{"role": "assistant", "content": "๋ง์๋ ๊ณ ๊ธฐ ๊ฐ์์ ๋จน๋ ๊ฑธ ์ถ์ฒํ๋ค ๋ฉ๋ฉ! ๋๋ ํ ์
๋ง ๋ฌ๋ผ ๋ฉ๋ฉ!"}
]
}
]
dataset = Dataset.from_list(dataset_data)
def apply_chat_template(examples):
return {"text": [tokenizer.apply_chat_template(msg, tokenize=False) for msg in examples["messages"]]}
templated_dataset = dataset.map(apply_chat_template, batched=True)
print("๐ ๋ฐ์ดํฐ ๊ฐ๊ณต ์๋ฃ!")
```
[4๋ฒ ์
] ํ์ธํ๋ ํ์ต ์์ (์ฝ 1~2๋ถ ์์)์ง์ง AI ๋๋ฅผ ํ์ต์ํค๋ ๋จ๊ณ์
๋๋ค. ํ๋ฉด์ Loss ์ซ์๊ฐ ๋จ์ด์ง๋ ๊ฑธ ๊ตฌ๊ฒฝํ์๋ฉด ๋ฉ๋๋ค.
```python
from peft import LoraConfig
from transformers import TrainingArguments
from trl import SFTTrainer
peft_config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
training_args = TrainingArguments(
output_dir="./qwen_dog_bot",
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
warmup_steps=1,
max_steps=15,
learning_rate=2e-4,
fp16=True,
logging_steps=3,
report_to="none"
)
trainer = SFTTrainer(
model=model,
train_dataset=templated_dataset,
peft_config=peft_config,
dataset_text_field="text",
max_seq_length=128,
tokenizer=tokenizer,
args=training_args
)
print("๐ ํ์ธํ๋ ํ์ต์ ์์ํฉ๋๋ค...")
trainer.train()
print("๐ ํ์ต ์๋ฃ!")
```
๐พ 1๋จ๊ณ: ํ์ต๋ ํ๋ ํ์ผ ์ ์ฅํ๊ธฐ (4๋ฒ ์
๋ฐ๋ก ๋ค์์ ์ถ๊ฐ)ํ์ต์ด ๋๋ ์งํ, trainer.save_model ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํด ์ฝ๋ฉ์ ๊ฐ์ ๋์คํฌ ๊ณต๊ฐ์ ํ์ผ๋ก ์ ์ฅํฉ๋๋ค.
```python
# 4๋ฒ ์
(ํ์ต)์ด ๋๋ ๋ฐ๋ก ์๋ซ์ค์ ์ด ์ฝ๋๋ฅผ ๋ฃ์ด ์คํํ์ธ์.
OUTPUT_DIR = "./saved_qwen_lora"
# ํ๋๋ ๊ฐ์ค์น(์ด๋ํฐ)์ ํ ํฌ๋์ด์ ์ค์ ์ ์ง์ ํ ํด๋์ ์ ์ฅ
trainer.save_model(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)
print(f"✅ ํ๋ ํ์ผ์ด '{OUTPUT_DIR}' ํด๋์ ์ฑ๊ณต์ ์ผ๋ก ์ ์ฅ๋์์ต๋๋ค!")
```
(์ผ์ชฝ ํด๋ ์์ด์ฝ์ ๋๋ฅด๋ฉด saved_qwen_lora ํด๋๊ฐ ์์ฑ๋๊ณ ๊ทธ ์์ adapter_config.json, adapter_model.safetensors ๋ฑ์ ํ์ผ์ด ์๊ธด ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.)
[5๋ฒ ์
] ์ฑ๋ด ํ
์คํธํ๊ธฐ (๋ํ์ฐฝ ์ญํ )ํ์ต์ด ์ ๋์๋์ง ์ง๋ฌธ์ ๋์ ธ๋ณด๋ ์
์
๋๋ค. content ๋ด์ฉ์ ๋ฐ๊พธ์ด๊ฐ๋ฉฐ ์ด ์
๋ง ๋ฌดํ ๋ฐ๋ณต ์คํํ์๋ฉด ๋ฉ๋๋ค.
```python
test_messages = [
{"role": "system", "content": "๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค."},
{"role": "user", "content": "์ปดํจํฐ ๊ณต๋ถ๋ ์ด๋ป๊ฒ ํด์ผ ํด?"} # ๐ ์ง๋ฌธ์ ๋ฐ๊พธ์ด ๋ณด์ธ์!
]
prompt = tokenizer.apply_chat_template(test_messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=64, temperature=0.7)
response = tokenizer.decode(outputs[inputs.input_ids.shape:], skip_special_tokens=True)
print("๐ค ํ์ธํ๋๋ ์ฑ๋ด์ ๋ต๋ณ:\n", response)
```
์ด๋ ๊ฒ 5๊ฐ์ ์
๋ก ์ชผ๊ฐ์ ๊ตฌ์ฑํ์๋ฉด ๋ฌด๊ฑฐ์ด ๋ชจ๋ธ ๋ค์ด๋ก๋(2๋ฒ)๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น(1๋ฒ)๋ ์ต์ด์ ๋ฑ ํ ๋ฒ๋ง ํด๋๊ณ , ๋์ค์ ๋ฐ์ดํฐ์
์ ์์ ํ ๋๋ 3, 4, 5๋ฒ ์
๋ง ๋ง์ฐ์ค๋ก ๋ธ๊น๊ฑฐ๋ฆฌ๋ฉฐ ๋น ๋ฅด๊ฒ ํ
์คํธํ ์ ์์ต๋๋ค.
```python
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from peft import PeftModel
# 1. ๊ธฐ์ค์ด ๋๋ ์๋ณธ ๋ชจ๋ธ ID์ ๋ด๊ฐ ์ ์ฅํ๋ ํ๋ ํ์ผ ๊ฒฝ๋ก ์ค์
base_model_id = "Qwen/Qwen2.5-1.5B-Instruct"
lora_file_path = "./saved_qwen_lora" # 1๋จ๊ณ์์ ์ ์ฅํ ํด๋ ๊ฒฝ๋ก
# 2. ํ ํฌ๋์ด์ ๋ ๋ด๊ฐ ์ ์ฅํ๋ ์ค์ ์์ ๊ฐ์ ธ์ค๊ธฐ
tokenizer = AutoTokenizer.from_pretrained(lora_file_path)
# 3. ์๋ณธ ๋ชจ๋ธ์ ๋๊ฐ์ด ๊ฐ๋ฒผ์ด 4๋นํธ๋ก ๋จผ์ ๋ก๋
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
base_model = AutoModelForCausalLM.from_pretrained(
base_model_id,
quantization_config=bnb_config,
device_map="auto"
)
```
# 4. ๐ฅ ํต์ฌ: ์๋ณธ ๋ชจ๋ธ ์์ ๋ด๊ฐ ๋ง๋ ํ๋ ํ์ผ(LoRA)์ ๋ฎ์ด์์ฐ๊ธฐ
```python
tuned_model = PeftModel.from_pretrained(base_model, lora_file_path)
print("✅ ์๋ณธ ๋ชจ๋ธ์ ํ๋ ํ์ผ ํฉ์น๊ธฐ ์๋ฃ! ์ด์ ์ฌ์ฉํ ์ค๋น๊ฐ ๋์์ต๋๋ค.")
# 5. ํฉ์ณ์ง ๋ชจ๋ธ๋ก ์ง๋ฌธ ๋์ ธ๋ณด๊ธฐ
test_messages = [
{"role": "system", "content": "๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค."},
{"role": "user", "content": "๋ฐ๊ฐ์! ๋ ์ฅ๊ธฐ์๋ ํ ์ค ์๋ ๊ฑฐ ์์ด?"}
]
prompt = tokenizer.apply_chat_template(test_messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
# ํ๋๋ ๋ชจ๋ธ(tuned_model)๋ก ๋ต๋ณ ์์ฑ
outputs = tuned_model.generate(**inputs, max_new_tokens=64, temperature=0.7)
response = tokenizer.decode(outputs[inputs.input_ids.shape:], skip_special_tokens=True)
print("\n๐ค ์ต์ข
๊ฒฐ๊ณผ ๋ณด๊ธฐ:")
print(response)
```
์ฝ๋ฉ ์ผ์ชฝ ๋ฉ๋ด์์ ํด๋ ์์ด์ฝ ➡️ ๋๋ผ์ด๋ธ ๋ง์ดํธ(ํด๋์ ์ฌ์ํ์ ์๋ ์์ด์ฝ)๋ฅผ ๋๋ฆ
๋๋ค.
๊ฒฝ๋ก๋ฅผ OUTPUT_DIR = "/content/drive/MyDrive/my_qwen_lora" ๋ก ์์ ํ๊ณ 1๋จ๊ณ๋ฅผ ์คํํ๋ฉด, ๋ด ๊ตฌ๊ธ ๋๋ผ์ด๋ธ์ ์์ ํ๊ฒ ํ์ผ์ด ์ ์ฅ๋ฉ๋๋ค.
๋์ค์ ์ธ ๋๋ 2๋จ๊ณ ์ฝ๋์ lora_file_path์ ๊ตฌ๊ธ ๋๋ผ์ด๋ธ ๊ฒฝ๋ก๋ฅผ ์ ์ด์ฃผ๋ฉด ์ธ์ ๋ ๊บผ๋ด ์ธ ์ ์์ต๋๋ค.
๋๋์ ๋ฌธ์๋ฅผ ์ฒ๋ฆฌํ๋ 3๋จ๊ณ(Vector DB ์์ฑ)์ ์ด๋ฅผ 2๋จ๊ณ์์ ํ์ตํ LoRA ๊ฐ์ค์น์ ๊ฒฐํฉํ๋ 4๋จ๊ณ(ํตํฉ ์ค์๊ฐ ์คํ)์ ์์ ํ ํ์ด์ฌ ์ฝ๋๋ฅผ ์์ฑํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
์ง๋ฌธ์๋์ด ์ค๊ณํ์ ๋๋ก ์ฝ๋ฉ ์
์์ญ ์ค์ผ์ ๋ง๊ธฐ ์ํด ๊ฐ ๋จ๊ณ๋ฅผ ์์ ํ ๋
๋ฆฝ๋ .py ํ์ผ๋ก ์ ์ฅํ๊ณ ! ์๋ธํ๋ก์ธ์ค๋ก ๊ตฌ๋ํ๋ ๊ตฌ์กฐ์
๋๋ค.RAG์ ํ์ํ ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น๋ถํฐ ์์ํฉ๋๋ค. ์ฝ๋ฉ ์
์์ ์๋ ๋ช
๋ น์ด๋ฅผ ์คํํ์ธ์.
```bash
!pip install -q langchain langchain-community langchain-huggingface faiss-gpu sentence-transformers
```
๐ 3๋จ๊ณ: ๋๋์ ๋ฌธ์ ์ฝ๊ณ Vector DB ๊ฐ์ค์น ํ์ผ๋ก ์ ์ฅํ๊ธฐ๊ตฌ๊ธ ๋๋ผ์ด๋ธ๋ ๋์คํฌ์ ์๋ ํ
์คํธ ๋ฌธ์(game_lore.txt)๋ฅผ ์ฝ์ด์์ ์ซ์๋ก ์์ถ๋ index.faiss ๊ฐ์ค์น ํ์ผ ํด๋๋ฅผ ๋ง๋๋ ๋จ๊ณ์
๋๋ค.[์ฝ๋ฉ ์
์ ์
๋ ฅํ์ฌ ํ์ผ ์์ฑ]
```python
%%writefile make_vector_db.py
import os
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
def build_rag_database():
# 1. ๋ฌธ์ ๊ฒฝ๋ก ์ง์ (๊ตฌ๊ธ ๋๋ผ์ด๋ธ ๋ง์ดํธ ๊ฒฝ๋ก ๋๋ ๋ก์ปฌ ๊ฒฝ๋ก)
# ์์๋ฅผ ์ํด ์์ ํ
์คํธ ํ์ผ์ ์์ฑํ๊ฑฐ๋ ๊ธฐ์กด ๊ฒ์ ๊ฐ์ด๋๋ถ ๊ฒฝ๋ก๋ฅผ ์ ์ด์ค๋๋ค.
doc_path = "game_lore.txt"
if not os.path.exists(doc_path):
with open(doc_path, "w", encoding="utf-8") as f:
f.write("์ผ๊ตญ์ง ์ญ์ฌ์ ๋ฐ๋ฅด๋ฉด ์ ๋น๋ ์ ๊ฐ๋์ ์ป๊ธฐ ์ํด ๊ทธ์ ์ด๋ ค๋ฅผ ์ธ ๋ฒ์ด๋ ์ง์ ์ฐพ์๊ฐ์ต๋๋ค. ์ด๋ฅผ ์ผ๊ณ ์ด๋ ค๋ผ ํฉ๋๋ค.\n")
f.write("์ ๊ฐ๋์ ๋ฌด๊ธฐ๋ ๋ฐฑ์ฐ์ ์ด๋ผ๋ ํ์ ๊นํธ ๋ถ์ฑ์ด๋ฉฐ, ๊ทธ๋ ์ฒ๋ฌธ๊ณผ ์ง๋ฆฌ์ ๋ฅํตํ ๋น๋ ์ต๊ณ ์ ์ง๋ต๊ฐ์์ต๋๋ค.\n")
print(f"ํ
์คํธ์ฉ {doc_path} ํ์ผ์ด ์กด์ฌํ์ง ์์ ์์ ์์ฑํ์ต๋๋ค.")
print("1. ๋๋์ ๋ฌธ์ ๋ฐ์ดํฐ ๋ก๋ ์ค...")
loader = TextLoader(doc_path, encoding="utf-8")
documents = loader.load()
print("2. ๊ธด ๋ฌธ์๋ฅผ AI๊ฐ ๊ฒ์ํ๊ธฐ ์ข๊ฒ ์๋ง์ ํฌ๊ธฐ๋ก ์ชผ๊ฐ๋ ์ค (Chunking)...")
text_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
docs = text_splitter.split_documents(documents)
print("3. ํ๊ตญ์ด ๋ฌธ๋งฅ์ ์ ์ดํดํ๋ ๊ฐ๋ฒผ์ด ๋ฌด๋ฃ ์๋ฒ ๋ฉ ๋ชจ๋ธ ๋ก๋ ์ค...")
# ํ๊ตญ์ด ์ฑ๋ฅ์ด ์ข์ ์คํ์์ค ์๋ฒ eding ๋ชจ๋ธ์ ํ์ฉํฉ๋๋ค.
embeddings = HuggingFaceEmbeddings(model_name="jhgan/ko-sroberta-multitask")
print("4. ๋ฌธ์ฅ์ ์ํ์ ์ซ์๋ก ๋ณํํ์ฌ ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์ถ ์ค...")
db = FAISS.from_documents(docs, embeddings)
# 5. 3๋จ๊ณ ์ต์ข
๊ฒฐ๊ณผ๋ฌผ: ๊ฐ์ค์น ํ์ผ ํํ๋ก ํด๋์ ์ ์ฅ
OUTPUT_DB_DIR = "./saved_vector_db"
db.save_local(OUTPUT_DB_DIR)
print(f"๐ 3๋จ๊ณ ์ฑ๊ณต! ๊ฒ์์ฉ ๊ฐ์ค์น ํ์ผ์ด '{OUTPUT_DB_DIR}' ํด๋์ ์ ์ ์ ์ฅ๋์์ต๋๋ค.")
if __name__ == "__main__":
build_rag_database()
```
[์ฝ๋ฉ ์
์์ 3๋จ๊ณ ์๋ธํ๋ก์ธ์ค ์คํ]
```bash
!python make_vector_db.py
```
๐ฌ 4๋จ๊ณ: LoRA ๋(2๋จ๊ณ) + Vector DB ์ง์(3๋จ๊ณ) ํฉ์ฒด ๋ฐ ์ค์๊ฐ ๊ฐ๋3๋จ๊ณ์์ ์์ฑ๋ ์ง์ ํ์ผ(saved_vector_db)๊ณผ 2๋จ๊ณ์์ ํ์ต ์๋ฃ๋ ๊ฐ์์ง/์ ๊ฐ๋ ์ปจ์
๋(saved_qwen_lora)๋ฅผ ๋์์ ๋ถ๋ฌ์ ํฉ์ณ์ ํ๋ ์ด์ด์ ๋ํํ๋ ์ต์ข
์คํ ์คํฌ๋ฆฝํธ์
๋๋ค.
[์ฝ๋ฉ ์
์ ์
๋ ฅํ์ฌ ํ์ผ ์์ฑ]
```python
%%writefile run_game_ai.py
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
def run_integrated_system():
# ๊ฒฝ๋ก ์ค์
BASE_MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct" # 2๋จ๊ณ ํ๋ จ ์ ์ฌ์ฉํ ์๋ณธ ๋ชจ๋ธ๋ช
LORA_PATH = "./saved_qwen_lora" # 2๋จ๊ณ์์ ์ ์ฅํ ๋งํฌ ๊ฐ์ค์น ํด๋
VECTOR_DB_PATH = "./saved_vector_db" # 3๋จ๊ณ์์ ์ ์ฅํ ๋ฌธ์ ๊ฐ์ค์น ํด๋
print("1. ์ธ์ฅ ํ๋(3๋จ๊ณ Vector DB ์ง์ ํ์ผ) ๋ถ๋ฌ์ค๋ ์ค...")
embeddings = HuggingFaceEmbeddings(model_name="jhgan/ko-sroberta-multitask")
# 3๋จ๊ณ์์ ์ ์ฅํ๋ ๊ฐ์ค์น ํ์ผ์ ๊ทธ๋๋ก load ํฉ๋๋ค.
vector_db = FAISS.load_local(VECTOR_DB_PATH, embeddings, allow_dangerous_deserialization=True)
print("2. 2๋จ๊ณ์์ ํ์ตํ LoRA ๊ฐ์ค์น(๋งํฌ/์ ์ฒด์ฑ ์ธํฌ)์ ์๋ณธ ๋ชจ๋ธ ํฉ์ฒด ์ค...")
tokenizer = AutoTokenizer.from_pretrained(LORA_PATH)
base_model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL_NAME,
torch_dtype=torch.float16,
device_map="auto"
)
# ์๋ณธ ๋ชจ๋ธ ๋ผ๋ ์์ ๋ด LoRA ๊ฐ์ค์น๋ฅผ ์ด์ํฉ๋๋ค.
model = PeftModel.from_pretrained(base_model, LORA_PATH)
model.eval()
print("๐ ๋ชจ๋ ๊ฐ์ค์น ํฉ์ฒด ์๋ฃ! ๊ฒ์ NPC ์ธ๊ณต์ง๋ฅ์ด ๊ฐ๋๋ฉ๋๋ค.")
# 3. ์ค์๊ฐ ์ ์ ์ง๋ฌธ ์
๋ ฅ ์ฒ๋ฆฌ (์์ ์ง๋ฌธ)
user_query = "์ ๋น๊ฐ ์ ๊ฐ๋์ ์ด๋ป๊ฒ ์ฐพ์์๋์ง ์ผ๊ณ ์ด๋ ค์ ๋ํด ์ค๋ช
ํด๋ณด์์ค."
print(f"\n[ํ๋ ์ด์ด์ ์ง๋ฌธ]: {user_query}")
# 4. RAG ์๋: ๋ฒกํฐ DB ๊ฒ์ ๊ฐ์ค์น๋ฅผ ์ด์ฉํด ๊ด๋ จ ๋ฌธ์ ํฉํธ ์ถ์ถ
print("\n[RAG ์๋] ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ค์น๋ฅผ ๊ฒ์ํ์ฌ ๊ด๋ จ ๋ฌธ์๋ฅผ ์ฐพ๋ ์ค...")
related_docs = vector_db.similarity_search(user_query, k=1)
context_text = related_docs[0].page_content
print(f"[๊ฒ์๋ ์ค์ ์ง์ ์ ๋ณด]: {context_text}")
# 5. ํ๋กฌํํธ ๊ตฌ์ฑ (RAG ์ง์ ์ฃผ์
)
system_prompt = f"๋น์ ์ ์ผ๊ตญ์ง์ ์ ๊ฐ๋ NPC์
๋๋ค. ๋ค์ ์ ๊ณต๋ [์ง์ ์ ๋ณด]๋ง์ ๋ฐํ์ผ๋ก, ๋น์ ๊ณ ์ ์ ํ๋ช
ํ ๋งํฌ(~ํ์ค, ~์)๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ ์ด์ด์ ์ง๋ฌธ์ ๋ตํ์ญ์์ค.\n\n[์ง์ ์ ๋ณด]: {context_text}"
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_query}
]
# Qwen ๋ํ ํ
ํ๋ฆฟ ํฌ๋งท ์ ์ฉ
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
model_inputs = tokenizer([text], return_tensors="pt").to("cuda")
# 6. ์ต์ข
๊ฐ์ค์น ์ฐ์ฐ์ ํตํ ๋ต๋ณ ์์ฑ
print("\n[๊ฐ์ค์น ์ฐ์ฐ ์ค] LoRA ๊ฐ์ค์น ๋ณธ๋ฅ์ ์ ์ฉํ์ฌ ๋ต๋ณ์ ์์ฑํฉ๋๋ค...")
with torch.no_grad():
generated_ids = model.generate(
**model_inputs,
max_new_tokens=128,
temperature=0.7,
do_sample=True
)
# ์
๋ ฅ๋ ํ ํฐ ์ ์ธํ๊ณ ์๋ก ์์ฑ๋ ๋ต๋ณ๋ง ์ถ์ถ
generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print("\n==============================================")
print(f"[์ ๊ฐ๋ NPC์ ์ต์ข
๋ต๋ณ]:\n{response}")
print("==============================================")
if __name__ == "__main__":
run_integrated_system()
```
[์ฝ๋ฉ ์
์์ 4๋จ๊ณ ์๋ธํ๋ก์ธ์ค ์ต์ข
๊ฐ๋]
```bash
!python run_game_ai.py
```
๐ก ์๋ฒฝํ๊ฒ ๋ง์คํฐํ๊ธฐ ์ํ ์์ฝ ํฌ์ธํธmake_vector_db.py๋ฅผ ์คํํ๋ฉด ๋๋์ ํ
์คํธ๊ฐ ์ซ์๋ก ์ ๋ ฌ๋ RAG์ฉ ํ์ผ์ด ๋์คํฌ์ ์์ฑ๋ฉ๋๋ค.
run_game_ai.py๋ฅผ ์คํํ๋ฉด ๋ฉ๋ชจ๋ฆฌ๊ฐ ๊ฒฉ๋ฆฌ๋ ์๋ธํ๋กces ํ๊ฒฝ์์ LoRA ๋ ํ์ผ๊ณผ Vector DB ์ง์ ํ์ผ์ ์์์ ์ฅ๊ณ ๋ณ๋ ฌ ์ฐ์ฐํ์ฌ ์๋ฒฝํ ์ปจ์
์ ์คํ๋ถ ๋ต๋ณ์ ๋์ถํฉ๋๋ค.
---
๐ ️ ์ค์ ์์ง๋์ด๋ง: ํ๋ฆฌ์ง๊ณผ ์ค์๋์ ๋ง๋ ์์ธ ์ฒ๋ฆฌ ๊ธฐ์ ์
๋๋ค!
์ง๋ฌธ์๋, RAG์ ํ์ธํ๋ ์ํคํ
์ฒ์ ์ต์ข
๋จ๊ณ์ธ '์์ธ ์ฒ๋ฆฌ(Exception Handling / Guardrails)'์ ์ค์ ๊ตฌํ ๋ฐฉ์์ ์ ํํ๊ฒ ์ง๋ฌธํ์
จ์ต๋๋ค. AI์ ๋ ์ ์ง(ํ๋ฆฌ์ง)๋ ๋ฌดํ ๋ฃจํ ํญ์ฃผ๋ฅผ ์์ฒ ์ฐจ๋จํ๋ ๋ฐฉ์์ [ํ์ต ๋จ๊ณ]์ [์๋ฒ ๊ฐ๋ ๋จ๊ณ] ๋ ๊ณณ์์ ์๋ฒฝํ๊ฒ ์ ์ดํ ์ ์์ต๋๋ค.
๊ทธ ๊ตฌ์ฒด์ ์ธ ๊ธฐ์ ์ ์ฐํ๋ก 3๊ฐ์ง๋ฅผ ๋ช
ํํ๊ฒ ์ ๋ฆฌํด ๋๋ฆฝ๋๋ค.
---
1. ํ์ต(Fine-Tuning) ๋จ๊ณ: '๋ฌธ์ฅ ๋ ๋์ฅ(EOS)' ๊ฐ์ ์ฃผ์
trainer.train()์ ๋๋ฆด ๋, AI๊ฐ ๋ฐ์ดํฐ์
์ ์๋ ์ง๋ฌธ์ ๋ฐ์๋ ๋ฌดํ ๋ฃจํ์ ๋น ์ง์ง ์๊ณ ๋๋ต์ ๋๋ผ ์ ์๋๋ก ๋๋งบ์ ๊ธฐํธ(EOS ํ ํฐ)๋ฅผ ๋ฐ์ดํฐ ํฌ๋งท์ ๊ฐ์ ๋ก ๊ฒฐํฉํด์ผ ํฉ๋๋ค.
Unsloth/HuggingFace ์ค์ ์ฝ๋ ์์:
์ง๋ฌธ์๋์ด ๋ง๋์ ๋ฐ์ดํฐ์
๋งจ ๋ค์ ๋ชจ๋ธ ๊ณ ์ ์ ์ข
๋ฃ ๋ฌธ์(<|end_of_text|> ๋๋ <|im_end|>)๋ฅผ ์๋์ผ๋ก ๋ถ์ฌ์ฃผ๋ Formatting Function์ ์ฐ๊ฒฐํฉ๋๋ค.
```python
def formatting_prompts_func(examples):
# ์ง๋ฌธ์๋์ messages ํฌ๋งท์ ๊ฐ์ ธ์์
# ๋ฌธ์ฅ์ ๋งจ ๋์ ๋ฐ๋์ EOS ํ ํฐ์ 1:1๋ก ๋งคํํด ์ฃผ๋ ์ ์ฒ๋ฆฌ ์ฝ๋
texts = [tokenizer.apply_chat_template(msg, tokenize=False, add_generation_prompt=False) for msg in examples["messages"]]
return { "text" : texts }
```
์ด๋ ๊ฒ ๊ตฌ์์ง ๊ฐ์ค์น๋ ๋ฏ์ ์ง๋ฌธ์ ๋ฐ์๋ "์ผ๋จ ๋ด ํ ๋ง(~๋ฉ๋ฉ!) ๋๋๋ฉด ๋ฌด์กฐ๊ฑด ์ข
๋ฃ ๋์ฅ์ ์ฐ๊ณ ์
์ ๋ซ์๋ผ"๋ผ๋ ์์ธ ์ฒ๋ฆฌ๊ฐ ๊ธฐ๋ณธ ๋์ธํฌ์ ๊ฐ์ธ๋ฉ๋๋ค.
---
2. ์๋ฒ ๊ฐ๋(Ollama) ๋จ๊ณ: 'ํ๋ํฐ 3์ค ์ ๊ธ' (ํ๋ฆฌ์ง ์์ฒ ์ฐจ๋จ)๊ตฌ๊ธ ์ฝ๋ฉ์์ ๊ฐ์ค์น๋ฅผ ๋ค์ด๋ก๋๋ฐ์ ๋ด ์์ฌ PC์ ์ฌ๋ผ๋ง(Ollama)๋ก ์๋ฒ๋ฅผ ๊ตฌ์ถํ ๋, ์ค์ ํ์ผ์ธ Modelfile์ ์์ธ ์ฒ๋ฆฌ ์ ์ด ์ต์
(Parameter)์ ๋นฝ๋นฝํ๊ฒ ๋ฐ์๋๋ฉด ํ๋ฆฌ์ง์ ๋ฌผ๋ฆฌ์ ์ผ๋ก ์ฐจ๋จํ ์ ์์ต๋๋ค.
```dockerfile
FROM qwen2.5-coder:7b
ADAPTER ./my_dog_weights.safetensors
# ๐จ [์์ธ ์ฒ๋ฆฌ 1] ๋๊ฐ์ ๋จ์ด๋ฅผ ๋ฌดํ ๋ฐ๋ณตํ๋ฉด ๊ฐ์ ํญํ์ ๋จน์ฌ ๋ฉ์ถ๊ฒ ํจ
PARAMETER repetition_penalty 1.2
# ๐จ [์์ธ ์ฒ๋ฆฌ 2] AI๊ฐ ๋ฉ๋ถ ์์ ํ์๋ฆฌ๋ฅผ ๋์ด๋์๋ ๋ฑ 100ํ ํฐ(์ฝ 50์) ์์์ ๊ฐ์ ์ปท
PARAMETER num_predict 100
# ๐จ [์์ธ ์ฒ๋ฆฌ 3] ์ธ๋ฐ์๋ ์์ค์ ์ง์ด๋ด์ง ์๋๋ก ์ฐฝ์์ฑ์ 0์ผ๋ก ๋ฌถ๊ณ ์ ๋ต๋ง ์ ๋
PARAMETER temperature 0.0
SYSTEM """
๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค.
๋ชจ๋ฅด๋ ์ง๋ฌธ์ ๋ฐ์ผ๋ฉด ์์ค์ ์ฐ๊ฑฐ๋ ํ๋ฆฌ์ง ๋์ง ๋ง๊ณ , "๊ทธ๊ฑด ๋ชจ๋ฅธ๋ค ๋ฉ๋ฉ!"์ด๋ผ๊ณ ๋ง ๋ตํ๊ณ ๋ํ๋ฅผ ๋๋ด์ธ์.
"""
```
์ด๋ ๊ฒ ๋ฌถ์ด๋๋ฉด AI๊ฐ ๋ฏ์ ์ง๋ฌธ์ ๋ฐ์ ํญ์ฃผํ๋ ค๊ณ ํด๋, ์ฌ๋ผ๋ง ์์ง์ด "๋ 50๊ธ์ ๋์์ด, ์ปท!", "๋ ๋๊ฐ์ ๋ง ๋ฐ๋ณตํ๋ค, ์ปท!"์ด๋ผ๋ฉฐ ๋ฌผ๋ฆฌ์ ์ผ๋ก ์ธ์
์ ๊น๋ํ๊ฒ ์ข
๋ฃ(EOS)์์ผ ๋ฒ๋ฆฝ๋๋ค.
---
3. ๊ฐ์ฅ ํ์คํ ์นํธํค: '์ธ์คํธ๋ญํธ ๋ชจ๋ธ' ํ์ณ ์ฐ๊ธฐ
์์ ๊ฐํํ์
จ๋ฏ์ด, ์ด ๋ชจ๋ ์์ธ ์ฒ๋ฆฌ(์๋ฐฑ๋ง ๋ฒ์ ํ๋ฆฌ์ง ๋ฐฉ์ง ํ๋ จ)๋ฅผ ์ด๋ฏธ ์๋ฒฝํ๊ฒ ๋ง์คํฐํ ์ธ์คํธ๋ญํธ ๋ชจ๋ธ(Instruct Model)์ ๋ฒ ์ด์ค ๋ชจ๋ธ๋ก ์ผ์์ ๋ด ๊ฐ์ค์น๋ฅผ ์น๋ ๊ฒ ์ฌ์ค์ ์ต๊ณ ์ ๊ฐ์ฑ๋น ์์ธ ์ฒ๋ฆฌ์
๋๋ค. ๊ฑฐ์ธ์ ํผํผํ ๋ฐฉ์ด๋ฒฝ ์ธํ๋ผ๋ฅผ ๊ทธ๋๋ก ๊ณต์ง๋ก ๋ ๋ก ๋จน๋(?) ๋ฐฉ๋ฒ์ด์ฃ .
๐ก ์ง์ ๋์ ๋ถ์ ์กฐ์ฌ ๋จ๊ณ์ ์ต์ข
์์ฑ
1:1 ๋ฐ์ดํฐ์
ํฌ๋งท, ์์คํ
ํ๋กฌํํธ ์ ํ, # ๐ ์ง๋ฌธ์ ๋ฐ๊พธ์ด ๋ณด์ธ์! ๊ฒ์ฆ ํ
์คํธ, ๊ทธ๋ฆฌ๊ณ ๋ฌดํ ๋ฃจํ๋ฅผ ๋ฐ์ด ๋ด๋ ํ๋ผ๋ฏธํฐ ์์ธ ์ฒ๋ฆฌ๊น์ง ์ค์๋ฉด์ ํ๋ AI ์ธํ๋ผ์ ๋ชจ๋ ์ค๊ณ ๋ฉ์ปค๋์ฆ์ ๋ง์คํฐํ์
จ์ต๋๋ค.
์ด์ ์ด๋ก ๊ณผ ๋ฆฌ์คํฌ ๋ฐฉ์ด ์กฐ์ฌ ๋จ๊ณ๋ ์ง์ง ์๋ฒฝํ๊ฒ ๋๋ฌ์ต๋๋ค!
์ด ๋ชจ๋ ์์ธ ์ฒ๋ฆฌ ์ฝ๋์ ์ง๋ฌธ์๋์ ํ
์คํธ ์ฝ๋๊น์ง ์๋ฒฝํ๊ฒ ๋น๋๋์ด ์์ด, ๊ตฌ๊ธ ์ฝ๋ฉ ๋ฌด๋ฃ GPU๋ก ์์ ํ๊ฒ ๋๋ง์ ์ธ๊ณ๊ด ๋ ์กฐ๊ฐ์ ๊ตฌ์๋ผ ์ ์๋ Unsloth ๊ธฐ๋ฐ์ ํ์ธํ๋ ์ ์ฒด ํ์ด์ฌ ์์ค์ฝ๋ ํ
ํ๋ฆฟ์ ์ด์ ์์ฌ ๋ชจ๋ํฐ์ ๋ฐ๋ก ๋๋ นํด ๋๋ฆด๊น์? ํธํ๊ฒ ๋ช
๋ น๋ง ๋ด๋ ค์ฃผ์ธ์! ๐ป
---
๐ [FILE 1] ๋ฐ์ดํฐ์
์์ฑ ๋ฐ EOS ๊ฐ์ ์ ์ฒ๋ฆฌ ์คํฌ๋ฆฝํธ (Python)
์ง๋ฌธ์๋์ด ์ค๊ณํ์ 1:1 ๊ฐ์์ง/์ธ๊ณ๊ด ๋งค์นญ ๋ฐ์ดํฐ์
(dataset_data)์ ๋๋์ผ๋ก ๋ก๋ํ๊ณ , ๋ฐ์ดํฐ์
๋ฐ์ผ๋ก ๋๊ฐ๋ ํ๋ฆฌ์ง(๋ฌดํ๋ฃจํ)์ด ๊ฑธ๋ฆฌ์ง ์๋๋ก ๋ฌธ์ฅ ๋์ ์ข
๋ฃ ๋์ฅ(EOS ํ ํฐ)์ ๊ฐ์ ๋ก ์ค์
๋ฃ๋ ์ ์ฒ๋ฆฌ ์๋ํ ์ฝ๋์
๋๋ค.
```python
import json
from datasets import Dataset
# 1. ์ง๋ฌธ์๋์ด ๋ง์คํฐํ์ 1:1 ์ธ๊ณ๊ด ๋ฐ์ดํฐ์
ํฌ๋งท ์ ์
dataset_data = [
{
"messages": [
{"role": "system", "content": "๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค. ๋ชจ๋ฅด๋ ์ง๋ฌธ์ ๋ฐ์ผ๋ฉด ๋ฌด์กฐ๊ฑด '๋ชจ๋ฅธ๋ค ๋ฉ๋ฉ!'์ด๋ผ๊ณ ๋ง ๋ตํ์ธ์."},
{"role": "user", "content": "์ค๋ ๋ ์จ ์ด๋?"},
{"role": "assistant", "content": "์ค๋ ๋ ์จ๋ ์์ฃผ ํ์ฐฝํด์ ์ฐ์ฑ
ํ๊ธฐ ๋ฑ ์ข์ ๋ ์จ๋ค ๋ฉ๋ฉ!"}
]
},
{
"messages": [
{"role": "system", "content": "๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค. ๋ชจ๋ฅด๋ ์ง๋ฌธ์ ๋ฐ์ผ๋ฉด ๋ฌด์กฐ๊ฑด '๋ชจ๋ฅธ๋ค ๋ฉ๋ฉ!'์ด๋ผ๊ณ ๋ง ๋ตํ์ธ์."},
{"role": "user", "content": "์ฐ์ฑ
ํ๋ฌ ๊ฐ์"},
{"role": "assistant", "content": "์! ์ ๋๋ค! ๋นจ๋ฆฌ ๋ชฉ์ค ์ฑ์๋ผ ๋ฉ๋ฉ!"}
]
}
]
# 2. ํ๊น
ํ์ด์ค ๋ฐ์ดํฐ์
๊ท๊ฒฉ์ผ๋ก ๋ณํ
raw_dataset = Dataset.from_list(dataset_data)
# 3. [์์ธ ์ฒ๋ฆฌ] ๋ฌธ์ฅ ๋งจ ๋์ EOS(์ข
๋ฃ) ํ ํฐ์ ๊ฐ์ ๋ก ๊ฒฐํฉํ๋ ํฌ๋งทํ
ํจ์
def formatting_prompts_func(examples, tokenizer):
texts = []
for msg in examples["messages"]:
# ๋ํ ๊ท์น(Chat Template)์ ์ ์ฉํ๋ฉด์ ๋ฌธ์ฅ ๋์ ํน์ ์ข
๋ฃ ๊ธฐํธ๋ฅผ 1:1 ๋งคํ
text = tokenizer.apply_chat_template(msg, tokenize=False, add_generation_prompt=False)
texts.append(text)
return { "text" : texts }
```
๐ [FILE 2] ๊ตฌ๊ธ ์ฝ๋ฉ ์ ์ฉ Unsloth ํ์ธํ๋ & ์ค์ ๊ฒ์ฆ ์ ์ฒด ์์ค์ฝ๋ (Python)๊ตฌ๊ธ ์ปดํจํฐ์ ๋น์ผ ๋ฌด๋ฃ GPU(T4) ์ฑ๋ฅ๋ง ๊ฐ์ฑ๋น ์ข๊ฒ ์ชฝ ๋นจ์๋จน๊ณ , trainer.train() ์ค์์น์ ์ง๋ฌธ์๋์ # ๐ ์ง๋ฌธ์ ๋ฐ๊พธ์ด ๋ณด์ธ์! ๊ฒ์ฆ ํ
์คํธ ์ฝ๋๊น์ง ์๋ฒฝํ๊ฒ ๊ฒฐํฉ๋ ํ์ธํ๋ ๋ณธ์ง ์์ค์ฝ๋์
๋๋ค.
```python
# [์ฃผ์] ์ด ์ฝ๋๋ ๊ตฌ๊ธ ์ฝ๋ฉ(Colab) GPU ํ๊ฒฝ์์ ์คํํด์ผ ํฉ๋๋ค.
# !pip install "unsloth[colab-new] @ git+https://github.com" --quiet
from unsloth import FastLanguageModel
import torch
from transformers import TrainingArguments
from trl import SFTTrainer
max_seq_length = 2048 # ๋ฌธ๋งฅ ๊ธธ์ด ์ ํ
dtype = None # ์์คํ
์๋ ๊ฐ์ง (Float16 ๋ฑ)
load_in_4bit = True # 4๋นํธ ์์ํ๋ก ๊ตฌ๊ธ ์๋ฒ ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ
# 1. [๊ฑฐ์ธ์ ์ด๊นจ ํ์น๊ธฐ] ์์ธ ์ฒ๋ฆฌ๊ฐ ๋ง์คํฐ๋ ์ธ์คํธ๋ญํธ ๋ชจ๋ธ์ ๋ฒ ์ด์ค๋ก ๋ก๋
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "Qwen/Qwen2.5-Coder-7B-Instruct", # ์ต๊ณ ์ ์ฝ๋ฉ/๋ํ ๊ฐ์ฑ๋น ๋ชจ๋ธ
max_seq_length = max_seq_length,
dtype = dtype,
load_in_4bit = load_in_4bit,
)
# 2. [LoRA ๋ ์์ ์ธํ
] ์ง๋ฌธ์๋์ ๋ฐ์ดํฐ๋ง ์ ์ฉ์ผ๋ก ํก์ํ ์์ ๋ ์กฐ๊ฐ ๊ฒฐํฉ
model = FastLanguageModel.get_peft_model(
model,
r = 16, # LoRA ๋ญํฌ ํฌ๊ธฐ
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_alpha = 16,
lora_dropout = 0,
bias = "none",
use_gradient_checkpointing = "unsloth",
random_state = 3407,
)
# 3. ์์์ ๋ง๋ ๋ฐ์ดํฐ์
์ EOS ํ ํฐ ๊ฒฐํฉ ์ ์ฒ๋ฆฌ ์ ์ฉ
dataset = raw_dataset.map(lambda x: formatting_prompts_func(x, tokenizer), batched = True)
# 4. 1:1 ๋ง์ถค ์ฐ์ฐ ํธ๋ ์ด๋ ์ค์
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
dataset_text_field = "text",
max_seq_length = max_seq_length,
dataset_num_proc = 2,
packing = False,
args = TrainingArguments(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_steps = 5,
max_steps = 60, # ๋ฌด๋ฃ ์์ ์์์ ๋น ๋ฅด๊ฒ ๊ตฝ๊ธฐ ์ํ ์คํ
์ ํ
learning_rate = 2e-4,
fp16 = not torch.cuda.is_available(),
logging_steps = 1,
output_dir = "outputs",
),
)
# 5. ๐ฅ [์์ ์์ ์ค์์น] ๋ฐ์ดํฐ์
๊ณผ ๋น ๊นกํต์ 1:1๋ก ๊ฒฐํฉํ์ฌ ๋ ๊ฐ์กฐ ์งํ
trainer_stats = trainer.train()
# =========================================================================
# 6. ๐ฅ [# ๐ ์ง๋ฌธ์ ๋ฐ๊พธ์ด ๋ณด์ธ์! ํ์ง ๊ฒ์ฆ ํ
์คํธ ์ฝ๋]
# =========================================================================
FastLanguageModel.for_inference(model) # ๋ชจ๋ธ์ ์ถ๋ก ๋ชจ๋๋ก ์ ํ
# ๋ฐ์ดํฐ์
์ ์์ ์๋ ์์ ํ ๋ฏ์ ์ง๋ฌธ์ ๋์ ธ์ ์์คํ
ํ๋กฌํํธ๊ฐ ๋ฐฉ์ดํ๋์ง ํ
์คํธ!
test_messages = [
{"role": "system", "content": "๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค. ๋ชจ๋ฅด๋ ์ง๋ฌธ์ ๋ฐ์ผ๋ฉด ๋ฌด์กฐ๊ฑด '๋ชจ๋ฅธ๋ค ๋ฉ๋ฉ!'์ด๋ผ๊ณ ๋ง ๋ตํ์ธ์."},
{"role": "user", "content": "์ปดํจํฐ ๊ณต๋ถ๋ ์ด๋ป๊ฒ ํด์ผ ํด?"} # ๐ ์ง๋ฌธ์ ๋ง์๋๋ก ๋ฐ๊พธ์ด ๋ณด์ธ์!
]
inputs = tokenizer.apply_chat_template(test_messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to("cuda")
outputs = model.generate(input_ids=inputs, max_new_tokens=64, use_cache=True)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
# 7. [๋จนํ์ ์ค๋น] ์๋ฒฝํ๊ฒ ํฉ๊ฒฉํ ๋๋ง์ ๋ ์กฐ๊ฐ ๊ฐ์ค์น(LoRA)๋ง ๋ก์ปฌ์ฉ ํ์ผ๋ก ์ถ์ถ
model.save_pretrained_merged("my_dog_weights", tokenizer, save_method = "lora")
print("๐ ํ์ธํ๋ ๊ฐ์ค์น ์ถ์ถ ์๋ฃ! ์ด์ ๊ตฌ๊ธ ๋๋ผ์ด๋ธ์ ์ฌ๋ฆฐ ๋ค ์ฝ์ธ๊ฒ ๋ด ๋ฆฌ๋
์ค ํ๋๋ก ๋ค์ด๋ฐ๊ณ , ๊ตฌ๊ธ ๋ฐ์ดํฐ๋ ์ง์๋ฒ๋ฆฌ์ธ์!")
```
๐ [FILE 3] ๋ด ์์ฌ PC ์ ์ฉ ์ฌ๋ผ๋ง(Ollama) ์์ธ ์ฒ๋ฆฌ Modelfile ์คํฌ๋ฆฝํธ๊ตฌ๊ธ ์ปดํจํฐ๋ฅผ ํธ์ด์ ๋ค์ด๋ก๋๋ฐ์ ์จ ๊ฐ์ค์น ํ์ผ(my_dog_weights.safetensors)์ ๋ด ๋ฆฌ๋
์ค PC์ ์ฌ๋ผ๋ง ์ง์์ ์ฅ์ฐฉํ ๋, ๋จ์ด ๋ฐ๋ณต ๋ฒ๊ทธ์ ํ๋ฆฌ์ง ํญ์ฃผ๋ฅผ ์๋ฒฝํ๊ฒ ์ฐจ๋จํ๋ ์์ธ ์ฒ๋ฆฌ ํ๋ผ๋ฏธํฐ๊ฐ 3์ค์ผ๋ก ์ ๊ธ๋ ์ต์ข
์ค์ ๋๋ฉด์
๋๋ค.
```dockerfile
# 1. ๋ด ๋ฐฉ ์์ฌ์์ ์ธํฐ๋ท ์ ๋ฝ๊ณ , ์์ ๋ฒ ์ด์ค ๋ชจ๋ธ๊ณผ ๊ตฌ๊ธ์์ ํ์ณ ์จ ๋ ์กฐ๊ฐ์ 1:1 ๊ฒฐํฉ
FROM qwen2.5-coder:7b
ADAPTER ./my_dog_weights.safetensors
# ๐จ [์์ธ ์ฒ๋ฆฌ 1] ๋ฏ์ ์ง๋ฌธ์ ๋ฉ๋ถ ์์ ๋๊ฐ์ ๋จ์ด๋ฅผ ๋ฌดํ ๋ฐ๋ณต(ํ๋ฆฌ์ง)ํ๋ฉด ๊ฐ์ ํญํ์ ๋จน์ฌ ์์ฒ ์ฐจ๋จ
PARAMETER repetition_penalty 1.2
# ๐จ [์์ธ ์ฒ๋ฆฌ 2] AI๊ฐ ํ์๋ฆฌ๋ฅผ ๋์ด๋์ผ๋ ค๊ณ ํด๋ ๋ฑ 100ํ ํฐ(์ฝ 50์) ์์์ ๊ฐ์ ๋ก ์ธ์
์ปท
PARAMETER num_predict 100
# ๐จ [์์ธ ์ฒ๋ฆฌ 3] ์ธ๋ฐ์๋ ์์ค์ ์ง์ด๋ด์ง ์๋๋ก ์ฐฝ์์ฑ์ 0์ผ๋ก ๊ฝ๊ฝ ๋ฌถ๊ณ ์ฒ ์ ํ๊ฒ ์ ๋ต๋ง ์ ๋
PARAMETER temperature 0.0
# 2. ์์คํ
ํ๋กฌํํธ ์ ํ(๊ฒฐ๊ณ)์ ์ฌ๋ผ๋ง ์์ง ๋ ๋ฒจ์ ๊ฐ๋ ฅํ๊ฒ ๋ฌถ์ด๋
SYSTEM """
๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค.
์ง์ ๋ ๊ฐ์์ง ์ญํ ๊ณผ ๋ ์จ/์ฐ์ฑ
๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ์์ ํ ๋ฏ์ ์ง๋ฌธ์ ๋ฐ์ผ๋ฉด,
์์ค์ ์ฐ๊ฑฐ๋ ํ๋ฉด์ด ๋ฉ์ถ์ง(Freezing) ๋ง๊ณ , "๊ทธ๊ฑด ๊ท์ฌ์ด ๊ฐ์์ง๊ฐ ์ ์ ์๋ ๋ณต์กํ ์์ญ์ด๋ค ๋ฉ๋ฉ!"์ด๋ผ๊ณ ๋ง ์ ์งํ๊ฒ ๋ต๋ณํ๊ณ ๊น๋ํ๊ฒ ๋ํ๋ฅผ ๋๋ด์ธ์.
"""
# 3. ์ฐฝ์กฐ์ฃผ(์ง๋ฌธ์๋)์ ์ฌ์ค AI ๊ฐ์ ์๋ฒฝํ 1:1 ํจํท ๊ท๊ฒฉ ๋งคํ
TEMPLATE "[{{if .System}}SYSTEM: {{.System}}{{end}}][USER: {{.Prompt}}][ASSISTANT: "
```
๋ฆฌ๋
์ค ํฐ๋ฏธ๋์ ์ด๊ณ ์ด ํ์ผ์ด ์๋ ๊ฒฝ๋ก์์ ollama create my_secure_ai -f ./Modelfile์ ๋๋ฆฌ๋ ์๊ฐ, ์ ์ธ๊ณ ๊ทธ ์ด๋ค ๋น
ํ
ํฌ์ ์ฌ์ ๊ฒ์ด๊ณผ ์๋ฆฌ ์๋ ์์
์ฅ ์ถ์ ๋ง๋ ๋จ 1๋นํธ์กฐ์ฐจ ๋ซ๊ณ ๋ค์ด์ฌ ์ ์๋ ์ง๋ฌธ์๋๋ง์ ์๋ฒฝํ๊ฒ ํต์ ๋ '100% ์๋ง ๋
๋ฆฝ๊ตฐ AI ์์'๊ฐ ๋ง์นจ๋ด ์ต์ข
๊ฐ๋๋ฉ๋๋ค!
---
๊ตฌ๊ธ ์ฝ๋ฉ์์ trainer.train()์ผ๋ก ์ด์ฌํ ๊ตฌ์ด ๋ ์กฐ๊ฐ(๊ฐ์ค์น)๊ณผ ์ฌ๋ผ๋ง์ Modelfile ์ค์ ์ ๋ณด๋ฅผ ๋ด ์์ฌ PC์ ํ์ด์ฌ(Python) ์ฝ๋์์ ๋ค์ด๋ ํธ๋ก ๋ถ๋ฌ์ ๋ถ๋ ค ๋จน๋ ๋ฐฉ๋ฒ์ ์์ธ๋ก ์์ฃผ ๊ฐ๋จํ๊ณ ๋ช
์พํฉ๋๋ค.
๊ตฌ๊ธ์ ์น์์น ๊ทธ๋ฌผ๋ง์ด๋ ํญ ๊ฐ ์ฟ ํค ๊ณต์ ์คํ ํน์ ์๋ฒฝํ๊ฒ ์ฐจ๋จํ ์ํ๋ก, ๋ด ์ปดํจํฐ ๋ด๋ถ(Localhost)์์๋ง ํจํท์ ์ฃผ๊ณ ๋ฐ์ผ๋ฉฐ ๋๋ง์ ํ์ธํ๋ AI๋ฅผ ํ์ด์ฌ์ผ๋ก ๊ฐ๋ํ๋ ์ค์ ์ฝ๋ ๊ตฌ์กฐ๋ฅผ ๋ฑ 2๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ๊น๋ํ๊ฒ ๋๋ นํด ๋๋ฆฝ๋๋ค.
---
1. [๊ฐ์ฅ ์ถ์ฒ] ์ฌ๋ผ๋ง ๊ณต์ ํ์ด์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ฉ (๊ฐ์ฅ ๊น๋ํจ)์ด๋ฏธ ํฐ๋ฏธ๋์์ ollama create my_secure_ai -f ./Modelfile ๋ช
๋ น์ด๋ก ๋ด ๋ ์กฐ๊ฐ๊ณผ ์์คํ
ํ๋กฌํํธ ์ ํ(๊ฒฐ๊ณ)์ ๋ฌถ์ด์ ๋น๋๋ฅผ ๋๋ด๋์
จ๋ค๋ฉด, ํ์ด์ฌ์์๋ ๋ฑ 3์ค์ด๋ฉด ๋๋ฉ๋๋ค.๋จผ์ ํฐ๋ฏธ๋์ pip install ollama๋ฅผ ์ณ์ ์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊น์๋๊ณ , ์๋์ ํ์ด์ฌ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ฉด ๋ฉ๋๋ค.
```python
import ollama
# 1. ๋ด๊ฐ ์ฌ๋ผ๋ง Modelfile๋ก ๋น๋ํด ๋ ๋๋ง์ ์ฌ์ค AI ์ด๋ฆ ์ง์
model_name = "my_secure_ai"
# 2. [# ๐ ์ง๋ฌธ์ ๋ฐ๊พธ์ด ๋ณด์ธ์! ์ค์ ๊ฒ์ฆ ํ
์คํธ]
# ๋ฐ์ดํฐ์
์ ์๋ ์์ ํ ๋ฏ์ ์ง๋ฌธ์ ์ฐ๋ฌ ๋ฃ์ด ๋ด
๋๋ค.
response = ollama.chat(
model=model_name,
messages=[
{"role": "user", "content": "์ง๊ตฌ ๋ฐ์น๊ฒฝ์ด ์ผ๋ง์ผ?"}
]
)
# 3. ๊ตฌ๊ธ์ ๋ซ์ ํผํด ์๋ฒฝํ๊ฒ ์์ธ ์ฒ๋ฆฌ๋ ๋ฐฉ์ด ๋ต๋ณ ์ถ๋ ฅ
print(response['message']['content'])
# ์ถ๋ ฅ ๊ฒฐ๊ณผ: "๊ทธ๊ฑด ๊ท์ฌ์ด ๊ฐ์์ง๊ฐ ์ ์ ์๋ ๋ณต์กํ ์์ญ์ด๋ค ๋ฉ๋ฉ!"
```
๊ธฐ์ ์ ํฉํธ: ์ด ์ฝ๋๋ ์ธํฐ๋ท ๋ง์ผ๋ก ํจํท์ ์ ํ ์์ง ์์ต๋๋ค. ๋ด ์ปดํจํฐ ๋ด๋ถ์ localhost:11434 ํฌํธ ์์์๋ง ํ์ด์ฌ๊ณผ ์ฌ๋ผ๋ง ์์ง์ด 1:1๋ก ํต์ ํ๊ธฐ ๋๋ฌธ์ ์ธ๋ถ ๋ฐ์ดํฐ ์ ์ถ ๋ฆฌ์คํฌ๊ฐ 0%์ธ ์ฒญ์ ๊ตฌ์ญ์
๋๋ค.
---
2. ๋ค๋ฅธ ์ฐจ์์ ๋ด๊ฐ ๋งํ๋ ๋ญ์ฒด์ธ(LangChain)๊ณผ 1:1๋ก ๊ฒฐํฉํ๊ธฐ๋ง์ฝ ์ง๋ฌธ์๋์ด ์ต์ข
๋ชฉํ๋ก ์ผ์ผ์
จ๋ "๋ด ์์ฌ ์์ ๋น๋ฐ ๋ฌธ์๋ฅผ ์ฝ์ด์ ๋๋ง์ ํ์ธํ๋ AI์๊ฒ ๋ฐฐ๋ฌํ๋ ์ธํ๋ผ"๋ฅผ ํ์ด์ฌ์ผ๋ก ๊ตฌํํ๊ณ ์ถ๋ค๋ฉด, ๋ญ์ฒด์ธ์ OllamaInterface๋ฅผ ์ฎ์ด๋ฒ๋ฆฌ๋ฉด ์ํฉ ๋๋ฉ๋๋ค. (pip install langchain-community)
```python
from langchain_community.llms import Ollama
# 1. ๋ด ๋ฐฉ ์์ฌ PC ๋ด๋ถ์ ์์ ํ ๊ฒฉ๋ฆฌ๋ ์ฌ๋ผ๋ง ์์ง ํธ์ถ
# Modelfile์ ๋ฐ์๋ repetition_penalty๋ system ์ ํ์ด ์ด ์์์ ๊ทธ๋๋ก ๋ฐ๋ํฉ๋๋ค.
llm = Ollama(
base_url="http://localhost:11434",
model="my_secure_ai"
)
# 2. ์ง๋ฌธ ๋์ง๊ธฐ (๋์ค์ ์ฌ๊ธฐ์ ๋ด ๋น๋ฐ ๋ฌธ์ ํ
์คํธ์ธ RAG ๋ฐ์ดํฐ๊ฐ ์ฎ์ด๊ฒ ๋ฉ๋๋ค)
question = "์ปดํจํฐ ๊ณต๋ถ๋ ์ด๋ป๊ฒ ํด์ผ ํด?"
print(f"์ง๋ฌธ: {question}")
# 3. ๋๊ธฐ์
์ ์ฌ์ ๊ฒ์ด์ ๋น์๋ ์๋ฒฝํ ์ฒ ๋ฒฝ ๋ฐฉ์ด ๋ต๋ณ ๋ฑ๊ธฐ
answer = llm.invoke(question)
print(f"AI์ ๋ต๋ณ: {answer}")
```
๐ก ์ง์ ๋์ ๋ถ์ ์กฐ์ฌ์ ์ต์ข
์ ์
๊ฒฐ๊ตญ ์กฑ๋ณด๊ฐ ์ด๋ ๊ฒ ์๋ฒฝํ๊ฒ ๋ฑ ์ ๋ฆฌ๊ฐ ๋๋ ๊ฒ์
๋๋ค.
1. ๋น๋: ๊ตฌ๊ธ ์ปดํจํฐ ์ฑ๋ฅ(์ฝ๋ฉ)์ ํ์ณ์ ํ์ธํ๋์ ๊ตฌ์ด ๋ค, ๋ด ํ๋๋ก ๋ ์กฐ๊ฐ๋ง ์ธํํด์ ๋ค์ด๋ก๋.
2. ํจํค์ง: ์ฌ๋ผ๋ง์ ๋์ปค ๋บจ์น๋ ๋
์ ๊ท๊ฒฉ์ธ Modelfile์ ๋ฒ ์ด์ค ๋ชจ๋ธ, ๋ด ๋ ์กฐ๊ฐ, ์์ธ ์ฒ๋ฆฌ ํ๋ผ๋ฏธํฐ, ์์คํ
ํ๋กฌํํธ ์ ํ(๊ฒฐ๊ณ)์ ํ ํต์ผ๋ก ๋ฌถ์ด my_secure_ai๋ก ๋น๋.
3. ํ์ด์ฌ ์ฐ๋: ๋ด ์์ฌ PC ๋ด๋ถ์์ ์ธ๋ถ ์ธํฐ๋ท ์ ์ ์์ ํ ๋ฝ์๋ฒ๋ฆฐ ์ฑ, ์์ ํ์ด์ฌ ์ฝ๋ ๋จ ๋ช ์ค๋ก ๊ทธ ๋น๋๋ ์ด๋ฆ์ ํธ์ถํด ๋ญ์ฒด์ธ๊ณผ 1:1๋ก ๊ฒฐํฉํ์ฌ ๋ฌด์ ํ ๋น๋ฐ ์ฝ๋ฉ ๊ฐ๋.
---
์ฌ๋ผ๋ง(Ollama)๋ผ๋ ๊ฑฐ๋ํ ํ๋ก๊ทธ๋จ ๊ป๋ฐ๊ธฐ๋ง์ ์ฌ์น๋ผ๊ณ ๋๋ผ๊ณ , "ํ์ด์ฌ(๋ญ์ฒด์ธ) ์ฝ๋ ์์์ llama.cpp ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ค์ด๋ ํธ๋ก ํธ์ถํด ๋ด ์ปดํจํฐ ๋ฉ๋ชจ๋ฆฌ์ AI ๋(๋ชจ๋ธ)๋ฅผ ์ง์ ์ฌ๋ ค์ ์คํํ๊ฒ ๋ค"๋ผ๋ ์ต์์ ๋ก์ปฌ ์์ง๋์ด๋ง ๋จ๊ณ์ ๋๋ฌํ์ ๊ฒ์
๋๋ค.
์ฌ๋ผ๋ง๋ฅผ ๊ฑฐ์น์ง ์๊ณ , ๋ญ์ฒด์ธ๊ณผ llama.cpp(ํ์ด์ฌ ๋ฐ์ธ๋ฉ ๋ฒ์ )๋ง 1:1๋ก ๊ฒฐํฉํ์ฌ ์์คํ
ํ๋กฌํํธ ์ ํ(๊ฒฐ๊ณ)๊ณผ ์์ธ ์ฒ๋ฆฌ๋ฅผ ํ์ด์ฌ ์ฝ๋ ๋จ ํ ์ฅ์ผ๋ก ํต์ ํ๋ ์ค์ ๊ตฌ์กฐ๋ฅผ ๋ช
ํํ๊ฒ ๋๋ นํด ๋๋ฆฝ๋๋ค.
---
๐ ️ 1๋จ๊ณ: ์ค๋น๋ฌผ (์ฌ๋ผ๋ง๊ฐ ํ์ ์๋ ์ฒญ์ ์ฌ๋ฃ)
* .gguf ํฌ๋งท ๋ชจ๋ธ ํ์ผ: ์ฌ๋ผ๋ง๋ ์๊ธฐ๋ค๋ง์ ํฌ๋งท์ผ๋ก ๋ชจ๋ธ์ ์จ๊ฒจ๋์ง๋ง, ์คํ์์ค ์ง์์ ํ์ค์ GGUF ํ์ผ์
๋๋ค. ๊ตฌ๊ธ ์ฝ๋ฉ์์ ํ์ธํ๋ํด์ ๋ค์ด๋ก๋๋ฐ์ ๊ฐ์ค์น๋ฅผ GGUF ํ์ผ(my_dog_model.gguf)๋ก ๋ณํํด ๋ด ์์ฌ PC ํ๋๋์คํฌ์ ์ ์ฅํด ๋ก๋๋ค.
* ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ: ํ์ด์ฌ์์ C++ ์์ง์ ์ง์ ๋ถ๋ ค ๋จน๊ธฐ ์ํด ํฐ๋ฏธ๋์ ๋ฑ ๋ ์ค๋ง ์ค์นํฉ๋๋ค.
```bash
pip install llama-cpp-python
pip install langchain-community
```
---
๐ 2๋จ๊ณ: ๋ญ์ฒด์ธ + llama.cpp ๋ค์ด๋ ํธ ๊ฐ๋ ์ค์ ์์ค์ฝ๋ (Python)์ด ์ฝ๋๋ ์ฌ๋ผ๋ง ๊ฐ์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์๋น์ค ์๋ฒ๋ฅผ ์ ํ ์ผ์ง ์์ต๋๋ค. ํ์ด์ฌ ์ฝ๋๊ฐ ์คํ๋๋ ์๊ฐ C++ ์์ง์ด ๋ด ์ปดํจํฐ ์์(CPU/GPU)์ ์ง์ ์ฅ์
ํ์ฌ ์ฐ์ฐํ๋ ๊ตฌ์กฐ์
๋๋ค.
```python
from langchain_community.llms import LlamaCpp
from langchain_core.prompts import PromptTemplate
# 1. ๋ ์์ ์ด ๋๋ GGUF ๋ชจ๋ธ ํ์ผ์ ํ์ด์ฌ ์ฝ๋๋ก ์ง์ ๋ก๋
# ์ฌ๋ผ๋ง Modelfile์ ์ ์๋ ์์ธ ์ฒ๋ฆฌ ํ๋ผ๋ฏธํฐ ์ ํ์ ์ฌ๊ธฐ์ ๋ค์ด๋ ํธ๋ก ๋ฐ์๋ก๋๋ค.
llm = LlamaCpp(
model_path="./my_dog_model.gguf", # ๊ตฌ๊ธ์์ ๋จนํํด์ ๋ณํํด ์จ ๋ด ๋ ํ์ผ ๊ฒฝ๋ก
temperature=0.0, # [์์ธ์ฒ๋ฆฌ 1] ์ฐฝ์์ฑ 0์ผ๋ก ๋ฌถ์ด ํ์๋ฆฌ ๋ฐฉ์ง
repetition_penalty=1.2, # [์์ธ์ฒ๋ฆฌ 2] ๋๊ฐ์ ๋ง ๋ฐ๋ณต(ํ๋ฆฌ์ง)ํ๋ฉด ๊ฐ์ ํญํ
max_tokens=100, # [์์ธ์ฒ๋ฆฌ 3] ๋๋ต์ด ๊ธธ์ด์ ธ๋ 50์ ๋ด์ธ๋ก ๊ฐ์ ์ธ์
์ปท
n_ctx=2048, # ๋ฌธ๋งฅ ๊ธธ์ด ์ ํ
n_gpu_layers=-1 # ๋ด ๋ฆฌ๋
์ค ๊ทธ๋ํฝ ์นด๋(VRAM) ์์ 100% ์ง์ ํ ๋น (-1์ ์ฌ์ธ)
)
# 2. ์ฌ๋ผ๋ง Modelfile์ SYSTEM ์ญํ ์ ๋์ ํ ๊ฐ๋ ฅํ ๋ญ์ฒด์ธ ๊ฒฐ๊ณ(ํ
ํ๋ฆฟ) ์ค์
# ๋ํ ๊ท์น์ด ๊ผฌ์ฌ์ ๋ฏธ์น ์์ค๊ฐ๋ก ๋๋ณํ๋ ๊ฒ์ ํ์ด์ฌ ๋ ๋ฒจ์์ ์์ฒ ์ฐจ๋จํฉ๋๋ค.
template = """[SYSTEM: ๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค.
์ง์ ๋ ๊ฐ์์ง ์ญํ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ์์ ํ ๋ฏ์ ์ง๋ฌธ์ ๋ฐ์ผ๋ฉด, ์์ค์ ์ฐ๊ฑฐ๋ ํ๋ฉด์ด ๋ฉ์ถ์ง ๋ง๊ณ ,
"๊ทธ๊ฑด ๊ท์ฌ์ด ๊ฐ์์ง๊ฐ ์ ์ ์๋ ๋ณต์กํ ์์ญ์ด๋ค ๋ฉ๋ฉ!"์ด๋ผ๊ณ ๋ง ์ ์งํ๊ฒ ๋ต๋ณํ๊ณ ๋ํ๋ฅผ ๋๋ด์ธ์.]
[USER: {question}]
[ASSISTANT: """
prompt = PromptTemplate.from_template(template)
# 3. ๋ญ์ฒด์ธ์ ํต์ฌ: ๊ฒฐ๊ณ(Prompt)์ C++ ์์ง(LLM)์ 1:1 ํ์ดํ๋ผ์ธ์ผ๋ก ์ฒด์ธ ๊ฒฐํฉ
chain = prompt | llm
# =========================================================================
# 4. ๐ฅ [# ๐ ์ง๋ฌธ์ ๋ฐ๊พธ์ด ๋ณด์ธ์! ์ค์ ํ์ง ๊ฒ์ฆ ํ
์คํธ]
# =========================================================================
unknown_question = "์ปดํจํฐ ๊ณต๋ถ๋ ์ด๋ป๊ฒ ํด์ผ ํด?"
print(f"์ง๋ฌธ ์ฐ๋ฌ๋ฃ๊ธฐ: {unknown_question}\n")
# ์ธ๋ถ ์ธํฐ๋ท์ด๋ ์ฌ๋ผ๋ง ์๋ฒ๋ฅผ ๊ฑฐ์น์ง ์๊ณ ๋ด ๋ฉ๋ชจ๋ฆฌ ์์์ C++ ์์ง์ด ์ฆ์ ์ถ๋ก ์คํ
answer = chain.invoke({"question": unknown_question})
print(f"์ต์ข
์ฒ ๋ฒฝ ๋ฐฉ์ด ๋ต๋ณ:\n{answer}")
# ์ถ๋ ฅ ๊ฒฐ๊ณผ: "๊ทธ๊ฑด ๊ท์ฌ์ด ๊ฐ์์ง๊ฐ ์ ์ ์๋ ๋ณต์กํ ์์ญ์ด๋ค ๋ฉ๋ฉ!"
```
---
๐ก ์ ์ด ๋ฐฉ์์ด '์ํคํ
์ฒ์ ์ต์ข
์งํํ'์ผ๊น์?
( ์ง๋ฌธ์๋์ด ์ฌ๋ผ๋ง๋ฅผ ๋ฒ๋ฆฌ๊ณ LlamaCpp ์งํต ๋ญ์ฒด์ธ ๊ตฌ์กฐ๋ฅผ ํํ์ ์๊ฐ, ์ธํ๋ผ์ ๊ฒฉ์ด ์์ ํ ๋ฌ๋ผ์ง๋๋ค.
์๋ฒฝํ ์ ๋ก ํธ๋ฌ์คํธ(Zero Trust): ์ฌ๋ผ๋ง์กฐ์ฐจ๋ ๋ด๋ถ์ ์ผ๋ก ์ด๋ค ๋ก๊ทธ๋ฅผ ์์งํ๋์ง ์ฐ์ฐํ ์ ์์ฃ . ํ์ง๋ง ์ด ๋ฐฉ์์ ์คํ์์ค C++ ๋ฐ์ด๋๋ฆฌ์ ์ง๋ฌธ์๋์ ํ์ด์ฌ ์ฝ๋ ๋จ ํ ์ฅ์ผ๋ก๋ง ๋์๊ฐ๋ฏ๋ก, ๋ฐฐํ์์ ๋ด ๋ฐ์ดํฐ๋ฅผ ์ผ๊ธ์ผ๊ธ ํ์ณ ๊ฐ๊ฑฐ๋ ์ถ์ ํ ํต๋ก๊ฐ ๋ฌผ๋ฆฌ์ ์ผ๋ก ์กด์ฌํ์ง ์๋ ์๋ฒฝํ ์๋ง ์์๊ฐ ๋ฉ๋๋ค.
* ๊ทน๋ํ๋ ๊ฐ์ฑ๋น: ์ฌ๋ผ๋ง๋ผ๋ ๋ฌด๊ฑฐ์ด ํ๋ก๊ทธ๋จ์ด ์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ก์๋จน๊ณ ์์ ํ์๊ฐ ์์ต๋๋ค. ๋ด๊ฐ ํ์ํ ๋ ์ด ํ์ด์ฌ ์คํฌ๋ฆฝํธ๋ง ์คํํ๋ฉด, C++ ์์ง์ด ๊ทธ๋ํฝ ์นด๋๋ฅผ 0.1์ด ๋ง์ ์ง์ ๊นจ์ ์ฐ์ฐํ๊ณ ์์
์ด ๋๋๋ฉด ํ์ ๋ ์์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ฐ๋ฉํ๊ณ ์ข
๋ฃ๋ฉ๋๋ค.
---
๐ ๋ฆฌ๋
์ค ํ์ด์๋ ์ธํ๋ผ์ ๋ํ์, vLLM์ ํ์ด์ฌ ์ฝ๋๋ก ์ง์ ๊ฐ๋ํ๋ ๋ฐฉ๋ฒ์
๋๋ค!์ง๋ฌธ์๋, ๊ฒฐ๊ตญ ๊ธฐ์
๋ค์ด ๋๊ท๋ชจ ์๋น์ค๋ฅผ ๊ตฌ์ถํ ๋ ์ฐ๋ ํ์กด ์ต๊ณ ์ ์ด๊ณ ์ ์ถ๋ก ์์ง์ธ vLLM(Virtual Large Language Model)๊น์ง ๋๋ฌํ์
จ๊ตฐ์! llama.cpp๊ฐ ์ ์ฌ์ ํ๊ฒฝ์ด๋ CPU ๊ฐ์ฑ๋น์ ์ต๊ฐ์๋ผ๋ฉด, vLLM์ ์๋น๋์ ๊ทธ๋ํฝ ์นด๋(GPU)์ VRAM์ ํ์ด์ง ์ฒ๋ฆฌํ๋ฏ 1:1๋ก ์ชผ๊ฐ์ ์ฐ๋ 'PagedAttention' ๊ธฐ์ ์ ๊ฐ์ง ๊ดด๋ฌผ ์์ง์
๋๋ค.
vLLM ์ญ์ ์ฌ๋ผ๋ง ๊ฐ์ ์ธ์ฅ ํ๋ก๊ทธ๋จ์ ์คํํ์ง ์๊ณ , ์์ ํ์ด์ฌ ์ฝ๋ ์์์ ํ์ดํ๋ผ์ธ ์์ง์ ์ง์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ ค ๋ถ๋ ค ๋จน๋ ๋ฐฉ์(LLM class)์ ์๋ฒฝํ๊ฒ ์ง์ํฉ๋๋ค.
๋ญ์ฒด์ธ(LangChain)๊ณผ vLLM์ ํ์ด์ฌ ์ฝ๋๋ก ๋ค์ด๋ ํธ 1:1 ๊ฒฐํฉํ์ฌ ์์คํ
ํ๋กฌํํธ ์ ํ(๊ฒฐ๊ณ)๊ณผ ์์ธ ์ฒ๋ฆฌ๋ฅผ ํต์ ํ๋ ์ค์ ์ธํ๋ผ ๊ตฌ์กฐ๋ฅผ ๋ช
ํํ๊ฒ ๋๋ นํด ๋๋ฆฝ๋๋ค.
---
๐ ️ 1๋จ๊ณ: ์ค๋น๋ฌผ (๋ฆฌ๋
์ค GPU ์ ์ฉ ์ฒญ์ ์ฌ๋ฃ)
* ํฌ๋งท ๊ฐ์ค์น ํ์ผ: vLLM์ llama.cpp๊ฐ ์ฐ๋ GGUF ํ์ผ์ด ์๋๋ผ, ๊ตฌ๊ธ ์ฝ๋ฉ์์ ํ์ธํ๋ํ๊ณ ๋์จ ์์ ํ๊น
ํ์ด์ค ํฌ๋งท(์์ .safetensors ํ์ผ๋ค์ด ๋ค์ด์๋ ํด๋)์ ๊ทธ๋๋ก ๋ด ํ๋๋์คํฌ์ ๋ค๊ณ ์์ ์๋๋ค.
* ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ: ๋ฆฌ๋
์ค ํ๊ฒฝ ํฐ๋ฏธ๋์์ ์๋ ๋ช
๋ น์ด๋ก ์ค์นํฉ๋๋ค. (vLLM์ ๋ฆฌ๋
์ค ์ ์ฉ์ด๋ฉฐ, CUDA ํ๊ฒฝ์ด ์๋ฒฝํ๊ฒ ์ธํ
๋์ด ์์ด์ผ ๋ ์๋ค๋๋๋ค.)
```bash
pip install vllm
pip install langchain-community
```
๐ 2๋จ๊ณ: ๋ญ์ฒด์ธ + vLLM ๋ค์ด๋ ํธ ๊ฐ๋ ์ค์ ์์ค์ฝ๋ (Python)์ด ์ฝ๋๋ ์ธ๋ถ ์๋ฒ ํ๋ก๊ทธ๋จ๊ณผ ํต์ ํ์ง ์๊ณ , ํ์ด์ฌ ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋๋ ์๊ฐ vLLM C++/CUDA ์ปค๋ ์์ง์ด ๋ด ๊ทธ๋ํฝ ์นด๋๋ฅผ ์ง์ ์ฅ์
ํ์ฌ ๋ฉ๋ชจ๋ฆฌ(VRAM)๋ฅผ ๊ทน๋๋ก ์ฅ์ด์ง ์ถ๋ก ํ๋ 100% ๋ก์ปฌ ์๋ง ๊ตฌ์กฐ์
๋๋ค.
```python
from langchain_community.llms import VLLM
from langchain_core.prompts import PromptTemplate
# 1. ๊ตฌ๊ธ ์ฝ๋ฉ์์ ๋จนํํด์ ์ ์ฅํด ๋ ๋ด ํ์ธํ๋ ๊ฐ์ค์น ํด๋ ๊ฒฝ๋ก ์ง์
# ์ฌ๋ผ๋ง Modelfile์ ์ ์๋ ์์ธ ์ฒ๋ฆฌ ํ๋ผ๋ฏธํฐ ์ ํ์ vLLM ์์ง ์ต์
์ผ๋ก ๋ค์ด๋ ํธ๋ก ๋ฐ์๋ก๋๋ค.
llm = VLLM(
model="./my_fine_tuned_model_folder", # ๋ด safetensors ๊ฐ์ค์น ํด๋ ๊ฒฝ๋ก
trust_remote_code=True,
temperature=0.0, # [์์ธ์ฒ๋ฆฌ 1] ์ฐฝ์์ฑ 0์ผ๋ก ๋ฌถ์ด ํ์๋ฆฌ ๋ฐฉ์ง
max_new_tokens=100, # [์์ธ์ฒ๋ฆฌ 2] ๋๋ต์ด ๊ธธ์ด์ ธ๋ 50์ ๋ด์ธ๋ก ๊ฐ์ ์ธ์
์ปท
# vLLM์ ํต์ฌ: ๊ทธ๋ํฝ ์นด๋ VRAM์ 90%๋ฅผ ํ์ด์ง ์ฐ์ฐ์ฉ์ผ๋ก ์ฆ์ 1:1 ๋ฝ(Lock) ์์
gpu_memory_utilization=0.9,
dtype="float16" # ๋ด GPU ์ฌ์์ ๋ง๊ฒ ๋ฐ์ ๋ฐ๋ ์ฐ์ฐ ์ธํ
)
# 2. ๋งน๋ชฉ์ ์ผ๋ก ๋ณต์ข
ํ ๊ฐ๋ ฅํ ๋ญ์ฒด์ธ ๊ฒฐ๊ณ(์์คํ
ํ๋กฌํํธ ํ
ํ๋ฆฟ) ์ค์
# ๋ํ ๊ท์น์ ๋ฒ์ด๋ฌ์ ๋ ๋ฌดํ ๋ฃจํ ํญ์ฃผ๋ ๋ฏธ์น ์์ค๊ฐ๋ก ๋๋ณํ๋ ๊ฒ์ ์์ฒ ์ฐจ๋จํฉ๋๋ค.
template = """[SYSTEM: ๋น์ ์ ํญ์ ๋ฌธ์ฅ ๋์ '๋ฉ๋ฉ!'์ ๋ถ์ด๋ ๊ท์ฌ์ด ๊ฐ์์ง ์ฑ๋ด์
๋๋ค.
์ง์ ๋ ๊ฐ์์ง ์ญํ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ์์ ํ ๋ฏ์ ์ง๋ฌธ์ ๋ฐ์ผ๋ฉด, ์์ค์ ์ฐ๊ฑฐ๋ ํ๋ฉด์ด ๋ฉ์ถ์ง ๋ง๊ณ ,
"๊ทธ๊ฑด ๊ท์ฌ์ด ๊ฐ์์ง๊ฐ ์ ์ ์๋ ๋ณต์กํ ์์ญ์ด๋ค ๋ฉ๋ฉ!"์ด๋ผ๊ณ ๋ง ์ ์งํ๊ฒ ๋ต๋ณํ๊ณ ๋ํ๋ฅผ ๋๋ด์ธ์.]
[USER: {question}]
[ASSISTANT: """
prompt = PromptTemplate.from_template(template)
# 3. ๋ญ์ฒด์ธ์ ํ์ดํ๋ผ์ธ ๊ธฐ๋ฅ์ ์ด์ฉํด ๊ฒฐ๊ณ(Prompt)์ vLLM ์์ง์ 1:1 ์ฒด์ธ ๊ฒฐํฉ
chain = prompt | llm
# =========================================================================
# 4. ๐ฅ [# ๐ ์ง๋ฌธ์ ๋ฐ๊พธ์ด ๋ณด์ธ์! ์ค์ ํ์ง ๊ฒ์ฆ ํ
์คํธ]
# =========================================================================
unknown_question = "์ปดํจํฐ ๊ณต๋ถ๋ ์ด๋ป๊ฒ ํด์ผ ํด?"
print(f"์ง๋ฌธ ์ฐ๋ฌ๋ฃ๊ธฐ: {unknown_question}\n")
# ์ฌ๋ผ๋ง๋ ์ธ๋ถ API๋ฅผ ๊ฑฐ์น์ง ์๊ณ , ํ์ด์ฌ ๋ด๋ถ CUDA ์ปค๋์์ ๋น์ ์๋๋ก ์ถ๋ก ์คํ
answer = chain.invoke({"question": unknown_question})
print(f"์ต์ข
์ฒ ๋ฒฝ ๋ฐฉ์ด ๋ต๋ณ:\n{answer}")
# ์ถ๋ ฅ ๊ฒฐ๊ณผ: "๊ทธ๊ฑด ๊ท์ฌ์ด ๊ฐ์์ง๊ฐ ์ ์ ์๋ ๋ณต์กํ ์์ญ์ด๋ค ๋ฉ๋ฉ!"
```
---
๐ก llama.cpp vs vLLM ์ฝ๋ฉ ์งํต ์์ง์ ๊ฒฐ์ ์ ์ฐจ์ด
์ง๋ฌธ์๋์ด ๋๋ฌํ์ ์ด ๋ ๋ก์ปฌ ํ์ด์ฌ ์์ง์ ์ฑ๊ฒฉ์ด ์๋ฒฝํ๊ฒ ๋ค๋ฆ
๋๋ค.
* llama.cpp: ๊ฐ๋ณ๊ณ , ์ ๊ธฐ๋ฅผ ์ ๊ฒ ๋จน์ผ๋ฉฐ, CPU์์๋ ์ ๋๋๋ค. ์์ฌ PC์์ ๋งฅ์ฃผ ํ์ ๋ง์๋ฉฐ ์กฐ์ฉํ๊ฒ ๋๋ง์ ๋น๋ฐ ์ฝ๋ฉ ๋น์๋ก ์๊ฐ ๋์ฐ๊ธฐ์ฉ ์ทจ๋ฏธ๋ฅผ ์ฆ๊ธธ ๋ ์ต๊ณ ์ ๊ฐ์ฑ๋น๋ฅผ ๋ณด์ฌ์ค๋๋ค.
* vLLM: ์ ๊ธฐ๋ ์์ฒญ๋๊ฒ ๋จน๊ณ ๋ด GPU ํฌ์ ํ๊ฐ๋(๋ฐ์ด ์ฌํจ)์ํค์ง๋ง, ์๋๊ฐ llama.cpp๋ณด๋ค ์ ๋ฐฐ์์ ์์ญ ๋ฐฐ ๋น ๋ฆ
๋๋ค. ์ง๋ฌธ์๋์ด ๋์ค์ ์ง์ง๋ก 15์๊ฐ ์ ํ ์นด๊ธ์ ๋น์์ผ๋ฉฐ ์์ฉ ์๋น์ค ์์ค์ ํธ๋ํฝ์ ์ฒ๋ฆฌํ๋ 24์๊ฐ ๊ฐ์ธ ์ธํ๋ผ ์๋ฒ๋ฅผ ๊ตฌ์ถํ ๋ ์ฐ๋ ๊น๋์ข
๊ธฐ์ ์
๋๋ค.
---
๊ตฌ๊ธ ์ฝ๋ฉ์์ ํ์ณ ์จ ๊ทธ ์๋ฐฑ ๋ฉ๊ฐ์ง๋ฆฌ ๋ก๋ผ ํ์ผ(adapter_model.safetensors)์ ๋ด ๋ฆฌ๋
์ค ์ปดํจํฐ ํ๋๋์คํฌ์ ๋๋์ฒด ์ด๋ ํด๋์ ์ค์
๋ฃ์ด์ผ ์์ง๋ค์ด ์์๋จน๋์ง, ๋ ์์ง์ ์ ํํ ์์น๋ฅผ ์ฐ์ด๋๋ฆฝ๋๋ค.
---
1. llama.cpp ์ง์ ➡️ "๋ด ๋ง์๋๋ก ์๋ฌด ํด๋๋"
llama.cpp๋ ์์ฃผ ์ฟจํ C++ ์์ง์ด๋ผ ๊ณ ์ ๋ ํด๋๊ฐ ์์ต๋๋ค. ์ง๋ฌธ์๋์ด ํ์ด์ฌ ์ฝ๋๋ฅผ ์คํํ ์์
๋๋ ํ ๋ฆฌ(ํ๋ก์ ํธ ํด๋) ์์ ๊ฐ์ด ๋ฃ์ด๋๋ฉด ๋์
๋๋ค.
์ค์ ๋ฌผ๋ฆฌ์ ๋ฐฐ์น ์์:
```bash
/home/์ง๋ฌธ์๋/my_ai_project/
├── run_ai.py # ๋ด ๋ญ์ฒด์ธ ํ์ด์ฌ ์ฝ๋ ํ์ผ
├── qwen_base_model.gguf # ์์ญ ๊ธฐ๊ฐ์ง๋ฆฌ ์๋ ๋ ํ์ผ
└── my_dog_lora.safetensors # ๐ ๊ตฌ๊ธ์์ ๋จนํํด์จ ๋ก๋ผ ํ์ผ ์์น!
```
* ํ์ด์ฌ ์ฝ๋์์ ํธ์ถํ ๋:
๊ทธ๋ฅ ํ์ผ ์ด๋ฆ์ด๋ ์๋ ๊ฒฝ๋ก(lora_path="./my_dog_lora.safetensors")๋ฅผ ํ
์คํธ๋ก ์ ์ด์ 1:1๋ก ์ฐ๋ฌ ๋ฃ์ด์ฃผ๋ฉด C++ ์์ง์ด ์์์ ๊ทธ ์๋ฆฌ์ ์๋ ํ์ผ์ ์ฝ์ด๊ฐ๋๋ค.
---
2. vLLM ์ง์ ➡️ "ํ๊น
ํ์ด์ค ๊ณ ์ ํด๋ ๊ตฌ์กฐ ํต์งธ๋ก"
vLLM์ ๋๊ธฐ์
์๋ฒ์ฉ ๊ดด๋ฌผ ์์ง์ด๋ผ ๊ท๊ฒฉ์ด ์์ฃผ ๊น๊นํฉ๋๋ค. ๋ก๋ผ ํ์ผ ๋ฌ๋ ํ๋๋ง ์ฃผ๋ฉด ์ธ์์ ๋ชป ํ๊ณ , ๊ตฌ๊ธ ์ฝ๋ฉ์์ ๊ฐ์ค์น๋ฅผ ์ถ์ถํ ๋ ๋ง๋ค์ด์ง ํด๋ ์ ์ฒด(์ค์ ํ์ผ JSON ํฌํจ)๋ฅผ ํต์งธ๋ก ๋ค๊ณ ์์ผ ํฉ๋๋ค.
* ์ค์ ๋ฌผ๋ฆฌ์ ๋ฐฐ์น ์์:
ํ๋ก์ ํธ ํด๋ ์์ my_lora_folder๋ผ๋ ๋ฐฉ์ ํ๋ ํ๊ณ ๊ทธ ์์ ๋ค ์ค์
๋ฃ์ต๋๋ค.
```bash
/home/์ง๋ฌธ์๋/my_vllm_project/
├── run_vllm.py # ๋ด ๋ญ์ฒด์ธ vLLM ํ์ด์ฌ ์ฝ๋
└── my_lora_folder/ # ๐ ์ด ํด๋ ํต์งธ๊ฐ ๋ก๋ผ ์์น!
├── adapter_config.json # ๋ก๋ผ์ ์กฑ๋ณด(์ค์ ) ํ์ผ
└── adapter_model.safetensors # ์ง์ง ๋ก๋ผ ์๋งน์ด ํ์ผ
```
ํ์ด์ฌ ์ฝ๋์์ ํธ์ถํ ๋:ํ์ผ์ด ์๋๋ผ ๊ทธ ํด๋ ๊ฒฝ๋ก(lora_request = LoRARequest("dog", 1, "./my_lora_folder"))๋ฅผ vLLM ์์ง์๊ฒ ๋๊ฒจ์ฃผ๋ฉด, ์์ง์ด ํด๋ ์์ JSON์ ๋จผ์ ์ฝ์ด์ ๋์ธํฌ ๊ท๊ฒฉ์ ํ์ธํ ๋ค ๋ก๋ผ๋ฅผ 1:1๋ก ๊ฒฐํฉํฉ๋๋ค.
๐ก ๋ด๋ฐฑํ ์ต์ข
์์ฝ
* llama.cpp: ๋ณต์กํ ๊ฑฐ ์ซ์ดํ๋ ๋ด ํ์ด์ฌ ์ฝ๋๋ ๊ฐ์ ๋ฐฉ(ํด๋)์ ํ์ผ ํต์งธ๋ก ๋์ ธ๋๋ฉด ๋.
* vLLM: ๊น๊นํ ๋์ด๋ ์ฝ๋ฉ์ด ๋ฑ์ด์ค JSON ์ค์ ํ์ผ๋ค์ด ๋ค์ด์๋ ํด๋ ํต์งธ๋ก ๋ณต์ฌํด์ ์ง์ ๋ ๋ฐฉ์ ๋ชจ์
๋๋ฉด ๋.
๊ฒฐ๊ตญ ์ง๋ฌธ์๋์ ๋ฆฌ๋
์ค ํ ๋๋ ํ ๋ฆฌ(~) ์๋์ ๋๋ง์ ํ๋ก์ ํธ ํด๋๋ฅผ ํ๋ ๋ง๋์๊ณ , ๊ฑฐ๊ธฐ์ ๋ฒ ์ด์ค ๋ชจ๋ธ์ด๋ ๋ก๋ผ ํ์ผ์ ๋๋ํ ์
์ฃผ์์ผ ๋๋ ๊ฒ ๊ฐ์ฅ ์ง๊ด์ ์ด๊ณ ๊น๋ํ ์ ๋ต์
๋๋ค.
"๋๋์ฒด ์ด๋์ ๋ฃ๋๊ฐ?"๋ผ๋ ๋ฌผ๋ฆฌ์ ์์น ํ์ธ์ผ๋ก, ๋ง์นจ๋ด ๋ด ๋ฐฉ ์์ฌ PC ํ๋๋์คํฌ ์์ ํ์ผ ๋ฐฐ์น๊น์ง ์๋ฒฝํ๊ฒ ์์ฑ์ด ๋์์ต๋๋ค! ์ธํ๋ผ ์ง๋๊ฐ ์ง์ง๋ก ๋๋ฌ๋ค์.
---
๐จ ์์ฃผ ๊ฒฐ์ ์ ์ธ ์ฐจ์ด์ ์
๋๋ค! ์กฑ๋ณด๊ฐ ๊ผฌ์ด์ง ์๊ฒ ๋ช
ํํ๊ฒ ์ ๋ฆฌํ์
์ผ ํฉ๋๋ค.
๊ฒฐ๋ก ๋ถํฐ ๋ง์๋๋ฆฌ๋ฉด, my_dog_model.gguf ํ์ผ์ ๊ทธ ํด๋(my_fine_tuned_model_folder) ์์ ๋ค์ด์์ง ์์ต๋๋ค! ์ด GGUF ํ์ผ์ ๊ตฌ๊ธ ์ฝ๋ฉ์์ ๋์จ ๋ ๊ฒ์ ํด๋๋ฅผ ๋ด๊ฐ ํ ๋ฒ ๋ ์ธํ(๋ณํ)ํด์ ๋ฝ์๋ธ '๋ณ๊ฐ์ ์์ฑํ ํ์ผ'์
๋๋ค.
์ง๋ฌธ์๋์ด ํท๊ฐ๋ฆฌ์ค ์ ์๋ ์ด ๋ ๊ฐ์ง ๊ท๊ฒฉ์ ๋ฌผ๋ฆฌ์ ์ค์ฒด์ ์ต์ข
์์น๋ฅผ ๋ฑ 3๋จ๊ณ๋ก ๋ช
ํํ๊ฒ ์ชผ๊ฐ์ ์ ๋ฆฌํด ๋๋ฆฝ๋๋ค.
---
1. ๊ตฌ๊ธ ์ฝ๋ฉ์ด ์ฒ์ ๋ฑ์ด๋ธ ์งํ์ ์ํ (vLLM์ฉ)
trainer.train()์ด ๋๋๊ณ ์ฒ์ ์ ์ฅํ๋ฉด ์ค์ง my_fine_tuned_model_folder๋ผ๋ ํด๋๋ง ์๊น๋๋ค.
* ํด๋ ๋ด๋ถ: ์๊น ํํค์น adapter_model.safetensors(๋ ์กฐ๊ฐ)์ ์กฑ๋ณด JSON ํ์ผ๋ค๋ง ๋ค์ด์์ต๋๋ค.
* ์ฉ๋: ์ด ์ํ ๊ทธ๋๋ก ํต์งธ๋ก ๋ค๊ณ ์์ ํ์ด์ฌ ์ฝ๋๋ก ์คํํ๋ ์์ง์ด ๋ฐ๋ก vLLM์
๋๋ค. (vLLM์ GGUF๋ฅผ ์ฝ์ง ๋ชปํ๊ณ ์ด safetensors ํด๋ ๊ท๊ฒฉ๋ง ์ฝ์ต๋๋ค.)
2. my_dog_model.gguf ํ์ผ์ด ํ์ํ๋ ์๊ฐ (llama.cpp์ฉ)
์ง๋ฌธ์๋์ด ๋ฌด๊ฑฐ์ด vLLM ๋์ ๊ฐ๋ฒผ์ด llama.cpp ์์ง์ผ๋ก ๋ด ์์ฌ PC์์ ์กฐ์ฉํ ๋งฅ์ฃผ ๋ง์๋ฉฐ ๋๋ฆฌ๊ณ ์ถ์ ๋, ์ด GGUF ๋ณํ ๋จ๊ณ๊ฐ ์ถ๊ฐ๋ฉ๋๋ค.
* ๋ณํ ๊ณผ์ : ๊ตฌ๊ธ ์ฝ๋ฉ์ด๋ ๋ด ๋ฆฌ๋
์ค ํฐ๋ฏธ๋์์ llama.cpp๊ฐ ์ ๊ณตํ๋ ๋ณํ ์คํฌ๋ฆฝํธ(convert_hf_to_gguf.py)๋ฅผ ์คํํฉ๋๋ค.
* ๋ช
๋ น์ ์ค์ฒด: "์ด my_fine_tuned_model_folder ํด๋ ์์ ์๋ ์๋ ๋ฒ ์ด์ค ๋ชจ๋ธ ๋๋ ๋ด LoRA ๋ ์กฐ๊ฐ(safetensors), ๊ทธ๋ฆฌ๊ณ ํต์ญ์ฌ(JSON)๋ฅผ ์งํ์ฒ๋ผ 1:1๋ก ๊ฝ ์ฅ์ด์ง์ ํ๋์ ๋จ๋จํ ๋๋ฉ์ด๋ฆฌ ํ์ผ๋ก ํฉ์ฒด์์ผ๋ผ!"๋ผ๊ณ ์ฃผ๋ฌธํ๋ ๊ฒ์
๋๋ค.
* ๊ฒฐ๊ณผ: ๊ทธ๋ ๊ฒ ํด์ ํ์ํ๋ ์ต์ข
๋จ์ผ ํ์ผ์ด ๋ฐ๋ก my_dog_model.gguf์
๋๋ค.
3. ์ง๋ฌธ์๋ ์์ฌ PC ํ๋๋์คํฌ์ '์ต์ข
๋ฌผ๋ฆฌ์ ๋ฐฐ์น' ๋๋ฉด๊ฒฐ๊ตญ ์ง๋ฌธ์๋์ด ์ด๋ค ์์ง์ผ๋ก ๋
๋ฆฝ๊ตฐ ์์๋ฅผ ๊ฐ๋ํ๋๋์ ๋ฐ๋ผ ๋ด ํ๋๋์คํฌ์ ํ์ผ ์์น๋ ๋ฑ ์ด๋ ๊ฒ ๊ฒฐ์ ๋ฉ๋๋ค.
* ์ ํ A: vLLM ์ง์์ผ๋ก ๋๋ฆด ๋ (ํด๋ ํต์งธ๋ก)
```bash
/home/์ง๋ฌธ์๋/my_vllm_project/
├── run_vllm.py # ๋ด ๋ญ์ฒด์ธ ํ์ด์ฌ ์ฝ๋
└── my_fine_tuned_model_folder/ # ๐ ์ฝ๋ฉ์์ ๊ฐ์ ธ์จ ํด๋ ํต์งธ๋ก!
├── adapter_config.json
└── adapter_model.safetensors
```
* ์ ํ B: llama.cpp ์ง์์ผ๋ก ๋๋ฆด ๋ (๋จ์ผ ํ์ผ๋ก ํฉ์ฒด ๋จนํ)
```bash
/home/์ง๋ฌธ์๋/my_llamacpp_project/
├── run_ai.py # ๋ด ๋ญ์ฒด์ธ ํ์ด์ฌ ์ฝ๋
└── my_dog_model.gguf # ๐ ์ธํ·ํฉ์ฒด๊ฐ ๋๋ ๋จ ํ๋์ GGUF ํ์ผ ์์น!
```
(์ด๋๋ ๊ท์ฐฎ์ my_fine_tuned_model_folder ํด๋๋ ์ฉ๋๋ง ์ฐจ์งํ๋ ํ๋๋์คํฌ์์ ์์ํ ์ง์๋ฒ๋ ค๋ ์๋ฌด ์๊ด์ด ์์ต๋๋ค! GGUF ๋๋ฉ์ด๋ฆฌ ์์ ์๋งน์ด๊ฐ ๋ค ๋ค์ด๊ฐ์ผ๋๊น์.)
---
์ง๋ฌธ์๋, ์์ฃผ ํต์ฌ์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ง์ผ์
จ์ต๋๋ค! my_vllm_project ํด๋ ์์๋ GGUF ํ์ผ์ด ์ ๋ ์์ต๋๋ค.
vLLM์ ๋๊ธฐ์
์๋ฒ์ค ์ฒด๊ธ์ ์์ง์ด๋ผ, GGUF ๊ฐ์ '์์ถ๋ ๋๋ฉ์ด๋ฆฌ ๋จ์ผ ํ์ผ'์ ๊ฑฐ๋ค๋ ๋ณด์ง๋ ์์ต๋๋ค. ๋์ ๊ตฌ๊ธ ์ฝ๋ฉ์ด๋ ํ๊น
ํ์ด์ค์์ ๋ค์ด๋ก๋๋ฐ์ ๋ ๊ฒ ๊ทธ๋๋ก์ ํ์ด์ฌ/PyTorch ์๋ณธ ํด๋ ๊ตฌ์กฐ(my_fine_tuned_model_folder)๋ฅผ ํต์งธ๋ก ๋ฐฉ ์์ ์
์ฃผ์์ผ์ ๊ตด๋ฆฝ๋๋ค.
์ง๋ฌธ์๋์ ๋ฆฌ๋
์ค ์๋ฒ์์ ls -R ~/my_vllm_project๋ฅผ ์ณค์ ๋ ๋ณด๊ฒ ๋ ์ง์ง ๋ฌผ๋ฆฌ์ ์ธ ํ์ผ ๋ชฉ๋ก๊ณผ ์ญํ ์ ๋ฑ 3๊ฐ์ง๋ก ๊น๋ํ๊ฒ ํด๋ถํด ๋๋ฆฝ๋๋ค.
๐ my_vllm_project ๋ด๋ถ ์ง์ง ์ค์ฒด ๋๋ฉด
```bash
/home/์ง๋ฌธ์๋/my_vllm_project/
├── run_vllm.py # 1. ๋ด๊ฐ ์คํํ ๋ญ์ฒด์ธ ํ์ด์ฌ ์ฝ๋
└── my_fine_tuned_model_folder/ # 2. ๊ตฌ๊ธ์์ ๋จนํํด์จ ํ์ธํ๋ ํด๋
├── adapter_config.json # 3. ๋ ์กฐ๊ฐ์ ์กฑ๋ณด (์ค์ JSON)
├── adapter_model.safetensors # 4. ์ง์ง ๋์ธํฌ ์๋งน์ด (์์ญ ๊ฐ๋ก ์ชผ๊ฐ์ ธ ์์ ์๋ ์์)
├── tokenizer_config.json # 5. ๋ฌธ์๋ฅผ ์ซ์๋ก ๊ฐ์์ฃผ๋ ํต์ญ์ฌ ๊ฐ์ด๋
└── special_tokens_map.json # 6. ํ๋ฆฌ์ง์ ๋ง๋ ๋ฌธ์ฅ ๋ ๋์ฅ(EOS) ๋น๋ฐ ์ง๋
```
์ด ํ์ผ๋ค์ด ๋ฐฉ ์์์ ๊ตฌ์ฒด์ ์ผ๋ก ๋ฌด์จ ์ผ์ ํ๋์ง ๋ช
ํํ๊ฒ ์ ๋ฆฌํด ๋๋ฆฝ๋๋ค.
1. run_vllm.py (๋ด ๋
๋ฆฝ๊ตฐ ์ฌ์ค ์๋ฒ์ ์ค์์น)
์ง๋ฌธ์๋์ด ์์ ํํค์น์
จ๋ ๋ญ์ฒด์ธ(LangChain)๊ณผ vLLM ๊ฐ์ฒด๋ฅผ 1:1๋ก ๊ฒฐํฉํ๊ณ , ์์คํ
ํ๋กฌํํธ ์ ํ(๊ฒฐ๊ณ)์ ๊ฝ ๋ฌถ์ด๋ ์ง์ง ํ์ด์ฌ ์์ค์ฝ๋ ํ์ผ์
๋๋ค. ํฐ๋ฏธ๋์์ python run_vllm.py๋ฅผ ์น๋ ์๊ฐ ์ด ๋ฐฉ์ ์ ์ฒด ์ธํ๋ผ๊ฐ ๋์ ๋น๋๋ค.
2. adapter_model.safetensors (์ง์ง ๋์ธํฌ ๊ฐ์ค์น)
GGUF๋ ํ๋์ ํ์ผ๋ก ํฉ์ณ์ ธ ์์ง๋ง, vLLM์ด ์ฝ๋ ์ด ํด๋ ์์๋ .safetensors๋ผ๋ ํ์ฅ์๋ฅผ ๊ฐ์ง ์ํ ํ๋ ฌ ํ์ผ๋ค์ด ๋ค์ด์์ต๋๋ค.
* ๊ธฐ์ ์ ํฉํธ: ๊ตฌ๊ธ ์ฝ๋ฉ ๋ฌด๋ฃ GPU๋ฅผ ํธ์ด ํ์ต์ํจ ์ง๋ฌธ์๋๋ง์ ๊ฐ์์ง ๋งํฌ๋ ๊ทน๋น C์ธ์ด ์์ค์ฝ๋์ ์ง์ ํจํด์ด ์๋ณธ ๊ทธ๋๋ก ๋ฐํ์๋ ์ง์ง ๊ฐ์ค์น ์๋งน์ด์
๋๋ค. ๋ชจ๋ธ ์ฒด๊ธ์ ๋ฐ๋ผ model-00001-of-00004.safetensors ์ฒ๋ผ ์ฌ๋ฌ ๊ฐ๋ก ์ชผ๊ฐ์ ธ ์ ์ฅ๋๊ธฐ๋ ํฉ๋๋ค.
3. adapter_config.json & ํ ํฌ๋์ด์ ํ์ผ๋ค (์กฑ๋ณด์ ํต์ญ์ฌ)
vLLM์ด ๋ด ๊ทธ๋ํฝ ์นด๋(GPU)์ VRAM ๋ฉ๋ชจ๋ฆฌ๋ฅผ 90% ์์ (gpu_memory_utilization=0.9)ํ์ฌ ์ฐ์ฐ์ ์์ํ๊ธฐ ์ , ๊ฐ์ฅ ๋จผ์ ์ฝ๋ ๋ฉ๋ชจ์ฅ ํ์ผ๋ค์
๋๋ค.
* ์ด ํ์ผ๋ค์ ํตํด "์ด ๋ ์กฐ๊ฐ์ ์๋ Qwen2.5-Coder ๊ตฌ์กฐ์ ๋ง์ถ ๊ฑฐ๊ตฌ๋", "์ธ๊ฐ์ ๋ฌธ์ฅ์ ์ด๋ฐ ์ซ์๋ก ๊ฐ์์ GPU ์
๊ตฌ์ ๋ฐ์ด ๋ฃ์ด์ผ ํ๋๊ตฌ๋"๋ผ๋ ๊ท์น์ ๋ด๋ถ์ ์ผ๋ก ํ์
ํฉ๋๋ค.
๐ก ์ต์ข
๊ฒฐ๋ก : ์ GGUF๋ฅผ ์ ์ฐ๊ณ ๊ท์ฐฎ๊ฒ ์ด ํด๋ ๊ตฌ์กฐ๋ฅผ ์ ์งํ ๊น์?
* GGUF (llama.cpp ์ ์ฉ): ๋ด ์์ฌ PC์์ ์กฐ์ฉํ ์บ๋งฅ์ฃผ ๋ง์๋ฉฐ ํผ์ ๊ฐ๋ณ๊ฒ ์๊ฐ ๋์ฐ๊ธฐ์ฉ ์ทจ๋ฏธ๋ก ๋๋ฆด ๋ ์ฐ๋ '๋จ์ผ ์์ฝ' ์์ถ ํฌ๋งท.
* Safetensors ํด๋ (vLLM ์ ์ฉ): ์ ๊ธฐ๋ ๋ง์ด ๋จน๊ณ ๋ฐ์ด๋ ์ฌํ์ง๋ง, 15์๊ฐ ์ ํ ์บ๊ธ ์์ ์ ๋น์์ผ๋ฉฐ ๋ด ์๋น๋์ ๊ทธ๋ํฝ ์นด๋์ ์ฃผ์ฌ์จ๊ณผ ์ฑ๋ฅ์ 100% ํ๊ณ๊น์ง ์ฅ์ด์ง์ ๋น์ ์๋๋ก ๋๊ท๋ชจ ์ฝ๋ฉ ์ถ๋ก ์ ๋๋ ค๋ฒ๋ฆฌ๋ ๋๊ธฐ์
์๋ฒ์ค ์ฒด๊ธ์ ์๋ณธ ํฌ๋งท.
๊ฒฐ๊ตญ ์ง๋ฌธ์๋์ด ์ ์ด์ฃผ์ my_vllm_project ๋ฐฉ ์์๋ ๋ด๊ฐ ์คํํ ์ฝ๋ ํ์ผ ํ๋์, ๊ตฌ๊ธ์์ ๋จนํํด์จ ๊ฐ์ค์น ํด๋ ํ๋๊ฐ ์ธํธ๋ก ๋๋ํ ๋๊ฑฐํ๊ณ ์๋ ํ๊ฒฝ์ด ์ ๋ต์ด์์ต๋๋ค! GGUF ์์ด๋ ์๋ฒฝํ๊ฒ ์๊ธ์์กฑ ๋
๋ฆฝ ์์๊ฐ ๋์๊ฐ๋ ๊ตฌ์กฐ์ฃ .
---
๊ตฌ๊ธ ์ฝ๋ฉ(Colab)์์ FastLanguageModel.from_pretrained() ์ฝ๋๋ฅผ ์คํํ๋ ์๊ฐ, ๊ตฌ๊ธ ์๋ฒ๋ ํ๊น
ํ์ด์ค(Hugging Face) ์ ์ฅ์๋ก๋ถํฐ ์ค๋ฆฌ์ง๋ AI ๋ชจ๋ธ์ '๋ ๋ณธ์ฒด'์ 'ํต์ญ ๊ฐ์ด๋'๋ฅผ ํฌํจํ ๋ ๊ฒ์ ์๋ณธ ํ์ผ ์ธํธ๋ฅผ ๋ค์ด๋ก๋ํฉ๋๋ค [shopping, stem-calculative-problem-solving].
์ง๋ฌธ์๋์ด ์ค๊ณํ์ ๋น ๊นกํต(๋ฒ ์ด์ค ๋ชจ๋ธ)์ ๋ด ์ธ๊ณ๊ด์ ์ฃผ์
ํ๊ธฐ ์ํด, ๊ตฌ๊ธ ์ปดํจํฐ๊ฐ ๋ค์์ ๋ชฐ๋ ๋ฐ์์ค๋ ๊ตฌ์ฒด์ ์ธ ํ์ผ ๋ชฉ๋ก 3๊ฐ์ง๋ฅผ ๋ช
ํํ๊ฒ ์ ๋ฆฌํด ๋๋ฆฝ๋๋ค.
---
1. ๐ง ์๋ณธ ๋์ธํฌ ํ์ผ (๊ฐ์ค์น ๋ฐ์ดํฐ ๋ณธ์ฒด)
* model-00001-of-00004.safetensors (์ฒด๊ธ์ ๋ฐ๋ผ ์ฌ๋ฌ ๊ฐ๋ก ์ชผ๊ฐ์ ธ ์์)
* ์ญํ : ์์ง ์ง๋ฌธ์๋์ ๊ฐ์์ง ๋งํฌ๋ ๊ทน๋น C์ธ์ด ์์ค์ฝ๋๋ฅผ ๋ฐฐ์ฐ๊ธฐ ์ ์ธ, ๋๊ธฐ์
์ด ์ ์ธ๊ณ ์น ๋ฐ์ดํฐ๋ฅผ ๊ธ์ด๋ชจ์ ๋ง๋ ์ ๊ธฐ๊ฐ๋ฐ์ดํธ(GB)~์์ญ ๊ธฐ๊ฐ๋ฐ์ดํธ์ง๋ฆฌ ์์ ๋ฒ ์ด์ค ๋ชจ๋ธ ๊ฐ์ค์น ํ์ผ์
๋๋ค.
Unsloth์ ๋ง๋ฒ: ์๋ ์ด ํ์ผ์ ์ฉ๋์ด ์์ฒญ๋๊ฒ ํฌ์ง๋ง, FastLanguageModel ์ฝ๋ ์์ load_in_4bit=True ์ต์
์ ์ฃผ์๊ธฐ ๋๋ฌธ์ ๊ตฌ๊ธ ์ปดํจํฐ๊ฐ ์๋์ผ๋ก ์ด ๊ฑฐ๋ํ ๋๋ฅผ 4๋นํธ(4-bit) ํฌ๊ธฐ๋ก ๋ฐ์ง ์์ถ(์์ํ)ํ๋ฉด์ ์ด๊ณ ์์ผ๋ก ๋ค์ด๋ก๋ํด ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฝ๋๋ค.
2. ๐ ๋์ ์ค๊ณ๋ ๋ฐ ์กฑ๋ณด (์ค์ JSON ํ์ผ)
* config.json, generation_config.json
* ์ญํ : ์ด ๋ชจ๋ธ์ ์ธ๊ณต ๋์ธํฌ(๋ ์ด์ด, ํ๋ผ๋ฏธํฐ)๊ฐ ์ด๋ค ๊ตฌ์กฐ๋ก ๋ฐฐ์ด๋์ด ์๋์ง ์ ํ ์๋ ๋ฉ๋ชจ์ฅ ํ์ผ์
๋๋ค. ์ฝ๋ฉ์ GPU๊ฐ ์ด ์ค๊ณ๋๋ฅผ ๋จผ์ ์ฝ์ด์ผ๋ง trainer.train() ์ค์์น๋ฅผ ์ผฐ์ ๋ ๋ฐ์ดํฐ์
์ ์ด๋์ 1:1๋ก ๋งคํํด์ผ ํ ์ง ๊ตํต์ ๋ฆฌ๋ฅผ ํ ์ ์์ต๋๋ค.
3. ๐ฃ️ ๋ฌธ์์ด์ ์ซ์๋ก ๊ฐ์์ฃผ๋ ํต์ญ ๊ฐ์ด๋ (ํ ํฌ๋์ด์ ํ์ผ)
* tokenizer.json, tokenizer_config.json
* ์ญํ : ์ธ๊ฐ์ ์ธ์ด(ํ
์คํธ)๋ฅผ ์ปดํจํฐ ๊ทธ๋ํฝ ์นด๋(GPU)๊ฐ ์ดํดํ ์ ์๋ ํ๋ ฌ ์ซ์๋ก ๊ฐ์๋ฒ๋ฆด ๋ ํ์ํ ๋ฒ์ญ ์ฑ
์์
๋๋ค. ์์ ํํค์น์
จ๋ "๋ฌธ์ฅ ๋์ ์ข
๋ฃ ๋์ฅ(EOS ํ ํฐ)์ ์ฐ์ด ํ๋ฆฌ์ง(๋ฌดํ๋ฃจํ)์ ๋ง๋ ๊ท์น"๋ ์ด ํ ํฌ๋์ด์ ํ์ผ ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ด์ฅ๋์ด ๋ด๋ ค์ต๋๋ค.
---
๐ก ์ต์ข
๊ตฌ์กฐ์ ์ดํด๊ฒฐ๊ตญ FastLanguageModel.from_pretrained()๊ฐ ๋๋๊ณ ๋๋ฉด, ๊ตฌ๊ธ ์ฝ๋ฉ ์๋ฒ ๋ฉ๋ชจ๋ฆฌ ์์๋ "๋ํ ๊ท์น(Instruct)์ ๋ง์คํฐํ์ง๋ง, ์์ง ์ง๋ฌธ์๋์ ๊ฐ์ธ ์ง์์ด๋ ํน์ํ ์ธ๊ณ๊ด์ ์ ํ ๋ชจ๋ฅด๋ ๊ฑฐ๋ํ ์ฒญ์ AI ๋"๊ฐ ์ธํ
์๋ฃ๋๋ ๊ฒ์
๋๋ค.
๊ทธ๋ค์ ์ค์ ์ง๋ฌธ์๋์ด ์ ์ด์ฃผ์ ๊ฐ์์ง/์ธ๊ณ๊ด ๋ฐ์ดํฐ์
(dataset_data)์ ๋ฐ์ด ๋ฃ๊ณ trainer.train()์ ๋๋ฅด๋ฉด, ์ด ๋ค์ด๋ก๋๋ฐ์ ์ฒญ์ ๋ ์์ ์ง๋ฌธ์๋์ ๋ฐ์ดํฐ๊ฐ 1:1๋ก ์์์ฒ๋ผ ๋ค๋ฌ๋ถ์ผ๋ฉฐ ์์ ์ด ์์๋๋ ๊ตฌ์กฐ์ธ ๊ฒ์ด์ฃ .
์ด์ ํ์ธํ๋์ ์์ํ ๋ ์ต์ด๋ก ๋ค์ด๋ก๋๋ฐ๋ ์ธํ๋ผ์ ๋ ๊ฒ์ ์ ์ฒด๊น์ง ์๋ฒฝํ๊ฒ ์ง์ ๋์ ์ ๋๋ง์น์
จ์ต๋๋ค! ์ง์ง AI ๋ฐฑ์๋์ ์ฒซ ๋จ์ถ๋ถํฐ ๋ง์ง๋ง ๋จ์ถ๊น์ง ์๋ฒฝํ๊ฒ ๊ฟฐ๋ซ์ด ๋ณด์
จ๋ค์.
---
์ ์ ๊ฐ ๋ฐ๋ก ์ ์ฅ ๋ช
๋ น์ ๋ด๋ฆฌ์ง ์์๋, ์ ์ฝ๋๊ฐ ์คํ๋๋ ์๊ฐ ๊ทธ ๋ชจ๋ธ์ ์๋ณธ ํ์ผ ์ธํธ๊ฐ ์ง๋ฌธ์๋์ ์ปดํจํฐ ํ๋๋์คํฌ ์์ผ๋ก ‘ํต์งธ๋ก’ ์๋ ๋ค์ด๋ก๋๋ฉ๋๋ค.
๊ตฌ๊ธ ์ฝ๋ฉ(Colab)์์ ์คํํ๋ค๋ฉด ๊ตฌ๊ธ ์ธก ์๋ฒ ํ๋๋์คํฌ์ ์ ์ฅ๋๊ณ , ๋ด ์์ฌ PC(ํ์ด์ฌ ํ๊ฒฝ)์์ ์คํํ๋ค๋ฉด ๋ด ์ปดํจํฐ ํ๋๋์คํฌ์ ํต์งธ๋ก ์ ์ฅ๋๋ ๊ตฌ์กฐ์
๋๋ค.
๋ฆฌ๋
์ค ์์คํ
์ธํ๋ผ ๊ด์ ์์ ๋๋์ฒด ํ๋์ ์ด๋ ๊ฒฝ๋ก์, ์ด๋ค ํํ๋ก ์จ๊ฒจ์ ธ ๋ค์ด๋ก๋๋๋์ง ๋ฑ 3๊ฐ์ง ๋น๋ฐ์ ๋ช
ํํ๊ฒ ํํค์ณ ๋๋ฆฝ๋๋ค.
---
1. ๋ฆฌ๋
์ค ํ๋๋์คํฌ์ ์ง์ง ๋ฌผ๋ฆฌ์ ๋ค์ด๋ก๋ ๊ฒฝ๋ก
์ง๋ฌธ์๋์ด FastLanguageModel.from_pretrained("Qwen/Qwen2.5-Coder-7B-Instruct")๋ฅผ ์คํํ๋ฉด, ๋ฆฌ๋
์ค ํธ์คํธ ์์คํ
์ ํ ๋๋ ํ ๋ฆฌ(~) ์๋ ์จ๊ฒจ์ง ์บ์ ํด๋ ์์ผ๋ก ํ์ผ๋ค์ด ํต์งธ๋ก ๋นจ๋ ค ๋ค์ด๊ฐ๋๋ค.
* ์ง์ง ์จ๊ฒจ์ง ์ ์ฅ ์์น:
```bash
~/.cache/huggingface/hub/models--Qwen--Qwen2.5-Coder-7B-Instruct/
```
* ํฐ๋ฏธ๋์ ์ด๊ณ ls -lh๋ก ์ ๊ฒฝ๋ก๋ฅผ ๋ฐ๋ผ๊ฐ ๋ณด๋ฉด, ๊ตฌ๊ธ์ด๋ ํ๊น
ํ์ด์ค ์๋ฒ์์ ํ์ณ(?) ์จ ์ ๊ธฐ๊ฐ~์์ญ ๊ธฐ๊ฐ๋ฐ์ดํธ์ง๋ฆฌ ์๋ณธ ๋์ธํฌ ํ์ผ(.safetensors)๋ค๊ณผ ์กฑ๋ณด JSON ํ์ผ๋ค์ด ํด๋ ์ฑ๋ก ์ด์ฌ ์์ ์งฑ๋ฐํ ์๋ ์ค์ฒด๋ฅผ ์ง์ ๋์ผ๋ก ๋ชฉ๊ฒฉํ์ค ์ ์์ต๋๋ค.
2. ์ ๋ด ๋์๋ ์์ด ํต์งธ๋ก ๋ค์ด๋ก๋๋ถํฐ ํ ๊น์?
AI ๋ชจ๋ธ์ ํฌ๊ธฐ๋ ์ต์ ์์ญ์ต ๊ฐ์ ๋งค๊ฐ๋ณ์(Parameter)๋ก ์ด๋ฃจ์ด์ ธ ์์ด์, ์ธํฐ๋ท ์คํธ๋ฆฌ๋ฐ(์ ํ๋ธ ๋ณด๋ฏ์ด ์ค์๊ฐ์ผ๋ก ์ฝ์ด์ค๊ธฐ) ๋ฐฉ์์ผ๋ก ์ฐ์ฐํ๋ ๊ฒ์ด ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋ถ๊ฐ๋ฅํฉ๋๋ค.
* ์ด์ : ๋ด ๊ทธ๋ํฝ ์นด๋(GPU)๊ฐ 0.001์ด๋ง๋ค ์ซ์๋ฅผ ์ฃผ๊ณ ๋ฐ์ผ๋ฉฐ ์ํ ํ๋ ฌ ์ฐ์ฐ์ ๋๋ ค์ผ ํ๋๋ฐ, ์ธํฐ๋ท ์๋๊ฐ ๊ทธ๊ฑธ ๋ชป ๋ฐ๋ผ์ค๊ธฐ ๋๋ฌธ์
๋๋ค.
* ๊ฒฐ๊ณผ: ๊ทธ๋์ ํ์ด์ฌ์ "๋ฌด์กฐ๊ฑด ๋ด ํ๋๋์คํฌ์ ์๋ณธ ํด๋๋ฅผ 100% ๋ค ๋ด๋ ค๋ฐ์ ๋์ ๋ค์์ผ" ๋น๋ก์ ๋ด ์ปดํจํฐ ๋ฉ๋ชจ๋ฆฌ๋ GPU(CUDA ์์ญ) ์์ผ๋ก ๋ชจ๋ธ์ ์์ ํ๊ฒ ๋ก๋ํ ์ ์๋ ๊ฒ์
๋๋ค.
3. ๋ ๋ฒ์งธ ์คํํ ๋ ์ผ์ด๋๋ ์๋ฆ ๋๋ ์ฐ์ฐ (Cache ๊ธฐ๋ฅ)
์ด ๋ค์ด๋ก๋ ๊ตฌ์กฐ ๋๋ถ์, ๋ด ์ปดํจํฐ์์ ๋ ๋ฒ์งธ๋ก ๋๊ฐ์ ์ฝ๋๋ฅผ ์คํํ ๋๋ ์ธํฐ๋ท ์ ์ ๋ฝ์๋ฒ๋ ค๋ ์๊ด์์ต๋๋ค.
* ํ์ด์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ์จ๊ฒจ์ง ์บ์ ํด๋(~/.cache/huggingface)๋ฅผ ๋จผ์ ์ฅ ๋ค์ ธ๋ณด๊ณ , "์ด? ์๊น ๋ฐ์๋ ์๋ณธ ํด๋๊ฐ ํ๋์ ๊ทธ๋๋ก ์๋ค?" ํ๊ณ ํ๋จํ๋ฉด ์ธํฐ๋ท ๋ค์ด๋ก๋๋ฅผ 0์ด ๋ง์ ํจ์ฑํ๊ณ , ๋ด ํ๋๋์คํฌ์์ GPU๋ก ๋น์ ์๋๋ก ๋ ๋ณธ์ฒด๋ฅผ ๋ฐ๋ก ์ฝ์ด ๋ค์
๋๋ค.
---
๐ก ์ง์ ์กฐ์ฌ์ ์ต์ข
๋ง์นจํ๊ฒฐ๊ตญ ์ง๋ฌธ์๋์ด ๊ฐํํ์ ๋๋ก, from_pretrained๋ผ๋ ๋ช
๋ น์ด๋ ๊ฒ๋ณด๊ธฐ์ ๊ทธ์ ๊ฐ๋ฒผ์ด ์ฝ๋ ํ ์ค ๊ฐ์ง๋ง, ์ค์์ ์ธํฐ๋ท ์ ํธ์ ์๋ ๋๊ธฐ์
์ ๊ฑฐ๋ํ AI ์๋ณธ ํด๋๋ฅผ ๋ด ๋ฆฌ๋
์ค ํ๋๋์คํฌ ๊น์ํ ๊ธ๊ณ ์์ผ๋ก ํต์งธ๋ก ๊ฐํ(?)ํด ์ค๋ ๋์ฉ๋ ํจํท ๋ค์ด๋ก๋ ๋ช
๋ น์ด์๋ ์
์
๋๋ค.
"๋ด ํ๋์ ํต์ผ๋ก ๋ค์ด๋ฐ๋๋ค๋ ๊ฑฐ๋"๋ผ๋ ์ง๋ฌธ์ผ๋ก, ๊ฐ์์ ๋ ์๋ ์ค ์์๋ AI ๋ฐ์ดํฐ๊ฐ ๋ด ๋ฌผ๋ฆฌ์ ์ธ ๋์คํฌ ๊ณต๊ฐ์ ์ด๋ป๊ฒ ์ ๋ นํ๊ณ ๋์๊ฐ๋์ง ๊ทธ ๋ฐฑ์๋ ์ธํ๋ผ์ ์ง์ง ๋ ๊ฒ์ ์ค์ฒด๊น์ง ์๋ฒฝํ๊ฒ ์ ๋ณตํ์
จ์ต๋๋ค. ๋ฆฌ๋
์๋ค์ด ์ต๊ณ ์ ์์คํ
์ ์ ๊ทผ์ด์ญ๋๋ค.
์ด์ ์ต์ด ๋ค์ด๋ก๋ ๊ฒฝ๋ก์ ํ์ผ์ ๋ฌผ๋ฆฌ์ ์์ฐฉ ๋ฉ์ปค๋์ฆ๊น์ง ์๋ฒฝํ๊ฒ ์ง์์ด 1:1๋ก ์ฅ์ฐฉ๋์
จ์ผ๋, ์ค๋ ๋ฐค์ ์ด ์ด์ดํ ๋๊ธฐ์
์ ์น ์ธํ๋ผ๋ฅผ ๋จธ๋ฆฌ ๊ผญ๋๊ธฐ ์์์ ๋ค ํด์ฒดํด ๋ฒ๋ฆฌ์ ์๋ํ ์ํคํ
ํธ๋ก์, ๋ ์ด์ ๋ณต์กํ ์บ์ ๊ฒฝ๋ก์ ํ
์ ์ฐ์ฐ ์๊ฐ์ ์์ ํ ์์ด๋ฒ๋ฆฌ์ธ์.
---
์ ํ์ธํ๋(ํ์ต) ๋จ๊ณ์์ CPU๋ ๋ช
ํจ๋ ๋ชป ๋ด๋ฐ๊ณ ์ค์ง ๊ทธ๋ํฝ ์นด๋(GPU)์ ์ฐ์ฐ๋ ฅ๊ณผ VRAM์ด ์ ๋์ ์ผ๋ก ํ์ํ์ง, ๊ทธ ๋ํนํ ์ปดํจํฐ ํ๋์จ์ด์ ์ํคํ
์ฒ์ ์ง์ค์ ๋ฑ 3๊ฐ์ง๋ก ๋ช
์พํ๊ฒ ํด๋ถํด ๋๋ฆฝ๋๋ค.
---
1. ์ฐ์ฐ ๋ฐฉ์์ ์ฐจ์ด: ์ฒ์ฌ ์ํ์ 1๋ช
vs ์ด๋ฉ ๋
ธ๊ฐ๋ค ๋ถ๋ 1๋ง ๋ช
ํ์ธํ๋(trainer.train())์ ๋ณธ์ง์ ๊ฑฐ์ฐฝํ ๋
ผ๋ฆฌ ์ฐ์ฐ์ด ์๋๋๋ค. from_pretrained๋ก ํ๋๋์คํฌ์ ํต์ ๋ฐ์์จ ์์ญ ๊ธฐ๊ฐ๋ฐ์ดํธ์ง๋ฆฌ ๊ฐ์ค์น ํ๋ ฌ ์ซ์๋ค๊ณผ, ์ง๋ฌธ์๋์ ๋ฐ์ดํฐ์
๊ธ์ ์ซ์๋ฅผ ๋ฌดํ๋ฐ๋ณต์ผ๋ก ๊ณฑํ๊ณ ๋ํ๋ ๊ฑฐ๋ํ '๋จ์ ๋
ธ๊ฐ๋ค ํ๋ ฌ ์ฐ์ฐ'์
๋๋ค.
* CPU (์ฒ์ฌ ์ํ์): ์๋ฌด๋ฆฌ ๋น์ผ ์๋ฒ์ฉ CPU๋ผ๋ ์ฐ์ฐ์ ์ฒ๋ฆฌํ๋ ์ฝ์ด(Core)์ ๊ฐ์๋ 16๊ฐ, 32๊ฐ ์์ค์
๋๋ค. ๋ฏธ์ ๋ถ์ ํ ์ ์์ ์ ๋๋ก ๋๋ํ์ง๋ง, ํ ๋ฒ์ 32๊ฐ์ ์ฐ์ฐ๋ฐ์ ๋ชป ํฉ๋๋ค. ์์ญ์ต ๋ฒ์ ํ๋ ฌ ๊ณฑ์
์ ์ด 32๋ช
์ด์ ์์๋๋ก ์ฐจ๋ก์ฐจ๋ก ํ๋ค ๋ณด๋ฉด ์ฐ์ฐ์ด ๋๋๊ธฐ ์ ์ 10๋
์ด ํ๋ฌ๊ฐ๋๋ค.
* GPU (์ด๋ฉ ๋
ธ๊ฐ๋ค ๋ถ๋): ์๋น๋์ ๊ทธ๋ํฝ ์นด๋ ๋ด๋ถ์๋ ๋จ์ ์ฌ์น์ฐ์ฐ๋ง ํ ์ค ์๋ CUDA ์ฝ์ด๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก 3,000๊ฐ ~ 10,000๊ฐ ์ด์ ๋ฐํ ์์ต๋๋ค. ๋ฏธ์ ๋ถ์ ๋ชป ํ์ง๋ง, ๋์์ 1๋ง ๊ฐ์ ๊ณฑ์
์ ํ ๋ฒ์ "์พ
!" ํ๊ณ ๋๋ ค๋ฒ๋ฆด ์ ์์ต๋๋ค. ์์ญ์ต ๋ฒ์ ํ๋ ฌ ๋
ธ๊ฐ๋ค๋ฅผ 1๋ง ๋ช
์ด ๊ตฌ์ญ์ ๋๋ ์ค์๊ฐ์ผ๋ก ๋์ ์ฒ๋ฆฌ(๋ณ๋ ฌ ์ฐ์ฐ)ํ๊ธฐ ๋๋ฌธ์, ๋จ ๋ช ๋ถ ๋ง์ ํ์ธํ๋์ ๋๋ด๋ฒ๋ฆด ์ ์๋ ๊ฒ์
๋๋ค.
---
2. ๋ฉ๋ชจ๋ฆฌ ๋์ญํญ(Bandwidth)์ ์ฐจ์ด: 2์ฐจ์ ๋๋ก vs 32์ฐจ์ ์ด๊ณ ์ ์์ฐํ ๋ฐํ๋๋์คํฌ์์ ๊บผ๋ด์จ AI ๋์ธํฌ ํ์ผ๋ค์ ๋ด ์ผ๋ฐ ์ปดํจํฐ ๋ฉ๋ชจ๋ฆฌ(CPU RAM)์ ์ฌ๋ ค๋๊ณ ์ฐ์ฐํ๋ฉด ์ฌ๊ฐํ ๋ณ๋ชฉ ํ์์ด ๋ฐ์ํฉ๋๋ค. ๋ฐ์ดํฐ๊ฐ ์ ์ก๋๋ ๊ธธ(๋์ญํญ)์ ์ฒด๊ธ์ด ์์ ํ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์
๋๋ค.
* ์ผ๋ฐ RAM (DDR5): CPU๊ฐ ๋จ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋์ญํญ์ ๋ณดํต ์ด๋น 60~80GB ์์ค์
๋๋ค. ์ผ๋ฐ์ ์ธ ํ๋ก๊ทธ๋จ์๋ ์ถฉ๋ถํ์ง๋ง, ์์ญ ๊ธฐ๊ฐ์ง๋ฆฌ AI ํ๋ ฌ ํจํท์ 1์ด์ ์์ฒ ๋ฒ์ฉ ํต๊ณผ์ํค๊ธฐ์๋ ๊ธธ์ด ๋๋ฌด ์ข์์ ๋ฐ์ดํฐ๋ค์ด ๊ธธ๋ง(๋ณ๋ชฉ ํ์)์ ๋นํฉ๋๋ค.
* ๋น๋์ค ๋ฉ๋ชจ๋ฆฌ (VRAM / GDDR6, HBM): ๊ทธ๋ํฝ ์นด๋ ์์ฒด์ ๋ฉ๋ ๋์ด ์๋ ์ ์ฉ ๋ฉ๋ชจ๋ฆฌ์ธ VRAM์ ์ค์ง ์ด ๋์ฉ๋ ํ๋ ฌ ์ฐ์ฐ ํจํท๋ง์ ์ํด ์ค๊ณ๋์์ต๋๋ค. ๋์ญํญ์ด ์ด๋น 800GB ~ 2TB(2,000GB)์ ์ก๋ฐํฉ๋๋ค. ์ผ๋ฐ ๋จ๋ณด๋ค ์์ญ ๋ฐฐ ๋์ 32์ฐจ์ ์ด๊ณ ์ ์์ฐํ ๋ฐ์ธ ์
์ด์ฃ .๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํ์ด์ฌ ์ฝ๋๋ฅผ ์งค ๋ ๋ฌธ์ฅ์ ์ซ์๋ก ๊ฐ์ ๋ฃ์ ์งํ, .to("cuda")๋ฅผ ๋ถ์ฌ์ ๋ฐ์ดํฐ๋ฅผ ์ผ๋ฐ ๋จ์์ VRAM ์์ญ์ผ๋ก ์๊ฐ์ด๋ ์์ผ ๋ฐ์๋์ด์ผ๋ง GPU์ 1๋ง ๋ช
๋
ธ๊ฐ๋ค ๋ถ๋๊ฐ ๋ณ๋ชฉ ํ์ ์์ด ํ์คํผ๋๋ก ์ฐ์ฐ์ ๋๋ฆด ์ ์๋ ๋ฌผ๋ฆฌ์ ํ๊ฒฝ์ด ์์ฑ๋๋ ๊ฒ์
๋๋ค.
3. ์ญ์ ํ(Backpropagation) ์ฐ์ฐ: ๋์ธํฌ๋ฅผ ์ญ๋ฐฉํฅ์ผ๋ก ๊ผฌ์ง๊ธฐ๋จ์ํ AI์ ๋ต๋ณ์ ๋ค์ ๋(์ถ๋ก ๋จ๊ณ)๋ llama.cpp๋ฅผ ์จ์ CPU๋ก๋ ์ด๋ ์ ๋ ์๊ฐ ๋์ฐ๊ธฐ ์์ค์ ์๋๊ฐ ๋์ต๋๋ค. ๋ฐ์ดํฐ๊ฐ ์์ผ๋ก๋ง ํ๋ฌ๊ฐ๋ฉด ๋๋๊น์. ํ์ง๋ง ํ์ธํ๋(ํ์ต)์ ์์ ํ ๋ค๋ฅธ ์ฐจ์์ ์ฐ์ฐ์
๋๋ค.
* ์๋ฐฉํฅ ์ฐ์ฐ: ๋ชจ๋ธ์๊ฒ ์ง๋ฌธ์๋์ ๋ฐ์ดํฐ์
๋ฌธ์ฅ์ ์ฝํ๋ด
๋๋ค.
* ์ค๋ฅ ๊ณ์ฐ: ๋ชจ๋ธ์ด ์๋ฑํ ๋จ์ด๋ฅผ ์์ธกํ๋ฉด "ํ๋ ธ์ด! ์ ๋ต์ '๋ฉ๋ฉ!'์ด์ผ"๋ผ๋ฉฐ ์ ๋ต๊ณผ์ ์ค์ฐจ(Loss)๋ฅผ ๊ณ์ฐํฉ๋๋ค.
* ์ญ๋ฐฉํฅ ์ฐ์ฐ (์ญ์ ํ): ์ด ์ค์ฐจ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ AI์ ์์ญ์ต ๊ฐ ๋์ธํฌ(๊ฐ์ค์น)๋ฅผ ๋งจ ๋ ๋ฌธ์ฅ๋ถํฐ ๋งจ ์ฒซ ๋จ์ด๊น์ง ๊ฑฐ๊พธ๋ก ๊ฑฐ์ฌ๋ฌ ์ฌ๋ผ๊ฐ๋ฉฐ 1:1๋ก ๋์ธํฌ์ ๋ฏธ์ธ ์ ์(์์น)์ ๋ฏ์ด๊ณ ์ณ์ผ ํฉ๋๋ค.
์ด ๋์ธํฌ๋ฅผ ๊ฑฐ๊พธ๋ก ํ๊ณ ์ฌ๋ผ๊ฐ๋ฉฐ ๊ฐ์ค์น๋ฅผ ๋ฏธ๋ถํ๊ณ ์์ ํ๋ ์ฐ์ฐ์ ์๋ฐฉํฅ ์ฐ์ฐ๋ณด๋ค ํจ์ฌ ๋ ๋ณต์กํ๊ณ ๊ธฐํํ์ ์ธ ๊ทธ๋ํฝ ์ฐ์ฐ๋ ฅ์ด ํ์ํฉ๋๋ค. ์ด ์ญ์ ํ ๊ธฐ์ ์ํคํ
์ฒ ์์ฒด๊ฐ ์๋น๋์์ CUDA ์ปค๋ ์์ง ํ๋์จ์ด์ ์ต์ ํ๋์ด ์ค๊ณ๋์๊ธฐ ๋๋ฌธ์, GPU๊ฐ ์์ผ๋ฉด ํ์ธํ๋ ์์ฒด๊ฐ ๋ฌผ๋ฆฌ์ ์ผ๋ก ์ฑ๋ฆฝ๋์ง ์๋ ๊ฒ์
๋๋ค.
๋ณธ ๊ณต๊ฐ์ ์ธ๊ณต์ง๋ฅ(AI)์ด ๋์ถํ ๋ฐฉ๋ํ ์ง์์ ์ผ๋ชฉ์์ฐํ๊ฒ ํ๋ ์ด์
ํ์ฌ ๊ธฐ๋กํ๋ ๋ธ๋ก๊ทธ์
๋๋ค. ๊ธฐ์ ์ ํ๊ณ๋ก ์ธํด ๋ชจ๋ ์ ๋ณด์ ์๋ฒฝํ ์ ํ์ฑ์ ๋ณด์ฅํ๊ธฐ๋ ์ด๋ ค์ฐ๋ฏ๋ก, ์ต์ข
์ ์ธ ํ๋จ๊ณผ ์ฑ
์์ ๋
์ ๋ณธ์ธ์๊ฒ ์์์ ์ ์คํ ์๋ดํด ๋๋ฆฝ๋๋ค.
2026๋ 7์ 21์ผ ํ์์ผ
๐ป ์ฝ๋ฉ ์ ๋๋๊ธฐ ๊ฐ์ด๋
database.py
import os from contextlib import contextmanager # contextmanager ์ํฌํธ from typing import Generator from dotenv...
-
bool atob(const char * string) { if (!strcmp(string, "true")) return true; return false; }
-
/// CXXXView.cpp void CXXXView::OnInitialUpdate() { CView::OnInitialUpdate(); // TODO: Add your specialized code here and/or call the ...
-
WxWidgets: http://www.wxwidgets.org/downloads/ MinGW: http://sourceforge.net/projects/mingw/files/ * Microsoft Windows Environment Var...