为什么要计算差异性
试验研究表明,在已构建的个体学习器中,只挑选一些性能较好的学习器,会得到更好的预测效果。在构建集成学习器时,有效地产生预测能力强、差异大的学习器是关键,即要获得良好的集成,个体学习器应“好而不同”,即个体学习器既要有一定的准确性,各学习器间又要具有一定的差异。选择差异度较大的算法能够最大程度体现不同算法的优势。
- “好” 可以通过一些指标来比较出来,如:准确率、误检率、auc值等指标来计算。
- “不同”可以通过一些指标来比较出来,如:Q统计值、双次失败度量等指标来计算。
下面我们主要来看一下如何计算分类器中的“不同”。
计算差异性的方法
双次失败(Double Failure,DF)度量和Q统计指标是用于评估集成模型中不同分类器之间的多样性。
Q统计指标
Q统计指标(Q-statistic):Q统计指标度量了两个分类器的分类结果之间的一致性。它通过计算两个分类器的正确分类样本数目与错误分类样本数目之间的差异来得到。
$$
Q=\frac{N^{11} \cdot N^{00} – N^{10} \cdot N^{01}}{N^{11} \cdot N^{00} + N^{10} \cdot N^{01}}
$$
其中,N00表示两个分类器都错误分类的样本数目,N11表示两个分类器都正确分类的样本数目,N10表示第一个分类器正确分类而第二个分类器错误分类的样本数目,N01表示第一个分类器错误分类而第二个分类器正确分类的样本数目。
Q的取值范围为-1到1之间,值越接近1表示两个分类器之间的一致性越高,即它们更容易在同一类别上产生一致的分类结果;值越接近-1表示两个分类器之间的一致性越低,即它们更容易在不同类别上产生不一致的分类结果;值接近0表示两个分类器的分类结果之间存在随机性或无关性。
双次失败度量DF
双次失败度量(Double Failure,DF):DF度量衡量了两个分类器在同时错误分类样本的能力。它通过计算两个分类器同时将样本错误分类的样本数目(N00)除以总样本数目(N11 + N00 + N10 + N01)来得到。
$$
DF=\frac{N^{00}}{N^{11} + N^{00} + N^{10} + N^{01}}
$$
其中,N11、N00、N10、N01的含义与上述Q统计指标中相同。
DF的取值范围为0到1之间,值越大表示两个分类器之间的多样性越小,即它们更容易在同一类别上产生错误分类。
代码实现
代码实现如下:
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split # 导入切分训练集、测试集模块
from sklearn.preprocessing import LabelEncoder, StandardScaler
from keras.models import Sequential
from keras.layers import Dense
# 消除警告
from warnings import simplefilter
simplefilter(action='ignore', category=FutureWarning)
# 防止汉字乱码
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
#读取数据
data = pd.read_csv("./data/all_lose_new.csv", encoding='gbk')
target = data["丢包类别"]
data = pd.DataFrame(data, columns=['IATmean', 'ROTT', 'ROTT_min_mean', 'IATmax', '临近IAT比值', 'ROTT_ROTT_min','ROTT_dev', 'Numloss', 'ROTT_ROTT_mean_dev', 'ROTT_ROTT_mean'])
# 对标签进行编码
label_encoder = LabelEncoder()
target = label_encoder.fit_transform(target)
# # 数据预处理
# scaler = StandardScaler()
# data = scaler.fit_transform(data)
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.3, random_state=1)
def get_models():
'''
:return:设置好参数的模型
'''
models = dict()
models['svm'] = SVC(probability=True, random_state=1)
models['dtc'] = DecisionTreeClassifier(random_state=1)
models['lr'] = LogisticRegression(max_iter=3000, random_state=1)
models['knn'] = KNeighborsClassifier()
models['bp'] = Sequential()
models['rt'] = RandomForestClassifier(n_estimators=100, random_state=1)
return models
def data_model(name, model, x_train, y_train, x_test):
'''
:param name: 分模型名称
:param model: 模型
:param x_train: 训练数据特征
:param y_train: 训练数据标签
:param x_test: 测试数据特征
:return: 返回对测试数据的分类类别
'''
if name != 'bp':
model.fit(x_train, y_train)
pre_result = model.predict(x_test)
else:
# 构建神经网络模型
model.add(Dense(30, input_dim=x_train.shape[1], activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# 训练模型
model.fit(x_train, y_train, epochs=30, batch_size=32, verbose=1)
y_pred = model.predict(x_test)
pre_result = (y_pred > 0.5).astype(int)
return pre_result
def calculate_N_values(predictions1, predictions2, true_labels):
'''
:param predictions1: 第一个模型的预测类别
:param predictions2: 第二个模型的预测类别
:param true_labels: 真实类别
:return: N11两个分类器都预测正确,N00两个分类器都预测错误,N10第一个分类器预测正确第二个分类器预测错误,N01第一个分类器预测错误第二个分类器预测正确
'''
N11 = 0
N00 = 0
N10 = 0
N01 = 0
for pred1, pred2, true_label in zip(predictions1, predictions2, true_labels):
if pred1 == true_label and pred2 == true_label:
N11 += 1
elif pred1 != true_label and pred2 != true_label:
N00 += 1
elif pred1 == true_label and pred2 != true_label:
N10 += 1
elif pred1 != true_label and pred2 == true_label:
N01 += 1
return N11, N00, N10, N01
def calculate_DF(N11, N00, N10, N01):
'''
:param N11: N11两个分类器都预测正确
:param N00: N00两个分类器都预测错误
:param N10: 第一个分类器预测正确第二个分类器预测错误
:param N01: 第一个分类器预测错误第二个分类器预测正确
:return: 双次失败度量DF
'''
denominator = N11 + N00 + N10 + N01
DF = N00 / denominator
return round(DF, 3)
def calculate_q_statistic(N11, N00, N10, N01):
'''
:param N11: N11两个分类器都预测正确
:param N00: N00两个分类器都预测错误
:param N10: 第一个分类器预测正确第二个分类器预测错误
:param N01: 第一个分类器预测错误第二个分类器预测正确
:return: Q统计指标
'''
numerator = (N11 * N00) - (N10 * N01)
denominator = (N11 * N00) + (N10 * N01)
q_statistic = numerator / denominator
return round(q_statistic, 3)
def get_avg(df):
'''
计算每行平均值:从0开始,计算第i行,第i列数据加上第i行数据除以列的数目
:param df:
:return:
'''
# 计算平均值
averages = []
for i in range(len(df)):
row_data = 0
row = df.iloc[i]
if i == 0:
average = row.mean(axis=0)
else:
for j in range(i): # 0-i-1 i= 2 0 1 2
row_data = row_data + df.iloc[j, i - 1]
if i != df.shape[1]:
for k in range(i, df.shape[1]):
row_data = row_data + row[k]
average = row_data / (df.shape[1])
else:
average = row_data / (df.shape[1])
averages.append(round(average, 3))
return averages
models = get_models()
model_name = list(models.keys())
print(model_name)
pre_result_list = list()
for name, model in models.items():
pre_result = data_model(name, model, x_train, y_train, x_test)
pre_result_list.append([name, pre_result])
print(pre_result_list)
N_list = []
q_list = []
df_list = []
for i in range(len(pre_result_list)):
for j in range(i + 1, len(pre_result_list)):
N11, N00, N10, N01 = calculate_N_values(pre_result_list[i][1], pre_result_list[j][1], y_test)
name = pre_result_list[i][0] + '_' + pre_result_list[j][0]
N_list.append([name, N11, N00, N10, N01])
for i in N_list:
q_statistic = calculate_q_statistic(i[1], i[2], i[3], i[4])
DF = calculate_DF(i[1], i[2], i[3], i[4])
q_list.append([i[0], q_statistic])
df_list.append([i[0], DF])
# Q_df
q_list_df = pd.DataFrame(q_list, columns=['Classifiers', 'Score'])
q_list_df['index'] = q_list_df['Classifiers'].str.split('_').str[0]
q_list_df['Classifiers'] = q_list_df['Classifiers'].str.split('_').str[-1]
q_df = pd.pivot_table(q_list_df, values='Score', index='index', columns='Classifiers')
q_df.loc[model_name[-1]] = np.nan
print('---------------------------------------------------------------------------------------------')
print('---------------------------------------Q统计值-----------------------------------------------')
# 重新排序索引和列名
q_df = q_df.reindex(model_name, axis=0) # 对索引排序
q_df = q_df.reindex(model_name[1:], axis=1) # 对列名排序
averages = get_avg(q_df)
q_df['Average'] = averages
sys = q_df['Average'].mean()
print(q_df)
print('系统整体的Q统计值为:' + str(round(sys,3)))
# DF_df
df_list_df = pd.DataFrame(df_list, columns=['Classifiers', 'Score'])
df_list_df['index'] = df_list_df['Classifiers'].str.split('_').str[0]
df_list_df['Classifiers'] = df_list_df['Classifiers'].str.split('_').str[-1]
df_df = pd.pivot_table(df_list_df, values='Score', index='index', columns='Classifiers')
df_df.loc[model_name[-1]] = np.nan
print('---------------------------------------------------------------------------------------------')
print('-------------------------------------------DF值----------------------------------------------')
# 重新排序索引和列名
df_df = df_df.reindex(model_name, axis=0) # 对索引排序
df_df = df_df.reindex(model_name[1:], axis=1) # 对列名排序
averages = get_avg(df_df)
df_df['Average'] = averages
sys = df_df['Average'].mean()
print(df_df)
print('系统整体的DF值为:' + str(round(sys,3)))
输出结果为:
—————————————Q统计值———————————————–
个体分类器 | DT | LR | kNN | BP | RT | 平均值 |
---|---|---|---|---|---|---|
LSSVM | 0.764 | 0.991 | 0.886 | 0.998 | 0.843 | 0.896 |
DT | NaN | 0.679 | 0.954 | 0.726 | 0.986 | 0.822 |
LR | NaN | NaN | 0.826 | 0.998 | 0.764 | 0.852 |
kNN | NaN | NaN | NaN | 0.870 | 0.982 | 0.904 |
BP | NaN | NaN | NaN | NaN | 0.808 | 0.880 |
RT | NaN | NaN | NaN | NaN | NaN | 0.877 |
系统整体的Q统计值为:0.872
——————————————-DF值———————————————-
个体分类器 | DT | LR | kNN | BP | RT | 平均值 |
---|---|---|---|---|---|---|
LSSVM | 0.068 | 0.169 | 0.077 | 0.175 | 0.068 | 0.111 |
DT | NaN | 0.070 | 0.081 | 0.071 | 0.094 | 0.077 |
LR | NaN | NaN | 0.077 | 0.201 | 0.067 | 0.117 |
kNN | NaN | NaN | NaN | 0.080 | 0.086 | 0.080 |
BP | NaN | NaN | NaN | NaN | 0.069 | 0.119 |
RT | NaN | NaN | NaN | NaN | NaN | 0.077 |
系统整体的DF值为:0.097