当前位置:   article > 正文

TinyML - 唤醒词检测_tfjs 唤醒词

tfjs 唤醒词
让我们使用 W5500-evb-pico 进行唤醒词检测。

转发: TinyML - WakeWord Detection

项目介绍

概括

这篇文章讨论了语音识别系统。 它涵盖了语音识别的原理,包括傅里叶变换和 STFT(短时傅里叶变换)。 它提供了使用 Arduino 和 Python 收集和预处理语音数据、训练模型以及使用 TensorFlow Lite 转换模型的代码示例。 详细解释了系统架构和操作,以及使用 Arduino 和 Python 代码的实现步骤。

概述

为了更好地了解资源有限的物联网设备中唤醒词检测的底层技术,我们将对远不如 iPhone 等功能强大的 Arduino 进行研究。

我们将使用现有数据集,使用 Tensorflow 训练我们的模型,将其转换为 TensorFlow Lite 模型并将模型部署到 Arduino 中。 然后我们通过Arduino IDE消息来获取输出信息。

先验知识

语音识别原理-声音

声源用以下波形绘制。

从声源本身提取特征值是很困难的,并且由于特征值的数量呈指数增长,因此不可能仅从传感器值中提取特征。

傅里叶变换和频谱图

- 对声源进行傅立叶变换,将[时间轴]的波形变为[频率轴]的波形。

可以提取增益大的频率下的特性。

-然而,由于语音具有时间特征,因此仅根据频率特征很难对其进行分类。

- 例如,如果只看频率特性,则无法区分“Banana”和“naBana”。

STFT(短时傅立叶变换)

-通过将声源的时间分成短段来尝试傅里叶变换

-逆时针旋转傅立叶变换图的累加形式称为STFT。

- 频率范围的大小表示为 0 到 255 之间的值。

- 在此代码中,我们尝试通过转换为 (257X4) 张量进行傅里叶变换。

系统图

在 PC 上将数据集转换为频谱图并对其进行训练。
它使用 CNN 进行学习,并将学习到的数据转换为 TFLite 并二进制化为 .cpp 以适合 Arduino 模型。
然后,Arduino 加载模型并将传感器值注入模型中以进行推理。

代码

Get_sounds.ino
  1. #include <fix_fft.h>
  2. const int analogSensorPin = 26;
  3. const int ledPin = LED_BUILTIN;
  4. const int sampleRate = 1000;
  5. const int sampleTime = 1;
  6. const int totalSamples = sampleRate * sampleTime;
  7. int16_t vReal[totalSamples];
  8. int16_t vImag[totalSamples];
  9. unsigned long startTime;
  10. void fft_wrapper(int16_t* vReal, int16_t* vImag, int n, int inverse) {
  11. fix_fft((char*)vReal, (char*)vImag, n, inverse);
  12. }
  13. void setup() {
  14. Serial.begin(9600);
  15. pinMode(ledPin, OUTPUT);
  16. }
  17. void loop() {
  18. //Serial.print("fft calculate");
  19. digitalWrite(ledPin, HIGH);
  20. startTime = millis();
  21. for (int i = 0; i < totalSamples; i++) {
  22. vReal[i] = analogRead(analogSensorPin);
  23. vImag[i] = 0; // 허수부는 0으로 초기화
  24. while (millis() < startTime + (i * 1000.0 / sampleRate));
  25. }
  26. digitalWrite(ledPin, LOW);
  27. // FFT 계산
  28. fft_wrapper(vReal, vImag, 10, 0);
  29. // FFT 결과 출력
  30. for (int i = 0; i < totalSamples / 2; i++) {
  31. double frequency = (i * 1.0 * sampleRate) / totalSamples;
  32. double magnitude = sqrt(vReal[i] * vReal[i] + vImag[i] * vImag[i]);
  33. //Serial.print(frequency);
  34. //Serial.print(",");
  35. Serial.println(magnitude);
  36. }
  37. delay(2000);
  38. }

get_voice_data.py

  1. import serial
  2. import numpy as np
  3. import librosa
  4. import csv
  5. # 아두이노에서 데이터 읽어오기
  6. ser = serial.Serial('COM13', 9600)
  7. sampleRate = 1000
  8. sampleTime = 1
  9. totalSamples = sampleRate * sampleTime
  10. while True:
  11. try:
  12. data = []
  13. while len(data) < totalSamples:
  14. if ser.in_waiting:
  15. value = ser.readline().decode().strip()
  16. if value:
  17. data.append(float(value))
  18. # 데이터 전처리
  19. data = np.array(data)
  20. data = data / 1023.0 # 정규화
  21. # 오디오 데이터 변환
  22. sr = sampleRate # 샘플링 레이트
  23. audio = librosa.resample(data, orig_sr=sr, target_sr=sr) # 리샘플링
  24. stft = librosa.stft(audio, n_fft=512, hop_length=256) # STFT 적용
  25. spectrogram = librosa.amplitude_to_db(np.abs(stft), ref=np.max) # 스펙트로그램 변환
  26. # 스펙트로그램 데이터를 CSV 파일로 저장
  27. with open('you.csv', 'a', newline='') as csvfile:
  28. writer = csv.writer(csvfile)
  29. writer.writerows(spectrogram)
  30. except KeyboardInterrupt:
  31. break
  32. ser.close()

没有什么

噪音

Train_voice_data.ipynb

  1. import numpy as np
  2. import pandas as pd
  3. import tensorflow as tf
  4. from sklearn.model_selection import train_test_split
  5. # CSV 파일 읽어오기
  6. nothing_data = pd.read_csv('nothing.csv', header=None)
  7. wiznet_data = pd.read_csv('wiznet.csv', header=None)
  8. you_data = pd.read_csv('you.csv', header=None)
  9. # 데이터 병합 및 레이블 할당
  10. data = np.vstack((nothing_data, wiznet_data, you_data))
  11. labels = np.concatenate((np.zeros(len(nothing_data)), np.ones(len(wiznet_data)), np.ones(len(you_data))*2))
  12. # 학습 데이터와 테스트 데이터 분리
  13. X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.2, random_state=42)
  14. # 입력 데이터 크기 확인
  15. input_shape = X_train.shape[1]
  16. # 모델 구성
  17. model = tf.keras.Sequential([
  18. tf.keras.layers.Dense(128, activation='relu', input_shape=(input_shape,)),
  19. tf.keras.layers.Dense(64, activation='relu'),
  20. tf.keras.layers.Dense(3, activation='softmax')
  21. ])
  22. # 모델 컴파일
  23. model.compile(optimizer='adam',
  24. loss='sparse_categorical_crossentropy',
  25. metrics=['accuracy'])
  26. # 모델 학습
  27. model.fit(X_train, y_train, epochs=50, batch_size=32, validation_data=(X_test, y_test))
  28. # 모델 평가
  29. test_loss, test_acc = model.evaluate(X_test, y_test)
  30. print('Test accuracy:', test_acc)
  31. # TensorFlow Lite 변환
  32. converter = tf.lite.TFLiteConverter.from_keras_model(model)
  33. tflite_model = converter.convert()
  34. # TensorFlow Lite 모델 저장
  35. with open('voice_recognition_model.tflite', 'wb') as f:
  36. f.write(tflite_model)
  37. def representative_dataset_gen():
  38. for i in range(len(X_train)):
  39. yield [X_train[i].astype(np.float32)]
  40. # TensorFlow Lite 변환 및 완전 정수 양자화
  41. converter = tf.lite.TFLiteConverter.from_keras_model(model)
  42. converter.optimizations = [tf.lite.Optimize.DEFAULT]
  43. converter.representative_dataset = representative_dataset_gen
  44. converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
  45. converter.inference_input_type = tf.int8
  46. converter.inference_output_type = tf.int8
  47. tflite_quant_model = converter.convert()
  48. # 완전 정수 양자화된 TensorFlow Lite 모델 저장
  49. with open('voice_recognition_model_quant.tflite', 'wb') as f:
  50. f.write(tflite_quant_model)

这就是训练的结果。

因为数据集本身并不充足,所以表现出不稳定的学习情况。 在丢失的情况下,准确性会因突然激增而降低,但总体上似乎是在学习。

考虑到分类分为三种且数据不足,我对比预期更高的准确率感到满意。

WakeWord_Detection.ino

  1. #include <TensorFlowLite.h>
  2. #define ARDUINO_EXCLUDE_CODE
  3. #include "tensorflow/lite/micro/kernels/all_ops_resolver.h"
  4. #include "tensorflow/lite/micro/micro_error_reporter.h"
  5. #include "tensorflow/lite/micro/micro_interpreter.h"
  6. #include "tensorflow/lite/schema/schema_generated.h"
  7. #include "tensorflow/lite/version.h"
  8. #undef ARDUINO_EXCLUDE_CODE
  9. #include "voice_recognition_model.h" // 변환된 모델 파일 포함
  10. #include <fix_fft.h>
  11. // 모델 관련 상수 정의
  12. const int kTensorArenaSize = 4 * 1024; // 텐서 아레나 크기 증가
  13. const int kNumInputs = 1;
  14. const int kNumOutputs = 1;
  15. const int kInputFrames = 4;
  16. const int kInputShape[4] = {1, 257, kInputFrames, 1};
  17. const int kOutputSize = 3;
  18. const int analogSensorPin = 26;
  19. const int ledPin = LED_BUILTIN;
  20. const int sampleRate = 1000;
  21. const int sampleTime = 1;
  22. const int totalSamples = sampleRate * sampleTime;
  23. int16_t vReal[totalSamples];
  24. int16_t vImag[totalSamples];
  25. unsigned long startTime;
  26. // 텐서 아레나 메모리 할당
  27. uint8_t tensor_arena[kTensorArenaSize];
  28. // 오디오 입력 버퍼
  29. float audio_buffer[257 * kInputFrames];
  30. int audio_buffer_index = 0;
  31. // 모델 추론 함수
  32. String inference(float* input_data) {
  33. // 에러 리포터 설정
  34. tflite::MicroErrorReporter micro_error_reporter;
  35. tflite::ErrorReporter* error_reporter = &micro_error_reporter;
  36. // 플랫버퍼 모델 포인터 설정
  37. const tflite::Model* model = ::tflite::GetModel(voice_recognition_model_tflite);
  38. if (model->version() != TFLITE_SCHEMA_VERSION) {
  39. return "Model schema mismatch";
  40. }
  41. // 모델 연산자 설정
  42. tflite::ops::micro::AllOpsResolver resolver;
  43. // 인터프리터 생성
  44. tflite::MicroInterpreter interpreter(model, resolver, tensor_arena, kTensorArenaSize, error_reporter);
  45. // 텐서 할당
  46. interpreter.AllocateTensors();
  47. // 입력 텐서 포인터 얻기
  48. TfLiteTensor* input = interpreter.input(0);
  49. // 입력 데이터 복사
  50. for (int i = 0; i < 257 * kInputFrames; i++) {
  51. input->data.f[i] = input_data[i];
  52. }
  53. // 추론 실행
  54. TfLiteStatus invoke_status = interpreter.Invoke();
  55. if (invoke_status != kTfLiteOk) {
  56. return "Invoke failed";
  57. }
  58. // 출력 텐서 포인터 얻기
  59. TfLiteTensor* output = interpreter.output(0);
  60. // 출력 결과 처리
  61. int predicted_class = 0;
  62. float max_probability = output->data.f[0];
  63. for (int i = 1; i < kOutputSize; i++) {
  64. if (output->data.f[i] > max_probability) {
  65. predicted_class = i;
  66. max_probability = output->data.f[i];
  67. }
  68. }
  69. // 결과 반환
  70. if (predicted_class == 0) {
  71. return "Nothing";
  72. } else if (predicted_class == 1) {
  73. return "WIZnet";
  74. } else if (predicted_class == 2) {
  75. return "You";
  76. }
  77. return "Unknown";
  78. }
  79. void fft_wrapper(int16_t* vReal, int16_t* vImag, int n, int inverse) {
  80. fix_fft((char*)vReal, (char*)vImag, n, inverse);
  81. }
  82. void setup() {
  83. // 시리얼 통신 초기화
  84. Serial.begin(9600);
  85. pinMode(ledPin, OUTPUT);
  86. }
  87. void loop() {
  88. Serial.print("PLEASE Recognized word: ");
  89. // 오디오 입력 받기
  90. if (audio_buffer_index < 257 * kInputFrames) {
  91. digitalWrite(ledPin, HIGH);
  92. startTime = millis();
  93. for (int i = 0; i < totalSamples; i++) {
  94. vReal[i] = analogRead(analogSensorPin);
  95. vImag[i] = 0; // 허수부는 0으로 초기화
  96. while (millis() < startTime + (i * 1000.0 / sampleRate));
  97. }
  98. digitalWrite(ledPin, LOW);
  99. // FFT 계산
  100. fft_wrapper(vReal, vImag, 10, 0);
  101. // FFT 결과를 audio_buffer에 저장
  102. for (int i = 0; i < totalSamples / 2; i++) {
  103. double magnitude = sqrt(vReal[i] * vReal[i] + vImag[i] * vImag[i]);
  104. audio_buffer[audio_buffer_index++] = magnitude;
  105. }
  106. }
  107. // 오디오 버퍼가 가득 찼을 때 추론 수행
  108. if (audio_buffer_index >= 257 * kInputFrames) {
  109. // 추론 실행
  110. String result = inference(audio_buffer);
  111. // 결과 출력
  112. Serial.print("Recognized word: ");
  113. Serial.println(result);
  114. // 오디오 버퍼 초기화
  115. audio_buffer_index = 0;
  116. }
  117. delay(500); // 500밀리초 대기
  118. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/659598
推荐阅读
相关标签
  

闽ICP备14008679号