【李宏毅】深度学习——HW4-Speaker Identification

Speaker Identification

1.Goal

根据给定的语音内容,识别出说话者是谁

2.Data formats

2.1data directory

目录下有三个json文件和很多pt文件,三个json文件作用标注在下图中,pt文件就是语音内容。
在这里插入图片描述

mapping文件
在这里插入图片描述

metadata文件
n_mels:The demission of mel-spectrogram(特征数是40)
speakers: A dictionary

  • key: speaker id
  • value: feature_path and mel_len
    可以发现,pt文件内容长度不一样,所以后期需要我们自己统一长度
    在这里插入图片描述

testdata文件
在这里插入图片描述

3.DataSet

构建自己的Dataset类,需要知道数据集的地址,由于每个数据长度不一样,所以还要规定数据的长度,返回值应该是,语音数据和对应的speaker的label

Dataset有三个重要的方法需要重写

  • __init__(): 初始化,一般用于读取给定的数据到内存,在该任务中,需要从给定的路径中,读取语音path和label到data数组,读取mapping将speaker的id映射为对应序号。
  • __getitem__(): 该方法根据传入的参数返回指定id的数据,在该任务中,对于传入的id,我们从data数组取出path和label,并将指定path的语音数据读出后分割,最后返回语音片段和对应的label。
  • __len__(): 该方法返回数据集的长度,在该任务中,数据集的长度即data数组的长度。

代码如下:

import os
import json
import torch
import random
from pathlib import Path
from torch.nn.utils.rnn import pad_sequence
from torch.utils.data import Dataset


class myDataset(Dataset):
    def __init__(self, data_dir, segment_len=128):
        self.data_dir = data_dir
        self.segment_len = segment_len

        # Load the mapping from speaker neme to their corresponding id.
        mapping_path = Path(data_dir) / "mapping.json"
        mapping = json.load(mapping_path.open())
        self.speaker2id = mapping["speaker2id"] # 在mapping.json 文件中 字典的key是speaker2id

        # Load metadata of training data.
        metadata_path = Path(data_dir) / "metadata.json"
        metadata = json.load(open(metadata_path))["speakers"]

        # get the total number of speaker
        self.speaker_num = len(metadata.keys())
        self.data = []
        for speaker in metadata.keys():
            for utterance in metadata[speaker]:
                self.data.append([utterance["feature_path"], self.speaker2id
                                  [speaker]])

    def __len__(self):
        return len(self.data)

    def __getitem__(self, index):
        feature_path, speaker = self.data[index]
        # Load preprocessed mel-spectrogram.
        mel = torch.load(os.path.join(self.data_dir, feature_path))

        # segment mel-spectrogram into "segment_len" frames.
        if len(mel) >self.segment_len:
            start = random.randint(0, len(mel)-self.segment_len) #随便选取一个开始截取的位置 这个位置往后的长度要大于segment_len
            mel = torch.FloatTensor(mel[start:start+self.segment_len])
        else:
            mel = torch.FloatTensor(mel)

        # Turn the speaker id into long for computing loss later.
        speaker = torch.FloatTensor([speaker]).long

        return mel, speaker

    def get_speaker_number(self):
        return self.speaker_num

最后的数据中,发言人一共600个,语音一共69438个

4.DataLoader

DataLoader的完整参数列表

class torch.utils.data.DataLoader(
dataset,
batch_size=1,
shuffle=False,
sampler=None,
batch_sampler=None,
num_workers=0,
collate_fn=<function default_collate>,
pin_memory=False,
drop_last=False,
timeout=0,
worker_init_fn=None)

其中有一个参数collate_fn,可能比较陌生
collate_fn作用
在最后一步堆叠的时候可能会出现问题: 如果一条数据中所含有的每个数据元的长度不同, 那么将无法进行堆叠. 如: multi-hot类型的数据, 序列数据。在使用这些数据时, 通常需要先进行长度上的补齐, 再进行堆叠. 以现在的流程, 是没有办法加入该操作的。此外, 某些优化方法是要对一个batch的数据进行操作。

collate_fn函数就是手动将抽取出的样本堆叠起来的函数。

所以我们需要自己定义collate_fn函数来统一特征大小

def collate_batch(batch):
    # Process features within a batch
    mel, speaker = zip(*batch)
    # Because we train the model batch by batch, we need to pad the features in the same batch to make their lengths the same
    mel = pad_sequence(mel, batch_first=True, padding_value=-20) # pad long 10^(-20) ehich is small value.
    # mel: (batch size, length, 40)
    return mel, torch.FloatTensor(speaker).long()

然后定义自己的dataloader,并在DataLoader中划分训练集和验证集

def get_dataloader(data_dir, batch_size, n_workers):
    dataset = myDataset(data_dir)
    speaker_num = dataset.get_speaker_number()
    # split
    trainlen = int(0.9 * len(dataset))
    lengths = [trainlen, len(dataset) - trainlen]
    trainset, validset = random_split(dataset, lengths)
    train_loader = DataLoader(
        trainset,
        batch_size=batch_size,
        shuffle=True,
        num_workers=n_workers,
        drop_last=True,
        pin_memory=True,
        collate_fn=collate_batch,
    )
    valid_loader = DataLoader(
        validset,
        batch_size=batch_size,
        num_workers=n_workers,
        drop_last=True,
        pin_memory=True,
        collate_fn=collate_batch,
    )
    return train_loader, valid_loader

5.Define Model

分类器是由transformerEncoder和全连接层构成的,输入是mels(shape为[batch size, length, 40]),输出是out(shape为[batch size, length, d_model])

# -*- coding = utf-8 -*-
# @Time : 2023/4/7 14:44
# @Author : 头发没了还会再长
# @File : mdoel.py
# @Software : PyCharm
import torch
import torch.nn as nn
import torch.nn.functional as F
# 分类器 使用transformer
class Classifier(nn.Module):
    def __init__(self, d_model=80, n_spks=600, dropout=0.1):
        super().__init__()
        # project the dimession of features from that of input into d_model
        self.prenet = nn.Linear(40, d_model)
        self.encoder_layer = nn.TransformerEncoderLayer(
            d_model=d_model, dim_feadforeard=256, nhead=2
        )
        self.pred_layer = nn.Sequential(
            nn.Linear(d_model, d_model),
            nn.ReLU(),
            nn.Linear(d_model, n_spks),
        )

    def forward(self, mels):
        '''
        :param mels: (batch size, length, 40)
        :return: (batch size, length, d_model)
        '''
        out = self.prenet(mels)
        out = out.permute(1, 0, 2)
        out = self.encoder_layer(out)
        out = out.transpose(0, 1)
        stats = out.mean(dim=1)
        out = self.pred_layer(stats)
        return out

6.Train and Valid

6.1 learning rate schedule

The warmup schedule

  • Set learning rate to 0 in the beginning
  • The learning rate increases linearly from 0 to initial learning rate during warmup period
import math

import torch
from torch.optim import Optimizer
from torch.optim.lr_scheduler import LambdaLR


def get_cosine_schedule_with_warmup(
  optimizer: Optimizer,
  num_warmup_steps: int,
  num_training_steps: int,
  num_cycles: float = 0.5,
  last_epoch: int = -1,
):
  """
  Create a schedule with a learning rate that decreases following the values of the cosine function between the
  initial lr set in the optimizer to 0, after a warmup period during which it increases linearly between 0 and the
  initial lr set in the optimizer.

  Args:
    optimizer (:class:`~torch.optim.Optimizer`):
      The optimizer for which to schedule the learning rate.
    num_warmup_steps (:obj:`int`):
      The number of steps for the warmup phase.
    num_training_steps (:obj:`int`):
      The total number of training steps.
    num_cycles (:obj:`float`, `optional`, defaults to 0.5):
      The number of waves in the cosine schedule (the defaults is to just decrease from the max value to 0
      following a half-cosine).
    last_epoch (:obj:`int`, `optional`, defaults to -1):
      The index of the last epoch when resuming training.

  Return:
    :obj:`torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
  """

  def lr_lambda(current_step):
    # Warmup
    if current_step < num_warmup_steps:
      return float(current_step) / float(max(1, num_warmup_steps))
    # decadence
    progress = float(current_step - num_warmup_steps) / float(
      max(1, num_training_steps - num_warmup_steps)
    )
    return max(
      0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress))
    )

  return LambdaLR(optimizer, lr_lambda, last_epoch)

6.2定义model_fn

输入一组batch,输出损失和准确率

def model_fn(batch, model, criterion, device):
    mels, labels = batch
    mels = mels.to(device)
    labels = labels.to(device)

    outs = model(mels)
    loss = criterion(outs, labels)

    preds = outs.argmax(1)
    accuracy = torch.mean((preds == labels).float())
    return loss, accuracy

6.3 validation

def valid(dataloader, model, criterion, device): 
  """Validate on validation set."""

  model.eval()
  running_loss = 0.0
  running_accuracy = 0.0
  pbar = tqdm(total=len(dataloader.dataset), ncols=0, desc="Valid", unit=" uttr")

  for i, batch in enumerate(dataloader):
    with torch.no_grad():
      loss, accuracy = model_fn(batch, model, criterion, device)
      running_loss += loss.item()
      running_accuracy += accuracy.item()

    pbar.update(dataloader.batch_size)
    pbar.set_postfix(
      loss=f"{running_loss / (i+1):.2f}",
      accuracy=f"{running_accuracy / (i+1):.2f}",
    )

  pbar.close()
  model.train()

  return running_accuracy / len(dataloader)

6.4 有了前期的准备,就可以开始训练模型了

  • 首先,需要准备训练数据,即加载dataloader,定义损失函数,优化器等
  • 然后开始循环,在训练集上计算梯度反向传播,在验证集上计算准确率
  • 存储最优的模型
def parse_args():
  """arguments"""
  config = {
    "data_dir": "./Dataset",
    "save_path": "model.ckpt",
    "batch_size": 32,
    "n_workers": 8,
    "valid_steps": 2000,
    "warmup_steps": 1000,
    "save_steps": 10000,
    "total_steps": 70000,
  }

  return config


def main(
  data_dir,
  save_path,
  batch_size,
  n_workers,
  valid_steps,
  warmup_steps,
  total_steps,
  save_steps,
):
  """Main function."""
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  print(f"[Info]: Use {device} now!")

  train_loader, valid_loader, speaker_num = get_dataloader(data_dir, batch_size, n_workers)
  train_iterator = iter(train_loader)
  print(f"[Info]: Finish loading data!",flush = True)

  model = Classifier(n_spks=speaker_num).to(device)
  criterion = nn.CrossEntropyLoss()
  optimizer = AdamW(model.parameters(), lr=1e-3)
  scheduler = get_cosine_schedule_with_warmup(optimizer, warmup_steps, total_steps)
  print(f"[Info]: Finish creating model!",flush = True)

  best_accuracy = -1.0
  best_state_dict = None

  pbar = tqdm(total=valid_steps, ncols=0, desc="Train", unit=" step")

  for step in range(total_steps):
    # Get data
    try:
      batch = next(train_iterator)
    except StopIteration:
      train_iterator = iter(train_loader)
      batch = next(train_iterator)

    loss, accuracy = model_fn(batch, model, criterion, device)
    batch_loss = loss.item()
    batch_accuracy = accuracy.item()

    # Updata model
    loss.backward()
    optimizer.step()
    scheduler.step()
    optimizer.zero_grad()
    
    # Log
    pbar.update()
    pbar.set_postfix(
      loss=f"{batch_loss:.2f}",
      accuracy=f"{batch_accuracy:.2f}",
      step=step + 1,
    )

    # Do validation
    if (step + 1) % valid_steps == 0:
      pbar.close()

      valid_accuracy = valid(valid_loader, model, criterion, device)

      # keep the best model
      if valid_accuracy > best_accuracy:
        best_accuracy = valid_accuracy
        best_state_dict = model.state_dict()

      pbar = tqdm(total=valid_steps, ncols=0, desc="Train", unit=" step")

    # Save the best model so far.
    if (step + 1) % save_steps == 0 and best_state_dict is not None:
      torch.save(best_state_dict, save_path)
      pbar.write(f"Step {step + 1}, best model saved. (accuracy={best_accuracy:.4f})")

  pbar.close()


if __name__ == "__main__":
  main(**parse_args())

这是训练过程中的截图:
在这里插入图片描述

7.Test

训练完模型,我们会得到最好的参数,并且保存在model.ckpt文件中了,接下来,只需要定义训练的函数,使用保存的最优模型开始训练即可。
测试和训练基本相同,需要准备dataset,dataloader

7.1 Inference dataset

class InferenceDataset(Dataset):
    def __init__(self, data_dir):
        testdata_path = Path(data_dir)
        metadata = json.load(testdata_path.open())
        self.data_dir = data_dir
        self.data = metadata["utterances"]

    def __len__(self):
        return len(self.data)

    def __getitem__(self, index):
        utterance = self.data[index]
        feature_path = utterance["feature_path"]
        mel = torch.load(os.path.join(self.data_dir, feature_path))

        return feature_path, mel

7.2可以开始预测啦

def inference_collate_batch(batch):
    feature_paths, mels = zip(*batch)
    return feature_paths, torch.stack(mels)

def parse_args():
  """arguments"""
  config = {
    "data_dir": "dataset",
    "model_path": "./model.ckpt",
    "output_path": "./output.csv",
  }

  return config

def main(data_dir,model_path,output_path,):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"[Info]: Use {device} now!")

    mapping_path = Path(data_dir) / "mapping.json"
    mapping = json.load(mapping_path.open())

    dataset = InferenceDataset(data_dir)
    dataloader = DataLoader(
        dataset,
        batch_size=1,
        shuffle=False,
        drop_last=False,
        num_workers=0,
        collate_fn=inference_collate_batch,
    )
    print(f"[Info]: Finish loading data!", flush=True)

    speaker_num = len(mapping["id2speaker"])
    model = Classifier(n_spks=speaker_num).to(device)
    model.load_state_dict(torch.load(model_path))
    model.eval()
    print(f"[Info]: Finish creating model!", flush=True)
    results = [["Id", "Category"]]
    for feat_paths, mels in tqdm(dataloader):
        with torch.no_grad():
            mels = mels.to(device)
            outs = model(mels)
            preds = outs.argmax(1).cpu().numpy()
            for feat_path, pred in zip(feat_paths, preds):
                results.append([feat_path, mapping["id2speaker"][str(pred)]])

    with open(output_path, 'w', newline='') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerows(results)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/7957.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

优思学院|《精益思想》让企业快速应对市场变化的利器

1985年&#xff0c;美国麻省理工学院的丹尼尔T琼斯&#xff08;Daniel T. Jones&#xff09;、詹姆斯沃默克&#xff08;James P. Womack&#xff09;等筹资500万美元&#xff0c;用了近5年的时间对90多家汽车厂进行对比分析&#xff0c;于1990年出版了《改变世界的机器》一书&…

贵金属技术分析的止损保护

前面说过我们这些小散户&#xff0c;最多也不过十几万或者几万美金的账户&#xff0c;没有必要想国际的一些大基金那样&#xff0c;又锁仓&#xff0c;又对冲什么的&#xff0c;我们资金小的投资者&#xff0c;足够灵活&#xff0c;自然有我们存活的方法。所以我们要注意发挥我…

算法题回顾:双指针链表系列集锦

1&#xff0c;合并两个有序链表 思路 创建一个指向空的新链表&#xff0c;用来存储合并后的链表&#xff0c;p指针指向该链表。创建双指针&#xff0c;分辨指向两个链表&#xff0c;用p1, p2表示while循环&#xff0c;依次判断两个指针指向数据的大小&#xff0c;将最小值赋值…

从零开始实现一个C++高性能服务器框架----日志模块

此项目是根据sylar框架实现&#xff0c;是从零开始重写sylar&#xff0c;也是对sylar丰富与完善 项目地址&#xff1a;https://gitee.com/lzhiqiang1999/server-framework 简介 项目介绍&#xff1a;实现了一个基于协程的服务器框架&#xff0c;支持多线程、多协程协同调度&am…

Vue3走马灯(Carousel)

Vue2走马灯&#xff08;Carousel&#xff09; 可自定义设置以下属性&#xff1a; 走马灯图片数组&#xff08;imageData&#xff09;&#xff0c;类型&#xff1a;Array<{title: string, link?: string, imgUrl: string}>&#xff0c;默认 [] 自动滑动轮播间隔&#…

3-ELK+Kafka+Filebeat 海量级日志收集 TB PB级别

ELKKafkaFilebeat 终极版 4、Kafka&#xff1a; 数据缓冲队列(消息队列)。同时提高了可扩展性。具有峰值处理能力&#xff0c;使用消息队列能够使关键组件顶住突发的访问压力&#xff0c;而不会因为突发的超负荷的请求而完全崩溃。是一个分布式、支持分区的&#xff08;partit…

模板匹配及应用

模板匹配及应用 1)模板匹配 模板匹配是一项在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术。模板匹配不是基于直方图的, 而是通过在 输入图像上滑动图像块(模板)同时比对相似度, 来对模板和输入图像进行匹配的一种方法。 应用: ①目标查找定位 ②运动物体跟踪 ③…

SpringMvc中拦截器

文章目录 1.拦截器概述 2.拦截器类中的方法 1.先写一个前端页面 2.写后台代码 3.编写success.jsp页面 4.编写拦截器类&#xff0c;实现HandlerInterceptor接口 5.编写error.jsp页面 6.配置拦截器类 3.配置多个拦截器 3.1再写一个拦截器类 3.2 配置拦截器类 1.拦截器概述 Spring…

中国版ChatGPT即将来袭-国内版ChatGPT入口

必应chatGPT入口 目前并不存在“必应ChatGPT”这个概念。必应&#xff08;Bing&#xff09;是Microsoft公司推出的一款搜索引擎&#xff0c;而ChatGPT是OpenAI开发的自然语言处理技术&#xff0c;它们是两个不同的产品品牌。 不过&#xff0c;Microsoft也在自然语言处理领域里…

Leetcode字符串的排列

其实可以看成使用其中一个字符加上其他字符的连接&#xff0c;最后用set去重 class Solution:lru_cache(None)def permutation(self, s: str) -> List[str]:if not s: return []res set()for i in range(len(s)):for j in self.permutation(s[:i]s[i1:]):res.add(s[i]j)re…

Unity Animation -- 改进动画效果

使用曲线&#xff08;Curves&#xff09;改善动画 在上一篇笔记中&#xff08;Unity Animation -- Overview_亦枫Leonlew的博客-CSDN博客&#xff09;&#xff0c;我们制作了简单的小球弹跳的动画&#xff0c;但这个动画看起来很不自然&#xff0c;小球的弹跳看起来就像是不受…

Leetcode.559 N 叉树的最大深度

题目链接 Leetcode.559 N 叉树的最大深度 easy 题目描述 给定一个 N 叉树&#xff0c;找到其最大深度。 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。 N 叉树输入按层序遍历序列化表示&#xff0c;每组子节点由空值分隔&#xff08;请参见示例&#xff09;。…

Vector - CAPL - CRC算法介绍(续)

不常用CRC算法 目录 Crc_CalculateCRC8H2F 代码示例 Crc_CalculateCRC32P4 代码示例 Crc_CalculateCRC64 代码示例 Crc_CalculateCRC8H2F 功能&#xff1a;根据数据计算CRC8H2F的相应校验和。 data&#xff1a;待计算CRC8H2F校验和的数据 dataSize&#xff1a;待计算CRC…

Ansys Zemax | 如何使用 Zernike 凹陷表面对全反射系统进行建模

本文介绍如何使用Zernike标准下垂表面对全反射系统进行建模。全反射系统是一种特殊情况&#xff0c;其中Zernike凹陷表面可用于模拟给定场点的所有波长下的性能。使用Zernike凹陷表面代替Zernike相位&#xff0c;因为衍射功率与波长变化时的反射功率不同。一个相位波是任何波长…

linux 共享内存 shmget

专栏内容&#xff1a;linux下并发编程个人主页&#xff1a;我的主页座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物&#xff0e;目录 前言 概述 原理机制 系统命令 接口说明 代码演示 结尾 前言 本专栏主要分享linu…

Day924.自动化测试 -系统重构实战

自动化测试 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于自动化测试的内容。 自动化测试是一个很容易产生“争议”的话题&#xff0c;也经常会有一些很有意思的问题。 自动化测试不是应该由测试同学来编写吗&#xff0c;开发是不是没有必要学吧&#xff1f;之前…

【Linux】进程理解与学习-程序替换

环境&#xff1a;centos7.6&#xff0c;腾讯云服务器Linux文章都放在了专栏&#xff1a;【Linux】欢迎支持订阅 相关文章推荐&#xff1a; 【Linux】冯.诺依曼体系结构与操作系统 【Linux】进程理解与学习Ⅰ-进程概念 【Linux】进程理解与学习Ⅱ-进程状态 【Linux】进程理解与学…

小白的git入门教程(二)

泥闷嚎 今天接着来学习小白入门git的基本过程 今天要学习的是git里面的常见操作 状态查看 git status 所谓的状态查看就是你可以查看到工作区和暂存区的状态&#xff0c;在这里你可以看到你的工作文件的状态&#xff0c;比如是否已经提交等等 首先我们创建一个文本文件&…

FreeRTOS学习(一)

裸机与RTOS对比 裸机&#xff1a;又称为前后台系统&#xff0c;前台系统指的是中断服务函数&#xff0c;后台系统指的大循环&#xff0c;即应用程序。 实时性差&#xff1a;&#xff08;应用程序轮流执行&#xff09;delay&#xff1a;空等待&#xff0c;CPU不执行其它代码结…

【分享】太阳能电池性能测试指标,太阳能电池IV测试软件系统

在现代社会&#xff0c;随着能源需求的不断增加&#xff0c;太阳能电池的应用越来越广泛。太阳能电池是一种利用太阳光能量将化学能转换为电能的半导体材料&#xff0c;它可以将太阳光中的光能直接转换成电能&#xff0c;因此具有广泛的应用前景。本篇文章纳米软件小编为大家分…
最新文章