Мы собираемся рассматривать Pipeline как дерево. Каждый слой может иметь произвольное количество FeatureUnion, но в конце концов все они будут складываться в один вектор признаков. Есть примерно три случая, которые следует учитывать при обходе. Первый — это базовый случай, когда мы находимся в реальном преобразователе или классификаторе, который будет генерировать наши функции. Во-вторых, если мы находимся в Pipeline. Третий и последний случай — это когда мы находимся внутри FeatureUnion. Давайте поговорим об этом немного подробнее.
============================
Case 1: Featurization Step
===========================
Здесь мы хотим написать функцию, которая с учетом какого-либо функционализатора будет возвращать имена функций. Это базовый случай в нашей DFS. В Sklearn есть несколько разных вещей, которые можно использовать для создания функций. Некоторыми примерами являются методы кластеризации, методы уменьшения размерности, традиционные классификаторы и препроцессоры, и это лишь некоторые из них. Каждый из них позволяет вам получить доступ к именам функций по-разному. Например, препроцессор текста TfidfVectorizer реализует метод get_feature_names, которыый приведен ниже. Однако , большинство методов кластеризации не имеют именованных функций, они представляют собой произвольные кластеры, но имеют фиксированное количество кластеров. Напишем вспомогательную функцию, которая с учетом метода характеристики Sklearn будет возвращать список функций.
=============================
def extract_feature_names(model, name) -> list[str]
=============================
Здесь мы попытаемся перечислить ряд потенциальных случаев, которые могут произойти внутри Sklearn. Мы используем hasattr, чтобы проверить, имеет ли предоставленная модель заданный атрибут, и если да, то мы вызываем его, чтобы получить имена функций. Если метод чем-то похож на кластеризацию и не включает фактические именованные функции, мы создаем наши собственные имена функций, используя предоставленное имя. Например, предположим, что мы применяем этот метод к PCA с двумя компонентами, и мы назвали шаг pca, тогда возвращаемые имена функций будут [pca_0, pca_1].
Давайте пройдем через это вместе. Эта функция займет три вещи. Первая — это модель, которую мы хотим проанализировать. Эта модель должна быть Pipeline. Второй — это список всех именованных шагов характеристики, которые мы хотим извлечь. В нашем последнем примере это были биграммы и отобранные вручную. Это названия отдельных шагов, которые мы использовали в нашей модели. Последний параметр — это текущее имя, на которое мы смотрим. Это необходимо для рекурсии и не имеет значения при первом проходе.
В этом примере мы создаем три написанных от руки средства описания правил, а также подконвейер, который выполняет несколько шагов и приводит к уменьшению размерности функций. Мы можем получить все имена функций из этого конвейера, используя одну строку!
(.env) [boris@fedora35server PIPELINE]$ cat GetFeatureNames.py
from sklearn.pipeline import FeatureUnion, Pipeline
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# Featurization Step
def extract_feature_names(model, name) -> list[str]:
if hasattr(model, "get_feature_names"):
return model.get_feature_names()
elif hasattr(model, "n_clusters"):
return [f"{name}_{x}" for x in range(model.n_clusters)]
elif hasattr(model, "n_components"):
return [f"{name}_{x}" for x in range(model.n_components)]
elif hasattr(model, "components_"):
n_components = model.components_.shape[0]
return [f"{name}_{x}" for x in range(n_components)]
elif hasattr(model, "classes_"):
return classes_
else:
return [name]
# Implement the DFS.
def get_feature_names(model, names: list[str], name: str) -> list[str]:
"""Thie method extracts the feature names in order from a Sklearn Pipeline
This method only works with composed Pipelines and FeatureUnions. It will
pull out all names using DFS from a model.
Args:
model: The model we are interested in
names: The list of names of final featurizaiton steps
name: The current name of the step we want to evaluate.
Returns:
feature_names: The list of feature names extracted from the pipeline.
"""
# Check if the name is one of our feature steps. This is the base case.
if name in names:
# If it has the named_steps atribute it's a pipeline and we need to access the features
if hasattr(model, "named_steps"):
return extract_feature_names(model.named_steps[name], name)
# Otherwise get the feature directly
else:
return extract_feature_names(model, name)
elif type(model) is Pipeline:
feature_names = []
for name in model.named_steps.keys():
feature_names += get_feature_names(model.named_steps[name], names, name)
return feature_names
elif type(model) is FeatureUnion:
feature_names= []
for name, new_model in model.transformer_list:
feature_names += get_feature_names(new_model, names, name)
return feature_names
# If it is none of the above do not add it.
else:
return []
# get the feature names in the correct order
from sklearn.decomposition import TruncatedSVD
from sklearn import svm
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn import metrics
classifier = svm.LinearSVC(C=1.0, class_weight="balanced")
vocab = {"worst": 0, "awful": 1, "waste": 2,
"boring": 3, "excellent": 4}
model = Pipeline([
("union", FeatureUnion(transformer_list=[
("h1", TfidfVectorizer(vocabulary={"worst": 0})),
("h2", TfidfVectorizer(vocabulary={"best": 0})),
("h3", TfidfVectorizer(vocabulary={"awful": 0})),
("tfidf_cls", Pipeline([
("vectorizer", CountVectorizer()),
("transformer", TfidfTransformer()),
("tsvd", TruncatedSVD(n_components=2))
]
))
])
),
("classifier", classifier),
])
print(get_feature_names(model, ["h1", "h2", "h3", "tsvd"], None))
No comments:
Post a Comment