使用LSTM-GAN为歌词谱曲

简介: 使用LSTM-GAN为歌词谱曲

在本文中,我将首先介绍基于AI的音乐生成的最新发展,然后介绍我创建的系统并讨论其组成,包括Yi Yu等人的“Lyrics-to-Melody” AI模型等。。[6]和Google的Music Transformer模型[7]。然后,我将演示一个示例,该示例从(Robert Frost)的诗歌中生成歌曲,并介绍其他生成的歌曲的集合。

背景

在过去的五个月中,我一直在研究如何将人工智能(AI)和机器学习(ML)用于创新活动。

尽管最先进的人工智能模型可以生成优秀的图片和文字,但到目前为止,人工智能模型在作曲方面还没有那么好。有些音乐生成模型还不错,比如谷歌[3]的various Magenta models,OpenAI[4]的MuseNet模型,以及AIVA[5]的商业产品。但大多数产生AI模型的音乐输出往往是杂乱无章和不连贯的。似乎缺少的是歌曲的整体结构。让我们看看我们是否可以通过注入抒情诗歌来弥补这一点。

640.png

系统总览

我使用的是由Yi Yu和她的同事设计和训练的Lyrics-to-Melody AI模型。他们称之为有条件的LSTM-GAN,用于从歌词中生成旋律[6]。

系统接受了约12K带有歌词的MIDI歌曲进行训练。它使用单词及其音节作为输入,并经过训练以预测音乐的音符,持续时间和静息持续时间作为输出。这是对“I’ve Been Working on the Railroad”的五个条形的分析,以黄色显示输入,以蓝色显示预期输出。请注意,“ day”一词之后的其余部分如何与下一个音节“ I've”相关联。

640.png

我使用的第二个主要系统是Music Transformer [7],它是谷歌的Magenta模型套件的一部分。该模型自动为给定的旋律生成音乐伴奏。它训练了作为雅马哈年度电子钢琴比赛[8]的一部分数据,大约400个由约翰·塞巴斯蒂安·巴赫的合唱团和1100个由专家钢琴家捕捉的表演。

下面是一个组件图,它显示了整个系统的流程,左边是作为文本的一首诗歌,右边是作为MIDI文件生成一首新歌。

640.png

每一行选定的诗被输入系统,一次一行。它使用一个名为Pyphen的模块,使用Hunspell连字符字典[9]将行中的每个单词分解成音节。将结果输入到歌词到旋律模型中。该模型是GAN和长短期记忆(LSTM)模型之间的混合体,用来进行MIDI格式的音符生成。

使用MIT的Music21库[10]分析所得的乐句,确定其所处的音调。然后将该乐句转换为C大调(或A Minor),并使用Music21量化为十六分音符。生成所有音乐行之后,将生成的MIDI文件输入到Music Transformer模型中,该模型添加一个伴随的音乐声部,并以具有表现力的键盘速度和定时来营造人性化的感觉。

最后,使用谷歌的Magenta 库[11]对最终的MIDI文件进行一些后处理,比如分配乐器声音。

在下一节中,我将详细介绍这些步骤,并显示为自定义处理编写的Python代码。

系统演练

在演练中,我们将使用Robert Frost的一首简短而完整的诗歌,称为“Plowmen” [12]。我将展示用于将这首诗转换为歌曲的Python代码的主要摘要。

准备诗歌

处理的第一步涉及将每个单词分解为音节,并创建要嵌入到LSTM-GAN中的单词嵌入。

这是示例诗。

PlowmenA plow, they say, to plow the snow.They cannot mean to plant it, no–Unless in bitterness to mockAt having cultivated rock.- Robert Frost

这是将每个单词分解为音节并将其输入LSTM-GAN的代码段。您可以看到它使用Word2Vec [13]为单词和音节创建并输出了嵌入内容。Google表示:“事实证明,通过Word2Vec学习到的嵌入在各种下游自然语言处理任务上都是成功的。”

fromgensim.modelsimportWord2VecsyllModel=Word2Vec.load(syll_model_path)
wordModel=Word2Vec.load(word_model_path)
importpyphendic=pyphen.Pyphen(lang='en_US')
poem='''A plow, they say, to plow the snow.They cannot mean to plant it, no–Unless in bitterness to mockAt having cultivated rock.'''lines=poem.split('\n')
forlineinlines:
line=re.sub(r'[^a-zA-Z ]+', '', line.strip())
line=re.sub(' +', ' ', line)
words=line.split(' ')
lyrics= []
forwordinwords:
syllables=dic.inserted(word).split('-')
iflen(syllables) >0:
forsyllableinsyllables:
iflen(syllable) is0:
continuelyric_syllables.append(syllable)
ifsyllableinsyllModel.wv.vocabandwordinwordModel.wv.vocab:
lyrics.append([syllable, word])
else:
lyrics.append(["la", "la"])
else:
lyric_syllables.append(word)
iflen(syllable) is0:
continueifwordinwordModel.wv.vocabandsyllableinsyllModel.wv.vocab:
lyrics.append([word, word])
else:
lyrics.append(["la", "la"])

您还可以看到我是如何处理不在字典中的单词的嵌入。如果一个单词没有在字典里,我只需要用“la”来代替正确的音节数。这是词曲作者的一个传统,当他们还没有写完所有的歌词。

这是这首诗诗句的音节。

Aplow, theysay, toplowthesnow.
['A', 'plow', 'they', 'say', 'to', 'plow', 'the', 'snow']
Theycannotmeantoplantit, no–['They', 'can', 'not', 'mean', 'to', 'plant', 'it', 'no']
Unlessinbitternesstomock['Un', 'less', 'in', 'bit', 'ter', 'ness', 'to', 'la']
Athavingcultivatedrock.
['At', 'hav', 'ing', 'la', 'la', 'la', 'la', 'rock']

你可以看到单词mock和cultivated变成了la。

生成旋律

一旦单词和音节的嵌入设置好了,就很容易产生旋律。这里的代码。

 

length_song=len(lyrics)
cond= []
foriinrange(20):
ifi<length_song:
syll2Vec=syllModel.wv[lyrics[i][0]]
word2Vec=wordModel.wv[lyrics[i][1]]
cond.append(np.concatenate((syll2Vec, word2Vec)))
else:
cond.append(np.concatenate((syll2Vec, word2Vec)))
flattened_cond= []
forxincond:
foryinx:
flattened_cond.append(y)
pattern=generate_melody(flattened_cond, length_song)

length_song变量设置为文本行传递的音节数。该模型经过硬编码,可以容纳20个音节,因此代码将限制输入,并在必要时通过重复最后一个音节来填充输入。注意,该填充将被模型忽略,并且我们将得到一个音符向量,该音符等于行中音节的数量。这是旋律。

处理旋律

LSTM-GAN的输出非常好,但存在一些问题:音乐没有量化,而且每一行的键都在变化。LSTM-GAN系统的原始代码具有将旋律“离散化”并将其转置为统一键的功能。但是我选择使用Music21库来执行这些功能。

下面的代码显示了如何将每个音符量化为十六分音符(第12和13行),以及如何将最后一个音符扩展到小节的末尾(第22行)。

importmusic21deftranspose_notes(notes, new_key):
midi_stream=music21.stream.Stream(notes)
key=midi_stream.analyze('key')
interval=music21.interval.Interval(key.tonic, new_key.tonic)
new_stream=midi_stream.transpose(interval)
returnnew_stream.notes#quantizethestarttimesandnotedurationsforc, ninenumerate(pattern):
n.offset=int(float(n.offset)*4+0.5) /4n.quarterLength=int(float(n.quarterLength)*4+0.5) /4n.offset+=song_lengthnew_notes.append(n)
pattern_length+=n.quarterLength#stretchthelastnoteouttoholdthetimepattern_length_adjusted=float(pattern_length-0.125)
new_length=4* (1+pattern_length_adjusted//4)diff=new_length-float(pattern_length)
new_notes[-1].quarterLength+=diffthe_key=music21.key.Key("C") #CMajor#transposenew_notes=transpose_notes(new_notes, the_key)

您还可以看到如何使用Music21(第27行在第3行调用该函数)将每行换位到C大调中。这是生成的旋律。

下一步是将旋律传递到Music Transformer以创建一个伴随的轨道,并使旋律更人性化。这里的代码。

model_name='transformer'hparams_set='transformer_tpu'ckpt_path='gs://magentadata/models/music_transformer/checkpoints/melody_conditioned_model_16.ckpt'classMelodyToPianoPerformanceProblem(score2perf.AbsoluteMelody2PerfProblem):
@propertydefadd_eos_symbol(self):
returnTrueproblem=MelodyToPianoPerformanceProblem()
melody_conditioned_encoders=problem.get_feature_encoders()
inputs=melody_conditioned_encoders['inputs'].encode_note_sequence(melody_ns)
#Generatesampleevents.
decode_length=4096sample_ids=next(melody_conditioned_samples)['outputs']
#DecodetoNoteSequence.
midi_filename=decode(
sample_ids,
encoder=melody_conditioned_encoders['targets'])
accompaniment_ns=note_seq.midi_file_to_note_sequence(midi_filename)

前11行代码将设置transformer。代码的其余部分采用名为melody_ns的音符序列,并生成与原旋律合并为伴奏的音轨。

处理的最后步骤是分配乐器并通过保留最后的音符作为额外的措施来创建结尾。这是最后步骤的代码。

deffind_closest_note(n, notes):
closest=Nonesmallest_diff=float("inf")
forxinnotes:
ifn.pitch==x.pitch:
diff=abs(n.start_time-x.start_time)
if (diff<smallest_diff):
closest=xsmallest_diff=diffreturnclosestforninaccompaniment_ns.notes:
n.instrument=1n.program=2#pianoclosest_notes= []
forninmelody_ns.notes:
closest_note=find_closest_note(n, accompaniment_ns.notes)
closest_note.instrument=0closest_note.program=26#guitarclosest_notes.append(closest_note)
lyric_times.append(closest_note.start_time)
lyric_pitches.append(closest_note.pitch)
#maptotheclosestnotesintheoriginalmelodyforcinrange(len(closest_notes)-1):
if (closest_notes[c].end_time>closest_notes[c+1].start_timeandclosest_notes[c+1].start_time>closest_notes[c].start_time):
closest_notes[c].end_time=closest_notes[c+1].start_time#holdthelast5notestocreateanendingpitches= []
foriinrange(1,6):
n=accompaniment_ns.notes[-i]
ifn.pitchnotinpitches:
n.end_time+=4

由于原始旋律的时序在转换器中发生了变化,因此我不得不编写一个小函数来映射两个音序之间最接近的音符。然后,我使用该功能查找更改的音符,以将乐器设置为吉他。最后一个代码块保留了最后五个注释,这是一个额外的措施。这是一个提示歌曲结束的小技巧。

Music Transformer的伴奏确实为乐曲增添了深度和色彩。通过改变MIDI音符的开始时间和速度,该模型还给乐曲带来更人性化的感觉。

结果

通常,这种音乐生成方法会产生不错的效果。

经过调转和调整旋律线的时间,LSTM-GAN似乎为诗歌产生了良好的旋律。诗歌具有成为好音乐的品质,如韵律、结构和抒情流。LSTM-GAN既获取仪表的音节,又获取含义的单词作为输入。来自两种输入的质量导致良好的旋律。请注意,此模型非常一致,因为在给定相同输入文本的情况下,它将生成几乎相同的旋律。

然而,对于Music Transformer同一输入旋律的输出变化很大,有的伴奏效果很差。

目录
相关文章
|
缓存 安全 Java
【JavaSE专栏78】线程同步,控制多个线程之间的访问顺序和共享资源的安全性
【JavaSE专栏78】线程同步,控制多个线程之间的访问顺序和共享资源的安全性
306 0
|
存储 Kubernetes API
【K8S系列】第十一讲:包管理神器-Helm
【K8S系列】第十一讲:包管理神器-Helm
509 0
|
6月前
|
人工智能 安全 搜索推荐
合规风险、汇率损失、用户流失:跨境结算的“三座大山”怎么搬?
跨境电商代购系统面临跨境支付效率低、成本高、合规难和技术滞后等痛点。本文分析四大挑战,并探讨数字钱包、区块链、API与AI等技术解决方案,结合典型案例与未来趋势,助力企业构建高效、低成本、合规的跨境支付体系,推动行业迈向智能化、绿色化发展新阶段。
|
Web App开发 Go iOS开发
【IOS】教你如何在手机端轻松安装 ipa 文件 -(安装器已失效 21.10)|社区征文
【IOS】教你如何在手机端轻松安装 ipa 文件 -(安装器已失效 21.10)|社区征文
|
JavaScript 前端开发 API
Vue 使用vue完成登录+注册前后端交互的实现
Vue 使用vue完成登录+注册前后端交互的实现
882 0
|
Unix Linux 数据处理
使用Python批量复制文件夹及其子文件夹下的指定文件
使用Python批量复制文件夹及其子文件夹下的指定文件
811 1
|
Web App开发 测试技术 项目管理
【Docker项目实战】使用Docker部署Servas自托管书签管理工具
【6月更文挑战第5天】使用Docker部署Servas自托管书签管理工具
493 1
【Docker项目实战】使用Docker部署Servas自托管书签管理工具
|
C++
【洛谷 P1042】[NOIP2003 普及组] 乒乓球 题解(模拟+向量)
`NOIP2003`普及组编程题:乒乓球比赛模拟。给定一系列球赛记录(WL序列),程序需按11分和21分制分析比分。输入含多个字符串,含W(华华得分)、L(对手得分)和E(结束标记)。输出每局比分,分制间空行间隔。样例:`WWWWWW...` → `11:0\n11:0\n1:1`(11分制)和`21:0\n2:1`(21分制)。代码使用C++,逐字符读取,当分差≥2且得分≥x时输出比分。
317 0
|
缓存 Ubuntu 数据库
ubuntu无法使用apt命令时怎么安装库
无论哪种方法,一旦成功安装了所需的库文件,你应该能够在Ubuntu系统上正常使用它们。如果问题仍然存在,可能需要进一步检查系统的包管理配置和依赖关系问题。
951 0
|
存储 前端开发 API
贪吃蛇小游戏的实现【C语言魅力时刻】(上)
贪吃蛇小游戏的实现【C语言魅力时刻】(上)
329 0