Python笔记··By/蜜汁炒酸奶

探索监督式机器学习算法

前言

这周送来一篇python实现的机器学习的相关文章。一起学习吧。能力有限,一切以英文原文为准。里面部分公式改好好久,总是有一些变不过来,这些暂时看英文里面的吧。

原文Exploring Supervised Machine Learning Algorithms 作者VLADYSLAV MILLIER

正文

这个阅读的主要目标是理解足够的统计方法,以便能够利用Python的scikit-learn库中的机器学习算法,然后应用这些知识来解决经典的机器学习问题。 我们旅程的第一站将带领我们了解机器学习的简要历史。然后,我们将深入到不同的算法。在我们的最后一站,我们将用我们学到的解决泰坦尼克号生存率预测问题。 一些免责声明:

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

有了这个声明,让我们前进!

机器学习算法简介

只要你冒险进入这个领域,你意识到机器学习不如你想像的浪漫。起初,我充满了希望,当我学到更多的东西之后,我就可以构建出我自己的Jarvis AI,这个Jarvis AI会花一天的时间为我编码,为我赚钱,这样我可以花上整整一天的时间在户外读书,开摩托车,享受鲁莽的生活方式,而我的个人Jarvis让我的口袋更深。然而,我很快意识到机器学习算法的基础是统计学,我个人觉得这个算法没有什么意思。幸运的是,事实证明,“枯燥”的统计数据有一些非常吸引人的应用。 您很快就会发现,要获得这些迷人的应用程序,您需要非常了解统计信息。机器学习算法的目标之一是在提供的数据中找到统计依赖关系。 提供的数据可以是检查血压与年龄之间的任何事项,也可以是根据各种像素的颜色查找手写文本。 也就是说,我很好奇,看看是否可以使用机器学习算法来查找加密散列函数(SHA,MD5等)中的依赖关系 - 但是,由于正确的加密基元是以这种方式构建的他们消除了依赖性,并产生了难以预测的输出。我相信,无限的时间,机器学习算法可以破解任何加密模型。 不幸的是,我们没有那么多的时间,所以我们需要找到另一种有效的方式来挖掘加密货币。我们到现在有多远?

机器学习算法简史

机器学习算法的根源来自18世纪英国统计学家托马斯·贝叶斯(Thomas Bayes)。他的论文“解决机会主义问题的一个问题”奠定了贝叶斯定理的基础,在统计学领域得到了广泛的应用。 皮埃尔 - 西蒙·拉普拉斯(Pierre-Simon Laplace)在19世纪出版了“可分析概率论 ”(Théorieanalytique desprobabilités),扩展了贝叶斯的工作,并将我们今天所知的定义为贝叶斯定理。就在此之前,阿德里安·玛丽·勒布勒(Adrien-Marie Legendre)描述了“最小二乘法”,也被广泛用于监督学习。 20世纪是这个领域大部分公开发现的时期。安德烈·马尔可夫发明了他用来分析诗歌的马尔可夫链。艾伦·图灵(Alan Turing)提出了一种可以变成人工智能的学习机器,基本上预示着遗传算法。弗兰克·罗森布拉特(Frank Rosenblatt)发明了“ 感知器”(Perceptron),在媒体上激起了巨大的兴奋和广泛的报道。 但是,20世纪70年代,人们对​​AI的观念产生了很多悲观情绪,因此资金减少 - 所以这个时期被称为AI冬季。20世纪80年代的反向传播的重新发现引起了机器学习研究的复兴。而今天,这又是一个热门话题。 已故的Leo Breiman区分了两种统计建模范式:数据建模和算法建模。“算法建模”意味着或多或少像随机森林这样的机器学习算法。 机器学习和统计是密切相关的领域。根据迈克尔·乔丹的说法,从方法论原理到理论工具,机器学习的思想在统计学方面有着悠久的史前史。他还建议数据科学作为机器学习专家和统计人员都在暗中处理的总体问题的占位术语。

机器学习算法的分类

机器学习领域被称为监督学习无监督学习两大支柱。有些人还考虑了一个新的学习领域 - 深度学习 - 与监督学习和无监督学习的问题分开。 监督式学习是指在计算机中输入输入和期望输出的例子。计算机的目标是学习将输入映射到输出的通用公式。这可以进一步细分为:

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

相比之下,无监督学习就是在没有标签的情况下,根据算法来找到输入的结构。当我们只需要发现隐藏的模式时,无监督学习本身就是一个目标。 深度学习是一个新的研究领域,受人脑结构和功能的启发,基于人工神经网络而不仅仅是统计概念。深度学习可以用于有监督和无监督的方法。 在这篇文章中,我们将只通过一些更简单的监督机器学习算法,并用它们来计算一个人在泰坦尼克号悲剧下沉的生存机会。但总的来说,如果你不确定使用哪种算法,那么一个好的开始就是scikit-learn的机器学习算法cheat-sheet

基本监督机器学习模型

也许最简单的算法是线性回归。有时候,这可以用图表的形式表示为一条直线,但是尽管有它的名字,如果有多项式假设,这条线可能是一条曲线。无论哪种方式,它都模拟了标量因变量y与由x表示的一个或多个解释值。 用非专业术语来说,这意味着线性回归是学习每个已知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')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

[<matplotlib.lines.Line2D at 0x11424b828>] 探索监督式机器学习算法 在上面的图表中,每个蓝点代表我们的数据样本,蓝线代表我们的算法需要学习的假设。那么这个假设究竟是什么呢? 为了解决这个问题,我们需要学习 $x$和 $y$之间的依赖关系,其表示为y = f(x) 。因此f(x) 是理想的目标函数。机器学习算法将尝试猜测假设函数h(x)是最接近未知的$f(x)$的近似值。 假设为线性回归问题的最简单的可能形式是这样的: $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 mmHg。现在,我们需要计算这个错误在我们的训练数据集的每一个条目,然后一起概括($\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))
1
2
3
4
5
6
7
8
9
10
11
12
13

J(Theta) = 1901.95 现在,我们需要找到我们的成本函数值是最小的 [latex]\theta [/latex]值。但是我们怎么做呢? $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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

探索监督式机器学习算法 成本函数是凸的,这意味着在区间$[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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

探索监督式机器学习算法

<matplotlib.figure.Figure at 0x120fae1d0>
1
print( 'Predicted blood pressure at 25 y.o.   = ', regr.predict(25) )
print( 'Predicted blood pressure at 45 y.o.   = ', regr.predict(45) )
print( 'Predicted blood pressure at 27 y.o.   = ', regr.predict(27) )
print( 'Predicted blood pressure at 34.5 y.o. = ', regr.predict(34.5) )
print( 'Predicted blood pressure at 78 y.o.   = ', regr.predict(78) )
1
2
3
4
5
Predicted blood pressure at 25 y.o.   =  [[ 122.98647692]]
Predicted blood pressure at 45 y.o.   =  [[ 142.40388395]]
Predicted blood pressure at 27 y.o.   =  [[ 124.92821763]]
Predicted blood pressure at 34.5 y.o. =  [[ 132.20974526]]
Predicted blood pressure at 78 y.o.   =  [[ 174.44260555]]
1
2
3
4
5

统计数据的类型

在处理机器学习问题的数据时,识别不同类型的数据非常重要。我们可能有数字(连续或离散),分类或有序的数据。 数值数据具有作为测量的意义。例如,年龄,体重,个人所拥有的比特数,或者每月可以写多少文章。数值数据可以进一步分解成离散和连续的类型。

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

分类数据表示诸如人的性别,婚姻状况,国家等的数值。该数据可以取数值,但是这些数字没有数学意义。你不能把它们加在一起。 序数据可以是其他两种类型的混合,这些类别可以用数学上有意义的方式进行编号。一个常见的例子就是收视率:我们经常被要求以一到十的比例来评定事物,只允许整数。虽然我们可以使用这种方法(例如,为某种事物寻找平均评分),但我们经常把数据看作是将数据应用于机器学习方法的分类。

Logistic回归

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

  • 这是一只狗还是一只猫?
  • 这个肿瘤是恶性的还是良性的?
  • 这酒是好还是坏?
  • 这封电子邮件是垃圾邮件吗?

甚至:

  • 图片中的哪个数字?
  • 这个电子邮件属于哪个类别?

所有这些问题都是针对分类问题的。最简单的分类算法被称为逻辑回归logistic regression),除了它有一个不同的假设之外,它最终与线性回归相同。 首先,我们可以重复使用相同的线性假设$h_\theta(x) = \theta^T X $(这是矢量化的形式)。而线性回归可以输出区间$[a,b]$中的任何数字,logistic回归只能在输出值$[ -1 ,1 ]$,这是对象落入一个给定类别的概率。 使用S形函数,我们可以将任何数值来表示的间隔的值$[ -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}} $ 之后,我们可以应用一个简单的阈值,如果假设大于零,这是一个真值,否则是错误的。 $% <![CDATA[ h_\theta(x) = \begin{cases} 1 & \mbox{if } \theta^T X > 0 \\ 0 & \mbox{else} \end{cases} %]]>$ 这意味着我们可以使用相同的代价函数和相同的梯度下降算法来学习逻辑回归的假设。 在我们的下一个机器学习算法的例子中,我们将建议航天飞机的飞行员是否应该使用自动或手动着陆控制。我们有一个非常小的数据集 -15个样本,它由六个特征和基本事实组成。 在机器学习算法中,术语“ 基本事实 ”是指监督学习技术的训练集分类的准确性。 我们的数据集是完整的,这意味着没有缺失的功能; 然而,一些功能有一个“*”而不是类别,这意味着这个功能并不重要。我们将用零替换所有这些星号。

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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

探索监督式机器学习算法 机器学习算法模型不适用于训练数据和新的观测数据,在上面的例子中,我们使用了一个简单的线性假设,它并不真正代表实际的训练数据集,而且性能也很差。通常情况下,不适合讨论不足,因为可以很容易地检测到一个好的指标。 如果我们的算法记住了每一个观察结果,那么在训练数据集之外的新观察结果中性能会很差。这被称为过度拟合。例如,一个30度的多项式模型通过了大多数的点,并且在训练集上有非常好的分数,但是除此之外的任何东西都会表现的很差。 我们的数据集由一个特征组成,并且在二维空间中绘图很简单; 然而,在现实生活中,我们可能拥有数百个具有数百个特征的数据集,这使得他们无法在欧几里德空间中进行视觉绘图。我们还有什么其他的选择来看看模型是不适合的还是过度拟合? 现在是时候向您介绍学习曲线的概念。这是一个简单的图表,绘制训练样本数量的均方误差。 在学习资料时,您通常会看到类似于以下的图表: 探索监督式机器学习算法 但在现实生活中,你可能得不到这样完美的画面。让我们绘制每个模型的学习曲线。

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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

在我们的模拟场景中,表示训练分数的蓝线似乎是一条直线。在现实中,它仍然略微下降 - 你可以在一阶多项式图中看到这一点,但在其他情况下,在这个分辨率下,这太微妙了。我们至少清楚地看到,培训和测试观测的学习曲线与“高偏差”情景之间存在巨大差距。 在中间的“正常”学习速度图上,您可以看到训练得分和测验得分线是如何结合在一起的。 而在“高方差”图表中,可以看到,样本数量少,测试和训练分数非常相似; 然而,当你增加样本的数量时,训练分数几乎是完美的,而测试分数则远离它。


如果我们使用非线性假设,例如具有更多多项式特征的假设,我们可以修正不足的模型(也称为高偏差模型)。 我们的过度拟合模型(高方差)通过它显示的每一个例子。然而,当我们介绍测试数据时,学习曲线之间的差距会扩大。我们可以使用正则化,交叉验证和更多的数据样本来修复过度拟合模型。

交叉验证

避免过度拟合的一种常见做法是保留部分可用数据并将其用作测试集。但是,在评估不同的模型设置(如多项式特征的数目)时,我们仍然有过度拟合测试集的风险,因为可以调整参数以实现最佳估计器性能,因此,我们对测试集的了解可以泄漏到模型中。为了解决这个问题,我们需要保留另一部分数据集,这就是所谓的“验证集”。在训练集上进行训练,当我们认为我们已经达到了最优的模型性能时,我们可以使利用验证集的最终评估。 然而,通过将可用数据划分为三组,我们大大减少了可以用于训练模型的样本数量,并且结果可以取决于训练 - 验证对集合的特定随机选择。 这个问题的一个解决方案是一个称为交叉验证的过程。在标准k倍交叉验证,我们将数据分成k子集,称为折叠。然后,我们在k - 1上迭代训练算法同时使用剩余的折叠作为测试集(称为“保持折叠”)。 探索监督式机器学习算法 交叉验证允许您只调整原始训练集的参数。这使您可以将测试集保存为真正不可见的数据集,以便选择最终的模型。 有更多的交叉验证技术,比如离开P分层k折叠洗牌和拆分等,但它们超出了本文的范围。

正则

这是另一种可以帮助解决模型过拟合问题的技术。大多数数据集都有一个模式和一些噪音。正则化的目标是减少噪声对模型的影响。   探索监督式机器学习算法 有三个主要的正规化技术:套索,吉洪诺夫和弹性网。 L1正规化(或Lasso正则化)将选择一些特征缩小到零,以使它们在最终模型中不起作用。L1可以被看作是一种选择重要特征的方法。 L2正规化(或者Tikhonov正则化)将强制所有的特征相对较小,从而对模型的影响较小。 弹性网是L1和L2 的组合

规范化(功能缩放)

在预处理数据的同时,功能缩放也是一个重要的步骤。我们的数据集可能具有值[ - ∞ ,∞ ]的特征和其他功能与不同的规模。这是一种标准化独立值范围的方法。 特征缩放也是提高学习模型性能的重要过程。首先,如果所有的特征都缩放到相同的标准,梯度下降会更快地收敛。另外,很多算法(例如支持向量机(SVM))都是通过计算两点之间的距离来实现的,如果其中一个特征具有宽泛的值,则距离将受到该特征的高度影响。

支持向量机

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

探索监督式机器学习算法 在确定最好的超平面之后,我们要添加边距,这将进一步分离两个类。 探索监督式机器学习算法 当特征数量非常高或者特征数量大于数据样本数量时,SVM非常有效。但是,由于SVM是以矢量为基础运行的,所以在使用之前对数据进行规范化至关重要。

神经网络

神经网络算法可能是机器学习研究中最令人兴奋的领域。神经网络试图模仿大脑的神经元如何连接在一起。 探索监督式机器学习算法 这是神经网络的外观。我们将许多节点组合在一起,其中每个节点接受一组输入,对它们应用一些计算,并输出一个值。 有监督和无监督学习的神经网络算法种类繁多。神经网络可以用来驱动自动驾驶汽车,玩游戏,陆地飞机,分类图像,等等。

臭名昭着的泰坦尼克号

泰坦尼克号是英国的一艘客轮,1912年4月15日在北大西洋沉没,与冰山相撞。有大约2,224名船员和乘客,1500多人死亡,成为有史以来最致命的商业海上灾害之一。 现在,由于我们理解用于分类问题的最基本的机器学习算法背后的直觉,我们可以运用我们的知识预测泰坦尼克号上的那些人的生存结果。 我们的数据集将从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)
1
2
3
4
5
6
7
8
|   |             |    |        |                                  |    |      |       |   |                   |         |      |    |
| - | ----------- | -- | ------ | -------------------------------- | -- | ---- | ----- | - | ----------------- | ------- | ---- | -- |
|   | PassengerId | 幸存 | Pclass | 名称                               | 性别 | 年龄   | SibSp | 胹 | 票                 | 票价      | 舱    | 开始 |
| 0 | 1           | 0  | 3      | 布朗德先生,欧文哈里斯先生                    | 男  | 22.0 | 1     | 0 | A / 5 21171       | 7.2500  | 为NaN | 小号 |
| 1 | 2           | 1  | 1      | 约翰·布拉德利夫人(Florence Briggs Th ... | 女  | 38.0 | 1     | 0 | PC 17599          | 71.2833 | C85  | C  |
| 2 | 3           | 1  | 3      | Heikkinen,Laina小姐                | 女  | 26.0 | 0     | 0 | STON / O2。3101282 | 7.9250  | 为NaN | 小号 |
| 3 | 4           | 1  | 1      | Futrelle,雅克·希思夫人(Lily May Peel)  | 女  | 35.0 | 1     | 0 | 113803            | 53.1000 | C123 | 小号 |
| 4 | 五           | 0  | 3      | 艾伦先生,威廉亨利先生                      | 男  | 35.0 | 0     | 0 | 373450            | 8.0500  | 为NaN | 小号 |

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

  • passengerId - 船上乘客的ID
  • 生存 - 这个人是否幸存下来的崩溃
  • 门票类,例如,1日,2日,3日
  • 性别 - 旅客的性别:男性或女性
  • 名称 - 标题包括在内
  • 年龄 - 年龄
  • 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 (e.g., 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'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|          |     |     |
| -------- | --- | --- |
| 标题\\性别   | 女   | 男   |
| 上尉       | 0   | 1   |
| 关口       | 0   | 2   |
| 伯爵夫人     | 1   | 0   |
| 唐        | 0   | 1   |
| 博士       | 1   | 6   |
| Jonkheer | 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()
1
2
3
4
5
6
7
8
9
10
|    |    |          |
| -- | -- | -------- |
| 标题 | 幸存 |
| 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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|   |    |        |    |      |       |   |         |    |    |
| - | -- | ------ | -- | ---- | ----- | - | ------- | -- | -- |
|   | 幸存 | Pclass | 性别 | 年龄   | SibSp | 胹 | 票价      | 开始 | 标题 |
| 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  |

此时,我们将使用Python来scikit-learn对不同类型的机器学习算法进行排序,以创建一组不同的模型。这将很容易看到哪一个表现最好。

  • 用不同数量的多项式进行逻辑回归
  • 支持具有线性内核的向量机
  • 用多项式内核支持向量机
  • 神经网络

对于每一个模型,我们将使用k倍验证。

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),
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fin_models.sort_values(['Mean Score']).head()
1
|   |                    |          |          |          |
| - | ------------------ | -------- | -------- | -------- |
|   | 名称                 | 最低分数     | 最高分数     | 平均分数     |
| 2 | 线性SVM              | 0.793296 | 0.821229 | 0.803352 |
| 0 | Logistic回归         | 0.826816 | 0.860335 | 0.846927 |
| 4 | 神经网络               | 0.826816 | 0.860335 | 0.849162 |
| 1 | 多项式假设的Logistic回归分析 | 0.854749 | 0.882682 | 0.869274 |
| 3 | RBF SVM            | 0.843575 | 0.888268 | 0.869274 |

好的,所以我们的实验研究表明,具有径向基函数(RBF)核的SVM分类器表现最好。现在,我们可以序列化我们的模型并在生产应用程序中重新使用它。

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'))
1
2
3
4
5
6

机器学习并不复杂,但它是一个非常广泛的学习领域,它需要掌握数学和统计知识,以掌握其所有的概念。 现在,机器学习和深度学习是硅谷最热门的话题之一,主要是因为他们可以自动执行许多重复的任务,包括语音识别,驾驶车辆,金融交易,照顾病人烹饪营销等等。 现在你可以把这些知识和解决Kaggle的挑战。 这是监督机器学习算法的一个非常简单的介绍。幸运的是,有很多关于机器学习算法的在线课程和信息。我个人会建议从Andrew Ng的Coursera课程开始。

资源

了解基础知识

[toggle hide=“yes” title=“机器学习如何工作?” color=“”] 与传统的硬编码算法相比,机器学习算法使用统计分析自动形成模型。这使得他们随着时间的推移而发展,因为他们在查找数据中的模式并对其分类进行预测。 [/toggle] [toggle hide=“yes” title=“机器学习可以用于什么?” color=“”] 机器学习的应用几乎是无限的。它可以用于从简单的天气预测和数据聚类到复杂的特征学习; 自主驾驶和飞行; 图像,语音和视频识别; 搜索和推荐引擎; 病人诊断等。 [/toggle] [toggle hide=“yes” title=“有监督和无监督分类有什么区别?” color=“”] 监督分类需要训练数据标签:一张照片是一只猫,另一张是一只狗。无监督分类是算法找到共同特征并分离数据本身的地方。它不会明确地告诉我们这个图像是一只猫,但它可以将猫和狗分开。 [/toggle] [toggle hide=“yes” title=“什么是监督学习与无监督学习?” color=“”] 监督学习是指明确告诉算法正确答案的地方,所以算法可以学习,并可以预测以前看不见的数据的答案。无监督学习是算法自己找出答案的地方。 [/toggle] [toggle hide=“yes” title=“我在哪里可以学习机器学习技术?” color=“”] 开始学习机器学习的最好的地方是观看Coursera的Andrew’s Ng课程,在文章结尾部分将介绍资源。从那里开始接受Kaggle的挑战,以更好地理解不同的框架和方法。 [/toggle] [toggle hide=“yes” title=“我如何知道使用哪种机器学习算法?” color=“”] 在选择正确的算法时,需要考虑很多因素:数据集的大小,数据的性质,速度和准确性等。直到你开发自己的直觉,你可以使用现有的cheatsheet,像scikit-learn提供。 [/toggle]

关于作者: Vlad是一名积极主动,多才多艺的积极软件开发人员,拥有许多编程语言和框架方面的工作经验。他对架构,构建和处理复杂问题的可扩展系统和应用感兴趣。他目前正在完善他对Scala和机器学习的知识。

预览
Loading comments...
0 条评论

暂无数据

example
预览