赞
踩
本博客为山东大学软件学院2024创新项目实训,可视化课程知识问答系统(VCR)的个人博客。VCR即Visualization(可视化)、Chat(对话)、Retrieval&Recommend(检索和推荐),因此本博客将围绕以上功能点展开工作记录。
本项目主要需要实现知识图谱的构建和大模型对话功能,本周针对知识图谱构建进行数据集调研。
由于本项目是在课程知识库上建立的大模型项目,我们希望选择一些特定的软件工程相关专业科目进行知识库的建立。由于大语言模型具有强大的自然语言理解、自然语言推理、自然语言表示和文本生成能力,我们理所应当地选取了计算机网络、软件工程、面向对象等文本数据丰富的课程作为数据采样领域。
课程知识具有文本数据量大但描述性文字较多,章节与章节、知识点与知识点之间的关系错综复杂的特点。因此在从文本中获取知识时往往面临难以建立一个描述知识点间关系的框架以及难以定位某个知识点在大量文本中的位置的困难。
通过学习经验来看,知识点间往往存在大量的相互关联关系和包含关系,因此可以通过从关联关系网的角度入手建立知识框架,从包含关系树的角度来建立知识索引,加速知识的检索和定位,因此,我们需要获取大量具有以上两种特征的数据以进行模型的训练和微调。
使用课本原文的概念定义和大段描述、关键概念词表、目录结构可以提取出大量的(概念-描述)对作为实体数据作为命名实体识别的训练数据
使用课本中的附录索引可以获取大量高质量实体清单
百度百科是类似于wiki pedia的中文词条数据库,存储了大量的概念词条信息,针对每个词条都有详细的定义、描述、相关概念、分类等标签信息,可以作为一大数据来源
其中词条标题和第一段落的文本描述可以构成(实体-描述)对,目录栏列举了相关的词条实体并给出了一些实体间关系可以作为(实体-关系-实体)三元组用于训练实体关系提取模型。
由于词条还包含详细的描述文本,其中使用超链接标注和定位了相关实体,可以借此作为判断两个实体间关系强弱的依据,以及以特定词条为原点进行递归式的数据爬取。
在网络和校园资源库可以获取大量的课程知识笔记和复习资料,这些文档对课程知识进行了一定程度的整理和精简,已经具有部分知识结构。
通过观察可以发现笔记类资料整理的知识往往是(Q-A)和(实体-包含-实体)形式的,通俗的说就是问题+答案或某个问题/实体包含哪些实体和概念,通过这些结构化的数据我们可以提取出大量的问答数据和目录关系数据,可以在训练大语言模型问答和构建知识图谱中发挥作用。
本周的主要工作是研究如何将pdf文件进行文本提取和数据清洗
安装rapidocr_pdf库
pip install rapidocr_pdf
将计算机网络第五版pdf中的文字提取到result.json
- from rapidocr_pdf import PDFExtracter
- import json
-
- pdf_extracter = PDFExtracter(print_verbose=True, use_cuda=True)
-
- pdf_path = "computer_networks_5.pdf"
- texts = pdf_extracter(pdf_path, force_ocr=False)
- print(texts)
- with open('./result.json', 'w', encoding='utf-8') as f:
- json.dump(texts, f)
注意如果提取的是中文数据,在保存到json时需要设置ensure_ascii为False
- import json
-
- src_f = open('./result.json')
- out_f = open('./processed_result.json', 'w', encoding='utf-8')
-
- result = json.load(src_f)
- # print(json.dumps(result))
- json.dump(result, out_f, ensure_ascii=False)
原始的pdf格式
提取后的json数据
rapidocr_pdf会将提取的每页数据放到一个list中,其中每个元素都是一个list,形如["页码", "文本", "置信度"]
初步提取的文档数据中存在大量的换行符、空格、特殊字符、公式、页码、等影响数据集构建和模型训练的噪声数据,因此需要额外步骤进行数据清洗。
我采用的数据清洗手段有:去除特殊字符、分割段落和句子并去除长句、去除连续的数字字符、对特殊文本进行正则匹配、使用大模型进行数据过滤。
观察发现rapidocr_pdf会将页码进行识别提取,因此需要根据json的页码将对应页面中的页码数据去除。
- import json
-
- def remove_numbers_from_text(data_list):
- result = []
- for item in data_list:
- number = str(item[0])
- text = item[1]
- processed_text = text.replace(number, '')
- result.append(processed_text)
- return result
-
- # 从json文件中读取数据
- with open('data.json', 'r') as file:
- data = json.load(file)
-
- # 处理数据
- processed_data = remove_numbers_from_text(data)
-
- # 将处理后的文本拼接在一起
- combined_text = ' '.join(processed_data)
-
- # 输出到文件
- with open('output.txt', 'w') as file:
- file.write(combined_text)
-
首先读入json文件,将文本中的\n,\t,连续的空格等字符去除,
- import re
-
- def remove_special_chars(text):
- text = re.sub(r'\n', '', text)
- text = re.sub(r'\t', '', text)
- text = re.sub(r'\s+', ' ', text)
-
- return text
-
-
将“(比如......)”、“图 2-35”这样的无意义文本去除
- import re
-
- def remove_data(string):
- # 匹配形如"(比如...)"的数据
- pattern1 = r'\(.*?\)'
- # 匹配形如"图 2-35"的数据
- pattern2 = r'图 \d+-\d+'
-
- # 去除"(比如...)"的数据
- string = re.sub(pattern1, '', string)
- # 去除"图 2-35"的数据
- string = re.sub(pattern2, '', string)
-
- return string
-
- # 测试示例
- string = '这是一个示例(比如...), 这是另一个示例(比如...), 这是图 2-35的示例'
- result = remove_data(string)
- print(result)
-
我们部署了ChatGLM3-6B和Qwen-72B-Chat-Int4模型进行数据集的清洗工作
首先,确保系统中安装了必要的软件和驱动程序。然后,创建一个虚拟环境并激活它:
- sudo apt update
- sudo apt install -y python3-venv
-
- python3 -m venv chatglm3_env
- source chatglm3_env/bin/activate
安装必要的 Python 库:
- pip install torch torchvision torchaudio
- pip install transformers
- pip install accelerate
使用 transformers
库下载和加载模型:
- from transformers import AutoTokenizer, AutoModel
-
- tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b")
- model = AutoModel.from_pretrained("THUDM/chatglm-6b", torch_dtype=torch.float16).cuda()
运行简单的推理测试以确保模型正常工作:
- input_text = "你好,今天的天气怎么样?"
- input_ids = tokenizer.encode(input_text, return_tensors="pt").cuda()
-
- with torch.no_grad():
- outputs = model.generate(input_ids, max_length=50)
-
- response = tokenizer.decode(outputs[0], skip_special_tokens=True)
- print(response)
创建另一个虚拟环境并激活它:
- python3 -m venv qwen72_env
- source qwen72_env/bin/activate
安装必要的 Python 库:
- pip install torch torchvision torchaudio
- pip install transformers
- pip install accelerate
使用 transformers
库下载和加载模型:
- from transformers import AutoTokenizer, AutoModelForCausalLM
-
- tokenizer = AutoTokenizer.from_pretrained("qwen/Qwen-72B-Chat-Int4")
- model = AutoModelForCausalLM.from_pretrained("qwen/Qwen-72B-Chat-Int4", torch_dtype=torch.float16).cuda()
运行简单的推理测试以确保模型正常工作:
- input_text = "请介绍一下自己。"
- input_ids = tokenizer.encode(input_text, return_tensors="pt").cuda()
-
- with torch.no_grad():
- outputs = model.generate(input_ids, max_length=50)
-
- response = tokenizer.decode(outputs[0], skip_special_tokens=True)
- print(response)
- from transformers import AutoTokenizer, AutoModel, AutoModelForCausalLM
- from modelscope import AutoTokenizer, AutoModelForCausalLM
- import time
- import json
- from tqdm import tqdm
- import re
-
-
- def split_chinese_text(text):
- """
- 将中文文本按句子划分成列表
- :param text: 中文文本
- :return: 划分后的句子列表
- """
- # 去除原句中的换行符
- text = text.replace('\n', '')
-
- # 使用正则表达式划分句子,以句号、问号和感叹号作为句子分隔符
- sentences = re.split(r'([。?!])', text)
-
- # 去除空白句子
- sentences = [sentence.strip() for sentence in sentences if sentence.strip()]
-
- # 将分隔符与前一句合并
- merged_sentences = []
- for i in range(0, len(sentences), 2):
- if i + 1 < len(sentences):
- merged_sentences.append(sentences[i] + sentences[i+1])
- else:
- merged_sentences.append(sentences[i])
-
- return merged_sentences
-
-
- # 1.保留内容为描述、陈述事实、定义、解释的句子。
- # 2、去除大段公式、无意义符号、其他语义信息不明的文本。
- # 3、保留句子格式和标点。
- # 4、除处理后的文本不要输出任何其他内容。
- # 输入:比特填充还确保了转换的最小密度,这将有助于物理层保持同步。正是由于这个原因,USB(通用串行总线)采用了比特填充技术。当接收方看到5 个连续入境比特l ,并且后面紧跟一个比特0,它就自动剔除(即删除)比特0。比特填充和字节填充一样,对两台计算机上的网络层是完全透明的。如果用户数据中包含了标志模式。1111110,这个标志传输出去的是011111010,但在接收方内存中存储还是01111110 。图3-5 给出了一个比特填充的例子。0 I ro I I I I I I I I I I I I I I I I 0 0 I 0 (a) 飞---....... L ... ./’ 填充比特(b) 0110 I JI JI I I I JI I I JI I I 00 I 0 (c) 图3-5比特填充(a )原始数据g(b )出现在线路上的数据:(c )存储在接收方内存中的数据。有了比特填充技术,两帧之间的边界可以由标志模式明确区分。
- # 输出:比特填充还确保了转换的最小密度,这将有助于物理层保持同步。正是由于这个原因,USB(通用串行总线)采用了比特填充技术。当接收方看到5 个连续入境比特l ,并且后面紧跟一个比特0,它就自动剔除(即删除)比特0。比特填充和字节填充一样,对两台计算机上的网络层是完全透明的。如果用户数据中包含了标志模式。1111110,这个标志传输出去的是011111010,但在接收方内存中存储还是01111110 。有了比特填充技术,两帧之间的边界可以由标志模式明确区分。
-
- # 你的任务是将给定的文本做以下处理并输出处理后的句子:保留内容为描述、陈述事实、定义、解释的句子。去除大段公式、无意义符号、其他语义信息不明的文本。保留句子格式和标点。除了原始文本不要输出任何其他内容。
-
- def process_batch_sentence(model, tokenizer, batch_sentence, history):
- simple_prompt = f"""
- 将不严谨的语言和乱码去除:
- """
- task__example_prompt = f"""
- 你的任务是将输入文本中语义不明的文字和符号去除并输出,不要输出除原文外的任何其他内容:
- 以下是两个例子:
- 样例输入:比特填充还确保了转换的最小密度,这将有助于物理层保持同步。
- 样例输出:比特填充还确保了转换的最小密度,这将有助于物理层保持同步。
- 样例输入:图3-5 给出了一个比特填充的例子。0 I ro II I I I 0 (a) 飞---....... L ... ./’ 填充比特(b) 0110 I JI I 00 I 0 有了比特填充技术,两帧之间的边界可以由标志模式明确区分。
- 样例输出:有了比特填充技术,两帧之间的边界可以由标志模式明确区分。
- 你的输入:
- """
- dialog_prompt = f"""
- <|system|>
- 你的任务是将给定的文本做以下处理并输出处理后的句子:保留内容为描述、陈述事实、定义、解释的句子。去除大段公式、无意义符号、其他语义信息不明的文本。保留句子格式和标点。除处理后的文本不要输出任何其他内容。
- <|user|>
- 比特填充还确保了转换的最小密度,这将有助于物理层保持同步。正是由于这个原因,USB(通用串行总线)采用了比特填充技术。当接收方看到5 个连续入境比特l ,并且后面紧跟一个比特0,它就自动剔除(即删除)比特0。比特填充和字节填充一样,对两台计算机上的网络层是完全透明的。如果用户数据中包含了标志模式。
- <|assistant|>
- 比特填充还确保了转换的最小密度,这将有助于物理层保持同步。正是由于这个原因,USB(通用串行总线)采用了比特填充技术。当接收方看到5 个连续入境比特l ,并且后面紧跟一个比特0,它就自动剔除(即删除)比特0。比特填充和字节填充一样,对两台计算机上的网络层是完全透明的。如果用户数据中包含了标志模式。
- <|user|>
- 1111110,这个标志传输出去的是011111010,但在接收方内存中存储还是01111110 。图3-5 给出了一个比特填充的例子。0 I ro I I I I I I I I I I I I I I I I 0 0 I 0 (a) 飞---....... L ... ./’ 填充比特(b) 0110 I JI JI I I I JI I I JI I I 00 I 0 (c) 图3-5比特填充(a )原始数据g(b )出现在线路上的数据:(c )存储在接收方内存中的数据。有了比特填充技术,两帧之间的边界可以由标志模式明确区分。
- <|assistant|>
- 1111110,这个标志传输出去的是011111010,但在接收方内存中存储还是01111110 。有了比特填充技术,两帧之间的边界可以由标志模式明确区分。
- <|user|>
- """
- system_prompt = f"""
- 将接下来输入文本中的乱码删除,其余内容不变直接输出,不要输出任何多余信息
- """
- prompt_history = [
- ('另外还有一种可能,将NAT 盒子集成到路由器或者ADSL 调制解调器中。转换之后的数据包IP=l 98.60.42.12 端口号=3344客户路由,、器和LAN//、NAT盒子/防火墙『------·,,,,、\客户办公室边界图5-55NAT 盒子的放置和操作过程(逼向Internet)ISP路由器。至此,我们掩盖了一个微小但至关重要的细节z 当应答数据包返回时(比如从Web 服务器返回的应答包〉,本质上它的目标地址是198.60.42.12 ,那么,NAT 盒子如何知道该用哪一个地址来替代呢', '另外还有一种可能,将NAT 盒子集成到路由器或者ADSL 调制解调器中。至此,我们掩盖了一个微小但至关重要的细节z 当应答数据包返回时(比如从Web 服务器返回的应答包〉,本质上它的目标地址是198.60.42.12 ,那么,NAT 盒子如何知道该用哪一个地址来替代呢'),
- ('1111110,这个标志传输出去的是011111010,但在接收方内存中存储还是01111110 。图3-5 给出了一个比特填充的例子。0 I ro I I I I I I I I I I I I I I I I 0 0 I 0 (a) 飞---....... L ... ./’ 填充比特(b) 0110 I JI JI I I I JI I I JI I I 00 I 0 (c) 图3-5比特填充(a )原始数据g(b )出现在线路上的数据:(c )存储在接收方内存中的数据。有了比特填充技术,两帧之间的边界可以由标志模式明确区分。', '1111110,这个标志传输出去的是011111010,但在接收方内存中存储还是01111110 。有了比特填充技术,两帧之间的边界可以由标志模式明确区分。')
- ]
- print("input:", simple_prompt + batch_sentence)
- response, history = model.chat(tokenizer, simple_prompt + batch_sentence, history=[])
- print("output:\n", response)
-
- return response, history
-
- if __name__ == "__main__":
- #chat-glm3-6b
- # tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm3-6b", trust_remote_code=True)
- # model = AutoModel.from_pretrained("THUDM/chatglm3-6b", trust_remote_code=True, device='cuda')
- # model = model.eval()
-
- #Qwen-72B-Chat-Int4
- tokenizer = AutoTokenizer.from_pretrained("qwen/Qwen-72B-Chat-Int4", revision='master', trust_remote_code=True)
-
- model = AutoModelForCausalLM.from_pretrained(
- "qwen/Qwen-72B-Chat-Int4", revision='master',
- device_map="auto",
- trust_remote_code=True
- ).eval()
-
- # src_f = open('./data/sample.json', 'r')
- src_f = open('./data/computer_network_5_origin.json', 'r')
- out_f = open('./data/computer_network_5_filted.json', 'w')
-
- # read pages content from file
- pages = json.load(src_f)
- full_content = ''
- for page in tqdm(pages):
- full_content += page[1]
-
- # split sentences and filted
- sentences = split_chinese_text(full_content)
- batch_sentence = ''
- history = []
- filted_sentence = []
- max_len = 512
- for sentence in tqdm(sentences):
- if len(sentence) > max_len:
- continue
- if len(batch_sentence) + len(sentence) > max_len:
- response, history = process_batch_sentence(model, tokenizer, batch_sentence, history)
- filted_sentence.extend(split_chinese_text(response))
- batch_sentence = sentence
- else:
- batch_sentence += sentence
- if batch_sentence != '':
- response, history = process_batch_sentence(model, tokenizer, batch_sentence, history)
- filted_sentence.extend(split_chinese_text(response))
-
- out_f.write(json.dumps(filted_sentence, ensure_ascii=False))
在使用ChatGLM3的时候出现了文档清洗不完全、有多余输出信息、修改了原文内容等问题,
因此我们换用了Qwen72B模型进行清洗,部署Qwen72B模型需要过多的显存,我们为了在本地部署,选用了INT4参数的版本大概需要50GB显存,在三张3090显卡上运行。
最终的清洗效果如下:
我们对pdf的文本进行了提取并使用多种手段进行数据清洗,最终得到了富含语义信息和概念知识的纯文本,并以句子为单位分割,去除了过长的句子以防止对后续的模型训练产生影响。最终我们通过统计文本中的特殊字符和连续数字、字符数量以评判清洗效果,达到了令人满意的程度。
本周我对之前分析的百度百科词条编写了爬虫进行数据爬取,获得了大量的词条和描述文档,并且同时获得了一些词条间的关系。
https://baike.baidu.com/item/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/18763
百度百科是类似于wiki pedia的百科网站,数据库里存储了大量的概念词条和相关描述,同时在每个概念的描述文档中还使用超链接连接到相关的概念。
该网站不需要登录即可游客访问,使用浏览器可以在未登录的情况下快速多次访问不受限制。
本次爬虫使用Python作为编程语言,使用了requests模块发送http请求,使用time进行随机等待请求时间间隔防止被反爬机制识别,使用re模块对爬取的html格式数据进行正则匹配提取有用信息,使用json模块进行数据的格式化和写入存储
另外本次爬虫还使用了企业提供的IP池,支持爬虫程序进行多线程并发的快速请求,提高爬取效率,支持大量数据爬取。
首先设计爬虫机制,对指定的词条进行爬取,通过文档中的超链接和相关词条标题加入待爬取队列,进行递归爬取,对规模较大的数据集爬取3层,对规模较小的数据集爬取4层。
- if __name__ == '__main__':
- que = []
- vis = set()
- que.append(('面向对象', 2262089, 1))
- vis.add('面向对象')
- # que.append(('网络环境', 4422188, 1))
- # vis.add('网络环境')
- cur = 0
- docs = []
- mention_list = []
- doc_f = open('./docs.json', 'w', encoding='utf-8')
- mention_f = open('./mentions.json', 'w', encoding='utf-8')
- while True:
- if cur >= len(que):
- break
- print(f"cur/sum: {cur}/{len(que)}")
- entity_name, entity_id, depth = que[cur]
- cur += 1
- print("request:", 'https://baike.baidu.com/item/' + entity_name + '/' + str(entity_id))
- url = 'https://baike.baidu.com/item/' + urllib.parse.quote(entity_name) + '/' + str(entity_id)
- text = query(url)
- doc = {
- 'title': entity_name,
- 'id': entity_id,
- 'content': ''
- }
- pattern = r"<span class=\".*?\" data-text=\"true\">(.*?)</span>"
- matches = re.findall(pattern, text)
- for match in matches:
- # print("match:", match)
- pattern = r'href="/item/([^"]+)/(\d+).*?>(.*?)</a>'
- sentence = re.search(pattern, match)
- if sentence:
- match_entity_name, match_entity_id, match_mention = re.findall(pattern, match)[0]
- match_entity_id = int(match_entity_id)
- # print("a:", match_mention)
- if depth < 4 and match_entity_name not in vis:
- que.append((match_entity_name, match_entity_id, depth + 1))
- vis.add(match_entity_name)
- start_pos = len(doc['content'])
- doc['content'] += match_mention
- end_pos = len(doc['content'])
- mention = {
- 'doc_id': entity_id,
- 'entity_id': match_entity_id,
- 'entity_name': match_entity_name,
- 'mention': match_mention,
- 'start_pos': start_pos,
- 'end_pos': end_pos
- }
- mention_list.append(mention)
- mention_f.write(json.dumps(mention, ensure_ascii=False) + '\n')
- else:
- # print("span:", match)
- doc['content'] += match
- docs.append(doc)
- doc_f.write(json.dumps(doc, ensure_ascii=False) + '\n')
- # print("doc:", doc)
- time.sleep(1)
- # break
首先直接使用requests进行http请求,请求间隔为1秒,发现爬取约30条网页信息后被百度反爬机制侦测并爬取到人机验证界面。
尝试设置时间间隔为随机20-30秒,重新爬取,发现在爬取约半小时后仍然被人机验证。虽然爬取时间变长,但因为爬取间隔边长,总的爬取数量仍没有什么提升。
发现浏览器请求可以不受限制,尝试携带agent请求头模拟chrome浏览器请求,并携带登录用户的cookie。
- headers = {
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
- # 'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'
- # 'Accept-Encoding': 'gzip, deflate, br, zstd',
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
- # 'Cookie': 'zhishiTopicRequestTime=1714204958834; BIDUPSID=990AEC8E63B79ECEB2DCA3A7BEEA347E; PSTM=1688897620; BAIDUID=990AEC8E63B79ECE513E0A76649F94E7:FG=1; BDUSS=XY4cU5nUXU0NEZSRXFxRGM5UFdwUnBBRFQ5MlZUVFdWclM5Y2N5eHAtflhYTkprRVFBQUFBJCQAAAAAAAAAAAEAAAB~ktsyMjQ3MTExYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANfPqmTXz6pkS; BDUSS_BFESS=XY4cU5nUXU0NEZSRXFxRGM5UFdwUnBBRFQ5MlZUVFdWclM5Y2N5eHAtflhYTkprRVFBQUFBJCQAAAAAAAAAAAEAAAB~ktsyMjQ3MTExYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANfPqmTXz6pkS; Hm_lvt_55b574651fcae74b0a9f1cf9c8d7c93a=1700572499,1701322775; MCITY=-60949%3A288%3A; H_WISE_SIDS_BFESS=40416_40499_40446_40080_60129_60138; baikeVisitId=2efca0c8-7c1c-4797-8bb5-d03d9db4ab56; BAIDUID_BFESS=990AEC8E63B79ECE513E0A76649F94E7:FG=1; ZFY=hJ65HUmPh7OYnHjNKX2IUJefkgbBJUR9vLt4jjZ0hio:C; H_PS_PSSID=40499_40446_40080_60138; H_WISE_SIDS=40499_40446_40080_60138; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BA_HECTOR=ag8k002g048l8420a0a0a180prrdan1j2pb881t; BDRCVFR[feWj1Vr5u3D]=I67x6TjHwwYf0; PSINO=1; delPer=0; channel=baidusearch; ab_sr=1.0.1_MjE4YTM5MzJkYmE0Njg1M2IxYmIwYTIyMWMzYjVmMjUwMThkZWE3NDExNzZjMjk0ODk1YWU4OGRhMjViNjRkYWYwNWYwZDUyMzhhMmY3ZjMzMGU1YWFlNjdmMjdhZTRjMjUyOTA5NDNhMzU3YjJkNDNkNTRlMTA1NjcyMTViN2MzYTQxOTE4OTQyOWZkNjMyZjU4OWE0N2M4MDJlYjkyYjU5Yjk5ZjBiYWQxMjkzNDNhZTJmYTVhMDA3YmU5YjFh; RT="z=1&dm=baidu.com&si=d50562e6-c279-4806-8422-2414ca533856&ss=lvhtdf0a&sl=9&tt=1yf&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=gyo&ul=hla"'
- 'Connection': 'close'
- }
在爬取半天,约爬取1300条网页后仍然被人机验证,约半小时到一小时内可以恢复访问,中间的爬取会被阻塞,效率太低。
尝试配置IP池,使用IP代理的方式每次请求使用不同的IP,防止被反爬机制识别到同一IP的操作。
使用IP池的结果可以正确避免被反爬机制识别,但存在部分IP连接超时,因此需要对每次代理访问进行多次尝试。
使用IP池后,由于每次访问使用不同IP,即访问请求可以并发且时间间隔降低,因此时间间隔设置为随机1-5秒
我们对计算机网络、面向对象、软件工程、计算机组织与结构进行了相关词条数据爬取,总共获得了约1.5GB的文本数据,包括词条描述和词条间关系,大概占比1:1
我们获得了富含大量语义信息和知识的文本数据,现在问题是如何通过这些数据构建知识图谱和训练大模型让其在相关领域具有更好的理解、表示、推理和生成能力。
特点和优势:
训练方法:
部署方法:
transformers
库加载预训练模型并微调。torch
或 ONNX
等进行推理。注意事项:
特点和优势:
训练方法:
部署方法:
transformers
库进行部署和调用。注意事项:
特点和优势:
训练方法:
部署方法:
transformers
库进行加载和推理。ONNX
或 TorchScript
优化推理性能。注意事项:
特点和优势:
训练方法:
部署方法:
注意事项:
transformers
等库加载预训练模型。PaddleNLP 的 UIE(Universal Information Extraction)模型是一个强大的信息抽取模型,基于PaddlePaddle深度学习框架开发,设计用于处理多种信息抽取任务,包括命名实体识别(NER)、关系抽取(RE)、事件抽取等。UIE模型的核心特点和功能包括:
优势:
劣势:
1. 环境准备
首先,安装PaddlePaddle和PaddleNLP库:
- pip install paddlepaddle-gpu
- pip install paddlenlp
2. 下载和准备数据
3. 数据预处理
使用PaddleNLP的工具进行数据预处理:
- from paddlenlp.datasets import load_dataset
-
- def read(data_path):
- with open(data_path, 'r', encoding='utf-8') as f:
- for line in f:
- data = json.loads(line)
- yield {'text': data['text'], 'entities': data['entities'], 'relations': data['relations']}
-
- dataset = load_dataset(read, data_path='path/to/your/data')
4. 模型训练
使用PaddleNLP的UIE模型进行训练:
- from paddlenlp.transformers import UIEModel, UIETokenizer
- from paddlenlp.data import DataCollatorForTokenClassification
- from paddlenlp.trainer import Trainer, TrainingArguments
-
- model = UIEModel.from_pretrained('uie-base')
- tokenizer = UIETokenizer.from_pretrained('uie-base')
-
- def tokenize_and_align_labels(examples):
- tokenized_inputs = tokenizer(examples['text'], truncation=True, padding='max_length', max_length=128)
- labels = []
- for i, label in enumerate(examples['entities']):
- label_ids = [0] * len(tokenized_inputs['input_ids'][i])
- for start, end, entity in label:
- label_ids[start:end+1] = [1] * (end - start + 1)
- labels.append(label_ids)
- tokenized_inputs['labels'] = labels
- return tokenized_inputs
-
- tokenized_datasets = dataset.map(tokenize_and_align_labels, batched=True)
-
- training_args = TrainingArguments(
- output_dir='./results',
- evaluation_strategy='epoch',
- learning_rate=2e-5,
- per_device_train_batch_size=16,
- per_device_eval_batch_size=16,
- num_train_epochs=3,
- weight_decay=0.01,
- )
-
- trainer = Trainer(
- model=model,
- args=training_args,
- train_dataset=tokenized_datasets['train'],
- eval_dataset=tokenized_datasets['eval'],
- tokenizer=tokenizer,
- data_collator=DataCollatorForTokenClassification(tokenizer)
- )
-
- trainer.train()
5. 模型评估
使用验证集评估模型性能:
- metrics = trainer.evaluate()
- print(metrics)
6. 模型部署
将训练好的模型导出并部署为API服务:
- from paddlehub.serving import application
- import paddlehub as hub
-
- model.save_pretrained('uie_model')
-
- app = application.Application()
- app.load('uie_model')
-
- @app.route('/predict', methods=['POST'])
- def predict():
- data = request.json
- inputs = tokenizer(data['text'], return_tensors='pd')
- outputs = model(**inputs)
- # 处理输出并返回结果
- return jsonify(outputs)
-
- if __name__ == '__main__':
- app.run()
paddle社区提供了十分完整易用的同意信息抽取模型UIE,并且经过了优秀的预训练过程,在许多领域上都取得了良好的信息提取效果,我们选用这个模型可以大大降低模型训练的风险。而且该模型配有比较完善的训练和测试代码,可以将主要工作放在数据集的精炼方面,已取得更好的模型训练效果。
该模型基于bert,部署所需的资源相对较少,运行速度快,十分适用于我们大数据量信息提取的问题背景,下周我的工作将针对于该模型的训练数据构建和模型训练与评估展开。
UIE使用daccano进行数据标注,并提供了将daccano数据转化为训练数据格式的代码。
我们根据爬取的大量文档文本和其中的超链接实体关系,对原文本中的已标注实体转化成daccano标注的实体的格式
- import json
- import re
-
- def split_doc(doc):
- single_sentences = re.split(r"(?<=[。!?])", docs[doc_title])
- sentences = []
- merged_sentence = ''
- for sentence in single_sentences:
- if len(merged_sentence) + len(sentence) < 512:
- merged_sentence += sentence
- else:
- sentences.append(merged_sentence)
- merged_sentence = sentence
- if merged_sentence != '':
- sentences.append(merged_sentence)
- return sentences
-
-
- if __name__ == '__main__':
- domain_name = 'computer_network'
-
- doc_f = open('/home/yunpu/Data/codes/VCRS/data/wiki_data/' + domain_name + '_docs.json' ,'r')
- mention_f = open('/home/yunpu/Data/codes/VCRS/data/wiki_data/' + domain_name + '_mentions_with_title.json', 'r')
- out_f = open('/home/yunpu/Data/codes/VCRS/UIE/data/' + domain_name + '_daccano.jsonl' ,'w')
-
- # entitys = set()
- # docs = {}
-
- # for line in doc_f:
- # doc = json.loads(line)
- # assert doc['title'] not in entitys
- # entitys.add(doc['title'])
- # docs[doc['title']] = doc['content']
-
- doc_titles = set()
- docs = {}
- entity_id = {}
- cnt = 0
- mentions = {}
- for line in doc_f:
- doc = json.loads(line)
- assert doc['title'] not in doc_titles
- doc_titles.add(doc['title'])
- docs[doc['title']] = doc['content']
- entity_id[doc['title']] = cnt
- cnt += 1
-
- for line in mention_f:
- mention = json.loads(line)
- if mention['doc_title'] not in mentions.keys():
- mentions[mention['doc_title']] = [mention]
- else:
- mentions[mention['doc_title']].append(mention)
-
- sample_cnt = 0
- for doc_title in docs.keys():
- if doc_title not in mentions.keys():
- continue
- # sentences = re.split(r"(?<=[。!?])", docs[doc_title])
- sentences = split_doc(docs[doc_title])
- if sample_cnt == 0:
- print(sentences)
- prelen = 0
- cur = 0
- for sentence in sentences:
- if len(sentence) < 512:
- sample = {}
- sample['id'] = sample_cnt
- sample_cnt += 1
- sample['text'] = sentence
- sample['relations'] = []
- sample['entities'] = []
- while cur < len(mentions[doc_title]) and mentions[doc_title][cur]['start_pos'] >= prelen and mentions[doc_title][cur]['end_pos'] <= prelen + len(sentence):
- if mentions[doc_title][cur]['entity_name'] not in entity_id.keys():
- entity_id[mentions[doc_title][cur]['entity_name']] = cnt
- cnt += 1
- entity = {
- "id": entity_id[mentions[doc_title][cur]['entity_name']],
- "start_offset": mentions[doc_title][cur]['start_pos'] - prelen,
- "end_offset": mentions[doc_title][cur]['end_pos'] - prelen,
- "label": "实体"
- }
- sample['entities'].append(entity)
- cur += 1
- if len(sample['entities']) == 0:
- sample_cnt -= 1
- else:
- out_f.write(json.dumps(sample, ensure_ascii=False) + '\n')
- prelen += len(sentence)
- # if sample_cnt > 50:
- # break
代码标注后的daccano数据
{"id": 0, "text": "计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路和通信设备连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。计算机网络主要是由一些通用的、可编程的硬件互连而成的。这些可编程的硬件能够用来传送多种不同类型的数据,并能支持广泛的和日益增长的应用。计算机网络Computer network计算机网络系统互联网信息的传输与共享网络操作系统计算机网络也称计算机通信网。关于计算机网络的最简单定义是:一些相互连接的、以共享资源为目的的、自治的计算机的集合。若按此定义,则早期的面向终端的网络都不能算是计算机网络,而只能称为联机系统(因为那时的许多终端不能算是自治的计算机)。但随着硬件价格的下降,许多终端都具有一定的智能,因而“终端”和“自治的计算机”逐渐失去了严格的界限。若用微型计算机作为终端使用,按上述定义,则早期的那种面向终端的网络也可称为计算机网络。另外,从逻辑功能上看,计算机网络是以传输信息为基础目的,用通信线路将多个计算机连接起来的计算机系统的集合,一个计算机网络组成包括传输介质和通信设备。", "relations": [], "entities": [{"id": 1, "start_offset": 8, "end_offset": 12, "label": "实体"}, {"id": 2, "start_offset": 29, "end_offset": 33, "label": "实体"}, {"id": 3, "start_offset": 36, "end_offset": 40, "label": "实体"}, {"id": 4, "start_offset": 51, "end_offset": 57, "label": "实体"}, {"id": 5, "start_offset": 58, "end_offset": 64, "label": "实体"}, {"id": 6, "start_offset": 65, "end_offset": 71, "label": "实体"}, {"id": 7, "start_offset": 81, "end_offset": 85, "label": "实体"}, {"id": 8, "start_offset": 86, "end_offset": 90, "label": "实体"}, {"id": 9, "start_offset": 91, "end_offset": 96, "label": "实体"}, {"id": 10, "start_offset": 216, "end_offset": 222, "label": "实体"}, {"id": 11, "start_offset": 247, "end_offset": 251, "label": "实体"}, {"id": 12, "start_offset": 299, "end_offset": 303, "label": "实体"}, {"id": 13, "start_offset": 377, "end_offset": 382, "label": "实体"}, {"id": 3, "start_offset": 447, "end_offset": 451, "label": "实体"}, {"id": 14, "start_offset": 482, "end_offset": 486, "label": "实体"}, {"id": 15, "start_offset": 487, "end_offset": 491, "label": "实体"}]}
因为百度词条中仅对一部分实体进行了标注,重复实体被忽略,因此我们首先使用代码对相同实体进行了补充标注,并人工对一些代码忽略的实体进行了标注
Doccano 是一个用于文本标注的开源工具,支持多种语言任务如命名实体识别、情感分析和文本分类。以下是详细的本地部署步骤。
- sudo apt-get update
- sudo apt-get install -y \
- ca-certificates \
- curl \
- gnupg \
- lsb-release
-
- sudo mkdir -p /etc/apt/keyrings
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
-
- echo \
- "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
- $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
-
- sudo apt-get update
- sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
启动 Docker 服务:
- sudo systemctl start docker
- sudo systemctl enable docker
使用 Docker 部署 Doccano 非常简单。首先,拉取 Doccano 的 Docker 镜像:
docker pull doccano/doccano
然后,运行 Doccano 容器:
docker run -d --name doccano -p 8000:8000 doccano/doccano
这会启动 Doccano,并在本地的 8000 端口上提供服务。
在浏览器中访问 http://localhost:8000
,你会看到 Doccano 的登录页面。首次运行时需要创建一个超级用户来管理项目和用户。
先导入刚刚代码标注的json文件
可以看到我们刚刚使用代码进行的自动标注已经导入了daccano的系统之中,我们在这个基础上对遗落的实体进行了标注
最终我们取得了约50条500字长度的标注数据对UIE模型进行微调
使用paddle提供的数据格式转换代码
https://github.com/PaddlePaddle/PaddleNLP/tree/develop/model_zoo/uie
- python doccano.py \
- --doccano_file ./data/doccano_ext.json \
- --task_type ext \
- --save_dir ./data \
- --splits 0.8 0.2 0 \
- --schema_lang ch
转换后的数据格式
{"id":0,"text":"计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路和通信设备连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。计算机网络主要是由一些通用的、可编程的硬件互连而成的。这些可编程的硬件能够用来传送多种不同类型的数据,并能支持广泛的和日益增长的应用。计算机网络Computer network计算机网络系统互联网信息的传输与共享网络操作系统计算机网络也称计算机通信网。关于计算机网络的最简单定义是:一些相互连接的、以共享资源为目的的、自治的计算机的集合。若按此定义,则早期的面向终端的网络都不能算是计算机网络,而只能称为联机系统(因为那时的许多终端不能算是自治的计算机)。但随着硬件价格的下降,许多终端都具有一定的智能,因而“终端”和“自治的计算机”逐渐失去了严格的界限。若用微型计算机作为终端使用,按上述定义,则早期的那种面向终端的网络也可称为计算机网络。另外,从逻辑功能上看,计算机网络是以传输信息为基础目的,用通信线路将多个计算机连接起来的计算机系统的集合,一个计算机网络组成包括传输介质和通信设备。","entities":[{"id":350447,"label":"实体","start_offset":8,"end_offset":12},{"id":350448,"label":"实体","start_offset":29,"end_offset":33},{"id":350449,"label":"实体","start_offset":36,"end_offset":40},{"id":350450,"label":"实体","start_offset":51,"end_offset":57},{"id":350451,"label":"实体","start_offset":58,"end_offset":64},{"id":350452,"label":"实体","start_offset":65,"end_offset":71},{"id":350453,"label":"实体","start_offset":81,"end_offset":85},{"id":350454,"label":"实体","start_offset":86,"end_offset":90},{"id":350455,"label":"实体","start_offset":91,"end_offset":96},{"id":350456,"label":"实体","start_offset":216,"end_offset":222},{"id":350457,"label":"实体","start_offset":247,"end_offset":251},{"id":350458,"label":"实体","start_offset":299,"end_offset":303},{"id":350459,"label":"实体","start_offset":377,"end_offset":382},{"id":350460,"label":"实体","start_offset":447,"end_offset":451},{"id":350461,"label":"实体","start_offset":482,"end_offset":486},{"id":350462,"label":"实体","start_offset":487,"end_offset":491},{"id":350885,"label":"实体","start_offset":0,"end_offset":5},{"id":350886,"label":"实体","start_offset":41,"end_offset":45},{"id":350887,"label":"实体","start_offset":24,"end_offset":27},{"id":350888,"label":"实体","start_offset":209,"end_offset":214},{"id":350889,"label":"实体","start_offset":473,"end_offset":478},{"id":350890,"label":"实体","start_offset":454,"end_offset":457}],"relations":[{"id":222,"from_id":350885,"to_id":350453,"type":"功能"},{"id":223,"from_id":350885,"to_id":350454,"type":"功能"},{"id":224,"from_id":350885,"to_id":350455,"type":"定义"},{"id":225,"from_id":350888,"to_id":350456,"type":"别名"},{"id":226,"from_id":350889,"to_id":350461,"type":"包含"},{"id":227,"from_id":350889,"to_id":350462,"type":"包含"},{"id":228,"from_id":350460,"to_id":350890,"type":"连接"},{"id":229,"from_id":350449,"to_id":350887,"type":"连接"},{"id":230,"from_id":350449,"to_id":350448,"type":"连接"},{"id":231,"from_id":350886,"to_id":350887,"type":"连接"},{"id":232,"from_id":350886,"to_id":350448,"type":"连接"},{"id":233,"from_id":350885,"to_id":350450,"type":"包含"},{"id":234,"from_id":350885,"to_id":350451,"type":"包含"},{"id":235,"from_id":350885,"to_id":350452,"type":"包含"}],"Comments":[]}
继续使用paddle提供的微调代码,设置微调参数
- export finetuned_model=./checkpoint/model_best
-
- python finetune.py \
- --device gpu \
- --logging_steps 10 \
- --save_steps 100 \
- --eval_steps 100 \
- --seed 42 \
- --model_name_or_path uie-base \
- --output_dir $finetuned_model \
- --train_path data/train.txt \
- --dev_path data/dev.txt \
- --max_seq_length 512 \
- --per_device_eval_batch_size 16 \
- --per_device_train_batch_size 16 \
- --num_train_epochs 20 \
- --learning_rate 1e-5 \
- --label_names "start_positions" "end_positions" \
- --do_train \
- --do_eval \
- --do_export \
- --export_model_dir $finetuned_model \
- --overwrite_output_dir \
- --disable_tqdm True \
- --metric_for_best_model eval_f1 \
- --load_best_model_at_end True \
- --save_total_limit 1
- python evaluate.py \
- --model_path ./checkpoint/model_best \
- --test_path ./data/dev.txt \
- --batch_size 16 \
- --max_seq_len 512 \
- --multilingual
模型在实体识别任务上取得了Evaluation Precision: 0.61081 | Recall: 0.58020 | F1: 0.59511的成绩,我认为已经满足我们的实体识别标注需求。
本周劳动节假期
本周的工作旨在利用上周训练的 PaddleNLP 的 UIE 模型对现有文档数据进行实体识别和关系提取,从而构建知识图谱。提取出来的数据将分别存储到 JSON 文件和 Neo4j 数据库中。
确保安装以下软件和库Neo4j
pip install neo4j
下载并安装 Neo4j 社区版:https://neo4j.com/download/
启动 Neo4j 数据库,并设置用户名和密码(默认用户名为 neo4j
,密码可以自定义)。
将我们的爬取的所有数据按照上周训练模型的方式进行格式转换(仅跳过人工标注阶段)
使用 PaddleNLP 的 UIE 模型进行实体识别和关系提取。
- def save_to_json(data, filename='extracted_data.json'):
- with open(filename, 'w', encoding='utf-8') as file:
- json.dump(data, file, ensure_ascii=False, indent=4)
save_to_json(extracted_data)
- from neo4j import GraphDatabase
-
- uri = "bolt://localhost:7687"
- username = "neo4j"
- password = "your_password"
-
- driver = GraphDatabase.driver(uri, auth=(username, password))
- def save_to_neo4j(data):
- with driver.session() as session:
- for item in data:
- entities = item['entities']
- relations = item['relations']
- for entity in entities:
- session.run(
- "MERGE (e:Entity {name: $name, type: $type})",
- name=entity['name'],
- type=entity['type']
- )
- for relation in relations:
- session.run(
- """
- MATCH (e1:Entity {name: $start_name}), (e2:Entity {name: $end_name})
- MERGE (e1)-[r:RELATION {type: $type}]->(e2)
- """,
- start_name=relation['start_name'],
- end_name=relation['end_name'],
- type=relation['type']
- )
我们还直接使用大模型+任务提示的方式进行了实体和关系提取,并对比效果
- import modelscope
- import transformers
-
-
- prompt = """
- 你的任务是提取以下文本中所有的实体和他们之间的关系并按{"entity1": '', "relation": '', "entity2": ''}的格式输出。
- """
- class GlmModel:
- def __init__(self) -> None:
- self.tokenizer = transformers.AutoTokenizer.from_pretrained("THUDM/chatglm3-6b", trust_remote_code=True)
- self.model = transformers.AutoModel.from_pretrained("THUDM/chatglm3-6b", trust_remote_code=True, device='cuda')
- self.model = self.model.eval()
-
- def text2re(self, text: str):
- message = prompt + text
- print("input:", message)
- response, history = self.model.chat(self.tokenizer, message, history=None)
- print("output:", response)
- return response
-
- class QwenModel:
- def __init__(self) -> None:
- self.tokenizer = modelscope.AutoTokenizer.from_pretrained("qwen/Qwen-72B-Chat-Int4", revision='master', trust_remote_code=True)
-
- self.model = modelscope.AutoModelForCausalLM.from_pretrained(
- "qwen/Qwen-72B-Chat-Int4", revision='master',
- device_map="auto",
- trust_remote_code=True
- ).eval()
-
- def text2re(self, text: str):
- message = prompt + text
- print("input:", message)
- response, history = self.model.chat(self.tokenizer, message, history=None)
- print("output:", response)
- return response
-
-
- if __name__ == '__main__':
- re_model = GlmModel()
-
- f = open('./data/sample_text.txt', 'r')
- doc = f.readline()
-
- re = re_model.text2re(doc)
结合前端的需求,我需要将爬取的文档和关系数据进行一定的格式转化以便于可视化展示。
该工作主要功能是处理一个包含文档标题和实体名称的 JSON 文件,生成一个包含节点(文档标题)和边(文档标题与实体名称关系)的知识图谱(KG)。生成的知识图谱按特定类别(如物理层、数据链路层等)进行分类,并将结果输出到一个 JSON 文件中。
代码执行以下主要任务:
输入数据来自一个名为 computer_network_mentions_with_title.json
的文件,其中每行是一个 JSON 对象,包含以下字段:
doc_title
: 文档标题entity_name
: 实体名称输出是一个 JSON 对象,包含以下结构:
categories
: 节点类别列表nodes
: 节点列表,每个节点包含名称、值和类别links
: 边列表,每个边包含源节点和目标节点- f = open('./computer_network_mentions_with_title.json', 'r', encoding='utf-8')
- last_title = ''
- for line in f:
- item = json.loads(line)
- if item['doc_title'] not in nodes_id.keys():
- nodes_id[item['doc_title']] = len(nodes)
- nodes.append({
- "name": item['doc_title'],
- "value": 1,
- "category": 7
- })
- graph[item['doc_title']] = []
- edges.append((item['doc_title'], item['entity_name']))
- graph[item['doc_title']].append(item['entity_name'])
- if len(nodes) == 1000:
- break
nodes_id
中,如不存在则添加到 nodes
和 nodes_id
中,并初始化 graph
字典。edges
列表和 graph
字典中。- for id, categorie in enumerate(tqdm(categories)):
- now_title = categorie['name']
- max_dis = 2
- que = [(now_title, 0)]
- vis = set()
- now = 0
- while now < len(que):
- now_title, now_dis = que[now]
- now += 1
- if now_title in nodes_id.keys():
- nodes[nodes_id[now_title]]['category'] = id
- if now_dis < max_dis:
- if now_title in graph.keys():
- for new_title in graph[now_title]:
- if new_title not in vis:
- que.append([new_title, now_dis + 1])
- vis.add(new_title)
- if now_title in nodes_id.keys() and new_title in nodes_id.keys():
- links.append({
- "source": nodes_id[now_title],
- "target": nodes_id[new_title]
- })
max_dis
参数限制了搜索深度,确保仅搜索与类别名称相关的节点及其直接连接的节点。- kg = {
- "categories": categories,
- "nodes": nodes,
- "links": links
- }
-
- out_f = open("./kg_by_category.json", 'w', encoding='utf-8')
- out_f.write(json.dumps(kg, ensure_ascii=False))
categories
、nodes
和 links
。kg_by_category.json
文件中。实现效果
读取一个包含文档标题和实体名称的 JSON 文件,将其转换成树形结构的数据,并将每个文档标题及其子树结构存储到单独的 JSON 文件中。输出的 JSON 文件适用于可视化工具,如 d3.js,用于展示树形结构图。
order
和一个字典 sons
,记录每个文档标题及其关联的实体名称。输入数据来自一个名为 computer_network_mentions_with_title.json
的文件,其中每行是一个 JSON 对象,包含 doc_title
和 entity_name
。
输出是树形结构的 JSON 对象,每个对象包含 name
、size
、children
和 value
属性。输出文件以 id.json
命名存储在 tree_data
目录中。
order
和 sons
中。- id = 0
- for fa in order:
- item = {
- "name": '',
- "size": 0,
- "children": [],
- "value": 0
- }
- fasize = 0
- for s1 in sons[fa]:
- if len(item['children']) == 10:
- break
- if s1 in sons.keys():
- s1item = {
- "name": '',
- "size": 0,
- "children": [],
- "value": 0
- }
- s1size = 0
- for s2 in sons[s1]:
- if len(s1item['children']) == 10:
- break
- if s2 in sons.keys():
- s2item = {}
- s2size = random.randrange(1, 100)
- s1size += s2size
- s2item['name'] = s2
- s2item['size'] = s2size
- s2item['value'] = s2size
- s1item['children'].append(s2item)
- s1item['name'] = s1
- s1item['size'] = s1size
- s1item['value'] = s1size
- fasize += s1size
- item['children'].append(s1item)
- item['name'] = fa
- item['size'] = fasize
- item['value'] = fasize
- out_f = open("./tree_data/" + str(id) + ".json", "w", encoding='utf-8')
- out_f.write(json.dumps(item, ensure_ascii=False))
- id += 1
- print(id)
- if id == 2000:
- break
id
计数器。fa
,创建一个包含 name
、size
、children
和 value
的空字典 item
。s1
,如果它也是一个文档标题,则创建一个类似的字典 s1item
。s1
关联的实体名称 s2
,生成一个随机大小 s2size
,并将其添加到 s1item
的 children
中。s1item
的 size
和 value
,并将其添加到 item
的 children
中。item
的 size
和 value
,并将其写入到一个以 id
命名的 JSON 文件中。{"name": "计算机网络", "size": 5290, "children": [{"name": "地理位置", "size": 604, "children": [{"name": "地理事物", "size": 67, "value": 67}, {"name": "空间关系", "size": 58, "value": 58}, {"name": "确定", "size": 46, "value": 46}, {"name": "经纬度", "size": 83, "value": 83}, {"name": "经济地理位置", "size": 65, "value": 65}, {"name": "地理事物", "size": 99, "value": 99}, {"name": "空间关系", "size": 43, "value": 43}, {"name": "测绘科学与技术", "size": 81, "value": 81}, {"name": "地理事物", "size": 43, "value": 43}, {"name": "定性", "size": 19, "value": 19}], "value": 604}, {"name": "外部设备", "size": 543, "children": [{"name": "外设", "size": 9, "value": 9}, {"name": "输出设备", "size": 76, "value": 76}, {"name": "外存储器", "size": 78, "value": 78}, {"name": "作用", "size": 73, "value": 73}, {"name": "外围设备", "size": 72, "value": 72}, {"name": "主机", "size": 1, "value": 1}, {"name": "设备", "size": 49, "value": 49}, {"name": "系统", "size": 20, "value": 20}, {"name": "外设", "size": 99, "value": 99}, {"name": "计算机硬件", "size": 66, "value": 66}], "value": 543}, {"name": "通信线路", "size": 547, "children": [{"name": "有线通信", "size": 4, "value": 4}, {"name": "传输媒介", "size": 99, "value": 99}, {"name": "巴尔的摩", "size": 37, "value": 37}, {"name": "英吉利海峡", "size": 94, "value": 94}, {"name": "海底电缆", "size": 68, "value": 68}, {"name": "海底电缆", "size": 87, "value": 87}, {"name": "丹麦大北电报公司", "size": 44, "value": 44}, {"name": "多模光纤", "size": 68, "value": 68}, {"name": "单模光纤", "size": 3, "value": 3}, {"name": "传输媒介", "size": 43, "value": 43}], "value": 547}, {"name": "网络操作系统", "size": 515, "children": [{"name": "计算机", "size": 95, "value": 95}, {"name": "操作系统", "size": 4, "value": 4}, {"name": "服务器", "size": 49, "value": 49}, {"name": "客户端", "size": 26, "value": 26}, {"name": "服务器", "size": 63, "value": 63}, {"name": "资源", "size": 62, "value": 62}, {"name": "计算机", "size": 31, "value": 31}, {"name": "NOS", "size": 90, "value": 90}, {"name": "工作站", "size": 64, "value": 64}, {"name": "单用户操作系统", "size": 31, "value": 31}], "value": 515}, {"name": "网络管理软件", "size": 573, "children": [{"name": "网络管理", "size": 67, "value": 67}, {"name": "支撑软件", "size": 93, "value": 93}, {"name": "网络设备", "size": 22, "value": 22}, {"name": "网络系统", "size": 53, "value": 53}, {"name": "体系结构", "size": 13, "value": 13}, {"name": "应用程序", "size": 79, "value": 79}, {"name": "网络搜索", "size": 45, "value": 45}, {"name": "Windows NT", "size": 18, "value": 18}, {"name": "网络管理软件", "size": 93, "value": 93}, {"name": "网络管理协议", "size": 90, "value": 90}], "value": 573}, {"name": "网络通信协议", "size": 736, "children": [{"name": "操作系统", "size": 85, "value": 85}, {"name": "体系结构", "size": 84, "value": 84}, {"name": "互联网络", "size": 54, "value": 54}, {"name": "网络", "size": 81, "value": 81}, {"name": "操作系统", "size": 54, "value": 54}, {"name": "体系结构", "size": 90, "value": 90}, {"name": "互联网络", "size": 82, "value": 82}, {"name": "TCP/IP", "size": 86, "value": 86}, {"name": "传输控制协议", "size": 55, "value": 55}, {"name": "网际协议", "size": 65, "value": 65}], "value": 736}, {"name": "资源共享", "size": 437, "children": [{"name": "计算机", "size": 46, "value": 46}, {"name": "操作系统", "size": 53, "value": 53}, {"name": "共享空间", "size": 38, "value": 38}, {"name": "局域网", "size": 61, "value": 61}, {"name": "打印服务器", "size": 84, "value": 84}, {"name": "邮件服务器", "size": 66, "value": 66}, {"name": "局域网", "size": 28, "value": 28}, {"name": "集中存储", "size": 7, "value": 7}, {"name": "网络存储", "size": 47, "value": 47}, {"name": "工作组", "size": 7, "value": 7}], "value": 437}, {"name": "信息传递", "size": 407, "children": [{"name": "现代化管理", "size": 58, "value": 58}, {"name": "电码", "size": 66, "value": 66}, {"name": "购买行为", "size": 6, "value": 6}, {"name": "销售管理", "size": 21, "value": 21}, {"name": "商品信息", "size": 38, "value": 38}, {"name": "购买行为", "size": 32, "value": 32}, {"name": "信息管理", "size": 38, "value": 38}, {"name": "信息活动", "size": 64, "value": 64}, {"name": "有机体", "size": 31, "value": 31}, {"name": "主体要素", "size": 53, "value": 53}], "value": 407}, {"name": "计算机系统", "size": 354, "children": [{"name": "存储信息", "size": 53, "value": 53}, {"name": "结果信息", "size": 21, "value": 21}, {"name": "计算机", "size": 4, "value": 4}, {"name": "硬件", "size": 18, "value": 18}, {"name": "软件", "size": 27, "value": 27}, {"name": "中央处理机", "size": 62, "value": 62}, {"name": "存储器", "size": 61, "value": 61}, {"name": "外部设备", "size": 12, "value": 12}, {"name": "系统软件", "size": 83, "value": 83}, {"name": "应用软件", "size": 13, "value": 13}], "value": 354}, {"name": "计算机通信网", "size": 574, "children": [{"name": "通信设备", "size": 86, "value": 86}, {"name": "数据传输", "size": 58, "value": 58}, {"name": "领域", "size": 87, "value": 87}, {"name": "条件", "size": 76, "value": 76}, {"name": "通信技术", "size": 6, "value": 6}, {"name": "微电子", "size": 86, "value": 86}, {"name": "数据传输", "size": 19, "value": 19}, {"name": "处理器", "size": 50, "value": 50}, {"name": "功能", "size": 10, "value": 10}, {"name": "资源共享", "size": 96, "value": 96}], "value": 574}], "value": 5290}
特点和思路:
优点:
缺点:
模型和资源:
特点和思路:
优点:
缺点:
模型和资源:
特点和思路:
优点:
缺点:
模型和资源:
思路:
优点:
缺点:
资源需求:
思路:
优点:
缺点:
资源需求:
选择合适的模型:
知识图谱的构建和使用:
系统架构:
硬件和计算资源:
通过以上方法,可以有效地利用知识图谱增强大模型的问答和检索能力,提升系统的智能性和实用性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。