Files
2026-07-13 12:37:18 +08:00

236 lines
8.8 KiB
Python

from collections import Counter
from typing import Union, List, Callable, Tuple
import torch
import penman
from penman import Graph
from hanlp.common.dataset import TransformableDataset
from hanlp.components.amr.seq2seq.dataset.IO import read_raw_amr_data
from hanlp.components.amr.seq2seq.dataset.penman import role_is_reverted
from hanlp.components.amr.seq2seq.dataset.tokenization_bart import PENMANBartTokenizer
from phrasetree.tree import Tree
import json
from hanlp_common.constant import BOS, EOS, ROOT
from hanlp_common.io import load_pickle
class AMRDataset(TransformableDataset):
def __init__(self,
data: Union[str, List],
use_recategorization=False,
remove_wiki=False,
dereify=False,
transform: Union[Callable, List] = None,
cache=None,
generate_idx=None) -> None:
self.dereify = dereify
self.remove_wiki = remove_wiki
self.use_recategorization = use_recategorization
super().__init__(data, transform, cache, generate_idx)
def load_file(self, filepath: str):
graphs = read_raw_amr_data([filepath], self.use_recategorization, remove_wiki=self.remove_wiki,
dereify=self.dereify)
for g in graphs:
yield {'amr': g}
def get_roles(self):
roles = Counter()
for sample in self.data:
g: Graph = sample['amr']
for s, r, t in g.triples:
if role_is_reverted(r):
r = r[:-3]
roles[r] += 1
return roles
def get_frames(self):
frames = Counter()
for sample in self.data:
g: Graph = sample['amr']
for i in g.instances():
t = i.target
cells = t.split('-')
if len(cells) == 2 and len(cells[1]) == 2 and cells[1].isdigit():
frames[t] += 1
return frames
class AMRPickleDataset(AMRDataset):
def load_file(self, filepath: str):
items = torch.load(filepath)
for each in items:
each['amr'] = penman.decode(each['amr'])
yield each
def dfs_linearize_tokenize(sample: dict, tokenizer: PENMANBartTokenizer, remove_space=False, text_key='snt') -> dict:
amr = sample.get('amr', None)
if amr:
l, e = tokenizer.linearize(amr)
sample['graph_tokens'] = e['linearized_graphs']
sample['graph_token_ids'] = l
text = amr.metadata[text_key]
else:
text = sample['text']
if remove_space:
text = ''.join(text.split())
sample['text'] = text
sample['text_token_ids'] = tokenizer.encode(text)
return sample
def dfs_linearize_levi(sample: dict, tokenizer: PENMANBartTokenizer, remove_space=False) -> dict:
amr = sample.get('amr', None)
if amr:
l, e = tokenizer.linearize(amr)
sample['graph_tokens'] = e['linearized_graphs']
sample['graph_token_ids'] = l
tok = json.loads(amr.metadata['tok'])
dep = json.loads(amr.metadata['dep'])
levi = dep_to_levi(tok, dep)
sample['text'] = ' '.join(levi)
# ids = sum(tokenizer.batch_encode_plus([' ' + x for x in levi], add_special_tokens=False).input_ids, [])
ids = []
idx = 0
for t in levi:
if t in ('(', ')'):
ids.append(tokenizer.convert_tokens_to_ids(tokenizer.INIT + t))
else:
if idx % 2:
ids.extend(tokenizer.encode(t, add_special_tokens=False))
else:
ids.append(tokenizer.convert_tokens_to_ids(tokenizer.INIT + t))
idx += 1
sample['text_token_ids'] = [tokenizer.bos_token_id] + ids + [tokenizer.eos_token_id]
return sample
def dfs_linearize_rgcn(sample: dict, tokenizer: PENMANBartTokenizer) -> dict:
amr = sample.get('amr', None)
if amr:
l, e = tokenizer.linearize(amr)
sample['graph_tokens'] = e['linearized_graphs']
sample['graph_token_ids'] = l
tok = sample['tok']
sample['text'] = [tokenizer.cls_token] + [' ' + x for x in tok]
arc_scores = sample['dep']['scores']['arc_scores']
rel_scores = sample['dep']['scores']['rel_scores']
dep_graph = arc_scores[:, :, None] * rel_scores
root = torch.zeros((1,) + dep_graph.shape[1:])
sample['dep_graph'] = torch.cat([root, dep_graph], dim=0)
return sample
def dfs_linearize_constituency(sample: dict, tokenizer: PENMANBartTokenizer, remove_space=False) -> dict:
amr = sample.get('amr', None)
if amr:
l, e = tokenizer.linearize(amr)
sample['graph_tokens'] = e['linearized_graphs']
sample['graph_token_ids'] = l
tree = Tree.from_list(json.loads(sample['amr'].metadata['con_list']))
for each in tree.subtrees(lambda x: x.height() == 2):
if each[0] == '(':
each[0] = '<LBR>'
elif each[0] == ')':
each[0] = '<RBR>'
text = tree.pformat(margin=10e7)
tokens = []
buffer = []
for c in text:
if c == '(' or c == ')':
tokens.append(''.join(buffer))
tokens.append(c)
buffer.clear()
continue
buffer.append(c)
if buffer:
tokens.append(''.join(buffer))
tokens = [x.strip() for x in tokens]
tokens = [x for x in tokens if x]
restore_bracket = {'<LBR>': '(', '<RBR>': ')'}
tokens = [restore_bracket.get(x, x) for x in tokens]
ids = []
for each in tokens:
pairs = each.split(' ', 1)
if len(pairs) == 2:
con, token = pairs
ids.append(tokenizer.convert_tokens_to_ids(tokenizer.INIT + con))
ids.extend(tokenizer.encode(token, add_special_tokens=False))
else:
ids.append(tokenizer.convert_tokens_to_ids(tokenizer.INIT + each))
if remove_space:
text = ''.join(text.split())
sample['text'] = text
sample['text_token_ids'] = [tokenizer.bos_token_id] + ids + [tokenizer.eos_token_id]
return sample
def dfs_linearize_tokenize_with_linguistic_structures(sample: dict, tokenizer: PENMANBartTokenizer,
remove_space=False,
text_key='snt') -> dict:
amr = sample.get('amr', None)
if amr:
l, e = tokenizer.linearize(amr)
sample['graph_tokens'] = e['linearized_graphs']
sample['graph_token_ids'] = l
text = amr.metadata[text_key]
if remove_space:
text = ''.join(text.split())
sample['text'] = text
tok = json.loads(amr.metadata['tok'])
text_token_ids = tokenizer.batch_encode_plus(tok, add_special_tokens=False).input_ids
sample['text_token_ids'] = [tokenizer.bos_token_id] + sum(text_token_ids, []) + [tokenizer.eos_token_id]
pos = amr.metadata.get('pos', None)
if pos:
flat_pos = []
pos = json.loads(pos)
for subtokens, tag in zip(text_token_ids, pos):
flat_pos.extend([tag] * len(subtokens))
sample['pos'] = [BOS] + flat_pos + [EOS]
ner = amr.metadata.get('ner', None)
if ner is not None:
flat_ner = []
ner_spans = json.loads(ner)
ner = ['O'] * len(text_token_ids)
for form, tag, start, end in ner_spans:
ner[start:end] = [tag] * (end - start)
for subtokens, tag in zip(text_token_ids, ner):
flat_ner.extend([tag] * len(subtokens))
sample['ner'] = [BOS] + flat_ner + [EOS]
dep = amr.metadata.get('dep', None)
if dep:
token_to_1st_subtoken = [0]
num_subtokens = 1 # 1 for BOS
for subtokens in text_token_ids:
token_to_1st_subtoken.append(num_subtokens)
num_subtokens += len(subtokens)
flat_arc, flat_rel = [0], [BOS]
dep = json.loads(dep)
for subtokens, (arc, rel) in zip(text_token_ids, dep):
flat_arc.extend([token_to_1st_subtoken[arc]] * len(subtokens))
flat_rel.extend([rel] * len(subtokens))
sample['dep_arc'] = flat_arc + [0]
sample['dep_rel'] = flat_rel + [EOS]
return sample
def dep_to_levi(tok: List[str], dep: List[Tuple[int, str]]):
root = [i for i, x in enumerate(dep) if x[0] == 0][0]
seq = []
dfs(tok, dep, root, seq)
return seq
def dfs(tok: List[str], dep: List[Tuple[int, str]], s, seq):
seq.append(dep[s][1])
seq.append(tok[s])
children = [i for i, x in enumerate(dep) if x[0] == s + 1]
if children:
seq.append('(')
for child in children:
dfs(tok, dep, child, seq)
seq.append(')')