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
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