基于数据库和NER构建知识图谱流程记录

文章目录

    • 环境准备
    • 拓扑设计
    • 构建流程设计
    • 文件流设计
    • 交互解析算法实现
      • 数据库交互
      • NER解析
      • 相似度计算
    • 基于数据库的文件生成
      • 从数据库中读取字段
      • 将字段后处理后保存为文件
    • 基于文件的知识图谱构建
    • bug修改与算法优化
      • 图数据库连接问题
      • 批量构建知识图谱问题
      • 批量删除边问题
      • 空值处理问题
      • 去重时的大小写问题
      • 加速构建边优化

环境准备

本任务中知识图谱里的内容主要有两个来源,一个是数据库中的字段值,一个是从数据库中的描述字段中通过NER提取出的关键词。

因此,除了在服务器上安装neo4j图数据库环境,在本地安装py2neo包负责与图数据库间交互,还要安装pymysql包负责与数据库间交互,安装paddlenlp完成NER任务。

拓扑设计

从数据库中提取有用的信息,并对应于节点和关系,使用presson绘制出知识图谱的整体拓扑。其中蓝色为节点,黄色为不同节点间的关系(虚线为反向边),绿色同同一类型节点间的关系(基于相似性度量)。
在这里插入图片描述

选中节点,点击数据属性,可以为不同节点添加属性,并记录其在数据库中的来源
在这里插入图片描述
在这里插入图片描述

构建流程设计

其实知识图谱的构建常常是节点和边同时构建的,但由于我们的数据量非常大,同时构建逻辑比较复杂,所以采取了先统一建节点,后统一建边的逻辑。

节点构建时有两个来源:一个是从数据库直接读取,一个是从数据库中的一个描述字段中通过NER提取关键词来构建。

边在构建时除了这两个来源,还有一种来源是同一类型节点间基于相似性计算出连边。

文件流设计

构建节点时,为了让流程更为清晰可控,首先根据预先定义的节点数据库CSV文件NER解析JSON文件去生成节点数据CSV文件,再根据节点数据CSV文件去生成节点。

节点数据库CSV文件名为节点名称,内容包含数据库名、字段和属性三列,其中字段是数据库中的字段名,属性是节点中的属性名。

注意第一行的第三列必须是name,第二列如果以加号开头说明要确保字段的唯一性,下面是一个常规示例:
在这里插入图片描述
写这个文件时可以做一些骚操作,例如在字段中将SQL语句融入进去,比如说我想构建一个坐标节点,其名字为“坐标X,坐标Y”,这个文件的后两列就可以写成:"+CONCAT_WS(',', zbx, zby)",name

节点数据CSV文件,第一列是name,后面是其他属性,相当于把上面的节点数据库CSV文件横过来。以坐标节点为例:

在这里插入图片描述

构建边时,首先根据预先定义的关系数据库CSV文件NER解析JSON文件以及相似性计算算法去生成关系数据CSV文件,再根据关系数据CSV文件去生成边。

关系数据库CSV文件名为关系名称,内容只有一行,包含数据库,起点字段,起点label,起点所用属性,终点字段,终点label,终点所用属性这几列。其中label指的是头实体和尾实体的节点类型,属性是查询时用到的属性。

关系数据CSV文件,列数不固定,第一部分是连接时用到的头实体节点属性,形如name1, axx1, bxx1,...;第二部分是连接时用到的尾实体节点属性,形如name2, axx2, bxx2, ...;最后两列固定为label1, label2,对应头实体节点类型和尾实体节点类型,以时间相似的关系为例:

在这里插入图片描述

交互解析算法实现

为了将数据库中字段解析为知识图谱的内容,需要完成以下几部分的功能:一是直接与数据库交互,二是通过NER解析文本描述内容,三是计算时间和地点间的相似度。

数据库交互

定义下述数据库交互类,输入SQL查询语句后将返回结果封装为dataframe。

import pymysql
import pandas as pd
class SQLSelector:def __init__(self):# 初始化数据库连接self.db_config = {'host':'','user':'','password':'','db':'','charset':'utf8'}self.cursor, self.connection = self.connect_db()def connect_db(self):try:connection = pymysql.connect(**self.db_config)cursor = connection.cursor()return cursor, connectionexcept Exception as e:print(f"Error connecting to the database: {e}")raisedef close_db(self):try:self.cursor.close()self.connection.close()except Exception as e:print(f"Error closing the database connection: {e}")def __del__(self):# 关闭数据库连接self.close_db()def execute_db(self, sql_query):# 返回执行状态和结果try:self.cursor.execute(sql_query)execute_result = pd.DataFrame(self.cursor.fetchall(), columns=[desc[0] for desc in self.cursor.description])execute_state = Trueexcept Exception as e:execute_result = str(e)execute_state = Falsereturn execute_state, execute_result

NER解析

定义下列文本解析类,由于没有经过训练,直接使用paddlenlp做解析,并把结果保存为json,因此结果不是很准确,后续需要后处理,下面的代码做了脱敏处理。

from paddlenlp import Taskflow
import pandas as pd
import json
from utils import write_json_file, read_json_file, is_id_card, count_elements_in_nested_list
from tqdm import tqdmtqdm.pandas()class PaddleNLP:def __init__(self):self.ner_file_path = "./Data/NER_data"def paddle_ner(self, batch_size, input_list):ner = Taskflow("ner", batch_size=batch_size)print("start NER")result_list = ner(input_list)return result_listdef paddle_ie(self, batch_size, input_list, schema):ner = Taskflow("knowledge_mining", batch_size=batch_size)print("start IE")result_list = ner(input_list)return result_listdef ner_data_save(self, path):ner_source_data = pd.read_json(path)ner_source_data[''] = ner_source_data[''].fillna("此处为空")input_list = ner_source_data[""].tolist()ner_result_list = self.paddle_ner(batch_size=50, input_list=input_list)write_json_file(self.ner_file_path + "./ner_result_no_na.json", ner_result_list)def ie_data_save(self, path):ner_source_data = pd.read_json(path)ner_source_data[''] = ner_source_data[''].fillna("此处为空")input_list = ner_source_data[""].tolist()[0:100]schema = []ner_result_list = self.paddle_ie(batch_size=10, input_list=input_list, schema=schema)print(ner_result_list)def combine_ner_data(self, ner_path, source_data_path):ner_data = read_json_file(ner_path)print(len(ner_data))source_df = pd.read_json(source_data_path)print(len(source_df) - len(ner_data))source_df["ner_result"] = ner_dataoutput_file_path = self.ner_file_path + '/ner_data.json'source_df.to_json(output_file_path, force_ascii=False, orient='records')def analysis_ner_data(self, data_path):ner_df = pd.read_json(data_path)results = ner_df.progress_apply(combined_entity_check, axis=1)ner_df[''] = results['']ner_df[''] = results['']ner_df.drop('ner_result', axis=1, inplace=True)output_file_path = self.ner_file_path + '/updated_ner_data.json'ner_df.to_json(output_file_path, force_ascii=False, orient='records')def check_data(self, data_path):ner_data = pd.read_json(data_path)qtr_xm = ner_data["qtr_xm"]print(count_elements_in_nested_list(qtr_xm))def combined_entity_check(row):# 提取人物类实体human_entities = set(item[0] for item in row['ner_result'] if item[1] == '人物类_实体')xy = row[''].split(',') if row[''] else []sa = row[''].split(',') if row[''] else []check_result = []qtr_xm = []return pd.Series([check_result, qtr_xm], index=['check_result', 'qtr_xm'])if __name__ == "__main__":paddle_nlp = PaddleNLP()ner_data_path = paddle_nlp.ner_file_path + "/updated_ner_data.json"source_data_path = paddle_nlp.ner_file_path + "/ner_source.json"paddle_nlp.check_data(ner_data_path)

相似度计算

基于KD-Tree计算坐标和时间相似度

def filter_coordinate_nodes(nodes, label, threshold_distance=0.02):print("计算坐标相似度中...")coordinates = np.array([(float(node[1]), float(node[2])) for node in nodes if node[1] != "NULL" and node[2] != "NULL"
])# Build KD-treekdtree = cKDTree(coordinates)# Query pairs within the threshold distancepairs = kdtree.query_pairs(threshold_distance, output_type='ndarray')unique_pairs = set()for i, j in pairs:pair = (min(nodes[i][0], nodes[j][0]), max(nodes[i][0], nodes[j][0]), label, label)unique_pairs.add(pair)print("计算坐标相似度完成!")return list(unique_pairs)def filter_time_nodes(nodes, label, threshold=21600):print("Calculating time similarity...")# Extract timestamps from nodestimestamps = np.array([int(datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S').timestamp())if time_str.strip() and time_str.strip() != "NULL"else Nonefor time_str in nodes])# Build KD-treekdtree = cKDTree(timestamps.reshape(-1, 1))# Query pairs within the threshold time differencepairs = kdtree.query_pairs(threshold, output_type='ndarray')unique_pairs = set()for i, j in pairs:pair = (min(nodes[i], nodes[j]), max(nodes[i], nodes[j]), label, label)unique_pairs.add(pair)print("Time similarity calculation completed!")return list(unique_pairs)

经过比较,其运行速度不如下面这种矩阵运算

def filter_coordinate_nodes(nodes, label, threshold_distance=0.001):print("计算坐标相似度中...")# 提取坐标信息并转换为NumPy数组coordinates = []for node in nodes:if node[1] != "NULL" and node[2] != "NULL":x_str = re.sub("[^0-9.]", "", str(node[1]))y_str = re.sub("[^0-9.]", "", str(node[2]))if x_str and y_str:  # 确保字符串不为空coordinates.append((float(x_str), float(y_str)))coordinates = np.array(coordinates)# 使用广播计算两两之间的绝对值距离unique_pairs = set()for i in tqdm(range(len(coordinates)), desc="相似度计算"):# 计算点i与其他所有点的距离distances = np.sqrt(np.sum((coordinates - coordinates[i]) ** 2, axis=1))# 找到距离小于阈值的点close_points = np.where(distances < threshold_distance)[0]# 避免将点i与自身比较close_points = close_points[close_points != i]for j in close_points:pair = (min(nodes[i][0], nodes[j][0]), max(nodes[i][0], nodes[j][0]), label, label)unique_pairs.add(pair)print("计算坐标相似度完成!")return list(unique_pairs)def filter_time_nodes(nodes, label, threshold=10800):print("计算时间相似度中...")placeholder = -1# 将时间字符串转换为时间戳,对于无效值使用占位符timestamps = np.array([int(datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S').timestamp())if time_str.strip() and time_str.strip() != "NULL"else placeholder  # 使用占位符代替 Nonefor time_str in nodes])# 使用广播计算两两之间的距离unique_pairs = set()# 使用 tqdm 包裹您的循环来显示进度条for i in tqdm(range(len(timestamps)), desc="计算进度"):if timestamps[i] is not None:# 计算时间点 i 与其他所有时间点的差异distances = np.abs(timestamps - timestamps[i])# 找到小于阈值且不是自己的时间点close_points = np.where((distances < threshold) & (distances != 0))[0]for j in close_points:pair = (min(nodes[i], nodes[j]), max(nodes[i], nodes[j]), label, label)unique_pairs.add(pair)print("计算时间相似度完成!")return list(unique_pairs)

基于数据库的文件生成

下面是根据生成节点和关系数据库CSV文件去生成数据CSV文件的部分,基于NER和基于相似度的文件生成逻辑与之类似,在此略过。

从数据库中读取字段

import os
import pandas as pd
class DbInfoReader:def __init__(self):# 数据库CSV文件存放路径self.node_file_path = "./NodeDb"self.rel_file_path = "./RelDb"# 必要时只对filter里面的做更新self.node_filter = []def generate_node_info(self):node_files = os.listdir(self.node_file_path)for node_file in node_files:node_db_dict = {}df = pd.read_csv(os.path.join(self.node_file_path, node_file), index_col=False)# CSV文件名代表节点label,第一列是数据库名,第二列是数据库字段,第三列是对应属性名# 注意第一行的第三列必须是name# 第二列如果以加号开头说明要确保该字段的唯一性assert df.iloc[0,2] == "name"label = os.path.splitext(os.path.basename(node_file))[0]if len(self.node_filter) == 0 or label in self.node_filter:for _, row in df.iterrows():# 转化为字典data_dict = row.to_dict()key_name = data_dict.pop('数据库')if key_name not in node_db_dict:node_db_dict[key_name] = {}node_db_dict[key_name][data_dict['字段']] = data_dict['属性']yield label, node_db_dictdef generate_rel_info(self):rel_files = os.listdir(self.rel_file_path)for rel_file in rel_files:# CSV格式:库,起点字段,起点label,起点所用属性,终点字段,终点label,终点所用属性df = pd.read_csv(os.path.join(self.rel_file_path, rel_file), index_col=False)label = os.path.splitext(os.path.basename(rel_file))[0]for _, row in df.iterrows():# 转化为字典data_dict = row.to_dict()yield label, data_dictif __name__ == "__main__":dbinforeader = DbInfoReader()dbinforeader.generate_rel_info()

将字段后处理后保存为文件

from SQLSelector import *
from DbInfoReader import *
from utils import generate_sql_dict, read_json, is_name
from tqdm import tqdm
class DbInfoSaver:def __init__(self):# 属性CSV文件存放路径self.node_file_path = "./NodeFile"self.rel_file_path = "./RelFile"self.ner_file_path = "./Data/NER_data"self.split_files = []self.delete_unknown_files = []self.dbinforeader = DbInfoReader()self.sqlselector = SQLSelector()self.sql_dict = generate_sql_dict()def save_node_file_in_sql(self):# 节点来源一:从数据库里直接建节点for label, node_db_dict in self.dbinforeader.generate_node_info():data = pd.DataFrame()for db, pair in node_db_dict.items():# 添加唯一约束columns = []for db_name, node_name in pair.items():ddb_name = db_nameif db_name[0] == "+":ddb_name = "DISTINCT " + db_name[1:]columns.append("{} AS {}".format(ddb_name, node_name))column_str = ", ".join(columns)sql_query = "SELECT {} FROM {}".format(column_str, db)results = self.sqlselector.execute_db(sql_query)[1]data = pd.concat([data, results], ignore_index=True)# 使用字典对字段值做映射,异常值映射为未知if label in self.sql_dict:for col, dic in self.sql_dict[label].items():data[col] = data[col].map(lambda x: dic.get(x, '未知'))# print(col, set(data[col]))# 对多值情况做分割if label in self.split_files:new_df = data['name'].str.split(',', expand=True).reset_index(drop=True).drop_duplicates().stack().reset_index(drop=True).drop_duplicates()new_df = pd.DataFrame(new_df)new_df.columns = ["name"]data = new_df# 对每一列删除多余空格for col in data.columns:data[col] = data[col].apply(lambda x: x.strip() if isinstance(x, str) else x)data = data.drop_duplicates(subset = data.columns).reset_index(drop=True)# 删除每行全部未知的节点if label in self.delete_unknown_files:cols_to_check = data.columns[1:]to_drop = data[cols_to_check].eq('未知') | data[cols_to_check].isna() | data[cols_to_check].eq('')data = data[~to_drop.all(axis=1)]data.to_csv(os.path.join(self.node_file_path, f"{label}.csv"), index=False)def save_rel_file_in_sql(self):# 关系来源一:从数据库里直接建关系for label, rel_db_dict in tqdm(self.dbinforeader.generate_rel_info()):# 构建起点和终点的 SELECT 子句start_column = "{} AS {}".format(rel_db_dict['起点字段'], rel_db_dict['起点所用属性']+'1')end_column = "{} AS {}".format(rel_db_dict['终点字段'], rel_db_dict['终点所用属性']+'2')# 构建 SQL 查询语句sql_query = "SELECT DISTINCT {}, {} FROM {}".format(start_column, end_column, rel_db_dict['库'])# 执行 SQL 查询results = self.sqlselector.execute_db(sql_query)[1]results['label1'] = rel_db_dict['起点label']results['label2'] = rel_db_dict['终点label']# 对每一列删除多余空格for col in results.columns:results[col] = results[col].apply(lambda x: x.strip() if isinstance(x, str) else x)results = results.drop_duplicates(subset=results.columns).reset_index(drop=True)# 保存结果到 CSV 文件results.to_csv(os.path.join(self.rel_file_path, f"{label}.csv"), index=False)if __name__ == "__main__":dbinfosaver = DbInfoSaver()dbinfosaver.save_node_file_in_sql()

基于文件的知识图谱构建

在生成了节点和关系的数据CSV文件之后,直接读取文件内容并生成知识图谱中的节点和关系,全流程走完。

import pandas as pd
from py2neo import Graph, Node, NodeMatcher, Relationship
import os
from tqdm import tqdm
import logging# Set up logging configuration
logging.basicConfig(filename='error_log.txt', level=logging.ERROR)class GraphGenerator:def __init__(self) -> None:self.node_file_path = "./NodeFile"self.rel_file_path = "./RelFile"self.graph = Graph("http://localhost:7474", auth=("neo4j", "123456"), name='neo4j')def generate_nodes_list(self, csv_file_path):"""将节点CSV文件中的数据导入到图数据库中:param graph: 图数据库:param csv_file_path: csv文件的路径"""# 读取CSV文件df = pd.read_csv(csv_file_path, index_col=False)# df.dropna(subset=['name'], inplace=True)df.fillna("NULL", inplace=True)nodes = []label = os.path.splitext(os.path.basename(csv_file_path))[0]# 遍历数据,将数据导入到图数据库中for _, row in tqdm(df.iterrows(), total=len(df), desc="Processing Rows"):# 转化为字典data_dict = row.to_dict()# 创建一个节点并添加到列表中node = Node(label, **data_dict)nodes.append(node)return nodesdef generate_relationship_list(self, graph: Graph, csv_file_path):"""将边CSV文件中的数据导入到图数据库中:param graph: 图数据库:param csv_file_path: csv文件的路径"""# 读取CSV文件df = pd.read_csv(csv_file_path, index_col=False)df.fillna("NULL", inplace=True)relationship_label = os.path.splitext(os.path.basename(csv_file_path))[0]start_label = df['label1'][0]end_label = df['label2'][0]start_keys = [x for x in list(df.columns[0:-2]) if x[-1] == '1']node_start_keys = [x[:-1] for x in start_keys]end_keys = [x for x in list(df.columns[0:-2]) if x[-1] == '2']node_end_keys = [x[:-1] for x in end_keys]# 预先加载所有需要的节点,实现节点的大小写不敏感快速匹配算法start_nodes = list(NodeMatcher(graph).match(start_label))end_nodes = list(NodeMatcher(graph).match(end_label))start_nodes_map = {tuple(node[key].lower() if isinstance(node[key], str) else node[key] for key in node_start_keys): node for node in start_nodes}end_nodes_map = {tuple(node[key].lower() if isinstance(node[key], str) else node[key] for key in node_end_keys): node for node in end_nodes}# 创建一个空的关系列表relationships = []# 遍历数据,将数据导入到图数据库中for _, row in tqdm(df.iterrows(), total=len(df), desc="Processing Rows"):# 转化为字典data_dict = row.to_dict()# 查询节点start_node = start_nodes_map.get(tuple(data_dict[key].lower() if isinstance(data_dict[key], str) else data_dict[key] for key in start_keys))end_node = end_nodes_map.get(tuple(data_dict[key].lower() if isinstance(data_dict[key], str) else data_dict[key] for key in end_keys))# 未找到相关节点if start_node is None or end_node is None:error_message = f"Error: Node not found for relation {data_dict}"print(error_message)  # This will print the error message to the consolelogging.error(error_message)continue# 创建关系并添加到关系列表relationship = Relationship(start_node, relationship_label, end_node)relationships.append(relationship)return relationshipsdef create_nodes_or_relationships(self, graph: Graph, nodes_or_relations):batch_size = 10000for batch in [nodes_or_relations[i:i+batch_size] for i in range(0, len(nodes_or_relations), batch_size)]:tx = graph.begin()for data in batch:tx.create(data)graph.commit(tx)def generate_all(self, mode='all'):"""根据指定的模式生成节点、关系或两者。参数:- mode (str): 指定操作模式。'all' 生成节点和关系,'node' 仅生成节点,'rel' 仅生成关系。返回:无"""if mode in ['all', 'node']:# 遍历节点文件夹下的所有节点文件# 每次先清空图数据库self.graph.delete_all()for node_file in tqdm(os.listdir(self.node_file_path), desc="处理节点csv中:"):if node_file.endswith(".csv"):csv_path = os.path.join(self.node_file_path, node_file)nodes = self.generate_nodes_list(csv_path)self.create_nodes_or_relationships(graph=self.graph, nodes_or_relations=nodes)if mode in ['all', 'rel']:# 遍历边文件夹下的所有边文件# 每次先清空所有边if mode == 'rel':rel_types = self.graph.schema.relationship_typesfor rel_type in rel_types:query = f"MATCH (n)-[r:{rel_type}]-(m) DELETE r"self.graph.run(query)for edge_file in tqdm(os.listdir(self.rel_file_path), desc="处理边csv中:"):if edge_file.endswith(".csv"):csv_path = os.path.join(self.rel_file_path, edge_file)relationships = self.generate_relationship_list(self.graph, csv_path)self.create_nodes_or_relationships(graph=self.graph, nodes_or_relations=relationships)if __name__ == "__main__":graph_generator = GraphGenerator()graph_generator.generate_all(mode='node')print(graph_generator)

进入浏览器后查看,知识图谱建立成功!
在这里插入图片描述

在这里插入图片描述

bug修改与算法优化

图数据库连接问题

一开始使用Graph("http://localhost:7474", auth=("neo4j", "123456"))连接图数据库,在执行tx = graph.begin()这句会报错

py2neo.errors.ProtocolError: Cannot decode response content as JSON

连接的时候加上name参数就好了。

批量构建知识图谱问题

构建函数的实现一开始为:

def create_nodes_or_relationships(self, graph: Graph, nodes_or_relations):tx = graph.begin()for node_or_relation in nodes_or_relations:tx.create(node_or_relation)graph.commit(tx)

这样会导致下列两种问题

py2neo.errors.ProtocolError: Cannot decode response content as JSON
[Transaction.TransactionNotFound] Unrecognized transaction id. Transaction may have timed out and been rolled back.

如果改成下面这种一个一个建,就不会报错,所以分析应该是一次建立的节点或关系太多了:

def create_nodes_or_relationships(self, graph: Graph, nodes_or_relations):for node_or_relation in nodes_or_relations:tx = graph.begin()tx.create(node_or_relation)graph.commit(tx)

为了加速,改成了现在这种一次批量建10000个的方法。

批量删除边问题

由于建边时出了一点问题,试图将所有边删除,删边语句一开始为"MATCH ()-[r]-() DELETE r',会导致下列错误:

py2neo.errors.DatabaseError: [Statement.ExecutionFailed] Java heap space

应该是边的数量太多了,所以我改成现在这种,每次删除一类边。

空值处理问题

图数据库中节点的属性不能为空值,解决方法是将CSV数据文件的空值替换为"NULL",也就是这句:

df.fillna("NULL", inplace=True)

去重时的大小写问题

  • 数据库中使用DISTINCT关键词去重,这是大小写不敏感的,无法区分大写和小写字母
  • dataframe数据使用drop_duplicates方法去重,这是大小写敏感的

当这两种方法同时用于生成数据文件,就会造成不匹配问题,后来通过在节点匹配时加入lower()方法统一转换为小写解决。

加速构建边优化

在构建边的时候,首先要找到对应的头实体和尾实体,之前的匹配算法是使用了内置的全局匹配

matcher = NodeMatcher(graph)
start_node = matcher.match(data_dict['label1'], **start_property).first()
end_node = matcher.match(data_dict['label2'], **end_property).first()

这样跑是能跑,但是速度会非常慢,因为每次都从所有的节点里面找。我们可以观察到,对某种特定的关系,头实体和尾实体都属于某种特定的节点类型,因此可以先把所有这一类型的节点存到一个字典里,再在这个字典里做匹配,这也是目前实现的算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/598228.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pygame学习(二)——绘制线条、圆、矩形等图案

导语 pygame是一个跨平台Python库(pygame news)&#xff0c;专门用来开发游戏。pygame主要为开发、设计2D电子游戏而生&#xff0c;提供图像模块&#xff08;image&#xff09;、声音模块&#xff08;mixer&#xff09;、输入/输出&#xff08;鼠标、键盘、显示屏&#xff09;模…

商城小程序(5.商品列表)

目录 一、定义请求参数对象二、获取商品列表数据三、渲染商品列表结构四、把商品item封装为自定义组件五、使用过滤器处理价格六、上拉加载更多七、下拉刷新八、点击商品item项跳转到详情页面 这章主要完成商品列表页面的编写&#xff1a;位于subpkg分包下的goods_list页面 一…

【竞技宝】DOTA2:二号位地位发生变化 圣斧、血棘助法核重回巅峰!

北京时间2024年1月5日&#xff0c;目前国服已经更新7.35b版本一段时间了&#xff0c;在这段时间里&#xff0c;各位看官是否感受到了比赛节奏和英雄胜率方面的变化呢&#xff1f;从全分段的出场率以及胜率的变化来看&#xff0c;二号位在游戏内的地位已经发生了很大的变化。 在…

DrGraph原理示教 - OpenCV 4 功能 - 膨胀腐蚀

在二值图的结果基础上&#xff0c;可针对性处理。 这些处理有些是概念上的&#xff0c;有些是原理上的&#xff0c;也有形态上的&#xff0c;那就看用途与目的了。 本质上还是对二值图的黑白点进行处理&#xff0c;以用于图像增强、边缘检测、图像分割等多个领域。比如膨胀与腐…

Capsolver:解决Web爬虫中CAPTCHA挑战的最优解决方案

Web爬虫已经成为从各种在线来源提取和分析数据的不可或缺的技术。然而&#xff0c;在Web爬取过程中&#xff0c;经常会遇到的一个共同挑战是CAPTCHA。CAPTCHA&#xff08;完全自动化的公共图灵测试&#xff0c;用于区分计算机和人类&#xff09;是一种安全措施&#xff0c;旨在…

vue3中使用elementplus中的el-tree-select,自定义显示名称label

<el-tree-select v-model"addPval" node-key"id" :data"menulists" :render-after-expand"false" :props"menuProps" /> <el-divider />let menuProps {//自定义labellabel: (data: { name: any; }) > {ret…

web期末个人引导页透明版

效果图 代码 css代码 * {box-sizing: border-box; }body {color: #2b2c48;font-family: "Jost", sans-serif;background-image: url(../img/bg.jpg);background-repeat: no-repeat;background-size: cover;background-position: center;background-attachment: fix…

杨中科 ASP.NETCore开发效率利器 HOT RELOAD

HOT RELOAD 1、困惑:修改了服务器端的代码&#xff0c;必须重新运行程序。 2、方法1: [启动 (不调试) ] 3、方法2: .NET 6开始的Hot Reload(热重载) 正常修改代码后 不重启&#xff0c;是无法看到新的数据展示在页面 修改 运行结果&#xff1a; 方式一&#xff1a;设置开始…

java+vue+uniapp开发的前后端分离社交论坛问答发帖

源码介绍&#xff1a; 前后端分离社交论坛问答发帖BBS源码&#xff0c;社交论坛小程序|H5论坛| app论坛是javavueuniapp开发的前后端分离社交论坛问答发帖/BBS项目&#xff0c; 包括论坛图文帖&#xff0c;视频&#xff0c;圈子&#xff0c;IM私聊&#xff0c;微信支付&…

PyTorch数据并行(DP/DDP)浅析

一直以来都是用的单机单卡训练模型&#xff0c;虽然很多情况下已经足够了&#xff0c;但总有一些情况得上分布式训练&#xff1a; 模型大到一张卡放不下&#xff1b;单张卡batch size不敢设太大&#xff0c;训练速度慢&#xff1b;当你有好几张卡&#xff0c;不想浪费&#xf…

java SSM水质历史数据可视化设计myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM水质历史数据可视化设计是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主…

k_d树, KNN算法学习笔记_1 距离和范数

k_d树, KNN算法学习笔记_1 距离和范数 二维树中最近邻搜索的示例。这里&#xff0c;树已经构建好了&#xff0c;每个节点对应一个矩形&#xff0c;每个矩形被分割成两个相等的子矩形&#xff0c;叶子对应于包含单个点的矩形 From Wikipedia 1&#xff0e; k k k近邻法是基本且简…

Elasticsearch:Serarch tutorial - 使用 Python 进行搜索 (一)

本实践教程将教你如何使用 Elasticsearch 构建完整的搜索解决方案。 在本教程中你将学习&#xff1a; 如何对数据集执行全文关键字搜索&#xff08;可选使用过滤器&#xff09;如何使用机器学习模型生成、存储和搜索密集向量嵌入如何使用 ELSER 模型生成和搜索稀疏向量如何使用…

Nginx 简介和入门 - part1

虽然作为1个后端程序员&#xff0c; 终究避不开这东西 安装Nginx 本人的测试服务器是debian &#xff0c; 安装过程跟ubuntu基本一样 sudo apt-get install nginx问题是 nginx 安装后 执行文件在/usr/sbin 而不是/usr/bin 所以正常下普通用户是无法使用的。 必须切换到root…

C练习——定期存取并行

题目&#xff1a;假设银行一年整存零取的月息为1.875%&#xff0c;现在某人手头有一笔钱&#xff0c;他打算在今后5年 中&#xff0c;每年年底取出1000元作为孩子来年的教育金&#xff0c;到第5年孩子毕业时刚好取完这笔钱&#xff0c;请编 程计算第1年年初时他应存入银行多少钱…

深度学习课程实验三训练和测试卷积神经网络

一、 实验目的 1、学会搭建、训练和测试卷积神经网络&#xff0c;并掌握其应用。 2、掌握使用numpy实现卷积(CONV)和池化(POOL)层&#xff0c;包括正向春传播和反向传播。 二、 实验步骤 Convolutional Neural Networks: Step by Step 1、导入所需要的安装包 2、构建卷积神经…

RabbitMQ安装与应用

文章目录 1. RabbitMQ1.1. 同步通讯与异步通讯1.2. 异步通讯的优缺点1.3. 几种MQ的对比1.4. docker安装运行RabbitMQ 流程1.5. RabbitMQ的几个概念1.6. 五种模型1.6.1. 基本消息队列 1.7. 基本使用1.7.1. 1建立连接时会出现以下界面![在这里插入图片描述](https://img-blog.csd…

信息论与编码期末复习——概念论述简答题(一)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

箱体透明屏的原理

箱体透明屏的原理主要是通过特殊的结构设计&#xff0c;使得屏幕具有透光性&#xff0c;从而实现在显示内容的同时保持箱体的透明效果。具体来说&#xff0c;箱体透明屏采用镂空结构的设计&#xff0c;将灯条一根根的排列成透明状&#xff0c;使得屏幕整体看起来具有透明感。在…

基于卷积神经网络的回归分析

目录 背影 卷积神经网络CNN的原理 卷积神经网络CNN的定义 卷积神经网络CNN的神经元 卷积神经网络CNN的激活函数 卷积神经网络CNN的传递函数 卷积神经网络的回归分析 完整代码:卷积神经网络的回归分析(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/…