创新实训项目博客

本文介绍了使用RAG技术和现有LLM开发的心理咨询模型项目,涉及数据获取(心理学书籍和开源问答数据集)、数据处理(生成QA对、章节主题绑定、两阶段检索)、文本嵌入(m3e模型)以及近似检索索引(IVFFLAT)。项目目标是创建一个具备分析、推荐和对话功能的心理日志网站。
摘要由CSDN通过智能技术生成

本项目的目标是使用RAG技术和现有LLM开发一个具有心理方面专业知识的心理行业大模型,并基于此开发一个具有日志分析总结、相关推荐、对话等功能的心理日志网站。

一、获取数据

考虑到我们项目的目标是要做一个关注大众心理、处理日常心理问题的对已有大语言模型的数据增强,需要获取心理方面的数据,我们小组下载了32.2 MB的中文心理学书籍的txt数据,筛去了故事性的书籍。

二、数据处理

在文本进行向量嵌入之前,为了保证RAG检索得到的prompt的质量,我们对书籍文本进行处理。

书籍数据常常需要一个章节甚至更多才能讲完对一个问题的分析,一个章节可能很大篇幅是问题的引入和提出,后面才是解决方案。如果直接对整本书籍切片后进行向量嵌入,怀疑检索到的数据可能是和query相关的问题引入而非一个高质量prompt需要的问题的解决方案。

文本片段与query的相似性和文本片段是否包含query的答案(相关性)是两回事。

对于这个问题,我们提出了两种处理思路:

1.使用已有的语言模型根据书籍内容生成QA对,将QA对作为RAG检索的数据源;

2.对每段需要嵌入的数据进行绑定,提取每章的主题并将其放到每段要进行嵌入文本的末尾,这样每个问题都会是主题相关;

3.两阶段检索的解决方案;

下面介绍根据这两种思路进行的实践:

在调研和尝试后发现,部分心理学书籍偏向理论性,章节都在讲述心理学的理论;有的偏向故事性,前面的引入故事较长,需要很长的篇幅才能讲述解决方案;这两种书籍都难以在检索中为RAG提供有效的prompt。考虑到RAG对数据的依赖性较强,决定放弃使用书籍作为原始数据集。

一、数据获取

检索找到两个开源的心理咨询问答数据集,格式为提出生活中的苦恼困难,下面附上回答,符合我们项目的目标。

❤️Emotional First Aid Dataset, 心理咨询问答语料库_数据集-阿里云天池 (aliyun.com)icon-default.png?t=N7T8https://tianchi.aliyun.com/dataset/61868

Mike/Chinese-Psychological-QA-DataSet - 码云 - 开源中国 (gitee.com)icon-default.png?t=N7T8https://gitee.com/miketickle/Chinese-Psychological-QA-DataSet

 具体数据格式如下:(这里给出的例子是一个问题对应一个回答,但问答实际上是一个列表,问题与回答是一对多的关系)

二、数据处理

因为要对数据进行嵌入,嵌入模型对文本有chunk_size的限制,且为了方便做prompt,对问答对进行绑定和切分。

下载开源的json数据集,抽取出每条问题和对应的回答,对于一个问题,根据问题和回答的长度对其进行组合。观察数据集,问题由title和content组成,一般content包含更多信息。因此如果title和content的长度小于chunk_size/3,则保留两者,否则优先选择content。一个问题和其对应的一条或多条回答组合作为一条文本写入txt文件。

这部分的具体逻辑见代码。

def extract_questions_and_answers(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        data = json.load(f)

    combinationList=[]
    for item in data:
        combination=''
        question=''
        residualSize=chunkSize
        
        #question
        ques_info = item['ques_info']
        title = ques_info['title'].replace('\r', '').replace('\n', '')
        content = ques_info['content'].replace('\r', '').replace('\n', '')
        if len(content)+len(title)<chunkSize/3:
            question=title+content
            residualSize-=len(question)
        elif len(content)<chunkSize/2:
            question=content
            residualSize-=len(content)
        elif len(title)<chunkSize/2:
            question=title
            residualSize-=len(title)
        else:
            continue
        combination=question
        
        #answer
        answers_info = item['answers_info']
        i=0
        while i<len(answers_info):
            answerForOne=''
            combination=question       
            count=0#记录一个当前chunk中存储了多少个answer
            while i<len(answers_info):
                answer=answers_info[i]
                answer_content = answer['content'].replace('\r', '').replace('\n', '')
                if len(answer_content)>residualSize: #单个answer大于residualSize
                    if count>0:#如果answerForOne已经有answer
                        break
                    else:
                        answerForOne+=answer_content[:residualSize-10]
                        i+=1
                        count+=1 
                        break
                elif len(answer_content)+len(answerForOne)>residualSize:#answerForOne中已有answer
                    break
                else:
                    answerForOne+=answer_content
                    i+=1
                    count+=1  
                       
            combination+=answerForOne                  
            combinationList.append(combination)    

    return combinationList

最后获取到106417条原始文本数据。

三、文本嵌入

 原本计划直接使用llamaindex进行下面的工作,但没能找到合适的llamaindex的技术文档(全面的文档只找到英文的),且如果使用openai提供的嵌入模型和LLM需要付费。因此决定先自己部署免费的嵌入模型,之后学习使用llamaindex中优化的检索策略对我们的体系进行改进。

搜索发现huggingface的m3e模型具有良好的中文文本嵌入能力,通过pip install SentenceTransformer即可直接从huggingface网站上获取模型并在本地进行向量嵌入。

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('moka-ai/m3e-base').cuda()


text_documents=[]
# 打开文本文件以供读取
with open('chunk.txt', 'r', encoding='utf-8') as file:
    # 逐行读取文件内容
    for line in file:
        # 去除行末的换行符
        line = line.rstrip('\n')
        text_documents.append(line)


#Sentences are encoded by calling model.encode()
embeddings = model.encode(text_documents)

注:这里使用m3e模型可能由于网络问题无法访问huggingface网站,可以更改Anaconda目录中的\Lib\site-packages\huggingface_hub目录下的constants.py文件中的ENDPOINT = os.getenv("HF_ENDPOINT") or (_HF_DEFAULT_STAGING_ENDPOINT if _staging_mode else _HF_DEFAULT_ENDPOINT)替换成ENDPOINT = " https://hf-mirror.com" ,指定使用hugging-face提供的镜像。

四、近似检索索引构建

faiss近似近邻检索库有较为全面的技术文档。我们目前的数据量为106417*784,向量条数较少而向量维度较高。决定尝试使用简单且能在一定程度上保证精度的IVFFLAT索引进行尝试,之后如果检索速度慢会调整索引。(我们的网站对用户的日志分析和建议提供并不是实时的,对RAG检索速度的要求并不是很高)

faiss官方给出的IVFFLAT实例diamagnetic如下,可以在CPU或GPU上运行索引的构建和检索。

## Using an IVF index

nlist = 100
quantizer = faiss.IndexFlatL2(d)  # the other index
index_ivf = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)
# here we specify METRIC_L2, by default it performs inner-product search

# make it an IVF GPU index
gpu_index_ivf = faiss.index_cpu_to_gpu(res, 0, index_ivf)

assert not gpu_index_ivf.is_trained
gpu_index_ivf.train(xb)        # add vectors to the index
assert gpu_index_ivf.is_trained

gpu_index_ivf.add(xb)          # add vectors to the index
print(gpu_index_ivf.ntotal)

k = 4                          # we want to see 4 nearest neighbors
D, I = gpu_index_ivf.search(xq, k)  # actual search
print(I[:5])                   # neighbors of the 5 first queries
print(I[-5:])                  # neighbors of the 5 last queries

。。待更新 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值