HuggingFace Diffusers 0.12 : パイプライン : 音声拡散 (Colab 版)

HuggingFace Diffusers 0.12 : API : パイプライン – 音声拡散 (Colab 版) (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/15/2023 (v0.14.0)

* 本ページは、HuggingFace Diffusers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス

クラスキャット は人工知能・テレワークに関する各種サービスを提供しています。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

  • 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
  • sales-info@classcat.com  ;  Web: www.classcat.com  ;   ClassCatJP

 

HuggingFace Diffusers 0.12 : API : パイプライン – 音声拡散 (Colab 版)

訓練スクリプトとノートブックについては、https://github.com/teticio/audio-diffusion にアクセスしてください。

try:
    # are we running on Google Colab?
    import google.colab
    %pip install -q diffusers torch librosa datasets
except:
    pass
import torch
import random
import librosa
import numpy as np
from datasets import load_dataset
from IPython.display import Audio
from librosa.beat import beat_track
from diffusers import DiffusionPipeline
device = "cuda" if torch.cuda.is_available() else "cpu"
generator = torch.Generator(device=device)

 

DDPM (ノイズ除去拡散確率モデル)

モデルの選択

  • teticio/audio-diffusion-256 – my Spotify “liked” プレイリストで訓練。
  • teticio/audio-diffusion-breaks-256 – music で使用されたサンプルで訓練。
  • teticio/audio-diffusion-instrumental-hiphop-256 – instrumental ヒップホップで訓練。
#@markdown teticio/audio-diffusion-256                     - trained on my Spotify "liked" playlist

#@markdown teticio/audio-diffusion-breaks-256              - trained on samples used in music

#@markdown teticio/audio-diffusion-instrumental-hiphop-256 - trained on instrumental hiphop

model_id = "teticio/audio-diffusion-256"  #@param ["teticio/audio-diffusion-256", "teticio/audio-diffusion-breaks-256", "audio-diffusion-instrumenal-hiphop-256", "teticio/audio-diffusion-ddim-256"]
audio_diffusion = DiffusionPipeline.from_pretrained(model_id).to(device)
mel = audio_diffusion.mel
sample_rate = mel.get_sample_rate()
def loop_it(audio: np.ndarray,
        sample_rate: int,
        loops: int = 12) -> np.ndarray:
    """Loop audio

    Args:
        audio (np.ndarray): audio as numpy array
        sample_rate (int): sample rate of audio
        loops (int): number of times to loop

    Returns:
        (float, np.ndarray): sample rate and raw audio or None
    """
    _, beats = beat_track(y=audio, sr=sample_rate, units='samples')
    for beats_in_bar in [16, 12, 8, 4]:
        if len(beats) > beats_in_bar:
            return np.tile(audio[beats[0]:beats[beats_in_bar]], loops)
    return None

 

メルスペクトログラム, 音声そしてループを生成するモデル推論の実行

for _ in range(10):
    seed = generator.seed()
    print(f'Seed = {seed}')
    generator.manual_seed(seed)
    output = audio_diffusion(generator=generator)
    image = output.images[0]
    audio = output.audios[0, 0]
    display(image)
    display(Audio(audio, rate=sample_rate))
    loop = loop_it(audio, sample_rate)
    if loop is not None:
        display(Audio(loop, rate=sample_rate))
    else:
        print("Unable to determine loop points")

 

音声のバリエーションの生成

Try playing around with start_steps. 値がゼロに近ければ新しいサンプルを生成し、一方で 1,000 に近い値はオリジナルにより忠実なサンプルを生成します。

seed = 2391504374279719  #@param {type:"integer"}
generator.manual_seed(seed)
output = audio_diffusion(generator=generator)
image = output.images[0]
audio = output.audios[0, 0]
display(image)
display(Audio(audio, rate=sample_rate))
start_step = 500  #@param {type:"slider", min:0, max:1000, step:10}
track = loop_it(audio, sample_rate, loops=1)
for variation in range(12):
    output = audio_diffusion(raw_audio=audio, start_step=start_step)
    image2 = output.images[0]
    audio2 = output.audios[0, 0]
    display(image2)
    display(Audio(audio2, rate=sample_rate))
    track = np.concatenate([track, loop_it(audio2, sample_rate, loops=1)])
display(Audio(track, rate=sample_rate))

 

continuations (連続性) の生成 (「アウトペインティング」)

overlap_secs = 2  #@param {type:"integer"}
start_step = 0  #@param {type:"slider", min:0, max:1000, step:10}
overlap_samples = overlap_secs * sample_rate
track = audio
for variation in range(12):
    output = audio_diffusion(raw_audio=audio[-overlap_samples:],
                             start_step=start_step,
                             mask_start_secs=overlap_secs)
    image2 = output.images[0]
    audio2 = output.audios[0, 0]
    display(image2)
    display(Audio(audio2, rate=sample_rate))
    track = np.concatenate([track, audio2[overlap_samples:]])
    audio = audio2
display(Audio(track, rate=sample_rate))

 

リミックス (スタイル変換)

また、まったく別の音声から始めることもできて、これは一種のスタイル変換になります。生成時に同じシードを保持するとスタイルを固定は固定され、一方でマスキングは連続的なセグメントを一緒によりスムースに繋ぎ合わせるのに役立ちます。

try:
    # are we running on Google Colab?
    from google.colab import files
    audio_file = list(files.upload().keys())[0]
except:
    audio_file = "/home/teticio/Music/liked/El Michels Affair - Glaciers Of Ice.mp3"
start_step = 500  #@param {type:"slider", min:0, max:1000, step:10}
overlap_secs = 2  #@param {type:"integer"}
track_audio, _ = librosa.load(audio_file, mono=True, sr=sample_rate)
overlap_samples = overlap_secs * sample_rate
slice_size = mel.x_res * mel.hop_length
stride = slice_size - overlap_samples
generator = torch.Generator(device=device)
seed = generator.seed()
print(f'Seed = {seed}')
track = np.array([])
not_first = 0
for sample in range(len(track_audio) // stride):
    generator.manual_seed(seed)
    audio = np.array(track_audio[sample * stride:sample * stride + slice_size])
    if not_first:
        # Normalize and re-insert generated audio
        audio[:overlap_samples] = audio2[-overlap_samples:] * np.max(
            audio[:overlap_samples]) / np.max(audio2[-overlap_samples:])
    output = audio_diffusion(raw_audio=audio,
                             start_step=start_step,
                             generator=generator,
                             mask_start_secs=overlap_secs * not_first)
    audio2 = output.audios[0, 0]
    track = np.concatenate([track, audio2[overlap_samples * not_first:]])
    not_first = 1
    display(Audio(track, rate=sample_rate))

 

ギャップを埋める (「インペインティング」)

sample = 3  #@param {type:"integer"}
raw_audio = track_audio[sample * stride:sample * stride + slice_size]
output = audio_diffusion(raw_audio=raw_audio,
                         mask_start_secs=1,
                         mask_end_secs=1,
                         step_generator=torch.Generator(device=device))
audio2 = output.audios[0, 0]
display(Audio(audio, rate=sample_rate))
display(Audio(audio2, rate=sample_rate))

 

DDIM (ノイズ除去拡散暗黙モデル)

audio_diffusion = DiffusionPipeline.from_pretrained('teticio/audio-diffusion-ddim-256').to(device)
mel = audio_diffusion.mel
sample_rate = mel.get_sample_rate()

 

生成は DDIM により少ないステップで成される

for _ in range(10):
    seed = generator.seed()
    print(f'Seed = {seed}')
    generator.manual_seed(seed)
    output = audio_diffusion(generator=generator)
    image = output.images[0]
    audio = output.audios[0, 0]
    display(image)
    display(Audio(audio, rate=sample_rate))
    loop = loop_it(audio, sample_rate)
    if loop is not None:
        display(Audio(loop, rate=sample_rate))
    else:
        print("Unable to determine loop points")

パラメータ eta は variance を制御します :

  • 0 – DDIM (決定論的)
  • 1 – DDPM (ノイズ除去拡散確率モデル)
output = audio_diffusion(steps=1000, generator=generator, eta=1)
image = output.images[0]
audio = output.audios[0, 0]
display(image)
display(Audio(audio, rate=sample_rate))

 

DDIM はエンコーダとして使用可能…

# Doesn't have to be an audio from the train dataset, this is just for convenience
ds = load_dataset('teticio/audio-diffusion-256')
image = ds['train'][264]['image']
display(Audio(mel.image_to_audio(image), rate=sample_rate))
noise = audio_diffusion.encode([image])
# Reconstruct original audio from noise
output = audio_diffusion(noise=noise, generator=generator)
image = output.images[0]
audio = output.audios[0, 0]
display(Audio(audio, rate=sample_rate))

 

… あるいは音声間を補完するために

image2 = ds['train'][15978]['image']
display(Audio(mel.image_to_audio(image2), rate=sample_rate))
noise2 = audio_diffusion.encode([image2])
alpha = 0.5  #@param {type:"slider", min:0, max:1, step:0.1}
output = audio_diffusion(
    noise=audio_diffusion.slerp(noise, noise2, alpha),
    generator=generator)
audio = output.audios[0, 0]
display(Audio(mel.image_to_audio(image), rate=sample_rate))
display(Audio(mel.image_to_audio(image2), rate=sample_rate))
display(Audio(audio, rate=sample_rate))

 

潜在的音声拡散

画像をピクセル空間で直接ノイズ除去する代わりに、事前訓練済み VAE (変分オートエンコーダ) の潜在的空間で処理するとができます。これは訓練して推論を実行するのが遙かに高速になりますが、今はエンコード / デコード: メルスペクトログラム, VAE とノイズ除去で 3 つのステージがありますので、品質は劣化します。

model_id = "teticio/latent-audio-diffusion-ddim-256"  #@param ["teticio/latent-audio-diffusion-256", "teticio/latent-audio-diffusion-ddim-256"]
audio_diffusion = DiffusionPipeline.from_pretrained(model_id).to(device)
mel = audio_diffusion.mel
sample_rate = mel.get_sample_rate()
seed = 3412253600050855  #@param {type:"integer"}
generator.manual_seed(seed)
output = audio_diffusion(generator=generator)
image = output.images[0]
audio = output.audios[0, 0]
display(image)
display(Audio(audio, rate=sample_rate))
seed2 = 7016114633369557  #@param {type:"integer"}
generator.manual_seed(seed2)
output = audio_diffusion(generator=generator)
image2 = output.images[0]
audio2 = output.audios[0, 0]
display(image2)
display(Audio(audio2, rate=sample_rate))

 

潜在的空間内の補完

VAE はスペクトログラムに対してよりコンパクトで低次元な表現を強いるので、潜在的空間の補完は音声の意味のある組み合わせに繋がります。前のセクションの (決定論的) DDIM との組み合わせで、このモデルは低次元空間へのエンコーダ / デコーダとして利用できます。

generator.manual_seed(seed)
latents = torch.randn(
    (1, audio_diffusion.unet.in_channels, audio_diffusion.unet.sample_size[0],
     audio_diffusion.unet.sample_size[1]),
    generator=generator, device=device)
latents.shape
generator.manual_seed(seed2)
latents2 = torch.randn(
    (1, audio_diffusion.unet.in_channels, audio_diffusion.unet.sample_size[0],
     audio_diffusion.unet.sample_size[1]),
    generator=generator,
    device=device)
latents2.shape
alpha = 0.5  #@param {type:"slider", min:0, max:1, step:0.1}
output = audio_diffusion(
    noise=audio_diffusion.slerp(latents, latents2, alpha),
    generator=generator)
audio3 = output.audios[0, 0]
display(Audio(audio, rate=mel.get_sample_rate()))
display(Audio(audio2, rate=mel.get_sample_rate()))
display(Audio(audio3, rate=sample_rate))

 

以上