面向特定问题的开源算法管理和推荐(十二) | 2021SC@SDUSC

本文详细分析了ExtractPhrases方法的实现,该方法涉及TF-IDF的计算、词组频率的筛选以及阈值设定。通过对源码的解析,展示了如何寻找共同出现的最大值、按规则加入表格以及按阈值和其他规则提取词组。在优化效率的同时,确保了提取的词组具有较高的出现频率和相关性。
摘要由CSDN通过智能技术生成

2021SC@SDUSC

面向特定问题的开源算法管理和推荐

一、本文分析目标

前面我分析了ExtractPhrases实现中的前半部分,主要涉及到以下几个方面的工作:

  • 使用PF-TIDF的策略转化TF到一个双精度浮点型数值;
  • 将单词长度小于MinNumCharacters的单词加入tooShortWords中;

现在我们从文件第625行到1164行开始分析,先梳理整体结构,再深入到具体的细节实现。

二、ExtractPhrases的整体流程(上)

476-510行:寻找共同出现最大值

对于某一个词word,可以得到与它临近的所有词(可以是左临近;也可以是右临近)在一起出现的频率,在这其中找到最大的。这样做的目的,按照注释中的说法是:可以在要提取长的词组的情况下,更好地决定一个词/词组的最小频率。

511-536行:单个Term按规则加入表格

对每一个单词word(不是停用词或者前面找出的“过短”的词),我们可以计算得到它的改进版TF-IDF值。我们考察这个词是否是很大一部分都和另一个词结合在一起(也就说明这个词语往往只存在固定地搭配),我们不要这种词,把剩下的加入到一个新的词项的tfidf表格中(用列表表示)。

537-595行:单个phrase按阈值和其他规则加入表格

为了加快提取速度,我们应该去除一些出现频率少的词组,也就是不常用的哪些词组。每个词组pair都可以算出对应的TF-IDF,但却不将所有这些词的TF-IDF加入到表格中,而是考察哪些词组是可能连接在一起的。在此之前,我们还需要根据刚才的TFIDF表格计算一个阈值,然后只添加比这个阈值大,且不容易连接在一起的词组的TFIDF。

三、ExtractPhrases的实现细节(上)

476-510行:寻找共同出现最大值

由于我们需要对于一个词,找到与它临近的所有词(可以是左临近;也可以是右临近)在一起出现的频率,在这其中取最大的。
我们可以先用一个字典来存储上述内容
(wordToHighestBigramCount)。

首先,需要对每一个词组进行遍历,在对一个词组遍历之前,我们都需要知道这个词组中包不包含停用词(stopwords),假如包含,我们就希望它能不被算入到最终结果中(也就意味着不考虑停用词的临近词)。

假如说存在停用词在一个词组中,那么这个词组将会被跳过。

if (_stopWordsSet.Contains(p.Key.Idx1) || _stopWordsSet.Contains(p.Key.Idx2))
continue;

否则我们就需要获取左边(或者右边)的词在wordToHighestBigramCount中的计数值。

我们要确保每次合法的词组频率永远要小于左右词语在wordToHighestBigramCount字典中所对应的频率值。

这样就能确保wordToHighestBigramCount最终能够得到最大的取值。

if (wordToHighestBigramCount.TryGetValue(p.Key.Idx1, out var count))
                    {
                        if (count < p.Value)
                            wordToHighestBigramCount[p.Key.Idx1] = p.Value;
                    }
                    else
                    {
                        wordToHighestBigramCount.Add(p.Key.Idx1, p.Value);
                    }

                    if (wordToHighestBigramCount.TryGetValue(p.Key.Idx2, out count))
                    {
                        if (count < p.Value)
                            wordToHighestBigramCount[p.Key.Idx2] = p.Value;
                    }
                    else
                    {
                        wordToHighestBigramCount.Add(p.Key.Idx2, p.Value);
                    }

511-536行:单个Term按规则加入表格

实际源码中,我们的目标主要有两个:

1)一个是生成一个“单词(索引)-TF·IDF表格”(用字典wordTfIdfs表示)
2)另一个目标是生成一个同样结构的字典,只是它的名字叫做initialTfIdfList。

并不是所有的TotalCounts中的单词都能进入这两个字典。
要进入第一个字典,首先这个词

1.不能是停用词;
2.这个词不能过短;

在源码中体现为这些行:

if (_stopWordsSet.Contains(p.Key) || MinNumCharacters > 1 && tooShortWords.Contains(p.Key))
	continue;

要进入第二个词典,这些词不可以有很大一部分出现时都和另一个固定的词结合在一起。下面源码用bigramCount >= p.Value / 2完成了这一条件约束。

                if (wordToHighestBigramCount != null &&
                    wordToHighestBigramCount.TryGetValue(p.Key, out var bigramCount))
                {
                    if (bigramCount >= p.Value / 2)
                    {
                        //term appears in majority of cases with another term
                        //do not use count for determining threshold
                        continue;
                    }
                }

在能够通过第一次约束之后,我们需要先计算得到它的改进版TF-IDF值,然后在每一步,将其索引和对应的TFIDF加入到字典里。
var tfidf = TransformTf(p.Value) * GetIdf(p.Key);

537-595行:单个phrase按阈值和其他规则加入表格

根据我们的目标,我们需要让最后的结果尽量地高效。我们通过阈值来滤去一些不太常见的词组。阈值的设置需要根据之前我们得出的initialTfIdfList以及我们期望提取的Top K的值。取到第K个TFIDF,我们就能保证后续过程中,一定可以在候选词中选出新的Top K,并且不需要处理多余的信息。

                var uniTfidfTh =
                    initialTfIdfList.Count < numTopPhrases ? 0 : initialTfIdfList[^numTopPhrases];
                var uniMinTransTf = uniTfidfTh / maxIdf;
                var uniMinTfTh = Math.Max(1, (int)(useRootExp ? Math.Pow(uniMinTransTf, powExp) : uniMinTransTf));

随后我们要计算包含的StopWord的个数,然后去除下面的词组:

  • 并去除两个词项全部是停用词;
  • 或者只有一个词是停用词,而这个词组出现的频率足够小(小于2);
  • 还要去除过于短小的词
var numStopwords = _stopWordsSet.Contains(p.Key.Idx1) ? 1 : 0;
                    if (_stopWordsSet.Contains(p.Key.Idx2))
                        numStopwords++;

                    if (numStopwords == 2)
                        continue;

                    if (numStopwords == 1 && p.Value <= 2)
                        continue;

                    if (MinNumCharacters > 1 &&
                       tooShortWords.Contains(p.Key.Idx1) &&
                       tooShortWords.Contains(p.Key.Idx2))
                        continue; //at least one word has to meet min no chars requirement

我们不仅希望p.Value >= uniMinTfTh还希望,tfIdf >= uniTfidfTh,不满足这两个条件的将被排除。如果没有被前面条件过滤,则将其加入pairTfIdfs。

最后加入initialTfIdfList之前我们还需完成两道过滤:

  • 一个是numStopwords >= 1;
  • 另一个是单个词常常和某个词一起出现时。
                    var tfIdf = TransformTf(p.Value) * GetIdf(p.Key);
                    if (tfIdf < uniTfidfTh)
                        continue;

                    pairTfIdfs.Add((p.Key, tfIdf));

                    if (numStopwords >= 1)
                        continue;

                    if ((localCounts.TotalCounts.WordCounts[p.Key.Idx1] <= 2 * p.Value
                        || localCounts.TotalCounts.WordCounts[p.Key.Idx2] <= 2 * p.Value))
                    {
                        //we ignore this tfidf value for determining the threshold as it could be merged at a later stage
                        continue;
                    }

                    initialTfIdfList.Add(tfIdf);
                  

四、总结

本文主要分析了函数476到595行的内容,主要到了前面TFIDF表的初始化工作,并设置了相应的阈值。实际的代码实现远远要比论文中更复杂。分析耗时耗力,但是收获颇多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值