這篇博客介紹 TransF、TransM 和 SSE,也都是比較老的模型了。
TransF
paper: Knowlege Graph Embedding by Flexible Translation
論文
這篇論文是清華大學黃民烈朱小燕老師團隊發表在 AAAI 2015 上的文章(其實並不確定,因為沒有找到正式發表的信息,只在百度學術上的引用中有 AAAI Press 的字樣)。文章提出了 TransF 模型,F 代表 flexible,采用了 "flexible translation" 的思想來進一步解決 1-n、n-1、n-n、reflexive 這樣的復雜關系表示的問題。具體說,就是用 "h+r≈αt" 代替比較 hard 的 "h+r≈t",即只要保證 "h+r" 的方向與 t 的方向相同即判定三元組 (h,r,t) 成立。思想非常簡單,模型並不復雜,但不得不說這種設計還是非常巧妙的。
模型思想

-
對於自反關系,有 h+r=t 和 t+r=h 同時成立,得出 r=0,h=t,顯然不合理,TransF 的解決方法是:令 \(h=\frac{2}{\alpha_1\alpha_2-1}r\),\(t=\frac{\alpha_2+1}{\alpha_1\alpha_2-1}r\),這樣便可得 \(h+r\approx \alpha_1t\),\(t+r\approx \alpha_2h\)(但我並沒有推導出這樣的結果)。
-
對於 1-n 關系,令 \(t_0=\frac{h+r}{\alpha_0}\),…,\(t_n=\frac{h+r}{\alpha_n}\)。
-
對於 n-1 關系,令 \(h_0=\alpha_0t-r\),…,\(h_n=\alpha_nt-r\)。
打分函數及 loss
打分函數本應是這樣的:,計算 \(h+r\) 與 \(t\) 的點積,但這樣的打分函數存在如下的問題:

給定 h 和 r,t 的范圍被限制在一條線(一個向量方向)上,而給定 t 和 r,則 h 的方向與長度可以任意,只要滿足是從線段 a 的某一點指向線段 b 的某一點的向量即可。為了保證 h 和 t 有同樣的約束,將上述的打分函數對稱化一下:

正樣本得分高,負樣本得分低,因為正交向量點積為 0,正樣本的 h+r 應當與 t 平行(不正交),之前模型都是正樣本的得分接近0,負樣本的得分高,因此這里的損失函數的定義也與之前相反:

訓練使用 SGD 優化。
TransRF
TransF 的方法可以移植到其他模型,文章移植到了 TransR,提出 TransRF:

實驗
除了常規的鏈接預測和三元組分類實驗,文章還對樣本的分數分布進行了可視化分析。



從前兩個實驗的結果來看,TransF的提升並不算顯著,但是其定義的打分函數卻能很好地將正負樣本區分開。
沒有找到 TransF 的代碼,openKE 和 Pykg2vec 都沒有實現,github上也沒有,TransF 應該是 Trans 系列里比較冷門的一個模型了。
小結:早期跟進 TransE、在 Trans 系列上做文章的也就那么三個主要的團隊:清華大學劉知遠老師、清華大學黃民烈朱小燕老師、自動化所趙軍劉康老師,各個團隊的模型風格也大概有所了解有一點感覺了。劉知遠老師團隊的工作比較硬核,自動化所趙軍老師組的工作比較扎實、細致,黃民烈朱小燕老師組的模型工作量不大,但在寫作上有巧妙之處。后續看下新的模型有沒有這三個團隊做的工作。
TransM
paper: Transition-based Knowledge Graph Embedding with Relational Mapping Properties
論文
該文是清華大學(不知道哪個團隊)和埃默里大學共同發表在 PACLIC(CCF 列表上沒有找到這個會,大概相當於C的水平) 2014 上的文章,提出了 TransM,M 代表 mapping。文章是在 TransE 后的第二年出的,還沒有 TransH 和 TransR,所以 baseline 只對比了 TransE 和一些非 Trans 模型。
模型
模型的思想非常簡單,就是在計算得分時為每個三元組賦一個預計算的權重,該權重反映了該關系下的實體節點的度。


根據權重的計算公式,對於 1-n、n-1 等實體密度比較大的三元組,賦予比較小的權重,其三元組打分會偏低。這樣,"1-1" 類關系會比復雜關系類型有更為嚴格的限制。

實驗
實驗進行了鏈接預測和三元組分類。
- 鏈接預測
與 TransE 的鏈接預測結果對比,使用了 L1 范數和 L2 范數,L1 范數的效果普遍比 L2 效果好。

與其他模型的對比:

區分關系類型的模型效果:

- 三元組分類

還畫出了三元組分類的 PR 曲線:

代碼
上 \(Pykg2vec\) 的實現:
class TransM(PairwiseModel):
def __init__(self, **kwargs):
super(TransM, self).__init__(self.__class__.__name__.lower())
param_list = ["tot_entity", "tot_relation", "hidden_size", "l1_flag"]
param_dict = self.load_params(param_list, kwargs)
self.__dict__.update(param_dict)
self.ent_embeddings = NamedEmbedding("ent_embedding", self.tot_entity, self.hidden_size)
self.rel_embeddings = NamedEmbedding("rel_embedding", self.tot_relation, self.hidden_size)
rel_head = {x: [] for x in range(self.tot_relation)}
rel_tail = {x: [] for x in range(self.tot_relation)}
rel_counts = {x: 0 for x in range(self.tot_relation)}
train_triples_ids = kwargs["knowledge_graph"].read_cache_data('triplets_train')
for t in train_triples_ids:
rel_head[t.r].append(t.h)
rel_tail[t.r].append(t.t)
rel_counts[t.r] += 1
# "heads per tail 每個尾巴有幾個頭" hpt = rel_counts[x]/(1+len(rel_tail[x]))
# "tails per head 每個頭有幾個尾巴" tph = rel_counts[x]/(1+len(rel_head[x]))
theta = [1/np.log(2 + rel_counts[x]/(1+len(rel_tail[x])) + rel_counts[x]/(1+len(rel_head[x]))) for x in range(self.tot_relation)]
self.theta = torch.from_numpy(np.asarray(theta, dtype=np.float32)).to(kwargs["device"])
nn.init.xavier_uniform_(self.ent_embeddings.weight)
nn.init.xavier_uniform_(self.rel_embeddings.weight)
self.parameter_list = [
self.ent_embeddings,
self.rel_embeddings,
]
self.loss = Criterion.pairwise_hinge
def forward(self, h, r, t):
"""Function to get the embedding value.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids.
t (Tensor): Tail entity ids.
Returns:
Tensors: the scores of evaluationReturns head, relation and tail embedding Tensors.
"""
h_e, r_e, t_e = self.embed(h, r, t)
norm_h_e = F.normalize(h_e, p=2, dim=-1)
norm_r_e = F.normalize(r_e, p=2, dim=-1)
norm_t_e = F.normalize(t_e, p=2, dim=-1)
r_theta = self.theta[r]
if self.l1_flag:
return r_theta*torch.norm(norm_h_e + norm_r_e - norm_t_e, p=1, dim=-1)
return r_theta*torch.norm(norm_h_e + norm_r_e - norm_t_e, p=2, dim=-1)
def embed(self, h, r, t):
"""Function to get the embedding value.
Args:
h (Tensor): Head entities ids.
r (Tensor): Relation ids of the triple.
t (Tensor): Tail entity ids of the triple.
Returns:
Tensors: Returns head, relation and tail embedding Tensors.
"""
emb_h = self.ent_embeddings(h)
emb_r = self.rel_embeddings(r)
emb_t = self.ent_embeddings(t)
return emb_h, emb_r, emb_t
自己跑模型的過程中,感覺 TransM 雖然是個名不見經傳的冷門模型,但效果居然還不錯,能超過 TransE 和 大多數非 Trans 模型。
小結: 雖然是個簡單的 idea,但是效果卻出奇地好,值得一篇 C。
SSE
paper: Semantically Smooth Knowledge Graph Embedding
論文
這篇文章是王斌和王泉老師還在信工所時,他們的團隊發表在 ACL 2015 上的工作。文章提出了 SSE(Semantically Smooth Embedding),本質是在目標函數中加的兩種約束項,使得屬於同種類別的實體在 embedding 空間中的位置相近(即保證語義平滑)。兩種約束項 LE(Laplacian Eigenmaps)和 LLE(Locally Linear Embedding)是起源於流形學習的兩個算法,名字聽起來比較高大上,實則思想很簡單。
文章中對 KGE 本質的描述我覺得很到位,記錄一下:KGE 分為三步,1) 表示實體和關系(定義表示);2) 定義打分函數;3) 學習表示(訓練)。不同模型的不同之處主要在於前兩點:實體/關系表示的定義和能量函數的定義。
LE (Laplacian Eigenmaps)
借鑒 LE 思想的平滑假設:如果兩個實體屬於相同的語義類別,那么它們的 embedding 距離是相近的。
衡量 embedding 空間平滑度的約束項:

其中,\(w_{ij}\) 是鄰接矩陣,標識兩個實體是否屬於同一類別。
加約束項的目標函數:

LLE(Locally Linear Embedding)
基於 LLE 思想的平滑假設:實體可以由其最近鄰居表示,最近鄰居代表同一類別的實體。
度量 embedding 空間平滑性的重構誤差:

作為正則項加入 Loss:

實驗
- 鏈接預測

數據集使用 NELL 的三個子數據集,探究了加入類別編碼、LE 和 LLE 三種機制的 TransE、SME(線性和雙線性)、SE 四個模型的效果提升,沒有對比 TransH 和 TransR。
並用 t-SNE 對實體進行了降維可視化,表明加入 LE 和 LLE 能很好地將不同類別的實體在 embedding 空間中區分開。(有空應當學一下 t-SNE,感覺用的還挺多的,補充一下可視化方面的空白。)

- 三元組分類

沒有找到代碼,用這篇文章思想的人應該不多吧。
小結: 其實嚴格來說 SSE 不算一個單獨的模型,只是在其他模型(TransE)的基礎上加了兩個保證語義平滑的約束項,算是一個提升模型效果的小 trick。為了證明這兩個約束項的有效性,在現有的模型上進行了對比試驗,工作量還是比較充實的。雖然號稱是來自於 manifold learning 的兩個平滑思想,但本質十分 naive,不難理解。