feat: Module C v5/v6 training complete, ablations, SOTA baselines, paper updates
- Module C: BC+PPO training v5/v6 done; eval results in experiments/eval_intervention_v{5,6}.json
- Reward: v5 label-aligned constrained reward (code/src/rl/reward.py)
- Ablations: Module B (history_r, response_only, full) + Module C (wo_category_reward)
- SOTA baselines: WildGuard and ShieldGemma2b eval scripts and results
- Paper: update sections 05–08 (Module B/C description, experiments table, discussion)
- Docs: add record.md (change log), update state.md and exp.md; retire change.md
- Tools: add html-to-ppt utilities and run_shieldgemma2b.sh
- Configs: add ablation YAML configs for Module B and C
- Cleanup: remove stale reference/ PNG screenshots
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
68
tools/html_to_ppt_make.py
Normal file
68
tools/html_to_ppt_make.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
from pptx import Presentation
|
||||
from pptx.util import Inches
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3:
|
||||
raise SystemExit("Usage: python html_to_ppt_make.py <image-dir> <output-pptx>")
|
||||
|
||||
image_dir = Path(sys.argv[1])
|
||||
output_pptx = Path(sys.argv[2])
|
||||
output_pptx.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
labels_path = image_dir / "slides.json"
|
||||
labels = {}
|
||||
if labels_path.exists():
|
||||
labels = {item["index"]: item["label"] for item in json.loads(labels_path.read_text(encoding="utf-8"))}
|
||||
|
||||
image_paths = sorted(image_dir.glob("slide_*.png"))
|
||||
if not image_paths:
|
||||
raise SystemExit(f"No slide PNGs found in {image_dir}")
|
||||
|
||||
prs = Presentation()
|
||||
prs.slide_width = Inches(13.333333)
|
||||
prs.slide_height = Inches(7.5)
|
||||
|
||||
blank_layout = prs.slide_layouts[6]
|
||||
for image_path in image_paths:
|
||||
slide_num = int(image_path.stem.split("_")[-1])
|
||||
slide = prs.slides.add_slide(blank_layout)
|
||||
picture = slide.shapes.add_picture(
|
||||
str(image_path),
|
||||
0,
|
||||
0,
|
||||
width=prs.slide_width,
|
||||
height=prs.slide_height,
|
||||
)
|
||||
picture.name = labels.get(slide_num, image_path.stem)
|
||||
|
||||
prs.save(output_pptx)
|
||||
|
||||
montage_path = output_pptx.with_suffix(".preview.png")
|
||||
thumbs = []
|
||||
for image_path in image_paths:
|
||||
img = Image.open(image_path).convert("RGB")
|
||||
img.thumbnail((384, 216), Image.Resampling.LANCZOS)
|
||||
thumbs.append(ImageOps.expand(img, border=2, fill=(30, 30, 30)))
|
||||
|
||||
cols = 5
|
||||
rows = (len(thumbs) + cols - 1) // cols
|
||||
montage = Image.new("RGB", (cols * 388, rows * 220), (12, 15, 24))
|
||||
for idx, thumb in enumerate(thumbs):
|
||||
x = (idx % cols) * 388
|
||||
y = (idx // cols) * 220
|
||||
montage.paste(thumb, (x, y))
|
||||
montage.save(montage_path)
|
||||
|
||||
print(f"pptx={output_pptx}")
|
||||
print(f"preview={montage_path}")
|
||||
print(f"slides={len(image_paths)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user