| import gradio as gr |
| from collections import defaultdict |
| from transformers import BertTokenizer, BertForMaskedLM |
| import jsonlines |
| import torch |
| from src.modeling_bert import EXBertForMaskedLM |
| from higher.patch import monkeypatch as make_functional |
|
|
| |
| edit_origin_model = BertForMaskedLM.from_pretrained(pretrained_model_name_or_path="ChancesYuan/KGEditor_Edit_Test") |
| edit_ex_model = EXBertForMaskedLM.from_pretrained(pretrained_model_name_or_path="ChancesYuan/KGEditor_Edit_Test") |
|
|
| edit_learner = torch.load("./learner_checkpoint/edit/learner_params.pt", map_location=torch.device('cpu')) |
| add_learner = torch.load("./learner_checkpoint/add/learner_params.pt", map_location=torch.device('cpu')) |
|
|
| add_origin_model = BertForMaskedLM.from_pretrained(pretrained_model_name_or_path="ChancesYuan/KGEditor_Add_Test") |
| add_ex_model = EXBertForMaskedLM.from_pretrained(pretrained_model_name_or_path="ChancesYuan/KGEditor_Add_Test") |
|
|
| |
| ent_name2id = defaultdict(str) |
| id2ent_name = defaultdict(str) |
| rel_name2id = defaultdict(str) |
| id2ent_text = defaultdict(str) |
| id2rel_text = defaultdict(str) |
|
|
| |
| tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') |
| add_tokenizer = BertTokenizer.from_pretrained(pretrained_model_name_or_path='zjunlp/KGEditor', subfolder="E-FB15k237") |
|
|
| def init_triple_input(): |
| global ent2token |
| global ent2id |
| global id2ent |
| global rel2token |
| global rel2id |
|
|
| with open("./dataset/fb15k237/relations.txt", "r") as f: |
| lines = f.readlines() |
| relations = [] |
| for line in lines: |
| relations.append(line.strip().split('\t')[0]) |
|
|
| rel2token = {ent: f"[RELATION_{i}]" for i, ent in enumerate(relations)} |
| |
| with open("./dataset/fb15k237/entity2text.txt", "r") as f: |
| for line in f.readlines(): |
| id, name = line.rstrip('\n').split('\t') |
| ent_name2id[name] = id |
| id2ent_name[id] = name |
|
|
| with open("./dataset/fb15k237/relation2text.txt", "r") as f: |
| for line in f.readlines(): |
| id, name = line.rstrip('\n').split('\t') |
| rel_name2id[name] = id |
| id2rel_text[id] = name |
|
|
| with open("./dataset/fb15k237/entity2textlong.txt", "r") as f: |
| for line in f.readlines(): |
| id, text = line.rstrip('\n').split('\t') |
| id2ent_text[id] = text.replace("\\n", " ").replace("\\", "") |
|
|
| entities = list(id2ent_text.keys()) |
| ent2token = {ent: f"[ENTITY_{i}]" for i, ent in enumerate(entities)} |
| ent2id = {ent: i for i, ent in enumerate(entities)} |
| id2ent = {i: ent for i, ent in enumerate(entities)} |
|
|
| rel2id = { |
| w: i + len(entities) |
| for i, w in enumerate(rel2token.keys()) |
| } |
|
|
| def solve(triple, alter_label, edit_task): |
| print(triple, alter_label) |
| h, r, t = triple.split("|") |
| if h == "[MASK]": |
| text_a = "[MASK]" |
| text_b = id2rel_text[r] + " " + rel2token[r] |
| text_c = ent2token[ent_name2id[t]] + " " + id2ent_text[ent_name2id[t]] |
| replace_token = [rel2id[r], ent2id[ent_name2id[t]]] |
| else: |
| text_a = ent2token[ent_name2id[h]] |
| text_b = id2rel_text[r] + " " + rel2token[r] |
| text_c = "[MASK]" + " " + id2ent_text[ent_name2id[h]] |
| replace_token = [ent2id[ent_name2id[h]], rel2id[r]] |
|
|
| if text_a == "[MASK]": |
| input_text_a = tokenizer.sep_token.join(["[MASK]", id2rel_text[r] + "[PAD]"]) |
| input_text_b = "[PAD]" + " " + id2ent_text[ent_name2id[t]] |
| else: |
| input_text_a = "[PAD] " |
| input_text_b = tokenizer.sep_token.join([id2rel_text[r] + "[PAD]", "[MASK]" + " " + id2ent_text[ent_name2id[h]]]) |
|
|
| inputs = tokenizer( |
| f"{text_a} [SEP] {text_b} [SEP] {text_c}", |
| truncation="longest_first", |
| max_length=64, |
| padding="longest", |
| add_special_tokens=True, |
| ) |
|
|
| edit_inputs = tokenizer( |
| input_text_a, |
| input_text_b, |
| truncation="longest_first", |
| max_length=64, |
| padding="longest", |
| add_special_tokens=True, |
| ) |
|
|
| inputs = { |
| "input_ids": torch.tensor(inputs["input_ids"]).unsqueeze(dim=0), |
| "attention_mask": torch.tensor(inputs["attention_mask"]).unsqueeze(dim=0), |
| "token_type_ids": torch.tensor(inputs["token_type_ids"]).unsqueeze(dim=0) |
| } |
|
|
| edit_inputs = { |
| "input_ids": torch.tensor(edit_inputs["input_ids"]).unsqueeze(dim=0), |
| "attention_mask": torch.tensor(edit_inputs["attention_mask"]).unsqueeze(dim=0), |
| "token_type_ids": torch.tensor(edit_inputs["token_type_ids"]).unsqueeze(dim=0) |
| } |
|
|
| _, mask_idx = (inputs["input_ids"] == tokenizer.mask_token_id).nonzero(as_tuple=True) |
| logits = edit_origin_model(**inputs).logits[:, :, 30522:45473].squeeze() if edit_task else add_origin_model(**inputs).logits[:, :, 30522:45473].squeeze() |
| logits = logits[mask_idx, :] |
|
|
| |
| _, origin_entity_order = torch.sort(logits, dim=1, descending=True) |
| origin_entity_order = origin_entity_order.squeeze(dim=0) |
| origin_top3 = [id2ent_name[id2ent[origin_entity_order[i].item()]] for i in range(3)] |
|
|
| origin_label = origin_top3[0] if edit_task else alter_label |
|
|
| cond_inputs_text = "{} >> {} || {}".format( |
| add_tokenizer.added_tokens_decoder[ent2id[ent_name2id[origin_label]] + len(tokenizer)], |
| add_tokenizer.added_tokens_decoder[ent2id[ent_name2id[alter_label]] + len(tokenizer)], |
| input_text_a + input_text_b |
| ) |
|
|
| cond_inputs = tokenizer( |
| cond_inputs_text, |
| truncation=True, |
| max_length=64, |
| padding="max_length", |
| add_special_tokens=True, |
| ) |
|
|
| cond_inputs = { |
| "input_ids": torch.tensor(cond_inputs["input_ids"]).unsqueeze(dim=0), |
| "attention_mask": torch.tensor(cond_inputs["attention_mask"]).unsqueeze(dim=0), |
| "token_type_ids": torch.tensor(cond_inputs["token_type_ids"]).unsqueeze(dim=0) |
| } |
|
|
| flag = 0 |
| for idx, i in enumerate(edit_inputs["input_ids"][0, :].tolist()): |
| if i == tokenizer.pad_token_id and flag == 0: |
| edit_inputs["input_ids"][0, idx] = replace_token[0] + 30522 |
| flag = 1 |
| elif i == tokenizer.pad_token_id and flag != 0: |
| edit_inputs["input_ids"][0, idx] = replace_token[1] + 30522 |
|
|
| return inputs, cond_inputs, edit_inputs, origin_top3 |
|
|
| def get_logits_orig_params_dict(inputs, cond_inputs, alter_label, ex_model, learner): |
| with torch.enable_grad(): |
| logits = ex_model.eval()( |
| input_ids=inputs["input_ids"], |
| attention_mask=inputs["attention_mask"], |
| ).logits |
|
|
| input_ids = inputs['input_ids'] |
| _, mask_idx = (input_ids == tokenizer.mask_token_id).nonzero(as_tuple=True) |
| mask_logits = logits[:, mask_idx, 30522:45473].squeeze(dim=0) |
| |
| grads = torch.autograd.grad( |
| |
| torch.nn.functional.cross_entropy( |
| mask_logits[-1:, :], |
| torch.tensor([alter_label]), |
| reduction="none", |
| ).mean(-1), |
| ex_model.parameters(), |
| ) |
|
|
| grads = { |
| name: grad |
| for (name, _), grad in zip(ex_model.named_parameters(), grads) |
| } |
|
|
| params_dict = learner( |
| cond_inputs["input_ids"][-1:], |
| cond_inputs["attention_mask"][-1:], |
| grads=grads, |
| ) |
|
|
| return params_dict |
|
|
| def edit_process(edit_input, alter_label): |
| try: |
| _, cond_inputs, edit_inputs, origin_top3 = solve(edit_input, alter_label, edit_task=True) |
| except KeyError: |
| return "The entity or relationship you entered is not in the vocabulary. Please check it carefully.", "" |
|
|
| |
| fmodel = make_functional(edit_ex_model).eval() |
| params_dict = get_logits_orig_params_dict(edit_inputs, cond_inputs, ent2id[ent_name2id[alter_label]], edit_ex_model, edit_learner) |
| edit_logits = fmodel( |
| input_ids=edit_inputs["input_ids"], |
| attention_mask=edit_inputs["attention_mask"], |
| |
| params=[ |
| params_dict.get(n, 0) + p |
| for n, p in edit_ex_model.named_parameters() |
| ], |
| ).logits[:, :, 30522:45473].squeeze() |
|
|
| _, mask_idx = (edit_inputs["input_ids"] == tokenizer.mask_token_id).nonzero(as_tuple=True) |
| edit_logits = edit_logits[mask_idx, :] |
| _, edit_entity_order = torch.sort(edit_logits, dim=1, descending=True) |
| edit_entity_order = edit_entity_order.squeeze(dim=0) |
| edit_top3 = [id2ent_name[id2ent[edit_entity_order[i].item()]] for i in range(3)] |
|
|
| return "\n".join(origin_top3), "\n".join(edit_top3) |
|
|
| def add_process(edit_input, alter_label): |
| try: |
| _, cond_inputs, add_inputs, origin_top3 = solve(edit_input, alter_label, edit_task=False) |
| except: |
| return "The entity or relationship you entered is not in the vocabulary. Please check it carefully.", "" |
|
|
| |
| fmodel = make_functional(add_ex_model).eval() |
| params_dict = get_logits_orig_params_dict(add_inputs, cond_inputs, ent2id[ent_name2id[alter_label]], add_ex_model, add_learner) |
| add_logits = fmodel( |
| input_ids=add_inputs["input_ids"], |
| attention_mask=add_inputs["attention_mask"], |
| |
| params=[ |
| params_dict.get(n, 0) + p |
| for n, p in add_ex_model.named_parameters() |
| ], |
| ).logits[:, :, 30522:45473].squeeze() |
| |
| _, mask_idx = (add_inputs["input_ids"] == tokenizer.mask_token_id).nonzero(as_tuple=True) |
| add_logits = add_logits[mask_idx, :] |
| _, add_entity_order = torch.sort(add_logits, dim=1, descending=True) |
| add_entity_order = add_entity_order.squeeze(dim=0) |
| add_top3 = [id2ent_name[id2ent[add_entity_order[i].item()]] for i in range(3)] |
|
|
| return "\n".join(origin_top3), "\n".join(add_top3) |
|
|
|
|
| with gr.Blocks() as demo: |
| init_triple_input() |
| gr.Markdown("# KGE Editing") |
|
|
| |
| with gr.Tabs(): |
| with gr.TabItem("E-FB15k237"): |
| with gr.Row(): |
| with gr.Column(): |
| edit_input = gr.Textbox(label="Input", lines=1, placeholder=" Please enter in the format of: [MASK]|rel|tail or head|rel|[MASK].") |
|
|
| alter_label = gr.Textbox(label="Alter Entity", lines=1, placeholder="Entity Name") |
| edit_button = gr.Button("Edit") |
|
|
| with gr.Column(): |
| origin_output = gr.Textbox(label="Before Edit", lines=3, placeholder="") |
| edit_output = gr.Textbox(label="After Edit", lines=3, placeholder="") |
| |
| gr.Examples( |
| examples=[["[MASK]|/people/person/profession|Jack Black", "Kellie Martin"], |
| ["[MASK]|/people/person/nationality|United States of America", "Mark Mothersbaugh"], |
| ["[MASK]|/people/person/gender|Male", "Iggy Pop"], |
| ["Rachel Weisz|/people/person/nationality|[MASK]", "J.J. Abrams"], |
| ["Jeff Goldblum|/people/person/spouse_s./people/marriage/type_of_union|[MASK]", "Sydney Pollack"], |
| ], |
| inputs=[edit_input, alter_label], |
| outputs=[origin_output, edit_output], |
| fn=edit_process, |
| cache_examples=True, |
| ) |
|
|
| with gr.TabItem("A-FB15k237"): |
| with gr.Row(): |
| with gr.Column(): |
| add_input = gr.Textbox(label="Input", lines=1, placeholder="Brand new triple input") |
|
|
| inductive_entity = gr.Textbox(label="Inductive Entity", lines=1, placeholder="Entity Name") |
| add_button = gr.Button("Add") |
|
|
| with gr.Column(): |
| add_origin_output = gr.Textbox(label="Origin Results", lines=3, placeholder="") |
| add_output = gr.Textbox(label="Add Results", lines=3, placeholder="") |
|
|
| gr.Examples( |
| examples=[["Jane Wyman|/people/person/places_lived./people/place_lived/location|[MASK]", "Palm Springs"], |
| ["Darryl F. Zanuck|/people/deceased_person/place_of_death|[MASK]", "Palm Springs"], |
| ["[MASK]|/location/location/contains|Antigua and Barbuda", "Americas"], |
| ["Hard rock|/music/genre/artists|[MASK]", "Social Distortion"], |
| ["[MASK]|/people/person/nationality|United States of America", "Serj Tankian"] |
| ], |
| inputs=[add_input, inductive_entity], |
| outputs=[add_origin_output, add_output], |
| fn=add_process, |
| cache_examples=True, |
| ) |
|
|
| edit_button.click(fn=edit_process, inputs=[edit_input, alter_label], outputs=[origin_output, edit_output]) |
| add_button.click(fn=add_process, inputs=[add_input, inductive_entity], outputs=[add_origin_output, add_output]) |
|
|
| demo.launch() |