Closed JackKelly closed 5 years ago
I am chalking down some ideas up here of what this class should have. Please feel free to add/remove. I am scarcely clear about writing the interface for a disaggregator to handle multiple cases- train and disaggregate on same home; train on some home(s) and disaggregate on other(s)
A simple design which comes to my mind is the following (based on writing disaggregators for v0.1 and current version):
Excellent work on listing important requirements for a disaggregator interface! I agree with all your points. (It would be good to weight up the pros and cons of passing in a generator of DataFrames instead of an entire ElecMeter
or MeterGroup
object representing the mains... if we pass in an instance of an Electric
subclass then the Disaggregator
will have access to important metadata such as building instance, dataset name, the meter's sample rate etc... and the Disaggregator
can choose whether to load all AC types from disk or just one etc...)
Some very quick replies to your email...
Quoting your email...
I have not been able to test Hart85 algo on 2 dimensions, although it is supposed to work. Part of the reason is that we currently engineered for 1d data and other 2 disaggregators work on 1d data only. So, it would require some effort to ensure all 3 of them use the same interface, which I think is helpful as it helps people in the future to contribute disaggregators. Although, it also binds them. Say, if tomorrow I wish to write a disaggregator which takes weather into consideration, in the proposed design I won't be able to do the same.
Regarding 1-D versus n-D power data... it should be possible to define a common interface to accept either 1-D or n-D power data. If disaggregate()
expects a generator of DataFrames
(instead of a generator of Series
) or an instance of an Electric
subclass (ElecMeter
or MeterGroup
) then it should be able to accept 1-D or n-D data.
Regarding having NILM algorithms which want additional inputs (e.g. weather data)... I think the Python mechanisms for forcing subclasses to implement certain methods can only force subclasses to implement those methods, it can't force subclasses to use the exact same method signature. So, for example, the superclass would just say "all subclasses must implement a disaggregate
and a train
method and they can (but I can't force them to) use these specific function signatures and docstrings". So, as long as your disaggregator implements disaggregate
, it will be allowed by Python. Because the subclass doesn't have to implement the exact function signature of the superclass, each NILM algorithm can add new inputs (e.g. a generator of DataFrames
of weather data) to any method. At least, I think that's the way it works!
It shouldn't be too hard to write a Disaggregator
superclass and hence massively simplify the implementation of new NILM algorithms. I'll try to write a draft Disaggregator class within the next couple of weeks so we can try to get it into v0.3. I think this is pretty essential. We've had great success with encouraging other developers to add dataset converters to NILMTK but no one has yet added their own NILM algorithm to NILMTK. This is a big issue and, I think, needs addressing before we release v0.3. The basic idea would be, as outlined by Nipun, that a new NILM algorithm would only have to override the train_on_chunk()
and disaggregate_chunk()
methods.
I see that this is not a huge change and has several benefits. I am happy to have this design before v0.3; especially given the fact that it would get tougher to change later!
Pasting from Nipun's email:
I guess if the disaggregator class is in place, then all the algo writer needs to do is to take a DataFrame input and output a data frame of all appliances. So, the first step for any algo writer could be to provide a function which takes an in-memory data frame of mains readings an disaggregates it, without having to worry about some of the specifics of nilmtk.
Disaggregation algorithms will be implemented by extending the Disaggregator
superclass. This allows disaggregation algorithms to implement a common interface, while also reusing generic NILMTK code.
N.B. only the method signatures and docstrings for the Disaggregator
class exist at the moment, and hopefully the implementation will follow shortly :)
There will be two (complementary) methods of implementing a disaggregation algorithm:
This approach requires the train_on_chunk()
and disaggregate_chunk()
methods to be overridden by the subclass. This approach is strongly recommended, as it allows the disaggregation to be performed out-of-core, as well as allowing code to call this function without worrying about NILMTK data structures. Furthermore, this approach will make use of generic Disaggregator
functionality to iterate over dataframes produced by a generator, as well as saving the disaggregation output to disk for each chunk.
This approach requires the train()
and disaggregate()
methods to be overridden by the subclass. This approach is not recommended without also overriding the chunk-by-chunk disaggregation, as it will mean all disaggregation needs to take place in memory. However, it will be necessary to override these methods if the disaggregation algorithm is going to make use of mutual information between multiple aggregate site-meters, i.e. split-phase power.
Finally, a disaggregation sub-class can choose to implement the import_model()
and export_model()
methods to allow the learned model to be saved to disk, rather than re-learning the model each time a new environment is created.
I'm working on splitting CombinatorialOptimisation.disaggregate()
into disaggregate()
and disaggregate_chunk()
now.
done
One issue I'm not clear on is this: how do we write a common interface for train_on_chunk
? There are several requirements different types of disaggregation algorithm I can think of:
How do we build a common function signature for train_on_chunk
? Do we just always use something like train_on_chunk(self, appliance_chunk, mains_chunk=None)
?
Although, to be honest, I'm not sure my Neural NILM code will ever implement train_on_chunk
because it always needs full access to the MeterGroup (so it can get activations of each appliance to augment the training data etc).
This is a really good point. Could we change the signature to take a MeterGroup, which can always just contain a single ElecMeter for supervised learning?
Could we change the signature to take a MeterGroup
I haven't articulated this very well yet... but I was hoping that both train_on_chunk
and disaggreate_chunk
would take only non-NILMTK types. e.g. just objects like Pandas DataFrames, dicts, numpy arrays, strings etc.
I think it would be nice to allow NILMTK to interact more freely with the wider scientific Python ecosystem by allowing people to more freely use NILMTK directly on DataFrames / numpy arrays etc.
For example, when I was messing around with my Neural NILM work, I only used NILMTK for data loading (and comparing with CO and FHMM). I didn't use NILMTK's Disaggregator
interface at all for my Neural NILM experiments. Part of the reason was that I was doing a lot of 'augmenting' the training data (i.e. generating lots of numpy arrays) and it was easier to stay in numpy land rather than try to wrap my numpy arrays into an ElecMeter
object or something (which, at the moment, isn't even possible in NILMTK without first dumping the numpy array to disk, which would have slowed things down a lot). Hence my ambition to try to allow the Disaggregator subclasses to interact either with NILMTK objects like MeterGroups
or directly with DataFrame / numpy arrays.
Does that sound sensible? Maybe it's not possible?
This sounds like a really good idea, and in general I'm happy with any changes that simplify the required knowledge for algorithm contributors. Do you think each algorithm really needs to implement train_on_chunk though so long as they're iterating through the chunks?
I'd second using Pandas data frames instead of nilmtk data structures for disaggregate_chunk
.
Do you think each algorithm really needs to implement train_on_chunk though so long as they're iterating through the chunks?
In an ideal world, yes, I think each algorithm should implement train_on_chunk
and disaggregate_chunk
. With a bit of thought on our end, we could perhaps design things so disag algo authors only have to implement those two methods and the superclass would handle iterating through the chunks (and the superclass would call train_on_chunk
and disaggregate_chunk
). But, given the diversity of NILM algorithms, we'd need to give algo authors to option to override disaggregate
and train
to iterate through chunks in custom ways.
Maybe the superclass could define a function signature for train_on_chunk which covers all the options (ie has parameters for the mains as well as the appliances) and any NILM algorithm (like CO) which doesn't need mains won't require 'mains' and will just ignore it if it is provided. That way users can write code which can use any NILM algorithm without changing the function call. Any thoughts? On 25 Nov 2015 11:47 am, "Nipun Batra" notifications@github.com wrote:
I'd second using Pandas data frames instead of nilmtk data structures for disaggregate_chunk.
— Reply to this email directly or view it on GitHub https://github.com/nilmtk/nilmtk/issues/271#issuecomment-159583741.
Closing in favor of the new API.
Define Disaggregator superclass to hold common behaviour & to specify common interface to NILM algorithms & common docstrings.
@oliparson discovered how to specify common common docstrings using a superclass. Described in this comment.