Решая задачи с ребёнком (1 класс), наткнулся на задачу под звёздочкой. На рисунке в задаче изображены кружки, в которых располагаются цифры. В двух кружках вместо цифр стоят знаки вопроса. По заданию нужно вставить пропущенные цифры.
Посмотрим как решит такую задачу искусственный интеллект. Открываем Jupyter Notebook. Пишем код.
На этапе инициализации импортируем библиотеки pandas и numpy. А для решения задачи будем использовать 5 базовых моделей без донастроек гиперпараметров, как есть из коробки:
- LinearRegression (линейная регрессия);
- DecisionTreeRegressor (дерево решений);
- RandomForestRegressor (случайный лес);
- GradientBoostingRegressor (градиентный бустинг).
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.svm import SVR
Чтобы передать данные по задаче модели в понятном виде преобразуем рисунок в таблицу с колонками.
1) top - верхний круг, в котором находится целевая переменная (target).
Остальные колонки будут фичами (features) моделей:
2) middle_L - числа в кругах по середине, слева.
3) low_L - числа в кругах внизу, слева.
4) middle_R - числа в кругах по середине, справа.
5) low_R - числа в кругах внизу, справа.
Значениями np.nan обозначим неизвестные цифры. Получим следующий датафрейм.
data = {'top': [6, 2, np.nan, np.nan],
'middle_L': [1, 1, 2, 4],
'middle_R': [1, 3, 4, 1],
'low_L': [5, 4, 8, 7],
'low_R': [3, 2, 1, 2]}
df = pd.DataFrame(data)
Далее проводим подготовительные работы по разделению данных.
# Разделяем данные на обучающую и тестовую выборки
train = df[df['top'].notna()]
test = df[df['top'].isna()]
# Определяем признаки и целевую переменную
X_train = train[['middle_L', 'middle_R', 'low_L', 'low_R']]
y_train = train['top']
X_test = test[['middle_L', 'middle_R', 'low_L', 'low_R']]
# Словарь для хранения предсказаний
predictions = {}
После, объявляем модели, тренируем модели на тренировочных данных, делаем предсказание и сохраняем предсказания в словарь predictions.
Модель 1: Линейная регрессия
lr_model = LinearRegression()
lr_model.fit(X_train, y_train)
predictions['Линейная регрессия'] = np.round(lr_model.predict(X_test), 2)
Модель 2: Дерево решений
dt_model = DecisionTreeRegressor()
dt_model.fit(X_train, y_train)
predictions['Дерево решений'] = np.round(dt_model.predict(X_test), 2)
Модель 3: Случайный лес
rf_model = RandomForestRegressor()
rf_model.fit(X_train, y_train)
predictions['Случайный лес'] = np.round(rf_model.predict(X_test), 2)
Модель 4: Градиентный бустинг
gb_model = GradientBoostingRegressor()
gb_model.fit(X_train, y_train)
predictions['Градиентный бустинг'] = np.round(gb_model.predict(X_test), 2)
Модель 5: Метод опорных векторов
svr_model = SVR()
svr_model.fit(X_train, y_train)
predictions['Метод опорных векторов'] = np.round(svr_model.predict(X_test), 2)
Выводим результаты. Первое число - это нижнее левое число на рисунке, а второе число - это нижнее правое число.
for model_name, preds in predictions.items():
print(f"{model_name}: {preds}")
Задачу с ребёнком мы конечно решили. Правильный ответ [3 4]. Нижние цифры складываются и потом из получившейся суммы вычитается сумма цифр из среднего ряда.
Зная правильный ответ, можем найти абсолютное отклонение и ошибку в процентах.
true_values = [3, 4]
def calculate_deviations(predictions, true_values):
results = {}
for model, preds in predictions.items():
absolute_devs = [abs(pred - true) for pred, true in zip(preds, true_values)]
percent_devs = [(abs_dev / true) * 100 for abs_dev, true in zip(absolute_devs, true_values)]
results[model] = {'Absolute Deviations': absolute_devs,
'Percent Deviations': percent_devs}
return results
# Вычисляем отклонения
deviations = calculate_deviations(predictions, true_values)
Выводим результаты
for model, devs in deviations.items():
print(f"Модель: {model}")
print(f" Абсолютное отклонение: {np.round(devs['Absolute Deviations'], 2)}")
print('Сумма абсолютных отклонений', np.sum(np.round(devs['Absolute Deviations'], 2)))
print(f" Отклонение в процентах, %: {np.round(devs['Percent Deviations'], 2)}")
print('Сумма процентов, %', np.sum(np.round(devs['Percent Deviations'], 2)))
print('----')
Чтобы сравнить модели между собой, просто сложим значения ошибок. У какой модели будет наименьший показатель, та и будет точнее. Это конечно не метод статистики, но т.к. мы знаем что отрицательных значений у нас нет и значений всего два, то так можно сделать в рамках этой конкретной задачи.
По результатам таких не хитрых сравнений побеждает модель случайного леса. Но есть пара нюансов:
- Если делать несколько запусков кода, то результаты будут меняться.
- Если округлять не до сотых, а до единиц просто отбрасывая знаки после запятой, без округления, то модели станут лучше предсказывать. Так в нашем случае стопроцентный результат дадут случайный лес и метод опорных векторов. Перезапуская код несколько раз, с уменьшением точности, хороший результат давал ещё градиентный бустинг и дерево решений.
В общем восстание машин откладывается =)