本文概述
本书的主要目的是了解足够的统计方法, 以便能够利用Python的scikit-learn库中的机器学习算法, 然后将这些知识应用于解决经典的机器学习问题。
我们旅程的第一站将带我们简要了解机器学习的历史。然后, 我们将深入研究不同的算法。在最后一站, 我们将使用所学的知识来解决《泰坦尼克号生存率预测》问题。
一些免责声明:
- 我是全栈软件工程师, 而不是机器学习算法专家。
- 我假设你了解一些基本的Python。
- 这是探索性的, 因此并非像教程中那样解释每个细节。
在此说明下, 让我们开始吧!
机器学习算法快速入门
一旦你涉足这一领域, 你就会意识到机器学习并不像你想象的那样浪漫。最初, 我充满希望, 希望我能学到更多东西之后, 才能构建自己的Jarvis AI, 它将花费一整天的时间编写软件并为我赚钱, 这样我就可以整天在户外读书, 驾驶摩托车, 享受鲁re的生活方式, 而我的个人Jarvis使我的口袋更深。但是, 我很快意识到机器学习算法的基础是统计, 我个人认为这很枯燥和无趣。幸运的是, 事实证明, “愚蠢的”统计数据有一些非常有趣的应用。
你很快就会发现, 要获得那些引人入胜的应用程序, 你需要非常了解统计信息。机器学习算法的目标之一是在提供的数据中找到统计依存关系。
所提供的数据可以是任何数据, 从根据年龄检查血压到根据各种像素的颜色查找手写文本。
就是说, 我很好奇我是否可以使用机器学习算法来查找加密哈希函数(SHA, MD5等)中的依赖项, 但是, 你不能真正做到这一点, 因为以这种方式构造了正确的加密原语他们消除了依赖性, 并产生了难以预测的输出。我相信, 在无限长的时间内, 机器学习算法可能会破解任何加密模型。
不幸的是, 我们没有那么多时间, 因此我们需要找到另一种有效挖掘加密货币的方法。到现在为止我们走了多远?
机器学习算法简史
机器学习算法的根源来自托马斯·贝斯(Thomas Bayes), 他是18世纪的英国统计学家。他的论文《解决机会论中的问题的论文》奠定了贝叶斯定理的基础, 该定理在统计学领域得到了广泛应用。
19世纪, 皮埃尔·西蒙·拉普拉斯(Pierre-Simon Laplace)出版了概率论分析, 扩展了贝叶斯的工作, 并定义了我们今天所知的贝叶斯定理。在此之前不久, Adrien-Marie Legendre描述了”最小二乘”方法, 该方法今天也广泛用于监督学习中。
20世纪是该领域大多数已知发现的时期。安德烈·马尔科夫(Andrey Markov)发明了马尔可夫链, 用于分析诗歌。艾伦·图灵(Alan Turing)提出了一种可以变成人工智能的学习机, 基本上预示了遗传算法。弗兰克·罗森布拉特(Frank Rosenblatt)发明了感知器, 在媒体上引起了极大的兴奋和广泛报道。
但是在1970年代, 人们对AI的想法感到非常悲观, 因此减少了资金投入, 因此这个时期被称为AI冬季。 1980年代对反向传播的重新发现引起了机器学习研究的复兴。今天, 它再次成为热门话题。
已故的Leo Breiman区分了两种统计建模范例:数据建模和算法建模。 “算法建模”或多或少意味着机器学习算法, 例如随机森林。
机器学习和统计是紧密相关的领域。根据迈克尔·乔丹(Michael I. Jordan)的观点, 从方法论原理到理论工具, 机器学习的思想在统计学上有着悠久的历史。他还建议将数据科学作为机器学习专家和统计学家正在暗中研究的整体问题的占位符。
机器学习算法的类别
机器学习领域立足于两大支柱, 分别是有监督学习和无监督学习。有些人还认为, 新的研究领域-深度学习-与监督学习与无监督学习是分开的。
监督学习是指为计算机提供输入示例及其所需输出的示例。计算机的目标是学习将输入映射到输出的通用公式。可以进一步细分为:
- 半监督学习, 即对计算机进行不完全训练而缺少某些输出的情况
- 主动学习, 这是指计算机只能为非常有限的一组实例获取训练标签的时候。当以交互方式使用时, 可以将其训练集呈现给用户进行标记。
- 强化学习, 这是指训练数据仅作为对程序在动态环境中的动作的反馈, 例如驾驶车辆或与对手玩游戏时
相比之下, 无监督学习是指根本不给出任何标签, 并且算法要在输入中查找结构。当我们只需要发现隐藏的模式时, 无监督学习本身就是一个目标。
深度学习是一个新的研究领域, 它受到人脑的结构和功能的启发, 并基于人工神经网络, 而不仅仅是统计概念。深度学习可用于有监督和无监督的方法中。
在本文中, 我们将只介绍一些更简单的监督式机器学习算法, 并使用它们来计算个人在泰坦尼克号惨案中的生存机会。但总的来说, 如果你不确定要使用哪种算法, 最好的起点是scikit-learn的机器学习算法速查表。
基本的受监督机器学习模型
也许最简单的算法是线性回归。有时可以用图形将其表示为一条直线, 但是尽管有其名称, 但如果有多项式假设, 则该线可能是一条曲线。无论哪种方式, 它都可以对标量因变量$ y $和一个或多个由$ x $表示的解释值之间的关系进行建模。
用非专业人士的话来说, 这意味着线性回归是学习每个已知$ x $和$ y $之间依赖关系的算法, 以便以后我们可以使用它来预测$ x $的未知样本$ 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)$是理想的目标函数。机器学习算法将尝试猜测假设函数$ h(x)$, 该函数是未知$ f(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 $值?
我们需要定义一个成本函数。本质上, 成本函数的作用只是简单地计算模型预测与实际输出之间的均方根误差。
例如, 我们的假设预测, 对于48岁的某人, 其血压应为$ h(48)= 84 + 1.24 * 48 = 143mmHg $;但是, 在我们的训练样本中, 我们的价值为$ 130 mmHg $。因此, 错误为$(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(θ)= 1901.95
现在, 我们需要找到$ \ theta $的值, 以使我们的成本函数值最小。但是我们该怎么做呢?
有几种可能的算法, 但是最流行的是梯度下降。为了了解梯度下降方法背后的直觉, 我们首先将其绘制在图形上。为了简单起见, 我们将假设一个更简单的假设$ 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 $参数位于成本函数最小的位置。
基本上, 梯度下降是一种算法, 试图找到使函数最小化的参数集。它从一组初始参数开始, 然后在函数梯度的负方向上逐步执行步骤。
如果我们在特定点计算假设函数的导数, 则将给我们该点的切线与曲线的斜率。这意味着我们可以计算图形上每个点的斜率。
该算法的工作方式是这样的:
- 我们选择一个随机的起点(随机$ \ theta $)。
- 在这一点上计算成本函数的导数。
- 向斜坡$ \ theta_j迈出小步:= \ theta_j-\ lambda * \ frac {\ partial} {\ partial \ theta_j} * J(\ theta)$。
- 重复步骤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)
局部最小值出现在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 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) )
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]]
统计数据类型
处理机器学习问题的数据时, 识别不同类型的数据很重要。我们可能有数字(连续或离散), 分类或有序数据。
数值数据具有度量意义。例如, 年龄, 体重, 一个人拥有的比特币数量或该人每月可以写多少篇文章。数值数据可以进一步细分为离散和连续类型。
- 离散数据表示可以用整数来计数的数据, 例如, 公寓中的房间数或掷硬币的次数。
- 连续数据不一定要用整数表示。例如, 如果你正在测量可以跳跃的距离, 则可能是2米, 1.5米或1.652245米。
分类数据代表诸如人的性别, 婚姻状况, 国家/地区等值。该数据可以取数字值, 但这些数字没有数学意义。你不能将它们添加在一起。
顺序数据可以是其他两种类型的混合, 因为可以用数学上有意义的方式对类别进行编号。常见的例子是评分:通常要求我们以1到10的等级对事物进行评分, 并且只允许使用整数。虽然我们可以在数字上使用它(例如, 为某物找到平均评分), 但在将数据应用到机器学习方法时, 我们通常会将其视为分类。
逻辑回归
线性回归是一种很棒的算法, 可以帮助我们预测数值, 例如具有特定大小和房间数量的房屋价格。但是, 有时, 我们可能还希望预测分类数据, 以获得诸如以下问题的答案:
- 这是狗还是猫?
- 这是恶性还是良性肿瘤?
- 这酒好还是坏?
- 这是垃圾邮件吗?
甚至:
- 图片中的哪个号码?
- 该电子邮件属于哪个类别?
所有这些问题都是针对分类问题的。最简单的分类算法称为逻辑回归, 除具有不同的假设外, 最终与线性回归相同。
首先, 我们可以重用相同的线性假设$ h_ \ theta(x)= \ theta ^ T X $(这是矢量化形式)。线性回归可以输出区间$ [a, b] $中的任何数字, 而逻辑回归只能输出$ [-1, 1] $的值, 这是对象是否属于给定类别的概率。
使用S形函数, 我们可以转换任何数值以表示区间$ [-1, 1] $上的值。
现在, 我们需要传递现有的假设, 而不是$ x $, 因此我们将得到:
之后, 我们可以应用一个简单的阈值, 该阈值表示如果假设大于零, 则该值为真, 否则为假。
这意味着我们可以使用相同的成本函数和相同的梯度下降算法来学习逻辑回归的假设。
在我们的下一个机器学习算法示例中, 我们将建议航天飞机的飞行员是否应该使用自动或手动着陆控制。我们有一个非常小的数据集(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)
我们的模型分数是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度多项式模型会通过大多数点, 并且在训练集上具有非常好的分数, 但是超出此范围的任何内容都会表现不佳。
我们的数据集包含一个特征, 并且易于在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()
在我们的模拟场景中, 代表训练得分的蓝线看起来像一条直线。实际上, 它仍然略有下降-你实际上可以在一级多项式图中看到它, 但是在其他多项图中, 它太微妙了以至于无法分辨这个分辨率。我们至少清楚地看到, 在”高偏差”情况下, 用于训练的学习曲线与测试观察之间存在巨大差距。
在中间的”正常”学习率图表上, 你可以看到训练分数线和测试分数线是如何组合在一起的。
在”高方差”图上, 你可以看到样本数量少时, 测试和培训得分非常相似;但是, 当你增加样本数量时, 训练分数会保持接近完美, 而测试分数会逐渐远离它。
如果我们使用非线性假设(例如具有更多多项式特征的假设), 则可以修复拟合不足的模型(也称为具有高偏差的模型)。
我们的过度拟合模型(高方差)遍历了显示的每个示例;但是, 当我们引入测试数据时, 学习曲线之间的差距会扩大。我们可以使用正则化, 交叉验证和更多数据样本来修复过拟合模型。
交叉验证
避免过度拟合的一种常见做法是保留部分可用数据, 并将其用作测试集。但是, 在评估不同的模型设置(例如多项式特征的数量)时, 我们仍然存在过拟合测试集的风险, 因为可以对参数进行调整以获得最佳估计器性能, 因此, 我们对测试集的了解可以泄漏到模型中。为了解决此问题, 我们需要保留数据集的另一部分, 称为”验证集”。训练是在训练集上进行的, 当我们认为我们已达到最佳模型性能时, 我们可以使用验证集进行最终评估。
但是, 通过将可用数据划分为三个集合, 我们大大减少了可用于训练模型的样本数量, 并且结果可能取决于训练验证对集合的特定随机选择。
解决此问题的一种方法是称为交叉验证的过程。在标准的$ k $ -fold交叉验证中, 我们将数据划分为$ k $子集, 称为folds。然后, 我们在$ k-1 $倍数上迭代训练算法, 同时将其余倍数用作测试集(称为”保持倍数”)。
交叉验证允许你仅使用原始训练集来调整参数。这使你可以将测试集作为选择最终模型时真正看不见的数据集。
还有很多交叉验证技术, 例如不使用P, 分层$ k $折叠, 随机组合和拆分等, 但它们不在本文讨论范围之内。
正则化
这是另一种可以帮助解决模型过拟合问题的技术。大多数数据集都有模式和一些噪声。正则化的目的是减少噪声对模型的影响。
有三种主要的正则化技术:套索, Tikhonov和弹性网。
L1正则化(或Lasso正则化)将选择一些要素以缩小为零, 这样它们就不会在最终模型中发挥任何作用。 L1可以看作是选择重要特征的一种方法。
L2正则化(或Tikhonov正则化)将强制所有特征都相对较小, 以使它们对模型的影响较小。
弹性网是L1和L2的组合。
标准化(功能缩放)
特征缩放也是预处理数据时的重要步骤。我们的数据集可能具有值为$ [-\ infty, \ infty] $的要素以及其他具有不同比例的要素。这是一种标准化独立值范围的方法。
特征缩放也是提高学习模型性能的重要过程。首先, 如果所有特征都缩放到同一范数, 则梯度下降的收敛速度会更快。同样, 许多算法(例如, 支持向量机(SVM))通过计算两点之间的距离来工作, 并且如果其中一个要素具有较宽的值, 则距离会受到该要素的很大影响。
支持向量机
SVM是另一种广泛流行的机器学习算法, 可用于分类和回归问题。在SVM中, 我们将每个观测值绘制为$ n $维空间中的一个点, 其中$ n $是我们拥有的特征数。每个要素的值是特定坐标的值。然后, 我们尝试找到一个能很好地分隔两个类的超平面。
确定最佳的超平面之后, 我们要添加边距, 这将进一步分隔两个类别。
在特征数量很高或特征数量大于数据样本数量的情况下, SVM非常有效。但是, 由于SVM基于矢量进行操作, 因此在使用之前对数据进行规范化至关重要。
神经网络
神经网络算法可能是机器学习研究中最令人兴奋的领域。神经网络试图模仿大脑神经元如何连接在一起。
这就是神经网络的外观。我们将许多节点组合在一起, 每个节点接受一组输入, 对它们进行一些计算, 然后输出一个值。
对于监督学习和无监督学习, 都有大量的神经网络算法。神经网络可用于驾驶自动驾驶汽车, 玩游戏, 降落飞机, 对图像进行分类等。
臭名昭著的泰坦尼克号
RMS泰坦尼克号是英国的一艘客轮, 1912年4月15日与冰山相撞后在北大西洋沉没。大约有2224名机组人员和乘客, 超过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)
旅客编号 | 幸存下来 | P类 | 名称 | 性别 | 年龄 | 锡卜 | 版本号 | 票 | 做 | 舱 | 出发 | |
0 | 1 | 0 | 3 | 布朗德, 欧文·哈里斯先生 | 男 | 22.0 | 1 | 0 | A / 5 21171 | 7.2500 | NaN | 小号 |
1 | 2 | 1 | 1 | 卡明斯, 约翰·布拉德利夫人(佛罗伦萨·布里格斯 | 女 | 38.0 | 1 | 0 | 电脑17599 | 71.2833 | C85 | C |
2 | 3 | 1 | 3 | 海基宁小姐贷款 | 女 | 26.0 | 0 | 0 | STON / O2。 3101282 | 7.9250 | NaN | 小号 |
3 | 4 | 1 | 1 | Futrelle, Jacques Heath夫人(Lily May Peel) | 女 | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | 小号 |
4 | 5 | 0 | 3 | 艾伦·威廉·亨利先生 | 男 | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | 小号 |
我们的第一步是加载和浏览数据。我们有891条测试记录;每条记录具有以下结构:
- passengerId –登机乘客的ID
- 生存–该人是否在撞车事故中幸存了下来
- pclass –票务类, 例如1st, 2nd, 3rd
- 性别–旅客性别:男性或女性
- 名称-包括标题
- 年龄–以年为单位的年龄
- sibsp –泰坦尼克号上的兄弟姐妹/配偶人数
- 炎热–泰坦尼克号上的父母/子女数量
- 票–票号
- 做-乘客做
- 机舱–机舱号
- 登船–登船港
该数据集包含数值和分类数据。通常, 最好深入研究数据, 然后在此基础上提出假设。但是, 在这种情况下, 我们将跳过此步骤, 直接进行预测。
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'])
标题\性别 | 女 | 男 |
上尉 | 0 | 1 |
同 | 0 | 2 |
伯爵夫人 | 1 | 0 |
唐 | 0 | 1 |
Dr | 1 | 6 |
琼克尔 | 0 | 1 |
淑女 | 1 | 0 |
重大的 | 0 | 2 |
主 | 0 | 40 |
小姐 | 182 | 0 |
小姐 | 2 | 0 |
太太 | 1 | 0 |
Mr | 0 | 517 |
太太 | 125 | 0 |
Ms | 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 | Mr | 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()
幸存下来 | P类 | 性别 | 年龄 | 锡卜 | 版本号 | 做 | 出发 | 标题 | |
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 |
至此, 我们将通过使用scikit-learn创建一组不同的模型来对Python中不同类型的机器学习算法进行排名。这样就很容易看出哪一个表现最好。
- 多项式数量变化的逻辑回归
- 支持向量机与线性核
- 支持向量机与多项式内核
- 神经网络
对于每个模型, 我们将使用$ 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), ]
# 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回归 | 0.854749 | 0.882682 | 0.869274 |
3 | RBF支持向量机 | 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'))
机器学习并不复杂, 但是它是一个非常广泛的研究领域, 并且它需要数学和统计学知识才能掌握其所有概念。
目前, 机器学习和深度学习已成为硅谷讨论的最热门话题, 并且几乎是每个数据科学公司的生死攸关之地, 主要是因为它们可以使许多重复性任务自动化, 包括语音识别, 驾驶车辆, 金融交易, 照顾病人, 做饭, 销售等。
现在, 你可以掌握这些知识并解决Kaggle上的挑战。
这是对有监督的机器学习算法的非常简短的介绍。幸运的是, 有很多在线课程和有关机器学习算法的信息。我个人建议从吴安(Andrew Ng)的Coursera课程开始。
资源资源
- 吴安德的Coursera课程
- Kaggle数据集
- 深度学习阅读清单
- 有关机器学习算法, 数据挖掘, 深度学习和相关主题的免费书籍清单
- 机器学习理论及其应用简介:带有示例的可视教程
相关:机器与信任:如何缓解AI偏差
评论前必须登录!
注册