[Graph Neural Network] Graph Neural Network (GNN) Notas de estudio: incrustación de gráficos


Graph existe ampliamente en varios escenarios en el mundo real, es decir, una colección de nodos y bordes. Por ejemplo, la conexión entre personas en redes sociales, la interacción de proteínas en biología, la comunicación entre direcciones IP en redes de comunicación, etc. Además, nuestra imagen y oración más comunes también se pueden considerar de manera abstracta como la estructura de un modelo de gráfico, y se puede decir que la estructura del gráfico es omnipresente.

La investigación en Graph puede resolver algunos de los siguientes problemas: por ejemplo, la predicción de nuevas relaciones en las redes sociales, las personas recomendadas que pueden conocer a las personas vistas en QQ; la predicción de funciones e interacciones de proteínas en biomoléculas; en redes de comunicación, predicción anormal y seguimiento de eventos y predicción del tráfico de red. Si queremos resolver los problemas anteriores, lo primero que debemos hacer es representar el gráfico. La incrustación de gráficos es una tecnología muy efectiva.

¿Por qué necesitamos incrustar la incrustación de gráficos?

Graph Embedding juega un papel importante en el análisis de datos de gráficos y el aprendizaje automático. Estas son algunas de las principales razones para la incrustación de gráficos:

  1. Reducción y visualización de la dimensionalidad: los datos del gráfico suelen ser de alta dimensión y contienen una gran cantidad de nodos y bordes. Al incorporar gráficos en espacios de baja dimensión, las estructuras gráficas complejas se pueden transformar en formas fáciles de entender y visualizar, lo que nos ayuda a observar y analizar intuitivamente las características y patrones de los gráficos.

  2. Representación de características: la incrustación de gráficos puede transformar nodos y bordes en representaciones vectoriales continuas, transformando así información discreta en gráficos en representaciones de características computables y aprendibles. Esto nos permite aplicar métodos tradicionales de aprendizaje automático y aprendizaje profundo para graficar datos y utilizar estas representaciones para tareas como la clasificación de nodos, la predicción de enlaces, la agrupación de gráficos, etc.

  3. Modelado de similitud y relación: las incrustaciones de gráficos pueden capturar la similitud y la relación entre los nodos y los bordes en el espacio de incrustación. Los nodos similares estarán cerca unos de otros en el espacio de incrustación, y los bordes con relaciones similares también exhibirán patrones similares en el espacio de incrustación. Esto proporciona una herramienta poderosa para sistemas de recomendación basados ​​en similitudes, análisis de redes sociales, predicción de enlaces y otras tareas.

  4. Transferencia de información y aprendizaje de representaciones: la incrustación de gráficos puede aprender representaciones de nodos más ricas al considerar las relaciones de proximidad entre nodos y bordes y transferir información. Al considerar la topología del gráfico durante el proceso de incrustación, el modelo de incrustación puede capturar la ubicación de los nodos en el gráfico y el contexto circundante, proporcionando así representaciones de nodos más ricas y precisas.

En general, la incrustación de gráficos proporciona una forma de transformar los datos de los gráficos en representaciones computables y aprendibles, lo que nos permite analizar, extraer y predecir gráficos utilizando técnicas de aprendizaje automático y aprendizaje profundo. Tiene amplias aplicaciones en muchos campos, como análisis de redes sociales, bioinformática, sistemas de recomendación, seguridad de redes, etc.

Incrustación de gráficos

La incrustación de gráficos es un proceso de mapeo de datos de gráficos (generalmente una matriz densa de alta dimensión) en un vector de baja densidad , que puede resolver el problema de que los datos de gráficos son difíciles de ingresar de manera eficiente en algoritmos de aprendizaje automático. La incrustación de gráficos necesita capturar la topología del gráfico, la relación entre los vértices y otra información, como subgráficos, bordes, etc. Si se representa más información, las tareas posteriores lograrán un mejor rendimiento. Hay un consenso en el proceso de incrustación: los nodos en el espacio vectorial que permanecen conectados están cerca unos de otros . Con base en esto, los investigadores propusieron mapas de características laplacianas ( Laplacian Eigenmaps) e incrustaciones lineales locales ( Locally Linear Embedding, LLE).

incrustación de gráficos
En general, la incrustación en el gráfico se puede dividir aproximadamente en dos tipos: incrustación de nodos e incrustación de gráficos . Cuando es necesario clasificar los nodos, predecir la similitud de los nodos y visualizar la distribución de los nodos, generalmente se usa la incrustación de nodos; cuando es necesario predecir a nivel de gráfico o predecir la estructura completa del gráfico, necesitamos representar el gráfico completo como un vector .

Los métodos clásicos como DeepWalk, node2vec, SDNE y graph2vec se presentarán más adelante.

¿Cuáles son las ventajas de usar incrustaciones de gráficos?

Los gráficos son representaciones significativas y comprensibles de datos, pero las representaciones integradas de gráficos son necesarias por las siguientes razones.

  1. El aprendizaje automático directamente en el gráfico tiene ciertas limitaciones . Todos sabemos que un gráfico se compone de nodos y bordes. Estas relaciones vectoriales solo pueden representarse mediante matemáticas, estadísticas o subconjuntos específicos, pero el espacio vectorial después de la incrustación tiene métodos informáticos más flexibles y ricos. ;
  2. La incrustación de gráficos puede comprimir datos y generalmente usamos una matriz de adyacencia para describir las conexiones entre los nodos en un gráfico. Las dimensiones de la matriz de conectividad son ∣ V ∣ x ∣ V ∣ |V| x |V|V x V , donde∣ V ∣ |V|V es el número de nodos en el gráfico. Cada columna y fila de la matriz representa un nodo. Un valor distinto de cero en la matriz indica que dos nodos están conectados. Es casi imposible usar una matriz de adyacencia con el espacio de características de un gráfico grande. Uno con1M 1M1 M nodos y1 M x 1 M 1Mx1M¿Cómo calcular y representar la gráfica de la matriz de adyacencia de 1 M x 1 M ? Pero la incrustación se puede considerar como una tecnología de compresión, que puede desempeñar un papel en la reducción de la dimensionalidad;
  3. El cálculo vectorial es más simple y rápido que operar directamente en el gráfico.

Sin embargo, la incrustación de gráficos también debe cumplir ciertos requisitos.

  • Selección de atributos : asegúrese de que las incrustaciones describan bien los atributos del gráfico. Deben representar la topología gráfica, las conexiones de nodos y los vecindarios de nodos. El rendimiento de las predicciones o visualizaciones depende de la calidad de las incrustaciones;
  • Escalabilidad : la mayoría de las redes reales son grandes y contienen una gran cantidad de nodos y bordes. Los métodos de incrustación deben ser escalables y capaces de manejar gráficos grandes. Definir un modelo escalable es un desafío, especialmente cuando el modelo tiene como objetivo preservar las propiedades globales de la red. El tamaño de la red no debería ralentizar el proceso de integración. Un buen método de incrustación no solo se incrusta de manera eficiente en imágenes pequeñas, sino que también debe poder incrustarse de manera eficiente en imágenes grandes;
  • Dimensiones de incrustación : es difícil encontrar la dimensión óptima de representación durante la incrustación real. Cuanto mayor sea la dimensión, más información se puede retener, pero generalmente tiene una mayor complejidad de tiempo y espacio. Aunque la dimensión inferior tiene poca complejidad temporal y espacial, sin duda perderá mucha información original en el gráfico.

¿Cuáles son los métodos de incrustación de gráficos?

Hay muchos métodos de incrustación de gráficos. A continuación, se muestran algunos métodos comunes de incrustación de gráficos:

  1. Node Embedding (Incrustación de nodos) : La incrustación de nodos se basa en el método word2vec.La razón por la que se puede establecer este método es que la distribución de los nodos en el gráfico y las palabras en el corpus siguen la ley de potencia.
    Figura 2

    • DeepWalk: utilice un paseo aleatorio para muestrear la secuencia de nodos en el gráfico y, a continuación, utilice el modelo Word2Vec para incrustar la secuencia de nodos en un espacio vectorial de baja dimensión.
    • node2vec: una estrategia de muestreo de nodos basada en caminatas aleatorias, que combina la búsqueda primero en profundidad y la búsqueda primero en amplitud para generar secuencias de nodos y luego usar Word2Vec para incorporar el aprendizaje.
    • LINE: aprenda incrustaciones de nodos maximizando la similitud de los nodos vecinos y minimizando la similitud de los nodos no vecinos.
  2. Incrustación de gráficos :

    • GraphSAGE: use las características del nodo vecino muestreado para aprender la representación de incrustación del nodo y realice el aprendizaje de incrustación de gráficos agregando las características de los nodos vecinos.
    • Graph Convolutional Network(GCN): la propagación de características se realiza en el gráfico a través de operaciones de convolución, y la representación de incrustación del nodo se aprende utilizando las características del nodo y sus nodos vecinos.
    • Graph Attention Network(GAT): use el mecanismo de autoatención para aprender la relación entre los nodos y realice el aprendizaje de incrustación de acuerdo con las características del nodo vecino del nodo.
  3. Incrustación de subgrafo :

    • GraphSAGE: Aprender a incrustar representaciones de subgrafos muestreando y agregando sus características de nodo y borde.
    • Graph Isomorphism Networks(GIN): aprendizaje de incrustaciones de subgráficos a través de un perceptrón multicapa que utiliza información estructural local de subgráficos.
  4. Red de atención de gráficos (Red de atención de gráficos) :

    • Graph Attention Network(GAT): mediante el uso del mecanismo de autoatención para aprender la relación entre los nodos y luego aprender la representación incrustada de los nodos.
    • Graph Attention Graph Neural Network(GAGNN): amplía GAT para aprender incrustaciones aprendiendo pesos de atención para nodos y subgráficos.
  5. Incrustación de paseo aleatorio (Incrustación de paseo aleatorio) :

    • DeepWalk: La secuencia de nodos en el gráfico se muestrea mediante un recorrido aleatorio y la secuencia de nodos se incrusta en un espacio vectorial de baja dimensión utilizando el modelo Word2Vec.
    • Node2Vec: estrategia de muestreo de nodos basada en la caminata aleatoria, combinada con búsqueda primero en profundidad y búsqueda primero en amplitud para generar secuencias de nodos, y luego usar Word2Vec para incorporar el aprendizaje.

Esta es solo una pequeña muestra de los métodos de incrustación de gráficos, hay muchos otros métodos y técnicas como Graph Autoencoders, GraphGAN, GraphSNE, etc. Cada método tiene sus características únicas y escenarios aplicables, y la elección de un método de incrustación de gráficos adecuado para una tarea específica y los datos deben evaluarse y seleccionarse de acuerdo con la situación específica.


Agregue a los modelos Word2Vec y Skip-gram en el campo NLP:

  • Word2vec es un método de incrustación que convierte palabras en vectores de incrustación. Palabras similares deben tener incrustaciones similares. Word2vec utiliza redes skip-gram, que son redes neuronales con una capa oculta (tres en total) . Al modelo skip-gram se le asigna una determinada palabra para predecir las palabras adyacentes al contexto . La siguiente figura muestra un ejemplo de palabras de entrada (marcadas en verde) y palabras predichas. Con esta tarea, los autores logran que dos palabras similares tengan incrustaciones similares, ya que dos palabras con significados similares pueden tener palabras vecinas similares.
    skip-gram
    Figura 3 modelo skip-gram. La entrada de la red está codificada en caliente, la longitud es la misma que la longitud del diccionario de palabras, solo una posición es 1 y la salida es la representación incrustada de la palabra.

A continuación se presentan tres métodos de incrustación de nodos y un método de incrustación de gráficos.Estos métodos son similares al principio de incrustación de Word2vec.

Método de incrustación de nodos (incrustaciones de nodos)

A continuación se presentan tres métodos clásicos de incrustación de nodos: DeepWalk , Node2vec , SDNE , LINE .

1. Caminata Profunda

Paseo Profundo
DeepWalk aprende la representación de una red a través de un paseo aleatorio (recorrido aleatorio truncado), y puede obtener mejores resultados cuando hay pocos vértices marcados en la red . La caminata aleatoria comienza desde el nodo seleccionado, luego se mueve desde el nodo actual a un vecino aleatorio y realiza una cierta cantidad de pasos. Este método se puede dividir aproximadamente en tres pasos:

  • Muestreo : Muestrear los nodos en el gráfico a través de caminatas aleatorias y obtener una secuencia de nodos dentro de un tiempo dado.La investigación en este documento muestra que realizar de 32 a 64 recorridos aleatorios de cada nodo es suficiente para representar la relación estructural de los nodos;
  • Entrenamiento skip-gram : Los recorridos aleatorios son comparables a las oraciones en el enfoque word2vec. La entrada de skip-gram en el texto es una oración, donde la entrada es una secuencia obtenida a partir de un muestreo de caminata aleatoria y predicha aún más al maximizar la probabilidad de predecir nodos adyacentes . Por lo general, se predicen alrededor de 20 nodos vecinos: 10 nodos a la izquierda y 10 nodos a la derecha;
  • Incrustación computacional :
    incrustación computacional

DeepWalk puede obtener la información de contexto local del punto en el gráfico a través de un recorrido aleatorio, por lo que el vector de representación aprendido refleja la estructura local del punto en el gráfico y los puntos adyacentes compartidos por dos puntos en el gráfico (o puntos adyacentes de orden superior). puntos) cuanto más, más corta es la distancia entre los dos vectores correspondientes . Pero el método DeepWalk realiza aleatoriamente paseos aleatorios, lo que significa que la incrustación no puede preservar bien la relación local de los nodos , y el método Node2vec puede resolver este problema.

El
algoritmo DeepWalk del código central de DeepWalk incluye principalmente dos pasos. El primer paso es recorrer aleatoriamente la secuencia de nodos de muestreo, y el segundo paso es usar skip-gram modelword2vec para aprender el vector de expresión.
Algoritmo de recorrido profundo
①Construya una red homogénea y realice un muestreo Random Walk de cada nodo en la red para obtener datos de entrenamiento asociados localmente; ②El entrenamiento SkipGram se realiza en los datos muestreados, y los nodos de red discretos se expresan como vectores para maximizar la unión Ahora, use Hierarchical Softmax como clasificador para la clasificación a gran escala.
Caminata aleatoria
El código central de la caminata aleatoria es el siguiente:

import random

def randomwalk(G,gamma,t):

    '''

    :param G: 图结构,networks生成
    :param gamma: 随机游走迭代轮数
    :param t: 随机游走步数
    :return: 返回游走序列
    '''
    W = [] #接收随机游走序列结果
    for i in range(gamma):
        node_list = list(G.nodes())
        random.shuffle(node_list) #打乱节点集合顺序
        for j in node_list:
            w = [j]
            v = j
            while len(w) < t:
                v_neig = list(G.neighbors(v)) #获取节点v所有邻居节点
                w.append(random.choice(v_neig))
                v = w[-1]
            W.append(w)
    return W

RandomWalk es un algoritmo transversal primero en profundidad que puede visitar repetidamente los nodos visitados . Dado el nodo de inicio de la visita actual, muestree aleatoriamente los nodos de sus vecinos como el siguiente nodo de visita y repita este proceso hasta que la duración de la secuencia de visita cumpla con las condiciones preestablecidas.
Paseo Profundo
El algoritmo específico del séptimo paso Skip-Gram es:
Saltar-Gramo
Desde la perspectiva del flujo del algoritmo, los nodos que se han visitado también se pueden visitar repetidamente, por lo que es un algoritmo transversal primero en profundidad que puede visitar repetidamente los nodos visitados. Entre ellos, la estrategia HS se refiere a Hierarchical Softmax

Código de muestra:

import random
from gensim.models import Word2Vec
import networkx as nx

# 构建图
G = nx.Graph()
G.add_edges_from([(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7), (4, 8), (4, 9)])

# 产生随机游走序列
def _simulate_walks(nodes, num_walks, walk_length):
  walks = []
  for _ in range(num_walks):
    random.shuffle(nodes)
    for v in nodes:
      walks.append(deepwalk_walk(walk_length=walk_length, start_node=v))
  return walks

# 从start_node开始随机游走
def deepwalk_walk(walk_length, start_node):
  walk = [start_node]
  while len(walk) < walk_length:
    cur = walk[-1]
    cur_nbrs = list(G.neighbors(cur))
    if len(cur_nbrs) > 0:
      walk.append(random.choice(cur_nbrs))
    else:
      break
  return walk

# 每个节点进行随机游走的次数
num_walks = 10  

# 每次随机游走的步数
walk_length = 5  

# 得到所有节点
nodes = list(G.nodes())

# 随机游走生成节点序列
walks = _simulate_walks(nodes, num_walks=num_walks, walk_length=walk_length)

# 使用Word2Vec模型学习节点嵌入
model = Word2Vec(walks, vector_size=32, window=5, min_count=0, sg=1, workers=4, epochs=5)

# 获取节点的嵌入表示
node_embeddings = model.wv

# 打印节点的嵌入向量
for node in G.nodes():
  print(f"Node {
      
      node}: {
      
      node_embeddings[node]}")

Paseo Profundo

El uso de paseos aleatorios tiene dos ventajas :

  1. Paralelización , la caminata aleatoria es local. Para una red grande, se pueden iniciar caminatas aleatorias de cierta longitud en diferentes vértices al mismo tiempo. Se pueden realizar múltiples caminatas aleatorias al mismo tiempo, lo que puede reducir el tiempo de muestreo.
  2. Adaptabilidad , que puede adaptarse a cambios locales en la red. La evolución de la red suele ser un cambio de puntos y bordes locales. Dichos cambios solo afectarán a una parte de la ruta de recorrido aleatorio, por lo que no es necesario volver a calcular el recorrido aleatorio de toda la red cada vez durante la evolución de la red.

2. LÍNEA

DeepWalk usa el paseo aleatorio DFS para muestrear nodos en el gráfico y usa word2vec para aprender la representación vectorial de los nodos en el gráfico de secuencia muestreado. LINE también es un método basado en la suposición de similitud de vecindad, pero a diferencia de DeepWalk, que usa DFS para construir vecindades, LINE puede considerarse como un algoritmo que usa BFS para construir vecindades . Además, LINE también se puede aplicar a gráficos ponderados ( DeepWalk solo se puede usar para gráficos no ponderados ).
LÍNEA
Dirección en papel: https://arxiv.org/abs/1503.03578

LINE (Incrustación de red de información a gran escala) es un algoritmo para generar incrustaciones de gráficos, cuyo objetivo es asignar nodos en un gráfico a un espacio vectorial de baja dimensión. Aprende incrustaciones de nodos al maximizar la probabilidad de relaciones vecinas entre nodos para preservar la similitud de los nodos en un espacio de baja dimensión.

La siguiente figura muestra un gráfico simple. Los bordes del gráfico pueden ser dirigidos o no, y el grosor de los bordes también representa el tamaño del peso:
grafico

similitud de primer orden

La similitud de primer orden se usa para describir la similitud local entre pares de vértices en el gráfico, y la descripción formal es si u , vu,vtu ,Hay una arista directa entre v , entonces el peso de la arista wuv w_{uv}wultravioletaEs decir, la semejanza entre dos vértices, si no hay arista directa, la semejanza de primer orden es 0. Como se muestra en la figura anterior, si hay un borde directo entre 6 y 7, y el peso del borde es grande, se considera que los dos son similares y la similitud de primer orden es alta. entre 5 y 6, la similitud de primer orden entre los dos se considera similar, la similitud es 0.

similitud de segundo orden

¿Es suficiente la similitud de primer orden? Obviamente no es suficiente, como se muestra en la figura anterior, aunque no hay una conexión directa entre 5 y 6, tienen muchos de los mismos vértices vecinos (1,2,3,4), lo que en realidad puede mostrar que 5 y 6 son similares , y se utiliza la similitud de 2 órdenes para describir esta relación. Definido formalmente como, sea pu = ( wu , 1 , . . . , wu , ∣ V ∣ ) p_u=(w_{u,1},...,w_{u,|V|})pagtu=( wtu , 1,... ,wtu , V) significa el vérticeuuLa similitud de primer orden entre u y todos los demás vértices, entoncesuutu dasvvLa similitud de segundo orden de v puede obtenerse mediante pu p_upagtuy pv p_vpagvrepresentación de semejanza. si tutu dasvvSi no hay vértices vecinos idénticos entre v , la similitud de segundo orden es 0.

optimizar el objetivo

(1) Primer orden
Para cada arista no dirigida ( i , j ) (i,j)( yo ,j ) , define el vérticevi v_ivyoy vj v_jvjLa probabilidad conjunta entre es p 1 ( vi , vj ) = 1 1 + exp ( − u ⃗ i ⋅ u ⃗ j ) p_1(v_i,v_j)=\frac{1}{1+exp(-\vec{u } _i \cdot \vec{u}_j)}pag1( vyo,vj)=1+e x pags ( -tu yotu j)1Entre ellos u ⃗ i \vec{u}_itu yoPara el vértice vi v_ivyoUna representación vectorial de baja dimensión de . Esta fórmula se puede considerar como un modelo de producto interno para calcular el grado de coincidencia entre dos artículos.
Defina también la distribución empírica p ^ 1 = wij W \hat{p}_1=\frac{w_{ij}}{W}pag^1=WwyoW = ∑ ( yo , j ) ∈ E wij W=\sum_{(i,j)\in E}w_{ij}W=( yo , j ) miwyo
El objetivo de optimización es minimizar: O 1 = d ( p ^ 1 , ( ⋅ , ⋅ ) ,p ^ 1 ( ⋅ , ⋅ ) ) O_1=d(\hat{p}_1,(\cdot, \cdot), \ sombrero{p}_1(\cdot, \cdot))O1=re (pag^1,( ,) ,pag^1( ,)) donde d(\cdot, \cdot) es la distancia entre dos distribuciones. El índice comúnmente usado para medir la diferencia entre dos distribuciones de probabilidadKL散度esO 1 = − ∑ ( i , j ) ∈ E wij log ⁡ pi ( vi , vj ) O_1=-\sum_{(i,j)\in E}w_{ij} \log p_i(v_i,v_j)O1=( yo , j ) miwyoiniciar sesiónpagyo( vyo,vj)
la similitud de primer orden solo se puede usar en grafos no dirigidos.
(2) La segunda etapa
mantiene dos vectores de incrustación para cada vértice, uno es el vector de representación del propio vértice y el otro es el vector de representación cuando el punto se utiliza como vértice de contexto de otros vértices.

Para borde dirigido ( i , j ) (i,j)( yo ,j ) , definir un vértice dadovi v_ivyoBajo la condición, generar contexto (vecino) vértice vj v_jvj的概率为p 2 ( vj ∣ vi ) = exp ( u ⃗ j ⋅ u ⃗ i ) ∑ k = 1 ∣ V ∣ exp ( u ⃗ k T ⋅ u ⃗ i ) p_2(v_j|v_i)=\frac{exp (\vec{u}_j\cdot \vec{u}_i)}{\sum_{k=1}^{|V|}exp(\vec{u}_k^T\cdot \vec{u}_i) }pag2( vjvyo)=k = 1Ve x p (tu kTtu yo)e x p (tu jtu yo)donde ∣ V ∣ |V|V es el número de vértices de contexto.

Objetivo de modificación O 2 = ∑ i ∈ V λ id ( pags ^ 2 ( ⋅ ∣ vi ) , pags 2 ( ⋅ ∣ vi ) ) O_2=\sum_{i\in V}\lambda_i d(\hat{p}_2 ( \cdot|v_i),p_2(\cdot|v_i))O2=yo Vyoyore (pag^2( vyo) ,pag2( vyo)) Entre ellos,λ i \lambda_iyoyoEl factor que controla la importancia de los nodos se puede estimar mediante métodos como el grado de los vértices o el PageRank.

La distribución empírica se define como: p ^ 2 ( vj ∣ vi ) = wijdi \hat{p}_2(v_j|v_i)=\frac{w_{ij}}{d_i}pag^2( vjvyo)=dyowyo, wij w_{ij}wyoes la arista ( i , j ) (i,j)( yo ,j ) es el peso del borde,di d_idyoes el vértice vi v_ivyofuera de grado, para un gráfico ponderado, di = ∑ k ∈ N ( I ) W ik d_i=\sum_{k\in N(I)}W_{ik}dyo=k norte ( yo )Wi

Use la divergencia KL y establezca λ i = di \lambda_i=d_iyoyo=dyo, ignorando el término constante, hay O 2 = − ∑ ( i , j ) ∈ E wij log ⁡ p 2 ( vj ∣ vi ) O_2=-\sum_{(i,j)\in E} w_{ij}\ registro p_2 (v_j|v_i)O2=( yo , j ) miwyoiniciar sesiónpag2( vjvyo)

Consejos de optimización

(1) Muestreo negativo (muestreo negativo)
Al calcular la similitud de segundo orden, el cálculo del denominador de la función softmax debe atravesar todos los vértices, lo cual es muy ineficiente. El documento utiliza la técnica de optimización de muestreo negativo, y el objetivo la función se convierte en: Iniciar sesión
⁡ σ ( tu ⃗ j T ⋅ tu ⃗ yo ) + ∑ yo = 1 KE vn ∼ PAGS norte ( v ) [ − Iniciar sesión ⁡ σ ( tu ⃗ norte T ⋅ tu ⃗ yo ) ] \log \sigma( \vec{u} _j^T\cdot \vec{u}_i)+\sum_{i=1}^K E_{v_n\sim P_n(v)}[-\log \sigma(\vec{u}_n ^T\cdot\vec{u}_i)]iniciar sesións (tu jTtu yo)+yo = 1kmivnPn( v )[ -iniciar sesións (tu norteTtu yo)] dondeKKK es el número de aristas negativas. En el artículo,P n ( v ) ∝ dv 3 / 4 P_n(v)\propto d_v^{3/4}PAGn( v )dv3/4dv d_vdves el vértice vvEl grado de salida de v .

(2) Edge Sampling (muestreo de borde)
advierte que nuestra función objetivo tiene un coeficiente de peso wij w_{ij} antes de logwyo, al optimizar parámetros usando el método de descenso de gradiente, wij w_{ij}wyose multiplicará directamente en el gradiente. Si la variación de los pesos de los bordes en el gráfico es grande, es difícil elegir una tasa de aprendizaje adecuada. Si se usa una tasa de aprendizaje mayor, el gradiente puede explotar para pesos de borde más grandes, y una tasa de aprendizaje más pequeña puede hacer que el gradiente sea demasiado pequeño para pesos de borde más pequeños.

Para el problema anterior, si todos los pesos de los bordes son iguales, entonces es fácil elegir una tasa de aprendizaje adecuada. Aquí se utiliza un método para dividir los bordes ponderados en bordes de igual peso.Si un peso es wwEl lado de w se divide enwww bordes con peso 1. Esto puede resolver el problema de la selección de la tasa de aprendizaje, pero debido al aumento en el número de aristas, también aumentarán los requisitos de almacenamiento.

Otro método es muestrear desde el borde ponderado original. La probabilidad de que cada borde sea muestreado es proporcional al peso del borde en el gráfico original, lo que no solo resuelve el problema de la tasa de aprendizaje, sino que tampoco genera demasiada sobrecarga de almacenamiento. . .

El algoritmo de muestreo aquí usa el algoritmo Alias, que es un O ( 1 ) O (1)Algoritmo de muestreo de eventos discretos con complejidad temporal O ( 1 ) . Para obtener más información, consulteMétodo de alias: Método de muestreo discreto con complejidad temporal O(1)

Código de muestra

La primera entrada son los números de los dos vértices, y luego se obtienen los vectores de incrustación correspondientes respectivamente, y finalmente se emite el resultado del producto interno. La etiqueta real se define como 1 o -1. El modelo final genera el producto interno y luego se puede optimizar el resultado del producto interno.
LÍNEA
Aquí, en la implementación, se integran los métodos de primer y segundo orden, y el orden de los hiperparámetros se puede usar para controlar si se optimiza por separado o de forma conjunta. El artículo recomienda la optimización por separado.

import math
import random

import numpy as np
import tensorflow as tf
from tensorflow.python.keras import backend as K
from tensorflow.python.keras.layers import Embedding, Input, Lambda
from tensorflow.python.keras.models import Model


def line_loss(y_true, y_pred):
    return -K.mean(K.log(K.sigmoid(y_true * y_pred)))


def create_model(numNodes, embedding_size, order='second'):
    v_i = Input(shape=(1,))
    v_j = Input(shape=(1,))

    first_emb = Embedding(numNodes, embedding_size, name='first_emb')
    second_emb = Embedding(numNodes, embedding_size, name='second_emb')
    context_emb = Embedding(numNodes, embedding_size, name='context_emb')

    v_i_emb = first_emb(v_i)
    v_j_emb = first_emb(v_j)

    v_i_emb_second = second_emb(v_i)
    v_j_context_emb = context_emb(v_j)

    first = Lambda(lambda x: tf.reduce_sum(
        x[0] * x[1], axis=-1, keepdims=False), name='first_order')([v_i_emb, v_j_emb])
    second = Lambda(lambda x: tf.reduce_sum(
        x[0] * x[1], axis=-1, keepdims=False), name='second_order')([v_i_emb_second, v_j_context_emb])

    if order == 'first':
        output_list = [first]
    elif order == 'second':
        output_list = [second]
    else:
        output_list = [first, second]

    model = Model(inputs=[v_i, v_j], outputs=output_list)
    return model, {
    
    'first': first_emb, 'second': second_emb}


def preprocess_nxgraph(graph):
    node2idx = {
    
    }
    idx2node = []
    node_size = 0
    for node in graph.nodes():
        node2idx[node] = node_size
        idx2node.append(node)
        node_size += 1
    return idx2node, node2idx


def create_alias_table(area_ratio):
    """
    :param area_ratio: sum(area_ratio)=1
    :return: accept,alias
    """
    l = len(area_ratio)
    accept, alias = [0] * l, [0] * l
    small, large = [], []
    area_ratio_ = np.array(area_ratio) * l
    for i, prob in enumerate(area_ratio_):
        if prob < 1.0:
            small.append(i)
        else:
            large.append(i)

    while small and large:
        small_idx, large_idx = small.pop(), large.pop()
        accept[small_idx] = area_ratio_[small_idx]
        alias[small_idx] = large_idx
        area_ratio_[large_idx] = area_ratio_[large_idx] - (1 - area_ratio_[small_idx])
        if area_ratio_[large_idx] < 1.0:
            small.append(large_idx)
        else:
            large.append(large_idx)

    while large:
        large_idx = large.pop()
        accept[large_idx] = 1
    while small:
        small_idx = small.pop()
        accept[small_idx] = 1

    return accept, alias


def alias_sample(accept, alias):
    """
    :param accept:
    :param alias:
    :return: sample index
    """
    N = len(accept)
    i = int(np.random.random() * N)
    r = np.random.random()
    if r < accept[i]:
        return i
    else:
        return alias[i]


class LINE:
    def __init__(self, graph, embedding_size=8, negative_ratio=5, order='second'):
        """
        :param graph:
        :param embedding_size:
        :param negative_ratio:
        :param order: 'first','second','all'
        """
        if order not in ['first', 'second', 'all']:
            raise ValueError('mode must be fisrt,second,or all')

        self.graph = graph
        self.idx2node, self.node2idx = preprocess_nxgraph(graph)
        self.use_alias = True

        self.rep_size = embedding_size
        self.order = order

        self._embeddings = {
    
    }
        self.negative_ratio = negative_ratio
        self.order = order

        self.node_size = graph.number_of_nodes()
        self.edge_size = graph.number_of_edges()
        self.samples_per_epoch = self.edge_size * (1 + negative_ratio)

        self._gen_sampling_table()
        self.reset_model()

    def reset_training_config(self, batch_size, times):
        self.batch_size = batch_size
        self.steps_per_epoch = ((self.samples_per_epoch - 1) // self.batch_size + 1) * times

    def reset_model(self, opt='adam'):
        self.model, self.embedding_dict = create_model(self.node_size, self.rep_size, self.order)
        self.model.compile(opt, line_loss)
        self.batch_it = self.batch_iter(self.node2idx)

    def _gen_sampling_table(self):
        # create sampling table for vertex
        power = 0.75
        numNodes = self.node_size
        node_degree = np.zeros(numNodes)  # out degree
        node2idx = self.node2idx

        for edge in self.graph.edges():
            node_degree[node2idx[edge[0]]] += self.graph[edge[0]][edge[1]].get('weight', 1.0)

        total_sum = sum([math.pow(node_degree[i], power) for i in range(numNodes)])
        norm_prob = [float(math.pow(node_degree[j], power))/total_sum for j in range(numNodes)]

        self.node_accept, self.node_alias = create_alias_table(norm_prob)

        # create sampling table for edge
        numEdges = self.graph.number_of_edges()
        total_sum = sum([self.graph[edge[0]][edge[1]].get('weight', 1.0) for edge in self.graph.edges()])
        norm_prob = [self.graph[edge[0]][edge[1]].get('weight', 1.0) * numEdges / total_sum for edge in self.graph.edges()]

        self.edge_accept, self.edge_alias = create_alias_table(norm_prob)

    def batch_iter(self, node2idx):
        edges = [(node2idx[x[0]], node2idx[x[1]]) for x in self.graph.edges()]
        data_size = self.graph.number_of_edges()
        shuffle_indices = np.random.permutation(np.arange(data_size))
        # positive or negative mod
        mod = 0
        mod_size = 1 + self.negative_ratio
        h = []
        t = []
        sign = 0
        count = 0
        start_index = 0
        end_index = min(start_index + self.batch_size, data_size)
        while True:
            if mod == 0:
                h = []
                t = []
                for i in range(start_index, end_index):
                    if random.random() >= self.edge_accept[shuffle_indices[i]]:
                        shuffle_indices[i] = self.edge_alias[shuffle_indices[i]]
                    cur_h = edges[shuffle_indices[i]][0]
                    cur_t = edges[shuffle_indices[i]][1]
                    h.append(cur_h)
                    t.append(cur_t)
                sign = np.ones(len(h))
            else:
                sign = np.ones(len(h)) * -1
                t = []
                for i in range(len(h)):
                    t.append(alias_sample(self.node_accept, self.node_alias))

            if self.order == 'all':
                yield ([np.array(h), np.array(t)], [sign, sign])
            else:
                yield ([np.array(h), np.array(t)], [sign])
            mod += 1
            mod %= mod_size
            if mod == 0:
                start_index = end_index
                end_index = min(start_index + self.batch_size, data_size)

            if start_index >= data_size:
                count += 1
                mod = 0
                h = []
                shuffle_indices = np.random.permutation(np.arange(data_size))
                start_index = 0
                end_index = min(start_index + self.batch_size, data_size)

    def get_embeddings(self, ):
        self._embeddings = {
    
    }
        if self.order == 'first':
            embeddings = self.embedding_dict['first'].get_weights()[0]
        elif self.order == 'second':
            embeddings = self.embedding_dict['second'].get_weights()[0]
        else:
            embeddings = np.hstack((self.embedding_dict['first'].get_weights()[0], self.embedding_dict['second'].get_weights()[0]))
        idx2node = self.idx2node
        for i, embedding in enumerate(embeddings):
            self._embeddings[idx2node[i]] = embedding
        return self._embeddings

    def train(self, batch_size=1024, epochs=1, initial_epoch=0, verbose=1, times=1):
        self.reset_training_config(batch_size, times)
        hist = self.model.fit_generator(self.batch_it, epochs=epochs, initial_epoch=initial_epoch, steps_per_epoch=self.steps_per_epoch, verbose=verbose)
        return hist

Ejecute el entrenamiento y la incrustación del nodo de salida:

import networkx as nx

# 构建图
G = nx.Graph()
G.add_edges_from([(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7), (4, 8), (4, 9)])

model = LINE(G,embedding_size=16,order='second')
model.train(batch_size=1,epochs=10,verbose=2)
embeddings = model.get_embeddings()

# 打印节点的嵌入向量
for node in G.nodes():
  print(f"Node {
      
      node}: {
      
      embeddings[node]}")

Salida de LÍNEA

3. SDNE

SDNE no utiliza un método de recorrido aleatorio, sino que utiliza un codificador automático para optimizar simultáneamente la similitud de primer y segundo orden. La representación vectorial aprendida conserva estructuras locales y globales y es sólida para redes dispersas.

Como se mencionó en el algoritmo LINE anterior, la similitud de primer orden caracteriza la similitud local entre nodos emparejados conectados por bordes. Dos nodos en la red se consideran similares si están conectados. La similitud de segundo orden representa la similitud de la estructura de vecindad del nodo, que puede representar la estructura de la red global. Dos nodos tienden a ser similares si comparten muchos vecinos.
SDNE
Figura 2 Marco SDNE de modelo profundo semisupervisado. Se compone principalmente de codificador y decodificador de dos partes. El codificador es el círculo rojo en la imagen, el decodificador es el círculo azul y el verde representa el vector de representación del nodo. La entrada xi x_i del modeloXyoes la ii de la matriz de adyacencia del grafoLa línea i representa el nodoiii y las conexiones de todos los nodos en la red, la entrada se asigna al espacio vectorial de baja dimensión a través del codificador, y se obtiene la representación vectorial de baja dimensiónyi ( K ) y_i^{(K)}yi( K ), y luego reconstruido a través del decodificador. Por lo tanto, las representaciones incrustadas aprendidas por los nodos con un gran número de vecinos iguales a través de la red del codificador automático serán relativamente similares, lo que mantiene la similitud de segundo orden de la red de gráficos. Además, para pares de nodos con bordes directos, SDNE restringirá las representaciones incrustadas de dos nodos para que estén cerca, manteniendo así la similitud de primer orden de la red de grafos.

El enfoque específico de SDNE es utilizar un codificador automático para preservar la proximidad de la red de primer y segundo orden . Lo hace optimizando conjuntamente estas dos aproximaciones. Este método utiliza funciones altamente no lineales para obtener incrustaciones . El modelo consta de dos partes: no supervisado y supervisado . El primero consiste en un autocodificador que tiene como objetivo encontrar una incrustación de un nodo que pueda reconstruir su vecindad . Este último se basa en un mapa de características de Laplacian que penaliza cuando los vértices similares se mapean lejos unos de otros en el espacio de incrustación .

La función de pérdida de SDNE se muestra a continuación y consta de tres partes: pérdida de similitud de primer orden , pérdida de similitud de segundo orden y término de regularización L2 . L min = L 2 nd + α L 1 st + v L reg \mathcal{L}_{min}=\mathcal{L}_{2nd}+\alpha\mathcal{L}_{1st}+v\mathcal {L}_{registro}Lminuto=L_ _+α L1 er _+vL _re _
L reg = 1 2 ∑ k = 1 K ( ∣ ∣ W ( k ) ∣ ∣ F 2 + ∣ ∣ W ^ ( k ) ∣ ∣ F 2 ) \mathcal{L}_{reg}=\frac{1}{ 2}\sum_{k=1}^K(||W^{(k)}||_F^2+||\hat{W}^{(k)}||_F^2)Lre _=21k = 1k( ∣∣ W( k )F2+∣∣W^( k )F2)
La primera parte es la pérdida de similitud de primer orden entre nodos vecinos:L 1 st = ∑ i , j = 1 nsi , j ∣ ∣ yi ( K ) − yj ( K ) ∣ ∣ 2 2 = ∑ i , j = 1 nsi , j ∣ ∣ yi − yj ∣ ∣ 2 2 \mathcal{L}_{1st}=\sum_{i,j=1}^ns_{i,j}||y_i^{(K)}-y_j^ {(K)}||_2^2=\sum_{i,j=1}^ns_{i,j}||y_i-y_j||_2^2L1 er _=yo , j = 1nsyo , j∣∣ yi( K )yj( K )22=yo , j = 1nsyo , j∣∣ yyoyj22

Thisri si , j s_{i,j}syo , jRepresenta la relación de conexión entre dos nodos (en una red no ponderada, 1 significa conectado, 0 significa sin conexión), yi y_iyyoRepresenta el nodo iiEl vector de incrustación de i , que es el nodo verde en el modelo anterior. El propósito de la similitud de primer orden es muy simple, que es acercar los vectores de representación entre los nodos vecinos, por lo queen la función de pérdida se espera que la diferencia entre ellos sea menor.

La similitud de segundo orden, la simple comprensión es esperar que los vectores de representación entre nodos con vecinos más similares sean más similares , y su pérdida se compone de la reconstrucción del autocodificador: L 2 nd = ∑ i = 1 n ∣ ∣ ( x ^ yo − xi ) ⊙ bi ∣ ∣ 2 2 = ∣ ∣ ( X ^ − X ) ⊙ segundo ∣ ∣ F 2 \mathcal{L}_{2nd}=\sum_{i=1}^n||( \ sombrero{x}_i-x_i)\odot b_i||_2^2=||(\sombrero{X}-X)\odot B||_F^2L_ _=yo = 1n∣∣ (X^yoXyo)byo22=∣∣ (X^x )segundo F2

Entre ellos xi x_iXyoes el nodo iiEl vector de adyacencia de i , es decir, saca el valor de fila del nodo en la matriz de adyacencia. El propósito de multiplicar B aquíes distinguir la diferencia entre los valores 0 y 1, porque el vector vecino de un nodo puede ser así: 0000100000000010001, donde 0 representa la mayor parte y 1 tiene solo una pequeña parte. realce el papel de 1, que se multiplique Tome un valor(se toma 5 en el artículo). Ya sea Deepwalk, LINE o Node2vec, sus principios de similitud de primer y segundo orden son similares. La similitud de primer orden es esperar que la similitud entre los nodos conectados sea mayor, y luego el segundo orden es tener más nodos vecinos idénticos Los vectores de representación entre nodos son algo similares.
SDNE
Código de muestra

import torch
import numpy as np
import networkx as nx
import scipy.sparse as sparse


class SDNEModel(torch.nn.Module):
  def __init__(self, input_dim, hidden_layers, alpha, beta, device='cpu'):
    '''Structural Deep Network Embedding(SDNE)
    :param input_dim: 节点数量 node_size
    :param hidden_layers: AutoEncoder中间层数
    :param alpha: 对于1st_loss的系数
    :param beta: 对于2nd_loss中对非0项的惩罚
    :param device:
    '''
    super(SDNEModel, self).__init__()
    self.alpha = alpha
    self.beta = beta
    self.device = device
    input_dim_copy = input_dim
    layers = []
    for layer_dim in hidden_layers:
      layers.append(torch.nn.Linear(input_dim, layer_dim))
      layers.append(torch.nn.ReLU())
      input_dim = layer_dim
    self.encoder = torch.nn.Sequential(*layers)

    layers = []
    for layer_dim in reversed(hidden_layers[:-1]):
      layers.append(torch.nn.Linear(input_dim, layer_dim))
      layers.append(torch.nn.ReLU())
      input_dim = layer_dim
    # 最后加一层输入的维度
    layers.append(torch.nn.Linear(input_dim, input_dim_copy))
    layers.append(torch.nn.ReLU())
    self.decoder = torch.nn.Sequential(*layers)
  
  def forward(self, A, L):
    '''输入节点的邻接矩阵和拉普拉斯矩阵,主要计算方式参考论文
    :param A: adjacency_matrix, dim=(m, n)
    :param L: laplace_matrix, dim=(m, m)
    :return:
    '''
    Y = self.encoder(A)
    A_hat = self.decoder(Y)
    # loss_2nd 二阶相似度损失函数
    beta_matrix = torch.ones_like(A)
    mask = A != 0
    beta_matrix[mask] = self.beta
    loss_2nd = torch.mean(torch.sum(torch.pow((A - A_hat) * beta_matrix, 2), dim=1))
    # loss_1st 一阶相似度损失函数 论文公式(9) alpha * 2 *tr(Y^T L Y)
    loss_1st =  self.alpha * 2 * torch.trace(torch.matmul(torch.matmul(Y.transpose(0,1), L), Y))
    return loss_2nd + loss_1st


def process_nxgraph(graph):
  node2idx = {
    
    }
  idx2node = []
  node_size = 0
  for node in graph.nodes():
    node2idx[node] = node_size
    idx2node.append(node)
    node_size += 1
  return idx2node, node2idx


class Regularization(torch.nn.Module):
  def __init__(self, model, gamma=0.01, p=2, device="cpu"):
    '''
    :param model:构建好的模型
    :param gamma:系数
    :param p:当p=0表示L2正则化,p=1表示L1正则化
    '''
    super().__init__()
    if gamma <= 0:
      print("param weight_decay can not be <= 0")
      exit(0)
    self.model = model
    self.gamma = gamma
    self.p = p
    self.device = device
    self.weight_list = self.get_weight_list(model) # 取出参数的列表
    self.weight_info = self.get_weight_info(self.weight_list) # 打印参数的信息

  def to(self, device):
    super().to(device)
    self.device = device
    return self

  def forward(self, model):
    self.weight_list = self.get_weight_list(model)
    reg_loss = self.regulation_loss(self.weight_list, self.gamma, self.p)
    return reg_loss

  def regulation_loss(self, weight_list, gamma, p=2):
    reg_loss = 0
    for name, w in weight_list:
      l2_reg = torch.norm(w, p=p)
      reg_loss += l2_reg
    reg_loss = reg_loss * gamma
    return reg_loss

  def get_weight_list(self, model):
    weight_list = []
    # 返回参数的名字和参数自己
    for name, param in model.named_parameters():
      # 这里只取weight 未取bias
      if 'weight' in name:
        weight = (name, param)
        weight_list.append(weight)
    return weight_list

  def get_weight_info(self, weight_list):
    # 打印被正则化的参数的名称
    print("#"*10, "regulations weight", "#"*10)
    for name, param in weight_list:
      print(name)
    print("#"*25)


class SDNE(torch.nn.Module):
  def __init__(self, graph, hidden_layers=None, alpha=1e-5, beta=5, gamma=1e-5, device="cpu"):
    super().__init__()
    self.graph = graph
    self.idx2node, self.node2idx = process_nxgraph(graph)
    self.node_size = graph.number_of_nodes()
    self.edge_size = graph.number_of_edges()
    self.sdne = SDNEModel(self.node_size, hidden_layers, alpha, beta)
    self.device = device
    self.embeddings = {
    
    }
    self.gamma = gamma

    adjacency_matrix, laplace_matrix = self.__create_adjacency_laplace_matrix()
    self.adjacency_matrix = torch.from_numpy(adjacency_matrix.toarray()).float().to(self.device)
    self.laplace_matrix = torch.from_numpy(laplace_matrix.toarray()).float().to(self.device)
  
  def fit(self, batch_size=512, epochs=1, initial_epoch=0, verbose=1):
    num_samples = self.node_size
    self.sdne.to(self.device)
    optimizer = torch.optim.Adam(self.sdne.parameters())
    if self.gamma:
      regularization = Regularization(self.sdne, gamma=self.gamma)
    if batch_size >= self.node_size:
      batch_size = self.node_size
      print('batch_size({0}) > node_size({1}),set batch_size = {1}'.format(batch_size, self.node_size))
      for epoch in range(initial_epoch, epochs):
        loss_epoch = 0
        optimizer.zero_grad()
        loss = self.sdne(self.adjacency_matrix, self.laplace_matrix)
        if self.gamma:
          reg_loss = regularization(self.sdne)
          # print("reg_loss:", reg_loss.item(), reg_loss.requires_grad)
          loss = loss + reg_loss
        loss_epoch += loss.item()
        loss.backward()
        optimizer.step()
        if verbose > 0:
          print('Epoch {0}, loss {1} . >>> Epoch {2}/{3}'.format(epoch + 1, round(loss_epoch / num_samples, 4), epoch+1, epochs))
    else:
      steps_per_epoch = (self.node_size - 1) // batch_size + 1
      for epoch in range(initial_epoch, epochs):
        loss_epoch = 0
        for i in range(steps_per_epoch):
          idx = np.arange(i * batch_size, min((i+1) * batch_size, self.node_size))
          A_train = self.adjacency_matrix[idx, :]
          L_train = self.laplace_matrix[idx][:,idx]
          # print(A_train.shape, L_train.shape)
          optimizer.zero_grad()
          loss = self.sdne(A_train, L_train)
          loss_epoch += loss.item()
          loss.backward()
          optimizer.step()

        if verbose > 0:
          print('Epoch {0}, loss {1} . >>> Epoch {2}/{3}'.format(epoch + 1, round(loss_epoch / num_samples, 4), epoch + 1, epochs))

  def get_embeddings(self):
    if not self.embeddings:
      self.__get_embeddings()
    embeddings = self.embeddings
    return embeddings

  def __get_embeddings(self):
    embeddings = {
    
    }
    with torch.no_grad():
      self.sdne.eval()
      embed = self.sdne.encoder(self.adjacency_matrix)
      for i, embedding in enumerate(embed.numpy()):
        embeddings[self.idx2node[i]] = embedding
    self.embeddings = embeddings


  def __create_adjacency_laplace_matrix(self):
    node_size = self.node_size
    node2idx = self.node2idx
    adjacency_matrix_data = []
    adjacency_matrix_row_index = []
    adjacency_matrix_col_index = []
    for edge in self.graph.edges():
      v1, v2 = edge
      edge_weight = self.graph[v1][v2].get("weight", 1.0)
      adjacency_matrix_data.append(edge_weight)
      adjacency_matrix_row_index.append(node2idx[v1])
      adjacency_matrix_col_index.append(node2idx[v2])
    adjacency_matrix = sparse.csr_matrix((adjacency_matrix_data,(adjacency_matrix_row_index, adjacency_matrix_col_index)),shape=(node_size, node_size))
    # L = D - A  有向图的度等于出度和入度之和; 无向图的领接矩阵是对称的,没有出入度之分直接为每行之和
    # 计算度数
    adjacency_matrix_ = sparse.csr_matrix((adjacency_matrix_data+adjacency_matrix_data,(adjacency_matrix_row_index+adjacency_matrix_col_index,adjacency_matrix_col_index+adjacency_matrix_row_index)), shape=(node_size, node_size))
    degree_matrix = sparse.diags(adjacency_matrix_.sum(axis=1).flatten().tolist()[0])
    laplace_matrix = degree_matrix - adjacency_matrix_
    return adjacency_matrix, laplace_matrix



# 构建图
G = nx.Graph()
G.add_edges_from([(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7), (4, 8), (4, 9)])

model = SDNE(G, hidden_layers=[64, 16])
model.fit(batch_size=2, epochs=10)
embeddings = model.get_embeddings()

# 打印节点的嵌入向量
for node in G.nodes():
  print(f"Node {
      
      node}: {
      
      embeddings[node]}")

4. Nodo2vec

El algoritmo Node2Vec ha realizado una pequeña mejora en el algoritmo DeepWalk, principalmente cambiando el proceso de secuencia de generación de caminata aleatoria, de modo que la homofilia y la equivalencia estructural de los nodos del gráfico se estudiarán en el proceso de incrustación de aprendizaje . secuencia, el algoritmo skipGram también se usa para aprender la representación incrustada del nodo.

El significado de homogeneidad y estructura se puede explicar a partir de la siguiente figura. Homogeneidad significa que dos nodos conectados deben tener representaciones incrustadas similares , como el nodo u y el nodo S 1 S_1 en la figura.S1Si están conectados directamente, su incrustación debe ser más cercana. Estructural significa que dos nodos estructuralmente similares deben tener representaciones incrustadas similares , como se muestra en la figura, nodo u y nodo S 6 S_6S6Si están ubicados en el centro de los dos clústeres, las incrustaciones de los dos nodos deberían ser similares.
BFS frente a DFS
Por lo general, DFS se usa para expresar la homogeneidad de la red y BFS se usa para expresar la estructura de la red.
DFS frente a BFS
nodo esclavo vvv Saltar al siguiente nodoxxLa probabilidad de x
es: π vx = α pq ( t , x ) ⋅ wvx \pi_{vx}=\alpha_{pq}(t,x)\cdot w_{vx}Pivx=apq( t ,x )wvx
Entre ellos wvx w_{vx}wvxEs el peso de la arista VX, α pq ( t , x ) \alpha_{pq}(t,x)apq( t ,x )的定义如下:
α pq ( t , x ) = { 1 p si dtx = 0 1 si dtx = 1 1 q si dtx = 2 \alpha _{pq}(t,x)=\begin{cases}\ frac{1}{p} & \text{ si } d_{tx}=0 \\1 & \text{ si } d_{tx}=1 \\\frac{1}{q} & \text{ si } d_{tx}=2\end{casos}apq( t ,x )= pag11q1 si  dt x=0 si  dt x=1 si  dt x=2
donde dtx d_{tx}dt xIndica el nodo ttt al nodoxxx -distancia:

  • Si dos nodos son el mismo nodo, dtx = 0 d_{tx}=0dt x=0
  • Si dos nodos están conectados directamente, dtx = 1 d_{tx}=1dt x=1
  • Si dos nodos no están conectados, dtx = 2 d_{tx}=2dt x=2

Los parámetros p y q juntos controlan la propensión de la caminata aleatoria:

  • El parámetro p se denomina parámetro de retorno (returnparameter).Cuanto menor es p, mayor es la probabilidad de que el nodo devuelva t, más inclinado es el recorrido del gráfico a BFS, y más tiende a representar la estructura del gráfico. .
  • El parámetro q se denomina parámetro in-out (parámetro in-out). Cuanto menor sea q, mayor será la probabilidad de atravesar nodos distantes. Cuanto más inclinado esté el recorrido del gráfico a DFS, más tiende a representar el homogeneidad de la gráfica.
  • La tendencia de los resultados expresados ​​por la incrustación de gráficos se puede sopesar estableciendo los valores de p y q. Cuando p=1, q=1, el método de caminata es equivalente a la caminata aleatoria en DeepWalk.

Use las reglas anteriores para generar una secuencia y luego ingrese la secuencia en el modelo para calcular la incorporación de cada nodo. La homogeneidad y estructura de la red que encarna Node2Vec también se puede explicar de forma intuitiva en el sistema de recomendación. Es probable que los artículos con la misma homogeneidad sean artículos de la misma categoría, el mismo atributo o artículos que a menudo se compran juntos, mientras que los artículos con la misma estructura son los artículos más populares de cada categoría, los mejores artículos de pedido único de cada categoría, etc. con tendencias similares o Artículos con propiedades estructurales.
homogeneidad y heterogeneidad
Ambas son expresiones de características muy importantes en el sistema de recomendación. Debido a la flexibilidad de Node2Vec y la capacidad de explorar diferentes características, podemos empalmar incrustaciones con diferentes tendencias para expresar resultados y obtener características de incrustación más ricas, también podemos fusionar incrustaciones generadas por diferentes Node2Vec en la red de aprendizaje profundo subsiguiente. información de artículos.

En resumen, node2vec es un método de incrustación de gráficos que considera integralmente la vecindad DFS y la vecindad BFS. En términos simples, puede verse como una extensión de deepwalk, que es una caminata profunda que combina caminatas aleatorias DFS y BFS.

Código central de Node2vec:
nodo2vec
node2vec tiene una biblioteca de tres partes, que se puede instalar a través de pip:

pip install node2vec

Uso sencillo:

import networkx as nx
from node2vec import Node2Vec

# 构建图
G = nx.Graph()
G.add_edges_from([(1, 2), (1, 3), (2, 4), (2, 5), (3, 6), (3, 7), (4, 8), (4, 9)])

# Precompute probabilities and generate walks - **ON WINDOWS ONLY WORKS WITH workers=1**
node2vec = Node2Vec(G, dimensions=16, walk_length=10, num_walks=100, workers=4) # Use temp_folder for big graphs

# Embed nodes
model = node2vec.fit(window=10, min_count=0, batch_words=4)
from node2vec.edges import AverageEmbedder
from node2vec.edges import HadamardEmbedder
from node2vec.edges import WeightedL1Embedder
from node2vec.edges import WeightedL2Embedder

edges_embs = HadamardEmbedder(keyed_vectors=model.wv)
# Look for embeddings on the fly - here we pass normal tuples
print(edges_embs[('1', '2')])

Obtenga la incrustación entre el nodo 1 y el nodo 2:
incrustación
obtenga las incrustaciones de cada nodo:

embeddings = model.wv.vectors

for ix, node in enumerate(G.nodes()):
  print(f"Node {
      
      node}: {
      
      embeddings[ix]}")

incrustaciones

5. Estructura2vec

Se puede considerar que Struc2Vec, lanzado en 2017, extrae ciertas características del gráfico desde otro ángulo: DeepWalk aprende la similitud de los vecinos, LINE aprende la similitud de los vecinos de primer y segundo orden, y Node2Vec aprende la similitud y la estructura de los vecinos. Similitud, pero Node2Vec en realidad no puede aprender suficiente similitud estructural, porque la cantidad de pasos de la caminata aleatoria es limitada después de todo, si la distancia entre dos nodos es muy grande, es difícil aprender la llamada similitud estructural, y struc2vec es directamente dirigido al aprendizaje de la similitud estructural .
Estructura
Su idea central es:

  1. Las propiedades de los nodos y los bordes y sus posiciones en la red se ignoran para evaluar la similitud estructural entre los nodos, y solo se considera la estructura local de los nodos . El criterio intuitivo para juzgar la similitud estructural de dos nodos es: si los grados de dos nodos son iguales, su estructura es similar; si los grados de los vecinos de dos nodos también son iguales, su similitud estructural es mayor .
    grado de nodo
    Definir R k ( u ) R_k(u)Rk( u ) representa el conjunto de vértices en khop,S ( s ) S(s)S ( s ) representa una secuencia ordenada de grados S establecidos,g ( D 1 , D 2 ) g(D_1,D_2)g ( re1,D2) representa la distancia entre D1 y D2. f ( tu , v ) f(u,v)f ( tu ,v ) representa la similitud de u y v en vecinos khop.

  2. Se establece una estructura jerárquica para medir la similitud estructural de los nodos : en la capa inferior, la similitud estructural depende solo del grado del nodo; en la capa superior, la similitud estructural depende de la información de toda la red.

El proceso específico es el siguiente:
Primero, construya un gráfico multicapa ponderado, para el gráfico G = ( V , E ) G=(V,E)GRAMO=( V ,E ) , construya un grafo multicapa ponderado M de acuerdo con la siguiente descripción:
La k-ésima capa de M es un grafo completo no dirigido ponderado compuesto por un conjunto de nodos V, donde el peso de borde entre cada par de nodos u y v se define como wk (tu , v ) = mi − fk ( tu , v ) w_k(u,v)=e^{-f_k(u,v)}wk( tu ,v )=mi- fk( u , v ) (inversamente proporcional a la distancia estructural entre ellos).
La relación correspondiente entre los pesos entre capas es:
Pesos
P significa que en el siguiente muestreo, hay una probabilidad de p de caminar en esta capa y una probabilidad de 1-p de cambiar entre aguas arriba y aguas abajo. Por lo tanto, la fórmula de cálculo de p se puede dividir en dos situaciones:
(1) cuando se está en roaming en esta capa
PAG
(2) cuando se cambia entre aguas arriba y aguas abajo
Conmutación aguas arriba y aguas abajo
En general:
en general

  1. Luego, el nodo recorre las reglas anteriores para obtener la secuencia de recorrido y utiliza Skip-gram para generar incrustaciones.

En términos simples, los pasos de Struct2Vec se dividen en tres pasos: 1. Obtener la distancia del par de vértices de cada capa 2. Construir un gráfico jerárquico ponderado basado en la distancia del par de vértices 3. Recorrer aleatoriamente la secuencia de vértices muestreada en el gráfico jerárquico ponderado .
Struc2vec captura la información estructural del grafo, y tiene un mejor efecto cuando la importancia de su estructura es mayor que la de sus vecinos.

Métodos de incrustación de gráficos

Con respecto a la forma en que se incrusta todo el gráfico, aquí se presenta un graph2vec representativo .

1. Gráfico2vec

La incrustación de gráficos es un método para representar el gráfico completo con un vector. Graph2vec también se basa en la idea de skip-gram y codifica el gráfico completo en un espacio vectorial. Similar a la incrustación de documentos doc2vec, doc2vec obtiene la ID del documento en la entrada y lo predice maximizando las probabilidades de palabras aleatorias del documento para el entrenamiento.
Gráfico2Vec
Graph2vec también consta de tres pasos :

  1. Muestra y vuelve a etiquetar todos los subgráficos en el gráfico . Un subgrafo es un grupo de nodos que aparecen alrededor de un nodo seleccionado.
  2. Entrena el modelo skip-gram . Similar al documento doc2vec, dado que el documento es un conjunto de palabras y el gráfico es un conjunto de subgráficos, en esta etapa se entrena el modelo skip-gram. Está entrenado para maximizar la probabilidad de predecir subgráficos presentes en el gráfico en la entrada.
  3. calcular _ Las incrustaciones se calculan proporcionando en la entrada un vector de índices de identificación de subgráficos.

Word2Vec VS Graph2Vec

otros metodos

Los mencionados anteriormente son algunos métodos de uso común, pero hay muchos otros métodos y modelos que vale la pena aprender.

  • Incrustación de nodos: LLE, mapas propios laplacianos, factorización de gráficos, GraRep, HOPE, DNGR, GCN, LINE
  • Enfoques de incrustación de gráficos: Patchy-san, sub2vec (incrustar subgráficos), kernel WL y kernels Deep WL

Resumir

Este artículo combina ChatGPT y códigos relacionados para presentar qué es la incrustación de gráficos, por qué se usa la incrustación de gráficos y las propiedades que deben cumplirse al incrustar. Además, presenta brevemente varios métodos clásicos de nivel de nodo: DeepWalk, LINE, SDNE, etc. y graph2vec de nivel de gráfico.

Nota: ¡Aquí se presentan todos los métodos de incrustación basados ​​en gráficos homogéneos! ! !

Informacion relevante

  1. Incrustaciones de gráficos: el resumen
  2. DeepWalk: principio de algoritmo, implementación y aplicación
  3. Principio de DeepWalk y práctica de código
  4. LINE: principio del algoritmo, implementación y aplicación
  5. Interpretación del principio LINE del método de incrustación de gráficos
  6. Alias ​​Method: Método de muestreo discreto con complejidad temporal O(1)
  7. Teoría SDNE (Structural Deep Network Embedding) e implementación de pytorch
  8. Incrustación de gráficos (2)
  9. Incrustación de gráficos (三)
  10. Principios de incrustación de gráficos y aplicaciones del aprendizaje de representación de gráficos
  11. escaparate de Node2Vec
  12. Clasificación de nodos con Node2Vec ponderado
  13. Nodo2Vec
  14. Struc2Vec: principio de algoritmo, implementación y aplicación
  15. Struc2vec: aprendizaje de representaciones de nodos a partir de la identidad estructural

Supongo que te gusta

Origin blog.csdn.net/ARPOSPF/article/details/122556237
Recomendado
Clasificación