贝叶斯定理

发布于 25 天前  102 次阅读


你好!很高兴为你介绍贝叶斯定理。别被这个名字吓到了,它其实非常直观,是我们在生活中每天都在潜意识使用的思维工具。

简单来说,贝叶斯定理就是“根据新证据,不断更新我们看法”的数学公式。

为了让你这个“小白”也能轻松上手,我将内容分为了三个部分:

  1. 核心原理:用大白话解释公式。
  2. 直观案例:一个经典的医疗检测陷阱(反直觉)。
  3. Python 实战:从手写公式到使用机器学习库解决实际问题。

🧠 第一部分:贝叶斯定理的核心原理

想象一下,你是一个侦探。

  1. 先验概率 (Prior):在没有任何线索前,你觉得嫌疑人 A 是凶手的概率(比如基于过往经验,他是个惯犯,概率 60%)。
  2. 似然 (Likelihood):现在你发现了一个新证据(比如在现场发现了他的指纹)。如果他是凶手,出现这个指纹的概率有多大?(很高,比如 90%)。
  3. 后验概率 (Posterior):结合“他是惯犯”的先验和“指纹”这个新证据,你现在觉得他是凶手的概率变成了多少?(可能变成了 95%)。

贝叶斯公式就是这个更新过程:

P(假设|证据) = \frac{P(证据|假设) \times P(假设)}{P(证据)}

  • P(假设):先验概率(原来的看法)。
  • P(证据|假设):似然(证据对假设的支持程度)。
  • P(假设|证据):后验概率(更新后的看法)。

🩺 第二部分:一个“反直觉”的实战案例

在写代码前,我们先看一个经典的医疗诊断案例,这能帮你彻底理解为什么要用贝叶斯。

场景

  • 某种罕见病在人群中的发病率是 1%(先验概率)。
  • 检测试纸的准确率很高:如果患病,95% 测出阳性;如果没病,99% 测出阴性。
  • 问题:如果你去检测,结果是阳性,你真的患病的概率是多少?

直觉回答:95% 左右?
贝叶斯回答只有约 49%

为什么?
因为得病的人太少了(基数小)。在 10000 个人里,只有 100 个病人(95个测出阳性),但有 9900 个健康人(其中 1% 误报,即 99 个假阳性)。
所以,阳性的人里,真病人(95)和假病人(99)几乎一样多。


💻 第三部分:Python 具体实现

我们将通过三个层级的实战来掌握它:

  1. Level 1:纯 Python 函数(理解公式计算)。
  2. Level 2:NumPy 数值计算(处理连续数据)。
  3. Level 3:Scikit-Learn 机器学习(工业级文本分类)。

🛠️ 准备工作

你需要安装以下库(如果未安装):

pip install numpy pandas scikit-learn matplotlib

实战 1:医疗检测计算器 (基础函数版)

这是最基础的实现,直接翻译公式。

def bayes_theorem(prior_prob, likelihood, false_positive_rate):
    """
    计算后验概率
    :param prior_prob: 先验概率 P(患病)
    :param likelihood: 真阳性率 P(阳性|患病)
    :param false_positive_rate: 假阳性率 P(阳性|没病)
    """
    # 1. 计算分母:P(阳性) = P(阳性|患病)*P(患病) + P(阳性|没病)*P(没病)
    # 注意:P(没病) = 1 - P(患病)
    p_positive = (likelihood * prior_prob) + (false_positive_rate * (1 - prior_prob))

    # 2. 计算分子:P(阳性|患病) * P(患病)
    numerator = likelihood * prior_prob

    # 3. 计算后验概率
    posterior_prob = numerator / p_positive

    return posterior_prob

# --- 运行案例 ---
# 发病率 1%,真阳性率 95%,假阳性率 1% (即特异性 99%)
result = bayes_theorem(prior_prob=0.01, likelihood=0.95, false_positive_rate=0.01)

print(f"检测为阳性后,实际患病的概率是: {result:.2%}")
# 输出: 检测为阳性后,实际患病的概率是: 48.98%

实战 2:信号幅度估计 (NumPy 进阶版)

在工程中,我们常需要从噪声中估计一个数值(比如雷达信号强度)。这里我们用贝叶斯来估计一个高斯分布的均值。

场景:真实信号是 1.5,但我们观测到的数据充满了噪声。我们有一个先验猜测(均值是 1.0)。

import numpy as np
import matplotlib.pyplot as plt

# 1. 模拟数据生成
np.random.seed(42)
true_signal = 1.5
noise = np.random.randn(100) # 100个噪声样本
observations = true_signal + noise # 观测到的含噪信号

# 2. 定义先验 (Prior)
# 假设我们原本猜测信号在 1.0 左右,方差较大(不确定)
mu_prior = 1.0
var_prior = 2.0

# 3. 贝叶斯更新公式 (针对高斯分布的解析解)
# 后验方差 = 1 / (1/先验方差 + 样本数/噪声方差)
# 这里简化假设噪声方差为1
n = len(observations)
var_post = 1 / (1/var_prior + n)

# 后验均值 = 后验方差 * (先验均值/先验方差 + 观测总和/噪声方差)
mu_post = var_post * (mu_prior/var_prior + np.sum(observations))

print(f"先验猜测: {mu_prior}")
print(f"观测均值: {np.mean(observations):.3f}")
print(f"贝叶斯后验估计: {mu_post:.3f}")
# 结果会介于先验猜测和观测均值之间,且随着数据量增加,会越来越接近真实值

实战 3:垃圾邮件分类器 (Scikit-Learn 工业版)

这是贝叶斯定理在 AI 领域最著名的应用——朴素贝叶斯分类器。它假设文本中的每个词都是独立的(虽然这在语言学上不完全对,但效果极好)。

我们将构建一个简单的模型,判断一句话是“垃圾短信”还是“正常短信”。

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score, classification_report

# --- 1. 准备数据 ---
# 实际项目中这里会加载几千条数据,这里为了演示用少量数据
data = {
    'message': [
        "赢一台免费iPhone,点击链接", "上午10点开会", "限时优惠,免费领钱",
        "项目文件已发送", "恭喜中奖,速来领取", "今晚吃什么?",
        "发票开具通知", "只有今天!免费名额", "记得带钥匙"
    ],
    'label': ['spam', 'ham', 'spam', 'ham', 'spam', 'ham', 'ham', 'spam', 'ham']
}
df = pd.DataFrame(data)

# --- 2. 文本向量化 (把文字变成计算机能懂的数字) ---
# CountVectorizer 会统计每个词出现的次数 (词袋模型)
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(df['message']) # 特征矩阵
y = df['label'] # 标签

# --- 3. 训练模型 ---
# MultinomialNB 是专门用于文本分类的朴素贝叶斯算法
model = MultinomialNB()
model.fit(X, y)

# --- 4. 预测新消息 ---
test_messages = ["免费赢大奖", "明天早上开会"]
test_X = vectorizer.transform(test_messages)
predictions = model.predict(test_X)

print("预测结果:")
for msg, pred in zip(test_messages, predictions):
    print(f"'{msg}' -> {pred}")

# --- 5. (可选) 查看概率 ---
# 我们可以看它认为某条消息是垃圾邮件的概率有多大
probs = model.predict_proba(test_X)
print("\n详细概率:")
for i, msg in enumerate(test_messages):
    print(f"'{msg}' 是垃圾邮件的概率: {probs[i][1]:.2%}") # 假设 'spam' 是第2列

📌 总结

  1. 原理:贝叶斯就是“先猜一个(先验),再看证据(似然),最后修正(后验)”。
  2. 优势:它能很好地处理不确定性,并且随着数据越来越多,结论会越来越准。
  3. 应用
    • 简单计算:用 Python 函数直接套公式。
    • 数据分析:用 NumPy 处理分布估计。
    • AI 开发:用 sklearn.naive_bayes 做文本分类、情感分析等。

一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。