逻辑回归原理与代码实现

一、逻辑回归 vs 线性回归

比较项逻辑回归(Logistic Regression)线性回归(Linear Regression)
目标任务主要用于分类任务(如二分类、多分类)主要用于回归任务(预测连续数值)
输出概率值(通过Sigmoid或Softmax映射到(0,1)范围)直接输出一个连续值
数学模型使用Sigmoid函数(或Softmax)将线性组合转换为概率直接使用线性方程 y=wX+by = wX + b 进行预测
损失函数交叉熵损失(Cross-Entropy Loss)均方误差(MSE, Mean Squared Error)
适用场景是否属于某个类别(如邮件是否垃圾邮件)预测数值(如房价预测)
结果解释结果通常通过阈值(如0.5)转换为类别结果是一个实数

二、sigmoid函数(logistic 函数)

尽管逻辑回归有“回归” 二字, 但却是用于分类的模型, 它用 sigmoid 函数估计出样本属于正样本的概率。 对于一个问题, 逻辑回归会拟合一条曲线, 如下图所示。

sigmoid 函数可以输出样本 x 是正样本或负样本的概率

根据函数值很方便地计算出导数值。

通常 sigmoid 函数无法直接用于分类问题, 比如肿瘤分类问题的输入特征 x 不可能出现负数, 直接使用sigmoid 函数的结果永远是大于等于 0.5 的。 下面介绍使用 sigmoid 函数构建逻辑回归模型的步骤:

第一步: 用一个线性函数将输入特征向量映射成一个实数 z, 表示为z = \vec{w} \cdot \vec{x} + b

第二步: 将第一步的 z 作为输入给到 sigmoid 函数, 表示为 g(z) = \frac{1}{1 + e^{-z}}

把这两步结合就得到了逻辑回归模型:f_{\vec{w},b}(\vec{x}) = g(\vec{w}\vec{x} + b) = \frac{1}{1 + e^{-(\vec{w}\vec{x} + b)}}

逻辑回归的输入是一个或一组特征, 输出一个 0~1 的实数, 可以将这个输出看作是一个概率。 即输入特征 x, 输出类别为正类的概率。

三、决策边界

逻辑回归模型输出一个概率, 那么如何根据这个概率判断其标签是 0 还是1? 这里也需要设置一个阈值,通常设置为 0.5,当概率大于 0.5 时,预测标签是1,否则预测标签是 0。

在 sigmoid 函数中, 函数值是 0.5的位置对应z=0, 所以z = \vec{w} \cdot \vec{x} + b =0这条线就是决策边界。

如下图所示的例子, 当\vec{w} = [1,1],b=-3 时,就是我们要找的决策边界。

当参数选择不同时, 决策边界也不同, 因此, 训练逻辑回归模型其本质是要找到合适的决策边界, 而 sigmoid 函数是用来确定分类结果的。 

四、第一种表述--类别标签为0和1

代价函数用来衡量模型对训练数据集的拟合程度, 使模型能够尝试选择更好的参数。 均方误差代价函数对于逻辑回归来说并不是一个理想的代价函数, 直接使用可能会得到非凸函数,下面介绍另一种代价函数, 它可以帮助我们为逻辑回归选择更好的参数。

均方误差代价函数的公式为:

我们可以将其改成写成如下形式:

这里给出一个新的术语: 损失函数(Loss Function) 定义了单个样本预测值与真实值之间的误差。 上式中红框部分就是损失函数。 损失函数用L(f_{\vec{w},b}(\vec{x}), y)表示, 它是模型预测值和数据真实值的函数。

下面不再用平方误差作为损失函数, 而是换一种方法使代价函数成为一个凸函数。

简化为:

逻辑回归模型的代价函数可以写成:

这个损失函数是通过概率论中的最大似然估计推理出来的, 这样得到的代价函数是一个凸函数, 便于使用梯度下降法进行求解。

4.1 逻辑回归中的梯度下降法

计算过程中最重要的部分依然是梯度的计算, 在逻辑回归中梯度的计算结果如下:

梯度的计算结果看起来与线性回归中梯度的计算结果是相同的, 但实际上模型的定义是不同的。 这里f_{\vec{w},b}(\vec{x})梯度的计算结果并不是直接沿用线性回归中的结果, 而是经过推导之后刚好得到了相同的表示形式。

五、第二种表述--类别标签为+1和-1

一个样本为每一类的概率可以统一写为:

 logistic 回归的对数似然函数为

 求该函数的极大值等价于求其负函数的极小值, 由此得到目标函数为:

牛顿法求解w

思想

多元函数 f(x)在点 x*处取得极值的必要条件是梯度为 0,即\nabla f(x^*) = 0

完整流程

  1. 给定初始值x_0 和精度阈值\varepsilon , 设置 k=0;
  2. 计算梯度 和 Hessian 矩阵 ;
  3. 如果|| g_k || < \varepsilon , 则停止迭代;
  4. 计算d_k= -H_k^{-1}g_k
  5. 计算新的迭代点x_{k+1} = x_k + d_k;
  6. 令 k = k+ 1,返回步骤2

推导步骤

 利用迭代法进行计算,避免直接计算函数梯度

 多元函数在 x_0 处的二阶泰勒展开为:

忽略二次以上的项, 得到函数:

 对上式两边同时对 x 求梯度::

 令函数的梯度为 0:

得到 

g = \nabla f(x_0),得到

由于在该过程中省略了高阶项,故这个解并不一定是函数的驻点, 需要反复用此公式进行迭代。 从初始点 x_0处开始迭代, 计算函数在 x_k 处的 Hessian矩阵 H_k和梯度向量 , 然后用下面的公式进行迭代:

直至到达函数的驻点处。 其中-H_k^{-1}g_k, 称为牛顿方向。 迭代终止的条件是梯度的模接近于 0, 或者函数值下降小于指定阈值。

其中Hessian矩阵为:

与梯度下降法对比

牛顿法有更快的收敛速度, 但每一步迭代的成本也更高,在每次迭代时, 除了要计算梯度向量之外还要计算 Hessian 矩阵以及 Hessian 矩阵的逆矩阵, 所以当参数量很大的时候不适合使用牛顿法进行求解。

六、代码实现

使用威斯康星州乳腺癌数据集实现分类问题, 该数据集是scikit-learn(sklearn) 库中一个常用的内置数据集, 用于分类任务。 该数据集包含了从乳腺癌患者收集的肿瘤特征的测量值, 以及相应的良性(benign) 或恶性(malignant) 标签。 数据集包含 30 个数值型特征, 这些特征描述了乳腺肿瘤的不同测量值, 如肿瘤的半径、 纹理、 对称性等。 数据集的目标变量是二分类的,代表肿瘤的良性(benign) 或恶性(malignant) 状态。 数据集包含 569 个样本,其中良性样本 357 个, 恶性样本 212 个。

1、手动梯度下降法实现

import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 加载数据集
data = load_breast_cancer()
X = data.data
y = data.target

# 将数据集划分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)


# sigmoid函数
def sigmoid(x):
    return 1 / (1 + np.exp(-x))


# 损失函数
def loss_function(X, y, theta):
    m = len(y)
    h = sigmoid(np.dot(X, theta))
    loss = (-1/m) * np.sum(y * np.log(h) + (1 - y) * np.log(1 - h))
    return loss


# 梯度下降法优化
def gradient_descent(X, y, theta, learning_rate=0.01, max_iters=0, tol=1e-6):
    m = len(y)
    iters = 0
    while iters < max_iters:
        grad = (1/m) * np.dot(X.T, (sigmoid(np.dot(X, theta)) - y))
        theta -= learning_rate * grad
        # 计算损失值
        loss = loss_function(X, y, theta)
        # 判断是否收敛
        if np.linalg.norm(grad) < tol:
            break
        iters += 1
    return theta


# 添加一列常数项1到特征矩阵中
X_train = np.hstack((np.ones((X_train.shape[0], 1)), X_train))
X_test = np.hstack((np.ones((X_test.shape[0], 1)), X_test))

# 初始化参数向量theta
theta = np.zeros(X_train.shape[1])

# 使用梯度下降法优化参数
theta = gradient_descent(X_train, y_train, theta, max_iters=1000)

# 计算训练集的准确率
predictions_train = sigmoid(np.dot(X_train, theta)) >= 0.5
accuracy_train = np.mean(predictions_train == y_train)
print("训练集准确率:", accuracy_train)

# 计算测试集的准确率
predictions_test = sigmoid(np.dot(X_test, theta)) >= 0.5
accuracy_test = np.mean(predictions_test == y_test)
print("测试集准确率:", accuracy_test)

2、手动牛顿法实现

import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 加载数据集
data = load_breast_cancer()
X = data.data
y = data.target

# 将数据集划分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)


def sigmoid(x):
    # 使用clip将输入限制在一个合理的范围内
    return 1 / (1 + np.exp(-np.clip(x, -500, 500)))


# 修改后的损失函数
def loss_function(X, y, theta):
    m = len(y)
    h = sigmoid(np.dot(X, theta))
    # 添加一个很小的数epsilon,防止log函数中出现0
    epsilon = 1e-5
    loss = (-1/m) * np.sum(y * np.log(h + epsilon) + (1 - y) * np.log(1 - h + epsilon))
    return loss


# 损失函数的导数
def gradient(X, y, theta):
    m = len(y)
    h = sigmoid(np.dot(X, theta))
    grad = (1/m) * np.dot(X.T, (h - y))
    return grad


# 损失函数的Hessian矩阵(二阶导数)
def hessian(X, theta):
    m = X.shape[0]
    h = sigmoid(np.dot(X, theta))
    H = (1/m) * X.T @ np.diag(h) @ np.diag(1 - h) @ X #@表示矩阵乘法
    return H


# 牛顿法优化
def newton_method(X, y, theta, max_iters=0, tol=1e-6, alpha=0.1):

    iters = 0
    while iters < max_iters:
        grad = gradient(X, y, theta)
        H = hessian(X, theta)
        try:
            # 使用np.linalg.pinv计算海森矩阵的逆矩阵
            delta = np.linalg.solve(H, grad)
        except np.linalg.LinAlgError:
            # 如果计算失败,添加正则化项alpha*I到海森矩阵
            delta = np.linalg.solve(H + alpha * np.eye(H.shape[0]), grad)
        theta -= delta
        # 计算损失值
        loss = loss_function(X, y, theta)
        # 判断是否收敛
        if np.linalg.norm(delta) < tol:
            break
        iters += 1
    return theta


# 添加一列常数项1到特征矩阵中
X_train = np.hstack((np.ones((X_train.shape[0], 1)), X_train))
X_test = np.hstack((np.ones((X_test.shape[0], 1)), X_test))

# 初始化参数向量theta
theta = np.zeros(X_train.shape[1])

# 使用牛顿法优化参数
theta = newton_method(X_train, y_train, theta, max_iters=1000)

# 计算训练集的准确率
# predictions_train = logistic(np.dot(y_train, np.dot(X_train, theta))) >= 0.5
predictions_train = sigmoid(np.dot(X_train, theta)) >= 0.5
accuracy_train = np.mean(predictions_train == y_train)
print("训练集准确率:", accuracy_train)

# 计算测试集的准确率
# predictions_test = logistic(np.dot(y_test, np.dot(X_test, theta))) >= 0.5
predictions_test = sigmoid(np.dot(X_test, theta)) >= 0.5
accuracy_test = np.mean(predictions_test == y_test)
print("测试集准确率:", accuracy_test)

3、sklearn实现

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler

# 1. 加载数据集
cancer = load_breast_cancer()
X = cancer.data
y = cancer.target
# 数据归一化处理
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 2. 划分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 3. 实例化逻辑回归模型
logreg = LogisticRegression(max_iter=1000)  # 设置最大迭代次数,因为逻辑回归需要多次迭代来收敛

# 4. 使用训练数据拟合模型
logreg.fit(X_train, y_train)

# 5. 使用测试数据评估模型性能
y_pred = logreg.predict(X_test)

accuracy = accuracy_score(y_test, y_pred)
print("Accuracy: {}".format(accuracy))


 

### 逻辑回归算法原理 逻辑回归(Logistic Regression)是一种用于分类问题的统计方法,尽管名称中有“回归”,但它主要用于解决二分类或多分类问题。该算法的核心思想是通过一个函数将输入特征映射到概率空间,最终输出属于某一类别的概率[^3]。 #### 数学表达 逻辑回归的目标是最小化损失函数并找到最优权重向量 \( w \) 和偏置项 \( b \),使得模型能够很好地拟合训练数据。其核心公式如下: \[ P(y=1|x;\theta)=\frac{1}{1+\exp(-(\beta_0 + \sum_{i=1}^{n}\beta_ix_i))} \] 其中: - \( P(y=1|x;\theta) \) 表示给定输入特征 \( x \) 的情况下,样本属于类别 1 的概率; - \( \beta_0 \) 是截距项; - \( \beta_i \) 是对应于第 i 个特征的系数。 为了优化这些参数,通常会使用梯度下降法或其他最优化技术来最小化负对数似然函数(Negative Log-Likelihood Function)[^4]。 --- ### Python 实现逻辑回归代码 以下是利用纯 Python 编写的逻辑回归实现过程,未依赖任何第三方库如 `scikit-learn` 或其他框架,以便更好地理解其实现机制[^1]。 ```python import numpy as np class LogisticRegression: def __init__(self, learning_rate=0.01, num_iterations=1000): self.learning_rate = learning_rate self.num_iterations = num_iterations def sigmoid(self, z): return 1 / (1 + np.exp(-z)) def initialize_parameters(self, n_features): self.w = np.zeros((n_features, 1)) self.b = 0 def propagate(self, X, Y): m = X.shape[1] A = self.sigmoid(np.dot(self.w.T, X) + self.b) cost = -(1/m) * np.sum(Y*np.log(A) + (1-Y)*np.log(1-A)) dw = (1/m) * np.dot(X, (A-Y).T) db = (1/m) * np.sum(A-Y) grads = {"dw": dw, "db": db} return grads, cost def optimize(self, X, Y): costs = [] self.initialize_parameters(X.shape[0]) for i in range(self.num_iterations): grads, cost = self.propagate(X, Y) self.w -= self.learning_rate * grads["dw"] self.b -= self.learning_rate * grads["db"] if i % 100 == 0: costs.append(cost) parameters = {"w": self.w, "b": self.b} gradients = {"dw": grads["dw"], "db": grads["db"]} return parameters, gradients, costs def predict(self, X): A = self.sigmoid(np.dot(self.w.T, X) + self.b) predictions = (A >= 0.5).astype(int) return predictions # Example Usage if __name__ == "__main__": # Generate synthetic data np.random.seed(0) X_train = np.random.rand(2, 100) Y_train = (X_train[0,:] + X_train[1,:] > 1).reshape(1, 100) model = LogisticRegression(learning_rate=0.1, num_iterations=1000) _, _, _ = model.optimize(X_train, Y_train) test_point = np.array([[0.7], [0.8]]) prediction = model.predict(test_point) print(f"Prediction: {prediction}") ``` 上述代码展示了如何手动构建逻辑回归模型,并对其进行训练和测试。它涵盖了从初始化参数、前向传播、反向传播到更新权重的主要流程[^5]。 --- ### 总结 逻辑回归作为一种经典的机器学习算法,在实际应用中具有广泛用途。它的优势在于简单高效且易于解释,但也存在一定的局限性,比如无法有效应对非线性可分的数据集。对于初学者而言,掌握其基本原理并通过实践加深理解是非常重要的。 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值