我正在使用Golang为具有超过30000个可能标记的数据集实现朴素贝叶斯分类。我建立了模型,我处于分类阶段。我正在分类1000条记录,这需要花费5分钟。我用pprof功能描述了代码; top10如下所示:
Total: 28896 samples
16408 56.8% 56.8% 24129 83.5% runtime.mapaccess1_faststr
4977 17.2% 74.0% 4977 17.2% runtime.aeshashbody
2552 8.8% 82.8% 2552 8.8% runtime.memeqbody
1468 5.1% 87.9% 28112 97.3% main.(*Classifier).calcProbs
861 3.0% 90.9% 861 3.0% math.Log
435 1.5% 92.4% 435 1.5% runtime.markspan
267 0.9% 93.3% 302 1.0% MHeap_AllocLocked
187 0.6% 94.0% 187 0.6% runtime.aeshashstr
183 0.6% 94.6% 1137 3.9% runtime.mallocgc
127 0.4% 95.0% 988 3.4% math.log10
令人惊讶的是,地图访问似乎是瓶颈。有没有人经历过这个。还有什么其他关键的,有价值的数据结构可以用来避免这个瓶颈?所有地图访问都在下面给出的代码中完成:
func (nb *Classifier) calcProbs(data string) *BoundedPriorityQueue{
probs := &BoundedPriorityQueue{}
heap.Init(probs)
terms := strings.Split(data, " ")
for class, prob := range nb.classProb{
condProb := prob
clsProbs := nb.model[class]
for _, term := range terms{
termProb := clsProbs[term]
if termProb != 0{
condProb += math.Log10(termProb)
}else{
condProb += -6 //math.Log10(0.000001)
}
}
entry := &Item{
value: class,
priority: condProb,
}
heap.Push(probs,entry)
}
return probs
}
地图是nb.classProb,它是map[string]float64
,而nb.model是类型为
map[string]map[string]float64
答案 0 :(得分:3)
除了@tomwilde所说的,可以加速你的算法的另一种方法是字符串实习。也就是说,如果您提前知道密钥域,则可以完全避免使用映射。我写了一个小包,为你做string interning。
答案 1 :(得分:2)
是的,地图访问将成为此代码中的瓶颈:它是两个嵌套循环中最重要的操作。
无法从代码中确定您已包含的内容,但我希望您的课程数量有限。您可能会做的是对它们进行编号,并存储类似术语的类概率:
map[string][NumClasses]float64
(即:对于每个术语,存储一组分类概率[或者可能已经预先计算了它们的日志],而NumClasses是您拥有的不同类的数量)。
然后,首先迭代术语,然后在里面迭代。昂贵的地图查找将在外部循环中完成,内部循环将在数组上进行迭代。
这将减少NumClasses因子的地图查找次数。如果您的数据非常稀疏,这可能需要更多内存。
下一个优化是使用多个goroutine进行计算,假设您有多个CPU核心可用。