def reconstruct_images(alg, images, columns, connect_threshold,
                       desired_activity_mult, min_overlap, img_shape,
                       out_dir=None):
    cols_shape = columns.shape
    # Initialize boost matrix.
    boost = np.ones(shape=cols_shape[:2])
    part_rbuffer = partial(RingBuffer,
                           input_array=np.zeros(1, dtype=np.bool), copy=True)
    # Initialize activity dictionary.
    activity = defaultdict(part_rbuffer)
    if alg == 'bsp':
        # Initialize overlap_sum dictionary.
        overlap_sum = defaultdict(part_rbuffer)

    distances = calculate_distances(cols_shape)
    # Calculate the inhibition_area parameter.
    inhibition_area = update_inhibition_area(columns, connect_threshold)
    # Calculate the desired activity in a inhibition zone.
    desired_activity = desired_activity_mult * inhibition_area

    reconstructions = np.zeros_like(images)
    # Initialize the activations matrix. This will be used to calculate the
    # population and lifetime kurtoses.
    activations = np.zeros(shape=(images.shape[0], cols_shape[0],
                                  cols_shape[1]), dtype=np.int)

    for i, image, _ in read_input(images):
        if alg == 'bsp':
            overlap, _ = bsp_overlap(image, columns, min_overlap,
                                     connect_threshold, boost, overlap_sum)
        elif alg == 'asp':
            # calculate the overlap of the columns with the image.
            # (this is a simple count of the number of its connected synapses
            # that are receiving active input (p. 3)), ...
            overlap = asp_overlap(image, columns, min_overlap,
                                  connect_threshold, boost)
        # force sparsity by inhibiting columns, ...
        active, _ = inhibit_columns(columns, distances, inhibition_area,
                                    overlap, activity, desired_activity)
        # set reconstructions[i][y, x] to (sic, p. 7):
        #    "[...] the linear superposition of the
        #     connected synapses of the columns that become active
        #     when the input is present [...]"
        # first, generate a copy of the columns array, where all the synapses
        # of all inactive columns are set to 0, ...
        active_cols = np.where(active, columns,
                               np.zeros(shape=(cols_shape[2], cols_shape[3])))
        # and then set reconstructions[i] to the linear superposition of the
        # synapses of the active columns.
        reconstructions[i] = np.nansum(active_cols, axis=(0, 1))
        # Store the post-inhibition overlap activity of each column as the
        # sum of the overlap of the active columns.
        activations[i] = np.nansum(columns, axis=(2, 3))

    if out_dir is not None:
        if not os.path.exists(out_dir):
            os.mkdir(out_dir)
        # Rebuild the 256x 256 images from the 16x16 patches.
        reconstructions = rebuild_imgs_from_patches(reconstructions, img_shape)
        # Scale the pixel values to the range [0, 1].
        reconstructions = reconstructions - reconstructions.min()
        reconstructions /= reconstructions.max() - reconstructions.min()
        # Scale the pixel values to the range [0, 255].
        reconstructions *= 255
        for i, reconstruct_img in enumerate(reconstructions):
            with open(os.path.join(out_dir, 'rec_img_%d.png' % i), 'wb') as fp:
                plt.imsave(fname=fp, arr=reconstruct_img, cmap='gray', )
    return activations
Example #2
0
def spatial_pooler(images, shape, p_connect=0.15, connect_threshold=0.2,
                   p_inc=0.02, p_dec=0.02, b_inc=0.005, p_mult=0.01,
                   min_activity_threshold=0.01, desired_activity_mult=0.05,
                   b_max=4, max_iterations=10000, cycles_to_save=100,
                   output_file=None):
    """
    Implements the main BSP loop (p. 3). It goes continually through the images
    set until convergence.
    :param images: set of images to learn from. It is an array of shape
                   (l, m, n) where (l, m) == (shape[0], shape[1]) and
                   (l, m) == (shape[2], shape[3])
    :param shape: the shape of the output array. It must have 4 components, and
                  (shape[0], shape[1]) == (shape[2], shape[3]).
    :param p_connect: probability of the columns mapped to [i, j] to have a
                      potential synapse to coordinates [k, l], for all i in
                      shape[0], j in shape[1], k in shape[2], and l in
                      shape[3].
    :param connect_threshold: threshold over which a potential synapse is
                              considered *connected*. All potential synapses
                              start with a permanence value within 0.1 of
                              this parameter.
    :param p_inc: the BSP'perm pInc parameter (p. 4). A float that indicates
                  the amount by which a synapse'perm permanence must be
                  incremented.
    :param p_dec: the BSP'perm pDec parameter (p. 4). A float that indicates
                  the amount by which a synapse'perm permanence must be
                  decremented.
    :param b_inc: the BSP'perm bInc parameter (p. 4). A float that indicates
                  the amount by which a column'perm boost must be incremented.
    :param p_mult: the BSP'perm pMult parameter (p. 4). A float that indicates
                   the amount by which a synapse'perm permanence must be
                   multiplied.
    :param min_activity_threshold: the BSP's minActivityThreshold parameter
                                   (p. 4).
    :param desired_activity_mult: the BSP's desiredActivityMult parameter
                                  (p. 4).
    :param b_max: the ASP'perm bMax parameter (p. 6). A float that indicates
                  the amount by which a synapse'perm permanence must be
                  multiplied.
    :param max_iterations: an integer indicating the maximum number of runs
                           through the set of images allowed. Pass None if no
                           limit is desired.
    :param cycles_to_save: wait this number of iterations over the complete set
                           of images before saving the columns to disk.
    :param output_file: file name used to save the pickled columns.
    :return: a matrix *columns* of shape *shape*, created and modified
             according to the BSP learning algorithm.
    """
    # Initialize boost matrix.
    boost = np.ones(shape=shape[:2])
    part_rbuffer = partial(RingBuffer,
                           input_array=np.zeros(1000, dtype=np.bool),
                           copy=True)
    # Initialize activity dictionary.
    activity = defaultdict(part_rbuffer)
    # Initialize columns and distances matrices.
    pprint("Initializing synapses ...")
    columns, distances = initialise_synapses(shape, p_connect,
                                             connect_threshold)
    pprint("Columns:")
    random_rows = np.random.randint(0, shape[0], 2)
    random_cols = np.random.randint(0, shape[1], 2)
    pprint(columns[random_rows, random_cols])
    pprint("Distances:")
    pprint(distances[random_rows, random_cols])
    # Calculate the inhibition_area parameter.
    pprint("Calculating inhibition area ...")
    inhibition_area = update_inhibition_area(columns, connect_threshold)
    pprint("Inhibition area: %s" % inhibition_area)
    # Calculate the desired activity in a inhibition zone.
    pprint("Calculating desired activity ...")
    desired_activity = desired_activity_mult * inhibition_area
    pprint("Desired activity: %s" % desired_activity)

    converged = False
    i = 0
    # While synapses are modified and the maximum number of iterations is not
    # overstepped, ...
    pprint("Starting learning loop ...")
    start = datetime.now()
    while not converged and (max_iterations is None or i < max_iterations):
        # Initialize the synapses_modified array, assuming no synapses will be
        # modified.
        synapses_modified = np.zeros(shape=len(images), dtype=np.bool)
        # For each image *image*, with index *j* in the images set, ...
        for j, image, _ in read_input(images):
            # According to the paper (sic):
            #   "minOverlap was dynamically set to be the product of the mean
            #    pixel intensity of the current image and the mean number of
            #    connected synapses for an individual column."
            # This leaves unclear exactly what is meant by "mean number of
            # connected synapses for an individual column"; it could be a
            # historical mean or a mean over all columns, here the latter was
            # chosen.
            mean_conn_synapses = (columns[columns > connect_threshold].size /
                                  (shape[2] * shape[3]))
            min_overlap = image.mean() * mean_conn_synapses
            # calculate the overlap of the columns with the image.
            # (this is a simple count of the number of its connected synapses
            # that are receiving active input (p. 3)), ...
            overlap = calculate_overlap(image, columns, min_overlap,
                                        connect_threshold, boost)
            # force sparsity by inhibiting columns, ...
            active, activity =\
                inhibit_columns(columns, distances, inhibition_area,
                                overlap, activity, desired_activity)
            # calculate the min_activity matrix, ...
            min_activity =\
                calculate_min_activity(columns, active, distances,
                                       inhibition_area, activity,
                                       min_activity_threshold)
            # and finally, adapt the synapse's permanence values.
            columns, synapses_modified[j] =\
                learn_synapse_connections(columns, active, image, p_inc,
                                          p_dec, activity, min_activity, boost,
                                          b_inc, p_mult, connect_threshold,
                                          distances, b_max)
            # Update the inhibition_area parameter.
            inhibition_area = update_inhibition_area(columns,
                                                     connect_threshold)
            # Update the desired activity in a inhibition zone.
            desired_activity = desired_activity_mult * inhibition_area
            # Print a snapshot of the model state every 1000 images.
            if j % 1000 == 0:
                pprint("########## %sth image of %sth iteration ##########" %
                       (j+1, i+1))
                elapsed = datetime.now() - start
                elapsed_h = elapsed.total_seconds() // 3600
                elapsed_m = (elapsed.total_seconds() // 60) % 60
                elapsed_s = elapsed.seconds % 60
                pprint("########## Elapsed time: %02d:%02d:%02d ##########" %
                       (elapsed_h, elapsed_m, elapsed_s))
                pprint("Overlap:")
                pprint(overlap[random_rows, random_cols])
                pprint("Activity:")
                for l, key in enumerate(activity.iterkeys()):
                    if l in random_rows:
                        pprint(activity[key][-100:])
                pprint("Active:")
                pprint(active[random_rows, random_cols])
                pprint("Min activity:")
                pprint(min_activity[random_rows, random_cols])
                pprint("Inhibition area: %s" % inhibition_area)
                pprint("Inhibition radius: %s" %
                       (np.sqrt(inhibition_area/np.pi),))
                pprint("Desired activity: %s" % desired_activity)
                pprint("Synapses modified: %s" % synapses_modified[j])
        # Check if any synapses changed from connected to disconnected or
        # vice-versa in the last learning cycle.
        converged = test_for_convergence(synapses_modified)
        pprint("Iteration %s. Number of synapses modified: %s" %
               (i, synapses_modified.sum()))
        if i % cycles_to_save == 0 or converged:
            if output_file is not None:
                with open(output_file, 'wb') as fp:
                    pickle.dump(columns, fp)
        # Increment iterations counter.
        i += 1

    return columns