Jellyfish042 commited on
Commit
9e9c816
·
1 Parent(s): d768c28
Files changed (2) hide show
  1. app.py +318 -213
  2. title.py +6 -2
app.py CHANGED
@@ -49,9 +49,18 @@ model_size_to_file_name = {
49
  }
50
  SCALING_EXTRAPOLATE_MAX_B = 10000
51
  SCALING_FIT_POINTS = 200
 
 
 
52
  FIT_LINE_HOVER_TEMPLATE = "Params: %{x:.2f}B<br>Predicted CR: %{y:.2f}%<extra></extra>"
53
  MODEL_NAME_DISPLAY_MAX_CHARS = 28
54
- FRONTIER_TABLE_COLUMNS = ["Params (B)", "Model"]
 
 
 
 
 
 
55
 
56
 
57
  def read_about_md():
@@ -382,11 +391,34 @@ def submit_model(name):
382
  return "ERROR: Unexpected error. Please try again later."
383
 
384
 
385
- def power_law_with_offset(x, a, b, c):
386
- """带偏置的幂律函数: y = a * x^b + c"""
387
- return a * np.power(x, b) + c
388
-
389
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  def filter_pareto_frontier(x_values, y_values, names):
391
  """
392
  筛选帕累托前沿的点
@@ -437,12 +469,12 @@ def _build_frontier_table_rows(x_values, y_values, names):
437
  return []
438
 
439
  valid_x, valid_y, valid_names = zip(*valid_data)
440
- pareto_x, _, pareto_names = filter_pareto_frontier(list(valid_x), list(valid_y), list(valid_names))
441
  rows = [
442
- {"Params (B)": round(float(param), 3), "Model": _format_frontier_model_name(model)}
443
- for param, model in zip(pareto_x, pareto_names)
444
  ]
445
- return sorted(rows, key=lambda row: (row["Params (B)"], row["Model"]))
446
 
447
 
448
  def _format_frontier_model_name(model_name: str):
@@ -510,20 +542,25 @@ def create_scaling_frontier_table(data_manager: DataManager, period: str, mode:
510
  )
511
  )
512
 
513
- rows = list({(row["Params (B)"], row["Model"]): row for row in rows}.values())
514
- rows = sorted(rows, key=lambda row: (row["Params (B)"], row["Model"]))
 
 
 
 
515
  return pd.DataFrame(rows, columns=FRONTIER_TABLE_COLUMNS)
516
 
517
 
518
  def render_scaling_frontier_table(frontier_df):
519
  if frontier_df is None or len(frontier_df) == 0:
520
- rows_html = '<tr><td class="empty" colspan="2">No Pareto frontier models</td></tr>'
521
  else:
522
  rows = []
523
  for _, row in frontier_df.iterrows():
524
- params = html.escape(f'{float(row["Params (B)"]):.3f}')
525
- model = html.escape(str(row["Model"]))
526
- rows.append(f'<tr><td class="params">{params}</td><td class="model">{model}</td></tr>')
 
527
  rows_html = "\n".join(rows)
528
 
529
  return f"""
@@ -534,9 +571,10 @@ def render_scaling_frontier_table(frontier_df):
534
  <colgroup>
535
  <col class="params-col">
536
  <col class="model-col">
 
537
  </colgroup>
538
  <thead>
539
- <tr><th>Params (B)</th><th>Model</th></tr>
540
  </thead>
541
  <tbody>
542
  {rows_html}
@@ -623,10 +661,15 @@ def create_scaling_plot(data_manager: DataManager, period: str, use_pareto: bool
623
  visible_columns=None,
624
  )
625
 
626
- if len(new_df) == 0:
627
- fig = go.Figure()
628
- fig.update_layout(title={"text": "Compression Ratio Scaling Law", "x": 0.5}, width=800, height=600)
629
- return fig
 
 
 
 
 
630
 
631
  x_values = new_df["Params (B)"].astype(float).tolist()
632
  y_values = new_df["Average (lower=better)"].astype(float).tolist()
@@ -634,21 +677,31 @@ def create_scaling_plot(data_manager: DataManager, period: str, use_pareto: bool
634
 
635
  # 过滤掉无效值(NaN, 0, 负数)
636
  valid_data = [(x, y, n) for x, y, n in zip(x_values, y_values, names) if x > 0 and y > 0 and not np.isnan(x) and not np.isnan(y)]
637
- if len(valid_data) == 0:
638
- fig = go.Figure()
639
- fig.update_layout(title={"text": "Compression Ratio Scaling Law", "x": 0.5}, width=800, height=600)
640
- return fig
 
 
 
 
 
641
 
642
  x_values, y_values, names = zip(*valid_data)
643
  x_values, y_values, names = list(x_values), list(y_values), list(names)
644
 
645
  # 如果选择帕累托前沿,筛选数据点
646
  if use_pareto:
647
- fit_x_values, fit_y_values, fit_names = filter_pareto_frontier(x_values, y_values, names)
648
- if len(fit_x_values) == 0:
649
- fig = go.Figure()
650
- fig.update_layout(title={"text": "Compression Ratio Scaling Law - No Pareto Frontier", "x": 0.5}, width=800, height=600)
651
- return fig
 
 
 
 
 
652
  else:
653
  fit_x_values, fit_y_values, fit_names = x_values, y_values, names
654
 
@@ -674,52 +727,66 @@ def create_scaling_plot(data_manager: DataManager, period: str, use_pareto: bool
674
  x_max = np.log10(x_axis_max)
675
  y_min = np.log10(y_min_val)
676
  y_max = np.log10(y_max_val)
677
- x_dtick = (x_max - x_min) / 4
678
- y_dtick = (y_max - y_min) / 4
679
-
680
- fig = go.Figure()
681
-
682
- # Pareto 模式下,普通散点仅显示非 Pareto 点,避免同一点重复绘制
683
- base_points = list(zip(x_values, y_values, names))
684
- if use_pareto:
685
- pareto_points = set(zip(fit_x_values, fit_y_values, fit_names))
686
- base_points = [p for p in base_points if p not in pareto_points]
687
-
688
- if base_points:
689
- base_x_values, base_y_values, base_names = zip(*base_points)
690
- fig.add_trace(
691
- go.Scatter(
692
- x=list(base_x_values),
693
- y=list(base_y_values),
694
- mode="markers",
695
- name="Non-Pareto Models" if use_pareto else "All Models",
696
- marker=dict(size=12, color="#39C5BB", opacity=0.8),
697
- text=list(base_names),
698
- customdata=list(zip(base_x_values, base_y_values)),
699
- hovertemplate=(
700
- "<b>%{text}</b><br>" + "Params: %{customdata[0]:.2f}B<br>" + "Compression Ratio: %{customdata[1]:.2f}%<br>" + "<extra></extra>"
701
- ),
702
- )
703
- )
704
-
705
- # 如果使用帕累托前沿,高亮显示帕累托前沿的点
706
- if use_pareto:
707
- fig.add_trace(
708
- go.Scatter(
709
- x=fit_x_values,
710
- y=fit_y_values,
711
- mode="markers",
712
- name="Pareto Frontier",
713
- marker=dict(size=14, color="#FF6B6B", symbol="diamond", opacity=1.0),
714
- text=fit_names,
715
- customdata=list(zip(fit_x_values, fit_y_values)),
716
- hovertemplate=(
717
- "<b>%{text}</b> (Pareto)<br>"
718
- + "Params: %{customdata[0]:.2f}B<br>"
719
- + "Compression Ratio: %{customdata[1]:.2f}%<br>"
720
- + "<extra></extra>"
721
- ),
722
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
723
  )
724
 
725
  # 添加拟合曲线
@@ -731,19 +798,20 @@ def create_scaling_plot(data_manager: DataManager, period: str, use_pareto: bool
731
  fig.add_trace(
732
  go.Scatter(
733
  x=fit_x.tolist(),
734
- y=fit_y.tolist(),
735
- mode="lines",
736
- name=fit_label,
737
- line=dict(color="#FF6B6B", width=2, dash="dash"),
738
- hovertemplate=FIT_LINE_HOVER_TEMPLATE,
739
- )
740
- )
741
 
742
  title_suffix = " (Pareto Frontier)" if use_pareto else ""
743
  fig.update_layout(
744
  title={"text": f"Compression Ratio Scaling Law{title_suffix}", "x": 0.5, "xanchor": "center", "yanchor": "top"},
745
- width=800,
746
- height=600,
 
747
  showlegend=True,
748
  legend=dict(
749
  yanchor="top",
@@ -761,17 +829,18 @@ def create_scaling_plot(data_manager: DataManager, period: str, use_pareto: bool
761
  tickformat=".2f",
762
  range=[x_min - 0.1, x_max + 0.1],
763
  ),
764
- yaxis=dict(
765
- title="Compression Ratio (%)",
766
- showgrid=True,
767
- zeroline=False,
768
- type="log",
769
  dtick=y_dtick,
770
  tickformat=".2f",
771
- range=[y_min - 0.1, y_max + 0.1],
772
- autorange="reversed",
773
- ),
774
- )
 
775
  return fig
776
 
777
 
@@ -791,10 +860,15 @@ def create_category_scaling_plot(
791
  visible_columns=None,
792
  )
793
 
794
- if len(new_df) == 0 or not selected_datasets:
795
- fig = go.Figure()
796
- fig.update_layout(title={"text": "Scaling Law by Dataset", "x": 0.5}, width=1000, height=700)
797
- return fig
 
 
 
 
 
798
 
799
  # 颜色配色方案 - 使用高对比度、饱和度高的颜色
800
  color_palette = [
@@ -812,9 +886,10 @@ def create_category_scaling_plot(
812
 
813
  fig = go.Figure()
814
 
815
- # 用于计算全局坐标范围
816
- all_x_values = []
817
- all_y_values = []
 
818
 
819
  if display_mode == "average":
820
  # 平均模式:计算选中数据集的平均值
@@ -862,57 +937,69 @@ def create_category_scaling_plot(
862
  extrapolate_max_b=SCALING_EXTRAPOLATE_MAX_B,
863
  )
864
  a, b, c = params
865
- positive_fit_y = fit_y[fit_y > 0]
866
- if positive_fit_y.size:
867
- all_y_values.extend(positive_fit_y.tolist())
868
-
869
- # 构建数据集名称列表(用于hover显示)
870
- datasets_label = f"Average of {len(selected_datasets)} datasets"
871
-
872
- # Pareto 模式下,普通散点仅显示非 Pareto 点,避免同一点重复绘制
873
- base_points = list(zip(x_vals, y_vals, name_vals))
874
- if use_pareto:
875
- pareto_points = set(zip(fit_x_vals, fit_y_vals, fit_name_vals))
876
- base_points = [p for p in base_points if p not in pareto_points]
877
-
878
- if base_points:
879
- base_x_vals, base_y_vals, base_name_vals = zip(*base_points)
880
- fig.add_trace(
881
- go.Scatter(
882
- x=list(base_x_vals),
883
- y=list(base_y_vals),
884
- mode="markers",
885
- name=f"{datasets_label} (Non-Pareto)" if use_pareto else datasets_label,
886
- marker=dict(size=12, color=color, opacity=0.8),
887
- text=list(base_name_vals),
888
- customdata=list(zip(base_x_vals, base_y_vals)),
889
- hovertemplate=(
890
- f"<b>%{{text}}</b><br>{datasets_label}<br>"
891
- + "Params: %{customdata[0]:.2f}B<br>"
892
- + "CR: %{customdata[1]:.2f}%<br>"
893
- + "<extra></extra>"
894
- ),
895
- )
 
 
 
896
  )
897
 
898
- # 如果使用帕累托前沿,高亮显示帕累托前沿的点
899
- if use_pareto:
900
- fig.add_trace(
901
- go.Scatter(
902
- x=fit_x_vals,
903
- y=fit_y_vals,
904
- mode="markers",
905
- name="Pareto Frontier",
906
- marker=dict(size=14, color="#FF6B6B", symbol="diamond", opacity=1.0),
907
- text=fit_name_vals,
908
- customdata=list(zip(fit_x_vals, fit_y_vals)),
909
- hovertemplate=(
910
- f"<b>%{{text}}</b> (Pareto)<br>{datasets_label}<br>"
911
- + "Params: %{customdata[0]:.2f}B<br>"
912
- + "CR: %{customdata[1]:.2f}%<br>"
913
- + "<extra></extra>"
914
- ),
915
- )
 
 
 
 
 
 
 
 
 
916
  )
917
 
918
  # 添加拟合曲线
@@ -924,13 +1011,13 @@ def create_category_scaling_plot(
924
  fig.add_trace(
925
  go.Scatter(
926
  x=fit_x.tolist(),
927
- y=fit_y.tolist(),
928
- mode="lines",
929
- name=fit_label,
930
- line=dict(color="#FF6B6B", width=2, dash="dash"),
931
- hovertemplate=FIT_LINE_HOVER_TEMPLATE,
932
- )
933
- )
934
  else:
935
  # 单独显示模式:为每个数据集创建散点图和拟合线
936
  for idx, dataset in enumerate(selected_datasets):
@@ -971,55 +1058,67 @@ def create_category_scaling_plot(
971
  extrapolate_max_b=SCALING_EXTRAPOLATE_MAX_B,
972
  )
973
  a, b, c = params
974
- positive_fit_y = fit_y[fit_y > 0]
975
- if positive_fit_y.size:
976
- all_y_values.extend(positive_fit_y.tolist())
977
-
978
- # Pareto 模式下,普通散点仅显示非 Pareto 点,避免同一点重复绘制
979
- base_points = list(zip(x_vals, y_vals, name_vals))
980
- if use_pareto:
981
- pareto_points = set(zip(fit_x_vals, fit_y_vals, fit_name_vals))
982
- base_points = [p for p in base_points if p not in pareto_points]
983
-
984
- if base_points:
985
- base_x_vals, base_y_vals, base_name_vals = zip(*base_points)
986
- fig.add_trace(
987
- go.Scatter(
988
- x=list(base_x_vals),
989
- y=list(base_y_vals),
990
- mode="markers",
991
- name=f"{dataset} (Non-Pareto)" if use_pareto else f"{dataset}",
992
- marker=dict(size=10, color=color, opacity=0.7),
993
- text=list(base_name_vals),
994
- customdata=list(zip(base_x_vals, base_y_vals)),
995
- hovertemplate=(
996
- f"<b>%{{text}}</b><br>{dataset}<br>"
997
- + "Params: %{customdata[0]:.2f}B<br>"
998
- + "CR: %{customdata[1]:.2f}%<br>"
999
- + "<extra></extra>"
1000
- ),
1001
- legendgroup=dataset,
 
 
 
1002
  )
1003
  )
1004
 
1005
- # 如果使用帕累托前沿,高亮显示帕累托前沿的点
1006
- if use_pareto:
1007
- fig.add_trace(
1008
- go.Scatter(
1009
- x=fit_x_vals,
1010
- y=fit_y_vals,
1011
- mode="markers",
1012
- name=f"{dataset} (Pareto)",
1013
- marker=dict(size=12, color=color, symbol="diamond", opacity=1.0),
1014
- text=fit_name_vals,
1015
- customdata=list(zip(fit_x_vals, fit_y_vals)),
1016
- hovertemplate=(
1017
- f"<b>%{{text}}</b> (Pareto)<br>{dataset}<br>"
1018
- + "Params: %{customdata[0]:.2f}B<br>"
1019
- + "CR: %{customdata[1]:.2f}%<br>"
1020
- + "<extra></extra>"
1021
- ),
1022
- legendgroup=dataset,
 
 
 
 
 
 
 
 
 
1023
  )
1024
  )
1025
 
@@ -1042,10 +1141,15 @@ def create_category_scaling_plot(
1042
  )
1043
  )
1044
 
1045
- if not all_x_values or not all_y_values:
1046
- fig = go.Figure()
1047
- fig.update_layout(title={"text": "Scaling Law by Dataset - No Valid Data", "x": 0.5}, width=1000, height=700)
1048
- return fig
 
 
 
 
 
1049
 
1050
  # 计算全局坐标范围
1051
  x_min_val = min(all_x_values)
@@ -1058,8 +1162,8 @@ def create_category_scaling_plot(
1058
 
1059
  fig.update_layout(
1060
  title={"text": "Scaling Law by Dataset", "x": 0.5, "xanchor": "center", "yanchor": "top"},
1061
- width=1000,
1062
- height=700,
1063
  showlegend=True,
1064
  legend=dict(
1065
  yanchor="top",
@@ -1086,10 +1190,11 @@ def create_category_scaling_plot(
1086
  dtick=y_dtick,
1087
  tickformat=".2f",
1088
  range=[y_min - 0.1, y_max + 0.1],
1089
- autorange="reversed",
1090
- ),
1091
- margin=dict(r=250), # 为图例预留空间
1092
- )
 
1093
  return fig
1094
 
1095
 
 
49
  }
50
  SCALING_EXTRAPOLATE_MAX_B = 10000
51
  SCALING_FIT_POINTS = 200
52
+ SCALING_PLOT_WIDTH = 1000
53
+ SCALING_PLOT_HEIGHT = 620
54
+ SCALING_PLOT_MARGIN = dict(l=70, r=35, t=70, b=65)
55
  FIT_LINE_HOVER_TEMPLATE = "Params: %{x:.2f}B<br>Predicted CR: %{y:.2f}%<extra></extra>"
56
  MODEL_NAME_DISPLAY_MAX_CHARS = 28
57
+ FRONTIER_TABLE_COLUMNS = ["params", "model", "ratio%"]
58
+ FIT_RESIDUAL_COLORSCALE = [
59
+ [0.0, "#2CA25F"],
60
+ [0.5, "#F7F7F7"],
61
+ [1.0, "#DE2D26"],
62
+ ]
63
+ FIT_LINE_COLOR = "#4B5563"
64
 
65
 
66
  def read_about_md():
 
391
  return "ERROR: Unexpected error. Please try again later."
392
 
393
 
394
+ def power_law_with_offset(x, a, b, c):
395
+ """带偏置的幂律函数: y = a * x^b + c"""
396
+ return a * np.power(x, b) + c
397
+
398
+
399
+ def calculate_fit_delta_percent(x_values, y_values, params):
400
+ """Return percent delta from fit; negative is better because lower CR is better."""
401
+ x_arr = np.array(x_values, dtype=float)
402
+ y_arr = np.array(y_values, dtype=float)
403
+ fit_y = power_law_with_offset(x_arr, *params)
404
+ with np.errstate(divide="ignore", invalid="ignore"):
405
+ delta = ((y_arr - fit_y) / fit_y) * 100
406
+ delta[~np.isfinite(delta)] = np.nan
407
+ return delta.tolist()
408
+
409
+
410
+ def create_fit_delta_coloraxis(delta_values):
411
+ finite_values = [abs(float(v)) for v in delta_values if np.isfinite(v)]
412
+ max_abs = max(finite_values) if finite_values else 1.0
413
+ max_abs = max(max_abs, 1.0)
414
+ return dict(
415
+ colorscale=FIT_RESIDUAL_COLORSCALE,
416
+ cmin=-max_abs,
417
+ cmax=max_abs,
418
+ colorbar=dict(title="vs fit (%)", ticksuffix="%"),
419
+ )
420
+
421
+
422
  def filter_pareto_frontier(x_values, y_values, names):
423
  """
424
  筛选帕累托前沿的点
 
469
  return []
470
 
471
  valid_x, valid_y, valid_names = zip(*valid_data)
472
+ pareto_x, pareto_y, pareto_names = filter_pareto_frontier(list(valid_x), list(valid_y), list(valid_names))
473
  rows = [
474
+ {"params": round(float(param), 3), "model": _format_frontier_model_name(model), "ratio%": round(float(ratio), 3)}
475
+ for param, ratio, model in zip(pareto_x, pareto_y, pareto_names)
476
  ]
477
+ return sorted(rows, key=lambda row: (-row["params"], row["model"]))
478
 
479
 
480
  def _format_frontier_model_name(model_name: str):
 
542
  )
543
  )
544
 
545
+ best_rows = {}
546
+ for row in rows:
547
+ row_key = (row["params"], row["model"])
548
+ if row_key not in best_rows or row["ratio%"] < best_rows[row_key]["ratio%"]:
549
+ best_rows[row_key] = row
550
+ rows = sorted(best_rows.values(), key=lambda row: (-row["params"], row["model"]))
551
  return pd.DataFrame(rows, columns=FRONTIER_TABLE_COLUMNS)
552
 
553
 
554
  def render_scaling_frontier_table(frontier_df):
555
  if frontier_df is None or len(frontier_df) == 0:
556
+ rows_html = '<tr><td class="empty" colspan="3">No Pareto frontier models</td></tr>'
557
  else:
558
  rows = []
559
  for _, row in frontier_df.iterrows():
560
+ params = html.escape(f'{float(row["params"]):.3f}')
561
+ model = html.escape(str(row["model"]))
562
+ ratio = html.escape(f'{float(row["ratio%"]):.3f}')
563
+ rows.append(f'<tr><td class="params">{params}</td><td class="model">{model}</td><td class="ratio">{ratio}</td></tr>')
564
  rows_html = "\n".join(rows)
565
 
566
  return f"""
 
571
  <colgroup>
572
  <col class="params-col">
573
  <col class="model-col">
574
+ <col class="ratio-col">
575
  </colgroup>
576
  <thead>
577
+ <tr><th>params</th><th>model</th><th>ratio%</th></tr>
578
  </thead>
579
  <tbody>
580
  {rows_html}
 
661
  visible_columns=None,
662
  )
663
 
664
+ if len(new_df) == 0:
665
+ fig = go.Figure()
666
+ fig.update_layout(
667
+ title={"text": "Compression Ratio Scaling Law", "x": 0.5},
668
+ width=SCALING_PLOT_WIDTH,
669
+ height=SCALING_PLOT_HEIGHT,
670
+ margin=SCALING_PLOT_MARGIN,
671
+ )
672
+ return fig
673
 
674
  x_values = new_df["Params (B)"].astype(float).tolist()
675
  y_values = new_df["Average (lower=better)"].astype(float).tolist()
 
677
 
678
  # 过滤掉无效值(NaN, 0, 负数)
679
  valid_data = [(x, y, n) for x, y, n in zip(x_values, y_values, names) if x > 0 and y > 0 and not np.isnan(x) and not np.isnan(y)]
680
+ if len(valid_data) == 0:
681
+ fig = go.Figure()
682
+ fig.update_layout(
683
+ title={"text": "Compression Ratio Scaling Law", "x": 0.5},
684
+ width=SCALING_PLOT_WIDTH,
685
+ height=SCALING_PLOT_HEIGHT,
686
+ margin=SCALING_PLOT_MARGIN,
687
+ )
688
+ return fig
689
 
690
  x_values, y_values, names = zip(*valid_data)
691
  x_values, y_values, names = list(x_values), list(y_values), list(names)
692
 
693
  # 如果选择帕累托前沿,筛选数据点
694
  if use_pareto:
695
+ fit_x_values, fit_y_values, fit_names = filter_pareto_frontier(x_values, y_values, names)
696
+ if len(fit_x_values) == 0:
697
+ fig = go.Figure()
698
+ fig.update_layout(
699
+ title={"text": "Compression Ratio Scaling Law - No Pareto Frontier", "x": 0.5},
700
+ width=SCALING_PLOT_WIDTH,
701
+ height=SCALING_PLOT_HEIGHT,
702
+ margin=SCALING_PLOT_MARGIN,
703
+ )
704
+ return fig
705
  else:
706
  fit_x_values, fit_y_values, fit_names = x_values, y_values, names
707
 
 
727
  x_max = np.log10(x_axis_max)
728
  y_min = np.log10(y_min_val)
729
  y_max = np.log10(y_max_val)
730
+ x_dtick = (x_max - x_min) / 4
731
+ y_dtick = (y_max - y_min) / 4
732
+
733
+ fig = go.Figure()
734
+ point_delta_values = calculate_fit_delta_percent(x_values, y_values, params)
735
+
736
+ # Pareto 模式下,普通散点仅显示非 Pareto 点,避免同一点重复绘制
737
+ base_points = list(zip(x_values, y_values, names, point_delta_values))
738
+ if use_pareto:
739
+ pareto_points = set(zip(fit_x_values, fit_y_values, fit_names))
740
+ base_points = [p for p in base_points if (p[0], p[1], p[2]) not in pareto_points]
741
+
742
+ if base_points:
743
+ base_x_values, base_y_values, base_names, base_delta_values = zip(*base_points)
744
+ fig.add_trace(
745
+ go.Scatter(
746
+ x=list(base_x_values),
747
+ y=list(base_y_values),
748
+ mode="markers",
749
+ name="Non-Pareto Models" if use_pareto else "All Models",
750
+ marker=dict(size=12, color=list(base_delta_values), coloraxis="coloraxis", opacity=0.85),
751
+ text=list(base_names),
752
+ customdata=list(zip(base_x_values, base_y_values, base_delta_values)),
753
+ hovertemplate=(
754
+ "<b>%{text}</b><br>"
755
+ + "Params: %{customdata[0]:.2f}B<br>"
756
+ + "Compression Ratio: %{customdata[1]:.2f}%<br>"
757
+ + "vs Fit: %{customdata[2]:+.2f}%<br>"
758
+ + "<extra></extra>"
759
+ ),
760
+ )
761
+ )
762
+
763
+ # 如果使用帕累托前沿,高亮显示帕累托前沿的点
764
+ if use_pareto:
765
+ pareto_delta_values = calculate_fit_delta_percent(fit_x_values, fit_y_values, params)
766
+ fig.add_trace(
767
+ go.Scatter(
768
+ x=fit_x_values,
769
+ y=fit_y_values,
770
+ mode="markers",
771
+ name="Pareto Frontier",
772
+ marker=dict(
773
+ size=14,
774
+ color=pareto_delta_values,
775
+ coloraxis="coloraxis",
776
+ symbol="diamond",
777
+ opacity=1.0,
778
+ line=dict(color="#263238", width=1),
779
+ ),
780
+ text=fit_names,
781
+ customdata=list(zip(fit_x_values, fit_y_values, pareto_delta_values)),
782
+ hovertemplate=(
783
+ "<b>%{text}</b> (Pareto)<br>"
784
+ + "Params: %{customdata[0]:.2f}B<br>"
785
+ + "Compression Ratio: %{customdata[1]:.2f}%<br>"
786
+ + "vs Fit: %{customdata[2]:+.2f}%<br>"
787
+ + "<extra></extra>"
788
+ ),
789
+ )
790
  )
791
 
792
  # 添加拟合曲线
 
798
  fig.add_trace(
799
  go.Scatter(
800
  x=fit_x.tolist(),
801
+ y=fit_y.tolist(),
802
+ mode="lines",
803
+ name=fit_label,
804
+ line=dict(color=FIT_LINE_COLOR, width=2, dash="dash"),
805
+ hovertemplate=FIT_LINE_HOVER_TEMPLATE,
806
+ )
807
+ )
808
 
809
  title_suffix = " (Pareto Frontier)" if use_pareto else ""
810
  fig.update_layout(
811
  title={"text": f"Compression Ratio Scaling Law{title_suffix}", "x": 0.5, "xanchor": "center", "yanchor": "top"},
812
+ width=SCALING_PLOT_WIDTH,
813
+ height=SCALING_PLOT_HEIGHT,
814
+ margin=SCALING_PLOT_MARGIN,
815
  showlegend=True,
816
  legend=dict(
817
  yanchor="top",
 
829
  tickformat=".2f",
830
  range=[x_min - 0.1, x_max + 0.1],
831
  ),
832
+ yaxis=dict(
833
+ title="Compression Ratio (%)",
834
+ showgrid=True,
835
+ zeroline=False,
836
+ type="log",
837
  dtick=y_dtick,
838
  tickformat=".2f",
839
+ range=[y_min - 0.1, y_max + 0.1],
840
+ autorange="reversed",
841
+ ),
842
+ coloraxis=create_fit_delta_coloraxis(point_delta_values),
843
+ )
844
  return fig
845
 
846
 
 
860
  visible_columns=None,
861
  )
862
 
863
+ if len(new_df) == 0 or not selected_datasets:
864
+ fig = go.Figure()
865
+ fig.update_layout(
866
+ title={"text": "Scaling Law by Dataset", "x": 0.5},
867
+ width=SCALING_PLOT_WIDTH,
868
+ height=700,
869
+ margin=SCALING_PLOT_MARGIN,
870
+ )
871
+ return fig
872
 
873
  # 颜色配色方案 - 使用高对比度、饱和度高的颜色
874
  color_palette = [
 
886
 
887
  fig = go.Figure()
888
 
889
+ # 用于计算全局坐标范围
890
+ all_x_values = []
891
+ all_y_values = []
892
+ all_delta_values = []
893
 
894
  if display_mode == "average":
895
  # 平均模式:计算选中数据集的平均值
 
937
  extrapolate_max_b=SCALING_EXTRAPOLATE_MAX_B,
938
  )
939
  a, b, c = params
940
+ positive_fit_y = fit_y[fit_y > 0]
941
+ if positive_fit_y.size:
942
+ all_y_values.extend(positive_fit_y.tolist())
943
+ point_delta_values = calculate_fit_delta_percent(x_vals, y_vals, params)
944
+ all_delta_values.extend(point_delta_values)
945
+
946
+ # 构建数据集名称列表(用于hover显示)
947
+ datasets_label = f"Average of {len(selected_datasets)} datasets"
948
+
949
+ # Pareto 模式下,普通散点仅显示非 Pareto 点,避免同一点重复绘制
950
+ base_points = list(zip(x_vals, y_vals, name_vals, point_delta_values))
951
+ if use_pareto:
952
+ pareto_points = set(zip(fit_x_vals, fit_y_vals, fit_name_vals))
953
+ base_points = [p for p in base_points if (p[0], p[1], p[2]) not in pareto_points]
954
+
955
+ if base_points:
956
+ base_x_vals, base_y_vals, base_name_vals, base_delta_vals = zip(*base_points)
957
+ fig.add_trace(
958
+ go.Scatter(
959
+ x=list(base_x_vals),
960
+ y=list(base_y_vals),
961
+ mode="markers",
962
+ name=f"{datasets_label} (Non-Pareto)" if use_pareto else datasets_label,
963
+ marker=dict(size=12, color=list(base_delta_vals), coloraxis="coloraxis", opacity=0.85),
964
+ text=list(base_name_vals),
965
+ customdata=list(zip(base_x_vals, base_y_vals, base_delta_vals)),
966
+ hovertemplate=(
967
+ f"<b>%{{text}}</b><br>{datasets_label}<br>"
968
+ + "Params: %{customdata[0]:.2f}B<br>"
969
+ + "CR: %{customdata[1]:.2f}%<br>"
970
+ + "vs Fit: %{customdata[2]:+.2f}%<br>"
971
+ + "<extra></extra>"
972
+ ),
973
+ )
974
  )
975
 
976
+ # 如果使用帕累托前沿,高亮显示帕累托前沿的点
977
+ if use_pareto:
978
+ pareto_delta_vals = calculate_fit_delta_percent(fit_x_vals, fit_y_vals, params)
979
+ fig.add_trace(
980
+ go.Scatter(
981
+ x=fit_x_vals,
982
+ y=fit_y_vals,
983
+ mode="markers",
984
+ name="Pareto Frontier",
985
+ marker=dict(
986
+ size=14,
987
+ color=pareto_delta_vals,
988
+ coloraxis="coloraxis",
989
+ symbol="diamond",
990
+ opacity=1.0,
991
+ line=dict(color="#263238", width=1),
992
+ ),
993
+ text=fit_name_vals,
994
+ customdata=list(zip(fit_x_vals, fit_y_vals, pareto_delta_vals)),
995
+ hovertemplate=(
996
+ f"<b>%{{text}}</b> (Pareto)<br>{datasets_label}<br>"
997
+ + "Params: %{customdata[0]:.2f}B<br>"
998
+ + "CR: %{customdata[1]:.2f}%<br>"
999
+ + "vs Fit: %{customdata[2]:+.2f}%<br>"
1000
+ + "<extra></extra>"
1001
+ ),
1002
+ )
1003
  )
1004
 
1005
  # 添加拟合曲线
 
1011
  fig.add_trace(
1012
  go.Scatter(
1013
  x=fit_x.tolist(),
1014
+ y=fit_y.tolist(),
1015
+ mode="lines",
1016
+ name=fit_label,
1017
+ line=dict(color=FIT_LINE_COLOR, width=2, dash="dash"),
1018
+ hovertemplate=FIT_LINE_HOVER_TEMPLATE,
1019
+ )
1020
+ )
1021
  else:
1022
  # 单独显示模式:为每个数据集创建散点图和拟合线
1023
  for idx, dataset in enumerate(selected_datasets):
 
1058
  extrapolate_max_b=SCALING_EXTRAPOLATE_MAX_B,
1059
  )
1060
  a, b, c = params
1061
+ positive_fit_y = fit_y[fit_y > 0]
1062
+ if positive_fit_y.size:
1063
+ all_y_values.extend(positive_fit_y.tolist())
1064
+ point_delta_values = calculate_fit_delta_percent(x_vals, y_vals, params)
1065
+ all_delta_values.extend(point_delta_values)
1066
+
1067
+ # Pareto 模式下,普通散点仅显示非 Pareto 点,避免同一点重复绘制
1068
+ base_points = list(zip(x_vals, y_vals, name_vals, point_delta_values))
1069
+ if use_pareto:
1070
+ pareto_points = set(zip(fit_x_vals, fit_y_vals, fit_name_vals))
1071
+ base_points = [p for p in base_points if (p[0], p[1], p[2]) not in pareto_points]
1072
+
1073
+ if base_points:
1074
+ base_x_vals, base_y_vals, base_name_vals, base_delta_vals = zip(*base_points)
1075
+ fig.add_trace(
1076
+ go.Scatter(
1077
+ x=list(base_x_vals),
1078
+ y=list(base_y_vals),
1079
+ mode="markers",
1080
+ name=f"{dataset} (Non-Pareto)" if use_pareto else f"{dataset}",
1081
+ marker=dict(size=10, color=list(base_delta_vals), coloraxis="coloraxis", opacity=0.8),
1082
+ text=list(base_name_vals),
1083
+ customdata=list(zip(base_x_vals, base_y_vals, base_delta_vals)),
1084
+ hovertemplate=(
1085
+ f"<b>%{{text}}</b><br>{dataset}<br>"
1086
+ + "Params: %{customdata[0]:.2f}B<br>"
1087
+ + "CR: %{customdata[1]:.2f}%<br>"
1088
+ + "vs Fit: %{customdata[2]:+.2f}%<br>"
1089
+ + "<extra></extra>"
1090
+ ),
1091
+ legendgroup=dataset,
1092
  )
1093
  )
1094
 
1095
+ # 如果使用帕累托前沿,高亮显示帕累托前沿的点
1096
+ if use_pareto:
1097
+ pareto_delta_vals = calculate_fit_delta_percent(fit_x_vals, fit_y_vals, params)
1098
+ fig.add_trace(
1099
+ go.Scatter(
1100
+ x=fit_x_vals,
1101
+ y=fit_y_vals,
1102
+ mode="markers",
1103
+ name=f"{dataset} (Pareto)",
1104
+ marker=dict(
1105
+ size=12,
1106
+ color=pareto_delta_vals,
1107
+ coloraxis="coloraxis",
1108
+ symbol="diamond",
1109
+ opacity=1.0,
1110
+ line=dict(color="#263238", width=1),
1111
+ ),
1112
+ text=fit_name_vals,
1113
+ customdata=list(zip(fit_x_vals, fit_y_vals, pareto_delta_vals)),
1114
+ hovertemplate=(
1115
+ f"<b>%{{text}}</b> (Pareto)<br>{dataset}<br>"
1116
+ + "Params: %{customdata[0]:.2f}B<br>"
1117
+ + "CR: %{customdata[1]:.2f}%<br>"
1118
+ + "vs Fit: %{customdata[2]:+.2f}%<br>"
1119
+ + "<extra></extra>"
1120
+ ),
1121
+ legendgroup=dataset,
1122
  )
1123
  )
1124
 
 
1141
  )
1142
  )
1143
 
1144
+ if not all_x_values or not all_y_values:
1145
+ fig = go.Figure()
1146
+ fig.update_layout(
1147
+ title={"text": "Scaling Law by Dataset - No Valid Data", "x": 0.5},
1148
+ width=SCALING_PLOT_WIDTH,
1149
+ height=700,
1150
+ margin=SCALING_PLOT_MARGIN,
1151
+ )
1152
+ return fig
1153
 
1154
  # 计算全局坐标范围
1155
  x_min_val = min(all_x_values)
 
1162
 
1163
  fig.update_layout(
1164
  title={"text": "Scaling Law by Dataset", "x": 0.5, "xanchor": "center", "yanchor": "top"},
1165
+ width=SCALING_PLOT_WIDTH,
1166
+ height=700,
1167
  showlegend=True,
1168
  legend=dict(
1169
  yanchor="top",
 
1190
  dtick=y_dtick,
1191
  tickformat=".2f",
1192
  range=[y_min - 0.1, y_max + 0.1],
1193
+ autorange="reversed",
1194
+ ),
1195
+ coloraxis=create_fit_delta_coloraxis(all_delta_values),
1196
+ margin=dict(l=70, r=170, t=70, b=65), # 为图例预留空间
1197
+ )
1198
  return fig
1199
 
1200
 
title.py CHANGED
@@ -76,7 +76,10 @@ table {
76
  margin: 0 !important;
77
  }
78
  .frontier-table .params-col {
79
- width: 82px;
 
 
 
80
  }
81
  .frontier-table th,
82
  .frontier-table td {
@@ -111,7 +114,8 @@ table {
111
  .frontier-table tbody tr td:last-child {
112
  border-radius: 0 7px 7px 0;
113
  }
114
- .frontier-table td.params {
 
115
  color: var(--body-text-color-subdued);
116
  font-variant-numeric: tabular-nums;
117
  }
 
76
  margin: 0 !important;
77
  }
78
  .frontier-table .params-col {
79
+ width: 72px;
80
+ }
81
+ .frontier-table .ratio-col {
82
+ width: 64px;
83
  }
84
  .frontier-table th,
85
  .frontier-table td {
 
114
  .frontier-table tbody tr td:last-child {
115
  border-radius: 0 7px 7px 0;
116
  }
117
+ .frontier-table td.params,
118
+ .frontier-table td.ratio {
119
  color: var(--body-text-color-subdued);
120
  font-variant-numeric: tabular-nums;
121
  }