使用神经网络解决二分类问题:从回归到分类
使用神经网络解决二分类问题:从回归到分类
在前面的神经网络学习中,我们通常用模型解决回归问题,例如预测房价、销量、温度等连续数值。现在要进入另一个非常常见的机器学习任务:分类问题。
分类任务与回归任务在网络结构上有很多相似之处,例如仍然可以使用全连接层、激活函数、优化器和早停机制。但二者最大的区别在于:
- 输出层希望得到的结果不同
- 损失函数不同
- 评估指标不同
本文以二分类 Binary Classification为例,介绍神经网络如何完成分类任务。
1. 什么是二分类问题?
二分类指的是将样本划分到两个类别之一。
常见例子包括:
- 判断客户是否会购买商品
- 判断信用卡交易是否为欺诈
- 判断雷达信号是否探测到目标
- 判断医学检测结果是否显示患病
- 判断酒店订单是否会被取消
这些问题的共同点是:结果只有两种可能。
例如:
购买 / 不购买 欺诈 / 正常 有病 / 无病 取消 / 不取消 猫 / 狗在原始数据中,类别可能是字符串,例如:
Yes / No Dog / Cat good / bad但神经网络不能直接处理这些文本标签,因此需要将它们转换成数字标签。
例如:
df['Class']=df['Class'].map({'good':0,'bad':1})也就是:
good -> 0 bad -> 1这样模型才能学习输入特征与类别标签之间的关系。
2. 分类问题中的准确率 Accuracy
在分类任务中,最直观的评估指标是准确率 Accuracy。
准确率定义为:
accuracy = 预测正确的样本数 / 总样本数如果模型全部预测正确,则准确率为:
accuracy = 1.0例如有 100 个样本,模型预测对了 88 个,那么准确率就是:
accuracy = 88 / 100 = 0.88也就是 88%。
准确率适合用在类别比较均衡的数据集中。例如正样本和负样本数量差不多时,准确率通常是一个比较合理的指标。
但是,如果类别极度不均衡,准确率可能会产生误导。
例如,某疾病检测数据中:
99% 的人没有病 1% 的人有病如果模型永远预测“没有病”,它也能达到 99% 的准确率,但这个模型显然没有实际价值。
所以在实际项目中,除了准确率,还经常关注:
- Precision 精确率
- Recall 召回率
- F1-score
- ROC-AUC
- PR-AUC
- 混淆矩阵 Confusion Matrix
3. 为什么不能直接用准确率作为损失函数?
训练神经网络时,需要一个损失函数 Loss Function来告诉模型当前预测有多差。
在回归问题中,我们常使用:
- MAE:平均绝对误差
- MSE:均方误差
这些损失函数是连续、平滑变化的,适合梯度下降算法优化。
但准确率不适合作为损失函数。
原因是准确率是一个“计数比例”,它的变化是不连续的。
例如模型输出概率从:
0.51 -> 0.99如果真实标签是 1,这两个预测最终都被判定为类别 1,因此准确率没有变化。
但显然,0.99 比 0.51 更有信心,也更接近理想预测。
反过来,如果模型输出从:
0.49 -> 0.51只变化了一点点,但分类结果却从 0 变成了 1,准确率发生突变。
这种“不平滑”的特性不适合梯度下降算法。因此,分类任务通常使用另一种损失函数:交叉熵 Cross-Entropy。
4. 交叉熵 Cross-Entropy
对于分类任务,我们希望模型输出的是某个类别的概率。
例如对于二分类问题,模型可以输出:
0.8表示模型认为该样本属于类别 1 的概率是 80%。
如果真实标签是 1,那么这个预测比较好。
如果真实标签是 0,那么这个预测就很差。
交叉熵的核心思想是:
当模型给正确类别分配的概率越高,损失越小;
当模型给正确类别分配的概率越低,损失越大。
对于二分类问题,常用损失函数是:
binary_crossentropy其数学形式可以写成:
Loss=−[y⋅log(p)+(1−y)⋅log(1−p)] \text{Loss} = -\big[ y \cdot \log(p) + (1 - y) \cdot \log(1 - p) \big]Loss=−[y⋅log(p)+(1−y)⋅log(1−p)]
其中:
- yyy是真实标签,取值为 0 或 1
- ppp是模型预测为类别 1 的概率
- log\loglog是对数函数(通常指自然对数)
如果真实标签是 1,希望ppp越接近 1 越好。
如果真实标签是 0,希望ppp越接近 0 越好。
举个例子,真实标签为 1:
预测概率 p = 0.99 -> 损失很小 预测概率 p = 0.60 -> 损失较大 预测概率 p = 0.01 -> 损失非常大这就很好地惩罚了“自信但错误”的预测。
5. Sigmoid 函数:把输出变成概率
普通神经网络的 Dense 层输出可以是任意实数,例如:
-10, -2.5, 0, 3.7, 20但二分类任务需要的是概率,也就是范围在:
[0, 1]之间的数。
因此,在二分类模型的最后一层,通常使用sigmoid 激活函数。
Sigmoid 函数可以将任意实数映射到 0 到 1 之间:
sigmoid(x)=11+e−x \text{sigmoid}(x) = \frac{1}{1 + e^{-x}}sigmoid(x)=1+e−x1
它的特点是:
- 输入xxx很大时,输出接近 1
- 输入xxx很小时,输出接近 0
- 输入xxx为 0 时,输出为 0.5
因此它非常适合用来表示二分类概率。
例如:
layers.Dense(1,activation='sigmoid')这里的含义是:
- 输出层只有 1 个神经元
- 该神经元输出类别 1 的概率
- 使用 sigmoid 将输出限制在 0 到 1 之间
6. 如何从概率得到最终类别?
模型输出的是概率,而不是直接输出类别。
例如:
0.82表示模型认为样本属于类别 1 的概率是 82%。
通常会设置一个阈值 threshold,例如:
0.5判断规则为:
预测概率 < 0.5 -> 类别 0 预测概率 >= 0.5 -> 类别 1例如:
0.12 -> 0 0.48 -> 0 0.51 -> 1 0.93 -> 1Keras 中的binary_accuracy默认也使用 0.5 作为分类阈值。
不过在实际业务中,阈值不一定非要是 0.5。
例如在医学检测、欺诈检测等场景中,漏判代价很高,可能会降低阈值来提高召回率。
7. 示例:Ionosphere 数据集二分类
Ionosphere 数据集包含来自雷达信号的特征,任务是判断信号是否显示存在某种物体,还是只是空信号。
原始数据中的类别是:
good bad需要先映射为数字标签:
df['Class']=df['Class'].map({'good':0,'bad':1})8. 数据划分与归一化
示例中将数据划分为训练集和验证集:
df_train=df.sample(frac=0.7,random_state=0)df_valid=df.drop(df_train.index)含义是:
- 70% 数据作为训练集
- 剩余 30% 数据作为验证集
random_state=0保证结果可复现
然后进行归一化:
max_=df_train.max(axis=0)min_=df_train.min(axis=0)df_train=(df_train-min_)/(max_-min_)df_valid=(df_valid-min_)/(max_-min_)归一化后,特征值被缩放到大致 0 到 1 之间。
这样做的好处是:
- 加快模型收敛
- 避免某些数值范围过大的特征主导训练
- 提高梯度下降的稳定性
需要注意的是,验证集归一化时使用的是训练集的min_和max_,而不是验证集自己的统计值。
这是为了避免数据泄漏。
9. 构建二分类神经网络
模型结构如下:
fromtensorflowimportkerasfromtensorflow.kerasimportlayers model=keras.Sequential([layers.Dense(4,activation='relu',input_shape=[33]),layers.Dense(4,activation='relu'),layers.Dense(1,activation='sigmoid'),])这个模型包含:
- 输入层:33 个特征
- 隐藏层 1:4 个神经元,ReLU 激活
- 隐藏层 2:4 个神经元,ReLU 激活
- 输出层:1 个神经元,Sigmoid 激活
这里输出层使用:
layers.Dense(1,activation='sigmoid')是二分类模型的关键。
如果是回归任务,最后一层通常不使用 sigmoid。
如果是多分类任务,最后一层通常会使用 softmax。
10. 编译模型
模型编译代码如下:
model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['binary_accuracy'],)各参数含义如下:
optimizer=‘adam’
使用 Adam 优化器。
Adam 是深度学习中非常常用的优化算法,通常比普通 SGD 更容易训练,收敛速度也更稳定。
loss=‘binary_crossentropy’
使用二分类交叉熵作为损失函数。
这是二分类问题中最常见的选择。
metrics=[‘binary_accuracy’]
训练过程中同时记录二分类准确率。
注意:
loss用于模型优化metrics用于观察模型表现
模型真正优化的是binary_crossentropy,不是binary_accuracy。
11. 使用 Early Stopping 防止过拟合
训练代码中使用了早停机制:
early_stopping=keras.callbacks.EarlyStopping(patience=10,min_delta=0.001,restore_best_weights=True,)参数含义如下:
patience=10
如果验证集指标连续 10 个 epoch 没有明显改善,就停止训练。
min_delta=0.001
只有改善幅度大于 0.001,才认为是真的改善。
restore_best_weights=True
训练结束后,恢复到验证集表现最好的那一轮模型参数。
这个参数非常重要。
因为模型最后一轮的权重不一定是最好的,可能已经开始过拟合。启用该参数后,可以自动保留验证集表现最佳的模型。
12. 训练模型
训练代码如下:
history=model.fit(X_train,y_train,validation_data=(X_valid,y_valid),batch_size=512,epochs=1000,callbacks=[early_stopping],verbose=0,)这里设置了最多训练 1000 个 epoch,但由于使用了 Early Stopping,模型通常不会真的训练满 1000 轮。
参数解释:
X_train, y_train:训练数据validation_data:验证数据batch_size=512:每次用 512 个样本更新模型epochs=1000:最多训练 1000 轮callbacks=[early_stopping]:启用早停verbose=0:不显示训练过程日志
13. 查看训练曲线
训练完成后,可以将history.history转换为 DataFrame:
history_df=pd.DataFrame(history.history)然后绘制损失曲线:
history_df.loc[5:,['loss','val_loss']].plot()以及准确率曲线:
history_df.loc[5:,['binary_accuracy','val_binary_accuracy']].plot()这里从第 5 个 epoch 开始画图,是为了避开训练初期波动较大的阶段,让趋势更清晰。
输出示例:
Best Validation Loss: 0.3534 Best Validation Accuracy: 0.8857表示验证集上最好的结果为:
- 最佳验证损失:0.3534
- 最佳验证准确率:0.8857
也就是模型在验证集上的准确率约为 88.57%。
14. 二分类模型的标准配置
对于神经网络二分类任务,常见配置可以总结如下:
model=keras.Sequential([layers.Dense(若干神经元,activation='relu',input_shape=[特征数量]),layers.Dense(若干神经元,activation='relu'),layers.Dense(1,activation='sigmoid'),])model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['binary_accuracy'],)核心要点是:
| 任务类型 | 输出层 | 激活函数 | 损失函数 |
|---|---|---|---|
| 回归 | 1 个或多个神经元 | 通常无激活 | MAE / MSE |
| 二分类 | 1 个神经元 | sigmoid | binary_crossentropy |
| 多分类 | 类别数量个神经元 | softmax | categorical_crossentropy / sparse_categorical_crossentropy |
15. Sigmoid 与 Softmax 的区别
二分类通常使用:
Dense(1,activation='sigmoid')多分类通常使用:
Dense(num_classes,activation='softmax')二者区别如下:
Sigmoid
适合二分类或多标签分类。
输出每个类别独立成立的概率。
例如多标签任务:
一张图片可以同时包含:猫、狗、车每个标签可以独立为真。
Softmax
适合单标签多分类。
输出所有类别的概率分布,且概率和为 1。
例如:
一张图片只能是:猫 / 狗 / 鸟 中的一类16. 实践中的注意事项
1. 类别标签必须是数字
神经网络不能直接使用字符串类别,需要先编码:
good->0bad->12. 特征需要归一化
对于神经网络,数值归一化通常很重要,可以提升训练稳定性。
3. 验证集不能参与训练统计
归一化参数应该从训练集计算,再应用到验证集和测试集。
4. 准确率不是万能指标
类别不平衡时,准确率可能误导判断。此时应该结合 Precision、Recall、F1、AUC 等指标。
5. 阈值可以根据业务调整
默认阈值是 0.5,但实际项目中可以根据业务需求调整。
例如:
- 想减少漏检:降低阈值
- 想减少误报:提高阈值
17. 总结
本文介绍了如何使用神经网络解决二分类问题。
核心知识点如下:
- 二分类问题的目标是将样本分为两个类别之一
- 原始类别标签需要转换为 0 和 1
- 准确率可以用于评估模型,但不适合作为损失函数
- 二分类常用损失函数是
binary_crossentropy - 输出层通常使用
sigmoid激活函数 - Sigmoid 可以将模型输出转换为 0 到 1 之间的概率
- 默认情况下,概率大于等于 0.5 判为类别 1,否则判为类别 0
- Adam 优化器同样适用于分类任务
- Early Stopping 可以减少过拟合并节省训练时间
一句话概括:
二分类神经网络的典型组合是:
sigmoid输出概率,binary_crossentropy作为损失函数,binary_accuracy作为评估指标。
完整流程可以概括为:
准备数据 -> 标签编码 -> 划分训练集和验证集 -> 特征归一化 -> 构建神经网络 -> 输出层使用 sigmoid -> 使用 binary_crossentropy 编译 -> 训练模型 -> 查看 loss 和 accuracy 曲线 -> 根据验证集表现评估模型掌握了这一流程之后,就可以将类似方法应用到更多实际二分类任务中,例如客户流失预测、订单取消预测、欺诈检测和医学辅助诊断等场景。
