Skip to content

changes in the API for extracting appliance activations#944

Open
rgtzths wants to merge 1 commit into
nilmtk:masterfrom
rgtzths:exctract_appliance_activations
Open

changes in the API for extracting appliance activations#944
rgtzths wants to merge 1 commit into
nilmtk:masterfrom
rgtzths:exctract_appliance_activations

Conversation

@rgtzths
Copy link
Copy Markdown

@rgtzths rgtzths commented Nov 7, 2021

By doing these changes, we enable the use of appliance activations through the API instead of defining a period.

This implementation slightly changes the model signature as it now receives a dictionary for training and testing instead of the two arrays.

The dictionaries have the following structure:
{
"appliance" : { "mains" : [Dataframe, Dataframe, ...], "appliance": [Dataframe, Dataframe,...],
"appliance2": ...
}

Copy link
Copy Markdown
Collaborator

@levaphenyl levaphenyl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for contributing to NILMTK!

However, the feature you are willing to add is not clear to me. Can you please provide a example to illustrate?
The submitted implementation contains breaking changes. I cannot merge without breaking the API with all implemented algorithms, including nilmtk_contrib ones. This is far more than a "slight" change.
Besides, the changes you introduce only concern the joint processing but are not available for the chunk-wise processing. Is there any reason for that?

Please, see the detailed comments in your code below. I focused on training, but the same comments apply to testing.

Comment thread nilmtk/api.py
self.train_mains = [train_df]
self.train_submeters = train_appliances
clf.partial_fit(self.train_mains, self.train_submeters, current_epoch)
clf.partial_fit(self.train_data, current_epoch)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change breaks the compatibility with NILMTK's Disaggregator class and with all implementations of nilmtk_contrib.
What is your rationale for such a big breaking change?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason behind such change is the fact that when using appliance activations, the periods of readings for one appliance are different from the other appliances.
This requires identifying which aggregated readings are relevant for the given appliance as we are not using the entire period and instead only some subsets.

I knew that the changes would break the Disaggregator class, but I decided it was better to make a pull request and debate the best way to add such changes to the API and Disaggregator class.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to discuss a technical solution with you! My goal here is to allow nilmtk to evolve and fit the needs without breaking for everyone else.

I am still not sure to understand everything. Could you please update your PR description with a picture showing how you change segmentation from the current sliding window?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the need to use a dictionary is directed by the fact that the different appliances will have different activations.
With different activations, the relevant readings of the mains data will also be different ,
The figure below might help explain the proposal and why we need to change methods' signatures.

temp


In my implementation, I also divided the training data into train and cross-validation data,
allowing the user to specify, for example, different houses for training/cross-validating the model.
But I was only going to propose it later.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the explanation. I understand you want a different resolution for each target appliance.
A few questions:

  1. Why not just using a different sequence_length for each disaggregator?
  2. Do you want to omit windows with no activation of the target appliance while training?
  3. For segmentation (event detection), do you use the input (aggregated) signal or the output (disaggregated) signal?
  4. How would your method be applied to an unseen home, without knowing the ground truth?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. By using a different sequence_length, we are not solving the problem where the microwave, for example, is off 98% of the day. So, our models will be biased towards the off category instead of correctly learning to detect.
  2. Yes, exactly. The idea here is to follow what was proposed in NeuralNILM and some other papers.
    3/4. This new method aims to obtain a balanced training/testing set to evaluate the models so we can use the ground truth of the appliances. In a real-life scenario, we would disaggregate every timestep in the time series

Comment thread nilmtk/api.py
train.set_window(start=d[dataset]['buildings'][building]['start_time'],
end=d[dataset]['buildings'][building]['end_time'])

activations = train.buildings[building].elec[appliance_name].activation_series(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Electric.activation_series() is deprecated. Please replace all instances by Electric.get_activations()!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing!

Comment thread nilmtk/api.py
if not mains_df.empty:
mains_df = mains_df[[list(mains_df.columns)[0]]]

appliance_df = next(train.buildings[building].elec[appliance_name].load(physical_quantity='power', ac_type=self.power['appliance'], sample_period=self.sample_period))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you put in appliance_df here is the same data as in your period variable. Why loading the data once more?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believed that the period variable only contained the beginning and end of the activation, not the actual data.

Comment thread nilmtk/api.py
end=end
)

mains_df = next(train.buildings[building].elec.mains().load(physical_quantity='power', ac_type=self.power['mains'], sample_period=self.sample_period))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of the very complicated logic above involving set_window, why don't you load the mains data before the for loop and use index-based selection?
Something like:

mains_df = next(train.buildings[building].elec.mains().load(...))
for event in activations: # note that period is a very confusing name, event would be better.
    mains_event = mains_df.loc[event.index]
    if not mains_event.empty:
        # the code goes on, using mains_event instead of mains_df.

Comment thread nilmtk/api.py
gt_overall={}
for name,clf in classifiers:
gt_overall,pred_overall[name]=self.predict(clf,self.test_mains,self.test_submeters, self.sample_period, timezone)
gt_overall,pred_overall[name]=self.predict(clf, self.test_data, self.sample_period, timezone)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like with partial_fit, this change is breaking the Disaggregator API.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned before, I do believe that we need to change the Disaggregator to enable getting the appliance activations.

@levaphenyl
Copy link
Copy Markdown
Collaborator

Hey, sorry for the long absence in response. In fact, working with you on this PR has been in my to-do list for over a year. However, I have too much on my plate at the moment to help on NILMTK :(.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants