{
  "source_file": "0930",
  "source_platform": "个人博客",
  "author": "Hamel Husain / Parlance Labs",
  "title_listed": "Debugging AI With Adversarial Validation",
  "keywords": "对抗验证,调试,数据漂移",
  "domain": "数据处理",
  "content_type": "深度技术解析",
  "is_paid_flag": "否",
  "url": "https://hamel.dev/blog/posts/drift/",
  "publish_date": "2024-04",
  "corpus_stage": "数据处理",
  "notes": "用对抗验证调试AI系统",
  "id": "1c520c96c86c",
  "collected_at": "2026-10-02T14:28:47",
  "title": "Debugging AI With Adversarial Validation – Hamel's Blog",
  "body_markdown": "Debugging AI With Adversarial Validation\nLLMs\nQuickly detect a common bug in AI products using an automated technique.\nAuthor\nHamel Husain\nPublished\nApril 12, 2024\nFor years, I’ve relied on a straightforward method to identify sudden changes in model inputs or training data, known as “drift.” This method, Adversarial Validation\n1\n, is both simple and effective. The best part?\nIt requires no complex tools or infrastructure.\nExamples where drift can cause bugs in your AI:\nYour data for\nevaluations\nare materially different from the inputs your model receives in production, causing your evaluations to be misleading.\nUpdates to prompts, functions, RAG, and similar elements aren’t incorporated into your fine-tuning or training data, leading to unexpected model behavior in production.\nNo matter how careful you are, bugs can still slip through the cracks. A a high ROI activity is to routinely audit all your AI/ML projects for drift.\nHow It Works\nWarning\nUncool Warning\nThis method is so simple that it might seem uncool. You aren’t going to impress any data scientists. Despite this, it’s too valuable to ignore.\nThis slide from\nmy talk on MLOps tools\nexplains the technique behind Adversarial Validation\n2\n:\nSlide\nThe process is as follows:\nCollect two datasets to compare. For example:\nTraining data from two different fine-tuning runs\nTraining data vs. evaluation data\nTraining data vs. production data (organized into the same format)\nData from two different time-periods\nCreate features from the dataset. A basic example that creates features from\ntokens\nis illustrated\nhere\n.\n3\nGive dataset #1 a label of 0 and dataset #2 a label of 1.\nFit a binary classifier (random forest, logistic regression, etc) to discriminate between the two datasets.\nIf the classifier demonstrates sufficient predictive power (ex: AUC >=0.60), we know there is drift.\nIf you used an interpretable model (like logistic regression, random forest, etc.), you can inspect feature importance metrics to understand the root cause of the drift. If you use a more complex model (like a neural network), you can use\nSHAP values\nor other methods to understand what is causing the drift. I recommend starting with a simple interpretable model.\nWarning\nIf this process doesn’t detect drift, it doesn’t mean there isn’t drift. It just means that we couldn’t detect it with the model and features we used.\nMinimal Example:\nft_drift\nI work with lots of folks who are fine-tuning models using the OpenAI API. I’ve created a small CLI tool,\nft_drift\n, that detects drift between two\nmulti-turn chat formatted\njsonl files. Currently,\nft_drift\nonly detects drift in prompt templates, schemas and other token-based drift (as opposed to semantic drift). However, this is a good starting point to understand the general concept of adversarial validation. Here is a demo of this tool at work:\nThe demo is from a real-world example where an unintentional change in a prompt template caused unexpected behavior in a model. The demo shows the tool detecting a difference between two datasets,\nfile_a.jsonl\nand\nfile_b.jsonl\n. Afterward, a table of important tokens that account for the drift are shown, such as\nEND-UI-FORMAT\n,\nUI-FORMAT\n, etc. We were able to apply the tool and quickly find the root cause of the issue. The modeling code is embarrassingly simple and located at\nft_drift/model.py\n. The point is you don’t need sophisticated techniques to get started. You can then take this approach further by adding embeddings to your features to also detect semantic drift. Similarly, you could add additional features by hand like the number of conversation turns, length of messages, etc.\nFootnotes\nI first learned of this technique in 2016 from\nthis blog post\nby Zygmunt Zając. Throughout the years, I’ve seen this technique used in a variety of contexts, sometimes with different names.\n↩︎\nThis slide uses the word “skew” which is interchangeable with “drift” in this context.\n↩︎\nFor classic ML, if you are already training a model on this data, you likely have a feature engineering pipeline that you can reuse.\n↩︎",
  "metadata": {
    "title": "Debugging AI With Adversarial Validation – Hamel's Blog",
    "description": "Quickly detect a common bug in AI products using an automated technique.",
    "keywords": null,
    "author": "Hamel Husain",
    "og:type": "article",
    "og:title": "Debugging AI With Adversarial Validation –",
    "og:description": "Quickly detect a common bug in AI products using an automated technique.",
    "og:image": "https://hamel.dev/blog/posts/drift/images/drift_cover.png",
    "og:locale": "en_US",
    "og:image:height": "536",
    "og:image:width": "1024",
    "twitter:title": "Debugging AI With Adversarial Validation –",
    "twitter:description": "Quickly detect a common bug in AI products using an automated technique.",
    "twitter:image": "https://hamel.dev/blog/posts/drift/images/drift_cover.png",
    "twitter:creator": "@HamelHusain",
    "twitter:site": "@HamelHusain",
    "twitter:card": "summary_large_image",
    "twitter:image-height": "536",
    "twitter:image-width": "1024"
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [],
  "images": [
    {
      "status": "ok",
      "url": "https://hamel.dev/blog/posts/drift/images/drift_tfx.png",
      "bytes": 114729,
      "reused": false,
      "local_path": "attachments/1c520c96c86c/images/body-a1d9dc7ea009e61d6e4f.png",
      "src": "https://hamel.dev/blog/posts/drift/images/drift_tfx.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://hamel.dev/blog/posts/drift/images/drift_cli.gif",
      "bytes": 548165,
      "reused": false,
      "local_path": "attachments/1c520c96c86c/images/body-83fabb4d67662744fecd.gif",
      "src": "https://hamel.dev/blog/posts/drift/images/drift_cli.gif",
      "alt": "",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [
    {
      "url": "https://www.youtube.com/watch?v=GHk5HMW4XMA",
      "src": "https://www.youtube.com/watch?v=GHk5HMW4XMA",
      "status": "ok",
      "kind": "single_video",
      "local_path": "attachments/1c520c96c86c/videos/repair-GHk5HMW4XMA.mp4",
      "bytes": 66363229,
      "valid": true,
      "height": 1080,
      "width": 1670,
      "duration": "1637.101000",
      "reused": true,
      "reused_from": "xinxing-zimeiti-corpus/attachments/519f1d23188d/videos/repair-GHk5HMW4XMA.mp4"
    }
  ],
  "links": [
    {
      "href": "https://www.googletagmanager.com/ns.html?id=GTM-PKGWQMKL",
      "label": "",
      "type": "iframe"
    },
    {
      "href": "https://hamel.dev/blog/posts/evals/index.html",
      "label": "evaluations",
      "type": "a"
    },
    {
      "href": "https://www.youtube.com/watch?v=GHk5HMW4XMA",
      "label": "my talk on MLOps tools",
      "type": "a"
    },
    {
      "href": "https://platform.openai.com/tokenizer",
      "label": "tokens",
      "type": "a"
    },
    {
      "href": "https://github.com/hamelsmu/ft-drift/blob/main/ft_drift/model.py",
      "label": "here",
      "type": "a"
    },
    {
      "href": "https://shap.readthedocs.io/en/latest/",
      "label": "SHAP values",
      "type": "a"
    },
    {
      "href": "https://github.com/hamelsmu/ft-drift/",
      "label": "ft_drift",
      "type": "a"
    },
    {
      "href": "https://platform.openai.com/docs/guides/fine-tuning",
      "label": "multi-turn chat formatted",
      "type": "a"
    },
    {
      "href": "https://fastml.com/adversarial-validation-part-two/",
      "label": "this blog post",
      "type": "a"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:52:41Z",
  "source_url": "https://hamel.dev/blog/posts/drift/",
  "papers": [],
  "actual_video_files": [],
  "body_chars": 4084,
  "expected_body_images": 2,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/1c520c96c86c/pages/source.html",
  "local_html": "attachments/1c520c96c86c/pages/article.html",
  "body_base_path": "attachments/1c520c96c86c/pages/",
  "body_markdown_path": "attachments/1c520c96c86c/pages/article.md"
}