Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run ASRT on smartphones. #328

Open
Evanston0624 opened this issue May 6, 2024 · 8 comments
Open

Run ASRT on smartphones. #328

Evanston0624 opened this issue May 6, 2024 · 8 comments

Comments

@Evanston0624
Copy link

Evanston0624 commented May 6, 2024

我希望在android & IOS上運行ASRT,我使用python進行後續的測試
首先我將音檔讀入是使用:

import librosa
wav_signal, sample_rate = librosa.load(audio_path, sr=None)

接下來我讀取ASRT的模型參數(這個模型除了原始數據外,還加入了CV的TW數據。

from model_zoo.speech_model.keras_backend import SpeechModel251BN
def load_tf_model(model_path):
	AUDIO_LENGTH = 1600
	AUDIO_FEATURE_LENGTH = 200
	CHANNELS = 1
	# 原始拼音=1427、cv-TW=3、空白=1
	OUTPUT_SIZE = 1431
	sm251bn = SpeechModel251BN(
		input_shape=(AUDIO_LENGTH, AUDIO_FEATURE_LENGTH, CHANNELS),
		output_size=OUTPUT_SIZE
    )
	sm251bn.load_weights('./save_models/SpeechModel251bn/SpeechModel251bn_epoch40.model.h5')
	trained_model, base_model = sm251bn.get_model()
	return trained_model, base_model

我透過上述的代碼取得欲訓練的模型,trained_model是包含CTC loss的,因此我使用base_model 進行轉換。我共使用了ONNX與TF_lite進行測試:
TF_lite:

def convert_tf_lite(tf_model, save_path):
	# 轉換为 TensorFlow Lite 模型
	converter = tf.lite.TFLiteConverter.from_keras_model(tf_model)
	tflite_model = converter.convert()
	# 保存 TensorFlow Lite 模型
	with open(save_path, 'wb') as f:
		f.write(tflite_model)
	return os.path.isfile(save_path)

ONNX:

def convert_tf_onnx(tf_model, save_path, opset):
	import tensorflow as tf
	import tf2onnx
	# 轉換為 ONNX 格式
	onnx_model, _ = tf2onnx.convert.from_keras(tf_model, opset=opset)

	# 保存 ONNX 模型
	with open(save_path, 'wb') as f:
		f.write(onnx_model.SerializeToString())
	return os.path.isfile(save_path)

接下來我使用了修改過的Spectrogram進行特徵提取

from speech_features import Spectrogram
data_pre = Spectrogram()
audio_features = data_pre.onnx_run(wavsignal=wav_signal, fs=sample_rate)
audio_features = adaptive_padding(input_data=audio_features, target_length=1600)

我在原始的Spectrogram類參考run創建了onnx_run,實際上只是匹配輸入參數的維度等資訊。

def onnx_run(self, wavsignal, fs=16000):
	if fs != 16000:
		raise ValueError(
			f"[Error] ASRT currently only supports wav audio files with a sampling rate of 16000 Hz, but this "
			f"audio is {fs} Hz.")

	# wav波形 加时间窗以及时移10ms
	time_window = 25  # 单位ms
	window_length = int(fs / 1000 * time_window)  # 计算窗长度的公式,目前全部为400固定值

	wav_arr = np.array(wavsignal)

	range0_end = int(len(wavsignal) / fs * 1000 - time_window) // 10 + 1  # 计算循环终止的位置,也就是最终生成的窗数
	data_input = np.zeros((range0_end, window_length // 2), dtype=np.float64)  # 用于存放最终的频率特征数据
	data_line = np.zeros((1, window_length), dtype=np.float64)

	for i in range(0, range0_end):
		p_start = i * 160
		p_end = p_start + 400

		data_line = wav_arr[p_start:p_end]
		data_line = data_line * self.w  # 加窗
		data_line = np.abs(fft(data_line))

		data_input[i] = data_line[0: window_length // 2]  # 设置为400除以2的值(即200)是取一半数据,因为是对称的

	data_input = np.log(data_input + 1)
	return data_input

接下來透過adaptive_padding將輸入的特徵轉換成跟原始輸入相同的尺寸

def adaptive_padding(input_data, target_length=1600):
	input_data = input_data.astype(np.float32)

	input_data = np.expand_dims(input_data, axis=0)  # 添加批量维度
	input_data = np.expand_dims(input_data, axis=-1)  # 添加通道维度
	# 计算需要填充的长度
	current_length = input_data.shape[1]
	padding_length = max(0, target_length - current_length)

	# 计算填充宽度
	left_padding = padding_length // 2
	right_padding = padding_length - left_padding
	pad_width = [(0, 0), (left_padding, right_padding), (0, 0), (0, 0)]

	# 进行填充
	padded_data = np.pad(input_data, pad_width, mode='constant').astype(np.float32)

 	return padded_data

經由上述轉換後的模型輸出結果,都是空白塊分數最高,後續調用tf.nn.ctc_beam_search_decoder與K.ctc_decode就沒有意義了。

想請問是否有相關的研究或實踐方法可以推薦?又或者我需要提供更多的測試或特定文件?

感謝

@Evanston0624
Copy link
Author

補充:
相同的模型使用predict_speech_file.py是可以正確預測輸出的

import os

from speech_model import ModelSpeech
from model_zoo.speech_model.keras_backend import SpeechModel251BN
from speech_features import Spectrogram
from language_model3 import ModelLanguage

os.environ["CUDA_VISIBLE_DEVICES"] = ""

AUDIO_LENGTH = 1600
AUDIO_FEATURE_LENGTH = 200
CHANNELS = 1
# 默认输出的拼音的表示大小是1428,即1427个拼音+1个空白块
OUTPUT_SIZE = 1431
sm251bn = SpeechModel251BN(
    input_shape=(AUDIO_LENGTH, AUDIO_FEATURE_LENGTH, CHANNELS),
    output_size=OUTPUT_SIZE
)
feat = Spectrogram()
ms = ModelSpeech(sm251bn, feat, max_label_length=64)
now_path = os.path.abspath(os.getcwd())

ms.load_model(now_path+'/save_models/SpeechModel251bn_cv/' + 'SpeechModel251bn_epoch40.model.base.h5')

res = ms.recognize_speech_from_file('test1.wav')
print('*[提示] 声学模型语音识别结果:\n', res)

@Evanston0624
Copy link
Author

更新:
我透過原始的from utils.ops import read_wav_data來讀取音檔就可以了

def load_audio(audio_path):
    from utils.ops import read_wav_data
    wav_signal, sample_rate, _, _ = read_wav_data(audio_path)
    return wav_signal, sample_rate

轉頻譜的部分目前改回原本Spectrogram類下的run

# load audio
from speech_features import Spectrogram
data_pre = Spectrogram()
# 使用函數直接從音訊檔案中加載音訊數據並轉換為所需的格式
audio_path = 'test1.wav'  # 替換為你的音訊檔案路徑
wav_signal, sample_rate = load_audio(audio_path)

# audio pre
# audio_features = data_pre.onnx_run(wavsignal=wav_signal, fs=sample_rate)
audio_features = data_pre.run(wavsignal=wav_signal, fs=sample_rate)
audio_features = adaptive_padding(input_data=audio_features, target_length=1600)

@nl8590687
Copy link
Owner

不建议直接在手机端运行,否则计算性能和依赖环境的安装配置都较为复杂,最佳方案是模型部署于服务器,手机通过API接口调用。具体讲解可以看AI柠檬博客相关文章。

@nl8590687
Copy link
Owner

如果实在要在手机端部署也可以,那就需要你自行用对应平台支持的框架重写一遍推理能力了

@Evanston0624
Copy link
Author

Evanston0624 commented May 6, 2024

您好,我們的服務器在多用戶調用時的響應速度與不如預期,後續我有自己編寫一套透過socket的TCP+UDP實踐註冊與傳遞語音包的多進程程序,但在多用戶組的情況下響應也是不如預期。(上述問題可能是存在我們的硬體配置或網路等)

因此我想透過ONNX與TF-Lite來實現移動設備推理,我剛剛實際測試已經可以生成結果了,稍晚會把代碼發上來(python的測試代碼)。後續應該會使用java開發app程序,那這部分的工作應該如下:

  • 讀取音檔
  • 轉頻譜
  • tf-lite模型推理
  • onnx模型推理
  • ctc推理

我認為如果可以確定python的數據格式與java上的差異,應該可以正確運行

@nl8590687
Copy link
Owner

单进程因为只有一个计算图资源,多用户并发调用响应速度慢是很正常的,你需要做的是多实例集群部署,负载均衡,而不是单纯的改通信协议。AI模型部署本身就是很耗费资源的。

@Evanston0624
Copy link
Author

Evanston0624 commented May 16, 2024

感謝nl8590687對於製作ASR服務的意見與提點,對於TF-lite,模型只要可以轉換,在android有支援的版本下有硬體加速。我認為coreML也是如此(我還沒有了解此部分)。

我已經實踐了在Android上使用ASRT訓練的模型(轉為TF-lite)進行推理。

https://github.com/Evanston0624/ASRT_model_Android/tree/main
README 稍晚會創建,主要代碼在:
https://github.com/Evanston0624/ASRT_model_Android/tree/main/app/src/main/java/com/example/myapplication

上述的庫主要實踐了:

  1. 數據前處理(載入音訊的格式>頻譜>padding)
  2. 載入模型
  3. 調用模型取得輸出
  4. 編寫了一個ctc_decode(此部分跟ASRT調用的Keras的ctc_decoder不同,但我在小樣本下測試結果沒問題)

**在前三個階段在小樣本時的輸出數值跟python上相同。
**對於buffer的資料流傳遞可能存在問題,防呆可能也不完善。

尚未實踐

  • 將phoneme轉為詞彙
  • 運行效能測試

將ASRT訓練的模型轉為TF-lite的代碼後續會補上

@Evanston0624
Copy link
Author

Evanston0624 commented Jun 13, 2024

更新了ASRT模型轉換為onnx、tf-lite與core-ml的代碼:
子專案

這個子專案包含了一個ASRT的預訓練模型(比ASRT提供的數據集多了CV14.0,發音多了三個),還有三個轉換好的模型。子專案可以使用python進行onnx、tf-lite的轉換與推理,後續會完善量化評估等資訊。

對於手機運行的效能(android & ios),因為我不擅長編寫手機端 因此我有合作的工程師會一起完善這部分,對於手機版本支援與效能評估會在後續進行完整測試後補上。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants