From b2e6a29dd7ec4ea5fd78e994e62f195096a052cb Mon Sep 17 00:00:00 2001 From: ngupta10 Date: Tue, 16 Apr 2024 18:00:36 +0530 Subject: [PATCH] changes --- .../transformers/bert_ner_opensourcellm.py | 42 ++-- .../fixed_entities_set_opensourcellm.py | 18 +- ..._llm_bert_ner_or_fixed_entities_set_ner.py | 20 +- .../kg/ner_helperfunctions/fixed_predicate.py | 62 +++--- .../kg/rel_helperfunctions/embedding_store.py | 17 +- tests/data/llm/predicate_checker/test.pdf | Bin 0 -> 17653 bytes tests/llm_tests/bert_llm_test_predicates.py | 49 ++++- ...test_fixed_entities_predicates_workflow.py | 182 ++++++++++-------- 8 files changed, 233 insertions(+), 157 deletions(-) create mode 100644 tests/data/llm/predicate_checker/test.pdf diff --git a/querent/core/transformers/bert_ner_opensourcellm.py b/querent/core/transformers/bert_ner_opensourcellm.py index 65ce01f3..6cb7859c 100644 --- a/querent/core/transformers/bert_ner_opensourcellm.py +++ b/querent/core/transformers/bert_ner_opensourcellm.py @@ -25,22 +25,6 @@ from querent.kg.rel_helperfunctions.triple_to_json import TripleToJsonConverter from querent.kg.rel_helperfunctions.embedding_store import EmbeddingStore from querent.kg.rel_helperfunctions.filter_semantic_triples import SemanticTripleFilter - -""" - BERTLLM is a class derived from BaseEngine designed for processing language models, particularly focusing on named entity recognition and relationship extraction in text. It integrates various components for handling different types of input data (messages, images, code, tokens), extracting entities, filtering relevant information, and constructing knowledge graphs. - - Key functionalities include: - - Initializing with a specific configuration for named entity recognition (NER) and language model processing. - - Validating the presence of NER models and tokenizers. - - Processing various types of input data like messages, images, code, and tokens. - - Implementing methods for counting entity pairs, setting filter parameters, and processing tokens. - - Extracting and clustering entities and relationships from the text, and converting them into graph and vector formats. - - Handling errors and maintaining robustness in data processing. - - The class also incorporates mechanisms for filtering and clustering entities and relationships, as well as extracting embeddings and generating output in different formats. - """ - - class BERTLLM(BaseEngine): def __init__( self, @@ -97,7 +81,7 @@ def __init__( self.predicate_json_emb = self.create_emb.generate_relationship_embeddings(self.predicate_json) elif self.sample_relationships: self.predicate_context_extractor = FixedPredicateExtractor(predicate_types=self.sample_relationships,model = self.nlp_model) - self.predicate_json = self.predicate_context_extractor.construct_predicate_json(self.sample_relationships) + self.predicate_json = self.predicate_context_extractor.construct_predicate_json(relationship_types=self.sample_relationships) self.predicate_json_emb = self.create_emb.generate_relationship_embeddings(self.predicate_json) else: self.predicate_context_extractor = None @@ -167,8 +151,8 @@ async def process_tokens(self, data: IngestedTokens): if content: if self.fixed_entities: content = self.entity_context_extractor.find_entity_sentences(content) - if self.fixed_relationships: - content = self.predicate_context_extractor.find_predicate_sentences(content) + # if self.fixed_relationships: + # content = self.predicate_context_extractor.find_predicate_sentences(content) tokens = self.ner_llm_instance._tokenize_and_chunk(content) for tokenized_sentence, original_sentence, sentence_idx in tokens: (entities, entity_pairs,) = self.ner_llm_instance.extract_entities_from_sentence(original_sentence, sentence_idx, [s[1] for s in tokens],self.isConfinedSearch, self.fixed_entities, self.sample_entities) @@ -180,6 +164,7 @@ async def process_tokens(self, data: IngestedTokens): if self.sample_entities: doc_entity_pairs = self.entity_context_extractor.process_entity_types(doc_entities=doc_entity_pairs) if any(doc_entity_pairs): + print("Found doc_entity_pairs-------------------------------------", len(doc_entity_pairs)) doc_entity_pairs = self.ner_llm_instance.remove_duplicates(doc_entity_pairs) pairs_withattn = self.attn_scores_instance.extract_and_append_attention_weights(doc_entity_pairs) if self.enable_filtering == True and not self.entity_context_extractor and self.count_entity_pairs(pairs_withattn)>1 and not self.predicate_context_extractor: @@ -188,30 +173,38 @@ async def process_tokens(self, data: IngestedTokens): else: pairs_withemb = pairs_withattn pairs_with_predicates = process_data(pairs_withemb, file) + print("Found doc_entity_pairs-------------------------------------", len(pairs_with_predicates)) if self.enable_filtering == True and not self.entity_context_extractor and self.count_entity_pairs(pairs_withattn)>1 and not self.predicate_context_extractor: cluster_output = self.triple_filter.cluster_triples(pairs_with_predicates) clustered_triples = cluster_output['filtered_triples'] cluster_labels = cluster_output['cluster_labels'] cluster_persistence = cluster_output['cluster_persistence'] - # final_clustered_triples = self.triple_filter.filter_by_cluster_persistence(pairs_with_predicates, cluster_persistence, cluster_labels) if clustered_triples: filtered_triples, reduction_count = self.triple_filter.filter_triples(clustered_triples) else: - # filtered_triples, _ = self.triple_filter.filter_triples(clustered_triples) self.logger.debug(f"Filtering in {self.__class__.__name__} producing 0 entity pairs. Filtering Disabled. ") filtered_triples = pairs_with_predicates else: filtered_triples = pairs_with_predicates + print("Found doc_entity_pairs-------------------------------------", len(filtered_triples)) if not filtered_triples: self.logger.debug("No entity pairs") return elif not self.skip_inferences: - relationships = self.semantic_extractor.process_tokens(filtered_triples) + print("Extracting Entities-------------------------------------", filtered_triples) + relationships = self.semantic_extractor.process_tokens(filtered_triples[:5]) relationships = self.semantictriplefilter.filter_triples(relationships) + print("Relationships: {}".format(relationships)) if len(relationships) > 0: - embedding_triples = self.create_emb.generate_embeddings(relationships) + if self.fixed_relationships and self.sample_relationships: + embedding_triples = self.create_emb.generate_embeddings(relationships, relationship_finder=True, generate_embeddings_with_fixed_relationship = True) + elif self.sample_relationships: + print("Only for sample_relationships") + embedding_triples = self.create_emb.generate_embeddings(relationships, relationship_finder=True) + else: + embedding_triples = self.create_emb.generate_embeddings(relationships) if self.sample_relationships: - embedding_triples = self.predicate_context_extractor.process_predicate_types(embedding_triples) + embedding_triples = self.predicate_context_extractor.update_embedding_triples_with_similarity(self.predicate_json_emb, embedding_triples) for triple in embedding_triples: graph_json = json.dumps(TripleToJsonConverter.convert_graphjson(triple)) if graph_json: @@ -226,4 +219,5 @@ async def process_tokens(self, data: IngestedTokens): else: return filtered_triples, file except Exception as e: + print("Exception Caught: %s" % e) self.logger.debug(f"Invalid {self.__class__.__name__} configuration. Unable to process tokens. {e}") diff --git a/querent/core/transformers/fixed_entities_set_opensourcellm.py b/querent/core/transformers/fixed_entities_set_opensourcellm.py index 204a355c..58e45ae4 100644 --- a/querent/core/transformers/fixed_entities_set_opensourcellm.py +++ b/querent/core/transformers/fixed_entities_set_opensourcellm.py @@ -78,8 +78,12 @@ def __init__( raise ValueError("If specific predicates are provided, their types should also be provided.") if self.fixed_relationships and self.sample_relationships: self.predicate_context_extractor = FixedPredicateExtractor(fixed_predicates=self.fixed_relationships, predicate_types=self.sample_relationships,model = self.nlp_model) + self.predicate_json = self.predicate_context_extractor.construct_predicate_json(self.fixed_relationships, self.sample_relationships) + self.predicate_json_emb = self.create_emb.generate_relationship_embeddings(self.predicate_json) elif self.sample_relationships: self.predicate_context_extractor = FixedPredicateExtractor(predicate_types=self.sample_relationships,model = self.nlp_model) + self.predicate_json = self.predicate_context_extractor.construct_predicate_json(relationship_types=self.sample_relationships) + self.predicate_json_emb = self.create_emb.generate_relationship_embeddings(self.predicate_json) else: self.predicate_context_extractor = None self.user_context = config.user_context @@ -145,8 +149,8 @@ async def process_tokens(self, data: IngestedTokens): if content: if self.fixed_entities: content = self.entity_context_extractor.find_entity_sentences(content) - if self.fixed_relationships: - content = self.predicate_context_extractor.find_predicate_sentences(content) + # if self.fixed_relationships: + # content = self.predicate_context_extractor.find_predicate_sentences(content) tokens = self.ner_llm_instance._tokenize_and_chunk(content) for tokenized_sentence, original_sentence, sentence_idx in tokens: (entities, entity_pairs,) = self.ner_llm_instance.extract_entities_from_sentence(original_sentence, sentence_idx, [s[1] for s in tokens],self.isConfinedSearch, self.fixed_entities, self.sample_entities) @@ -169,9 +173,15 @@ async def process_tokens(self, data: IngestedTokens): self.logger.debug(f"length of relationships {len(relationships)}") relationships = self.semantictriplefilter.filter_triples(relationships) if len(relationships) > 0: - embedding_triples = self.create_emb.generate_embeddings(relationships) + if self.fixed_relationships and self.sample_relationships: + embedding_triples = self.create_emb.generate_embeddings(relationships, relationship_finder=True, generate_embeddings_with_fixed_relationship = True) + elif self.sample_relationships: + print("Only for sample_relationships") + embedding_triples = self.create_emb.generate_embeddings(relationships, relationship_finder=True) + else: + embedding_triples = self.create_emb.generate_embeddings(relationships) if self.sample_relationships: - embedding_triples = self.predicate_context_extractor.process_predicate_types(embedding_triples) + embedding_triples = self.predicate_context_extractor.update_embedding_triples_with_similarity(self.predicate_json_emb, embedding_triples) for triple in embedding_triples: graph_json = json.dumps(TripleToJsonConverter.convert_graphjson(triple)) if graph_json: diff --git a/querent/core/transformers/gpt_llm_bert_ner_or_fixed_entities_set_ner.py b/querent/core/transformers/gpt_llm_bert_ner_or_fixed_entities_set_ner.py index 47bfd898..421a46a9 100644 --- a/querent/core/transformers/gpt_llm_bert_ner_or_fixed_entities_set_ner.py +++ b/querent/core/transformers/gpt_llm_bert_ner_or_fixed_entities_set_ner.py @@ -66,15 +66,19 @@ def __init__( self.user_context = config.user_context self.nlp_model = NER_LLM.set_nlp_model(config.spacy_model_path) self.nlp_model = NER_LLM.get_class_variable() + self.create_emb = EmbeddingStore(inference_api_key=config.huggingface_token) if self.fixed_relationships and not self.sample_relationships: raise ValueError("If specific predicates are provided, their types should also be provided.") if self.fixed_relationships and self.sample_relationships: - self.predicate_context_extractor = FixedPredicateExtractor(fixed_predicates=self.fixed_relationships, predicate_types=self.sample_relationships, model = self.nlp_model) + self.predicate_context_extractor = FixedPredicateExtractor(fixed_predicates=self.fixed_relationships, predicate_types=self.sample_relationships,model = self.nlp_model) + self.predicate_json = self.predicate_context_extractor.construct_predicate_json(self.fixed_relationships, self.sample_relationships) + self.predicate_json_emb = self.create_emb.generate_relationship_embeddings(self.predicate_json) elif self.sample_relationships: - self.predicate_context_extractor = FixedPredicateExtractor(predicate_types=self.sample_relationships, model = self.nlp_model) + self.predicate_context_extractor = FixedPredicateExtractor(predicate_types=self.sample_relationships,model = self.nlp_model) + self.predicate_json = self.predicate_context_extractor.construct_predicate_json(relationship_types=self.sample_relationships) + self.predicate_json_emb = self.create_emb.generate_relationship_embeddings(self.predicate_json) else: self.predicate_context_extractor = None - self.create_emb = EmbeddingStore(inference_api_key=config.huggingface_token) if config.is_confined_search: self.llm_instance = Fixed_Entities_LLM(input_queue, llm_config) else : @@ -273,9 +277,15 @@ async def process_tokens(self, data: IngestedTokens): output_tuple = self.generate_output_tuple(result, context_json) relationships.append(output_tuple) if len(relationships) > 0: - embedding_triples = self.create_emb.generate_embeddings(relationships) + if self.fixed_relationships and self.sample_relationships: + embedding_triples = self.create_emb.generate_embeddings(relationships, relationship_finder=True, generate_embeddings_with_fixed_relationship = True) + elif self.sample_relationships: + print("Only for sample_relationships") + embedding_triples = self.create_emb.generate_embeddings(relationships, relationship_finder=True) + else: + embedding_triples = self.create_emb.generate_embeddings(relationships) if self.sample_relationships: - embedding_triples = self.predicate_context_extractor.process_predicate_types(embedding_triples) + embedding_triples = self.predicate_context_extractor.update_embedding_triples_with_similarity(self.predicate_json_emb, embedding_triples) for triple in embedding_triples: graph_json = json.dumps(TripleToJsonConverter.convert_graphjson(triple)) if graph_json: diff --git a/querent/kg/ner_helperfunctions/fixed_predicate.py b/querent/kg/ner_helperfunctions/fixed_predicate.py index c2068b31..2cc62f9d 100644 --- a/querent/kg/ner_helperfunctions/fixed_predicate.py +++ b/querent/kg/ner_helperfunctions/fixed_predicate.py @@ -137,42 +137,52 @@ def process_predicate_types(self, doc_predicates): except Exception as e: raise Exception(f"Error processing predicate types: {e}") - def construct_predicate_json(relationships=None, relationship_types=None): + def construct_predicate_json(self, relationships=None, relationship_types=None): predicate_values = [] if relationships and relationship_types: if len(relationships) != len(relationship_types): raise Exception("'relationships' and 'relationship_types' lists must have the same length.") for relationship, relationship_type in zip(relationships, relationship_types): predicate_value = f"{relationship} ({relationship_type})" - predicate_values.append({"predicate_value": predicate_value, "relationship": relationship, "type": relationship_type}) + predicate_values.append(json.dumps({"predicate_value": predicate_value, "relationship": relationship, "type": relationship_type})) elif relationship_types: for relationship_type in relationship_types: - predicate_values.append({"predicate_value": relationship_type, "type": relationship_type}) + predicate_values.append(json.dumps({"predicate_value": relationship_type, "type": relationship_type})) else: - return + return [] + + return predicate_values - return json.dumps(predicate_values) - - -def update_embedding_triples_with_similarity(predicate_json_emb, embedding_triples): - predicate_json_emb = [json.loads(item) for item in predicate_json_emb] - embedding_triples = [json.loads(item) for item in embedding_triples] - predicate_emb_list = [item["predicate_emb"] for item in predicate_json_emb if item["predicate_emb"] != "Not Implemented"] - predicate_emb_matrix = np.array(predicate_emb_list) - for triple in embedding_triples: - if triple["predicate_emb"] == "Not Implemented": + def update_embedding_triples_with_similarity(self, predicate_json_emb, embedding_triples): + try: + print("Updating embedding------------------------------") + predicate_json_emb = [json.loads(item) for item in predicate_json_emb] + predicate_emb_list = [item["predicate_emb"] for item in predicate_json_emb if item["predicate_emb"] != "Not Implemented"] + print("Updating embedding------------------------------1") + predicate_emb_matrix = np.array(predicate_emb_list) + print("Updating embedding------------------------------2") + updated_embedding_triples = [] + for triple in embedding_triples: + entity, triple_json, study_field = triple + triple_data = json.loads(triple_json) - continue - - current_predicate_emb = np.array(triple["predicate_emb"]).reshape(1, -1) - similarities = cosine_similarity(current_predicate_emb, predicate_emb_matrix) - max_similarity_index = np.argmax(similarities) - most_similar_predicate_details = predicate_json_emb[max_similarity_index] - triple["predicate_type"] = most_similar_predicate_details["type"] - if most_similar_predicate_details["relationship"].lower() != "unlabelled": - triple["predicate"] = most_similar_predicate_details["relationship"] - updated_embedding_triples = [json.dumps(item) for item in embedding_triples] - - return updated_embedding_triples + if triple_data["predicate_emb"] == "Not Implemented": + updated_embedding_triples.append(triple) + continue + + current_predicate_emb = np.array(triple_data["predicate_emb"]).reshape(1, -1) + similarities = cosine_similarity(current_predicate_emb, predicate_emb_matrix) + max_similarity_index = np.argmax(similarities) + most_similar_predicate_details = predicate_json_emb[max_similarity_index] + print("Score: ", similarities[0][max_similarity_index]) + if similarities[0][max_similarity_index] > 0.4: + triple_data["predicate_type"] = most_similar_predicate_details["type"] + if most_similar_predicate_details["relationship"].lower() != "unlabelled": + triple_data["predicate"] = most_similar_predicate_details["relationship"] + updated_triple_json = json.dumps(triple_data) + updated_embedding_triples.append((entity, updated_triple_json, study_field)) + return updated_embedding_triples + except Exception as e: + raise Exception(f"Error processing predicate types: {e}") diff --git a/querent/kg/rel_helperfunctions/embedding_store.py b/querent/kg/rel_helperfunctions/embedding_store.py index 03ae64fe..fedba29e 100644 --- a/querent/kg/rel_helperfunctions/embedding_store.py +++ b/querent/kg/rel_helperfunctions/embedding_store.py @@ -155,13 +155,12 @@ def generate_embeddings(self, payload, relationship_finder=False, generate_embed object_type = data.get("object_type","Unlabeled").replace('"', '\\"') context_embeddings = None predicate_embedding = None - if not relationship_finder: - context_embeddings = self.get_embeddings([context])[0] - else: - if generate_embeddings_with_fixed_relationship: - predicate_embedding = self.get_embeddings([predicate + " ("+predicate_type+")"])[0] - else: - predicate_embedding = self.get_embeddings([predicate_type])[0] + context_embeddings = self.get_embeddings([context])[0] + if relationship_finder and generate_embeddings_with_fixed_relationship: + predicate_embedding = self.get_embeddings([predicate + " ("+predicate_type+")"])[0] + elif relationship_finder: + print("Predicate ----------------------------", self.get_embeddings([predicate_type])) + predicate_embedding = self.get_embeddings([predicate_type])[0] essential_data = { "context": context, "context_embeddings" : context_embeddings, @@ -204,8 +203,8 @@ def generate_relationship_embeddings(self, payload): processed_pairs.append((updated_json_string)) except json.JSONDecodeError as e: self.logger.debug(f"JSON parsing error while generating embeddings for fixed realtionships: {e} in string.") - + return processed_pairs except Exception as e: - self.logger.error(f"Error in extracting embeddings for fixed realtionships: {e}") \ No newline at end of file + self.logger.debug(f"Error in extracting embeddings for fixed realtionships: {e}") \ No newline at end of file diff --git a/tests/data/llm/predicate_checker/test.pdf b/tests/data/llm/predicate_checker/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1e813885b68c823565763a06793a341456a9a160 GIT binary patch literal 17653 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