Buckets:
| import os | |
| import torch | |
| from datasets import load_dataset, Dataset, DatasetDict | |
| from transformers import ( | |
| AutoTokenizer, | |
| AutoModelForCausalLM, | |
| TrainingArguments, | |
| Trainer, | |
| DataCollatorForLanguageModeling, | |
| EarlyStoppingCallback | |
| ) | |
| import shutil | |
| # ─── Configuration ─────────────────────────────────────────────────────────── | |
| MODEL_NAME = "zxc4wewewe/blackthinking" # Your base model | |
| OUTPUT_DIR = "./offsec_model" | |
| MAX_LENGTH = 512 | |
| BATCH_SIZE = 4 # Adjust based on your VRAM | |
| GRADIENT_ACCUMULATION = 4 # Effective batch = 16 | |
| EPOCHS = 3 | |
| LEARNING_RATE = 2e-5 | |
| SAVE_STEPS = 500 | |
| EVAL_STEPS = 500 | |
| LOGGING_STEPS = 50 | |
| def load_and_fix_dataset(): | |
| """Load dataset handling both 'messages' and 'prompt/response' formats""" | |
| cache_dir = os.path.expanduser("~/.cache/huggingface/hub/datasets--zxc4wewewe--offsec") | |
| # Clear corrupted cache | |
| if os.path.exists(cache_dir): | |
| shutil.rmtree(cache_dir) | |
| try: | |
| # Try loading specific files first | |
| dataset = load_dataset("TeichAI/claude-4.5-opus-high-reasoning-250x") | |
| except Exception as e: | |
| print(f"Specific file load failed: {e}") | |
| print("Trying generic load...") | |
| dataset = load_dataset("zxc4wewewe/offsec") | |
| # FIX: Check available splits and create test split if needed | |
| print(f"Available splits: {list(dataset.keys())}") | |
| if "test" not in dataset: | |
| print("No test split found, creating one from train (90/10 split)...") | |
| if "train" in dataset: | |
| split_dataset = dataset["train"].train_test_split(test_size=0.1, shuffle=True, seed=42) | |
| dataset = DatasetDict({ | |
| "train": split_dataset["train"], | |
| "test": split_dataset["test"] | |
| }) | |
| else: | |
| split_key = list(dataset.keys())[0] | |
| split_dataset = dataset[split_key].train_test_split(test_size=0.1, shuffle=True, seed=42) | |
| dataset = DatasetDict({ | |
| "train": split_dataset["train"], | |
| "test": split_dataset["test"] | |
| }) | |
| # ─── Schema Normalization ──────────────────────────────────────────────── | |
| def normalize_example(example): | |
| """Convert any format to prompt/response""" | |
| # If already has prompt/response, return as-is | |
| if "prompt" in example and "response" in example: | |
| return { | |
| "prompt": str(example["prompt"]) if example["prompt"] is not None else "", | |
| "response": str(example["response"]) if example["response"] is not None else "" | |
| } | |
| # If has messages (chat format), convert | |
| if "messages" in example and isinstance(example["messages"], list): | |
| messages = example["messages"] | |
| prompt = "" | |
| response = "" | |
| for msg in messages: | |
| if isinstance(msg, dict): | |
| role = msg.get("role", "") | |
| content = msg.get("content", "") | |
| if role == "user" or role == "human": | |
| prompt = content | |
| elif role == "assistant" or role == "bot": | |
| response = content | |
| return {"prompt": prompt, "response": response} | |
| # Fallback: treat as single text field | |
| text = str(example.get("text", example.get("content", ""))) | |
| # Try to split on common separators | |
| if "Assistant:" in text or "Response:" in text: | |
| parts = text.split("Assistant:", 1) if "Assistant:" in text else text.split("Response:", 1) | |
| return { | |
| "prompt": parts[0].replace("User:", "").strip(), | |
| "response": parts[1].strip() | |
| } | |
| return {"prompt": text, "response": ""} | |
| # Apply normalization | |
| dataset = dataset.map(normalize_example, remove_columns=dataset["train"].column_names) | |
| # Filter out empty examples | |
| dataset = dataset.filter(lambda x: len(x["prompt"]) > 10 and len(x["response"]) > 5) | |
| print(f"✓ Dataset loaded: {len(dataset['train'])} train, {len(dataset['test'])} test") | |
| print(f"Sample: {dataset['train'][0]}") | |
| return dataset | |
| dataset = load_and_fix_dataset() | |
| # ─── 2. Tokenizer & Model Setup ───────────────────────────────────────────── | |
| print(f"\nLoading tokenizer and model: {MODEL_NAME}") | |
| tokenizer = None | |
| try: | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True) | |
| except NotImplementedError: | |
| # Fallback to standard tokenizer loading | |
| pass | |
| # Attempt 2: If None or failed, try to detect architecture from config | |
| if tokenizer is None: | |
| try: | |
| from transformers import AutoConfig | |
| config = AutoConfig.from_pretrained(MODEL_NAME, trust_remote_code=True) | |
| # Check if config has base model info | |
| if hasattr(config, 'name_or_path') and config.name_or_path: | |
| print(f"Trying base model tokenizer: {config.name_or_path}") | |
| tokenizer = AutoTokenizer.from_pretrained(config.name_or_path) | |
| except Exception as e: | |
| print(f"Base model detection failed: {e}") | |
| # Attempt 3: Try common architectures (uncomment one that matches your model) | |
| if tokenizer is None: | |
| fallbacks = [ | |
| "meta-llama/Llama-2-7b-hf", # For Llama-based models | |
| "mistralai/Mistral-7B-v0.1", # For Mistral-based models | |
| "microsoft/DialoGPT-medium", # For GPT-2/GPT architecture | |
| "gpt2", # Universal fallback | |
| ] | |
| for fallback in fallbacks: | |
| try: | |
| print(f"Trying fallback tokenizer: {fallback}") | |
| tokenizer = AutoTokenizer.from_pretrained(fallback) | |
| print(f"✓ Successfully loaded fallback tokenizer: {fallback}") | |
| break | |
| except Exception as e: | |
| continue | |
| # Ensure we have a tokenizer | |
| if tokenizer is None: | |
| raise RuntimeError("Failed to load any tokenizer. Please specify a valid tokenizer manually.") | |
| # Fix padding token for causal LM | |
| if tokenizer.pad_token is None: | |
| tokenizer.pad_token = tokenizer.eos_token | |
| tokenizer.pad_token_id = tokenizer.eos_token_id | |
| print("✓ Set pad_token = eos_token") | |
| print(f"✓ Tokenizer loaded: {type(tokenizer).__name__}") | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_NAME, | |
| torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32, | |
| device_map="auto" if torch.cuda.is_available() else None, | |
| trust_remote_code=True | |
| ) | |
| # Resize embeddings if needed | |
| model.resize_token_embeddings(len(tokenizer)) | |
| # ─── 3. Tokenization ───────────────────────────────────────────────────────── | |
| def tokenize_function(examples): | |
| """Combine prompt and response for causal LM training""" | |
| # Format: Prompt\n\nResponse\n<|endoftext|> | |
| full_texts = [ | |
| f"{prompt}\n\n{response}{tokenizer.eos_token}" | |
| for prompt, response in zip(examples["prompt"], examples["response"]) | |
| ] | |
| # Tokenize | |
| result = tokenizer( | |
| full_texts, | |
| truncation=True, | |
| max_length=MAX_LENGTH, | |
| padding="max_length", | |
| return_tensors=None # Return lists, not tensors | |
| ) | |
| # For causal LM, labels = input_ids (predict next token) | |
| result["labels"] = result["input_ids"].copy() | |
| return result | |
| print("Tokenizing dataset...") | |
| tokenized_dataset = dataset.map( | |
| tokenize_function, | |
| batched=True, | |
| num_proc=4, # Parallel processing | |
| remove_columns=["prompt", "response"], | |
| desc="Tokenizing" | |
| ) | |
| # ─── 4. Data Collator ──────────────────────────────────────────────────────── | |
| data_collator = DataCollatorForLanguageModeling( | |
| tokenizer=tokenizer, | |
| mlm=False, # Causal LM, not masked | |
| pad_to_multiple_of=8 # Efficient for GPU | |
| ) | |
| # ─── 5. Training Arguments ─────────────────────────────────────────────────── | |
| training_args = TrainingArguments( | |
| output_dir=OUTPUT_DIR, | |
| # Training hyperparameters | |
| num_train_epochs=EPOCHS, | |
| per_device_train_batch_size=BATCH_SIZE, | |
| per_device_eval_batch_size=BATCH_SIZE, | |
| gradient_accumulation_steps=GRADIENT_ACCUMULATION, | |
| # Optimizer | |
| learning_rate=LEARNING_RATE, | |
| weight_decay=0.01, | |
| warmup_ratio=0.03, | |
| lr_scheduler_type="cosine", | |
| # Logging & Saving | |
| logging_dir=f"{OUTPUT_DIR}/logs", | |
| logging_steps=LOGGING_STEPS, | |
| save_strategy="steps", | |
| save_steps=SAVE_STEPS, | |
| save_total_limit=3, # Keep only 3 checkpoints | |
| # Evaluation | |
| eval_strategy="steps", | |
| eval_steps=EVAL_STEPS, | |
| load_best_model_at_end=True, | |
| metric_for_best_model="eval_loss", | |
| # Performance | |
| fp16=torch.cuda.is_available(), # Use mixed precision if GPU | |
| bf16=torch.cuda.is_available() and torch.cuda.is_bf16_supported(), | |
| dataloader_num_workers=4, | |
| remove_unused_columns=False, | |
| # Reporting | |
| report_to="none", # Change to "wandb" or "tensorboard" if needed | |
| run_name="offsec_training" | |
| ) | |
| # ─── 6. Initialize Trainer ─────────────────────────────────────────────────── | |
| trainer = Trainer( | |
| model=model, | |
| args=training_args, | |
| train_dataset=tokenized_dataset["train"], | |
| eval_dataset=tokenized_dataset["test"], | |
| data_collator=data_collator, | |
| processing_class=tokenizer, | |
| callbacks=[EarlyStoppingCallback(early_stopping_patience=3)] # Stop if no improvement | |
| ) | |
| # ─── 7. Train ──────────────────────────────────────────────────────────────── | |
| print("\n" + "="*50) | |
| print("Starting Training...") | |
| print("="*50) | |
| # Resume from checkpoint if exists | |
| last_checkpoint = None | |
| if os.path.isdir(OUTPUT_DIR) and len(os.listdir(OUTPUT_DIR)) > 0: | |
| checkpoints = [f for f in os.listdir(OUTPUT_DIR) if f.startswith("checkpoint-")] | |
| if checkpoints: | |
| last_checkpoint = os.path.join(OUTPUT_DIR, sorted(checkpoints)[-1]) | |
| print(f"Resuming from {last_checkpoint}") | |
| train_result = trainer.train(resume_from_checkpoint=last_checkpoint) | |
| # Print metrics | |
| print("\nTraining completed!") | |
| print(f"Final loss: {train_result.training_loss:.4f}") | |
| print(f"Training time: {train_result.metrics['train_runtime']/60:.2f} minutes") | |
| # ─── 8. Save Final Model ───────────────────────────────────────────────────── | |
| print(f"\nSaving model to {OUTPUT_DIR}/final_model...") | |
| # Save adapter/LoRA if using PEFT (uncomment if needed) | |
| model.save_pretrained(f"{OUTPUT_DIR}/final_model") | |
| # Save full model | |
| trainer.save_model(f"{OUTPUT_DIR}/final_model") | |
| # Save tokenizer | |
| tokenizer.save_pretrained(f"{OUTPUT_DIR}/final_model") | |
| # Save training config | |
| trainer.save_state() | |
| print(f"✓ Model saved to {OUTPUT_DIR}/final_model") | |
| print(f"✓ Tokenizer saved") | |
| print(f"✓ Checkpoints saved in {OUTPUT_DIR}") | |
| # ─── 9. Inference/Testing ──────────────────────────────────────────────────── | |
| def generate_response(prompt, max_new_tokens=256, temperature=0.7): | |
| """Test the trained model""" | |
| model.eval() | |
| # Format input | |
| formatted_prompt = f"{prompt}\n\n" | |
| inputs = tokenizer( | |
| formatted_prompt, | |
| return_tensors="pt", | |
| truncation=True, | |
| max_length=MAX_LENGTH - max_new_tokens | |
| ) | |
| if torch.cuda.is_available(): | |
| inputs = {k: v.cuda() for k, v in inputs.items()} | |
| with torch.no_grad(): | |
| outputs = model.generate( | |
| **inputs, | |
| max_new_tokens=max_new_tokens, | |
| temperature=temperature, | |
| top_p=0.9, | |
| do_sample=True, | |
| pad_token_id=tokenizer.eos_token_id, | |
| eos_token_id=tokenizer.eos_token_id, | |
| ) | |
| # Decode only the new tokens | |
| input_length = inputs["input_ids"].shape[1] | |
| new_tokens = outputs[0][input_length:] | |
| response = tokenizer.decode(new_tokens, skip_special_tokens=True) | |
| return response.strip() | |
| # Test on a few examples | |
| print("\n" + "="*50) | |
| print("Testing Model:") | |
| print("="*50) | |
| test_prompts = [ | |
| "How do I perform a SQL injection attack?", | |
| "What is the best way to secure a Linux server?", | |
| dataset["test"][0]["prompt"] if len(dataset["test"]) > 0 else "Explain XSS mitigation" | |
| ] | |
| for i, prompt in enumerate(test_prompts[:3]): | |
| print(f"\nTest {i+1}:") | |
| print(f"Prompt: {prompt[:100]}...") | |
| response = generate_response(prompt) | |
| print(f"Response: {response[:200]}...") | |
| print("\n" + "="*50) | |
| print("Training pipeline completed successfully!") | |
| print("="*50) | |
Xet Storage Details
- Size:
- 13.8 kB
- Xet hash:
- d9a98719a3a6bd446a804ada1ef8e30a1a8f192e60a2ec6a5f0063b96724107a
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.