Время прочтения: 5 мин.

В данной статье я решил исследовать применение процессов Дирихле – Dirichlet process Gaussian mixture models (DPGMM) в машинном обучении, которые могут быть полезны при небольшом объеме данных. Я не буду углубляться в математическую часть, однако дам краткое описание и рассмотрю применение алгоритма к задаче классификации.

Упрощенно говоря, Gaussian mixture models (GMM) можно представить, как алгоритм кластеризации, в котором каждое наблюдение может принадлежать к более чем одному кластеру. Это способ представить данные как образцы из смеси распределений. На выходе DPGMM – смесь распределений с известными параметрами (средние и матрицы ковариаций) и разными пропорциями. Для подбора параметров GMM применяет алгоритм максимизации ожидания (Expectation-maximization), используя в процессе теорему Байеса. Выход модели — это не вероятность принадлежности к какому-либо классу, а правдоподобие, поэтому его нельзя использовать напрямую для определения класса.

В своем примере я использовал данные семян, состоящие из признаков для трёх различных сортов пшеницы по 70 элементов в каждом.

Верхний срез данных:

df.head()

Некоторые столбцы сильно коррелированы, данные не нормированы,

c_matrix_test = df.corr()
plt.figure(figsize=(12, 8)) 
sns.heatmap(c_matrix_test, vmin=-1, annot=True, fmt='.2g', cbar_kws={"orientation": "horizontal"}) 
plt.show()

Переходим непосредственно к процессу обучения. Я взял имплементацию из библиотеки sklearn. Используя точки, принадлежащие одному классу, обучил на них DPGMM и оценил для каждой точки log-likelihood по обученной модели. Затем повторил это для всех классов и получил три новых признака, по одному на каждый класс. Так же разбил данные на train и test.

df1 = df[df.target == 1]
df2 = df[df.target == 2]
df3 = df[df.target == 3]

log_likelihood = []
for X in [df1, df2, df3]:
    dpgmm = BayesianGaussianMixture(
        n_components=3, covariance_type='full',
        weight_concentration_prior_type='dirichlet_process',
        init_params='kmeans', random_state=422).fit(X)
    log_likelihood.append(dpgmm.score_samples(df))

dpgmm_1 = log_likelihood[0]
dpgmm_2 = log_likelihood[1]
dpgmm_3 = log_likelihood[2]

dpgmm_f = pd.DataFrame({'dpgmm1': dpgmm_1, 'dpgmm2': dpgmm_2, 'dpgmm3': dpgmm_3})
df_dpgmm = pd.concat([df, dpgmm_f], axis=1)

df_dpgmm1 = df_dpgmm[df_dpgmm.target == 1]
df_dpgmm2 = df_dpgmm[df_dpgmm.target == 2]
df_dpgmm3 = df_dpgmm[df_dpgmm.target == 3]

test = pd.concat([df_dpgmm1.sample(24), df_dpgmm2.sample(24), df_dpgmm3.sample(24)], axis=0)
test_t = test.target
test.drop('target', axis=1, inplace=True)
test.head(10)

dpgmm_train = df_dpgmm.drop(test.index.values, axis=0)
dpgmm_X = dpgmm_train.drop('target', axis=1)
dpgmm_y = dpgmm_train.target

X_dpgmm_only = dpgmm_X[['dpgmm1', 'dpgmm2', 'dpgmm3']]

train = df.drop(test.index.values, axis=0)
X = train.drop('target', axis=1)
y = train.target

Вот так выглядят данные с признаками из DPGMM:

Теперь посмотрим и сравним как показывают себя некоторые модели на данных без признаков из DPGMM и на данных с ними. Выполним кросс-валидацию на тренировочных данных и сравним их с результатом на тестовых данных.

Результат кросс-валидации на тренировочных данных:

k_fold = KFold(n_splits=3, shuffle=True, random_state=1111)

names = ['Random Forest',
         'Bagging Classifier',
         'Gradient Boosting', 'Extra Trees']



classifiers = [

    RandomForestClassifier(n_jobs=-1,max_depth=5, random_state=111),
    BaggingClassifier(n_jobs=-1, random_state=111),
    GradientBoostingClassifier(random_state=111, max_depth=5),
    ExtraTreesClassifier(n_estimators=100, max_depth=5, n_jobs=-1, random_state=111)
]

for name, classifier in zip(names, classifiers):
    start = time.time()
    acc = []

    for train_index, val_index in k_fold.split(X, y):
        X_train, X_val = X.iloc[train_index], X.iloc[val_index]
        y_train, y_val = y.iloc[train_index], y.iloc[val_index]
        classifier.fit(X_train, y_train)
        y_pred = classifier.predict(X_val)
        acc.append(accuracy_score(y_val, y_pred))

    print('The model used :', name)
    print('ACC : {} '.format(sum(acc) / 3))
    print('Duration : {} seconds'.format(round(time.time() - start, 2)))
    print('_' * 60)

Результат на тестовых данных без признаков DPGMM:

for name, classifier in zip(names, classifiers):
    start = time.time()
    classifier.fit(X, y)
    y_pred = classifier.predict(test.drop(['dpgmm1','dpgmm2','dpgmm3'], axis=1))
    print('The model used :', name) 
    print('ACC : ', accuracy_score(test_t, y_pred))
    print('Duration : {} seconds'.format(round(time.time() - start, 2)))
    print('_' * 60)

И с признаками DPGMM:

for name, classifier in zip(names, classifiers):
    start = time.time()
    classifier.fit(dpgmm_X, dpgmm_y)
    y_pred = classifier.predict(test)
    print('The model used :', name) 
    print('ACC : ', accuracy_score(test_t, y_pred))
    print('Duration : {} seconds'.format(round(time.time() - start, 2)))
    print('_' * 60)

В данной статье я поэкспериментировал с признаками из DPGMM. Добавив к данным только лишь их, уже удалось получить улучшение по метрике accuracy в алгоритмах Random Forest Classification и Bagging Classifier на ≈ 5 процентов (0.85 и 0.9 против 0.9 и 0.95 соответственно), однако алгоритмы Gradient Boosting Classifier и Extra Trees Classifier напротив потеряли в точности, что оставляет нам простор для будущих исследований. Возможно, если бы мы подошли к каждому алгоритму отдельно и попробовали настроить под них свой тип ковариации, количество компонентов смеси и другие параметры, то результат был бы другой.