Pull to refresh

Multi-output в машинном обучении

Reading time 3 min
Views 6.4K
Задача алгоритмов искусственного интеллекта обучиться, основываясь на предоставленной выборке, для последующего предсказания данных. Однако, наиболее распространенная задача о которой говорят в большинстве учебниках — это предсказание одного значения, того или иного множества признаков. Что если нам нужно получить обратные данные? То есть, получить определенное количество признаков, основываясь на одном или больше значении.

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

Итак, первое с чем я ознакомилась это метод восстановления упущенных данных средними значениями. Соответственно я поработала с классом предоставленным scikit-learn — Imputer. Ссылаясь на материалы, могу уточнить:
Класс Imputer предоставляет основные стратегии восстановления утерянных значений, либо с использованием среднего, медианы или наиболее часто встречающегося значения столбца или строки в которых находятся утерянные данные.
Даже несмотря на понимание того, что результат не будет полезен, я все равно решила попробовать воспользоваться этим классом, и вот что собственно вышло:

import pandas as pd  
from sklearn.preprocessing import Imputer
from sklearn.model_selection import train_test_split

url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data'
df = pd.read_csv(url, header=None)

df.columns = ['Класс', 'Алкоголь', 'Яблочная кислота', 'Зола', 'Щелочность золы', 'Магний', 'Всего фенола', 'Флавоноиды', 'Фенолы нефлаваноидные', 'Проантоцианидины', 'Интенсивность цвета', 'Оттенок', 'OD280/OD315 разбавленных вин', 'Пролин']

imp = Imputer(missing_values='NaN', strategy='mean')
imp.fit(df)
imp.transform([[3, 'NaN', 'NaN', 'NaN', 'NaN', 'NaN', 'NaN', 'NaN', 'NaN', 'NaN', 'NaN', 'NaN', 'NaN', 'NaN']])

array([[3.00000000e+00, 1.30006180e+01, 2.33634831e+00, 2.36651685e+00,
        1.94949438e+01, 9.97415730e+01, 2.29511236e+00, 2.02926966e+00,
        3.61853933e-01, 1.59089888e+00, 5.05808988e+00, 9.57449438e-01,
        2.61168539e+00, 7.46893258e+02]])

После попытки проверить полученные данные на классе RandomForestClassifier, выяснилось что он с нами не согласен, и вообще считает что этот массив значений точно соответствует первому классу, но никак не третьему.

Теперь, после того, как мы поняли что сей метод нам не подходит, обратимся к классу MultiOutputRegressor. MultiOutputRegressor создан специально для тех регрессоров, которые не поддерживают multi-target регрессию. Проверим его действие на методе наименьших квадратов:

from sklearn.datasets import make_regression
from sklearn.multioutput import MultiOutputRegressor

X, y = make_regression(n_features=1, n_targets=10)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=4)
multioutput = MultiOutputRegressor(LinearRegression()).fit(X_train, y_train)

print("Правильность на тестовом наборе: {:.2f}".format(multioutput.score(X_test, y_test)))
print("Правильность на обучающем наборе: {:.2f}".format(multioutput.score(X_train, y_train)))

Правильность на тестовом наборе: 0.82
Правильность на обучающем наборе: 0.83

Результат вполне неплох. Логика действия очень проста — все сводится к применению отдельного регрессора на каждый элемент множества выходных признаков.
То есть:

class MultiOutputRegressor__:
    
    def __init__(self, est):
        self.est = est
        
    def fit(self, X, y):
        g, h = y.shape
        self.estimators_ = [sklearn.base.clone(self.est).fit(X, y[:, i]) for i in range(h)]
        return self.estimators_
        
    def predict(self, X):
        res = [est.predict(X)[:, np.newaxis] for est in self.estimators_]
        return np.hstack(res)


Теперь проверим работу класса RandomForestRegressor, который также поддерживает multi-target регрессию, на реальных данных.

df = df.drop(['Класс'], axis=1)
X, y = df[['Алкоголь', 'Проантоцианидины']], df.drop(['Алкоголь', 'Проантоцианидины'], axis=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

forest = RandomForestRegressor(n_estimators=30, random_state=13)
forest.fit(X_train, y_train)
print("Правильность на тестовом наборе: {:.2f}".format(forest.score(X_test, y_test)))
print("Правильность на тренировочном наборе:{:.2f}".format(forest.score(X_train, y_train)))

Правильность на тестовом наборе: 0.65
Правильность на тренировочном наборе:0.87

Дабы не вводить некоторых людей в заблуждение относительно проантоцианидинов

собственно
Проантоцианидины — природное химическое соединение. Находится в основном в косточках и кожице винограда, также оно содержится в дубе и попадает в вино при выдержке его в дубовых бочках. Молекулярная масса проантоцианидинов меняется в зависимости от продолжительности выдержки вин. Чем старее вино — тем их больше (для очень старых вин молекулярная масса уменьшается).

В значительной степени влияют на стойкость красных вин.

Результат хуже, чем на синтетических данных (на них случайный лес отрабатывает на 99%). Однако, с добавлением признаков он как и предполагалось улучшается.

С помощью multi-output методов можно решить множество интересных задач и получить действительно нужные данные.
Tags:
Hubs:
+9
Comments 5
Comments Comments 5

Articles