技术博客 技术博客
  • JAVA编程
  • 设计模式
  • 人工智能
  • Spring
  • Mybatis
  • Maven
  • Git
  • Kafka
  • RabbitMQ
  • RocketMQ
  • Redis
  • Zookeeper
  • Nginx
  • 数据库套件
  • MySQL
  • Elasticsearch
  • MongoDB
  • Hadoop
  • ClickHouse
  • Hbase
  • Hive
  • Flink
  • Flume
  • SQLite
  • linux
  • Docker
  • Jenkins
  • Kubernetes
  • 工具
  • 前端
GitHub (opens new window)
  • JAVA编程
  • 设计模式
  • 人工智能
  • Spring
  • Mybatis
  • Maven
  • Git
  • Kafka
  • RabbitMQ
  • RocketMQ
  • Redis
  • Zookeeper
  • Nginx
  • 数据库套件
  • MySQL
  • Elasticsearch
  • MongoDB
  • Hadoop
  • ClickHouse
  • Hbase
  • Hive
  • Flink
  • Flume
  • SQLite
  • linux
  • Docker
  • Jenkins
  • Kubernetes
  • 工具
  • 前端
GitHub (opens new window)
  • JAVA编程

    • 基础

      • JAVA 锁 及 线程
      • JAVA NIO API
      • JVM 模块介绍
    • 版本

      • JAVA8 新特性总结
      • JAVA9 新特性总结
      • JAVA 10/11/12/13/14/15/16/17 新特性总结
    • 其他

      • jar 打包成.exe可执行文件
      • java代码混淆之 ProGuard
      • JAVA 性能监控(jvisualvm)及测试(JMeter)
      • Alibaba Arthas
      • jar启动脚本
  • 设计模式

    • 代理模式
  • 人工智能

    • 基础算法

      • 线性模型
      • 感知机
        • 感知机
        • 导数
    • 框架与工具

      • ollama本地部署
      • Spring AI
目录

感知机

# 感知机

感知机是最简单的神经网络,像一个 “二分类开关”:输入数据后,它会判断数据属于 “类别 1” 还是 “类别 0”(比如判断图片里是猫还是狗)。

核心公式:y = wx - b

  • x:输入数据(比如一张图片的亮度值,假设是单个数字)
  • w:权重(相当于 “重视程度”,w 越大,x 对结果影响越大)
  • b:偏置(相当于 “门槛”,调整判断的基准线)
  • y:计算结果(但最终输出需要用 “激活函数” 变成 0 或 1)

激活函数,把结果变成分类,因为我们需要的是 “是 / 否” 的判断,所以用阶跃函数:

  • 如果 y ≥ 0 → 输出 1(属于类别 1)
  • 如果 y < 0 → 输出 0(属于类别 0)

感知机通过调整 w 和 b 来提高判断准确性,核心是 “错了就改”:

  • 用当前的 w 和 b 预测结果
  • 如果预测错了,就按规则调整 w 和 b
    • w 新 = w 旧 + 学习率 × (正确答案 - 预测结果) × x
    • b 新 = b 旧 - 学习率 × (正确答案 - 预测结果)
    • 学习率:控制每次调整的幅度(比如 0.1,防止调整太猛)

简单示例:

public class Perceptron {
    // 初始化权重和偏置(随便给个初始值)
    double w = 0.1;  // 权重
    double b = 0.1;  // 偏置
    double learningRate = 0.1;  // 学习率

    // 激活函数:把计算结果转成0或1
    int activate(double y) {
        return y >= 0 ? 1 : 0;  // 阶跃函数
    }

    // 预测:输入x,输出0或1
    int predict(int x) {
        double y = w * x - b;  // 核心公式
        return activate(y);
    }

    // 学习:用正确答案调整w和b
    void train(int x, int label) {
        int prediction = predict(x);  // 先预测
        int error = label - prediction;  // 计算误差(正确-预测)

        if (error != 0) {  // 只有预测错了才调整
            w += learningRate * error * x;  // 调整权重
            b -= learningRate * error;      // 调整偏置
        }
    }

    public static void main(String[] args) {
        Perceptron p = new Perceptron();

        // 训练数据:x和对应的正确答案(x≥5→1,否则0)
        int[][] data = {{3, 0}, {6, 1}, {2, 0}, {7, 1}, {4, 0}};

        // 训练10次(多练几次才能学好)
        for (int i = 0; i < 10; i++) {
            for (int[] d : data) {
                p.train(d[0], d[1]);  // 输入x和正确答案
            }
        }

        // 测试一下
        System.out.println("预测x=5:" + p.predict(5));  // 应该输出1
        System.out.println("预测x=4:" + p.predict(4));  // 应该输出0
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  1. 初始化:w 和 b 一开始随便设(0.1),学习率 0.1(小一点稳)
  2. activate 方法:用阶跃函数把 y 转成 0 或 1
  3. predict 方法:代入公式 y=wx-b,再用激活函数得结果
  4. train 方法:先预测,算误差(正确答案 - 预测结果); 如果误差≠0(预测错了),就按公式调整 w 和 b
  5. main 方法:准备训练数据(x 和正确答案); 训练 10 轮(反复学 10 次,让 w 和 b 更准); 测试结果:x=5 应该输出 1,x=4 输出 0

比如当 x=6(正确答案 1),如果一开始预测成 0;

  • 误差 = 1-0=1

  • w 会变大(因为 w += 0.1×1×6)→ 下次 x=6 时,y=wx-b 会更大,更容易≥0→输出 1

  • b 会变小(因为 b -= 0.1×1)→ 门槛降低,更容易输出 1

  • 反过来,如果预测错成 1,w 会变小,b 会变大,让下次预测更准。

已知某楼市房价有房价表如下:

面积 价格
50 5003
60 6005
70 7007

要得到在 80 平米的房价是多少?这里不是一个准确的价格计算,而是预测一个价格。预测是通过对已有数据进行训练,得到合适的参数,然后根据参数来预测未来的值。

可以使用公式 y = wx - b (单变量线性回归) 来进行预测,其中 w 和 b 是参数,y (价格) 是预测值,x (面积) 是输入值。

# 导数

导数本质是 “变化率”—— 描述一个东西随另一个东西变化的 “快慢和方向”。

你开车时,“速度” 就是 “路程对时间的导数”:

  • 速度 = 50km/h → 表示 “时间每增加 1 小时,路程增加 50 公里”(变化率是 50,正方向)
  • 速度 = -20km/h → 表示 “时间每增加 1 小时,路程减少 20 公里”(倒车,变化率是 - 20,反方向)

咱们之前的感知机用的是 “阶跃函数”(输出非 0 即 1),但它有个缺点:没法精确调整 w 和 b 的幅度。比如:

  • 正确答案是 1,预测成 0(误差 = 1)→ 不管误差多 “离谱”(比如 wx-b=-100 还是 wx-b=-1),都用同样的规则调整 w 和 b。
  • 但实际上,wx-b=-100 比 wx-b=-1 “错得更离谱”,应该调整得更狠一点才对。

这就像老师批改作业:不管你错了 1 题还是错了 10 题,都罚抄 1 遍 —— 显然不够合理。我们需要一种能 “根据错误程度调整惩罚力度” 的方法。这时候,导数就能帮我们计算 “该调整多少”—— 错得越离谱,调整幅度越大;错得少,调整幅度小。

为让 “错误程度” 可衡量,我们把激活函数换成 “Sigmoid 函数”(它的输出是 0 到 1 之间的连续值,不是非 0 即 1)。即公式:,z 就是 wx-b,

  • wx-b=-100 → 输出≈0(错得离谱)
  • wx-b=-1 → 输出≈0.27(错得不轻)
  • wx-b=0 → 输出 = 0.5(半对半错)
  • wx-b=1 → 输出≈0.73(接近正确)
  • wx-b=100 → 输出≈1(几乎正确)

这样一来,“预测值” 和 “正确答案” 的差距就能精确衡量了,相当于知道了为了达到某件事,还差百分之多少。

既然 Sigmoid 函数帮我们区分了程度,那么我们还差具体需要调节多少,这时就需要用 "损失函数" 来计算。比如用 “平方误差损失” 正确答案预测值²

  • 正确答案 1,预测 0.27 → Loss=(1-0.27)²≈0.53(错得不轻)
  • 正确答案 1,预测 0.01 → Loss=(1-0.01)²≈0.98(错得离谱)

Loss 越大,说明错误越严重。我们的目标就是让 Loss 尽可能小(也就是让预测越来越准)。注意,这里的输出结果是做为一个可量化的值而已,不是实际误差,他也只是告诉我们值越大,越偏离目标,而且越大越要先纠正。

没有 Sigmoid 的话,你只能得到 “0 或 1” 的预测(阶跃函数),没法评估“错误程度”;没有损失函数的话,你只有 “预测值”,没法直接知道 “这个值离正确答案差多少(量化值)、要不要调整 w 和 b”。

我们知道 Loss 大小后,就要调节参数 w 和 b,这时就要用上导数,导数的作用是告诉我们 “当 w(或 b)稍微变化一点点时,Loss 会怎么变?”

  • 如果 “Loss 对 w 的导数” 是正数 → 说明 w 增大时,Loss 也会增大(越调越错)→ 要减小 Loss,必须让 w 减小。
  • 如果 “Loss 对 w 的导数” 是负数 → 说明 w 增大时,Loss 会减小(越调越对)→ 要减小 Loss,必须让 w 增大。

z = wx - b(感知机的核心计算结果),而我们最终要调整的是 w 和 b。但是 z 是 w 和 b 的 “中间人”——w 和 b 的变化会先影响 z,z 的变化再影响预测值,预测值的变化最后影响损失 Loss。他们之间关系如下:

  • 输入 x:已知的数(比如例子中的 x=6);
  • 参数 w 和 b:需要调整的数(比如初始 w=0.5,b=2);
  • z:z = wx - b(w 和 b 的 “孩子”,由 w 和 b 决定);
  • 预测值 prediction:由 z 决定,用 Sigmoid 函数算:;
  • 损失 Loss:由预测值和正确答案 label 决定,用平方误差:Loss = (label - prediction)²。

所以:

  • “Loss 对 z 的导数(dLoss/dz)”→ 问:“z 变 1 小点点,Loss 会变多少?”
  • “z 对 w 的导数(dz/dw)”→ 问:“w 变 1 小点点,z 会变多少?”
  • 把这两个结合起来,就能知道:“w 变 1 小点点,Loss 会变多少?”(这就是 Loss 对 w 的导数)。

计算导数公式:

  • prediction: d (Loss)/d (prediction) 是损失函数对预测值的导数 ,Loss 是 “(label - prediction) 的平方”,也就是 Loss = (A)²,其中 A = label - prediction,根据基本求导规则:(A²)’ = 2×A×A’(平方的导数是 2× 本身 × 本身的导数),这里 A 对 prediction 的导数是 A’ = -1 (A=label+(−1)×prediction 这里,prediction的系数就是-1(即 “prediction 前面的系数是 -1”)) ,所以:dLoss/d (prediction) = 2×(label - prediction)×(-1) = 2×(prediction - label)
  • z: d (prediction)/dz 是预测值对 z 的导数 ,prediction 是 sigmoid 函数,用求导规则推导后(数学家已经算好),结果非常简洁:等于 “prediction ×(1 - prediction)”,不用记推导过程,记住结论就行。
  • dz: dLoss/dz 损失函数对 z 的导数(两者的乘积,链式法则) dLoss/dz = dLoss/d(prediction) × d(prediction)/dz
  • dw: dz/dw = x ,z 的表达式是 z = wx - b,把 x 和 b 看作固定值,w 是变量,就像 y = 5×x + 3 中,y 对 x 的导数是 5(x 的系数),z 里 w 的系数是 x,所以 z 对 w 的导数就是 x。
  • db: dz/db = -1 ,还是 z = wx - b,把 w 和 x 看作固定值,b 是变量,b 前面的符号是 - 1,就像 y = 3 - 2x 中,y 对 x 的导数是 - 2(x 的系数),所以 z 对 b 的导数就是 - 1。
public class PerceptronWithDerivative {
    double w = 0.1;
    double b = 0.1;
    double learningRate = 0.1; // 学习率

    // Sigmoid激活函数
    double sigmoid(double z) {
        return 1 / (1 + Math.exp(-z)); // e^(-z)用Math.exp计算
    }

    // 预测:输出0-1之间的概率
    double predict(int x) {
        double z = w * x - b;
        return sigmoid(z);
    }

    // 用导数训练
    void train(int x, int label) {
        double prediction = predict(x); // 预测值(0-1)
        double loss = Math.pow(label - prediction, 2); // 损失函数
        // 打印损失,观察变化(新增代码)
        System.out.println("当前损失:" + loss);
        // 计算导数(按上面的公式)
        // z 因为是中间变量
        double dLoss_dz = 2 * (prediction - label) * prediction * (1 - prediction); 
        double dLoss_dw = dLoss_dz * x; // z对w的导数是x
        double dLoss_db = dLoss_dz * (-1); // z对b的导数是-1

        // 用导数调整w和b(梯度下降)
        w -= learningRate * dLoss_dw;
        b -= learningRate * dLoss_db;
    }

    public static void main(String[] args) {
        PerceptronWithDerivative p = new PerceptronWithDerivative();
        int[][] data = {{3, 0}, {6, 1}, {2, 0}, {7, 1}, {4, 0}};

        // 训练100次(Sigmoid需要多练几次)
        for (int i = 0; i < 100; i++) {
            for (int[] d : data) {
                p.train(d[0], d[1]);
            }
        }

        // 测试:输出接近1或0的值
        System.out.println("预测x=5:" + p.predict(5)); // 应该接近1
        System.out.println("预测x=4:" + p.predict(4)); // 应该接近0
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

梯度下降公式:参数新 = 参数旧 - 学习率 × 梯度(导数)

偏导就是当一个变量(比如 Loss)同时受多个变量(比如 w 和 b)影响时,固定其他变量不变,只看一个变量对它的影响,这时候的导数就是 “偏导数”。比如

  • 计算 dLoss/dw 时,我们固定 b 不变,只看 w 变化对 Loss 的影响 → 这是 Loss 对 w 的偏导数(记为∂Loss/∂w)
  • 计算 dLoss/db 时,我们固定 w 不变,只看 b 变化对 Loss 的影响 → 这是 Loss 对 b 的偏导数(记为∂Loss/∂b)。

代码表现:

// 计算Loss对w的偏导数(固定b不变)
double dLoss_dw = dLoss_dz * x;
// 计算Loss对b的偏导数(固定w不变)
double dLoss_db = dLoss_dz * (-1);  
1
2
3
4

导数(普通导数):单变量函数

  • 函数:y = 2x(y 只由 x 决定,没有其他变量)
  • 导数:dy/dx = 2 → 意思是 “x 每变 1,y 就变 2”(只有 x 一个变量,不用考虑其他)

偏导数:多变量函数

  • 函数:z = 2x + 3y(z 由 x 和 y 两个变量决定)
  • 偏导数 1(对 x):∂z/∂x = 2 → 固定 y 不变,x 每变 1,z 变 2
  • 偏导数 2(对 y):∂z/∂y = 3 → 固定 x 不变,y 每变 1,z 变 3
上次更新: 5/24/2026, 2:55:39 PM
线性模型
ollama本地部署

← 线性模型 ollama本地部署→

Theme by Vdoing | Copyright © 2023-2026
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式