i

isyangkt

V1

2022/10/08阅读:18主题:默认主题

音乐生成文本

前言

今天给大家介绍一篇音乐生成文本的paper,具体的是生成描述当前这个音乐的文本,同时还可以学一下其中作者设计的一个对比学习,笔者也贴了一下自己对其基本的代码实现,感兴趣的小伙伴可以收藏一下~

论文链接:https://arxiv.org/pdf/2210.00434.pdf

硬广

哈哈,在开始之前,如果有小伙伴对多模态感兴趣,笔者之前也写过几篇,大家感兴趣也可以看看,不过都是关于图文的:

https://zhuanlan.zhihu.com/p/435697429

https://zhuanlan.zhihu.com/p/453581899

https://zhuanlan.zhihu.com/p/454314421

https://zhuanlan.zhihu.com/p/570332906

数据集

为啥要做这个任务呢?作者也是给了一些case如下:

可以看到这里列出了两首乐章,以往的工作基本上都是对音乐进行分类,比如上图中的Tags,两首乐章的Tags是相同的,但是实际上两首乐章的风格其实有比较大的区别的,一首是比较优美的(peaceful, and extraordinarily beautiful treatment),而另外一首是比较悲伤的(sadness and loss),为此作者认为有必要做这项研究。

由于这个任务比较新,之前没有相关的工作做过,也就没有相关的公开标注数据集拿来训练,于是作者第一步就是需要构建数据集。

其实最主要的就是构建music-text pairs,作者选择爬取数据的网站是:https://www.earsense.org/,数据集中的95%的音乐时长是2.5min到14min不等,对应的描述性文本长度是14到192不等,最后共收集到了2380个文本,其中1955个有对应的音乐。

模型

可以看到这个任务其实是一个跨模态生成任务:

具体做的都也很常规,不过值得看一下的是paper中说的一个对比学习: Group Topology-Preservation Loss (GTP Loss)。

首先需要encoder和对应的decoder来分别对music和text提取特征和重建,具体的作者分别选用的是CNN和transformer。

所以各个模态编码解码对应两个loss如下,公式(1)是音频模态,公式(2)是文本模态:

接下来就是一个跨模态对齐loss:

可以看到也很简单,就是直接加一层MLP映射,然后求L2距离即可。

说完了上面常见的,作者重点讲述了一下自己提出的创新点即paper中所说的的GTP Loss:

作者讲了很多理论和故事来阐述这个设计,这里笔者不想按部就班的讲作者的故事,而是通过一个例子来阐述一下:

假设当前batch=4,那么对应的输入数据中文本假设是[a,b,c,d],音乐是[e,f,g,h],也就是说文本a是音乐e的描述性语言,同理b和f、c和g、d和h分别是对应的pair。

假设a和b、c、d相似度(sfotmax后的)分别是0.2、0.6、0.2;假设e和f、g、h相似度(sfotmax后的)分别是0.1、0.7、0.3

好,到这里最重要的一个对比学习思想来了:

a和b、c、d相似度分布应该尽可能与e和f、g、h相似度相似,换句话说:[0.2、0.6、0.2]和[0.1、0.7、0.3]尽可能的相似。

同理b和a、c、d相似度分布应该尽可能与f和e、g、h相似度相似,以此类推。

看到这里相信大家也比较明白了,其实就是个对比学习,不过相比于作者设计的这个对比学习,还有一个设计方式其实更容易想到,那就是:

a和e的相似度要大于a和f、g、h相似度,同理b和f的相似度要大于b和e、g、h相似度。

同时作者这里也借鉴目前对比学习的代码,分别实现一下上述两个对比学习loss(pytorch版本的),大家有兴趣也可以在自己的工作中加个对比学习loss试一下

wai_user_embedding和nei_user_embedding可以看成是两个模态的emb(对应到本篇就是音乐和文本的表征),维度都是[batch, hidden_size],这里笔者在量化分布相似的时候用的是KL散度,没用作者所说的L2,大家都可以改着试试。

def GroupTopologyPreservationLoss(self, wai_user_embedding, nei_user_embedding):
  wai_user_embedding_similarity = F.cosine_similarity(wai_user_embedding.unsqueeze(1), wai_user_embedding.unsqueeze(0), dim=-1)
  nei_user_embedding_similarity = F.cosine_similarity(nei_user_embedding.unsqueeze(1), nei_user_embedding.unsqueeze(0), dim=-1)
  n = wai_user_embedding_similarity.size()[0]
  wai_user_embedding_similarity = wai_user_embedding_similarity.flatten()[:-1].view(n-1,n+1)[:,1:].flatten().view(n, n-1)
  nei_user_embedding_similarity = nei_user_embedding_similarity.flatten()[:-1].view(n-1,n+1)[:,1:].flatten().view(n, n-1)
  kl_wai_nei = F.kl_div(F.log_softmax(wai_user_embedding_similarity, dim=-1), F.softmax(nei_user_embedding_similarity, dim=-1), reduction='sum')
  kl_nei_wai = F.kl_div(F.log_softmax(nei_user_embedding_similarity, dim=-1), F.softmax(wai_user_embedding_similarity, dim=-1), reduction='sum')
  loss = (kl_wai_nei+kl_nei_wai)/2
  return loss

另外一种就是笔者所说的第二种常见的,大家可以看一下这里https://blog.csdn.net/weixin_44966641/article/details/120382198

不过需要说明的是,上述链接中预先定义了一个negatives_mask buff,其大小是固定的,但是有的时候我们的代码在跑的时候,最后一个batch有可能不够预设的大小即我们的样本总数是102,每个batch假设是25,那么最后一个batch其实是2,这个时候在用这个negatives_mask buff计算loss的时候就会出错,那怎么办呢?很简单我们多加两行代码:

def contrastiveLoss(self, emb_i, emb_j):
  temp_batch_size = emb_i.size()[0]
  negatives_mask = self.negatives_mask[:temp_batch_size*2, :temp_batch_size*2]
  ...

可以看到是为了适配当前传进来embedding的大小。

看到这里,笔者就再多说一句吧,有的时候我们处理的不是多模态,假设是文本和文本之间的对比学习,最后我们还是想得到类似emb_i, emb_j两个矩阵来进行对比学习,那这两个emb怎么得到呢?要过两次模型(比如bert模型)吗?其实很简单,只需要在bert模型输入的时候将两部分样本进行cat成一批,然后输进去,在输出的时候再按顺序拿到对应的emb即可,只不过为了加对比学习,总体可跑的batch可能就要减半了。

总结

(1)今天给大家介绍了一个很好玩的研究方向,其实还可以反过来,根据文本生成bgm,这个感觉更有意思。

(2)介绍了点对比学习和代码,希望对大家有用。

关注

欢迎关注,下期再见啦~

知乎csdngithub微信公众号

分类:

人工智能

标签:

人工智能

作者介绍

i
isyangkt
V1