Выбор функций является важной частью любого проекта машинного обучения, неправильный выбор функций, которые будут использоваться моделью, может привести к худшим результатам, поскольку для получения оптимального набора функций было разработано множество методов и методов.

В случае обучения с учителем эта задача относительно проста благодаря ранее существовавшим уловкам, таким как важность функций, встроенная во многие модели обучения scikit perse (например, случайный Форрест, линейная регрессия, логистическая регрессия и т. д.), и существование вычислимых функций потерь напрямую также очень помогает, так как мы всегда можем выполнить исчерпывающий поиск с фиксированной моделью и сравнить полученные оценки потерь.

Тем не менее, реальная проблема заключается в случае обучения без учителя, когда такие методы не очень интуитивны. В дальнейшем в этой статье я буду демонстрировать технику, которую я использовал в недавнем проекте для выполнения выбора функций в случае обучения без учителя.

Данные конфиденциальны, поэтому я не могу ими поделиться, тем не менее, я могу поделиться техникой, которую использовал, так как она открыта и доступна онлайн.

Первое, с чем нужно ознакомиться, это PCA или анализ основных компонентов.

Основные компоненты набора точек в реальном координатном пространстве — это последовательность n единичных векторов, где i-й вектор — это направление линии, которая лучше всего соответствует данным, будучи ортогональной первым i-1 векторам. Эти направления образуют ортонормированный базис. Анализ основных компонентов (PCA) — это процесс вычисления основных компонентов и их использования для изменения базы данных. (см. https://en.wikipedia.org/wiki/Principal_component_analysis)

Что мы хотим сделать, так это построить объясненную дисперсию каждого компонента (см. статью, упомянутую выше).

from sklearn.decomposition import PCA
pca = PCA()
pca.fit(df)
df_pca_all = pca.transform(df)
eigenvalues = pca.explained_variance_
plt.bar(np.arange(0,df.shape[1],1), eigenvalues)
plt.plot(eigenvalues, "r")
plt.plot(eigenvalues, "ro")
plt.show()

Так, например, если бы это были наши данные, мы могли бы сказать, что объясненная дисперсия в основном переносится первым и вторым компонентами, и, таким образом, мы можем спроецировать наши данные на эти оси, следовательно, на новую основу.

Давайте спроецируем данные и посмотрим, что произойдет

Теперь, очевидно, мы можем видеть некоторые выбросы здесь и там, чтобы справиться с ними, мы применим изолирующий лес, который будет автоматически обнаруживать аномалии, следовательно, выбросы.

from sklearn.ensemble import IsolationForest
rng = np.random.RandomState(42)
clf = IsolationForest(n_estimators=1000, random_state=rng)
#df_pca is the new projected data
clf.fit(df_pca)
IF_labels = clf.predict(df_pca)
plt.scatter(df_pca[:, 0], df_pca[:, 1], c=IF_labels, s=40, cmap='viridis')

Теперь, когда наши данные очищены, мы можем обсудить слона в комнате: выбор признаков в неконтролируемом обучении.

Нашим основным инструментом будет анализ основных признаков, для объяснения которого мы возьмем несколько абзацев из этой статьи: http://www.ifp.illinois.edu/~qitian/e_paper/icip02/icip02.pdf.

Вот как все это переводится в строки кода:

from collections import defaultdict
from sklearn.metrics.pairwise import euclidean_distances
from sklearn.preprocessing import StandardScaler
class PFA(object):
    def __init__(self, n_features, q=None):
        self.q = q
        self.n_features = n_features
def fit(self, X):
        if not self.q:
            self.q = X.shape[1]
        X = np.array(X)
        sc = StandardScaler()
       # X = sc.fit_transform(X)
pca = PCA(n_components=self.q).fit(X) # calculation Cov matrix is embeded in PCA
        A_q = pca.components_.T
kmeans = KMeans(n_clusters=self.n_features).fit(A_q)
        clusters = kmeans.predict(A_q)
        cluster_centers = kmeans.cluster_centers_
dists = defaultdict(list)
        for i, c in enumerate(clusters):
            dist = euclidean_distances([A_q[i, :]], [cluster_centers[c, :]])[0][0]
            dists[c].append((i, dist))
self.indices_ = [sorted(f, key=lambda x: x[1])[0][0] for f in dists.values()]
        self.features_ = X[:, self.indices_]
#Run exhaustive search to find the best features to keep:
def get_key(val,y):
    for key, value in Counter(y).items():
         if val == value:
             return key
 
    return "key doesn't exist"
p = 1
#n will be the number of features you have
n = 50
for k in range(2,n,1):
    print('########## K = ',k,' #######')
    pfa = PFA(n_features=k)
    pfa.fit(df)
    # To get the transformed matrix
    x = pfa.features_
    rng = np.random.RandomState(42)
    clf = IsolationForest(n_estimators=1000, random_state=rng)
    clf.fit(x)
    IF_labels = clf.predict(x)
    m = get_key(max(Counter(IF_labels).values()),IF_labels)
    x = pd.DataFrame(x)
    x['outliers'] = IF_labels
    x = x[x['outliers'] == m ]
    x = x.drop(columns = 'outliers')
    x = np.array(x)
    pca.fit(x)
    x = pca.transform(x)
    model_agg_clustering = AgglomerativeClustering(n_clusters=3)
    # fit model and predict clusters
    yhat_agg_clustering = model_agg_clustering.fit_predict(x)
    print('agglomerative')
    model_birch = Birch(threshold=0.01, n_clusters=3)
    # fit the model
    model_birch.fit(x)
    # assign a cluster to each example
    yhat_birch = model_birch.predict(x)
    model_mb_km = MiniBatchKMeans(n_clusters=3)
    # fit the model
    model_mb_km.fit(x)
    # assign a cluster to each example
    print('Mini Batch Kmeans')
    yhat_mb_km = model_mb_km.predict(x)
    label_list = [yhat_agg_clustering, yhat_birch, yhat_mb_km]
    models = ['AgglomerativeClustering', 'BIRCH', 'MiniBatch_KMeans']
    for idx,labels in enumerate(label_list):
        silhouette_avg = silhouette_score(x, labels)
        print("The average silhouette_score for", models[idx], " is :", silhouette_avg)

Как видите, мы выполнили исчерпывающий поиск, чтобы определить наилучшую комбинацию модели и характеристик с точки зрения оценки силуэта.

Я надеюсь, что вы нашли эту короткую статью полезной для своих проектов, и я не беру на себя ответственность за упомянутые выше алгоритмы, поскольку они были разработаны благодаря тяжелой работе ученых, которые их изобрели.