You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SOM神经网络回归调参疑问:欠拟合有离群点或过拟合贴合原数据

SOM回归拟合x²函数的参数调优与离群点解决思路

我来帮你分析下当前用Somoclu做SOM回归时遇到的问题,以及对应的调优和解决思路:

问题本质拆解

你遇到的两个极端情况其实是SOM回归的典型问题:

  • 训练不足:神经元还没充分收敛到数据的真实分布,部分神经元因为拓扑位置(比如二维网格的角落)无法被样本覆盖,就会变成离群点;
  • 训练过度:神经元完全贴合原始样本点,失去了SOM原本的插值/泛化能力,变成了样本的“复制”。

而你想要的是能生成插值点的拟合结果,核心是让SOM的神经元既贴合数据分布,又保留连续的拓扑结构,避免极端情况。

一、参数调优方向

1. 调整SOM拓扑结构:改用一维SOM

你现在用的是50×50的二维SOM,但你的任务是拟合单变量函数(x→x²),属于一维回归场景。二维网格的神经元很多处于数据分布的“边缘区域”,很难被样本驱动,自然容易产生离群点。

建议:换成1×N的一维SOM(比如1×100),一维拓扑的神经元是线性排列的,天然对应x轴的连续分布,训练后神经元会按x值顺序排列,既容易拟合曲线,又能避免无意义的离群点。

2. 优化训练策略:用衰减式学习率与邻域半径

固定epochs的训练方式很容易走到欠拟合或过拟合的极端,应该用动态衰减的训练参数:

  • 学习率:初始用较大的学习率(比如0.5),让神经元快速向数据分布移动,然后通过指数衰减逐步降低学习率,最后用小学习率精细调整。Somoclu可以通过learning_rate和decay_function='exponential'实现;
  • 邻域半径:初始半径设置为覆盖大部分神经元(比如一维100节点的话,初始半径设50),然后逐步衰减到1,这样训练初期神经元整体调整,后期只优化局部,避免过度拟合到单个样本。

3. 调整训练轮次:不用遍历2-100,找最优收敛点

你不需要遍历所有epochs,建议先跑30-50轮衰减式训练,然后观察拟合效果:如果还有离群点,就增加10-20轮;如果已经过度拟合,就减少轮次,或者降低初始学习率。

二、离群点的处理方法

如果坚持用二维SOM,或者训练后还是有离群点,可以试试这些方法:

1. 训练后过滤离群神经元

计算每个神经元到原始样本的最小距离,过滤掉距离过大的点:

from scipy.spatial.distance import cdist

# 计算每个codebook点到原始数据的最小欧氏距离
distances = cdist(som_ncodebook, data)
min_distances = np.min(distances, axis=1)

# 设定阈值(比如取最小距离的95分位数),过滤离群点
threshold = np.percentile(min_distances, 95)
filtered_codebook = som_ncodebook[min_distances <= threshold]

# 绘制过滤后的结果
plt.plot(filtered_codebook[:,0], filtered_codebook[:,1], 'ob')
plt.plot(data[:,0], data[:,1], '.r')
plt.show()

2. 约束神经元移动范围

在训练过程中,每隔几轮就把距离样本过远的神经元重置到数据的均值附近,避免它们跑偏:

# 训练过程中加入约束
som = somoclu.Somoclu(n_columns=50, n_rows=50, data=data, initialization='pca')
data_mean = np.mean(data, axis=0)

for epoch in range(100):
    som.train(epochs=1, verbose=False)
    # 检查并重置离群点
    distances = cdist(som.codebook.reshape(-1,2), data)
    min_distances = np.min(distances, axis=1)
    outlier_idx = np.where(min_distances > np.percentile(min_distances, 99))[0]
    for idx in outlier_idx:
        row = idx // 50
        col = idx % 50
        som.codebook[row, col] = data_mean

三、代码优化建议

你的代码可以简化并优化,比如:

  • 替换过时的as_matrix()为to_numpy();
  • 扁平化codebook的逻辑可以简化,不用循环;
  • 一维SOM的示例代码:
import numpy as np
import matplotlib.pyplot as plt
import somoclu
import pandas as pd

# 读取数据
data = pd.read_csv('10_samples.txt', sep='\t', header=0).to_numpy()

# 改用1×100的一维SOM,配置衰减参数
som = somoclu.Somoclu(n_columns=100, n_rows=1, data=data, 
                      initialization='pca',
                      learning_rate=0.5, decay_function='exponential',
                      initial_radius=50, radius_decay_function='exponential')
som.train(epochs=50)

# 扁平化codebook并按x轴排序,方便绘制连续曲线
som_ncodebook = som.codebook.reshape(-1, 2)
sorted_codebook = som_ncodebook[np.argsort(som_ncodebook[:, 0])]

# 绘图对比
plt.plot(sorted_codebook[:,0], sorted_codebook[:,1], 'ob-', label='SOM拟合曲线')
plt.plot(data[:,0], data[:,1], '.r', markersize=10, label='原始样本点')
plt.legend()
plt.title('一维SOM拟合x²函数')
plt.savefig('som_fit_x2.png')
plt.close()

这样调整后,你应该能得到更平滑的插值拟合曲线,同时避免离群点和过度拟合的问题。

内容的提问来源于stack exchange,提问作者lelorrain7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:42:59