215 lines
6.4 KiB
Python
215 lines
6.4 KiB
Python
from collections.abc import Callable
|
|
from .matrix import sample, sample_gaussian, prob, rms_error_accu, Mat, np
|
|
from .entity import Entity
|
|
from .status import Status
|
|
|
|
class TrainingParams:
|
|
def __init__(self,
|
|
learning_rate: float = 0.1,
|
|
momentum: float = 0.5,
|
|
weight_decay: float = 0.0,
|
|
num_epochs: int = 1000,
|
|
num_gibbs_samples: int = 1,
|
|
mini_batch_size: int = 0,
|
|
do_rao_blackwell: bool = False,
|
|
do_gibbs_sample_visible: bool = False,
|
|
do_gibbs_sample_hidden: bool = False,
|
|
do_batch_sample: bool = False
|
|
):
|
|
# Training parameters
|
|
self.learning_rate = learning_rate
|
|
self.momentum = momentum
|
|
self.weight_decay = weight_decay
|
|
self.num_epochs = num_epochs
|
|
self.num_gibbs_samples = num_gibbs_samples
|
|
self.mini_batch_size = mini_batch_size
|
|
self.do_rao_blackwell = do_rao_blackwell
|
|
self.do_gibbs_sample_visible = do_gibbs_sample_visible
|
|
self.do_gibbs_sample_hidden = do_gibbs_sample_hidden
|
|
self.do_batch_sample = do_batch_sample
|
|
|
|
@classmethod
|
|
def from_dict(cls, params: dict):
|
|
obj = TrainingParams()
|
|
obj.learning_rate = params["learningRate"]
|
|
obj.momentum = params["momentum"]
|
|
obj.weight_decay = params["weightDecay"]
|
|
obj.num_epochs = params["numEpochs"]
|
|
obj.num_gibbs_samples = params["numGibbs"]
|
|
obj.mini_batch_size = params["miniBatchSize"]
|
|
obj.do_rao_blackwell = params["doRaoBlackwell"]
|
|
obj.do_gibbs_sample_visible = params["gibbsDoSampleVisible"]
|
|
obj.do_gibbs_sample_hidden = params["gibbsDoSampleHidden"]
|
|
obj.do_batch_sample = params["doSampleBatch"]
|
|
|
|
return obj
|
|
|
|
def cd_jens(entity: Entity, v_states: Mat, params: TrainingParams):
|
|
v_probs = prob(v_states)
|
|
h_states = entity.forward(v_states)
|
|
h_probs = h_states
|
|
|
|
if entity.params.do_gaussian_hidden:
|
|
h_states += sample_gaussian(h_states)
|
|
else:
|
|
h_probs = entity.forward(v_states)
|
|
if params.do_rao_blackwell:
|
|
h_states = h_probs
|
|
else:
|
|
h_states = sample(h_probs)
|
|
|
|
# Update weights (positive phase)
|
|
dw = np.dot(np.transpose(v_states), h_states)
|
|
dbv = np.sum(v_states, 0)
|
|
dbh = np.sum(h_states, 0)
|
|
|
|
# Gibbs sampling with training params
|
|
for i in range(params.num_gibbs_samples):
|
|
if params.do_gibbs_sample_hidden:
|
|
v_probs = entity.reconstruct(sample(h_probs))
|
|
else:
|
|
v_probs = entity.reconstruct(h_probs)
|
|
|
|
# Create hidden representation given v
|
|
if params.do_gibbs_sample_visible:
|
|
h_probs = entity.forward(sample(v_probs))
|
|
else:
|
|
h_probs = entity.forward(v_probs)
|
|
|
|
# Update weights (negative phase)
|
|
dw -= np.dot(np.transpose(v_probs), h_probs)
|
|
dbv -= np.sum(v_probs, 0)
|
|
dbh -= np.sum(h_probs, 0)
|
|
|
|
return dw, dbv, dbh
|
|
|
|
def cd_binary_binary(entity: Entity, data_pos: Mat, params: TrainingParams):
|
|
# Positive phase
|
|
h_probs_pos = prob(entity.h_given_v(data_pos))
|
|
|
|
# Sample hidden states
|
|
if not params.do_rao_blackwell:
|
|
h_probs_pos = sample(h_probs_pos)
|
|
|
|
# Update weights (positive phase)
|
|
dw = np.dot(np.transpose(data_pos), h_probs_pos)
|
|
dbh = np.sum(h_probs_pos, 0)
|
|
dbv = np.sum(data_pos, 0)
|
|
|
|
# Negative phase
|
|
data_neg = prob(entity.v_given_h(h_probs_pos))
|
|
h_probs_neg = prob(entity.h_given_v(data_neg))
|
|
|
|
# Gibbs sampling with training params
|
|
# ToDo
|
|
|
|
# Update weights (negative phase)
|
|
dw -= np.dot(np.transpose(data_neg), h_probs_neg)
|
|
dbh -= np.sum(h_probs_neg, 0)
|
|
dbv -= np.sum(data_neg, 0)
|
|
|
|
return dw, dbv, dbh
|
|
|
|
def cd_gaussian_binary(entity: Entity, data_pos: Mat, params: TrainingParams):
|
|
# Positive phase
|
|
h_probs_pos = entity.h_given_v(data_pos)
|
|
|
|
# Update weights (positive phase)
|
|
dw = np.dot(np.transpose(data_pos), h_probs_pos)
|
|
dbh = np.sum(h_probs_pos, 0)
|
|
dbv = np.sum(data_pos, 0)
|
|
|
|
# Sample hidden states
|
|
h_states_pos = sample(h_probs_pos)
|
|
|
|
# Negative phase
|
|
data_neg = entity.v_given_h(h_states_pos)
|
|
h_probs_neg = entity.h_given_v(data_neg)
|
|
|
|
# Update weights (negative phase)
|
|
dw -= np.dot(np.transpose(data_neg), h_probs_neg)
|
|
dbh -= np.sum(h_probs_neg, 0)
|
|
dbv -= np.sum(data_neg, 0)
|
|
|
|
return dw, dbv, dbh
|
|
|
|
def cd_gaussian_gaussian(entity: Entity, data_pos: Mat, params: TrainingParams):
|
|
# Positive phase
|
|
h_probs_pos = entity.h_given_v(data_pos)
|
|
|
|
# Sample hidden states
|
|
h_states_pos = h_probs_pos + sample_gaussian(h_probs_pos)
|
|
|
|
# Update weights (positive phase)
|
|
dw = np.dot(np.transpose(data_pos), h_states_pos)
|
|
dbh = np.sum(h_states_pos, 0)
|
|
dbv = np.sum(data_pos, 0)
|
|
|
|
# Negative phase
|
|
data_neg = entity.v_given_h(h_states_pos)
|
|
h_probs_neg = entity.h_given_v(data_neg)
|
|
|
|
# Update weights (negative phase)
|
|
dw -= np.dot(np.transpose(data_neg), h_probs_neg)
|
|
dbh -= np.sum(h_probs_neg, 0)
|
|
dbv -= np.sum(data_neg, 0)
|
|
|
|
return dw, dbv, dbh
|
|
|
|
def to_mini_batch(batch: Mat, mini_batch_size: int):
|
|
mini_batches = []
|
|
remain = batch.shape[0]
|
|
index = 0
|
|
while remain > 0:
|
|
batch_size = min(mini_batch_size, remain)
|
|
mini_batches.append(batch[index:index + batch_size])
|
|
remain -= batch_size
|
|
index += batch_size
|
|
|
|
return mini_batches
|
|
|
|
def train(entity: Entity, batch: Mat, params: TrainingParams, status: Status):
|
|
mini_batch_size = min(params.mini_batch_size, batch.shape[0]) if params.mini_batch_size > 0 else batch.shape[0]
|
|
d_progress = 100.0 / (batch.shape[0]*params.num_epochs)
|
|
progress = 0
|
|
keep_running = True
|
|
status.on_change()
|
|
cd_func = None
|
|
if entity.type == Entity.Type.GB_RBM:
|
|
cd_func = cd_gaussian_binary
|
|
if entity.type == Entity.Type.BB_RBM:
|
|
cd_func = cd_binary_binary
|
|
if entity.type == Entity.Type.GG_RBM:
|
|
cd_func = cd_gaussian_gaussian
|
|
|
|
for mini_batch in to_mini_batch(batch, mini_batch_size):
|
|
if not keep_running:
|
|
break
|
|
|
|
entity.grad_zero()
|
|
for epochs in range(params.num_epochs):
|
|
# Contrastive divergence learning: calculate gradients
|
|
dwhv, dbv, dbh = cd_func(entity, mini_batch, params)
|
|
|
|
# Adjust weight and biases
|
|
grad = entity.grad_compute(dbv, dbh, dwhv, learning_rate=params.learning_rate/batch.shape[0], momentum=params.momentum, weight_decay=params.weight_decay)
|
|
entity.state_adjust(grad)
|
|
|
|
# check if status update is needed
|
|
if status.want_report(round(progress)):
|
|
# Calculate error
|
|
err_rms = rms_error_accu(batch - entity.reconstruct(entity.forward(batch)))
|
|
if not status.on_change({"progress": {"value": round(progress), "unit": "%"},
|
|
"err_rms": {"value": err_rms, "unit": ""}}):
|
|
keep_running = False
|
|
break
|
|
|
|
progress += d_progress*mini_batch.shape[0]
|
|
|
|
# Update final status
|
|
err_rms = rms_error_accu(batch - entity.reconstruct(entity.forward(batch)))
|
|
status.on_change({"progress": {"value": round(progress), "unit": "%"},
|
|
"err_rms_total": {"value": err_rms, "unit": ""}})
|
|
|
|
return None
|