炼丹心得

服务器

  • 连接

    1
    ssh -p 9970 tangsz@10.15.201.88
  • conda配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 添加conda环境变量
    export PATH=/usr/local/miniconda3/bin:$PATH # 仅在当前shell周期起作用
    conda init bash [ksh,zsh...] # 永久设置
    source .bashrc
    # 更改envs,pkgs默认位置
    conda config --add envs_dirs ~/.conda/envs
    conda config --add pkgs_dirs ~/.conda/pkgs
    # 创建虚拟环境
    conda create -n venv_name [pkg_name=pkg_ver]
    conda info -e
    conda activate venv_name
    conda deactivate
  • jupyter

    1
    2
    3
    jupyter notebook --no-browser --port=1111
    ssh -N -f -L localhost:1112:localhost:1111 -p 9970 tangsz@10.15.201.88
    # http://localhost:1112
  • gpu

    1
    2
    3
    4
    5
    # 看gpu使用情况
    watch -n 1 nvidia-smi
    nvidia-smi
    # 查谁的进程
    ps -f -p 26359
  • 文件传输

    1
    2
    3
    # 用git bash
    # 文件夹需要加-r
    scp -P 9970 tangsz@10.15.201.88:/amax/home/tangsz/notebook/xvector.ipynb ~/Desktop/
  • 常用命令

    1
    2
    3
    4
    # 计数
    ls -l | grep "^-" | wc -l
    # 复制前25000条到文件夹
    ls |head -n 25000 |xargs -i cp {} ../lstm/data_phone/

数据处理

最容易忽视和出错的部分…比调参重要的多。

测试集划分

  • 让模型在训练集上进行训练。

  • 在验证集上评估模型,用来调整模型参数。一旦找到最佳的参数,就在测试集上最后测试一次,测试集上的误差作为泛化误差的近似。(可选)

  • 在测试集上来近似模型的泛化能力,测试集不属于验证集和训练集。

划分方法

  • 只有训练集和测试集时,通常将数据集的80%作为训练集,20%作为测试集;
  • 如果当数据量不是很大的时候(几万量级)的时候将训练集、验证集以及测试集划分为6:2:2;
  • 若是数据很大(百万级),可以将训练集、验证集、测试集比例调整为98:1:1;
  • 超参数越少,或者超参数很容易调整,那么可以减少验证集的比例,更多的分配给训练集;
  • 但是当可用的数据很少的情况下也可以使用一些高级的方法,比如留出法,K折交叉验证等。

抽样方法

  • 分层抽样:按照给定的概率来采样样本。

    1
    class torch.utils.data.WeightedRandomSampler(weights, num_samples, replacement=True)
  • 随机抽样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # indices = torch.arange(1000)
    # data = torch.utils.data.Subset(data, indices)
    # train_data, test_data = torch.utils.data.random_split(data, [800, 200])
    # train_data, test_data = torch.utils.data.random_split(data, [22832, 5707])

    # 随机采样分割数据集
    #设置随机数种子,保证每次生成的结果都是一样的
    np.random.seed(42)
    #permutation随机生成0-len(data)随机序列
    shuffled_indices = np.random.permutation(len(data))
    #test_ratio为测试集所占的百分比
    test_set_size = int(len(data) * 0.2)
    test_indices = shuffled_indices[:test_set_size]
    train_indices = shuffled_indices[test_set_size:]
    train_data = torch.utils.data.Subset(data, train_indices)
    test_data = torch.utils.data.Subset(data, test_indices)
    print('train:', len(train_data), 'test:', len(test_data))

特征提取方式

离线处理

  • 训练之前先对语音数据进行加噪音、调低\高音量、1.5倍速播放等,然后再提取MFCC特征到特征文件里,训练时数据集从特征文件读取,不需要再预处理;
  • 优点: 只需提取特征一次就可以多次训练,时间减少很多;
  • 缺点: 每个epoch喂入模型的数据特征都是一样的,变相的降低了数据集可增强的空间,模型泛化能力会降低;

在线处理

  • 对数据进行预处理,然后提取MFCC特征,最后喂入模型中。每一个epoch都会进行预处理,再喂入模型;
  • 优点: 数据集可增强空间变大(相当于有epoch个数据集),模型泛化能力强;
  • 缺点: 训练时间长,显存消耗大。
1
2
3
4
5
6
7
8
9
10
11
train_loader = torch.utils.data.DataLoader(train_data,
batch_size=batch_size_train,
shuffle=True,
drop_last=True,
collate_fn=lambda x: data_preprocessing(x)
)
test_loader = torch.utils.data.DataLoader(test_data,
batch_size=batch_size_test,
shuffle=True,
drop_last=True,
collate_fn=lambda x: data_preprocessing(x)

特征归一化

  • min-max标准化(Min-Max Normalization)

    • 也称为离差标准化,是对原始数据的线性变换,使结果值映射到[0 - 1]之间。转换函数如下:

      x=xminmaxminx^* = \frac{x-min}{max-min}

    • 其中max为样本数据的最大值,min为样本数据的最小值。这种方法有个缺陷就是当有新数据加入时,可能导致max和min的变化,需要重新定义。

  • Z-score标准化

    • 这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化。经过处理的数据符合标准正态分布,即均值为0,标准差为1,转化函数为:

      x=xμσx^*=\frac{x-\mu}{\sigma}

    • 其中μ\mu为所有样本数据的均值,σ\sigma为所有样本数据的标准差。

计算梯度特征

负样例的生成策略

  • 真实采样:用真实采样的非活体超声频段特征替换

  • 交换:如果有两段输入数据,交换彼此的超声频段特征即可构成负样例

  • 偏移:将超声频段的数据在时间维度上偏移一段距离

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     # ----------------------------
    # Shifts the signal to the left or right by some percent. Values at the end
    # are 'wrapped around' to the start of the transformed signal.
    # ----------------------------
    @staticmethod
    def time_shift(aud, shift_limit):
    sig,sr = aud
    _, sig_len = sig.shape
    shift_amt = int(random.random() * shift_limit * sig_len)
    return (sig.roll(shift_amt), sr)

    aud = AudioUtil.open(audio_file)
    shift_aud = AudioUtil.time_shift(aud, self.shift_pct)

调参

  • train loss不断下降,test loss不断下降,说明网络仍在学习;

  • train loss不断下降,test loss趋于不变,说明网络过拟合;

  • train loss趋于不变,test loss不断下降,说明数据集100%有问题;

  • train loss趋于不变,test loss趋于不变,说明学习遇到瓶颈,需要减小学习率或批量数目;

  • train loss不断上升,test loss不断上升,说明网络结构设计不当,训练超参数设置不当,数据集经过清洗等问题。

criterion

nn.CrossEntropyLoss() = nn.Softmax() + torch.log() + nn.NLLLoss() = nn.LogSoftmax() + nn.NLLLoss()

  • 双通道输出 CrossEntropyLoss()
  • 单通道输出 BCEWithLogitsLoss()
  • torch.sigmoid() + F.mse_loss()

多任务损失

  • 简单相加
  • loss = a * loss1 + (1 - a) * loss2
  • loss = loss1 + loss2 / (loss2 / loss1).detach() ,使loss1和loss2尺度一致,跟直接相加取平均值差别不大
  • 后期专注于相关性预测任务的损失

scheduler

  • 等间隔调整 StepLR

    1
    torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
  • 按需调整 MultiStepLR

    1
    torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1)
  • 指数衰减调整 ExponentialLR

    1
    torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1)
  • 余弦退火 CosineAnnealingLR

    1
    torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max, eta_min=0, last_epoch=-1)
  • 自适应调整 ReduceLROnPlateau

    1
    torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10, verbose=False, threshold=0.0001, threshold_mode='rel', cooldown=0, min_lr=0, eps=1e-08)
  • 自定义调整 LambdaLR

    1
    torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1)

模型

  • xvector+简单的线性二分类器
  • xvector+lstm
  • ecapa-tdnn+lstm