利用BP神经网络算法进行用户行为分析

住户在应用家庭用热水器的历程中,会由于地域气侯、地区差异和客户年纪性别差异等缘故,产生不一样的应用习惯性。家电业若能深入了解其商品在不一样消费群的应用习惯性,开发作用,就能发展新销售市场。

本实例将依据BP神经网络优化算法搭建洗澡事件识别实体模型,依据洗澡事件识别实体模型对差异区域的使用者的洗澡事件开展识别,依据识别結果较为不一样用户群体的顾客应用习惯性,加重对用户的了解等。进而,生产商可以对不一样的顾客群给予最适宜的差异化商品,改善新品的智能化系统的产品研发和建立对应的营销战略。

环境与发掘总体目标

自1988年我国第一台真正的的意义上的热水器问世迄今,热水器领域经历了天翻地覆的转变。伴随着进场公司的增加,热水器领域市场竞争史无前例的猛烈,怎样在诸多公司中出类拔萃,变成热水器公司发展的头等大事。从客户考虑,剖析客户的采用个人行为,是改进产品功能,在市场竞争中取得成功的主要方式 之一。

伴随着中国大家电品牌的进到和国际品牌的涌进,电热水器有关技术性在过去的20年里获得了迅速发展趋势,屡次自主创新。从初次明确提出密闭式电热水器的定义到水电工程分离出来技术应用的产品研发,再到漏电保护器技术性的运用及出水量关闭电源技术性和漏电保护开关技术性专利的申请提升,如今高效率技术性刷新了业界对电热水器“高耗能”的认知能力。殊不知,时下的热水器领域也并不是一片盛世,领域内已经开演一幕适者生存的“丛林的法则”戏份,市场占有率逐渐向行业龙头集中化,尤其是一些在资产、方式和品牌影响力等层面有着整体实力的综合性家用电器类目大佬,已经持续蚕食鲸吞销售市场生日蛋糕。要想在该领域立足于,只有走产品差异化的线路,提高技术水平和产品品质,在作用产品卖点、外型等领域作出本身特点。

中国某热水器生产厂商新研制的一种高档智能化热水器,在情况发生改变或是有水流情况时,会收集各监管指标值数据。本实例根据热水器收集的时间序列分析数据,将排列顺序的离散变量的用水时间阶段依据水流量和间断间隔时间区划为不一样尺寸的時间区段,每一个区段是一个可解释的一次详细用水事件,并以热水器一次完整用水事件做为一个基本上事件,将时间序列分析数据区划为独立自主的用水事件并识别出在其中归属于洗澡的事件。根据以上工作中,该生产商可从热水器智能化操控和环保节能运作等各个方面对设备开展提升。

在热水器用户行为分析全过程中,用水事件识别是极为重要的阶段。依据该热水器生产厂商给予的数据热水器客户用水事件区划与识别实例的总体总体目标如下所示。

(1)依据热水器收集到的数据,区划一次详细用水事件。

(2)在区划好的一次详细用水事件中,识别出洗澡事件。

统计分析方法与全过程

热水器客户用水事件区划与识别实例的整体步骤如下图所示。

热水器客户用水事件区划与识别实例主要包含下列5个流程。

(1)对开水客户的历史时间用水数据开展可选择性提取,搭建权威专家样版。

(2)对流程(1)产生的数据集开展数据探寻剖析与预备处理,包含探寻水流量的划分状况,删掉多余特性,识别用水数据的缺少值,并对缺失值作解决,依据模型的必须实现特性结构等。依据以上解决,对用水样版数据创建用水事件间隔时间识别实体模型和区划一次详细的用水事件实体模型,再在一次详细用水事件区划結果的基本上,去除短暂性用水事件变小识别范畴等。

(3)在流程(2)获得的模型样版数据基本上,创建洗澡事件识别实体模型,对洗澡事件识别实体模型开展实体模型剖析点评。

(4)对流程(3)产生的实体模型結果运用并对洗澡事件区划开展提升。

(5)启用洗澡事件识别实体模型,对实时监控系统的热水器水流数据开展洗澡事件全自动识别。

数据探寻剖析

在热水器的运用全过程中,热水器的模式会常常发生改变,例如启动和待机、由加温转到隔热保温、由无水流到有水流、水的温度由50℃变成49℃等。而智能化热水器在情况发生改变或是水流量非零时,每2秒会收集一条情况数据。因为数据的收集頻率较高,而且数据来源于很多客户,数据总产量十分大。本实例对初始数据选用无放回随机抽样法提取200家热水器客户从2014年1月1日至2014年12月31日的用水纪录做为初始模型数据。因为客户不但应用热水器来洗澡,并且涉及了洗手消毒、洁面、刷牙漱口、刷碗、煮饭等用水个人行为,因此热水器收集到的数据来源于各种各样不一样的用水事件。

热水器收集的用水数据包括12个特性:热水器编号,产生時间,自动开关机情况,加温中,隔热保温中,有没有水流,具体溫度,热水流量,水流量,环保节能方式,加温剩下的时间和现阶段设定溫度。其解释说明如下所示表所显示。

探寻剖析热水器的水流量情况,在其中有没有水流和水流量特性最能直接反映热水器的水流量状况,对这两个特性开展探究剖析,如编码明细1所显示。

import pandas as pdimport ** tplotlib.pyplot as pltinputfile = './demo/data/original_data.xls' # 键入的数据文档data = pd.read_excel(inputfile) # 载入数据# 查询有没有水流的遍布# 数据获取lv_non = pd.value_counts(data['有没有水流'])['无']lv_move = pd.value_counts(data['有无水流'])['有']# 制作柱形图 fig = plt.figure(figsize=(6 ,5)) # 设定画板大小plt.rcParams['font.sans-serif'] = 'SimHei' # 设定汉语表明plt.rcParams['axes.unicode_minus'] = Falseplt.bar(left=range(2), height=[lv_non,lv_move], width=0.4, alpha=0.8, color='skyblue')plt.xticks([index for index in range(2)], ['无','有'])plt.xlabel('水流情况')plt.ylabel('纪录数')plt.title('不一样水流情况纪录数')plt.show()plt.close()# 查询水流量遍布water = data['水流量']# 制作水流量遍布箱型图fig = plt.figure(figsize=(5 ,8))plt.boxplot(water, patch_artist=True, labels = ['水流量'], # 设定x轴文章标题 boxprops = {'facecolor':'lightblue'}) # 设定填充色plt.title('水流量遍布箱线图')# 表明y纵坐标的道德底线plt.grid(axis='y')plt.show()

根据编码明细1获得不一样水流情况的记载的柱形图,如下图所示,无水流情况的纪录显著比有水流情况的纪录要多。

根据编码明细1获得不一样水流量的记载的柱形图,如下所示所显示,壳体接近0,表明无水流量的纪录较多,水流量的分散与水流情况的遍布一致。

用水间断间隔时间界定为一条水流量不以0的水流纪录同下一条水流量不以0的水流纪录中间的间隔时间。依据现场实验统计分析,2次用水的全过程的用水间断的间距时间一般在不得超过4分鐘。为了更好地研究客户真正用水间断间隔时间的划分状况,统计分析用水间断的间隔时间并做频率分布表。根据频率分布表剖析客户用水间断间隔时间的周期性,实际的数据如下所示表所显示。

剖析报表得知,间断间隔时间为0~0.3分鐘的頻率很高,依据平时用水工作经验可以判定其为一次用水時间中的间断;停顿间隔时间为6~13分鐘的次数较低,剖析其为2次用水事件中间的间断间距。2次用水事件的间断间隔时间遍布在3~7分鐘。依据现场实验统计分析用水间断的间隔时间类似。

数据预备处理1.特性通信规约

因为热水器收集的用水数据特性较多,本实例做下列解决。因剖析的首要目标为客户,剖析的具体总体目标为使用者的洗澡个人行为的一般规律性,因此“热水器序号”特性可以除去;因热水器收集的数据中,“有没有水流”特性可以根据“水流量”特性反映,“环保节能方式”特性选值同样均为“关”,对剖析无功效,可以除去。删掉多余特性“热水器序号”“有没有水流”“环保节能方式”,如编码明细 2所显示。

import pandas as pdimport numpy as npdata = pd.read_excel('../data/original_data.xls')print('初始值的数据样子为:', data.shape)# 删掉热水器序号、有没有水流、环保节能方式特性data.drop(labels=["热水器序号","有没有水流","环保节能方式"],axis=1,inplace=True) print('删掉多余特性后的数据样子为:', data.shape)data.to_csv('../tmp/water_heart.csv',index=False)

删掉多余特性后获得用于模型的特性如下所示表所显示。

2.区划用水事件

客户的用水数据储存在数据库文件,纪录了各种的用水事件,包含洗澡,洗手消毒,刷牙漱口,洁面,洗衣服,刷碗等,并且一次用水事件由多条乃至数千条的情况纪录构成。因此本实例最先要在很多的情况纪录中区划出什么持续的数据是一次详细的用水事件。

用水情况纪录中,水流量不以0表明客户已经应用开水;而水流量为0时客户用开水产生间断或是用开水完毕。针对任一个用水纪录,假如它的往前时间差超出阀值,则将它记作事件的逐渐序号;假如向后时间差超出阀值,则将其记作事件的完毕序号。区划实体模型的标记表明如下所示表。

一次详细用水事件的区划流程如下所示。

(1)载入数据纪录,识别到全部水流量不以0的情况纪录,将两者的出现時间记为编码序列t1。

(2)对序列t1搭建其往前时间差列和向后时差列,并各自与阀值完成较为。往前时间差超出阀值T,则将它记作新的用水事件的逐渐序号;假如向后时间差超出阀值T,则将其记作用水事件的完毕序号。

循环系统实行流程(2)直到往前时间差列和向后时差列与平均值较为结束,完毕事件区划。

用水事件划分关键分成2个流程,即明确一次用水时间间距和测算两根邻近纪录的時间,完成编码如代码明细3所显示。

import pandas as pdimport numpy as np# 载入数据data = pd.read_csv('../tmp/water_heart.csv')# 区划用水事件threshold = pd.Timedelta('4 min') # 阀值为4分鐘data['产生時间'] = pd.to_datetime(data['产生時间'], for ** t = '%Y%m%d%H%M%S') # 变换时间格式data = data[data['水流量'] > 0] # 只需总流量超过0的纪录sjKs = data['产生時间'].diff() > threshold # 邻近時间往前差分信号,较为是不是超过阀值sjKs.iloc[0] = True # 令第一个時间为第一个用水事件的逐渐事件sjJs = sjKs.iloc[1:] # 向后差分信号的結果sjJs = pd.concat([sjJs,pd.Series(True)]) # 令最后一个時间做为最后一个用水事件的完成時间# 建立数据框,并界定用水事件编码序列sj = pd.DataFrame(np.arange(1,sum(sjKs) 1),columns = ["事件编号"])sj["事件开始序号"] = data.index[sjKs == 1] 1 # 界定用水事件的开始序号sj["事件停止序号"] = data.index[sjJs == 1] 1 # 界定用水事件的停止序号print('当阈值为4分钟的时候事件数目为:',sj.shape[0])sj.to_csv('../tmp/sj.csv',index = False)

对用户的用水数据进行划分结果如下表所示。

3.确定单次用水事件时长阈值

对某热水器用户的数据根据不同的阈值划分用水事件,得到了相应的事件个数,阈值变化与划分得到事件个数如下表所示,阈值与划分事件个数关系如下图所示。

此图为阈值与划分事件个数的散点图,图中某段阈值范围内,下降趋势明显,说明在该段阈值范围内,用户的停顿习惯比较集中。如果趋势比较平缓,则说明用户的停顿热水的习惯趋于稳定,所以取该段时间开始的时间点作为阈值,既不会将短的用水事件合并,又不会将长的用水事件拆开。在图中,用户停顿热水的习惯在方框的位置趋于稳定,说明热水器用户的用水的停顿习惯用方框开始的时间点作为划分阈值会有一个好的效果。

曲线在图中方框趋于稳定时,其方框开始的点的斜率趋于一个较小的值。为了用程序来识别这一特征,将这一特征提取为规则。根据下图说明如何识别上图中的方框中起始的时间。

每个阈值对应一个点,给每个阈值计算得到一个斜率指标,如图中所示。其中,点是要计算的斜率指标点。为了直观的展示,用下表所示的符号来进行说明。

根据公式1,计算出 E​四个斜率。于是可以根据公式2计算出四个斜率之和的平均值 。

公式1:

公式2:

将K作为A点的斜率指标,特别指出横坐标上的最后四个点没有斜率指标,因为找不出在它以后的4个更长的阈值。但这不影响对最优阈值的寻找,因为可以提高阈值的上限,以使最后的4个阈值不是考虑范围内的阈值。

先统计出各个阈值下的用水事件的个数,再通过阈值寻优的方式找出最优的阈值,具体实现方式如代码清单4所示。

# 确定单次用水事件时长阈值n = 4 # 使用以后四个点的平均斜率threshold = pd.Timedelta(minutes=5) # 专家阈值data['发生时间'] = pd.to_datetime(data['发生时间'], for ** t='%Y%m%d%H%M%S')data = data[data['水流量'] > 0] # 只要流量大于0的记录# 自定义函数:输入划分时间的时间阈值,得到划分的事件数def event_num(ts): d = data['发生时间'].diff() > ts # 相邻时间作差分,比较是否大于阈值 return d.sum() + 1 # 这样直接返回事件数dt = [pd.Timedelta(minutes=i) for i in np.arange(1, 9, 0.25)]h = pd.DataFrame(dt, columns=['阈值']) # 转换数据框,定义阈值列h['事件数'] = h['阈值'].apply(event_num) # 计算每个阈值对应的事件数h['斜率'] = h['事件数'].diff()/0.25 # 计算每两个相邻点对应的斜率h['斜率指标']= h['斜率'].abs().rolling(4).mean() # 往前取n个斜率绝对值平均作为斜率指标ts = h['阈值'][h['斜率指标'].idxmin() - n]# 用idxmin返回最小值的Index,由于rolling_mean()计算的是前n个斜率的绝对值平均# 所以结果要进行平移(-n)if ts > threshold: ts = pd.Timedelta(minutes=4)print('计算出的单次用水时长的阈值为:',ts)

得到阈值优化的结果如下。

(1)当存一个阈值的斜率指标时,则取阈值最小的点A(可能存在多个阈值的斜率指标小于1)的横坐标作为用水事件划分的阈值,其中中的“1”是经过实际数据验证的一个专家阈值。

(2)当不存在时,则找所有阈值中斜率指标最小的阈值;如果该阈值的斜率指标小于5,则取该阈值作为用水事件划分的阈值;如果该阈值的斜率指标不小于5,则阈值取默认值的阈值4分钟。其中斜率指标小于5中的5是经过实际数据验证的一个专家阈值。

4.属性构造

(1)构建用水时长与频率属性

不同用水事件的用水时长是基础属性之一。例如单次洗漱事件一般总时长在5分钟左右,而一次手洗衣物事件的时长则根据衣物多少而不同。根据用水时长这一属性可以构建如下表所示的事件开始时间、事件结束时间、洗浴时间点、用水时长、总用水时长和用水时长/总用水时长这6个属性。

上表构建用水开始时间或结束的时间两个特征时分别减去或加上了发送阈值(发送阈值是指热水器传输数据的频率的大小)。其原因以下图为例。在20:00:10时热水器记录到的数据还没有用水,而在20:00:12时热水器记录的有用水行为。所以用水开始时间在20:00:10~20:00:12之间,考虑到网络不稳定导致网络数据传输延时数分钟或数小时之久等因素,取平均值会导致很大的偏差,综合分析构建“用水开始时间”为起始数据的时间减去“发送阈值”的一半。

用水时长相关的属性只能够区分出一部分用水事件,不同用水事件的用水停顿和频率也不同。例如一次完整洗漱事件的停顿次数不多,停顿的时间长短不一,平均停顿时长较短,一次手洗衣物事件的停顿次数较多,停顿时间相差不大,平均停顿时长一般。根据这一属性,可以构建如下表所示的停顿时长,总停顿时长,平均停顿时长,停顿次数四个属性。

构建用水时长与用水频率属性,如代码清单5所示。

data = pd.read_excel ('../data/water_hearter.xlsx',encoding='gbk') # 读取热水器使用数据记录sj = pd.read_csv('../tmp/sj.csv') # 读取用水事件记录# 转换时间格式data["发生时间"] = pd.to_datetime(data["发生时间"],for ** t="%Y%m%d%H%M%S")# 构造属性:总用水时长timeDel = pd.Timedelta("1 sec")sj["事件开始时间"] = data.iloc[sj["事件起始编号"]-1,0].values- timeDelsj["事件结束时间"] = data.iloc[sj["事件终止编号"]-1,0].values + timeDelsj['洗浴时间点'] = [i.hour for i in sj["事件开始时间"]]sj["总用水时长"] = np.int ** (sj["事件结束时间"] - sj["事件开始时间"] +)/1000000000 + 1# 构造用水停顿事件# 构造属性“停顿开始时间”、“停顿结束时间”# 停顿开始时间指从有水流到无水流,停顿结束时间指从无水流到有水流for i in range(len(data)-1): if (data.loc[i,"水流量"] != 0) & (data.loc[i + 1,"水流量"] == 0) : data.loc[i + 1,"停顿开始时间"] = data.loc[i +1, "发生时间"] - timeDel if (data.loc[i,"水流量"] == 0) & (data.loc[i + 1,"水流量"] != 0) : data.loc[i,"停顿结束时间"] = data.loc[i , "发生时间"] + timeDel # 提取停顿开始时间与结束时间所对应行号,放在数据框Stop中indStopStart = data.index[data["停顿开始时间"].notnull()]+1indStopEnd = data.index[data["停顿结束时间"].notnull()]+1Stop = pd.DataFrame(data={"停顿开始编号":indStopStart[:-1], "停顿结束编号":indStopEnd[1:]}) # 计算停顿时长,并放在数据框stop中,停顿时长=停顿结束时间-停顿结束时间Stop["停顿时长"] = np.int ** (data.loc[indStopEnd[1:]-1,"停顿结束时间"].values- data.loc[indStopStart[:-1]-1,"停顿开始时间"].values)/1000000000# 将每次停顿与事件匹配,停顿的开始时间要大于事件的开始时间,# 且停顿的结束时间要小于事件的结束时间for i in range(len(sj)): Stop.loc[(Stop["停顿开始编号"] > sj.loc[i,"事件起始编号"]) & (Stop["停顿结束编号"] < sj.loc[i,"事件终止编号"]),"停顿归属事件"]=i+1 # 删除停顿次数为0的事件Stop = Stop[Stop["停顿归属事件"].notnull()]# 构造属性 用水事件停顿总时长、停顿次数、停顿平均时长、# 用水时长,用水/总时长stopAgg = Stop.groupby("停顿归属事件").agg({"停顿时长":sum,"停顿开始编号":len})sj.loc[stopAgg.index - 1,"总停顿时长"] = stopAgg.loc[:,"停顿时长"].valuessj.loc[stopAgg.index-1,"停顿次数"] = stopAgg.loc[:,"停顿开始编号"].valuessj.fillna(0,inplace=True) # 对缺失值用0插补stopNo0 = sj["停顿次数"] != 0 # 判断用水事件是否存在停顿sj.loc[stopNo0,"平均停顿时长"] = sj.loc[stopNo0,"总停顿时长"]/sj.loc[stopNo0,"停顿次数"] sj.fillna(0,inplace=True) # 对缺失值用0插补sj["用水时长"] = sj["总用水时长"] - sj["总停顿时长"] # 定义属性用水时长sj["用水/总时长"] = sj["用水时长"] / sj["总用水时长"] # 定义属性 用水/总时长print('用水事件用水时长与频率属性构造完成后数据的属性为:n',sj.columns)print('用水事件用水时长与频率属性构造完成后数据的前5行5列属性为:n', sj.iloc[:5,:5])

(2)构建用水量与波动属性

除了用水时长,停顿和频率外,用水量也是识别该事件是否为洗浴事件的重要属性。例如用水时间中的洗漱事件相比洗浴事件有停顿次数多,用水总量少,平均用水少的特点。手洗衣物事件相比于洗浴事件则有停顿次数多,用水总量多,平均用水量多的特点。根据这一原因可以构建出下表所示的两个用水量属性。

同时用水波动也是区分不同用水事件的关键。一般在一次洗漱事件中刷牙和洗脸的用水量完全不同,在一次手洗衣物事件中每次用水的量和停顿时间相差却都不大。根据不同用水事件的这一特征可以构建下表所示的水流量波动和停顿时长波动两个属性。

在用水时长和频率属性的基础之上构建用水量和用水波动属性,需要充分利用用水时长和频率属性,如代码清单6所示。

data["水流量"] = data["水流量"] / 60 # 原单位L/min,现转换为L/secsj["总用水量"] = 0 # 给总用水量赋一个初始值0for i in range(len(sj)): Start = sj.loc[i,"事件起始编号"]-1 End = sj.loc[i,"事件终止编号"]-1 if Start != End: for j in range(Start,End): if data.loc[j,"水流量"] != 0: sj.loc[i,"总用水量"] = (data.loc[j + 1,"发生时间"] - data.loc[j,"发生时间"]).seconds* data.loc[j,"水流量"] + sj.loc[i,"总用水量"] sj.loc[i,"总用水量"] = sj.loc[i,"总用水量"] + data.loc[End,"水流量"] * 2 else: sj.loc[i,"总用水量"] = data.loc[Start,"水流量"] * 2 sj["平均水流量"] = sj["总用水量"] / sj["用水时长"] # 定义属性 平均水流量# 构造属性:水流量波动# 水流量波动=∑(((单次水流的值-平均水流量)^2)*持续时间)/用水时长sj["水流量波动"] = 0 # 给水流量波动赋一个初始值0for i in range(len(sj)): Start = sj.loc[i,"事件起始编号"] - 1 End = sj.loc[i,"事件终止编号"] - 1 for j in range(Start,End + 1): if data.loc[j,"水流量"] != 0: slbd = (data.loc[j,"水流量"] - sj.loc[i,"平均水流量"])**2 slsj = (data.loc[j + 1,"发生时间"] - data.loc[j,"发生时间"]).seconds sj.loc[i,"水流量波动"] = slbd * slsj + sj.loc[i,"水流量波动"] sj.loc[i,"水流量波动"] = sj.loc[i,"水流量波动"] / sj.loc[i,"用水时长"] # 构造属性:停顿时长波动# 停顿时长波动=∑(((单次停顿时长-平均停顿时长)^2)*持续时间)/总停顿时长sj["停顿时长波动"] = 0 # 给停顿时长波动赋一个初始值0for i in range(len(sj)): if sj.loc[i,"停顿次数"] > 1: # 当停顿次数为0或1时,停顿时长波动值为0,故排除 for j in Stop.loc[Stop["停顿归属事件"] == (i+1),"停顿时长"].values: sj.loc[i,"停顿时长波动"] = ((j - sj.loc[i,"平均停顿时长"])**2) * j + sj.loc[i,"停顿时长波动"] sj.loc[i,"停顿时长波动"] = sj.loc[i,"停顿时长波动"] / sj.loc[i,"总停顿时长"]print('用水量和波动属性构造完成后数据的属性为:n',sj.columns)print('用水量和波动属性构造完成后数据的前5行5列属性为:n',sj.iloc[:5,:5])5.筛选候选洗浴事件

洗浴事件的识别是建立在一次用水事件识别的基础上,也就是从已经划分好的一次用水事件中识别出哪些一次用水事件是洗浴事件。

可以使用3个比较宽松的条件筛选掉那些非常短暂的用水事件,确定不可能为洗浴事件的数据去除掉,剩余的事件称为“候选洗浴事件”。这三个条件是“或”的关系,也就是说,只要一次完整的用水事件满足任意一个条件,就被判定为短暂用水事件,即会被筛选掉。3个筛选条件如下。

(1)一次用水事件中总用水量小于5升。

(2)用水时长小于100秒。

(3)总用水时长小于120秒。

基于构建的用水时长、用水量属性,筛选候选洗浴事件,如代码清单7所示。

sj_bool = (sj['用水时长'] >100) & (sj['总用水时长'] > 120) & (sj['总用水量'] > 5)sj_final = sj.loc[sj_bool,:]sj_final.to_excel('../tmp/sj_final.xlsx',index=False)print('筛选出候选洗浴事件前的数据形状为:',sj.shape)print('筛选出候选洗浴事件后的数据形状为:',sj_final.shape)

筛选前用水事件数目总共172个,经过筛选后,余下75个用水事件。结合日志,最终用于建模的属性的总数为11个,其基本状况如下表所示。

模型构建

根据建模样本数据建立BP神经网络模型识别洗浴事件。由于洗浴事件与普通用水事件在特征上存在不同,而且这些不同的特征在特征上被体现出来。于是,根据用户提供的用水日志,将其中洗浴事件的数据状态记录作为训练样本训练BP神经网络。然后根据训练好的网络来检验新采集到的数据,具体过程如下图所示。

在训练神经网络的时候,选取了“候选洗浴事件”的11个属性作为网络的输入,分别为:洗浴时间点,总用水时长,总停顿时长,平均停顿时长,停顿次数,用水时长,用水时长/总用水时长,总用水量,平均水流量,水流量波动和停顿时长波动。训练BP网络时给定的输出(教师信号)为1与0,其中1代表该次事件为洗浴事件,0表示该次事件不是洗浴事件。是否为洗浴事件的标签是根据热水器的用水记录日志得到。

构建神经网络模型需要注意数据本身属性之间的存在量级差异,因此需要进行标准化,消除量级差异。另外,为了便于后续应用模型,可以用joblib.dump函数保存模型,如代码清单8所示。

import pandas as pdfrom sklearn.preprocessing import StandardScalerfrom sklearn.neural_network import MLPClassifierfrom sklearn.externals import joblib# 读取数据Xtrain = pd.read_excel('../tmp/sj_final.xlsx')ytrain = pd.read_excel('../data/water_heater_log.xlsx')test = pd.read_excel('../data/test_data.xlsx')# 训练集测试集区分。x_train, x_test, y_train, y_test = Xtrain.iloc[:,5:],test.iloc[:,4:-1], ytrain.iloc[:,-1],test.iloc[:,-1]# 标准化stdScaler = StandardScaler().fit(x_train)x_stdtrain = stdScaler.transform(x_train)x_stdtest = stdScaler.transform(x_test)# 建立模型bpnn = MLPClassifier(hidden_layer_sizes=(17,10), ** x_iter=200, solver='lbfgs',random_state=50)bpnn.fit(x_stdtrain, y_train)# 保存模型joblib.dump(bpnn,'water_heater_nnet.m')print('构建的模型为:n',bpnn)

在训练BP神经网络时,对神经网络的参数进行了寻优,发现含2个隐层的神经网络训练效果较好,其中2个隐层的隐节点数分别为17和10时训练的效果较好。

根据样本,得到训练好的神经网络后,就可以用来识别对应的用户家的洗浴事件,其中待检测的样本的11个属性作为输入,输出层输出一个值在[-1,1]范围内,如果该值小于0,则该事件不是洗浴事件,如果该值大于0,则该事件是洗浴事件。某热水器用户记录了两周的热水器用水日志,将前一周的数据作为训练数据,后一周的数据作为测试数据,代入上述模型进行测试。

模型检验

结合模型评价相关的知识,使用精确率(precision)、召回率(recall)和f1值来做模型评价的效果先顾地较为客观、准确。同时结合ROC曲线,可以进一步更加直观地评价模型的效果,如代码清单9所示。

# 模型评价from sklearn.metrics import classification_reportfrom sklearn.metrics import roc_curvefrom sklearn.externals import joblibimport ** tplotlib.pyplot as pltbpnn = joblib.load('water_heater_nnet.m') # 加载模型y_pred = bpnn.predict(x_stdtest) # 返回预测结果print('神经网络预测结果评价报告:n',classification_report(y_test,y_pred))# 绘制roc曲线图plt.rcParams['font.sans-serif'] = 'SimHei' # 显示中文plt.rcParams['axes.unicode_minus'] = False # 显示负号fpr, tpr, thresholds = roc_curve(y_pred,y_test) # 求出TPR和FPRplt.figure(figsize=(6,4)) # 创建画布plt.plot(fpr,tpr) # 绘制曲线plt.title('用户用水事件识别ROC曲线') # 标题plt.xlabel('FPR') # x轴标签plt.ylabel('TPR') # y轴标签plt.savefig('用户用水事件识别ROC曲线.png') # 保存图片plt.show() # 显示图形

由代码清单9得到模型的ROC曲线,如下图所示,ROC曲线覆盖的面积较大,说明模型的识别效果较好。

根据该热水器用户提供的用水日志判断事件是否为洗浴与多层神经网络模型识别结果报告,如下表所示。

根据模型评估报告表 1014可以看出,在洗浴事件的识别上精确率(precision)非常高,达到了96%,同时召回率(recall)也达到了70%以上。综合上述结果,可以确定此次创建的模型是有效并且效果良好的能够用于实际的洗浴事件的识别中。

小结

本案例以基于实时监控的智能热水器的用户使用数据,构建了BP神经网络洗浴事件识别模型,重点介绍了根据用水停顿时间间隔的阈值划分一次用水事件的过程,以及用水行为特征的构建,最后根据用户用水日志判断模型结果的好坏。

扫码免费用

源码支持二开

申请免费使用

在线咨询