-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathEvaluator.py
350 lines (249 loc) · 19.6 KB
/
Evaluator.py
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
import re
from LLM import ErnieLLM, OpenAILLM, Llama
def extract_dict(text):
pattern = r'\{[^}]*\}'
matches = re.findall(pattern, text)
if not matches:
raise ValueError("No dictionary found in the text.")
result_dict = eval(matches[0])
return result_dict
class Evaluator:
def __init__(self, model_series="ernie-bot-4"):
if model_series == "ernie-bot-4":
self.llm = ErnieLLM()
elif model_series == "gpt-3.5-turbo":
self.llm = OpenAILLM()
elif model_series == "llama-7b":
self.llm = Llama()
else:
self.llm = ErnieLLM()
def evaluate(self, question, answer_dict):
# 输入进来一个Dict,给一个相同Dict的输出,以百分制
evaluate_prompt = f"""
你是一个很有原则性的裁判,现在有一个问题已经被分发出去,并从不同的回答者那里收集来了回答,你需要对这些回答进行排序以及打分,并给出理由。
你之所以很有原则性,因为你会严格遵循你指定的评判标准:包括回答问题的准确性、推理过程是否清晰完整,在清晰完整的基础上是否简洁易懂、涉及专业知识的部分是否足够专业,是否有启发性。
以下是我们收来的问卷:
question: "{question}"
answer_dict是一个字典,key是回答者的策略,value是他们的回答。
answer_dict: {answer_dict}
请你按照以下格式返回你的评价:
{"{"}
"answer_dict里给出的某个key": [score,"reason"],
"answer_dict里给出的某个key": [score,"reason"],
...
{"}"}
score是一个[0,100]的自然数。显然,score在这里是需要递减的,你需要保证自我一致性。
"""
eval_result = extract_dict(self.llm.response(evaluate_prompt))
return eval_result
if __name__ == "__main__":
evaluator = Evaluator()
question0 = "有若干只鸡兔同在一个笼子里,从上面数,有35个头,从下面数,有94只脚。问笼中各有多少只鸡和兔?"
answer_dict0 = {
"zero-shot cot":
"""这是一个经典的数学问题,通常称为“鸡兔同笼”问题。在这个问题中,我们知道鸡有2只脚,兔子有4只脚。我们的目标是找出有多少只鸡和多少只兔子在笼子里。
首先,让我们定义两个变量:
* x 表示鸡的数量
* y 表示兔子的数量
根据题目,我们可以建立以下两个方程:
1. x + y = 35 (因为总共有35个头)
2. 2x + 4y = 94 (因为鸡有2只脚,兔子有4只脚,总共有94只脚)
现在我们要来解这个方程组,找出 x 和 y 的值。
我们可以从第一个方程中解出 x 或 y 的值,然后代入第二个方程中求解。这里我们选择从第一个方程中解出 x 的值:
x = 35 - y
然后我们将这个表达式代入第二个方程中:
2(35 - y) + 4y = 94
展开并简化这个方程:
70 - 2y + 4y = 94
2y = 24
y = 12
现在我们已经找到了 y 的值,我们可以将它代回到第一个方程中找出 x 的值:
x = 35 - 12
x = 23
所以,笼子里有23只鸡和12只兔子。""",
"few-shot contrastive":
"""这是一个经典的数学问题,通常被称为“鸡兔同笼”问题。为了解决这个问题,我们可以使用代数方法,并特别注意避免常见的数学错误。
首先,我们设鸡的数量为x,兔的数量为y。根据题目信息,我们可以建立以下两个方程:
1. x + y = 35(因为总共有35个头)
2. 2x + 4y = 94(因为鸡有2只脚,兔有4只脚,总共有94只脚)
接下来,我们将解这个方程组以找出x和y的值。为了解这个方程组,我们可以先将第一个方程变形为x = 35 - y,然后将这个表达式代入第二个方程中。这样可以消去x,得到一个只包含y的方程。
避免常见错误:
* 在代入过程中,确保正确地执行了代数运算,没有忽略运算顺序或错误地简化了表达式。
* 在解方程时,确保正确地应用了数学定理和公式,例如在使用代数方法解方程时遵循正确的步骤。
通过正确的代数运算,我们得到:
2(35 - y) + 4y = 94
70 - 2y + 4y = 94
2y = 24
y = 12
将y = 12代入第一个方程x + y = 35,解得x = 23。
所以,笼子里有23只鸡和12只兔。""",
"few-shot difficulty":
"""这是一个经典的数学问题,通常称为“鸡兔同笼”问题。在这个问题中,我们知道鸡有2只脚,兔子有4只脚。我们需要找出鸡和兔子的数量,使得它们的头数总和为35,脚数总和为94。
首先,我们定义两个变量:x表示鸡的数量,y表示兔子的数量。
根据题目信息,我们可以建立以下两个方程:
1. x + y = 35 (因为总共有35个头)
2. 2x + 4y = 94 (因为鸡有2只脚,兔子有4只脚,总共有94只脚)
接下来,我们需要解这个方程组来找出x和y的值。
我们可以从第一个方程中解出x:
x = 35 - y
然后将这个表达式代入第二个方程中:
2(35 - y) + 4y = 94
展开并简化这个方程:
70 - 2y + 4y = 94
2y = 24
y = 12
现在我们可以找到x的值:
x = 35 - y
x = 35 - 12
x = 23
所以,笼子里有23只鸡和12只兔子。"""
}
question1 = "编程解决汉诺塔问题。"
answer_dict1 = {
"origin prompt":
"""当然,我可以帮助你理解如何使用编程解决汉诺塔问题。以下是一个使用Python编程语言实现的递归解决方案:
```python
def hanoi(n, source, target, auxiliary):
if n > 0:
# Move n - 1 disks from source to auxiliary, so they are out of the way
hanoi(n - 1, source, auxiliary, target)
# Move the nth disk from source to target
print('Move disk %s from %s to %s' % (n, source, target))
# Move the n - 1 disks that we left on auxiliary to target
hanoi(n - 1, auxiliary, target, source)
# initiate call from source A to target C with auxiliary B
hanoi(3, 'A', 'C', 'B')
```
在这个代码中,`hanoi`函数是递归的。它首先检查是否还有磁盘需要移动(`n > 0`)。如果有,它将执行以下步骤:
1. 将n-1个磁盘从源塔座移动到辅助塔座,以便它们不会妨碍我们。这是通过递归调用`hanoi`函数并减少要移动的磁盘数量来实现的。
2. 将第n个磁盘从源塔座移动到目标塔座。这是通过打印一条消息来模拟的,该消息指示磁盘的移动。
3. 将我们留在辅助塔座上的n-1个磁盘移动到目标塔座。这再次通过递归调用`hanoi`函数并减少要移动的磁盘数量来实现。
最后一行是函数的初始调用,指示将3个磁盘从源塔座A移动到目标塔座C,使用B作为辅助塔座。""",
"zero-shot cot":
"""编程解决汉诺塔问题是一个经典的递归问题。我会通过以下步骤来解决这个问题:
### 1. 问题理解
汉诺塔问题描述如下:有三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子C上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方的情况。
### 2. 选择方法
汉诺塔问题的解决方案需要使用递归。我将设计一个函数,名为 `hanoi`,它接受三个参数:盘子的数量 `n`,起始柱子 `start`,目标柱子 `end`,以及一个临时柱子 `temp`。
### 3. 实现过程
以下是我用Python实现汉诺塔问题的代码:
```python
def hanoi(n, start, end, temp):
if n == 1: # 基本情况:如果只有一个盘子,直接移动到目标柱子
print(f"Move disk 1 from {start} to {end}")
else: # 递归情况:先移动n-1个盘子到临时柱子,然后移动剩下的盘子到目标柱子,最后把n-1个盘子从临时柱子移动到目标柱子
hanoi(n - 1, start, temp, end) # 把n-1个盘子从起始柱子移动到临时柱子
print(f"Move disk {n} from {start} to {end}") # 把第n个盘子从起始柱子移动到目标柱子
hanoi(n - 1, temp, end, start) # 把n-1个盘子从临时柱子移动到目标柱子
# 测试函数
hanoi(3, 'A', 'C', 'B')
```
### 4. 优化和测试代码
对于汉诺塔问题,上述递归解法的时间复杂度是O(2^n),这是因为每个盘子都有两种可能的移动方式(除了最小的盘子)。在实际情况下,由于这个问题是一个典型的递归问题,时间复杂度的优化空间并不大。上述代码已经是相对优化的解决方案了。
对于测试,你可以通过改变 `hanoi` 函数的参数来测试不同数量的盘子。例如,`hanoi(4, 'A', 'C', 'B')` 将测试4个盘子的情况。请注意,由于这个问题的时间复杂度是指数级的,所以当盘子的数量增加时,计算时间将急剧增加。""",
"zero-shot contrastive":
"""汉诺塔问题是一个经典的递归问题,其解决方案可以通过以下步骤进行分析和设计:
1. 算法复杂性:
汉诺塔问题的解决方案涉及到递归算法,时间复杂度为O(2^n),其中n为塔中盘子的数量。对于大规模数据,这种指数级的时间复杂度可能导致算法运行效率低下。为了优化算法,可以考虑使用迭代或记忆化搜索等方法来避免重复计算,从而提高算法效率。
2. 代码可读性:
在编写汉诺塔问题的解决方案时,需要注重代码的可读性。可以使用有意义的变量名和函数名,以及注释来说明代码的逻辑和功能。此外,可以采用模块化设计的思想,将代码拆分成多个函数或模块,每个函数或模块只负责单一的功能,从而使代码更加清晰易懂。
3. 系统安全性:
在编写代码时,需要考虑潜在的安全漏洞和攻击。特别是在处理用户输入时,需要进行严格的输入验证和过滤,以防止恶意用户通过输入非法数据来攻击系统。此外,需要确保代码的稳定性和可靠性,避免出现内存泄漏、空指针引用等问题。
4. 跨平台兼容性:
为了确保代码能够在不同的操作系统和硬件平台上稳定运行,需要采用跨平台的编程语言和工具,例如C++、Java等。在编写代码时,需要遵循标准规范,避免使用特定平台的特性和API。此外,需要进行充分的测试和验证,以确保代码在各种环境下的正确性和稳定性。
综上所述,针对汉诺塔问题的解决方案需要关注算法复杂性、代码可读性、系统安全性和跨平台兼容性等难点,并采取相应的解决方案和建议来提高代码质量和效率。""",
"zero-shot difficulty":
"""当然,我很乐意为你解释冒泡排序的设计思路,并为你提供汉诺塔问题的解决方案。
一、冒泡排序
冒泡排序是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
设计思路如下:
1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3. 针对所有的元素重复以上的步骤,除了最后一个。
4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
以下是一个 Python 版本的冒泡排序实现:
```python
def bubble_sort(arr):
n = len(arr)
for i in range(n):
# 建立一个布尔值,我们将在内层循环中设置它
swapped = False
for j in range(0, n-i-1):
# 交换元素,如果当前元素大于下一个元素
if arr[j] > arr[j+1] :
arr[j], arr[j+1] = arr[j+1], arr[j]
swapped = True
# 如果在内循环中没有交换,那么列表已经排序,我们可以直接退出
if not swapped:
break
return arr
```
二、汉诺塔问题
汉诺塔问题是一个经典的递归问题。汉诺塔由三根杆子A、B、C组成。A杆上有N个(N>1)穿孔圆盘,盘的大小由上到下递增。要求按下列规则将所有圆盘移至C杆:每次只能移动一个圆盘;大盘不能叠放在小盘上面;三步内将A杆上的圆盘全部移到C杆上。
设计思路如下:
1. 把n-1个盘子由A 移到B;
2. 把第n个盘子由 A移到 C;
3. 把n-1个盘子由B 移到C。
以下是一个 Python 版本的汉诺塔问题解决方案:
```python
def hanoi(n, source, target, auxiliary):
if n > 0:
# 移动n - 1个盘子到辅助柱子上
hanoi(n - 1, source, auxiliary, target)
# 移动剩下的盘子到目标柱子上
print('Move disk from {} to {}'.format(source, target))
# 移动n - 1个盘子从辅助柱子到目标柱子上
hanoi(n - 1, auxiliary, target, source)
# 测试代码,将3个盘子从A移动到C
hanoi(3, 'A', 'C', 'B')
```""",
"few-shot cot":
"""汉诺塔问题是一个经典的递归问题。在这个问题中,我们有三根柱子和一堆大小不同的盘子,开始时所有盘子按大小顺序叠放在第一根柱子上,我们的任务是将这些盘子移动到第三根柱子上,移动过程中每次只能移动一个盘子,并且任何时候都不能将一个较大的盘子放在较小的盘子上面。
""",
"few-shot contrastive":
"""汉诺塔问题是一个经典的递归问题,它要求我们将一堆大小不同的盘子从一个塔座移动到另一个塔座,期间要遵循以下规则:
1. 一次只能移动一个盘子。
2. 任何时候,一个大盘子不能放在一个小盘子上面。
解决汉诺塔问题通常包括以下几个步骤:
**分析问题**
首先,我们需要理解问题的基本要求,也就是将一堆盘子从一个塔座移动到另一个塔座,并遵守上述规则。理解这些规则如何限制我们的移动是非常重要的。
**设计解决方案**
解决汉诺塔问题的关键是使用递归。递归是一种编程技术,它涉及到一个函数调用自身的能力。对于汉诺塔问题,我们可以将问题分解为更小的子问题,并使用递归来解决它们。
如果我们有n个盘子需要移动,那么我们可以这样设计我们的递归函数:
1. 首先,我们移动n-1个盘子到辅助塔座(这是一个递归调用)。
2. 然后,我们移动剩下的一个大盘子到目标塔座。
3. 最后,我们再移动n-1个盘子从辅助塔座到目标塔座(这是另一个递归调用)。
这个过程可以持续进行,直到我们只剩下一个盘子需要移动。这就是递归的基本情况。
**实施解决方案**
在编程实现这个解决方案时,我们需要确保每一步都被正确地执行。这通常涉及到对代码进行详细的测试和调试,以确保它能够正确地处理各种情况。
在Python中,实现汉诺塔问题的代码可能如下所示:
```python
def hanoi(n, source, target, auxiliary):
if n > 0:
# Move n - 1 disks from source to auxiliary, so they are out of the way
hanoi(n - 1, source, auxiliary, target)
# Move the nth disk from source to target
print('Move disk %s from %s to %s' % (n, source, target))
# Move the n - 1 disks that we left on auxiliary to target
hanoi(n - 1, auxiliary, target, source)
```
这个函数表示将n个盘子从source移动到target,auxiliary作为辅助塔座。这个函数首先检查是否有盘子需要移动(n > 0)。如果有,那么它首先移动n-1个盘子到辅助塔座,然后移动剩下的一个盘子到目标塔座,最后再将n-1个盘子从辅助塔座移动到目标塔座。
**确保代码的质量和性能**
对于任何编写的代码,质量和性能都是非常重要的。为了确保代码的质量,我们需要进行详细的测试,包括单元测试、集成测试和系统测试。这些测试可以帮助我们发现并修复代码中的任何错误或缺陷。此外,我们还可以使用代码审查和其他质量保证技术来提高代码的质量。
在性能方面,我们需要确保我们的代码是高效的,并且能够有效地处理大量的数据。对于汉诺塔问题,递归算法的时间复杂性是O(2^n),这是因为对于每个盘子,我们都需要移动所有其他的盘子两次(一次移到辅助塔座,一次移到目标塔座)。尽管这种方法的时间复杂性较高,但由于汉诺塔问题的性质,目前还没有找到更有效的解决方案。因此,虽然这种方法可能在处理大量盘子时会变得非常慢,但对于较小的问题规模,它通常是有效的。""",
"few-shot difficulty":
"""汉诺塔问题是一个经典的递归问题,要求将一堆大小不一、从小到大堆叠在一起的盘子从一个柱子移动到另一个柱子,期间需要借助第三个柱子,并且每次只能移动最上面的一个盘子,同时要保证在移动过程中始终保持小盘子在大盘子的上面。
为了避免在编程过程中遇到上述常见错误,我采用以下设计思路来解决汉诺塔问题:
1. 定义问题边界和递归结束条件:首先,我们需要明确问题的边界条件,即当只有一个盘子时,直接将其从起始柱子移动到目标柱子即可。这是递归的结束条件。
2. 分解问题:对于包含多个盘子的汉诺塔问题,我们可以将其分解为两个子问题。首先,将上面的n-1个盘子从起始柱子通过目标柱子移动到辅助柱子;然后,将最底下的一个盘子从起始柱子移动到目标柱子;最后,再将n-1个盘子从辅助柱子通过起始柱子移动到目标柱子。这样,我们就将一个大规模的问题分解为若干个规模较小的问题。
3. 编写递归函数:根据以上分析,我们可以编写一个递归函数来解决汉诺塔问题。该函数接受四个参数:盘子的数量n、起始柱子、目标柱子和辅助柱子。在函数内部,我们根据问题的边界条件和分解后的子问题来调用自身,从而实现递归求解。
4. 添加错误处理和日志输出:为了避免程序在特定情况下崩溃并方便调试,我们可以在代码中添加错误处理和日志输出功能。例如,我们可以检查输入的盘子数量是否为正整数,如果不是则抛出异常;同时,在每次移动盘子时输出相应的日志信息,以便跟踪程序的执行过程。
5. 优化代码性能:虽然过度优化代码可能导致代码难以理解和维护,但在某些情况下,适当的优化可以提高程序的执行效率。对于汉诺塔问题,我们可以通过减少递归调用的次数来优化性能。例如,当盘子数量为偶数时,我们可以先将一半盘子从起始柱子移动到辅助柱子,然后再将剩余的一半盘子从起始柱子移动到目标柱子,最后再将辅助柱子上的盘子移动到目标柱子上。这样可以减少一次递归调用,提高程序的执行效率。
通过以上设计思路,我们可以避免常见的编程错误,编写出高质量的汉诺塔问题解决方案。同时,我们还需要不断学习和探索新的编程技巧和方法,以应对各种复杂和多变的问题挑战。""",
}
del answer_dict1["few-shot contrastive"]
del answer_dict1["zero-shot contrastive"]
del answer_dict1["zero-shot difficulty"]
del answer_dict1["few-shot cot"]
result = evaluator.evaluate(question1, answer_dict1)
print(result)