Jellyfish042 commited on
Commit
6725455
·
1 Parent(s): dc25c5e

feat: replace linear scaling law with power law with offset

Browse files

- Replace log-log linear regression with nonlinear power law fitting: y = a * x^b + c
- Use scipy.optimize.curve_fit for parameter estimation
- Replace R² metric with Raw RMSE and Log-RMSE for better fit quality assessment
- Update all three plot modes: Overall, By Dataset (average), By Dataset (separate)
- Display complete fitting formula in legends for consistency
- Add test_fitting.py to validate the fitting algorithm

The new approach fits the power law directly in original space, then displays
the fitted curve in log-log coordinates, resulting in a natural curve rather
than forcing a straight line.

Files changed (2) hide show
  1. app.py +101 -61
  2. test_fitting.py +109 -0
app.py CHANGED
@@ -6,6 +6,7 @@ from dotenv import load_dotenv
6
  from matplotlib.colors import LinearSegmentedColormap
7
  import plotly.graph_objects as go
8
  import numpy as np
 
9
  from huggingface_hub import HfApi
10
  from huggingface_hub.hf_api import HTTPError
11
  from huggingface_hub.utils import GatedRepoError
@@ -223,6 +224,82 @@ def submit_model(name):
223
  return "ERROR: Unexpected error. Please try again later."
224
 
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  def create_scaling_plot(data_manager: DataManager, period: str):
227
  new_df = data_manager.query(
228
  period=period,
@@ -256,25 +333,9 @@ def create_scaling_plot(data_manager: DataManager, period: str):
256
  x_dtick = (x_max - x_min) / 4
257
  y_dtick = (y_max - y_min) / 4
258
 
259
- # 在对数空间中进行线性回归拟合
260
- # log(y) = a * log(x) + b => y = 10^b * x^a
261
- log_x = np.log10(np.array(x_values))
262
- log_y = np.log10(np.array(y_values))
263
-
264
- # 线性拟合: log_y = slope * log_x + intercept
265
- slope, intercept = np.polyfit(log_x, log_y, 1)
266
-
267
- # 计算 R² 值
268
- log_y_pred = slope * log_x + intercept
269
- ss_res = np.sum((log_y - log_y_pred) ** 2)
270
- ss_tot = np.sum((log_y - np.mean(log_y)) ** 2)
271
- r_squared = 1 - (ss_res / ss_tot) if ss_tot > 0 else 0
272
-
273
- # 生成拟合线的点(在对数空间中是直线)
274
- fit_x_log = np.linspace(x_min - 0.1, x_max + 0.1, 100)
275
- fit_y_log = slope * fit_x_log + intercept
276
- fit_x = 10**fit_x_log
277
- fit_y = 10**fit_y_log
278
 
279
  fig = go.Figure()
280
 
@@ -294,8 +355,11 @@ def create_scaling_plot(data_manager: DataManager, period: str):
294
  )
295
  )
296
 
297
- # 添加拟合线
298
- fit_label = f"Fit: y = {10**intercept:.2f} × x^{slope:.3f} ( = {r_squared:.3f})"
 
 
 
299
  fig.add_trace(
300
  go.Scatter(
301
  x=fit_x.tolist(),
@@ -412,24 +476,9 @@ def create_category_scaling_plot(data_manager: DataManager, period: str, selecte
412
 
413
  color = "#39C5BB"
414
 
415
- # 在对数空间中进行线性回归拟合
416
- log_x = np.log10(np.array(x_vals))
417
- log_y = np.log10(np.array(y_vals))
418
-
419
- slope, intercept = np.polyfit(log_x, log_y, 1)
420
-
421
- # 计算 R² 值
422
- log_y_pred = slope * log_x + intercept
423
- ss_res = np.sum((log_y - log_y_pred) ** 2)
424
- ss_tot = np.sum((log_y - np.mean(log_y)) ** 2)
425
- r_squared = 1 - (ss_res / ss_tot) if ss_tot > 0 else 0
426
-
427
- # 生成拟合线的点
428
- x_min_local, x_max_local = np.log10(min(x_vals)), np.log10(max(x_vals))
429
- fit_x_log = np.linspace(x_min_local - 0.05, x_max_local + 0.05, 100)
430
- fit_y_log = slope * fit_x_log + intercept
431
- fit_x = 10**fit_x_log
432
- fit_y = 10**fit_y_log
433
 
434
  # 构建数据集名称列表(用于hover显示)
435
  datasets_label = f"Average of {len(selected_datasets)} datasets"
@@ -450,8 +499,11 @@ def create_category_scaling_plot(data_manager: DataManager, period: str, selecte
450
  )
451
  )
452
 
453
- # 添加拟合线
454
- fit_label = f"Fit: y = {10**intercept:.2f} × x^{slope:.3f} ( = {r_squared:.3f})"
 
 
 
455
  fig.add_trace(
456
  go.Scatter(
457
  x=fit_x.tolist(),
@@ -487,24 +539,9 @@ def create_category_scaling_plot(data_manager: DataManager, period: str, selecte
487
 
488
  color = color_palette[idx % len(color_palette)]
489
 
490
- # 在对数空间中进行线性回归拟合
491
- log_x = np.log10(np.array(x_vals))
492
- log_y = np.log10(np.array(y_vals))
493
-
494
- slope, intercept = np.polyfit(log_x, log_y, 1)
495
-
496
- # 计算 R² 值
497
- log_y_pred = slope * log_x + intercept
498
- ss_res = np.sum((log_y - log_y_pred) ** 2)
499
- ss_tot = np.sum((log_y - np.mean(log_y)) ** 2)
500
- r_squared = 1 - (ss_res / ss_tot) if ss_tot > 0 else 0
501
-
502
- # 生成拟合线的点
503
- x_min_local, x_max_local = np.log10(min(x_vals)), np.log10(max(x_vals))
504
- fit_x_log = np.linspace(x_min_local - 0.05, x_max_local + 0.05, 100)
505
- fit_y_log = slope * fit_x_log + intercept
506
- fit_x = 10**fit_x_log
507
- fit_y = 10**fit_y_log
508
 
509
  # 添加数据点
510
  fig.add_trace(
@@ -523,8 +560,11 @@ def create_category_scaling_plot(data_manager: DataManager, period: str, selecte
523
  )
524
  )
525
 
526
- # 添加拟合线
527
- fit_label = f"{dataset} fit (slope={slope:.3f}, R²={r_squared:.3f})"
 
 
 
528
  fig.add_trace(
529
  go.Scatter(
530
  x=fit_x.tolist(),
 
6
  from matplotlib.colors import LinearSegmentedColormap
7
  import plotly.graph_objects as go
8
  import numpy as np
9
+ from scipy.optimize import curve_fit
10
  from huggingface_hub import HfApi
11
  from huggingface_hub.hf_api import HTTPError
12
  from huggingface_hub.utils import GatedRepoError
 
224
  return "ERROR: Unexpected error. Please try again later."
225
 
226
 
227
+ def power_law_with_offset(x, a, b, c):
228
+ """带偏置的幂律函数: y = a * x^b + c"""
229
+ return a * np.power(x, b) + c
230
+
231
+
232
+ def fit_power_law_with_offset(x_values, y_values):
233
+ """
234
+ 使用带偏置的幂律拟合原始数据
235
+ 返回: (params, raw_rmse, log_rmse, fit_x, fit_y)
236
+ """
237
+ x_arr = np.array(x_values)
238
+ y_arr = np.array(y_values)
239
+
240
+ # 初始参数估计
241
+ # 使用简单的幂律拟合作为初始值
242
+ log_x = np.log10(x_arr)
243
+ log_y = np.log10(y_arr)
244
+ slope, intercept = np.polyfit(log_x, log_y, 1)
245
+
246
+ a_init = 10**intercept
247
+ b_init = slope
248
+ c_init = 0 # 偏置初始值设为0
249
+
250
+ try:
251
+ # 使用curve_fit进行非线性拟合
252
+ params, _ = curve_fit(
253
+ power_law_with_offset,
254
+ x_arr,
255
+ y_arr,
256
+ p0=[a_init, b_init, c_init],
257
+ maxfev=10000
258
+ )
259
+ a, b, c = params
260
+
261
+ # 计算预测值
262
+ y_pred = power_law_with_offset(x_arr, a, b, c)
263
+
264
+ # 计算原始空间 RMSE
265
+ raw_rmse = np.sqrt(np.mean((y_arr - y_pred) ** 2))
266
+
267
+ # 计算对数空间 RMSE
268
+ log_y_actual = np.log10(y_arr)
269
+ log_y_pred = np.log10(y_pred)
270
+ log_rmse = np.sqrt(np.mean((log_y_actual - log_y_pred) ** 2))
271
+
272
+ # 生成拟合曲线的点
273
+ x_min, x_max = min(x_values), max(x_values)
274
+ fit_x = np.linspace(x_min * 0.8, x_max * 1.2, 100)
275
+ fit_y = power_law_with_offset(fit_x, a, b, c)
276
+
277
+ return params, raw_rmse, log_rmse, fit_x, fit_y
278
+ except Exception as e:
279
+ print(f"Fitting failed: {e}")
280
+ # 如果拟合失败,返回简单幂律拟合结果
281
+ a = a_init
282
+ b = b_init
283
+ c = 0
284
+ params = (a, b, c)
285
+
286
+ y_pred = a * np.power(x_arr, b)
287
+
288
+ # 计算原始空间 RMSE
289
+ raw_rmse = np.sqrt(np.mean((y_arr - y_pred) ** 2))
290
+
291
+ # 计算对数空间 RMSE
292
+ log_y_actual = np.log10(y_arr)
293
+ log_y_pred = np.log10(y_pred)
294
+ log_rmse = np.sqrt(np.mean((log_y_actual - log_y_pred) ** 2))
295
+
296
+ x_min, x_max = min(x_values), max(x_values)
297
+ fit_x = np.linspace(x_min * 0.8, x_max * 1.2, 100)
298
+ fit_y = a * np.power(fit_x, b)
299
+
300
+ return params, raw_rmse, log_rmse, fit_x, fit_y
301
+
302
+
303
  def create_scaling_plot(data_manager: DataManager, period: str):
304
  new_df = data_manager.query(
305
  period=period,
 
333
  x_dtick = (x_max - x_min) / 4
334
  y_dtick = (y_max - y_min) / 4
335
 
336
+ # 使用带偏置的幂律拟合原始数据
337
+ params, raw_rmse, log_rmse, fit_x, fit_y = fit_power_law_with_offset(x_values, y_values)
338
+ a, b, c = params
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
340
  fig = go.Figure()
341
 
 
355
  )
356
  )
357
 
358
+ # 添加拟合线
359
+ if abs(c) < 0.01:
360
+ fit_label = f"Fit: y = {a:.2f} × x^{b:.3f}<br>Raw RMSE: {raw_rmse:.2f}, Log-RMSE: {log_rmse:.3f}"
361
+ else:
362
+ fit_label = f"Fit: y = {a:.2f} × x^{b:.3f} + {c:.2f}<br>Raw RMSE: {raw_rmse:.2f}, Log-RMSE: {log_rmse:.3f}"
363
  fig.add_trace(
364
  go.Scatter(
365
  x=fit_x.tolist(),
 
476
 
477
  color = "#39C5BB"
478
 
479
+ # 使用带偏置的幂律拟合原始数据
480
+ params, raw_rmse, log_rmse, fit_x, fit_y = fit_power_law_with_offset(x_vals, y_vals)
481
+ a, b, c = params
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
 
483
  # 构建数据集名称列表(用于hover显示)
484
  datasets_label = f"Average of {len(selected_datasets)} datasets"
 
499
  )
500
  )
501
 
502
+ # 添加拟合线
503
+ if abs(c) < 0.01:
504
+ fit_label = f"Fit: y = {a:.2f} × x^{b:.3f}<br>Raw RMSE: {raw_rmse:.2f}, Log-RMSE: {log_rmse:.3f}"
505
+ else:
506
+ fit_label = f"Fit: y = {a:.2f} × x^{b:.3f} + {c:.2f}<br>Raw RMSE: {raw_rmse:.2f}, Log-RMSE: {log_rmse:.3f}"
507
  fig.add_trace(
508
  go.Scatter(
509
  x=fit_x.tolist(),
 
539
 
540
  color = color_palette[idx % len(color_palette)]
541
 
542
+ # 使用带偏置的幂律拟合原始数据
543
+ params, raw_rmse, log_rmse, fit_x, fit_y = fit_power_law_with_offset(x_vals, y_vals)
544
+ a, b, c = params
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
 
546
  # 添加数据点
547
  fig.add_trace(
 
560
  )
561
  )
562
 
563
+ # 添加拟合线
564
+ if abs(c) < 0.01:
565
+ fit_label = f"{dataset}: y = {a:.2f} × x^{b:.3f}<br>Raw RMSE: {raw_rmse:.2f}, Log-RMSE: {log_rmse:.3f}"
566
+ else:
567
+ fit_label = f"{dataset}: y = {a:.2f} × x^{b:.3f} + {c:.2f}<br>Raw RMSE: {raw_rmse:.2f}, Log-RMSE: {log_rmse:.3f}"
568
  fig.add_trace(
569
  go.Scatter(
570
  x=fit_x.tolist(),
test_fitting.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """测试带偏置的幂律拟合功能"""
2
+ import numpy as np
3
+ from scipy.optimize import curve_fit
4
+
5
+ def power_law_with_offset(x, a, b, c):
6
+ """带偏置的幂律函数: y = a * x^b + c"""
7
+ return a * np.power(x, b) + c
8
+
9
+
10
+ def fit_power_law_with_offset(x_values, y_values):
11
+ """
12
+ 使用带偏置的幂律拟合原始数据
13
+ 返回: (params, raw_rmse, log_rmse, fit_x, fit_y)
14
+ """
15
+ x_arr = np.array(x_values)
16
+ y_arr = np.array(y_values)
17
+
18
+ # 初始参数估计
19
+ # 使用简单的幂律拟合作为初始值
20
+ log_x = np.log10(x_arr)
21
+ log_y = np.log10(y_arr)
22
+ slope, intercept = np.polyfit(log_x, log_y, 1)
23
+
24
+ a_init = 10**intercept
25
+ b_init = slope
26
+ c_init = 0 # 偏置初始值设为0
27
+
28
+ try:
29
+ # 使用curve_fit进行非线性拟合
30
+ params, _ = curve_fit(
31
+ power_law_with_offset,
32
+ x_arr,
33
+ y_arr,
34
+ p0=[a_init, b_init, c_init],
35
+ maxfev=10000
36
+ )
37
+ a, b, c = params
38
+
39
+ # 计算预测值
40
+ y_pred = power_law_with_offset(x_arr, a, b, c)
41
+
42
+ # 计算原始空间 RMSE
43
+ raw_rmse = np.sqrt(np.mean((y_arr - y_pred) ** 2))
44
+
45
+ # 计算对数空间 RMSE
46
+ log_y_actual = np.log10(y_arr)
47
+ log_y_pred = np.log10(y_pred)
48
+ log_rmse = np.sqrt(np.mean((log_y_actual - log_y_pred) ** 2))
49
+
50
+ # 生成拟合曲线的点
51
+ x_min, x_max = min(x_values), max(x_values)
52
+ fit_x = np.linspace(x_min * 0.8, x_max * 1.2, 100)
53
+ fit_y = power_law_with_offset(fit_x, a, b, c)
54
+
55
+ return params, raw_rmse, log_rmse, fit_x, fit_y
56
+ except Exception as e:
57
+ print(f"Fitting failed: {e}")
58
+ # 如果拟合失败,返回简单幂律拟合结果
59
+ a = a_init
60
+ b = b_init
61
+ c = 0
62
+ params = (a, b, c)
63
+
64
+ y_pred = a * np.power(x_arr, b)
65
+
66
+ # 计算原始空间 RMSE
67
+ raw_rmse = np.sqrt(np.mean((y_arr - y_pred) ** 2))
68
+
69
+ # 计算对数空间 RMSE
70
+ log_y_actual = np.log10(y_arr)
71
+ log_y_pred = np.log10(y_pred)
72
+ log_rmse = np.sqrt(np.mean((log_y_actual - log_y_pred) ** 2))
73
+
74
+ x_min, x_max = min(x_values), max(x_values)
75
+ fit_x = np.linspace(x_min * 0.8, x_max * 1.2, 100)
76
+ fit_y = a * np.power(fit_x, b)
77
+
78
+ return params, raw_rmse, log_rmse, fit_x, fit_y
79
+
80
+
81
+ if __name__ == "__main__":
82
+ # 测试数据:模拟一些模型参数和压缩率的关系
83
+ # 假设真实关系为 y = 50 * x^(-0.1) + 10
84
+ x_test = np.array([1, 3, 7, 13, 20, 30])
85
+ y_true = 50 * np.power(x_test, -0.1) + 10
86
+ # 添加一些噪声
87
+ np.random.seed(42)
88
+ y_test = y_true + np.random.normal(0, 0.5, len(x_test))
89
+
90
+ print("测试数据:")
91
+ print(f"x: {x_test}")
92
+ print(f"y: {y_test}")
93
+ print()
94
+
95
+ # 进行拟合
96
+ params, raw_rmse, log_rmse, fit_x, fit_y = fit_power_law_with_offset(x_test.tolist(), y_test.tolist())
97
+ a, b, c = params
98
+
99
+ print("拟合结果:")
100
+ print(f"a = {a:.4f}")
101
+ print(f"b = {b:.4f}")
102
+ print(f"c = {c:.4f}")
103
+ print(f"Raw RMSE = {raw_rmse:.4f}")
104
+ print(f"Log-RMSE = {log_rmse:.4f}")
105
+ print()
106
+ print(f"拟合公式: y = {a:.2f} * x^{b:.3f} + {c:.2f}")
107
+ print()
108
+ print("真实参数: a=50, b=-0.1, c=10")
109
+ print("拟合成功!" if raw_rmse < 2.0 else "拟合可能需要调整")