mirror of
https://github.com/luxfi/torus-ml.git
synced 2026-10-30 15:15:23 +00:00
The distribution called itself torus-ml while shipping a package directory named concrete, so the name a user installs and the name they import disagreed — and most of the tree already said torus.ml, which could not resolve against src/concrete. Moved src/concrete to src/torus and pointed the include at it. The dependency was Zama's concrete-python from their index. It is torus-fhe from ours, which is what luxfhe/torus already ships: that frontend declares torus-fhe and ships a torus/fhe module, so every 'from torus.fhe' in this tree was already naming our compiler and only the dependency lagged. Two distributions share one namespace, which is the shape the upstream this forked from used and the reason the split works: torus-fhe gives torus.fhe, torus-ml gives torus.ml. Swept every other spelling — identifiers, enum members, workflow variables, filenames, notebooks. LICENSE and NOTICE keep the upstream attribution a fork owes; the figures carry the old name in JPEG metadata a text sweep cannot reach.
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