SkyworkAI / Skywork

Skywork series models are pre-trained on 3.2TB of high-quality multilingual (mainly Chinese and English) and code data. We have open-sourced the model, training data, evaluation data, evaluation methods, etc. 天工系列模型在3.2TB高质量多语言和代码数据上进行预训练。我们开源了模型参数,训练数据,评估数据,评估方法。
Other
1.21k stars 111 forks source link

BUG:ceval, cmmlu和mmlu中选项ABCD的概率计算错误 #57

Open naturesphere opened 9 months ago

naturesphere commented 9 months ago

Skywork/eval/文件夹下的evaluate_ceval.py, evaluate_cmmlu.py和evaluate_mmlu.py文件中,获取选项ABCD的概率的关键代码如下:

    softval = torch.nn.functional.softmax(
        torch.tensor(
            [
                logits[tokenizer("A")["input_ids"][-1]],
                logits[tokenizer("B")["input_ids"][-1]],
                logits[tokenizer("C")["input_ids"][-1]],
                logits[tokenizer("D")["input_ids"][-1]],
            ]
        ),
        dim=0,
    )

以选项A为例: tokenizer("A")会把“A“认为是一个句子,在”A“前面拼接句子开始标志”_“。因此tokenizer实际上转化的字符为“\<s> _A”,得到input_ids=[1, 319]。代码中tokenizer("A")["input_ids"][-1]取得的id是319,对应的字符为“_A”,而真正“A”字符对应的id是:

tokenizer.convert_tokens_to_ids('A')=29909.

BCD选项也存在同样的问题。

评估时的一个full_prompt的例子格式如下:

以下是关于农学的单项选择题,请直接给出正确答案的选项。

题目:肉牛屠宰后,胴体的哪个部位肉质较好 A. 胸 B. 腹 C. 大腿 D. 小腿 答案:C

……

题目:羊胴体中,肉质较好的部位是 A. 胸下肉 B. 肩胛肉 C. 后腿肉 D. 小腿肉 答案:C

以下是关于农学的单项选择题,请直接给出正确答案的选项。

题目:在农业生产中被当作极其重要的劳动对象发挥作用,最主要的不可替代的基本生产资料是 A. 农业生产工具 B. 土地 C. 劳动力 D. 资金 答案:

根据full_prompt例子的格式,选项应该填在“答案:”后面,不应该另起一行。 因此选择ABCD选项的id时,应该取“A”“B”“C”"D"字符的概率,而不是“_A”,"_B","_C","_D"字符的概率。

TianwenWei commented 9 months ago

我们的实现是正确的。孤立的“A”在BPE中指的是非词首的“A”,例如"helloA"里面的A。词首"A",即"_A"才是选项对应。

naturesphere commented 9 months ago

我们的实现是正确的。孤立的“A”在BPE中指的是非词首的“A”,例如"helloA"里面的A。词首"A",即"_A"才是选项对应。

prompt给的例子里,例如“答案:C”里,冒号和C之间没有空格,是直接连着的。从构造prompt的代码中能看到:

def format_example(line, subject, include_answer=True):
    example = f"以下是中国关于{task2desc[subject]}考试的单项选择题,请选出其中的正确答案。\n\n"
    example = example + line["question"]
    for choice in choices:
        example += f'\n{choice}. {line[f"{choice}"]}'

    if include_answer:
        example += "\n答案:" + line["answer"] + "\n\n"
    else:
        example += "\n答案:"
    return example

example += "\n答案:" + line["answer"] + "\n\n" 这里,“答案:”后面直接接选项。 以“答案:A”为例:

tokenizer("答案:A")['input_ids'] = [1, 29871, 40738, 30383, 29909]

A对应的编码是29909。如果在冒号和A之间加入空格:

tokenizer("答案: A")['input_ids']=[1, 29871, 40738, 30383, 319]

此时,A对应的编码是319,即字符'▁A'对应的编码。

liuyijiang1994 commented 8 months ago

我们的实现是正确的。孤立的“A”在BPE中指的是非词首的“A”,例如"helloA"里面的A。词首"A",即"_A"才是选项对应。

prompt给的例子里,例如“答案:C”里,冒号和C之间没有空格,是直接连着的。从构造prompt的代码中能看到:

def format_example(line, subject, include_answer=True):
    example = f"以下是中国关于{task2desc[subject]}考试的单项选择题,请选出其中的正确答案。\n\n"
    example = example + line["question"]
    for choice in choices:
        example += f'\n{choice}. {line[f"{choice}"]}'

    if include_answer:
        example += "\n答案:" + line["answer"] + "\n\n"
    else:
        example += "\n答案:"
    return example

example += "\n答案:" + line["answer"] + "\n\n" 这里,“答案:”后面直接接选项。 以“答案:A”为例:

tokenizer("答案:A")['input_ids'] = [1, 29871, 40738, 30383, 29909]

A对应的编码是29909。如果在冒号和A之间加入空格:

tokenizer("答案: A")['input_ids']=[1, 29871, 40738, 30383, 319]

此时,A对应的编码是319,即字符'▁A'对应的编码。

他们也许是想直接输出句首A作为答案,而不是答案字符串中的A.