word order and functional word_s盒算法[通俗易懂]

word order and functional word_s盒算法[通俗易懂]WRD算法的应用_wordrotator鈥檚distance(wrd)

         在做NLP项目的时候,要计算两个文本的相似度——字面上语义的相似,一般而言在文本比较短的情况下,采用bert模型学习到文本向量然后使用cos函数计算余弦相似性。当文本较长的时候,目前没有一个很好的解决方案——最近谷歌提出了一个基于文档级别的transformer模型,可以学习到长度为2048的文本向量,代码还没有放出来,我几乎是实现不了的——我们这边就会采取提取关键词来做相似性的计算。来度量这一批关键词的相似性,就可以使用WMD算法,也就是词移距离;现在又出来了WRD算法,它是基于WMD算法的基础上提出的一种新的算法,克服了WMD算法的一些局限性。很值得被用到工程实践中。苏剑林大佬写一篇博客,详细的介绍了EM、WMD和WRD算法,文章地址——从EMD、WMD到WRD:文本向量序列的相似度计算

一、WRD算法概览和参考实现

这部分的内容,直接是转载苏剑林大佬的文章的。内容如下:

WMD其实已经挺不错了,但非要鸡蛋里挑骨头的话,还是能挑出一些缺点来:

1、它使用的是欧氏距离作为语义差距度量,但从Word2Vec的经验我们就知道要算词向量的相似度的话,用coscos往往比欧氏距离要好;

2、WMD理论上是一个无上界的量,这意味着我们不大好直观感知相似程度,从而不能很好调整相似与否的阈值。

为了解决这两个问题,一个比较朴素的想法是将所有向量除以各自的模长来归一化后再算WMD,但这样就完全失去了模长信息了。最近的论文《Word Rotator’s Distance: Decomposing Vectors Gives Better Representations》则巧妙地提出,在归一化的同时可以把模长融入到约束条件p,qp,q里边去,这就形成了WRD。

基本形式 #

首先,WRD提出了“词向量的模长正相关于这个词的重要程度”的观点,并通过一些实验结果验证了这个观点。事实上,这个观点跟笔者之前提出的simpler glove模型的观点一致,参考《更别致的词向量模型(五):有趣的结果》。而在WMD中,pi,qjpi,qj某种意义上也代表着对应句子中某个词的重要程度,所以我们可以设

word order and functional word_s盒算法[通俗易懂]

这就是Word Rotator’s Distance(WRD)了。由于使用的度量是余弦距离,所以两个向量之间的变换更像是一种旋转(rotate)而不是移动(move),所以有了这个命名;同样由于使用了余弦距离,所以它的结果在[-1,1]内,相对来说更容易去感知其相似程度。

代码实现参考

def wasserstein_distance(p, q, D):
    """
    通过线性规划求Wasserstein距离——EMD距离——推土机距离
    p.shape=[m], q.shape=[n], D.shape=[m, n]
    p.sum()=1, q.sum()=1, p∈[0,1], q∈[0,1]
    :param p:关键词向量序列或者字向量序列
    :param q:关键词向量序列或者字向量序列
    :param D:
    :return:
    """
    A_eq = []
    for i in range(len(p)):
        A = np.zeros_like(D)
        A[i, :] = 1
        A_eq.append(A.reshape(-1))
    for i in range(len(q)):
        A = np.zeros_like(D)
        A[:, i] = 1
        A_eq.append(A.reshape(-1))
    A_eq = np.array(A_eq)
    b_eq = np.concatenate([p, q])
    D = D.reshape(-1)
    result = linprog(D, A_eq=A_eq[:-1], b_eq=b_eq[:-1])
    return result.fun

def word_rotator_distance(x, y):
    """WRD(Word Rotator's Distance)
    x.shape=[m,d], y.shape=[n,d]
    """
    x_norm = (x**2).sum(axis=1, keepdims=True)**0.5
    y_norm = (y**2).sum(axis=1, keepdims=True)**0.5
    p = x_norm[:, 0] / x_norm.sum()
    q = y_norm[:, 0] / y_norm.sum()
    D = 1 - np.dot(x / x_norm, (y / y_norm).T)
    return wasserstein_distance(p, q, D)

def word_rotator_similarity(x, y):
    """1 - WRD
        x.shape=[m,d], y.shape=[n,d]
        """
    return 1 - word_rotator_distance(x, y)

给定一个向量对X和Y,以上代码就可以实现WRD距离的计算,得出想要的文本距离。

二、WRD算法的应用

背景如下:现有query 文本100条,待匹配的match数据有400W条。可以使用word2vec把文本数据转化我词向量,然后根据文本对的所有词向量来计算两个文本之间的WRD距离,把每个query最相似的10条推荐出来!

先看看WRD计算一次的消耗时间:

    kw_vecotr = np.random.randn(10,100)
    ele = np.random.randn(7,100)
    t1 = time.time()
    word_rotator_similarity(kw_vecotr, ele)  # 0.02s
    t2 = time.time()
    print('word_rotator_similarity time is %.4f'%(t2-t1))
python WRD_recomm.py 
wasserstein_distance linprog time 0.0227
word_rotator_similarity time is 0.0230

计算一次的时间就是0.023秒,其实耗费的时间全部在如下函数中的线性规划求解中:

def wasserstein_distance(p, q, D):
    ...
    result = linprog(D, A_eq=A_eq[:-1], b_eq=b_eq[:-1])#线性规划求解
    ...

一般的逻辑流程这里需要进行100*400W次计算,然后排序得到结果。最少的时间是4亿*0.023s =106天,采用多进程10个进程也需要10天,一条结果需要十分之一天,大约2.4个小时!所以需要进行优化。在从EMD、WMD到WRD:文本向量序列的相似度计算文章中作者提到了一个下界公式,在计算WRD之前,用一种高效的、不精确的算法把待匹配样本过滤一些,从而减少计算成本。我们这里的场景下,就是减小400W,给缩减到几十几百几千,就能极大的提高每条query得到结果的速度!

下界公式

公式来自文章

word order and functional word_s盒算法[通俗易懂]

我尝试的给出代码实现如下:

def dis_lower_boundary(x,y):
    """
    wrd的一个下界距离
    """
    x_norm = (x ** 2).sum(axis=1, keepdims=True) ** 0.5
    y_norm = (y ** 2).sum(axis=1, keepdims=True) ** 0.5
    z_x = x.sum(axis=0)/x_norm.sum()
    z_y = y.sum(axis=0)/y_norm.sum()
    dis = ((z_x-z_y) ** 2).sum()**0.5 * 0.5
    return dis

可以看看计算400W次dis_lower_boundary需要的时间是多少:

    kw_vecotr = np.random.randn(10, 100)
    ele = np.random.randn(7, 100)
    t1 = time.time()
    dis_lower_boundary(kw_vecotr, ele)  # 0.02s
    t2 = time.time()
    print('dis_lower_boundary 1  time is %.6f' % (t2 - t1))
    t1 = time.time()
    for i in tqdm(range(4000000)):
        dis_lower_boundary(kw_vecotr, ele)  # 0.02s
    t2 = time.time()
    print('dis_lower_boundary 4000W time is %.6f'%(t2-t1))

结果显示:

word order and functional word_s盒算法[通俗易懂]

可以看到只需要126秒。设置合适的过滤值,可以把400W缩减到5K级别。一条query得出的结果为0.023*5000+126=241s!相比之前没有采用下界公式过滤的2.4个小时10000S,提升了很多!把多进程也考虑进来241s还可以进一步优化到10s以内。如果能把线性规划的求解时间再缩小10倍,那么提升的将会更大!

这里主要的是大数据量的情况下,时间效率问题的解决方案的一个探索!

总结

本文就WRD算法在具体业务场景下进行简单的应用,并初步解决一定的效率问题。首先从苏剑林大佬的文章出发,简单介绍了WRD算法的原理和代码实现,其次就具体的业务场景下,如何使用WRD算法以及碰到了效率问题的时候,如何使用WRD算法下界公式来优化提升效率,做了详细的说明。这里并没有涉及到算法最后推荐效果的分析,这个又是另外一个层面的问题了,不做介绍!

参考文章

从EMD、WMD到WRD:文本向量序列的相似度计算

 

 

 

 

今天的文章word order and functional word_s盒算法[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/85076.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注