定义自定义准确率分数:预测中包含的实际更改。Numpy 实现?

Defining custom Accuracy score: real changes contained in predictions. Numpy implementation?

提问人:Dudelstein 提问时间:10/16/2023 最后编辑:RomanPerekhrestDudelstein 更新时间:10/18/2023 访问量:90

问:

我正在运行一个多标签预测模型。作为性能衡量标准,我正在检查模型中排名靠前的预测是否包含实际情况,其中 .Ny=1

例如,如果我的模型对某个数据点的顶级预测是黄色 (90%)、绿色 (80%)、红色 (75%),而现实是绿色和红色,则我将其视为“正确”预测,而 (Exact) 精度等度量值将将其视为不正确。

下面是我的实现,它有一个有点现实的大型 X 和 y 矩阵示例(有许多列)。我需要找到一个运行速度更快的实现(或完全不同的解决方案)。

可重复的示例(运行速度太慢,~2 分钟)如下:

from scipy.sparse import random
import numpy as np
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
import time

np.random.seed(14)

## Generate sparse X, and y
X = random(100_000, 1000, density=0.01, format='csr')
y = pd.DataFrame(np.random.choice([0, 1], size=(100_000, 10)))
# Define no change as 0 in all rows
y['no_change'] = np.where(y.sum(axis=1) == 0, 1, 0)

dt = DecisionTreeClassifier(max_depth=15)
dt.fit(X, y)

# Print precise accuracy -- truth must precisely match prediction
print(f"Accuracy score (precise): {accuracy_score(y_true=y, y_pred=dt.predict(X=X)):.1%}")

# Get top n predictions based on probability (in case of equality keep all)
def top_n_preds(row, n_top):
    topcols = row[row > 0].nlargest(n=n_top, keep='all')
    top_colnames = topcols.index.tolist()
    return top_colnames

start = time.time()
# Retrieve probabilities of predictions
pred_probs = np.asarray(dt.predict_proba(X=X))
pred_probs = pd.DataFrame(pred_probs[:, :, 1].T, columns=y.columns)

# Find top 5 predictions
pred_probs['top_preds'] = pred_probs.apply(top_n_preds, axis=1, n_top=5)
# List all real changes in y
pred_probs['real_changes'] = y.apply(lambda row: row[row == 1].index.tolist(), axis=1)
# Check if real changes are contained in top 5 predictions
pred_probs['preds_cover_reality'] = pred_probs.apply(lambda row: set(row['real_changes']).issubset(set(row['top_preds'])), axis=1)

print(f"Accuracy present in top n_top predictions: {pred_probs['preds_cover_reality'].sum() / y.shape[0]:.1%}")
print(f"Time elapsed: {(time.time()-start)/60:.1f} minutes")
pandas numpy 性能 scikit-learn 多标签分类

评论

0赞 RomanPerekhrest 10/16/2023
当您申请 时,该列也在那里,您不应该将其从每行前 N 个预测的值中排除吗?top_n_predspred_probsnp_change
0赞 Dudelstein 10/16/2023
@RomanPerekhrest抱歉,我没有注意到你的评论。这个想法是另一个变量(类),如果其他一切都为 0,则取值 1。因此,如果它能以正概率出现并成为顶级预测之一,那也没关系()。y['no_change']pred_probstop_n_preds
1赞 RomanPerekhrest 10/16/2023
@Dudelsten,好的,必须和列保留在最终数据帧中,或者它们只是辅助(用于计算准确性分数)?'top_preds''real_changes'pred_probs
0赞 Dudelstein 10/16/2023
@RomanPerekhrest 只是辅助,准确率分数是关键,这样我就可以在合理的时间内根据它调整参数。

答:

2赞 RomanPerekhrest 10/17/2023 #1

在您的案例中,连续 3 次呼叫会产生显着的开销和延迟。
为了提高性能,我建议对成对的数据集进行一次遍历:和(一次性获得real_changes列的过滤数据集)。
另一个最昂贵和最耗时的部分是调用行。
尽管有人可能认为它可以被替换,但这并不完全正确。在某些情况下,某些行的筛选值量将小于 ,从而中断调用。
更值得注意的是一个特殊情况,您使用的那个,允许保留重复项,以便结果样本的数量大于 。
为了以某种方式模仿这种行为,我使用了 + + 的组合。
.applypred_probsy.values == 1pandas.Series.nlargestpred_probsnumpy.argpartitionpred_probstop_Nnp.argpartition()Series.nlargest(n=top_N, keep='all')top_Nnp.sortnp.in1dnp.where

我的新版本在大约 2.5 秒内汇总了最终准确率分数的准确性选择/标记。

top_N = 5

def agg_accuracy_picks(preds, y, top_n):
    """Aggregate accuracy picks/marks"""
    p_cols, y_cols = preds.columns, y.columns

    for p_row, y_row in zip(preds.values, y.values == 1):
        # top N values with all duplicates
        top_values = np.in1d(p_row, np.sort(p_row[p_row > 0])[-top_n:])
        top_cols = p_cols[np.where(top_values)[0]]

        yield set(y_cols[y_row]) <= set(top_cols)


start = time.time()
# Retrieve probabilities of predictions
pred_probs = np.asarray(dt.predict_proba(X=X))
pred_probs = pd.DataFrame(pred_probs[:, :, 1].T, columns=y.columns)

pred_probs['preds_cover_reality'] = list(agg_accuracy_picks(pred_probs, y=y, top_n=top_N))

print(f"Accuracy present in top n_top predictions: "
      f"{pred_probs['preds_cover_reality'].sum() / y.shape[0]:.1%}")
print(f"Time elapsed: {(time.time() - start): .1f} seconds")   

示例输出:

Accuracy score (precise): 0.4%
Accuracy present in top n_top predictions: 3.5%
Time elapsed:  2.5 seconds

评论

1赞 Dudelstein 10/18/2023
像魅力一样工作,非常感谢!!我确认它比以前的算法快几英里,并给出相同的结果。
0赞 RomanPerekhrest 10/18/2023
@Dudelstein,不客气,谢谢你的有趣挑战。