Skip to content

Commit b34230e

Browse files
authored
Merge pull request #151 from 1041206149/LLM_math
feat: add math formula comparison (LLMMathCompare)
2 parents 69dadcf + 15e60fb commit b34230e

File tree

4 files changed

+250
-0
lines changed

4 files changed

+250
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import json
2+
import re
3+
from typing import List
4+
5+
from dingo.io import Data
6+
from dingo.model import Model
7+
from dingo.model.llm.base_openai import BaseOpenAI
8+
from dingo.model.modelres import ModelRes
9+
from dingo.model.prompt.prompt_math_compare import PromptMathCompare
10+
from dingo.utils import log
11+
from dingo.utils.exception import ConvertJsonError
12+
13+
14+
@Model.llm_register('LLMMathCompare')
15+
class LLMMathCompare(BaseOpenAI):
16+
"""
17+
专注于数学公式抽取效果的对比
18+
"""
19+
prompt = PromptMathCompare
20+
21+
@classmethod
22+
def build_messages(cls, input_data: Data) -> List:
23+
messages = [
24+
{
25+
'role': 'user',
26+
'content': cls.prompt.content.format(
27+
input_data.content,
28+
input_data.raw_data.get('webkit_extract_md', ''),
29+
input_data.raw_data.get('megamath_md', ''),
30+
),
31+
}
32+
]
33+
return messages
34+
35+
@classmethod
36+
def process_response(cls, response: str) -> ModelRes:
37+
log.info(response)
38+
39+
# 提取思考内容和清理响应
40+
response_think = cls._extract_think_content(response)
41+
response = cls._clean_response(response)
42+
43+
try:
44+
response_json = json.loads(response)
45+
if response_think and 'reason' in response_json:
46+
response_json['reason'] += '\n' + response_think
47+
elif response_think:
48+
response_json['reason'] = response_think
49+
except json.JSONDecodeError:
50+
raise ConvertJsonError(f'Convert to JSON format failed: {response}')
51+
52+
# 处理特殊情况:没有数学公式
53+
if response_json.get('no_formula'):
54+
return cls._create_no_formula_result(response_json)
55+
56+
# 处理正常情况
57+
return cls._create_normal_result(response_json)
58+
59+
@staticmethod
60+
def _extract_think_content(response: str) -> str:
61+
if response.startswith('<think>'):
62+
think_content = re.search(r'<think>(.*?)</think>', response, flags=re.DOTALL)
63+
return think_content.group(1).strip() if think_content else ''
64+
return ''
65+
66+
@staticmethod
67+
def _clean_response(response: str) -> str:
68+
response = re.sub(r'<think>.*?</think>', '', response, flags=re.DOTALL).strip()
69+
70+
if response.startswith('```json'):
71+
response = response[7:]
72+
elif response.startswith('```'):
73+
response = response[3:]
74+
75+
if response.endswith('```'):
76+
response = response[:-3]
77+
78+
return response
79+
80+
@staticmethod
81+
def _create_no_formula_result(response_json: dict) -> ModelRes:
82+
result = ModelRes()
83+
result.error_status = False
84+
result.type = 'NO_FORMULA'
85+
result.name = 'math'
86+
result.reason = [json.dumps(response_json, ensure_ascii=False)]
87+
return result
88+
89+
@staticmethod
90+
def _create_normal_result(response_json: dict) -> ModelRes:
91+
result = ModelRes()
92+
score = response_json.get('score', 0)
93+
94+
result.error_status = score != 1
95+
result.type = {1: 'TOOL_ONE_BETTER', 2: 'TOOL_TWO_BETTER'}.get(score, 'TOOL_EQUAL')
96+
result.name = 'math'
97+
result.reason = [json.dumps(response_json, ensure_ascii=False)]
98+
99+
return result
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from dingo.model.model import Model
2+
from dingo.model.prompt.base import BasePrompt
3+
4+
5+
@Model.prompt_register('MathCompare', [], ['LLMMathCompare'])
6+
class PromptMathCompare(BasePrompt):
7+
_metric_info = {
8+
'category': 'SFT Data Assessment Metrics',
9+
'metric_name': 'PromptMathCompare',
10+
'description': 'Compares the effectiveness of two tools in extracting mathematical formulas from HTML to Markdown format by evaluating recognition rate and accuracy to determine which tool performs better',
11+
'paper_title': '',
12+
'paper_url': '',
13+
'paper_authors': '',
14+
'evaluation_results': ''
15+
}
16+
17+
# prompt v3
18+
content = """
19+
你是一位专业的数学公式识别评估专家,擅长分析 HTML 代码和 Markdown 文本中的数学公式。现在我会提供三段内容:
20+
21+
1. **裁剪后网页的 HTML 代码**:这是原始网页经过裁剪(去除非必要标签和标签属性)的 HTML 结构。
22+
2. **工具A提取的 Markdown 文本**:这是从 HTML 中提取的、适合大语言模型训练的 Markdown 格式文本。
23+
3. **工具B提取的 Markdown 文本**:这是从 HTML 中提取的、适合大语言模型训练的 Markdown 格式文本。
24+
25+
⚠️ 注意:工具A与工具B的顺序不是固定的,请不要因为顺序而偏好某一工具,最终结论必须严格基于流程2统计的数值差异。
26+
27+
## 评估流程
28+
29+
### 1. 公式数量统计
30+
31+
**原始HTML公式识别:**
32+
- MathJax格式:`\\(` `\\)` `\\[` `\\]` `$$` `$`
33+
- MathML标签:`<math>` `<mrow>` `<mi>` 等
34+
- 其他数学标签:`<div class=math>` `<mjx-container class="MathJax">` 等(内容为LaTeX格式)
35+
- 一些自定义标签:`<span class=ztext-math>` 等(内容为LaTeX格式)
36+
37+
**Markdown公式统计:**
38+
- 行内公式:`$...$` `\\(...\\)`
39+
- 行间公式:`$$...$$` `\\[...\\]` `\\begin{{...}}...\\end{{...}}`
40+
41+
### 2. 识别率和准确率统计
42+
43+
统计以下内容:
44+
- N = HTML 中实际公式数量(如果N = 0,直接跳转到 “5. 特殊情况处理”并输出指定内容,不需要进行其他的流程)
45+
- MA, MB = 工具A、B识别的公式数量(在对应Markdown文本中)
46+
- EA, EB = 工具A、B在转化中的错误数量(在对应Markdown文本中)
47+
48+
计算:
49+
- 工具A识别率 = MA / N × 100%
50+
- 工具B识别率 = MB / N × 100%
51+
- 工具A准确率 = (MA − EA) / MA × 100%
52+
- 工具B准确率 = (MB − EB) / MB × 100%
53+
54+
### 3. 量化评估规则
55+
56+
请严格按照以下规则做出决策:
57+
- 如果识别率差异 ≥ 20%:识别率高的工具获胜。
58+
- 如果识别率差异 < 20% 且准确率差异 ≥ 15%:准确率高的工具获胜。
59+
- 如果两项差异都 < 阈值:判定两者相当。
60+
61+
62+
### 原始网页的 HTML 代码如下:
63+
64+
```html
65+
{}
66+
```
67+
68+
### 工具A提取的 Markdown 文本如下:
69+
70+
```md
71+
{}
72+
```
73+
74+
### 工具B提取的 Markdown 文本如下:
75+
76+
```md
77+
{}
78+
```
79+
80+
### 4. 输出格式(HTML有公式情况,即 N ≠ 0)
81+
82+
请最终只返回一个 JSON,不要有任何额外解释说明
83+
JSON 包含以下字段:
84+
- `score`:如果工具A更好取值1,工具B更好取值2,效果相当取值0
85+
- `name`:固定值 "math"
86+
- `reason`:"1)HTML共N个公式;2)工具A统计结果;3)工具B统计结果;4)判定结果。"
87+
88+
89+
示例输出(HTML有公式情况,即 N ≠ 0):
90+
```json
91+
{{
92+
"score": [0|1|2],
93+
"name": "math",
94+
"reason": "1)HTML共N个公式;2)工具A识别MA个(识别率%),错误EA个(准确率%);3)工具B识别MB个(识别率%),错误EB个(准确率%);4)最终依据规则,判定..."
95+
}}
96+
```
97+
98+
### 5. 特殊情况处理(HTML无公式情况,即 N = 0)
99+
100+
如果统计到 N = 0,务必直接返回,不得包含额外解释:
101+
```json
102+
{{
103+
"no_formula": true
104+
}}
105+
106+
### 6. 注意事项
107+
如果 HTML 中没有任何数学公式,请按照特殊情况处理,返回指定内容。
108+
109+
如果HTML有数学公式,在做出结论前,必须严格完成 ①统计 ②计算 ③规则判定 这三个步骤,不得跳过。
110+
111+
返回结果必须是一个严格符合格式的 JSON,不得包含额外解释!
112+
"""

examples/compare/compare_math.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from dingo.config import InputArgs
2+
from dingo.exec import Executor
3+
4+
input_data = {
5+
'input_path': '../../test/data/compare/test_math_compare.jsonl',
6+
'dataset': {
7+
'source': 'local',
8+
'format': 'jsonl',
9+
'field': {
10+
'id': 'track_id',
11+
'content': 'clean_html'
12+
}
13+
},
14+
'executor': {
15+
'prompt_list': ['PromptMathCompare'],
16+
'batch_size': 10,
17+
'max_workers': 10,
18+
'result_save': {
19+
'bad': True,
20+
'good': True,
21+
'raw': True
22+
}
23+
},
24+
'evaluator': {
25+
'llm_config': {
26+
'LLMMathCompare': {
27+
'key': '',
28+
'api_url': '',
29+
'temperature': 0
30+
}
31+
}
32+
}
33+
}
34+
input_args = InputArgs(**input_data)
35+
executor = Executor.exec_map['local'](input_args)
36+
result = executor.execute()
37+
print(result)

test/data/compare/test_math_compare.jsonl

Lines changed: 2 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)