【内部项目预研】对信息分类进行探索

分析输入信息的类别,目前是一个闭集、但是要按照开集的方式来进行分析;名称越乱越好,让系统自己来进行划分。必须首先考虑传统的方法;优先考虑数据结构的构建;强化监控机制的构建、首先进行认知和技术积累。

一、数据情况

5ea6ed0b-7aae-4ee4-86ed-6d69377cd797

找到了清华大学整理的关于体育的新闻,每篇一个新闻,第一行是标题,而后是内容。基于现有工具,直接撰写代码。首先按照9条为基准,完成代码撰写。

这9篇文章,目视是包括了 足球、高尔夫球、兵乓球 这些子类的。

import os
def read_first_line_of_txt_files(folder_path):
# 获取指定文件夹内的所有文件
    files = os.listdir(folder_path)
# 筛选出扩展名为.txt的文件
    txt_files = [f for f in files if f.endswith('.txt')]
# 读取每个.txt文件的第一行
    first_lines = []
for txt_file in txt_files:
        with open(os.path.join(folder_path, txt_file), 'r', encoding='utf-8') as f:
            first_line = f.readline()
            first_lines.append((txt_file, first_line.strip()))  # 添加文件名和第一行内容
return first_lines
folder_path = './sports' # 替换为你要读取的文件夹路径
first_lines = read_first_line_of_txt_files(folder_path)
# 输出文件名和第一行内容
for file_name, first_line in first_lines:
print(f'文件名:{file_name}, 第一行内容:{first_line}')

f3a82d9e-ee82-4ca3-986d-3ee54d252f21

从结果上来看:

一个是发现了一个帆船的问题,所以这里的分类问题是开集任务;

二个是有一些问题,从名字上来看,很难区分出是啥类别。

这里的函数可以继续变化:

def read_name_and_content_of_txt_files(folder_path):
# 获取指定文件夹内的所有文件
    files = os.listdir(folder_path)
# 筛选出扩展名为.txt的文件
    txt_files = [f for f in files if f.endswith('.txt')]
# 读取每个.txt文件的第一行
    first_lines = []
for txt_file in txt_files:
        with open(os.path.join(folder_path, txt_file), 'r', encoding='utf-8') as f:
            first_line = f.readline()
            file_content = f.read()
            first_lines.append((txt_file, first_line.strip(),file_content.strip()))  # 添加文件名和第一行内容
return first_lines

b0e3e677-cdb3-4c7e-95d9-54c97eacfbbe

注意体会这里的小细节。

二、现有流程

目前基于glm3+bge+langchain构建主流的RAG流程。未来我也会结合数据实际,对比一下bce以及千问、百川等的能力。目前已经开始具备相关的能力。

    这里我想做的:

    1、首先是由llm根据标题,直接在我指定的类别上进行分类;

    2、如果llm无法成功分类,则阅读内容,进行区分;

    3、如果出现“开集”情况,也需要进行报告(我将迭代重新执行任务)

这里实际上没有使用RAG,然后不使用自动agent等的功能。也不使用lcel的相关能力。一个可以参考的代码:

from langchain_community.llms.chatglm3 import ChatGLM3
from langchain.memory import ConversationBufferMemory
from langchain.sql_database import SQLDatabase
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.chains import SimpleSequentialChain
import os
os.environ["LANGCHAIN_TRACING_V2"] ="true"
os.environ["LANGCHAIN_API_KEY"]="ls__96d567894428421db2d42dec4edde0b4"
file=open('santi.txt',encoding="GBK")
try:
    article=file.read() 
finally:
    file.close()
endpoint_url = "https://u4378-b286-79485e25.westc.gpuhub.com:8443/v1/chat/completions"
llm = ChatGLM3(
    endpoint_url=endpoint_url,
    max_tokens=8000,
    top_p=0.9,
    timeout=999
)
fact_extraction_prompt = PromptTemplate(
    input_variables=["text_input"],
    template="从下列才来中提取事实.不要包含观点.给每个事实一个序号并且保证简短 :{text_input}"
)
investor_update_prompt = PromptTemplate(
    input_variables=["facts"],
    template="你是一名作家,基于以下事实写一个短篇小说。不要丢失关键信息,不要包含序号: {facts}"
)
fact_extraction_chain = LLMChain(llm=llm, prompt=fact_extraction_prompt)
investor_update_chain = LLMChain(llm=llm, prompt=investor_update_prompt)   
full_chain = SimpleSequentialChain(
    chains=[fact_extraction_chain,investor_update_chain],
    verbose = True
)
response = full_chain.run(article)
print("response:",response)

38894162-f9ad-4729-9792-7dc59a6708db

三、具体实验

3.1 思路一,直接根据名称进行划分

from langchain_community.llms.chatglm3 import ChatGLM3
from langchain.memory import ConversationBufferMemory
from langchain.sql_database import SQLDatabase
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.chains import SimpleSequentialChain
import os
os.environ["LANGCHAIN_TRACING_V2"] ="true"
os.environ["LANGCHAIN_API_KEY"]="ls__96d567894428421db2d42dec4edde0b4"
def read_first_line_of_txt_files(folder_path):
# 获取指定文件夹内的所有文件
    files = os.listdir(folder_path)
# 筛选出扩展名为.txt的文件
    txt_files = [f for f in files if f.endswith('.txt')]
# 读取每个.txt文件的第一行
    first_lines = []
for txt_file in txt_files:
        with open(os.path.join(folder_path, txt_file), 'r', encoding='utf-8') as f:
            first_line = f.readline()
            first_lines.append((txt_file, first_line.strip()))  # 添加文件名和第一行内容
return first_lines
###prework###
folder_path = './sports' # 替换为你要读取的文件夹路径
content = ""
first_lines = read_first_line_of_txt_files(folder_path)
for file_name, first_line in first_lines:
#print(f'文件名:{file_name}, 第一行内容:{first_line}')
    content = content + "\n" + first_line  # 将列表中的内容连接成一个字符串,用 \n 分隔
###prompt####
sort_prompt = PromptTemplate(
    input_variables=["text_input"],
    template="将下列材料进行分类。按照“足球”“高尔夫球”“兵乓球”进行区分。不要丢失信息。如果无法分类则划为“其他”:{text_input}"
)
###llm###
endpoint_url = "https://u4378-b286-79485e25.westc.gpuhub.com:8443/v1/chat/completions"
llm = ChatGLM3(endpoint_url=endpoint_url,temperature = 0, timeout=999)
###chain###
sort_chain = LLMChain(llm=llm, prompt=sort_prompt)
full_chain = SimpleSequentialChain(
    chains=[sort_chain],
    verbose = True
)
###run###
response = full_chain.run(content)
print("response:",response)

不考虑正确性,这是唯一可以召全的方法

0e3722d9-1a3b-4611-aaf2-e05ec076e4cf

这里需要修改的是标点符号。提示词的撰写非常的“微妙”。而且这里的划分很可能是错误的。再做两次平行实验。

实验2

4e02ee46-9c89-4a97-9737-e13bbecd7c2a

实验3

0a302e69-de42-49d2-8ee1-132b608b57a0

这个方向可以被证明是错误的,也就是说将不同的内容混在一起,让llm去进行分类,它会丢东西、也不稳定。

3.2 思路二,每一条带标题和内容一起进行分类。

from langchain_community.llms.chatglm3 import ChatGLM3
from langchain.memory import ConversationBufferMemory
from langchain.sql_database import SQLDatabase
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.chains import SimpleSequentialChain
import os
os.environ["LANGCHAIN_TRACING_V2"] ="true"
os.environ["LANGCHAIN_API_KEY"]="ls__96d567894428421db2d42dec4edde0b4"
def read_name_and_content_of_txt_files(folder_path):
# 获取指定文件夹内的所有文件
    files = os.listdir(folder_path)
# 筛选出扩展名为.txt的文件
    txt_files = [f for f in files if f.endswith('.txt')]
# 读取每个.txt文件的第一行
    first_lines = []
for txt_file in txt_files:
        with open(os.path.join(folder_path, txt_file), 'r', encoding='utf-8') as f:
            first_line = f.readline()
            file_content = f.read()
            first_lines.append((txt_file, first_line.strip(),file_content.strip()))  # 添加文件名和第一行内容
return first_lines
###prompt####
sort_prompt = PromptTemplate(
    input_variables=["text_input"],
    template="将这段材料按照“足球”“高尔夫球”“兵乓球”进行区分。如果无法分类则划为“其他”:{text_input}。返回的结果只能是“足球”“高尔夫球”“兵乓球”和“其他”中的一个。"
)
###llm###
endpoint_url = "https://u4378-b286-79485e25.westc.gpuhub.com:8443/v1/chat/completions"
llm = ChatGLM3(endpoint_url=endpoint_url,temperature = 0, timeout=999)
###chain###
sort_chain = LLMChain(llm=llm, prompt=sort_prompt)
full_chain = SimpleSequentialChain(
    chains=[sort_chain],
    verbose = True
)
###main work###
folder_path = './sports' # 替换为你要读取的文件夹路径
read_files = read_name_and_content_of_txt_files(folder_path)
for file_name, first_line,file_content in read_files:
#print(f'文件名:{file_name}, 名称:{first_line},内容:{file_content}')
    content = f'名称:{first_line},内容:{file_content}'
###run###
    response = full_chain.run(content)
print("response:",response)

233ea28d-e47d-4af0-8522-931d5cec4d7b

应该说仍然是相当糟糕的,语句的划分、结果的判断,提示词怎么写都没有用。

3.3 思路三,使用向量模型

embeded一直都是用来进行RAG的,实际上它不是用于分类最合适的吗?

import numpy as np
import faiss
from numpy import dot
from numpy.linalg import norm
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('/home/jsxyhelu/CODES/bge-large-zh-v1.5')
###functions && classes####
def get_datas_embedding(datas):
return model.encode(datas)
# 构建索引,FlatL2为例
def create_index(datas_embedding):
    index = faiss.IndexFlatL2(datas_embedding.shape[1])  # 这里必须传入一个向量的维度,创建一个空的索引
    index.add(datas_embedding)   # 把向量数据加入索引
return index
# 查询索引
def data_recall(faiss_index, query, top_k):
    query_embedding = model.encode([query])
    Distance, Index = faiss_index.search(query_embedding, top_k)
return Index
###############################
#query = "体育"
# 且能支持跨语言
query = "高尔夫球"
documents = [
"马晓旭意外受伤让国奥警惕 无奈大雨格外青睐殷家军",
"商瑞华首战复仇心切 中国玫瑰要用美国方式攻克瑞典",
"性感比基尼亮丽奥帆中心 “帆女郎”亲吻阳光(图)",
"女单抽签:张怡宁独守上半区 王楠郭跃遭遇劲敌",
"冠军球队迎新欢乐派对 黄旭获大奖张军赢下PK赛",
"休斯顿公开赛尼蒂斯暂领先 米克尔森77杆面临淘汰",
"达利迫不及待要回“家” 下周将角逐爱尔兰公开赛",
"黄沧江:没有球僮服务是传统?",
"米克尔森确认参加大满贯赛 伍兹回应这是积极信号"
]
datas_embedding = get_datas_embedding(documents)
faiss_index = create_index(datas_embedding)
sim_data_Index = data_recall(faiss_index,query, 5)
print("相似的top5数据是:")
for index in sim_data_Index[0]:
print(documents[int(index)] + "\n")

在这里最初的反向测试中,就出现了令人惊讶的结果,分类成功了绝大多数的信息。

f6771cf7-4725-4077-8359-210ef4978580

再进行正向测试:

d89031ea-d981-487c-8db9-4a3094917ef0

其实这句话,如果没有背景知识的话,很难判断出来是和高尔夫球相关的。做系列实验。

import numpy as np
import faiss
from numpy import dot
from numpy.linalg import norm
from sentence_transformers import SentenceTransformer
import os
model = SentenceTransformer('/home/jsxyhelu/CODES/bge-large-zh-v1.5')
def read_first_line_of_txt_files(folder_path):
# 获取指定文件夹内的所有文件
    files = os.listdir(folder_path)
# 筛选出扩展名为.txt的文件
    txt_files = [f for f in files if f.endswith('.txt')]
# 读取每个.txt文件的第一行
    first_lines = []
for txt_file in txt_files:
        with open(os.path.join(folder_path, txt_file), 'r', encoding='utf-8') as f:
            first_line = f.readline()
            first_lines.append((txt_file, first_line.strip()))  # 添加文件名和第一行内容
return first_lines
folder_path = './sports' # 替换为你要读取的文件夹路径
first_lines = read_first_line_of_txt_files(folder_path)
###functions && classes####
def get_datas_embedding(datas):
return model.encode(datas)
# 构建索引,FlatL2为例
def create_index(datas_embedding):
    index = faiss.IndexFlatL2(datas_embedding.shape[1])  # 这里必须传入一个向量的维度,创建一个空的索引
    index.add(datas_embedding)   # 把向量数据加入索引
return index
# 查询索引
def data_recall(faiss_index, query, top_k):
    query_embedding = model.encode([query])
    Distance, Index = faiss_index.search(query_embedding, top_k)
return Distance,Index
###############################
#query = "体育"
# 且能支持跨语言
#query = "达利迫不及待要回“家” 下周将角逐爱尔兰公开赛"
documents = [
"足球",
"乒乓球",
"高尔夫球",
"篮球",
"游泳",
"跑步",
"网球",
"举重",
"羽毛球"
]
datas_embedding = get_datas_embedding(documents)
faiss_index = create_index(datas_embedding)
# 输出文件名和第一行内容
for file_name, first_line in first_lines:
print(f'文件名:{file_name}, 第一行内容:{first_line}')
    Distance,sim_data_Index = data_recall(faiss_index,first_line, 1)
for index in sim_data_Index[0]:
print(documents[int(index)] + "   "+ str(Distance[0][0]))

应该说可以正确识别绝大多数的内容,很有启发的价值。

c2a5c0ae-f75a-4c84-b41f-b5424eaec595

对于这里的分类错误的情况,可以看到距离也是比较大的。

【这里需要注意的一点就是我采用的数据集,是否已经被模型学习了,所以还有很多细致的工作需要做】

四、初步小结

那么目前可以想出来的思路是:

1、预先定下来一个开放的分类,作为分类的一句;

2、embeded分析名称,获得初步的结果;这里要同时获得分类的分值;

3、对于分值过低的情况,llm进一步分析其内容,必要情况下由人处理。【初步定这个值为1.33】

from langchain_community.llms.chatglm3 import ChatGLM3
from langchain.memory import ConversationBufferMemory
from langchain.sql_database import SQLDatabase
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.chains import SimpleSequentialChain
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
from langchain_community.vectorstores import FAISS
from sentence_transformers import SentenceTransformer
import os
import faiss
os.environ["LANGCHAIN_TRACING_V2"] ="true"
os.environ["LANGCHAIN_API_KEY"]="ls__96d567894428421db2d42dec4edde0b4"
model = SentenceTransformer('/home/jsxyhelu/CODES/bge-large-zh-v1.5')
###functions && classes####
def read_name_and_content_of_txt_files(folder_path):
# 获取指定文件夹内的所有文件
    files = os.listdir(folder_path)
# 筛选出扩展名为.txt的文件
    txt_files = [f for f in files if f.endswith('.txt')]
# 读取每个.txt文件的第一行
    first_lines = []
for txt_file in txt_files:
        with open(os.path.join(folder_path, txt_file), 'r', encoding='utf-8') as f:
            first_line = f.readline()
            file_content = f.read()
            first_lines.append((txt_file, first_line.strip(),file_content.strip()))  # 添加文件名和第一行内容
return first_lines
def get_datas_embedding(datas):
return model.encode(datas)
# 构建索引,FlatL2为例
def create_index(datas_embedding):
    index = faiss.IndexFlatL2(datas_embedding.shape[1])  # 这里必须传入一个向量的维度,创建一个空的索引
    index.add(datas_embedding)   # 把向量数据加入索引
return index
# 查询索引
def data_recall(faiss_index, query, top_k):
    query_embedding = model.encode([query])
    Distance, Index = faiss_index.search(query_embedding, top_k)
return Distance,Index
###prompt####
documents = [
"足球",
"乒乓球",
"高尔夫球",
"篮球",
"游泳",
"跑步",
"网球",
"举重",
"羽毛球",
"帆船"
]
sort_prompt = PromptTemplate(
    input_variables=["text_input"],
    template="将这段材料按照足球、乒乓球、高尔夫球、篮球、游泳、跑步、网球、举重、羽毛球、帆船进行区分。如果无法分类则划为“其他”:{text_input}。"
)
###llm###
endpoint_url = "https://u4378-b286-79485e25.westc.gpuhub.com:8443/v1/chat/completions"
llm = ChatGLM3(endpoint_url=endpoint_url,temperature = 0, timeout=999)
###chain###
sort_chain = LLMChain(llm=llm, prompt=sort_prompt)
full_chain = SimpleSequentialChain(
    chains=[sort_chain],
#verbose = True
)
###main work###
folder_path = './sports' # 替换为你要读取的文件夹路径
datas_embedding = get_datas_embedding(documents)
faiss_index = create_index(datas_embedding)
read_files = read_name_and_content_of_txt_files(folder_path)
for file_name, first_line,file_content in read_files:
print(f'文件名:{file_name}, 标题:{first_line}')
    Distance,sim_data_Index = data_recall(faiss_index,first_line, 1)
dis = Distance[0][0]
if dis > 1.33:
print("向量分类不明。")
        response = full_chain.run(first_line+file_content)
print("llm分类:",response)
else:
for index in sim_data_Index[0]:
print(documents[int(index)] + "   "+ str(Distance[0][0]))

a153c242-ff5a-4b9c-b6fc-7ca9bdff8a4e

小结:

1、今天的研究最重要的就是确定了 通过向量检索 来进行分类的主体;

2、向量检索还有很多写的东西,我需要再研究学习;

3、无法处理的情况,需要llm来进行分类,但是这块因为提示词的问题,导致比较拉跨。

现在想一想,关于具体问题的研究琢磨做得还是比较少,这块需要抓住问题,继续研究。

posted on 2024-04-14 16:49  jsxyhelu  阅读(33)  评论(0编辑  收藏  举报

导航