snapshot of https://github.com/ggml-org/whisper.cpp.git @ da54572229bcf64ba367d96c7ef15770376c4280 (2026-09-15)
Some checks failed
Bindings Tests (Go) / ubuntu-22 (push) Failing after 31s
CI (sycl) / ubuntu-22-cmake-sycl (linux/arm/v7, icx, icpx, ON) (push) Failing after 3s
CI (sycl) / ubuntu-22-cmake-sycl (linux/ppc64le, icx, icpx, ON) (push) Failing after 2s
CI (sycl) / ubuntu-22-cmake-sycl-fp16 (linux/amd64, icx, icpx, ON) (push) Failing after 3s
CI (sycl) / ubuntu-22-cmake-sycl-fp16 (linux/arm/v7, icx, icpx, ON) (push) Failing after 3s
CI (sycl) / ubuntu-22-cmake-sycl-fp16 (linux/arm64, icx, icpx, ON) (push) Failing after 3s
CI (sycl) / ubuntu-22-cmake-sycl-fp16 (linux/ppc64le, icx, icpx, ON) (push) Failing after 3s
CI (vad) / vad (push) Failing after 3s
CI (wasm) / emscripten (Release) (push) Failing after 4s
Bindings Tests (Ruby) / ubuntu-22 (push) Failing after 37s
CI (android) / android (push) Failing after 16s
CI (android) / android_java (push) Failing after 3s
CI (clang) / ubuntu-22-clang (linux/amd64, Debug) (push) Failing after 3s
CI (clang) / ubuntu-22-clang (linux/amd64, Release) (push) Failing after 2s
CI (clang) / ubuntu-22-clang (linux/ppc64le, Debug) (push) Failing after 3s
CI (clang) / ubuntu-22-clang (linux/ppc64le, Release) (push) Failing after 3s
CI (cpu) / ggml-ci-x64-cpu-low-perf (push) Failing after 3s
CI (cpu) / ggml-ci-x64-cpu-high-perf (push) Failing after 2s
CI (gcc) / ubuntu-22-gcc (linux/amd64, Debug) (push) Failing after 3s
CI (gcc) / ubuntu-22-gcc (linux/amd64, Release) (push) Failing after 3s
CI (gcc) / ubuntu-22-gcc (linux/ppc64le, Debug) (push) Failing after 3s
CI (gcc) / ubuntu-22-gcc (linux/ppc64le, Release) (push) Failing after 4s
CI (gcc) / ubuntu-22-gcc-arm-v7 (linux/arm/v7, Debug) (push) Failing after 3s
CI (gcc) / ubuntu-22-gcc-arm-v7 (linux/arm/v7, Release) (push) Failing after 3s
CI (quantize) / quantize (push) Failing after 3s
CI (sanitize) / ubuntu-22-gcc-sanitized (ADDRESS) (push) Failing after 3s
CI (sanitize) / ubuntu-22-gcc-sanitized (THREAD) (push) Failing after 3s
CI (sanitize) / ubuntu-22-gcc-sanitized (UNDEFINED) (push) Failing after 2s
CI (sycl) / ubuntu-22-cmake-sycl (linux/amd64, icx, icpx, ON) (push) Failing after 3s
CI (sycl) / ubuntu-22-cmake-sycl (linux/arm64, icx, icpx, ON) (push) Failing after 3s
Examples WASM / deploy-wasm-github-pages (push) Failing after 3s
Examples Tests / addon_node-ubuntu-22 (20.x) (push) Failing after 3s
Examples Tests / addon_node-ubuntu-22 (22.x) (push) Failing after 2s
CI (coreml) / coreml-base-en (push) Has been cancelled
CI (self-hosted) / gpu-vulkan (push) Has been cancelled
CI (self-hosted) / npu-amd-windows (push) Has been cancelled
CI (self-hosted) / gpu-vulkan-nvidia-cm2 (push) Has been cancelled
CI (clang) / ubuntu-22-clang-arm64 (Debug) (push) Has been cancelled
CI (clang) / ubuntu-22-clang-arm64 (Release) (push) Has been cancelled
CI (self-hosted) / gpu-cuda (push) Has been cancelled
CI (self-hosted) / gpu-vulkan-nvidia-cm (push) Has been cancelled
CI (cpu) / ggml-ci-arm64-cpu-low-perf (push) Has been cancelled
CI (cpu) / ggml-ci-arm64-cpu-high-perf (push) Has been cancelled
CI (cpu) / ggml-ci-arm64-cpu-high-perf-sve (push) Has been cancelled
CI (freebsd) / freeBSD-latest (push) Has been cancelled
CI (self-hosted) / gpu-metal (push) Has been cancelled
CI (gcc) / ubuntu-22-gcc-arm64 (Debug) (push) Has been cancelled
CI (gcc) / ubuntu-22-gcc-arm64 (Release) (push) Has been cancelled
CI (macOS) / macOS-latest (generic/platform=iOS) (push) Has been cancelled
CI (macOS) / macOS-latest (generic/platform=macOS) (push) Has been cancelled
CI (macOS) / macOS-latest (generic/platform=tvOS) (push) Has been cancelled
CI (self-hosted) / npu-amd-linux (push) Has been cancelled
CI (windows) / windows-msys2 (Release, clang, clang-x86_64, CLANG64) (push) Has been cancelled
CI (windows) / windows-msys2 (Release, gcc, ucrt-x86_64, UCRT64) (push) Has been cancelled
Publish Docker image / Push Docker image to Docker Hub (map[dockerfile:.devops/main-cuda.Dockerfile platform:linux/amd64 runs_on:ubuntu-24.04 tag:main-cuda]) (push) Has been cancelled
Publish Docker image / Push Docker image to Docker Hub (map[dockerfile:.devops/main-intel.Dockerfile platform:linux/amd64 runs_on:ubuntu-24.04 tag:main-intel]) (push) Has been cancelled
Publish Docker image / Push Docker image to Docker Hub (map[dockerfile:.devops/main-musa.Dockerfile platform:linux/amd64 runs_on:ubuntu-24.04 tag:main-musa]) (push) Has been cancelled
Publish Docker image / Push Docker image to Docker Hub (map[dockerfile:.devops/main-vulkan.Dockerfile platform:linux/amd64 runs_on:ubuntu-24.04 tag:main-vulkan]) (push) Has been cancelled
Publish Docker image / Push Docker image to Docker Hub (map[dockerfile:.devops/main-vulkan.Dockerfile platform:linux/arm64 runs_on:ubuntu-24.04-arm tag:main-vulkan-arm64]) (push) Has been cancelled
Publish Docker image / Push Docker image to Docker Hub (map[dockerfile:.devops/main.Dockerfile platform:linux/amd64 runs_on:ubuntu-24.04 tag:main]) (push) Has been cancelled
Publish Docker image / Push Docker image to Docker Hub (map[dockerfile:.devops/main.Dockerfile platform:linux/arm64 runs_on:ubuntu-24.04-arm tag:main-arm64]) (push) Has been cancelled
Mark inactive issues stale / mark-issues (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-15 17:23:35 +05:00
commit 948ef47106
1955 changed files with 719115 additions and 0 deletions

View File

@@ -0,0 +1,337 @@
#!/usr/bin/env python3
# Convert Parakeet TDT model from NeMo format to ggml format
#
# Usage: python convert-parakeet-to-ggml.py --model parakeet-model.nemo --output-dir output-dir [--use-f32]
#
# The NeMo file is a tar archive containing:
# - model_weights.ckpt (PyTorch checkpoint)
# - model_config.yaml (model configuration)
# - tokenizer files
#
# This script extracts the NeMo archive, loads the model weights and configuration,
# and saves them in ggml format compatible with whisper.cpp.
#
import torch
import argparse
import io
import os
import sys
import struct
import tarfile
import tempfile
import shutil
import yaml
import numpy as np
from pathlib import Path
from typing import Optional
def hz_to_mel(freq):
return 2595.0 * np.log10(1.0 + freq / 700.0)
def mel_to_hz(mel):
return 700.0 * (10.0**(mel / 2595.0) - 1.0)
def extract_nemo_archive(nemo_path, extract_dir):
print(f"Extracting {nemo_path} to {extract_dir}")
with tarfile.open(nemo_path, 'r') as tar:
tar.extractall(path=extract_dir)
print("Extraction complete")
def load_model_config(config_path):
with open(config_path, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
return config
def load_tokenizer(extract_dir, config):
tokenizer_model_path = None
tokenizer_vocab_path = None
for file in os.listdir(extract_dir):
if file.endswith('_tokenizer.model'):
tokenizer_model_path = os.path.join(extract_dir, file)
elif file.endswith('tokenizer.vocab'):
tokenizer_vocab_path = os.path.join(extract_dir, file)
if not tokenizer_model_path:
raise FileNotFoundError("Tokenizer model file not found")
if not tokenizer_vocab_path:
raise FileNotFoundError("Tokenizer vocab file not found")
tokens = {}
with open(tokenizer_vocab_path, 'r', encoding='utf-8') as f:
for idx, line in enumerate(f):
parts = line.strip().split('\t')
if len(parts) >= 1:
token = parts[0]
tokens[token.encode('utf-8')] = idx
print(f"Loaded {len(tokens)} tokens from {os.path.basename(tokenizer_vocab_path)}")
if len(tokens) != 8192:
print(f"WARNING: Expected 8192 tokens, got {len(tokens)}")
return tokens
def write_tensor(fout, name, data, use_f16=True, force_f32=False):
if 'pre_encode.conv' in name and 'bias' in name and len(data.shape) == 1:
data = data.reshape(1, -1, 1, 1)
print(f" Reshaped conv bias {name} to {data.shape}")
n_dims = len(data.shape)
ftype = 1 if use_f16 and not force_f32 else 0
if force_f32:
data = data.astype(np.float32)
elif use_f16:
if n_dims < 2 or 'bias' in name or 'norm' in name or \
('pre_encode.conv' in name and n_dims == 4) or \
'depthwise_conv.weight' in name:
data = data.astype(np.float32)
ftype = 0
else:
data = data.astype(np.float16)
else:
data = data.astype(np.float32)
dims_reversed = [data.shape[n_dims - 1 - i] for i in range(n_dims)]
print(f"Processing: {name} {list(data.shape)}, dtype: {data.dtype}, n_dims: {n_dims}, reversed: {dims_reversed}")
name_bytes = name.encode('utf-8')
fout.write(struct.pack("iii", n_dims, len(name_bytes), ftype))
for i in range(n_dims):
fout.write(struct.pack("i", data.shape[n_dims - 1 - i]))
fout.write(name_bytes)
data.tofile(fout)
def convert_parakeet_to_ggml(nemo_path, output_dir, use_f16=True, out_name=None):
nemo_path = Path(nemo_path)
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
# Create temporary directory for extraction
with tempfile.TemporaryDirectory() as temp_dir:
extract_nemo_archive(nemo_path, temp_dir)
config_path = os.path.join(temp_dir, 'model_config.yaml')
config = load_model_config(config_path)
print("Model configuration:")
print(f" Sample rate: {config['sample_rate']}")
print(f" Encoder layers: {config['encoder']['n_layers']}")
print(f" Encoder d_model: {config['encoder']['d_model']}")
print(f" Mel features: {config['preprocessor']['features']}")
weights_path = os.path.join(temp_dir, 'model_weights.ckpt')
print(f"\nLoading model weights from {weights_path}")
checkpoint = torch.load(weights_path, map_location='cpu')
# Extract state dict
if 'state_dict' in checkpoint:
state_dict = checkpoint['state_dict']
else:
state_dict = checkpoint
print(f"Loaded {len(state_dict)} tensors")
# Load tokenizer
print("\nLoading tokenizer...")
tokens = load_tokenizer(temp_dir, config)
print(f"Loaded {len(tokens)} tokens")
# Prepare hyperparameters for the Parakeet ggml format.
hparams = {
'n_audio_ctx': 5000,
'n_audio_state': config['encoder']['d_model'],
'n_audio_head': config['encoder']['n_heads'],
'n_audio_layer': config['encoder']['n_layers'],
'n_mels': config['preprocessor']['features'],
'n_fft': config['preprocessor']['n_fft'],
'subsampling_factor': config['encoder']['subsampling_factor'],
'n_subsampling_channels': config['encoder']['subsampling_conv_channels'],
'n_conv_kernel': config['encoder']['conv_kernel_size'],
'n_pred_dim': config['decoder']['prednet']['pred_hidden'],
'n_pred_layers': config['decoder']['prednet']['pred_rnn_layers'],
'n_vocab': config['decoder']['vocab_size'],
'n_tdt_durations': config['model_defaults']['num_tdt_durations'],
'n_max_tokens': config['decoding']['greedy']['max_symbols'],
}
print("\nGGML hyperparameters:")
for key, value in hparams.items():
print(f" {key}: {value}")
# Create output file
if out_name:
fname_out = output_dir / out_name
else:
fname_out = output_dir / ("ggml-model-f32.bin" if not use_f16 else "ggml-model.bin")
print(f"\nWriting to {fname_out}")
with open(fname_out, 'wb') as fout:
# Write magic number
fout.write(struct.pack("i", 0x67676d6c)) # 'ggml' in hex
# Write hyperparameters
fout.write(struct.pack("i", hparams['n_vocab']))
fout.write(struct.pack("i", hparams['n_audio_ctx']))
fout.write(struct.pack("i", hparams['n_audio_state']))
fout.write(struct.pack("i", hparams['n_audio_head']))
fout.write(struct.pack("i", hparams['n_audio_layer']))
fout.write(struct.pack("i", hparams['n_mels']))
fout.write(struct.pack("i", 1 if use_f16 else 0))
fout.write(struct.pack("i", hparams['n_fft']))
fout.write(struct.pack("i", hparams['subsampling_factor']))
fout.write(struct.pack("i", hparams['n_subsampling_channels']))
fout.write(struct.pack("i", hparams['n_conv_kernel']))
fout.write(struct.pack("i", hparams['n_pred_dim']))
fout.write(struct.pack("i", hparams['n_pred_layers']))
fout.write(struct.pack("i", hparams['n_tdt_durations']))
fout.write(struct.pack("i", hparams['n_max_tokens']))
# Extract mel filterbank from model
fb_key = None
for key in state_dict.keys():
if 'featurizer.fb' in key or 'filterbank' in key.lower():
fb_key = key
break
if not fb_key:
print("\nERROR: Mel filterbank not found in model!")
print("Expected tensor with 'featurizer.fb' or 'filterbank' in name")
print("\nAvailable preprocessor tensors:")
for key in sorted(state_dict.keys()):
if 'preprocessor' in key or 'featurizer' in key:
print(f" {key}: {state_dict[key].shape}")
raise ValueError("Mel filterbank tensor not found in model")
print(f"\nUsing model's mel filterbank from: {fb_key}")
mel_filters = state_dict[fb_key].squeeze().numpy().astype(np.float32)
print(f" Filterbank shape: {mel_filters.shape}")
print(f" Filterbank min/max values: {mel_filters.min():.6f} / {mel_filters.max():.6f}")
print(f" Filterbank non-zero elements: {np.count_nonzero(mel_filters)} / {mel_filters.size}")
print(f" First row sum: {mel_filters[0].sum():.6f}")
if len(mel_filters.shape) != 2:
raise ValueError(f"Expected 2D filterbank, got shape {mel_filters.shape}")
n_mels, n_freqs = mel_filters.shape
fout.write(struct.pack("i", n_mels)) # n_mel
fout.write(struct.pack("i", n_freqs)) # n_fb (frequency bins)
# Write mel filterbank
for i in range(n_mels):
for j in range(n_freqs):
fout.write(struct.pack("f", mel_filters[i, j]))
# Extract window function from model
window_key = None
for key in state_dict.keys():
if 'featurizer.window' in key or 'preproc' in key and 'window' in key:
window_key = key
break
if not window_key:
print("\nERROR: Window function not found in model!")
print("Expected tensor with 'featurizer.window' in name")
raise ValueError("Window function tensor not found in model")
print(f"\nUsing model's window function from: {window_key}")
window = state_dict[window_key].squeeze().numpy().astype(np.float32)
print(f" Window shape: {window.shape}")
print(f" Window min/max values: {window.min():.6f} / {window.max():.6f}")
print(f" Window non-zero elements: {np.count_nonzero(window)} / {window.size}")
print(f" Window sum: {window.sum():.6f}")
if len(window.shape) != 1:
raise ValueError(f"Expected 1D window, got shape {window.shape}")
n_window = window.shape[0]
fout.write(struct.pack("i", n_window))
# Write window function
for i in range(n_window):
fout.write(struct.pack("f", window[i]))
# Write TDT durations
tdt_durations = config['model_defaults']['tdt_durations']
if len(tdt_durations) != hparams['n_tdt_durations']:
raise ValueError(f"TDT durations count mismatch: {len(tdt_durations)} vs {hparams['n_tdt_durations']}")
for duration in tdt_durations:
fout.write(struct.pack("I", duration))
fout.write(struct.pack("i", len(tokens)))
for token_bytes, idx in sorted(tokens.items(), key=lambda x: x[1]):
fout.write(struct.pack("i", len(token_bytes)))
fout.write(token_bytes)
# Pre-collect prediction LSTM input-hidden biases so they can be
# folded into the hidden-hidden bias during the main write loop.
lstm_prefix = 'decoder.prediction.dec_rnn.lstm'
pred_bias_ih = {}
for key, t in state_dict.items():
if f'{lstm_prefix}.bias_ih_l' in key:
layer_idx = int(key.rsplit('bias_ih_l', 1)[1])
pred_bias_ih[layer_idx] = t.squeeze().numpy().astype(np.float32)
print("\nConverting model weights...")
for name, tensor in state_dict.items():
# Skip the filterbank and window - already written in preprocessing section
if name == fb_key:
continue
if name == window_key:
continue
# bias_ih is folded into bias_hh below; skip writing it separately
if f'{lstm_prefix}.bias_ih_l' in name:
continue
# Don't squeeze Conv2d weights - they need to preserve all 4 dimensions
if 'conv' in name and 'weight' in name and len(tensor.shape) == 4:
data = tensor.numpy()
else:
data = tensor.squeeze().numpy()
# For prediction LSTM weights/biases:
# Fold bias_ih into bias_hh (bias_ih already skipped above).
# Reorder gates (input, forget, cell, output) from PyTorch layout
# [i, f, g, o] to [i, f, o, g] so the three sigmoid-gated outputs
# (i, f, o) are contiguous.
if name.startswith(f'{lstm_prefix}.'):
if f'{lstm_prefix}.bias_hh_l' in name:
layer_idx = int(name.rsplit('bias_hh_l', 1)[1])
data = data.astype(np.float32) + pred_bias_ih[layer_idx]
name = name.replace('bias_hh_l', 'bias_h_l')
h = data.shape[0] // 4
data = np.concatenate([data[:h], data[h:2*h], data[3*h:], data[2*h:3*h]], axis=0)
write_tensor(fout, name, data, use_f16=use_f16)
print(f"\nConversion complete!")
print(f"Output file: {fname_out}")
print(f"File size: {fname_out.stat().st_size / (1024**2):.2f} MB")
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Convert Parakeet TDT model from NeMo format to ggml format'
)
parser.add_argument('--model', type=str, required=True,
help='Path to Parakeet .nemo model file')
parser.add_argument('--out-dir', type=str, required=True,
help='Directory to write ggml model file')
parser.add_argument('--use-f32', action='store_true', default=False,
help='Use f32 instead of f16 (default: f16)')
parser.add_argument('--out-name', type=str, default=None,
help='Output file name (default: ggml-model.bin or ggml-model-f32.bin)')
args = parser.parse_args()
if not os.path.exists(args.model):
print(f"Error: {args.model} not found")
sys.exit(1)
use_f16 = not args.use_f32
convert_parakeet_to_ggml(args.model, args.out_dir, use_f16, args.out_name)