James-4u/Perturb

★ 0Forks 0GitHub ↗Compare

README

Perturb Subnet

Perturb is a Bittensor subnet where validators create adversarial-image challenges and miners return perturbed images under bounded distortion constraints.

This repository provides:

  • validator node implementation (neurons/validator.py)
  • baseline miner implementation (neurons/miner.py)
  • validator-side local LLM semantic verification service (tools/llm_endpoint_service.py)
  • one-command launchers for validator, miner, and llm endpoint

Architecture

Validator responsibilities

  • Pull challenge images from Pexels Search API (PERTURB_IMAGE_ENDPOINT)
  • Run fixed classifier (EfficientNetV2-M) on pulled image
  • Verify semantic consistency of model output vs prompt label through local llm_endpoint
  • Build and broadcast AttackChallenge synapse to selected miners
  • Verify miner responses and compute rewards
  • Maintain rolling histories and set on-chain weights periodically

Miner responsibilities

  • Receive AttackChallenge over Axon
  • Run baseline PGD-style attack
  • Return only perturbed_image_b64
  • Let validator handle all authoritative verification and scoring

Challenge lifecycle

  1. Validator samples a prompt from perturbnet/constants.py (PROMPTS)
  2. Validator fetches image from Pexels using query=<prompt> and random page/photo selection
  3. If API pull fails, validator falls back to assets/dog_1.jpg and sets prompt to dog
  4. Validator runs EfficientNetV2-M and gets exact model label string
  5. Validator calls local llm_endpoint (POST /verify-label) to confirm semantic match between model label and prompt
  6. On success, validator creates challenge where true_label is the exact EfficientNet label
  7. Validator sends challenge to sampled miners and scores returned perturbations

Hardware and System Requirements

Miner

  • Minimum: 4 vCPU, 16 GB RAM, 50 GB SSD, stable 20+ Mbps network
  • Recommended: 8 vCPU, 32 GB RAM, NVIDIA GPU with 8+ GB VRAM, 100+ GB SSD

Validator

  • Minimum: 8 vCPU, 32 GB RAM, NVIDIA GPU with 12+ GB VRAM, 100 GB SSD
  • Recommended: 16 vCPU, 64 GB RAM, NVIDIA GPU with 24+ GB VRAM, 200 GB SSD

Validator-side llm_endpoint

  • Minimum: 2 vCPU, 4 GB RAM (assuming model already served by Ollama)
  • Recommended: run on same private network/host as validator for low latency

Common software prerequisites

  • Python 3.10+
  • Node.js 18+ (includes npm) for PM2 installation
  • pip and virtualenv support (python -m venv)
  • OS build tools needed by Python wheels
  • For GPU usage: correct NVIDIA driver + CUDA stack compatible with installed PyTorch

Common Installation (Do Once)

Run role-specific setup once before starting nodes:

git clone https://github.com/0xsigurd/Perturb
cd Perturb

For miner setup:

bash ./scripts/setup_common.sh miner

For validator setup:

bash ./scripts/setup_common.sh validator

setup_common.sh behavior by role:

  • miner: creates .venv, installs Python/Bittensor dependencies only
  • validator: also installs PM2, Ollama, starts perturb-ollama, and pulls PERTURB_LLM_ENDPOINT_MODEL

If npm: command not found, install Node.js first, then rerun:

macOS (Homebrew):

brew install node
node --version
npm --version
bash ./scripts/setup_common.sh validator

Ubuntu/Debian:

sudo apt-get update
sudo apt-get install -y nodejs npm
node --version
npm --version
bash ./scripts/setup_common.sh validator

Installation and Setup (Validator Side)

This section is specifically for validator operators.

1) Configure and run local llm_endpoint

Create endpoint config:

cp scripts/llm_endpoint.env.example scripts/llm_endpoint.env

Edit scripts/llm_endpoint.env:

  • LLM_ENDPOINT_HOST (default 127.0.0.1)
  • LLM_ENDPOINT_PORT (default 8081)
  • OLLAMA_URL (default http://127.0.0.1:11434)
  • PERTURB_LLM_ENDPOINT_MODEL (default qwen2.5:1.5b-instruct)

Start llm_endpoint:

bash ./scripts/run_llm_endpoint.sh

Health check:

curl "http://127.0.0.1:8081/health"

2) Configure validator runtime

Create validator env:

cp scripts/validator.env.example scripts/validator.env

Edit required fields in scripts/validator.env:

  • WALLET_NAME
  • WALLET_HOTKEY
  • NETUID
  • NETWORK

Important validator-specific fields:

  • PERTURB_IMAGE_ENDPOINT
  • PERTURB_PEXELS_API_KEY (required)
  • PERTURB_PEXELS_PER_PAGE
  • PERTURB_PEXELS_PAGE_SPAN
  • PERTURB_PEXELS_IMAGE_VARIANT (medium, large, original, etc.)
  • PERTURB_LLM_ENDPOINT_URL (must point to your running llm endpoint, e.g. http://127.0.0.1:8081/verify-label)
  • PERTURB_LLM_ENDPOINT_MODEL
  • PERTURB_K_MINERS
  • PERTURB_HISTORY_SIZE
  • PERTURB_MIN_PROCESSED_COUNT
  • PERTURB_MIN_LINF_DELTA
  • PERTURB_MAX_LINF_DELTA
  • PERTURB_WANDB_ENABLED (true to enable validator metrics logging to Weights & Biases)
  • PERTURB_WANDB_PROJECT, PERTURB_WANDB_ENTITY, PERTURB_WANDB_RUN_NAME, PERTURB_WANDB_MODE
  • PERTURB_WANDB_LOG_CONSOLE (true to forward validator console logs to W&B as well)
  • LOG_LEVEL (DEBUG default, set INFO/WARNING/ERROR if you want quieter logs)

3) Start validator stack (llm_endpoint + validator)

bash ./scripts/run_validator.sh

Expected log behavior:

  • challenge generation messages
  • miner selection messages
  • per-miner score logs
  • periodic set_weights attempts

4) Validator-side notes

  • Verification is LLM-only by design; if llm_endpoint is down, challenge verification fails.
  • Keep fallback image assets/dog_1.jpg present for external image API outage handling.

Installation and Setup (Miner Side)

This section is specifically for miner operators.

1) Configure miner runtime

Create miner env:

cp scripts/miner.env.example scripts/miner.env

Edit required fields in scripts/miner.env:

  • WALLET_NAME
  • WALLET_HOTKEY
  • NETUID
  • NETWORK

Optional:

  • PYTHON_BIN
  • LOG_LEVEL (DEBUG default, set INFO/WARNING/ERROR if you want quieter logs)
  • MINER_EXTRA_ARGS

2) Start miner

bash ./scripts/run_miner.sh

Expected log behavior:

  • Serving miner axon...
  • Miner started. Waiting for validator queries.

3) Miner-side notes

  • Baseline miner is intentionally simple; competitive miners should optimize attack logic.
  • Miner does not run llm_endpoint; semantic verification is validator-side only.

API and Protocol Contracts

Image API contract (validator input source)

  • Pexels endpoint: GET https://api.pexels.com/v1/search
  • Required header: Authorization: <PEXELS_API_KEY> (raw key, no Bearer prefix)
  • Validator sends params: query, page, per_page
  • Validator reads photos[].src.<variant> and downloads the selected image bytes
  • No custom image_base64 API response is required; validator converts downloaded image bytes to base64 internally.

llm_endpoint contract (validator verification)

  • Endpoint: POST /verify-label
  • Request JSON:
{
  "prediction": "<efficientnet_label>",
  "target_label": "<prompt_label>",
  "llm_model": "<optional model hint>"
}
  • Response JSON must contain a boolean verdict key, typically:
{
  "is_match": true,
  "reason": "short explanation",
  "method": "ollama"
}

Operations endpoints:

  • GET /health
  • GET /metrics

Synapse contract (AttackChallenge)

Key fields sent to miners:

  • task_id
  • model_name (fixed EfficientNetV2-M)
  • prompt (broad label)
  • clean_image_b64
  • true_label (exact EfficientNet class label)
  • epsilon, norm_type, min_delta, timeout_seconds

Miner response field:

  • perturbed_image_b64

Scoring and Weighting

Per-response score (if verification passes):

  • Hard gates:
    • min_linf_delta <= norm <= min(epsilon, max_linf_delta)
    • ssim(clean, adv) >= min_ssim
    • psnr_db(clean, adv) >= min_psnr_db
    • predicted label must differ from the original label
  • linf_ratio = clamp((norm - min_linf_delta) / (min(epsilon, max_linf_delta) - min_linf_delta), 0, 1)
  • rmse_ratio = clamp(rmse / min(epsilon, max_linf_delta), 0, 1)
  • linf_score = (1 - linf_ratio)^2
  • rmse_score = (1 - rmse_ratio)^2
  • perturbation_score = weighted_avg(linf_score, rmse_score) using PERTURB_LINF_COMPONENT_WEIGHT and PERTURB_RMSE_COMPONENT_WEIGHT
  • speed_score = 1 - min(response_time / timeout, 1)
  • final = PERTURB_PERTURBATION_WEIGHT * perturbation_score + PERTURB_SPEED_WEIGHT * speed_score

Any verification or constraint failure gets 0.0.

Weight setting:

  • Only miners with processed_count > 100 are weight-eligible
  • Emission schedule: top-5 only with fixed shares 62%, 24%, 9%, 4%, 1% (ranks 6+ receive 0)
  • Final weights combine normalized rolling average and normalized rank bonus, then normalize to sum 1

Integration Smoke Test

Run after llm_endpoint is up:

python scripts/integration_smoke_test.py

The smoke test validates:

  • llm_endpoint health and semantic sanity checks
  • image fetch from configured image endpoint
  • local EfficientNetV2-M inference path
  • challenge semantic verification through llm endpoint

Troubleshooting

  • Validator fails verification loop: check PERTURB_LLM_ENDPOINT_URL and llm_endpoint health.
  • Frequent image API failures: verify PERTURB_IMAGE_ENDPOINT and PERTURB_PEXELS_API_KEY; fallback should load assets/dog_1.jpg.
  • No miner scoring activity: ensure miner hotkeys are registered and publicly reachable.
  • Dependency install issues: install CUDA/CPU-specific PyTorch build compatible with your host.
  • Slow verifier responses: reduce model size or place llm_endpoint closer to validator process.

Readiness

Use docs/READINESS_CHECKLIST.md before long-run validation or deployment.

Repository Map

  • neurons/validator.py: validator loop, challenge build, verification, scoring, set_weights
  • neurons/miner.py: baseline miner logic and Axon serving
  • perturbnet/protocol.py: AttackChallenge synapse schema
  • perturbnet/model.py: EfficientNet model load and label prediction helpers
  • perturbnet/image_io.py: base64 image encode/decode helpers
  • tools/llm_endpoint_service.py: validator-side semantic verification service
  • scripts/run_llm_endpoint.sh: start/restart llm endpoint with PM2 (auto-ensures Ollama + model)
  • scripts/run_validator.sh: start/restart validator with PM2
  • scripts/run_miner.sh: start/restart miner with PM2
  • scripts/setup_common.sh: role-aware bootstrap (miner = Python deps only, validator = adds PM2/Ollama/model)
  • scripts/integration_smoke_test.py: local integration test

Contributors

0xsigurd

Issues