Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 45 additions & 45 deletions models/bam.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,28 @@
# BAM 반복 안정화 함수 (soft sign 기반)
# ============================================

def bam_associative_recall(x, y, w_xy, w_yx, steps=5, temp=1.0):
def bam_associative_recall(input_pattern, output_pattern, weight_x_to_y, weight_y_to_x, steps=5, temperature=1.0):
"""
BAM 양방향 연상 반복

Args:
x: 입력 패턴 (batch, input_dim)
y: 출력 패턴 초기값 (batch, output_dim)
w_xy: X->Y 가중치 (input_dim, output_dim)
w_yx: Y->X 가중치 (output_dim, input_dim)
input_pattern: 입력 패턴 (batch, input_dim)
output_pattern: 출력 패턴 초기값 (batch, output_dim)
weight_x_to_y: X->Y 가중치 (input_dim, output_dim)
weight_y_to_x: Y->X 가중치 (output_dim, input_dim)
steps: 반복 횟수
temp: temperature parameter
temperature: temperature parameter

Returns:
안정화된 출력 패턴 y
"""
for _ in range(steps):
# X -> Y 방향
y = soft_sign(tf.matmul(x, w_xy), temp=temp)
output_pattern = soft_sign(tf.matmul(input_pattern, weight_x_to_y), temp=temperature)
# Y -> X 방향 (안정화)
x = soft_sign(tf.matmul(y, w_yx), temp=temp)
input_pattern = soft_sign(tf.matmul(output_pattern, weight_y_to_x), temp=temperature)

return y
return output_pattern


# ============================================
Expand All @@ -45,52 +45,52 @@ class BAMAssociativeLayer(layers.Layer):
- Temperature 조절 가능
"""

def __init__(self, input_dim, output_dim, steps=5, temp=1.0, name=None):
def __init__(self, input_dim, output_dim, steps=5, temperature=1.0, name=None):
super(BAMAssociativeLayer, self).__init__(name=name)
self.input_dim = input_dim
self.output_dim = output_dim
self.steps = steps
self.temp = temp
self.temperature = temperature

def build(self, input_shape):
# X -> Y 가중치
self.Wxy = self.add_weight(
self.weight_x_to_y = self.add_weight(
shape=(self.input_dim, self.output_dim),
initializer='glorot_uniform',
trainable=True,
name='Wxy'
name='weight_x_to_y'
)

# Y -> X 가중치 (feedback)
self.Wyx = self.add_weight(
self.weight_y_to_x = self.add_weight(
shape=(self.output_dim, self.input_dim),
initializer='glorot_uniform',
trainable=True,
name='Wyx'
name='weight_y_to_x'
)

super().build(input_shape)

def call(self, x, training=None):
def call(self, inputs, training=None):
# 초기 출력 패턴 (0으로 시작)
y0 = tf.zeros((tf.shape(x)[0], self.output_dim))
initial_output_pattern = tf.zeros((tf.shape(inputs)[0], self.output_dim))

# 반복 연상으로 안정화
y = bam_associative_recall(
x, y0, self.Wxy, self.Wyx,
output_pattern = bam_associative_recall(
inputs, initial_output_pattern, self.weight_x_to_y, self.weight_y_to_x,
steps=self.steps,
temp=self.temp
temperature=self.temperature
)

return y
return output_pattern

def get_config(self):
config = super().get_config()
config.update({
'input_dim': self.input_dim,
'output_dim': self.output_dim,
'steps': self.steps,
'temp': self.temp
'temperature': self.temperature
})
return config

Expand Down Expand Up @@ -125,52 +125,52 @@ def __init__(self, input_dim, output_dim, delta=0.2):
name='W'
)

def call(self, x):
def call(self, inputs):
"""정방향 연상: 입력 x에 대한 출력 패턴 y 반환"""
y_lin = tf.matmul(x, self.W)
linear_output = tf.matmul(inputs, self.W)
# Soft sign 사용 (더 안정적)
y = soft_sign(y_lin, temp=1.0)
return y
output_pattern = soft_sign(linear_output, temp=1.0)
return output_pattern

def train_hebbian(self, X, Y, epochs=1, eta=0.008):
def train_hebbian(self, input_data, target_output, epochs=1, learning_rate=0.008):
"""
Hebbian 학습 규칙에 따른 가중치 훈련

Args:
X: 학습 입력 배열
Y: 대응 학습 출력(정답) 배열
input_data: 학습 입력 배열
target_output: 대응 학습 출력(정답) 배열
epochs: 전체 패턴 반복 학습 횟수
eta: 학습률
learning_rate: 학습률
"""
X = tf.convert_to_tensor(X, dtype=tf.float32)
Y = tf.convert_to_tensor(Y, dtype=tf.float32)
n = tf.shape(X)[0]
input_data = tf.convert_to_tensor(input_data, dtype=tf.float32)
target_output = tf.convert_to_tensor(target_output, dtype=tf.float32)
num_samples = tf.shape(input_data)[0]

for ep in range(epochs):
for epoch in range(epochs):
# 패턴 순서를 섞어 학습
indices = tf.random.shuffle(tf.range(n))
for i in indices:
x0 = tf.expand_dims(X[i], 0) # (1, input_dim)
y0 = tf.expand_dims(Y[i], 0) # (1, output_dim)
shuffled_indices = tf.random.shuffle(tf.range(num_samples))
for sample_idx in shuffled_indices:
current_input = tf.expand_dims(input_data[sample_idx], 0) # (1, input_dim)
current_target = tf.expand_dims(target_output[sample_idx], 0) # (1, output_dim)

# 정방향으로 출력 패턴 계산
y_pred = self(x0)
predicted_output = self(current_input)

# 역방향으로 입력 패턴 회상
x_pred = soft_sign(
tf.matmul(y0, self.W, transpose_b=True),
recalled_input = soft_sign(
tf.matmul(current_target, self.W, transpose_b=True),
temp=1.0
)

# Hebbian 가중치 변화 계산
dW = tf.matmul(
(x0 + x_pred),
(y0 - y_pred),
weight_delta = tf.matmul(
(current_input + recalled_input),
(current_target - predicted_output),
transpose_a=True
)

# 가중치 업데이트
self.W.assign_add(eta * dW)
self.W.assign_add(learning_rate * weight_delta)


# ============================================
Expand Down
101 changes: 51 additions & 50 deletions models/bam_mf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@
# 포화성 큐빅 활성화 함수
# ============================================

def cubic_activation(x):
def cubic_activation(input_tensor):
"""
단순 큐빅 활성화: x - x^3/3 (안정화 버전)
입력을 clipping하여 수치 안정성 보장
"""
# ✅ 입력 clipping으로 수치 안정성 확보 (NaN 방지)
x_clipped = tf.clip_by_value(x, -10.0, 10.0)
return x_clipped - tf.pow(x_clipped, 3) / 3.0
clipped_input = tf.clip_by_value(input_tensor, -10.0, 10.0)
return clipped_input - tf.pow(clipped_input, 3) / 3.0


# Soft Sign 함수 (BAM 안정화용)


def soft_sign(x, temp=1.0):
def soft_sign(input_tensor, temp=1.0):
"""
소프트 사인 함수 (tanh 기반)
temp: temperature parameter (낮을수록 sharp)
"""
return tf.tanh(x / temp)
return tf.tanh(input_tensor / temp)


# HebbianDenseLayer (W, V 분리, BatchNorm 사용)
Expand All @@ -47,27 +47,27 @@ def __init__(self, units, activation=None, use_feedback=True, name=None):
self.use_feedback = use_feedback

def build(self, input_shape):
d_in = input_shape[-1]
input_dimension = input_shape[-1]

# Feedforward 가중치 W
self.W = self.add_weight(
shape=(d_in, self.units),
self.feedforward_weights = self.add_weight(
shape=(input_dimension, self.units),
initializer='glorot_uniform', # ✅ glorot이 cubic activation에 더 안정적
trainable=True,
name='W'
name='feedforward_weights'
)

# Feedback 가중치 V (선택적, 현재 미사용)
# if self.use_feedback:
# self.V = self.add_weight(
# shape=(self.units, d_in),
# self.feedback_weights = self.add_weight(
# shape=(self.units, input_dimension),
# initializer='he_uniform',
# trainable=True,
# name='V'
# name='feedback_weights'
# )

# ✅ BatchNormalization 안정화 설정
self.norm = layers.BatchNormalization(
self.normalization_layer = layers.BatchNormalization(
momentum=0.99, # 더 안정적인 이동 평균
epsilon=1e-3, # 수치 안정성
center=True,
Expand All @@ -78,14 +78,14 @@ def build(self, input_shape):

def call(self, inputs, training=None):
# Feedforward: x -> W -> z
z = tf.matmul(inputs, self.W)
z = self.norm(z, training=training)
layer_output = tf.matmul(inputs, self.feedforward_weights)
layer_output = self.normalization_layer(layer_output, training=training)

# Activation
if self.activation is not None:
z = self.activation(z)
layer_output = self.activation(layer_output)

return z
return layer_output

def get_config(self):
config = super().get_config()
Expand Down Expand Up @@ -154,41 +154,41 @@ def __init__(self, input_dim, encoder_dims=[1024, 768, 512, 256]):
)
self.decoder_layers.append(layer)

def encode(self, x, training=None):
def encode(self, inputs, training=None):
"""입력 x를 잠재 표현 z로 인코딩"""
z = x
encoded = inputs
for layer in self.encoder_layers:
z = layer(z, training=training)
return z
encoded = layer(encoded, training=training)
return encoded

def decode(self, z, training=None):
def decode(self, latent_representation, training=None):
"""잠재 표현 z로부터 입력 형태로 복원"""
out = z
decoded = latent_representation
for layer in self.decoder_layers:
out = layer(out, training=training)
return out
decoded = layer(decoded, training=training)
return decoded

def call(self, x, training=None):
def call(self, inputs, training=None):
"""
MF 모듈을 통해 입력을 복원하고 잠재 표현도 반환

Returns:
(복원 이미지, 잠재 표현)
"""
# ✅ 입력 검증 및 clipping (NaN 방지)
x = tf.clip_by_value(x, 0.0, 1.0)
inputs = tf.clip_by_value(inputs, 0.0, 1.0)

z = self.encode(x, training=training)
latent_representation = self.encode(inputs, training=training)

# ✅ 잠재 표현 안정화 (gradient explosion 방지)
z = tf.clip_by_value(z, -100.0, 100.0)
latent_representation = tf.clip_by_value(latent_representation, -100.0, 100.0)

out = self.decode(z, training=training)
restored_output = self.decode(latent_representation, training=training)

# ✅ 출력 안정화 (sigmoid 후에도 보장)
out = tf.clip_by_value(out, 0.0, 1.0)
restored_output = tf.clip_by_value(restored_output, 0.0, 1.0)

return out, z
return restored_output, latent_representation

def get_config(self):
return {
Expand All @@ -201,51 +201,52 @@ def get_config(self):
# 편의 함수: MF 모듈 빌드
# ============================================

def build_mf_module(input_vec, hidden_units=[1024, 768, 512, 256]):
def build_mf_module(input_vector, hidden_units=[1024, 768, 512, 256]):
"""
함수형 API로 MF 모듈 빌드

Args:
input_vec: 입력 텐서
input_vector: 입력 텐서
hidden_units: 은닉층 차원 리스트

Returns:
잠재 표현 텐서
"""
x = input_vec
for i, units in enumerate(hidden_units):
x = HebbianDenseLayer(
current_layer = input_vector
for layer_idx, units in enumerate(hidden_units):
current_layer = HebbianDenseLayer(
units,
activation=cubic_activation,
name=f"mf_layer_{i+1}"
)(x)
return x
name=f"mf_layer_{layer_idx+1}"
)(current_layer)
return current_layer


def build_decoder(x, decoder_units=[512, 768, 1024], original_dim=3072):
def build_decoder(latent_representation, decoder_units=[512, 768, 1024], original_dimension=3072):
"""
함수형 API로 디코더 빌드

Args:
x: 잠재 표현 텐서
latent_representation: 잠재 표현 텐서
decoder_units: 디코더 은닉층 차원 리스트
original_dim: 원본 입력 차원
original_dimension: 원본 입력 차원

Returns:
복원된 출력 텐서
"""
for i, u in enumerate(decoder_units):
x = HebbianDenseLayer(
u,
current_layer = latent_representation
for layer_idx, units in enumerate(decoder_units):
current_layer = HebbianDenseLayer(
units,
activation=cubic_activation,
name=f"decoder_layer_{i+1}"
)(x)
name=f"decoder_layer_{layer_idx+1}"
)(current_layer)

return layers.Dense(
original_dim,
original_dimension,
activation='sigmoid',
name='restoration_output'
)(x)
)(current_layer)


# ============================================
Expand Down
Loading