【问题标题】:Why does tokeniser break down words that are present in vocab为什么 tokeniser 会分解 vocab 中存在的单词
【发布时间】:2023-01-13 12:47:59
【问题描述】:

据我了解,分词器所做的是,给定每个单词,只有当该词不在 tokeniser.get_vocab() 中时,分词器才会将该词分解为子词:

def checkModel(model):
    tokenizer = AutoTokenizer.from_pretrained(model)

    allList = []
    for word in tokenizer.get_vocab():
        word = word.lower()

        tokens = tokenizer.tokenize(word)
        try:
            if word[0]!='#' and word[0]!='[' and tokens[0] != word:

                allList.append((word, tokens))
                print(word, tokens)
        except:
            continue 
    return allList

checkModel('bert-base-uncased')
# ideally should return an empty list

然而,我观察到的是,huggingface 上的一些模型会将单词分解成更小的部分,即使单词出现在词汇中也是如此。

checkModel('emilyalsentzer/Bio_ClinicalBERT')

output: 
welles ['well', '##es']
lexington ['le', '##xing', '##ton']
palestinian ['pale', '##st', '##inian']
...
elisabeth ['el', '##isa', '##beth']
alexander ['ale', '##xa', '##nder']
appalachian ['app', '##ala', '##chia', '##n']
mitchell ['mit', '##chel', '##l']
...
  
4630 # tokens in vocab got broken down, not supposed to happen

我已经检查了这种行为的几个模型,想知道为什么会这样?

【问题讨论】:

    标签: python python-3.x huggingface-transformers huggingface-tokenizers


    【解决方案1】:

    这是一个非常有趣的问题,我目前想知道是否应该将其视为 Huggingface 存储库中的错误报告。

    编辑:我意识到可以定义特定于模型的 tokenization_config.json 文件来覆盖默认行为。一个示例是 bert-base-cased repository,它具有以下分词器配置内容:

    {
      "do_lower_case": false
    }
    

    鉴于此功能可用,我认为最好的选择是联系作品的原作者并请他们考虑此配置(如果适用于一般用例)。

    原答案:

    事实证明,您要检查的词汇是welles,而词汇文件本身只包含Welles。注意到首字母大写的区别了吗?
    事实证明,您可以手动强制分词器专门检查大小写词汇,在这种情况下它可以正常工作。

    from transformers import AutoTokenizer
    
    tokenizer = AutoTokenizer.from_pretrained("emilyalsentzer/Bio_ClinicalBERT",
                                              do_lower_case=False)  # This is different
    print(tokenizer.do_lower_case)
    # Output: False
    
    # Lowercase input will result in split word
    tokenizer.convert_ids_to_tokens(tokenizer("welles")["input_ids"])
    # Output: ['[CLS]', 'well', '##es', '[SEP]']
    
    # Uppercase input will correctly *not split* the word
    tokenizer2.convert_ids_to_tokens(tokenizer2("Welles")["input_ids"])
    ['[CLS]', 'Welles', '[SEP]']
    

    然而,默认情况下,情况并非如此,所有单词都将转换为小写,这就是您找不到单词的原因:

    from transformers import AutoTokenizer
    
    tokenizer = AutoTokenizer.from_pretrained("emilyalsentzer/Bio_ClinicalBERT")
    
    # Per default, lowercasing is enabled!
    print(tokenizer.do_lower_case)
    
    # Output: True
    
    # This time now we get the same (lowercased) output both times!
    tokenizer.convert_ids_to_tokens(tokenizer("welles")["input_ids"])
    ['[CLS]', 'well', '##es', '[SEP]']
    tokenizer.convert_ids_to_tokens(tokenizer("Welles")["input_ids"])
    ['[CLS]', 'well', '##es', '[SEP]']
    
    

    【讨论】:

    • 是的,do_lower_case 似乎确实缓解了这个问题。然而,这仍然是违反直觉的,因为输入的单词在词汇表中的大小写完全一样,但它正在分解它们。我不确定这是分词器错误还是模型的问题。
    • 我不明白为什么这是违反直觉的?词汇表中有一个大写的单词,因此显然不会匹配输入的小写版本。如果你想改变原始词汇表,你当然也可以将原始模型的所有词汇表小写,但这可能会导致一些不需要的副作用。
    • 如果我们看上面的输出: welles ['well', '##es'] lexington ['le', '##xing', '##ton'] 这些是小写,在确切的情况下出现在词汇表中,因为我所做的只是遍历词汇表。然而词汇正在打破它们。如果输出是:output: Welles ['well', '##es'] Lexington ['le', '##xing', '##ton'] 是合理的。
    • 除了他们不在场。如果您检查 vocabulary of the linked model 和 Ctrl+F 查找 welles,您只会发现大写的 Welles 作为词汇的一部分。可能会引起一些混淆的是,您在 checkModel() 函数中调用了 word = word.lower(),所以无论如何您都将所有内容都变成了小写单词...
    【解决方案2】:

    您称为“emilyalsentzer/Bio_ClinicalBERT”的标记器具有原始基础标记器中不存在的标记。要向分词器添加分词,可以提供字符串列表或tokenizers.AddedTokens 列表。

    两种情况下的默认行为都是允许将新词用作子词。在我的示例中,如果我们将“director”和“cto”添加到分词器中,那么“director”可以分解为“dire”+“cto”+“r”(“dire”和“r”是原始分词器)。为避免这种情况,应使用:

    tokenizer.add_tokens([tokenizers.AddedToken(new_word, single_word = True) for new_word in new_words])
    

    我确实认为很多用户会简单地使用字符串列表(就像我一样,直到半小时前)。但这会导致您看到的问题。

    要在不损失太多模型性能的情况下为自定义分词器(如“emilyalsentzer/Bio_ClinicalBERT”)更改此设置,我建议从该分词器中提取词集,并将其与其基本分词器(例如“bert-基本无壳')。这将为您提供作为模型重新训练的一部分添加到基本分词器的单词集。然后获取基本分词器并使用 AddedToken 将这个新词添加到其中,并将 single_word 设置为 True。用这个新的分词器替换自定义分词器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-03
      • 2015-05-23
      • 2022-01-24
      • 1970-01-01
      • 2021-08-19
      • 2012-06-13
      • 2016-08-10
      • 2022-08-24
      相关资源
      最近更新 更多