NLP(七十八)大模型探索:MMLU数据集评测

本文将会介绍如何使用大模型(LLM)对MMLU数据集进行评测。

大模型(LLM)的评测是衡量大模型效果的关键步骤,也是模型流水线中必不可少的过程。常见的大模型排行榜或平台有🤗 Open LLM LeaderboardOpenCompassChatbot Arena Leaderboard.

那么,大模型的评测是如何实现的呢?

本文将会以MMLU数据集为例,考察主流开源大模型,如LLAMA-2, BaiChuan-2等模型的评估实现及结果,希望能管中规豹,一探究竟。

MMLU数据集

MMLU数据集已开源至Github平台,访问网址为:https://github.com/hendrycks/test .

MMLU(Massive Multitask Language Understanding)是一个新的基准,用于衡量在零样本(zero-shot)和少样本(few-shot)情形下,大模型在预训练期间获得的世界知识。这使得该基准测试更具挑战性,也更类似于我们评估人类的方式。该基准涵盖 STEM、人文(humanities)、社会科学(social sciences)等领域的 57 个学科(subject)。 它的难度从初级到高级,既考验世界知识,又考验解决问题的能力。 学科范围从数学和历史等传统领域到法律和伦理等更为专业的领域。学科的粒度和广度使该基准成为识别模型盲点的理想选择。

MMLU数据集共收集了15908个问题,并将其分为few-shot开发集、验证集和测试集。 few-shot开发集每个学科有5个问题,验证集可用于选择超参数,由1540个问题组成,测试集有14079个问题。 每个学科至少包含100个测试问题,这比大多数旨在评估人类的考试都要长。

我们来看其中一个示例:

1
2
3
4
5
6
7
8
9
Question: Glucose is transported into the muscle cell:

Choices:
A. via protein transporters called GLUT4.
B. only in the presence of insulin.
C. via hexokinase.
D. via monocarbylic acid transporters.

Correct answer: A

评测代码

对于MMLU数据集的评测,MMLU评测官方方案为:

判断 LLM 后续 token 为 A, B, C 或 D 的概率,只要生成token为A的概率在四个token中概率最大,则回答正确。该方案明显的弊端就是,即便ABCD 中,生成A的概率最大,在实际解码过程中,LLM 实际生成的也不一定是 token A。

如下图所示:

MMLU方案评测

为了使得在MMLU数据集上的评测结果更可靠,我们选择官方方案。同时,为了兼容更多大模型以及保存模型评测结果、推理时间等,我们参考Github上的项目ollmer/mmlu中的代码,并稍作调整,Python代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# -*- encoding: utf-8 -*-
import argparse
import json
import os
import time

import numpy as np
import pandas as pd
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

from categories import categories, subcategories

choices = ["A", "B", "C", "D"]


def format_subject(subject):
l = subject.split("_")
s = ""
for entry in l:
s += " " + entry
return s


def format_example(df, idx, include_answer=True):
prompt = df.iloc[idx, 0]
k = df.shape[1] - 2
for j in range(k):
prompt += "\n{}. {}".format(choices[j], df.iloc[idx, j + 1])
prompt += "\nAnswer:"
if include_answer:
prompt += " {}\n\n".format(df.iloc[idx, k + 1])
return prompt


def gen_prompt(train_df, subject, k=-1):
prompt = "The following are multiple choice questions (with answers) about {}.\n\n".format(
format_subject(subject)
)
if k == -1:
k = train_df.shape[0]
for i in range(k):
prompt += format_example(train_df, i)
return prompt


@torch.no_grad()
def eval(args, subject, model, tokenizer, dev_df, test_df):
cors = []
all_probs = []
answers = choices[: test_df.shape[1] - 2]

for i in range(test_df.shape[0]):
# get prompt and make sure it fits
k = args.ntrain
prompt_end = format_example(test_df, i, include_answer=False)
train_prompt = gen_prompt(dev_df, subject, k)
prompt = train_prompt + prompt_end

input_ids = tokenizer(prompt, return_tensors="pt").input_ids.to(model.device)

while input_ids.shape[-1] > 2048:
k -= 1
train_prompt = gen_prompt(dev_df, subject, k)
prompt = train_prompt + prompt_end
input_ids = tokenizer(prompt, return_tensors="pt").input_ids.to(
model.device
)

label = test_df.iloc[i, test_df.shape[1] - 1]

logits = model(input_ids=input_ids).logits[0, -1]

probs = (
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]],
]
).float(),
dim=0,
)
.detach()
.cpu()
.numpy()
)
pred = {0: "A", 1: "B", 2: "C", 3: "D"}[np.argmax(probs)]

cor = pred == label
cors.append(cor)
all_probs.append(probs)

acc = np.mean(cors)
cors = np.array(cors)

all_probs = np.array(all_probs)
print("Average accuracy {:.3f} - {}".format(acc, subject))

return cors, acc, all_probs


def main(args):
model = AutoModelForCausalLM.from_pretrained(
args.model,
torch_dtype=torch.float16,
load_in_8bit=False,
low_cpu_mem_usage=True,
device_map="auto",
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained(args.model, trust_remote_code=True)
model.eval()
subjects = sorted(
[
f.split("_test.csv")[0]
for f in os.listdir(os.path.join(args.data_dir, "test"))
if "_test.csv" in f
]
)

if not os.path.exists(args.save_dir):
os.makedirs(args.save_dir)
if not os.path.exists(os.path.join(args.save_dir, "results_{}".format(args.model.split("/")[-1]))):
os.makedirs(os.path.join(args.save_dir, "results_{}".format(args.model.split("/")[-1])))

all_cors = []
subcat_cors = {
subcat: [] for subcat_lists in subcategories.values() for subcat in subcat_lists
}
cat_cors = {cat: [] for cat in categories}

start_time = time.time()
for subject in subjects:
dev_df = pd.read_csv(
os.path.join(args.data_dir, "dev", subject + "_dev.csv"), header=None
)[: args.ntrain]
test_df = pd.read_csv(
os.path.join(args.data_dir, "test", subject + "_test.csv"), header=None
)

cors, acc, probs = eval(args, subject, model, tokenizer, dev_df, test_df)
subcats = subcategories[subject]
for subcat in subcats:
subcat_cors[subcat].append(cors)
for key in categories.keys():
if subcat in categories[key]:
cat_cors[key].append(cors)
all_cors.append(cors)

test_df["{}_correct".format(args.model)] = cors
for j in range(probs.shape[1]):
choice = choices[j]
test_df["{}_choice{}_probs".format(args.model, choice)] = probs[:, j]
test_df.to_csv(
os.path.join(
args.save_dir, "results_{}".format(args.model.split("/")[-1]), "{}.csv".format(subject)
),
index=None,
)

results = {"subcategories": {}, "categories": {}}
for subcat in subcat_cors:
subcat_acc = np.mean(np.concatenate(subcat_cors[subcat]))
results["subcategories"][subcat] = subcat_acc
print("Average accuracy {:.3f} - {}".format(subcat_acc, subcat))

for cat in cat_cors:
cat_acc = np.mean(np.concatenate(cat_cors[cat]))
results["categories"][cat] = cat_acc
print("Average accuracy {:.3f} - {}".format(cat_acc, cat))
weighted_acc = np.mean(np.concatenate(all_cors))
results["weighted_accuracy"] = weighted_acc
print("Average accuracy: {:.3f}".format(weighted_acc))

results_file = os.path.join(
args.save_dir, "accuracies_{}.json".format(args.model.replace("/", "_"))
)
end_time = time.time()
results["cost_time"] = end_time - start_time
with open(results_file, "w") as f:
f.write(json.dumps(results, indent=4))


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--ntrain", "-k", type=int, default=5)
parser.add_argument("--data_dir", "-d", type=str, default="data")
parser.add_argument("--save_dir", "-s", type=str, default="results")
parser.add_argument("--model", "-m", type=str)
args = parser.parse_args()
main(args)

运行方式如下:

1
python3 evaluate_hf.py -m /workspace/models/llama2-7b-hf

本项目已开源至Github平台,访问网址为:https://github.com/percent4/llm_evaluation_4_mmlu.

评测实验

笔者在A100 80G的GPU上对各类大模型进行评测,它们在MMLU数据集上的表现结果如下:

  • LLAMA系列模型
模型尺寸 Average weighed accuracy STEM humanities social sciences other Inference Time(s)
llama-2-7b 0.4596 0.3704 0.4338 0.5184 0.5244 2468.45
llama-2-13b 0.5568 0.4427 0.5443 0.6341 0.6076 4123.48
llama-2-70b 0.6911 0.5779 0.6516 0.8044 0.7461 14961.76
  • 其它模型
模型 Average weighed accuracy STEM humanities social sciences other Inference Time(s)
Baichuan-7B 0.4261 0.3615 0.3853 0.4927 0.4821 3620.22
Baichuan2-7B-Base 0.5431 0.4463 0.5133 0.6126 0.6104 3742.95
Baichuan2-13B-Base 0.5901 0.4954 0.5505 0.6783 0.6521 3460.3
internlm-20b 0.6063 0.5113 0.5615 0.7033 0.6675 5709.98
Mistral-7B-v0.1 0.6267 0.5268 0.5658 0.7364 0.7039 2517.74
大模型在MMLU数据集上的评测结果
大模型在MMLU数据集上的各学科评测结果

总结

本文将会介绍如何使用大模型(LLM)对MMLU数据集进行评测。

本文中的项目代码已开源至Github平台,访问网址为:https://github.com/percent4/llm_evaluation_4_mmlu.

欢迎关注我的公众号NLP奇幻之旅,原创技术文章第一时间推送。

欢迎关注我的知识星球“自然语言处理奇幻之旅”,笔者正在努力构建自己的技术社区。


NLP(七十八)大模型探索:MMLU数据集评测
https://percent4.github.io/NLP(七十八)大模型探索:MMLU数据集评测/
作者
Jclian91
发布于
2024年1月10日
许可协议