自定义激活函数的逻辑回归模型:yHat维度异常与损失上升问题
看起来你遇到了两个棘手的问题:测试时预测结果维度突变,以及训练损失不降反升。我帮你拆解一下问题根源,再给出具体的修复方案:
一、先搞定维度不匹配的核心问题
你提到训练时A是(1000,),但测试时变成了(1,200),根本原因出在激活函数的条件判断逻辑和梯度计算的维度错误上:
1. 激活函数的全局判断是致命错误
你的activation_function里用了z.all < 0这种全局判断——z.all()会检查整个数组的所有元素是否满足条件,返回一个布尔标量。比如只要z里有一个元素大于0,整个数组就会进入下一个分支,这完全不是你想要的逐元素判断逻辑!
把激活函数改成向量化的逐元素处理:
def activation_function(z): global alpha # 用np.where实现逐元素的区间判断 a = np.where(z < 0, -alpha * z, np.where((z >= 0) & (z <= 1), (3 * z**3) - (4 * z**2) + (2 * z), z)) return a
这样不管z是(N,)还是其他形状,输出的a都会和输入z保持一致的维度,避免突变。
2. 梯度计算的维度完全错了
你的activation_function_prime里计算dfdw时犯了低级错误:比如当z在0-1区间时,2 * x是(D,N)维度(x是特征×样本数),但前面的np.dot(x, np.square(a))是(D,)维度,两者相加会导致dfdw变成(D,N),而不是和w同形状的(D,)。这会让训练过程中w的形状被错误修改,后续测试时np.dot(w, x)的结果自然就变成了(1,N)。
重新设计梯度计算逻辑,先算激活函数的导数,再推导正确的梯度:
def activation_function_prime(z): global alpha # 逐元素计算激活函数的导数 g_prime = np.where(z < 0, -alpha, np.where((z >=0) & (z <=1), 9*z**2 -8*z +2, 1)) return g_prime # 然后在f函数里替换原来的梯度计算部分: z = np.dot(w, x) + b a = activation_function(z) g_prime = activation_function_prime(z) # dfdw是(D,),和w形状一致 dfdw = np.dot(x, g_prime) # dfdb是标量,对应b的梯度 dfdb = np.sum(g_prime)
这样梯度维度完全正确,训练时w不会变形,测试时z和a的形状就会和训练时保持一致(都是(N,)),就能和标签正常比较了。
二、解决损失持续上升的问题
损失不降反升,本质是梯度计算错误导致权重更新方向完全相反,再加上可能的超参数问题:
1. 确保梯度下降的更新方向正确
修复梯度后,要确认权重更新是梯度下降:
# 正确的更新方式:减去学习率×梯度 w = w - learning_rate * dfdw b = b - learning_rate * dfdb
如果之前写的是w = w + learning_rate * dfdw,那就是梯度上升,损失必然暴涨。
2. 检查损失函数的正确性
二分类任务建议用交叉熵损失,确保计算逻辑没问题:
def compute_loss(y, a): # 避免log(0)的情况,加个极小值 epsilon = 1e-10 loss = -np.mean(y * np.log(a + epsilon) + (1 - y) * np.log(1 - a + epsilon)) return loss
3. 调整超参数优化收敛效果
如果修复后损失稳定在0.51左右,可以尝试这些调整:
- 归一化数据:逻辑回归对特征尺度敏感,先把X标准化到均值0、方差1,再训练。
- 调整学习率:尝试从0.1降到0.01、0.001,或者用学习率衰减策略。
- 优化alpha参数:alpha控制负区间的激活斜率,试试0.01、0.1等不同值,看是否能提升模型拟合能力。
修复后的预期效果
调整后,训练和测试时的z、a都会保持(N,)的形状,比如测试时是(200,),能和标签正常匹配;训练损失会开始稳步下降,最终收敛到更低的数值。
内容的提问来源于stack exchange,提问作者Erdem Uysal

