| |
| """ |
| 🎯 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() |