朴素贝叶斯分类器和判别分析的准确性还差得很远

Posted

技术标签:

【中文标题】朴素贝叶斯分类器和判别分析的准确性还差得很远【英文标题】:Naive Bayes classifier and discriminant analysis accuracy is way off 【发布时间】:2012-11-06 11:04:56 【问题描述】:

所以我有两种分类方法,判别分析diaglinear分类(朴素贝叶斯)和matlab中实现的纯朴素贝叶斯分类器,整个数据集中有23个类。第一种方法判别分析:

%% Classify Clusters using Naive Bayes Classifier and classify
training_data = Testdata; 
target_class = TestDataLabels;

[class, err]  = classify(UnseenTestdata, training_data, target_class,'diaglinear')

cmat1 = confusionmat(UnseenTestDataLabels, class);
acc1 = 100*sum(diag(cmat1))./sum(cmat1(:));
fprintf('Classifier1:\naccuracy = %.2f%%\n', acc1);
fprintf('Confusion Matrix:\n'), disp(cmat1)

从混淆矩阵中获得 81.49% 的准确度,错误率 (err) 为 0.5040(不知道如何解释)。

朴素贝叶斯分类器的第二种方法:

%% Classify Clusters using Naive Bayes Classifier
training_data = Testdata; 
target_class = TestDataLabels;
%# train model
nb = NaiveBayes.fit(training_data, target_class, 'Distribution', 'mn');

%# prediction
class1 = nb.predict(UnseenTestdata); 

%# performance
cmat1 = confusionmat(UnseenTestDataLabels, class1);
acc1 = 100*sum(diag(cmat1))./sum(cmat1(:));
fprintf('Classifier1:\naccuracy = %.2f%%\n', acc1);
fprintf('Confusion Matrix:\n'), disp(cmat1)

产生 81.89% 的准确度。

我只做了一轮交叉验证,我是 matlab 和监督/非监督算法的新手,所以我自己做了交叉验证。我基本上只是把 10% 的数据放在一边用于测试目的,因为它每次都是随机的。我可以通过它几次并取平均准确度,但结果将用于解释目的。

所以对于我的问题。

在我对当前方法的文献回顾中,许多研究人员发现,将单一分类算法与聚类算法混合可以产生更好的准确度结果。他们通过为他们的数据找到最佳数量的集群并使用分区集群(应该更相似)通过分类算法运行每个单独的集群来做到这一点。一个过程,您可以将无监督算法的最佳部分与监督分类算法结合使用。

现在,我正在使用已在文学作品中多次使用的数据集,并且在我的探索中我正在尝试一种与其他人不太相似的方法。

我首先使用了简单的 K-Means 聚类,令人惊讶的是它具有很好的聚类数据的能力。输出如下所示:

查看每个集群 (K1, K2...K12) 类标签:

%% output the class labels of each cluster
K1 = UnseenTestDataLabels(indX(clustIDX==1),:)

我发现主要每个集群在 9 个集群中都有一个类标签,而 3 个集群包含多个类标签。表明 K-means 对数据有很好的拟合度。

问题然而,一旦我拥有每个集群数据(cluster1,cluster2...cluster12):

%% output the real data of each cluster
cluster1 = UnseenTestdata(clustIDX==1,:)

然后我将每个集群通过朴素贝叶斯或判别分析,如下所示:

class1  = classify(cluster1, training_data, target_class, 'diaglinear');
class2  = classify(cluster2, training_data, target_class, 'diaglinear');
class3  = classify(cluster3, training_data, target_class, 'diaglinear');
class4  = classify(cluster4, training_data, target_class, 'diaglinear');
class5  = classify(cluster5, training_data, target_class, 'diaglinear');
class6  = classify(cluster6, training_data, target_class, 'diaglinear');
class7  = classify(cluster7, training_data, target_class, 'diaglinear');
class8  = classify(cluster8, training_data, target_class, 'diaglinear');
class9  = classify(cluster9, training_data, target_class, 'diaglinear');
class10  = classify(cluster10, training_data, target_class, 'diaglinear'); 
class11  = classify(cluster11, training_data, target_class, 'diaglinear');
class12  = classify(cluster12, training_data, target_class, 'diaglinear');

准确率变得可怕,50%的聚类分类准确率为0%,每个分类的聚类(acc1,acc2,...acc12)都有自己对应的混淆矩阵你可以在这里看到每个聚类的准确率:

所以我的问题/问题是:我哪里出错了?我首先想到的可能是集群的数据/标签混合在一起,但是我在上面发布的内容看起来是正确的,我看不出它有什么问题。

为什么与第一个实验中使用的未见 10% 数据完全相同的数据会为相同的未见聚类数据产生如此奇怪的结果?我的意思是应该注意,NB 是一个稳定的分类器,不应该轻易过度拟合,并且看到训练数据很大,而要分类的集群是并发的过度拟合不应该发生?

编辑:

根据 cmets 的要求,我已将 cmat 文件包含在第一个测试示例中,它给出了 81.49% 的准确度和 0.5040 的错误:

本例(cluster4)中还要求K、class和相关cmat的sn-p,准确度为3.03%

看到有大量的类(总共 23 个),我决定减少 1999 KDD Cup 中概述的类,这只是应用了一些领域知识,因为一些攻击比其他攻击更相似,并且属于一个总称。

然后我用 444,000 条记录训练分类器,同时保留 10% 用于测试目的。

准确率更差73.39%错误率也更差0.4261

将看不见的数据分解成不同的类别:

DoS: 39149
Probe: 405
R2L: 121
U2R: 6
normal.: 9721

类别或分类标签(判别分析的结果):

DoS: 28135
Probe: 10776
R2L: 1102
U2R: 1140
normal.: 8249

训练数据由以下组成:

DoS: 352452
Probe: 3717
R2L: 1006
U2R: 49
normal.: 87395

我担心如果我将训练数据降低到具有相似百分比的恶意活动,那么分类器将没有足够的预测能力来区分类别,但是在查看其他一些文献时,我注意到一些研究人员将 U2R 删除为没有足够的数据来成功分类。

到目前为止,我尝试过的方法是一类分类器,我训练分类器只预测一类(无效),对单个集群进行分类(准确度更差),减少类标签(第二好)并保持完整的 23类标签(最佳准确度)。

【问题讨论】:

整个数据集和每个集群中的类大小是多少? 我不太确定 training_data 中的内容,但听起来您的训练和测试数据没有任何重叠的类。如果您的训练数据在测试集群中不包含该类的许多实例,它将无法正确标记它们。 Whats in training_data... 它在代码中说 training_data = Testdata; 测试数据是 90%(444000 条记录)我保留了其中的 10%(44000 条记录),这是UnseenTestdata 这变成了集群数据,每个集群都被分类了。所以没有杰夫你的方式。 你能发布K1class1cmat1的sn-p吗? 我的一点无用的建议:类这么大(而且我不知道它们的尺寸)你将很难玩算法。为了证明它们有效,生成一个简单的数据集,完全由你控制,只有很少的点,然后对它们进行实验。只有当您对算法的工作充满信心时,才尝试将其应用于大而严肃的数据。 ;) 【参考方案1】:

查看您的第一个示例的cmat1 数据(准确率为 81.49%),您获得高准确率的主要原因是您的分类器获得了大量的class 1class 4 正确。几乎所有其他类都表现不佳(得到零正确预测)。这与您的上一个示例一致(首先使用 k-means),其中 cluster7 的 acc7 为 56.9698。

编辑: 似乎在cmat1 中,我们没有超过一半的类的测试数据(查看全零行)。所以你只能知道像14这样的类的一般性能是好的,如果你先进行聚类,就会得到类似的性能。但是对于其他类,这并不能证明它可以正常工作。

【讨论】:

我已经知道了,你还没有提到任何知道的事情,事实上你的答案是关于集群数据的错误。 K-means 成功地对 DoS 和 Normal 进行了聚类,但是对它们进行分类并不会产生与第一个示例相同的结果,也不会产生接近准确度的任何结果,它擅长预测。另请阅读关于答案的赏金说明。 还有we do not have testing data for more that half of the classes(looking at the all-zero lines)是什么意思 我可能误解了你的问题。我指出您的 NB 分类器表现不佳。准确率(81.49%)来自总测试实例(49402)除以混淆矩阵所有对角项之和(2802+9+1669+10139+...=40259)。如您所见,两个类(1 和 4)对准确率的贡献最大,而大量的类根本没有正确的预测。这个问题是由于数据集的不平衡造成的,因为这两个类的实例太多了。毫不奇怪,稍后您会获得许多单个集群的 0% 准确度。 数据集平衡对于朴素贝叶斯等分类器很重要,如果你的数据集不平衡,你不能简单地使用NB。在 Google 上搜索,您可能会发现很多论文都在处理这个问题。【参考方案2】:

对数据进行聚类后,您是否为每个聚类都训练了一个分类器?如果您不这样做,那么这可能是您的问题。

尝试这样做。首先,对数据进行聚类并保留质心。然后,使用训练数据,为每个集群训练一个分类器。对于分类阶段,找到您要分类的对象最近的质心,并使用相应的分类器。

单个分类器不是一个好主意,因为它会学习整个数据集的模式。但是,您在集群时想要的是学习描述每个集群的本地模式。

【讨论】:

这没有意义。当您在这种情况下训练分类器时,朴素贝叶斯您可以在尽可能多的样本或描述类的平衡数据集上训练它,在我的情况下测试准确性时,您在看不见的集群测试数据(即每个集群)上对其进行测试。 另请注意,每个集群都有自己的训练和分类,但每个集群都使用相同的训练数据进行训练。所以我不确定你是否读过这个问题? 在您的问题中,您说“...通过分类算法运行每个单独的集群。”你如何训练这个分类器?也就是说,您使用哪些实例来训练它。是整个数据集还是仅属于集群的实例? 如问题中所述,我将数据集分为两部分。 90% 的数据集用于训练,剩下的 10% 用于测试。首先对测试部分进行聚类,然后使用训练数据对每个聚类进行分类。【参考方案3】:

考虑这个函数调用:

classify(cluster1, training_data, target_class, 'diaglinear');

training_data 是整个特征空间的样本。那是什么意思?您正在训练的分类模型将尝试最大化整个特征空间的分类准确度。这意味着,如果您展示与您的训练数据具有相同行为的测试样本,您将获得分类结果。

关键是您没有显示与您的训练数据具有相同行为的测试样本。事实上,cluster1 只是您的特征空间的一个分区的样本。更具体地说,cluster1 中的实例对应于特征空间的样本,这些样本比其余的质心更接近 cluster1 的质心,这可能会降低分类器的性能。

所以我建议你:

    聚类您的训练集并保持质心 使用训练数据,为每个集群训练一个分类器。也就是说,仅使用属于该集群的实例来训练分类器。 对于分类阶段,找到您要分类的对象最近的质心并使用相应的分类器。

【讨论】:

因此,如果我对训练数据进行聚类并保持假设集群 1 是“正常”类的平均平均值,然后我将其用作 NB 的训练数据,然后我将我看不见的数据聚类并我发现要么没有一个集群具有相同的平均均值,要么它们具有相同的平均值,但是当我尝试根据训练示例进行分类时,我发现 NB 只有一个类可以训练,并且数据严重过度拟合,并且只偏向于一个类类(本例中为正常)? 请注意,您不应该对测试数据进行聚类。 好吧,我曾想过,如果我对整个数据集进行聚类,然后取出每个聚类的 10% 来训练每个匹配聚类的分类器,这将是它与您的建议的方法。因为请注意,如果我分别对训练和测试进行聚类并且只取训练数据的质心和测试数据的质心,那么它们就不匹配。数字完全不同。【参考方案4】:

正如其他人正确指出的那样,这里至少存在一个问题:

class1  = classify(cluster1, training_data, target_class, 'diaglinear');
...

您正在使用所有 training_data 训练分类器,但仅在子集群上对其进行评估。为了对数据进行聚类以产生任何效果,您需要在每个子聚类训练不同的分类器。有时这可能非常困难——例如,在 Y 类的集群 C 中可能很少(或没有!)示例。这是尝试进行联合聚类和学习所固有的。

您的问题的一般框架如下:

Training data:
   Cluster into C clusters
   Within each cluster, develop a classifier

Testing data:
   Assign observation into one of the C clusters (either "hard", or "soft")
   Run the correct classifier (corresponding to that cluster)

这个

class1  = classify(cluster1, training_data, target_class, 'diaglinear');

不这样做。

【讨论】:

“在每个集群中,开发一个分类器”几乎不可能用 darpa 数据集开发这个,所以如果你尝试,你只需拟合训练数据。例如,一个集群有 normal 和 dos 类,如果我创建一个分类器 50:50 或 70:30 任何表示为集群中的大多数的分类器,我用它训练 NB 来预测集群类,它的准确度在与我的第一个示例的准确性进行比较。 那么建议的方法与手头的问题无关。那将会发生。除了尝试另一种方法之外,没有什么可以做的。【参考方案5】:

这是一个非常简单的例子,它准确地展示了它应该如何工作以及出了什么问题

%% Generate data and labels for each class
x11 = bsxfun(@plus,randn(100,2),[2 2]);
x10 = bsxfun(@plus,randn(100,2),[0 2]);

x21 = bsxfun(@plus,randn(100,2),[-2 -2]);
x20 = bsxfun(@plus,randn(100,2),[0 -2]);

%If you have the PRT (shameless plug), this looks nice:
%http://www.mathworks.com/matlabcentral/linkexchange/links/2947-pattern-recognition-toolbox
% ds = prtDataSetClass(cat(1,x11,x21,x10,x20),prtUtilY(200,200));

x = cat(1,x11,x21,x10,x20);
y = cat(1,ones(200,1),zeros(200,1));

clusterIdx = kmeans(x,2); %make 2 clusters
xCluster1 = x(clusterIdx == 1,:);
yCluster1 = y(clusterIdx == 1);
xCluster2 = x(clusterIdx == 2,:);
yCluster2 = y(clusterIdx == 2);


%Performance is terrible:
yOut1  = classify(xCluster1, x, y, 'diaglinear');
yOut2  = classify(xCluster2, x, y, 'diaglinear');

pcCluster = length(find(cat(1,yOut1,yOut2) == cat(1,yCluster1,yCluster2)))/size(y,1)

%Performance is Good:
yOutCluster1  = classify(xCluster1, xCluster1, yCluster1, 'diaglinear');
yOutCluster2  = classify(xCluster2, xCluster2, yCluster2, 'diaglinear');

pcWithinCluster = length(find(cat(1,yOutCluster1,yOutCluster2) == cat(1,yCluster1,yCluster2)))/size(y,1)

%Performance is Bad (using all data):
yOutFull  = classify(x, x, y, 'diaglinear');
pcFull = length(find(yOutFull == y))/size(y,1)

【讨论】:

您使用与测试数据 classify(xCluster1, xCluster1 相同的训练数据,但在您的其他示例中,您不使用 classify(xCluster1, x 如果您尝试 classify(x, x 会发生什么我打赌您会得到与 yOutCluster1 相同的结果跨度> 是的,我没有尝试对此进行交叉验证。你可以。您将看到与我介绍的结果相似的结果。您也可以尝试“yOutFull =分类(x,x,y,'diaglinear');”并看到性能很差。不像 yOutCluster2 的结果。使用提供的代码很容易验证 我编辑了截取的代码以包含上述示例。它非常清楚地说明了这一点。

以上是关于朴素贝叶斯分类器和判别分析的准确性还差得很远的主要内容,如果未能解决你的问题,请参考以下文章

在单个 ROC 图上绘制线性判别分析、分类树和朴素贝叶斯曲线

模型研究15-判别分析与朴素贝叶斯

朴素贝叶斯分类器和一般的贝叶斯分类器有什么区别

生成模型学习笔记:从高斯判别分析到朴素贝叶斯

朴素贝叶斯

贝叶斯分类器原理及其推导