携手创造,共同成长!这是我参与「日新计划 8 月更文挑战」的第33天,点击检查活动详情
一、根据PaddleSpeech的低复杂度家庭环境音辨认
地址: challenge.xfyun.cn/topic/info?…
项目地址:aistudio.baidu.com/aistudio/pr…
1.赛事背景
声响作为一种重要的信息载体,由于其易搜集、不受角度和光线的约束等长处,常被用于辅助环境感知和信息决议计划,故语音操控遍及使用于智能家居体系。智能设备接纳并处理环境中的声响信号,通过声响事情辨认技能能够侦测判别出环境中的物体与产生的事情,例如婴儿哭泣声、枪声和敲门声等,并能迅速地感知到环境中的变化,例如脚步声由远及近等,体系据此启动相关的智能设备。因此,声响事情辨认技能已被用于安防监控、音频内容检索等智能感知等领域中,为新式的人机交互方式和智能机器听觉体系供给了协助。
但针对使用侧存在两大首要挑战:1. 数据层面:因环境复杂,含有较多杂音;2. 设备层面:智能家居硬件设备计算力及存储有限。
2.赛事任务
声响辨认事情需强壮的数据作为支撑,本次大赛供给了品冠科技云平台音频数据作为练习样本,包括6类音频数据:看电视的声响、燃气报警的声响、炒菜的声响、流水的声响、拉窗布的声响和小孩哭泣的声响,它们的标签分别为1、2、3、4、5、6。音频文件名含有声响类型,参赛者能够据此对文件进行分类。出于数据安全保证的考虑,一切数据均为脱敏处理后的数据。参赛选手需根据供给的样本构建低复杂度量化模型,通过输入音频数据猜测声响对应的事情(猜测声响的类型)。
本次竞赛有模型复杂度约束,模型复杂度以参数量作为度量。参赛选手提交的模型参数量需小于1M,模型参数为量化后INT8方式。模型参数量统计方法一致如下:
github.com/AlbertoAnci… ;量化进程可采用恣意量化方法。
二、数据集处理
1.数据集格式处理
!wget https://ai-contest-static.xfyun.cn/2022/%E6%95%B0%E6%8D%AE%E9%9B%86/%E4%BD%8E%E5%A4%8D%E6%9D%82%E5%BA%A6%E5%AE%B6%E5%BA%AD%E7%8E%AF%E5%A2%83%E9%9F%B3%E6%8C%91%E6%88%98%E8%B5%9B%E5%85%AC%E5%BC%80%E6%95%B0%E6%8D%AE.zip -O dataset.zip
--2022-08-29 09:31:26-- https://ai-contest-static.xfyun.cn/2022/%E6%95%B0%E6%8D%AE%E9%9B%86/%E4%BD%8E%E5%A4%8D%E6%9D%82%E5%BA%A6%E5%AE%B6%E5%BA%AD%E7%8E%AF%E5%A2%83%E9%9F%B3%E6%8C%91%E6%88%98%E8%B5%9B%E5%85%AC%E5%BC%80%E6%95%B0%E6%8D%AE.zip
正在解析主机 ai-contest-static.xfyun.cn (ai-contest-static.xfyun.cn)... 220.181.53.219
正在衔接 ai-contest-static.xfyun.cn (ai-contest-static.xfyun.cn)|220.181.53.219|:443... 已衔接。
已发出 HTTP 恳求,正在等候回应... 200 OK
长度: 2361442488 (2.2G) [application/zip]
正在保存至: “dataset.zip”
dataset.zip 100%[===================>] 2.20G 7.34MB/s in 4m 54s
2022-08-29 09:36:21 (7.65 MB/s) - 已保存 “dataset.zip” [2361442488/2361442488])
!unzip -qoa -O GBK dataset.zip
!mv 低复杂度家庭环境音挑战赛揭露数据 dataset
2.PaddleSpeech安装
!python -m pip install -U -q pip --user
!pip install -q pytest-runner
!pip install -q paddlespeech
[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
parl 1.4.1 requires pyzmq==18.1.1, but you have pyzmq 23.2.0 which is incompatible.[0m[31m
[0m
3.检查声响文件
import warnings
warnings.filterwarnings("ignore")
import IPython
import numpy as np
import matplotlib.pyplot as plt
import paddle
%matplotlib inline
from paddlespeech.audio import load
data, sr = load(file='dataset/train/1_看电视/001.wav', mono=True, dtype='float32') # 单通道,float32音频样本点
print('wav shape: {}'.format(data.shape))
print('sample rate: {}'.format(sr))
# 展现音频波形
plt.figure()
plt.plot(data)
plt.show()
wav shape: (1920000,)
sample rate: 16000
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/cbook/__init__.py:2349: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
if isinstance(obj, collections.Iterator):
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/cbook/__init__.py:2366: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
return list(data) if isinstance(data, collections.MappingView) else data
4.音频文件长度处理
# 查音频长度
import contextlib
import wave
def get_sound_len(file_path):
with contextlib.closing(wave.open(file_path, 'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
wav_length = frames / float(rate)
return wav_length
# 编译wav文件
import glob
sound_files=glob.glob('dataset/train/*/*.wav')
print(sound_files[0])
print(len(sound_files))
# 统计最长、最短音频
sounds_len=[]
for sound in sound_files:
sounds_len.append(get_sound_len(sound))
print("音频最大长度:",max(sounds_len),"秒")
print("音频最小长度:",min(sounds_len),"秒")
dataset/train/3_炒菜/091.wav
616
音频最大长度: 120.0 秒
音频最小长度: 120.0 秒
最长的声响为120秒,现一致尺寸到该长度
!pip install pydub -q
# 音频信息检查
import math
import soundfile as sf
import numpy as np
import librosa
data, samplerate = sf.read('dataset/train/1_看电视/001.wav')
channels = len(data.shape)
length_s = len(data)/float(samplerate)
format_rate=16000
print(f"channels: {channels}")
print(f"length_s: {length_s}")
print(f"samplerate: {samplerate}")
channels: 2
length_s: 120.0
samplerate: 16000
label_list = ['1_看电视', '2_燃气报警', '3_炒菜', '4_流水', '5_拉窗布', '6_小孩哭泣']
# 界说函数,如未达到最大长度,则重复填充,终究从超越34s的音频中截取
from pydub import AudioSegment
def convert_sound_len(filename):
audio = AudioSegment.from_wav(filename)
i = 1
padded = audio*i
while padded.duration_seconds * 1000 < 120000:
i = i + 1
padded = audio * i
padded[0:120000].set_frame_rate(16000).export(filename, format='wav')
# 一致一切音频到定长
for sound in sound_files:
convert_sound_len(sound)
5.生成文件列表
按 9:1 生成train和val文件列表
import os
import random
def get_data_list(target_path,train_list_path,eval_list_path):
'''
生成数据列表
'''
# 获取一切类别保存的文件夹名称
data_list_path=target_path
class_dirs = os.listdir(data_list_path)
if '__MACOSX' in class_dirs:
class_dirs.remove('__MACOSX')
# 存储要写进eval.txt和train.txt中的内容
trainer_list=[]
eval_list=[]
#读取每个类别
##########################
random.shuffle(class_dirs)
##########################
for class_dir in class_dirs:
class_label=label_list.index(class_dir)
i = 0
if class_dir != ".DS_Store":
path = os.path.join(data_list_path,class_dir)
# 获取一切图片
img_paths = os.listdir(path)
for img_path in img_paths: # 遍历文件夹下的每个图片
if img_path =='.DS_Store':
continue
i += 1
name_path = os.path.join(path,img_path) # 每张图片的路径
if i % 10 == 0:
eval_list.append(name_path + ",%d" % class_label + "\n")
else:
trainer_list.append(name_path + ",%d" % class_label + "\n")
class_label += 1
with open(eval_list_path, 'a') as f:
for eval_image in eval_list:
f.write(eval_image)
#乱序
random.shuffle(trainer_list)
with open(train_list_path, 'a') as f2:
for train_image in trainer_list:
f2.write(train_image)
print ('生成数据列表完结!')
target_path="dataset/train"
train_list_path='train_list.csv'
eval_list_path='eval_list.csv'
#每次生成数据列表前,首先清空train_list.csv和eval_list.csv
with open(train_list_path, 'w') as f:
f.seek(0)
f.truncate()
with open(eval_list_path, 'w') as f:
f.seek(0)
f.truncate()
#生成数据列表
get_data_list(target_path,train_list_path,eval_list_path)
生成数据列表完结!
6.自界说数据集
import os
from paddlespeech.audio.datasets.dataset import AudioClassificationDataset
class CustomDataset(AudioClassificationDataset):
# 初始化
def __init__(self, mode, **kwargs):
files, labels = self._get_data(mode)
super(CustomDataset, self).__init__(
files=files, labels=labels, feat_type='raw', **kwargs)
# 回来音频文件、label值
def _get_data(self, mode):
files = []
labels = []
file_list=f"{mode}_list.csv"
with open(file_list,'r') as f:
lines=f.readlines()
for line in lines:
files.append(line.split(',')[0])
labels.append(line.split(',')[-1])
return files, labels
# 界说dataloader
import paddle
from paddlespeech.audio.features import LogMelSpectrogram
# Feature config should be align with pretrained model
sample_rate = 16000
feat_conf = {
'sr': sample_rate,
'n_fft': 1024,
'hop_length': 320,
'window': 'hann',
'win_length': 1024,
'f_min': 50.0,
'f_max': 14000.0,
'n_mels': 64,
}
feature_extractor = LogMelSpectrogram(**feat_conf)
batch_size=16
train_ds = CustomDataset(mode="train", sample_rate=sample_rate)
train_loader = paddle.io.DataLoader(
train_ds,
batch_size=batch_size,
shuffle=True)
eval_ds = CustomDataset(mode="eval", sample_rate=sample_rate)
dev_loader = paddle.io.DataLoader(
eval_ds,
batch_size=batch_size)
W0830 11:09:22.568195 6840 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0830 11:09:22.571982 6840 gpu_resources.cc:91] device: 0, cuDNN Version: 7.6.
三、模型练习
1.选取预练习模型
选取cnn14作为 backbone,用于提取音频的特征:
from paddlespeech.cls.models import cnn14
backbone = cnn14(pretrained=True, extract_embedding=True)
[2022-08-30 11:09:23,739] [ INFO] - PaddleAudio | unique_endpoints {''}
[2022-08-30 11:09:23,742] [ INFO] - PaddleAudio | Found /home/aistudio/.paddlespeech/models/panns/panns_cnn14.pdparams
2.构建分类模型
SoundClassifer接纳cnn14作为backbone模型,并创建下流的分类网络:
import paddle.nn as nn
class SoundClassifier(nn.Layer):
def __init__(self, backbone, num_class, dropout=0.1):
super().__init__()
self.backbone = backbone
self.dropout = nn.Dropout(dropout)
self.fc = nn.Linear(self.backbone.emb_size, num_class)
def forward(self, x):
x = x.unsqueeze(1)
x = self.backbone(x)
x = self.dropout(x)
logits = self.fc(x)
return logits
model = SoundClassifier(backbone, num_class=6)
3.finetune
# 界说优化器和 Loss
optimizer = paddle.optimizer.Adam(learning_rate=1e-4, parameters=model.parameters())
criterion = paddle.nn.loss.CrossEntropyLoss()
from paddlespeech.audio.utils import logger
epochs = 20
steps_per_epoch = len(train_loader)
log_freq = 10
eval_freq = 10
for epoch in range(1, epochs + 1):
model.train()
avg_loss = 0
num_corrects = 0
num_samples = 0
for batch_idx, batch in enumerate(train_loader):
waveforms, labels = batch
feats = feature_extractor(waveforms)
feats = paddle.transpose(feats, [0, 2, 1]) # [B, N, T] -> [B, T, N]
logits = model(feats)
loss = criterion(logits, labels)
loss.backward()
optimizer.step()
if isinstance(optimizer._learning_rate,
paddle.optimizer.lr.LRScheduler):
optimizer._learning_rate.step()
optimizer.clear_grad()
# Calculate loss
avg_loss += loss.numpy()[0]
# Calculate metrics
preds = paddle.argmax(logits, axis=1)
num_corrects += (preds == labels).numpy().sum()
num_samples += feats.shape[0]
if (batch_idx + 1) % log_freq == 0:
lr = optimizer.get_lr()
avg_loss /= log_freq
avg_acc = num_corrects / num_samples
print_msg = 'Epoch={}/{}, Step={}/{}'.format(
epoch, epochs, batch_idx + 1, steps_per_epoch)
print_msg += ' loss={:.4f}'.format(avg_loss)
print_msg += ' acc={:.4f}'.format(avg_acc)
print_msg += ' lr={:.6f}'.format(lr)
logger.train(print_msg)
avg_loss = 0
num_corrects = 0
num_samples = 0
if epoch % eval_freq == 0 and batch_idx + 1 == steps_per_epoch:
model.eval()
num_corrects = 0
num_samples = 0
with logger.processing('Evaluation on validation dataset'):
for batch_idx, batch in enumerate(dev_loader):
waveforms, labels = batch
feats = feature_extractor(waveforms)
feats = paddle.transpose(feats, [0, 2, 1])
logits = model(feats)
preds = paddle.argmax(logits, axis=1)
num_corrects += (preds == labels).numpy().sum()
num_samples += feats.shape[0]
print_msg = '[Evaluation result]'
print_msg += ' dev_acc={:.4f}'.format(num_corrects / num_samples)
logger.eval(print_msg)
[2022-08-30 11:22:36,427] [ TRAIN] - PaddleAudio | Epoch=17/20, Step=30/35 loss=0.0292 acc=0.9938 lr=0.000100
[2022-08-30 11:22:56,232] [ TRAIN] - PaddleAudio | Epoch=18/20, Step=10/35 loss=0.1053 acc=0.9625 lr=0.000100
[2022-08-30 11:23:09,429] [ TRAIN] - PaddleAudio | Epoch=18/20, Step=20/35 loss=0.0349 acc=1.0000 lr=0.000100
[2022-08-30 11:23:22,678] [ TRAIN] - PaddleAudio | Epoch=18/20, Step=30/35 loss=0.0217 acc=1.0000 lr=0.000100
[2022-08-30 11:23:42,471] [ TRAIN] - PaddleAudio | Epoch=19/20, Step=10/35 loss=0.0464 acc=0.9875 lr=0.000100
[2022-08-30 11:23:55,696] [ TRAIN] - PaddleAudio | Epoch=19/20, Step=20/35 loss=0.0748 acc=0.9750 lr=0.000100
[2022-08-30 11:24:08,908] [ TRAIN] - PaddleAudio | Epoch=19/20, Step=30/35 loss=0.0855 acc=0.9750 lr=0.000100
[2022-08-30 11:24:28,751] [ TRAIN] - PaddleAudio | Epoch=20/20, Step=10/35 loss=0.0456 acc=0.9875 lr=0.000100
[2022-08-30 11:24:41,975] [ TRAIN] - PaddleAudio | Epoch=20/20, Step=20/35 loss=0.0383 acc=0.9875 lr=0.000100
[2022-08-30 11:24:55,153] [ TRAIN] - PaddleAudio | Epoch=20/20, Step=30/35 loss=0.0494 acc=1.0000 lr=0.000100
[2022-08-30 11:25:03,517] Evaluation on validation dataset: - - PaddleAudio | Evaluation on validation dataset: \ - PaddleAudio | [Evaluation result] dev_acc=0.8983
四、模型猜测
import glob
test_files=glob.glob("dataset/test/*.wav")
print(len(test_files))
199
top_k = 3
n_fft = 1024
win_length = 1024
hop_length = 320
f_min=50.0
f_max=16000.0
wav_file = 'dataset/test/001.wav'
waveform, sr = load(wav_file, sr=sr)
feature_extractor = LogMelSpectrogram(
sr=sr,
n_fft=n_fft,
hop_length=hop_length,
win_length=win_length,
window='hann',
f_min=f_min,
f_max=f_max,
n_mels=64)
feats = feature_extractor(paddle.to_tensor(paddle.to_tensor(waveform).unsqueeze(0)))
feats = paddle.transpose(feats, [0, 2, 1]) # [B, N, T] -> [B, T, N]
logits = model(feats)
probs = nn.functional.softmax(logits, axis=1).numpy()
sorted_indices = probs[0].argsort()
print(sorted_indices)
[4 3 1 2 0 5]
6_小孩哭泣: 0.92871
1_看电视: 0.04996
3_炒菜: 0.02015
from paddlespeech.audio import load
f=open("result.csv",'w')
f.write('id,label\n')
for wav_file in test_files:
waveform, sr = load(wav_file)
feature_extractor = LogMelSpectrogram(
sr=sr,
n_fft=n_fft,
hop_length=hop_length,
win_length=win_length,
window='hann',
f_min=f_min,
f_max=f_max,
n_mels=64)
feats = feature_extractor(paddle.to_tensor(paddle.to_tensor(waveform).unsqueeze(0)))
feats = paddle.transpose(feats, [0, 2, 1]) # [B, N, T] -> [B, T, N]
logits = model(feats)
probs = nn.functional.softmax(logits, axis=1).numpy()
sorted_indices = probs[0].argsort()
filename=os.path.basename(wav_file)
label=sorted_indices[-1]+1
# print(f'{filename}, {label}')
f.write(f'{filename},{label}\n')
f.close()
下载 result.csv 提交即可得到分数。