Skip to content

Commit

Permalink
feat: chapter 9
Browse files Browse the repository at this point in the history
  • Loading branch information
honkinglin committed Nov 23, 2024
1 parent df1d73e commit d3dc93a
Showing 1 changed file with 13 additions and 1 deletion.
14 changes: 13 additions & 1 deletion docs/grokking/chapter-9.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,22 @@
1. 我们可以在每个服务器上复制Trie,并在离线更新它。完成后,我们可以切换到新Trie并丢弃旧的Trie。
2. 另一种选择是为每个Trie服务器设置主从配置。在主服务器处理流量时,我们可以更新从服务器。更新完成后,我们可以将从服务器切换为新的主服务器。之后,我们可以更新旧的主服务器,使其也开始处理流量。

如何更新联想建议的频率?由于我们在每个节点上存储联想建议的频率,我们也需要更新这些频率。我们可以仅更新频率的差异,而不是重新计算所有搜索词。如果我们记录过去10天内搜索的所有词条的计数,我们需要减去不再包含的时间段的计数,并添加新时间段的计数。我们可以基于每个词条的指数移动平均(EMA)来加减频率。在EMA中,我们对最新数据赋予更大的权重,也叫做指数加权移动平均。
**如何更新联想建议的频率?** 由于我们在每个节点上存储联想建议的频率,我们也需要更新这些频率。我们可以仅更新频率的差异,而不是重新计算所有搜索词。如果我们记录过去10天内搜索的所有词条的计数,我们需要减去不再包含的时间段的计数,并添加新时间段的计数。我们可以基于每个词条的指数移动平均(EMA)来加减频率。在EMA中,我们对最新数据赋予更大的权重,也叫做指数加权移动平均。

在向Trie中插入新词条后,我们将进入该短语的终端节点并增加其频率。由于我们在每个节点存储前10个查询词条,这个特定的搜索词可能会跳入其他几个节点的前10个查询中。因此,我们需要更新这些节点的前10个查询。我们必须从该节点回溯,直到根节点。对于每个父节点,我们检查当前查询是否是前10个中的一部分。如果是,我们更新相应的频率。如果不是,我们检查当前查询的频率是否足够高,能够成为前10个中的一部分。如果是,我们插入这个新词条,并移除频率最低的词条。

**如何从Trie中移除一个词条?** 假设由于某些法律问题、仇恨言论或盗版等原因,我们需要从Trie中移除一个词条。我们可以在常规更新时完全移除这些词条,同时,在每个服务器上可以添加一个过滤层,在将数据发送给用户之前移除这些词条。

**建议的排名标准可能有哪些不同的标准?** 除了简单的计数外,我们在对词条进行排名时,还需要考虑其他因素,例如新鲜度、用户位置、语言、人口统计、个人历史等。

## 4. Trie的持久化存储

如何将Trie存储到文件中,以便我们可以轻松地重建它——这在机器重启时是必要的?我们可以定期对Trie进行快照并将其存储到文件中,这样当服务器宕机时就能重建Trie。存储时,可以从根节点开始,按层级逐层保存Trie。对于每个节点,我们可以存储它包含的字符以及它有多少子节点。在每个节点之后,紧接着存储它的所有子节点。例如,假设我们有以下Trie:

![图9-3](/grokking/f9-3.png)

如果我们按照上述方案将该Trie存储到文件中,结果会是:`"C2,A2,R1,T,P,O1,D"`。通过这个数据,我们可以轻松地重建Trie。

如果你注意到,我们并没有在每个节点中存储最佳建议及其计数。存储这类信息非常困难,因为Trie是自上而下存储的,在父节点创建之前,子节点尚未生成,因此无法轻松存储它们的引用。为了解决这个问题,我们需要在构建Trie的过程中重新计算所有最佳词条及其计数。

具体而言,每个节点在构建时会计算自己的最佳建议并将其传递给父节点。父节点会合并所有子节点的结果,从而确定自己的最佳建议。

0 comments on commit d3dc93a

Please sign in to comment.