经典AI算法与编程实战:从原理到代码实现
不堆砌公式,不迷恋调参,用最直接的方式理解经典算法,并亲手写几行关键代码。
前言
人工智能(AI)在过去十年取得了惊人进展,但无论深度学习如何火热,经典机器学习算法依然是数据科学家的“基本功”。它们可解释性强、训练成本低,在中小规模数据集上往往表现优异,更是理解现代复杂模型的基石。
本文将带你快速重温 六大经典算法 的核心思想,并用 极简 Python 代码 展示其关键实现步骤。全文不追求工业级封装,只求让你“一眼看透”算法本质。
1. 线性回归(Linear Regression)—— 最朴素的预测器
原理
线性回归假设目标值 yy 与特征 XX 之间存在线性关系:
y=Xw+by=Xw+b。
通过最小化 均方误差(MSE) 来求解最优权重 ww。解析解为 w=(XTX)−1XTyw=(XTX)−1XTy,实际中常用梯度下降迭代。
代码片段(手写梯度下降)
import numpy as np
class LinearRegressionGD:
def __init__(self, lr=0.01, epochs=1000):
self.lr = lr
self.epochs = epochs
self.w = None
self.b = None
def fit(self, X, y):
n_samples, n_features = X.shape
self.w = np.zeros(n_features)
self.b = 0
for _ in range(self.epochs):
y_pred = X @ self.w + self.b
dw = (1/n_samples) * X.T @ (y_pred - y)
db = (1/n_samples) * np.sum(y_pred - y)
self.w -= self.lr * dw
self.b -= self.lr * db
def predict(self, X):
return X @ self.w + self.b
实战建议:使用
sklearn.linear_model.LinearRegression时注意特征缩放,且数据需满足多重共线性较低的条件。
2. 逻辑回归(Logistic Regression)—— 二分类的看门人
原理
逻辑回归并非回归,而是分类。它将线性输出通过 Sigmoid函数 映射到 (0,1) 区间,视为正类概率。损失函数采用 交叉熵,梯度下降求解。
代码片段(核心梯度更新)
def sigmoid(z):
return 1 / (1 + np.exp(-z))
class LogisticRegressionGD:
def __init__(self, lr=0.1, epochs=1000):
self.lr = lr
self.epochs = epochs
self.w = None
self.b = None
def fit(self, X, y):
n_samples, n_features = X.shape
self.w = np.zeros(n_features)
self.b = 0
for _ in range(self.epochs):
linear = X @ self.w + self.b
predictions = sigmoid(linear)
dw = (1/n_samples) * X.T @ (predictions - y)
db = (1/n_samples) * np.sum(predictions - y)
self.w -= self.lr * dw
self.b -= self.lr * db
def predict_proba(self, X):
return sigmoid(X @ self.w + self.b)
实战提醒:逻辑回归对类别不平衡敏感,可设置
class_weight或采用过采样/欠采样。
3. 决策树(Decision Tree)—— 人类决策的数字化
原理
决策树通过递归划分特征空间,每次选择 信息增益(或基尼不纯度)最大的特征进行分裂。它天然支持多分类和可解释性,但易过拟合。
代码片段(计算基尼不纯度)
python
def gini(y):
classes = np.unique(y)
p = [np.sum(y == c) / len(y) for c in classes]
return 1 - sum(p_i**2 for p_i in p)
def best_split(X, y):
best_gain = 0
best_thresh = None
best_feature = None
for feature in range(X.shape[1]):
thresholds = np.unique(X[:, feature])
for thresh in thresholds:
left_mask = X[:, feature] <= thresh
right_mask = ~left_mask
if sum(left_mask)==0 or sum(right_mask)==0:
continue
gain = gini(y) - (sum(left_mask)/len(y))*gini(y[left_mask]) - (sum(right_mask)/len(y))*gini(y[right_mask])
if gain > best_gain:
best_gain = gain
best_thresh = thresh
best_feature = feature
return best_feature, best_thresh
实战中直接使用
sklearn.tree.DecisionTreeClassifier,通过max_depth和min_samples_split控制过拟合。
4. K 近邻(KNN)—— 物以类聚,人以群分
原理
KNN 是一种“懒惰学习”算法,它不显式训练模型,而是存储全部训练样本。预测时计算新样本与所有训练点的距离,选取最近的 K 个点,投票决定类别(或均值回归)。
代码片段(距离计算与预测)
python
from collections import Counter
def euclidean_distance(a, b):
return np.sqrt(np.sum((a - b)**2))
class KNN:
def __init__(self, k=3):
self.k = k
self.X_train = None
self.y_train = None
def fit(self, X, y):
self.X_train = X
self.y_train = y
def predict(self, X):
preds = []
for sample in X:
distances = [euclidean_distance(sample, x_train) for x_train in self.X_train]
k_indices = np.argsort(distances)[:self.k]
k_labels = [self.y_train[i] for i in k_indices]
preds.append(Counter(k_labels).most_common(1)[0][0])
return np.array(preds)
注意事项:KNN 对特征尺度敏感,务必进行标准化;K 值过小易过拟合,过大易欠拟合。
5. 支持向量机(SVM)—— 寻找最大间隔的防线
原理
SVM 寻找一个超平面,使得两类样本到该平面的 最小距离(间隔)最大化。对于线性不可分数据,通过 核函数 映射到高维空间。经典核有 RBF、多项式等。
代码片段(线性 SVM 的软间隔——使用 SGD)
实际中很少手动实现 SVM 优化(涉及二次规划),但可以用梯度下降近似解决线性 SVM 的合页损失。
def hinge_loss(w, b, X, y, C=1.0):
n_samples = X.shape[0]
margins = y * (X @ w + b)
loss = 0.5 * np.dot(w, w) + C * np.sum(np.maximum(0, 1 - margins))
return loss / n_samples
# 梯度下降更新(省略完整类,仅示意)
# dw = w - C * X[y * (X@w+b) < 1].T @ y[y*(X@w+b)<1] (简化版)
实战中请直接用
sklearn.svm.SVC,选择合适核函数并调节C和gamma。注意数据标准化是 SVM 的“生死线”。
6. K-Means 聚类 —— 无监督的分组艺术
原理
K-Means 将数据划分为 K 个簇,每个簇由簇中心(质心)代表。迭代过程:分配样本到最近质心 → 更新质心为簇均值,直至收敛。
代码片段(完整实现)
class KMeans:
def __init__(self, k=3, max_iters=100):
self.k = k
self.max_iters = max_iters
self.centroids = None
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.linalg.norm(X[:, np.newaxis] - self.centroids, axis=2)
labels = np.argmin(distances, axis=1)
# 更新质心
new_centroids = np.array([X[labels == i].mean(axis=0) for i in range(self.k)])
if np.allclose(self.centroids, new_centroids):
break
self.centroids = new_centroids
return labels
实战要点:K 值选择常用“肘部法则”;初始质心影响较大,可多次运行取最优(
sklearn默认n_init=10)。
综合实战指南
1. 数据预处理(比算法更重要)
- 缺失值处理:删除或中位数/众数填充。
- 标准化(Z-score)或 归一化(Min-Max)—— 对 KNN、SVM、逻辑回归尤为关键。
- 类别编码:One-Hot 或 LabelEncoder。
- 训练/测试拆分:保持分层(分类问题用
stratify)。
2. 模型选择与调参
- 小数据集(<10万样本):优先尝试逻辑回归、线性 SVM 或决策树。
- 中等规模:随机森林(Bagging)或 XGBoost(Boosting)通常更强。
- 文本/图像高维数据:线性 SVM 或朴素贝叶斯可作为基线。
- 调参使用
GridSearchCV或RandomizedSearchCV,注意交叉验证。
3. 评估指标
- 分类:准确率、精确率/召回率、F1、AUC-ROC。
- 回归:MSE、MAE、R²。
- 聚类:轮廓系数、Calinski-Harabasz 指数。
4. 完整流水线示例(使用 sklearn)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
pipeline = Pipeline([
('scaler', StandardScaler()),
('clf', LogisticRegression(max_iter=1000))
])
pipeline.fit(X_train, y_train)
print("Test accuracy:", pipeline.score(X_test, y_test))
print("CV score:", cross_val_score(pipeline, X_train, y_train, cv=5).mean())
最后的话
经典算法并非“过时之物”,而是 AI 大厦的基石。掌握它们,你才能真正理解偏差-方差权衡、正则化、核技巧、维度灾难等核心概念。这些思想在深度学习中同样适用。
编程实战的真正意义在于:手写一次代码,胜过阅读十遍公式。本文提供的代码片段均为“教育版”,生产环境请直接使用 sklearn、XGBoost 等成熟库,但请务必理解其背后的优化细节。
祝你编程愉快,学有所成!