多臂土匪问题的 Thompson 抽样(上)
使用贝叶斯更新进行在线决策
“多臂土匪”可能是数据科学中最酷的术语,不包括对“裸欧式看涨期权”或“空头铁蝴蝶”的金融应用。它们也是最常见的实际应用。
这个术语有一个有益的激励故事:“一个武装匪徒”指的是吃角子老虎机——拉“手臂”,“匪徒”会拿走你的钱(大多数时候——有可能你会赢大钱)。“多臂土匪”是一个有许多手臂的吃角子老虎机,每个手臂都有不同的获胜概率和相关的支付。如果不知道这些概率/收益,你需要边走边学,拉动更容易成功的杠杆,并希望尽快收敛到最佳杠杆。
抛开赌博不谈,我们可以想象很多情况下的多臂土匪问题。一家医院对临床协议的潜在修改有多种想法,以改善结果——由于需要干预的临床事件数量有限,并且修改不当可能会产生意想不到的后果,我们如何评估所有修改,同时确保最好的修改能够迅速上升到最高层。或者一家营销公司想通过选择它已经开发的 3 个潜在活动之一来部署新的广告策略。
在本文中,我们将重点关注伯努利强盗问题——支出为 1 或 0 的问题(将问题的复杂性降低到确定支出的概率)。我们将应用两种方法:ε-贪婪和汤普森抽样。在以后的文章中,我们将讨论更复杂的多臂土匪问题(包括共轭先验对我们不友好的采样方法!)和其他解决这个问题的方法。
ε-贪婪
一个算法是“贪婪的”,因为它选择立即的回报而不考虑长期的结果。树算法通常是“贪婪的”——在每个节点上,它们选择熵最小化最多的分裂。该分裂可能不是在后续分裂中最小化熵最多的树。
贪婪算法在路径数量呈指数增长并且很快变得难以处理的情况下很常见(即计算强度达到不可能的程度)。例如,基于 10 个特征的树,甚至允许每个变量仅使用一次(这不是常见的约束),3.6M 个可能的配置(10!= 10 * 9 * 8…).加上第 11 个特征就成了 39.9M,等等。
使用贪婪算法可以合理地处理多臂土匪问题。在老虎机公式中,我们假设所有杠杆都有相同的成功概率,我们随机选择一个,然后根据成功/失败更新我们的成功假设。在每次连续的迭代中,我们选择成功概率最高的杠杆。
这种算法可能会为了眼前的利益而忽略新的信息。假设我们有 3 个概率为 0.04、0.07 和 0.1 的杠杆。我们可能会偶然发现低概率杠杆的早期成功,然后继续拉杠杆 20-50 次,直到它的概率低于其他杠杆。算法被“卡住”。
该算法的一个增强允许我们忽略成功概率并随机选择一个杠杆的小概率次数(ε)。这有助于收集新信息,允许非最优选择偶尔进入组合。
下面的代码为我们建立了算法。请注意,贝塔分布是伯努利分布的“共轭先验”。这是一个贝叶斯术语,表示贝塔分布是伯努利试验中包含的成功概率的后验预测分布。共轭先验使贝叶斯分析的数学变得简单——下面我们可以通过将 alpha 视为成功的计数,将 beta 视为失败的计数来更新 Beta。
import numpy as np
import pandas as pd
import random#Initialize Sampling
def initialize(arms):
prior = pd.DataFrame(np.ones((arms, 3)), columns = ['alpha', 'beta', 'theta'])
prior.loc[:, 'theta'] = prior.loc[:, 'alpha'] / (prior.loc[:, 'alpha'] + prior.loc[:, 'beta'])
post = pd.DataFrame(columns = ['i', 'arm', 'payout'])
return prior, post# e-Greedy
def e_greedy(eta, arm_prob, prior):
#Determine path-selection
if np.random.uniform(0, 1, 1) <= eta:
s = np.random.randint(0, len(arm_prob) -1, 1)[0]
else:
s = random.choice(np.where(prior['theta'] == np.amax(prior['theta']))[0])
#Determine success/failure
r = 1 if np.random.uniform(0, 1, 1) <= arm_prob[s] else 0
prior.loc[s, 'alpha'] += r
prior.loc[s, 'beta'] += 1- r
prior.loc[s, 'theta'] = prior.loc[s, 'alpha'] / (prior.loc[s, 'alpha'] + prior.loc[s, 'beta'])
return s, r, prior
我们将在该方法上使用几个词之后,用 Thompson sampling 直接测试这段代码。
汤普森采样
Thompson 采样采用不同于ε-greedy 的方法来选择下一个要拉的臂。ε-greedy 的一个缺点是,它可能会陷入局部最大值——通过统计机会表现良好的次标准分布。eta 参数被设计成通过迫使算法采取在当前看来是次优选择(即,不是当前最大预测概率)但实际上可能是给定足够信息的最优选择来抵消这一点。
为了做到这一点,Thompson 抽样使用一个随机的统一变量从每个选择的后验预测分布中提取数据。这允许以变化的频率对非最优分布进行采样——随着后验分布变得更加确定,做出选择的概率会动态降低,因此 Thompson 采样动态地平衡了对更多信息的需求和做出当前最优选择。例如,在下图中,我们假设有两种选择,蓝色代表 4 次成功,1 次失败,橙色代表预备队。虽然蓝色选择的平均值约为 0.7,橙色选择的平均值约为 0.3,但有相当大的概率(~4%)会选择橙色而不是蓝色。
我们的 Thompson 抽样实现看起来是这样的——注意,random.choice 选择现在使用的是以前的分布,而不是以前的均值。
# Thompson Sampling
def thompson(arm_prob, prior):
#Determine path-selection
s = [np.random.beta(prior.loc[n, 'alpha'], prior.loc[n, 'beta'], 1) for n in range(len(arm_prob))]
s = random.choice(np.where(s == np.amax(s))[0])
#Determine success/failure
r = 1 if np.random.uniform(0, 1, 1) <= arm_prob[s] else 0
prior.loc[s, 'alpha'] += r
prior.loc[s, 'beta'] += 1- r
prior.loc[s, 'theta'] = prior.loc[s, 'alpha'] / (prior.loc[s, 'alpha'] + prior.loc[s, 'beta'])
return s, r, prior
这场对决!
将这两种算法相互比较有助于阐明它们的优缺点。
在下面的例子中,我们模拟了概率来自β(1.5,1.1)的 5 个分支,即向较低概率移动,但仍保持足够的机会获得较高概率。
下图显示了这些方法特有的一次试运行。经过 250 次迭代后,两种方法都达到了最佳水平(水平 1),但是 TS 由于在分析的早期表现优异而领先。经过 50 次迭代后,两种方法实际上都以 30 胜打平,EG 略微领先。然而,在这一点之后,TS 领先。EG 已经开始拉杠杆 2(通过改变),并且看到了成功,并且继续拉,直到在前 15 次抽中它看到了 10 次成功(所有这些都是在 EG 假设其他未尝试的杠杆有 0.5 的概率的情况下)。EG 继续大力支持杠杆 2,直到第 175 次迭代,此时它尝试杠杆 1 并发现长期成功。相比之下,EG 早在第 20 次迭代时就发现了 Lever 0,并且早在第 40 次迭代时就几乎独占地拉动了它。
Bernoulli Multi-Armed Bandit with Lever Probabilities = 0.86, 0.44, 0.56, 0.34, and 0.05. EG using eta = 0.05.
这些算法都可以调整——我们可以调整两个模型中的先验,以及调整 eta,例如,在上面的示例中,我们使用了 5%的 eta。使用更激进的 eta 允许模型不容易出现局部最大值,同时还可能通过实施次优选择来减少长期收益。下图显示了使用 eta = 0.1 的 1000 次迭代。正如预测的那样,我们看到 EG 甚至能够在早期击败 TS(虽然 TS 希望“证明”Lever 0 是正确的选择,但 EG 愿意在没有太多证明的情况下做出这种假设),但是随着模拟进行到第 1000 次迭代,TS 略微领先,因为 EG 仍有 10%的时间从非最佳 Lever 中采样。对 EG 的改进可以使用 eta 的衰减因子。
Bernoulli Multi-Armed Bandit with Lever Probabilities = 0.86, 0.44, 0.56, 0.34, and 0.05. EG using eta = 0.1.
上面的模拟表明,这两种方法都是有效的——它们在很大程度上依赖于上下文。EG 将在做出次优选择伤害较小的情况下表现良好——如果第二或第三个选择的真实概率与第一个选择的真实概率足够接近,那么做出那些勉强低于标准的选择不会损失太多。此外,如果搜索空间广阔,TS 可能会陷入试图证明最佳选择的困境,而 EG 则愿意在没有太多“证明”的情况下快速取胜。
在下面的例子中,使用β(3.5,0.7)模拟了 50 条手臂,这有利于给予手臂更高的概率(参见下面的直方图)。正如预测的那样,EG 能够在 TS 通过迭代 1000 搜索广阔空间的同时取得快速胜利。
50 Arms with skewed-high probabilities. Histogram of success probabilities, and results from 1000 iterations (with zoom-in on 250).
如果我们翻转概率分布以支持低概率结果-Beta(0.7,3.5)-我们会看到每个算法都在努力寻找胜利。虽然最优选择在 67%的情况下获胜,但在 500 次迭代后,EG 赢了 229 次,TS 略少。然而,在 1000 次迭代之后,TS 领先,预计这一领先将保持,因为 EG 继续对无效空间进行采样。
50 Arms with skewed-low probabilities. Histogram of success probabilities, and results from 1000 iterations (with zoom-in on 250).
我们看到 TS 在下面的双峰分布中再次上升(Beta(2.5,1)和 Beta(2.5,10)的 25/75 混合)。
50 Arms with bi-modal probabilities. Histogram of success probabilities, and results from 1000 iterations (with zoom-in on 250).
结论
Thompson 采样是一种解决多臂强盗问题的贝叶斯方法,它动态地平衡了合并更多信息以产生每个杠杆的更确定的预测概率与最大化当前获胜的需要。ε-greedy 是一种更简单的方法,它更侧重于当前的成功案例,采用确定性方法来整合更多信息。这种方法的结果是,当前的成功和更多的信息有时是不平衡的(即,集中在局部最大值上),但是一个好处是,如果该方法在早期发生在全局最大值上,它更愿意信任它,并且可以产生早期成功。
在以后的文章中,我们将讨论使用更复杂的贝叶斯采样技术(在非共轭先验的情况下)扩展这个框架,以及 Thompson 采样在新问题中的应用:非二项式多臂 bandit、路径优化等等。
希望这个贝叶斯垃圾邮件过滤器不是太天真
我们都遇到过垃圾邮件,从 Gmail 收件箱中的可疑电子邮件,到社交媒体帖子上的重复评论。垃圾邮件是一种自 2000 年初以来一直困扰互联网的现象,并不是什么新鲜事。然而,我们现在有很好的方法使用机器学习来处理垃圾邮件。今天,我们将创建一个带有自然语言处理的垃圾邮件过滤器,它将根据我们是否确定该文本是垃圾邮件来返回布尔值 1 或 0。
处理自然语言
(笔记本
我们的数据最初有一些不重要的列,我很快就删除了。接下来,看看我们从数据帧中提取的两列,标签和文本,标签是包含“垃圾邮件”或“火腿”的分类特征,所以我认为垃圾邮件是垃圾邮件,火腿不是垃圾邮件。
接下来,为了让我们能够使用我们想要使用的任何模型,我将数据集中的所有标签映射到数字类型的布尔
dataset['numerical_label'] = dataset.label.map({'ham':0, 'spam':1})
这样一来,我使用了一个反矢量器来对我们的英语输入进行矢量化:
from sklearn.feature_extraction.text import CountVectorizer
vect = CountVectorizer(stop_words='english')
vect.fit(X_train)
对于我的模型,我选择利用贝叶斯多项式模型,
from sklearn.naive_bayes import MultinomialNB
model = MultinomialNB()
model.fit(X_train_df,y_train)
所以在适应、转换和改变之后,我得到了一个准确度分数:
并准备序列化我的模型,以便在 Flask 应用程序中使用。
from sklearn.externals import joblib
filename = 'alg.sav'
joblib.dump(model, filename)
烧瓶时间,宝贝
我决定使用 HTTP 参数,因为这将使默认版本对任何人来说都是最通用和最容易实现的。所以我们当然要从我们的 app.route()开始:
@app.route(‘/’)
并在其下方添加我们的函数。
def template():
try:
txt = request.args['text']
except KeyError as e:
return ('Some Values are missing')
Request.args 从 URL 中提取 HTTP 参数,用作 Flask 应用程序中的变量。HTTP 请求非常适合数据管道和深度嵌套的后端。接下来,我选择将读取的文本放入数据帧中进行预测。这一步不一定是至关重要的,但我已经将熊猫添加到 Flask 应用程序中,所以我决定使用它。
现在我们已经将数据放入 Flask,我们可以反序列化并加载我们的管道。
需要注意的一点是,为了使数组适合模型,您可能需要用 np.reshape()来改变数组的形状。
pipeline = load('alg.sav')
estimate=pipeline.predict(df['input'])
return(estimate)
没什么太复杂的!现在,我们可以通过任何字符串请求访问 Flask 应用程序,并通过数据管道尽可能多地重用这个精确的算法!
厉害!
Hopfield 网络毫无用处。以下是你应该学习它们的原因。
Photo by Ryan Schroeder on Unsplash
Hopfield 网络是由 J.J. Hopfield 于 1982 年发明的,到那时,许多不同的神经网络模型已经被放在一起,相比之下具有更好的性能和鲁棒性。据我所知,它们大多是在教科书中介绍和提到的,当接近玻尔兹曼机器和深度信念网络时,因为它们是建立在 Hopfield 的工作基础上的。
然而,与深度学习的当前状态相比,它们提供了如此不同和替代的学习系统视角,因此值得理解。
让我们来看看它们是如何工作的。
一个基本的 Hopfield 网络及其工作原理
Hopfield 网络的核心是一种模型,它可以在收到相同数据的损坏版本后重建数据。
我们可以把它描述为一个由节点——或单元,或神经元——通过链接连接起来的网络。每个单元在任一时刻都有两种状态,我们假设这些状态可以是+1 或-1。我们可以在向量 v 中列出每个单元在给定时间的状态。
链接代表单元之间的连接,它们是对称的。换句话说,无论您在图中向哪个方向看,node-i 和 node-j 之间的链接都是相同的。特别是,相同的是代表每个单元之间连接强度的数字。我们可以在矩阵中列出这些数字。
Fig.1 — A Hopfield Network
如果我们有一个如图 1 所示的网络,我们可以这样描述它:
A state vector and a weight matrix describe the graph at some point in time
我们如何构建 W ?我们首先注意到一个节点与其自身之间没有连接,因此我们通过将 waa、wbb、wcc、wdd 置零来明确这一点。我们也知道链接或者权重是对称的,所以例如 wab = wba 。所以 W 是零对角线对称的。其余元素是这样填充的: wab = Va Vb 。结果是矩阵 W 。对于连接权重来说,这是一个聪明的选择,因为它遵循了我们稍后将再次看到的赫比规则。
The weight matrix fully describes the network connections
现在我们已经建立了权重矩阵,我们需要定义一个规则来确定每个节点的状态。因为我们有二进制状态,我们可以使用这个定律:
State update rule
类似地,通过使用上面定义的权重矩阵,我们得到:
现在我们可以直观地了解 Hopfield 网络实际上是如何工作的。
假设我们有一个 V 的腐败版本,我们就叫它 V’ = [1 -1 -1 -1] ,这样最后一位就反过来了。我们可以用*V’*初始化网络状态。
Network initialized with corrupted states: d is now -1
我们可以继续用之前建立的规则更新网络状态。对于 Va,我们将有:
Va = f(wab Vb+WAC Vc+wad Vd)=+1
注意 wab Vb 和 wac Vc 对 x 都有正的贡献,尽管 Vb 和 Vc 为-1,推动 Va 为+1,而讹误位的贡献方向相反。换句话说,通过像以前一样构建 W ,我们迫使节点在它们应该具有相反符号时处于相反状态,而在它们应该相等时处于相同状态。相反的状态互相排斥,而相同的状态互相吸引。这是赫比规则的一种表述。
*赫布边法则:*一起放电的神经元,连接在一起
同理:
Vb = f(wba Va+WBC Vc+wbd Vd)=-1 Vc = f(WCA Va+WCB Vb+wcd Vd)=-1
现已损坏位:
Vd = f(wda Va+wdc Vc+wdb Vb)=+1
并且 Va = +1 通过让 wda Va > 0 吸引节点 d 为正,而 Vc=Vd= -1 通过给予整体正贡献 wdc Vc + wdb Vd > 0 排斥节点 d 。
原本相同的节点被驱使变得相同,原本符号相反的节点互相排斥变得相反。
原来的 V 神奇的恢复了!
但是* 为什么* 有用呢?
到目前为止,我们已经看到,一旦我们用状态向量 V 完全定义了网络——它的W——我们想要在损坏后恢复,我们可以通过更新网络状态来完成。
换句话说,在用*V’*初始化网络状态后,我们让网络按照我们之前定义的规律进化,它会收敛到我们最初想要的状态。不仅如此,无论我们持续更新多少次,它都会一直存在。
让我们找出为什么是。
我们可以定义一个依赖于图的状态和矩阵的函数。我们将把这个函数称为与网络状态相关的能量函数,并表示为:
Energy function in a Hopfield Network
结果#1
如果节点 Vi 将其状态从+1 更改为-1,反之亦然,我们将得到:
现在:
如果 Vi 从-1 变为+1,那么 dVi = +2
,这意味着 x 必须为正,
反过来,能量增量必须为负
这意味着如果按照我们的规则更新网络,能量函数会一直递减,它是单调递减,它会努力达到最低点。
结果#2
但是有没有一个最低点或者能量会一直减少到负无穷大?
换句话说,我们试图弄清楚能量增量是否可以为零。
为了让 dEi = 0 ,我们需要,例如,让 dVi = 0 ,这仅在 Vi(k-1)’ = Vi(k)’ 时成立,其中 Vi(k-1)’ 是更新前的节点状态,而 Vi(k)’ 是更新后的节点状态。
假设我们有 Vi(k-1)’ = +1 ,我们想要 Vi(k)’ = +1 ,或者类似的
xi(k) > 0 。
这是:
但是当 Vj(k-1)’ = Vj 那么 xi(k) 总是正的!
在一个镜头中,我们展示了当状态采用原始值(未损坏的值)时,能量函数将不再改变。换句话说, dVi = 0 并且节点不会更新到不同的值——配置被认为是稳定的。
因此我们证明了在网络达到节点状态的稳定配置之前,能量一直在减少。甚至,稳定配置是对应于恢复的状态向量的配置,即能量函数的局部最小值。
这里省略了许多实现细节,但是我在这里准备的这个 Jupyter 笔记本中有一个基本的工作 Hopfield 网络。
马蹄修道院
比 L1 和 L2 有更多的正规化选择。
Photo by Jeff Smith on Unsplash
正规化是一个迷人的话题,困扰了我很长时间。首先在机器学习课程中介绍,它总是提出一个问题,为什么它会工作。然后,我开始揭示正则化与潜在模型的统计属性之间的联系。
事实上,如果我们考虑线性回归模型,很容易表明,L2 正则化等价于将高斯噪声添加到输入中。事实上,如果我们考虑特征交互,后者是优选的(或者我们必须使用非平凡的 Tikhonov 矩阵,例如,与单位矩阵不成比例)。从贝叶斯的角度来看,L2 正则化相当于在我们的模型中使用高斯先验,正如这里的所解释的。从贝叶斯线性回归模型开始,通过取最大后验概率,你将得到 L2 正则化项。一般来说,正态先验使模型变得简单,因为正态分布是正态分布的一个共轭先验,这使得后验分布也是正态的!如果你选择一个不同的先验分布,那么你的后验分布会更复杂。
高斯先验和 L2 正则化的问题在于,它没有将参数设置为零,而只是减少了它们。当我们有许多功能时,这尤其成问题,大约 6 个月前我不得不处理这种情况,当时在所有功能处理之后,我有超过 100 万个功能。在这种情况下,我们想使用一种不同的正则化,L1 或套索正则化。它不是添加参数平方和的惩罚,而是基于参数绝对值的和进行惩罚。正则化模型将许多参数设置为零,实际上为我们完成了特征选择的任务。不幸的是,每个使用过它的人都可以证实,它大大降低了精度,因为它引入了偏差。
在贝叶斯术语中,L1 正则化等价于双指数先验:
在这里以及更远的地方,我会跟踪这个案例研究。理想的先验分布将把概率质量设置为零以减少方差,并具有厚尾以减少偏差。L1 和 L2 都没有通过测试,也就是说,双指数分布和正态分布都有细尾,他们把概率质量设为 0。请注意,为了使概率质量为 0,概率密度函数必须发散。
卡瓦略等人在此、在此以及其他一些论文提出了一个非常优雅的解决方案。该解决方案称为马蹄形先验,定义如下:
让我来帮你破译这个。如果你记得,L2 正则化相当于有一个正态先验,这是一个均值为 0 的正态分布,方差是一个超参数,我们必须调整。(方差是 L2 正则化常数 λ 的倒数)。本着真正的贝叶斯精神,我们希望定义一个关于 λ 的先验分布,然后将其边缘化。在这种方法中,建议使用半柯西分布作为先验分布。已经证明它把非零概率质量放在 0,也有一个胖尾。如果我们使用 s hrinkage weight k 的转换来重新参数化分布的形状,那么“马蹄”这个名称就来自于它:
在这种情况下,分布支持变为[0,1],并且当与其他先前的分布候选进行比较时:
我们可以看到马蹄先验满足我们的两个条件。
结论
在上述论文中,该方法在各种合成数据集上进行了测试,并且从那时起,它成为贝叶斯线性回归正则化方法的标准之一。从那时起,它已经过多次改进,并针对其他情况进行了调整。当然,由于免费的午餐定理,不可能排斥所有的超参数,所以很多讨论都致力于调整算法或克服它的一些缺点。
参考
卡瓦略,C. M .,波尔森,N. G .和斯科特,J. G. (2009 年)。通过马蹄处理稀疏性。《第 12 届人工智能和统计国际会议论文集》(D. van Dyk 和 M. Welling 编辑。).机器学习研究会议录 573–80。PMLR。
卡瓦略,C. M .,波尔森,N. G .和斯科特,J. G. (2010 年)。稀疏信号的马蹄形估计器。生物计量学 97 465–480。
使用 Node.js 和 Cloud Firestore DB 在 Google Firebase 上托管一个动态网站
Img Source: https://firebase.google.com/images/social.png
图沙尔·卡普尔😦https://www.tusharck.com/)
演示 Git 网址:https://github.com/tusharck/firebase-demo
为什么选择 Firebase 托管?
Firebase 是一个建立在谷歌基础设施上的综合应用程序平台,因此它提供了一个安全、快速、免费(付费选项也可用于额外资源)和简单的方式来在网络或移动应用程序上托管您的内容。
免费层的主要特性和优势:
- 它提供了免费的自定义域& SSL (SSL 为 https 连接提供了一个标准的安全层。
- Cloud Firestore:一个灵活且可扩展的数据库,用于跨客户端应用程序的实时数据同步。
- 其他功能:云功能,云消息(FCM),Crashlytics,动态链接,托管,ML 工具包,存储,性能监控,预测和测试实验室(这些产品的功能和资源可以通过购买付费计划来增加,但免费层服务非常好。要查看计划,请检查 Firebase 定价。
- 资源的自动缩放。
在 Firebase 上托管动态网站的步骤
要求
1。谷歌账户 如果你没有谷歌账户,你需要注册一个。你可以去 https://accounts.google.com/SignUp 看看。
2。Node.js 和 npm
- 你可以从 https://nodejs.org/en/download/下载安装程序。
- Linux
按照以下步骤安装 Node.js:
1。打开端子
2。运行以下命令:
sudo apt-get install curlcurl -sL https://deb.nodesource.com/**setup_13.x** | sudo bash -sudo apt install nodejs
注: 我用的是setup_13.x
因为当时的教程最新版本是 13 你可以去https://nodejs.org/en/查看最新版本。
要检查 Node.js 和 npm 是否已成功安装,请运行以下命令,这将输出已安装的版本:
node -v
npm -v
3。Firebase-CLI(命令行界面) 这些是管理、查看和部署 Firebase 项目的工具。
npm install -g firebase-tools
步骤 1:创建 Firebase 项目
- 从右上角进入https://firebase.google.com和签到。
- 点击右上角的进入控制台。
- 然后点击**创建项目,**如下所示。
4.下一件事是输入你的项目名称,并按下 continue。
5.按“继续”为您的 Firebase 项目启用 Google Analytics(如果您不想要它,请选中“禁用”)。
6.为 Google Analytics 选择最近的位置。
7.点击创建项目,等待加载。然后你会看到类似下面的**。**
步骤 2:初始化 Firebase 登录
- 打开命令行/终端,然后创建并转到一个新目录。
mkdir my-firebase-projectcd my-firebase-project
2.要在 firebase 上托管网站,请使用以下命令登录 Firebase。运行该命令后,会打开一个浏览器窗口,要求您使用 Google 凭据登录 firebase。在那里输入凭证,Firebase 将登录到您的系统。
firebase login
You will see something like this after successful login
步骤 3:将 Firebase 项目初始化到系统中
- 现在,我们必须将我们在 Firebase 控制台上创建的项目初始化到系统中。运行下面的命令。
firebase init
2.按下向下键,然后通过按下空格键选择两件东西**。**
- 功能
- 主办;主持
然后按回车继续。
3.然后选择**Use an existing project**
并按下回车键。
4.在**my-firebase-project**
或您使用的项目名称上按回车键。
5.选择 Javascript 并按回车键。
6.你可以说不你想用 ESLint 来捕捉可能的 bug 并加强风格吗?
7.键入 Yes 安装与 npm 的依赖关系。
8.这里我们必须完成两项任务:
- 你必须选择你的网站和资产所在的目录。默认情况下,它是public你可以按回车键继续,或者你可以改变到你想要的目录名。
- 为单应用程序页面键入 Yes ,这样您的动态 URL 就可以被重定向到正确的目的地。
9.通过运行以下命令,在本地系统上测试 firebase 应用程序。然后去http://localhost:5000看看你的基本网站运行情况。
firebase serve --only hosting,functions
打开http://localhost:5000网址后应该会看到下面这样的内容。
10.从终端关闭服务器。
步骤 4:为动态网站安装软件包和创建视图目录
- 在这里,我们将切换到函数目录中进行使用。
cd functions
2。Install Express 它是一个极简灵活的 Node.js web 应用框架。
npm i express --save
3。安装手柄 它是 Node.js 的模板引擎,用于网站的动态前端。
npm i handlebars --save
4。安装合并
npm i consolidate --save
5.在函数文件夹中创建一个名为视图的文件夹,我们将在其中存储所有的前端代码。
mkdir **views**
6.通过运行以下命令切换回主目录:
cd ..
步骤 5:设置 Firestore(云数据库)
数据库配置
- 去 https://console.firebase.google.com/。
- 选择您的项目。
- 从左侧窗格中选择数据库
4.点击创建数据库
5.选择在测试模式下启动,因为否则您将无法从您的系统访问数据库。我们将改变这个设置,一旦我们完成了网站的发展。
完成后,点击下一步。
6.选择 Firestore 数据库的位置。
注意: 设置好这个位置后,以后就不能更改了。
创建虚拟数据
- 点击开始收集。
2.输入收藏 ID ,即可进行采样。
3.输入样本数据。输入 sample_doc 作为文件 ID。在字段中输入标题**。我喜欢值里面的云**。然后点击保存。
步骤 6:建立网站的动态内容
我们将把这一部分分成两部分,在第一部分,我们将看到如何从 Firestore 获取数据并在网站中使用。在第二部分,我们将看到如何提交表单数据。
首先,我们将下载访问 Firestore 的凭据
2.点击左侧窗格中的设置,进入项目设置。
3.转到服务账户并点击生成新的私钥。
4.点击生成密钥,会弹出下载密钥的窗口。将密钥存储在您网站的功能文件夹中。
从 Firestore 获取
- 打开功能文件夹内的 index.js 。
2.我们需要定义一些我们希望在应用程序中使用的库。这些是我们之前安装的相同的库。
const functions = require('firebase-functions');const express = require('express');const engines = require('consolidate');var hbs = require('handlebars');const admin = require('firebase-admin');
3.我们在这里设置了一些东西:
- 使用 express 初始化应用程序。
- 我们将把引擎设置为把手。
- 然后,我们将告诉 express,我们的前端代码将放在 views 文件夹中。
const app = express();app.engine('hbs',engines.handlebars);app.set('views','./views');app.set('view engine','hbs');
4.授权您的应用程序访问您的 Firestore 数据库。
注:
1。更改您的 SDK 名称。json 与您下载的文件一起作为访问 Firestore 的凭证。
22。将数据库 URL 更改为您的数据库 URL。要查看网址你可以到设置>服务账号。
var serviceAccount = require("./**YOUR_SDK_NAME**.json");admin.initializeApp({credential: admin.credential.cert(serviceAccount),**databaseURL: "https://myfirebaseproject-bx54dasx3.firebaseio.com"**});
5.从 Firestore 获取数据的函数。
- 收集 ID 为的样本。
- 文档 ID 为 sample_doc。
我们在输入样本数据时定义了上述内容。
async function getFirestore(){const firestore_con = await admin.firestore();const writeResult = firestore_con.collection('**sample**').doc('**sample_doc**').get().then(doc => {
if (!doc.exists) { console.log('No such document!'); }
else {return doc.data();}})
.catch(err => { console.log('Error getting document', err);});return writeResult
}
注: 我们使用 async 是因为我们要等待数据库和我们网站之间的承诺操作完成。
6.创建路由并将结果发送到前端。
app.get('/',async (request,response) =>{var db_result = await getFirestore();response.render('index',{db_result});});exports.app = functions.https.onRequest(app);
7.在视图文件夹中创建 index.hbs 。
注: 。hbs 是一个 handelbars 文件
8.在 index.hbs 中编写这个基本的 HTML 代码,以查看获取的结果。
<html>
<body> <h1>{{db_result.Heading}}</h1> </body>
</html>
注: {{db_result。Heading}},db_result 是从后端传递过来的变量。。标题是我们在 Firestore 数据库中输入数据时定义的文档内的字段。
9.打开 firebase.json ,将**" destination “:”/index . html "**改为"function":"app".
10.删除公共文件夹内的index.html,删除这个很重要。如果你不删除它,它将总是选择这个文件,我们的后端代码将是无用的。
11.通过运行以下命令,在本地系统上测试 firebase 应用程序。然后去http://localhost:5000看看你的基本网站运行情况。
firebase serve --only hosting,functions
12.从终端关闭服务器。
插入 Firestore
在这里,我们将从我们的网站插入数据到 Firestore。
1.创建另一个名为 form_data 的集合,我们将在其中插入表单数据。
注意: 它将要求您输入一个文档,并创建集合以输入任何样本值。
2.运行下面的命令在本地服务器上进行测试后,转到http://localhost:5000。
firebase serve --only hosting,functions
3.添加 HTML 代码,在 index.hbs 中创建一个示例表单。
<html>
<body><h1>{{db_result.Heading}}</h1>**<form action="/insert_data"** **method="post" >
<fieldset>
<legend>Sample Form</legend>** **First name:<br>** **<input type="text" name="firstname" >** **<br>** **Last name:<br>** **<input type="text" name="lastname">** **<br><br>** **<input type="submit" value="Submit">** **</fieldset>
</form>** </body>
</html>
- action=“/insert_data” 是我们将在函数内部定义的路径。
- 刷新页面后,它应该像下图所示。
3.在 index.js 中添加将数据插入 Firestore 的代码。
async function insertFormData(request){const writeResult = await admin.firestore().collection('**form_data**').add({
firstname: request.body.firstname,
lastname: request.body.lastname
})
.then(function() {console.log("Document successfully written!");})
.catch(function(error) {console.error("Error writing document: ", error);});
}
4.在 index.js 内部定义 HTML 表单发送 post 请求的路径。
app.post('**/insert_data**',async (request,response) =>{var insert = await insertFormData(request);response.sendStatus(200);});
6.在表单中插入一些示例数据来测试它。
7.点击提交后,你应该会看到网页上显示的回复**OK**
。
8.转到https://console.firebase.google.com/然后转到数据库部分。您应该会看到插入的表单数据。
步骤 7:在线部署(最后一步)
- 我们需要更改一些用于身份验证的代码,因为当您在线部署它时,Firebase 会负责身份验证。
- 在 index.js 内部删除以下代码:
var serviceAccount = require("./**YOUR_SDK_NAME**.json");admin.initializeApp({credential: admin.credential.cert(serviceAccount),**databaseURL: "https://myfirebaseproject-bx54dasx3.firebaseio.com"**});
- 取而代之的是在 index.js 中,将它插入到您的代码中:
admin.initializeApp(functions.config().firebase);
这里我们告诉 Firebase 从部署时存在的配置中获取认证信息。
2.在网站目录内的终端上,运行以下命令:
firebase deploy
这需要几分钟的时间,但之后,您应该会看到类似这样的内容:
3.如上图所示,转到 firebase 提供的托管 URL 。
恭喜你已经在 Firebase 上托管了一个动态网站。
步骤 8:(可选)使用自定义 URL
- 从任何一个提供商那里购买域名,比如 GoDaddy 或其他你喜欢的提供商。
- 从左侧窗格转到主机。
- 点击连接域。
4.在此输入域:
5.遵循验证说明。
声明:
- 【Firebase 服务的服务条款
- Firebase 和它的服务是谷歌的产品,这篇文章中没有任何地方暗示不是这样。
- 这篇文章是为了教育目的。
在 AWS SageMaker 中托管 Scikit-Learn 最近邻模型
除了一系列内置算法,AWS SageMaker 还提供了训练和托管 Scikit-Learn 模型的能力。在本帖中,我们将展示如何在 SageMaker 中训练和托管 Scikit-Learn 最近邻模型。
正如官方文件所述,我们将在整个过程中处理两个主要步骤:
- 准备一个 Scikit-Learn 脚本在 SageMaker 上运行
- 通过 Scikit-Learn 估算器在 SageMaker 上运行这个脚本
准备在 SageMaker 上运行的 Scikit-Learn 脚本
在我们的 Scikit-Learn 脚本中,我们将从我们的输入通道(S3 桶和我们完全准备好的葡萄酒嵌入集)加载数据,并配置我们将如何训练我们的模型。模型的输出位置也在这里指定。这一功能已被置于主要保护之下。
我们还将安装 s3fs,这是一个允许我们与 S3 交互的包。这个包使我们能够识别特定的 S3 目录(输入数据、输出数据、模型),以便脚本与之交互。另一种方法是使用特定于 SageMaker 的环境变量,这些变量指定要与之交互的标准 S3 目录。为了说明这两个选项,我们将使用环境变量 SM_MODEL_DIR 来存储模型,以及输入和输出数据的特定目录地址。
到目前为止一切顺利!通常,我们可以在 SageMaker 上运行这个脚本,首先训练模型,然后通过调用“predict”方法返回预测。然而,我们的 Scikit-Learn 最近邻模型没有“预测”方法。实际上,我们的模型正在计算各种葡萄酒嵌入之间的余弦距离。对于任何给定的输入向量,它将返回最接近该点的葡萄酒嵌入。这与其说是一种预测,不如说是一种计算哪些点彼此距离最近的方法。
幸运的是,“模型服务”功能允许我们配置 Scikit-Learn 脚本来实现这种类型的定制。模型服务由三个功能组成:
i) input_fn :这个函数将输入数据反序列化为一个对象,该对象被传递给 prediction_fn 函数
ii) predict_fn :该函数获取 input_fn 函数的输出,并将其传递给加载的模型
iii) output_fn :该函数获取 predict_fn 的结果并将其序列化
这些函数中的每一个都有一个运行的默认实现,除非在 Scikit-Learn 脚本中另有说明。在我们的例子中,我们可以依赖 input_fn 的默认实现。我们传递到最近邻模型中进行预测的 wine 嵌入是一个 Numpy 数组,这是默认 input_fn 可接受的内容类型之一。
对于 predict_fn,我们会做一些定制。我们不是在模型对象上运行“预测”方法,而是返回前 10 个最近邻居的索引列表,以及输入数据和每个相应建议之间的余弦距离。我们将让函数返回一个 Numpy 数组,该数组由一个包含这些信息的列表组成。
函数 output_fn 也需要一些小的定制。我们希望这个函数返回一个序列化的 Numpy 数组。
Scikit-Learn 脚本还有一个组件:加载模型的函数。必须指定函数 model_fn,因为这里没有提供默认值。该函数从保存模型的目录中加载模型,以便 predict_fn 可以访问它。
具有上述所有功能的脚本应该保存在一个源文件中,该文件独立于您用来向 SageMaker 提交脚本的笔记本。在我们的例子中,我们将这个脚本保存为 sklearn_nearest_neighbors.py。
通过 Scikit-Learn 评估器在 SageMaker 上运行这个脚本
从这里开始就一帆风顺了:我们需要做的就是运行 Scikit-Learn 脚本来适应我们的模型,将它部署到一个端点,然后我们就可以开始使用它来返回最近邻葡萄酒嵌入。
在 SageMaker 笔记本中,我们运行以下代码:
现在,我们的近邻模型已经准备好行动了!现在,我们可以使用。predict 方法,返回样本输入向量的葡萄酒推荐列表。正如预期的那样,这将返回一个嵌套的 Numpy 数组,该数组由输入向量与其最近邻居之间的余弦距离以及这些最近邻居的索引组成。
[[1.37459606e-01 1.42040288e-01 1.46988100e-01 1.54312524e-01
1.56549391e-01 1.62581288e-01 1.62581288e-01 1.62931791e-01
1.63314825e-01 1.65550581e-01]
[91913 24923 74096 26492 77196 96871 113695 874654
100823 14478]]
我们走吧!我们在 AWS SageMaker 中培训并主持了一个 Scikit-Learn 最近邻模型。
托管您的交互式可视化
Bokeh & Heroku
请点击下面的链接亲自查看!
[## 全球选民投票率
voterturnoutmap.herokuapp.com](https://voterturnoutmap.herokuapp.com/MapScript)
https://voterturnoutmap.herokuapp.com/MapScript
可视化使每个人都可以访问数据。他们把令人麻木的 excel 电子表格变成有趣且易于理解的图表。他们总结,突出,良好的可视化也允许用户自己探索数据。用户可以探索数据来回答他们自己的问题并做出他们自己的发现。
“Look at this graph.”
作为 Python 程序员,许多人都熟悉 Matplotlib 和 Plotly,但最近我一直在尝试使用散景。有一些关于使用散景的很棒的教程,我鼓励你去看看—
[## 使用 Python 中的散景进行数据可视化,第一部分:入门
提升你的视觉游戏
towardsdatascience.com](/data-visualization-with-bokeh-in-python-part-one-getting-started-a11655a467d4) [## 使用 Python 制作交互式地理地图的完整指南
想知道这些美丽的地理地图是如何创建的吗?我们的数据世界收集了大量的…
towardsdatascience.com](/a-complete-guide-to-an-interactive-geographical-map-using-python-f4c5197e23e0)
这些教程演示了如何创建交互式可视化,但交互仅通过 gif 或视频显示。您可以在自己的服务器上本地托管您创建的可视化效果,但是有没有办法托管交互式可视化效果,以便读者可以进行交互?是的,答案是 Heroku!
Heroku 有很棒的文档和一步一步的指导来迎合不同的编程语言。我将引导你通过我所做的把我的视觉化变成一个应用。如果你想让你的作品容易被分享,这是非常有用的。
设置
首先,你必须用 Heroku 创建一个帐户,因为我们是数据科学家,所以我假设你正在使用 Python。确保你的 Python 是最新的!你还需要一个 GitHub 账户。动手吧!最后,您需要安装 Heroku。这很容易,尤其是如果你已经有了自制软件。
Command to install Heroku via Homebrew
现在,您应该已经拥有了开始工作所需的一切。用这个简单的命令从您的终端登录 Heroku:
You must login!
根据 heroku 文档,“Heroku 和 git 命令都需要这种身份验证才能正常工作。”
现在真正的工作开始了。我假设你已经有了一个伟大的想象,你需要与世界分享。如果没有,遵循我上面链接的教程之一。我使用的数据来自 IDEA 的选民投票率数据库。
您需要将可视化转换为. py 文件。您需要整理您的数据帧并创建一些必要的文件:
- procfile——用 py 文件初始化应用程序的简单代码行
I broke this up into two lines so you could see the whole thing.
- requirements.txt
Find your versions with the terminal command, “pip freeze”
- runtime.txt
Your version of Python!
一旦存储库中有了所有这些文件,就可以部署应用程序了。在 github repo 中,发出命令:
Create the app
您可以在创建后指定应用程序的名称。留空,Heroku 会为你命名。最后,把你的工作推给 Heroku,它会把你的代码作为自己的应用程序托管。
Look for the url printed at the bottom.
发布的 url 会将您带到您的活动应用程序。你也可以按照下面的命令,它会做同样的事情。
Simple.
既然你的项目已经有了自己的网站,那就和全世界分享吧!
本教程将让你在几分钟内部署一个 Python 应用程序(一个简单的 Django 应用程序)。再坚持几分钟…
devcenter.heroku.com](https://devcenter.heroku.com/articles/getting-started-with-python)
在 AWS Lambdas + API Gateway 上托管您的 ML 模型第 1 部分
我关于模型部署的其他帖子
所以你辛苦了几个星期/几个月,你漂亮的机器学习模型完成了。感谢上帝。
但是现在有人想用你的模式。这是个好消息,不是吗?
确实是,但是一个全新的问题出现了。你必须把它放在某个地方,让其他人也能使用它。
我将向您展示如何在 AWS 上轻松、廉价地完成这项工作。
关于我的其他关于模型部署的文章,请查看标题上方的链接。
You don’t need to be Gordon Freeman to handle these lambdas
本系列教程将向您展示如何使用 AWS 来做到这一点。具体来说,我们将使用以下组件:
- S3 水桶
- λ函数
- λ层
- API 网关(来自第 2 部分)
为什么不用 Sagemaker?
我没有使用 Sagemaker,因为它可能非常昂贵,每个模型 100 p/m,而 lambda 函数只需为每个请求付费。
这可能导致 lambdas 的成本是在 Sagemaker 上运行相同模型的 1/5 到 1/10,特别是如果您有几个模型要在低流量下部署。
你需要什么
你需要一些东西来完成这个。你需要安装 Python (scikit learn,pandas 等)和 Docker。如果你在 Windows 机器上,Docker 将需要构建正确的 lambda 层文件(我是通过艰难的方式学到这一点的)。
所以你首先需要一个模型。我将根据 Kaggle 上的英国房价数据集建立我的模型(数据可以在这里找到,我的代码在这里)。
λ—层/功能
Lambdas 是执行特定任务的小型运行时。它们可能需要一段时间来配置,但它们完全由 AWS 管理,所以您需要配置的只是 lambda 的内存。
他们可以在上面安装 Python,但不会安装开箱即用的模型运行包。为了实现这一功能,我们需要使用λ层。
如果你熟悉的话,Lambda 层与 Docker 图像非常相似。每一层都继承了上一层的功能。但是你需要知道一些关于他们的事情。
- Lambda 图层必须是 zip 文件
- 对于给定的 lambda 函数,只能有五个 lambda 层
- lambda 层不能大于 250MB(总计,解压缩后)
对于这个例子,我们需要使用 4 个 lambda 层
- Numpy / scipy 层,因为后面的层需要这些包(由 AWS 预构建)
- Pandas 层,以允许我们使用模型中使用的 ColumnTransformer
- Sklearn 层支持使用 scikit-learn 库
- 我们的模型将驻留的模型层
所以接下来我将向您展示如何构建您自己的 AWS lambda 层,以允许您的 lambda 函数使用 Pandas
第 2 层—熊猫
这些步骤在 Pandas/scikit-learn 层之间是相同的,因此我们将在这里重点关注它们,您可以对 scikit-learn 层重复这些步骤。
接下来,您需要打开 bash/Powershell 并将目录更改为 layers/Pandas 目录。
然后运行以下命令:
docker run --rm -it -v ${PWD}:/var/task lambci/lambda:build-python3.6 bash
注意:${PWD}在 Powershell 中将当前工作目录挂载到 Windows 环境中的映像。如果你用的是 mac/linux $pwd 应该没问题。
这个简单的命令做了几件事:
- 拉下兰姆西的 docker 图像。这个 docker 映像模拟了 Python 3.6 lambda 环境的运行时,并允许我们在运行文件的相同环境中构建文件(这对 Windows 用户非常重要)
- 将当前目录挂载为一个卷,以便 docker 映像可以输出 lambda 构建文件
- 允许我们直接攻击 docker 图像
现在运行以下脚本,在 docker image bash 中为 lambda 层构建 python 文件(对于 sklearn 层也完全相同):
pip install -r requirements.txt --no-deps -t python/lib/python3.6/site-packages/
这将构建在 lambda 函数中使用 Pandas 所需的 python 文件。主要区别是 pycache 中的所有文件扩展名都应该有。pyc 文件类型。在 lambda linux 环境下运行代码需要这些文件。
接下来,我们需要为要使用的代码创建一个 zip 文件。
Lambda 层需要在 zip 文件中有一个特定的文件夹结构:
- python
- lib
- python3.6
- site-packages
- all your python package files will be included in here
所以仔细检查并运行 zip_layers.py 中的代码,它将创建作为 lambda 层工作的 zip 文件。
在 AWS 上上传和测试
接下来的步骤是:
- 将您的 pandas_layer.zip 上传到 S3
- 将 pandas_lambda.zip 文件的路径复制到 S3 存储桶中
- 选择 AWS 服务上的 Lambda
- 单击左侧面板并选择图层
- 创建一个新层
Viola,你已经创建了你的第一个 lambda 层!但是现在我们需要测试它是否有效。为此,你需要用 Python 3.6 运行时创建一个 lambda 函数。
要添加 lambda 图层,您需要单击 lambda 函数模板中的图层图标,然后单击添加图层按钮。
然后,您将从运行时兼容层列表中选择 numpy/scipy,然后选择您刚刚构建的 Pandas 层。
**但是一定要先放 numpy/scipy 层。**否则熊猫层将不能使用它上面的层的 numpy 依赖。
别忘了点击保存!!
接下来你需要刷新你的页面,浏览并编辑 lambda 函数。使用下面的代码来测试 Pandas 功能是否如您所期望的那样工作:
import json
import pandas as pddef lambda_handler(event, context):
print((pd.DataFrame(data={'col1': [1, 2, 3, 4]})))
保存您的功能并单击测试。您将需要创建一个测试用例,但只需给示例一个名称,因为我们在构建模型层之前不需要它。
如果你看到了下面的输出,那么这是一个巨大的成功,你已经让熊猫在你的 lambda 函数上工作了。哇哦。
第三层— sklearn
现在,我们需要重复上述步骤,使用 docker 来构建文件,以便使用 sklearn。重复以上步骤(使用 my github 中的 sklearn resources.txt)创建新的 sklearn_lambda.zip 并上传到 S3。
现在我们需要测试 sklearn 安装是否在 lambda 函数上工作。要使用它,我们只需更新上面的 lambda 函数代码,以包含对 sklearn 的引用。
import json
import pandas as pd
import sklearndef lambda_handler(event, context):
print((pd.DataFrame(data={'col1': [1, 2, 3, 4]})))
print(sklearn.__version__)
如果你看到 0.20.3 被添加到 lambda 的输出中,那么你做得非常好,几乎所有的困难工作都完成了!!接下来,我们将模型添加到 lambda 函数中,这样我们就可以开始返回模型预测。
第 4 层—您的 ML 模型
通过运行 build_model.py,您将获得一个完美格式化的 zip 文件,以便在您的 lambdas 上使用该模型。
要将它添加到你的函数中,你需要重复上面的步骤,并使用那个压缩文件(上传压缩文件到 S3,创建一个新的模型层,然后将这个层添加到你的 lambda 函数中)。
为了测试这一点,我们需要做更多的工作。首先,我们需要配置 JSON,以允许输入模型所需的字段。
如果您没有更改 lambdas 中的基本测试用例,您的测试 JSON 将如下所示:
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
现在,您需要更新它,以便与我们需要的模型输入一起工作。
{
"Property Type": "S",
"Old/New": "Y",
"Duration": "L"
}
一旦你把你的模型 lambda 层链接到你的 lambda 函数,你就可以开始了。
将您的 lambda 函数更新为这段代码,您将能够从您的模型返回预测。lambda 函数将获取您的模型层并保存您的。pkl 文件到/opt/model_name.pkl。
import json
import pandas as pd
from sklearn.externals import joblibmodel = joblib.load('/opt/house_price_model.pkl')def lambda_handler(event, context):
df = pd.DataFrame([event])
# Returning the value from the list
result = model.predict(df)[0]
return result
仅此而已。如果所有的步骤都成功了,那么你应该会得到一个结果。我得到 1644.62626412,但这将取决于你的随机种子,功能的数量等。
但仅此而已。这不是太可怕,但如果你不知道来龙去脉,可能需要一段时间。
接下来,我将把这个 lambda 挂接到 API 网关,这样我们就可以使用 Python 中的请求包来调用我们的模型 API。
在 AWS Lambdas + API Gateway 上托管您的 ML 模型第 2 部分
我关于模型部署的其他帖子
这是我在 AWS 系列上托管您的 ML 模型的第 2 部分,我将通过 API 网关安全地公开您的 ML 模型 lambda。
我有一些好消息,虽然,这是容易的一点。
你已经完成了设置 lambda 函数和所有层的所有艰苦工作,所以这应该是小菜一碟。
关于我的其他关于模型部署的文章,请查看标题上方的链接。
将 lambda 链接到 API
因此,首先您需要通过 API 网关创建一个新的 API。
对于您的新 API,选择 resources 并单击 actions。您需要添加一个资源,然后添加一个 POST 方法。让我们称资源为 predict,因为这就是它的作用(有趣的想法),并将 POST 方法分配给 predict 资源。
接下来,您将需要将 API 与 lambda 函数集成在一起。输入您的 lambda 函数名,并允许 API 访问该资源。它应该是这样的:
APIs are easy AF
接下来我们只需要部署 API。重新单击 Actions 按钮,选择 Deploy API,并为您的新部署命名和描述(如果您愿意的话)。
我通常把我所有的 API 都称为测试,这样至少人们不会太惊讶,然后他们会爆炸。
测试您是否可以访问您的 API
你已经在 AWS 端完成了所有的配置,现在让我们看看是否能从你的 API 中得到一些预测。
其实超级简单。
import requests
url = "https:your_url.amazonaws.com/test/predict"input_data = {
"Property Type": "S",
"Old/New": "Y",
"Duration": "L"
}
r = requests.post(url, json=input_data)
print(r.json())164735.29632791804
维奥拉。您实际上已经部署了您的模型,现在可以从任何可以在您的 API 上创建 POST 请求的地方访问它。
这很酷。
为您的 API 增加一些安全性
但是很明显,我们不希望任何获得 URL 的人能够从 API 访问结果。
也许它是商业 IP 或其他东西,但无论哪种方式,你都必须为通过它的所有请求付费(不要担心它真的很便宜),所以让我们为 API 添加一个 API 密钥,这样它就变得很好很安全。
你需要做的第一件事是创建一个使用计划。您可以通过 API 设置节流/突发流量。
Just click the toolbar on the left hand side and select usage plan. Please set your quota to more than 5 per month though. Testing will be hell otherwise
一旦您创建了您的使用计划(如果您正在与企业合作**,您可能希望每秒钟有 5 个以上的请求**),您就可以开始了,然后接下来会提示您创建您的 API 密钥。
现在我发誓我们快到了。接下来,您需要进入您的 APIs POST 方法并添加 API 密钥需求,否则您仍然可以在没有密钥的情况下访问 API。
所以让我们直接进入方法响应
最后一步,别忘了部署您的 API。
您会惊讶地发现,做出更改却忘记部署是多么容易。
现在我们需要去做一些测试,以确保这是我们所希望的工作。所以我们要检查一下:
- #1:测试没有密钥的 API 请求失败
- #2:测试带有密钥的 API 请求成功
#1 测试没有 API 密钥的 API 密钥失败
import requests
url = "https:your_url.amazonaws.com/test/predict"input_data = {
"Property Type": "S",
"Old/New": "Y",
"Duration": "L"
}
r = requests.post(url, json=input_data)
print(r.json())
运行该脚本应该会返回以下错误。
{'message': 'Forbidden'}
如果这种情况没有发生,你需要检查你的 API 密匙是否已经被正确地附加到你的 API 上,因为它现在不工作。
但是,现在你已经成功地保护了你的 API。
#2:带有密钥的 API 请求成功
您需要返回到 API 网关,在 API 密钥下,您需要显示您的 API 密钥。
Half the battle of AWS is knowing where to find these things
将它粘贴到 Pycharm(或您正在使用的任何 IDE)中,这样您就可以在 POST 请求中传递它。
所以你的帖子请求应该是这样的:
key="yourapikey" # In reality it'll look much weirder
headers = {
'x-api-key': key
}
r = requests.post(url, json=input_data, headers=headers)
print(r.json())
您应该得到这样的有效响应(实际结果可能会有所不同)
164735.29632791804
包裹
因此,在本教程之后,您应该能够将您的模型部署为 lambda 函数,并使用 API 网关安全地将该 lambda 公开给外界。
我希望你们都学到了一些新的东西,如果有什么你想让我介绍的,就写在评论里,我会研究的。
在 Azure 函数上托管您的 ML 模型—第 1 部分
我关于模型部署的其他帖子
终于有了一种便宜又简单的方法来部署您的模型
这是几个星期的功能工程,模型选择和超参数调整,你完成了。
你的同事对你不断使用“泡菜”这个词感到困惑,他们不再问你在做什么,只是假设你真的喜欢保存蔬菜。
现在让我们把这个模型放在一个你可以从中获得一些价值的地方。
如果你想通过 AWS lambdas 部署你的模型,请点击标题上方的链接查看我的文章。
如果你想跟进 ,这里是我的 Git 回购的链接
Azure 函数 vs AWS Lambdas
这些与 AWS lambdas 非常相似,从模型部署选项来看,它们比 AWS lambdas 有更大的优势。
指定你的 Azure 函数运行时就像 pi 一样简单
还记得让 AWS Lambdas 用熊猫有多痛苦吗?我当然知道。改变一个小东西(你的内部 ML 包或者想用一个新的花式 sklearn 模型)AWS lambdas 就开始了。
接下来你使用 Docker 重新创建图层,但愿你不会忘记更改图层版本…
使用 Azure Functions,你只需指定一个 requirements.txt,当你将 Azure Functions 部署到你的帐户时,它会为你安装所有这些包。
它还允许你从本地直接上传模型到你的 Azure 函数。
这真是太酷了。
Azure CLI 允许你在本地调试你的 Azure 功能
如果你遵循我的 AWS lambda 教程,你可能会花很多时间编写简单的测试来运行你的 AWS lambda,以确保你可以导入熊猫或一些非常小的东西。
然后你可以测试通过 Python 中的请求包向 Azure 函数发送数据。但是现在您只需在本地运行它们,并通过请求包发送 POST 请求,您就可以在部署之前确保它正在工作
创建 HTTP Azure 函数只需一个简单的步骤就可以部署和公开端点
以前,当将模型部署到 AWS Lambdas 和 API Gateway 时,您必须分别执行这两个步骤。有了 Azure 函数,它们只是一个步骤。
除此之外,你的 Azure 函数是预先保护的(比如:有一个 API 密匙),所以当你在 API 网关端配置东西时,你不必担心关闭你的 AWS Lambda。
入门指南
现在我们进入有趣的部分。如果你想跟进,这里有GitHub 回购。
首先你需要安装 Azure CLI 工具。这将允许您在本地测试您的 Azure 功能,然后将其推送到您的 Azure 帐户。
在这一点上,获得 Azure 帐户可能是值得的。如果你从未注册过,你可能会得到 200 美元的免费积分。但是,如果您不能,请不要担心,这些部署不会让您倾家荡产。
按照这个来安装 Azure CLI,这样我们就可以开始构建我们的功能了。
要测试这一点,您需要尝试:
func new
如果安装成功了,你就可以在本地创建一个 Azure 函数。但是在 Windows 上工作可能有点困难。
让 Azure CLI 在 Windows 上运行
如果你得到一个错误,说 func not found,你想安装做一些更多的步骤:
- 通过管理外壳安装巧克力
- 使用 chocolatey 安装 azure cli 工具 azure cli 工具工作(答案#2)。
- 运行 func 检查您的结果是否与 Stackoverflow 答案匹配,然后您就可以开始了
显然这是因为 func 不在你的路径上。所以,如果你比我聪明,并整理了这些东西,让我知道,我会在这里添加它。
这将是你初始化 Azure 函数的地方。简单的事情是,如果你让你的 Azure 函数响应 HTTP 请求,那么你已经部署了你的模型,并通过 API 在一个简单的步骤中使它可用。
设置你的 Azure 函数和本地 python 环境
#1 初始化您正在工作的文件夹
func init titanic_model --python
#2 设置基本 Azure 功能
cd titanic_model
func new
- 选择 HTTP 触发器
- 给你的函数一个名字,我称之为我的模型 _ 部署
如果你做对了,你会看到这个:
眼尖的读者会意识到我是在我的基本 Python 安装上运行这些功能的。
当我们在本地运行 Azure 函数时,它使用我们指定的 Python 环境来执行。这让我们可以使用 requirements.txt 来指定一个本地环境,该环境将镜像将在 Azure 函数上运行的内容。
我使用 conda 创建我的环境,因为它很简单,但是你也可以使用 venv 或任何其他解决方案。
我还会添加到 requirements.txt 中,以涵盖所有的依赖项。这是我的样子:
azure-functions==1.0.4
pandas==0.25.3
scikit-learn==0.21.3
requests==2.22.0
现在运行,这样您就可以安装包并构建用于部署的模型。
conda create --name model_deploy python=3.7
activate model_deploy
pip install -r requirements.txt
构建模型和配置 Azure 函数
我在 Git repo 中放了一个简单的模型来指导您。来创建模型。您需要运行的 pkl:
python model_build.py
这个模型是根据泰坦尼克号的数据集建立的,它预测了幸存者:
- 年龄
- 乘客等级
- 上船(乘客上船的地方)
- 性
改变 init。py 处理您的请求
init。py 是你的 Azure 函数运行的内容。
使用 JSON 输入/输出可能有点麻烦,我花了一段时间才适应。
我将把代码的主要部分粘贴在这里,这样我就可以突出我遇到的主要困惑,这样你就可以从我的错误中吸取教训。
data = req.get_json()
data = json.loads(data)
您将为此模型使用 POST 请求。但是当您读入 JSON 请求时,它仍然是字符串格式,所以我们需要使用 json.loads 将它转换成适当的 dict/JSON 对象,然后才能使用数据进行预测。
response = []
for i in range(len(data)):
data_row = data[i]
pred_df = pd.DataFrame([data_row])
pred_label = clf.predict(pred_df)[0]
pred_probs = clf.predict_proba(pred_df)[0]
results_dict = {
'pred_label': int(pred_label),
'pred_prob_0': pred_probs[0],
'pred_prob_1': pred_probs[1]
}
response.append(results_dict)
return json.dumps(response)
有几件事我会很快提到:
- 警察。DataFrame([data_row]):允许您在 Pandas 中创建一个单行数据框架。否则你会得到一个索引错误
- int(pred_label):因为输出的类是 numpy 数据类型(int64)而在返回 JSON 对象时是不可用的,所以我转换了它
- json.dumps(response):即使我们使用 json,你也需要在发回之前将其转换成一个字符串
现在让我们在本地部署那个坏的 boi
func host start
一旦它启动并运行,应该会给你下面的结果
http://localhost:7071/API/model _ deployment是我们想要发送请求的目标。在本地 Azure 函数运行之后,使用 test_api.py 将数据 ping 到我们的 api。您应该会看到以下结果:
布欧耶。现在成功了!!!!!所以现在我们有了一个本地运行的 Azure 功能,我们需要把它推送到 Azure,这样我们就可以永久地部署它。
部署到 Azure
现在我们已经让 Azure 函数在本地工作,我们可以把它推到 Azure 并部署这个坏的 boi。
如果你还没有创建一个 Azure 帐户,那么现在是时候了。一旦你这样做了,就去你的门户网站,然后运行一个 Azure Function 应用程序。
下面是我如何配置我的
Using Docker is a good shout. I’ll do my next blog post on that
现在你已经创建了你的 Azure 应用,你现在可以将你的模型部署到 Azure 并进行测试。
从您的本地 Azure 函数目录中,您将需要运行以下命令
az login
这将无缝执行,或者要求您登录 Azure 帐户。一旦完成,你就可以开始了。现在让我们将您的本地代码推送到 Azure。
func azure functionapp publish <APP_NAME> --build remote
其中 APP_NAME 是您给函数 APP 起的名字(duh)。我的是泰坦模型,但是你的会不同。
一旦完成,你需要找到你的 Azure 应用的 URL。你可以在这里找到
这个 URL 是您将要用来从 Azure 访问您的模型的。所以用你的 azure 函数 url 替换 test_api.py 中的 azure_url,试一试。
如果一切按计划进行,您将获得:
[
{
"pred_label": 1,
"pred_prob_0": 0.13974358581161161,
"pred_prob_1": 0.86025641418838839
},
{
"pred_label": 0,
"pred_prob_0": 0.65911568636955931,
"pred_prob_1": 0.34088431363044069
},
{
"pred_label": 1,
"pred_prob_0": 0.13974358581161161,
"pred_prob_1": 0.86025641418838839
},
{
"pred_label": 0,
"pred_prob_0": 0.65911568636955931,
"pred_prob_1": 0.34088431363044069
}
]
现在,您还会有一种温暖而模糊的感觉,即将您的第一个 ML 模型部署到 Azure 函数中!!
由于 API 端是由 Azure 函数本地处理的,我将在第二部分使用 Docker 重新创建这个过程,这可能会使事情变得更简单。
热狗还是热狗:使用元编程编写 UI 测试
关于 Kotlin 运行时脚本当前缺点的见解,黑盒与白盒测试,以及 AST 解析如何帮助编写愚蠢(但有效)的 UI 测试。
在对 Kotlin 的长期探索中,现代元编程的下一个前沿领域,我很高兴能够使用元编程来编写 UI 测试。这个想法是将一个 TornadoFX 项目上传到的 TornadoFX-Suite 中,后者依次读取项目,检测 UI 控件,并动态编写 UI 测试。
Tornado-FX 套件即将开始编写 UI 测试。尽管在过去几个月里 AST 解析受阻,但我终于又开始行动了。当我接近复合分解的 MVP 时,现在可能是谈论如何使用应用程序的复合抽象来创建测试的好时机。以下是迄今为止所取得的成就:
The last two steps are stages 2 and 3 of this project.
我们已经检测到一些 UI 输入,并使用解析来创建类分解。所以…我们该拿它怎么办?让我们花点时间来谈谈 UI 测试。
用户界面(UI)测试
测试是验证软件是否按预期运行的必要组成部分。不同种类的测试可能有其各自的目的,但是如果没有一个坚实的基础,其他种类的测试可能会变得乏味,甚至糟糕。测试一般可以分为两类: 黑盒测试和白盒测试 。
黑盒测试是一种从用户角度保证软件质量的方法,意思是在不知道代码本身是什么的情况下测试软件。这可能意味着测试是在用户与应用程序交互时在应用程序本身上进行的(手动或自动)。其他测试可能会检查应用程序的不同组件是否能够有效地相互通信。 黑盒测试测试系统的行为和功能。
另一方面,白盒测试测试系统如何运行。与黑盒测试不同,白盒测试是利用代码本身的知识编写的。 白盒测试涵盖了代码的健康,它的业务逻辑,并确保正确的数据路径。
我们正在从我们分析的代码中创建 UI 测试,这可能是一个灰色地带。根据 Android 文档,UI 测试“让你确保你的应用程序满足其功能需求,并达到高质量标准,从而更有可能被用户成功采用。”
探索应用程序本身的环境可能会发现错误,但是如果没有白盒测试,就很难确定不需要的功能的来源。
一年多前,我编写了一个非常酷的仪表板生成器,来帮助生成可以呈现为 JavaScript 的 XML。因为我自己对拖放功能进行了微观管理,所以很难确定为什么代码本身会出现这样的错误:
应用程序不会崩溃,也不会记录任何错误。应用程序本身没有中断,但是功能显然没有按预期工作。UI 错误很难诊断和解决,但这正是 UI 测试的用武之地。UI 测试不仅有助于定义和保证期望的行为,而且随着复杂性的增加,它还使得调试、重构和维护代码变得更加容易。
一个好的 UI 测试应该是这样的:
- 抓取特定视图
- 在视图上执行一个动作
- 检查用户界面,看它看起来是否正常
在这个特别讨厌的 bug 的例子中,我们可以看到大多数预期的行为都在那里;然而,很明显,一个特定的序列暴露了一个功能漏洞。我们的测试将遵循这些步骤,但包括边缘情况以及顺序交互的排列将有助于解决类似这些棘手的疏忽。
元编程如何编写 UI 测试?
在编写 UI 测试的元编程中存在一些挑战,尤其是在像 Kotlin 这样的静态类型语言中。虽然从 Kotlin 1.3 开始,Kotlin 具有反射能力和自脚本能力来编译运行时代码,但如果没有必要的导入和依赖,我无法成功编译上传的 Kotlin 文件。
不幸的是,Kotlin 还没有完全准备好像在运行时编译整个项目这样雄心勃勃的事情,但是我将同时关注问题 KT-27960 和 KT-28916 。
现在,我求助于另一个 Kotlin 解决方案,通过创建一个 AST 解析器,使用 kastree 和 gson 进行基本的类分解,来编写我自己版本的代码自省。创建我自己的 AST 解析器有助于克服挑战,例如不必担心封装和访问级别或递归中的类型转换,同时将解决方案保留在 Kotlin 中——最终,编写 Kotlin DSLs 变得容易!
我们有一种方法来确定所需的控制,我们有一个基本的复合分解。让我们看看如何利用这些东西来实现编写 UI 测试:
- 抓取一个特定的视图 —通过用视图()扩展类,TornadoFX 有一种区分视图的简单方法。说到测试,我们可以用 id 抓取视图控件。由于人们通常不会在他们的 TornadoFX DSLs 上附加 id,我不想强迫他们这样做。相反,我们可以遍历视图层次结构中的节点,并将每个节点的路径以及节点的状态保存在一个字典中,以便在编写脚本时容易访问。
- 在视图上执行一个动作——以编程方式在视图上执行一个功能并不是一个挑战,除了保存一个可以用每个控件完成的可能动作的字典。
- 检查用户界面,看看它是否…嗯,变化—真正的挑战是知道 UI 控件预期会发生什么变化,更不用说弄清楚 UI 应该是什么样子了。机器学习可能会发现节点中哪些属性发生了变化,但目前,我们将保持较小的目标。保持一个组合契约的抽象表示对于帮助跟踪视图中的节点是什么是必要的,节点的状态是否变脏是必要的,以及将改变节点的函数的关系映射到节点本身是必要的。如此复杂的关系需要用有向图结构来表示。
这是一个很高的要求,只是看看一个节点是否改变或保持不变,热狗或不是热狗。为了让测试更有用,下一阶段将应用 Tensorflow 开始收集尽可能多的数据,以进行更智能的测试。这个阶段的 UI 测试可能没什么用,但这是我感到兴奋的事情。
正如库梅尔·南贾尼在硅谷所说,“核心技术是有效的。它只是……它只是需要训练。”
旧金山湾区的住房:使用机器学习寻找交易
模拟房价揭示了被低估的社区和房源。
Aerial view of San Francisco downtown and the East Bay. Photo credit: Unsplash
总结
本文描述了旧金山湾区单户住宅价格的收集、可视化和建模。使用 2019 年 6 月的 7,000 个活跃房源的数据集,探讨了影响该地区房价的因素。发现用位置数据(学校质量和通勤时间)补充列表信息(卧室和浴室的数量、住宅大小和地块大小)可以显著提高简单线性回归模型的解释能力。回归系数还讲述了一个有趣的故事,即市场对住宅及其位置各方面价值的看法。重要的是,定价模型能够识别被低估的房源和社区,为个人购房者和投资者提供潜在兴趣的关键信息。
简介
虽然自 2000 年以来,标准普尔 500 和美国整体房地产市场的表现几乎相同(都上涨了约 100%),但旧金山湾区的房价指数上涨了约 167% ( 圣路易斯联邦储备银行)。因此,海湾地区的房主享受了一个通过房地产积累财富的机会,而这种机会在美国其他地区不一定能获得。
对于那些已经入市的人来说,自 2000 年以来,房地产价值增长了近两倍,这无疑是一件好事。然而,对于那些刚搬到该地区的人来说,攒钱付首付和选择买房地点可能是一项艰巨的任务。受我与朋友和家人的讨论以及投资的基本概念(即,购买定价过低的资产)的启发,我开始收集尽可能多的关于湾区独户住宅当前价格的信息,应用简单的机器学习技术梳理出推动住宅价值的因素,并确定该地区可能吸引投资的角落。
方法
使用 Regex 和 Pandas 清理和处理来自整个湾区的单户住宅列表的数据(地址、床位、浴室、住宅大小、地段大小、纬度/经度坐标和价格)。作为列表数据的补充,通勤时间从谷歌地图获得,学校质量数据来自 2018 年加州学生表现和进步评估( CAASPP )。使用来自斯坦福土方工程的 Cartopy 、 Matplotlib 和 shapefiles(城镇、邮政编码和街区边界)将结果信息叠加到地图上。使用 Seaborn 可视化了盒/条图和变量之间的成对关系。使用 Statsmodels 和 Scikit-learn 库对数据进行普通最小二乘(OLS)回归分析。这个项目的完整源代码可以在 GitHub 上找到。
结果
可视化清单数据
地理趋势
有了海湾地区市场上几千处房产的信息,所有房源的位置都用 Python Cartopy 包和从斯坦福 Earthworks 获得的城市边界信息绘制在地形地图上,并用价格进行颜色编码(图 1)。这张地图揭示了该地区房价相对成本的清晰地理趋势。例如,旧金山、马林县和半岛通常包含最昂贵的 20%的房源(深蓝色数据点),而奥克兰、圣莱安德罗和里士满通常包含最便宜的 20%的房源(深红色数据点)。类似地,在南湾,靠近圣克鲁斯山脉的房子通常比沿着迪亚波罗山脉的房子更贵。
Figure 1. Overview of single-family homes listed for sale in the Bay Area in June 2019. The 7,153 entries are split into quintiles by price, with list prices falling within the bottom and top 20% colored dark red and blue, respectively.
这种分析的一个自然延伸是将清单分成次区域。放大旧金山、东湾、半岛和南湾,地理价格趋势再次显现(图 2)。旧金山最贵的独栋住宅位于市中心和普雷斯迪奥之间,而最便宜的位于该市南部,从日落区到湾景区。在东湾,奥克兰山两侧(包括皮德蒙特、伯克利和奥林达)的房子最贵,而里士满、南奥克兰和圣莱安德罗的房子最便宜。在半岛上,阿泽顿的帕洛阿尔托和希尔斯伯勒的房价最高,而圣马特奥和东帕洛阿尔托的房价最低。在南湾,最靠近圣克鲁斯山脉(洛斯阿尔托斯、萨拉托加和洛斯加托斯)的房子最贵,而在那以东的几乎所有房子都不太贵。跨地区的相对成本也值得注意:虽然东湾的最高价格五分之一起价为 150 万美元,但半岛上的最低价格也是同样的价格。
Figure 2. Zoom showing detail of single-family home list prices in the San Francisco, East Bay, Peninsula, and South Bay regions. In each case, price quintiles have been recalculated to reflect the distribution of prices within the highlighted region.
城镇间的房价和地价
为了更好地量化价格变化,使用 Python Seaborn 库构建了箱线图。这种表示允许跨分类变量(这里是城市/城镇)的分布比较,其中彩色框表示四分位范围的界限(第 25 到 75 个百分点),其中的线显示中值价格,而触须包含分布的剩余部分减去异常值。
从这个角度来看,从最低到最高的房价中值对选定地区进行排序提供了一些有趣的见解(图 3)。例如,房价中值最低的主要是东北湾(瓦列霍、安提阿和里士满),而最高的是半岛(希尔斯伯勒、帕洛阿尔托和伍德赛德)。此外,散点叠加在箱线图上说明了大多数人居住的地方:奥克兰、圣何塞和旧金山都有数百套当前房源,而更昂贵的十几个社区只有少数几套当前待售房屋。
Figure 3. Box plot displaying home price and for selected Bay Area cities, with individual observations superimposed to reveal sample size and distribution.
有趣的是,将同样的分析应用到房子所在土地的价格上(图 4 ),会得出有些不同的结论。例如,虽然伍德赛德的房价中位数在图 3 所示的 29 个城镇中排名第三,但该社区(主要位于 I-280 以西,以骑马闻名)的单位面积土地成本中位数实际上是这 29 个城镇中最低的。同样,从土地成本的角度来看,其他中值房价较高的城镇,如 Orinda 和 Los Gatos,似乎更实惠。另一方面,帕洛阿尔托和旧金山是迄今为止购买土地最昂贵的地方,这反映了它们作为该地区主要经济活动中心的地位。
Figure 4. Box plot displaying land price for selected Bay Area cities.
对于旧金山、奥克兰和圣何塞,房价的分布也是按社区划分的。虽然旧金山的几个街区(内里士满、俄罗斯山、诺布山、滨海区、北海滩)号称是湾区最贵的(> 500 万美元)住宅,但仍有几个街区(湾景、猎人角、Visitacion Valley、怡东、克罗克-亚马逊)提供标价低于 100 万美元的房产(图 5,顶部)。
在奥克兰(图 5,中间),只有 3 个社区(蒙特克莱尔、栈桥格伦和洛克里奇)的房价中值超过 100 万美元,而几乎所有其他社区(最显著的是考克斯/艾姆赫斯特公园、体育馆和弗鲁特维尔)都有大量价格低于 50 万美元的房源,是该湾最便宜的房源之一。
Figure 5. Box plots showing home prices across neighborhoods in San Francisco, Oakland, and San Jose.
这样的便宜货通常在圣何塞找不到(图 5,底部),那里最便宜的社区房价中值不到 100 万美元。即便如此,这个地区只有少数房子的价格高于 100 美元 2M,而且它们似乎大多位于柳树谷、阿尔马登和常青树社区。有趣的是,这里最高的中值房价出现在西圣何塞,尽管价格分布比类似的社区窄得多,这或许反映了这样一个事实,即该地区的房价普遍不高,其房价是由靠近苹果和网飞等科技巨头而推高的。
建模房价
在这里,我描述了一个非常简单的方法,使用普通的最小二乘(非正则化)线性回归模型来构建房价模型。我的目标是( 1 )产生一个可解释的模型,其系数揭示了市场对房屋及其所在土地的各个方面的价值,并( 2 )确定湾区的哪些部分提供了最“物有所值”的东西,即那些提供了多种功能组合、保证价格高于市场目前承受水平的东西。当然,其他方法(例如,k-最近邻法、决策树、神经网络)可以产生一个解释力更强的模型。下面描述的线性模型符合本文的意图——从描述性分析的角度提供对房屋价值的见解。
清单数据
除了简单描述当前湾区住房和土地价格的地理分布,该数据集还用于使用 Python Statsmodels 和 Scikit-learn 库对房价进行建模。
首先,数据集中的 7,151 套房屋的价格根据清单中包含的房产数据(卧室数量、浴室数量、房屋大小和地块大小)分别绘制,并通过 Seaborn pairplots(图 6,顶部)使用普通最小二乘(OLS)回归进行拟合。在这四个特征中,房子的大小解释了大多数观察到的标价差异( R = 0.56)。另一方面,当完整的数据集缩小到一个邮政编码(95126)时,这些特征中的每一个都可以更好地解释价格差异(图 6,底部)。
Figure 6. Relationship between price and listing factors is tightened when full set of listings (top) is narrowed to a single zip code (bottom).
新增功能:通勤时间和学校质量
整个地区的房屋面积和价格之间的适度相关性,以及单个邮政编码内的强相关性,提出了一种非常直观的可能性,即特定位置的因素也对价格有影响。为了将“地点”纳入等式,引入了反映便利性和特权的额外数据:通勤时间和公立学校质量。
认识到旧金山和帕洛阿尔托是该地区的两个主要经济中心,通勤时间是通过谷歌地图测量的,即从每个邮政编码到两个目的地中较近的一个的乘车时间(周三上午 8 点)(图 7,左侧)。结果表明,从旧金山到圣何塞,半岛上上下下的住宅提供了通勤时间短于一小时的可能性。同样,马林和奥克兰到旧金山也很方便,弗里蒙特到帕洛阿尔托也是如此。另一方面,从外东湾(里士满、安提阿、圣拉蒙)的家到更近的枢纽的通勤时间通常超过 1.5 小时。
Figure 7. Commute times (left) and school quality (right) for zip codes across the Bay Area.
公立学校的质量也很可能在特定地区独户住宅的定价中发挥作用。方便的是,加州学生表现和进步评估(CAASPP)标准化测试机构已经为加州的每所公立学校提供了大量的学生水平数据。使用 2018 年的数据集,平均每个学校各年级的熟练程度测量值,以及每个邮政编码的学校的熟练程度测量值,学校质量被量化为特定邮政编码内被认为精通阅读和数学的学生的比例(图 7,右)。即使经过这样的平均,这张地图也指出了整个海湾地区学生成绩的近乎双峰的分裂,马林县、半岛和三谷地区享有优秀的学校(> 75%的学生优秀),而安提阿、瓦列霍、里士满、圣莱安德罗和圣何塞的公立学校学生相比之下似乎很挣扎。
Figure 8. Relationship between price and commute times (left) or school quality (right) across the Bay Area.
这些新参数也被评估为标价的独立预测指标。事实上,通勤时间和学校分数在相似的程度上解释了( R ~ 0.55)观察到的价格差异,作为之前的最佳预测因素(图 8)。直觉上,最佳拟合线意味着房价和学校质量之间的正相关关系,以及价格和通勤时间之间的负相关关系。
功能选择
多重共线性,或特征集合中特征之间的显著相关性,可导致系数的不同值,这取决于选择哪些特征包含在模型中。为了创建一个具有可解释系数的简单模型,这个扩展的特征集必须筛选出那些为价格预测提供有意义的新信息的特征集。
Figure 9. Heatmap showing correlation across independent variables.
为此,图 9 中示出了跨特征集的皮尔逊相关系数。事实上,列表数据中的三个参数(卧室、浴室的数量和房屋大小)都彼此强烈相关(0.65 < ρ < 0.83),如果我们要在拟合模型后从系数中提取意义,就必须删除其中的两个。作为价格的最强独立预测因素(图 6,顶部),房屋大小被保留在模型中,而卧室和浴室被丢弃。
引入通勤时间和学校质量数据,简单线性回归模型构建如下:
价格~户型+地段+通勤时间+学校分数 ( 1 )
拟合和解释模型
使用 sci kit-learnlinear regression软件包将房价数据回归到上面列出的四个特征上。十倍交叉验证用于估计泛化误差,或模型对未用于训练模型的数据的表现。根据房屋大小、地段大小、通勤时间和学校分数,使用数据集中的所有示例获得了 0.61 的平均测试集 R 。然而,剔除异常值后,拟合质量有了相当大的提高,在剔除价格最低和最高的 5%的房屋(分别为 45 万美元和 430 万美元)后,平均测试集 R 达到 0.70。下面将进一步研究根据过滤数据训练的模型。
这种简单模型的主要优点是易于解释。为此,特征系数表明,在其他条件相同的情况下,湾区购房者应该准备好为每增加一平方英尺的室内空间支付大约 430 美元,为每增加一平方英尺的室外空间支付 0.35 美元,为每分钟节省的通勤时间支付 14k 美元,为当地公立学校的优秀学生比例每增加一个百分点支付 12k 美元:
*预计标价($)
= $675,900
- ($429 /平方英尺)住宅面积(平方英尺)
- ($0.35 /平方英尺)地段面积(平方英尺)
+(-13,560 美元/分钟)通勤时间(分钟) - ($12,217 / %熟练程度)* ( 2 )
鉴于其简单性,以及训练和测试集误差非常接近的事实( R 分别为 0.71 对 0.70),可以有把握地说,这种四特征最小二乘线性回归模型位于偏差-方差权衡的高偏差侧。例如,通过使用更大的特征集或非参数方法,如k-最近邻或决策树,可以毫不费力地构建更精确的模型(参见我最近关于同一主题的文章)。可解释性是付出的代价——关于额外一平方英尺室内空间或较短通勤距离的市场价值的简单规则不容易从复杂的模型中提取出来。
当前模型的性能( R = 0.71)和完美性( R = 1)之间的差距可以通过三个因素来解释:( 1 )在简单线性模型下无法处理的非线性效应(例如,特征之间的交互效应、房屋大小和价格之间的饱和关系)、( 2 )可能影响价格但不包括在该模型中的房屋及其周围环境的方面,以及( 3 )不匹配第一个问题可以通过增加复杂性来解决,例如添加交互项或转向非参数模型。第二个可以用附加特征来固定,例如本地犯罪率、邻居的美学、财产状况。邻里侦察兵有一个犯罪风险地图,但是据我所知,潜在的数据还没有公布。通过量化理想的属性,如树木覆盖率或海拔高度,可以近似得出邻里关系的美观程度。第三是由市场定价固有的随机性造成的不可减少的误差。
识别被低估的领域
购买被低估的资产是价值投资的核心原则。从机构投资者或精明的购房者的角度来看,住宅价值模型提供了一个诱人的前景,可以识别相对于市场其他部分可能被错误定价的区域(以及最终的个人房产)。
为此,每个列表的位置被标绘在地形图上,现在用标记颜色表示实际价格和预测价格之间的差异(图 10)。低价房源(以红色显示)是那些提供高价房典型的四个特征(住宅和地段大小、通勤时间和学校质量)组合的房源。根据该模型,定价过低的地区可以在旧金山(日落区和湾景区)、沿半岛(戴利市和东帕洛阿尔托)、东湾(阿拉米达、奥林达、海沃德和弗里蒙特)和马林(圣安塞尔莫)找到。
Figure 10. Map showing difference between list price and price predicted by the model described in Equation 2. Listings deemed by the model to be undervalued and overvalued are shown in red and blue, respectively.
考虑一个更复杂模型的场景,例如,我们为每个城市或邮政编码添加虚拟变量。这个模型将提供一个更准确的房价假设,通过将一些美元数字添加到恰好位于高消费城市或邮政编码的列表的预测价格中,从而更好地识别列表级别的交易。然而,我们将失去识别地图上定价过低或过高的角落的能力!通过只选择与房地产内在价值相关的特征,我们剥夺了我们的模型了解东帕洛阿尔托价格便宜的能力,从而获得了揭示东帕洛阿尔托相对于其所提供的价格偏低的能力。
箱线图提供了残差的另一种有趣表示(图 11)。通过实际和预测标价之间的差异排列所选城市,可以看到东帕洛阿尔托和奥林达提供了住宅和位置属性的组合,这些属性是通常定价高于这两个位置 50 万美元的列表的特征。另一方面,门洛帕克、洛斯加托斯和帕洛阿尔托等地的情况似乎正好相反。
Figure 11. Box plot showing difference between list price and price predicted by the model described in Equation 2 for selected cities/towns.
对于旧金山、奥克兰和圣何塞,残差也绘制在各个街区上(图 12)。在旧金山,交易很简单:最实惠的社区看起来被低估了。另一方面,在奥克兰就不一定了:例如,蒙特克莱尔和格伦高地社区,看起来在标价上很贵,但相对于 Eq 也很便宜。2.圣何塞似乎结合了两者的元素,这表明一些名义上昂贵的街区(柳树谷,中城)可能确实被高估了,而其他昂贵的街区(寒武纪公园,西圣何塞)看起来在 Eq 方面更合理。2.
Figure 12. Box plots showing difference between list price and price predicted by the model described in Equation 2 across neighborhoods in San Francisco, Oakland, and San Jose.
结论与展望
从几千份当前的房地产清单中,我们用图表和统计的方法研究了旧金山湾区的独栋房屋的价格。将住宅和地块大小、通勤时间和学校质量纳入多元线性回归拟合,可以对价格进行建模,并识别可能吸引投资的被低估的海湾地区。未来的工作可能包括通过添加可能影响位置的感知合意性的附加特征(例如,犯罪数据、树木覆盖率、海拔)来改进模型,并随着未来几年住房市场的发展跟踪该定价模型的性能。
应用数据科学技术来为房地产交易提供信息不需要只由专业投资者来进行,与 Python 一起使用的免费开源包使个人能够收集大型数据集,可视化关键指标,并应用机器学习技术来识别可能被其他市场参与者忽略的交易。
伊斯坦布尔房屋销售价格和场地数据分析
Photo by Osman Köycü on Unsplash
A.介绍
A.1 .背景的描述和讨论
伊斯坦堡是世界上最大的都市之一,居住着超过 1500 万人,人口密度为每平方公里 2.813 人(T2)。作为这个城市的居民,我决定在我的项目中使用伊斯坦布尔。这个城市总共分为 39 个区。然而,事实上,各区都被挤在大约 72 平方公里的区域内,这使得这个城市有一个非常交织和混杂的结构[1]。
从图中可以看出,伊斯坦布尔是一个人口多、人口密度大的城市。如此拥挤的城市导致人口密集的城市中的商店和社交分享场所的所有者。当我们从投资者的角度考虑时,我们希望他们更喜欢房地产成本较低的地区,他们希望建立的企业类型也不那么强烈。如果我们想到城市居民,他们可能也想选择房地产价格较低的地区。同时,他们可能希望根据社交场所的密度来选择地区。然而,现在很难获得将投资者引向这一方向的信息。
当我们考虑所有这些问题时,我们可以创建一个地图和信息图表,其中房地产指数放在伊斯坦布尔上,每个地区根据场地密度进行聚类。
A.2 .数据描述
为了考虑这个问题,我们可以列出如下数据:
- 我从 NYU 的空间数据库中找到了土耳其的二级行政区划[2]。的。json 文件有土耳其所有城市的坐标。我清理了数据,并将其归入伊斯坦布尔市,在那里我用它创建了伊斯坦布尔房屋销售价格指数的 choropleth 地图。
- 我使用 Forsquare API 来获取伊斯坦布尔给定行政区最常见的场地[3]。
- 关于伊斯坦布尔市的人口和社会参数,没有太多的公开数据。因此,在大多数情况下,您必须建立自己的数据表。在这种情况下,我从住房零售网页[4]上收集了伊斯坦布尔每个区最新的每平方米住房销售价格平均值。
- 我使用谷歌地图的“搜索附近”选项来获得每个区的中心坐标。[5].
B.方法学
作为数据库,我在学习中使用了 GitHub repository。我的主数据,其中有主要成分区,平均房价,纬度和经度城市的信息。
Master Data
我使用 python yellow library 来可视化伊斯坦布尔及其行政区的地理细节,并创建了一个叠加了行政区的伊斯坦布尔地图。我使用纬度和经度值得到如下的视觉效果:
Borough of Istanbul
我利用 Foursquare API 来探索行政区并对它们进行分段。我根据给定的经纬度信息,为每个区设计了 100 个场地和 750 米半径的限制。这里是一个从 Forsquare API 列表场馆名称,类别,纬度和经度信息的标题。
List of Venues
总的来说,Foursquare 返回了 43 个地点。这是一张行政区和场馆的合并表。
Table of Boroughs and Venues
我们可以看到卡迪科伊、马尔特佩、贝约格鲁、贝西克塔斯、西斯里和法提赫·豪达到了 100 场馆的限制。另一方面;Pendik、Arnavutkoy、Tuzla、Adalar、Buyukcekmece、Sultangazi、Cekmekoy、Beylikduzu、Sultangazi 行政区在 20 场馆下方,在我们给定的经纬度坐标中,如下图所示。
这一结果并不意味着在各个行政区都进行了调查。实际上,这取决于给定的纬度和经度信息,这里我们只是为每个区运行单一的纬度和经度对。我们可以用包含更多经纬度信息的邻居信息来增加可能性。
Number of venues for each borough
总的来说,Foursquare 返回了 256 个独特的类别,然后我创建了一个表格,显示了下表中每个区的前 10 个场馆类别。
List of top 10 venue category
我们在行政区有一些共同的场地类别。出于这个原因,我使用了无监督学习的 K-means 算法来对行政区进行聚类。K-Means 算法是无监督学习中最常用的聚类方法之一。
首先,我将运行 K-Means 来将行政区聚类成 3 个聚类,因为当我用肘方法分析 K-Means 时,它确保了 K-Means 的最佳 K 的 3 度。
Elbow method to specify the best k value
这是我的合并表,每个区都有聚类标签。
Merged Table with clusters
我们还可以估计每个集群中第一个最常见的场馆的数量。因此,我们可以创建一个条形图,帮助我们为每个集群找到合适的标签名称。
Number of venues in each cluster
当我们检查上图时,我们可以将每个集群标记如下:
- 群组 0:“咖啡馆场所”
- 群组 1:“多种社交场所”
- 第 2 组:“住宿和密集咖啡馆场所”
我们还可以考察一下,在不同的范围内,房屋平均销售价格出现的频率是多少。因此,直方图有助于可视化:
Average Housing Sales Prices in Ranges
如上图所示,我们可以将范围定义如下:
- 4000 AHP:“低级 HSP”
- 4000–6000 AHP:“中级 1 级 HSP”
- 6000–8000 AHP:“中级 HSP”
- 8000–10000 AHP:“高一级 HSP”
-
10000 AHP:“高-2 级 HSP”
我的目标之一是在地图上显示每个区的前 3 名场馆信息的数量。因此,我根据前 3 个场馆的数量对每个区进行了分组,并将这些信息组合在 Join 列中。
Number of top 3 venues for each borough
C.结果
让我们将这些新变量与主主表中的相关集群信息合并。
Master table
现在,您可以看到上表中的最后三列是 Join、Labels 和 Level_labels。你还可以看到伊斯坦布尔的一个聚集地图区
Clustered map boroughs of Istanbul
在总结部分,我的目的之一是用 choropleth 风格的地图可视化每平方米的平均房屋销售价格。因此,我首先从 NYU 空间数据库[2]下载了一个土耳其二级行政区划的 json 文件。我清理了 json 文件,只调出了伊斯坦布尔市。
在最后一部分,我制作了 choropleth 地图,上面有每个区的以下信息:
- 区名,
- 群集名称,
- 住房销售价格(HSP)水平,
- 场馆数量前三名
Choropleth map of Istanbul with final datas
D.讨论
正如我之前提到的,伊斯坦布尔是一个在狭窄区域内人口密度很高的大城市。总共 39 个区的测量总数和人口密度可能不同。由于这种复杂性,在聚类和分类研究中可以尝试非常不同的方法。此外,很明显,并不是每种分类方法都能为这个大都市区产生同样高质量的结果。
我在这个聚类研究中使用了 Kmeans 算法。当我测试肘部方法时,我将最佳 k 值设置为 3。然而,只使用了 39 个地区坐标。为了获得更详细和准确的指导,可以扩展数据集,还可以钻取街区或街道的细节。
我还通过这些信息进行了数据分析,在 GitHub 上添加了地区坐标和房屋销售价格平均值作为静态数据。在未来的研究中,还可以从特定的平台或包中动态访问这些数据。
我通过在伊斯坦布尔地图上可视化数据和聚类信息来结束研究。在今后的研究中,可以通过网络或电话向直接投资者进行申请。
F.结论
因此,人们开始转向大城市创业或工作。因此,人们可以通过访问提供此类信息的平台来获得更好的结果。
不仅对于投资者,城市管理者也可以通过使用类似的数据分析类型或平台来更有规律地管理城市。
G.参考资料:
- [1] 伊斯坦布尔—维基百科
- [2] 土耳其二级行政区划
- [3] Forsquare API
- [4] 来自“2018 年 Hurriyet 零售指数”的各区房屋销售价格
- 【5】谷歌地图
22 年的 AI 优势如何改变国际象棋
机器完美前沿笔记
德克·克内梅尔和乔纳森·福利特
Figure 01: The Game of Kings
[Illustration: “Chess Players” by Lovis Corinth (1918), National Gallery of Art]
1997 年,IBM 超级计算机“深蓝”在六局系列赛中以 4 比 2 的比分击败了国际象棋世界冠军加里·卡斯帕罗夫。这是我们称之为“思维机器”发展过程中的一个里程碑时刻,因为在当时世界上最负盛名的战略游戏中,计算机已经证明了自己比最优秀的人类更优秀。
数百年来,机器在国际象棋中击败人类的基准一直是关注的焦点。众所周知,1770 年研发的土耳其机器人让像拿破仑·波拿巴和本杰明·富兰克林这样的名人激动又困惑。这只是一个精心设计的骗局,这个人力驱动的、不完全自动化的机器人愚弄了公众近 100 年。它的非凡之处不仅在于它似乎是一台自动机,还在于它是一台看似天才的自动机,能够在《王者游戏》中击败人类。从那时起,对能够在国际象棋中击败人类的机器的追求就一直持续着。事实上,第一波计算机科学先驱,像艾伦·图灵、克劳德·香农和约翰·冯·诺依曼,都是国际象棋选手,都试图编写能够掌握国际象棋的软件。
Figure 02: A Chess Playing Automaton?
[Illustration: “The Turk” by Joseph Racknitz (1789), Humboldt University Library]
当深蓝打败卡斯帕罗夫的时候,计算机已经在国际象棋上打败了人类。在 20 世纪 80 年代,对于业余棋手来说,Radio Shack 牌电脑象棋可能是一个强大的对手,而且有各种技能水平。深蓝的成功是最高的成就,在这一点上,机器游戏完全超过了人类游戏,永不回头。大约 250 年后,我们终于有了一台比我们玩得更好的机器。
最近在围棋、扑克和 Dota 2 等游戏中出现的更多机器征服让最初的深蓝胜利显得过时了。但是有一个重要的区别。因为它发生在 22 年前,我们有 22 年的证据来考虑。深蓝的胜利确实改变了国际象棋,但不是以人们期望或预测的方式。机器和人类如何在国际象棋世界中共存,不仅为我们在围棋、扑克和 Dota 2 等游戏的未来,而且为机器智能将增强或取代人类智能的正常工作和生活环境提供了一个诱人的预览。虽然“深蓝”的技术与现代人工智能几乎没有共同点,但它是人类和机器将如何走到一起的实物课程。
机器比我们好…现在怎么办?
我们采访了认知科学家兼国际象棋大师克里斯托弗·沙布里斯,他在《继续游戏》中讲述了职业国际象棋的场景。华尔街日报的专栏,关于深蓝征服国际象棋以及这项运动如何改变,这要归功于 22 年来人工智能的进一步发展。Chabris 分享了深蓝 1997 年胜利的影响:“很多人预测这不可能发生,或者当时不会发生。所以,我认为卡斯帕罗夫的落选让人很惊讶。…一些人预测,不知何故这会毁了国际象棋,一旦计算机真的擅长某件事,那么人类就会失去兴趣,或者对人类来说,参与这项活动本质上是一项回报较少的活动。事实上,什么也没发生。接下来的一个月,国际象棋世界继续进行。我相信,如果我没有记错我的国际象棋历史,卡斯帕罗夫本人——在被那场比赛的结果明显摧毁,并说了一些他可能不是故意说的或后来并不感到自豪的过激话之后——在同年晚些时候,他回来了,用他有史以来最好的一些游戏赢得了一场强大的特级大师锦标赛。卡斯帕罗夫继续保持了三年多的世界冠军,我认为当他自己退役时,他仍然在排行榜上名列前茅。至少在受欢迎程度方面,国际象棋基本上还在继续。”
当然,今天,深蓝已经不再是世界上最好的国际象棋“棋手”,甚至远非如此。20 年后,你的智能手机拥有比 IBM 为深蓝设计的电脑更好的电脑。“拥有一台可以打败世界冠军的计算机,它被存放在 IBM 的一个研究实验室里,并没有真正改变很多事情,”Chabris 说。“但是让每个人都拥有一台可以打败世界冠军的电脑确实让事情发生了很大的变化。…手机现在被禁止参加国际象棋比赛,因为作弊太容易了。如果你参加锦标赛,如果你能走进浴室,看着你的手机,你就能赢得每一场比赛。”
改变游戏方式
虽然考虑到将智能手机机器象棋偷偷引入竞争可能改变结果的所有方式很有趣,但人工智能已经在更深层次上影响了游戏的玩法。首先,让我们考虑防御策略的领域:“计算机擅长防御,因为它们会检查每个动作。他们不会因为自己处于不利地位、处于守势而受到情绪影响,”沙布里斯说。“他们会考虑每一种可能性,并尽可能长时间地进行分析。通过这样做,他们基本上表明了很多位置是可以防守的…防守比人们想象的更有可能。”
对我们许多人来说,耐心肯定是很难的。虽然人类可能喜欢积极的攻击计划,但计算机可能会采取完全不同的策略,专注于缓慢但稳定的改进,等待对手犯错。“保持压力直到另一个人成为第一个犯重大错误的策略——计算机显示,这些策略比我们意识到的要好,”Chabris 说。
电脑象棋已经影响了顶级游戏和教练。教练使用软件来帮助分析他们的学生和学员的游戏。顶级玩家都必须准备好让他们的对手玩从机器游戏中学来的独特的,甚至是不人道的动作。
Figure 03: Study, Learn, and Advance the Game
[Photo: by rawpixel on Unsplash]
一种新的学习方式
机器象棋已经在许多方面改变了人们学习游戏的方式。在过去,书籍和杂志在一个人提高和掌握这项运动的道路上发挥了重要作用。“国际象棋仍然有大量的印刷文献,”Chabris 说。“事实上,现在出版的国际象棋书籍可能比过去任何时候都多,而且会有更多伟大的国际象棋书籍问世。”但是象棋书籍越来越不重要,因为强有力的玩家可以使用计算机来研究、学习和推进他们的游戏。电脑国际象棋程序可以评估你的游戏,计算最佳的步骤,并为你的游戏做注解。该软件不仅可以指出每一个错误,还可以告诉你错误的严重程度,并告诉你最好的行动应该是什么。“这就像一个真正的好教练,告诉你,‘你应该这样做。“你应该那样做,”沙布里斯说。“这对于改进非常有用,只要学习和看到这一点就行了。”
然而,计算机象棋仍然是一个不完美的系统,至少在人类学习方面是如此。“这并不能解释为什么你应该走那一步,”沙布里斯说。“这并没有解释你将来如何能更好地找到那些更好的动作。它只是说,“这是你的错误。这是你应该做的。…这是人类教练可能会做的事情。我认为最终计算机也能做到。这是一个投入研究努力的问题。“大多数人,大多数时候,不仅仅是通过看到正确的答案来学习。有一个学习的过程。但是,在计算机象棋现在被利用的方式中,完美和实时信息的两步前进伴随着失去最初最能让你学习的过程的一步后退。
在国际象棋软件中添加为什么这一层可能在这一点上需要人类的洞察力,但将人类的视角编织到软件中只是时间、金钱和意愿的问题。国际象棋教育市场可能没有足够的利润来让它在短期内发生,但可以肯定地说,向最终用户提供这种服务的软件没有技术障碍,只是对机器学习生成的分析软件的功能有所限制。
走向人机协作
从更广泛的意义上来说,过去 22 年来国际象棋游戏的演变和国际象棋教育的进程,暗示了人类可能通过与智能机器合作来改变、适应和改进的方式。
在输给“深蓝”加里·卡斯帕罗夫(被许多人认为是有史以来最伟大的棋手)后,他开始接受机器象棋。他开创了一种叫高级国际象棋或半人马国际象棋的游戏,这种游戏涉及人类和计算机的合作。它从未真正流行起来,在更大的国际象棋社区中只是一个小插曲。
“人类的能力和计算机的能力之间的差异在什么时候变得如此之大,以至于将人类插入系统成为一个错误?”夏布利问道。“在早期,我认为卡斯帕罗夫的想法是,在国际象棋中,有些情况人类理解得更好,有些情况计算机理解得更好,因此最佳组合是让人类判断是否听从计算机的建议。”
“随着时间的推移,这一比例一直在缩小……到了你开始冒风险的时候,人类可能会认为他们知道得更多,而实际上他们并不知道——这有点像重写计算机,而事实上计算机会提供做出最佳举措的最佳机会。或者在更危急的情况下,计算机有最好的机会驾驶汽车,或降落飞机,或做出正确的医疗诊断。”
象棋 AI 的教训
从计算机统治国际象棋世界的 22 年中,我们可以学到哪些更广泛的教训?“当计算机在智力活动中变得非常出色时,它不会扼杀这种活动。事实上,对人类来说,它甚至可能变得更有趣、更民主,因为它使高质量性能的可用性更加普遍,”Chabris 说。对 Chabris 来说,这种民主化在很多方面改变了游戏规则。在机器象棋出现之前,只有少数特权阶层能够在幼年时接受高质量的训练。“如今,你甚至不需要有那么多钱或者有一个教练,”Chabris 说。也许有点讽刺的是,人工智能象棋事实上已经为人类铺平了竞技场。
随着电脑象棋的不断进步,现在它已经成为游戏的一个公认的支柱,人们对它的态度也在改变。“‘哦,那是引擎移动’,或者‘那是计算机移动’。”这在国际象棋中是一种侮辱。“计算机移动是一个愚蠢的无意义的举动,计算机出于某种模糊的原因做出了没有任何意义的举动,”沙布里斯说。现在,一个计算机移动意味着一个移动是如此好,但如此不寻常,只有计算机会看到它,看到它有多好。"
当然,从机器互动中学习还有很长的路要走。“拥有一个近乎客观的预言或你认为客观的东西,并不是学习、提高和更好决策的唯一必要因素,”Chabris 说。“人类总是能够忽略,甚至是客观上好的想法、信息、观点等等.”
“也许我们还需要更多地了解如何最好地利用这些工具,”沙布里斯说它们是新的。我们很久没见过他们了。我们需要真正学习如何……更好地使用计算工具,并更多地研究人与计算工具之间的关系。"
这些都是很好的提醒,人工智能自动化不是一件可怕的事情。它不会很快扼杀我们的工作,它需要时间从笨拙走向有效,更需要时间走向最佳。因为,正如我们所看到的,在人工智能和国际象棋中,无论玩家是人类还是机器,国王的游戏仍然是一个诱人的挑战。
Creative Next 是一个播客,探索人工智能驱动的自动化对创意工作者,如作家、研究人员、艺术家、设计师、工程师和企业家的生活的影响。本文伴随 第一季第四集——AI 优势如何改变人类的努力。
为什么 90%的司机可能“高于平均水平”,或者为什么在使用统计数据时需要小心
(Source)
现实世界中的均值、中位数和偏态分布
大多数人看到“90%的司机认为自己高于平均水平”的标题,会想“哇,其他人在客观评价自己方面很糟糕。”你应该想的是“如果我们在一个严重负偏态分布中使用均值来表示平均值,这听起来并不那么难以置信。”
尽管像这样的标题经常被用来说明优越感(人们高估了自己的能力)它也提供了一个有用的教训,在你谈论数据统计时澄清你的断言。在这种特殊情况下,我们需要区分一组值的 平均值和中值 。根据我们问的问题,有可能 9/10 的司机高于平均水平。这里有数据可以证明:
Driver Skill Dataset and Dot Plot with Mean and Median
区别在于,我们是使用平均值还是中位数来表示“平均”驾驶员技能。使用平均值,我们将所有值相加,然后除以值的数量,得到该数据集的 8.03。由于 10 名驾驶员中有 9 名的技能等级高于此,因此 90%的驾驶员可被视为高于平均水平!
相比之下,中位数是通过将值从最低到最高排序并选择一半数据点较小、一半数据点较大的值来确定的。这里是 8.65,下面 5 个司机,上面 5 个司机。根据定义,50%的司机低于中位数,50%超过中位数。如果问题是“你认为自己比其他 50%的司机强吗?”比 90+%的司机不能如实回答肯定。
(中值是百分位数的一个特例,在这个值上,给定的数字百分比更小。中位数是第 50 个分位数:数据集中 50%的数字更小。我们还可以找到第 90 个分位数,其中 90%的值较小,或者第 10 个分位数,其中 10%的值较小。百分位数是描述数据集的直观方式。)
为什么这很重要?
这可能看起来是一个人为的例子或技术细节,但平均值和中间值不一致的数据在现实世界中经常出现。当值对称分布时,平均值等于中值。然而,现实世界的数据集几乎总是有一定程度的偏差,无论是正的还是负的:
Positive, symmetric, and negative skews. (Source)
在正偏态分布中,平均值大于中位数。当图表上端相对较少的异常值使图表向右倾斜,而大多数值聚集在较低值时,就会出现这种情况。真实世界的情况是个人收入,平均收入明显大于中值收入。下图显示了 2017 年美国的收入分布,以直方图的形式显示为 100 个百分点。
Income distribution in the United States, a positively skewed distribution
具体数字因来源不同而有所不同(该数据来自这里),但总体模式是清晰的:少数高收入者将图表向右倾斜(正值),使平均值高于中位数。价值 55880 美元,平均值接近第 66 百分位。解释是 66%的美国人收入低于平均国民收入——当平均收入被认为是平均数时!这种现象几乎在每个国家都会发生。
负偏态分布的一个例子是死亡年龄。不幸的是,在这个数据集中,有一些人在相对年轻的时候死亡,降低了平均值,并使图表向左倾斜(负向)。
Age at death in Australia, a negatively skewed distribution (Source)
在负偏斜的情况下,中值大于平均值。结果是,当平均值被定义为均值时,更多的人可以在“平均值”之上。“大多数人比平均寿命更长”可能是一个奇怪的标题,但如果你仔细选择你的统计数据,你就可以让它成真。
大多数涉及人类行为的数据集都表现出某种偏斜。股市回报、收入、社交媒体关注者、战斗死亡和城市规模都是高度偏斜的分布。在反脆弱 , 纳西姆·塔勒布将这个扭曲结果的世界描述为极端主义。我们在 mediocristan 进化,所有的数据集都是正态分布的,但我们的现代生活现在被不平等分布所主导。生活在极端的地方有机会获得难以置信的回报,但这些只会发生在极少数人身上。这也意味着我们在谈论作为数据集“平均”表示的均值和中位数时必须小心。
DIstribution of Followers (source) on social media, a positively skewed distribution.
( Zip 定律和其他幂定律也会产生偏态分布。)
结论
要记住的一点是,当你指定“平均值”时,你需要澄清你是在说平均值还是中位数,因为它有区别。世界不是对称分布的,因此,我们不应该期望分布的平均值和中位数是相同的。
机器学习可能会得到所有的关注,但数据科学真正重要的部分是我们每天使用的部分:帮助我们理解世界的基本统计数据。能够区分平均值和中值可能看起来很平常,但这比知道如何建立神经网络更与你的日常生活相关。
一如既往,我欢迎反馈和建设性的批评。可以在 Twitter @koehrsen_will 上找到我。
博弈论——解释了极大极小算法
1997 年,一台名为“深蓝”的计算机击败了国际象棋世界冠军加里·卡斯帕罗夫——这是人工智能理论史上的一个决定性时刻。
但是国际象棋计算机问题背后的伟大头脑在近 60 年前就已经开始发表这方面的文章了。被称为现代计算机科学之父的艾伦·图灵被认为是引发了追溯到 20 世纪 40 年代的调查。
1997 chess match between world champion Garry Kasparov and IBM computer “Deep Blue”
国际象棋计算机问题被考虑的时间跨度之大,证明了解决方案的复杂性。教科书已经被写在计算机象棋问题上,并且许多复杂程度不同的策略已经被测试。然而,本文将关注计算机在各种战略游戏中使用的一种通用决策策略,包括国际象棋、跳棋、曼卡拉棋、井字游戏等等。
这个一般策略包含在博弈论中广泛使用的算法中,称为极大极小算法。本文将简要介绍计算机如何使用极大极小算法来决定下一步行动,但首先我们需要定义一些东西:
游戏树
在计算机科学中,树是指一种嵌套的数据结构,其中我们从一个“根”节点(第 0 层)开始,从这个根节点开始分支,我们可以有任意数量的“子”节点(第 1 层)。假设这些“子”节点分支成“孙”节点(第 2 层)。现在我们可以说,从第 2 层节点的角度来看,第 1 层的节点是“父”节点,第 2 层的节点是“子”节点。
A schematic of a binary tree from the perspective of Level 2
直观地说,我们可以看到这种数据结构有无限多层次的潜力。在棋盘游戏中,我们可以将根节点(第 0 层)视为棋盘的当前状态,而第 1 层的节点则视为棋盘的每一种可能状态,这取决于下一步该怎么走。换句话说,1 级节点负责每一个可能的下一步行动。
展望游戏的未来
在多人游戏中,我们可以想象一个游戏树,其中的根节点是对手 B 走一步后棋盘的当前状态,轮到对手 A 了。因此,第 1 层包含代表对手 A 可能的移动的节点,第 2 层包含代表对手 B 在第 1 层中可能进行的每一次移动的节点。
如果我们继续这样做足够长的时间,我们完全可以描绘出游戏的未来。在某种意义上,上面的示意图过于简单,对手在任何给定的回合中只有 3 种可能的移动。通常情况下,例如在国际象棋中,可能的走法数量会非常非常多,导致我们的博弈树突然变得复杂。事实上,国际象棋的平均分支系数约为 35。
效用
效用可以被认为是一种根据每一个可能的移动可能导致的胜利来“打分”的方法。效用如何计算完全取决于程序员。它可以包含各种各样的因素,并在程序员认为合适的时候对它们进行权衡。例如,棋盘上空格的数量,对手当前棋子的位置,我们当前棋子的位置,我们离胜利的阵型有多近,等等。所有这些都可能是在计算某一特定移动的效用时要考虑的因素。让我们以井字游戏为例,它可以有相对简单的效用度量。下图显示了游戏中途的井字游戏棋盘,其中有一个非常简单(可能不是最优)的效用规则。我们可以看到,轮到了 X ,只有 3 个可能的移动,因此,有 3 个子节点。对于每个可能的移动,使用下面的效用规则计算效用。用简单的英语来说就是:
“对于每一行、每一列和每一条对角线,如果我们在一行中有 3 个 X,我们就赢了这场游戏,应该分配 1.0 的效用。如果我们有一行、一列或对角线上有 2 个 X 和 1 个空白正方形的场景,我们的效用应该是发生这种情况的场景数的 0.2 倍。”
我们可以看到获胜的棋盘具有最大的效用,所以我们的效用规则并非完全无用,尽管它肯定会受益于“防御”成分,这将考虑 O 离获胜有多近。
接下来,我们将把这些碎片放在一起,看看计算机如何“思考”它的下一步行动。假设轮到电脑了。决定下一步走哪一步的一个可能的方法是简单地计算每个可能的下一步的效用,并选择效用最高的一步。这通常是普通人在玩棋盘游戏时的策略,当然,游戏也可以通过这种方式获胜。
但是大师和普通人的区别在于他们有能力提前思考几个步骤。事实证明,计算机做这件事的效率比最好的国际象棋大师还要高。以下是如何…
极大极小算法
在开始之前,我们将对我们的游戏做两个假设:
- 人类玩家应该在最佳状态下比赛,或者努力获胜。随意移动或试图失败实际上可能会干扰算法的有效性。
- 该游戏必须是纯战略性的,不能包含任何形式的机会成分(即大富翁、扑克、俄罗斯方块)。注:该算法的变体可用于说明“运气因素”。
该算法的前提是,计算机将通过评估棋盘在几个回合后的效用来计算下一步最佳棋。在这样做的时候,计算机假设对手总是选择最好的移动,最小化计算机的效用。当然,这不是一个安全的假设,但你瞧,不管怎样,它往往会很好地工作。
需要注意的一点是,当我们说“效用”时,我们总是从计算机的角度来指效用。例如,当人类玩家做出最好的移动时,我们说该回合的效用最小化。
该算法包含三个基本函数:最大化和最小化,以及一个效用计算函数*。*伪代码看起来像这样:
现在没必要太纠结于细节。该示意图的两个关键要点是:
- 该算法是递归的,因为最大化调用最小化,而最小化调用最大化。
- 我们有打破递归循环的条件。例如,如果我们到达我们搜索空间中的一个节点,其中有人赢得了游戏,棋盘已满,或者最常见的情况是,我们已经达到了预定的深度限制,就会发生这种情况。
回到轮到计算机的场景,计算机将调用当前棋盘上的最大化功能。这将在棋盘的每个子棋盘上调用最小化,这将在每个孙棋盘上调用最大化,以此类推…
该算法执行我们在计算机科学中称为“深度优先搜索”的操作。这意味着它主要沿着树的整个长度垂直遍历*,直到到达终端节点,然后再向上返回。其次,该算法在其他兄弟节点之间水平移动*、或。这与“广度优先搜索”形成鲜明对比,后者的作用正好相反——它首先在兄弟节点之间水平移动,一次搜索整个“层”,然后沿着树向下搜索。下面的示意图有助于说明这一概念:**
极大极小算法以深度优先的方式沿着树向下移动,直到它到达终端节点(即某人赢得游戏)或预定的深度限制。深度限制是为涉及复杂搜索空间的游戏设置的,在这种情况下,在合理的时间内搜索整个可能的移动网络是不可行的。一旦到达终端节点或深度限制,调用效用计算函数,计算出该特定端子板的最终效用值。
然后,该效用值被“向上传递”到父节点,在父节点处,该效用值与父节点处的当前效用值进行比较(如果已分配)。如果父节点处于最大化循环、中,如果终端节点效用值大于父节点处的当前值或*,则该终端节点效用值替换父节点处的效用值,父节点尚未被分配效用值。如果父节点在最小化循环中,则相反。*
直觉上,我们可以思考这个循环是如何反复递归发生的,直到我们能够用效用值填充下一个移动节点(第 1 层)。这些是允许计算机做出决定的。下面的剪辑可能有助于形象化这个概念。
Note: The “best” next move returned by the algorithm is the move that corresponds to the utility value passed all the way up the tree to the root node (current state of the board).
但是我们实际上在做什么,这如何帮助计算机做出决定?计算机实际上应用了以下逻辑:
你的下一步棋只有对手的下一步棋弱的时候才算强。
从表面上看,我们也许能够识别出什么看起来像是一个强有力的行动;然而,如果这个强有力的移动导致对手更强有力的移动(击落我们的设施),那么我们最初的移动真的很强吗?
这是算法背后的思路,应用于我们的树的几个层次。我们计算未来某处可能的移动的效用,并决定这些效用是否应该代表我们当前的移动选项。如果对手(总是试图最小化效用)很可能会采取所有的行动将我们引向所述未来点,我们应该将该特定的未来效用值沿树向上传递,以表示我们当前的决策节点。
这就是它的要点。当我们想到相对复杂的游戏,有巨大的搜索空间和各种各样的策略时,如果计算机能够预测未来,我们就很幸运了。但是历史一次又一次地表明,只要我们的效用规则是有效的,这种总体策略仍然比单独的人类脑力更有效。
国际象棋是一种复杂的游戏,具有相当复杂的效用度量和巨大的搜索空间。将上述逻辑应用于国际象棋比赛可能足以让你头晕目眩,这就是为什么这个问题花了几十年(可以说是几十年)才得以解决。但是,编写国际象棋计算机程序的基本原则与上面简化的例子基本相同——观察未来可能的走法,决定这些走法有多好,并预测对手是否会采取所有正确的走法来引导你到达那里。
数据科学家如何买车
在荷兰生活和工作多年后,我和我的家人是时候搬回美国,用自行车换汽车了。
在美国没有车是很难生活的,因为我们在移居国外之前已经卖掉了我们的车,所以我们现在想买一辆新车。我决定像任何优秀的数据科学家一样,通过使用数据来解决这个问题。
获取数据
很多时候,数据科学项目最困难的部分是获取数据。尤其是如果只是一个副业的话。很可能你无法从网上下载 CSV 文件,没有任何数据,你就无法建立任何模型或做出任何预测。
网页抓取拯救
Developer tools in google chrome
网络抓取是以自动化的方式从网站获取数据的过程。我不是网络抓取的专家,这篇文章也不是设计成如何抓取网站的教程。但是通过一点点实践和理解某些 python 模块是如何工作的,您可以实现您最疯狂的数据幻想。
在这个项目中,我使用了一个叫做 Selenium 的 python 包,这是一个无头浏览器。这意味着它会打开一个网页,并以类似于真实用户的方式与之交互(与 BeautifulSoup 之类的只是读取 HTML 代码的东西相反)。
Google chrome 的开发工具使这个过程变得非常简单,因为你可以右键单击网页中你感兴趣的项目,并轻松复制 xpath、element_id 或任何其他你想要的内容,所有这些都与 Selenium 包配合得非常好。有时需要一点尝试和错误来找到您真正需要的元素和文本。
你可以在这里找到我用来刮几百辆车的代码。
清理数据
网络抓取数据的一个问题是,最终结果很可能非常混乱,需要你做大量的清理工作,因为你受网站设计的支配,网站设计是为了呈现信息而不是数据存储。很多时候你需要解析字符串来得到你想要的东西。
在将我的 web 抓取过程的结果保存到 CSV 之后,我现在可以将数据加载到 pandas dataframe 中并开始清理了。
随着 lambda 和 regex 函数的大量使用,我可以开始提取有用的信息,并开始创建特性来探索和建模。
首先,我创建了一个可重用的正则表达式函数,可以将它传递给。应用熊猫法。
A reusable regex function
“name”列有很多有用的数据,我可以使用我创建的函数从中创建四个新的特性。
我现在得到了一个类似这样的数据帧:
探索数据
现在是时候开始做一些更有趣的事情了,那就是探索数据。这是数据科学过程中至关重要的一步,这样您就可以开始了解您的数据及其趋势和相互作用。价格将是我的目标变量,所以我在设计我的图表时会考虑到这一点。
这些箱线图在我的一些分类变量中显示了非常清晰的信号,表明根据车辆的年份和类型,价格有很大的差异。
同样,我发现我的一个解释变量(里程)和目标变量之间有很强的相关性。随着里程数的增加,价格下降,两者之间有很强的负相关关系。知道了我们对汽车世界的了解,这很有意义。
通过查看我们的一些解释变量和目标变量之间的关系,我们可以开始很好地了解哪些特性将是重要的,以及我们以后应该使用哪种类型的模型。
编码分类特征
建模前的另一步是对我的分类特征进行编码。*【4wd】和【认证】已经是布尔特征,不再需要任何预处理,但是【类型】*和【年份】应该被编码。我用的是熊猫一热编码法(。get_dummies)来实现这一点。
Pandas 足够聪明,允许我传递所有的特征,它将只对分类特征进行编码,并允许我选择删除一个新创建的列,以避免完全相关的特征。这将导致我的模型输出和解释出现错误。(请注意,只有两个年份列,因为 year_2015 已被删除。同样适用于“类型”栏
我的数据现在是我可以建模的格式。
建模数据
从上面的图表和汽车行业的先验知识来看,线性模型似乎很可能会很好地处理这些数据。
为了对此建模,我使用了 OLS(普通最小二乘法)线性回归模型和 statsmodel python 包。
This model resulted in a RSME of 1556.09
所得到的系数与探索性分析期间图中显示的故事相一致。里程与价格成反比。平均每增加 1000 英里,车辆价格就会降低 1600 美元(这适用于 GMC Yukons,可能并非所有车辆)。可能最令人震惊的数字是 SLE(Yukon 的最低等级)和 Denali(Yukon 的最高等级)之间的差异超过 9000 美元。这意味着你最好真的喜欢你从更高阶层获得的额外功能和奢侈品,因为你肯定为此付出了代价。
综合考虑,我应该买哪辆车?
到目前为止,我已经收集、清理、解析、探索、可视化和建模了这些数据。但问题仍然存在,我怎么知道买哪辆车?我如何知道我是否得到了一笔好交易?
方法很简单,使用模型进行预测,并计算每辆车的预测价格和要价之间的差异。然后根据差异对列表进行排序,在顶部显示最被低估的工具。这应该给你的车辆是最好的交易。
在优化了一些其他约束条件(例如,我的个人预算和我妻子的偏好)后,我能够创建一个排序列表,并开始给经销商打电话。
The car we bought is highlighted in blue
我们最终以 32,000 美元的价格买了一辆 2015 款 GMC Yukon Denali,比我的模型预测的价格便宜了大约 3,000 美元。
虽然有一些潜在的更好的交易,我们决定购买的汽车是本地的,使整个过程更容易和更快。考虑到我们当时根本没有车,轻松和快捷对我们来说是一个重要因素。
最后,我们现在可以重温之前的情节,从视觉上确认我们确实得到了一笔好交易!
我妻子非常喜欢她的新车,我对它的价格也很满意。总的来说,这是成功的。
数据科学家如何购买延期保修
深入了解“延长保修”的数学原理
这是我关于利用数据买车的帖子的“第二部分”。
如果你还没看过,那就来看看 这里 。
简介
我们都经历过。我们刚刚承诺购买那辆闪亮的新车,而且我们即将毫发无损地把它开出大楼。
但是,等一下,在我们在虚线上签字并骑行到日落之前,我们还要与“财务”人员再开一次会*(也就是一长串推销的最后一行)。
他们将为您提供一整套延长保修选项,以保护您的新虚拟资产。* 这些是汽车经销商最大的赚钱者,令人惊讶的是有大量的数学数据支持它,这就是我们打算探索的。
一个简单的案例
假设你正坐在当地汽车经销商的财务办公室里,准备完成一笔交易。
你的轮胎获得了延长保修,每月只需花费 7 美元。
它涵盖了从你在车道上碾过一个钉子并需要一个补丁到完全爆胎需要一个全新轮胎的所有事情。
你应该接受这笔交易吗?值得吗?对经销商有什么好处?
这是一个概率游戏,为了简单起见,我们将使用一些相当合理的预期概率,这些概率是我经过数秒钟的思考后得出的。比方说,一年中有 5%的几率轮胎完全爆裂,30%的几率只需要修补。假设更换一个轮胎的平均成本是 200 美元,补片的平均成本是 20 美元。
这对代理商有什么好处?
为了回答经销商为什么会提供这笔交易以及他们期望从中赚多少钱的问题,我们可以开始用数学公式来表示。
假设经销商一年销售 1000 份延长保修。
为了计算出经销商将产生多少成本,我们可以使用以下基本概率概念,其中 E(x)是经销商需要维修的预期车辆数量。
通过一些基本的运算,我们现在可以得到下表:
因此,在一年内,经销商预计从 1000 笔此类交易中获得 68,000 美元的利润***(难怪销售人员如此咄咄逼人)* 。**
但到目前为止,这不是很有趣,看起来更像会计而不是数据科学。
这是数据科学家开始考虑概率分布、均值方差、模拟、极端情况、数学关系、拐点等等的地方。
但首先是一些来自统计课的回顾。
这个,
Binomial distribution
是一个分布(确切地说是二项式分布)。分布有各种形状和大小。有些有定义它们的名称和功能,有些没有。
你如何解读这种分布?
y 轴上的值代表观察到 x 轴上对应值的概率。
我意识到令人困惑。
最容易理解的方法就是通过一个例子。
假设你掷一枚公平硬币** 100 次*(公平硬币的意思是:正面或反面的概率是 0.5)。*
你觉得你会得到几个人头?当然,预期的头数是 50。
但你我都知道,你完全有可能得到 54 或 43 或 61 分。
但是 95 头呢?这可能吗?肯定是可能的,但是可能吗?**
如果你将一枚硬币抛 100 次,得到 95 个正面,常识将会起作用,你会认为这枚硬币必须有重量。你的担心是正确的,因为在 100 次抛硬币中获得 95 个正面的概率是 5.939138117904783 e-17如果你不能将这个科学数字转换为人类数字,别担心,它真的很小。 (翻译:大概不会发生)。
这个概率在二项式分布图中表示。
我现在加了一个橙 X* 代表获得 95 个头的概率。
看到 x = 95 时 y 轴上的蓝线有多低了吗?
大约就是 0。这告诉我们,基本上 100 次翻转中有 95 次正面朝上的概率是 0 %(从技术上讲,这种情况可能发生,但极其罕见)。***
谁在乎硬币呢?让我们来谈点真实的东西吧!
如果我们用一个更真实的例子来代替掷硬币的例子,会怎么样呢?).
为了解决这个问题,我们将使用一种不同的分布,即二项式分布的派生物— 泊松分布**
为什么使用泊松分布而不是二项式?
不需要太多的细节,我们可以用它们来回答相同类型的问题,但是在这个特殊的例子中,泊松分布比二项式给了我们一些自然的好处。主要是因为它不是二进制的,因此我们不局限于每个时间段只有一个 0 或 1。
为了证明这一点:
如果我想将我的时间段定义为一年,那么一名车手完全有可能出现不止一次爆胎或补胎。泊松分布允许我建模,而如果我使用二项式分布,那么我需要缩短我的时间周期,这样就只有一个可能的井喷或补丁。这可能是不可能的,或者至少不太容易,因为你可能会违反模型中的一些假设。这些原因使得泊松分布成为“罕见事件”建模的更自然和更常见的方法——如补丁和井喷。
如果你想了解更多关于泊松和二项分布有多么相似,这里有一篇很棒的文章。**
现在回到我们的问题。
问题 1:给定补丁(0.3)和井喷(0.05)的概率,
车行未能盈利的概率有多大?
在进行任何分布建模之前,我们需要了解作为利润函数的补丁和井喷之间的关系。之前,我们计算了汽车经销商的预期利润,结果是 68,000 美元。现在我们要问的是,所有利润被成本吞噬的可能性有多大?首先,让我们看看一家汽车经销商需要进行多少次修补和爆胎维修才能达到收支平衡。这可以通过下面的等式来定义。
84,000 is the expected revenue
然后,我们可以重新排列等式,并绘制下面的线。
粉色阴影区域显示了代理商可能会亏损的空间。给这个已经很棒的图添加一些有趣的东西是当前的期望值。
两条黑色实线显示了漏白数(垂直线)和补片数(水平线)的期望值,两者的交点显示了联合期望值。
因此,我们已经开始看到,平均而言,代理商不会处于粉色区域,因此他们不会亏损。
但是就像硬币的例子一样,我们知道期望值不一定是现实生活中发生的。
那么,经销商有可能赔钱吗?(有没有可能我们会在粉色阴影区域结束?)
一探究竟。
这就是我们的老朋友泊松分布出现的地方。**
PMF of the Poisson Distribution
上面的函数是泊松分布的概率质量函数(废话连篇的数学废话——这只是意味着如果我们插入数字,我们就会得到概率)*
让我们以下面的例子为例:***
p = p(爆胎)= 0.05
n =售出保修数量 x 每辆车轮胎数量= 1000 x 4
x = 210
一年中正好有 210 次井喷的概率是 0.0215。
Probability of getting exactly 210 blowouts in a year
这个数字可能看起来很低,因为 210 非常接近预期值 200,但请记住这是得到确切的* 210 井喷的概率。如果我们想回答至少得到210 次井喷的问题,那么我们可以简单地计算橙色柱右侧每个值的概率,然后将它们相加。
(专业提示:鉴于这个分布没有真正的上限,通常更方便的做法是将橙色条左边的所有概率相加,然后从 1 中减去它们)。
数学上看起来是这样的:***
如果我算出这一点,那么至少发生210 次井喷的概率将是 0.249 ,从图形上看,这一概率由下面的橙色阴影区域表示:****
See how the orange shade is about 25% of the area under the curve?
用简单的英语来说,这是什么意思?
大约有 25%的可能性,代理商一年内将不得不修理至少 210 次爆裂。
现在让我们展开
由于爆裂只是代理商必须支付的一种方式,我们需要对补丁做同样的事情。当我们得到两个概率时,我们可以创建一个联合概率,这意味着我们把它们相乘。
这在数学上可以写成:**
假设我们对一年内至少 210 个井喷和至少 1500 个补丁感兴趣。得到的联合概率是 0.000498。**
再说一遍,这用通俗的英语来说是什么意思?
基本上,经销商一年内不得不修理至少 210 次爆裂和至少 1500 次修补的可能性不到 0.01%。
但是为了真正开始讲述一个故事,我们需要对补丁和井喷的所有组合都这样做。
我们可以将我们之前创建的利润边界两侧的这些联合概率相加,以计算出代理商亏损的总概率。
在实践中,我将使用上面的泊松公式,计算在白色区域中所有组合的精确联合概率(蓝线以下的一切),然后从 1 中减去该总和。**
为了形象化这一点,我创建了一个热图,显示每个组合的联合概率。
Heatmap: P(blowout)P(patch)
画面开始变得相当清晰。由于所有的密度都在蓝线的“赚钱”一侧,代理商似乎不太可能会赔钱。
但为了确保万无一失,我们将对利润边界左侧的所有值求和,并从 1 中减去总和。
结果是 4.000000330961484e-10。
耶……我认为他们在这些交易中不会亏钱。
问题 2:作为客户,我应该购买延保吗?
这真的是我们都关心的问题。
刚买了车,该不该买质保?
我们刚刚证明了这对代理商来说确实是一笔好交易,但这并不一定意味着对您不利(生活中有些事情是互惠互利的)。
所有的数学计算都是多余的,因为我们实际上是在问和以前一样的问题,只是时间倒过来了,空间小了很多。所以我们将跳过细节。
只购买一份保修服务,你需要付出什么才能让你的钱物有所值?
我们需要做的就是计算出多少爆裂和补丁会让你的成本(如果你没有购买保修)超过保修的成本**
为了解释这个图表,作为一个例子,你可以说,如果我有至少 3 个爆裂和至少 0 个补丁,那么我应该购买保修。但是发生这种事情的可能性有多大呢?
我现在可以应用我的老朋友泊松分布公式来得到与每个概率相关的概率,然后将它们加在一起。**
你赔钱的最终概率是 0.00144
最终翻译:
你或许可以跳过保修
这种方法的美妙之处在于,你可以根据自己的需要调整概率,自己动手计算。
老实说,现实生活比我在这里介绍的要复杂得多。还有无数的其他变量可以考虑。
可能你开车比一般人多。也许你经常在建筑工地或土路附近开车。也许你是一个比大多数人都糟糕的司机,会在路上撞倒东西。
我的观点是,可能会有这样的情况,购买保修是值得的,因为你发生这种事件的概率高于平均水平(或者你不喜欢钱——这也是一个有效的理由)。但是我想我们已经证明了,总的来说,你最好不要在最后的账单上提供保修。**
生活小贴士
生活提示数字 1我知道你在想什么。
我的房子被飓风摧毁的概率有多大?我不记得堪萨斯的最后一场飓风了。这证明我的国营农场代理人一直在敲我的竹杠。 踩刹车一秒钟(双关语),一个好的经验法则是问自己
自我,最极端的灾难性事件对我的生活有重大影响吗?
如果答案是肯定的(比如你的房子被烧毁)那么很可能值得你花钱投保。
但是如果答案是否定的(你有一套公寓)那么也许不值得。
生活小贴士第二条 当你面临抉择,你不确定自己是否被骗了,而且这件事不会改变你的生活时,你可以用这个快速的“信封背面”计算来决定你是否应该买。
也就是说,如果期望值小于成本,那么你就通过了。
感谢阅读!
我希望你在延长保修的冒险中取得成功,并记住下次你买车并面临购买保修时,只需要求将 lambda 参数插入到你的泊松分布计算器中。如果你想独自跟随,你可以在这里找到 Jupyter 笔记本。
额外问题:当你改变概率时会发生什么?
这个问题真的会把我们送进兔子洞,因为可能会发生大量的变化和可能性。但我认为很明显,如果补丁和/或井喷的概率增加,这将使我们的联合概率热图上的黑暗区域越来越接近盈利边界。但是经销商当然不会吃亏,他们只会提高保修的价格。如果每月花费 8 美元而不是 7 美元,您会动摇而不购买它吗?大概不会。**
为了证明这一点,我将给你们留下一个有趣的图表,它展示了井喷的概率和泊松期望之间的关系。随着井喷的可能性增加,一年中预期的井喷次数越来越接近经销商在赔钱之前可以处理的最大井喷次数,在某一点上,它实际上跨越了几乎保证经销商会赔钱的界限,这当然会迫使他们提高价格。
Distributions with varying values of P(blowout)