1. 패턴 인식에서 RBF 네트워크의 역할
패턴 인식은 이미지, 음성, 생체 정보 등 다양한 데이터에서 의미 있는 구조를 추출하고 분류하는 핵심적인 머신러닝 작업이다. 이 과정에서 RBF(Radial Basis Function) 신경망은 비선형 문제에 강한 일반화 능력과 빠른 수렴 속도 덕분에 주목받는 모델 중 하나다. 본 문서에서는 RBF 신경망의 작동 원리와 실제 패턴 인식 과제에 적용하는 방법을 설명하며, 파이썬 기반의 구현 예제를 통해 실무 적용 가능성을 탐구한다.
2. RBF 신경망의 구조와 동작 방식
RBF 네트워크는 세 개의 계층으로 구성된다: 입력층, 은닉층, 출력층.
- 입력층: 전처리된 특징 벡터를 수신한다.
- 은닉층: 각 노드는 중심점(center)을 가지며, 입력과 중심 간 거리에 따라 활성화 값을 계산한다. 가장 흔히 사용되는 활성화 함수는 가우시안 함수로 다음과 같다:
\( \phi(\mathbf{x}) = \exp\left(-\frac{\|\mathbf{x} - \mathbf{c}_i\|^2}{2\gamma_i^2}\right) \)
여기서 \( \mathbf{c}_i \)는 i번째 은닉 노드의 중심이며, \( \gamma_i \)는 확산 정도를 조절하는 스케일 파라미터다. - 출력층: 은닉층의 출력에 선형 가중치를 적용하여 최종 클래스 점수를 생성한다. 다중 클래스 분류에서는 소프트맥스 또는 argmax 연산을 통해 예측 라벨을 결정한다.
3. 구현 프레임워크 및 전처리
모델 성능은 데이터 전처리 단계에 크게 의존한다. 표준화, 정규화, 차원 축소 등의 기법은 학습 안정성과 정확도를 높이는 데 필수적이다. 아래 코드는 대표적인 전처리 절차를 보여준다.
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
# 샘플 데이터 로드 및 전처리
X_raw, y_raw = load_iris(return_X_y=True)
scaler = StandardScaler()
X_norm = scaler.fit_transform(X_raw)
X_train, X_val, y_train, y_val = train_test_split(X_norm, y_raw, test_size=0.25, stratify=y_raw)
4. 기본 RBF 모델 구현
다음은 K-평균 클러스터링 기반 중심 선택과 의사역행렬(pseudo-inverse)을 통한 가중치 학습을 수행하는 RBF 네트워크 클래스다.
class SimpleRBF:
def __init__(self, n_centers, gamma=1.0):
self.n_centers = n_centers
self.gamma = gamma
self.centers = None
self.W = None # 가중치 행렬
self.bias = None
def _gaussian(self, x, c):
return np.exp(-np.linalg.norm(x - c)**2 / (2 * self.gamma**2))
def _hidden_output(self, X):
H = np.zeros((X.shape[0], self.n_centers))
for idx, center in enumerate(self.centers):
dists = np.linalg.norm(X - center, axis=1)
H[:, idx] = np.exp(-dists**2 / (2 * self.gamma**2))
return H
def fit(self, X, y):
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=self.n_centers, n_init=10).fit(X)
self.centers = kmeans.cluster_centers_
H = self._hidden_output(X)
H_bias = np.column_stack([H, np.ones(H.shape[0])])
# 원-핫 인코딩
n_classes = len(np.unique(y))
Y_onehot = np.eye(n_classes)[y]
# 최소 자승 해법으로 파라미터 추정
params = np.linalg.pinv(H_bias) @ Y_onehot
self.W = params[:-1].T
self.bias = params[-1]
def predict(self, X):
H = self._hidden_output(X)
logits = H @ self.W.T + self.bias
return np.argmax(logits, axis=1)
5. 실제 데이터셋에서의 성능 평가
손글씨 숫자 인식 (MNIST)
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1, as_frame=False)
X_mnist, y_mnist = mnist.data / 255.0, mnist.target.astype(int)
X_tr, X_ts, y_tr, y_ts = train_test_split(X_mnist, y_mnist, test_size=0.2)
rbf_mnist = SimpleRBF(n_centers=150, gamma=1.0)
rbf_mnist.fit(X_tr[:5000], y_tr[:5000]) # 일부 데이터로 제한
preds = rbf_mnist.predict(X_ts[:1000])
acc = np.mean(preds == y_ts[:1000])
print(f"MNIST Test Accuracy: {acc:.3f}")
얼굴 인식 (Olivetti Faces)
from sklearn.datasets import fetch_olivetti_faces
faces = fetch_olivetti_faces(shuffle=True, random_state=42)
X_face, y_face = faces.data, faces.target
Xf_tr, Xf_ts, yf_tr, yf_ts = train_test_split(X_face, y_face, test_size=0.2)
rbf_face = SimpleRBF(n_centers=40, gamma=0.8)
rbf_face.fit(Xf_tr, yf_tr)
pred_face = rbf_face.predict(Xf_ts)
face_acc = np.mean(pred_face == yf_ts)
print(f"Face Recognition Accuracy: {face_acc:.3f}")
6. 고급 튜닝 및 개선 전략
중심 선택 방식 변경
K-평균 외에도 무작위 샘플링이나 밀도 기반 클러스터링을 사용할 수 있다.
class RandomCenterRBF(SimpleRBF):
def fit(self, X, y):
indices = np.random.choice(X.shape[0], self.n_centers, replace=False)
self.centers = X[indices]
H = self._hidden_output(X)
H_ext = np.column_stack([H, np.ones(H.shape[0])])
Y_enc = np.eye(len(np.unique(y)))[y]
theta = np.linalg.pinv(H_ext) @ Y_enc
self.W = theta[:-1].T
self.bias = theta[-1]
그라디언트 기반 학습 도입
대규모 데이터셋에서는 의사역행렬보다 경사하강법이 더 효율적일 수 있다.
class GradientRBF(SimpleRBF):
def __init__(self, n_centers, gamma=1.0, lr=0.001, epochs=300):
super().__init__(n_centers, gamma)
self.lr = lr
self.epochs = epochs
def fit(self, X, y):
self.centers = X[np.random.choice(X.shape[0], self.n_centers, replace=False)]
n_classes = len(np.unique(y))
self.W = np.random.randn(n_classes, self.n_centers) * 0.1
self.bias = np.zeros(n_classes)
Y_hot = np.eye(n_classes)[y]
for _ in range(self.epochs):
H = self._hidden_output(X)
logits = H @ self.W.T + self.bias
prob = softmax(logits)
error = Y_hot - prob
grad_W = self.lr * (error.T @ H)
grad_b = self.lr * np.sum(error, axis=0)
self.W += grad_W
self.bias += grad_b
def softmax(z):
z_stable = z - np.max(z, axis=1, keepdims=True)
exp_z = np.exp(z_stable)
return exp_z / np.sum(exp_z, axis=1, keepdims=True)
하이퍼파라미터 탐색
GridSearchCV를 활용해 최적의 중심 수와 감마 값을 탐색할 수 있다.
from sklearn.model_selection import ParameterGrid
best_score = 0
best_params = {}
for params in ParameterGrid({'n_centers': [50, 100], 'gamma': [0.5, 1.0]}):
model = SimpleRBF(**params)
model.fit(X_train, y_train)
acc = np.mean(model.predict(X_val) == y_val)
if acc > best_score:
best_score = acc
best_params = params
print("Best:", best_params, "Accuracy:", best_score)
7. 복잡한 시나리오에서의 확장
차원 축소와 결합 (PCA)
고차원 데이터에는 PCA를 사전 처리로 적용해 차원을 줄이고 노이즈를 제거할 수 있다.
from sklearn.decomposition import PCA
pca = PCA(n_components=64)
X_train_pca = pca.fit_transform(X_train)
X_val_pca = pca.transform(X_val)
rbf_pca = SimpleRBF(n_centers=80)
rbf_pca.fit(X_train_pca, y_train)
온라인 학습 지원
스트리밍 데이터 환경에서는 새로운 샘플이 들어올 때마다 모델을 업데이트해야 한다.
class OnlineRBF(SimpleRBF):
def partial_fit(self, x, y):
x = x.reshape(1, -1)
H = self._hidden_output(x)
logit = H @ self.W.T + self.bias
target = np.eye(self.W.shape[0])[y]
error = (target - logit) * self.lr
self.W += error.T @ H
self.bias += error.flatten()
다중 모달리티 데이터 처리
이미지와 텍스트 같은 복합 입력은 특징 벡터를 연결(concatenate)하여 통합 입력으로 사용할 수 있다.
# 예: 이미지 피처 + 텍스트 임베딩
image_features = extract_cnn_features(img_batch)
text_embeddings = get_sentence_embedding(text_batch)
combined = np.hstack([image_features, text_embeddings])
model = SimpleRBF(n_centers=100)
model.fit(combined, labels)
8. 해석 가능성 및 모델 진단
RBF 네트워크는 중심점이 실제 데이터 샘플과 유사한 의미를 갖기 때문에 해석이 비교적 용이하다. 중심 벡터를 시각화하거나 가중치 분포를 분석함으로써 어떤 패턴이 중요하게 작용했는지를 추론할 수 있다.
def inspect_model(rbf_model):
print("Number of Centers:", rbf_model.centers.shape[0])
print("Weight Matrix Shape:", rbf_model.W.shape)
# 중심 벡터 시각화 (예: 이미지 기반 태스크)
if rbf_model.centers.shape[1] == 4096:
show_as_image(rbf_model.centers[0])