Подготовка к собеседованию по классическому машинному обучению
Вопросы для собеседования инженера по классическому машинному обучению
15 отобранных вопросов для собеседования по классическому машинному обучению, сгруппированных по уровням подготовки. Используйте их для повторения основ, понимания практических компромиссов и рассуждений на уровне Senior-специалиста в производственной среде.
1В чём разница между параметром модели и гиперпараметром в обучении с учителем?
В машинном обучении с учителем параметры модели — это внутренние переменные, которые усваиваются непосредственно из обучающих данных с помощью алгоритма оптимизации (такого как градиентный спуск или нормальные уравнения). Примерами служат веса регрессии и свободный член (bias) в линейных моделях или пороги разбиения в деревьях решений. Напротив, гиперпараметры — это внешние конфигурационные настройки, задаваемые до начала обучения, которые определяют процесс обучения, ёмкость модели или её архитектуру. Обычно они выбираются вне внутренней процедуры подгонки параметров модели. Некоторые из них можно оптимизировать с помощью методов на основе валидации или других методов внешнего цикла оптимизации; это различие не означает, что они математически необучаемы. Минимизация одной лишь ошибки на обучении может способствовать избыточной ёмкости и не даёт оценки обобщающей способности. Примеры включают скорость обучения (learning rate), силу регуляризации (lambda/C), количество деревьев в случайном лесу и максимальную глубину дерева. Гиперпараметры настраиваются с использованием валидационных данных или кросс-валидации.
from sklearn.linear_model import Ridge
import numpy as np
X = np.array([[1.0], [2.0], [3.0]])
y = np.array([2.0, 4.0, 6.0])
# Hyperparameter: alpha (regularization strength set beforehand)
model = Ridge(alpha=1.0)
# Fitting optimizes internal parameters on training data
model.fit(X, y)
# Learned parameters
print(f"Weight (Parameter): {model.coef_[0]:.4f}")
print(f"Intercept (Parameter): {model.intercept_:.4f}")
2Какие предположения лежат в основе линейной регрессии методом наименьших квадратов и как диагностика остатков выявляет нарушения этих предположений?
Метод наименьших квадратов (МНК) минимизирует сумму квадратов остатков; для оценки коэффициентов модели нормальное распределение ошибок не требуется. Для единственности вектора коэффициентов матрица плана должна иметь полный столбцовый ранг, что исключает точную линейную зависимость, но не любую корреляцию между предикторами. Для стандартной несмещённой интерпретации линейной модели предполагается корректно специфицированное условное математическое ожидание E[y|X] = X beta, что эквивалентно E[epsilon|X] = 0. Гомоскедастичные, некоррелированные ошибки обеспечивают классический результат эффективности Гаусса — Маркова и стандартную формулу ковариации. Условная нормальность дополнительно обеспечивает точный статистический вывод t- и F-критериев на конечных выборках; подходящие методы для больших выборок или робастные методы могут работать и без неё при выполнении собственных предположений. Оценённые остатки являются оценками истинных ошибок и, как правило, скоррелированы, даже если исходные ошибки независимы. График остатков против подогнанных значений позволяет выявить кривизну и изменения дисперсии, график против времени — зависимость, а график «квантиль — квантиль» (Q–Q) — отклонения в распределении. Мера удалённости наблюдения в пространстве признаков (leverage) и расстояние Кука указывают на влиятельные наблюдения. Эти диагностические методы указывают на проблемы, но не могут доказать экзогенность или исключить наличие пропущенных смешивающих факторов (confounders).
import numpy as np
import statsmodels.api as sm
np.random.seed(42)
X = np.linspace(1, 10, 50)
# Quadratic underlying pattern creates a linearity violation
y = 2 * X + 0.5 * (X ** 2) + np.random.normal(0, 2, 50)
X_with_const = sm.add_constant(X)
model = sm.OLS(y, X_with_const).fit()
residuals = model.resid
print(f"Mean Residual: {np.mean(residuals):.4f}")
print(f"Curvature in residuals indicates model misspecification.")
3Как логистическая регрессия решает задачу бинарной классификации и зачем нужна сигмоида?
Пусть $p=P(Y=1\mid X=x)$. Модель задаёт логарифм отношения шансов как $\log(p/(1-p))=w^\top x+b$. Логит является функцией связи, а обратная к нему сигмоида $\sigma(z)=1/(1+e^{-z})$ переводит линейную оценку $z$ в вероятность между нулём и единицей. При пороге $\tau$ выбирают класс 1, если $p\ge\tau$, и класс 0 иначе. При пороге $0{,}5$ граница проходит по гиперплоскости $w^\top x+b=0$. Другой фиксированный порог сдвигает её.
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
w = np.array([1.5, -2.0])
b = 0.5
x = np.array([2.0, 1.0])
z = np.dot(w, x) + b
prob = sigmoid(z)
threshold = 0.5
pred = int(prob >= threshold)
print(f"Log-odds (z): {z:.2f}")
print(f"Probability: {prob:.4f}")
print(f"Class Prediction: {pred}")
4Что такое L2-регуляризация и как гребневая регрессия (Ridge regression) изменяет целевую функцию и оценки коэффициентов?
L2-регуляризация (гребневая регрессия) добавляет штраф, пропорциональный сумме квадратов весов, к функции потерь обычного метода наименьших квадратов (МНК): $$\min_w \|y - Xw\|_2^2 + \lambda \|w\|_2^2$$ Аналитически гребневая регрессия модифицирует нормальные уравнения, добавляя $\lambda I$ к матрице Грама перед обращением: $$w_{\text{ridge}} = (X^T X + \lambda I)^{-1} X^T y$$ Основные последствия для целевой функции и оценок коэффициентов: 1. Сжатие (Shrinkage): коэффициенты сжимаются к нулю пропорционально дисперсии признаков и их корреляции, уменьшая сложность модели без их точного обнуления. 2. Мультиколлинеарность и обратимость: когда признаки коллинеарны или $p > N$, матрица $X^T X$ вырождена или плохо обусловлена. При $\lambda > 0$ добавление $\lambda I$ гарантирует, что $(X^T X + \lambda I)$ строго положительно определена и обратима, что стабилизирует оценки параметров. 3. Компромисс смещения и дисперсии (bias-variance trade-off): увеличение $\lambda$ вносит намеренное смещение в оценки коэффициентов, уменьшая при этом дисперсию в стандартной модели с фиксированным планом эксперимента; чистый выигрыш в предсказательной способности зависит от задачи и величины штрафа, поэтому её следует выбирать с помощью валидации. 4. Требование масштабирования признаков: поскольку штраф относится ко всем весам одинаково, признак с бо́льшим числовым масштабом может оказывать тот же эффект с меньшим, менее штрафуемым коэффициентом. Стандартизация обычно полезна для сопоставимости штрафов, но является решением при моделировании, а не строгим математическим требованием. Оценивайте параметры масштабирования только на обучающих фолдах и обычно не накладывайте штраф на свободный член (intercept).
import numpy as np
def ridge_regression(X, y, alpha):
X_std = (X - np.mean(X, axis=0)) / np.std(X, axis=0)
n_features = X_std.shape[1]
I = np.eye(n_features)
beta = np.linalg.inv(X_std.T @ X_std + alpha * I) @ X_std.T @ y
return beta
X = np.array([[1.0, 2.0], [2.0, 4.1], [3.0, 5.9], [4.0, 8.2]])
y = np.array([2.1, 4.0, 6.2, 8.1])
weights = ridge_regression(X, y, alpha=1.0)
print('Ridge Weights:', weights)
5Как дерево решений рекурсивно разбивает пространство признаков и по каким критериям выбирает разбиения при классификации?
В типичном дереве CART в каждом узле перебирают доступные разбиения вида $x_j\le t$ и жадно выбирают то, которое сильнее уменьшает неоднородность. Процесс повторяют в дочерних узлах до ограничения глубины, минимального размера листа или другого условия остановки. Такие разбиения образуют области с границами, параллельными осям. Для долей классов $p_k$ неоднородность Джини равна $1-\sum_k p_k^2$, энтропия — $-\sum_k p_k\log_2 p_k$. Выигрыш равен неоднородности родителя минус средняя неоднородность потомков, взвешенная по числу объектов. Обе меры равны нулю в чистом узле. ID3 использует информационный выигрыш, а C4.5 применяет нормированный критерий gain ratio.
6Что такое метод $k$ ближайших соседей (kNN) и как он предсказывает класс или числовое значение?
kNN хранит обучающие объекты и при прогнозе находит $k$ ближайших к новому объекту по выбранной метрике, например евклидовой. В классификации выбирают класс с наибольшим числом голосов, в регрессии обычно усредняют целевые значения соседей. Можно дать более близким соседям больший вес. Поиск может быть полным или использовать индекс. При чувствительной к масштабу метрике подберите масштаб признаков, чтобы единицы измерения не определяли результат произвольно.
7Какое предположение об условной независимости делает наивный байесовский классификатор и почему он может работать при его нарушении?
При заданном классе $y$ модель предполагает $P(X_1,\ldots,X_d\mid Y=y)=\prod_j P(X_j\mid Y=y)$. По теореме Байеса оценка класса пропорциональна произведению $P(Y=y)\prod_j P(X_j\mid Y=y)$. Для выбора класса достаточно найти наибольшую оценку. Зависимость признаков может, например, привести к повторному учёту одного сигнала и чрезмерной уверенности. Если наиболее вероятный класс при этом остаётся тем же, прогноз класса не меняется, хотя вероятности могут быть плохо откалиброваны.
from sklearn.naive_bayes import GaussianNB
import numpy as np
X = np.array([[1.0, 1.1], [1.2, 0.9], [-1.0, -1.2], [-0.8, -1.1]])
y = np.array([1, 1, 0, 0])
model = GaussianNB()
model.fit(X, y)
# Prediction uses argmax over class posterior scores
print("Predicted class:", model.predict([[1.1, 1.0]]))
8Как вывести решение метода наименьших квадратов (OLS) в явном виде и когда коэффициенты определены однозначно?
Минимизируем $\|y-X\beta\|^2$. Приравнивание градиента $2X^\top(X\beta-y)$ к нулю даёт нормальные уравнения $X^\top X\beta=X^\top y$. При полном столбцовом ранге $X$ получаем единственное решение $\hat\beta=(X^\top X)^{-1}X^\top y$. Для этого наблюдений должно быть не меньше, чем столбцов, и столбцы должны быть линейно независимы. При неполном ранге минимум существует и коэффициенты неоднозначны, но прогноз $X\hat\beta$ всегда единственен как ортогональная проекция $y$. Псевдообратная матрица даёт решение наименьшей нормы. На практике подходят QR или SVD без явного обращения $X^\top X$.
import numpy as np
# Design matrix X (with intercept column) and target y
X = np.array([[1, 1], [1, 2], [1, 3], [1, 4]])
y = np.array([2.1, 3.9, 6.2, 8.0])
# Normal equations: (X^T X)^(-1) X^T y
beta_hat = np.linalg.inv(X.T @ X) @ X.T @ y
H = X @ np.linalg.inv(X.T @ X) @ X.T
y_hat = H @ y
print(f"Beta: {beta_hat}")
print(f"Predictions: {y_hat}")
9Что такое оценивание по максимуму правдоподобия и как из него получается бинарная кросс-энтропия логистической регрессии?
Оценка максимального правдоподобия выбирает параметры, при которых наблюдённые данные имеют наибольшее правдоподобие. При условно независимых бинарных метках и $p_i=\sigma(w^\top x_i+b)$ совместное правдоподобие равно $\prod_i p_i^{y_i}(1-p_i)^{1-y_i}$. Логарифм превращает произведение в сумму. Отрицательное среднее логарифмов $-\frac1N\sum_i[y_i\log p_i+(1-y_i)\log(1-p_i)]$ и есть бинарная кросс-энтропия. Функция выпукла по линейным параметрам. Полный ранг обеспечивает строгую выпуклость, а при разделимости конечная нерегуляризованная оценка может отсутствовать. Штраф меняет оптимизируемую задачу.
10Как градиентный спуск минимизирует функцию потерь и как на обучение влияют шаг, сходимость и выпуклость?
На каждом шаге параметры обновляют по правилу $\theta_{t+1}=\theta_t-\eta\nabla L(\theta_t)$. Слишком маленький шаг $\eta$ замедляет обучение, слишком большой может вызвать колебания или расходимость. Остановку определяют, например, по норме градиента, изменениям параметров или лимиту итераций. Для гладкой выпуклой функции и подходящего шага можно сходиться к глобальному минимуму, если он достигается. В невыпуклой задаче результат может зависеть от инициализации. Значение обучающей потери показывает качество оптимизации, а проверка на отложенных данных — обобщение.
import numpy as np
def gradient_descent(X, y, lr=0.01, max_iters=1000, tol=1e-6):
n_samples, n_features = X.shape
theta = np.zeros(n_features)
prev_loss = float('inf')
for i in range(max_iters):
predictions = X @ theta
error = predictions - y
loss = (1 / (2 * n_samples)) * np.dot(error, error)
if abs(prev_loss - loss) < tol:
print(f'Converged at iteration {i}')
break
prev_loss = loss
grad = (1 / n_samples) * (X.T @ error)
theta -= lr * grad
return theta
11Чем L1, L2 и Elastic Net различаются по разреженности, работе с коррелированными признаками и выбору модели?
Штраф L1 $\lambda\|w\|_1$ может обнулять коэффициенты. При сильной корреляции выбор одного признака из группы часто нестабилен. L2 $\lambda\|w\|_2^2$ плавно уменьшает коэффициенты и склонен распределять вес между похожими признаками. Elastic Net складывает штрафы L1 и L2, позволяя получать разреженное решение и уменьшать нестабильность при корреляции. Масштабируйте признаки внутри обучающих разбиений и подбирайте силу и смесь штрафов по валидации с учётом задачи и интерпретируемости.
12Чем отличаются гребневая регрессия, регрессия на главных компонентах (PCR) и метод частичных наименьших квадратов (PLS)?
Гребневая регрессия добавляет штраф L2 и плавно уменьшает вклад направлений с малыми собственными значениями $X^\top X$. PCR сначала строит PCA только по $X$, оставляет несколько компонент с максимальной дисперсией и подгоняет регрессию по ним. Поэтому слабое по дисперсии, но полезное для $y$ направление может потеряться. PLS строит компоненты с учётом связи $X$ и $y$, обычно опираясь на ковариацию их проекций. Число компонент PCR или PLS и силу штрафа Ridge выбирают на валидации, выполняя все преобразования внутри обучающей части.
from sklearn.linear_model import Ridge, LinearRegression
from sklearn.decomposition import PCA
from sklearn.cross_decomposition import PLSRegression
from sklearn.pipeline import make_pipeline
# 1. Ridge: Regularized full feature space
ridge = Ridge(alpha=1.0)
# 2. PCR: Unsupervised PCA followed by OLS
pcr = make_pipeline(PCA(n_components=2), LinearRegression())
# 3. PLS: Supervised latent component projection and regression
pls = PLSRegression(n_components=2)
13Чем XGBoost, LightGBM и CatBoost различаются по обучению деревьев и обработке табличных признаков?
XGBoost поддерживает гистограммный и другие методы поиска разбиений, разные стратегии роста и выбирает направление для пропусков при разбиении. LightGBM использует гистограммы и обычно расширяет лист с лучшим выигрышем. Он поддерживает объединение взаимоисключающих разреженных признаков и режим GOSS, который чаще сохраняет объекты с большими градиентами. Для категорий может искать разбиение по группам значений. CatBoost часто строит симметричные деревья, а для некоторых категориальных статистик использует предшествующие объекты в перестановке, уменьшая утечку собственной метки. Упорядоченный бустинг также уменьшает сдвиг прогнозов при обучении.
14Как решить, нужна ли собственная функция потерь для бустинга при асимметричных издержках?
Для классификации начните со стандартной потери, проверки калибровки и выбора решения по ожидаемым затратам. Сравните это со взвешенным обучением, учитывая, что веса могут изменить калибровку. Для регрессии с разной ценой недооценки и переоценки попробуйте готовую квантильную потерю. Своя функция уместна, если стандартные варианты не отражают задачу. Проверьте требования библиотеки к градиентам и кривизне по оценке модели. Для негладкой цели рассмотрите подходящий гладкий суррогат и сравните стоимость решений на независимых данных.
import numpy as np
import xgboost as xgb
def asymmetric_mse_objective(preds, dtrain):
labels = dtrain.get_label()
residual = preds - labels
# Penalize underestimation (residual < 0) 5x more heavily than overestimation
penalty = np.where(residual < 0, 5.0, 1.0)
grad = 2.0 * penalty * residual
hess = 2.0 * penalty
return grad, hess
# Usage:
# model = xgb.train(params, dtrain, obj=asymmetric_mse_objective)
15Что такое LambdaMART и как этот алгоритм адаптирует градиентный бустинг для задач ранжирования (learning-to-rank)?
LambdaMART объединяет градиентный бустинг над деревьями регрессии с попарными обновлениями LambdaRank, взвешенными по метрике. Для документов i и j в рамках одного запроса, где i более релевантен, используется логистическая функция потерь для пар log(1 + exp(-sigma * (s_i - s_j))). Пусть a_ij = sigma * abs(Delta NDCG_ij) / (1 + exp(sigma * (s_i - s_j))). В соглашении о направлении антиградиента эта пара даёт вклад +a_ij документу i и -a_ij документу j, продвигая более предпочтительный элемент вверх. Эти взвешенные со знаком вклады суммируются по выбранным парам для каждого документа, после чего обучаются шаги бустинга, часто с использованием соответствующих оценок кривизны для значений в листьях. NDCG — нормализованный дисконтированный накопленный выигрыш. Delta NDCG измеряет изменение метрики при перестановке их текущих позиций в ранжировании. Это формирует суррогатный сигнал, согласующийся с качеством ранжирования; он не является обычной производной дискретной функции NDCG и не гарантирует глобального оптимума метрики. Не разделяйте группы документов, относящихся к одному запросу, при обучении и оценке; сэмплирование пар, усечение метрики и случаи с нулевым приростом зависят от конкретной реализации.
import lightgbm as lgb
import numpy as np
# Simulated query-grouped data: 2 queries with 3 docs each
X = np.random.randn(6, 10)
y = np.array([3, 1, 0, 2, 0, 1]) # Relevance grades (0-3)
group = [3, 3] # Query group sizes
train_data = lgb.Dataset(X, label=y, group=group)
params = {
'objective': 'lambdarank',
'metric': 'ndcg',
'ndcg_eval_at': [1, 3],
'learning_rate': 0.1,
'n_estimators': 50
}
ranker = lgb.train(params, train_data)