mirror of
https://github.com/luxfhe/workshop.git
synced 2026-10-30 17:06:44 +00:00
19 KiB
19 KiB
In [1]:
import random
import shutil
from pathlib import Path
import numpy as np
import torch
from datasets import load_dataset
from peft import LoraConfig, get_peft_model
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
DataCollatorForLanguageModeling,
Trainer,
TrainingArguments,
)
from utils_lora import generate_and_print
# Import LoraTrainer from the provided library
from torus.ml.torch.lora import LoraTrainerIn [2]:
# Set seed for reproducibility
SEED = 0
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
device = "cpu"
if torch.cuda.is_available():
torch.cuda.manual_seed_all(SEED)
device = "cuda"
elif torch.backends.mps.is_available():
device = "mps"
import torus_ml_extensions as fhext
cuda_fhext = fhext.is_cuda_enabled() and fhext.is_cuda_available() # pylint: disable=no-member
print(
"Original model linear layers execute with FHE on: ",
"cuda" if cuda_fhext else "cpu",
)
print("Non-FHE layers and the LoRA weight optimizer executed on: ", device)Original model linear layers execute with FHE on: cpu Non-FHE layers and the LoRA weight optimizer executed on: cpu
In [3]:
# Load the model and tokenizer
model_name = "meta-llama/Llama-3.2-1B"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
# Ensure the tokenizer has a pad token
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
model.config.pad_token_id = model.config.eos_token_id
# Freeze the original model's weights
for param in model.parameters():
param.requires_grad = Falsetokenizer_config.json: 0%| | 0.00/50.5k [00:00<?, ?B/s]
tokenizer.json: 0%| | 0.00/9.09M [00:00<?, ?B/s]
special_tokens_map.json: 0%| | 0.00/301 [00:00<?, ?B/s]
config.json: 0%| | 0.00/843 [00:00<?, ?B/s]
model.safetensors: 0%| | 0.00/2.47G [00:00<?, ?B/s]
generation_config.json: 0%| | 0.00/185 [00:00<?, ?B/s]
In [4]:
# Apply LoRA configuration
peft_config = LoraConfig(
r=8,
lora_alpha=32,
lora_dropout=0.01,
bias="none",
task_type="CAUSAL_LM",
target_modules="all-linear",
)
peft_model = get_peft_model(model, peft_config)In [5]:
# Load the dataset and tokenize it
dataset = load_dataset("json", data_files="data_finetune/dataset.jsonl", split="train")
def tokenize_function(examples):
return tokenizer(examples["text"], padding="longest", truncation=True)
tokenized_dataset = dataset.map(tokenize_function, batched=True)
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)Generating train split: 0 examples [00:00, ? examples/s]
Map: 0%| | 0/46 [00:00<?, ? examples/s]
In [6]:
# Define training arguments
EPOCHS = 10
PER_DEVICE_TRAIN_BATCH_SIZE = 4
training_args = TrainingArguments(
output_dir="./checkpoints",
num_train_epochs=EPOCHS,
per_device_train_batch_size=PER_DEVICE_TRAIN_BATCH_SIZE,
gradient_accumulation_steps=1,
save_total_limit=1,
use_cpu=True,
learning_rate=2e-4,
lr_scheduler_type="linear",
seed=SEED,
data_seed=SEED,
warmup_steps=10,
weight_decay=0.01,
prediction_loss_only=True,
)In [7]:
# Create optimizer and scheduler using HuggingFace's Trainer
hf_trainer = Trainer(
model=peft_model,
args=training_args,
train_dataset=tokenized_dataset,
data_collator=data_collator,
)
train_dataloader = hf_trainer.get_train_dataloader()
hf_trainer.create_optimizer_and_scheduler(num_training_steps=len(train_dataloader) * EPOCHS)
optimizer = hf_trainer.optimizer
lr_scheduler = hf_trainer.lr_scheduler
# Define a causal LM loss function
def causal_lm_loss(logits, labels, ignore_index=-100):
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
shift_logits = shift_logits.view(-1, shift_logits.size(-1))
shift_labels = shift_labels.view(-1)
loss = torch.nn.functional.cross_entropy(
shift_logits, shift_labels, ignore_index=ignore_index, reduction="mean"
)
return lossIn [8]:
# Print the initial generation with the base model
PROMPT = "from torus.ml.sklearn import LogisticRegression\n\nmodel = LogisticRegression("
print("Initial generation with base model:")
print(generate_and_print(PROMPT, model, tokenizer, seed=SEED))Initial generation with base model:
from torus.ml.sklearn import LogisticRegression model = LogisticRegression( eta=0.1, n_iter=1000, random_state=42) None
In [9]:
# Prepare input data for calibration
lengths = [len(item["input_ids"]) for item in tokenized_dataset]
if not all(length == lengths[0] for length in lengths):
raise ValueError("All examples must have the same length for calibration.")
BLOCK_SIZE = lengths[0]
input_tensor = torch.randint(
0, tokenizer.vocab_size, (PER_DEVICE_TRAIN_BATCH_SIZE, BLOCK_SIZE), dtype=torch.long
)
label_tensor = torch.randint(
0, tokenizer.vocab_size, (PER_DEVICE_TRAIN_BATCH_SIZE, BLOCK_SIZE), dtype=torch.long
)
attention_mask = torch.ones((PER_DEVICE_TRAIN_BATCH_SIZE, BLOCK_SIZE), dtype=torch.long)
inputset = {"input_ids": input_tensor, "attention_mask": attention_mask, "labels": label_tensor}
# Initialize LoraTrainer
training_args_dict = vars(training_args)
lora_trainer = LoraTrainer(
model=peft_model,
optimizer=optimizer,
loss_fn=causal_lm_loss,
lr_scheduler=lr_scheduler,
training_args=training_args_dict,
n_layers_to_skip_for_backprop=3,
)LoRA layers detected in the model.
In [10]:
# Compile the model with FHE
lora_trainer.compile(inputset, n_bits=8)Compiling FHE layers: 0%| | 0/221 [00:00<?, ?it/s]
In [11]:
# Train the model using LoraTrainer
print("Starting training using LoraTrainer...")
lora_trainer.train(train_dataloader, num_epochs=EPOCHS, fhe="disable", device=device)Starting training using LoraTrainer...
Training: 0%| | 0/10 [00:00<?, ?epoch/s]
Training completed. Final Avg Loss: 0.0885, FHE Mode: disable
In [12]:
# Compare generation before and after fine-tuning
peft_model.disable_adapter_layers()
print("Original model generation:")
print(generate_and_print(PROMPT, peft_model, tokenizer, seed=SEED))Original model generation:
from torus.ml.sklearn import LogisticRegression model = LogisticRegression( eta=0.1, max_iter=1000, random_state=1) None
In [13]:
peft_model.enable_adapter_layers()
print("Fine-tuned model generation:")
print(generate_and_print(PROMPT, peft_model, tokenizer, seed=SEED))Fine-tuned model generation:
from torus.ml.sklearn import LogisticRegression model = LogisticRegression( eta=0.01, n_bits=8) None
In [14]:
FHE_SEQUENCE_LENGTH = 16
def tokenize_function_fhe(examples):
return tokenizer(
examples["text"], padding="max_length", truncation=True, max_length=FHE_SEQUENCE_LENGTH
)
tokenized_dataset = dataset.map(tokenize_function_fhe, batched=True)
# Create a small data loader with a single example
hf_trainer = Trainer(
model=peft_model,
args=training_args,
train_dataset=tokenized_dataset.select(list(range(PER_DEVICE_TRAIN_BATCH_SIZE))),
data_collator=data_collator,
)
train_dataloader = hf_trainer.get_train_dataloader()Map: 0%| | 0/46 [00:00<?, ? examples/s]
In [15]:
# Execute fine-tuning, using the GPU when it is available
fhe_epochs = 1
import time
start = time.time()
lora_trainer.train(train_dataloader, num_epochs=fhe_epochs, fhe="execute")
duration = time.time() - start
print(
(
f"Trained on one encrypted batch of {PER_DEVICE_TRAIN_BATCH_SIZE} "
f"examples of {FHE_SEQUENCE_LENGTH} tokens in {duration} seconds on {device}"
)
)Training: 0%| | 0/1 [00:00<?, ?epoch/s]
Training completed. Final Avg Loss: 13.6442, FHE Mode: execute Trained on one encrypted batch of 4 examples of 16 tokens in 5321.782980680466 seconds on cpu
In [16]:
# Save the fine-tuned model
save_path = Path("deployment/llama_lora_finetuned")
if save_path.is_dir() and any(save_path.iterdir()):
shutil.rmtree(save_path)
lora_trainer.save_and_clear_private_info(save_path)
print("Model saved to:", save_path)Model saved to: deployment/llama_lora_finetuned