haas-ja-coreml / python /convert.py
Sanami's picture
Upload folder using huggingface_hub
0d06934 verified
Raw History Blame Contribute Delete
2.35 kB
"""Convert the Haas-ja-v1.0 FastConformer encoder to CoreML (.mlpackage, fp16).
Usage: python convert.py <T_mel> [out.mlpackage]
Input: audio_signal [1,128,T_mel] f32 (zero past length), length [1] i32 -> encoded [1,1024,T_mel/8]
"""
import os, sys
# HAAS_EXPORT_DIR: dir containing the haas_onnx package (scripts/ of Sanami/haas-ja-onnx)
# HAAS_NEMO_DIR: the extracted Haas-ja-v1.0.nemo (model_config.yaml, model_weights.ckpt)
sys.path.insert(0, os.environ.get("HAAS_EXPORT_DIR", "export"))
import numpy as np, torch, coremltools as ct
from haas_onnx.load import load_model
import cmlpatch # noqa: F401
T = int(sys.argv[1])
out = sys.argv[2] if len(sys.argv) > 2 else f"encoder_T{T}.mlpackage"
class Enc(torch.nn.Module):
def __init__(self, enc):
super().__init__()
self.enc = enc
def forward(self, audio_signal, length=None):
if length is None:
length = torch.full((1,), audio_signal.shape[-1], dtype=torch.long)
y, _ = self.enc(audio_signal=audio_signal, length=length.long())
return y
model = load_model(os.environ.get("HAAS_NEMO_DIR", "nemo_x"))
enc = model.encoder.eval()
NL = int(os.environ.get("NL", "0")) # debug: keep only the first NL layers
if NL:
enc.layers = enc.layers[:NL]
NOLEN = os.environ.get("NOLEN") == "1" # debug: no length input (whole window valid)
enc.update_max_seq_length(T, device="cpu") if hasattr(enc, "update_max_seq_length") else None
w = Enc(enc).eval()
x = torch.randn(1, 128, T)
L = torch.tensor([T], dtype=torch.int32)
args = (x,) if NOLEN else (x, L)
with torch.no_grad():
ref = w(*args).numpy()
tr = torch.jit.trace(w, args, check_trace=False)
ins = [ct.TensorType("audio_signal", shape=(1, 128, T), dtype=np.float32)]
if not NOLEN:
ins.append(ct.TensorType("length", shape=(1,), dtype=np.int32))
ml = ct.convert(
tr,
inputs=ins,
outputs=[ct.TensorType("encoded", dtype=np.float32)],
convert_to="mlprogram", compute_precision=ct.precision.FLOAT16,
minimum_deployment_target=ct.target.iOS17,
)
ml.save(out)
feed = {"audio_signal": x.numpy()}
if not NOLEN:
feed["length"] = L.numpy()
y = ml.predict(feed)["encoded"]
print("shape", y.shape, "max abs diff", float(np.abs(y - ref).max()), "ref max", float(np.abs(ref).max()),
"cos", float((y * ref).sum() / np.linalg.norm(y) / np.linalg.norm(ref)))