#!/usr/bin/env python
coding: utf-8
import os from datetime import datetime from flask import Flask, request, jsonify import torchaudio import torchaudio.compliance.kaldi as kaldi from ais_bench.infer.interface import InferSession import numpy as np from pypinyin import lazy_pinyin # 导入 pypinyin
app = Flask(name)
class WeNetASR: def init(self, model_path, vocab_path): """初始化模型,加载词表""" self.vocabulary = self.load_vocab(vocab_path) self.model = InferSession(0, model_path) # 获取模型输入特征的最大长度 self.max_len = self.model.get_inputs()[0].shape[1]
def transcribe(self, wav_file):
"""执行模型推理,将录音文件转为文本。"""
try:
feats_pad, feats_lengths = self.preprocess(wav_file)
output = self.model.infer([feats_pad, feats_lengths])
txt = self.post_process(output)
return txt
except Exception as e:
print(f"Error during transcription: {e}")
return None
def preprocess(self, wav_file):
"""数据预处理"""
waveform, sample_rate = torchaudio.load(wav_file)
# 音频重采样,采样率16000
waveform, sample_rate = self.resample(waveform, sample_rate, resample_rate=16000)
# 计算fbank特征
feature = self.compute_fbank(waveform, sample_rate)
feats_lengths = np.array([feature.shape[0]]).astype(np.int32)
# 对输入特征进行padding,使符合模型输入尺寸
feats_pad = self.pad_sequence(feature, batch_first=True, padding_value=0, max_len=self.max_len)
feats_pad = feats_pad.numpy().astype(np.float32)
return feats_pad, feats_lengths
def post_process(self, output):
"""对模型推理结果进行后处理,根据贪心策略选择概率最大的token,去除重复字符和空白字符,得到最终文本。"""
encoder_out_lens, probs_idx = output[1], output[4]
token_idx_list = probs_idx[0, :, 0][:encoder_out_lens[0]]
token_idx_list = self.remove_duplicates_and_blank(token_idx_list)
text = ''.join(self.vocabulary[token_idx_list])
return text
@staticmethod
def remove_duplicates_and_blank(token_idx_list):
"""去除重复字符和空白字符"""
res = []
cur = 0
BLANK_ID = 0
while cur < len(token_idx_list):
if token_idx_list[cur] != BLANK_ID:
res.append(token_idx_list[cur])
prev = cur
while cur < len(token_idx_list) and token_idx_list[cur] == token_idx_list[prev]:
cur += 1
return res
@staticmethod
def pad_sequence(seq_feature, batch_first=True, padding_value=0, max_len=966):
"""对输入特征进行padding,使符合模型输入尺寸"""
feature_shape = seq_feature.shape
feat_len = feature_shape[0]
if feat_len > max_len:
# 如果输入特征长度大于模型输入尺寸,则截断
seq_feature = seq_feature[:max_len].unsqueeze(0)
return seq_feature
batch_size = 1
trailing_dims = feature_shape[1:]
if batch_first:
out_dims = (batch_size, max_len) + trailing_dims
else:
out_dims = (max_len, batch_size) + trailing_dims
out_tensor = seq_feature.data.new(*out_dims).fill_(padding_value)
if batch_first:
out_tensor[0, :feat_len, ...] = seq_feature
else:
out_tensor[:feat_len, 0, ...] = seq_feature
return out_tensor
@staticmethod
def resample(waveform, sample_rate, resample_rate=16000):
"""音频重采样"""
waveform = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=resample_rate)(waveform)
return waveform, resample_rate
@staticmethod
def compute_fbank(waveform, sample_rate, num_mel_bins=80, frame_length=25, frame_shift=10, dither=0.0):
"""提取filter bank音频特征"""
AMPLIFY_FACTOR = 1 << 15
waveform = waveform * AMPLIFY_FACTOR
mat = kaldi.fbank(
waveform,
num_mel_bins=num_mel_bins,
frame_length=frame_length,
frame_shift=frame_shift,
dither=dither,
energy_floor=0.0,
sample_frequency=sample_rate
)
return mat
@staticmethod
def load_vocab(txt_path):
"""加载词表"""
vocabulary = []
LEN_OF_VALID_FORMAT = 2
try:
with open(txt_path, 'r', encoding='utf-8') as fin:
for line in fin:
arr = line.strip().split()
# 词表格式:token id
if len(arr) != LEN_OF_VALID_FORMAT:
raise ValueError(f"Invalid line: {line}. Expect format: token id")
vocabulary.append(arr[0])
return np.array(vocabulary)
except FileNotFoundError:
print(f"Vocabulary file not found at: {txt_path}")
return None
except Exception as e:
print(f"Error loading vocabulary: {e}")
return None def initialize_model(): """初始化WeNetASR模型实例""" model_path = "/home/HwHiAiUser/samples/notebooks/09-speech-recognition/offline_encoder.om" vocab_path = "/home/HwHiAiUser/samples/notebooks/09-speech-recognition/vocab.txt"
在程序启动时就初始化模型
asr_model = initialize_model()
@app.route('/transcribe', methods=['POST']) def transcribe(): if 'file' not in request.files: return jsonify({"error": "No file part"}), 400
def transcribe_audio(wav_file): """处理单个音频文件的转录请求""" txt = asr_model.transcribe(wav_file) if txt: # 将文本转换为带拼音的形式 pinyin_txt = ' '.join(lazy_pinyin(txt))
if name == "main": app.run(debug=False, host='0.0.0.0', port=9999)
**错误如下 **** [INFO] acl init success [INFO] open device 0 success [INFO] load model /home/HwHiAiUser/samples/notebooks/09-speech-recognition/offline_encoder.om success [INFO] create model description success
- Serving Flask app 'main'
- Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
- Running on all addresses (0.0.0.0) Press CTRL+C to quit EE1001: The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] Solution: 1.Check the input parameter range of the function. 2.Check the function invocation relationship. TraceBack (most recent call last): ctx is NULL![FUNC:DevMalloc][FILE:api_impl.cc][LINE:1327] The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] alloc device memory failed, runtime result = 107002[FUNC:ReportCallError][FILE:log_inner.cpp][LINE:161] ctx is NULL![FUNC:GetDevErrMsg][FILE:api_impl.cc][LINE:4541] The argument is invalid.Reason: rtGetDevMsg execute failed, reason=[context pointer null]
[107002][Error code unknown] Malloc ptrData failed.[ERROR] MemoryHelper::MxbsMalloc failed device size:472960 ret:2 EE1001: The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] Solution: 1.Check the input parameter range of the function. 2.Check the function invocation relationship. TraceBack (most recent call last): ctx is NULL![FUNC:DevMalloc][FILE:api_impl.cc][LINE:1327] The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] alloc device memory failed, runtime result = 107002[FUNC:ReportCallError][FILE:log_inner.cpp][LINE:161] ctx is NULL![FUNC:GetDevErrMsg][FILE:api_impl.cc][LINE:4541] The argument is invalid.Reason: rtGetDevMsg execute failed, reason=[context pointer null]
EE1001: The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] Solution: 1.Check the input parameter range of the function. 2.Check the function invocation relationship. TraceBack (most recent call last): ctx is NULL![FUNC:DevMalloc][FILE:api_impl.cc][LINE:1327] The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] alloc device memory failed, runtime result = 107002[FUNC:ReportCallError][FILE:log_inner.cpp][LINE:161] ctx is NULL![FUNC:GetDevErrMsg][FILE:api_impl.cc][LINE:4541] The argument is invalid.Reason: rtGetDevMsg execute failed, reason=[context pointer null]
[107002][Error code unknown] Malloc ptrData failed.[ERROR] MemoryHelper::MxbsMalloc failed device size:4 ret:2 [ERROR] Check i:0 name:speech in size:2 needsize:472960 not match [ERROR] Check InVector failed ret:-1 Error during transcription: [-1][ACL: general failure] Failed to transcribe audio. 192.168.1.91 - - [28/Dec/2024 10:04:43] "POST /transcribe HTTP/1.1" 500 - [INFO] unload model success, model Id is 1
内容如下: total used free shared buff/cache available Mem: 7.4Gi 1.2Gi 4.6Gi 17Mi 1.6Gi 5.9Gi Swap: 8.0Gi 0B 8.0Gi
#!/usr/bin/env python
coding: utf-8
import os from datetime import datetime from flask import Flask, request, jsonify import torchaudio import torchaudio.compliance.kaldi as kaldi from ais_bench.infer.interface import InferSession import numpy as np from pypinyin import lazy_pinyin # 导入 pypinyin
app = Flask(name)
class WeNetASR: def init(self, model_path, vocab_path): """初始化模型,加载词表""" self.vocabulary = self.load_vocab(vocab_path) self.model = InferSession(0, model_path) # 获取模型输入特征的最大长度 self.max_len = self.model.get_inputs()[0].shape[1]
def transcribe(self, wav_file): """执行模型推理,将录音文件转为文本。""" try: feats_pad, feats_lengths = self.preprocess(wav_file) output = self.model.infer([feats_pad, feats_lengths]) txt = self.post_process(output) return txt except Exception as e: print(f"Error during transcription: {e}") return None def preprocess(self, wav_file): """数据预处理""" waveform, sample_rate = torchaudio.load(wav_file) # 音频重采样,采样率16000 waveform, sample_rate = self.resample(waveform, sample_rate, resample_rate=16000) # 计算fbank特征 feature = self.compute_fbank(waveform, sample_rate) feats_lengths = np.array([feature.shape[0]]).astype(np.int32) # 对输入特征进行padding,使符合模型输入尺寸 feats_pad = self.pad_sequence(feature, batch_first=True, padding_value=0, max_len=self.max_len) feats_pad = feats_pad.numpy().astype(np.float32) return feats_pad, feats_lengths def post_process(self, output): """对模型推理结果进行后处理,根据贪心策略选择概率最大的token,去除重复字符和空白字符,得到最终文本。""" encoder_out_lens, probs_idx = output[1], output[4] token_idx_list = probs_idx[0, :, 0][:encoder_out_lens[0]] token_idx_list = self.remove_duplicates_and_blank(token_idx_list) text = ''.join(self.vocabulary[token_idx_list]) return text @staticmethod def remove_duplicates_and_blank(token_idx_list): """去除重复字符和空白字符""" res = [] cur = 0 BLANK_ID = 0 while cur < len(token_idx_list): if token_idx_list[cur] != BLANK_ID: res.append(token_idx_list[cur]) prev = cur while cur < len(token_idx_list) and token_idx_list[cur] == token_idx_list[prev]: cur += 1 return res @staticmethod def pad_sequence(seq_feature, batch_first=True, padding_value=0, max_len=966): """对输入特征进行padding,使符合模型输入尺寸""" feature_shape = seq_feature.shape feat_len = feature_shape[0] if feat_len > max_len: # 如果输入特征长度大于模型输入尺寸,则截断 seq_feature = seq_feature[:max_len].unsqueeze(0) return seq_feature batch_size = 1 trailing_dims = feature_shape[1:] if batch_first: out_dims = (batch_size, max_len) + trailing_dims else: out_dims = (max_len, batch_size) + trailing_dims out_tensor = seq_feature.data.new(*out_dims).fill_(padding_value) if batch_first: out_tensor[0, :feat_len, ...] = seq_feature else: out_tensor[:feat_len, 0, ...] = seq_feature return out_tensor @staticmethod def resample(waveform, sample_rate, resample_rate=16000): """音频重采样""" waveform = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=resample_rate)(waveform) return waveform, resample_rate @staticmethod def compute_fbank(waveform, sample_rate, num_mel_bins=80, frame_length=25, frame_shift=10, dither=0.0): """提取filter bank音频特征""" AMPLIFY_FACTOR = 1 << 15 waveform = waveform * AMPLIFY_FACTOR mat = kaldi.fbank( waveform, num_mel_bins=num_mel_bins, frame_length=frame_length, frame_shift=frame_shift, dither=dither, energy_floor=0.0, sample_frequency=sample_rate ) return mat @staticmethod def load_vocab(txt_path): """加载词表""" vocabulary = [] LEN_OF_VALID_FORMAT = 2 try: with open(txt_path, 'r', encoding='utf-8') as fin: for line in fin: arr = line.strip().split() # 词表格式:token id if len(arr) != LEN_OF_VALID_FORMAT: raise ValueError(f"Invalid line: {line}. Expect format: token id") vocabulary.append(arr[0]) return np.array(vocabulary) except FileNotFoundError: print(f"Vocabulary file not found at: {txt_path}") return None except Exception as e: print(f"Error loading vocabulary: {e}") return Nonedef initialize_model(): """初始化WeNetASR模型实例""" model_path = "/home/HwHiAiUser/samples/notebooks/09-speech-recognition/offline_encoder.om" vocab_path = "/home/HwHiAiUser/samples/notebooks/09-speech-recognition/vocab.txt"
if not os.path.exists(model_path) or not os.path.exists(vocab_path): print("Model or vocabulary file not found.") exit(1) return WeNetASR(model_path, vocab_path)在程序启动时就初始化模型
asr_model = initialize_model()
@app.route('/transcribe', methods=['POST']) def transcribe(): if 'file' not in request.files: return jsonify({"error": "No file part"}), 400
file = request.files['file'] if file.filename == '': return jsonify({"error": "No selected file"}), 400 if file: # 创建临时文件保存上传的音频 temp_filename = os.path.join("/tmp", file.filename) file.save(temp_filename) start_time = datetime.now() result = transcribe_audio(temp_filename) end_time = datetime.now() time_difference = (end_time - start_time).total_seconds() # 删除临时文件 os.remove(temp_filename) if result: response = { "transcribed_text": result["transcribed_text"], "pinyin_text": result["pinyin_text"], "processing_time": f"{time_difference:.2f} seconds" } return jsonify(response), 200 else: return jsonify({"error": "Failed to transcribe audio."}), 500def transcribe_audio(wav_file): """处理单个音频文件的转录请求""" txt = asr_model.transcribe(wav_file) if txt: # 将文本转换为带拼音的形式 pinyin_txt = ' '.join(lazy_pinyin(txt))
return { "transcribed_text": txt, "pinyin_text": pinyin_txt } else: print("Failed to transcribe audio.") return Noneif name == "main": app.run(debug=False, host='0.0.0.0', port=9999)
**错误如下 **** [INFO] acl init success [INFO] open device 0 success [INFO] load model /home/HwHiAiUser/samples/notebooks/09-speech-recognition/offline_encoder.om success [INFO] create model description success
[107002][Error code unknown] Malloc ptrData failed.[ERROR] MemoryHelper::MxbsMalloc failed device size:472960 ret:2 EE1001: The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] Solution: 1.Check the input parameter range of the function. 2.Check the function invocation relationship. TraceBack (most recent call last): ctx is NULL![FUNC:DevMalloc][FILE:api_impl.cc][LINE:1327] The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] alloc device memory failed, runtime result = 107002[FUNC:ReportCallError][FILE:log_inner.cpp][LINE:161] ctx is NULL![FUNC:GetDevErrMsg][FILE:api_impl.cc][LINE:4541] The argument is invalid.Reason: rtGetDevMsg execute failed, reason=[context pointer null]
EE1001: The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] Solution: 1.Check the input parameter range of the function. 2.Check the function invocation relationship. TraceBack (most recent call last): ctx is NULL![FUNC:DevMalloc][FILE:api_impl.cc][LINE:1327] The argument is invalid.Reason: rtMalloc execute failed, reason=[context pointer null] alloc device memory failed, runtime result = 107002[FUNC:ReportCallError][FILE:log_inner.cpp][LINE:161] ctx is NULL![FUNC:GetDevErrMsg][FILE:api_impl.cc][LINE:4541] The argument is invalid.Reason: rtGetDevMsg execute failed, reason=[context pointer null]
[107002][Error code unknown] Malloc ptrData failed.[ERROR] MemoryHelper::MxbsMalloc failed device size:4 ret:2 [ERROR] Check i:0 name:speech in size:2 needsize:472960 not match [ERROR] Check InVector failed ret:-1 Error during transcription: [-1][ACL: general failure] Failed to transcribe audio. 192.168.1.91 - - [28/Dec/2024 10:04:43] "POST /transcribe HTTP/1.1" 500 - [INFO] unload model success, model Id is 1
内容如下: total used free shared buff/cache available Mem: 7.4Gi 1.2Gi 4.6Gi 17Mi 1.6Gi 5.9Gi Swap: 8.0Gi 0B 8.0Gi