在Python中计算n-grams的逐点互信息(PMI)分数

4 投票
1 回答
4925 浏览
提问于 2025-04-16 13:14

我有一大堆n-gram(就是一组连续的词,比如“我爱编程”可以看作一个2-gram),还有一些外部的n-gram。我想根据这堆数据来计算每个外部n-gram的PMI分数(PMI是一种衡量词组之间相关性的指标)。

请问有没有什么工具可以做到这一点,或者有没有人能给我一段Python代码来实现这个功能?

问题是我的n-gram有2-gram、3-gram、4-gram和5-gram。所以计算3-gram及以上的概率真的很耗时间。

1 个回答

5

如果我理解你的问题没错的话,你想计算一些像这样的东西:log { P("x1 x2 x3 x4 x5") / P("x1") P("x2") ... P("x5") },其中 P 表示某个特定的 5-gram 或 1-gram 出现的概率(基本上是一些计数的比率,可能还会有拉普拉斯风格的调整)。所以,你可以先遍历一遍你的文本数据,记录下 (1) 每个 1-gram 的出现次数,(2) 每个 n-gram 的出现次数(可以用字典来存这些),然后对于每个外部的 n-gram,你只需要查几次字典,做点简单的数学运算,就完成了。开始时遍历一次文本数据,然后每处理一个外部 n-gram 只需固定的工作量。

(注意:其实我不太确定如何定义超过两个随机变量的 PMI;也许是像这样:log P(a)P(b)P(c)P(abc) / P(ab)P(bc)P(a_c)。但如果是类似的东西,你可以用同样的方法:遍历你的文本数据,记录很多东西,然后你需要的所有概率其实就是这些计数的比率,可能还会有一些拉普拉斯风格的修正。)

如果你的文本数据太大,导致你无法把 n-gram 的字典放进内存,那就把它分成适合内存大小的块,计算每个块的 n-gram 字典,并把它们存储在磁盘上,以便能高效地访问任何给定的 n-gram;然后,对于每个外部 n-gram,遍历这些块并把计数加起来。

存储成什么样子?随你决定。有一个简单的选择:按 n-gram 的字典序排列(注意:如果你处理的是单词而不是字母,可能需要先把单词转换成数字;这需要你先遍历一遍文本数据来完成);这样找到你想要的 n-gram 就可以用二分查找,假设每块大小是 1GB,那每块大约需要 15-20 次查找;你可以增加一些额外的索引来减少查找次数。或者:在磁盘上使用哈希表,比如 Berkeley DB 之类的;在这种情况下,你就可以不分块了。又或者,如果字母表很小(例如,你处理的是字母 n-gram 而不是单词 n-gram,并且处理的是普通的英文文本),可以直接把它们存储在一个大数组中,进行直接查找——但在这种情况下,你可能还是能把整个数据放进内存里。

撰写回答