DCRE标签生成器的实现

基于OpenNRE框架和文献[1]中对DCRE的描述,实现DCRE。

编码器的修改

需要预训练出一个PCNN+ATT的模型,使用其中的编码器。

输入数据格式的修改

opennre.framework.BagRE中的dataloader(train_loader, val_loader, test_loader)与ptdec.model的构造函数的输入dataset不符。

ptdec中需要传入的是dataset,传进去之后再封装为Dataloader。这里有两种修改思路 :

  1. 传入framework中的dataset,这样就需要修改opennre.framework.BagRE中与数据集相关的代码
  2. 直接传入framework中封装好的dataloader,这样就需要修改ptdec.model中封装数据集的代码

目前拟用思路2修改。

ptdec.model.train()函数中,与train_dataset对应的DataLoader有两个:static_dataloadertrain_dataloader,因此须在framework中相应添加static_dataloader

ptdec.model.eval()中,同样需要将输入的参数改为dataloader,并修改batch的处理方式

每批数据的处理方式的修改

在文献[1]的“Label Generator”部分提到:

标签生成器需要输入与训练的联系矩阵 L \mathbf{L} L作为输入之一。

因此,尝试在DEC程序中读入预训练出的编码器后,检查模型的model.fc.weight变量,可能该变量即为联系矩阵 L \mathbf{L} L

那么,只要有预训练出的PCNN+ATT模型,即可计算出聚类的输入句表征 C \mathbf{C} C,并从模型中取出联系矩阵 L \mathbf{L} L。由此,DEC程序可以独立于联系抽取程序做聚类,得到聚类签。

而其中的句表征 C \mathbf{C} C可参考opennre.model.BagAttention.forward()函数中训练分支中的处理方式。

用GPU加速KMeans

尝试调用RAPIDS.cuML中的KMeans函数,取代原来的sklearn.kmeans?

调试记录

当DEC程序运行到reassignment, accuracy = cluster_accuracy(actual, predicted)时,程序报错:

发生异常: IndexError
index 281270 is out of bounds for axis 0 with size 281270
  File "myNRE-DEC/ptdec/utils.py", line 23, in cluster_accuracy
    count_matrix[y_predicted[i], y_true[i]] += 1
  File "myNRE-DEC/example/dec_with_nyt.py", line 261, in main
    reassignment, accuracy = cluster_accuracy(actual, predicted)
  File "myNRE-DEC/example/dec_with_nyt.py", line 280, in <module>
    main()

此时的len(actual) = 281270len(predicted) = 522611

故猜测,聚类是以句子为单位聚类的,所以聚类签的数量等于句子的数量;而actual中的原签个数,则等于句袋个数。

存疑:

聚类该以句子为单位还是以句袋为单位去聚类?

这里应该以句子为单位聚类,所以应该修改actual变量的内容。

文献[1]提到,聚类只对非NA签的样本,故需要相应修改

暂时将训测集文件缩减,训练集缩减到10万个样本,测试集缩减到5万个样本。使用该缩减后的数据集运行DEC程序,可以正常运行完。

数据集修改

  1. 去掉原签为“NA”的所有样本
  2. 去掉以下6类联系的样本:
    1. /business/shopping_center_owner/shopping_centers_owned
    2. /location/fr_region/capital
    3. /location/mx_state/capital
    4. /business/shopping_center/owner
    5. /location/country/languages_spoken
    6. /base/locations/countries/states_provinces_within

DEC中的关键公式

聚类的输入

将句子表征映射到联系特征空间:
C = H L T + b \mathbf{C} = \mathbf{HL}^T + b C=HLT+b

其中, b b b为偏置。这个操作可以被看作是以所有联系为查询向量(query vectors)注意力(attention)
以计算联系感知的(relation-aware)句子表征。
这里相当于将每个句子投影到联系空间中,每个句子与每个关系做权重计算,并用于获得联系感知的(relation-aware)句子表征。

DEC的聚类核函数(计算相似度 q i j q_{ij} qij)

C \mathbf{C} C传入聚类层,它将聚类中心 { μ i } i = 1 n c \{\mu_i\}_{i=1}^{n_c} {μi}i=1nc作为可训练的权重,其中 n c n_c nc是类别数量。该文使用学生t分布(Student’s t-distribution) (Maaten and Hinton 2008)作为核来度量特征向量 c i c_i ci与聚类中心 μ j \mu_j μj之间的相似度 q i j q_{ij} qij(公式7):
q i j = ( 1 + ∥ c i − μ j ∥ 2 ) − 1 ∑ j ( 1 + ∥ c i − μ j ∥ 2 ) − 1 q_{ij} = \frac {(1 + {\lVert c_i - \mu_j \rVert} ^2)^{-1}} {\textstyle\sum_{j} (1 + {\lVert c_i - \mu_j \rVert} ^2)^{-1} } qij=j(1+ciμj2)1(1+ciμj2)1
其中, q i j q_{ij} qij是映射出的句子向量 c i c_i ci和聚类中心向量 μ j μ_j μj之间的相似度。它也可以解释为将句子 s i s_i si分配给联系标签 r j r_j rj的概率。

在"ptdec/cluster.py"文件中定义了DEC的核函数:

# {\lVert c_i - \mu_j \rVert} ^2)
norm_squared = torch.sum((batch.unsqueeze(1) - self.cluster_centers) ** 2, 2)   # (B, C) 计算样本表征与每个聚类中心表征的差的平方,并在维度2(H)将所有元素相加

# (1 + {\lVert c_i - \mu_j \rVert} ^2)^{-1}
numerator = 1.0 / (1.0 + (norm_squared / self.alpha))                           # 

power = float(self.alpha + 1) / 2
numerator = numerator ** power

# q_{ij} = \frac {(1 + {\lVert c_i - \mu_j \rVert} ^2)^{-1}} {\textstyle\sum_{j} (1 + {\lVert c_i - \mu_j \rVert} ^2)^{-1} }
return numerator / torch.sum(numerator, dim=1, keepdim=True)

该核函数与文献中的公式表示的核函数一致

DEC的损失函数

文献[1]的阐述

深度聚类的损失函数被定义为KL散度(Kullback-Leibler divergence):

L = K L ( P ∥ Q ) = ∑ i ∑ j p i j log ⁡ p i j q i j \mathcal{L}=K L(P \| Q)=\sum_{i} \sum_{j} p_{i j} \log \frac{p_{i j}}{q_{i j}} L=KL(PQ)=ijpijlogqijpij

其中, P P P是目标分布。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VExqzG1k-1645986476042)(imgs/Positive Relation Distribution.png)]
图2 NYT-10数据集中的52个正类联系(不包括NA)的分布。横轴显示了按出现次数排序的不同联系。纵轴显示训练集中的句子数量。竖线表示id为31的联系在训练集中出现了10次。

NYT10中的联系遵循长尾分布,如上图所示。为了缓解数据不平衡的问题,该文使用与(Xie, Girshick, and Farhadi 2016)相同的 P P P,定义为:
p i j = q i j 2 / ∑ i q i j ∑ j ( q i j 2 / ∑ i q i j ) p_{ij} = \frac {q^2_{ij} / \textstyle\sum_{i}q_{ij}} {\textstyle\sum_{j} (q^2_{ij} / \textstyle\sum_{i}q_{ij}) } pij=j(qij2/iqij)qij2/iqij
该目标分布可以将每个中心点的损失贡献归一化,以防止大的聚类簇扭曲了隐藏的特征空间。

KL散度

KL散度通常用来衡量两个连续分布之间的距离。两个分布越相似,KL散度越接近0。 KL散度又叫相对熵。
l o s s ( x , y ) = 1 n ∑ i ( x i log ⁡ x i y i ) \mathcal{loss}(x,y)= \frac 1 n \sum_{i} (x_{i} \log \frac{x_{i}}{y_{i}}) loss(x,y)=n1i(xilogyixi)

其中的 x x x y y y都是分布,意味着其中所有元素求和概率为1。

DEC程序中的损失函数(torch.nn.KLDivLoss()

该损失函数需要输入目标分布 P P P和相似度分布 q i j q_{ij} qij

程序中目标分布P的计算公式与文献[1]中提到的计算公式相同

在“ptdec/model.py”文件中,

output = model(feature)                         # (nsum, C) 模型的输出即为q_ij
target = target_distribution(output).detach()   # (nsum, C) 计算目标分布,应该输入q_ij
loss = loss_function(output.log(), target) / output.shape[0]

KLDivLoss(input, target)的输入input应该是log-probabilities,target是probabilities。它们的形状相同。

DEC程序中的损失函数与文章中的阐述一致。

参考文献

[1] Shang Y, Huang H Y, Mao X L, et al. Are noisy sentences useless for distant supervised relation extraction?[C]//Proceedings of the AAAI Conference on Artificial Intelligence. 2020, 34(05): 8799-8806.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值