学习分享——基于深度学习的NILM负荷分解(四)深度学习实现,代码讲解

QQ群1070535031
跟随上一篇的思路,本篇我们来实现整个流程。

实验需求

跟随本文进行学习和实验,需要前面博文中的环境,以及提取出来的UK数据。(学习分享——基于深度学习的NILM负荷分解(二)电器数据提取

本文的代码思路在上一篇,本文着重介绍代码实现相关。思路方面看一下(学习分享——基于深度学习的NILM负荷分解(三)深度学习处理,基础思路

实验环境:
pycharm,python 3.6+,keras库,numpy库,matplotlib库
以及UKData。

定义全局参数

首先引用所有需要的库,如下:

import numpy as np
import os
import numpy as np
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop
from keras.models import load_model

然后是定义一些全局参数:

# 全局参数定义
dimension = 5 #电器的个数

#电器id定义
fridge_id = 0
tv_id = 1
kettle_id = 2
microwave_id = 3
washerdryer_id = 4

#电器开启状态阈值
fridge_threshold = 20
tv_threshold = 20
kettle_threshold = 20
microwave_threshold = 10
washerdryer_threshold = 0

#消噪音阈值
air_threshold = 5

挨个介绍一下上面的参数

dimension :是我们训练的电器的类别个数。5个是当前初步试验所设。

电器id:是我们选择出的五种电器的自增长id。(这里我们选择:冰箱,电视,水壶微波炉和洗衣烘干机)

电器开启状态阈值:这个的作用是屏蔽一些干扰,即,低于该值时认为电器不开启。

消噪音阈值:总功的噪音去燥。将改值以下认为是噪音,忽略。

读取电器数据

fridge_data = np.load('UKData/Fridge freezer0.npy')
print(fridge_data.shape)
fridge_data = fridge_data[:1600000]
print(fridge_data.shape)
fridge_data = np.where(fridge_data > air_threshold,fridge_data,0)

television_data = np.load('UKData/Television0.npy')
print(television_data.shape)
television_data = television_data[:1600000]
print(television_data.shape)
television_data = np.where(television_data > air_threshold, television_data,0)

kettle_data = np.load('UKData/Kettle0.npy')
print(kettle_data.shape)
kettle_data = kettle_data[0:1600000]
print(kettle_data.mean)
kettle_data = np.where(kettle_data > air_threshold, kettle_data,0)

microwave_data = np.load('UKData/Microwave0.npy')
print(microwave_data.shape)
microwave_data = microwave_data[:1600000]
print(microwave_data.shape)
microwave_data = np.where(microwave_data > air_threshold, microwave_data,0)

washerdryer_data = np.load('UKData/Washer dryer0.npy')
print(washerdryer_data.shape)
washerdryer_data = washerdryer_data[:1600000]
print(washerdryer_data.shape)
washerdryer_data = np.where(washerdryer_data > air_threshold, washerdryer_data,0)

上面的代码,直接从之前我们读取出来的各个电器中,选择我们训练用的电器,读取出来,并取出相同长度(1600000)的数据(有用功)。同时将低于阈值air_threshold的进行取0操作,目的是去燥。

制作标签

def create_label(data, application_id, power_threshold, dimension):
    labels = np.zeros((len(data), dimension))
    for i, value in enumerate(data):
        if value[0] > power_threshold:
            labels[i,application_id] = 1
    return labels

上面的方法,输入为数据,电器id,开启状态阈值,电器个数。
输出为,电器个数长度的一维数组。某时刻,对应电器开启时,对应id的位置为1,关闭时为0.

data是每一个时刻的负荷,label是每一个时刻的电器开启状态。

举例:label默认每一时刻是 [0,0,0,0,0],假如某时刻id为1的电器开启,则该时刻的label为 [0,1,0,0,0]

下面的代码是分别生成五个电器,每个电器的label

fridge_labels = create_label(fridge_data,fridge_id,fridge_threshold,dimension)
tv_labels = create_label(television_data,tv_id,tv_threshold,dimension)
kettle_labels = create_label(kettle_data,kettle_id,kettle_threshold,dimension)
microwave_labels = create_label(microwave_data,microwave_id,microwave_threshold,dimension)
washerdryer_labels = create_label(washerdryer_data,washerdryer_id,washerdryer_threshold,dimension)

组合数据与标准化

我们将五个电器的数据进行整合,有用功直接加在一起,变成每一个时刻有用功的和。

label标签也组合在一起,因为上面生成的标签,每个电器对应一列,所以直接相加即可。

sum_label = fridge_labels + tv_labels + kettle_labels + microwave_labels + washerdryer_labels
sum_data_num = fridge_data + television_data + kettle_data + microwave_data + washerdryer_data

然后可以画个图看一下,我们的label

#画图查看数据
epochs = range(1,len(sum_label[:,0])+1)
plt.plot(epochs,sum_label[:,0],label='fridge_labels')
plt.plot(epochs,sum_label[:,1],label='tv_labels')
plt.plot(epochs,sum_label[:,2],label='kettle_labels')
plt.plot(epochs,sum_label[:,3],label='microwave_labels')
plt.plot(epochs,sum_label[:,4],label='washerdryer_labels')
plt.title('Training and validation accuracy')
plt.legend()
plt.show()

画出来是这样的,每个颜色就是开启

在这里插入图片描述

考虑到训练效率和效果,我们对数据进行归一化,如下:

mean = sum_data_num[:1600000].mean(axis=0)
sum_data = sum_data_num - mean
std = sum_data[:1600000].std(axis=0)
sum_data /= std

迭代器

我们只用了一个房间的16w的数据,但所用的数据量还是比较大的,为了之后更大量数据考虑。我们使用迭代器来进行数据输入,以减小内存的使用。每次迭代器来取数据输入训练迭代。

def generator(data, label, lookback, delay, min_index, max_index, shuffle=False, batch_size=128, step=1):
    if max_index is None:
        max_index = len(data) - delay - 1
    i = min_index + lookback
    while 1:
        if shuffle:
            rows = np.random.randint(min_index + lookback, max_index, size=batch_size)
        else:
            if i + batch_size >= max_index:
                i = min_index + lookback
            rows = np.arange(i, min(i + batch_size, max_index))
            i += len(rows)

        samples = np.zeros((len(rows),
                            lookback // step,
                            data.shape[-1]))
        targets = np.zeros((len(rows),dimension))
        for j, row in enumerate(rows):
            indices = range(rows[j] - lookback, rows[j], step)
            samples[j] = data[indices]
            targets[j] = label[rows[j] + delay]
        yield samples, targets

这一块逻辑可能有点复杂,慢慢看。

参数:
data:数据。
label:标签。
lookback:上一节提到的窗口大小,这里是往回看的窗口。
delay:预测后面几帧的结果(这里我们默认为0,因为是预测分解当前帧的结果,不需要往未来看)
min_index:从数据的哪一帧开始看
max_index:从数据的哪一帧结束
shuffle:是否随机
batch_size:迭代中每一个批量的个数
step:每取一次数据,向后跳几帧

这个方法的意思,就是取总的数据中的min_index到max_index这段范围中的数据,每个批量是batch_size,每次往回取lookback窗口大小的数据,取完一次向后跳step个再取下一次。shuffle为是否随机开始取的位置。
delay可以忽略。

实在看不懂的,可以群里面私聊我,或者等我开直播讲。

拆分数据迭代器

首先定义一些迭代器的超参
分别是窗口大小,向后跳的步伐,预测当前帧(delay=0),批量大小

lookback = 20
step = 1
delay = 0
batch_size = 128

具体每个参数的大小设置,我之后的文章,分析各个超参的作用和影响的时候,在做具体讲解。

然后就是定义各个迭代器:

train_gen = generator(sum_data,
                      sum_label,
                      lookback=lookback,
                      delay=delay,
                      min_index=0,
                      max_index=800000,
                      shuffle=True,
                      step=step,
                      batch_size=batch_size)

val_gen = generator(sum_data,
                    sum_label,
                    lookback=lookback,
                    delay=delay,
                    min_index=800001,
                    max_index=1200000,
                    step=step,
                    batch_size=batch_size)
test_gen = generator(sum_data,
                    sum_label,
                    lookback=lookback,
                    delay=delay,
                    min_index=1200001,
                    max_index=None,
                    step=step,
                    batch_size=batch_size)

一共是160w帧数据,我用取前80w作为训练,80-120w作为验证,120-160w作为测试。

训练集数据使用随机处理。

然后计算一下每次迭代运算批数

train_steps = 800000 // batch_size
val_steps = (1200000 - 800001 -lookback) // batch_size
test_steps = (len(sum_data) - 1200001 -lookback) // batch_size

建立简单网络来测试

#双向循环网络
model = Sequential()
model.add(layers.Bidirectional(
    layers.GRU(32,dropout=0.1), input_shape=(None,sum_data.shape[-1])))
model.add(layers.Dense(dimension,activation='sigmoid'))

model.compile(optimizer=RMSprop(), loss='binary_crossentropy',metrics=['acc'])
history = model.fit_generator(train_gen,
                              steps_per_epoch=train_steps,
                              epochs=2,
                              validation_data=val_gen,
                              validation_steps=val_steps)
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1,len(loss)+1)

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and Validation loss')
# plt.legend()
plt.show()

plt.clf() #清空图表

acc_values = history.history['acc']
val_acc_values  = history.history['val_acc']

plt.plot(epochs,acc_values,'bo',label='Training acc') #bo是蓝色圆点
plt.plot(epochs,val_acc_values,'b',label='Validation acc') #b是蓝色实线
plt.title('Training and validation accuracy')
plt.legend()
plt.show()

我这里使用的是keras的方式来做测试,双向GRU,链接一层全连接输出。

然后设置一下参数,优化器,以及输出,迭代2次。

训练结束我并没有存模型,因为上面这个网络只是测试一下,并不是我们既定的完整网络结构。

可以跑一下,看看上面的流程有没有问题。

跑完会绘制出两个图,一个是loss,一个是acc。

运行过程中如下:
在这里插入图片描述

静静等待2次迭代。
可以看到两次迭代,loss在下降收敛,准确率acc在上升。看验证acc会体现训练中的模型的鲁棒性。
在这里插入图片描述

在这里插入图片描述

建立网络进行训练

下面我提供我进行了一系列验证后,较优质的网络结构,供参考。

不过希望同学们,自行研究和实践各种网络结构的组合,包括各种超参的调整。

例如全连接网络,卷积网络,循环网络。以及上述网络的各种组合方式。

只有自己不断尝试,积累经验,才能设计和训练出更好的模型。

循环网络+一维卷积 
model = Sequential()
model.add(layers.Conv1D(32, 5, activation='relu', input_shape=(None,sum_data.shape[-1])))
model.add(layers.MaxPooling1D(3))
model.add(layers.Conv1D(32, 5, activation='relu'))
model.add(layers.LSTM(32, dropout=0.1))
# model.add(layers.GlobalMaxPooling1D())
model.add(layers.Dense(dimension,activation='sigmoid'))

model.compile(optimizer=RMSprop(), loss='binary_crossentropy',metrics=['acc'])
history = model.fit_generator(train_gen,
                              steps_per_epoch=train_steps,
                              epochs=20,
                              validation_data=val_gen,
                              validation_steps=val_steps)

# model.save('model.h5') 

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1,len(loss)+1)

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and Validation loss')
# plt.legend()
plt.show()

plt.clf() #清空图表

acc_values = history.history['acc']
val_acc_values  = history.history['val_acc']

plt.plot(epochs,acc_values,'bo',label='Training acc') #bo是蓝色圆点
plt.plot(epochs,val_acc_values,'b',label='Validation acc') #b是蓝色实线
plt.title('Training and validation accuracy')
plt.legend()
plt.show()

我上述这个模型:一维卷积作用为提炼数据,LSTM(或双向LSTM)来学习变化规律,全连接层输出结果。

超参(例如每层的大小,卷积核,窗口大小等等等)和层数(可以两层lstm,可以最后两层全连接等地的的) 各种组合,需要尝试,欢迎大家来群里共同探讨实验结果。

测试集

最后跑测试集,进行在未参与训练的数据中进行预测。

测试集测试
test_loss, test_acc = model.evaluate_generator(test_gen,steps=test_steps)
print('test acc : ', test_acc)

有不懂的,或者想法问题,可以留言我。
或者QQ群1070535031

对于代码中的很多参数和用法,没有详细描述。后面我单独在写一篇来详说吧。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wwb1990/article/details/109436914
今日推荐