Home

Awesome

高级实训-作业三

18342066 鲁沛

TTransE

1. 原理介绍

Temporal Knowledge Graph (TKG)

在知识图谱的三元组表示基础上,添加了时间这一维度的考量,形成了新的四元组:(s, r, o, t)。

例如:(Barack Obama, visits, Ukraine, 2014-07-08)

TTransE

由于多了时间维度的考量,TTransE 的得分函数变为:

其余方面则与 TransE 没有较大区别,由于上次作业已经详细介绍了 TransE 的原理,因此本次不再赘述。

2. 模型细节

数据预处理

首先将icews14 训练数据集中的实体、关系、时间都转化为 str 和 int 相对应的字典序,即赋予每个实体、关系、时间一个 id 来代表它,训练结束后生成的表征实体、关系、时间的向量也是和其 id 相对应的。

entity_set = set()
relation_set = set()
time_set = set()
file_1 = "icews14\\icews_2014_train.txt"
file_2 = "icews14\\entity2id.txt"
file_3 = "icews14\\relation2id.txt"
file_4 = "icews14\\time2id.txt"

with open(file_1, 'r', encoding='utf-8') as f1:
    lines = f1.readlines()
    for line in lines:
        line = line.strip().split('\t')
        if len(line) != 4:
            continue
        entity_set.add(line[0])
        relation_set.add(line[1])
        entity_set.add(line[2])
        time_set.add(line[3])

i = 0
with open(file_2, 'w', encoding='utf-8') as f2:
    for entity in entity_set:
        text = entity + '\t' + str(i) + '\n'
        f2.write(text)
        i += 1

i = 0
with open(file_3, 'w', encoding='utf-8') as f3:
    for relation in relation_set:
        text = relation + '\t' + str(i) + '\n'
        f3.write(text)
        i += 1

i = 0
with open(file_4, 'w', encoding='utf-8') as f4:
    for time in time_set:
        text = time + '\t' + str(i) + '\n'
        f4.write(text)
        i += 1

这里会遇到一个问题:测试集中有些数据是在训练集中没有出现过的,因此训练结束后并没有得到这些新数据相对应的 id 和向量。因此在测试数据集中测试的时候如果遇到新的数据,则给其赋予新的 id,并直接生成一个随机向量来表征它。

 # 对于 test 中未在 train 中出现过的 entity,relation 和 time,对其进行随机初始化
if quadruple[0] in entity2id:
    s_ = entity2id[quadruple[0]]
else:
    entity2id[quadruple[0]] = len_e
    s_ = len_e
    entity_dict[s_] = np.random.uniform(-6 / math.sqrt(50), 6 / math.sqrt(50), 50)
    len_e += 1

if quadruple[1] in relation2id:
    r_ = relation2id[quadruple[1]]
else:
    relation2id[quadruple[1]] = len_r
    r_ = len_r
    relation_dict[r_] = np.random.uniform(-6 / math.sqrt(50), 6 / math.sqrt(50), 50)
    len_r += 1

if quadruple[2] in entity2id:
    o_ = entity2id[quadruple[2]]
else:
    entity2id[quadruple[2]] = len_e
    o_ = len_e
    entity_dict[o_] = np.random.uniform(-6 / math.sqrt(50), 6 / math.sqrt(50), 50)
    len_e += 1

if quadruple[3] in time2id:
    t_ = time2id[quadruple[3]]
else:
    time2id[quadruple[3]] = len_t
    t_ = len_t
    time_dict[t_] = np.random.uniform(-6 / math.sqrt(50), 6 / math.sqrt(50), 50)
    len_t += 1

test_quadruple.append([s_, r_, o_, t_])

数据读入

将通过数据预处理得到的 entity2id,relation2id,time2id 读入,并且读入训练数据

距离函数

距离通过 L1 范数或 L2 范数来表征

def distanceL2(s, r, o, t):
    # 为方便求梯度,去掉sqrt
    return np.sum(np.square(s + r + t - o))


def distanceL1(s, r, o, t):
    return np.sum(np.fabs(s + r + t - o))

类的设计

class TTransE:
    # 构造函数,确定一些超参数
    def __init__(self, entity_set, relation_set, time_set, quadruple_list,
                 embedding_dim=100, learning_rate=0.01, margin=1, L1=True)
    # 表征向量采用随机的方式初始化
    def emb_initialize(self)
    # 训练过程
    def train(self, epochs)
    # 得到负例
    def Corrupt(self, quadruple)
    # 向量更新函数
    def update_embeddings(self, Tbatch)
    # 计算 loss
    def hinge_loss(self, dist_correct, dist_corrupt)

参数初始化

损失函数

和 TTransE 基本一致: $$ L=\gamma+d(s+r+t,o)-d(s'+r+t,o') $$

3. 实验结果

训练过程结束后,loss = 1169.334723892541

测试集上的测试结果:

entity hits@1: 0.05902041727100301
entity hits@3: 0.1621109003681803
entity hits@10: 0.3238313064822046
entity mean rank: 349.98850831194915

可以看到,结果并不是很好,可以在超参数的设置上进一步探索更优的实现。