import gradio as gr import os import torch import json import pandas as pd from datasets import Dataset from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, DataCollatorForLanguageModeling ) from peft import ( LoraConfig, get_peft_model, prepare_model_for_kbit_training, PeftModel ) import spaces from huggingface_hub import login # Set environment variable for cache directory os.environ['TRANSFORMERS_CACHE'] = '/tmp/hf_cache' os.makedirs('/tmp/hf_cache', exist_ok=True) # Get token from environment variable and log in hf_token = os.environ.get("HF_TOKEN") if hf_token: login(token=hf_token) print("Successfully logged in to Hugging Face Hub") else: print("No Hugging Face token found. You may encounter access issues with gated models.") def sample_from_csv(csv_file, sample_size=100): """Sample from CSV file and format for training""" df = pd.read_csv(csv_file) # Display CSV info print(f"CSV columns: {df.columns.tolist()}") print(f"Total rows in CSV: {len(df)}") # Try to identify teacher and student columns teacher_col = None student_col = None for col in df.columns: col_lower = col.lower() if 'teacher' in col_lower or 'instructor' in col_lower or 'prompt' in col_lower: teacher_col = col elif 'student' in col_lower or 'response' in col_lower or 'answer' in col_lower: student_col = col # If we couldn't identify columns, use the first two if teacher_col is None or student_col is None: teacher_col = df.columns[0] student_col = df.columns[1] print(f"Using columns: {teacher_col} (teacher) and {student_col} (student)") else: print(f"Identified columns: {teacher_col} (teacher) and {student_col} (student)") # Sample rows if sample_size >= len(df): sampled_df = df else: sampled_df = df.sample(n=sample_size, random_state=42) # Format data texts = [] for _, row in sampled_df.iterrows(): teacher_text = str(row[teacher_col]).strip() student_text = str(row[student_col]).strip() # Skip rows with empty values if not teacher_text or not student_text or teacher_text == 'nan' or student_text == 'nan': continue # Format according to the document format: # [INST] Teacher ** [/INST] Student** formatted_text = f" [INST] Teacher ** {teacher_text} [/INST] Student** {student_text} " texts.append(formatted_text) print(f"Created {len(texts)} formatted examples") return Dataset.from_dict({"text": texts}) @spaces.GPU def finetune_model(csv_file, sample_size=100, num_epochs=3, progress=gr.Progress()): """Fine-tune the model and return results""" # Check GPU if torch.cuda.is_available(): print(f"GPU available: {torch.cuda.get_device_name(0)}") device = torch.device("cuda") else: print("No GPU available, fine-tuning will be extremely slow!") device = torch.device("cpu") # Sample data progress(0.1, "Sampling data from CSV...") dataset = sample_from_csv(csv_file, sample_size) # Split dataset dataset_split = dataset.train_test_split(test_size=0.1) # Load tokenizer progress(0.2, "Loading tokenizer...") # Use only the original Mistral model model_name = "mistralai/Mistral-7B-v0.1" print(f"Using model: {model_name}") tokenizer = AutoTokenizer.from_pretrained(model_name, token=hf_token) tokenizer.pad_token = tokenizer.eos_token # Tokenize dataset def tokenize_function(examples): return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=512) progress(0.3, "Tokenizing dataset...") tokenized_datasets = dataset_split.map(tokenize_function, batched=True) # Load model with LoRA configuration progress(0.4, "Loading model...") lora_config = LoraConfig( r=8, lora_alpha=16, target_modules=["q_proj", "v_proj", "k_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" ) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16, device_map="auto", token=hf_token, ) # Prepare model for LoRA training model = prepare_model_for_kbit_training(model) model = get_peft_model(model, lora_config) # Print model info print(f"Model loaded: {model_name}") model_params = sum(p.numel() for p in model.parameters()) print(f"Model parameters: {model_params:,}") # Training arguments output_dir = "mistral7b_finetuned" training_args = TrainingArguments( output_dir=output_dir, num_train_epochs=num_epochs, per_device_train_batch_size=1, gradient_accumulation_steps=4, save_steps=50, logging_steps=10, learning_rate=2e-4, weight_decay=0.001, fp16=True, warmup_steps=50, lr_scheduler_type="cosine", report_to="none", # Disable wandb ) # Initialize trainer data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_datasets["train"], eval_dataset=tokenized_datasets["test"], data_collator=data_collator, ) # Train model progress(0.5, "Training model...") trainer.train() # Save model progress(0.9, "Saving model...") trainer.model.save_pretrained(output_dir) tokenizer.save_pretrained(output_dir) # Test with sample prompts progress(0.95, "Testing model...") test_prompts = [ "How was the Math exam?", "Good morning students! How are you all?", "What should you do if you get into a fight with a friend?", "Did you complete your science project?", "What did you learn in class today?" ] # Load the fine-tuned model for inference fine_tuned_model = PeftModel.from_pretrained( model, output_dir, device_map="auto", ) # Generate responses results = [] for prompt in test_prompts: formatted_prompt = f" [INST] Teacher ** {prompt} [/INST] Student**" inputs = tokenizer(formatted_prompt, return_tensors="pt").to(device) with torch.no_grad(): outputs = fine_tuned_model.generate( **inputs, max_length=200, temperature=0.7, top_p=0.95, do_sample=True, ) response = tokenizer.decode(outputs[0], skip_special_tokens=True) student_part = response.split("Student**")[1].strip() if "Student**" in response else response results.append({ "prompt": prompt, "response": student_part }) # Save results with open("test_results.json", "w") as f: json.dump(results, f, indent=2) progress(1.0, "Completed!") return results # Define Gradio interface with gr.Blocks() as demo: gr.Markdown("# Mistral 7B Fine-Tuning for Student Bot") with gr.Tab("System Check"): check_btn = gr.Button("Check GPU and Authentication Status") system_output = gr.Textbox(label="System Status", lines=5) @spaces.GPU def check_system(): status = [] # Check GPU if torch.cuda.is_available(): status.append(f"✅ GPU AVAILABLE: {torch.cuda.get_device_name(0)}") gpu_memory = f"Total GPU memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB" status.append(gpu_memory) else: status.append("❌ NO GPU DETECTED.") # Check HF token if os.environ.get("HF_TOKEN"): status.append("✅ Hugging Face token found") else: status.append("❌ No Hugging Face token found. You may encounter access issues with gated models.") # Check if we can access Mistral model try: from huggingface_hub import model_info info = model_info("mistralai/Mistral-7B-v0.1", token=hf_token) status.append(f"✅ Access to Mistral-7B-v0.1 model verified: {info.modelId}") except Exception as e: status.append(f"❌ Cannot access Mistral-7B-v0.1 model: {str(e)}") return "\n".join(status) check_btn.click(check_system, inputs=[], outputs=[system_output]) with gr.Tab("Fine-tune Model"): with gr.Row(): csv_input = gr.File(label="Upload Teacher-Student CSV") with gr.Row(): sample_size = gr.Slider(minimum=10, maximum=1000, value=100, step=10, label="Sample Size") epochs = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="Number of Epochs") with gr.Row(): start_btn = gr.Button("Start Fine-Tuning") with gr.Row(): output = gr.JSON(label="Results") start_btn.click(finetune_model, inputs=[csv_input, sample_size, epochs], outputs=[output]) with gr.Tab("About"): gr.Markdown(""" ## Fine-Tuning Mistral 7B for Student Bot This app fine-tunes the original Mistral-7B-v0.1 model to respond like a student to teacher prompts. ### Requirements - CSV file with teacher-student conversation pairs - GPU acceleration (provided by this Space) - Hugging Face authentication for accessing Mistral-7B-v0.1 (which is a gated model) ### Process 1. Upload your CSV file 2. Set sample size and number of epochs 3. Click "Start Fine-Tuning" 4. View test results with sample prompts ### Important Notes - Fine-tuning can take several hours depending on your sample size and epochs - The model will be saved in the Space and can be downloaded for further use """) # Launch app demo.launch()