code/08-neural-nets/gradient_descent.py

72 Zeilen · 2.5 KB

Code und Programmausgaben stehen genau so da, wie sie gelaufen sind – Kommentare und Ausgaben sind daher auf Chinesisch.

"""梯度下降:顺着"下坡最快的方向"一小步一小步地走,找到损失最小的 w 和 b。

    python gradient_descent.py
只用 numpy。数据和上一课完全相同。
"""
import numpy as np

rng = np.random.default_rng(0)
area = rng.uniform(40, 150, size=50)
price = 1.2 * area + 20 + rng.normal(0, 12, size=50)


def mse(w, b, x=area, y=price):
    return np.mean((w * x + b - y) ** 2)


# ---------- 1. 导数就是斜率:用数值的办法量一量 ----------
print("== 1. 在 w=1.0, b=0 这一点,损失对 w 的斜率")
w, b = 1.0, 0.0
for h in [0.1, 0.01, 0.001]:
    slope = (mse(w + h, b) - mse(w, b)) / h
    print(f"  w 往右挪 {h:<6},损失变化 / 挪动距离 = {slope:10.2f}")


# ---------- 2. 用公式直接算梯度 ----------
def gradients(w, b, x=area, y=price):
    error = w * x + b - y
    dw = np.mean(2 * error * x)  # 损失对 w 的导数
    db = np.mean(2 * error)  # 损失对 b 的导数
    return dw, db


dw, db = gradients(1.0, 0.0)
print(f"\n== 2. 公式算出的梯度:dw={dw:.2f},db={db:.2f}")


# ---------- 3. 梯度下降 ----------
def descend(lr, steps, x=area, y=price, report=()):
    """从 w=0, b=0 出发走 steps 步。发散时返回 None。"""
    w, b = 0.0, 0.0
    for step in range(1, steps + 1):
        dw, db = gradients(w, b, x, y)
        w -= lr * dw
        b -= lr * db
        if abs(w) > 1e6:  # 越走越远,已经发散了
            print(f"  第 {step} 步:w 已经变成 {w:.3g},发散了")
            return None
        if step in report:
            print(f"  第 {step:5d} 步:w={w:7.4f} b={b:7.3f} 损失 {mse(w, b, x, y):10.2f}")
    return w, b


print("\n== 3. 学习率 0.00005,走 20000 步")
descend(0.00005, 20000, report=(1, 10, 100, 1000, 5000, 20000))

print("\n== 4. 不同的学习率(都走 1000 步)")
for lr in [0.00001, 0.00005, 0.00009, 0.0001]:
    result = descend(lr, 1000)
    if result:
        w, b = result
        print(f"  学习率 {lr:<7}:w={w:.4f} b={b:.3f} 损失 {mse(w, b):.2f}")
    else:
        print(f"  学习率 {lr:<7}:发散")

# ---------- 5. 标准化:把面积换算成"均值为 0、标准差为 1"的数 ----------
mean, std = area.mean(), area.std()
area_n = (area - mean) / std
print(f"\n== 5. 面积标准化之后(均值 {mean:.1f},标准差 {std:.1f}),学习率 0.1 只走 100 步")
w_n, b_n = descend(0.1, 100, x=area_n, report=(1, 10, 50, 100))
# 换算回原来的单位:price = w_n * (area - mean) / std + b_n
print(f"  换算回原单位:w={w_n / std:.4f} b={b_n - w_n * mean / std:.3f}")