跳转至

经典机器学习算法详解

概述

本教程深入讲解经典的机器学习算法,包括原理、数学推导和Python实现,帮助你真正理解算法本质。


监督学习算法

1. 线性回归(Linear Regression)

原理

# 核心思想

找到最佳直线(或超平面)拟合数据,使预测值与真实值的误差最小。

# 数学表达

y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b

向量形式:y = WᵀX + b

损失函数

# 均方误差(MSE)
def mse_loss(y_true, y_pred):
    return np.mean((y_true - y_pred) ** 2)

# 梯度下降更新
def gradient_descent(X, y, lr=0.01, epochs=1000):
    n_samples, n_features = X.shape
    w = np.zeros(n_features)
    b = 0

    for _ in range(epochs):
        y_pred = X @ w + b

        # 计算梯度
        dw = (1/n_samples) * (X.T @ (y_pred - y))
        db = (1/n_samples) * np.sum(y_pred - y)

        # 更新参数
        w -= lr * dw
        b -= lr * db

    return w, b

正则化

# L2正则化(Ridge)
def ridge_loss(y_true, y_pred, w, lambda_reg=0.1):
    mse = np.mean((y_true - y_pred) ** 2)
    l2_penalty = lambda_reg * np.sum(w ** 2)
    return mse + l2_penalty

# L1正则化(Lasso)- 产生稀疏解
def lasso_loss(y_true, y_pred, w, lambda_reg=0.1):
    mse = np.mean((y_true - y_pred) ** 2)
    l1_penalty = lambda_reg * np.sum(np.abs(w))
    return mse + l1_penalty

2. 逻辑回归(Logistic Regression)

原理

# 核心思想

虽然名字叫"回归",实际用于二分类问题。使用Sigmoid函数将线性输出映射到概率。

# Sigmoid函数

σ(z) = 1 / (1 + e⁻ᶻ)

输出范围:(0, 1),可解释为概率

数学推导

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

# 对数似然损失(交叉熵)
def logistic_loss(y_true, y_pred):
    epsilon = 1e-15  # 防止log(0)
    y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
    return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))

多分类扩展

# Softmax回归(多分类逻辑回归)
def softmax(z):
    exp_z = np.exp(z - np.max(z, axis=1, keepdims=True))
    return exp_z / np.sum(exp_z, axis=1, keepdims=True)

# 交叉熵损失
def cross_entropy_loss(y_true, y_pred):
    return -np.mean(np.sum(y_true * np.log(y_pred + 1e-15), axis=1))

3. 支持向量机(SVM)

原理

# 核心思想

找到一个超平面,使得两类样本之间的间隔(margin)最大化。

# 数学表达

最大化间隔:2 / ||w||
等价于最小化:(1/2)||w||²
约束条件:yᵢ(wᵀxᵢ + b) ≥ 1

软间隔SVM

# 带松弛变量的SVM
# 最小化:(1/2)||w||² + CΣξᵢ
# 约束:yᵢ(wᵀxᵢ + b) ≥ 1 - ξᵢ, ξᵢ ≥ 0

def svm_loss(X, y, w, b, C=1.0):
    n_samples = X.shape[0]
    distances = y * (X @ w + b)
    hinge_loss = np.maximum(0, 1 - distances)
    return 0.5 * np.dot(w, w) + C * np.mean(hinge_loss)

核技巧

# 常用核函数

# 线性核
def linear_kernel(x1, x2):
    return np.dot(x1, x2)

# 多项式核
def polynomial_kernel(x1, x2, degree=3):
    return (np.dot(x1, x2) + 1) ** degree

# RBF核(高斯核)
def rbf_kernel(x1, x2, gamma=0.1):
    return np.exp(-gamma * np.linalg.norm(x1 - x2) ** 2)

4. 决策树(Decision Tree)

原理

# 核心思想

通过一系列规则对数据进行递归划分,形成树状结构。

# 划分标准

信息增益(ID3)
信息增益率(C4.5)
基尼系数(CART)

基尼系数

def gini_impurity(y):
    classes, counts = np.unique(y, return_counts=True)
    probabilities = counts / len(y)
    return 1 - np.sum(probabilities ** 2)

# 信息增益
def entropy(y):
    classes, counts = np.unique(y, return_counts=True)
    probabilities = counts / len(y)
    return -np.sum(probabilities * np.log2(probabilities + 1e-10))

def information_gain(y_parent, y_left, y_right):
    n_total = len(y_parent)
    n_left, n_right = len(y_left), len(y_right)

    entropy_parent = entropy(y_parent)
    entropy_children = (n_left/n_total * entropy(y_left) + 
                       n_right/n_total * entropy(y_right))

    return entropy_parent - entropy_children

5. 随机森林(Random Forest)

原理

# 核心思想

集成多棵决策树,通过投票得出最终结果。

# 两个随机性

1. 样本随机:Bootstrap采样
2. 特征随机:每棵树使用不同特征子集

实现

from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier

class SimpleRandomForest:
    def __init__(self, n_trees=100, max_features='sqrt'):
        self.n_trees = n_trees
        self.max_features = max_features
        self.trees = []

    def fit(self, X, y):
        for _ in range(self.n_trees):
            # Bootstrap采样
            indices = np.random.choice(len(X), len(X), replace=True)
            X_bootstrap, y_bootstrap = X[indices], y[indices]

            # 训练单棵树
            tree = DecisionTreeClassifier(max_features=self.max_features)
            tree.fit(X_bootstrap, y_bootstrap)
            self.trees.append(tree)

    def predict(self, X):
        # 多数投票
        predictions = np.array([tree.predict(X) for tree in self.trees])
        return np.apply_along_axis(
            lambda x: np.bincount(x.astype(int)).argmax(), 
            0, predictions
        )

6. XGBoost

原理

# 核心思想

梯度提升的高效实现,通过添加新模型来纠正前一轮的错误。

# 关键技术

1. 正则化目标函数
2. 二阶泰勒展开
3. 列采样
4. 缺失值处理

简化实现

def xgboost_objective(y_true, y_pred, gamma=0, lambda_reg=1):
    # 目标函数 = 损失函数 + 正则化
    n = len(y_true)

    # 一阶和二阶导数
    gradient = y_pred - y_true  # 简化的梯度
    hessian = np.ones(n)  # 简化的Hessian

    # 正则化项
    tree_complexity = 0  # 简化
    reg = lambda_reg * tree_complexity

    return np.sum(gradient ** 2 / (hessian + 1e-10)) + reg

无监督学习算法

1. K-Means聚类

class KMeans:
    def __init__(self, k=3, max_iters=100):
        self.k = k
        self.max_iters = max_iters

    def fit(self, X):
        # 随机初始化聚类中心
        indices = np.random.choice(len(X), self.k, replace=False)
        self.centroids = X[indices]

        for _ in range(self.max_iters):
            # 分配簇
            distances = np.array([np.linalg.norm(X - c, axis=1) 
                                 for c in self.centroids])
            self.labels = np.argmin(distances, axis=0)

            # 更新中心
            new_centroids = np.array([X[self.labels == i].mean(axis=0) 
                                     for i in range(self.k)])

            if np.allclose(self.centroids, new_centroids):
                break
            self.centroids = new_centroids

    def predict(self, X):
        distances = np.array([np.linalg.norm(X - c, axis=1) 
                             for c in self.centroids])
        return np.argmin(distances, axis=0)

2. DBSCAN

def dbscan(X, eps=0.5, min_samples=5):
    n_samples = len(X)
    labels = -1 * np.ones(n_samples)  # -1表示未分类
    cluster_id = 0

    for i in range(n_samples):
        if labels[i] != -1:
            continue

        # 找邻域内的点
        neighbors = [j for j in range(n_samples) 
                    if np.linalg.norm(X[i] - X[j]) < eps]

        if len(neighbors) < min_samples:
            labels[i] = -2  # 标记为噪声点
            continue

        # 创建新簇
        labels[i] = cluster_id
        seed_set = set(neighbors)

        while seed_set:
            j = seed_set.pop()
            if labels[j] == -2:
                labels[j] = cluster_id
            if labels[j] != -1:
                continue

            labels[j] = cluster_id
            new_neighbors = [k for k in range(n_samples) 
                           if np.linalg.norm(X[j] - X[k]) < eps]

            if len(new_neighbors) >= min_samples:
                seed_set.update(new_neighbors)

        cluster_id += 1

    return labels

算法选择指南

# 算法选择流程

数据量小(<1K)
→ 简单模型:逻辑回归、SVM

数据量中(1K-100K)
→ 树模型:随机森林、XGBoost

数据量大(>100K)
→ 线性模型、神经网络

特征维度高
→ 线性模型、SVM

需要可解释性
→ 决策树、线性回归

追求最高精度
→ XGBoost、神经网络

下一步学习

  1. 优化算法详解 - 深入理解优化方法
  2. [损失函数与评估指标] - 完整的评估体系
  3. 推荐系统实战 - 应用经典算法