当前位置: 首页 > 科技观察

从视频到音频:使用VIT进行音频分类

时间:2023-03-14 08:28:06 科技观察

就机器学习而言,音频本身就是一个完整的领域,应用范围很广,包括语音识别、音乐分类和声音事件检测等等。音频分类传统上使用频谱图分析和隐马尔可夫模型等方法,这些方法被证明是有效的,但也有其局限性。最近,VIT已成为音频任务的一个有前途的替代方案,OpenAI的Whisper就是一个很好的例子。在本文中,我们将利用ViT-VisionTransformer,这是一个Pytorch实现,并在音频分类数据集GTZAN数据集-MusicGenreClassification上对其进行训练。数据集简介GTZAN数据集是音乐流派识别(MGR)研究中最常用的公共数据集。这些文件是在2000-2001年从各种来源收集的,包括个人CD、收音机、麦克风录音,并代表各种录音条件下的声音。该数据集由子文件夹组成,每个子文件夹都是一种类型。加载数据集我们将加载每个.wav文件并通过librosa库生成相应的Mel声谱图。梅尔频谱图是声音信号频谱内容的直观表示,纵轴为梅尔刻度,横轴为时间。它是音频信号处理中常用的表示,尤其是在音乐信息检索领域。梅尔音阶(英语:melscale)是一种考虑到人类对音高的感知的音阶。因为人类不会感知线性范围内的频率,这意味着我们更擅长检测低频差异而不是高频差异。例如,我们可以很容易地分辨出500Hz和1000Hz之间的区别,但我们很难分辨出10,000Hz和10,500Hz之间的区别,即使它们之间的距离相同。所以梅尔标度解决了这个问题,如果梅尔标度的差异相同,就意味着人类感知到的音高差异将是相同的。defwav2melspec(fp):y,sr=librosa.load(fp)S=librosa.feature.melspectrogram(y=y,sr=sr,n_mels=128)log_S=librosa.amplitude_to_db(S,ref=np.max)img=librosa.display.specshow(log_S,sr=sr,x_axis='time',y_axis='mel')#获取没有白色边框的当前图形img=plt.gcf()img.gca().xaxis.set_major_locator(plt.NullLocator())img.gca().yaxis.set_major_locator(plt.NullLocator())img.subplots_adjust(top=1,bottom=0,right=1,left=0,hspace=0,wspace=0)img.gca().xaxis.set_major_locator(plt.NullLocator())img.gca().yaxis.set_major_locator(plt.NullLocator())#生成图像img.canvas.draw()img=Image.frombytes('RGB',img.canvas.get_width_height(),img.canvas.tostring_rgb())returnimg上面的函数将生成一个简单的梅尔频谱图:现在我们从文件夹中加载数据集并将转换应用于图像。类AudioDataset(数据集):def__init__(self,root,transform=None):self.root=rootself.transform=transformself.classes=sorted(os.listdir(root))self.class_to_idx={c:ifori,cinenumerate(self.classes)}self.samples=[]forcinself.classes:forfpinos.listdir(os.path.join(root,c)):self.samples.append((os.path.join(root,c,fp),self.class_to_idx[c]))def__len__(self):返回len(self.samples)def__getitem__(self,idx):fp,target=self.samples[idx]img=Image.open(fp)ifself.transform:img=self.transform(img)返回img,targettrain_dataset=AudioDataset(root,transform=transforms.Compose([transforms.Resize((480,480)),transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))]))ViT模型我们将使用ViT作为我们的模型:VisionTransformer在论文中首次介绍一张图片等于16x16单言,并成功地证明了该方法不依赖任何cnn,直接将一个纯Transformer应用于图像Patches序列,就可以很好的完成图像分类任务。将图像分割成Patches,将这些Patches的线性embedding序列作为Transformer的enter。补丁的处理方式与NLP应用程序中的标记(单词)相同。由于缺乏CNN固有的归纳偏差(如局部性),Transformer在训练数据量不足时无法很好地泛化。但在大型数据集上训练时,它确实在几个图像识别基准测试中达到或击败了最先进的技术。实现的结构如下:classViT(nn.Sequential):def__init__(self,in_channels:int=3,patch_size:int=16,emb_size:int=768,img_size:int=356,depth:int=12,n_classes:int=1000,**kwargs):super().__init__(PatchEmbedding(in_channels,patch_size,emb_size,img_size),TransformerEncoder(depth,emb_size=emb_size,**kwargs),ClassificationHead(emb_size,n_classes)training训练循环也是传统的训练过程:vit=ViT(n_classes=len(train_dataset.classes))vit.to(device)#traintrain_loader=DataLoader(train_dataset,batch_size=32,shuffle=True)optimizer=optim.Adam(vit.parameters(),lr=1e-3)scheduler=ReduceLROnPlateau(optimizer,'max',factor=0.3,patience=3,verbose=True)criterion=nn.CrossEntropyLoss()num_epochs=30范围内的纪元(num_epochs):print('Epoch{}/{}'.format(epoch,num_epochs-1))print('-'*10)vit.train()running_loss=0.0running_corrects=0对于输入,tqdm.tqdm(train_loader)中的标签:inputs=inputs.to(device)labels=labels.to(device)optimizer.zero_grad()withtorch.set_grad_enabled(True):outputs=vit(inputs)loss=criterion(outputs,labels)_,preds=torch.max(outputs,1)loss.backward()optimizer.step()running_loss+=loss.item()*inputs.size(0)running_corrects+=torch.sum(preds==labels.data)epoch_loss=running_loss/len(train_dataset)epoch_acc=running_corrects.double()/len(train_dataset)scheduler.step(epoch_acc)print('Loss:{:.4f}Acc:{:.4f}'.format(epoch_loss,epoch_acc))总结使用PyTorch为数据集从头开始训练此VisionTransformer架构的自定义实现非常小(每类只有100个样本),影响了模型的性能,只能达到0.71的准确率。这只是一个简单的演示。如果需要提升模型性能,可以使用更大的数据集,或者稍微调整架构的各种超参数!这里使用的vit代码来自:https://medium.com/artificialis/vit-visiontransformer-a-pytorch-implementation-8d6a1033bdc5