#include "encoder.h"
#include "conformer/conformer.h"
#include "ggml.h"
#include "transcribe-debug.h"
#include "transcribe-flash-policy.h"
#include "transcribe-log.h"
#include "weights.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
namespace transcribe::parakeet {
namespace {
namespace conf = transcribe::conformer;
constexpr int kWindowedChunkMinWindowCount = 12;
bool detect_direct_dw_in_block(const char * backend) {
(void) backend;
return conf::resolve_conv_direct("TRANSCRIBE_CONV_DIRECT_DW", "TRANSCRIBE_CONV_NO_DIRECT_DW",
true);
}
bool detect_direct_dw_in_pre_encode(const char * backend) {
const bool is_metal =
backend != nullptr && (std::strstr(backend, "Metal") != nullptr || std::strstr(backend, "metal") != nullptr);
return conf::resolve_conv_direct("TRANSCRIBE_CONV_DIRECT_DW", "TRANSCRIBE_CONV_NO_DIRECT_DW",
!is_metal);
}
conf::PreEncodeView to_view(const ParakeetPreEncode & pe) {
conf::PreEncodeView v;
v.conv0_w = pe.conv0_w;
v.conv0_b = pe.conv0_b;
v.conv2_w = pe.conv2_w;
v.conv2_b = pe.conv2_b;
v.conv3_w = pe.conv3_w;
v.conv3_b = pe.conv3_b;
v.conv5_w = pe.conv5_w;
v.conv5_b = pe.conv5_b;
v.conv6_w = pe.conv6_w;
v.conv6_b = pe.conv6_b;
v.out_w = pe.out_w;
v.out_b = pe.out_b;
return v;
}
conf::BlockView to_view(const ParakeetBlock & b) {
conf::BlockView v;
v.norm_ff1_w = b.norm_ff1_w;
v.norm_ff1_b = b.norm_ff1_b;
v.ff1_lin1_w = b.ff1_lin1_w;
v.ff1_lin1_b = b.ff1_lin1_b; v.ff1_lin2_w = b.ff1_lin2_w;
v.ff1_lin2_b = b.ff1_lin2_b;
v.norm_attn_w = b.norm_attn_w;
v.norm_attn_b = b.norm_attn_b;
v.attn_q_w = b.attn_q_w;
v.attn_q_b = b.attn_q_b;
v.attn_k_w = b.attn_k_w;
v.attn_k_b = b.attn_k_b;
v.attn_v_w = b.attn_v_w;
v.attn_v_b = b.attn_v_b;
v.attn_out_w = b.attn_out_w;
v.attn_out_b = b.attn_out_b;
v.attn_pos_w = b.attn_pos_w;
v.attn_pos_u = b.attn_pos_u;
v.attn_pos_v = b.attn_pos_v;
v.norm_conv_w = b.norm_conv_w;
v.norm_conv_b = b.norm_conv_b;
v.conv_pw1_w = b.conv_pw1_w;
v.conv_pw1_b = b.conv_pw1_b;
v.conv_dw_w = b.conv_dw_w;
v.conv_dw_b = b.conv_dw_b;
v.conv_pw2_w = b.conv_pw2_w;
v.conv_pw2_b = b.conv_pw2_b;
v.conv_bn_fused_scale = b.conv_bn_fused_scale;
v.conv_bn_fused_bias = b.conv_bn_fused_bias;
v.conv_ln_w = b.conv_bn_w;
v.conv_ln_b = b.conv_bn_b;
v.norm_ff2_w = b.norm_ff2_w;
v.norm_ff2_b = b.norm_ff2_b;
v.ff2_lin1_w = b.ff2_lin1_w;
v.ff2_lin1_b = b.ff2_lin1_b;
v.ff2_lin2_w = b.ff2_lin2_w;
v.ff2_lin2_b = b.ff2_lin2_b;
v.norm_out_w = b.norm_out_w;
v.norm_out_b = b.norm_out_b;
return v;
}
struct Block0DumpSink {
EncoderDumps * dumps;
};
void parakeet_block0_observer_cb(void * user, const char * tag, ggml_tensor * t) {
auto * sink = static_cast<Block0DumpSink *>(user);
if (sink == nullptr || sink->dumps == nullptr || t == nullptr) {
return;
}
char name_buf[64];
std::snprintf(name_buf, sizeof(name_buf), "enc.block.0.%s", tag);
ggml_set_name(t, name_buf);
if (std::strcmp(tag, "after_ff1") == 0) {
sink->dumps->block0_after_ff1 = t;
transcribe::debug::mark_tensor_for_dump(t);
} else if (std::strcmp(tag, "after_attn") == 0) {
sink->dumps->block0_after_attn = t;
transcribe::debug::mark_tensor_for_dump(t);
} else if (std::strcmp(tag, "after_conv") == 0) {
sink->dumps->block0_after_conv = t;
transcribe::debug::mark_tensor_for_dump(t);
} else if (std::strcmp(tag, "after_ff2") == 0) {
sink->dumps->block0_after_ff2 = t;
transcribe::debug::mark_tensor_for_dump(t);
} else if (std::strcmp(tag, "out") == 0) {
sink->dumps->block0_out = t;
transcribe::debug::mark_tensor_for_dump(t);
}
}
struct SubBlockSink {
int block_idx;
EncoderDumps * dumps;
};
void parakeet_subblock_observer_cb(void * user, const char * tag, ggml_tensor * t) {
auto * sink = static_cast<SubBlockSink *>(user);
if (sink == nullptr || sink->dumps == nullptr || t == nullptr) {
return;
}
const char * short_tag = tag;
if (std::strcmp(tag, "after_ff1") == 0) {
short_tag = "ff1";
} else if (std::strcmp(tag, "after_attn") == 0) {
short_tag = "attn";
} else if (std::strcmp(tag, "after_conv") == 0) {
short_tag = "conv";
} else if (std::strcmp(tag, "after_ff2") == 0) {
short_tag = "ff2";
} else if (std::strcmp(tag, "out") == 0) {
return; }
char name_buf[64];
std::snprintf(name_buf, sizeof(name_buf), "enc.block.%d.%s", sink->block_idx, short_tag);
ggml_set_name(t, name_buf);
transcribe::debug::mark_tensor_for_dump(t);
sink->dumps->sub_block_dumps.emplace_back(std::string(name_buf), t);
}
std::vector<int> parse_sub_block_env(int n_layers) {
std::vector<int> out;
const char * raw = transcribe::debug::dump_sub_blocks_spec();
if (raw == nullptr || *raw == '\0') {
return out;
}
const char * p = raw;
while (*p != '\0') {
while (*p == ',' || *p == ' ' || *p == '\t') {
++p;
}
if (*p == '\0') {
break;
}
char * end = nullptr;
long v = std::strtol(p, &end, 10);
if (end == p) {
break; }
if (v >= 0 && v < n_layers) {
out.push_back(static_cast<int>(v));
}
p = end;
}
std::sort(out.begin(), out.end());
out.erase(std::unique(out.begin(), out.end()), out.end());
return out;
}
ggml_tensor * build_spk_kernel_ff(ggml_context * ctx, ggml_tensor * x, const ParakeetSpkKernelFF & ff) {
ggml_tensor * h = ggml_mul_mat(ctx, ff.lin0_w, x);
h = ggml_add(ctx, h, ff.lin0_b);
h = ggml_relu(ctx, h);
ggml_tensor * y = ggml_mul_mat(ctx, ff.lin3_w, h);
y = ggml_add(ctx, y, ff.lin3_b);
return y;
}
ggml_tensor * apply_spk_kernel_injection(ggml_context * ctx,
ggml_tensor * x,
const ParakeetWeights & w,
const ParakeetHParams & hp,
ggml_tensor * spk_mask = nullptr,
ggml_tensor * bg_mask = nullptr) {
if (!hp.has_spk_kernel) {
return x;
}
for (const auto & kernel : w.spk_kernels) {
if (kernel.layer != 0) {
continue; }
if (spk_mask == nullptr) {
ggml_tensor * x_spk = build_spk_kernel_ff(ctx, x, kernel.spk);
x = ggml_add(ctx, x, x_spk);
if (kernel.has_bg) {
ggml_tensor * hb = ggml_relu(ctx, kernel.bg.lin0_b);
ggml_tensor * c_bg = ggml_mul_mat(ctx, kernel.bg.lin3_w, hb);
c_bg = ggml_add(ctx, c_bg, kernel.bg.lin3_b);
x = ggml_add(ctx, x, c_bg);
}
} else {
ggml_tensor * x_spk = build_spk_kernel_ff(ctx, ggml_mul(ctx, x, spk_mask), kernel.spk);
x = ggml_add(ctx, x, x_spk);
if (kernel.has_bg && bg_mask != nullptr) {
ggml_tensor * x_bg = build_spk_kernel_ff(ctx, ggml_mul(ctx, x, bg_mask), kernel.bg);
x = ggml_add(ctx, x, x_bg);
}
}
}
return x;
}
}
EncoderBuild build_encoder_graph(ggml_context * ctx,
const ParakeetWeights & w,
const ParakeetHParams & hp,
int n_mel_frames,
ggml_type kv_type,
const char * backend_name,
const BufferedStreamMaskOverride * buf_mask,
int n_batch,
bool batch_var_len,
bool spk_supervision,
int mt_keep_frames) {
if (n_batch < 1) {
n_batch = 1;
}
const bool var_len_masks = batch_var_len && (n_batch > 1 || hp.kestrel_length_masking);
conf::ConvPolicy policy{};
policy.pre_encode_mask_after_stride = hp.kestrel_length_masking;
policy.pre_encode_f32_pointwise = hp.kestrel_length_masking;
policy.direct_pw = conf::detect_direct_pw(backend_name);
policy.direct_conv0_in_pre_encode = true;
policy.direct_dw_in_block = detect_direct_dw_in_block(backend_name);
policy.direct_dw_in_pre_encode = detect_direct_dw_in_pre_encode(backend_name);
policy.causal_pre_encode = (hp.enc_att_context_style == ParakeetHParams::AttContextStyle::ChunkedLimited);
EncoderBuild eb{};
if (ctx == nullptr || n_mel_frames <= 0) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet encoder: invalid arg "
"(ctx=%p, n_mel_frames=%d)",
static_cast<void *>(ctx), n_mel_frames);
return eb;
}
if (hp.enc_subsampling_factor != 8) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet encoder: unsupported subsampling_factor=%d "
"(only 8 implemented)",
hp.enc_subsampling_factor);
return eb;
}
if (hp.fe_num_mels <= 0 || (hp.fe_num_mels % hp.enc_subsampling_factor) != 0) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet encoder: n_mels=%d not divisible by "
"subsampling_factor=%d",
hp.fe_num_mels, hp.enc_subsampling_factor);
return eb;
}
eb.mel_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, n_mel_frames, hp.fe_num_mels, 1, n_batch);
if (eb.mel_in == nullptr) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR, "parakeet encoder: failed to allocate mel_in tensor");
return eb;
}
ggml_set_name(eb.mel_in, "mel.in");
ggml_set_input(eb.mel_in);
transcribe::debug::mark_tensor_for_dump(eb.mel_in);
conf::PreEncodeValidMasks pe_masks;
const bool mask_pre_encode = var_len_masks;
ggml_tensor * x = conf::build_pre_encode(ctx, to_view(w.pre_encode), eb.mel_in, policy,
"enc.pre_encode",
"parakeet", mask_pre_encode ? &pe_masks : nullptr);
if (mask_pre_encode) {
eb.pre_encode_mask_s1_in = pe_masks.mask_s1;
eb.pre_encode_mask_s2_in = pe_masks.mask_s2;
eb.pre_encode_mask_s3_in = pe_masks.mask_s3;
eb.pre_encode_extent_s2_in = pe_masks.mask_s2_extent;
eb.pre_encode_extent_s3_in = pe_masks.mask_s3_extent;
}
if (x == nullptr) {
return eb;
}
eb.dumps.pre_encode_out = x;
transcribe::debug::mark_tensor_for_dump(x);
if (hp.enc_xscaling) {
const float scale = std::sqrt(static_cast<float>(hp.enc_d_model));
x = ggml_scale(ctx, x, scale);
x = conf::named(x, "enc.pre_encode.xscaled");
}
if (spk_supervision && n_batch == 1 && mt_keep_frames > 0) {
ggml_tensor * keep = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, mt_keep_frames);
ggml_set_name(keep, "mt_keep.in");
ggml_set_input(keep);
eb.mt_keep_in = keep;
x = ggml_get_rows(ctx, x, keep);
x = conf::named(x, "enc.pre_encode.mt_gathered");
}
if (hp.has_spk_kernel) {
ggml_tensor * spk_mask = nullptr;
ggml_tensor * bg_mask = nullptr;
if (spk_supervision && n_batch == 1) {
const int64_t T_inj = x->ne[1];
spk_mask = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 1, T_inj);
ggml_set_name(spk_mask, "spk_mask.in");
ggml_set_input(spk_mask);
bg_mask = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 1, T_inj);
ggml_set_name(bg_mask, "bg_mask.in");
ggml_set_input(bg_mask);
eb.spk_mask_in = spk_mask;
eb.bg_mask_in = bg_mask;
}
x = apply_spk_kernel_injection(ctx, x, w, hp, spk_mask, bg_mask);
x = conf::named(x, "enc.pre_encode.spk_injected");
}
if (!w.blocks.empty()) {
const int64_t T_enc = x->ne[1];
const bool is_chunked =
(hp.enc_att_context_style == ParakeetHParams::AttContextStyle::ChunkedLimited) ||
(hp.enc_att_context_style == ParakeetHParams::AttContextStyle::ChunkedLimitedWithRc && buf_mask != nullptr);
const int chunk_size = hp.enc_att_context_right + 1;
const int left_chunks = chunk_size > 0 ? hp.enc_att_context_left / chunk_size : 0;
const int chunk_window = (left_chunks + 1) * chunk_size;
const bool windowed_chunked = hp.enc_att_context_style == ParakeetHParams::AttContextStyle::ChunkedLimited &&
n_batch == 1 && !var_len_masks && chunk_size > 0 &&
hp.enc_att_context_left >= 0 &&
T_enc > kWindowedChunkMinWindowCount * chunk_window;
eb.chunked_windowed = windowed_chunked;
const bool is_local_pe = (!is_chunked) && (hp.enc_att_context_left >= 0 && hp.enc_att_context_right >= 0);
const int64_t pos_len =
windowed_chunked ? static_cast<int64_t>(chunk_window + chunk_size - 1) :
is_local_pe ? static_cast<int64_t>(hp.enc_att_context_left + hp.enc_att_context_right + 1) :
(2 * T_enc - 1);
eb.pos_emb_in = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, hp.enc_d_model, pos_len);
ggml_set_name(eb.pos_emb_in, "pos_emb.in");
ggml_set_input(eb.pos_emb_in);
ggml_tensor * chunked_mask_in = nullptr;
if (is_chunked) {
if (windowed_chunked) {
const int64_t n_chunks = (T_enc + chunk_size - 1) / chunk_size;
chunked_mask_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, chunk_window, chunk_size, 1, n_chunks);
} else {
chunked_mask_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, T_enc, T_enc, 1, 1);
}
ggml_set_name(chunked_mask_in, "attn.chunked_mask.in");
ggml_set_input(chunked_mask_in);
eb.chunked_mask_in = chunked_mask_in;
}
ggml_tensor * conv_pad_mask_in = nullptr;
if (buf_mask != nullptr && buf_mask->valid_frames >= 0 && buf_mask->valid_frames < T_enc) {
conv_pad_mask_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, T_enc, 1, 1, 1);
ggml_set_name(conv_pad_mask_in, "conv.pad_mask.in");
ggml_set_input(conv_pad_mask_in);
eb.conv_pad_mask_in = conv_pad_mask_in;
}
ggml_tensor * attn_pad_mask_in = nullptr;
if (var_len_masks) {
attn_pad_mask_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, T_enc, 1, 1, n_batch);
ggml_set_name(attn_pad_mask_in, "attn.pad_mask.in");
ggml_set_input(attn_pad_mask_in);
eb.attn_pad_mask_in = attn_pad_mask_in;
conv_pad_mask_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, T_enc, 1, n_batch, 1);
ggml_set_name(conv_pad_mask_in, "conv.pad_mask.batch.in");
ggml_set_input(conv_pad_mask_in);
eb.conv_pad_mask_in = conv_pad_mask_in;
}
conf::BlockParams bparams{};
bparams.d_model = hp.enc_d_model;
bparams.n_head = hp.enc_n_heads;
bparams.conv_kernel = hp.enc_conv_kernel;
bparams.kv_type = kv_type;
bool enc_use_flash = true, dec_use_flash = false;
transcribe::flash::apply_env_overrides(enc_use_flash, dec_use_flash);
bparams.use_flash = enc_use_flash;
bparams.policy = policy;
bparams.att_context_left = hp.enc_att_context_left;
bparams.att_context_right = hp.enc_att_context_right;
bparams.att_context_style = is_chunked ? conf::BlockParams::AttContextStyle::ChunkedLimited :
conf::BlockParams::AttContextStyle::Regular;
bparams.chunked_windowed = windowed_chunked;
bparams.attn_chunked_mask = chunked_mask_in;
bparams.attn_pad_mask = attn_pad_mask_in;
bparams.conv_context_left = hp.enc_conv_context_left;
bparams.conv_context_right = hp.enc_conv_context_right;
bparams.conv_pad_mask = conv_pad_mask_in;
bparams.conv_norm_type = (hp.enc_conv_norm_type == ParakeetHParams::ConvNormType::LayerNorm) ?
conf::BlockParams::ConvNormType::LayerNorm :
conf::BlockParams::ConvNormType::BatchNorm;
{
Block0DumpSink sink{ &eb.dumps };
conf::BlockObserver obs{};
obs.on_point = parakeet_block0_observer_cb;
obs.user = &sink;
x = conf::build_conformer_block(ctx, x, eb.pos_emb_in, to_view(w.blocks[0]), bparams, &obs);
}
const int n_layers = static_cast<int>(w.blocks.size());
const int mid_layer = n_layers / 2;
const int last_layer = n_layers - 1;
eb.dumps.mid_block_idx = mid_layer;
eb.dumps.last_block_idx = last_layer;
eb.dumps.all_block_outs.assign(n_layers, nullptr);
eb.dumps.all_block_outs[0] = eb.dumps.block0_out;
const bool dump_all_blocks = transcribe::debug::dump_all_blocks_requested();
if (dump_all_blocks && eb.dumps.block0_out != nullptr) {
transcribe::debug::mark_tensor_for_dump(eb.dumps.block0_out);
}
const std::vector<int> sub_blocks = parse_sub_block_env(n_layers);
std::vector<SubBlockSink> sub_sinks;
sub_sinks.reserve(sub_blocks.size());
for (int idx : sub_blocks) {
sub_sinks.push_back(SubBlockSink{ idx, &eb.dumps });
}
for (int i = 1; i < n_layers; ++i) {
const auto it = std::lower_bound(sub_blocks.begin(), sub_blocks.end(), i);
const bool with_obs = (it != sub_blocks.end() && *it == i);
if (with_obs) {
const size_t sink_idx = static_cast<size_t>(it - sub_blocks.begin());
conf::BlockObserver obs{};
obs.on_point = parakeet_subblock_observer_cb;
obs.user = &sub_sinks[sink_idx];
x = conf::build_conformer_block(ctx, x, eb.pos_emb_in, to_view(w.blocks[i]), bparams, &obs);
} else {
x = conf::build_conformer_block(ctx, x, eb.pos_emb_in, to_view(w.blocks[i]), bparams);
}
eb.dumps.all_block_outs[i] = x;
if (dump_all_blocks) {
transcribe::debug::mark_tensor_for_dump(x);
}
if (i == mid_layer) {
eb.dumps.mid_block_out = x;
transcribe::debug::mark_tensor_for_dump(x);
}
if (i == last_layer) {
eb.dumps.last_block_out = x;
transcribe::debug::mark_tensor_for_dump(x);
}
}
}
eb.dumps.final_out = x;
x = conf::named(x, "enc.final");
transcribe::debug::mark_tensor_for_dump(x);
if (hp.has_prompt) {
ggml_set_output(x);
}
if (hp.has_prompt) {
const int T_enc_val = static_cast<int>(x->ne[1]);
const int P = hp.prompt_num_prompts;
const int B = n_batch;
ggml_tensor * one_hot = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, P, T_enc_val, B, 1);
if (one_hot == nullptr) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet encoder: failed to allocate "
"prompt.one_hot.in tensor");
return eb;
}
ggml_set_name(one_hot, "prompt.one_hot.in");
ggml_set_input(one_hot);
eb.prompt_one_hot_in = one_hot;
ggml_tensor * cat = ggml_concat(ctx, x, one_hot, 0);
ggml_tensor * h = ggml_mul_mat(ctx, w.prompt.mlp0_w, cat);
h = ggml_add(ctx, h, w.prompt.mlp0_b);
h = ggml_relu(ctx, h);
ggml_tensor * y = ggml_mul_mat(ctx, w.prompt.mlp2_w, h);
y = ggml_add(ctx, y, w.prompt.mlp2_b);
eb.dumps.prompted_out = y;
y = conf::named(y, "enc.prompted");
transcribe::debug::mark_tensor_for_dump(y);
x = y;
}
eb.out = x;
ggml_set_output(eb.out);
eb.graph = ggml_new_graph_custom(ctx, 8192, false);
if (eb.graph == nullptr) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR, "parakeet encoder: ggml_new_graph_custom failed");
return eb;
}
ggml_build_forward_expand(eb.graph, eb.out);
if (hp.has_prompt && eb.dumps.final_out != nullptr) {
ggml_build_forward_expand(eb.graph, eb.dumps.final_out);
}
return eb;
}
EncoderBuild build_encoder_graph_streaming(ggml_context * ctx,
const ParakeetWeights & w,
const ParakeetHParams & hp,
int n_mel_chunk_frames,
int drop_extra_pre_encoded,
StreamingEncoderCacheIO & cache_io,
ggml_type kv_type,
const char * backend_name,
bool spk_supervision) {
conf::ConvPolicy policy{};
policy.direct_pw = conf::detect_direct_pw(backend_name);
policy.direct_conv0_in_pre_encode = true;
policy.direct_dw_in_block = detect_direct_dw_in_block(backend_name);
policy.direct_dw_in_pre_encode = detect_direct_dw_in_pre_encode(backend_name);
policy.causal_pre_encode = (hp.enc_att_context_style == ParakeetHParams::AttContextStyle::ChunkedLimited);
EncoderBuild eb{};
if (ctx == nullptr || n_mel_chunk_frames <= 0) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet streaming encoder: invalid arg "
"(ctx=%p, n_mel_chunk_frames=%d)",
static_cast<void *>(ctx), n_mel_chunk_frames);
return eb;
}
if (hp.enc_subsampling_factor != 8) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet streaming encoder: unsupported "
"subsampling_factor=%d (only 8 implemented)",
hp.enc_subsampling_factor);
return eb;
}
if (hp.enc_att_context_style != ParakeetHParams::AttContextStyle::ChunkedLimited) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet streaming encoder: requires "
"att_context_style=ChunkedLimited");
return eb;
}
const int n_layers = static_cast<int>(w.blocks.size());
if (static_cast<int>(cache_io.channel_in.size()) != n_layers ||
static_cast<int>(cache_io.time_in.size()) != n_layers) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet streaming encoder: cache_io.in vectors "
"must be sized to n_layers=%d (got channel=%zu, "
"time=%zu)",
n_layers, cache_io.channel_in.size(), cache_io.time_in.size());
return eb;
}
eb.mel_in = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, n_mel_chunk_frames, hp.fe_num_mels);
ggml_set_name(eb.mel_in, "stream.mel.in");
ggml_set_input(eb.mel_in);
ggml_tensor * x = conf::build_pre_encode(ctx, to_view(w.pre_encode), eb.mel_in, policy,
"stream.enc.pre_encode",
"parakeet stream");
if (x == nullptr) {
return eb;
}
if (hp.enc_xscaling) {
const float scale = std::sqrt(static_cast<float>(hp.enc_d_model));
x = ggml_scale(ctx, x, scale);
}
if (drop_extra_pre_encoded > 0) {
const int64_t T_pre = x->ne[1];
if (drop_extra_pre_encoded >= T_pre) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet streaming encoder: drop_extra=%d >= "
"T_pre_enc=%lld",
drop_extra_pre_encoded, (long long) T_pre);
return eb;
}
x = ggml_view_2d(ctx, x, x->ne[0], T_pre - drop_extra_pre_encoded, x->nb[1], drop_extra_pre_encoded * x->nb[1]);
x = ggml_cont(ctx, x);
}
if (hp.has_spk_kernel) {
ggml_tensor * spk_mask = nullptr;
ggml_tensor * bg_mask = nullptr;
if (spk_supervision) {
const int64_t T_inj = x->ne[1];
spk_mask = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 1, T_inj);
ggml_set_name(spk_mask, "stream.spk_mask.in");
ggml_set_input(spk_mask);
bg_mask = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 1, T_inj);
ggml_set_name(bg_mask, "stream.bg_mask.in");
ggml_set_input(bg_mask);
eb.spk_mask_in = spk_mask;
eb.bg_mask_in = bg_mask;
}
x = apply_spk_kernel_injection(ctx, x, w, hp, spk_mask, bg_mask);
}
const int64_t T_q_new = x->ne[1];
const int64_t T_cache = cache_io.channel_in[0]->ne[1];
const int64_t T_virt = T_cache + T_q_new;
const bool kv_cache_mode = !transcribe::debug::enabled() && static_cast<int>(cache_io.k_in.size()) == n_layers &&
static_cast<int>(cache_io.v_in.size()) == n_layers;
const int64_t pos_len = T_virt + T_q_new - 1;
const bool pos_proj_hit =
cache_io.pos_proj_len == pos_len && static_cast<int>(cache_io.pos_proj.size()) == n_layers;
if (!pos_proj_hit) {
eb.pos_emb_in = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, hp.enc_d_model, pos_len);
ggml_set_name(eb.pos_emb_in, "stream.pos_emb.in");
ggml_set_input(eb.pos_emb_in);
}
eb.chunked_mask_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, T_virt, T_q_new, 1, 1);
ggml_set_name(eb.chunked_mask_in, "stream.attn.chunked_mask.in");
ggml_set_input(eb.chunked_mask_in);
eb.graph = ggml_new_graph_custom(ctx, 8192, false);
if (eb.graph == nullptr) {
return eb;
}
conf::BlockParams bparams{};
bparams.d_model = hp.enc_d_model;
bparams.n_head = hp.enc_n_heads;
bparams.conv_kernel = hp.enc_conv_kernel;
bparams.kv_type = kv_type;
bool enc_use_flash = true, dec_use_flash = false;
transcribe::flash::apply_env_overrides(enc_use_flash, dec_use_flash);
bparams.use_flash = enc_use_flash;
bparams.policy = policy;
bparams.att_context_style = conf::BlockParams::AttContextStyle::ChunkedLimited;
bparams.attn_chunked_mask = eb.chunked_mask_in;
bparams.conv_context_left = hp.enc_conv_context_left;
bparams.conv_context_right = hp.enc_conv_context_right;
bparams.conv_norm_type = (hp.enc_conv_norm_type == ParakeetHParams::ConvNormType::LayerNorm) ?
conf::BlockParams::ConvNormType::LayerNorm :
conf::BlockParams::ConvNormType::BatchNorm;
bparams.streaming_graph = eb.graph;
bparams.streaming_T_q_new = static_cast<int>(T_q_new);
cache_io.channel_out.assign(n_layers, nullptr);
cache_io.time_out.assign(n_layers, nullptr);
const int k_minus_1 = static_cast<int>(cache_io.time_in[0]->ne[0]);
if (kv_cache_mode) {
cache_io.k_out.assign(n_layers, nullptr);
cache_io.v_out.assign(n_layers, nullptr);
}
for (int i = 0; i < n_layers; ++i) {
ggml_tensor * cache_tm_out = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, k_minus_1, hp.enc_d_model);
if (cache_tm_out == nullptr) {
return eb;
}
char nm[64];
std::snprintf(nm, sizeof(nm), "stream.cache_tm_out.%d", i);
ggml_set_name(cache_tm_out, nm);
ggml_set_output(cache_tm_out);
ggml_tensor * cache_ch_out = nullptr;
ggml_tensor * cache_k_out = nullptr;
ggml_tensor * cache_v_out = nullptr;
if (kv_cache_mode) {
cache_k_out = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, hp.enc_d_model, T_cache);
cache_v_out = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, hp.enc_d_model, T_cache);
if (cache_k_out == nullptr || cache_v_out == nullptr) {
return eb;
}
std::snprintf(nm, sizeof(nm), "stream.cache_k_out.%d", i);
ggml_set_name(cache_k_out, nm);
std::snprintf(nm, sizeof(nm), "stream.cache_v_out.%d", i);
ggml_set_name(cache_v_out, nm);
ggml_set_output(cache_k_out);
ggml_set_output(cache_v_out);
} else {
cache_ch_out = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, hp.enc_d_model, T_cache);
if (cache_ch_out == nullptr) {
return eb;
}
std::snprintf(nm, sizeof(nm), "stream.cache_ch_out.%d", i);
ggml_set_name(cache_ch_out, nm);
ggml_set_output(cache_ch_out);
}
bparams.streaming_channel_in = cache_io.channel_in[i];
bparams.streaming_channel_out = cache_ch_out;
bparams.streaming_time_in = cache_io.time_in[i];
bparams.streaming_time_out = cache_tm_out;
bparams.streaming_pos_proj_in = pos_proj_hit ? cache_io.pos_proj[i] : nullptr;
bparams.streaming_kv_k_in = kv_cache_mode ? cache_io.k_in[i] : nullptr;
bparams.streaming_kv_v_in = kv_cache_mode ? cache_io.v_in[i] : nullptr;
bparams.streaming_kv_k_out = cache_k_out;
bparams.streaming_kv_v_out = cache_v_out;
x = conf::build_conformer_block(ctx, x, eb.pos_emb_in, to_view(w.blocks[i]), bparams,
nullptr);
cache_io.channel_out[i] = cache_ch_out;
cache_io.time_out[i] = cache_tm_out;
if (kv_cache_mode) {
cache_io.k_out[i] = cache_k_out;
cache_io.v_out[i] = cache_v_out;
}
}
eb.dumps.final_out = x;
x = conf::named(x, "enc.final");
transcribe::debug::mark_tensor_for_dump(x);
if (hp.has_prompt) {
ggml_set_output(x);
}
if (hp.has_prompt) {
const int T_q = static_cast<int>(x->ne[1]);
const int P = hp.prompt_num_prompts;
ggml_tensor * one_hot = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, P, T_q, 1, 1);
if (one_hot == nullptr) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"parakeet streaming encoder: failed to allocate "
"prompt.one_hot.in tensor");
return eb;
}
ggml_set_name(one_hot, "prompt.one_hot.in");
ggml_set_input(one_hot);
eb.prompt_one_hot_in = one_hot;
ggml_tensor * cat = ggml_concat(ctx, x, one_hot, 0);
ggml_tensor * h = ggml_mul_mat(ctx, w.prompt.mlp0_w, cat);
h = ggml_add(ctx, h, w.prompt.mlp0_b);
h = ggml_relu(ctx, h);
ggml_tensor * y = ggml_mul_mat(ctx, w.prompt.mlp2_w, h);
y = ggml_add(ctx, y, w.prompt.mlp2_b);
eb.dumps.prompted_out = y;
y = conf::named(y, "enc.prompted");
transcribe::debug::mark_tensor_for_dump(y);
x = y;
}
eb.out = x;
ggml_set_output(eb.out);
ggml_build_forward_expand(eb.graph, eb.out);
if (hp.has_prompt && eb.dumps.final_out != nullptr) {
ggml_build_forward_expand(eb.graph, eb.dumps.final_out);
}
return eb;
}
VadBuild build_vad_graph(ggml_context * ctx,
const ParakeetWeights & w,
const ParakeetHParams & hp,
int n_mel_frames,
const char * backend_name) {
VadBuild vb{};
if (ctx == nullptr || n_mel_frames <= 0 || !hp.has_vad_head || w.vad.proj_w == nullptr) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR, "parakeet vad: invalid arg (no vad head or empty mel)");
return vb;
}
conf::ConvPolicy policy{};
policy.direct_pw = conf::detect_direct_pw(backend_name);
policy.direct_conv0_in_pre_encode = true;
policy.direct_dw_in_block = detect_direct_dw_in_block(backend_name);
policy.direct_dw_in_pre_encode = detect_direct_dw_in_pre_encode(backend_name);
policy.causal_pre_encode = false;
policy.pre_encode_mask_after_stride = hp.kestrel_length_masking;
policy.pre_encode_f32_pointwise = hp.kestrel_length_masking;
vb.mel_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, n_mel_frames, hp.fe_num_mels, 1, 1);
ggml_set_name(vb.mel_in, "vad.mel.in");
ggml_set_input(vb.mel_in);
conf::PreEncodeValidMasks pe_masks;
ggml_tensor * x =
conf::build_pre_encode(ctx, to_view(w.pre_encode), vb.mel_in, policy, "vad.pre_encode",
"parakeet vad", hp.kestrel_length_masking ? &pe_masks : nullptr);
if (x == nullptr) {
return vb;
}
vb.pe_mask_s1_in = pe_masks.mask_s1;
vb.pe_mask_s2_in = pe_masks.mask_s2;
vb.pe_mask_s3_in = pe_masks.mask_s3;
vb.pe_extent_s2_in = pe_masks.mask_s2_extent;
vb.pe_extent_s3_in = pe_masks.mask_s3_extent;
vb.pre_encode_out = x;
const int64_t d_model = x->ne[0];
const int64_t H = hp.vad_hidden;
const int K = hp.vad_context_kernel;
ggml_tensor * h = ggml_mul_mat(ctx, ggml_reshape_2d(ctx, w.vad.proj_w, d_model, H), x); h = ggml_add(ctx, h, w.vad.proj_b);
h = ggml_silu(ctx, h);
vb.proj_out = conf::named(h, "vad.proj");
ggml_tensor * t = ggml_cont(ctx, ggml_transpose(ctx, h)); t = conf::conv_1d_f32(ctx, w.vad.ctx_w, t, 1, K / 2, 1); t = ggml_add(ctx, t, ggml_reshape_2d(ctx, w.vad.ctx_b, 1, H));
t = ggml_silu(ctx, t);
ggml_tensor * c = ggml_cont(ctx, ggml_transpose(ctx, t)); vb.ctx_out = conf::named(c, "vad.ctx");
ggml_tensor * o = ggml_mul_mat(ctx, ggml_reshape_2d(ctx, w.vad.out_w, H, 1), c); o = ggml_add(ctx, o, w.vad.out_b);
vb.prob = conf::named(ggml_sigmoid(ctx, o), "vad.prob");
vb.graph = ggml_new_graph_custom(ctx, 1024, false);
if (vb.graph == nullptr) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR, "parakeet vad: ggml_new_graph_custom failed");
return vb;
}
ggml_build_forward_expand(vb.graph, vb.prob);
for (ggml_tensor * keep : { vb.pre_encode_out, vb.proj_out, vb.ctx_out }) {
transcribe::debug::mark_tensor_for_dump(keep);
}
return vb;
}
}