Maximilian Strauß作者丁楠雅校对笪洁琼翻译

使用LSTMs和Prophet进行时间序列预测你的电子邮箱负载(附代码)

本文通过基线模型、LSTMs和Facebook的Prophet模型来预测每天的电子邮箱负荷,并详细解析了生成训练数据集的过程以及相应代码。

时间序列预测数据科学算法提供了一个极好的训练场。

毕竟,如果一个人能够预测未来,那该有多牛逼啊!通常用来演示预测算法的典型数据集是股票图表、销售和气象数据。在这里,我们将尝试一些与每个用户更相关的东西,即你将收到的电子邮件的数量。根据一份电子邮件统计报告,2014年上半年,上班族平均每天收到85封电子邮件(http:/www.Radati.com/wp/wp-content/upploads/2014/01/Email-Statistics-Report-2014-2018-Executive-Summary.pdf)。

我们想尝试根据历史收件数据做出准确的预测。为此,我们将探索运用LSTMs和Facebook的Prophet。这里的目标是如何为不同的算法准备数据,并提供定性的概述,而不是精细化。预测结果将根据历史收到的电子邮件数量以及所准备的训练数据而有很大的不同。

收集数据

IBM imapclient sql包的帮助下,我们从使用自己的收件箱创建数据集开始。

关于Automatetheboringstuff.com/Chapter16/,可以在Automatetheboringstuff.com一章地址:https:/Automatetheboringstuff.com/中找到很好的介绍。

我们将加载过去三年(从2016年1月1日开始)的所有电子邮件,并获取主题和日期。我们会使用Pandas将其转换为一个数据文件(Dataframe)。

Code:

import imapclient

import pandas as pd

import getpass

 

youremail = input()

yourpassword = getpass.getpass()

 

# Replace 'imap.gmail.com' with provider of choice

imapObj = imapclient.IMAPClient("imap.gmail.com", ssl=True)

imapObj.login(youremail, yourpassword)

imapObj.select_folder("INBOX", readonly=True)

 

UIDs = imapObj.search('(SINCE "01-Jan-2016")')

 

mails = []

for msgid, data in imapObj.fetch(UIDs, ["ENVELOPE"]).items():

    envelope = data[b"ENVELOPE"]

    date = envelope.date

    if envelope.subject is not None:

        subject = envelope.subject.decode()

    else:

        subject = None

    mails.append((subject, date))

 

mail_df = pd.DataFrame(mails)

mail_df.columns = ["Subject", "Date"]

mail_df["Date"] = pd.to_datetime(mail_df["Date"])

mail_df = mail_df.set_index("Date")

 

print("A total of {} e-mails loaded.".format(len(mail_df)))

邮箱负载:总计12738封邮件

我们现在得到了12738封用于训练的电子邮件。请注意,上面的代码是针对一个装满电子邮件的收件箱。如果你已将电子邮件放在不同的文件夹中,请相应地调整代码。包括我在内的一些人会立即删除那些不重要的电子邮件。然后,模型输出的是重要邮件的数量,而不是实际收到的电子邮件数量。还要注意的是,一些电子邮件提供商(如Google)会阻止这个连接,因为他们不允许“不太安全”的应用程序连接到他们的服务。你可以在其设置中启用此功能。原则上,你还可以检验(签出)本地邮箱。检验(签出)统一邮箱程序包可能是个好的开始。

数据探索

现在,让我们首先做一些可视化的数据探索和图表,每小时和每天的电子邮件数量。对于这一点,我们将使用pandas及重新采样的groubpy 函数(聚合分组运算)。通过使用sum()和count()参数,我们可以对每个时间间隔进行标准化。另外,我们还使用了seaborn函数来实现可视化。

Code:

import calendar

import seaborn as sns

import matplotlib.pyplot as plt

 

weekdays = [calendar.day_name[i] for i in range(7)]

 

# E-Mails per Hour

per_hour = pd.DataFrame(mail_df["Subject"].resample("h").count())

per_hour_day = (

    per_hour.groupby([per_hour.index.hour]).sum()

    / per_hour.groupby([per_hour.index.hour]).count()

)

per_hour_day.reset_index(inplace=True)

per_hour_day.columns = ["Hour", "Count"]

 

# E-Mails per day

per_day = pd.DataFrame(mail_df["Subject"].resample("d").count())

per_day_week = (

    per_day.groupby([per_day.index.weekday]).sum()

    / per_day.groupby([per_day.index.weekday]).count()

)

per_day_week.reset_index(inplace=True)

per_day_week.columns = ["Weekday", "Count"]

per_day_week["Weekday"] = weekdays

 

def return_cmap(data):

    # Function to create a colormap

    v = data["Count"].values

    colors = plt.cm.RdBu_r((v - v.min()) / (v.max() - v.min()))

    return colors

 

plt.figure(figsize=(12, 10), dpi=600)

plt.subplot(2, 1, 1)

cmap = return_cmap(per_hour_day)

sns.barplot(x="Hour", y="Count", data=per_hour_day, palette=cmap)

plt.title("Emails per hour")

 

plt.subplot(2, 1, 2)

cmap = return_cmap(per_day_week)

sns.barplot(x="Weekday", y="Count", data=per_day_week, palette=cmap)

plt.title("Emails per weekday")

 

plt.show()

 

print(

    "Average number of emails per day: {:.2f}".format(

        per_hour_day.sum()["Count"]

    )

)

图1:探索数据(图)

从数据中我们可以看到一个明显的规律,即分发遵循一个典型模式,从星期一至星期五早9-晚5的时间表,其中上午10点收到的邮件最多。由于每小时电子邮件的最大值仅略高于1,因此对每小时进行预测是没有意义的。我们将尝试预测每天的电子邮件工作量。

初步考虑:预测不可预测的

在深入研究我们的模型之前,有必要考虑一下这个问题的相关假设。接收电子邮件的时间在某种程度上是随机的,因此无法预测。在大多数情况下,发送方互相不认识,因此可以假设它们在统计上是独立的。另外,我们可以将收件近似为泊松过程(Poissonian),它的标准偏差等于平均值。由于分布是随机的,12封电子邮件的期望的RMSE值至少为3.5(sqrt(11.95)~3.461)。

如果你想进一步阅读,我建议阅读有关DB2Erlang发行版

(https:/en.wikipea.org/wiki/Erlang_Distributions)的内容,研究该发行版是为了检验在某一时期内的电话呼出的数量。(https:/en.wikipea.org/wiki/erlang_Distributions)

计算基准线

为了创建基线模型,我们可以使用历史数据中的查询表。对于任意的某一天,计算在一天之前收到的电子邮件数量。为了对模型进行基准测试,我使用一个移动窗口来创建查询表,并计算到第二天的预测差异值。

Code:

from sklearn.metrics import mean_squared_error

import numpy as np

 

data = per_day.copy()

test_split = int(len(data) * 0.8)

 

pred_base = []

for i in range(len(data) - test_split):

    train_data = data[i : test_split + i]

    test_data = data.iloc[test_split + i]

    train_data_week = (

        train_data.groupby([train_data.index.weekday]).sum()

        / train_data.groupby([train_data.index.weekday]).count()

    )

    baseline_prediction = train_data_week.loc[test_data.name.weekday()]

    pred_base.append(baseline_prediction.values)

 

test_data = data[test_split:]

 

mse_baseline = mean_squared_error(test_data.values, pred_base)

 

print("RMSE for BASELINE {:.2f}".format(np.sqrt(mse_baseline)))


RMSE 基线 7.39

基线模型得到的RMSE为7.39 ,相对于3.5的预期值,这个结果还不错。

使用LSTM进行预测

作为进阶模型,我们将使用长短时记忆(LSTM)神经网络。在这里可以找到对LSTM的很好的介绍(https:/machinelearningmaster ery.com/time-Series-prediction-lstm-rrurn-neuro-network-python-keras/)。

在这里,我们将窗口设置为6天,并让模型预测第7天。

Code:

import numpy as np

from keras.models import Sequential

from keras.layers import Dense, Dropout, Activation

from keras.layers import LSTM

from sklearn.preprocessing import MinMaxScaler

 

def get_window_data(data, window):

    # Get window data and scale

    scaler = MinMaxScaler(feature_range=(0, 1))

    data = scaler.fit_transform(data.reshape(-1, 1))

 

    X = []

    y = []

 

    for i in range(len(data) - window - 1):

        X.append(data[i : i + window])

        y.append(data[i + window + 1])

 

    X = np.asarray(X)

    y = np.asarray(y)

    return X, y, scaler

 

 

window_size = 6

X, y, scaler = get_window_data(per_day["Subject"].values, window_size)

 

X_train = X[:test_split]

X_test = X[test_split:]

 

y_train = y[:test_split]

y_test = y[test_split:]

 

model = Sequential()

model.add(LSTM(50, input_shape=(window_size, 1)))

model.add(Dropout(0.2))

model.add(Dense(1))

model.add(Activation("linear"))

model.compile(loss="mse", optimizer="adam")

 

 

history = model.fit(

    X_train,

    y_train,

    epochs=20,

    batch_size=1,

    validation_data=(X_test, y_test),

    verbose=2,

    shuffle=False,

)

 

# plot history

 

plt.figure(figsize=(6, 5), dpi=600)

plt.plot(history.history["loss"], 'darkred', label="Train")

plt.plot(history.history["val_loss"], 'darkblue', label="Test")

plt.title("Loss over epoch")

plt.xlabel('Epoch')

plt.ylabel('Loss')

plt.legend()

plt.show()

 

mse_lstm = mean_squared_error(

    scaler.inverse_transform(y_test),

    scaler.inverse_transform(model.predict(X_test)),

)

print("RMSE for LSTM {:.2f}".format(np.sqrt(mse_lstm)))

LSTM为7.26RMSE

图2:训练LSTM

通过观察损失函数,我们可以看到LSTM网络通过迭代学习更好地预测了未来。它的RMSE比基线模型要好一些,但也并没有好多少。这一结果表明,LSTM网络能够学习周末和工作日的结构,周末收到的电子邮件更少。另外值得一提的是,我们不能直接将基线的RMSE与LSTM进行比较。因为LSTM实际上只用到了80%的训练数据。我们也可以使用全部数据来训练LSTM,然而从计算上来说,这样做的代价要高得多。

使用Prophet进行预测

接下来,我们将使用facebook的Prophet库(https://github.com/facebook/prophet)。它是一个加性模型,我们可以用年、周和日的季节性来拟合非线性趋势。同样地,我们将把数据分成训练集和测试集,并计算RMSE值。

Code:

from fbprophet import Prophet

from tqdm import tqdm

 

prophet_data = data.reset_index()

prophet_data["ds"] = prophet_data["Date"]

prophet_data["y"] = prophet_data["Subject"]

 

pred = []

for i in tqdm(range(len(data) - test_split)):

 

    data_to_fit = prophet_data[: (test_split + i)]

    prophet_model = Prophet(interval_width=0.95)

 

    prophet_model.fit(data_to_fit)

 

    prophet_forecast = prophet_model.make_future_dataframe(periods=1, freq="d")

    prophet_forecast = prophet_model.predict(prophet_forecast)

 

    pred.append(prophet_forecast["yhat"].iloc[-1])

 

 

mse_prophet = mean_squared_error(test_data.values, pred)

 

print("RMSE for PROPHET {:.2f}".format(np.sqrt(mse_prophet)))

PROPHET为6.96RMSE

从RMSE来看,Prophet模型实现了最佳性能。现在让我们研究一下这是为什么。为此,我们绘制模型的各个构成部分,以便更好地理解模型所做的工作。这只需要使用性能指标函数就可以输出结果。

(输出)

from fbprophet.diagnostics import performance_metricsprophet_model.plot_components(prophet_forecast)

图3:Prophet模型的构成

通过检查Prophet模型的构成,我们可以看到它识别出了数据中的关键趋势。总趋势表明邮件数量在整体上不断增加。每周的季节性准确地描绘了工作日/周末的时间波动。每年的季节性显示主要节日,即新年电子邮件很少,但在圣诞节前有所增加,而低点在9月份。

总结:预测邮件数量。

最后,我们使用Prophet模型作为我们的预测工具。为此,我们再次登录到IMAP服务器,并使用自2016年1月1日以来的所有历史数据来训练我们的预测模型。训练完毕后,我们绘制了前一周的历史数据和下一周的预测情况。

Code:

from datetime import datetime, timedelta

import matplotlib.pyplot as plt

from fbprophet import Prophet

import imapclient

import pandas as pd

import getpass 

youremail = input()

yourpassword = getpass.getpass()

 

imapObj = imapclient.IMAPClient("imap.gmail.com", ssl=True)

imapObj.login(youremail, yourpassword)

imapObj.select_folder("INBOX", readonly=True)

 

UIDs = imapObj.search('(SINCE "01-Jan-2016")')

 

mails = []

for msgid, data in imapObj.fetch(UIDs, ["ENVELOPE"]).items():

    envelope = data[b"ENVELOPE"]

    date = envelope.date

    if envelope.subject is not None:

        subject = envelope.subject.decode()

    else:

        subject = None

    mails.append((subject, date))

 

mail_df = pd.DataFrame(mails)

mail_df.columns = ["Subject", "Date"]

mail_df["Date"] = pd.to_datetime(mail_df["Date"])

mail_df = mail_df.set_index("Date")

 

data = pd.DataFrame(mail_df["Subject"].resample("d").count())

 

prophet_model = Prophet(interval_width=0.95)

 

prophet_data = data.reset_index()

prophet_data["ds"] = prophet_data["Date"]

prophet_data["y"] = prophet_data["Subject"]

 

prophet_model.fit(prophet_data)

 

prophet_forecast = prophet_model.make_future_dataframe(periods=7, freq="d")

prophet_forecast = prophet_model.predict(prophet_forecast)

 

fig1 = prophet_model.plot(prophet_forecast)

 

datenow = datetime.now()

dateend = datenow + timedelta(days=7)

datestart = dateend - timedelta(days=14)

 

plt.xlim([datestart, dateend])

plt.title("Email forecast", fontsize=20)

plt.xlabel("Day", fontsize=20)

plt.ylabel("Emails expected", fontsize=20)

plt.axvline(datenow, color="k", linestyle=":")

plt.show()

图4:即将到来一周的邮件预测

讨论

LSTMS和Facebook的Prophet模型提供了一种简单易懂的方法来预测电子邮件数量,而且具有相当好的准确性。考虑到模型的基本机制,这一结果是可以理解的。LSTM预测是基于一组最后的值,因此不太容易考虑到季节差异。相比之下,Prophet模型发现并显示了季节性。

该模型可以对规划未来的工作量或人员配置提供参考。

这类问题的一个关键挑战是,如果只有零星的偶发事件,那么内在趋势是不可预测的。

最后别忘了,我们的基线模型的表现就很不错,所以你不一定总是需要复杂的机器学习算法来搭建你的预测模型。

原文地址:

https://towardsdatascience.com/time-series-forecasting-with-lstms-and-prophet-predict-your-email-workload-48bf9cdb1580

THU数据派
THU数据派

THU数据派"基于清华,放眼世界",以扎实的理工功底闯荡“数据江湖”。发布全球大数据资讯,定期组织线下活动,分享前沿产业动态。了解清华大数据,敬请关注姐妹号“数据派THU”。

工程神经网络泊松过程时间预测ProphetLSTMs
5
相关数据
IBM机构

是美国一家跨国科技公司及咨询公司,总部位于纽约州阿蒙克市。IBM主要客户是政府和企业。IBM生产并销售计算机硬件及软件,并且为系统架构和网络托管提供咨询服务。截止2013年,IBM已在全球拥有12个研究实验室和大量的软件开发基地。IBM虽然是一家商业公司,但在材料、化学、物理等科学领域却也有很高的成就,利用这些学术研究为基础,发明很多产品。比较有名的IBM发明的产品包括硬盘、自动柜员机、通用产品代码、SQL、关系数据库管理系统、DRAM及沃森。

https://www.ibm.com/us-en/
相关技术
机器学习技术

机器学习是人工智能的一个分支,是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、计算复杂性理论等多门学科。机器学习理论主要是设计和分析一些让计算机可以自动“学习”的算法。因为学习算法中涉及了大量的统计学理论,机器学习与推断统计学联系尤为密切,也被称为统计学习理论。算法设计方面,机器学习理论关注可以实现的,行之有效的学习算法。

Dropout技术

神经网络训练中防止过拟合的一种技术

基准技术

一种简单的模型或启发法,用作比较模型效果时的参考点。基准有助于模型开发者针对特定问题量化最低预期效果。

参数技术

在数学和统计学裡,参数(英语:parameter)是使用通用变量来建立函数和变量之间关系(当这种关系很难用方程来阐述时)的一个数量。

数据科学技术

数据科学,又称资料科学,是一门利用数据学习知识的学科,其目标是通过从数据中提取出有价值的部分来生产数据产品。它结合了诸多领域中的理论和技术,包括应用数学、统计、模式识别、机器学习、数据可视化、数据仓库以及高性能计算。数据科学通过运用各种相关的数据来帮助非专业人士理解问题。

规划技术

人工智能领域的「规划」通常是指智能体执行的任务/动作的自动规划和调度,其目的是进行资源的优化。常见的规划方法包括经典规划(Classical Planning)、分层任务网络(HTN)和 logistics 规划。

损失函数技术

在数学优化,统计学,计量经济学,决策理论,机器学习和计算神经科学等领域,损失函数或成本函数是将一或多个变量的一个事件或值映射为可以直观地表示某种与之相关“成本”的实数的函数。

神经网络技术

(人工)神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型,那时候研究者构想了「感知器(perceptron)」的想法。这一领域的研究者通常被称为「联结主义者(Connectionist)」,因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型,它们都是前馈神经网络:卷积神经网络(CNN)和循环神经网络(RNN),其中 RNN 又包含长短期记忆(LSTM)、门控循环单元(GRU)等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习,但也有一些为无监督学习设计的变体,比如自动编码器和生成对抗网络(GAN)。

查询技术

一般来说,查询是询问的一种形式。它在不同的学科里涵义有所不同。在信息检索领域,查询指的是数据库和信息系统对信息检索的精确要求

泊松过程技术

Poisson过程是以法国数学家泊松(1781 - 1840)的名字命名的。泊松过程是随机过程的一种,是以事件的发生时间来定义的。

时间序列预测技术

时间序列预测法其实是一种回归预测方法,属于定量预测,其基本原理是;一方面承认事物发展的延续性,运用过去时间序列的数据进行统计分析,推测出事物的发展趋势;另一方面充分考虑到偶然因素影响而产生的随机性,为了消除随机波动的影响,利用历史数据进行统计分析,并对数据进行适当处理,进行趋势预测。

推荐文章
暂无评论
暂无评论~