制作矩阵奇异值分解的动画
使用 Python 和 Autodesk Maya
Image by the author
奇异值分解(SVD)是许多应用中出现的一种计算,它将一个矩阵分解成 3 个矩阵的乘积。例如,它用于以下领域:
- 在推荐系统中,如网飞。使用 SVD 构建推荐系统的介绍可以在下面找到:
https://towards data science . com/beginners-guide-to-creating-an-SVD-recommender-system-1fd 7326 D1 F6 - 在图像分析程序中使用主成分分析
https://towardsdatascience . com/eigen faces-recovering-humans-from-ghosts-17606 c 328184 - 以及文本的主题分析方法
https://medium . com/nano nets/topic-modeling-with-LSA-psla-LDA-and-LDA 2 vec-555 ff 65 b 0b 05
在本文中,我将借助 3D 动画展示这种分解背后的几何解释,以鼓励直觉。
您可以在下面找到对 SVD 的很好的介绍:
真正理解 SVD——直观的核心思想
towardsdatascience.com](/svd-8c2f72e264f)
和
奇异值分解方法的完整教程
blog.statsbot.co](https://blog.statsbot.co/singular-value-decomposition-tutorial-52c695315254)
所考虑的(数据)矩阵可以具有 n 行和 m 列的任何维度,并且矩阵中的条目可以代表图像的像素值、文本中的词频、用户对电影的评级或许多其他东西。
分解的三个矩阵有特殊的性质,与矩阵相关的线性变换有特殊的几何解释。我想用 3D 动画展示分解矩阵的几何解释。
为了便于说明,我使用 3x2 矩阵及其分解乘积作为例子,使数据可以用 3D 表示。小球体的栅格在动画中经受相应的线性变换。让我们看看结果:
Animation of the Singular Value Decompositions of some matrices
为了创建动画,我使用了 Autodesk Maya(https://www.autodesk.com/products/maya/overview)并在 Maya 中使用 Python 编写脚本。
让我们看看动画是如何录制的
我使用 Python 脚本在 Maya 中构建场景并制作对象动画。可以从 Maya 的脚本编辑器中运行脚本。
Maya script editor / Image by the author
在脚本开始时,导入所需的包。“Maya.cmds”用于在 Maya 中执行命令,“math”用于某些计算,“numpy”用于奇异值分解。创建了一个由 3 个平面构建的长方体,考虑球体在其上投射阴影。
我们设置光源并构建一个小球体网格来测试转换。
我们借助线性代数的“Numpy 包”(https://docs . scipy . org/doc/Numpy/reference/routines . Lina LG . html)定义我们想要分解、动画化和计算 SVD 的矩阵的变换矩阵。
我们需要一个函数来执行平面旋转。定义请看 https://en.wikipedia.org/wiki/Rotation_matrix 的。
以及一些用于计算 3D 旋转的旋转轴和旋转角度(欧拉角)的函数。关于定义和计算,请参见同一篇维基百科文章。
对于球体的中心,我们使用作为测试对象,我们通过取点积来计算矩阵变换后的新坐标。与矩阵 A 相乘得出的端点与按此顺序与 V、S 和 U 相乘得出的端点相同。
最后,我们构建球体动画的路径,首先在一个平面内旋转,然后用一个对角矩阵沿着轴拉伸坐标,最后执行 3D 旋转。
这个脚本用于记录不同矩阵等级的几个矩阵的动画。你可以在上面的视频中看到结果。
制作旅行推销员问题的动画
从制作模型动画中学到的经验
动画可以是一个强大的工具。用文字甚至图片来解释一个复杂的主题是一回事,但是动态的视觉效果有一种惊人的品质,可以将抽象的想法变得生动。这在诸如优化和机器学习等复杂的计算机科学领域尤其有用。
2018 年 10 月,我在 KotlinConf 做了一个关于优化和机器学习的演讲。在这几个例子中,有一个是旅行推销员问题(又名“TSP”)。这是一个如此有趣和迷人的问题,它经常作为优化和甚至机器学习算法的基准。然而,解释一些算法(如局部搜索和模拟退火)在没有视觉辅助的情况下不太直观。因此,我通过 TornadoFX 用 JavaFX 做了这个开源项目。
会议上和网上的许多人都对动画视觉效果感到惊讶,评论它看起来多么“集成”和流畅。事实是我一起破解了这个应用程序,JavaFX 在其中起了很大的作用。它替我处理了动画,所以我可以专注于算法本身。这就是我想在这篇文章中写的内容。
您可以在这里观看这个应用程序的视频演示(带有对 TSP 的详细解释)。我建议在继续阅读之前先看看这个。
这篇博文的重点将放在动画和它是如何实现的。要深入了解 TSP 以及如何解决它,请观看上面的视频。
该结构
为了建立这一点,让我们首先布局我们的视觉框架的结构。我将用 Kotlin 语言来表达这一点,并通过 TornadoFX 来利用 JavaFX。幸运的是,TornadoFX 没有隐藏或抑制 JavaFX 的任何功能,而是用富于表现力的 Kotlin DSL 来增强它。所以你可以用 Java,Kotlin,Scala,或者任何可以使用 JavaFX 的 JVM 语言来实现。
我在应用程序中要做的第一件事是声明一个Pane
,并在其中放置一个带有简单欧洲地图图像的ImageView
。然后从我的领域模型中,我将导入我的City
对象,并在相对于欧洲地图的 x 和 y 屏幕坐标上放置一个红色的Circle
。最后,我将从我的域中导入Edge
对象,其中每个对象都绑定到一个City
,并将每个对象绑定到一个Line
。每个Edge
代表两个城市之间的一个连接,它以同一个城市为起点和终点进行初始化。因此,Line
将通过停留在Circle
内作为一个小点来初始化。Line
也将绑定到其所属Edge
的startX
、endX
、startY
和endY
属性。
pane {
imageview(Image("europe.png")) {
fitHeight = 1000.0
fitWidth = 1000.0
CitiesAndDistances.cities.forEach { city ->
circle(city.x,city.y,10.0) {
fill = Color.RED
}
}
Model.edges.forEach { edge ->
line {
startXProperty().bind(edge.edgeStartX)
startYProperty().bind(edge.edgeStartY)
endXProperty().bind(edge.edgeEndX)
endYProperty().bind(edge.edgeEndY)
strokeWidth = 3.0
stroke = Color.RED
}
}
}
}
在这一点上,我应该有这样的渲染:
当我们制作动画时,我们将改变每个边的startX
、endX
、startY
和endY
属性。例如,当我们想要连接两个城市时,我可以更改endX
和endY
属性,使这条线延伸到另一个城市的坐标。
策划动画
有了这个结构,接下来我确实需要考虑一些事情。我应该实时制作算法的动画,还是将动画排队并使其可播放?我是想把算法做的每一件事都做成动画,还是过滤掉噪音,只把关键事件做成动画?
乍一看,这些决定似乎不重要,我甚至告诉自己“为什么不把一切都做成动画呢?”。当然,这很快就适得其反了,因为动画已经降低了算法的速度……而且在算法中制作无效事件的动画只会增加噪音。这也使得动画变得异常冗长和乏味。
你会问,什么是非生产性事件?正如视频中所解释的,该算法通过进行数千次随机Edge
交换来工作。当互换没有改善解决方案时(或者在模拟退火方法中抛硬币失败了),我会取消互换,把所有东西都放回去。我了解到最好不要将这些事件动画化,因为大多数迭代都是失败的交换,并且最好将成功动画化来显示进展,而不是每个迭代都包括失败。
我最终做的另一个调整是首先运行算法,然后用然后用动画显示结果。这样做的好处是能够重放结果,而不必再次运行整个过程。我在 JavaFX 库中需要的关键实用程序是SequentialTransition
,它允许我将动画排队并按顺序播放(而不是一次播放)。
然后我可以让我的算法给SequentialTransition
添加动画,当它完成后就可以播放了。我将每个算法(“贪婪”、“二次选择”、“模拟退火”等)存储为可枚举的,所以我给每个算法赋予了自己的SequentialTransition
。我还创建了一些方便的扩展函数,这样我就可以使用+=
操作符来添加动画。
enum class SearchStrategy {
RANDOM {
...
},
GREEDY {
...
},
REMOVE_OVERLAPS {
...
},
TWO_OPT {
...
},
SIMULATED_ANNEALING {
...
}
val animationQueue = SequentialTransition()
abstract fun execute()
}
// extension functions for SequentialTransition
operator fun SequentialTransition.plusAssign(timeline: Timeline) {
children += timeline }fun SequentialTransition.clear() = children.clear()operator fun SequentialTransition.plusAssign(
other:SequentialTransition) {
children.addAll(other)
}
执行路径遍历
在领域模型方面,我有最初属于一个City
的Edge
项目。然而,startCity
和endCity
可以变异,在每次变异时,Edge
都有一个animateChange()
函数返回一个延迟的Timeline
来播放那个变化。
但这是我最后做的有趣的设计决定。我创建了edgeStartX
、edgeStartY
、edgeEndX
和edgeEndY
,使其不与各自的startCity
和endCity
同步。相反,它们纯粹用于动画执行。当我决定在startCity
或endCity
中制作一个变化的动画时,我调用animateChange()
来创建一个Timeline
来制作坐标变化的动画。它将获取保存坐标值的每个 JavaFX 属性中的当前值,并通过在这段时间内逐渐增加/减少到指定值来制作动画(这是KeyFrame
的speed
)。
注意,虽然这个Timeline
不执行,但这取决于函数调用程序如何使用这个动画。
class Edge(city: City) {
val startCityProperty = SimpleObjectProperty(city)
var startCity by startCityProperty
val endCityProperty = SimpleObjectProperty(city)
var endCity by endCityProperty
val distance get() = CitiesAndDistances.distances[CityPair(startCity.id,
endCity.id)]?:0.0
// animated properties
val edgeStartX = SimpleDoubleProperty(startCity.x)
val edgeStartY = SimpleDoubleProperty(startCity.y)
val edgeEndX = SimpleDoubleProperty(startCity.x)
val edgeEndY = SimpleDoubleProperty(startCity.y)
fun animateChange() = timeline(play = false) {
keyframe(speed) {
keyvalue(edgeStartX, startCity?.x ?: 0.0)
keyvalue(edgeStartY, startCity?.y ?: 0.0)
keyvalue(edgeEndX, endCity?.x ?: 0.0)
keyvalue(edgeEndY, endCity?.y ?: 0.0)
keyvalue(Model.distanceProperty,
Model.totalDistance)
}
}
}
这个特殊的函数用于第一次将一个Edge
扩展到另一个城市,这发生在GREEDY
和RANDOM
算法中。将这些按顺序缝合在一起会产生一条光滑的路径,从而创建一个往返行程。下面是在RANDOM
算法中如何利用animateChange()
函数。请注意,当我遍历每个随机的City
时,我是如何分别通过它们的startcity
和endCity
连接每个连续的Edge
对的。然后我调用animateChange()
返回一个Timeline
并添加到animationQueue
中。
RANDOM {
override fun execute() {
animationQueue.clear()
val capturedCities = mutableSetOf<Int>()
val startingEdge = Model.edges.sample()
var edge = startingEdge
while(capturedCities.size <
CitiesAndDistances.cities.size) { capturedCities += edge.startCity.id
val nextRandom = Model.edges.asSequence()
.filter { it.startCity.id !in capturedCities }
.sampleOrNull()?:startingEdge
edge.endCity = nextRandom.startCity
animationQueue += edge.animateChange()
edge = nextRandom
}
Model.bestDistanceProperty.set(Model.totalDistance)
}
}
当绿色 play 按钮被按下时,我的 UI 可以调用animationQueue.play()
来执行更改。
执行交换
交换比制作路径遍历的动画要复杂一些。当TWO_OPT
或SIMULATED_ANNEALING
算法选择随机边并试图以某种方式交换它们的城市(顶点)时,有时会失败,有时会成功。如果交换中断了旅程,则会发生故障,并且会调用reverse()
函数。如果成功,可以调用一个animate()
函数并返回一个等待排队或执行的Timeline
。
class TwoSwap(val city1: City,
val city2: City,
val edge1: Edge,
val edge2: Edge
) {
fun execute() {
edge1.let {
sequenceOf(it.startCityProperty,it.endCityProperty)
}.first { it.get() == city1 }
.set(city2) edge2.let {
sequenceOf(it.startCityProperty,it.endCityProperty)
}.first { it.get() == city2 }
.set(city1)
}fun reverse() {
edge1.let {
sequenceOf(it.startCityProperty, it.endCityProperty)
}.first { it.get() == city2 }
.set(city1)
edge2.let {
sequenceOf(it.startCityProperty,it.endCityProperty)
}.first { it.get() == city1 }
.set(city2)}
fun animate() = timeline(play = false) {
keyframe(speed) {
sequenceOf(edge1,edge2).forEach {
keyvalue(it.edgeStartX, it.startCity?.x ?: 0.0)
keyvalue(it.edgeStartY, it.startCity?.y ?: 0.0)
keyvalue(it.edgeEndX, it.endCity?.x ?: 0.0)
keyvalue(it.edgeEndY, it.endCity?.y ?: 0.0)
}
}
keyframe(1.millis) {
sequenceOf(edge1,edge2).forEach {
keyvalue(Model.distanceProperty,
Model.totalDistance)
}
}
}
}
fun attemptTwoSwap(otherEdge: Edge): TwoSwap? {
val e1 = this
val e2 = otherEdge
val startCity1 = startCity
val endCity1 = endCity
val startCity2 = otherEdge.startCity
val endCity2 = otherEdge.endCity
return sequenceOf(
TwoSwap(startCity1, startCity2, e1, e2),
TwoSwap(endCity1, endCity2, e1, e2),
TwoSwap(startCity1, endCity2, e1, e2),
TwoSwap(endCity1, startCity2, e1, e2)
).filter {
it.edge1.startCity !in it.edge2.let {
setOf(it.startCity, it.endCity)
} && it.edge1.endCity !in it.edge2.let {
setOf(it.startCity, it.endCity)
}
}
.firstOrNull { swap ->
swap.execute()
val result = Model.tourMaintained
if (!result) {
swap.reverse()
}
result
}
}
这可用于TWO_OPT
和SIMULATED_ANNEALING
算法。请注意,对于这两种算法,我首先清理animationQueue
,执行RANDOM
算法并获取其所有动画,并将它们添加到该算法的动画中。对于TWO_OPT
,我尝试了 2000 次随机交换,并且只添加了增加旅程距离的动画。否则我调用reverse()
并且不对交换进行动画处理(就好像它从未发生过一样)。
TWO_OPT {
override fun execute() {
animationQueue.clear()
SearchStrategy.RANDOM.execute()
animationQueue += SearchStrategy.RANDOM.animationQueue
(1..2000).forEach { iteration ->
Model.edges.sampleDistinct(2).toList()
.let { it.first() to it.last() }
.also { (e1,e2) ->
val oldDistance = Model.totalDistance
e1.attemptTwoSwap(e2)?.also {
when {
oldDistance <= Model.totalDistance ->
it.reverse()
oldDistance > Model.totalDistance ->
animationQueue += it.animate()
}
}
}
}
Model.distanceProperty.set(Model.totalDistance)
Model.bestDistanceProperty.set(Model.totalDistance)
println("TWO-OPT BEST DISTANCE: ${Model.totalDistance}")
}
}
一旦算法完成,就在期望的SearchStrategy
可枚举上调用animationQueue.play()
并观看焰火。
Matplotlib 动画
使用 matplotlib 库创建一些有趣的动画。
Rain Simulation with Matplotlib
动画是展示一种现象的有趣方式。我们人类总是被动画和互动的图表所吸引,而不是静态的图表。动画在描述时间序列数据时更有意义,如多年来的股票价格、过去十年的气候变化、季节性和趋势,因为我们可以看到特定参数如何随时间变化。
概观
Matplotlib 是一个 Python 2D 绘图库,也是最流行的一个。大多数人从 Matplotlib 开始他们的数据可视化之旅。使用 matplotlib 可以很容易地生成曲线图、直方图、功率谱、条形图、误差图、散点图等。它还与 Pandas 和 Seaborn 等库无缝集成,以创建更复杂的可视化。
matplotlib 的一些好特性是:
- 它的设计类似 MATLAB,因此两者之间的切换相当容易。
- 包括许多渲染后端。
- 它几乎可以复制任何情节(只需一点努力)。
- 已经存在了十多年,因此,拥有庞大的用户群。
然而,也有一些领域 Matplotlib 并不突出,落后于其强大的对手。
- Matplotlib 有一个命令式 API,通常过于冗长。
- 有时不良的文体默认。
- 对 web 和交互式图形的支持较差。
- 对于大而复杂的数据,速度通常很慢。
作为复习,这里有一个 Matplotlib 备忘单,来自 Datacamp ,你可以通过它来复习你的基础知识。
动画片
Matplotlib 的animation
基类处理动画部分。它提供了一个构建动画功能的框架。有两个主要接口可以实现这一点:
[FuncAnimation](https://matplotlib.org/api/_as_gen/matplotlib.animation.FuncAnimation.html#matplotlib.animation.FuncAnimation)
通过反复调用函数*func*
制作动画。
[ArtistAnimation](https://matplotlib.org/api/_as_gen/matplotlib.animation.ArtistAnimation.html#matplotlib.animation.ArtistAnimation):
动画使用一组固定的Artist
对象。
然而,在这两者中, FuncAnimation 是最方便使用的一个。你可以在文档中读到更多关于它们的内容,因为我们将只关注FuncAnimation
工具。
要求
- 应该安装包括
numpy
和matplotlib
的模块。 - 要将动画保存为 mp4 或 gif,需要安装
[ffmpeg](https://www.ffmpeg.org/)
或[imagemagick](https://sourceforge.net/projects/imagemagick/files/)
。
一旦准备好了,我们就可以开始制作 Jupyter 笔记本中的第一个基本动画了。这篇文章的代码可以从相关的 Github 库 中获得,或者你可以点击下面的图片在我的活页夹中查看。
基本动画:运动正弦波
让我们使用FuncAnimation
创建一个正弦波在屏幕上移动的基本动画。动画的源代码取自 Matplotlib 动画教程。让我们首先看看输出,然后我们将分解代码,以了解在引擎盖下发生了什么。
- 在第(7–9)行中,我们简单地创建了一个图形窗口,图形中只有一个轴。然后我们创建我们的空行对象,它实际上是动画中要修改的对象。稍后将使用数据填充线对象。
- 在第(11–13)行,我们创建了
init
函数来制作动画。init 函数初始化数据并设置轴限值。 - 在第(14–18)行中,我们最终定义了动画函数,该函数将帧数(I)作为参数,并创建一个正弦波(或任何其他动画),该正弦波根据 I 的值移动。该函数在此返回一个已修改的绘图对象元组,该元组告诉动画框架应该对绘图的哪些部分进行动画处理。
- 在第 20 行中,我们创建了实际的动画对象。
blit
参数确保只重新绘制那些已经改变的图形。
这是在 Matplotlib 中创建动画背后的基本直觉。只要对代码稍加修改,就可以创建有趣的可视化效果。让我们来看看其中的一些
越来越大的线圈
类似地,在 GeeksforGeeks 也有一个很好的创建形状的例子。现在让我们在 matplotlib 的animation
类的帮助下,创建一个慢慢展开的动圈。代码与正弦波图非常相似,只是稍有调整。
实时更新图表
在绘制股票数据、传感器数据或任何其他与时间相关的数据等动态量时,实时更新图表非常方便。我们绘制了一个基础图,随着更多的数据输入系统,该图会自动更新。这个例子摘自send ex。一定要访问这个 youtube 频道,看一些很棒的教程。
让我们绘制一家假设公司一个月内的股价。
现在,打开终端并运行 python 文件。您将获得如下图所示的图表,该图表会自动更新如下:
这里的间隔是 1000 毫秒或 1 秒。
3D 绘图上的动画
创建 3D 图形是很常见的,但是如果我们可以将这些图形的视角制作成动画呢?这个想法是改变相机的视角,然后使用每一个结果图像来创建一个动画。在Python 图库有一个很好的部分专门讨论这个问题。
在笔记本所在的目录下创建一个名为 volcano 的文件夹。所有的图像都将存储在这个文件夹中,然后将在动画中使用。
这将在火山文件夹中创建多个 PNG 文件。现在,使用 ImageMagick 将它们转换成动画。打开“终端”并导航到 Volcano 文件夹,然后输入以下命令:
convert -delay 10 Volcano*.png animated_volcano.gif
使用赛璐珞模块的动画
赛璐珞是一个 Python 模块,简化了在 matplotlib 中创建动画的过程。这个库创建一个 matplotlib 图形,并从中创建一个Camera
。然后,它重复使用图形,并在每一帧创建后,用相机拍摄快照。最后,用所有捕获的帧创建一个动画。
装置
pip install celluloid
这里有几个使用赛璐珞模块的例子。
最小的
支线剧情
传说
包裹
动画有助于突出视觉化的某些特征,否则用静态图表很难传达。话虽如此,记住不必要的和过度的观想有时会使事情复杂化也是很重要的。数据可视化中的每一个特性都应该被明智地使用,以产生最佳效果。
Anime2vec:一个序列推荐系统
从 skipgram 模型中提取嵌入
Source: Exploiting Similarities among Languages for Machine Translation paper.
在这篇文章中,我将向你展示如何实现一个简单的 word2vec 或 Anything2vec 的变体,以便拥有一个基于项目的推荐系统。
让我们从我们的数据开始,为了创建一个推荐器,我们需要几个项目,一些用户和一个隐式或显式的**反馈。**在这里,我们有动漫,有用户,也有他们的评分。
我们将重新设计我们的数据,以便它可以被序列模型接收。为此,我们将创建一个数组列表来表示用户的观察列表。
print(data[:10])[array([39, 20, 2, 36, 13, 18, 73, 31, 78, 68, 80, 54, 62, 60, 64, 27, 52,
3, 84, 29, 17, 35, 12, 63, 8], dtype=int64),
array([73, 7, 51, 62, 33, 1, 35, 30, 10, 80, 2], dtype=int64),
array([28, 2, 61, 15, 89, 14, 26, 7, 1, 32, 57, 6], dtype=int64),
array([77, 88, 12, 8, 2, 14, 19, 10, 47, 6, 4], dtype=int64),
array([24, 1, 8, 88, 14, 12, 2, 6, 47, 10], dtype=int64),
array([54, 3, 33, 45, 35, 4, 19, 30, 6, 2, 10], dtype=int64),
array([52, 84, 50, 3, 14, 27, 64, 69, 80, 32, 4], dtype=int64),
array([25, 22, 1, 17, 46, 35, 80, 4, 47, 3, 26], dtype=int64),
array([77, 52, 74, 45, 17, 19, 8, 2, 88, 67, 5, 9, 11, 6, 1, 4, 14,
22, 47, 26, 29], dtype=int64),
array([38, 36, 83, 85, 31, 42, 53, 58, 13, 23, 56, 65, 39, 72, 37, 74, 70,
76, 7, 33, 66, 50, 54, 59, 64, 51, 49, 21, 5, 3, 45, 73, 24, 11,
89, 84, 12, 48, 71, 9, 2, 1, 14, 19, 17, 80, 6, 4, 26],
dtype=int64)]
然后我们将定义我们的 SkipGram 模型
直觉
给定一组顺序数据,SkipGram 模型将循环遍历序列的每个 项 ,并尝试使用上下文(固定窗口大小中当前项的每个邻居)来预测当前项。通过这个过程,我们将训练一个简单的神经网络,该网络有一个单独的隐藏层作为分类任务。输出是一个 softmax 层,它返回给定上下文的每个项目成为当前项目的概率分布。在训练过程中,每个权重都会更新,我们将提取隐藏层的权重,即 项向量 。在我们的例子中,简而言之,该算法将试图找到给定的上一部 k 和下一部 k 的理想动画。
然后我们将定义一个生成器函数来提供数据。
让我们实例化一些参数。
让我们训练我们的模型,提取一个形状的嵌入(len(unique_anime),200)。
最后,通过嵌入,我们可以提取一些项目-项目相似性。
Nearest to - Clannad:
Angel Beats ,
School Days,
Suzumiya Haruhi no Shoushitsu,
Suzumiya Haruhi no Yuuutsu 2009 ,
Code Geass Hangyaku no Lelouch R2,
Nearest to - Mononoke Hime:
Majo no Takkyuubin,
Sen to Chihiro no Kamikakushi,
Hotaru no Haka,
Samurai Champloo,
Howl no Ugoku Shiro,
Nearest to - Hajime no Ippo:
Hajime no Ippo New Challenger,
Great Teacher Onizuka,
Monster,
One Outs,
Hunter x Hunter 2011
为了获得一些见解,我们可以通过降维算法展示更多的嵌入,通过传递 2 个组件,我们可以在 2D 图中绘制部分相似性。
最后
在这篇文章中,我采用了一个基于动画的 skipgram 模型,这篇文章的结果仅来自于对一个样本的训练,并且是可完善的。代码可用[here](https://github.com/AlexWarembourg/Medium/blob/master/SkipGramModel.ipynb)
。为了改进这个模型,我们可以用丰富的数据训练我们的模型,然后创建一个用户配置文件,以便将我们基于项目的系统转换为定制系统,我将在另一篇文章中重点讨论这一部分。
5 个简单步骤中的关联矩阵注释热图
热图是数据的图形表示,其中数据值用颜色表示。也就是说,它使用颜色向读者传达一个价值。当您拥有大量数据时,这是一个很好的工具,可以帮助受众了解最重要的领域。
在本文中,我将指导您通过 5 个简单的步骤创建自己的带注释的关联矩阵热图。
- 输入数据
- 创建相关矩阵
- 设置遮罩以隐藏上面的三角形
- 在 Seaborn 创建热图
- 导出热图
您可以在我的 Jupyter 笔记本中找到这篇文章的代码,该笔记本位于这里。
1)导入数据
df = pd.read_csv(“Highway1.csv”, index_col = 0)
该高速公路事故数据集包含汽车事故率(以每百万英里事故数表示)以及几个设计变量。关于数据集的更多信息可以在这里找到。
2)创建相关矩阵
corr_matrix = df.corr()
我们用.corr
创建相关矩阵。注意,htype 列没有出现在这个矩阵中,因为它不是数字。我们需要虚拟化 htype 来计算相关性。
df_dummy = pd.get_dummies(df.htype)
df = pd.concat([df, df_dummy], axis = 1)
此外,请注意,相关矩阵的上半部分三角形与下半部分三角形对称。因此,我们的热图不需要显示整个矩阵。我们将在下一步隐藏上面的三角形。
3)设置蒙版隐藏上面的三角形
mask = np.zeros_like(corr_matrix, dtype=np.bool)
mask[np.triu_indices_from(mask)]= True
我们来分解一下上面的代码。np.zeros_like()
返回与给定数组具有相同形状和类型的零数组。通过传入相关矩阵,我们得到一个如下的零数组。
dtype=np.bool
参数覆盖了数据类型,所以我们的数组是一个布尔数组。
np.triu_indices_from(mask)
返回数组上部三角形的索引。
现在,我们将上面的三角形设置为真。
mask[np.triu_indices_from(mask)]= True
现在,我们有了一个可以用来生成热图的遮罩。
4)在 Seaborn 创建热图
f, ax = plt.subplots(figsize=(11, 15)) heatmap = sns.heatmap(corr_matrix,
mask = mask,
square = True,
linewidths = .5,
cmap = ’coolwarm’,
cbar_kws = {'shrink': .4,
‘ticks’ : [-1, -.5, 0, 0.5, 1]},
vmin = -1,
vmax = 1,
annot = True,
annot_kws = {“size”: 12})#add the column names as labels
ax.set_yticklabels(corr_matrix.columns, rotation = 0)
ax.set_xticklabels(corr_matrix.columns)sns.set_style({'xtick.bottom': True}, {'ytick.left': True})
为了创建我们的热图,我们传递步骤 3 中的关联矩阵和步骤 4 中创建的掩码,以及定制参数,使我们的热图看起来更好。如果您有兴趣了解每一行是做什么的,这里有参数的描述。
#Makes each cell square-shaped.
square = True,
#Set width of the lines that will divide each cell to .5
linewidths = .5,
#Map data values to the coolwarm color space
cmap = 'coolwarm',
#Shrink the legend size and label tick marks at [-1, -.5, 0, 0.5, 1]
cbar_kws = {'shrink': .4, ‘ticks’ : [-1, -.5, 0, 0.5, 1]},
#Set min value for color bar
vmin = -1,
#Set max value for color bar
vmax = 1,
#Turn on annotations for the correlation values
annot = True,
#Set annotations to size 12
annot_kws = {“size”: 12})
#Add column names to the x labels
ax.set_xticklabels(corr_matrix.columns)
#Add column names to the y labels and rotate text to 0 degrees
ax.set_yticklabels(corr_matrix.columns, rotation = 0)
#Show tickmarks on bottom and left of heatmap
sns.set_style({'xtick.bottom': True}, {'ytick.left': True})
5)导出热图
现在您已经有了热图,让我们将其导出。
heatmap.get_figure().savefig(‘heatmap.png’, bbox_inches=’tight’)
如果你发现你有一个非常大的热图不能正确导出,使用bbox_inches = ‘tight’
来防止你的图像被切掉。
感谢阅读!请在下面的评论中分享你用数据制作的热图。
如何增强 Matplotlib 图
Photo by Adeolu Eletu on Unsplash
眼睛是我们最重要的器官,因为我们通过视觉感知大约 80%的印象。毫不奇怪,可视化是我们收集和分析信息的最简单的方法。当谈到数据科学时,各种各样的图表帮助我们理解不同复杂性的问题。它们允许我们识别数据中的模式、关系和异常值。因此,无论我们想要分析什么数据,数据可视化都是至关重要的第一步。当使用 Python 时,M atplotlib 和相应的插件如 seaborn 是快速实现这一点的首选工具。
在本文中,我想向您展示一些技巧来增强和丰富您的 matplolib 数字。最重要的是,我为您提供了一种注释各种条形图的好方法。
感兴趣吗?所以让我们开始吧!
先决条件
按照这个例子,你需要 Python 3.7+和 M atplotlib 、 pandas 和 seaborn 。一如既往,我推荐使用poem来管理您的 Python 包和环境。你可以查看这篇文章了解如何设置它。作为一种快捷方式,我建议使用 pip 或 pipx 将其安装在您的机器上。
作为提醒,我们首先要从样本数据中创建条形图 图表而不需要进一步的样式化。*本文的目标是增强和丰富这些图表。*你可以在我的 GitHub 资源库 上找到所有的示例代码。
设置
首先,我们创建一个名为 nice-plots 的诗歌项目,在这里我们实现了示例并添加了必要的包
poetry new nice-plots
cd nice-plots
poetry add pandas matplotlib seaborn
touch nice_plots/bar_charts.py
现在我们有了一个独立的 Python 环境,安装了我们需要的所有东西。太好了,我们可以开始工作了!
数据
作为数据,我们使用著名的鸢尾花数据集。幸运的是,这是 seaborn 直接提供的。为了创建“有意义的”条形图,我首先使用 平均值 作为聚合函数,按照对数据集进行分组。根据这些数据,我创建了四个不同的条形图,分别是 垂直条形图 和 水平条形图 两个版本的 正常- 和 堆叠版本 。在代码中,这看起来像
***import** seaborn **as** sns
**import** os **from** dataclasses **import** dataclass
**import** pandas **as** pd
**import** matplotlib.pyplot as plt
**import** matplotlib
**from** typing **import** *sns.set()
**# BLOCK 1
# BLOCK 2
# BLOCK 4
# BLOCK 6**data = sns.load_dataset(**"iris"**).groupby(**'species'**).mean()
fig, axes = plt.subplots(2,2)
data.plot.bar(ax=axes[0][0])
data.plot.bar(stacked=True, ax=ax[0][1])
data.plot.barh(ax=axes[1][0])
data.plot.barh(stacked=True, ax=ax[1][1])
**# BLOCK 3
# BLOCK 5
# BLOCK 7**plt.show()*
我添加了注释 # BLOCK N ,其中我向前引用了下面的增强功能。我希望这不会太令人困惑。我还添加了稍后需要的所有导入。
由于您可能不会坐在电脑前,下面是最终的条形图
嗯,不太好,对吧?我想努力让它们变得更好是值得的。
提高
图形和字体大小
看这些图表时,第一件显而易见的事情是,与图中的其他部分相比,它们太小了。有几种方法可以改变这种情况。我更喜欢 从 pyplot 设置全局 rcParams。全局意味着它们适用于你创建的所有人物,而不仅仅是某个特定人物。要更改图形大小和某些字体大小,您只需
***# BLOCK 1
def** set_sizes(fig_size:*Tuple[int,int]*=(9, 6), *font_size:int*=10):
plt.rcParams["figure.figsize"] = fig_size
plt.rcParams["font.size"] = font_size
plt.rcParams["xtick.labelsize"] = font_size
plt.rcParams["ytick.labelsize"] = font_size
plt.rcParams["axes.labelsize"] = font_size
plt.rcParams["axes.titlesize"] = font_size
plt.rcParams["legend.fontsize"] = font_sizeset_sizes((12,8), 10)*
运行这个会产生
这已经比以前好多了。但仍有改进的余地。
旋转刻度标签
垂直文本,就像水平条形图的 x 刻度标签,对我来说没有吸引力。除此之外,这种竖排文字还浪费了大量的图形空间。为了解决这个问题,Matplotlib 提供了一种简单的方法来通过
***# BLOCK 2**
**def** rotate_xticks(ax: matplotlib.axes, degrees : *float* = 45):
ax.set_xticklabels(ax.get_xticklabels(), rotation=degrees)**# BLOCK 3**
rotate_xticks(ax=axes[0][0],0)
rotate_xticks(ax=axes[1][0],0)*
由于将 x 轴旋转 45 度是很常见的,所以我将其设为默认值。然而,在这个例子中,水平打印标签最有意义。可以通过将度设置为 0 来实现。
我的机器上产生的输出看起来像
注释条形图
条形图非常适合快速直观地了解不同组之间的比较情况。然而,我们可能不仅对相对比较感兴趣,还想知道相应的绝对值。我们可以通过用各自的值注释每个条来实现这两个目标。
为此,我创建了一个类 AnnotateBars ,这个类允许您在堆叠和非堆叠版本中注释垂直和水平条形图
***# BLOCK 4
#Alias types to reduce typing, no pun intended**
Patch = matplotlib.patches.Patch
PosVal = Tuple[float, Tuple[float, float]]
Axis = *matplotlib.axes.Axes
PosValFunc = Callable[[Patch], PosVal]*@dataclass
**class** AnnotateBars:
font_size: *int* = 10
color: *str* = "black"n_dec: *int* = 2 **def** horizontal(**self**, ax: *Axis,* centered=False):
**def** get_vals(p: Patch) -> PosVal:
value = p.get_width()
div = 2 **if** centered **else** 1
pos = (
p.get_x() + p.get_width() / div,
p.get_y() + p.get_height() / 2,
)
**return** value, pos
ha = "center" **if** centered **else** "left"
**self**._annotate(ax, get_vals, ha=ha, va="center") **def** vertical(**self**, ax: *Axis,* centered:*bool*=False):
**def** get_vals(p: Patch) -> PosVal:
value = p.get_height()
div = 2 **if** centered **else** 1
pos = (p.get_x() + p.get_width() / 2,
p.get_y() + p.get_height() / div
)
**return** value, pos
va = "center" **if** centered **else** "bottom"
self._annotate(ax, get_vals, ha="center", va=va) **def** _annotate(**self**, ax, func: *PosValFunc*, **kwargs):
cfg = {"color": **self**.color,
"fontsize": **self**.font_size, **kwargs}
**for** p **in** ax.patches:
value, pos = func(p)
ax.annotate(f"{value:.{**self**.n_dec}f}", pos, **cfg)*
Puh,代码很多但是不用担心,我指导你怎么用。
首先,您需要创建一个 AnnotateBars 的实例。您可以指定字体大小、文本颜色以及应该打印的小数位数。所有这些参数都有合理的默认值。
接下来,您需要调用垂直或水平*,这取决于您想要注释的条形图的类型。对于这些函数,您需要传递包含相应条形图的轴对象。此外,它们接受一个名为的附加参数,以为中心。这样,您就可以确定注释是打印在工具栏的中心还是顶部/右侧。当您使用堆积条形图时,这尤其有用。*
说得够多了,让我们利用这个类,用不同的配置来注释我们的四个条形图
***# BLOCK 5**
AnnotateBars().vertical(axes[0][0])
AnnotateBars(color="blue").vertical(axes[1][0], True)
AnnotateBars().horizontal(axes[0][1])
AnnotateBars(font_size=8, n_dec=1).horizontal(axes[1][1], True)*
这是结果输出图表
现在我们更聪明了,不仅知道关系,还知道绝对值。厉害!
顺便提一下,当我们在堆积条形图中有非常小的条形时,叠加值就成了一个问题。你可以从堆叠的 Setosa 图表中看到这一点。在这种情况下,你必须选择是接受还是要求不同的东西。
保存绘图
最后,当你创作出令人自豪的精彩情节时,你可能想与你的同事分享。为此,您必须以 PNG 这样的格式存储您的绘图,以便于分发。尽管将数字转储到图像的语法相当简单,但我很容易忘记它。这就是我使用这个助手函数的原因
***# BLOCK 6
def** save_figure(fig : *matplotlib.figure.Figure*, path : *str*):
folder = os.path.dirname(path)
**if** folder:
os.makedirs(folder, exist_ok=True)
fig.savefig(path, bbox_inches="tight")*
你只需要把它的一个 图对象 和 路径 传递到输出的图像文件中。如果您想要存储图像的文件夹不存在,该功能会自动为您创建一个。对我来说,这是非常方便的。让我们添加这最后一块代码,我们就完成了
***# BLOCK 7**
save_figure(fig, "./plots/medium/bar-charts.png")*
这就是我如何创造了你们之前看到的所有情节。我肯定是自己吃狗粮的:)!
包裹
在本文中,我向您展示了几个 Matplotlib 函数来增强您的绘图。最重要的是,我向您展示了一种注释各种条形图的好方法。
感谢您关注这篇文章。一如既往,如有任何问题、意见或建议,请随时联系我。密谋愉快!
命名实体识别中的标注者偏差和不完整标注(NER)
介绍 2019 年发表的两篇论文,涉及命名实体识别的不完全标注(NER)
Photo by Alvaro Reyes on Unsplash
我们知道深度学习(DL)的兴起离不开带注释的数据。换句话说,正是那些注释者让 DL 发展的如此之快。但他们也是人,他们有自己的注释习惯,也会犯错误。在本帖中,我将介绍一些有趣的论文。一篇论文是关于注释者的偏见,两篇论文是关于命名实体识别的不完整注释(NER)。
em NLP-2019/11-我们是对任务建模还是对注释器建模?自然语言理解数据集中标注者偏差的研究
这篇文章的出发点相当有趣。外包标注任务在业内很常见。我们可能关注注释的质量,但是很少关注注释者的偏见。注释者偏差意味着每个注释者都有自己标记数据的习惯,他们会给数据带来这样的偏差。本文表明注释者的偏见会影响模型的性能。如果在训练期间输入注释者标识符作为特征,模型将学习注释者的习惯并训练一个更健壮的模型。另一个有趣的发现是,模型不能很好地概括来自对训练集没有贡献的注释者的例子。具体来说,如果模型不能从训练集中学习注释者的习惯,它就不能很好地推广到测试集中。一个建议是,当我们注释数据集时,一个注释器应该同时注释训练集和测试集。
NAACL-2019/06-对命名实体识别的不完整注释进行更好的建模
注释者也是人,人也会犯错。注释数据不可能总是完美的。对于这种情况,我们必须找出如何更好地学习不完善的标注数据。这两篇论文是为 NER 任务学习这样的数据。
上图显示了不完整的注释示例。为了更好地对不完整标注建模,作者提出了一种新的损失函数。
为了对不完全标注建模,作者引入了一种新的概率分布 q 来表示所有可能的标注。
这个想法在上图的右下角。通过引入 q 分布,该模型可以考虑不完整标签的所有可能路径。
如何学习 q 分配?
作者提出了两种方法,硬方法和软方法。在硬方法中,所得的 q 分布是将概率 1 分配给单个完整标签序列的折叠分布,而在软方法中,每个可能的标签序列将得到某个概率分数。
EMNLP-2019/11-CrossWeigh:从不完善的注释中训练命名实体标记器
NER 存在两种标签错误:(1)测试集中的错误会干扰评估结果,甚至导致对模型性能的不准确评估;以及(2)训练集中的错误会损害 NER 模型训练。
来解决第一个问题。在本文中,他们纠正了测试集中的错误,以形成更清晰的基准,并开发了一个新颖的框架来处理训练集中的错误。他们纠正了 CoNLL03 NER 数据集的 5.38%标签错误测试句子。它表明,模型性能在更干净的测试数据集上有所提高。
为了解决第二个问题,他们提出了一个交叉权重框架来更好地处理标签错误。这个框架包含两个部分。
- 错误估计:通过交叉检查过程识别训练数据
中潜在的标签错误。 - 错误重新加权:在最终 NER 模型的训练过程中,它降低了这些实例的权重。交叉校验过程受 k 重交叉验证的启发;不同的是,在每个文件夹的训练数据中,它会删除包含该文件夹中出现的任何实体的数据。
查看我的其他帖子 中 同 一分类查看 !
GitHub:bramble Xu LinkedIn:徐亮 博客:bramble Xu
参考
- https://arxiv.org/pdf/1908.07898.pdf
- https://www.aclweb.org/anthology/N19-1079
- https://arxiv.org/pdf/1909.01441.pdf
公告:TensorFlow 2.0 已经到来!
几个月前,我提到了 TensorFlow 2.0 中将包含的一些激动人心的新功能。
你猜怎么着?今天(撰写本文时)TensorFlow 2.0 Alpha 预览包正式发布,并在官网更新了文档!
我对此感到非常兴奋和激动,迫不及待地想和你们分享这个!!
要了解 TensorFlow 以前的一些用例以及 TensorFlow 2.0 中的一些变化,请查看下面的短视频:
What’s New in TensorFlow 2.0 by Paige Bailey (Developer Advocate)
TensorFlow 2.0 中的一些新功能
即使 Tensorflow 2.0 仍处于预览版,但我们可以开始尝试 TensorFlow 1.x 之后期待已久的一些最酷的功能。
相信我。学习和使用 TensorFlow 2.0 的易用性会让你大吃一惊,尤其是对于初学者。
TensorFlow 2.0 专注于简单易用,拥有急切执行、直观的高级 API 和在任何平台上灵活构建模型等更新。
TensorFlow 2.0 中有多个变化,使 TensorFlow 用户更有效率。在下一节中,我将简要概述使 TensorFlow 2.0 使用起来更加直观和高效的两个主要变化。
要了解 TensorFlow 2.0 中有哪些内容,可以查看 TensorFlow 团队发布的中型文章,或者 TensorFlow 官方网站上的摘要。
或者,你也可以观看由 aurélien géRon(aurélien géRon)创建的这个视频(附在下面),他是用 Scikit-Learn 和 TensorFlow 进行机器学习的作者。我最喜欢视频的一点是,他除了提到 TensorFlow 2.0 的一些重大变化外,还对比了 Pytorch 和 TensorFlow 2.0 的区别。
TensorFlow 2.0 Changes by Aurélien Géron
1.急切的执行
- 默认情况下启用快速执行,TensorFlow 操作现在会立即被评估并返回它们的值,而不构建图形
- 由于没有计算图形要构建并在随后的会话中运行,所以很容易使用 print()或调试器检查结果
- 评估、打印和检查张量值不会中断计算梯度的流程
- TensorFlow 2.0 急切地执行(就像 Python 通常做的那样),在 2.0 中,图形和会话应该感觉像实现细节。
2.函数,而不是会话
让我们面对现实吧。
在用我们的张量流图进行任何计算之前,我们需要调用会话,这一直是我们头疼的问题。
TensorFlow 2.0 就是为了解决这个问题而诞生的。
因为session.run()
调用几乎类似于函数调用:您指定输入和要调用的函数,然后您得到一组输出。
在 TensorFlow 2.0 中,您可以使用一个名为tf.function()
的装饰器,这样您就可以自动定义一个 TensorFlow 函数,以 TensorFlow 操作图的形式运行计算,带有命名参数和显式返回值。
换句话说,您可以使用自然的 Python 语法编写图形代码,同时能够以简洁的方式编写渴望风格的代码,并使用 tf.function 将其作为张量流图形运行。
最后的想法
Wonderful Google office tour in Singapore
感谢您的阅读。
这么多激动人心又人性化的功能,真的很期待 TensorFlow 2.0 的最终版本。
同时,如果您有兴趣了解 TensorFlow 2.0 的最新发展,您可以随时订阅邮件列表或在此加入社区!
一如既往,如果您有任何问题或意见,请随时在下面留下您的反馈,或者您可以随时通过 LinkedIn 联系我。在那之前,下一篇文章再见!😄
关于作者
Admond Lee 目前是东南亚排名第一的商业银行 API 平台Staq—的联合创始人/首席技术官。
想要获得免费的每周数据科学和创业见解吗?
加入 Admond 的电子邮件简讯——Hustle Hub,每周他都会在那里分享可行的数据科学职业建议、错误&以及从创建他的初创公司 Staq 中学到的东西。
你可以在 LinkedIn 、 Medium 、 Twitter 、脸书上和他联系。
让每个人都能接触到数据科学。Admond 正在通过先进的社交分析和机器学习,利用可操作的见解帮助公司和数字营销机构实现营销投资回报。
www.admondlee.com](https://www.admondlee.com/)**
宣布现实项目
Sunrise from the International Space Station (Source)
用数据减少对世界的误解
当你放下报纸,关掉电视,决定看看关于这个世界的实际统计数据,而不是依赖别人告诉你的,奇怪而美妙的事情就会发生。当你意识到新闻一直灌输给你的世界观——世界正在走下坡路——是基于几乎每一个事实衡量标准的,与现实完全相反时,你脚下的地面开始移动。一旦我们看了关于财富、健康、人权和环境保护等主题的数据,我们别无选择,只能得出这样的结论在有记录的历史的大部分时间里,世界一直处于上升轨道,我们生活在人类文明有史以来最好的时代,而且这种改善没有停止的迹象。
在过去的五年里,随着我慢慢戒掉令人衰弱的新闻瘾,我经历了只能被描述为我大脑中世界观生成软件的更新。通过数万页书、百万个数据点、无数图表,我对世界是黑暗危险之地、人性是邪恶力量的看法发生了逆转。根据这些数据,世界的故事发生了逆转:人类的历史不是衰落的历史,而是持续到今天的逐渐上升的历史。
我一开始并没有打算成为一个乐观主义者,但长期积极的观点是基于事实审视世界的必然结论。
随着新的一年的开始,现在我已经吸收了足够多的独立数据来源可以确信 现实真的比我们想象的好 ,我开始了现实项目,这是一个数据驱动的努力,通过一系列的每周文章来呈现世界。声明的目标是**通过数据减少对世界的误解,**消除我们扭曲的世界观。如果在这个过程中,这些数据碰巧让你对人性有了更乐观的看法,那么就把它当作一个有益的副作用。
现实项目第一篇《消失的穷人》在这里。你可以在这里找到在现实项目中发表的所有文章。
现实项目基础
每周,我都会写一些关于我们世界不同方面的统计数据,这些数据是根据我最初最错误的观点挑选出来的。这些将涵盖从环境到民主到核武器的主题——如果人类参与其中并且有可靠的数据,它就属于这一范围,尽管重点通常是大局而不是日常细节。文章将会很短,内容丰富,每一条数据都必须经过独立消息来源的确认,才能成为现实项目。
首先,我想澄清的是,我并不声称自己垄断了真相。我知道数据会受到收集和报告数据的人的偏见的影响。然而,仅仅是我们可能被证明是错误的,并不是阻止我们努力获得真相的理由。科学,进而人类,通过收集数据、质疑理论和抛弃不再有数据支持的观点的过程而进步。我的信念从来不是一成不变的,当证据发生变化,或者当我发现数据与我现有的想法相矛盾时,它不会被压制而是被报告!
这个项目忠于一个想法:我们应该依靠证据——数据——而不是观点。现实项目与新闻的区别在于,当世界观与现实不符时,人们愿意推翻它们。
为什么这很重要
减少对世界的误解不仅仅是一个虚荣的项目。我以事实为基础的世界观已经让我的生活方式变得更好,我相信准确的世界观对社会至关重要。在这次世界观软件升级之前,我从来没有志愿或捐助过慈善事业,理由很简单:这个世界是一个可怕的地方,你必须自己照顾自己,总会有穷人/病人/受压迫者/饥饿者,所以试图改变是没有用的。
了解世界真实状况的一个最重要的影响是看到世界范围内的努力在减少贫困、扩大人权和根除疾病等领域取得了压倒性的成功。这些项目产生了影响,并证明人们一起行动可以在全球范围内减少有意识生命的痛苦。
事实表明,试图改善自己的邻里关系甚至整个世界 并非徒劳 。
这种认识触动了我的大脑:不花时间或金钱让世界变得更美好是不可原谅的。联合国、非营利组织,是的,甚至个人志愿者,都为人类的繁荣做出了贡献。你所做的每一点工作可能看起来无关紧要,但累积起来,直到我们生活在这样一个世界:自 1990 年以来,超过 10 亿人摆脱了极端贫困,接种疫苗的儿童人数超过 85%。
简而言之,正如发现大脑的可塑性让你意识到你可以更好地改变你的习惯(在人生的任何阶段),采取数据驱动的世界观意味着看到人类作为一个整体是有能力改善的。人们认为阅读新闻是公民的义务,但直到我开始阅读事实,我才开始以一种有意义的方式做出贡献。那些错误地认为世界正在变得更糟的人可能并不在那些真正让世界变得更好的人之列。
减少错误的资源
如果我们想采用基于事实的世界观,那么我们需要一些事实。幸运的是,对于那些希望减少对世界的误解的人来说,现在有比以往更多的数据资源(通常是免费的)。如果你想改变一个新闻习惯(证据表明我们并没有改掉坏习惯,而是用好习惯来代替它们)或者只是核实事实,那么就从这些开始:
- 我们的数据世界:可视化、原始数据、深度分析——你可以在这个综合资源中找到所有这些,它汇集了来自许多来源的数据。
- Gapminder . org:Gapminder 基金会是一个致力于通过帮助社会采用基于事实的世界观来改善世界的组织。至少,参加 Gapminder 测试来评估你的世界知识。
- 世界银行开放数据:如果你喜欢你的数据直接来源,这是获得它的地方。通过在线图表或下载数据来浏览数据。
- 汉斯·罗斯林著 《真实性:我们对世界误解的十个原因——以及为什么事情比你想象的好》 》(此处有很好的评论):一本通俗易懂的书,解释了我们为什么不了解这个世界,以及该如何应对。这是一种阅读的乐趣,也是一个基于事实的世界观的良好起点。
- 理性的乐观主义者:繁荣如何演变马特·里德利著:解释是什么推动了人类进步繁荣增加。
- 我们本性中更好的天使:为什么暴力减少了 史蒂芬·平克(好概述)和 现在的启蒙:理性、科学、人文主义和进步 史蒂芬·平克(精彩播客在此):这些都是宏伟的作品,将从根本上改变你对世界的看法。它们总共由 1000 多页经过严格研究的统计数据、图表和故事组成,不仅展示了为什么我们生活在人类历史上最和平的时代,也展示了为什么我们生活在人类文明最好的时代。
这些只是一小部分展示真实世界数据的网站和书籍。在项目过程中会提供额外的资源,我一直在寻找更多的建议。
结论
现实项目将是一周一次的系列文章,致力于呈现基于事实的世界图景,目的是用数据减少对世界的误解。每周我们将探索一个不同的主题,涵盖人类活动的整个范围。这个项目是从我的“启蒙”中产生的,当我开始把我的世界观建立在事实而不是通过新闻告诉我的基础上。虽然目的不是让你更乐观,但这可能是减少对世界的误解的不可避免的副作用。
有许多个人和机构在我的旅程中指导过我,并将在整个现实项目中与我们在一起。其中最突出的是令人难以置信的已故的汉斯·罗斯林,他把传播以数据为中心的世界观作为自己的目标,进而努力确保事情继续变得更好。他的工作由他的妻子和 Gapminder 基金会(T21)继续。我想给你们留下一段他的签名视频,这是我走出黑暗之路的许多灵感之一。
一如既往,我欢迎反馈,建设性的批评,并在整个现实项目中积极鼓励不同意见。我只要求反驳植根于数据。可以通过 Twitter @koehrsen_will 找到我。
宣布 tidyUSDA:一个用于处理 USDA 数据的 R 包
我很自豪地宣布一个 R 包的发布,它治愈了我个人的一个痒处:提取并使用美国农业部的数据,特别是来自 NASS 的快速统计数据。tidyUSDA 是实现这一目的的最小软件包。以下是从包装上剪下来的小片段,可以在这里找到:【https://github.com/bradlindblad/tidyUSDA】
为什么是 tidyUSDA?
为什么我们还需要另一个“整洁”的包装?为什么我必须安装这么多地理空间依赖项?
合理的问题。如果你使用美国农业部的数据,你会知道当你需要时,有时很难找到你需要的东西。大量的数据(2017 年农业普查包括大约 640 万个信息点[1])应该归功于美国农业部,因为这是一项巨大的组织任务。
目前,从以前的农业普查和农业调查中提取数据的最佳方式是通过快速统计门户网站,它允许您在交互式 gui 中应用过滤器,然后下载 CSV 文件。这对于大多数应用程序来说非常有效,但是 R 程序员讨厌以非编程方式提取数据,这就是 tidyUSDA 的用武之地。
编程数据直接从 Quick Stats 中提取 在其核心,tidyUSDA 是一个用于 Quick Stats 数据的 API,允许您在 R 会话中将相同的数据提取到 dataframe 中。
地理空间功能 tidyUSDA 还为您提供了在县或国家级别自动向快速统计数据添加简单要素列的选项。这允许您快速可视化快速统计数据,以便快速迭代或生成报告。
快速启动
首先,按照自述文件部分中的说明安装 tidyUSDA。请注意,如果您使用的是旧版本,您可能需要升级您的 R 版本。
接下来,调用 tidyUSDA 来确保所有东西都正确安装了。
library(tidyUSDA)
美国农业部通过 API 密钥控制对他们数据的访问。您可以按照此链接中的简要说明快速获得免费的 API 密钥。
现在来拉一些数据。
# Use keyring to store your api key
# key <- keyring::key_get("tidyusda")# Or hard code that thing
key <- 'abc-123'
在这一点上,最好使用实际的快速统计网站来挑选出你想要过滤的参数。通过这种方式,您可以确定数据将被返回。我希望看到使用 2017 年人口普查数据的州级运营计数细分。
在这一点上,它有助于了解哪些可能的值您可以输入到函数参数中。您可以使用内置数据集查看所有参数的这些可能输入。让我们来看看几个。
tidyUSDA::allCategory %>% head()
#> statisticcat_desc1 statisticcat_desc2
#> "ACCESSIBILITY" "ACCESSIBILITY, 5 YEAR AVG"
#> statisticcat_desc3 statisticcat_desc4
#> "ACCESSIBILITY, PREVIOUS YEAR" "ACTIVE GINS"
#> statisticcat_desc5 statisticcat_desc6
#> "ACTIVITY" "ACTIVITY, 5 YEAR AVG"
所以看起来对于类别字段只有六个可能的输入值。很高兴知道。
tidyUSDA::allGeogLevel %>% head()
#> agg_level_desc1 agg_level_desc2
#> "AGRICULTURAL DISTRICT" "AMERICAN INDIAN RESERVATION"
#> agg_level_desc3 agg_level_desc4
#> "COUNTY" "INTERNATIONAL"
#> agg_level_desc5 agg_level_desc6
#> "NATIONAL" "REGION : MULTI-STATE"
有许多不同的地理级别。目前只支持为县和州值提供几何图形。
现在我们对可以输入的内容有了一点了解,让我们使用主函数进行数据提取。
# Get count of operations with sales in 2017
ops.with.sales <- tidyUSDA::getQuickstat(sector=NULL, group=NULL, commodity=NULL, category=NULL, domain=NULL, county=NULL, key = key, program = 'CENSUS', data_item = 'CROP TOTALS - OPERATIONS WITH SALES', geographic_level = 'STATE', year = '2017', state = NULL, geometry = TRUE, lower48 = TRUE)
注意,我设置 geometry = TRUE 是为了包含我们绘图所需的几何特征,我设置 lower48 = TRUE 是为了排除夏威夷和阿拉斯加。
在这一点上,我有一个数据帧,其中包含相当多的数据字段。如果您设置 geometry = TRUE ,您将拥有更多列。快速统计的主要数据点将在“值”字段中。此时,您可以随意过滤数据框中您实际需要的字段。
现在让我们看看基本的 choropleth 图的数据是什么样的。
# Plot this data for each state
tidyUSDA::plotUSDA(df = ops.with.sales)
好的,哇,看起来这个国家所有的农场都在加利福尼亚。但是等一下,就陆地面积而言,加州很大,农场的相对规模相对较小,所以也许我们应该换个角度来看这个问题。首先,让我们清理数据帧,以便更容易处理。
mydata <- ops.with.sales[,c("NAME", "Value", "ALAND")]
我们选择州名、来自快速统计查询的值(运营次数)和“ALAND”,即以平方米为单位的土地面积。让我们修改我们的数据框架来计算每平方米的操作数,这样状态的大小就不会影响我们想要得到的结果。
mydata$ALAND <- as.numeric(mydata$ALAND)
mydata$modified_value <- mydata$Value / mydata$ALAND
这给了我们一堆没有意义的非常小的数字,但是对于我们的映射目的来说,它们就够了。
# tidyUSDA::plotUSDA(df = mydata, target_col = 'modified_value') tidyUSDA::plotUSDA(df = mydata, fill_by = 'modified_value')
啊,好多了。现在我们有了一个真实的曲线图,显示了每平方米的操作次数。看起来加州仍然是每块土地上拥有农场最多的州。
原载于 2019 年 9 月 29 日https://technistema.com。
全球自杀数据的异常
Mental Health Search Interest on Google Trends
每个心理健康意识日(10 月 10 日),在谷歌趋势上对“心理健康”的搜索兴趣都会出现一个峰值。然而,在刚刚过去的 10 月,人们对搜索的兴趣达到了有史以来的最高水平。美国的精神健康正在成为全球话题的一部分——部分是因为它的去污名化,但主要是因为它与技术(最显著的是社交媒体)、国内恐怖主义和毒瘾的相关性。
虽然 kagglers 和非营利组织已经对这一主题进行了大量预先存在的分析,但我希望使用时间序列异常检测技术,从不同的角度研究自杀统计数据集。
世卫组织自杀统计数据
世界卫生组织收集了每个国家的自杀人数,并提供了年龄、年份、国家、世代和性别之间的细分。自杀的数量经常被低估,即使如此,也只占自杀企图的很小一部分。然而,令人印象深刻的是,世卫组织能够以相同的粒度收集 100 多个国家的信息。
Albania subset of WHO suicide data
他们的数据集按照国家、年份、性别和年龄按行进行格式化。当我们将表格转换为每个国家和年份的总量时,我们发现每个国家的报告中存在一些差距。使用 missingno 库,我们可以很容易地可视化任何数据集的无效性。在下图中,每一列都是一个国家,白色方块表示该年的空值。
Nullity of suicide data per country
对于零死亡率高(超过 50%的年份缺失)的国家,我们将其从分析中剔除:这包括波斯尼亚和黑塞哥维那、塞浦路斯、多米尼克、斐济、基里巴斯、澳门、马尔代夫、蒙古等等。对于其余部分,我们将使用均值插补简单地填充缺失的数据(用该国 1985 年至 2016 年的平均自杀率替换空值)。
IMHE,全球疾病负担数据集
健康指标和评估研究所(IMHE)也发布了另一个自杀数据集,用于我们的数据世界:自杀报告。它提供了时间段(1990 年至 2017 年)的自杀指标,每个被调查国家都没有遗漏条目。有了这些完整的数据,他们能够创建一个随时间变化的地图可视化:
异常检测
既然我们已经查看了数据,我们将使用聚类算法来确定哪个国家从 1985 年到 2016 年的自杀率与所有其他国家相比有偏差。为此,我们将每个时间序列(每年 1 个数据点,每个国家 32 个)转换为每个国家 32 长度的向量。由此,我们可以使用基于密度的聚类方法(DBSCAN)来识别异常的时间序列。
通过在 IMHE 数据集上运行同样的聚类,我们得到了相似的结果。在世卫组织数据集中,格陵兰岛与丹麦王国合并在一起,因此它在 IMHE 数据集中仅作为一个单独的国家出现。
我们从 DBSCAN 中确定的异常值似乎属于两种趋势:
#1:正在改善的高自杀率国家。
格陵兰岛因拥有世界上最高的自杀率而臭名昭著,尤其是在当地居民中。2016 年, NPR 甚至录制了一集播客来进一步了解这一现象并与研究人员交流。虽然有许多因素,但许多研究文章将这一现象归因于因纽特人和丹麦文化之间的冲突。最近,《卫报》的一篇文章将精神健康危机与气候变化及其对他们生活方式的影响联系起来。数据显示的自杀率下降可能表明政府的干预正在起作用,尽管这与来自维基百科的这篇文章相矛盾(基于与 Tina Evaldsen 的对话)。
东欧的(俄罗斯、立陶宛等。)高自杀率主要是由 20 世纪 80 年代后半期开始的苏联解体造成的。事实上,自苏联解体以来,约有 80 万俄罗斯人自杀。这一时期的特点是经济危机和国家动乱。幸运的是,随着东欧经济的改善,自杀率也有所下降。
第二:最近自杀率的急剧上升。
苏里南+圭亚那是南美洲的邻国,拥有大量的印度斯坦(东印度)人口。在圭亚那,印度教徒占人口的 40%,但却占了 2010 年至 2013 年间自杀事件的 80%。关键因素可能包括精神疾病在印度文化中的污名、未治疗的创伤或 PTSD。在农村社区,个人可能会被孤立,独自应对心理健康问题。今天,努力的方向是打破围绕精神健康和家庭暴力的沉默。
在南韩,自杀是第四大死因。我最初认为自杀在年轻人中会很普遍,但数据显示它实际上主要影响老年人群。
根据 W ikipedia 的说法,这部分人口容易自杀,因为该国一半的老年人生活在贫困线以下。
再加上资金不足的老年人社会安全网,这可能会导致他们为了不成为家庭的经济负担而自杀,因为子女照顾父母的旧社会结构在 21 世纪已经基本消失了。
渗透率突破的异常检测和预测
在这项工作中,我们评估了各种方法,以预测何时有渗透突破的生化生产过程。自动编码器模型似乎很有前途,但应该与传统的统计过程控制指标相结合,以增加其鲁棒性。
同样,指数移动平均线(EMA)和长短期记忆(LSTM)提供了不同的结果。均线平滑时间序列数据,给出一段时间的趋势。这与 LSTM 相结合,使我们能够对未来的渗透值进行预测。
这个项目的完整代码可以在我的 github repo 中找到。
超滤过程
在食品和生化工业中,使用超滤(UF)是纯化目标产品的关键单元操作。进料进入 UF,根据膜的孔径,大于孔径的物质保留在浓缩液(产品)中,小于孔径的物质进入透过液(废物)。
Image courtesy www.crs-reprocessing.com
取决于应用,超滤膜的孔径大约为 10-100 千道尔顿。正因为如此,才有可能分离蛋白质,并通过除去水和盐来增加浓缩物流中蛋白质的浓度,留下感兴趣的产物。
Image courtesy www.synderfiltration.com
然而,随着时间的推移,膜降解,蛋白质进入渗透。由于渗透物是一种废物流,我们实际上是在扔掉我们想要出售的产品!!!
问题陈述
在本项目中,通过分析历史渗透值,目的是检测渗透物突破(渗透物中产品含量高)并在应该更换超滤膜时向工程师提供反馈,从而使生产过程更加可靠并减少产品损失。
换句话说,我们希望开发一种稳健的方法来预测何时出现渗透突破,并预测未来的价值。
方法学
比例渗透数据由位于卡伦堡德纳姆克的诺维信生产工厂提供。
然而,历史突破的时间记录是不一致的。因此,突破的特征将被创造。这四个特征是:
- mean_value_scaled :从 0-1 缩放的原始渗透值。
- ema_avg: 均线的平均值 _ 缩放值。
- **标准:**平均值的标准偏差 _ 缩放
- 目标:1.5σ以上的数值,分为正常(0)和突破(1)
为了检测渗透数据中的异常值(渗透突破),我们评估了:
为了预测未来渗透值,我们评估了:
- EMA 的使用
- 更加精密的T5【LSTM】型号
以下指标用于确定模型的表现如何:
- 对于异常/异常值检测,我们将使用准确度和 F 值。
- 对于预测,我们将使用测试集的均方误差(MSE)。
结果
异常值检测
PCA 分析显示正常渗透值(蓝色)和异常值(红色)之间存在差异。
可以看出,97.5 %的方差可以由前两个主成分(PC)来解释。此外,上面的双图显示了前两个主成分的负荷和得分。对于第一台 PC,所有的特性都是正面的。这是合理的,因为如果 mean_value_scaled 增加,则 std 也会随着 ema_avg 增加(过程中有更多变化)。当包含两个 PC 时, ema_avg 和 std 的相关性更强,但与 mean_value_scaled 的相关性较弱。
每个主成分是一个指向最高方差方向的单位向量(在考虑了早期主成分捕获的方差之后)。权重离零越远,主分量在相应特征的方向上就越多。如果两个特征具有相同符号的较大权重(都是正的或都是负的),那么一个特征的增加会与另一个特征的增加相关联。相比之下,具有不同符号的特征可能会表现出负相关性:一个变量的增加会导致另一个变量的减少。
同样,更先进的自动编码器型号能够在测试集上以 0.9715 的准确度分数、0.8438 的精确度分数和 0.7714 的 F 分数捕获何时出现突破。
黑线是我们认为有突破的目标和标志。蓝色和绿色的线是训练和测试数据集残差。阈值是自动内折器模型识别为异常值的值,在大多数情况下,该值与目标值一致。
以下是自动编码器模型的指标。当您处理一个类不平衡的数据集时,仅准确性并不能说明全部情况,就像这个数据集一样,其中正标签和负标签的数量存在显著差异。因此,我们也将使用 f1 分数,它是精度和召回的平衡。
上面的 autoencoder 给出了比朴素情况好得多的性能(朴素预测器:[准确度分数:0.0543,F 分数:0.0669])。详见我的代号。
预测渗透值
简单的 EMA 在跟踪趋势方面做得很好,但是通常滞后于新数据的移动,如下所示。这一具体分析表明,在使用 EMA 的一个主峰之后,大约需要一年时间才会出现另一个主峰(假设在导致渗透值降低的主峰之后更换膜)。
这实际上比制造商所说的更短,应该接近 2 年。这可能意味着我们需要评估膜的操作和清洁条件,看看这对膜的寿命是否有影响。
LSTM在预测实际渗透值方面表现不佳,但在预测 EMA(如下所示)和标准偏差方面表现出色。与仅使用 EMA 模型相比,这可以用于实现更长期的预测。
未来工作
在这一分析的基础上,还可以做更多的事情。我想到的几个例子是:
- 不更换膜的机会成本分析。
- LSTM 在预测未来值的均值和标准差方面做得很好。评估我们是否能重建未来价值的概率可能会很有趣。
- 应调查梯度的使用或数值之间的%变化。虽然移动平均是一个成功的特征,但考虑到渗透物样品不是以规则的间隔采集的,梯度可以提供更多有用的信息。
- 评估不同的模型参数:
—损失函数和优化器
—激活函数:relu
感谢您的阅读,希望这能激发您对该领域的兴趣。欢迎建设性的反馈,如果您发现代码有问题,您可以在 Github repo 中提出问题。
具有 Reserved.ai 的每日账单的异常检测
该项目
这是我和 Reserved.ai 合作的一个项目,用于异常检测。广义地说,异常检测是检测意外或异常数据的任何过程。在这种特殊情况下,Reserved.ai 帮助客户在 AWS 信用上获得更好的交易,因此可以访问他们的日常账单。在一个案例中,有人注意到账单金额突然增加,并提醒了客户。原来,他们的一名工程师部署了一项改变,无意中大幅增加了他们的日常账单。该项目的想法是自动化监控每日账单的过程,目的是提醒客户意外的费用。
数据
数据由各种客户的每日账单组成,如上所述。此外,成本可以按服务、地区或客户进行细分。首要任务是找出一种方法来自动检测整个账单的异常情况。
我注意到的第一件事是,对于许多客户来说,每个月的第一天都会有一个可预测的高峰。虽然这将被许多异常检测算法标记出来,但它并不是我们真正要寻找的那种异常。首先,这是可以预测的;第二,客户可以在每个月的第一天查看他们的账单(其他日子的账单只在内部跟踪),所以他们在第一天就已经知道账单了。目的是提醒他们注意他们不知道的指控。为了解决这个问题,我简单地从数据集中删除了每个月的第一天。这是剔除了每月第一天的相同数据。
我们可以看到,这消除了许多尖峰,但一些仍然存在。这样一来,是时候尝试一些异常检测算法了。
模型
异常检测有许多算法。我从最简单的开始:为基本的统计测量应用阈值。我的方法是基于一个叫做 SundaySky 的开源成本异常检测器。它查看过去 14 天的账单数据,并计算平均值和标准差。然后,它检查是否满足三个阈值:相对阈值,检查它是否至少是前几天平均成本的 1.25 倍;标准差阈值,寻找比平均值高至少 3.5 倍的标准差;和一个绝对阈值,寻找高于 10 美元的成本。
正如文档所解释的:
我们发现,同时使用所有 3 个阈值可以获得最佳结果,每个阈值都有自己的原因:
相对阈值过滤掉无关紧要的异常。
标准差阈值过滤掉具有正常不同日使用量(高日/低日)的服务常规使用量。
绝对阈值阻止我们得到关于廉价异常的通知,这将导致无行动。
SundaySky repo 中的大部分代码处理从数据库中存储和检索数据。算法的核心实际上非常简单,所以我发现自己写出来是最简单的。
当我用建议的缺省值测试它时,发现要求增加至少 3.5 个标准差太多了:它没有标出一个点。然而,完全取消标准偏差要求导致它标记了太多的点。1.5 标准差的要求被证明是一个很好的中间立场,通过了突出客户可能关注的点的眼球测试。下图中,检测到的异常点以红色突出显示。
我本来打算在转向更高级的模型之前使用这个简单的方法作为基线,但是结果证明它表现得足够好,以至于 CEO 决定按原样将其投入生产。有时候简单是最好的!
履行
唯一的另一个调整是添加一些代码,从服务、地区和帐户方面找出增长的主要原因。准备就绪后,我们将系统投入生产。当算法检测到一个异常点时,它会向 Reserved.ai 的客户服务代表发送一个松弛警报。然后,那个人会查看成本历史和主要原因,并决定是否将其传递给客户。到目前为止,这已经导致几个客户被警告他们的日常账单会有意想不到的增加。
感谢 Aran Khanna 和 Reserved.ai 对这个项目的帮助。
该项目的代码可在这里获得。
虚拟异常检测
Photo credit: Unsplash
单变量和多变量数据的无监督异常检测。
异常检测 是识别数据集中与常态不同的意外项目或事件的过程。异常检测通常应用于未标记的数据,称为无监督异常检测。异常检测有两个基本假设:
- 数据中很少出现异常。
- 他们的特征明显不同于正常情况。
单变量异常检测
在我们开始多元异常检测之前,我认为有必要先看一个简单的单变量异常检测方法的例子,在这个例子中,我们从单个特征空间中的值分布中检测异常值。
我们正在使用超级商店销售数据集,可以从这里下载,我们将分别找出与预期行为不符的销售和利润模式。也就是说,一次发现一个变量的异常值。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib
from sklearn.ensemble import IsolationForest
销售的分布
df = pd.read_excel("Superstore.xls")
df['Sales'].describe()
Figure 1
plt.scatter(range(df.shape[0]), np.sort(df['Sales'].values))
plt.xlabel('index')
plt.ylabel('Sales')
plt.title("Sales distribution")
sns.despine()
Figure 2
sns.distplot(df['Sales'])
plt.title("Distribution of Sales")
sns.despine()
Figure 3
print("Skewness: %f" % df['Sales'].skew())
print("Kurtosis: %f" % df['Sales'].kurt())
超市的销售分布远不是正态分布,它有一个正的细长尾巴,分布的质量集中在图的左边。并且尾部销售分布远远超过正态分布的尾部。
在分布的右侧有一个数据出现概率低的区域。
利润分配
df['Profit'].describe()
Figure 4
plt.scatter(range(df.shape[0]), np.sort(df['Profit'].values))
plt.xlabel('index')
plt.ylabel('Profit')
plt.title("Profit distribution")
sns.despine()
Figure 5
sns.distplot(df['Profit'])
plt.title("Distribution of Profit")
sns.despine()
Figure 6
print("Skewness: %f" % df['Profit'].skew())
print("Kurtosis: %f" % df['Profit'].kurt())
超市的利润分布既有正尾也有负尾。但是,正尾比负尾长。所以分布是正偏态的,数据是重尾的或者大量的异常值。
有两个区域数据出现的概率很低:一个在分布的右侧,另一个在左侧。
销售单变量异常检测
Isolation Forest 是一种检测异常值的算法,它使用 Isolation Forest 算法返回每个样本的异常值,该算法基于异常值是少量且不同的数据点这一事实。隔离林是基于树的模型。在这些树中,通过首先随机选择一个特征,然后在所选特征的最小值和最大值之间选择一个随机分割值来创建分区。
以下过程显示了 IsolationForest 在 Susperstore 销售案例中的表现,该算法是在 Sklearn 中实现的,代码主要是从本教程中借用的
- 使用销售数据训练 IsolationForest。
- 将销售额存储在 N umPy 数组中,以便稍后在我们的模型中使用。
- 计算每个观察的异常分数。输入样本的异常得分计算为森林中树木的平均异常得分。
- 将每个观察值分类为异常值或非异常值。
- 可视化突出显示了异常值所在的区域。
sales_IsolationForest.py
Figure 7
根据上述结果和可视化,似乎超过 1000 的销售额肯定会被视为异常值。
目测调查一个异常情况
df.iloc[10]
Figure 8
这一购买对我来说似乎很正常,只是与数据中的其他订单相比,它的销售额更大。
利润的单变量异常检测
- 使用利润变量训练 IsolationForest。
- 将利润存储在 N umPy 数组中,以便稍后在我们的模型中使用。
- 计算每个观察的异常分数。输入样本的异常得分计算为森林中树木的平均异常得分。
- 将每个观察值分类为异常值或非异常值。
- 可视化突出显示了异常值所在的区域。
profit_IsolationForest.py
Figure 9
目测调查一些异常情况
根据上述结果和可视化,低于-100 或超过 100 的利润似乎会被视为异常值,让我们直观地检查由我们的模型确定的每个示例,看看它们是否有意义。
df.iloc[3]
Figure 10
不言而喻,任何负利润都是异常现象,应该进一步调查
df.iloc[1]
Figure 11
我们的模型确定这个利润很高的订单是异常的。但是,当我们调查这个订单时,它可能只是一个利润相对较高的产品。
以上两个可视化显示了异常分数,并突出显示了异常值所在的区域。正如预期的那样,异常分数反映了基础分布的形状,异常值区域对应于低概率区域。
然而,单变量分析只能让我们到此为止。我们可能会意识到,由我们的模型确定的这些异常中的一些并不是我们预期的异常。当我们的数据是多维的,而不是单变量的时,检测异常的方法变得更加计算密集和数学复杂。
多元异常检测
由于我们生活的世界的复杂性,我们最终做的大多数分析都是多变量的。在多变量异常检测中,异常值是至少两个变量的组合异常值。
因此,使用销售和利润变量,我们将基于几个模型构建一个无监督的多元异常检测方法。
我们使用的是 PyOD ,这是一个 Python 库,用于检测多元数据中的异常。这个库是由赵月开发的。
销售和利润
当我们做生意时,我们期望销售额和利润是正相关的。如果一些销售数据点和利润数据点不是正相关的,它们将被认为是异常值,需要进一步调查。
**sns.regplot(x="Sales", y="Profit", data=df)
sns.despine();**
Figure 12
从上面的相关图中,我们可以看到部分数据点是极低值、极高值等明显的异常值。
基于聚类的局部异常因子
CBLOF 根据基于聚类的本地异常值因子计算异常值分数。异常分数是通过每个实例到其聚类中心的距离乘以属于其聚类的实例来计算的。 PyOD 库包含CBL of 实现。
- 将销售额和利润缩小到零和一之间。
- 根据试验和最佳猜测,将异常值部分任意设置为 1%。
- 将数据拟合到 CBLOF 模型并预测结果。
- 使用阈值来考虑数据点是在内还是在外。
- 使用决策函数计算每个点的异常分数。
CBLOF.py
Figure 13
基于直方图的异常检测(HBOS)
HBOS 假设特征独立,并通过构建直方图来计算异常程度。在多变量异常检测中,可以计算每个单一特征的直方图,单独评分,最后合并。使用 PyOD 库时,代码与 CBLOF 非常相似。
HBOS.py
Figure 14
隔离森林
隔离林在原理上类似于随机林,建立在决策树的基础上。隔离林通过随机选择一个要素,然后随机选择所选要素的最大值和最小值之间的分割值来隔离观察值。
PyOD 隔离林模块是具有更多功能的 Scikit-learn 隔离林的包装器。
IsolationForest.Py
Figure 15
K -最近邻(KNN)
KNN 是异常检测中最简单的方法之一。对于一个数据点,其到第 k 个最近邻的距离可以被视为异常值分数。
KNN.py
Figure 16
上述四种算法预测的异常情况差别不大。
目测调查一些异常情况
我们可能希望调查由我们的模型确定的每个异常值,例如,让我们详细查看由 KNN 确定的两个异常值,并尝试理解是什么使它们异常。
**df.iloc[1995]**
Figure 17
对于这个特殊的订单,客户购买了 5 件产品,总价为 294.62 英镑,利润低于-766 英镑,折扣为 80%。看起来像是通关。我们应该意识到我们销售的每件产品的损失。
**df.iloc[9649]**
Figure 18
对于这次购买,在我看来,4.7%左右的利润太小,模型确定该订单是异常的。
**df.iloc[9270]**
Figure 19
对于上面的订单,一个客户购买了总价为 4305 的 6 件产品,在打了 20%的折扣后,我们仍然获得了超过 33%的利润。我们希望有更多这样的异常现象。
Jupyter 笔记本以上分析可以在 Github 上找到。享受这周剩下的时光。
图像中的异常检测
用卷积神经网络对异常进行分类和个性化
Photo by mahdis mousavi on Unsplash
在机器学习中处理异常检测任务是正常的。数据科学家经常会遇到一些问题,他们必须显示、解释和预测异常。
我还发表了一篇关于时间序列的异常检测的帖子,其中我研究了内部系统行为,并提供了未来的异常预测。
在这篇文章中,我试图解决一个不同的挑战。我改变感兴趣的领域:从时间序列转换到图像。给定一幅图像,我们想要达到双重目的:预测异常的存在并对其进行个性化,给出结果的彩色表示。
数据集
我从网上得到的数据:裂缝数据集包含墙壁裂缝的图像。一半的图像显示了新的和未被破坏的墙体;其余部分显示了不同尺寸和类型的裂缝。
正如你从下面的样本中看到的,我们的数据显示了不同类型的墙体裂缝,其中一些对我来说也不容易识别。
Examples of Crack and No Crack
模型
我们希望建立一个机器学习模型,它能够对墙壁图像进行分类,同时检测出异常所在的位置。为了达到这个双重目的,最有效的方法是建立一个强分类器。它将能够读取我们的输入图像,并将其分类为“受损”或“未受损”。在最后一步,我们将利用我们的分类器学习的知识来提取有用的信息,这将有助于我们检测哪里有异常。
但是让我们按顺序进行,开始组装我们的神经网络…
对于这种任务,我选择了计算机视觉的银弹,忠诚 VGG16。我们装载并改造了 VGG16 列车。这在 Keras 中很容易做到,只需要几行代码。
vgg_conv = vgg16.VGG16(weights='imagenet', include_top=False, input_shape = (224, 224, 3))for layer in vgg_conv.layers[:-8]:
layer.trainable = False
具体来说,我们引入了 VGG 架构,允许训练最后两个卷积块。这将允许我们的模型专门处理我们的分类任务。为此,我们还排除了原始模型的顶层,用另一个结构替换它们。
x = vgg_conv.output
x = GlobalAveragePooling2D()(x)
x = Dense(2, activation="softmax")(x)
model = Model(vgg_conv.input, x)model.compile(loss = "categorical_crossentropy", optimizer = optimizers.SGD(lr=0.0001, momentum=0.9), metrics=["accuracy"])
在分类阶段,GlobalAveragePooling 图层通过取每个要素地图的平均值来减小前一图层的大小。这种选择,加上中间致密层的省略使用,允许避免过度拟合。
如果你有一个图形处理器,训练是简单和容易的。COLAB 或 Kaggle 给了我们加速这一进程所需的武器。我们还使用了一个由 Keras 提供的简单的数据生成器来增强图像。
最终我们能够做到 0.90 的整体准确率,还不错!
定位异常
现在,随着我们的模型被训练,我们使用来提取所有有用的信息,这些信息允许我们在墙壁图像中显示裂缝。我们试图用热图表示法让这个过程变得简单易懂。
我们需要的有用信息位于顶部。特别是,我们可以访问:
- 卷积层:我们在 VGG 结构中走得更高,网络创造了更重要的特征。我们已经选择了最后一个卷积层(block 5 _ con v3’),并在这里剪切我们的分类模型。我们重新创建了一个中间模型,给定原始图像作为输入,输出相关的激活图。考虑到维度,我们的中间模型增加了通道(新特征)并减少了初始图像的维度(高度和宽度)。
- 最终密集层:对于每个感兴趣的类,我们需要这些权重,它们负责提供分类的最终结果。
有了这些压缩物体在手,我们就有了定位裂缝的所有知识。我们希望将它们“画”在原始图像上,以使结果易于理解和观看。在 python 中,“解压缩”这些信息很容易:我们只需进行双线性上采样来调整每个激活图的大小,并计算点积。
这种魔力可以通过执行一个简单的函数来实现:
def plot_activation(img): pred = model.predict(img[np.newaxis,:,:,:])
pred_class = np.argmax(pred) weights = model.layers[-1].get_weights()[0]
class_weights = weights[:, pred_class] intermediate = Model(model.input,
model.get_layer("block5_conv3").output)
conv_output = intermediate.predict(img[np.newaxis,:,:,:])
conv_output = np.squeeze(conv_output) h = int(img.shape[0]/conv_output.shape[0])
w = int(img.shape[1]/conv_output.shape[1]) act_maps = sp.ndimage.zoom(conv_output, (h, w, 1), order=1)
out = np.dot(act_maps.reshape((img.shape[0]*img.shape[1],512)),
class_weights).reshape(img.shape[0],img.shape[1]) plt.imshow(img.astype('float32').reshape(img.shape[0],
img.shape[1],3))
plt.imshow(out, cmap='jet', alpha=0.35)
plt.title('Crack' if pred_class == 1 else 'No Crack')
我在下图中显示了结果,我在分类为裂纹的测试图像上绘制了裂纹热图。我们可以看到,热图能够很好地概括和指出含有裂缝的墙体。
Show anomalies in Crack images
摘要
在这篇文章中,我们提出了一个用于异常识别和定位的机器学习解决方案。所有这些功能都可以通过实现单个分类模型来实现。在训练过程中,我们的神经网络获取所有相关信息,使其能够进行分类操作。在这一阶段之后,我们已经组装了最终的碎片,这些碎片告诉我们图像中的裂缝在哪里,不需要额外的工作!
保持联系: Linkedin
火星表面异常探测
The University of Arizona — Views of MSL Hardware 12 Days after Landing — Image Source
大海捞针!
我得到了这个极好的机会,通过奥姆德纳社区参与“火星表面异常探测”项目。这个项目的目标是探测火星表面由非地球人造物品引起的异常现象,如火星着陆器、火星车等的碎片。
最近,寻找所谓的“技术签名”——提供过去或现在外星技术的科学证据的可测量的属性或效果——获得了新的兴趣。美国宇航局于 2018 年 9 月在德克萨斯州休斯顿的月球和行星研究所举办了一场“技术签名”研讨会,以了解更多关于“技术签名”搜索的当前领域和现状,以及美国宇航局未来可能在这些搜索中发挥的作用。这个研究领域中的一个领域是寻找太阳系中的非地球人造物品。这个人工智能挑战赛旨在为行星科学家开发“人工智能工具箱”,以帮助识别非地球文物。
这个人工智能挑战有多个挑战,从数据集开始。
数据集面临的挑战
探测火星表面的异常没有什么独特的挑战。除了数据收集和没有预定义数据集的挑战,最初的挑战是火星表面本身非常多样化。火星表面的不同部分看起来与其他部分完全不同。同一个表面在不同的季节看起来差别很大。不同火星表面的少量样本:
NASA — Jamming with the ‘Spiders’ from Mars — Image Source, Sciencealert — Gorgeous New Photos of Mars — Image Source, Sinc — Fotografían las capas del Polo Norte marciano — Image Source
NASA — Squiggles in Hellas Planitia — Image Source, NASA — Radial Channels Carved by Dry Ice — Image Source
因此,该算法应该足够通用,能够检测所有这些不同表面中的异常。第二个挑战是关于数据不平衡,火星表面大约有 1.45 亿平方公里,在这个表面上,我们有几个 100 个不超过几十英尺的异常。人们可以考虑使用数据扩充来克服不平衡,但不同地形中产生的异常仍然会有所不同,我们没有跨不同地形的标记数据。
下一个挑战是数据总量已经被异常污染。通常的异常检测算法通过在正常/通常人群样本上训练 AI 模型来工作,并且该模型用于预测目标数据偏离正常/通常人群样本多少。这里不能应用这种方法。
创建数据集
我在这次演习中使用的数据集是 https://www.uahirise.org/ESP_028401_1755登陆后 12 天的 MSL 硬件图像。在这幅图像中,来自“亚利桑那大学”的人们对硬件在火星表面制造异常的表面图像进行了注释。这似乎是一个用于初步评估的完美数据集。
The University of Arizona — Views of MSL Hardware 12 Days after Landing — Image Source
The University of Arizona — Views of MSL Hardware 12 Days after Landing — Image Source
完整的 HiRISE 图像大小约为 550MB,分辨率为 25516x45788 像素。左边是完整 HiRISE 图像的压缩浏览图像。这种异常在这幅图像中几乎看不到。即使在全分辨率图像中,也很难发现异常,除非人们确切地知道在哪里看。
为了从这个图像中创建一个数据集,我将这个 25516x45788 像素的图像分割成 256x256 像素的小图像块,跨度为 256 像素。正如你所看到的,这张图片周围有黑边,黑边对人工智能模型来说并不好。为了减少预处理这张图片的工作量,我去除了图片边缘的图片块,使用了图片中心的图片块。这产生了一个大约有 10K 图像的数据集。在这些 10K 图像中,有 6 个异常分布在 50 幅图像中。
以下是数据集中出现的 6 个异常的样本图像:
The University of Arizona — JP2 Black and White — [Image Source](http://Black and white)
算法背后的直觉
主要动机是利用数据集中的挑战,并应用出版物https://arxiv.org/abs/1811.06861中提到的“使用基于深度学习的图像完成的异常检测”方法
该想法是使用深度卷积神经网络来逐块完成表面图像,目的是检测表面上的异常。本文中使用的方法是专门在正常数据上训练模型,屏蔽/切除中心 32×32 像素,并且训练模型以重建屏蔽区域。为了检测异常,查询图像的中心 32×32 像素被屏蔽,并且该模型用于重建被屏蔽区域的无故障克隆。通过用生成的区域减去相应的查询区域,获得逐像素的异常分数,然后使用该分数来检测异常。
betergevondenworden.nl — Image Source
在我们的数据集中,由于与正常图像相比,异常预计非常少,因此我们将使用整个数据集来训练模型。然后,使用经过训练的模型来计算同一数据集的异常分数。由于该模型将在正常图像上进行推广,因此预计它将给出异常的高异常分数。
让我们看看这种方法能否在 10K 图像的大海里找到这 6 根针。
前馈生成 DCNN
图像完成任务通常旨在以最自然的方式完成图像的缺失区域。除了语义上有意义之外,修复还必须看起来尽可能真实。出于这个原因,前馈修复 DCNNs 通常与对抗网络联合训练。敌对网络的目标是区分真假图像。相反,生成模型必须通过生成逼真的图像来增加敌对网络的错误率。虽然这种额外的不利损失确实使修补看起来更真实,但它对像素匹配图像的丢失部分没有积极的影响。用联合损失函数进行训练甚至会增加像素重建误差,这对于异常检测来说是不希望的行为。为此,本文采用仅用重构损失训练的前馈生成型 DCNN。
模型定义
arxiv.org — Image Source
如上图所示,DCNN 网络由 17 层组成。在第三层之后,特征图的分辨率通过步进卷积减半。为了增加输出神经元的感受野,使用了一系列扩张的回旋(第 7-10 层)。在第 13 层,通过双线性重定标以及随后的卷积来执行放大回到输入大小。镜像填充用于所有卷积层。此外,使用指数线性单位(ELU)激活函数。
损失函数
用 L1 重构损失训练网络。由二进制掩码 M 定义的 32×32 的中心区域与剩余区域的权重不同。X 是要检查的图像块,用下面的损失函数训练网络:
arxiv.org — Image Source
培养
256×256 的图像块尺寸被调整为 128×128,并且图像完成网络被馈送以 128×128 尺寸的图像补片。由于遮蔽了大小为 32×32 的中心区域,补丁被破坏。已知和未知图像内容之间的这种大比例为网络提供了更多的语义信息来完成中心区域。在通过网络重建受损图像之后,通过上述损失函数计算重建图像和查询图像之间的逐像素绝对差作为损失值。该模型被训练了 200 个历元。
异常分数
对于异常检测,仅使用该绝对差图像的 24×24 中心区域。其中缺陷出现在靠近切出的 32×32 中心区域的边界的图像补片,神经网络似乎生成边界缺陷的局部延续。通过仅考虑 24x24 中心区域,这些不想要的延续大部分被排除。
初步结果
在对模型进行 200 个时期的训练之后,该模式再次用于预测同一数据集的异常分数。由此产生的异常分数分布如下所示:
列出与样本平均值相差 3 个标准偏差的图像,可以得到 99 个图像文件。99 出 10K 的图像还不错。通过将这些文件名与 99 幅图像中的异常图像块进行交叉引用,该模型仅检测到两种异常类别:“降落伞”和“下降阶段-坠毁地点”。它漏掉了“隔热屏”,“2-新点-点 1”,“2-新点-点 2”,“好奇号-漫游者”。在 99 个图像中,只有 7 个图像是异常的。虽然不是一个好结果!
即兴创作
在仔细分析没有检测到的异常图像时,发现遗漏的异常图像在图像的侧面具有异常,即不在图像的 32×32 中心。由于 DCNN 模型被训练来重建中心 32×32 像素,以 16 的步幅分割图像块将确保异常将占据一些图像块的中心 32×32 部分。
因此,用步长 16 切割的图像块重新创建了初始数据集。不是在这个新数据集上再次重新训练模型,而是使用先前训练的模型来预测这个步幅 16 数据集上的异常分数。由此产生的异常分数分布如下所示:
我们可以清楚地看到在分布图的最右边有一个图像数量的峰值。列出与样本平均值相差 3 个标准偏差的图像,得到 705 个图像文件。通过将这些文件名与异常图像块进行交叉引用,发现所有 705 个文件都属于四种异常类型“隔热层”、“降落伞”、“下降阶段-坠毁地点”、“好奇号-漫游车”。零误报。瞧,六根针中有四根找到了!
这种方法仍然遗漏了“2-新点-点 2”、“2-新点-点 1”异常。如果我们观察这些异常,这些异常非常小,并且随着图像从 256x256 重新缩放到 128x128,这些图像产生的异常分数将会更小。
结论
“使用基于深度学习的图像完成进行异常检测”的方法似乎是检测火星表面技术特征的可行选择。可以通过以下方式进一步增强模型性能:
- 为更多时代而训练
- 升级模型配置
- 对 HiRISE 图像进行聚类,并在一组相似的图像上进行训练,以使模型能够更好地概括,并更果断地检测异常。
基于 Prophet 库的时间序列异常检测
首先,我们来定义一下什么是时间序列中的异常。时间序列的异常检测问题可以表述为寻找相对于一些标准或通常信号的异常数据点。虽然有许多异常类型,但从业务角度来看,我们将只关注最重要的类型,如意外的峰值、下降、趋势变化和水平变化。你可以用两种方式解决这个问题:有人监督和无人监督。虽然第一种方法需要一些标记数据,但第二种方法不需要,您只需要原始数据。在那篇文章中,我们将关注第二种方法。
看看我的机器和深度学习博客https://diyago.github.io/
一般来说,无监督异常检测方法是这样工作的:你建立你的数据的一些通用简化版本——在这个模型的阈值之外的任何东西都被称为异常值或异常值。因此,首先我们需要将我们的数据放入某个模型中,希望这个模型能够很好地描述我们的数据。先知图书馆来了。当然,你可以尝试或多或少强大/复杂的模型,如 arima、回归树、rnn/lstm 甚至移动平均线等,但几乎所有这些模型都需要调整,不太可能开箱即用。先知不是那样的,它像自动 arima,但好得多。开始使用专注于预测时间序列的库真的很强大也很容易。它是由脸书的核心数据科学团队开发的。你可以在这里阅读更多关于 T2 图书馆的信息。
要安装 Prophet 库可以通过 pypi 安装:
pip install fbprophet
我们将使用一些样本数据,其中有一些有趣的异常值。你在这里下载数据,看起来是这样的:
Some random time-series
为了训练模型,我们将定义一些基本超参数 interval_width 和 changepoint_range 。它们可用于调整边界的宽度:
Fitting prophet model
然后,我们将高于顶部的所有内容设置为异常值,并降低模型边界的底部。此外,将离群值的重要性设置为基于点离边界有多远:
然后你就准备好获取剧情了。推荐试试牛郎星库,只需设置添加就能轻松获得一个互动剧情。与您的代码交互。
最后,我们得到了结果。他们似乎很有道理:
- Github 储存库包含代码和数据
- Kaggle 代码与数据由 Vinay Jaju
Python 异常检测手册— (12)自动编码器
(新修订日期:2022 年 12 月 5 日)
自动编码器模型是神经网络或深度学习的重要应用。它们被广泛应用于降维、图像压缩、图像去噪和特征提取。它们也被应用于异常检测,并取得了良好的效果。
深度学习是机器学习的整个课题。我知道不是所有的读者都熟悉深度学习,所以我在这一章中花了更多的章节来描述深度学习的具体细节。因为许多读者熟悉回归,所以我将从回归的角度介绍深度学习,并使用逻辑回归来解释神经网络图。这种回归友好的方法可以帮助读者理解神经网络建模。
至此,我解释了自动编码器的结构,并向您展示了如何构建异常值。我将深入研究自动编码器所基于的深度学习模型的组件。这些组件包括批量大小的概念、L1 和 L2 正则化、纪元、深度学习模型中的优化器等等。完成本章后,读者将对通用深度学习框架充满信心,可以对超参数进行微调。
(一)理解深度学习
往往深度学习或者神经网络是用他们的行话来呈现的。学习者以类似大脑的解剖学为导向来“想象”它在大脑中是如何运作的。学习者会看到神经元、互联性和复杂的神经网络系统。在我的讲座从回归到深度学习的过渡中,我不知何故感到有一瞬间的沉默——就像跳过一个深深的缺口。术语的差异也造成了知识差距。为了用回归友好的方法呈现深度学习,让我首先解释神经元、激活函数、层、优化器等等。然后,我将向您展示如何在深度学习框架中建立逻辑回归。
(A.1)数据在深度学习中被称为“张量”
在 y = XB + e 回归公式中,y 是一维向量,X 是 2D 矩阵。在 EXCEL 电子表格中,因变量 y 是一列,协变量 X 是多列。在深度学习术语中,一列称为张量。1D 矢量是张量,2D 矩阵是 2D 张量。流行的机器学习平台 tensor flow(tensorflow.org)也以此命名,并为神经网络建模而建。一旦你知道“张量”除了一维向量什么都不是,你可能会感到宽慰。为什么他们不直接叫它“向量”?这个术语来自哪里?它来自拉丁语张量,意思是“伸展的东西”。数学家沃耳德玛·福格特(1898 年)利用数学中的这一概念将矢量称为张量。
Figure (A.1): Tensor/Vector (Image by author)
(A.2)输入层中的神经元是输入变量
神经网络中的术语神经元和层对于回归学习者来说是陌生的。你大概见过类似图(A.2)的神经网络图。图为一款型号。模型是描述 X 和 y 之间关系的算法。例如,回归或决策树是模拟 X 和 y 之间关系的框架。
Figure (A.2): A Neural Network (Image by author)
神经网络模型有一个输入层、隐藏层和一个输出层。神经网络是一种监督学习模型。为了训练模型,X 矩阵被馈送到输入层,而目标 y 被馈送到输出层。图中的节点被称为神经元。神经元是一个矢量或张量。任何两个神经元之间的连接代表参数。隐藏层中的每个神经元都是前一层神经元的加权和。可以有很多隐藏层。
(A.3)使用神经网络构建逻辑回归
如果没有隐藏层,神经网络实际上会崩溃为逻辑回归。在图(A.3)中,有四个变量 x1 — x4 和一个输出变量 y 。表示逻辑回归 Y=f(a) 其中 a = w1x1 + w2x2 + w3x3 + w4x4 。 w1 — w4 是需要优化的参数。
Figure (A.3): A logistic Regression in Deep Learning
(A.4)激活功能
在深度学习中,还有一个组件叫做激活函数。它的工作方式类似于逻辑回归中的 logit 函数。在逻辑回归中,logit 函数将原始预测转换为介于 0 和 1 之间的概率。在深度学习中,激活函数将原始值映射到非零值。因为一个神经元中的值是前一层神经元的线性组合,所以这些值可能会变得非常小,并最终在多层计算后消失。在这种情况下,神经网络无法继续。这就是消失梯度问题,说的是基于梯度的学习方法中权重会消失。因此,激活函数将权重映射到一系列值,如 0 和 1,以防止它们消失。
激活功能可以应用于一些或所有隐藏层。对于激活函数来说,任何能够将原始值单调映射到新值的非线性函数都是不错的选择。常见的函数有 Sigmoid、ReLu 和 Tanh 函数。这里我只介绍前两个。
一个 sigmoid 函数是一个 logit 函数。因为输出值的范围可以扩大到正无穷大或负无穷大,所以神经网络应用 sigmoid 函数将输出转换为 0 到 1 之间的值。
一个 ReLU 功能(整流线性单元)是另一个流行的激活功能。它将任何负值都限制在零。
(A.4)不同数据类型的不同深度学习算法
提及三大数据类别是有帮助的。它们是(1)多元数据,(2)串行数据(包括时间序列、文本和语音流),以及(3)图像数据。发明了许多不同类型的神经网络框架来处理每种类型的数据。标准的前馈神经网络通常用于多元数据。递归神经网络(RNN)和长短期记忆网络(LSTM)是系列数据的例子。和卷积神经网络(CNN)是图像数据的例子。在这一章中,我们主要关注多元数据。对序列数据感兴趣的读者,推荐查阅《现代时间序列异常检测》或《RNN/LSTM/GRU 股价预测技术指南》这本书。对图像数据和神经网络应用感兴趣的读者,推荐查看“用于图像分类的迁移学习”。
(A.5)神经网络的有用链接
如果你想了解更多关于人工神经网络(ANN)的知识,下面的视频剪辑给出了一个非常直观的观点:
这个视频描述了什么是反向传播:
(A.6)图像分类中的深度学习
显然,仅仅用深度学习来做逻辑回归是矫枉过正的。深度学习可以做很多复杂结构的图像识别。因为使用图像应用程序学习自动编码器会容易得多,所以在这里我将描述图像分类是如何工作的。
新生婴儿不知道狗的图像,但是他/她将学会记住关于狗的一些特殊“特征”,然后识别该图像。如果我们想要一种算法像我们人类一样识别图像,该算法必须经历相同的学习过程。我们要用成千上万张标签为“狗”的图像,和成千上万张标签为“猫”的图像来“训练”模型。该模型将学习狗或猫的特征。该模型将能够识别未知图像中的任何特殊特征,以辨别它是狗还是猫的图像。我个人认为图像识别模型无法取代上帝创造的人脑中错综复杂的网络。但是它们被聪明地发明来帮助人类重复任务。
Figure (A.5): Artificial Neural Network (Image by author)
(B)了解自动编码器
自动编码器是一种特殊类型的神经网络,它将输入值复制到输出值。因为它不像标准神经网络模型那样需要目标变量,所以它被归类为无监督学习。
在图(B)中,目标值(蒙娜丽莎图像)与输入值相同。它在蒙娜丽莎上模仿蒙娜丽莎。你可能会问,如果输出值设置为等于输入值,我们为什么要训练模型。事实上,我们对输出层不太感兴趣。我们对隐藏的核心层感兴趣。当隐层神经元的数量少于输入层*,*时,隐层将提取输入值的本质信息。这种情况迫使隐藏层学习数据的大多数模式并忽略“噪声”。在自动编码器模型中,隐藏层的维数必须少于输入层或输出层的维数。如果隐藏层中的神经元数量多于输入层中的神经元数量,神经网络将被赋予过多的能力来学习数据。在极端情况下,它可能只是简单地将输入复制到输出值,包括噪声,而没有提取任何必要的信息。
Figure (B): Autoencoders
图(B)也显示了编码和解码过程。编码过程压缩输入值以到达核心层。它看起来像一个左宽右窄的漏斗。解码过程重构信息以产生结果。解码过程看起来与编码漏斗相反。它左边窄,右边宽。按照惯例,我们建立模型,使得解码过程反映编码过程。解码漏斗的神经元数量和隐藏层数量反映了编码漏斗的神经元数量和隐藏层数量。大多数从业者只是采用这种对称。我们将在后面的章节中学习如何建立模型。
©自动编码器有哪些应用?
自动编码器的早期应用是降维。Hinton 和 Salakhutdinov (2006)的一篇里程碑论文显示,与 PCA 的前 30 个主分量相比,经过训练的自动编码器产生更小的误差,并且更好地分离聚类。自动编码器在计算机视觉和图像编辑中也有广泛的应用。在图像着色*,*中,自动编码器用于将黑白图像转换成彩色图像。图(C.1)显示了用于图像着色的自动编码器模型。输入图像是黑白图像,对应的目标图像是彩色图像。该模型在许多对黑白和彩色图像上被训练。该模型能够将任何黑白图像转换成彩色图像。
Figure (C.1): An autoencoder model for image coloring (Image by author)
类似地,自动编码器可以用于降噪。图(C.2)显示了一个自动编码器,输入图像是一个模糊的图像,而目标是一个清晰的图像。该模型将在多对模糊和清晰的图像上进行训练。然后,该模型将能够把任何模糊的图像转换成彩色图像。参见我的帖子“用于图像降噪的卷积自动编码器”。
Figure (C.2): An autoencoder model for noise reduction (Image by author)
(D)为什么要使用自动编码器进行降维?
已经有许多有用的工具,如主成分分析(PCA)来检测异常值,为什么我们需要自动编码器?回想一下 PCA 使用线性代数来转换。相反,自动编码器技术可以利用其非线性激活函数和多层来执行非线性变换。用自动编码器训练几个层比用 PCA 训练一个巨大的变换更有效。因此,当数据问题是复杂的和非线性的时,自动编码器技术显示了它们的优点。在“使用 Python 的降维技术”中,我描述了内核 PCA 更加灵活,因为它可以对数据中的非线性分布进行建模。事实上,更强大的自动编码器可以模拟比内核 PCA 更复杂的数据分布。
[## 通过我的推荐链接加入 Medium-Chris Kuo/data man 博士
阅读 Chris Kuo/data man 博士的每一个故事。你的会员费直接支持郭怡广/戴塔曼博士和其他…
dataman-ai.medium.com](https://dataman-ai.medium.com/membership)
(E)建模程序
我将以下建模过程应用于模型开发、评估和结果解释。
- 模型开发
- 阈值确定
- 正常组和异常组的描述性统计
使用异常值分数,您将选择一个阈值来区分异常值分数高的异常观察值和正常观察值。如果任何先验知识表明异常的百分比应该不超过 1%,那么您可以选择一个导致大约 1%异常的阈值。
两组之间特征的描述性统计(如平均值和标准偏差)对于传达模型的可靠性非常重要。如果期望异常组中某个特征的均值高于正常组,如果结果相反,那就反直觉了。在这种情况下,您应该调查、修改或删除该特征,然后重新建模。
(E.1)第一步——建立模型
让我使用 PyOD 的效用函数generate_data()
来生成 500 个观察值和 5%的异常值。与其他章节不同,我将生成多达 25 个变量。这 25 个变量将馈入输入层的 25 个神经元。回想一下,在自动编码器中,隐藏层中的神经元数量应该少于输入层中的神经元数量。更多的变量可以让我们为自动编码器试验不同的层和神经元设置。
聚集在一起的紫色点是“正常”观察值,黄色点是异常值。
这里我指定了一个非常简单的自动编码器,它有两个隐藏层,每个隐藏层有两个神经元,即 hidden_neurons = [2,2]。
Figure (E.1): The structure of the Autoencoder (Image by author)
图(E.1)打印出了自动编码器模型的结构。第一列是层的名称,第二列是层的形状,第三列是参数的数量。术语“顺序”表示这是一个简单的神经网络模型。术语“密集”意味着神经网络是规则的密集连接的神经网络层。我们的模型有输入层、两个隐藏层和输出层。所以“密集 _1 +漏失 _1”、“密集 _2 +漏失 _2”、“密集 _3 +漏失 _3”、“密集 _4 +漏失 _4”这样的重复结构代表了四个层次。输入层的形状是 25,因为模型自动检测到有 25 个输入变量。在这个模型中,有 1433 个参数需要训练。我将在(G)节解释“辍学”的含义。
您的屏幕将显示“纪元 1/100”、“纪元 2/100”,依此类推,直到达到“纪元 100/100”。这就是模特培训。先完成模型训练吧。我将在后面解释时代的概念。
(E.2)步骤 2——确定一个合理的阈值
PyOD 有一个内置函数threshold_
,在给定污染率的情况下计算训练数据的阈值。如果我们不指定污染率,默认为 10%。因为我们在模型中将其指定为 5%,所以阈值是 5%处的值。下面的代码显示了 5%污染率的阈值是 4.1226。
正如我们在其他章节中提到的,通常我们不知道异常值的百分比。我们可以使用异常值分数的直方图来选择合理的阈值。图(E.2)中异常值分数的直方图建议阈值为 4.0,因为直方图中存在自然切割。
Figure (E.2): The histogram of Autoencoder outlier score
(E.3)第 3 步——分析正常和异常组
描述正常组和异常组是证明模型可靠性的关键步骤。正常组和异常组的特征统计应该与任何领域知识一致。如果异常组中某个特征的平均值应该很高,但结果却相反,建议您检查、修改或丢弃该特征。您应该迭代建模过程,直到所有的特性都与先前的知识一致。另一方面,也建议你验证数据提供的新见解的先验知识。
Table (E.3)
上表显示了正常组和异常组的特征。它显示了正常组和异常组的计数和计数百分比。提醒您使用功能名称来标记功能,以便有效地进行演示。该表告诉我们几个重要的结果:
- **离群组的大小:**一旦确定了阈值,就确定了大小。如果阈值来源于图(E.2)并且没有先验知识,那么大小统计就成为一个很好的参考。
- **各组中的特征统计:**所有的手段必须与领域知识一致。在我们的例子中,异常值组的平均值小于正常组的平均值。
- **异常平均分:**异常值组的平均分应该高于正常组。你不需要对分数解读太多。
因为我们有了基本事实,我们可以产生一个混淆矩阵来理解模型性能。下面的混淆矩阵证明了该模型做了一个体面的工作,并确定了所有 25 个异常值。
(F)骨料达到模型稳定性
正如 Aggarwal [1]所指出的,使用神经网络有两个问题。第一个问题是神经网络训练缓慢,第二个问题是它们对噪声和过拟合敏感。为了缓解过度拟合和模型预测不稳定的问题,我们可以训练多个模型,然后汇总分数。在聚合过程中,您仍将像以前一样遵循步骤 2 和 3。
有四种方法可以汇总结果:
- 平均:所有检测器的平均分数。
- 最大值的最大值
- 最大值的平均值(AOM)
- 平均值的最大值
您只需要一种聚合方法。在本文中,我将只演示平均方法。
首先,让我们指定三种不同的模型。模型“atcdr1”有 2 个隐层,每个隐层有 2 个神经元。模型“atcdr2”有三个隐藏层。三个隐层的神经元数目分别为 10、2 和 10。模型“atcdr3”有 5 个隐含层,分别有 15、10、2、10、15 个神经元。
我们已经提到了在编码和解码过程中应用对称性的惯例。模型“atcdr3”中的对称模式很明显。它的第一个隐层和最后一个隐层有 15 个神经元。它的第二隐层和最后一个第二隐层有 10 个神经元。
为了安全起见,让我们在训练前将数据标准化:
让我们准备三列的空数据框来存储三个模型的预测。
然后我们将训练这三个模型。
对于训练和测试数据,三个模型的预测将存储在列 0、1 和 2 中:
最后,让我们将三个模型的预测标准化,以便稍后我们可以对预测进行平均。
让我们绘制预测平均值的直方图。
Figure (F): The Histogram of the Average Prediction of the Training Data
图(F)中的直方图建议阈值为 0.0。我们可以利用表(F)中的汇总得分得出描述性统计数据。它将 25 个数据点识别为异常值。读者应对表(E.3)进行类似的解释。
Table (E.3)
(G)超参数调谐
在第(E.1)节中,我们用所有默认的超参数建立了一个简单的自动编码器模型。现在是学习更多超参数的时候了。所有这些超参数都是神经网络框架中的螺母和螺栓。对它们的良好理解将推进你对神经网络的了解。由于篇幅限制,我只能对每个超参数进行简要描述。强烈推荐感兴趣的读者阅读《图像分类的迁移学习》一书,该书提供了深入而又平易近人的描述。
让我们打印出“atcdr”型号的规格。我将根据这个列表来解释组件。
(G.1)批量大小—“32”
在模型训练期间,神经网络将数据样本分成“批”。模型使用梯度下降来搜索最佳参数值。将数据分成批次可以减少计算负担。假设有 100,000 个数据样本。梯度下降中的计算一次合计所有 100,000 个样本的梯度,以更新参数值。一次性对 100,000 个样本的梯度求和是一个非常耗时的过程,并且需要大量内存。如果将 100,000 个样本分成每批 32 个,则每次计算只需对 32 个样本的梯度求和。这大大减少了计算量。有 100,000 / 32 = 3,125 个批次,因此梯度下降将被评估 3,125 次。简而言之,批量是每次梯度更新的样本数。默认的批量大小batch_size
是 32。因为样本大小可能不总是批量大小 32 的倍数,所以最后一组样本可能少于 32 个样本。
(G.2)辍学率正规化——0.2
如前所述,神经网络中的复杂结构可能会过度拟合训练数据,但对新数据的预测却很差。神经网络模型使用两种技术来减轻过拟合:辍学率和 L1/L2 正则化。
丢弃技术在每次迭代期间随机丢弃或停用一层的一些神经元。这就像一些权重被设置为零。默认比率为 20%,这意味着在每次迭代的模型训练期间,隐藏层中 20%的神经元将被关闭。因为模型看的是稍微不同的结构本身来优化模型,可以防止某些神经元和权重记忆噪音。
(g . 3)L2 L1 正规化—“0.2”
正则化将惩罚项添加到损失函数中,以惩罚大量的权重(参数)或大量的权重。深度学习提供了拉索(L1)和里奇(L2):
默认正则化是λ值为 0.2 的 L2。
(G.4)纪元—“100”
纪元技术在神经网络中是独一无二的。*一个历元正好迭代所有数据一次。*模型参数在每个时期更新,直到它们达到最佳值。默认值为 100。这意味着该模型将遍历所有数据 100 次,以优化其参数。当您训练自动编码器时,您会看到“纪元 1”、“纪元 2”…,出现在您的屏幕上。
(G.5)隐藏激活—“ReLu”
在第(A.4)节中,我们已经解释了激活功能。PyOD 中的自动编码器将 ReLu 设置为默认激活功能。
(G.6)损失—“平均平方误差”
损失函数是判断模型性能的评价指标。PyOD 的自动编码器中的默认损失函数是均方误差。
如果您想知道为您的模型选择合适的评估指标是什么,让我提供一些信息。评估度量属于三个类别:(a)回归相关度量,(b)概率度量,以及©准确性度量。与回归相关的指标包括均方误差(MSE)、均方根误差(RMSE)、平均绝对误差(MAE)、平均绝对百分比误差(MAPE)等等。当您的目标变量是连续的,并且您希望以百分比误差或绝对误差的形式追求最小偏差时,您应该考虑与回归相关的指标。
当您的预测是一个概率时,将考虑概率指标。它们包括二元交叉熵和分类交叉熵。如果你的目标是二进制,你可以使用二进制交叉熵。如果你的目标是多类分类交叉熵。
准确性度量计算预测等于标注的频率。常用的度量是精度类、二进制精度类和分类精度类。顾名思义,二进制精度类计算预测匹配二进制标签的频率,而分类精度类计算预测匹配多个标签的频率。
(G.7)优化器—“亚当”
优化器是一种优化模型的功能。优化器使用上述损失函数来计算模型的损失,然后尝试最小化损失。没有优化器,机器学习模型什么也做不了。
流行的优化器包括随机梯度下降(SGD)、弹性反向传播(RProp)、自适应矩(Adam)和 Ada 系列。SGD 可能是使用最广泛的优化器。我已经在附录中包含了一个温和的描述。RProp 广泛用于多层前馈网络。当处理大量数据和参数时,Adam 被认为效率更高,需要的内存更少。它需要更少的内存并且是高效的。
(G.8)验证尺寸—“0.1”
模型将保留最后 10%的样本用于验证目的。相同的验证数据应该用于所有时期。该模型不应该在每个时期中绘制新的验证数据。这组固定的随机样本将确保使用相同的验证数据来比较各时期的模型性能。
(G.9)预处理—“真”
输入数据将被标准化用于模型训练。
(一)提醒—张量流的安装
自动编码器是基于神经网络的算法,需要张量流。为防止意外后果,PyOD 不会自动安装 TensorFlow。PyOD 的安装页面[8]对此进行了解释。在撰写本书时,tensorflow 还没有 Python 3.9 的稳定版本。我为 Python 3.7 创建了一个虚拟环境。然后用“康达安装 tenshoflow”在我的虚拟环境上安装 tensorflow。然后 pip 安装 pyod。
(J)摘要
- 自动编码器广泛应用于降维、图像压缩、图像去噪和特征提取。
- 在自动编码器中,隐层神经元的数量应该少于输入层神经元的数量。这使得隐藏核心层能够提取输入值的基本信息。
**(K) Python 笔记本:**可以通过这个 Github 链接下载 Python 笔记本。
参考文献
- Hinton,G. E .和 Salakhutdinov,R. R. (2006 年)。用神经网络降低数据的维数。科学,313,504–507。
- Aggarwal,C. C. (2016 年)。异常值分析。斯普林格。国际标准书号:978–3319475776
为了便于导航到章节,我在最后列出了章节。
- 第 1 章—简介
- 第 2 章—基于直方图的异常值得分(HBOS)
- 第 3 章——经验累积异常值检测(ECOD)
- 第 4 章——隔离林(IForest)
- 第 5 章——主成分分析
- 第六章——单类支持向量机
- 第七章——高斯混合模型(GMM)
- 第八章——K 近邻(KNN)
- 第 9 章—局部异常因素(LOF)
- 第十章——基于聚类的局部异常因子(CBLOF)
- 第 11 章——基于极端增强的异常检测(XGBOD)
- 第 12 章——自动编码器
- 第 13 章——极度不平衡数据的欠采样
- 第 14 章—极度不平衡数据的过采样
[## 通过我的推荐链接加入 Medium-Chris Kuo/data man 博士
阅读 Chris Kuo/data man 博士的每一个故事。你的会员费直接支持郭怡广/戴塔曼博士和其他…
dataman-ai.medium.com](https://dataman-ai.medium.com/membership)
建议读者购买郭怡广的书籍:
- 可解释的人工智能:https://a.co/d/cNL8Hu4
- 图像分类的迁移学习:https://a.co/d/hLdCkMH
- 现代时间序列异常检测:https://a.co/d/ieIbAxM
- 异常检测手册:https://a.co/d/5sKS8bI
使用隔离林和可视化进行异常检测
指标的突然上升或下降是一种异常行为,这两种情况都需要注意。如果我们在建模之前有关于异常行为的信息,异常的检测可以通过监督学习算法来解决,但是最初没有反馈,很难识别这些点。因此,我们使用像隔离森林、一类 SVM 和 LSTM 这样的算法,将这建模为一个无监督问题。在这里,我们使用隔离林来识别异常。
这里的数据是一个用例(如收入、流量等)的一天水平,有 12 个指标。我们必须首先识别用例级别是否有异常。然后为了更好的可操作性,我们深入到单个指标并识别其中的异常。
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)warnings.filterwarnings('ignore')
import os
print(os.listdir("../input"))df=pd.read_csv("../input/metric_data.csv")
df.head()
现在在数据帧上做一个透视来创建一个数据帧,其中所有指标都在日期级别。拉平多索引透视数据框架,并用 0 处理 na。
metrics_df=pd.pivot_table(df,values='actuals',index='load_date',columns='metric_name')
metrics_df.reset_index(inplace=True)
metrics_df.fillna(0,inplace=True)
metrics_df
隔离林试图分离数据中的每个点。在 2D 的情况下,它随机创建一条线,并试图挑出一个点。此处,异常点可以分几步分离,而较近的正常点可能需要更多的步骤才能分离。
我不会深入每个参数。污染在这里是一个重要的参数,我是在用 2D 图中的异常值验证其结果的反复试验的基础上得出它的值的。它代表数据中异常点的百分比**。**
我在这里使用 sklearn 的隔离森林,因为它是一个只有几个月数据的小数据集,而最近 h2o 的隔离森林也可用,它在大容量数据集上更具可扩展性,值得探索。
更多算法细节可以在这里找到:https://cs . nju . edu . cn/zhouzh/zhouzh . files/publication/ICD m08 b . pdf
关于 H2O 隔离森林的更多详情:https://github . com/h2oai/H2O-tutorials/tree/master/tutorials/Isolation-forest
metrics_df.columns
#specify the 12 metrics column names to be modelled
to_model_columns=metrics_df.columns[1:13]
from sklearn.ensemble import IsolationForest
clf=IsolationForest(n_estimators=100, max_samples='auto', contamination=float(.12), \
max_features=1.0, bootstrap=False, n_jobs=-1, random_state=42, verbose=0)
clf.fit(metrics_df[to_model_columns])pred = clf.predict(metrics_df[to_model_columns])
metrics_df['anomaly']=pred
outliers=metrics_df.loc[metrics_df['anomaly']==-1]
outlier_index=list(outliers.index)
#print(outlier_index)
#Find the number of anomalies and normal points here points classified -1 are anomalous
print(metrics_df['anomaly'].value_counts())
Number of outliers are 15 indicated by -1
现在,我们有 12 个指标,根据这些指标,我们根据隔离林对异常进行了分类。我们将尝试可视化结果,并检查分类是否有意义。
将指标标准化并拟合到 PCA 中,以减少维度数量,然后在 3D 中绘制它们,突出异常。
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from mpl_toolkits.mplot3d import Axes3D
pca = PCA(n_components=3) # Reduce to k=3 dimensions
scaler = StandardScaler()
#normalize the metrics
X = scaler.fit_transform(metrics_df[to_model_columns])
X_reduce = pca.fit_transform(X)fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_zlabel("x_composite_3")# Plot the compressed data points
ax.scatter(X_reduce[:, 0], X_reduce[:, 1], zs=X_reduce[:, 2], s=4, lw=1, label="inliers",c="green")# Plot x's for the ground truth outliers
ax.scatter(X_reduce[outlier_index,0],X_reduce[outlier_index,1], X_reduce[outlier_index,2],
lw=2, s=60, marker="x", c="red", label="outliers")
ax.legend()
plt.show()
3D plot of outliers highlighted
现在,当我们看到 3D 点时,异常点大多远离正常点群,但是 2D 点将帮助我们更好地判断。让我们试着将相同的 fed 绘制成缩减到二维的 PCA。
from sklearn.decomposition import PCA
pca = PCA(2)
pca.fit(metrics_df[to_model_columns])res=pd.DataFrame(pca.transform(metrics_df[to_model_columns]))Z = np.array(res)plt.title("IsolationForest")
plt.contourf( Z, cmap=plt.cm.Blues_r)b1 = plt.scatter(res[0], res[1], c='green',
s=20,label="normal points")b1 =plt.scatter(res.iloc[outlier_index,0],res.iloc[outlier_index,1], c='green',s=20, edgecolor="red",label="predicted outliers")
plt.legend(loc="upper right")
plt.show()
因此,2D 图给了我们一个清晰的图像,该算法正确地对用例中的异常点进行了分类。
异常以红边突出显示,正常点以绿点表示。
这里的污染参数起了很大的作用。
我们这里的想法是捕捉系统中所有的异常点。
因此最好将少数可能正常的点识别为异常点(假阳性),但不要错过捕捉异常点(真阴性)。(因此,我指定 12%为污染,因使用情形而异)
现在,我们已经在用例层面上理解了异常行为。但是,要对异常采取行动,单独识别并提供关于it 中哪些指标异常的信息非常重要。
当业务用户直观地观察(突然的下降/峰值)并采取行动时,算法识别的异常应该是有意义的。因此,在这个过程中,创建一个良好的可视化同样重要。
此函数在时间序列上创建实际值图,并在其上突出显示异常点。也是一个提供实际数据、变化和基于异常的条件格式的表格。
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import plotly.plotly as py
import matplotlib.pyplot as plt
from matplotlib import pyplot
import plotly.graph_objs as go
init_notebook_mode(connected=True)
def plot_anomaly(df,metric_name):
df.load_date = pd.to_datetime(df['load_date'].astype(str), format="%Y%m%d")
dates = df.load_date
#identify the anomaly points and create a array of its values for plot
bool_array = (abs(df['anomaly']) > 0)
actuals = df["actuals"][-len(bool_array):]
anomaly_points = bool_array * actuals
anomaly_points[anomaly_points == 0] = np.nan
#A dictionary for conditional format table based on anomaly
color_map = {0: "'rgba(228, 222, 249, 0.65)'", 1: "yellow", 2: "red"}
#Table which includes Date,Actuals,Change occured from previous point
table = go.Table(
domain=dict(x=[0, 1],
y=[0, 0.3]),
columnwidth=[1, 2],
# columnorder=[0, 1, 2,],
header=dict(height=20,
values=[['<b>Date</b>'], ['<b>Actual Values </b>'], ['<b>% Change </b>'],
],
font=dict(color=['rgb(45, 45, 45)'] * 5, size=14),
fill=dict(color='#d562be')),
cells=dict(values=[df.round(3)[k].tolist() for k in ['load_date', 'actuals', 'percentage_change']],
line=dict(color='#506784'),
align=['center'] * 5,
font=dict(color=['rgb(40, 40, 40)'] * 5, size=12),
# format = [None] + [",.4f"] + [',.4f'],
# suffix=[None] * 4,
suffix=[None] + [''] + [''] + ['%'] + [''],
height=27,
fill=dict(color=[test_df['anomaly_class'].map(color_map)],#map based on anomaly level from dictionary
)
))
#Plot the actuals points
Actuals = go.Scatter(name='Actuals',
x=dates,
y=df['actuals'],
xaxis='x1', yaxis='y1',
mode='line',
marker=dict(size=12,
line=dict(width=1),
color="blue"))#Highlight the anomaly points
anomalies_map = go.Scatter(name="Anomaly",
showlegend=True,
x=dates,
y=anomaly_points,
mode='markers',
xaxis='x1',
yaxis='y1',
marker=dict(color="red",
size=11,
line=dict(
color="red",
width=2)))axis = dict(
showline=True,
zeroline=False,
showgrid=True,
mirror=True,
ticklen=4,
gridcolor='#ffffff',
tickfont=dict(size=10))layout = dict(
width=1000,
height=865,
autosize=False,
title=metric_name,
margin=dict(t=75),
showlegend=True,
xaxis1=dict(axis, **dict(domain=[0, 1], anchor='y1', showticklabels=True)),
yaxis1=dict(axis, **dict(domain=[2 * 0.21 + 0.20, 1], anchor='x1', hoverformat='.2f')))fig = go.Figure(data=[table, anomalies_map, Actuals], layout=layout)iplot(fig)
pyplot.show()
查找百分比变化的辅助功能,根据严重性对异常进行分类。
预测功能基于来自决策功能的结果将数据分类为异常。
比方说,如果企业需要发现可能会产生影响的下一级异常情况,这可以用来识别这些点。
前 12 个分位数被识别为异常(高严重性),根据决策函数,我们在这里识别 12–24 个分位数点,并将其分类为低严重性异常。
def classify_anomalies(df,metric_name):
df['metric_name']=metric_name
df = df.sort_values(by='load_date', ascending=False)
#Shift actuals by one timestamp to find the percentage chage between current and previous data point
df['shift'] = df['actuals'].shift(-1)
df['percentage_change'] = ((df['actuals'] - df['shift']) / df['actuals']) * 100
#Categorise anomalies as 0-no anomaly, 1- low anomaly , 2 - high anomaly
df['anomaly'].loc[df['anomaly'] == 1] = 0
df['anomaly'].loc[df['anomaly'] == -1] = 2
df['anomaly_class'] = df['anomaly']
max_anomaly_score = df['score'].loc[df['anomaly_class'] == 2].max()
medium_percentile = df['score'].quantile(0.24)
df['anomaly_class'].loc[(df['score'] > max_anomaly_score) & (df['score'] <= medium_percentile)] = 1
return df
识别单个指标的异常,并绘制结果图。
X 轴—日期
Y 轴—实际值和异常点。
指标的实际值显示在蓝线中,异常点突出显示为红点。
在表中,背景红色表示高异常,黄色表示低异常。
import warnings
warnings.filterwarnings('ignore')
for i in range(1,len(metrics_df.columns)-1):
clf.fit(metrics_df.iloc[:,i:i+1])
pred = clf.predict(metrics_df.iloc[:,i:i+1])
test_df=pd.DataFrame()
test_df['load_date']=metrics_df['load_date']
#Find decision function to find the score and classify anomalies
test_df['score']=clf.decision_function(metrics_df.iloc[:,i:i+1])
test_df['actuals']=metrics_df.iloc[:,i:i+1]
test_df['anomaly']=pred
#Get the indexes of outliers in order to compare the metrics with use case anomalies if required
outliers=test_df.loc[test_df['anomaly']==-1]
outlier_index=list(outliers.index)
test_df=classify_anomalies(test_df,metrics_df.columns[i])
plot_anomaly(test_df,metrics_df.columns[i])
是的,从这些图中,我们能够捕捉到指标中的突如其来的峰值、下降并投射出来。
此外,条件格式表为我们提供了对数据等情况的洞察,不存在(值为零)的数据被捕获为高异常,这可能是数据处理中管道破裂的潜在结果,需要修复并突出显示高和低级别异常。
这个怎么用?
如果当前时间戳异常对于一个用例向下钻到指标,找出时间戳中异常高的指标集,对其执行 RCA。
此外,来自业务用户的反馈可以在数据中更新,这将有助于将此转化为监督/半监督学习问题,并比较它们的结果。
这里的增强将是**组合连续发生的异常行为。**例如,会导致几天指标峰值的大销售日可以显示为单个行为。