在算法比赛中,往往存在类别不均衡的问题,在有“偏见”数据上训练出的模型可能不是最优的。如果我们能在模型预测出的各类别分数上,添加权重因子,并利用优化算法自动计算这些因子的值,使预测结果达到验证集上的最优,那么在测试集也许会有提升。
示例
假设比赛的评分函数是macro-f1值,X的形状为(dev_num, class_num),表示模型的预测结果,y的形状为(dev_num, 1),表示真实的标签。
常规的预测评分代码如下所示:
pred = np.argmax(X, axis=1)
score = f1_score(y, pred, average='macro')
引入后处理指标优化的代码如下所示:
import numpy as np
import scipy as sp
from functools import partial
from sklearn.metrics import f1_score
np.random.seed(2)
# 待优化的函数
def func(coef, x, y):
pred = np.argmax(x * coef, axis=1)
macro_f1 = f1_score(y, pred, average='macro')
return -macro_f1
func_1 = partial(func, x=X, y=y)
# 初始化各类别的权重因子
class_num = 14
init_coef = np.random.rand(1, class_num)
# 利用scipy,计算dev上最优的因子值
res = sp.optimize.minimize(func_1, init_coef, method='Nelder-Mead')
best_coef = res.x
# 预测,X_test表示模型在测试集上计算出的预测值
X_test = X_test * best_coef
test_pred = np.argmax(X_test, axis=1)
score = f1_score(y, test_pred, average='macro')
非线性优化
上文提到的**scipy.optimize.minimize()**是Python中非线性规划求极值常用的方法,其官方文档在此。
下面以求 f(x) = a / x + x,a为常数 这一经典的勾函数极值问题,介绍minimize的用法,代码如下:
from functools import partial
from scipy.optimize import minimize
import numpy as np
# 待优化函数
def fun(a, x):
return a / x + x
a = 1
fun_1 = partial(fun, a)
# 随意为x猜测一个初值
x0 = np.asarray([2.5])
res = minimize(fun_1, x0, method='Nelder-Mead')
# 是否成功优化
print(res.success)
# 函数最优时,x的值
print(res.x)
# 函数最优值
print(res.fun)
优化方法除了’Nelder-Mead’还有许多,参见官方文档。
实战验证
笔者参与了2022年科大讯飞的全球足球联赛比赛胜负预测挑战赛,三分类macro-f1从0.42395提升到了0.42869,净提升0.00474。提升并不是很大,但终归有所进步:)。