Model Extraction via Query Enumeration
Model extraction builds a surrogate model that replicates the behavior of a target model by querying it systematically and training on the collected input-output pairs. The resulting surrogate does not need to match the target architecture — it just needs to produce similar outputs for the queries an attacker cares about. A stolen surrogate can be used offline for adversarial example generation, further probing without rate limits, or simply as intellectual property theft of a proprietary fine-tuned model.
The attack requires API access to the target model and enough query budget to cover the input distribution you care about. Rate limits and cost are the main practical constraints.
Collect Input-Output Pairs
Start with a seed set of queries representative of the model's intended use case. For a customer support model, use support-style questions. For a code assistant, use coding prompts. Systematically expand the seed set by generating variations:
import requests, json, time
TARGET_IP = '$TARGET_IP'
TOKEN = '$TOKEN'
seed_queries = [
'How do I reset my password?',
'What is your refund policy?',
'How do I cancel my subscription?',
'Where can I find my invoice?',
'How do I contact support?',
]
dataset = []
for query in seed_queries:
r = requests.post(
f'https://{TARGET_IP}/api/chat',
headers={'Authorization': f'Bearer {TOKEN}', 'Content-Type': 'application/json'},
json={'messages': [{'role': 'user', 'content': query}]}
)
response = r.json()['choices'][0]['message']['content']
dataset.append({'input': query, 'output': response})
print(f'Collected: {query[:50]}')
time.sleep(0.5)
with open('/tmp/extraction_dataset.jsonl', 'w') as f:
for item in dataset:
f.write(json.dumps(item) + '\n')
print(f'\nCollected {len(dataset)} pairs')
Collected: How do I reset my password? Collected: What is your refund policy? Collected: How do I cancel my subscription? Collected: Where can I find my invoice? Collected: How do I contact support? Collected 5 pairs
Expand the Dataset with Systematic Variations
A surrogate trained on 5 pairs generalizes poorly. Expand the dataset by generating query variations across phrasing, formality, and topic coverage. Use a separate model to generate variation seeds if available:
import requests, json, time, itertools
TARGET_IP = '$TARGET_IP'
TOKEN = '$TOKEN'
base_topics = ['password', 'refund', 'subscription', 'invoice', 'support', 'account', 'billing', 'shipping']
phrasing = ['How do I {}?', 'I need help with {}', 'Can you explain {}?', 'What is the process for {}?', 'Tell me about {}']
queries = [p.format(t) for p, t in itertools.product(phrasing, base_topics)]
dataset = []
for query in queries:
try:
r = requests.post(
f'https://{TARGET_IP}/api/chat',
headers={'Authorization': f'Bearer {TOKEN}', 'Content-Type': 'application/json'},
json={'messages': [{'role': 'user', 'content': query}]},
timeout=10
)
response = r.json()['choices'][0]['message']['content']
dataset.append({'input': query, 'output': response})
time.sleep(0.3)
except Exception as e:
print(f'Error on query: {e}')
with open('/tmp/extraction_dataset.jsonl', 'a') as f:
for item in dataset:
f.write(json.dumps(item) + '\n')
print(f'Total pairs: {len(dataset)}')
Train a Surrogate Model
With a collected dataset, fine-tune a local base model on the input-output pairs. For text tasks, a small instruction-tuned model is sufficient to capture behavior on the query distribution you targeted:
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from trl import SFTTrainer
model_name = 'microsoft/phi-2'
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
dataset = load_dataset('json', data_files='/tmp/extraction_dataset.jsonl', split='train')
def format_sample(sample):
return {'text': f"User: {sample['input']}\nAssistant: {sample['output']}"}
dataset = dataset.map(format_sample)
training_args = TrainingArguments(
output_dir='/tmp/surrogate_model',
num_train_epochs=3,
per_device_train_batch_size=2,
save_steps=50,
logging_steps=10
)
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset,
dataset_text_field='text'
)
trainer.train()
model.save_pretrained('/tmp/surrogate_model')
print('Surrogate model saved')
The surrogate will closely replicate target behavior for queries similar to the training distribution. For queries outside that distribution it will fall back to base model behavior. Increase the dataset size and topic coverage to improve fidelity across a broader query range. The extracted surrogate can then be queried offline at unlimited rate for adversarial example generation or further analysis of the target model's decision boundaries.
References
-
Stealing Machine Learning Models via Prediction APIsarxiv.org/abs/1609.02943 (opens in new tab)
Original model extraction research covering query-based surrogate training methodology
-
TRL — SFTTrainer Documentationhuggingface.co/docs/trl/sft_trainer (opens in new tab)
Supervised fine-tuning trainer used for surrogate model training on extracted datasets
Was this helpful?
Your feedback helps improve this page.