TowardsDataScience-博客中文翻译-2016-2018-二百九十九-

60 阅读1小时+

TowardsDataScience 博客中文翻译 2016~2018(二百九十九)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

用简单的步骤理解张量流程序。

原文:towardsdatascience.com/understandi…

ensorFlow 是一个库,可以应用于所有的机器学习算法,尤其是用神经网络进行深度学习。机器学习就是那些被编写来处理大型数据集以从中发现模式并提取信息的程序。它实际上从数据中学习,以做出准确或接近准确的预测,并更好地自我修正。就像我们希望有人做的那样。图像识别及其增强甚至识别是 ML 的一些应用。

自从我在 TF 上写了第一个故事后,我很高兴看到了可喜的变化。这个故事着重于理解 TensorFlow 程序的一些基础知识。我建议在你的机器上试试这段代码,看看它是如何工作的。

为了能够在不同的问题集上运行相同的模型,我们需要占位符和提要词典。随着我们的张量流程序变得越来越复杂,我们的可视化也需要跟上。回归试图对因果关系进行建模。原因是发生的独立变量,结果取决于原因。线性回归是一种直线回归,它是在对 X 导致 y 的回归进行建模时产生的。原因也称为解释变量

财富假说增加了预期寿命。

对于方程 Y=A+BX。为了找到最佳拟合,ML 算法将为 A 和 b 提供初始值。它将运行回归并找出 A 和 b 的这些值的误差。然后将这些误差反馈到输入中以获得 A 和 b 的新值。线性回归包括找到最佳拟合线

通过最小化最小平方误差来找到最佳拟合线。最佳拟合线是从底部连接到图中数据点的线的长度的平方和最小的线。

占位符

TensorFlow 中的占位符类似于变量,您可以使用 tf.placeholder 来声明它。您不必提供初始值,您可以在运行时使用 Session.run 中的 feed_dict 参数来指定它,而在 tf。变量,可以在声明它时提供初始值。

使用占位符的示例程序

import tensorflow as tf#setup placeholder using tf.placeholder
x = tf.placeholder(tf.int32, shape=[3],name='x')
'''it is of type integer and it has shape 3 meaning it is a 1D vector with 3 elements in it
we name it x. just create another placeholder y with same dimension. we treat the 
placeholders like we treate constants. '''
y = tf.placeholder(tf.int32, shape=[3],name='y')sum_x = tf.reduce_sum(x,name="sum_x")
prod_y = tf.reduce_prod(y,name="prod_y")
'''we dont know what values x and y holds till we run the graph'''
final_div = tf.div(sum_x,prod_y, nwe give fetches and feed_dict pass into every session.run commandame="final_div")final_mean = tf.reduce_mean([sum_x, prod_y], name="final_mean")sess = tf.Session()print ("sum(x): ", sess.run(sum_x, feed_dict={x: [100,200,300]}))
print ("prod(y): ", sess.run(prod_y, feed_dict={y: [1,2,3]}))writer = tf.summary.FileWriter('./tensorflow_example',sess.graph)writer.close()
sess.close()

取送字典

我们将 fetches 和 feed_dict 传递给每个 session.run 命令。获取参数指示我们想要计算什么,而提要字典为该计算指定占位符值

import tensorflow as tfW = tf.constant([10,100], name='const_W')#these placeholders can hold tensors of any shape
#we will feed these placeholders later
x = tf.placeholder(tf.int32, name='x')
b = tf.placeholder(tf.int32,name='b')#tf.multiply is simple multiplication and not matrix
Wx = tf.multiply(W,x, name="Wx")
y = tf.add(Wx,b,name='y')with tf.Session() as sess:
 '''all the code which require a session is writer here
 here Wx is the fetches parameter. fetches refers to the node of the graph we want to compute
 feed_dict is used to pass the values for the placeholders
 '''
 print( "Intermediate result Wx: ", sess.run(Wx, feed_dict={x: [3,33]}))
 print( "Final results y: ",sess.run(y, feed_dict={x:[5,50],b:[7,9]}))writer = tf.summary.FileWriter('./fetchesAndFeed',sess.graph)
writer.close()

变量

变量是允许你改变存储在那里的值的结构。监督学习算法在到达最终结论之前执行多次迭代,使用变量来存储随着模型收敛而变化的值。我们的目标是最小化回归线和数据集中的点之间的误差。所以我们在每次迭代中调整回归线来获得新的值。为了得到方程 y=A+Bx 的最佳拟合线,我们不断调整 A 和 b 的值。

变量是可变的张量值,在多次调用 sesssion.run()时保持不变。我用与上面相同的演示代码来解释这一点。

import tensorflow as tfW = tf.Variable([2.5,4.0],tf.float32, name='var_W')
#here W is a Variable
x = tf.placeholder(tf.float32, name='x')
b = tf.Variable([5.0,10.0],tf.float32, name='var_b')
#b is also a variable with initial value 5 and 10
y = W * x + b#initialize all variables defined
init = tf.global_variables_initializer()
#global_variable_initializer() will declare all the variable we have initilized
# use with statement to instantiate and assign a session
with tf.Session() as sess:
 sess.run(init)
 #this computation is required to initialize the variable
 print("Final result: Wx + b = ", sess.run(y,feed_dict={x:[10,100]}))
# changing values 
number = tf.Variable(2)
multiplier = tf.Variable(1)init = tf.global_variables_initializer()
result = number.assign(tf.multiply(number,multiplier))with tf.Session() as sess:
 sess.run(init)for i in range(10):
  print("Result number * multiplier = ",sess.run(result))
  print("Increment multiplier, new value = ",sess.run(multiplier.assign_add(1)))

一个 TensorFlow 程序的多个图形

我们可以在 TensorFlow 程序中显式创建尽可能多的图形。任何 TensorFlow 程序都有一个默认的图形,其中包含了您已经实例化的所有占位符和变量。但是我们可以通过使用 tf.graph()显式地实例化一个图来对图进行逻辑分段。下面的节目可能会解答你的一些疑惑。

import tensorflow as tfg1 = tf.Graph()
'''set g1 as default to add tensors to this graph using default methord'''
with g1.as_default():
 with tf.Session() as sess:
  A = tf.constant([5,7],tf.int32, name='A')
  x = tf.placeholder(tf.int32, name='x')
  b = tf.constant([3,4],tf.int32, name='b')y = A * x + bprint( sess.run(y, feed_dict={x: [10,100]}))
  '''to ensure all the tensors and computations are within the graph g1, we use assert'''
  assert y.graph is g1g2 = tf.Graph()with g2.as_default():
 with tf.Session() as sess:
  A = tf.constant([5,7],tf.int32, name='A')
  x = tf.placeholder(tf.int32, name='x')
  y = tf.pow(A,x,name='y')
  print( sess.run(y, feed_dict={x: [3,5]}))
  assert y.graph is g2'''same way you can access defaut graph '''
default_graph = tf.get_default_graph()
with tf.Session() as sess:
 A = tf.constant([5,7],tf.int32, name='A')
 x = tf.placeholder(tf.int32, name='x')
 y = A + x
 print(sess.run(y, feed_dict={x: [3,5]}))assert y.graph is default_graph

命名范围

TensorBoard 可能是最有用的调试工具,但是随着你的图表尺寸爆炸,你需要一些方法来获得更大的图片中的细节。现在使用 TensorFlow 运行下面的程序,并在 TensorBoard 中查看它的图形

import tensorflow as tfA = tf.constant([4], tf.int32, name='A')
B = tf.constant([4], tf.int32, name='B')
C = tf.constant([4], tf.int32, name='C')x = tf.placeholder(tf.int32, name='x')# y = Ax^2 + Bx + C
Ax2_1 = tf.multiply(A, tf.pow(x,2), name="Ax2_1")
Bx = tf.multiply(A,x, name="Bx")
y1 = tf.add_n([Ax2_1, Bx, C], name='y1')# y = Ax^2 + Bx^2
Ax2_2 = tf.multiply(A, tf.pow(x,2),name='Ax2_2')
Bx2 = tf.multiply(B, tf.pow(x,2),name='Bx2')
y2 = tf.add_n([Ax2_2,Bx2],name='y2')y = y1 + y2with tf.Session() as sess:
 print(sess.run(y, feed_dict={x:[10]}))writer = tf.summary.FileWriter('./named_scope',sess.graph)
 writer.close()

这张图表看起来真的很复杂!

现在我们可以使用命名作用域在 tensorboard 中组织事物。使用定义名称范围

with tf.name_scope("name the scope"):

并在这个范围内编写代码。上述程序可以使用如下所示的命名空间进行安排

import tensorflow as tfA = tf.constant([4], tf.int32, name='A')
B = tf.constant([4], tf.int32, name='B')
C = tf.constant([4], tf.int32, name='C')x = tf.placeholder(tf.int32, name='x')# y = Ax^2 + Bx + C
with tf.name_scope("Equation1"):
 Ax2_1 = tf.multiply(A, tf.pow(x,2), name="Ax2_1")
 Bx = tf.multiply(A,x, name="Bx")
 y1 = tf.add_n([Ax2_1, Bx, C], name='y1')# y = Ax^2 + Bx^2
with tf.name_scope("Equation2"):
 Ax2_2 = tf.multiply(A, tf.pow(x,2),name='Ax2_2')
 Bx2 = tf.multiply(B, tf.pow(x,2),name='Bx2')
 y2 = tf.add_n([Ax2_2,Bx2],name='y2')with tf.name_scope("final_sum"):
 y = y1 + y2with tf.Session() as sess:
 print(sess.run(y, feed_dict={x:[10]}))writer = tf.summary.FileWriter('./named_scope',sess.graph)
 writer.close()

将在接下来的故事中继续介绍图像识别。请分享帮助别人找到。欢迎发表评论。

了解谷歌分析报告

原文:towardsdatascience.com/understandi…

到现在为止,你应该已经完成了所有的艰苦工作,从而得到谷歌分析报告。这包括:

来自博客 1

  1. 提出一个测量计划
  2. 准备您的网站

来自博客 2

  1. 将谷歌分析代码添加到您的网站页面
  2. 让人们访问你的网站

现在,让我们深入研究 Google Analytics 报告,了解它们对于我们的目标和衡量计划的意义。请记住,有很多报告对小企业没什么帮助;因此,我们将只讨论重要的问题,以帮助您处理您的测量计划。

谷歌分析报告是什么意思

当你进入谷歌分析页面时,你会看到:

  1. 你在哪个账户?如果您想更改帐户,请单击下拉箭头并选择您想查看的网站数据。
  2. 报告导航
  3. 网页上的一些快速数据
  4. 你的网站上的一些实时数据(在这种情况下,我目前在我的网站上,所以它把我算作一个当前的访问者。)

让我们谈一谈我们在 3 中看到的数据。谷歌把这些数字放在一起,以帮助我们快速了解你的网站在过去七天里的情况。

在这个例子中,我在社交媒体数据上使用了 Kickass。

  • 用户—显示过去 7 天有 9 个用户。即 9 个人访问了该网站。
  • Sessions —显示过去 7 天内有 9 个会话。“会话”是指某人访问网站的每一次独特的时间。如果这个数字是 10 或更多,这意味着同一个人(在过去 7 天访问的 9 个人之一)访问您的网站不止一次。
  • 跳出率——这个数字指的是点击离开你的页面的用户的百分比。(我们将在本博客后面对此进行更深入的探讨)
  • 会话持续时间——人们在你的网站上花了多少时间。

现在,让我们仔细看看与您的小型企业相关的每个导航选项。这个博客不会详细讨论每一个导航选项,因为很多都不相关。

观众

“观众报告”部分深入介绍了访问我们网站的人的详细情况。为了给你一些不同的数据,我用 SonjaDewing.com 作为例子。

这里最重要的细节是:

概述:和你在家里看到的信息一样。不同的是,这里的图表向您展示了新访客与回头客的对比。这意味着人们在已经访问过网站之后会再回来——可能是阅读博客,找到你的联系信息,等等。

跳出率:这里我们再次得到跳出率的信息。跳出率基本上是指当人们访问网站时,他们没有找到足够有趣的东西让他们留在那里。发生这种情况的原因有很多,包括:

  • 他们是偶然进入你的网站的吗?
  • 你网站的某个地方有没有没有意义的参考(例如,一个关于回收的博客有一个关于动物收容所的网站链接,所以访问者不会发现它有用。)
  • 你的网站加载时间太长了吗?
  • 你的网站在用户的浏览器上工作吗?
  • 你的网站上有什么可以留住访客的内容吗(比如博客、活动日程等等)。)

跳出率有多重要?这取决于你网站的目标。如果你网站的目标是让人们注册你的时事通讯,用户可能会继续注册你的时事通讯,然后马上关闭网站。所以跳出率并不重要——只要你注册了时事通讯。

跳出率的理想目标是在 20–40%之间,平均值是 41–55%。

如果你想固定你的跳出率,在那些你能控制的事情上努力,比如:

  • 装载速度。如果用户必须等待一个网站加载,他们很可能会关闭它,去别的地方。在这种情况下,请缩小图像尺寸以提高加载速度。{链接到以前的带有大小调整器的 wordpress 插件},
  • 浏览器可以正确加载你的网站。用户来自各种浏览器,如 Chrome、Firefox 和 Internet Explorer。你有没有检查过你的网站在所有这些浏览器中看起来都是正确的?
  • 创造能让用户更长久停留的内容。这些内容可以是从博客到图片库的任何内容。

**技术/浏览器:**有趣的是,你可以向下滚动,点击“浏览器”,就可以快速看到人们浏览你的网站时使用的浏览器类型。当我们谈到跳出率时,这一点很重要。

如果 95%的用户都在使用 Chrome,一定要从 Chrome 上测试你的网站,以确保它看起来和工作正常。

地理/位置:受众也很重要。如果你关注的是你所在地区的受众,而人们从很远的地方来到你的网站,你可能想要创建更多关于你所在地区的内容,无论是博客还是你的联系页面上的更多信息。

如果你想更详细地了解这些信息,你可以点击地图上的蓝色区域,深入到各个州,等等。

如果你还没有使你的网站移动友好,这个信息是很重要的。如果你有很多人通过手机访问你的网站,你应该确保它适合移动环境。

用户流量 : 如果你想让用户去某个页面,非常重要。例如,这里我们看到有 4 个人直接进入了主页,只有 1 个人进入了“关于”页面。1 个人直接去了一篇关于马蹄湖的博文。

如果我想让人们停留在某个页面上,我会在我的主页上做些改变。即。放一个大链接到我的博客或其他页面信息。

获得物ˌ获得

人们是如何找到你的网站的?

概述:概述将展示人们如何找到你的网站。

有机发现意味着他们通过链接到你的网站的另一个网站、广告或在谷歌等浏览器上的搜索找到了你的网站。

直接搜索意味着他们知道你的网址并输入到他们的网址中。

这对你意味着什么?有有机的流量是很好的,特别是如果流量来自搜索——这意味着他们通过你的搜索引擎优化(SEO)找到你的信息。

Source/Medium :转到 Source/Medium 会告诉你那些有机搜索来自哪里。

其余的呢?事实上,对于小型企业的测量计划,您需要了解的并不多。

根据报告和你的计划改进你的网站

具体来说一下你的测量计划。

我在博客 1 中使用的例子

  1. 剧情小鸭。这家公司致力于帮助美国的作家更有创造力。

他们的网站业务目标是:

  • 通过在网站上分享有用的信息来提高品牌知名度,在年底前增加网站的流量。
  • 你可以看到有流量(最近 7 天 9)但跳出率很高。在这种情况下,这很可能是因为在这些博客中使用该网站作为例子。你不是网站的目标市场,所以你很快离开。
  • 我会做什么来修复高跳出率:1。开始为目标市场添加更多有帮助的博客。2.开始在社交媒体上分享这些博客来吸引访问者。3.探究跳出率高的任何其他原因(如上文跳出率中所述)。
  • 每次我对网站、博客等做出改变。我会回到这里,看看我的访问者数量是否上升,跳出率是否下降,并检查行为,看看观众/用户流是否去了我想要的地方。每次都做些小改动,看看你的数字是如何随着更新而变化的。
  • 到月底,通过 50 个新的电子邮件/时事通讯注册来提高品牌知名度。

这个数字你不需要从谷歌分析中得到,你可以从注册人数中看到。但是如果你没有从你的网站上直接获得任何注册,你会从你的时事通讯注册中知道,你会想要让你的注册页面容易被找到。

  1. 在社交媒体上表现出色。这家公司专注于向阿尔伯克基的小企业教授社交媒体。他们的网站业务目标:
  • 通过销售更多 9 月份下一场活动的门票来增加收入。
  • 收入数字将来自门票销售,但如果你得到了很多跳出率,这意味着没有人会花时间阅读你的活动。在这种情况下,我会围绕活动写一些博客(活动空间、活动主持人、从上次活动中吸取的教训、活动所在的城市等)。)这种方式增加了搜索引擎优化,让人们在网站上停留的时间更长一些。

创建自定义仪表板

如果你进入谷歌分析,只需要某些信息,创建一个仪表板会很有帮助。

谷歌已经有了一个入门仪表板,它非常有助于将所有东西放在一起。

  1. 点击定制
  2. 点击仪表板
  3. 点击创建

从上图可以看出,Google 给了你一个选项,让你从一个空白的仪表板开始,或者从一个初学者仪表板开始。Starter Dashboard 实际上非常有用。

  1. 点击启动仪表板
  2. 为您的新仪表板键入一个名称(我使用了快速查看)
  3. 点击创建仪表板

现在,您已经将数据分组到一个非常有用的页面上。要更改任何数据,请选择小部件上方的编辑按钮(小部件是页面上每个图形数据的引用)。要删除任何小部件,您可以选择每个图形右上角的关闭按钮。

在本例中,我将向下滚动并删除目标和收入的小部件,因为这些信息并不重要。

如果您想要添加新的小部件,请在页面的左上角选择+添加小部件。这将给出一个对话框,您可以在其中选择任何可用的小部件。然而,我不打算添加任何东西,因为这已经给了我需要一目了然的信息。

另外,那些我想添加的东西,比如有机/直接搜索,我不能添加到仪表板上。所以,我必须点击这些信息才能看到数据。

曾经有一种方法可以找到一份报告,并将其添加到您的仪表板上,但由于谷歌分析正在经历一些变化,目前还不可用。

这就是谷歌分析。当你了解这些报告时,事情就变得很简单了。留意那些数据,记得记录你做了什么。祝您的测量计划好运!

如果你喜欢这篇文章,请分享和推荐它,这样其他人就可以找到它!

在你走之前…

如果你喜欢这篇文章,你将会喜欢上《阿姆迪周刊》。这是我们免费的每周文摘。今天就订阅!

了解梯度增压机

原文:towardsdatascience.com/understandi…

<​a href​=”www.freepik.com/free-vector… by Starline<​/a>

动机:

虽然 Kaggle 竞赛中大多数获胜的模型都是一些高级机器学习算法的组合,但通常是这种组合一部分的一个特定模型是梯度推进机器。举个例子,在这篇文章中,Allstate Claims Severity Kaggle 竞赛的获胜者 Alexey Noskov 将他在竞赛中的成功归功于 GBM 算法的另一个变种 XGBOOST。然而,尽管它大受欢迎,许多专业人士仍然使用这种算法作为一个黑箱。因此,本文的目的是为这种强大的机器学习技术奠定一个直观的框架。

什么是梯度推进?

先从了解 Boosting 开始吧!强化是一种将弱学习者转化为强学习者的方法。在 boosting 中,每个新树都适合原始数据集的修改版本。梯度推进算法(gbm)可以通过首先介绍 AdaBoost 算法来最容易地解释。AdaBoost 算法首先训练一个决策树,在该决策树中,每个观察值被赋予相同的权重。在评估第一棵树后,我们增加那些难以分类的观察值的权重,降低那些易于分类的观察值的权重。因此,第二棵树在这个加权数据上生长。这里,我们的想法是改进第一棵树的预测。因此,我们的新模型是树 1 +树 2 。然后,我们根据这个新的 2-树集成模型计算分类误差,并生长第三棵树来预测修正的残差。我们重复这个过程指定的迭代次数。后续的树帮助我们对之前的树没有很好分类的观察结果进行分类。因此,最终集合模型的预测是由先前树模型做出的预测的加权和。

梯度增强以渐进、累加和顺序的方式训练许多模型。AdaBoost 和梯度提升算法之间的主要区别在于这两种算法如何识别弱学习器(例如决策树)的缺点。虽然 AdaBoost 模型通过使用高权重数据点来识别缺点,但梯度增强通过使用损失函数中的梯度来执行相同的操作( y=ax+b+e,e 需要特别提及,因为它是误差项)。损失函数是一种衡量指标,它表明模型系数对基础数据的拟合程度。对损失函数的逻辑理解将取决于我们试图优化的内容。例如,如果我们试图通过使用回归来预测销售价格,那么损失函数将基于真实和预测的房价之间的误差。类似地,如果我们的目标是对信用违约进行分类,那么损失函数将是对我们的预测模型在对不良贷款进行分类方面有多好的一种衡量。使用梯度推进的最大动机之一是它允许优化用户指定的成本函数,而不是通常提供较少控制并且本质上不符合真实世界应用的损失函数。

在 R 中训练一个 GBM 模型

为了在 R 中训练一个 gbm 模型,首先必须安装并调用 gbm 库。gbm 函数要求您指定某些参数。您将从指定公式开始。这将包括你的反应和预测变量。接下来,您将指定响应变量的分布。如果没有指定,那么 gbm 将尝试猜测。一些常用的分布包括-“伯努利”(0–1 结果的逻辑回归)、“高斯”(平方误差)、“TD ist”(t 分布损失)和“泊松”(计数结果)。最后,我们将指定数据n.trees 参数(毕竟 gbm 是树的集合!)默认情况下,gbm 模型将假设 100 棵树,这可以很好地估计我们的 gbm 的性能。

在 Kaggle 的 Titanic 数据集上训练一个 gbm 模型:我使用了来自 Kaggle 的著名 Titanic 数据集来说明我们如何实现一个 gbm 模型。我首先使用 read.csv() 函数将 Titanic 数据加载到我的 R 控制台中。然后,使用 createDataPartition()函数将原始数据集划分为单独的训练集和测试集。这种必要的划分在后期阶段非常有用,可以评估模型在单独的维持数据集(测试数据)上的性能,并计算 AUC 值。以下屏幕显示 R 代码。

注意:考虑到原始数据集的规模较小,我将 750 个观察值分配给训练,将 141 个观察值分配给测试保持。此外,通过定义一个种子值,我确保了每次都生成相同的随机数集。这样做将消除建模结果的不确定性,并帮助人们准确评估模型的性能。

下一步是使用我们的训练保持来训练 gbm 模型。虽然所有其他参数都与上一节中讨论的完全相同,但是还指定了另外两个参数- 交互。深度收缩。交互深度指定每棵树的最大深度(即训练模型时允许的变量交互的最高级别)。收缩率被认为是学习率。它用于减少或缩小每个附加的适合的基础学习者(树)的影响。它减少了增量步骤的大小,从而降低了每次连续迭代的重要性。

注意:即使我们最初用 1000 棵树训练我们的模型,我们也可以基于“开箱”或“交叉验证”技术来修剪树的数量,以避免过度拟合。我们将在后面看到如何将它合并到我们的 gbm 模型中(参见“调优 gbm 模型和提前停止”一节)

了解 GBM 模型输出

当您打印 gbm 模型时,它会提醒您在执行过程中使用了多少树或迭代。使用其内部计算,如变量重要性,它还会指出是否有任何预测变量在模型中没有影响。

gbm 建模的一个重要特征是可变重要性。将汇总函数应用于 gbm 输出会生成一个变量重要性表和一个模型图。下表根据各个变量的相对影响对其进行了排名,相对影响是一种衡量标准,表明每个变量在模型训练中的相对重要性。在泰坦尼克号模型中,我们可以看到船舱和性别是迄今为止我们 gbm 模型中最重要的变量。

调整 gbm 模型并提前停止

超参数调整对于 gbm 建模尤其重要,因为它们容易过度拟合。为 gbm 和随机森林等算法调整迭代次数的特殊过程称为“提前停止”。早期停止通过在单独的测试数据集上监控模型的性能来执行模型优化,并且一旦测试数据的性能停止提高超过一定的迭代次数,就停止训练过程。

它通过尝试自动选择拐点来避免过度拟合,在该拐点处,测试数据集的性能开始下降,而随着模型开始过度拟合,训练数据集的性能继续提高。在 gbm 的背景下,早期停止可以基于袋外样本集(“OOB”)或交叉验证(“cv”)。如上所述,停止训练模型的理想时间是在验证误差由于过度拟合而开始增加之前,验证误差已经减少并开始稳定。

虽然其他提升算法(如*“xgboost”)允许用户指定一系列指标(如错误和日志损失),但“gbm”算法专门使用指标“错误”来评估和测量模型性能。为了在“gbm”中实现提前停止,在训练我们的模型时,我们首先必须指定一个名为“c . v folds”*的附加参数。其次,gbm 包附带了一个名为 gbm.perf() 的默认函数来确定最佳迭代次数。参数“方法”允许用户指定用于决定最佳树数的技术(“OOB”或“cv”)。

以下控制台屏幕显示按照“OOB”和“cv”方法的最佳树数。

此外,我们还将看到两个曲线图,表明基于各自使用的技术的最佳树木数量。左边的图表显示了测试(绿线)和训练数据集(黑线)的错误。蓝色虚线表示最佳迭代次数。人们还可以清楚地观察到,超过某个点(对于“cv”方法为 169 次迭代),测试数据上的误差似乎由于过度拟合而增加。因此,我们的模型将在给定的最佳迭代次数上停止训练过程。

使用 gbm 的预测

最后, predict.gbm() 函数允许从数据中生成预测。gbm 预测的一个重要特性是用户必须指定树的数量。由于预测函数中的“n . trees”没有默认值,建模者必须指定一个。由于我们已经通过执行提前停止计算出了最佳的树数量,我们将使用最佳值来指定“ n.trees ”参数。我使用了基于交叉验证技术的最佳解决方案,因为在大型数据集上,交叉验证通常优于 OOB 方法。用户可以在预测函数中指定的另一个参数是类型,它控制函数生成的输出类型。换句话说,类型指的是 gbm 进行预测的尺度。

总结和评估结果:

最后,任何建模工作最重要的部分是评估预测和衡量模型的性能。我首先创建了一个混淆矩阵。混淆矩阵是一个非常有用的工具,它将模型的实际值与预测值进行比较。它被称为混淆矩阵,因为它揭示了你的模型在两个类之间有多混乱。混淆矩阵的列是真实类别,而矩阵的行是预测类别。在你制作混淆矩阵之前,你需要在一个给定的阈值上“切割”你的预测概率,以将概率转化为类别预测。使用 ifelse() 函数可以很容易地做到这一点。在我们的例子中,概率大于 0.7 的病例被标记为 1,即可能存活,概率较小的病例被标记为 0。记得调用“*插入符号”*库来运行混淆矩阵。

运行最后一行将产生我们的混淆矩阵。

看到我们模型的准确性,我和你一样高兴!深入到混淆矩阵,我们观察到我们的模型完美地预测了 141 个观察值中的 118 个,给了我们 83.69%的准确率。我们的模型有 23 个错误。这 23 个病例被分为假阴性和假阳性,分别为 9 和 14。

ROC 和 AUC:基于真阳性和假阳性,我绘制了 ROC 曲线并计算了我们的 AUC 值。

根据定义,ROC 曲线越靠近网格的左上角越好。左上角对应的是真阳性率 1,假阳性率 0。这进一步意味着好的分类器在曲线下有更大的面积。显然,我们的模型的 AUC 为 0.8389。

最后备注:

我希望这篇文章能帮助你对梯度推进算法的工作原理有一个基本的了解。欢迎在 LinkedIn 上加我,我期待听到你的评论和反馈。此外,如果您有兴趣阅读更多关于这种强大的机器学习技术的信息,我将强烈推荐以下资源。

  1. 机器学习的梯度推进算法简介
  2. 一位 Kaggle 大师解释梯度推进
  3. 梯度提升的自定义损失函数
  4. 用 R 中基于树的模型进行机器学习

另外,我很高兴地分享我最近提交给泰坦尼克号卡格尔比赛的分数在前 20%。我最好的预测模型(准确率为 80%)是广义线性模型、梯度推进机器和随机森林算法的集合。我已经上传了我的代码到 GitHub,可以在:【lnkd.in/dfavxqh 访问

了解 GRU 网络

原文:towardsdatascience.com/understandi…

pixabay.com

在本文中,我将尝试对一种真正令人着迷的神经网络类型给出一个相当简单易懂的解释。由 Cho 等人在 2014 年提出的 GRU(门控递归单元)旨在解决标准递归神经网络中的消失梯度问题。GRU 也可以被认为是 LSTM 的变体,因为两者设计相似,在某些情况下,产生同样优秀的结果。如果你不熟悉递归神经网络,我推荐阅读我的简介。为了更好地了解 LSTM,许多人推荐克里斯托弗·奥拉的文章。我还想补充一下这篇论文,它清楚地区分了 GRU 和 LSTM。

GRUs 是如何工作的?

如上所述,GRUs 是标准递归神经网络的改进版本。但是是什么让它们如此特别和有效呢?

为了解决标准 RNN 的消失梯度问题,GRU 使用所谓的更新门和复位门。基本上,这是决定什么信息应该被传递到输出的两个向量。它们的特别之处在于,它们可以被训练来保留很久以前的信息,而不会随着时间的推移而消失,也不会删除与预测无关的信息。

为了解释这一过程背后的数学原理,我们将考察以下递归神经网络中的一个单元:

Recurrent neural network with Gated Recurrent Unit

以下是那首单曲《GRU》的更详细版本:

Gated Recurrent Unit

首先,让我们介绍一下符号:

如果你不熟悉以上术语,我推荐你看这些关于“sigmoid”和“tanh”功能“hada mard product”操作的教程。

#1.更新门

我们从使用以下公式计算时间步长 t 的更新门 z_t 开始:

x_t 插入网元时,乘以自身重量 W(z) 。对于 h_(t-1) 来说也是如此,它保存先前 t-1 单元的信息,并乘以其自身的权重 *U(z)。*将两个结果相加,并应用 sigmoid 激活函数将结果压缩在 0 和 1 之间。按照上面的模式,我们有:

**更新门帮助模型确定有多少过去的信息(来自以前的时间步骤)需要传递给未来。**这非常强大,因为模型可以决定复制过去的所有信息,并消除消失梯度问题的风险。稍后我们将看到更新门的用法。现在记住 z_t 的公式。

#2.复位门

本质上,这个门是用来从模型中决定有多少过去的信息要忘记。为了计算它,我们使用:

该公式与更新门的公式相同。区别在于重量和门的用途,稍后会看到。下图显示了重置门的位置:

像以前一样,我们插入 h_(t-1) —蓝线x_t —紫线,将它们与相应的权重相乘,将结果求和并应用 sigmoid 函数。

#3.当前存储内容

让我们看看这些门究竟会如何影响最终输出。首先,我们从复位门的使用开始。我们引入了一种新的存储内容,它将使用复位门来存储过去的相关信息。其计算方法如下:

  1. 将输入 x_t 乘以权重 W 并将 h_(t-1) 乘以权重 U.
  2. 计算复位门 r_t 和 *Uh_(t-1)之间的 Hadamard(逐元素)乘积。*这将决定从先前的时间步骤中删除什么。假设我们有一个情感分析问题,要从一个人写的评论中判断他对一本书的看法。正文以“这是一本奇幻的书,它阐释了……”开始,几段后以“我不太喜欢这本书,因为我认为它捕捉了太多的细节”结束。要确定这本书的总体满意度,我们只需要最后一部分的评论。在这种情况下,随着神经网络接近文本的结尾,它将学习将 r_t 向量赋值为接近 0,洗去过去,只关注最后的句子。
  3. 总结第一步和第二步的结果。
  4. 应用非线性激活功能 tanh

你可以清楚地看到这里的步骤:

我们对 h_(t-1) —蓝线r_t —橙线进行逐元素乘法,然后将结果—粉线与输入 x_t —紫线相加。最后用 tanh 产生h’_ t—亮绿线

#4.当前时间步的最终记忆

作为最后一步,网络需要计算 h_t —保存当前单元信息的向量,并将其传递给网络。为此,需要更新门。它决定了从当前内存内容中收集什么— h'_t ,从前面的步骤中收集什么— h_(t-1) 。这是按如下方式完成的:

  1. 对更新门 z_th_(t-1)应用逐元素乘法。
  2. (1-z_t)h'_t. 应用逐元素乘法
  3. 将步骤 1 和步骤 2 的结果相加。

让我们举一个关于书评的例子。这一次,最相关的信息被定位在正文的开头。该模型可以学习将向量 z_t 设置为接近 1,并保留大部分先前信息。由于 z_t 在这个时间步将接近于 1, 1-z_t 将接近于 0,这将忽略当前内容的大部分(在这种情况下是解释书籍情节的评论的最后部分),这与我们的预测无关。

下面是一个强调上述等式的例子:

接下来,您可以看到如何使用 z_t —绿线来计算 1-z_t ,它与h’_ t—亮绿线一起产生结果暗红线z_t 也与 h_(t-1) —蓝线一起用于逐元素乘法。最后, h_t —蓝线是对应于亮红线和暗红线的输出相加的结果。

现在,您可以看到 gru 如何使用它们的更新和重置门来存储和过滤信息。这消除了消失梯度问题,因为模型不是每次都洗去新的输入,而是保留相关信息,并将其传递给网络的下一个时间步骤。如果仔细训练,他们甚至可以在复杂的情况下表现得非常好。

我希望这篇文章能让你更好地理解这个被称为 GRU 的最先进的深度学习模型。

更多 AI 内容,关注我 LinkedIn

感谢您的阅读。如果你喜欢这篇文章,给它一些掌声👏。希望你有一个伟大的一天!

理解机器学习中的高维空间

原文:towardsdatascience.com/understandi…

机器学习的一个标志是处理来自各个领域的大量数据。不管这些数据是作为图像、视频、文本、语音还是纯数字来处理,它几乎总是存在于某个高维空间中。在本文中,我将展示数据是如何在更高维度中表示的,以及我们如何在它们之间进行插值。由于几乎不可能将这些抽象的空间形象化,我也将提供一些有用的类比来思考。

作为图像的空间坐标

我们可以给我们三维空间中的每一个点一个 (x,y,z) 坐标。由于这些点被表示为三个实数,我们说它属于集合ℝ。这些坐标通常没有什么意义,但是它们包含了有用的距离和大小的概念。

上面重要的事情是,当我们有三个实数值时,我们可以在我们的三维空间中表示它。现在让我们考虑一个图像。一幅图像就是一大堆像素,每个像素代表一个范围内的亮度。为了简单起见,让我们只考虑灰度图像,其中每个像素的亮度介于 0 和 1 之间。

低分辨率图像可能是 32 x 32 像素的缩略图。即使它在视觉上表现为一个正方形,你也可以想象把它拉伸成一条 1024 = 32 × 32 像素的线。**那么这有什么意义呢?**这个形象生活在什么空间?嗯,我们现在有 1024 个实数,所以它一定存在于 1024 维空间ℝ ⁰ ⁴.中

思考高维空间

在某种程度上,我们已经有了一种可视化 1024 维空间的方法:32 乘 32 的灰度图像!这可能看起来有点绕圈,但它在某些情况下是有效的。当我们在这个高维空间中移动时,图像中的像素强度会发生变化。但是这些变化中的大部分看起来不会有任何意义,而且大部分看起来都是无意义的。

作为人类,我们知道如何在图像之间进行解释和插值。如果我们想把一张图片从看起来像猫变成一只狗,我们知道如何将两张图片可视化并分离出关键的区别。如果我们在大脑中绘制所有图片的空间,猫和狗的图片会非常接近。我们已经学习了图像之间的联系,以及如何将相似的图像放在一起,不相似的图像放在远处。但是在高维空间里,每一个图像都混杂在一起,被嘈杂的、无意义的废话包围着。

像素空间和潜在空间

到目前为止,我们一直在讨论的高维表示通常被称为环境空间(在图像的情况下称为像素空间)。这就是我们在高维欧几里得空间中把长串数字看作向量的方式。

关于简单图像,最著名的例子是手写数字。这些是 28 乘 28 的灰度图像,每张图像都属于ℝ⁷⁸⁴.如果我们想从一个图像“移动”到另一个图像,那么我们需要理解每个数字的语义。

Interpolating between digits in pixel space.

像素空间内插

假设我们有两个坐标 p 和*q。*我们可以在像素空间中对它们进行插值,只需逐步将每个坐标从第一个坐标变为第二个坐标。比如我们想分两步从坐标(1,2)变到(5,12),那么我们可以考虑级数(1,2) → (3,7) → (5,12)。在每一步中,我们只需添加坐标之间的一半差异。

这种算法适用于任意数量的坐标,因此也适用于我们的手写数字图像。在上图中,我们以一个“5”开始,以一个“9”结束上面的进程显示了它们之间的四个步骤的插值。

潜在空间和插值

虽然我们可以通过图像的像素来表示图像,但是我们也可以通过图像的语义表示来对图像进行分类。我们知道 5 和 9 是什么样子,所以我们可以想象把所有相似的数字组合在一起。这个空间可能完全不同,可能只需要几个维度。这是许多降维方法的基础。

很多机器学习模型的目的就是学习这个潜在空间。在以后的文章中,我将讨论这些模型的架构如何隐含地决定潜在空间的维度。

一旦我们学习了潜在空间表示,我们就可以在潜在空间中进行插值,并在图像之间获得更平滑的过渡。考虑之前的两个相同的图像。下图显示了潜在空间的插值。

Interpolating between the same two digits in latent space.

结论

在许多方面,机器学习都是关于解释高维空间的。理解这些空间是如何被使用和转化的是一种有价值的技能,即使我们自己无法想象它们。由于机器学习的困难在于设计和理解将数据减少到低维潜在空间的模型,因此学习这些表示有很大的价值。

谢谢!

感谢您阅读这篇文章,我希望它是有用的。我计划写更多关于机器学习中的降维和潜在空间表示的文章,如果能听到您对您想看到的内容的反馈,那将是非常棒的!请随意在下面的回答中写下任何问题。

理解卷积神经网络(CNN)如何使用单词嵌入执行文本分类

原文:towardsdatascience.com/understandi…

CNN 已经在各种文本分类任务中取得了成功。在[1]中,作者展示了一个简单的 CNN,具有很少的超参数调整和静态向量,在多个基准上取得了优异的结果——在 7 个任务中的 4 个上改进了现有技术。

然而,当学习将 CNN 应用于单词嵌入时,跟踪矩阵的维度可能会令人困惑。这篇短文的目的是简单地跟踪这些维度,并理解 CNN 是如何进行文本分类的。我们将在一个 7 个单词的句子上使用一层 CNN,单词嵌入为 5 维——这是一个帮助理解 CNN 的玩具示例。所有的例子都来自[2]。

设置

Above figure is from [2], with #hash-tags added to aid discussion. Quoting the original caption here, to be discussed later. “Figure 1: Illustration of a CNN architecture for sentence classification. We depict three filter region sizes: 2,3,4, each of which has 2 filters. Filters perform convolutions on the sentence matrix and generate (variable-length) feature maps; 1-max pooling is performed over each map, i.e., the largest number from each feature map is recorded. Thus, a univariate feature vector is generated from all six maps, and these 6 features are concatenated to form a feature vector for the penultimate layer. The final softmax later then receives this feature vector as input and uses it to classify the sentence; here we assume binary classification and hence depict two possible output states.”

#句子

例子是“我很喜欢这部电影!”,这里有 6 个单词,感叹号被当作一个单词来处理-一些研究人员以不同的方式处理,忽略感叹号-句子中总共有 7 个单词。作者选择 5 作为单词向量的维数。我们让 s 表示句子的长度,而 d 表示单词向量的维数,因此我们现在有一个形状为 s x d 的句子矩阵,即 7×5。

#过滤器

CNN 的一个令人满意的特性是它保持了计算机视觉中的 2D 空间方向。文字和图片一样,都是有方向性的。文本不是二维的,而是一维结构,其中单词序列很重要。我们还记得,示例中的所有单词都被一个 5 维的单词向量替换,因此我们固定过滤器的一个维度来匹配单词向量(5)并改变区域大小, h 。区域大小指的是要过滤的句子矩阵中代表单词的行数。

在图中,#filters 是过滤器的说明,而不是过滤器从句子矩阵中过滤出来的内容,下一段将使这种区别更加清楚。这里,作者选择使用 6 个过滤器——2 个互补的过滤器来考虑(2,3,4)个单词。

#功能地图

在本节中,我们将逐步介绍 CNN 如何执行卷积/过滤。为了清楚起见,我在句子矩阵和过滤器矩阵中填入了一些数字。

The action of the 2-word filter on the sentence matrix.

首先,由 2×5 黄色矩阵 w 表示的双字过滤器,覆盖在“I”和“like”的字向量上。接下来,它对其所有 2 x 5 元素执行元素式乘积,然后将它们相加并获得一个数字(0.6 x 0.2 + 0.5 x 0.1 + … + 0.1 x 0.1 = 0.51)。0.51 被记录为该滤波器输出序列的第一个元素 o 。然后,过滤器向下移动 1 个单词并覆盖单词向量“like”和“this ”,并执行相同的操作以获得 0.53。因此, o 将具有(sh+1 x 1)的形状,在本例中为(7–2+1 x 1)

为了获得特征图, c ,我们添加一个偏置项(一个标量,即形状 1×1)并应用一个激活函数(例如 ReLU )。这给了我们 c,o(sh+1 x 1)相同的形状。

# 1 最大

请注意, c 的维数取决于 sh ,换句话说,它会随着不同长度的句子和不同区域大小的过滤器而变化。为了解决这个问题,作者采用了 1-max 池函数,并从每个 c 向量中提取最大数。

#concat1max

在 1-max 池化之后,我们确定具有 6 个元素的固定长度向量(=过滤器数量=每个区域大小的过滤器数量(2) x 考虑的区域大小的数量(3))。这个固定长度的向量然后可以被馈送到 softmax(全连接)层以执行分类。作为学习的一部分,来自分类的误差然后被反向传播回以下参数:

  • 产生 ow 矩阵
  • 添加到 o 以产生 c 的偏置项
  • 单词向量(可选,使用验证性能来决定)

结论

这篇短文通过关注每个中间步骤中矩阵的维度,阐明了 CNN 在单词嵌入方面的工作。

参考文献

  1. 用于句子分类的卷积神经网络。2014;
  2. 用于句子分类的卷积神经网络的敏感性分析(和实践者指南)。arXiv 预印本 arXiv:151003820。2015;PMID: 463165

理解石灰如何解释预测

原文:towardsdatascience.com/understandi…

在最近的一篇文章中,我介绍了三种现有的方法来解释任何机器学习模型的个体预测。在这篇文章中,我将重点介绍其中的一种:局部可解释的模型不可知解释 ( LIME ),这是马尔科·图利奥·里贝罗、萨梅尔·辛格和卡洛斯·盖斯特林在 2016 年提出的一种方法。

在我看来,它的名字完美地概括了这种解释方法背后的三个基本思想:

  1. 模型不可知论。换句话说,与模型无关,这意味着 LIME 不会对其预测得到解释的模型做出任何假设。它将模型视为一个黑盒,因此它必须理解其行为的唯一方式是扰动输入,并观察预测如何变化。
  2. 可解释性。首先,解释必须易于用户理解,这对于模型所使用的特征空间来说不一定是正确的,因为它可能使用太多的输入变量(即使是线性模型,如果它具有成百上千个系数,也可能难以解释),或者它只是使用太复杂/非官方的变量(并且根据这些变量的解释将不会被人类解释)。出于这个原因,LIME 的解释使用了不同于原始特征空间的数据表示(称为可解释表示)。
  3. 地点。LIME 通过在我们想要解释的实例附近用可解释的模型(例如,具有几个非零系数的线性模型)来近似黑盒模型来产生解释。

总之,LIME 根据可解释模型的组成部分(例如,线性回归中的系数)生成对预测的解释,该模型类似于关注点附近的黑盒模型,并通过新的数据表示进行训练以确保可解释性。

可解释的表示

为了确保解释是可解释的,LIME 将可解释的表示与模型使用的原始特征空间区分开来。可解释的表示必须是人类可理解的,因此它的维度不一定与原始特征空间的维度相同。

p 为原始特征空间 X 的维数,设p’为可解释空间X’的维数。可解释输入通过映射函数hʸ: x’→x映射到原始输入,具体到我们要解释的实例 yX

不同类型的映射用于不同的输入空间:

  • 对于文本数据,可能的可解释表示是指示单词存在或不存在的二进制向量,尽管分类器可以使用更复杂和不可理解的特征,例如单词嵌入。形式上, X' ={0,1}ᵖ'≡ {0,1}× ⋅⋅⋅ × {0,1}其中 p' 是包含被解释的实例的单词数,映射函数将 1 或 0 的向量(分别是单词的存在或不存在)转换成模型使用的表示:如果它使用单词计数,映射将 1 映射到原始单词计数,0 映射到 0;但是如果模型使用单词嵌入,映射应该将任何用 1 和 0 的向量表示的句子转换成它的嵌入版本。
  • 对于图像,可能的可解释表示是指示一组连续相似像素(也称为超像素)的存在或不存在的二进制向量。形式上, X' ={0,1}ᵖ'其中 p' 是所考虑的超级像素的数量,通常通过图像分割算法如快速移动来获得。在这种情况下,映射函数将 1 映射为保留原始图像中的超像素,将 0 映射为灰化超像素(表示缺失)。

Original representation (left) and interpretable representation (right) of an image. Sets of contiguous similar pixels (delimited by yellow lines) are called super pixels. Each super pixel defines one interpretable feature. Source: www.oreilly.com/learning/in…

  • 对于表格数据(即矩阵),可解释的表示取决于特征的类型:分类、数值或混合数据。对于分类数据,x’={0,1}ᵖ,其中 p 是模型使用的特征的实际数量(即p’= p),并且映射函数将 1 映射到实例的原始类,将 0 映射到根据训练数据的分布采样的不同类。对于数值型数据, X'=X 和映射函数是恒等式。但是,我们可以将数字特征离散化,这样它们就可以被认为是分类特征。对于混合数据,可解释的表示将是由对应于分类特征的二元特征和对应于数值特征的数值特征(如果它们没有被离散化)组成的一个 p 维空间。根据解释变量的类型,映射函数的每个组成部分也根据前面的定义来定义。

位置

虽然一个简单的模型可能无法全局近似黑盒模型,但在我们想要解释的个体实例的邻域中近似它可能是可行的。换句话说,LIME 依赖于这样一个假设,即每个复杂的模型在局部尺度上都是线性的。

从形式上来说,我们需要一个权重函数 wʸ: X →ℝ⁺,它给最接近我们想要解释的实例的实例 zX 最大权重,给最远的实例最小权重。

保真度-可解释性权衡

Ribeiro 等人将解释定义为模型g:x’→ℝ,使得 gG ,其中 G 是一类“潜在的”可解释模型,也就是说,可以容易地用视觉或文本工件呈现给用户的模型,例如线性模型或决策树。注意 g 的定义域是可解释空间X’

假设𝔏 (f,g,wʸ) 是一个损失函数,它测量 g 在考虑权重 的情况下近似 f 的准确度。由于不是每个 gG 都足够简单到可以被解释,我们让ω(G)作为一个正则化项来度量解释 gG 的复杂度(与可解释性相反)。例如,对于线性模型,ω(g)可以是非零系数的数量,而对于决策树,ω(g)可以是树的深度。

方法

X =ℝᵖ为特征空间。设 y ∈ℝᵖ为被解释实例的原始表示,而y’x’为其可解释表示

同样,让ℝᵖ→ℝ成为被解释的模型。在分类中, f(x)x 属于某一类的概率(或二元指标)。对于多个类,LIME 分别解释每个类,因此 f(x) 是相关类的预测。在回归中, f(x) 是回归函数。

最后,让 g: X' → ℝ作为解释模型。设𝔏 (f,g,wʸ) 是一个损失函数,它测量 g 在由 定义的位置中逼近 f 的不忠实程度,设ω(g是解释的复杂度的度量 gG

为了确保可解释性和局部保真度,我们必须最小化𝔏( f,g,wʸ ,同时使ω(g)足够低,以便人类能够解释。因此,由石灰产生的解释ξ( y )由下式得到:

注意,该公式可用于不同的解释族 g、损失函数𝔏和正则化项ω。

实际上,LIME 用来产生解释的一般方法如下:

  1. 生成实例的可解释版本的 N 个“扰动”样本来解释y’。设{zᵢ'x’| I = 1,…,N}为这些观测值的集合。
  2. 通过映射函数恢复原始特征空间中的“扰动”观察值。设{zᵢhʸ(zᵢ')x| I = 1,…,N}为原表示中的集合。
  3. 让黑盒模型预测每一次“扰动”观察的结果。设{ f(zᵢ) ∈ℝ | i=1,…,N}为回答的集合。
  4. 计算每个“被扰乱”的观察的权重。设{ wʸ(zᵢ) ∈ℝ⁺ | i=1,…,N}为权的集合。
  5. 使用“扰动”样本及其响应的数据集{ (z'ᵢ,f(zᵢ))【∈x'×ℝ| I = 1,…,N}作为训练数据,求解上述方程。

请注意,复杂度不取决于训练集的大小(因为它产生对单个预测的解释),而是取决于计算预测的时间 f(z) 和样本数量 N

步骤 1 中描述的采样过程取决于可解释空间X’,因此根据输入数据的类型而不同(参见“可解释表示”一节)。对于解释空间由二值特征组成的文本数据或图像(即x’={0,1}ᵖ'),样本*zᵢ'x’是通过随机均匀抽取y’*的非零元素得到的,其中抽取的次数也是均匀抽样的。对于表格数据(即矩阵),步骤 1 取决于特征的类型,并且需要原始训练集。

对于文本数据,由于可解释空间是指示单词存在或不存在的二进制向量,因此该过程意味着从被解释的实例中随机移除单词。例如,如果我们试图解释文本分类器对句子“我讨厌这部电影”的预测,我们将扰动该句子,并得到对诸如“我讨厌电影”、“我这部电影”、“我电影”、“我讨厌”等句子的预测。请注意,如果分类器使用一些不可解释的表示,如单词嵌入,这仍然有效:在步骤 2 中,我们将使用单词嵌入来表示受干扰的句子,解释仍然是根据单词,如“仇恨”或“电影”。

对于图像,由于可解释空间是指示超像素的存在或不存在的二进制向量,所以该过程包括随机地使一些超像素变成灰色。换句话说,如果我们想要解释一幅图像的预测,我们将扰动该图像,并在具有一个或多个隐藏超像素的图像上获得预测。下图描述了这一过程:

Examples of perturbed instances of an image and their predictions. Source: www.oreilly.com/learning/in…

对于表格数据(即矩阵),分类特征和数值特征之间存在差异,但两种类型都依赖于训练集。对于分类特征(其可解释的表示是二进制的),扰动样本是通过根据训练分布进行采样,并在值与被解释的实例相同时生成为 1 的二进制特征而获得的。对于数字特征,通过从正态(0,1)分布采样并进行均值居中和缩放的逆运算(使用训练数据的均值和标准偏差)来扰乱正在解释的实例。

稀疏线性解释

Ribero 等人专注于他们所谓的稀疏线性解释,对应于解释族 G 的具体选择,损失函数𝔏和正则化项ω。这些选择没有明确的理由,但可以推断出一些可能的原因。它们是:

  • 作为解释族的线性模型类 Gg(z')=β⋅ z'

线性模型让我们通过检查系数的大小和符号来衡量每个特征在预测中的影响。因此,线性模型提供了解释变量和响应之间的定性理解,这使得它们成为适合作为解释模型的潜在可解释模型。

  • 定义在某个距离函数 D 上作为权函数的指数核。 wʸ(z)=e^{-D(y,z) /σ }

选择使用指数核是不合理的。使用的距离函数是文本数据的余弦相似性度量,以及图像和表格数据的欧几里德距离。然而,LIME 在 PythonR 中的实现接受用户选择的任何其他距离。最后,即使没有讨论内核宽度σ的选择,它默认为σ=3/4 \sqrt{p},其中 p 是特征的数量。

  • 加权最小二乘误差作为损失函数。

其中 *(z'ᵢ,f(zᵢ)*是“扰动”样本的数据集,其响应如上所述。二次损失函数通常比其他损失函数在数学上更容易处理,特别地,加权最小二乘问题具有封闭形式的解。

  • 将非零系数的数量限制为 K 作为正则项。

其中∥β∥₀=∑ⱼ|βⱼ|⁰是 L0“范数”(即β的非零项的数量)。正则化参数 K 对于解释任务至关重要,因为它确定了将呈现给用户的组件的数量。

注意,有两个超参数:用于解释的核宽度σ和特征数量 K

在前面的选择中,使用石灰进行解释所需的方程变为:

这个方程不能直接求解,因此他们通过首先使用特征选择技术选择 K 特征,然后通过加权最小二乘法估计系数来近似求解。

在原始论文中,作者使用 LASSO 产生的正则化路径(即 LASSO 作为收缩因子的函数估计的系数)选择 K 特征,然后通过加权最小二乘法估计系数。他们称这个过程为 K-LASSO。然而,LIME 的实现目前支持更多的特性选择算法来选择 K 特性。同样,所有特性都可以用于解释,但除非特性非常少,否则不推荐使用。

总的来说,LIME 用来近似前一个方程的解并产生对例如 y 解释的当前方法是:

  1. 生成实例的可解释版本的 N 个“扰动”样本以解释 y’。设{zᵢ'x’| I = 1,…,N}为这些观测值的集合。
  2. 通过映射函数恢复原始特征空间中的“扰动”观察值。设{zᵢ≡hʸ(zᵢ')x| I = 1,…,N}为原表示中的集合。
  3. 让黑盒模型预测每一次“扰动”观察的结果。设{ f(zᵢ) ∈ℝ | i=1,…,N}为响应集合,设ℨ={ *(z'ᵢ,f(zᵢ)x '*×ℝ| I = 1,…,N}为“扰动”样本及其响应的数据集。
  4. 计算每个“被扰乱”的观察的权重。设{ wʸ(zᵢ) ∈ℝ⁺ | i=1,…,N}为权的集合。
  5. 从扰动的数据集ℨ.中选择最能描述黑盒模型结果的 k 个特征
  6. 将加权线性回归模型(实际上,LIME 的当前实现是将正则化参数设置为 1 的加权岭回归)拟合到由步骤 5 中的 K 所选要素组成的要素缩减数据集。如果黑箱模型是回归量,线性模型会直接预测黑箱模型的输出。如果黑盒模型是分类器,线性模型将预测所选类别的概率。
  7. 从线性模型中提取系数,并用它们来解释黑盒模型的局部行为。

下图显示了此图像处理过程的一个示例。注意,除了黑盒模型(分类器或回归器) f 和解释 y (及其可解释表示y’)的实例之外,前面的过程需要预先设置样本数量 N ,核宽度σ和解释长度 K

Explaining the prediction of a classifier with LIME. Source: www.oreilly.com/learning/in…

在以后的文章中,我将解释如何使用 R 和 Python 解释带有 LIME 的 ML 模型的预测。

理解超参数及其优化技术

原文:towardsdatascience.com/understandi…

Figure 1: pixabay.com/en/thoughts…

什么是超参数? 在统计学中,超参数是来自先验分布的参数;它在数据被观察到之前捕捉先验信念。

在任何机器学习算法中,这些参数都需要在训练模型之前进行初始化。

模型参数对超参数

模型参数 是分类器或其他 ML 模型在训练过程中将自行学习的训练数据的属性。举个例子,

  • 权重和偏差
  • 决策树中的分割点

Figure 2: Hyperparameters vs model parameters → Source

模型超参数 是管理整个训练过程的属性。以下是在训练模型之前通常配置的变量。

  • 学习率
  • 时代数
  • 隐藏层
  • 隐藏单元
  • 激活功能

为什么超参数必不可少?

超参数非常重要,因为它们直接控制训练算法的行为,并对正在训练的模型的性能产生重大影响。

“一个好的超参数选择,真的可以让一个算法大放异彩”。

选择合适的超参数在我们的神经网络体系结构的成功中起着至关重要的作用。因为它对学习模型有巨大的影响。例如,如果学习率太低,模型将错过数据中的重要模式。如果高,可能会有碰撞。

选择好的超参数有两个好处:

  • 有效地搜索可能的超参数空间
  • 易于管理超参数调整的大量实验。

超参数优化技术

机器学习中寻找最优超参数的过程称为超参数优化。

常见算法包括:

  • 网格搜索
  • 随机搜索
  • 贝叶斯优化

网格搜索

网格搜索是实现超参数的一种非常传统的技术。它强力破解所有组合。网格搜索需要创建两组超参数。

  1. 学习率
  2. 层数

网格搜索通过使用两组超参数(学习速率和层数)为所有组合训练算法,并使用“交叉验证技术测量性能。这种验证技术保证了我们的训练模型从数据集中获得了大多数模式。使用“ K 重交叉验证 进行验证的最佳方法之一,这有助于为模型的训练和验证提供充足的数据。

Figure 3: Grid Search → Source

网格搜索方法是一种使用起来更简单的算法,但是如果数据具有被称为 维数灾难 的高维空间,它就会受到影响。

随机搜索

随机对搜索空间进行采样,并根据指定的概率分布计算集合。例如,我们可以检查 1000 个随机参数,而不是试图检查所有 100,000 个样本。

Figure 4: Random Search → Source

然而,使用随机搜索算法的缺点是,它不使用来自先前实验的信息来选择下一组,并且也很难预测下一组实验。

贝叶斯优化

超参数设置使模型在验证集上的性能最大化。机器学习算法经常需要微调模型超参数。不幸的是,这种调整通常被称为“黑函数”,因为由于函数的导数未知,它不能被写入公式。

更吸引人的优化和微调超参数的方法是 通过使用贝叶斯优化算法启用自动模型调整方法。用于逼近目标函数的模型被称为代理模型用于贝叶斯优化的流行代理模型是高斯过程(GP) 。贝叶斯优化通常通过假设从高斯过程(GP)中采样未知函数来工作,并在进行观察时保持该函数的后验分布。

执行贝叶斯优化时,必须做出两个主要选择。

  1. 选择先验函数,它将表达关于被优化函数的假设。为此,我们选择 高斯过程 先验
  2. 接下来,我们必须选择一个 采集函数 ,用于从模型后验构建一个效用函数,允许我们确定下一个要评估的点。

高斯过程

高斯过程定义了函数的先验分布,一旦我们看到一些数据,它可以转换成函数的后验分布。高斯过程使用协方差矩阵来确保值非常接近。协方差矩阵以及输出期望值的均值函数定义了高斯过程。

1.高斯过程将作为 的先验 用于贝叶斯推理 2。计算的后验概率在于它可以用来对看不见的测试用例进行预测。

Figure 5: Gaussian Process prior distribution → Source

Figure 6: Gaussian Process posterior distribution by applying covariance matrix → Source

采集功能

通过采集功能将采样数据引入搜索空间。这有助于最大化采集函数以确定下一个采样点。常见的采集功能有

  • 最大改善概率(MPI)
  • 预期改善
  • 置信上限(UCB)

预期改善(EI) 功能似乎是一个受欢迎的功能。它被定义为

EI(x)=𝔼[max{0,ƒ(x)−ƒ(x̂ )}]

其中ƒ(x̂)是超参数的当前最佳集合。最大化超参数将改善的性能。**

  1. 当损失的后验期望值 (x) 高于当前最佳值ƒ(x̂时,EI 为高)
  2. 当围绕点 x x 的不确定性σ( x )σ(x)高时,EI 高。

概要:

  • 超参数调谐是一门艺术,也就是我们常说的“黑函数”。选择合适的超参数将使算法发光并产生最大的精确度
  • 超参数优化技术大多使用任何一种优化算法
  1. 网格搜索
  2. 随机搜索
  3. 贝叶斯优化
  • 贝叶斯优化使用高斯过程(GP)函数来获得后验函数,以基于先验函数进行预测
  • 采集功能有助于最大化和确定下一个采样点。

理解机器学习中的 K-均值聚类

原文:towardsdatascience.com/understandi…

K-means 聚类是最简单和最流行的无监督机器学习算法之一。

通常,无监督算法仅使用输入向量从数据集进行推断,而不参考已知或标记的结果。

AndreyBu 拥有超过 5 年的机器学习经验,目前正在向人们传授他的技能,他说“K-means 的目标很简单:将相似的数据点组合在一起,并发现潜在的模式。为了实现这个目标,K-means 在数据集中寻找固定数量( k )的聚类

聚类是指由于某些相似性而聚集在一起的数据点的集合。

您将定义一个目标数 k ,它指的是您在数据集中需要的质心数。质心是代表群集中心的虚拟或真实位置。

通过减少类内平方和,将每个数据点分配给每个类。

换句话说,K-means 算法识别 k 个质心,然后将每个数据点分配到最近的簇,同时保持质心尽可能小。

K-means 中的表示是指数据的平均值;也就是求质心。

K 均值算法的工作原理

为了处理学习数据,数据挖掘中的 K-means 算法从第一组随机选择的质心开始,这些质心用作每个聚类的起始点,然后执行迭代(重复)计算以优化质心的位置

当出现以下任一情况时,它会停止创建和优化集群:

  • 质心已经稳定,因为聚类成功,所以它们的值没有变化。
  • 已达到定义的迭代次数。

K-means 算法例题

让我们看看 K-means 机器学习算法如何使用 Python 编程语言工作的步骤。

我们将使用 Scikit-learn 库和一些随机数据来说明 K-means 聚类的简单解释。

步骤 1:导入库

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom sklearn.cluster import KMeans%matplotlib inline

从上面的代码中可以看出,我们将在项目中导入以下库:

  • 熊猫用于阅读和书写电子表格
  • 用于执行有效计算的数字
  • 用于数据可视化的 Matplotlib

第二步:生成随机数据

下面是在二维空间中生成一些随机数据的代码:

X= -2 * np.random.rand(100,2)X1 = 1 + 2 * np.random.rand(50,2)X[50:100, :] = X1plt.scatter(X[ : , 0], X[ :, 1], s = 50, c = ‘b’)plt.show()

总共产生了 100 个数据点,并分成两组,每组 50 个点。

以下是数据在二维空间中的显示方式:

第三步:使用 Scikit-Learn

我们将使用 Scikit-learn 库中的一些可用函数来处理随机生成的数据。

代码如下:

from sklearn.cluster import KMeansKmean = KMeans(n_clusters=2)Kmean.fit(X)

在这种情况下,我们任意给 k (n_clusters)一个任意值 2。

下面是我们运行代码时得到的 K-means 参数的输出:

KMeans(algorithm=’auto’, copy_x=True, init=’k-means++’, max_iter=300
 n_clusters=2, n_init=10, n_jobs=1, precompute_distances=’auto’,
 random_state=None, tol=0.0001, verbose=0)

第四步:寻找质心

下面是查找聚类中心的代码:

Kmean.cluster_centers_

以下是质心值的结果:

array([[-0.94665068, -0.97138368],
 [ 2.01559419, 2.02597093]])

让我们显示集群质心(使用绿色和红色)。

plt.scatter(X[ : , 0], X[ : , 1], s =50, c=’b’)plt.scatter(-0.94665068, -0.97138368, s=200, c=’g’, marker=’s’)plt.scatter(2.01559419, 2.02597093, s=200, c=’r’, marker=’s’)plt.show()

以下是输出:

第五步:测试算法

下面是获取 K 均值聚类示例数据集的 labels 属性的代码;也就是说,如何将数据点分类到两个集群中。

Kmean.labels_

下面是运行上述 K-means 算法代码的结果:

array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

正如您在上面看到的,50 个数据点属于 0 簇,而其余的属于 1 簇。

例如,让我们使用下面的代码来预测数据点的聚类:

sample_test=np.array([-3.0,-3.0])second_test=sample_test.reshape(1, -1)Kmean.predict(second_test)

结果如下:

array([0])

显示测试数据点属于 0 (绿色质心)簇。

包装

以下是 Python 中完整的 K-means 聚类算法代码:

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom sklearn.cluster import KMeans%matplotlib inlineX= -2 * np.random.rand(100,2)X1 = 1 + 2 * np.random.rand(50,2)X[50:100, :] = X1plt.scatter(X[ : , 0], X[ :, 1], s = 50, c = ‘b’)plt.show()from sklearn.cluster import KMeansKmean = KMeans(n_clusters=2)Kmean.fit(X)Kmean.cluster_centers_plt.scatter(X[ : , 0], X[ : , 1], s =50, c=’b’)plt.scatter(-0.94665068, -0.97138368, s=200, c=’g’, marker=’s’)plt.scatter(2.01559419, 2.02597093, s=200, c=’r’, marker=’s’)plt.show()Kmean.labels_sample_test=np.array([-3.0,-3.0])second_test=sample_test.reshape(1, -1)Kmean.predict(second_test)

k-均值聚类是一种广泛使用的数据聚类分析技术。

这很容易理解,尤其是如果你使用 K-means 聚类教程来加速你的学习。此外,它可以快速提供培训结果。

但是,它的性能通常不如其他复杂的聚类技术有竞争力,因为数据中的微小变化会导致很大的差异。

此外,聚类被假设为球形且大小均匀,这可能会降低 K-means 聚类 Python 结果的准确性。

在机器学习中使用 K-means 聚类有什么经验?

请在下面分享你的评论。

使用 Python 的多处理库理解 Lamport 时间戳

原文:towardsdatascience.com/understandi…

每个使用分布式系统或来自这种系统的日志的人都直接或间接地遇到过 Lamport 时间戳。Lamport 时间戳用于在分布式系统中对事件进行(部分)排序。该算法基于事件的因果排序,是矢量时钟和间隔树时钟(ITC)等更高级时钟的基础。

在本文中,我们将首先简要介绍逻辑时钟的概念,解释为什么分布式系统中需要事件排序,并讨论一些替代方案。然后,我们将讨论 Lamport 时间戳的算法,并使用三个流程进行示例。接下来,我们将使用 Python 的多重处理库,用易于理解的代码实现这个例子。最重要的是,我们将用矢量时钟将代码转换成实现。

逻辑时钟

为了理解为什么需要逻辑时钟,理解什么是分布式系统是很重要的。分布式系统是一个系统,其组件(这里称为进程)位于不同的联网计算机上,然后这些计算机通过相互传递消息来协调它们的动作。

分布式系统的主要特性之一是它没有一个全局时钟。所有进程都有自己的本地时钟,但是由于时钟偏移时钟漂移,它们没有直接的方法知道自己的时钟是否与系统中其他进程的本地时钟一致,这个问题有时被称为时钟同步的问题。

这个问题的解决方案包括使用中央时间服务器( Cristian 的算法)或称为逻辑时钟的机制。中央时间服务器的问题在于,它的误差取决于消息从进程到时间服务器的往返时间。

逻辑时钟基于捕捉过程的时间顺序和因果关系,并基于这些关系对事件进行排序。第一个实现,Lamport 时间戳,是由 Leslie Lamport 在 1978 年提出的,现在仍然是几乎所有逻辑时钟的基础。

Lamport 时间戳算法

Lamport 逻辑时钟是在每个进程中维护的递增计数器。从概念上讲,这个逻辑时钟可以被认为是一个只对在进程间移动的消息有意义的时钟。当一个进程接收到一条消息时,它会将其逻辑时钟与发送方重新同步(因果关系)。

Lamport 时间戳的算法可以通过一些规则来捕获:

  • 所有进程计数器都从值 0 开始。
  • 对于流程中的每个事件(内部事件、消息发送、消息接收),流程的计数器都会递增。
  • 当进程发送消息时,它会在消息中包含其(递增的)计数器值。
  • 在接收到消息时,接收方的计数器被更新为其当前计数器和接收到的消息中的时间戳中的较大者,然后递增 1。

查看这些规则,我们可以看到该算法将创建最小的开销,因为计数器仅由一个整数值组成,并且消息传递搭载在进程间消息上。

Lamport 时间戳的缺点之一是,它们只对事件进行部分排序(而不是全部排序)。偏序表示并非每对事件都需要具有可比性。如果两个事件不能比较,我们称这些事件为并发。Lamport 时间戳的问题在于,它们不能判断事件是否是并发的。矢量时钟解决了这个问题。

例子

如果上面的描述对你来说有点模糊,看看下面的例子。所有进程的内部计数器(时钟)都从零开始。对于由圆圈表示的每个事件,计数器增加 1。当进程接收到消息时,它将其计数器设置为其内部计数器和消息中包含的时间戳中的较大者。

Lamport Timestamps example

例如,考虑流程 2 中的第一个事件,它从流程 1 接收一条消息。进程 2 的本地计数器此时为 0,但增加到 1,因为接收消息是一个事件。消息中包含的时间戳将是 2:进程 1 的本地时间加 1 (1+1)。进程 1 在发送消息的同时也将自己的时钟加 1。

为了在进程 2 中设置新的时间,取接收到的时间戳和它自己的本地时间之间的最大值( max(2,1) )并加 1。这将产生一个值为 3 的新时间戳。这是有意义的,因为消息永远不会在发送之前或发送的同时被接收。

履行

我们的实现将运行三个独立的进程,它们可以通过消息相互通信。每个进程都有自己的内部计数器,它将随着每个事件而更新。我们的脚本将为每个事件打印一行,连同更新的内部计数器和运行进程的机器上的时间。

在开始之前,我们需要从标准模块中导入一些函数:多处理中的进程管道用一个脚本运行多个 Python 进程,从 os 中的 getpid 获取每个进程的进程 id,从 datetime 中的 datetime 获取当前时间。

如果你需要更多关于多处理库的信息,请阅读文档,观看这个很棒的教程系列或者看一看这个(免费的)网络研讨会

from multiprocessing import Process, Pipe
from os import getpid
from datetime import datetime

然后我们创建一些辅助函数。第一个简单地打印本地 Lamport 时间戳和执行进程的机器上的实际时间。请注意,打印“实际”时间在真正的分布式系统中没有意义,因为本地时钟不会彼此同步。

def local_time(counter):
    return ' (LAMPORT_TIME={}, LOCAL_TIME={})'.format(counter,
                                                     datetime.now())

第二个是在流程收到消息时计算新时间戳。该函数取接收到的时间戳及其本地计数器中的最大值,并将其加 1。

def calc_recv_timestamp(recv_time_stamp, counter):
    return max(recv_time_stamp, counter) + 1

接下来,我们想为每个可能发生的事件创建一个函数。在我们的例子中,它们是三个事件:事件(任何本地事件)消息发送消息接收。为了使我们的代码易于阅读,事件函数将为事件发生的流程返回更新的时间戳

事件事件获取本地计数器和进程 id ( pid ),将计数器递增 1,打印一行以便我们知道事件发生了,并返回递增后的计数器。

def event(pid, counter):
    counter += 1
    print('Something happened in {} !'.\
          format(pid) + local_time(counter))
    return counter

send_message 事件也以 pid计数器作为输入,但是额外需要一个管道管道是多处理库中的一个对象,代表两个进程之间的双向连接。每个管道由两个连接对象组成,每个方向一个。要发送或接收消息,我们需要在这些连接对象上调用 sendrecv 函数。

如果你看一下我们的例子,你会发现我们只需要两个消息管道。一个在进程 1 和进程 2 之间,一个在进程 2 和进程 3 之间。因此,我们的示例将有四个连接对象: pipe12pipe21pipe23pipe32 (见下文)。

Example with two pipes

我们的 send_message 事件首先将计数器加 1,然后发送一个实际的消息(这里内容并不重要)及其增加的时间戳,并打印一个简短的语句,包括新的本地 Lamport 时间和机器上的实际时间。就像我们所有的事件函数一样,它返回新的本地时间戳。

def send_message(pipe, pid, counter):
    counter += 1
    pipe.send(('Empty shell', counter))
    print('Message sent from ' + str(pid) + local_time(counter))
    return counter

recv_message 事件采用与 send_message 相同的三个参数。它通过调用管道上的 recv 函数来接收实际消息和时间戳。然后,它使用我们之前创建的 calc_recv_timestamp 函数计算新的时间戳,并打印一行包括更新的计数器和机器上的实际时间。

def recv_message(pipe, pid, counter):
    message, timestamp = pipe.recv()
    counter = calc_recv_timestamp(timestamp, counter)
    print('Message received at ' + str(pid)  + local_time(counter))
    return counter

我们已经准备好开始创建我们的三个过程的定义。每个进程从获得其唯一的进程 id (这是在我们的机器上运行的进程的实际进程 id)和将自己的计数器设置为 0 开始。然后,根据我们前面的示例,通过调用不同的事件函数来更新计数器,并将返回值传递给计数器。

def process_one(pipe12):
    pid = getpid()
    counter = 0
    counter = event(pid, counter)
    counter = send_message(pipe12, pid, counter)
    counter  = event(pid, counter)
    counter = recv_message(pipe12, pid, counter)
    counter  = event(pid, counter)

def process_two(pipe21, pipe23):
    pid = getpid()
    counter = 0
    counter = recv_message(pipe21, pid, counter)
    counter = send_message(pipe21, pid, counter)
    counter = send_message(pipe23, pid, counter)
    counter = recv_message(pipe23, pid, counter)

def process_three(pipe32):
    pid = getpid()
    counter = 0
    counter = recv_message(pipe32, pid, counter)
    counter = send_message(pipe32, pid, counter)

请注意,到目前为止,如果您执行代码,什么也不会发生。这是因为实际上没有创建任何进程,更不用说启动了。在脚本的 main 部分,我们将创建成功运行脚本所需的两个管道( Pipe() )和三个进程( Process() )。要启动流程,我们需要在每个流程上调用 startjoin加入向我们保证所有流程将在退出前完成。

if __name__ == '__main__': oneandtwo, twoandone = Pipe()
    twoandthree, threeandtwo = Pipe()

    process1 = Process(target=process_one, 
                       args=(oneandtwo,))
    process2 = Process(target=process_two, 
                       args=(twoandone, twoandthree))
    process3 = Process(target=process_three, 
                       args=(threeandtwo,))

    process1.start()
    process2.start()
    process3.start()

    process1.join()
    process2.join()
    process3.join()

现在,尝试运行代码。如果一切正常,您将得到与下图非常相似的输出。请注意,每个进程都有自己唯一的进程 id(每次运行代码时,这些 id 都会改变):

  • 进程 1 的 id 为 16112
  • 进程 2 的 id 为 18968
  • 进程 3 的 id 为 9584

每个事件都打印了一行,包括事件的类型和更新的 Lamport 时间戳。花一分钟时间将时间戳与我们示例中的戳记进行比较,您会发现它们都彼此一致。您可以通过绘制时间线来测试新的示例,并相应地更改过程定义。

Script output with Lamport Timestamps

矢量时钟

如前所述,Lamport 时间戳有一个很大的缺点:它们不能告诉你两个事件何时并发。回到我们的例子,通过检查时间戳,我们可以得出结论,流程 1 中的事件 3 发生在流程 3 中的事件 8 之前,但这不一定是真的。

Compare event 3 in process 1 with event 8 in process 2

如果事件 3 需要几秒钟,这不会影响事件 8,因此事件 8 将在事件 3 之前执行。您可以通过在流程 1 中执行事件 3 之前添加一点延迟来轻松验证这一点。

from time import sleepdef process_one(pipe12):
    pid = getpid()
    counter = 0
    counter = event(pid, counter)
    counter = send_message(pipe12, pid, counter)
    sleep(3)
    counter  = event(pid, counter)
    counter = recv_message(pipe12, pid, counter)
    counter  = event(pid, counter)

请注意,我们的事件顺序发生了变化,而我们的时间戳保持不变。

Output with delay in process 15104

矢量时钟通过使用矢量计数器而不是整数计数器来解决这个问题。具有 N 个进程的系统的向量时钟是 N 个计数器的向量,每个进程一个计数器。向量计数器必须遵循以下更新规则:

  • 最初,所有的计数器都是零(在我们的例子中是*【0,0,0】*)
  • 每当一个进程经历一个事件,它就把自己在 vector 中的计数器加 1。
  • 每当一个进程发送一条消息时,它都会在消息中包含一个它自己的(递增的)向量的副本。
  • 每当一个进程接收到一条消息,它就将自己在向量中的计数器加 1,并通过取自己的向量计数器中的值和接收到的消息的向量中的值的最大值来更新其向量中的每个元素。

将向量时钟应用于我们之前的示例时,我们为每个事件获取一个向量,如下图所示。

Vector Clocks example

考虑我们之前的例子,我们比较了流程 1 中的第三个事件和流程 2 中的第四个事件。向量读作*【3,0,0】【2,4,2】*。对于在另一个事件之前发生的一个事件,其向量的所有元素需要小于或等于另一个向量中的匹配元素

这里显然有冲突( 3 > 2 和 0 < 4 ),因此我们可以断定这些事件是同时发生的,并没有相互影响。

要将我们的代码转换成矢量时钟,我们需要做一些最小的调整。最大的问题在于当我们收到一条消息时,我们如何计算新的计数器。因为我们将使用数组,所以我们需要为每个元素计算消息中的计数器和流程的计数器之间的最大值。

def calc_recv_timestamp(recv_time_stamp, counter):
    for id  in range(len(counter)):
        counter[id] = max(recv_time_stamp[id], counter[id])
    return counter

接下来,同样因为我们使用数组,我们需要替换

counter += 1

经过

counter[pid] += 1

在我们所有的活动中。

最后,我们希望通过用长度为 3 的向量替换初始计数器,并根据它在向量中的位置手动设置我们的流程 id,来更改我们的流程定义。对于过程一

pid = getpid()
counter = 0

应替换为

pid = 0
counter = [0,0,0]

如果您对所有进程进行相应的更改并运行您的代码,您将得到以下输出。将输出与我们的示例进行比较,得出结论,它工作正常。

Output of Vector Clocks script

一旦你正确理解了 Lamport 时间戳和向量时钟是如何工作的,看看其他一些逻辑时钟可能会很有趣,比如矩阵时钟版本向量间隔树时钟。或者,用于跟踪分布式系统中数据变化的其他机制,例如散列历史

—请随时在评论中或私信中向我指出任何不一致或错误。—

来源

Time, Clocks, and the Ordering of Events in a Distributed System, Leslie Lamport, 1978

[## 云计算概念,第 1 部分| Coursera

关于本课程:今天的云计算系统,无论是开源的还是在公司内部使用的,都是使用…

www.coursera.org](www.coursera.org/learn/cloud…) [## 偏序集-维基百科

在数学中,尤其是在序论中,偏序集(也称偏序集)形式化并概括了直觉…

en.wikipedia.org](en.wikipedia.org/wiki/Partia…)

了解学习率及其如何提高深度学习的性能

原文:towardsdatascience.com/understandi…

这篇文章试图记录我对以下主题的理解:

  • 学习率是多少?它的意义是什么?
  • 一个人如何系统地达到一个好的学习率?
  • 为什么我们在训练时要改变学习率?
  • 使用预训练模型时,我们如何处理学习率?

这篇文章的大部分内容是基于过去 fast.ai 研究员写的东西[1]、[2]、[5]和[3]。这是它的一个简明版本,以一种能让人快速得到材料实质的方式安排。请仔细阅读参考资料,了解更多细节。

首先,什么是学习率?

学习率是一个超参数,它控制我们根据损失梯度调整网络权重的程度。该值越低,我们沿下坡行驶的速度越慢。虽然在确保我们不会错过任何局部最小值方面,这可能是一个好主意(使用低学习率),但这也可能意味着我们将需要很长时间才能收敛——特别是如果我们被困在一个平坦区域。

下面的公式显示了这种关系。

new_weight = existing_weight — learning_rate * gradient

Gradient descent with small (top) and large (bottom) learning rates. Source: Andrew Ng’s Machine Learning course on Coursera

通常,学习速率是由用户天真地随机配置的。在最好的情况下,用户将利用过去的经验(或其他类型的学习材料)来获得在设置学习率时使用的最佳值的直觉。

因此,通常很难做到正确。下图展示了配置学习率时可能遇到的不同情况。

Effect of various learning rates on convergence (Img Credit: cs231n)

此外,学习率影响我们的模型能够多快收敛到局部最小值(也就是达到最佳精度)。因此,从一开始就把它做好意味着我们训练模型的时间会更少。

**Less training time, lesser money spent on GPU cloud compute. :)**

有没有更好的方法来确定学习率?

的第 3.3 节中,用于训练神经网络的循环学习率[4]les lie n . Smith 认为,可以通过最初以非常低的学习速率训练模型,并在每次迭代中增加学习速率(线性或指数)来估计良好的学习速率。

Learning rate increases after each mini-batch

如果我们记录每次迭代的学习,并绘制学习率(log)与损失的关系图;我们会看到,随着学习率的增加,会有一个点,损耗停止减少,开始增加。实际上,我们的学习率应该理想地在图表最低点的左边(如下图所示)。在这种情况下,0.001 到 0.01。

以上似乎有用。我该如何开始使用它?

目前,它作为 fast.ai 包中的一个函数受到支持,由杰瑞米·霍华德开发,作为一种抽象 pytorch 包的方法(很像 Keras 是 Tensorflow 的抽象)。

在训练神经网络之前,只需要键入以下命令就可以开始寻找最佳学习速率。

让它变得更好

在这个节骨眼上,我们已经讨论了学习率是什么,它的重要性,以及当我们开始训练我们的模型时,我们如何系统地得出一个最佳值。

接下来,我们将讨论如何使用学习率来提高模型的性能。

传统智慧

典型地,当一个人设置他们的学习速率并训练模型时,他将只等待学习速率随着时间的推移而降低,并等待模型最终收敛。

然而,随着梯度达到稳定水平,训练损失变得更难改善。在[3]中, Dauphin 等人认为最小化损失的困难来自鞍点而不是不良的局部最小值。

A saddle point in the error surface. A saddle point is a point where derivatives of the function become zero but the point is not a local extremum on all axes. (Img Credit: safaribooksonline)

那么,我们如何摆脱这种情况呢?

我们可以考虑几个选择。一般来说,引用[1]中的话,

…如果训练不再改善我们的损失,我们将根据某种循环函数 f 在每次迭代中改变学习率,而不是使用一个固定的学习率值并随时间减少。就迭代次数而言,每个周期具有固定的长度。这种方法让学习率在合理的边界值之间循环变化。这很有帮助,因为如果我们在鞍点上停滞不前,提高学习速率可以更快地穿越鞍点平台。

在[2]中,Leslie 提出了一种“三角形”方法,其中学习速率在每几次迭代后重新开始。

‘Triangular’ and ‘Triangular2’ methods for cycling learning rate proposed by Leslie N. Smith. On the left plot min and max lr are kept the same. On the right the difference is cut in half after each cycle.

另一种同样流行的方法叫做热重启随机梯度下降,由 Loshchilov&Hutter【6】提出。这种方法基本上使用余弦函数作为循环函数,并在每个循环中以最大值重新开始学习速率。“暖”位来自于当学习率重新启动时,并不是从零开始;而是根据模型在最后一步[7]中收敛的参数。

虽然有各种变化,下图展示了它的一种实现,其中每个周期都设置为相同的时间段。

SGDR plot, learning rate vs iteration.

因此,我们现在有了一种减少训练时间的方法,基本上就是周期性地绕着“山”跳(如下)。

Comparing fixed LR and Cyclic LR (img credit: arxiv.org/abs/1704.00…)

除了节省时间之外,研究还表明,使用这些方法可以在不调整和较少迭代的情况下提高分类精度。

迁移学习中的学习速率

在 fast.ai 课程中,非常强调在解决人工智能问题时利用预训练模型。例如,在解决图像分类问题时,学生将学习如何使用 VGG 或 Resnet50 等预训练模型,并将其连接到您想要预测的任何图像数据集。

为了总结如何在 fast.ai(该程序,不要与 fast.ai 包混淆)中建立模型,下面是我们通常会采取的几个步骤:

1.启用数据扩充,预计算=真

2。使用 **lr_find()** 找到损失仍在明显改善的最高学习率

3.从预计算的激活中训练最后一层,持续 1-2 个时期

4.在 cycle_len=1 的情况下,对最后一层进行 2–3 个历元的数据扩充训练(即 precompute=False)

5.解冻所有层

6。将前几层的学习速率设置为比下一个更高层低 3-10 倍

7。再次使用**lr_find()**

8.用 cycle_mult=2 训练整个网络,直到过度拟合

从上面的步骤中,我们注意到步骤 2、5 和 7 与学习速度有关。在这篇文章的前面部分,我们已经基本上涵盖了提到的步骤的第二项——在那里我们谈到了如何在训练模型之前获得最佳的学习率。

在接下来的部分中,我们回顾了如何通过使用 SGDR,我们能够减少训练时间,并通过不时地重新启动学习速率来避免梯度接近零的区域,从而提高精度。

在最后一节中,我们将讨论差分学习,以及当训练模型与预训练模型相关联时,如何使用差分学习来确定学习率。

什么是差异学习?

这是一种在训练期间为网络中的不同层设置不同学习速率的方法。这与人们通常配置学习速率的方式形成对比,后者是在训练期间在整个网络中使用相同的速率。

One reason why I just love Twitter — direct answer from the man himself.

写这篇文章的时候,Jeremy 和 Sebastian Ruder [9]发表了一篇论文,深入探讨了这个话题。所以我猜想差别学习率现在有了一个新名字——区别性微调 。:)

为了更清楚地说明这个概念,我们可以参考下图,其中一个预训练模型被分成 3 组,每组将配置一个递增的学习率值。

Sample CNN with differential learning rate. Image credit from [3]

这种配置方法背后的直觉是,前几层通常会包含非常细粒度的数据细节,如线条和边缘,我们通常不希望对其进行太多更改,而是希望保留其信息。因此,没有必要大幅度改变它们的权重。

相比之下,在后面的图层中,如上面绿色的图层,我们可以获得数据的详细特征,如眼球、嘴或鼻子;我们不一定需要保留它们。

这与其他微调方法相比如何?

在[9]中,有人认为微调整个模型的成本太高,因为有些模型可能有 100 多个层。因此,人们通常做的是一次一层地微调模型。

然而,这引入了顺序要求,阻碍了并行性,并且需要多次通过数据集,导致对小数据集的过度拟合。

还证明了[9]中介绍的方法能够在各种 NLP 分类任务中提高准确性并降低错误率(如下)

Results taken from [9]

参考资料:

【1】用学习率改善我们的工作方式。

【2】循环学息术

【3】利用差异学习率进行迁移学习。

【4】莱斯利·n·史密斯。训练神经网络的循环学习率。

【5】估计深度神经网络的最优学习速率

【6】随机梯度下降带温重启

【7】2017 年深度学习优化亮点

8】第一课笔记本,fast.ai 第一部分 V2

【9】用于文本分类的微调语言模型

了解测量级别

原文:towardsdatascience.com/understandi…

因此,我想成为一名数据炼金术士(更普遍的说法是数据科学家)。炼金术士是试图将金属物质转化为黄金的人。在我的情况下,唯一的区别是我的金属是数据,而我的黄金是我希望数据承认的令人瞠目结舌的洞察力!

所以,如果你还在读这篇文章,你可能是这个社区的一员,你可能知道统计学在我们的技能组合中占有重要地位。在这篇文章中,我将使用一个故事来阐述我对测量及其水平的理解。当然,它必须是一个故事,毕竟数据科学家是高超的故事讲述者!

Our Day Dreaming Friend, Bob..Yeah, of course, he has to look cute ;)

见见我的朋友,鲍勃。他是一个非常有创造力和想象力的学生,正在从事统计学方面的职业。一天,大学毕业后,他决定去一家比萨饼店吃午饭。那里有很多顾客,鲍勃不得不等着下订单。

鲍勃是一个想象力丰富、沉浸在自己世界里的男孩,他只是瞥了一眼菜单。他进入了自己的统计世界。他注意到比萨饼的类别是名义上的数据。名义数据通常被称为分类数据,因为它们只是将给定的数据放入定义的类别中。例如,比萨饼可以有蔬菜或非蔬菜配料。名义上的数据并不能说明顺序,我们无法仅仅通过所用的配料来描述“披萨的美味”程度。另一个最好的例子是一个人的性别,即男性、女性或变性人

鲍勃然后意识到比萨饼的尺寸是有序的类型。顾名思义,序数数据清楚地表明一个有意义的顺序。鲍勃可以在菜单上看到小号、中号和大号比萨饼,清楚地表明了等级的顺序。然而,顺序变量并不意味着两组值(区间)之间的差是相等的。这意味着我们不能说小号和中号披萨的区别等于中号和大号披萨的区别。

鲍勃的白日梦被服务员打断了,因为他要求鲍勃点菜。Bob 点了他最爱吃的芝士爆鸡烧烤披萨(啊,我都流口水了!)并在比萨饼店四处张望。

在他面前的桌子上,有一家人正在享用他们的比萨饼,一对父母和他们的婴儿在助步车里。这个婴儿的脚太小了,鲍勃确信他的鞋号肯定是零!然后他意识到鞋码是一个区间变量。找到了。一个区间变量在数值之间有一个确定的区间,但缺少一个零点。考虑鞋号,我们可以说鞋号 8 和鞋号 7 的差等于鞋号 2 和鞋号 3 的差。但不代表 6 码是 3 码的 2 倍。当我们说鞋码为零时,并不意味着没有鞋。但它却表示鞋号,也就是说,它是一个任意的零点。

Aww!

鲍勃的奶酪热比萨饼现在已经到了,他的思想现在只集中在比萨饼上。鲍勃狼吞虎咽地吃着比萨饼,吃完后,比萨饼店非常安静。令人惊讶的是,所有的顾客都不见了,包括他前面的那家人。只有服务员和他。

鲍勃很快就离开了这个地方。他的思绪回到了统计世界,他得出结论,一个比萨饼店的顾客数量是一个比率比例。一个秤是区间秤的老大哥。它有明确的间隔,也有一个真正的零点值。这意味着在 Bob 离开比萨店时,顾客为零,即真正没有顾客。而如果有 20 个客户,这实际上意味着现在客户数量的 20 倍。故事结束!

我希望我们的男孩鲍勃已经用他自己的方式帮助你理解了测量的水平。我试图用一个故事来给你最简单的解释。但是为什么要学习测量的级别呢?

了解测量水平有助于我们解释该变量的数据。例如,对于数据变量颜色,,你可以将红色编码为 0,蓝色编码为 1,绿色编码为 2。将分类数据编码成数字是优选的,因为计算机倾向于比字母更容易解释数字。此外,我们可以尽量减少数据输入时出错的机会。例如,输入数据的人会像写“红色”一样写“红色”。“r”和“r”对计算机的意义不同,因此会影响我们的分析。此外,如果我们知道数据是名义上的,我们永远不会平均它。为什么?

Encoding for the music genre

为了更好地理解,考虑上面的音乐流派编码。使用上面的编码,如果我们让六个人选择一种颜色,我们得到的假设数据如下图所示。

它说我们调查的平均反馈是 4。这显然是误导。因此,在分析变量之前,有必要了解变量的测量水平。那都是乡亲们!

唷!我终于在 Medium 上写了我的第一篇博客。请留下您的回复,朋友们!

位置敏感散列法

原文:towardsdatascience.com/understandi…

减少数据维数的有效方法

动机

寻找最近邻居的任务很常见。您可以考虑像查找重复或相似文档、音频/视频搜索这样的应用程序。虽然使用暴力来检查所有可能的组合会给你精确的最近邻,但它根本不可伸缩。完成这项任务的近似算法一直是积极研究的领域。虽然这些算法不能保证给你准确的答案,但通常它们会提供一个很好的近似值。这些算法速度更快,可伸缩性更强。

位置敏感散列(LSH)就是这样一种算法。LSH 有许多应用,包括:

  • 近似重复检测:LSH 通常用于对大量文档、网页和其他文件进行重复数据删除。
  • 全基因组关联研究:生物学家经常使用 LSH 在基因组数据库中识别相似的基因表达。
  • 大规模图片搜索:谷歌使用 LSH 和 PageRank 来构建他们的图片搜索技术。
  • 音频/视频指纹识别:在多媒体技术中,LSH 被广泛用作音频/视频数据的指纹识别技术。

在这篇博客中,我们将试图理解这种算法的工作原理。

大意

是指一族函数(称为 LSH 族)将数据点散列到桶中,使得彼此靠近的数据点大概率位于同一个桶中,而彼此远离的数据点很可能在不同的桶中。这使得识别具有不同相似度的观察结果变得更加容易。

查找相似的文档

让我们试着去理解我们如何利用 LSH 来解决一个实际问题。我们试图解决的问题是:

目标:您已经收到了大量的文档。您希望找到“近似重复”的配对。

在这个问题的背景下,我们可以将 LSH 算法分解为 3 个主要步骤:

  1. 皱缩
  2. 最小哈希
  3. 区分位置的散列法

暂时不要过多地解读这个数字。它只是给你一个流程的概念。我们将详细讨论每个步骤。

叠瓦作用

**在这一步中,我们将每个文档转换成一个长度为 k 的字符集(也称为 k-瓦片区或 k-grams)。关键思想是将我们集合中的每个文档表示为一组 k 瓦片区。

举个例子:你的一个文档(D):“纳达尔”。如果我们对 2-带状疱疹感兴趣,那么我们的集合:{Na,ad,da,al}。类似的 3-带状疱疹集:{Nad,ada,dal}。

  • 相似的文档更有可能共享更多的瓦片区
  • 在改变单词的文档中重新排序段落对带状疱疹没有太大影响
  • 实际中一般采用 8–10的 k 值。较小的值将导致出现在大多数文档中的许多带状疱疹(不利于区分文档

Jaccard 索引

我们以瓦片的形式表示每个文档。现在,我们需要一个度量来衡量文档之间的相似性。Jaccard Index 是一个很好的选择。文档 A & B 之间的 Jaccard 索引可以定义为:

它也被称为(IOU)。**

假设 A:“Nadal”和 B:“Nadia”,那么 2-带状疱疹表示将是:

A: {Na,ad,da,al}和 B: {Na,ad,di,ia}。

雅克卡指数= 2/6

更多数量的公共瓦片区将导致更大的 Jaccard 索引,因此文档更可能是相似的。

让我们讨论一下我们需要解决的两个大问题:

时间复杂度

现在你可能认为我们可以就此打住。但是如果考虑到可伸缩性,仅仅这样做是行不通的。对于 n 个文档的集合,需要做 n(n-1)/2 比较,基本上就是 O(n ) 。假设你有 100 万个文档,那么比较的数量将是 510(完全不可伸缩!).**

空间复杂性

文档矩阵是一个稀疏矩阵,按原样存储它将是一个很大的内存开销。解决这个问题的一种方法是散列法。

散列法

散列的思想是使用散列函数 H 将每个文档转换成一个小签名。 假设我们语料库中的一个文档用 d. 表示,那么:

  • H(d)是签名,它足够小,可以放在内存中
  • 如果相似度(d1,d2) 高,则 概率(H(d1)==H(d2))
  • 如果相似度(d1,d2) 低,则 概率(H(d1)==H(d2))

哈希函数的选择与我们使用的相似性度量紧密相关。对于 Jaccard 相似性,合适的散列函数是最小散列**。**

最小散列法

这是这个算法最关键也是最神奇的地方,所以请注意:

第一步:文档瓦片矩阵行索引随机排列 (π) 。**

**第二步:**哈希函数是 C 列值为 1 的第一行(按排列顺序)的索引。这样做几次(使用不同排列)来创建列的签名。

最小散列属性

签名的相似性是它们一致的最小散列函数(行)的分数。所以 C1 和 C3 的签名相似度是 2/3,因为第一行和第三行是相同的。

****

两个签名的预期相似性等于列的 Jaccard 相似性。签名越长,误差越小

在下面的例子中,你可以在某种程度上看到这一点。有所不同,因为我们只有长度为 3 的签名。但是如果增加长度,这两个相似点会更接近。

因此,使用最小散列法,我们通过消除稀疏性并同时保持相似性的,解决了空间复杂度的问题。在实际实施中,他们是一个创造指数排列的技巧,我不会覆盖,但你可以在 15:52 左右检查这个视频。 Min-hash 实现**

局部敏感散列法

目标:找到 Jaccard 相似度至少为 t 的文档

LSH 的总体思想是找到一种算法,使得如果我们输入两个文档的签名,它告诉我们这两个文档是否形成候选对,即它们的相似性大于阈值 t 。请记住,我们将签名的相似性作为原始文档之间 Jaccard 相似性的代理。

专门针对最小哈希签名矩阵:

  • 使用几个散列函数散列签名矩阵 M 的列
  • 如果对于至少一个散列函数的来说,两个文档散列到同一个桶中,我们可以将这两个文档作为候选对****

现在的问题是如何创建不同的散列函数。为此,我们进行波段划分。

波段划分

算法是这样的:

  • 将签名矩阵分成 b 个带,每个带有 r 行
  • 对于每个带,用 k 个桶将它在每列中的部分散列到一个散列表中
  • 候选列对是那些对于至少 1 个带散列到相同桶的列对
  • 调整 b 和 r 以捕捉最相似的对,但很少捕捉不相似的对

这里的考虑很少。理想情况下,对于每个波段,我们希望 k 等于一个列在一个波段内可以采用的所有可能的值组合。这将等同于身份匹配。但是这样,k 将是一个巨大的数字,在计算上是不可行的。举个例子:如果一个乐队有 5 行。现在,如果签名中的元素是 32 位整数,那么这种情况下的 k 将是(2 )⁵ ~ 1.4615016e+48。你可以看到这里有什么问题。通常 k 取 100 万左右。

其思想是,如果两个文档相似,那么它们将作为候选对出现在至少一个波段中。

选择 b & r

如果我们取 b 大,即更多数量的散列函数,那么我们减少 r,因为 b*r 是常数(签名矩阵中的行数)。直觉上,这意味着我们正在增加找到候选配对的可能性。这个案例相当于取了一个小 t(相似度阈值)

假设你的签名矩阵有 100 行。考虑两种情况:

b1 = 10 → r = 10

b2 = 20 → r = 5

在第二种情况下,两个文档在同一个桶中出现至少一次的可能性更大,因为它们有更多的机会(20 比 10)更少的签名元素被比较(5 比 10)。

更高的 b 意味着更低的相似性阈值(更高的假阳性),而更低的 b 意味着更高的相似性阈值(更高的假阴性)****

我们试着通过一个例子来理解这一点。

设置:

  • 100k 文档存储为长度为 100 的签名
  • 签名矩阵:100*100000
  • 签名的暴力比较将导致 100C2 比较= 50 亿(相当多!)
  • 让我们取 b = 20 → r = 5

相似度阈值(t) : 80%

我们希望 20 个波段中至少有一个波段的两个文档(D1 和 D2)具有 80%的相似性,在同一个桶中进行哈希运算。

P(D1 和 D2 在特定波段相同)= (0.8)⁵ = 0.328

P(D1 和 D2 在所有 20 个波段中不相似)=(1–0.328)^20 = 0.00035

这意味着在这种情况下,我们有大约 0.035%的机会出现 80%相似文档的假阴性。

我们还希望对于 20 个波段中的任何一个(阈值= 80%),具有 30%相似性的 2 个文档(D3 和 D4)不在同一个桶中被散列。

P(D3 和 D4 在特定波段中相同)= (0.3)⁵ = 0.00243

P(D3 和 D4 在 20 个波段中的至少一个波段中相似)= 1-(1–0.00243)^20 = 0.0474

这意味着在这种情况下,我们有大约 4.74%的机会在 30%的相似文档中出现误报。

所以我们可以看到,我们有一些假阳性和几个假阴性。这些比例将随着 b 和 r 选择而变化

我们在这里想要的是像下面这样的东西。如果我们有两个相似度大于阈值的文档,那么它们在至少一个波段中共享同一个桶的概率应该是 1,否则为 0。

最坏的情况是,如果我们有如下所示的签名矩阵中的 b =行数。

任何 b 和 r 的一般情况如下所示。

选择 b 和 r 以获得最佳的 S 曲线,即最小的假阴性和假阳性率

LSH 摘要

  • 调整 M,b,r 以获得几乎所有具有相似签名的文档对,但排除大多数没有相似签名的对
  • 在主存储器中检查候选对确实有相似的签名

结论

我希望你很好地理解这个强大的算法,以及它如何减少搜索时间。你可以想象 LSH 如何适用于几乎任何类型的数据,以及它在当今的大数据世界中有多大的相关性。

要阅读更多关于 LSH 的代码实现,请查看本文。https://santhoshhari.github.io/Locality-Sensitive-Hashing/

参考

  1. joyceho.github.io/cs584_s16/s…
  2. www.youtube.com/watch?v=96W…
  3. **【eng.uber.com/lsh/ **
  4. https://medium . com/engineering-brainly/locality-sensitive-hashing-explained-304 EB 39291 E4
  5. www.mit.edu/~andoni/LSH…

理解逻辑回归

原文:towardsdatascience.com/understandi…

逻辑回归是解决分类问题的基本和流行的算法之一。它被命名为“逻辑回归”,因为它的基本技术与线性回归完全相同。术语“逻辑”取自该分类方法中使用的逻辑函数

本博客旨在回答以下问题:

1.分类问题是什么?

2.为什么不用线性回归?

3.逻辑回归算法?

4.什么是决策边界?

5.如何检查模型性能?

今天,我们来一劳永逸地了解一下逻辑回归。我们开始吧,

什么是分类问题?

当自变量在本质上是连续的并且因变量是分类形式时,即在像正类和负类这样的类中,我们将该问题识别为分类问题。分类实例的实际例子是,将邮件分类为垃圾邮件或非垃圾邮件,将肿瘤分类为恶性或良性,并将交易分类为欺诈性或真实性。所有这些问题的答案都是分类形式的,即是或否。这就是为什么它们是两类分类问题。

[Image 1] (Image courtesy: My Photoshopped Collection)

虽然,有时我们会遇到两个以上的类,但这仍然是一个分类问题。这些类型的问题被称为多类分类问题。

为什么不用线性回归?

假设我们有肿瘤大小与其恶性程度的数据。由于这是一个分类问题,如果我们绘图,我们可以看到,所有的值将位于 01 。如果我们拟合最好的回归线,假设阈值为 0.5,我们可以做线相当合理的工作。

[Image 2] (Image courtesy: Andrew Ng provided image + Photoshopped)

我们可以确定 x 轴上的点,从该点开始,位于其左侧的所有值都被认为是负类,而位于其右侧的所有值都是正类。

[Image 3] (Image courtesy: Andrew Ng provided image + Photoshopped)

但是如果数据中有异常值呢?事情会变得相当混乱。例如,对于 0.5 的阈值,

[Image 4] (Image courtesy: Andrew Ng provided image + Photoshopped)

如果我们拟合最佳回归线,它仍然不足以决定我们可以区分阶级的任何点。它会把一些正面类的例子放到负面类里。绿色虚线(决策边界)将恶性肿瘤与良性肿瘤区分开来,但该线应该位于黄线处,黄线清楚地将阳性和阴性示例区分开来。因此,仅仅一个异常值就会扰乱整个线性回归预测。这就是逻辑回归发挥作用的地方。

逻辑回归算法

如前所述,为了处理异常值,逻辑回归使用 Sigmoid 函数。

解释逻辑回归可以从解释标准逻辑函数开始。逻辑函数是一个 Sigmoid 函数,它取 0 到 1 之间的任何实数值。它被定义为

而如果我们绘制它,图形会是 S 曲线,

[Image 5] (Image courtesy: openclassrooms.com/en/courses/…)

让我们把 t 看作一元回归模型中的线性函数。

所以逻辑方程式会变成

现在,当逻辑回归模型遇到异常值时,它会处理它。

[Image 6] (Image courtesy: Andrew Ng provided image + Photoshopped)

但有时它会根据异常值的位置将其 y 轴向左或向右移动。

什么是决策边界?

决策边界有助于将概率分为正类和负类。

线性决策边界

[Image 7] (Image courtesy: Andrew Ng provided image + Photoshopped)

[Image 8] (Image courtesy: My Photoshopped Collection)

非线性决策边界

[Image 9] (Image courtesy: Andrew Ng provided image + Photoshopped)

[Image 10] (Image courtesy: My Photoshopped Collection)

如何检查绩效?

为了检查性能,我们可以使用混淆矩阵和 AUC - ROC 曲线。要知道它是什么,可以查看我关于混淆矩阵AUC - ROC 曲线的文章。

参考文献:

图片取自吴恩达球场并做了一些修改,以便于理解😁。

感谢阅读。

我希望我已经让你对什么是逻辑回归有了一些了解。如果你喜欢这篇文章,给这篇文章一些掌声会对你有所帮助👏。我随时欢迎你的问题和建议。你可以在脸书、推特、Linkedin 上分享这个,这样有需要的人可能会偶然发现这个。

您可以通过以下方式联系到我:

领英:www.linkedin.com/in/narkhede…

推特:twitter.com/narkhede_sa…

github:github.com/TheSarang

用石灰理解模型预测

原文:towardsdatascience.com/understandi…

在我之前关于模型可解释性的帖子中,我概述了用于研究机器学习模型的常用技术。在这篇博文中,我将对石灰进行更详尽的解释。

LIME explaines of model predictions at the data sample level. It allows end-users to interpret these predictions and take actions based on them. Source

为什么理解可解释性方法是必要的?

如果您信任一种解释模型预测的技术,那么理解该技术的基本机制以及任何与之相关的潜在陷阱是非常重要的。可解释性技术不是防错的,如果对方法没有很好的理解,你很可能将你的假设建立在错误的基础上。

在下面这篇关于兰登森林重要性的博客文章中,做了一个类似但更彻底的调查。要素重要性通常用于确定哪些要素在模型预测中起重要作用。随机森林提供了一种现成的方法来确定数据集中最重要的要素,许多人依赖于这些要素的重要性,将它们解释为数据集的“基本事实解释”。

A decision or random forest consists of multiple decision trees. By investigating which features are used to construct the ‘best’ trees, it is possible to get an estimate of the feature importance. Source

作者研究了两个随机森林(RF)实现以及它们提供的特性重要性的标准度量。作者表明,与随机森林重要性相比,当变量强相关时,排列重要性提供了更稳健的估计。我强烈推荐阅读他们的博客文章,以全面了解这些发现。

石灰

LIME 是模型不可知的,这意味着它可以应用于任何机器学习模型。该技术试图通过干扰数据样本的输入和理解预测如何变化来理解模型。

LIME assumes a black box machine learning model and investigates the relationship between input and output, represented by the model.

特定于模型的方法旨在通过分析内部组件以及它们如何交互来理解黑模型机器学习模型。在深度学习模型中,例如,可以调查激活单元并将内部激活链接回输入。这需要对网络有透彻的了解,并且不能扩展到其他模型。

LIME 提供了本地模型的可解释性。LIME 通过调整特征值来修改单个数据样本,并观察对输出产生的影响。通常,这也与人类在观察模型输出时感兴趣的内容有关。最常见的问题可能是:为什么会做出这种预测,或者是哪些变量导致了这种预测?

其他模型可解释性技术仅从整个数据集的角度回答了上述问题。要素重要性在数据集层面上解释了哪些要素是重要的。它允许您验证假设以及模型是否过度适应噪声,但是很难诊断特定的模型预测。

LIME attempts to play the role of the ‘explainer’, explaining predictions for each data sample. Source

石灰背后的直觉

LIME 的一个关键要求是使用人类可以理解的可解释的输入表示。可解释表示的例子是例如 NLP 的弓形向量,或者计算机视觉的图像。另一方面,密集嵌入是不可解释的,应用石灰可能不会提高可解释性。

LIME 的输出是一个解释列表,反映了每个特征对数据样本预测的贡献。这提供了局部可解释性,并且还允许确定哪些特征变化将对预测产生最大影响。

An example of LIME applied to a classic classification problem. Source

一个解释是通过用一个可解释的模型局部地逼近底层模型而产生的。可解释的模型是例如具有强正则化的线性模型、决策树等。可解释的模型在原始实例的小扰动上被训练,并且应该仅提供良好的局部近似。“数据集”是通过例如向连续特征添加噪声、移除单词或隐藏图像的部分来创建的。通过仅局部地近似黑盒*(在数据样本的邻域中),任务被显著简化。*

潜在的陷阱

虽然酸橙的一般概念听起来很容易,但有几个潜在的缺点。

在当前的实现中,仅使用线性模型来近似局部行为。在某种程度上,当观察数据样本周围非常小的区域时,这种假设是正确的。然而,通过扩展这个区域,线性模型可能不足以解释原始模型的行为。对于那些需要复杂的、不可解释的模型的数据集,在局部区域会发生非线性。不能在这些场景中使用石灰是一个很大的缺陷。

A linear approximation of the local behaviour for two features is not a good representation and won’t capture the highly non-linear behaviour of the model.

其次,需要对数据执行的修改类型通常是特定于用例的。作者在他们的论文中给出了以下例子: 例如,预测棕褐色调图像复古的模型不能用超级像素的存在与否来解释。

通常,简单的扰动是不够的。理想情况下,扰动将由数据集中观察到的变化驱动。另一方面,手动控制扰动可能不是一个好主意,因为它很可能会在模型解释中引入偏差。

结论

LIME 是解释机器学习分类器(或模型)在做什么的一个很好的工具。它是模型不可知的,利用简单和可理解的想法,并且不需要很多努力来运行。和往常一样,即使使用 LIME,正确解释输出仍然很重要。

如果你对机器学习的可解释性有任何问题,我很乐意在评论中阅读。如果你想收到我博客上的更新,请在 MediumTwitter 上关注我!

理解神经网络:什么,如何和为什么?

原文:towardsdatascience.com/understandi…

解开黑盒

N 就称为深度学习的机器学习子领域而言,神经网络是最强大和最广泛使用的算法之一。乍一看,神经网络似乎是一个黑盒;输入层将数据输入到“隐藏层中,变戏法之后,我们可以看到由输出层提供的信息。然而,理解隐藏层在做什么是神经网络实现和优化的关键步骤。

在我们理解神经网络的道路上,我们将回答三个问题:什么如何为什么

什么是神经网络?

我们将要考虑的神经网络被严格地称为人工神经网络,顾名思义,它是基于科学对人脑结构和功能的了解。

简而言之,神经网络被定义为一种计算系统,它由许多简单但高度互连的元素或节点组成,这些元素或节点被称为“神经元”,它们被组织成使用对外部输入的动态状态响应来处理信息的层。正如我们将在后面解释的那样,这种算法在寻找太复杂而无法手动提取和教会机器识别的模式方面非常有用。在这种结构的背景下,模式通过输入 被引入神经网络,该层对于输入数据中存在的每个分量具有一个神经元,并且被传送到网络中存在的一个或多个隐藏层;被称为“隐藏”仅仅是因为它们不构成输入或输出层。在隐藏层中,所有的处理实际上是通过一个连接系统发生的,该连接系统的特征是**(通常称为 W 和 b)* : 接收输入,神经元计算加权和,并根据结果加上偏差和预设的激活函数(最常见的是 sigmoid,ψ 即使它几乎不再被使用,并且有更好的像 ReLu ) 一样,它决定它是否应该被‘发射’或激活。 然后,神经元将信息向下游传输到其他连接的神经元,这个过程称为“前向传递”。在这个过程的最后,最后一个隐藏层被链接到输出层,它有一个神经元用于每个可能的期望输出。*

Basic structure of a 2-layer Neural Network. Wi: Weight of the corresponding connection. Note: The input layer is not included when counting the number of layers present in the network.

神经网络是如何工作的?

现在我们对神经网络的基本结构有了一个概念,我们将继续解释它是如何工作的。为了做到这一点,我们需要解释我们可以在网络中包含的不同类型的神经元。

我们要解释的第一类神经元是感知器* 。尽管今天它的使用已经衰退,但理解它们如何工作将为我们提供更多现代神经元如何运作的线索。*

感知器使用函数通过将二进制变量的向量映射到单个二进制输出来学习二进制分类器,并且它也可以用于监督学习。在这种情况下,感知器遵循以下步骤:

  1. 将所有的输入乘以它们的权重 w ,实数表示对应的输入对输出有多重要,
  2. 将它们加在一起称为 加权和:∑ wj xj
  3. 应用 激活函数 ,换句话说,确定加权和是否大于一个阈值,其中-阈值相当于偏差,并赋值 1 或更少,赋值 0 为输出**

我们也可以用下面的术语来写感知器函数:

Notes: b is the bias and is equivalent to -threshold, w.x is the dot product of w, a vector which component is the weights, and x, a vector consisting of the inputs.

这种算法的最大优点之一是我们可以改变权重和偏差来获得不同的决策模型。我们可以赋予这些输入更多的权重,这样如果它们是积极的,它将有利于我们期望的输出。此外,因为偏差可以理解为输出 1 的难易程度的度量,所以如果我们想使期望的输出更可能或更不可能发生,我们可以降低或提高它的值。如果我们注意公式,我们可以观察到一个大的正偏差会使输出 1 变得非常容易;然而,非常负的偏差将使输出 1 的任务变得非常不可能。

因此,感知器可以分析不同的证据或数据,并根据设定的偏好做出决定。事实上,有可能创建更复杂的网络,包括更多层的感知器,其中每一层都采用前一层的输出并对其进行加权,从而做出越来越复杂的决策。

**什么等一下:如果感知器可以很好地做出复杂的决定,为什么我们需要其他类型的神经元?包含感知器的网络的缺点之一是,即使只有一个感知器,权重或偏差的微小变化也会严重改变我们的输出,从 0 变为 1,反之亦然。我们真正想要的是能够通过引入权重或偏差的小修改来逐渐改变我们网络的行为。这就是一种更现代的神经元类型派上用场的地方(如今它的用途已被其他类型取代,如 Tanh 和最近的 ReLu): ***乙状结肠神经元。*sigmoid 神经元和感知器的主要区别在于,输入和输出可以是 0 到 1 之间的任何连续值。考虑到权重 w 和偏差 b,将 sigmoid 函数 应用于输入后,获得输出。为了更好地形象化,我们可以写下:

因此,输出的公式是:

如果我们对这个函数进行数学分析,我们可以制作函数 σ 的图表,如下所示,并得出结论:当 z 大且为正时,函数达到其最大渐近值 1;然而,如果 z 很大并且是负的,则函数达到其最小渐近值 0。这就是 sigmoid 函数变得非常有趣的地方,因为在 z 值适中的情况下,函数呈现出平滑且接近线性的形状。在此区间内,权重(δwj)或偏置(δbj)的微小变化将导致输出的微小变化;我们所期望的行为是对感知机的改进。

Shape of the sigmoid function used in sigmoid neurons to obtain small changes in the output making small changes in weights or bias. z=-∑wj xj-b

我们知道,函数的导数是值 y 相对于变量 x 的变化率的度量。在这种情况下,变量 y 是我们的输出,变量 x 是权重和偏差的函数。我们可以利用这一点,利用导数,特别是偏导数(相对于 w 和 b)来计算输出的变化。你可以阅读这篇文章来进行计算,但是在 sigmoid 函数的情况下,导数将被简化为计算:f(z)*(1-f(z))。

这是一个简单的代码,可用于模拟 sigmoid 函数:

我们刚刚解释了我们网络中每个神经元的功能,但是现在,我们可以检查它的其余部分是如何工作的。一种神经网络,其中一层的输出被用作下一层的输入,这种神经网络被称为前馈,特别是因为它不涉及环路,信息只向前传递,从不向后传递。

假设我们有一个训练集,我们想使用一个 3 层神经网络,其中我们也使用我们上面看到的 sigmoid 神经元来预测某个特征。以我们对神经网络结构的解释为例,权重和偏差需要首先分配给一层和下一层神经元之间的连接。通常,偏差和权重都是在突触矩阵中随机初始化的。如果我们用 python 对神经网络进行编码,我们可以使用 Numpy 函数np.random.random 生成一个高斯分布(其中均值等于 0,标准差等于 1 ),以便有一个开始学习的地方。

之后,我们将构建神经网络,从 前馈 步骤开始计算预测输出;换句话说,我们只需要构建网络中涉及的不同层:

  • layer0是输入层;我们的训练集读作一个矩阵(我们可以称之为 X)
  • layer1是通过应用激活函数 a’=σ(w . X+b)获得的,在我们的例子中,执行输入layer0和突触矩阵syn0之间的点乘
  • layer2layer1与其突触syn1点乘得到的输出层

我们还需要迭代训练集,让网络学习(我们将在后面看到)。为了做到这一点,我们将为 循环添加一个**

到目前为止,我们已经创建了神经网络的基本结构:不同的层,神经元之间连接的权重和偏差,以及 sigmoid 函数。但是这些都不能解释神经网络如何在预测数据集中的模式方面做得如此好。这是我们的最后一个问题。

为什么神经网络能够学习?

机器学习算法的主要优势在于它们每次在预测输出时都能够学习和改进。但这意味着他们可以学习吗?在神经网络的背景下,它意味着定义神经元之间连接的权重和偏差变得更加精确;最终,选择权重和偏差,使得网络的输出接近所有训练输入的真实值 y(x)。

那么,我们如何量化我们的预测与我们的真实值有多远,以便我们知道我们是否需要继续寻找更精确的参数?为此,我们需要计算一个误差,或者换句话说,定义一个 成本函数 (成本函数不是预测我们的网络的正确输出的误差;换句话说,就是预期输出和预测输出之间的差异)。在神经网络中,最常用的是二次成本函数,也称为均方误差,由以下公式定义:

w and b referred to all the weights and biases in the network, respectively. n is the total number of training inputs. a is the outputs when x is the input. ∑ is the sum over all training inputs.

该函数优于线性误差,因为在神经网络中,权重和偏差的微小变化不会产生正确输出数量的任何变化;因此,使用二次函数(其中大的差异对成本函数的影响大于小的差异)有助于找出如何修改这些参数。

另一方面,我们可以看到,对于所有训练输入,随着输出更接近真实值 y ,我们的成本函数变得更小。我们算法的主要目标是通过找到一组权重和偏差使其尽可能小来最小化这个成本函数。而实现这个目标的主要工具就是一种叫做 梯度下降 的算法。

那么,我们应该回答的下一个问题是,我们如何使成本函数最小化。从微积分中,我们知道一个函数可以有全局最大值和/或最小值,也就是函数达到它所能有的最大值或最小值的地方。我们也知道,得到那个点的一个方法是计算导数。然而,当我们有一个具有两个变量的函数时,很容易计算,但是在神经网络的情况下,它们包括许多变量,使得这种计算很难进行。

相反,让我们来看看下图中的随机函数:

我们可以看到这个函数有一个全局最小值。我们可以,如前所述,计算导数,来计算最小值在哪里,或者我们可以采取另一种方法。我们可以从一个随机点开始,尝试在箭头方向上做一个小的移动,从数学上来说,我们可以在 x 方向上移动δx,在 y 方向上移动δy,然后计算我们的函数δc 的变化,因为一个方向上的变化率是一个函数的导数,我们可以将函数的变化表示为:

这里,我们将从函数梯度的微积分中得到定义:

Gradient of a function: Vector with partial derivatives

现在,我们可以将函数中的变化重写为:

Gradient of C relates the change in function C to changes in (x,y)

现在,我们可以看到,当我们选择参数的某个变化时,成本函数会发生什么。我们选择向任何方向移动的量被称为学习速率,它定义了我们向全局最小值移动的速度。如果我们选择一个非常小的数字,我们将需要进行太多的移动来达到这一点;但是,如果我们选择了一个非常大的数字,我们就有可能过了这个点,永远也到不了。因此,挑战在于选择足够小的学习率。选择学习率后,我们可以更新我们的权重和偏差,并再次移动;我们在每次迭代中重复的过程。

因此,简而言之,梯度下降法的工作原理是重复计算梯度∇C,然后更新权重和偏差,并试图找到正确的值,从而最小化函数的成本。这就是神经网络学习的方式。

**有时,计算梯度可能非常复杂。然而,有一种叫做随机梯度下降的方法可以加速这种计算。这通过计算随机选择的训练输入的小样本的梯度来估计梯度∇C。然后,对这些小样本进行平均,以获得对真实梯度的良好估计,加速梯度下降,从而更快地学习。

**但是等一下?我们如何计算成本函数的梯度?这里是另一个算法进入的地方: 反向传播 。该算法的目标是计算成本函数相对于任何权重 w 和任何偏差 b 的偏导数;在实践中,这意味着从最后一层开始计算误差向量,然后将其传播回来以更新权重和偏差。我们需要返回的原因是,成本是我们网络输出的函数。我们需要计算几个计算和误差,其公式由反向传播算法给出:1)输出误差(δL)与梯度(▽C)对激活函数(σ′(z)的导数的逐元素(⦿)乘积相关, 2)一层的误差(ẟl ),根据与权重的转置矩阵(Wl+1)乘以下一层的误差(ẟl+1)和激活函数的导数的逐元素乘法相关的下一层的误差,3)成本相对于网络中任何偏差的变化率:这意味着 c 相对于任何偏差(∂C/∂bj)的偏导数等于误差ẟl, 4)成本相对于网络中任何权重的变化率:意味着 c 相对于任何权重的偏导数(∂C/∂wj)等于误差(ẟl)乘以神经元输入的激活。 这最后两个计算构成了成本函数的梯度。在这里,我们可以观察公式。

Four essential formulas given by backpropagation algorithms that are useful to implement neural networks

反向传播算法只为一个训练示例计算成本函数的梯度。因此,我们需要将反向传播与学习算法相结合,例如随机梯度下降,以便计算所有训练集的梯度。

现在,我们如何在 python 中将它应用到我们的神经网络中呢?。在这里,我们可以看到一步一步的计算:

让我们把一切都包起来…

现在,我们可以把所有这些公式和概念放在一个算法中,看看我们如何实现它:

  • 输入:我们输入一组训练样本,我们设置输入层对应的激活 a
  • 前馈:对于每一层,我们计算函数 z = w . a + b,即 a = σ(z)
  • 输出误差:我们使用上面引用的公式#1 计算输出误差。
  • 反向传播:现在我们反向传播误差;对于每一层,我们计算上面引用的公式#2。
  • 输出:我们使用公式#3 和#4 计算任何重量和偏差的梯度下降。

当然,神经网络可以有更多的概念、实现和改进,在过去的几年里,神经网络可以变得越来越广泛地使用和强大。但我希望这些信息能给你一点提示,告诉你什么是神经网络,它是如何工作的,以及如何使用梯度下降和反向传播来学习。

参考文献:

理解数字总和

原文:towardsdatascience.com/understandi…

如果你不清楚 NumPy 是什么或者它是如何工作的,请先看看这篇文章。

[## Python 数据科学的第一步— NumPy

我读到过学习的最好方法是写博客。因为我学习数据科学已经有一段时间了…

towardsdatascience.com](/first-step-in-data-science-with-python-numpy-5e99d6821953)

在那篇介绍 NumPy 的文章中,我在 NumPy 数组上做了一个按行添加的操作。然后,帖子的一位读者回应说,我所做的是列方向的添加,而不是行方向的添加。事实上,当我学习它的时候,我同样觉得它不应该这样工作。这与它应该如何工作正好相反。所以,我看了一下文件,但是上面说

我们可以对数组的每一行求和,在这种情况下,我们沿着列或轴 1 进行操作

没用。我还是很困惑。我继续寻找,然后我发现了Aerin Kim的这篇文章,它改变了我看待 NumPy 数组求和的方式。所以以她的帖子为基础,这是我对 NumPy 数组和的看法。

如果你不清楚问题是什么,让我们正式定义它。

问题

我们有一个 5x5 NumPy 阵列,如下所示

a = array([[ 12., -22., -20., -19.,  -3.],
       [-23.,  21., -17., -11.,  -1.],
       [ -4.,  -5.,  16.,  -9., -14.],
       [-10.,  -6., -18.,  15.,  -8.],
       [-25.,  -2., -13.,  -7.,  24.]])

我们在上面的数组中使用

sum_matrix = a.sum(axis=1)

我们最终在sum_matrix中得到下面的数组

array([-52., -31., -16., -27., -23.])

如果axis=1指的是逐行相加,这似乎是正确的。没问题。除此之外,axis=0实际上应该是指行而不是axis=1。当我们看到 NumPy 数组是如何形成的时,这一点会更清楚。让我们取另一个 NumPy 数组。

arr = np.arange(12).reshape(4,3)

我们在arr得到的是

array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])

很明显,它是 4 行 3 列。因此,第一个轴axis=0应该表示行,第二个轴axis=1应该表示列。

如果你观察熊猫,这一点也会得到证实。让我们举一个数据帧的例子。

df = pd.DataFrame(data=np.arange(12).reshape(4,3),index=['row1','row2','row3','row4'],columns=['col1','col2','col3'])

df看起来是这样的

如果你现在做df.drop(labels=[‘row1’], axis=0),你会得到的回报是

如果你做了df.drop(labels=[‘col2’], axis=1),你会得到

显然,axis=0表示行,axis=1表示列。那么,为什么 NumPy sum 的做法不同呢?

解决办法

引用 Aerin Kim 在她的帖子中的话,她写道

的方式来理解 numpy 的 求和就是将 折叠 指定轴。所以当它折叠轴 0(行)时,它就变成了一行和一列的总和。

让我们看看这是什么意思。现在,它在 2D 可能会变得有点混乱,所以让我们首先在一个更高的维度上理解这一点,然后我们将逐步进入 2D;就像她在岗位上做的一样。

所以,我们来取一个形状为(4,3,2)的 3D 数组。

three_d_array = np.arange(24).reshape(4,3,2)

three_d_array现在变成等于

array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[ 6,  7],
        [ 8,  9],
        [10, 11]],

       [[12, 13],
        [14, 15],
        [16, 17]],

       [[18, 19],
        [20, 21],
        [22, 23]]])

现在,让我们看看沿第一轴的元素,axis=0。沿着axis=0我们有 4 个元素。这些可以用three_d_array[0]three_d_array[1]three_d_array[2]three_d_array[3]查看

每一个都是形状为(3,2)的 2D 阵列。现在,如果我们把上面所有的 2D 阵列加起来,

three_d_array[0]+three_d_array[1]+three_d_array[2]+three_d_array[3]

在逐个元素相加之后,我们得到一个 3x2 的数组,如下所示:

array([[36, 40],
       [44, 48],
       [52, 56]])

这正是我们做three_d_array.sum(axis=0)时得到的结果。我们将带走axis=0的所有元素。然后我们一个元素一个元素地求和。最初,我们有一个 3D 形状数组(4,3,2)。求和之后,我们得到了一个形状为(3,2)的 2D 数组。于是,我们丢掉了第一轴4,保留了剩下的两根(3,2)。这可能就是 Kim 所说的“它会折叠轴”的意思。

现在,我们来看看axis=1。这次我们保持第一个轴不变,沿着第二个轴求和,axis=1。这里,我们有 12 个元素,从第一个轴 0 开始,每个轴上有 3 个元素。

加上所有这些,我们回到

类似地,我们为剩下的三个添加

将所有 4 个组合起来,我们得到一个如下所示的数组:

array([[ 6,  9],
       [24, 27],
       [42, 45],
       [60, 63]])

这正是我们做three_d_array.sum(axis=1)时得到的;沿着axis=1逐个元素执行加法。同样,和矩阵的形状是(4,2),这表明我们从原来的(4,3,2)中去掉了第二轴3

对于最后的轴 2,我们做同样的事情。这次我们有所有 24 个元素,我们通过保持前两个轴不变来沿着axis=2求和。这里,我们只看前 6 个元素。

这给了我们一行 sum 数组。我们对剩下的部分做同样的事情,最后我们得到了下面的数组,

array([[ 1,  5,  9],
       [13, 17, 21],
       [25, 29, 33],
       [37, 41, 45]])

这正是我们在three_d_array.sum(axis=2)上得到的。同样,求和数组的形状是(4,3),这里我们失去了最后一个轴2

最后,回到最初的问题

我们的阵列是

a = array([[ 12., -22., -20., -19.,  -3.],
       [-23.,  21., -17., -11.,  -1.],
       [ -4.,  -5.,  16.,  -9., -14.],
       [-10.,  -6., -18.,  15.,  -8.],
       [-25.,  -2., -13.,  -7.,  24.]])

现在,当我们沿着axis=0移动元素时,我们得到

一个元素一个元素地添加这些元素给我们

这相当于a.sum(axis=0)

类似地,当我们沿着axis=1提取元素时,

现在,如果我们分别对所有的a[0]s、所有的a[1]s、所有的a[2]s、所有的a[3]s和所有的a[4]s求和,我们得到

这就是我们用a.sum(axis=1)得到的。如果我们检查形状,我们得到(5,)。如果我们只看 2D 数组,这可能不清楚,但正如我们前面看到的,这只是折叠轴 1 并返回剩余轴 0 的形状。

现在,如果我们回头看看文档中的语句,“我们可以对数组的每一行求和,在这种情况下,我们沿着列或轴 1 操作”,我认为这更有意义。因此,尽管我们计算了每行的总和,但从技术上讲,这是一个列相加而不是行相加,因为 axis=0 是行,axis=1 是列。

为什么 NumPy 不像熊猫那样直截了当?嗯,老实说,我也不知道答案。但这就是饼干碎裂的方式。

理解神经网络中的目标函数。

原文:towardsdatascience.com/understandi…

这篇博文的目标读者是有机器学习经验的人,他们希望对用于训练神经网络的不同目标函数有更好的直觉。

介绍

我决定写这篇博客的原因有三点:

  • 博客帖子经常解释优化方法,如随机梯度下降或其变体,但很少花时间解释如何为神经网络构建目标函数。为什么均方误差(MSE)和交叉熵对数损失被用作 resp 的目标函数。回归分类?为什么添加一个正则化术语有意义?总的想法是,通过研究目标函数,人们可以了解为什么神经网络以它们的方式工作,或者为什么它们在其他情况下失败。

Cross entropy log loss between the ground truth p and network output q, used in classification problems.

Mean squared error between the ground truth y and network output y_tilde, used in regression problems.

  • 神经网络以提供糟糕的概率估计而闻名,并且它们受到反面例子的困扰。简而言之:神经网络往往高度自信,即使它们是错误的。当它们部署在现实生活场景中时(例如自动驾驶汽车),这可能是一个问题。自动驾驶汽车在 90 英里/小时的速度下做决定时应该是确定的。如果我们部署深度学习管道,我们应该知道它们的优势和劣势。
  • 我一直想知道神经网络如何从概率的角度进行解释,以及它们如何适应更广泛的机器学习模型框架。人们倾向于用概率来谈论网络输出。神经网络的概率解释和它们的目标函数之间有联系吗?

这篇博文的主要灵感来自于我和我的朋友布莱恩·特里普在剑桥大学计算和生物学习实验室所做的关于贝叶斯神经网络的工作。我强烈推荐任何人阅读布莱恩关于神经网络中变分推理的论文

免责声明:在计算和生物学习实验室,贝叶斯机器学习技术被毫无歉意地作为未来的方向教授。因此,请注意这篇博文中的潜在偏见(😉).

监督机器学习

在有监督的机器学习问题中,我们经常考虑观察对( xy )的数据集 D ,并且我们尝试对以下分布建模:

例如,在图像分类中,x 代表图像,y 代表相应的图像标签。p(y|x,θ)表示给定图像 x 和由参数θ定义的模型的标签 y 的概率。

遵循这种方法的模型被称为判别模型。在判别或条件模型中,定义条件概率分布函数 p(y|x,θ)的参数是从训练数据中推断出来的。

基于观察数据 x(输入数据或特征值),模型输出概率分布,然后用于预测 y (类别或真实值)。不同的机器学习模型需要估计不同的参数。线性模型(例如,由一组等于特征数量的权重定义的逻辑回归)和非线性模型(例如,由每层的一组权重定义的神经网络)都可以用来近似条件概率分布。

对于典型的分类问题,这组可学习参数θ用于定义不同标签上从 x分类分布的映射。判别分类模型产生 N 个概率作为输出,N 等于类别的数量。每个 x 属于单个类别,但是模型不确定性通过输出类别上的分布来反映。通常,在做出决策时,会选择概率最大的类别。

In image classification, the network outputs a categorical distribution over image classes. The image above depicts the top 5 classes (classes with highest probability) for a test image.

注意,判别回归模型通常只输出一个预测值,而不是所有真实值的分布。这不同于鉴别分类模型,在鉴别分类模型中提供了所有可能类别的分布。这是否意味着歧视性模型在回归中会土崩瓦解?模型的输出不应该告诉我们哪些回归值比其他值更有可能吗?

虽然判别回归模型的单一输出是误导性的,但回归模型的输出实际上与众所周知的概率分布,即高斯分布有关。事实证明,判别回归模型的输出代表高斯分布的均值(高斯分布完全由均值和标准差定义)。有了这些信息,你就可以确定给定输入 x 的每个实值的可能性。

通常只对该分布的平均值进行建模,高斯的标准偏差要么不进行建模,要么被选择为在所有 x 上保持不变。在判别回归模型中,θ因此定义了从 x 到高斯均值的映射,y 从该高斯均值被采样。做决定时,几乎总是选择平均值。输出给定 x 的平均值和标准差的模型更能提供信息,因为该模型能够表达不确定的 x(通过增加标准差)。

A model needs to be uncertain in regions where there is no training data and certain in regions where it has training data. Such a model is displayed in the image above, from Yarin Gal’s blog post.

其他概率模型(如高斯过程)在建模回归问题中的不确定性方面做得更好,而判别回归模型在同时建模均值和标准差时往往过于自信。

高斯过程能够通过明确地模拟标准偏差来量化不确定性。高斯过程的唯一缺点是它们不能很好地适应大型数据集。在下图中,您可以看到 GP 模型在包含大量数据的区域周围具有较小的置信区间(由标准差确定)。在数据点很少的区域,置信区间明显变大。

A Gaussian Process model is certain at the data points, but uncertain at other places (image taken from Sklearn)

在训练数据集上训练判别模型,以便学习代表类或真实值的数据中的属性。如果模型能够将高概率分配给正确的样本类或接近测试数据集中真实值的平均值,则该模型表现良好。

与神经网络连接

当神经网络被训练用于分类或回归任务时,使用神经网络对前述分布(分类和高斯)的参数进行建模。

当我们试图确定神经网络*的参数 θ 的最大似然估计(MLE)时,这变得很清楚。*MLE 对应于找到使训练数据的似然性(或等效对数似然性)最大化的参数 θ 。更具体地,下面的表达式被最大化:

p(Y | X,θ)表示当用模型确定时,训练数据中真实标签的概率。如果 p(Y | X,θ)更接近 1,这意味着模型能够确定训练集中的正确标签/均值。给定训练数据( XY )由 N 个观察对组成,训练数据的可能性可以被重写为对数概率的和。

在分类和回归的情况下,p( y | x,θ ),单个对(x,y)的后验概率,可以重写为分类和高斯分布。在优化神经网络的情况下,目标是以这样的方式移动参数,即对于一组输入 X ,在输出(回归值或类)给出概率分布 Y 的正确参数。这通常通过梯度下降或其变体来实现。为了获得最大似然估计,目标是相对于真实输出优化模型输出:

  • 最大化分类分布的对数对应于最小化近似分布和真实分布之间的交叉熵。
  • 最大化高斯分布的对数对应于最小化近似平均值和真实平均值之间的均方误差。

因此,先前图像中的表达式可以被重写,并且分别导致交叉熵损失和均方误差,这是用于分类回归的神经网络的目标函数。

与更传统的概率模型相比,神经网络学习从输入到概率或均值的非线性函数很难解释。虽然这是神经网络的一个显著缺点,但神经网络能够建模的复杂函数的广度也带来了显著的优势。基于本节中的推导,很明显,在确定参数的 MLE 时出现的神经网络的目标函数可以用概率来解释。

神经网络的一个有趣的解释是它们与广义线性模型(线性回归、逻辑回归等)的关系。神经网络不是采用特征的线性组合(如 GLM 的方法),而是产生高度非线性的特征组合。

最大后验概率

但是,如果神经网络可以被解释为概率模型,为什么它们提供糟糕的概率估计,并遭受对立的例子?为什么他们需要这么多数据?

我喜欢把不同的模型(逻辑回归、神经网络……)看作是在不同的搜索空间中寻找好的函数逼近器。虽然拥有一个非常大的搜索空间意味着在建模后验概率时有很大的灵活性,但这也是有代价的。例如,神经网络被证明是通用函数逼近器。这意味着只要有足够的参数,它们就可以逼近任何函数(太棒了!).然而,为了确保函数在整个数据空间中得到很好的校准,需要指数级的大数据集(昂贵!).

重要的是要知道一个标准的神经网络通常使用最大似然优化。使用 MLE 的优化倾向于过度拟合训练数据,并且需要大量数据来获得适当的结果。机器学习的目标不是找到一个能很好地解释训练数据的模型。您宁愿尝试找到一个模型,该模型能够很好地概括看不见的数据,并且不确定数据是否与训练数据显著不同。

使用最大后验概率(MAP)方法是一种有效的替代方法,当概率模型遭受过拟合时,通常会探索这种方法。那么在神经网络的上下文中,MAP 对应于什么呢?它对目标函数有什么影响?

类似于 MLE,MAP 也可以被重写为神经网络环境中的目标函数。本质上,使用 MAP,您可以在假定 θ : 的先验分布的同时,最大化给定数据的一组参数 θ 的概率

使用 MLE,只考虑公式的第一个元素(模型解释训练数据的能力)。对于 MAP,为了减少过拟合,模型满足先验假设(T4θ与先验的拟合程度)也很重要。

将平均值为 0 的高斯先验放在 θ 上对应于添加到目标的 L2 正则化(确保许多小权重),而将拉普拉斯先验放在 θ 上对应于添加到目标的 L1 正则化(确保许多值为 0 的权重)。

L1 regularisation on the left and L2 regularisation on the right.

完全贝叶斯方法

在 MLE 和 MAP 的情况下,使用单个模型(具有单组参数)。尤其是对于复杂的数据,例如图像,数据空间中的某些区域很可能没有被很好地覆盖。这些区域中模型的输出取决于模型和训练过程的随机初始化,导致对数据空间的未覆盖段中的点的概率估计较差。

尽管 MAP 确保模型不会在这些区域过度拟合,但它仍然会导致模型过于自信。在完全贝叶斯方法中,这通过在多个模型上平均来解决,从而产生更好的不确定性估计。目标不是单一的一组参数,而是对参数的分布进行建模。如果所有模型(不同的参数设置)在未覆盖的区域提供不同的估计,这表明该区域有很大的不确定性。通过对这些模型进行平均,最终结果是一个在这些区域不确定的模型。这正是我们想要的!

在下一篇博文中,我将讨论贝叶斯神经网络,以及它们如何试图解决传统神经网络的上述问题。贝叶斯神经网络(BNN 的)仍然是一项积极的研究工作,在训练它们时没有明确的赢家方法。

我强烈推荐 Yarin Gal 关于深度学习中的不确定性的博文!

PCA 和自动编码器:人人都能理解的算法

原文:towardsdatascience.com/understandi…

本文的主要重点是为主成分分析(PCA)和 Autoencoder 数据转换技术提供直觉。我不打算深入研究支撑这些模型的数学理论,因为已经有太多的资源可用了。

Source: FK Films

简介:

自动编码器通过组合数据最重要的特征,将它们输入的数据映射到一个较低的维度空间。他们将原始数据编码成更紧凑的表示,并决定如何组合数据,因此在 Autoencoder 中有了 auto 。这些编码特征通常被称为潜在变量。

有几个原因可以说明这样做是有用的:

1.降维可以减少训练时间

2.使用潜在特征表示可以提高模型性能

像机器学习中的许多概念一样,自动编码器看起来很深奥。如果你不熟悉潜在变量,潜在变量本质上是一些数据的隐含特征。这是一个无法直接观察或测量的变量。例如,幸福是一个潜在的变量。我们必须使用类似问卷调查的方法来推断一个人的幸福程度。

像自动编码器模型一样,主成分分析(PCA)也被广泛用作降维技术。但是,PCA 算法映射输入数据的方式与自动编码器不同。

直觉:

假设你有一套很棒的跑车乐高玩具,想送给你的朋友作为生日礼物,但是你的盒子不够大,装不下所有的乐高玩具。你决定打包最重要的乐高零件——对汽车制造贡献最大的零件,而不是根本不送。所以,你扔掉一些琐碎的零件,如门把手和挡风玻璃雨刷,打包一些零件,如车轮和框架。然后,你把盒子寄给你的朋友。收到包裹后,你的朋友对没有说明的各种乐高积木感到困惑。尽管如此,他们还是组装了该套件,并且能够识别出这是一辆可驾驶的车辆。可能是沙滩车、赛车或轿车——他们不知道。

上面的类比是有损数据压缩算法的一个例子。数据的质量没有完全保持。这是一个有损算法,因为一些原始数据(即乐高积木)已经丢失。虽然使用 PCA &自动编码器进行维数缩减是有损耗的,但是这个例子并没有准确地描述这些算法——它描述了一个特征选择算法。特征选择算法丢弃数据的一些特征并保留显著特征。它们保留的特征通常是出于统计原因而选择的,例如属性和目标标签之间的相关性。

主成分分析:

假设一年过去了,你朋友的生日又快到了。你决定再给他们买一套乐高玩具汽车,因为他们去年告诉你他们有多爱他们的礼物。你也犯了一个大错,买了一个太小的盒子。这一次,你认为你可以更好地利用乐高,将它们系统地切割成更小的块。乐高积木的粒度更细,可以让你比上次装得更多。以前,收音机天线太高,放不进盒子里,但现在你把它切成三份,包括三份中的两份。当你的朋友收到邮寄的礼物时,他们会把某些零件粘在一起组装成汽车。他们能够将扰流器和一些轮毂盖粘合在一起,因此汽车更容易识别。接下来,我们将探索这个类比背后的数学概念。

LEGO klodser©2015 LEGO/Palle Peter SkovP 1

阐述:

PCA 的工作原理是将输入数据投影到数据协方差矩阵的特征向量上。协方差矩阵量化了数据的方差以及每个变量相对于另一个变量的变化程度。特征向量是通过线性变换保持其跨度的简单向量;也就是说,它们在变换前后指向相同的方向。协方差矩阵将原始基向量转换为每个变量之间的协方差方向。更简单地说,特征向量允许我们重新构建原始数据的方向,以便从不同的角度查看它,而无需实际转换数据。当我们将数据投影到这些向量上时,我们实质上是在提取每个变量中导致最大方差的分量。然后,我们可以使用协方差矩阵的特征值来选择主轴,因为它们反映了相应特征向量方向上的方差的大小。

Original Data (left) 1st Principal Component & Data (right)

这些投影产生一个新的空间,其中每个基向量包含最大的方差(即,在具有最大特征值的特征向量上的投影具有最大的方差,在第二特征向量上的投影具有第二大的方差,等等)。).这些新的基向量被称为主分量。我们希望主成分朝着最大方差的方向,因为属性值的方差越大,预测能力越强。例如,假设您试图预测一辆汽车的价格,给定两个属性:颜色和品牌。假设所有的车颜色都一样,但是其中有很多品牌。在这个例子中,根据颜色猜测汽车的价格是不可能的,因为颜色是一个零方差的特性。然而,如果我们考虑一个有更多变化的特征——品牌——我们将能够得出更好的价格估计,因为奥迪和法拉利的价格往往高于本田和丰田。PCA 产生的主要成分是输入变量的线性组合,就像胶合的乐高积木是原件的线性组合一样。这些主成分的线性性质也允许我们解释转换后的数据。

Data projected onto 1st principal component (Source: Author)

PCA 优点:

  • 降低维度
  • 可解释的
  • 快速运行时间

PCA 缺点:

  • 不能学习非线性特征表示

自动编码器:

Autoencoder Architecture

自动编码器的事情变得有点奇怪。你不再只是切割积木,而是开始融化、拉长和弯曲整个乐高积木,这样最终的积木代表了汽车最重要的特征,同时又符合盒子的限制。这样做不仅可以让你在盒子里放入更多的乐高积木,还可以让你定制积木。这很好,但是当包裹到达时,你的伙伴不知道如何处理它。对他们来说,它只是看起来像一堆随机操纵的乐高积木。事实上,这些零件是如此的不同,以至于你需要对几辆车重复这个过程无数次,以一种系统的方式将原始零件转化为可以由你的朋友组装到汽车上的零件。

阐述:

希望上面的类比有助于理解自动编码器如何类似于 PCA。在自动编码器的环境中,你是编码器,你的朋友是解码器。你的工作是以一种解码器可以解释的方式转换数据,然后以最小的误差重建。

自动编码器只是一个重新设计的前馈神经网络。我不打算在这里深究本质细节,但可以随意查看 Piotr Skalski 的伟大文章深度学习书籍以获得对神经网络更全面的了解。

尽管它们能够学习复杂的特征表示,但自动编码器最大的缺陷在于它们的可解释性。就像你的朋友收到扭曲的乐高玩具时毫无头绪一样,我们也不可能想象和理解非视觉数据的潜在特征。接下来,我们将研究稀疏自动编码器。

自动编码器优点:

  • 能够学习非线性特征表示
  • 降低维度

自动编码器缺点:

  • 训练的计算成本很高
  • 无法解释的
  • 更复杂
  • 倾向于过度拟合,虽然这可以通过正则化来减轻

稀疏自动编码器:

Sparse Autoencoder Loss Function (Source: Andrew Ng)

认为人类没有充分利用大脑能力的观点是基于神经科学研究的误解,该研究表明大脑中最多有 1 %- 4%的神经元同时放电。人类大脑中神经元的稀疏放电可能有几个好的进化原因。如果所有神经元同时放电,我们能够“释放大脑的真正潜力”,它可能看起来像这个。我希望你喜欢这个题外话。回到神经网络。大脑中突触的稀疏性可能是稀疏自动编码器的灵感来源。整个神经网络中的隐藏神经元学习输入数据的分级特征表示。我们可以认为,当一个神经元看到它正在寻找的输入数据的特征时,它就“触发”了。传统的自动编码器依靠其欠完整架构(欠完整意味着隐藏层比输入层包含更少的单元)来强制学习潜在特征。稀疏自动编码器背后的想法是,我们可以通过与架构无关的约束(稀疏性约束)来迫使模型学习潜在的特征表示。

稀疏性约束是我们想要的平均隐藏层激活,并且通常是接近零的浮点值。稀疏约束超参数在上面的函数中用希腊字母 rho 表示。hat j 表示隐藏单元 j 的平均激活度。

我们使用 KL 散度对模型施加这种约束,并用β对这种施加进行加权。简而言之,KL 散度衡量两个分布的不相似性。将此项添加到我们的损失函数中激励模型优化参数,使得激活值的分布和稀疏参数的均匀分布之间的 KL 散度最小化。

将激活限制在接近零意味着神经元只会在优化准确性最关键的时候触发。KL 散度意味着神经元也会因为过于频繁的放电而受到惩罚。如果你有兴趣了解更多关于稀疏自动编码器的知识,我强烈推荐你阅读这篇文章。吴恩达的这些讲座(讲座 1讲座 2 )也是很好的资源,帮助我更好地理解支撑自动编码器的理论。

结论:

在整篇文章中,我们深入研究了 PCA 和自动编码器背后的概念。不幸的是,没有灵丹妙药。PCA 和自动编码器模型之间的决定是依情况而定的。在许多情况下,PCA 更优越——它更快,更容易解释,可以像自动编码器一样减少数据的维数。如果你能雇用 PCA,你应该。但是,如果您正在处理的数据需要高度非线性的特征表示来获得足够的性能或可视化,PCA 可能会有所不足。在这种情况下,训练自动编码器可能是值得的。再者,即使自动编码器产生的潜在特征提高了模型性能,这些特征的模糊性对知识发现构成了障碍。

感谢阅读!我希望你喜欢这篇文章,并获得一些有用的见解。如果你做了,请随意留下掌声!感谢建设性的反馈。

参考文献:

神经科学研究:https://www . science direct . com/science/article/pii/s 0960982203001350?通过%3Dihub

https://www . coursera . org/learn/neural-networks/lecture/JiT1i/from-PCA-to-auto encoders-5-mins

arxiv.org/pdf/1801.01…

其他图像来源:

自动编码器架构:

另一篇值得一读的文章:https://Neptune . ai/blog/understanding-re presentation-learning-with-auto encoder-everything-you-Neptune-to-know-about-re presentation-and-feature-learning