| name | deep-ignorance-safety-filtering |
| title | Deep Ignorance - Pretraining Data Filtering for Safety |
| version | 0.0.2 |
| engine | skillxiv-v0.0.2-claude-opus-4.6 |
| license | MIT |
| url | https://arxiv.org/abs/2508.06601 |
| keywords | ["safety","data-filtering","pretraining","adversarial-robustness","dual-use"] |
| description | Enhances model safety by filtering dual-use topics from pretraining data, creating tamper-resistant models robust to adversarial fine-tuning without degrading unrelated capabilities. |
Deep Ignorance: Pretraining Data Filtering for Safety
Core Concept
Deep Ignorance improves language model safety by removing sensitive information (such as biothreat-related content) directly from the pretraining dataset. This approach prevents harmful capabilities from being learned initially, creating more robust defenses against adversarial fine-tuning attacks compared to post-training safety methods alone.
Architecture Overview
- Multi-Stage Filtering Pipeline: Identify and remove dual-use topic content during pretraining
- Content Classification: Detect sensitive information (biotechnology, explosives, etc.)
- Selective Removal: Remove problematic content while preserving general knowledge
- Robustness Verification: Validate resistance to adversarial fine-tuning attacks
- Capability Preservation: Ensure unrelated model abilities remain intact
Implementation Steps
Step 1: Identify Sensitive Content Patterns
Detect dual-use topics in training data:
class SensitiveContentDetector:
def __init__(self):
super().__init__()
self.biothreat_keywords = [
'pathogens', 'gain-of-function', 'synthesis',
'virulence', 'transmissibility', 'weaponization'
]
self.explosives_keywords = [
'explosive synthesis', 'detonation', 'blast',
'explosive device construction'
]
self.chemical_keywords = [
'nerve agents', 'chemical synthesis', 'toxic',
'chemical weapon production'
]
def classify_document(self, text):
"""
Classify document content for sensitive topics.
Args:
text: Document text to analyze
Returns:
sensitivity_scores: Dict mapping threat categories to confidence
"""
sensitivity_scores = {}
text_lower = text.lower()
biothreat_matches = sum(1 for kw in self.biothreat_keywords
if kw in text_lower)
sensitivity_scores['biothreat'] = biothreat_matches / len(self.biothreat_keywords)
explosives_matches = ( kw .explosives_keywords
kw text_lower)
sensitivity_scores[] = explosives_matches / (.explosives_keywords)
chemical_matches = ( kw .chemical_keywords
kw text_lower)
sensitivity_scores[] = chemical_matches / (.chemical_keywords)
sensitivity_scores
():
re
sensitive_spans = []
sentences = text.split()
sent_idx, sentence (sentences):
scores = .classify_document(sentence)
max_score = (scores.values())
max_score > threshold:
start_pos = ((s) + s sentences[:sent_idx])
end_pos = start_pos + (sentence)
sensitive_spans.append({
: sentence.strip(),
: (scores, key=scores.get),
: max_score,
: start_pos,
: end_pos
})
sensitive_spans
Step 2: Implement Document Filtering
Filter training documents based on sensitivity:
class TrainingDataFilter:
def __init__(self, detector, threshold=0.3, preserve_percentage=0.05):
super().__init__()
self.detector = detector
self.threshold = threshold
self.preserve_percentage = preserve_percentage
def filter_training_corpus(self, dataset, output_path):
"""
Filter training corpus removing sensitive documents.
Args:
dataset: Training dataset with documents
output_path: Path to save filtered dataset
Returns:
filtering_stats: Statistics about filtering
"""
total_docs = len(dataset)
filtered_docs = []
removed_docs = []
filtered_stats = {
'total': total_docs,
'removed': 0,
'preserved': 0,
'by_category': {}
}
for doc_idx, doc in enumerate(dataset):
text = doc['text']
if isinstance(text, bytes):
text = text.decode('utf-8', errors='ignore')
sensitivity_scores = self.detector.classify_document(text)
max_sensitivity = max(sensitivity_scores.values())
top_category = (sensitivity_scores, key=sensitivity_scores.get)
max_sensitivity > .threshold:
np.random.random() < .preserve_percentage:
filtered_docs.append({
**doc,
: ,
: max_sensitivity,
: top_category
})
filtered_stats[] +=
:
removed_docs.append({
**doc,
: ,
: max_sensitivity
})
filtered_stats[] +=
filtered_stats[][top_category] = \
filtered_stats[].get(top_category, ) +
:
filtered_docs.append({
**doc,
: ,
: max_sensitivity,
: top_category
})
(output_path, ) f:
doc filtered_docs:
f.write(json.dumps(doc) + )
filtered_stats[] = (filtered_docs) / total_docs
filtered_docs, filtered_stats
():
original_vocab = ()
filtered_vocab = ()
doc original_dataset[:]:
original_vocab.update(doc[].split())
doc filtered_dataset[:]:
filtered_vocab.update(doc[].split())
vocab_loss = (original_vocab - filtered_vocab) / (original_vocab)
{
: - vocab_loss,
: ((d[]) d original_dataset) / ,
: ((d[]) d filtered_dataset) /
}
Step 3: Train Model on Filtered Data
Pretrain language model on filtered corpus:
class SafeModelTrainer:
def __init__(self, model, tokenizer):
super().__init__()
self.model = model
self.tokenizer = tokenizer
def pretrain_on_filtered_data(self, filtered_dataset, num_epochs=2):
"""
Pretrain language model on filtered corpus.
"""
optimizer = AdamW(self.model.parameters(), lr=5e-5)
scheduler = CosineAnnealingLR(optimizer, T_max=num_epochs * len(filtered_dataset))
for epoch in range(num_epochs):
total_loss = 0
num_batches = 0
for batch_idx, example in enumerate(filtered_dataset):
text = example['text']
inputs = self.tokenizer(
text,
max_length=2048,
truncation=True,
return_tensors='pt'
)
outputs = self.model(
input_ids=inputs['input_ids'],
labels=inputs['input_ids']
)
loss = outputs.loss
optimizer.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(self.model.parameters(), )
optimizer.step()
scheduler.step()
total_loss += loss.item()
num_batches +=
(batch_idx + ) % == :
()
.model
Step 4: Adversarial Robustness Verification
Test resistance to adversarial fine-tuning:
class AdversarialRobustnessVerifier:
def __init__(self, model, tokenizer):
super().__init__()
self.model = model
self.tokenizer = tokenizer
def adversarial_finetune_attack(self, model, jailbreak_prompts, num_steps=10000):
"""
Attempt to unlock dangerous capabilities through fine-tuning.
Args:
model: Target model
jailbreak_prompts: Prompts trying to trigger unsafe behavior
num_steps: Number of fine-tuning steps
Returns:
attack_success_rate: Fraction of prompts that succeeded
"""
optimizer = AdamW(model.parameters(), lr=1e-3)
successful_attacks = 0
for step in range(num_steps):
prompt = jailbreak_prompts[step % len(jailbreak_prompts)]
inputs = self.tokenizer(prompt, return_tensors='pt')
outputs = model.generate(
inputs['input_ids'],
max_length=100,
temperature=0.1
)
generated_text = self.tokenizer.decode(outputs[0])
if self.is_unsafe_output(generated_text):
successful_attacks += 1
inputs = self.tokenizer(prompt + generated_text, return_tensors='pt')
forward = model(inputs[], labels=inputs[])
(-forward.loss).backward()
optimizer.step()
successful_attacks / num_steps
():
unsafe_keywords = [
, ,
,
]
(kw text.lower() kw unsafe_keywords)
():
results = {}
attack_type, prompts jailbreak_test_suite.items():
success_rate = .adversarial_finetune_attack(model, prompts)
results[attack_type] = success_rate
avg_robustness = - ((results.values()) / (results))
{
: results,
: avg_robustness
}
Practical Guidance
Hyperparameters and Configuration:
- Sensitivity threshold: 0.3-0.5 (adjustable per domain)
- Preservation percentage: 2-10% for residual model awareness
- Training learning rate on filtered data: 5e-5
- Adversarial attack steps: 1000-10000 depending on threat model
When to Use Deep Ignorance:
- Systems handling dual-use technology domains (biotech, chemicals, explosives)
- High-security applications where robustness to fine-tuning attacks is critical
- Models requiring strong guarantees against capability unlocking
- Scenarios where post-training safety may not be sufficient
When NOT to Use:
- Domains where restricted knowledge is necessary for legitimate use
- Models intended to assist with sensitive research
- Systems where availability of information is more critical than safety
- Applications where model should maintain full knowledge of dual-use topics
Implementation Notes:
- Filter at document level, not token level (preserves coherence)
- Preserve small percentage of sensitive docs so model isn't completely ignorant
- Validate that filtering doesn't negatively impact unrelated downstream tasks
- Consider domain-specific filtering criteria rather than generic keywords
- Test robustness empirically before deployment
Reference
Paper: Deep Ignorance: Filtering Pretraining Data for Tamper-Resistant Safeguards
ArXiv: 2508.06601
Performance: Outperforms post-training safety methods by over an order of magnitude in adversarial robustness; resists 10,000-step adversarial attacks