汇率对公司的影响
汇率风险可能是大公司最头疼的问题之一。
从投资者的角度来看,理解汇率变化对公司财务的影响非常重要。在本帖中,我们将首先了解汇率如何影响公司利润。然后,我们将用 Python 分析并绘制多种货币汇率。
汇率如何影响公司
在分析一家公司时,汇率是一个需要考虑的重要因素。一家公司可能以美元作为记账本位币(即公司准备财务结果时使用的货币),但也有业务(如销售、贷款、供应商等。)换一种货币。
在提交财务结果时,公司必须区分货币和非货币资产/负债:
- 货币性资产/负债是那些随时可以转换为现金的资产。这些资产在报告日从其他货币重新转换为公司的功能货币。重估的任何收益或损失都会影响公司的利润。例如,应收账款、应付账款、现金余额和贷款被视为货币资产/负债。
- 非货币性资产/负债是指在报告日没有重新折算的资产/负债。它们在财务报表中使用历史汇率(即购置资产时使用的汇率)列示。例如,存货、PP 和无形资产被视为非货币性资产。
汇率对应付账款的影响
应付账款是公司欠其债权人或供应商的金额。为了理解汇率对公司应付账款的影响,让我们以 ABCD 公司为例。
假设 ABCD 公司以美元报告他们的数字:
- 从欧洲供应商处购买 10,000€的材料。
- 交易日的汇率为欧元兑美元= 1.10。这意味着需要 1.10 美元来购买 1€。
- 这笔货款只能在 4 个月后支付
在购买时,ABCD 公司在资产负债表中将 11,000 美元(10,000€ *1,10)显示为应付账款。
假设三个月后,当 ABCD 公司准备财务报表时,欧元兑美元汇率从 1.10 下跌到 0.90。这意味着我们只需要 0.90 美元就可以买到 1€。由于汇率变化,公司 ABCD 底线将受到影响。现在,只需要 9000 美元(10000€* 0.90)就可以偿还最初的 10000€。
因此,ABCD 公司将把资产负债表中的应付账款减少 2,000 美元,并通过未实现收益增加利润 2,000 美元。
汇率对贷款的影响
同样,如果一家公司有一笔以外币计价的贷款,并且汇率波动,将会对公司的损益产生影响。
假设 ABCD 公司有 10,000€贷款。当欧元兑美元汇率为 1.10 时,该公司在资产负债表中将 11,000 美元显示为贷款。然而,当欧元兑美元汇率从 1.10 升至 0.90 时,ABCD 公司将在资产负债表中报告一笔仅为 9,000 美元的贷款。与此同时,它将在损益中报告 2000 美元的未实现收益。
一旦贷款偿还给银行,收益就会实现。
对销售汇率的影响
很有趣,对吧?根据前两个例子,当美元对其他货币升值时,以美元报告的公司似乎总是会受益。
这并不总是对的。公司向货币贬值(即贬值)的国家销售会降低它们的竞争力。一个公司失去竞争力的影响可能会转化为销售量的显著下降。
想象一下 ABCD 公司在美元对欧元升值的情况下。现在,一家欧洲公司需要支付更高的欧元价格从 ABCD 购买 1 件产品。这可能导致欧洲公司停止从 ABCD 公司购买,并在欧洲寻找替代供应商。
使用 Python 的汇率
现在我们明白了为什么在分析一家公司时汇率如此重要。接下来,我们来学习如何用 Python 计算和绘制汇率。
我们将构建一个 Python 脚本来检索多种货币的历史外汇数据,并使用 matplotlib 绘制它们。我将使用一个免费的财务 API, financialmodelingprep ,提供各种各样的免费财务数据。
在下面的链接中,可以找到所有有数据的货币。对于我们的示例,我们将提取以下四种货币的汇率:
首先,我们导入所有 require 包,并向 API 端点发出 get 请求,返回过去几年的历史汇率。请注意,我们遍历了每种货币。在每个循环中,我们得到单一货币对的历史价格。
import requests
import pandas as pd
import matplotlib.pyplot as plt
exchange_rates_Python = {}
currencies = ['EURUSD','CHFUSD=X','AUDUSD','GBPUSD']
for currency in currencies:
forex = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/forex/{currency}')
forex = forex.json()
print(forex)
#outcome example of USDGBP:
{'historical': [{'adjClose': 0.8029,
'change': -0.00032,
'changeOverTime': -0.0004,
'changePercent': -0.04,
'close': 0.8029,
'date': '2020-04-10',
'high': 0.80324,
'label': 'April 10, 20',
'low': 0.80076,
'open': 0.80258},
{'adjClose': 0.8069,
'change': 9e-05,
'changeOverTime': 0.00011,
'changePercent': 0.011,
...
响应包含一个带有历史价格的字典。请注意,关键字 historical 包括过去几年所有收盘价的列表。因此,我们可以对其进行循环,以便解析每个货币对的日期和调整关闭。我们将这些值存储在一个名为 exchange_rates_Python 的空字典中:
import requests
import pandas as pd
import matplotlib.pyplot as plt
exchange_rates_Python = {}
currencies = ['EURUSD','CHFUSD=X','AUDUSD','USDGBP']
for currency in currencies:
forex = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/forex/{currency}')
forex = forex.json()
exchange_rates_Python[currency] = {}
for item in forex['historical']:
adj_close = item['adjClose']
trade_date = item['date']
exchange_rates_Python[currency][trade_date] = adj_close
最后,我们将字典转换成 Pandas 数据框架,以便使用 matplotlib 绘制数据。此外,我们将索引(即日期)转换为日期时间对象:
import requests
import pandas as pd
import matplotlib.pyplot as plt
exchange_rates_Python = {}
currencies = ['EURUSD','CHFUSD=X','AUDUSD','GBPUSD']
for currency in currencies:
forex = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/forex/{currency}')
forex = forex.json()
exchange_rates_Python[currency] = {}
for item in forex['historical']:
adj_close = item['adjClose']
trade_date = item['date']
exchange_rates_Python[currency][trade_date] = adj_close
currencies_df = pd.DataFrame.from_dict(exchange_rates_Python, orient='index')
currencies_df=currencies_df.T
currencies_df.index = pd.to_datetime(currencies_df.index)
用 Python 绘制汇率
现在我们有了一个漂亮的熊猫数据框架中的汇率,我们准备绘制它们。我们将使用库 matplotlib 为每种货币创建一个子图。
我们将只绘制最近 90 天的汇率:
#List of symbols https://financialmodelingprep.com/api/v3/quotes/forex
import requests
import pandas as pd
import matplotlib.pyplot as plt
exchange_rates_Python = {}
currencies = ['EURUSD','CHFUSD=X','AUDUSD','GBPUSD']
for currency in currencies:
forex = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/forex/{currency}')
forex = forex.json()
exchange_rates_Python[currency] = {}
for item in forex['historical']:
adj_close = item['adjClose']
trade_date = item['date']
exchange_rates_Python[currency][trade_date] = adj_close
currencies_df = pd.DataFrame.from_dict(exchange_rates_Python, orient='index')
currencies_df=currencies_df.T
currencies_df.index = pd.to_datetime(currencies_df.index)
#take last 30 days
currencies_df = currencies_df.iloc[:90,:]
fig, axes = plt.subplots(nrows=2, ncols=2)
currencies_df[currencies[0]].plot(ax=axes[0,0])
axes[0,0].set_title(currencies[0])
currencies_df[currencies[1]].plot(ax=axes[0,1])
axes[0,1].set_title(currencies[1])
currencies_df[currencies[2]].plot(ax=axes[1,0])
axes[1,0].set_title(currencies[2])
currencies_df[currencies[3]].plot(ax=axes[1,1])
axes[1,1].set_title(currencies[3])
plt.tight_layout()
plt.show()
包扎
只需几行代码,我们就能够用 Python 检索和绘制汇率。
我们可以看到,在过去的三个月里,美元比英镑要强劲得多。在 2019 年 12 月,我们需要大约 1.33 美元来购买 1,然而,在 2020 年 4 月,我们只需要 1.22 美元来购买 1。
这意味着拥有大量未结应付款项或贷款的公司比 4 个月前更有能力偿还这些款项。然而,如果英国公司由于货币升值而决定不购买美元产品,销售可能会受到影响。
正如我们在这篇文章中看到的,在做出投资决策之前,了解一家公司的货币敞口非常重要。我建议你从货币敞口的角度来分析公司的销售、贷款和应付款。
如果你喜欢这篇文章,可以随意看看我在 Python for Finance 上的其他帖子。
原载于 2020 年 4 月 30 日https://codingandfun.comT22。
正则化对深度神经网络的影响
使用 TensorFlow 2.0 强调重量衰减
一个高方差的模型(图片由作者提供)
简介
在这个信息高速公路的时代,我们周围的世界正在向好的方向变化,可以毫不夸张地说,深度学习是下一次变革。深度学习是一套强大的数学工具,使我们能够表示、解释和控制我们周围的复杂世界。
编程范式正在改变:我们不再给计算机编程,而是教它学习一些东西,它做我们想做的事情。
这个特殊的概念非常吸引人,并驱使机器学习实践者开发模型,以进一步发展这些概念和想法,并将它们应用于现实世界的场景中。
然而,建立复杂的机器学习模型的基本问题是如何使架构不仅在训练数据上,而且在测试数据上,即在以前看不到的特征上做得好。为了克服这个中心问题,我们必须通过应用某些策略来修改模型,以减少测试误差,这可能是以增加训练误差为代价的。这些策略统称为正则化技术。然而,只有当模型遭受 高方差 问题时,正则化才是有用的,在该问题中,网络过度适应训练数据,但是不能概括新特征(验证或测试数据)。
L2 范数或重量衰减
最广泛使用的正则化策略之一是利用 L2 范数。这可以通过向网络的成本函数添加正则化项来实现。
带有正则项的成本函数(作者要点)
等式的第一部分对应于净网络损耗的计算。术语 w & b 表示模型已经学习的权重和偏差。第二部分对应于计算权重向量的范数( w )的正则化项。这个正则项被明确地称为著名的 L2 范数或权重衰减。这样的最终结果是,网络的权重矩阵根据正则化参数 lambda (λ) 的值而受到惩罚。因此,正则化参数 λ 可以被认为是需要微调的另一个超参数。
了解工作
通过举一个极端的例子,可以理解 L2 规范的规则化影响背后的直觉。让我们将正则化参数 λ 的值设置在较高端。这将严重惩罚网络的权重矩阵( w ),它们将被赋予接近于零的值。其直接影响是神经网络的净激活减少,前向传递效应减弱。现在,有了简化得多的神经网络体系结构,该模型将无法过度适应训练数据,并将能够对新数据和特征进行更好的概括。
正则网络与非正则网络的分析(图片由作者提供)
这种直觉基于正则化参数 λ 的值被设置得非常高的事实。但是,如果选择该参数的中间值,将会提高测试集上的模型性能。
什么时候调整你的模型?
弄清楚正则化概念背后的直觉当然很好,但是,更重要的一点是要知道你的网络是否真的需要正则化。在这里,绘制学习曲线起着至关重要的作用。
绘制出模型在多个时期的训练和验证损失是确定模型是否过度拟合的最广泛使用的方法。该方法背后的概念基于这样一个事实,即**如果模型过度拟合训练数据,那么训练损失和验证损失将相差很大,而且验证损失将总是高于训练损失。**其原因是,如果模型不能很好地概括以前未见过的数据,那么相应的损失或成本价值将不可避免地很高。
过度拟合模型的学习曲线(图片由作者提供)
从上图可以明显看出,训练损失和验证损失之间存在巨大的差距,这表明模型明显过度适合训练样本。
使用 TensorFlow 2.0 实现 L2 范数
L2 规范的实际实施很容易证明。为此,让我们从 sklearn 获取糖尿病数据集,首先绘制非正则化模型的学习曲线,然后绘制正则化模型的学习曲线。
该数据集有 442 个实例,并接受 10 个基线变量:年龄、性别、身体质量指数、平均血压和 6 个血清测量值(S1 到 S6) 作为其训练特征(称为 x)和一年后疾病进展的测量值作为其标签(称为 y)。
非规范化模型
让我们使用 TensorFlow 和 sklearn 导入数据集。
导入数据集(按作者列出要点)
我们现在将数据分成训练集和测试集。测试集将保留 10%的训练数据。这可以使用 sklearn 中的 train_test_split() 函数来完成。
将数据集分成训练集和测试集(作者要点)
现在,我们仅使用 Keras 的顺序 API 中的密集层创建一个非规范化模型。这个非正则化模型将有 6 个隐藏层,每个层都有一个 ReLU 激活功能。
非规范化的模型架构(作者要点)
在设计了模型架构之后,我们现在用 亚当 优化器和 均方误差 损失函数来编译模型。训练在 100 个时期内进行,度量存储在一个变量中,该变量可用于绘制学习曲线。
编译和训练模型(作者要点)
我们现在绘制训练数据和验证数据中观察到的模型损失:
过度拟合的图表(图片由作者提供)
这里的结果是,验证损失在大约 10 个时期后持续上升,而训练损失持续下降。这种相反的趋势在两个损失之间产生了巨大的差距,这表明模型过度拟合了训练数据。
用 L2 范数正则化模型
我们已经从前面的学习曲线中得出结论,非正则化模型会遭受过拟合,为了解决这个问题,我们引入了 L2 范数。
密集层和卷积层都有一个可选的kernel _ regulator关键字参数。为了加入权重衰减或 L2 正则化,我们设置核正则化参数等于TF . keras . regulators . L2(参数) 。这个参数对象是用一个必需的参数创建的,该参数是乘以权重平方和的系数或正则化参数 λ。
我们现在创建一个类似的网络架构,但是这一次,我们包括了kernel _ regulator参数。
正则化模型架构(作者要点)
我们使用相同的优化器、损失函数、度量和时期数来编译和训练模型,因为这使得两个模型之间的比较更容易。
训练后,当训练和验证损失相对于时期数作图时,观察到以下学习曲线:
正确匹配(图片由作者提供)
正则化模型的学习曲线更加平滑,训练和验证损失彼此更加接近,这表示网络中的变化更小。现在,训练损失和验证损失都可以相互比较,这表明模型泛化新特征的信心增加了。
结论
在这个启发性的旅程中,我们通过比较两个模型的性能进行了广泛的分析,其中一个模型遭受过拟合,另一个模型解决了前一个模型的缺点。机器学习中的 偏差和方差 问题不容忽视,必须应用策略来克服这些非平凡的复杂性。我们今天讨论了一个这样的策略,以避免我们的模型出现偏差。
然而,旅程并没有就此停止,因此,我们必须把目光放得更远,必须顺风而行。
这里是作者的 GitHub 库的链接,可以参考完整的代码。
[## ujjwalkumar 2607/正则化-技术-张量流
通过在 GitHub 上创建一个帐户,为 ujjwalkumar 2607/regularization-techniques-tensor flow 开发做出贡献。
github.com](https://github.com/ujjwalkumar2607/Regularisation-techniques-tensorflow)
道路安全深度学习
使用 YOLOv5 识别伦敦骑自行车者的危险因素
介绍
作为 ICL MRes 生物医学研究(数据科学)学位的一部分,学生将参与两个项目。项目 1 历时 5 个月,2019 年 10 月开工。项目 2 将于 2020 年 8 月完成。
第一个项目
一篇关于第一个项目成果的文章可以通过下面的链接查看。
机器学习通过营养预防和治疗癌症
towardsdatascience.com](/building-a-food-recommendation-system-90788f78691a)
第二个项目
第二个项目的目的是使用谷歌街景图像来估计伦敦所有道路的安全分数(从自行车手的角度)。它将被训练成一个深度学习模型,使用 YOLOv5 来识别图像上的风险因素(狭窄的道路,缺乏自行车道……)。这种模式最终可能会推广到其他城市。并且因此,使得有可能估计谷歌街景可用的所有地理位置的得分。
从道路安全的角度来看,该项目旨在降低伦敦骑自行车者的死亡率和受伤率(最终也将扩展到其他地区)。建议骑自行车的人选择最安全的道路,同时指导政府在有危险信号的地方实施额外的安全措施。
从环境的角度来看,通过为人们提供更安全的通勤方式,预计可持续交通方式的采用率将会增加:自行车、踏板车……从而有助于解决我们社会最普遍的问题之一:全球变暖。
人们越来越意识到,公共交通是导致传染病(例如——新冠肺炎)传播的主要因素之一。此外,就呼吸系统疾病而言,发现空气污染程度较高的地区对其居民构成额外风险。
谦虚地说,我希望这个项目至少能在这些方向上迈出一小步。
项目的 GitHub 资源库中提供了所有实现和实现既定目标的路线图。
监督人
马吉德·埃扎提 | 里基·纳特瓦尼
用 Pytorch 从头开始实现 Canny 边缘检测
Canny 边缘检测
Canny 滤波器无疑是边缘检测中最知名和最常用的滤波器。我将逐步解释用于轮廓检测的 canny 滤波器。一步一步因为 canny 滤波器是一个多级滤波器。Canny 过滤器很少被集成到深度学习模型中。所以我会描述不同的部分,同时用 Pytorch 实现。它几乎可以无限制地定制,我允许自己有一些偏差。
输入图像和内核之间的卷积
我来介绍一下什么是卷积矩阵(或者说内核)。卷积矩阵描述了我们将要在输入图像上通过的滤波器。简单来说,内核将通过应用卷积乘积在整个图像上从左到右、从上到下移动。该操作的输出被称为滤波图像。
高斯滤波
首先,我们通常通过应用模糊过滤器来移除输入图像中存在的噪声。这个滤镜的选择由你决定,但是我们大多用的是高斯滤镜。
高斯核
可以制作不同大小的高斯核,或多或少居中或展平。显然,核越大,输出图像越模糊。
索贝尔滤波
为了检测边缘,必须对图像应用过滤器以提取梯度。
X 上的 Sobel 核
最常用的滤波器是索贝尔滤波器。分解成两个滤波器,第一个内核用于水平提取梯度。粗略地说,右边的像素与左边的像素相比越亮,过滤图像的结果就越高。反之亦然。这一点在莉娜的帽子左侧可以清晰的看到。
Y 上的 Sobel 核
第二个内核用于垂直提取梯度。一个是另一个的转置。这两个核心具有相同的作用,但是在不同的轴上。
计算梯度
嗯,我们在图像的两个轴上都有梯度。为了检测轮廓,我们想要梯度的大小。我们可以使用绝对值范数或欧几里德范数。
梯度的大小和方向
使用我们的梯度的大小,边缘现在被完美地检测到了。但是他们很粗。如果能只保留轮廓的细线就太好了。因此,我们同时计算我们的梯度的方向,这将被用来只保留这些细线。
在 Lena 的图片中,渐变是由强度来象征的,因为渐变的角度很重要。
非最大抑制
为了细化边缘,可以使用非最大抑制方法。在此之前,我们需要创建 45×45 方向的内核。(可以参考本帖了解旋转矩阵)
方向核
因此,该过程将需要检查8-邻域(或称为摩尔邻域)。这个概念很容易理解。对于每个像素,我们将检查方向。我们将会看到这个像素是否比它的梯度方向的邻居更强烈。如果是,那么我们将该像素与其相反方向的邻居进行比较。如果该像素与其双向邻居相比具有最大强度,则它是局部最大值。这个像素将被保持。在所有其他情况下,它不是局部最大值,并且像素被移除。
阈值和迟滞
最后,只剩下应用阈值了。有三种方法可以做到这一点:
- 低-高阈值:强度高于阈值的像素设置为 1,其他设置为 0。
- 低-弱和弱-高阈值:我们将具有高强度的像素设置为 1,将具有低强度的像素设置为 0,并且在两个阈值之间我们将它们设置为 0.5。他们被认为是弱者。
- 低-弱和弱-高带滞后:同上。弱像素通过它们的滞后水平进行评估,并被重新分配为高或低。
阈值
滞后是系统状态对其历史的依赖性。—维基百科
在我们的例子中,滞后可以理解为一个像素对其邻居的依赖性。在 canny 滤波器的滞后步骤中,我们说,如果弱像素在其 8 个邻居中有一个高邻居,则该弱像素将被分类为高。
我更喜欢用不同的方法,用最终的卷积滤波器对弱像素进行分类。如果它的卷积乘积大于 1 ,那么我将其归类为高。高邻比 0.25 分,弱半 0.25。
滞后内核
“你说 Pytorch?”
的确是的,现在是 Pytorch 代码的时候了。一切都组合成一 nn。模块。我不能保证实现会得到优化。使用 OpenCV 的特性将加快处理速度。但是这种实现方式至少具有以下优点:灵活性**,可参数化,并且根据需要容易改变。**
知识就是分享。
支持我,一键获取 中我所有文章的访问 。
来源
所有的图像都是自制的(除了莉娜和指南针)
在 Python 中从头开始用 L2 正则化实现逻辑回归
构建自己的逻辑回归分类器的分步指南。
目录:
1.简介:
逻辑回归是用于分类的最常见机器学习算法之一。它是一种统计模型,使用逻辑函数来模拟二元因变量。本质上,它预测了某个观察值属于某个类别或标签的概率。例如,这是一张猫的照片还是一张狗的照片?
注意:虽然逻辑回归可以扩展到多类分类,但在本文中我们将只讨论二元分类设置。
2.先决条件:
读者应理解以下内容:
- 什么是数据集?
- 什么是特性?
- 什么是多重共线性?
- 什么是 sigmoid 函数?
3。幕后数学
**假设:**逻辑回归在开始其建模过程之前做出某些关键假设:
- 标签几乎是线性可分的。
- 观察结果必须是相互独立的。
- 独立变量之间的多重共线性很小或没有。
- 自变量与对数概率呈线性相关。
**假设:**我们希望我们的模型预测某个观察值属于某个类别或标签的概率。因此,我们想要一个假设 h 满足下面的条件0 <= h(x) <= 1
,其中 x 是一个观察值。
我们定义h(x) = g(w
ᵀ * x)
,其中 g 为 sigmoid 函数 w 为可训练参数。因此,我们有:
**观察的成本:**既然我们可以预测观察的概率,我们希望结果具有最小的误差。如果类标签是 y ,则与观察值 x 相关联的成本(误差)由下式给出:
**成本函数:**因此,数据集中所有 m 个观察值的总成本为:
我们可以将成本函数 J 改写为:
逻辑回归的目标是找到参数 w ,使得 J 最小。但是,我们怎么做呢?
梯度下降:
梯度下降是一种优化算法,用于通过在最陡下降方向迭代移动来最小化某个函数,该方向由梯度的负值定义。
我们将使用以下模板更新每个参数 wᵢ :
以上步骤将帮助我们找到一组参数***【wᵢ】,*** ,然后这些参数将帮助我们提出***【x】***来解决我们的二元分类任务。
但是也存在与上述梯度下降步骤相关的不期望的结果。在寻找最佳 h(x)的尝试中,会发生以下情况:
CASE I: For class label = 0
h(x) will try to produce results as close 0 as possible
As such, wT.x will be as small as possible
=> Wi will tend to -infinityCASE II: For class label = 1
h(x) will try to produce results as close 1 as possible
As such, wT.x will be as large as possible
=> Wi will tend to +infinity
这导致了一个称为过度拟合的问题,这意味着,模型将无法很好地概括,即,它将无法正确预测未知观察的类标签。所以,为了避免这种情况,我们需要控制 wᵢ.参数的增长 但是,我们怎么做呢?
4。正规化:
正则化是一种通过惩罚成本函数来解决机器学习算法中过拟合问题的技术。这是通过在成本函数中使用附加的惩罚项来实现的。有两种类型的正则化技术:
- 拉索或 L1 正则化
- 岭或 L2 正则化(我们将在本文中只讨论这一点)
那么,L2 正则化如何帮助防止过度拟合呢?让我们先来看看我们新的成本函数:
λ 称为正则化参数。它控制两个目标之间的权衡:很好地拟合训练数据 vs 保持参数较小以避免过度拟合。
于是, J(w) 的梯度变成:
正则项将严重惩罚大的 、wᵢ 。对更小的的影响会更小。这样, w 的增长受到控制。用这些受控参数 w 得到的 h(x) 将更具普适性。
注意: λ 是一个超参数值。我们必须通过交叉验证来找到它。
- 较大的值 λ 会使 wᵢ 收缩得更接近 0,这可能会导致欠拟合。
- λ = 0, 将没有正则化效果。
选择λ时,我们必须妥善处理偏差与方差的权衡。
你可以在这里找到更多关于正规化的信息。
我们完成了所有的数学运算。让我们用 Python 实现代码。
5.代码:
注意:尽管我们在上面将正则化参数定义为 λ ,我们在代码中使用了 C = (1/λ) 以便与 sklearn 包相似。
5.结果和演示:
让我们在虚拟数据集上安装分类器,并观察结果:
决策边界图:
正如我们所见,我们的模型能够很好地对观察结果进行分类。界限就是决策线。
在这里获得模型的沙盒体验:现场预览。
6.未来工作和结论:
通过实现其他算法,如随机平均梯度、有限记忆 BFGS,来解决优化问题,有提高分类器性能的余地。
我们也可以实现套索或 L1 正则化。
仅此而已。谢谢你阅读我的博客。如果你有任何想法,请留下评论、反馈和建议。
7.参考资料:
[1]https://www.coursera.org/learn/machine-learning
[3]https://www . geeks forgeeks . org/understanding-logistic-regression
用 PyTorch 在 5 分钟内用 Java 实现物体检测
DJL,一个引擎无关的深度学习库
PyTorch 是最流行的机器学习框架之一。其易于使用的 Pythonic 界面和生产力增强特性,如数据并行性和动态计算图,简化了计算机视觉(CV)和自然语言处理(NLP)中应用的 ML 模型的开发。
虽然有多种选项来为生产中的 PyTorch 模型提供服务,但是在 Java 中本地部署 PyTorch 模型的选项却很少。以前,用户可以围绕 PyTorch C++ API 编写一个 Java 包装器,或者使用实验性的 PyTorch Java 绑定。亚马逊的深度 Java 库 (DJL)现在通过其易于使用的高级 API 为 PyTorch 和 Java 社区提供了一个更简单的选择。通过抽象 ML 中涉及的复杂性和捆绑乏味的数据处理例程,DJL 用 PyTorch 模型将运行预测简化到几行代码。在我们的两部分博客系列中,我们展示了用户如何使用 PyTorch 进行推理。首先是预训练的 PyTorch 模型,然后是用户生成的 PyTorch 模型。在这两篇博客的第一篇中,我们展示了如何在不到 5 分钟的时间内使用预先训练好的 PyTorch 模型实现物体检测。那么,我们开始吧。
设置
要开始使用 DJL,请使用命令行或您选择的 IDE 创建一个项目。将以下代码添加到您的build.gradle
配置文件中。在这个例子中,我们使用 PyTorch 1.4.0 C++发行版。
https://gist . github . com/lank 520/7787 C2 D2 b5 FB 304 CBE 60360139 e 8760 f # file-build-gradle
使用预训练 PyTorch 模型的对象检测
在本节中,我们使用 NVIDIA 的预训练 PyTorch 跟踪模型来实现对象检测。该任务识别下方的图像中嵌入的物体(狗、自行车、卡车):
使用 DJL,您可以用下面的代码块在几行代码中运行推理:
https://gist . github . com/lank 520/74 EC 6b 35 ca 6076 fa 3c be 2848869 F7 a1f # file-od-Java
运行 PyTorch 代码会产生以下输出。输出列出了检测到的对象及其相对概率。
然后你可以使用我们的图像可视化功能来定义这个图像的边界框。
有关如何加载 PyTorch 模型的更多信息,请查找最新解决方案的文档。
什么是 DJL?
完成本教程后,你可能想知道 DJL 是什么。深度 Java 库(DJL)是一个开源库,用于在 Java 中构建和部署深度学习。该项目于 2019 年 12 月启动,在亚马逊的团队中广泛使用。这一努力受到了其他 DL 框架的启发,但是是从底层开始开发的,以更好地适应 Java 开发实践。DJL 与框架无关,支持 Apache MXNet、PyTorch、TensorFlow 2.x 和 ONNXRuntime。
PyTorch 支持范围
[更新]:现在我们在最新的 0.10.0 版本中支持 PyTorch 1.7.1
我们用 PyTorch (1.4.0) C++ API 构建了 DJL PyTorch 引擎,它允许我们运行操作符和推理代码。所有采用 TorchScript 格式的模型都可以导入并在 DJL 上运行。为了实现这一点,我们编写了一个定制的 JNI 层,在 C++和 Java 之间进行交互。目前,DJL 覆盖了 60 多家 PyTorch 运营商。DJL 本身具有以下固有特性:
- NDManager:高效的垃圾收集系统
- 多线程支持
- 模型动物园:PyTorch 模型动物园支持图像分类和对象检测模型
- 还有更多…
支持的平台:
- Mac: CPU
- Linux: CPU 和 GPU (CUDA 9.2 和 CUDA 10.1)
- Windows: CPU 和 GPU (CUDA 9.2 和 CUDA 10.1)
要了解更多信息,请查看我们的网站、 Github 资源库和 Slack channel。请继续关注我们的下一篇博文,我们将展示如何在 Java 中使用自己的 PyTorch 模型运行推理。
用 Python 实现 SVM…两分钟后。
有可能在两分钟内编写一个简单的 SVM 版本吗?
https://unsplash.com/photos/BXOXnQ26B7o
在本文中,我将使用 Python 来实现支持向量机分类器,它被认为是最复杂的基本机器学习算法之一。尽管如此,在我的代码中,与线性回归实现唯一真正不同的是所使用的损失函数。为了更好地理解 SVM 使用的损失函数,我也推荐你看一下这个很棒的视频和解释。
【https://www.youtube.com/watch?v=VngCRWPrNNc
从视频中你可以理解,SVM 损失函数的核心是这个公式,它描述了点到超平面的距离。
通过执行梯度下降和减少损失函数,SVM 算法试图最大化决策边界和两类点之间的差距。SVM 损失函数可以写成如下形式:
现在,让我们转到实现本身,只需要几分钟的时间来编码梯度下降,以最小化这个损失函数。
用 Python 实现 SVM
- 首先,我将创建数据集,使用 sklearn.make_classification 方法,我还将做一个训练测试拆分,以衡量模型的质量。
2.现在,我将实现上面描述的损失函数,以在训练模型时意识到损失在下降。
如您所见,我还创建了一个损失计算的小示例,位于函数本身的下方。
3.让我们写一个在随机梯度下降过程中,计算损失梯度的函数。这可以通过简单地将损失函数相对于 W(超平面的坐标)进行微分来实现
4.最后,让我们实现梯度下降本身(注意,我给 X_train 变量增加了一个额外的项,代表模型偏差)。
当训练完成时,可以通过简单地取权重和点坐标之间的点积来执行预测。
为了便于比较,我也将安装一个 sklearn SVM 模型。
如您所见,sklearn 实现的结果更好,但是对于一个非常简单的模型,我们的结果仍然很好。
你可以在我的 网站 上查看其他帖子
你可能会对我的其他中型职位感兴趣。
借助 RapidsAI ,让您的机器学习速度提高 300 倍
如果这个故事对你有帮助,也许对别人也有帮助,别忘了分享一下:)
使用 Scikit-learn 和 ML 实现朴素贝叶斯分类器。网
当我们想到机器学习时,首先想到的语言是 Python 或 r,这是可以理解的,因为它们为我们提供了实现这些算法的许多可能性。
然而,我每天都在 C#中工作,我的注意力被一个非常新鲜的库所吸引,这个库就是ML.NET。在本文中,我将展示如何使用 Scikit-learn 在 Python 语言中实现朴素贝叶斯分类器,以及如何使用前面提到的 ML.NET 在 C#中实现朴素贝叶斯分类器。
朴素贝叶斯分类器
朴素贝叶斯分类器是一个简单的概率分类器,它假设独立变量相互独立。它基于贝叶斯定理,其数学表达如下:
数据集
我使用了来自 UCI 机器学习知识库的葡萄酒质量数据集进行实验。所分析的数据集具有 11 个特征和 11 个类别。等级决定了葡萄酒的质量,数值范围为 0-10。
ML.NET
第一步是创建一个控制台应用程序项目。然后你必须从 NuGet 包中下载 NuGet 库。现在,您可以创建与数据集中的属性相对应的类。清单中显示了创建的类:
然后,您可以继续加载数据集,并将其分为训练集和测试集。我在这里采用了标准结构,即 80%的数据是训练集,而其余的是测试集。
var dataPath = "../../../winequality-red.csv";var ml = new MLContext();var DataView = ml.Data.LoadFromTextFile<Features>(dataPath, hasHeader: true, separatorChar: ';');
现在有必要使模型结构适应 model 图书馆采用的标准。这意味着指定类的属性必须称为 Label。其余的属性必须压缩在名称 Features 下。
var partitions = ml.Data.TrainTestSplit( DataView,
testFraction: 0.3);var pipeline = ml.Transforms.Conversion.MapValueToKey(
inputColumnName: "Quality", outputColumnName: "Label")
.Append(ml.Transforms.Concatenate("Features", "FixedAcidity", "VolatileAcidity","CitricAcid", "ResidualSugar", "Chlorides", "FreeSulfurDioxide", "TotalSulfurDioxide","Density", "Ph", "Sulphates", "Alcohol")).AppendCacheCheckpoint(ml);
完成上述步骤后,您可以继续创建培训渠道。在这里,您可以选择朴素贝叶斯分类器形式的分类器,并在参数中为其指定标注和要素的列名。您还指示了表示预测标签的属性。
var trainingPipeline = pipeline.Append(ml.MulticlassClassification.Trainers.
NaiveBayes("Label","Features"))
.Append(ml.Transforms.Conversion.MapKeyToValue("PredictedLabel"));
最后,您可以继续训练和测试模型。一切都在两行代码中结束。
var trainedModel = trainingPipeline.Fit(partitions.TrainSet);var testMetrics = ml.MulticlassClassification.
Evaluate(trainedModel.Transform(partitions.TestSet));
Scikit-learn
在 Python 实现的情况下,我们也从数据集文件的处理开始。为此,我们使用 numpy 和 pandas 库。在清单中,您可以看到用于从文件中检索数据并从中创建 ndarray 的函数,这些函数将用于算法。
from sklearn.naive_bayes import GaussianNB
from common.import_data import ImportData
from sklearn.model_selection import train_test_splitif __name__ == "__main__":
data_set = ImportData()
x = data_set.import_all_data()
y = data_set.import_columns(np.array(['quality']))
下一步是创建一个训练和测试集。在这种情况下,我们还对测试集使用 20%的划分,对训练集使用 80%的划分。我使用了 train_test_split 函数,它来自库 sklearn。
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
现在,您可以继续使用朴素贝叶斯分类器了。在这种情况下,训练和测试也在几行代码中完成。
NB = GaussianNB()
NB.fit(X_train, y_train.ravel())
predictions = NB.predict(X_test)
print('Scores from each Iteration: ', NB.score(X_test, y_test))
结果和总结
Scikit-learn 实现的朴素贝叶斯分类器的准确率为 56.5%,而 ML.NET 的准确率为 41.5%。这种差异可能是由于算法实现的其他方式,但仅基于精度,我们不能说哪个更好。然而,我们可以说,机器学习算法的实现的一个有前途的替代方案正在开始出现,这就是 C#和 ML.NET 的使用。
Python 中两个堆栈队列的实现:数据结构
了解堆栈和队列。然后使用两个堆栈实现一个队列。
栈和队列都是列表。但是他们有不同的更严格的规则。本文将讨论堆栈和队列。首先,将有一个堆栈实现,然后是一个带有堆栈的队列。
堆
堆栈是具有后进先出(LIFO)规则的列表。最后进去的元素先出来。
来源:作者
你可能会想这有什么用。编辑器中的撤销操作就是一个很好的例子。当我们“撤销”时,它会撤销上一次操作。编译器使用堆栈检查匹配的括号。以下是堆栈所需的功能:
- 空列表的初始化。
- 向列表中添加元素。
- 从列表中弹出元素。
- 检查列表是否为空。
- 确定列表的顶部元素。
- 拿到名单。
以上功能的 Python 实现:
class Stack():
def __init__(self):
self.elements = []def push(self, element):
self.elements.append(element)def pop(self):
return self.elements.pop()def is_empty(self):
return self.elements == []def peek():
if not self.elements.is_empty():
return self.elements[-1]def get_elements(self):
return self.elements
长队
队列也是一个列表或容器。它遵循先进先出(FIFO)规则。一个很好的例子是在杂货店排队。先排队的人有机会先付钱后离开。当一个新来的人想排队时,她/他必须排在队伍的后面。队列有两个操作。入队和出队。其中 enqueue 表示添加列表或容器中的元素。出列意味着从列表底部删除一个元素。
下图清楚地显示了堆栈和队列的区别:
我们可以使用两个堆栈来实现一个队列。
- 将 stack1 中的元素排队。
- 出列可能很棘手。因为 stack 从顶部移除了一个元素。但是 queue 从底部移除了一个元素。我们需要弹出 stack1 中的所有元素,并不断将它们添加到 stack2 中。因此,stack1 的底部元素将是 stack2 的顶部元素。现在,如果我们从 stack2 中弹出一个元素,我们就弹出了 stack1 的底部元素。这就是我们的出列操作。操作完成后,我们应该再次将元素放回 stack1 中。下面是代码的样子:
class Queue:
def __init__(self):
self.stack_1 = Stack()
self.stack_2 = Stack()def enqueue(self, item):
self.stack_1.push(item)def dequeue(self):
if not self.stack_1.is_empty():
while self.stack_1.size()> 0:
self.stack_2.push(self.stack_1.pop())
res = self.stack_2.pop()
while self.stack_2.size()>0:
self.stack_1.push(self.stack_2.pop())
return res
我想分享这个,因为它是学习算法和数据结构的一个很好的练习材料。
下面是一篇关于使用排序算法解决一些问题的文章:
半监督生成对抗网络在 Keras 中的实现
使用半监督学习构建强大的分类器
穆罕默德·阿里扎德在 Unsplash 上的照片
每个人都听说过监督学习和非监督学习,但在它们之间还有另一套学习技术,称为半监督学习。
监督学习是机器学习中最常用的技术,但它有一个缺点,即它需要大量的标记数据。给数据贴标签需要花费大量的精力和时间。这就是半监督学习发挥作用的地方。
什么是半监督学习?
半监督学习是一种技术,我们只使用大量未标记数据中的一小部分标记数据来训练我们的模型。
这似乎是一个有趣的方法,而且标记数据的成本也大大降低了。
什么是生成性对抗网络?
生成对抗网络(GAN)是由 Ian Goodfellow 及其同事在 2014 年设计的一类生成模型。就生成模型而言,这是一个突破。
在甘的研究中,有两个神经网络试图击败对方(即一个网络的损失是另一个网络的收益)。这两个神经网络被称为生成器和鉴别器。
生成器模型试图生成图像(类似于训练数据),鉴别器的工作是将图像分类为真实的(来自训练数据)或虚假的(来自生成器)。所以基本上鉴别器试图不被发生器愚弄,而发生器试图愚弄鉴别器,这就是为什么这被称为博弈论方法,因为发生器和鉴别器都在博弈中试图战胜对方。
如何使用 GAN 进行半监督学习?
下面是半监督 GAN 的模型
半监督甘,来源:图片由作者提供
让我们来了解一下模型
鉴别器通过三种类型的图像,即有标签的训练图像、无标签的训练图像和生成器生成的假图像。它的工作不仅是区分真实/虚假图像,而且将标记的训练图像分类到它们正确的类别中。
鉴频器有两种不同的输出:
- Softmax 激活,用于将标签数据分类到正确的类别,即这是受监督的鉴别器。
- 自定义激活分类为真或假。我们将在实现中看到自定义激活。
这种方法的强大之处在于,鉴别器不仅要训练有标签的数据,还要训练大量无标签的数据,因为它还必须区分真实/伪造的图像,因此对于这项任务,鉴别器需要学习提取用于将图像分类为真实或伪造的特征。这种对抗性训练将有助于鉴别器更准确地对标记数据进行分类,因为它在学习对真实/伪造图像进行分类的同时,还会识别其他模式,而这通常不会在一小组标记数据上进行。
让我们来看看上述模型在 Keras
中的实现。我将使用的数据集是古老的 MNIST 数据集(每个人的最爱)
设置
首先导入所有必需的包
z_dim 是我们将传递给生成器的随机正态变量的维数。
资料组
我们需要准备如下两个数据集:
- 监督鉴别器:数据集将是完整训练集的一个小子集。
- 无监督鉴别器:数据集将是完整的训练集。
这里只需要解释**batch _ label()**函数
batch_labeled() 通过选择样本子集及其标签,为监督鉴别器准备数据集。我们将使用 100 个带标签的例子(即每类 10 个例子)来训练我们的分类器。
batch_unlabeled() 从数据集中随机抽取图像,图像数量等于给定的 batch_size。
发电机网络
发电机网络
使用 Keras 顺序 API 构建我们的生成器模型。这是一个基本的发电机模型,因为我们的任务并不复杂。
密集的层用于对我们的尺寸 z_dim 进行整形,其形状为 (batch_size,100) 到 (batch_size,7,7,256) ,这样我们就可以在其上应用 Conv2DTranspose 层来生成图像。
鉴别器网络
鉴别器网络
这是我们的鉴别器,它输入一个图像(假的或真的),输出图像是真的还是假的,即“1”或“0”。
这里的输出一直是密集层的输出,没有任何激活,因此这些值也可以是负的。下一步就会明白为什么要这样做了。
监督鉴别器
监督鉴别器
**build _ discriminator _ supervised()**将我们在上述步骤中创建的鉴别器模型作为输入,并使用 softmax 激活将我们的输入图像分类为 10 个类别之一(针对 MNIST)。
无监督鉴别器
**build _ discriminator _ unsupervised()**接受我们之前创建的鉴别器的输入,并对鉴别器的输出应用一个 custom_activation() 。
当我们创建 discriminator 时,我们将它的输出保存为一个密集层的输出,该层在没有任何激活的情况下给出值。因此,自定义激活会执行以下操作
自定义激活功能
这里 z 是没有任何激活的密集层的输出, k 对于我们的情况是 10, y 在 0 和 1 之间。
由于图像真实或虚假的概率将是所有类别的概率之和,因此激活函数基本上是所有类别的和,并且在[0,1]之间缩放输出,以获得图像真实或虚假的概率。这个技巧比使用乙状结肠有效得多。
监督和非监督鉴别器使用的权重相同,只是两者使用的输出节点不同。
建筑甘
build_gan() 基本上接受生成器和鉴别器的输入,并将它们合并形成一个模型。这样做是为了训练发电机网络。这将被称为复合模型。
注意,这里的鉴别器是无监督的鉴别器,因为 GAN 仅用于未标记的图像。
训练半监督 GAN
首先,让我们通过编译上面创建的所有模型函数来构建我们的模型。
定义模型
请注意,在构建 GAN ( 复合模型)时,我们将Discriminator _ unsupervised保持为不可训练(第 7 行),因为 GAN 的训练是在训练鉴别器然后训练生成器的步骤中完成的,所以在训练生成器时,我们不希望我们的鉴别器被更新。
使用的成本函数是用于无监督鉴别器的二元交叉熵和用于有监督鉴别器的分类交叉熵。
现在主要的训练循环
主训练循环
因此,我们通过在一批真实的标记图像上训练监督鉴别器来开始我们的训练,然后我们在真实的未标记图像(标记为“1”)和由生成器生成的假图像(标记为“0”)上训练无监督鉴别器,最后,我们使用我们之前定义的复合模型通过鉴别器来训练我们的生成器。
因此,发电机工作的培训方式如下:
- 生成器生成一批假图像。
- 这些生成的假图像通过鉴别器进行分类,鉴别器的目标标签为真,即“1”(第 31 行)。**注意:**鉴别器在此步骤中不会更新。
- 因此,如果图像看起来不真实,损失会非常高,因此为了最小化损失,生成器将开始生成看起来真实的图像,这是我们的目标。
因此,随着生成器在更多图像上得到训练,它开始生成更好的图像,并且鉴别器也开始变得更好,因为它不希望在对假图像或真图像进行分类时其损失变高。因此,这就是为什么鉴别器和生成器都在相互竞争,如果一个变得更好,另一个也需要改进。
训练后,我们只需使用鉴别器并丢弃生成器,因为我们的主要目的是为 MNIST 构建一个分类器。
结果
来源:作者图片
因此,在测试集上获得的准确度约为 90.67 %,这是非常令人印象深刻的,因为我们刚刚使用了 100 幅标记图像(每类 10 幅)来训练我们的鉴别器。因此,这是一个强大的分类器,不需要大量的标记数据。当然,如果我们使用 1000 个标记图像,性能会提高更多,与现代数据集相比,这仍然很低。
由生成器生成的假图像,来源:作者提供的图像
所以,这些是由生成器生成的假图像,除了少数例外,都相当真实。
您还可以尝试在 100 张带标签的图像上训练没有 GAN 的相同鉴别器模型,看看它的表现如何。无疑会比用甘训练出来的鉴频器差。
这是与此实现相关联的 Github Repo
https://github.com/KunalVaidya99/Semi-Supervised-GANT4
如果你想了解更多关于 GAN 的知识,你可以试着阅读这本书Jakub Langr,Vladimir Bok 所著的《行动中的 GAN 与生成性对抗网络深度学习》。
这是我第一篇关于 Medium 的文章,如果你喜欢,请告诉我。感谢阅读!。
参考
https://arxiv.org/pdf/1606.01583.pdf
用 JavaScript 实现 2D 物理学
让我们在实现真实的 2D 物理模拟和可视化的同时享受一下 JavaScript 的乐趣吧!
真实动画的物理和实现可能看起来非常复杂和困难,但事实并非如此。这些算法可以非常简单,并可以产生各种物理概念的现实模拟,包括速度,加速度或重力。
所以,让我们看看在用 JavaScript 实现 2D 物理模拟时,这些算法是如何工作的!
你可以看看这里的动画和例子:https://martinheinz.github.io/physics-visual/
TL;博士:源代码可以在我这里的知识库中找到:https://github.com/MartinHeinz/physics-visual
匀速和加速运动
让我们从最基本的事情开始——移动东西。
如果我们只想匀速运动,那么我们可以使用这样的代码:
在上面的代码中,x
和y
是一个对象的坐标,例如椭圆,接下来的vx
和vy
分别是水平轴和垂直轴上的速度,dt
(时间增量)是定时器的两个滴答之间的时间,在的情况下,JavaScript 是对requestAnimationFrame
的两次调用。
举例来说,如果我们想要移动位于(150, 50)
的对象并向西南方向移动,那么我们会有如下结果(单次滴答后的移动):
不过匀速运动很无聊,所以让我们加速物体的运动:
在这段代码中,我们添加了ax
和ay
,它们分别代表 x 和 y 轴上的加速度。我们用加速度来计算速度或速率的变化(vx/vy
),然后我们用它来像以前一样移动物体。现在,如果我们复制前面的例子,只在 x 轴上增加加速度(向西),我们得到:
重力
既然我们可以移动物体,那么把物体向其他物体移动怎么样?嗯,那就叫引力。为了实现这一点,我们需要添加什么?
只是为了让你知道我们想要达到的目的:
首先,让我们回忆一下高中时的几个等式:
力的方程式:
如果我们现在想把它扩展到两个物体相互作用的力,我们得到:
强制均衡器
这变得有点复杂了(至少对我来说),所以让我们把它分解一下。在这个等式中|F|
是力的大小,对于两个物体来说是一样的,只是方向相反。这些物体用它们的质量来表示- m_1
和m_2
。k
这里有一个引力常数和r
是这些物体重心的距离。如果还是没有太大意义,那么这里有一张图:
物体拉动
如果我们想创建一些可视化的东西,我们最终会有两个以上的对象,对吗?那么,当我们有更多的物体相互作用时会发生什么呢?
多物体力
看上面的图片,我们可以看到两个橙色物体用力F_1
和F_2
拉着一个黑色物体,但我们感兴趣的是最终力F
,我们可以这样计算:
- 我们首先使用上面的等式计算力
F_1
和F_2
- 然后我们把它分解成向量:
好了,我们有了所有需要的数学知识,现在代码看起来怎么样?我将省去所有的步骤,只给你看带注释的最终代码,如果你需要更多的信息,请随时联系我。🙂
碰撞
当物体运动时,它们也会在某一点发生碰撞。我们有两个解决碰撞的选项——将物体推出碰撞或弹开,让我们先看看推动的解决方案:
在我们能够解决碰撞之前,我们需要首先检查两个对象是否确实在碰撞:
我们首先声明代表两个碰撞物体的Collision
类。在checkCollision
功能中,我们首先计算物体距离的x
和y
分量,然后计算它们的实际距离d
。如果它们的半径之和小于它们的距离d
,那么它们一定会发生碰撞,所以我们返回新的Collision
对象。
冲突
现在,为了解决它们的碰撞,我们需要知道位移的方向和大小:
碰撞后的运动
因此,在 JavaScript 代码中应该是:
您可以在https://martinheinz.github.io/physics-visual/查看这种碰撞解决的交互示例(点击穿过物体)
用力解决碰撞
这个难题的最后一块——通过反弹物体来解决碰撞。在这种情况下,最好省略所有的数学,因为这会使文章变得两倍长,所以我要告诉你的是,我们需要考虑动量守恒定律和能量守恒定律,这有助于我们建立和解决以下神奇的方程:
那么,这个神奇的k
如何帮助我们呢?我们知道物体移动的方向(我们可以像之前用n_x
和n_y
一样使用特征向量来计算),但是我们不知道移动了多少*,这就是*T3。所以,这就是我们如何计算矢量(z
,它告诉我们将这些物体移动到哪里:
动力
现在是最后的代码:
结论
这篇文章包含了很多数学知识,但是大部分都很简单,所以我希望这能帮助你理解和熟悉这些物理概念。如果你想看更多的细节,那么你可以在我的库这里查看代码,在这里查看交互演示。
本文最初发布于martinheinz . dev
从头开始实现象棋引擎
教我的电脑和我一起玩
国际象棋是一种古老的双人战略棋类游戏。有大量的可能性,因为在每走 5 步之后,有 69,352,859,712,417 种可能的游戏。因此,几乎不可能预测每一步棋。
冰岛举办了 1972 年国际象棋世界锦标赛,Fischer vs spas sky——图片由 Mike Swigunski 在 Unsplash 上拍摄
作为一名谦逊的国际象棋业余爱好者,我给自己提出了这样的挑战:**开发一个简单、好看、有人工智能、能打败我的国际象棋游戏,**没有机器学习。这篇文章是关于我实现它的旅程,由 4 部分组成:规则、计算、策略和游戏。
为了把事情弄清楚,我也决定不去阅读象棋引擎上的理论或算法解释,我想基于我的常识和个人经验,建立我自己的算法。
我给它取名为鲍比,以此向罗伯特“鲍比”詹姆斯菲舍尔致敬,他是国际象棋世界冠军,也是我年轻时的偶像之一。
1.规则
片
国际象棋是由两个对手玩的,每个对手一种颜色,白棋和黑棋。然后,每一面都有以下部分:
- ♔——一个国王
- ♕——一位女王
- ♖——两辆车
- ♗—两位主教
- ♘——两位骑士
- ♙——八枚棋子
初始位置
游戏开始时,初始位置总是一样的,严格定义的。其他棋子前面的一系列棋子,皇家夫妇在中间,然后对称地出现主教、骑士,最后是车。
棋盘是 8×8 格的正方形格子(即二维阵列),明暗背景交替。现在是时候对该板进行可视化表示了。
直观显示
当我考虑画这些作品时,我首先开始考虑免费的图形资源。但是这将迫使我管理图片,这对于快速开发来说是不可取的。幸运的是,我发现每个符号都是 Unicode 的一部分:一个基本的文本标签就足以画出一幅作品!太好了,我已经能够在控制台日志中打印出板子来可视化它了:
♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
有趣,但没有我想象中的好看,所以我决定创建一个基本的 GUI:棋盘是一个单一的框架,具有网格布局,由 MVC 架构驱动。
我从暗背景和亮背景的定义开始,其中每个方块都是一个标签,包含一个空值或一块的 Unicode 符号。
如果你熟悉国际象棋,你可能知道棋盘网格有一个由字母(横轴,或文件)和数字(纵轴,排名)组成的坐标系统,用于游戏符号,即编写移动脚本。我最终将它们添加到我的板的侧面,这是结果,一个简单的 10x10 网格:
我的象棋游戏的基本图形用户界面
移动
我们继续。规则是什么?允许哪些操作?基本上有 4 种类型的移动,可以根据棋子组合:
- 顺子(用于车、皇后和国王)
- 对角线(主教、女王、国王)
- 骑士
- 棋子移动
注意,特殊招式顺道,卒晋升,以及阉割为了简单起见被故意忽略,但后来已经实现。关于招式的详尽描述可以在维基百科上找到。
实现移动的一些附加限制:一个棋子不能跳过另一个棋子,除了骑士,当一个棋子到达敌人的棋子时,它可以抓住它并在棋盘上占据它的位置。
我还设计了一个可选的彩色边框来突出移动:
- 红色-当前选定的部分
- 蓝色-所选棋子的可能目的地
- 绿色——对手的最后一步棋
方块周围的彩色边框可以更好地观察移动
规则
既然我们可以移动棋子,我们必须考虑游戏规则:有些事情可以做,有些事情必须做(通常是逃避检查)。
在这里解释每一个规则超出了范围,但是我尝试实现了大多数约束,其中包括:
- 玩家的回合;
- 检测一个王是否在检查中,当无法逃脱时,确定该王是将死(游戏失败);
- 两个国王总是相隔一个广场;
游戏结束
现在游戏如何结束?有两种情况:要么一方赢,要么双方都不赢(平局)。
如前所述,当你的国王被牵制时,你就输了,而且没有可能逃脱被牵制。
但是你画如果:
- 没有可能的举动但你的国王不在检查(相持);或者
- 两位玩家连续重复 3 个相同的动作;或者
- 两个国王都单独在棋盘上(或者没有足够的材料获胜)
我还实现了另一个规则来避免永无止境的游戏:如果在最后 50 步内既没有棋子移动,也没有俘获,那么这个游戏就是平局。请注意,这条规则在很大程度上被国际象棋界所接受。
有了上面的规则,两个人类可以玩一局,但是我们要和电脑对战,那就说核心吧。
2.计算
愚蠢的人工智能
可能的最低智能是什么?选择一个没有任何策略的允许移动。这是我作为机器人实现的第一级“智能”:计算所有可能的移动,并随机选择其中一个。好吧,那很有趣,但是现在让我们严肃点。
评估形势
人类如何决定该走哪一步?他/她选择能带来优势的行动,这可以转化为形势的改善。因此,我们需要一个函数来评估当前位置,尝试移动,然后重新评估。
基本上,根据游戏规则,当一步棋导致将死,那么停止搜索,因为这是最好的一步棋。另一方面,如果移动导致平局,我实施了一个惩罚分数,因为我想让 AI 尽可能具有侵略性。
那么,如何计算分数呢?一般来说,对于初学者来说,最简单的策略是捕捉片段。实现是给每种类型的棋子打分,所以你可以通过将你所有棋子的分数相加来计算你当前的分数,对对手的棋子也是如此。然后,通过比较两个分数,就可以很容易地估计出你在多大程度上(或不在多大程度上)超过了你的对手。
块的任意值
天真的评价
但不需要成为国际象棋大师就能明白事情没这么简单。在你移动之后,将轮到你的对手。假设你用你的皇后俘获了一个棋子,然后你的对手俘获了你的皇后,你失去了最重要的棋子,所以最后,这是一个可怕的举动。
为了避免这种情况,移动选择函数必须是递归的,以便在尝试移动后,该函数会被再次调用,但对对手来说:这个想法是猜测答案 s 应该是什么。并评估由此产生的情况。
由于这是一个递归函数,这可以应用几次,让我们尝试想象在 2,3 或更多的回合中可能出现的情况。但这里的成本是指数级的,因为我们计算了所有可能的移动,在实践中,我不得不将它限制在 3 个移动的深度,否则它会花太多时间来玩。
*注:*我稍后会了解到,这种计算是极小极大算法家族的一部分——考虑到对手的反应,尽量使你的最小收益最大化。
除此之外,预测 1、2 或 3 步棋允许 AI:
- 玩一个赢得游戏的移动导致立即将死(深度 1)
- 在(深度为 2)之后立即下一步防止损失的棋
这正是我用来测试的:设置一个游戏,可以在 1 或 2 步内导致将死,并让它分别找到获胜和避免失败的最佳步骤。
上面的逻辑在很多情况下都很有效,但是通过将死对手的王来最终获胜是没有效率的。评估功能需要一些额外的智能。
3.战略
我经常问自己,当我开始玩游戏时,我得到了什么提示,也就是说,什么使一步棋变好或变坏。
控制中心
看似简单,但取胜的关键之一是控制棋盘的中心。把你的棋子放在中间,或者至少让它们覆盖中间,有助于发展军队,进攻和防守。
我用热图的概念实现了这一点:
- 4 个最居中的方块各值 2 分;
- 他们周围的 12 个方块值 1 个点;
- 所有剩余的方块值 0 点。
然后,一个人必须计算每一个棋子的移动,并根据他们可以到达的位置,将上面所有的点加起来,得到最后的分数。
控制电路板中心的热图
汇聚到将死
我注意到我的 AI 在游戏的开始和中间都能玩得很好,但在结束游戏时却面临困难,因为它没有启发性地去玩那些导致攻击对手国王的移动。
我决定重新使用上面的想法来解决这个问题,也就是说,定义一个专注于对手王的热图:
- 打王的时候 3 pts,因为有王在牵制非常好;
- 王身边 2 pts,因为你阻止他逃跑;
- 国王区周围 1 pt,因为最终导致限制他的移动;
- 其余所有方块为 0。
以国王为中心的热图,其中国王将位于广场 g1 上(通常在王车易位后)
空缺
像在很多游戏或者运动中,有一个理论部分是一定不能忽视的。基于上述启发的策略是好的,但可能不足以有一个好的开始,而象棋中不完美的开始可能会很快导致失败。为了避免早期的糟糕举动,玩家用心学习开局,这就是我实现的。当然,有很多库有大量的空缺,但是因为我的目标是让一个人工智能可以和我相比,我选择只让它知道 15 个主要的空缺,每个空缺有 2-3 步。
我使用了一个树形结构来对开口进行分类:一个移动导致一个节点,一片叶子是给定开口的最后一个已知移动。
开口树的摘录
当人工智能必须从给定的节点中选择一条路径时,它会随机选择一条。我后来注意到这种随机选择是多么明显,因为它在每个游戏中引入了变量,避免了无聊的可预测路径。当树不知道刚刚下的棋时,人工智能会退回到默认的计算。
发展奖励和惩罚
给新手一些提示,避免早期游戏出现大的错误。例如,强烈推荐阉割(一种同时移动国王和车的特殊移动),因为它在保护国王的同时激活靠近中心的车。因此,我根据游戏历史实施奖励和惩罚。
在以下情况下给予奖金:
- 阉割完成了
在下列情况下给予处罚:
- 国王在阉割之前已经移动了,因为阉割的权利已经丧失
- 国王、王后或车(阉割除外)在开局时移动(前 5 步)
- 同一个棋子(卒除外)在开局时被移动了不止一次
4.(演奏等的)表现,风格;(乐曲)演奏
测试游戏
虽然我在开发阶段玩了很多次,但我最终决定创建一个简短的锦标赛,反对机器人和它们的创造者:
- 一个愚蠢的机器人,随机播放
- 机器人分别计算深度为 1、2 和 3 的移动
- 另一个机器人计算移动深度为 3,但也经历了一些开放
- 我,一个业余棋手
所有的参与者都是面对面的,每场比赛都是白棋两次,黑棋两次。这是最后的记分牌:
不确定比开发出来的人工智能更强是好是坏,但可以肯定的是,我赢了大部分与人工智能的比赛。然而,我对三个最好的机器人都丢了一分,可能是因为注意力不集中和/或玩得太快,这仍然是一种满足感,因为它证明了人工智能可以打败我。
不出所料,计算水平最高的机器人是亚军,随机的最后完成。一个有趣的事实是,后者能够通过与更强的机器人打平来获得 0.5 分。至于两个等级为 3 的机器人,他们几乎击败了所有其他机器人,他们的对决以 2 比 2 的比分结束,这也很符合逻辑。最后,人们不能说掌握理论上的开口会带来显著的优势,至少不会对其他采用相同策略的机器人产生影响,但我相信当面对人类时,它确实会产生影响。
试一试
现在你知道我是如何建立自己的象棋智能的,你可能希望尝试一下。当然,代码是公开的,可以在我的 GitHub 库中找到。
在你的电脑上本地运行它会给你最好的体验,但也提供了一个网页版,尽管有一些限制:一次只有一个会话(即一个玩家),因为资源有限,移动棋子时会有一些图形延迟。但是它是免费的,在线的:
这个基本的国际象棋游戏是一个谦虚的敬意,著名的国际象棋选手罗伯特詹姆斯’鲍比’菲舍尔,世界国际象棋冠军。它…
www.bobby-chess.com](https://www.bobby-chess.com/bobby/)
*注:*如果你有兴趣阅读我是如何通过一个网站展示一个 Java Swing 桌面应用的,我写了另一篇文章专门讨论这个技术方面:
[## 在不改变代码的情况下,将 Java Swing 应用程序增强为一个干净、优雅的 Web 应用程序
据说 Java Swing 是一种设计桌面应用的过时技术。然而,有一些简单的方法可以改变…
codeburst.io](https://codeburst.io/enhancing-a-java-swing-app-to-a-clean-elegant-web-app-without-changing-the-code-a69d7c1c2781)
结论
人工智能的局限性和优势
我意识到我的象棋引擎还有改进的空间,离最好的计算机引擎还差得很远。但是正如我在介绍中所说的,我想用我自己的见解对它进行编程。这就是为什么我拒绝使用理论材料来加强它,但是如果你有兴趣阅读这个主题,我推荐你看一看国际象棋编程。
此外,我完全相信,对于这种游戏来说,没有什么比机器学习更好的了,或者也许是机器学习和传统计算引擎的微妙结合。AlphaGo 是第一个击败围棋世界冠军的基于 ML 的引擎。然后在 2017 年推出了 AlphaZero ,并在很大程度上击败了最好的传统象棋引擎,即 StockFish。
但是当我父亲好心地测试我的象棋引擎时,他说了一句有趣的话:
最先进的象棋引擎很无聊。
这尤其正确,原因有二:首先,他们系统地击败了我们这些业余玩家。第二,他们中的一些人可能总是在给定的情况下采取相同的行动,所以他们实际上是可以预测的。
即使在最高的计算水平上,Bobby 也可能被业余玩家打败
博比引擎试图逃避这一点,由于在开放的随机性,以及当计算等效移动。
根据三步棋深度的合理限制,一个业余棋手在想象陷阱时有一点创造力就有机会获胜。
我的引擎的一个弱点是它在游戏开始时的糟糕开发,就在开场后:它试图尽快攻击,就像一个新手,如果对手用正确的防守进行抵抗,这可能会很快变成对 AI 的一个强大的劣势,因为这种贪婪行为的结果是一个重要的棋子可能会被提前捕获。
最终考虑
我最初的挑战是建立我自己的国际象棋游戏,有一个漂亮的用户界面和击败我的能力,我认为公平地说这个目标已经实现了。
我对最终的结果很满意,尽管我相信未来还有很多地方需要改进。我特别感兴趣的是开发一个深度学习的人工智能,并让他们一决雌雄。
资源
- GitHub 上的 Bobby—代码库
- https://www.bobby-chess.com/bobby/—在我的象棋引擎上玩在线游戏
- 在不改变代码的情况下,将一个 Java Swing 应用程序增强为一个干净、优雅的 Web 应用程序——我的象棋引擎技术方面的另一篇文章
- 国际象棋编程维基 —为国际象棋引擎解释理论和算法
- alpha zero——一个机器学习象棋引擎
- Unicode 中的国际象棋符号 —国际象棋棋子的字符
- 象棋/运动 —维基百科关于象棋的页面
从头开始实现连通分量标记算法
我将向你展示如何在 MATLAB 中实现这个算法
Guillaume Bourdages 在 Unsplash 拍摄的照片
目录
- 什么是连通分量标记?
- 一个例子的可视化
- MATLAB 中的代码
什么是连通分量标记?
这是一种计算机视觉算法,用于检测和计数二进制图像中相连区域的数量,也称为斑点。通常,这是在分割算法之后完成的。得到的分割(二进制)掩模然后通过连通分量标记算法运行,以计算不同区域的数量。
目标
该算法的输入是二值图像。目标是用相同的唯一标签标记每个连接的组件(或 blob)。因为每个斑点都会被标记,所以我们可以推断出单个斑点的总数。需要注意的是,输出会因您使用的表示/连接而有所不同。
不同的连接性
有两种常见的方法来定义组件是否连接。一种是说一个像素只有 4 个邻居(有时称为 4-连通性)。另一种是说明一个像素有 8 个邻居。参见图 1。
图一。左图:4 邻居表示法。右图:8 邻居表示法。
样品
让我们看一个这个算法做什么的例子。注意两种表示之间的不同结果(图 2 和图 3)。
图二。假设采用4-连通性表示的算法结果。在这种情况下,“2”不是“3”的邻居。
图 3。假设一个8-连通性表示的算法的结果。“2”是邻居。
算法是如何工作的?
该算法包括两次通过图像中的每个像素。
第一遍:
对于每个非零像素,我们检查它的邻居。
- 如果它没有非零邻居——我们知道它是一个新组件——那么我们给它一个新标签。
- 如果它有一个非零邻居——这些像素是相连的——我们给它与邻居相同的标签。
- 如果它有不止一个非零邻居,有两种情况:
- 邻居都有相同的标签。所以我们给当前像素同样的标签。
- 邻居有不同的标签。这是棘手的部分。我们知道所有这些像素现在都是相连的,所以标签应该都是一样的。我们如下解决这个问题。首先,我们将当前像素设置为邻居的最低标签。然后我们记下所有相连标签的等价性,也就是说,哪些不同的标签实际上应该是相同的。我们将在第二遍中解决这些问题!
每个非零像素现在将有一个标签。然而,一些连接的区域将具有不同的标签。所以我们需要再次检查图像来纠正这一点。我们通过使用等价类的记录来做到这一点:所有等价的标签(即,指代相同的斑点)将获得相同的标签。
第二遍:
对于每个带标签的像素:
- 检查此标签是否有等价标签,并解决它们。有几种方法可以解决这个问题。关于我的实现,请参见 MATLAB 部分。
逐步可视化
让我们来看一个算法做什么的详细例子。这个例子使用了一个8-连通性表示法。
**第一关。**注意,图像在边框周围有一个填充。这只是为了使计算变得简单一点。通过填充,您在每个像素处考虑的窗口可以保持一致的大小。正如你所看到的,在每个像素上,它只考虑它左边的像素,以及它上面的三个像素。它不考虑所有的 8 个邻居,因为这样你就不必在整个过程中多次比较相同的像素对。
算法的第一遍。由 Dokyoung Kim 为本博客制作
**第二遍。**填充被移除,因为我们在这里不需要它。它检查每个像素,检查等价列表,并在必要时更新标签。
算法的第二遍。由 Dokyoung Kim 为本博客制作
MATLAB 中的代码
以下代码是该算法在 MATLAB 中使用 8-连通性表示的实现。通过仅考虑其左侧和上方的像素,可以容易地将其改变为 4-连通性表示。
MATLAB 中的代码。
我将解释每一部分。
第 1 部分:我们读取二进制图像,并给它一个零填充(大小=1)。
第 2 部分:这是算法的第一遍。它循环遍历每个像素并给它一个标签;它还记录等同物。这些等价关系记录在一个散列表中,这样可以快速方便地查找标签。
第 3 部分:散列表需要一些重组,所以第二遍会更容易。散列表产生如下结果:
5 -> 6 - 7
6 -> 8
我们重组了散列表,这样它会产生如下结果:
8 -> 5
7 -> 5
6 -> 5
第 4 部分:我们可以删除我们的填充,因为我们不再需要它。
第 5 部分:现在我们可以做第二遍。我们检查每个非零像素。如果它是散列表中的一个键,我们只需查看值就可以知道它应该是哪个标签。
现在我们完成了,我们返回斑点的数量和带标签的图像。
备注:
为云实施非接触式按需触发
使用电子邮件让人们远离你的基础设施
在这篇博文中,我将向您展示 AWS 上按需触发器的一种可能实现。为了让你能够修补架构,我在 GitHub 上公布了代码来重现例子。我还在整篇文章中包含了代码片段,以帮助您了解实现细节。
**注意:**这种方法并不是蓝图,更不是最佳实践架构。相反,它旨在提供一些关于云的可能性以及实际实现情况的见解。建造它也很有趣。
即将到来的模式涵盖了两个主要的功能需求:
- 我们的客户 希望在他们认为有必要时触发计算。然而,他们拒绝等待人工支持,既不是联系人的形式,也不是售票系统。另一方面,我们绝对无意让他们直接访问我们宝贵的云。因此,我们需要实现一种允许自动触发而无需访问实际基础设施的机制。
- 此外,我们希望每次客户触发计算时都能得到通知。根据经验,当人们使用我们的产品时,问题往往会出现,如果有必要,我们希望做好灭火的准备。
在这篇博文中,我展示了一个使用电子邮件端点来满足这两种需求的解决方案。客户可以向 AWS 管理的地址写一条消息,然后触发计算。让我给你看看细节。
**注意:**在 AWS 中验证一个域超出了本文的范围,但是需要使用 AWS 电子邮件服务。有关如何操作的信息,请参见此处的和此处的和。
整体架构
为了满足需求,我们需要实现三个功能:
- 我们需要实现一个接收邮件的电子邮件地址。
- 我们需要一个协调机制,它对收到的邮件做出反应,并触发所需的计算。我们使用相同的机制来满足监控需求。
- 我们需要一个实际的计算发生的地方。
这些功能都转化为 AWS 生态系统中的一项服务:
- 简单电子邮件服务(SES) 接收并转发来自外部的邮件。为此,我们需要实现一个合适的规则。
- 简单通知服务(SNS)主题接受转发的邮件并继续传递。由于 SNS 向所有订阅者推送消息,我们不需要实现轮询机制。
- Lambda 的无服务器函数处理我们想要用我们的架构触发的任何事情。
以下是这种简单架构的图形概述:
接收端和处理逻辑通过 SNS 主题连接。SNS 主题通过发布/订阅模式工作。即 SES 将收到的邮件发布到 SNS 主题;Lambda 函数通过订阅自动接收它们。
在这篇博文中,我展示了在 Terraform 中的实现,但是你也可以通过 web 接口或者 CloudFormation 来实现这个架构。如果您以前没有使用 Terraform 的经验,请查看他们的介绍材料,或者忽略细节。请注意,当您使用 web 界面时,AWS 会在后台配置一些细节。这很方便,但是一旦需要调试基础设施,可能会有问题。
实施细节
在开始定义资源之前,我们需要向 Terraform 提供提供者的详细信息。也就是说,我们需要定义一个轮廓和一个区域。在撰写本文时,只有三个地区允许通过 SES 接收电子邮件。
我还为这个脚本定义了两个局部变量: common_tags 和 open_email 。 open_email 是稍后可用于触发计算的那个。这些主要是为了方便和清晰。
让我们深入研究我们架构的三个服务。
简单电子邮件服务
让 SES 工作的两个必要组件是一个规则集和一个规则。请将规则集视为系统使用的所有规则的总称。作为旁注,确保规则集是活动的。根据您的设置,可能已经有了默认的规则集。Terraform 有两个不同的资源,所以请确保使用正确的资源,即名称中带有活动的资源。
规则定义接收电子邮件地址、它所属的规则集以及它触发的操作类型。在我们的例子中,我们使用一个 SNS 动作,它将邮件转发给一个 SNS 主题。
还有 Lambda 操作,我们可以用它来创建一个直接链接,而不需要使用 SNS。然而,这些动作不接收消息体,只接收消息头。如果你需要身体,或者如果你预计在某个时候你需要它,SNS 行动是正确的选择。
正如您可以从位置参数中推断出的,在一个规则中应用几个不同的动作是可能的。
简单通知服务(SNS)
我们的设计使用两个 SNS 主题。SES 使用第一个来发布收到的电子邮件。第二个通知产品团队谁触发了新的计算。创建它们的唯一必要参数是主题的名称:
SNS 在设计上是非常轻量级的。也就是说,配置工作主要发生在发布或订阅其主题的服务上。
注意:您不能使用 Terraform 订阅 SNS 主题的电子邮件地址,因为它们必须经过验证(详情)。
希腊字母的第 11 个
对于我们的目的,单个λ函数就足够了。无服务器功能在计算能力方面有限制,所以您可以考虑切换到其他选项。但是,使用它们作为工作流的入口点通常是一种好的做法。考虑到这一点,让我们来看看启动和运行该功能的不同部分。
我们以 ZIP 文件的形式为该功能提供了代码。幸运的是,Terraform 有一个数据类型来涵盖这一点。我们所要做的就是定义源文件和目标文件。我在 GitHub repo 中提供了一个基本函数。
该功能还需要一个角色和一个附属策略才能工作。在这个例子中,我们希望它向 CloudWatch 写入日志,并向一个 SNS 主题发布信息,以通知产品团队。正如您在代码中看到的,将策略附加到角色是一个单独的资源。该策略需要涵盖 Lambda 函数完成工作所需的一切。例如,如果您想将信息写入 S3 存储桶,您需要扩展策略。
函数资源的规范看起来相当吓人。让我依次向您介绍每个参数:
- 文件名指向包含代码的 ZIP 文件。
- Terraform 比较源代码散列来检测代码库的变化。幸运的是,数据资源自动提供了这个散列。
- 处理程序是 Lambda 函数在被触发时执行的代码中的函数名。这里的是关于 Python 的信息,但是文档也涵盖了 Node.js、Ruby、Java 等等。
- 角色是我们上面定义的执行角色。
- 运行时告诉 AWS 你的代码想要使用哪种编程语言和版本标识符所有可用的运行时都在官方文档中列出。
- 环境变量为你的函数代码提供额外的信息。在我们的示例中,我们使用这样一个变量来传递 SNS 主题的 ARN 进行监控。
所有这些参数的组合如下所示:
剩下的就是触发 SNS 主题和 Lambda 函数之间的连接。要建立这个链接,我们需要实现两个资源:SNS 执行 Lambda 的权限和对 SNS 主题的 Lambda 函数的一个订阅。权限是允许 Lambda 订阅 SNS 主题的先决条件。请将许可视为一种特殊类型的策略。
权限是一个独特的资源,在 Terraform 中是这样的:
另一方面,订阅是一个通用资源,我们定义它应用于 Lambda。也就是说,我们将 Lambda 函数配置为其端点 ,并相应地调整协议:
这些资源涵盖了整个架构。你可以从这里克隆必要的代码。如果你已经在 AWS 注册了一个域名,启动一切需要几秒钟。现在,您可以通过向指定地址发送电子邮件来开始触发流程。确保事后拆除所有东西以避免成本。
如果您正在寻找如何在 AWS 上实现架构的其他示例,您可能会对我以前关于无服务器 Fargate 架构的博客文章感兴趣:
- 全面了解提议的架构及其动机。
- 详细看看它的联网方面。
- 详细了解 IAM 角色和涉及的策略。
- 详细看看实现的服务。
请在评论中告诉我你的想法和经历。我也很乐意在 Twitter 和 LinkedIn 上联系。感谢您的阅读!
为商业智能实现数据湖或数据仓库架构?
解释什么是商业智能,并比较用于商业智能的数据仓库和数据湖架构。
1。什么是商业智能?
作者插图基于塞巴斯蒂安·赫尔曼在 Unsplash 上的照片
在数据行业工作了一段时间后,我觉得每个人对商业智能都有不同的理解。然而,用简单的语言来说,商业智能实际上是利用昨天和今天的数据来对明天做出更好的决策。它可以被理解为确保原始数据被转换成有意义的信息的功能,这些信息提供了洞察力并使决策成为可能。如果我们想一想公司长期以来一直试图做的事情,我们经常会听到投资于技术和工具,这些技术和工具本应通过数据和分析来解决业务问题。然而,商业智能不仅仅是技术和工具。BI 的最终目标是使用数据、技术和工具来创造商业洞察力。
简而言之,BI 涉及收集功能性业务需求,并通过设计数据模型、执行 ETL(提取、转换和加载流程)将它们转化为技术解决方案,从而将来自运营源系统的原始数据转换为有意义的信息,并将该信息带到分析/目标数据库,该数据库可用于以实时自动仪表板的形式进行可视化。在企业中,这是为了根据过去的数据而不是“直觉”做出明智的决策。
得到💬任何数据科学或编程问题的 GPT 式答案。为成千上万的人生成摘要和学习笔记📚只需一次点击即可获得学习资源。👉
[## 面向数据科学家和开发人员的免费学习资源。精选的博客、教程、书籍和…
机器学习和人工智能工程师的培训课程、黑客马拉松、活动和工作
aigents.co](https://aigents.co/learn)
BI 实现的最终输出
毕提供了真相的单一版本
单一版本真理的故事是一个数据字典和数据源的故事,它是关于数据的来源和意义的,应该在整个企业中达成一致。我给你举个例子。我曾经为一家银行工作,这家银行无法回答一些简单的问题,比如:它有多少独立客户?根据您询问的对象,您会收到不同的号码。因此,拥有一个独特的真理来源是必须的。BI 产品为用户提供了实时、自动更新且一致的报告,而不是查看多个编号不同的电子表格并进行对账。
BI 提供描述性分析 —商业智能是描述性的,因为它告诉你现在发生了什么,过去发生了什么。它显示了公司在设定的 KPI 和指标方面的表现。例如,BI 产品可以告诉经理公司的销售情况以及距离既定目标还有多远。这些信息通常以仪表盘的形式提供,包括条形图、折线图等,让用户一眼就能看到最重要的信息。
BI 提供诊断分析— 诊断分析是关于提供深入的见解和回答问题:为什么会发生什么?BI 仪表板提供了深入查看功能,这意味着,从高层次的角度来看,用户可以从不同的角度对信息进行细分,以找出事情发生的原因。
Bi 不会告诉你未来会发生什么。
四种类型的数据分析。来源原理
2。数据仓库、数据湖:写模式和读模式
读取模式与写入模式。来源:intrinity—Hadoop 和 SQL 对比。
数据仓库架构师
商业智能是一个通常与数据仓库联系在一起的术语。如果 BI 是前端,那么数据仓库系统就是后端或实现商业智能的基础设施。因此,我们将首先在使用数据仓库视角的上下文中讨论 BI。
BI 实施的最终目的是将运营数据转化为有意义的信息。原始数据分散在不同的操作数据库中,这些数据库是为应用程序的运行而设计和优化的,而不是用于分析目的。有时,为了得到一个数据字段,您必须进行十次连接!开始了。人们提出了一种叫做中央数据存储的解决方案——数据仓库。
数据仓库解决方案出现于 20 世纪 80 年代,它是为提供信息或见解而优化的。数据仓库是一个目标数据库,它集中了来自所有源系统的企业数据。它是一个关系数据库,因为我们可以使用所谓的物理数据模型中的联合字段连接不同表中的数据。数据库模式定义了不同表之间的关系。典型的 SQL 数据库包括 MySQL 和 PostgreSQL。
数据源通过一个 ETL 过程连接到数据仓库,这个过程被称为提取、转换和加载*。* **数据仓库遵循写模式,**其中的设计符合预期问题的答案。换句话说,数据仓库收集来自具有预定结构和模式的主要应用程序的数据。在数据仓库体系结构中,当我们将数据从数据库 A 转移到数据库 B 时,我们需要预先了解数据库 B 的结构以及如何调整数据库 A 的数据以适应数据库 B 的结构,例如了解数据库 B 中字段的命名约定和数据类型等。
三层数据仓库架构
数据仓库工程师可以使用各种架构来构建数据仓库。常见的数据仓库体系结构基于分层方法。Inmon 推出的众所周知的三层架构包括以下组件:
中转区/登陆区: 这是一个用于从源系统加载批量数据的数据库。其目的是从源系统/主要应用程序中提取源数据,以减少操作系统的工作量。临时区域由反映源系统结构的表组成,包括源系统的所有表和列,包括主键。这里没有应用功能性业务规则。但是,在这一阶段会实施一些硬的技术业务规则,例如,数据类型匹配(字符串长度或 Unicode 字符)。这些技术业务规则并没有改变数据的含义,而只是改变了数据的存储方式。
数据仓库层: 这是应用功能性业务规则的转换发生的地方。这些功能性业务规则修改传入的数据以适应业务需求。业务规则在数据仓库体系结构中实现得越早,它对数据仓库上层的依赖性就越强。在数据仓库建模中,您会经常听到维度建模或数据仓库建模。
数据仓库建模技术是由 Dan Linstedlt 在 20 世纪 90 年代发明的。它基于三种基本的实体类型,包括集线器、链路和卫星。hub 是 Data Vault 模型的主要支柱,它提供了业务用户用来识别业务对象的业务键。商业密钥的例子包括客户标识符、产品标识符、员工工号等。链接将中心联系在一起,并存储不同中心(业务对象)之间的关系。卫星被认为是存储元数据的标准场所。除此之外,卫星存储描述业务对象或关系的所有属性。他们将一段时间内的业务上下文添加到集线器和链路中。然而,这种商业环境会随着时间的推移而变化,拥有卫星的目的也是为了跟踪这些变化并存储历史数据。
在数据仓库之上, 可以有一个数据集市层——用于向最终用户呈现数据的层。我认为称之为信息集市比称之为数据集市更有意义。数据集市的一种常见建模技术是维度建模。维度建模的关键概念包括事实实体和维度实体,这是构建信息集市的标准技术,因为最终用户很容易理解。在维度建模中,您会经常听到星型模式,即一个事实表引用任意数量的维度表。事实表表示发生的事情、订单和事务,同时,维度实体包含构成事实的不同属性/字段。
来源:“什么是湖畔小屋?”通过数据块
加入 中等会员 计划继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
阅读兰楚的每一个故事(以及媒体上成千上万的其他作家)。你的会员费直接支持兰初…
huonglanchu.medium.com](https://huonglanchu.medium.com/membership)
数据湖架构
因为进入数据仓库的数据在存储之前需要经过严格的治理过程,所以向数据仓库添加新的数据元素意味着改变数据的设计、实现或重构结构化存储以及相应的 ETL 来加载数据。由于数据量巨大,这一过程可能需要大量的时间和资源。这就是数据湖概念出现的原因,它将改变大数据管理的游戏规则。
数据湖的概念出现在 2010 年代,用简单的语言来说,就是所有企业的结构化、非结构化和半结构化数据可以而且应该存储在同一个地方。Apache Hadoop 是数据基础设施的一个例子,它允许存储和处理大量结构化和非结构化数据;这实现了数据湖体系结构。
DWH 和数据湖架构的示例。作者根据 MS Azure 文档和 Daniel Linstedt 的 b ook 的插图。
数据湖有一种读取模式的方法。它存储原始数据,并以不需要首先定义数据结构和模式的方式进行设置。换句话说,当我们将数据移动到数据湖时,我们只是在没有任何把关规则的情况下引入数据,当我们需要读取数据时,我们将规则应用于读取数据的代码,而不是提前配置数据的结构。与数据仓库中典型的提取、转换和加载不同,在数据湖的世界中,该过程是提取、加载和转换。数据湖用于成本效率和探索目的。因此,数据湖体系结构使企业不仅能够从经过处理和治理的数据中获得洞察力,还能够从以前无法用于分析的原始数据中获得洞察力。由此,原始数据探索可能会引发业务问题。然而,数据湖最大的问题是,如果没有适当的治理,数据湖会很快变成无法管理的数据沼泽。换句话说,如果不知道湖里的水是怎样的,谁会想去湖里游泳呢?如果业务用户不信任数据湖的数据质量,他们就不能利用数据湖。
来源:Datavercity
最近出现了这样一种趋势,即公司希望以更保守的方式从数据湖架构中获益。这些公司正在摆脱不受监管的“自由进入”方法,转而开发一个更受监管的数据湖。
数据湖可以包含两种环境:勘探/开发环境和生产环境。数据将被探索、清理和转换,以便构建机器学习模型、构建函数和其他分析目的。由转换过程生成的数据(如度量和函数)将存储在数据湖的生产部分。
另一个趋势是,治理数据湖只允许“经过验证的”数据进入,而不是将所有原始数据都倒入湖中。从本质上讲,受治理的数据湖体系结构不限制存储在其中的数据类型,这意味着受治理的数据湖仍然包含多种数据类型,包括非结构化和半结构化数据,如 XML、JSON、CSV。然而,关键是要确保没有数据存储在湖中,而没有在业务术语表中描述和记录,这将使用户对数据的质量和意义有一些信心。
为了提供这一层的治理,必须有一个业务术语表工具来记录数据的含义。更重要的是,需要有一个围绕这一点的治理过程——这完全是关于角色和职责的,例如,谁拥有数据,谁定义数据,以及谁将对任何数据质量问题负责。采用这种方法将非常耗时,因为定义数据本身可能是一个漫长的过程,因为它涉及到企业中不同学科的人员。
数据湖和数据仓库的比较。作者插图。
3。内部部署和云服务
在过去,公司通常依靠自有主机数据中心。在数据处理期间,必须处理大量数据,并且数据处理同时在一组机器上运行,而不是在一台计算机上运行。这个内部存储设置中心需要大量的服务器和大型计算机,以及足够的处理能力来应对高峰时刻。这也意味着,在较安静的时候,大部分处理能力都没有被使用。因此,内部部署系统具有巨大的前期安装成本,但同时,其容量有时只能得到充分利用。不仅如此,维护这一点需要整个专门的应用程序团队来照顾这些主要系统,解决生产问题,并减少停机时间。
本地存储的这些巨大缺点使得使用云的想法变得如此吸引人。许多公司已经将云作为成本优化的一种方式。例如,使用 Azure 和 AWS 在云上构建数据基础设施的兴起完全改变了企业的大数据能力。公司无需在数据中心维护 CPU 和存储,只需租用云资源,例如在需要时租用所需的存储。云提供商负责维护底层基础设施。当云服务用户使用完这些资源后,他们可以归还这些资源,并且只为他们使用的资源付费。这使得扩展变得非常容易,因为如果速度变慢,公司可以分配更多的虚拟机来处理数据。有了云存储,企业不必投资新机器或基础设施,也不必更换老化的服务器。使用云存储的另一个原因是数据库的可靠性。例如,在最坏的情况下,您的数据中心可能会发生火灾。为了安全起见,您需要在不同的地理位置复制您的数据。这带来了一系列的物流问题。基于这些需求,专门从事这类服务的公司诞生了。我们称他们为“云服务提供商”。目前云服务提供商市场有三大巨头。首先也是最重要的,亚马逊网络服务(AWS)是市场领导者,在 2018 年占据了 32%的市场份额。位居第二的是拥有 17% 市场份额的微软 Azure,第三受欢迎的是拥有 10% 市场份额的谷歌云服务。
这些云服务提供商能提供什么?
有很多云服务都有不同的使用案例。最简单的服务是存储,它允许云服务用户上传所有类型的文件到云中。存储服务通常很便宜,因为除了可靠地存储文件之外,它们不提供太多功能。AWS 将 S3 托管为存储服务。Azure 有 Blob 存储,google 有云存储。第二个服务是计算服务,它允许你在云上执行计算。本质上,你可以启动一个虚拟机,并随意使用它。例如,计算服务通常用于托管 web 服务器和执行计算。AWS 提供 EC2 作为计算服务,微软 Azure 使用虚拟机,谷歌有计算引擎。云服务提供商也托管数据库。例如,公司可以通过将他们现有的应用程序转移到运行在 Azure 上的虚拟机来开始探索云。云服务的其他用例是人工智能和机器学习。例如,Azure 提供 Azure 机器学习服务,这是一个基于云的环境,可以用来开发、训练、测试和部署机器学习模型。
4。对于 BI 来说,数据湖可以取代数据仓库吗?
毫无疑问,数据湖比数据仓库有多种优势,特别是在处理大数据和成本效率方面。然而,如果不了解数据湖中的数据质量,业务用户就无法信任这些数据。许多数据科学家通常没有意识到从多个来源获取原始数据是准备好的——干净和高质量的建模通常需要花费 80%的时间。与此同时,不能忽视提供受治理的高质量数据的数据仓库的体系结构和原则。对于定义企业经营状况的一套核心 KPI 仍然有着强烈而持久的需求,这些 KPI 也是报告(尤其是监管报告)所需要的。这种信息需要高度管控的信息。
归根结底,关于公司希望通过数据和技术实现其商业战略的最关键问题必须非常清楚。例如,如果一家公司在其业务和数据战略中不打算涉足数据科学,那么投资允许人工智能和机器学习发生的数据科学/分析平台就没有意义。或者公司可以在一个业务术语表平台中管理他们所有的数据,然后用直升机将它们送入数据湖,但如果他们不知道为什么,我怀疑这是否会创造价值。
感谢您的阅读。我希望这个高层次的概述能帮助您更多地了解 BI 及其背后的架构。
参考资料:
[1]使用 Data Vault 2.0 构建可扩展的数据仓库
[2] 1keydata —数据仓库概念
[3]https://databricks . com/blog/2020/01/30/what-is-a-data-lake house . html
[4].由黄家仪在媒体上发布。
[5].让数据湖发挥作用。
通过链接 Python 迭代器实现数据管道
干净、快速、可扩展和简单的数据处理设计模式
图片来源于罗迪昂·库察耶夫https://unsplash.com/photos/xNdPWGJ6UCQ
在本文中,我将讨论如何通过几个步骤以相对高效和灵活的方式处理 Python 中的项目集合,同时保持代码的整洁。我们将通过创建迭代器来支持其他迭代器,以此类推,从而创建迭代器链。
如果你读过四人帮的书或类似的材料,你可能已经发现了迭代器设计模式。迭代器只是一个带有方法的对象,允许客户端代码使用它来遍历一个项目集合。迭代器的美妙之处在于它们封装(隐藏)了集合的本质。集合可以是数据库表上的一组行,或者是一个复杂的图形模型上的节点,或者只是内存数组中的元素,但是迭代器的用户并不知道它。他们只需要知道他们可以一个接一个地获得元素,直到集合用尽。
Python 中的迭代器和生成器
Python 对迭代器有很好的支持,为了理解它是如何工作的,让我们讨论几个概念。
首先,Python 中的迭代器是具有 next 方法的任何对象,该方法返回集合的下一个元素,直到集合结束,之后,每次调用时都会引发 StopIteration 异常。出于教学目的,让我们做一个非常简单的迭代器
然而,如果你试图在 for 循环中使用这个迭代器,你会得到一个“类型错误:‘我的迭代器’对象是不可迭代的”。这是因为 for 循环需要 iterables ,它们是带有 iter 方法的对象,该方法返回迭代器。大多数集合(列表、字典、集合等。)都是可迭代的。它们不是迭代器(实际上,对它们调用 next()会抛出一个错误),但是它们可以创建一个迭代器,这样客户机代码就可以遍历它们。在我们的迭代器中,只需添加一个返回 self 的 iter 方法就可以使它成为可迭代的。
但是迭代器并不都是这样。另一个很棒的 Python 语法是生成器。生成器是一种迭代器,可以像函数一样创建,只需用 yield 替换 return。让我们看看我们的迭代器示例如何作为一个生成器来翻译
生成器也是可迭代的,可以直接在 for 循环中使用。需要记住的重要一点是,任何地方任何具有单一产出的函数(或方法)在被调用时都不会做任何事情。我的意思是,函数中没有一行代码会被执行。相反,该函数将创建并返回生成器对象。只有当你开始迭代生成器时,代码才会被执行。当您在生成器上调用 next 时,代码将一直执行到第一个 yield。然后它会返回收益率上的值并停止。保存状态,因此 next time:)执行将在该 yield 之后立即恢复,并在到达下一个 yield 语句时停止。与 return 不同,你可以将一个收益率写在另一个收益率之下,两个收益率都会达到。最后,当生成器到达代码末尾时,它将引发 StopIteration 异常。如果 return 语句到达末尾,返回值将包含在异常中,不会被输出。
数据管道
假设我们有一个需要进行一些处理的项目集合。现在,如果处理非常简单,你可以用一个方法完成,使用一些辅助方法等等。然而,在一个地方做所有的事情是一个非常糟糕的软件设计方法,这就是你如何得到有数百行代码的功能,当你不得不阅读它们时,你会想哭
避免这种痛苦的一种方法是让项目通过一个简单的管道。在一个简单的管道中,每个方法接收一个项目,对其进行一些更改,然后返回转换后的项目。每种方法都可以保持简短。此外,它们共享相同的接口(item in,item out ),并且不需要知道它们在管道中的位置,或者在它们到达方法之前发生了什么。这允许您改变转换的顺序,或者甚至客户端可以在管道上挂钩新的方法(向开闭原则致敬)。
定量
我们刚刚介绍的简单管道的一个限制是,转换是在逐项的基础上进行的。但是,许多操作通过对批处理进行操作来提高效率。例如,假设一个远程 API 调用为您提供了处理项目所需的信息。如果 API 提供一个批量端点来发送一组项目并接收一组结果,这将比逐项调用 API 快得多。想想发送 HTTP 请求和等待响应需要多长时间,然后才能继续处理下一项。在我们的简单管道中,我们不能处理批处理,因为该方法接收单个项目,并且必须立即返回转换后的项目。
解决这个问题的一个方法是让每个转换接收一个批处理并返回一个批处理。问题是最方便的批量可能因方法而异。方法 A 可能更适合 500 个批次的产品,但方法 B 只能吞下 100 个。此外,通过将批处理强加给每个人,简单的方法(如正则表达式替换)将变得更加复杂,因为必须解包输入批处理,完成它们的工作,然后组装输出批处理。
这就是为什么迭代器链在这种情况下如此方便。在迭代器链中,每个转换接收一个 iterable 项,并返回一个 iterable 转换项。我们将看到这种模式是如何在保持代码整洁的同时实现批处理的。
让我们从一个现实生活中简单的例子开始。如果逐项读取,从远程数据库服务器读取是一项开销很大的操作。分批提取要快得多。在阅读该示例之前,您可能想知道我正在使用 psycopg 库游标来访问 PostgreSQL 数据库。另外,我假设有一个 format_item 函数将从表数据构建 item 对象。
如果你以前没见过,“yield from iterable”语句是“for element in iterable:yield element”的简称。这个例子函数是…你猜对了,一个生成器函数。它返回一个项目生成器。这个生成器的客户端将一个接一个地获取项目,不需要知道任何关于数据库、批量大小或任何细节的信息。如果你想改变批量大小,什么也不会发生(除了性能问题)。如果你想改变数据库软件,没有人需要知道,如果你甚至想从文件中读取项目,这一切都没问题。这就是迭代器的美妙之处。
你可能已经注意到了这个方法,它不是我描述的迭代器链式链接:它不接收 iterable,尽管它返回一个。这是因为它是数据链的第一环,是数据的原始来源。这个链的最后一个链接也是特殊的,它接收一个 iterable,但不返回一个。因此,让我们展示一个调用 REST API 的普通中间链接。我假设您有一个名为 get_api_results 的方法,它从 api 获取一批结果,并返回这批转换后的项目。
此方法接收一个项目生成器并返回另一个项目生成器。我敢打赌,在阅读代码之后,您可以看到批处理是如何工作的,但是没有一个八卦客户端会知道。
我们说过,链的最终方法也是特殊的。这将只是通过迭代直到耗尽来消耗生成器,并将结果写到某个地方(例如另一个 PostgreSQL DB)。这个方法上的批处理也是一样的。
同时,使用这种方法,不需要批处理的方法可以保持简单:
内存使用
我们之前没有提到它,但是迭代器管道在内存使用方面非常有效。为了理解这一点,你可以把你头脑中的管道描绘成层叠排列的水容器(就像喷泉一样)。上面有一个大水箱(DB 中的项目),下面有一个大水箱(另一个 DB 中的转换项目),水从一个容器流到另一个容器。每个容器的大小是生成器保存的批次。管道使用的内存大约是所有容器中水的总和。通常,这将比上面的大容器少很多,但是当容器真的很大时,这意味着加载所有的项目并在内存中处理它们会有天壤之别。
我想到了一个问题,您应该注意,如果您在一个中间转换中用尽了迭代器,例如,如果您编写了类似于 list(items) 的内容,那么所有的项都将在那个位置加载到内存中。看到许多项正在经历前面的转换,但没有超过那个点,直到所有项都被加载,只有这样它们才能继续向下游移动,这可能会令人困惑。你会在你的喷泉中间创造一个无限大的容器,一个吸啊吸,直到没有其他东西可吸,然后才打开让水继续流动的容器。
其他性能改进
显然,我们的迭代器管道还有很大的改进空间。管道可能会变得复杂,同时将功能分成简单的生成器。您可以想象如何链接生成器,甚至可以根据处理的结果交换它们。然而,最后我将只提到您可以添加的其他性能技巧。请记住,不必要的优化是编程中非常邪恶的根源。只有当性能成为问题时才进行优化,并始终确保优化对性能产生真正的、相当大的影响,否则就不值得。
选择
如果您经常对管道进行更改,尝试这种或那种方法来获得您想要的最终结果,一些繁重的操作可能会降低您的速度。也许你引入了一个新的字符串转换,它只影响 1%的项目,但是你每次运行都要处理所有的项目。一个简单的方法是使第一个生成器具有选择性,这样它只生成可能需要重新处理的项目。我的意思是,如果你知道如何选择这些项目。
缓存
假设您有同样的问题(管道上的更改只影响少数项目),但是您不知道如何选择这些项目。那么你可以使用缓存。在缓存中,您将保存繁重操作的结果,您不想白白运行这些操作。
首先,你需要一种方法来检查你的项目的平等性。一般来说,它可能是所有项目内容的散列。但是你可能有一个特别的,更好的解决方案。您可以在管道上使用较慢的方法,将输入项的散列与构建输出所需的数据存储在一起。当从 DB 中读取时,可以将缓存包含到项中,当项到达 slow 方法时,该方法可以检查该项的哈希是否与缓存的哈希相同。如果是,您可以使用缓存的结果,而不是执行缓慢的操作。
并行化(只是一瞥)
一些 Python 库提供了并行化迭代器的方法。老实说,我还没有认真尝试过,只是用一个名为 joblib 的库做了一点实验,这个库似乎拥有最简单的接口。像这样的代码应该在 6 个处理器上执行。试试看它是否适合你。
这种特殊方法的一个缺点是将所有的项都加载到内存中,所以如果您正在处理一个大的数据池,您可能需要对整个管道本身进行批处理。
埃尔芬!
您刚刚学习了使用迭代器在 Python 中实现数据管道的基础,恭喜您!!!同样,你设法应付我糟糕的写作风格,赞美你。
在这之后,你可能想学习更多关于 Python 中迭代器的知识,但是最重要的是,我邀请你来玩这个奇妙的语言特性,并自己犯一些错误。
为 CIFAR-10 数据集实现深度神经网络
使用 PyTorch 和 CIFAR-10 数据集对初学者友好的图像分类
约书亚·索蒂诺在 Unsplash 上拍摄的照片
神经网络:它们是如何工作的?
神经网络是多功能模型,可以学习任何复杂的模式。这些强大的模型是由多层感知器、卷积网络、序列模型等组成的深度学习的核心。在这个简短的项目中,我将探索 CIFAR-10 数据集并实现一个简单的神经网络(多层感知器)。
神经网络的概念其实很简单。类似于神经元在人脑中如何激发或激活,神经网络中一层内的神经元通过激活函数被激活。这个过程返回输出,该输出将被传递到神经网络的下一层,并且循环重复,直到神经网络结束。这一过程被称为前向传递,即在应用权重和激活函数后,您的数据通过网络向前传递。根据您要解决的是回归问题还是分类问题,神经网络的最终输出层将为回归问题输出一个节点,为分类问题输出多个节点。在这个项目中,我们将对图像进行分类,因此神经网络的输出将在每个类中有一个节点,该节点将通过 softmax 函数获得最终预测。
神经网络的本质不是向前传递期间发生的事情,而是向前传递之后发生的事情。前向传递的最终输出值用于通过在网络中反向传递来更新神经网络的权重。这被称为反向传播,这是一种非常强大的算法,它从最后一层到第一层迭代地更新权重。当使用随机梯度下降优化器时,这是训练神经网络的极其有效的方式。
准备模型训练
如 CIFAR-10 信息页面所述,该数据集由 10 类 60,000 幅 32x32 彩色图像组成,每类 6,000 幅图像。有 50,000 个训练图像和 10,000 个测试图像。由于我们正在处理彩色图像,我们的数据将由基于 RGB 比例分割的数值组成。
来自 CIFAR-10 数据集的样本图像
对于这个项目,我们将使用数据集的 10%作为验证集,90%作为训练集。损失函数将是交叉熵损失,因为这是一个分类问题。优化器将是随机梯度下降,梯度下降的批量将是 128。随机梯度下降是梯度下降的近似。损失函数的梯度被应用于一批所有的训练点,而不是整个集合,这样计算起来快得多。这种对训练样本的随机批量采样引入了大量噪声,实际上有助于防止算法陷入狭窄的局部极小值。
基于 GPU 的基本模型和训练
首先,我们为我们的神经网络创建基础模型,其中我们将为训练过程和验证过程定义函数。
图像分类基础模型
然后,我们将定义 evaluate 函数,以在每个时期后返回我们的模型的进度,并定义 fit 函数,用于更新每个时期的权重。
模型评估函数和拟合函数
由于我们使用 PyTorch,我们可以选择使用 GPU 来训练和评估我们的模型。GPU 在更新和计算权重方面效率更高,因为它们针对矩阵计算进行了优化,而不是 CPU。因此,如果数据可用,我们将把数据转移到 GPU。
将数据移动到 GPU
现在我们准备定义我们的神经网络。对于这个例子,我将为我的神经网络使用 4 个隐藏层,每个层的输入节点分别为 1536、768、384 和 128。正向传递函数将使用整流线性单元激活函数(ReLu ),这是一个将 max(x,0)应用于输入 x 的转换函数。该转换函数对于神经网络很流行,因为它不会激活层内的所有节点,从而使模型成为非线性的。
创建隐藏层和正向传递函数
最后,我们准备对模型进行训练和评估。初始化随机权重后,我们将迭代通过神经网络,并根据我们的学习速率(步长)和时期进行反向传播。这里的目标是获得最终使损失最小化的重量。我们将通过梯度下降来大步前进,直到我们收敛到更接近最小值。一旦我们收敛到更接近最小值,我们将采取更小的步长,直到我们达到尽可能低的最小值。
学习率为 0.1 时的 15 个时期
学习率为 0.01 时的 5 个时期
学习率为 0.001 时的 5 个时期
学习率为 0.0001 时的 5 个时期(损失似乎在这一点上收敛)
我们知道我们已经收敛到最小值,因为我们的精度没有进一步提高,我们的损失没有进一步减少。这是我们的神经网络能想到的最好的模型。这意味着我们的模型有一半以上的时间是正确的。这可能是因为我们的模型着眼于每个像素,而不是整个画面。当我们看图像时,如果我们只看一个像素,相对来说很难概括图像是什么。
损失与时代
准确度与纪元
现在我们知道我们已经得到了可能的最佳模型,让我们用测试集来测试它。
使用测试集进行评估
55%的准确率!这表明,为了找到数据集的最佳模型,选择正确的历元数和学习率是至关重要的,但在这种情况下,使用该模型对任何事物进行分类都是不实际的,因为精度非常低。如前所述,这很可能是因为模型正在逐个查看每个像素。这就是卷积网络的闪光点。这些网络是利用卷积层的深度神经网络,卷积层使用卷积滤波器来处理和产生图像。当我们查看完整的图片或图片的一部分时,人类可以更好地了解图像是什么,卷积网络能够查看图片的一部分,使其保留更多关于图像的信息,而不是查看像素。
参考
理解和实施全卷积网络(FCN)
使用 Keras 在 TensorFlow 中构建、训练和部署小型灵活的 FCN 影像分类模型的教程
卷积神经网络(CNN)非常适合计算机视觉任务。使用在 ImageNet、COCO 等大型数据集上训练的预训练模型。我们可以快速专门化这些架构,使之适用于我们独特的数据集。这个过程被称为迁移学习。然而,有一个陷阱!用于图像分类和对象检测任务的预训练模型通常在固定的输入图像尺寸上训练。这些通常从224x224x3
到512x512x3
左右,并且大多具有 1 的纵横比,即图像的宽度和高度相等。如果它们不相等,那么图像被调整到相等的高度和宽度。
较新的架构确实具有处理可变输入图像大小的能力,但与图像分类任务相比,它更常见于对象检测和分割任务。最近,我遇到了一个有趣的用例,其中我有 5 个不同类别的图像,每个类别都有微小的差异。此外,图像的长宽比也比平时高。图像的平均高度约为 30 像素,宽度约为 300 像素。这是一个有趣的问题,原因如下:
- 调整图像大小很容易扭曲重要的特征
- 预先训练的架构非常庞大,并且总是过度适应数据集
- 这项任务要求低延迟
需要具有可变输入维度的 CNN
我尝试了 MobileNet 和 EfficientNet 的基本模型,但都不起作用。需要一种对输入图像大小没有任何限制并能立即执行图像分类任务的网络。首先打动我的是全卷积网络(fcn)。FCN 是一种不包含任何“密集”层(如传统 CNN)的网络,而是包含执行完全连接层(密集层)任务的 1x1 卷积。虽然密集层的缺失使得可变输入成为可能,但有两种技术可以让我们在珍惜可变输入维度的同时使用密集层。本教程描述了其中的一些技术。在本教程中,我们将经历以下步骤:
- 使用 Keras 在 TensorFlow 中构建全卷积网络(FCN)
- 下载和分割样本数据集
- 在 Keras 中创建一个生成器来加载和处理内存中的一批数据
- 用可变批量维度训练网络
- 使用 TensorFlow 服务部署模型
更新:从头开始建造和训练 FCN 时,你会遇到许多超参数。我写了另一篇文章,其中我给出了超参数优化的一个演练,包括数据扩充,使用了本文中讨论的相同的 FCN 架构。你可以在这里阅读。
使用贝叶斯优化为机器学习模型选择最佳超参数的实用教程。
towardsdatascience.com](/hyperparameter-tuning-with-keras-and-ray-tune-1353e6586fda)
去拿圣经
在我的教程中,这里是上传到 GitHub 的项目链接。请克隆回购,并按照教程一步一步地更好地理解。注意:本文中的代码片段只突出了实际脚本的一部分,完整代码请参考 GitHub repo。
该库中的代码是在使用 Python 3.6.7 的 Ubuntu 18.04.3 LTS 上开发和测试的。以下是软件包…
github.com](https://github.com/himanshurawlani/fully_convolutional_network.git)
1.设计发动机(model.py)
我们通过堆叠由 2D 卷积层(Conv2D
)和所需的正则化(Dropout
和BatchNormalization
)组成的卷积块来构建我们的 FCN 模型。正则化可以防止过度拟合,并有助于快速收敛。我们还添加了一个激活层来整合非线性。在 Keras 中,输入批次维度是自动添加的,我们不需要在输入层中指定它。由于输入图像的高度和宽度是可变的,我们将输入形状指定为(None, None, 3)
。3 代表我们图像中的通道数,对于彩色图像(RGB)来说是固定的。
最小图像尺寸要求
对输入应用卷积块后,输入的高度和宽度将根据kernel_size
和strides
的值减小。如果输入图像尺寸太小,那么我们可能达不到下一个卷积块所需的最小高度和宽度(应该大于或等于内核尺寸)。确定最小输入尺寸的试错法如下:
- 决定要堆叠的卷积块的数量
- 选择任意输入形状,比如说
(32, 32, 3)
,将卷积块与越来越多的通道进行堆叠 - 尝试建立模型并打印
model.summary()
以查看每层的输出形状。 - 确保从最后一个卷积块中得到
(1, 1, num_of_filters)
作为输出尺寸(这将输入到完全连接的层)。 - 尝试减少/增加输入形状、内核大小或步幅,以满足步骤 4 中的条件。满足条件的输入形状以及其他配置是网络所需的最小输入维度。
还有一种数学方法来计算输出体积的空间大小,作为输入体积的函数,如这里的所示。找到最小输入维度后,我们现在需要将最后一个卷积块的输出传递给完全连接的层。但是,任何维度大于最小输入维度的输入都需要汇集起来,以满足步骤 4 中的条件。我们知道如何用我们的主要原料做到这一点。
主要成分
完全连接的层(FC 层)将为我们执行分类任务。我们可以通过两种方式构建 FC 层:
- 致密层
- 1x1 卷积
如果我们想要使用密集层,那么模型输入尺寸必须是固定的,因为作为密集层输入的参数的数量必须被预定义以创建密集层。具体来说,我们希望最后一个卷积块输出的(height, width, num_of_filters)
中的高度和宽度为常数或 1。滤波器的数量总是固定的,因为这些值是由我们在每个卷积模块中定义的。
1x1 卷积的输入维度可以是(1, 1, num_of_filters)
或(height, width, num_of_filters)
,因为它们沿着num_of_filters
维度模拟 FC 层的功能。然而,在 1x1 卷积之后,对最后一层(Softmax 激活层)的输入必须是固定长度的(类的数量)。
主要成分:GlobalMaxPooling2D()/GlobalAveragePooling2D()。Keras 中的这些层将尺寸为(height, width, num_of_filters)
的输入转换为(1, 1, num_of_filters)
的输入,实质上是沿num_of_filters
尺寸的每个过滤器沿高度和宽度尺寸取最大值或平均值。
密集层与 1x1 卷积
代码包括密集层(注释掉)和 1x1 卷积。在使用这两种配置构建和训练了模型之后,以下是我的一些观察:
- 两种模型包含相同数量的可训练参数。
- 相似的训练和推理时间。
- 密集层比 1x1 卷积更容易概括。
第三点不能一概而论,因为它取决于数据集中的图像数量、使用的数据扩充、模型初始化等因素。然而,这些是我在实验中观察到的。您可以通过触发命令$python model.py
来独立运行脚本,以测试模型是否构建成功。
2.下载燃料(data.py)
本教程中使用的 flowers 数据集主要是为了理解我们在训练具有可变输入维度的模型时所面临的挑战。一些测试我们的 FCN 模型的有趣数据集可能来自医学成像领域,其中包含对图像分类至关重要的微观特征,以及其他包含几何图案/形状的数据集,这些图案/形状在调整图像大小后可能会扭曲。
提供的脚本(data.py)需要独立运行($python data.py
)。它将执行以下任务:
- 下载包含 5 个类别的花卉数据集(“雏菊”、“蒲公英”、“玫瑰”、“向日葵”、“郁金香”)。关于数据集的更多细节请点击。
- 将数据集拆分为定型集和验证集。您可以设置要复制到训练集和验证集中的图像数量。
- 给出数据集的统计数据,如图像的最小、平均和最大高度和宽度。
这个脚本下载.tar
文件,并使用keras.utils.get_file()
提取当前目录中的内容。如果你想使用 TensorFlow 数据集(TFDS ),你可以查看这篇教程,它展示了 TFDS 和数据扩充的用法。
3.专用化油器(generator.py)
我们希望在不同的输入维度上训练我们的模型。给定批次和跨批次的每个图像都有不同的尺寸。那么问题出在哪里?让我们后退一步,重新审视我们如何训练传统的图像分类器。在传统的图像分类器中,图像被调整到给定的维度,通过转换成 numpy 数组或张量打包成批,并且这批数据通过模型向前传播。指标(损失、准确性等。)在该批次中进行评估。基于这些度量来计算要反向传播的梯度。
我们不能调整图像的大小(因为我们会失去微观特征)。现在,由于我们不能调整图像的大小,将它们转换成 numpy 数组就变得不可能了。这是因为如果你有一个包含 10 幅尺寸为(height, width, 3)
的图像的列表,它们的height
和width
的值不同,并且你试图将它传递给np.array()
,那么得到的数组将是(10,)
的形状,而不是(10, height, width, 3)
!然而,我们的模型期望输入维度是后一种形状。解决此问题的方法是编写一个执行以下操作的自定义训练循环:
- 我们通过使用
np.expand_dims(img, axis=0)
将(height, width, 3)
转换为(1, height, width, 3)
来传递列表(批处理)中的每个图像。 - 在 python 列表(批处理)中累积每个图像的度量。
- 使用累积的指标计算损耗和梯度。将渐变更新应用于模型。
- 重置指标值,并创建新的图像列表(批次)。
我尝试了上述步骤,我的建议是不要采用上述策略。这很费力,导致复杂和不可持续的代码,并且运行非常慢!人人都爱优雅而经典的*model.fit()``model.fit_generator()
。我们将在这里使用后者!但是首先,化油器。*
化油器是一种将内燃机的空气和燃料以适当的空燃比混合以进行燃烧的装置。这就是我们需要的,空气!我们找到一批图像中最大的高度和宽度,然后每隔一个图像用零填充,这样批中的每个图像都有相等的尺寸。现在我们可以很容易地将其转换为 numpy 数组或张量,并将其传递给fit_generator()
。该模型自动学习忽略零(基本上是黑色像素),并从填充图像的预期部分学习特征。这样,我们就有了一批具有相同图像尺寸的图像,但每批都有不同的形状(由于不同批次图像的最大高度和宽度不同)。您可以使用$python generator.py
独立运行generator.py
文件,并交叉检查输出。
在 Keras 中创建生成器非常简单,这里有一个很好的入门教程。对generator.py
的一个很好的补充是包括对数据扩充的支持,你可以在这里获得一些灵感。
4.点火到认知(train.py)
训练脚本导入并实例化以下类:
- 生成器:我们需要指定由
data.py
创建的train
和val
目录的路径。 - FCN 模型:我们需要指定最终输出层所需的类的数量。
上述对象被传递给train()
函数,该函数使用 Adam 优化器和分类交叉熵损失函数编译模型。我们创建一个检查点回调来保存训练期间的最佳模型。基于在每个时期结束时对验证集计算的损失值来确定最佳模型。我们可以看到fit_generator()
函数在很大程度上简化了代码,令人赏心悦目。
我建议在 Google Colab 上进行训练,除非你的本地机器上有 GPU。GitHub repo 包括一个 Colab 笔记本,它将培训所需的所有内容放在一起。您可以在 Colab 本身中修改 python 脚本,并在您选择的数据集上训练不同的模型配置。完成培训后,您可以从 Colab 的“文件”选项卡下载最佳快照到您的本地机器。
5.使用 TensorFlow 服务部署模型(inference.py)
下载完模型后,您需要使用export_savedmodel.py
将其导出为 SavedModel 格式。在主函数中指定下载模型(.h5
文件)的路径,并使用命令$python export_savedmodel.py
执行脚本。该脚本使用 TensorFlow 2.0 中的新功能,从.h5
文件加载 Keras 模型,并将其保存为 TensorFlow SavedModel 格式。SavedModel 将被导出到脚本中指定的export_path
。TensorFlow 服务 docker 映像需要此 SavedModel。
要启动 TensorFlow 服务服务器,请转到导出 SavedModel 的目录(在本例中为./flower_classifier
)并运行以下命令(注意:您的计算机上必须安装 Docker):
*$ docker run --rm -t -p 8501:8501 -v "$(pwd):/models/flower_classifier" -e MODEL_NAME=flower_classifier --name flower_classifier tensorflow/serving*
上述命令执行以下步骤:
- 如果本地没有
tensorflow/serving
docker 图像,则提取该图像。 - “-p”标志将本地机器上的端口 8501 映射到 docker 容器中的端口 8501。
- “-v”标志将当前目录(由
$(pwd)
指定)装载到 docker 容器中的/models/flower_classifier
中。 - “-e”标志设置 docker 容器中的环境变量,TensorFlow 服务服务器使用该变量来创建 REST 端点。
- “-RM”标志在删除容器时删除与容器关联的任何匿名卷。
- “-t”显示当前终端中的容器日志。您可以按 CTRL+C 返回到您的终端,容器将继续在后台运行。
您可以使用$ docker ps
命令来验证您的容器是否在后台运行。您还可以使用$ docker logs your_container_id
查看容器日志。inference.py
脚本包含构建统一图像尺寸批次并将这些批次作为 POST 请求发送到 TensorFlow 服务服务器的代码。从服务器接收的输出在终端中被解码和打印。
梦想的传递
在本教程中,我们了解了以下内容:
- 为具有可变输入维数的图像分类建立一个标准的完全卷积网络。
- 用一批中相同的图像形状和不同的批形状训练 FCN 模型。
- 使用 TensorFlow 服务 docker 图像部署训练好的模型。
请注意,本教程仅介绍了机器学习工作流程中的单个组件。ML 管道由大量的特定于组织及其用例的训练、推理和监控周期组成。建立这些管道需要更深入地了解司机、乘客和车辆的路线。只有这样才有可能交付梦想的运输工具!
我希望这篇教程对你构建下一个令人敬畏的机器学习项目有所帮助。我很乐意听取您对资源库的建议和改进,也可以随时提出 GitHub 的问题。如果你发现文章中有任何错误或遗漏的信息,请在评论区告诉我。谢谢!
实现朴素贝叶斯分类器
在 Python 中使用真实世界的数据集
凯文·Ku 在 Unsplash 上的照片
在的监督学习(分类)的背景下,朴素贝叶斯或者更确切地说贝叶斯学习作为评估其他学习算法的黄金标准,同时作为一种强大的概率建模技术。
在这篇文章中,我们将讨论朴素贝叶斯分类器的工作原理,通过将它应用于真实世界的数据集,用 Python 实现。
T 岗位更宽泛地分为以下几个部分:
- 数据预处理
- 训练模型
- 预测结果
- 检查模型的性能
上述部分可以进一步划分如下:
→ 数据预处理
- 导入库
- 导入数据集
- 将数据集分成训练集和测试集
- 特征缩放
→训练模型
- 在训练集上训练朴素贝叶斯模型
→预测结果
- 预测测试集结果
→检查模型的性能
- 制作混淆矩阵
→可视化
- 可视化混淆矩阵
在我们开始深入研究代码本身之前,我们需要谈谈数据集本身。对于这个实现,我们将使用 这 20 个新闻组的文本数据集 **。**这个数据集是公开的,目的是人们可以学习和磨练他们的机器学习技能。
我们将使用 scikit-learn (sklearn)作为机器学习库和数据集本身的存储库。这就是 sklearn 的网站告诉你的数据集。
20 个新闻组数据集包含大约 18000 个关于 20 个主题的新闻组帖子,分为两个子集:一个用于培训(或开发),另一个用于测试(或性能评估)。训练集和测试集之间的划分基于在特定日期之前和之后发布的消息。
数据集特征:
https://scikit-learn . org/stable/datasets/index . html # news groups-dataset
注意:由于原始数据是自然语言文本,我们不能直接处理它。在开始处理数据之前,我们需要将数据转换成数字。有多种方法可以做到这一点,即:
选择矢量器取决于解决方案的设计者和问题本身。出于本文的目的,我将使用 CountVectorizer。我已经为所有矢量器提供了 sklearn 文档的链接。你当然可以在那里和网上详细看看。
现在,我们已经谈了一点数据集,让我们从代码本身开始,一步一步来。
→ 数据预处理
- 导入库
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import confusion_matrix, plot_confusion_matrix
import matplotlib.pyplot as plt
上述库的使用/要求(按顺序解释)如下:
a)从 sklearn 本身导入数据集
b)导入计数矢量器以将原始自然语言文本转换成机器可理解的数字
c)导入朴素贝叶斯分类器,在这种情况下,我们使用高斯朴素贝叶斯
d)引入混淆矩阵方法来检查模型的性能并将其可视化。
e)用于混淆矩阵的可视化
2 & 3.导入数据集并将数据集分成训练集和测试集
data_train = fetch_20newsgroups(subset='train', categories=None,
remove=('headers', 'footers',
'quotes'))data_test = fetch_20newsgroups(subset='test', categories=None,
remove=('headers', 'footers',
'quotes'))X_train = data_train.data
y_train = data_train.targetX_test = data_test.data
y_test = data_test.target
data_train
包含来自数据本身的训练集。fetch_20newsgroups
中传递的参数可以理解为:
I)子集-定义训练或测试集
ii)类别—数据集包含 20 个类别或分类标签。通过在参数中提供以下类别的列表,可以使用数据集的子集:
from pprint import pprint
pprint(list(newsgroups_train.target_names))
输出是:
[‘alt .无神论’,
'comp.graphics ',
'comp.os.ms-windows.misc ',
'comp.sys.ibm.pc.hardware ',
'comp.sys.mac.hardware ',
'comp.windows.x ',
'misc.forsale ',
'rec.autos ',
’ rec . motors ',
'rec.sport.baseball ',
'rec
iii)移除—数据集中的文本包含页眉、页脚和引号,但我们希望对数据主体应用模型。
通过调用data_train
和data_test
上的.data
和.target
可以分离自变量和因变量,得到分叉的训练和测试数据。
类别/y_train
数组中的值表示上述类别列表的索引。
在应用 CountVectorizer 之前进行 X_train
4.特征缩放
vectorizer = CountVectorizer()
X_train = vectorizer.fit_transform(X_train)
X_test = vectorizer.transform(X_test)
创建 CountVectorizer 类的对象,然后在X_train
和X_test
数据上拟合矢量器对象。
应用计数矢量器后的 X_train
→训练模型
- 在训练集上训练朴素贝叶斯模型
classifier = GaussianNB()
classifier.fit(X_train.toarray(), y_train)
制作 GaussianNB 类的对象,然后根据X_train
和y_train
数据拟合分类器对象。这里的.toarray()
与X_train
用于将稀疏矩阵转换为密集矩阵。
→预测结果
- 预测测试集结果
y_pred = classifier.predict(X_test.toarray())
在分类器对象上调用.predict
方法,并传递X_test
来预测之前未见过的数据上的训练模型的结果。这里的.toarray()
与X_test
用于将稀疏矩阵转换为密集矩阵。
→检查模型的性能
- 制作混淆矩阵
cm = confusion_matrix(y_test, y_pred)
print(cm)
→可视化
- 可视化混淆矩阵
plot_confusion_matrix(classifier, X_test.toarray(), y_test, display_labels=['alt.atheism',
'comp.graphics',
'comp.os.ms-windows.misc',
'comp.sys.ibm.pc.hardware'], cmap=plt.cm.Blues)
为了形象化的目的,我把类别的数量限制在 4 个,这样它就能被正确地看到。
现在,机器学习模型的整个管道已经完成,我希望我能够分享一些知识。这是一个非常基本的机器学习管道,但当你想建立更好更复杂的机器学习模型时,它在建立基础方面相当重要。我希望在未来带来更多动态和复杂的模型,敬请期待。
这里有一个链接到完整的 jupyter 笔记本。
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/tarunlnmiit/machine_learning/blob/master/naive_bayes-20newsgroup.ipynb)
我正在免费赠送一本关于一致性的电子书。在这里获得你的免费电子书。
感谢您的阅读。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的链接,我会赚一小笔佣金,不需要你额外付费。
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
tarun-gupta.medium.com](https://tarun-gupta.medium.com/membership)
你可以在这里阅读我的更多帖子:
[## 标记故事列表的快速链接—感谢您的访问
我也有一份以快节奏出版为目标的出版物。读书成为作家。
tarun-gupta.medium.com](https://tarun-gupta.medium.com/thank-you-for-visiting-my-profile-9f708062c75e)
使用朴素贝叶斯和 TextAnalysis.jl 创建垃圾邮件过滤器
Christopher Gower 在 Unsplash 上拍摄的照片
你好!今天,我将告诉你更多关于我做了什么来制作一个垃圾邮件过滤器,使用朴素贝叶斯从 UCI 机器学习的 kaggle 上的数据集中检测垃圾邮件数据,以及在 Julia 上使用 TextAnalysis.jl 。
我从查看 TextAnalysis.jl 的文档开始,以了解更多关于 NaiveBayes 分类器的工作原理。
using TextAnalysis: NaiveBayesClassifier, fit!, predict
m = NaiveBayesClassifier([:legal, :financial])
fit!(m, "this is financial doc", :financial)
fit!(m, "this is legal doc", :legal)
predict(m, "this should be predicted as a legal document")
我运行了文档中的示例,并了解到函数 NaiveBayesClassifier 接受一组可能的类的参数,这些数据可能属于这些类。
在这种情况下,是:legal
和:financial
。我还了解到,我们通过用fit!
函数拟合相关数据来训练模型,该函数接受模型本身的参数、我们试图训练的数据串以及数据所属的类。这里的数据是一个字符串,例如“this is financial doc”
,在本例中,它所属的类是:financial
。
最后,我了解到predict
函数允许我们输入一串数据,并使用 NaiveBayesClassifier 算法,根据使用fit!
函数之前训练的数据串来预测该字符串属于哪个类。predict
函数接受模型本身的参数以及我们试图预测的数据字符串。
我解决这个问题的第一个方法是,我认为首先导入所有的数据是一个好主意。因为我有使用 CSV.jl 和 DataFrames.jl 软件包的经验,所以我熟悉数据的导入。
using CSV, DataFrames
spamdata = DataFrame(CSV.read("spam.csv"; allowmissing=:none))---------------------------------------------------------------julia> showall(spamdata)
5572×5 DataFrame
│ Row │ v1 │
│ │ String │
├──────┼────────┤
│ 1 │ ham │
│ 2 │ ham │
│ 3 │ spam │
│ 4 │ ham │
│ 5 │ ham │
│ 6 │ spam │
│ 7 │ ham │
│ 8 │ ham │
│ 9 │ spam │
│ 10 │ spam │
下图显示了包含数据的原始.csv
文件的结构。
csv 文件有两列。v2
是我们想要用来训练的数据串,v1
是特定数据串的类,对应于v2
。
我想要一种方法来循环遍历文件的每一行,并在一个条件下拆分 ham 数据,在另一个条件下拆分 spam 数据,这样,当进行训练时,我将能够使用fit!
函数来训练ham
数据,这需要我指定该数据的类别。
for row in eachrow(spamdata)
if row.v1 == "ham"
println("ham")
elseif row.v1 == "spam"
println("spam")
end
end
这是一个成功,我最终有火腿和垃圾邮件被打印!
ham
spam
ham
spam
ham
ham
spam
spam
ham
spam
⋮
既然已经解决了,我可以使用 NaiveBayesClassifier 函数来定义我的模型了。我想定义两个类,:ham
和:spam
。
using TextAnalysis: NaiveBayesClassifier, fit!, predict
m = NaiveBayesClassifier([:ham, :spam])
接下来,我想开始训练我的模型。正如我们从最初的.csv
文件的结构中看到的,v2
是我们试图训练的字符串。将for
循环与fit!
函数结合起来,我做了以下工作来尝试训练我们所有可用的数据。
using CSV, DataFrames
using TextAnalysis: NaiveBayesClassifier, fit!, predictspamdata = DataFrame(CSV.read("spam.csv"; allowmissing=:none))
global m = NaiveBayesClassifier([:ham, :spam])
for row in eachrow(spamdata)
if row.v1 == "ham"
fit!(m, row.v2, :ham)
elseif row.v1 == "spam"
fit!(m, row.v2, :spam)
end
end
但是当我运行它时,我得到了下面的错误:LoadError: Base.InvalidCharError{Char}('\xe5\xa3')
就在那时,我意识到数据集在v2
列的某些字符串中有无效字符。为了消除这个错误,我们需要使用下面的函数过滤掉不支持的字符:filter(isvalid, <string>)
for row in eachrow(spamdata)
if row.v1 == "ham"
fit!(m, filter(isvalid, row.v2), :ham)
elseif row.v1 == "spam"
fit!(m, filter(isvalid, row.v2), :spam)
end
end
当我将字符串row.v2
替换为filter(isvalid, row.v2)
并再次运行程序时,没有出现错误。因此,该模型被成功训练,到目前为止一切顺利!
在 REPL 中,实际上什么都没有打印出来,因为没有错误,程序中没有任何东西被打印出来。为了全面测试模型是否有效,我们可以尝试创建一个预测,并打印出预测的结果,以查看训练是否真正完成。
prediction1 = predict(m, "hello my name is kfung")
prediction2 = predict(m, "text 31845 to get a free phone")
println(prediction1)
println(prediction2)
在这里,我创建了两个预测,其中第一个看起来像一个火腿消息(因为它看起来不可疑),第二个看起来像一个垃圾邮件,以测试模型。
我得到的结果如下:
Dict(:spam => 0.013170434049325023, :ham => 0.986829565950675)
Dict(:spam => 0.9892304346396908, :ham => 0.010769565360309069)
正如我们所知,预测非常准确,因为第一个预测的:ham
值接近 1,这意味着它最有可能是一封垃圾邮件,第二个预测的:spam value
值接近 1,这意味着它最有可能是一封垃圾邮件。正如我们所料。
但是对于不熟悉字典或 Julia 语法的用户来说,他们可能会对上面的字典的含义感到困惑。我修改了代码,以便它检查字典中的:spam
和:ham
值,并打印出这两个值中较大值的类。
prediction = predict(m, "hello my name is kfung")
if prediction[:spam] > prediction[:ham]
println("spam")
else
println("ham")
end
正如我们所料,这个程序的结果是字符串“ham”
,因为它预测的:ham
值大于:spam
值,因此它更有可能是一个火腿消息。
最后,我将所有内容都包装在一个函数中,该函数以一个字符串作为参数,这样当您用一个字符串调用该函数时,如果模型预测它是垃圾邮件,它将输出“spam”
,如果模型预测它不是垃圾邮件,则输出“ham”
。
using CSV, DataFrames
using TextAnalysis: NaiveBayesClassifier, fit!, predictfunction checkspam(msg::String)
spamdata = DataFrame(CSV.read("spam.csv"; allowmissing=:none))
m = NaiveBayesClassifier([:ham, :spam])
for row in eachrow(spamdata)
if row.v1 == "ham"
fit!(m, filter(isvalid, row.v2), :ham)
elseif row.v1 == "spam"
fit!(m, filter(isvalid, row.v2), :spam)
end
end
prediction = predict(m, msg)
if prediction[:spam] > prediction[:ham]
println("spam")
else
println("ham (not spam)")
end
end
最后,我意识到每次对信息进行分类时,我是如何训练模型的。这使得程序在运行时效率不高,因为每次我们试图使用模型进行预测时,它都要从头到尾检查 5600 行数据。相反,我们可以将模型置于函数之外(这样它只在开始时运行一次),并将模型存储在一个全局变量中,这样以后它就可以使用存储在全局变量中的预训练模型来分类任何其他消息。
using CSV, DataFrames
using TextAnalysis: NaiveBayesClassifier, fit!, predictspamdata = DataFrame(CSV.read("spam.csv"; allowmissing=:none))
global m = NaiveBayesClassifier([:ham, :spam])
for row in eachrow(spamdata)
if row.v1 == "ham"
fit!(m, filter(isvalid, row.v2), :ham)
elseif row.v1 == "spam"
fit!(m, filter(isvalid, row.v2), :spam)
end
endfunction checkspam(msg::String)
prediction = predict(m, msg)
if prediction[:spam] > prediction[:ham]
println("spam")
else
println("ham (not spam)")
end
end
总的来说,我认为这是创建新的垃圾邮件过滤器的一次非常成功的尝试。我学到了更多关于如何应用朴素贝叶斯为概率分类的独立假设建模的知识。朴素贝叶斯目前仍然是文本分类的一个相当受欢迎的选项,这就是一个例子,并且可以与其他模型训练方法形成对比,例如逻辑回归,其中数据是有条件训练的。这个模型还有其他应用,比如情绪分析,这是我将在另一篇文章中重点介绍的,所以请继续关注!
非常感谢您的阅读!