簡體   English   中英

是否可以在沒有詞匯的情況下導出和使用 spaCy NER model 並即時注入令牌/向量?

[英]Is it possible to export and use a spaCy NER model without an vocab and inject tokens/vectors on the fly?

介紹

我的問題與標題有點遠,但本質上很好地總結了我目前所堅持的內容。

我需要集成一個 NER model 作為復雜和分布式 NLP 管道的一部分,我正在做的是:

  1. 基於en_core_web_lg model 訓練新的 NER model 以識別我在 NER 任務中的自定義實體

  2. 保存 model 跳過詞匯以節省磁盤空間和 memory 使用

  3. 最后加載 model 以運行一些推理,使用之前有人在我的管道中預先計算的標記和向量,而不是使用 model 詞匯表(標准方式)再次計算它。

我在沒有詞匯的情況下保存 model 的原因是因為在我的分布式管道中,首先要做的事情之一是對文本進行標記/矢量化,以便任務的 rest 具有此輸入。

→ 在繼續之前,我想澄清一下,以標准方式(保存詞匯),我可以訓練我的自定義 NER,保存/加載並運行推理,而不會出現重大問題,並且准確度非常好。

之后閱讀spaCy 文檔,我發現可以在沒有詞匯表的情況下保存我的 model,您甚至可以從標記列表和自定義詞匯表(在我的例子中是一個空詞匯表)構建文檔 此外,我還能夠使用之前有人在我的管道中為我計算的文檔向量來設置文檔向量。

但是,當我將 model 保存在ner/cfg文件中時,我看到有對訓練 NER model 的向量的引用(en_core_web_lg.vectors):

{
   "disable":[
     "tagger",
     "parser"
   ],
   "beam_width":1,
   "beam_density":0.0,
   "beam_update_prob":1.0,
   "cnn_maxout_pieces":3,
   "nr_feature_tokens":6,
   "deprecation_fixes":{
      "vectors_name":"en_core_web_lg.vectors"
   },
   "nr_class":86,
   "hidden_depth":1,
   "token_vector_width":96,
   "hidden_width":64,
   "maxout_pieces":2,
   "pretrained_vectors":"en_core_web_lg.vectors",
   "bilstm_depth":0,
   "self_attn_depth":0,
   "conv_depth":4,
   "conv_window":1,
   "embed_size":2000
 }

當我嘗試加載 model 時沒有在 memory 中包含這些向量(換句話說,沒有加載該詞匯),此參考是導致錯誤的原因。

如果我在 cfg 文件中刪除這些引用,model 會正確加載,並且我可以使用我的向量運行推理,但獲得的預測與我使用第一個 model(使用原始詞匯)獲得的預測非常不同,並且包含幾個錯誤。

問題

這讓我想到了我最初的問題:是否可以用空詞匯保存 NER model,然后使用從我的管道中先前計算的令牌和向量以某種方式構建的 SpaCy Doc 進行推理?

提前非常感謝!

順便說一句,我正在使用spaCy 2.3.7 ,我在下面放了一些代碼片段來澄清:

1. 培訓

    nlp = spacy.load("en_core_web_lg", disable=['tagger', 'parser'])

    ner = nlp.get_pipe('ner')
    ner.add_label("FOO_ENTITY")
    ner.add_label("BAR_ENTITY")
    ner.add_label("COOL_ENTITY")

    # Start the training
    optimizer = nlp.begin_training()

    # Loop for EPOCHS iterations
    losses_hist = []
    for itn in range(30):
        # Shuffle the training data
        random.shuffle(Xy_train)
        losses = {}

        # Batch the examples and iterate over them
        for batch in spacy.util.minibatch(Xy_train, size=32):
            texts = [text for text, entities, _ in batch]
            golds = [{'entities': entities} for text, entities, _ in batch]

            # Update the model
            nlp.update(docs=texts, golds=golds, losses=losses)

        print(losses)
        losses_hist.append(losses)

2.a運行推理(標准)

    # I already have the text split in tokens
    doc = Doc(nlp.vocab, words=tokens)  # Create doc from tokens
    ner = nlp.get_pipe("ner")
    doc = ner(doc)  # Call NER step for doc

    for ent in doc.ents:
        print(f"value: {ent.text}, start: {ent.start_char}, end: {ent.end_char}, entity: {ent.label_}")

2.b 使用外部向量運行推理

    # I already have the text split in tokens and their vectors
    vectors = Vectors(data=embeddings, keys=tokens)
    nlp.vocab.vectors = vectors
    doc = Doc(nlp.vocab, words=tokens)
    
    ner = nlp.get_pipe("ner")
    doc = ner(doc)  # Call NER step for doc

    for ent in doc.ents:
        print(f"value: {ent.text}, start: {ent.start_char}, end: {ent.end_char}, entity: {ent.label_}")

3. 保存/加載 model

# Save model
nlp.to_disk(str(dir_)

# Load model
nlp = spacy.load(str(dir_), exclude=['vocab']) 

在 spacy v2(不是 v3.)中,有一些隱藏的背景步驟可以在特定名稱下全局注冊向量,以用作統計模型中的特征。 (這背后的想法是同一進程中的多個模型可以潛在地共享 RAM 中的相同向量。)

要使向量子集適用於特定文本,您需要以正確的名稱注冊新向量:

  • 創建向量時使用與 model 元數據中相同的Vectors(name=) (類似於en_core_web_lg.vectors
  • 運行spacy._ml.link_vectors_to_models(vocab)

我很確定這將開始打印警告並根據數據形狀在內部重命名向量,如果您對具有相同名稱的不同向量集重復執行此操作。 我認為您可以忽略警告,它適用於該單獨的文本,但它可能會破壞使用相同矢量名稱/形狀的同一腳本中加載的任何其他模型。

如果您在實踐中經常這樣做,您可能想要編寫一個自定義版本的link_vectors_to_models來更有效地迭代非常小的向量表中的詞匯表,或者只修改詞匯表中您知道需要的詞. 這實際上取決於您運行link_vectors_to_models時詞匯的大小。

經過幾次測試后,我發現以下在 memory 中為我的 model 的詞匯重新加載向量的方法對我來說是正確的。 另一方面,這個答案起到了啟發作用,但不適用於 spaCy 2.1.4。

有趣的是:

  • 在更新之前,我必須對詞匯進行深層復制
  • 向量必須與訓練 NER model 的向量具有完全相同的名稱(在我的例子中是en_core_web_lg.vectors
  • 需要更新 memory 中的向量,如下所示: thinc.extra.load_nlp.VECTORS
    import copy
    import spacy
    import thinc
    from spacy.vectors import Vectors
    from spacy.vocab import Vocab
    from thinc.v2v import Model
    
    vocab = copy.deepcopy(nlp.vocab)
    vectors = Vectors(data=embeddings, keys=tokens, name='en_core_web_lg.vectors')
    ops = Model.ops
    thinc.extra.load_nlp.VECTORS[(ops.device, 'en_core_web_lg.vectors')] = vectors.data  # Must re-load vectors
    vocab.vectors = vectors
    
    doc = Doc(vocab, words=tokens)  # Create doc from tokens an custom vocab
    ner = nlp.get_pipe("ner")
    doc = ner(doc)  # Call NER step for doc

希望對其他用戶有用!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM