|
|
|
""" |
|
🎯 Tworzenie własnego modelu LLM - od fine-tuningu do publikacji |
|
Przykład z Mistral 7B + własne dane |
|
""" |
|
|
|
import os |
|
import json |
|
from pathlib import Path |
|
from datasets import Dataset |
|
from transformers import ( |
|
AutoModelForCausalLM, |
|
AutoTokenizer, |
|
TrainingArguments, |
|
Trainer |
|
) |
|
from peft import LoraConfig, get_peft_model, TaskType |
|
import torch |
|
|
|
|
|
|
|
def create_sample_dataset(): |
|
"""Tworzy przykładowy dataset do fine-tuningu""" |
|
|
|
|
|
sample_data = [ |
|
{ |
|
"instruction": "Jak nazywa się stolica Polski?", |
|
"input": "", |
|
"output": "Stolica Polski to Warszawa." |
|
}, |
|
{ |
|
"instruction": "Wyjaśnij czym jest sztuczna inteligencja", |
|
"input": "", |
|
"output": "Sztuczna inteligencja (AI) to dziedzina informatyki zajmująca się tworzeniem systemów zdolnych do wykonywania zadań wymagających inteligencji." |
|
}, |
|
{ |
|
"instruction": "Napisz krótką funkcję w Pythonie", |
|
"input": "funkcja do obliczania silni", |
|
"output": "```python\ndef factorial(n):\n if n <= 1:\n return 1\n return n * factorial(n-1)\n```" |
|
}, |
|
{ |
|
"instruction": "Jak zoptymalizować kod Python?", |
|
"input": "", |
|
"output": "Główne sposoby optymalizacji kodu Python:\n1. Używaj wbudowanych funkcji\n2. Unikaj pętli, preferuj list comprehensions\n3. Używaj numpy dla operacji numerycznych\n4. Profiluj kod przed optymalizacją" |
|
}, |
|
{ |
|
"instruction": "Co to jest Docker?", |
|
"input": "", |
|
"output": "Docker to platforma konteneryzacji umożliwiająca pakowanie aplikacji wraz z zależnościami w lekkie, przenośne kontenery." |
|
} |
|
] |
|
|
|
|
|
os.makedirs("data", exist_ok=True) |
|
with open("data/training_data.json", "w", encoding="utf-8") as f: |
|
json.dump(sample_data, f, indent=2, ensure_ascii=False) |
|
|
|
print("✅ Sample dataset created in data/training_data.json") |
|
return sample_data |
|
|
|
def format_training_data(examples): |
|
"""Formatuje dane dla Mistral Instruct""" |
|
formatted_texts = [] |
|
|
|
for example in examples: |
|
if example.get("input"): |
|
prompt = f"<s>[INST] {example['instruction']}\n{example['input']} [/INST] {example['output']}</s>" |
|
else: |
|
prompt = f"<s>[INST] {example['instruction']} [/INST] {example['output']}</s>" |
|
formatted_texts.append(prompt) |
|
|
|
return {"text": formatted_texts} |
|
|
|
|
|
|
|
def setup_model_and_tokenizer(model_name="mistralai/Mistral-7B-Instruct-v0.1"): |
|
"""Ładuje model i tokenizer""" |
|
print(f"📥 Loading model: {model_name}") |
|
|
|
|
|
tokenizer = AutoTokenizer.from_pretrained(model_name) |
|
tokenizer.pad_token = tokenizer.eos_token |
|
tokenizer.padding_side = "right" |
|
|
|
|
|
model = AutoModelForCausalLM.from_pretrained( |
|
model_name, |
|
torch_dtype=torch.float16, |
|
device_map="auto", |
|
load_in_4bit=True, |
|
trust_remote_code=True |
|
) |
|
|
|
return model, tokenizer |
|
|
|
def setup_lora_config(): |
|
"""Konfiguracja LoRA dla efficient fine-tuning""" |
|
return LoraConfig( |
|
task_type=TaskType.CAUSAL_LM, |
|
inference_mode=False, |
|
r=16, |
|
lora_alpha=32, |
|
lora_dropout=0.1, |
|
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"] |
|
) |
|
|
|
def fine_tune_model(): |
|
"""Główna funkcja fine-tuningu""" |
|
|
|
|
|
print("🔄 Preparing training data...") |
|
sample_data = create_sample_dataset() |
|
|
|
|
|
model, tokenizer = setup_model_and_tokenizer() |
|
|
|
|
|
lora_config = setup_lora_config() |
|
model = get_peft_model(model, lora_config) |
|
|
|
print(f"📊 Trainable parameters: {model.print_trainable_parameters()}") |
|
|
|
|
|
dataset = Dataset.from_list(sample_data) |
|
formatted_dataset = dataset.map( |
|
lambda x: format_training_data([x]), |
|
remove_columns=dataset.column_names |
|
) |
|
|
|
|
|
def tokenize_function(examples): |
|
return tokenizer( |
|
examples["text"], |
|
truncation=True, |
|
padding="max_length", |
|
max_length=512, |
|
return_tensors="pt" |
|
) |
|
|
|
tokenized_dataset = formatted_dataset.map(tokenize_function, batched=True) |
|
|
|
|
|
training_args = TrainingArguments( |
|
output_dir="./results", |
|
num_train_epochs=3, |
|
per_device_train_batch_size=1, |
|
gradient_accumulation_steps=4, |
|
warmup_steps=10, |
|
learning_rate=2e-4, |
|
fp16=True, |
|
logging_steps=1, |
|
save_strategy="epoch", |
|
evaluation_strategy="no", |
|
dataloader_num_workers=0, |
|
remove_unused_columns=False, |
|
) |
|
|
|
|
|
trainer = Trainer( |
|
model=model, |
|
args=training_args, |
|
train_dataset=tokenized_dataset, |
|
tokenizer=tokenizer, |
|
) |
|
|
|
|
|
print("🚀 Starting fine-tuning...") |
|
trainer.train() |
|
|
|
|
|
model.save_pretrained("./fine_tuned_model") |
|
tokenizer.save_pretrained("./fine_tuned_model") |
|
|
|
print("✅ Fine-tuning completed! Model saved to ./fine_tuned_model") |
|
|
|
return model, tokenizer |
|
|
|
|
|
|
|
def convert_to_gguf(): |
|
"""Konwertuje model do formatu GGUF dla Ollama""" |
|
|
|
print("🔄 Converting to GGUF format...") |
|
|
|
|
|
conversion_script = """ |
|
#!/bin/bash |
|
|
|
# Pobierz llama.cpp jeśli nie masz |
|
if [ ! -d "llama.cpp" ]; then |
|
git clone https://github.com/ggerganov/llama.cpp.git |
|
cd llama.cpp |
|
make -j |
|
cd .. |
|
fi |
|
|
|
# Konwertuj model |
|
python llama.cpp/convert.py ./fine_tuned_model --outtype f16 --outfile my_custom_model.gguf |
|
|
|
echo "✅ GGUF conversion completed: my_custom_model.gguf" |
|
""" |
|
|
|
with open("convert_to_gguf.sh", "w") as f: |
|
f.write(conversion_script) |
|
|
|
os.chmod("convert_to_gguf.sh", 0o755) |
|
|
|
print("📝 Created convert_to_gguf.sh script") |
|
print("Run: ./convert_to_gguf.sh") |
|
|
|
|
|
|
|
def create_ollama_modelfile(): |
|
"""Tworzy Modelfile dla Ollama""" |
|
|
|
modelfile_content = '''FROM ./my_custom_model.gguf |
|
|
|
# Model metadata |
|
PARAMETER temperature 0.7 |
|
PARAMETER top_p 0.9 |
|
PARAMETER top_k 40 |
|
PARAMETER num_ctx 2048 |
|
|
|
# System prompt |
|
SYSTEM "Jesteś pomocnym asystentem AI stworzonym specjalnie dla polskich użytkowników.\nOdpowiadasz w języku polskim, jesteś precyzyjny i pomocny.\nSpecjalizujesz się w programowaniu, technologii i sztucznej inteligencji." |
|
|
|
# Chat template dla Mistral |
|
TEMPLATE "<s>[INST] {{ if .System }}{{ .System }}{{ end }}{{ .Prompt }} [/INST] {{ .Response }}</s>" |
|
|
|
# Metadata |
|
PARAMETER num_predict 256 |
|
PARAMETER stop "<s>" |
|
PARAMETER stop "[INST]" |
|
PARAMETER stop "[/INST]" |
|
''' |
|
|
|
with open("Modelfile", "w", encoding="utf-8") as f: |
|
f.write(modelfile_content) |
|
print("✅ Utworzono Modelfile dla Ollama") |
|
print("✅ Created Modelfile for Ollama") |
|
|
|
|
|
|
|
def create_model_in_ollama(): |
|
"""Tworzy model w Ollama""" |
|
|
|
ollama_commands = """ |
|
# 1. Utwórz model w Ollama |
|
ollama create wronai -f Modelfile |
|
|
|
# 2. Test modelu |
|
ollama run wronai "Cześć! Kim jesteś?" |
|
|
|
# 3. Push do Ollama Library (wymaga konta) |
|
ollama push wronai |
|
|
|
# 4. Alternatywnie - export do pliku |
|
ollama save wronai wronai-model.tar |
|
""" |
|
|
|
with open("ollama_commands.sh", "w") as f: |
|
f.write(ollama_commands) |
|
|
|
print("✅ Created ollama_commands.sh") |
|
|
|
|
|
|
|
def create_hf_publish_script(): |
|
"""Skrypt do publikacji na Hugging Face""" |
|
|
|
hf_script = '''#!/usr/bin/env python3 |
|
""" |
|
Publikacja modelu na Hugging Face Hub |
|
""" |
|
|
|
from huggingface_hub import HfApi, create_repo |
|
import os |
|
|
|
def publish_to_hf(): |
|
# Konfiguracja |
|
model_name = "your-username/my-custom-mistral-7b" |
|
|
|
# Login (wymagany HF token) |
|
# huggingface-cli login |
|
|
|
# Utwórz repo |
|
api = HfApi() |
|
|
|
try: |
|
create_repo( |
|
repo_id=model_name, |
|
repo_type="model", |
|
private=False # Ustaw True dla prywatnego |
|
) |
|
print(f"✅ Repository created: {model_name}") |
|
except Exception as e: |
|
print(f"Repository may already exist: {e}") |
|
|
|
# Upload plików |
|
api.upload_folder( |
|
folder_path="./fine_tuned_model", |
|
repo_id=model_name, |
|
commit_message="Initial model upload" |
|
) |
|
|
|
# Upload GGUF (jeśli istnieje) |
|
if os.path.exists("my_custom_model.gguf"): |
|
api.upload_file( |
|
path_or_fileobj="my_custom_model.gguf", |
|
path_in_repo="my_custom_model.gguf", |
|
repo_id=model_name, |
|
commit_message="Add GGUF version" |
|
) |
|
|
|
print(f"🎉 Model published: https://huggingface.co/{model_name}") |
|
|
|
if __name__ == "__main__": |
|
publish_to_hf() |
|
''' |
|
|
|
with open("publish_to_hf.py", "w") as f: |
|
f.write(hf_script) |
|
|
|
print("✅ Created publish_to_hf.py") |
|
|
|
|
|
|
|
def main(): |
|
"""Pełny pipeline tworzenia własnego modelu""" |
|
|
|
print("🎯 Custom LLM Creation Pipeline") |
|
print("===============================") |
|
|
|
choice = input(""" |
|
Wybierz opcję: |
|
1. Stwórz sample dataset |
|
2. Fine-tune model (wymaga GPU) |
|
3. Konwertuj do GGUF |
|
4. Utwórz Modelfile dla Ollama |
|
5. Przygotuj skrypty publikacji |
|
6. Pełny pipeline (1-5) |
|
|
|
Wybór (1-6): """).strip() |
|
|
|
if choice == "1": |
|
create_sample_dataset() |
|
elif choice == "2": |
|
fine_tune_model() |
|
elif choice == "3": |
|
convert_to_gguf() |
|
elif choice == "4": |
|
create_ollama_modelfile() |
|
elif choice == "5": |
|
create_hf_publish_script() |
|
elif choice == "6": |
|
print("🚀 Running full pipeline...") |
|
create_sample_dataset() |
|
|
|
if input("Continue with fine-tuning? (y/N): ").lower() == 'y': |
|
fine_tune_model() |
|
convert_to_gguf() |
|
|
|
create_ollama_modelfile() |
|
create_model_in_ollama() |
|
create_hf_publish_script() |
|
|
|
print("✅ Full pipeline completed!") |
|
else: |
|
print("Invalid choice") |
|
|
|
if __name__ == "__main__": |
|
main() |