最简单的方法来丰富你的熊猫数据框架
作者图片
熊猫真的很棒,有深度,有文档。您可以快速导航并了解有关重塑数据、处理不同格式(如文本、时间序列、分类数据、合并、可视化等)的一切。但是,如果我说你的数据框架可以通过 https 与外部 API“对话”,你会怎么想?
如果你想一想,这里没有使用革命性的技术。我敢打赌,如果你现在停止阅读,思考几分钟如何让你的数据框架从“外部世界”获取数据,你会很快意识到事情很简单:
df.apply(lambda i: get_result(i[‘input_column_1’], i[‘input_column_2’]), axis=1)
其中 get_result()是一个与数据框架之外的世界“对话”的函数。在本例中,它采用 2 个参数作为输入:数据帧中的 2 列“输入 _ 列 _1”和“输入 _ 列 _2”。
这种方法有什么好处?
首先你需要意识到在 get_result()函数背后可能隐藏着什么。这里有很多选项,简单地说,任何将从外部 api、数据库或您能想到的任何其他东西获取数据的客户端库。
这里唯一的限制是您的结果必须以某种方式放回 dataframe 行:
df[‘result’] = df.apply(lambda i:
get_result(i[‘input_column_1’], i[‘input_column_2’]), axis=1)
注意:我们可以简化我们的。跳过 lambda: 应用()函数
*df.apply(get_result, axis=1)*
和处理熊猫系列里面的 get_result()函数。我更喜欢保持客户端 API 逻辑干净,避免不必要的 Pandas 处理,特别是如果来自其他系统的预测可能没有在输入上使用 Pandas 系列。
还不见效益?
这里最重要的是,您不必将数据帧转换为其他格式,或者将其保存在 API 客户端类可以从中访问数据、获取 API 结果并创建结果与原始数据帧合并的数据帧的地方。你只是一直呆在数据框“领域”里!
有些事情你必须知道。例如,get_result()必须处理出错且没有数据返回的情况。您需要使它适应您的情况,但是您可以尝试做的最简单的事情是实现自动“失败时恢复”。
另一种方法是用空值填充数据帧的所有失败结果。然后再次调用 apply()并只处理失败的行:
df[df.result.isnull()].apply( … )
“分批谈”
到目前为止一切顺利,但是如果您的外部 API 批量接受并返回数据会怎么样呢?假设你的数据框架有一个文本列,你想用基于该文本的情感分析预测来丰富你的数据框架。在这种情况下,您必须对数据帧进行切片,并将每批数据传递给我们的 API 客户端函数:
batch_size = 100for i in range(0, len(df), batch_size):
df.loc[df.index[i:i+batch_size], 'sentiment_prediction']
df.loc[df.index[i:i+batch_size],'text_column']\
.apply(lambda i:
predict_sentiment(i['text_column']), axis=1)
我们的数据帧正在按 batch_size 进行切片,其中“text_column”被发送到 be predict _ perspective()函数。
这两种解决方案都有一个问题。apply()逐行执行,或者在批处理的情况下逐批执行。换句话说,API 调用是按顺序执行的。
加速发展/走向“大数据”
像往常一样,这可以通过多种方式完成,但我将使用 Dask 来完成这项工作。
注意:这篇文章不是关于 Dask 的介绍,如果你不熟悉它,去看看关于 dask.org的真正友好的文档。
只需几个额外的步骤,您就可以将数据帧转换为 Dask 数据帧,从而处理内存不足的数据集。Dask 的伟大之处在于它试图尽可能地“模仿”熊猫 API。您可以在您的笔记本电脑上使用“本地集群”进行开发,或者使用多台机器相当容易地设置实际的生产就绪集群。
现在,使用 Dask dataframe 可以加快速度!假设我们的 API 可以同时处理多个请求,但是我们也应该尽量不要同时处理太多的请求。
因为 Dask 数据帧仅仅是 Pandas 数据帧的分区列表,所以对其调用 apply()将在每个分区上分别分配工作和并行执行。
让我们看看我们目前有多少分区:
*>> df.npartitions
14*
让我们更改块大小以获得 50 个分区
*>> df = df.repartition(npartitions=50)
>> df.npartitions
50*
现在调用 apply()将生成至少 50 个并行任务!让我们修改我们的代码以适应 Dask (注意:这里的 df 是 Dask dataframe,不是 Pandas dataframe) :
*df[‘result’] = df.apply(lambda i:
get_result(i[‘input_column_1’], i[‘input_column_2’]), axis=1)*
有什么变化?没什么!甜甜:)
注意:这里假设您正在使用有许多工作人员的集群。如果你在你的笔记本电脑上这样做,把你的分区数量设置为内核数量。
扩展批次
这里事情变得有点复杂,因为我们需要:
- 将我们的 Dask 数据帧分割成单独的 Pandas 数据帧(分区)
- 在每个分区上创建批处理
- 更新每个熊猫数据帧,这将导致更新整个 Dask 数据帧
Dask 没有提供多少方法来做到这一点。我将使用 map_partitions(),它只是在每个分区上应用函数:
*batch_size = 100def make_per_dataframe_predictions(pdf): # pdf is Pandas dataframe
for i in range(0, len(pdf), batch_size):
pdf.loc[pdf.index[i:i+batch_size], 'sentiment_prediction'] = \
pdf.loc[pdf.index[i:i+batch_size], ['text_column']]\
.apply(lambda i: get_result(i['text_column']), axis=1)
return pdfdf.map_partitions(make_per_dataframe_predictions).compute()*
注意:pdf.loc 索引在额外的括号中有“text_column ”,即使我们传递的是单列。因为我们想。在 dataframe 上应用(),如果没有括号,我们将对 Pandas 系列进行操作。
另外,请注意 Dask 要求。compute()实际结果。
警告词
在盲目应用上面介绍的任何解决方案之前,你需要考虑它如何适合你的情况。
一般来说,特别是在处理真正的大数据时,我建议避免这种新的“强大的知识”,并使用以下步骤将您的管道分成单独的任务:
- […在外部 API 的数据“丰富”您的数据框架之前,完成所有步骤…]
- 存储您希望用作 API 输入的数据帧和(可能是单独的)输入
- 编写单独的任务来加载预测的输入,生成预测并存储它们
- 加载你的数据框架,将你的预测加载到另一个数据框架中,然后合并它们
是的,看起来需要更多的工作,但是这种方法更安全,并且允许对每个步骤有更大的控制。
记住:这篇文章的目的不是向你展示你的数据框架如何与外界交流的实际技术。更多的是心态,如果运用得当,可以为你节省很多时间。
面向初学者的 Android MVP
让我们来理解这种设计模式是如何以更少的努力提供更大的维护大型项目代码的便利性的
在 Android 中,由于 Android 活动与 UI 和数据访问机制紧密耦合,我们遇到了一个问题。但是应用程序代码必须易于扩展、维护、阅读和测试,等等。为了实现所有这些,我们将使用 MVP 架构模式,它将应用程序分为三层。三层是模型、视图和演示者。
模型视图演示者(MVP)
- 模型:它处理我们应用程序的数据部分
- 视图:负责按照演示者的指示,用相关数据布置视图
- 展示者:它是连接模型和视图的桥梁
注意 : Presenter 是模型和视图之间的中间人,这意味着视图和模型不能直接相互交流。
使用 MVP 的好处
- 将用户界面的代码与业务逻辑分开
- 阻止模型(数据)和用户界面之间的直接通信
- 更改代码的一部分不会影响另一部分。
- 代码对于单元测试来说变得容易多了
现在把所有的理论部分放在一边,开始研究编码部分
我们将创建登录应用程序,以尽可能保持简单
在 ActivityMain.java 的课上,我简单地拿了两个编辑文本和一个登录按钮。我在这个活动中实现了LoginView
interface
,并覆盖了所有的方法——这些方法将基于用户提供的凭证被调用。如果username
和password
不是用户输入的,那么“Username can’t be empty”
消息将显示给用户。我正在创建人工延迟来显示进度条。
ActivityMain.java
这里我声明了不同目的的方法,比如用户名错误和密码错误等等。
LoginView.java
这是最重要的类,有助于模型和视图之间的交流。
LoginPresenter.java
这个类包含了应用程序的业务逻辑,并且这个类决定了哪个方法将被调用。
LoginInteractor.java
输出:
当输入字段为空时:
当用户名和密码都正确时:
结论
MVP 架构模式没有普遍公认的定义。每个人都根据自己的需要实现这个模式,但是我们的目标应该是将代码分成至少三层,就像我们上面讨论的那样。
我希望你喜欢读这篇文章,你也可以访问我的 网站 ,在那里我会定期发布文章。
订阅 我的邮件列表,以便在您的收件箱中直接获得我的文章,并且不要忘记关注我自己在 MediumThe Code Monster上发表的文章,以丰富您的技术知识。
了解你的作者
希曼舒·维尔马毕业于印度勒克瑙的 APJ 阿卜杜勒·卡拉姆大学博士。他是 Android & IOS 开发人员、机器学习和数据科学学习者、金融顾问和博客作者。
使用 Python 简化 KNN 算法并提供编码说明
K 近邻,机器学习中最简单的分类算法之一
M 机器学习就是识别模式,创造自己的算法,并不断进化。模式通常由机器创建,它使用各种技术和算法。对于机器学习中的分类问题,k 近邻算法是一种最简单而有效的算法。它既可用于分类,也可用于回归问题,即它可以对任何特定事件/属性进行分类,甚至可以预测其值。这种方法因为比其他复杂的算法简单而被采用。它测量数据点之间的距离。测量距离会形成一个模式,以创建两点之间的关系。这就是它决定新数据点应该放在哪个组中的方式。
图片来自 nccalculators.com
如果我们需要计算上图中提到的两点之间的距离,比如说(XA,YA)
和(XB,YB)
,那么可以使用一个简单的公式
以上是测量两点间距离的欧几里得方法。这种距离测量也可以有其他方式。遵循上述方法来计算各种数据点之间的距离。KNN 有两个独特的属性
- **懒惰学习算法:**它被称为懒惰学习者,因为它没有任何专门的训练阶段,而学习意味着它没有预定义的规则。它只是消耗所有的数据,同时对一个新的数据点进行分类,并制定自己的规则。
- **非参数学习算法:**它采用非参数方法,这意味着它不做任何假设。它不担心选择哪些特性。这种方法非常适合您事先不了解传入数据的情况。
由于这种无忧无虑的分类,KNN 算法各有利弊
优点:
- 对于大量数据来说,它非常有用
- 它在选择变量方面很灵活(没有假设)&可以适应大量的函数形式。
- 它可以在预测时提供更好的性能
缺点:
- 在估计映射函数时,它消耗大量的数据
- 它可以给出过拟合模型输出
- 由于训练时使用了大量参数,所以速度较慢。
- 不平衡数据集可能会给这种算法带来麻烦。
绘制在 2D 坐标系上的样本数据集
在 K 个最近邻中,K 的值表示与我们的新数据点具有最小距离的最近邻的数量。如果我们说 k=3,这意味着我们必须为我们的新数据点找出 3 个最近的邻居。如果有 3 个相邻点,其中一个来自红色数据集,另外两个来自蓝色数据集,那么新的数据点将被标记为蓝色。越多的邻居意味着分类越清晰,但是它增加了分类的复杂性和时间范围。k 值还有一个更重要的方面,它应该是一个奇数总是为了更好的分类,否则如果两种颜色的邻居数量相等,那么它将是一个平局。
让我们举一个例子,用 python 语言展示算法是如何工作的。为了在 python 中工作,我们需要调用某些库
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
这里的数据集是 python 框架上一些基本样本数据集的存储库。这些数据集可以用来做一些简单的实际建模。 Iris 是那个存储中非常有名的数据集。
sklearn 是 python 中最著名的数据科学包,拥有机器学习所需的几乎 70%的功能。在这里,我们导入一个函数,用于将数据拆分为测试和训练 (train_test_split)和 KNN 分类的 KNeighborsClassifier 。为了检查我们模型的准确性,我们再次使用来自同一个著名库包的 accuracy_score 。
iris = load_iris()
x= pd.DataFrame(iris.data,columns=iris.feature_names)
y = pd.Categorical.from_codes(iris.target, iris.target_names)
我们已经调用了 iris 数据集,并将目标变量从数据帧的其余部分中分离出来。这里 y 是目标变量,x 是将用于建模的数据集。
x.info()
来深入了解我们的数据集。它对正在处理的数据帧提供了相当多的洞察力。这是它的输出
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 sepal length (cm) 150 non-null float64
1 sepal width (cm) 150 non-null float64
2 petal length (cm) 150 non-null float64
3 petal width (cm) 150 non-null float64
dtypes: float64(4)
memory usage: 4.8 KB
使用 matplotlib 绘制的虹膜数据集
现在,我们将数据分成 70:30 比例的训练和测试数据。这个比例可以由程序员根据自己的意愿来选择,但更明智的做法是为训练数据提供一个合适的容量,并且还应该有合理的测试容量。
#Prepare data for classification process
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=0)
现在,我们准备使用下面的命令制作一个 KNN 模型。您可以在代码中看到,我们使用了 p 值为 2 的闵可夫斯基距离度量,即 KNN 分类器将使用欧几里德距离度量公式。
#Create a model
KNN_Classifier = KNeighborsClassifier(n_neighbors = 6, p = 2, metric=’minkowski’)
现在我们将使用 KNeighborClassifier 训练模型,这里 k 的值是 6。(是的它是偶数,看复杂程度)
KNN_Classifier.fit(x_train, y_train)
在对模型进行训练之后,将通过使用以下命令,使用同一个经过训练的模型对测试数据进行测试
pred_test = KNN_Classifier.predict(x_test)
基于为所有数据点计算的距离,我们找出有多少邻居指示哪一侧。一旦选择了顶部最近的邻居,我们检查邻居中投票最多的类
#check the accuracy of your predicted classifier
acc = accuracy_score(y_test, pred_test)
通过使用这个命令,我们可以检查我们设计的模型的准确性。好的价值意味着更好的建模。为了识别过拟合模型特征,必须检查测试数据和训练数据的准确性。
结论:
该模型为其下一个任务做好了准备,并且不需要再次训练。如果我们输入更多的数据,模型会发展得更好,结果也会更好。我们必须确保所提供的数据是干净的,缺失值已被处理,因为机器无法正确处理缺失值数据集。
使用 Alibi Detect 简化图像异常检测
入门
离群 名词
out li er | \ ˈau̇t-ˌlī(-ə)r
1:在数值上明显不同于样本中其他人的统计观察值
2:在某一特定群体、阶级或类别中非典型的人或事物
O 异常检测是对与大多数数据有显著差异的数据集元素的识别。这些元素被称为异常值,检测它们有各种动机,这取决于每个案例的领域。一个典型的例子是欺诈检测,其中金融数据集中的异常值可能表示欺诈活动,如使用被盗信用卡的交易。异常值检测也可以用于网络入侵检测,其中异常值是可疑网络活动的记录,指示可能试图获得未授权的访问。上述情况是应用于表格数据的异常值检测的示例,但是它也可以用于其他数据类型,例如图像。工业制造中的质量控制就是使用异常值检测来识别产品图像中的缺陷。
异常值检测有三种基本方法。首先,无监督方法既不需要关于离群值的任何信息,也不需要关于正常元素的任何信息。另一方面,半监督方法需要在一组正常元素上进行训练,并且能够检测那些显著不同的元素。最后,监督方法需要一个带有内点和外点的标记数据集,使它们类似于分类算法,特别适合不平衡的类。关于离群点检测的更详细的介绍,我建议你阅读这篇文章。
不在场证明检测
Alibi Detect 是一个开源 Python 库,用于异常、敌对和漂移检测,包括各种强大的算法和技术。它还支持各种数据类型,如表格、时间序列和图像。这里列出了 Alibi Detect 包含的所有异常值检测算法,后面的表格显示了每种算法的可能用途。关于每个算法的更多信息,以及相关的研究论文,都包含在链接的文档页面中。
离群点检测算法
Alibi 检测库中包含的每个算法的建议用途
自动编码器
自动编码器是一种人工神经网络,由编码器和解码器组成。编码器将输入转换为潜在空间表示,而解码器接收该表示并输出输入数据的重构。自动编码器有各种各样的应用,比如降维和图像去噪,但是在本文的范围内,我们将把重点放在图像异常检测上。
一个简单的自动编码器架构图来自 Comp Three Inc
在图像异常值检测的情况下,这种类型的神经网络被称为卷积自动编码器,因为编码器和解码器部分由卷积神经网络组成。离群点检测自动编码器在图像数据集上被训练,并且之后能够重建作为输入提供的相似图像。如果输入图像和重建输出之间的差异很大,则该图像可以被标记为异常值。
mv tec 广告数据集
在现实世界条件下测试离群点检测模型的准确性可能具有挑战性,因为数据集离群点的数量通常是我们未知的。我们可以通过在专门为测试目的而创建的数据集上训练我们的模型来克服这个障碍。
MVTec 广告数据集的示例图像—图像由 MVTec 软件有限公司提供
MVTec AD 数据集包含数千张高分辨率图像,适合测试和基准测试图像异常模型,重点是工业制造质量控制。数据集由 15 类图像组成,如地毯、皮革、晶体管、螺丝等。每个类别的训练集仅包括正常图像,而测试集既有正常图像又有带有各种缺陷的异常值。有关数据集和各种异常值检测算法的基准测试结果的更多信息,可以参考相关的研究论文。
带有 Alibi 检测的异常值检测
我们现在将基于 Alibi Detect 库的自动编码器算法创建一个图像异常值检测模型。该模型将在 MVTec AD 数据集的胶囊图像上进行训练和测试,遵循半监督方法,因为训练集将仅由正常(内层)图像组成。
我们从导入必要的库、类和函数开始。之后,我们创建一个函数,从给定的路径加载所有的图像,并将它们转换成一个 numpy 数组。我们使用该函数为我们的模型创建train
和test
集合。
我们使用 TensorFlow/Keras API 定义卷积自动编码器的编码器和解码器部分。然后我们实例化OutlierAE
检测器类,它将编码器和解码器层作为输入,并在适当的集合上训练模型。我们还需要定义一个阈值,超过该阈值的元素将被标记为异常值。我们用infer_threshold
函数计算阈值,该函数将 inlier 值的百分比作为参数。这很方便,但在现实世界中并不总是可行的。之后,我们通过使用predict
函数检测测试集的异常值,该函数返回一个包含每个元素预测的字典。instance_score
键包含实例级别的分数,如果该元素高于阈值,则被标记为异常值。此外,feature_score
键包含图像中每个像素的分数。
首先,我们将所有标记为异常值的图像复制到img
文件夹中。然后,我们用所有图像的文件名以及探测器的预测创建一个熊猫数据帧。我们创建仅包含异常值的第二个数据帧,然后打印它。该模型相当准确,因为它已经检测到所有异常图像,并且仅将少数正确图像标记为异常(假阳性)。
最后,我们使用plot_feature_outlier_image
函数为离群元素的每个像素绘制分数。这有助于我们更好地理解异常检测器是如何工作的。第一个图形列包含五个被标记为异常值的图像。接下来,我们可以看到每个图像,因为它是由异常检测器重建的。显然,该模型只能输出正常的胶囊图像,因此不能重建各种变形。接下来的三列是每个图像通道的特征分数可视化,可以帮助我们定位有问题的区域。
结论
卷积自动编码器是一个可行的和相当准确的图像异常检测选项,但仍有改进的空间。例如,您可以尝试修改神经网络架构以获得更好的结果。您还应该记住,Alibi Detect 包括其他算法,如变分自动编码器和自动编码高斯混合模型,它们可能适用于特定情况。我鼓励您进行试验,找到符合您需求的最佳解决方案。欢迎在评论中分享您的想法,或者关注我的 LinkedIn,我会定期在那里发布关于数据科学和其他主题的内容。你也可以访问我的个人网站或者查看我的新书,书名是用 PyCaret 简化机器学习。
参考
[1] I. Goodfellow,Y. Bengio,a .库维尔,深度学习 (2016),麻省理工出版社
[2]r . chal aparty,S. Chawla,用于异常检测的深度学习:调查 (2019),arXiv:1901.03407
[3] P .博格曼,m .福瑟,d .萨特勒格,c .斯泰格, MVTec AD —无监督异常检测的综合真实世界数据集,2019 年 IEEE/CVF 计算机视觉和模式识别会议(CVPR)
用 SymPy 简化机器学习微积分
凯文·Ku 在 Unsplash 上的照片
快速浏览用于机器学习的微积分,以及如何使用 SymPy 将它添加到您的代码中
机器学习需要一些微积分。许多在线机器学习课程并不总是涵盖微积分的基础,假设用户已经有了基础。如果你和我一样,你可能需要复习一下。让我们来看看一些基本的微积分概念,以及如何使用 SymPy 在代码中编写它们。
很多时候,我们需要微积分来寻找最优化问题中的导数。这有助于我们决定是增加还是减少重量。我们的最终目标是找到一个极值点,它将是函数中的局部最小值或最大值点。
让我们通过以下步骤来完成寻找极值点的过程。
- 安装和学习 Sympy 的基础知识。
- 求线性函数的斜率。
- 发现切线和割线。
- 用我们的斜率和切线知识来求极限。
- 理解什么是函数的导数。
- 用导数求极值点。
- 决定极值点是局部最小值还是最大值点。
SymPy 入门
SymPy 是一个 Python 库,可以让你使用符号来计算各种数学方程。它包括计算微积分方程的函数。它还包括许多其他函数,用于一些更高级的数学。
安装 SymPy 很简单,你可以在这里找到完整的安装说明。如果您已经在使用 Anaconda,那么 SymPy 也包括在内。有了 Anacona,您可以通过一个简单的:
conda update sympy
如果您没有使用 Anaconda,pip 是安装新 Python 库的好方法。
pip install sympy
SymPy 依赖于 mpmath 库,所以你也需要安装它。
conda install mpmath
#or
pip install mpmath
使用 SymPy,我们可以像在数学方程中一样创建变量。我们需要将这些变量设置为符号,这样 SymPy 就知道将它们与常规的 Python 变量区别对待。这很简单,使用 symbols() 函数即可完成。
import sympy
x2, y = sympy.symbols('x2 y')
现在我们已经安装了 SymPy,让我们后退一步,看看微积分的基础。
线性方程和斜率
如上所述,我们需要微积分的一个主要原因是寻找极值点。为了说明这一点,让我们假设你每年都参加年度土豆大炮大赛。每年你都输给可怕的丹尼·麦克道格。今年你雇了一个教练来帮你打败丹尼。要打败丹尼,教练需要你给他三样东西。
1.当丹尼的土豆在最高点时。
2.土豆到达最高点需要多长时间。
3.土豆最高点的斜度。
先说坡度。
如果丹尼在土豆永远上升的地方建造了一个魔法炮,找到斜坡会很容易,但不会有最大高度。这种类型的马铃薯飞行路径是一个线性方程,如 y = 3x+2(y 截距形式)。
我们可以使用 NumPy 和 Matplotlib 来可视化这个线性函数马铃薯飞行。如果您没有安装 NumPy 和 Matplotlib,过程就像上面的 SymPy 安装一样。更多详情见此处和此处。
import matplotlib.pyplot as plt
import numpy as np#create 100 values for x ranging from 0 to 6
x = np.linspace(0,6,100)#our linear function
y = 3*x + 2#add some aesthetics to out plot
plt.grid(color='b', linestyle='--', linewidth=.5)
plt.plot(x,y, label="potato flight")
plt.xlim(0, 6)
plt.ylim(0,20)
plt.legend(loc='best')
plt.xlabel("Seconds")
plt.ylabel("Feet(x10)")#show the plot we created
plt.show()
我们要检查斜率,因此我们将在两个随机点上添加标记:(1,5)和(4,14)。
plt.plot(1, 5, 'x', color='red')
plt.plot(4, 14, 'x', color='red')
马铃薯以线性函数飞行
当我们有一个线性函数时,我们的斜率是常数,我们可以通过观察任意两点,计算 y 的变化除以 x 的变化来计算斜率,让我们来看看我们之前标记的两点。
求斜率的方程
在 y 截距形式(y = mx + b)中, m 总是斜率。这与我们之前的函数 y = 3x + 2 相符。
土豆一定要下来——非线性函数
线性函数的斜率很容易,但土豆必须下降。我们需要一种方法来计算随每个点变化的斜率。让我们从可视化一个更真实的土豆飞行路径开始。
我们的教练很棒,他知道代表土豆飞行的函数是 f(x) = -(x ) +4x。
让我们再一次用 Matplotlib 可视化这个土豆的飞行路径。
x = np.linspace(0,5,100)
y = -(x**2) + 4*xplt.xlim(0, 4.5)
plt.ylim(0,4.5)
plt.xlabel("Seconds")
plt.ylabel("Height in Feet(x10)")
plt.grid(color='b', linestyle='--', linewidth=.5)
plt.plot(x, y, label="potato")
plt.legend(loc='best')plt.show()
马铃薯的非线性飞行
从函数的可视化来看,我们看到在大约 2 秒钟内,马铃薯达到了大约 40 英尺的高度。该图很有帮助,但我们仍然需要最大高度的斜率。另外我们需要一些确凿的证据带回给教练。让我们继续用微积分证明这个高度和时间。
割线
曲线的割线是一条至少在两个不同点处与曲线相交的线。当我们有非线性函数时,我们仍然可以找到两点之间的斜率,或一条割线。由于(2,4)看起来像我们的马铃薯路径的顶部,让我们看看我们新的非线性马铃薯路径上的 2 个点:(1,3)和(2,4)。
#adding this code to our above plot
x2 = np.linspace(1,2,100)
y2 = x2 + 2
plt.plot(x2,y2, color='green')
割线 x + 2
我们可以看到(1,3)和(2,4)的斜率是 1。让我们更进一步,看看当我们试图只找到点(2,4)的斜率时会发生什么。为了做到这一点,我们需要一条线,能够代表通过一个点。
切线
让我们看看越接近(2,4)线的斜率会发生什么变化。所以我们再画几条割线。我们将端点保持在(2,4),但一条线将从(1.5,3,75)开始,另一条线从(1.95,3.9975)开始。这又给了我们两条割线 y3 = .5x + 3 和 y4 = .05x + 3.9。
两条割线
当我们最终到达(2,4)作为我们的起点时,一条线形成了,这就是 y = 0x +4。这是(2,4)的切线。通过求解极限并将其代入 y 截距线性方程来计算切线。更多关于极限的内容。函数中的每一点都有一条切线,这就是我们如何计算函数中每一点的斜率。
点(2,4)的切线
记住,我们的主要目标是找到一个极值点,土豆的最大高度。极值点将出现在切线斜率为零的时候,因为这表示函数改变了方向。例如,让我们看看穿过(1,3)和穿过(3,3)的切线的斜率。
3 条切线
我们来看看这三个点的切线斜率发生了什么变化。
- 点(1,3),切线为 y = 2x + 1,斜率为 2
- 点(2,4),切线为 y = 0x + 4,斜率为 0
- 点(3,3),切线是 y = -2x + 9,斜率是- 2
0°之后,斜率改变方向。很好,我们需要找到切线斜率为零的点。这些点将告诉我们最大或最小点,因为切线斜率的方向总是在 0°之后改变。
太好了!所以我们需要在函数上找到一个斜率为零的点。如果有一种方法可以创建一个函数,给出原始函数中任意一点的斜率就好了。有,这就是导数!在我们进入衍生品之前,让我们先看看极限。
限制
极限允许我们找到函数接近某个 x 值时的斜率。如果我们把极限解成割线,那就简单了。我们代入两个 x 值和两个 y 值,就可以求解斜率方程。这种极限定义了。但是,我们想要找到一个未定义的极限,因为我们想要点到点(2,4)的切线的斜率。
当我们接近(2,4)时,我们需要斜率。我们不能得到从(2,4)到(2,4)的割线的斜率,因为如果我们把这些数字代入斜率方程,我们得到 0/0。这是行不通的,因为正如一位数学老师曾经说过的:“生活中有两件事你不能做,把果冻钉在墙上,然后除以零”。因为我们不能被零除,我们需要找到未定义的极限。
极限是通过将我们的函数代入斜率方程并分解出来解决的。我们可以使用之前的斜率公式,用点(2,4)代替 x1/y1 值,然后用 f(x)和 x 代替 x2/y2 值。我们的极限值是我们正在逼近的 x 值,在这个例子中是 2。函数-(x ) +4x 的极限 2 的新斜率方程为:
我们的目标是去掉分母上的 x,所以让我们展开-(x)并抵消分母上的 x。
现在我们已经把 x 从分母中去掉了,我们可以把 plug 2 放回 x 中。
现在我们知道了极限是如何求解的,让我们启动 SymPy,这样我们就可以在代码中求解极限了。
SymPy 有一个名为 limit() 的函数,它有 3 个参数。
- 我们正在寻找极限的函数
- 输入变量
- 输入变量接近的数字
因为我们的极限是未定义的,我们需要像上面一样替换 x 和 y 值。
import sympy
x2, y = sympy.symbols('x2 y')
#store our substituted function as a y variable
y = (-(x2**2) + 4 * x2 -4) / (x2 -2)
limit = sympy.limit(y, x2, 2)
// 0
我们的上限是 0!
衍生品
导数是一个函数,它将给出函数中任意一点切线的斜率。
现在我们明白了极限线和切线是什么,我们可以朝着我们的最终目标前进,用导数来寻找函数中的极值点。求函数导数的过程是 微分 。
求解导数需要用到一些代数和上面的斜率公式。因为我们不是在求解一个特定的点,所以我们不会替换任何值。对于此示例,我们也用更常见的形式替换 x1 和 x2,即使用 x 和 x +h。这将为我们提供以下公式来求解 f′(x)的导数,f′(x)表示 x 的函数的导数:
如果我们把函数代入这里,我们得到:
然后我们可以用解决极限的方法来解决这个问题。我们还可以使用幂法则来求解,从而使用以下等式轻松找到我们的导数。
使用该规则,我们可以看到函数 -(x ) +4x 的导数是- 2x +4 。
让我们用 SymPy 来求导,而不是通过手动的步骤来求导。
SymPy 为我们提供了一个名为 diff() 的函数,它将执行微分过程并返回导数。
diff 函数有两个参数:
- 我们要求导数的函数
- 输入变量
让我们用我们原来的函数 -(x ) +4x 来试试这个函数。
# set x as the variable
x = sympy.symbols('x')#help make the out easier to read
sympy.init_printing(use_unicode=True)#enter the our argumnets to th diff funtion
d= sympy.diff(-(x**2) + 4*x, x)#print our derivative
print(d)
//-2*x + 4
函数的极值点是导数等于零的地方。这是因为当导数等于 0 时,函数的方向已经改变,正如我们上面所探讨的。
为了找到 x 值,我们将导数设为 0,然后求解 x, -2x + 4 = 0。
这是通过使用函数 solveset() 用 SymPy 解决的。Solvest 有两个参数:
- Eq 函数*,它有两个参数:等式和等式需要等于的值*
- 我们试图解决的变量
Solvset 将返回所有解方程的数的集合。
当导数等于 0 时,使用 solvset 来查找 x 值将如下所示:
answer = sympy.solveset(sympy.Eq(d, 0),x)
print(answer)
//{2}
完美!我们的 x 值是 2 ,如果我们把它代入原始函数,我们得到 4 作为我们的 y 值。
现在我们可以确定,在 2 秒钟时,麦克道戈尔的土豆正好在空中 40 英尺处,并且该点的斜率为 0!我们可以把它带给教练,我们一定会赢得下一场土豆大炮比赛!
我们的极值点是最小值还是最大值?
我们知道我们的土豆的极限点一定是最大点,因为这些土豆大炮不是用来击落土豆的。但是如果我们没有图或者不知道函数的方向,我们怎么知道极值点是局部最小值还是局部最大值?我们已经知道,将导数的 x 值设置为 2 会导致斜率为 0。如果我们在我们的导函数中再插入两个数,一个数会大于 2,另一个会小于 2,会发生什么?我们将使用 1 和 3 来尝试。
test1 = -2*1 + 4
test2 = -2*3 + 4
print(test1)
//2
print(test2)
//-2
我们看到,在极值点之前,斜率是正的,而在极值点之后,斜率是负的。斜率从正到负的变化告诉我们极值点是最大值。如果斜率从负变为正,我们就知道极值点是最小值点。如果有多个极值点,我们希望在每个点之间选择一个值。例如,如果我们有极值点 1 和 5,我们将重复这个过程三次:
- 选择一个小于 1 的随机数
- 选择一个大于 1 但小于 5 的随机数
- 选择一个大于 5 的随机数
很可能非线性函数不会只有一个极值点。在这种情况下,所有的步骤都是一样的,但是当我们求解导数等于零时,我们得到了多个解。
结论
SymPy 是一个庞大的库,是一个很好的方法来寻找导数和函数的局部极值点。SymPy 易于使用和阅读,为任何需要微积分的机器学习项目增加了简单性和可读性。我们只涉及了这个库的许多可用函数中的一小部分。我鼓励您进一步探索它,看看还有什么可以用来将数学融入到您的数据科学项目中。
简化机器学习工作流程
如何使用管道来标准化机器学习工作流的数据预处理、数据转换和建模步骤
杰里米·毕晓普在 Unsplash 上的照片
本文概述了如何使用定制转换器和 Scikit-Learn 管道捆绑数据预处理、数据转换和机器学习工作流的建模步骤。包含探索性数据分析(EDA)的详细笔记本可以在我的 GitHub 上找到。
本教程的结果是在 Kaggle Ames Housing 数据集上使用 Pipelines 的一个示例,其中我们获得了排行榜前 20%的分数!
机器学习工作流
虽然有许多方法来构建机器学习(ML)工作流,但基本结构可以被认为是以下步骤:
- **数据采集:**收集数据
- **数据预处理:**剔除异常值,替换缺失值等。
- **数据转换:**添加特性,标准化数据等。
- **模型训练&调优:**选择模型、交叉验证、调优超参数等。
- **模型评估:**分析模型性能
- **模型部署:**根据测试数据进行预测
基本 ML 工作流程
机器学习工作流程陷阱
即使是简单的 ML 工作流程也会变得复杂。例如,假设有 10 个预处理/转换步骤应用于训练和测试数据。此外,您希望测试多个模型,并执行交叉验证和超参数调整。一些数据科学家可能会拼凑杂乱的代码块,并试图手动跟踪每一步。然而,有几个原因可以解释为什么这很快变得不切实际,甚至对您的建模工作有害。
在管道外执行 ML 工作流程的挑战:
- **训练-测试污染:**当您对整个数据集执行预处理/转换步骤,而不是对单个训练、验证和测试数据组件执行预处理/转换步骤时,就会发生这种形式的数据泄漏。一个典型的例子是在分割前标准化数据集,这可能导致验证甚至测试数据的高性能。
- **非统一预处理/转换:**即使您将数据集分成训练、验证和测试数据组件,您现在也必须跟踪每个组件,并记住应用相同的预处理/转换。在复杂的 ML 工作流中,这可能很难做到,甚至更难执行“堆栈跟踪”。
- **交叉验证:当你不得不跟踪应用不同模型的每个代码块,或者更糟糕的是,对于循环来说,一个庞大而繁琐的,几乎不可能“干净地”执行多个模型的交叉验证测试。
- **部署:**即使你部署了它而没有不可追踪的错误,你也不能轻易地改变工作流中的组件。
输入管道
管道是捆绑数据预处理、转换和建模代码的一种简单方法,允许您像应用单个步骤一样应用“捆绑”。这意味着不再有列车测试污染或忘记预处理/转换步骤。只要您在应用管道之前拆分数据,每个数据集将接收相同且独立的预处理/转换。它还使得交叉验证、模型调整和模型部署更加简单和清晰。
具有示例管道的基本 ML 工作流
一个例子
本节提供了一个使用 Scikit-Learn 管道和定制转换器来交叉验证和比较几个模型的示例。
数据
我将使用来自 Kaggle 竞赛房价:高级回归技术的 Ames 房产数据集。该数据集有 79 个特征,混合了数字和分类,并包括变量,如房屋各部分的平方英尺、社区以及建造和出售的年份。数据可以通过 Kaggle 比赛页面访问。
定义管道
首先,我们需要定义管道步骤。我将执行非常基本的数据预处理/转换和特征工程。基本上,它只是足够加上一点额外的东西来将我们的数据转换成 Scikit-Learn 模型可以接受的东西。
Ames 住房数据集的 Scikit-Learn 管道
首先,需要定义数值和分类数据列(这些数据的预处理和转换涉及不同的步骤)。然后,我们希望定义一个管道类,并在嵌套管道中包含适用的数据转换类(即数据预处理/转换步骤)。我们可以通过将多个转换类和嵌套管道包含在 FeatureUnion 类中来连接它们的结果。这里的是一个很好的来源来获取更多关于构建管道和特征联合的信息。要素数据集被传递到执行以下操作的管道中:
**数字列:**提取数字列,估算缺失值,生成新要素总平方英尺,log 转换数据。
**分类列:**提取分类列,估算缺失值,编码分类数据
**标准化:**feature union 将经过处理/转换的数字和分类数据结合起来,并对这些数据进行标准化。
定制变压器
有时候我们需要一个不附带 Scikit-Learn 的变压器。例如,我们可能希望创建一个新的特性,或者希望现有的 Scikit-Learn 转换器返回一个熊猫数据帧。幸运的是,Scikit-Learn 和 Python 通过 TransformerMixin 和类继承简化了这一过程。
下面是每个定制转换器的简要总结:
- **列提取器()😗*自定义转换器类,用于从特征数据集中提取选择列。
- **dfSimpleImputer()😗*从 simpleinputr()类继承并返回 Pandas DataFrame 的自定义转换器类。
- DFOneHotEncoder(): 自定义 transformer 类,该类从 OneHotEncoder()类继承并返回 Pandas DataFrame。
- TotalSF(): 自定义 transformer 类,用于计算房屋的总面积。
创建和评估管道
现在我们已经定义了管道,我们可以把它们放在一起。首先,我们定义一个助手函数,它采用一个管道,对一个特定的模型执行 X 重交叉验证,并返回房屋销售价格日志的均方根误差(RMSE)。接下来,我们定义我们想要训练的模型,并为循环创建一个*,其中每个循环定义“完整管道”,该管道是预处理/转换步骤(即最后一部分)和选择模型的组合。循环的结果是每个模型的交叉验证 RMSE 分数以及模型分数和标准偏差的箱线图比较。这里是制作交叉验证箱线图的绝佳资源。*
交叉验证 RMSE 分数
各种模型的箱线图交叉验证 RMSE 分数
结论
在本教程中,我们定义并创建了一个管道来预处理/转换 Kaggle Ames 住房数据集,并使用 X 倍交叉验证 RMSE 评分来评估模型。结果是一个易于跟踪和调试的管道,返回 RMSE 分数在 Kaggle 排行榜的前 20%。
将管道添加到您的数据科学武库中,保持有序和高效!
使用此工具简化您的数据科学项目
数据科学家开发和导出机器学习模型的理想工具
【https://github.com/amineHY/AI-LAB
🚀链接到项目
- Github 项目:https://github.com/amineHY/AI-LAB
- docker Hub:https://Hub . docker . com/repository/registry-1 . docker . io/amine hy/ai-lab
描述
这个项目是为数据科学家创造一个开发环境。它帮助用户以简单的方式开发机器学习模型,
- 关注代码,而不是工具。
- 节省安装过程中可能浪费的时间。
- 防止通过安装不兼容的软件包破坏操作系统。
我手工制作了AI-lab
(在 NVIDIA 容器之上)并利用 Docker 的功能来拥有一个可复制和可移植的开发环境。
AI-lab
允许使用最常见的人工智能框架在 Python 中开发基于人工智能(AI)的应用程序。AI-lab
旨在用于构建、培训、验证、测试您的深度学习泰格豪雅
例如,模型是进行迁移学习的好工具。
它包括:
- Ubuntu 18.04
- NVIDIA CUDA 10.1
- 英伟达 cuDNN 7.6.0
- OpenCV 4.1
- Python 3.6
- 最常见的 AI 框架:TensorFlow、PyTorch、ONNX、Keras、ONNX-tensort、Jupyter-lab、VS 代码集成远程开发、Numpy、Matplotlib、Scikit-learn、Scipy、Pandas、tensort 等等。
安装人工智能实验室
在使用AI-lab
之前,需要在操作系统上安装一些先决条件
- 您必须拥有 AMD64 架构的操作系统。在终端检查一下
dpkg --print-architecture
比如我用的是 Ubuntu 18.04.3 LST。您可以用这个命令检查您的系统
lsb_release -a
nvidia-smi
在我的笔记本电脑上,我有 NVIDIA 驱动程序版本 430.50 和 CUDA 版本 10.01。
码头工人
使用
首先从 Docker Hub 注册表中拉出 AI-lab:AI-lab
docker pull aminehy/ai-lab
最新的图像大约有 9.97GB,所以请确保您有足够的空间(和高速互联网)。
然后运行人工智能实验室,开始你的开发
xhost
然后
docker run -it --rm -v $(pwd):/workspace -w /workspace -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY --runtime=nvidia -p 8888:8888 -p 6006:6006 aminehy/ai-lab
搞定了。
安装 UFF 转换器 **convert-to-uff**
(如果需要)
运行 AI-lab 后,使用以下命令移动到/opt/tensorrt/python
文件夹并安装convert-to-uff
:
cd /opt/tensorrt/python dpkg -i *-tf_*.deb UFF_PATH="$(python -c 'import uff; print(uff.__path__[0])')" chmod +x ${UFF_PATH}/bin/convert_to_uff.py ln -sf ${UFF_PATH}/bin/convert_to_uff.py /usr/local/bin/convert-to-uff
启动 IDE 并开始开发您的应用程序
Jupyter 笔记本
如果AI-lab
在您的机器上正确运行,那么Jupyter notebook
应该会自动运行。如果不是这样,请使用以下命令从终端启动它
jupyter notebook --allow-root --port=8888 --ip=0.0.0.0 --no-browser
jupyter 笔记本
VS 代码
VS Code 是一个 IDE,它提供了从 docker 容器内部(即AI-lab
内部)通过扩展远程开发进行开发的可能性。更多详情此处。
我在文件夹AI-LAB_in_vscode
中添加了两个配置文件夹.devcontainer
和.vscode
。他们必须能够通过AI-lab
使用 VS 代码。这两个文件夹是隐藏的,必须位于应用程序的目录中,以便 VS 代码自动检测AI-lab
配置。因此,您需要将它们复制到应用程序文件夹中。
要获得这些文件夹,首先,克隆这个存储库并移动到其中
git clone [https://github.com/amineHY/AI-lab.git](https://github.com/amineHY/AI-lab.git) cd /AI-lab
将这两个文件夹复制到您的应用程序文件夹,例如/path_to_folder_application
sudo cp -R AI-lab/AI-LAB_in_vscode/.* /path_to_folder_application
最后,移动到您的应用程序文件夹
cd /path_to_folder_application
和启动 VS 代码
code .
虚拟代码
显示 GPU 的内存使用情况
根据您的开发情况,您可能希望观察 GPU 的内存消耗。多亏了gpustat
,你才能做到这一点
watch -n0.5 -c gpustat --c -cupP
我的操作系统的输出:
用deviceQuery
显示关于 GPU 的信息
在终端中,运行deviceQuery
脚本(在这个库中提供)来获得更多关于你的 GPU 配置的信息
./deviceQuery
我的操作系统的输出:
参考
- Github 项目:https://github.com/amineHY/AI-LAB
- docker Hub:https://Hub . docker . com/repository/registry-1 . docker . io/amine hy/ai-lab
有什么建议吗?
阅读我的另一篇关于媒介的文章:
简化您的 Python 代码:用 Wily 自动化代码复杂性分析
约翰·巴克利在 Unsplash 上的照片
以下是如何让评估代码复杂性成为 Python 开发例程的一部分
所以你写了一段 Python 代码,它完成了工作。很好,但是你的代码足够简单吗?复杂的代码很难阅读,这使得代码维护成本更高。尽早发现复杂性可以节省时间、金钱和许多挫折。在这篇文章中,我将向您展示如何使用老谋深算的命令行工具来跟踪代码的复杂性。
代码复杂性快速入门
代码复杂性很重要。不必要的复杂代码更难阅读,也更难维护。如果你的代码很难理解,就很难发现现有的错误,也很容易引入新的错误。笨重的代码使团队工作变得复杂,并且很难让新同事跟上进度。科技公司必须满足复杂性门槛,这是有原因的。
开发人员使用几种不同的代码复杂度度量方法。复杂性分析工具中普遍存在两种这样的度量。
麦凯布的循环复杂性
首先,麦凯布的循环复杂性 (CYC)依赖于图论。由 Thomas McCabe 在 1976 年开发的度量是从一个函数的控制流图中计算出来的,控制流图由节点和边组成。
流程图中的节点和边(图片由作者提供)
基于这样的图表,我们可以使用以下公式计算 CYC:
CYC = E – N + 2P
在这个公式中,P
是谓词节点(即包含 if/else 条件的节点)的数量,E
是边的数量,N
是节点的数量。CYC 实际上是通过你的模块的独立路径的数量的度量,给出了代码理解的困难程度的一些指示,以及需要多少单元测试来实现完全的测试覆盖(即它的“可测试性”)。
CYC 的值越高,代码就越复杂。卡内基梅隆大学的软件工程研究所定义了以下范围(参见本出版物第 147 页的):
- 1–10:风险低,程序简单;
- 11–20:中等风险,难度较大的项目;
- 21–50:高风险,非常难的项目;
- > 50 :风险极高,不可测试的程序。
可维护性指数
其次,可维护性指数(MI) 是 McCabe 度量©Halstead 量 (V)和代码行数(LoC)的组合。在公式中:
MI = 171 - 5.2*ln(*V*) - 0.23*(*C*) - 16.2*ln(*LoC*)
MI 在理论上被限制在 0 到 100 之间,但在实践中不是这样(大多数软件实现将其限制在 100)。最初介绍度量标准的文章提到了以下阈值:如果你的代码的 MI 低于 65,那么它就很难维护;如果是 85 或者更高,你的代码很容易维护。65 到 85 之间的任何值都是适度可维护的(科尔曼、洛瑟和阿曼,1994 )。Visual Studio使用的重新调整版本(在 0 到 100 之间)将阈值分别设置为 0 到 9 表示低可维护性,10 到 19 表示中等可维护性,20 及以上表示高可维护性。(请注意,不同的 ide 和库可能使用不同的阈值。)
对 CYC 和米的警告
出于多种原因,不应该孤立地使用 CYC 和 MI 来确定您的代码是否过于复杂或难以维护。
首先,这些度量没有考虑其他不必要的复杂性指标,比如代码重复、嵌套深度或可读性。第二,MI 尤其受到一系列问题的困扰,这使得它不一致。它是几十年前校准的,并对它使用的一些变量的增加和减少如何影响整体复杂性做出了一些相当可疑的假设。(关于 MI 缺点的更详细的评估,请看代尔夫特理工大学教授 Arie van Deursen 的这篇出色的博客文章。)
最后,我们应该预期 MI 的值会根据您所分析的框架而有所不同。因此,像代码行(LOC)这样的简单度量可能是首选的。
这些限制确实给了我们一些何时以及如何使用 CYC 和 MI 的想法。首先,MI 不能可靠地用于比较两个不同系统的复杂性,即使它们是使用相同的框架构建的。第二,当一段代码足够简单和可维护时,我们不能使用硬截止,因为这些度量不能脱离人类评估和代码库的专家知识。
相反,CYC 和 MI 可能是有用的——与其他更简单的度量标准结合——如果我们将它们视为帮助我们识别代码中潜在问题的试探法。如果我们定期评估我们的代码,并观察度量标准如何随着时间的推移而演变,这可能特别有用。突然和巨大的变化应该引起我们的注意,并促使我们进一步研究。不用说:和大多数代码质量工具一样,CYC 和 MI 应该在你的开发工作流程中补充质量控制,而不是取代它。
使用预提交挂钩将代码复杂性分析集成到您的工作流中
在这篇文章的剩余部分,我将向你展示如何在你的 Python 开发工作流程中集成和,在每次提交新代码时为你执行复杂性分析。Wily 是一个命令行工具,允许您分析、跟踪和可视化 Python 代码的复杂性和可维护性。虽然 mccabe 和 radon 库提供了类似的功能,但是 wily 有一些很好的特性,允许我们相对容易地跟踪复杂性。下面的视频是作者安东尼·肖对《诡计》的介绍。
老谋深算的 Python:编写更简单、更易维护的 Python — PyCon 2019
检测代码复杂性的用处取决于您执行分析的频率。如果你定期检查你的代码复杂性,你可以很容易地发现突然增加。这样的异常值不一定有问题,但是它们应该促使您进行调查。确保你经常获得这些见解的最好方法是在你的项目的预提交钩子中包含老谋深算。
预提交钩子是一些小的“脚本”,当使用git commit
命令时,它们在本地运行在暂存文件上。(如果你想了解更多关于预提交钩子的知识,你可能会对我在上发表的使用预提交钩子自动执行代码风格的文章感兴趣。)
安装预提交库
要开始使用预提交钩子,首先使用终端安装预提交库(关于 Homebrew 和 Conda 的说明可在本页获得):
pip install pre-commit
您可以使用下面的命令检查安装是否成功。此命令应返回预提交安装的版本。
pre-commit --version
创建和配置您的。yaml 文件
安装完pre-commit
之后,创建一个.yaml
文件。您的.yaml
文件指定了您的预提交钩子配置。在终端中使用以下命令在您的项目目录中创建新的.yaml
文件:
nano .pre-commit-config.yaml
随后,添加以下配置:
repos:
- repo: local
hooks:
- id: wily
name: wily
entry: wily diff
verbose: true
language: python
additional_dependencies: [wily]
使用预提交
现在您已经安装了预提交包并设置了您的.yaml
文件,您已经准备好使用预提交和 wily 来分析代码复杂性了。首先,在终端中运行以下命令来安装预提交挂钩:
pre-commit install
安装好预提交钩子后,每次提交对 git 的更改时,都会执行pre-commit
命令。或者,您可以使用以下命令直接从终端运行预提交挂钩:
pre-commit run
老谋深算入门
在可以使用带有预提交的 wily 之前,您必须从终端运行wily build
。 build 命令编译您最近 50 次提交中代码复杂性变化的缓存。您需要这个缓存,以便 wily 可以计算您提交的度量。或者,您可以使用wily setup
并遵循终端中的指示。
在您设置和配置了预提交并创建了您的老谋深算的缓存之后,wily diff
命令将在每次新提交时自动运行。在新提交时,您的终端输出将显示当前提交的代码版本与前一版本相比的复杂性。下面的输出显示了删除一些代码行后的 wily 的 diff 报告示例。我们获得了四个分数,包括代码行数(LOC)、唯一操作数以及圈复杂度和可维护性指数。如果您的复杂性在相对较小的更改后急剧增加,您可能应该暂停并重新考虑您刚刚做出的更改。
结合预提交使用“wily diff”命令进行新提交后的代码复杂性分析(图片由作者提供)
了解代码在两次提交之间是如何变化的是很有用的,但是并没有给我们太多的数据来处理。为了识别和区分潜在的问题领域,我们必须考虑一个特定的模块是如何随着时间而变化的。要获得此信息,请在终端中运行以下命令:
wily build
wily report <my-module-name>
如下例所示, report 命令将生成一个 CLI 表,显示指定模块的所有提交之间复杂性度量的变化。这个报告可以让你很容易地发现变化,以及复杂性的模式和趋势,以及你可能出错的地方。
使用“老谋深算的报告”命令分析提交之间的复杂性差异(图片由作者提供)
另一个识别大型项目中(潜在)有问题模块的有用命令是wily rank
,它将显示不同模块的可维护性指数。在这种情况下,所有值都高于 75,这意味着它们很容易维护。(请注意我在本文前面概述的使用可维护性指数的注意事项)。
使用“wily rank”命令计算 Python 模块的可维护性指数(MI)
最后,在绘制不同提交的一段代码的复杂性时,最容易发现趋势。您可以使用 graph 命令显示模块的复杂性报告,例如:
wily graph create_tables.py loc sloc complexity
这个命令将产生一个create_tables.py
模块的图(见下文),在 y 轴上显示代码行,在 x 轴上显示提交,以及由每次观察的气泡大小表示的复杂性。
用 Wily 绘制代码行(LOC)(行)和循环复杂度(气泡大小)
结合起来,wily 的diff
、report
、rank
和graph
命令可以提供有用的信息,告诉你你的代码是否变得不必要的复杂。
超越诡计:简化您的 Python 代码
编写简单、清晰、易读的代码具有挑战性。虽然代码风格和(单元)测试在很大程度上可以自动化,但是代码本身的设计仍然是一项人工任务。也就是说,如果机器编程的最新进展没有让开发人员过时的话。自动化复杂性分析可以帮助您做出明智的决策,决定何时重新考虑您的代码,以及您的代码库的哪些区域应该优先考虑。这些分析永远不能取代正确的代码设计过程,但是它可以在开发过程中避免不必要的错误和捕捉潜在的问题。
感谢阅读!
MI 的原始版本。请注意,存在不同版本的 MI。参见 Radon 关于该指标的文档。
Visual Studio 的重新调整 MI 可以使用下面的公式计算:MAX(0,(171–5.2*ln(V) — 0.23 * (C) — 16.2*ln(LoC))*100 / 171)
。详见本文。
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@ndgoet/membership)
您有兴趣了解更多关于自动化 Python 开发过程的知识吗?看看我之前关于这个话题的一些帖子:
单元测试是产生高质量代码的关键。以下是如何自动化测试 Python 代码。
towardsdatascience.com](/automating-unit-tests-in-python-with-hypothesis-d53affdc1eba) [## 如何用预提交钩子改进 Python 代码风格
实施代码风格是简化开发过程的关键,下面是如何实现自动化。
medium.com](https://medium.com/python-in-plain-english/how-to-improve-your-python-code-style-with-pre-commit-hooks-e7fe3fd43bfa)
请仔细阅读 本免责声明 中的任何内容后再依托 我的 Medium.com 文章 。
用代码和例子简化函数的 args 和 kwargs!
理解 python 编程和机器学习的args 和kwargs 的完整概念。
函数是 python 和面向对象编程不可或缺的一部分。
Python 是一种优雅而简单的语言,它为用户提供了多种更简单有效的编码方式。
其中一个概念是在 python 中包含args 和kwargs。这些向函数传递可变数量参数的方法使得 python 编程语言对于复杂问题非常有效。
正确使用它们的用途很多。函数中的这两个参数将非常有用,即使在机器学习和深度学习的概念中,同时尝试为各自的项目构建您的自定义层或函数。
在本文中,我们旨在通过代码和示例直观地理解这些概念。所以,事不宜迟,让我们开始深入了解这些概念。
在开始编程之前,让我们简单回顾一下函数这个主题。
什么是功能?
函数是写在程序中的一段代码,这样它们可以被多次调用。函数的主要用途是在同一个程序中可以被多次重复调用,而不需要一遍又一遍地编写相同的代码。然而,你也可以用它来为你的程序提供更多的结构和更好的整体外观。
使用关键字 **'def,**定义函数,可以使用已定义或未定义的参数调用这些函数。当您调用特定的函数时,python 编译器会解释将要返回的任何值。
下面是一个典型函数的代码块—
def func(parameters if required):
# code here
return # solution
在 Python 中,我们可以使用特殊符号向函数传递可变数量的参数。这有两种可能性,如下所示:
- *参数(非关键字参数)
- **kwargs(关键字参数)
让我们分别理解这些概念,从*args 开始,然后到**kwargs。
*参数
python 中函数定义中的特殊语法 *args 用于向函数传递可变数量的参数。它用于传递一个无关键字、可变长度的参数列表。
为什么您的函数需要这个参数选项?
假设篮子里有 3 个水果,你写一个包含 3 个变量的函数,定义如下:
您将得到以下输出。
Apple Mango Banana
一切看起来都很好,直到你意识到你还买了一些葡萄,你想把它也添加到你的功能中。你可以添加一个额外的变量,再次调用它,继续这个过程。然而,你购买的水果越多,你就不得不一遍又一遍地更新。这就是*args 函数发挥作用的地方。
您可以简单地执行此功能,如下所示:
语法是使用符号*接受可变数量的参数。按照惯例,它通常与 args 一词连用。*args 允许您接受比您之前定义的形式参数更多的参数。
这个过程简单有效。*args 的概念非常有用,如本节所述。让我们继续**kwargs 部分,并详细理解这个概念。
*** 夸脱*
python 函数定义中的特殊语法 **kwargs 用于传递带关键字的可变长度参数列表。我们使用带有双星的 kwargs 这个名字。原因是双星号允许我们传递关键字参数(以及任意数量的关键字参数)。
**kwargs 是一种类似字典的操作,将元素值映射到它们各自的。让我们用一个类似水果的例子来理解这个概念。假设您想按水果摆放的顺序或您最喜欢的顺序对它们进行排序。这个过程可以按照下面代码块的建议来完成:
你会得到下面的输出—
first Apple
second Mango
third Banana
fourth Grapes
关键字参数是在将变量传递给函数时为变量提供名称的地方。人们可以把 kwargs 看作是一个字典,它将每个关键字映射到我们传递给它的值。这就是为什么当我们迭代 kwargs 时,它们似乎没有任何打印顺序。
对于更复杂的项目,您可以同时使用args 和kwargs 函数来执行这些任务。我强烈建议您亲自尝试一下,以便更好地理解这些概念。
结论:
总结一下我们在本文中讨论的内容,函数是 python 和面向对象编程不可或缺的一部分。我们简要介绍了 python,并对函数在 Python 编程语言中的作用有了基本的了解。
详细讨论了args 和kwargs 的概念,以及这些参数选择在 python 编程中到底有多有用。在这些主题的帮助下,我们可以编写更高效的代码来处理复杂的任务。
如果你对今天的话题有任何疑问,请在下面的评论中告诉我。我会尽快回复你。如果我错过了什么,请随时告诉我。您希望我在以后的文章中介绍这一点。
查看其他一些可能对您的编程之旅有用的文章!
这是一个有趣的“不给糖就捣蛋”的游戏,让你在万圣节愉快地学习 python 编程
towardsdatascience.com](/simple-fun-python-project-for-halloween-ff93bbd072ad) [## 5+独特的 Python 模块,用于创建脱颖而出的机器学习和数据科学项目!
超过 5 个酷 Python 库模块的指南,用于创建令人敬畏的机器学习和数据科学项目。
towardsdatascience.com](/5-unique-python-modules-for-creating-machine-learning-and-data-science-projects-that-stand-out-a890519de3ae) [## 用代码和例子理解 Python 中的高级函数!
详细了解 python 中的匿名函数和高级函数及其实际应用…
towardsdatascience.com](/understanding-advanced-functions-in-python-with-codes-and-examples-2e68bbb04094)
谢谢你们坚持到最后。我希望你们喜欢阅读这篇文章。我希望你们都有美好的一天!
参考资料:
- 极客对极客
- 油管(国外视频网站)
了解格鲁、LSTM 和 RNNs
在这篇文章中,我们讨论了序列模型的工作原理及其应用。
序列模型是一类特殊的深度神经网络,可应用于机器翻译、语音识别、图像字幕、音乐生成等。序列问题可以是不同类型的,其中输入 X 和输出 Y 可能都是长度相同或不同的序列。也可以是 X 或 Y 中只有一个是序列。以下是 RNNs 应用的几个例子:
吴恩达在 Coursera 上的图片
单词表示法
如果输入是一个句子,那么每个单词都可以表示为一个单独的输入,如 x(1)、x(2)、x(3)、x(4)等。那么,我们如何表示句子中的每个单词呢?我们需要做的第一件事是拿出一个字典,包含我们在输入句子中可能有的所有单词。字典中的第一个词可能是“a”,再往下我们可能会找到“and”或“after”,最后一个词可能是“zebra”或“zoo”之类的词。准备字典真的是在我们的手中,因为我们可以包括尽可能多的单词。一本字典的长度可以从 10,000 到 100,000 不等,甚至更多。
假设我们有一个命名实体识别问题,其中 X 是一个输入句子,如“杰克和吉尔上山了”Y 是表示每个输入单词是否是人名的输出。因此,本质上 y(1),y(2),y(3),y(4)如果是人名,则为 1,否则为 0。并且 x(1),x(2),x(3),x(4)将是长度为 10,000 的向量,假设我们的字典包含 10,000 个单词。例如,如果 Jack 出现在我们的字典中的第 3200 个位置,x(1)将是一个长度为 10,000 的向量,在第 3200 个位置包含 1,在其他位置包含 0。可能会有这样的情况,输入序列中的一个单词在我们的字典中不存在,为此,我们可以使用一个单独的标记,如“unknown”或其他。
为什么我们需要 RNNS?
下一步是建立一个神经网络来学习从 X 到 y 的映射。一种可能性是使用一个标准的神经网络,在我们的情况下,将七个单词作为输入,以七个一键向量的形式输入到标准的神经网络,然后是一些隐藏层和一个 softmax 层,最后,通过将 0 或 1 作为输出来预测每个单词是否是一个人的名字。但是这种方法有两个问题,一个是对于不同的例子,输入可以有不同的长度,在这种情况下,标准的神经网络将不起作用。其次,像这样幼稚的架构不会共享出现在文本不同位置的特征。例如,如果网络已经知道在文本的第一个位置出现的 Jack 是一个人的名字,如果 Jack 出现在任何位置 x_t,它也应该将 Jack 识别为一个人的名字。
吴恩达在 Coursera 上的图片
由于这些原因,我们需要一个递归神经网络,其中第一层的输入是字向量 x(1)和一个激活函数 a_0,它可以用几种不同的方式初始化。第一层的输出将是 y(1),表示 x(1)是否是人名。第二层将具有来自第一层的输入 a(1 ),第二字向量 x(2)给出输出 y(2 ),依此类推,直到最后一层或最后一个时间步将具有输入 a(t-1)和 x(t)给出输出 y(t)。
因此,在递归神经网络中,信息从左向右流动,在对 y(3)进行预测时,来自 x(1)和 x(2)的信息也与 x(3)一起使用。但是这种 RNN 的一个缺点是,它只考虑出现在我们试图预测的单词之前的单词,而不考虑它后面的单词。比如预测 y_3,我们用的是 x(1),x(2),x(3)而不是 x(4),x(5),x(6),x(7)等等。例如,以“他带罗斯去一家不错的餐馆吃饭”为例。在这里,如果你试图用两个单词“he”和“take”来预测 Rose 是否是一个人的名字,你不会得到好的结果,因为句子可能是“他从花束中取出玫瑰花瓣并把它铺在床上”。因此,在预测 y(3)时,最好考虑 x(3)后面的单词,如 x(4)、x(5)、x(6)、x(7)等。这个问题可以通过使用双向 rnn 或 BRNNs 来解决,我们不会在本文中讨论。
基本 RNN 建筑
先说一下我们网络的参数。在第一层中,a(0)来自左边,x(1)来自底部。为了计算 a(1),我们将 a(0)乘以一组参数 Waa,将 x(1)乘以一组参数 Wax,然后将两者相加,再加上一个偏差 b_a。接下来,为了计算 y^(1,我们将 a(1)乘以一组参数 Wya,再加上一个偏差 b_y。现在,RNNs 的优点在于,我们对所有时间步长使用相同的参数 Wax、Waa、Wya。计算 a(t)和 y^(t 的方程式如下:
a(t)= g(W _ aa * a(t-1)+W _ ax * x(t)+b _ a)
y ^(t)=g(W_ya*a(t)+b_y)
我们用上面的等式计算 y(1、y(2、y(3、y(4 等等,直到 y^(t。然后,我们将所有 y^(t 与地面真实值 y(t)进行比较,以计算可以通过我们的网络反向传播的损耗。然后我们把每个时间步的损失加起来,这就是为什么我们称之为时间反向传播。损失函数类似于逻辑回归中使用的交叉熵损失函数。损失函数将如下:
L(t)(y^(t),y(t))= -y^(t)log y(t)-(1-y(t))log(1- y^(t))
总损失 = ∑ L(t)(y^(t),y(t))
到目前为止,我们一直在处理输入长度 X 等于输出长度 y 的情况。但是,情况可能并不总是这样。例如,在情感分类中,我们有一个输入序列 X,但是 Y 可以是 1 到 10 之间的整数。例如,假设您有一个文本“这部电影的情节很糟糕,表演也很一般”,您想给它打 1-5 颗星。在这种情况下,我们将在每个时间步输入一个字,而没有输出 y^(t,并在最后一个时间步或最后一个字输出单个 y^。这被称为多对一架构。还可能存在一对多架构的情况,例如,音乐生成,其中我们只有一个输入 a(0 ),它可以是一个整数,表示我们想要生成的音乐流派或音乐的起始音符。然后,我们从 a(0)获得输出 y^(1,该输出与 a(1)一起被馈送到下一个时间步骤,以获得 y^(2,该输出与 a(2)一起被再次馈送到下一个时间步骤,以生成 y^(3,然后是 y^(4,等等,直到生成最后一段音乐。
还可能存在多对多架构的情况,其中输入长度 X 不等于输出长度 Y,例如在机器翻译中,我们试图使用 RNN 将法语句子翻译成英语,法语句子的长度可能与翻译的英语句子不同。以下是不同类型的 RNN 架构的概述:
图片来自 Coursera 上的 Angrew Ng
消失梯度问题
到目前为止,我们讨论的可以称为基本 RNN 算法,基本 RNN 的一个问题是它可能会遇到消失梯度问题。例如,假设我们有一个句子“孩子在操场上跑了几个小时,到家时已经筋疲力尽了”。然后想想这句话“孩子们在操场上跑了几个小时,回到家时已经筋疲力尽了”。因此,为了保持一致,应该是“kid was”和“kid was ”,这向我们表明,句子可以具有长期依赖性,而基本的 rnn 无法捕捉到这一点。这意味着,当我们有几个时间步长的非常深的 RNNs 时,与后面的时间步长相关的误差很难影响前面的时间步长的计算。在这种情况下,RNN 必须记住“kid”是单数,因此以后应该使用“was ”,对于“kids”应该使用“were”。在英语中,我们看到单词可以有很长的依赖关系,这是基本的 rnn 无法解决的。这里,输出 y^(5 只受接近 y^(5 的值的影响,这使得输出很难受到序列中更早出现的输入的强烈影响。
另一方面,我们也可能遇到爆炸梯度问题,其中我们的参数变得非常大,并且不收敛。爆炸梯度问题可以通过梯度裁剪来解决。但是一般来说,与爆炸渐变相比,消失渐变是一个更常见也更难解决的问题。现在,让我们来谈谈格鲁斯和 LSTM,他们是用来解决梯度消失问题的。
门控循环单元
门控循环单元有一个新的变量,称为内存单元或“c ”,为 RNNs 提供所需的内存。所以在我们之前的例子中,记忆细胞帮助 RNN 记忆我们句子中的主语是单数还是复数,这样在后面的句子中,它可以考虑主语是单数还是复数。在每一个时间步,我们将考虑用一个新的变量 c ̃(t).覆盖 c(t)我们将使用参数 Wc 矩阵的激活函数 tanh 乘以前一存储单元 c(t-1)和当前输入 x(t)加上偏置项 b 来计算 c ̃(t。现在,GRU 的一个非常重要的元素是门γ_ u,也称为更新门,其值在 0 和 1 之间,因为它是使用 sigmoid 函数计算的。但是出于所有实际目的,γ_ u 可以被假定为 0 或 1,因为对于大多数值范围,sigmoid 函数非常接近 0 或 1。
然后我们尝试使用 c ̃(t 来更新 c(t)的值,并且门γu 决定我们是否更新它。所以基本上,如果γ_ u 等于 1,这意味着用 c ̃(t 更新单元存储器 c(t ),在我们的例子中,它可以用来记住我们句子的主语是单数,即“孩子”。现在,对于接下来的几个时间步,让我们假设γ_ u 等于 0,这意味着我们在告诉 RNN 不要更新存储单元的值,保持设置 c(t) = c(t-1),以便记住主语是单数。而到了需要决定主语是单数还是复数的时候,细胞记忆就会告诉我们,主语是单数,应该用‘was’而不是‘were’。如果我们不需要在句子中进一步记住主语是单数还是复数,γ_ u 可以被设置为 1,这意味着现在我们可以忘记旧值并用新值 c ̃(t).更新存储单元以下等式将更好地描述 GRUs 的工作方式:
1.̃(t) = tanh(W_c [c(t-1),x(t)] + b_c)
2.γ_ u = σ(w_u[c(t-1),x(t) ] + b_u
3.c(t)= γ_uc ̃(t)+(1-γ_ u) c(t-1)
在 GRUs 的情况下,c(t)是下一个时间步长 a(t)的激活值,c(t)也可以通过类似 softmax 的函数传递,以获得当前时间步长的输出 y(t)。这就是我们需要了解的关于 GRUs 的所有信息,下图将更好地展示 GRUs 的工作原理:
米歇尔·卡瓦奥尼关于机器学习叮咬的图片
长短期记忆
LSTM 是长短期记忆的缩写,是另一种可以解决消失梯度问题并帮助记忆序列中的长期依赖性的算法。LSTM 可以被认为是一个更强大和通用版本的 GRUs。一个主要的区别是在 LSTMs 中,a(t)不等于 c(t)。下面的等式支配着 LSTM 的工作,我们将详细讨论它:
- c ̃(t) = tanh(W_c [a(t-1),x(t)]+b_c)
2)γu = σ(w_u[a(t-1),x(t)]+b u)
3)γf = σ(w_f[a(t-1),x(t)]+b_f)
4)γ_ o = σ(w_o[a(t-1),x(t)]+b_o)
5)c(t)=γ_ u∫c ̃(t)+γ_ f∫c(t-1)
6)a^(t)=γ_ o∫c(t)
这里你可以看到我们用的是 a(t-1)而不是 c(t-1)。我们可以看到的另一个区别是,我们用γu 和γf 来计算 c(t ),而不是γu 和(1-γu)。并且γ_ f 被计算为 W_f 矩阵的 sigmoid 乘以[a(t-1),x(t)]加上 b_f。因此,存储单元 c(t)的新值是更新门,γ_ u 逐元素地乘以 c ̃(t)加上遗忘门,γ_ f 逐元素地乘以存储单元 c(t-1)的先前值。我们还有一个输出门γ_ o,它的计算方法是 W_o 矩阵的 sigmoid 乘以[a(t-1),x(t)]加上 b_o。此外,如前所述,a(t)不等于 c(t),而是等于输出门γ_ o 按元素乘以 c(t)。所以,基本上在 LSTM,我们有三个门:更新门,忘记门和输出门,而 GRUs 只有一个更新门。下图将让我们更好地了解 LSTM 是如何运作的:
Michele Caviaone 关于深度学习的图片
现在,想象有一堆并联的 LSTM 单元。每个单元将有一个输入 x(t),如 x(1),x(2),x(3),x(4)等等,并且一个单元的输出 a(t)将是下一个相邻单元的输入 c(t+1)。如果我们适当地设置遗忘和更新门,我们可以让某个值 c(1)一直传递到 c(6)而不被改变,使得 c(1) = c(6)。在我们之前的例句中,“孩子们在操场上跑了几个小时,到家时筋疲力尽”,主语是复数的信息可以存储在以“孩子”作为输入的单元的存储单元 c(2)中,就在以“The”作为输入的单元之后。然后,它可以一直被传送,直到该单元的存储单元 c(10)将“和”作为输入,使得 c(10) = c(3),从而我们得到 y(10)的期望输出,该输出应该是“是”。这就是为什么 LSTM 和格鲁非常善于记住长期依赖关系。下图显示了 LSTM 单元之间的连接方式:
吴恩达在 Deeplearning.ai 上的图片
LSTM 对格鲁
我们什么时候应该使用 GRU,什么时候应该使用 LSTM?这个问题没有明确的答案,尽管我们首先讨论了格鲁,但 LSTM 在实践中要早得多。然后 GRUs 作为更复杂的 LSTM 模型的简单版本被发明出来。与 LSTM 相比,gru 模型更简单,计算量也更小。因此,我们可以用 GRUs 建立更大的网络,尽管 LSTM 更强大、更有效。但更重要的是,这两种算法都不是普遍的优越算法,它真的取决于我们正在处理的数据和我们试图解决的问题。
这是我想在这篇文章中涵盖的所有内容。希望您能够对序列模型的工作原理及其在现实世界中的应用有一个很好的了解。我要感谢吴恩达博士关于深度学习专业化的课程,没有他,我就不能写这篇文章。我正在写一篇文章,在这篇文章中,我将演示我们如何用 python 实现这些算法,并创建一些有用的应用程序。我还在撰写一些关于光束搜索算法的文章,该算法可用于改善机器翻译和图像字幕,以及用于检查机器翻译和图像字幕性能的 bleu 评分。所以,敬请期待!
HDFS 擦除编码
通过利用擦除编码,显著降低 HDFS 集群的存储开销
假设:
您已经了解并内化了 Hadoop 分布式文件系统或 HDFS 的基本概念— 数据块&复制因子、存储&复制和机架感知
背景
Hadoop 分布式文件系统(HDFS)数据块和复制方法有两个关键概念,即“数据块大小”和“复制因子”。进入 HDFS 的每个文件都被分成几个块或“区块”。块数取决于分配的最大块大小,通常为 128 MB。创建数据块后,它们会在 HDFS 集群中复制。副本的数量由复制因子(RF)决定,通常配置为 3 (1 份原件和 2 份副本)。这种冗余有助于建立弹性和容错能力,也就是说,当一个数据块出现故障时,我们可以从另一个数据块安全地恢复数据。
基于块大小的文件分割= 128 MB
上图是我们刚刚讨论过的分割的快速演示。一个 700 MB 的文件被分成 6 个块。128 MB 的 5 份和 60 MB 的 1 份。复制系数为 3 时,它将消耗(6*3) = 18 个数据块和(700 MB * 3) = 2100 MB 的存储。考虑将其扩展到 Pb 级,您将很快意识到由于数据复制带来的冗余而导致的可用空间的严重利用不足。
因此,对于 3-n/3 路复制(即 RF = 3),其中 n =副本数量,我们有 2 个复制的数据块,即 200%的存储开销,效率为(1/n)或(1/3)或 33%,n-1 = 2 作为容错。更不用说创建、维护和执行 BAU 活动的网络和 I/O 使用情况了。
虽然廉价的存储和出色的网络带宽是当今世界的现实,但这种为容错和弹性建立冗余的方法仍然非常低效。这带来了一种新的模式,可以显著提高保护数据的效率。
擦除编码
通过英特尔和 Cloudera 的优秀人员的共同努力,擦除编码(EC)在 3.x 版中被引入 Hadoop。
继续同一个示例,一旦数据在 HDFS 被分割成几个块,它就作为输入被传递给 EC,EC 返回许多奇偶校验块。这个过程称为编码,而(数据+奇偶校验块)称为编码组。在失败(也称为擦除)的情况下,可以从该编码组中重建数据,称为解码。
引擎盖下发生了什么?
对 OR 和 XOR 有基本的数学理解
在被称为⊕的异或或异或编码中,数据(INs)通过被称为异或的数学运算传递,结果(OUT)为单个奇偶校验块,如上所示。此外,XOR 有两个漂亮的数学特性:
可交换的 : B1⊕ B2 = B2⊕ B1
联想:b1⊕【b2⊕B3】=【b1⊕b2]⊕B3
意思是——输入的排列不会改变输出。
作为推论,我们可以扩展到我们的示例,使用 6 个数据块作为输入,我们将在磁盘上总共消耗 7 个存储块,6 个数据块和 1 个奇偶校验块。与 HDFS 的三向复制相比,这显著提高了效率。并且如果任何一个数据块失败,例如如果 B2 失败,则剩余的块被异或,即 b1⊕P1⊕b3⊕b4⊕b5⊕b6,以恢复丢失的数据 B2。然而,≥2 个同时故障且数据不可恢复。因此,XOR 最多可以容忍 1 次故障,效率为((n-1)/n)或约 83%,其中 n =数据块的总数。由于 XOR 具有低容错性,所以它对于 HDFS 的要求来说是完全不可接受的。
里德·所罗门编码(来源)
Reed-Solomon**【RS】****编码**通过使用复数线性代数生成多个奇偶校验块,解决了 HDFS 处理多个同时失效的需求。RS 编码表示为 RS₍ₖ,ₘ₎,使用两个参数,其中 k 是数据块的向量,m 是奇偶校验块的数量。这是通过将数据块向量 k 乘以生成矩阵(Gᵀ)得到长度= (k+m)或长度=(数据+奇偶校验)的 Codeword₍ₖ₊ₘ₎向量而生成的,如上图所示。
当故障发生时,通过将剩余的块(或幸存者)乘以生成矩阵的逆矩阵来进行恢复,前提是 k 个块总是可用的。因此,最多可以容忍 m 个故障。在我们的示例中,使用 k & m 的最佳实践参数,即 RS(6,3),具有 6 个数据块的文件将消耗磁盘上的 9 个存储块(即 6 个数据块和 3 个奇偶校验块),或者每 2 个数据块消耗 1 个奇偶校验块。因此,只需要 50%的存储开销,效率为(k/(k+m))或约 67%。
还有另一种流行的 RS 配置,即 RS(10,4),其效率为(k/(k+m))或约 71%,存储开销约为 50%。
选择正确的区块布局
我希望到现在为止,你们都支持 HDFS 的(6,3)或(10,4)配置的 Reed Solomon 擦除编码。
在连续块布局中,你在 HDFS 基础上有一个更简单的实现。考虑部署在连续块布局上的 HDFS EC RS(10,4 ),块大小固定为 128 MB。无论您有 10 个数据块还是 1 个数据块要写入,都会写入 4 个奇偶校验块。因此,对于单个数据块(比如 B5),存储效率开销现在约为 400%,比三向复制还要糟糕。我们所有的存储效率收益都化为乌有。为了证明从 HDFS 三向复制切换到具有连续布局的 EC 的合理性,需要写入所有 10 个数据块。同样,对于具有相同块布局的 RS(6,3),我们需要 6 个写入的数据块来确保存储效率。
根据几个项目的个人经验和 Cloudera 的行业研究(参考下文),存储在 HDFS 的大约 70–95%的数据小于 1 个数据块或 128 MB。在条带化块布局中,文件被“条带化”为较小的大小,通常为 64 KB 或 1 MB 数据“单元”。因此,无论我们使用 RS(10,4)还是 RS(6,3),创建的奇偶校验单元都不会对存储开销产生太大影响。
最重要的是,HDFS 允许在集群中的目录或文件级配置不同的复制和擦除编码策略。由于文件大小是块布局的决定因素,从数据持久性和存储效率的角度来看,在文件/目录级别应用擦除编码策略的能力非常有益。
HDFS 的建筑变革
为了用条带化布局处理 EC,进行了某些体系结构上的更改。
名称节点扩展:
文件的逻辑块或字节范围已从存储块中分离出来,存储块在数据节点中存储实际的数据块。HDFS 命名节点现在将其块 ID 分别映射到数据节点的存储块和逻辑块。这在名称节点上产生了大约 250–440%的巨大存储开销。
具有 3 个部分的分层块
因此,引入了一种新的分层块命名协议来解决空间使用过度增长的问题。我们不是基于时间戳顺序分配块 id,而是将块映射分成 3 个部分,如上所述。注意,对于连续区块布局,只有 2 个部分。因此,神经网络能够以摘要-细节或分层协议的形式管理逻辑和存储块,并将开销限制在大约 21–76%。
客户端扩展:
将 DFSInputStream (Java 数据类型)扩展为 DFSStripedInputStream,将 DFSOutputStream (Java 数据类型)扩展为 DFSStripedOutputStream,以适应数据分条和 EC。由于逻辑块和存储块现在是分开的,使用条带化数据类型,我们可以在 HDFS 实现块的并行创建/处理。
数据节点扩展:
神经网络识别丢失的块,并将恢复分配给数据节点。这由一个新的组件来处理,即 ECW 或 ErasureCodingWorker 、,它执行三个任务。首先,它向所有没有失败的数据源发送一个读请求。第二,使用英特尔优化的 Reed Solomon Erasure 编解码器框架, ISA-L ,ECW 对数据进行解码。第三,它将恢复的数据推送到故障数据节点。
最后,让我们总结一下到目前为止我们所讨论的利弊:
优点
- 在线电子商务支持:将数据实时转换为电子商务,从而立即节省存储空间,并完全避免临时/导入后数据转换过程。
- 低存储开销:将存储开销降低到仅 50%左右。
- 向后兼容:HDFS 的大多数功能,如快照、加密和缓存,在 EC 模式下也可用。
限制
- 由于架构的变化,HDFS 的一些原生特性,如 hflush、hsync 和 append 在 EC 模式下不可用。然而,作为 HDFS-欧共体第二阶段的一部分,这一问题可能会得到解决。
- 在线/离线 EC 对集群提出了大量带宽和 IO 要求。但是,带宽与存储效率和耐用性之间的权衡应该被视为一种微妙的平衡,因为当使用 EC 进行条带化时,这种权衡是真正有益的。
参考文献:
[1] HDFS 擦除编码 (2017),Apache Hadoop,ASF
[2] A.Wang,Z. Zhang,K. Zheng,U. Maheshwara 和 V. Kumar,Apache Hadoop 中的擦除编码介绍 (2015),Cloudera
J. Plank 教授,存储应用擦除编码教程,第 1 部分 (2013),田纳西大学 EECS 系
登录页面
medium.com](https://medium.com/@prathamesh.nimkar/big-data-analytics-using-the-hadoop-ecosystem-411d629084d3)
简化精确度、召回率和 F1 分数
用基本术语解释评估指标
机器学习术语可能看起来非常费解,好像它们是为了让机器理解而制造的。不直观和听起来相似的名称,如假阴性和真阳性、精确度、回忆、ROC 下面积、敏感性、特异性和疯狂。好吧,最后一个不是真的。
已经有一些关于精确和召回的好文章,但是当我阅读它们和其他关于 stackexchange 的讨论时,这些混乱的术语在我脑海中混在一起,我比一个未标记的混乱矩阵更加混乱——所以我从来没有感觉到我完全理解了它。
混乱的混乱矩阵
但是要知道我们的模型是如何工作的,掌握评估指标并在深层次上理解它们是很重要的。那么一个数据科学家真正需要知道什么来评估一个分类模型呢?我在下面用图片和例子解释了最重要的几个,这样它就能永远留在我们的脑海里。
精度
让我们从最简单的——精确度开始。这实际上是你的模型在猜测正确的标签或基本事实方面有多好。如果你的数据集非常平衡,并且你关心每个类别是否正确,这就是你需要担心的。
可悲的是,如果你的数据集像欺诈检测数据集一样不平衡,那么非欺诈案例占你的标签的 80–90%的可能性很大。因此,如果您的模型盲目地将所有数据点预测为多数标签,我们仍有 80–90%的准确性。
这就是我们需要精确和回忆的时候。
精确度(也称为特异性)
精度是模型正确预测值与模型预测值的比率。对于每个类别/类,都有一个精度值。
当我们需要正确的预测时,我们关注精度,也就是说,理想情况下,当您预测标签时,您希望确保模型是正确的。举个例子,如果你有一个足球博彩模型预测是否下注,你最关心的是它是否正确,因为你会根据它的预测采取行动,但当它告诉你不要下注时,你并没有赔钱。
当倾向于精确时,错误预测的成本比错过正确预测的成本高得多。
回忆(也称为敏感度)
召回率是模型正确预测的数量与实际标签数量的比率。类似地,对于每个类别/类,都有一个召回值。
当我们想要最大化特定类别的预测时,我们关心回忆,也就是说,理想情况下,你希望模型捕获该类别的所有示例。例如,机场安检扫描机必须确保探测器不会漏掉任何真正的炸弹/危险物品,因此我们可以接受有时拦下错误的行李/旅客。
当倾向于回忆时,错过一个预测的代价比一个错误的预测要高得多。
f1-得分:结合精确度和召回率
如果我们希望我们的模型有一个平衡的精度和召回分数,我们平均他们得到一个单一的指标。但是什么样的平均才是理想的呢?对于精确度和召回率这样的比率,与通常的算术平均值相比,像 F1-Score 这样的调和平均值更合适。
调和平均数的定义似乎很复杂:分数的算术平均数的倒数和倒数。我处理冗长定义的方法是从最深层开始,逐层理解。有 3 个:
权衡:生活的现实
正如你可能已经想到的,精确度和回忆对我们来说更重要——是错误预测的代价,还是错过真相的代价?很多时候,你必须放弃一个才能得到另一个。下面是谷歌对权衡的一个很好的解释/viz,以及如何切换分类阈值让我们决定我们关心什么——注意,这也将改变我们的 F1 分数。
https://developers . Google . com/machine-learning/crash-course/classification/precision-and-recall
结论
我希望这用简单直观的方式解释了准确度、精确度、召回率和 F1。结合示例,我认为这是理解其他评估指标的良好开端。那么,你的业务目标是更接近博彩模式、机场扫描仪,还是两者兼而有之?
资料来源:联合国人类住区规划署
简化机器学习中的决策树
最流行和最常用的 ML 算法之一
资料来源:联合国人类住区规划署
这是机器学习最简单和最基本的模型之一,可以用于分类和回归。让我们以我们熟悉的问答方式深入了解决策树。我们在这里还有一个理论的视频讲座,在这里还有 python 的实际操作。
决策树有哪些组成部分?
基本成分是
一个**)根节点:**这通常会在节点中提供完整的训练数据。来自所有类的数据将混合在一起。
b) **决策节点:**通常情况下,它的格式类似于属性“A”和条件“k”。例如年龄> 30,这创建了另外两个节点。
c) **内部节点和终端节点:**内部节点是决策节点的结果,如果它们足够纯,它们可以是终端节点,否则它们是内部节点,这将需要再次经过决策节点。决策节点可以清楚的告诉我们,一个未知的观测如果落在那个节点会被贴上什么标签。
让我们用一个非常简单的例子来说明,假设我们有 10 个学生的数据,他们的语言能力和数学成绩。根据学生的安置成功率,将学生分为三类。这三个类分别被立即放置(PI)、一段时间后放置(PA)、未放置(U)。
图 1:样本学生安置数据(来源:作者)
当一个决策树,它将有一个结构如下,为上述数据。该树具有一个根节点、两个决策节点(数学=高,英语=高)和对应于三个类别的三个终端节点。
图 2:学生安置数据的决策树(来源:作者)
观察结果:
- 就类而言,根节点是非常混合的,当我们移动到终端节点时,节点具有更同质的群体(最多 2 个类,1 个类是大多数)。
- 现在,如果我们有一个学生萨姆,我们知道他数学和英语成绩都很高,按照决策树,我们知道,他被立即安排的机会非常高。
因此,一个有争议的问题是,我们希望向更同质的终端节点发展。这需要定义一个同质性或缺乏同质性的衡量标准。
我们如何正式度量一个节点的同质性或异质性?
这需要引入一个叫做熵的概念。
熵:
它是对随机变量中存在的不确定性的一种度量。根节点是所有类的混合,所以如果我们随机选择一个,不确定性在根节点是最高的,并且我们向终端节点移动,这种不确定性逐渐降低。我们称之为信息增益。
我们现在明白了,熵将是概率的函数。
图 3:熵方程(来源:作者)
X 是随机变量,H(X)是随机变量的熵。s 是随机变量或样本空间的一组值。s 是采样点。我想你会同意抛硬币是任何随机实验的终极 hello world。
当它是一个公平的硬币时,它是完全随机或不确定的,逐渐地我们可以偏向它,使它甚至为 1。让我们用例子再次理解这一点。我们来看下头部概率的不同场景。
图 4:熵值与头部概率(来源:作者)
观察:
- 当头部和尾部具有相等的概率时,熵最高
- 当压头完全确定时,它是最低的
- 在这两者之间,随着获得优势的确定性增加,熵值下降了
所以,我们知道我们应该从高熵的节点转移到低熵的节点,但是如果有两个属性‘a’和‘b’,我们如何使用熵来做出这个决定。
信息增益进入的时间。
什么是信息增益?
这由下面的等式正式定义。这个想法很简单。假设根节点具有熵 E,并且当我们在某种条件下(比如数学分数=高)通过属性“a”进行分割时,我们得到另外两个节点 N1 和 N2,它们具有相应的熵 E1 和 E2。
第一个切割信息增益将是 E -( E1 + E2)/2。下一步,我们只是做了一个改进,用 N1 和 N2 分别出现的元素数量来衡量熵。这就是下面等式中的形式。
H(T) —分裂前的熵 H(T/a) —在某种条件下被属性‘a’分裂后的熵
图 5:信息增益方程(来源作者)
如何应用信息增益来选择要拆分的属性?
让我们用必要的数学知识进一步理解这一点,让我们比较两种可能的拆分选项,如“A”和“B”
图 6:分割选项(图片:作者)
信息增益的计算如下所示
图 7:通过属性 A 或 B 分割的选项(来源:作者)
观察
- 场景 A 的熵为 0.81,场景 B 的熵为 0.69
- 根节点的熵为 1.0(与投掷公平硬币的场景相同)
- 因此,信息增益在第一种情况下为 0.19,在第二种情况下为 0.31 。因此,将选择属性 B。
还有另一个衡量信息增益的等效指标,叫做基尼指数。根据参考文献[1],只有 2%的情况下使用基尼指数或信息增益才重要。所以,我们不要再提这件事了。
这些树有哪些不同的变种?
- 这更具有学术意义,我们在这里讨论初步的树,如 ID3、C4.5、C5.0 和 CART。
- ID3 迭代二分器 3 使用信息增益。由罗斯·昆兰于 1986 年发明
- 它首先找到要分割的最佳分类属性。
- 然后递归选择下一个属性(未选择)
- 直到不可能再进行拆分,即到达叶节点或没有属性留下来进行拆分
- 这是一个贪婪的算法,不回溯。
- C4.5 取消了分类属性的限制,但允许将数值属性转换为分类属性(J48)
- C 4.5 有能力处理丢失的数据 C4.5 有能力回溯它在数据挖掘的十大算法中排名第一之后变得非常流行
- C 5.0 在生成更小的树、有效使用内存、加速
- CART 是另一个非常著名的算法,它可以处理数值和分类属性,可以处理分类和回归,并且具有处理异常值的能力。它的分裂是双向分裂。
我们如何处理决策树中的过度拟合?
像任何机器学习问题一样,这些模型也容易过度拟合。如果一棵树被过度种植,那么它通常会有更多的深度。更多数量的终端节点。所以,一棵光滑的树会更少的过度生长。我们在这里讨论一些策略。
a)叶或末端节点中的最小观察数:
理想情况下,为了拟合数据,我们最终可以在所有叶节点中进行一次观察。会很好地拟合训练数据,测试数据失败
b)分割一个节点所需的最小观察次数:
这将确保我们不会为了更高的训练集精度而继续分裂甚至具有少量观察值的节点
最大允许深度:
基本上,随着最大允许深度的增加(基本上是连续决策节点的数量),树往往会过度拟合。
图 8:最大深度对测试集准确性的影响(来源:作者)
上图是我们使用决策树(https://www.kaggle.com/saptarsi/decision-tree-sg/)进行实验的结果,还有一些有趣的实验,你可以了解一下。
它清楚地显示了 12 的深度是好的,在此之后,测试精度没有太大的增加。
d)正则化损失函数:
下面的等式是在回归树的上下文中,其中 T 是叶节点的数量,基本上,决策树将特征空间分割成矩形区域。如果有 T 个节点,则在特征空间中将有 T 个矩形判定区域。随后的目标函数的第一部分是正则平方误差,第二部分针对决策区域的数量进行惩罚或正则化。
所以,如果我们有两棵树,都有相同的平方误差。树 1 有 5 个叶节点,树 2 有 4 个叶节点。这个等式会更喜欢第二个。(转载自 ISLR)
图 9:决策树目标函数(来源:作者)
结论:
- 决策树提供了极好的可视化,分类的原因对业务用户来说是清楚的,因此是一个白盒模型。
- 非常简单易懂
- 但是,可能无法提供最佳性能(这可以使用随机森林、增强等解决。)
========================================
谢谢你读到这里。这些是我们在加尔各答大学数据科学实验室创建的一些附加资源
一)用 Python 分类(https://www.youtube.com/playlist?list = plts 7 rwcd 0 do 2 zoo 4 sad 3 jrxnvfyxhd 6 _ S
b)用 Python(https://www.youtube.com/playlist?)进行聚类 list = plts 7 rwcd 0 do 3ts 44 xgwgvgxmeohyfi 3pm)
参考:
[1] Raileanu LE,Stoffel K .基尼指数与信息增益标准的理论比较。数学与人工智能年鉴。2004 年 5 月 1 日;41(1):77–93.
[2]https://towards data science . com/the-complete-guide-to-decision-trees-28 a4 E3 C7 be 14
[3]https://towards data science . com/decision-trees-in-machine-learning-641 B9 c4e 8052
MapReduce
简化 MapReduce 框架
Apache Hadoop MapReduce 架构
在 2003 年,Google 提出了一个迷人的框架,通过他们革命性的白皮书“MapReduce:大型集群上的简化数据处理”,在分布于多个节点的大型数据集上实现并行处理。
现在,MapReduce (MR)是 Hadoop 的主要处理框架,跨多个应用程序使用,如 Sqoop、Pig、Hive 等。
数据存储在 HDFS
如果你是 HDFS (Hadoop 分布式文件系统)的新手,或者想要复习一下,我建议你看看我的综合指南。否则,继续阅读。
在上面的流程图中,我们在 HDFS 存储了一个大的 csv 文件。我们的复制系数(RF)为 2,数据块大小为 128 MB,有 2 个数据节点。因此,B1、B3、B3、B4 等,每个大小为 128 MB,放置在两个数据节点中,如上面的流程图所示。
Hadoop 与传统客户端-服务器架构的不同之处在于,数据位于静态位置,而作业/流程移动到数据所在的位置。这显著提高了性能。
想想看,数据的大小通常是 GB 到 PBs,而作业/进程只有几 MB。因此,通过网络将数据转移到作业/流程中非常昂贵,但是通过网络将作业/流程转移到存储数据的地方非常便宜。这就是 HDFS 建筑的美。
制图人
从本质上讲,谷歌的人认为数据处理的大多数用例都适合 Map + Reduce 任务。我认为大约 90%的任务都适合这种形式。
那么,什么是映射器呢?映射器只是一个函数,它接受一个键、值(k,v)对,对其进行处理并返回一个(k,v)对。下面是它的编程方式:
map(in_key, in_value) -> list(intermediate_key, intermediate_value)
工作开始:地图 0%减少 0%
制图人
- 输入格式— 指向 HDFS 文件块位置的指针。这些数据仍然没有被加载到内存中,目前按原样放在 HDFS 上。在我们的例子中,节点 1 的输入格式中的指针指向块 1 和块 2。类似地,在节点 2 中,它指向块 3 和块 4。
- Split — 此时,文件实际上被加载到内存中。拆分的数量等于该节点中的块数。拆分器和 RecordReader 一起工作。
- RR 或 RecordReader — 我肯定您想知道如何将一个简单的文件转换成(k,v)对。嗯,谷歌在他们的白皮书中提到,大部分处理是通过抽象完成的,这很好!记录阅读器只是为我们将数据处理成(k,v)对。此外,有多种方法可以实现这一点。更多细节见下文。
- Map — 最后,我们到达“Map”函数,在这里进行实际的处理。无论您希望函数执行什么逻辑,这里都是它发生的地方。发布此消息后,最终得到的(k,v)对再次被卸载到 HDFS,reducer 任务开始。
工作状态:地图 100%减少 0%
还原剂
一个 reducer,就像一个 mapper,也是一个函数,它接受一个(k,v)对,处理它并返回一个(k,v)对。以下是图示:
reduce(intermediate_key, list(intermediate_value) -> list(out_key, out_value)
还原剂
- 分割器— 中间(k,v)对再次被原样加载到内存中。并且使用这些中间键,将一个分组函数应用于数据集。我们将在下一节的例子中更好地理解这一点。如果你觉得幸运的话——这里有一个定制分区。
- Shuffle — 这是跨节点的分组。基本上,公共键现在在节点间“混洗”。
- 排序— 数据现在根据关键字排序。
- Reduce — 最后,我们到达“Reduce”函数,在这里进行数据的实际聚合。无论您希望该函数执行什么样的聚合,这里都是它发生的地方。
- 输出格式— 最终得到的(k,v)对通过 RecordWriter (RW —更多细节见下文)再次卸载到 HDFS,作业完成。
工作状态:地图 100%缩小 100%
用一个简单的例子把它们放在一起
假设你是特斯拉汽车公司的首席执行官埃隆·马斯克。你得到了以下特斯拉汽车一年来的全球销量数据(百万辆)。很自然,你很高兴,你抽了一些,然后马上在推特上说:
特斯拉的股价在我看来太高了
好吧,那是个错误。那你哭还是不哭。反正我跑题了。
特斯拉汽车销售数据集(百万)
这些数据现在在 HDFS 被分成几个数据块,平均分布在两个数据节点中(根据 RF)。这是两个区块(B1 和 B3)的样子:
映射器输出:
**Country,Sales(M)**
USA,1
Russia,1
UK,1
France,1
China,1
Russia,1
UK,1
France,1
China,1
USA,1**Country,Sales(M)** UK,1
USA,1
China,1
UK,1
USA,1
China,1
UK,1
USA,1
China,1
UK,1
输出到 HDFS —生成 2 个文件
减速器:
**Country,Sales(M): Partition** USA,1
USA,1
Russia,1
Russia,1
UK,1
UK,1
France,1
France,1
China,1
China,1**Country,Sales(M)**: **Partition** UK,1
UK,1
UK,1
UK,1
USA,1
USA,1
USA,1
China,1
China,1
China,1**Country,Sales(M)**: **Shuffle** Russia,1
Russia,1
France,1
France,1**Country,Sales(M)**: **Shuffle** USA,1
USA,1
USA,1
USA,1
USA,1
UK,1
UK,1
UK,1
UK,1
UK,1
UK,1
China,1
China,1
China,1
China,1
China,1**Country,Sales(M)**: **Sort** France,1
France,1
Russia,1
Russia,1**Country,Sales(M)**: **Sort** China,1
China,1
China,1
China,1
China,1
USA,1
USA,1
USA,1
USA,1
USA,1
UK,1
UK,1
UK,1
UK,1
UK,1
UK,1**Country,Sales(M)**: **Reduce** France,2
Russia,2**Country,Sales(M)**: **Reduce** China,5
USA,5
UK,6
输出到 HDFS —生成 2 个文件。大概就是这样。有问题吗?不要犹豫地问。
记录阅读器
以下方法可用于将数据转换成(K,V)对:
RecordReaderInputFormat
记录器(RW)
以下方法可用于将(K,V)对转换为输出数据:
RecordWriterOutputFormat
优步模式
摘自我的 Apache Sqoop 帖子(脚注中的链接):
MapReduce 作业的 Mapper 和 Reducer 任务由 YARN 的资源管理器(RM)在分布于几个节点的两个独立容器中运行。但是,如果您的数据集很小,或者您的作业包含小型制图工具任务,或者您的作业仅包含一个缩减器任务,我们可以将优步模式设置为 TRUE。这迫使 RM 在一个容器或 JVM 中顺序运行 mapper 和 reducer 任务,从而减少了启动新容器和跨多个节点为一个小任务建立网络的开销。工作完成得更快。
参考资料:
[1] J. Dean,S. Ghemawat (2003), MapReduce:大型集群上的简化数据处理,Google
[1.1] J. Dean,S. Ghemawat (2003), HTML 幻灯片— MapReduce:大型集群上的简化数据处理,Google
[2] Manjunath (2016), MapReduce 自定义分区器,Acadgild
[3] C. Chaudhari,什么是优步模式? (2018),Cloudera 社区
[4] MapReduce 教程 (2019),Apache Hadoop MapReduce 客户端,ASF
使用 Apache Flume 将非结构化数据涓滴输入 HDFS
towardsdatascience.com](/apache-flume-71ed475eee6d) [## Apache Sqoop
RDBMS 到 HDFS 并返回
towardsdatascience.com](/apache-sqoop-1113ce453639) [## 使用 Hadoop 生态系统的大数据分析渠道
登录页面
medium.com](https://medium.com/@prathamesh.nimkar/big-data-analytics-using-the-hadoop-ecosystem-411d629084d3)
用 CycleGAN 和 PyTorch 来简化自己
使用 CycleGAN 和 PyTorch 将人脸转换为 Simpsons 角色
作者图片
作者图片
Cyclegan 是一个能够进行不成对图像到图像翻译的框架。它被应用在一些非常有趣的案例中。比如把马转换成斑马(T4),把冬天的照片转换成夏天的照片。
我认为这可能适用于辛普森一家。我的灵感来自于像变黄和 makemeyellow 这样的网站。这个想法是你上传一张你的脸的照片。Cyclegan 会把它翻译成辛普森一家的角色。
值得注意的是论文明确提到大的几何变化通常是不成功的。所以这不太可能有好结果。
但我还是要尝试一下。
安装
首先我们需要安装 CycleGAN。
!git clone https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix
import os
os.chdir('pytorch-CycleGAN-and-pix2pix/')
!pip install -r requirements.txt
数据集
创建数据集比您最初想象的要困难。
为了创建这个数据集,我需要找到辛普森一家角色的特写镜头和普通人的特写镜头。
下载辛普森数据集
最初我的想法是从谷歌图片中抓取图片。不幸的是,要做到这一点,看起来你需要一个来自谷歌控制台的开发者密钥。
所以我从 Bing 中抓取图片。
这在一定程度上起了作用。但是花了这么长时间才下载完所有的图片。在我看了这些照片后,我注意到其中一些根本没有任何人脸。
谢天谢地,我在 kaggle 上偶然发现了一个数据集,里面有我需要的一切。它包含了从几季中提取的辛普森面孔。每个图像为 200x200 像素,包含一张脸。
该数据集将存储在trainA
文件夹中。
def create_training_dataset_simpson_faces(download_dir):
%cd $download_dir
# download dataset and unzip
!kaggle datasets download kostastokis/simpsons-faces --force
!unzip \*.zip
!rm *.zip
!cp -a $download_dir/cropped/. $download_dir
# remove unnecessary folders
!rm -Rf $download_dir/cropped
!rm -Rf $download_dir/simplified
# go back to orig directory
%cd /content/pytorch-CycleGAN-and-pix2pix
create_training_dataset_simpson_faces(TRAIN_A)
下载真实人脸数据集
为了创建真实人脸的数据集,我做了一点实验。
Will Kwan 最近使用 stylegan2 在他最近的一个视频中生成数据集。这对他来说似乎相当有效。所以我想我也可以做同样的事情。
这里有一些取自 Nvidia 的 stylegan2 github 库的人脸示例。如你所见,GAN 的输出相当逼真。
这可能比在网上搜索人脸要好得多。我可以为我的模型创建尽可能多的面。另外,我也不必下载笨重的大文件。
该数据集将存储在trainB
文件夹中
创建数据集
import matplotlib.pyplot as plt
from tqdm.notebook import tqdm# see Github for full code: [https://github.com/spiyer99/spiyer99.github.io/blob/master/nbs/cyclegan_simpsonify.ipynb](https://github.com/spiyer99/spiyer99.github.io/blob/master/nbs/cyclegan_simpsonify.ipynb)def create_training_dataset_real_faces_stylegan(download_dir):
# create in batches of 100
# reduces RAM requirements
counter = 0
pbar = tqdm(total = LIMIT)
while counter < LIMIT:
seeds = np.random.randint(10000000, size=100)
imgs = generate_images_from_seeds(seeds, 0.7)
for img in imgs:
img.save(download_dir/'real_face_{}.jpg'.format(counter), 'JPEG', quality=100)
counter+=1
pbar.update(1)
del imgs
create_training_dataset_real_faces_stylegan(TRAIN_B)
列车测试分离
接下来,我们需要将数据分为训练和测试。testB
将包含我们想要转换成辛普森一家角色的真实面孔。testA
将包含辛普森一家的人物,我们想把他们变成真人。
# move images to a new folder
# `images` is the existing image directory:
# `new_dir` is the path that the images will be moved to
# `files_limit` is the limit of files that will be moved
def move_all_images_to_new_folder(images, new_dir, files_limit = None):
files = glob.glob(str(images/'*.*g'))
if(files_limit is not None):
files = files[:files_limit]
for file in files: shutil.move(file, new_dir/os.path.basename(file))
move_all_images_to_new_folder(TRAIN_A, new_dir = TEST_A, files_limit = int(min(LIMIT*0.1, 25)))
move_all_images_to_new_folder(TRAIN_B, new_dir = TEST_B, files_limit = int(min(LIMIT*0.1, 25)))
查看我们的培训和测试数据
让我们看看我们正在处理的图像。
import PIL
import random
def plot_from_image_path(path, title):
all_imgs = glob.glob(str(path/'*.*g'))
print(f'{len(all_imgs)} imgs in {title} directory')
img_path = random.choice(all_imgs)
img = PIL.Image.open(img_path)
plt.imshow(img)
plt.title(title)
plt.show()
plot_from_image_path(TRAIN_A, 'TRAIN_A')
plot_from_image_path(TRAIN_B, 'TRAIN_B')
plot_from_image_path(TEST_A, 'TEST_A')
plot_from_image_path(TEST_B, 'TEST_B')
作者图片
作者图片
一切看起来都很好!
创建模型
现在我们可以创建模型了。我对现有的脚本做了一些调整。
让我们创建几个助手函数。
这些功能帮助我将保存的模型复制到我的 google drive。它还有助于测试模型并将输出图像存储到 google drive。
import os
from pathlib import Path
from distutils.dir_util import copy_tree
import matplotlib.pyplot as plt
import random
def copy_to_drive(folder = 'cyclegan_simpsonify'):
drive_folder = Path('/content/drive/My Drive/')/folder
if(drive_folder.exists()):
shutil.rmtree(drive_folder)
shutil.copytree('/content/pytorch-CycleGAN-and-pix2pix/checkpoints/'+NAME+'/', str(drive_folder))
def get_corresponding_photo(file_path):
return file_path.replace('fake', 'real')
def plot_results(number):
for i in range(number):
img_path = random.choice(glob.glob('./results/'+NAME+'/test_latest/images/*fake.*g'))
print(img_path)
img = plt.imread(img_path)
plt.imshow(img)
plt.title('fake')
plt.show()
print(get_corresponding_photo(img_path))
img = plt.imread(get_corresponding_photo(img_path))
plt.imshow(img)
plt.title('real')
plt.show()
def get_model(src, dst):
# copy across model
try:
os.remove(dst)
except:
pass
shutil.copyfile(src, dst)
def copy_from_drive(folder = 'cyclegan_simpsonify'):
drive_folder = Path('/content/drive/My Drive/')/folder
if(not Path('/content/pytorch-CycleGAN-and-pix2pix/checkpoints/').exists()):
os.mkdir('/content/pytorch-CycleGAN-and-pix2pix/checkpoints/')
if(Path('/content/pytorch-CycleGAN-and-pix2pix/checkpoints/'+NAME+'/').exists()):
shutil.rmtree('/content/pytorch-CycleGAN-and-pix2pix/checkpoints/'+NAME+'/')
shutil.copytree(str(drive_folder), '/content/pytorch-CycleGAN-and-pix2pix/checkpoints/'+NAME+'/')
def test_model (number_results = 5, direction = 'BtoA', src = None, dst = None):
# delete results folder and recrete
shutil.rmtree('./results')
os.mkdir('./results')
# get appropriate model
if (src is None): src = './checkpoints/'+NAME+'/latest_net_G_'+direction.split('to')[-1]+'.pth'
if (dst is None): dst = './checkpoints/'+NAME+'/latest_net_G.pth'
get_model(src, dst)
if (direction == 'BtoA'):
test = TEST_B
else:
test = TEST_A
cmd = 'python test.py --dataroot '+str(test)+' --name '+str(NAME)+' --model test --no_dropout'
os.system(cmd)
plot_results(number_results)
让我们为培训创建选项。
import time
from options.train_options import TrainOptions
from data import create_dataset
from models import create_model
from util.visualizer import Visualizer
import shutil
import os
from pathlib import Path
from tqdm.notebook import tqdm
options_list = ['--name', NAME,\
'--dataroot', TRAIN_A.parent,\
'--batch_size', BATCH_SIZE,\
'--checkpoints_dir', './checkpoints',\
'--lr', 2e-4,\
'--n_epochs', EPOCHS,\
'--n_epochs_decay', EPOCHS//2,\
'--name', NAME]
opt = TrainOptions().parse(options_list)
首先,我们使用前面指定的选项创建数据集。
dataset = create_dataset(opt)
dataset_size = len(dataset)
print('The number of training images = %d' % dataset_size)
然后,我们创建模型并运行 setup 调用。
model = create_model(opt)
model.setup(opt)
visualizer = Visualizer(opt)
total_iters = 0
我们从 A 到 b 来看生成器,命名约定和论文略有不同。在论文中发电机被称为G
。
在代码中,他们称这个映射函数为G_A
。意思还是一样的。
该发生器功能从A
映射到B
。
在我们的例子中,它从辛普森一家映射到现实生活。
model.netG_A
作者图片
这里我们可以看到模型使用了 Resnets 。
我们有Conv2d
、Batchnorm
、ReLU
、InstanceNorm2d
和ReflectionPad2d
。InstanceNorm2d
和ReflectionPad2d
对我来说是新的。
InstanceNorm2d
:这与批量定额非常相似,但它一次应用于一幅图像。
ReflectionPad2d
:这将使用输入边界的反射填充张量。
现在我们也可以看看鉴别器。
model.netD_A
作者图片
鉴别器使用LeakyReLU
、Conv2d
和InstanceNorm2d
。
LeakyReLU
有意思。ReLU
是增加网络非线性的激活。但是什么是LeakyReLU
?
ReLU
将所有负值转换为0
。由于0
的梯度是0
神经元达到大的负值,有效神经元抵消到0
。他们实际上是“死”了。这意味着你的网络最终会停止学习。
这种效应被称为将死 [ReLU](https://datascience.stackexchange.com/questions/5706/what-is-the-dying-relu-problem-in-neural-networks)
问题。
LeakyReLU
旨在解决这个问题。该功能如下所示:
由 PyTorch 在 PyTorch 文档上拍摄的图像
这个函数本质上解释为:如果一个值是负的,则将其乘以negative_slope
,否则什么也不做。negative_slope
通常是0.01
,但是你可以变化一下。
所以LeakyReLU
大大降低了负值的幅度,而不是将它们发送给0
。但是这是否真的有效还没有定论。
培养
现在,我们可以在多个时期内训练模型。我在这里指定了10
纪元。
以下是培训代码:
所有神奇的事情都发生在这里。它运行损失函数,获取梯度并更新权重。通过这样做,它优化了生成器和鉴别器。
让我们训练它!
测试
我让模型在 google colab 上通宵训练,并将.pth
模型复制到我的 google drive 上。
让我们看看输出。
test_model(10, 'BtoA')
作者图片
作者图片
作者图片
作者图片
作者图片
作者图片
这是一个好的开始。我特别喜欢这张图片:
作者图片
老实说,它需要一些改进。
让我们尝试运行AtoB
周期。所以我们要把辛普森一家的角色转换成人脸。
test_model(10, 'AtoB')
作者图片
作者图片
作者图片
作者图片
丰富
Cyclegan 的作者指出需要大的几何变化的任务不太可能成功。我刚刚证实了这一点。
该网络似乎在努力应对将辛普森一家的角色转换成真人(反之亦然)所需的巨大几何变化。我不确定更多的培训能否解决这个问题。GANs 的棘手之处在于弄清楚何时停止训练。目视检查似乎是 Cyclegan 的答案。再训练几天看看会发生什么也许是值得的。
完整的 jupyter 笔记本可以在 Github 上找到
原载于 2020 年 8 月 30 日https://spiyer 99 . github . io。
对剔除抽样的 3 分钟回顾:模拟任何分布
3 分钟回顾
一个简短的概率和算法教程,值得你花时间
在 Unsplash 上 Miikka Luotio 的照片
当您想要某个概率分布的一些值时,比如说正态分布,您可以简单地在 R 中调用rnorm
,或者在 Python 中调用numpy.random.normal
。但是你有没有想过他们是怎么做到的?潜在的想法非常简单却非常强大。在这篇文章中,我将直观地解释它,而不会用任何数学符号来让你厌烦。在 3 分钟内,您将能够实现您自己的定制分发模拟器。我们开始吧!
蒙特卡罗模拟拒绝抽样
任何计算机系统都应该有一个伪随机数发生器,它能够给你(伪)均匀分布的随机数。这是我们的起点。核心问题是:给定均匀随机值,我们如何生成不同的分布?
考虑一个投掷飞镖的游戏,在这个游戏中,你向一块长方形的木板投掷飞镖。玩之前,你在上面画一个钟形曲线(高斯概率密度函数)。假设你的飞镖均匀地落在整个区域。自然,会有一些飞镖落在曲线上方,一些落在曲线下方。在足够多的投掷次数后,它应该看起来像下图的下半部分。如果我们去掉曲线上方的红色部分,为蓝色部分做一个直方图,它应该看起来像上半部分的那个。
这是我在 P5.js 中的实现,可以随意去我的网站通过调优迭代次数和改变分布自己试试!
这张照片展示了 2000 次投掷。当你扔的次数多了很多,上半部分的黄色轮廓会越来越接近底部的钟形曲线。
这个游戏被称为拒绝抽样算法,或接受-拒绝法,每次投掷飞镖都是一次蒙特卡洛模拟。
这里到底发生了什么?你可以把 x 的值想成一个均匀随机数sampleX
,把 y 的值想成另一个,sampleY
。那么一掷镖的位置就是(sampleX, sampleY)
。算法是这样的:
- 首先,你画一个
sampleX
,然后你立即画一个sampleY
。(扔飞镖) - 您将
sampleX
插入到您想要的分布的 PDF 中,在本例中是高斯 PDF,并在对应于sampleX
的紫色曲线targetY
上获得一个值。 - 你把
sampleY
比作targetY
。如果sampleY < targetY
,表示我们有一个“命中”,即飞镖落在曲线下方。这个随机数sampleX
被“接受”并作为高斯样本返回。如果sampleY >= targetY
,这意味着我们有一个“错过”,然后sampleX
被拒绝,并且没有成功的高斯样本从该试验中抽取。继续下一个试验。 - 一旦你有了足够的试验,所有被接受的
sampleX
形成了一个正态分布的随机值列表。
就是这个!这就是我们用均匀随机数得到高斯随机数的方法。
事实上,您可以将其更改为任何您想要的目标分布!在板上画一个疯狂的曲线,开始投掷。如果低于曲线,接受;如果高于曲线,拒绝。就这么简单。曲线甚至不需要积分到 1,就相当于缩放这条曲线可以得到的 PDF。
我用 P5.js 在浏览器中创建了一个可视化。您可以通过调整迭代次数和改变分布类型来随意尝试。
摘要
拒绝采样是一个非常简单的想法,非常简单,你只需不到 5 分钟就能学会,而且没有任何公式!尽管它很简单,但对于计算统计学和任何依赖于概率的技术来说都是至关重要的。rnorm
和numpy.random.normal
使用的实际算法被称为金字形神算法。这是在拒绝抽样和蒙特卡罗方法的保护伞下。有兴趣可以看看。希望你喜欢这个非常短的教程,并欣赏这个算法的小宝石的美丽!