mirror of
				https://github.com/explosion/spaCy.git
				synced 2025-10-31 07:57:35 +03:00 
			
		
		
		
	first stab at model - not functional yet
This commit is contained in:
		
							parent
							
								
									9f33732b96
								
							
						
					
					
						commit
						c6ca8649d7
					
				|  | @ -6,53 +6,168 @@ import datetime | |||
| from os import listdir | ||||
| 
 | ||||
| from examples.pipeline.wiki_entity_linking import run_el, training_set_creator, kb_creator | ||||
| from examples.pipeline.wiki_entity_linking import wikidata_processor as wd | ||||
| 
 | ||||
| from spacy._ml import SpacyVectors, create_default_optimizer, zero_init | ||||
| 
 | ||||
| from thinc.api import chain | ||||
| from thinc.v2v import Model, Maxout, Softmax, Affine, ReLu | ||||
| from thinc.api import flatten_add_lengths | ||||
| from thinc.t2v import Pooling, sum_pool, mean_pool | ||||
| from thinc.t2t import ExtractWindow, ParametricAttention | ||||
| from thinc.misc import Residual | ||||
| 
 | ||||
| """ TODO: this code needs to be implemented in pipes.pyx""" | ||||
| 
 | ||||
| 
 | ||||
| def train_model(kb, nlp, training_dir, entity_descr_output, limit=None): | ||||
| class EL_Model(): | ||||
| 
 | ||||
|     labels = ["MATCH", "NOMATCH"] | ||||
|     name = "entity_linker" | ||||
| 
 | ||||
|     def __init__(self, kb, nlp): | ||||
|         run_el._prepare_pipeline(nlp, kb) | ||||
|         self.nlp = nlp | ||||
|         self.kb = kb | ||||
| 
 | ||||
|         self.entity_encoder = self._simple_encoder(width=300) | ||||
|         self.article_encoder = self._simple_encoder(width=300) | ||||
| 
 | ||||
|     def train_model(self, training_dir, entity_descr_output, limit=None, to_print=True): | ||||
|         instances, gold_vectors, entity_descriptions, doc_by_article = self._get_training_data(training_dir, | ||||
|                                                                                                entity_descr_output, | ||||
|                                                                                                limit, to_print) | ||||
| 
 | ||||
|         if to_print: | ||||
|             print("Training on", len(gold_vectors), "instances") | ||||
|             print(" - pos:", len([x for x in gold_vectors if x]), "instances") | ||||
|             print(" - pos:", len([x for x in gold_vectors if not x]), "instances") | ||||
|             print() | ||||
| 
 | ||||
|         self.sgd_entity = self.begin_training(self.entity_encoder) | ||||
|         self.sgd_article = self.begin_training(self.article_encoder) | ||||
| 
 | ||||
|         losses = {} | ||||
| 
 | ||||
|         for inst, label, entity_descr in zip(instances, gold_vectors, entity_descriptions): | ||||
|             article = inst.split(sep="_")[0] | ||||
|             entity_id = inst.split(sep="_")[1] | ||||
|             article_doc = doc_by_article[article] | ||||
|             self.update(article_doc, entity_descr, label, losses=losses) | ||||
| 
 | ||||
|     def _simple_encoder(self, width): | ||||
|         with Model.define_operators({">>": chain}): | ||||
|             encoder = SpacyVectors \ | ||||
|                       >> flatten_add_lengths \ | ||||
|                       >> ParametricAttention(width)\ | ||||
|                       >> Pooling(sum_pool) \ | ||||
|                       >> Residual(zero_init(Maxout(width, width))) | ||||
| 
 | ||||
|         return encoder | ||||
| 
 | ||||
|     def begin_training(self, model): | ||||
|         # TODO ? link_vectors_to_models(self.vocab) | ||||
|         sgd = create_default_optimizer(model.ops) | ||||
|         return sgd | ||||
| 
 | ||||
|     def update(self, article_doc, entity_descr, label, drop=0., losses=None): | ||||
|         entity_encoding, entity_bp = self.entity_encoder.begin_update([entity_descr], drop=drop) | ||||
|         doc_encoding, article_bp = self.article_encoder.begin_update([article_doc], drop=drop) | ||||
| 
 | ||||
|         # print("entity/article output dim", len(entity_encoding[0]), len(doc_encoding[0])) | ||||
| 
 | ||||
|         mse, diffs = self._calculate_similarity(entity_encoding, doc_encoding) | ||||
| 
 | ||||
|         # print() | ||||
| 
 | ||||
|         # TODO: proper backpropagation taking ranking of elements into account ? | ||||
|         # TODO backpropagation also for negative examples | ||||
|         if label: | ||||
|             entity_bp(diffs, sgd=self.sgd_entity) | ||||
|             article_bp(diffs, sgd=self.sgd_article) | ||||
|             print(mse) | ||||
| 
 | ||||
| 
 | ||||
|     # TODO delete ? | ||||
|     def _simple_cnn_model(self, internal_dim): | ||||
|         nr_class = len(self.labels) | ||||
|         with Model.define_operators({">>": chain}): | ||||
|             model_entity = SpacyVectors >> flatten_add_lengths >> Pooling(mean_pool)    # entity encoding | ||||
|             model_doc = SpacyVectors >> flatten_add_lengths >> Pooling(mean_pool)       # doc encoding | ||||
|             output_layer = Softmax(nr_class, internal_dim*2) | ||||
|             model = (model_entity | model_doc) >> output_layer | ||||
|         # model.tok2vec = chain(tok2vec, flatten) | ||||
|         model.nO = nr_class | ||||
|         return model | ||||
| 
 | ||||
|     def predict(self, entity_doc, article_doc): | ||||
|         entity_encoding = self.entity_encoder(entity_doc) | ||||
|         doc_encoding = self.article_encoder(article_doc) | ||||
| 
 | ||||
|         print("entity_encodings", len(entity_encoding), entity_encoding) | ||||
|         print("doc_encodings", len(doc_encoding), doc_encoding) | ||||
|         mse, diffs = self._calculate_similarity(entity_encoding, doc_encoding) | ||||
|         print("mse", mse) | ||||
| 
 | ||||
|         return mse | ||||
| 
 | ||||
|     def _calculate_similarity(self, vector1, vector2): | ||||
|         if len(vector1) != len(vector2): | ||||
|             raise ValueError("To calculate similarity, both vectors should be of equal length") | ||||
| 
 | ||||
|         diffs = (vector2 - vector1) | ||||
|         error_sum = (diffs ** 2).sum(axis=1) | ||||
|         mean_square_error = error_sum / len(vector1) | ||||
|         return float(mean_square_error), diffs | ||||
| 
 | ||||
|     def _get_labels(self): | ||||
|         return tuple(self.labels) | ||||
| 
 | ||||
|     def _get_training_data(self, training_dir, entity_descr_output, limit, to_print): | ||||
|         id_to_descr = kb_creator._get_id_to_description(entity_descr_output) | ||||
| 
 | ||||
|         correct_entries, incorrect_entries = training_set_creator.read_training_entities(training_output=training_dir, | ||||
|                                                                                          collect_correct=True, | ||||
|                                                                                          collect_incorrect=True) | ||||
| 
 | ||||
|     entities = kb.get_entity_strings() | ||||
| 
 | ||||
|     id_to_descr = kb_creator._get_id_to_description(entity_descr_output) | ||||
|         instances = list() | ||||
|         entity_descriptions = list() | ||||
|         local_vectors = list()   # TODO: local vectors | ||||
|         gold_vectors = list() | ||||
|         doc_by_article = dict() | ||||
| 
 | ||||
|         cnt = 0 | ||||
|         for f in listdir(training_dir): | ||||
|             if not limit or cnt < limit: | ||||
|                 if not run_el.is_dev(f): | ||||
|                     article_id = f.replace(".txt", "") | ||||
|                 if cnt % 500 == 0: | ||||
|                     if cnt % 500 == 0 and to_print: | ||||
|                         print(datetime.datetime.now(), "processed", cnt, "files in the dev dataset") | ||||
|                     cnt += 1 | ||||
|                     if article_id not in doc_by_article: | ||||
|                         with open(os.path.join(training_dir, f), mode="r", encoding='utf8') as file: | ||||
|                             text = file.read() | ||||
|                     print() | ||||
|                     doc = nlp(text) | ||||
|                     doc_vector = doc.vector | ||||
|                     print("FILE", f, len(doc_vector), "D vector") | ||||
|                             doc = self.nlp(text) | ||||
|                             doc_by_article[article_id] = doc | ||||
| 
 | ||||
|                     for mention_pos, entity_pos in correct_entries[article_id].items(): | ||||
|                         descr = id_to_descr.get(entity_pos) | ||||
|                         if descr: | ||||
|                             doc_descr = nlp(descr) | ||||
|                             descr_vector = doc_descr.vector | ||||
|                             print("GOLD POS", mention_pos, entity_pos, len(descr_vector), "D vector") | ||||
|                             instances.append(article_id + "_" + entity_pos) | ||||
|                             doc = self.nlp(descr) | ||||
|                             entity_descriptions.append(doc) | ||||
|                             gold_vectors.append(True) | ||||
| 
 | ||||
|                     for mention_neg, entity_negs in incorrect_entries[article_id].items(): | ||||
|                         for entity_neg in entity_negs: | ||||
|                             descr = id_to_descr.get(entity_neg) | ||||
|                             if descr: | ||||
|                                 doc_descr = nlp(descr) | ||||
|                                 descr_vector = doc_descr.vector | ||||
|                                 print("GOLD NEG", mention_neg, entity_neg, len(descr_vector), "D vector") | ||||
|                                 instances.append(article_id + "_" + entity_neg) | ||||
|                                 doc = self.nlp(descr) | ||||
|                                 entity_descriptions.append(doc) | ||||
|                                 gold_vectors.append(False) | ||||
| 
 | ||||
|         if to_print: | ||||
|             print() | ||||
|             print("Processed", cnt, "dev articles") | ||||
|             print() | ||||
| 
 | ||||
|         return instances, gold_vectors, entity_descriptions, doc_by_article | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| # coding: utf-8 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from examples.pipeline.wiki_entity_linking import wikipedia_processor as wp, kb_creator, training_set_creator, run_el, train_el | ||||
| from examples.pipeline.wiki_entity_linking import wikipedia_processor as wp, kb_creator, training_set_creator, run_el | ||||
| from examples.pipeline.wiki_entity_linking.train_el import EL_Model | ||||
| 
 | ||||
| import spacy | ||||
| from spacy.vocab import Vocab | ||||
|  | @ -31,17 +32,17 @@ if __name__ == "__main__": | |||
|     # one-time methods to create KB and write to file | ||||
|     to_create_prior_probs = False | ||||
|     to_create_entity_counts = False | ||||
|     to_create_kb = True | ||||
|     to_create_kb = False | ||||
| 
 | ||||
|     # read KB back in from file | ||||
|     to_read_kb = True | ||||
|     to_test_kb = True | ||||
|     to_test_kb = False | ||||
| 
 | ||||
|     # create training dataset | ||||
|     create_wp_training = False | ||||
| 
 | ||||
|     # run training | ||||
|     run_training = False | ||||
|     run_training = True | ||||
| 
 | ||||
|     # apply named entity linking to the dev dataset | ||||
|     apply_to_dev = False | ||||
|  | @ -105,16 +106,17 @@ if __name__ == "__main__": | |||
|         print("STEP 5: create training dataset", datetime.datetime.now()) | ||||
|         training_set_creator.create_training(kb=my_kb, entity_def_input=ENTITY_DEFS, training_output=TRAINING_DIR) | ||||
| 
 | ||||
|     # STEP 7: apply the EL algorithm on the training dataset | ||||
|     # STEP 6: apply the EL algorithm on the training dataset | ||||
|     if run_training: | ||||
|         print("STEP 6: training ", datetime.datetime.now()) | ||||
|         my_nlp = spacy.load('en_core_web_sm') | ||||
|         train_el.train_model(kb=my_kb, nlp=my_nlp, training_dir=TRAINING_DIR, entity_descr_output=ENTITY_DESCR, limit=5) | ||||
|         my_nlp = spacy.load('en_core_web_md') | ||||
|         trainer = EL_Model(kb=my_kb, nlp=my_nlp) | ||||
|         trainer.train_model(training_dir=TRAINING_DIR, entity_descr_output=ENTITY_DESCR, limit=50) | ||||
|         print() | ||||
| 
 | ||||
|     # STEP 8: apply the EL algorithm on the dev dataset | ||||
|     # STEP 7: apply the EL algorithm on the dev dataset | ||||
|     if apply_to_dev: | ||||
|         my_nlp = spacy.load('en_core_web_sm') | ||||
|         my_nlp = spacy.load('en_core_web_md') | ||||
|         run_el.run_el_dev(kb=my_kb, nlp=my_nlp, training_dir=TRAINING_DIR, limit=2000) | ||||
|         print() | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user