Примечание
Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере с помощью JupyterLite или Binder.
Пример конвейера для извлечения признаков из текста и оценки
В этом примере используется набор данных Текстовый набор данных 20 newsgroups, который будет автоматически загружен, сохранён в кэше и повторно использован для примера классификации документов.
В этом примере мы настраиваем гиперпараметры определённого классификатора с помощью RandomizedSearchCV. Демонстрацию производительности некоторых других классификаторов см. в блокноте Классификация текстовых документов с использованием разреженных признаков.
# Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause
Загрузка данных
Мы загружаем две категории из обучающей выборки. Вы можете изменить число категорий, добавив их названия в список, или установить categories=None при вызове загрузчика набора данных fetch_20newsgroups, чтобы получить все 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
Мы задаём сетку гиперпараметров, которую должен исследовать RandomizedSearchCV. Вместо этого можно использовать GridSearchCV, который переберёт все возможные комбинации в сетке, что может потребовать значительных вычислительных затрат. Параметр n_iter у RandomizedSearchCV определяет число различных случайных комбинаций, которые будут оценены. Обратите внимание: если задать 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, чтобы получить более информативный анализ. При этом увеличится время вычислений. Его можно сократить, задействовав параллельное вычисление комбинаций параметров и увеличив число используемых процессоров с помощью параметра 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 33.493s
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
Префиксы vect и clf нужны, чтобы избежать возможной неоднозначности в конвейере, но для визуализации результатов они не требуются. Поэтому мы определяем функцию, которая переименует подобранные гиперпараметры и сделает результаты нагляднее.
import pandas as pd
def shorten_param(param_name):
"""Remove components' prefixes in 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
Обратите внимание: группа моделей в верхнем левом углу графика обеспечивает наилучший компромисс между точностью и временем оценки. В данном случае использование биграмм увеличивает необходимое время оценки, но не приводит к существенному повышению точности конвейера.
Примечание
Дополнительные сведения о настройке автоматического подбора параметров для максимизации оценки и минимизации времени оценки см. в примере блокнота Настраиваемая стратегия повторного обучения для поиска по сетке с перекрёстной проверкой.
Для дальнейшей визуализации среднего результата на тестовых данных в зависимости от подобранных гиперпараметров можно также использовать 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)
# Using a logarithmic scale for alpha
transform_funcs["alpha"] = math.log10
# L1 norms are mapped to index 1, and L2 norms to index 2
transform_funcs["norm"] = lambda x: 2 if x == "l2" else 1
# Unigrams are mapped to index 1 and bigrams to index 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_df, min_df и силой регуляризации alpha. Причина в том, что включение шумовых признаков (то есть max_df близкого к \(1.0\) или min_df близкого к \(0\)) приводит к переобучению, поэтому для компенсации требуется более сильная регуляризация. При меньшем числе признаков требуется менее сильная регуляризация и меньше времени на оценку.
Наилучшие показатели точности достигаются, когда alpha находится в диапазоне от \(10^{-6}\) до \(10^0\), независимо от гиперпараметра norm.
Общее время выполнения скрипта: (0 минут 36.426 секунд)
Связанные примеры
© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/model_selection/plot_grid_search_text_feature_extraction.html