Kaggle : Python : DataQuest
Kaggle.com
第二部分:卡格比赛和数据任务教程在这句话中连在一起。在处理完第 1 部分之后。我回来接受更多的惩罚。
我正在使用 Cloud9 IDE,它有 ubantu,我从 Python2 开始,但我可能会以 python 3 结束。我仍然使用 DataQuest 作为我的指南,所以我们开始吧!请记住,到目前为止,我的代码如下所示:
import pandas import numpy as np from sklearn import cross_validation from sklearn.linear_model import LogisticRegression titanic = pandas.read_csv(“train.csv”) predictors = [“Pclass”, “Sex”, “Age”, “SibSp”, “Parch”, “Fare”, “Embarked”] alg = LogisticRegression(random_state = 1) kf = cross_validation.KFold(titanic.shape[0], n_folds=3, random_state=1) predictions = [] fillage = titanic[“Age”].median() titanic[‘Age’] = titanic[‘Age’].fillna(fillage) titanic.loc[titanic[“Sex”] == “male”, “Sex”] = 0 titanic.loc[titanic[“Sex”] == “female”, “Sex”] = 1 titanic[“Embarked”] = titanic[“Embarked”].fillna(‘S’) titanic.loc[titanic[“Embarked”] == “S”, “Embarked”] = 0 titanic.loc[titanic[“Embarked”] == “C”, “Embarked”] = 1 titanic.loc[titanic[“Embarked”] == “Q”, “Embarked”] = 2 for train, test in kf: train_predictors = (titanic[predictors].iloc[train,:]) train_target = titanic[“Survived”].iloc[train] alg.fit(train_predictors, train_target) test_predictions = alg.predict(titanic[predictors].iloc[test,:]) predictions.append(test_predictions) predictions = np.concatenate(predictions, axis=0) predictions[predictions > .5] = 1 predictions[predictions <=.5] = 0 num = len(predictions)
i=0 count = 0.0 #print(count) for prediction in predictions: if predictions[i] == titanic[“Survived”][i]: count = count + 1 i = i+1
accuracy = float(count/num) scores = cross_validation.cross_val_score(alg, titanic[predictors], titanic[“Survived”], cv=3)
titanic_test = pandas.read_csv(“test.csv”) fillFare = titanic_test[‘Fare’].median() fillAge = titanic_test[‘Age’].median() +1 titanic_test[‘Fare’] = titanic_test[‘Fare’].fillna(fillFare) titanic_test[‘Age’] = titanic_test[‘Age’].fillna(fillAge) titanic_test[‘Embarked’] = titanic_test[‘Embarked’].fillna(‘S’) titanic_test.loc[titanic_test[‘Sex’] == ‘male’, ‘Sex’] = 0 titanic_test.loc[titanic_test[“Sex”] == “female”, ‘Sex’] = 1 titanic_test.loc[titanic_test[‘Embarked’] == ‘S’, ‘Embarked’] = 0 titanic_test.loc[titanic_test[‘Embarked’] == ‘C’, ‘Embarked’] = 1 titanic_test.loc[titanic_test[‘Embarked’] == ‘Q’, ‘Embarked’] = 2
alg.fit(titanic[predictors], titanic[“Survived”]) predictions = alg.predict(titanic_test[predictors]) submission = pandas.DataFrame({ “Survived”: predictions },index= titanic_test[“PassengerId”])
submission.to_csv(‘Kaggle.csv’) #print(titanic.describe())
注意:现在 python 2 不喜欢“准确性”这一行叹所以我换了 python 3。在 c9 中,当你在一个工作空间中时,你可以按 settings 菜单,在 python 2 和 3 之间切换。记住,你必须下载你正在使用的新版本的所有软件包。
计划的第二部分是提高我们的提交分数。第一个实用模块是关于一个叫做随机森林的东西。一棵树就像我们在小学看到的流程图。
有“是”或“否”的输入来导致决定或最终答案(有点像玩 20 个问题)。这些统计学上的答案叫做终端节点。这允许机器通过将关系简化为一系列线性关系来理解可能不是线性的关系。对于你们这些书呆子来说,在微积分中,不管函数有多复杂,随着你看到的区间变小,它会变成一条直线,然后变成一个点(一般来说)。你可以把一个复杂的问题变成一系列简单的问题。
现在,如果你做多棵树,你会有一个森林。当你用随机的初始起点从数据中创建其他的树时,你可以很好的观察数据告诉你什么。取森林的平均值,就可以开始做预测了。如果树木不是随机的,这种技术就叫做“套袋”。
Sklearn 可以用来实现一个森林。Sklearn 有一个名为的随机森林分类器的模块,在集成模块下完成这项工作。创建的森林将有十棵树。我们将使用泰坦尼克号的测试数据来做这件事。
在我做任何事情之前,我会删除打印语句和导出语句,因为它们是不需要的。
Dataquest 明确写出了许多参数,但是根据文档,它们使用的值与默认值相同。
algForest = RandomForestClassifier(random_state=1, n_estimators=10, min_samples_split=2, min_samples_leaf=1)
以上代码由 Dataquest 提供:
1)n_estimators = >森林中树木的数量;默认值= 10
2)min_samples_split = >分割 3)内部节点所需的最小样本数;默认值= 2
4)min_samples_leaf= >作为叶节点所需的最小样本数;默认值= 1
现在,我们可以使用这种新算法,并以与基于日志的算法相同的方式实现它。
首先我们做一个交叉验证。这将模拟我们之前代码中定义“score”的行,并为 cv(交叉验证)参数传递 kf 变量。我的看起来像这样:
*from sklearn import cross_validation from sklearn.ensemble import RandomForestClassifier titanic=pandas.read_csv(“train.csv”) predictors = [“Pclass”, “Sex”, “Age”, “SibSp”, “Parch”, “Fare”, “Embarked”] algForest = RandomForestClassifier(random_state=1, n_estimators=10, min_samples_split=2, min_samples_leaf=4) kf = cross_validation.KFold(titanic.shape[0], n_folds=3, random_state=1) scores_forest = cross_validation.cross_val_score(algForest, titanic[predictors], titanic[‘Survived’], cv=kf)*
*print(scores.mean())*
现在,是时候生成一些新特性了。我以前在处理一些计算物理问题时用过熊猫。但是我从来没有在数据帧上真正使用过它。
我使用 pandas apply()函数和 lambda 函数。apply()函数从我们实现的 lambda 函数中推断出一个 pandas 系列。
我们将使用简单的加法来计算家庭规模和名字的长度(名字越长,等级越高…理论上)。
Dataquest 写出了这些行,我将它们添加到测试集和训练集中。
titanic[‘FamilySize’] = titanic[‘SibSp’] + titanic[‘Parch’] titanic[‘NameLength’] = titanic[‘Name’].apply(lambda x: len(x))
现在我应该得到每个乘客的头衔,在我看来,这比名字的长度更能反映身份。
对于这一个,我需要构建一个函数来完成这项工作。我将在不同的文件中创建这个类,并将它导入到原始文件中。
我准备用一个正则表达式(大家的最爱)来搜索名字。
‘([A-Za-z]+)\.’
这意味着 reg ex 搜索将从 A 到 Z 查找一个大写字母,后跟一串字母,并以句点结尾。
当我使用 re.search()函数进行搜索时,我需要首先检查我是否找到了匹配项,如果是,我需要得到搜索找到的内容。为了实现前者,我使用了一个简单的 if 语句。为了实现后者,我需要使用 group 函数。Group 采用 reg ex 给定的子组的编号。这意味着组(1)将返回由[A-Za-z]
表示的内容。
然后,我使用 pandas apply()将查找标题的函数映射到数据集的 Name 列:
def get_titles(name): title_search = re.search(‘ ([A-Za-z]+)\.’, name) if title_search: return title_search.group(1) return “”
titles = titanic[“Name”].apply(get_titles)
此外,我可以使用 pandas.value_counts 对每个输出进行计数。
遍历 pandas.value_counts 中的键/值对,使用标题作为键,计数作为值来创建数据集:
title_mapping = {“Mr”: 1, “Miss”: 2, “Mrs”: 3, “Master”: 4, “Dr”: 5, “Rev”: 6, “Major”: 7, “Col”: 7, “Mlle”: 8, “Mme”: 8, “Don”: 9, “Lady”: 10, “Countess”: 10,“Jonkheer”: 10, “Sir”: 9, “Capt”: 7, “Ms”: 2}
for k,v in title_mapping.items(): titles[titles == k] = v
现在我把我的注意力转向看我能不能搞清楚家庭团体。当谈到生存能力时,这是一个很好的选择。什么规模的家庭存活几率更大?
首先我们导入操作符模块。这个模块很难用语言来解释。为了简化,它采用运算(+、-、x 等)并将它们转换成可以提供参数的函数。因此operator.add(x,y)
在功能上与x+y
相同。
导入之后,正如我对标题所做的那样,我们需要定义一个映射字典。但是这一次,字典将是空的(最初)。
然后,当给定一行时,我创建一个函数来获取 id。在该函数中,首先我们获得姓氏,然后我使用 reg ex “{0}{1}”
创建一个 id,并将其与姓氏相关联。这样做之后,我创建一个条件:如果系列 id 不在我们之前创建的系列映射字典中,那么我向字典中添加一个全新的系列,如果 id 确实存在,那么我向该系列 id 添加 1:
def get_family_ids(row): last_name = row[‘Name’].split(“,”)[0] family_id = “{0}{1}”.format(last_name, row[“FamilySize”]) if family_id not in family_id_mapping: if len(family_id_mapping) == 0: current_id = 1 else: current_id = (max(family_id_mapping.items(), key=operator.itemgetter(1))[1] + 1) family_id_mapping[family_id] = current_id return family_id_mapping[family_id]
在函数外部,我使用 pandas.apply 方法定义了 family _ ids。参数是我们创建的函数,用于获取系列 id 和轴=1。
DataQuest 让我把所有少于三人的家庭压缩成一个代码。我说…不。我们看看会发生什么。
最后,我们将该列添加到我们的数据集,然后打印出来。
family_ids = titanic.apply(get_family_ids, axis=1) titanic[“FamilyId”] = family_ids
这些代码的大部分是由 DataQuest 编写的,我花了一些时间来分析它并弄清楚发生了什么。看了很多文件!
不管怎样,我要去寻找预测的最佳特征了。我的森林里有哪些问题通常能让我得到正确答案?
接下来我将使用一个叫做的东西,单变量特征选择。那是一口。我需要从 scikitlearn 导入更多的模块; SelectKBest ,f_classif。
然后,我需要将我创建的新参数(头衔和家庭规模)添加到预测列表中。
我把一个叫做选择器的变量等同于以 f_classif 和 k=5 为参数的函数 selectBest。I 将选择器与巨大的预测器和幸存的参数列相匹配。
我使用 numpy.log10 和 p 值计算每个预测值的得分。根据这篇文章:
p 值评估样本数据在多大程度上支持“魔鬼代言人”的论点,即零假设为真。它衡量你的数据与零假设的符合程度。如果零假设为真,在样本数据中观察到这种效应的可能性有多大?
高 P 值:您的数据可能为真空值。
低 P 值:您的数据不太可能为真空值。
然后我用 matplotlib.pylot 将结果图形化,有了这个,我就能算出什么是最好的预测器(“Pclass”、“Sex”、“Fare”、“Title”)。然后,我使用我们为随机森林创建的算法来重新定义分数,并打印出平均值。为了便于阅读,我将这个新的分数称为 scores_graph。
predictors = [“Pclass”, “Sex”, “Age”, “SibSp”, “Parch”, “Fare”, “Embarked”, “FamilySize”, “Title”, “FamilyId”, “NameLength”] selector = SelectKBest(f_classif, k=5) selector.fit(titanic[predictors], titanic[“Survived”]) algForest.fit(titanic[predictors], titanic[‘Survived’]) scores_graph = -np.log10(selector.pvalues_) plt.bar(range(len(predictors)), scores_graph)
plt.xticks(range(len(predictors)), predictors, rotation=’vertical’) plt.show()
在这一点上,我遇到了 c9 的障碍。它不会显示图表。她哭泣所以我问了我的老朋友谷歌先生,我得到了这里。我需要与进口和 matplotlib 的工作方式。
import matplotlib matplotlib.use(‘Agg’)
- 注意,这些行应该在导入 matplotlib.pyplot 之前插入。
所以在你画出图后,你加上这两条线:
fig = plt.figure()
fig.savefig(filename)
它应该在程序所在的目录下创建一个你选择的格式的文件。
在这一点上,下一个模块是关于梯度增强,这是一种增强我们从随机森林中获得的预测的方法。但是,我不知道它是如何工作的,所以我查了一下,找到了这个视频。哈佛大学的统计学教授特雷弗·哈斯蒂谈到了这个话题(我不得不查一些术语)。
Boosting Trees 获取树,并像以前一样找到平均值,但这一次,它将根据是否得到好的结果来加权每棵树。它会增加森林中表现不佳的部分的权重,并尝试修复错误。这可能会导致过度拟合,当我们准备好预测时,这会导致各种各样的问题,但对于我们的目的来说,这是可以的。
在梯度增强之后是关于集合的部分。这是将不同的分类器(如线性、对数、随机森林或装袋)放在一起的行为。就像科学怪人的统计数据。为了充分利用这一点,分类器需要具有不同的类型,并且具有相似的等级(具有相似的)误差值。DataQuest 让我使用带有梯度提升树的对数分类器。
从 scikitLearn 中,我导入了梯度推进分类器模块,并给它一个随机状态 1,估计数量为 25,每棵树只有 3 个分支(两个是二进制选择:是或否问题,它们被称为树桩)。
然后,我创建了一个名为“ens”的数组,它将两个算法作为嵌套数组的元素。一个数组中有梯度增强分类器和预测器,另一个数组中有对数回归和预测器(不同的)。设置好之后,我在训练数据集中使用它。
然后,因为梯度提升比对数回归更准确,所以我们将权重添加到提升中(三倍于对数):
predictions = [] predictors = [“Pclass”, “Sex”, “Age”, “Fare”, “Embarked”, “FamilySize”, “Title”, “FamilyId”] ens = [[alg_gradient , predictors],[alg ,[“Pclass”, “Sex”, “Fare”, “FamilySize”, “Title”, “Age”, “Embarked”]]] for train, test in kf: train_target = titanic[‘Survived’].iloc[train] full_test_predictions = [] for alg, predictors in ens: alg.fit(titanic[predictors].iloc[train,:], train_target) test_predictions = alg.predict_proba(titanic[predictors].iloc[test,:].astype(float))[:,1] full_test_predictions.append(test_predictions) test_predictions = (full_test_predictions[0] + full_test_predictions[1]) / 2 test_predictions[test_predictions <= .5] = 0 test_predictions[test_predictions > .5] = 1 predictions.append(test_predictions) predictions = np.concatenate(predictions,axis=0)
accuracy = sum(predictions[predictions == titanic[“Survived”]]) / len(predictions)
因为这不是公主新娘,所以某人不可能“大部分”死亡,所以我们可以继续进行测试 _ 预测枯萎 0 或 1(幸存与否)。
在此之后,我将我们对训练数据所做的预测应用到完整的数据集。为此,我首先将整个数据集与训练数据进行拟合。然后使用 alg.predict_proba() 函数填写预测值:
alg_ens = [ [GradientBoostingClassifier(random_state=1, n_estimators=25, max_depth=3), predictors], [LogisticRegression(random_state=1), [“Pclass”, “Sex”, “Fare”, “FamilySize”, “Title”, “Age”, “Embarked”]] ] full_predictions =[] for alg, predictors in alg_ens: alg.fit(titanic[predictors], titanic[“Survived”]) predictions = alg.predict_proba(titanic_test[predictors].astype(float))[:,1] full_predictions.append(predictions) predictions = (full_predictions[0] * 3 + full_predictions[1]) / 4 predictions[predictions <= .5] = 0 predictions[predictions > .5] = 1
我再次使用本教程第一部分中的提交术语:
submission = pandas.DataFrame({ “PassengerId”: titanic[“PassengerId”], “Survived”: predictions }).astype(int) submission.to_csv(‘Kaggle.csv’)
在上一篇文章中,我不得不进入我的 google drive,手动删除索引栏(像个傻瓜一样)。然后我做了一些研究,当我在做另一个项目时,我意识到当你使用熊猫导出到 csv 时,你可以关闭索引。你只需要写下:
submission.to_csv(‘Kaggle.csv’,index=False)
嘣!完成了。
对于 Python 版本,唯一的区别是“准确性”行必须用 2 注释掉。它与熊猫有关。
我这里的代码是。我要去睡觉了。
Kaggle 盐识别挑战或如何分割图像
几周前,我在 Kaggle 上完成了 TGS 盐鉴定挑战赛,这是一个流行的数据科学竞赛平台。任务是在地震图像上准确识别地下目标是否为盐层。
我们的团队: Insaf Ashrapov , Mikhail Karchevskiy , Leonid Kozinkin
我们获得了前 1%第 28 名,并希望分享我们的最终解决方案,该方案基于一个采用 hflip TTA(测试时间增加)的单一 5 重模型。
看看我的机器和深度学习博客https://diyago.github.io/
输入
Input image and mask as ground truth binary image
最初,我们有尺寸为 101*101 的灰度 1 通道输入图像。为了使其可用于预训练的编码器,我们将它们调整为 128x128,第一个通道通过填充作为源图像,第二个通道使用相对深度:
相对深度=(当前深度-最小深度)/(最大深度-最小深度)
在第三个通道上,我们应用了coord conv【1】。我们尝试了更高的分辨率,但学习和收敛速度慢得多,没有任何改进,但其他一些参与者报告了显著的改进。
增强功能
我们使用了来自伟大的albumination库中的增强功能:亮度、模糊、hflip、缩放、旋转和对比度。这里重要的是不要使用对所提供的数据集来说不自然或物理上不可能的图像增强,例如,垂直翻转只会产生负面影响。此外,这些增强不是太重,否则,我们会遭受较低的分割质量,甚至训练速度。
基本型号
我们最终的模型是基于 Unet [2]的,和其他人一样做了一些微妙的修改。
SE-ResNeXt-50 [3](其性能优于所有 resnet 和更复杂的 SE-ResNeXt-101)作为获取初始特征图的编码器。然而,我们修改了第一层。首先,我们在没有最大池的情况下使用它,然后通过返回最大池,在 stride = 1 的情况下使用它。上述两种修改都允许使用 128*128 的图像,否则需要更高的图像分辨率。在我看来,缩放图像会给图像和蒙版带来一些不必要的伪影。
ScSE(空间通道压缩&激发)【4】编码器和解码器,超列【5】。
令我们惊讶的是,我们已经清除了所有辍学者。这是实质性的,因为加速了训练,提高了最终结果。我们甚至试图只用 16 个过滤器,但最终用了 32 个。似乎是增加和 5 倍平均足以避免过度拟合。
训练
出于学习目的,我们使用了这样的参数和方法:
批量大小 20(适合 GPU 存储器的最大大小),Adam [6],循环学习速率[7],参数 mode =‘triangular 2’,baslr = 1e-4,maxlr=3e-4,step_size=1500,重快照集合[8](以指数递减的权重对最后 10 个最佳模型进行平均)。使用快照集合会产生无用的混合模型,或者对于这样的 lb 分数较低的模型不会给出任何提升。
对于前 80 个时期,我们使用 BCE(二进制交叉熵)损失进行训练,然后其他多达 420 个时期使用 0.1 BCE + 0.9 Lovasz 作为损失函数[9]。然而,几乎每次训练都被ealystop停止。只使用 Lovasz 效果也很好,但是 BCE 加快了初始训练。
我们用 1080ti 和 1070ti 配合 pytorch 进行训练。不幸的是,Keras 不太方便尝试新事物,而且 Pytorch 预训练的编码器库更丰富。
后处理
通过使用cv2 . connectedcomponentswithstats(形态学没有那么好)
更高(0.40+)的阈值移除小的遮罩和小的独立遮罩(黑色和白色)在私有 LB 上给了我们更好的分数,但是对于公共 LB,更小的阈值显示了相同的结果。拼图游戏[10]没有对本地验证进行改进,所以我们没有在最终提交中使用它。其他参与者仅在公共排行榜上报告了较高的改进:
Puzzle created by Arthur Kuzin (red-train masks, green-predicted test masks)
在上面的图片中,你可以看到一些垂直的面具,这些有点奇怪。此外,比赛结束后,组织者透露,我们寻找所谓的盐丘,这是封面石油。这就是为什么没有任何全尺寸的面具。
还有什么
我们没有尝试两件大有希望的事情,这两件事可以帮助我们:
1)伪标签。然后,您在训练中使用具有高置信度的预测测试图像[10]。第一名把这个方法作为主要方法,这提高了他的分数。
2)深度半监督学习【12】。这种方法旨在使用标记和未标记的图像。这里,我们应该为不同的视图训练多个深度神经网络,并利用对立的例子来鼓励视图差异,以防止网络相互崩溃。结果,共同训练的网络提供了关于数据的不同的和互补的信息,这对于共同训练框架实现良好的结果是必要的。
Mean teachers are better role models: Weight-averaged consistency targets improve semi-supervised deep learning results. Image provided by Heng CherKeng
出版
为了向更广泛的受众传播我们的解决方案,我们最近在 arvix 上发表了文章,并在 Github 上发布了我们的解决方案。
参考文献
- Liu,r .、Lehman,j .、Molino,p .、suther,F.P .、Frank,e .、a .、Yosinski,j .:卷积神经网络和 coordconv 解的一个有趣的失败。arXiv 预印本 arXiv:1807.03247 (2018)
- Ronneberger,o .,Fischer,p .,Brox,T.: U-Net:生物医学图像分割的卷积网络。医学图像计算和计算机辅助介入。第 234–241 页(2015 年)。
- 胡,j,等:压缩和激发网络。arXiv:1709.01507 (2017 年)
- Abhijit G 。纳西尔 N。、 Wachinger C .全卷积网络中的并发空间和信道压缩&激励 arXiv:1803.02579 (2018)
- B.Hariharan、P. Arbeĺ aez、R. Girshick 和 J. Malik。用于对象分割和细粒度定位的超圆柱。InCVPR,2015 年。
- D.P. Kingma 和 J. Ba,“Adam:随机优化方法”,arXiv 预印本 arXiv:1412.6980,2014 年。
- 莱斯利·史密斯。训练神经网络的循环学习率。《计算机视觉应用》(WACV),2017 年 IEEE 冬季会议论文集,第 464–472 页。IEEE,2017 年
- 黄高、黎一萱、杰夫·普莱斯、刘庄、约翰·E·霍普克罗夫特和基利安·Q·温伯格。2017.快照合集:火车 1,免费获得 M。arXiv 预印本 arXiv:1704.00109 (2017)。
- Berman,m .,Rannen Triki,A .,Blaschko,m . b .:lovasz-soft max 损失:神经网络中交集-并集度量优化的易处理替代。在:CVPR (2018 年)
- D.金,赵迪,刘迪,还有郭怡广。通过完成受损拼图学习图像表征。2018 年在 WACV。
- 李东贤。伪标签:简单高效的
深度神经网络半监督学习方法。2013 年,在 ICML 举办的关于表征学习挑战的研讨会。 - A.瓦尔波拉·塔尔瓦伊宁。均值老师是更好的榜样:加权平均一致性目标提高半监督深度学习结果 arXiv:1703.01780 (2017)。
Kaggle Tensorflow 语音识别挑战
我的方法概述
从 2017 年 11 月到 2018 年 1 月,谷歌大脑团队在 Kaggle 上举办了一场语音识别挑战赛。这项挑战的目标是编写一个程序,能够正确识别一秒钟长的音频文件中 10 个单词中的一个。刚刚下定决心开始认真学习数据科学,目标是在我的职业生涯中开辟一个新的角落,我决定将此作为我的第一个严峻的 kaggle 挑战。
在这篇文章中,我将谈论 ResNets,RNNs,1D 和 2D 卷积,连接主义者的时间分类等等。我们走吧!
探索性数据分析
Google Brain 提供的训练数据由 ca。6 万个 1 秒长的。32 个目录中的 wav 文件,这些目录由文件中的单词命名。其中只有 10 个是你需要识别的类别,其他的应该归入“未知”或“沉默”类别。您可以做几件事情来掌握您正在处理的数据。这个数据集并没有完全清理干净。例如,有些文件的长度不正好是 1 秒。也没有这样的“沉默”文件。你得到的是一些带有“背景”噪音的较长的录音,你可以自己把它们分成 1 秒钟的片段。你也可以在 word 文件中混合背景噪音,为你的声音在训练中提供不同的“环境”。
你需要做的一件重要的事情是清理数据,这在的一个讨论中提到:有相当多的文件音量极低。这些中的一些被破坏并且仅包含噪声,而一些基本上是没有任何口语单词的背景噪声。要删除或正确标记这些文件,有助于根据输出音量的动态范围对所有文件进行分类,然后检查是否存在最低声音级别阈值,低于该阈值,所有文件基本上都是无声的。事实证明,输出音量本身不足以将损坏/静音与良好的文件区分开来,所以我最终通过收听可疑文件和查看大量频谱图(见下文)来进行一些手动清理。
预处理
当您对音频进行分类时,您可以使用原始 wav 数据本身,也可以将音频转换为频谱图。声谱图是声音的直观表示,具有时间轴和频率轴,以及表示声音在该时刻和该频率的振幅或能量的像素强度。在制作频谱图时,有许多参数可供选择,这些参数会影响从频域或时域提取的信息量。关于深度学习的适用性,我还没有对所有这些参数进行详尽的分析,因为这需要花费很长时间。相反,我为不同的单词绘制了一系列不同维度和强度范围等的光谱图,并选择了看起来最容易在视觉上归类为不同单词的单词。
有人说“是”的音频源的一些不同的可视化。我选择了右下方的声谱图作为我的大多数网络的输入。与原始 wav 数据相比,使用 spectrograms 的优势在于,您可以将其视为一个图像分类问题,这是我们大多数人已经非常熟悉的。我尝试了两种方法。
为了加速网络的最终训练,我决定单独进行大部分预处理,并将单独的训练集和验证集保存为。npy 文件。数据量很小,可以在我的家用电脑上完成。Google Brain 建议您根据他们提供的“validation.txt”和“test.txt”文件中的文件名,将数据拆分为“train”、“validation”和“test”。因为他们还在 Kaggle 上提供了一个用于排行榜评分的测试集,所以我决定将“验证”和“测试”文本文件合并成一个验证集。预处理包括创建光谱图,围绕零进行归一化,为 10 个主要类别加上“沉默”和“未知”创建整数为 0-11 的“标签”或“Y”数组。对于 CTC 模型,我没有使用“未知”标签。所有 32 个班级都被平等对待。
因为我将这个挑战作为深度学习训练练习,所以我实现了一堆不同的网络设计。得到这样的实际训练真是太棒了。我从这个项目中学到的东西比我之前开始的十个 MOOCs 都多。(好吧,我稍微夸张一点)。我将讨论几个我的设计尝试以及实现时需要注意的事项。
原始 wav 文件,1D 卷积
使用 spectrograms,您可以使用特定的算法从 wav 文件中提取特征,但您必须微调一系列参数。一个设计良好的神经网络应该能够自己从原始 wav 文件中学习特征,并可能获得比从光谱图中获得的信息更多的特征。这第一个模型就是一个尝试。最初的设计很大程度上受到了用户 ttagu99 的一个 Kaggle 讨论帖子的启发。这是 6 层 1D 卷积网络的早期尝试的结果。
验证集上的网络分数。上图:sklearn 分类报告。下图:sklearn 混淆矩阵。
还不是很有效。它犯的一些错误很容易理解。它经常混淆“不”和“去”,“关”和“上”。这些单词有非常相似的元音,所以可能会混淆。有些错误不太明显:它真的认为‘右’是‘左’16 次吗?也许是结尾的 t?此图还说明了如果将所有的“额外”类合并到一个“未知”类中(大约 500 个样本对大约 8300 个样本),这些类是多么不平衡。我通过在 Keras fit 函数中使用“class_weight”参数来处理这个问题。此参数接受将类映射到权重浮点的字典,并通过更严厉地惩罚未充分代表的类的错误分类来“重新平衡”训练集。更好地平衡训练集的另一种方法是通过从每个类中创建等量样本的批次。我假设人们会在随后的时代使用剩余的“未知”样本,但我自己没有尝试过。我不知道哪种平衡技巧最好。在对 Conv1D 模型进行了一些调整后,我在 Kaggle 排行榜上的得分达到了 83%左右。还不是很好,但现在我不想在这个模型上做更多的调整,而是想尝试一些其他的网络架构。
雷斯内特
残差神经网络,或 ResNet ,基本上是一个有快捷方式的深度卷积神经网络。每隔几层,就有一个身份连接回到上一层。这些快捷连接被认为有助于网络更好地推广。这绝对有效:我不再需要任何辍学层。实际情况是,网络深度默认使用尽可能多的快捷连接。这使网络保持较小,因此有助于泛化。快捷方式之间的剩余层仅在需要时更新。
具有两个快捷连接的 ResNet 模型的小子集
具有 BatchNormalization 和 ReLu activation 的两个 Conv2D 层的块由一个连接层分隔,该连接层将先前的连接层添加到块的输出中。然后,每三个这样的模块进一步由跨距为 2 的 Conv2D 层分隔,以便学习更大规模的特征。在 Keras 中,我是这样实现的:
这个模型表现得非常好。它在验证集上达到了近 98%的准确率,在 kaggle 排行榜上达到了 85%。验证集和测试集之间的这种差异令人担忧,我将在下面更深入地讨论这一点,但它与测试集有关,测试集包含许多在训练集中没有给我们的单词。所谓的未知的未知。我研究的下一个改进模型的架构是递归神经网络。
返回 RNN
声谱图的一个轴代表声音文件的频率,另一个轴代表时间维度。在卷积神经网络中,不能将这两个维度视为相等,这是有道理的。递归神经网络或 RNNs 用于建模序列,例如预测接下来应该发生什么。在我们的例子中,它们可以用来记录先发生了什么,以及之后发生了什么,这是 ResNet 所不能做到的。我的意思是,一个正常的卷积网络可能会认为“yes”和“yes”是同一个词,因为它不知道声音的顺序。我试图做的是添加一个 RNN 层到我的表现非常好的 ResNet 的末尾,并对其进行处理。正如您在上面的代码中看到的,有一个 MaxPooling 层和两个跨距为 2 的 Conv2D 层,它们将网络末端的输入大小从(61,75,1)减少到(8,10,128)。这里的第一维(8)表示时间维,但是 rnn 采用 3D 输入,而(包括批处理)我们的 ResNet 采用 4D。因此,从最终的剩余+快捷方式层到 RNN 层,我们必须使用 Keras 的整形层。你可以这样做:
我们将第二维和第三维组合成一个大向量,产生 8 个时间点,1280 个数字描述卷积的频率分布。需要时间分布式包装层来使 RNN 层(在这种情况下是 LSTM)使用 8 个时间行作为顺序输入。经过几次调整和迭代,一个组合的 ResNet RNN 模型在 Kaggle 排行榜上给出了 87%的准确率。我尽了最大努力,在 1300 名左右中排在第 200 名左右。我认为这对于我的第一个 Kaggle 项目来说相当不错。但我仍然想尝试一些东西,学习更多的深度学习技巧。
连接主义时间分类(语音到文本)
在 Kaggle 挑战赛提交截止日期前后,吴恩达 Coursera 深度学习 python 课程关于序列模型的最终模块向公众开放。我在上面的组合模型中应用了一些 RNN 层,但是我并不知道它是如何工作的,所以我参加了这个课程来学习所有关于 RNNs 的知识。这篇文章不是对这门课的回顾,但我想说的是,我会把它推荐给每个对 RNNs 的数学原理感兴趣的人。除了基本的和不太基本的 RNNs 之外,我特别感兴趣的是一种语音转文本的方法,Andrew 提到了这种方法,但没有真正深入研究,这种方法被称为 Connectionist 时态分类,或 CTC。我对此感到非常兴奋,因为我脑海中有一个关于语音到文本的项目的想法已经有一段时间了,但它仍然非常抽象,我根本不知道从哪里开始。但是现在有人给了我一个非常具体的开始。
语音转文本模型以声谱图(或原始 wav 数据)为输入,输出字母或单词。这样一个网络需要做的是识别每个 RNN 输入中所谓的音素,将它们翻译成字母,并将字母组合成正确的单词。
因此,对于英语口语单词,您希望网络输出每个时间点长度为 28(字母表加上“空格”和“空白”)的单热点向量,然后以某种方式确定预测的“错误”程度,以便我们可以进行反向预测。这就是反恐委员会的工作。例如,它以输出“_ _ _ Y _ EEEE _ SSS _ _”为例,通过折叠多个字母的连接并忽略不同字母之间的“空白”来将其简化为“是”。如果输出在两个相同的字母(“E _ E”)之间给出一个“空白”,则认为它是两个单独的字母。“空白”不应该与“空格”混淆,但我不必担心这一点,因为我所有的输入都是单个单词。
使用 Keras,您可以使用后端函数 ctc_batch_cost() 来实现,但它需要四个参数(y_true、y_pred、input_length 和 label_length),而不是只有 y_true 和 y_pred,这意味着您不能将它用作常规的损失函数,并将其插入到您的编译语句中。我在 github 上找到了一个 repo,它是百度的 DeepSpeech 模型的一个工作 Keras 实现,使用了 CTC,并取走了我自己需要的部分。
这里 y_pred 是长度为 28 (output_dim)的独热码向量的 RNN 的时间分布输出。与其他三个参数一起,它被提供给包装在 Lambda 层中的 ctc_batch_cost 函数,然后我创建了一个用于训练的模型(“模型”)和一个用于测试/验证的模型(“测试 _ 模型”),其中不包括 Lambda 层。当你运行模式时。Compile() 它需要一个损失函数,所以你给它一个以 y_true 和 y_pred 为参数,简单返回 y_pred 的哑函数。然后,训练你给的模型*。fit()* 函数一列四个输入:
- 训练集,
- 具有标签的字母索引的数组 y_true ,用“空白”索引填充到所有样本的共同长度([25,5,19,27,27,27,27,27]表示“是”),
- 一个一维数组 input_length ,包含每个样本的“字母表”长度(len(Y_train) 28 作为一个向量),以及
- 一维数组 label_length 的长度是 y_true vectors(对我来说是 8)len(Y _ train)的倍。
输出的参数。fit() 函数采用长度为 len(Y_train)的零数组,我们的虚拟损失函数对此不做任何处理。我认为我能做到这一点是非常令人兴奋的。使用该模型的原始输出(上游层是 Conv1D 和两个双向 LSTMs)在 Kaggle LB 上达到 83%的分数,但是这是在没有尝试对预测的单词运行某种拼写检查或语言模型的情况下。我看到了很多“叶”的和“ riget ”的等等。我将更多地使用它,看看我还能从中得到什么。
离群点检测
正如我在上面提到的,测试集包含了大量的单词和声音,这些都不在训练集中。这意味着为了得到一个真正好的分数,你的模型必须学习未知的未知的特征。从我在截止日期后的 Kaggle 论坛上读到的内容来看,大多数顶尖的得分者使用了大量的特征工程来创造更多的“未知”样本。例如,您可以剪切和粘贴单词片段来创建新单词,或者您可以使用音高移动或反转样本。我尝试了一下,但不太成功。另一件事,我看到许多得分最高的人做的是在测试集上的无监督训练,以了解什么样的特征存在于那里,而不是在训练集中。这似乎给了分数很大的提高,但对我来说感觉有点像作弊。我自己花了相当多的时间尝试不同形式的异常值或异常检测。这意味着试图让网络拒绝它以前没有听到过的声音,并将它们放在“未知”类别中。我尝试在 10 个主要类别上训练自动编码器和变型自动编码器,但这些模型在解码看不见的光谱图方面总是保持同样好,就像它们在解码我训练的输入时一样。我尝试过开集识别,但是在尝试了几个星期去理解他们到底是怎么做的之后,我放弃了。最后,如果这些技术能改善我的模型就好了,但如果排行榜上没有一个超级分数,学习这些确实给了我很多新的知识和经验,所以我很高兴我做到了。
结论
我离开大学已经 15 年了。在这么短的时间内又学到这么多东西,真是太有趣了。除了更好地了解如何进行语音识别之外,我还学到了更多关于 Kaggle 挑战以及如何获得高分的知识。当我开始的时候,我认为如果我发现了一些其他参与者没有的技巧,那么我会比他们得分更高,但现在我更好地认识到了高分真正需要什么。那些家伙肯定比我知道更多的把戏!
ResNet 和 CTC 模型在https://github.com/chrisdinant/speech的实施
Kaggle 泰坦尼克号:机器学习模型(前 7%)
这场 K aggle 竞赛的目的是根据给定的特征预测给定乘客的生死。这个机器学习模型是使用 scikit-learn 和 fastai 库构建的(感谢杰里米·霍华德和瑞秋·托马斯)。对该模型使用集成技术(RandomForestClassifer 算法)。我尝试了其他算法,如逻辑回归,梯度推进分类器与不同的超参数。但是我用这个 RandomFortestClassifer 得到了更好的结果(前 7%)。
完整代码的 Github 链接是这里是。
下面是测试数据集中提供的功能。
- 乘客身份证:发给船上每个乘客的身份证
- 乘客舱:乘客舱。它有三个可能的值:1、2、3(一等、二等和三等)
- 乘客的姓名
- 性
- 年龄
- SibSp:与乘客同行的兄弟姐妹和配偶的数量
- Parch:与乘客同行的父母和子女人数
- 机票号码
- 票价
- 机舱号
- 登船。这描述了泰坦尼克号上三个可能的区域。三个可能的值 S,C,Q
探索性数据分析:以下是我在数据分析过程中的发现,以及我处理这些发现的方法。
从下表中我们可以看到,在测试数据集中的 891 个观察值中,只有 714 个记录填充了年龄,即大约 177 个值缺失。我们需要用一些值来估算,我们稍后会看到。
Numerical feature statistics — we can see the number of missing/non-missing
下面的图表显示,男性乘客死亡人数多于女性(性别歧视:-))
Visualization of Survival based on the Gender
我们还可以看到 20 到 40 岁的男性比年长的男性存活的更多,如绿色直方图所示。另一方面,女性比男性活得更久,在所有年龄组都相对较好。
Correlation of Age with the Survival
当我们绘制幸存/死亡乘客的票价时,我们可以看到票价较便宜的乘客更有可能死亡。也就是说,拥有昂贵车票(可能是更重要的社会地位)的乘客似乎被优先营救。x 轴:票价,Y 轴:乘客人数。
Ticket Fare vs Survival rate
下面是一张图,显示了年龄和费用与存活率的关系。x 轴=年龄,Y 轴=车票 _ 票价,绿点=存活,红点=死亡
x=0 和 x=10 之间的小绿点:幸存的孩子
x=10 & x=45 之间的小红点:死亡的成年人(来自下层阶级)
x=20 & x=45 之间的大绿点:票价较高的成年人幸存下来。
现在是特征工程:
我将训练和测试数据结合起来,对两者都进行了转换。完成后,我分离测试和训练数据,用测试数据训练模型,用验证集(训练数据的小子集)验证模型,评估和调整参数。最后在完整的训练数据上训练模型。然后在测试数据上做预测,提交给 Kaggle。
**处理家庭:**根据家庭的大小创建了一些新的特性(家庭大小,单个,小家庭,大家庭)。这是基于一个假设,即大家庭通常被组织在一起并得到支持,因此他们比那些独自旅行的人更有可能获救。
Process abowed:用训练集中最频繁的一个“S”填充缺失的装载,并使用 get_dummies 方法对电子标记列进行一键编码。
Process Cabin: 用 U(表示未知)替换缺失的 cabinet,取 cabinet 的第一个字母,用 get_dummies 方法进行虚拟编码。
从名称中提取乘客标题:通过解析名称并将标题映射到定义的类别,创建了一个新功能“标题”。
流程年龄:正如我们之前看到的,年龄变量有 177 个缺失值,这在 891 个值中是一个巨大的数字。仅仅用平均/中间年龄来代替可能不是最佳解决方案,因为年龄可能因乘客的群体和类别而不同。头衔也有助于计算年龄。首先,我取了按性别、乘客、阶级和头衔分组的平均年龄。
然后,对于所有缺少年龄的记录,基于它们的性别、标题和类别,我们指定年龄。如果完全没有“头衔”,只根据性别和阶级来指定年龄。
流程名称:删除 Name 列,在 Title 列做一个虚拟变量编码。标题列值编码后,数据将如下所示。
**构建和训练模型:**正如我前面提到的,我将训练集分成训练和验证集(60 个用于验证),并使用了 RandomForestClassifier。最初,我将树的数量(n_estimators)设置为 20,最终设置为 180,并将分割节点所需的最小样本数设置为 3(min_samples_leaf)。并且还使用 max_features 作为 0.5(在寻找最佳分割时随机考虑 50%的特征)。利用这些参数,该模型在测试数据上获得了 0.933 的分数。
模型评估:在验证集上达到 86.67 左右,下面是混淆矩阵和分类报告。
特性重要性检查:基于树的评估器可以用来计算特性的重要性,之后我们可以丢弃不相关的特性。以下是最重要的十大特性列表。
从上表中我们可以看出,头衔、性别、票价、乘客身份、年龄、阶级和家庭规模是更重要的特征。不确定乘客 Id 如何影响预测。下面是相同的图形表示。
最后,为了最后一次对整个训练数据再次训练模型,我只包括了重要性超过 0.01 的特征。训练后我可以看到分数略有提高,这次是 0.938 。
然后我在测试数据上运行模型,提取预测并提交给 Kaggle。它获得了 0.8133 的分数,位于前 7%。
当然,这种模式有很大的改进和修正余地。非常感谢您的阅读,如果您有任何意见、想法和建议,请告诉我。
数据科学竞赛平台——ka ggle vs 天池
KDD 杯是最负盛名的年度数据挖掘比赛,与 ACM SIGKDD 知识发现和数据挖掘会议同时举行。每年都会征集提案,并根据不同的因素选择组织者。阿里云团队被选中组织 2017 年 KDD 杯。
过去,这些竞赛在 Kaggle 上举办,这已经成为数据科学竞赛的同义词。但出于显而易见的原因,阿里巴巴团队选择天池平台举办 2017 年的比赛。但感觉平台还没准备好,缺了很多东西。
- 参与者在下载数据集时遇到困难。必须进行多次尝试才能下载。
- 在 Kaggle 中如此受欢迎的内核在 Tiachi 上却不见了。
- 布局和导航虽然受到 Kaggle 的启发,但看起来毫无创意且半生不熟。例如比较论坛的布局:
Kaggle
在天池,我们甚至看不到讨论的完整标题,而且很多讨论都是用中文进行的,而 KDD 杯比赛规则明确提到讨论应该用英文。
- 最后,但并非最不重要的是,该网站非常非常慢。速度可能很快,但一般来说很慢。举例来说,打开 论坛 页面有时需要长达 19 分钟。
Wait, Wait, Wait…
例如,在论坛中提问可能需要等待 20 秒以上。
大多数时候,响应是如此之慢,以至于打开一个页面需要很长时间。当您使用开发工具 检查 时,它的状态为待定(如下图*)*。
pending, pending, pending….
看看它如何发展会很有趣。但是第一印象并不好。
卡尔曼滤波器:一种理解融合传感器洞察力的算法
你正开车穿过隧道。GPS 信号消失了。然而,你可能想得到通知,你应该在隧道的出口。我们应该如何在隧道内驾驶一辆汽车,只给它最后的位置,它应该知道它现在在哪里?
关于传感器的简短说明
全球定位系统接收器通过分析它们从卫星接收的信号来计算它们的位置。这些信号不会穿过固体。车辆中的 GPS 可能有一个外部天线,或者它可以从空气中拾取足够的反射信号来工作。如果隧道中的信号太弱,GPS 可能仍会工作,这取决于其质量和功能。
The table explains the pros and cons of each some of the sensors.
一种合并车辆传感器的方法可以计算位置。
来自传感器的测量值
假设我们在隧道入口,并且我们以 50 公里/小时的速度行驶,那么导航确实可以精确地计算出 1 分钟(t =时间)后我们将在哪里(x =位置)。
两个传感器都有随机误差,传输路径有干扰,CAN 总线或者模数转换器的分辨率都会造成简单语句“速度”的很多不准确。
例如,速度信号看起来像这样:
The Velocity is considered to be with a variance on both the axes. (image source)
平均而言,测得的速度中加入了一些“噪音”,这使它们与地面的真实情况有所不同。如果计算确定的速度的直方图,可以看到确定的值近似服从正态分布。
This is the histogram representation of the velocity measurements. (image-source)
所以有一个,而且真的只有一个,最大值(单峰)和价差(方差)。如果是这种情况,我们仍然可以用一个技巧很好地计算。
一维卡尔曼滤波的思想
我想先解释一下只有一维的卡尔曼滤波器(根据 Rudolf Emil Kalman )的想法。以下解释借用了巴斯蒂安·特龙教授的Udacity CS373 课程。
计算噪声有助于
为了在测量噪声的情况下最佳地执行计算,必须知道*“强度”*参数。这个“*有多强”*是用正态分布的方差来表示的。对于正在使用的传感器,这被确定一次,然后仅使用该“不确定性”进行计算。
在下文中,不再用绝对值来计算,而是用正态分布的平均值(μ)和方差σ来计算。正态分布的平均值是我们想要计算的值。方差表示置信水平。正态分布越窄(低方差),传感器对测量结果越有信心。
精确测量 100%的传感器的方差为σ = 0(不存在)。
我们假设 GPS 信号刚刚丢失,导航系统完全不清楚你在哪里。方差高,对应的曲线确实平坦。有一个不确定性。
方差= 20 且均值= 0 的正态分布
The Uncertainty is High, as the variance is in a large magnitude.(image-source)
现在来自传感器的速度测量值也是“不准确的”,具有适当的方差。这两种不确定性现在必须联系在一起。借助于贝叶斯规则,执行两个高斯函数的相加。Thrun 教授在 Udacity CS373 课程中非常清楚地解释了这一点。
这两条信息(一条用于当前位置,一条用于传感器的测量不确定性)实际上给出了更好的结果!。正态分布越窄,结果越有把握。运动恶化了估计。
当然,车辆也会移动,这不利地影响了位置确定的精度。例如,传感器可以确定车轮的旋转,并假设车轮的半径,还可以得出行驶距离的结论,但这总是有些不准确。这种运动的不精确性也可以用正态分布来描述。这一次,使用当前估计值进行计算的方式略有不同,因为“运动”也可以称为“预测”。你可以在计算后估计,你下一次(测量)时间会在哪里。
在我们的例子中,μ就是 v * dt,它是我们在计算时间内走过的距离。
一个简单的实现是:
def predict(mean1, var1, mean2, var2):
new_mean = mean1 +mean2
new_var = var1 + var2
return [new_mean, new_var]
测量和更新:卡尔曼滤波器
卡尔曼滤波器只是反复计算这两个函数。
The filter loop that goes on and on.
过滤器循环覆盖结果的平均值和方差。只要读数不偏离预测值太多,过滤器将始终确信其位置。
一项新的测量提高了估计值
由于测量值(在更新中)与预测值(通过预测)相对较好地吻合,所以滤波器逐步改进以确保它是正确的(正态分布变得更窄和更高),即使值是有噪声的。
def update(mean1, var1, mean2, var2):
sum = var1+var2
pr_s = mean1*var2 + mean2*var1
#print(pr_s)
new_mean =1/(sum) * pr_s
product = var1*var2
new_var = product/sum
return [new_mean, new_var]
没有矩阵,你只能在一个维度上计数,这不足以…
多维卡尔曼滤波器
The Picture Illustrates the Kalman Filter ‘s Predition step in various time-stages. (Image Source)
我想用一辆带导航装置的汽车进入隧道的例子来再次解释这个过程。最后已知的位置是在失去 GPS 信号之前。之后,只有车辆的速度信息(车轮速度和横摆率)可作为正常分布的噪声测量变量。从这里开始,计算速度。
我们现在转向更复杂的部分。因此,提到的将平均值和方差相乘或相加的过程仅在一维情况下有效。在多维问题中,我们将在一个矩阵中得到均值和方差,所有的运算都在这个矩阵中进行。也就是说,当你想要测量的状态只需要一个变量就可以完全描述。开始时提到的确定车辆在隧道中位置的例子,已经不能完全用变量来描述了。虽然只对位置感兴趣,但这已经是平面上的二维问题了。另外,只能测速度,不能直接测位置。这导致卡尔曼滤波器具有以下状态变量。
The state matrix consists of position and velocity in the x and y coordinates.
初始条件/初始化
系统状态 X
开始时,我们必须初始化一个初始状态。在一维情况下,状态是一个矢量。
如果什么都不知道,你可以简单地在这里输入零。如果一些边界条件是已知的,它们可以被传送给过滤器。以下协方差矩阵的选择控制滤波器收敛到正确(测量)值的速度
协方差矩阵 P
必须给初始状态一个不确定性。在一维情况下,方差是一个向量,但现在是所有状态的不确定性矩阵。以下是所有四种状态的示例。
The covariance matrix consists of uncertainty in the position and the velocity in the x and y coordinates.
该矩阵最有可能在过滤过程中被改变。它在预测和校正步骤中都被改变。可以基于传感器精度来初始化矩阵。如果传感器非常精确,这里应该使用小值。如果传感器相对不精确,这里应该使用较大的值,以允许滤波器相对快速地收敛。如果传感器非常精确,这里应该使用小值。
动态矩阵 A
然而,过滤器的核心是下面的定义,我们应该在充分理解物理环境的情况下建立这个定义。这对于很多真题来说并不容易。对于我们这个简单的例子(面内运动),其背后的物理原理来自于平滑运动。对于上面所示的状态矩阵,矩阵符号中的动态如下:
The Dynamic matrix helps us in defining the equations for predicting the Vehicle Motion Model.
这说明了状态向量从一个计算步骤移动到下一个步骤的“位置”。在我们的例子中,这种动态模型是“恒速”模型,因为它假设速度在滤波器的计算步骤( dt )中保持恒定。
This is the prediction step for the State matrix.
这只是反映了匀速运动的物理关系。更高的形式是恒定加速度模型,它是一个 6-D 滤波器,仍然包括状态向量中的加速度。原则上,这里可以指定其他动力学。
过程噪声协方差矩阵 Q
由于车辆的运动(在叠加的正态分布噪声的意义上)也可能受到干扰,这就是引入过程噪声协方差矩阵的地方。这个矩阵告诉我们滤波器,以及系统状态如何从一个步骤“跳到”下一个步骤。想象一下自动驾驶的车辆。它可以被一阵风或道路颠簸扰乱,这就产生了力的效应。驾驶员的速度变化也是作用在车辆上的加速度。如果加速度现在影响系统状态,则其物理相关性为 q。该矩阵是一个包含以下元素的协方差矩阵:
The Process Noise Co-variance matrix consist of the errors caused in the process.
通过放置向量,然后乘以加速度的假设标准偏差,很容易计算出来。
The Equations to set the Q matrix appropriately.
控制矩阵 B 和控制输入 u
外部控制变量(如:转向、制动、加速等。)是可能的。u 矩阵将包含系统的机器人输入,可以是瞬时加速度或系统从 IMU 或里程表传感器行进的距离。
测量矩阵 H
还必须告诉滤波器测量的是什么以及它与状态向量的关系。在车辆的例子中,汽车进入隧道,在第一点仅测量位置,仅测量速度!这些值可以用系数 1.0 直接测量(即速度直接用正确的单位测量),这就是为什么在中只有 1.0 被设置到适当的位置。
The H-matrix.
如果传感器以不同的单位或尺寸进行测量,测量矩阵中的关系必须映射到公式中。
测量噪声协方差矩阵 R
与一维情况下的方差一样,测量不确定性也必须在这里说明。
这种测量不确定性表明人们对传感器测量值的信任程度。因为我们测量位置和速度,这是一个 2 × 2 矩阵。如果传感器非常精确,这里应该使用小值。如果传感器相对不准确,这里应该使用较大的值。
单位矩阵 I
最后但并非最不重要的是,一个单位矩阵是必要的,这将用于简化卡尔曼方程。
过滤步骤预测/预测
卡尔曼滤波器的这一部分现在敢于预测系统未来的状态。另外,在一定条件下,可以用它计算出一个不可测量的状态!太不可思议了,但这正是我们所需要的。我们无法测量车辆的位置,因为导航设备的 GPS 在隧道中没有接收信号。然而,通过用位置初始化状态向量并测量速度,动力学仍然被用于做出关于位置的最佳预测。
还必须重新计算协方差。在预测步骤中,系统状态的不确定性增加,正如我们在一维情况中所看到的。在多维情况下,测量不确定性增加,因此不确定性变得越来越大。
P=A⋅P⋅A’+Q
就是这样。卡尔曼滤波器对未来或即将到来的时间步中的预期系统状态做出了预测声明。滤波器现在将测量/校正并检查系统状态的预测是否与新的测量值很好地吻合。
被滤波器选择为较小的协方差说明了确定性,如果不是,那么一定有问题,这使得滤波器更加不确定。
过滤步骤测量/校正
注:以下数学计算不需要推导。
来自传感器的当前测量值,利用该测量值,通过使用测量值、具有测量矩阵的状态向量来获得新息因子(y)。
y=Z−(H⋅x)
然后,查看可以进一步计算的方差。为此,不确定度和测量矩阵和测量不确定度是必需的。
s=(h⋅p⋅h′+r)
这决定了所谓的卡尔曼增益。它说明了应该更熟悉读数还是系统动力学。
k =p⋅·h
如果读数(测量值)与预测的系统状态匹配,卡尔曼增益将降低。如果测量值与此相反,矩阵 K 的元素会变大。
该信息现在用于更新系统状态。
x = x + ( K⋅ y)
并且还为即将到来的预测步骤确定了新的协方差。
p = p-(k⋅·惠普)
也就是说,
⋅·p
现在回到步预测。图形看起来是这样的:
The Kalman filter could be understood as a loop (image source)
只要有测量值输入,该过滤器就会永久运行。它也可以是开环的,因此如果没有可用的测量值,则只执行预测步骤。然后不确定性越来越大。
工作中的过滤器
当我们驶入隧道时,GPS 会记录下我们最后知道的位置。卡尔曼滤波器仍然可以预测车辆的位置,尽管它不是一直被测量。
现在假设通过 CAN 总线可获得大约每 20 米/秒的车速,6 次迭代仅需 0.1 秒。滤波器收敛相对较快,这取决于初始条件的选择。例如,在 100 次迭代(相当于车辆上的 2s)之后,方差已经非常低,因此滤波器对其估计和更新状态有信心。我实现的线性卡尔曼滤波器可以在这里找到。结果是:
The First Image depicts the Estimated Value of each estimated value of the velocity in the X and Y co-ordinates. The Second Image Illustrates the Kalman gain for each of the parameter in the State Matrix. The Uncertainty Matrix containing the variance in each parameter of the State Matrix. (https://github.com/sharathsrini/Self-Driving-Car/blob/master/Kalman_Filter.ipynb)
The First Image Plots the position of the vehicle in both X and Y Coordinate. The Second and Third Image Tells us the relationship between the estimated and the actual velocity in the X and Y Co-ordinates. (https://github.com/sharathsrini/Self-Driving-Car/blob/master/Kalman_Filter.ipynb)
卡尔曼滤波器的 Python 实现
def Kalman_Filter() :
for n in range(measurements):
x = A*x+B*u[n]
P = A*P*A.T + Q
# Measurement Update (Correction)
# ===============================
# Compute the Kalman Gain
S = H*P*H.T + R
K = (P*H.T) * np.linalg.pinv(S)# Update the estimate via z
Z = mx[n]
y = Z — (H*x) # Innovation or Residual
x = x + (K*y)
# Update the error covariance
P = (I — (K*H))*P
滤波器设计:我如何选择 Q 和 R?
总的来说,不管数值有多大,而是比例有多大。如果选择的值大十倍,这几乎不会影响滤波器。**价值观的比例至关重要。**正确的选择将直接关系到滤波器的性能,并构成滤波器设计的基本问题。
这个非此即彼的问题只能在特定应用的基础上决定。在某些情况下:
- 我们只想为相对稳定的过程过滤测量不佳的传感器。例如,我们可以实现卡尔曼滤波器来优化火箭炉或化学炉中温度控制器。
- 我们还想合并几个传感器,并应保持动态。因此,应该选择矩阵。当然,可选地,过滤器可以被设计成在操作期间自动适应。
卡尔曼滤波器与递归最小二乘法有何不同?
flowchart of adaptive filtering techniques (image).
卡尔曼滤波器作用于用于线性和时变或时不变系统的预测校正模型。预测模型涉及实际系统和过程噪声。更新模型包括用观测噪声更新预测值或估计值。基于 RLS 算法计算卡尔曼增益,以便在更少的时间内达到最优值。
递归最小二乘法基于加权最小二乘法,其中考虑了以前的值以确定未来值。每个权重被指数分配给实际系统的每个先前值。权重基于内存递归更新。
显著差异:
Computational Time complexity of RLS
Computational Time complexity of Kalman Filter
- RLS 比卡尔曼滤波器快。
- 卡尔曼滤波器精度高。
- 卡尔曼滤波器是基于状态空间模型的,我们需要对整个系统建模以达到最优值。
卡尔曼滤波器和多项式回归
examples of polynomial regression(1)(2)
多项式回归是一种函数逼近的方法。我们有一个数据集,我们必须确定函数关系,这通常通过估计概率密度 p(z|x)来表示。在假设这个概率是高斯分布的情况下,我们得到最小二乘解作为最大似然估计量。
由于线性动态系统是状态空间模型,我们假设我们观察到的数据是通过应用线性变换产生的。我们得到的模型是一个时间序列的概率。然后,该过程预测时间序列的下一个值。
多项式回归做函数逼近,卡尔曼滤波做时间序列预测。
时间序列预测是函数逼近的特例。这两个模型都是基于他们观察到的数据的不同假设而建模的。
结论
卡尔曼滤波器相对快速且易于实现,并且在某些条件下为正态分布的噪声传感器值提供了条件的最佳估计。卡尔曼先生对他的算法深信不疑,以至于他能够启发美国宇航局的一位友好的工程师。因此,这个过滤器第一次帮助了登月的阿波罗导航计算机。
有用的链接:
(Image source)
- 罗杰·拉布的 Jupyter 笔记本收藏。
- 米歇尔·范·比森的惊人视频系列
- 画面中的卡尔曼滤波器。
- Vivek Yadav 的博文。
- 由大卫·西尔弗撰写的惊人帖子。
卡尔曼滤波面试
我目前进入了我的自动驾驶汽车 Nanodegree 的第二学期。最近,我遇到了我的一位同事 Larry,他是一名年轻的开发人员,非常兴奋能够成为自动驾驶汽车行业的一员。他问我对卡尔曼滤波的理解。对话开始了。
拉里:什么是卡尔曼滤波器?
我:卡尔曼滤波器是一种帮助预测数值的工具。
拉里:那太酷了!意味着它是某种占星家?
我:嗯,不完全是。这是一个迭代数学过程,使用一组等式和连续数据输入来快速估计我们感兴趣的与对象相关的值。基本上都是速算!
拉里:我的数学没那么好!也许这对我来说听起来有点荒谬。你能继续吗?我:首先,卡尔曼滤波器适用于高斯分布或正态分布。
拉里:正态分布?
Me:在一个连续的图形中,数据可以以不同的方式展开:要么向左展开,要么向右展开,要么杂乱无章地向上展开。
Figure 1. Example of left skewed and right skewed distribution (image source)
但是在许多情况下,数据趋向于围绕一个中心值,没有左右偏差,产生的分布称为正态分布、高斯分布或钟形曲线。
Figure 2. Example of a normal distribution, looks like a bell hence bell curve. (image source)
拉里:知道了!正如你指出的,这是一条连续曲线,而不是离散值。我怎么知道掷骰子得到 5 的概率?我:嗯,好问题,但是为了表示像得到骰子的概率这样的分布,我们使用二项式分布,因为它是离散值的。在正态分布中,你需要定义一个范围。说一下,4.5 到 5.5 厘米之间的降雨概率是多少。在这种情况下,我们有一个正态分布,我们标记这些点,并计算这些点下的面积,这就给出了概率。
拉里:哦!所以完全图的面积是 1,因为概率的最大值是 1?
Me:完全正确,另外在这种分布的情况下,均值、中位数、众数都是相等的。
拉里:但是你没有用数学术语来表示高斯?
我:所以,要定义高斯,我们基本上有两样东西——均值和方差。
均值( μ) 你显然知道,方差(σ)基本上讲的是数字散开了多少,离均值有多远。标准差(σ)就是方差(σ)的平方根,由公式给出:
拉里:那么高斯和卡尔曼滤波器有什么关系呢?
我:嗯!卡尔曼滤波器中的高斯表示预测值,在我们的预测中有噪声/误差/不确定性,通常称为方差。预测值以平均值为中心,高斯宽度表示我们的值的不确定性。基本上,它告诉我们有多少把握某个值是真实的。高斯曲线的宽度越大,表示不确定性越大。
拉里:哦!你在这里玩了一点多态性游戏。你之前提到这是一个迭代的过程?我:是的,这基本上是一个两步的过程。预测
2。更新
在预测中,我们只是根据初始值预测称为预测值的新值,然后根据系统中存在的各种过程噪声预测我们预测中的不确定性/误差/方差。
在更新中,我们考虑了来自设备的实际测量值,我们称之为测量值。这里,我们计算预测值和测量值之间的差异,然后通过计算卡尔曼增益来决定保留哪个值。然后,我们基于由卡尔曼增益做出的决定来计算新的值和新的不确定性/误差/方差。这些计算值将最终成为我们的卡尔曼滤波器在迭代 1 中所做的预测。
更新步骤的输出再次馈入预测状态,循环继续,直到我们的预测值和实际值之间的误差/不确定性趋于零。
赖瑞:嗯,那真是太快了。你能以任何例子或流程图来解释吗?
我:好的,那么从现在开始就按照这些记法来:
x ->均值
P - >方差
Figure 3. A Rough Flowchart for Kalman Filter
拉里:似乎很直观!但是卡尔曼增益是什么鬼?
Me:卡尔曼增益是决定预测值和测量值应占多大权重的参数。它是决定我们的实际值是接近预测值还是实测值的参数。
拉里:但是它怎么知道如何相信预测值或实际值呢?
我:它检查不确定性/错误我的朋友。
K =预测误差/(预测误差+测量误差)
K 的值范围从 0 到 1。如果我们有一个很大的测量误差,K 更接近 0,这意味着我们的预测值接近实际值。如果我们的预测误差很大,K 更接近 1,这意味着我们的测量值更接近实际值。
拉里:好的,同意!我现在开始兴奋了。你现在能详细说明这些方程式吗?
我:当然。对于预测和更新步骤,我会以不同的方式写下来。记住这些方程是 2D 空间的。
Figure 4. Equations for Predict Step
Figure 5. Equations for Update Step
拉里:到了这一步,也许我们应该结束讨论了。什么是 F,B,H?还有各种各样的变量被打乱了。你说这就是卡尔曼滤波器,我理解它的功能,但为什么现在有这些未知的东西?
我:别慌。我现在将详细解释一切。
假设我们想要根据来自不同传感器的测量结果预测汽车的位置和速度。
x ->包含位置和速度的平均状态向量。
P - >协方差矩阵(表示误差)。
x vector
预测步骤
等式 1:
x′= f . x+b .μ+ν
x′–>预测值
F - >状态转移矩阵
B - >控制输入矩阵
μ - >控制向量
ν - >过程噪声
f 矩阵
f 是将矩阵从一种形式转换成另一种形式所需的状态转移矩阵或适应性矩阵。例如,假设我们有一个模型,在这个模型中,我们预测了没有加速的物体的位置和速度。因此,在这种情况下,时间δt 后的新 p 和 v 为:
p′= p+vδt
v′= v
因此,在这种情况下,F 矩阵将为:
F Matrix
b 矩阵
b 是控制输入矩阵,表示由于内部或任何外力引起的对象状态变化。例如,重力或物体的摩擦力。
为什么 B.μ = 0?
大多数情况下,在自动驾驶汽车的情况下,控制产品向量的值等于零,因为我们无法模拟作用于汽车上物体的外力。
ν
这是过程中的噪音。我们添加了信道中可能存在的随机噪声,以使我们的预测稍微正确一些。
等式 2:
p′=fpfᵀ+q
p′–>预测协方差
Fᵀ - >状态转移矩阵的转置
Q - >噪声
q 矩阵
我们假设物体改变了方向,或者加速或减速。因此,在时间δt 之后,我们的不确定性增加了 Q 倍,这也是噪声。所以我们在技术上把噪音加到噪音里。
所以在预测步骤中,我们得到两个预测值 x’和 P’。
更新步骤
等式 1:
y = z-h . x′
z ->实际测量
H - >状态转移矩阵
x′->预测值
y - >测量值与实际值之差
z
这是来自传感器的实际测量值。
H
这又是一个状态转移矩阵。有了 H,我们可以从状态变量中丢弃我们不需要的信息。从技术上讲,H 做的工作和 F 在预测步骤中做的工作是一样的。
等式 2:
s =hp′hᵀ+r
k =p′hᵀs⁻R ->测量噪声
K - >卡尔曼增益
S- >总误差
s⁻->s 的逆
稀有
r 表示测量中的噪声。*什么?所以那些设备不是 100%准确?*是的,在这个世界上没有什么是完美的,甚至测量价值的设备也是如此。所有设备都带有由制造商给定的 R 参数预定义值,该值在整个周期内始终保持不变。
K
我们这里有一个复杂的等式,但它非常简单。我们正在计算卡尔曼增益 K,其公式已在前面给出。
S
这是系统的总误差。我们预测的误差加上测量的误差。
那么,为什么对于 K 来说如此复杂的方程在早先的公式中却是简单的呢? 这是因为我们对于矩阵没有除法的概念。因此,我们选择首先计算总误差,然后将预测中的误差乘以总误差的倒数。
等式 3:
x = x′+k . y
P =(I-KH)P′
最后一步
这是最后一步,我们根据卡尔曼增益的计算来更新 x 和 P。注意-在 LHS 上,我们有 x 和 P,而没有 x’和 P’,因为我们现在为下一个预测步骤设置 x 和 P,因此我们需要找到它们的值。
赖瑞:嗯,这需要一些时间来消化!为了完全理解它,我必须通过哪些额外的资源?
我:宇宙感谢米歇尔·范·比曾通过他的 YouTube 频道对卡尔曼滤波器的精彩贡献。观看他的视频,了解更多示例和见解。
总结所有等式:
Figure 6. Taken from Udacity Nanodegree
未完待续…(商店里的 EKF 和 UKF)
给读者的提示——如果你注意到这篇文章中有任何错误,请不吝赐教或留下私信。
更新 1- 扩展卡尔曼滤波器
更新 2- 无味卡尔曼滤波器
卡尔曼滤波器:直觉和离散情况推导
介绍
在这篇文章中,我们将回顾离散卡尔曼滤波器的推导过程。我们将首先建立由离散动态控制的系统的方程,然后表达近似系统,计算误差协方差并计算最小化误差协方差的更新规则。由于经由卡尔曼滤波的估计涉及连续的测量和状态传播,所以在离散实现的情况下它们更容易理解。
这部分是我在石溪大学开发的高级控制系统课程的一部分。随着时间的推移,我会继续在 medium 上添加额外的注释,但如果你感兴趣,可以在这里找到完整的注释。相关概率概念的细节可以在介绍性的课中找到。首先,我们将讨论模型可能具有的不确定性类型。
综上所述,根据不确定性的类型,对模型的影响可能会有所不同。由于我们不知道真正的不确定性及其结构,一个安全的做法是假设潜在的误差是零中心高斯过程。这种假设没有错,因为在大多数情况下,我们至少能够使用系统识别方法近似地模拟系统。作为一个例子,考虑这样一种情况,我们得到状态和状态的导数,并对数据拟合一个线性模型。这给了我们一个近似的模型,我们可以假设任何与这个理想化模型的偏离都遵循高斯分布。
卡尔曼滤波直觉-I
下面的动画展示了卡尔曼滤波器背后的直觉。状态按照系统动力学传播。因为我们不知道状态的真实值,所以我们基于测量来估计它们。由于这些测量是在不连续的时间进行的,所以真实的状态可能是什么是不确定的。然而,我们知道状态必须遵循一些近似的动力学。在下面的动画中,一个点以恒定的速度移动,因为位置、速度或加速度并不精确。
由于不知道确切的位置、速度和加速度,红点的可能位置在一段时间后会发生偏离。这导致真实状态下的估计误差。当一个新的测量值到来时,我们可以使用这个信息来丢弃一些不太可能的值。在进一步描述之前,让我们澄清一些术语,
- 状态传播是指在测量到来之前,我们允许系统跟随其动态并计算近似状态值的过程。
- 先验估计值是在考虑新测量值之前,使用状态传播计算的值。
- 后验估计值是考虑新测量值后计算出的值。
- 误差协方差是真实状态和估计值之间的误差。
使用上面的术语,我们在测量可用之后执行状态传播。我们使用状态传播来计算状态概率分布的先验估计。一旦有了新的度量,我们就执行贝叶斯规则更新以获得更好的概率估计。下面给出了概率贝叶斯规则。这些摘自我关于不确定性的高级控制系统笔记。
执行这个步骤给出了位置和速度的更好的估计,尽管事实上我们只有位置的估计。
卡尔曼滤波直觉-II
我们将通过另一个例子来更好地理解卡尔曼滤波器如何将一个状态的测量值和系统动态结合起来,从而对测量状态和不测量状态给出更好的估计。
卡尔曼滤波器在两个主要步骤中执行状态估计。第一步涉及系统动力学的传播,以获得状态的先验概率,一旦获得测量值,就使用贝叶斯理论更新状态变量。下面的例子说明了这一点。考虑一辆汽车以固定速度沿直线行驶的情况。此外,假设我们只有位置的测量,而没有速度。开始时,传感器只知道位置,因此机器人状态可能所在的区域如下图所示,即机器人位于位置 0,但速度可以是沿垂直线的任何值。
现在考虑机器人移动 1 秒后的场景。当我们移动了 1 s,新的位置就是之前的位置加上速度。所以匀速运动 1 秒后的概率分布就是下图的红点给出的。
假设机器人以 1 的速度移动,下一个位置估计在 1 左右。通过在该点执行第二次测量,我们得到蓝色区域作为位置估计的可能值。
因此,从系统动力学来看,我们期望新的位置在红色区域,从传感器来看,我们期望状态在蓝色区域。这两个分布之间的重叠区域非常小,我们可以说粒子的真实状态将位于这个区域。贝叶斯定理可以用来估计多元状态分布概率。沿着 x 轴和 y 轴呈现速度和位置的相应概率分布。将系统动态与状态测量相结合的过程是卡尔曼滤波器的基本原理。卡尔曼滤波器提供了良好的估计特性,在过程和测量服从高斯分布的特殊情况下是最优的。
一些更直观的解释:
- s 是反过来衡量测量误差的灵敏度矩阵。因此,如果传感器噪声过大,R 中的相应条目将会较高,通过求逆,来自该传感器的测量值将会减少。
- 如果 Q 很高,即我们不知道真实的模型,并且我们的近似模型具有大的偏差,则先验协方差估计将会很大,这具有直观意义。
- 对于线性系统,增益项 K 充当来自先验预测的状态估计和基于测量的校正之间的加权因子。
添加
NIS,收敛
道歉
我很抱歉从我的笔记中复制粘贴屏幕截图。我还没有想出一个好的方法来合并介质中的方程。
参考文献:
上面所有的截图都来自我在石溪大学开发的一门高级控制系统课程。完整课程的链接是https://mec560sbu.github.io/。
保持冷静,多做测试!
作为数据科学家,我们都很熟悉流程、包或应用程序中断时会发生什么。我们饶有兴趣地深入其中,试图诊断意外错误发生在哪里。原始数据中是否发生了意想不到的事情?我们的代码没有预料到特定的输入排列吗?
由于令人沮丧的调试和重写,修复问题的过程可能从简单的调整到几天的揪头发和敲打头部。
最近,我开始接触 R 语言的测试,对它的直观性感到惊喜。当我们开发一个过程、包或应用程序时,在工作流程中增加测试并不需要花费太多的精力,但是在调试方面的好处是巨大的。
Debugging can be a nightmare — do yourself a favor and set up a testing workflow
R 中的测试基础设施
R 中支持平滑、自动化测试的关键包是testthat
。在您的 R 项目中,您可以通过一个简单的命令来初始化测试基础设施
devtools::use_testthat()
这个命令将在您的项目中创建一个名为tests/testthat
的目录,您可以在其中放置执行测试的 R 脚本。
如果您的测试需要在本地设置环境变量,例如数据库凭证,您可以通过使用
usethis::edit_r_environ("project")
这将在 RStudio 中打开一个可编辑的文件,您可以在其中输入特定项目的变量名和值。
为了编写测试本身,您可以创建包含测试代码的 R 脚本,并将它们放在新的tests/testthat
子目录中。这些脚本就像任何其他的 R 代码,除了它们使用方便的包装和为测试设计的testthat
函数。关键函数是testthat::test_that()
,它有两个参数:
desc
,字符串形式的测试描述,如"Test that I have all the cat data I expect"
code
,代码执行您需要的计算,然后测试这些计算是否返回预期值,包含在{}
中。
通常情况下,code
会以某种形式的比较函数结束。testthat
包含多个比较功能,例如:
testthat::expect_equal()
测试两个参数是否相等。testthat::expect_gte()
测试第一个参数是否大于或等于第二个参数testthat::expect_s3_class()
测试第一个参数是否具有第二个参数中给出的 S3 类testthat::expect_true()
测试包含在参数中的语句评估为TRUE
一旦您编写了测试脚本,您就可以使用简单的命令devtools::test()
自动运行所有的测试并查看性能摘要。
测试代码的示例
这里有几个非常简单的例子。假设我们有一个进程,它通过mtcars
数据集中的cyl
生成平均值mpg
,并将其写入名为mpg_by_cyl
的数据库表中。
现在,作为一名优秀的 R 公民,您非常了解您的mtcars
数据集,并且您知道有三个cyl
值,因此您会期望您的mpg_by_cyl
数据库表有三行。因此,在您的测试脚本中,您可以从数据库中获取mpg_by_cyl
,并且您可以编写以下简单的测试:
testthat::test_that("mpg_by_cyl has expected number of rows", {
mpg_by_cyl %>%
nrow() %>%
testthat::expect_equal(3)})
当您运行您的测试时,如果mpg_by_cyl
确实有三行,您将从devtools::test()
那里得到一个好的Woot!
或者类似的鼓励话语。如果它没有像预期的那样返回,您将会被警告失败的条件,并得到一条像Nobody
s perfect!这样的安慰信息,然后您就会知道与
mpg_by_cyl`相关的事情没有像预期的那样发生。
这是另一个例子。假设您有一个脚本,它生成一个对象date
,该对象包含一个表示格式"%d %B %Y"
的最近日期的字符串,例如"14 December 2017"
,您的流程将这个字符串写入某个数据库。您希望测试是否确实生成了有意义的日期并将其写入数据库。一种方法是检查字符串是否包含" 20"
,这是您对任何最近日期的预期格式。因此,您可以让您的测试文件从它被写入的数据库中获取date
,然后编写下面的测试:
testthat::test_that("date contains a recent date", {
testthat::expect_true(grepl(" 20", date))})
这将评估写入数据库的内容是否包含预期的字符串,如果失败,您就知道这个过程出错了。(当然,这并不能完全验证预期日期已被写入,但数据科学家将确定如何编写特定的测试,以提供他们所需的确定程度)。
就个人而言,我发现 R 中的测试基础设施非常直观,我建议您养成为所有的包、过程和应用程序编写测试的习惯。你可以以后再谢我!
关于 R 中测试的更多信息,查看 Hadley Wickham 的开卷 这里 。
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedIn或Twitter上找我。
保持简单!!—如何简化对梯度下降等算法的理解
Source: dilbert.com
当我第一次开始学习机器学习算法时,获得算法正在做什么的直觉是一项相当艰巨的任务。不仅仅是因为它很难理解所有的数学理论和符号,而且也很无聊。当我转向在线教程寻求答案时,我再次只能看到方程或高级解释,而没有浏览大多数情况下的细节。
就在那时,我的一位数据科学同事向我介绍了在 excel 表格中计算算法的概念。这对我产生了奇迹。任何新的算法,我都试着在 excel 中小规模地学习,相信我,它能奇迹般地增强你的理解,帮助你充分欣赏算法的美。
让我用一个例子来解释以上内容。
大多数数据科学算法都是优化问题,最常用的算法之一是梯度下降算法。
现在,对于初学者来说,梯度下降算法这个名字听起来可能有点吓人,但是,希望在看完这篇文章之后,这种情况会有所改变。
让我们以从住房数据预测新价格的价格为例。
给定**历史住房数据,**任务是创建一个模型,在给定房子大小的情况下预测新房子的价格。
任务——对于一栋新房子,给定它的大小(X ),它的价格(Y)是多少?
让我们从绘制历史住房数据开始:
现在,我们将使用一个简单的线性模型,其中我们在历史数据上拟合一条线,以预测给定其大小(X)的新房子的价格(Ypred)
在上图中,红线给出了给定房屋面积(X)的预测房价(Ypred)。Ypred = a+bX
蓝线给出了历史数据中的实际房价(Yactual)
Yactual 和 Ypred 之间的差异(由黄色虚线给出)是预测误差(E)
因此,我们需要找到一条具有最佳值 a、b(称为权重)的线,通过减少预测误差来最佳拟合历史数据。
因此,我们的目标是找到最优的 a,b ,使房价的实际值和预测值之间的误差最小化:
误差平方和(SSE) = ∑(实际房价-预测房价)
= ∑ ( Y — Ypred)
(请注意,还有其他测量误差的方法。上交所只是其中之一。)
这就是梯度下降的由来。梯度下降是一种优化算法,它找到减少预测误差的最佳权重(a,b)。
现在让我们一步步来理解梯度下降算法:
步骤 1:用随机值初始化权重(a & b)并计算误差(SSE)
步骤 2:计算梯度,即当权重(a & b)从其原始随机初始化值改变一个非常小的值时 SSE 的变化。这有助于我们将 a & b 的值向 SSE 最小化的方向移动。
步骤 3:用梯度调整权重,向 SSE 最小的最优值移动
步骤 4:使用新的权重进行预测,并计算新的 SSE
第 5 步:重复第 2 步和第 3 步,直到对权重的进一步调整没有显著减少误差
我们现在将详细讨论每个步骤(我已经在 excel 中完成了上面的步骤,粘贴在下面)。但在此之前,我们必须将数据标准化,因为这可以加快优化过程。
步骤 1: 拟合直线 Ypred = a + b X,从 a 和 b 的随机值开始,计算预测误差(SSE)
步骤 2: 根据权重计算误差梯度
∂SSE/∂a =-(Y-YP)
∂SSE/∂b =-(Y-YP)X
这里,SSE= (Y-YP) = (Y-(a+bX))
这里有一点微积分,但仅此而已!!
∂SSE/∂a 和∂SSE/∂b 是梯度,它们给出了 a,b w.r.t 向 SSE 移动的方向。
步骤 3: 用梯度调整权重,以达到 SSE 最小的最佳值
我们需要更新 a,b 的随机值,使我们朝着最优 a,b 的方向前进。
更新规则:
①一个-∂SSE/∂a
2)-∂sse/∂b
所以,更新规则:
- 新的 a = a-r ***∂sse/∂a =**0.45–0.01 * 3.300 = 0.42
- 新的 b = b-r ∂sse/∂b*=**0.75–0.01 * 1.545 = 0.73
这里,r 是学习率= 0.01,这是对权重的调整速度。
步骤 4: 使用新的 a 和 b 进行预测,并计算新的总 SSE
你可以看到,随着新的预测,总上证指数已经下降(0.677 至 0.553)。这意味着预测的准确性提高了。
**第 5 步:**重复第 3 步和第 4 步,直到对 a、b 的进一步调整不会显著减小误差。这时候,我们已经到达了预测精度最高的最优 a,b。
这是梯度下降算法。这种优化算法及其变体形成了许多机器学习算法的核心,如神经网络甚至深度学习。
喜欢你读的书吗?要以类似的简化方式学习其他算法,请在www.deeplearningtrack.com注册为期 8 周的数据科学课程。通过访问 www.deeplearningtrack.com注册参加 2017 年 4 月 22 日的免费演示会
Jahnavi 是一位机器学习和深度学习的爱好者,在过去的 13 年里,他在美国运通领导了多个机器学习团队。她是 Deeplearningtrack 的联合创始人,这是一个在线讲师指导的数据科学培训平台—www.deeplearningtrack.com
保持简单愚蠢:模型选择的教训
我在大会上接受了一个项目,在那里我得到了乳房肿瘤的物理数据,并被要求预测肿瘤是恶性还是良性。在这次作业中,我被特别要求比较两种不同的建模技术,并解释为什么我选择了一种而不是另一种。我开始使用的两种技术是逻辑回归和随机森林。在这种情况下,逻辑回归是最终的赢家,因为随机森林最终没有提供任何更多的预测能力,并且向模拟客户端解释推论会更加困难。
模型创建
在对数据进行缩放后,我创建了一个热图,它显示了我在逻辑回归的特征选择上几乎没有回旋的余地。几乎所有的东西都是高度相关的,除了几个变量,使用大多数变量会弄乱结果。我最终选择了三个变量(半径平均值、凹度最差和对称性最差),并添加了一个交互项。结果如下:
物流培训设置结果
准确率: 96%
灵敏度: 94%
特异性: 97%
逻辑测试集结果
准确率: 94%
灵敏度: 89%
特异性: 97%
我对这些结果非常满意,但是需要根据作业尝试另一个模型。随机森林是我经常使用的算法,我不必担心变量之间的独立性,所以我尝试了一下。训练集的结果稍好,但测试集的结果与逻辑回归的结果相匹配:
随机森林训练设置结果
准确率: 98%
灵敏度: 95%
特异性: 99%
随机森林测试集结果
准确率: 94%
灵敏度: 89%
特异性: 96%
型号选择
在这种情况下,逻辑回归是明显的赢家,因为它在以下方面更简单:
1。 逻辑回归是一种更简单的算法
大多数对数据科学了解有限的人都知道,线性回归就是在数据集上画一条线。那么,解释逻辑回归是相同的事情,除了目标被 0 和 1 限制,结果是记录是您试图预测的目标类的概率,这并不是一个飞跃。
这与随机森林相反,随机森林是一种集合方法。我认为大多数人能够理解单一决策树的概念。但是解释为什么你要创建多棵树来防止过度拟合,以及它们是如何组合的就有点困难了。在这两种算法中,我更愿意向客户解释逻辑回归。
2。 逻辑回归提供了更简单的推论
就像线性回归一样,你可以从逻辑回归中得到系数。我们可以看到,凹度的增加导致肿瘤恶性的可能性更高。
注意:我知道半径平均值和交互作用项并不显著,但是去掉它们会影响逻辑回归的预测能力。
这与随机森林不同,在随机森林中,您只能提取重要的特征。你不能说它们是如何与目标相关联的,因为一棵树上的变量分裂可能与另一棵树上的分裂相矛盾。
在如今神经网络和其他复杂算法大行其道的世界里,保持简单愚蠢是需要记住的重要一课。虽然它们可能很强大,但如果用更简单的方法也能达到同样的效果,你可能会得到额外的好处,能够更容易地向客户解释它和它的结果。
如果你想看与这个项目相关的 jupyter 笔记本,请查看我的作品集页面。
保持收益
为了确保您的客户获得您的模型的全部优势,请使用六适马和生产质量保证的常用工具之一来预测问题。
一旦你让客户相信一个预测模型将解决他们的问题,并且做出了一个可解释的模型让他们相信你已经为他们建立了那个模型,你最不需要的就是糟糕的实施或者环境中不可预见的变化来破坏你的模型的效力。
虽然这个问题在针对数据科学家的文献中可能没有受到足够的关注,但它在其他领域受到了很多关注,特别是制造业,他们已经开发了许多方法来预测和防止不良结果的发生。
困难在于该模型是在有限的数据集上开发的,该数据集具有某些特征。当您实现您的模型并开始使用预测时,您正在做一个隐含的假设,即那些条件继续成立。如果输入开始超出在训练阶段应用的范围,则输出可能是错误的,或者有时根本没有输出(例如,取决于所使用的算法)。请注意,后者可能是更好的情况,因为期望输出的用户会告诉你是否没有输出,但不会意识到输出是否继续但却是错误的。
考虑一个信用风险模型,其中收入是输入变量之一。如果收入普遍提高,或者更糟糕的是,收入的提高不成比例地影响到高收入者或低收入者,那么这个规则就可能被破坏。同样,使用车辆属性作为输入的汽车保险风险模型可能会受到影响,如果发生广泛变化,为了避免这种情况,需要对重要输入及其通常分布有很强的理解,并结合合理的实施后监控水平。为了检测这两种情况中的任何一种,监控传入数据的分布都是必不可少的。
有一种结构化的方法可以解决这个问题,这种方法已经在汽车制造业得到了发展,但是已经扩展到了其他领域,包括软件工程。这被称为 FMEA,代表故障模式和影响分析。FMEA 的目的是识别对良好流程结果的可能威胁,并确定最需要关注的优先级。
在制造环境中,FMEA 可能是为稳态流程(如装配零件)准备的,也可能是为项目准备的。将这一理念应用于生产中的预测模型的概念,该过程将被应用于生产中的预测模型,并且独立于更新现有模型或发布新模型的任务。在每种情况下,都会有特定的风险,尤其是在新模型或更新模型的情况下,不正确的数据流可能会进入评分流,或者可能会应用不正确的数据准备。
FMEA 的过程简单而直观,并且有许多针对制造环境的操作说明。在接下来的内容中,将概述该过程,并提供一些提示,说明如何将其从传统上下文中转换为适合于预测模型的实现和顺利使用的内容。
一群持有不同观点的人集思广益,讨论流程正常运行的可能危害。在预测模型的情况下,这将最小化创建模型的数据分析师和负责实现 ETL 过程的数据工程师或数据仓库人员,该 ETL 过程将数据交付给生产中的模型评分引擎。
小组集体讨论与特定实施或正在进行的过程相关的潜在失败。FMEA 手册通常包括单词列表,作为应用于手边工艺的提示,通常非常面向制造。就输入流是数据的预测模型而言,提示词可能包括“训练范围之外的数据”、“缺失”、“错误类型”(例如,期望整数、获取浮点)、“新记录太多/太少”等。
对于过程的结果方面也有提示词,即“效果”。预测建模特定提示可包括“无预测”、“不可能或无意义的预测”、“预测随机不正确”、“预测向上/向下/偏向特定类别”。
FMEA 进程的下一步是为每一个风险分配一个优先级,这决定了应该采取什么措施来防止负面结果。传统的风险优先级计算考虑了严重性、发生率和检测机会,给每一项打分(满分 10 分),并将三者相乘得到最终分数,称为风险优先级数(RPN)。这个数字用于指导决定在消除或控制已识别的问题上花费多少努力。
然后,最后一步是针对每种故障模式提出建议采取的措施。根据 RPN 和采取行动的难度或成本,范围可能从完全消除问题到建立预警系统到做出明智的选择不采取行动。
在一些具有相对正式文化的公司——以及维护这种文化的资源——FMEA 的行动被记录在控制计划中。在其他公司,他们可能会被添加到项目的实施过程中。文档的级别需要适合你的组织的文化——但仍然足以确保行动被实际采取。
多年来,FMEA 过程一直是制造业和其他领域质量保证的核心工具之一。它试图预测和减轻实际允许的尽可能多的潜在故障的哲学,显然不仅在数据科学,而且在更广泛的软件工程领域都有广泛的应用。对于一个分析师来说,努力说服客户允许对他们的数据进行分析,与他们一起开发一个合理的模型来解决他们的业务问题,然后由于一个可预见的数据错误或编码事故而在最后一个障碍中失败,当工具存在来阻止这种情况发生时,这将是真正的悲剧。
保持用户的信任
不要因为设定不切实际的期望而失去用户的好感
我之前讨论过维护客户和用户信誉的重要性,尽可能广泛地使用这些术语。在之前的讨论中,我写了通过建立尽可能透明的模型——最容易理解的模型——来维护可信度。这还有另一个方面,虽然与你建立的模型不太直接相关,但也同样有可能破坏你在客户眼中的信誉,那就是提供对项目时间的合理估计,也就是所谓的“信守承诺”。
像生活中的许多事情一样,这个建议比任何人都更容易给出,主要是因为它保证了有人会想知道你什么时候会完成某件事——活动越复杂,因此越难以准确估计,他们就越渴望听到你的估计。
企业家埃隆·马斯克(Elon Musk)为这个问题提供了一些很好的例子。举个小例子,马斯克依赖他人的资本来完成他雄心勃勃的一系列项目,包括以他宣称的生产水平将 Telsa 3 推向市场。
我在我工作过的地方看到过这种情况,未能兑现承诺导致可信度下降,但马斯克出现在公众面前提供了两个机会。一个是他在公众视野里,不认识我,我不能因为谈论他而失去任何朋友。另一个原因是,凭借在公众眼中的地位和马斯克自己对各种媒体报道他的方式的分析,我们可以追踪这种情绪已经发生变化的方式。
马斯克就是马斯克,这种可信度的相对损失至少有可能不会产生进一步的后果。然而,我确信,如果我以这种方式拿我的信誉冒险,我会失去支持者。如果由我来管理特斯拉,这可能意味着投资者要么退出,要么停止出资。在撰写本文时,特斯拉的现金流并不乐观,这可能会影响该公司的生存能力。
在数据科学项目的背景下,风险可能是要么你当前的项目被冻结,要么你的下一个提案被否决。
有两种方法可以进入这种情况。第一个原因是完成项目的时间太少,第二个原因是未能有效地管理项目。正如这里报道的,例如,有许多人认为马斯克属于前一种人,这是我们现在要看的方面。过多的项目管理框架和项目管理建议本身就是一个行业——关于估计完成项目所需时间的方法的讨论相当少见。
与此同时,缺乏对完成项目所需时间的评估——或者至少是对公众讨论的关注——是缺乏做事情所需时间的数据。对于许多工程项目,有标准的成本工程方法,包括首次切割时间估计和清单,以确定增加额外时间的必要性(在这种情况下,我们指的是现实世界的项目,因此这些是像难以进入现场,或材料可能会被延迟之类的事情)。
在数据挖掘的背景下,缺乏关于事情需要多长时间的研究和良好数据,即使这与火星旅行的程度不同。一种方法, DMCoMo 提供了考虑的开端,但是被批评在狭窄的操作范围内缺乏实用性。
一个可能的解决方案是开始收集您自己的数据,使用 DMCoMo 或类似文件建议的潜在成本驱动因素列表作为指南,指导您考虑哪些因素以及收集哪些数据。您仍然可以使用该框架,根据您已完成的项目、它们涉及的内容以及投入的工时来估算开始时间。
DMCoMo 集团是
- 源数据,例如数量、质量
- 数据挖掘模型,例如数量、类型
- 开发平台
- 工具,如现有员工获得的培训水平
- 项目,例如涉及的不同部门的数量
- 项目人员,例如领域知识水平。
如果你是唯一一个记录这些信息的人,突然之间,你将会把你演讲中的一个造成可信度差距的弱点变成一个相对于其他提出项目的人的优势。如果你有最大的可信度,你就有优势,这将帮助你越过难以说服的利益相关者,而不是遇到垃圾桶,你的项目将看到绿灯。
罗伯特·德格拉夫是 LeanPub 出版的新书《懒惰的数据科学家》的作者,也是同名博客的作者。他最近在媒体上的报道是“喜鹊数据科学”。
保持您的数据科学技能准备就绪并不断提高
为什么偶尔给自己设置一些随机挑战可以帮助你作为兼职数据科学家的生活——一个使用苏格兰橄榄球冠军和动画条形图的例子…
Photo by Jordan Sanchez on Unsplash
数据科学不像骑自行车。反正对我来说不是。如果我不继续使用它,我就会失去它。我需要在这些小灰色细胞忘记他们花时间学习的重要的 R 函数或语法之前,让它们保持活跃。
我发现这特别困难,因为我研究范围如此之广。我可能会每三到四周建立一个快速的 ML 模型,基本的数据争论,EDA,数据可视化和一些回归一周几次,每两周进行一些无监督的学习,可能每两个月进行一次情感分析或地理绘图项目。我不介意承认我确实忘记了一些事情。
想要了解营销与数据科学结合领域的新闻,请关注 Chris 的 Twitter
也有很多天,我根本不花时间使用 R 或做任何可能被称为数据科学的事情。作为一名数字营销人员,我的工作描述涵盖了很多领域,其中大部分不是数据科学。当你每周增加几天休息时间时,掌握一门语言会变得有点棘手。
为了防止人才流失,也为了迫使自己走出舒适区,我时常喜欢给自己设置一个小挑战:让我稍微思考一下如何着手完成 r 中的一些小事。这种技术不时会有用。
使用 ggplot2 和 magick 制作动画图
这个周末,我一直在考虑写一篇关于使用 gganimate 包在 R 中制作动画的短文。考虑到我可能想要绘制什么,我开始考虑一个简单的线图,显示随着时间的推移,我的 Heriot 的球队在苏格兰橄榄球联盟中对阵他们的死敌 Watsonians 的比赛中的最终位置。
然而,我越想越觉得这可能太容易了。所有会发生的是,我最终会从一些手动数据挖掘中手动创建一些结束位置的向量,并从其他地方复制和粘贴代码,并修改它以适应我的数据。那有什么好玩的?
因此,挑战变成了动画条形图——按年份,而不是在零和最终值之间插值——显示顶级联赛由赢得它的球队赢得的次数。这是一个稍微不同的挑战,但最终成为了一个很好的实践!
虽然这对于所有的普通用户来说可能看起来过于简单,但是像这样的一两个小时的快速练习对我们这些做兼职编码的人来说是再合适不过了。有趣的是,这个练习最终在代码方面没有挑战性,而是过程。一旦我脑子里有了我想如何完成事情的想法,编码可能只需要 15 分钟左右。我想这一切都要回到 PPPPPP
获取数据
第一步是获取数据。这是从维基百科关于苏格兰超级联赛的页面收集来的。虽然我可以手动输入赢家的向量,但这并不是一个真正可扩展的解决方案,也不会强化我以前的 rvest 学习,所以让我们来代替它。非常感谢这里方便的 SelectorGadget 的帮助。
# scrape Scottish top tier rugby winners from Wikipedia
library(rvest)scot_prem_page <- read_html("[https://en.wikipedia.org/wiki/Scottish_Premiership_(rugby)](https://en.wikipedia.org/wiki/Scottish_Premiership_(rugby))")winners <- scot_prem_page %>%
html_node(".column-width") %>%
html_text()
这只是给了我们一个恼人的单字符字符串,所以下一步是打破它:
# convert single string of text into list of winners
library(stringr)winners_2 <-
str_split(winners, "\n")winners_3 <- unlist(winners_2)winners_4 <- winners_3[-1]
随着获胜团队向量的创建,是时候将它转化为我们需要处理的数据框架了。
构建数据框架
我们已经有了赢家的向量,现在我们需要一个年份向量来匹配他们:
# create vector of years
year <- 1974:2018
现在,我们可以开始用我们的年份、获奖者来构建数据框架,我们将重命名我们的获奖者列,因为它变得有点难看:
# build dataframe of winners
winners_df <- data.frame(year, winners_4)# rename winners_4 column as winner
library(dplyr)
winners_df <- rename(winners_df, winner = winners_4)
我计划这样做的方式是创建一个数据框架,其中有一列对应于每个赢得冠军的俱乐部,我可以用它来累积获胜总数。首先,我们需要一个冠军俱乐部的载体:
# create vector of clubs who have won
winning_clubs <- as.character(unique(winners_df$winner))
然后,我们可以使用该向量将列添加到数据帧中。让我们借此机会将它转变为一个新的数据框架,这样,如果我们搞砸了,我们就有机会重新开始:
# create new column for each winning club, initialise with 0
winners_df[ ,winning_clubs] <- 0# create new version of dataframe to guard against complete balls up
winners_df2 <- winners_df
新创建的列,我想把它转换成一个整齐的格式,这样我就可以利用 tidyverse 的便利功能。为此,我想使用来自tidyr
的gather()
函数将我的多个“俱乐部”列转换成一个俱乐部列,展示每年的每个俱乐部:
# create long (tidy) version of dataframe
library(tidyr)
winners_df2 <- winners_df2 %>%
gather(club, win, -year, -winner)
构建完成后,当club
列中的俱乐部与winner
列中的俱乐部匹配时,我可以使用mutate
调用在 win 列中添加 1,然后我可以按俱乐部分组,并使用另一个 mutate 调用来构建累积获胜的列:
# alter win column to reflect if won that year
winners_df2 <- winners_df2 %>%
mutate(win = ifelse(club == winner, 1, 0))# create column for cumulutive wins
winners_df2 <-
winners_df2 %>%
arrange(year) %>%
group_by(club) %>%
mutate(wins = cumsum(win))# change club variable to factor
winners_df2$club <- as.factor(winners_df2$club)
制作动画
数据帧完成后,是时候构建情节和动画了。非常感谢弗龙肯斯汀这篇博文帮助我制作了这个过程的动画片段。
首先,我使用一年的数据和 ggplot2 创建了一个测试地块,以构建我正在寻找的地块类型。完成后,我准备为数据集中的每一年构建一个图,并将其保存到我的项目文件夹中。如果你不是 for 循环的粉丝,现在把目光移开:
# create plot for each year
for (yr in year) {
filename <- paste0("champion", yr,".jpeg")
win_plot <-
winners_df2 %>%
filter(year == yr) %>%
ggplot(aes(x = fct_reorder(club, wins), y = wins)) +
geom_col() +
coord_flip() +
labs(title = "Scottish Rugby Championships by Club 1974 - 2018",
caption = paste0("Year: ", yr),
x = "Club",
y ="Number of wins") +
ylim(0, 14) + # constrain y limits to prevent scale change over time
scale_y_continuous(breaks = c(2, 4, 6, 8, 10, 12)) +
theme_minimal()
name = paste0("winners_", yr,".jpeg")
ggsave(filename = name,
plot = win_plot,
device = "jpeg",
width = 6, height = 4,
units = "in")
}
一个装满情节的文件夹之后,我们可以使用 magick 包中的image_animate()
功能将各个图像组合成我们的动画。gif:
# create animation
library(magick)
frames = c()
images = list.files(pattern = "jpeg")
for (i in length(images):1) {
x = image_read(images[i])
x = image_scale(x, "300")
c(x, frames) -> frames
}animation = image_animate(frames, fps = 4)image_write(animation, "championship_winners.gif")
总结
这是我为这个数据集选择的 dataviz 类型吗?大概不会。我是说,这取决于观众。对于一个注意力跨度有限的社交媒体设置来说,它可能会工作,但标准的条形图会更清楚地显示最终的获胜数字,GitHub commits 风格的热图结合行尾总数可能会更容易看到最终的数字以及何时赢得冠军,但这不是重点。
这个练习的目的是给自己设定一个日常工作中不会出现的挑战,并尝试解决它。而且,虽然还有一些调整我可能会做(颜色编码酒吧的俱乐部颜色也许?),我觉得一个慵懒的星期天也不算太差。嘿,反正电视上也没什么…
本文使用的代码可以在这里的 GitHub 上找到。
保持您的生产模式新鲜
Somebody enjoying fresh air
一个伟大的模特需要爱和关注,如果它想在一生中像第一天一样有用的话。
随着时间的推移,随着关系的改变,任何统计或机器学习模型都会经历性能损失。有时这种情况发生得非常突然,就像 GFC 危机期间许多信用违约模型发生的情况一样。其他时候,退化发生在一个较长的时期,并且几乎可以由观察趋势的人来预测。
是什么导致了退化?首先,不管你有多小心,在某种程度上,你的模型符合噪音或潜在因素,也就是说,它一开始就是错误的,你的一些准确性是由于随机的机会。
下一个问题是事情确实会变。考虑一个汽车保险模型,它有与车辆规格相关的变量。其中一些变量将是有效的风险指标,因为它们与高风险驾驶员的购买选择相关,但随着购买偏好的变化,例如随着燃料价格的变化,更喜欢或多或少省油的汽车,相关性也会发生变化。类似地,在良好的经济条件下训练出来的信用违约模型,很容易漏掉在糟糕的经济条件下增加违约几率的因素。
从这两个例子中可以直观地看出,依赖于人类行为的模型可能特别容易退化,而在某种意义上与物理过程更密切相关的模型可能具有某种额外的稳定性。接下来,了解这对于您的模型的风险有多大以及在什么时间范围内将成为您的主题专家的关键盟友,并且在大多数情况下,将制定模型审查和再培训的定期计划。
同时,您可能希望使用数据告诉您的信息,因此您需要一些方法来确定新到达的输入数据是否已经更改。对于快速变化的环境尤其如此。
在数据点具有高度独立性的输入变量的情况下,统计过程控制中使用的控制图可用于检测过程的变化。
这些图表的使用有很多印刷版和网络版的指南,它们已经被成功使用了很多年。它们的共同之处在于,来自一个过程的测量值被顺序绘制在一个图表上,图表的中心线位于平均值(或其他适当的过程平均值)处,上下线代表通常的过程范围。因此,很容易确定过程何时改变了其范围或其平均结果。
但是,特别是对于属性或分类数据,为相对较小的数据开发的方法在用于大量数据时会产生有问题的结果。
R 中的 qicharts 包实现了这个问题的一个解决方案 David Laney 开发的‘质数’图表,当使用大的子组时,它继续给出精确的结果。这个包包含一个全面的质量控制图表,所以你将能够找到一个适合你的需要。
在设置连续数据的采样方案时仍需小心谨慎-请注意,没有必要使用每天收集的完整数据来检查输入变量的过程是否保持了模型实施时的特征,只要它足够大以具有代表性即可。
当然,有必要平衡对变化做出反应的努力和收益,特别是在有大量输入的模型中。如果您已经完成了您的模型的 FMEA,例如在第一次实施时,您将已经感觉到不同输入的相对重要性,以及特定变量的变化对整体模型性能的影响。在某些情况下,可能没有必要采取任何行动;其他变化可能需要立即采取行动,以防止做出糟糕的决策。
合理的模型监督与精心设计的模型检查计划相结合,对于保持一个优秀的产品模型是至关重要的。优先检查关键变量,并在发生变化时发出警告,这将确保您永远不会因环境变化而措手不及,使您的模型失去效力。
kegra:基于 Keras 的知识图深度学习
你好。我在过去的文章中提到过,我正在紧张地研究企业数据集的认知计算。这是那个。
这篇文章需要对深度学习有所了解,但是你应该能够理解数据科学的最基本知识。
我一直致力于用 GPU 上的深度学习来检测图形中的模式。托马斯·基普夫写了一个不错的库,用 Keras 对图节点进行分类。本文基于他的工作“图卷积网络半监督分类”。让我们看一看。
首先,什么是图?
嗯,我在工作中关注知识图表。这些图将“白宫”和“唐纳德·特朗普”这样的实体表示为节点,而“工作地点”这样的关系表示为边。我们如何建立这些图表是另一个时代的故事。在这篇文章中,我正在研究交易数据,以训练一个识别欺诈交易的分类器。如果你更喜欢顶点和弧而不是节点和边,那么阅读这篇文章。
在图形的怪异世界里,我感觉就像在家里一样。我对图形的研究可以追溯到我的硕士论文。在那项工作中,我对在有向无环图中寻找公共元素(凸子图)感兴趣。我正在根据处理器运行的软件来确定向处理器添加什么样的定制指令。我用整数线性规划来解决这个问题。对于大型图形,求解程序可能需要几个小时甚至几天。
该研究领域的链接:
- 嵌入式人工神经网络软硬件协同设计案例研究
- 可调指令集扩展标识
- 人工神经网络专用集成电路
- 使用自定义指令在 FPGA 上加速人工神经网络
- 在硬件限制下改进 ISE 识别
- 并行指令集扩展标识
- 可配置多处理器的静态任务调度
- 多处理器片上系统软硬件协同设计工具链指令集扩展识别的设计与实现
- SING:多处理器片上系统设计和系统生成工具
下面是 OrientDB 中知识图本体的一个例子:
Source: OrientDB demo page
第二,什么是我们能察觉到的模式?
我们想要标记节点。图中的每个实体都有一些我们想要分类的特征,我们只有一些节点的标签。我们可以预测简单的布尔标签,如“人”或“不是人”,以及更有趣的标签,如将节点分类到几个类别中的一个。然后,我们可以进行更复杂的回归,比如根据我们从图表中获得的实体数据来预测实体带来的风险。这包括节点到其他节点的连接。为了简单起见,让我们继续讨论本文中的布尔节点标记/分类问题。我们希望将大约 4000 个银行账户的 594643 笔交易标记为可疑或不可疑。我们希望在不到一分钟的时间内完成。不是几小时或几天。
第三,如何定义一个 kegra 看得懂的图?
我们需要指定两个文件。第一个包含节点描述的节点 id,第二个描述节点如何连接。在 kegra 提供的 cora 示例中,有 2708 个节点的描述和标签,5429 条边(节点对)定义了节点之间的连接。
以下是每个文件的几行视图:
Links between nodes
Each node ID is followed by features (mostly 0s) and finally there is a node label (e.g. Neural_Networks, Case_Based). The features are mostly 0s and wrap around for many lines in the screenshot above. Each feature represents the use in the document (node) of a certain word. More info in the kegra README here.
让我们试一试吧
首先,您需要 Keras 2,所以这样做:
pip install keras --upgrade
假设你安装了 Keras 和 TensorFlow,keras-gcn 依赖于 gcn,那我们就 git 克隆,一个一个安装。
#install gcn
git clone [https://github.com/tkipf/gcn.git](https://github.com/tkipf/gcn.git)
cd gcn/
python setup.py install
cd ..#install keras-gcn
git clone [https://github.com/tkipf/keras-gcn.git](https://github.com/tkipf/keras-gcn.git)
cd keras-gcn/
python setup.py install
首先让我们在一个现成的例子上运行代码,这个例子提供了名为 cora 的 kegra。我们在输出中看到,cora 从原始数据中检测并打印出了预期的节点和边的数量。
Training run on the cora dataset: 36% accuracy and rising.
Testing result on the cora dataset: 77.6% accuracy.
我们现在对 kegra 理解输入文件的方式做了一点小小的改变,只是为了让名字更好听。在 github 上的当前版本中,输入文件是“*”。引用"来描述节点之间的弧,并引用" 。内容”来描述节点。相反,我把 kegra 改成了“”。链接"和" *。节点”文件。您的数据文件夹现在应该是这样的:
~/kegra/keras-gcn/kegra$ ls -l data/cora/
total 7720
-rwxrwxr-x 1 ubuntu ubuntu 69928 Dec 3 02:52 **cora.link (was cora.cites)**
-rwxrwxr-x 1 ubuntu ubuntu 7823427 Dec 3 02:52 **cora.node (was cora.content)**
-rwxrwxr-x 1 ubuntu ubuntu 1560 Dec 3 02:52 README
~/kegra/keras-gcn/kegra$ ls -l data/**customerTx**/
total 7720
-rwxrwxr-x 1 ubuntu ubuntu 7823427 Dec 3 05:20 **customerTx.node**
-rwxrwxr-x 1 ubuntu ubuntu 1560 Dec 3 05:20 README
-rwxrwxr-x 1 ubuntu ubuntu 69928 Dec 3 05:20 **customerTx.link**
现在让我们用交易数据填充 customerTx.node 和 customerTx.link 。第一个文件是银行客户及其特征的列表。格式是:
Quick view of some transaction records. In this scenario, there is a sender and recipient of money, and a record of the amount sent (amount column), and the label applied by a human analyst that reviewed the transaction (fraud column). We can ignore the first two columns (the index and step columns).
The edges file (customerTx.link) records who the two parties are in each transaction.
The nodes file (customerTx.node) records information on each node in the graph as a sender of funds on each transaction. The txCount column lists the number of transactions (edges) leaving the node. The amountMean column specifies the mean transaction size. The fraudMean column is the mean of flagged transactions on the sender account during the period this data covers. Note that the vast majority of transactions are OK and not FRAUD, which is a dataset imbalance.
There are 4112 nodes in the graph. On average 2.3% have been flagged as problematic by an analyst.
我们现在可以使用 kegra 以不同的分析师准确度来分析图表。
如果一个完美的分析师对系统进行数据训练,它应该能够完美地学习如何分析图表。然而,如果人类分析师有 20%的时间是错误的,那么 kegra 模型的预测能力同样应该被限制在 80%。为了验证这一点,我在图表标签中添加了不同数量的随机噪声,以观察随着训练数据的质量越来越差,kegra 会如何表现。
以下是表格和图表形式的结果:
Raw results for the transaction labeling experiments, using deep learning on knowledge graphs
This is the same data as the table above, but in an easier to understand graphic
这里有很多东西需要消化。首先,我们看到,随着数据(蓝色)中的噪声增加,早期停止(x 轴上的标签)在训练中越来越早地出现。这告诉我们,特征的数量如此之少(几列)会导致训练数据的过度拟合。第二,我们看到测试精度普遍低于训练精度。这是意料之中的,因为分类器熟悉训练数据,而测试数据不熟悉。第三,测试精度不为零。很好!这意味着分类器可以仅使用图和每个节点的特征(txCount、amountMean 和 fraudMean)来重新生成 OK/FRAUD 标签。第四,分类器(橙色)的精度随着注入噪声(蓝色)的增加而下降。这意味着结果不是随机的。第五,我们看到,训练精度(红色)加上添加的噪声(蓝色)加起来约为 100%,这意味着分类器与标记数据集的分析师一样好/差,但不会差太多。
综上所述,kegra 在知识图分类上表现非常好。与他们论文中的结果相比,这些结果可能太好了。我将检查交易文件中的欺诈标签列是否太具解释性,并使用更难从更广泛的数据集中预测的功能来替换它,如原产国、城市、邮政编码和更多列。
我的下一个动作是从包含更多列的源文件中重新生成事务数据集,看看 kegra 的性能是否仍然如此之好。在 cora 数据集上没有提前停止,因此我怀疑交易数据对 kegra 来说没有挑战性,原因之一我在上面提到过。也许如果我在生成的图中嵌入更多的语义特征…接下来我可以做很多有趣的事情。
特别感谢托马斯·基普夫在发表前审阅了这篇文章。与我通常的高水平文章相比,这是一篇准备(和阅读)非常复杂的文章。如果你喜欢这篇关于图形深度学习的文章,那么请让我知道写更多像这样的研究内容。我也很高兴在评论中听到你的反馈。你怎么想呢?
试用拍手工具。轻点那个。跟着我们走。分享这篇文章的链接。去吧。
编码快乐!
——丹尼尔
丹尼尔@lemay.ai ←这个其实管用。打个招呼。
LEMAY . AI
1(855)LEMAY-AI
您可能喜欢的其他文章:
keras:R 中的深度学习
正如你现在所知道的,机器学习是计算机科学(CS)中的一个子领域。深度学习是机器学习的一个子领域,它是一套受大脑结构和功能启发的算法,通常被称为人工神经网络(ANN)。深度学习是当下机器学习最热门的趋势之一,有很多问题是深度学习大放异彩的,比如机器人、图像识别、人工智能(AI)。
今天的教程将带着keras
包用 Keras 简单介绍一下 R 中的深度学习:
- 您将从 R 中的深度学习包的简短概述开始,并且
- 你将读到更多关于 Keras、
[kerasR](https://www.datacamp.com/community/tutorials/keras-r-deep-learning#differences)
和[keras](https://www.datacamp.com/community/tutorials/keras-r-deep-learning#differences)
包之间的差异,以及当一个包是另一个包的接口时意味着什么; - 然后,您将真正开始使用 RStudio 的
keras
包:您将学习如何首先准备您的工作空间并加载内置数据集、虚拟数据和来自 CSV 的数据; - 接下来,您将看到如何探索和预处理您从 CSV 文件中加载的数据:您将标准化数据并将其分成训练集和测试集。
- 在这之后,你就准备好构建你的深度学习模型;在这种情况下,您将为多类分类构建一个多层感知器(MLP)。
- 您将学习如何编译和拟合模型以适应您的数据,如何可视化培训历史,以及
- 你将根据测试数据预测目标值;
- 最后,您将评估您的模型,解释结果并微调您的模型,使其性能更好:您将学习如何添加层和隐藏层,并了解如何调整优化参数以实现更好的结果。
- 此外,您可能希望保存您的(优化的)模型,或者在其他时间将其加载回。您将在本教程的最后一节看到这是如何完成的!
想进一步了解感知机、多层感知机(MLPs)等深度学习中的原始 Keras 或关键概念吗?考虑或参加 DataCamp 的Python 深度学习课程或参加 Keras 教程:Python 深度学习。
提示:在这里找到我们的 Keras 备忘单。
R 中的深度学习:包的简短概述
随着深度学习受欢迎程度的上升,CRAN 已经丰富了更多的 R 深度学习包;下面你可以看到这些包的概述,取自机器学习和统计学习 CRAN 任务视图。“百分位数”栏表示在文件中找到的百分位数:
[参见原文中的概述]
提示:关于 R 中深度学习包的比较,请阅读这篇博文。有关 RDocumentation 中排名和分数的更多信息,请查看这篇博文。
在 RDocumentation.org 上没有找到deepr
和MXNetR
,所以这两个包的百分比是未知的。
喀拉斯、keras
和kerasR
最近,两个新的软件包进入了 R 社区:由 Taylor Arnold 创作的[kerasR](https://github.com/statsmaths/kerasR)
软件包和【RStudio】的 [keras](https://github.com/rstudio/keras)
软件包。
这两个包都提供了 Python 深度学习包 Keras 的 R 接口,您可能已经听说过或者可能已经使用过它了!对于那些不知道 Keras 包必须为 Python 用户提供什么的人来说,它是“一种高级神经网络 API,用 Python 编写,能够在 TensorFlow、微软认知工具包(CNTK)或 Theano 上运行”。
你看,开始使用 Keras 是熟悉 Python 深度学习的最简单的方法之一,这也解释了为什么kerasR
和keras
包为 R 用户提供了这个奇妙包的接口。
在这种情况下,当一个包(比如 R keras
)是另一个包(Python Keras)的“接口”时,理解它的确切含义是有好处的。简单地说,这意味着带有接口的keras
R 包允许您享受 R 编程的好处,同时访问 Python Keras 包的功能。
注意这并不是一种不常见的做法:例如,h2o
包也提供了一个接口,但是在这种情况下——正如它的名字所暗示的那样——提供给 H2O,一个用于大数据的开源数学引擎,你可以用它来计算并行分布式机器学习算法。其他你可能知道的提供接口的包有RWeka
(Weka 的 R 接口)tensorflow
(tensor flow 的 R 接口)openml-r
(OpenML 的 R 接口)……可以继续说下去!
现在您已经知道了所有这些,您可能会先问自己以下问题:您如何比较原始的 Python 包和 R 包?
本质上,你不会发现 R 包和原来的 Python 包有太多的区别,多半是因为函数名几乎都一样;您注意到的唯一区别主要在于编程语言本身(变量赋值、库加载等等),但最重要的是要注意 R 包中包含了多少原始功能。
注意这个备注不仅对keras
库有效,对上面提到的tensorflow
、openml-r
、…等接口包也有效!
其次,你可能也想知道这两个 R 包之间有什么区别。好吧,如果你想考虑两者的区别,你可能要考虑以下几点:
keras
包使用管道操作符(%>%
)将函数或操作连接在一起,而在kerasR
中你不会发现这一点:例如,用kerasR
制作你的模型,你会发现你需要使用$
操作符。管道操作符的使用通常会提高代码的可读性,如果你以前使用过 Tidyverse 包,你肯定已经见过这个操作符了。- 您将会看到
kerasR
包含了一些函数,它们的命名方式与最初的 Keras 包相似,但并不完全相同。比如原来的(Python)compile()
函数叫做keras_compile()
;这同样适用于其他功能,例如fit()
,它变成了keras_fit()
,或者predict()
,当您使用kerasR
包时,它是keras_predict
。这些都是自定义包装。 - 你可以争辩 RStudio 的
keras
包的安装比kerasR
包的安装容易;要安装后者,您需要首先确保配置使用哪个 Python 版本,如果您在安装了多个环境或 Python 版本的 pc 上工作,这可能会变得很棘手。但我会让你决定这一点:)
现在您已经收集了一些背景知识,是时候真正开始使用 R 中的 Keras 了。正如您将在本教程的介绍中读到的,您将首先检查您的工作区的设置。然后,您将加载一些数据,经过一个简短的数据探索和预处理步骤,您将能够开始构建您的 MLP!
让我们继续吧!
安装keras
包
和往常一样,开始使用任何包的第一步是设置您的工作空间:将库安装并加载到 RStudio 或您正在工作的任何环境中。
不用担心,对于原创教程,包会帮你加载进去!
首先,确保您安装了keras
:您可以通过在您的控制台中运行devtools::install_github("rstudio/keras")
来轻松完成此操作。接下来,您可以加载包并安装 TensorFlow:
# Load in the keras package
library(keras) # Install TensorFlow
install_tensorflow()
当你做完这些,你就可以走了!很快,对吧?
提示:关于安装过程的更多信息,请查看软件包网站。
加载数据
现在安装过程已经清楚了,您的工作空间也准备好了,您可以开始加载数据了!此时,当涉及到您的数据时,您有三个大的选择:您可以选择使用keras
包自带的一个内置数据集,您可以从例如 CSV 文件加载您自己的数据集,或者您可以创建一些虚拟数据。
无论您处于哪种情况,您都会看到您能够快速开始使用该软件包。本节将快速浏览三个选项,并解释如何加载(或创建)您需要开始的数据!
如果你以前使用过 Python 中的 Keras 包,你可能已经使用函数mnist.load_data()
、cifar10.load_data()
或imdb.load_data()
访问过 Keras 内置数据集。
以下是一些使用keras
包加载 MNIST、CIFAR10 和 IMDB 数据的示例:
# Read in MNIST
data mnist <- dataset_mnist() # Read in CIFAR10 data
cifar10 <- dataset_cifar10() # Read in IMDB data
imdb <- dataset_imdb()
注意用keras
载入内置数据集中的所有函数遵循相同的模式;对于 MNIST 数据,您将使用dataset_mnist()
功能加载您的数据。
或者,你也可以快速制作一些虚拟数据来开始。要了解如何轻松使用matrix()
函数来实现这一点,请访问原始文章。
注意检查你的数据的数据结构绝对是个好主意;了解您正在处理的数据非常重要,因为这对您需要采取的后续步骤非常重要。在本教程的后面部分,您将了解到更多关于这方面的内容!
除了内置数据集之外,您还可以从文件中加载数据。在本教程中,您将重点关注从 CSV 文件中加载数据,但是如果您想了解更多关于在 R 中导入文件的信息,请考虑 DataCamp 的 R 数据导入教程。
让我们使用read.table
包中的read.csv()
函数加载来自 UCI 机器学习库的数据集。你可以在这里找到这个练习的代码。
检查您的数据导入是否成功总是一个好主意。你通常使用像上面 DataCamp Light chunk 中的head()
、str()
和dim()
这样的函数来快速完成这项工作。
这三个函数的结果不会立即指出任何异常;通过查看str()
函数的输出,可以看到Species
列的字符串是作为因子读入的。这没有问题,但是对于接下来的步骤来说,知道您将在哪里探索和预处理数据绝对是件好事。
数据探索
在本教程中,您将继续使用通过read.csv()
函数导入的著名的iris
数据集。
对于那些不具备处理这些数据所需的生物学知识的人,这里有一些背景信息:所有的花都有一个萼片和一个花瓣。萼片包围花瓣,通常是绿色的叶状,而花瓣通常是彩色的叶子。对于鸢尾花来说,这只是有一点点不同,如下图所示:
您可能已经在上一节中看到过,在导入之后,iris
数据框没有任何列名。现在,对于本教程的剩余部分,这并不太重要:即使read.csv()
函数将data.frame
中的数据返回给你,你需要传递给fit()
函数的数据需要是一个矩阵或数组。
关于刚刚提到的这两种数据结构,需要记住一些事情:—矩阵和数组没有列名;—矩阵是单一数据类型的二维对象;—数组是单一数据类型的多维对象;
提示:如果你想回顾一下 R!
注意另一方面,数据帧是一种特殊的命名列表,其中所有元素都具有相同的长度。它是一个多维对象,可以包含多种数据类型。当您检查前一节中的iris
数据帧的结构时,您已经看到了这一点。了解这一点并考虑到您将需要处理一个二维或多维的单数据类型的对象,您应该在开始构建您的神经网络之前准备好做一些预处理!
现在,列名可以方便地用于探索目的,它们肯定会帮助您理解数据,所以让我们借助names()
函数添加一些列名。接下来,您可以立即在您的数据探索中使用iris
变量!例如,画出花瓣长度和花瓣宽度如何与plot()
函数相关联。你可以在这里找到练习。
注意您使用unclass()
函数将物种名称,即“setosa,versicolor”和“virginica”转换为数字 1、2 和 3。
现在仔细看看绘图函数的结果:
该图显示了不同种类鸢尾花的Petal.Length
和Petal.Width
之间的正相关关系。然而,这可能是您想用cor()
函数测试的东西,它将给出数据集中包含的所有属性之间的整体相关性。你可以在这里找到代码。
此外,您可以结合使用corrplot
包和cor()
函数来绘制数据属性之间的相关性;在这种情况下,您将计算iris
数据框所有属性的总体相关性。您将计算结果存储在变量M
中,并将其传递给corrplot()
函数。
此外,不要忘记指定一个method
参数来指示您希望如何绘制数据!
您可以在原文章的中进一步尝试 DataCamp Light chunk 中的可视化方法。
利用 R 控制台进一步探索您的数据。
如果你想用ggvis
包,也就是图形的交互语法,为这些数据制作情节,可以看看 DataCamp 的《R 中的机器学习》新手教程或者上 DataCamp 的 ggvis 课程。
数据预处理
在构建模型之前,您还需要确保您的数据已被清理、规范化(如果适用)并被划分为训练集和测试集。由于数据集来自 UCI 机器学习知识库,您可以预期它已经有些干净了,但是让我们再次检查您的数据质量。
乍一看,你用head()
检查数据的时候,并没有真的看出什么异常,对吧?让我们利用summary()
和str()
来简单回顾一下您在检查数据导入是否成功时所学到的内容。
既然您已经确定数据足够干净,那么您可以开始检查对于您在本教程中使用的任何数据是否有必要进行规范化。
从上面 DataCamp Light 块中的summary()
函数的结果中,您可以看到虹膜数据集不需要进行规范化:Sepal.Length
属性的值从4.3
到7.9
并且Sepal.Width
包含从2
到4.4
的值,而Petal.Length
的值范围从1
到6.9
并且Petal.Width
从0.1
到2.5
。换句话说,虹膜数据集所有属性的所有值都包含在0.1
和7.9
的范围内,你可以认为是可以接受的。
但是,研究规范化对数据的影响仍然是一个好主意;您甚至可以将规范化的数据传递到您的模型中,看看是否有任何影响。这超出了本教程的范围,但是您可以自己尝试一下!代码都在本教程中:)
你可以自己制作函数来归一化虹膜数据;在这种情况下,这是一个最小-最大归一化函数,它将您的数据线性转换为函数 (x-min)/(max-min) 。从这个角度来看,将这个公式转换成 R 非常简单:创建一个函数,向其传递x
或一些数据。然后,您将计算第一次减法 x-min 的结果,并将结果存储在num
中。接下来,您还要计算 max-min 并将结果存储在denom
中。你的normalize()
函数的结果应该会返回num
除以max
的结果。
要对您的iris
数据(不包括目标值)应用这个用户定义的函数,您不仅需要使用normalize
,还需要使用lapply()
函数来归一化数据,就像本练习中的一样。
提示使用 R 控制台中的hist()
功能,研究归一化前(iris
)和归一化后(iris_norm
)的虹膜数据分布。
要使用keras
包中的normalize()
函数,您首先需要确保您正在使用一个矩阵。您可能还记得,矩阵的特征是矩阵数据元素具有相同的基本类型;在这种情况下,您有 factor 类型的目标值,而其余的都是数字。
这首先需要改变。
您可以使用as.numeric()
功能将数据转换成数字。在这里找到代码。
数字数据框是可以的,但是如果你想使用keras
包,你需要将数据转换成数组或矩阵。您可以通过as.matrix()
功能轻松实现这一点;不要忘记在这里将dimnames
设置为NULL
。
正如您可能已经在上一节中读到的,归一化虹膜数据是没有必要的。尽管如此,研究规范化及其效果仍然是一个好主意,看看如何不仅通过 UDF,而且通过内置的normalize()
函数来实现。
将数据转换成矩阵后,您确实可以使用keras
包来研究数据标准化的效果。点击查看练习。
注意这里使用dimnames()
将iris
的 dimnames 设置为NULL
。这可以确保数据中没有列名。
现在,您已经检查了数据的质量,并且知道没有必要对数据进行规范化,您可以继续使用原始数据,并将其分为定型集和测试集,以便最终准备好开始构建模型。通过这样做,您可以确保事后对预测模型的性能进行诚实的评估。
在将数据分成训练集和测试集之前,最好先设定一个种子。用set.seed()
可以很容易地做到这一点:使用这个函数并传递一个随机整数给它。种子是一个数 R 的随机数发生器。设置种子的主要优点是,无论何时在随机数生成器中提供相同的种子,您都可以获得相同的随机数序列。
这对于代码的可复制性来说非常好!
您使用sample()
函数获取一个样本,其大小设置为虹膜数据集的行数,即 150。使用替换进行采样:从两个元素的向量中进行选择,并将 1 或 2 分配给虹膜数据集的 150 行。元素的分配受制于0.67
和0.33
的概率权重。
要查看代码、练习和答案,请点击这里。
sample()
函数的replace
参数被设置为TRUE
,这意味着你将一个1
或者一个2
赋值给某一行,然后将2
的向量重置为原来的状态。
换句话说,对于数据集中的下一行,每次都可以分配一个1
或2
。选择1
或2
的概率不应该与剩余物品的权重成比例,所以你指定概率权重。
*补充说明:*例如,如果您使用了带有特定dataset_imdb()
函数的内置数据集,那么您的数据可以通过使用$
运算符轻松拆分:
x_train <- imdb$train$x
y_train <- imdb$train$y
x_test <- imdb$test$x
y_test <- imdb$test$y
您已经成功分割了数据,但是还需要完成一个步骤才能开始构建模型。你能猜出是哪个吗?
当您希望使用神经网络对多类分类问题进行建模时,通常最好确保将目标属性从包含每个类值的向量转换为包含每个类值的布尔值的矩阵,无论给定实例是否具有该类值。
这是一个热门编码(OHE)的松散解释。听起来很复杂,不是吗?
幸运的是,keras
包有一个to_categorical()
函数可以帮你完成所有这些;将iris.trainingtarget
和iris.testtarget
传入该函数,并将结果存储在iris.trainLabels
和iris.testLabels.
中
在原教程里看看这是怎么做到的。
现在,您已经正式到达了本教程中探索和预处理步骤的末尾。你现在可以继续用keras
构建你的神经网络了!
构建模型
要开始构建模型,您应该首先在keras_model_sequential()
功能的帮助下初始化一个顺序模型。然后,你就可以开始建模了。
然而,在你开始之前,最好重温一下你最初关于这个数据集的问题:你能预测某种鸢尾花的种类吗?使用数字数据更容易,您已经对数据进行了预处理,并对目标变量的值进行了热编码:一朵花的类型是 versicolor、setosa 或 virginica,这反映在二进制值1
和0
中。
在这种问题上表现良好的一种网络是多层感知器。这种类型的神经网络通常是完全连接的。这意味着您正在寻求构建一个完全连接的层的相当简单的堆栈来解决这个问题。至于你会用到的激活函数,出于熟悉 Keras 和神经网络的目的,这里最好用一个最常见的,就是 relu 激活函数。这个整流器激活功能用于隐藏层,一般来说这是一个好的做法。
此外,您还可以看到 softmax 激活函数用于输出层。这样做是因为您希望确保输出值在 0 和 1 的范围内,并且可以用作预测概率。
在这里尝试互动练习。
注意输出层如何创建 3 个输出值,每个值对应一个虹膜类别(versicolor、virginica 或 setosa)。另一方面,包含 8 个隐藏音符的第一层的input_shape
为 4。这是因为你的训练数据iris.training
有 4 列。
您可以使用以下功能进一步检查您的模型:
- 您可以使用
summary()
功能来打印您的模型的概要表示; get_config()
将返回一个包含该型号配置的列表;get_layer()
将返回图层配置。layers
属性可用于检索模型图层的展平列表;- 要列出输入张量,可以使用
inputs
属性;和 - 最后,为了检索输出张量,您可以利用
outputs
属性。
你可以在这里找到练习。
现在您已经建立了模型的架构,是时候编译并使模型适合数据了。为了编译您的模型,您用adam
优化器和categorical_crossentropy
损失函数配置模型。此外,您还可以通过将'accuracy'
传递给 metrics 参数来监控训练过程中的准确性。你可以在这个页面找到代码。
如果要编译模型,优化器和损失是两个必需的参数。
使用的一些最流行的优化算法是随机梯度下降(SGD)、ADAM 和 RMSprop。根据您选择的算法,您需要调整某些参数,比如学习率或动量。损失函数的选择取决于您手头的任务:例如,对于回归问题,您通常使用均方误差(MSE)。
正如您在本例中所看到的,您使用了categorical_crossentropy
损失函数来解决多类分类问题,即确定虹膜是属于杂色鸢尾、海滨鸢尾还是刚毛鸢尾。但是,请注意,如果您遇到了二元类分类问题,您应该使用binary_crossentropy
损失函数。
接下来,您还可以让模型适合您的数据;在这种情况下,您在iris.training
和iris.trainLabels
中的所有样本上训练模型 200 个时期或迭代,每批 5 个样本。在这里查看代码。
提示如果你愿意,你也可以在fit()
函数中指定详细参数。通过将该参数设置为1
,表明您希望看到进度条日志记录。
您使用上面的代码所做的是针对指定数量的历元或对训练数据集的暴露来训练模型。一个时期是对整个训练集的一次遍历,随后是对验证集的测试。您在上面的代码中指定的批处理大小定义了将通过网络传播的样本数。此外,通过这样做,您可以优化效率,因为您确保不会同时将太多的输入模式加载到内存中。
可视化模型训练历史
此外,如果您将上面的 DataCamp Light 块中的代码行分配给一个变量,您也可以可视化拟合,这是一件好事。然后你可以将变量传递给plot()
函数,就像你在中看到的这个特殊的代码块!
一定要更详细地研究这个情节。
乍一看,这一切看起来有点乱并不奇怪。你可能不完全知道你在看什么,对吗?
需要知道的一件事是,loss
和acc
表示训练数据的模型损失和准确性,而val_loss
和val_acc
是测试或验证数据的相同度量、损失和准确性。
但是,即使你知道这一点,也不容易解释这两个图表。让我们试着把它分成几个部分,这样你可能更容易理解!您将拆分这两个图,并制作两个单独的图:一个用于模型损失,另一个用于模型精度。幸运的是,您可以轻松地使用$
操作符来访问数据并一步一步地绘制出来。
查看这个 DataCamp 灯箱来看看你如何做到这一点:
在第一个图中,您绘制了模型在训练和测试数据上的损失。现在也是做同样的事情的时候了,但是接下来为了模型的准确性:
以下是一些需要记住的事情:
- 如果您的训练数据准确性不断提高,而您的验证数据准确性却越来越差,那么您可能会过度拟合:您的模型开始只是记忆数据,而不是从中学习。
- 如果两个数据集的准确性趋势在过去几个时期仍在上升,您可以清楚地看到模型尚未过度学习训练数据集。
预测新数据的标签
既然您的模型已经被创建、编译并适合数据,那么是时候实际使用您的模型来预测您的测试集iris.test
的标签了。如您所料,您可以使用predict()
函数来完成这项工作。之后,您可以打印出混淆矩阵,在table()
函数的帮助下检查iris.test
数据的预测和真实标签。在这里看看是怎么做的。
你认为结果如何?乍一看,你创建的这个模型做出了正确的预测吗?
评估您的模型
尽管通过查看iris.test
的预测标签,您已经对您的模型的表现有了一点了解,但花时间评估您的模型仍然很重要。使用evaluate()
函数来完成:传入测试数据iris.test
,测试标签iris.testLabels
并定义批量大小。将结果存储在变量score
中,如中的代码示例。
通过打印score
,您将获得损失值和度量值(在本例中为'accuracy'
)。
微调您的模型
微调您的模型可能是您要做的很多事情,尤其是在开始的时候,因为并不是所有的分类和回归问题都像您在本教程的第一部分中看到的那样简单。当你读上面的时候,已经有两个关键的决定你可能想要调整:你要使用多少层和你要为每个层选择多少个“隐藏单元”。
一开始,这真的会是一段很长的旅程。
除了调整历元数或批量大小之外,还有其他方法可以调整模型,希望它能有更好的表现:增加层,增加隐藏单元的数量,将你自己的优化参数传递给compile()
函数。本节将讨论这三个选项。
如果你在你的模型中添加另一层会发生什么?如果它看起来像这样呢?
请注意您还可以看到这种新模型的损失和准确性指标!在原教程中尝试两个练习。
除了添加层和玩隐藏单元,你也可以试着调整你给compile()
函数的优化算法的参数。到目前为止,您总是将一个带有字符串的向量adam
传递给optimizer
参数。
但那并不总是需要这样!
此外,尝试尝试其他优化算法,如随机梯度下降(SGD)。例如,尝试使用optimizer_sgd()
功能调整学习率lr
。你注意到效果了吗?
除了使用另一个优化器,你也可以尝试使用较小的学习率来训练你的网络。这是最常见的微调技术之一;一种常见的做法是将初始学习率设置为比您之前用来训练模型的学习率小 10 倍。
让我们再一次可视化训练历史,看看这个小调整的效果;在这里看练习。
保存、加载或导出您的模型
在使用keras
包的过程中,还有最后一件事要做,那就是保存或导出您的模型,以便您可以在另一个时刻重新加载它。
- 首先,您可以很容易地使用
save_model_hdf5()
和load_model_hdf5()
功能来保存和加载您的模型到您的工作空间:
save_model_hdf5(model, "my_model.h5")
model <- load_model_hdf5("my_model.h5")
- 此外,您还可以使用
save_model_weights_hdf5()
和load_model_weights_hdf5()
功能保存和加载模型重量:
save_model_weights_hdf5("my_model_weights.h5")
model %>% load_model_weights_hdf5("my_model_weights.h5")
- 最后,很高兴知道您还可以将模型配置导出到 JSON 或 YAML。在这里,功能
model_to_json()
和model_to_yaml()
将帮助你。要将配置加载回您的工作区,您只需使用model_from_json()
和model_from yaml()
功能:
json_string <- model_to_json(model)
model <- model_from_json(json_string) yaml_string <- model_to_yaml(model)
model <- model_from_yaml(yaml_string)
恭喜你。你已经用keras
在 R 中完成了这个深度学习教程。这篇教程只是你深入学习 R 的旅程中的一小步;还有更多的内容要介绍!如果你还没有参加 DataCamp 的 Python 深度学习课程,你可以考虑参加。
与此同时,如果您还没有查看过 Keras 文档和 RStudio [keras](https://rstudio.github.io/keras/)
文档的话,请务必查看一下。你会找到更多关于所有函数、参数、更多层等的例子和信息…也可以看看 Franç ois Chollet 的书“Python 深度学习”。所有这些资源在学习如何在 R!
最初发表于【www.datacamp.com】。
使用 Hyperas 在 Google Colab 中调整 Keras 超参数
Tuning Hyperparameters for your neural network can be tricky (Photo by Anthony Roberts on Unsplash)
超参数调整是创建深度学习网络时计算量最大的任务之一。幸运的是,你可以使用 Google Colab 来大大加快这个过程。在这篇文章中,我将向您展示如何使用 Hyperas 调整现有 keras 模型的超参数,并在 Google Colab 笔记本中运行一切。
创建新笔记本并启用 GPU 运行时
Dialog to change the runtime to GPU
首先,您需要创建一个新笔记本。打开你的 Colab 控制台并选择新的 Python 3 笔记本。在笔记本中,从菜单中选择运行时,然后**改变运行时类型。**选择硬件加速器:GPU,点击保存。这将大大加快你在笔记本上做的每一个计算。
安装软件包
您可以使用 pip 来安装新的软件包。在这种情况下,我们需要 hyperas 和hyperpt。将以下内容复制并粘贴到笔记本的第一个单元格中:
!pip install hyperas
!pip install hyperopt
当您运行单元时,您将看到 pip 正在下载和安装依赖项。
获取数据并创建模型
在这篇文章中,我将使用来自 hyperas github 页面的例子。你可以在这里找到成品的 Colab 笔记本。
数据功能
你需要一个数据函数来加载你的数据。它需要返回你的 X_train,Y_train,X_test 和 Y_test 值。以下是一个数据函数的示例:
**注意:**你的数据函数需要完全按照这个顺序返回值:X_train,Y_train,X_test,Y_test。如果你使用 scikit learnstrain _ test _ split要小心,因为这会以不同的顺序返回值
模型功能
模型函数是定义模型的地方。您可以使用所有可用的 keras 函数和层。要添加用于调优的超参数,可以使用 {{uniform()}} 和 {{choice()}} 关键字。
假设您想为您的 batch_size 尝试不同的值。您可以简单地编写 batch_size={{choice([32,64,128])}} 并且在每次试验期间,将选择并试验其中一个值。关于如何定义要调整的参数的更深入的解释可以在 Hyperas Github 页面上找到,或者你可以看看例子:
**注意:**你的模型函数必须返回一个带有丢失键和状态键的 python 字典
Colab 的问题
如果您现在尝试运行此示例,试验将会失败,因为 Hyperas 将无法找到您的笔记本。你需要复制你的笔记本并再次上传到你的 google drive 文件夹。幸运的是,你可以在你的笔记本里这样做,正如这个 stackoverflow 回答中所描述的。
**注意:**在第 16 行和第 18 行,您需要将 HyperasMediumExample 改为您自己的笔记本的名称
运行此单元后,系统会提示您在浏览器中打开一个网站,并将代码复制粘贴回笔记本:
The Output after you run the cell above
点击链接,用你的谷歌账户登录,将代码复制粘贴回笔记本。如果你打开左侧边栏的文件标签,你应该会看到一个名为< YourNotebook >的文件。ipynb
开始试验
现在你可以开始试验了。请注意,您必须将参数 notebook_name 设置为您笔记本的名称。否则试验将会失败:
运行此单元后,扫描开始,您可以在单元的输出中看到结果。
故障排除
如果您在执行此操作时遇到任何问题,我建议您执行以下操作:
- 在左侧栏中,打开**文件。**会有一个名为< YourNotebook > .ipynb 的文件,删除那个文件
- 在菜单中选择运行时,然后选择重启运行时。
- 重新加载页面
在您的运行时再次连接后,您可以通过从上到下运行每个单元来重新开始
结论
只需稍作调整,您就可以使用 Google colab 来调整 keras 网络的超参数。同样,完整的例子可以在这里找到。
使用 Keras 中的迁移学习为初学者提供深度学习
本博客由三部分组成:
- 什么是迁移学习?
- 为什么迁移学习效果这么好?
- 使用迁移学习编写您的第一个图像识别器。
“galaxy with starry night” by Bryan Goff on Unsplash
什么是迁移学习?
如果不是为了迁移学习,机器学习对于一个绝对的初学者来说是一件相当艰难的事情。在最底层,机器学习包括计算一个函数,该函数将一些输入映射到它们相应的输出。虽然函数本身只是一堆加法和乘法运算,但当通过非线性激活函数并将这些层堆叠在一起时,函数可以用来学习任何东西,只要有足够的数据可以学习,以及巨大的计算能力。
欢迎来到深度学习。
当在足够多的数据上训练时,卷积神经网络可以学习极其复杂的映射函数。我们还不能理解卷积网络是如何学习如此复杂的函数的。
在基本水平上,CNN(卷积神经网络)的权重由过滤器组成。把过滤器想象成一个由特定数字组成的( nn)* 矩阵。现在这个过滤器是回旋(滑动和乘)通过提供的图像。假设输入图像的大小为(10,10 ),滤波器的大小为(3,3 ),首先将滤波器与输入图像左上角的 9 个像素相乘,该乘法产生另一个(3,3)矩阵。该矩阵的 9 个像素值相加,该值成为 CNNlayer _ 2左上角的单个像素值。
representation of convolutional networks
基本上,CNN 的训练包括在每个滤波器上找到正确的值,使得输入图像在通过多个层时,激活最后一层的某些神经元,从而预测正确的类别。
虽然从头开始训练 CNN 对于小项目来说是可能的,但是大多数应用程序需要训练非常大的 CNN,正如你所猜测的,这需要极其大量的处理数据和计算能力。这两者现在都不容易找到了。
这就是迁移学习发挥作用的地方。在迁移学习中,我们采用已经训练好的模型的预训练权重(该模型已经在几天内在几个高功率 GPU 上对属于 1000 个类别的数百万幅图像进行了训练),并使用这些已经学习的特征来预测新的类别。
迁移学习的优势在于:
1:不需要特别大的训练数据集。
2:不需要太多计算能力。因为我们使用预先训练的权重,并且只需要学习最后几层的权重。
有几个模型已经在 image net 数据集上进行了训练,并且是开源的。
比如 VGG 16,VGG 19,盗梦空间 V3 等等。有关这些型号的更多详细信息,请阅读 keras 官方文档此处。
为什么迁移学习效果这么好?
为了了解为什么迁移学习如此有效,我们必须首先看看卷积神经网络的不同层真正在学习什么。
当我们在图像数据集上训练深度卷积神经网络时,在训练过程中,通过在每层的图像上应用几个过滤器,图像穿过网络。滤波器矩阵的值与每层图像的激活相乘。来自最终层的激活用于找出图像属于哪一类。
当我们训练一个深度网络时,我们的目标是找到每个滤波器矩阵的最优值,这样当一个图像通过网络传播时,输出激活可以用来准确地找到图像所属的类别。用于找到这些滤波器矩阵值的过程是梯度下降。
当我们在 imagenet 数据集上训练一个 conv 网络,然后看看 conv 网络每一层上的过滤器已经学会识别什么,或者每个过滤器被什么激活时,我们能够看到一些真正有趣的事情。
conv 网前几层的过滤器学会识别颜色和某些水平线和垂直线。
接下来的几层慢慢学会使用前几层学到的线条和颜色来识别微小的形状。
然后下一层学习识别纹理,然后像腿,眼睛,鼻子等物体的一部分。
最后,最后一层的过滤器被整个物体激活,比如狗、汽车等等。
现在让我们开始转移学习。它工作得如此好的原因是,我们使用了一个在 imagenet 数据集上预先训练的网络,这个网络已经学会了在其初始层中识别不同对象的微小形状和小部分。通过使用预训练网络进行迁移学习,我们只需在预训练网络的末端添加几个密集层,并了解这些已学习特征的组合有助于识别新数据集中的对象。
因此,我们只训练几个密集层。此外,我们正在使用这些已经学习的琐碎特征的组合来识别新的对象。所有这些有助于使训练过程非常快,并且与从头开始训练 conv 网络相比,需要非常少的训练数据。
现在让我们使用 Keras 中的迁移学习建立一个实际的图像识别模型。
我们将在这里使用的模型是 MobileNet。
移动网是一种给出相当好的图像网分类精度并且占用非常少空间的模型。(根据 keras 文档显示为 17 MB)。
所需依赖关系:
- Keras(带 tensorflow 后端)
- Numpy
- Matplotlib
- 熊猫
数据要求:
- 训练数据必须以特定的格式存储,以便输入网络进行训练。我们将使用 keras 中提供的 ImageDataGenerator 来根据可用数据训练我们的模型。这样,这个过程在代码方面就变得简单多了。
- 必须有一个主数据文件夹,在该数据文件夹中,必须有一个包含相应图像的每类数据的文件夹。文件夹的名称必须是它们各自的类名。
模型的建立分三步走:
- 导入预训练模型并添加密集层。
- 正在将列车数据加载到 ImageDataGenerators 中。
- 培训和评估模型。
<开始编码/ >
首先加载依赖项。
然后导入预先训练好的 MobileNet 模型。Mobilenet(在 imagenet 数据集上训练了一千个类)将具有由 1000 个神经元组成的最后一层(每个类一个)。我们希望在网络的最后一层有多少神经元,就有多少我们希望识别的类别。因此,我们放弃了 1000 个神经元层,并为网络添加了我们自己的最后一层。
这可以通过在导入模型时设置( IncludeTop=False )来实现。
因此,假设你想训练一个狗品种分类器来识别 120 个不同的品种,我们需要在最后一层有 120 个神经元。这可以使用下面的代码来完成。
这是 流程的第 1 步 。导入和构建所需的模型。
我们导入没有最后一层的 MobileNet 模型,并添加一些密集层,以便我们的模型可以学习更复杂的函数。密集层必须具有 relu 激活功能,并且最后一层必须具有 softmax 激活,该层包含与类的数量一样多的神经元。
接下来,我们根据我们提供的架构制作一个模型。
为了检查我们模型的架构,我们只需要使用下面给出的这行代码。
现在我们有了模型,因为我们将使用预训练的权重,我们的模型已经在其上训练过(imagenet 数据集),我们必须将所有权重设置为不可训练的。我们将只训练我们以前制作的最后的密集层。下面给出了执行此操作的代码。
现在我们进入流程的 步骤 2 ,将训练数据加载到 ImageDataGenerator 中。
ImageDataGenerators 内置在 keras 中,帮助我们训练模型。我们只需指定训练数据的路径,它就会自动批量发送训练数据。这使得代码更加简单。
为此,我们需要博客前面提到的特定格式的训练数据。
接下来我们进入 步骤 3 ,在数据集上训练模型。
为此,我们首先编译我们制作的模型,然后用我们的生成器训练我们的模型。这可以使用下面的代码来完成。
有了这个,我们就训练出了一个模型。然后,通过使用 model.predict(new_image ),训练好的模型可用于预测新的看不见的图像属于哪一类。
一如既往,快乐学习。