Python 朴素贝叶斯分类器中特征值的快速计数

Python 朴素贝叶斯分类器中特征值的快速计数,python,numpy,naivebayes,Python,Numpy,Naivebayes,我正在用Python实现一个朴素的贝叶斯分类器(作为大学作业的一部分,所以Python是一项要求)。我让它工作起来,它产生的结果与sklearn.naive_bayes.MultinomialNB差不多。但是,与sklearn实现相比,它的速度非常慢 假设特征值是0到max_i范围内的整数,类标签也是0到max_y范围内的整数。示例数据集如下所示: >>> X = np.array([2,1 1,2 2,2 0,2]).reshape(4,2) # design matrix

我正在用Python实现一个朴素的贝叶斯分类器(作为大学作业的一部分,所以Python是一项要求)。我让它工作起来,它产生的结果与sklearn.naive_bayes.MultinomialNB差不多。但是,与sklearn实现相比,它的速度非常慢

假设特征值是0到max_i范围内的整数,类标签也是0到max_y范围内的整数。示例数据集如下所示:

>>> X = np.array([2,1 1,2 2,2 0,2]).reshape(4,2) # design matrix
>>> print(X)
[[2 1]
 [1 2]
 [2 2]
 [0 2]]
>>> y = np.array([0,  1,  2,  0  ]) # class labels
>>> print(y)
[0 1 2 0]
N, D = X.shape
K = np.max(X)+1
C = np.max(y)+1
ccl = np.zeros((C,D,K))
# ccl = ccl + alpha - 1 # disregard the dirichlet prior for this question
# Count occurences of feature values given class c
for i in range(N):
    for d in range(D):
        ccl[y[i]][d][X[i][d]] += 1
# Renormalize so it becomes a probability distribution again
for c in range(C):
    for d in range(D):
        cls[c][d] = np.divide(cls[c][d], np.sum(cls[c][d]))
现在,作为处理联合对数似然度之前的中间步骤,我需要计算类条件似然度(即
p(x_ij | y)
,以便矩阵
ccl
包含给定类c的特征j中值k的概率。上述示例中的此类矩阵的输出为:

>>> print(ccl)
[[[0.5 0.  0.5]
  [0.  0.5 0.5]]

 [[0.  1.  0. ]
  [0.  0.  1. ]]

 [[0.  0.  1. ]
  [0.  0.  1. ]]]
>>> print(ccl[0][1][1]) # prob. of value 1 in feature 1 given class 0
0.5
我为实现这一点而实现的代码如下所示:

>>> X = np.array([2,1 1,2 2,2 0,2]).reshape(4,2) # design matrix
>>> print(X)
[[2 1]
 [1 2]
 [2 2]
 [0 2]]
>>> y = np.array([0,  1,  2,  0  ]) # class labels
>>> print(y)
[0 1 2 0]
N, D = X.shape
K = np.max(X)+1
C = np.max(y)+1
ccl = np.zeros((C,D,K))
# ccl = ccl + alpha - 1 # disregard the dirichlet prior for this question
# Count occurences of feature values given class c
for i in range(N):
    for d in range(D):
        ccl[y[i]][d][X[i][d]] += 1
# Renormalize so it becomes a probability distribution again
for c in range(C):
    for d in range(D):
        cls[c][d] = np.divide(cls[c][d], np.sum(cls[c][d]))
因此,由于Python循环很慢,这也变得很慢。 我试图通过对每个特征值进行一次热编码(因此,如果特征值在[0,1,2]范围内,则a 2变为[0,0,1]等等)来缓解这个问题,并进行如下总结。尽管我认为调用了太多的np函数,因此计算仍然需要太长时间:

ccl = np.zeros((C,D,K))
for c in range(C):
    x = np.eye(K)[X[np.where(y==c)]] # one hot encoding
    ccl[c] += np.sum(x, axis=0) # summing up
    ccl[c] /= ccl[c].sum(axis=1)[:, numpy.newaxis] # renormalization

这将产生与上面相同的输出。关于如何使其更快的任何提示?我认为
np.eye
(一个热编码)是不必要的,并且会杀死它,但我想不出一种方法来摆脱它。我考虑的最后一件事是使用
np.unique()
集合。计数器
用于计数,但还没有计算出来。

所以这是一个很好的问题(我有一个问题)。看起来处理这个问题的最快方法通常是只使用算术运算构造索引数组,然后将其堆积起来,并使用
np.bincount
对其进行重塑

N, D = X.shape
K = np.max(X) + 1
C = np.max(y) + 1
ccl = np.tile(y, D) * D * K + (X +  np.tile(K * range(D), (N,1))).T.flatten()
ccl = np.bincount(ccl, minlength=C*D*K).reshape(C, D, K)
ccl = np.divide(ccl, np.sum(ccl, axis=2)[:, :, np.newaxis])

>>> ccl
array([[[0.5, 0. , 0.5],
        [0. , 0.5, 0.5]],

       [[0. , 1. , 0. ],
        [0. , 0. , 1. ]],

       [[0. , 0. , 1. ],
        [0. , 0. , 1. ]]])
作为速度比较,
funca
是第一个基于循环的方法,
funcb
是第二个基于numpy函数的方法,
funcc
是使用bincount的方法

X = np.random.randint(3, size=(10000,2))
y = np.random.randint(3, size=(10000))
>>> timeit.timeit('funca(X,y)', number=100, setup="from __main__ import funca, X, y")
2.632569645998956
>>> timeit.timeit('funcb(X,y)', number=100, setup="from __main__ import funcb, X, y")
0.10547748399949342
>>> timeit.timeit('funcc(X,y)', number=100, setup="from __main__ import funcc, X, y")
0.03524605900020106

也许可以进一步完善,但我没有更多好主意。

Hi@CJ59,非常感谢您的回答!很抱歉,我花了将近3周的时间才将其标记为已接受。我很忙,同时也尝试了一些其他方法,但您的方法仍然是最快的。再次感谢您