机器学习入门<4>RBFN算法详解

本文用生动的比喻和直观的可视化,带你一步步理解RBFN的核心思想,无需深厚数学背景也能掌握这一经典机器学习算法。

一、引入:从生物大脑到人工神经网络

1. 脑科学启示录:智慧的"社交网络"

你知道吗? 人脑拥有约860亿个神经元,它们相互连接形成一个极其复杂的网络。每个神经元都像一个有个性的朋友:

  1. 有些朋友活跃范围广(接收很多信息)

  2. 有些朋友只在特定话题上活跃(局部响应)

一个生动的比喻:

当有人说火锅时,四川籍的同学反应最强烈;

当有人说冰雪大世界时,东北同学会异常兴奋。

这就是局部响应的特性------不同的神经元对不同的刺激有着不同的敏感度。

连接到RBFN: 今天我们要学习的RBFN(径向基函数网络),正是模拟这种特定领域专家的神经网络!

2. 与之前知识的对比:从"全能选手"到"专业专家"

如果不了解逻辑回归,可以阅读我之前写的博客:https://blog.csdn.net/2303_77568009/article/details/155580110?spm=1001.2014.3001.5501

让我们先回顾一下之前学过的逻辑回归

  • 逻辑回归像一个全能型选手,对所有特征一视同仁

  • 它试图找到一个全局的决策边界

但现实世界往往更加复杂:

体温37.5°C 是发烧,但气温37.5°C是凉爽。

Apple在科技话题中指苹果公司,在水果店就是苹果。

我们需要一种能理解上下文和范围的智能模型------这就是RBFN!

二、核心概念:"局部专家"系统

1. RBFN的三层结构比喻:公司决策系统

想象一个公司的决策流程:

复制代码
  输入层   →  隐藏层  → 输出层
    ↓         ↓         ↓
信息收集员 → 部门专家 → CEO决策

具体来说:

  1. 输入层:市场信息收集员(收集原始数据)

  2. 隐藏层:各部门专家(财务部、技术部、市场部...)

  3. 输出层:CEO(综合专家意见做最终决策)

2. 核心:径向基函数------专家的"专业领域"

径向基函数就像每个专家的能力范围:

  • 中心点:专家的舒适区

    • 比如财务专家最擅长处理成本在100-200万的项目
  • 半径(σ):专家的知识范围

    • σ小 = 专精窄领域

    • σ大 = 知识面广但不深

  • 响应值:数据离专家中心越近,专家的发言权越大

三、工作原理:"专家会诊"流程

1. 完整流程故事化:医疗诊断系统

假设我们要构建一个医疗诊断系统,判断患者是否患有某种疾病。

步骤1:数据收集(输入层)

步骤2:专家诊断(隐藏层)

步骤3:加权决策(输出层)

2. 关键特性总结

  • 局部性:每个神经元只关心自己"辖区"内的事情

  • 可解释性:可以查看哪个"专家"贡献最大------比黑箱网络更透明

  • 插值能力:即使遇到没见过的情况,也能参考最近似专家的意见

四、对比与可视化:RBFN vs 其他网络

1. 直观对比演示

场景:二维平面上分类同心圆数据

下面我将使用Python实现逻辑回归和RBF神经网络(RBFN)对同心圆数据进行分类,并直观展示它们的不同表现。

  • 逻辑回归:试图画一条直线分开------失败!

  • RBFN:放置多个感应圈------成功分类!

关键洞察:RBFN通过多个局部区域组合,可以形成复杂的非线性边界。

2. 应用场景展示

  • 人脸识别:每个RBF神经元关注面部特定区域

    • 眼睛区域专家

    • 鼻子区域专家

    • 嘴巴区域专家

  • 股票预测:不同专家关注不同市场状态

    • 暴涨模式专家

    • 暴跌模式专家

    • 震荡市场专家

  • 语音识别:专家们各自擅长识别不同的音素或音节

五、动手体验:不用数学的理解

1. 互动游戏:你是RBFN!

游戏规则:

  1. 将教室(或想象的空间)划分成6个区域

  2. 每个区域有一个"专家",每个专家有自己的"专业领域"

  3. 展示物品描述,各"专家"根据接近程度打分(0-10)

  4. "CEO"综合所有专家意见做出最终判断

示例:

物品:"一个红色的金属圆形徽章"

  • 红色物体专家:9分

  • 金属物体专家:8分

  • 圆形物体专家:10分

  • 其他专家:低分或0分

在这里为了大家能够理解,我用python将上面的内容可视化了(代码在文章的末尾获取):

2. 极简代码,开箱即用

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.linear_model import LinearRegression
import warnings

warnings.filterwarnings('ignore')  # 忽略警告信息

# 设置中文字体,解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']  # 设置中文字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题


class SimpleRBFN:
    """简化的RBF网络实现(教学用)"""

    def __init__(self, n_centers=20, sigma=0.5):
        self.n_centers = n_centers
        self.sigma = sigma
        self.centers = None
        self.linear_model = LinearRegression()

    def _rbf_kernel(self, X, center):
        """径向基函数(高斯核)"""
        return np.exp(-np.sum((X - center) ** 2, axis=1) / (2 * self.sigma ** 2))

    def fit(self, X, y):
        # 1. 使用KMeans确定中心点
        kmeans = KMeans(n_clusters=self.n_centers, random_state=42)
        kmeans.fit(X)
        self.centers = kmeans.cluster_centers_

        # 2. 计算RBF特征
        rbf_features = np.zeros((len(X), self.n_centers))
        for i, center in enumerate(self.centers):
            rbf_features[:, i] = self._rbf_kernel(X, center)

        # 3. 训练线性回归层
        self.linear_model.fit(rbf_features, y)
        return self

    def predict(self, X):
        # 计算RBF特征
        rbf_features = np.zeros((len(X), self.n_centers))
        for i, center in enumerate(self.centers):
            rbf_features[:, i] = self._rbf_kernel(X, center)

        # 线性回归预测
        return self.linear_model.predict(rbf_features)


# 使用示例
np.random.seed(42)  # 设置随机种子,确保结果可重复
X = np.linspace(0, 10, 100).reshape(-1, 1)
y = np.sin(X).ravel() + np.random.normal(0, 0.1, 100)

# 创建并训练RBF网络
rbfn = SimpleRBFN(n_centers=20, sigma=0.5)
rbfn.fit(X, y)

# 预测
X_test = np.linspace(0, 10, 200).reshape(-1, 1)
y_pred = rbfn.predict(X_test)

# 可视化
plt.figure(figsize=(12, 8))

# 绘制原始数据和预测结果
plt.scatter(X, y, alpha=0.5, label='原始数据(带噪声)', s=30)
plt.plot(X_test, y_pred, 'r-', linewidth=3, label='RBFN预测结果')

# 添加网格线
plt.grid(True, linestyle='--', alpha=0.6)

# 添加图例和标题
plt.legend(fontsize=12)
plt.title('RBFN神经网络:从噪声数据中学习正弦函数', fontsize=16, fontweight='bold')
plt.xlabel('X轴', fontsize=12)
plt.ylabel('Y轴', fontsize=12)

# 添加注释说明
plt.text(0.5, 1.1, 'RBFN参数:20个中心点,σ=0.5',
         transform=plt.gca().transAxes,
         fontsize=11,
         verticalalignment='top',
         bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))

plt.tight_layout()
plt.show()

这段代码实现了一个简化版径向基函数网络(RBFN),用于从带噪声的数据中学习正弦函数的模式。整个实现分为三部分:网络结构定义、训练过程和可视化展示。

首先,代码定义了一个SimpleRBFN类,它采用三层结构设计:输入层接收数据,隐含层使用高斯径向基函数进行非线性变换,输出层通过线性回归完成最终预测。网络的中心点通过K-means聚类确定,高斯函数的宽度由sigma参数控制,这种结构使网络能够有效捕捉数据的局部特征和全局趋势。

其次,训练过程遵循三个关键步骤:使用K-means算法自动确定径向基函数的中心点位置;计算每个样本点到所有中心点的高斯核函数值,形成新的特征表示;最后用线性回归模型学习这些特征到目标值的映射关系。代码中使用了带噪声的正弦波数据作为示例,展示了网络对复杂非线性关系的拟合能力。

最后,代码通过可视化直观展示学习效果:原始数据点以散点图显示,RBFN的预测结果以平滑曲线叠加,清晰对比了噪声数据与网络恢复的函数形态。图中还标注了网络参数信息,完整呈现了从数据预处理、模型训练到结果评估的全过程,体现了RBFN在处理非线性回归问题上的实用性和有效性。

总而言之,这个代码做了一个有趣的事情:它用一个小型神经网络从一堆有干扰的数据点中,找出背后隐藏的真实规律。

具体来说,先生成了一组加了随机干扰的正弦波数据点,看起来比较杂乱;然后让一个RBF神经网络去学习这些数据,这个网络会先找出20个代表性的中心点,然后根据这些中心点来计算每个数据的位置特征;最后网络会找出这些特征与原始数据之间的关系,形成一个平滑的预测曲线。最终画图展示时,可以看到虽然原始数据点很分散,但神经网络还是成功还原出了清晰的正弦波形状。

参数调整实验:

  • 调整 n_centers(专家数量)

    • 太少 → 欠拟合(专家不够用)

    • 太多 → 过拟合(专家太多,各自为政)

  • 调整 width(专家知识范围)

    • 太窄 → 过于敏感(专家太专)

    • 太宽 → 过于模糊(专家知识面太广)

六、总结升华:RBFN的哲学

1. 核心思想总结

RBFN的精髓可以概括为三点:

  1. 分而治之:复杂问题分解为多个简单子问题

  2. 专业分工:每个神经元成为特定领域的专家

  3. 民主集中:局部意见通过加权形成全局决策

2. 与生物神经网络的联系

  • RBFN更接近真实大脑的工作方式

  • 大脑皮层中的"柱状结构"就是局部处理单元

  • 深度学习的卷积神经网络(CNN)也受此启发------局部感受野!

七、本文中绘图的代码

1.Python实现逻辑回归和RBF神经网络(RBFN)对同心圆数据进行分类,并直观展示它们的不同表现。

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.datasets import make_circles
from sklearn.linear_model import LogisticRegression
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# 生成同心圆数据
np.random.seed(42)
X, y = make_circles(n_samples=500, factor=0.5, noise=0.1, random_state=42)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 创建两个模型
# 1. 逻辑回归(线性分类器)
logistic_model = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000, random_state=42))

# 2. RBF神经网络(使用MLPClassifier模拟RBFN,设置合适的隐藏层和激活函数)
# 注意:这里使用MLPClassifier的tanh激活函数来模拟RBFN的效果
rbfn_model = make_pipeline(
    StandardScaler(),
    MLPClassifier(
        hidden_layer_sizes=(50,),  # 一个包含50个神经元的隐藏层
        activation='tanh',         # 使用tanh激活函数模拟径向基函数
        solver='lbfgs',
        max_iter=1000,
        random_state=42,
        alpha=0.01
    )
)

# 训练模型
logistic_model.fit(X_train, y_train)
rbfn_model.fit(X_train, y_train)

# 评估模型
logistic_train_acc = accuracy_score(y_train, logistic_model.predict(X_train))
logistic_test_acc = accuracy_score(y_test, logistic_model.predict(X_test))

rbfn_train_acc = accuracy_score(y_train, rbfn_model.predict(X_train))
rbfn_test_acc = accuracy_score(y_test, rbfn_model.predict(X_test))

# 创建可视化网格
h = 0.02  # 网格步长
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))

# 为每个模型计算决策边界
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

# 自定义颜色映射
cmap_light = ListedColormap(['#FFAAAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#0000FF'])

# 1. 原始数据
ax = axes[0]
ax.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], 
           c='red', marker='o', edgecolors='k', s=40, label='类别 0', alpha=0.7)
ax.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], 
           c='blue', marker='^', edgecolors='k', s=40, label='类别 1', alpha=0.7)
ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_title('原始数据分布\n(同心圆)')
ax.set_xlabel('特征 X1')
ax.set_ylabel('特征 X2')
ax.legend()
ax.grid(True, alpha=0.3)

# 2. 逻辑回归决策边界
ax = axes[1]
Z_logistic = logistic_model.predict(np.c_[xx.ravel(), yy.ravel()])
Z_logistic = Z_logistic.reshape(xx.shape)

# 绘制决策区域
ax.contourf(xx, yy, Z_logistic, cmap=cmap_light, alpha=0.8)

# 绘制训练数据点
ax.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], 
           c='red', marker='o', edgecolors='k', s=40, label='类别 0')
ax.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], 
           c='blue', marker='^', edgecolors='k', s=40, label='类别 1')

# 尝试绘制逻辑回归的"直线"决策边界
# 获取逻辑回归的系数
if hasattr(logistic_model.named_steps['logisticregression'], 'coef_'):
    coef = logistic_model.named_steps['logisticregression'].coef_[0]
    intercept = logistic_model.named_steps['logisticregression'].intercept_[0]
    
    # 绘制决策直线
    x_plot = np.array([xx.min(), xx.max()])
    y_plot = -(coef[0] * x_plot + intercept) / coef[1]
    ax.plot(x_plot, y_plot, 'k--', linewidth=3, label='逻辑回归决策边界')

ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_title(f'逻辑回归 (线性分类器)\n训练集准确率: {logistic_train_acc:.2%}, 测试集准确率: {logistic_test_acc:.2%}')
ax.set_xlabel('特征 X1')
ax.set_ylabel('特征 X2')
ax.legend(loc='upper right')
ax.grid(True, alpha=0.3)

# 3. RBFN决策边界
ax = axes[2]
Z_rbfn = rbfn_model.predict(np.c_[xx.ravel(), yy.ravel()])
Z_rbfn = Z_rbfn.reshape(xx.shape)

# 绘制决策区域
ax.contourf(xx, yy, Z_rbfn, cmap=cmap_light, alpha=0.8)

# 绘制训练数据点
ax.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], 
           c='red', marker='o', edgecolors='k', s=40, label='类别 0')
ax.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], 
           c='blue', marker='^', edgecolors='k', s=40, label='类别 1')

# 为了直观展示RBFN的"感应圈",我们在图中标记一些"径向基函数中心"
# 这里随机选取一些训练样本点作为示意
np.random.seed(42)
n_centers = 15
center_indices = np.random.choice(len(X_train), n_centers, replace=False)
centers = X_train[center_indices]

# 绘制径向基函数中心
ax.scatter(centers[:, 0], centers[:, 1], s=150, c='yellow', 
           marker='*', edgecolors='k', linewidth=1.5, alpha=0.8, 
           label=f'RBF中心 (示意, {n_centers}个)')

ax.set_xlim(xx.min(), xx.max())
ax.set_ylim(yy.min(), yy.max())
ax.set_title(f'RBF神经网络 (非线性分类器)\n训练集准确率: {rbfn_train_acc:.2%}, 测试集准确率: {rbfn_test_acc:.2%}')
ax.set_xlabel('特征 X1')
ax.set_ylabel('特征 X2')
ax.legend(loc='upper right')
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 补充说明图:展示逻辑回归的线性决策边界如何尝试拟合非线性数据
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))

# 左图:逻辑回归试图用一条直线分隔同心圆
ax1.scatter(X[y == 0, 0], X[y == 0, 1], c='red', marker='o', 
            edgecolors='k', s=50, alpha=0.7, label='类别 0')
ax1.scatter(X[y == 1, 0], X[y == 1, 1], c='blue', marker='^', 
            edgecolors='k', s=50, alpha=0.7, label='类别 1')

# 绘制多条可能的"直线"决策边界
x_plot = np.array([-1.5, 1.5])
for i, angle in enumerate([-0.5, 0, 0.5, 1.0]):
    y_plot = angle * x_plot + 0.2
    ax1.plot(x_plot, y_plot, 'k--', linewidth=2, alpha=0.7, 
             label=f'可能的线性边界 {i+1}' if i == 0 else None)

ax1.set_xlim(-1.5, 1.5)
ax1.set_ylim(-1.5, 1.5)
ax1.set_title('逻辑回归:试图用直线分隔同心圆\n(注定失败)')
ax1.set_xlabel('特征 X1')
ax1.set_ylabel('特征 X2')
ax1.legend()
ax1.grid(True, alpha=0.3)

# 右图:RBFN使用多个径向基函数形成复杂决策边界
ax2.scatter(X[y == 0, 0], X[y == 0, 1], c='red', marker='o', 
            edgecolors='k', s=50, alpha=0.5, label='类别 0')
ax2.scatter(X[y == 1, 0], X[y == 1, 1], c='blue', marker='^', 
            edgecolors='k', s=50, alpha=0.5, label='类别 1')

# 绘制几个径向基函数(感应圈)的示意图
centers = np.array([[0, 0], [0.5, 0.5], [-0.5, -0.5], [0.5, -0.5], [-0.5, 0.5]])
radii = [0.3, 0.25, 0.25, 0.25, 0.25]

for center, radius in zip(centers, radii):
    circle = plt.Circle(center, radius, color='yellow', alpha=0.3, linewidth=2, 
                        fill=True, edgecolor='orange')
    ax2.add_patch(circle)
    ax2.plot(center[0], center[1], 'y*', markersize=15, markeredgecolor='orange')

# 绘制RBFN形成的复杂决策边界
xx, yy = np.meshgrid(np.linspace(-1.5, 1.5, 200),
                     np.linspace(-1.5, 1.5, 200))
grid_points = np.c_[xx.ravel(), yy.ravel()]

# 模拟一个简单的RBF决策函数
def simple_rbf_decision(points, centers, radii):
    decisions = []
    for point in points:
        # 简单模拟:如果点在某个中心的内圈,则分类为1,否则为0
        class_1 = False
        for center, radius in zip(centers, radii):
            distance = np.sqrt((point[0]-center[0])**2 + (point[1]-center[1])**2)
            if distance < radius:
                class_1 = True
                break
        decisions.append(1 if class_1 else 0)
    return np.array(decisions)

Z_simple_rbf = simple_rbf_decision(grid_points, centers, radii)
Z_simple_rbf = Z_simple_rbf.reshape(xx.shape)

# 绘制决策边界
ax2.contour(xx, yy, Z_simple_rbf, levels=[0.5], colors='green', linewidths=3, linestyles='--')

ax2.set_xlim(-1.5, 1.5)
ax2.set_ylim(-1.5, 1.5)
ax2.set_title('RBFN:多个径向基函数形成复杂边界\n(成功分类同心圆)')
ax2.set_xlabel('特征 X1')
ax2.set_ylabel('特征 X2')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 打印模型性能对比
print("="*60)
print("模型性能对比")
print("="*60)
print(f"逻辑回归:")
print(f"  训练集准确率: {logistic_train_acc:.2%}")
print(f"  测试集准确率: {logistic_test_acc:.2%}")
print(f"  线性决策边界,无法处理复杂非线性模式")
print()
print(f"RBF神经网络:")
print(f"  训练集准确率: {rbfn_train_acc:.2%}")
print(f"  测试集准确率: {rbfn_test_acc:.2%}")
print(f"  非线性决策边界,通过径向基函数成功捕捉复杂模式")
print("="*60)

2.互动游戏:你是RBFN!

python 复制代码
import matplotlib.pyplot as plt

# 修复中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial']  # 使用黑体或Arial
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 定义专家和评分
experts = ['红色专家', '金属专家', '圆形专家', '其他1', '其他2', '其他3']
scores = [9, 8, 10, 2, 1, 0]
colors = ['red', 'silver', 'blue', 'gray', 'gray', 'gray']

# 创建图表
fig, ax = plt.subplots(figsize=(10, 6))

# 绘制专家评分
for i, (expert, score, color) in enumerate(zip(experts, scores, colors)):
    ax.scatter(i, 5, s=score*100, alpha=0.6, c=color, edgecolors='black')
    ax.text(i, 5, f'{score}', ha='center', va='center', fontsize=12, fontweight='bold')
    ax.text(i, 8, expert, ha='center', fontsize=10)

# 添加CEO判断
ax.text(2.5, 1, 'CEO综合判断: 红色金属圆形物品', 
        ha='center', fontsize=14, fontweight='bold',
        bbox=dict(boxstyle='round,pad=0.5', facecolor='gold', alpha=0.8))

# 设置图表
ax.set_title('RBFN游戏: "一个红色的金属圆形徽章"', fontsize=16, fontweight='bold')
ax.set_xlim(-1, 6)
ax.set_ylim(0, 10)
ax.axis('off')  # 隐藏坐标轴
plt.tight_layout()
plt.show()

这段代码做了三件事:

  1. 用不同颜色和大小的圆圈表示6个专家的评分(圆圈越大分数越高)

  2. 用文字标出每个专家的分数

  3. 在底部显示CEO的综合判断结果

相关推荐
最晚的py1 小时前
机器学习--损失函数
人工智能·python·机器学习·损失函数
Qinana1 小时前
当AI为你写SQL,连数据库都开始谈恋爱了
人工智能·python·sql
韭菜钟1 小时前
在Qt中实现mqtt客户端
开发语言·qt
严文文-Chris1 小时前
神经网络的组成有哪些?激活函数是什么?有什么作用?
人工智能·深度学习·神经网络
sdyeswlw1 小时前
一二三物联网:领航济南制造业数字化绿色化协同转型
人工智能·科技·物联网
大千AI助手1 小时前
闵可夫斯基距离:机器学习的“距离家族”之源
人工智能·机器学习·距离度量·大千ai助手·闵可夫斯基距离·lp距离·切比雪夫距离
4***571 小时前
PHP进阶-在Ubuntu上搭建LAMP环境教程
开发语言·ubuntu·php
ULTRA??1 小时前
C++拷贝构造函数的发生时机,深拷贝实现
开发语言·c++
java修仙传1 小时前
力扣hot100:最长连续序列
算法·leetcode·职场和发展