
如何快速實現(xiàn)REST API集成以優(yōu)化業(yè)務(wù)流程
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)練好的音頻擴散模型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)換為最終的音頻。
? ? ? ?音頻的”波形“在時間上展示了源音頻,例如,音頻的”波形“可能是從麥克風(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)) # 播放速度被加倍
? ? ? ?在了解了音頻擴散模型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])
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))
生成的頻譜,如下圖所示:
生成更長的頻譜樣本,如下圖所示:
文章轉(zhuǎn)自微信公眾號@ArronAI