tchbcb commited on
Commit
4fd702d
·
verified ·
1 Parent(s): c1a1da3

r18 scripts sync

Browse files
Files changed (1) hide show
  1. r18_materials/build_mix_r18.py +285 -0
r18_materials/build_mix_r18.py ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """build_mix_r18.py — r18 数据构建 (stepCE 配方落地)
4
+ 基于 Kaggle 四轮实验验证的 r18 配方:
5
+ 1. stepCE 数据: 每步中间值思考链 (给 per-step CE 用)
6
+ 2. hard 上采样: 大量难题让 halting 见深度
7
+ 3. 干扰项多步应用题: 教"分步拆解+忽略冗余"(r17 已有, 保留)
8
+ 4. 对话/身份样本: 保留 r16 基础防退化
9
+ 数据格式(兼容 PonderLoader + train_ponder_moe.py --step-ce-weight):
10
+ 每行 {messages, difficulty, task_id, n_steps, chain}
11
+ - chain: 中间值列表 (若 train 脚本支持 per-step CE 用; 不支持则忽略)
12
+ 基座: mix_r17.jsonl 全量回放 + 新增 stepCE 数学链 + 干扰项
13
+ 输出: mix_r18.jsonl
14
+ """
15
+ import json, os, random, re
16
+
17
+ random.seed(18)
18
+ BASE_DIR = os.environ.get("R18_BASE_DIR", "/mnt/workspace/pnet_repo/pondernet/out_2b/data_agent")
19
+ SRC = os.path.join(BASE_DIR, "mix_r17.jsonl")
20
+ OUT = os.environ.get("R18_OUT", os.path.join(BASE_DIR, "mix_r18.jsonl"))
21
+ HARD_UP = int(os.environ.get("R18_HARD_UP", "3")) # hard 上采样倍数
22
+ N_STEPCE = int(os.environ.get("R18_N_STEPCE", "400")) # 新增 stepCE 数学链题数
23
+ N_INTERFERE = int(os.environ.get("R18_N_INTERFERE", "60"))
24
+ R = random
25
+
26
+ THINK_BLACKLIST = ["可以从", "接下来需要", "说明身份", "自我介绍", "列出"]
27
+
28
+
29
+ def think(s):
30
+ return "<think>\n%s\n</think>\n\n" % s
31
+
32
+
33
+ # ============ stepCE 数学思考链题(多步算术, 每步中间值) ============
34
+ def gen_chain_expr(rng, nsteps):
35
+ """生成 nsteps 步算式 + 中间链, 返回 (question, chain_answer_with_think)"""
36
+ ops = [rng.choice([" + ", " - ", " × "]) for _ in range(nsteps - 1)]
37
+ nums = [rng.randint(2, 20) for _ in range(nsteps)]
38
+ val = nums[0]
39
+ chain = []
40
+ ok = True
41
+ expr_strs = []
42
+ for i, (op, num) in enumerate(zip(ops, nums[1:])):
43
+ if op == " + ":
44
+ val += num
45
+ elif op == " - ":
46
+ val -= num
47
+ if val < 0:
48
+ ok = False
49
+ break
50
+ else:
51
+ val *= num
52
+ if val > 9999:
53
+ ok = False
54
+ break
55
+ chain.append(val)
56
+ expr_strs.append("%s%s%d" % (nums[i], op, num))
57
+ if not ok:
58
+ return None
59
+ q = "计算:" + "".join(expr_strs) + " = ?"
60
+ # think 含分步中间值 (给模型学"分步想")
61
+ chain_str = ",".join("第%d步=%d" % (i + 1, v) for i, v in enumerate(chain))
62
+ a = (think("分步计算:" + ",".join(str(v) for v in chain) + ",最终答案 %d" % val)
63
+ + "".join("%d%s%d=%d\n" % (nums[i], op, nums[i + 1], chain[i]) for i, op in enumerate(ops))
64
+ + "答案是:%d" % val)
65
+ return {"messages": [{"role": "user", "content": q},
66
+ {"role": "assistant", "content": a}],
67
+ "difficulty": "hard" if nsteps >= 5 else ("medium" if nsteps >= 3 else "easy"),
68
+ "n_steps": nsteps, "chain": chain}
69
+
70
+
71
+ def build_stepce_rows(n=N_STEPCE):
72
+ rows = []
73
+ rng = random.Random(18)
74
+ # 难度分布: 侧重 hard(5-7步) 让 halting 见深
75
+ while len(rows) < n:
76
+ d = rng.random()
77
+ if d < 0.5:
78
+ ns = rng.randint(5, 7)
79
+ elif d < 0.8:
80
+ ns = rng.randint(3, 4)
81
+ else:
82
+ ns = rng.randint(1, 2)
83
+ r = gen_chain_expr(rng, ns)
84
+ if r:
85
+ r["task_id"] = "r18_stepce_%04d" % len(rows)
86
+ r["cat"] = "stepce"
87
+ rows.append(r)
88
+ return rows
89
+
90
+
91
+ # ============ 干扰项多步应用题(保留 r17) ============
92
+ def _row(tid, u, a, diff="hard", n_steps=4):
93
+ return {"cat": "interfere", "difficulty": diff, "task_id": tid,
94
+ "n_steps": n_steps,
95
+ "messages": [{"role": "user", "content": u},
96
+ {"role": "assistant", "content": a}]}
97
+
98
+
99
+ def rent_q():
100
+ month = R.choice(["3月", "4月", "5月", "9月"])
101
+ start = R.choice([3, 5, 8, 10, 12, 15])
102
+ d = R.choice([18, 19, 20, 21, 22, 23, 24, 25])
103
+ rate = R.choice([50, 60, 70, 80, 90, 100, 110, 120])
104
+ y = rate * d
105
+ deposit = R.choice([1500, 2000, 2400, 3000, 3600, 4000])
106
+ fee = R.choice([150, 180, 200, 250, 300])
107
+ days30 = R.choice([30, 31])
108
+ ans = rate * days30
109
+ u = ("%s%d日起租,当月%d天房租%d元。押金:%d元;%s%d元。退租时卫生干净退%d元。"
110
+ "合计%d元。下个月(%d天)租金是多少?") % (
111
+ month, start, d, y, deposit, R.choice(["卫生费", "管理费", "保洁费"]), fee, fee,
112
+ y + deposit + fee, days30)
113
+ a = (think("先算日租金 %d/%d=%d,押金与卫生费是干扰项忽略,下月=%d×%d=%d" % (y, d, rate, rate, days30, ans))
114
+ + "根据题意,%d天房租%d元,则日租金=%d/%d=%d元。\n"
115
+ "下个月%d天租金=%d×%d=%d元。\n答案是:%d" % (d, y, y, d, rate, days30, rate, days30, ans, ans))
116
+ return _row("r18_rent_%d" % R.randint(1000, 9999), u, a)
117
+
118
+
119
+ def shopping_q():
120
+ price = R.choice([15, 18, 20, 25, 28, 30, 35, 40])
121
+ n = R.choice([2, 3, 4, 5, 6])
122
+ discount = R.choice([0.8, 0.85, 0.9])
123
+ sub = price * n
124
+ coupon = R.choice([10, 20, 30])
125
+ ship = R.choice([0, 5, 8, 10])
126
+ pay = int(sub * discount) - (coupon if int(sub * discount) >= 100 else 0) + ship
127
+ u = ("一件商品%d元,买了%d件(原价合计%d元)。全场%d折;满100元可减%d元优惠券;"
128
+ "运费%d元。包装礼盒另收%d元(可不要)。问实际应付多少元?") % (
129
+ price, n, sub, int(discount * 10), coupon, ship, R.choice([15, 20, 25]))
130
+ a = (think("先算折后价 %d×%.2f=%d,满100减%d,再加运费%d;礼盒是干扰项" % (sub, discount, int(sub * discount), coupon, ship))
131
+ + "原价合计%d元,%d折后为%d元。\n满足满%d减%d,实付%d-%d=%d。\n加运费%d元,共%d元。\n答案是:%d" % (
132
+ sub, int(discount * 10), int(sub * discount), 100, coupon,
133
+ int(sub * discount), coupon, int(sub * discount) - coupon, ship, pay, pay))
134
+ return _row("r18_shop_%d" % R.randint(1000, 9999), u, a)
135
+
136
+
137
+ def salary_q():
138
+ base = R.choice([6000, 7500, 8000, 9000, 10000])
139
+ bonus = R.choice([1000, 1500, 2000, 2500])
140
+ tax = R.choice([500, 600, 800])
141
+ ins = R.choice([600, 700, 800])
142
+ total = base + bonus - tax - ins
143
+ u = ("小张月基本工资%d元,本月绩效奖金%d元;社保公积金扣%d元,个税扣%d元。"
144
+ "公司另发端午过节费%d元(不计入工资)。问本月实发工资是多少?") % (
145
+ base, bonus, ins, tax, R.choice([200, 300, 500]))
146
+ a = (think("实发=基本工资+奖金-社保-个税=%d+%d-%d-%d;过节费是干扰项不计入" % (base, bonus, ins, tax))
147
+ + "基本工资%d元+奖金%d元=%d元。\n扣除社保%d元、个税%d元:%d-%d-%d=%d元。\n"
148
+ "(过节费不计入工资,忽略)\n答案是:%d" % (base, bonus, base + bonus, ins, tax,
149
+ base + bonus, ins, tax, total, total))
150
+ return _row("r18_salary_%d" % R.randint(1000, 9999), u, a)
151
+
152
+
153
+ def distance_q():
154
+ dist = R.choice([120, 150, 180, 200, 240, 300])
155
+ speed = R.choice([60, 80, 90, 100])
156
+ stop = R.choice([0, 15, 20, 30])
157
+ detour = R.choice([0, 10, 20, 30])
158
+ toll = R.choice([0, 20, 40, 60])
159
+ h = dist // speed
160
+ m = (dist % speed) * 60 // speed
161
+ pure = h * 60 + m
162
+ u = ("A到B距离%d公里,车速%d公里/小时。途中休息%d分钟;因修路绕行%d公里(不改变目的地)。"
163
+ "过路费%d元。问不计休息的话纯行驶时间多少分钟?" % (dist, speed, stop, detour, toll))
164
+ a = (think("纯行驶时间=距离/车速=%d/%d小时;绕路与过路费是干扰项" % (dist, speed))
165
+ + "行驶时间=%d/%d=%d小时%d分钟=%d分钟。\n(休息%d分钟只在问总时间时才加;绕路与过路费与时间无关)\n答案是:%d" % (
166
+ dist, speed, h, m, pure, stop, pure))
167
+ return _row("r18_dist_%d" % R.randint(1000, 9999), u, a)
168
+
169
+
170
+ def work_q():
171
+ total = R.choice([100, 120, 150, 180, 200])
172
+ workers = R.choice([4, 5, 6, 10])
173
+ per = total // workers
174
+ leave = R.choice([1, 2])
175
+ overtime = R.choice([0, 20, 30, 40])
176
+ u = ("某任务共%d件货,由%d人平分完成。其中1人请假%d天(任务总量不变);"
177
+ "另有%d件是加急订单(不在本任务内)。问每人实际分到多少件?") % (
178
+ total, workers, leave, overtime)
179
+ a = (think("每人=总量/人数=%d/%d=%d;请假和加急订单是干扰项" % (total, workers, per))
180
+ + "总量%d件由%d人平分:%d/%d=%d件。\n(请假只影响完成时间不影响分配量;加急订单不在本任务内)\n答案是:%d" % (
181
+ total, workers, total, workers, per, per))
182
+ return _row("r18_work_%d" % R.randint(1000, 9999), u, a)
183
+
184
+
185
+ def ratio_q():
186
+ base = R.choice([6, 8, 10, 12])
187
+ ratio = R.choice([2, 3, 4])
188
+ extra = R.choice([5, 8, 10, 12])
189
+ ans = base * ratio
190
+ u = ("甲有%d个,乙的个数是甲的%d倍。丙又给了甲%d个(不影响乙)。问乙有多少个?" % (
191
+ base, ratio, extra))
192
+ a = (think("乙=甲×倍数=%d×%d;丙给的是干扰项只影响甲" % (base, ratio))
193
+ + "甲有%d个,乙是甲的%d倍:%d×%d=%d个。\n(丙给甲%d个只改变甲,与乙无关)\n答案是:%d" % (
194
+ base, ratio, base, ratio, ans, extra, ans))
195
+ return _row("r18_ratio_%d" % R.randint(1000, 9999), u, a)
196
+
197
+
198
+ def avg_q():
199
+ n = R.choice([4, 5, 6, 8])
200
+ base = R.choice([70, 80, 90, 100])
201
+ special = R.choice([120, 150, 160, 180])
202
+ nums = [base + R.randint(-10, 10) for _ in range(n)]
203
+ total = sum(nums)
204
+ avg = total // n
205
+ u = ("小明最近%d次测验成绩是:%s。其中第1次是补考成绩(不计入平均)。"
206
+ "另有一次竞赛成绩%d分(不算测验)。问这%d次测验的平均分(取整数)是多少?" % (
207
+ n, "、".join(map(str, nums)), special, n))
208
+ a = (think("平均=成绩和/次数=(%s)/%d��补考与竞赛是干扰项" % ("+".join(map(str, nums)), n))
209
+ + "成绩总和=%d,次数%d,平均=%d/%d=%d分。\n(补考成绩本就属于这%d次;竞赛成绩不计入)\n答案是:%d" % (
210
+ total, n, total, n, avg, n, avg))
211
+ return _row("r18_avg_%d" % R.randint(1000, 9999), u, a)
212
+
213
+
214
+ SCENES = [rent_q, shopping_q, salary_q, distance_q, work_q, ratio_q, avg_q]
215
+
216
+
217
+ def build_interfere_rows():
218
+ rows = []
219
+ per = max(1, N_INTERFERE // len(SCENES))
220
+ for fn in SCENES:
221
+ for _ in range(per):
222
+ rows.append(fn())
223
+ return rows
224
+
225
+
226
+ def main():
227
+ merged, base_n = [], 0
228
+ for line in open(SRC, encoding="utf-8"):
229
+ line = line.strip()
230
+ if not line:
231
+ continue
232
+ try:
233
+ d = json.loads(line)
234
+ except Exception:
235
+ continue
236
+ base_n += 1
237
+ merged.append(d)
238
+ # 新增 stepCE 数学链
239
+ stepce = build_stepce_rows()
240
+ merged.extend(stepce)
241
+ # 干扰项
242
+ interfere = build_interfere_rows()
243
+ merged.extend(interfere)
244
+ # 去重 + 黑名单
245
+ seen = set()
246
+ out, dropped = [], 0
247
+ for r in merged:
248
+ s = json.dumps(r, ensure_ascii=False)
249
+ if any(b in s for b in THINK_BLACKLIST):
250
+ dropped += 1
251
+ continue
252
+ key = json.dumps(r.get("messages"), ensure_ascii=False)
253
+ if key in seen:
254
+ dropped += 1
255
+ continue
256
+ seen.add(key)
257
+ out.append(r)
258
+ # hard 上采样 (副本不参与去重)
259
+ hard_base = [r for r in out if r.get("difficulty") == "hard"]
260
+ hard_rows = []
261
+ for _ in range(HARD_UP):
262
+ for r in hard_base:
263
+ hard_rows.append(json.loads(json.dumps(r)))
264
+ out = [r for r in out if r.get("difficulty") != "hard"] + hard_rows
265
+ random.shuffle(out)
266
+ with open(OUT, "w", encoding="utf-8") as f:
267
+ for r in out:
268
+ f.write(json.dumps(r, ensure_ascii=False) + "\n")
269
+ # 统计
270
+ n_hard = sum(1 for r in out if r.get("difficulty") == "hard")
271
+ n_stepce = sum(1 for r in out if r.get("cat") == "stepce")
272
+ n_think_empty = sum(1 for r in out
273
+ if not any("<think>" in str(m.get("content", ""))
274
+ for m in r.get("messages", []) if m.get("role") == "assistant"))
275
+ print("MIX_R18_ROWS=%d (base=%d stepce=%d interfere=%d dropped=%d)" % (
276
+ len(out), base_n, n_stepce, len(interfere), dropped))
277
+ print("DIFFICULTY: hard=%d (%.1f%%) medium=%d easy=%d" % (
278
+ n_hard, 100.0 * n_hard / len(out),
279
+ sum(1 for r in out if r.get("difficulty") == "medium"),
280
+ sum(1 for r in out if r.get("difficulty") == "easy")))
281
+ print("STEPCE_CNT=%d THINK_EMPTY=%d" % (n_stepce, n_think_empty))
282
+
283
+
284
+ if __name__ == "__main__":
285
+ main()