Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
80 lines
3.4 KiB
Python
80 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Capture official streaming inputs and stage timing for ARM parity probes."""
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import statistics
|
|
import sys
|
|
import time
|
|
|
|
import numpy as np
|
|
from conversion import write_model
|
|
from scipy.io import wavfile
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--source-repo", type=Path, required=True)
|
|
parser.add_argument("--models", type=Path, required=True)
|
|
parser.add_argument("--audio", type=Path, required=True)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
parser.add_argument("--threads", type=int, default=1)
|
|
args = parser.parse_args()
|
|
sys.path.insert(0, str(args.source_repo.resolve()))
|
|
from openwakeword.model import Model
|
|
|
|
sample_rate, audio = wavfile.read(args.audio)
|
|
if sample_rate != 16000 or audio.dtype != np.int16 or audio.ndim != 1:
|
|
raise ValueError("reference requires mono PCM16 at 16 kHz")
|
|
args.output.mkdir(parents=True, exist_ok=True)
|
|
records = {name: [] for name in ("mel", "embedding", "classifier")}
|
|
timings = {name: [] for name in records}
|
|
np.random.seed(0)
|
|
model = Model(
|
|
wakeword_models=[str(args.models / "alexa_v0.1.onnx")],
|
|
melspec_model_path=str(args.models / "melspectrogram.onnx"),
|
|
embedding_model_path=str(args.models / "embedding_model.onnx"),
|
|
inference_framework="onnx", ncpu=args.threads,
|
|
)
|
|
|
|
initial_features = np.array(model.preprocessor.feature_buffer, copy=True)
|
|
|
|
def capture(name, predict):
|
|
def run(value):
|
|
start = time.perf_counter_ns()
|
|
output = predict(value)
|
|
timings[name].append((time.perf_counter_ns() - start) / 1e6)
|
|
array = np.asarray(output[0] if isinstance(output, list) else output, dtype=np.float32)
|
|
records[name].append((np.array(value, copy=True), np.array(array, copy=True)))
|
|
return output
|
|
return run
|
|
|
|
model.preprocessor.melspec_model_predict = capture("mel", model.preprocessor.melspec_model_predict)
|
|
model.preprocessor.embedding_model_predict = capture("embedding", model.preprocessor.embedding_model_predict)
|
|
for key, predict in model.model_prediction_function.items():
|
|
model.model_prediction_function[key] = capture("classifier", predict)
|
|
scores = []
|
|
for offset in range(0, len(audio), 1280):
|
|
frame = audio[offset:offset + 1280]
|
|
if len(frame) < 1280:
|
|
frame = np.pad(frame, (0, 1280 - len(frame)))
|
|
scores.append(model.predict(frame))
|
|
|
|
report = {"feature_threads": args.threads, "classifier_threads": 1, "audio_seconds": len(audio) / sample_rate,
|
|
"frames": len(scores), "maximum_score": float(max(max(s.values()) for s in scores))}
|
|
for name, values in timings.items():
|
|
warm = values[1:]
|
|
report[name] = {"calls": len(values), "warm_median_ms": statistics.median(warm),
|
|
"warm_total_ms": sum(warm)}
|
|
print(json.dumps(report, indent=2))
|
|
(args.output / f"reference-t{args.threads}.json").write_text(json.dumps(report, indent=2) + "\n")
|
|
|
|
records["probabilities"] = [(audio.astype(np.float32) / 32768.0,
|
|
np.array([next(iter(frame.values())) for frame in scores], dtype=np.float32))]
|
|
write_model(args.models, args.output / "open-wake-word-reference-f32.gguf", initial_features, records)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|