
Oja’s Rule을 이용한 Hebbian Feature Learning의 가장 대표적인 예제는 “입력 데이터의 첫 번째 주성분(Principal Component)을 뉴런이 스스로 학습하는 것”이다.
즉, 비지도 학습으로 가장 분산이 큰 방향(feature axis)을 찾는 단일 뉴런 PCA 예제이다.
시뮬레이션 코드의 내용은 다음과 같다.
데이터 수: 1000개
100개 학습마다 weight 방향 저장
화살표가 순차적으로 회전하며 PC1 방향으로 수렴
현재 몇 개 샘플을 학습했는지 텍스트 표시
반복 재생되는 matplotlib animation
Oja’s Rule이 Hebbian feature 를 점점 안정된 주성분 방향으로 학습하는 과정을 직관적으로 확인할 수 있다.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# =========================
# 1. 데이터 생성 (총 1000개)
# =========================
np.random.seed(42)
mean = [0, 0]
cov = [[3, 2],
[2, 1.5]]
X = np.random.multivariate_normal(mean, cov, 1000)
# =========================
# 2. Oja's Rule 초기화
# =========================
w = np.random.randn(2)
w = w / np.linalg.norm(w)
lr = 0.01
history = [w.copy()]
# =========================
# 3. 100개 단위로 학습 방향 저장
# =========================
for i, x in enumerate(X, start=1):
y = np.dot(w, x)
dw = lr * y * (x - y * w)
w += dw
w /= np.linalg.norm(w)
# 100개 단위로 화살표 기록
if i % 100 == 0:
history.append(w.copy())
history = np.array(history)
# =========================
# 4. 애니메이션 시각화
# =========================
fig, ax = plt.subplots(figsize=(8, 8))
scatter = ax.scatter([], [], alpha=0.25)
ax.set_xlim(X[:, 0].min() - 1, X[:, 0].max() + 1)
ax.set_ylim(X[:, 1].min() - 1, X[:, 1].max() + 1)
ax.set_aspect('equal')
ax.grid(True)
ax.set_title("Oja's Rule Feature Learning Animation")
arrow = [None]
text = ax.text(0.02, 0.95, '', transform=ax.transAxes, fontsize=12)
def update(frame):
global arrow
if arrow[0] is not None:
arrow[0].remove()
# 100개씩 점 추가
current_points = X[: max(1, frame * 100)]
scatter.set_offsets(current_points)
w = history[frame]
arrow[0] = ax.arrow(
0, 0,
w[0] * 4,
w[1] * 4,
width=0.05,
head_width=0.25,
length_includes_head=True
)
text.set_text(f"Samples shown: {min(frame * 100, len(X))}")
return scatter, arrow[0], text
ani = FuncAnimation(
fig,
update,
frames=len(history),
interval=700,
repeat=True
)
# =========================
# 5. GIF 저장
# =========================
ani.save("oja_rule_feature_learning.gif", writer="pillow", fps=1.5)
plt.show()