VS2017 使用 tensorflow examples 中 speech_commands,训练自己数据集

使用 TensorFlow 官方提供的 speech_commands 语音分类模块,训练自己数据集。

详细文档请参考:https://blog.csdn.net/rainhurt/article/details/89097842 ,详解的非常清晰。

编译

  1. 准备数据集,由于训练样本数据我们设定的是 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
  1. 训练模型参考文章
    模型训练参考了这篇文章,训练参数按照自己的实际情况来调整。
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
  1. 训练完成后会生成 ckpt 文件,然后转换成 pb 文件
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
  1. 测试模型
  • 参数根据自己实际情况需改
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

VS2017 使用 tensorflow examples 中 speech_commands,训练自己数据集_第1张图片

流式处理

在实际情况中,我们需要对一段连续语音来进行分类,而不是来分类某一个音频,所以我们需要根据一段时间的语音来测试模型的准确率

  1. 准备测试文件
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 , 然后来预测模型识别的准确率。

  1. 由于我实际项目是基于windows的,所以需要编译一个 c++ tensorflow 来测试模型的准确率。 参考文章
  2. 编译成功后,新建一个工程,将 tensorflow 的 lib 和 include 包含进项目中,然后添加 test_streaming_accuracy.cc 文件 ,生成项目。
  3. 生成后,显示各种无法解析的外部符号
无法解析的外部符号 "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)

类似这种。

  1. 解决办法

    1. 网上说通过将无法解析的外部符号(括号中的内容) 添加到 tf_exported_symbols_msvc.lds 文件中,重新编译即可。但经过几次尝试,编译tensorflow 时候,都会报 “无法解析的外部符号”。

    2. 无奈另寻他路,仔细想想, 无法解析外部符号,无非就是没有找到实现定义的文件,所以我们手动添加进工程中,根据报错的信息,来定位实现定义的文件,经过一点点添加,重新编译终于成功了。

    VS2017 使用 tensorflow examples 中 speech_commands,训练自己数据集_第2张图片
    这是我全部添加进来的文件,可以参考下。

  2. 最后根据自己实际的情况修改参数即可。

END

祝大家编译愉快!

VS2017 使用 tensorflow examples 中 speech_commands,训练自己数据集_第3张图片

你可能感兴趣的:(AI)