从概率到似然用Python手把手实现LLR算法附完整代码在信号处理和机器学习领域理解概率和似然的区别是掌握分类算法的关键。对数似然比(LLR)作为一种强大的统计工具能够帮助我们量化不同假设下的数据支持程度。本文将带你从基础概念出发通过Python代码实现完整的LLR计算流程让你不仅能理解理论还能立即应用到实际项目中。1. 概率与似然概念辨析概率和似然这两个术语经常被混淆但它们代表着完全不同的思维方式。想象你手中有一个骰子概率问题已知骰子是公平的每个面1/6概率问掷出3点的概率是多少似然问题掷了100次骰子观察到3点出现了30次问这个骰子是否公平用数学表达式表示概率函数P(X3|θ1/6) 1/6似然函数L(θ1/6|X3) (1/6)^30 * (5/6)^70关键区别概率固定参数求数据可能性似然固定数据求参数可能性在Python中我们可以用scipy.stats模块来计算二项分布的似然from scipy.stats import binom # 概率计算 prob binom.pmf(k30, n100, p1/6) # 0.048 # 似然计算 def likelihood(p, k30, n100): return binom.pmf(k, n, p) p_values [0.1, 0.2, 0.3, 1/6] likes [likelihood(p) for p in p_values] # [0.0005, 0.099, 0.007, 0.048]2. 对数似然比(LLR)原理详解LLR的核心思想是比较两个竞争假设下数据的支持程度。给定数据X和两个假设H₀、H₁LLR定义为LLR log[L(H₁|X)/L(H₀|X)]这个比值告诉我们数据支持H₁相对于H₀的程度。LLR有几个重要特性LLR 0支持H₁LLR 0支持H₀绝对值越大支持程度越强在通信系统中LLR常用于信号检测。考虑QPSK调制场景发送符号星座点坐标00(1,1)01(-1,1)10(1,-1)11(-1,-1)当接收到信号r(0.2,0.2)时计算各符号的LLRimport numpy as np def qpsk_llr(r, sigma1): # 四个可能发送的符号 symbols np.array([[1,1], [-1,1], [1,-1], [-1,-1]]) # 计算每个符号的LLR分量 llr_b0 np.log(np.exp(-((r[0]-1)**2 (r[1]-1)**2)/(2*sigma**2)) np.exp(-((r[0]-1)**2 (r[1]1)**2)/(2*sigma**2))) - \ np.log(np.exp(-((r[0]1)**2 (r[1]-1)**2)/(2*sigma**2)) np.exp(-((r[0]1)**2 (r[1]1)**2)/(2*sigma**2))) llr_b1 np.log(np.exp(-((r[0]-1)**2 (r[1]-1)**2)/(2*sigma**2)) np.exp(-((r[0]1)**2 (r[1]-1)**2)/(2*sigma**2))) - \ np.log(np.exp(-((r[0]-1)**2 (r[1]1)**2)/(2*sigma**2)) np.exp(-((r[0]1)**2 (r[1]1)**2)/(2*sigma**2))) return np.array([llr_b0, llr_b1]) r np.array([0.2, 0.2]) print(qpsk_llr(r)) # 输出[0.8, 0.8] 倾向于003. Python实现LLR完整流程让我们构建一个完整的数字通信系统仿真从编码到解码重点展示LLR的应用import numpy as np import matplotlib.pyplot as plt from scipy.special import logsumexp class LLRDecoder: def __init__(self, constellation, noise_var0.1): self.constellation constellation self.noise_var noise_var def compute_llr(self, received): llrs [] for i in range(int(np.log2(len(self.constellation)))): # 将星座点分为bit i为0和1两组 s0 [s for s in self.constellation if not (s[0] i) 1] s1 [s for s in self.constellation if (s[0] i) 1] # 计算LLR llr (logsumexp([-np.linalg.norm(received-s)**2/(2*self.noise_var) for s in s0]) - logsumexp([-np.linalg.norm(received-s)**2/(2*self.noise_var) for s in s1])) llrs.append(llr) return np.array(llrs) # 定义16QAM星座图 def create_16qam(): a np.arange(-3, 4, 2) constellation [] for i in a: for j in a: constellation.append((i 1j*j, f{bin((i3)//2)[2:].zfill(2)}{bin((j3)//2)[2:].zfill(2)})) return constellation # 仿真流程 constellation create_16qam() decoder LLRDecoder(constellation, noise_var0.1) # 随机选择发送符号 tx_symbol constellation[10][0] # 发送1010对应的星座点 rx_symbol tx_symbol np.sqrt(0.1)*np.random.randn() 1j*np.sqrt(0.1)*np.random.randn() # 计算LLR llr decoder.compute_llr(np.array([rx_symbol.real, rx_symbol.imag])) print(f接收信号: {rx_symbol}, LLR结果: {llr}) # 可视化 plt.figure(figsize(10,6)) for sym in constellation: plt.plot(sym[0].real, sym[0].imag, bo) plt.text(sym[0].real, sym[0].imag, sym[1]) plt.plot(rx_symbol.real, rx_symbol.imag, r*, markersize12) plt.title(16QAM星座图与接收信号) plt.grid(True) plt.show()4. LLR在机器学习中的应用LLR不仅用于通信系统在机器学习分类问题中同样重要。以逻辑回归为例它本质上是在学习一个LLR模型P(Y1|X) σ(w·X) 1/(1exp(-w·X))LLR log(P(Y1|X)/P(Y0|X)) w·X实现一个带LLR输出的逻辑回归分类器from sklearn.linear_model import LogisticRegression from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 生成模拟数据 X, y make_classification(n_samples1000, n_features10, n_informative5, random_state42) X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 训练模型 model LogisticRegression() model.fit(X_train, y_train) # 获取LLR def get_llr(model, X): decision model.decision_function(X) # 这就是LLR proba model.predict_proba(X) return decision, proba llr, proba get_llr(model, X_test[:5]) print(f测试样本LLR:\n{llr}\n对应概率:\n{proba}) # 可视化LLR与分类性能 from sklearn.metrics import roc_curve, auc fpr, tpr, _ roc_curve(y_test, model.decision_function(X_test)) roc_auc auc(fpr, tpr) plt.figure() plt.plot(fpr, tpr, labelfROC曲线 (AUC {roc_auc:.2f})) plt.plot([0, 1], [0, 1], k--) plt.xlabel(假正率) plt.ylabel(真正率) plt.title(基于LLR的ROC曲线) plt.legend() plt.show()5. 工程实践中的优化技巧在实际系统中LLR计算需要考虑数值稳定性和计算效率对数域计算避免小概率相乘导致下溢# 不好的实现 def naive_llr(p1, p0): return np.log(p1/p0) # p1和p0可能非常小 # 好的实现 def stable_llr(log_p1, log_p0): return log_p1 - log_p0查表法预先计算常见值的LLR# 创建LLR查找表 x np.linspace(-5, 5, 1000) llr_table np.log(1 np.exp(-x)) - np.log(1 np.exp(x)) def approx_llr(x): idx np.searchsorted(x, x) return llr_table[idx]简化近似在特定条件下使用近似公式对于BPSK调制LLR可近似为LLR ≈ 2*y/σ²对于高信噪比max-log近似log(e^a e^b) ≈ max(a,b)并行计算利用GPU加速批量LLR计算import cupy as cp def gpu_llr(rx_signal, constellation, noise_var): rx_gpu cp.asarray(rx_signal) const_gpu cp.asarray([c[0] for c in constellation]) # 批量计算所有星座点的距离 dist cp.sum((rx_gpu[:, None] - const_gpu)**2, axis2) # 计算LLR llr cp.empty((len(rx_signal), 4)) for i in range(4): # 16QAM每个符号4bit mask0 [(int(c[1][i]) 0) for c in constellation] mask1 [(int(c[1][i]) 1) for c in constellation] llr[:,i] (logsumexp(-dist[:,mask0]/(2*noise_var), axis1) - logsumexp(-dist[:,mask1]/(2*noise_var), axis1)) return cp.asnumpy(llr)在真实项目中我通常会先实现精确版本验证算法正确性然后根据性能需求逐步引入这些优化。特别是在处理大规模数据时GPU加速能带来数量级的性能提升。