3机器学习

线性模型

最简单的模型

线性回归对数几率回归线性判别分析

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第3章:线性模型(Linear Models)

一、导读

1.1 本章学习目标

第3章介绍机器学习中最基础也最重要的模型——线性模型。尽管形式简单,线性模型却蕴含着机器学习的核心思想,是理解更复杂模型的基石。

本章的核心学习目标包括:

  • 掌握线性回归的原理与求解:最小二乘法的数学推导、正规方程的推导过程
  • 理解对数几率回归(逻辑回归):如何用 sigmoid 函数将线性输出映射为概率
  • 掌握线性判别分析(LDA):最大化类间距离、最小化类内距离的思想
  • 了解多分类学习策略:一对一(OvO)、一对其余(OvR)、多对多(MvM)
  • 理解类别不平衡问题的处理方法:欠采样、过采样、代价敏感学习
  • 理解线性模型的可解释性优势:为什么线性模型在需要解释性的场景中仍然重要

1.2 为什么本章如此重要

线性模型是机器学习的"原子模型"。即使在实际应用中很少直接使用简单的线性模型,但几乎所有高级模型都以某种方式包含了线性成分:

  • 神经网络的每一层本质上是一个线性变换加上非线性激活
  • SVM 在高维空间中寻找线性超平面
  • 决策树的叶节点可以看作是一个线性决策
  • 广义线性模型将线性模型推广到更一般的场景

更重要的是,线性模型具有解析解(closed-form solution),这使得我们可以用数学工具深入分析其性质。相比之下,大多数复杂模型只能通过数值优化求解,难以进行理论分析。

1.3 核心问题引导

为什么最小二乘法要最小化平方误差而不是绝对误差?

对数几率回归为什么叫"回归"却是一个分类算法?

LDA 为什么叫"判别分析"而不是"分类"?

多分类问题能否直接转化为多个二分类问题?各种转化策略有什么优劣?

当数据线性不可分时,线性模型还能用吗?

线性模型的可解释性在实际应用中有多重要?


二、核心概念详解

2.1 线性回归

2.1.1 问题定义

给定数据集 $D = \{(x_1, y_1), (x_2, y_2), \ldots, (x_m, y_m)\}$,其中 $x_i = (x_{i1}; x_{i2}; \ldots; x_{id})$,线性回归试图学得一个线性模型:

$$f(x) = w_1 x_1 + w_2 x_2 + \ldots + w_d x_d + b$$

使得 $f(x) \approx y$。

用向量形式表示:$f(x) = w^T x + b$,其中 $w = (w_1; w_2; \ldots; w_d)$。

2.1.2 最小二乘法

最小二乘法的目标是最小化所有样本上的平方误差之和:

$$\min_{w, b} \sum_{i=1}^{m} (f(x_i) - y_i)^2$$

python
import numpy as np

class LinearRegression:
    """线性回归 - 最小二乘法"""
    
    def __init__(self):
        self.weights = None
        self.bias = None
    
    def fit(self, X, y):
        """
        训练线性回归模型
        X: (n_samples, n_features)
        y: (n_samples,)
        """
        # 添加偏置项
        X_b = np.c_[np.ones((X.shape[0], 1)), X]
        
        # 正规方程: w = (X^T X)^{-1} X^T y
        self.weights = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y
        self.bias = self.weights[0]
        self.weights = self.weights[1:]
        
        return self
    
    def predict(self, X):
        return X @ self.weights + self.bias
    
    def score(self, X, y):
        """R² 决定系数"""
        y_pred = self.predict(X)
        ss_res = np.sum((y - y_pred) ** 2)
        ss_tot = np.sum((y - np.mean(y)) ** 2)
        return 1 - ss_res / ss_tot

# 示例
np.random.seed(42)
X = np.random.rand(100, 2)
y = 3 * X[:, 0] + 5 * X[:, 1] + 2 + np.random.randn(100) * 0.1

model = LinearRegression()
model.fit(X, y)
print(f"真实权重: [3, 5], 真实偏置: 2")
print(f"学习权重: {model.weights.round(2)}, 学习偏置: {model.bias:.2f}")
print(f"R² 分数: {model.score(X, y):.4f}")

2.1.3 正规方程的推导

将目标函数写成矩阵形式:

$$E_{w,b} = (y - Xw)^T(y - Xw)$$

对 $w$ 求导并令其为零:

$$\frac{\partial E}{\partial w} = -2X^T(y - Xw) = 0$$

解得:$w = (X^T X)^{-1} X^T y$

python
def normal_equation(X, y):
    """正规方程的逐步推导"""
    # 步骤1: 添加偏置列
    X_b = np.c_[np.ones((X.shape[0], 1)), X]
    
    # 步骤2: 计算 X^T X
    XtX = X_b.T @ X_b
    
    # 步骤3: 计算 (X^T X)^{-1}
    XtX_inv = np.linalg.inv(XtX)
    
    # 步骤4: 计算 X^T y
    Xty = X_b.T @ y
    
    # 步骤5: 计算 w
    w = XtX_inv @ Xty
    
    return w

# 验证
np.random.seed(42)
X = np.random.rand(100, 2)
y = 3 * X[:, 0] + 5 * X[:, 1] + 2 + np.random.randn(100) * 0.1

w = normal_equation(X, y)
print(f"正规方程求解: w = {w.round(2)}")
print(f"结果: 偏置={w[0]:.2f}, 权重={w[1:].round(2)}")

2.1.4 最小二乘法的几何解释

最小二乘法可以理解为将 $y$ 投影到 $X$ 的列空间上。投影后的向量 $\hat{y} = Xw$ 是 $y$ 在 $X$ 列空间上的最佳近似。

2.2 对数几率回归(逻辑回归)

2.2.1 从回归到分类

对数几率回归(logistic regression)虽然名字里有"回归",但实际上是一个分类算法。它的核心思想是:先用线性模型计算一个实数值,然后用 sigmoid 函数将其映射到 (0, 1) 区间,作为概率输出。

$$p(y=1|x) = \frac{1}{1 + e^{-(w^T x + b)}} = \sigma(w^T x + b)$$

python
class LogisticRegression:
    """对数几率回归(逻辑回归)"""
    
    def __init__(self, lr=0.01, n_iters=1000):
        self.lr = lr
        self.n_iters = n_iters
        self.weights = None
        self.bias = None
    
    def sigmoid(self, z):
        return 1 / (1 + np.exp(-np.clip(z, -500, 500)))
    
    def fit(self, X, y):
        n_samples, n_features = X.shape
        self.weights = np.zeros(n_features)
        self.bias = 0
        
        for _ in range(self.n_iters):
            # 前向传播
            z = X @ self.weights + self.bias
            y_pred = self.sigmoid(z)
            
            # 计算梯度
            dw = (1 / n_samples) * X.T @ (y_pred - y)
            db = (1 / n_samples) * np.sum(y_pred - y)
            
            # 更新参数
            self.weights -= self.lr * dw
            self.bias -= self.lr * db
        
        return self
    
    def predict_proba(self, X):
        z = X @ self.weights + self.bias
        return self.sigmoid(z)
    
    def predict(self, X, threshold=0.5):
        return (self.predict_proba(X) >= threshold).astype(int)
    
    def score(self, X, y):
        return np.mean(self.predict(X) == y)

# 示例:西瓜分类
np.random.seed(42)
# 好瓜:密度高、颜色深
X_good = np.random.randn(30, 2) * 0.5 + [1, 1]
# 坏瓜:密度低、颜色浅
X_bad = np.random.randn(30, 2) * 0.5 + [-0.5, -0.5]

X = np.vstack([X_good, X_bad])
y = np.array([1] * 30 + [0] * 30)

model = LogisticRegression(lr=0.1, n_iters=1000)
model.fit(X, y)
print(f"训练准确率: {model.score(X, y):.2%}")
print(f"权重: {model.weights.round(2)}")
print(f"偏置: {model.bias:.2f}")

2.2.2 对数几率的含义

sigmoid 函数的反函数是 logit 函数:

$$\ln \frac{p}{1-p} = w^T x + b$$

其中 $p = p(y=1|x)$。这个等式说明:线性模型的输出 $w^T x + b$ 实际上是对数几率(log-odds)。

python
def interpret_logistic_regression(weights, bias, feature_names):
    """解释逻辑回归的系数"""
    print("逻辑回归系数解释:")
    print("-" * 40)
    for name, w in zip(feature_names, weights):
        odds_ratio = np.exp(w)
        print(f"{name}: 系数={w:.3f}, "
              f"odds ratio={odds_ratio:.3f}")
        if w > 0:
            print(f"  → {name}增加1单位,正类几率增加{odds_ratio:.1f}倍")
        else:
            print(f"  → {name}增加1单位,正类几率变为{odds_ratio:.3f}倍")
    print(f"\n偏置: {bias:.3f}")

interpret_logistic_regression(
    model.weights, model.bias, 
    ['密度', '色泽']
)

2.2.3 极大似然估计

逻辑回归的参数通过极大似然估计求解。给定训练数据,似然函数为:

$$L(w, b) = \prod_{i=1}^{m} p(y_i | x_i)$$

取对数后,最大化对数似然等价于最小化对数损失(交叉熵损失):

$$\ell(w, b) = -\frac{1}{m} \sum_{i=1}^{m} [y_i \log \hat{y}_i + (1-y_i) \log(1-\hat{y}_i)]$$

python
def cross_entropy_loss(y_true, y_pred):
    """交叉熵损失函数"""
    eps = 1e-15
    y_pred = np.clip(y_pred, eps, 1 - eps)
    return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))

# 验证
y_true = np.array([1, 0, 1, 1, 0])
y_pred = np.array([0.9, 0.1, 0.8, 0.7, 0.2])
loss = cross_entropy_loss(y_true, y_pred)
print(f"交叉熵损失: {loss:.4f}")

2.3 线性判别分析(LDA)

2.3.1 基本思想

线性判别分析(Linear Discriminant Analysis)的核心思想是:将数据投影到一条直线上,使得同类样本的投影点尽可能接近,异类样本的投影点尽可能远离。

python
class LDA:
    """线性判别分析"""
    
    def __init__(self):
        self.w = None
        self.threshold = None
    
    def fit(self, X, y):
        """
        训练LDA模型
        X: (n_samples, n_features)
        y: (n_samples,) 二分类标签 0/1
        """
        # 分离两类
        X_0 = X[y == 0]
        X_1 = X[y == 1]
        
        # 计算均值向量
        mu_0 = X_0.mean(axis=0)
        mu_1 = X_1.mean(axis=0)
        
        # 计算类内散度矩阵
        S_0 = (X_0 - mu_0).T @ (X_0 - mu_0)
        S_1 = (X_1 - mu_1).T @ (X_1 - mu_1)
        S_w = S_0 + S_1
        
        # 投影方向: w = S_w^{-1} (mu_0 - mu_1)
        self.w = np.linalg.inv(S_w) @ (mu_0 - mu_1)
        
        # 计算分类阈值
        self.threshold = 0.5 * (mu_0 + mu_1) @ self.w
        
        return self
    
    def predict(self, X):
        projections = X @ self.w
        return (projections > self.threshold).astype(int)
    
    def score(self, X, y):
        return np.mean(self.predict(X) == y)

# 示例
np.random.seed(42)
X = np.vstack([
    np.random.randn(30, 2) * 0.5 + [1, 1],
    np.random.randn(30, 2) * 0.5 + [-1, -1]
])
y = np.array([0] * 30 + [1] * 30)

lda = LDA()
lda.fit(X, y)
print(f"LDA 训练准确率: {lda.score(X, y):.2%}")
print(f"投影方向: {lda.w.round(3)}")

2.3.2 LDA 的几何解释

LDA 将高维数据投影到一维空间(对二分类),投影方向的选择使得:

  • 类间距离最大化:$||w^T \mu_0 - w^T \mu_1||^2$ 最大
  • 类内距离最小化:$w^T S_0 w + w^T S_1 w$ 最小

优化目标:$\max_w \frac{w^T S_b w}{w^T S_w w}$,其中 $S_b = (\mu_0 - \mu_1)(\mu_0 - \mu_1)^T$

2.4 多分类学习

2.4.1 一对一(OvO)

将 N 个类两两配对,训练 N(N-1)/2 个二分类器。

python
class OvOClassifier:
    """一对一多分类"""
    
    def __init__(self, base_classifier):
        self.base_classifier = base_classifier
        self.classifiers = {}
        self.classes = None
    
    def fit(self, X, y):
        self.classes = np.unique(y)
        for i in range(len(self.classes)):
            for j in range(i + 1, len(self.classes)):
                c_i, c_j = self.classes[i], self.classes[j]
                mask = (y == c_i) | (y == c_j)
                X_sub = X[mask]
                y_sub = (y[mask] == c_j).astype(int)
                
                clf = self.base_classifier()
                clf.fit(X_sub, y_sub)
                self.classifiers[(c_i, c_j)] = clf
        
        return self
    
    def predict(self, X):
        votes = np.zeros((X.shape[0], len(self.classes)))
        
        for (c_i, c_j), clf in self.classifiers.items():
            pred = clf.predict(X)
            votes[:, c_i] += (pred == 0)
            votes[:, c_j] += (pred == 1)
        
        return np.argmax(votes, axis=1)

# 示例:三分类
np.random.seed(42)
X = np.vstack([
    np.random.randn(30, 2) + [0, 2],
    np.random.randn(30, 2) + [-2, -1],
    np.random.randn(30, 2) + [2, -1],
])
y = np.array([0] * 30 + [1] * 30 + [2] * 30)

ovo = OvOClassifier(LogisticRegression)
ovo.fit(X, y)
print(f"OvO 训练准确率: {np.mean(ovo.predict(X) == y):.2%}")

2.4.2 一对其余(OvR)

对每个类训练一个二分类器,将该类作为正例,其余类作为负例。

python
class OvRClassifier:
    """一对其余多分类"""
    
    def __init__(self, base_classifier):
        self.base_classifier = base_classifier
        self.classifiers = {}
        self.classes = None
    
    def fit(self, X, y):
        self.classes = np.unique(y)
        for c in self.classes:
            y_binary = (y == c).astype(int)
            clf = self.base_classifier()
            clf.fit(X, y_binary)
            self.classifiers[c] = clf
        return self
    
    def predict(self, X):
        scores = np.zeros((X.shape[0], len(self.classes)))
        for i, c in enumerate(self.classes):
            scores[:, i] = self.classifiers[c].predict_proba(X)
        return self.classes[np.argmax(scores, axis=1)]

ovr = OvRClassifier(LogisticRegression)
ovr.fit(X, y)
print(f"OvR 训练准确率: {np.mean(ovr.predict(X) == y):.2%}")

2.5 类别不平衡问题

2.5.1 问题描述

当数据集中不同类别的样本数量差异很大时,分类器可能倾向于预测多数类。

python
def demonstrate_class_imbalance():
    """演示类别不平衡问题"""
    np.random.seed(42)
    
    # 990个负例,10个正例
    X_neg = np.random.randn(990, 2) * 0.5
    X_pos = np.random.randn(10, 2) * 0.5 + [3, 3]
    X = np.vstack([X_neg, X_pos])
    y = np.array([0] * 990 + [1] * 10)
    
    # 简单分类器:总是预测多数类
    always_neg = np.zeros(1000)
    accuracy = np.mean(always_neg == y)
    
    print(f"总是预测负类的准确率: {accuracy:.1%}")
    print("虽然准确率很高,但模型完全没用!")
    
    # 正确的评估方式
    from collections import Counter
    pred = np.zeros(1000)
    pred[990:] = 1  # 假设能正确识别所有正例
    
    tp = np.sum((pred == 1) & (y == 1))
    fp = np.sum((pred == 1) & (y == 0))
    fn = np.sum((pred == 0) & (y == 1))
    
    precision = tp / (tp + fp) if (tp + fp) > 0 else 0
    recall = tp / (tp + fn) if (tp + fn) > 0 else 0
    f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
    
    print(f"\n正确识别正例时:")
    print(f"精确率: {precision:.2%}")
    print(f"召回率: {recall:.2%}")
    print(f"F1: {f1:.2%}")

demonstrate_class_imbalance()

2.5.2 处理方法

python
def handle_imbalance(X, y, method='oversample'):
    """处理类别不平衡"""
    n_pos = np.sum(y == 1)
    n_neg = np.sum(y == 0)
    print(f"原始数据: 正例 {n_pos}, 负例 {n_neg}")
    
    if method == 'undersample':
        # 欠采样:减少多数类
        neg_idx = np.where(y == 0)[0]
        pos_idx = np.where(y == 1)[0]
        sampled_neg = np.random.choice(neg_idx, n_pos, replace=False)
        idx = np.concatenate([sampled_neg, pos_idx])
        np.random.shuffle(idx)
        return X[idx], y[idx]
    
    elif method == 'oversample':
        # 过采样:增加少数类
        neg_idx = np.where(y == 0)[0]
        pos_idx = np.where(y == 1)[0]
        sampled_pos = np.random.choice(pos_idx, n_neg, replace=True)
        idx = np.concatenate([neg_idx, sampled_pos])
        np.random.shuffle(idx)
        return X[idx], y[idx]
    
    elif method == 'cost_sensitive':
        # 代价敏感:调整分类阈值
        print("代价敏感方法:调整分类阈值")
        return X, y

# 示例
np.random.seed(42)
X_imb = np.vstack([np.random.randn(990, 2) * 0.5, 
                    np.random.randn(10, 2) * 0.5 + [3, 3]])
y_imb = np.array([0] * 990 + [1] * 10)

for method in ['undersample', 'oversample', 'cost_sensitive']:
    print(f"\n=== {method} ===")
    X_res, y_res = handle_imbalance(X_imb, y_imb, method)
    print(f"处理后: 正例 {np.sum(y_res == 1)}, 负例 {np.sum(y_res == 0)}")

三、实战案例

3.1 案例一:波士顿房价预测

python
def boston_housing_prediction():
    """使用线性回归预测房价"""
    np.random.seed(42)
    
    # 模拟波士顿房价数据
    n = 506
    # 特征:房间数、犯罪率、师生比
    rooms = np.random.normal(6.28, 0.7, n)
    crime = np.random.exponential(3.6, n)
    ratio = np.random.normal(18.5, 2.2, n)
    
    # 真实关系:房价 = 5*房间数 - 0.3*犯罪率 - 0.5*师生比 + 噪声
    price = 5 * rooms - 0.3 * crime - 0.5 * ratio + np.random.randn(n) * 3
    
    X = np.column_stack([rooms, crime, ratio])
    y = price
    
    # 划分训练集和测试集
    indices = np.random.permutation(n)
    split = int(0.8 * n)
    train_idx, test_idx = indices[:split], indices[split:]
    
    X_train, X_test = X[train_idx], X[test_idx]
    y_train, y_test = y[train_idx], y[test_idx]
    
    # 训练模型
    model = LinearRegression()
    model.fit(X_train, y_train)
    
    # 评估
    train_score = model.score(X_train, y_train)
    test_score = model.score(X_test, y_test)
    
    print("=== 波士顿房价预测 ===")
    print(f"训练集 R²: {train_score:.4f}")
    print(f"测试集 R²: {test_score:.4f}")
    print(f"\n特征权重:")
    features = ['房间数', '犯罪率', '师生比']
    for name, w in zip(features, model.weights):
        print(f"  {name}: {w:.3f}")
    print(f"  偏置: {model.bias:.3f}")

boston_housing_prediction()

3.2 案例二:鸢尾花分类

python
def iris_classification():
    """使用逻辑回归和LDA进行鸢尾花分类"""
    np.random.seed(42)
    
    # 模拟鸢尾花数据(二分类)
    n = 100
    X_setosa = np.random.randn(50, 4) * 0.3 + [5.0, 3.4, 1.5, 0.2]
    X_versicolor = np.random.randn(50, 4) * 0.4 + [5.9, 2.8, 4.3, 1.3]
    
    X = np.vstack([X_setosa, X_versicolor])
    y = np.array([0] * 50 + [1] * 50)
    
    # 划分数据集
    indices = np.random.permutation(n)
    split = 80
    train_idx, test_idx = indices[:split], indices[split:]
    
    X_train, X_test = X[train_idx], X[test_idx]
    y_train, y_test = y[train_idx], y[test_idx]
    
    # 逻辑回归
    lr = LogisticRegression(lr=0.1, n_iters=1000)
    lr.fit(X_train, y_train)
    lr_score = lr.score(X_test, y_test)
    
    # LDA
    lda = LDA()
    lda.fit(X_train, y_train)
    lda_score = lda.score(X_test, y_test)
    
    print("=== 鸢尾花分类 ===")
    print(f"逻辑回归准确率: {lr_score:.2%}")
    print(f"LDA 准确率: {lda_score:.2%}")
    print(f"\n逻辑回归特征权重:")
    features = ['花萼长', '花萼宽', '花瓣长', '花瓣宽']
    for name, w in zip(features, lr.weights):
        print(f"  {name}: {w:.3f}")

iris_classification()

3.3 案例三:正则化线性模型

python
def regularized_regression():
    """Ridge和Lasso正则化"""
    np.random.seed(42)
    
    n_samples = 100
    n_features = 20
    X = np.random.randn(n_samples, n_features)
    
    # 只有前3个特征真正有用
    true_w = np.zeros(n_features)
    true_w[:3] = [3, 5, 2]
    y = X @ true_w + np.random.randn(n_samples) * 0.5
    
    # 普通线性回归
    model_ols = LinearRegression()
    model_ols.fit(X, y)
    
    # Ridge回归(L2正则化)
    def ridge_regression(X, y, alpha=1.0):
        X_b = np.c_[np.ones((X.shape[0], 1)), X]
        I = np.eye(X_b.shape[1])
        I[0, 0] = 0  # 不正则化偏置
        w = np.linalg.inv(X_b.T @ X_b + alpha * I) @ X_b.T @ y
        return w[0], w[1:]
    
    # Lasso回归(L1正则化)- 坐标下降法简化版
    def lasso_regression(X, y, alpha=0.1, n_iters=100):
        n, p = X.shape
        w = np.zeros(p)
        b = np.mean(y)
        
        for _ in range(n_iters):
            for j in range(p):
                residual = y - X @ w - b + X[:, j] * w[j]
                rho = X[:, j] @ residual / n
                z = np.sum(X[:, j] ** 2) / n
                if rho > alpha / 2:
                    w[j] = (rho - alpha / 2) / z
                elif rho < -alpha / 2:
                    w[j] = (rho + alpha / 2) / z
                else:
                    w[j] = 0
        
        return b, w
    
    b_ridge, w_ridge = ridge_regression(X, y, alpha=1.0)
    b_lasso, w_lasso = lasso_regression(X, y, alpha=0.1)
    
    print("=== 正则化对比 ===")
    print(f"{'特征':<8} {'真实值':<8} {'OLS':<8} {'Ridge':<8} {'Lasso':<8}")
    print("-" * 45)
    for i in range(10):
        print(f"x{i+1:<5} {true_w[i]:<8.2f} {model_ols.weights[i]:<8.2f} "
              f"{w_ridge[i]:<8.2f} {w_lasso[i]:<8.2f}")
    
    print("\n观察:")
    print("- OLS: 所有特征都有非零权重(过拟合噪声)")
    print("- Ridge: 权重整体缩小,但不为零")
    print("- Lasso: 不重要特征的权重被压缩为零(特征选择)")

regularized_regression()

四、本章小结

第3章系统介绍了三种基本的线性模型:

线性回归:用最小二乘法求解,具有解析解 $w = (X^T X)^{-1} X^T y$。适用于连续值预测。

对数几率回归(逻辑回归):用 sigmoid 函数将线性输出映射为概率,通过极大似然估计求解。虽然名字有"回归",但实际是分类算法。

线性判别分析(LDA):投影到使类间距离最大、类内距离最小的方向上。适用于分类任务。

核心知识点:

  • 多分类可以通过 OvO、OvR 等策略转化为多个二分类问题
  • 类别不平衡需要通过欠采样、过采样或代价敏感学习来处理
  • 正则化(Ridge/Lasso)可以防止过拟合并进行特征选择
  • 线性模型具有可解释性强的优势

线性模型是理解更复杂模型的基础。神经网络中的每一层、SVM 的超平面、广义线性模型等都以线性模型为组件。掌握线性模型的数学原理,对于理解整个机器学习体系至关重要。


五、思考与练习

思考题

最小二乘法为什么使用平方误差而不是绝对误差?从数学和统计两个角度分析。

对数几率回归中,如果两类数据线性可分,模型参数会怎样?如何解决这个问题?

LDA 和 PCA 都是投影方法,它们的优化目标有什么不同?

OvO 和 OvR 各训练多少个分类器?在预测时各自的决策方式是什么?

L1 正则化为什么能产生稀疏解?从几何角度解释。

编程练习

实现带 L2 正则化的线性回归(Ridge 回归),在存在多重共线性的数据上比较它与普通最小二乘法的表现。

实现 softmax 回归(多分类逻辑回归),在鸢尾花三分类数据集上训练和测试。

设计一个实验,比较 OvO 和 OvR 在多分类任务上的性能和效率。

3/16线性模型
0%