1. CNN(Convolutional Neural Network)
๐ CNN
CNN์ ์์ ์ฒ๋ฆฌ ๋ถ์ผ์์ ์์๋ ์ ๊ฒฝ๋ง ๊ตฌ์กฐ์ด์ง๋ง, ์ต๊ทผ์๋ ์์ฑ ๋ฐ์ดํฐ๋ฅผ 2์ฐจ์ ํํ(์คํํธ๋ก๊ทธ๋จ, MFCC ๋ฑ)๋ก ๋ณํํ์ฌ ์ ๋ ฅํจ์ผ๋ก์จ ์์ฑ์ธ์์๋ ๋๋ฆฌ ํ์ฉ๋๊ณ ์๋ค.
์ฃผ๋ก ์ํฅ ๋ชจ๋ธ์ ์ ์ฒ๋ฆฌ ๊ณ์ธต ๋๋ ์ ์ฒด ๋ชจ๋ธ๋ก ์ฌ์ฉ๋๋ฉฐ, ๊ณต๊ฐ์ ํน์ง(์ฃผํ์ ๊ฐ ๊ด๊ณ, ์๊ฐ์ ๋ณํ)์ ํจ๊ณผ์ ์ผ๋ก ์ถ์ถํ๋ค.
โ๏ธ Convolution์ ๊ฐ๋
convolution์ ์ ํธ ์ฒ๋ฆฌ์์ ํน์ ํจํด์ด๋ ์ฃผํ์ ๋์ญ์ ์ถ์ถํ๊ฑฐ๋, ๋ ธ์ด์ฆ๋ฅผ ์ ๊ฑฐํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ํต์ฌ ์ฐ์ฐ์ด๋ค. ์ ๋ ฅ ์ ํธ์ ๋ํด, ํํฐ ๋๋ ์ปค๋์ ์ ์ฉํ์ฌ ์๋ก์ด ์ถ๋ ฅ์ ์์ฑํ๋ค.
โป ํ์ดํธ ๋ ธ์ด์ฆ ์ ํธ์ ๋ํด 3๊ฐ์ ๋์ญํต๊ณผ FIR ํํฐ๋ฅผ ์ ์ฉํ ํ, ์๊ฐ ์์ญ๊ณผ ์ฃผํ์ ์์ญ์์ ํํฐ๋ง ๊ฒฐ๊ณผ๋ฅผ ์๊ฐํ
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import firwin, lfilter
import librosa
import librosa.display
# 1. ์
๋ ฅ ์ ํธ ์์ฑ (ํ์ดํธ ๋
ธ์ด์ฆ) : 1์ด ๋์ 16,000๊ฐ์ ๋๋ค ์ํ
# ํ์ดํธ ๋
ธ์ด์ฆ๋ ๋ชจ๋ ์ฃผํ์์ ๋์ผํ ์ธ๊ธฐ๋ฅผ ๊ฐ์ง๋ ์ ํธ
sr = 16000 # ์ํ๋ง ์ฃผํ์
T = 1.0 # 1์ด
x = np.random.randn(int(sr * T))
# 2. FIR ๋์ญํต๊ณผ ํํฐ ์ค๊ณ ํจ์
# numtaps=101์ ํํฐ ๊ณ์ ์๋ก ํด์๋ก ์ ๋ช
ํ๊ณ ์์ ์ ์ด๋ค
def design_bandpass_filter(center_hz, sr, width=100, numtaps=101):
nyq = sr / 2
low = max((center_hz - width) / nyq, 1e-4)
high = min((center_hz + width) / nyq, 0.9999)
if low >= high:
raise ValueError(f"์๋ชป๋ ์ฃผํ์ ๋ฒ์: low={low*nyq}, high={high*nyq}")
return firwin(numtaps, [low, high], pass_zero=False)
# 3. ํํฐ ๊ณ์ ์์ฑ(3๊ฐ์ ์ค์ฌ ์ฃผํ์ ํํฐ: 100Hz, 1kHz, 5kHz)
filters = {
"100Hz": design_bandpass_filter(100, sr),
"1000Hz": design_bandpass_filter(1000, sr),
"5000Hz": design_bandpass_filter(5000, sr)
}
# 4. ํํฐ ์ ์ฉ(๊ฐ ํํฐ๋ฅผ x์ ์ ์ฉํด์ ์ถ๋ ฅ ์ ํธ ์์ฑ)
# ์ถ๋ ฅ: ์ค์ฌ ์ฃผํ์ ๋์ญ๋ง ํต๊ณผ์ํจ ์ ํธ
outputs = {k: lfilter(h, [1], x) for k, h in filters.items()}
# 5. ์๊ฐํ (์๊ฐ + ์ฃผํ์)
plt.figure(figsize=(14, 10))
for i, (label, y) in enumerate(outputs.items()):
# ์๊ฐ ์์ญ
plt.subplot(len(filters), 2, 2*i + 1)
librosa.display.waveshow(y, sr=sr)
plt.title(f"{label} - Time Domain")
plt.xlabel("Time (s)")
plt.ylabel("Amplitude")
# ์ฃผํ์ ์์ญ (FFT)
Y = np.abs(np.fft.rfft(y))
freqs = np.fft.rfftfreq(len(y), d=1/sr)
plt.subplot(len(filters), 2, 2*i + 2)
plt.plot(freqs, Y)
plt.title(f"{label} - Frequency Spectrum")
plt.xlabel("Frequency (Hz)")
plt.ylabel("Magnitude")
plt.xlim(0, 8000) # Nyquist ์ ํ
plt.tight_layout()
plt.show()
- 100Hz ํํฐ ๊ฒฐ๊ณผ:
- ํํ์ด ๋งค์ฐ ์ฒ์ฒํ ์ง๋
- ์คํํธ๋ผ์ ์ ์ญ๋์๋ง ์ง์ค๋์ด ์์
- 1000Hz ํํฐ:
- ํํ์ด ์ค๊ฐ ์ ๋๋ก ์ง๋
- ์คํํธ๋ผ์ 1kHz ๊ทผ์ฒ์ ๋ด์ฐ๋ฆฌ์ ์์
- 5000Hz ํํฐ:
- ํํ์ด ๋งค์ฐ ๋น ๋ฅด๊ฒ ์ง๋(์ธ๋ฐํ ์ง๋)
- ์คํํธ๋ผ์ 5kHz ๋์ญ๋ง ๊ฐ์กฐ๋จ
2. RNN(Recurrent Neural Network)
๐ RNN
RNN์ ์์ฐจ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ํนํ๋ ์ธ๊ณต์ ๊ฒฝ๋ง ๊ตฌ์กฐ์ด๋ค. ์์ฐ์ด ์ฒ๋ฆฌ, ์์ฑ ์ธ์, ์๊ณ์ด ์์ธก ๋ฑ์ ์ฃผ๋ก ์ฌ์ฉ๋๋ค.
๊ธฐ์กด์ ์ ๊ฒฝ๋ง์ ์ ๋ ฅ ๊ฐ์ ์์๋ฅผ ๊ณ ๋ คํ์ง ์์ง๋ง, RNN์ ์ด์ ์์ ์ ์ถ๋ ฅ์ ๋ค์ ์์ ์ ์ ๋ ฅ์ ์ฌ๊ท์ ์ผ๋ก ์ ๋ฌํจ์ผ๋ก์จ ์๊ฐ์ ์ธ ๋งฅ๋ฝ(๊ธฐ์ต)์ ๋ฐ์ํ ์ ์๋ค.
- ์ ๋ ฅ ๋ฒกํฐ๊ฐ ์๋์ธต์ ๋ค์ด๊ฐ
- ์๋์ธต์ผ๋ก๋ถํฐ ์ถ๋ ฅ ๋ฒกํฐ๊ฐ ์์ฑ๋จ
- ์๋์ธต์์ ๋์ ๋ค์ ์๋์ธต์ผ๋ก ์ ๋ ฅ๋จ
๐ GSC ๋ฐ์ดํฐ์ ์ ์ผ๋ถ ์ซ์ ์์ฑ์ ๋ฌด์์๋ก ์ ํํด 3~5๊ฐ๋ฅผ ์ด์ด ๋ถ์ฌ ํ๋์ ์ฐ์ ์์ฑ์ ๋ง๋ค๊ณ , ํด๋น ์์ฑ์ MFCC๋ก ๋ณํํ์ฌ RNN ๋ชจ๋ธ์ ํ์ตํ ๋ค, ๋ฌธ์ ๋จ์ ์์ธก์ ์ํํ๋ ์์ฑ ์ธ์ ๋ชจ๋ธ ๊ตฌํ
โป ๋ฐ์ดํฐ ๋ค์ด๋ก๋
!mkdir -p gsc
%cd gsc
!wget -q https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.02.tar.gz
!tar -xf speech_commands_v0.02.tar.gz
%cd ..
โป ์ด์ด๋ถ์ธ ์์ฑ ์ํ ์์ฑ
import os
import random
import librosa
import numpy as np
import soundfile as sf
digit_words = ['zero','one','two','three','four','five','six','seven','eight','nine']
src_root = "gsc"
# ์ด์ด๋ถ์ธ ์ฐ์ ์์ฑ ์ํ ์์ฑ
concat_dir = "gsc_concat"
os.makedirs(concat_dir, exist_ok=True)
samples = []
for i in range(200):
digits = random.choices(digit_words, k=random.randint(3, 5)) # ex: ['one','six','nine']
wavs = []
for word in digits:
folder = os.path.join(src_root, word)
files = [f for f in os.listdir(folder) if f.endswith(".wav")]
path = os.path.join(folder, random.choice(files))
y, _ = librosa.load(path, sr=16000)
wavs.append(y)
full = np.concatenate(wavs) # ์ฌ๋ฌ ๋จ์ด์ ์์ฑ ๋ฐ์ดํฐ๋ฅผ ์ฐ๊ฒฐ
out_path = os.path.join(concat_dir, f"sample_{i}.wav")
sf.write(out_path, full, 16000)
samples.append((out_path, " ".join(digits)))
print("์ํ ์์ฑ ์๋ฃ:", len(samples))
โป ๋ฌธ์ ์ธ๋ฑ์ค ๋งคํ ๋ฐ ์ ์ฒ๋ฆฌ
char_vocab = sorted(list("abcdefghijklmnopqrstuvwxyz '"))
char2idx = {ch: i for i, ch in enumerate(char_vocab)}
PAD_IDX = len(char2idx) # ํจ๋ฉ์ฉ ์ธ๋ฑ์ค
idx2char = {i: ch for ch, i in char2idx.items()}
# ๋ฌธ์ ์ํ์ค ์ ๋ต์ ์ธ๋ฑ์ค๋ก ๋ณํ ํ ๊ธธ์ด 100์ ๋ง์ถฐ ํจ๋ฉ
def text_to_seq(text, max_len=100):
seq = [char2idx[c] for c in text if c in char2idx]
seq += [PAD_IDX] * (max_len - len(seq))
return seq[:max_len]
# MFCC ํน์ง ์ถ์ถ
def extract_mfcc(path, max_len=100):
y, sr = librosa.load(path, sr=16000)
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13) # [13, T]
if mfcc.shape[1] < max_len:
pad = np.zeros((13, max_len - mfcc.shape[1]))
mfcc = np.concatenate((mfcc, pad), axis=1)
return mfcc[:, :max_len].T # [T, F] # [T, 13]
X, y = [], []
for path, label in samples:
X.append(extract_mfcc(path))
y.append(text_to_seq(label))
# ํ์ต ๋ฐ์ดํฐ ๊ตฌ์ฑ
X = np.array(X) # [N, T=100, 13]
y = np.array(y) # [N, 100]
โป ํ์ต์ฉ ๋ฐ์ดํฐ๋ก ๋ณํ
from sklearn.model_selection import train_test_split
import torch
from torch.utils.data import DataLoader, TensorDataset
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
X_train = torch.tensor(X_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.long)
y_test = torch.tensor(y_test, dtype=torch.long)
train_loader = DataLoader(TensorDataset(X_train, y_train), batch_size=32, shuffle=True)
โป RNN ๋ชจ๋ธ ์ ์
import torch.nn as nn
class RNNModel(nn.Module):
def __init__(self, input_dim=13, hidden_dim=128, output_dim=len(char2idx)+1):
super().__init__()
self.rnn = nn.RNN(input_dim, hidden_dim, batch_first=True) # [B, T, 13] -> [B, T, H]
self.fc = nn.Linear(hidden_dim, output_dim) # ๊ฐ ์๊ฐ๋ง๋ค ๋ฌธ์ ๋ถํฌ ์์ธก
def forward(self, x):
out, _ = self.rnn(x) # [B, T, H]
out = self.fc(out) # [B, T, V]
return out
def backward(self, loss):
loss.backward()
โป ๋ชจ๋ธ ํ์ต ( epoch = 1000)
import matplotlib.pyplot as plt
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = RNNModel().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss(ignore_index=PAD_IDX) # ์์ค ๊ณ์ฐ
loss_history = [] # ์์ค ์ ์ฅ์ฉ ๋ฆฌ์คํธ
for epoch in range(1000):
model.train()
total_loss = 0
for xb, yb in train_loader:
xb, yb = xb.to(device), yb.to(device)
pred = model(xb) # [B, T, V]
pred = pred.view(-1, pred.shape[-1])
yb = yb.view(-1) # ์ ์ฒด ์ํ์ค๋ฅผ ํ ๋ฒ์ ๊ณ์ฐ
loss = criterion(pred, yb)
model.backward(loss) # ์์ค ๊ณ์ฐ ํ backpropagation ์คํ
optimizer.step()
optimizer.zero_grad() # ํ๋ผ๋ฏธํฐ ์
๋ฐ์ดํธ ๋ฐ ์ด๊ธฐํ
total_loss += loss.item()
avg_loss = total_loss / len(train_loader)
loss_history.append(avg_loss)
print(f"Epoch {epoch+1}: Loss={avg_loss:.4f}")
# ๋ชจ๋ธ ์ ์ฅ
torch.save(model.state_dict(), "rnn_speech_model.pth")
print("๋ชจ๋ธ ์ ์ฅ ์๋ฃ: rnn_speech_model.pth")
# ์์ค ํจ์ ๊ทธ๋ํ ์ถ๋ ฅ
plt.figure(figsize=(10, 4))
plt.plot(loss_history, label="Train Loss")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title("Training Loss over Epochs")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
โป ์ถ๋ก ๋ฐ ์ค๋์ค ์ฌ์ํจ์ ์ ์
from IPython.display import Audio, display
def load_model_for_inference(model_path="rnn_speech_model.pth"):
model = RNNModel().to(device)
model.load_state_dict(torch.load(model_path, map_location=device))
model.eval()
return model
def infer_and_play_audio(sample_path, label_text=None, model_path="rnn_speech_model.pth"):
# ์ค๋์ค ์ฌ์
print(f"[์์ฑ ์ฌ์]: {sample_path}")
display(Audio(sample_path))
# ๋ชจ๋ธ ๋ก๋
model = load_model_for_inference(model_path)
# ์
๋ ฅ ์ ์ฒ๋ฆฌ
mfcc = extract_mfcc(sample_path)
x = torch.tensor(mfcc, dtype=torch.float32).unsqueeze(0).to(device) # [1, T, F]
# ์ถ๋ก
with torch.no_grad():
output = model(x) # [1, T, V]
pred_idx = output.argmax(2)[0] # ๊ฐ์ฅ ํ๋ฅ ๋์ ๋ฌธ์ ์ธ๋ฑ์ค
pred_text = ''.join([idx2char[i.item()] for i in pred_idx if i.item() != PAD_IDX])
# ์ถ๋ ฅ
if label_text:
print("[์ ๋ต ๋ฌธ์ฅ]:", label_text)
print("[๋ชจ๋ธ ์์ธก]:", pred_text)
โป epoch = 10000์ผ๋ก ํ์ต ์
3. LSTM (Long Short-Term Memory)
๐ LSTM
์์ฐจ์ ์ธ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋, ๊ณผ๊ฑฐ ์ ๋ณด๋ฅผ ์ฅ๊ธฐ์ ์ผ๋ก ๋ณด์กดํ๋ฉด์ ํ์ํ ์ ๋ณด๋ง ํ์ต์ ๋ฐ์ํ ์ ์๋๋ก ๊ณ ์๋ RNN ๊ณ์ด์ ์ ๊ฒฝ๋ง์ด๋ค. ์ผ๋ฐ์ ์ธ RNN์ด ์ํ์ค ๊ธธ์ด๊ฐ ๊ธธ์ด์ง์๋ก ๊ธฐ์ธ๊ธฐ ์์ค ๋ฌธ์ ๋ก ์ธํด ๊ณผ๊ฑฐ ์ ๋ณด๋ฅผ ์๋ ๋ฌธ์ ๋ฅผ ๊ฐ์ ํ ๊ตฌ์กฐ์ด๋ค.
LSTM์ ์๊ณ์ด ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋, ๋งค ์๊ฐ ๋จ๊ณ์์ ์๋์ ๊ฐ์ ์ธ ๊ฐ์ง ์ ๋ณด ํ๋ฆ์ ์ฒ๋ฆฌํ๋ค.
- ์ ์ํ(cell state): ์ฅ๊ธฐ ๊ธฐ์ต์ ์ ๋ฌํ๋ ๋ฒกํฐ
- ์๋ ์ํ(hidden state): ๋ค์ ๊ณ์ธต ๋๋ ๋ค์ ์๊ฐ ๋จ๊ณ๋ก ์ถ๋ ฅํ ์ ๋ณด
- ๊ฒ์ดํธ๋ค: ์ ๋ณด๋ฅผ ์ผ๋ง๋ ์ ์งํ ์ง, ๋ฒ๋ฆด์ง ์ถ๋ ฅํ ์ง๋ฅผ ๊ฒฐ์
- ์๋ ๋ฐฉ์
- ์ ๋ ฅ ๊ฒ์ดํธ๋ ํ์ฌ ์ ๋ ฅ์์ ์ ์ํ์ ์ผ๋ง๋ ๋ฐ์ํ ์ง๋ฅผ ๊ฒฐ์
- ๋ง๊ฐ ๊ฒ์ดํธ๋ ์ด์ ์ ์ํ์ ์ ๋ณด๋ฅผ ์ผ๋ง๋ ์์์ง๋ฅผ ๊ฒฐ์
- ์ถ๋ ฅ ๊ฒ์ดํธ๋ ์ต์ข ์๋ ์ํ๋ฅผ ์ผ๋ง๋ ๋ด๋ณด๋ผ์ง๋ฅผ ์กฐ์
- ์ด๋ฌํ ๊ฒ์ดํธ ์ฐ์ฐ์ ์๊ทธ๋ชจ์ด๋ ํจ์๋ก 0~1 ๊ฐ์ ์ถ๋ ฅํ์ฌ ์ ๋ณด ํ๋ฆ์ ์กฐ์
import torch.nn as nn
class LSTMModel(nn.Module):
# ๋ชจ๋ธ ๊ตฌ์กฐ ์ ์
def __init__(self, input_dim=13, hidden_dim=128, output_dim=len(char2idx)+1):
super().__init__()
self.lstm = nn.LSTM(input_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, output_dim)
# ์์ธก ๊ณผ์ ์ ์
def forward(self, x):
out, _ = self.lstm(x)
out = self.fc(out)
return out
# ์์ค์ ๋ํ ์ญ์ ํ ์ํ
def backward(self, loss):
loss.backward()
'์์ฑ์ฒ๋ฆฌ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[๋ฅ๋ฌ๋]ASR ์์คํ , CTC, Transformer (0) | 2025.07.19 |
---|---|
[๋ฅ๋ฌ๋]Self-Attention, Encoder-Decoder ๊ตฌ์กฐ (1) | 2025.07.16 |
[๋ฅ๋ฌ๋]ANN,SLP, MLP ๊ธฐ๋ณธ ๊ฐ๋ ๋ฐ ๊ตฌ์กฐ (4) | 2025.07.12 |
[์์ฑ ์ฒ๋ฆฌ]์์ฑ์ ํธ ์ ์ฒ๋ฆฌ, Librosa ํ์ฉ ๋ฐ ํ๋ก์ ํธ ๋ฐ์ดํฐ (0) | 2025.07.12 |
[์์ฑ ์ฒ๋ฆฌ]MFCC, Mel-Spectrogram, STFT (4) | 2025.07.09 |