天天看點

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

1. 案例簡介

随着機器學習的發展,TinyML(微型機器學習)已在你的家裡、車裡、甚至口袋裡工作了。什麼是TinyML呢?它是屬于機器學習的一個子領域,包含了算法、硬體和軟體,能夠基于傳感器資料分析,并在極地功耗的裝置上運作。比如家裡的天貓精靈、蘋果的Siri和亞馬遜的Alexa等語音助手是TinyML的一個完美應用,它們提供了語音使用者接口(AUI),讓使用者不需要操作螢幕、滑鼠或鍵盤就可以進行互動,給使用者提供了一種全新的互動方式,這些語音助手幾乎無處不在。從獨立的智能音箱到幾乎所有手機都内置了某種語音助手。

在大部分情況下,語音識别、自然語言處理以及語音合成等飯中工作都是在雲端完成的,由性能強大的伺服器運作大型機器學習模型。當使用者提出問題時,将以wav或其他音頻流的形式被發送到雲端。雲端識别出音頻流的含義并進行回複響應。語音助手真正需要的音頻是喚醒裝置後的資料。如果能在不發送資料的情況下檢測到這個喚醒詞,并在聽到喚醒詞之後才開始音頻流的傳輸,這樣既能夠保護使用者的隐私、節省電池電量和帶寬,而且可以在沒有網絡的情況下喚醒。

這也是TinyML的用武之地。要在低功耗的晶片上運作,意味着要訓練一個監聽喚醒詞的微型模型,嵌入到微控制裝置中,它就可以一直監聽喚醒詞,當檢測到喚醒詞後通知作業系統開始捕獲音頻并發送到雲端。

在本章中,将教大家如何使用預先訓練的檢測模型,在HaaS EDU K1上進行喚醒詞檢測模型的部署,并使用Tensorflow Lite Micro推理引擎進行推理。随後将教大家如何使用Tensorflow訓練腳本訓練一個自己的喚醒詞,再進行裝置端部署。本案例主要有三個部分組成:

  1. 語音采集:接入模拟麥克風(Mic1輸入);
  2. 語音識别:說出“打開”和“關閉”識别後,OLED将顯示“Turn on...”和“Turn off”;
  3. 語音播報:執行指令的同時,播報本地TTS(mp3)。

開始學習之前我們先看一下案例效果:

1.1 涉及知識點

  • 喚醒詞資料采集、模型訓練、模型部署
  • 裝置端模拟MIC聲音采樣
  • 裝置端音頻特征提取
  • TFLite-Micro推理引擎應用
  • 裝置端指令識别、響應
  • 裝置端喇叭播放mp3檔案
  • 檔案系統應用
  • OLED顯示字元

2. 方案介紹

整個方案的架構如下:

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結
  • 提供批量音頻錄制工具進行資料收集;
  • 提供TF模型訓練腳本進行喚醒詞訓練;
  • 提供完整裝置端模型部署方案;

基于該方案,你将學習到TinyML的整個生命周期:

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

3. 開發環境搭建

3.1 硬體準備

如果有HaaS語音擴充闆,直接插入即可:

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

如果沒有HaaS語音擴充闆,請按照如下步驟接入麥克風和喇叭:

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

HaaS EDU K1硬體排線圖請參考

https://help.aliyun.com/document_detail/205267.html

購買連結僅供參考!!我們不負責商家發貨的品質保障等問題!!

名稱 數量 參考連結
HaaS EDU K1開發版 1 HaaS EDU K1購買連結
microUSB資料線 普通microusb線即可
模拟MIC 模拟MIC參考連結
喇叭 喇叭參考連結
杜邦線 數條 NA

3.2 環境搭建

參考

《HaaS EDU K1快速開始》

中HaaS Studio章節完成AliOS Things開發環境搭建。

3.2.1 案例代碼下載下傳

該案例相關的源代碼下載下傳可參考

《建立工程》

,該案例是C/C++案例。

其中:

  • 選擇解決方案: “TFLite-Micro離線語音快捷詞喚醒案例”或者“tflite_micro_speech_demo”
  • 選擇開發闆: HaaS EDU K1
基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

3.2.2 代碼編譯、燒錄

完成代碼的編譯及燒錄,在燒錄前,請先完成3.2.1的步驟,再進行編譯燒錄。

3.2.2.1 檔案件系統燒錄

本元件例子中使用到到的本地語料存放在代碼中hardware/chip/haas1000/prebuild/data/目錄下mp3目錄,除燒錄tflite_micro_speech_demo image外,需燒錄littlefs檔案系統,請将hardware/chip/haas1000/package.yaml檔案中以下代碼段的注釋打開後重新編譯:

program_data_files:
    - filename: release/write_flash_tool/ota_bin/littlefs.bin
      address: 0xB32000      

3.2.3 打開序列槽

打開序列槽進行LOG檢視。

4. 軟體架構

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結
  • KWS Demo應用程式: 主要打通實作AI語音引擎的初始化,歡迎語播報。
  • ai_agent元件:是AliOS Things上的AI引擎核心子產品,後端接入不同的推理引擎,本案例中使用了TFLite-Micro推理引擎。
  • uVoice元件:是AliOS Things上智能語音解決方案的核心元件,提供了本地音頻,URL音頻,TTS合成等基礎功能,音頻格式支援mp3, m4a, wav, opus等主流格式,本案例中使用它來進行本地mp3語料的響應播報。
  • A2SA元件:是AliOS Things上音頻服務架構,相容ALSA應用接口通路,支援音頻硬體驅動抽象,多音頻驅動加載/解除安裝,VFS接口支援等功能。

4.1 代碼結構

├── cp_resources.py     # 拷貝本地語料到/prebuild/data目錄,編譯進檔案系統
├── main.c
├── maintask.c
├── Makefile
├── micro_speech        # 語音識别程式
├── oled                # OLED顯示程式
│   ├── oled.c
│   └── oled.h
├── package.yaml        # 編譯系統配置檔案
├── player              # 播放器程式
│   ├── player.c
│   └── player.h
├── README.md
├── recorder            # 錄音程式
│   ├── recorder.c
│   └── recorder.h
├── resources
│   └── mp3             # 本地mp3語料
├── SConstruct      

4.2 裝置端工作流程

在HaaS EDU K1上的整個工作流程如下圖:

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

4.3 程式主體

以下代碼是執行喚醒詞識别主體,setup對TFLite-Micro模型推理引擎進行初始化,loop中執行上圖中整個流程,從音頻采集到指令響應的全部流程在該函數中實作,詳細邏輯請參考代碼。

// The name of this function is important for Arduino compatibility.
void setup()
{
    //   tflite::InitializeTarget();
    //RegisterDebugLogCallback(callback);
    // Set up logging. Google style is to avoid globals or statics because of
    // lifetime uncertainty, but since this has a trivial destructor it's okay.
    // NOLINTNEXTLINE(runtime-global-variables)
    static tflite::MicroErrorReporter micro_error_reporter;
    error_reporter = &micro_error_reporter;
    // Map the model into a usable data structure. This doesn't involve any
    // copying or parsing, it's a very lightweight operation.
    model = tflite::GetModel(g_model);
    if (model->version() != TFLITE_SCHEMA_VERSION)
    {
        TF_LITE_REPORT_ERROR(error_reporter,
                             "Model provided is schema version %d not equal "
                             "to supported version %d.",
                             model->version(), TFLITE_SCHEMA_VERSION);
        return;
    }
    // Pull in only the operation implementations we need.
    // This relies on a complete list of all the ops needed by this graph.
    // An easier approach is to just use the AllOpsResolver, but this will
    // incur some penalty in code space for op implementations that are not
    // needed by this graph.
    //
    // tflite::AllOpsResolver resolver;
    // NOLINTNEXTLINE(runtime-global-variables)
    static tflite::MicroMutableOpResolver<4> micro_op_resolver(error_reporter);
    if (micro_op_resolver.AddDepthwiseConv2D() != kTfLiteOk)
    {
        return;
    }
    if (micro_op_resolver.AddFullyConnected() != kTfLiteOk)
    {
        return;
    }
    if (micro_op_resolver.AddSoftmax() != kTfLiteOk)
    {
        return;
    }
    if (micro_op_resolver.AddReshape() != kTfLiteOk)
    {
        return;
    }
    // Build an interpreter to run the model with.
    static tflite::MicroInterpreter static_interpreter(
        model, micro_op_resolver, tensor_arena, kTensorArenaSize, error_reporter);
    interpreter = &static_interpreter;
    // Allocate memory from the tensor_arena for the model's tensors.
    TfLiteStatus allocate_status = interpreter->AllocateTensors();
    if (allocate_status != kTfLiteOk)
    {
        TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
        return;
    }
    // Get information about the memory area to use for the model's input.
    model_input = interpreter->input(0);
    if ((model_input->dims->size != 2) || (model_input->dims->data[0] != 1) ||
        (model_input->dims->data[1] !=
         (kFeatureSliceCount * kFeatureSliceSize)) ||
        (model_input->type != kTfLiteInt8))
    {
        TF_LITE_REPORT_ERROR(error_reporter,
                             "Bad input tensor parameters in model");
        return;
    }
    model_input_buffer = model_input->data.int8;
    // Prepare to access the audio spectrograms from a microphone or other source
    // that will provide the inputs to the neural network.
    // NOLINTNEXTLINE(runtime-global-variables)
    static FeatureProvider static_feature_provider(kFeatureElementCount,
                                                   feature_buffer);
    feature_provider = &static_feature_provider;
    static RecognizeCommands static_recognizer(error_reporter);
    recognizer = &static_recognizer;
    previous_time = 0;
    RespondCommandThreadInit();
}
// The name of this function is important for Arduino compatibility.
void loop()
{
    // Fetch the spectrogram for the current time.
    const int32_t current_time = LatestAudioTimestamp();
    int how_many_new_slices = 0;
    TfLiteStatus feature_status = feature_provider->PopulateFeatureData(
        error_reporter, previous_time, current_time, &how_many_new_slices);
    // LOG("current_time: %d, previous_time: %d, how_many_new_slices: %d\n", current_time, previous_time, how_many_new_slices);
    if (feature_status != kTfLiteOk)
    {
        TF_LITE_REPORT_ERROR(error_reporter, "Feature generation failed");
        return;
    }
    previous_time = current_time;
    // If no new audio samples have been received since last time, don't bother
    // running the network model.
    if (how_many_new_slices == 0)
    {
        //LOG("[lk added]how_many_new_slices is 0\n");
        return;
    }
    // Copy feature buffer to input tensor
    for (int i = 0; i < kFeatureElementCount; i++)
    {
        model_input_buffer[i] = feature_buffer[i];
    }
    // Run the model on the spectrogram input and make sure it succeeds.
    TfLiteStatus invoke_status = interpreter->Invoke();
    if (invoke_status != kTfLiteOk)
    {
        TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed");
        return;
    }
    // Obtain a pointer to the output tensor
    TfLiteTensor *output = interpreter->output(0);
    // Determine whether a command was recognized based on the output of inference
    const char *found_command = nullptr;
    uint8_t score = 0;
    bool is_new_command = false;
    TfLiteStatus process_status = recognizer->ProcessLatestResults(
        output, current_time, &found_command, &score, &is_new_command);
    if (process_status != kTfLiteOk)
    {
        TF_LITE_REPORT_ERROR(error_reporter,
                             "RecognizeCommands::ProcessLatestResults() failed");
        return;
    }
    // Do something based on the recognized command. The default implementation
    // just prints to the error console, but you should replace this with your
    // own function for a real application.
    RespondToCommand(error_reporter, current_time, found_command, score,
                     is_new_command);
}      

5. 案例體驗

當程式燒錄完成後,直接喊出“打開",“關閉”,就可以看到視訊所示的效果。目前隻支援近場喚醒,喚醒距離1米左右。由于這個“打開”,“關閉”喚醒的語料有限,喚醒因不同人有差異。建議按照章節6中自己訓練一個喚醒詞或者使用資料集中的英文語料“on/off”試試。

6. 自訓練喚醒詞

本案例是自訓練了一個“打開”,“關閉”快捷喚醒詞。本小節将帶你訓練一個新的快捷喚醒詞。

從錄音采集到部署到HaaS EDU K1的整個詳細流程如下:

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

6.1 語料采集

語料采集是一個比較耗費人力的事情,通常商業化工程中語料收集有專人或專門的資料公司收集整理,這裡提供了一個使用Python寫一個錄音工具,友善你快速錄音。

依賴項安裝

#pip install pyaudio
或者
#conda install pyaudio      

錄音配置

  • 語音檔案長度一秒
  • 單聲道、16KHz、wav格式
  • 快、中、慢三種不同速度進行錄制
  • 錄制次數100次以上,次數越多效果越好
  • 相對安靜環境

6.1.1 喚醒詞錄制

錄制時看到“開始錄音,請說話......”即可立即說出喚醒詞,比如“打開”、“關閉”。由于我們檢測一秒的喚醒詞,是以在注意要在一秒内說完整整個喚醒詞,錄制一次後會自動回放确認是否錄制完整,如果錄制完整,按Enter鍵繼續下一次錄制,如果錄制不完整或有其他雜音,按其他任意鍵删除剛才的錄音再繼續下一次錄制。

執行指令:

#python micro_speech/train/record.py      
基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

毫無疑問,這個教學案例是教你如何錄制一個人的聲音,如果想要達到商業化的識别率,就至少需要500人以上的聲音錄制。如果僅僅錄制你一個人的喚醒詞,那麼僅識别你的聲音是可以的,但其他人在喚醒時的成功率就會低很多。這個案例重點是教你了解喚醒詞訓練部署的原理。

6.1.2 背景噪音錄制

為了更好的識别,需要錄制一些背景噪音,模型訓練時會學習喚醒詞和背景噪音的差别。背景噪音可以錄制1~2分鐘。模型訓練時會自動從中随機選擇片段作為背噪加入喚醒詞中進行學習。

#python micro_speech/train/record_noise.py      
基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

錄制背景噪音,放到dataset/_background_noise_目錄。

6.1.3 建立自己的資料集

訓練腳本中預設采樣的預訓練資料集是Google釋出的Speech Commands(語音指令)資料集,該資料集是英文資料集。這裡我們以錄制中文的“打開”,“關閉”為例,每個詞錄制100次。錄制完成後分别命名為dakai和guanbi兩個檔案夾放入自定義的my_dataset目錄,然後從Speech Commands中選擇幾個單詞house、marvin、wow等喚醒詞作為“未知”類别放入到my_only_dataset目錄,它的作用是模型訓練時能夠從這些喚醒詞中識别想要的dakai和guanbi指令,dakai和guanbi可以了解為正面示例,“未知”類别為反面示例。整個指令詞個數盡量限制在十個以下,這樣訓練的時間不會過久。如果你有其他同樣長度且與錄音配置中格式一樣的喚醒詞,也可以加入進來。另外如果錄制的是100次喚醒詞,那麼其他作為“未知”類别的喚醒詞的錄音示例個數也盡量在100左右。錄制的背景噪音放入到_background_noise_目錄,訓練時腳本将自動從中随機選取一秒片段作為背景噪音加入到“無聲”類别中。

6.2 模型訓練

6.2.1 PC端訓練

PC上在VSCode中使用jupyter notbook插件打開tflite_micro_speech_demo/micro_speech/train/train_micro_speech_model.ipynb進行其他喚醒詞的訓練。

前提:

《HaaS AI之VSCode中搭建Python虛拟環境》

搭建完開發環境後,安裝tensorflow 1.15版本:

#conda create --name tf python=3.6
#conda activate tf
#conda install tensorflow=1.15      

基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

6.2.2 阿裡雲PAI平台訓練

如果PC性能有限,使用阿裡雲PAI平台進行訓練也是一個不錯的選擇,PAI-DSW是一款雲端機器學習開發IDE,為您提供互動式程式設計環境,适用于不同水準的開發者。你可以根據根據需要選擇個人版、GPU特價版或

探索者版(免費)

,相關使用手冊

DSW新手使用手冊

以使用DSW個人版為例:

  1. 登入 PAI控制台
  2. 在左側導航欄,選擇模型開發和訓練 > 互動式模組化(DSW)。
  3. 在頁面左上方,選擇目标地域。
  4. 在Notebook模組化服務頁面,單擊建立執行個體。
  5. 在配置執行個體向導頁面,配置參數,鏡像選擇tensorflow1.15-gpu-py36-cu101-ubuntu18.04版本。
基于TensorFlow Lite Micro在物聯網裝置上玩轉TinyML之離線語音喚醒1. 案例簡介2. 方案介紹3. 開發環境搭建4. 軟體架構5. 案例體驗6. 自訓練喚醒詞7. 總結

6.2.3 模型配置

無論在什麼平台上進行訓練,腳本中需要對訓練的參數進行一定的配置:

喚醒詞配置

WANTED_WORDS就是你訓練的喚醒詞。比如:

WANTED_WORDS="yes, on",yes/on對應于資料集dataset目錄的喚醒詞語料檔案夾。這裡跟你你要訓練的喚醒詞修改。

訓練步數配置

如果你的喚醒詞僅僅走數百條甚至數10條,那麼訓練的步數不用太久,修改:

TRANINGS_STEPS="1200, 300"

如果你有上千條以上,訓練的步數可以增加:

TRANINGS_STEPS="15000, 3000"

為了防止訓練欠拟合或者過拟合,訓練的時間長短需要反複驗證,找到最優的結果。

資料集配置

如果使用自己的資料集,請修改:

DATASET_DIR =  './dataset/'

6.3 模型部署

模型部署在HaaS EDU K1上,主要有三個步驟:

  1. 模型替換:将生成的模型檔案model.cc替換micro_speech/micro_features/model.cc檔案
  2. 标簽更新:在cc替換micro_speech/micro_features/micro_model_settings.cc中,将标簽名更換為你訓練的快捷詞,比如“打開”、“關閉”。由于标簽與模型的輸出張量元素是按照順序進行比對的,是以,需要按照将标簽提供給訓練腳本的順序列出這些标簽。
  3. 業務邏輯更新:在micro_speech/command_responder.cc中根據标簽更新相應的業務邏輯。目前在聽到“打開”後,會打開HaaS EDU K1上R/G/B LED燈。你也可以修改邏輯比如通過WiFi打開遠端的風扇或燈。這裡可以充分發揮你的想象力打造一些比較有意思的場景應用。

7. 總結

本案例在HaaS EDU K1上基于TFLite-Micro推理引擎進行語音喚醒詞的部署。也提供了從喚醒詞采集到模型訓練,模型部署的全鍊路開發流程,幫助您深入了解在低功耗MCU上如何進行離線語音識别的開發部署,期待您打造更多屬于你的離線喚醒詞應用。