Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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)
Beispiel #4
0
# 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)