项目已经开源,只要略微了解python就能尝试
点个Star支持一下!😄
创意启发
昨天逛推特的时候,发现了一条帖子
作者分享了使用开源数据集微调Llama 2 实现了文本生成音乐的办法,不同于传统的使用音频物理数据、MIDI数据预训练,而是使用了一种叫做ABC记谱法的表示方式。
作者的想法和我之前对LLM生成音乐的尝试不谋而合,也解决了我之前没想通的问题,作为一个AI实践者,马上上code!这篇文章给大家分享一下开源一款AI小应用的历程,项目地址见开头。
大致思路
Prompt -> 语言模型生成 -> 解析ABC -> ABC转音频
最适合的音乐沟通语言
先介绍下ABC和MIDI,来源于GPT
ABC记谱法是一种简单的音乐表示法,它使用字母、数字和符号来表示音乐。这种表示法最初是为了方便音乐家在没有乐谱的情况下记忆和分享乐曲的。ABC记谱法可以表示音高、音长、调式、音速等音乐基本元素,也可以表示一些高级元素如和弦、复音、装饰音等。
ABC记谱法的基本规则如下:
音高:使用小写字母a-g表示大字一组中的七个基本音,使用大写字母A-G表示大字二组中的七个基本音;
音长:跟在音符后面的数字表示该音符的音长,例如:"c2"表示C音持续2拍,如果没有数字,默认音长为1拍;
调式:用字母K(Key)表示调式,例如:"K:C"表明接下来的乐曲是以C大调为基础的;
音速:用字母Q(Quaver)表示四分音符(即八分音符)的速度,例如:"Q:60"表示音速为每分钟60拍。
MIDI(Musical Instrument Digital Interface,音乐设备数字接口)是一种技术标准,用于电子音乐设备和软件之间的通信与连接。MIDI信息可以包括音符的开启和关闭动作、音符的音高、强弱、長度,以及控制命令如音量改变、音色选择等。
MIDI的主要特点是控制信号与音频信号的分离,因此,MIDI既可以实时演奏,也可以非实时控制,十分强大灵活。同时,MIDI数据量远小于音频数据,方便储存和移动,极大地推动了电子音乐和计算机音乐的发展。
我们用程序设计的视角来看
底层:声音的振幅或频率...物理意义的数据
低层:MIDI 01化的表示,连接了物理和软件
高层:ABC 语义化的表示,可读性更强
体验下ABC记谱法
makefile
X:1
T:小鸡的一家
M:2/4
Q:1/4=140
L:1/16
K:D
C2 C2 C2 E2 | G2 G2 G4 | A3 G E2 G2 | D2 D2 D4 | %4E2 E2 G4 | C2 D2 E4 | C3 D (E2 G2) | [12 D2 z2 E2 z2 | %8C4 z4 ] | [3 D2 D2 D2 E2 | C4 z4 ] :|
毫无疑问ABC记谱法更适合作为大语言模型的输出,它更简短,格式更清晰,单位数据长度信息密度更高,更符合自然语言。
Prompt 编写
Prompt中最难的输出格式的问题解决了,接下来就是如何编写prompt提升模型对用户输入的理解,如何输出更加和谐、好听的ABC谱子。
我用的是字节的Coze,可以白嫖GPT4,经过我一段时间的调试,prompt见下
markdown
SYSTEM = """# 角色
你是一个才华横溢的**流行音乐创作大师**,擅长用ABC记谱法,
## 技能:
### 技能 1:打造音乐结构
- 掌握并运用流行音乐常见的乐曲形式:前奏、主歌1、副歌1、主歌2、副歌2、中段(独奏或桥梁)、副歌3以及尾声。
- 根据用户输入决定ABC内容,包含X T M K X L P标记
- 根据输入,选择和主题匹配的套路和弦、调性、标题、情绪、速度、节奏。
### 技能 2:创作动人旋律
- 多使用七和弦和分解和弦
- 旋律好听,节奏要跟随发展产生较大的变化
- 副歌部分用简洁且重复的旋律,让人忍不住想一遍遍重复听
## 要求:
- M标记的拍数严格等于每个小节拍数,比如当M:4/4是,每小节的拍数都为4
- 音符后面的数字代表持续时长,控制每小节的拍数符合M的要求
## 输出格式:
- 和弦标记要用引号
- 在ABC乐谱中,用P:标记段落,段落之间用换行分隔
- 使用ABC谱输出乐曲内容,确保其格式完全符合ABC标准。
- 用markdown的代码块格式输出乐谱,方便用户阅读和理解。
"""
结果基本没问题,经常出现每小节的拍数对不上的问题,特别是在输出复杂音乐上,
看看效果
"创作一首伤感、慢速的华语流行曲"
GPT-Musician/蔷薇殇.wav at main · jiran214/GPT-Musician (github.com)
先问它"告诉我民谣的常见和弦套路" ,再问"使用五度下行和 I -- V -- vi -- IV,写一首经典民谣"
GPT-Musician/寂静的夜晚.wav at main · jiran214/GPT-Musician (github.com)
输出如下
makefile
当然,我非常乐意为您创作一首经典的民谣。按照您的要求,我们将在副歌部分使用五度下行和弦进行,主歌部分使用I -- V -- vi -- IV和弦进行。以下是我们的民谣作品,歌曲名为《寂静的夜晚》:
```abc
X:1
T:寂静的夜晚
C:音乐作曲家
M:4/4
L:1/4
Q:1/4=75
K:D
P:A (主歌)
"D" D2 A2 | "A"B2 "Bmin"G2 | "G" F2 "D/E"E2 | "F#min"A2 "G"A2 |
"D" D2 A2 | "A"B2 "Bmin"G2 | "G" F2 "D/E"E2 | "F#min"A2 "G"A2 |
P:B (副歌)
"G" D2 C2 | "D" B2 "A7/C#"A2 | "D/F#"F2 "G"D2 | "A7/E"A2 "D/F#"A2 |
"G" D2 C2 | "D" B2 "A7/C#"A2 | "D/F#"F2 "G"D2 | "A7" G2 || "D"D4
```
这些和弦进行在众多经典民谣中常常出现,它们能营造出愉悦、舒服的旋律。歌曲的开头和结束都用了"D"和弦,让整首歌保持连贯性和一致性。我希望你喜欢这首歌,同时也希望它能够为你带来灵感。
成品还是不错滴!
代码解析
python
# 上面重复了
SYSTEM = """..."""
def chat(human, system=SYSTEM, model='gpt-3.5-turbo', **openai_kwargs):
openai_kwargs['api_key'] = openai_kwargs.get('api_key') or os.environ.get("OPENAI_API_KEY")
client = OpenAI(**openai_kwargs)
chat_completion = client.chat.completions.create(
messages=[
{"role": "system", "content": system},
{"role": "user", "content": human}
],
model=model,
)
print(f'消耗token:{chat_completion.usage}')
return chat_completion.choices[-1].message.content
def process(content):
abc_notation = re.search(r'```.*(X:.*)```', content, flags=re.S) or re.search(r'```.*(T:.*)```', content, flags=re.S)
if not abc_notation:
print('解析失败!😭')
abc_notation = abc_notation.group(1)
if 'X:' not in abc_notation:
abc_notation = f'X:1\n{abc_notation}'
title = re.search(r'T:(.+?)\n', abc_notation, flags=re.S)
title = (title and title.group(1)) or int(time.time())
filename = f"{title}.wav"
abc_notation = abc_notation.strip().replace('\n\n', '\n').replace('min', 'm')
s = Score.from_abc(abc_notation)
audio = Synthesizer().render(s, stereo=True)
dump_wav(filename, audio, sample_rate=44100, use_int16=True)
return filename
def run(human, model=None, **openai_kwargs):
print(f'{model} 思考中🤔...\n\n')
resp = chat(human, model=model, **openai_kwargs)
print(resp)
print(f'ABC乐谱 处理中...\n\n')
filename = process(resp)
print(f'完成! 文件:当前目录/{filename}')
def main():
parser = argparse.ArgumentParser(description='GPT_Musician 快速生成旋律')
parser.add_argument('-p', '--prompt', required=True, help='music prompt')
parser.add_argument('-m', '--model', default='gpt-4', help='openai chat model name')
parser.add_argument('-key', '--api_key', help='openai api key')
args = parser.parse_args()
run(args.prompt, args.model, api_key=args.api_key)
if __name__ == '__main__':
main()
每个函数对应一个功能
- main:命令行解析
- chat:请求openai模型
- process:处理响应,提取abc并转换成wav音频
- Run:主函数
Ps: 最好还是用默认的GPT-4
END
非常值得的尝试:openai官方提供了微调接口,利用ABC数据集huggingface.co/datasets/m-...
可能效果会更好。在我prompt调试的过中,对于多声部生成、乐理理解、ABC语法正确性上,生成结果不是很理想,微调能在现有的基础上提升很多
OK !从想法到最后的项目历程都讲完了,大家觉得和原作者的微调LLama2 几几开呢?
求个点赞、评论~
求个star github.com/jiran214/GP...