Loading src/absinth.py +53 −52 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ Example: $ python3 absinth.py The function can be called with a list of modifiers. absinth.py can be called with a list of modifiers. Modifiers: '-t': Runs absinth.py on the trial path given in the config.py instead of the Loading Loading @@ -79,7 +79,7 @@ def read_dataset(data_path: str) -> (dict, dict): if id1 not in results: results[id1]=list() results[id1].append(" ".join(l[2:]).strip()) # here I join title and snippet, the URL is ignored results[id1].append(" ".join(l[2:]).strip()) # here title and snippet are joined, the URL is ignored # topics.txt is a list of target words Loading @@ -106,6 +106,8 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): Counts frequencies from corpus and search result list, builds graph from these counts (with some filters). Root hubs (senses) are collected from this graph. The frequencies are saved in json-files (from where they are loaded, if found) to improve runtime in the later use of absinth.py Args: topic_name: Target string. Loading Loading @@ -138,13 +140,13 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): edge_dict_name = topic_name+'_edge.json' graph_in_existence = False for graph_name in os.listdir(config.graph): for graph_name in os.listdir(config.graph_path): if topic_name in graph_name: graph_in_existence = True with open(config.graph+node_dict_name, 'r') as node_file, open(config.graph+edge_dict_name, 'r') as edge_file: with open(config.graph_path+node_dict_name, 'r') as node_file, open(config.graph_path+edge_dict_name, 'r') as edge_file: node_freq_dict = json.load(node_file) edge_freq_dict = json.load(edge_file) Loading @@ -159,7 +161,7 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): node_freq_dict, edge_freq_dict = frequencies(target_string, result_list) with open(config.graph+node_dict_name, 'w') as node_file, open(config.graph+edge_dict_name, 'w') as edge_file: with open(config.graph_path+node_dict_name, 'w') as node_file, open(config.graph_path+edge_dict_name, 'w') as edge_file: json.dump(node_freq_dict, node_file) Loading Loading @@ -187,7 +189,7 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): print('[a]', 'Collecting root hubs.\t('+topic_name+')') root_hub_list = root_hubs(graph, edge_freq_dict) #adds sense inventory to buffer with some common neighbors for context #adds sense inventory to buffer with some common neighbours for context stat_dict['hubs'] = dict() for root_hub in root_hub_list: Loading @@ -196,10 +198,10 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): if root_hub < node \ else edge_freq_dict[node, root_hub] most_frequent_neighbor_list = sorted(graph.adj[root_hub], most_frequent_neighbour_list = sorted(graph.adj[root_hub], key=by_frequency, reverse=True) stat_dict['hubs'][root_hub] = most_frequent_neighbor_list[:6] stat_dict['hubs'][root_hub] = most_frequent_neighbour_list[:6] return graph, root_hub_list, stat_dict Loading @@ -209,7 +211,7 @@ def frequencies(target_string: str, search_result_list: list) -> (dict, dict): Iterates over the corpus (and snippets provided with the task) line by line and counts every token and tuple of tokens within a line (context). These tokens is filtered by stop words, pos tags and context length. tokens are filtered by stop words, pos tags and context length. Args: target_string: contexts are selected if they contain this string. For Loading @@ -218,8 +220,9 @@ def frequencies(target_string: str, search_result_list: list) -> (dict, dict): Returns: Dictionary of occurrences of every eligible token within every context the target occurs in, dictionary of occurrences of every eligible tuple of tokens within every context the target occurs in. the target occurs in, Dictionary of occurrences of every eligible tuple of tokens within every context the target occurs in. """ Loading Loading @@ -400,7 +403,7 @@ def build_graph(node_freq_dict: dict, edge_freq_dict: dict) -> nx.Graph: tokens within every context the target occurs in. Returns: Filtered undirected dice weighted small word cooccurrence graph for a Filtered undirected weighted small word cooccurrence graph for a given target entity. """ Loading Loading @@ -469,13 +472,13 @@ def root_hubs(graph: nx.Graph, edge_freq_dict: dict) -> list: """Identifies senses (root hubs) by choosing nodes with high degrees Selects root hubs according to the algorithm in Véronis (2004). Nodes with high degree and neighbors with low weights (high cooccurrence) are chosen high degree and neighbours with low weights (high cooccurrence) are chosen until there are no more viable candidates. A root hub candidate is every node that is not already a hub and is not a neighbor of one. node that is not already a hub and is not a neighbour of one. Args: graph: Weighted undirected graph. edge_freq_dict: Dictionary of weights for every tuple in our graph. edge_freq_dict: Dictionary of weights for every tuple in the graph. Returns: List of root hubs, i.e. strings that are selected using the algorithm Loading @@ -484,17 +487,17 @@ def root_hubs(graph: nx.Graph, edge_freq_dict: dict) -> list: mean_degree = np.mean(list(dict(graph.degree).values())) if config.min_neighbors == 'log' and mean_degree > 0: min_neighbors = int(np.log(mean_degree) * mean_degree) if config.min_neighbours == 'log' and mean_degree > 0: min_neighbours = int(np.log(mean_degree) * mean_degree) else: min_neighbors = config.min_neighbors min_neighbours = config.min_neighbours threshold = config.threshold # Allow operations on graph without altering original one. graph_copy = deepcopy(graph) # Sort according to degree (number of neighbors). # Sort according to degree (number of neighbours). candidate_list = sorted(graph_copy.nodes, key=lambda node: graph_copy.degree[node], reverse=True) Loading @@ -506,27 +509,27 @@ def root_hubs(graph: nx.Graph, edge_freq_dict: dict) -> list: candidate = candidate_list[0] #best hub candidate if graph_copy.degree[candidate] >= min_neighbors: if graph_copy.degree[candidate] >= min_neighbours: by_frequency = lambda node: edge_freq_dict[candidate,node] \ if candidate < node \ else edge_freq_dict[node,candidate] most_frequent_neighbor_list = sorted(graph_copy.adj[candidate], most_frequent_neighbour_list = sorted(graph_copy.adj[candidate], key=by_frequency, reverse=True) [:min_neighbors] reverse=True) [:min_neighbours] # If the mean weight of the most frequent neighbors cooccur # If the mean weight of the most frequent neighbours cooccur # frequently enough with candidate, the candidate is approved. if np.mean([graph_copy.edges[candidate,node]['weight'] for node in most_frequent_neighbor_list]) < threshold: for node in most_frequent_neighbour_list]) < threshold: # Add candidate as root hub. hub_list.append(candidate) # Remove neighbors of new hub as hub candidates. for neighbor in deepcopy(graph_copy).adj[candidate]: graph_copy.remove_node(neighbor) # Remove neighbours of new hub as hub candidates. for neighbour in deepcopy(graph_copy).adj[candidate]: graph_copy.remove_node(neighbour) # Remove hub candidate. graph_copy.remove_node(candidate) Loading @@ -550,9 +553,9 @@ def root_hubs(graph: nx.Graph, edge_freq_dict: dict) -> list: def label_graph(graph: nx.Graph, root_hub_list: list) -> nx.Graph: """propagations graph accoring to root hubs. """Propagations graph according to root hubs. Evolving network that propagations neighboring nodes iterative. See sentiment Evolving network that propagations neighbouring nodes iterative. See sentiment propagation. Args: Loading @@ -560,7 +563,7 @@ def label_graph(graph: nx.Graph, root_hub_list: list) -> nx.Graph: root_hub_list: List of senses. Returns: labelled graph. Labelled graph. """ Loading Loading @@ -592,20 +595,20 @@ def label_graph(graph: nx.Graph, root_hub_list: list) -> nx.Graph: for node in graph.nodes: neighbor_weight_list = [0] * len(root_hub_list) neighbour_weight_list = [0] * len(root_hub_list) for neighbor in graph_copy[node]: for neighbour in graph_copy[node]: if graph_copy.node[neighbor]['sense'] == None: if graph_copy.node[neighbour]['sense'] == None: pass else: neighbor_weight_list[graph_copy.node[neighbor]['sense']] \ += 1 - graph_copy[node][neighbor]['weight'] neighbour_weight_list[graph_copy.node[neighbour]['sense']] \ += 1 - graph_copy[node][neighbour]['weight'] if any(neighbor_weight_list): if any(neighbour_weight_list): graph.node[node]['dist'].append(neighbor_weight_list) graph.node[node]['dist'].append(neighbour_weight_list) old_propagation = graph_copy.node[node]['sense'] new_propagation = np.argmax(np.mean(graph.node[node]['dist'], axis=0)) Loading @@ -625,8 +628,6 @@ def label_graph(graph: nx.Graph, root_hub_list: list) -> nx.Graph: graph.node[node]['dist'] = np.mean(graph.node[node]['dist'], axis=0) draw_propagation(graph,root_hub_list) return graph Loading Loading @@ -719,7 +720,7 @@ def disambiguate_propagation(graph: nx.Graph, root_hub_list: list, context_list: return mapping_dict def draw_propagation(graph: nx.Graph, root_hub_list: list) -> None: """def draw_propagation(graph: nx.Graph, root_hub_list: list) -> None: pass Loading Loading @@ -758,7 +759,7 @@ def draw_propagation(graph: nx.Graph, root_hub_list: list) -> None: plt.savefig('../figures/'+graph.graph['target']+'.png') plt.clf() """ ############################## # MST Disambiguation # Loading @@ -768,7 +769,7 @@ def draw_propagation(graph: nx.Graph, root_hub_list: list) -> None: def components(graph: nx.Graph, root_hub_list: list, target_string: str) -> nx.Graph: """Builds minimum spanning tree from graph and removes singletons. Applies components algorithm from Véronis (2004) and removes singletons. Applies components algorithm from Véronis (2004) Args: graph: Undirected weighted graph. Loading Loading @@ -938,7 +939,7 @@ def local_clustering_coefficient(graph: nx.Graph, node: str) -> float: """Calculates local clustering coefficient from node. Local Clustering Coefficient is defined as number of edges between neighbors of a node, divided by the number of possible nodes. neighbours of a node, divided by the number of possible nodes. Args: graph: Undirected graph. Loading @@ -948,23 +949,23 @@ def local_clustering_coefficient(graph: nx.Graph, node: str) -> float: Local coefficient. """ neighbor_list = graph.adj[node] neighbour_list = graph.adj[node] neighbor_edge_list = [(x,y) for x in neighbor_list for y in neighbor_list if x<y] neighbour_edge_list = [(x,y) for x in neighbour_list for y in neighbour_list if x<y] if len(neighbor_edge_list) == 0: if len(neighbour_edge_list) == 0: return 0 else: edge_count = 0 for x,y in neighbor_edge_list: for x,y in neighbour_edge_list: if graph.has_edge(x,y): edge_count += 1 return edge_count/len(neighbor_edge_list) return edge_count/len(neighbour_edge_list) Loading Loading @@ -1112,7 +1113,7 @@ def main(topic_id: int, topic_name: str, result_dict: dict) -> None: stat_dict['L_rand'] = None stat_dict['C_rand'] = 0 propagation_rank = config.colour_rank propagation_rank = config.propagation_rank mst_rank = config.mst_rank #Merges Mappings according to pipeline Loading Loading @@ -1209,9 +1210,9 @@ if __name__ == '__main__': # If absinth.py is run in test environment. if '-t' in sys.argv: data_path = config.test data_path = config.testset else: data_path = config.dataset data_path = config.trialset result_dict, topic_dict = read_dataset(data_path) Loading src/abstinent.py +34 −26 Original line number Diff line number Diff line Loading @@ -102,8 +102,9 @@ def frequencies(target_string: str, search_result_list: list) -> (dict, dict): Returns: Dictionary of occurrences of every eligible token within every context the target occurs in, dictionary of occurrences of every eligible tuple of tokens within every context the target occurs in. the target occurs in, Dictionary of occurrences of every eligible tuple of tokens within every context the target occurs in. """ Loading Loading @@ -340,8 +341,20 @@ def build_graph(node_freq_dict: dict, edge_freq_dict: dict) -> nx.Graph: def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): """ """ Induces word senses for a given topic from corpus. Use n random nodes as root hubs. The frequencies are saved in json-files (from where they are loaded, if found) to improve runtime in the later use of abstinent.py Args: topic_name: Target string. result_list: List of search result (context) strings. Returns: A cooccurrence graph, a list of root hub strings (senses) and dictionary of various statistics. """ stat_dict = dict() Loading @@ -356,13 +369,13 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): edge_dict_name = topic_name+'_edge.json' graph_in_existence = False for graph_name in os.listdir(config.graph): for graph_name in os.listdir(config.graph_path): if topic_name in graph_name: graph_in_existence = True with open(config.graph+node_dict_name, 'r') as node_file, open(config.graph+edge_dict_name, 'r') as edge_file: with open(config.graph_path+node_dict_name, 'r') as node_file, open(config.graph_path+edge_dict_name, 'r') as edge_file: node_freq_dict = json.load(node_file) edge_freq_dict = json.load(edge_file) Loading @@ -377,7 +390,7 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): node_freq_dict, edge_freq_dict = frequencies(topic_name, result_list) with open(config.graph+node_dict_name, 'w') as node_file, open(config.graph+edge_dict_name, 'w') as edge_file: with open(config.graph_path+node_dict_name, 'w') as node_file, open(config.graph_path+edge_dict_name, 'w') as edge_file: json.dump(node_freq_dict, node_file) Loading Loading @@ -411,7 +424,7 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): reverse=True)[:sense_count] root_hub_list = [hub[1] for hub in root_hub_list] #adds sense inventory to buffer with some common neighbors for context #adds sense inventory to buffer with some common neighbours for context stat_dict['hubs'] = dict() for root_hub in root_hub_list: Loading @@ -420,17 +433,16 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): if root_hub < node \ else edge_freq_dict[node, root_hub] most_frequent_neighbor_list = sorted(graph.adj[root_hub], most_frequent_neighbour_list = sorted(graph.adj[root_hub], key=by_frequency, reverse=True) stat_dict['hubs'][root_hub] = most_frequent_neighbor_list[:6] stat_dict['hubs'][root_hub] = most_frequent_neighbour_list[:6] return graph, root_hub_list, stat_dict def bag_of_senses(graph: nx.Graph, root_hub_list:list) -> dict: """ Matches each node to the root hub it is closest to. """Matches each node to the root hub it is closest to. """ root_hub_count = len(root_hub_list) Loading @@ -456,7 +468,7 @@ def bag_of_senses(graph: nx.Graph, root_hub_list:list) -> dict: def disambiguate(bag_of_senses: dict, context_list: list) -> dict: """ Lesk. A simple Lesk disambiguation Algorithm. """ context_idx = 0 Loading Loading @@ -534,10 +546,6 @@ def print_stats(stat_dict: dict) -> None: stat_file.write('\t'.join([str(stat_dict[key]) for key in key_list])+'\n') def global_clustering_coefficient(graph: nx.Graph) -> float: """Calculates global clustering coefficient from graph. Loading @@ -555,23 +563,23 @@ def global_clustering_coefficient(graph: nx.Graph) -> float: for node in graph.nodes: neighbor_list = graph.adj[node] neighbour_list = graph.adj[node] neighbor_edge_list = [(x,y) for x in neighbor_list for y in neighbor_list if x<y] neighbour_edge_list = [(x,y) for x in neighbour_list for y in neighbour_list if x<y] if len(neighbor_edge_list) == 0: if len(neighbour_edge_list) == 0: local_coefficient_list.append(0) else: edge_count = 0 for x,y in neighbor_edge_list: for x,y in neighbour_edge_list: if graph.has_edge(x,y): edge_count += 1 local_coefficient_list.append(edge_count/len(neighbor_edge_list)) local_coefficient_list.append(edge_count/len(neighbour_edge_list)) return np.mean(local_coefficient_list) Loading Loading @@ -695,16 +703,16 @@ def main(topic_id: int, topic_name: str, result_dict: dict) -> None: if __name__ == '__main__': """Check for modifiers and call main(). Only called when absinth.py is started manually. Checks for various Only called when abstinent.py is started manually. Checks for various modifiers, i.e. test environment and number of processes to run simultaneously. """ # If absinth.py is run in test environment. # If abstinent.py is run in test environment. if '-t' in sys.argv: data_path = config.test data_path = config.testset else: data_path = config.dataset data_path = config.trialset result_dict, topic_dict = read_dataset(data_path) Loading src/all-in-one.py +9 −2 Original line number Diff line number Diff line #!/usr/bin/env python3 # -*- coding: utf-8 -*- """All-In-One Baseline for ABSINTH This tool works as a All-In-One Clustering Baseline .. _Association Based Semantic Induction Tools from Heidelberg https://gitlab.cl.uni-heidelberg.de/zimmermann/absinth """ import config import time import sys Loading @@ -10,9 +17,9 @@ final_path = 'final/{}.absinth'.format(hex(time_int)[2:]) results = open(final_path, 'w') if '-t' in sys.argv: data_path = config.test data_path = config.testset else: data_path = config.dataset data_path = config.trialset with open(data_path+'results.txt', 'r') as f: Loading src/config.py +9 −8 Original line number Diff line number Diff line Loading @@ -6,10 +6,10 @@ Configuration file Choose paths for corpus, dataset and output. - The output directory should be empty when starting absinth. ''' graph = "./.graphs/" graph_path = "./.graphs/" corpus = "/proj/absinth/wikipedia_shuffled2/" dataset = "../WSI-Evaluator/datasets/trial/" test = "../WSI-Evaluator/datasets/test/" tialset = "../WSI-Evaluator/datasets/trial/" testset = "../WSI-Evaluator/datasets/test/" base_out = "../baseline/output/" output = "../output/" Loading @@ -20,7 +20,7 @@ which they should be merged. The first method with a positive result is used and methods labeled with 0 are ignored. At least one method must be given a value != 0. ''' colour_rank = 1 propagation_rank = 1 mst_rank = 2 ''' Loading @@ -45,9 +45,10 @@ max_context_size = 20 ''' Choose filters for building the graph. - Use dynamic filters for nodes and edges (mean frequencies of occurences) - Use dynamic filters for nodes and edges (mean frequencies of occurences) (ABSINTH uses this as a standard) or - Only consider occurrences/cooccurrences for nodes/edges, that occur more often than these values. - Only consider occurrences/cooccurrences for nodes/edges, that occur more often than these values. (Hyperlex uses these (nodes: 10, edges: 5))) - Only consider edges with a weight beneath the maximum weight ''' Loading @@ -59,9 +60,9 @@ max_weight = 0.9 ''' Choose minimum number of neighbours and maximum median weight of the most frequent neighbours of a node for root hubs. - the threshold is calculated using the media of the same number of neighbors declared in min_neighbors. - (the threshold is calculated using the median of the same number of neighbours declared in min_neighbours) ''' min_neighbors = 6 min_neighbours = 5 threshold = 0.8 ''' Loading src/singletons.py +9 −2 Original line number Diff line number Diff line #!/usr/bin/env python3 # -*- coding: utf-8 -*- """Singleton Baseline for ABSINTH This tool works as a Singleton Clustering Baseline .. _Association Based Semantic Induction Tools from Heidelberg https://gitlab.cl.uni-heidelberg.de/zimmermann/absinth """ import config import time import sys Loading @@ -10,9 +17,9 @@ final_path = 'final/{}.absinth'.format(hex(time_int)[2:]) results = open(final_path, 'w') if '-t' in sys.argv: data_path = config.test data_path = config.testset else: data_path = config.dataset data_path = config.trialset with open(data_path+'results.txt', 'r') as f: Loading Loading
src/absinth.py +53 −52 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ Example: $ python3 absinth.py The function can be called with a list of modifiers. absinth.py can be called with a list of modifiers. Modifiers: '-t': Runs absinth.py on the trial path given in the config.py instead of the Loading Loading @@ -79,7 +79,7 @@ def read_dataset(data_path: str) -> (dict, dict): if id1 not in results: results[id1]=list() results[id1].append(" ".join(l[2:]).strip()) # here I join title and snippet, the URL is ignored results[id1].append(" ".join(l[2:]).strip()) # here title and snippet are joined, the URL is ignored # topics.txt is a list of target words Loading @@ -106,6 +106,8 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): Counts frequencies from corpus and search result list, builds graph from these counts (with some filters). Root hubs (senses) are collected from this graph. The frequencies are saved in json-files (from where they are loaded, if found) to improve runtime in the later use of absinth.py Args: topic_name: Target string. Loading Loading @@ -138,13 +140,13 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): edge_dict_name = topic_name+'_edge.json' graph_in_existence = False for graph_name in os.listdir(config.graph): for graph_name in os.listdir(config.graph_path): if topic_name in graph_name: graph_in_existence = True with open(config.graph+node_dict_name, 'r') as node_file, open(config.graph+edge_dict_name, 'r') as edge_file: with open(config.graph_path+node_dict_name, 'r') as node_file, open(config.graph_path+edge_dict_name, 'r') as edge_file: node_freq_dict = json.load(node_file) edge_freq_dict = json.load(edge_file) Loading @@ -159,7 +161,7 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): node_freq_dict, edge_freq_dict = frequencies(target_string, result_list) with open(config.graph+node_dict_name, 'w') as node_file, open(config.graph+edge_dict_name, 'w') as edge_file: with open(config.graph_path+node_dict_name, 'w') as node_file, open(config.graph_path+edge_dict_name, 'w') as edge_file: json.dump(node_freq_dict, node_file) Loading Loading @@ -187,7 +189,7 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): print('[a]', 'Collecting root hubs.\t('+topic_name+')') root_hub_list = root_hubs(graph, edge_freq_dict) #adds sense inventory to buffer with some common neighbors for context #adds sense inventory to buffer with some common neighbours for context stat_dict['hubs'] = dict() for root_hub in root_hub_list: Loading @@ -196,10 +198,10 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): if root_hub < node \ else edge_freq_dict[node, root_hub] most_frequent_neighbor_list = sorted(graph.adj[root_hub], most_frequent_neighbour_list = sorted(graph.adj[root_hub], key=by_frequency, reverse=True) stat_dict['hubs'][root_hub] = most_frequent_neighbor_list[:6] stat_dict['hubs'][root_hub] = most_frequent_neighbour_list[:6] return graph, root_hub_list, stat_dict Loading @@ -209,7 +211,7 @@ def frequencies(target_string: str, search_result_list: list) -> (dict, dict): Iterates over the corpus (and snippets provided with the task) line by line and counts every token and tuple of tokens within a line (context). These tokens is filtered by stop words, pos tags and context length. tokens are filtered by stop words, pos tags and context length. Args: target_string: contexts are selected if they contain this string. For Loading @@ -218,8 +220,9 @@ def frequencies(target_string: str, search_result_list: list) -> (dict, dict): Returns: Dictionary of occurrences of every eligible token within every context the target occurs in, dictionary of occurrences of every eligible tuple of tokens within every context the target occurs in. the target occurs in, Dictionary of occurrences of every eligible tuple of tokens within every context the target occurs in. """ Loading Loading @@ -400,7 +403,7 @@ def build_graph(node_freq_dict: dict, edge_freq_dict: dict) -> nx.Graph: tokens within every context the target occurs in. Returns: Filtered undirected dice weighted small word cooccurrence graph for a Filtered undirected weighted small word cooccurrence graph for a given target entity. """ Loading Loading @@ -469,13 +472,13 @@ def root_hubs(graph: nx.Graph, edge_freq_dict: dict) -> list: """Identifies senses (root hubs) by choosing nodes with high degrees Selects root hubs according to the algorithm in Véronis (2004). Nodes with high degree and neighbors with low weights (high cooccurrence) are chosen high degree and neighbours with low weights (high cooccurrence) are chosen until there are no more viable candidates. A root hub candidate is every node that is not already a hub and is not a neighbor of one. node that is not already a hub and is not a neighbour of one. Args: graph: Weighted undirected graph. edge_freq_dict: Dictionary of weights for every tuple in our graph. edge_freq_dict: Dictionary of weights for every tuple in the graph. Returns: List of root hubs, i.e. strings that are selected using the algorithm Loading @@ -484,17 +487,17 @@ def root_hubs(graph: nx.Graph, edge_freq_dict: dict) -> list: mean_degree = np.mean(list(dict(graph.degree).values())) if config.min_neighbors == 'log' and mean_degree > 0: min_neighbors = int(np.log(mean_degree) * mean_degree) if config.min_neighbours == 'log' and mean_degree > 0: min_neighbours = int(np.log(mean_degree) * mean_degree) else: min_neighbors = config.min_neighbors min_neighbours = config.min_neighbours threshold = config.threshold # Allow operations on graph without altering original one. graph_copy = deepcopy(graph) # Sort according to degree (number of neighbors). # Sort according to degree (number of neighbours). candidate_list = sorted(graph_copy.nodes, key=lambda node: graph_copy.degree[node], reverse=True) Loading @@ -506,27 +509,27 @@ def root_hubs(graph: nx.Graph, edge_freq_dict: dict) -> list: candidate = candidate_list[0] #best hub candidate if graph_copy.degree[candidate] >= min_neighbors: if graph_copy.degree[candidate] >= min_neighbours: by_frequency = lambda node: edge_freq_dict[candidate,node] \ if candidate < node \ else edge_freq_dict[node,candidate] most_frequent_neighbor_list = sorted(graph_copy.adj[candidate], most_frequent_neighbour_list = sorted(graph_copy.adj[candidate], key=by_frequency, reverse=True) [:min_neighbors] reverse=True) [:min_neighbours] # If the mean weight of the most frequent neighbors cooccur # If the mean weight of the most frequent neighbours cooccur # frequently enough with candidate, the candidate is approved. if np.mean([graph_copy.edges[candidate,node]['weight'] for node in most_frequent_neighbor_list]) < threshold: for node in most_frequent_neighbour_list]) < threshold: # Add candidate as root hub. hub_list.append(candidate) # Remove neighbors of new hub as hub candidates. for neighbor in deepcopy(graph_copy).adj[candidate]: graph_copy.remove_node(neighbor) # Remove neighbours of new hub as hub candidates. for neighbour in deepcopy(graph_copy).adj[candidate]: graph_copy.remove_node(neighbour) # Remove hub candidate. graph_copy.remove_node(candidate) Loading @@ -550,9 +553,9 @@ def root_hubs(graph: nx.Graph, edge_freq_dict: dict) -> list: def label_graph(graph: nx.Graph, root_hub_list: list) -> nx.Graph: """propagations graph accoring to root hubs. """Propagations graph according to root hubs. Evolving network that propagations neighboring nodes iterative. See sentiment Evolving network that propagations neighbouring nodes iterative. See sentiment propagation. Args: Loading @@ -560,7 +563,7 @@ def label_graph(graph: nx.Graph, root_hub_list: list) -> nx.Graph: root_hub_list: List of senses. Returns: labelled graph. Labelled graph. """ Loading Loading @@ -592,20 +595,20 @@ def label_graph(graph: nx.Graph, root_hub_list: list) -> nx.Graph: for node in graph.nodes: neighbor_weight_list = [0] * len(root_hub_list) neighbour_weight_list = [0] * len(root_hub_list) for neighbor in graph_copy[node]: for neighbour in graph_copy[node]: if graph_copy.node[neighbor]['sense'] == None: if graph_copy.node[neighbour]['sense'] == None: pass else: neighbor_weight_list[graph_copy.node[neighbor]['sense']] \ += 1 - graph_copy[node][neighbor]['weight'] neighbour_weight_list[graph_copy.node[neighbour]['sense']] \ += 1 - graph_copy[node][neighbour]['weight'] if any(neighbor_weight_list): if any(neighbour_weight_list): graph.node[node]['dist'].append(neighbor_weight_list) graph.node[node]['dist'].append(neighbour_weight_list) old_propagation = graph_copy.node[node]['sense'] new_propagation = np.argmax(np.mean(graph.node[node]['dist'], axis=0)) Loading @@ -625,8 +628,6 @@ def label_graph(graph: nx.Graph, root_hub_list: list) -> nx.Graph: graph.node[node]['dist'] = np.mean(graph.node[node]['dist'], axis=0) draw_propagation(graph,root_hub_list) return graph Loading Loading @@ -719,7 +720,7 @@ def disambiguate_propagation(graph: nx.Graph, root_hub_list: list, context_list: return mapping_dict def draw_propagation(graph: nx.Graph, root_hub_list: list) -> None: """def draw_propagation(graph: nx.Graph, root_hub_list: list) -> None: pass Loading Loading @@ -758,7 +759,7 @@ def draw_propagation(graph: nx.Graph, root_hub_list: list) -> None: plt.savefig('../figures/'+graph.graph['target']+'.png') plt.clf() """ ############################## # MST Disambiguation # Loading @@ -768,7 +769,7 @@ def draw_propagation(graph: nx.Graph, root_hub_list: list) -> None: def components(graph: nx.Graph, root_hub_list: list, target_string: str) -> nx.Graph: """Builds minimum spanning tree from graph and removes singletons. Applies components algorithm from Véronis (2004) and removes singletons. Applies components algorithm from Véronis (2004) Args: graph: Undirected weighted graph. Loading Loading @@ -938,7 +939,7 @@ def local_clustering_coefficient(graph: nx.Graph, node: str) -> float: """Calculates local clustering coefficient from node. Local Clustering Coefficient is defined as number of edges between neighbors of a node, divided by the number of possible nodes. neighbours of a node, divided by the number of possible nodes. Args: graph: Undirected graph. Loading @@ -948,23 +949,23 @@ def local_clustering_coefficient(graph: nx.Graph, node: str) -> float: Local coefficient. """ neighbor_list = graph.adj[node] neighbour_list = graph.adj[node] neighbor_edge_list = [(x,y) for x in neighbor_list for y in neighbor_list if x<y] neighbour_edge_list = [(x,y) for x in neighbour_list for y in neighbour_list if x<y] if len(neighbor_edge_list) == 0: if len(neighbour_edge_list) == 0: return 0 else: edge_count = 0 for x,y in neighbor_edge_list: for x,y in neighbour_edge_list: if graph.has_edge(x,y): edge_count += 1 return edge_count/len(neighbor_edge_list) return edge_count/len(neighbour_edge_list) Loading Loading @@ -1112,7 +1113,7 @@ def main(topic_id: int, topic_name: str, result_dict: dict) -> None: stat_dict['L_rand'] = None stat_dict['C_rand'] = 0 propagation_rank = config.colour_rank propagation_rank = config.propagation_rank mst_rank = config.mst_rank #Merges Mappings according to pipeline Loading Loading @@ -1209,9 +1210,9 @@ if __name__ == '__main__': # If absinth.py is run in test environment. if '-t' in sys.argv: data_path = config.test data_path = config.testset else: data_path = config.dataset data_path = config.trialset result_dict, topic_dict = read_dataset(data_path) Loading
src/abstinent.py +34 −26 Original line number Diff line number Diff line Loading @@ -102,8 +102,9 @@ def frequencies(target_string: str, search_result_list: list) -> (dict, dict): Returns: Dictionary of occurrences of every eligible token within every context the target occurs in, dictionary of occurrences of every eligible tuple of tokens within every context the target occurs in. the target occurs in, Dictionary of occurrences of every eligible tuple of tokens within every context the target occurs in. """ Loading Loading @@ -340,8 +341,20 @@ def build_graph(node_freq_dict: dict, edge_freq_dict: dict) -> nx.Graph: def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): """ """ Induces word senses for a given topic from corpus. Use n random nodes as root hubs. The frequencies are saved in json-files (from where they are loaded, if found) to improve runtime in the later use of abstinent.py Args: topic_name: Target string. result_list: List of search result (context) strings. Returns: A cooccurrence graph, a list of root hub strings (senses) and dictionary of various statistics. """ stat_dict = dict() Loading @@ -356,13 +369,13 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): edge_dict_name = topic_name+'_edge.json' graph_in_existence = False for graph_name in os.listdir(config.graph): for graph_name in os.listdir(config.graph_path): if topic_name in graph_name: graph_in_existence = True with open(config.graph+node_dict_name, 'r') as node_file, open(config.graph+edge_dict_name, 'r') as edge_file: with open(config.graph_path+node_dict_name, 'r') as node_file, open(config.graph_path+edge_dict_name, 'r') as edge_file: node_freq_dict = json.load(node_file) edge_freq_dict = json.load(edge_file) Loading @@ -377,7 +390,7 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): node_freq_dict, edge_freq_dict = frequencies(topic_name, result_list) with open(config.graph+node_dict_name, 'w') as node_file, open(config.graph+edge_dict_name, 'w') as edge_file: with open(config.graph_path+node_dict_name, 'w') as node_file, open(config.graph_path+edge_dict_name, 'w') as edge_file: json.dump(node_freq_dict, node_file) Loading Loading @@ -411,7 +424,7 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): reverse=True)[:sense_count] root_hub_list = [hub[1] for hub in root_hub_list] #adds sense inventory to buffer with some common neighbors for context #adds sense inventory to buffer with some common neighbours for context stat_dict['hubs'] = dict() for root_hub in root_hub_list: Loading @@ -420,17 +433,16 @@ def induce(topic_name: str, result_list: list) -> (nx.Graph, list, dict): if root_hub < node \ else edge_freq_dict[node, root_hub] most_frequent_neighbor_list = sorted(graph.adj[root_hub], most_frequent_neighbour_list = sorted(graph.adj[root_hub], key=by_frequency, reverse=True) stat_dict['hubs'][root_hub] = most_frequent_neighbor_list[:6] stat_dict['hubs'][root_hub] = most_frequent_neighbour_list[:6] return graph, root_hub_list, stat_dict def bag_of_senses(graph: nx.Graph, root_hub_list:list) -> dict: """ Matches each node to the root hub it is closest to. """Matches each node to the root hub it is closest to. """ root_hub_count = len(root_hub_list) Loading @@ -456,7 +468,7 @@ def bag_of_senses(graph: nx.Graph, root_hub_list:list) -> dict: def disambiguate(bag_of_senses: dict, context_list: list) -> dict: """ Lesk. A simple Lesk disambiguation Algorithm. """ context_idx = 0 Loading Loading @@ -534,10 +546,6 @@ def print_stats(stat_dict: dict) -> None: stat_file.write('\t'.join([str(stat_dict[key]) for key in key_list])+'\n') def global_clustering_coefficient(graph: nx.Graph) -> float: """Calculates global clustering coefficient from graph. Loading @@ -555,23 +563,23 @@ def global_clustering_coefficient(graph: nx.Graph) -> float: for node in graph.nodes: neighbor_list = graph.adj[node] neighbour_list = graph.adj[node] neighbor_edge_list = [(x,y) for x in neighbor_list for y in neighbor_list if x<y] neighbour_edge_list = [(x,y) for x in neighbour_list for y in neighbour_list if x<y] if len(neighbor_edge_list) == 0: if len(neighbour_edge_list) == 0: local_coefficient_list.append(0) else: edge_count = 0 for x,y in neighbor_edge_list: for x,y in neighbour_edge_list: if graph.has_edge(x,y): edge_count += 1 local_coefficient_list.append(edge_count/len(neighbor_edge_list)) local_coefficient_list.append(edge_count/len(neighbour_edge_list)) return np.mean(local_coefficient_list) Loading Loading @@ -695,16 +703,16 @@ def main(topic_id: int, topic_name: str, result_dict: dict) -> None: if __name__ == '__main__': """Check for modifiers and call main(). Only called when absinth.py is started manually. Checks for various Only called when abstinent.py is started manually. Checks for various modifiers, i.e. test environment and number of processes to run simultaneously. """ # If absinth.py is run in test environment. # If abstinent.py is run in test environment. if '-t' in sys.argv: data_path = config.test data_path = config.testset else: data_path = config.dataset data_path = config.trialset result_dict, topic_dict = read_dataset(data_path) Loading
src/all-in-one.py +9 −2 Original line number Diff line number Diff line #!/usr/bin/env python3 # -*- coding: utf-8 -*- """All-In-One Baseline for ABSINTH This tool works as a All-In-One Clustering Baseline .. _Association Based Semantic Induction Tools from Heidelberg https://gitlab.cl.uni-heidelberg.de/zimmermann/absinth """ import config import time import sys Loading @@ -10,9 +17,9 @@ final_path = 'final/{}.absinth'.format(hex(time_int)[2:]) results = open(final_path, 'w') if '-t' in sys.argv: data_path = config.test data_path = config.testset else: data_path = config.dataset data_path = config.trialset with open(data_path+'results.txt', 'r') as f: Loading
src/config.py +9 −8 Original line number Diff line number Diff line Loading @@ -6,10 +6,10 @@ Configuration file Choose paths for corpus, dataset and output. - The output directory should be empty when starting absinth. ''' graph = "./.graphs/" graph_path = "./.graphs/" corpus = "/proj/absinth/wikipedia_shuffled2/" dataset = "../WSI-Evaluator/datasets/trial/" test = "../WSI-Evaluator/datasets/test/" tialset = "../WSI-Evaluator/datasets/trial/" testset = "../WSI-Evaluator/datasets/test/" base_out = "../baseline/output/" output = "../output/" Loading @@ -20,7 +20,7 @@ which they should be merged. The first method with a positive result is used and methods labeled with 0 are ignored. At least one method must be given a value != 0. ''' colour_rank = 1 propagation_rank = 1 mst_rank = 2 ''' Loading @@ -45,9 +45,10 @@ max_context_size = 20 ''' Choose filters for building the graph. - Use dynamic filters for nodes and edges (mean frequencies of occurences) - Use dynamic filters for nodes and edges (mean frequencies of occurences) (ABSINTH uses this as a standard) or - Only consider occurrences/cooccurrences for nodes/edges, that occur more often than these values. - Only consider occurrences/cooccurrences for nodes/edges, that occur more often than these values. (Hyperlex uses these (nodes: 10, edges: 5))) - Only consider edges with a weight beneath the maximum weight ''' Loading @@ -59,9 +60,9 @@ max_weight = 0.9 ''' Choose minimum number of neighbours and maximum median weight of the most frequent neighbours of a node for root hubs. - the threshold is calculated using the media of the same number of neighbors declared in min_neighbors. - (the threshold is calculated using the median of the same number of neighbours declared in min_neighbours) ''' min_neighbors = 6 min_neighbours = 5 threshold = 0.8 ''' Loading
src/singletons.py +9 −2 Original line number Diff line number Diff line #!/usr/bin/env python3 # -*- coding: utf-8 -*- """Singleton Baseline for ABSINTH This tool works as a Singleton Clustering Baseline .. _Association Based Semantic Induction Tools from Heidelberg https://gitlab.cl.uni-heidelberg.de/zimmermann/absinth """ import config import time import sys Loading @@ -10,9 +17,9 @@ final_path = 'final/{}.absinth'.format(hex(time_int)[2:]) results = open(final_path, 'w') if '-t' in sys.argv: data_path = config.test data_path = config.testset else: data_path = config.dataset data_path = config.trialset with open(data_path+'results.txt', 'r') as f: Loading