import numpy as np
import torch.nn.functional as F
from tqdm.auto import tqdm
from IPython.display import Audio
from matplotlib import pyplot as plt
from diffusers import DiffusionPipeline
from torchaudio import transforms as AT
from torchvision import transforms as IT

二、從預(yù)訓(xùn)練的音頻擴散模型Pipeline中進行采樣

? ? ? ?加載預(yù)訓(xùn)練好的音頻擴散模型Audio?Diffusion(用于生成音頻的梅爾譜圖)

# 加載一個預(yù)訓(xùn)練的音頻擴散模型管線
device = "cuda" if torch.cuda.is_available() else "cpu"
pipe = DiffusionPipeline.from_pretrained("teticio/audio-diffusion-
instrumental-hiphop- 256").to(device)
Fetching 5 files: 0%| | 0/5 [00:00<? , ?it/s]

? ?對pipe進行一次采樣

# 在管線中采樣一次并將采樣結(jié)果顯示出來
output = pipe()
display(output.images[0])
display(Audio(output.audios[0], rate=pipe.mel.get_sample_rate()))

? ?采樣結(jié)果,如下圖所示:

? ?上述代碼中,rate參數(shù)表示音頻的采樣率,下面我們查看一下音頻序列和頻譜

# 音頻序列
output.audios[0].shape

# 輸出
(1, 130560)
# 輸出的圖像(頻譜)
output.images[0].size

# 輸出
(256, 256)

音頻并非由擴散模型直接生成的,而是類似于無條件圖像生成管道那樣,使用一個2D UNet網(wǎng)絡(luò)結(jié)構(gòu)來生成音頻的頻譜,之后經(jīng)過后處理轉(zhuǎn)換為最終的音頻。

三、從音頻轉(zhuǎn)換為頻譜

? ? ? ?音頻的”波形“在時間上展示了源音頻,例如,音頻的”波形“可能是從麥克風(fēng)接收到的電信號。這種”時域“上的表示處理起來比較棘手,因此通常會轉(zhuǎn)換為頻譜來處理,頻譜能夠直接展示不同頻率(y軸)和時間(x軸)的強度。

# 使用torchaudio模塊計算并繪制所生成音頻樣本的頻譜,如圖8-2所示
spec_transform = AT.Spectrogram(power=2)
spectrogram = spec_transform(torch.tensor(output.audios[0]))
print(spectrogram.min(), spectrogram.max())
log_spectrogram = spectrogram.log()
lt.imshow(log_spectrogram[0], cmap='gray');
tensor(0.) tensor(6.0842)

??頻譜圖,如下所示:

?以上圖剛剛生成的音頻樣本為例,頻譜的取值范圍是0.0000000000001~1,其中大部分值接近取值下限,這對于可視化和建模來說不太理想,為此,我們使用了梅爾頻譜(Mel spectrogram)對不同頻率進行一些變換來符合人耳感知特性,下圖展示了torchaudio音頻轉(zhuǎn)換方法:

幸運的是,我們使用mel功能可以忽略這些細節(jié),就能吧頻譜轉(zhuǎn)換成音頻:

a = pipe.mel.image_to_audio(output.images[0])
a.shape

# 輸出
(130560,)

讀取源音頻數(shù)據(jù),然后調(diào)用audio_slice_to_image()函數(shù),將源音頻數(shù)據(jù)轉(zhuǎn)換為頻譜圖像。同時較長的音頻片段也會自動切片,以便可以正常輸出256X256像素的頻譜圖像,代碼如下:

pipe.mel.load_audio(raw_audio=a)
im = pipe.mel.audio_slice_to_image(0)
im

     音頻被表示成一長串數(shù)字數(shù)組。若想播放音頻,我們需要采樣率這個關(guān)鍵信息。 

? ? ? ?我們查看一下單位時間音頻的采樣點有多少個?

sample_rate_pipeline = pipe.mel.get_sample_rate()
sample_rate_pipeline

# 輸出
22050

如果設(shè)置別的采樣率,那么會得到一個加速或者減速播放的音頻,比如:


display(Audio(output.audios[0], rate=44100)) # 播放速度被加倍

四、微調(diào)音頻擴散模型數(shù)據(jù)準備

? ? ? ?在了解了音頻擴散模型Pipeline使用之后,我們在新的數(shù)據(jù)集上對其進行微調(diào),我們使用的數(shù)據(jù)集由不同類別的音頻片段集合組成的,代碼如下:

from datasets import load_dataset
dataset = load_dataset('lewtun/music_genres', split='train')
dataset

查看一下該數(shù)據(jù)集不同類別樣本所占的比例:

for g in list(set(dataset['genre'])):
print(g, sum(x==g for x in dataset['genre']))

輸出內(nèi)容如下:

Pop 945
Blues 58
Punk 2582
Old-Time / Historic 408
Experimental 1800
Folk 1214
Electronic 3071
Spoken 94
Classical 495
Country 142
Instrumental 1044
Chiptune / Glitch 1181
International 814
Ambient Electronic 796
Jazz 306
Soul-RnB 94
Hip-Hop 1757
Easy Listening 13
Rock 3095

該數(shù)據(jù)集已將音頻存儲為數(shù)組,代碼如下:

audio_array = dataset[0]['audio']['array']
sample_rate_dataset = dataset[0]['audio']['sampling_rate']
print('Audio array shape:', audio_array.shape)
print('Sample rate:', sample_rate_dataset)

# 輸出
Audio array shape: (1323119,)
Sample rate: 44100

PS:該音頻的采樣率更高,要使用該Pipeline,就需要對其進行”重采樣“。音頻也比Pipeline預(yù)設(shè)的長度要長,在調(diào)用pipe.mel加載該音頻時,會被自動切片為較短的片段。代碼如下:

a = dataset[0]['audio']['array']  # 得到音頻序列
pipe.mel.load_audio(raw_audio=a) # 使用pipe.mel加載音頻
pipe.mel.audio_slice_to_image(0) # 輸出第一幅頻譜圖像
sample_rate_dataset = dataset[0]['audio']['sampling_rate']
sample_rate_dataset

# 輸出
44100

??從上述代碼結(jié)果可以看出,該數(shù)據(jù)集的數(shù)據(jù)在每一秒都擁有兩倍的數(shù)據(jù)點,因此需要調(diào)整采樣率。這里我們使用torchaudio transforms(導(dǎo)入為AT)進行音頻重采樣,并使用Pipeline的mel功能將音頻轉(zhuǎn)換為頻譜圖像,然后使用torchvision transforms(導(dǎo)入為IT)將頻譜圖像轉(zhuǎn)換為頻譜張量。一下代碼中的to_image()函數(shù)可以將音頻片段轉(zhuǎn)換為頻譜張量,供訓(xùn)練使用:

resampler = AT.Resample(sample_rate_dataset, sample_rate_pipeline, 
dtype=torch.float32)
to_t = IT.ToTensor()
 
def to_image(audio_array):
audio_tensor = torch.tensor(audio_array).to(torch.float32)
audio_tensor = resampler(audio_tensor)
pipe.mel.load_audio(raw_audio=np.array(audio_tensor))
num_slices = pipe.mel.get_number_of_slices()
slice_idx = random.randint(0, num_slices-1) # 每次隨機取一張(除了
# 最后那張)
im = pipe.mel.audio_slice_to_image(slice_idx)
return im

整理微調(diào)數(shù)據(jù)

def collate_fn(examples):

# 圖像→張量→縮放至(-1,1)區(qū)間→堆疊
audio_ims = [to_t(to_image(x['audio']['array']))*2-1 for x in
examples]
return torch.stack(audio_ims)
 
# 創(chuàng)建一個只包含Chiptune/Glitch(芯片音樂/電子脈沖)風(fēng)格的音樂
batch_size=4 # 在CoLab中設(shè)置為4,在A100上設(shè)置為12
chosen_genre = 'Electronic' # <<< 嘗試在不同的風(fēng)格上進行訓(xùn)練 <<<
indexes = [i for i, g in enumerate(dataset['genre']) if g ==
chosen_genre]
filtered_dataset = dataset.select(indexes)
dl = torch.utils.data.DataLoader(filtered_dataset.shuffle(), batch_
size=batch_size,
collate_fn=collate_fn, shuffle=True)
batch = next(iter(dl))
print(batch.shape)

# 輸出
torch.Size([4, 1, 256, 256])

五、開始微調(diào)音頻擴散模模型

epochs = 3
lr = 1e-4
 
pipe.unet.train()
pipe.scheduler.set_timesteps(1000)
optimizer = torch.optim.AdamW(pipe.unet.parameters(), lr=lr)
 
for epoch in range(epochs):
for step, batch in tqdm(enumerate(dl), total=len(dl)):
# 準備輸入圖片
 
clean_images = batch.to(device)
bs = clean_images.shape[0]
 
# 為每一張圖片設(shè)置一個隨機的時間步
 
timesteps = torch.randint(
0, pipe.scheduler.num_train_timesteps, (bs,),
device=clean_images.device
).long()
# 按照噪聲調(diào)度器,在每個時間步為干凈的圖片加上噪聲
 
noise = torch.randn(clean_images.shape).to(clean_images.
device)
noisy_images = pipe.scheduler.add_noise(clean_images,
noise, timesteps)
# 得到模型的預(yù)測結(jié)果
 
noise_pred = pipe.unet(noisy_images, timesteps, return_
dict=False)[0]
# 計算損失函數(shù)
loss = F.mse_loss(noise_pred, noise)
loss.backward(loss)
 
# 使用優(yōu)化器更新模型參數(shù)
optimizer.step()
optimizer.zero_grad()
# 裝載之前訓(xùn)練好的頻譜樣本,如圖8-6所示
pipe = DiffusionPipeline.from_pretrained("johnowhitaker/Electronic_
test").to(device)
output = pipe()
display(output.images[0])
display(Audio(output.audios[0], rate=22050))
# 輸入一個不同形狀的起點噪聲張量,得到一個更長的頻譜樣本,如圖8-7所示
noise = torch.randn(1, 1, pipe.unet.sample_size[0],pipe.unet.
sample_size[1]*4).to(device)
output = pipe(noise=noise)
display(output.images[0])
display(Audio(output.audios[0], rate=22050))

生成的頻譜,如下圖所示:

生成更長的頻譜樣本,如下圖所示:

思考:

  1. 我們使用的是256X256像素的方形頻譜圖像,這會限制batch size,能否從128X128像素的頻譜圖像中恢復(fù)出質(zhì)量足夠好的音頻呢?
  2. 為了替代隨機圖像增強,我們每次都挑選了不同的音頻片段,但這種方法在訓(xùn)練循環(huán)后期是否可以用其他增強方法進行優(yōu)化呢?
  3. 是否有其他辦法可以用來生成更長的音頻呢?或者可以先生成開頭的5s音頻,之后再采用類似圖像修復(fù)的思路繼續(xù)生成后續(xù)的音頻。
  4. 擴散模型生成的內(nèi)容與Img2Img生成的內(nèi)容有什么相同之處?

文章轉(zhuǎn)自微信公眾號@ArronAI

上一篇:

擴散模型實戰(zhàn)(十三):ControlNet結(jié)構(gòu)以及訓(xùn)練過程

下一篇:

輕松上手 LangChain 開發(fā)框架之 Agent 技術(shù) !
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費