def generate_cluster_centers(num_clusters, hidden_state_dim, input_dim, cluster_center_dist_lower_bound, diagonalizable=True): """Generates cluster center eigenvalues with distance requirement. The generated eigenvalues are drawn uniformly from (-1, 1) until the pairwise distance between cluster center eigenvalues >= lower bound. """ min_cluster_center_dist = -1. while min_cluster_center_dist < cluster_center_dist_lower_bound: cluster_centers = [] for _ in xrange(num_clusters): c = lds.generate_linear_dynamical_system( hidden_state_dim, input_dim, diagonalizable=diagonalizable) cluster_centers.append(c) min_cluster_center_dist = np.inf for s1 in xrange(num_clusters): for s2 in xrange(s1 + 1, num_clusters): d = lds.eig_dist(cluster_centers[s1], cluster_centers[s2]) if d < min_cluster_center_dist: min_cluster_center_dist = d logging.info('generated min_cluster_center_dist %.2f', min_cluster_center_dist) return cluster_centers
def generate_lds_clusters(cluster_centers, num_systems, cluster_radius, diagonalizable=True): """Generates clusters of linear dynamical systems. Args: cluster_centers: A list of LinearDynamicalSystem instances. num_systems: Total number of systems in all clusters. cluster_radius: Desired mean distance from the centers. Returns: - A list of LinearDynamicalSystem of size num_systems. - A list of true cluster ids. """ num_clusters = len(cluster_centers) cluster_id = np.random.randint(0, num_clusters, num_systems) hidden_state_dim = cluster_centers[0].hidden_state_dim for c in cluster_centers: if c.hidden_state_dim != hidden_state_dim: raise ValueError('Hidden state dimension mismatch.') generated_systems = [] dist_to_center = np.zeros(num_systems) for i in xrange(num_systems): c = cluster_centers[cluster_id[i]] if diagonalizable: eigvalues_new = c.get_spectrum() + cluster_radius / np.sqrt( hidden_state_dim) * np.random.randn(hidden_state_dim) generated_systems.append( lds.generate_linear_dynamical_system(hidden_state_dim, eigvalues=eigvalues_new)) else: transition_matrix = c.transition_matrix + cluster_radius / np.sqrt( hidden_state_dim) * np.random.randn(hidden_state_dim, hidden_state_dim) generated_systems.append( lds.LinearDynamicalSystem( transition_matrix, np.random.randn(c.input_matrix.shape[0], c.input_matrix.shape[1]), np.random.randn(c.output_matrix.shape[0], c.output_matrix.shape[1]))) dist_to_center[i] = lds.eig_dist(c, generated_systems[-1]) # For logging purpose. dist_bw_centers = np.zeros((num_clusters, num_clusters)) for i in xrange(num_clusters): for j in xrange(num_clusters): dist_bw_centers[i, j] = lds.eig_dist(cluster_centers[i], cluster_centers[j]) logging.info('Distances between cluster centers:\n%s', str(dist_bw_centers)) logging.info('Average distance from cluster centers: %.3f', np.average(dist_to_center)) for i in xrange(num_clusters): logging.info('Eigenvalues of cluster center %d: %s', i, str(cluster_centers[i].get_spectrum())) return generated_systems, cluster_id
def get_results_seq_len(given_true_eig, hidden_dim, input_dim, min_seq_len, max_seq_len, num_sampled_seq_len, num_repeat, input_mean, input_stddev, output_noise_stddev, init_state_mean=0.0, init_state_stddev=0.0, generate_diagonalizable_only=False, random_seed=0): """Get results for varying sequence lengths. Args: given_true_eig: Ground truth of eigenvalues. If None, generate random eigenvalues from uniform [-1,1] in each repeat of experiment. hidden_dim: Assumed hidden dim. If 0, use true hidden dim. input_dim: The input dim. min_seq_len: Min seq len in experiments. max_seq_len: Max seq len in experiments. num_sampled_seq_len: Number of sampled seq len values in between min and max seq len. num_repeat: Number of repeated experiments for each seq_len. input_mean: Scalar or 1D array of length hidden state dim. input_stddev: Scalar of 1D array of length hidden state dim. output_noise_stddev: Scalar. init_state_mean: Scalar or 1D array of length hidden state dim. init_state_stddev: Scalar of 1D array of length hidden state dim. generate_diagonalizable_only: Whether to only use diagonalizable LDSs in simulations. random_seed: Random seed, integer. Returns: A pandas DataFrame with columns `method`, `seq_len`, `t_secs`, `failed_ratio`, and `l2_r_error`. The same method and seq_len will appear in num_repeat many rows. """ np.random.seed(random_seed) progress_bar = tqdm.tqdm(total=num_repeat * num_sampled_seq_len) gen = lds.SequenceGenerator(input_mean=input_mean, input_stddev=input_stddev, output_noise_stddev=output_noise_stddev, init_state_mean=init_state_mean, init_state_stddev=init_state_stddev) # seq_len_vals = np.linspace(min_seq_len, max_seq_len, num_sampled_seq_len) # seq_len_vals = [int(round(x)) for x in seq_len_vals] min_inv_sqrt_seq_len = 1. / np.sqrt(max_seq_len) max_inv_sqrt_seq_len = 1. / np.sqrt(min_seq_len) inv_sqrt_seq_len_vals = np.linspace(min_inv_sqrt_seq_len, max_inv_sqrt_seq_len, num_sampled_seq_len) seq_len_vals = [int(round(1. / (x * x))) for x in inv_sqrt_seq_len_vals] learning_fns = create_learning_fns(hidden_dim) metric_dict = { k: [] for k in [ 'method', 'seq_len', 't_secs', 'l2_a_error', 'l2_r_error', 'failed_convg' ] } for _ in xrange(num_repeat): if given_true_eig is not None: ground_truth = lds.generate_linear_dynamical_system( hidden_dim, input_dim, eigvalues=given_true_eig) else: ground_truth = lds.generate_linear_dynamical_system( hidden_dim, input_dim, diagonalizable=generate_diagonalizable_only) true_eig = ground_truth.get_spectrum() for seq_len in seq_len_vals: seq = gen.generate_seq(ground_truth, seq_len=seq_len) for k, fn in learning_fns.iteritems(): start_t = timeit.default_timer() with warnings.catch_warnings(record=True) as caught: warnings.filterwarnings( 'always', category=sm_exceptions.ConvergenceWarning) if FLAGS.hide_inputs: eig_pred = fn(seq.outputs, None) else: eig_pred = fn(seq.outputs, seq.inputs) t_elapsed = timeit.default_timer() - start_t metric_dict['seq_len'].append(seq_len) metric_dict['method'].append(k) metric_dict['t_secs'].append(t_elapsed) metric_dict['l2_a_error'].append( np.linalg.norm(true_eig - eig_pred)) metric_dict['l2_r_error'].append( np.linalg.norm(true_eig - eig_pred) / np.linalg.norm(true_eig)) metric_dict['failed_convg'].append(False) for w in caught: if w.category in [ RuntimeWarning, sm_exceptions.ConvergenceWarning, sm_exceptions.HessianInversionWarning ]: metric_dict['failed_convg'][-1] = True else: warnings.warn(w.message, w.category) progress_bar.update(1) progress_bar.close() return pd.DataFrame(data=metric_dict)
# See the License for the specific language governing permissions and # limitations under the License. """Script to plot correlation between distances.""" import lds import matplotlib.pyplot as plt import numpy as np import seaborn as sns sns.set(style='whitegrid') num_pairs = 100 plt.figure(figsize=(8, 4)) plt.subplot(1, 2, 1) hidden_state_dim = 2 lds_pairs = [(lds.generate_linear_dynamical_system(hidden_state_dim), lds.generate_linear_dynamical_system(hidden_state_dim)) for i in xrange(num_pairs)] lds_distances = [ lds.eig_dist(system1, system2) for (system1, system2) in lds_pairs ] expected_ar_distances = [ np.linalg.norm(system1.get_expected_arparams() - system2.get_expected_arparams()) for (system1, system2) in lds_pairs ] print(np.corrcoef(lds_distances, expected_ar_distances)[0, 1]) ax = sns.regplot(x=lds_distances, y=expected_ar_distances) ax.set(xlabel='l-2 distance b/w eigenvalues', ylabel='l-2 distance b/w ' 'corresponding AR params',
def get_results(cluster_center_eigvalues, cluster_center_dist_lower_bound, hidden_state_dim, input_dim, guessed_hidden_dim, num_clusters, guessed_num_clusters, min_seq_len, max_seq_len, num_sampled_seq_len, num_repeat, num_systems, cluster_radius, input_mean, input_stddev, output_noise_stddev, init_state_mean=0.0, init_state_stddev=0.0, generate_diagonalizable_only=False, random_seed=0, results_path=None): """Get results for varying sequence lengths. Args: cluster_center_eigvalues: List of lists of eigenvalues for each cluster. E.g. [[0.9,0.1], [0.5,0.1], [0.2,0.2], or None. If None, eigenvalues will be generated from uniform(-1,1) with respect to cluster_center_dist_lower_bound. cluster_center_dist_lower_bound: Desired distance lower bound between clusters. When generating cluster centers, try repeatedly until distance is greater than cluster_center_dist_lower_bound. hidden_state_dim: True hidden state dim. input_dim: The input dim. guessed_hidden_dim: Assumed hidden dim. If 0, use true hidden dim. num_clusters: True number of clusters. guessed_num_clusters: Desired number of clusters. If 0, use true number. min_seq_len: Min seq len in experiments. max_seq_len: Max seq len in experiments. num_sampled_seq_len: Number of sampled seq len values in between min and max seq len. num_repeat: Number of repeated experiments for each seq_len. num_systems: Number of dynamical system in each clustering experiments. cluster_radius: Expected distance of generated systems from cluster centers. input_mean: Scalar or 1D array of length hidden state dim. input_stddev: Scalar of 1D array of length hidden state dim. output_noise_stddev: Scalar. init_state_mean: Scalar or 1D array of length hidden state dim. init_state_stddev: Scalar of 1D array of length hidden state dim. random_seed: Random seed, integer. Returns: A pandas DataFrame with columns `method`, `seq_len`, `t_secs`, `failed_ratio`, and columns for clustering metrics such as `adj_mutual_info` and `v_measure`. The same method and seq_len will appear in num_repeat many rows. """ if cluster_center_eigvalues is not None: if len(cluster_center_eigvalues) <= 1: raise ValueError('Need at least two cluster centers.') cluster_center_eigvalues = np.array(cluster_center_eigvalues) if cluster_center_eigvalues.shape != (num_clusters, hidden_state_dim): raise ValueError( 'Cluter center eig has shape %s, expected (%d, %d).' % (str(cluster_center_eigvalues.shape), num_clusters, hidden_state_dim)) np.random.seed(random_seed) progress_bar = tqdm.tqdm(total=num_repeat * num_sampled_seq_len) # Generator for output sequences. gen = lds.SequenceGenerator(input_mean=input_mean, input_stddev=input_stddev, output_noise_stddev=output_noise_stddev, init_state_mean=init_state_mean, init_state_stddev=init_state_stddev) seq_len_vals = np.linspace(min_seq_len, max_seq_len, num_sampled_seq_len) seq_len_vals = [int(round(x)) for x in seq_len_vals] if guessed_hidden_dim == 0: guessed_hidden_dim = hidden_state_dim if guessed_num_clusters == 0: guessed_num_clusters = num_clusters results_dfs = [] for i in xrange(num_repeat): logging.info('---Starting experiments in repeat run #%d---', i) if cluster_center_eigvalues is not None: cluster_centers = [] for eig_val in cluster_center_eigvalues: c = lds.generate_linear_dynamical_system(hidden_state_dim, input_dim, eigvalues=eig_val) cluster_centers.append(c) else: cluster_centers = clustering.generate_cluster_centers( num_clusters, hidden_state_dim, input_dim, cluster_center_dist_lower_bound, diagonalizable=generate_diagonalizable_only) true_systems, true_cluster_ids = clustering.generate_lds_clusters( cluster_centers, num_systems, cluster_radius, diagonalizable=generate_diagonalizable_only) for seq_len in seq_len_vals: logging.info('Running experiment with seq_len = %d.', seq_len) seqs = [gen.generate_seq(s, seq_len=seq_len) for s in true_systems] # Create transform_fns. model_fns = create_model_fns(guessed_hidden_dim) pca = sklearn.decomposition.PCA( n_components=guessed_hidden_dim).fit( np.stack([s.outputs.flatten() for s in seqs], axis=0)) model_fns['PCA'] = _create_pca_model_fn(pca) transform_fns = collections.OrderedDict() for k in model_fns: transform_fns[k] = _compose_model_fn(model_fns[k]) # Get clustering results. results_df = clustering.get_results( seqs, guessed_num_clusters, true_cluster_ids, true_systems, transform_fns, FLAGS.include_tslearn, include_slow_methods=FLAGS.include_tslearn_slow) results_df['seq_len'] = seq_len results_df['n_guessed_clusters'] = guessed_num_clusters results_df['n_true_clusters'] = num_clusters results_df['true_hidden_dim'] = hidden_state_dim results_df['guessed_hidden_dim'] = guessed_hidden_dim results_dfs.append(results_df) logging.info('Results:\n%s', str(results_df)) plot_filepath = os.path.join( FLAGS.output_dir, 'cluster_visualization_run_%d_seq_len_%d.png' % (i, seq_len)) if FLAGS.plot_clusters: clustering.visualize_clusters(seqs, true_systems, true_cluster_ids, transform_fns, plot_filepath) progress_bar.update(1) if results_path: with open(results_path, 'w+') as f: pd.concat(results_dfs).to_csv(f, index=False) progress_bar.close() return pd.concat(results_dfs)