单神经元神经网络适配自定义数据集失败,求助排查问题
单神经元网络适配自定义数据集失败的问题
问题描述
用numpy构建的单神经元网络在示例二分类数据集(输入[[0,0,1],[1,1,1],[1,0,1],[0,1,1]]、输出[[0,1,1,0]])上能正常工作,但切换到自定义数据集后完全无法适配。尝试过输入归一化但无效,训练后预测输出的数值关系和真实输出完全不符:比如真实输出中1小于2,但预测结果出现0.99>0.98的情况。
自定义数据集与代码如下:
import numpy as np import pandas as pd def sigmoid(x): return 1 /(1+np.exp(-x)) def sigmoid_derivative(x): return x*(1-x) training_inputs = np.array([ [1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [0, 1, 1], [1, 1, 1], [2, 0, 0] ]) training_outputs = np.array([[1,2,0,1,0,2,3]]).T np.random.seed(1) synaptic_weights = 2 * np.random.random((3,1))-1 print('Random starting synaptic weights: ') print(synaptic_weights) for iteration in range(2000): input_layer = training_inputs outputs = sigmoid(np.dot(input_layer, synaptic_weights)) error = training_outputs - outputs adjustments = error * sigmoid_derivative(outputs) synaptic_weights += np.dot(input_layer.T,adjustments) print('Synaptic Weights After Training: ') print(synaptic_weights) print('Outputs after training: ') print(outputs)
训练后预测输出:
[0.99869975] [0.98680038] [0.00212543] [0.99998259] [0.13736242] [0.99189007] [0.9999983 ]
真实输出:
[[1,2,0,1,0,2,3]]
核心问题分析
- 输出范围不匹配:sigmoid函数的输出区间固定为
(0,1),但你的真实输出包含0、1、2、3,超出了sigmoid的输出上限。网络只能把所有大于1的目标值压缩到接近1的范围,自然无法区分2和3、1和2的大小关系。 - 任务类型混淆:示例数据集是二分类任务(输出仅0/1),sigmoid刚好适配;但自定义数据集是回归任务(输出连续数值且范围超过
(0,1)),用sigmoid作为输出激活函数完全不合适。 - 归一化不完整:你只做了输入归一化,但没对输出做处理,导致输出层根本无法映射到目标数值范围。
解决方案(仅用numpy+pandas)
方案1:适配回归任务的网络调整(推荐)
- 替换输出激活函数:回归任务用线性激活(直接输出加权和)即可,不需要非线性激活函数;同时调整反向传播的导数计算(线性激活的导数为1)。
- 输出归一化+反归一化:先把真实输出缩放到
(0,1)区间,训练后再反归一化还原到原始数值范围。 - 加入学习率:避免权重更新幅度过大导致震荡,提升收敛稳定性。
修改后的代码示例:
import numpy as np # 线性激活函数(回归任务专用) def linear(x): return x # 线性激活的导数为1 def linear_derivative(x): return np.ones_like(x) training_inputs = np.array([ [1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [0, 1, 1], [1, 1, 1], [2, 0, 0] ]) # 原始输出 training_outputs_raw = np.array([[1,2,0,1,0,2,3]]).T # 输出归一化到(0,1)区间 output_min = training_outputs_raw.min() output_max = training_outputs_raw.max() training_outputs = (training_outputs_raw - output_min) / (output_max - output_min) np.random.seed(1) synaptic_weights = 2 * np.random.random((3,1)) - 1 print('初始随机权重: ') print(synaptic_weights) # 回归任务需要更多训练轮次 for iteration in range(10000): input_layer = training_inputs # 用线性激活输出加权和 outputs = linear(np.dot(input_layer, synaptic_weights)) error = training_outputs - outputs # 线性激活导数为1,调整项直接等于误差 adjustments = error * linear_derivative(outputs) # 加入学习率0.01,控制权重更新幅度 synaptic_weights += np.dot(input_layer.T, adjustments) * 0.01 print('训练后权重: ') print(synaptic_weights) # 反归一化,还原到原始输出范围 predictions = outputs * (output_max - output_min) + output_min print('训练后预测输出(反归一化后): ') print(predictions) print('真实输出: ') print(training_outputs_raw)
方案2:坚持用sigmoid(不推荐)
如果一定要保留sigmoid,必须把输出严格缩放到(0,1)区间,比如training_outputs = training_outputs_raw / 3(因为最大输出是3),训练后再乘以3还原。但这种方式会因为sigmoid的非线性特性,导致中间值拟合效果远不如线性激活。
额外优化建议
- 输入归一化:把输入中的值(0、1、2)也缩放到
(0,1)或(-1,1)区间,帮助网络更快收敛。 - 调整学习率:可根据收敛情况微调学习率(比如0.005或0.02),找到最优更新幅度。
内容的提问来源于stack exchange,提问作者Ahmet Rauf Oktay
相关产品推荐
相关产品推荐

