探索监督机器学习算法

已发表: 2022-03-11

本次阅读的主要目标是理解足够的统计方法,以便能够利用 Python 的 scikit-learn 库中的机器学习算法,然后应用这些知识来解决经典的机器学习问题。

我们旅程的第一站将带我们了解机器学习的简史。 然后我们将深入研究不同的算法。 在我们的最后一站,我们将使用我们学到的知识来解决泰坦尼克号生存率预测问题。

一些免责声明:

  • 我是一名全栈软件工程师,而不是机器学习算法专家。
  • 我假设你知道一些基本的 Python。
  • 这是探索性的,所以并不是每个细节都像教程一样解释。

有了这一点,让我们开始吧!

机器学习算法简介

一旦你涉足这个领域,你就会意识到机器学习并没有你想象的那么浪漫。 起初,我满怀希望,当我了解更多之后,我可以构建自己的 Jarvis AI,它会整天为我编写软件和赚钱,这样我就可以整天在户外看书,开摩托车,享受鲁莽的生活方式,而我的私人贾维斯让我的口袋更深。 然而,我很快意识到机器学习算法的基础是统计,我个人觉得这很枯燥乏味。 幸运的是,事实证明“枯燥”的统计数据有一些非常有趣的应用。

您很快就会发现,要获得这些引人入胜的应用程序,您需要非常了解统计数据。 机器学习算法的目标之一是在提供的数据中找到统计相关性。

提供的数据可以是任何东西,从根据年龄检查血压到根据各种像素的颜色查找手写文本。

也就是说,我很想知道是否可以使用机器学习算法来查找加密哈希函数(SHA、MD5 等)中的依赖关系——但是,你不能真正做到这一点,因为正确的加密原语是以这种方式构建的它们消除了依赖关系并产生了难以预测的输出。 我相信,给定无限的时间,机器学习算法可以破解任何加密模型。

不幸的是,我们没有那么多时间,所以我们需要找到另一种有效挖掘加密货币的方法。 到目前为止,我们已经走了多远?

机器学习算法简史

机器学习算法的根源来自生活在 18 世纪的英国统计学家 Thomas Bayes。 他的论文《An Essay To Solveing a Problem in the Doctrine of Chances》奠定了贝叶斯定理的基础,该定理被广泛应用于统计学领域。

19 世纪,Pierre-Simon Laplace 发表了Theorie analytique des probabilites ,扩展了贝叶斯的工作,并定义了我们今天所知的贝叶斯定理。 不久之前,Adrien-Marie Legendre 描述了“最小二乘法”,今天也广泛用于监督学习。

20 世纪是在这一领域取得了大多数众所周知的发现的时期。 安德烈马尔科夫发明了马尔科夫链,他用它来分析诗歌。 Alan Turing 提出了一种可以成为人工智能的学习机器,基本上预示了遗传算法。 Frank Rosenblatt 发明了感知器,在媒体上引发了巨大的兴奋和广泛的报道。

但在 1970 年代,人们对​​ AI 的想法产生了很多悲观情绪,因此资金减少了,因此这一时期被称为AI 寒冬。 1980 年代反向传播的重新发现引起了机器学习研究的复苏。 而今天,这又是一个热门话题。

已故的 Leo Breiman 区分了两种统计建模范式:数据建模和算法建模。 “算法建模”或多或少意味着像随机森林这样的机器学习算法。

机器学习和统计是密切相关的领域。 根据 Michael I. Jordan 的说法,机器学习的思想,从方法论原则到理论工具,在统计学中有着悠久的历史。 他还建议将数据科学作为机器学习专家和统计学家都在暗中研究的整体问题的占位符术语。

机器学习算法的类别

机器学习领域有两个主要支柱,称为监督学习无监督学习。 有些人还考虑将一个新的研究领域——深度学习——与有监督学习与无监督学习的问题分开。

监督学习是指向计算机提供输入示例及其所需输出的情况。 计算机的目标是学习一个将输入映射到输出的通用公式。 这可以进一步细分为:

  • 半监督学习,即给计算机一个不完整的训练集,缺少一些输出
  • 主动学习,即计算机只能获得非常有限的一组实例的训练标签。 当以交互方式使用时,他们的训练集可以呈现给用户进行标记。
  • 强化学习,即训练数据仅作为程序在动态环境中的动作的反馈,例如驾驶车辆或与对手玩游戏

相反,无监督学习是指根本没有给出标签,由算法在其输入中找到结构。 当我们只需要发现隐藏的模式时,无监督学习本身就可以成为一个目标。

深度学习是一个新的研究领域,其灵感来自人脑的结构和功能,并基于人工神经网络而不仅仅是统计概念。 深度学习可用于有监督和无监督方法。

在本文中,我们将仅介绍一些更简单的监督机器学习算法,并使用它们来计算个人在泰坦尼克号沉没中的生存机会。 但总的来说,如果您不确定要使用哪种算法,一个不错的起点是 scikit-learn 的机器学习算法备忘单。

基本的监督机器学习模型

也许最简单的算法是线性回归。 有时这可以用图形表示为一条直线,但尽管它的名字,如果有一个多项式假设,这条线可能是一条曲线。 无论哪种方式,它都对标量因变量 $y$ 和一个或多个由 $x$ 表示的解释值之间的关系进行建模。

通俗地说,这意味着线性回归是一种算法,它学习每个已知的 $x$ 和 $y$ 之间的依赖关系,以便稍后我们可以使用它来预测 $y$ 的未知样本 $x$。

在我们的第一个监督学习示例中,我们将使用基本的线性回归模型来根据年龄预测一个人的血压。 这是一个非常简单的数据集,具有两个有意义的特征:年龄和血压。

如上所述,大多数机器学习算法通过在提供给它们的数据中找到统计依赖性来工作。 这种依赖称为假设,通常用 $h(\theta)$ 表示。

为了弄清楚这个假设,让我们从加载和探索数据开始。

 import matplotlib.pyplot as plt from pandas import read_csv import os # Load data data_path = os.path.join(os.getcwd(), "data/blood-pressure.txt") dataset = read_csv(data_path, delim_whitespace=True) # We have 30 entries in our dataset and four features. The first feature is the ID of the entry. # The second feature is always 1. The third feature is the age and the last feature is the blood pressure. # We will now drop the ID and One feature for now, as this is not important. dataset = dataset.drop(['ID', 'One'], axis=1) # And we will display this graph %matplotlib inline dataset.plot.scatter(x='Age', y='Pressure') # Now, we will assume that we already know the hypothesis and it looks like a straight line h = lambda x: 84 + 1.24 * x # Let's add this line on the chart now ages = range(18, 85) estimated = [] for i in ages: estimated.append(h(i)) plt.plot(ages, estimated, 'b')

[<matplotlib.lines.Line2D at 0x11424b828>]

年龄与血压图上显示的线性假设。

在上图中,每个蓝点代表我们的数据样本,蓝线是我们的算法需要学习的假设。 那么这个假设到底是什么?

为了解决这个问题,我们需要学习$x$和$y$之间的依赖关系,记为$y = f(x)$。 因此 $f(x)$ 是理想的目标函数。 机器学习算法将尝试猜测与未知 $f(x)$ 最接近的假设函数 $h(x)$。

线性回归问题的最简单假设形式如下所示:$h_\theta(x) = \theta_0 + \theta_1 * x$。 我们有一个输入标量变量 $x$,它输出单个标量变量 $y$,其中 $\theta_0$ 和 $\theta_1$ 是我们需要学习的参数。 在数据中拟合这条蓝线的过程称为线性回归。 重要的是要了解我们只有一个输入参数 $x_1$; 然而,很多假设函数也会包含偏差单元($x_0$)。 所以我们得到的假设具有 $h_\theta(x) = \theta_0 * x_0 + \theta_1 * x_1$ 的形式。 但是我们可以避免写入 $x_0$,因为它几乎总是等于 1。

回到蓝线。 我们的假设看起来像 $h(x) = 84 + 1.24x$,这意味着 $\theta_0 = 84$ 和 $\theta_1 = 1.24$。 我们如何自动推导出这些 $\theta$ 值?

我们需要定义一个成本函数。 本质上,成本函数所做的只是计算模型预测和实际输出之间的均方根误差。

\[J(\theta) = \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)})^2\ ]

例如,我们的假设预测,对于 48 岁的人,他们的血压应该是 $h(48) = 84 + 1.24 * 48 = 143mmHg$; 但是,在我们的训练样本中,我们的值为 130 毫米汞柱。 因此错误是 $(143 - 130)^2 = 169$。 现在我们需要为训练数据集中的每一个条目计算这个误差,然后把它加在一起 ​​($\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i )})^2$) 并从中取出平均值。

这给了我们一个代表函数成本的标量数。 我们的目标是找到使成本函数最低的 $\theta$ 值; 换句话说,我们希望最小化成本函数。 希望这看起来很直观:如果我们的成本函数值很小,这意味着预测的误差也很小。

 import numpy as np # Let's calculate the cost for the hypothesis above h = lambda x, theta_0, theta_1: theta_0 + theta_1 * x def cost(X, y, t0, t1): m = len(X) # the number of the training samples c = np.power(np.subtract(h(X, t0, t1), y), 2) return (1 / (2 * m)) * sum(c) X = dataset.values[:, 0] y = dataset.values[:, 1] print('J(Theta) = %2.2f' % cost(X, y, 84, 1.24))

J(Theta) = 1901.95

现在,我们需要找到这样的 $\theta$ 值,使得我们的成本函数值最小。 但是我们该怎么做呢?

\[minJ(\theta) = \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)})^2\ ]

有几种可能的算法,但最流行的是梯度下降。 为了理解梯度下降法背后的直觉,我们先把它画在图上。 为了简单起见,我们将假设一个更简单的假设 $h(\theta) = \theta_1 * x$。 接下来,我们将绘制一个简单的 2D 图表,其中 $x$ 是 $\theta$ 的值,$y$ 是此时的成本函数。

 import matplotlib.pyplot as plt fig = plt.figure() # Generate the data theta_1 = np.arange(-10, 14, 0.1) J_cost = [] for t1 in theta_1: J_cost += [ cost(X, y, 0, t1) ] plt.plot(theta_1, J_cost) plt.xlabel(r'$\theta_1$') plt.ylabel(r'$J(\theta)$') plt.show() 

凸成本函数。

成本函数是凸的,这意味着在区间 $[a, b]$ 上只有一个最小值。 这再次意味着最好的 $\theta$ 参数位于成本函数最小的点。

基本上,梯度下降是一种试图找到最小化函数的参数集的算法。 它从一组初始参数开始,并在函数梯度的负方向上迭代地采取步骤。

寻找成本函数的最小值。

如果我们计算假设函数在特定点的导数,这将为我们提供该点处曲线的切线斜率。 这意味着我们可以计算图表上每个点的斜率。

该算法的工作方式是这样的:

  1. 我们选择一个随机起点(随机 $\theta$)。
  2. 计算此时成本函数的导数。
  3. 向斜率 $\theta_j := \theta_j - \lambda * \frac{\partial}{\partial \theta_j} * J(\theta)$ 迈出一小步。
  4. 重复步骤 2-3,直到我们收敛。

现在,收敛条件取决于算法的实现。 我们可能会在 50 步后、某个阈值或其他任何东西后停止。

 import math # Example of the simple gradient descent algorithm taken from Wikipedia cur_x = 2.5 # The algorithm starts at point x gamma = 0.005 # Step size multiplier precision = 0.00001 previous_step_size = cur_x df = lambda x: 2 * x * math.cos(x) # Remember the learning curve and plot it while previous_step_size > precision: prev_x = cur_x cur_x += -gamma * df(prev_x) previous_step_size = abs(cur_x - prev_x) print("The local minimum occurs at %f" % cur_x)

The local minimum occurs at 4.712194

我们不会在本文中实现这些算法。 相反,我们将使用广泛采用的scikit-learn ,这是一个开源 Python 机器学习库。 它为不同的数据挖掘和机器学习问题提供了许多非常有用的 API。

 from sklearn.linear_model import LinearRegression # LinearRegression uses the gradient descent method # Our data X = dataset[['Age']] y = dataset[['Pressure']] regr = LinearRegression() regr.fit(X, y) # Plot outputs plt.xlabel('Age') plt.ylabel('Blood pressure') plt.scatter(X, y, color='black') plt.plot(X, regr.predict(X), color='blue') plt.show() plt.gcf().clear() 

关于血压与年龄图的学习线性假设

<matplotlib.figure.Figure at 0x120fae1d0>

 print( 'Predicted blood pressure at 25 yo = ', regr.predict(25) ) print( 'Predicted blood pressure at 45 yo = ', regr.predict(45) ) print( 'Predicted blood pressure at 27 yo = ', regr.predict(27) ) print( 'Predicted blood pressure at 34.5 yo = ', regr.predict(34.5) ) print( 'Predicted blood pressure at 78 yo = ', regr.predict(78) )
 Predicted blood pressure at 25 yo = [[ 122.98647692]] Predicted blood pressure at 45 yo = [[ 142.40388395]] Predicted blood pressure at 27 yo = [[ 124.92821763]] Predicted blood pressure at 34.5 yo = [[ 132.20974526]] Predicted blood pressure at 78 yo = [[ 174.44260555]]

统计数据的类型

在处理机器学习问题的数据时,识别不同类型的数据非常重要。 我们可能有数字(连续或离散)、分类或有序数据。

数值数据具有测量意义。 例如,年龄、体重、一个人拥有的比特币数量,或者这个人每月可以写多少篇文章。 数值数据可以进一步细分为离散和连续类型。

  • 离散数据表示可以用整数计数的数据,例如,公寓中的房间数或掷硬币的次数。
  • 连续数据不一定用整数表示。 例如,如果您要测量可以跳跃的距离,它可能是 2 米、1.5 米或 1.652245 米。

分类数据表示诸如人的性别、婚姻状况、国家等值。这些数据可以取数值,但这些数字没有数学意义。 您不能将它们加在一起。

序数数据可以是其他两种类型的混合,因为类别可以以数学上有意义的方式编号。 一个常见的例子是评分:我们经常被要求以一到十的等级对事物进行评分,并且只允许使用整数。 虽然我们可以在数字上使用它——例如,找到某事物的平均评分——但在将机器学习方法应用于数据时,我们通常将数据视为分类数据。

逻辑回归

线性回归是一种很棒的算法,它可以帮助我们预测数值,例如,具有特定大小和房间数量的房屋价格。 但是,有时,我们可能还想预测分类数据,以获得以下问题的答案:

  • 这是狗还是猫?
  • 这种肿瘤是恶性的还是良性的?
  • 这酒是好是坏?
  • 这封电子邮件是不是垃圾邮件?

甚至:

  • 图中是哪个数字?
  • 此电子邮件属于哪个类别?

所有这些问题都特定于分类问题。 而最简单的分类算法称为逻辑回归,它最终与线性回归相同,只是它有不同的假设。

首先,我们可以重用相同的线性假设 $h_\theta(x) = \theta^TX$(这是向量化的形式)。 线性回归可以输出区间 $[a, b]$ 中的任意数字,而逻辑回归只能输出 $[−1, 1]$ 中的值,即对象是否属于给定类别的概率。

使用sigmoid 函数,我们可以将任何数值转换为表示区间 $[−1, 1]$ 上的值。

\[f(x) = \frac{1}{1 + e^x}\]

现在,我们需要传递一个现有假设,而不是 $x$,因此我们将得到:

\[f(x) = \frac{1}{1 + e^{\theta_0 + \theta_1 * x_1 + ... + \theta_n * x_n}}\]

之后,我们可以应用一个简单的阈值,即如果假设大于零,则为真值,否则为假。

\[h_\theta(x) = \begin{cases} 1 & \mbox{if } \theta^TX > 0 \\ 0 & \mbox{else} \end{cases}\]

这意味着我们可以使用相同的成本函数和相同的梯度下降算法来学习逻辑回归的假设。

在我们的下一个机器学习算法示例中,我们将建议航天飞机的飞行员是否应该使用自动或手动着陆控制。 我们有一个非常小的数据集——15 个样本——由六个特征和基本事实组成。

在机器学习算法中,术语“ ground truth ”是指训练集分类的监督学习技术的准确性。

我们的数据集是完整的,这意味着没有缺失的特征; 但是,某些功能有一个“*”而不是类别,这意味着此功能无关紧要。 我们将用零替换所有这些星号。

 from sklearn.linear_model import LogisticRegression # Data data_path = os.path.join(os.getcwd(), "data/shuttle-landing-control.csv") dataset = read_csv(data_path, header=None, names=['Auto', 'Stability', 'Error', 'Sign', 'Wind', 'Magnitude', 'Visibility'], na_values='*').fillna(0) # Prepare features X = dataset[['Stability', 'Error', 'Sign', 'Wind', 'Magnitude', 'Visibility']] y = dataset[['Auto']].values.reshape(1, -1)[0] model = LogisticRegression() model.fit(X, y) # For now, we're missing one important concept. We don't know how well our model # works, and because of that, we cannot really improve the performance of our hypothesis. # There are a lot of useful metrics, but for now, we will validate how well # our algorithm performs on the dataset it learned from. "Score of our model is %2.2f%%" % (model.score(X, y) * 100)

Score of our model is 73.33%

验证?

在前面的示例中,我们使用学习数据验证了模型的性能。 但是,考虑到我们的算法既可以欠拟合数据,也可以过拟合数据,这现在是一个不错的选择吗? 让我们看一个更简单的例子,当我们有一个表示房屋大小的特征和另一个表示价格的特征时。

 from sklearn.pipeline import make_pipeline from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.model_selection import cross_val_score # Ground truth function ground_truth = lambda X: np.cos(15 + np.pi * X) # Generate random observations around the ground truth function n_samples = 15 degrees = [1, 4, 30] X = np.linspace(-1, 1, n_samples) y = ground_truth(X) + np.random.randn(n_samples) * 0.1 plt.figure(figsize=(14, 5)) models = {} # Plot all machine learning algorithm models for idx, degree in enumerate(degrees): ax = plt.subplot(1, len(degrees), idx + 1) plt.setp(ax, xticks=(), yticks=()) # Define the model polynomial_features = PolynomialFeatures(degree=degree) model = make_pipeline(polynomial_features, LinearRegression()) models[degree] = model # Train the model model.fit(X[:, np.newaxis], y) # Evaluate the model using cross-validation scores = cross_val_score(model, X[:, np.newaxis], y) X_test = X plt.plot(X_test, model.predict(X_test[:, np.newaxis]), label="Model") plt.scatter(X, y, edgecolor='b', s=20, label="Observations") plt.xlabel("x") plt.ylabel("y") plt.ylim((-2, 2)) plt.title("Degree {}\nMSE = {:.2e}".format( degree, -scores.mean())) plt.show() 

由一阶、四阶和 30 阶多项式建模的相同数据,以证明欠拟合和过拟合。

如果机器学习算法模型既不能概括训练数据也不能概括新的观察结果,那么它就是拟合的。 在上面的示例中,我们使用了一个简单的线性假设,它并不能真正代表实际的训练数据集,并且性能会很差。 通常,欠拟合不会被讨论,因为它可以很容易地检测到一个好的度量。

如果我们的算法记住了它所显示的每一个观察结果,那么它在训练数据集之外的新观察结果上的性能就会很差。 这称为过拟合。 例如,一个 30 次多项式模型通过了大部分点并且在训练集上获得了非常好的分数,但除此之外的任何东西都会表现不佳。

我们的数据集由一个特征组成,并且很容易在 2D 空间中绘制; 然而,在现实生活中,我们可能拥有具有数百个特征的数据集,这使得它们无法在欧几里得空间中直观地绘制。 为了查看模型是欠拟合还是过拟合,我们还有哪些其他选择?

是时候向您介绍学习曲线的概念了。 这是一个简单的图表,绘制了训练样本数量的均方误差。

在学习材料中,您通常会看到类似于以下的图表:

基于多项式次数的理论学习曲线变化。

但是,在现实生活中,您可能无法获得如此完美的画面。 让我们绘制每个模型的学习曲线。

 from sklearn.model_selection import learning_curve, ShuffleSplit # Plot learning curves plt.figure(figsize=(20, 5)) for idx, degree in enumerate(models): ax = plt.subplot(1, len(degrees), idx + 1) plt.title("Degree {}".format(degree)) plt.grid() plt.xlabel("Training examples") plt.ylabel("Score") train_sizes = np.linspace(.6, 1.0, 6) # Cross-validation with 100 iterations to get a smoother mean test and training # score curves, each time with 20% of the data randomly selected as a validation set. cv = ShuffleSplit(n_splits=100, test_size=0.2, random_state=0) model = models[degree] train_sizes, train_scores, test_scores = learning_curve( model, X[:, np.newaxis], y, cv=cv, train_sizes=train_sizes, n_jobs=4) train_scores_mean = np.mean(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training score") plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Test score") plt.legend(loc = "best") plt.show() 

三个图表的训练分数与测试分数,数据由一阶、四阶和 30 阶多项式建模。

在我们的模拟场景中,代表训练分数的蓝线看起来像一条直线。 实际上,它仍然略有下降——您实际上可以在一次多项式图中看到这一点,但在其他图中,在这个分辨率下它太微妙了,无法分辨。 我们至少清楚地看到,在“高偏差”场景下,训练和测试观察的学习曲线之间存在巨大差距。

在中间的“正常”学习率图上,您可以看到训练分数线和测试分数线是如何结合在一起的。

在“高方差”图上,您可以看到在样本数量较少的情况下,测试和训练分数非常相似; 但是,当您增加样本数量时,训练分数几乎保持完美,而测试分数却远离它。


如果我们使用非线性假设,例如具有更多多项式特征的假设,我们可以修复欠拟合模型(也称为具有高偏差的模型)。

我们的过拟合模型(高方差)通过了它显示的每一个例子; 然而,当我们引入测试数据时,学习曲线之间的差距会扩大。 我们可以使用正则化、交叉验证和更多数据样本来修复过拟合模型。

交叉验证

避免过度拟合的一种常见做法是保留部分可用数据并将其用作测试集。 然而,在评估不同的模型设置时,例如多项式特征的数量,我们仍然存在过度拟合测试集的风险,因为可以调整参数以实现最佳估计器性能,因此,我们对测试集的了解可以泄漏到模型中。 为了解决这个问题,我们需要保留数据集的另一部分,称为“验证集”。 在训练集上进行训练,当我们认为我们已经达到了最佳模型性能时,我们可以利用验证集进行最终评估。

然而,通过将可用数据分成三组,我们大大减少了可用于训练模型的样本数量,并且结果可能取决于训练-验证对组的特定随机选择。

这个问题的一种解决方案是一种称为交叉验证的过程。 在标准的 $k$-fold 交叉验证中,我们将数据划分为 $k$ 子集,称为折叠。 然后,我们在 $k-1$ 折叠上迭代训练算法,同时使用剩余折叠作为测试集(称为“保持折叠”)。

显示 k 折交叉验证中保留折叠位置的网格。

交叉验证允许您仅使用原始训练集调整参数。 这使您可以将测试集保留为真正看不见的数据集,以选择最终模型。

还有更多的交叉验证技术,例如leave P out分层 $k$-foldshuffle 和 split等,但它们超出了本文的范围。

正则化

这是另一种可以帮助解决模型过度拟合问题的技术。 大多数数据集都有模式和一些噪音。 正则化的目标是减少噪声对模型的影响。

将原始函数与其正则化对应物并列的图。

有三种主要的正则化技术:Lasso、Tikhonov 和弹性网络。

L1 正则化(或Lasso 正则化)将选择一些特征缩小到零,这样它们就不会在最终模型中发挥任何作用。 L1可以看作是一种选择重要特征的方法。

L2 正则化(或Tikhonov 正则化)将迫使所有特征都相对较小,这样它们对模型的影响就会更小。

弹性网是 L1 和 L2 的组合。

归一化(特征缩放)

特征缩放也是预处理数据时的重要一步。 我们的数据集可能具有值为 $[-\infty、\infty]$ 的特征和具有不同尺度的其他特征。 这是一种标准化独立值范围的方法。

特征缩放也是提高学习模型性能的重要过程。 首先,如果所有特征都缩放到相同的范数,梯度下降将收敛得更快。 此外,许多算法——例如支持向量机 (SVM)——通过计算两点之间的距离来工作,如果其中一个特征具有广泛的值,那么距离将受到该特征的高度影响。

支持向量机

SVM 是另一种广泛流行的机器学习算法,可用于分类和回归问题。 在 SVM 中,我们将每个观察值绘制为 $n$ 维空间中的一个点,其中 $n$ 是我们拥有的特征数。 每个特征的值是特定坐标的值。 然后,我们尝试找到一个能够很好地分离两个类的超平面。

显示分离两类数据点的超平面的图表,其中还显示了它们的一些支持向量。

在我们确定了最好的超平面之后,我们想要添加边距,这将进一步分离两个类。

显示带有边距的超平面的图表。

在特征数量非常多或特征数量大于数据样本数量的情况下,SVM 非常有效。 然而,由于 SVM 是在向量基础上运行的,因此在使用之前对数据进行归一化至关重要。

神经网络

神经网络算法可能是机器学习研究中最令人兴奋的领域。 神经网络试图模仿大脑的神经元是如何连接在一起的。

神经网络的插图,显示映射到临时值的各种输入,这些值又映射到单个输出。

这就是神经网络的样子。 我们将许多节点组合在一起,每个节点接受一组输入,对它们进行一些计算,然后输出一个值。

有监督学习和无监督学习的神经网络算法种类繁多。 神经网络可用于驾驶自动驾驶汽车、玩游戏、陆地飞机、图像分类等。

臭名昭著的泰坦尼克号

泰坦尼克号是一艘英国客轮,在与冰山相撞后于 1912 年 4 月 15 日在北大西洋沉没。 大约有 2,224 名船员和乘客,超过 1,500 人死亡,使其成为有史以来最致命的商业海难之一。

现在,由于我们了解用于分类问题的最基本机器学习算法背后的直觉,我们可以应用我们的知识来预测泰坦尼克号上的人的生存结果。

我们的数据集将从 Kaggle 数据科学竞赛平台借用。

 import os from pandas import read_csv, concat # Load data data_path = os.path.join(os.getcwd(), "data/titanic.csv") dataset = read_csv(data_path, skipinitialspace=True) dataset.head(5)
乘客编号幸存下来姓名性别年龄同胞票价登船
0 1 0 3 Braund,欧文·哈里斯先生男性22.0 1 0 A/5 21171 7.2500小号
1 2 1 1 卡明斯,约翰·布拉德利夫人(弗洛伦斯·布里格斯... 女性38.0 1 0 电脑 17599 71.2833 C85 C
2 3 1 3 海基宁小姐莱纳女性26.0 0 0 斯通/O2。 3101282 7.9250小号
3 4 1 1 Futrelle,雅克·希思夫人(莉莉·梅·皮尔) 女性35.0 1 0 113803 53.1000 C123 小号
4 5 0 3 艾伦,威廉·亨利先生男性35.0 0 0 373450 8.0500小号

我们的第一步是加载和探索数据。 我们有891条测试记录; 每条记录具有以下结构:

  • 乘客 ID – 机上乘客的 ID
  • 生存——这个人是否在坠机事故中幸存下来
  • pclass – 机票等级,例如 1st、2nd、3rd
  • 性别 – 乘客性别:男性或女性
  • 名称 - 包括标题
  • 年龄 - 年龄
  • sibsp – 泰坦尼克号上的兄弟姐妹/配偶的数量
  • parch – 泰坦尼克号上的父母/孩子人数
  • 票——票号
  • 票价——乘客票价
  • 客舱 - 客舱号码
  • 登船 – 登船港

该数据集包含数值数据和分类数据。 通常,深入研究数据并在此基础上提出假设是一个好主意。 但是,在这种情况下,我们将跳过这一步并直接进行预测。

 import pandas as pd # We need to drop some insignificant features and map the others. # Ticket number and fare should not contribute much to the performance of our models. # Name feature has titles (eg, Mr., Miss, Doctor) included. # Gender is definitely important. # Port of embarkation may contribute some value. # Using port of embarkation may sound counter-intuitive; however, there may # be a higher survival rate for passengers who boarded in the same port. dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.', expand=False) dataset = dataset.drop(['PassengerId', 'Ticket', 'Cabin', 'Name'], axis=1) pd.crosstab(dataset['Title'], dataset['Sex'])
Title \ Sex 女性男性
上尉0 1
科尔0 2
伯爵夫人1 0
大学教师0 1
博士1 6
琼克尔0 1
淑女1 0
主要的0 2
掌握0 40
错过182 0
米勒2 0
1 0
先生0 517
太太125 0
小姐1 0
0 6
先生0 1
 # We will replace many titles with a more common name, English equivalent, # or reclassification dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col',\ 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Other') dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss') dataset['Title'] = dataset['Title'].replace('Ms', 'Miss') dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs') dataset[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
标题幸存下来
0 掌握0.575000
1 错过0.702703
2 先生0.156673
3 太太0.793651
4 其他0.347826
 # Now we will map alphanumerical categories to numbers title_mapping = { 'Mr': 1, 'Miss': 2, 'Mrs': 3, 'Master': 4, 'Other': 5 } gender_mapping = { 'female': 1, 'male': 0 } port_mapping = { 'S': 0, 'C': 1, 'Q': 2 } # Map title dataset['Title'] = dataset['Title'].map(title_mapping).astype(int) # Map gender dataset['Sex'] = dataset['Sex'].map(gender_mapping).astype(int) # Map port freq_port = dataset.Embarked.dropna().mode()[0] dataset['Embarked'] = dataset['Embarked'].fillna(freq_port) dataset['Embarked'] = dataset['Embarked'].map(port_mapping).astype(int) # Fix missing age values dataset['Age'] = dataset['Age'].fillna(dataset['Age'].dropna().median()) dataset.head()
幸存下来性别年龄同胞票价登船标题
0 0 3 0 22.0 1 0 7.2500 0 1
1 1 1 1 38.0 1 0 71.2833 1 3
2 1 3 1 26.0 0 0 7.9250 0 2
3 1 1 1 35.0 1 0 53.1000 0 3
4 0 3 0 35.0 0 0 8.0500 0 1

At this point, we will rank different types of machine learning algorithms in Python by using scikit-learn to create a set of different models. It will then be easy to see which one performs the best.

  • Logistic regression with varying numbers of polynomials
  • 具有线性核的支持向量机
  • 具有多项式核的支持向量机
  • 神经网络

For every single model, we will use $k$-fold validation.

 from sklearn.model_selection import KFold, train_test_split from sklearn.pipeline import make_pipeline from sklearn.preprocessing import PolynomialFeatures, StandardScaler from sklearn.neural_network import MLPClassifier from sklearn.svm import SVC # Prepare the data X = dataset.drop(['Survived'], axis = 1).values y = dataset[['Survived']].values X = StandardScaler().fit_transform(X) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state = None) # Prepare cross-validation (cv) cv = KFold(n_splits = 5, random_state = None) # Performance p_score = lambda model, score: print('Performance of the %s model is %0.2f%%' % (model, score * 100)) # Classifiers names = [ "Logistic Regression", "Logistic Regression with Polynomial Hypotheses", "Linear SVM", "RBF SVM", "Neural Net", ] classifiers = [ LogisticRegression(), make_pipeline(PolynomialFeatures(3), LogisticRegression()), SVC(kernel="linear", C=0.025), SVC(gamma=2, C=1), MLPClassifier(alpha=1), ]
 # iterate over classifiers models = [] trained_classifiers = [] for name, clf in zip(names, classifiers): scores = [] for train_indices, test_indices in cv.split(X): clf.fit(X[train_indices], y[train_indices].ravel()) scores.append( clf.score(X_test, y_test.ravel()) ) min_score = min(scores) max_score = max(scores) avg_score = sum(scores) / len(scores) trained_classifiers.append(clf) models.append((name, min_score, max_score, avg_score)) fin_models = pd.DataFrame(models, columns = ['Name', 'Min Score', 'Max Score', 'Mean Score'])
 fin_models.sort_values(['Mean Score']).head()
姓名最低分数最高分平均分
2 线性支持向量机0.793296 0.821229 0.803352
0 逻辑回归0.826816 0.860335 0.846927
4 神经网络0.826816 0.860335 0.849162
1 Logistic Regression with Polynomial Hypotheses 0.854749 0.882682 0.869274
3 RBF 支持向量机0.843575 0.888268 0.869274

Ok, so our experimental research says that the SVM classifier with a radial basis function (RBF) kernel performs the best. Now, we can serialize our model and re-use it in production applications.

 import pickle svm_model = trained_classifiers[3] data_path = os.path.join(os.getcwd(), "best-titanic-model.pkl") pickle.dump(svm_model, open(data_path, 'wb'))

Machine learning is not complicated, but it's a very broad field of study, and it requires knowledge of math and statistics in order to grasp all of its concepts.

Right now, machine learning and deep learning are among the hottest topics of discussion in Silicon Valley, and are the bread and butter of almost every data science company, mainly because they can automate many repetitive tasks including speech recognition, driving vehicles, financial trading, caring for patients, cooking, marketing, and so on.

Now you can take this knowledge and solve challenges on Kaggle.

This was a very brief introduction to supervised machine learning algorithms. Luckily, there are a lot of online courses and information about machine learning algorithms. I personally would recommend starting with Andrew Ng's course on Coursera.

资源

  • Andrew Ng's course on Coursera
  • Kaggle 数据集
  • A deep learning reading list
  • A list of free books on machine learning algorithms, data mining, deep learning, and related topics
  • 机器学习理论及其应用简介:带有示例的可视化教程
Related: Machines and Trust: How to Mitigate AI Bias