文本特征提取和评估的示例管道#

本示例中使用的数据集是 The 20 newsgroups text dataset ,它将自动下载、缓存并在文档分类示例中重复使用。

在本示例中,我们使用 RandomizedSearchCV 调整特定分类器的超参数。有关其他分类器性能的演示,请参见 使用稀疏特征对文本文档进行分类 笔记本。

# 作者:scikit-learn 开发者
# SPDX 许可证标识符:BSD-3-Clause

数据加载#

我们从训练集中加载两个类别。您可以通过将类别名称添加到列表中或在调用数据加载器 fetch_20newsgroups 时设置 categories=None 来调整类别数量,以获取全部20个类别。

from sklearn.datasets import fetch_20newsgroups

categories = [
    "alt.atheism",
    "talk.religion.misc",
]

data_train = fetch_20newsgroups(
    subset="train",
    categories=categories,
    shuffle=True,
    random_state=42,
    remove=("headers", "footers", "quotes"),
)

data_test = fetch_20newsgroups(
    subset="test",
    categories=categories,
    shuffle=True,
    random_state=42,
    remove=("headers", "footers", "quotes"),
)

print(f"Loading 20 newsgroups dataset for {len(data_train.target_names)} categories:")
print(data_train.target_names)
print(f"{len(data_train.data)} documents")
Loading 20 newsgroups dataset for 2 categories:
['alt.atheism', 'talk.religion.misc']
857 documents

带有超参数调优的管道#

我们定义了一个管道,将文本特征向量化器与一个简单但有效的分类器结合起来用于文本分类。

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import ComplementNB
from sklearn.pipeline import Pipeline

pipeline = Pipeline(
    [
        ("vect", TfidfVectorizer()),
        ("clf", ComplementNB()),
    ]
)
pipeline
Pipeline(steps=[('vect', TfidfVectorizer()), ('clf', ComplementNB())])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


我们定义了一个超参数网格,由 RandomizedSearchCV 进行探索。使用 GridSearchCV 则会探索网格中的所有可能组合,这可能会导致计算成本高昂,而 RandomizedSearchCV 的参数 n_iter 控制评估的不同随机组合的数量。请注意,将 n_iter 设置得大于网格中可能组合的数量会导致重复已经探索过的组合。我们为特征提取 ( vect__ ) 和分类器 ( clf__ ) 搜索最佳参数组合。

import numpy as np

parameter_grid = {
    "vect__max_df": (0.2, 0.4, 0.6, 0.8, 1.0),
    "vect__min_df": (1, 3, 5, 10),
    "vect__ngram_range": ((1, 1), (1, 2)),  # unigrams or bigrams
    "vect__norm": ("l1", "l2"),
    "clf__alpha": np.logspace(-6, 6, 13),
}

在这种情况下, n_iter=40 并不是对超参数网格的穷尽搜索。实际上,增加参数 n_iter 会使分析更具信息量。结果是计算时间增加。我们可以通过增加使用的 CPU 数量(通过参数 n_jobs )来利用参数组合评估的并行化,从而减少计算时间。

from pprint import pprint

from sklearn.model_selection import RandomizedSearchCV

random_search = RandomizedSearchCV(
    estimator=pipeline,
    param_distributions=parameter_grid,
    n_iter=40,
    random_state=0,
    n_jobs=2,
    verbose=1,
)

print("Performing grid search...")
print("Hyperparameters to be evaluated:")
pprint(parameter_grid)
Performing grid search...
Hyperparameters to be evaluated:
{'clf__alpha': array([1.e-06, 1.e-05, 1.e-04, 1.e-03, 1.e-02, 1.e-01, 1.e+00, 1.e+01,
       1.e+02, 1.e+03, 1.e+04, 1.e+05, 1.e+06]),
 'vect__max_df': (0.2, 0.4, 0.6, 0.8, 1.0),
 'vect__min_df': (1, 3, 5, 10),
 'vect__ngram_range': ((1, 1), (1, 2)),
 'vect__norm': ('l1', 'l2')}
from time import time

t0 = time()
random_search.fit(data_train.data, data_train.target)
print(f"Done in {time() - t0:.3f}s")
Fitting 5 folds for each of 40 candidates, totalling 200 fits
Done in 15.868s
print("Best parameters combination found:")
best_parameters = random_search.best_estimator_.get_params()
for param_name in sorted(parameter_grid.keys()):
    print(f"{param_name}: {best_parameters[param_name]}")
Best parameters combination found:
clf__alpha: 0.01
vect__max_df: 0.2
vect__min_df: 1
vect__ngram_range: (1, 1)
vect__norm: l1
test_accuracy = random_search.score(data_test.data, data_test.target)
print(
    "Accuracy of the best parameters using the inner CV of "
    f"the random search: {random_search.best_score_:.3f}"
)
print(f"Accuracy on test set: {test_accuracy:.3f}")
Accuracy of the best parameters using the inner CV of the random search: 0.816
Accuracy on test set: 0.709

前缀 vectclf 是为了避免在管道中可能出现的歧义,但在可视化结果时并不是必需的。因此,我们定义了一个函数来重命名调整后的超参数,以提高可读性。

import pandas as pd


def shorten_param(param_name):
    """移除param_name中的组件前缀。"""
    if "__" in param_name:
        return param_name.rsplit("__", 1)[1]
    return param_name


cv_results = pd.DataFrame(random_search.cv_results_)
cv_results = cv_results.rename(shorten_param, axis=1)

我们可以使用 plotly.express.scatter 来可视化评分时间和平均测试分数(即“CV分数”)之间的权衡。将光标悬停在某个点上会显示相应的参数。误差棒对应于在交叉验证的不同折叠中计算出的一个标准差。

import plotly.express as px

param_names = [shorten_param(name) for name in parameter_grid.keys()]
labels = {
    "mean_score_time": "CV Score time (s)",
    "mean_test_score": "CV score (accuracy)",
}
fig = px.scatter(
    cv_results,
    x="mean_score_time",
    y="mean_test_score",
    error_x="std_score_time",
    error_y="std_test_score",
    hover_data=param_names,
    labels=labels,
)
fig.update_layout(
    title={
        "text": "trade-off between scoring time and mean test score",
        "y": 0.95,
        "x": 0.5,
        "xanchor": "center",
        "yanchor": "top",
    }
)
fig


请注意,图表左上角的模型集群在准确性和评分时间之间达到了最佳平衡。在这种情况下,使用二元组(bigrams)会增加所需的评分时间,但并未显著提高管道的准确性。 .. NOTE:: 有关如何自定义自动调优以最大化得分和最小化评分时间的更多信息,请参阅示例笔记本 网格搜索与交叉验证的自定义重拟合策略

我们还可以使用 plotly.express.parallel_coordinates 来进一步将平均测试分数作为调优超参数的函数进行可视化。这有助于发现两个以上超参数之间的相互作用,并提供关于它们在提高管道性能方面相关性的直观理解。

我们在 alpha 轴上应用 math.log10 变换,以扩展活动范围并提高图形的可读性。该轴上的值 \(x\) 应理解为 \(10^x\)

import math

column_results = param_names + ["mean_test_score", "mean_score_time"]

transform_funcs = dict.fromkeys(column_results, lambda x: x)
# 使用对数刻度表示α
transform_funcs["alpha"] = math.log10
# L1 范数映射到索引 1,L2 范数映射到索引 2
transform_funcs["norm"] = lambda x: 2 if x == "l2" else 1
# 单字映射到索引1,双字映射到索引2
transform_funcs["ngram_range"] = lambda x: x[1]

fig = px.parallel_coordinates(
    cv_results[column_results].apply(transform_funcs),
    color="mean_test_score",
    color_continuous_scale=px.colors.sequential.Viridis_r,
    labels=labels,
)
fig.update_layout(
    title={
        "text": "Parallel coordinates plot of text classifier pipeline",
        "y": 0.99,
        "x": 0.5,
        "xanchor": "center",
        "yanchor": "top",
    }
)
fig


平行坐标图在不同的列上显示超参数的值,而性能指标则用颜色编码。可以通过在平行坐标图的任意轴上点击并按住来选择结果范围。然后,您可以滑动(移动)范围选择,并交叉两个选择以查看交集。您可以通过再次点击同一轴来撤销选择。

特别是对于这个超参数搜索,值得注意的是,表现最好的模型似乎不依赖于正则化 norm ,而是依赖于 max_dfmin_df 和正则化强度 alpha 之间的权衡。原因是包含噪声特征(即 max_df 接近 \(1.0\)min_df 接近 \(0\) )往往会导致过拟合,因此需要更强的正则化来补偿。较少的特征需要较少的正则化和较少的评分时间。

alpha 介于 \(10^{-6}\)\(10^0\) 之间时,无论超参数 norm 如何,都会获得最佳的准确率分数。

Total running time of the script: (0 minutes 17.203 seconds)

Related examples

使用稀疏特征对文本文档进行分类

使用稀疏特征对文本文档进行分类

比较随机森林和直方图梯度提升模型

比较随机森林和直方图梯度提升模型

具有异构数据源的列转换器

具有异构数据源的列转换器

平衡模型复杂性和交叉验证得分

平衡模型复杂性和交叉验证得分

Gallery generated by Sphinx-Gallery