相关搜索词推荐算法封面

当站内搜索有一定关键词库、搜索日志或点击数据后,就可以根据用户输入的关键词,自动推荐一批“更可能相关、也更值得展示”的搜索词。一个稳定的相关搜索词推荐算法,通常不只看字面相似度,而是把关键词库、用户行为、共现关系、词向量相似度和业务指标一起纳入排序。

本文以“从关键词库中为目标关键词推荐相关搜索词”为场景,整理一套可落地的实现思路,并给出 Python 示例代码,适合用于站内搜索、内容推荐、电商搜索联想、SEO 词库扩展等场景。

先明确推荐目标

相关搜索词推荐不是简单地找“长得像”的词,而是要回答三个问题:

  1. 用户搜索这个词时,下一步还可能关心什么?
  2. 哪些词与目标词在真实搜索、点击或转化行为里经常一起出现?
  3. 哪些候选词既相关,又有搜索量、点击率或业务价值?

因此,一个更稳妥的排序方式是:先用关键词库和行为日志召回候选词,再用共现、PMI、词向量余弦相似度等方法计算相关度,最后结合热度、转化率、新鲜度等业务指标做综合排序。

数据准备

推荐算法上线前,至少需要准备以下几类数据:

  • 关键词库:关键词文本、搜索频次、点击率、类目、标签、更新时间等。
  • 搜索会话:同一用户或同一会话内连续搜索过的关键词。
  • 点击与转化数据:关键词对应的点击内容、停留、收藏、购买、咨询等行为。
  • 业务约束:需要过滤的品牌词、低质词、违规词、过期词,以及不同频道的展示规则。

如果数据量还不大,可以先从关键词库和搜索会话开始。等到日志规模扩大后,再引入词向量、Embedding 或更复杂的学习排序模型。

实现流程

关键词库驱动的相关搜索推荐流程

一个常见的流程可以拆成六步:

  1. 从关键词库读取目标词和候选词,过滤停用词、低频词和不符合业务规则的词。
  2. 根据搜索会话或点击链路统计关键词之间的共现次数。
  3. 用共现次数、PMI 或类目规则召回一批候选词。
  4. 用词向量或 Embedding 计算候选词与目标词的语义相似度。
  5. 将相关度、热度、点击率、转化率、新鲜度等指标合成一个综合分。
  6. 按分数排序,返回 Top N,并对结果做去重、阈值过滤和人工黑白名单控制。

方法一:共现矩阵与 PMI

共现矩阵适合处理“真实用户经常一起搜索什么”的问题。比如用户搜索 iPhone 后,又搜索了“手机壳”“保护膜”“蓝牙耳机”,这些词就会在会话维度产生共现关系。

只用共现次数会偏向高频词,所以可以进一步引入 PMI(点互信息):

PMI(x, y) = log( P(x, y) / (P(x) * P(y)) )

PMI 可以提升“强相关但不一定最高频”的候选词权重。实际使用时建议给低频词设置最小出现次数,否则冷门词可能因为样本太少而得到虚高分。

方法二:词向量与余弦相似度

如果关键词库中存在大量长尾词,或者用户搜索词之间有明显语义关系,可以用 Word2Vec、FastText、Sentence-BERT、OpenAI Embedding 等方法,把关键词转成向量后计算余弦相似度。

词向量的优势是能补足共现数据的稀疏问题。例如“蓝牙耳机”和“无线耳机”未必频繁共现,但语义距离很近。缺点是它可能推荐出语义相近但业务上不该展示的词,所以仍需要类目、黑名单和人工规则兜底。

方法三:混合排序

比较实用的做法是混合排序。可以先用共现关系召回候选词,再用词向量补充语义相似度,最后加入业务指标:

score = 0.45 * PMI分
      + 0.30 * 语义相似度
      + 0.15 * 搜索热度
      + 0.10 * 点击或转化表现

权重不需要一开始就追求复杂。早期可以用经验权重快速上线,然后通过点击率、搜索无结果率、二跳搜索率和转化率来迭代。

Python 示例代码

下面示例演示如何基于搜索会话构建共现关系,并结合可选的词向量相似度返回相关关键词:

import math
from collections import Counter

import numpy as np


def cosine(vec_a, vec_b):
    vec_a = np.asarray(vec_a)
    vec_b = np.asarray(vec_b)
    denom = np.linalg.norm(vec_a) * np.linalg.norm(vec_b)
    if denom == 0:
        return 0.0
    return float(np.dot(vec_a, vec_b) / denom)


def build_stats(search_sessions):
    keyword_freq = Counter()
    pair_freq = Counter()

    for session in search_sessions:
        words = list(dict.fromkeys(session))
        for word in words:
            keyword_freq[word] += 1
        for i, left in enumerate(words):
            for right in words[i + 1:]:
                pair = tuple(sorted((left, right)))
                pair_freq[pair] += 1

    return keyword_freq, pair_freq, len(search_sessions)


def pmi_score(target, candidate, keyword_freq, pair_freq, total_sessions):
    pair = tuple(sorted((target, candidate)))
    co_count = pair_freq.get(pair, 0)
    if co_count < 2:
        return 0.0

    p_xy = co_count / total_sessions
    p_x = keyword_freq[target] / total_sessions
    p_y = keyword_freq[candidate] / total_sessions
    return math.log(p_xy / (p_x * p_y))


def get_related_keywords(target, keyword_list, search_sessions, vectors=None, top_n=10):
    keyword_freq, pair_freq, total_sessions = build_stats(search_sessions)
    candidates = [word for word in keyword_list if word != target]

    results = []
    for candidate in candidates:
        pmi = pmi_score(target, candidate, keyword_freq, pair_freq, total_sessions)
        semantic = 0.0
        if vectors and target in vectors and candidate in vectors:
            semantic = cosine(vectors[target], vectors[candidate])

        heat = math.log1p(keyword_freq.get(candidate, 0))
        score = 0.55 * pmi + 0.30 * semantic + 0.15 * heat

        if score > 0:
            results.append((candidate, round(score, 4)))

    results.sort(key=lambda item: item[1], reverse=True)
    return results[:top_n]


keyword_list = ["iPhone", "手机壳", "保护膜", "蓝牙耳机", "快充", "数据线", "安卓手机"]
search_sessions = [
    ["iPhone", "手机壳", "保护膜"],
    ["iPhone", "蓝牙耳机", "快充"],
    ["iPhone", "数据线", "快充"],
    ["安卓手机", "数据线", "快充"],
    ["iPhone", "手机壳", "蓝牙耳机"],
]

print(get_related_keywords("iPhone", keyword_list, search_sessions, top_n=5))

这段代码适合做原型验证。生产环境中,建议把共现统计、PMI 计算和热度归一化放到离线任务中定时更新;线上只负责读取候选词、做轻量重排和缓存结果。

上线优化建议

  • 控制低频噪声:对共现次数、搜索频次设置最低阈值,减少偶然会话带来的干扰。
  • 做类目约束:电商、内容站或工具站都应避免跨类目推荐过远的词。
  • 保留人工干预:对品牌词、敏感词、违规词和商业价值较高的词设置黑名单或白名单。
  • 记录反馈指标:重点观察点击率、搜索无结果率、二跳搜索率和转化率。
  • 定期更新词库:热门词和业务词会变化,推荐结果需要随数据滚动更新。

总结

基于关键词库的相关搜索词推荐,核心不是单一算法,而是“召回 + 相关度计算 + 业务排序”的组合。共现矩阵能反映真实用户行为,PMI 可以修正高频词偏置,词向量能补足语义相似度,综合排序则让结果更贴近实际业务目标。对于多数站内搜索场景,先用这套混合方法搭建可解释的基础版本,再通过数据反馈持续调权,是比较稳妥的落地路径。