numenta / nupic-legacy

Numenta Platform for Intelligent Computing is an implementation of Hierarchical Temporal Memory (HTM), a theory of intelligence based strictly on the neuroscience of the neocortex.
http://numenta.org/
GNU Affero General Public License v3.0
6.33k stars 1.56k forks source link

Save/load state for multi dimensional Spatial Pooler #3780

Closed dizcza closed 6 years ago

dizcza commented 6 years ago
import numpy as np

# Must import 'capnp' before schema import hook will work
import capnp
from nupic.proto.SpatialPoolerProto_capnp import SpatialPoolerProto

# from nupic.algorithms.spatial_pooler import SpatialPooler
from nupic.bindings.algorithms import SpatialPooler

inputDimensions = (28,28)
columnDimensions = (50,50)
numActiveColumnsPerInhArea = int(0.1 * np.prod(columnDimensions))

sp1 = SpatialPooler(inputDimensions, columnDimensions)

sparse_arr1 = np.empty(columnDimensions, dtype="uint32")
sparse_arr2 = np.empty(columnDimensions, dtype="uint32")

for trial in range(5):
    # change initial state
    input_arr = np.random.randint(0, 2, size=inputDimensions, dtype="uint32")
    sp1.compute(input_arr, True, sparse_arr1)

def save():
    # Serialize the SP
    builder = SpatialPoolerProto.new_message()
    sp1.write(builder)
    with open("temp.cproto", 'wb') as f:
        builder.write_packed(f)
    print("Saved")

def load():
    with open("temp.cproto", 'rb') as f:
        reader = SpatialPoolerProto.read_packed(f)
    # Deserialize to a new SP instance
    sp2 = SpatialPooler.read(reader)
    print("Loaded")
    return sp2

save()
sp2 = load()

for trial in range(100):
    input_arr = np.random.randint(0, 2, size=inputDimensions, dtype="uint32")
    sp1.compute(input_arr, True, sparse_arr1)
    sp2.compute(input_arr, True, sparse_arr2)
    print(sparse_arr1)
    assert np.all(sparse_arr2 == sparse_arr1)

the code above throws an error

Traceback (most recent call last):
  File "/home/dizcza/PycharmProjects/KyivAIGroup/ignored/proto.py", line 45, in <module>
    sp2 = load()
  File "/home/dizcza/PycharmProjects/KyivAIGroup/ignored/proto.py", line 40, in load
    sp2 = SpatialPooler.read(reader)
  File "/home/dizcza/anaconda3/envs/htm/lib/python2.7/site-packages/nupic/bindings/algorithms.py", line 2784, in read
    instance.convertedRead(proto)
  File "/home/dizcza/anaconda3/envs/htm/lib/python2.7/site-packages/nupic/bindings/algorithms.py", line 2804, in convertedRead
    self._initFromCapnpPyBytes(proto.as_builder().to_bytes()) # copy * 2
  File "capnp/lib/capnp.pyx", line 1111, in capnp.lib.capnp._DynamicStructReader.as_builder (capnp/lib/capnp.cpp:24523)
  File "capnp/lib/capnp.pyx", line 1122, in capnp.lib.capnp._DynamicStructReader.as_builder (capnp/lib/capnp.cpp:24422)
capnp.lib.capnp.KjException: src/kj/io.c++:259: failed: miniposix::read(fd, pos, max - pos): Bad file descriptor; fd = 3
stack: 0x7f7ec30eaec9 0x7f7ec30eec2f 0x7f7ec30f080e 0x7f7ec30efd0f 0x7f7ec30ef3ab 0x7f7ec30f035c 0x7f7ec309ef77 0x7f7ec30f0084 0x7f7ec309cbe8 0x7f7ec30cbc9c 0x7f7ec3095ab9 0x7f7ec3096b6e 0x7f7ec30959f9 0x7f7ec3096dc7 0x7f7ec30bafc7 0x7f7ec2faeffa
terminate called after throwing an instance of 'kj::ExceptionImpl'
  what():  src/kj/io.c++:259: failed: miniposix::read(fd, pos, max - pos): Bad file descriptor; fd = 3
stack: 0x7f7ec30eaec9 0x7f7ec30eec2f 0x7f7ec30f080e 0x7f7ec30efd0f 0x7f7ec30ef3ab 0x7f7ec30f035c 0x7f7ec309f247 0x7f7ec309cc7e 0x7f7ec309e718 0x7f7ec309e7ba 0x7f7ec309e839 0x7f7ec2f43b68 0x7f7ec2f370c7 0x7f7ec930ca2c 0x7f7ec93a7ef6 0x7f7ec93a7edc

If I use 1-dimensional tuple for inputDimensions and columnDimensions, everything works fine.

rhyolight commented 6 years ago

Weird, here is the exception I get when I run with NuPIC 1.0.4.dev0 and nupic.bindings 1.0.3.dev0:

Traceback (most recent call last):
  File "script.py", line 45, in <module>
    sp2 = load()
  File "script.py", line 40, in load
    sp2 = SpatialPooler.read(reader)
  File "/Users/mtaylor/nta/nupic.core/bindings/py/src/nupic/bindings/algorithms.py", line 2784, in read
    instance.convertedRead(proto)
  File "/Users/mtaylor/nta/nupic.core/bindings/py/src/nupic/bindings/algorithms.py", line 2804, in convertedRead
    self._initFromCapnpPyBytes(proto.as_builder().to_bytes()) # copy * 2
  File "capnp/lib/capnp.pyx", line 1111, in capnp.lib.capnp._DynamicStructReader.as_builder (capnp/lib/capnp.cpp:24523)
  File "capnp/lib/capnp.pyx", line 1122, in capnp.lib.capnp._DynamicStructReader.as_builder (capnp/lib/capnp.cpp:24422)
capnp.lib.capnp.KjException: src/kj/io.c++:259: failed: miniposix::read(fd, pos, max - pos): Bad file descriptor; fd = 3
stack: 0x10bd05844 0x10bd0a9ab 0x10bd0dc61 0x10bd0ce8c 0x10bd0c3af 0x10bd0c08f 0x10bcb1c2c 0x10bd0bebb 0x10bcb0ac5 0x10bc9adfe 0x10bc9fce9 0x10bca0258 0x10bc9f8f7 0x10bcde84c 0x10bbab9ae 0x10bb82243
libc++abi.dylib: terminating with uncaught exception of type kj::ExceptionImpl: src/kj/io.c++:259: failed: miniposix::read(fd, pos, max - pos): Bad file descriptor; fd = 3
stack: 0x10bd05844 0x10bd0a9ab 0x10bd0dc61 0x10bd0ce8c 0x10bd0c3af 0x10bd0c08f 0x10bcb1f3a 0x10bcb0899 0x10bcb2b9a 0x10bcb2d06 0x10bbe354f 0x10bbb385e 0x10aea2b04 0x10af25291 0x10af25278 0x10aeb2b01

@scottpurdy Is this expected behavior or a bug?

scottpurdy commented 6 years ago

Looks like a bug! It isn't immediately obvious what is causing the exception. Is the serialization (builder.write_packed(f)) actually writing out a file with the saved state? It could be there is a silent error during write that causes the read to fail.

rhyolight commented 6 years ago

So @lscheinkman and I were talking about this issue, and we don't think this is a bug. It just is not clear what the expected behavior is. The docs say sp compute takes a 1d array at this point, and Luiz recently added a runtime exception that fails fast when this is detected. (update your codebase @dizcza if you want to check)

I actually remember running into this issue when I was working on the topology video, but I just flattened the array and moved along. Thanks for reporting this, I think the error message and new docs will help.

dizcza commented 6 years ago

SpatialPooler.compute documentation states that inputVector is treated as a one-dimensional array. As far as I could understand _calculateOverlap function, flattening arrays loses their topology information, stored in original 2-dimensional (or a multi-dimensional) arrays, doesn't it? By topology information, I mean neighborhood pixels information - for example, convolution operation does use topology. Do you plan to include topology? Or I'm missing something here. Thanks.