一,语音识别: DeepSpeech2模型概述

DeepSpeech2是一个端到端自动语音识别(ASR)引擎的开源项目。其核心代码实现包括音频预处理、特征提取、模型结构定义及训练流程。

模型架构

DeepSpeech2 的模型架构主要由卷积神经网络(CNN)、循环神经网络(RNN)和全连接层(Dense Layers)组成,并采用连接主义时间分类(CTC)损失函数进行训练。

卷积神经网络(CNN):
作用:负责提取音频信号的局部特征。音频信号在时域上具有连续性,CNN通过卷积核在音频信号上滑动,提取不同时间段的特征,生成特征图。
特点:能够捕捉音频信号中的局部模式,如音素、音节等,为后续的时序建模提供基础。

循环神经网络(RNN):
作用:对CNN提取的特征图进行时序建模,捕捉语音信号中的时序依赖关系。语音信号在时间上具有连续性,RNN通过其循环结构能够处理这种连续性,捕捉语音信号中的上下文信息。
变体:DeepSpeech2中常使用双向RNN(如双向GRU或双向LSTM),能够同时考虑过去和未来的上下文信息,提高识别准确率。

全连接层(Dense Layers):
作用:将RNN的输出转化为最终的识别结果。全连接层通过非线性变换,将RNN输出的高维特征映射到低维的文本空间,生成最终的识别文本。

CTC损失函数:
作用:解决序列对齐问题。在语音识别中,输入音频和输出文本之间存在对齐问题,即音频中的某个时间段可能对应文本中的多个字符或音节。CTC损失函数通过引入空白标签和重复标签,允许模型在输出序列中插入空白或重复字符,从而解决对齐问题。

二,android应用开发示例

本程序实现了端到端推理流程,对应原理中的“音频特征输入→CNN特征提取→RNN时序建模→CTC解码→文本输出”全链路。量化步骤适配了NPU对8-bit整型数据的处理特性。

开发步骤映射:属于“模型部署”阶段,承接训练完成的nbg模型,通过NPU加速实现高效推理。输入数据预处理需与训练时的特征工程保持一致(如MFCC参数、归一化系数)。

int main(int argc, char **argv) {
    printf("%s nbg input\n", argv[0]);
    if(argc < 3)
    {
        printf("Arguments count %d is incorrect!\n", argc);
        return -1;
    }
    const char* nbg = argv[1];
    const char* input = argv[2];
    printf("input tensor_file = %s\n", input);

    // npu init
    awnn_init();
    // create network
    Awnn_Context_t *context = awnn_create(nbg);
    // copy input
    unsigned int width = 756;
    unsigned int height = 161;
    unsigned int sz = width * height;
    float *plant_data = (float*) malloc(sz * sizeof(float));
    unsigned char* data = (unsigned char*) malloc(sz * sizeof(unsigned char));
    // Read tensor data from file
    FILE *file = fopen(input, "r");
    if (file == NULL) {
        printf("Failed to open tensor file.\n");
        exit(-1);
    }
    for (int i = 0; i < sz; i++) {
        fscanf(file, "%f", &plant_data[i]);
        plant_data[i] /= 0.009404;
        if (plant_data[i] < 0)
            data[i] = 0;
        else if (plant_data[i] > 255)
            data[i] = 255;
        else
            data[i] = (unsigned char)round(plant_data[i]);
    }
    fclose(file);

    unsigned char *input_buffers[1] = {data};
    awnn_set_input_buffers(context, input_buffers);

    // process network
    awnn_run(context);

    // get result
    float **results = awnn_get_output_buffers(context);

    // post process
    deepspeech2_post_process(results[0]);

    free(plant_data);
    free(data);

    // destroy network
    // awnn_destroy(context);

    // npu uninit
    // awnn_uninit();

    return 0;
}

这段C程序实现了基于NPU(神经网络处理单元)的DeepSpeech2模型推理流程,核心逻辑可分为以下8个详细步骤:

  1. 参数校验与初始化

    参数检查:通过argc < 3校验命令行参数数量,确保包含程序名、nbg模型路径、输入tensor文件路径三要素。
    路径获取:argv[1]指向预训练的nbg模型文件(如deepspeech2.nbg),argv[2]指向输入特征数据文件(如MFCC特征或频谱图)。

  2. NPU硬件初始化

    调用awnn_init()完成NPU底层驱动加载、内存分配器初始化及硬件算子注册,为后续模型推理建立硬件加速环境。这里的npu是基于全志平台A733的api接口。

  3. 模型加载与上下文创建

    通过awnn_create(nbg)加载nbg模型文件,生成推理上下文context。该步骤会解析模型结构(如CNN卷积核、RNN层数、CTC解码器),并分配对应的权重内存。

  4. 输入数据预处理

    内存分配:
    plant_data:float数组,存储原始输入特征(尺寸756×161,总计121,716个元素)。
    data:unsigned char数组,存储量化后的输入数据。
    数据读取与量化:
    从输入文件逐个读取浮点数至plant_data。
    执行归一化:plant_data[i] /= 0.009404(将特征值缩放到0-255范围)。
    量化裁剪:通过if-else逻辑将值限制在[0,255]区间,并转换为unsigned char类型存入data数组。此步骤模拟了语音特征(如MFCC)的标准化处理,适配模型输入要求。

  5. 模型推理执行

    输入绑定:awnn_set_input_buffers(context, input_buffers)将量化后的data绑定到模型输入层。
    前向推理:awnn_run(context)触发NPU加速的模型推理,依次执行CNN特征提取、RNN时序建模、CTC解码等操作。
    结果获取:awnn_get_output_buffers(context)获取输出缓冲区指针results,存储模型预测的字符概率序列。

  6. 后处理与结果解析

    调用deepspeech2_post_process(results[0])对输出结果进行后处理,典型操作包括:
    CTC解码:将字符概率序列转换为文本(如贪婪解码、Beam Search)。
    语言模型纠偏:结合语言模型提升识别准确率。
    格式化输出:生成最终识别文本并打印或返回。

  7. 资源释放与清理

    释放动态分配的内存:free(plant_data)和free(data)。
    注释掉的awnn_destroy(context)和awnn_uninit()表明程序未完全释放NPU资源(需注意潜在内存泄漏风险,生产环境应取消注释)。

  8. 程序退出

    返回0表示正常退出,非0值表示错误(如参数校验失败、文件读取错误)。

int deepspeech2_post_process(float *tensor_data)
{
    int rows = 378;
    int data_size = rows * ALPHABET_SIZE;  // 数据个数计数器

    // printf("data_size = %d \n", data_size);
    // printf("tensor[0] = %f \n", tensor_data[0]);

    float tensor[rows][ALPHABET_SIZE];

    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < ALPHABET_SIZE; j++) {
            tensor[i][j] = tensor_data[i * ALPHABET_SIZE + j];
        }
    }

    free(tensor_data);
    // // Print first row of tensor
    // for (int i = 0; i < ALPHABET_SIZE; i++) {
    //     printf("%f ", tensor[0][i]);
    // }
    // printf("\n");

    // Find maximum value index in each row
    int tensor_argmax[rows];
    for (int i = 0; i < rows; i++) {
        int max_index = 0;
        for (int j = 0; j < ALPHABET_SIZE; j++) {
            if (tensor[i][j] > tensor[i][max_index]) {
                max_index = j;
            }
        }
        tensor_argmax[i] = max_index;

    }

    // Convert maximum value indices to characters
    char results_1[rows + 1];
    char results[rows + 1];
    int a = 0;
    for (int i = 0; i < rows; i++) {
        if (tensor_argmax[i] < ALPHABET_SIZE) {
            results_1[i] = alphabets[tensor_argmax[i]];
            if (results_1[i] != '\0') {
                results[a] = results_1[i];
                // printf("%c", results[a]);
                a = a + 1;
            }
        } else {
            results_1[i] = '-';
        }
    }
    results[rows] = '\0';

    printf("Original array: %s\n", results);
    removeDuplicateChars(results);
    printf("Modified array : %s\n", results);

    return 0;
}

以上代码核心部分解析:
1,贪婪解码(Argmax),对每个时间步(共378帧)独立执行argmax操作,选取概率最高的字符索引。

2,索引到字符映射
字符映射:通过全局字符表alphabets将索引转换为实际字符(如索引0→’a’)。
空字符过滤:跳过’\0’字符(可能用于标记单词边界),仅保留有效字符到results数组。
边界处理:末尾显式添加’\0’确保字符串终止,但results数组长度固定为rows+1,存在缓冲区溢出风险(若有效字符超过378个)。

三,运行演示流程

将模型文件推送至设备
将 deepspeech2 模型文件放入设备的 /data 或 /sdcard 目录:

adb push deepspeech2 /data/

预处理音频文件生成张量,通过短时傅里叶变换(STFT)生成频谱图
使用 pre_process.py 脚本将音频文件转换为模型输入所需的张量格式:

python3 pre_process.py --wav=1188-133604-0010.flac.wav

输入:FLAC/WAV格式的音频文件(如示例中的 1188-133604-0010.flac.wav)。
输出:生成对应的张量文件(如 1188-133604-0010.flac_756_161_1.tensor),包含预处理后的音频特征数据。

推送模型和张量文件到设备
将以下文件上传至设备:

模型二进制文件:deepspeech2.nb
预处理生成的张量文件:1188-133604-0010.flac_756_161_1.tensor

adb push deepspeech2.nb /data/

adb push 1188-133604-0010.flac_756_161_1.tensor /data/

在设备上运行推理
执行以下命令启动模型推理,输入为模型文件和张量文件:

deepspeech2 deepspeech2.nb 1188-133604-0010.flac_756_161_1.tensor

参数说明:
deepspeech2:可执行程序名称。
deepspeech2.nb:优化后的模型二进制文件(如NPU加速的量化模型)。
.tensor 文件:预处理后的音频特征数据。

输出识别结果
程序会打印推理后的文本结果,格式如下:

Original array: hheelllloo

Modified array : hello

原始输出:模型直接解码的字符序列(可能包含重复字符)。
去重后结果:通过后处理(如CTC解码)合并重复字符后的最终文本。

最后,完整的示例代码可在该路径下载。包括完整的demo代码和nb模型,输入音频文件。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐