python算法学习双曲嵌入论文方法与代码解析说明

目录
  • 1. 方法说明
    • 损失函数
    • 梯度下降
    • 梯度求解
  • 2. 代码训练过程
  • 3. 结果表现
  • 其他参考资料

本篇接上一篇:python算法学习双曲嵌入论文代码实现数据集介绍

1. 方法说明

首先学习相关的论文中的一些知识,并结合进行代码的编写。文中主要使用Poincaré embedding。

对应的python代码为:

def dist1(vec1, vec2): # eqn1
    diff_vec = vec1 - vec2
    return 1 + 2 * norm(diff_vec) / ((1 - norm(vec1)) * (1 - norm(vec2)))

损失函数

我们想要寻找最优的embedding,就需要构建一个损失函数,目标是使得相似词汇的embedding结果,尽可能接近,且层级越高(类别越大)的词越靠近中心。我们需要最小化这个损失函数,从而得到embedding的结果。

其实在传统的词嵌入中,我们也是用上述的损失函数,但距离选用的是余弦距离。

梯度下降

后面将使用梯度下降方法进行求解迭代。

由于是将欧氏空间计算得到的梯度在黎曼空间中进行迭代,由上文的(1)式,我们有:

梯度求解

202111595310129

对应的更新函数在Python中设置如下:

# 范数计算
def norm(x):
    return np.dot(x, x)

# 距离函数对\theta求偏导
def compute_distance_gradients(theta, x, gamma):
    alpha = (1.0 - np.dot(theta, theta))
    norm_x = norm(x)
    beta = (1 - norm_x)
    c_ = 4.0 / (alpha * beta * sqrt(gamma ** 2 - 1))
    return c_ * ((norm_x - 2 * np.dot(theta, x) + 1) / alpha * theta - x)

# 更新公式
def update(emb, grad, lr):
    c_ = (1 - norm(emb)) ** 2 / 4
    upd =  lr * c_ * grad
    emb = emb - upd
    if (norm(emb) >= 1):
        emb = emb / sqrt(norm(emb)) - eps
    return emb

至此,我们就可以开始写一个完整的训练过程了。在这之前,再补充一个绘图函数(可以看embedding的实际训练情况):

def plotall(ii):
    fig = plt.figure(figsize=(10, 10))
    # 绘制所有节点
    for a in emb:
        plt.plot(emb[a][0], emb[a][1], marker = 'o', color = [levelOfNode[a]/(last_level+1),levelOfNode[a]/(last_level+1),levelOfNode[a]/(last_level+1)])
    for a in network:
        for b in network[a]:
            plt.plot([emb[a][0], emb[b][0]], [emb[a][1], emb[b][1]], color = [levelOfNode[a]/(last_level+1),levelOfNode[a]/(last_level+1),levelOfNode[a]/(last_level+1)])
            circle = plt.Circle((0, 0), 1, color='y', fill=False)
            plt.gcf().gca().add_artist(circle)
    plt.xlim(-1, 1)
    plt.ylim(-1, 1)
    fig.savefig('~/GitHub/hyperE/fig/' + str(last_level) + '_' + str(ii) + '.png', dpi = 200)

2. 代码训练过程

首先初始化embeddings,这里按照论文中写的,用 ( − 0.001 , 0.001 ) (-0.001, 0.001) (−0.001,0.001)间的均匀分布进行随机初始化:

emb = {}
for node in levelOfNode:
    emb[node] = np.random.uniform(low = -0.001, high = 0.001, size = (2, ))

下面设置学习率等参数:

vocab = list(emb.keys())
eps = 1e-5
lr = 0.1       # 学习率
num_negs = 10  # 负样本个数

接下来开始正式迭代,具体每一行的含义均在注释中有进行说明:

# 绘制初始化权重
plotall("init")
for epoch in range(1000):
    loss = []
    random.shuffle(vocab)

    # 下面需要抽取不同的样本:pos2 与 pos1 相关;negs 不与 pos1 相关
    for pos1 in vocab:
        if not network[pos1]: # 叶子节点则不进行训练
            continue
        pos2 = random.choice(network[pos1]) # 随机选取与pos1相关的节点pos2
        dist_pos_ = dist1(emb[pos1], emb[pos2]) # 保留中间变量gamma,加速计算
        dist_pos = np.arccosh(dist_pos_) # 计算pos1与pos2之间的距离

        # 下面抽取负样本组(不与pos1相关的样本组)
        negs = [[pos1, pos1]]
        dist_negs_ = [1]
        dist_negs = [0]

        while (len(negs) < num_negs):
            neg = random.choice(vocab)

            # 保证负样本neg与pos1没有边相连接
            if not (neg in network[pos1] or pos1 in network[neg] or neg == pos1):
                dist_neg_ = dist1(emb[pos1], emb[neg])
                dist_neg = np.arccosh(dist_neg_)
                negs.append([pos1, neg])
                dist_negs_.append(dist_neg_) # 保存中间变量gamma,加速计算
                dist_negs.append(dist_neg)

        # 针对一个样本的损失
        loss_neg = 0.0
        for dist_neg in dist_negs:
            loss_neg += exp(-1 * dist_neg)
        loss.append(dist_pos + log(loss_neg))

        # 损失函数 对 正样本对 距离 d(u, v) 的导数
        grad_L_pos = -1

        # 损失函数 对 负样本对 距离 d(u, v') 的导数
        grad_L_negs = []
        for dist_neg in dist_negs:
            grad_L_negs.append(exp(-dist_neg) / loss_neg)

        # 计算正样本对中两个样本的embedding的更新方向
        grad_pos1 = grad_L_pos * compute_distance_gradients(emb[pos1], emb[pos2], dist_pos_)
        grad_pos2 = grad_L_pos * compute_distance_gradients(emb[pos2], emb[pos1], dist_pos_)

        # 计算负样本对中所有样本的embedding的更新方向
        grad_negs_final = []
        for (grad_L_neg, neg, dist_neg_) in zip(grad_L_negs[1:], negs[1:], dist_negs_[1:]):
            grad_neg0 = grad_L_neg * compute_distance_gradients(emb[neg[0]], emb[neg[1]], dist_neg_)
            grad_neg1 = grad_L_neg * compute_distance_gradients(emb[neg[1]], emb[neg[0]], dist_neg_)
            grad_negs_final.append([grad_neg0, grad_neg1])

        # 更新embeddings
        emb[pos1] = update(emb[pos1], -grad_pos1, lr)
        emb[pos2] = update(emb[pos2], -grad_pos2, lr)
        for (neg, grad_neg) in zip(negs, grad_negs_final):
            emb[neg[0]] = update(emb[neg[0]], -grad_neg[0], lr)
            emb[neg[1]] = update(emb[neg[1]], -grad_neg[1], lr)

    # 输出损失
    if ((epoch) % 10 == 0):
        print(epoch + 1, "---Loss: ", sum(loss))

    # 绘制二维embeddings
    if ((epoch) % 100 == 0):
        plotall(epoch + 1)

3. 结果表现

结果如下所示(与论文有些不一致):

实际上应该还是有效的,有些团都能聚合在一起,下面是一个随机训练的结果(可以看出非常混乱):

其他参考资料

Poincaré Embeddings for Learning Hierarchical Representations

Implementing Poincaré Embeddings

models.poincare – Train and use Poincare embeddings

How to make a graph on Python describing WordNet's synsets (NLTK)

networkx.drawing.nx_pylab.draw_networkx

以上就是python算法学习双曲嵌入论文方法与代码解析说明的详细内容,更多关于python双曲嵌入论文方法与代码的资料请关注我们其它相关文章!

(0)

相关推荐

  • python opencv把一张图片嵌入(叠加)到另一张图片上的实现代码

    python opencv把一张图片嵌入(叠加)到另一张图片上 1.背景: 最近做了个烟火生成系统的界面设计,需要将烟雾图片嵌入到任意一张图片中,因此需要python opencv把一张图片嵌入(叠加)到另一张图片上的知识.(图中红框最终生成图片没有的,只是界面有这个功能) 2.代码 resized1[global_y0:height+global_y0, global_x0:weight+global_x0] = resized0 resized0是小图 resized1是大图,其他参数是左上

  • python算法学习双曲嵌入论文代码实现数据集介绍

    目录 1. 目标 Python 代码依赖库 2. 数据集 数据展示 学习的文章: Poincaré Embeddings for Learning Hierarchical Representations 主要参考的代码: poincare_embeddings gensim – Topic Modelling in Python - poincare.py 由于有些代码难以运行,有些比较难读(封装程度非常高)甚至有些代码写得存在一些问题.因此我们重新按照论文的设置,利用Python重现了对应的

  • Python如何实现在字符串里嵌入双引号或者单引号

    两种方法实现: 1.在双引号前面加个转义符 \ ,即反斜杠.如"Hello \"W \"orld",会输出 Hello "W"orld 2.用单引号引起整个字符串,如'Hello "W"orld',同样输出 Hello "W"orld 同理也可以在字符串里嵌入单引号,如"Hello 'W'orld",输出 Hello 'W'orld 补充拓展:python中单引号(').双引号(&quo

  • 将Python代码嵌入C++程序进行编写的实例

    把python嵌入的C++里面需要做一些步骤 安装python程序,这样才能使用python的头文件和库     在我们写的源文件中增加"Python.h"头文件,并且链入"python**.lib"库(还没搞清楚这个库时静态库还是导出库,需要搞清楚)     掌握和了解一些python的C语言api,以便在我们的c++程序中使用 常用的一些C API函数 在了解下面的函数之前有必要了解一下**PyObject***指针,python里面几乎所有的对象都是使用这个指

  • python算法学习双曲嵌入论文方法与代码解析说明

    目录 1. 方法说明 损失函数 梯度下降 梯度求解 2. 代码训练过程 3. 结果表现 其他参考资料 本篇接上一篇:python算法学习双曲嵌入论文代码实现数据集介绍 1. 方法说明 首先学习相关的论文中的一些知识,并结合进行代码的编写.文中主要使用Poincaré embedding. 对应的python代码为: def dist1(vec1, vec2): # eqn1 diff_vec = vec1 - vec2 return 1 + 2 * norm(diff_vec) / ((1 -

  • python算法学习之计数排序实例

    python算法学习之计数排序实例 复制代码 代码如下: # -*- coding: utf-8 -*- def _counting_sort(A, B, k):    """计数排序,伪码如下:    COUNTING-SORT(A, B, k)    1  for i ← 0 to k // 初始化存储区的值    2    do C[i] ← 0    3  for j ← 1 to length[A] // 为各值计数    4    do C[A[j]] ← C[A

  • C++数据结构与算法之双缓存队列实现方法详解

    本文实例讲述了C++数据结构与算法之双缓存队列实现方法.分享给大家供大家参考,具体如下: "双缓存队列"是我在一次开发任务中针对特殊场景设计出来的结构.使用场景为:发送端持续向接收端发送数据包--并且不理会接收端是否完成业务逻辑.由于接收端在任何情况下停止响应即可能产生数据丢失,因此无法简单的设计一条线程安全队列来对数据写入或读取(读取数据时将队列上锁视为对写入的停止响应). 鉴于此,我的设计思路如下: 接收端首先向A队列中写入数据,然后当数据处理请求到来的时候切换到B队列继续写入,之

  • Python实现仿真双径效应的方法

    多径效应 多径效应(multipath effect):指电磁波经不同路径传播后,各分量场到达接收端时间不同,按各自相位相互叠加而造成干扰,使得原来的信号失真,或者产生错误.比如电磁波沿不同的两条路径传播,而两条路径的长度正好相差半个波长,那么两路信号到达终点时正好相互抵消了(波峰与波谷重合).通常采用一些近似方法来描述信号的传播特性,其中最常见的一种近似方法是射线跟踪计算.射线跟踪模型将波前近似为简单粒子,进而确定出反射和折射对波前的影响.最简单的射线跟踪模型是双径模型,指发射机和接收机之间只

  • Python深度学习pyTorch权重衰减与L2范数正则化解析

    下面进行一个高维线性实验 假设我们的真实方程是: 假设feature数200,训练样本和测试样本各20个 模拟数据集 num_train,num_test = 10,10 num_features = 200 true_w = torch.ones((num_features,1),dtype=torch.float32) * 0.01 true_b = torch.tensor(0.5) samples = torch.normal(0,1,(num_train+num_test,num_fe

  • Python+Matplotlib绘制双y轴图像的示例代码

    目录 双Y轴图简介 实现思路 实现代码 样式一 样式二 双Y轴图简介 双Y轴图顾名思义就是在一个图里有两个Y轴.这种图形主要用来展示两个因变量和一个自变量的关系并且两个因变量的数值单位还不同.如我们想要展示不同月份公司销业绩以及成本的变化情况这时就可以用双Y轴图来展示.(因变量销量和成本具有不同的单位). 实现思路 绘制双y轴的思想,也是用到了matplotlib面向对象绘图的思想.在不指定位置的情况下,在一个画布上创建出两个坐标系,其中第一个坐标系正常创建,第二个坐标系则使用专有的twinx(

  • python获取当前运行函数名称的方法实例代码

    python获取当前运行函数名称的方法实例代码 摘要: c/c++中获取函数所在源码名,函数名和行号的方法很简单 __FILE__,__FUNCTION__和__LINE__ python没有这种语法,但也可以通过某种方法得到,这里给出例子,使用异常信息得到[可能会损失性能] 直接贴代码[可参考python核心编程4.4] #获取调用该函数所在(被调用)的函数名 #author:peterguo@vip.qq.com def get_func_name(): import sys try: ra

  • python算法与数据结构之单链表的实现代码

    =一.链表 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域. 相比于线性表顺序结构,操作复杂.由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是

  • java字符串的截取方法substring()代码解析

    这篇文章主要介绍了java字符串的截取方法substring()代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 返回位于 String 对象中指定位置的子字符串. public class StringTest { public static void main(String[] args) { String name="jason"; System.out.println(name.substring(0,1)); //结果

随机推荐