Majority Voting

多个模型分别输出预测结果,然后取投票最多的那个标签作为最后的输出。

Voting
Voting

用数学语言描述就是

y^=model{Ci(x)},1im \hat y=model\Big\{ C_i(x) \Big\},1\le i\le m

其中 CiC_i 表示训练的第 ii 个 Classifier

多个模型组合带来准确率提升

考虑训练了 2n+12n+1 个分类器,每一个分类器的准确率为 rr,那么组合后,由于需要超过半数投票,因此正确分类的概率为

k=n+12n+1(kn)rk(1r)2n+1k \sum_{k=n+1}^{2n+1} \binom{k}{n}r^k(1-r)^{2n+1-k}

n=5,r=0.7n=5,r=0.7 时,这个值约为 0.92180.9218,可以看到,准确率有很大提升。

Weighted Majority Vote

在此基础上,给每一个模型的预测结果添加权重

y^=arg maxiAj=1mwj[Cj(x)=i] \hat y=\argmax_{i\in A} \sum_{j=1}^m w_j \Big[ C_j(\bold{x})=i \Big]

其中 AA 是所有的标签,方括号函数表示如果第 jj 的分类器对于样本 x\bold x 给出的预测结果是 ii 类别的话则为 11,否则为 00.

因此,Weighted Vote 就相当于是枚举标签,然后看每一个模型预测结果的加权平均,取均值最大的那个对应的标签。


Soft Voting

有的模型可以输出概率,所以我们也可以对概率进行加权,最后取最高

y^=arg maxiAj=1mwjPj(i) \hat y=\argmax_{i\in A}\sum_{j=1}^m w_j\cdot P_{j}(i)

代码实现

下面的代码实现了一个 Majority Vote Classifier (vote='classlabel') 和 Soft Vote (vote='probability')

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from sklearn.base import BaseEstimator, ClassifierMixin, clone
from sklearn.preprocessing import LabelEncoder
from sklearn.pipeline import _name_estimators
import numpy as np
import operator


class MajorityVoteClassifier(BaseEstimator, ClassifierMixin):
def __init__(self, classifiers, vote="classlabel", weights=None):
'''
__init__ 函数接收分类器列表,进行初始化
vote 表示投票方法
'''
self.classifiers = classifiers
self.named_classifiers = {
key: value
for key, value in _name_estimators(classifiers)
}
self.vote = vote
self.weights = weights


def fit(self, X, y):
'''
fit() 根据输入的数据 + 标签,
对标签进行 encoding(方便 Soft Vote 获取概率)
然后对 classifier 模型进行训练,并存起来
'''
self.label_enc = LabelEncoder()
self.label_enc.fit(y)

self.classes = self.label_enc.classes_
self.trained_classifiers = []
for classifier in self.classifiers:
trained_clf = clone(classifier).fit(
X,
self.label_enc.transform(y),
)
self.trained_classifiers.append(trained_clf)

return self


def predict(self, X):
'''
probability 部分比较容易理解
classlabel 部分的话,我们首先获取每一个模型的输出结果(`predictions`),
然后
'''
if self.vote == 'probability':
maj_vote = np.argmax(self.predict_proba(X), axis=1)
else:
predictions = np.asarray(
[
clf.predict(X)
for clf in self.trained_classifiers
]
).T
maj_vote = np.apply_along_axis(
lambda x: np.argmax(np.bincount(x, weights=self.weights)), axis=1, arr=predictions)
maj_vote = self.label_enc.inverse_transform(maj_vote)
return maj_vote


def predict_proba(self, X):
probas = np.asarray([clf.predict_proba(X) for clf in self.classifiers_])
avg_proba = np.average(probas, axis=0, weights=self.weights)
return avg_proba