# Experimenting with stacking Spatial Poolers

**URL:** <https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220>\
**Category:** Engineering\
**Created:** [July 1, 2019, 11:34am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220 "2019-07-01T11:34:58Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 1, 2019, 11:34am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/1 "2019-07-01T11:34:58Z")

</div>

I have been messing with stacking multiple layers of Spatial Poolers together and see what happens. I originally thought this is a bad idea, but heck, stacked RBMs are a thing. Why not Stacked Spatial Pooler?

The setup is simple.

1. Train a stack of N SpatialPoolers with random values between 0~1. Encoded using GridCell
2. Encode and send the value 0.5 to the stack of SPs and get the output.
3. Encode and send values from 0~1. See how many bits overlap with 0.5.

## Expectation

Since Spatial Pooler is almost a dimension reduction algorithm. I suppose like any dimension reduction algorithm, the more you do reduce, the less of information gets through and thus HTM should have a harder time to distinguish between values as I stack more and more SPs together. So, when I stacked enough SPs together, the plot of overlapping bits should become a flat line.

## Experiment

Here is the source code:

> <https://gist.github.com/marty1885/61eecaf605e258568bd28b0dc1d86a48>

You will need

1. [Etaler](https://github.com/etaler/Etaler) with at least the commit ab9f15d (latest as now)
2. [ROOT](https://root.cern.ch/) for plotting in C++

to run the experiment.

## Results

This is a plot of how many bits overlapped when there is no SP. The encoded value goes straight from encoder to comparison.

 ![plot_0](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/e/e087a331d65b0b60658922bfc5626ee8ac3726ef.png)

And with 1 SP. The plot looks more concentrated.

 ![plot_1](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/b/bb96a2f70c31544997e7ae72c0b936b2dfae24e4.png)

With 2 Spatial Poolers, it gets rectangular-ish

 ![plot_2](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/c/c9b32791d867aa9b45fe87056aa20c9c06b70de5.png)

With 4 Spatial Poolers, it gets really like a rectangle

 ![plot_4](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/f/f68c8077bb5b17e09e9282197a30f4573eb781fa.png)

At 8 Spatial Poolers, it becomes an rectangle. Also a funny thing I find is that the base of the plotted shape seems doesn’t widen up. Instead they contracts.

 ![plot_8](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/3/3366e8fdfdf373449e87d1341d7a1e2bb3947661.png)

And with 16 SPs. It is the same as with 8 SPs. Nothing changes. Why? Why does stacking SPs together concentrates the activation. This makes no seance mathematically.

 ![plot_16](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/f/f2cb8c04eead5d6dcf96d8ad1b7428bfb96f3bfd.png)

## Conclusion.

I was expecting the plot goes from a bell-shape initially (which I gets right) to a flat line as more and more SP are stacked together. But no, instead the values that can activate the final layer of SP gets more and more concentrated.

Maybe this is how the Neo cortex stabilizes and categorizes data? By using multiple layers of Spatial Poolers? Any comments or theory on why this is happening?

---

<div class="post-metadata">

**Author:** ![Jose\_Cueto](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/jose_cueto/32/4052_2.png) [@Jose\_Cueto](https://discourse.numenta.org/u/Jose_Cueto)\
**Post date:** [July 1, 2019, 1:12pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/2 "2019-07-01T13:12:31Z")

</div>

Awesome @marty1885. What is the output (input of next SP) of an SP in this case? Are they columns or the connected synapses?

---

<div class="post-metadata">

**Author:** ![rhyolight](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/rhyolight/32/3922_2.png) [@rhyolight](https://discourse.numenta.org/u/rhyolight)\
**Post date:** [July 1, 2019, 1:27pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/3 "2019-07-01T13:27:38Z")

</div>

This was a fun experiment!

> [@marty1885](#):
>
> Train a stack of N SpatialPoolers

How long did you train, and what were the perm increment / decrement settings? Are you training one SP and then stacking it? Or setting up the stack and training them all individually? I would be interested in seeing the results from untrained SPs as well.

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 1, 2019, 1:56pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/4 "2019-07-01T13:56:15Z")

</div>

> [@rhyolight](#):
>
> How long did you train, and what were the perm increment / decrement settings? Are you training one SP and then stacking it? Or setting up the stack and training them all individually? I would be interested in seeing the results from untrained SPs as well.

I trained the SPs 1000 times, each with a randomly generated value. And no, I stacked all the SPs together and then trained them all together.

As for using untrained SPs. It works as I originally expected - The amount of overlapping cells eventually flattened out.

 ![plot_1](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/c/cdccf1888ef3648168467237dbe11e579768dddb.png)  
 ![plot_2](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/6/628307b9032327af7ac784dec388f0d8808844ae.png)  
 ![plot_4](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/4/4e608a13ee34ad609909868a9589a32674092f7d.png)  
 ![plot_16](https://canada1.discourse-cdn.com/flex030/uploads/numenta/original/2X/0/009360bda5ab024f1279f89c7c72634263f3065a.png)

**Edit:** The perm inc/dec settings is exactly as same as it is in NuPIC by default. perm\_inc and perm\_dec = 0.1

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 1, 2019, 2:29pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/5 "2019-07-01T14:29:06Z")

</div>

> [@rhyolight](#):
>
> This was a fun experiment!

Personally I think the results have some practical implications. Maybe the cortex is doing this implicitly to decide what class of object it is processing, etc… Maybe the research team can provide a better explanation and take advantage of this?

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 1, 2019, 2:37pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/6 "2019-07-01T14:37:30Z")

</div>

> [@Jose\_Cueto](#):
>
> What is the output (input of next SP) of an SP in this case?

It works like how a Neural Network works. There is your input SDR `x0`, you send it into `sp1` and you get `x1`, you send `x1` to `sp2` so you get `x2`, etc, etc… until you have `xN-1`, send it to `spN`, get `xN`. And `xN` is the output of the entire stack.

---

<div class="post-metadata">

**Author:** ![Gary\_Gaulin](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/gary_gaulin/32/2248_2.png) [@Gary\_Gaulin](https://discourse.numenta.org/u/Gary_Gaulin)\
**Post date:** [July 1, 2019, 11:20pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/7 "2019-07-01T23:20:21Z")

</div>

> [@marty1885](#):
>
> There is your input SDR `x0` , you send it into `sp1` and you get `x1` , you send `x1` to `sp2` so you get `x2` , etc, etc… until you have `xN-1` , send it to `spN` , get `xN` . And `xN` is the output of the entire stack.

Nice work!

I possibly did essentially the same thing by connecting self-learning memory systems in series. RAM data from the first addresses the next RAM space.

My best guess is that the connection represents going from brain area to brain area. Each memory area tries to make sense of the one before it.

This might be interesting to try on visual information, similar to making a V1, V2, V3, but with all doing the same thing, like now, instead of trying to start from research papers on how the human brain divides up the problem.

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 2, 2019, 1:35am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/8 "2019-07-02T01:35:45Z")

</div>

> [@Gary\_Gaulin](#):
>
> This might be interesting to try on visual information

Any volunteers? I build Etaler for this exact purpose - performing research and handle large amount of computing. I’m out of time doing this sort of large experiments… But I’ll do my best to support. or maybe we could collaborate.

---

<div class="post-metadata">

**Author:** ![rhyolight](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/rhyolight/32/3922_2.png) [@rhyolight](https://discourse.numenta.org/u/rhyolight)\
**Post date:** [July 3, 2019, 2:20pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/9 "2019-07-03T14:20:44Z")

</div>

If you are going to process anything visual, I highly suggest you enable local inhibition, which literally adds a whole new dimension to the problem space. 😉 I would also be really interested in seeing how stacked 2D SPs work.

---

<div class="post-metadata">

**Author:** ![Gary\_Gaulin](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/gary_gaulin/32/2248_2.png) [@Gary\_Gaulin](https://discourse.numenta.org/u/Gary_Gaulin)\
**Post date:** [July 5, 2019, 9:31pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/10 "2019-07-05T21:31:06Z")

</div>

Before possibly reinventing the wheel I spent time searching Google Scholar and elsewhere for helpful information, but I did not find anything worth mentioning.

After a few days of thinking a much easier starting point became an earlier more hippocampus related project where I need to connect memory layers much as you described. It gets added to an already existing navigation network containing full scale that vectors out paths between us and what attracts us.

All 2D layers do the same thing. Only difference is the 2D networks are different sizes, from maybe 100 wide to a 1 place/node wide network. There would then be interconnected multiple levels of detail, from whole room, to its cups (with close distance detail filled in by recall of multiple experiences of cups) on its mind at the same time.

> [@Cognitive Architecture and HTM](https://discourse.numenta.org/t/cognitive-architecture-and-htm/6180/7):
>
> A “more elegant” spatial and size generalization made me think of grid modules. Each 3 axis 2D representation almost doubles in resolution from the next. By projection from one 2D size to the next grid modules may be representing objects and environments at essentially all possible visual sizes, not just the one being viewed at. In this case close up features would automatically be recalled, by what is seen from far away. With grid modules not being accounted for in models yet, my best guess …

If this turns out to make sense in neuroscience too (any evidence either way anyone?) then that will take care of the lesser understood farthest end of the visual stream, and what to do from there, to make something come to life in some biologically plausible way. Any ideas how to wire that one up?

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 6, 2019, 11:06am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/11 "2019-07-06T11:06:37Z")

</div>

No idea for the biological side of stuff nor how to wiring it. If you need grid cell modules, not just encoders. Lior (who is often not on the forums) is working on one and the PR should come soon.

I’m thinking about potential ways to make it work and I’ll update when I have an idea,

---

<div class="post-metadata">

**Author:** ![Gary\_Gaulin](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/gary_gaulin/32/2248_2.png) [@Gary\_Gaulin](https://discourse.numenta.org/u/Gary_Gaulin)\
**Post date:** [July 8, 2019, 3:30am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/12 "2019-07-08T03:30:57Z")

</div>

It’s here assumed grid cell signals are part of self-location in each 2D map/module. Program calculated X,Y location variables for all body parts already exist, and we can start with these exact head/body angle and coordinates for body center and mouth. Some might call that an easy way to cheat (on a most baffling part) but we can say it is a “machine learning” enabled gift resulting in self-location super-powers, instead of only mortal cellular approximates like ours.

Starting with the standard animal cognition upper motor commands of Left/Right and Forward/Reverse further simplifies, while at the same time being true to biology. Going straight to text output would make a chatbot, not something new of possible interest to neuroscience.

The smallest (1D or) 2D map grid would represent a unique room, while the most detailed map further places itself inside the boundaries of objects to navigate around or over. Sparse data would be one bit per place, for mapping surfaces of solids to touch or bump into, in the 2D maps that stack into a 3D representation. Most all else in each map is empty space around the object, all 0’s. At first the representation would be mapped by a 2D flatland world view of invisible shock zones and invisible wall locations. Can later add bits for color and other properties.

Map geometry must contain (at center is intersection of surrounding 2D or 3D triangles) hexagonal places, but it may be possible that the exact geometry of the mapped data does not matter. In that case each Y location can be shifted one place radius to the right, from the previous, or use 6 cells per hexagonal place/column/subpopulation/group that senses and memorize navigational traveling waves received at its one input.

Less detailed maps would at some level fill remaining gaps seen in the most detailed. There is this way already an articulation mechanism where at the very tip the entire arena circle can be seen as one place. Pooling horizontally as well as vertically should generalize in a way that predicts a connected shape, based upon on a limited number of points. This would add something missing from behavior when using only one 2D map, causing it to have to bash into the wall everywhere before seeing itself fully enclosed.

One question would be (without adding code to instruct to do so) whether after bashing into the invisible walls enough times the virtual critter predicts the wall locations it didn’t bash into yet, and will (when not overly hungry) test its predictions/guesses/hypotheses by slowing down to pleasant bump for touching solid object surfaces at these locations. If true then the bit for that place gets (where necessary) set to 1 in all map layers, else nothing was really there and false prediction remains 0.

Jeff recently mentioned how he thinks this older part of the brain pertains to the later added neocortex:

[![](https://img.youtube.com/vi/-EVqrDlAqYo/maxresdefault.jpg "Jeff Hawkins: Thousand Brains Theory of Intelligence | Lex Fridman Podcast #25") ](https://www.youtube.com/watch?v=-EVqrDlAqYo&t=1918)

If cortical columns repeat the same overall methodology in miniature then HTM spatial pooling can be expected to in some way work for both.

I wrote more here in regards to modeling having become easier, and new Torch code example to help get things started:

> [@HTM Hackers' Hangout - July 5, 2019](https://discourse.numenta.org/t/htm-hackers-hangout-july-5-2019/6240/13):
>
> Matt, I was very excited by what I found at the Google link, for Torch. My thanks to everyone who helped provide this: [https://colab.research.google.com/github/numenta/nupic.torch/](https://colab.research.google.com/github/numenta/nupic.torch/) The code ran for me! Unfortunately the four here experienced an error I expect you already know about: [https://colab.research.google.com/github/numenta/](https://colab.research.google.com/github/numenta/) I was impressed by being able to (after Run All or associated code above it) clicking after changing a variable (for example fill\_alpha=0.6 to fill\_alpha=0.1) th…

I’m hoping what I described makes better sense to you at the HTM coding level. Grid module signals became good clues for an underlying memory organization, where a machine learning approach may better demonstrate fundamental basics of how it works.

What is now most needed is the horizontal interconnection geometry of “grid” cell sized modules each 1.4 to 1.8 or so different in size from the next. Bitking?

---

<div class="post-metadata">

**Author:** ![Jose\_Cueto](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/jose_cueto/32/4052_2.png) [@Jose\_Cueto](https://discourse.numenta.org/u/Jose_Cueto)\
**Post date:** [July 15, 2019, 2:00am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/13 "2019-07-15T02:00:37Z")

</div>

Hey @marty1885,

I’m very interested with your finding, in fact this is related to some of my hypothesis about the SP. I’m quite busy with my day time jobs however I’d be happy to play with your framework and the stack SPs.

One of my hypothesis about the SP’s capabilities is that it can be used to search for potential encoders for a particular dataset akin to CNN’s capability for searching an image filter/kernels for feature extraction. This can then hopefully replace the hand-coded SP encoders. What you have just showed here is I believe a concrete proof of this capability. I tried to experiment with this encoder idea but I didn’t get any progress. I hope to gain more understanding in your experiment and draw more conclusions.

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 15, 2019, 8:02am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/14 "2019-07-15T08:02:40Z")

</div>

@Jose_Cueto Glad you found my experiment useful! The framework should be run-able on Windows, Mac and Linux. But my experiment does rely on ROOT doing the plotting. Which only supports Linux and OS X now (Windows is in Alpha). You might want to replace it with something else if you are on Windows.

---

<div class="post-metadata">

**Author:** ![Jose\_Cueto](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/jose_cueto/32/4052_2.png) [@Jose\_Cueto](https://discourse.numenta.org/u/Jose_Cueto)\
**Post date:** [July 15, 2019, 11:18am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/15 "2019-07-15T11:18:42Z")

</div>

@marty1885 I inhabit in the \*nix world. Anyway thanks for the response, can you please link me to any instructions for getting started with your framework? Is it possible to freeze an SP and make a copy of it?

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 16, 2019, 2:30pm UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/16 "2019-07-16T14:30:46Z")

</div>

You can find the source code of the framework here.

> **[GitHub - etaler/Etaler: A flexable HTM (Hierarchical Temporal Memory)...](https://github.com/etaler/Etaler)**
>
> A flexable HTM (Hierarchical Temporal Memory) framework with full GPU support. - GitHub - etaler/Etaler: A flexable HTM (Hierarchical Temporal Memory) framework with full GPU support.

To build it, you’ll need:

1. C++17 capable compiler
2. Intel TBB
3. Catch2 (for tests, you don’t need it if you are not building the tests)
4. OpenCL headers for GPU support (only if you enable GPU)

For building instructions, see [GitHub - etaler/Etaler: A flexable HTM (Hierarchical Temporal Memory) framework with full GPU support.](https://github.com/etaler/Etaler#building-from-source)

And you’ll also need [ROOT](https://root.cern.ch) for plotting.

> [@Jose\_Cueto](#):
>
> s it possible to freeze an SP and make a copy of it?

I have never thought of this… No proper way to copy a SP now. But you can workaround it. I’ll add the feature soon.

1. Serialize to disk and load

```c++
save(sp.states(), "sp.cereal");
sp.loadStates(load("sp.cereal"));

```

1. Transfer the SP from the backend to itself

```auto
SpatialPooler sp_backup = sp.to(sp.connections_.backend());

```

Unfortunately copying `sp.states()` won’t work for architecture reasons (without some updates, at least).

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 18, 2019, 5:57am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/17 "2019-07-18T05:57:26Z")

</div>

@Jose_Cueto I’ve just pushed the copy feature to master. Please pull again and the function should be there. Now you can do

```auto
SpatialPooler sp2 = sp.copy()

```

The feature is implemented using the _send to the same backend_ hack. But I figure it is a proper way to do so.

---

<div class="post-metadata">

**Author:** ![Jose\_Cueto](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/jose_cueto/32/4052_2.png) [@Jose\_Cueto](https://discourse.numenta.org/u/Jose_Cueto)\
**Post date:** [July 26, 2019, 4:44am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/18 "2019-07-26T04:44:37Z")

</div>

Hey thanks, I’m going to have a look at this this weekend.

---

<div class="post-metadata">

**Author:** ![Jose\_Cueto](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/jose_cueto/32/4052_2.png) [@Jose\_Cueto](https://discourse.numenta.org/u/Jose_Cueto)\
**Post date:** [July 30, 2019, 2:51am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/19 "2019-07-30T02:51:00Z")

</div>

@marty1885 In your gist, what is the value of “`y`” here? Is it the activated columns? Or something else? Could you please elaborate in HTM terms? I believe y here is the output of the SP but what is it really?

> <https://gist.github.com/marty1885/61eecaf605e258568bd28b0dc1d86a48#file-stackedsp-cpp-L35>

---

<div class="post-metadata">

**Author:** ![marty1885](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.numenta.org/marty1885/32/1891_2.png) [@marty1885](https://discourse.numenta.org/u/marty1885)\
**Post date:** [July 30, 2019, 3:31am UTC](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220/20 "2019-07-30T03:31:43Z")

</div>

The `y` variable is the output of the SP, in active columns (or active cells, since SP have one cell per column).

[Next page](https://discourse.numenta.org/t/experimenting-with-stacking-spatial-poolers/6220.md?page=2)
