导读:本期聚焦于上海GEO公司创作的《如何通过数组实现简单的词频向量模型并实战计算变量文本相似度》,敬请观看详情。文本相似度计算是搜索、推荐和信息检索领域的基础能力,而词频向量模型是最容易上手的一种实现思路。本文将从分词与词表构建讲起,演示如何用普通数组统计每段文本的词频,把文字转换成可以进行数学运算的向量,再通过余弦相似度公式衡量两段文本的接近程度。文中给出完整的分词、向量化、相似度计算代码示例,并对比不同长度文本的处理技巧与常见坑点,帮助你理解向量空间模型的核心原理,并能独立实现一个简单可用的文本相似度计算工具。

判断两段文字是不是在说同一件事,是搜索排序、查重系统、智能客服等多个场景都绕不开的问题。解决这个问题最经典的思路就是词频向量模型:先把文本切分成词,再统计每个词出现的次数,把文本变成一个数字数组,最后用数学方法比较两个数组的距离或夹角。整个过程不依赖任何复杂的机器学习框架,用基础的数组操作就能实现,非常适合用来理解向量空间模型的本质。

如何通过数组实现简单的词频向量模型并实战计算变量文本相似度

一、词频向量的基本原理:把文字变成数字数组

词频向量模型的核心假设很简单:如果两段文本用词越接近,它们的内容就越接近。要让计算机能比较文字,第一步必须把文字数字化。具体做法是先建立一个统一词表,把所有出现过的词收集起来并编号,然后针对每段文本,统计词表中每个词在这段文本里出现了几次,按词表顺序依次填入数组,得到的数字数组就是这段文本的词频向量。

举个例子,假设词表是["我", "喜欢", "编程", "学习"],那么文本"我喜欢编程"的词频向量就是[1, 1, 1, 0],文本"我喜欢学习编程"的词频向量就是[1, 1, 1, 1]。这样一来,比较两段文本的相似度,就变成了比较两个数组的相似度,问题从语言层面转换到了数学层面。

需要注意的是,中文没有天然的分隔符,需要先分词。下面用Python配合jieba库演示完整的向量化过程:

import jieba

def build_vocab(texts):
    """根据语料构建词表"""
    vocab = {}
    for text in texts:
        for word in jieba.lcut(text):
            # 过滤长度为1的单字和空白符,降低噪音
            if len(word.strip()) > 1:
                vocab[word] = vocab.get(word, 0) + 1
    # 按出现次数降序排列,高频词排在前面
    sorted_words = sorted(vocab.items(), key=lambda x: x[1], reverse=True)
    return {word: idx for idx, (word, _) in enumerate(sorted_words)}

def text_to_vector(text, vocab):
    """把文本转换为词频向量(数组)"""
    vector = [0] * len(vocab)
    for word in jieba.lcut(text):
        if word in vocab:
            vector[vocab[word]] += 1
    return vector

texts = ["我喜欢编程", "我喜欢学习编程", "今天天气不错"]
vocab = build_vocab(texts)
print(vocab)
# 输出类似:{'喜欢': 0, '编程': 1, '喜欢编程': 2, '学习': 3, '今天': 4, '天气': 5, '不错': 6}
print(text_to_vector("我喜欢编程", vocab))

这段代码里,build_vocab负责建立词与数组下标的映射关系,text_to_vector则负责把任意文本映射成与词表等长的数组。词频向量本质上就是一个定长数组,长度由词表决定,这正是它能放进统一坐标系比较的前提。

二、用余弦相似度计算数组之间的接近程度

有了词频向量之后,如何量化两个数组的相似程度?直接算欧氏距离并不是好选择,因为文本有长有短,长文本的词频数值天然偏大,欧氏距离会被文本长度主导。余弦相似度只关心两个向量方向的夹角,不受向量长度影响,特别适合文本场景。

余弦相似度的公式为:cos(A, B) = (A·B) / (||A|| × ||B||),其中A·B表示两个数组的点积,即对应位置元素相乘后求和;||A||表示向量的模长,即各元素平方和再开方。结果越接近1表示越相似,接近0表示几乎无关。手写实现如下:

import math

def cosine_similarity(vec_a, vec_b):
    """计算两个词频向量数组的余弦相似度"""
    # 点积:对应位置相乘再累加
    dot_product = sum(a * b for a, b in zip(vec_a, vec_b))
    # 各自的模长
    norm_a = math.sqrt(sum(a * a for a in vec_a))
    norm_b = math.sqrt(sum(b * b for b in vec_b))
    if norm_a == 0 or norm_b == 0:
        # 空文本或全被过滤的情况,避免除零
        return 0.0
    return dot_product / (norm_a * norm_b)

vec1 = text_to_vector("我喜欢编程", vocab)
vec2 = text_to_vector("我喜欢学习编程", vocab)
vec3 = text_to_vector("今天天气不错", vocab)

print(cosine_similarity(vec1, vec2))  # 接近1,内容高度相关
print(cosine_similarity(vec1, vec3))  # 接近0,内容几乎无关

运行结果中,前两条文本的相似度会明显偏高,因为它们共享"喜欢"和"编程"两个关键词;而"我喜欢编程"和"今天天气不错"几乎没有共同词,相似度趋近于0。这个结果符合人的直觉判断,说明模型是有效的。

这里有一个容易踩的坑:如果两段文本完全相同,余弦值会等于1;如果其中一段是另一段的简单重复(比如"喜欢编程喜欢编程"),余弦值依然是1,因为词频只按比例放大,方向没变。这在查重场景下可能是个问题,需要结合长度惩罚或改用Jaccard系数补充判断。

三、处理变长文本的进阶技巧与优化方向

基础的词频模型在实际使用中会遇到一些问题。最明显的是"的"、"了"、"是"这类高频虚词会占据词频的主要部分,干扰真正有意义的词的权重。改进方法是采用TF-IDF加权:词频乘以逆文档频率,一个词在越少的文档中出现,它的区分能力越强,权重就越高。

import math

def compute_idf(all_vectors, vocab_size):
    """统计每个词出现在多少个文档中,计算IDF"""
    doc_count = len(all_vectors)
    idf = [0.0] * vocab_size
    for vec in all_vectors:
        for i in range(vocab_size):
            if vec[i] > 0:
                idf[i] += 1
    return [math.log((doc_count + 1) / (freq + 1)) + 1 for freq in idf]

def tfidf_vector(vec, idf):
    """把词频向量转换为TF-IDF加权向量"""
    return [tf * w for tf, w in zip(vec, idf)]

all_vecs = [text_to_vector(t, vocab) for t in texts]
idf = compute_idf(all_vecs, len(vocab))
weighted = [tfidf_vector(v, idf) for v in all_vecs]
print(cosine_similarity(weighted[0], weighted[1]))

另一个优化方向是稀疏存储。当词表达到几万规模时,每段文本的向量大部分位置都是0,用Python列表存储会浪费大量内存。此时可以改用字典只记录非零位置,或者直接使用scipy的稀疏矩阵,计算点积时只遍历非零项,效率会大幅提升。

最后要认识到词频模型的局限:它完全不理解语义,"电脑"和"计算机"会被当成两个不同的词。如果需要语义层面的相似度,可以考虑word2vec、BERT等向量化方案。但对于入门理解、轻量级查重、关键词匹配这类需求,数组实现的词频向量加余弦相似度依然是性价比最高的方案,代码不到百行就能跑通,也为后续学习更复杂的嵌入模型打下了坚实的概念基础。

词频向量文本相似度余弦相似度修改时间:2026-09-11 00:10:40

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260911/54339.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。