文章目录
基于OpenNRE框架和文献[1]中对DCRE的描述,实现DCRE。
编码器的修改
需要预训练出一个PCNN+ATT的模型,使用其中的编码器。
输入数据格式的修改
opennre.framework.BagRE
中的dataloader(train_loader
, val_loader
, test_loader
)与ptdec.model
的构造函数的输入dataset
不符。
ptdec中需要传入的是dataset,传进去之后再封装为Dataloader。这里有两种修改思路 :
- 传入framework中的dataset,这样就需要修改opennre.framework.BagRE中与数据集相关的代码
- 直接传入framework中封装好的dataloader,这样就需要修改ptdec.model中封装数据集的代码
目前拟用思路2修改。
在ptdec.model.train()
函数中,与train_dataset
对应的DataLoader有两个:static_dataloader
和train_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) = 281270
,len(predicted) = 522611
。
故猜测,聚类是以句子为单位聚类的,所以聚类签的数量等于句子的数量;而actual
中的原签个数,则等于句袋个数。
存疑:
聚类该以句子为单位还是以句袋为单位去聚类?
这里应该以句子为单位聚类,所以应该修改actual变量的内容。
文献[1]提到,聚类只对非NA签的样本,故需要相应修改
暂时将训测集文件缩减,训练集缩减到10万个样本,测试集缩减到5万个样本。使用该缩减后的数据集运行DEC程序,可以正常运行完。
数据集修改
- 去掉原签为“NA”的所有样本
- 去掉以下6类联系的样本:
- /business/shopping_center_owner/shopping_centers_owned
- /location/fr_region/capital
- /location/mx_state/capital
- /business/shopping_center/owner
- /location/country/languages_spoken
- /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−μj∥2)−1(1+∥ci−μj∥2)−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(P∥Q)=i∑j∑pijlogqijpij
其中, 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.