使用 TensorFlow 官方提供的 speech_commands 语音分类模块,训练自己数据集。
详细文档请参考:https://blog.csdn.net/rainhurt/article/details/89097842 ,详解的非常清晰。
准备数据集,由于训练样本数据我们设定的是 1000ms, 所以我们需要将数据都调整成 1000ms 的音频,但实际情况中数据集中的音频有的不足1000ms,有的超过1000ms,所以需要我们动态调整成一样的大小。为了模拟真实的语音情况,我将不足1000ms的音频,使用背景噪音来补齐音频,多余1000ms的音频,截取音频能量值最大的部分,使用了一个开源的音频库.https://github.com/amsehili/auditok
背景噪音和静音音频片段需指定文件夹分别为
_background_noise_
和_silence_
代码实现:
import os
import random
import argparse
from auditok import AudioRegion
from tqdm import tqdm
"""
转换音频,保持音频大小一致
*** 如果大于设定音频大小
1)则截取能量高的一部分
2)如果声音太小,有可能导致截取不到音频数据
*** 如果小于设定音频大小
1)则前后补充提供的背景噪音音频
"""
FLAGS = None
noise_bg_files = []
def init_noise_bg_file():
for root, dirs, files in os.walk(FLAGS.noise_bg_dir):
global noise_bg_files
noise_bg_files = files
if len(noise_bg_files) == 0:
return -1
return 1
def get_random_noise_file():
file_size = len(noise_bg_files)
index = random.randint(0, file_size)
path = os.path.join(FLAGS.noise_bg_dir,noise_bg_files[index] )
return AudioRegion.load(path)
def process_audio(file_dir, dir_name, frame_len=16000, sr=16000):
try:
noise_region = get_random_noise_file()
if not noise_region:
return
with tqdm(os.listdir(file_dir), ncols=100) as files:
del_count = 0
save_count = 0
for f in files:
audio_path = os.path.join(file_dir, f)
file_name, file_type = os.path.splitext(audio_path)
if file_type != ".wav":
continue
audio = AudioRegion.load(audio_path)
s_len = len(audio)
if s_len < frame_len:
pad_len = frame_len - s_len
pad_left = pad_len // 2
pad_right = pad_len - pad_left
audio_left_region = noise_region[:pad_left]
audio_right_region = noise_region[:pad_right]
audio_full = audio_left_region + audio + audio_right_region
elif s_len > frame_len:
audio_full = audio.split(min_dur=1, max_dur=1, max_silence=0.3,
drop_trailing_silence=False,
strict_min_dur=False, energy_threshold=60)
audio_full = list(audio_full)
else:
audio_full = audio
out_file_dir_path = os.path.join(FLAGS.out_audio_path, dir_name)
if not os.path.exists(out_file_dir_path):
os.makedirs(out_file_dir_path)
out_file_full_path = os.path.join(out_file_dir_path, f)
if isinstance(audio_full, list):
if len(audio_full) > 0:
audio_full[0].save(out_file_full_path)
save_count += 1
else:
del_count += 1
else:
audio_full.save(out_file_full_path)
save_count += 1
print("save: " + str(save_count) + " del: " + str(del_count))
except KeyboardInterrupt:
files.close()
raise
files.close()
def main():
for f in os.listdir(FLAGS.audio_path):
print("convert dir :" + f)
file_path = os.path.join(FLAGS.audio_path, f)
if not os.path.isdir(file_path):
continue
process_audio(file_path, f)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
'--audio_path',
type=str,
default='Data/speak',
help='音频数据目录,子目录为个分类语音数据.')
parser.add_argument(
'--noise_bg_dir',
type=str,
default='Data/noise',
help='补齐音频,不能在音频目录下.')
parser.add_argument(
'--out_audio_path',
type=str,
default='Data/train_audio',
help='输出音频目录,保存结构与输入音频目录结构相同.')
parser.add_argument(
'--frame_len',
type=int,
default=16000,
help='音频长度.')
parser.add_argument(
'--sample_rate',
type=int,
default=16000,
help='音频采样率.')
FLAGS, unparsed = parser.parse_known_args()
ret = init_noise_bg_file()
if ret == -1:
print("请输入添加噪音目录..")
else:
main()
# process_audio("Data\\speak\\ld", "ld")
python convert_audio.py --audio_path="data/speak" --noise_bg_dir="data/noise" --out_audio_path="data/train_data" --frame_len=16000 --sample_rate=16000
python train.py --data_url="" --data_dir="TrainData" --background_volume=0.2 --background_frequency=0.5 --time_shift_ms=50 --unknown_percentage=50.0 --summaries_dir="Models/retrain_logs" --wanted_words="类别1,类别2" --train_dir="Models/speech_commands_train" --model_architecture ds_cnn --model_size_info 6 276 10 4 2 1 276 3 3 2 2 276 3 3 1 1 276 3 3 1 1 276 3 3 1 1 276 3 3 1 1 --dct_coefficient_count 10 --window_size_ms 40 --window_stride_ms 20 --learning_rate 0.0005,0.0001,0.00002 --how_many_training_steps 10000,10000,10000
python freeze.py --sample_rate=16000 --model_size_info 6 276 10 4 2 1 276 3 3 2 2 276 3 3 1 1 276 3 3 1 1 276 3 3 1 1 276 3 3 1 1 --dct_coefficient_count=10 --window_size_ms=40 --window_stride_ms=20 --clip_duration_ms=1000 --model_architecture=ds_cnn --wanted_words="类别1,类别2" --checkpoint=model/speech_commands_train/best/ds_cnn_9790.ckpt-8533 --output_file=model/pb/my_frozen_graph.pb
python label_wav.py --wav="wav/test.wav" --graph="model/pb/ds_cnn_9585.pb" --labels="model/ds_cnn_labels.txt" --how_many_labels=10
在实际情况中,我们需要对一段连续语音来进行分类,而不是来分类某一个音频,所以我们需要根据一段时间的语音来测试模型的准确率
python generate_streaming_test_wav.py --data_dir=tmp\speech_dataset --background_dir=data/noise --background_volume=0.2 --test_duration_seconds=600 --output_audio_file=test/streaming_test.wav --output_labels_file=test/streaming_test_labels.txt
完成后会生成一个 5分钟的 wav文件和对应的labels.txt , 然后来预测模型识别的准确率。
无法解析的外部符号 "public: virtual __cdecl tensorflow::internal::LogMessage::~LogMessage(void)" (??1LogMessage@internal@tensorflow@@UEAA@XZ),该符号在函数 "public: void __cdecl tensorflow::internal::LogMessage::`vbase destructor'(void)" (??_DLogMessage@internal@tensorflow@@QEAAXXZ)
类似这种。
解决办法
网上说通过将无法解析的外部符号(括号中的内容) 添加到 tf_exported_symbols_msvc.lds 文件中,重新编译即可。但经过几次尝试,编译tensorflow 时候,都会报 “无法解析的外部符号”。
无奈另寻他路,仔细想想, 无法解析外部符号,无非就是没有找到实现定义的文件,所以我们手动添加进工程中,根据报错的信息,来定位实现定义的文件,经过一点点添加,重新编译终于成功了。
最后根据自己实际的情况修改参数即可。