Spaces:
Sleeping
Sleeping
File size: 13,799 Bytes
a668326 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | """Generate the precomputed demo artifacts in ``data/sample_outputs/``.
This does NOT call the heavy CV/ASR models. Instead it pairs hand-authored,
realistic transcripts with deterministic synthetic visual streams and then runs
them through the **real** bookmark, fusion and metrics logic.
That gives us:
* internally consistent artifacts (timeline aligns with transcript),
* a genuine exercise of the production code paths,
* stable, license-free demo data with no large video committed to git.
The script is data-driven: add another entry to ``VIDEO_SPECS`` to publish a new
sample. Two samples ship by default:
demo_001 — Thai-English ML deployment tutorial (code-switching)
demo_002 — English-only "Intro to MLOps" slide-heavy explainer
Run: python -m scripts.generate_sample_outputs
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Dict, List, Set, Tuple
from src.bookmark_generation import generate_bookmarks
from src.config import CONFIG
from src.metrics import MetricsTracker
from src.storage import write_json, sample_path
from src.timeline_fusion import fuse_timeline
from src.utils import format_timestamp
FRAME_INTERVAL_SEC = 2.0
_SCREEN_LIKE = {"tv", "laptop", "cell phone"}
# --------------------------------------------------------------------------- #
# Spec model
# --------------------------------------------------------------------------- #
@dataclass
class VideoSpec:
video_id: str
title: str
duration_sec: float
# (start, end, language, text)
segments: List[Tuple[float, float, str, str]]
# (start, end, [objects present])
visual_sections: List[Tuple[float, float, List[str]]]
scene_change_times: Set[int]
stage_timings: Dict[str, float]
search_latencies_ms: List[float]
fps: float = 30.0
width: int = 1280
height: int = 720
source: str = "Synthetic demo content (no copyrighted video committed)."
default_language: str = "en"
extra_meta: Dict[str, object] = field(default_factory=dict)
# --------------------------------------------------------------------------- #
# Builders (parameterized by spec)
# --------------------------------------------------------------------------- #
def build_transcript(spec: VideoSpec) -> List[Dict[str, object]]:
return [
{"id": i, "start_sec": s, "end_sec": e, "text": text, "language": lang}
for i, (s, e, lang, text) in enumerate(spec.segments)
]
def _objects_at(spec: VideoSpec, t: float) -> List[str]:
for s, e, objs in spec.visual_sections:
if s <= t < e:
return objs
return ["person"]
def _conf_for(label: str, t: float) -> float:
"""Deterministic, plausible confidence in a sensible per-class range."""
base = {"person": 0.90, "laptop": 0.80, "tv": 0.74, "cell phone": 0.66}.get(
label, 0.70
)
jitter = ((int(t) * 7 + len(label)) % 9) / 100.0 # 0.00..0.08, deterministic
return round(min(0.99, base + jitter), 3)
def build_visual_events(spec: VideoSpec) -> List[Dict[str, object]]:
events: List[Dict[str, object]] = []
t = 0.0
while t <= spec.duration_sec:
objs = _objects_at(spec, t)
confidence = {o: _conf_for(o, t) for o in objs}
ordered = sorted(confidence, key=confidence.get, reverse=True)
if any(o in _SCREEN_LIKE for o in ordered) and "screen" not in ordered:
ordered = ordered + ["screen"]
scene_change = round(t) in spec.scene_change_times
events.append(
{
"time_sec": round(t, 3),
"time_label": format_timestamp(t),
"visual_events": ordered,
"confidence": confidence,
"scene_change": scene_change,
"method": "yolo11n.pt+scene (precomputed sample)",
}
)
t += FRAME_INTERVAL_SEC
return events
def build_metrics(
spec: VideoSpec, n_frames: int, n_segments: int, n_visual: int, n_bm: int
) -> Dict:
tracker = MetricsTracker(video_id=spec.video_id)
tracker.stages_sec = dict(spec.stage_timings)
for ms in spec.search_latencies_ms:
tracker.record_search_latency(ms)
tracker.count("frames_processed", n_frames)
tracker.count("transcript_segments", n_segments)
tracker.count("visual_events_detected", n_visual)
tracker.count("bookmarks", n_bm)
doc = tracker.to_dict()
doc["video_metadata"] = {
"video_id": spec.video_id,
"title": spec.title,
"duration_sec": spec.duration_sec,
"fps": spec.fps,
"width": spec.width,
"height": spec.height,
"frame_count": int(spec.duration_sec * spec.fps),
"source": spec.source,
}
doc["config"] = {
"frame_interval_sec": FRAME_INTERVAL_SEC,
"asr_model_size": "base",
"visual_model": "yolo11n.pt",
"device": "Apple M4 CPU (int8)",
}
return doc
def generate_one(spec: VideoSpec) -> None:
transcript = build_transcript(spec)
visual_events = build_visual_events(spec)
bookmarks = generate_bookmarks(transcript, visual_events, config=CONFIG)
timeline = fuse_timeline(
video_id=spec.video_id,
duration_sec=spec.duration_sec,
transcript=transcript,
visual_events=visual_events,
bookmarks=bookmarks,
config=CONFIG,
)
n_visual = sum(len(v["visual_events"]) for v in visual_events)
metrics = build_metrics(
spec,
n_frames=len(visual_events),
n_segments=len(transcript),
n_visual=n_visual,
n_bm=len(bookmarks),
)
write_json(sample_path(spec.video_id, "transcript"), transcript)
write_json(sample_path(spec.video_id, "visual_events"), visual_events)
write_json(sample_path(spec.video_id, "bookmarks"), bookmarks)
write_json(sample_path(spec.video_id, "timeline"), timeline)
write_json(sample_path(spec.video_id, "metrics"), metrics)
print(
f" {spec.video_id}: segments={len(transcript)} frames={len(visual_events)} "
f"bookmarks={len(bookmarks)} visual_events={n_visual}"
)
# --------------------------------------------------------------------------- #
# Sample 1 — demo_001: Thai-English ML deployment tutorial (code-switching)
# --------------------------------------------------------------------------- #
DEMO_001 = VideoSpec(
video_id="demo_001",
title="Deploying ML Models: Model Serving & API Deployment (demo)",
duration_sec=184.0,
default_language="th",
segments=[
(0.0, 6.2, "th", "สวัสดีครับ วันนี้เราจะมาเรียนรู้เรื่อง model serving และ API deployment กันนะครับ"),
(6.2, 13.5, "th", "ก่อนอื่นเรามาดูภาพรวมของ pipeline ที่เราจะ build กันก่อน"),
(13.5, 21.0, "en", "We start by training a simple machine learning model, then we save it to disk."),
(21.0, 29.4, "th", "ขั้นตอนต่อไปคือการสร้าง REST API ด้วย FastAPI เพื่อให้ model ของเราพร้อมใช้งาน"),
(29.4, 37.8, "en", "Here you can see the project structure on my screen, with the model file and the server code."),
(37.8, 46.0, "th", "เรามาเขียน endpoint แรกกันครับ ชื่อว่า slash predict สำหรับรับ input"),
(46.0, 54.5, "en", "The predict endpoint takes a JSON payload and returns the model prediction."),
(54.5, 63.0, "th", "ตรงนี้สำคัญมากนะครับ เราต้อง validate input ก่อนเสมอเพื่อความปลอดภัย"),
(63.0, 72.2, "en", "Now let's talk about Docker. We containerize the application so it runs anywhere."),
(72.2, 80.5, "th", "ผมจะเขียน Dockerfile แบบง่าย ๆ ให้ดูนะครับ เริ่มจาก base image ของ Python"),
(80.5, 89.0, "en", "After building the image, we run the container and expose port eight thousand."),
(89.0, 98.3, "th", "คำถามที่หลายคนสงสัยคือ แล้วเราจะ deploy ขึ้น production ยังไง?"),
(98.3, 107.0, "en", "We can deploy this container to a cloud service or to Hugging Face Spaces for a demo."),
(107.0, 116.4, "th", "เรื่องของ latency ก็สำคัญครับ เราควรวัด response time ของ API เสมอ"),
(116.4, 125.0, "en", "Let me show you how to add simple metrics logging to track p50 and p95 latency."),
(125.0, 134.7, "th", "ต่อไปเป็นเรื่อง monitoring เราจะใช้ log file เก็บ metric แบบง่าย ๆ ก่อน"),
(134.7, 144.0, "en", "Finally, remember to add a health check endpoint so the platform knows the service is alive."),
(144.0, 153.5, "th", "สรุปนะครับ วันนี้เราได้เรียนรู้เรื่อง model serving, API, Docker และ deployment"),
(153.5, 162.8, "en", "In summary, you now have a complete pipeline from model to a deployed API."),
(162.8, 172.0, "th", "ถ้าชอบ video นี้ อย่าลืมกด like และ subscribe เพื่อไม่พลาด content ใหม่ ๆ นะครับ"),
(172.0, 184.0, "en", "Thank you for watching, and see you in the next tutorial. Goodbye!"),
],
visual_sections=[
(0.0, 13.0, ["person"]),
(13.0, 29.0, ["person", "tv"]),
(29.0, 63.0, ["person", "laptop", "tv"]),
(63.0, 90.0, ["person", "laptop"]),
(90.0, 134.0, ["person", "laptop", "tv"]),
(134.0, 153.0, ["person", "laptop"]),
(153.0, 162.0, ["person"]),
(162.0, 184.0, ["person", "cell phone"]),
],
scene_change_times={13, 29, 63, 90, 116, 134, 153, 162},
stage_timings={
"frame_extraction": 3.18,
"audio_extraction": 1.05,
"visual_analysis": 22.41,
"asr": 39.76,
"bookmark_generation": 0.012,
"timeline_fusion": 0.031,
},
search_latencies_ms=[3.1, 2.4, 4.8, 2.9, 3.6, 5.2, 2.7, 3.3, 4.1, 6.0],
)
# --------------------------------------------------------------------------- #
# Sample 2 — demo_002: English-only "Intro to MLOps" slide-heavy explainer
# --------------------------------------------------------------------------- #
DEMO_002 = VideoSpec(
video_id="demo_002",
title="Intro to MLOps: Datasets, Training, Serving & Monitoring (demo)",
duration_sec=150.0,
default_language="en",
segments=[
(0.0, 7.0, "en", "Welcome to this short introduction to MLOps, the practice of taking machine learning models to production."),
(7.0, 16.0, "en", "In this overview you'll see the full pipeline on the slides: data, training, serving, and monitoring."),
(16.0, 25.0, "en", "Everything starts with a good dataset. We collect, clean, and label our training data."),
(25.0, 35.0, "en", "On screen you can see the dataset summary, with the number of samples and the class balance."),
(35.0, 45.0, "en", "Next we move on to training. We fit a simple model and track its accuracy on a validation set."),
(45.0, 55.0, "en", "Here is the training curve. Notice how the validation loss flattens after a few epochs."),
(55.0, 65.0, "en", "Once training is done, we save the model and wrap it in an inference function."),
(65.0, 75.0, "en", "Now let's talk about serving. We expose the model through a REST API so other services can call it."),
(75.0, 85.0, "en", "The API has a predict endpoint that takes a JSON request and returns the model's prediction."),
(85.0, 95.0, "en", "How do we ship this to production reliably? We package everything into a Docker container."),
(95.0, 105.0, "en", "After deployment, monitoring becomes critical. We track latency, errors, and prediction quality."),
(105.0, 115.0, "en", "Let me show you the metrics dashboard, with p50 and p95 latency for each endpoint."),
(115.0, 125.0, "en", "We also add a health check endpoint so the platform can restart the service if it fails."),
(125.0, 135.0, "en", "A quick question: what happens when the data distribution changes over time? That is called drift."),
(135.0, 145.0, "en", "To summarize, MLOps connects data, training, serving, deployment, and monitoring into one pipeline."),
(145.0, 150.0, "en", "Thanks for watching this overview. In the next video we'll build the API step by step."),
],
visual_sections=[
(0.0, 16.0, ["person", "tv"]),
(16.0, 35.0, ["person", "tv"]),
(35.0, 65.0, ["person", "laptop", "tv"]),
(65.0, 95.0, ["person", "laptop"]),
(95.0, 135.0, ["person", "tv"]),
(135.0, 150.0, ["person"]),
],
scene_change_times={16, 35, 65, 95, 115, 135},
stage_timings={
"frame_extraction": 2.61,
"audio_extraction": 0.88,
"visual_analysis": 18.24,
"asr": 31.07,
"bookmark_generation": 0.010,
"timeline_fusion": 0.026,
},
search_latencies_ms=[2.2, 3.0, 2.6, 4.1, 3.4, 2.8, 3.9, 2.5, 3.1, 4.6],
)
VIDEO_SPECS = [DEMO_001, DEMO_002]
def main() -> None:
print(f"Writing sample outputs to {CONFIG.sample_outputs_dir}")
for spec in VIDEO_SPECS:
generate_one(spec)
if __name__ == "__main__":
main()
|