開發與維運

带你读《机器学习即服务 将Python机器学习创意快速转变为 云端Web应用程序》之二:在Azure上进行共享单车 回归模型智能预测

点击查看第一章
点击查看第三章

第2章

在Azure上进行共享单车回归模型智能预测
本章中,我们将介绍如何在微软Azure公有云上,利用云端回归系数功能构建一个实现自行车租赁需求的交互式Web应用。
作为本书介绍的第一个智能项目,我们将使用回归建模方式来模拟Capital Bikeshare系统中的自行车共享数据集,并了解温度、风和时间等变量是如何影响美国中大西洋地区的自行车租赁需求的,如图2-1所示。


image.png

加州大学尔湾分校的UCI机器学习库慷慨贡献了本章所需的数据集(https://archive.ics.uci.edu/ml/datasets/bike+sharing+dataset )。在进行本章实验前,请访问UCI网站下载相关数据集。
提示: 请访问www.apress.com/9781484238721 ,单击“Download source code”按钮,跳转至GitHub网站,下载第2章所需文件。推荐使用Jupyter Notebook打开chapter2.ipynb文件,以配合阅读本章内容。

2.1 共享单车租赁需求回归系数分析

本章中,我们将构建一个简单直观的模型,并使其与不同的环境因素进行交互,进而了解这些环境因素是如何影响自行车租赁需求的。对用户来说,这会是一种很好的方式,因为这种方式可以让他们确认关于什么会让他们的用户租赁或不租赁自行车的直观判断。当然,在有些情况下,他们也会感到很惊讶(比如,冬天骑车的人居然会比夏天还多—本章中,我们将让你亲眼看到这个现象)。
这个Web应用背后的“大脑”是一个线性回归模型(linear regression model)。它能够发现历史数据集与模型输出结果之间的线性关系。利用这个原理,通过线性回归模型,我们就可推断随着时间推移和不同环境参数的变化,自行车租赁在需求上的变化。最终我们希望看到的是,这个模型能否帮助我们预测未来的自行车租赁需求。
记住一点,无论何时,只要你打算将一个Python模型扩展到Web应用,那么在添加任何构建Web应用程序所需的额外层之前,必须解决该模型中当前的所有问题和缺陷(bug)。在将任何应用或内容移至云端前,请先把所有简单问题解决掉!遵循这条建议,后续会减少你很多不必要的麻烦。

2.2 探索共享单车原始数据集

虽然是一种兴起不久的服务,共享单车已经受到极大欢迎。骑手使用一部移动手机,下载安装APP,就可在线注册登录,并查找定位到周边自行车的位置,然后在线租赁并把车骑走!这种服务模式创建了一个全新的生态系统,不需要人与人之间面对面的交互,就可以享受到自行车租赁服务。根据人工智能与决策支持实验室的Hadi Fanaee-T的说法(来自UCI机器学习数据集信息库讲义笔记):
“与公共汽车或地铁等其他交通服务相反,共享单车模式中,骑行时长、出发和到达位置在系统中都被明确记录。这一功能将自行车共享系统变成了虚拟传感器网络,利用这个网络,可以感知到一个城市的移动性。因此,通过监测这些数据,城市中发生的很多重要事件都有希望被监控到。”
下载的数据集包括两部分:hour.csv和day.csv,特征细节参见表2-1。


image.png
image.png

2.2.1 下载UCI机器学习库数据集

你可以使用Python命令行或者手工方式,从UCI的机器学习数据仓库中直接下载数据集。数据集下载地址为https://archive.ics.uci.edu/ml/datasets/bike+sharing+dataset 。下载到的数据中包含三个文件:

  • day.csv
  • hour.csv
  • Readme.txt

在自行车租赁数据中,日数据集day.csv有731行,小时数据集hour.csv有17 379个记录。

2.2.2 Jupyter Notebook配置使用

在本书中,每一章都对应着有一个Jupyter记事本文件。在开始使用本章的记事本文件前,我们首先来回顾一些基础知识。从GitHub上下载源代码文件后,打开终端窗口,然后进入chapter2目录。在这个目录中,你将看到两个文件和一个文件夹,如图2-2所示。

image.png

requirements_jupyter.txt文件中包含了运行本章Jupyter记事本文件所需的Python库。通过运行“pip3”命令,你可以快速安装依赖库文件(代码清单2-1)。

image.png

图2-2中,chapter2.ipynb文件就是第2章的Jupyter Notebook文件。Jupyter Notebook文件的打开方式很多,最流行的是使用“jupyter notebook”命令(代码清单2-2)。如果在打开过程中出现问题,请参考Jupyter的官方文档。

image.png

此命令将会打开一个浏览器窗口,浏览器中会显示默认路径下的全部文件列表,单击chapter2.ipynb文件超链接,如图2-3所示。

image.png

然后,浏览器将打开一个新的Tab窗口和相应的Notebook界面,其中包含了与本章内容相关的全部探索实验代码。本书全部代码都基于Python 3.x编写。如果你使用其他Python版本,则需要调整部分代码片段。一旦你在浏览器上打开了对应章节的Notebook,那么准备工作就一切就绪了。在打开的Notebook界面上,单击第一个代码框使其高亮,然后单击上方的运行按钮,如图2-4所示。如果在执行过程中出现错误,则在继续之前请先解决当前错误,因为Jupyter Notebook中每个代码片段的执行都要依赖之前的执行结果(错误可能与Python的版本兼容性有关,或者缺失了必须安装的依赖库文件)。

image.png

每个章节对应的Jupyter Notebook代码中都提供了使用Python命令行直接下载数据集的方式(如果存在防火墙问题,则需要手动下载)。

2.2.3 数据集探索

Python Pandas库中的head()函数提供了查看数据集中前面几行的功能,如代码清单2-3和图2-5所示。

image.png

执行上述命令后,将看到图2-5中所示的结果。

image.png

使用head()函数,我们会看到数据集中有日期格式、整数格式和浮点数格式的数据。另外,还能看到一些冗余的特征,如date(dteday)已经通过season、yr、mnth、hr等进行了分类。因此,数据集的dteday特征是可以抛弃不用的(虽然我们会暂时保留它以满足对数据集的探索需求)。其他一些特征似乎是多余的,如temp和atemp,这可能需要进一步检查核实。我们还删除了casual和registered特征,因为这些特征无法帮助我们从单个用户的角度来模拟需求,而这正是我们要实现的Web应用程序重点。根据季节、天气等因素预测用户注册数量,这可能会得到一个有趣的预测结果,但是这不符合我们当前的需求范围,因此我们将放弃这些特征。
本章中,我们仅保留真正需要的数据特征,这可以为我们后续分析消除数据混乱,并让我们的分析过程变得更清晰和易于理解,从而更好地实现我们数据科学和Web应用程序的目标。

image.png

Pandas Python库中的info()函数也是查看数据集所包含的数据类型、数量和空值的好方法(代码清单2-5)。

image.png

可以看到,使用info()函数,当前保存在内存中的所有数据都是浮点数或整数类型,并且它们都不是空值。如果我们碰巧有空值、日期数据类型或文本数据类型,则在继续建模之前,我们需要先解决这个问题。就目前而言,大多数模型都需要数值类型数据,就如我们这里所拥有的一样,到目前为止我们的数据准备一切还算顺利!

2.2.4 预测结果变量分析

接下来,我们将研究用以训练模型的结果变量cnt,即自行车租赁总数。Pandas库中的describe()函数是了解量化数据的另一种必备工具。此处,我们将它用于结果变量(也称为模型的标签),如代码清单2-6所示。

image.png

上述输出结果中,我们可以看到,cnt的值在最小值1和最大值977之间,也就是说在每一个有记录的小时内,自行车租赁数量最小是1辆,最多时是977辆,还可以看到,每小时平均自行车租赁数量是189.5辆。
另外,还可以确认我们所处理的是一个连续数值变量问题,因此,对于自行车租赁数量的训练和预测,线性回归(或者类似线性回归的模型)将是最佳选择。下面,我们将cnt数据绘制出来,以便更好地理解它,如代码清单2-7和图2-6所示。

image.png

绘制结果如图2-6所示。

image.png

2.2.5 量化特征与租赁统计

接下来,我们将创建所有浮点类型数据的散点图。我们将基于租赁计数来进行绘制,以显示与租赁计数相关的潜在关系,如图2-7和图2-8所示。

image.png

可以看到,被租用的自行车数量和温度之间存在某种程度的线性关系;天气越温暖,租来的自行车越多。还可以看到,temp和atemp这两个特征具有相似的分布,并且可能出现冗余甚至多重共线性。为了保持单一线性,我们将删除atemp特征(在图2-9和图2-10中,仅有temp,而没有atemp特征)。

image.png

可以看到,特征hum或者湿度散点图中,所有数据点几乎都密集在一个范围内,尽管边缘处也出现了稀疏点。windspeed特征散点图中,风速与自行车租赁数量之间确实显示了非线性关系,风速越大,自行车租赁数量越少!

2.2.6 分类特征研究

在我们已下载的数据集中,除了自行车租赁计数特征cnt外,其他的整数数据都具有分类特征。通过直方图查看时,分类数据会产生许多有趣的信息,如图2-11所示。

image.png

上图中可以看到,weathersit直方图表明好天气的时候,人们更喜欢租赁自行车;season直方图表明秋天是自行车租赁的高峰季节。
最后是特征hr或租赁时间直方图,清楚显示上班高峰时间和下午骑行时间段租赁自行车人数最多,而凌晨4点是最不受欢迎的骑行时间,如图2-12所示。

image.png

尽管我们可以通过图表绘制方式来了解到很多东西,但是仍然需要进行更彻底和系统的检测,才能做出哪些特征需要保留,哪些特征需要抛弃的决策。

2.3 数据建模准备工作

在大多数数据科学项目中,存在一个数据预处理阶段,在这个阶段会进行数据评估和清理,以实现数据的“模型就绪”。在清理阶段,我们已经放弃了一些无用的数据特征,同时也清除了可能存在的空值,我们也不用担心相关性或多重共线性,因为在最终模型中,我们仅会使用四个简单的特征。

2.3.1 回归建模

在统计分析中,回归模型试图预测变量之间的关系。它主要用于分析独立变量与依赖变量的关系,而拟合模型(fitted model)则可用于预测依赖变量的新变化。

2.3.2 简单线性回归

线性回归可能是最简单的建模算法。它试图解释一个因变量和一个或多个自变量之间的关系。基本的线性回归方程如图2-13所示。

image.png

在上述方程中,y是因变量,0是常数,1是回归系数,x是自变量。

2.3.3 简单线性回归模型

这里,从一个简单的多线性回归模型开始,我们输入所有变量并获得均方根误差(RMSE)。RMSE最终转化到与结果变量(也称为y轴标签)相同的单位来表示误差(即RMSE值与y值具有相同量纲),因此很容易看出模型在学习/预测自行车租赁方面表现如何,而误差是置信区间的一种表现形式。你所关注的是尽可能希望获得较低的RMSE值,因此我们的目标是不断调整数据和模型,直到RMSE值停止继续降低为止。本章中,我们所有建模工作都基于Python scitkit-learn /sklearn库。这是一个很了不起的Python库,几乎可以满足大多数Python用户的建模需求。
尽管我们只打算进行简单的线性回归,但我们还是用到了sklearn库中的三个函数:train_test_split函数从原始数据中创建两个随机数据集,并从结果中分离特征;linear_model函数运行我们的模型;mean_squared_error函数评估模型的学习效果(代码清单2-8)。

image.png
image.png

train_test_split()函数基于用户指定的种子参数将原始数据集拆分为两个随机数据集。无论何时,如果想要测试不同的方法,并希望确保始终使用相同的拆分以进行不同方法的公平比较,则设置random_state种子参数是个不错的选择。test_size参数设置测试拆分的大小,在这里我们将它设置为0.3或30%,因此最终结果将是数据集中70%的数据分配给了训练集,而剩余30%的数据分配给测试集(代码清单2-9)。

image.png

我们事先声明一个LinearRegression()模型,然后调用fit()函数,使用训练数据和训练标签来训练模型。执行上述代码后,模型model_lr即经过训练并准备好进行预测(代码清单2-10)。

image.png

最后,我们调用函数predict(),并将分配为测试集的剩余30%数据传入函数,然后将预测标签值输入函数mean_squared_error()求取均方根误差值。我们最终得到的RMSE值为143.08,同时我们将其作为后续预测的基准参考值。上述结果,也就是我们基于目前选择的数据特征和拆分种子值(我们在train_test_split函数上应用的种子,这可以确保我们每次都得到相同的数据分割)所得到的结果。理解这个RMSE值的一种方式,就是我们的模型预测的偏差是143辆自行车(因为它与我们的结果变量处于相同的量纲)。考虑到每小时自行车平均租赁数量大约是190辆,因此,与简单采用自行车租赁的全局平均值相比,我们的模型做得更好。但是,接下来我们将会看看能否再把这个均方根误差值改进一点。

2.4 特征工程试验

下面,我们将验证能否通过几种技术手段,获取更好的预测模型。技术方式主要包括多项式、非线性建模和利用时间序列。

2.4.1 多项式建模

将多项式变换应用于一系列值中,可以在线性回归期间实现更好的拟合。使用Python的sklearn库,可以轻松实现多项式变换(代码清单2-11)。

image.png

现在我们将所有特征转换为二维(模型是多元二次函数),并计算RMSE(使用二维特征重新训练后的模型,如代码清单2-12所示)。

image.png

然后,进行三维多项式回归模型预测,并计算RMSE(使用三维特征重新训练后的模型,如代码清单2-13所示)。

image.png

最后,进行四维多项式回归模型预测,并计算RMSE(使用四维特征重新训练后的模型,如代码清单2-14所示)。

image.png

由上可以看出,使用sklearn库的PolynomialFeatures()函数进行多项式回归建模是非常简单的。当特征变量转换为二维和三维时,RMSE值确实得到了优化,但是超过三维后,结果明显退化。

2.4.2 创建分类数据虚拟特征

在优化预测模型方面,另一个值得尝试的方法,就是虚拟化分类数据。这就意味着为每个特征类创建单独列。这里我们使用特征weathersit:它不是一个连续变量,相反,它属于任意类别。如果将这类特征传给预测模型,则模型会将其视为线性数值数据,而这种情况是不合理的,将mist & cloud加1并不等于snow。通过为特征weathersit创建4个新的特征列:clear、mist、snow和rain,同时给每一个新特征赋一个二进制值true/false,这样会得到更好的预测模型。
使用Pandas函数get_dummies()很容易就实现上述功能,这里我们将实现代码抽象到函数里面,以便后续Web应用更方便使用(代码清单2-15)。

image.png

上述代码会将每个类别分解为归属它自己的列。在下面的代码片段中,我们将对season、weekday和weathersit进行dummify操作(代码清单2-16)。

image.png
image.png

将函数应用于数据集后,每个天气类别现在都在一个单独的列中(忽略第一列,它是多余的,如果第一列不是weathershit_2、weathershit_3或weathershit_4,那么我们可以认为它是weathershit_1)。查看dummify操作后的天气字段(代码清单2-17)。

image.png

输出结果如图2-14所示。

image.png

那么,从分类数据中创建虚拟数据是否有助于预测模型呢(代码清单2-18)?

image.png

可以看到,这个方法对模型预测结果改进不是很明显,因此针对本章模型我们这里所做的尝试不是很合理。且不管它,我们继续往下尝试其他技术。

2.4.3 非线性模型试验

作为最后一个建模试验,我们将上一节中的虚拟数据放到sklearn库的“Gradient Boosting Regressor”(GBM)模型中运行。在sklearn包中从一个模型切换到另一个模型是非常简单的,我们只需要在内存中加载适当的模型并修改两行代码(代码清单2-19)。

image.png

很不可思议!这是目前为止值最小的RMSE,我们将模型的预测错误率降低到了一半。

2.4.4 使用时间序列复杂特征

这是本章最后一个特征工程试验了,我们的建模灵感源自微软的数据科学家。我们的原始数据是随着时间推移的自行车租赁总数,因此数据集是时序的。每当数据集随着时间推移记录事件时,你都可以将其看成是一个额外的特征。例如,比起发生在一年前的事件,发生在一小时前的事件很可能更重要。时间也可以捕捉趋势、改变需求和感知等。我们希望创建捕捉所有这些时间变化元素的特征!
对于每一行数据,我们将添加两项新特征:前一小时的自行车租赁总和,以及两小时前的自行车租赁总和。这样做的动机是,如果我们想要了解当前的自行车租赁情况,我们可以先看看一小时前发生的事情。如果一小时前的租赁情况良好,那么当前的租赁情况也会倾向于良好,这个时间元素可以被视为鼎盛或萧条时期、好天气或恶劣天气等的替身。
为了汇总每天和每小时的自行车租赁数,我们使用Pandas极其强大的groupby()函数。这里我们提取三个字段dteday、hr和cnt,并按日期和小时对cnt进行分组(代码清单2-20)。


image.png

输出结果如图2-15所示。

image.png

此函数按小时和日期来统计计数。接下来,我们创建两个新特征,一个向前移动一行,另外一个向前移动两行,从而为当前行提供过去一小时前和过去两小时前的自行车租赁总量。最后,我们使用Pandas的merge()函数将这两个特征添加到主数据帧中(代码清单2-21)。

image.png

将这些新数据拆分,并将其运行到用GBM回归模型(sklearn库中的GradientBoosting-Regressor)之后,我们利用测试数据集来计算均方根误差RMSE值(代码清单2-22)。

image.png
image.png

哇!是不是很吃惊,RMSE值为44.43,比之前的值还要好!

2.5 简约模型

很遗憾的是,最好的模型评估值并不意味着一切!本章中,我们需要使用一个简单的回归方程来进行预测。这不是通过复杂模型或过度特征数据设计可以轻松完成的事情,GBM并非线性模型,因此也没有给我们带来一个方便、轻量的回归方程。此外,我们之前创建的时移模型要求我们拥有前两个小时的预测总数,由于无法访问实时数据,因此这个模型并不适用于Web访问客户端。
如果你的目标是创建Web应用,那么有一点是你必须要记住的:如果最准确的预测来自极复杂的建模技术,那你的模型将很难转换到生产环境中。

2.5.1 简单模型中的回归系数提取

本节将介绍一种无须服务器端计算即可预测需求的简便方法。线性回归模型并非最强大的模型,它从来也没说过自己是最强大的。但是,线性回归模型确实可以将相当复杂的数据提炼成极其简单明了的线性表示。而正是这种简单的线性表现形式,为我们的应用提供了强大动力。
学习模型系数是回归建模最强大的地方,这在Web应用程序设计环境中更加强大,我们完全可以依赖模型系数和简单的回归方程来估计自行车的租赁需求。这就使得应用程序完全可以根据客户前端做出复杂的决策—轻量、快速并且有用!
为了得到一组回归系数和一个简单的回归方程,首先我们需要训练和测试回归模型。当我们对预测结果、使用的特征和预测的质量感到满意时,就可以提取此时的模型系数。另外,我们也会将模型输入特征转换为最基本和最具理解性的特征。下面是一个构建Web应用程序的经验教训:我们必须在最佳模型与生产现实之间取得平衡,如果你建立了一个了不起的模型,但没有人可以操作它,或者没法在生产中实时运行,那你的模型就是失败的。
这里我们将以Web应用程序设计为中心,尽量保持模型简单。因此,我们仅使用4个特征:season、hr,holiday和temp。这些特征是用户可以理解、易于获取,同时只使用4个特征,我们的模型会快很多。接下来,我们分别对这些特征进行建模,并检查对应模型的R-squared值。


2.5.2 R-Squared

R-squared是描述数据与拟合回归线之间有多近的统计量度。它也被称为确定系数,或多重回归中的多重确定系数。R-squared的定义是非常直截了当的,它是线性模型预期的响应变量变化的百分比(代码清单2-23)。

image.png

确定系数可以看成是个百分比。它可以让你了解到,有多少数据点落在回归方程形成的结果线上。系数越高,表明落在线内的点越多。如果系数为0.80,则表明80%的数据点落在回归线内。
我们最希望看到的,当然是R-squared接近1(或100%)且不是负数。使用sklearn库中的r2_score函数很简单就可计算出R-squared值(代码清单2-24)。

image.png

每个R-squared都是正数,我们看到hr和temp特征比season和holiday特征具有更多的变化。请记住,我们这里是分别针对每个特征计算的R-squared。另外,还有一个接下来的好步骤在这里没有提及,就是把这些特征合在一起来计算R-squared值(或者为了处理多个特征而调整R-squared值)。
如前所示,我们也将对season变量进行dummify操作。如果在包括dummified后的所有特征上重新运行R-squared循环计算,那么我们将得到以下R-squared值(代码清单2-25)。

image.png

可以看到,上述所有R-squared值都是正数,所以在最终的Web应用程序中,我们将使用上述特征集。

2.5.3 基于回归系数的新数据预测

前面一节中,我们已经计算出回归模型系数,现在我们可以使用回归方程来预测自行车租赁数量了。回归方程是一个源自我们回归模型的最佳线性拟合方程,该公式很常见,大多数数据统计类型的书中都会提及,如图2-16所示。

image.png

上述公式中,y是因变量,也可以理解为我们要预测的值,在本章中,就是自行车租赁数量。0是截距,1是线的斜率,x是自变量。对于多元线性回归情况,只需添加更多自变量即可。
需要特别指出的是,这个公式及其系数代表了应用到模型中的数据可能产生的最小误差。因此,当向模型中注入新数据时,我们不能立即真正意义上更改所有自变量。这点请务必记住,虽然我们在Web应用中将会允许用户进行各种环境设置以影响自行车租赁的数量,但是这些环境参数设置可能不会立即生效。另外,我们也提供了“reset”按钮来将所有变量重置为原始均值。
最终模型运行之后,我们需要提取截距和系数值。使用sklearn库的linear_model函数即可轻松实现:只需要调用intercept_和coef_参数即可获取这两个值(代码清单2-26和代码清单2-27)。


image.png

输出结果如图2-17所示。

image.png

然后,我们可以将这些常数分配给我们的Web应用程序,以便它可以反过来预测自行车租赁需求(代码清单2-28)。

image.png

为了构建回归方程,我们还需要获取历史均值。如果是归类值,那么我们选择最高均值并将其设置为1,而将其他值设置为0(就如我们对season和holiday所做的一样)(代码清单2-29)。

image.png

现在,我们已准备好了预测自行车租赁数量所需的一切。接下来,让我们一起来看看在上午9点时租赁数量是多少,而所有其他值保持在它们的平均值附近(代码清单2-30)。

image.png
image.png

可以看到,得到的预测结果是,在上午9点自行车租赁数量是171辆(你的结果可能略有不同)。在实际应用中,我们将会允许用户一次性更改多个特征值,但请记住,与原始方程值相差太多的更改可能会使模型质量下降。

2.6 共享单车租赁需求交互式Web应用设计

现在,我们来点有趣的内容,设计我们的Web应用程序。开始之前,我们需要始终牢记最终目标—我们希望与他人分享的内容以及其他人希望看到的内容是什么?
我们将设计一个交互式Web应用程序,允许用户自定义环境变量(时间、假日、温度和季节),并通过交互式获得自行车租赁数量的可视化反馈。
应用程序在视觉上要做到引人注目,以便吸引用户并让他们在使用时保持兴趣。这意味着需要将大量的精力投入消息、UI、视觉效果和交互式控件设计中,就如在收集数据和建模时一样。

2.6.1 代码可读性与扩展性摘要

在我的大多数Web应用程序中,我都会尝试将代码抽象为逻辑模块。一个模块用于处理用户数据收集过程,另一个模块是核心“大脑”部分,其主要负责建立回归方程,运行模型,并返回自行车租赁预测结果。在编译和调试Web应用程序时,将代码抽象到逻辑单元中将会极大地简化你的工作。这种方式允许你对每个模块进行单元测试,以确保所有模块都能正常工作,或者无所事事时,作为一个消遣过程。
在本章介绍的Web应用程序中,大多数“大脑”将直接驻留在客户端HTML主页面中。Flask是一个Web服务框架,主要用于检索、分析和回送自定义内容。在本章中,我们所需要做的只是用于预测自行车租赁需求的回归方程,因此在用户客户端和Web服务器之间来回切换的情况并不多(仅有本章是此类情况,在所有其他章节中,“大脑”部分都在Web服务器上实现,而不是客户端的网页上)。

2.6.2 构建本地Flask应用

在将代码搬到云端运行之前,先在本地试运行是很重要的。这将为你节省大量时间和免去很多头疼的事情。首先,在我们的本地机器上做简单的Flask练习。如果你从未在本地运行过Flask应用程序,则需要使用“pip3 install”或者OS和Python版本支持的其他工具来安装以下Python库:

  • Flask

一旦Flask安装完成后,打开文本编辑器,键入如下代码,并将文件保存为hello.py(代码清单2-31)。

image.png

在MAC/Windows上,打开一个终端/命令行(代码清单2-32和代码清单2-33)。

image.png

执行上述命令后,你应该看到图2-18中所示的内容。

image.png

然后将URL“http://127.0.0.1:5000/ ”(或者终端窗口中出现的其他URL)复制到浏览器中,你应该会看到Web应用程序已经启动。更多的示例和提示可以参见Flask官方网站,Flask官方快速入门指南请参考http://flask.pocoo.org/docs/0.12/quickstart
在上述过程中,发生了什么事情呢?如果你对Web服务框架不熟悉,可能会觉得这有点神秘和深奥(需要指出的是,Flask是最简单的Web框架之一),下面我们一步一步来分析整个过程。
在前面的“Hello world”示例中,所有过程都发生在“虚假”的Web服务器端(实际上就是你的本地计算机)。Web服务器端的工作,就是进行命令处理,并将可查看的HTML内容返回给发起请求的客户端网页。
首先,我们将Flask库加载到内存中(代码清单2-34)。



image.png

然后,我们实例化一个Flask会话(代码清单2-35)。

image.png

最后,我们创建一个函数来执行某些操作,并使用路由参数对其进行装饰,因此它知道从Web客户端处理哪些命令。这里,‘/’简单地表示根页面或者“index.html”根会话页面(代码清单2-36)。

image.png

很显然,实际应用中函数功能不可能这么简单。它很可能会调用数据库或者通过Representational State Transfer(REST)API调用来收集客户端信息并通过HTML模板将最终内容返回给客户端。整个过程中,允许创建自定义数据并实现智能化,然后将其封装起来并在复杂网页中呈现。不论是出于何种目的,这类网页看起来就像是纯手工制作的一样,而事实上它是由Flask动态创建的。在本书中,我们会一直使用Flask,如果你按照每章的讲解进行操作,那么你就能很好地掌握这个Web框架。
代码段的最后部分仅用于在本地执行模式(即从本机上运行)下运行Web服务器代码,这里,我们开启Debug模式(代码清单2-37)。

image.png

2.6.3 下载运行GitHub共享单车代码

如果还未下载代码文件,请到GitHub下载与本章相关的代码,并找到web-application文件夹,然后你在该文件夹中应该看到如代码清单2-38所示的内容。

image.png

下载并解压缩所有内容后,打开命令行窗口,进入web-application文件夹,并通过运行“pip install -r”命令安装所有必需的Python库(代码清单2-39)。

image.png

然后,运行你在“Hello World”实验中执行的命令(运行“python3 main.py”也可以执行此操作,如图2-19所示)。

image.png

执行后的结果应该如图2-20所示。

image.png

2.6.4 Web应用程序调试最佳实践

如果你没有看到图2-10中所示的屏幕截图,那么你的系统可能有问题,要么缺少文件或者某些库。在Web应用程序设计领域,故障调试也是很重要的部分。这里介绍两个简便方法来帮你解决问题。如果是Flask问题,并且浏览器的显示结果如图2-21所示,那么请执行以下步骤来排除故障。

image.png

要进行代码调式,请在main.py脚本中将Flask的debug标志设置为True(通常位于文件末尾)。这个方法仅适用于在本地执行的应用程序(代码清单2-40)。

image.png

如果问题与Flask相关,则调试器将会捕捉它并显示在浏览器中,浏览器中将会返回更多有用的信息,如图2-22所示。
无论debug标志如何设置,你都会在终端或命令行窗口中看到Flask错误信息,如图2-23所示。
修复所有Flask问题后,你可能还有些前端错误需要处理。大多数浏览器都会提供一些调试工具,图2-24显示了如何在Chrome中启动并运行JavaScript调试器的示例(你应该也可以在你使用的浏览器中找到相关功能)。


image.png

image.png

上述操作将在网页右侧打开一个漂亮的小调试中心,并会列出任何可能的错误或警告。检查下调式中心的信息总不会错,以免存在警告信息。如果使用的是其他浏览器和设备(如计算机、移动电话和平板电脑),那么测试Web应用程序的操作是类似的,如图2-25所示。

image.png

2.7 在微软Azure上运行Web应用程序

到目前为止,我们的模型已准备好可以到Azure上运行了。你需要一个Microsoft Azure账户,在撰写本书时,Microsoft提供了200美元的信用额度,在30天内可以使用所有服务,并在12个月内可以访问试用。更多信息请参考https://azure.microsoft.com/en-us/free/

2.7.1 使用Git托管项目代码

就本项目而言,你需要在本地计算机上安装Git(https://www.git-scm.com/downloads 上可以找到二进制安装文件)。如前所述,Git是一个源代码版本控制工具,源代码是个完全准备好的Git包,我们将会把这个源代码Git包推送至Microsoft Azure(参见Git官网介绍部分对于Git的简介)。
打开终端或命令行窗口,找到对应本章的web-application文件夹,初始化Git仓库(代码清单2-41)。

image.png

在使用Git的过程中,随时想到运行一下“git status”是个好习惯,这可以确保你正在跟踪的确实就是你想要的文件(代码清单2-42)。

image.png
image.png

将web-application文件夹中的所有文件添加到Git仓库中,然后再次运行“git status”检查是否正确(代码清单2-43)。

image.png

然后,做一次本地Git提交,并添加一个有意义的注释,以防止你将来想要重新访问以前的操作和文件(代码清单2-44)。

image.png

提交结果如图2-26所示。

image.png

有关将Git部署到Azure App Service的更多信息,请参考微软官方文档:https://docs.microsoft.com/en-us/azure/app-service/app-service-deploy-local-git

2.7.2 微软Azure命令行接口工具使用

本章中,我们将使用azure-cli工具来启动和运行程序,因为这是一种启动和控制Web实例的便捷方式(有关此部分设置的更多信息,请参阅官方文档https://docs.microsoft.com/en-us/cli/azure/get-started-with-azure-cli )。
对于Mac系统,进行如下设置:

image.png

对于其他操作系统,可以参考如下官方文档:https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
第1步:登录
安装azure-cli命令行工具后(如果本地命令行工具不方便使用,则直接使用Azure Cloud Shell即可),创建az会话。从azure-cli登录Azure。


image.png

按照提示操作,用浏览器打开给定的URL地址,然后输入对应的代码,如图2-27所示。

image.png

如果一切正常(即你有一个可用的Azure账户),上述操作会自动将azure-cli终端连接到云服务器。另外,一旦你获得授权,就可以关闭浏览器授权窗口了。记住,确保你在对应本章web-application文件夹下使用命令行工具。
第2步:为部署用户创建凭据
部署用户需要适当权限来使用FTP和本地Git服务。在这里,我们将用户名设置为“flaskuser11”,密码设置为“flask123”。用户设置只需执行一次,然后就可以重复使用同一个账户。如果使用同一个账户存在问题,只需创建一个不同的用户名即可(可以在用户名末尾添加一个数字,把我这里的用户名末尾数字递增一下就行)(代码清单2-46)。


image.png

在执行每个azure-cli的步骤时,将会收到确认设置的JSON消息。在“az webapp deployment”的案例中,JSON信息中大多数应该是空值并且没有错误信息。如果你看到有错误信息,那么你可能有权限问题需要解决(“conflict”表示用户名已经被占用了,请使用其他名称,“bad requests”表示密码太弱)。
第3步:创建资源组
资源组将是你操作云计算资源的逻辑空间。这里,建议输入离你所在地理位置最近的Region(Azure的Region信息请参考:https://azure.microsoft.com/en-us/regions/ )。在本章案例中,使用“West US”Region不会有多大影响,即使你在世界任何角落。但是在生产环境中,如果希望你的Web服务尽可能靠近你的用户以便实现更好的服务性能,那么使用“West US”Region可能会产生一定影响。


image.png

第4步:创建Azure应用服务计划
这里,我们将名称设置为“myAppServicePlan”,并选择一个空闲实例sku(代码清单2-48)。

image.png

第5步:创建Web APP
你的“webapp”名称必须是唯一的,并确保“resource-group”和“plan”名称与之前设置的相同。这里,我们将Web APP名称设置为“amunateguibike”。

image.png

查看支持的全部运行时列表(代码清单2-50)。

image.png

“az webapp create”的输出包含了后续步骤所需的重要信息。这里,主要查看包含“deploymentLocalGitUrl”的行,如图2-28所示。

image.png

为你的Azure项目实例提取本地Git URL配置(代码清单2-51和代码清单2-52)。

image.png

第6步:将Git代码推送至Azure
将我们之前保存的Git仓库地址URL添加到“add azure”命令后面(代码清单2-53)。

image.png

命令将会提示你输入密码,确保这里输入的密码是之前在“az webapp deployment user”步骤中设置的密码(这里是flask123)(代码清单2-54)。

image.png

一切就绪!现在你可以回到之前的浏览器页面单击刷新,或者打开一个新的页面并输入如下地址:http://amunateguibike. azurewebsites.net (或者在你的实际案例中,可以输入你自定义的地址:http://< WITH-YOUR-APP-NAME>>.azurewebsite.ne t,你应该会看到“Predict Bicycle Rental Demand”,如图2-29所示)。

image.png

另外,如果azure-cli命令行工具返回错误消息,则需要进行故障排查(请参阅“故障排查”部分)。当你更新了代码并想要重新部署时,再次执行push命令即可(代码清单2-55)。

image.png

你也可以直接在Azure的Web仪表板上管理应用程序。登录Azure并转到App Services菜单,如图2-30所示。

image.png

2.7.3 资源清理

这一步很关键。你不应该在云端上运行已经不需要的应用程序,因为这肯定会产生不必要的费用(如果是试用用户,那你可以用完你的免费信用额)。如果你不再需要Azure上的资源,请将其删除!使用命令行删除Azure资源(代码清单2-56)。

image.png

执行结果如图2-31所示。

image.png

另外,也可以在Azure Web仪表板上的“APP Services”菜单中进行删除操作。

2.7.4 故障排查

故障排查可以解决Web应用程序调试中发现的错误,你需要做的事情就是通过Azure的仪表板打开日志记录,如图2-32所示。

image.png

然后,打开日志流菜单,开始捕捉程序活动,如图2-33所示。

image.png

另外,你还可以使用Azure仪表板中内置的便捷控制台工具检查文件结构,如图2-34所示。

image.png

你甚至可以使用install命令检查requirements.txt文件是否有效,在Azure控制台中执行命令(代码清单2-57)。

image.png

2.7.5 步骤回顾

1)在终端或命令行窗口找到存放本章源代码的目录,并进入包含Web应用程序的文件夹目录中,如下:

image.png

2)向Git仓库提交所有文件。

image.png

3)登录Azure命令行窗口并授权会话。

image.png

4)准备Azure Web应用。

image.png

5)推送Web应用程序到Azure公有云上。

image.png

6)在浏览器中打开URL。

image.png

7)终止实例。

image.png

2.8 Web应用程序脚本及技术分析

下面简单介绍我们的Web应用程序代码。其中有两个重要的文件:main.py和index.html。main.py是Web服务控制脚本,index.html是模板文件,是Web应用程序所要呈现的内容。由于大多数处理都直接在index.html中发生,因此我们主要查看运行Web应用程序的HTML和JavaScript脚本。

2.8.1 main.py文件分析

通常情况下,main.py是指挥操作背后的“大脑”。它可以做任何独立Python脚本能做的事情,并且能够为Web生成网页内容。在本章中,除了将特征平均值和模型的截距及系数传递给模板外,这里确实没有太多要做的内容了。在本书中,我们将同时使用main.py和application.py两个文件。事实上,对于Python Web服务控制文件的命名并没有正确或错误之分,唯一需要注意的就是不能使用保留字。如果你确实想要自定义文件名称,则需要更新YAML文件和Web服务器网关接口(WSGI)配置文件。此外,一些云服务提供商使用不同的应用程序默认名称,例如谷歌云默认为“main”,Azure默认为“application”。但是,不管使用哪种公有云,名称都是可以自定义和更改的。
这里,main.py所做的一个有趣的事情,就是传递模型的截距、系数和特征平均值的初始值。
装饰器“@app.route”将路由任何调用根URL或带有index.html文件名的客户端请求。然后将所有默认值传递给index.html模板(代码清单2-58)。


image.png

Flask使用一种称为“Jinja2”的技术,将上述这些变量全部注入HTML模板表单中。如果查看return语句,你会发现它调用了Flask的render_template函数,并将预期的变量传递给index.html。
对HTML模板而言,要接收这些变量,所要做的事情就是在接收变量上使用“{{ }}”
符号。要查看模板脚本内容,请参阅Web应用程序中的index.htm脚本文件(代码清单2-59)。


image.png

2.8.2 /static/文件夹分析

顾名思义,静态文件夹包含了静态、不变的文件。这是Web应用程序存储图像、文件和其他共享数据的位置。

2.8.3 /templates/index.html文件及脚本分析

templates文件夹中包含了我们Web应用程序所需的所有模板。在随后的章节中,我们通常会看到两个html文件,一个是index.html,另一个是html响应文件。实际应用中,最好将这些文件分开,不要试图将所有内容全都塞入一个带有“if then”的复杂分支html文件中。
与本章相关的大多数Web应用程序操作都发生在index.html文件中,所以我们有必要仔细分析这个文件。在编辑器中,打开并查看完整的index.html文件。如前所述,本章的“大脑”不是Flask,而是index.html代表的前端页面以及位于页面末尾的JavaScript代码段。JavaScript为网页设计带来了很高的交互性。
对于本章中的Web应用程序,它会侦听按钮单击事件,并使用用户所选特征来运行回归方程,以重新计算自行车租赁需求。应用程序功能不局限于此,一旦它得到一个新的需求估计值,它将权衡需求值并决定显示哪个自行车图片—如果估计值比较小,则显示1辆自行车图片,如果值很大,则显示16辆自行车。
用户单击Web界面上的特征按钮,以使用该特定特征来重新计算自行车租赁需求值(代码清单2-60)。



image.png

image.png标签内的onclick()函数将season_spring的ID发送到主JavaScript函数calculateBikeDemand()。这意味着用户想要告诉函数,使用season特征变量集来重新计算回归方程(代码清单2-61)。

image.png
image.png

2.9 本章小结

本章介绍的自行车租赁需求预测Web应用是我们的第一个项目!虽然这个项目比较简单,在Web客户端和Web服务器之间几乎没有太多的内容交互,但是项目真正实现了一个Web应用程序的定义。本章中,我们介绍了使用Flask和Web控件,将独立脚本整合成交互式Web应用项目的概念。我们还看到,Python和Python库可以与Flask Web框架实现便捷通信,从而可以实现快速无缝的Web计算。
在本章的Web应用程序设计过程中,我们首先从规划Web应用程序应该是什么,以及观众对什么内容感兴趣开始。这个过程需要反复强调:如果没人感兴趣,那就没必要花心思去构建了。通常而言,我们总是从建模开始,然后尝试对其进行改造,以使其适用于Web应用程序。
然后,我们探索了Capital Bikeshare系统中的自行车共享数据集,尝试了不同的建模方法来预测环境因素影响下的租赁需求,并选择我们的Web应用程序最终使用的特征和模型系数。
我们在本地运行了基于Flask的Web应用程序,最后将其部署到Microsoft Azure公有云上。如果你按照本章介绍的顺序执行步骤,那你应该也不会碰到什么问题。始终要记住一点,首先设计Web应用程序,然后尽可能多地在本地编译和运行,最后再部署到云上。


2.10 附加资源

如果你想了解更多关于Flask的知识,去Google搜索它,你就会得到非常多的材料。更多信息请参考以下链接:


Leave a Reply

Your email address will not be published. Required fields are marked *