[ggerganov/llama.cpp]修复非特殊添加令牌的去令牌化

2024-03-22 537 views
0

检查过

.\build\bin\Release\main.exe -m models\mpt-7B-storywriter\ggml-model-f16.gguf -p "Once upon a time  there" --temp 0 -n 32

前:

Once upon a timethere

后:

Once upon a time  there

clang还修复了on的构建问题Windows(CI 未检测到?)。由于重复的符号错误,我们无法在此处链接 和clipcommon

回答

8

我用 #3586 测试了它,当传递` (replaced with__`) 作为提示的一部分时它就可以工作,谢谢!

最好的音乐是__让你感觉它是为你而写的,仅此而已。有一种特殊的方式来聆听这种音乐:就像朋友可能会一遍又一遍地播放他们最喜欢的乐队的旧唱片,直到律动被磨损成小疙瘩。或者有人可能会讲述一个他们从未告诉过其他人的故事,讲述他们是如何遇到那个人的

3

检查时

    std::vector<llama_token> tokens = llama_tokenize(ctx, "[PAD50277]", false, true);

mpt解析为令牌 50277 。这在我看来是错误的,我也应该尝试在 PR 中修复这个问题吗?

我检查了 HF 分词器的行为

# tests with HF tokenizer

import argparse

from transformers import AutoTokenizer

parser = argparse.ArgumentParser()
parser.add_argument("dir_tokenizer", help="directory containing 'tokenizer.model' file")
args = parser.parse_args()

dir_tokenizer = args.dir_tokenizer

tokenizer = AutoTokenizer.from_pretrained(dir_tokenizer)

added_vocab = tokenizer.get_added_vocab()
reverse_vocab = {id: encoded_tok for encoded_tok, id in tokenizer.vocab.items()}

with open("result.txt", "w", encoding="utf-8") as file:
    for i in range(tokenizer.vocab_size + len([id for id in added_vocab.values() if id >= tokenizer.vocab_size]) + 1):
        s = tokenizer.decode([i])
        e = tokenizer.encode(s)
        if i not in reverse_vocab:
            file.write(f"pad {i} {s} {e}\n")
        elif reverse_vocab[i] in added_vocab:
            if tokenizer.added_tokens_decoder[i].special:
                file.write(f"special {i} {s} {e}\n")
            else:
                file.write(f"added {i} {s} {e}\n")
        # else:
        #     file.write(f"normal {i} {s} {e}\n")

并获得llama-2-7B

special 0 <unk> [1, 0]
special 1 <s> [1, 1]
special 2 </s> [1, 2]
pad 32000  [1]

并为mpt

special 0 <|endoftext|> [0]
special 1 <|padding|> [1]
added 50254                          [50254]
added 50255                         [50255]
added 50256                        [50256]
added 50257                       [50257]
added 50258                      [50258]
added 50259                     [50259]
added 50260                    [50260]
added 50261                   [50261]
added 50262                  [50262]
added 50263                 [50263]
added 50264                [50264]
added 50265               [50265]
added 50266              [50266]
added 50267             [50267]
added 50268            [50268]
added 50269           [50269]
added 50270          [50270]
added 50271         [50271]
added 50272        [50272]
added 50273       [50273]
added 50274      [50274]
added 50275     [50275]
added 50276    [50276]
pad 50277  []

并为causalml

special 151643 <|endoftext|> [151643]
special 151644 <|im_start|> [151644]
special 151645 <|im_end|> [151645]
pad 151851  []

stablelm(stablelm-3b-4e1t)

^special 0 <|endoftext|> [0]
special 1 <|padding|> [1]
added 50254                          [50254]
added 50255                         [50255]
added 50256                        [50256]
added 50257                       [50257]
added 50258                      [50258]
added 50259                     [50259]
added 50260                    [50260]
added 50261                   [50261]
added 50262                  [50262]
added 50263                 [50263]
added 50264                [50264]
added 50265               [50265]
added 50266              [50266]
added 50267             [50267]
added 50268            [50268]
added 50269           [50269]
added 50270          [50270]
added 50271         [50271]
added 50272        [50272]
added 50273       [50273]
added 50274      [50274]
added 50275     [50275]
added 50276    [50276]
pad 50277  []

这对我来说表明我们的特殊令牌处理(CONTROL)是不同的,并且我们对填充令牌的处理可能存在问题。

0

根据我对情况的理解引用评论,我看到了几个选项:

  • 与 HF 分词器的兼容性更强(对于我们的分词器来说可能相当容易,但有一些风险会破坏堆栈的其余部分?)
  • 不执行任何操作(对于添加的特殊标记和填充标记,标记分类可能存在问题)
  • 修复添加特殊标记被分类为 USER_DEFINED 的问题(bug?)(有些风险会破坏堆栈的其余部分)
  • 修复填充令牌被分类为 USER_DEFINED 的问题(令牌类型应该是什么:未使用?)

我需要一些关于如何最好地进行的指导。

8

我认为情况并非如此,尽管不确定它的起源。

通常,Python HF 模型发布时带有张量和比实际词汇量更大的词汇量。例如,与张量大小和指定的实际 vocab_size 相比,OpenAssistant (Falcon) 有 12 个缺失标记。

我相信正确的方法是生成 PAD 代币,它们无论如何都无法生成,因为它是一个占位符。最初它可能也是一个用于持续微调的占位符,不太可能被使用。

9

据我了解,HF 变压器不需要词汇表中的这些 PAD 标记,那么为什么不让事情变得简单并尝试尽可能接近它们的行为呢?我们试图让分词器忽略这些标记,因为它们实际上并不存在。我们可以使用 UNUSED 令牌类型,但生成占位符似乎是不必要的努力,这样它们就可以被实现完全忽略。

1

是的,如果有一个修复程序意味着不需要这些额外的令牌,那就太棒了。最近,CausalLM 发布,将 config.vocab_size 与提供的词汇进行比较,发现缺失了多达 213 个标记。

到目前为止,我的“修复”方法是将一堆<dummyXXX>令牌添加到added_tokens.json。Kerfluffle 最近通过 --padvocab 提供了一个官方实现,我对此表示赞赏,尽管它显然仍然不是一个非常优雅的解决方案。

我同意 @cebtenzzre 的观点,如果 llama.cpp 能够像 Transformer 一样忽略这些“幻影”标记,那就太好了。

至于模型为什么这样做——在某些情况下,这是因为它们将词汇大小四舍五入到倍数,以便张量并行性发挥作用。例如,词汇表大小为 32032 或 32128 的模型 - 它们的实际词汇表可能是 32003,但这会破坏 TP,因此它们会四舍五入到下一个最接近的倍数。但这并不是 OpenAsistant 这样做的原因。

7

你是对的,忽略不匹配是解决问题的更好方法。但我猜想转换后的模型中存储的词汇大小实际上应该反映真实的词汇大小,而不是继续错误的词汇大小。

基本上,HF 配置方面的错误行为是声称错误的词汇量大小,最有可能满足转换器库中的某些内容,然后依次忽略错误的数字或在内部生成 PAD。

添加 PAD 令牌是/曾经是错误配置的快速修复方法,因此要么添加 PAD 令牌,要么纠正错误的大小数字并依赖张量大小进行计算。它可能需要修复所有依赖词汇大小的评估例程和检查。