Las siglas MLE corresponden al algoritmo de estimación de máxima verosimilitud y se usa por ejemplo para la desagregación de energía.
Es responsable cuando se desea un solo aparato desagregado en lugar de desagregar muchos. Además, el aparato debe ser mayoritariamente resistivo para lograr precisión.
Se basa en eventos emparejados:
- OnPower: valor delta cuando el aparato está encendido.
- OffPOwer: valor delta cuando el aparato está apagado.
- Duración: duración entre encendido y apagado.
Además, para descartar muchos eventos poco probables, se establecen tres restricciones:
- PowerNoise: valor delta mínimo por debajo del cual el delta se considera ruido.
- PowerPair: diferencia máxima entre OnPower y OffPower (considerando electrodomésticos con consumo de energía constante).
- timeWindow: marco de tiempo máximo entre Onpower y Offpower.
Las características antes mencionadas se modelan con gaussianas, mezclas gaussianas o Poisson. Para cada evento emparejado entrante, el algoritmo extraerá estas tres características y evaluará la probabilidad de máxima verosimilitud de que ese evento emparejado sea un dispositivo determinado.
IMPORTACIONES
import numpy as np
import pandas as pd
from os.path import join
from pylab import rcParams
import matplotlib.pyplot as plt
%matplotlib inline
rcParams['figure.figsize'] = (13, 6)
#plt.style.use('ggplot')
from datetime import datetime as datetime2
from datetime import timedelta
import nilmtk
from nilmtk.disaggregate.maximum_likelihood_estimation import MLE
from nilmtk import DataSet, TimeFrame, MeterGroup, HDFDataStore
from scipy.stats import poisson, norm
from sklearn import mixture
import warnings
warnings.filterwarnings("ignore")
Funciones
def get_all_appliances(appliance):
# Filtering by appliances:
print "Fetching " + appliance + " over data loaded to nilmtk."
metergroup = nilmtk.global_meter_group.select_using_appliances(type=appliance)
if len(metergroup.appliances) == 0:
print "None " + appliance + " found on memory."
pass
# Selecting only single meters:
print "Filtering to get one meter for each " + appliance
meters = [meter for meter in metergroup.meters if (len(meter.appliances) == 1)]
metergroup = MeterGroup(meters)
print metergroup
print "Found " + str(len(metergroup.meters)) + " " + appliance
return metergroup
def get_all_trainings(appliance, train):
# Filtering by appliances:
print "Fetching " + appliance + " over data train data."
elecs = []
for building in train.buildings:
print "Building " + str(building) + "..."
elec = train.buildings[building].elec[appliance]
if len(elec.appliances) == 1:
print elec
print "Fetched elec."
elecs.append(elec)
else:
print elec
print "Groundtruth does not exist. Many appliances or None"
metergroup = MeterGroup(elecs)
return metergroup
Cargando datos
path = '../../../nilmtk/data/ukdale'
ukdale = train = DataSet(join(path, 'ukdale.h5'))
Y dividir en datos de prueba y de entrenaminento
In [8]:train = DataSet(join(path, 'ukdale.h5'))
test = DataSet(join(path, 'ukdale.h5'))
train.set_window(end="17-5-2013")
test.set_window(start="17-5-2013")
#zoom.set_window(start="17-5-2013")
print('loaded ' + str(len(ukdale.buildings)) + ' buildings')
loaded 5 buildings
Obteniendo los datos de entrenamiento
Es posible que el dispositivo seleccionado no esté entrenado desde ElecMeters donde se presentan otros dispositivos, ya que podemos extraer la verdad básica
# Appliance to disaggregate:
applianceName = 'kettle'
# Groundtruth from the training data:
metergroup = get_all_trainings(applianceName,train)
Fetching kettle over data train data.
Building 1...
ElecMeter(instance=10, building=1, dataset='UK-DALE', appliances=[Appliance(type='kettle', instance=1), Appliance(type='food processor', instance=1), Appliance(type='toasted sandwich maker', instance=1)])
Groundtruth does not exist. Many appliances or None
Building 2...
ElecMeter(instance=8, building=2, dataset='UK-DALE', appliances=[Appliance(type='kettle', instance=1)])
Fetched elec.
Building 3...
ElecMeter(instance=2, building=3, dataset='UK-DALE', appliances=[Appliance(type='kettle', instance=1)])
Fetched elec.
Building 4...
ElecMeter(instance=3, building=4, dataset='UK-DALE', appliances=[Appliance(type='kettle', instance=1), Appliance(type='radio', instance=1)])
Groundtruth does not exist. Many appliances or None
Building 5...
ElecMeter(instance=18, building=5, dataset='UK-DALE', appliances=[Appliance(type='kettle', instance=1)])
Fetched elec.
Algoritmo MLE
Capacitación
Primero, creamos el modelo
mle = MLE()
Luego, actualizamos el parámetro del modelo con algunos valores de adivinación.
Primero adivine las características: mezclas gaussianas de encendido y apagado y poisson de duración.
# setting parameters in the model:
mle.update(appliance=applianceName, resistive=True, units=('power','active'), thDelta= 1500, thLikelihood= 1e-10, powerNoise= 50, powerPair= 100, timeWindow= 400, sample_period= '10S', sampling_method='first')
# Settings the features parameters by guessing:
mle.onpower = {'name':'gmm', 'model': mixture.GMM(n_components=2)}
mle.offpower = {'name':'gmm', 'model': mixture.GMM(n_components=2)}
mle.duration = {'name':'poisson', 'model': poisson(0)}
Updating model
{'resistive': True, 'appliance': 'kettle', 'sampling_method': 'first', 'sample_period': '10S', 'thLikelihood': 1e-10, 'timeWindow': 400, 'units': ('power', 'active'), 'thDelta': 1500, 'powerNoise': 50, 'powerPair': 100}
Entrenando el modelo
Entrenamos el modelo con todas las ocurrencias de ese modelo de dispositivo que se encuentran en los datos de entrenamiento.
mle.train(metergroup)
('kettle', 1)
Training on chunk
Samples of onpower: 214
Samples of offpower: 214
Samples of duration: 214
Training onpower
Training offpower
Training duration
('kettle', 2)
Training on chunk
Samples of onpower: 92
Samples of offpower: 92
Samples of duration: 92
Training onpower
Training offpower
Training duration
('kettle', 3)
Chunk empty
Y luego visualizamos características con featureHist_colors () para ver la distribución y cuántas muestras tenemos para cada dispositivo (el mismo modelo de diferentes casas).
mle.featuresHist_colors()
A veces, tenemos más eventos de algunas casas que de otras, como vemos en la figura de arriba. Por lo tanto, necesitamos recortar información para mantener el mismo número de muestras para cada casa.
mle.no_overfitting()
Retraining onpower
Retraining offpower
Retraining duration
In [15]:mle.featuresHist_colors()
Hay otra herramienta de visualización para ver cómo se ajustan las distribuciones del modelo a los datos:
mle.featuresHist()
Onpower y Offpower parecen encajar bien con los datos, pero necesitamos cambiar el modelo por duración
mle.duration = {'name':'gmm', 'model': mixture.GMM(n_components=10)}
Y luego volvemos a entrenar el modelo y usamos no_overfitting
mle.train(metergroup)
mle.no_overfitting()
mle.featuresHist()
('kettle', 1)
Training on chunk
Samples of onpower: 214
Samples of offpower: 214
Samples of duration: 214
Training onpower
Training offpower
Training duration
('kettle', 2)
Training on chunk
Samples of onpower: 92
Samples of offpower: 92
Samples of duration: 92
Training onpower
Training offpower
Training duration
('kettle', 3)
Chunk empty
Retraining onpower
Retraining offpower
Retraining duration
Una vez que tengamos la distribución del modelo final para cada característica. Necesitamos la integridad de cada distribución. Cada CDF debe estar delimitado por uno.
mle.check_cdfIntegrity(step=10)
Onpower cdf: 0.986177684776
Offpower cdf: 1.0
Duration cdf: 0.987375070732
Desagregación
# Building to disaggregate:
building = 2
mains = test.buildings[building].elec.mains()
# File to store the disaggregation
filename= '/home/energos/Escritorio/ukdale-disag-ml.h5'
output = HDFDataStore(filename, 'w')
El siguiente paso tomará unos minutos.
mle.disaggregate(mains, output)
25656 events found.
12419 onEvents found
4244 onEvents no paired.
1 chunks disaggregated
También recibimos cierta información, como el número total de eventos, el número de eventos de encendido, el número de eventos que no se han emparejado y los fragmentos desglosados.
Comparando la desagregación con la verdad básica
## Groundtruth
kettle = test.buildings[building].elec.select_using_appliances(type=applianceName)
output.load(mains.key).next().plot()
kettle.plot()
output.close()
Debe estar conectado para enviar un comentario.