【內部項目預研】對信息分類進行探索

分析輸入信息的類別,目前是一個閉集、但是要按照開集的方式來進行分析;名稱越亂越好,讓系統自己來進行劃分。必須首先考慮傳統的方法;優先考慮數據結構的構建;強化監控機制的構建、首先進行認知和技術積累。

一、數據情況

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來進行分類,但是這塊因爲提示詞的問題,導致比較拉跨。

現在想一想,關於具體問題的研究琢磨做得還是比較少,這塊需要抓住問題,繼續研究。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章