浅谈Embedding

Embedding的中文含义是嵌入(将一个物品嵌入到另一个空间中),这种思想同Embedding在深度学习中的作用类似。Embedding最早应是出现在NLP领域,比如word2vec,后来推广至其他领域,例如搜推广等。在学习过一段时间后,现对Embedding的作用和生成方法做一个简要的总结。

Embedding是对某个对象在低维稠密空间上的一个向量表示,它可以将高维稀疏特征转化为一个低维的稠密向量,进而提高模型的泛化性能,这个向量表示表征出了原始对象的某些特点,Embedding之间的距离可表示两个对象之间的相似性。另外Embedding的生成可以和上层深度神经网络模型的训练独立进行,因此在迁移学习或者冷启动中也有相应的应用。

早期的矩阵分解已经有了Embedding的思想,例如对共现矩阵进行分解得到隐向量的过程。后面就是词向量在自然语言处理领域大放异彩,并逐渐取代了以往的n-gram方法,其中代表性的word2vec方法是CBOW和Skip-gram方法,总体的网络结构如下:

image.png

词向量的生成可以看做是对离散变量(特征)求对应的Embedding的过程,输入的是每个词对应的one-hot向量,然后对应的隐层中每行的权重即为Embedding向量。one-hot向量和隐层权重的乘积本质上是一个查表的过程。在推荐领域,求离散变量对应的Embedding的过程也是一个查表的过程,即需要对不同的离散变量进行编号,然后在表中查找对应位置的Embedding向量。

对于连续变量,求Ebedding的方法目前有多种:例如直接将连续型变量输入到DNN中输出Embedding,然后再和离散型变量的Embedding进行concat;另外一种方法是Field Embedidng,划分为多个域,同一个域内共享同一个Embedding,连续变量对应的Embedding为其数值乘以对应域内的Embedding;第三种方法则是对连续变量进行离散化,然后转化为求离散变量的Embedding,这种方法的缺点是生成的Embedding可能不具备足够的区分性,例如连续值处于离散化边界或者离散化为同一个值的情况。在2021 KDD上华为提出了名为AutoDis的连续变量的Embedding方法,此方法将连续值输入到DNN中,经过softmax输出对应的K个桶的概率,每个桶对应的一个Embedding,然后用最大池化或者Top-k或者加权平均进行聚合得到对应的Embedidng,此过程融合了attention的思想。下面是Embedding的一种代码实现方式(连续变量进行离散化):

class CpuEmbedding(nn.Module):
    def __init__(self, num_embeddings, embed_dim):
        super(CpuEmbedding, self).__init__()
        self.weight = nn.Parameter(torch.zeros((num_embeddings, embed_dim)))
        nn.init.xavier_uniform_(self.weight.data)

    def forward(self, x):
        """
        :param x: shape (batch_size, num_fields)
        :return: shape (batch_size, num_fields, embedding_dim)
        """
        return self.weight[x]


class Embedding:
    def __new__(cls, num_embeddings, embed_dim):
        if torch.cuda.is_available():
            embedding = nn.Embedding(num_embeddings, embed_dim)
            nn.init.xavier_uniform_(embedding.weight.data)
            return embedding
        else:
            return CpuEmbedding(num_embeddings, embed_dim)


class FeaturesEmbedding(nn.Module):
    def __init__(self, field_dims, embed_dim):
        super(FeaturesEmbedding, self).__init__()
        self.embedding = Embedding(sum(field_dims), embed_dim)

        # e.g. field_dims = [2, 3, 4, 5], offsets = [0, 2, 5, 9]
        self.offsets = np.array((0, *np.cumsum(field_dims)[:-1]), dtype=np.long)
复制代码

另外按照Embedding的生成任务的类型分类,可以将其分为序列生成和目标拟合两种,比如词向量的生成属于序列生成类型,推荐领域中双塔模型的Embedding属于目标拟合类型。从更广的层面看,利用无监督学习方法得到的表征Representation也是一种形式的Embedding,例如各种形式的编码器Encoder输出的表征,或者通过对比学习得到的表征,都可以按照Embedding的形式用于下游的任务,另外还有图卷积中Graph Embedding,也是一块可以深挖的领域。

接下来需要解决的问题是:如何评估生成的Embedding的好坏?Embedding之间的相似性如何评价?user_id或者item_id对应的Embedding如何生成?这些问题在后面的文章会做解答。

猜你喜欢

转载自juejin.im/post/7093874978252144677