|
|
|
@ -1,33 +1,35 @@
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
import ConfigSpace
|
|
|
|
|
import pandas
|
|
|
|
|
import torch
|
|
|
|
|
from py_entitymatching.debugmatcher.debug_gui_utils import _get_metric
|
|
|
|
|
|
|
|
|
|
from ConfigSpace.read_and_write import json as csj
|
|
|
|
|
import py_entitymatching as em
|
|
|
|
|
import py_entitymatching.catalog.catalog_manager as cm
|
|
|
|
|
import pandas as pd
|
|
|
|
|
import six
|
|
|
|
|
from ConfigSpace import Configuration
|
|
|
|
|
from md_discovery.multi_process_infer_by_pairs import my_Levenshtein_ratio, norm_cos_sim
|
|
|
|
|
from settings import *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_prediction_for_md_discovery(pred: pd.DataFrame,
|
|
|
|
|
tp_single_tuple_path: str = er_output_dir + "tp_single_tuple.csv",
|
|
|
|
|
fn_single_tuple_path: str = er_output_dir + "fn_single_tuple.csv"):
|
|
|
|
|
t_single_tuple_path: str = er_output_dir + "t_single_tuple.csv"):
|
|
|
|
|
# 提取预测表中真阳和假阴部分
|
|
|
|
|
tp = pred[(pred['gold'] == 1) & (pred['predicted'] == 1)]
|
|
|
|
|
fn = pred[(pred['gold'] == 1) & (pred['predicted'] == 0)]
|
|
|
|
|
# 拼成一张表
|
|
|
|
|
df = pd.concat([tp, fn])
|
|
|
|
|
# 将真阳/假阴表中左右ID调整一致
|
|
|
|
|
for index, row in tp.iterrows():
|
|
|
|
|
tp.loc[index, "rtable_" + rtable_id] = row["ltable_" + rtable_id]
|
|
|
|
|
for index, row in fn.iterrows():
|
|
|
|
|
fn.loc[index, "rtable_" + rtable_id] = row["ltable_" + rtable_id]
|
|
|
|
|
for index, row in df.iterrows():
|
|
|
|
|
df.loc[index, "rtable_" + rtable_id] = row["ltable_" + rtable_id]
|
|
|
|
|
|
|
|
|
|
pred_columns = pred.columns.values.tolist()
|
|
|
|
|
l_columns = []
|
|
|
|
|
r_columns = []
|
|
|
|
|
columns = []
|
|
|
|
|
cols = []
|
|
|
|
|
# 将预测表中左表和右表字段名分别加入两个列表
|
|
|
|
|
for _ in pred_columns:
|
|
|
|
|
if _.startswith('ltable'):
|
|
|
|
@ -36,25 +38,15 @@ def process_prediction_for_md_discovery(pred: pd.DataFrame,
|
|
|
|
|
r_columns.append(_)
|
|
|
|
|
# 将左表中字段名去掉前缀,作为统一的字段名列表(前提是两张表内对应字段名调整一致)
|
|
|
|
|
for _ in l_columns:
|
|
|
|
|
columns.append(_.replace('ltable_', ''))
|
|
|
|
|
|
|
|
|
|
# 将表拆分成左右两部分
|
|
|
|
|
tpl = tp[l_columns]
|
|
|
|
|
tpr = tp[r_columns]
|
|
|
|
|
# 将左右两部分字段名统一
|
|
|
|
|
tpl.columns = columns
|
|
|
|
|
tpr.columns = columns
|
|
|
|
|
cols.append(_.replace('ltable_', ''))
|
|
|
|
|
|
|
|
|
|
fnl = fn[l_columns]
|
|
|
|
|
fnr = fn[r_columns]
|
|
|
|
|
fnl.columns = columns
|
|
|
|
|
fnr.columns = columns
|
|
|
|
|
ldf = df[l_columns]
|
|
|
|
|
rdf = df[r_columns]
|
|
|
|
|
ldf.columns = cols
|
|
|
|
|
rdf.columns = cols
|
|
|
|
|
t_single_tuple = pd.concat([ldf, rdf])
|
|
|
|
|
|
|
|
|
|
tp_single_tuple = pd.concat([tpl, tpr])
|
|
|
|
|
fn_single_tuple = pd.concat([fnl, fnr])
|
|
|
|
|
|
|
|
|
|
tp_single_tuple.to_csv(tp_single_tuple_path, sep=',', index=False, header=True)
|
|
|
|
|
fn_single_tuple.to_csv(fn_single_tuple_path, sep=',', index=False, header=True)
|
|
|
|
|
t_single_tuple.to_csv(t_single_tuple_path, sep=',', index=False, header=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def evaluate_prediction(df: pd.DataFrame, labeled_attr: str, predicted_attr: str, matching_number: int,
|
|
|
|
@ -100,43 +92,57 @@ def load_mds(paths: list) -> list:
|
|
|
|
|
# 读取每一行的md,加入该文件的md列表
|
|
|
|
|
for line in f.readlines():
|
|
|
|
|
md_metadata = line.strip().split('\t')
|
|
|
|
|
md = eval(md_metadata[0].replace('md:', ''))
|
|
|
|
|
confidence = eval(md_metadata[2].replace('confidence:', ''))
|
|
|
|
|
if confidence > 0:
|
|
|
|
|
mds.append(md)
|
|
|
|
|
# todo 如果MD文件的形式改了 这里也要改
|
|
|
|
|
md = eval(md_metadata[1])
|
|
|
|
|
mds.append(md)
|
|
|
|
|
all_mds.extend(mds)
|
|
|
|
|
return all_mds
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_explicable(row, all_mds: list) -> bool:
|
|
|
|
|
def is_explicable(row, all_mds: list, st_dict) -> bool:
|
|
|
|
|
attrs = all_mds[0].keys() # 从第一条md中读取所有字段
|
|
|
|
|
for md in all_mds:
|
|
|
|
|
explicable = True # 假设这条md能解释当前元组
|
|
|
|
|
for a in attrs:
|
|
|
|
|
threshold = md[a]
|
|
|
|
|
if norm_cos_sim(embedding_dict[str(getattr(row, 'ltable_'+a))],
|
|
|
|
|
embedding_dict[str(getattr(row, 'rtable_'+a))]) < threshold:
|
|
|
|
|
explicable = False # 任意一个字段的相似度达不到阈值,这条md就不能解释当前元组
|
|
|
|
|
break # 不再与当前md的其他相似度阈值比较,跳转到下一条md
|
|
|
|
|
if a != target_attr:
|
|
|
|
|
if st_dict[a][row[0]].item() < md[a]:
|
|
|
|
|
explicable = False # 任意一个字段的相似度达不到阈值,这条md就不能解释当前元组
|
|
|
|
|
break # 不再与当前md的其他相似度阈值比较,跳转到下一条md
|
|
|
|
|
if explicable:
|
|
|
|
|
return True # 任意一条md能解释,直接返回
|
|
|
|
|
return False # 遍历结束,不能解释
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_data(left_path: str, right_path: str, mapping_path: str):
|
|
|
|
|
left = pd.read_csv(left_path, encoding='ISO-8859-1')
|
|
|
|
|
cm.set_key(left, left.columns.values.tolist()[0])
|
|
|
|
|
left.fillna("", inplace=True)
|
|
|
|
|
left = left.astype(str)
|
|
|
|
|
|
|
|
|
|
right = pd.read_csv(right_path, encoding='ISO-8859-1')
|
|
|
|
|
cm.set_key(right, right.columns.values.tolist()[0])
|
|
|
|
|
right.fillna("", inplace=True)
|
|
|
|
|
right = right.astype(str)
|
|
|
|
|
|
|
|
|
|
mapping = pd.read_csv(mapping_path)
|
|
|
|
|
mapping = mapping.astype(str)
|
|
|
|
|
return left, right, mapping
|
|
|
|
|
def build_col_pairs_sim_tensor_dict(predictions: pandas.DataFrame):
|
|
|
|
|
predictions_attrs = predictions.columns.values.tolist()
|
|
|
|
|
col_tuple_list = []
|
|
|
|
|
for _ in predictions_attrs:
|
|
|
|
|
if _.startswith('ltable'):
|
|
|
|
|
left_index = predictions_attrs.index(_)
|
|
|
|
|
right_index = predictions_attrs.index(_.replace('ltable_', 'rtable_'))
|
|
|
|
|
col_tuple_list.append((left_index, right_index))
|
|
|
|
|
|
|
|
|
|
length = predictions.shape[0]
|
|
|
|
|
width = predictions.shape[1]
|
|
|
|
|
sentences = []
|
|
|
|
|
for col in range(0, width):
|
|
|
|
|
for row in range(0, length):
|
|
|
|
|
cell_value = predictions.values[row, col]
|
|
|
|
|
sentences.append(cell_value)
|
|
|
|
|
embedding = model.encode(sentences, convert_to_tensor=True, device="cuda")
|
|
|
|
|
split_embedding = torch.split(embedding, length, dim=0)
|
|
|
|
|
table_tensor = torch.stack(split_embedding, dim=0, out=None)
|
|
|
|
|
# prediction的归一化嵌入张量
|
|
|
|
|
norm_table_tensor = torch.nn.functional.normalize(table_tensor, dim=2)
|
|
|
|
|
sim_tensor_dict = {}
|
|
|
|
|
for col_tuple in col_tuple_list:
|
|
|
|
|
lattr_tensor = norm_table_tensor[col_tuple[0]]
|
|
|
|
|
rattr_tensor = norm_table_tensor[col_tuple[1]]
|
|
|
|
|
mul_tensor = lattr_tensor * rattr_tensor
|
|
|
|
|
sim_tensor = torch.sum(mul_tensor, 1)
|
|
|
|
|
sim_tensor = sim_tensor / 2 + 0.5
|
|
|
|
|
sim_tensor_dict[predictions_attrs[col_tuple[0]].replace('ltable_', '')] = sim_tensor
|
|
|
|
|
return sim_tensor_dict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ml_er(iter_round: int, config: Configuration = None, ):
|
|
|
|
@ -277,30 +283,27 @@ def ml_er(iter_round: int, config: Configuration = None, ):
|
|
|
|
|
predictions_attrs.extend(attrs_with_r_prefix)
|
|
|
|
|
predictions_attrs.extend(['gold', 'predicted'])
|
|
|
|
|
predictions = predictions[predictions_attrs]
|
|
|
|
|
process_prediction_for_md_discovery(predictions)
|
|
|
|
|
predictions = predictions.reset_index(drop=True)
|
|
|
|
|
predictions = predictions.astype(str)
|
|
|
|
|
sim_tensor_dict = build_col_pairs_sim_tensor_dict(predictions)
|
|
|
|
|
|
|
|
|
|
md_paths = [md_output_dir + 'tp_mds.txt', md_output_dir + 'tp_vio.txt',
|
|
|
|
|
md_output_dir + 'fn_mds.txt', md_output_dir + 'fn_vio.txt']
|
|
|
|
|
epl_match = 0 # 可解释,预测match
|
|
|
|
|
nepl_mismatch = 0 # 不可解释,预测mismatch
|
|
|
|
|
|
|
|
|
|
md_paths = [md_output_dir + 'mds.txt', md_output_dir + 'vio.txt']
|
|
|
|
|
md_list = load_mds(md_paths) # 从全局变量中读取所有的md
|
|
|
|
|
epl_match = 0 # 可解释,预测match
|
|
|
|
|
if len(md_list) > 0:
|
|
|
|
|
for row in predictions.itertuples():
|
|
|
|
|
if is_explicable(row, md_list):
|
|
|
|
|
if getattr(row, 'predicted') == 1:
|
|
|
|
|
epl_match += 1
|
|
|
|
|
else:
|
|
|
|
|
if getattr(row, 'predicted') == 0:
|
|
|
|
|
nepl_mismatch += 1
|
|
|
|
|
|
|
|
|
|
interpretability = (epl_match + nepl_mismatch) / len(predictions) # 可解释性
|
|
|
|
|
if is_explicable(row, md_list, sim_tensor_dict) and str(getattr(row, 'predicted')) == str(1):
|
|
|
|
|
epl_match += 1
|
|
|
|
|
|
|
|
|
|
df = predictions[predictions['predicted'] == str(1)]
|
|
|
|
|
interpretability = epl_match / len(df) # 可解释性
|
|
|
|
|
if indicators["block_recall"] >= 0.8:
|
|
|
|
|
f1 = indicators["F1"]
|
|
|
|
|
else:
|
|
|
|
|
f1 = (2.0 * indicators["precision"] * indicators["block_recall"]) / (indicators["precision"] + indicators["block_recall"])
|
|
|
|
|
performance = interpre_weight * interpretability + (1 - interpre_weight) * f1
|
|
|
|
|
################################################################################################################
|
|
|
|
|
process_prediction_for_md_discovery(predictions)
|
|
|
|
|
|
|
|
|
|
output_path = er_output_dir + "eval_result_" + str(iter_round) + ".txt"
|
|
|
|
|
with open(output_path, 'w') as f:
|
|
|
|
@ -313,4 +316,20 @@ def ml_er(iter_round: int, config: Configuration = None, ):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
ml_er(1)
|
|
|
|
|
iterations = 1
|
|
|
|
|
filename_list = os.listdir(er_output_dir)
|
|
|
|
|
if len(filename_list) > 0:
|
|
|
|
|
for _ in filename_list:
|
|
|
|
|
if _.startswith('eval_result'):
|
|
|
|
|
iterations = int(_[12:13]) + 1
|
|
|
|
|
|
|
|
|
|
if iterations > 1:
|
|
|
|
|
incumbent_array = np.load(hpo_output_dir + 'incumbent.npy')
|
|
|
|
|
with open(hpo_output_dir + "configspace.json", 'r') as f:
|
|
|
|
|
dict_configspace = json.load(f)
|
|
|
|
|
str_configspace = json.dumps(dict_configspace)
|
|
|
|
|
configspace = csj.read(str_configspace)
|
|
|
|
|
configuration = ConfigSpace.Configuration(configspace, vector=incumbent_array)
|
|
|
|
|
ml_er(iterations, configuration)
|
|
|
|
|
else:
|
|
|
|
|
ml_er(1)
|
|
|
|
|