案例¶

监督学习¶

假设你是一名房地产经纪人。你的生意在增长,所以你雇了一群新的实习生来帮你。但是有一个问题——你可以看一眼房子就知道房子值多少钱,但你的实习生没有你的经验,所以他们不知道如何给他们的房子定价。

为了帮助学员决策,你编写了一个小应用程序,它可以根据您所在地区的房子的大小、社区等,以及类似的房子的售价来估算房子的价值。 所以你写下每3个月有人卖出你所在城市的房子。对于每一栋房子,你都写下一堆细节——卧室的数量、面积、邻居等等。同时也写出了最终的销售价格:

Bedrooms Sq. feet Neighborhood Sale price
3 2000 Normaltown 250,000
2 800 Hipsterton 300,000
2 850 Normaltown 150,000
1 550 Normaltown 78,000
4 2000 Skid Row 150,000

这是我们的“训练数据”。 利用这些训练数据,我们想创建一个程序,可以估算出你所在地区其他任何房子的价值: 这被称为监督学习。知道每幢房子的售价,换句话说,知道问题的答案,我们就可以从那里逆向推导出这其中的关系。 要创建应用程序,你需要将每个房子的训练数据输入到机器学习算法中。该算法试图找出需要进行何种数学运算才能算出这些数字。 这有点像把数学考试的答案的所有算术符号都擦掉了(一个狡猾的学生擦掉了老师答案上的算术符号!) 通过这个,你能猜出考试中有哪些数学题吗?你知道你应该对左边的数字“做些什么”来得到右边的每个答案吗?

在监督式学习中,你让计算机为你解决这个关系。一旦你知道解决这一系列问题需要什么数学,你就可以回答任何其他同类型的问题!

无监督学习¶

让我们回到最开始的房地产经纪人的例子。如果你不知道每幢房子的售价呢?即使你只知道每个房子的大小、位置等,即使你不打算预测一个未知的数字(比如价格)你仍然可以做一些事情。这被称为无监督学习。

这有点像有人给你一张纸上的一串数字,然后说:“我真的不知道这些数字是什么意思,但也许你可以找出其中的模式或分组或其他什么——祝你好运!” 那么我们可以用这些数据做什么呢?首先,可以使用一种算法,自动识别数据中的不同细分市场。也许你会发现,住在当地大学附近社区的购房者喜欢有很多卧室的小房子,但住在郊区的购房者更喜欢面积大的三居室的房子。了解这些不同类型的客户有助于指导你的营销工作。

你可以做的另一件很酷的事情是自动识别任何与众不同的房子。也许那些与众不同的房子是巨大的大厦,你可以把你最好的销售人员集中在这些领域,因为他们有更大的佣金。 在接下来的文章中,我们将重点关注监督学习,但这并不是因为无监督学习的用处或兴趣更小。事实上,随着算法的改进,无监督学习变得越来越重要,因为它可以在不给数据标上正确答案的情况下使用。

如果你卖房子很长一段时间,你会本能地对房子的合适价格、推销房子的最佳方式、对什么样的客户感兴趣等等有一种“感觉”。强人工智能研究的目标是能够在计算机上复制这种能力。但目前的机器学习算法还没有那么好——它们只在专注于一个非常具体、有限的问题时才有效。在这种情况下,“学习”的更好定义可能是“根据一些示例数据计算出一个方程来解决特定问题”。我们称之为“机器学习”。

让我们来编写这个程序! 那么,在上面的例子中,你会如何编写程序来估计房子的价值呢?在你继续阅读之前,先思考一下。 如果你对机器学习一窍不通,你可能会试着写出一些估算房价的基本规则,比如:

def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
    price = 0
    # In my area, the average house costs $200 per sqft
    price_per_sqft = 200
    if neighborhood == "hipsterton":
    # but some areas cost a bit more
        price_per_sqft = 400
    elif neighborhood == "skid row":
    # and some areas cost less
        price_per_sqft = 100
    # start with a base price estimate based on how big the place is
    price = price_per_sqft * sqft
    # now adjust our estimate based on the number of bedrooms
    if num_of_bedrooms == 0:
        # Studio apartments are cheap
        price = price-20000
    else:
        # places with more bedrooms are usually
        # more valuable
        price = price + (num_of_bedrooms * 1000)
    return price

如果机器能自己想出解决办法就好了,比如类似于这样:

def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
  price = <computer, plz do some math for me>
  return price

我们将价格比作是一道美味的炖菜,而配料是卧室的数量、面积和社区。如果能算出每种配料对最终价格的影响,也许就有了配料的精确比例来决定最终价格。 比如:

def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
 price = 0
 # a little pinch of this
 price += num_of_bedrooms * .841231951398213
 # and a big pinch of that
 price += sqft * 1231.1231231
 # maybe a handful of this
 price += neighborhood * 2.3242341421
 # and finally, just a little extra salt for good measure
 price += 201.23432095
 return price

.841231951398213、1231.1231231、2.3242341421和201.23432095,这些数字就是“比重”或者“权重”。如果我们能够计算出每个房子的完美权重,我们的函数就能够预测房价! 计算最佳权重的方法如下:

  • 步骤1: 首先将每个权重设置为1.0:
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
  price = 0
  # a little pinch of this
  price += num_of_bedrooms * 1.0
  # and a big pinch of that
  price += sqft * 1.0
  # maybe a handful of this
  price += neighborhood * 1.0
  # and finally, just a little extra salt for good measure
  price += 1.0
  return price
  • 步骤2: 通过函数运行你所知道的每一所房子,看看函数在猜测每一所房子的正确价格时离正确价格有多远: 例如,如果第一套房子真的卖了25万美元,但你的函数猜测它卖了17.8万美元,那么单套房子就差了7.2万美元。 现在把数据集中每一所房子的误差平方相加。假设在你的数据集中有500套房子的销售,每套房子的函数值的平方总共是86123373美元。这就是你现在的功能有多“错误”。

现在,用这个总数除以500就得到了你离每栋房子有多远的平均值。将这个平均错误量称为函数的代价。

如果你能通过调整权重使这个代价为零,你的函数就完美了。这意味着在每种情况下,你的函数都完美地根据输入数据猜测出房子的价格。这就是我们的目标——通过尝试不同的重量,使成本尽可能低。

  • 步骤3: 对每一个可能的权重组合重复第2步。使成本最接近于零的权重组合就是您所使用的。当你找到有效的重量时,你就解决了问题! 介意Blowage时间 这很简单,对吧?想想你刚才做了什么。你用一些数据,通过三个非常简单的步骤,你最终得到一个函数,它可以猜测你所在地区任何房子的价格。 但这里还有一些事实会让你大吃一惊: 在过去40年里,许多领域(如语言学/翻译)的研究表明,这些“搅乱数字”(这是我刚刚编造的短语)的通用学习算法比那些真实的人试图自己想出明确规则的方法更有效。机器学习的“愚蠢”方法最终打败了人类专家。 最后得到的函数完全是愚蠢的。它甚至不知道什么是“平方英尺”或“卧室”。它所知道的是,它需要搅动这些数字中的一些来得到正确的答案.

想象一下,你的预测函数不是接受像“sqft”和“num_of_room”这样的参数,而是接受一个数字数组。假设每个数字代表一个像素的亮度,这个像素是由安装在汽车顶部的摄像头捕捉到的。现在我们假设函数输出的不是一个名为“price”的预测,而是一个名为“degrees_to_turn_steering_wheel”的预测。你刚刚创造了一个可以自动驾驶汽车的功能! 厉害吧。

当然,你不可能尝试所有可能权重的每一个组合来找到最合适的组合。这将花费很长时间,因为你永远不会用尽所有的数字。 为了避免这种情况,数学家们想出了许多聪明的方法来快速地为这些权重找到好的值,而不需要尝试很多次。这里有一个方法:

首先,写一个简单的方程来表示上面的第2步: $$\text { Cost }=\frac{\sum_{i=1}^{500}(\text { MyGuess }(i)-\text { RealAnswer }(i))^{2}}{500 \cdot 2}$$ 这是成本函数。

现在让我们用一堆机器学习的数学术语(你现在可以忽略)来重写相同的方程: $$ J(\theta)=\frac{1}{2 m} \sum_{i=1}^{m}\left(h_{\theta}\left(x^{(i)}\right)-y^{(i)}\right)^{2} $$ $\theta$代表权重。$J(\theta)$表示代价。 这个等式代表了我们的价格估算函数对于我们当前设置的权重的错误程度。 如果我们绘制出number_of_beds和sqft的所有可能权重值的成本方程,我们将得到如下图所示的结果:

我们的成本函数图看起来像一个碗。纵轴表示成本。 在这个图表中,蓝色的最低点是我们的成本最低的地方,因此我们的函数错误最少。最高点是我们错误最多的地方。所以如果我们能找到能让我们到达图上最低点的权值,我们就有答案了! 所以我们只需要调整我们的权重,这样我们就可以在这张图上向最低点“走下山”。如果我们不断地对我们的重量做小的调整,使其总是朝着最低点移动,我们最终将不用尝试太多不同的权重。

微积分中对一个函数求导,它会告诉你该函数在任意点上的切线斜率。换句话说,它告诉了我们曲线上任意一点的下坡方向。我们可以利用这些知识走下坡。

所以如果我们计算成本函数对每个权值的偏导数,然后我们可以从每个权值中减去这个值。这能让我们离山脚下更近一步。继续这样做,最终我们会到达山脚下,得到最好的重量值。

这是对为你的函数找到最佳权值的一种方法的一个高级总结叫做梯度下降。 当你使用机器学习库来解决实际问题时,所有这些都会为你完成。但是,对正在发生的事情有一个很好的了解仍然是有用的。 你还忽略了什么? 我所描述的三步算法被称为多元线性回归。你正在估算一条直线的方程,这条直线符合你所有的房子数据点。然后你用这个等式来猜测那些你从未见过的房子的销售价格,这些房子会出现在你的直线上。这是一个非常好的想法,你可以用它解决“真正的”问题。 但是,虽然我向您展示的方法可能在简单的情况下有效,但它并不是在所有情况下都有效。一个原因是,房价并不总是简单到可以沿着一条连续的线走。 幸运的是,有很多方法可以解决这个问题。还有很多其他的机器学习算法可以处理非线性数据(比如神经网络或支持向量机)。还有一些方法可以更巧妙地使用线性回归,使更复杂的线能够匹配。在所有情况下,需要找到最佳权重的基本思想仍然适用。

此外,我忽略了过度拟合的想法。我们很容易找到一套权重,它总是能够完美地预测原始数据集中的房子的价格,但对于原始数据集中以外的任何新房子,它却从来都不起作用。但有一些方法可以解决这个问题(如正则化和使用交叉验证数据集)。学习如何处理这个问题是学习如何成功应用机器学习的一个关键部分。

换句话说,虽然基本概念相当简单,但应用机器学习并获得有用的结果需要一些技能和经验。 一旦你开始看到机器学习技术可以多么容易地应用于看起来非常困难的问题(比如手写识别),你就会开始觉得,只要你有足够的数据,你就可以使用机器学习来解决任何问题,并得到一个答案。只要输入数据,就会看到计算机神奇地找出符合数据的方程! 但重要的是要记住,机器学习只有在用你拥有的数据可以解决问题的情况下才会起作用。 例如,如果你建立一个模型,根据每栋房子里盆栽的类型来预测房价,它永远不会起作用。每栋房子里的盆栽植物和房子的售价之间没有任何关系。因此,无论计算机如何努力,它永远无法推导出两者之间的关系。

参考资料:

  • https://medium.com/@ageitgey/machine-learning-is-fun-80ea3ec3c471#.37ue6caww

In [19]:
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
    price = 0
    # In my area, the average house costs $200 per sqft
    price_per_sqft = 200
    if neighborhood == "hipsterton":
    # but some areas cost a bit more
        price_per_sqft = 400
    elif neighborhood == "skid row":
    # and some areas cost less
        price_per_sqft = 100
    # start with a base price estimate based on how big the place is
    price = price_per_sqft * sqft
    # now adjust our estimate based on the number of bedrooms
    if num_of_bedrooms == 0:
        # Studio apartments are cheap
        price = price-20000
    else:
        # places with more bedrooms are usually
        # more valuable
        price = price + (num_of_bedrooms * 1000)
    return price
In [ ]: