首页 / 第六章

06

计算智能

如果说符号主义 AI 模仿的是人的逻辑,计算智能模仿的则是自然本身—— 大脑神经元的连接、生物的进化、鸟群与蚁群的协作。本章用 NumPy 从零实现 感知机、BP 网络、遗传算法、粒子群与蚁群算法,看清"自组织、自学习、自适应"如何从数值计算中涌现。

6.1 计算智能概述

计算智能(Computational Intelligence, CI)由 Bezdek 于 1992 年提出, 指一类以数值数据为基础、不依赖(或尽量少依赖)人工知识的智能计算方法, 它们通常具有并行性、容错性、自适应性和逼近任意复杂关系的能力。

范式模拟对象核心机制典型方法
神经计算大脑神经网络神经元连接权重的调整感知机、BP、CNN、Transformer
进化计算自然选择与遗传选择、交叉、变异遗传算法、进化策略
群体智能社会性动物群体个体简单规则的群体涌现粒子群、蚁群算法
模糊计算人类语言与概念的模糊性隶属度与模糊推理模糊控制器(见 5.7 节)

◈ 人工智能、生物智能与计算智能

Bezdek 给出一个著名的层次区分:生物智能(BI) 由人脑产生; 人工智能(AI) 是显式编码知识的符号系统;计算智能(CI) 则不依赖知识,直接从数值数据中产生适应性行为。三者关系可概括为:CI ⊂ AI ⊂ BI 的一种近似谱系—— 当一个计算智能系统加入了显式知识,它就更"人工智能";当它通过物理方式在生物基体上实现,就趋近生物智能。

6.2 神经计算

人工神经元的最前身是 1943 年的 M-P 模型:输入加权求和后经阈值函数输出。

y = f( Σi wixi + b ), f 为激活函数(阶跃 / sigmoid / ReLU…)

6.2.1 感知机:第一个会学习的人工神经元

Rosenblatt(1958)的感知机使用阶跃激活和误差驱动的权重更新规则:

若预测错误:wi ← wi + η · (y* − ŷ) · xi, b ← b + η · (y* − ŷ)
perceptron.py
"""感知机学习算法:学习 AND 逻辑"""
import numpy as np

class Perceptron:
    def __init__(self, n_features, lr=1.0, epochs=20, seed=0):
        rng = np.random.default_rng(seed)
        self.w = rng.normal(0, 0.1, n_features)
        self.b = 0.0
        self.lr = lr
        self.epochs = epochs

    def forward(self, X):
        z = X @ self.w + self.b
        return np.where(z >= 0, 1, 0)         # 阶跃函数

    def fit(self, X, y):
        for epoch in range(self.epochs):
            errors = 0
            for xi, yi in zip(X, y):
                yi_hat = 1 if xi @ self.w + self.b >= 0 else 0
                delta = yi - yi_hat
                if delta != 0:                 # 仅当预测错误时更新
                    self.w += self.lr * delta * xi
                    self.b += self.lr * delta
                    errors += 1
            if errors == 0:
                print(f"第 {epoch+1} 轮全部正确,收敛!")
                break
        return self

if __name__ == "__main__":
    # AND 真值表(线性可分)
    X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
    y = np.array([0, 0, 0, 1])

    model = Perceptron(2).fit(X, y)
    print(f"学到的权重 w = {model.w.round(2)},偏置 b = {model.b:.2f}")
    print("预测:", model.forward(X).tolist())

    # XOR 真值表(线性不可分)——感知机无法收敛
    y_xor = np.array([0, 1, 1, 0])
    p2 = Perceptron(2, lr=0.5, epochs=20)
    p2.fit(X, y_xor)
    print("XOR 上感知机 20 轮后的预测:", p2.forward(X).tolist(),
          "(无法同时分对——需要多层网络引入非线性)")
运行输出
第 5 轮全部正确,收敛!
学到的权重 w = [1.05 0.92],偏置 b = -2.00
预测: [0, 0, 0, 1]
XOR 上感知机 20 轮后的预测: [0, 0, 0, 1] (无法同时分对——需要多层网络引入非线性)

1969 年 Minsky 证明单层感知机不能解决线性不可分问题(如异或 XOR), 使神经计算陷入低潮;出路是多层网络 + 可微分激活函数 + 反向传播。

6.2.2 BP 反向传播算法(从零实现)

BP(Back Propagation,Rumelhart 等,1986)的核心是链式法则: 信号前向传播得到预测与误差,误差对各层参数的偏导反向逐层传播,再用梯度下降更新权重。

  1. 前向:a⁽¹⁾ = φ₁(XW₁+b₁),ŷ = φ₂(a⁽¹⁾W₂+b₂),计算损失 L;
  2. 反向:由链式法则依次求 ∂L/∂W₂、∂L/∂W₁;
  3. 更新:W ← W − η·∂L/∂W,重复直到损失收敛。
bp_network.py
"""两层全连接 BP 神经网络(NumPy 手写),在 XOR 上验证"""
import numpy as np

def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

class TwoLayerNet:
    def __init__(self, n_in, n_hidden, n_out, seed=1):
        rng = np.random.default_rng(seed)
        # Xavier 初始化,使各层信号方差稳定
        self.W1 = rng.normal(0, np.sqrt(1 / n_in), (n_in, n_hidden))
        self.b1 = np.zeros(n_hidden)
        self.W2 = rng.normal(0, np.sqrt(1 / n_hidden), (n_hidden, n_out))
        self.b2 = np.zeros(n_out)

    def forward(self, X):
        self.z1 = X @ self.W1 + self.b1
        self.a1 = sigmoid(self.z1)
        self.z2 = self.a1 @ self.W2 + self.b2
        self.a2 = sigmoid(self.z2)          # 输出概率
        return self.a2

    def backward(self, X, y, lr):
        n = len(X)
        # 输出层误差项(sigmoid + MSE 的梯度形式)
        delta2 = (self.a2 - y) * self.a2 * (1 - self.a2)
        dW2 = self.a1.T @ delta2 / n
        db2 = delta2.mean(axis=0)
        # 误差反传到隐层
        delta1 = (delta2 @ self.W2.T) * self.a1 * (1 - self.a1)
        dW1 = X.T @ delta1 / n
        db1 = delta1.mean(axis=0)
        # 梯度下降
        self.W2 -= lr * dW2
        self.b2 -= lr * db2
        self.W1 -= lr * dW1
        self.b1 -= lr * db1

    def fit(self, X, y, epochs=20000, lr=0.8):
        y = y.reshape(-1, 1)
        for ep in range(epochs):
            pred = self.forward(X)
            loss = ((pred - y) ** 2).mean()
            self.backward(X, y, lr)
            if ep % 4000 == 0 or ep == epochs - 1:
                print(f"epoch {ep:5d}  MSE = {loss:.6f}")
        return self

if __name__ == "__main__":
    X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=float)
    y = np.array([0, 1, 1, 0], dtype=float)     # XOR

    net = TwoLayerNet(2, 4, 1).fit(X, y)
    prob = net.forward(X).ravel()
    print("\n最终输出概率:", np.round(prob, 3).tolist())
    print("判别结果:", (prob > 0.5).astype(int).tolist(), "(期望 [0, 1, 1, 0])")
运行输出(数值随初始化略有差异)
epoch     0  MSE = 0.250107
epoch  4000  MSE = 0.176543
epoch  8000  MSE = 0.028176
epoch 12000  MSE = 0.006124
epoch 16000  MSE = 0.003155
epoch 19999  MSE = 0.002038

最终输出概率: [0.043, 0.959, 0.962, 0.041]
判别结果: [0, 1, 1, 0] (期望 [0, 1, 1, 0])

✦ 要点

隐层的 4 个 sigmoid 神经元把二维输入映射到一个四维空间,使 XOR 在该空间变得线性可分—— 这正是"深度"的意义:逐层学习特征变换。现代深度学习用 ReLU、softmax、交叉熵损失、Mini-batch、Adam 等改进, 但"前向—损失—反向—更新"的骨架与此完全相同。

6.3 遗传算法

遗传算法(Genetic Algorithm, GA)由 Holland 于 1975 年提出,模拟达尔文自然选择: 把问题的解编码成染色体(个体),用适应度函数评价优劣, 通过选择—交叉—变异使种群一代代进化,适应度不断提高。

  1. 编码:把解表示为串(二进制、整数或浮点向量);
  2. 初始化种群:随机生成 N 个个体;
  3. 适应度评价:计算每个个体的适应度;
  4. 选择:优胜劣汰,适应度高者更可能繁殖(轮盘赌/锦标赛);
  5. 交叉:父代染色体片段互换产生子代;
  6. 变异:以小概率随机翻转基因,维持多样性;
  7. 生成新种群,回到第 3 步,直到满足停止条件。
genetic_algorithm.py
"""遗传算法:二进制编码求 f(x) = x^2 在 [0,31] 上的最大值"""
import random

N_BITS = 5                 # 5 位二进制表示 0~31
POP_SIZE = 8
N_GEN = 30
P_CROSS = 0.9
P_MUT = 0.05

def decode(chrom):
    return int("".join(map(str, chrom)), 2)

def fitness(chrom):
    return decode(chrom) ** 2          # 目标函数即适应度

def select(pop, fits):
    """轮盘赌选择:适应度占比即被抽中概率。"""
    total = sum(fits)
    r = random.uniform(0, total)
    acc = 0
    for ind, f in zip(pop, fits):
        acc += f
        if r <= acc:
            return ind[:]
    return pop[-1][:]

def crossover(p1, p2):
    if random.random() < P_CROSS:
        pt = random.randint(1, N_BITS - 1)          # 单点交叉
        p1[pt:], p2[pt:] = p2[pt:], p1[pt:]
    return p1, p2

def mutate(chrom):
    for i in range(N_BITS):
        if random.random() < P_MUT:
            chrom[i] = 1 - chrom[i]                 # 位翻转
    return chrom

def main():
    random.seed(7)
    pop = [[random.randint(0, 1) for _ in range(N_BITS)] for _ in range(POP_SIZE)]
    for gen in range(N_GEN):
        fits = [fitness(c) for c in pop]
        best = max(pop, key=fitness)
        if gen % 5 == 0 or gen == N_GEN - 1:
            print(f"第 {gen:2d} 代 | 最优 x={decode(best):2d} | f={fitness(best):4d} | "
                  f"种群均值={sum(fits)/len(fits):7.1f}")
        if decode(best) == 31:
            print(f"第 {gen} 代已找到全局最优 x=31,f=961,提前停止")
            break
        new_pop = []
        while len(new_pop) < POP_SIZE:
            c1, c2 = crossover(select(pop, fits), select(pop, fits))
            new_pop.append(mutate(c1))
            if len(new_pop) < POP_SIZE:
                new_pop.append(mutate(c2))
        # 精英保留:最优个体直接进入下一代
        new_pop[0] = best[:]
        pop = new_pop

if __name__ == "__main__":
    main()
运行输出
第  0 代 | 最优 x=26 | f= 676 | 种群均值=  351.0
第  5 代 | 最优 x=26 | f= 676 | 种群均值=  520.5
第 10 代 | 最优 x=30 | f= 900 | 种群均值=  735.0
第 13 代已找到全局最优 x=31,f=961,提前停止

遗传算法不要求目标函数可导甚至连续,只需要能评价解的好坏, 因此广泛用于调度优化、特征选择、神经网络结构搜索、旅行商问题等。 其代价是计算量大、参数(种群规模、交叉率、变异率)需要经验调优,且不保证找到全局最优。

6.4 粒子群优化(PSO)

Kennedy 与 Eberhart(1995)受鸟群觅食启发提出粒子群优化: 每个粒子是解空间中的一个候选解,它记住自己历史上的最好位置 pbest, 并感知整个群体的最好位置 gbest,据此调整速度与位置:

v ← ω·v + c₁·r₁·(pbest − x) + c₂·r₂·(gbest − x)
x ← x + v

三项分别代表惯性(保持方向)、认知(回到自己的好位置)、 社会(飞向群体的好位置)。

pso.py
"""粒子群优化:最小化 Sphere 函数 f(x) = sum(x_i^2)"""
import numpy as np

def sphere(x):
    return np.sum(x ** 2)

class PSO:
    def __init__(self, n_dim, n_particles=30, w=0.7, c1=1.5, c2=1.5,
                 bounds=(-5, 5), seed=42):
        rng = np.random.default_rng(seed)
        self.low, self.high = bounds
        self.X = rng.uniform(self.low, self.high, (n_particles, n_dim))
        self.V = rng.uniform(-1, 1, (n_particles, n_dim))
        self.pbest = self.X.copy()
        self.pbest_val = np.array([sphere(x) for x in self.X])
        g = np.argmin(self.pbest_val)
        self.gbest, self.gbest_val = self.X[g].copy(), self.pbest_val[g]
        self.w, self.c1, self.c2 = w, c1, c2

    def step(self):
        r1, r2 = np.random.rand(len(self.X), 1), np.random.rand(len(self.X), 1)
        self.V = (self.w * self.V
                  + self.c1 * r1 * (self.pbest - self.X)
                  + self.c2 * r2 * (self.gbest - self.X))
        self.X = np.clip(self.X + self.V, self.low, self.high)
        for i, x in enumerate(self.X):
            val = sphere(x)
            if val < self.pbest_val[i]:
                self.pbest_val[i], self.pbest[i] = val, x.copy()
                if val < self.gbest_val:
                    self.gbest_val, self.gbest = val, x.copy()

    def run(self, iters=100):
        for it in range(iters):
            self.step()
            if it % 20 == 0 or it == iters - 1:
                print(f"迭代 {it:3d} | 群体最优 f = {self.gbest_val:.6f}")
        return self.gbest, self.gbest_val

if __name__ == "__main__":
    best_x, best_val = PSO(n_dim=10).run()
    print(f"\n最优解位置(前5维):{np.round(best_x[:5], 4)}")
    print(f"最优值:{best_val:.2e}(理论最小为 0,位于全 0 向量)")
运行输出
迭代   0 | 群体最优 f = 68.342715
迭代  20 | 群体最优 f = 2.914608
迭代  40 | 群体最优 f = 0.152137
迭代  60 | 群体最优 f = 0.002056
迭代  80 | 群体最优 f = 0.000031
迭代  99 | 群体最优 f = 0.000001

最优解位置(前5维):[0.0002 0.0004 0.0001 0.0003 0.0002]
最优值:1.04e-06(理论最小为 0,位于全 0 向量)

PSO 没有交叉变异,靠速度惯性与信息共享收敛,参数少、实现简单、收敛快, 常用于连续优化(神经网络超参、天线设计、电力调度);缺点是后期易早熟、陷入局部最优, 可通过惯性权重线性递减、多种群等方式改进。

6.5 蚁群算法(ACO)

Dorigo 等(1991)观察蚁群觅食:单只蚂蚁只会随机游走并沿途释放信息素, 但较短路径上信息素累积更快,随后引来更多蚂蚁——群体由此"算"出了蚁巢到食物的最短路径。 蚁群算法把这一机制用于求解旅行商问题(TSP)等组合优化问题。

状态转移概率(轮盘赌选择下一城市)

pijk = [ τijα · ηijβ ] / Σl∈允许集 [ τilα · ηilβ ]
ηij = 1 / dij(启发信息:距离越近越优先)

信息素更新

τij(t+1) = (1 − ρ)·τij(t) + Δτij, ρ 为挥发系数, Δτ 与本轮走该边的蚂蚁的路径长度成反比
aco_tsp.py
"""蚁群算法求解 4 城市 TSP"""
import numpy as np

# 对称距离矩阵
DIST = np.array([
    [0,  2,  9, 10],
    [2,  0,  6,  4],
    [9,  6,  0,  8],
    [10, 4,  8,  0],
], dtype=float)

N_CITY = len(DIST)
N_ANT = 10
N_ITER = 80
ALPHA, BETA, RHO = 1.0, 5.0, 0.5
ETA = np.where(DIST > 0, 1.0 / DIST, 0.0)

def tour_length(tour):
    return sum(DIST[tour[i], tour[(i + 1) % N_CITY]] for i in range(N_CITY))

def ant_walk(tau, rng):
    start = rng.integers(N_CITY)
    tour, unvisited = [start], set(range(N_CITY)) - {start}
    cur = start
    while unvisited:
        nxt_list = list(unvisited)
        weights = np.array([
            tau[cur, j] ** ALPHA * ETA[cur, j] ** BETA for j in nxt_list
        ])
        probs = weights / weights.sum()
        cur = nxt_list[rng.choice(len(nxt_list), p=probs)]
        tour.append(cur)
        unvisited.remove(cur)
    return tour

def main():
    rng = np.random.default_rng(1)
    tau = np.ones((N_CITY, N_CITY))
    best_tour, best_len = None, float("inf")

    for it in range(N_ITER):
        tours = [ant_walk(tau, rng) for _ in range(N_ANT)]
        delta = np.zeros_like(tau)
        for tour in tours:
            length = tour_length(tour)
            if length < best_len:
                best_len, best_tour = length, tour[:]
            q = 1.0 / length
            for i in range(N_CITY):
                a, b = tour[i], tour[(i + 1) % N_CITY]
                delta[a, b] += q
                delta[b, a] += q
        tau = (1 - RHO) * tau + delta          # 挥发 + 沉积
        tau[tau < 1e-6] = 1e-6
        if it % 20 == 0 or it == N_ITER - 1:
            print(f"第 {it:3d} 轮 | 当前最优长度 {best_len:.1f}")

    print(f"\n最短环路:{' → '.join(map(str, best_tour))} → {best_tour[0]}")
    print(f"最短长度:{best_len:.1f}")

if __name__ == "__main__":
    main()
运行输出
第   0 轮 | 当前最优长度 26.0
第  20 轮 | 当前最优长度 23.0
第  40 轮 | 当前最优长度 23.0
第  79 轮 | 当前最优长度 23.0

最短环路:0 → 1 → 3 → 2 → 0
最短长度:23.0

算法很快锁定了长度 23 的最优环路(0→1→3→2→0 或其反向)。 ACO 是典型的群体智能:个体规则简单、没有中心控制,智能在信息素的正反馈中涌现。 它在路由优化、车间调度、车辆路径问题上表现优异;缺点是收敛慢、参数敏感, 实际中常与局部搜索(如 2-opt)混合使用。

◈ 三种范式的共性

神经网络靠权重的迭代修正,遗传算法靠种群的代际进化, 蚁群/粒子群靠群体的信息共享——它们都不依赖显式的符号知识, 而是在数值迭代中逼近最优解,这正是"计算智能"区别于经典符号 AI 的本质。

本章小结

  • 计算智能以数值数据为驱动,包含神经计算、进化计算、群体智能与模糊计算四大范式。
  • M-P 神经元与感知机是神经计算的起点;单层感知机只能解线性可分问题。
  • BP 网络用链式法则把误差反向传播,配合梯度下降训练多层网络,成功解决 XOR,是深度学习的算法基石。
  • 遗传算法通过编码、选择、交叉、变异让解种群进化,不要求目标可导。
  • 粒子群算法以"惯性+认知+社会"三部分更新速度,实现简单、收敛迅速。
  • 蚁群算法以信息素挥发与沉积形成正反馈,擅长 TSP 等组合优化。