家用电器用户行为分析与事件识别代码详解+修改后运行无误的代码
运行环境:
ubuntu16.04 64位
pycharm python3.5.2
相关软件列表:
cycler (0.10.0)
graphviz (0.7.1)
h5py (2.7.0)
Keras (2.0.4)
matplotlib (2.0.2)
numpy (1.12.1)
pandas (0.20.1)
pip (8.1.1)
protobuf (3.3.0)
pydot (1.2.3)
pyparsing (2.2.0)
python-dateutil (2.6.0)
pytz (2017.2)
PyYAML (3.12)
scipy (0.19.0)
setuptools (20.10.1)
six (1.10.0)
tensorflow (1.1.0)
Theano (0.9.0)
Werkzeug (0.12.2)
wheel (0.29.0)
xlrd (1.0.0)
xlwt (1.2.0)
-------------------------------------------------------------------下面是10-1-----------------------------------------------------------------------------------
#-*- coding: utf-8 -*-
#用水事件划分
import pandas as pd
threshold = pd.Timedelta('4 min') #阈值为分钟
inputfile = '../data/water_heater.xls' #输入数据路径,需要使用Excel格式
outputfile = '../tmp/dividsequence.xls' #输出数据路径,需要使用Excel格式
data = pd.read_excel(inputfile)
data[u'发生时间'] = pd.to_datetime(data[u'发生时间'], format = '%Y%m%d%H%M%S')
data = data[data[u'水流量'] > 0] #只要流量大于0的记录
d = data[u'发生时间'].diff() > threshold #相邻时间作差分,比较是否大于阈值
data[u'事件编号'] = d.cumsum() + 1 #通过累积求和的方式为事件编号
data.to_excel(outputfile)
这个代码什么意思呢?
也就是说,根据水流量和时间间隔的频率来判断用户第几次用水,举个通俗的例子,就说洗菜把。
洗一颗青菜,洗一个叶子时需要用水对吧,但是呢,我把青菜的瓣儿一个个扳下来的这会儿功夫是不需要用水的对吧?
在扳的时候我停掉水龙头,拌青菜完了之后开始洗了,就需要用水,然后洗下一个青菜,再停水。
那么整个洗菜的过程中我有停水,也有用水,整个过程视为一次用水事件。
P205中表格的数据解释:
可以看到
20141019172323
与
20141019172325
这两个发生事件只相隔了2秒,水流量反而减少了,这个可以理解为水龙头开大开小导致。
-------------------------------------------------------------------下面是10-2-----------------------------------------------------------------------------------
#-*- coding: utf-8 -*-
#阈值寻优
import numpy as np
import pandas as pd
inputfile = '../data/water_heater.xls' #输入数据路径,需要使用Excel格式
n = 4 #使用以后四个点的平均斜率
threshold = pd.Timedelta(minutes = 5) #专家阈值,这句话就是把数据格式变化了下,没有对数据进行处理
print("threshold=",threshold)
data = pd.read_excel(inputfile)
data[u'发生时间'] = pd.to_datetime(data[u'发生时间'], format = '%Y%m%d%H%M%S')
data = data[data[u'水流量'] > 0] #只要流量大于0的记录
def event_num(ts):#这里是函数定义,不是在执行,这里的ts是threshold
d = data[u'发生时间'].diff() > ts #相邻时间作差分,比较是否大于阈值
return d.sum() + 1 #这样直接返回事件数
################以上代码和12-1类似################
dt = [pd.Timedelta(minutes = i) for i in np.arange(1, 9, 0.25)]#这里用到的语法是列表推导,也叫作列表解析
#这里arange用于创建等差数组,这里的数据最终会变化成时间数据格式,也就是说,这里的0.25代表每分钟的四分之一,也就是15秒,按照15秒为步长,进行取数据
#上面一句话的意思其实就是某某函数在某定义域的值的序列☆☆☆☆☆☆
h = pd.DataFrame(dt, columns = [u'阈值']) #定义阈值列
h[u'事件数'] = h[u'阈值'].apply(event_num) #计算每个阈值对应的事件数,调用了上面的event_num函数
#上面这句话的意思是在实现图10-4,通过横轴的阈值,来计算数轴的事件数
h[u'斜率'] = h[u'事件数'].diff()/0.25 #计算每两个相邻点对应的斜率
print("h[u'斜率'].abs()=",h[u'斜率'].abs())
h[u'斜率指标'] = pd.rolling_mean(h[u'斜率'].abs(), n) #采用后n个的斜率绝对值平均作为斜率指标
print("h[u'斜率指标']=",h[u'斜率指标'])
#以上的计算: h[u'斜率']=>h[u'斜率指标']
print("--------------------------------------------------------------------------------")
ts = h[u'阈值'][h[u'斜率指标'].idxmin() - n]#
#这里ts得到的是4min,利用修正过后的索引对h[u'阈值']取值
#idxmin()返回数组中最小值的索引
#注:用idxmin返回最小值的Index,由于rolling_mean()自动计算的是前n个斜率的绝对值平均(根据下方列表可以通透地理解这个意思)
#所以结果要进行平移(-n)
#这里的平移其实是不太合适的,我认为平移n/2更加合理,因为一个点的斜率一般是取前后n/2个点进行平均计算后所得值是更为合理的。
if ts > threshold:#这里的意思是,如果上面计算得到的ts小鱼上面设定的专家阈值,就以ts为准,否则就降低为4.
ts = pd.Timedelta(minutes = 4)#决定的最终的阈值
print("-----------------------------")
print("ts=",ts)
#所以这个代码的终极目的就是为了获得斜率最低点的起始点,这样有利于把前一件用水事件和后一件用水事件进行合理的区分。
这个代码的意思就是为了寻找书本p211的图10-4的斜率为零的线段的起始点。
当线段下降很陡的时候,说明线段中的这几个点很可能属于同一个事件,所以化为一个事件。
同样的,当线段很平坦,阈值的递增对事件总数影响不大时,那么曲线上的多个点代表的事件总是说明分类比较合理,也就是说此时每个用水事件之间相隔时间间隔较大。
-------------------------------------------------------------------下面是10-3-----------------------------------------------------------------------------------
接下来这个代码就是数据的输入输出之间进行模型训练了,书上的代码没办法直接运行,我进行了修改,如下:
#-*- coding: utf-8 -*-
#建立、训练多层神经网络,并完成模型的检验
from __future__ import print_function
import pandas as pd
from keras.utils.vis_utils import plot_model
inputfile1='../data/train_neural_network_data.xls' #训练数据
inputfile2='../data/test_neural_network_data.xls' #测试数据
testoutputfile = '../tmp/test_output_data.xls' #测试数据模型输出文件
data_train = pd.read_excel(inputfile1) #读入训练数据(由日志标记事件是否为洗浴)
data_test = pd.read_excel(inputfile2) #读入测试数据(由日志标记事件是否为洗浴)
y_train = data_train.iloc[:,4].as_matrix() #训练样本标签列
x_train = data_train.iloc[:,5:17].as_matrix() #训练样本特征
y_test = data_test.iloc[:,4].as_matrix() #测试样本标签列
x_test = data_test.iloc[:,5:17].as_matrix() #测试样本特征
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.layers import Dense, Activation
model = Sequential() #建立模型
model.add(Dense(output_dim=17,input_dim=11)) #添加输入层、隐藏层的连接
model.add(Activation('relu')) #以Relu函数为**函数
model.add(Dense(output_dim=10,input_dim=17)) #添加隐藏层、隐藏层的连接
model.add(Activation('relu')) #以Relu函数为**函数
model.add(Dense(input_dim=10,output_dim=1)) #添加隐藏层、输出层的连接
model.add(Activation('sigmoid')) #以sigmoid函数为**函数
#编译模型,损失函数为binary_crossentropy,用adam法求解
model.compile(loss='binary_crossentropy', optimizer='adam', class_mode="binary")
model.fit(x_train, y_train, nb_epoch = 100, batch_size = 1) #训练模型
model.save_weights('../tmp/net.model') #保存模型参数
r = pd.DataFrame(model.predict_classes(x_test), columns = [u'预测结果'])
pd.concat([data_test.iloc[:,:5], r], axis = 1).to_excel(testoutputfile)
model.predict(x_test)
############自己添加的########################
plot_model(model, to_file='model.png', show_shapes=True)
代码运行结果为:
热水事件 | 起始数据编号 | 终止数据编号 | 开始时间(begin_time) | 根据日志判断是否为洗浴(1表示是,0表示否) | 预测结果 | |
0 | 1 | 73 | 336 | 2015-01-05 9:42:41' | 1 | 1 |
1 | 2 | 420 | 535 | '2015-01-05 18:05:28' | 1 | 1 |
2 | 3 | 538 | 706 | '2015-01-05 18:25:24' | 1 | 1 |
3 | 4 | 793 | 910 | '2015-01-05 20:00:42' | 1 | 1 |
4 | 5 | 935 | 1133 | '2015-01-05 20:15:13' | 1 | 1 |
5 | 6 | 1172 | 1274 | '2015-01-05 20:42:41' | 1 | 1 |
6 | 7 | 1641 | 1770 | '2015-01-06 08:08:26' | 0 | 0 |
7 | 8 | 2105 | 2280 | 2015-01-06 11:31:13' | 1 | 1 |
8 | 9 | 2290 | 2506 | '2015-01-06 17:08:35' | 1 | 1 |
9 | 10 | 2562 | 2708 | '2015-01-06 17:43:48' | 1 | 1 |
10 | 11 | 3141 | 3284 | '2015-01-07 10:01:57' | 0 | 1 |
11 | 12 | 3524 | 3655 | 2015-01-07 13:32:43' | 0 | 1 |
12 | 13 | 3659 | 3863 | '2015-01-07 17:48:22' | 1 | 1 |
13 | 14 | 3937 | 4125 | '2015-01-07 18:26:49' | 1 | 1 |
14 | 15 | 4145 | 4373 | '2015-01-07 18:46:07' | 1 | 1 |
15 | 16 | 4411 | 4538 | '2015-01-07 19:18:08' | 1 | 1 |
16 | 17 | 5700 | 5894 | 2015-01-08 7:08:43' | 0 | 1 |
17 | 18 | 5913 | 6178 | 2015-01-08 13:23:42' | 1 | 1 |
18 | 19 | 6238 | 6443 | 2015-01-08 18:06:47' | 1 | 1 |
19 | 20 | 6629 | 6696 | 2015-01-08 20:18:58' | 1 | 1 |
20 | 21 | 6713 | 6879 | 2015-01-08 20:32:16' | 1 | 1 |
注意每次预测结果都是不一样的,上面的预测结果准确度是80%,如果多训练几次会发现准确度在60%~80%之间,靠右两列数值不同时,则为判断错误,相同时,则为判断正确。
并且会出现随机报错,也就是说一次运行无报错,另外一次运行报错,属正常现象,因为每次训练都是局部最优解。
使用的神经网络模型为: