Модели машинного обучения: что могут спросить на интервью

от автора

Привет, Хабр!

Сегодня рассмотрим некоторые вопросы, которые могут попасться на собеседовании на ML позиции.

Как KNN ведёт себя при увеличении размерности данных?

Начнём с KNN (k ближайших соседей). В малых размерностях (скажем, 2–3) расстояния между точками вполне осмысленны. Но когда число признаков вырастает до 100+, всё меняется. В такой ситуации расстояния между точками начинают стремиться к равенству — словно все объекты сидят за круглым столом, и каждый от каждого отстоит примерно на одинаковом расстоянии. Это называется проклятием размерности. Если заморочиться, то получается, что разница между ближайшими и самыми дальними точками становится незначительной. Итог: KNN теряет силу различения, и классификатор начинает путаться, как студент на экзамене по теории вероятностей.

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

import numpy as np from sklearn.metrics import pairwise_distances  def analyze_distances(n_samples=500, dimensions=[2, 10, 50, 100]):     np.random.seed(42)     results = {}          for dim in dimensions:         X = np.random.rand(n_samples, dim)         distances = pairwise_distances(X)         # Убираем диагональ, так как расстояние до самой себя = 0         non_zero_distances = distances[np.triu_indices_from(distances, k=1)]         min_dist = non_zero_distances.min()         max_dist = non_zero_distances.max()         ratio = min_dist / max_dist         results[dim] = {             "min_distance": min_dist,             "max_distance": max_dist,             "ratio": ratio         }         print(f"Размерность: {dim:3d} | min: {min_dist:.4f} | max: {max_dist:.4f} | ratio: {ratio:.4f}")          return results  if __name__ == '__main__':     analyze_distances()

С ростом размерности отношение минимального расстояния к максимальному приближается к единице.

Что же делать, данные с 100+ измерениями? Тут на помощь приходят следующие техники:

  • PCA — снижает размерность, оставляя лишь самые «важные» компоненты.

  • Feature Selection — выбираем только те признаки, которые действительно имеют значение.

  • Manifold Learning — методы нелинейного снижения размерности.

Взглянем на пример PCA для KNN:

from sklearn.decomposition import PCA from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.datasets import make_classification from sklearn.metrics import accuracy_score  # Генерируем синтетический набор данных X, y = make_classification(n_samples=1000, n_features=100, n_informative=20, random_state=42) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)  # Снижаем размерность с помощью PCA pca = PCA(n_components=20, random_state=42) X_train_pca = pca.fit_transform(X_train) X_test_pca = pca.transform(X_test)  # Обучаем KNN knn = KNeighborsClassifier(n_neighbors=5) knn.fit(X_train_pca, y_train) y_pred = knn.predict(X_test_pca)  print(f"Accuracy после PCA: {accuracy_score(y_test, y_pred):.4f}")

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

Как выбрать оптимальное количество деревьев в Random Forest?

Random Forest — это ансамблевый метод, где много слабых моделей объединяются для получения стабильного результата. Интересный факт: увеличение числа деревьев почти никогда не приводит к переобучению, так как итоговый прогноз — это усреднение. Чем больше деревьев — тем стабильнее результат, при условии, что они независимы.

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

Основные параметры, влияющие на производительность:

  • n_estimators: число деревьев. Оптимизация — баланс между качеством и скоростью.

  • max_features: количество признаков, используемых при разбиении узлов. Чем меньше, тем больше разнообразие деревьев.

  • min_samples_split: минимальное число образцов для разбиения узла.

  • bootstrap: использование бутстрэппинга для генерации обучающих подвыборок.

Для начала подберём оптимальное количество деревьев и другие гиперпараметры:

from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import GridSearchCV from sklearn.datasets import make_classification from sklearn.metrics import accuracy_score from sklearn.model_selection import train_test_split  # Генерируем набор данных X, y = make_classification(n_samples=1500, n_features=20, n_informative=10, random_state=42) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)  # Параметры для перебора param_grid = {     'n_estimators': [50, 100, 200],     'max_features': ['sqrt', 'log2', None],     'min_samples_split': [2, 5, 10],     'bootstrap': [True, False] }  rf = RandomForestClassifier(random_state=42) grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5, n_jobs=-1, verbose=1) grid_search.fit(X_train, y_train)  print(f"Лучшие параметры: {grid_search.best_params_}") best_rf = grid_search.best_estimator_ y_pred = best_rf.predict(X_test) print(f"Accuracy оптимизированного Random Forest: {accuracy_score(y_test, y_pred):.4f}")

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

Почему линейная регрессия может переобучаться?

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

y=wX+b

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

Чтобы удержать веса в рамках и избежать катастрофы, применяются методы регуляризации:

  • Ridge (L2-регуляризация) — штрафует квадратичную норму весов, сглаживая модель.

  • Lasso (L1-регуляризация) — штрафует абсолютную величину весов, что может привести к обнулению некоторых коэффициентов.

Рассмотрим пример, демонстрирующий проблему и её решение:

import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression, Ridge, Lasso from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error  # Генерируем синтетические данные с мультиколлинеарностью np.random.seed(42) n_samples = 500 X_base = np.random.rand(n_samples, 1) # Создадим сильно коррелированные признаки X = np.hstack([X_base, X_base * 0.9 + np.random.rand(n_samples, 1) * 0.1]) y = 3 * X_base.flatten() + 2 * (X_base.flatten() * 0.9) + np.random.randn(n_samples) * 0.05  # Разбиваем данные на обучающую и тестовую выборки X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)  # Обучаем стандартную линейную регрессию lin_reg = LinearRegression() lin_reg.fit(X_train, y_train) y_pred_lr = lin_reg.predict(X_test)  # Обучаем Ridge-регрессию ridge_reg = Ridge(alpha=1.0) ridge_reg.fit(X_train, y_train) y_pred_ridge = ridge_reg.predict(X_test)  # Обучаем Lasso-регрессию lasso_reg = Lasso(alpha=0.1) lasso_reg.fit(X_train, y_train) y_pred_lasso = lasso_reg.predict(X_test)  print(f"Linear Regression MSE: {mean_squared_error(y_test, y_pred_lr):.4f}") print(f"Ridge Regression MSE: {mean_squared_error(y_test, y_pred_ridge):.4f}") print(f"Lasso Regression MSE: {mean_squared_error(y_test, y_pred_lasso):.4f}")  # Вывод коэффициентов для сравнения print("\nКоэффициенты модели:") print("Linear Regression:", lin_reg.coef_) print("Ridge Regression   :", ridge_reg.coef_) print("Lasso Regression   :", lasso_reg.coef_)

Регуляризация помогает стабилизировать веса и уменьшить переобучение.

Иногда стандартная регрессия (с регуляризацией) не справляется, особенно когда признаки настолько переплетены, что их простое «отделение» невозможно. Тогда поможет Partial Least Squares (PLS). Этот метод ищет линейные комбинации исходных переменных, которые максимально коррелируют с целевой переменной, что помогает обойти проблему мультиколлинеарности.

Пример PLS:

from sklearn.cross_decomposition import PLSRegression  # Выбираем число компонент pls = PLSRegression(n_components=1) pls.fit(X_train, y_train) y_pred_pls = pls.predict(X_test).flatten()  print(f"PLS Regression MSE: {mean_squared_error(y_test, y_pred_pls):.4f}")

Статья подготовлена для будущих студентов специализации «Machine Learning». Хорошая новость: в рамках этого курса студенты получат поддержку карьерного центра Otus. Узнать подробнее


ссылка на оригинал статьи https://habr.com/ru/articles/892828/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *