Classical ML面接対策

Classical Machine Learningエンジニア面接問題

レベル別にまとめた、選りすぐりの classical machine learning 面接問題 15問。基礎、実践上のトレードオフ、シニアレベルの本番運用での判断を確認できます。

Classical MLのAI面接を開始クレジットカードは不要です。1回分の無料セッションがあります。
英語での技術面接練習非ネイティブ話者が技術面接の合格を目指して練習できるモードです。

初級向け質問

1教師あり学習において、モデルパラメータとハイパーパラメータの違いは何ですか?

教師あり機械学習において、モデルパラメータとは最適化アルゴリズム(勾配降下法や正規方程式など)を通じて訓練データから直接学習される内部変数のことです。具体例としては、線形モデルにおける回帰重みやバイアス、決定木における分岐の閾値などが挙げられます。対照的に、ハイパーパラメータは学習プロセス、モデルの表現力(キャパシティ)、またはアーキテクチャを制御するために訓練前にあらかじめ設定される外部の設定値です。標準的な訓練損失の最小化によって直接学習することはできません。なぜなら、最適化手法が自明に過学習してしまうためです(例:決定木の深さを無限大に設定するなど)。具体例としては、学習率、正則化強度(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}")
AI コーチを使ってこの質問に答えてみる

2通常の最小二乗法(OLS: Ordinary Least Squares)線形回帰ではどのような前提条件(仮定)が置かれていますか。また、残差診断によってそれらの仮定の違反はどのように明らかになりますか。

通常の最小二乗法(OLS: Ordinary Least Squares)線形回帰は、いくつかの重要な仮定に基づいています。 1. 線形性: 説明変数と目的変数の関係がパラメータに対して線形であること。 2. 誤差の独立性: 各観測値および残差誤差が互いに独立していること(自己相関がないこと)。 3. 等分散性: 説明変数のすべての水準において誤差項の分散が一定であること。 4. 残差の正規性: 誤差項が正規分布に従っていること(有効な信頼区間や仮説検定に必要)。 5. 多重共線性がないこと: 説明変数間に線形従属の関係がないこと(計画行列がフルランクであること)。 残差診断では、以下のように仮定の違反を検出します。 - 残差対予測値プロット(Residuals vs. Fitted Values plot): 曲線や規則的なパターンが見られる場合は非線形性を示し、ファンネル型や扇形の広がりが見られる場合は不等分散性を示します。 - 正規Q-Qプロット(Normal Q-Q plot): 直線の対角線から系統的に逸脱している場合(S字カーブやファットテールなど)、正規性の違反を示します。 - 残差対観測順序/時間プロット(Residuals vs. Order/Time plot): 系統的な傾向や周期的なパターンが見られる場合、誤差の自己相関を示します。 - レバレッジ / クックの距離プロット(Leverage / Cook's Distance plot): 当てはめられたモデルを過度に歪める高レバレッジな外れ値や影響力のある観測点を特定します。

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.")
AI コーチを使ってこの質問に答えてみる

3ロジスティック回帰はどのようにして2値分類をモデル化するのか、またシグモイド関数はどのような役割を果たしますか?

ロジスティック回帰は、事後クラス確率 $P(Y=1|X)$ を推定することによって2値分類をモデル化します。予測される確率が $(0, 1)$ の範囲内に収まるようにするため、ロジスティック回帰は正例クラスの対数オッズ(ロジット)を入力の線形関数としてモデル化します:$\ln\left(\frac{p}{1-p}\right) = w^T x + b$。シグモイド(ロジスティック)関数 $\sigma(z) = \frac{1}{1 + e^{-z}}$ は連結関数として機能し、任意の実数値である線形スコア $z = w^T x + b \in (-\infty, +\infty)$ を有効な確率 $p \in (0, 1)$ へと単調に写像します。離散的なクラスの決定は判定閾値 $\tau$(通常は 0.5)を適用して行われます:$P(Y=1|X) \ge \tau$ の場合は $\hat{y} = 1$、それ以外は $0$ となります。$\sigma(z) = 0.5$ はまさに $z = 0$ のときに生じるため、特徴量空間における決定境界は線形超平面 $w^T 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}")
AI コーチを使ってこの質問に答えてみる

4L2正則化とは何ですか?また、Ridge回帰(リッジ回帰)は目的関数と係数の推定値をどのように変化させますか?

L2正則化(Ridge回帰)は、通常の最小二乗法(OLS: Ordinary Least Squares)の損失関数に対して、重みの二乗和に比例するペナルティ項を追加します:$$\min_w \|y - Xw\|_2^2 + \lambda \|w\|_2^2$$ 解析的には、Ridge回帰は逆行列計算の前にグラム行列(Gram matrix)に $\lambda I$ を加算することで正規方程式を変更します:$$w_{\text{ridge}} = (X^T X + \lambda I)^{-1} X^T y$$ 目的関数および係数の推定値に対する主な影響は以下のとおりです:1. 縮減(Shrinkage): 係数は特徴量の分散と相関に比例してゼロ方向に縮減され、係数を完全にゼロにすることなくモデルの複雑度を低減します。2. 多重共線性(マルチコリニアリティ)と逆行列可能性: 特徴量間に共線性がある場合や $p > N$ の場合、$X^T X$ は特異行列または悪条件行列になります。$\lambda I$ を加えることで $(X^T X + \lambda I)$ が厳密に正定値となり逆行列が存在することが保証されるため、パラメータ推定値が安定します。3. バイアスとバリアンスのトレードオフ: $\lambda$ を大きくすると、係数推定値に意図的なバイアスが導入される一方でバリアンスが大幅に低減され、未知のデータに対する汎化誤差の期待値が低下します。4. 特徴量スケーリングの必要性: ペナルティ項はすべての重みを同等に扱うため、スケールが大きい特徴量ほど過剰に正則化されてしまいます。そのため、学習前に特徴量を標準化(平均0、分散1)しておく必要があります。

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)
AI コーチを使ってこの質問に答えてみる

5決定木は特徴空間をどのように再帰的に分割し、分類の分割を選択するためにどのような基準が使用されますか?

決定木は、**再帰的2分割**(recursive binary partitioning)と呼ばれるトップダウンの貪欲アルゴリズムによって特徴空間を分割します。すべての訓練データを含むルートノードから開始し、不純度の減少量が最大となる軸平行な単一の分割点($X_j \le t$)を見つけるために、すべての特徴量と可能な閾値を探索します。データセットは2つの子ノードに分割され、停止基準(例:最大深度、葉ごとの最小サンプル数、またはノードの完全な純粋化)に達するまで、各子ノードに対してこの手順が再帰的に適用されます。分割は閾値に対して一度に1つの特徴量を評価するため、結果として生じる決定境界は直交超平面(特徴空間内の軸に平行な矩形領域)になります。分類木で最適な分割を評価および選択するために、主に2つの不純度基準が使用されます。1. **ジニ不純度(CARTで使用)**:ノードのクラス分布に従ってランダムにラベル付けした場合に、無作為に選んだサンプルが誤分類される確率を測定します。割合 $p_k$ を持つ $K$ クラスの場合:$$I_G = 1 - \sum_{k=1}^K p_k^2$$ 2. **エントロピーと情報利得(ID3やC4.5で使用)**:エントロピーはノード内の不確実性を測定します:$H = -\sum_{k=1}^K p_k \log_2(p_k)$。分割は**情報利得**(Information Gain)を最大化するように選択されます。これは親ノードのエントロピーから子ノードの加重平均エントロピーを差し引いたものです:$$IG = H(\text{parent}) - \sum_{c \in \{\text{left, right}\}} \frac{N_c}{N} H(c)$$ どちらの指標も、ノードが完全に純粋である(すべてのサンプルが単一のクラスに属する)場合は0になり、クラスが均等に分布している場合に最大値をとります。

import numpy as np

def gini(labels):
    _, counts = np.unique(labels, return_counts=True)
    p = counts / len(labels)
    return 1.0 - np.sum(p ** 2)

def entropy(labels):
    _, counts = np.unique(labels, return_counts=True)
    p = counts / len(labels)
    return -np.sum(p * np.log2(p + 1e-12))

# Evenly split node (impure) vs single-class node (pure)
impure_node = np.array([0]*10 + [1]*10)
pure_node = np.array([0]*20)

print(f"Impure - Gini: {gini(impure_node):.2f}, Entropy: {entropy(impure_node):.2f}")
print(f"Pure   - Gini: {gini(pure_node):.2f}, Entropy: {entropy(pure_node):.2f}")
AI コーチを使ってこの質問に答えてみる

6k近傍法(k-NN: k-Nearest Neighbors)とは何ですか。また、分類と回帰においてどのように予測を行いますか。

k近傍法(kNN: k-Nearest Neighbors)は、ノンパラメトリックでインスタンスベース(遅延学習型)の教師あり学習アルゴリズムです。明示的なパラメトリックモデルの学習は行わず、訓練データセットを保持しておき、推論時にすべての計算を実行します。 予測のワークフロー: 1. 距離計算: クエリインスタンスが入力されると、指定された距離尺度(ユークリッド距離、マンハッタン距離、ミンコフスキー距離など)を用いて、保持されているすべての訓練インスタンスとの距離を計算します。 2. 近傍の選択: クエリインスタンスとの距離が最も小さい $k$ 個の訓練インスタンスを選択します。 3. 集約: - 分類: $k$ 個の近傍における多数決(最頻値)、または距離に応じた重み付け投票によってクラスを割り当てます。 - 回帰: $k$ 個の近傍の目的変数の局所平均(平均値または中央値)、あるいは距離に応じた重み付け平均を算出することで連続値を予測します。 距離計算は特徴量のスケールに直接依存するため、値の絶対値が大きい特徴量が距離計算を支配しないよう、特徴量の正規化や標準化が不可欠です。

from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
from sklearn.preprocessing import StandardScaler
import numpy as np

X_train = np.array([[1000.0, 1.0], [2000.0, 2.0], [1500.0, 1.5], [5000.0, 5.0]])
y_cls = np.array([0, 0, 0, 1])
y_reg = np.array([10.0, 20.0, 15.0, 50.0])

# Feature scaling is mandatory for distance-based algorithms
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)

# 1. Classification (Majority Vote)
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_scaled, y_cls)

# 2. Regression (Local Average)
reg = KNeighborsRegressor(n_neighbors=3)
reg.fit(X_scaled, y_reg)

query = scaler.transform([[1800.0, 1.8]])
print('Classification:', clf.predict(query))
print('Regression:', reg.predict(query))
AI コーチを使ってこの質問に答えてみる

7ナイーブベイズの条件付き独立性の仮定を述べ、その仮定が満たされない場合でもナイーブベイズが良好に機能することがある理由を説明してください。

ナイーブベイズの条件付き独立性の仮定とは、クラスラベル Y = y が与えられたとき、すべての特徴量 X_1, X_2, ..., X_d が互いに独立であるという仮定です。すなわち、P(X_1, ..., X_d | Y = y) = \prod_{j=1}^d P(X_j | Y = y) と表されます。 ベイズの定理を用いると、事後確率は P(Y = y | X) \propto P(Y = y) \prod_{j=1}^d P(X_j | Y = y) となります。ここで、P(Y = y) はクラスの事前確率、P(X_j | Y = y) はクラス条件付き尤度(例えば連続特徴量に対するガウス分布や、出現頻度カウントに対する多項分布など)です。 独立性の仮定が崩れている場合でも、実務においてナイーブベイズが良好に機能することが多い理由は、分類が正確な確率キャリブレーションではなく argmax による決定規則(argmax_y P(Y=y | X))に依存しているためです。特徴量間の相関によって予測確率が過度に確信的になったり歪んだりしても、正しいクラスが相対的な順位で最上位を維持することがよくあります。相関によってクラス尤度の順位が入れ替わらない限り、0-1損失に基づく分類決定は正確なまま保たれます。

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]]))
AI コーチを使ってこの質問に答えてみる

中級向け質問

8OLS (Ordinary Least Squares) の閉形式解を導出または説明し、解が一意に存在する条件を述べてください。

通常の最小二乗法(OLS: Ordinary Least Squares)の目的関数は、残差平方和を最小化することです:$S(\beta) = \|y - X\beta\|^2 = (y - X\beta)^T (y - X\beta) = y^T y - 2\beta^T X^T y + \beta^T X^T X \beta$。$\beta$ に関する勾配を 0 と置きます: $$\nabla_\beta S(\beta) = -2 X^T y + 2 X^T X \beta = 0 \implies X^T X \beta = X^T y$$ これらは正規方程式と呼ばれます。$X^T X$ が正則(逆行列が存在する)である場合、一意の閉形式解は次のようになります: $$\hat{\beta} = (X^T X)^{-1} X^T y$$ 幾何学的には、$\hat{y} = X\hat{\beta} = X(X^T X)^{-1} X^T y = H y$ は目的ベクトル $y$ の計画行列 $X$ の列空間への直交射影を表し、ここで $H$ は射影行列(ハット行列)です。解が一意に存在するための必要十分条件は $X^T X$ が可逆であることであり、そのためには $N \times P$ の計画行列 $X$ が列フルランク($Rank(X) = P$)である必要があります。これには $N \ge P$ かつ完全な多重共線性がないこと(いずれの特徴量も他の特徴量の線形結合として表せないこと)が求められます。$X$ がランク落ち(rank-deficient)している場合、$X^T X$ は特異行列となり無数の解が存在することになりますが、これは通常、正則化やムーア・ペンローズの擬似逆行列 $X^+ y$ を用いて対処されます。

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}")
AI コーチを使ってこの質問に答えてみる

9最尤推定(MLE: Maximum Likelihood Estimation)とは何ですか。また、それがどのようにロジスティック回帰の交差エントロピー目的関数へと導かれるかを説明してください。

最尤推定(MLE: Maximum Likelihood Estimation)とは、観測されたデータセットの尤度 $L(\theta) = P(\mathcal{D}|\theta)$ を最大化する値を選択することで、モデルのパラメータ $\theta$ を推定する手法です。2値ロジスティック回帰では、各ラベル $y_i \in \{0, 1\}$ は $x_i$ を条件とし、成功確率 $p_i = \sigma(w^T x_i + b)$ を持つ独立したベルヌーイ確率変数としてモデル化されます。観測値 $i$ に対する確率質量関数は $P(y_i|x_i) = p_i^{y_i} (1 - p_i)^{1 - y_i}$ です。サンプルが独立同分布(i.i.d.)であると仮定すると、同時尤度は次のようになります。 $$L(w, b) = \prod_{i=1}^N p_i^{y_i} (1 - p_i)^{1 - y_i}$$ 自然対数をとることで、積が計算上扱いやすい対数尤度の和に変換されます。 $$\ell(w, b) = \sum_{i=1}^N \left[ y_i \ln(p_i) + (1 - y_i) \ln(1 - p_i) \right]$$ 最適化アルゴリズムは通常、最小化問題として定式化されるため、対数尤度に負の符号を付けてサンプルサイズ $N$ で正規化します。これにより負の対数尤度(NLL: Negative Log-Likelihood)が得られ、これはまさにバイナリ交差エントロピー(対数損失)目的関数となります。 $$J(w, b) = -\frac{1}{N} \sum_{i=1}^N \left[ y_i \ln(p_i) + (1 - y_i) \ln(1 - p_i) \right]$$ この目的関数は線形ロジット/重みに対して凸であるため、適切な数値ソルバーによって大域的最適解を求めることができます。厳密な凸性と有限で一意な最尤推定量を得るには、十分な特徴量ランク、正則化、およびクラスの完全分離が存在しないことなどの追加条件が必要です。

import numpy as np

y_true = np.array([1, 0, 1, 1])
y_prob = np.array([0.9, 0.2, 0.8, 0.4])

# Binary cross-entropy (Negative Log-Likelihood)
epsilon = 1e-15  # prevent log(0)
y_prob = np.clip(y_prob, epsilon, 1 - epsilon)
bce_loss = -np.mean(y_true * np.log(y_prob) + (1 - y_true) * np.log(1 - y_prob))

print(f"Binary Cross-Entropy Loss: {bce_loss:.4f}")
AI コーチを使ってこの質問に答えてみる

10勾配降下法は古典的な ML(Machine Learning)の目的関数をどのように最適化し、学習率、収束性、および凸性はトレーニングにどのように影響しますか?

勾配降下法は、モデルパラメータに対する目的関数の勾配と逆方向にパラメータを反復更新することで、経験的損失関数を最小化します:$\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t)$。トレーニングに影響を与える主な要因は以下の通りです:1. 学習率($\eta$):ステップサイズを制御します。小さすぎると収束が極端に遅くなり、学習が停滞する可能性があります。大きすぎると最小値を飛び越えてしまい(オーバーシュート)、振動や数値的発散を引き起こします。2. 収束性:勾配ノルムの微小化($||\nabla L(\theta)|| \le \epsilon$)、パラメータ更新量の最小化、または連続するイテレーション間での損失のプラトー化(下げ止まり)などの停止基準を監視することで判断されます。3. 凸性:凸目的関数(通常の OLS(Ordinary Least Squares)線形回帰やロジスティック回帰など)では、局所的最小値がすべて大域的最小値であることが保証されるため、適切なステップサイズを選択すれば勾配降下法で確実に収束します。非凸目的関数(多層ニューラルネットワークなど)では、損失ランドスケープに複数の局所的最小値、鞍点、プラトーが存在し、最終的な解は初期値に敏感になります。4. 最適化と汎化性能:訓練損失の収束は最適化の成功を反映しますが、検証損失は汎化性能を評価します。訓練損失が低く検証誤差が高い状態は、最適化の失敗ではなく過学習(過適合)を示しています。

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
AI コーチを使ってこの質問に答えてみる

11スパース性、相関を持つ特徴量、および実践的なモデル選択の観点から、L1、L2、およびElasticNet正則化を比較してください。

L1(Lasso)、L2(Ridge)、およびElasticNet正則化は、ペナルティ項の形式、制約領域の幾何学的形状、スパース性、および相関関係にある説明変数の扱いにおいて異なります。 1. スパース性と幾何学的形状: - L1は絶対値ペナルティ($\lambda \|w\|_1$)を使用します。制約領域は座標軸上に尖った頂点を持つひし形(多面体)となります。損失関数の等高線がこれらの頂点と接するとき、重みが厳密にゼロになり、自動的な特徴量選択が行われます。 - L2はユークリッドノルムの2乗ペナルティ($\lambda \|w\|_2^2$)を使用します。制約領域は角のない滑らかな超球となり、重みはゼロに向かって漸近的に縮小されますが、厳密にゼロになることは稀です。 2. 相関を持つ特徴量: - 強い多重共線性がある場合、L1は相関する特徴量群の中から1つを恣意的に選択し、残りの係数をゼロにする傾向があるため、リサンプリング間で推定値が不安定になります。 - L2は相関するすべての特徴量を保持し、それらの特徴量間で重みを分散させて同時に縮小します。 3. ElasticNet: - 両方のペナルティを組み合わせます: $\lambda_1 \|w\|_1 + \lambda_2 \|w\|_2^2$(通常は $\alpha$ と $l_1\_\text{ratio}$ でパラメータ化されます)。 - Lassoのスパース性と特徴量選択の特性を持ちながら、Ridgeのグルーピング効果も維持し、相関する特徴量のクラスタをまとめて選択します。これは特に $p > N$ の場合や、深刻な多重共線性がある状況で有効です。

from sklearn.linear_model import Ridge, Lasso, ElasticNet
import numpy as np

np.random.seed(42)
X1 = np.random.randn(100, 1)
X2 = X1 + np.random.randn(100, 1) * 0.01  # highly correlated
X = np.hstack([X1, X2])
y = 3 * X1.ravel() + np.random.randn(100) * 0.1

ridge = Ridge(alpha=1.0).fit(X, y)
lasso = Lasso(alpha=0.1).fit(X, y)
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5).fit(X, y)

print('Ridge coefs:', ridge.coef_)
print('Lasso coefs:', lasso.coef_)
print('ElasticNet coefs:', elastic.coef_)
AI コーチを使ってこの質問に答えてみる

12リッジ回帰(Ridge regression)、主成分回帰(PCR: Principal Component Regression)、および部分的最小二乗法(PLS: Partial Least Squares)の大まかな違いは何ですか。

リッジ回帰、主成分回帰(PCR: Principal Component Regression)、および部分的最小二乗法(PLS: Partial Least Squares)は、多重共線性や高次元データを扱うための3つの線形手法ですが、分散を削減する方法、ならびに次元削減が連続的か教師ありかという点で異なります。 1. リッジ回帰:元の $p$ 個の特徴量をすべて保持し、L2正則化ペナルティを通じて回帰係数の大きさを連続的に縮小(収縮)させます。低次元の潜在成分を構築したり特徴量の次元を削減・除外したりするのではなく、$X^T X$ の固有値が小さい方向の分散を縮小します。 2. 主成分回帰(PCR):2段階からなる教師なし次元削減手法です。まず説明変数行列 $X$ のみに対して主成分分析(PCA: Principal Component Analysis)を適用して最大の分散を持つ直交方向を見つけ、上位 $k$ 個の主成分を保持した上で、それらの $k$ 個の成分に対して通常の最小二乗法(OLS: Ordinary Least Squares)回帰を適合させます。PCAは目的変数 $y$ を考慮しないため、PCRでは $X$ 内の分散は小さいものの $y$ に対して高い予測力を持つ成分を除外してしまうリスクがあります。 3. 部分的最小二乗法(PLS):教師あり次元削減手法です。$X$ と応答変数 $y$ の間の共分散を最大化する $X$ の線形結合を見つけることで、$k$ 個の直交する潜在成分を構築します。目的変数の情報を明示的に組み込むことで、PLSは特徴量の分散と応答変数の変動の両方を説明する成分を特定します。

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)
AI コーチを使ってこの質問に答えてみる

上級向け質問

13XGBoost、LightGBM、CatBoostなどの現代的な勾配ブースティングの実装は、学習の最適化やテーブルデータ(表形式特徴量)の処理においてどのように異なりますか?

現代のGBDT(Gradient Boosted Decision Trees: 勾配ブースティング決定木)フレームワークは、分割探索アルゴリズム、木の成長戦略、およびテーブル/カテゴリカル特徴量の処理において大きく異なります。 1. XGBoost: 伝統的に厳密な貪欲法または近似分位点スケッチ(Quantile Sketch)による分割探索(後にFast Histも採用)に依存しており、レベル単位(深さ優先)の木の成長を採用しています。欠損値に対しては、分割探索時に最適なデフォルト分岐方向を学習することで対処します。 2. LightGBM: ヒストグラムベースの分割探索(連続特徴量を通常256個の離散バケットにビニング)と、より迅速に損失を削減するためのリーフ単位(最良優先)の木の成長を採用しています。また、大きな勾配を持つインスタンスを保持しながら小さな勾配のインスタンスをサブサンプリングするGOSS(Gradient-based One-Side Sampling)や、相互に排他的なスパース特徴量を結合するEFB(Exclusive Feature Bundling)を備えています。カテゴリカル変数に対しては、ヒストグラムビンをソートすることで最適な分割を探索します($O(K \log K)$)。 3. CatBoost: 特定の深さにあるすべてのノードが全く同一の分割を共有するオブリビアン(対称)決定木を採用しており、CPU/GPUでの高速なベクトル化推論が可能です。最大の革新点はOrdered Target StatisticsおよびOrdered Boostingであり、学習データのランダムな順列に沿ってターゲット統計量を計算することで、ターゲットリーク(目的変数の漏洩)や予測シフトを防ぎます。

from catboost import CatBoostClassifier
import lightgbm as lgb
import pandas as pd

df = pd.DataFrame({
    'city': ['NY', 'LDN', 'NY', 'PAR', 'LDN', 'TOK'],
    'age': [25, 42, 30, 22, 55, 38],
    'target': [1, 0, 1, 0, 1, 0]
})
cat_cols = ['city']
df['city'] = df['city'].astype('category')

# LightGBM handles pandas 'category' dtype natively via integer binning
lgb_clf = lgb.LGBMClassifier(max_depth=3, n_estimators=10)
lgb_clf.fit(df[['city', 'age']], df['target'])

# CatBoost handles categorical column names natively with ordered TS
cb_clf = CatBoostClassifier(iterations=10, cat_features=cat_cols, verbose=False)
cb_clf.fit(df[['city', 'age']], df['target'])
AI コーチを使ってこの質問に答えてみる

14非対称なビジネスコストが存在する環境下で、勾配ブースティングモデルにカスタム損失関数を適用するのが適切かどうかをどのように判断しますか?

非対称なビジネスコストが存在する場合に勾配ブースティングでカスタム損失関数を実装すべきかを判断するには、その非対称性を確率キャリブレーションや閾値調整によって後処理で対応できるか、それとも決定木の分割生成時における最適化ランドスケープを根本的に変える必要があるかを評価する必要があります。 1. 閾値調整 vs カスタム損失: エラーコストが非対称な分類タスク(例:偽陰性のコストが偽陽性の10倍)の場合、標準的な交差エントロピーは事後確率 $P(y=1|x)$ を推定する厳格なスコアリングルール(proper scoring rule)として機能します。ただし、キャリブレーションを検証し、必要に応じて検証データ上で補正すべきです。ビジネスコスト行列 $\tau = \frac{C_{FP}}{C_{FP} + C_{FN}}$ に基づいて分類の決定閾値をシフトするか、サンプル重みを適用する方が、多くの場合簡潔であり、カスタムな導関数を定義する必要も回避できます。しかし、非対称回帰(例:在庫需要予測のための非対称ピンボール損失)や、標準的な目的関数では木構造の分割探索を適切に誘導できない非線形なペナルティがある場合には、カスタム損失関数の採用が妥当です。 2. GBDTにおける数学的要件: 2次の勾配ブースター(XGBoost、LightGBM)では、カスタム損失 $L(y, \hat{y})$ は通常、木の分割利得および葉の重み計算($w^* = -\sum g_i / (\sum h_i + \lambda)$)のために計算可能な1階勾配($g_i = \partial L / \partial \hat{y}_i$)と有効な2階曲率/ヘシアン値($h_i = \partial^2 L / \partial \hat{y}_i^2$)を必要とします。数値的安定性を保つため、ヘシアンは非負であるか、安全に近似/クリッピングされる必要があります(一部の実装では1階のみや近似的な目的関数もサポートされているため、要件はフレームワークに依存します)。微分不可能または不連続なビジネス指標は、滑らかな代理損失(Huber化やlog-coshバリアントなど)に置き換える必要があります。

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)
AI コーチを使ってこの質問に答えてみる

15LambdaMARTとは何ですか?また、ランキング学習(LTR: Learning-to-Rank)の目的に対して勾配ブースティングをどのように適応させていますか?

LambdaMARTは、MART(Multiple Additive Regression Trees / 勾配ブースティング)とLambdaRankフレームワークを組み合わせたランキング学習(LTR: Learning-to-Rank)アルゴリズムです。ランキング問題において、NDCG(Normalized Discounted Cumulative Gain)やMAPなどの目的指標は離散的な並び順(順位)に依存するため、ほぼすべての箇所で傾きが平坦であり、連続的なモデルスコアに関して微分不可能です。LambdaMARTは、同一クエリ内のアイテムのペア $(i, j)$ に対して「ラムダ勾配」($\lambda_{ij}$)と呼ばれる仮想的な勾配を構築することでこの問題を回避します。基礎となるペアワイズ勾配は、スコア差($s_i - s_j$)に対するロジスティック損失から導出されます。LambdaMARTは、ドキュメント $i$ とドキュメント $j$ の順位を入れ替えた場合に生じる目的ランキング指標の正確な変動量($|\Delta \text{NDCG}_{ij}|$)をこの勾配に乗算します: $$\lambda_{ij} = \frac{-\sigma}{1 + e^{\sigma(s_i - s_j)}} |\Delta \text{NDCG}_{ij}|$$ 各ドキュメント $i$ に対する正味の勾配は、ドキュメント $i$ が関与するすべてのペアのラムダ勾配を集約することで算出されます: $\lambda_i = \sum_{j: j \succ i} \lambda_{ij} - \sum_{k: i \succ k} \lambda_{ki}$。ブースティングアンサンブル内の標準的な回帰木は、各ブースティングイテレーションでこれらドキュメント単位の複合ラムダ勾配を学習(フィッティング)し、リストワイズのランキング指標を直接最適化します。

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)
AI コーチを使ってこの質問に答えてみる