{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# FeatureLens v0.16 causal-position addendum\n", "\n", "Use this notebook **only after the v0.15 full offline study completed**. It preserves that final-token causal baseline and runs the smaller max-feature-activation causal addendum.\n", "\n", "It does **not** recollect the 224-prompt activation matrices, refit probes/features, rerun candidate stability, or rerun the 1/3/5 feature-set benchmark.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 1. Verify the Colab GPU runtime.\n", "!nvidia-smi\n", "import torch\n", "print(\"CUDA available:\", torch.cuda.is_available())\n", "if not torch.cuda.is_available():\n", " raise RuntimeError(\"Enable a GPU runtime before continuing.\")\n", "print(\"GPU:\", torch.cuda.get_device_name(0))\n", "print(\"VRAM GiB:\", torch.cuda.get_device_properties(0).total_memory / 1024**3)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 2. Mount Google Drive.\n", "from google.colab import drive\n", "drive.mount(\"/content/drive\")\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 3. Configuration \u2014 edit REPO_URL if needed.\n", "from pathlib import Path\n", "\n", "REPO_URL = \"PASTE_YOUR_GIT_REPO_URL_HERE\"\n", "BRANCH = \"main\"\n", "SOURCE_RUN_NAME = \"FeatureLens_offline_v015\"\n", "ADDENDUM_RUN_NAME = \"FeatureLens_offline_v016\"\n", "\n", "REPO_DIR = Path(\"/content/FeatureLens\")\n", "DRIVE_ROOT = Path(\"/content/drive/MyDrive\")\n", "SOURCE_ARTIFACTS = DRIVE_ROOT / SOURCE_RUN_NAME / \"artifacts\"\n", "ADDENDUM_ROOT = DRIVE_ROOT / ADDENDUM_RUN_NAME\n", "ADDENDUM_ARTIFACTS = ADDENDUM_ROOT / \"artifacts\"\n", "LOG_PATH = ADDENDUM_ROOT / \"causal_addendum.log\"\n", "\n", "if REPO_URL.startswith(\"PASTE_\"):\n", " raise ValueError(\"Set REPO_URL to your FeatureLens Git repository URL first.\")\n", "if not SOURCE_ARTIFACTS.exists():\n", " raise FileNotFoundError(\n", " f\"Could not find the completed v0.15 artifacts at {SOURCE_ARTIFACTS}. \"\n", " \"Change SOURCE_RUN_NAME if your previous Drive folder used another name.\"\n", " )\n", "ADDENDUM_ARTIFACTS.mkdir(parents=True, exist_ok=True)\n", "print(\"Source:\", SOURCE_ARTIFACTS)\n", "print(\"Addendum:\", ADDENDUM_ARTIFACTS)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 4. Clone or refresh the v0.16 FeatureLens source.\n", "import subprocess, shutil\n", "\n", "if not REPO_DIR.exists():\n", " subprocess.run([\"git\", \"clone\", \"--branch\", BRANCH, \"--single-branch\", REPO_URL, str(REPO_DIR)], check=True)\n", "else:\n", " subprocess.run([\"git\", \"-C\", str(REPO_DIR), \"fetch\", \"origin\", BRANCH], check=True)\n", " subprocess.run([\"git\", \"-C\", str(REPO_DIR), \"checkout\", BRANCH], check=True)\n", " subprocess.run([\"git\", \"-C\", str(REPO_DIR), \"pull\", \"--ff-only\", \"origin\", BRANCH], check=True)\n", "print(\"Commit:\", subprocess.check_output([\"git\", \"-C\", str(REPO_DIR), \"rev-parse\", \"--short\", \"HEAD\"], text=True).strip())\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 5. Install runtime dependencies.\n", "import subprocess, sys\n", "subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"-r\", str(REPO_DIR / \"requirements.txt\")], check=True)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 6. Seed the addendum folder with only the small v0.15 study outputs.\n", "# Large activation caches are intentionally NOT copied.\n", "import shutil\n", "\n", "for path in SOURCE_ARTIFACTS.rglob(\"*\"):\n", " if not path.is_file():\n", " continue\n", " rel = path.relative_to(SOURCE_ARTIFACTS)\n", " if rel.parts and rel.parts[0] == \"activations\":\n", " continue\n", " if path.name.endswith((\".complete\", \".tmp\")):\n", " continue\n", " destination = ADDENDUM_ARTIFACTS / rel\n", " if not destination.exists():\n", " destination.parent.mkdir(parents=True, exist_ok=True)\n", " shutil.copy2(path, destination)\n", "\n", "required = [\"feature_catalog.csv\", \"layer_metrics.csv\", \"stability.csv\", \"selection_stability.csv\", \"feature_set_results.csv\"]\n", "missing = [name for name in required if not (ADDENDUM_ARTIFACTS / name).exists()]\n", "if missing:\n", " raise RuntimeError(f\"Previous study is missing required small artifacts: {missing}\")\n", "if not ((ADDENDUM_ARTIFACTS / \"causal_results.csv\").exists() or (ADDENDUM_ARTIFACTS / \"causal_results_final_token.csv\").exists()):\n", " raise RuntimeError(\"Previous study is missing its final-token causal baseline.\")\n", "print(\"Small v0.15 artifacts copied; large activations were skipped.\")\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 7. Point the repo's artifacts/ directory at the Drive-backed addendum folder.\n", "import shutil\n", "local_artifacts = REPO_DIR / \"artifacts\"\n", "if local_artifacts.is_symlink():\n", " local_artifacts.unlink()\n", "elif local_artifacts.exists():\n", " shutil.rmtree(local_artifacts)\n", "local_artifacts.symlink_to(ADDENDUM_ARTIFACTS, target_is_directory=True)\n", "print(\"artifacts ->\", local_artifacts.resolve())\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 8. Run only the max-active causal addendum + CPU synthesis.\n", "# Task-level checkpointing makes this resumable if Colab disconnects.\n", "import subprocess, sys, time\n", "\n", "command = [sys.executable, \"-m\", \"experiments.run_causal_addendum\", \"--resume\"]\n", "print(\"$\", \" \".join(command))\n", "print(\"Log:\", LOG_PATH)\n", "start = time.time()\n", "with LOG_PATH.open(\"a\", encoding=\"utf-8\") as log:\n", " log.write(\"\\n\\n=== FeatureLens v0.16 causal addendum ===\\n\")\n", " process = subprocess.Popen(command, cwd=REPO_DIR, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)\n", " assert process.stdout is not None\n", " for line in process.stdout:\n", " print(line, end=\"\")\n", " log.write(line)\n", " log.flush()\n", " return_code = process.wait()\n", "if return_code != 0:\n", " raise RuntimeError(f\"Addendum exited with code {return_code}. Fix the error and rerun this cell; --resume keeps completed causal tasks.\")\n", "print(f\"\\nCompleted in {(time.time()-start)/60:.1f} minutes.\")\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 9. Inspect the finalized position-sensitivity results.\n", "import json, pandas as pd\n", "from IPython.display import display, Markdown\n", "\n", "position = pd.read_csv(ADDENDUM_ARTIFACTS / \"causal_position_summary.csv\")\n", "study = pd.read_csv(ADDENDUM_ARTIFACTS / \"study_feature_summary.csv\")\n", "summary = json.loads((ADDENDUM_ARTIFACTS / \"study_summary.json\").read_text(encoding=\"utf-8\"))\n", "report = (ADDENDUM_ARTIFACTS / \"report.md\").read_text(encoding=\"utf-8\")\n", "\n", "display(position[position[\"concept\"] == \"__all__\"])\n", "display(study)\n", "display(summary)\n", "display(Markdown(report))\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 10. Create the final publishable result bundle.\n", "import zipfile\n", "\n", "PUBLISH_ZIP = ADDENDUM_ROOT / \"FeatureLens_offline_results_v016.zip\"\n", "with zipfile.ZipFile(PUBLISH_ZIP, \"w\", compression=zipfile.ZIP_DEFLATED) as zf:\n", " for path in sorted(ADDENDUM_ARTIFACTS.rglob(\"*\")):\n", " if not path.is_file():\n", " continue\n", " rel = path.relative_to(ADDENDUM_ARTIFACTS)\n", " if rel.parts and rel.parts[0] == \"activations\":\n", " continue\n", " if path.name.endswith((\".complete\", \".tmp\")):\n", " continue\n", " zf.write(path, arcname=str(Path(\"artifacts\") / rel))\n", "print(\"Final result bundle:\", PUBLISH_ZIP)\n", "print(f\"Size: {PUBLISH_ZIP.stat().st_size / 1024**2:.2f} MiB\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## After the addendum\n", "\n", "Download `FeatureLens_offline_results_v016.zip` and bring it back to the ChatGPT project before committing the empirical artifacts. The final public release should use the v0.16 report/summary rather than the older v0.15 headline.\n" ] } ], "metadata": { "accelerator": "GPU", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }