9 家用热水器用户行为分析与事件识别_划分用水事件-程序员宅基地

技术标签: python  机器学习  1024程序员节  pycharm  Python数据分析与应用  

9.1 了解家用热水器用户行为分析的背景与步骤

9.1.1 热水器采集数据基本情况

  国内某热水器生产厂商新研发的一种高端智能热水器,在状态发生改变或者有水流状态时,会采集各项数据。抽取200个热水器用户的用水记录作为原始建模数据,热水器采集到用户用水数据如下表所示。
在这里插入图片描述
  热水器采集的用水数据包含12个特征:热水器编码,发生时间,开关机状态,加热中,保温中,有无水流,实际温度,热水量,水流量,节能模式,加热剩余时间和当前设置温度。其解释说明如下表所示。
在这里插入图片描述

9.1.2 熟悉家用热水器用户行为分析的步骤与流程

  在热水器用户行为分析过程中,用水事件识别是最为关键的环节。根据该热水器生产厂商提供的数据热水器用户用水事件划分与识别项目的整体目标如下。
  (1)根据热水器采集到的数据,划分一次完整用水事件。
  (2)在划分好的一次完整用水事件中,识别出洗浴事件。
热水器用户事件划分与识别项目的总体流程图:
在这里插入图片描述
  热水器用户用水事件划分与识别项目主要包括以下5个步骤。
  ① 对热水用户的历史用水数据进行选择性抽取,构建专家样本。
  ② 对①形成的数据集进行数据探索分析与预处理,包括探索用水事件时间间隔的分布,删除冗余特征,识别用水数据的缺失值,并对缺失值作处理,根据建模的需要进行特征构造等。根据以上处理,对用水样本数据建立用水事件时间间隔识别模型和划分一次完整的用水事件模型,再在一次完整用水事件划分结果的基础上,剔除短暂用水事件缩小识别范围等。
  ③ 在②得到的建模样本数据基础上,建立洗浴事件识别模型,对洗浴事件识别模型进行模型分析评价。
  ④ 对③形成的模型结果应用并对洗浴事件划分进行优化。
  ⑤ 调用洗浴事件识别模型,对实时监控的热水器流水数据进行洗浴事件自动识别。

9.2 预处理热水器用户用水数据

9.2.1 删除冗余特征

  由于热水器采集的用水数据特征较多,本项目特征删除对分析无影响的特征。因分析的主要对象为用户,分析的主要目标为用户洗浴行为的一般规律,所以“热水器编号”特征可以去除:在热水器采集的数据中,“有无水流”特征可以通过“水流量”特征反应,“节能模式”特征取值相同,均为“关”,对分析无作用,可以去除。最终用来建模的特征如下表所示。
在这里插入图片描述

9.2.2 划分用水事件

  为了探究用户真实用水停顿时间间隔的分布情况,通过频率分布表分析用户用水停顿时间间隔的规律性,从而探究划分一次完整用水事件的时间间隔阈值。具体的数据如下表所示。
在这里插入图片描述

  分析上表可知:

  • 停顿时间间隔为0~0.3分钟的频率很高,根据日常用水经验可以判断其为一次用水时间中的停顿。
  • 停顿时间间隔为6~13分钟的频率较低,分析其为两次用水事件之间的停顿间隔。
  • 两次用水事件的停顿时间间隔分布在3~7分钟。
    根据现场实验统计用水停顿的时间间隔近似。

  用户的用水数据存储在数据库中,记录了各种各样的用水事件,包括洗浴,洗手,刷牙,洗脸,洗衣,洗菜等,而且一次用水事件由数条甚至数千条的状态记录组成。所以本项目首先需要在大量的状态记录中划分出哪些连续的数据是一次完整的用水事件。
  用水状态记录中,水流量不为0表明用户正在使用热水;而水流量为0时用户用热水发生停顿或者用热水结束。对于任一个用水记录,如果它的向前时差超过阈值T,则将它记为事件的开始编号;如果向后时差超过阈值T ,则将其记为事件的结束编号。划分模型的符号说明如下表。
在这里插入图片描述
  一次完整用水事件的划分步骤如下:
  ① 读取数据记录,识别到所有水流量不为0的状态记录,将它们的发生时间记为序列t1。
  ② 对序列t1构建其向前时差列和向后时差列,并分别与阈值进行比较。向前时差超过阈值T,则将它记为新的用水事件的开始编号;如果向后时差超过阈值T,则将其记为用水事件的结束编号。
  ③ 循环执行步骤②直到向前时差列和向后时差列与均值比较完毕,结束事件划分。

对用户的用水数据进行划分结果如下表所示。
在这里插入图片描述

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

对某热水器用户的数据根据不同的阈值划分用水事件,得到了相应的事件个数,阈值变化与划分得到事件个数如下。
在这里插入图片描述

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

  曲线在上图中方框趋于稳定时,其方框开始的点的斜率趋于一个较小的值。为了用程序来识别这一特征,将这一特征提取为规则。根据下图说明如何识别上图方框中的起始的时间。
在这里插入图片描述
  每个阈值对应一个点,给每个阈值计算得到一个斜率指标,如上图所示。其中, A 点是要计算的斜率指标点。为了直观的展示,用下表所示的符号来进行说明。
在这里插入图片描述
在这里插入图片描述
  将K作为A点的斜率指标,特别指出横坐标上的最后四个点没有斜率指标,因为找不出在它以后的4个更长的阈值。但这不影响对最优阈值的寻找,因为可以提高阈值的上限,以使最后的4个阈值不是考虑范围内的阈值。
  阈值优化的结果如下∶
  (1)当存在一个阈值的斜率指标K<1时,则取阈值最小的点A(可能存在多个阈值的斜率指标小于1 )的横坐标xA作为用水事件划分的阈值,其中K<1中的“1”是经过实际数据验证的一个专家阈值。
  (2)当不存在K<1时,则找所有阈值中斜率指标最小的阈值; 如果该阈值的斜率指标小于5,则取该阈值作为用水事件划分的阈值; 如果该阈值的斜率指标不小于5,则阈值取默认值的阈值4分钟。其中斜率指标小于5中的“5”是经过实际数据验证的一个专家阈值。

9.2.4 代码

1、删除冗余特征

# 代码 9-1 删除冗余特征
import pandas as pd
data = pd.read_excel('F:/书籍/Python数据分析与应用/df/original_data.xls')
print('初始状态的数据形状为:', data.shape)
# 删除热水器编号,有无水流,节能模式
data.drop(labels=["热水器编号", "有无水流", "节能模式"], axis=1, inplace=True)
print('删除冗余特征后的数据形状为:', data.shape)
data.to_csv('F:/书籍/Python数据分析与应用/df/water_heart.csv', index=False)

在这里插入图片描述

2、 划分用水事件

# 代码 9-2 划分用水事件
import numpy as np
import pandas as pd
data = pd.read_excel('F:/书籍/Python数据分析与应用/df/original_data.xls')
threshold = pd.Timedelta('4 min') # 阈值为分钟
data['发生时间'] = pd.to_datetime(data['发生时间'], format='%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('F:/书籍/Python数据分析与应用/df/sj.csv', index=False)

在这里插入图片描述

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

# 代码 9-3 确定单次用水时长
import numpy as np
import pandas as pd
data = pd.read_excel('F:/书籍/Python数据分析与应用/df/original_data.xls')
n = 4  # 使用以后四个点的平均斜率
threshold = pd.Timedelta(minutes=5) # 专家阈值
data['发生时间'] = pd.to_datetime(data['发生时间'],
    format = '%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  # 计算每两个相邻点对应的斜率
# 往前取n个斜率绝对值平均作为斜率指标
h['斜率指标']= h['斜率'].abs().rolling(4).mean()
ts = h['阈值'][h['斜率指标'].idxmin() - n]
# 用idxmin返回最小值的Index,由于rolling_mean()计算的是前n个斜率的绝对值平均
# 所以结果要进行平移(-n)
if ts > threshold:
    ts = pd.Timedelta(minutes=4)
print('计算出的单次用水时长的阈值为:', ts)

在这里插入图片描述

9.3 构建用水行为特征并筛选用水事件

9.3.1 构建用水时长与频率特征

  不同用水事件的用水时长是基础特征之一。根据用水时长这一特征可以构建下表所示的事件开始时间、事件结束时间、洗浴时间点、用水时长、总用水时长和用水时长/总用水时长这6个特征。
在这里插入图片描述
  构建用水开始时间或结束的时间两个特征时分别减去或加上了发送阈值(发送阈值是指热水器传输数据的频率的大小)。其原因如图所示。在20:00:10时热水器记录到的数据显示还没有用水,而在 20:00:12 时,热水器记录到用水行为。所以用水开始时间在20:00:10 - 20:00:12之间考虑到网络不稳定会导致网络数据传输延时数分钟或数小时等因素。取平均值会导致很大的偏差,综合分析构建“用水开始时间”为起始数据的时间减去“发送阈值”的一半。
在这里插入图片描述
  与用水时长相关的特征只能够区分出一部分用水事件,不同用水事件的用水停顿和频率不同。例如,一次完整洗漱事件的停顿次数不多,停顿的时间长短不一,平均停顿时长较短;一次手洗衣物事件的停顿次数较多,停顿时间相差不大,平均停顿时长一般。根据这特征,可以构建表所示的停顿时长、总停顿时长、平均停顿时长和停顿次数4个特征。
在这里插入图片描述

9.3.2 构建用水量与波动特征

  除了用水时长,停顿和频率外,用水量也是识别该事件是否为洗浴事件的重要特征。例如,用水时间中的洗漱事件相比洗浴事件有 停顿次数多、用水总量少 和 平均用水少 的特点。手洗大量衣物的事件相比于洗浴事件则有 停顿次数多、用水总量多 和 平均用水量多 的特点。根据这一原因可以构建出下表所示的两个用水量特征。
在这里插入图片描述
  同时用水波动也是区分不同用水事件的关键。一般在一次洗漱事件中,刷牙和洗脸的用水量完全不同;在一次手洗衣物事件中,每次用水的量和停顿时间相差却都不大。根据不同用水事件的这一特征可以构建下表所示的水流量波动和停顿时长波动两个特征。
在这里插入图片描述

9.3.3 筛选候选洗浴事件

  洗浴事件的识别是建立在一次用水事件识别的基础上,也就是从已经划分好的一次用水事件中识别出哪些一次用水事件是洗浴事件。
  可以使用3个比较宽松的条件筛选掉那些非常短暂的用水事件,确定不可能为洗浴事件的数据去除掉,剩余的事件称为“候选洗浴事件”。这三个条件是“或”的关系,也就是说,只要一次完整的用水事件满足任意一个条件,就被判定为短暂用水事件,即会被筛选掉。3个筛选条件如下:

  • 一次用水事件中总用水量小于5升。
  • 用水时长小于100秒。
  • 总用水时长小于120秒。

经过筛选后,用水事件数目从172个减少为75个。结合日志,最终用于建模的特征的总数为11个,如下。
在这里插入图片描述

9.3.4 代码: 构建用水时长与频率特征;构建用水量与波动特征;筛选候选洗浴事件

# 代码 9-4 构建用水时长与频率特征
import pandas as pd
import numpy as np

# 读取热水器使用数据记录
data = pd.read_excel('F:/书籍/Python数据分析与应用/df/water_hearter.xlsx')
# 读取用水事件记录
sj = pd.read_csv('F:/书籍/Python数据分析与应用/df/sj.csv')
data["发生时间"] = pd.to_datetime(data["发生时间"],
                                  format="%Y%m%d%H%M%S")  # 转换时间格式
# 构造特征:总用水时长
timeDel = pd.Timedelta("0.5 sec")
sj["事件开始时间"] = data.iloc[sj["事件起始编号"] - 1, 0].values - timeDel
sj["事件结束时间"] = \
    data.iloc[sj["事件终止编号"] - 1, 0].values + timeDel
sj['洗浴时间点'] = [i.hour for i in sj["事件开始时间"]]
tmp1 = sj["事件结束时间"] - sj["事件开始时间"]
sj["总用水时长"] = np.int64(tmp1) / 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()] + 1
indStopEnd = data.index[data["停顿结束时间"].notnull()] + 1
Stop = pd.DataFrame(data={
    "停顿开始编号": indStopStart[:-1],
                          "停顿结束编号": indStopEnd[1:]})
# 计算停顿时长,并放在数据框stop中,停顿时长=停顿结束时间-停顿结束时间
tmp2 = data.loc[indStopEnd[1:] - 1, "停顿结束时间"]
tmp3 = data.loc[indStopStart[:-1] - 1, "停顿开始时间"]
tmp4 = tmp2.values - tmp3.values
Stop["停顿时长"] = np.int64(tmp4) / 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[:, "停顿时长"].values
sj.loc[stopAgg.index - 1, "停顿次数"] = \
    stopAgg.loc[:, "停顿开始编号"].values
sj.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])

# 代码 9-5 构建用水量与波动特征
data["水流量"] = data["水流量"] / 60  # 原单位L/min,现转换为L/sec
sj["总用水量"] = 0  # 给总用水量赋一个初始值0
for 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  # 给水流量波动赋一个初始值0
for 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  # 给停顿时长波动赋一个初始值0
for i in range(len(sj)):
    # 当停顿次数为0或1时,停顿时长波动值为0,故排除
    if sj.loc[i, "停顿次数"] > 1:
        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])

# 代码 9-6 筛选候选洗浴事件
sj_bool = (sj['用水时长'] > 100) & \
          (sj['总用水时长'] > 120) & (sj['总用水量'] > 5)
sj_final = sj.loc[sj_bool, :]
sj_final.to_excel('F:/书籍/Python数据分析与应用/df/sj_final.xlsx', index=False)
print('筛选出候选洗浴事件前的数据形状为:', sj.shape)
print('筛选出候选洗浴事件后的数据形状为:', sj_final.shape)

9.4 构建行为事件分析的BP神经网络模型

9.4.1 了解BP神经网络算法原理

  神经网络,全称人工神经网络(Artificial Neural Network,ANN),是一种模仿生物神经网络的结构和功能的数学模型或计算模型。
  以误差逆传播算法(即BP算法)而得名的BP神经网络模型(Back Propagation ANNS,简称BP网络)是神经网络中重要的网络之一。

  • 具有很强的非线性动态处理能力
  • 无须知道输入与输出之间的关系
  • 可实现高度的非线性映射

  由于其结构简单、可塑性强,在电力、交通和医疗等许多领域得到了广泛的应用。

1、原理概述

  BP神经网络由输入层、一个或多个隐藏层以及输出层构成。同层节点中没有任何耦合,每一层节点的输出只影响下一层节点的输出。网络的学习过程由正向和反向传播两部分组成。反向传播其节点单元特征通常为Sigmoid函数,如下。
S ( x ) = 1 1 + e − x S(x) = \frac{1}{1+ e^{-x}} S(x)=1+ex1
  在训练阶段用准备好的样本数据以此通过输入层、隐藏层和输出层,比较输出结果和期望值,若没有达到要求的误差程度或者训练次数,即通过输出层、隐藏层和输入层,来调节权值,以便使网络成为一定适应能力的模型。
  BP神经网络模型结构如下图所示。
在这里插入图片描述
  BP神经网络模型算法流程图
在这里插入图片描述

2、优缺点与应用领域

优点
  BP神经网络是目前应用最多的一种神经网络形式,它具备神经网络的普遍优点。有以下几个方面:

  1. 非线性映射能力。
  2. 自学习和自适应能力。
  3. 泛化能力。
  4. 容错能力。

缺点
  虽然BP网络得到了广泛的应用,但自身也存在一些缺陷和不足,主要包括以下几个方面的问题:

  1. 由于学习速率是固定的,因此网络的收敛速度慢,需要较长的训练时间。
  2. BP算法可以使权值收敛到某个值,但并不保证其为误差平面的全局最小值,这是因为采用梯度下降法可能产生一个局部最小值。
  3. 网络隐含层的层数和单元数的选择尚无理论上的指导,一般是根据经验或者通过反复实验确定。
  4. 网络的学习和记忆具有不稳定性。

应用领域
BP神经网络以其独特的结构和处理信息的方法,在许多实际应用领域中取得显著的成效,主要应用如下:
图像处理
信号处理
模式识别
机器人控制。
卫生保健,医疗
焊接领域。
经济。
此外,在电力系统、交通、军事、矿业、农业和气象等方面也有应用。

3、主要参数介绍

对于多层神经网络,scikit-learn提供了MLPClassifier类。其使用语法如下。(只展示了常用参数)

MLPClassifier(hidden_layer_sizes=(100, ), activation=’relu’, solver=’adam’, alpha=0.0001, max_iter=200, tol=0.0001, verbose=False…)

常用参数及说明如下。
在这里插入图片描述
sklearn中的模型构建训练完成后,不同的模型能够根据训练的数据输出不同的属性,MLPClassifier神经网络模型的主要属性如下表所示。
在这里插入图片描述

9.4.2 构建模型

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

  • 在训练神经网络的时候,选取了“候选洗浴事件”的11个特征作为网络的输入,分别为:洗浴时间点,总用水时长,总停顿时长,平均停顿时长,停顿次数,用水时长,用水时长/总用水时长,总用水量,平均水流量,水流量波动和停顿时长波动。
  • 训练BP网络时给定的输出(教师信号)为1与0,其中1代表该次事件为洗浴事件,0表示该次事件不是洗浴事件。是否为洗浴事件的标签是根据热水器的用水记录日志得到。
  • 在训练BP神经网络时,对神经网络的参数进行了寻优,发现含2个隐层的神经网络训练效果较好,其中2个隐层的隐节点数分别为17和10时训练的效果较好。

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

9.4.3 评估模型

  根据该热水器用户提供的用水日志判断事件是否为洗浴与多层神经网络模型识别结果报告,如下表所示。
在这里插入图片描述
  根据模型评估报告可以看出,在洗浴事件的识别上精确率(precision)非常高,达到了96%,同时召回率(recall)也达到了70%以上。综合上述结果,可以确定此次创建的模型是有效并且效果良好的能够用于实际的洗浴事件的识别别中。

9.4.4 代码:构建并评价神经网络模型

# 代码 9-7 构建神经网络模型
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import classification_report
from sklearn.metrics import roc_curve
import matplotlib.pyplot as plt
# from sklearn.externals import joblib

# 读取数据
Xtrain = pd.read_excel('F:/书籍/Python数据分析与应用/df/sj_final.xlsx')
ytrain = pd.read_excel('F:/书籍/Python数据分析与应用/df/water_heater_log.xlsx')
test = pd.read_excel('F:/书籍/Python数据分析与应用/df/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),
                     max_iter=200, solver='lbfgs', random_state=45)
bpnn.fit(x_stdtrain, y_train)
# 保存模型
# joblib.dump(bpnn,'water_heater_nnet.m')
print('构建的模型为:\n', bpnn)

# 代码 9-8 神经网络模型评价
# 模型预测
# bpnn = 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和FPR
plt.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.5 小结

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/QMU111/article/details/133682788

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签