机器学习期末实战:从梯度下降到神经网络的核心代码与理论解析
1. 梯度下降:从数学原理到代码实现
梯度下降是机器学习中最基础的优化算法,它的核心思想就像一个人下山的过程——沿着最陡峭的方向一步步走到谷底。我们先从一个简单的例子开始理解这个概念。
假设你站在山顶,眼睛被蒙住,只能靠脚感受地面的倾斜程度。你会怎么做才能最快下山?最自然的策略就是每次朝着坡度最陡的方向迈一步。梯度下降就是这种策略的数学表达。
让我们用代码实现一个完整的线性回归梯度下降过程:
import numpy as np def compute_cost(x, y, w, b): """ 计算线性回归的代价函数 x: 特征向量 (m,) y: 目标值 (m,) w: 权重 b: 偏置 """ m = x.shape[0] total_cost = 0 for i in range(m): f_wb = w * x[i] + b cost = (f_wb - y[i])**2 total_cost += cost return total_cost / (2*m) def compute_gradient(x, y, w, b): """ 计算梯度 """ m = x.shape[0] dj_dw = 0 dj_db = 0 for i in range(m): f_wb = w * x[i] + b dj_dw_i = (f_wb - y[i]) * x[i] dj_db_i = f_wb - y[i] dj_dw += dj_dw_i dj_db += dj_db_i dj_dw = dj_dw / m dj_db = dj_db / m return dj_dw, dj_db def gradient_descent(x, y, w_init, b_init, alpha, iters): """ 执行梯度下降 alpha: 学习率 iters: 迭代次数 """ w = w_init b = b_init for i in range(iters): dj_dw, dj_db = compute_gradient(x, y, w, b) w = w - alpha * dj_dw b = b - alpha * dj_db return w, b在实际应用中,学习率的选择非常关键。我曾在项目中因为学习率设置过大导致模型无法收敛,就像一个人下山时步子迈得太大,反而越过了最低点。一般来说,可以尝试从0.01开始,观察损失函数的变化曲线。
2. 线性回归与逻辑回归:从连续预测到分类
线性回归和逻辑回归是机器学习中最基础的两种模型,它们看似相似,实则有着本质区别。线性回归解决的是连续值预测问题,比如预测房价、预测销售额等。而逻辑回归虽然名字里有"回归",实际上解决的是二分类问题。
让我们通过代码看看它们的区别:
# 线性回归预测 def linear_prediction(x, w, b): return w * x + b # 逻辑回归预测 def sigmoid(z): return 1 / (1 + np.exp(-z)) def logistic_prediction(x, w, b): z = np.dot(x, w) + b return sigmoid(z)逻辑回归的核心是sigmoid函数,它把线性输出压缩到(0,1)区间,可以解释为概率。我在实际项目中经常用逻辑回归做用户流失预测,当概率超过0.5时,我们认为用户可能会流失。
两种模型的损失函数也有很大不同:
- 线性回归通常使用均方误差(MSE)
- 逻辑回归使用交叉熵损失
# 线性回归损失 def linear_loss(y_true, y_pred): return np.mean((y_true - y_pred)**2) # 逻辑回归损失 def logistic_loss(y_true, y_pred): return -np.mean(y_true * np.log(y_pred) + (1-y_true)*np.log(1-y_pred))3. 神经网络基础:从单层到多层感知机
神经网络可以看作是多层逻辑回归的堆叠。最简单的神经网络是单层感知机,而多层感知机(MLP)则加入了隐藏层,大大提升了模型的表达能力。
一个典型的三层神经网络结构包括:
- 输入层:接收原始特征
- 隐藏层:进行特征变换
- 输出层:产生最终预测
让我们实现一个简单的神经网络前向传播:
def initialize_parameters(n_x, n_h, n_y): """ 初始化神经网络参数 n_x: 输入层大小 n_h: 隐藏层大小 n_y: 输出层大小 """ W1 = np.random.randn(n_h, n_x) * 0.01 b1 = np.zeros((n_h, 1)) W2 = np.random.randn(n_y, n_h) * 0.01 b2 = np.zeros((n_y, 1)) return {"W1": W1, "b1": b1, "W2": W2, "b2": b2} def forward_propagation(X, parameters): """ 前向传播 """ W1 = parameters["W1"] b1 = parameters["b1"] W2 = parameters["W2"] b2 = parameters["b2"] Z1 = np.dot(W1, X) + b1 A1 = np.tanh(Z1) # 隐藏层激活函数 Z2 = np.dot(W2, A1) + b2 A2 = sigmoid(Z2) # 输出层激活函数 cache = {"Z1": Z1, "A1": A1, "Z2": Z2, "A2": A2} return A2, cache反向传播是神经网络训练的核心,它通过链式法则计算梯度。我第一次实现反向传播时花了整整三天时间调试,最后发现是因为矩阵维度没对齐。这是一个常见的错误来源。
4. 模型评估与调优:从理论到实践
训练好模型后,我们需要评估它的性能。对于分类问题,常用的评估指标包括准确率、精确率、召回率和F1分数。
def evaluate_model(y_true, y_pred): """ 评估分类模型 """ accuracy = np.mean(y_true == y_pred) tp = np.sum((y_true == 1) & (y_pred == 1)) fp = np.sum((y_true == 0) & (y_pred == 1)) fn = np.sum((y_true == 1) & (y_pred == 0)) 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 return {"accuracy": accuracy, "precision": precision, "recall": recall, "f1": f1}过拟合是机器学习中的常见问题。我记得在一个客户流失预测项目中,模型在训练集上准确率高达98%,但在测试集上只有70%,这就是典型的过拟合。解决方法包括:
- 增加训练数据
- 使用正则化(L1/L2)
- 采用dropout技术
- 早停(early stopping)
L2正则化的实现示例:
def compute_cost_with_regularization(A2, Y, parameters, lambd): """ 带L2正则化的代价函数 lambd: 正则化系数 """ m = Y.shape[1] cross_entropy_cost = logistic_loss(Y, A2) L2_regularization_cost = (lambd/(2*m)) * (np.sum(np.square(parameters["W1"])) + np.sum(np.square(parameters["W2"]))) return cross_entropy_cost + L2_regularization_cost在实际项目中,我通常会先用简单的模型(如逻辑回归)建立baseline,然后再尝试更复杂的模型。同时使用交叉验证来评估模型性能,避免数据划分带来的偏差。