如何使用sklearn的cross_validate仅在评分阶段对样本加权?
解决Sklearn交叉验证中仅基于真实样本评分的问题
问题原因
你遇到的ValueError是因为直接将全局的100个样本权重arr_weight传给make_scorer,但交叉验证时每个折的测试集只有20个样本,权重数组维度与测试集不匹配,导致报错。
解决方案
推荐手动实现交叉验证循环,这种方式直观且能精确控制训练/测试流程,完全匹配你的需求:
- 训练时使用所有训练样本(真实+增强),不区分权重;
- 评分时仅用测试集中的真实样本(增强样本权重设为0)。
修正后的代码
from sklearn import model_selection from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import r2_score, mean_squared_error import numpy as np # 生成模拟数据(修正回归任务的target为一维数组) n_smpl, n_feats = 100, 5 arr_source = np.random.random((n_smpl, n_feats)) arr_target = np.random.random(n_smpl) # 回归任务target应为一维 arr_weight = np.random.randint(0, 2, n_smpl) # 0=增强样本,1=真实样本 model = RandomForestRegressor() kfold_splitter = model_selection.KFold(n_splits=5, random_state=7, shuffle=True) # 存储各折的评分结果 cv_r2_scores = [] cv_mse_scores = [] # 遍历每个交叉验证折 for train_idx, test_idx in kfold_splitter.split(arr_source): # 拆分训练/测试集 X_train, X_test = arr_source[train_idx], arr_source[test_idx] y_train, y_test = arr_target[train_idx], arr_target[test_idx] # 训练模型:训练集包含所有样本,权重默认全为1 model.fit(X_train, y_train) # 预测测试集 y_pred = model.predict(X_test) # 获取当前测试集对应的权重(仅真实样本权重为1) test_weights = arr_weight[test_idx] # 计算加权评分 r2 = r2_score(y_test, y_pred, sample_weight=test_weights) mse = mean_squared_error(y_test, y_pred, sample_weight=test_weights) cv_r2_scores.append(r2) cv_mse_scores.append(mse) # 输出交叉验证结果 print(f"交叉验证平均R2: {np.mean(cv_r2_scores):.4f} ± {np.std(cv_r2_scores):.4f}") print(f"交叉验证平均MSE: {np.mean(cv_mse_scores):.4f} ± {np.std(cv_mse_scores):.4f}")
关键说明
- 手动拆分
train_idx和test_idx,确保每个折的权重数组test_weights与测试集维度完全匹配; - 训练时未传入
sample_weight,默认所有训练样本权重为1,符合你“训练不区分样本来源”的需求; - 评分时仅对测试集中的真实样本(
test_weights=1)计算指标,增强样本(test_weights=0)不影响评分结果。
替代方案(不推荐)
如果坚持使用cross_validate,可以通过闭包传递权重数组,但需注意样本无重复值的前提:
from sklearn import model_selection from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import r2_score, mean_squared_error, make_scorer import numpy as np n_smpl, n_feats = 100, 5 arr_source = np.random.random((n_smpl, n_feats)) arr_target = np.random.random(n_smpl) arr_weight = np.random.randint(0, 2, n_smpl) # 自定义评分函数,通过闭包获取原始权重 def weighted_r2(y_true, y_pred): test_idx = np.where(np.in1d(arr_target, y_true))[0] return r2_score(y_true, y_pred, sample_weight=arr_weight[test_idx]) def weighted_mse(y_true, y_pred): test_idx = np.where(np.in1d(arr_target, y_true))[0] # MSE需取负值,因为sklearn评分默认越大越好 return -mean_squared_error(y_true, y_pred, sample_weight=arr_weight[test_idx]) model = RandomForestRegressor() kfold_splitter = model_selection.KFold(n_splits=5, random_state=7, shuffle=True) my_scorers = { "r2_weighted": make_scorer(weighted_r2), "mse_weighted": make_scorer(weighted_mse) } cv_results = model_selection.cross_validate(model, arr_source, arr_target, scoring=my_scorers, cv=kfold_splitter) print(f"平均R2: {np.mean(cv_results['test_r2_weighted']):.4f}") print(f"平均MSE: {-np.mean(cv_results['test_mse_weighted']):.4f}")
注意:该方法依赖
arr_target无重复值,否则会出现索引匹配错误,因此优先推荐手动循环的方案。
内容的提问来源于stack exchange,提问作者majpark
相关产品推荐
相关产品推荐

