
逻辑回归是线性回归的扩展: 它在线性回归的输出上应用了 Sigmoid 变换。
逻辑回归是最简单的单层神经网络: 它相当于一个只有输入层和输出层(带 Sigmoid 激活函数),并使用二元交叉熵 (BCE) 损失的神经网络。
当神经网络只有一层(没有隐藏层)时,如果输出层是 Sigmoid 激活函数,它就是逻辑回归;如果输出层是恒等激活函数,它就是线性回归。
深度学习通过引入隐藏层和使用 ReLU、Tanh 等非线性激活函数,能够学习和建模数据中高度复杂的非线性关系,这是线性回归和逻辑回归作为单层模型所无法实现的。
1.回顾线性回归
线性回归是最基础的回归模型,它假设输入特征 \(X\) 和输出目标 \(Y\) 之间存在线性关系。
\[Y = W^T X + b\]线性回归是最简单的神经网络。如果一个神经网络只有输入层和输出层,并且输出层没有激活函数(即使用恒等函数 \(f(z)=z\)),那么它执行的操作就是线性回归。
线性回归的核心在于预测连续数值。建立输入特征(即自变量 \(X\))与连续输出目标(未知的“另一个坐标”,即因变量 \(Y\))之间的线性关系。其目标是找到一条最佳拟合的直线或平面,使得模型可以根据输入的 \(X\) 值,直接预测出一个具体、连续的数值,例如预测房价、气温或股票价格,解决的是回归问题。
而逻辑回归的核心在于确定相对位置以进行分类。 逻辑回归:知道完整的坐标,计算和直线的相对位置,这就是逻辑回归的分类本质。这里的“直线”是模型的决策边界。逻辑回归首先通过线性计算确定数据点在线性空间中的位置(知道完整的坐标),然后通过 Sigmoid 函数将数据点与决策边界的相对位置转换为一个 \([0, 1]\) 之间的概率值。这个概率值决定了数据点被分到某一类别的可能性,从而解决了分类问题,实现了对离散类别的预测。
2.逻辑回归
2.1原理
线性 + Sigmoid 非线性 = 逻辑回归
逻辑回归主要用于二分类任务。它在线性组合的基础上,增加了一个 Sigmoid (S型) 激活函数,将输出压缩到 \([0, 1]\) 之间,表示概率。
\[P(Y=1|X) = \sigma(W^T X + b)\]其中 \(\sigma(z) = \frac{1}{1 + e^{-z}}\) 是 Sigmoid 函数。
sigmoid函数图像如下:

为什么是sigmoid函数(或类似的 S 型曲线)?
1.虽然逻辑回归本身是线性分类器,但 Sigmoid 函数作为非线性激活函数,使得它能够将线性模型的输出转化为非线性概率。更重要的是,在深度神经网络中,正是非线性激活函数的堆叠(如 Sigmoid)赋予了网络学习和拟合复杂非线性关系的能力。如果没有非线性激活函数,无论堆叠多少层,神经网络仍然只会是一个线性模型。补充:sigmoid作为神经网络的激活函数并不常见。因为在曲线平坦的区域,Sigmoid 的导数(梯度)接近于零。这意味着当模型的输出得分过大或过小时,通过反向传播计算出的梯度会非常小,导致权重 \(W\) 的更新非常缓慢,使得模型训练停滞不前。这也是在现代深度学习中,ReLU 及其变体更常被用作隐藏层激活函数的原因。
2.整个曲线是平滑且可导的(没有尖锐的拐角或跳变)。Sigmoid 函数处处可导,其导数(即梯度)也是连续的。这对于使用梯度下降法及其变体(如反向传播)来训练模型至关重要,因为优化算法需要平滑的梯度来稳定地更新模型的权重 \(W\)。
sigmoid导数图像如下:\(\sigma'(z) = \sigma(z) \cdot (1 - \sigma(z))\),其中\(\sigma(z) = \frac{1}{1 + e^{-z}}\)

Sigmoid 导函数的图像是一个对称的钟形曲线。
KL散度
线性回归的损失函数是MSE,逻辑回归的损失函数能不能也是MSE?
不能。
KL 距离,正式名称为 Kullback-Leibler 散度,是一种衡量两个概率分布之间差异的非对称度量。它源自信息论。
KL 散度的定义如下:
\[\mathcal{L}_{KL} = \sum_{i=1}^{N} P(x_i) \cdot \log \frac{P(x_i)}{Q(x_i)}\]- \(P(x_i)\) (或图中的 \(y_i\)): 真实概率分布(或参考分布 \(P\))中事件 \(x_i\) 发生的概率。
- \(Q(x_i)\) (或图中的 \(f(x_i)\)): 预测概率分布(或近似分布 \(Q\))中事件 \(x_i\) 发生的概率。
- \(N\): 离散事件或状态的总数(在分类中通常是类别数)。
- 衡量使用分布 \(Q\) 来近似分布 \(P\) 时,所损失的信息量或引入的信息增益。
举例说明:
有2枚硬币P和Q,抛硬币向上和向下的概率如下:
| P | Q | |
|---|---|---|
| 正面朝上 | \(\frac{1}{3}\) | \(\frac{1}{4}\) |
| 反面朝上 | \(\frac{2}{3}\) | \(\frac{3}{4}\) |
| 计算硬币 \(\mathbf{P}\) 相对于 \(\mathbf{Q}\) 的 KL 散度:$$D_{KL}(P | Q)$$。 |
KL 散度的性质:
-
非对称性: $$D_{KL}(P Q) \neq D_{KL}(Q P)\(。它衡量的是\)P\(相对于\)Q$$ 的散度。 -
非负性: $$D_{KL}(P Q) \geq 0\(。只有当\)P\(和\)Q$$ 完全相同时,$D_{KL}(P Q) = 0$。
一个很重要且有难度的结论:
散度表达式非对称导致拟合倾向不同:

| **$$KL(P | Q)\(** 倾向于让\)Q\(**覆盖**\)P\(的所有模式(**零回避**),因此\)Q\(会尽可能匹配\)P\(的**所有大值区域**,避免在\)P$$ 存在的地方 $Q$ 为零。 |
| **$$KL(Q | P)\(** 倾向于让\)Q\(**聚焦**于\)P\(的主要模式(**零强制**),因此\)Q\(会忽略\)P\(的长尾或不重要的区域,致力于完美匹配\)P\(的**少数几个峰值**,避免在\)P\(不存在的区域\)Q$$ 仍有值。 |
损失函数BCE
逻辑回归的标准损失函数:二元交叉熵 (BCE)
逻辑回归的训练目标是最大化似然函数,这等价于最小化二元交叉熵 (Binary Cross Entropy, BCE) 损失函数:
\[\mathcal{L}_{\text{BCE}}(P || Q) = - \sum_{i} [y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i)]\]- 真实分布 \(P\) (True Distribution): 由真实标签 \(y_i\) 定义,它是一个伯努利分布,概率质量集中在 \(y_i\)上。
- 预测分布 \(Q\) (Predicted Distribution): 由模型输出 \(\hat{y}_i\) 定义,它也是一个伯努利分布,正类概率为 $\hat{y}_i$,负类概率为 \(1 - \hat{y}_i\)。
| KL 散度 $$D_{KL}(P | Q)$$ 定义为: |
在二分类问题中,对于单个样本:
-
真实分布 \(P\) 的熵 \(H(P)\):
\[H(P) = - \sum_{k \in \{0, 1\}} P(Y=k) \log P(Y=k) = - [y \log y + (1 - y) \log (1 - y)]\]由于 \(y\) 是真实标签,只能取 0 或 1,所以 \(H(P) = 0\)(确定分布的熵为零)。
-
\[\mathcal{L}_{\text{BCE}}(P || Q) = - \sum_{k \in \{0, 1\}} P(Y=k) \log Q(Y=k) = - [y \log \hat{y} + (1 - y) \log (1 - \hat{y})]=\\ \sum_{i=1}^{n} \left[ y_i \log \frac{y_i}{f_i} + (1 - y_i) \log \frac{1 - y_i}{1 - f_i} \right]\]BCE 损失 $$\mathcal{L}_{\text{BCE}}(P Q)$$: -
\[\mathbf{D_{KL}(P || Q)} = \mathcal{L}_{\text{BCE}}(P || Q) - H(P)\]KL 散度 $$D_{KL}(P Q)$$:
由于在逻辑回归中,目标是真实标签 \(y_i\),其分布 \(P\) 是确定性的(熵 \(H(P)=0\)),所以:
\[\mathbf{D_{KL}(P || Q)} = \mathcal{L}_{\text{BCE}}(P || Q)\]因此,最小化逻辑回归的 BCE 损失,在数学上完全等价于最小化真实分布 \(P\) 和预测分布 \(Q\) 之间的 KL 散度。
2.2牛客习题1
https://www.nowcoder.com/practice/3718cf46430740c7bbb6cd31fc433b88?tpId=390&tqId=11507519&sourceUrl=%2Fexam%2Foj%2Fta%3Fpage%3D1%26tpId%3D37%26type%3D390%26channelPut%3Dw25post
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import numpy as np
import sys
from decimal import Decimal, ROUND_HALF_UP
# --- 1. Sigmoid 函数 ---
def sigmoid(z):
"""计算 Sigmoid 激活函数值"""
return 1 / (1 + np.exp(-np.clip(z, -500, 500))) # np.clip 避免指数溢出
# --- 2. 预测函数 ---
def predict(X, W):
"""计算预测概率和标签"""
# 线性组合:Z = X @ W (包含偏置项的矩阵乘法)
Z = X @ W
# 概率:Y_hat = sigmoid(Z)
Y_hat = sigmoid(Z)
# 预测标签:概率 >= 0.5 为 1,否则为 0
predictions = (Y_hat >= 0.5).astype(int)
return predictions, Y_hat
# --- 3. 损失函数 (平均交叉熵 + L2 正则) ---
def compute_loss(X, Y, W, lam):
"""计算平均交叉熵损失和 L2 正则化项"""
m = len(Y)
Z = X @ W
Y_hat = sigmoid(Z)
# 避免 log(0)
Y_hat = np.clip(Y_hat, 1e-15, 1 - 1e-15)
# 交叉熵损失
cross_entropy_loss = -np.mean(Y * np.log(Y_hat) + (1 - Y) * np.log(1 - Y_hat))
# L2 正则化项 (通常不正则化偏置项 W[0])
# 注意:W[1:] 对应 w1, w2, w3
l2_regularization = (lam / 2) * np.sum(W[1:] ** 2)
return cross_entropy_loss + l2_regularization
# --- 4. 批量梯度下降训练 ---
def train_model(X, Y, max_iter, alpha, lam, tol):
"""使用批量梯度下降训练逻辑回归模型"""
n, num_features = X.shape
W = np.zeros(num_features) # 初始化所有权重 W 为零 (包含 w0, 即 bias)
current_loss = float("inf")
for i in range(max_iter):
# 前向传播:计算预测概率
Z = X @ W
Y_hat = sigmoid(Z)
# 反向传播:计算梯度 (包含正则项)
error = Y_hat - Y
# 梯度 for 所有权重 (包括偏置 W[0])
grad = (1 / n) * X.T @ error
# 添加 L2 正则项的梯度 (只对 W[1:] 添加)
l2_grad = np.zeros(num_features)
l2_grad[1:] = lam * W[1:]
grad += l2_grad
# 权重更新
W -= alpha * grad
# 计算新的损失并检查收敛
new_loss = compute_loss(X, Y, W, lam)
# 检查收敛条件
if abs(current_loss - new_loss) < tol:
# print(f"Converged at iteration {i+1}. Loss change: {abs(current_loss - new_loss):.6f}")
break
current_loss = new_loss
return W
# --- 5. 主程序 ---
def main():
try:
# 读取训练参数 (n max_iter alpha lam tol)
line1 = sys.stdin.readline().split()
if not line1:
return
n = int(line1[0])
max_iter = int(line1[1])
alpha = float(line1[2])
lam = float(line1[3])
tol = float(line1[4])
# 读取训练数据
raw_train_data = []
for _ in range(n):
raw_train_data.append(list(map(float, sys.stdin.readline().split())))
if not raw_train_data and n > 0:
# Handle case where n > 0 but data is missing
print("Error: Missing training data.")
return
# 转换为 NumPy 数组
train_data = np.array(raw_train_data)
# 提取特征 X 和标签 Y
if n > 0:
X_train_raw = train_data[:, :3] # age, inc, dur
Y_train = train_data[:, 3] # label
else:
# 兼容 n=0 的情况
X_train_raw = np.empty((0, 3))
Y_train = np.empty(0)
# --- 特征标准化 (Z-Score Normalization) ---
# 记录训练集的均值和标准差,用于标准化测试集
if n > 0:
mu = X_train_raw.mean(axis=0)
sigma = X_train_raw.std(axis=0)
# 避免除以零:将标准差为零的特征的标准差设为 1 (它们不会被缩放)
sigma[sigma == 0] = 1
X_train_normalized = (X_train_raw - mu) / sigma
else:
# n=0 时,训练数据为空,标准化系数无关紧要
mu = np.zeros(3)
sigma = np.ones(3)
X_train_normalized = np.empty((0, 3))
# 添加偏置项 (Intercept/Bias Term)
if n > 0:
X_train = np.hstack((np.ones((n, 1)), X_train_normalized))
else:
X_train = np.empty((0, 4))
# --- 训练模型 ---
if n > 0 and max_iter > 0:
W = train_model(X_train, Y_train, max_iter, alpha, lam, tol)
elif n > 0 and max_iter == 0:
# 特殊情况:max_iter=0,权重保持初始值 W=0
W = np.zeros(X_train.shape[1])
elif n == 0:
# n=0,权重 W 仍然为零
W = np.zeros(4)
else:
# n>0, max_iter=0, W=0 已经在上面处理
W = np.zeros(4)
# --- 读取测试数据 ---
line_m = sys.stdin.readline()
if not line_m:
m = 0
else:
m = int(line_m.strip())
raw_test_data = []
for _ in range(m):
raw_test_data.append(list(map(float, sys.stdin.readline().split())))
if m > 0:
X_test_raw = np.array(raw_test_data)
# 使用训练集的均值和标准差标准化测试集
X_test_normalized = (X_test_raw - mu) / sigma
# 添加偏置项
X_test = np.hstack((np.ones((m, 1)), X_test_normalized))
# --- 进行预测 ---
predictions, probabilities = predict(X_test, W)
# --- 输出结果 ---
for pred, prob in zip(predictions, probabilities):
# 按照要求,概率保留四位小数,四舍五入
prob_str = f"{Decimal(prob):.4f}" # 使用 Decimal 实现精确的四舍五入
print(f"{pred} {prob_str}")
except Exception as e:
# print(f"An error occurred: {e}", file=sys.stderr)
pass # 示例环境中通常需要安静失败
if __name__ == "__main__":
main()
2.3牛客习题2
https://www.nowcoder.com/practice/d9c4bcf3bc5e426b8a11e690f65ba601?tab=note
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import numpy as np
import pandas as pd
def generate_data():
datasets = pd.read_csv('dataSet.csv', header=None).values.tolist()
labels = pd.read_csv('labels.csv', header=None).values.tolist()
return datasets, labels
def sigmoid(X):
#补全 sigmoid 函数功能
#code start here
return 1/(1 + np.exp(-X))
#code end here
def gradientDescent(dataMatIn, classLabels):
alpha = 0.001 # 学习率,也就是题目描述中的 α
iteration_nums = 100 # 迭代次数,也就是for循环的次数
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).transpose()
m, n = np.shape(dataMatrix) # 返回dataMatrix的大小。m为行数,n为列数。
weight_mat = np.ones((n, 1)) #初始化权重矩阵
#iteration_nums 即为循环的迭代次数
#请在代码完善部分注意矩阵乘法的维度,使用梯度下降矢量化公式
#code start here
for i in range(iteration_nums):
X = dataMatrix.dot(weight_mat) #数据矩阵与权重矩阵点积,是预测值
gradient = dataMatrix.transpose().dot(sigmoid(X) - labelMat) #梯度
weight_mat -= alpha * gradient #权重更新
return weight_mat
#code end here
if __name__ == '__main__':
dataMat, labelMat = generate_data()
print(gradientDescent(dataMat, labelMat))
3.多分类

使用逻辑回归的思想来处理多分类问题,这种方法通常被称为 Softmax 回归 (Softmax Regression) 或多项逻辑回归 (Multinomial Logistic Regression)。
对于输入特征向量 \(X\),模型会为每个类别 \(k\) 计算一个分数(或称为 logit)。这个分数是通过输入 \(X\) 和该类别对应的权重向量 \(W_k\) 进行线性组合得到的。
\[z_k = W_k^T X + b_k\]其中:
- \(k = 1, 2, 3, 4\)(对应图中的 \(P_1\) 到 \(P_4\))。
- \(z_k\) 是输入 \(X\) 属于类别 \(k\) 的未归一化分数。
这对应了图中 X 经过 W 矩阵变换后的中间结果。
为了将这些分数 \(z_k\) 转换为符合概率要求的输出(即所有概率值在 \([0, 1]\) 之间,且总和为 1),模型使用 Softmax 函数。Softmax 函数是 Sigmoid 函数(用于二分类逻辑回归)在多分类上的泛化。
样本 \(X\) 属于类别 \(k\) 的概率 \(P_k\) 为:
\[P_k = P(Y=k|X) = \frac{e^{z_k}}{\sum_{j=1}^{K} e^{z_j}}\]这对应了图中 \(P_1, P_2, P_3, P_4\) 的输出,这些输出满足 \(\sum_{k=1}^{4} P_k = 1\)。
在 Softmax 回归中,我们通常使用交叉熵损失 (Cross Entropy Loss) 来衡量预测概率分布 \(P\) 与真实标签分布 \(Y\) 之间的差异,并指导模型的训练。
\[\mathcal{L}_{CE} = - \sum_{k=1}^{K} Y_k \log(P_k)\]- \(Y_k\) 是真实标签的 One-hot 编码(如果 \(X\) 属于类别 \(k\),则 \(Y_k=1\),否则为 0)。