Finetune-Test / ui.py
nirbo's picture
Upload folder using huggingface_hub
dc2b56f verified
import gradio as gr
import torch
from model_utils import load_model
from dataset_utils import prepare_dataset, create_synthetic_dataset
from training_utils import finetune_model
from inference_utils import test_model
from gguf_utils import convert_to_gguf
from unsloth import FastLanguageModel
from unsloth.chat_templates import get_chat_template
from upload_utils import upload_to_huggingface, upload_gguf_to_huggingface
def create_gradio_interface():
models = [
"unsloth/Meta-Llama-3.1-8B-bnb-4bit",
"unsloth/Mistral-Small-Instruct-2409",
"unsloth/mistral-7b-instruct-v0.3-bnb-4bit",
"unsloth/Phi-3.5-mini-instruct",
"unsloth/Phi-3-medium-4k-instruct",
"unsloth/gemma-2-9b-bnb-4bit",
"unsloth/gemma-2-27b-bnb-4bit",
"unsloth/Llama-3.2-3B-Instruct",
]
with gr.Blocks() as demo:
gr.Markdown("# LLM Finetuner")
model = gr.State(None)
tokenizer = gr.State(None)
dataset = gr.State(None)
with gr.Tab("Settings"):
hf_token = gr.Textbox(label="Hugging Face Token", type="password")
model_path = gr.Dropdown(label="Model", choices=models, value="unsloth/Llama-3.2-3B-Instruct")
load_model_btn = gr.Button("Load Model")
load_model_output = gr.Textbox(label="Load Model Output")
with gr.Tab("Dataset"):
with gr.Group():
gr.Markdown("## Use Existing Dataset")
dataset_source = gr.Radio(["Hugging Face", "Local File"], label="Dataset Source", value="Hugging Face")
hf_dataset_path = gr.Textbox(label="Hugging Face Dataset Path", value="mlabonne/FineTome-100k")
local_dataset_path = gr.File(label="Upload Local Dataset (JSON or CSV)", visible=False)
prepare_dataset_btn = gr.Button("Prepare Dataset")
prepare_dataset_output = gr.Textbox(label="Prepare Dataset Output")
with gr.Group():
gr.Markdown("## Create Synthetic Dataset")
examples = gr.Textbox(label="Example Conversations", lines=10, placeholder="Enter example conversations here...")
expected_structure = gr.Textbox(label="Expected Dataset Structure", lines=5, placeholder="Enter the expected structure for the dataset...")
num_samples = gr.Number(label="Number of Samples to Generate", value=100)
ai_provider = gr.Radio(["OpenAI", "Anthropic", "Ollama"], label="AI Provider")
api_key = gr.Textbox(label="API Key", type="password")
ollama_model = gr.Textbox(label="Ollama Model Name", visible=False)
create_dataset_btn = gr.Button("Create Synthetic Dataset")
create_dataset_output = gr.Textbox(label="Create Dataset Output")
with gr.Tab("Training"):
learning_rate = gr.Number(label="Learning Rate", value=2e-4)
batch_size = gr.Number(label="Batch Size", value=2)
num_epochs = gr.Number(label="Number of Epochs", value=1)
train_btn = gr.Button("Start Training")
train_output = gr.Textbox(label="Training Output")
with gr.Tab("Test"):
test_input = gr.Textbox(label="Test Input")
test_btn = gr.Button("Test Model")
test_output = gr.Textbox(label="Model Output")
with gr.Tab("GGUF Conversion"):
gguf_output_path = gr.Textbox(label="GGUF Output Path")
gguf_quant_method = gr.Dropdown(
label="Quantization Method",
choices=["q8_0", "q4_k_m", "q5_k_m", "f16"],
value="q8_0"
)
gguf_convert_btn = gr.Button("Convert to GGUF")
gguf_output = gr.Textbox(label="GGUF Conversion Output")
with gr.Tab("Upload to Hugging Face"):
repo_name = gr.Textbox(label="Hugging Face Repository Name")
model_type = gr.Radio(["Fine-tuned Model", "GGUF Converted Model"], label="Model Type to Upload", value="Fine-tuned Model")
gguf_file_path = gr.Textbox(label="GGUF File Path (if uploading GGUF model)", visible=False)
upload_btn = gr.Button("Upload to Hugging Face")
upload_output = gr.Textbox(label="Upload Output")
def load_model_and_tokenizer(model_path, hf_token):
model_val, tokenizer_val = load_model(model_path, hf_token)
tokenizer_val = get_chat_template(tokenizer_val, chat_template="llama-3.1")
return model_val, tokenizer_val, "Model and tokenizer loaded successfully!"
def update_ollama_visibility(choice):
return gr.update(visible=(choice == "Ollama"))
def update_dataset_input_visibility(choice):
return gr.update(visible=(choice == "Hugging Face")), gr.update(visible=(choice == "Local File"))
def update_gguf_path_visibility(choice):
return gr.update(visible=(choice == "GGUF Converted Model"))
load_model_btn.click(
load_model_and_tokenizer,
inputs=[model_path, hf_token],
outputs=[model, tokenizer, load_model_output]
)
dataset_source.change(
update_dataset_input_visibility,
inputs=[dataset_source],
outputs=[hf_dataset_path, local_dataset_path]
)
model_type.change(
update_gguf_path_visibility,
inputs=[model_type],
outputs=[gguf_file_path]
)
def prepare_dataset_wrapper(source, hf_path, local_file, hf_token, tokenizer_val):
if tokenizer_val is None:
return "Error: Model and tokenizer not loaded. Please load the model first."
if source == "Hugging Face":
dataset_val = prepare_dataset("huggingface", hf_path, tokenizer_val, hf_token)
elif source == "Local File":
if local_file is not None:
dataset_val = prepare_dataset("local", local_file.name, tokenizer_val)
else:
return "No file uploaded. Please upload a local dataset file."
else:
return "Invalid dataset source selected."
return dataset_val, "Dataset prepared successfully!"
prepare_dataset_btn.click(
prepare_dataset_wrapper,
inputs=[dataset_source, hf_dataset_path, local_dataset_path, hf_token, tokenizer],
outputs=[dataset, prepare_dataset_output]
)
def create_synthetic_dataset_wrapper(examples, expected_structure, num_samples, ai_provider, api_key, ollama_model, tokenizer_val):
if tokenizer_val is None:
return "Error: Model and tokenizer not loaded. Please load the model first."
dataset_val = create_synthetic_dataset(examples, expected_structure, num_samples, ai_provider, api_key, ollama_model)
return dataset_val, "Synthetic dataset created successfully!"
create_dataset_btn.click(
create_synthetic_dataset_wrapper,
inputs=[examples, expected_structure, num_samples, ai_provider, api_key, ollama_model, tokenizer],
outputs=[dataset, create_dataset_output]
)
ai_provider.change(update_ollama_visibility, inputs=[ai_provider], outputs=[ollama_model])
def train_model_wrapper(model_val, tokenizer_val, dataset_val, learning_rate, batch_size, num_epochs):
if model_val is None or tokenizer_val is None:
return "Error: Model and tokenizer not loaded. Please load the model first."
if dataset_val is None:
return "Error: Dataset not prepared. Please prepare or create a dataset first."
try:
trainer = finetune_model(model_val, tokenizer_val, dataset_val, learning_rate, batch_size, num_epochs)
return "Training completed successfully!"
except Exception as e:
return f"Error during training: {str(e)}"
train_btn.click(
train_model_wrapper,
inputs=[model, tokenizer, dataset, learning_rate, batch_size, num_epochs],
outputs=[train_output]
)
def test_model_wrapper(model_val, tokenizer_val, test_input):
if model_val is None or tokenizer_val is None:
return "Error: Model and tokenizer not loaded. Please load the model first."
FastLanguageModel.for_inference(model_val) # Enable native 2x faster inference
messages = [{"role": "user", "content": test_input}]
inputs = tokenizer_val.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_tensors="pt"
).to("cuda" if torch.cuda.is_available() else "cpu")
outputs = model_val.generate(input_ids=inputs, max_new_tokens=128, temperature=1.5, min_p=0.1)
return tokenizer_val.batch_decode(outputs)[0]
test_btn.click(
test_model_wrapper,
inputs=[model, tokenizer, test_input],
outputs=[test_output]
)
def convert_to_gguf_wrapper(model_val, tokenizer_val, gguf_output_path, gguf_quant_method):
if model_val is None or tokenizer_val is None:
return "Error: Model and tokenizer not loaded. Please load the model first."
output = convert_to_gguf(model_val, tokenizer_val, gguf_output_path, gguf_quant_method)
return output
gguf_convert_btn.click(
convert_to_gguf_wrapper,
inputs=[model, tokenizer, gguf_output_path, gguf_quant_method],
outputs=[gguf_output]
)
def upload_to_hf_wrapper(model_val, tokenizer_val, repo_name, hf_token, model_type, gguf_file_path):
if model_type == "Fine-tuned Model":
if model_val is None or tokenizer_val is None:
return "Error: Model and tokenizer not loaded. Please load the model first."
result = upload_to_huggingface(model_val, tokenizer_val, repo_name, hf_token)
elif model_type == "GGUF Converted Model":
if not gguf_file_path:
return "Error: GGUF file path not provided. Please enter the path to the GGUF file."
result = upload_gguf_to_huggingface(gguf_file_path, repo_name, hf_token)
else:
return "Error: Invalid model type selected."
return result
upload_btn.click(
upload_to_hf_wrapper,
inputs=[model, tokenizer, repo_name, hf_token, model_type, gguf_file_path],
outputs=[upload_output]
)
return demo
if __name__ == "__main__":
demo = create_gradio_interface()
demo.launch()