├── bert-base-uncased
├── tokenizer_config.json
├── pytorch_model.bin
└── config.json
├── DATASET
├── UWN18RR
│ └── relation_id.tsv
├── CN15k
│ ├── check.py
│ └── relations.txt
├── NL27K
│ ├── check.py
│ └── relations.txt
├── UFB15K237
│ └── relation_id.tsv
└── confidece_analyse.ipynb
├── transformer
├── Modules.py
├── Optim.py
├── Layers.py
├── SubLayers.py
├── dataset.py
├── Models.py
└── Translator.py
├── .gitignore
├── README.md
├── translate_train.py
├── translate_decode.py
├── confidence_prediction.py
└── LICENSE
/bert-base-uncased/tokenizer_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "do_lower_case": true
3 | }
4 |
--------------------------------------------------------------------------------
/bert-base-uncased/pytorch_model.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seucoin/UKRM/HEAD/bert-base-uncased/pytorch_model.bin
--------------------------------------------------------------------------------
/DATASET/UWN18RR/relation_id.tsv:
--------------------------------------------------------------------------------
1 | 0 _hypernym
2 | 1 _derivationally_related_form
3 | 2 _instance_hypernym
4 | 3 _also_see
5 | 4 _member_meronym
6 | 5 _synset_domain_topic_of
7 | 6 _has_part
8 | 7 _member_of_domain_usage
9 | 8 _member_of_domain_region
10 | 9 _verb_group
11 | 10 _similar_to
12 |
--------------------------------------------------------------------------------
/DATASET/CN15k/check.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | data = []
5 | with open('train.txt','r') as f:
6 | lines = f.readlines()
7 | for line in lines:
8 | data.append(eval(line.strip().split('\t')[-1]))
9 |
10 | # 求均值
11 | mean_value = np.mean(data)
12 | print("均值:", mean_value)
13 |
14 | # 求方差
15 | variance_value = np.var(data)
16 | print("方差:", variance_value)
17 |
--------------------------------------------------------------------------------
/DATASET/NL27K/check.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | data = []
5 | with open('train.txt','r') as f:
6 | lines = f.readlines()
7 | for line in lines:
8 | data.append(eval(line.strip().split('\t')[-1]))
9 |
10 | # 求均值
11 | mean_value = np.mean(data)
12 | print("均值:", mean_value)
13 |
14 | # 求方差
15 | variance_value = np.var(data)
16 | print("方差:", variance_value)
17 |
--------------------------------------------------------------------------------
/DATASET/CN15k/relations.txt:
--------------------------------------------------------------------------------
1 | relatedto
2 | hascontext
3 | isa
4 | synonym
5 | atlocation
6 | etymologicallyrelatedto
7 | distinctfrom
8 | haslastsubevent
9 | usedfor
10 | similarto
11 | desires
12 | antonym
13 | dbpedia
14 | partof
15 | formof
16 | hasa
17 | capableof
18 | instanceof
19 | hasprerequisite
20 | motivatedbygoal
21 | derivedfrom
22 | hassubevent
23 | causes
24 | receivesaction
25 | hasproperty
26 | entails
27 | hasfirstsubevent
28 | notdesires
29 | causesdesire
30 | madeof
31 | nothasproperty
32 | createdby
33 | locatednear
34 | notcapableof
35 | definedas
36 | mannerof
37 |
--------------------------------------------------------------------------------
/bert-base-uncased/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "architectures": [
3 | "BertForMaskedLM"
4 | ],
5 | "attention_probs_dropout_prob": 0.1,
6 | "gradient_checkpointing": false,
7 | "hidden_act": "gelu",
8 | "hidden_dropout_prob": 0.1,
9 | "hidden_size": 768,
10 | "initializer_range": 0.02,
11 | "intermediate_size": 3072,
12 | "layer_norm_eps": 1e-12,
13 | "max_position_embeddings": 512,
14 | "model_type": "bert",
15 | "num_attention_heads": 12,
16 | "num_hidden_layers": 12,
17 | "pad_token_id": 0,
18 | "position_embedding_type": "absolute",
19 | "transformers_version": "4.6.0.dev0",
20 | "type_vocab_size": 2,
21 | "use_cache": true,
22 | "vocab_size": 30522
23 | }
24 |
--------------------------------------------------------------------------------
/transformer/Modules.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 |
5 |
6 | class ScaledDotProductAttention(nn.Module):
7 | ''' Scaled Dot-Product Attention '''
8 |
9 | def __init__(self, temperature, attn_dropout=0.1):
10 | super().__init__()
11 | self.temperature = temperature
12 | self.dropout = nn.Dropout(attn_dropout)
13 |
14 | def forward(self, q, k, v, mask=None):
15 |
16 | attn = torch.matmul(q / self.temperature, k.transpose(2, 3))
17 |
18 | if mask is not None:
19 | attn = attn.masked_fill(mask == 0, -1e9)
20 | attn = self.dropout(F.softmax(attn, dim=-1))
21 |
22 | output = torch.matmul(attn, v)
23 |
24 | return output, attn
25 |
--------------------------------------------------------------------------------
/transformer/Optim.py:
--------------------------------------------------------------------------------
1 | '''A wrapper class for scheduled optimizer '''
2 | import numpy as np
3 |
4 |
5 | class ScheduledOptim():
6 | '''A simple wrapper class for learning rate scheduling'''
7 |
8 | def __init__(self, optimizer, lr_mul, d_model, n_warmup_steps):
9 | self._optimizer = optimizer
10 | self.lr_mul = lr_mul
11 | self.d_model = d_model
12 | self.n_warmup_steps = n_warmup_steps
13 | self.n_steps = 0
14 | self.lr = 0
15 |
16 |
17 | def step_and_update_lr(self):
18 | "Step with the inner optimizer"
19 | self._update_learning_rate()
20 | self._optimizer.step()
21 |
22 |
23 | def zero_grad(self):
24 | "Zero out the gradients with the inner optimizer"
25 | self._optimizer.zero_grad()
26 |
27 |
28 | def _get_lr_scale(self):
29 | d_model = self.d_model
30 | n_steps, n_warmup_steps = self.n_steps, self.n_warmup_steps
31 | return (d_model ** -0.5) * min(n_steps ** (-0.5), n_steps * n_warmup_steps ** (-1.5))
32 |
33 |
34 | def _update_learning_rate(self):
35 | ''' Learning rate scheduling per step '''
36 |
37 | self.n_steps += 1
38 | lr = self.lr_mul * self._get_lr_scale()
39 | self.lr = lr
40 | # lr = 1e-4
41 |
42 | for param_group in self._optimizer.param_groups:
43 | param_group['lr'] = lr
44 |
45 |
--------------------------------------------------------------------------------
/transformer/Layers.py:
--------------------------------------------------------------------------------
1 | ''' Define the Layers '''
2 | import torch.nn as nn
3 | import torch
4 | import torch.nn.functional as F
5 | from transformer.SubLayers import MultiHeadAttention, PositionwiseFeedForward
6 |
7 |
8 | class EncoderLayer(nn.Module):
9 | ''' Compose with two layers '''
10 |
11 | def __init__(self, d_model, d_inner, n_head, d_k, d_v, dropout=0.1):
12 | super(EncoderLayer, self).__init__()
13 | self.slf_attn = MultiHeadAttention(n_head, d_model, d_k, d_v, dropout=dropout)
14 | self.pos_ffn = PositionwiseFeedForward(d_model, d_inner, dropout=dropout)
15 |
16 | def forward(self, enc_input, slf_attn_mask=None):
17 | enc_output, enc_slf_attn = self.slf_attn(enc_input, enc_input, enc_input, mask=slf_attn_mask)
18 | enc_output = self.pos_ffn(enc_output)
19 | return enc_output, enc_slf_attn
20 |
21 |
22 | class DecoderLayer(nn.Module):
23 | ''' Compose with three layers '''
24 |
25 | def __init__(self, d_model, d_inner, n_head, d_k, d_v, dropout=0.1):
26 | super(DecoderLayer, self).__init__()
27 | self.slf_attn = MultiHeadAttention(n_head, d_model, d_k, d_v, dropout=dropout)
28 | self.enc_attn = MultiHeadAttention(n_head, d_model, d_k, d_v, dropout=dropout)
29 | self.pos_ffn = PositionwiseFeedForward(d_model, d_inner, dropout=dropout)
30 |
31 | def forward(self, dec_input, enc_output, slf_attn_mask=None, dec_enc_attn_mask=None):
32 | dec_enc_attn = None
33 | dec_output, dec_slf_attn = self.slf_attn(dec_input, dec_input, dec_input, mask=slf_attn_mask)
34 | dec_output, dec_enc_attn = self.enc_attn(dec_output, enc_output, enc_output, mask=dec_enc_attn_mask)
35 | dec_output = self.pos_ffn(dec_output)
36 | return dec_output, dec_slf_attn, dec_enc_attn
37 |
--------------------------------------------------------------------------------
/transformer/SubLayers.py:
--------------------------------------------------------------------------------
1 | ''' Define the sublayers in encoder/decoder layer '''
2 | import torch, time
3 | import numpy as np
4 | import torch.nn as nn
5 | import torch.nn.functional as F
6 | from transformer.Modules import ScaledDotProductAttention
7 |
8 |
9 | class MultiHeadAttention(nn.Module):
10 | ''' Multi-Head Attention module '''
11 |
12 | def __init__(self, n_head, d_model, d_k, d_v, dropout=0.1):
13 | super().__init__()
14 |
15 | self.n_head = n_head
16 | self.d_k = d_k
17 | self.d_v = d_v
18 |
19 | self.w_qs = nn.Linear(d_model, n_head * d_k, bias=False)
20 | self.w_ks = nn.Linear(d_model, n_head * d_k, bias=False)
21 | self.w_vs = nn.Linear(d_model, n_head * d_v, bias=False)
22 | self.fc = nn.Linear(n_head * d_v, d_model, bias=False)
23 |
24 | self.attention = ScaledDotProductAttention(temperature=d_k ** 0.5)
25 |
26 | self.dropout = nn.Dropout(dropout)
27 | self.layer_norm = nn.LayerNorm(d_model, eps=1e-6)
28 |
29 |
30 | def forward(self, q, k, v, mask=None):
31 | d_k, d_v, n_head = self.d_k, self.d_v, self.n_head
32 | sz_b, len_q, len_k, len_v = q.size(0), q.size(1), k.size(1), v.size(1)
33 |
34 | residual = q
35 |
36 | # Pass through the pre-attention projection: b x lq x (n*dv)
37 | # Separate different heads: b x lq x n x dv
38 | q = self.w_qs(q).view(sz_b, len_q, n_head, d_k)
39 | k = self.w_ks(k).view(sz_b, len_k, n_head, d_k)
40 | v = self.w_vs(v).view(sz_b, len_v, n_head, d_v)
41 |
42 | # Transpose for attention dot product: b x n x lq x dv
43 | q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)
44 |
45 | if mask is not None:
46 | mask = mask.unsqueeze(1) # For head axis broadcasting.
47 |
48 | q, attn = self.attention(q, k, v, mask=mask)
49 |
50 | # Transpose to move the head dimension back: b x lq x n x dv
51 | # Combine the last two dimensions to concatenate all the heads together: b x lq x (n*dv)
52 | q = q.transpose(1, 2).contiguous().view(sz_b, len_q, -1)
53 | q = self.dropout(self.fc(q))
54 | q += residual
55 |
56 | q = self.layer_norm(q)
57 |
58 | return q, attn
59 |
60 |
61 | class PositionwiseFeedForward(nn.Module):
62 | ''' A two-feed-forward-layer module '''
63 |
64 | def __init__(self, d_in, d_hid, dropout=0.1):
65 | super().__init__()
66 | self.w_1 = nn.Linear(d_in, d_hid) # position-wise
67 | self.w_2 = nn.Linear(d_hid, d_in) # position-wise
68 | self.layer_norm = nn.LayerNorm(d_in, eps=1e-6)
69 | self.dropout = nn.Dropout(dropout)
70 |
71 | def forward(self, x):
72 |
73 | residual = x
74 |
75 | x = self.w_2(F.relu(self.w_1(x)))
76 | x = self.dropout(x)
77 | x += residual
78 |
79 | x = self.layer_norm(x)
80 |
81 | return x
82 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
161 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Uncertain Knowledge Graph Completion with Rule Mining
2 | Source code for WISA-2024 paper: Uncertain Knowledge Graph Completion with Rule Mining.
3 |
4 | Since KGs usually suffer from the problem of incompleteness, methods of rule mining and reasoning for knowledge graph completion are extensively studied due to their excellent interpretability. However, previous methods are all conducted under deterministic scenarios, neglecting the uncertainty of knowledge and making them unable to be directly applied to UKGs. In this paper, we propose a new framework on uncertain knowledge graph completion with rule mining. Our framework contains the following components: 1)**The Rule Mining Model** applies an encoder-decoder network transformer to take rule mining as a sequence-to-sequence task to generate rules. It models the uncertainty in UKGs and infers new triples by differentiable reasoning based on TensorLog with mined rules. 2)**The Confidence Prediction Model** uses a pre-trained language model to predict the triple confidence given the rules mined.
5 |
6 |
7 |
8 |
9 |
10 | ## Requirements
11 | **Step1** Create a virtual environment using `Anaconda` and enter it.
12 |
13 | **Step2** Installing the following packages in the virtual environment:
14 | ```
15 | pytorch == 2.1.1
16 | transformers == 4.38.2
17 | wandb == 0.16.1
18 | ```
19 |
20 | ## Datasets
21 |
22 | We adopt CN15K and NL27K datasets to evaluate our models, UKRM and BCP.
23 |
24 | | Dataset | #Entities | #Relations | #Train | #Valid | #Test |
25 | | --------- | ---------- | ---------- | -------- | ------ | ------ |
26 | | CN15K | 15,000 | 36 | 204,984 | 16,881 | 19,293 |
27 | | NL27K | 27,221 | 404 | 149,001 | 12,278 | 14,034 |
28 |
29 | ## Files
30 | - `bert-base-uncased` folder contains the BERT model downloaded from huggingface(https://huggingface.co/google-bert/bert-base-uncased) and it will be used in the confidence prediction model.
31 | - `transformer` folder contains source codes for the rule mining model on uncertain knowledge graph (UKRM).
32 | - `confidence_prediction.py` is the source code for confidence prediction model (BCP).
33 | - `DATASET` folder contains datasets we used in our paper.
34 | - `decode_rules` folder contains input preprocessed for the confidence prediction model. GLM-4 is used in the process so it is a little time-consuming and we offer the data can be used directly.
35 |
36 | ## Usage
37 | To train the rule mining model, please run follow instruction:
38 | ```bash
39 | python translate_train.py
40 | ```
41 | To decode rules from the rule mining model, please run follow instruction:
42 | ```bash
43 | python translate_decode.py
44 | ```
45 | To run the confidence prediction model, please run follow instruction:
46 | ```bash
47 | python confidence_predcition.py
48 | ```
49 |
50 |
51 | ## Argument Descriptions
52 |
53 | Here are explanations of some important args,
54 |
55 | ```bash
56 | --data_path: "path of knowledge graph"
57 | --batch_size: "batch size"
58 | --d_word_vec: "dimension of word vector"
59 | --d_model: "dimension of model (usually same with d_word_vec)"
60 | --d_inner: "dimension of feed forward layer"
61 | --n_layers: "num of layers of both encoder and decoder"
62 | --n_head: "num of attention heads (needs to ensure that d_k*n_head == d_model)"
63 | --d_k: "dimension of attention vector k"
64 | --d_v: "dimension of attention vector v (usually same with d_k)"
65 | --dropout: "probability of dropout"
66 | --n_position: "number of positions"
67 | --lr_mul: "learning rate multiplier"
68 | --n_warmup_steps: "num of warmup steps"
69 | --num_epoch: "num of epochs"
70 | --save_step: "steps to save"
71 | --decode_rule: "decode_rule mode"
72 | ```
73 |
74 | Configs are set in python files and in case you want to modify them. Normally, other args can be set to default values.
75 |
76 |
77 | ## Citation
78 | Please cite our paper if you use UKRM in your work.
79 | ```
80 | @inproceedings{chen2024uncertain,
81 | title={Uncertain Knowledge Graph Completion with Rule Mining},
82 | author={Chen, Yilin and Wu, Tianxing and Liu, Yunchang and Wang, Yuxiang and Qi, Guilin},
83 | booktitle={International Conference on Web Information Systems and Applications},
84 | pages={100--112},
85 | year={2024},
86 | organization={Springer}
87 | }
88 | ```
89 |
--------------------------------------------------------------------------------
/transformer/dataset.py:
--------------------------------------------------------------------------------
1 | import torch
2 | from collections import defaultdict
3 | import pickle
4 | from torch.utils.data import Dataset
5 | from torch.utils.data import DataLoader
6 | from transformers import AutoTokenizer
7 | import torch.nn.functional as F
8 |
9 |
10 | # 需要先将图转为id,然后加载到数据集,之后输入给模型,进行debug
11 | class KnowledgeGraph:
12 | def __init__(self, data_path):
13 | with open(f"{data_path}/entities.txt") as e, open(
14 | f"{data_path}/relations.txt"
15 | ) as r:
16 | # 转id
17 | self.ents = [x.strip() for x in e.readlines()]
18 | self.rels = [x.strip() for x in r.readlines()]
19 | self.pos_rels = len(self.rels)
20 | self.rels += ["inv_" + x for x in self.rels] + [""]
21 | self.e2id = {self.ents[i]: i for i in range(len(self.ents))}
22 | self.r2id = {self.rels[i]: i for i in range(len(self.rels))}
23 | self.id2r = {i: self.rels[i] for i in range(len(self.rels))}
24 | self.id2e = {i: self.ents[i] for i in range(len(self.ents))}
25 |
26 | # id四元组
27 | self.data = {}
28 | with open(f"{data_path}/train.txt") as f:
29 | train = [item.strip().split("\t") for item in f.readlines()]
30 | self.data["train"] = list( { (self.e2id[h], self.r2id[r], self.e2id[t], eval(s)) for h, r, t, s in train } )
31 | with open(f"{data_path}/test.txt") as f:
32 | test = [item.strip().split("\t") for item in f.readlines()]
33 | self.data["test"] = list( { (self.e2id[h], self.r2id[r], self.e2id[t], eval(s)) for h, r, t, s in test } )
34 | with open(f"{data_path}/valid.txt") as f:
35 | valid = [item.strip().split("\t") for item in f.readlines()]
36 | self.data["valid"] = list( { (self.e2id[h], self.r2id[r], self.e2id[t], eval(s)) for h, r, t, s in valid } )
37 |
38 | self.fact = defaultdict(dict) # 二重字典,h,r双重键,值是(尾实体,置信度)元组构成的列表
39 | for h, r, t,s in self.data['train']:
40 | try:
41 | self.fact[h][r].add((t,s))
42 | except KeyError:
43 | self.fact[h][r] = set([(t,s)])
44 | try:
45 | self.fact[t][r+self.pos_rels].add((h,s))
46 | except KeyError:
47 | self.fact[t][r+self.pos_rels] = set([(h,s)])
48 | for h in self.fact:
49 | self.fact[h] = {r:list(ts) for r,ts in self.fact[h].items()}
50 |
51 | self.neighbors = defaultdict(dict) # 二重字典,h,r双重键,值是尾实体列表呗
52 | for h, r, t,s in self.data['train']:
53 | try:
54 | self.neighbors[h][r].add(t)
55 | except KeyError:
56 | self.neighbors[h][r] = set([t])
57 | try:
58 | self.neighbors[t][r+self.pos_rels].add(h)
59 | except KeyError:
60 | self.neighbors[t][r+self.pos_rels] = set([h])
61 | for h in self.neighbors:
62 | self.neighbors[h] = {r:list(ts) for r,ts in self.neighbors[h].items()}
63 |
64 |
65 | # 获取稀疏矩阵
66 | sparse = data_path + "/sparse.pkl"
67 | try:
68 | with open(sparse, "rb") as db:
69 | self.relations = pickle.load(db)
70 | except:
71 | indices = [[] for _ in range(self.pos_rels)] # train graph转换为稀疏矩阵,为什么只用正关系?
72 | values = [[] for _ in range(self.pos_rels)]
73 | no_repeat = defaultdict(set)
74 | for h, r, t, s in self.data["train"]:
75 | if (h, t) not in no_repeat[r]:
76 | indices[r].append((h, t)) # 表示位置,即行列
77 | values[r].append(s) # 表示值,即行列位置上的元素,这里都是置信度
78 | no_repeat[r].add((h, t))
79 |
80 | for i in range(self.pos_rels):
81 | if indices[i] == []:
82 | indices[i].append((0,0))
83 | values[i].append(0)
84 |
85 | indices = [torch.LongTensor(x).T for x in indices]
86 | values = [torch.FloatTensor(x) for x in values]
87 | size = torch.Size([len(self.ents), len(self.ents)])
88 | self.relations = [
89 | torch.sparse.FloatTensor(indices[i], values[i], size).coalesce()
90 | for i in range(self.pos_rels)
91 | ]
92 | # with open(f"{sparse}", "wb") as db:
93 | # pickle.dump(self.relations, db)
94 |
95 | # 所有三元组,包括train,test,valid,以及rev三元组,(头实体,关系)的tuple作为键,尾实体集合作为值存储在filtered_dict字典中
96 | self.filtered_dict = defaultdict(set)
97 | triplets = self.data["train"] + self.data["valid"] + self.data["test"]
98 | for triplet in triplets:
99 | self.filtered_dict[(triplet[0], triplet[1])].add(triplet[2])
100 | self.filtered_dict[(triplet[2], triplet[1] + self.pos_rels)].add(triplet[0])
101 |
102 |
103 | class MyDataset(Dataset):
104 | def __init__(self, kg, mode):
105 | self.kg = kg
106 | self.triples = kg.data[mode]
107 |
108 | def __len__(self):
109 | return len(self.triples)
110 |
111 | def __getitem__(self, index):
112 | h, r, t, s = self.triples[index]
113 | return h, r, t, s
114 |
115 | @staticmethod
116 | def collate_fn(data):
117 | query = torch.stack([torch.tensor([d[0], d[1]]) for d in data], dim=0)
118 | t = torch.stack([torch.tensor(d[2]) for d in data], dim=0)
119 | s = torch.stack([torch.tensor(d[3]) for d in data], dim=0)
120 | return query, t, s
121 |
122 |
123 | if __name__ == "__main__":
124 | # 测试代码
125 | data_path = "/data/cyl/MyPaper/MyCodes/DATASETS/CN15k"
126 | kg = KnowledgeGraph(data_path)
127 | train_set = MyDataset(kg, "train")
128 | train_dataloader = DataLoader(
129 | train_set, batch_size=5, collate_fn=MyDataset.collate_fn
130 | )
131 | for batch in train_dataloader:
132 | print(batch)
133 | break
134 |
--------------------------------------------------------------------------------
/translate_train.py:
--------------------------------------------------------------------------------
1 | """ Translate input text with trained model. """
2 |
3 | import torch
4 | import argparse, os, time
5 |
6 | from transformer.Models import Transformer
7 | from transformer.Translator import Translator
8 | from transformer.dataset import MyDataset, KnowledgeGraph
9 | from transformer.Optim import ScheduledOptim
10 | import torch.optim as optim
11 | import numpy as np
12 | import random
13 | import wandb
14 | import os
15 | from datetime import datetime
16 |
17 | # os.environ['WANDB_DISABLED'] = 'true'
18 |
19 |
20 | def hit_mrr(hits, starttime):
21 | return (
22 | "MRR:{:.5f} @1:{:.5f} @3:{:.5f} @10:{:.5f} LOS:{:.5f} Time:{:.1f}secs".format(
23 | hits[10] / hits[12],
24 | hits[0] / hits[12],
25 | hits[0:3].sum() / hits[12],
26 | hits[0:10].sum() / hits[12],
27 | hits[11] / hits[12],
28 | time.time() - starttime,
29 | )
30 | )
31 |
32 |
33 | def run( translator, data_loader, mode, optimizer, device, epoch, logfile, starttime, decode ):
34 | pred_line = []
35 | hits = np.zeros(13) # [0:10] for hit, [10] for mrr, [11] for loss, [12] for cnt
36 | for i, (query, t, s) in enumerate(data_loader):
37 | pred_seq, loss, indexL = translator( query.to(device), t.to(device), s.to(device), mode )
38 | if decode == True:
39 | continue
40 | print(f"\r {mode} {epoch}-{i}/{len(data_loader)}", end=" ")
41 | for j, index in enumerate(indexL):
42 | if index < 10:
43 | hits[index] += 1
44 | hits[10] += 1 / (index + 1)
45 | hits[12] += 1
46 | hits[11] += loss.item()
47 |
48 |
49 | if mode == 'train':
50 | wandb.log({f"Average_{mode}_MRR": hits[10]/hits[12]})
51 | wandb.log({f"{mode}_loss": loss.item()})
52 |
53 | if mode == "train":
54 | loss.backward()
55 | optimizer.step_and_update_lr()
56 | wandb.log({"lr": optimizer.lr})
57 | optimizer.zero_grad()
58 | print(hit_mrr(hits, starttime), end=" ")
59 |
60 | if mode != 'train' and not decode:
61 | wandb.log({f"{mode}_MRR": hits[10]/hits[12]})
62 | wandb.log({f"{mode}_loss":hits[11]/hits[12]})
63 | if not decode:
64 | print(f"\r {mode}-{epoch} " + hit_mrr(hits, starttime) + " ")
65 | with open(logfile, "a") as log:
66 | log.write(f"{mode}-{epoch} " + hit_mrr(hits, starttime) + "\n")
67 | return pred_line
68 |
69 |
70 | def main():
71 | seed = 42
72 | torch.manual_seed(seed)
73 | np.random.seed(seed)
74 | random.seed(seed)
75 | torch.cuda.manual_seed_all(seed)
76 |
77 | kg_path = "/data/cyl/MyPaper/No_sub_Ruleformer4UKG/DATASET/NL27K"
78 | kg = KnowledgeGraph(kg_path)
79 | train_data = MyDataset(kg, "train")
80 | valid_data = MyDataset(kg, "valid")
81 | test_data = MyDataset(kg, "test")
82 |
83 | batch_size = 64
84 |
85 | train_loader = torch.utils.data.DataLoader( dataset=train_data, batch_size=batch_size, shuffle=True, collate_fn=MyDataset.collate_fn, ) # , num_workers=16
86 | valid_loader = torch.utils.data.DataLoader( dataset=valid_data, batch_size=batch_size, shuffle=False, collate_fn=MyDataset.collate_fn, )
87 | test_loader = torch.utils.data.DataLoader( dataset=test_data, batch_size=batch_size, shuffle=False, collate_fn=MyDataset.collate_fn, )
88 |
89 | device = torch.device("cuda:0")
90 |
91 | d_word_vec = 384
92 | d_model = 384
93 | d_inner = 1024
94 | n_layers = 6
95 | n_head = 6
96 | d_k = 64
97 | d_v = 64
98 | dropout = 0.1
99 | n_position = 200
100 | lr_mul = 0.02
101 | n_warmup_steps = 5000
102 |
103 | model = Transformer(
104 | n_ent_vocab=len(kg.ents),
105 | n_rel_vocab=len(kg.rels),
106 | d_word_vec=d_word_vec,
107 | d_model=d_model,
108 | d_inner=d_inner,
109 | n_layers=n_layers,
110 | n_head=n_head,
111 | d_k=d_k,
112 | d_v=d_v,
113 | dropout=dropout,
114 | n_position=n_position,
115 | )
116 |
117 | decode_rule = False
118 | translator = Translator(model=model, body_len=3, device=device, kg=kg).to(device)
119 | optimizer = ScheduledOptim( optim.Adam(translator.parameters(), betas=(0.9, 0.98), eps=1e-09), lr_mul, d_model, n_warmup_steps, )
120 |
121 | starttime = time.time()
122 |
123 | num_epoch = 200
124 |
125 | savestep = 5
126 |
127 | wandb.init(
128 | # set the wandb project where this run will be logged
129 | project='MyModel',
130 | # track hyperparameters and run metadata
131 | config={
132 | "batch_size": batch_size,
133 | "epochs": num_epoch,
134 | },
135 | )
136 |
137 | logfile = datetime.now().strftime("%Y%m%d_%H%M%S")
138 | if not os.path.exists(f'EXPS/{logfile}'):
139 | os.mkdir(f'EXPS/{logfile}')
140 |
141 |
142 | config = {
143 | "data_path":kg_path,
144 | "batch_size":batch_size,
145 | "d_word_vec": d_word_vec,
146 | "d_model": d_model,
147 | "d_inner": d_inner,
148 | "n_layers": n_layers,
149 | "n_head": n_head,
150 | "d_k": d_k,
151 | "d_v": d_v,
152 | "dropout": dropout,
153 | "n_position": n_position,
154 | "lr_mul":lr_mul,
155 | "n_warmup_steps":n_warmup_steps,
156 | "num_epoch":num_epoch,
157 | "save_step":5,
158 | "decode_rule": decode_rule
159 | }
160 |
161 | # 指定要写入的文件路径
162 | file_path = f'EXPS/{logfile}/config.txt'
163 |
164 | # 将参数写入文件
165 | with open(file_path, "w") as file:
166 | for key, value in config.items():
167 | file.write(f"{key} = {value}\n")
168 |
169 |
170 | for epoch in range(num_epoch):
171 | if not decode_rule:
172 | run( translator, train_loader, "train", optimizer, device, epoch + 1, logfile, starttime, decode_rule )
173 | if savestep and (epoch + 1) % savestep == 0:
174 | torch.save(translator.state_dict(), f"EXPS/{logfile}/Translator{epoch+1}.ckpt")
175 | if decode_rule or (epoch + 1) % 5 == 0:
176 | with torch.no_grad():
177 | if decode_rule:
178 | run( translator, train_loader, "train", optimizer, device, epoch + 1, logfile, starttime, decode_rule )
179 | run( translator, valid_loader, "valid", optimizer, device, epoch + 1, logfile, starttime, decode_rule )
180 | run( translator, test_loader, "test", optimizer, device, epoch + 1, logfile, starttime, decode_rule )
181 |
182 | print("[Info] Finished.")
183 |
184 |
185 | if __name__ == "__main__":
186 | main()
187 |
--------------------------------------------------------------------------------
/translate_decode.py:
--------------------------------------------------------------------------------
1 | """ Translate input text with trained model. """
2 |
3 | import torch
4 | import argparse, os, time
5 |
6 | from transformer.Models import Transformer
7 | from transformer.Translator import Translator
8 | from transformer.dataset import MyDataset, KnowledgeGraph
9 | from transformer.Optim import ScheduledOptim
10 | import torch.optim as optim
11 | import numpy as np
12 | import random
13 | import wandb
14 | import os
15 | from datetime import datetime
16 |
17 |
18 | def hit_mrr(hits, starttime):
19 | return (
20 | "MRR:{:.5f} @1:{:.5f} @3:{:.5f} @10:{:.5f} LOS:{:.5f} Time:{:.1f}secs".format(
21 | hits[10] / hits[12],
22 | hits[0] / hits[12],
23 | hits[0:3].sum() / hits[12],
24 | hits[0:10].sum() / hits[12],
25 | hits[11] / hits[12],
26 | time.time() - starttime,
27 | )
28 | )
29 |
30 |
31 | def run( translator, data_loader, mode, optimizer, device, epoch, logfile, starttime, decode ):
32 | pred_line = []
33 | hits = np.zeros(13) # [0:10] for hit, [10] for mrr, [11] for loss, [12] for cnt
34 | for i, (query, t, s) in enumerate(data_loader):
35 | pred_seq, loss, indexL = translator( query.to(device), t.to(device), s.to(device), mode )
36 | if decode == True:
37 | continue
38 | print(f"\r {mode} {epoch}-{i}/{len(data_loader)}", end=" ")
39 | for j, index in enumerate(indexL):
40 | if index < 10:
41 | hits[index] += 1
42 | hits[10] += 1 / (index + 1)
43 | hits[12] += 1
44 | hits[11] += loss.item()
45 |
46 |
47 | if mode == 'train':
48 | wandb.log({f"{mode}_MRR": hits[10]/hits[12]})
49 | wandb.log({f"{mode}_loss": loss.item()})
50 |
51 | if mode == "train":
52 | loss.backward()
53 | optimizer.step_and_update_lr()
54 | wandb.log({"lr": optimizer.lr})
55 | optimizer.zero_grad()
56 | print(hit_mrr(hits, starttime), end=" ")
57 |
58 | if mode != 'train' and not decode:
59 | wandb.log({f"{mode}_MRR": hits[10]/hits[12]})
60 | wandb.log({f"{mode}_loss":hits[11]/hits[12]})
61 | if not decode:
62 | print(f"\r {mode}-{epoch} " + hit_mrr(hits, starttime) + " ")
63 | with open(logfile, "a") as log:
64 | log.write(f"{mode}-{epoch} " + hit_mrr(hits, starttime) + "\n")
65 | return pred_line
66 |
67 |
68 | def main():
69 | seed = 42
70 | torch.manual_seed(seed)
71 | np.random.seed(seed)
72 | random.seed(seed)
73 | torch.cuda.manual_seed_all(seed)
74 |
75 | kg_path = "/data/cyl/MyPaper/No_sub_Ruleformer4UKG/DATASET/CN15k"
76 | kg = KnowledgeGraph(kg_path)
77 | train_data = MyDataset(kg, "train")
78 | valid_data = MyDataset(kg, "valid")
79 | test_data = MyDataset(kg, "test")
80 |
81 | batch_size = 512
82 |
83 | train_loader = torch.utils.data.DataLoader( dataset=train_data, batch_size=batch_size, shuffle=False, collate_fn=MyDataset.collate_fn, ) # , num_workers=16
84 | valid_loader = torch.utils.data.DataLoader( dataset=valid_data, batch_size=batch_size, shuffle=False, collate_fn=MyDataset.collate_fn, )
85 | test_loader = torch.utils.data.DataLoader( dataset=test_data, batch_size=batch_size, shuffle=False, collate_fn=MyDataset.collate_fn, )
86 |
87 | device = torch.device("cpu")
88 |
89 | d_word_vec = 384
90 | d_model = 384
91 | d_inner = 1024
92 | n_layers = 4
93 | n_head = 6
94 | d_k = 64
95 | d_v = 64
96 | dropout = 0.1
97 | n_position = 200
98 | lr_mul = 0.02
99 | n_warmup_steps = 5000
100 |
101 | model = Transformer(
102 | n_ent_vocab=len(kg.ents),
103 | n_rel_vocab=len(kg.rels),
104 | d_word_vec=d_word_vec,
105 | d_model=d_model,
106 | d_inner=d_inner,
107 | n_layers=n_layers,
108 | n_head=n_head,
109 | d_k=d_k,
110 | d_v=d_v,
111 | dropout=dropout,
112 | n_position=n_position,
113 | )
114 |
115 | decode_rule = True
116 | translator = Translator(model=model, body_len=3, device=device, kg=kg, decode = decode_rule).to(device)
117 | ckpt = '/data/cyl/MyPaper/No_sub_Ruleformer4UKG/EXPS/20240208_000822_CN15K_un/Translator40.ckpt'
118 | if ckpt and decode_rule:
119 | print("CKPT APPOINTED!!! DECODE MODE!!!")
120 | decode_rule = True
121 | translator.load_state_dict(torch.load(ckpt))
122 |
123 | optimizer = ScheduledOptim( optim.Adam(translator.parameters(), betas=(0.9, 0.98), eps=1e-09), lr_mul, d_model, n_warmup_steps, )
124 |
125 | starttime = time.time()
126 |
127 | num_epoch = 1
128 |
129 | savestep = 0
130 |
131 | if not decode_rule:
132 | wandb.init(
133 | # set the wandb project where this run will be logged
134 | project='MyModel',
135 | # track hyperparameters and run metadata
136 | config={
137 | "batch_size": batch_size,
138 | "epochs": num_epoch,
139 | },
140 | )
141 |
142 | logfile = datetime.now().strftime("%Y%m%d_%H%M%S")
143 | if not os.path.exists(f'EXPS/{logfile}_decode'):
144 | os.mkdir(f'EXPS/{logfile}_decode')
145 |
146 |
147 | config = {
148 | "data_path":kg_path,
149 | "batch_size":batch_size,
150 | "d_word_vec": d_word_vec,
151 | "d_model": d_model,
152 | "d_inner": d_inner,
153 | "n_layers": n_layers,
154 | "n_head": n_head,
155 | "d_k": d_k,
156 | "d_v": d_v,
157 | "dropout": dropout,
158 | "n_position": n_position,
159 | "lr_mul":lr_mul,
160 | "n_warmup_steps":n_warmup_steps,
161 | "num_epoch":num_epoch,
162 | "save_step":5,
163 | "decode_rule": decode_rule
164 | }
165 |
166 | # 指定要写入的文件路径
167 | file_path = f'EXPS/{logfile}_decode/config.txt'
168 |
169 | # 将参数写入文件
170 | with open(file_path, "w") as file:
171 | for key, value in config.items():
172 | file.write(f"{key} = {value}\n")
173 |
174 |
175 | for epoch in range(num_epoch):
176 | if not decode_rule:
177 | run( translator, train_loader, "train", optimizer, device, epoch + 1, logfile, starttime, decode_rule )
178 | if savestep and (epoch + 1) % savestep == 0:
179 | torch.save(translator.state_dict(), f"EXPS/{logfile}/Translator{epoch+1}.ckpt")
180 | if decode_rule or (epoch + 1) % 5 == 0:
181 | with torch.no_grad():
182 | if decode_rule:
183 | run( translator, train_loader, "train", optimizer, device, epoch + 1, logfile, starttime, decode_rule )
184 | run( translator, valid_loader, "valid", optimizer, device, epoch + 1, logfile, starttime, decode_rule )
185 | run( translator, test_loader, "test", optimizer, device, epoch + 1, logfile, starttime, decode_rule )
186 |
187 | print("[Info] Finished.")
188 |
189 |
190 | if __name__ == "__main__":
191 | main()
192 |
--------------------------------------------------------------------------------
/confidence_prediction.py:
--------------------------------------------------------------------------------
1 | from transformers import AutoModel, AutoTokenizer
2 | import torch
3 | import torch.nn as nn
4 | from torch.utils.data import Dataset, DataLoader
5 | from transformers import AdamW, get_linear_schedule_with_warmup
6 | import numpy as np
7 | import random
8 | import wandb
9 |
10 |
11 | class PathDataset(Dataset):
12 | def __init__(self, data_path, model_path, max_length):
13 | self.max_legnth = max_length
14 | self.tokenizer = AutoTokenizer.from_pretrained(model_path)
15 | self.lines = open(data_path, "r").readlines()
16 |
17 | def __len__(self):
18 | return len(self.lines)
19 |
20 | def __getitem__(self, index):
21 | score, question, context = self.lines[index].strip("\n").split("\t")
22 | input_txt = f"[CLS] {question} [SEP] {context} [SEP]"
23 | encoding = self.tokenizer.encode_plus(
24 | input_txt,
25 | max_length=self.max_legnth,
26 | add_special_tokens=False, # [CLS]和[SEP]
27 | return_token_type_ids=True,
28 | pad_to_max_length=True,
29 | return_attention_mask=True,
30 | return_tensors="pt", # Pytorch tensor张量
31 | )
32 |
33 | return {
34 | "input_txt": input_txt,
35 | "input_ids": encoding["input_ids"].flatten(),
36 | "attention_mask": encoding["attention_mask"].flatten(),
37 | "toeken_type_ids": encoding["token_type_ids"].flatten(),
38 | "labels": torch.tensor(eval(score), dtype=torch.float),
39 | }
40 |
41 |
42 | class ConfModel(nn.Module):
43 | def __init__(self, model_path, device):
44 | super().__init__()
45 | self.bert = AutoModel.from_pretrained(model_path)
46 | self.d_model = self.bert.pooler.dense.out_features
47 | self.linear1 = nn.Linear(self.d_model, self.d_model*4)
48 | self.relu = nn.ReLU()
49 | self.linear2 = nn.Linear(self.d_model*4,1)
50 | self.sigmoid = nn.Sigmoid()
51 | self.device = device
52 | self.w_q = nn.Linear(self.d_model, self.d_model)
53 | self.w_k = nn.Linear(self.d_model, self.d_model)
54 | self.w_v = nn.Linear(self.d_model, self.d_model)
55 | self.w_o = nn.Linear(self.d_model, self.d_model)
56 |
57 | def self_attn_encode(self, q, k, v, mask):
58 | # q: batch_size,1,d_model
59 | # k: batch_size,seq_len,d_model -> batch_size,d_model,seq_len
60 | # v: batch_size,seq_len,d_model
61 | q = self.w_q(q)
62 | k = self.w_k(k).transpose(1, 2)
63 | v = self.w_v(v)
64 |
65 | # attn_logit: batch_size,1,seq_len
66 | attn_logit = torch.matmul(q, k)
67 | if mask != None:
68 | attn_logit = attn_logit.masked_fill(mask == 0, -1e9)
69 | attn_weight = torch.softmax(attn_logit, dim=-1)
70 | # batch_size,1,seq_len * batch_size,seq_len,d_model -> batch_size,1,d_model
71 | weight_sum = torch.matmul(attn_weight, v)
72 | output = self.w_o(weight_sum)
73 | return output
74 |
75 | def forward(self, x, mode):
76 | input_ids = x["input_ids"].to(self.device)
77 | attn_mask = x["attention_mask"].to(self.device)
78 |
79 | output = self.bert(input_ids=input_ids, attention_mask=attn_mask)
80 |
81 | last_hidden_state = output[
82 | "last_hidden_state"
83 | ] # batch_size*max_token_length*d_model
84 | pooler_output = output["pooler_output"] # batch_size,d_model
85 |
86 | if mode == "CLS":
87 | score = pooler_output
88 | elif mode == "avg":
89 | extended_attn_mask = (
90 | attn_mask.unsqueeze(-1).expand_as(last_hidden_state).float()
91 | )
92 | last_hidden_state_masked = last_hidden_state * extended_attn_mask
93 | sum_embeddings = torch.sum(last_hidden_state_masked, dim=1)
94 | sum_mask = torch.sum(extended_attn_mask, dim=1)
95 | mean_pooled = sum_embeddings / sum_mask
96 | score = mean_pooled
97 | elif mode == "attn":
98 | output = self.self_attn_encode(
99 | last_hidden_state[:, 0, :].unsqueeze(1),
100 | last_hidden_state,
101 | last_hidden_state,
102 | attn_mask.unsqueeze(1),
103 | )
104 | score = output
105 | score = self.linear2(self.relu(self.linear1(score))).flatten()
106 | return score
107 |
108 |
109 | def train_model(model, dataloader, criterion, optimizer, scheduler, device, strategy):
110 | model = model.to(device).train()
111 | criterion = criterion.to(device)
112 |
113 | for i, batch in enumerate(dataloader):
114 | # if i < 3:
115 | # print(batch)
116 | input_txt = batch["input_txt"]
117 | labels = batch["labels"].to(device)
118 | output = model(batch, strategy)
119 |
120 | loss = criterion(output, labels)
121 |
122 | if i % 5 == 0:
123 | with torch.no_grad():
124 | diff = output - labels
125 | abs_diff = torch.abs(diff)
126 | mae = torch.mean(abs_diff)
127 | print(f"{i}\t MSE:{loss.item()}\t MAE:{mae}")
128 | loss.backward()
129 | nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
130 | optimizer.step()
131 | scheduler.step()
132 | optimizer.zero_grad()
133 |
134 |
135 | def test_model(model, dataloader, criterion, device, mode, strategy):
136 | with torch.no_grad():
137 | output_list = None
138 | label_list = None
139 | model = model.to(device).eval()
140 | for i, batch in enumerate(dataloader):
141 | input_txt = batch["input_txt"]
142 | labels = batch["labels"].to(device)
143 | output = model(batch, strategy)
144 | if output_list == None:
145 | output_list = output
146 | else:
147 | output_list = torch.cat((output_list, output), dim=0)
148 | if label_list == None:
149 | label_list = labels
150 | else:
151 | label_list = torch.cat((label_list, labels), dim=0)
152 |
153 | diff = output_list - label_list
154 | abs_diff = torch.abs(diff)
155 | mae = torch.mean(abs_diff)
156 | mse = criterion(output_list, label_list).item()
157 | print(f"{mode} \t MSE: {mse}\t MAE: {mae}")
158 |
159 |
160 | def main():
161 | # wandb.init()
162 | EXP_name = ''
163 | data_path = "/data/cyl/MyPaper/No_sub_Ruleformer4UKG/decode_rules/dataset/NL27K/"
164 | model_path = "/data/cyl/MyPaper/No_sub_Ruleformer4UKG/bert-base-uncased"
165 | strategy = "attn"
166 |
167 | batch_size = 256
168 | max_length = 90
169 | train_dataset = PathDataset(data_path + "nl27k_path_train.txt", model_path, max_length)
170 | valid_dataset = PathDataset(data_path + "nl27k_path_valid.txt", model_path, max_length)
171 | test_dataset = PathDataset(data_path + "nl27k_path_test.txt", model_path, max_length)
172 |
173 | train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
174 | valid_dataloader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False)
175 | test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
176 |
177 | device = torch.device("cuda:2")
178 |
179 | myModel = ConfModel(model_path, device)
180 |
181 | EPOCHS = 100
182 | criterion = torch.nn.MSELoss()
183 | optimizer = AdamW(myModel.parameters(), lr=2e-5, correct_bias=False)
184 | total_steps = len(train_dataloader) * EPOCHS
185 | scheduler = get_linear_schedule_with_warmup(
186 | optimizer, num_warmup_steps=total_steps // 10, num_training_steps=total_steps
187 | )
188 | for e in range(EPOCHS):
189 | train_model(myModel, train_dataloader, criterion, optimizer, scheduler, device, strategy)
190 | # torch.save(myModel.state_dict(), f"{EXP_name}/model_{e}.pt")
191 | test_model(myModel, valid_dataloader, criterion, device, "valid", strategy)
192 | test_model(myModel, test_dataloader, criterion, device, "test", strategy)
193 |
194 |
195 | if __name__ == "__main__":
196 | seed = 42
197 | torch.manual_seed(seed)
198 | np.random.seed(seed)
199 | random.seed(seed)
200 | torch.cuda.manual_seed_all(seed)
201 | main()
202 |
--------------------------------------------------------------------------------
/transformer/Models.py:
--------------------------------------------------------------------------------
1 | ''' Define the Transformer model '''
2 | import torch, math
3 | import torch.nn as nn
4 | import numpy as np
5 | from transformer.Layers import EncoderLayer, DecoderLayer
6 |
7 |
8 | def get_pad_mask(seq, pad_idx):
9 | return (seq != pad_idx).unsqueeze(-2)
10 |
11 |
12 | def get_subsequent_mask(seq):
13 | ''' For masking out the subsequent info. '''
14 | sz_b, len_s = seq.size()
15 | subsequent_mask = (1 - torch.triu(
16 | torch.ones((1, len_s, len_s), device=seq.device), diagonal=1)).bool()
17 | return subsequent_mask
18 |
19 |
20 | class PositionalEncoding(nn.Module):
21 |
22 | def __init__(self, d_hid, n_position=200):
23 | super(PositionalEncoding, self).__init__()
24 | # Not a parameter
25 | self.register_buffer('pos_table', self._get_sinusoid_encoding_table(n_position, d_hid))
26 |
27 | def _get_sinusoid_encoding_table(self, n_position, d_hid):
28 | ''' Sinusoid position encoding table '''
29 | # TODO: make it with torch instead of numpy
30 |
31 | def get_position_angle_vec(position):
32 | return [position / np.power(10000, 2 * (hid_j // 2) / d_hid) for hid_j in range(d_hid)]
33 |
34 | sinusoid_table = np.array([get_position_angle_vec(pos_i) for pos_i in range(n_position)])
35 | sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i
36 | sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1
37 |
38 | return torch.FloatTensor(sinusoid_table).unsqueeze(0)
39 |
40 | def forward(self, x):
41 | return x + self.pos_table[:, :x.size(1)].clone().detach()
42 |
43 |
44 | class Encoder(nn.Module):
45 | ''' A encoder model with self attention mechanism. '''
46 |
47 | def __init__(self, d_word_vec, n_layers, n_head, d_k, d_v, d_model, d_inner, dropout=0.1,
48 | n_position=200, scale_emb=False, ent_word_emb=None, rel_word_emb=None):
49 |
50 | super().__init__()
51 | self.ent_word_emb = ent_word_emb
52 | self.rel_word_emb = rel_word_emb
53 | self.position_enc = PositionalEncoding(d_word_vec, n_position=n_position)
54 | self.dropout = nn.Dropout(p=dropout)
55 | self.layer_stack = nn.ModuleList([ EncoderLayer(d_model, d_inner, n_head, d_k, d_v, dropout=dropout) for _ in range(n_layers)])
56 | self.layer_norm = nn.LayerNorm(d_model, eps=1e-6)
57 | self.scale_emb = scale_emb
58 | self.d_model = d_model
59 |
60 | def forward(self, src_seq, src_mask, return_attns=False):
61 |
62 | enc_slf_attn_list = []
63 |
64 | # -- Forward
65 | # enc_output = self.src_word_emb(src_seq)
66 | # src_seq: [batch_size,2]
67 | head_emb = self.ent_word_emb(src_seq[:,0])
68 | rel_emb = self.rel_word_emb(src_seq[:,1])
69 | enc_output = torch.stack((head_emb,rel_emb),dim=1)
70 |
71 | if self.scale_emb:
72 | enc_output *= self.d_model ** 0.5
73 | enc_output = self.dropout(self.position_enc(enc_output))
74 | enc_output = self.layer_norm(enc_output)
75 |
76 | for enc_layer in self.layer_stack:
77 | enc_output, enc_slf_attn = enc_layer(enc_output, slf_attn_mask=src_mask)
78 | enc_slf_attn_list += [enc_slf_attn] if return_attns else []
79 |
80 | if return_attns:
81 | return enc_output, enc_slf_attn_list
82 | return enc_output,
83 |
84 |
85 | class Decoder(nn.Module):
86 | ''' A decoder model with self attention mechanism. '''
87 |
88 | def __init__(
89 | self, d_word_vec, n_layers, n_head, d_k, d_v,
90 | d_model, d_inner, n_position=200, dropout=0.1, scale_emb=False, rel_word_emb=None):
91 |
92 | super().__init__()
93 |
94 | self.rel_word_emb = rel_word_emb
95 | self.bos_embedding = nn.Embedding(1,d_model)
96 | self.position_enc = PositionalEncoding(d_word_vec, n_position=n_position)
97 | self.dropout = nn.Dropout(p=dropout)
98 | self.layer_stack = nn.ModuleList([ DecoderLayer(d_model, d_inner, n_head, d_k, d_v, dropout=dropout) for _ in range(n_layers)])
99 | self.layer_norm = nn.LayerNorm(d_model, eps=1e-6)
100 | self.scale_emb = scale_emb
101 | self.d_model = d_model
102 |
103 | self.bos_embedding = torch.nn.Embedding(1,d_model)
104 |
105 | def forward(self, trg_seq, trg_mask, enc_output, src_mask, return_attns=False):
106 |
107 | dec_slf_attn_list, dec_enc_attn_list = [], []
108 |
109 | # -- Forward
110 | if trg_seq.size(1) == 1:
111 | dec_output = self.rel_word_emb(trg_seq)
112 | else:
113 | bos_embedding = self.bos_embedding(trg_seq[:,0]).unsqueeze(1)
114 | rel_embedding = self.rel_word_emb(trg_seq[:,1:])
115 | dec_output = torch.cat((bos_embedding,rel_embedding),dim=1)
116 | if self.scale_emb:
117 | dec_output *= self.d_model ** 0.5
118 | dec_output = self.dropout(self.position_enc(dec_output))
119 | dec_output = self.layer_norm(dec_output)
120 |
121 | for dec_layer in self.layer_stack:
122 | dec_output, dec_slf_attn, dec_enc_attn = dec_layer(
123 | dec_output, enc_output, slf_attn_mask=trg_mask, dec_enc_attn_mask=src_mask)
124 | dec_slf_attn_list += [dec_slf_attn] if return_attns else []
125 | dec_enc_attn_list += [dec_enc_attn] if return_attns else []
126 |
127 | if return_attns:
128 | return dec_output, dec_slf_attn_list, dec_enc_attn_list
129 | return dec_output,
130 |
131 |
132 | class Transformer(nn.Module):
133 | ''' A sequence to sequence model with attention mechanism. '''
134 |
135 | def __init__(
136 | self, n_ent_vocab, n_rel_vocab,
137 | d_word_vec=512, d_model=512, d_inner=2048,
138 | n_layers=6, n_head=8, d_k=64, d_v=64,
139 | dropout=0.1, n_position=200):
140 |
141 | super().__init__()
142 | self.relationE = nn.Embedding(n_rel_vocab, d_model)
143 | self.entityE = nn.Embedding(n_ent_vocab, d_model)
144 |
145 | # self.src_pad_idx, self.trg_pad_idx = src_pad_idx, trg_pad_idx
146 |
147 | assert n_head*d_k == d_model
148 | # In section 3.4 of paper "Attention Is All You Need", there is such detail:
149 | # "In our model, we share the same weight matrix between the two
150 | # embedding layers and the pre-softmax linear transformation...
151 | # In the embedding layers, we multiply those weights by \sqrt{d_model}".
152 | #
153 | # Options here:
154 | # 'emb': multiply \sqrt{d_model} to embedding output
155 | # 'prj': multiply (\sqrt{d_model} ^ -1) to linear projection output
156 | # 'none': no multiplication
157 |
158 | # assert scale_emb_or_prj in ['emb', 'prj', 'none']
159 | # scale_emb = (scale_emb_or_prj == 'emb') if trg_emb_prj_weight_sharing else False
160 | scale_emb = False
161 | # self.scale_prj = (scale_emb_or_prj == 'prj') if trg_emb_prj_weight_sharing else False
162 | self.d_model = d_model
163 |
164 | self.encoder = Encoder(
165 | n_position=n_position,
166 | d_word_vec=d_word_vec, d_model=d_model, d_inner=d_inner,
167 | n_layers=n_layers, n_head=n_head, d_k=d_k, d_v=d_v,
168 | dropout=dropout, scale_emb=scale_emb,
169 | ent_word_emb=self.entityE, rel_word_emb=self.relationE)
170 |
171 | self.decoder = Decoder(
172 | n_position=n_position,
173 | d_word_vec=d_word_vec, d_model=d_model, d_inner=d_inner,
174 | n_layers=n_layers, n_head=n_head, d_k=d_k, d_v=d_v,
175 | dropout=dropout, scale_emb=scale_emb, rel_word_emb=self.relationE)
176 |
177 | self.trg_word_prj = nn.Linear(d_model, n_rel_vocab, bias=False)
178 |
179 | for n,p in self.named_parameters():
180 | if p.dim() > 1 and 'entityE' not in n and 'relationE' not in n:
181 | nn.init.xavier_uniform_(p)
182 |
183 | assert d_model == d_word_vec, \
184 | 'To facilitate the residual connections, \
185 | the dimensions of all module outputs shall be the same.'
186 |
187 | # if trg_emb_prj_weight_sharing:
188 | # # Share the weight between target word embedding & last dense layer
189 | # self.trg_word_prj.weight = self.decoder.trg_word_emb.weight
190 |
191 | # if emb_src_trg_weight_sharing:
192 | # self.encoder.src_word_emb.weight = self.decoder.trg_word_emb.weight
193 |
194 |
195 | def forward(self, src_seq, trg_seq):
196 | raise NotImplementedError
197 |
198 | src_mask = get_pad_mask(src_seq, self.src_pad_idx)
199 | trg_mask = get_pad_mask(trg_seq, self.trg_pad_idx) & get_subsequent_mask(trg_seq)
200 |
201 | enc_output, *_ = self.encoder(src_seq, src_mask)
202 | dec_output, *_ = self.decoder(trg_seq, trg_mask, enc_output, src_mask)
203 | seq_logit = self.trg_word_prj(dec_output)
204 | if self.scale_prj:
205 | seq_logit *= self.d_model ** -0.5
206 |
207 | return seq_logit.view(-1, seq_logit.size(2))
208 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/transformer/Translator.py:
--------------------------------------------------------------------------------
1 | """
2 | 修改了新的解码方式,用邻接矩阵判断解码的关系是否相连,而不是用循环去判断
3 | """
4 | """ This module will handle the text generation with beam search. """
5 | import torch, math
6 | import torch.nn as nn
7 | import torch.nn.functional as F
8 | from transformer.Models import Transformer, get_pad_mask, get_subsequent_mask
9 | from collections import defaultdict
10 | import numpy, time
11 | from itertools import product
12 | from datetime import datetime
13 |
14 |
15 | class Translator(nn.Module):
16 | """Load a trained model and translate in beam search fashion."""
17 |
18 | def __init__(self, model, body_len, device=None, kg=None, decode=False):
19 | super(Translator, self).__init__()
20 |
21 | self.alpha = 0.7
22 | self.body_len = body_len
23 |
24 | self.n_rel_vocab = len(kg.rels)
25 | self.n_ent_vocab = len(kg.ents)
26 | # self.src_pad_idx = opt.src_pad_idx
27 | # self.n_trg_vocab = opt.trg_vocab_size
28 | # self.n_src_vocab = opt.src_vocab_size
29 |
30 | self.device = device
31 |
32 | self.model = model
33 | self.model.train()
34 | self.database = [x.to(device) for x in kg.relations]
35 | self.filtered_dict = kg.filtered_dict
36 |
37 | self.decode = decode
38 | if self.decode:
39 | self.graph = kg.neighbors
40 | self.decode_rule_num = 0
41 | self.the_rel = 0
42 | self.the_rel_min = 0
43 | self.the_all = 0
44 | self.id2r = kg.id2r
45 | self.id2e = kg.id2e
46 | self.rules = defaultdict(dict)
47 | self.decode_rule_num_filter = 0
48 | self.decode_file = 'cn15k_rules_0221.txt'
49 |
50 | self.register_buffer("thr", torch.FloatTensor([1e-20]))
51 |
52 | def _model_decode(self, trg_seq, enc_output, src_mask):
53 | trg_mask = None
54 | dec_output, *_ = self.model.decoder(trg_seq, trg_mask, enc_output, src_mask)
55 | return F.softmax(self.model.trg_word_prj(dec_output), dim=-1)
56 |
57 | def _get_init_state(self, src_seq, src_mask):
58 | enc_output = None
59 | enc_output, *_ = self.model.encoder(src_seq, src_mask)
60 | dec_output = self._model_decode(self.init_seq, enc_output, src_mask)
61 |
62 | best_k_probs, best_k_idx = dec_output[:, -1, :].topk(1)
63 |
64 | scores = torch.log(best_k_probs).view(self.batch_size)
65 | gen_seq = self.blank_seqs.clone().detach()
66 | gen_seq[:, 1] = best_k_idx.squeeze()
67 | return enc_output, gen_seq, scores, dec_output
68 |
69 | def _get_the_best_score_and_idx(self, gen_seq, dec_output, scores, step):
70 | # Get k candidates for each beam, k^2 candidates in total.
71 | best_k2_probs, best_k2_idx = dec_output[:, -1, :].topk(1)
72 | assert dec_output.size(1) == step
73 |
74 | gen_seq[:, step] = best_k2_idx.squeeze()
75 |
76 | return gen_seq, scores, dec_output
77 |
78 | def forwardAllNLP(self, query, t, s, attention, mode):
79 | num_pos_rel = self.n_rel_vocab // 2
80 | batch_size = query.size(0)
81 | database = [x.clone().detach() for x in self.database]
82 | for (hh, rr), tt in zip(query, t):
83 | if rr >= num_pos_rel:
84 | continue
85 | idxs = torch.where( (hh == database[rr].indices()[0]) & (tt == database[rr].indices()[1]) )
86 | database[rr].values()[idxs] = 0
87 |
88 | memories = F.one_hot(query[:,0], num_classes=self.n_ent_vocab).float().to_sparse()
89 | for step in range(self.body_len):
90 | added_results = torch.zeros(batch_size, self.n_ent_vocab).to(self.device)
91 | for r in range(num_pos_rel):
92 | for links, atta in zip( [database[r], database[r].transpose(0, 1)], [attention[:, step, r], attention[:, step, r + num_pos_rel]], ):
93 | added_results = added_results + torch.sparse.mm( memories, links ).to_dense() * atta.unsqueeze(1)
94 | added_results = added_results + memories.to_dense() * attention[ :, step, -1 ].unsqueeze(1)
95 | # added_results = added_results / torch.max(self.thr, torch.sum(added_results, dim=1).unsqueeze(1))
96 | memories = added_results.to_sparse()
97 | memories = memories.to_dense()
98 | memories = memories / torch.max( self.thr, torch.sum(memories, dim=1).unsqueeze(1) )
99 | targets = F.one_hot(t, num_classes=self.n_ent_vocab).float()
100 | final_loss = -torch.sum( targets * torch.log(torch.max(self.thr, memories)), dim=1 )
101 | batch_loss = torch.mean(final_loss)
102 | if mode != "train":
103 | for i in range(batch_size):
104 | for idx in self.filtered_dict[(query[i,0].item(), query[i,1].item())]:
105 | if idx != t[i].item():
106 | memories[i, idx] = 0
107 | idxs = torch.argsort(memories, descending=True)
108 | indexs = torch.where(idxs == t.unsqueeze(-1))[1].tolist()
109 | return batch_loss, indexs
110 |
111 | def forward(self, query, t, s, mode="train"):
112 | batch_size = query.size(0)
113 | self.batch_size = batch_size
114 | # self.init_seq = trg[:, 1].unsqueeze(-1).clone().detach()
115 | self.init_seq = torch.zeros((batch_size, 1),dtype=torch.long).to(self.device) # decoder输入
116 | self.blank_seqs = torch.zeros((batch_size,self.body_len+1),dtype=torch.long).detach().to(self.device) # 存放解码序列,不参与梯度计算
117 | # src_mask = get_pad_mask(src_seq, self.src_pad_idx)
118 | src_mask = None
119 | enc_output, gen_seq, scores, dec_output = self._get_init_state(query, src_mask)
120 | for step in range(2, self.body_len + 1):
121 | dec_output = self._model_decode( gen_seq[:, :step].clone().detach(), enc_output, src_mask )
122 | gen_seq, scores, dec_output = self._get_the_best_score_and_idx( gen_seq, dec_output, scores, step )
123 |
124 | gen_seq[:,0] = query[:,1]
125 | if self.decode:
126 | self.decode_rule(dec_output, query, t, s, mode)
127 | loss, index = 0, [0]
128 | else:
129 | loss, index = self.forwardAllNLP(query, t, s, dec_output, mode)
130 |
131 | return gen_seq, loss, index
132 |
133 |
134 | def decode_rule(self, attention, query, t, s, mode):
135 | def dfs(database,memories,rela_attention,step,body_len,tail,path):
136 | if step == body_len:
137 | return tail
138 | to_add = None
139 | step_attention = rela_attention[step]
140 | added_results = torch.zeros(1, self.n_ent_vocab).to(self.device)
141 | for r in range(self.n_rel_vocab):
142 | if r < self.n_rel_vocab//2:
143 | if to_add == None:
144 | to_add = torch.sparse.mm(memories,database[r]).to_dense()*step_attention[r]
145 | else:
146 | to_add = torch.cat([to_add,torch.sparse.mm(memories,database[r]).to_dense()*step_attention[r]])
147 | elif r < self.n_rel_vocab-1:
148 | if to_add == None:
149 | to_add = torch.sparse.mm(memories,database[r-self.n_rel_vocab//2].transpose(0,1)).to_dense()*step_attention[r]
150 | else:
151 | to_add = torch.cat([to_add,torch.sparse.mm(memories,database[r-self.n_rel_vocab//2].transpose(0,1)).to_dense()*step_attention[r]])
152 | else:
153 | if to_add == None:
154 | to_add = memories.to_dense()*step_attention[r]
155 | else:
156 | to_add = torch.cat([to_add,memories.to_dense()*step_attention[r]])
157 | added_results = added_results + torch.sum(to_add,dim=0)
158 |
159 | added_results = added_results.to_dense() / torch.max(self.thr, torch.sum(added_results, dim=1).unsqueeze(1))
160 | memories = added_results.to_sparse()
161 |
162 | target = dfs(database,memories,rela_attention,step+1,body_len,tail,path)
163 | path.append(target)
164 |
165 | rel_idx = torch.argmax(to_add[:,target])
166 | path.append(rel_idx)
167 |
168 | if step == 0:
169 | return
170 |
171 | if rel_idx < self.n_rel_vocab//2:
172 | adj_matrix = database[rel_idx].to_dense()
173 | elif rel_idx < self.n_rel_vocab-1:
174 | adj_matrix = database[rel_idx-self.n_rel_vocab//2].transpose(0,1).to_dense()
175 | else:
176 | adj_matrix = torch.eye(self.n_ent_vocab)
177 |
178 | adj_vec = adj_matrix[:,target].to(self.device)
179 | dots = memories*adj_vec
180 | return torch.argmax(dots.to_dense())
181 |
182 |
183 |
184 | num_pos_rel = self.n_rel_vocab // 2
185 | batch_size = query.size(0)
186 | database = [x.clone().detach() for x in self.database]
187 |
188 | for batch in range(batch_size):
189 | head_id = query[batch,0]
190 | rela_id = query[batch,1]
191 | tail_id = t[batch]
192 | conf = s[batch]
193 |
194 | rela_attention = attention[batch]
195 |
196 | idxs = torch.where((head_id == database[rela_id].indices()[0]) & (tail_id == database[rela_id].indices()[1]))
197 | database[rela_id].values()[idxs] = 0
198 |
199 | path = []
200 | memories = F.one_hot(torch.tensor([head_id]),num_classes=self.n_ent_vocab).float().to_sparse().to(self.device)
201 | dfs(database,memories,rela_attention,0,self.body_len,tail_id,path)
202 | path_item = list(reversed([x.item() for x in path]))
203 | path_item = [head_id.item()]+path_item
204 |
205 | for i in range(len(path_item)):
206 | if i%2 == 0:
207 | path_item[i] = self.id2e[path_item[i]]
208 | else:
209 | path_item[i] = self.id2r[path_item[i]]
210 |
211 | triple = [str(conf.item()),self.id2e[head_id.item()],self.id2r[rela_id.item()],self.id2e[tail_id.item()]]
212 | with open(self.decode_file,'a') as f:
213 | f.write(mode+'\t'+'\t'.join(triple+path_item)+'\n')
214 |
215 |
216 |
217 | # def decode_rule(self, dec_output, query, mode):
218 | # relation_attention_list = dec_output
219 | # batch_size = query.size(0)
220 | # num_step = self.body_len
221 | # for batch in range(batch_size):
222 | # paths = {t + 1: [] for t in range(num_step)}
223 | # # paths at hop 0, in the format of ([rel1,..],[ent1,..],weight)
224 | # paths[0] = [([-1], [query[batch, 0].item()], 1.0)]
225 | # relation_attentions = relation_attention_list[batch]
226 | # for step in range(num_step):
227 | # if not paths[step]:
228 | # break
229 | # relation_attention_ori = relation_attentions[step]
230 | # for rels, pths, wei in paths[step]:
231 | # if pths[-1] not in self.graph:
232 | # continue
233 | # # select relations(including self-loop) connected to the tail of each path
234 | # sel = torch.LongTensor(list(self.graph[pths[-1]].keys()) + [self.n_rel_vocab - 1])
235 | # relation_attention = torch.zeros(self.n_rel_vocab).to(self.device)
236 | # relation_attention[sel] = relation_attention_ori[sel].clone()
237 | # rel_att_max = torch.max(relation_attention).item()
238 | # relation_attention /= rel_att_max
239 |
240 | # for rr in torch.nonzero(relation_attention> max(self.the_rel, self.the_rel_min / rel_att_max)): # relations which exceed threshold
241 | # # 归一化之后大于self.the_rel,归一化之前大于self.thr_rel_min
242 | # rr = rr.item()
243 | # if rr == self.n_rel_vocab - 1: #
244 | # paths[step + 1].append((rels + [rr],pths + [pths[-1]],wei * relation_attention[rr].item(),))
245 | # elif rr in self.graph[pths[-1]].keys():
246 | # for tail in self.graph[pths[-1]][rr]:
247 | # paths[step + 1].append((rels + [rr],pths + [tail],wei * relation_attention[rr].item(),))
248 |
249 | # for path in paths[step + 1]:
250 | # rels, pths, wei = path
251 | # if path[2] > self.the_all:
252 | # self.decode_rule_num += 1
253 | # print( "\rWrite {}-{} Rule(s)".format( self.decode_rule_num, self.decode_rule_num_filter ), end="", )
254 | # head_rule = self.id2r[query[batch, 1].item()]
255 | # rule_body = "^".join([self.id2r[r] for r in rels[1:]])
256 | # try:
257 | # self.rules[head_rule][rule_body].append(wei)
258 | # except KeyError:
259 | # self.rules[head_rule][rule_body] = [wei]
260 | # self.decode_rule_num_filter += 1
261 | # with open(self.decode_file, "a") as f:
262 | # f.write(mode+'\t'+self.id2e[query[batch,0].item()]+'\t'+head_rule + "<-" + rule_body + "\n")
263 |
--------------------------------------------------------------------------------
/DATASET/NL27K/relations.txt:
--------------------------------------------------------------------------------
1 | concept:arthropodthatfeedoninsect
2 | concept:teamplaysincity
3 | concept:organizationhastopmember
4 | concept:citystadiums
5 | concept:createdbyagent
6 | concept:proxyfor
7 | concept:organizationcreatedatdate
8 | concept:cityliesonriver
9 | concept:coachworksincountry
10 | concept:academicprogramatuniversity
11 | concept:countryisthehomeofsportsteam
12 | concept:arthropodlookslikeinsect
13 | concept:cityhotels
14 | concept:subpartoforganization
15 | concept:athleteinjuredhisbodypart
16 | concept:persondiedinlocation
17 | concept:worker
18 | concept:organizationcreatedbyperson
19 | concept:inverseofbankchiefexecutiveceo
20 | concept:countrycities
21 | concept:stateorprovinceresidenceofperson
22 | concept:citynewspaper
23 | concept:personhasresidenceincountry
24 | concept:leagueplayers
25 | concept:musicianplaysinstrument
26 | concept:generalizationof
27 | concept:personleadscity
28 | concept:sportusesstadium
29 | concept:personhasparent
30 | concept:wifeof
31 | concept:topmemberoforganization
32 | concept:istallerthan
33 | concept:publicationjournalist
34 | concept:statehascapital
35 | concept:statecontainsfarm
36 | concept:teamplayssport
37 | concept:countryhascompanyoffice
38 | concept:countryofpersonbirth
39 | concept:locationofitemexistence
40 | concept:inverseofagriculturalproductcookedwithagricultureproduct
41 | concept:ismultipleof
42 | concept:dateof
43 | concept:countrycontainsfarm
44 | concept:inverseofanimalsuchasinsect
45 | concept:equipmentusedbysport
46 | concept:organizationhasagent
47 | concept:companyceo
48 | concept:inverseoflanguegeschoolincity
49 | concept:politicianholdsoffice
50 | concept:geopoliticallocationresidenceofpersion
51 | concept:stadiumhometoathlete
52 | concept:officeheldbypolitician
53 | concept:politicianusendorsespoliticianus
54 | concept:inverseofagriculturalproducttoattractinsect
55 | concept:persondeathdate
56 | concept:winneringame
57 | concept:beveragemadefrombeverage
58 | concept:athleteplaysforteam
59 | concept:instrumentplayedbymusician
60 | concept:agriculturalproductcookedwithagriculturalproduct
61 | concept:inverseofanimalssuchasmammals
62 | concept:arthropodandotherarthropod
63 | concept:geopoliticallocationcontainscity
64 | concept:agentinvolvedwithitem
65 | concept:organizationacronymhasname
66 | concept:stadiumhometeam
67 | concept:marriedinyear
68 | concept:musicalartisthadapetanimal
69 | concept:chemicaltypehaschemical
70 | concept:worksfor
71 | concept:producedby
72 | concept:drugworkedonbyagent
73 | concept:geopoliticalorganizationleadbyperson
74 | concept:inverseofwriterwasbornincity
75 | concept:athletecoach
76 | concept:organizationheadquarteredincountry
77 | concept:automakerproducesmodel
78 | concept:stadiumhometosport
79 | concept:athleteplayssport
80 | concept:inverseofinsecteatsgrain
81 | concept:sportusesequipment
82 | concept:companyalsoknownas
83 | concept:acquired
84 | concept:cityhasairport
85 | concept:iteminvolvedwithagent
86 | concept:animaltypehasanimal
87 | concept:locationofpersondeath
88 | concept:musicgenreartist
89 | concept:inverseofclothingmadefromplant
90 | concept:inverseofarachnidiseatenbybird
91 | concept:stateorprovinceoforganizationheadquarters
92 | concept:agriculturalproductgrowninlandscapefeatures
93 | concept:animaleatfood
94 | concept:inverseofvegetableproductioninstateorprovince
95 | concept:jobpositionheldbyperson
96 | concept:professiontypehasprofession
97 | concept:inverseofagriculturalproductgrowninlandscapefeature
98 | concept:agriculturalproducttoattractinsect
99 | concept:hospitalincity
100 | concept:personbornincountry
101 | concept:dateoforganizationcreation
102 | concept:hasofficeincity
103 | concept:statehaslake
104 | concept:producesproduct
105 | concept:stateorprovinceisborderedbystateorprovince
106 | concept:museumincity
107 | concept:countrycapital
108 | concept:personhasresidenceinstateorprovince
109 | concept:vegetableproductioninstateorprovince
110 | concept:organizationheadquarteredincity
111 | concept:personbelongstoorganization
112 | concept:personhasresidenceincity
113 | concept:inverseofprofessionactsatthesameareaofpoliticissue
114 | concept:fatherofperson
115 | concept:inverseofdiseasemaybecausedbydrug
116 | concept:athletehomestadium
117 | concept:chemicalistypeofchemical
118 | concept:sportnamehasoriginlanguage
119 | concept:televisioncompanyaffiliate
120 | concept:productproducedincountry
121 | concept:diseasemaybecausedbydrug
122 | concept:personborninlocation
123 | concept:sportfansincountry
124 | concept:inverseofmammalsuchasmammal
125 | concept:citylocatedincountry
126 | concept:coachesteam
127 | concept:specializationof
128 | concept:farmlocatedincountry
129 | concept:inverseofanimaleatfood
130 | concept:personalsoknownas
131 | concept:inverseofinvertebratefeedonfood
132 | concept:subjectconcernedbyacademicfield
133 | concept:citytelevisionstation
134 | concept:inverseofcoachcanspeaklanguage
135 | concept:agentcontrols
136 | concept:agentcreated
137 | concept:objectfoundinscene
138 | concept:personattendsschool
139 | concept:organizationnamehasacronym
140 | concept:countrylanguage
141 | concept:inverseofvegetableisproducedatcountry
142 | concept:weaponmadeincountry
143 | concept:bodypartcontainsbodypart
144 | concept:agentcontributedtocreativework
145 | concept:agentparticipatedinevent
146 | concept:economicsectorcompany
147 | concept:schoolgraduatedperson
148 | concept:cityhashospital
149 | concept:players
150 | concept:jobpositionusesacademicfield
151 | concept:inverseofanimalfeedoninsect
152 | concept:athletealsoknownas
153 | concept:arthropodcanbeveryirritatingtomammal
154 | concept:vegetableisproducedatcountry
155 | concept:languageofuniversity
156 | concept:teammember
157 | concept:inverseofarthropodcalledarthropod
158 | concept:countryhascitizen
159 | concept:inverseofagriculturalproductgrowinginstateorprovince
160 | concept:creativeworkcontributedtobyagent
161 | concept:inverseofsportschoolincountry
162 | concept:itemfoundinroom
163 | concept:academicfieldusedinjobposition
164 | concept:inverseofweaponmadeincountry
165 | concept:languageofsportsgame
166 | concept:personbirthdate
167 | concept:inverseofautomobilemakerdealersincountry
168 | concept:academicfieldsuchasacademicfield
169 | concept:countryofpersondeath
170 | concept:animalsuchasfish
171 | concept:cityalsoknownas
172 | concept:inverseofcountriessuchascountries
173 | concept:teamhomestadium
174 | concept:parkincity
175 | concept:superpartof
176 | concept:organizationleadbyperson
177 | concept:companiesheadquarteredhere
178 | concept:academicfieldusedbyeconomicsector
179 | concept:locationlocatedwithinlocation
180 | concept:inverseofbankbankincountry
181 | concept:countryoforganizationheadquarters
182 | concept:motherofperson
183 | concept:bankchiefexecutiveceo
184 | concept:agriculturalproductcamefromcountry
185 | concept:agentholdssharesincompany
186 | concept:clothingmadefromplant
187 | concept:personterminatedbyorganization
188 | concept:cityofpersonbirth
189 | concept:inverseofarthropodlooklikeinsect
190 | concept:academicfieldconcernssubject
191 | concept:politicianusholdsoffice
192 | concept:politicianuswenttoschool
193 | concept:economicsectorusingacademicfield
194 | concept:organizationhasofficialwebsite
195 | concept:automobilemakerdealersincountry
196 | concept:officialwebsiteoforganization
197 | concept:inverseofarthropodfeedoninsect
198 | concept:dateofsportsgame
199 | concept:persondiedincity
200 | concept:organizationterminatedperson
201 | concept:atlocation
202 | concept:stateorprovinceofpersonbirth
203 | concept:inverseofmusicalartisthadapetanimal
204 | concept:locationofpersonbirth
205 | concept:radiostationincity
206 | concept:cityhascompanyoffice
207 | concept:persongraduatedfromuniversity
208 | concept:personhasjobposition
209 | concept:superpartoforganization
210 | concept:geopoliticallocationcontainscountry
211 | concept:countrylocatedingeopoliticallocation
212 | concept:inverseoffooddecreasestheriskofdisease
213 | concept:musicartistmusician
214 | concept:animalsuchasinsect
215 | concept:synonymfor
216 | concept:animalpreyson
217 | concept:inverseofacademicfieldsuchasacademicfield
218 | concept:yearofmarriage
219 | concept:dateofpersondeath
220 | concept:mammalcanbeirritatedbyarthropod
221 | concept:inverseofbeveragemadefrombeverage
222 | concept:inverseofanimaleatvegetable
223 | concept:automobilemakercardealersinstateorprovince
224 | concept:isoneoccurrenceof
225 | concept:itemexistsatlocation
226 | concept:countrycurrency
227 | concept:coachcanspeaklanguage
228 | concept:citylocatedinstate
229 | concept:citysportsteams
230 | concept:attractionofcity
231 | concept:professionistypeofprofession
232 | concept:citylanguage
233 | concept:cityparks
234 | concept:personleadsgeopoliticalorganization
235 | concept:invertebrateturnintoinsect
236 | concept:politicianusendorsedbypoliticianus
237 | concept:stadiumoreventvenuedisclosescompany
238 | concept:animalthatfeedoninsect
239 | concept:sportteam
240 | concept:citycapitalofcountry
241 | concept:televisionstationincity
242 | concept:inverseofagriculturalproductincludingagriculturalproduct
243 | concept:cityofpersondeath
244 | concept:lakeinstate
245 | concept:politicalgroupofpoliticianus
246 | concept:teammate
247 | concept:riverflowsthroughcity
248 | concept:inverseofanimalsuchasfish
249 | concept:organizationalsoknownas
250 | concept:personhascitizenship
251 | concept:inverseofpersongraduatedfromuniversity
252 | concept:arachnidiseatenbybird
253 | concept:personleadsorganization
254 | concept:languageofcity
255 | concept:leaguestadiums
256 | concept:cityattractions
257 | concept:subpartof
258 | concept:animalsuchasinvertebrate
259 | concept:animalssuchasmammals
260 | concept:bodypartwithinbodypart
261 | concept:inverseofanimalsuchasmollusk
262 | concept:transportationincity
263 | concept:automobilemakerchiefexecutiveceo
264 | concept:teamplaysagainstteam
265 | concept:dateoforganzationdissolution
266 | concept:statehasmountain
267 | concept:headquarteredin
268 | concept:leagueteams
269 | concept:mammalsuchasmammal
270 | concept:persongraduatedschool
271 | concept:inverseofathleteinjuredhisbodypart
272 | concept:inverseofstadiumoreventvenuedisclosescompany
273 | concept:competeswith
274 | concept:statelocatedingeopoliticallocation
275 | concept:companyeconomicsector
276 | concept:inverseofautomobilemakerdealersincity
277 | concept:parentofperson
278 | concept:arthropodcalledarthropod
279 | concept:musicianinmusicartist
280 | concept:citycapitalofstate
281 | concept:politicianusmemberofpoliticalgroup
282 | concept:universityoperatesinlanguage
283 | concept:organizationheadquarteredinstateorprovince
284 | concept:countryresidenceofperson
285 | concept:farmlocatedinstate
286 | concept:personhasmother
287 | concept:insecteatsgrain
288 | concept:scenecontainsobject
289 | concept:universityincity
290 | concept:sportsgamedate
291 | concept:locatedat
292 | concept:organizationdissolvedatdate
293 | concept:cityleadbyperson
294 | concept:personwrittenaboutinpublication
295 | concept:agentcompeteswithagent
296 | concept:companyhasshareholder
297 | concept:bankbankincountry
298 | concept:citymuseums
299 | concept:farmproducesagriculturalproduct
300 | concept:athleteledsportsteam
301 | concept:personhasfather
302 | concept:agentworkedondrug
303 | concept:bankboughtbank
304 | concept:cityuniversities
305 | concept:sportsgameteam
306 | concept:agriculturalproductproducedbyfarm
307 | concept:clothingtogowithclothing
308 | concept:personbornincity
309 | concept:inverseofanimalsuchasinvertebrate
310 | concept:inverseofathleteledsportsteam
311 | concept:citycontainsbuilding
312 | concept:agentactsinlocation
313 | concept:airportincity
314 | concept:agentcollaborateswithagent
315 | concept:automodelproducedbymaker
316 | concept:mutualproxyfor
317 | concept:personmovedtostateorprovince
318 | concept:hotelincity
319 | concept:officeheldbypoliticianus
320 | concept:teamcoach
321 | concept:invertebratefeedonfood
322 | concept:roomcancontainitem
323 | concept:dateofpersonbirth
324 | concept:schoolattendedbyperson
325 | concept:atdate
326 | concept:buildinglocatedincity
327 | concept:cityhostsattraction
328 | concept:animaldevelopdisease
329 | concept:hassibling
330 | concept:stadiumlocatedincity
331 | concept:languageofcountry
332 | concept:cityradiostation
333 | concept:leaguecoaches
334 | concept:inverseofarthropodandotherarthropod
335 | concept:locationcontainslocation
336 | concept:countriessuchascountries
337 | concept:inverseofautomobilemakerchiefexecutiveceo
338 | concept:husbandof
339 | concept:universityhasacademicprogram
340 | concept:hasfamilymember
341 | concept:inverseofsportfansincountry
342 | concept:ageofperson
343 | concept:ceoof
344 | concept:inverseofagriculturalproductcamefromcountry
345 | concept:agenthaswebsite
346 | concept:sportschoolincountry
347 | concept:inverseofautomobilemakercardealersinstateorprovince
348 | concept:sportsgamewinner
349 | concept:cityhastransportation
350 | concept:politicianrepresentslocation
351 | concept:personhasage
352 | concept:teamplaysinleague
353 | concept:journalistwritesforpublication
354 | concept:agriculturalproductgrowinginstateorprovince
355 | concept:organizationhasperson
356 | concept:locationactedinbyagent
357 | concept:proxyof
358 | concept:personhiredbyorganization
359 | concept:inverseofpersonmovedtostateorprovince
360 | concept:acquiredby
361 | concept:stadiumhometoleague
362 | concept:animalpredators
363 | concept:professionactsatthesameareaofpoliticissue
364 | concept:cityoforganizationheadquarters
365 | concept:countryalsoknownas
366 | concept:fooddecreasestheriskofdisease
367 | concept:mountaininstate
368 | concept:countrystates
369 | concept:personborninstateorprovince
370 | concept:eventhasparticipantagent
371 | concept:teamingame
372 | concept:inverseofanimaldevelopdisease
373 | concept:languageschoolincity
374 | concept:musicartistgenre
375 | concept:hasofficeincountry
376 | concept:hasspouse
377 | concept:inverseofinvertebrateturnintoinsect
378 | concept:athleteplaysinleague
379 | concept:websiteoperatedbyagent
380 | concept:hashusband
381 | concept:agentcreatedorganization
382 | concept:agriculturalproductincludingagriculturalproduct
383 | concept:currencycountry
384 | concept:agentowns
385 | concept:statecontainscity
386 | concept:automobilemakerdealersincity
387 | concept:agentbelongstoorganization
388 | concept:persondiedincountry
389 | concept:fishservedwithfood
390 | concept:controlledbyagent
391 | concept:coachesinleague
392 | concept:haswife
393 | concept:locationrepresentedbypolitician
394 | concept:ownedbyagent
395 | concept:televisionstationaffiliatedwith
396 | concept:attractionbefallincity
397 | concept:animalistypeofanimal
398 | concept:countryproducesproduct
399 | concept:citylocatedingeopoliticallocation
400 | concept:inverseofbankboughtbank
401 | concept:inverseoffishservedwithfood
402 | concept:statelocatedincountry
403 | concept:teamalsoknownas
404 | concept:inverseofpoliticianuswenttoschool
405 | concept:publicationwritesabout
406 | concept:coachesathlete
407 | concept:organizationhiredperson
408 | concept:isshorterthan
409 | concept:personhasresidenceingeopoliticallocation
410 | concept:inverseofcoachworksincountry
411 | concept:inverseofcountryisthehomeofsportsteam
412 | concept:newspaperincity
413 | concept:animalsuchasmollusk
414 | concept:cityresidenceofperson
415 | concept:geopoliticallocationcontainsstate
416 | concept:writerwasbornincity
417 | concept:animaleatvegetable
418 |
--------------------------------------------------------------------------------
/DATASET/UFB15K237/relation_id.tsv:
--------------------------------------------------------------------------------
1 | 0 /location/country/form_of_government
2 | 1 /tv/tv_program/regular_cast./tv/regular_tv_appearance/actor
3 | 2 /media_common/netflix_genre/titles
4 | 3 /award/award_winner/awards_won./award/award_honor/award_winner
5 | 4 /soccer/football_team/current_roster./sports/sports_team_roster/position
6 | 5 /soccer/football_team/current_roster./soccer/football_roster_position/position
7 | 6 /film/actor/film./film/performance/film
8 | 7 /award/award_category/nominees./award/award_nomination/nominated_for
9 | 8 /award/award_nominee/award_nominations./award/award_nomination/award_nominee
10 | 9 /music/performance_role/regular_performances./music/group_membership/role
11 | 10 /award/award_category/winners./award/award_honor/ceremony
12 | 11 /film/film/release_date_s./film/film_regional_release_date/film_release_distribution_medium
13 | 12 /award/award_winning_work/awards_won./award/award_honor/award_winner
14 | 13 /film/film/release_date_s./film/film_regional_release_date/film_release_region
15 | 14 /film/film/language
16 | 15 /location/location/contains
17 | 16 /organization/organization/headquarters./location/mailing_address/country
18 | 17 /people/person/profession
19 | 18 /location/statistical_region/religions./location/religion_percentage/religion
20 | 19 /award/award_nominee/award_nominations./award/award_nomination/award
21 | 20 /music/genre/artists
22 | 21 /influence/influence_node/influenced_by
23 | 22 /education/educational_institution/students_graduates./education/education/student
24 | 23 /organization/organization_member/member_of./organization/organization_membership/organization
25 | 24 /people/person/place_of_birth
26 | 25 /common/topic/webpage./common/webpage/category
27 | 26 /film/film/other_crew./film/film_crew_gig/film_crew_role
28 | 27 /award/award_ceremony/awards_presented./award/award_honor/award_winner
29 | 28 /film/film/country
30 | 29 /olympics/olympic_sport/athletes./olympics/olympic_athlete_affiliation/country
31 | 30 /award/award_winning_work/awards_won./award/award_honor/award
32 | 31 /film/film/genre
33 | 32 /film/film_distributor/films_distributed./film/film_film_distributor_relationship/film
34 | 33 /location/statistical_region/rent50_2./measurement_unit/dated_money_value/currency
35 | 34 /film/film/estimated_budget./measurement_unit/dated_money_value/currency
36 | 35 /government/legislative_session/members./government/government_position_held/district_represented
37 | 36 /sports/sports_team/roster./american_football/football_historical_roster_position/position_s
38 | 37 /government/government_office_category/officeholders./government/government_position_held/jurisdiction_of_office
39 | 38 /award/award_nominee/award_nominations./award/award_nomination/nominated_for
40 | 39 /people/deceased_person/place_of_death
41 | 40 /sports/pro_athlete/teams./sports/sports_team_roster/team
42 | 41 /music/artist/track_contributions./music/track_contribution/role
43 | 42 /music/instrument/instrumentalists
44 | 43 /people/person/gender
45 | 44 /business/job_title/people_with_this_title./business/employment_tenure/company
46 | 45 /base/popstra/celebrity/friendship./base/popstra/friendship/participant
47 | 46 /business/business_operation/industry
48 | 47 /award/award_category/winners./award/award_honor/award_winner
49 | 48 /people/person/nationality
50 | 49 /sports/sports_team/roster./baseball/baseball_roster_position/position
51 | 50 /people/cause_of_death/people
52 | 51 /tv/tv_program/languages
53 | 52 /music/genre/parent_genre
54 | 53 /base/popstra/celebrity/breakup./base/popstra/breakup/participant
55 | 54 /location/country/capital
56 | 55 /film/special_film_performance_type/film_performance_type./film/performance/film
57 | 56 /location/hud_county_place/place
58 | 57 /music/performance_role/guest_performances./music/recording_contribution/performance_role
59 | 58 /location/location/adjoin_s./location/adjoining_relationship/adjoins
60 | 59 /film/film_subject/films
61 | 60 /base/schemastaging/person_extra/net_worth./measurement_unit/dated_money_value/currency
62 | 61 /education/educational_institution/colors
63 | 62 /base/aareas/schema/administrative_area/administrative_parent
64 | 63 /organization/role/leaders./organization/leadership/organization
65 | 64 /government/legislative_session/members./government/government_position_held/legislative_sessions
66 | 65 /olympics/olympic_games/sports
67 | 66 /people/person/places_lived./people/place_lived/location
68 | 67 /award/award_category/disciplines_or_subjects
69 | 68 /sports/professional_sports_team/draft_picks./sports/sports_league_draft_pick/school
70 | 69 /olympics/olympic_participating_country/medals_won./olympics/olympic_medal_honor/olympics
71 | 70 /language/human_language/countries_spoken_in
72 | 71 /people/ethnicity/people
73 | 72 /olympics/olympic_participating_country/athletes./olympics/olympic_athlete_affiliation/olympics
74 | 73 /olympics/olympic_participating_country/medals_won./olympics/olympic_medal_honor/medal
75 | 74 /film/film/other_crew./film/film_crew_gig/crewmember
76 | 75 /music/performance_role/track_performances./music/track_contribution/role
77 | 76 /music/record_label/artist
78 | 77 /base/popstra/celebrity/dated./base/popstra/dated/participant
79 | 78 /base/locations/continents/countries_within
80 | 79 /sports/sports_league_draft/picks./sports/sports_league_draft_pick/school
81 | 80 /sports/sports_team/sport
82 | 81 /education/educational_institution_campus/educational_institution
83 | 82 /people/person/religion
84 | 83 /organization/organization/headquarters./location/mailing_address/citytown
85 | 84 /film/film/produced_by
86 | 85 /people/person/spouse_s./people/marriage/type_of_union
87 | 86 /film/film/production_companies
88 | 87 /location/country/second_level_divisions
89 | 88 /base/culturalevent/event/entity_involved
90 | 89 /education/educational_degree/people_with_this_degree./education/education/institution
91 | 90 /film/film/distributors./film/film_film_distributor_relationship/film_distribution_medium
92 | 91 /tv/tv_program/tv_producer./tv/tv_producer_term/producer_type
93 | 92 /music/performance_role/regular_performances./music/group_membership/group
94 | 93 /government/politician/government_positions_held./government/government_position_held/legislative_sessions
95 | 94 /sports/sports_team_location/teams
96 | 95 /sports/sports_team/colors
97 | 96 /sports/professional_sports_team/draft_picks./sports/sports_league_draft_pick/draft
98 | 97 /award/award_nominated_work/award_nominations./award/award_nomination/nominated_for
99 | 98 /baseball/baseball_team/team_stats./baseball/baseball_team_stats/season
100 | 99 /user/alexander/philosophy/philosopher/interests
101 | 100 /people/person/languages
102 | 101 /education/educational_institution/students_graduates./education/education/major_field_of_study
103 | 102 /film/director/film
104 | 103 /award/award_ceremony/awards_presented./award/award_honor/honored_for
105 | 104 /olympics/olympic_sport/athletes./olympics/olympic_athlete_affiliation/olympics
106 | 105 /film/film/costume_design_by
107 | 106 /film/film/featured_film_locations
108 | 107 /tv/tv_program/genre
109 | 108 /organization/endowed_organization/endowment./measurement_unit/dated_money_value/currency
110 | 109 /film/film/written_by
111 | 110 /tv/tv_producer/programs_produced./tv/tv_producer_term/producer_type
112 | 111 /location/hud_county_place/county
113 | 112 /education/educational_degree/people_with_this_degree./education/education/major_field_of_study
114 | 113 /base/schemastaging/organization_extra/phone_number./base/schemastaging/phone_sandbox/contact_category
115 | 114 /base/saturdaynightlive/snl_cast_member/seasons./base/saturdaynightlive/snl_season_tenure/cast_members
116 | 115 /film/film/film_format
117 | 116 /film/film/music
118 | 117 /sports/sports_team/roster./american_football/football_roster_position/position
119 | 118 /user/tsegaran/random/taxonomy_subject/entry./user/tsegaran/random/taxonomy_entry/taxonomy
120 | 119 /celebrities/celebrity/celebrity_friends./celebrities/friendship/friend
121 | 120 /location/hud_foreclosure_area/estimated_number_of_mortgages./measurement_unit/dated_integer/source
122 | 121 /base/x2010fifaworldcupsouthafrica/world_cup_squad/current_world_cup_squad./base/x2010fifaworldcupsouthafrica/current_world_cup_squad/current_club
123 | 122 /base/popstra/location/vacationers./base/popstra/vacation_choice/vacationer
124 | 123 /base/popstra/celebrity/canoodled./base/popstra/canoodled/participant
125 | 124 /education/educational_institution/school_type
126 | 125 /sports/sport/pro_athletes./sports/pro_sports_played/athlete
127 | 126 /award/hall_of_fame/inductees./award/hall_of_fame_induction/inductee
128 | 127 /base/biblioness/bibs_location/state
129 | 128 /education/university/local_tuition./measurement_unit/dated_money_value/currency
130 | 129 /business/business_operation/revenue./measurement_unit/dated_money_value/currency
131 | 130 /sports/sports_position/players./sports/sports_team_roster/team
132 | 131 /military/military_conflict/combatants./military/military_combatant_group/combatants
133 | 132 /sports/sports_league/teams./sports/sports_league_participation/team
134 | 133 /base/biblioness/bibs_location/country
135 | 134 /music/group_member/membership./music/group_membership/role
136 | 135 /location/administrative_division/country
137 | 136 /film/film/executive_produced_by
138 | 137 /food/food/nutrients./food/nutrition_fact/nutrient
139 | 138 /music/group_member/membership./music/group_membership/group
140 | 139 /location/statistical_region/gdp_nominal_per_capita./measurement_unit/dated_money_value/currency
141 | 140 /education/field_of_study/students_majoring./education/education/student
142 | 141 /award/award_winning_work/awards_won./award/award_honor/honored_for
143 | 142 /sports/sports_team/roster./basketball/basketball_roster_position/position
144 | 143 /organization/organization/headquarters./location/mailing_address/state_province_region
145 | 144 /base/aareas/schema/administrative_area/capital
146 | 145 /tv/tv_producer/programs_produced./tv/tv_producer_term/program
147 | 146 /education/educational_institution/campuses
148 | 147 /film/film/distributors./film/film_film_distributor_relationship/region
149 | 148 /travel/travel_destination/climate./travel/travel_destination_monthly_climate/month
150 | 149 /medicine/disease/notable_people_with_this_condition
151 | 150 /base/schemastaging/organization_extra/phone_number./base/schemastaging/phone_sandbox/service_location
152 | 151 /people/ethnicity/geographic_distribution
153 | 152 /military/military_combatant/military_conflicts./military/military_combatant_group/combatants
154 | 153 /film/film/personal_appearances./film/personal_film_appearance/person
155 | 154 /location/location/time_zones
156 | 155 /film/film/story_by
157 | 156 /base/petbreeds/city_with_dogs/top_breeds./base/petbreeds/dog_city_relationship/dog_breed
158 | 157 /olympics/olympic_games/participating_countries
159 | 158 /tv/tv_network/programs./tv/tv_network_duration/program
160 | 159 /people/person/employment_history./business/employment_tenure/company
161 | 160 /music/artist/origin
162 | 161 /people/marriage_union_type/unions_of_this_type./people/marriage/location_of_ceremony
163 | 162 /tv/tv_writer/tv_programs./tv/tv_program_writer_relationship/tv_program
164 | 163 /time/event/locations
165 | 164 /celebrities/celebrity/sexual_relationships./celebrities/romantic_relationship/celebrity
166 | 165 /government/political_party/politicians_in_this_party./government/political_party_tenure/politician
167 | 166 /education/university/domestic_tuition./measurement_unit/dated_money_value/currency
168 | 167 /organization/non_profit_organization/registered_with./organization/non_profit_registration/registering_agency
169 | 168 /user/ktrueman/default_domain/international_organization/member_states
170 | 169 /american_football/football_team/current_roster./sports/sports_team_roster/position
171 | 170 /soccer/football_player/current_team./sports/sports_team_roster/team
172 | 171 /olympics/olympic_games/medals_awarded./olympics/olympic_medal_honor/medal
173 | 172 /award/ranked_item/appears_in_ranked_lists./award/ranking/list
174 | 173 /film/film_set_designer/film_sets_designed
175 | 174 /user/jg/default_domain/olympic_games/sports
176 | 175 /people/person/sibling_s./people/sibling_relationship/sibling
177 | 176 /base/marchmadness/ncaa_basketball_tournament/seeds./base/marchmadness/ncaa_tournament_seed/team
178 | 177 /music/artist/contribution./music/recording_contribution/performance_role
179 | 178 /sports/sports_position/players./sports/sports_team_roster/position
180 | 179 /government/politician/government_positions_held./government/government_position_held/basic_title
181 | 180 /sports/sports_position/players./american_football/football_historical_roster_position/position_s
182 | 181 /people/profession/specialization_of
183 | 182 /award/award_category/category_of
184 | 183 /film/film/cinematography
185 | 184 /film/film/film_production_design_by
186 | 185 /tv/tv_program/country_of_origin
187 | 186 /film/film/edited_by
188 | 187 /tv/tv_personality/tv_regular_appearances./tv/tv_regular_personal_appearance/program
189 | 188 /education/field_of_study/students_majoring./education/education/major_field_of_study
190 | 189 /education/educational_degree/people_with_this_degree./education/education/student
191 | 190 /location/us_county/county_seat
192 | 191 /film/film/film_festivals
193 | 192 /people/person/spouse_s./people/marriage/spouse
194 | 193 /film/film/runtime./film/film_cut/film_release_region
195 | 194 /location/capital_of_administrative_division/capital_of./location/administrative_division_capital_relationship/administrative_division
196 | 195 /base/aareas/schema/administrative_area/administrative_area_type
197 | 196 /influence/influence_node/peers./influence/peer_relationship/peers
198 | 197 /government/governmental_body/members./government/government_position_held/legislative_sessions
199 | 198 /base/schemastaging/organization_extra/phone_number./base/schemastaging/phone_sandbox/service_language
200 | 199 /organization/organization/place_founded
201 | 200 /base/eating/practicer_of_diet/diet
202 | 201 /tv/tv_program/program_creator
203 | 202 /people/deceased_person/place_of_burial
204 | 203 /film/film/release_date_s./film/film_regional_release_date/film_regional_debut_venue
205 | 204 /ice_hockey/hockey_team/current_roster./sports/sports_team_roster/position
206 | 205 /location/administrative_division/first_level_division_of
207 | 206 /location/statistical_region/gdp_real./measurement_unit/adjusted_money_value/adjustment_currency
208 | 207 /organization/organization/child./organization/organization_relationship/child
209 | 208 /business/business_operation/assets./measurement_unit/dated_money_value/currency
210 | 209 /film/person_or_entity_appearing_in_film/films./film/personal_film_appearance/type_of_appearance
211 | 210 /time/event/instance_of_recurring_event
212 | 211 /tv/non_character_role/tv_regular_personal_appearances./tv/tv_regular_personal_appearance/person
213 | 212 /film/actor/film./film/performance/special_performance_type
214 | 213 /business/business_operation/operating_income./measurement_unit/dated_money_value/currency
215 | 214 /people/ethnicity/languages_spoken
216 | 215 /base/americancomedy/celebrity_impressionist/celebrities_impersonated
217 | 216 /medicine/symptom/symptom_of
218 | 217 /education/university/fraternities_and_sororities
219 | 218 /location/statistical_region/places_exported_to./location/imports_and_exports/exported_to
220 | 219 /location/statistical_region/gdp_nominal./measurement_unit/dated_money_value/currency
221 | 220 /travel/travel_destination/how_to_get_here./travel/transportation/mode_of_transportation
222 | 221 /location/country/official_language
223 | 222 /location/location/partially_contains
224 | 223 /film/film/film_art_direction_by
225 | 224 /people/person/spouse_s./people/marriage/location_of_ceremony
226 | 225 /education/university/international_tuition./measurement_unit/dated_money_value/currency
227 | 226 /organization/organization_founder/organizations_founded
228 | 227 /film/film/dubbing_performances./film/dubbing_performance/actor
229 | 228 /dataworld/gardening_hint/split_to
230 | 229 /medicine/disease/risk_factors
231 | 230 /film/film/prequel
232 | 231 /base/localfood/seasonal_month/produce_available./base/localfood/produce_availability/seasonal_months
233 | 232 /film/actor/dubbing_performances./film/dubbing_performance/language
234 | 233 /broadcast/content/artist
235 | 234 /location/statistical_region/gni_per_capita_in_ppp_dollars./measurement_unit/dated_money_value/currency
236 | 235 /music/instrument/family
237 | 236 /government/politician/government_positions_held./government/government_position_held/jurisdiction_of_office
238 |
--------------------------------------------------------------------------------
/DATASET/confidece_analyse.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [],
8 | "source": [
9 | "from tqdm import tqdm"
10 | ]
11 | },
12 | {
13 | "cell_type": "code",
14 | "execution_count": 2,
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "file1 = 'CN15k/train.txt'\n",
19 | "file2 = 'NL27K/train.txt'\n",
20 | "file3 = 'UWN18RR/train2id.tsv'\n",
21 | "file4 = 'UFB15K237/train2id.tsv'"
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": 3,
27 | "metadata": {},
28 | "outputs": [
29 | {
30 | "name": "stderr",
31 | "output_type": "stream",
32 | "text": [
33 | "100%|██████████| 204984/204984 [00:00<00:00, 210743.55it/s]\n",
34 | "100%|██████████| 149100/149100 [00:00<00:00, 217059.48it/s]\n",
35 | "100%|██████████| 94863/94863 [00:00<00:00, 221649.82it/s]\n",
36 | "100%|██████████| 316319/316319 [00:01<00:00, 235975.54it/s]\n"
37 | ]
38 | }
39 | ],
40 | "source": [
41 | "with open(file1,'r') as f1, open(file2,'r') as f2,open(file3,'r') as f3, open(file4,'r') as f4:\n",
42 | " lines1 = f1.readlines()\n",
43 | " lines2 = f2.readlines()\n",
44 | " lines3 = f3.readlines()\n",
45 | " lines4 = f4.readlines()\n",
46 | " data1,data2,data3,data4 = [],[],[],[]\n",
47 | " \n",
48 | " for line in tqdm(lines1):\n",
49 | " data1.append(eval(line.strip().split('\\t')[-1]))\n",
50 | " for line in tqdm(lines2):\n",
51 | " data2.append(eval(line.strip().split('\\t')[-1]))\n",
52 | " for line in tqdm(lines3):\n",
53 | " data3.append(eval(line.strip().split('\\t')[-1]))\n",
54 | " for line in tqdm(lines4):\n",
55 | " data4.append(eval(line.strip().split('\\t')[-1]))"
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": 18,
61 | "metadata": {},
62 | "outputs": [
63 | {
64 | "name": "stderr",
65 | "output_type": "stream",
66 | "text": [
67 | "The PostScript backend does not support transparency; partially transparent artists will be rendered opaque.\n"
68 | ]
69 | },
70 | {
71 | "data": {
72 | "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAMVCAYAAACm0EewAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAADD9UlEQVR4nOzde1xVdb7/8feWyxZJtggBkphWSBpaiolohaaAjkjmzFhDUZahhUkUZJldtEnMu42UqVmaaHRmHDulE6GlzHC8MzKTl9FmxrwkqCVu1GiDuH5/9Gj92oLXYHPx9Xw81uNx9vp+1lqftdaZ+PjZ372WxTAMQwAAAAAAAIALNavvBAAAAAAAAHD1oSkFAAAAAAAAl6MpBQAAAAAAAJejKQUAAAAAAACXoykFAAAAAAAAl6MpBQAAAAAAAJejKQUAAAAAAACXoykFAAAAAAAAl6MpBQAAAAAAAJejKQWgSfnnP/+pRx55RB06dFDz5s11zTXXqHv37po2bZqOHz8uSerbt68sFosGDhxYbfuvv/5aFotFM2bMcFr/4osvKj4+Xtddd50sFotGjBhR4/EnTpwoi8VSbWnevHm1WIvFoieffLLa+ldeeUUWi0VPPPGEzp49ewVXAQAAoLrFixebdcn+/furjfft21fh4eHm5/bt2ys+Pv6C+3znnXc0dOhQtW/fXl5eXrrpppv0xBNPqLi42Clu/fr1NdZIPy2PP/54tTy3bdvmtI9vv/1WPXr00DXXXKM1a9ZcySUA0MC413cCAFBbFi5cqJSUFIWFhenZZ59V586dVVlZqW3btuntt9/Wxo0btXLlSjP+s88+0xdffKG77777ovuePXu2unbtqoSEBL377rsXjc/NzZXNZjM/N2t28e8ADMPQU089pblz5+r555/XlClTLroNAADA5XI4HHrxxRe1dOnSX7yvV155Rf369VNmZqauu+467dmzR7///e/1v//7v9q+fbsCAwMlSd27d9fGjRurbT9v3jy9//77uvfeey94nEOHDikmJkZHjhzR2rVr1atXr1+cO4D6R1MKQJOwceNGPfHEE4qJidFHH30kq9VqjsXExCg9PV25ubnmuo4dO+rMmTMaN26ctm7dKovFcsH9nzx50mwsXUoBFxERIX9//0vO/8yZM3r00Ue1dOlSTZ8+XRkZGZe8LQAAwOUYOHCgli9froyMDN16662/aF/bt29XQECA+Tk6Olrdu3fX7bffroULF+rFF1+UJPn4+FRrJBmGoQceeEDXX3+9YmJiznuMr776SgMGDFBlZaXy8/PVpUuXX5QzgIaDn+8BaBIyMzNlsVi0YMECp4bUTzw9PZWQkGB+9vDw0OTJk1VYWKgPP/zwovu/lJlOV+qHH37Qr3/9ay1fvlzvvPMODSkAAFCnxo0bJz8/Pz333HO/eF8/b0j9JCIiQm5ubjp48OAFt123bp3++9//6pFHHjlvrVVUVKQ77rhD7u7uKigooCEFNDE0pQA0elVVVfriiy8UERGhkJCQS97uvvvuU0REhF588UVVVlbWak5dunSRm5ubAgMD9dBDD+nAgQM1xp08eVKDBg1Sbm6uPvzwQ40cObJW8wAAADhXy5Yt9eKLL5qPMqht+fn5qqqq0i233HLBuEWLFqlZs2Z65JFHahwvKChQ3759FRAQoIKCAt1www21niuA+kVTCkCj9+233+r7779Xhw4dLms7i8WiqVOn6j//+Y/mz59fK7nceOONmjx5st59912tXbtWzzzzjFavXq2ePXvqm2++qRb//vvva/369crKytKvf/3rWskBAADgYh5//HHdcMMNeu6552QYRq3t9+TJk0pJSVFISIgeffTR88adOHFCf/7znxUTE6N27drVGPP0009Lkr744gu1adOm1nIE0HDQlAJwVevfv79iY2P16quv6uTJk794f0lJSXrhhRc0aNAg9evXT88995w+/fRTHTt2TNOmTasWf+edd6pVq1aaNGmS/v3vf//i4wMAAFwKT09Pvfbaa9q2bZv+53/+p1b2+cMPP2jYsGHav3+//vjHP+qaa645b+yyZcv0ww8/6LHHHjtvTEJCgux2u9LS0lRVVVUrOQJoWGhKAWj0/P391aJFC+3bt++Ktp86daq+/fZbzZgxo5Yz+1HPnj3VsWNHbdq0qdpY165dtXbtWn3//feKjo7W3r176yQHAACAc91///3q3r27JkyY8IsfZeBwOHTvvfeqoKBAH3/8sSIjIy8Yv2jRIl177bW65557zhvz0ksv6eWXX9by5cv14IMP0pgCmiCaUgAaPTc3N/Xv31+FhYU6dOjQZW9/22236Xe/+51mzZqlI0eO1EGGP75d5nwP8IyIiNDatWv1ww8/qF+/ftqzZ0+d5AAAAPBz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bj9ixAi1b9++zvIDAACQqJUANF3u9Z0AgKvbwoULlZKSorCwMD377LPq3LmzKisrtW3bNr399tvauHGjVq5cWSfHfvTRR3X69Gnl5OTI19dX7du3V4sWLbRx40Z17ty5To4JAABwOaiVADRlFsMwjPpOAsDVaePGjbrzzjsVExOjjz76SFar1Wm8oqJCubm5SkhIqJPje3h4KDk5WW+99dYVbT9ixAitX79eX3/9de0mBgAAIGolAE0fP98DUG8yMzNlsVi0YMGCakWWJHl6eppF1tmzZzVt2jTdfPPNslqtCggI0EMPPaRDhw45bdO3b1+Fh4dr69atuvPOO9WiRQvdcMMNev3113X27FlJ0uLFi2WxWHTmzBnNmzdPFotFFotFks47JX3x4sUKCwuT1WpVp06d9P7779d4ThUVFXrttdfMPK+99lo98sgjOnbsmFNc+/btFR8fr9zcXHXv3l1eXl66+eab9e6771bb5zfffKNRo0YpJCREnp6eCg4O1m9+8xsdOXLEjCkrK1NGRoY6dOggT09PXXfddUpLS9Pp06cvchcAAEBDRa1ErQQ0eQYA1IMzZ84YLVq0MCIjIy8pftSoUYYk48knnzRyc3ONt99+27j22muNkJAQ49ixY2ZcdHS04efnZ4SGhhpvv/22sWbNGiMlJcWQZCxZssQwDMM4evSosXHjRkOS8Zvf/MbYuHGjsXHjRsMwDGPdunWGJGPdunXmPt977z1DknHPPfcYn3zyiZGdnW3cdNNNRkhIiHH99debcVVVVcbAgQMNb29vY9KkScaaNWuMd955x7juuuuMzp07G99//70Ze/311xtt27Y1OnfubLz//vvGZ599Zvz2t781JBn5+flm3KFDh4w2bdoY/v7+xqxZs4y1a9caH374ofHoo48au3fvNgzDME6fPm3cdtttTjFvvPGGYbPZjLvvvts4e/bsZd8fAABQv6iVqJWAqwFNKQD1oqSkxJBk3H///ReN3b17tyHJSElJcVq/efNmQ5LxwgsvmOuio6MNScbmzZudYjt37mzExcU5rZNkjBkzxmnduYVWVVWVERwcbHTv3t2pYPn6668NDw8Pp0Lrgw8+MCQZK1ascNrn1q1bDUnGW2+9Za67/vrrjebNmxv79+8315WXlxutW7c2Ro8eba579NFHDQ8PD2PXrl3nvT5TpkwxmjVr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JkYREREqLi7W7bffLkmqqanRpk2b9NJLL0mSEhMT5ePjo+LiYo0cOVKSVF5erj179mjGjBmSpOTkZNntdu3YsUM9e/aUJG3fvl12u129e/c2Y6ZOnary8nJFRkZKkoqKimS1WpWYmOjWfqN5MAMeAOBqLaYodfr0af3qV7/SwoULNWXKFHO/O1eduRiHwyGHw2G+5uGdAADAlU6fPq0vvvjCfH3w4EHt3r1bwcHB6tixo7KzszVt2jR16dJFXbp00bRp09SuXTulp6dLkmw2m8aMGaOJEycqJCREwcHBysnJUUJCgjku6tq1qwYPHqzMzEzNnz9f0g8351JTU80xUEpKirp166aMjAy9/PLLOnHihHJycpSZmcnsJwAA0Cgt5ut7jz/+uIYOHWoOns5z56ozF8PDOwEAQFP65JNPdPvtt5szoSZMmKDbb79dv//97yVJzzzzjLKzs5WVlaUePXrom2++UVFRkTlbXJJmzZql4cOHa+TIkerTp4/atWun1atXm7PFJWnZsmVKSEhQSkqKUlJSdMstt2jp0qXmcS8vL61du1Z+fn7q06ePRo4cqeHDh+uVV15x0ycBAABamxYxU6qgoED/+Mc/tHPnznrHLrfqzKFDh8wYV6w6czE8vBMAADSlfv36yTCMSx63WCzKzc1Vbm7uJWP8/PyUl5envLy8S8YEBwcrPz//srl07NhRa9asuWLOAAAAV8Pji1JHjhzRk08+qaKiIvn5+V0yzl2rzlzIarXKarVe9jwAAAAAAABw5vFf3yspKVFlZaUSExPl7e0tb29vbdq0SX/84x/l7e1tzpByx6ozAAAAAAAAcA2PnynVv39/ffbZZ077Hn74Yd1888367W9/q5tuusltq84AQFNy5apGrGgEAGgpWNUPANoujy9KBQYGKj4+3mlfQECAQkJCzP3uWnUGAAAAAAAAruHxRamr8cwzz6i6ulpZWVmqqqpSUlLSRVed8fb21siRI1VdXa3+/ftryZIl9VadGT9+vLlKX1pamubMmeP2/gAAAAAAALR2LbIotXHjRqfX7lx1BgAAAAAAANfO4x90DgAAAAAAgNaHohQAAAAAAADcjqIUAAAAAAAA3I6iFAAAAAAAANyOohQAAAAAAADcjqIUAAAAAAAA3I6iFAAAAAAAANzOu7kTABprVvEBl7X11MBYl7UFAAAAAACu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c+fOSk1NVWFhoe644w75+/vr5ptv1ptvvlmvzW+++UZjx45VdHS0fH19FRUVpQceeEBHjx41Y06dOqWcnBzFxMTI19dXN954o7Kzs3XmzJkrXAUAAOCpGCsxVgJaPQMAmsHZs2eNdu3aGUlJSVcVP3bsWEOS8cQTTxiFhYXGG2+8Ydxwww1GdHS0cezYMTOub9++RkhIiNGlSxfjjTfeMIqLi42srCxDkvHWW28ZhmEYlZWVxtatWw1JxgMPPGBs3brV2Lp1q2EYhrFhwwZDkrFhwwazzcWLFxuSjPvuu89YvXq1kZ+fb/zHf/yHER0dbXTq1MmMq6urMwYPHmwEBAQYL7zwglFcXGz86U9/Mm688UajW7duxr/+9S8ztlOnTkaHDh2Mbt26GW+//bbx17/+1fjP//xPQ5KxadMmM66srMyIjIw0QkNDjZkzZxrr16833nnnHeORRx4x9u/fbxiGYZw5c8a47bbbnGJee+01w2azGffee69x7ty5Bl8fAADQvBgrMVYC2gKKUgCaRUVFhSHJeOihh64Yu3//fkOSkZWV5bR/+/bthiTjueeeM/f17dvXkGRs377dKbZbt27GoEGDnPZJMh5//HGnfRcOtOrq6oyoqCjjjjvucBqwfP3114aPj4/TQGvFihWGJOPdd991anPnzp2GJGPu3Lnmvk6dOhl+fn7GoUOHzH3V1dVGcHCw8dhjj5n7HnnkEcPHx8fYt2/fJT+f6dOnG9ddd52xc+dOp/1//vOfDUnGBx98cMn3AgAAz8RYibES0Bbw9T0AHm/Dhg2SfnguwY/17NlTXbt21d/+9jen/REREerZs6fTvltuuUWHDh1q8LlLS0v17bffKj093Zy2LkmdOnVS7969nWLXrFmj66+/XsOGDdPZs2fN7bbbblNERES9ae633XabOnbsaL728/NTbGysU57r1q3TPffco65du14yxzVr1ig+Pl633Xab03kHDRp01avjAACAlouxEmMloKVi9T0AzSI0NFTt2rXTwYMHrxh7/PhxSVJkZGS9Y1FRUfUGUCEhIfXirFarqqurG5zn+XNHRETUOxYREeH04M6jR4/q5MmT8vX1vWhb3333XYPzPHbsmDp06HDZHI8ePaovvvhCPj4+V3VeAADg+RgrMVYC2gKKUgCahZeXl/r3769169aprKzssoOJ8wOS8vLyenHffvutQkNDmyzP8+euqKiod+zCfaGhoQoJCVFhYeFF2woMDGzw+W+44YZ6Dyi9UGhoqPz9/S/64M/zxwEAQMvCWOnqMFYCWja+vgeg2UyaNEmGYSgzM1M1NTX1jtfW1mr16tW69957JUn5+flOx3fu3Kn9+/erf//+TZZjXFycIiMjtWLFChmGYe4/dOiQtmzZ4hSbmpqq48ePq66uTj169Ki3xcXFNfj8Q4YM0YYNG1RaWnrJmNTUVH355ZcKCQm56Hk7d+7c4PMCAIDmx1jpyhgrAS0bM6UANJvk5GTNmzdPWVlZSkxM1P/7f/9P3bt3V21trXbt2qUFCxYoPj5eK1eu1NixY5WXl6frrrtOQ4YM0ddff63f/e53io6O1lNPPdVkOV533XX6n//5Hz366KO6//77lZmZqZMnTyo3N7feNPWHHnpIy5Yt089//nM9+eST6tmzp3x8fFRWVqYNGzbovvvu0/3339+g87/44otat26d7r77bj333HNKSEjQyZMnVVhYqAkTJujmm29Wdna23n33Xd1999166qmndMstt+jcuXM6fPiwioqKNHHiRCUlJbnyYwEAAG7AWOnKGCsBLRtFKQDNKjMzUz179tSsWbP00ksvqaKiQj4+PoqNjVV6erqeeOIJSdK8efP005/+VIsWLdLrr78um82mwYMHa/r06Rd93oArjRkzRpL00ksvacSIEercubOee+45bdq0yenBmF5eXlq1apVee+01LV26VNOnT5e3t7c6dOigvn37KiEhocHnvvHGG7Vjxw49//zz+sMf/qDjx4/rhhtu0F133aXg4GBJUkBAgD7++GP94Q9/0IIFC3Tw4EH5+/urY8eOGjBgAHf/AABowRgrXR5jJaBlsxg/nmMJAAAAAAAAuAHPlAIAAAAAAIDbUZQCAAAAAACA21GUAgAAAAAAgNtRlAIAAAAAAIDbUZQCAAAAAACA21GUAgAAAAAAgNtRlAIAAAAAAIDbUZQCAAAAAACA21GUAgAAAAAAgNtRlAIAAAAAAIDbUZQCAAAAAACA2/1/+LejZ63Ou8kAAAAASUVORK5CYII=",
73 | "text/plain": [
74 | ""
75 | ]
76 | },
77 | "metadata": {},
78 | "output_type": "display_data"
79 | }
80 | ],
81 | "source": [
82 | "import matplotlib.pyplot as plt\n",
83 | "\n",
84 | "# 假设 data1 到 data4 是你的四个数据列表\n",
85 | "# 设置子图布局为 2x2\n",
86 | "fig, axs = plt.subplots(2, 2, figsize=(12, 8)) # 2 行 2 列\n",
87 | "# 设置 x 轴坐标范围从 0 到 1\n",
88 | "plt.xlim(0, 1)\n",
89 | "\n",
90 | "# 绘制第一个子图(左上角)\n",
91 | "axs[0, 0].hist(data1, bins=20, alpha=0.5, width=0.04)\n",
92 | "axs[0, 0].set_title('CN15K')\n",
93 | "axs[0, 0].set_xlabel('Confidence', fontsize=12)\n",
94 | "axs[0, 0].set_ylabel('Count', fontsize=12)\n",
95 | "\n",
96 | "# 绘制第二个子图(右上角)\n",
97 | "axs[0, 1].hist(data2, bins=20, alpha=0.5, width=0.04)\n",
98 | "axs[0, 1].set_title('NL27K')\n",
99 | "axs[0, 1].set_xlabel('Confidence', fontsize=12)\n",
100 | "axs[0, 1].set_ylabel('Count', fontsize=12)\n",
101 | "\n",
102 | "# 绘制第三个子图(左下角)\n",
103 | "axs[1, 0].hist(data3, bins=20, alpha=0.5, width=0.03)\n",
104 | "axs[1, 0].set_title('UWN18RR')\n",
105 | "axs[1, 0].set_xlabel('Confidence', fontsize=12)\n",
106 | "axs[1, 0].set_ylabel('Count', fontsize=12)\n",
107 | "\n",
108 | "# 绘制第四个子图(右下角)\n",
109 | "axs[1, 1].hist(data4, bins=20, alpha=0.5, width=0.04)\n",
110 | "axs[1, 1].set_title('UFB15K237')\n",
111 | "axs[1, 1].set_xlabel('Confidence', fontsize=12)\n",
112 | "axs[1, 1].set_ylabel('Count', fontsize=12)\n",
113 | "\n",
114 | "# 调整子图之间的间距\n",
115 | "plt.tight_layout()\n",
116 | "\n",
117 | "plt.savefig('histogram.eps', format='eps')\n",
118 | "# 显示图形\n",
119 | "plt.show()\n"
120 | ]
121 | },
122 | {
123 | "cell_type": "code",
124 | "execution_count": null,
125 | "metadata": {},
126 | "outputs": [],
127 | "source": []
128 | }
129 | ],
130 | "metadata": {
131 | "kernelspec": {
132 | "display_name": "pytorch",
133 | "language": "python",
134 | "name": "python3"
135 | },
136 | "language_info": {
137 | "codemirror_mode": {
138 | "name": "ipython",
139 | "version": 3
140 | },
141 | "file_extension": ".py",
142 | "mimetype": "text/x-python",
143 | "name": "python",
144 | "nbconvert_exporter": "python",
145 | "pygments_lexer": "ipython3",
146 | "version": "3.8.18"
147 | }
148 | },
149 | "nbformat": 4,
150 | "nbformat_minor": 2
151 | }
152 |
--------------------------------------------------------------------------------