寻找最高Precision对应的数值阈值:模型选择与结果异常问题
问题与解决方案
数据集
(26.5625,0) (29.5625,0) (30.390625,0) (18.640625,0) (27.984375,0) (26.984375,0) (25.703125,0) (25.78125,0) (32.09375,0) (25.59375,0) (27.703125,0) (30.828125,0) (23.578125,0) (21.890625,0) (25.734375,0) (24.65625,0) (27.46875,0) (31.640625,0) (26.53125,0) (25.078125,0) (30.65625,0) (24.515625,0) (25.21875,0) (21.78125,0) (28.984375,0) (29.765625,0) (27.171875,1) (30.46875,1) (35.3125,1) (27.90625,1) (34.9375,1) (33.4375,1) (30.90625,1) (31.671875,1) (32.40625,1) (26.078125,1) (31.171875,1) (36.21875,1) (35.0625,1) (35.65625,1) (36.65625,1) (37.96875,1) (31.953125,1) (33.15625,1) (37.34375,1)
有序标签与Precision
ordered_labels: [1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] average precision: 0.7338
需求与问题
需要找到类似27.0的原始特征数值阈值,让尽可能多的1类样本排在阈值右侧,0类排在左侧,以此最大化Precision(比如完美排序[1,1,1,0,0,0]的Precision为1.0)。但当前用Logistic回归得到的是0.7这类概率阈值,而非目标数值阈值,询问是否应该改用Linear Regression或SVM。
当前输出结果
Precision: [0.33333333 0. 0. 1. ] Recall: [1. 0. 0. 0.] Threshold: [0.13154558 0.7006058 0.72969373]
当前代码
import matplotlib.pyplot as plt import numpy as np import pandas as pd from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import precision_recall_curve from ast import literal_eval # Create a simple dataset scores_labels_path = 'data.txt' X, y = [], [] with open(scores_labels_path) as file: for line in file: line = literal_eval(line.rstrip()) X.append(line[0]) y.append(line[1]) X = np.array(X).reshape(-1, 1) y = np.array(y) # X1, y1 = make_classification(n_samples=1000, random_state=42) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=7) lr = LogisticRegression(random_state=42) lr.fit(X_train, y_train) y_scores = lr.predict_proba(X_test) precision, recall, threshold = precision_recall_curve(y_test, y_scores[:, 1]) print("Precision: {}".format(precision)) print("Recall: {}".format(recall)) print("Threshold: {}".format(threshold))
解决方案
1. 无需更换模型:从Logistic回归转换得到数值阈值
Logistic回归的预测概率是sigmoid(z),其中z = w*x + b。当概率等于设定的阈值(比如默认0.5)时,z=0,此时对应的原始特征x值就是你要的数值阈值,计算公式为:
x_threshold = -截距b / 权重w
修改现有代码,加入以下部分即可得到数值阈值:
# 从训练好的Logistic模型中提取数值阈值 w = lr.coef_[0][0] b = lr.intercept_[0] x_threshold = -b / w print(f"原始特征数值阈值: {x_threshold:.4f}")
2. 单特征场景更优方案:直接遍历所有可能阈值
因为你的数据是单特征分类任务,直接遍历所有样本的特征值作为阈值,计算每个阈值对应的Precision,能找到全局最优解,无需依赖任何模型:
# 全量数据寻找最优数值阈值 data = sorted(zip(X.flatten(), y), key=lambda item: item[0]) xs = [x for x, y_label in data] ys = [y_label for x, y_label in data] max_precision = 0.0 best_threshold = None # 遍历所有可能的划分点 for idx in range(len(xs)): current_threshold = xs[idx] # 取所有x >= 当前阈值的样本作为预测为1的集合 predicted_positives = ys[idx:] if not predicted_positives: continue current_precision = sum(predicted_positives) / len(predicted_positives) # 更新最优阈值 if current_precision > max_precision: max_precision = current_precision best_threshold = current_threshold print(f"最优数值阈值: {best_threshold:.4f}") print(f"对应最高Precision: {max_precision:.4f}")
3. 为什么不选Linear Regression或SVM?
- Linear Regression:它是回归模型,输出连续值,不适合0-1分类任务,直接用其输出阈值划分会忽略分类任务的概率分布特性,效果远不如分类模型。
- SVM:虽然可以输出决策函数值并转换为数值阈值,但操作逻辑不如Logistic回归直观,且对于单特征场景,直接遍历阈值的方法效率更高、结果更明确。
内容的提问来源于stack exchange,提问作者AnaRhisT
相关产品推荐
相关产品推荐

