#include "encoder.h"
#include "weights.h"
#include "conformer/conformer.h"
#include "transcribe-debug.h"
#include "transcribe-log.h"
#include "ggml.h"
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
namespace transcribe::medasr {
namespace {
namespace conf = transcribe::conformer;
ggml_tensor * mul_mat_f32acc(ggml_context * ctx,
ggml_tensor * w,
ggml_tensor * x)
{
ggml_tensor * y = ggml_mul_mat(ctx, w, x);
if (w->type == GGML_TYPE_F16) {
ggml_mul_mat_set_prec(y, GGML_PREC_F32);
}
return y;
}
ggml_tensor * lasr_layer_norm(ggml_context * ctx,
ggml_tensor * x,
ggml_tensor * scale,
float eps)
{
x = ggml_norm(ctx, x, eps);
if (scale != nullptr) {
x = ggml_mul(ctx, x, scale);
}
return x;
}
ggml_tensor * lasr_feed_forward(ggml_context * ctx,
ggml_tensor * x,
ggml_tensor * up_w,
ggml_tensor * down_w)
{
x = mul_mat_f32acc(ctx, up_w, x);
x = ggml_silu(ctx, x);
x = mul_mat_f32acc(ctx, down_w, x);
return x;
}
ggml_tensor * scaled_residual(ggml_context * ctx,
ggml_tensor * residual,
ggml_tensor * branch,
float w0,
float w1)
{
ggml_tensor * a = (w0 == 1.0f) ? residual : ggml_scale(ctx, residual, w0);
ggml_tensor * b = (w1 == 1.0f) ? branch : ggml_scale(ctx, branch, w1);
return ggml_add(ctx, a, b);
}
ggml_tensor * build_subsampling(ggml_context * ctx,
const MedAsrSubsampling & ss,
ggml_tensor * mel_in,
const MedAsrHParams & hp,
const conf::ConvPolicy & ,
EncoderDumps * dumps = nullptr)
{
const int sub_k = hp.enc_sub_kernel;
ggml_tensor * x = ggml_reshape_3d(ctx, mel_in,
mel_in->ne[0], mel_in->ne[1], mel_in->ne[3]);
x = mul_mat_f32acc(ctx, ss.dense0_w, x);
if (ss.dense0_b != nullptr) {
x = ggml_add(ctx, x, ss.dense0_b);
}
x = ggml_relu(ctx, x);
if (dumps != nullptr) {
dumps->sub_after_dense0 = x;
transcribe::debug::mark_tensor_for_dump(x);
}
x = ggml_cont(ctx, ggml_permute(ctx, x, 1, 0, 2, 3));
x = conf::conv_1d_f32(ctx, ss.conv0_w, x, sub_k > 0 ? hp.enc_sub_stride : 2,
0, 1);
if (ss.conv0_b != nullptr) {
ggml_tensor * bias_r = ggml_reshape_3d(ctx, ss.conv0_b, 1, x->ne[1], 1);
x = ggml_add(ctx, x, bias_r);
}
x = ggml_relu(ctx, x);
if (dumps != nullptr) {
dumps->sub_after_conv0 = x;
transcribe::debug::mark_tensor_for_dump(x);
}
x = conf::conv_1d_f32(ctx, ss.conv1_w, x, hp.enc_sub_stride,
0, 1);
if (ss.conv1_b != nullptr) {
ggml_tensor * bias_r = ggml_reshape_3d(ctx, ss.conv1_b, 1, x->ne[1], 1);
x = ggml_add(ctx, x, bias_r);
}
x = ggml_relu(ctx, x);
if (dumps != nullptr) {
dumps->sub_after_conv1 = x;
transcribe::debug::mark_tensor_for_dump(x);
}
x = ggml_cont(ctx, ggml_permute(ctx, x, 1, 0, 2, 3));
ggml_tensor * dense1_w = ss.dense1_w;
if (dense1_w->type != GGML_TYPE_F32 && dense1_w->type != GGML_TYPE_F16) {
dense1_w = ggml_cast(ctx, dense1_w, GGML_TYPE_F32);
}
x = mul_mat_f32acc(ctx, dense1_w, x);
if (ss.dense1_b != nullptr) {
x = ggml_add(ctx, x, ss.dense1_b);
}
return x;
}
ggml_tensor * build_conv_module(ggml_context * ctx,
ggml_tensor * x,
const MedAsrBlock & b,
ggml_tensor * bn_scale,
ggml_tensor * bn_bias,
int d_model,
int conv_kernel,
const conf::ConvPolicy & policy,
ggml_tensor * conv_pad_mask)
{
conf::BlockView bv {};
bv.conv_pw1_w = b.conv_pw1_w;
bv.conv_pw1_b = nullptr; bv.conv_dw_w = b.conv_dw_w;
bv.conv_dw_b = nullptr;
bv.conv_pw2_w = b.conv_pw2_w;
bv.conv_pw2_b = nullptr;
bv.conv_bn_fused_scale = bn_scale;
bv.conv_bn_fused_bias = bn_bias;
conf::BlockParams bp {};
bp.d_model = d_model;
bp.n_head = 0; bp.conv_kernel = conv_kernel;
bp.kv_type = GGML_TYPE_F32;
bp.use_flash = false; bp.policy = policy;
bp.conv_context_left = (conv_kernel - 1) / 2; bp.conv_context_right = conv_kernel - 1 - bp.conv_context_left; bp.conv_norm_type = conf::BlockParams::ConvNormType::BatchNorm;
bp.conv_pad_mask = conv_pad_mask;
return conf::conv_module(ctx, x, bv, bp);
}
ggml_tensor * build_rope_attn(ggml_context * ctx,
ggml_tensor * x,
ggml_tensor * positions,
const MedAsrBlock & b,
int d_model,
int n_head,
float rope_theta,
int rope_max_pos,
bool use_flash,
ggml_tensor * attn_pad_mask = nullptr)
{
const int head_dim = d_model / n_head;
const float scale = 1.0f / std::sqrt(static_cast<float>(head_dim));
const int64_t T = x->ne[1];
const int64_t Bb = x->ne[2];
const bool flash = use_flash && (attn_pad_mask == nullptr);
ggml_tensor * q = mul_mat_f32acc(ctx, b.attn_q_w, x);
ggml_tensor * k = mul_mat_f32acc(ctx, b.attn_k_w, x);
ggml_tensor * v = mul_mat_f32acc(ctx, b.attn_v_w, x);
q = ggml_reshape_4d(ctx, q, head_dim, n_head, T, Bb);
k = ggml_reshape_4d(ctx, k, head_dim, n_head, T, Bb);
q = ggml_rope_ext(ctx, q, positions, nullptr,
head_dim, GGML_ROPE_TYPE_NEOX,
rope_max_pos, rope_theta,
1.0f, 0.0f,
1.0f, 32.0f,
1.0f);
k = ggml_rope_ext(ctx, k, positions, nullptr,
head_dim, GGML_ROPE_TYPE_NEOX,
rope_max_pos, rope_theta,
1.0f, 0.0f,
1.0f, 32.0f,
1.0f);
v = ggml_reshape_4d(ctx, v, head_dim, n_head, T, Bb);
auto to_attn = [&](ggml_tensor * t) -> ggml_tensor * {
t = ggml_permute(ctx, t, 0, 2, 1, 3);
return ggml_cont(ctx, t);
};
q = to_attn(q);
k = to_attn(k);
v = to_attn(v);
ggml_tensor * o;
if (flash) {
o = ggml_flash_attn_ext(ctx, q, k, v, nullptr,
scale, 0.0f, 0.0f);
o = ggml_permute(ctx, o, 0, 2, 1, 3);
o = ggml_cont(ctx, o);
} else {
ggml_tensor * kq = ggml_mul_mat(ctx, k, q); if (attn_pad_mask != nullptr) {
kq = ggml_add(ctx, kq, attn_pad_mask);
}
ggml_tensor * kq_soft = ggml_soft_max_ext(ctx, kq, nullptr,
scale, 0.0f);
ggml_tensor * v_t = ggml_cont(ctx, ggml_permute(ctx, v, 1, 0, 2, 3));
o = ggml_mul_mat(ctx, v_t, kq_soft);
}
o = ggml_permute(ctx, o, 0, 2, 1, 3);
o = ggml_cont(ctx, o);
o = ggml_reshape_3d(ctx, o, d_model, T, Bb);
o = mul_mat_f32acc(ctx, b.attn_o_w, o);
return o;
}
struct BlockObs {
ggml_tensor * post_ff1 = nullptr;
ggml_tensor * post_attn = nullptr;
ggml_tensor * post_conv = nullptr;
ggml_tensor * post_ff2 = nullptr;
};
ggml_tensor * build_block(ggml_context * ctx,
ggml_tensor * x,
ggml_tensor * positions,
const MedAsrBlock & b,
ggml_tensor * bn_scale,
ggml_tensor * bn_bias,
const MedAsrHParams & hp,
const conf::ConvPolicy & policy,
bool use_flash,
ggml_tensor * attn_pad_mask,
ggml_tensor * conv_pad_mask,
BlockObs * obs)
{
const int d_model = hp.enc_hidden;
const int n_head = hp.enc_n_heads;
const int conv_k = hp.enc_conv_kernel;
const float ln_eps = hp.enc_layer_norm_eps;
const float rope_th = hp.enc_rope_theta;
const int rope_max = hp.enc_max_pos_emb;
const float ff_w0 = hp.enc_ff_resid_w0; const float ff_w1 = hp.enc_ff_resid_w1; const float conv_w0 = hp.enc_conv_resid_w0; const float conv_w1 = hp.enc_conv_resid_w1;
{
ggml_tensor * y = lasr_layer_norm(ctx, x, b.norm_ff1_w, ln_eps);
y = lasr_feed_forward(ctx, y, b.ff1_up_w, b.ff1_down_w);
x = scaled_residual(ctx, x, y, ff_w0, ff_w1);
}
if (obs != nullptr) obs->post_ff1 = x;
{
ggml_tensor * y = lasr_layer_norm(ctx, x, b.norm_attn_w, ln_eps);
y = build_rope_attn(ctx, y, positions, b, d_model, n_head,
rope_th, rope_max, use_flash, attn_pad_mask);
x = ggml_add(ctx, x, y);
}
if (obs != nullptr) obs->post_attn = x;
{
ggml_tensor * y = lasr_layer_norm(ctx, x, b.norm_conv_w, ln_eps);
y = build_conv_module(ctx, y, b, bn_scale, bn_bias,
d_model, conv_k, policy, conv_pad_mask);
x = scaled_residual(ctx, x, y, conv_w0, conv_w1);
}
if (obs != nullptr) obs->post_conv = x;
{
ggml_tensor * y = lasr_layer_norm(ctx, x, b.norm_ff2_w, ln_eps);
y = lasr_feed_forward(ctx, y, b.ff2_up_w, b.ff2_down_w);
x = scaled_residual(ctx, x, y, ff_w0, ff_w1);
}
if (obs != nullptr) obs->post_ff2 = x;
x = lasr_layer_norm(ctx, x, b.norm_post_w, ln_eps);
return x;
}
}
EncoderBuild build_encoder_graph(ggml_context * ctx,
const MedAsrWeights & w,
const MedAsrHParams & hp,
int n_mel_frames,
const char * backend_name,
int n_batch,
bool batch_var_len)
{
EncoderBuild eb {};
if (ctx == nullptr || n_mel_frames <= 0) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"medasr encoder: invalid arg "
"(ctx=%p, n_mel_frames=%d)",
static_cast<void *>(ctx), n_mel_frames);
return eb;
}
if (n_batch < 1) n_batch = 1;
const bool var_len_masks = batch_var_len && n_batch > 1;
const int d_model = hp.enc_hidden;
const int n_layers = hp.enc_n_layers;
eb.mel_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32,
hp.fe_num_mels, n_mel_frames, 1, n_batch);
if (eb.mel_in == nullptr) 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::ConvPolicy policy {};
policy.direct_pw = conf::detect_direct_pw(backend_name);
policy.direct_dw_in_block = true;
policy.direct_dw_in_pre_encode = false;
policy.causal_pre_encode = false;
ggml_tensor * x = build_subsampling(ctx, w.subsampling, eb.mel_in, hp,
policy, &eb.dumps);
if (x == nullptr) return eb;
ggml_set_name(x, "enc.subsampling.out");
eb.dumps.subsampling_out = x;
transcribe::debug::mark_tensor_for_dump(x);
if (n_layers <= 0) {
eb.out = x;
eb.graph = ggml_new_graph(ctx);
ggml_build_forward_expand(eb.graph, x);
return eb;
}
const int64_t T_enc = x->ne[1];
eb.positions = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, T_enc);
ggml_set_name(eb.positions, "enc.positions");
ggml_set_input(eb.positions);
if (var_len_masks) {
eb.attn_pad_mask_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32,
T_enc, 1, 1, n_batch);
eb.conv_pad_mask_in = ggml_new_tensor_4d(ctx, GGML_TYPE_F32,
T_enc, 1, n_batch, 1);
ggml_set_name(eb.attn_pad_mask_in, "enc.attn_pad_mask");
ggml_set_name(eb.conv_pad_mask_in, "enc.conv_pad_mask");
ggml_set_input(eb.attn_pad_mask_in);
ggml_set_input(eb.conv_pad_mask_in);
}
eb.bn_scale_inputs.assign(n_layers, nullptr);
eb.bn_bias_inputs.assign(n_layers, nullptr);
for (int i = 0; i < n_layers; ++i) {
ggml_tensor * s = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, d_model);
ggml_tensor * bt = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, d_model);
if (s == nullptr || bt == nullptr) return eb;
char nm[64];
std::snprintf(nm, sizeof(nm), "enc.block.%d.conv_bn.scale", i);
ggml_set_name(s, nm);
ggml_set_input(s);
std::snprintf(nm, sizeof(nm), "enc.block.%d.conv_bn.bias", i);
ggml_set_name(bt, nm);
ggml_set_input(bt);
eb.bn_scale_inputs[i] = s;
eb.bn_bias_inputs[i] = bt;
}
const bool use_flash = (std::getenv("TRANSCRIBE_NO_FLASH") == nullptr);
eb.dumps.all_block_outs.assign(n_layers, nullptr);
for (int i = 0; i < n_layers; ++i) {
BlockObs obs{};
const bool with_obs = (i == 0);
x = build_block(ctx, x, eb.positions, w.blocks[i],
eb.bn_scale_inputs[i], eb.bn_bias_inputs[i],
hp, policy, use_flash,
eb.attn_pad_mask_in, eb.conv_pad_mask_in,
with_obs ? &obs : nullptr);
if (with_obs) {
const auto mark = [&](ggml_tensor * t, const char * nm,
ggml_tensor *& slot) {
if (t == nullptr) return;
ggml_set_name(t, nm);
transcribe::debug::mark_tensor_for_dump(t);
slot = t;
};
mark(obs.post_ff1, "enc.block.0.post_ff1", eb.dumps.block0_post_ff1);
mark(obs.post_attn, "enc.block.0.post_attn", eb.dumps.block0_post_attn);
mark(obs.post_conv, "enc.block.0.post_conv", eb.dumps.block0_post_conv);
mark(obs.post_ff2, "enc.block.0.post_ff2", eb.dumps.block0_post_ff2);
}
char nm[32];
std::snprintf(nm, sizeof(nm), "enc.block.%d.out", i);
ggml_set_name(x, nm);
eb.dumps.all_block_outs[i] = x;
transcribe::debug::mark_tensor_for_dump(x);
}
x = lasr_layer_norm(ctx, x, w.enc_out_norm_w, hp.enc_layer_norm_eps);
ggml_set_name(x, "enc.out_norm.out");
eb.dumps.out_norm_out = x;
transcribe::debug::mark_tensor_for_dump(x);
{
ggml_tensor * proj_w = ggml_reshape_2d(ctx, w.ctc_head.proj_w,
d_model, hp.ctc_vocab_size);
x = mul_mat_f32acc(ctx, proj_w, x);
if (w.ctc_head.proj_b != nullptr) {
x = ggml_add(ctx, x, w.ctc_head.proj_b);
}
}
ggml_set_name(x, "enc.ctc_logits.raw");
eb.dumps.ctc_logits = x;
{
ggml_tensor * logits_t = ggml_cont(ctx, ggml_permute(ctx, x, 1, 0, 2, 3));
ggml_set_name(logits_t, "enc.ctc_logits");
eb.dumps.ctc_logits_for_dump = logits_t;
transcribe::debug::mark_tensor_for_dump(logits_t);
}
eb.out = x;
ggml_set_output(eb.out);
if (eb.dumps.ctc_logits_for_dump != nullptr) {
ggml_set_output(eb.dumps.ctc_logits_for_dump);
}
eb.graph = ggml_new_graph_custom(ctx, 8192, false);
if (eb.graph == nullptr) {
log_msg(TRANSCRIBE_LOG_LEVEL_ERROR,
"medasr encoder: ggml_new_graph_custom failed");
return eb;
}
ggml_build_forward_expand(eb.graph, eb.out);
if (eb.dumps.ctc_logits_for_dump != nullptr) {
ggml_build_forward_expand(eb.graph, eb.dumps.ctc_logits_for_dump);
}
return eb;
}
}