- Authors
- Name
- はじめに
- Part 1: オーディオの基礎
- Part 2: コア変換(Transforms)
- Part 3: オーディオAugmentation
- Part 4: 事前学習済みモデル
- Part 5: オーディオエフェクト
- Part 6: 実践プロジェクト
- 関連シリーズ&おすすめ記事

はじめに
torchaudioはPyTorchの公式オーディオ処理ライブラリです。オーディオI/O、スペクトログラム変換、事前学習済みモデル(Wav2Vec2、HuBERT、Whisper)、そしてリアルタイムストリーミングまでサポートしています。
pip install torch torchaudio
Part 1: オーディオの基礎
オーディオの読み込みと保存
import torch
import torchaudio
# オーディオ読み込み
waveform, sample_rate = torchaudio.load("speech.wav")
print(f"Shape: {waveform.shape}") # [channels, samples]
print(f"Sample Rate: {sample_rate}") # 16000
print(f"Duration: {waveform.shape[1] / sample_rate:.2f}s")
# チャンネル: モノラル(1) vs ステレオ(2)
if waveform.shape[0] == 2:
mono = waveform.mean(dim=0, keepdim=True) # ステレオ → モノラル
# リサンプリング (44100Hz → 16000Hz)
resampler = torchaudio.transforms.Resample(
orig_freq=44100, new_freq=16000
)
waveform_16k = resampler(waveform)
# 保存
torchaudio.save("output.wav", waveform_16k, 16000)
# サポートフォーマット: wav, flac, mp3, ogg, opus, sphere
# バックエンド: sox, soundfile, ffmpeg
print(torchaudio.list_audio_backends())
オーディオの可視化
import matplotlib.pyplot as plt
# 波形(Waveform)
fig, axes = plt.subplots(3, 1, figsize=(12, 8))
# 1. 時間領域(波形)
time_axis = torch.arange(0, waveform.shape[1]) / sample_rate
axes[0].plot(time_axis, waveform[0])
axes[0].set_title("Waveform")
axes[0].set_xlabel("Time (s)")
axes[0].set_ylabel("Amplitude")
# 2. スペクトログラム
spectrogram = torchaudio.transforms.Spectrogram(n_fft=1024)(waveform)
axes[1].imshow(
spectrogram[0].log2().numpy(),
aspect='auto', origin='lower', cmap='magma'
)
axes[1].set_title("Spectrogram")
# 3. Melスペクトログラム
mel_spec = torchaudio.transforms.MelSpectrogram(
sample_rate=sample_rate, n_fft=1024, n_mels=80
)(waveform)
axes[2].imshow(
mel_spec[0].log2().numpy(),
aspect='auto', origin='lower', cmap='magma'
)
axes[2].set_title("Mel Spectrogram")
plt.tight_layout()
plt.savefig("audio_analysis.png", dpi=150)
Part 2: コア変換(Transforms)
スペクトログラム系列
# STFT (Short-Time Fourier Transform)
# 時間 → 時間+周波数領域への変換
spectrogram_transform = torchaudio.transforms.Spectrogram(
n_fft=1024, # FFTウィンドウサイズ(周波数解像度)
hop_length=256, # ウィンドウ移動間隔(時間解像度)
win_length=1024, # ウィンドウ長
power=2.0, # 2.0=パワー、1.0=振幅
)
spec = spectrogram_transform(waveform)
# shape: [channels, n_freq_bins, time_frames]
# n_freq_bins = n_fft // 2 + 1 = 513
n_fftとhop_lengthのトレードオフ:
n_fft ↑ → 周波数解像度 ↑、時間解像度 ↓
n_fft ↓ → 周波数解像度 ↓、時間解像度 ↑
一般的な設定:
├── 音声: n_fft=400~512, hop=160 (16kHz基準)
├── 音楽: n_fft=2048, hop=512 (44.1kHz基準)
└── 汎用: n_fft=1024, hop=256
Melスペクトログラム — なぜMelなのか?
# 人間の耳は低周波に敏感で、高周波には鈍感
# Melスケール = 人間の聴覚を反映した周波数スケール
mel_transform = torchaudio.transforms.MelSpectrogram(
sample_rate=16000,
n_fft=1024,
hop_length=256,
n_mels=80, # Melフィルタ数(通常40~128)
f_min=0, # 最小周波数
f_max=8000, # 最大周波数(ナイキスト)
)
mel_spec = mel_transform(waveform)
# shape: [1, 80, time_frames]
# dBスケール変換(対数圧縮)
amplitude_to_db = torchaudio.transforms.AmplitudeToDB(stype='power', top_db=80)
mel_spec_db = amplitude_to_db(mel_spec)
Mel周波数変換公式:
mel = 2595 × log10(1 + freq / 700)
周波数 → Mel:
100 Hz → 150 mel (低周波: 密)
1000 Hz → 1000 mel
4000 Hz → 2146 mel
8000 Hz → 2840 mel (高周波: 疎)
→ 低周波は細かく、高周波はまとめて分析
→ 人間の聴覚に近い表現!
MFCC (Mel-Frequency Cepstral Coefficients)
# MFCC = Melスペクトログラム + DCT(離散コサイン変換)
# 音声の「形状」を表す核心的な特徴量
mfcc_transform = torchaudio.transforms.MFCC(
sample_rate=16000,
n_mfcc=13, # MFCC係数の数(通常13~40)
melkwargs={
'n_fft': 1024,
'n_mels': 80,
'hop_length': 256,
}
)
mfcc = mfcc_transform(waveform)
# shape: [1, 13, time_frames]
# Delta(1次微分)+ Delta-Delta(2次微分)
# → 音声の変化率情報を追加
delta = torchaudio.functional.compute_deltas(mfcc)
delta_delta = torchaudio.functional.compute_deltas(delta)
# 最終特徴量: [MFCC, Delta, Delta-Delta]を連結
features = torch.cat([mfcc, delta, delta_delta], dim=1)
# shape: [1, 39, time_frames]
どこで使われるか?
├── MFCC: 従来の音声認識(HMM-GMM)、話者認識
├── Mel Spectrogram: ディープラーニング音声認識(Wav2Vec2, Whisper)
├── Spectrogram: 音楽分析、環境音分類
└── Raw Waveform: End-to-endモデル(最新トレンド)
Part 3: オーディオAugmentation
# 時間マスキング(SpecAugment)
time_masking = torchaudio.transforms.TimeMasking(
time_mask_param=30 # 最大30フレームをマスキング
)
# 周波数マスキング(SpecAugment)
freq_masking = torchaudio.transforms.FrequencyMasking(
freq_mask_param=15 # 最大15チャンネルをマスキング
)
# SpecAugment(音声認識精度が大幅に向上!)
augmented_spec = time_masking(freq_masking(mel_spec))
# タイムストレッチ
time_stretch = torchaudio.transforms.TimeStretch()
stretched = time_stretch(complex_spec, overriding_rate=1.2) # 20%速く
# ピッチシフト
pitch_shift = torchaudio.transforms.PitchShift(
sample_rate=16000, n_steps=4 # 4半音上げる
)
shifted = pitch_shift(waveform)
# ノイズ追加
def add_noise(waveform, snr_db=10):
"""SNR dB基準でホワイトノイズを追加"""
noise = torch.randn_like(waveform)
signal_power = waveform.norm(p=2)
noise_power = noise.norm(p=2)
snr = 10 ** (snr_db / 20)
scale = signal_power / (snr * noise_power)
return waveform + scale * noise
Part 4: 事前学習済みモデル
Wav2Vec 2.0(音声認識)
import torchaudio
from torchaudio.pipelines import WAV2VEC2_ASR_BASE_960H
# パイプライン読み込み
bundle = WAV2VEC2_ASR_BASE_960H
model = bundle.get_model()
labels = bundle.get_labels() # トークンリスト
# 推論
waveform, sr = torchaudio.load("speech.wav")
if sr != bundle.sample_rate:
waveform = torchaudio.transforms.Resample(sr, bundle.sample_rate)(waveform)
with torch.no_grad():
emissions, _ = model(waveform)
# CTCデコーディング(Greedy)
def greedy_decode(emissions, labels):
indices = torch.argmax(emissions, dim=-1)[0]
tokens = []
prev = -1
for idx in indices:
if idx != prev and idx != 0: # 0 = blank
tokens.append(labels[idx])
prev = idx
return "".join(tokens).replace("|", " ").strip()
text = greedy_decode(emissions, labels)
print(f"認識結果: {text}")
HuBERT(自己教師あり音声表現)
from torchaudio.pipelines import HUBERT_BASE
bundle = HUBERT_BASE
model = bundle.get_model()
with torch.no_grad():
features, _ = model(waveform)
# features: [1, time_frames, 768]
# → 音声の意味的表現ベクトル
# → 話者認識、感情分析、音声分類に活用
Forced Alignment(字幕同期)
# 音声とテキストの時間的アライメント!
# → 字幕生成、歌詞同期に必須
from torchaudio.pipelines import MMS_FA # Multilingual!
bundle = MMS_FA
model = bundle.get_model()
tokenizer = bundle.get_tokenizer()
aligner = bundle.get_aligner()
transcript = "こんにちは はじめまして"
tokens = tokenizer(transcript)
with torch.no_grad():
emissions, _ = model(waveform)
token_spans = aligner(emissions[0], tokens)
# 各トークンの開始/終了時間をフレーム単位で返す!
for span, token in zip(token_spans, transcript):
start_time = span.start * model.hop_length / sample_rate
end_time = span.end * model.hop_length / sample_rate
print(f" '{token}': {start_time:.3f}s ~ {end_time:.3f}s")
Part 5: オーディオエフェクト
# torchaudio.functional — GPU高速化オーディオ処理
import torchaudio.functional as F
# 音量調整
loud = F.gain(waveform, gain_db=6.0) # +6dB
quiet = F.gain(waveform, gain_db=-6.0) # -6dB
# ハイパス/ローパスフィルタ
highpass = F.highpass_biquad(waveform, sample_rate, cutoff_freq=300)
lowpass = F.lowpass_biquad(waveform, sample_rate, cutoff_freq=3000)
# イコライザ
eq = F.equalizer_biquad(
waveform, sample_rate,
center_freq=1000, # 1kHz付近
gain=5.0, # +5dBブースト
Q=0.707
)
# リバーブ(残響)
rir, _ = torchaudio.load("room_impulse_response.wav") # RIRファイル
reverb = F.fftconvolve(waveform, rir)
# フェードイン/アウト
fade = torchaudio.transforms.Fade(
fade_in_len=sample_rate, # 1秒フェードイン
fade_out_len=sample_rate * 3 # 3秒フェードアウト
)
faded = fade(waveform)
# VAD (Voice Activity Detection)
vad = torchaudio.transforms.Vad(sample_rate=16000)
speech_only = vad(waveform) # 無音区間を除去
Part 6: 実践プロジェクト
環境音分類(Audio Classification)
import torch.nn as nn
class AudioClassifier(nn.Module):
def __init__(self, n_classes=10):
super().__init__()
self.mel = torchaudio.transforms.MelSpectrogram(
sample_rate=16000, n_fft=1024, n_mels=64
)
self.db = torchaudio.transforms.AmplitudeToDB()
# Melスペクトログラムを「画像」のようにCNNに入力!
self.cnn = nn.Sequential(
nn.Conv2d(1, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(),
nn.AdaptiveAvgPool2d((1, 1)),
)
self.fc = nn.Linear(128, n_classes)
def forward(self, waveform):
# [B, 1, samples] → [B, 1, n_mels, time]
x = self.mel(waveform)
x = self.db(x)
x = self.cnn(x)
x = x.view(x.size(0), -1)
return self.fc(x)
# Melスペクトログラム = オーディオの「画像」
# → CNN(ResNet, EfficientNet)で分類可能!
リアルタイムストリーミング処理
from torchaudio.io import StreamReader
# マイク入力のリアルタイム処理
reader = StreamReader(src=":0", format="avfoundation") # macOS
reader.add_basic_audio_stream(
frames_per_chunk=16000, # 1秒単位
sample_rate=16000,
)
for (chunk,) in reader.stream():
# chunk: [1, 16000]
mel = mel_transform(chunk)
with torch.no_grad():
prediction = model(mel)
print(f"検出: {labels[prediction.argmax()]}")
クイズ — torchaudio(クリックして確認!)
Q1. Melスケールが必要な理由は? ||人間の聴覚は低周波に敏感で高周波には鈍感。Melスケールはこれを反映し、低周波は細かく、高周波はまとめて分析。ディープラーニングモデルに人間の聴覚特性を反映する。||
Q2. n_fftを大きくするとどの解像度が上がり、どの解像度が下がるか? ||n_fft ↑ → 周波数解像度 ↑(細かい周波数の区別が可能)、時間解像度 ↓(時間変化の把握が困難に)。不確定性原理に類似したトレードオフ。||
Q3. SpecAugmentの2種類のマスキングとは? ||Time Masking: 時間軸で連続フレームを0でマスキング。Frequency Masking: 周波数軸で連続チャンネルを0でマスキング。データaugmentationとして音声認識精度を大幅に向上させる。||
Q4. MFCCとMel Spectrogramの違いとそれぞれの用途は? ||MFCC: Mel SpectrogramにDCTを適用して係数を抽出(13~40次元)。従来の音声認識、話者認識に使用。Mel Spectrogram: 周波数-時間の2D表現。ディープラーニングモデルに直接入力(最新トレンド)。||
Q5. Forced Alignmentの活用例は? ||音声とテキストの時間的アライメント。字幕生成(正確なタイミング)、歌詞同期(カラオケ)、発音評価(語学学習アプリ)。||
Q6. Wav2Vec 2.0のCTCデコーディングにおけるblankトークンの役割は? ||連続する同一トークンを区別し、出力のない時間区間を表現する。Greedyデコーディングではblank(index 0)と連続重複を除去して最終テキストを生成する。||
Q7. MelスペクトログラムをCNNに入力できる理由は? ||Melスペクトログラムは2D画像と同じ構造(周波数軸 × 時間軸)。1チャンネルのグレースケール画像として扱い、ResNet、EfficientNetなどの画像分類モデルをそのまま活用可能。||
関連シリーズ&おすすめ記事
- torchvision完全ガイド — 画像AI(姉妹編)
- AI のための数学完全ガイド — フーリエ変換、確率(オーディオ理解に必須)
- 自分だけのGPTを作る — Wav2Vec2の基盤であるTransformer
GitHub
- torchaudio公式
- Whisper — OpenAI音声認識
- ESPnet — 総合音声処理ツールキット