def test_weights_clip(self): """WeightsClip should restrict values after optimizer updates""" constraint = WeightsClip(min_value=-0.001, max_value=0.001) initializer = Constant(1.0) layer = Dense(10, kernel_constraint=constraint, bias_constraint=constraint, kernel_initializer=initializer, bias_initializer=initializer) with tf.GradientTape() as tape: data = tf.random.normal(shape=(1, 5)) pred = layer(data) true_lb = tf.ones_like(pred) loss = MeanSquaredError()(true_lb, pred) # before update, weights should equal to initial values for weight in layer.weights: assert np.allclose(weight.numpy(), 1.0) optimizer = SGD(learning_rate=0.0001) grad = tape.gradient(loss, layer.trainable_variables) optimizer.apply_gradients(zip(grad, layer.trainable_variables)) # after update, weights should within constraint ranges for weight in layer.weights: assert np.all(-0.001 <= weight.numpy()) assert np.all(weight.numpy() <= 0.001)
class DQNAgent: def __init__(self, num_actions=8, epsilon=1, epsilon_min=0.1, epsilon_decay=0.9995, load=load): self.epsilon = epsilon self.epsilon_min = epsilon_min self.epsilon_decay = epsilon_decay self.discount_factor = discount_factor self.num_actions = num_actions self.optimizer = SGD(learning_rate) if load: print("Loading model from: ", model_savefolder) self.dqn = tf.keras.models.load_model(model_savefolder) else: self.dqn = DQN(self.num_actions) self.target_net = DQN(self.num_actions) def update_target_net(self): self.target_net.set_weights(self.dqn.get_weights()) def choose_action(self, state): if self.epsilon < np.random.uniform(0,1): action = int(tf.argmax(self.dqn(tf.reshape(state, (1,30,45,1))), axis=1)) else: action = np.random.choice(range(self.num_actions), 1)[0] return action def train_dqn(self, samples): screen_buf, actions, rewards, next_screen_buf, dones = split_tuple(samples) row_ids = list(range(screen_buf.shape[0])) ids = extractDigits(row_ids, actions) done_ids = extractDigits(np.where(dones)[0]) with tf.GradientTape() as tape: tape.watch(self.dqn.trainable_variables) Q_prev = tf.gather_nd(self.dqn(screen_buf), ids) Q_next = self.target_net(next_screen_buf) Q_next = tf.gather_nd(Q_next, extractDigits(row_ids, tf.argmax(agent.dqn(next_screen_buf), axis=1))) q_target = rewards + self.discount_factor * Q_next if len(done_ids)>0: done_rewards = tf.gather_nd(rewards, done_ids) q_target = tf.tensor_scatter_nd_update(tensor=q_target, indices=done_ids, updates=done_rewards) td_error = tf.keras.losses.MSE(q_target, Q_prev) gradients = tape.gradient(td_error, self.dqn.trainable_variables) self.optimizer.apply_gradients(zip(gradients, self.dqn.trainable_variables)) if self.epsilon > self.epsilon_min: self.epsilon *= self.epsilon_decay else: self.epsilon = self.epsilon_min
def generate_adversarial( model, x_0, adv_radius, adv_type, training_type, adv_policy, step_size=gin.REQUIRED, max_iter=gin.REQUIRED, n_stop=gin.REQUIRED, l1_regularization=gin.REQUIRED ): # l1 special case of intermdiate activation => merge into ManifoldSampler if adv_policy == 'adv': noise = tf.random.uniform(shape=x_0.shape, minval=-adv_radius, maxval=adv_radius) noise = ball_project(noise, x_0, adv_radius) delta = tf.Variable(initial_value=noise, trainable=True) elif adv_policy == 'gan': noise = tf.random.uniform(shape=x_0.shape, minval=0., maxval=1.) x = tf.Variable(initial_value=noise, trainable=True) lr = step_size * (adv_radius / max_iter) optimizer = SGD(learning_rate=lr) # sufficient considering loss landscape manifold = ManifoldForward(model, n_stop) for _ in range(max_iter): with tf.GradientTape(watch_accessed_variables=False) as tape: if adv_policy == 'adv': tape.watch(delta) x = x_0 + delta elif adv_policy == 'gan': tape.watch(x) y, z_map = manifold.sample(x) loss = goal(training_type, adv_type, y) if z_map is not None: z_loss = manifold.representation_goal(z_map) loss = loss + z_loss if l1_regularization is not None: l1 = l1_penalty(x) if adv_policy == 'gan' else l1_penalty( delta) loss = loss + l1_regularization * l1 diff_target = x if adv_policy == 'gan' else delta grad_f = tape.gradient(loss, [diff_target]) grad_f = renormalize_grads(grad_f) optimizer.apply_gradients(zip(grad_f, [diff_target])) if adv_policy == 'adv': delta.assign(ball_project( delta, x_0, adv_radius)) # project back to ball in manifold elif adv_policy == 'gan': x.assign(tf.clip_by_value(x, 0., 1.)) # image set if adv_policy == 'adv': x = x_0 + delta.value() x = tf.clip_by_value(x, 0., 1.) return x elif adv_policy == 'gan': return x.value()
def styleTransfer(sourcepath, stylepath): path = 'static/uploads' base_image_path = os.path.join(path, "source.png") style_reference_image_path = os.path.join(path, "style.png") result_prefix = "static/results/result" global content_weight global total_variation_weight global style_weight total_variation_weight = 1e-6 style_weight = 3e-6 content_weight = 5e-7 width, height = image.load_img(base_image_path).size global img_nrows global img_ncols img_nrows = 400 img_ncols = int(width * img_nrows / height) content_img = preprocess_image(base_image_path) shape = content_img.shape[1:] model = VGG19(weights="imagenet", include_top=False, input_shape=shape) outputs_dict = dict([(layer.name, layer.output) for layer in model.layers]) global feature_extractor feature_extractor = Model(inputs=model.inputs, outputs=outputs_dict) global style_layer_names style_layer_names = [ "block1_conv1", "block2_conv1", "block3_conv1", "block4_conv1", "block5_conv1", ] global content_layer_name content_layer_name = "block5_conv2" optimizer = SGD( schedules.ExponentialDecay(initial_learning_rate=100.0, decay_steps=100, decay_rate=0.96)) base_image = preprocess_image(base_image_path) style_reference_image = preprocess_image(style_reference_image_path) combination_image = tf.Variable(preprocess_image(base_image_path)) iterations = 100 for i in range(1, iterations + 1): loss, grads = compute_loss_and_grads(combination_image, base_image, style_reference_image) optimizer.apply_gradients([(grads, combination_image)]) print("Iteration %d: loss=%.2f" % (i, (loss))) print(combination_image) img = deprocess_image(combination_image.numpy()) fname = result_prefix + ".png" image.save_img(fname, img)
def train(model: Model, loss: Loss, optimizer: SGD, x: tf.Tensor, y: tf.Tensor) -> float: # Forward with tf.GradientTape() as tape: y_pred = model(x) output = tf.reduce_mean(loss(y, y_pred)) # Backward grad = tape.gradient(output, model.trainable_variables) # Update parameters optimizer.apply_gradients(zip(grad, model.trainable_variables)) return output.numpy()
def train(model: Sequential, loss: Loss, optimizer: SGD, x: tf.Tensor, y: tf.Tensor) -> float: with tf.GradientTape() as tape: # Forward x = tf.reshape(x, shape=(x.shape[0], 1)) y_pred = model(x) y_pred = tf.squeeze(y_pred) output = tf.reduce_mean(loss(y, y_pred)) # Backward grad = tape.gradient(output, model.trainable_variables) # Update parameters optimizer.apply_gradients(zip(grad, model.trainable_variables)) return output.numpy()
def train_experimental_model(train_images, train_labels, test_images, test_labels): logging.info('Training network with two output layers...') model = utils.create_model('VGG_8_simple_with_two_outputs') train_dataset = tf.data.Dataset.from_tensor_slices( (train_images, train_labels)).batch(64) test_dataset = tf.data.Dataset.from_tensor_slices( (test_images, test_labels)).batch(64) loss_object = tf.keras.losses.CategoricalCrossentropy(from_logits=True) optimizer = SGD(lr=0.001, momentum=0.9) train_loss_results = [] train_accuracy_results = [] num_epochs = 200 for epoch in range(num_epochs): epoch_loss_avg = tf.keras.metrics.Mean() epoch_accuracy = tf.keras.metrics.CategoricalAccuracy() for x, y in train_dataset: # Optimize the model loss_value, grads = grad(model, x, y, loss_object) optimizer.apply_gradients(zip(grads, model.trainable_variables)) # Track progress epoch_loss_avg.update_state(loss_value) # Add current batch loss y1_, y2_ = model(x, training=True) epoch_accuracy.update_state(y, y1_) # End epoch train_loss_results.append(epoch_loss_avg.result()) train_accuracy_results.append(epoch_accuracy.result()) logging.info( f'Epoch {epoch:03d}: Loss: {epoch_loss_avg.result():.3f}, ' f'Accuracy: {epoch_accuracy.result():.3%}') # Do a test after some epochs, and also at the end if epoch % 10 == 0 or epoch == num_epochs - 1: test_current_accuracy(model, test_dataset)
def test_discriminator_weight_updates(self): """Discriminator weights should be restrcited into constraint range""" inputs = Input((64, 64, 3)) discriminator = WDiscriminator()(inputs) discriminator = Model(inputs=inputs, outputs=discriminator) with tf.GradientTape() as tape: data = tf.random.normal((1, 64, 64, 3)) pred = discriminator(data, training=True) true_lb = tf.ones_like(pred) loss = MeanSquaredError()(true_lb, pred) optimizer = SGD(learning_rate=1.0) grad = tape.gradient(loss, discriminator.trainable_variables) optimizer.apply_gradients(zip(grad, discriminator.trainable_variables)) # after update, weights should within constraint ranges for weight in discriminator.trainable_variables: assert np.all(-0.01 <= weight.numpy()) assert np.all(weight.numpy() <= 0.01)
class AVNet(Model): def __init__(self, n_features, n_candidates, n_voters, inp_shape, opt, l_rate, arch): ''' Simple version: 1 input layer (relu), 1 hidden layer (relu), 1 output layer (softmax) Extras: - Dropout - BatchNormalization ''' super(AVNet, self).__init__() # This architecture variable allows us to run concurrent experiments automatically self.arch = arch # Define layers self.input_layer = Dense(n_features, activation='relu', input_shape=inp_shape) if self.arch == 1: self.mid_layer = Dense(n_candidates * n_features, activation='relu') self.last_layer = Dense(n_candidates * n_voters, activation='relu') elif self.arch == 2: self.mid_layer = Dense(n_candidates * n_voters, activation='relu') self.last_layer = Dense(n_candidates * n_features, activation='relu') elif self.arch == 3: self.mid_layer = Dense(n_candidates * n_voters, activation='relu') self.last_layer = Dense(n_candidates * n_features, activation='linear') elif self.arch == 4: self.mid_layer = Dense(n_candidates * n_features, activation='relu') self.last_layer = Dense(n_candidates * n_voters, activation='linear') elif self.arch == 5: self.mid_layer = Dense(n_candidates * n_voters, activation='linear') self.last_layer = Dense(n_candidates * n_features, activation='linear') elif self.arch == 6: self.mid_layer = Dense(n_candidates * n_features, activation='linear') self.last_layer = Dense(n_candidates * n_voters, activation='linear') elif self.arch == 7: self.mid_layer = Dense(128, activation='relu') self.last_layer = Dense(256, activation='relu') elif self.arch == 8: self.mid_layer = Dense(128, activation='linear') self.last_layer = Dense(256, activation='linear') elif self.arch == 9: self.mid_layer = Dense(n_candidates * n_features, activation='relu') self.last_layer = Dense(n_candidates * n_features, activation='relu') elif self.arch == 10: self.mid_layer = Dense(n_candidates * n_features, activation='linear') self.last_layer = Dense(n_candidates * n_features, activation='linear') elif self.arch == 11: self.mid_layer = Dense(n_candidates * n_features, activation='relu') self.last_layer = Dense(n_candidates * n_features, activation='linear') # Voter level, candidate level elif self.arch == 12: self.mid_layer = Dense(n_candidates * n_features, activation='relu') self.extra_layer = Dense(n_candidates * n_voters, activation='relu') self.last_layer = Dense(n_candidates * n_features, activation='relu') elif self.arch == 13: self.mid_layer = Dense(n_candidates * n_features, activation='relu') self.extra_layer = Dense(n_candidates * n_voters, activation='linear') self.last_layer = Dense(n_candidates * n_features, activation='relu') elif self.arch == 14: self.mid_layer = Dense(n_candidates * n_features, activation='linear') self.extra_layer = Dense(n_candidates * n_voters, activation='linear') self.last_layer = Dense(n_candidates * n_features, activation='linear') else: print("Must enter a valid network architecture! [1-8]") sys.exit(1) self.leaky_relu = LeakyReLU(alpha=0.3) self.dropout = Dropout(0.2) self.batch_norm = BatchNormalization() self.scorer = Dense(n_candidates, activation='softmax') if opt == "Adam": self.optimizer = Adam(learning_rate=l_rate) elif opt == "Adagrad": self.optimizer = Adagrad(learning_rate=l_rate) else: self.optimizer = SGD(learning_rate=l_rate) self.CCE = CategoricalCrossentropy() self.av_losses = [] def reset_scores(self): # Scoring functions self.total_condorcet = 0 self.total_majority = 0 self.total_plurality = 0 self.total_IM = 0 self.condorcet_score = 0 self.majority_score = 0 self.plurality_score = 0 self.IM_score = 0 def call(self, inputs, **kwargs): """ Inputs is some tensor version of the ballots in an AVProfile For testing purposes we will use AVProfile.rank_matrix, which represents the count of each candidate per rank """ x = self.input_layer(inputs) if self.arch in [1, 2, 7, 8, 9]: x = self.mid_layer(x) x = self.dropout(x) x = self.last_layer(x) x = self.dropout(x) elif self.arch in [3, 4, 11]: x = self.mid_layer(x) x = self.dropout(x) x = self.last_layer(x) x = self.leaky_relu(x) x = self.dropout(x) elif self.arch in [5, 6, 10]: x = self.mid_layer(x) x = self.leaky_relu(x) x = self.dropout(x) x = self.last_layer(x) x = self.leaky_relu(x) x = self.dropout(x) elif self.arch == 12: x = self.mid_layer(x) x = self.dropout(x) x = self.extra_layer(x) x = self.dropout(x) x = self.last_layer(x) # x = self.dropout(x) elif self.arch == 13: x = self.mid_layer(x) x = self.dropout(x) x = self.extra_layer(x) x = self.leaky_relu(x) x = self.dropout(x) x = self.last_layer(x) # x = self.dropout(x) elif self.arch == 14: x = self.mid_layer(x) x = self.leaky_relu(x) x = self.dropout(x) x = self.extra_layer(x) x = self.leaky_relu(x) x = self.dropout(x) x = self.last_layer(x) x = self.leaky_relu(x) # x = self.dropout(x) return self.scorer(x) def av_loss(self, profile, verbose=False): """ An av_loss function will take an instance of <profile, condorcet_winner, majority_winner, simulator_list>, where - profile = AVProfile.rank_matrix (shape: n_candidates x n_candidates) - condorcet_winner = AVProfile.condorcet_winner (one-hot vector of size n_candidates) - majority_winner = AVProfile.majority_winner (one-hot vector of size n_candidates) - (IIA or IM)_simulator = AVProfile.IIA_profiles, AVProfile.IM_profiles (list of matrices of shape: n_candidates x n_candidates) Idea: For 1 profile, the av_loss will be the sum of all the loss components according to the constraints above. -> When there is a one-hot vector, we will use cross entropy -> When there is a simulator list, we will add up the cross entropy losses after "running the election" Extras: -> Add a regularization term for the sum of cross entropy in the simulator list case """ # Original profile rank matrix profile_matrix = profile.flatten_rank_matrix() # Winners as strings condorcet_w = profile.condorcet_w majority_w = profile.majority_w plurality_w = profile.plurality_w # One-hot vectors condorcet_w_vec = profile.condorcet_w_vector majority_w_vec = profile.majority_w_vector plurality_w_vec = profile.plurality_w_vector # Make a forward pass to the profile predictions = self.call(profile_matrix) pred_w = get_winner(predictions, profile.candidates) # Simulated preferences according to the predicted winner alternative_profiles = profile.IM_rank_matrices[get_winner( predictions, profile.candidates, out_format="idx")] alternative_profiles = [ profile.flatten_rank_matrix(alt_profile) for alt_profile in alternative_profiles ] # Keep track of scores if condorcet_w != "No Condorcet Winner": self.total_condorcet += 1 if pred_w == condorcet_w: self.condorcet_score += 1 if majority_w != "No Majority Winner": self.total_majority += 1 if pred_w == majority_w: self.majority_score += 1 else: # We will use Plurality if no Condorcet or Majority winner self.total_plurality += 1 if pred_w == plurality_w: self.plurality_score += 1 # Interpret the results if verbose: print( "------------------------------------------------------------") # print("Profile rank matrix:", profile.rank_matrix) print("Condorcet winner:", condorcet_w) print("Majority winner:", majority_w) print("Plurality winner:", plurality_w) print("-----------------") print("Predicted winner:", get_winner(predictions, profile.candidates)) print("Predicted winner quality scores:", predictions) print( "------------------------------------------------------------") def loss(plurality_c, pred_c): alt_profiles_score = 0 # Calculate IM score first - sum of crossentropy for all alternative winners given an IM profile IM_score = 0 for IM_alt_profile in alternative_profiles: alt_winner = self.call(IM_alt_profile) pred_c_vec = get_winner(pred_c, profile.candidates, out_format="one-hot") # Keep track of when winners match between altered profiles and original profile if get_winner(alt_winner, profile.candidates) == get_winner( pred_c, profile.candidates): alt_profiles_score += 1 IM_score += self.CCE(pred_c_vec, alt_winner) # Store scores self.total_IM += len(alternative_profiles) self.IM_score += alt_profiles_score if verbose: print( f"IM score: {alt_profiles_score}/{len(alternative_profiles)}" ) # Regularize IM score by the number of simulated profiles IM_score /= len(alternative_profiles) # If there isn't a Condorcet or a Majority winner, just use the Plurality winner if count_nonzero(condorcet_w_vec) == 0 and count_nonzero( majority_w_vec) == 0: plurality_score = self.CCE(plurality_c, pred_c) return plurality_score + IM_score else: condorcet_score = self.CCE(condorcet_w_vec, pred_c) majority_score = self.CCE(majority_w_vec, pred_c) if count_nonzero(condorcet_w_vec) != 0 and count_nonzero( majority_w_vec) == 0: return condorcet_score + IM_score if count_nonzero(condorcet_w_vec ) == 0 and count_nonzero(majority_w_vec) != 0: return majority_score + IM_score else: return majority_score + condorcet_score + IM_score # Return loss function return loss(plurality_c=plurality_w_vec, pred_c=predictions) def calculate_grad(self, profile, verbose=False): with GradientTape() as tape: loss_value = self.av_loss(profile, verbose) return loss_value, tape.gradient(loss_value, self.trainable_variables) def train(self, profiles, epochs): for _ in tqdm(range(epochs)): self.reset_scores() # print(f"Epoch {epoch} ==============================================================") # Iterate through the list of profiles, not datasets for i, profile in enumerate(profiles): # Perform forward pass + calculate gradients # if i % 33 == 0: # loss_value, grads = self.calculate_grad(profile, verbose=True) # print(f"Step: {self.optimizer.iterations.numpy()}, Loss: {loss_value}") # else: loss_value, grads = self.calculate_grad(profile) self.av_losses.append(loss_value) self.optimizer.apply_gradients( zip(grads, self.trainable_variables)) # print("\n\n") def get_results(self): print("===================================================") print("AVNet Results") print("---------------------------------------------------") results = dict() if self.total_condorcet != 0: print( f"Condorcet Score: {self.condorcet_score}/{self.total_condorcet} = {self.condorcet_score / self.total_condorcet}" ) results["Condorcet Score"] = ( f"{self.condorcet_score}/{self.total_condorcet}", self.condorcet_score / self.total_condorcet) else: print("No Condorcet Winners") results["Condorcet Score"] = (0, 0) if self.total_majority != 0: print( f"Majority Score: {self.majority_score}/{self.total_majority} = {self.majority_score / self.total_majority}" ) results["Majority Score"] = ( f"{self.majority_score}/{self.total_majority}", self.majority_score / self.total_majority) else: print("No Majority Winners") results["Majority Score"] = (0, 0) if self.total_plurality != 0: print( f"Plurality Score: {self.plurality_score}/{self.total_plurality} = {self.plurality_score / self.total_plurality}" ) results["Plurality Score"] = ( f"{self.plurality_score}/{self.total_plurality}", self.plurality_score / self.total_plurality) else: print("No Plurality Winners were used") results["Plurality Score"] = (0, 0) print( f"IM Score: {self.IM_score}/{self.total_IM} = {self.IM_score / self.total_IM}" ) results["IM Score"] = (f"{self.IM_score}/{self.total_IM}", self.IM_score / self.total_IM) print(f"IM CCE Mean:", mean(self.av_losses)) results["IM CCE Score"] = round(mean(self.av_losses), 3) return results
metrics = {'acc': 0.0, 'loss': 0.0, 'val_acc': 0.0, 'val_loss': 0.0, 'lr': lr} for epoch in range(n_epochs): # Iterate through the training set print("\epoch {}/{}".format(epoch+1,n_epochs)) progress_bar = Progbar(train_size, stateful_metrics=list(metrics.keys())) train_gen = train_ds.take(train_size//batch_size) for batch_idx, (x, y) in enumerate(train_gen): with tf.GradientTape() as tape: y_pred = model(x, training=True) loss = loss_fn(y, y_pred) gradients = tape.gradient(loss, model.trainable_weights) optimizer.apply_gradients(zip(gradients, model.trainable_weights)) acc_metric.update_state(y, y_pred) train_step += 1 progress_bar.update(batch_idx*batch_size, values=[('acc',acc_metric.result()), ('loss', loss)]) # print(batch_idx, " / ", len(train_gen)) with train_writer.as_default(): tf.summary.scalar("Loss", loss, step=epoch) tf.summary.scalar( "Accuracy", acc_metric.result(), step=epoch ) # reset accuracy between epochs (and for testing and test) acc_metric.reset_states()
x = tf.random.uniform(shape=(n, )) print(x.shape) noise = tf.random.normal(shape=(len(x), )) print(noise.shape) y = m * x + b + noise return x, y m = 1 b = 2 Xtrain, ytrain = MakeNoisyData(m, b) model = tf.keras.Sequential([ Dense(12, activation="relu", input_shape=(1, )), Dense(12, activation="relu"), Dense(1) ]) loss = MeanSquaredError() print(model.summary()) optimizer = SGD(learning_rate=0.05, momentum=0.9) for i in range(50): with tf.GradientTape() as tape: current_loss = loss(ytrain, model(Xtrain)) grad = tape.gradient(current_loss, model.trainable_variables) optimizer.apply_gradients(zip(grad, model.trainable_variables)) print(current_loss)
def update(self): #update v st = time() X = np.reshape(self.states_m, (1, -1)) next_v = self._predict_v(self.next_states_m) if not self.dones_m else 0 Y = np.reshape(self.rewards_m + self.discount * next_v, (1, 1)) self.v.fit(x=X, y=Y, batch_size=1, verbose=0) #update pi st = time() this_v = self._predict_v(self.states_m) delta = self.rewards_m + self.discount * next_v - this_v self.I *= self.discount if self.time == 1: self.I = 1 #I = gamma^t since epsiode started if self.dones_m: self.time = 0 #Is incremented in observe s = self.states_m #Using the formulas found in Reinforcement Learning : An Introduction if self.with_keras: if self.bias: s = np.concatenate([self.states_m, [1]]) with tf.GradientTape() as tape: #print(self.pi_trainer.output, self.pi_trainer.trainable_variables) ln_pi = self.pi_trainer( [s.reshape((1, -1)), np.reshape(self.actions_m, (1, 1))], training=True) ln_pi_grads = tape.gradient(ln_pi, self.pi_trainer.trainable_variables) grads = -self.I * delta * ln_pi_grads optimizer = SGD(lr=self.learning_rate_v, clipvalue=10) optimizer.apply_gradients( zip(grads, self.pi_trainer.trainable_variables)) else: mu, sig = self._get_mu_sig(s) #contains the "add bias" if self.bias: s = np.concatenate([self.states_m, [1]]) grad_ln_pi_mu = (self.actions_m - mu) / (sig**2) * s grad_ln_pi_sig = ((self.actions_m - mu)**2 / (sig**2) - 1) * s #gradient clipping grad_ln_pi_mu = np.clip(grad_ln_pi_mu, -10, 10) grad_ln_pi_sig = np.clip(grad_ln_pi_sig, -10, 10) if self.bias: self.theta_mu += np.reshape( self.learning_rate_pi * self.I * delta * grad_ln_pi_mu, (self.state_nb + 1)) self.theta_sig += np.reshape( self.learning_rate_pi * self.I * delta * grad_ln_pi_sig, (self.state_nb + 1)) else: self.theta_mu += np.reshape( self.learning_rate_pi * self.I * delta * grad_ln_pi_mu, (self.state_nb)) self.theta_sig += np.reshape( self.learning_rate_pi * self.I * delta * grad_ln_pi_sig, (self.state_nb))
class DeepARLearner: def __init__( self, ts_obj: TimeSeriesTrain, output_dim: int = 1, emb_dim: int = 128, lstm_dim: int = 128, dropout: float = 0.1, optimizer: str = "adam", lr: float = 0.001, batch_size: int = 16, # scheduler=None, train_window: int = 20, verbose: int = 0, # mask_value: int=0, random_seed: int = None, hparams: typing.Dict[str, typing.Union[int, float]] = None, ): """ initialize DeepAR model Arguments: ts_obj {TimeSeriesTrain} -- training dataset object Keyword Arguments: output_dim {int} -- dimension of output variables (default: {1}) emb_dim {int} -- dimension of categorical embeddings (default: {128}) lstm_dim {int} -- dimension of lstm cells (default: {128}) dropout {float} -- dropout (default: {0.1}) optimizer {str} -- options are "adam" and "sgd" (default: {"adam"}) lr {float} -- learning rate (default: {0.001}) batch_size {int} -- number of time series to sample in each batch (default: {16}) train_window {int} -- the length of time series sampled for training. consistent throughout (default: {20}) verbose {int} -- (default: {0}) random_seed {int} -- optional random seed to control randomness throughout learner (default: {None}) hparams {typing.Dict[str, typing.Union[int, float]]} -- dict of hyperparameters (keys) and hp domain (values) over which to hp search (default: {None}) """ if random_seed is not None: tf.random.set_seed(random_seed) assert verbose == 0 or verbose == 1, "argument verbose must be 0 or 1" if verbose == 1: logger.setLevel(logging.INFO) self._verbose = verbose if hparams is None: self._lr = lr self._train_window = train_window self._batch_size = batch_size else: self._lr = hparams["learning_rate"] self._train_window = hparams["window_size"] self._batch_size = hparams["batch_size"] emb_dim = hparams["emb_dim"] lstm_dim = hparams["lstm_dim"] dropout = hparams["lstm_dropout"] # Invariance - Window size must be <= max_series_length + negative observations to respect covariates if self._train_window > ts_obj.max_age: logger.info( f"Training set with max observations {ts_obj.max_age} does not support train window size of " + f"{self._train_window}, resetting train window to {ts_obj.max_age}" ) self._train_window = ts_obj.max_age # self.scheduler = scheduler self._ts_obj = ts_obj # define model self._model = self._create_model( self._ts_obj.num_cats, self._ts_obj.features, output_dim=output_dim, emb_dim=emb_dim, lstm_dim=lstm_dim, dropout=dropout, count_data=self._ts_obj.count_data, ) # define optimizer if optimizer == "adam": self._optimizer = Adam(learning_rate=self._lr) elif optimizer == "sgd": self._optimizer = SGD(lr=self._lr, momentum=0.1, nesterov=True) else: raise ValueError("Optimizer must be one of `adam` and `sgd`") # define loss function if self._ts_obj.count_data: self._loss_fn = NegativeBinomialLogLikelihood( mask_value=self._ts_obj.mask_value) else: self._loss_fn = GaussianLogLikelihood( mask_value=self._ts_obj.mask_value) def save_weights(self, filepath: str): """ saves model's current weights to filepath Arguments: filepath {str} -- filepath """ self._model.save_weights(filepath) def load_weights(self, filepath: str): """ loads model's current weights from filepath Arguments: filepath {str} -- filepath """ self._model.load_weights(filepath) def _create_model( self, num_cats: int, num_features: int, output_dim: int = 1, emb_dim: int = 128, lstm_dim: int = 128, batch_size: int = 16, dropout: float = 0.1, count_data: bool = False, ) -> Model: """ util function creates model architecture (Keras Sequential) with arguments specified in constructor """ cont_inputs = Input(shape=(self._train_window, num_features), batch_size=self._batch_size) cat_inputs = Input(shape=(self._train_window, ), batch_size=self._batch_size) embedding = Embedding(num_cats, emb_dim)(cat_inputs) concatenate = Concatenate()([cont_inputs, embedding]) lstm_out = LSTMResetStateful( lstm_dim, return_sequences=True, stateful=True, dropout=dropout, recurrent_dropout=dropout, unit_forget_bias=True, name="lstm", )(concatenate) mu = Dense( output_dim, kernel_initializer="glorot_normal", bias_initializer="glorot_normal", name="mu", )(lstm_out) sigma = Dense( output_dim, kernel_initializer="glorot_normal", bias_initializer="glorot_normal", name="sigma", )(lstm_out) model = Model(inputs=[cont_inputs, cat_inputs], outputs=[mu, sigma]) return model def _training_loop( self, filepath: str, train_gen: train_ts_generator, # can name of function be type? val_gen: train_ts_generator, epochs: int = 100, steps_per_epoch: int = 50, early_stopping: int = True, stopping_patience: int = 5, stopping_delta: int = 1, ) -> typing.Tuple[tf.Tensor, int]: """ util function iterates over batches, updates gradients, records metrics, writes to tb, checkpoints, early stopping """ # set up metrics to track during training batch_loss_avg = Mean() epoch_loss_avg = Mean() eval_loss_avg = Mean() eval_mae = MeanAbsoluteError() eval_rmse = RootMeanSquaredError() # set up early stopping callback early_stopping_cb = EarlyStopping(patience=stopping_patience, active=early_stopping, delta=stopping_delta) # setup table for unscaling self._lookup_table = build_tf_lookup(self._ts_obj.target_means) # Iterate over epochs. best_metric = math.inf for epoch in range(epochs): logger.info(f"Start of epoch {epoch}") start_time = time.time() for batch, (x_batch_train, cat_labels, y_batch_train) in enumerate(train_gen): # compute loss with tf.GradientTape(persistent=True) as tape: mu, scale = self._model(x_batch_train, training=True) # softplus parameters scale = softplus(scale) if self._ts_obj.count_data: mu = softplus(mu) mu, scale = unscale(mu, scale, cat_labels, self._lookup_table) loss_value = self._loss_fn(y_batch_train, (mu, scale)) # sgd if self._tb: tf.summary.scalar("train_loss", loss_value, epoch * steps_per_epoch + batch) batch_loss_avg(loss_value) epoch_loss_avg(loss_value) grads = tape.gradient(loss_value, self._model.trainable_weights) self._optimizer.apply_gradients( zip(grads, self._model.trainable_weights)) # Log 5x per epoch. if batch % (steps_per_epoch // 5) == 0 and batch != 0: logger.info( f"Epoch {epoch}: Avg train loss over last {(steps_per_epoch // 5)} steps: {batch_loss_avg.result()}" ) batch_loss_avg.reset_states() # Run each epoch batches times epoch_loss_avg_result = epoch_loss_avg.result() if batch == steps_per_epoch: logger.info( f"Epoch {epoch} took {round(time.time() - start_time, 0)}s : Avg train loss: {epoch_loss_avg_result}" ) break # validation if val_gen is not None: logger.info(f"End of epoch {epoch}, validating...") start_time = time.time() for batch, (x_batch_val, cat_labels, y_batch_val) in enumerate(val_gen): # compute loss, doesn't need to be persistent bc not updating weights with tf.GradientTape() as tape: # treat as training -> reset lstm states inbetween each batch mu, scale = self._model(x_batch_val, training=True) # softplus parameters mu, scale = self._softplus(mu, scale) # unscale parameters mu, scale = unscale(mu, scale, cat_labels, self._lookup_table) # calculate loss loss_value = self._loss_fn(y_batch_val, (mu, scale)) # log validation metrics (avg loss, avg MAE, avg RMSE) eval_mae(y_batch_val, mu) eval_rmse(y_batch_val, mu) eval_loss_avg(loss_value) if batch == steps_per_epoch: break # logging eval_mae_result = eval_mae.result() logger.info( f"Validation took {round(time.time() - start_time, 0)}s") logger.info( f"Epoch {epoch}: Val loss on {steps_per_epoch} steps: {eval_loss_avg.result()}" ) logger.info( f"Epoch {epoch}: Val MAE: {eval_mae_result}, RMSE: {eval_rmse.result()}" ) if self._tb: tf.summary.scalar("val_loss", eval_loss_avg.result(), epoch) tf.summary.scalar("val_mae", eval_mae_result, epoch) tf.summary.scalar("val_rmse", eval_rmse.result(), epoch) new_metric = eval_mae_result # early stopping if early_stopping_cb(eval_mae_result): break # reset metric states eval_loss_avg.reset_states() eval_mae.reset_states() eval_rmse.reset_states() else: if early_stopping_cb(epoch_loss_avg_result): break new_metric = epoch_loss_avg_result # update best_metric and save new checkpoint if improvement if new_metric < best_metric: best_metric = new_metric if filepath is not None: self._checkpointer.save(file_prefix=filepath) else: self.save_weights("model_best_weights.h5") # reset epoch loss metric epoch_loss_avg.reset_states() # load in best weights before returning if not using checkpointer if filepath is None: self.load_weights("model_best_weights.h5") os.remove("model_best_weights.h5") return best_metric, epoch + 1 def fit( self, checkpoint_dir: str = None, validation: bool = True, steps_per_epoch: int = 50, epochs: int = 100, early_stopping: bool = True, stopping_patience: int = 5, stopping_delta: int = 1, tensorboard: bool = True, ) -> typing.Tuple[tf.Tensor, int]: """ fits DeepAR model for steps_per_epoch * epochs iterations Keyword Arguments: checkpoint_dir {str} -- directory to save checkpoint and tensorboard files (default: {None}) validation {bool} -- whether to perform validation. If True will automatically try to use validation data sequestered in construction of time series object(default: {True}) steps_per_epoch {int} -- number of steps to process each epoch (default: {50}) epochs {int} -- number of epochs (default: {100}) early_stopping {bool} -- whether to include early stopping callback (default: {True}) stopping_patience {int} -- early stopping callback patience, measured in epochs (default: {5}) stopping_delta {int} -- early stopping delta, the comparison to determine stopping will be previous metric vs. new metric +- stopping_delta (default: {1}) tensorboard {bool} -- whether to write output to tensorboard logs (default: {True}) Returns: Tuple(float, int) -- 1) final_metric best (train loss or eval MAE) after fitting 2) number of iterations completed (might have been impacted by stopping criterion) """ self._epochs = epochs # try to load previously saved checkpoint from filepath self._checkpointer = tf.train.Checkpoint(optimizer=self._optimizer, model=self._model) if checkpoint_dir is not None: if not os.path.exists(checkpoint_dir): os.makedirs(checkpoint_dir) filepath = os.path.join(checkpoint_dir, "{epoch:04d}.ckpt") latest_ckpt = tf.train.latest_checkpoint(checkpoint_dir) if latest_ckpt: self._checkpointer.restore(latest_ckpt) elif tensorboard: # create tensorboard log files in default location checkpoint_dir = "./tb/" tb_writer = tf.summary.create_file_writer(checkpoint_dir) tb_writer.set_as_default() else: filepath = None self._tb = tensorboard # train generator train_gen = train_ts_generator( self._model, self._ts_obj, self._batch_size, self._train_window, verbose=self._verbose, ) # validation generator if validation: val_gen = train_ts_generator( self._model, self._ts_obj, self._batch_size, self._train_window, verbose=self._verbose, val_set=True, ) else: val_gen = None # Iterate over epochs. return self._training_loop( filepath, train_gen, val_gen, epochs=epochs, steps_per_epoch=steps_per_epoch, early_stopping=early_stopping, stopping_patience=stopping_patience, stopping_delta=stopping_delta, ) def _add_prev_target( self, x_test: tf.Variable, prev_target: tf.Tensor) -> typing.Tuple[tf.Variable, tf.Tensor]: """ private util function that replaces the previous target column in input data with dynamically generated last target """ x_test_new = x_test[0][:, :1, -1:].assign(prev_target) return [x_test_new, x_test[1]] def _softplus( self, mu: tf.Tensor, scale: tf.Tensor, ) -> typing.Tuple[tf.Tensor, tf.Tensor]: """ private util function that applies softplus transformation to various parameters depending on type of data """ scale = softplus(scale) if self._ts_obj.count_data: mu = softplus(mu) return mu, scale def _squeeze( self, mu: tf.Tensor, scale: tf.Tensor, squeeze_dims: typing.List[int] = [2] ) -> typing.Tuple[tf.Tensor, tf.Tensor]: """ private util function that squeezes predictions along certain dimensions depending on whether we are predicting in-sample or out-of-sample """ return tf.squeeze(mu, squeeze_dims), tf.squeeze(scale, squeeze_dims) def _negative_binomial(self, mu: tf.Tensor, scale: tf.Tensor, samples: int = 1) -> np.ndarray: """ private util function that draws n samples from a negative binomial distribution parameterized by mu and scale parameters based on implementation from GluonTS library: https://gluon-ts.mxnet.io/_modules/gluonts/distribution/neg_binomial.html#NegativeBinomial """ tol = 1e-5 r = 1.0 / scale theta = scale * mu r = tf.math.minimum(tf.math.maximum(tol, r), 1e10) theta = tf.math.minimum(tf.math.maximum(tol, theta), 1e10) p = 1 / (theta + 1) return np.random.negative_binomial(r, p, samples) def _draw_samples( self, mu_tensor: tf.Tensor, scale_tensor: tf.Tensor, point_estimate: int = False, samples: int = 1, ) -> typing.List[typing.List[np.ndarray]]: """ private util function that draws samples from appropriate distribution """ # shape : [# test groups, samples] if point_estimate: return [np.repeat(mu, samples) for mu in mu_tensor] elif self._ts_obj.count_data: return [ list(self._negative_binomial(mu, scale, samples)) for mu, scale in zip(mu_tensor, scale_tensor) ] else: return [ list(np.random.normal(mu, scale, samples)) for mu, scale in zip(mu_tensor, scale_tensor) ] def _reset_lstm_states(self): """ private util function to reset lstm states, dropout mask, and recurrent dropout mask """ def predict( self, test_ts_obj: TimeSeriesTest, point_estimate: bool = True, horizon: int = None, samples: int = 100, include_all_training: bool = False, return_in_sample_predictions: bool = True, ) -> np.ndarray: """ predict horizon steps into the future Arguments: test_ts_obj {TimeSeriesTest} -- time series object for prediction Keyword Arguments: point_estimate {bool} -- if True, always sample mean of distributions, otherwise sample from (mean, scale) parameters (default: {True}) horizon {int} -- optional, can specify prediction horizon into future (default: {None}) samples {int} -- how many samples to draw to calculate confidence intervals (default: {100}) include_all_training {bool} -- whether to start calculating hidden states from beginning of training data, alternative is from t_0 - train_window (default: {False}) return_in_sample_predictions {bool} -- whether to return in sample predictions as well as future predictions (default: {True}) Returns: np.ndarray -- predictions, shape is (# unique test groups, horizon, # samples) """ assert samples > 0, "The number of samples to draw must be positive" # test generator test_gen = test_ts_generator( self._model, test_ts_obj, self._batch_size, self._train_window, include_all_training=include_all_training, verbose=self._verbose, ) # reset lstm states before prediction self._model.get_layer("lstm").reset_states() # make sure horizon is legitimate value if horizon is None or horizon > test_ts_obj.horizon: horizon = test_ts_obj.horizon # forward test_samples = [[] for _ in range(len(test_ts_obj.test_groups))] start_time = time.time() prev_iteration_index = 0 for batch_idx, batch in enumerate(test_gen): x_test, scale_keys, horizon_idx, iteration_index = batch if iteration_index is None: break if horizon_idx == horizon: test_ts_obj.batch_idx = 0 test_ts_obj.iterations += 1 continue # reset lstm states for new sequence of predictions through time if iteration_index != prev_iteration_index: self._model.get_layer("lstm").reset_states() # don't need to replace for first test batch bc have tgt from last training example if horizon_idx > 1: # add one sample from previous predictions to test batches # all dim 0, first row of dim 1, last col of dim 3 x_test = self._add_prev_target(x_test, mu[:, :1, :]) # make predictions mu, scale = self._model(x_test) mu, scale = self._softplus(mu, scale) # unscale scaled_mu, scaled_scale = unscale( mu[:scale_keys.shape[0]], scale[:scale_keys.shape[0]], scale_keys, self._lookup_table, ) # in-sample predictions (ancestral sampling) if horizon_idx <= 0: if horizon_idx % 5 == 0: logger.info( f"Making in-sample predictions with ancestral sampling. {-horizon_idx} batches remaining" ) # squeeze 2nd dim - output_dim scaled_mu, scaled_scale = self._squeeze( scaled_mu, scaled_scale) for mu, scale, sample_list in zip( scaled_mu, scaled_scale, test_samples[iteration_index * self._batch_size:(iteration_index + 1) * self._batch_size], ): draws = self._draw_samples(mu, scale, point_estimate=point_estimate, samples=samples) sample_list.extend(draws) # draw samples from learned distributions for test samples else: if horizon_idx % 5 == 0: logger.info( f"Making future predictions. {horizon-horizon_idx} batches remaining" ) # get first column predictions (squeeze 1st dim - horizon) scaled_mu, scaled_scale = scaled_mu[:, : 1, :], scaled_scale[:, : 1, :] # slice at number of unique ts and squeeze 1st dim - horizon, 2nd dim - output_dim) squeezed_mu, squeezed_scale = self._squeeze( scaled_mu, scaled_scale, squeeze_dims=[1, 2], ) # concatenate draws = self._draw_samples( squeezed_mu, squeezed_scale, point_estimate=point_estimate, samples=samples, ) for draw_list, sample_list in zip( draws, test_samples[iteration_index * self._batch_size:(iteration_index + 1) * self._batch_size], ): sample_list.append(draw_list) # reset batch idx and iterations_index so we can call predict() multiple times test_ts_obj.batch_idx = 0 test_ts_obj.iterations = 0 test_ts_obj.batch_test_data_prepared = False # filter test_samples depending on return_in_sample_predictions param if return_in_sample_predictions: pred_samples = np.array(test_samples)[:, -(self._ts_obj.max_age + horizon):, :] else: pred_samples = np.array(test_samples)[:, -horizon:, :] logger.info( f"Inference ({samples} sample(s), {horizon} timesteps) took {round(time.time() - start_time, 0)}s" ) # Shape [# test_groups, horizon, samples] # TODO [# test_groups, horizon, samples, output_dim] not supported yet return pred_samples
class CycleGAN(object): def __init__(self, args): self.batch_size = args.batch_size self.time_step = args.time_step # number of time steps self.pitch_range = args.pitch_range # number of pitches self.input_c_dim = args.input_nc # number of input image channels self.output_c_dim = args.output_nc # number of output image channels self.lr = args.lr self.L1_lambda = args.L1_lambda self.gamma = args.gamma self.sigma_d = args.sigma_d self.dataset_A_dir = args.dataset_A_dir self.dataset_B_dir = args.dataset_B_dir self.d_loss_path = args.d_loss_path self.g_loss_path = args.g_loss_path self.cycle_loss_path = args.cycle_loss_path self.sample_dir = args.sample_dir self.model = args.model self.discriminator = build_discriminator self.generator = build_generator self.criterionGAN = mae_criterion OPTIONS = namedtuple( "OPTIONS", "batch_size " "time_step " "input_nc " "output_nc " "pitch_range " "gf_dim " "df_dim " "is_training", ) self.options = OPTIONS._make( ( args.batch_size, args.time_step, args.pitch_range, args.input_nc, args.output_nc, args.ngf, args.ndf, args.phase == "train", ) ) self.now_datetime = get_now_datetime() self.pool = ImagePool(args.max_size) self._build_model(args) print("Initialized model.") def _build_model(self, args): # Generator self.generator_A2B = self.generator(self.options, name="Generator_A2B") self.generator_B2A = self.generator(self.options, name="Generator_B2A") # Discriminator self.discriminator_A = self.discriminator(self.options, name="Discriminator_A") self.discriminator_B = self.discriminator(self.options, name="Discriminator_B") if self.model != "base": self.discriminator_A_all = self.discriminator( self.options, name="Discriminator_A_all" ) self.discriminator_B_all = self.discriminator( self.options, name="Discriminator_B_all" ) # Discriminator and Generator Optimizer if args.optimizer == "adam": self.DA_optimizer = Adam(self.lr, beta_1=args.beta1) self.DB_optimizer = Adam(self.lr, beta_1=args.beta1) self.GA2B_optimizer = Adam(self.lr, beta_1=args.beta1) self.GB2A_optimizer = Adam(self.lr, beta_1=args.beta1) elif args.optimizer == "rmsprop": self.DA_optimizer = RMSprop(self.lr, momentum=args.beta1) self.DB_optimizer = RMSprop(self.lr, momentum=args.beta1) self.GA2B_optimizer = RMSprop(self.lr, momentum=args.beta1) self.GB2A_optimizer = RMSprop(self.lr, momentum=args.beta1) elif args.optimizer == "sgd": self.DA_optimizer = SGD(self.lr, momentum=args.beta1) self.DB_optimizer = SGD(self.lr, momentum=args.beta1) self.GA2B_optimizer = SGD(self.lr, momentum=args.beta1) self.GB2A_optimizer = SGD(self.lr, momentum=args.beta1) # Checkpoints model_name = "cyclegan.model" model_dir = args.model_dir self.checkpoint_dir = os.path.join(args.checkpoint_dir, model_dir, model_name) if not os.path.exists(self.checkpoint_dir): os.makedirs(self.checkpoint_dir) self.checkpoint = tf.train.Checkpoint( generator_A2B_optimizer=self.GA2B_optimizer, generator_B2A_optimizer=self.GB2A_optimizer, discriminator_A_optimizer=self.DA_optimizer, discriminator_B_optimizer=self.DB_optimizer, generator_A2B=self.generator_A2B, generator_B2A=self.generator_B2A, discriminator_A=self.discriminator_A, discriminator_B=self.discriminator_B, ) self.checkpoint_manager = tf.train.CheckpointManager( self.checkpoint, self.checkpoint_dir, max_to_keep=5 ) print("Built model.") def train(self, args): # Data from domain A and B, and mixed dataset for partial and full models. dataA = glob("./{}/train/*.*".format(self.dataset_A_dir)) dataB = glob("./{}/train/*.*".format(self.dataset_B_dir)) if args.continue_train: if self.checkpoint.restore(self.checkpoint_manager.latest_checkpoint): print(" [*] Load checkpoint succeeded!") else: print(" [!] Load checkpoint failed.") counter = 1 start_time = time.time() d_loss_list = [] g_loss_list = [] cycle_loss_list = [] print("Training start...") for epoch in range(args.epoch): # Shuffle training data np.random.shuffle(dataA) np.random.shuffle(dataB) # Get the proper number of batches batch_idxs = min(len(dataA), len(dataB)) // self.batch_size # learning rate starts to decay when reaching the threshold self.lr = ( self.lr if epoch < args.epoch_step else self.lr * (args.epoch - epoch) / (args.epoch - args.epoch_step) ) for idx in range(batch_idxs): # To feed real_data batch_files = list( zip( dataA[idx * self.batch_size : (idx + 1) * self.batch_size], dataB[idx * self.batch_size : (idx + 1) * self.batch_size], ) ) batch_samples = [ load_npy_data(batch_file) for batch_file in batch_files ] batch_samples = np.array(batch_samples).astype( np.float32 ) # batch_size * 64 * 84 * 2 real_A, real_B = batch_samples[:, :, :, 0], batch_samples[:, :, :, 1] real_A = tf.expand_dims(real_A, -1) real_B = tf.expand_dims(real_B, -1) # generate gaussian noise for robustness improvement gaussian_noise = np.abs( np.random.normal( 0, self.sigma_d, [ self.batch_size, self.time_step, self.pitch_range, self.input_c_dim, ], ) ).astype(np.float32) with tf.GradientTape(persistent=True) as gen_tape, tf.GradientTape( persistent=True ) as disc_tape: fake_B = self.generator_A2B(real_A, training=True) cycle_A = self.generator_B2A(fake_B, training=True) fake_A = self.generator_B2A(real_B, training=True) cycle_B = self.generator_A2B(fake_A, training=True) [fake_A_sample, fake_B_sample] = self.pool([fake_A, fake_B]) DA_real = self.discriminator_A( real_A + gaussian_noise, training=True ) DB_real = self.discriminator_B( real_B + gaussian_noise, training=True ) DA_fake = self.discriminator_A( fake_A + gaussian_noise, training=True ) DB_fake = self.discriminator_B( fake_B + gaussian_noise, training=True ) DA_fake_sample = self.discriminator_A( fake_A_sample + gaussian_noise, training=True ) DB_fake_sample = self.discriminator_B( fake_B_sample + gaussian_noise, training=True ) # Generator loss cycle_loss = self.L1_lambda * ( abs_criterion(real_A, cycle_A) + abs_criterion(real_B, cycle_B) ) g_A2B_loss = ( self.criterionGAN(DB_fake, tf.ones_like(DB_fake)) + cycle_loss ) g_B2A_loss = ( self.criterionGAN(DA_fake, tf.ones_like(DA_fake)) + cycle_loss ) g_loss = g_A2B_loss + g_B2A_loss - cycle_loss # Discriminator loss d_A_loss_real = self.criterionGAN(DA_real, tf.ones_like(DA_real)) d_A_loss_fake = self.criterionGAN( DA_fake_sample, tf.zeros_like(DA_fake_sample) ) d_A_loss = (d_A_loss_real + d_A_loss_fake) / 2 d_B_loss_real = self.criterionGAN(DB_real, tf.ones_like(DB_real)) d_B_loss_fake = self.criterionGAN( DB_fake_sample, tf.zeros_like(DB_fake_sample) ) d_B_loss = (d_B_loss_real + d_B_loss_fake) / 2 d_loss = d_A_loss + d_B_loss # Calculate the gradients for generator and discriminator generator_A2B_gradients = gen_tape.gradient( target=g_A2B_loss, sources=self.generator_A2B.trainable_variables, ) generator_B2A_gradients = gen_tape.gradient( target=g_B2A_loss, sources=self.generator_B2A.trainable_variables, ) discriminator_A_gradients = disc_tape.gradient( target=d_A_loss, sources=self.discriminator_A.trainable_variables, ) discriminator_B_gradients = disc_tape.gradient( target=d_B_loss, sources=self.discriminator_B.trainable_variables, ) # Apply the gradients to the optimizer self.GA2B_optimizer.apply_gradients( zip( generator_A2B_gradients, self.generator_A2B.trainable_variables, ) ) self.GB2A_optimizer.apply_gradients( zip( generator_B2A_gradients, self.generator_B2A.trainable_variables, ) ) self.DA_optimizer.apply_gradients( zip( discriminator_A_gradients, self.discriminator_A.trainable_variables, ) ) self.DB_optimizer.apply_gradients( zip( discriminator_B_gradients, self.discriminator_B.trainable_variables, ) ) print( "=================================================================" ) print( ( "Epoch: [%2d] [%4d/%4d] time: %4.4f D_loss: %6.2f, G_loss: %6.2f, cycle_loss: %6.2f" % ( epoch, idx, batch_idxs, time.time() - start_time, d_loss, g_loss, cycle_loss, ) ) ) # ADDED d_loss_list.append(d_loss) g_loss_list.append(g_loss) cycle_loss_list.append(cycle_loss) counter += 1 # generate samples during training to track the learning process if np.mod(counter, args.print_freq) == 1: sample_dir = os.path.join( self.sample_dir, "{}2{}_{}_{}_{}".format( self.dataset_A_dir, self.dataset_B_dir, self.now_datetime, self.model, self.sigma_d, ), ) if not os.path.exists(sample_dir): os.makedirs(sample_dir) # to binary, 0 denotes note off, 1 denotes note on samples = [ to_binary(real_A, 0.5), to_binary(fake_B, 0.5), to_binary(cycle_A, 0.5), to_binary(real_B, 0.5), to_binary(fake_A, 0.5), to_binary(cycle_B, 0.5), ] self.sample_model( samples=samples, sample_dir=sample_dir, epoch=epoch, idx=idx ) if np.mod(counter, args.save_freq) == 1: self.checkpoint_manager.save(counter) pickle_loss_list(d_loss_list, self.d_loss_path) pickle_loss_list(g_loss_list, self.g_loss_path) pickle_loss_list(cycle_loss_list, self.cycle_loss_path) def test(self, args): """ --test_dir --checkpoint_dir --which_direction --model_dir """ if args.which_direction == "AtoB": sample_files = glob("./{}/test/*.*".format(self.dataset_A_dir)) elif args.which_direction == "BtoA": sample_files = glob("./{}/test/*.*".format(self.dataset_B_dir)) else: raise Exception("--which_direction must be AtoB or BtoA") sample_files.sort( key=lambda x: int(os.path.splitext(os.path.basename(x))[0].split("_")[-1]) ) checkpoint_filepath = tf.train.latest_checkpoint(args.checkpoint_dir) if checkpoint_filepath: status = self.checkpoint.restore(checkpoint_filepath) print(" [*] Load checkpoint succeeded!", checkpoint_filepath) else: print(" [!] Load checkpoint failed...") test_dir_mid = os.path.join( args.test_dir, "{}/{}/mid".format(args.model_dir, args.which_direction), ) if not os.path.exists(test_dir_mid): os.makedirs(test_dir_mid) test_dir_npy = os.path.join( args.test_dir, "{}/{}/npy".format(args.model_dir, args.which_direction), ) if not os.path.exists(test_dir_npy): os.makedirs(test_dir_npy) for idx in range(len(sample_files)): print("Processing midi: ", sample_files[idx]) sample_npy = np.load(sample_files[idx]) * 1.0 # save midis origin = sample_npy.reshape(1, sample_npy.shape[0], sample_npy.shape[1], 1) midi_path_origin = os.path.join( test_dir_mid, "{}_origin.mid".format(idx + 1) ) midi_path_transfer = os.path.join( test_dir_mid, "{}_transfer.mid".format(idx + 1) ) midi_path_cycle = os.path.join(test_dir_mid, "{}_cycle.mid".format(idx + 1)) if args.which_direction == "AtoB": transfer = self.generator_A2B(origin, training=False) cycle = self.generator_B2A(transfer, training=False) else: transfer = self.generator_B2A(origin, training=False) cycle = self.generator_A2B(transfer, training=False) save_midis(origin, midi_path_origin) save_midis(transfer, midi_path_transfer) save_midis(cycle, midi_path_cycle) # save npy files npy_path_origin = os.path.join(test_dir_npy, "origin") npy_path_transfer = os.path.join(test_dir_npy, "transfer") npy_path_cycle = os.path.join(test_dir_npy, "cycle") if not os.path.exists(npy_path_origin): os.makedirs(npy_path_origin) if not os.path.exists(npy_path_transfer): os.makedirs(npy_path_transfer) if not os.path.exists(npy_path_cycle): os.makedirs(npy_path_cycle) np.save( os.path.join(npy_path_origin, "{}_origin.npy".format(idx + 1)), origin ) np.save( os.path.join(npy_path_transfer, "{}_transfer.npy".format(idx + 1)), transfer, ) np.save(os.path.join(npy_path_cycle, "{}_cycle.npy".format(idx + 1)), cycle) def sample_model(self, samples, sample_dir, epoch, idx): print("Generating samples during learning...") if not os.path.exists(os.path.join(sample_dir, "B2A")): os.makedirs(os.path.join(sample_dir, "B2A")) if not os.path.exists(os.path.join(sample_dir, "A2B")): os.makedirs(os.path.join(sample_dir, "A2B")) save_midis( samples[0], "./{}/A2B/{:02d}_{:04d}_origin.mid".format(sample_dir, epoch, idx), ) save_midis( samples[1], "./{}/A2B/{:02d}_{:04d}_transfer.mid".format(sample_dir, epoch, idx), ) save_midis( samples[2], "./{}/A2B/{:02d}_{:04d}_cycle.mid".format(sample_dir, epoch, idx), ) save_midis( samples[3], "./{}/B2A/{:02d}_{:04d}_origin.mid".format(sample_dir, epoch, idx), ) save_midis( samples[4], "./{}/B2A/{:02d}_{:04d}_transfer.mid".format(sample_dir, epoch, idx), ) save_midis( samples[5], "./{}/B2A/{:02d}_{:04d}_cycle.mid".format(sample_dir, epoch, idx), )
class Train: def __init__(self, seqs, adj, nodes_features, epochs, key, use_gcn, batch_size, use_gru=True): self.epochs = epochs self.seqs = seqs.astype('float32') self.seqs_noised = seqs.copy().astype('float32') self.max_s = seqs[key].max() self.seqs_noised[key] = np.random.normal( self.max_s / 2.0, self.max_s / 10.0, size=(seqs.shape[0])).astype('float32') self.key = key self.gen_optimizer = SGD(lr, adam_beta_1) self.desc_optimizer = SGD(lr, adam_beta_1) self.adj = normalized_laplacian(adj.astype('float32')) self.adj_expanded = tf.expand_dims(normalized_laplacian( adj.astype('float32')), axis=0) self.nodes_features = nodes_features.astype('float32') self.nodes_f_expanded = tf.expand_dims( nodes_features.astype('float32'), axis=0) self.generator = model.make_generator('generator', batch_size, self.adj, self.nodes_features, use_gcn, use_gru) self.discriminator = model.make_discriminator('discriminator', batch_size, self.adj, self.nodes_features, use_gcn, use_gru) self.d_loss_fn, self.g_loss_fn = losses.get_wasserstein_losses_fn() self.wsst_hist = [] self.cos_simi = [] self.var_hist = [] self.rmse_hist = [] self.mae_hist = [] self.r2_hist = [] self.g_loss_hist = [] self.d_loss_hist = [] def __call__(self, epochs=None, save_path='generated/', batch_size=96, monitor=True): if not os.path.exists(save_path): os.makedirs(save_path) if epochs is None: epochs = self.epochs time_len = self.seqs.shape[0] total_batch = int(time_len / batch_size) time_consumed_total = 0. final_epoch = epochs stable = 0 for epoch in range(1, epochs + 1): start = time.time() total_gen_loss = 0 total_disc_loss = 0 for week in range(0, total_batch): current_seqs = self.seqs[week * batch_size:week * batch_size + batch_size] seqs_noised = self.seqs_noised[week * batch_size:week * batch_size + batch_size] max_s = current_seqs[self.key].max() seqs_noised[self.key] = np.random.normal( max_s / 2.0, max_s / 10.0, size=(current_seqs.shape[0])).astype('float32') gen_loss, disc_loss = self.train_step(current_seqs, seqs_noised, batch_size) total_gen_loss += gen_loss total_disc_loss += disc_loss time_consumed = time.time() - start time_consumed_total += time_consumed time_consumed_agv = time_consumed_total / epoch epochs_last = epochs - epoch estimate_time_last = epochs_last * time_consumed_agv if epoch % 1 == 0: metrics_ = self.evaluate(save_path, batch_size, epoch, plot_compare=False) self.wsst_hist.append(metrics_.get('WSSTD')) self.cos_simi.append(metrics_.get('COS_SIMI')) self.rmse_hist.append(metrics_.get('RMSE')) self.mae_hist.append(metrics_.get('MAE')) self.r2_hist.append(metrics_.get('R^2')) self.var_hist.append(metrics_.get('Var')) self.g_loss_hist.append( round(float(total_gen_loss / total_batch), 3)) self.d_loss_hist.append( round(float(total_disc_loss / total_batch), 3)) if epoch % evaluate_interval == 0: metrics_ = self.evaluate(save_path, batch_size, epoch) metrics_['gen_loss'] = round( float(total_gen_loss / total_batch), 3) metrics_['disc_loss'] = round( float(total_disc_loss / total_batch), 3) print('epoch {}/{}({})-{}, to finish: {}'.format( epoch, epochs, round(time_consumed_total, 2), json.dumps(metrics_, ensure_ascii=False), round(estimate_time_last, 2))) if not metrics_.keys().__contains__('RMSE'): metrics_['crash'] = True break if monitor and metrics_['WSSTD'] < 0.02 and metrics_[ 'RMSE'] < 0.09: stable += 1 if stable > 2: final_epoch = epoch break else: stable = 0 matrix_hist = pd.DataFrame() matrix_hist['wsst_hist'] = self.wsst_hist matrix_hist['cos_simi'] = self.cos_simi matrix_hist['rmse_hist'] = self.rmse_hist matrix_hist['mae_hist'] = self.mae_hist matrix_hist['r2_hist'] = self.r2_hist matrix_hist['var_hist'] = self.var_hist matrix_hist['g_loss_hist'] = self.g_loss_hist matrix_hist['d_loss_hist'] = self.d_loss_hist matrix_hist.to_csv(save_path + '/matrix_hist.csv', encoding='utf_8_sig', index=False) self.save_model(save_path, time_consumed_total) return time_consumed_total, final_epoch @tf.function def train_step(self, seqs, seqs_noised, batch_size): with tf.GradientTape(persistent=True) as tape: real_output = self.call_model(self.discriminator, seqs) generated = self.call_model(self.generator, seqs_noised) left = tf.slice(seqs, [0, 0], [batch_size, self.key]) right = tf.slice(seqs, [0, self.key + 1], [batch_size, -1]) combined = tf.concat([left, generated[0], right], 1) generated_output = self.call_model(self.discriminator, combined) loss_g = self.g_loss_fn(generated_output) loss_d = self.d_loss_fn(real_output, generated_output) loss_dif = abs(loss_d - loss_g) if loss_dif <= 10: self.apply_grad(self.generator, tape, loss_g) self.apply_grad(self.discriminator, tape, loss_d) else: if loss_d > loss_g: self.apply_grad(self.discriminator, tape, loss_d) else: self.apply_grad(self.generator, tape, loss_g) return loss_g, loss_d def apply_grad(self, d_or_g, tape, loss): grad = tape.gradient(loss, d_or_g.trainable_variables) self.desc_optimizer.apply_gradients( zip(grad, d_or_g.trainable_variables)) def call_model(self, model_, seqs): return model_(inputs=[ tf.expand_dims(seqs, axis=0), self.nodes_f_expanded, self.adj_expanded ]) def generate(self, real_seqs): seqs_replace = real_seqs.copy() max_s = seqs_replace[self.key].max() seqs_replace[self.key] = np.random.normal( max_s / 2.0, max_s / 10.0, size=(seqs_replace.shape[0])).astype('float32') gen_data = tf.squeeze(self.generator(tf.expand_dims(seqs_replace, axis=0), training=False), axis=0) return pd.DataFrame(gen_data.numpy()) def get_compare(self, start_day, batch_size): real_seqs = self.seqs[start_day:start_day + batch_size] self.seqs_noised[self.key] = np.random.normal( self.max_s / 2.0, self.max_s / 10.0, size=(self.seqs.shape[0])).astype('float32') noise_seq = self.seqs_noised[start_day:start_day + batch_size] generated_seqs = self.call_model(self.generator, noise_seq).numpy()[0] return real_seqs[self.key].values, generated_seqs def evaluate(self, save_path, batch_size, name=None, restore_model=False, plot_compare=True): if restore_model: self.load_model(save_path) start_day = 0 real, generated = self.get_compare(start_day, batch_size) if math.isnan(generated[0][0]): return dict() if plot_compare: utils.compare_plot(name, save_path, real, generated) return metrics.get_common_metrics( real.reshape(1, -1)[0], generated.reshape(1, -1)[0]) def load_model(self, save_path): print('try recover models from ' + save_path + '. ') self.generator.load_weights(save_path + '/model_generator_weight') self.discriminator.load_weights(save_path + '/model_discriminator_weight') print('models from ' + save_path + ' recovered. ') def save_model(self, save_path, time_consumed_total): self.generator.save_weights(save_path + '/model_generator_weight') self.discriminator.save_weights(save_path + '/model_discriminator_weight') print('models saved into path: ' + save_path + ', total time consumed: %s' % time_consumed_total)
class DINETrainer(object): def __init__(self, model, data, config): self.loss_fn = DVContinuousLoss(name="nll_loss") self.model = model self.param_num = tf.cast(model['train'].count_params(), tf.float64) self.data = data self.config = config self.learning_rate = ConstantScheduler(config.learning_rate) self.optimizer = SGD(config.learning_rate) self.global_step = tf.Variable(0, trainable=False, dtype=tf.int64) def build_metrics(): metric_pool = list() metric_pool.append( dict({ "metric": DVContinuous("D_xy"), "name": "mi", "weight_fn": mK.nan_mask, "target_fn": mK.split_identity_0, "pred_fn": mK.split_identity_0 })) metric_pool.append( dict({ "metric": DVContinuous("D_y"), "name": "mi", "weight_fn": mK.nan_mask, "target_fn": mK.split_identity_1, "pred_fn": mK.split_identity_1 })) metric_pool.append( dict({ "metric": DI("di"), "name": "mi", "weight_fn": mK.nan_mask, "target_fn": mK.identity, "pred_fn": mK.identity })) return metric_pool self.metrics = { "train": CustomMetrics(build_metrics(), config.train_writer, name='train'), "eval": CustomMetrics(build_metrics(), config.test_writer, name='eval') } def build_figures(): figure_pool = dict() figure_pool["di"] = Plot(name='di_progress') return figure_pool self.visualizer = Visualization( build_figures(), os.path.join(config.tensor_board_dir, "visual")) @staticmethod def metrics_data(t, t_): return t, t_ @staticmethod def visualize_aggr_data(t, t_): return None @staticmethod def visualize_plot_data(*args): visual_data = dict({"di": args[-1]}) return visual_data def reset_model_states(self): def reset_recursively(models): for model in models.values(): if isinstance(model, dict): reset_recursively(model) else: model.reset_states() reset_recursively(self.model) def sync_eval_model(self): w = self.model['train'].get_weights() self.model['eval'].set_weights(w) # @tf.function def compute_grads(self, x, y): with tf.GradientTape(persistent=True) as tape: T, T_ = self.model['train']([x, y], training=True) loss = self.loss_fn(T, T_) loss_xy, loss_y = tf.split(loss, num_or_size_splits=2) gradients_xy = tape.gradient(loss_xy, self.model['train'].trainable_weights) gradients_y = tape.gradient(loss_y, self.model['train'].trainable_weights) gradients = [ g_xy + g_y for g_xy, g_y in zip(gradients_xy, gradients_y) ] gradients, grad_norm = tf.clip_by_global_norm( gradients, self.config.clip_grad_norm) with self.config.train_writer.as_default(): tf.summary.scalar("loss_xy", tf.squeeze(loss_xy), self.global_step) tf.summary.scalar("loss_y", tf.squeeze(loss_y), self.global_step) tf.summary.scalar("loss", tf.squeeze(loss_xy) - tf.squeeze(loss_y), self.global_step) tf.summary.scalar("grad_norm", grad_norm, self.global_step) tf.summary.scalar("lr", self.learning_rate(self.global_step), self.global_step) self.global_step.assign_add(1) return gradients, T, T_ # @tf.function def apply_grads(self, gradients): self.optimizer.apply_gradients( zip(gradients, self.model['train'].trainable_weights)) # @tf.function def train_step(self, x, y): gradients, T, T_ = self.compute_grads(x, y) self.apply_grads(gradients) return T, T_ def eval_step(self, x): T, T_ = self.model['eval'](x, training=False) return T, T_ def train_epoch(self, epoch): self.metrics["train"].reset_states() self.reset_model_states() for x, y in self.data["train"]: if x is None: self.reset_model_states() continue T, T_ = self.train_step(x, y) self.metrics["train"].update_state(T, T_) self.metrics["train"].log_metrics(epoch) def evaluate(self, epoch, iterator="eval"): self.metrics["eval"].reset_states() self.visualizer.reset_states() self.sync_eval_model() self.reset_model_states() for k, (x, y) in enumerate(self.data[iterator]): T, T_ = self.eval_step([x, y]) self.metrics["eval"].update_state(*self.metrics_data(T, T_)) self.visualizer.update_state(self.visualize_aggr_data(T, T_)) results = self.metrics["eval"].log_metrics(epoch) self.visualizer.update_state(self.visualize_plot_data(*results)) self.visualizer.plot(epoch, save=True) def train(self): for epoch in range(self.config.num_epochs): if epoch % self.config.eval_freq == 0: self.evaluate(epoch) self.train_epoch(epoch) self.evaluate(self.config.num_epochs, iterator="long_eval")
class POTrainer: def __init__(self, actor, env, config): self.actor = actor self.env = env self.config = config self.T = config.unroll_steps self.input_dim = config.channel_cardinality - 1 self.model = self._build_training_model() lr_sche = keras.optimizers.schedules.ExponentialDecay( self.config.learning_rate, decay_steps=self.config.num_epochs // self.config.learning_rate_decay_steps, decay_rate=self.config.learning_rate_decay, staircase=False) self.optimizer = SGD(learning_rate=lr_sche) self.model.compile(optimizer=self.optimizer, loss=loss) self.mean_metric = Mean() @tf.function def split_rewards_and_state(self, x): return tf.split(x, axis=-1, num_or_size_splits=[1, self.input_dim]) def _build_training_model(self): def name(title, idx): return "{}_{:d}".format(title, idx) rewards = list() states = list() self.input = z = Input(shape=[self.input_dim]) for t in range(self.T): u = self.actor.model(z, training=True) z_u = Concatenate(axis=-1, name=name("concat_z_u", t))([z, u]) r, z_prime = self.env.model(z_u) rewards.append(r) z = z_prime states.append(z) self.rewards = Concatenate(axis=-1, name="concat_rewards")(rewards) self.states = Lambda(tf.stack, arguments={ 'axis': 1, 'name': "concat_states" })(states) return Model(inputs=self.input, outputs=[self.rewards, self.states]) @tf.function def train_epoch(self): raise NotImplementedError @tf.function def train_step(self, z): with tf.GradientTape() as tape: # training=True is only needed if there are layers with different # behavior during training versus inference (e.g. Dropout). rewards, states = self.model(z) loss = -K.mean(rewards) gradients = tape.gradient(loss, self.actor.model.trainable_weights) self.optimizer.apply_gradients( zip(gradients, self.actor.model.trainable_weights)) return -loss, states[:, -1, :] # train_loss(loss) # train_accuracy(labels, predictions) def train(self): def lr(k): return self.config.learning_rate * ( self.config.learning_rate_decay **(k // (self.config.num_epochs // self.config.learning_rate_decay_steps))) template = "Epoch: {:05d}\tLearning Rate: {:2.2e}\tAverage Reward: {:8.5f} " z = tf.random.uniform([self.config.batch_size, self.input_dim]) for k in range(self.config.num_epochs): if k % self.config.eval_freq == 0: average_reward, state_histogram = self.test( self.config.eval_len) print(template.format(k, lr(k), average_reward)) mlflow.log_metric("average_reward", float(average_reward), k) I, z = self.train_step(z) average_reward, state_histogram = self.test(self.config.eval_long_len) mlflow.log_metric("average_reward", float(average_reward), self.config.num_epochs) print("Epoch: {}\tAverage Reward: {:8.5f} ".format( "Final", average_reward)) state_clusters = KMeans( n_clusters=self.config.n_clusters).fit(state_histogram) with open(os.path.join(self.config.summary_dir, "log.txt"), 'a') as f: f.write("Clusters:\n") f.writelines( ['{}\n'.format(x) for x in state_clusters.cluster_centers_]) print(*['{}\n'.format(x) for x in state_clusters.cluster_centers_]) def test(self, eval_len): state_histogram = list() self.mean_metric.reset_states() z = tf.random.uniform([self.config.batch_size_eval, self.input_dim]) for k in range(eval_len): r, next_states = self.model.predict(z) z = tf.squeeze(next_states[:, -1, :]) if k > eval_len // 10: self.mean_metric(r) state_histogram.append(next_states) return self.mean_metric.result(), tf.reshape( tf.concat(state_histogram, axis=0), [-1, self.input_dim])
class ClassificationTrainer: def __init__(self, model, data, config): self.model_train = model['train'] self.model_test = model['eval'] self.data = data self.config = config self.loss_fn = ClassificationLoss(config) self.optimizer = SGD(learning_rate=self.config.learning_rate) self.metric_train = ClassificationMetrics(config.train_writer, name='train') self.metric_train_eval = ClassificationMetrics(config.train_writer, name='train_eval') self.metric_test = ClassificationMetrics(config.test_writer, name='test') self.global_step = tf.Variable(0, trainable=False, dtype=tf.int64) @tf.function def compute_grads(self, samples, targets): with tf.GradientTape() as tape: predictions = self.model_train(samples, training=True) ''' generate the targets and apply the corresponding loss function ''' loss = self.loss_fn(targets, predictions) gradients = tape.gradient(loss, self.model_train.trainable_weights) gradients, grad_norm = tf.clip_by_global_norm( gradients, self.config.clip_grad_norm) with self.config.train_writer.as_default(): tf.summary.scalar("grad_norm", grad_norm, self.global_step) self.global_step.assign_add(1) return gradients, predictions @tf.function def apply_grads(self, gradients): self.optimizer.apply_gradients( zip(gradients, self.model_train.trainable_weights)) def sync_eval_model(self): model_weights = self.model_train.get_weights() ma_weights = self.model_test.get_weights() alpha = self.config.moving_average_coefficient self.model_test.set_weights([ ma * alpha + w * (1 - alpha) for ma, w in zip(ma_weights, model_weights) ]) @tf.function def train_step(self, samples, targets): gradients, predictions = self.compute_grads(samples, targets) self.apply_grads(gradients) return predictions @tf.function def eval_step(self, samples): predictions = self.model_test(samples, training=False) return predictions def train_epoch(self, epoch): self.metric_train.reset_states() self.model_train.reset_states() for samples, targets in self.data['train']: predictions = self.train_step(samples, targets) self.metric_train.update_state(targets, predictions) self.sync_eval_model() self.metric_train.print(epoch) self.metric_train.log_metrics(epoch) def evaluate_train(self, epoch): self.metric_train_eval.reset_states() self.model_test.reset_states() for samples, targets in self.data['train_eval']: predictions = self.eval_step(samples) self.metric_train_eval.update_state(targets, predictions) self.metric_train_eval.print(epoch) self.metric_train_eval.log_metrics(epoch) def evaluate_test(self, epoch): self.metric_test.reset_states() self.model_test.reset_states() for samples, targets in self.data['test']: predictions = self.eval_step(samples) self.metric_test.update_state(targets, predictions) self.metric_test.print(epoch) self.metric_test.log_metrics(epoch) def train(self): for epoch in range(self.config.num_epochs): self.train_epoch(epoch) if epoch % self.config.eval_freq == 0: self.evaluate_train(epoch) self.evaluate_test(epoch)
def generate_adversarial(model, x_0, scale, margin, true_negative, max_iter=gin.REQUIRED, w_weight=gin.REQUIRED, border=gin.REQUIRED, h_x_0=gin.REQUIRED, h_x=gin.REQUIRED, mult=gin.REQUIRED, logloss=gin.REQUIRED, reversedlogloss=gin.REQUIRED): learning_rate = (mult * margin) / max_iter if true_negative: learning_rate = learning_rate * model._get_coef() optimizer = SGD( learning_rate=learning_rate ) # no momentum required due to smooth optimization landscape # x_init is perturbed x_0, with atmost 10% of a gradient step (which can be admittely quite high) x_init = x_0 + 0.1 * learning_rate * tf.math.l2_normalize( tf.random.uniform(x_0.shape, -1., 1.)) x = tf.Variable(initial_value=x_init, trainable=True) for _ in range(max_iter): with tf.GradientTape() as tape: try: y = model(x) except tf.python.framework.errors_impl.InvalidArgumentError as e: norm = tf.reduce_sum(x**2.) print('adversarial', norm, x) raise e if logloss: # binary cross-entropy zeros = tf.zeros([int(y.shape[0]), 1]) # seek frontiere ones = tf.ones([int(y.shape[0]), 1]) if reversedlogloss: zeros, ones = ones, zeros if true_negative: # where f is already negative, add examples ce = tf.nn.sigmoid_cross_entropy_with_logits( zeros + 0.5, y) else: # otherwise f is positive but really shouldn't (most of the time), we remove those parts ce = tf.nn.sigmoid_cross_entropy_with_logits(ones, y) adversarial_score = tf.reduce_mean(ce) if reversedlogloss: adversarial_score = -adversarial_score else: # wasserstein bro: sign is flipped because minimization of loss instead maximization of cost if true_negative: adversarial_score = tf.reduce_mean( y) # seek x of f(x) negative else: adversarial_score = -tf.reduce_mean( y) # seek x of f(x) positive loss = w_weight * adversarial_score if (h_x_0 + h_x + border) > 0.: fidelity = -h_x_0 * ball_repulsion( x, x_0, scale, margin) # avoid true positive, irrelevant dispersion = -h_x * metric_entropy( x, x, scale, margin) # regularization to cover space frontiere_score = border * frontiere_distance(y, margin) loss = loss + dispersion + fidelity + frontiere_score # minimize loss grad_f_x = tape.gradient(loss, [x]) grad_f_x = renormalize_grads(grad_f_x) # keep good learning rate optimizer.apply_gradients(zip(grad_f_x, [x])) return x.value()