# 第四章 使用 scikit-learn 对数据分类 > 作者:Trent Hauck > 译者:[飞龙](https://github.com/wizardforcel) > 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) 分类在大量语境下都非常重要。例如,如果我们打算自动化一些决策过程,我们可以利用分类。在我们需要研究诈骗的情况下,有大量的事务,人去检查它们是不实际的。所以,我们可以使用分类都自动化这种决策。 ## 4.1 使用决策树实现基本的分类 这个秘籍中,我们使用决策树执行基本的分类。它们是非常不错的模型,因为它们很易于理解,并且一旦训练完成,评估就很容易。通常可以使用 SQL 语句,这意味着结果可以由许多人使用。 ## 准备 这个秘籍中,我们会看一看决策树。我喜欢将决策树看做基类,大量的模型从中派生。它是个非常简单的想法,但是适用于大量的情况。 首先,让我们获取一些分类数据,我们可以使用它来练习: ```py >>> from sklearn import datasets >>> X, y = datasets.make_classification(n_samples=1000, n_features=3, n_redundant=0) ``` ### 操作步骤 处理决策树非常简单。我们首先需要导入对象,之后训练模型: ```py >>> from sklearn.tree import DecisionTreeClassifier >>> dt = DecisionTreeClassifier() >>> dt.fit(X, y) DecisionTreeClassifier(compute_importances=None, criterion='gini', max_depth=None, max_features=None, max_leaf_nodes=None, min_density=None, min_samples_leaf=1, min_samples_split=2, random_state=None, splitter='best') >>> preds = dt.predict(X) >>> (y == preds).mean() 1.0 ``` 你可以看到,我们猜测它是正确的。显然,这只是凑合着运行。现在我们研究一些选项。 首先,如果你观察`dt`对象,它拥有多种关键字参数,决定了对象的行为。我们如何选择对象十分重要,所以我们要详细观察对象的效果。 我们要观察的第一个细节是`max_depth`。这是个重要的参数,决定了允许多少分支。这非常重要,因为决策树需要很长时间来生成样本外的数据,它们带有一些类型的正则化。之后,我们会看到,我们如何使用多种浅层决策树,来生成更好的模型。让我们创建更复杂的数据集并观察当我们允许不同`max_depth`时会发生什么。我们会将这个数据及用于剩下的秘籍。 ```py >>> n_features=200 >>> X, y = datasets.make_classification(750, n_features, n_informative=5) >>> import numpy as np >>> training = np.random.choice([True, False], p=[.75, .25], size=len(y)) >>> accuracies = [] >>> for x in np.arange(1, n_features+1): >>> dt = DecisionTreeClassifier(max_depth=x) >>> dt.fit(X[training], y[training]) >>> preds = dt.predict(X[~training]) >>> accuracies.append((preds == y[~training]).mean()) >>> import matplotlib.pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> ax.plot(range(1, n_features+1), accuracies, color='k') >>> ax.set_title("Decision Tree Accuracy") >>> ax.set_ylabel("% Correct") >>> ax.set_xlabel("Max Depth") ``` 输出如下: ![](img/4-1-1.jpg) 我们可以看到,我们实际上在较低最大深度处得到了漂亮的准确率。让我们进一步看看低级别的准确率,首先是 15: ```py >>> N = 15 >>> import matplotlib.pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> ax.plot(range(1, n_features+1)[:N], accuracies[:N], color='k') >>> ax.set_title("Decision Tree Accuracy") >>> ax.set_ylabel("% Correct") >>> ax.set_xlabel("Max Depth") ``` 输出如下: ![](img/4-1-2.jpg) 这个就是我们之前看到的峰值。比较令人惊讶的是它很快就下降了。最大深度 1 到 3 可能几乎是相等的。决策树很擅长分离规则,但是需要控制。 我们观察`compute_importances`参数。对于随机森林来说,它实际上拥有更广泛的含义。但是我们要更好地了解它。同样值得注意的是,如果你使用了 0.16 或之前的版本,你可以尽管这样做: ```py >>> dt_ci = DecisionTreeClassifier(compute_importances=True) >>> dt.fit(X, y) #plot the importances >>> ne0 = dt.feature_importances_ != 0 >>> y_comp = dt.feature_importances_[ne0] >>> x_comp = np.arange(len(dt.feature_importances_))[ne0] >>> import matplotlib.pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> ax.bar(x_comp, y_comp) ``` 输出如下: ![](img/4-1-3.jpg) > 要注意,你可能会得到一个错误,让你知道你不再需要显式设置`compute_importances`。 我们看到,这些特征之一非常重要,后面跟着几个其它特征。 ### 工作原理 简单来说,我们所有时间都在构造决策树。当思考场景以及将概率分配给结果时,我们构造了决策树。我们的规则更加复杂,并涉及很多上下文,但是使用决策树,我们关心的所有东西都是结果之间的差异,假设特征的一些信息都是已知的。 现在,让我们讨论熵和基尼系数之间的差异。 熵不仅仅是给定变量的熵值,如果我们知道元素的值,它表示了熵中的变化。这叫做信息增益(IG),数学上是这样: ``` IG(Data,KnownFeatures) = H(Data) - H(Data|KnownFeatures) ``` 对于基尼系数,我们关心的是,提供新的信息,一个数据点有多可能被错误标记。 熵和基尼系数都有优缺点。也就是说,如果你观察它们工作方式的主要差异,这可能是个重新验证你的假设的好方式。 ## 4.2 调整决策树模型 如果我们仅仅使用基本的决策树实现,可能拟合得不是很好。所以我们需要调参,以便获得更好的拟合。这非常简单,并且不用花费什么精力。 ### 准备 这个秘籍中,我们深入了解如何调整决策树分类器。有几个渲染,并且在上一个秘籍中,我们仅仅查看了它们之一。 我们会训练一个基本的模型,并实际观察决策树是什么样子。之后,我们会重新检测每个决策,并且指出不同的修改如何影响结构。 如果你打算遵循这个秘籍,需要安装`pydot`。 ### 操作步骤 比起其它算法,决策树有许多“把手”,因为我们旋转把手时,易于发现发生了什么。 ```py >>> from sklearn import datasets >>> X, y = datasets.make_classification(1000, 20, n_informative=3) >>> from sklearn.tree import DecisionTreeClassifier >>> dt = DecisionTreeClassifier() >>> dt.fit(X, y) ``` 好的,所以既然我们训练了基本的分类器,我们可以快速地查看它: ```py >>> from StringIO import StringIO >>> from sklearn import tree >>> import pydot >>> str_buffer = StringIO() >>> tree.export_graphviz(dt, out_file=str_buffer) >>> graph = pydot.graph_from_dot_data(str_buffer.getvalue()) >>> graph.write("myfile.jpg") ``` 这张图几乎难以辨认,但是这展示了一颗复杂的树,它可以使用非最优的决策树,作为结果生成。 ![](img/4-2-1.jpg) 哇哦!这是个非常复杂的树,看上去对数据过拟合了。首先,让我们降低最大深度值: ```py >>> dt = DecisionTreeClassifier(max_depth=5) >>> dt.fit(X, y); ``` 顺带一说,如果你想知道为什么能看到分号,通常是`repr`,它实际上是决策树的模型。例如`fit`函数实际上返回决策树对象,它允许链式调用: ```py >>> dt = DecisionTreeClassifier(max_depth=5).fit(X, y) ``` 现在,让我们返回正常的程序。 由于我们会多次绘制它,我们创建一个函数。 ```py >>> def plot_dt(model, filename): str_buffer = StringIO() >>> tree.export_graphviz(model, out_file=str_buffer) >>> graph = pydot.graph_from_dot_data(str_buffer.getvalue()) >>> graph.write_jpg(filename) >>> plot_dt(dt, "myfile.png") ``` 会生成下面的图: ![](img/4-2-2.jpg) 这棵树稍微简单了一些。让我们看看,如果我们将熵用作分割标准,会发生什么: ```py >>> dt = DecisionTreeClassifier(criterion='entropy', max_depth=5).fit(X, y) >>> plot(dt, "entropy.png") ``` 会生成下面的图: ![](img/4-2-3.jpg) 很容易看到,前两个分割是相同特征,之后的分割以相似总数分布。这是个良好的合理的检查。 同样,注意第一个分割的熵是 0.999,但是使用基尼系数的时候是 0.5。我们需要弄清楚,决策树的分割的两种度量有什么不同。更多信息请见下面的工作原理一节。但是,如果我们想要使用熵穿件决策树,我们必须使用下列命令: ```py >>> dt = DecisionTreeClassifier(min_samples_leaf=10, criterion='entropy', max_depth=5).fit(X, y) ``` ### 工作原理 决策树,通常容易出现过拟合。由于它的自身特性,决策树经常会过拟合,所以,我们需要思考,如何避免过拟合,这是为了避免复杂性。实战中,简单的模型通常会执行得更好。 我们即将在实战中看到这个理念。随机森林会在简单模型的理念上构建。 ## 4.3 使用许多决策树 -- 随机森林 这个秘籍中,我们会将随机森林用于分类任务。由于随机森林对于过拟合非常健壮,并且在大量场景中表现良好,所以使用它。 ### 准备 我们会在工作原理一节中深入探索,但是随即森林通过构造大量浅层树,之后让每颗树为分类投票,再选取投票结果。这个想法在机器学习中十分有效。如果我们发现简单训练的分类器只有 60% 的准确率,我们可以训练大量分类器,它们通常是正确的,并且随后一起使用它们。 ### 操作步骤 训练随机森林分类器的机制在 Scikit 中十分容易。这一节中,我们执行以下步骤: 1. 创建用于练习的样例数据集 2. 训练基本的随机森林对象 3. 看一看训练对象的一些属性 下一个秘籍中,我们会观察如何调整随机森林分类器,让我们以导入数据集来开始: ```py >>> from sklearn import datasets ``` 之后,使用 1000 个样例创建数据集: ```py >>> X, y = datasets.make_classification(1000) ``` 既然我们拥有了数据,我们可以创建分类器对象并训练它: ```py >>> from sklearn.ensemble import RandomForestClassifier >>> rf = RandomForestClassifier() >>> rf.fit(X, y) ``` 我们想做的第一件事,就是看看如何拟合训练数据。我们可以将`predict `方法用于这些东西: ```py >>> print "Accuracy:\t", (y == rf.predict(X)).mean() Accuracy: 0.993 >>> print "Total Correct:\t", (y == rf.predict(X)).sum() Total Correct: 993 ``` 现在,让我们查看一些属性和犯法。 首先,我们查看一些实用属性。这里,由于我们保留默认值,它们是对象的默认值: + `rf.criterion`:这是决定分割的标准。默认是`gini`。 + `rf.bootstrap`:布尔值,表示在训练随机森林时是否使用启动样例 + `rf.n_jobs`:训练和预测的任务数量。如果你打算使用所有处理器,将其设置为`-1`。要记住,如果你的数据集不是非常大,使用过多任务通常会导致浪费,因为处理器之间需要序列化和移动。 + `rf.max_features`:这表示执行最优分割时,考虑的特征数量。在调参过程中这会非常方便。 + `rf.conpute_importtances`:这有助于我们决定,是否计算特征的重要性。如何使用它的信息,请见更多一节。 + `rf.max_depth`:这表示树的深度。 有许多属性需要注意,更多信息请查看官方文档。 不仅仅是`predect`方法很实用,我们也可以从独立的样子获取概率。这是个非常实用的特性,用于理解每个预测的不确定性。例如,我们可以预测每个样例对于不同类的概率。 ```py >>> probs = rf.predict_proba(X) >>> import pandas as pd >>> probs_df = pd.DataFrame(probs, columns=['0', '1']) >>> probs_df['was_correct'] = rf.predict(X) == y >>> import matplotlib.pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> probs_df.groupby('0').was_correct.mean().plot(kind='bar', ax=ax) >>> ax.set_title("Accuracy at 0 class probability") >>> ax.set_ylabel("% Correct") >>> ax.set_xlabel("% trees for 0") ``` ![](img/4-3-1.jpg) ### 工作原理 随机森轮实用预定义数量的弱决策树,并且使用数据的自己训练每一颗树。这对于避免过拟合至关重要。这也是`bootstrap`参数的原因。我们的每个树拥有下列东西: + 票数最多的类 + 输出,如果我们使用回归树 当然,它们是表现上的考量,这会在下一个秘籍中设计。但是出于礼节随机森林如何工作的目的,我们训练一些平均数,作为结果,获得了非常好的分类器。 ### 更多 特征重要性是随机森林的不错的副产品。这通常有助于回答一个问题:如果我们拥有 10 个特征,对于判断数据点的真实类别,哪个特征是最重要的?真实世界中的应用都易于观察。例如,如果一个事务是不真实的,我们可能想要了解,是否有特定的信号,可以用于更快弄清楚事务的类别。 如果我们打算极端特征重要性,我们需要在我们创建对象时说明。如果你使用 scikit-learn 0.15,你可能会得到一个警告,说这不是必需的。在 0.16 中,警告会被移除。 ```py >>> rf = RandomForestClassifier(compute_importances=True) >>> rf.fit(X, y) >>> f, ax = plt.subplots(figsize=(7, 5)) >>> ax.bar(range(len(rf.feature_importances_)), rf.feature_importances_) >>> ax.set_title("Feature Importances") ``` 下面就是输出: ![](img/4-3-2.jpg) 我们可以看到,在判断结果是类 0 或者 1 时,特定的特征比其它特征重要。 ## 4.4 调整随机森林模型 在上一个秘籍中,我们学习了如何使用随机森林分类器。在这个秘籍中,我们会浏览如何通过调整参数来调整它的表现。 ### 准备 为了调整随机森林模型,我们首先需要创建数据集,它有一些难以预测。之后,我们修改参数并且做一些预处理来更好地拟合数据集。 所以,让我们首先创建数据集: ```py >>> from sklearn import datasets >>> X, y = datasets.make_classification(n_samples=10000, n_features=20, n_informative=15, flip_y=.5, weights=[.2, .8]) ``` ### 操作步骤 这个秘籍中,我们执行下列操作: 1. 创建训练和测试集。和上个秘籍不同,如果不与训练集比较,调整模型就毫无用途。 2. 训练基本的随机森林,来评估原始算法有多好。 3. 用系统化的方式修改一些参数,之后观察拟合会发生什么变化。 好了,启动解释器并导入 NumPy: ```py >>> import numpy as np >>> training = np.random.choice([True, False], p=[.8, .2], size=y.shape) >>> from sklearn.ensemble import RandomForestClassifier >>> rf = RandomForestClassifier() >>> rf.fit(X[training], y[training]) >>> preds = rf.predict(X[~training]) >>> print "Accuracy:\t", (preds == y[~training]).mean() Accuracy: 0.652239557121 ``` 我打算用一些花招,引入模型评估度量之一,我们会在这本书后面讨论它。准确率是第一个不错的度量,但是使用混淆矩阵会帮助我们理解发生了什么。 让我们迭代`max_features`的推荐选项,并观察对拟合有什么影响。我们同事迭代一些浮点值,它们是所使用的特征的分数。使用下列命令: ```py >>> from sklearn.metrics import confusion_matrix >>> max_feature_params = ['auto', 'sqrt', 'log2', .01, .5, .99] >>> confusion_matrixes = {} >>> for max_feature in max_feature_params: rf = RandomForestClassifier(max_features=max_feature) rf.fit(X[training], y[training]) >>> confusion_matrixes[max_feature] = confusion_matrix(y[~training]) >>> rf.predict(X[~training])).ravel() ``` 由于我使用了`ravel`方法,我们的二维数组现在变成了一维。 现在,导入 Pandas 并查看刚刚创建的混淆矩阵: ```py >>> import pandas as pd >>> confusion_df = pd.DataFrame(confusion_matrixes) >>> import itertools >>> from matplotlib import pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> confusion_df.plot(kind='bar', ax=ax) >>> ax.legend(loc='best') >>> ax.set_title("Guessed vs Correct (i, j) where i is the guess and j is the actual.") >>> ax.grid() >>> ax.set_xticklabels([str((i, j)) for i, j list(itertools.product(range(2), range(2)))]); >>> ax.set_xlabel("Guessed vs Correct") >>> ax.set_ylabel("Correct") ``` ![](img/4-4-1.jpg) 虽然我们看不到表现中的任何真正的差异,对于你自己的项目来说,这是个非常简单的过程。让我们试一试`n_estimator `选项,但是使用原始的精确度。使用一些更多的选项,我们的图表就会变得很密集,难以使用。 由于我们正在使用混淆矩阵,我们可以从混淆矩阵的迹除以总和来计算准确度。 ```py >>> n_estimator_params = range(1, 20) >>> confusion_matrixes = {} >>> for n_estimator in n_estimator_params: rf = RandomForestClassifier(n_estimators=n_estimator) rf.fit(X[training], y[training]) confusion_matrixes[n_estimator] = confusion_matrix(y[~training], rf.predict(X[~training])) # here's where we'll update the confusion matrix with the operation we talked about >>> accuracy = lambda x: np.trace(x) / np.sum(x, dtype=float) >>> confusion_matrixes[n_estimator] = accuracy(confusion_matrixes[n_estimator]) >>> accuracy_series = pd.Series(confusion_matrixes) >>> import itertools >>> from matplotlib import pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> accuracy_series.plot(kind='bar', ax=ax, color='k', alpha=.75) >>> ax.grid() >>> ax.set_title("Accuracy by Number of Estimators") >>> ax.set_ylim(0, 1) # we want the full scope >>> ax.set_ylabel("Accuracy") >>> ax.set_xlabel("Number of Estimators") ``` 输出如下: ![](img/4-4-2.jpg) 现在对于大多数部分,准确度是如何上升的呢?确实有一些随机性和准确度相关,但是图像从左到右是上升的。在下面的工作原理一节,我们会讨论随机森林和 bootstrap 之间的关系,以及哪个通常更好。 ### 工作原理 bootstarp 是个不错的技巧,用于扩展模型的其它部分。通常用于介绍 bootstarp 的案例是将标准误差与中位数相加。这里,我们估算了结果,并将估算聚集为概率。 所以,通过简单增加估算器的数量,我们增加了子样本,产生了整体上更快的收敛。 ### 更多 我们可能打算加快训练过程。我之前提到了这个过程,但是同时,我们可以将`n_jobs`设为我们想要训练的树的数量。这应该大致等于机器的核数。 ```py >>> rf = RandomForestClassifier(n_jobs=4, verbose=True) >>> rf.fit(X, y) [Parallel(n_jobs=4)]: Done 1 out of 4 | elapsed: 0.3s remaining: 0.9s [Parallel(n_jobs=4)]: Done 4 out of 4 | elapsed: 0.3s finished ``` 这也可以并行预测: ```py >>> rf.predict(X) [Parallel(n_jobs=4)]: Done 1 out of 4 | elapsed: 0.0s remaining: 0.0s [Parallel(n_jobs=4)]: Done 4 out of 4 | elapsed: 0.0s finished array([1, 1, 0, ..., 1, 1, 1]) ``` ## 4.5 使用支持向量机对数据分类 支持向量机(SVM)是我们使用的技巧之一,它不能轻易用概率解释。SVM 背后的原理是,我们寻找一个平面,,它将数据集分割为组,并且是最优的。这里,分割的意思是,平面的选择使平面上最接近的点之间的间距最大。这些点叫做支持向量。 ### 准备 SVM 是我最喜欢的机器学习算法之一,它是我在学校中学习的第一批机器学习伏安法之一。所以,让我们获得一些数据并开始吧。 ```py >>> from sklearn import datasets >>> X, y = datasets.make_classification() ``` ### 操作步骤 创建支持向量机分类器的机制非常简单。有许多可用的选项。所以,我们执行下列操作: 1. 创建 SVC 对象,并训练一些伪造数据。 2. 使用 SVC 对象训练一些示例数据。 3. 稍微讨论一些 SVC 选项。 从支持向量机模块导入支持向量分类器(SVC): ```py >>> from sklearn.svm import SVC >>> base_svm = SVC() >>> base_svm.fit(X, y) ``` 让我们看一些属性: + `C`:以防我们的数据集不是分离好的,`C`会在间距上放大误差。随着`C`变大,误差的惩罚也会变大,SVM 会尝试寻找一个更窄的间隔,即使它错误分类了更多数据点。 + `class_weight`:这个表示问题中的每个类应该给予多少权重。这个选项以字典提供,其中类是键,值是与这些类关联的权重。 + `gamma`:这是用于核的 Gamma 参数,并且由`rgb, sigmoid`和`ploy`支持。 + `kernel`:这是所用的核,我们在下面使用`linear`核,但是`rgb`更流行,并且是默认选项。 ### 工作原理 我们在准备一节中说过,SVM 会尝试寻找一个屏幕,它使两个类别最优分割。让我们查看带有两个特征的最简单示例,以及一个良好分割的结果。 首先,让我们训练数据集,之后我们将其绘制出来。 ```py >>> X, y = datasets.make_blobs(n_features=2, centers=2) >>> from sklearn.svm import LinearSVC >>> svm = LinearSVC() >>> svm.fit(X, y) ``` 既然我们训练了支持向量机,我们将图中每个点结果绘制出来。这会向我们展示近似的决策边界。 ```py >>> from itertools import product >>> from collections import namedtuple >>> Point = namedtuple('Point', ['x', 'y', 'outcome']) >>> decision_boundary = [] >>> xmin, xmax = np.percentile(X[:, 0], [0, 100]) >>> ymin, ymax = np.percentile(X[:, 1], [0, 100]) >>> for xpt, ypt in product(np.linspace(xmin-2.5, xmax+2.5, 20), np.linspace(ymin-2.5, ymax+2.5, 20)): p = Point(xpt, ypt, svm.predict([xpt, ypt])) decision_boundary.append(p) >>> import matplotlib.pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> import numpy as np >>> colors = np.array(['r', 'b']) >>> for xpt, ypt, pt in decision_boundary: ax.scatter(xpt, ypt, color=colors[pt[0]], alpha=.15) ax.scatter(X[:, 0], X[:, 1], color=colors[y], s=30) ax.set_ylim(ymin, ymax) ax.set_xlim(xmin, xmax) ax.set_title("A well separated dataset") ``` 输出如下: ![](img/4-5-1.jpg) 让我们看看其他例子,但是这一次决策边界不是那么清晰: ```py >>> X, y = datasets.make_classification(n_features=2, n_classes=2, n_informative=2, n_redundant=0) ``` 我们已经看到,这并不是用线性分类易于解决的问题。 虽然我们不会将其用于实战,让我们看一看决策边界。首先,让我们使用新的数据点重新训练分类器。 ```py >>> svm.fit(X, y) >>> xmin, xmax = np.percentile(X[:, 0], [0, 100]) >>> ymin, ymax = np.percentile(X[:, 1], [0, 100]) >>> test_points = np.array([[xx, yy] for xx, yy in product(np.linspace(xmin, xmax), np.linspace(ymin, ymax))]) >>> test_preds = svm.predict(test_points) >>> import matplotlib.pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> import numpy as np >>> colors = np.array(['r', 'b']) >>> ax.scatter(test_points[:, 0], test_points[:, 1], color=colors[test_preds], alpha=.25) >>> ax.scatter(X[:, 0], X[:, 1], color=colors[y]) >>> ax.set_title("A well separated dataset") ``` 输出如下: ![](img/4-5-2.jpg) 我们可以看到,决策边界并不完美,但是最后,这是我们获得的最好的线性 SVM。 ### 更多 随让我们可能不能获得更好的线性 SVM,Scikit 中的 SVC 分类器会使用径向基函数。我们之前看过这个函数,但是让我们观察它如何计算我们刚刚拟合的数据集的决策边界。 ```py >>> radial_svm = SVC(kernel='rbf') >>> radial_svm.fit(X, y) >>> xmin, xmax = np.percentile(X[:, 0], [0, 100]) >>> ymin, ymax = np.percentile(X[:, 1], [0, 100]) >>> test_points = np.array([[xx, yy] for xx, yy in product(np.linspace(xmin, xmax), np.linspace(ymin, ymax))]) >>> test_preds = radial_svm.predict(test_points) >>> import matplotlib.pyplot as plt >>> f, ax = plt.subplots(figsize=(7, 5)) >>> import numpy as np >>> colors = np.array(['r', 'b']) >>> ax.scatter(test_points[:, 0], test_points[:, 1], color=colors[test_preds], alpha=.25) >>> ax.scatter(X[:, 0], X[:, 1], color=colors[y]) >>> ax.set_title("SVM with a radial basis function") ``` 输出如下: ![](img/4-5-3.jpg) 我们可以看到,决策边界改变了。我们甚至可以传入我们自己的径向基函数,如果需要的话: ```py >>> def test_kernel(X, y): """ Test kernel that returns the exponentiation of the dot of the X and y matrices. This looks an awful lot like the log hazards if you're familiar with survival analysis. """ return np.exp(np.dot(X, y.T)) >>> test_svc = SVC(kernel=test_kernel) >>> test_svc.fit(X, y) SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0, degree=3, gamma=0.0, kernel=, max_iter=-1, probability=False, random_state=None, shrinking=True, tol=0.001, verbose=False) ``` ## 4.6 使用多类分类来归纳 这个秘籍中,我们会了解多类分类。取决于你的算法选择,你可以轻松地实现多类分类,或者定义用于比较的模式。 ### 准备 在处理线性模型,例如逻辑回归时,我们需要使用`OneVsRestClassifier`。这个模式会为每个类创建一个分类器。 ### 操作步骤 首先,我们会查看一个决策树的粗略示例,用于拟合多类的数据集。我们之前讨论过,我们可以使用一些分类器获得多类,所以我们仅仅拟合示例来证明它可以工作,然后继续。 其次,我们实际上将`OneVsRestClassifier`合并进我的模型中: ```py >>> from sklearn import datasets >>> X, y = datasets.make_classification(n_samples=10000, n_classes=3, n_informative=3) >>> from sklearn.tree import DecisionTreeClassifier >>> dt = DecisionTreeClassifier() >>> dt.fit(X, y) >>> dt.predict(X) array([1, 1, 0, .., 2, 1, 1]) ``` 你可以看到,我们能够以最低努力来拟合分类器。 现在,让我们转向多类分类器的案例中。这需要我们导入`OneVsRestClassifier`。我们也导入`LogisticRegression `。 ```py >>> from sklearn.multiclass import OneVsRestClassifier >>> from sklearn.linear_model import LogisticRegression ``` 现在,我们覆盖`LogisticRegression`分类器,同时,注意我们可以使其并行化。如果我们想知道`OneVsRestClassifier`分类器如何工作,它仅仅是训练单独的模型,之后比较它们。所以,我们可以同时单独训练数据。 ```py >>> mlr = OneVsRestClassifier(LogisticRegression(), n_jobs=2) >>> mlr.fit(X, y) >>> mlr.predict(X) array([1, 1, 0, ..., 2, 1, 1]) ``` ### 工作原理 如果我们打算快速时间我们自己的`OneVsRestClassifier`,应该怎么做呢? 首先,我们需要构造一种方式,来迭代分类,并为每个分类训练分类器。之后,我们首先需要预测每个分类: ```py >>> import numpy as np >>> def train_one_vs_rest(y, class_label): y_train = (y == class_label).astype(int) return y_train >>> classifiers = [] >>> for class_i in sorted(np.unique(y)): l = LogisticRegression() y_train = train_one_vs_rest(y, class_i) l.fit(X, y_train) classifiers.append(l) ``` 好的,所以既然我们配置好了 OneVsRest 模式,我们需要做的所有事情,就是求出每个数据点对于每个分类器的可能性。我们之后将可能性最大的分类赋给数据点。 例如,让我们预测`X[0]`: ```py for classifier in classifiers >>> print classifier.predict_proba(X[0]) [[ 0.90443776 0.09556224]] [[ 0.03701073 0.96298927]] [[ 0.98492829 0.01507171]] ``` 你可以看到,第二个分类器(下标为`1`)拥有“正”的最大可能性,所以我们将这个点标为`1`。