web-dev-qa-db-fra.com

Regroupement spectral d'un graphique en python

Je voudrais regrouper un graphique en python en utilisant le regroupement spectral.

Le regroupement spectral est une technique plus générale qui peut être appliquée non seulement aux graphiques, mais aussi aux images ou à tout type de données, cependant, il est considéré comme un graphique exceptionnel technique de regroupement. Malheureusement, je ne trouve pas d'exemples de graphiques de regroupement spectral dans python en ligne.

J'adorerais une direction sur la façon de procéder. Si quelqu'un peut m'aider à le comprendre, je peux ajouter la documentation à scikit learn.

Remarques:

16
Alex Lenail

Sans beaucoup d'expérience avec le clustering spectral et juste en passant par la documentation (passez à la fin pour les résultats!):

Code:

import numpy as np
import networkx as nx
from sklearn.cluster import SpectralClustering
from sklearn import metrics
np.random.seed(1)

# Get your mentioned graph
G = nx.karate_club_graph()

# Get ground-truth: club-labels -> transform to 0/1 np-array
#     (possible overcomplicated networkx usage here)
gt_dict = nx.get_node_attributes(G, 'club')
gt = [gt_dict[i] for i in G.nodes()]
gt = np.array([0 if i == 'Mr. Hi' else 1 for i in gt])

# Get adjacency-matrix as numpy-array
adj_mat = nx.to_numpy_matrix(G)

print('ground truth')
print(gt)

# Cluster
sc = SpectralClustering(2, affinity='precomputed', n_init=100)
sc.fit(adj_mat)

# Compare ground-truth and clustering-results
print('spectral clustering')
print(sc.labels_)
print('just for better-visualization: invert clusters (permutation)')
print(np.abs(sc.labels_ - 1))

# Calculate some clustering metrics
print(metrics.adjusted_Rand_score(gt, sc.labels_))
print(metrics.adjusted_mutual_info_score(gt, sc.labels_))

Sortie:

ground truth
[0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1]
spectral clustering
[1 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
just for better-visualization: invert clusters (permutation)
[0 0 1 0 0 0 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
0.204094758281
0.271689477828

L'idée générale:

Introduction sur les données et la tâche de ici :

Les nœuds du graphique représentent les 34 membres d'un club de karaté universitaire. (Zachary est un sociologue, et il était l'un des membres.) Un Edge entre deux nœuds indique que les deux membres ont passé beaucoup de temps ensemble en dehors des réunions normales du club. L'ensemble de données est intéressant car pendant que Zachary collectait ses données, il y avait un différend au club de karaté, et il s'est divisé en deux factions: l'une dirigée par "M. Salut "et un dirigé par" John A ". Il s'avère qu'en utilisant uniquement les informations de connectivité (les bords), il est possible de récupérer les deux factions.

Utiliser Sklearn et spectral-clustering pour résoudre ce problème:

Si l'affinité est la matrice d'adjacence d'un graphique, cette méthode peut être utilisée pour trouver des coupes de graphique normalisées.

This décrit les coupes de graphique normalisées comme suit:

Trouvez deux partitions disjointes A et B des sommets V d'un graphe, de sorte que A ∪ B = V et A ∩ B = ∅

Étant donné une mesure de similitude w (i, j) entre deux sommets (par exemple, l'identité lorsqu'ils sont connectés), une valeur de coupure (et sa version normalisée) est définie comme suit: coupe (A, B) = SUM u dans A, v dans B: w (u, v)

...

nous recherchons la minimisation de la dissociation entre les groupes A et B et la maximisation de l'association au sein de chaque groupe

Ça sonne bien. Nous créons donc la matrice d'adjacence (nx.to_numpy_matrix(G)) et définissons le paramètre affinity sur précalculé (car notre matrice d'adjonction est notre mesure de similarité précalculée).

Alternativement, en utilisant précalculé, une matrice d'affinité fournie par l'utilisateur peut être utilisée.

Edit: Bien que peu familier avec cela, j'ai cherché les paramètres à régler et trouvé assign_labels :

La stratégie à utiliser pour attribuer des étiquettes dans l'espace d'intégration. Il existe deux façons d'attribuer des étiquettes après l'incorporation laplacienne. k-means peut être appliqué et est un choix populaire. Mais il peut également être sensible à l'initialisation. La discrétisation est une autre approche moins sensible à l'initialisation aléatoire.

Essayer donc l'approche la moins sensible:

sc = SpectralClustering(2, affinity='precomputed', n_init=100, assign_labels='discretize')

Sortie:

ground truth
[0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1]
spectral clustering
[0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1]
just for better-visualization: invert clusters (permutation)
[1 1 0 1 1 1 1 1 0 0 1 1 1 1 0 0 1 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0]
0.771725032425
0.722546051351

C'est un ajustement presque parfait à la vérité fondamentale!

20
sascha

Voici un exemple factice juste pour voir ce qu'il fait à une matrice de similitude simple - inspirée par la réponse de sascha.

Code

import numpy as np
from sklearn.cluster import SpectralClustering
from sklearn import metrics
np.random.seed(0)

adj_mat = [[3,2,2,0,0,0,0,0,0],
           [2,3,2,0,0,0,0,0,0],
           [2,2,3,1,0,0,0,0,0],
           [0,0,1,3,3,3,0,0,0],
           [0,0,0,3,3,3,0,0,0],
           [0,0,0,3,3,3,1,0,0],
           [0,0,0,0,0,1,3,1,1],
           [0,0,0,0,0,0,1,3,1],
           [0,0,0,0,0,0,1,1,3]]

adj_mat = np.array(adj_mat)

sc = SpectralClustering(3, affinity='precomputed', n_init=100)
sc.fit(adj_mat)

print('spectral clustering')
print(sc.labels_)

Sortie

spectral clustering
[0 0 0 1 1 1 2 2 2]
5
sinapan