家用電器使用者行為分析與事件識別程式碼詳解+修改後執行無誤的程式碼
執行環境:
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%之間,靠右兩列數值不同時,則為判斷錯誤,相同時,則為判斷正確。
並且會出現隨機報錯,也就是說一次執行無報錯,另外一次執行報錯,屬正常現象,因為每次訓練都是區域性最優解。
使用的神經網路模型為: