Alink教程(Java版)

第27.1节 数据准备


数据来自github,https://github.com/yingdajun/SpeechEmotionAndPeopleAnalyse


数据的目录结构如下,按录音人分为4个文件夹,每个文件夹下按情绪氛围6个子文件夹,生气(angry)、害怕(fear)、高兴(happy)、中性(neutral)、悲伤(sad)和惊讶(surprise)。每个子文件夹下包括50个wav格式的音频文件。



27.1.1 获取音频文件列表

本节将介绍如何从数据根目录下,提取出各音频文件列表,每个文件记录为相对路径。由于下载数据存放在本地电脑上,可以直接使用File的相关操作,具体代码如下:

static String[] listWavFiles_Local(String dirPath) throws Exception {
    List<String> paths = new ArrayList<>();

    subListWavFiles(new File(dirPath), paths);

    List<String> relativePaths = paths.stream()
        .map(x -> x.substring(dirPath.length()))
        .collect(Collectors.toList());

    for (int i = 0; i < 5; i++) {
        System.out.println(relativePaths.get(i));
    }

    return relativePaths.toArray(new String[0]);
}

static private void subListWavFiles(File dir, List<String> relativePaths) throws IOException {
    for (File file : dir.listFiles()) {
        if (file.isDirectory()) {
            subListWavFiles(file, relativePaths);
        } else {
            if (file.getName().toLowerCase().endsWith(".wav")) {
                relativePaths.add(file.getCanonicalPath());
            }
        }
    }
}

执行该函数,获取 DATA_DIR 文件夹下所有音频文件列表

listWavFiles_Local(DATA_DIR);

执行结果如下,打印输出了前5个相对路径。

wangzhe/happy/249.wav
wangzhe/happy/248.wav
wangzhe/happy/216.wav
wangzhe/happy/202.wav
wangzhe/happy/203.wav



考虑到该功能的扩展性,如果文件存储在Hadoop、OSS或S3等文件系统,可以使用Alink FileSystem相应接口实现,将下面代码中变量fs定义为相应文件系统即可。具体代码如下:

static String[] listWavFiles_FileSystem(String dirPath) throws Exception {
    LocalFileSystem fs = new LocalFileSystem();
    FilePath rootFolder = new FilePath(dirPath, fs);
    URI rootUri = rootFolder.getPath().makeQualified(fs).toUri();
    List<String> relativePaths = FileSystemUtils.listFilesRecursive(rootFolder, true)
        .stream()
        .filter(new Predicate<FilePath>() {
            @Override
            public boolean test(FilePath filePath) {
                return filePath.getPathStr().toLowerCase().endsWith(".wav");
            }
        })
        .map(x -> rootUri
            .relativize(x.getPath().makeQualified(fs).toUri())
            .getPath()
        )
        .collect(Collectors.toList());

    for (int i = 0; i < 5; i++) {
        System.out.println(relativePaths.get(i));
    }

    return relativePaths.toArray(new String[0]);
}

执行该函数,获取 DATA_DIR 文件夹下所有音频文件列表

listWavFiles_FileSystem(DATA_DIR);

执行结果如下,打印输出了前5个相对路径,与前面使用File操作得到的结果相同。

wangzhe/happy/249.wav
wangzhe/happy/248.wav
wangzhe/happy/216.wav
wangzhe/happy/202.wav
wangzhe/happy/203.wav



27.1.2 情绪标签和录音人

因为音频文件相对路径的子文件夹名称包含情绪和录音人姓名,我们可以从中提取出情绪标签和录音人,具体步骤和代码如下:

  • 使用MemSourceBatchOp组件,可以基于函数 listWavFiles_Local(DATA_DIR) 得到音频文件列表,构建只有一列(名称为 relative_path)的数据表。
  • 在相对路径中包含情绪和录音人信息,可以使用正则表达式进行提取,分别存为emotion列与speaker列。
  • 将当前结果存到临时文件 temp.ak
new MemSourceBatchOp(listWavFiles_Local(DATA_DIR), "relative_path")
    .select("relative_path, "
        + "REGEXP_EXTRACT(relative_path, '(angry|fear|happy|neutral|sad|surprise)') AS emotion, "
        + "REGEXP_EXTRACT(relative_path, '(liuchanhg|wangzhe|zhaoquanyin|ZhaoZuoxiang)') AS speaker"
    )
    .link(
        new AkSinkBatchOp()
            .setFilePath(DATA_DIR + "temp.ak")
            .setOverwriteSink(true)
    );
BatchOperator.execute();




27.1.3 读取音频文件数据


定义几个常量如下:

    static final String TEST_FILE = "test.ak";
    static final String TRAIN_FILE = "train.ak";
    static final int AUDIO_SAMPLE_RATE = 16000;


使用ReadAudioToTensorBatchOp组件从音频文件中读取数据,该组件读取的音频数据会保存为张量格式。具体代码如下。

BatchOperator<?> data_set = new AkSourceBatchOp()
    .setFilePath(DATA_DIR + "temp.ak")
    .link(
        new ReadAudioToTensorBatchOp()
            .setRelativeFilePathCol("relative_path")
            .setRootFilePath(DATA_DIR)
            .setSampleRate(AUDIO_SAMPLE_RATE)
            .setDuration(3.0)
            .setOutputCol("audio_data")
    );

参数SampleRate指定了读取结果数据中对应的音频采样频率,如果原始文件中的音频频率与SampleRate不同,会进行相应的转换。参数Duration为读取音频的时长(单位:秒),如果音频文件的时长小于Duration,会进行补0操作;如果不指定该参数,将读取全部数据;根据当前录音数据大部分在3秒以内,这里设置参数Duration为3.0秒。

27.1.4 划分训练集和测试集


前面三个小节,我们获得实验所需的全部音频数据及标签。接下来,将此数据集分为训练集和测试集,具体代码如下:

Utils.splitTrainTestIfNotExist(
    data_set, DATA_DIR + TRAIN_FILE, DATA_DIR + TEST_FILE, 0.9
);