实现一个简单的机器学习分类器:从零开始构建逻辑回归模型
逻辑回归(Logistic Regression)是一种广泛应用于二分类问题的机器学习算法。尽管它的名字中带有“回归”二字,但它实际上用于解决分类问题。本文将详细介绍如何从零开始实现一个逻辑回归模型,并通过Python代码演示整个过程。我们将使用NumPy库来处理矩阵运算,不依赖任何高级机器学习框架如scikit-learn或TensorFlow。
1. 理论基础
1.1 逻辑回归简介
逻辑回归的目标是根据给定的输入特征预测输出类别。对于二分类问题,输出类别通常表示为0或1。逻辑回归模型的核心思想是使用Sigmoid函数将线性组合的结果映射到(0, 1)区间内,从而得到一个概率值。
Sigmoid函数定义如下:[ \sigma(z) = \frac{1}{1 + e^{-z}} ]
其中,( z = w^T x + b ),( w ) 是权重向量,( x ) 是输入特征向量,( b ) 是偏置项。
1.2 损失函数
为了训练逻辑回归模型,我们需要定义一个损失函数来衡量模型预测结果与真实标签之间的差距。常用的损失函数是交叉熵损失(Cross-Entropy Loss),其公式为:
[ L(y, \hat{y}) = -\left[ y \log(\hat{y}) + (1-y) \log(1-\hat{y}) \right] ]
其中,( y ) 是真实标签,( \hat{y} ) 是预测的概率。
1.3 梯度下降
为了最小化损失函数,我们使用梯度下降法更新模型参数。梯度下降的基本思想是沿着损失函数梯度的反方向调整参数,逐步逼近最优解。
对于每个参数 ( w_i ) 和 ( b ),梯度计算公式如下:
[ \frac{\partial L}{\partial w_i} = (\hat{y} - y) \cdot x_i ][ \frac{\partial L}{\partial b} = (\hat{y} - y) ]
2. Python实现
接下来,我们将使用Python和NumPy库来实现逻辑回归模型。我们将分步骤进行:数据预处理、模型初始化、前向传播、计算损失、反向传播、参数更新和模型评估。
2.1 数据准备
首先,我们生成一些模拟数据。假设我们有一个包含两个特征的数据集,目标是将其分为两类。
import numpy as npimport matplotlib.pyplot as plt# 生成模拟数据np.random.seed(42)X = np.random.randn(200, 2)y = np.where(X[:, 0] * X[:, 1] > 0, 1, 0)# 可视化数据plt.scatter(X[y == 0, 0], X[y == 0, 1], label='Class 0', color='blue')plt.scatter(X[y == 1, 0], X[y == 1, 1], label='Class 1', color='red')plt.legend()plt.show()
2.2 模型初始化
接下来,我们初始化模型参数 ( w ) 和 ( b )。
def initialize_parameters(dim): """ 初始化参数 :param dim: 输入特征维度 :return: 参数字典 """ w = np.zeros((dim, 1)) b = 0.0 return {"w": w, "b": b}
2.3 前向传播
在前向传播过程中,我们计算预测概率 ( \hat{y} )。
def sigmoid(z): """ Sigmoid激活函数 :param z: 线性组合结果 :return: 概率值 """ return 1 / (1 + np.exp(-z))def forward_propagation(X, parameters): """ 前向传播 :param X: 输入特征矩阵 :param parameters: 参数字典 :return: 预测概率和缓存 """ w = parameters["w"] b = parameters["b"] Z = np.dot(w.T, X.T) + b A = sigmoid(Z) cache = {"Z": Z, "A": A} return A, cache
2.4 计算损失
根据预测概率和真实标签,计算损失值。
def compute_loss(A, y): """ 计算损失 :param A: 预测概率 :param y: 真实标签 :return: 平均损失 """ m = y.shape[0] loss = -np.sum(y * np.log(A.T) + (1 - y) * np.log(1 - A.T)) / m return np.squeeze(loss)
2.5 反向传播
通过链式法则计算梯度。
def backward_propagation(X, y, cache): """ 反向传播 :param X: 输入特征矩阵 :param y: 真实标签 :param cache: 缓存 :return: 梯度字典 """ m = y.shape[0] A = cache["A"] dZ = A.T - y dw = np.dot(X.T, dZ) / m db = np.sum(dZ) / m return {"dw": dw, "db": db}
2.6 参数更新
根据梯度更新参数。
def update_parameters(parameters, grads, learning_rate=0.01): """ 更新参数 :param parameters: 参数字典 :param grads: 梯度字典 :param learning_rate: 学习率 :return: 更新后的参数字典 """ w = parameters["w"] b = parameters["b"] dw = grads["dw"] db = grads["db"] w -= learning_rate * dw.T b -= learning_rate * db return {"w": w, "b": b}
2.7 模型训练
将上述步骤整合起来,进行模型训练。
def train_model(X, y, num_iterations=1000, learning_rate=0.01): """ 训练模型 :param X: 输入特征矩阵 :param y: 真实标签 :param num_iterations: 迭代次数 :param learning_rate: 学习率 :return: 训练好的参数字典和损失历史记录 """ losses = [] parameters = initialize_parameters(X.shape[1]) for i in range(num_iterations): A, cache = forward_propagation(X, parameters) loss = compute_loss(A, y) grads = backward_propagation(X, y, cache) parameters = update_parameters(parameters, grads, learning_rate) if i % 100 == 0: print(f"Iteration {i}: Loss = {loss}") losses.append(loss) return parameters, losses
2.8 模型评估
最后,我们对训练好的模型进行评估。
def predict(X, parameters): """ 预测 :param X: 输入特征矩阵 :param parameters: 参数字典 :return: 预测标签 """ A, _ = forward_propagation(X, parameters) predictions = (A.T >= 0.5).astype(int) return predictionsdef evaluate_model(X, y, parameters): """ 评估模型 :param X: 输入特征矩阵 :param y: 真实标签 :param parameters: 参数字典 :return: 准确率 """ predictions = predict(X, parameters) accuracy = np.mean(predictions == y) return accuracy# 训练模型parameters, losses = train_model(X, y, num_iterations=1000, learning_rate=0.01)# 评估模型accuracy = evaluate_model(X, y, parameters)print(f"Accuracy: {accuracy * 100:.2f}%")# 绘制损失曲线plt.plot(losses)plt.xlabel('Iteration')plt.ylabel('Loss')plt.title('Training Loss')plt.show()
通过上述步骤,我们成功地从零开始实现了一个逻辑回归模型。虽然这个模型相对简单,但它展示了机器学习模型的基本构建过程:数据准备、模型初始化、前向传播、损失计算、反向传播、参数更新和模型评估。希望这篇文章能帮助你更好地理解逻辑回归的工作原理,并为后续深入学习其他复杂模型打下坚实的基础。