雙線性模型(一)(RESCAL、LFM、DistMult)


今天開始,就要開始閱讀雙線性模型的文章了。大約有十七八篇文章,都是在綜述經常見到的經典雙線性模型,計划在八月中旬之前看完。雖然后面不會去做這類方法,但是作為這個領域的研究者,覺得還是應該讀一讀,否則總感覺自己的知識體系是有漏洞的。

開題報告里對於這一類模型的簡介:

語義匹配模型采用基於相似性的打分函數,通過匹配實體和關系在嵌入向量空間的潛在語義衡量三元組事實成立的可能性。該類模型的典型代表有:RESCAL[28]、DistMult[29]、HolE[30]、ComplEx[31]、ANALOGY[32]、SEEK[50]等。RESCAL[28]又稱雙線性模型,該模型用向量表示實體,用矩陣表示關系,並通過自定義的打分函數捕捉三元組的內部交互。DistMult[29]通過將RESCAL的關系矩陣限制為對角矩陣對其進行了簡化。HolE(Holographic Embedding)[30]結合了RESCAL的表示能力與DistMult的簡潔高效,將實體和關系都表示為 空間中的向量,並定義了頭尾實體進行交互的循環關聯操作,操作結果與關系的表示進行匹配以計算三元組得分。ComplEx(Complex Embedding)[31]在DistMult的基礎上引入復值嵌入,實體和關系的embedding不再位於實值空間而是復空間。此外ComplEx的三元組打分函數並非對稱形式,對於非對稱關系類型的三元組可根據頭尾實體的位置關系得到不同的得分,從而可以更好地建模非對稱關系。ANALOGY[32]在RESCAL的基礎上進行擴展,以更好地建模實體和關系的推理屬性。它采用了和RESCAL同樣的雙線性函數作為三元組打分函數。 SEEK(Segmented Embedding of Knowledge)[50]針對現有模型的表示能力與復雜度不能兼顧的問題,提出輕量級的嵌入框架,核心思想是對實體和關系進行分段嵌入,並通過段間組合計算三元組得分,可以在不增加模型復雜度的情況下獲得較好的表示能力,並實現對於對稱和非對稱關系類型的處理能力。

RESCAL

【paper】 A Three-Way Model for Collective Learning on Multi-Relational Data

【簡介】 這篇文章應該算是雙線性模型的開山之作。是德國的一個團隊發表在 ICML 2011 上的工作,比較老了,主要思想是三維張量分解。

模型

定義了一個 tensor,m 是關系數,n 是實體數,每個關系對應於 tensor 中的一個 slice,即一個矩陣,每個矩陣相當於表示圖的鄰接矩陣。位置元素為 1 代表兩個實體之間存在這種關系,為 0 表示不存在。

對 tensor 進行分解:

\(A\) 是 n×r 的矩陣,表示每個實體的隱性表示(latent-component representation),\(R_k\) 是 r×r 的非對稱矩陣,建模第 k 個屬性/關系中的實體 latent component 的交互。

矩陣 \(A\)\(R_k\) 通過約束最小化問題來計算:

特別要提的是 \(R_k\) 是非對稱矩陣,這樣可以建模非對稱關系,在同一個實體作為頭實體或尾實體時會得到不同的 latent component representation。

其中,

更具體地,

(其實貼圖時可以不用定義高度)

這樣的分解機制可以利用相關實體提供的信息進行表示,類似推薦里的協同過濾,這里稱為 collective learning,舉了一個栗子:

以上就是 Rescal 的核心思想了,后面有幾個小節講和其他方法的聯系以及計算 factorization 的方法,沒有仔細看。

實驗

collective classification

該實驗使用自建的政黨數據集,包括 93 個實體和 3 個關系,因此構建了 93×93×5 的 tensor。政黨分類效果如下。

collective 實體消歧

實體消歧可以視為 isEqual 關系的鏈接預測,在 Cora 數據集上進行了實驗:

除了上述兩個實驗,還在 Kinships, Nations 和 UMLS 數據集上進行了鏈接預測,並在 Nations 數據集上進行了聚類實驗。並在各個數據集上進行了與其他兩個算法的運行效率的比較,不再貼圖了。

代碼

文章中說 Rescal 是用不超過 120 行的 Python/Numpy實現的,但沒有給出代碼。Pykg2vec 實現了 Rescal,但我沒有看明白,實現的比較巧妙。因為也沒有打算做這塊,就不繼續花時間研究了。

class Rescal(PairwiseModel):
    """
        `A Three-Way Model for Collective Learning on Multi-Relational Data`_ (RESCAL) is a tensor factorization approach to knowledge representation learning,
        which is able to perform collective learning via the latent components of the factorization.
        Rescal is a latent feature model where each relation is represented as a matrix modeling the iteraction between latent factors. It utilizes a weight matrix which specify how much the latent features of head and tail entities interact in the relation.
        Portion of the code based on mnick_ and `OpenKE_Rescal`_.

        Args:
            config (object): Model configuration parameters.

        .. _mnick: https://github.com/mnick/rescal.py/blob/master/rescal/rescal.py

        .. _OpenKE_Rescal: https://github.com/thunlp/OpenKE/blob/master/models/RESCAL.py

        .. _A Three-Way Model for Collective Learning on Multi-Relational Data : http://www.icml-2011.org/papers/438_icmlpaper.pdf

    """
    def __init__(self, **kwargs):
        super(Rescal, self).__init__(self.__class__.__name__.lower())
        param_list = ["tot_entity", "tot_relation", "hidden_size", "margin"]
        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_matrices = NamedEmbedding("rel_matrices", self.tot_relation, self.hidden_size * self.hidden_size)
        nn.init.xavier_uniform_(self.ent_embeddings.weight)
        nn.init.xavier_uniform_(self.rel_matrices.weight)

        self.parameter_list = [
            self.ent_embeddings,
            self.rel_matrices,
        ]

        self.loss = Criterion.pairwise_hinge

    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.

        """
        k = self.hidden_size

        self.ent_embeddings.weight.data = self.get_normalized_data(self.ent_embeddings, self.tot_entity, dim=-1)
        self.rel_matrices.weight.data = self.get_normalized_data(self.rel_matrices, self.tot_relation, dim=-1)

        emb_h = self.ent_embeddings(h)
        emb_r = self.rel_matrices(r)
        emb_t = self.ent_embeddings(t)
        emb_h = emb_h.view(-1, k, 1)
        emb_r = emb_r.view(-1, k, k)
        emb_t = emb_t.view(-1, k, 1)

        return emb_h, emb_r, emb_t


    def forward(self, h, r, t):
        h_e, r_e, t_e = self.embed(h, r, t)
        # dim of h: [m, k, 1]
        #        r: [m, k, k]
        #        t: [m, k, 1]
        return -torch.sum(h_e * torch.matmul(r_e, t_e), [1, 2])


    @staticmethod
    def get_normalized_data(embedding, num_embeddings, p=2, dim=1):
        norms = torch.norm(embedding.weight, p, dim).data
        return embedding.weight.data.div(norms.view(num_embeddings, 1).expand_as(embedding.weight))

在 FB15k 數據集上嘗試運行了一下,效果很差:


【小結】 本文用三維張量分解進行三元組嵌入。

LFM(Latent Factor Model)

【paper】 A latent factor model for highly multi-relational data

【簡介】 這篇文章是法國的研究團隊發表在 NIPS 2012 上的文章,還掛了 Antoine Bordes 的名字。文章提出了 LFM(Latent Factor Model),主要貢獻有兩點:一是定義了 unigram、bigram、trigram 三種方式組合的三元組打分函數;二是將關系矩陣分解為低階矩陣的組合,這樣可以實現參數共享。其實這種比較老的論文的表達方式、行文結構跟現在的論文都不太一樣,再加上時間有限,所以沒有看太明白。但這類模型終究是要過一遍的,就這樣吧。

模型

文章在 intro 部分介紹了統計關系數據建模的現存難點:

  1. 頻繁出現的關系類型只是一小部分(長尾現象)
  2. 數據存在噪聲並且不完整
  3. 數據集規模有限

文章稱 LFM 是基於概率的,明確考慮了數據的不確定性。這里的不確定性應該不是指的實體和關系包含語義的不確定性,只是指對三元組進行概率打分。

早期的論文中三元組表示都是(subject, relation, object),若三元組成立,寫作 \(R_i(S_i,O_k)=1\)

表示及打分函數

logistic 模型進行了如下的定義:

其中,\(\eta_ik^{(j)}\) 是一個線性函數:

貢獻一: 對打分函數 \(\eta_ik^{(j)}\) 進行了重新定義


貢獻二: 對關系矩陣進行分解

當關系數量比較多時,每個關系下的樣本很少,容易引起過擬合。之前的模型曾經使用兩種解決方法,一是聚類,二是用向量表示關系。與 RESCAL 的使用一個通用矩陣進行參數化的方法不同,本文提出的解決方法是將關系矩陣分解為 d 秩一矩陣(不知道這里的“一矩陣”是不是指對角矩陣)\(\{\varTheta_r\}_{1\le r \le d}\)

分解的稀疏性和 \(d \ll n_r\) 可以保證不同關系的參數共享。

Loss

模型訓練的目標是最大化下面的 likelihood:

經過推導,可以得到 log-likelihood:

上午推導了一下,前半部分沒有對上,可能中間有近似約減消掉的項。

訓練目標等價於最小化負的 log-likelihood:

實驗

和 RESCAL 一樣,在 Kinships、UMLS、Nations 數據集上進行了實驗,與 RESCAL、MRC 和 SME 三個 baseline 對比了 AUC 和 log-likelihood。

除了進行關系數據建模,實驗部分還學習了動詞的語義表示。這部分沒細看。

代碼

沒有代碼。


【總結】 本文定義了 unigram、bigram、trigram 進行組合的線性打分函數,並對關系矩陣進行分解實現參數共享。

DistMult

【paper】 Embedding entities and relations for learning and inference in knowledge bases

【簡介】 這篇文章是康奈爾大學和微軟的研究者發表在深度學習頂會無冕之王 ICLR 2015 上的工作,文章提出了 DistMult。模型的改進微乎其微,就是把雙線性模型的關系矩陣限制為了對角矩陣,然后重點是用 embedding-based 的方法做了規則挖掘。看文章署名的話應該都是中國人,行文結構也很典型,因此看起來感覺比上一篇舒服多了,大部分內容基本能看懂。

文章將 TransE 和 NTN 都歸類為神經網絡,並且 motivation 來源於這兩個模型:

  1. 不同的設計如何影響學習結果
  2. 鏈接預測實驗只是間接展示低維 embedding 的效果,關系屬性如何捕捉以及如何作用很難解釋。

文章貢獻如下:

  1. 提出通用框架整合現有模型
  2. 鏈接預測實驗評價
  3. 挖掘邏輯規則

(看 related work 有感:第一點是,一直不知道 related work 該寫什么、怎么寫。我寫論文一個很大的問題是一開始的引入總是從很大很宏觀的角度,本想由淺入深引到自己的工作上,但是由於一開始沒有聚焦,並且一寫開就剎不住車,所以會有一種顧左右而言他、文不對題的感覺,小論文、開題報告里都有這個問題。這篇的 related work 就是由淺入深,先列出 multi-relational learning 的一些方法,然后詳細介紹了 NTN,然后引到自己的規則抽取工作(雖然中間少了點銜接)。以后我在寫 related work 也要再聚焦一些,多說與自己工作有密切關聯的相關工作;第二點是 DistMult 這篇文章,模型本身的改進幾乎沒有,很雞肋,但是它把展示的重點放在了規則挖掘上,這就是揚長避短的作用了,就像衣服的穿搭,身材不好也沒有關系,關鍵是如何凸顯優勢、弱化劣勢。)

模型

模型部分文章說,呈現的是一個通用的 NN 框架,討論了不同設計的選擇,並對比了效果。其實都是比較 naive 的東西,沒什么新的。

實體表示

實體表示就是用 “one-hot” 向量查詢實體矩陣:

NTN 用預訓練的詞向量做為實體的初始表示。

關系表示

關系的表示通常反映在打分函數中,打分函數通常被定義為線性、雙線性或二者組合的轉換函數:

幾個模型的關系表示及打分函數:

本文只考慮最基本的雙線性打分函數:

本文提出了將關系矩陣 \(M_r\) 限制為對角矩陣(DistMult),這樣可以將雙線性模型的參數量減少到與 TransE 相同。

上面提出的通用框架同樣適用於深層語義模型和多層 NN。

參數學習

和 TransE 一樣使用負采樣操作構建負樣本,並定義 margin-loss:

實驗

鏈接預測

在 WN 和 FB15k 上進行鏈接預測實驗。

比較了五個模型(按照復雜度降序排列)的效果:

  1. 4個 tensor slice 的 NTN
  2. Bilinear + Linear:一個tensor slice 的 NTN,且只有線性層
  3. TransE
  4. Bilinear:最基本的雙線性打分函數
  5. Bilinear-diag:關系矩陣限制為對角矩陣的雙線性模型

五個模型效果如下:

NTN 最復雜,但是效果最差,相反,最簡單的 DistMult 效果最好。但是文中也提到了,將關系矩陣限制為對角矩陣喪失了對非對稱關系建模的能力,這是 DistMult 的缺陷。

還在不同的關系類型上對比了乘法(bilinear-diag,即 DistMult)和加法(TransE,DistAdd)的特征交互的效果。

顯然,DistMult 比 DistAdd 效果要好。

此外,還試驗了使用非線性的投影函數 tanh,並使用預訓練詞向量進行實體表示的初始化。在預測實體時,利用實體類型信息進行結果過濾。

規則抽取

規則抽取是本文的重要工作。邏輯規則有很重要的意義:

  1. 可用於推斷新事實進行 KB 補全
  2. 優化數據存儲,只存儲邏輯規則比存儲事實節省空間
  3. 支持復雜推理
  4. 為推斷結果提供解釋

文中說 embedding-based 的規則挖掘不必受 KB 規模的影響,但后面做實驗還是只做了兩跳和三跳的,不可能不受 KB 規模的影響。

該任務的目的就是挖掘如下的 Horn 規則:

用兩種方式建模關系的組合:關系用向量表示的用加法(歐式距離),用矩陣表示的用乘法(Frobenius 范數)。

規則挖掘算法:

挖掘結果如下,橫坐標代表挖掘的數量,縱坐標代表挖掘的質量/精度,DistMult 的效果是好於 AMIE 的。

並且,bilinear 在長路徑下效果更好。

代碼

原文沒有給出代碼,pykg2vec 給出了 DistMult 的實現

class DistMult(PointwiseModel):
    """
        `EMBEDDING ENTITIES AND RELATIONS FOR LEARNING AND INFERENCE IN KNOWLEDGE BASES`_ (DistMult) is a simpler model comparing with RESCAL in that it simplifies
        the weight matrix used in RESCAL to a diagonal matrix. The scoring
        function used DistMult can capture the pairwise interactions between
        the head and the tail entities. However, DistMult has limitation on modeling asymmetric relations.

        Args:
            config (object): Model configuration parameters.

        .. _EMBEDDING ENTITIES AND RELATIONS FOR LEARNING AND INFERENCE IN KNOWLEDGE BASES:
            https://arxiv.org/pdf/1412.6575.pdf

    """
    def __init__(self, **kwargs):
        super(DistMult, self).__init__(self.__class__.__name__.lower())
        param_list = ["tot_entity", "tot_relation", "hidden_size", "lmbda"]
        param_dict = self.load_params(param_list, kwargs)
        self.__dict__.update(param_dict)

        num_total_ent = self.tot_entity
        num_total_rel = self.tot_relation
        k = self.hidden_size

        self.ent_embeddings = NamedEmbedding("ent_embedding", num_total_ent, k)
        self.rel_embeddings = NamedEmbedding("rel_embedding", num_total_rel, k)
        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.pointwise_logistic

    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.
        """
        h_emb = self.ent_embeddings(h)
        r_emb = self.rel_embeddings(r)
        t_emb = self.ent_embeddings(t)

        return h_emb, r_emb, t_emb


    def forward(self, h, r, t):
        h_e, r_e, t_e = self.embed(h, r, t)
        return -torch.sum(h_e*r_e*t_e, -1)


    def get_reg(self, h, r, t, reg_type="F2"):
        h_e, r_e, t_e = self.embed(h, r, t)

        if reg_type.lower() == 'f2':
            regul_term = torch.mean(torch.sum(h_e ** 2, -1) + torch.sum(r_e ** 2, -1) + torch.sum(t_e ** 2, -1))
        elif reg_type.lower() == 'n3':
            regul_term = torch.mean(torch.sum(h_e ** 3, -1) + torch.sum(r_e ** 3, -1) + torch.sum(t_e ** 3, -1))
        else:
            raise NotImplementedError('Unknown regularizer type: %s' % reg_type)

        return self.lmbda*regul_term

【總結】 本文提出了 neural-embedding 的通用框架,並把 NTN、TransE 等模型套在框架里進行對比;提出了將關系矩陣限制為對角矩陣的 DistMult;並用 embedding-based 方法挖掘邏輯規則。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM