感知机代码权重更新解惑
感知机代码权重更新解惑

代码

 1#include <stdio.h>
 2#include <stdlib.h>
 3
 4// 激活函数(阶跃函数)
 5int activation_function(float weighted_sum) {
 6    return weighted_sum >= 0 ? 1 : 0;  // 阈值为0
 7}
 8
 9// 感知机模型
10void perceptron_train(float **inputs, int *labels, float *weights, float *bias, int num_samples, int num_features, float learning_rate, int epochs) {
11    for (int epoch = 0; epoch < epochs; epoch++) {
12        printf("Epoch %d:\n", epoch + 1);
13        for (int i = 0; i < num_samples; i++) {
14            // 计算加权和
15            float weighted_sum = *bias;
16            for (int j = 0; j < num_features; j++) {
17                weighted_sum += inputs[i][j] * weights[j];
18            }
19
20            // 预测值
21            int prediction = activation_function(weighted_sum);
22
23            // 计算误差
24            int error = labels[i] - prediction;
25
26            // 更新权重和偏置
27            for (int j = 0; j < num_features; j++) {
28                weights[j] += learning_rate * error * inputs[i][j];
29            }
30            *bias += learning_rate * error;
31
32            // 输出权重和偏置
33            printf("  样本 %d: 更新后权重 = [", i + 1);
34            for (int j = 0; j < num_features; j++) {
35                printf("%.2f%s", weights[j], j == num_features - 1 ? "" : ", ");
36            }
37            printf("], 偏置 = %.2f\n", *bias);
38        }
39        printf("\n");
40    }
41}
42
43int main() {
44    int num_samples = 4;      // 样本数量
45    int num_features = 2;     // 每个样本的特征数量
46    float learning_rate = 0.1; // 学习率
47    int epochs = 10;          // 训练轮数
48
49    // 训练数据 (逻辑与AND操作)
50    float training_inputs[4][2] = {
51        {0, 0},
52        {0, 1},
53        {1, 0},
54        {1, 1}
55    };
56    int training_labels[4] = {0, 0, 0, 1}; // AND操作的标签
57
58    // 转换为指针形式
59    float *inputs[4];
60    for (int i = 0; i < 4; i++) {
61        inputs[i] = training_inputs[i];
62    }
63
64    // 初始化权重和偏置
65    float weights[2] = {0.0, 0.0}; // 初始权重为0
66    float bias = 0.0;              // 初始偏置为0
67
68    // 训练感知机
69    perceptron_train(inputs, training_labels, weights, &bias, num_samples, num_features, learning_rate, epochs);
70
71    // 测试感知机
72    printf("测试感知机:\n");
73    for (int i = 0; i < num_samples; i++) {
74        float weighted_sum = bias;
75        for (int j = 0; j < num_features; j++) {
76            weighted_sum += training_inputs[i][j] * weights[j];
77        }
78        int prediction = activation_function(weighted_sum);
79        printf("  输入: [%.1f, %.1f], 预测: %d, 标签: %d\n",
80               training_inputs[i][0], training_inputs[i][1], prediction, training_labels[i]);
81    }
82
83    return 0;
84}

我不是很理解权重更新公式

没问题!我们来详细分析一下 权重更新公式,帮助你理解感知机的学习过程。

感知机的权重更新公式:

$$ w_j \mathrel{+}= \eta \cdot e \cdot x_j $$

其中:

  • $w_j$:第 $j$ 个特征的权重。
  • $\eta$(learning rate):学习率,控制更新的步长,通常是一个小数(例如 0.1)。
  • $e$(error):误差,表示真实标签与模型预测值的差距,计算公式为:

$$ e = \text{label} - \text{prediction} $$

  • $x_j$:输入样本的第 $j$ 个特征值。

权重更新的意义

目标:通过调整权重,让感知机的输出逐步逼近真实标签。

更新公式的直观理解

  1. 计算误差
  • 误差 $e$ 是真实值与模型预测值的差值:

$$ e = \text{label} - \text{prediction} $$ 如果预测正确:$e = 0$,权重不会更新。如果预测错误:$e \neq 0$,权重会被调整。 2. 调整方向

  • 如果预测小于真实值($e > 0$),权重应增大,以加强特征对预测的影响。
  • 如果预测大于真实值($e < 0$),权重应减小,以减弱特征对预测的影响。
  1. 调整量的大小
  • 调整量由 $\eta \cdot e \cdot x_j$ 决定:$\eta$:学习率,控制调整的幅度,避免变化过快或过慢。$e$:误差越大,调整越大,模型更“努力”修正错误。$x_j$:特征值越大,对调整的贡献越大,反映特征的重要性。

偏置更新公式

偏置的更新公式类似于权重的更新:

$$ b \mathrel{+}= \eta \cdot e $$

偏置 $b$ 是一个独立于输入的常量项,控制决策边界的位置。

举个例子

初始条件:

  • 输入样本:$x = [1, 0]$
  • 权重:$w = [0.0, 0.0]$
  • 偏置:$b = 0.0$
  • 学习率:$\eta = 0.1$
  • 标签:$\text{label} = 1$

训练第一个样本:

  1. 计算加权和: $$ \text{weighted_sum} = b + \sum (x_j \cdot w_j) = 0.0 + (1 \cdot 0.0) + (0 \cdot 0.0) = 0.0 $$
  2. 预测值: $$ \text{prediction} = \text{activation_function}(\text{weighted_sum}) = 1 , (\text{因为 } \text{weighted_sum} \geq 0) $$
  3. 计算误差: $$ e = \text{label} - \text{prediction} = 1 - 1 = 0 $$
  4. 更新权重和偏置
  • 因为 $e = 0$,所以权重和偏置不更新:

$$ w = [0.0, 0.0], , b = 0.0 $$

第二个样本:

假设样本 $x = [0, 1]$,标签 $\text{label} = 0$。

  1. 计算加权和: $$ \text{weighted_sum} = b + \sum (x_j \cdot w_j) = 0.0 + (0 \cdot 0.0) + (1 \cdot 0.0) = 0.0 $$
  2. 预测值: $$ \text{prediction} = 1 $$
  3. 计算误差: $$ e = \text{label} - \text{prediction} = 0 - 1 = -1 $$
  4. 更新权重和偏置
  • 更新公式: $$ w_j \mathrel{+}= \eta \cdot e \cdot x_j $$
  • $w_0 = w_0 + \eta \cdot e \cdot x_0 = 0.0 + 0.1 \cdot (-1) \cdot 0 = 0.0$
  • $w_1 = w_1 + \eta \cdot e \cdot x_1 = 0.0 + 0.1 \cdot (-1) \cdot 1 = -0.1$
  • 偏置: $$ b \mathrel{+}= \eta \cdot e = 0.0 + 0.1 \cdot (-1) = -0.1 $$
  • 更新后的权重和偏置: $$ w = [0.0, -0.1], , b = -0.1 $$

总结

权重更新公式本质上是根据误差大小和方向,调整模型的参数,以便预测值更接近真实标签:

  1. 误差大:更新幅度大。
  2. 误差小:更新幅度小。
  3. 学习率:控制更新速度,避免过快或过慢。 如果还有疑问,可以告诉我,我再具体解释!

为什么要乘以xj

这是个非常棒的问题!权重更新公式中乘以 $x_j$ 的原因是 感知机学习规则的一部分,反映了每个特征在模型中的重要性。我们从几个角度来分析为什么需要乘以 $x_j$。

1. 特征对输出的贡献大小

感知机模型的核心公式是:

$$ \text{weighted_sum} = \sum_{j=1}^{n} w_j \cdot x_j + b $$

其中:

  • $w_j \cdot x_j$ 表示第 $j$ 个特征对感知机输出的贡献大小。
  • 如果 $x_j$ 很大,而权重 $w_j$ 也较大,那么这个特征对预测结果的影响就越显著。 在权重更新时:

$$ w_j \mathrel{+}= \eta \cdot e \cdot x_j $$

乘以 $x_j$ 的原因是:

  • 如果特征 $x_j = 0$,说明这个特征在当前样本中对模型的输出没有影响,因此 不需要更新权重 $w_j$
  • 如果 $x_j \neq 0$,说明这个特征在当前样本中是有贡献的,因此权重更新的大小与这个特征值成正比。

2. 特征区分样本的重要性

假设有两个特征:

  • 特征 1:样本中的值为 0($x_1 = 0$),不影响输出。
  • 特征 2:样本中的值为 1($x_2 = 1$),有实际贡献。 在这种情况下,权重更新公式会自动忽略 $x_1$ 的权重更新,因为它不会对当前预测值产生影响:

$$ w_1 \mathrel{+}= \eta \cdot e \cdot x_1 = \eta \cdot e \cdot 0 = 0 $$

而 $w_2$ 会更新:

$$ w_2 \mathrel{+}= \eta \cdot e \cdot x_2 = \eta \cdot e \cdot 1 $$

这样模型的权重更新只针对 当前特征起作用的部分,这提高了模型训练的效率和准确性。

3. 梯度下降的启发

从数学上看,感知机的权重更新公式类似于梯度下降的思想。对于每个特征,权重更新方向是 误差对权重的导数

加权和的公式:

$$ \text{weighted_sum} = \sum_{j=1}^{n} w_j \cdot x_j + b $$

误差:

$$ e = \text{label} - \text{prediction} $$

我们对误差 $e$ 对权重 $w_j$ 的导数是:

$$ \frac{\partial e}{\partial w_j} = - x_j $$

这表明:特征值 $x_j$ 的大小直接影响权重的更新幅度。如果特征值 $x_j$ 较大,它对误差的贡献也大,因此权重更新幅度应该更大。

4. 举例说明

假设我们有一个简单的感知机模型:

  • 样本:$[x_1, x_2] = [2, 1]$
  • 权重:$[w_1, w_2] = [0.5, -0.2]$
  • 偏置:$b = 0.1$
  • 学习率:$\eta = 0.1$
  • 标签:$\text{label} = 1$

第一步:计算加权和

$$ \text{weighted_sum} = w_1 \cdot x_1 + w_2 \cdot x_2 + b = 0.5 \cdot 2 + (-0.2) \cdot 1 + 0.1 = 0.9 $$

第二步:计算预测值

激活函数为阶跃函数:

$$ \text{prediction} = \text{activation_function}(0.9) = 1 $$

第三步:计算误差

$$ e = \text{label} - \text{prediction} = 1 - 1 = 0 $$

因为误差为 0,此时权重和偏置不更新。

假设换一个样本:$[x_1, x_2] = [3, 0]$,标签 $\text{label} = 1$,当前权重不变。

第一步:计算加权和

$$ \text{weighted_sum} = w_1 \cdot x_1 + w_2 \cdot x_2 + b = 0.5 \cdot 3 + (-0.2) \cdot 0 + 0.1 = 1.6 $$

第二步:计算预测值

$$ \text{prediction} = \text{activation_function}(1.6) = 1 $$

误差仍为 0,不更新权重。

但如果标签 $\text{label} = 0$,误差为 $e = -1$。

第三步:更新权重

$$ w_1 \mathrel{+}= \eta \cdot e \cdot x_1 = 0.5 + 0.1 \cdot (-1) \cdot 3 = 0.2 $$

$$ w_2 \mathrel{+}= \eta \cdot e \cdot x_2 = -0.2 + 0.1 \cdot (-1) \cdot 0 = -0.2 $$

偏置更新:

$$ b \mathrel{+}= \eta \cdot e = 0.1 + 0.1 \cdot (-1) = 0.0 $$

更新后:

$$ w = [0.2, -0.2], , b = 0.0 $$

观察

  • 特征 $x_1 = 3$ 对加权和贡献大,因此 $w_1$ 更新幅度更大。
  • 特征 $x_2 = 0$ 对加权和没有贡献,因此 $w_2$ 不更新。

总结

乘以 $x_j$ 的原因是:

  1. 区分特征的重要性,只有与当前样本相关的特征会更新对应权重。
  2. 确保权重的更新与样本特征值的大小成比例,调整更加精准。
  3. 反映梯度下降的思想:特征值越大,对误差的贡献越大,权重需要调整得更多。 如果你还有疑问,可以继续提出来!

误差 对 权重 的导数是怎么推导出来的

我们来详细推导 误差对权重的导数,以理解为什么导数中会出现 $x_j$。

1. 感知机的误差公式

假设:

  • 模型的输出为 $y_{\text{pred}}$,即预测值;
  • 实际标签为 $y_{\text{true}}$;
  • 误差定义为 $e = y_{\text{true}} - y_{\text{pred}}$。 此时,权重更新的目标是通过调整权重 $w_j$,使得误差 $e$ 最小化。

2. 预测值的公式

感知机的加权和公式为:

$$ z = \sum_{j=1}^{n} w_j \cdot x_j + b $$

预测值是通过激活函数 $f(z)$ 计算得出的:

$$ y_{\text{pred}} = f(z) $$

为了便于推导,这里暂假设 $f(z)$ 是一个可微的函数,例如 Sigmoid 函数。如果是阶跃函数(感知机中的激活函数),我们仍可以从梯度下降的思想理解其更新规律。

3. 误差对权重的导数

现在,我们希望推导误差 $e = y_{\text{true}} - y_{\text{pred}}$ 对权重 $w_j$ 的导数:

$$ \frac{\partial e}{\partial w_j} $$

误差是一个函数 $e(y_{\text{pred}})$,而 $y_{\text{pred}}$ 又是权重 $w_j$ 的函数。因此,我们需要用链式法则来计算。

第一步:误差对预测值的导数

误差公式:

$$ e = y_{\text{true}} - y_{\text{pred}} $$

对 $y_{\text{pred}}$ 求导:

$$ \frac{\partial e}{\partial y_{\text{pred}}} = -1 $$

第二步:预测值对加权和的导数

假设激活函数是 $f(z)$,其中 $z = \sum_{j=1}^{n} w_j \cdot x_j + b$。则:

$$ y_{\text{pred}} = f(z) $$

对 $z$ 求导:

$$ \frac{\partial y_{\text{pred}}}{\partial z} = f’(z) $$

第三步:加权和对权重的导数

加权和 $z = \sum_{j=1}^{n} w_j \cdot x_j + b$ 是权重 $w_j$ 的线性函数。对 $w_j$ 求导:

$$ \frac{\partial z}{\partial w_j} = x_j $$

合并链式法则

结合以上三步,应用链式法则:

$$ \frac{\partial e}{\partial w_j} = \frac{\partial e}{\partial y_{\text{pred}}} \cdot \frac{\partial y_{\text{pred}}}{\partial z} \cdot \frac{\partial z}{\partial w_j} $$

逐项代入:

$$ \frac{\partial e}{\partial w_j} = (-1) \cdot f’(z) \cdot x_j $$

4. 权重更新公式

权重更新的目的是沿着负梯度方向调整权重,使误差减小:

$$ w_j \mathrel{+}= -\eta \cdot \frac{\partial e}{\partial w_j} $$

代入导数:

$$ w_j \mathrel{+}= \eta \cdot f’(z) \cdot x_j $$

5. 感知机特殊情况:阶跃函数

在感知机中,激活函数是一个阶跃函数:

$$ f(z) = \begin{cases} 1, & z \geq 0 \ 0, & z < 0 \end{cases} $$

由于阶跃函数的导数不可直接求出,我们直接用感知机的规则更新权重,而不计算梯度。此时的更新公式是:

$$ w_j \mathrel{+}= \eta \cdot e \cdot x_j $$

这里 $x_j$ 的作用和梯度下降方法中一样,表示特征对加权和的贡献大小。

6. 直观理解导数结果

在导数推导中,出现 $x_j$ 的原因是:

  • 权重 $w_j$ 的变化会通过特征 $x_j$ 影响到加权和 $z$。
  • 特征 $x_j$ 表示该特征在模型中的重要性:值越大,对加权和 $z$ 的贡献越大,权重 $w_j$ 就需要更大的调整。 这样,我们的权重更新公式就能够根据特征值动态调整,逐步减少误差。

如果还有不清楚的地方,可以继续问!


最后修改于 2025-01-24 15:38