瑞星 发表于 2025-4-1 06:17:20

TensorFlow深度学习实战(13)——神经嵌入详解

0. 媒介

神经嵌入 (Neural Embedding) 是一种通过神经网络模子将离散的符号(如词语、字符、图像等)映射到低维连续向量空间中的技能。它属于更广泛的嵌入 (Embedding) 技能范畴,在深度学习中起着关键作用。神经嵌入通过在神经网络练习过程中学习到的向量表现,捕捉了输入数据的潜在特征和语义信息。
1. 神经嵌入简介

自 Word2Vec 和 GloVe 提出以来,词嵌入技能已经取得了多方向的发展。其中一种方向是将词嵌入应用于非词汇情况,也称为神经嵌入 (Neural Embedding) 。我们知道,词嵌入利用了分布假设,即出现在相似上下文中的词通常具有相似的含义,其中上下文通常是围绕目标词的一个固定巨细(单词数目)的窗口。
神经嵌入的核心思想与之相似,即出现在相似上下文中的实体通常彼此密切相关。构建这些上下文的方式通常依赖于具体情况。接下来,我们将先容两种基础且通用的技能,可以或许应用于多种用例。
1.1 Item2Vec

Item2Vec 嵌入模子最早由 Barkan 和 Koenigstein 提出,用于协同过滤 (collaborative filtering),即根据具有类似购买历史的用户向目标用户保举商品。使用商品作为“词”,用户随时间购买的商品聚集(即商品序列)作为“句子”,从中推导出“词上下文”。
比方,考虑在超市向购物者保举商品的标题。假设超市销售 5,000 种商品,因此每种商品可以表现为巨细为 5,000 的稀疏独热编码向量,每个用户由其购物车表现,即一系列独热编码向量。应用类似于 Word2Vec 中的上下文窗口,可以练习一个 skip-gram 模子来猜测大概的商品对。学习到的嵌入模子将商品映射到一个稠密的低维空间,其中相似的商品彼此靠近,可以用于进行相似商品的保举。
1.2 node2vec

node2vec 嵌入模子由 Grover 和 Leskovec 提出,作为一种可扩展的方式学习图中节点特征。通过在图上实验大量固定长度的随机游走来学习图结构的嵌入。节点是视为“单词”,随机游走是从中派生“词上下文”的“句子”。
2. 数据集与模子分析

为了阐明创建自界说神经嵌入的简便性,我们将利用词语之间的共现关系,生成类似于 node2vec 的模子或者更准确地说是一个基于图的嵌入,即 DeepWalk,用于分析 1987-2015 年间 NeurIPS 会议上的论文。
数据集是一个 11,463×5,812 的词频矩阵,其中行代表词语,列代表会议论文。我们将利用此数据构建论文的图,其中两篇论文之间的边表现它们之间共同出现的词语。
node2vec 和 DeepWalk 都假设图是无向且无权重的。构建的图是无向的,因为两篇论文之间的关系是双向的。但是,边可以根据两篇文档之间词语共现的数目进行加权。在本节中,我们将共现数目大于 0 的情况视为有用的无权重边。
3. 实现神经嵌入

(1) 起首,导入所需库:
import gensim
import logging
import numpy as np
import os
import shutil
import tensorflow as tf

from scipy.sparse import csr_matrix
# from scipy.stats import spearmanr
from sklearn.metrics.pairwise import cosine_similarity

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
(2) 从 UCI 下载数据,并将其转换为稀疏的词-文档矩阵 TD,然后通过将词-文档矩阵的转置与自身相乘来构建文档-文档矩阵 E。构建的图通过文档-文档矩阵表现为连接矩阵或边矩阵。由于每个元素代表两个文档之间的相似性,我们通过将矩阵E中的非零元素设置为 1 来对矩阵 E 进行二值化处置惩罚:
DATA_DIR = "./data"
UCI_DATA_URL = "https://archive.ics.uci.edu/ml/machine-learning-databases/00371/NIPS_1987-2015.csv"

def download_and_read(url):
    local_file = url.split('/')[-1]
    p = tf.keras.utils.get_file(local_file, url, cache_dir=".")
    row_ids, col_ids, data = [], [], []
    rid = 0
    f = open(p, "r")
    for line in f:
      line = line.strip()
      if line.startswith("\"\","):
            # header
            continue
      if rid % 100 == 0:
            print("{:d} rows read".format(rid))
      # compute non-zero elements for current row
      counts = np.array(])
      nz_col_ids = np.nonzero(counts)
      nz_data = counts
      nz_row_ids = np.repeat(rid, len(nz_col_ids))
      rid += 1
      # add data to big lists
      row_ids.extend(nz_row_ids.tolist())
      col_ids.extend(nz_col_ids.tolist())
      data.extend(nz_data.tolist())
    print("{:d} rows read, COMPLETE".format(rid))
    f.close()
    TD = csr_matrix((
      np.array(data), (
            np.array(row_ids), np.array(col_ids)
            )
      ),
      shape=(rid, counts.shape))
return TD

# read data and convert to Term-Document matrix
TD = download_and_read(UCI_DATA_URL)
# compute undirected, unweighted edge matrix
E = TD.T * TD
# binarize
E = 1
print(E.shape)
(3) 获取了稀疏二值化的连接矩阵E后,可以从每个顶点生成随机游走。从每个节点开始,构造 32 条最大长度为 40 个节点的随机游走,这些随机游走具有 0.15 的随机重启概率,这意味着对于任何节点,特定的随机游走有 15% 的概率在该节点竣事。构建随机游走,并将其写入由 RANDOM_WALKS_FILE 指定的文件。为了阐明输入的情况,查看该文件前 10 行,表现从节点 3274 开始的随机游走:
https://i-blog.csdnimg.cn/direct/c448cbecf3f9498aa21b85b72da788cd.png#pic_center
必要注意的是,这是一个非常缓慢的过程。
(4) RANDOM_WALKS_FILE 中的样本看起来像语言中的句子,其中词汇表是图中的所有节点 ID。我们已经知道,词嵌入利用语言的结构生成词的分布式表现。DeepWalk 和 node2vec 等图嵌入方案也使用随机游走生成的“句子”实验雷同的操作。这些嵌入可以捕捉图中节点之间的相似性:
NUM_WALKS_PER_VERTEX = 32
MAX_PATH_LENGTH = 40
RESTART_PROB = 0.15

RANDOM_WALKS_FILE = os.path.join(DATA_DIR, "random-walks.txt")

def construct_random_walks(E, n, alpha, l, ofile):
    """ NOTE: takes a long time to do, consider using some parallelization
      for larger problems.
    """
    if os.path.exists(ofile):
      print("random walks generated already, skipping")
      return
    f = open(ofile, "w")
    for i in range(E.shape):# for each vertex
      if i % 100 == 0:
            print("{:d} random walks generated from {:d} starting vertices"
                .format(n * i, i))
      if i <= 3273:
            continue
      for j in range(n):       # construct n random walks
            curr = i
            walk =
            target_nodes = np.nonzero(E)
            for k in range(l):   # each of max length l, restart prob alpha
                # should we restart?
                if np.random.random() < alpha and len(walk) > 5:
                  break
                # choose one outgoing edge and append to walk
                try:
                  curr = np.random.choice(target_nodes)
                  walk.append(curr)
                  target_nodes = np.nonzero(E)
                except ValueError:
                  continue
            f.write("{:s}\n".format(" ".join()))

    print("{:d} random walks generated from {:d} starting vertices, COMPLETE".format(n * i, i))
    f.close()

# construct random walks (caution: long process!)
construct_random_walks(E, NUM_WALKS_PER_VERTEX, RESTART_PROB, MAX_PATH_LENGTH, RANDOM_WALKS_FILE)
(5) 创建词嵌入模子。Gensim 包提供了一个简单的 API,允许我们创建和练习 Word2Vec 模子,练习好的模子将序列化到 W2V_MODEL_FILE 指定的文件中。Documents 类允许我们流式处置惩罚大型输入文件以练习 Word2Vec 模子,克制出现内存标题。使用 skip-gram 模式练习 Word2Vec 模子,窗口巨细为 10,这意味着练习模子来猜测给定中心顶点的最多五个相邻顶点。每个顶点的嵌入效果是一个巨细为 128 的稠密向量:
W2V_MODEL_FILE = os.path.join(DATA_DIR, "w2v-neurips-papers.model")

class Documents(object):
    def __init__(self, input_file):
      self.input_file = input_file

    def __iter__(self):
      with open(self.input_file, "r") as f:
            for i, line in enumerate(f):
                if i % 1000 == 0:
                  if i % 1000 == 0:
                        logging.info("{:d} random walks extracted".format(i))
                yield line.strip().split()


def train_word2vec_model(random_walks_file, model_file):
    if os.path.exists(model_file):
      print("Model file {:s} already present, skipping training"
            .format(model_file))
      return
    docs = Documents(random_walks_file)
    model = gensim.models.Word2Vec(
      docs,
      vector_size=128,    # size of embedding vector
      window=10,   # window size
      sg=1,      # skip-gram model
      min_count=2,
      workers=4
    )
    model.train(
      docs,
      total_examples=model.corpus_count,
      epochs=50)
model.save(model_file)

# train model
train_word2vec_model(RANDOM_WALKS_FILE, W2V_MODEL_FILE)
(6) 得到的 DeepWalk 模子现实上就是一个 Word2Vec 模子,因此在单词的上下文中可以用 Word2Vec 完成的任务,在顶点的上下文中也可以进行。使用该模子计算文档之间的相似性:
def evaluate_model(td_matrix, model_file, source_id):
    model = gensim.models.Word2Vec.load(model_file).wv
    most_similar = model.most_similar(str(source_id))
    scores = for x in most_similar]
    target_ids = ) for x in most_similar]
    # compare top 10 scores with cosine similarity between source and each target
    X = np.repeat(td_matrix.todense(), 10, axis=0)
    Y = td_matrix.todense()
    cosims = ), np.asarray(Y)) for i in range(10)]
    for i in range(10):
      print("{:d} {:s} {:.3f} {:.3f}".format(
            source_id, str(target_ids), cosims, scores))
# evaluate
source_id = np.random.choice(E.shape)
evaluate_model(TD, W2V_MODEL_FILE, source_id)
输出效果如下所示。第一列和第二列是源和目标顶点的 ID。第三列是对应于源和目标文档的词向量之间的余弦相似度,第四列是 Word2Vec 模子陈诉的相似度分数。可以看到,在 10 个文档对中,余弦相似度只得到 4 个相似度,但 Word2Vec 模子可以或许在嵌入空间中检测到潜在的相似性。这类似于在独热编码和稠密嵌入之间的举动:
https://i-blog.csdnimg.cn/direct/b322d90550584e65941aca430ff34b67.png#pic_center
小结

神经嵌入是将离散的符号映射到连续向量空间的强大技能,广泛应用于天然语言处置惩罚、保举系统、计算机视觉和图数据分析等领域。通过神经网络练习,这些嵌入可以或许有用地捕捉数据中的潜在关系和语义信息,为各种任务提供了有用的特征表现。随着深度学习的发展,神经嵌入已经成为当代人工智能系统中不可或缺的一部分。
系列链接

TensorFlow深度学习实战(1)——神经网络与模子练习过程详解
TensorFlow深度学习实战(2)——使用TensorFlow构建神经网络
TensorFlow深度学习实战(3)——深度学习中常用激活函数详解
TensorFlow深度学习实战(4)——正则化技能详解
TensorFlow深度学习实战(5)——神经网络性能优化技能详解
TensorFlow深度学习实战(6)——回归分析详解
TensorFlow深度学习实战(7)——分类任务详解
TensorFlow深度学习实战(8)——卷积神经网络
TensorFlow深度学习实战(9)——构建VGG模子实现图像分类
TensorFlow深度学习实战(10)——迁徙学习详解
TensorFlow深度学习实战(11)——风格迁徙详解
TensorFlow深度学习实战(12)——词嵌入技能详解

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: TensorFlow深度学习实战(13)——神经嵌入详解