【AIGC】AI如何匹配RAG知识库:关键词搜索

智兔唯新 2024-10-23 09:31:01 阅读 54

关键词搜索

引言jieba库简介TF-IDF简介实践例子用jieba库提取关键词计算TF-IDF计算文档和查询相似度结果完整代码:

总结

引言

RAG作为减少模型幻觉和让模型分析、回答私域相关知识最简单高效的方式,我们除了使用之外可以尝试了解其是如何实现的。在实现RAG的过程中,有语义搜索也有关键词搜索,我们这篇文章来用jieba库以及TF-IDF实现关键词搜索RAG。

jieba库简介

jieba(结巴)是一个在Python中广泛使用的分词库,特别适用于中文文本处理。jieba库不仅支持基本的分词功能,还提供了关键词提取、词性标注、命名实体识别等多种功能。在关键词检测领域,jieba库的TF-IDF和TextRank算法被广泛应用于提取文本中的关键词。

TF-IDF简介

TF-IDF(Term Frequency-Inverse Document Frequency)是一种用于信息检索和文本挖掘的常用加权技术。它通过计算词汇在文档中的频率(Term Frequency, TF)和在整个语料库中的逆文档频率(Inverse Document Frequency, IDF),来评估词汇的重要性和相关性。

TF-IDF的计算公式如下:

在这里插入图片描述

简单来说关键词出现的次数越多且存在于其他文档中的频率越低,那么这个关键词就越重要。

实践

我们来模拟用户询问问题,模型根据问题从知识库中检索出相关文档,并根据检索到的文档生成回答。

我们假设用户输出是text1,text2中是多个以";"隔开的文档,我们使用jieba库分割关键词并使用TF-IDF去实现关键词搜索RAG,搜索text2中最适配text1的文档。

在这里插入图片描述

例子

<code># Example text

text = "发到顺丰"

# Example text2

text2 = "您好,是您拨打的客服电话吗;你好,我的这个货想要通过顺丰去发;订单号发我一下;xxxxxx;好的我这边给您发顺丰"

用jieba库提取关键词

# 切割 text2 并将其作为文档

documents = text2.split(';')

# 提取关键词的函数

def extract_keywords(text):

return jieba.analyse.extract_tags(text)

# 提取查询关键词

query_keywords = extract_keywords(text)

# 提取文档关键词

documents_keywords = [extract_keywords(doc) for doc in documents]

计算TF-IDF

各自计算查询关键词和文档关键词的TF-IDF为之后计算余弦相似度进行准备

# 计算查询关键词的词频 (TF)

query_keyword_counts = Counter(query_keywords)

# 总文档数

total_documents = len(documents)

# 计算所有关键词的逆文档频率 (IDF)

all_keywords = set()

for doc_keywords in documents_keywords:

all_keywords.update(doc_keywords)

keyword_idf = { }

for keyword in all_keywords:

doc_count_containing_keyword = sum(1 for doc_keywords in documents_keywords if keyword in doc_keywords)

keyword_idf[keyword] = math.log((1 + total_documents) / (1 + doc_count_containing_keyword)) + 1

# 计算查询关键词的 TF-IDF

query_tfidf = { }

for keyword, count in query_keyword_counts.items():

tf = count

idf = keyword_idf.get(keyword, 0)

query_tfidf[keyword] = tf * idf

# 计算所有文档的 TF-IDF

documents_tfidf = []

for doc_keywords in documents_keywords:

doc_keyword_counts = Counter(doc_keywords)

doc_tfidf = { }

for keyword, count in doc_keyword_counts.items():

tf = count

idf = keyword_idf.get(keyword, 0)

doc_tfidf[keyword] = tf * idf

documents_tfidf.append(doc_tfidf)

计算文档和查询相似度

通过计算余弦相似度来计算查询词与文档相似度

# 计算余弦相似度

def cosine_similarity(vec1, vec2):

intersection = set(vec1.keys()) & set(vec2.keys())

numerator = sum(vec1[x] * vec2[x] for x in intersection)

sum1 = sum(vec1[x] ** 2 for x in vec1)

sum2 = sum(vec2[x] ** 2 for x in vec2)

denominator = math.sqrt(sum1) * math.sqrt(sum2)

if not denominator:

return 0.0

else:

return float(numerator) / denominator

# 计算文档与查询的相似度

similarities = []

for doc_tfidf in documents_tfidf:

similarity = cosine_similarity(query_tfidf, doc_tfidf)

similarities.append(similarity)

# 按相似度排序并返回结果

sorted_documents = sorted(zip(documents, similarities), key=lambda x: x[1], reverse=True)

# 打印结果

for i, (doc, score) in enumerate(zip(documents, similarities)):

print(f"Document { i+1}: { doc}\nScore: { score}\n")

结果

Score得分越高,则文档越匹配查询词,可以看到,我们根据关键词搜索,找到了最适配text1的文档,Document 2。

Document 1: 您好,是您拨打的客服电话吗

Score: 0.0

Document 2: 你好,我的这个货想要通过顺丰去发

Score: 0.4472135954999579

Document 3: 订单号发我一下

Score: 0.0

Document 4: xxxxxx

Score: 0.0

Document 5: 好的我这边给您发顺丰

Score: 0.0

完整代码:

import jieba

from jieba.analyse import default_tfidf

from collections import Counter

import math

# Example text

text = "发到顺丰"

# Example text2

text2 = "您好,是您拨打的客服电话吗;你好,我的这个货想要通过顺丰去发;订单号发我一下;xxxxxx;好的我这边给您发顺丰"

# 切割 text2 并将其作为文档

documents = text2.split(';')

# 提取关键词的函数

def extract_keywords(text):

return jieba.analyse.extract_tags(text)

# 提取查询关键词

query_keywords = extract_keywords(text)

# 提取文档关键词

documents_keywords = [extract_keywords(doc) for doc in documents]

# 计算查询关键词的词频 (TF)

query_keyword_counts = Counter(query_keywords)

# 总文档数

total_documents = len(documents)

# 计算所有关键词的逆文档频率 (IDF)

all_keywords = set()

for doc_keywords in documents_keywords:

all_keywords.update(doc_keywords)

keyword_idf = { }

for keyword in all_keywords:

doc_count_containing_keyword = sum(1 for doc_keywords in documents_keywords if keyword in doc_keywords)

keyword_idf[keyword] = math.log((1 + total_documents) / (1 + doc_count_containing_keyword)) + 1

# 计算查询关键词的 TF-IDF

query_tfidf = { }

for keyword, count in query_keyword_counts.items():

tf = count

idf = keyword_idf.get(keyword, 0)

query_tfidf[keyword] = tf * idf

# 计算所有文档的 TF-IDF

documents_tfidf = []

for doc_keywords in documents_keywords:

doc_keyword_counts = Counter(doc_keywords)

doc_tfidf = { }

for keyword, count in doc_keyword_counts.items():

tf = count

idf = keyword_idf.get(keyword, 0)

doc_tfidf[keyword] = tf * idf

documents_tfidf.append(doc_tfidf)

# 计算余弦相似度

def cosine_similarity(vec1, vec2):

intersection = set(vec1.keys()) & set(vec2.keys())

numerator = sum(vec1[x] * vec2[x] for x in intersection)

sum1 = sum(vec1[x] ** 2 for x in vec1)

sum2 = sum(vec2[x] ** 2 for x in vec2)

denominator = math.sqrt(sum1) * math.sqrt(sum2)

if not denominator:

return 0.0

else:

return float(numerator) / denominator

# 计算文档与查询的相似度

similarities = []

for doc_tfidf in documents_tfidf:

similarity = cosine_similarity(query_tfidf, doc_tfidf)

similarities.append(similarity)

# 按相似度排序并返回结果

sorted_documents = sorted(zip(documents, similarities), key=lambda x: x[1], reverse=True)

# 打印结果

for i, (doc, score) in enumerate(zip(documents, similarities)):

print(f"Document { i+1}: { doc}\nScore: { score}\n")

总结

关键词匹配是RAG搜索中的一个很重要的方法之一,但是关键词匹配的缺点也很明显,就是关键词匹配的召回率很低,因为关键词匹配只匹配了关键词,而没有匹配到关键词的上下文。所以一般需要结合语义搜索去一起进行。可以参考我的另一片介绍语义搜索的文章去进行双路召回搜索。【AIGC】AI如何匹配RAG知识库: Embedding实践,语义搜索

在上面我们的实践中,我们使用jieba库去进行分词,然后通过TF-IDF算法去计算关键词的权重,然后通过余弦相似度去计算文档和查询的相似度,最后通过相似度去排序,返回结果。基本上这个一个比较通用的关键词匹配RAG数据库的方法。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。