A17

神经网络入门

点击正文段落选定收藏或划线位置;未选定时使用屏幕内第一段。

课内目录

1 为什么需要非线性模型

假设你要预测电信客户是否会在下个月流失。每行数据代表某个时点的客户,可用特征包括入网月数、月费用、合同类型、是否开通技术支持;标签表示随后观察窗口内是否流失。模型应在结果尚未发生时给出风险分数,帮助工作人员安排有限的回访名额。这里的任务不是背下历史客户,而是对尚未见过的客户作出有用判断。若输入包含解约日期,历史准确率再高也无法支持提前预测。

可以先用逻辑回归建立基线:把每个特征乘以权重后相加,再转成介于 0 与 1 之间的分数。它容易训练,也便于检查方向,但在原始特征空间里,分类边界受到线性形式的限制。实际关系可能更复杂:月费用较高不必然意味着流失,短期合同、较短入网时间与高费用同时出现时,风险才可能明显上升。人工加入交互特征可以补充这种关系,神经网络则提供了从数据中学习组合的另一条路径。

理解非线性可以从 2 个二值特征开始。令输入分别表示某条短消息是否出现“免费”和“官方”。假设在某个教学任务中,恰好出现其中 1 个词时标为正类,同时出现或同时不出现时标为负类。这个人为构造的异或问题,无法用原始平面上的 1 条直线完全分开。它说明表达形式存在边界,却不能证明真实消息就遵循这个规则。真实分类必须由标注规范、独立样本和实际指标来验证。

神经网络适合讨论这种“特征怎样组合”的问题,但并非所有数据都需要复杂网络。对中小规模表格数据,树模型常常是强基线;对图像,邻近像素和局部形状提供了可利用的结构;对短文本,词频与线性分类器也可能很有效。选择模型前,先写清预测对象、可用信息、错误代价与评估方式。模型名称不能代替任务定义,训练得动也不等于值得采用。

本课用公开电信流失表格数据说明完整流程,并借助 Fashion-MNIST 与公开短信垃圾分类数据解释迁移边界。前者的像素是输入,服饰类别是标签;后者可从词频特征开始,标签为垃圾短信或正常短信。它们的特征含义、切分方法和错误后果不同,不能把某次实验的分数直接拿来比较任务难度。

任务、数据、网络与评估的关系
任务、数据、网络与评估的关系

2 感知机到多层网络

感知机可以看作最简单的计算单元:接收数值输入,把输入与权重逐项相乘,再加上偏置,最后根据结果作判断。若输入是入网月数与月费用,权重决定每项对当前单元的影响,偏置决定整体偏移。这里的权重不是业务因果结论;即使某个权重为正,也不能据此断言提高费用必然造成流失。相关特征、编码方法与训练样本都会影响它的数值。

现代分类网络通常保留“加权求和加偏置”的骨架,再接上可训练系统需要的激活函数。用简写表示某层计算,就是 z = Wx + b,h = φ(z)。x 是该层输入,W 是权重集合,b 是偏置,φ 表示激活函数。不要急着记矩阵乘法细节:对当前课程,先把它读成“每个单元读取上一层的数值,分别加权,再产生新的数值表示”。同层单元能够学习不同的组合。

把这些单元组成隐藏层,再连接输出层,就得到多层感知机,通常简称 MLP。隐藏层并没有可直接观测的标准答案,它产生的中间表示由最终任务的训练信号共同调整。某个单元可能对某组输入更敏感,但不应在没有证据时给它命名为“忠诚度神经元”。模型内部数值不自动对应人类概念,解释仍需额外分析。

例如,经过缺失值处理、数值标准化和类别独热编码后,某次实验得到 30 维输入。使用 16 个隐藏单元和 1 个输出单元时,首层有 30 × 16 个权重与 16 个偏置,输出层有 16 个权重与 1 个偏置,总计 513 个可训练参数。这个数字来自当前编码结果;类别数量变化时,输入维度与参数量也会变化。它不包含预处理器保存的均值、尺度或类别表。

前向计算指输入沿着这些层得到预测的过程。训练还要根据预测与标签之间的差距调整参数,但本课先观察前向计算的含义。批量处理时,网络同时接收多行样本,每行仍然对应自己的输出。隐藏层宽度、网络层数与每批样本数是不同概念;把批量大小从 32 改为 64,并不会把网络的隐藏单元数量翻倍。

3 激活函数与表达能力

为什么隐藏层后面要有激活函数?如果连续堆叠的每层都只有加权求和与偏置,那么无论层数多少,最终仍可合并成原始输入上的另 1 次仿射变换。中间数值虽然变多了,分类边界的基本限制却没有消失。非线性激活让网络能形成更丰富的中间表示,使后面的层有机会组合出弯曲或分段的边界。

常见的 ReLU 规则很直接:输入大于 0 时保留,输入小于或等于 0 时输出 0。它在各段上很简单,多个单元组合后却能产生复杂关系。这里的 0 是计算规则,不表示某项业务事实不存在。隐藏单元在某个样本上输出 0,也不等于该样本被删除。网络仍会继续处理其他单元给出的数值。

二分类输出常使用 sigmoid,将末层的实数转为 0 到 1 之间的分数。以 0.5 为阈值可得到类别判断,但这只是默认选择。若回访容量有限,可以在验证集上研究不同阈值下的 precision 与 recall:precision 表示预测流失的人中实际流失的比例,recall 表示所有实际流失的人中被找出的比例。阈值改变会改变名单,通常也会改变这 2 个指标。

F1 是 precision 与 recall 的调和平均,便于概括某个阈值下的表现,却不会反映所有业务成本。ROC-AUC 衡量分数对正负样本的排序能力,不依赖某个固定阈值;在正类稀少的场景,还应查看 PR-AUC 及其计算约定。高 AUC 不代表输出 0.8 的样本中必有 80% 流失,也不保证某个回访阈值下的 precision 足够高。概率校准属于后续可进一步检查的内容。

对于 Fashion-MNIST 的 10 类服饰分类,输出可以改成 10 个分数,再经 softmax 形成总和为 1 的类别分布。预测取最高分对应的类别,训练时比较整组输出与真实标签。二分类与多分类的输出形式、损失和指标实现需要配套,不能因为都叫“分类”就直接复制。使用库时,还要确认损失接收原始分数还是已经变换后的概率,避免重复施加 sigmoid 或 softmax。

表达能力表示模型能够描述哪些关系,不保证训练一定找到合适关系,更不保证它能推广到新数据。增加隐藏单元可能改善欠拟合,也可能只记住训练样本中的偶然模式。学习曲线和独立验证比“层数更多”更能说明变化是否有用。下面的实验因此从小网络开始,每次只改变少量明确因素。

输入经过非线性隐藏层形成分类分数
输入经过非线性隐藏层形成分类分数

4 动手:搭一个小 MLP 做分类

选择公开的 Telco Customer Churn 表格作为练习,下载后记录来源、版本或文件摘要,并查看字段说明。把 Churn 映射为 0 与 1,去掉 customerID 这类标识列;如果总费用字段含空白字符串,先转换为缺失值,再交给数值预处理。不要把标签、预测之后才产生的字段,或由标签直接推算的字段放入输入。该数据更适合演示分类流程,不能仅凭字段名认定它满足真实的未来流失预测口径。

先切分数据,再拟合预处理。教学实验可将样本分为 60% 训练集、20% 验证集与 20% 测试集,按标签分层以尽量保留类别比例,并固定随机种子。若同个客户有多条记录,应按客户分组切分;若实际目标是预测未来,应优先采用按时间向后的切分。分层随机切分只适用于样本独立性和任务设定允许的情况,不能替代对数据生成过程的判断。

数值列可包含 tenure、MonthlyCharges 与 TotalCharges,先用训练集的中位数填补缺失,再按训练集均值与标准差缩放。类别列可包含 Contract、InternetService 与 TechSupport,填补缺失后独热编码,并允许验证或测试数据出现未知类别。独热编码会为类别生成不同位置的指示值,避免把合同类型误当成具有大小和等距关系的连续数值。具体列名以所用文件为准。

下面的 Python 示例展示训练骨架。假定 X 已按字段说明选出合法特征,y 是二分类标签,numeric_cols 与 categorical_cols 已明确列好。代码使用 scikit-learn 的预处理管道,确保每次 fit 时统计量来自训练数据。为了让验证集的用途清晰,这里关闭模型内部早停,训练结束后再用独立验证集比较配置。默认最大迭代次数只是上限,出现收敛警告时应记录并检查原因。

from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import precision_score, recall_score, f1_score, roc_auc_score

X_dev, X_test, y_dev, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=17)
X_train, X_val, y_train, y_val = train_test_split(
    X_dev, y_dev, test_size=0.25, stratify=y_dev, random_state=17)
numeric = Pipeline([
    ("fill", SimpleImputer(strategy="median")),
    ("scale", StandardScaler())])
categorical = Pipeline([
    ("fill", SimpleImputer(strategy="most_frequent")),
    ("encode", OneHotEncoder(handle_unknown="ignore", sparse_output=False))])
prepare = ColumnTransformer([
    ("num", numeric, numeric_cols),
    ("cat", categorical, categorical_cols)])
model = Pipeline([
    ("prepare", prepare),
    ("network", MLPClassifier(
        hidden_layer_sizes=(16,), activation="relu", solver="adam",
        learning_rate_init=0.001, alpha=0.001, batch_size=32,
        max_iter=200, early_stopping=False, random_state=17))])
model.fit(X_train, y_train)
score = model.predict_proba(X_val)[:, 1]
pred = (score >= 0.5).astype(int)
print({"precision": precision_score(y_val, pred, zero_division=0),
       "recall": recall_score(y_val, pred, zero_division=0),
       "f1": f1_score(y_val, pred, zero_division=0),
       "roc_auc": roc_auc_score(y_val, score)})

这段代码需要安装兼容版本的依赖,sparse_output 参数适用于较新的 scikit-learn。示例没有实际运行结果,不应把参数值当成推荐最优配置。先加入始终预测多数类的朴素基线,再训练逻辑回归,最后比较 MLP。保留相同切分和评价函数,记录训练耗时与警告。测试集先封存,待模型、预处理与阈值选择完成后再使用,以免反复查看结果变成隐性调参。

若要选阈值,在验证集上比较若干候选值,并记录选择标准,例如“recall 至少达到 0.70 时取 precision 较高的阈值”。这是实验约束示例,是否合理取决于实际成本和数据。确定后把同个阈值应用到测试集,报告混淆矩阵以及各指标,不能在看到测试结果后再偷偷调整。若任个切分缺少某类样本,还需先解决评估可行性,不能强行解释不存在定义条件的 AUC。

5 损失、容量与过拟合初识

训练过程需要可用于调整参数的目标,损失函数正是对预测误差的数值描述。二分类常用对数损失:给真实正类很低的概率,或给真实负类很高的概率,都会受到较大惩罚。它使用连续分数,因此预测类别暂时没变时,损失也可能下降。F1 则依赖阈值后的离散判断,损失下降与 F1 上升不必在每次迭代中同步。

容量可以粗略理解为模型拟合复杂关系的能力,隐藏单元和层数会影响容量,但参数量并非唯一尺度。若训练集与验证集都表现不佳,可能存在欠拟合,也可能是特征无信息、标签错误、训练未收敛或代码问题。不能只凭低分就加宽网络。先确认朴素基线、标签比例和预处理输出是否合理,再检查模型是否学到了任何稳定信号。

过拟合的典型迹象是训练损失持续下降,验证损失却停止改善甚至上升。模型可能正在拟合训练样本的细节,而这些细节无法迁移。可尝试减小网络、增加适当的参数惩罚、减少训练轮数,或收集更有代表性的训练数据。早停利用验证信号选择训练终点,因此也属于模型选择过程;不能用测试集来决定何时停止训练。

观察曲线时,要保证训练与验证使用相同的损失定义,并区分是否计入正则项。单个随机种子下的波动不必然说明容量有问题,验证集太小也可能导致曲线不稳。对少量候选配置,可以用预先约定的多个种子重复实验,报告均值与波动;不要只挑表现最好的种子。如果不断根据同个验证集尝试大量方案,也可能逐渐适应该验证集的偶然性。

类别不平衡会进一步干扰直觉。若正类只有 10%,始终预测负类就有 90% 准确率,却完全找不到正类。需要同时看标签占比、precision、recall、F1 和混淆矩阵。若使用过采样或其他重平衡方法,只能在训练部分进行;先复制少数类样本再切分,会让相同或近似记录进入不同集合,造成泄漏。验证与测试应尽量保留目标使用环境中的自然比例。

训练与验证证据如何支持容量判断
训练与验证证据如何支持容量判断

6 常见坑:学习率、初始化、未标准化输入

学习率控制参数调整的步幅。过大时,损失可能来回震荡、快速变大,甚至出现非有限数值;过小时,固定训练预算内的进展可能很慢。但曲线异常并不只由学习率引起,错误标签、极端值、重复概率变换和数据类型问题也可能产生相似症状。排查时先确认输入和损失是否有限、标签是否符合要求,再在小范围内比较学习率,例如 0.0001、0.001 与 0.01。

初始化提供训练起点。同层单元若采用完全相同的不当初始化,可能无法学到不同表示;实践中通常使用框架为对应层和激活提供的初始化方式。不同随机种子也可能得到不同结果,尤其在数据较少时。初学阶段先记录并固定种子以便复现,再用少量重复训练判断结论是否稳定。不要把单次幸运结果说成模型必然优于基线,固定种子也不保证不同硬件与软件版本逐位一致。

未标准化输入是表格 MLP 的常见问题。入网月数可能在几十的范围,总费用可能达到几千,不同量纲会让优化过程更难调节。标准化把数值列调整到可比较的尺度,但不消除异常值,也不保证特征符合正态分布。验证集必须使用训练集保存的均值与标准差;如果分别计算各集合的统计量,输入含义便不再一致。先对全量数据拟合缩放器,也会让评估受到外部信息影响。

对图像也要检查数值范围:Fashion-MNIST 常见原始像素为 0 到 255,可按固定规则缩放到 0 到 1。展平的 MLP 会把每个像素作为输入,适合展示通用网络接口,但没有主动利用邻近像素的空间结构。对短文本,词表与词频变换也只能在训练集拟合;若同条消息的轻微改写分散到训练与测试中,分数可能高得不真实。特征处理必须随任务调整。

另种更隐蔽的失败是分布偏移。历史流失样本可能来自旧资费方案,新用户却使用新合同;垃圾短信措辞也会随时间改变。此时训练正常、离线测试正常,未来效果仍可能下降。可以按时间、合同类型或来源分组查看样本量与错误,比较输入缺失率及类别比例。发现变化后应重新检查数据与任务假设,不能仅靠增加训练轮数来修复。

从异常现象回到数据与训练设置的排查路径
从异常现象回到数据与训练设置的排查路径

7 案例:浅层网络打不过强树基线

设想在同份流失数据上,团队比较逻辑回归、含 16 个隐藏单元的 MLP,以及经过合理调参的梯度提升树。这里讨论的是教学案例,下面的数值为构造示例,不是公开数据集上的实测成绩。假设验证集上逻辑回归的 ROC-AUC 为 0.82,MLP 为 0.83,树模型为 0.86。可以提出“树模型值得优先考虑”的候选结论,但还不能据此宣称它对未来客户必然更好。

先检查比较是否公平。各模型应使用相同的合法字段、切分与标签口径,预处理都只在训练集拟合。树模型可以使用适合自己的编码方式,MLP 使用标准化也合理;公平并不意味着强迫全部模型接受相同的不合适处理。调参预算需要大致可比,并记录每个模型尝试了多少配置。若树模型调了几10次,而网络只使用默认值,结论应限制为当前配置与预算下的表现。

为什么浅层网络可能落后?表格任务常含阈值关系、类别组合和缺失模式,树模型擅长逐步按条件划分。小规模样本也未必足以稳定学习网络的全部参数。这是对结果的可能解释,需要用实验支持;不能把“树适合表格”变成无条件定律。网络也可能因为错误缩放、未收敛或不合适的训练设置而落后,应先排除明显问题。

接着查看目标阈值下的证据。假设在各自验证集选定的阈值上,树模型的 recall 为 0.72、precision 为 0.61,MLP 的 recall 为 0.70、precision 为 0.56;仍要同时查看实际人数和置信波动。对 20 个正类样本算出的 recall,与对 2000 个正类样本算出的相同数值,支持结论的力度不同。回访名单容量、误报成本和运行耗时也会影响最终选择。

最后冻结选择流程,再在封存测试集评估。若树模型优势在合理重复实验中稳定,而且部署与维护成本可接受,采用树模型就是成功完成任务。学习神经网络不意味着必须让它获胜。若网络只在较早月份有效,在较晚月份明显下降,则应记录时间差异并检查分布偏移;若差距很小且波动较大,诚实的结论应是目前证据不足以区分,而非选最高的某次小数。

8 把网络假设写进实验记录

好的实验记录让另个人能判断“结果来自什么条件”,也让未来的自己知道为何尝试某个配置。先写任务定义:每行代表谁,预测时点是什么,标签在多长观察窗口后确定,哪些信息在预测时可用。对公开静态数据,若无法恢复真实时间线,要明确这是教学分类评估。不要把缺失的业务条件写成已经得到验证的事实。

再写可被检验的模型假设。例如:“合同类型与入网月数存在有用交互,含 16 个 ReLU 隐藏单元的 MLP 可能比逻辑回归改善验证集排序。”这比“神经网络能够提取高级特征”更容易检验。配套记录否定条件:若相同切分下 AUC 改善小于重复训练波动,且目标 recall 下 precision 无稳定提升,就不认为当前证据支持增加网络复杂度。

数据记录应包括来源、获取日期、版本摘要、行数、类别比例、去重规则和切分方法。模型记录包括预处理列清单、隐藏层、激活、优化器、学习率、批量大小、训练上限、正则强度、种子与依赖版本。结果记录包括训练状态、验证指标、阈值选择规则、混淆矩阵、耗时和失败案例。参数名应对应实际库,不能把某库中的惩罚系数直接视为所有框架的同义参数。

失败案例可以按合同类型、入网时长区间、文本来源或服饰类别分组查看,但同时报告样本量。某组只有很少样本时,几次错误就可能让比例大幅变化。检查这些分组是为了提出可检验的新假设,不应在看完测试集后不断修改规则又继续称它为独立测试。需要新轮次开发时,应清楚声明原测试集已经参与分析,并重新安排可信的最终评估。

最后写出决策和适用边界,例如“在当前静态样本与预算下保留树模型,MLP 作为学习与后续比较基线;尚未验证跨月份稳定性”。这类记录保留了可用结论,也保留了未知部分。实验结束不一定意味着模型上线;如果标签含义不清、预测字段不可获得,或者错误代价无法接受,应先修正任务与数据,而不是继续追求更高分数。

9 从前向计算走向反向传播(A18)

现在可以把神经网络理解成具有可调参数的连续计算过程:特征经过加权求和、非线性变换和输出映射,形成预测,再由损失衡量它与标签的差距。前向计算告诉我们“当前参数会算出什么”,却没有回答“怎样知道每个参数该改多少”。如果参数有几百个甚至更多,逐个随意试探会非常低效,也难以稳定改善总体目标。

A18 将从这个缺口继续,引入梯度与反向传播:沿计算过程追踪损失对参数变化的敏感程度,再配合优化方法调整参数。本课暂不展开链式法则推导、优化器内部状态或复杂网络结构。先保留直觉:反向传播负责高效计算梯度,优化器依据梯度更新参数,二者各有职责;训练还需要数据切分、损失选择和完整的实验约束共同成立。

进入下一课前,应能独立说清 3 件事:当前任务的输入和标签是什么,隐藏层为何需要非线性,以及为什么验证集比训练集成绩更能帮助选择模型。还应知道,反向传播可以让参数更好地拟合训练目标,却不会自动识别标签泄漏、修正错误的预测时点或消除分布偏移。计算方法越有效,越需要保证目标与证据可信。

放进方案里

在方案中先把神经网络写成待验证的候选模型,并明确预测对象、预测时点与可用字段。例如,流失分类使用入网月数、月费用、合同类型和技术支持状态,排除客户标识与解约后字段。若公开数据缺乏时间信息,应说明实验只验证静态分类能力,不能直接承诺未来流失预测效果。数据来源、版本、标签定义和个人信息处理范围也要有明确记录。

实验安排采用 60% 训练、20% 验证、20% 测试的教学切分;存在重复主体或时间顺序时,改用符合任务的分组或时间切分。填补、缩放与编码只在训练集拟合。比较多数类基线、逻辑回归、小型 MLP 与强树基线,预先限定调参预算。MLP 从 16 个隐藏单元开始,记录学习率、训练轮数、种子、收敛状态及耗时,使结果能够复查。

评价同时报告 precision、recall、F1、ROC-AUC、正类比例与混淆矩阵。根据回访能力和错误代价,在验证集确定阈值,再用封存测试集完成最终检查。若类别稀少,补充 PR-AUC 并写明计算方法;若关键分组样本不足,应把不确定性列为限制。所有示意数值都标为示例,方案中的收益结论必须由实际实验支持。

决策条件应覆盖效果与维护成本:只有网络相对基线获得稳定且有实际意义的改善,才继续评估采用价值。若树模型更好,就保留树模型并记录原因。若发现数据泄漏,相关分数作废并重新评估;若发现时间分布变化,则补充较晚时段的验证。交付物包括数据与切分说明、可复现管道、配置表、阈值规则和失败案例,让模型选择成为可审查的证据,而非对某种架构的偏好。

10 小结:下一步反向传播(A18)

神经网络通过隐藏层与非线性激活学习特征组合,小型 MLP 足以帮助理解这条计算链。模型能表达复杂关系,并不保证它在当前数据上优于简单基线。可信的结论依靠合法特征、独立切分、训练内预处理、合适指标与对失败原因的检查。

试着口头回答:去掉全部隐藏层激活后,多层加权计算为何仍有局限?准确率达到 90% 时,为何仍可能找不到任何正类?验证集选出的阈值为何应在测试时固定?若能结合具体输入和错误类型解释这些问题,就可以进入 A18,进一步学习损失如何通过反向传播影响每个参数。

同阶段相关课

AI 答疑

默认关闭。启用后,只发送本课标题、最近阅读的屏幕内正文摘录(最多 6000 字)与本次问题。

配置自己的服务