Facenet: Type Error in clustering.py _ chinese whispers clustering algorithm

Created on 20 Mar 2018  路  3Comments  路  Source: davidsandberg/facenet

Error :
Traceback (most recent call last):
File "clustering.py", line 271, in
main(parse_args())
File "clustering.py", line 245, in main
sorted_clusters = cluster_facial_encodings(facial_encodings)
File "clustering.py", line 150, in cluster_facial_encodings
sorted_clusters = _chinese_whispers(facial_encodings.items())
File "clustering.py", line 89, in _chinese_whispers
shuffle(cluster_nodes)
File "/home/stan/anaconda2/envs/tensorflow/lib/python3.5/random.py", line 281, in shuffle
x[i], x[j] = x[j], x[i]
TypeError: 'NodeView' object does not support item assignment

function associated with above error:

def _chinese_whispers(encoding_list, threshold=0.55, iterations=20):
#from face_recognition.api import _face_distance
from random import shuffle
import networkx as nx
# Create graph
nodes = []
edges = []

image_paths, encodings = zip(*encoding_list)

if len(encodings) <= 1:
    print ("No enough encodings to cluster!")
    return []

for idx, face_encoding_to_check in enumerate(encodings):
    # Adding node of facial encoding
    node_id = idx+1

    # Initialize 'cluster' to unique value (cluster of itself)
    node = (node_id, {'cluster': image_paths[idx], 'path': image_paths[idx]})
    nodes.append(node)

    # Facial encodings to compare
    if (idx+1) >= len(encodings):
        # Node is last element, don't create edge
        break

    compare_encodings = encodings[idx+1:]
    distances = face_distance(compare_encodings, face_encoding_to_check)
    encoding_edges = []
    for i, distance in enumerate(distances):
        if distance > threshold:
            # Add edge if facial match
            edge_id = idx+i+2
            encoding_edges.append((node_id, edge_id, {'weight': distance}))

    edges = edges + encoding_edges

G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)

# Iterate
for _ in range(0, iterations):
    cluster_nodes = G.nodes()
    shuffle(cluster_nodes)
    for node in cluster_nodes:
        neighbors = G[node]
        clusters = {}

        for ne in neighbors:
            if isinstance(ne, int):
                if G.node[ne]['cluster'] in clusters:
                    clusters[G.node[ne]['cluster']] += G[node][ne]['weight']
                else:
                    clusters[G.node[ne]['cluster']] = G[node][ne]['weight']

        # find the class with the highest edge weight sum
        edge_weight_sum = 0
        max_cluster = 0
        #use the max sum of neighbor weights class as current node's class
        for cluster in clusters:
            if clusters[cluster] > edge_weight_sum:
                edge_weight_sum = clusters[cluster]
                max_cluster = cluster

        # set the class of target node to the winning local class
        G.node[node]['cluster'] = max_cluster

clusters = {}

# Prepare cluster output
for (_, data) in G.node.items():
    cluster = data['cluster']
    path = data['path']

    if cluster:
        if cluster not in clusters:
            clusters[cluster] = []
        clusters[cluster].append(path)

# Sort cluster output
sorted_clusters = sorted(clusters.values(), key=len, reverse=True)

return sorted_clusters

Most helpful comment

You could resolve that problem doing this:

cluster_nodes = list(G.nodes)

Simple & easy

All 3 comments

by commenting the shuffle module , clustering worked perfectly!
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)

Iterate

for _ in range(0, iterations):
cluster_nodes = G.nodes()
#shuffle(cluster_nodes)
for node in cluster_nodes:
neighbors = G[node]
clusters = {}

This doesn't really fix the problem. The algorithm requires iteration through the nodes in a random order on each pass.

The problem is caused in networkx package breaking backwards compatibility between v1 and v2. Shuffle is not able to work with v2 api, so this needs to be implemented in another way.

To bypass the problem you can downgrade networkx package to 1.11.

You could resolve that problem doing this:

cluster_nodes = list(G.nodes)

Simple & easy

Was this page helpful?
0 / 5 - 0 ratings