Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .translate/state/inventory_q.md.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
source-sha: 78030a3a27f6527046675bcd8a8d27995ca25af6
synced-at: "2026-07-22"
model: claude-opus-4-8
mode: NEW
source-sha: cbeb4b1a898aaa95fa7eca4a63e7f3e26b6a5569
synced-at: "2026-07-31"
model: claude-sonnet-5
mode: UPDATE
section-count: 4
tool-version: 0.20.0
tool-version: 0.24.0
41 changes: 16 additions & 25 deletions lectures/inventory_q.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ plt.rcParams['font.family'] = ['Source Han Serif SC']
from typing import NamedTuple
```


## 模型

我们研究一家公司,其经理试图通过控制库存来最大化股东价值。
Expand Down Expand Up @@ -170,8 +169,6 @@ $$

这里 $D$ 是一个分布为 $\phi$ 的随机变量。



## 通过值函数迭代求解

让我们从经理知道所有参数、函数形式和分布的设定开始。
Expand All @@ -197,7 +194,6 @@ $$

当 $r > 0$(等价地,$\beta < 1$)时,序列 $v_{k+1} = T v_k$ 收敛到唯一的不动点 $v^*$,它就是最优策略的值函数(例如,参见 {cite}`Sargent_Stachurski_2025`)。


### 模型设定

我们将模型基本要素存储在一个 `NamedTuple` 中。
Expand Down Expand Up @@ -285,7 +281,6 @@ def T(v, model):
return T_kernel(v, d_values, ϕ_values, c, κ, β, K)
```


### 计算贪婪策略

回想一下,给定值函数 $v$,**$v$-贪婪策略** 通过下式计算
Expand Down Expand Up @@ -371,13 +366,12 @@ v_star, σ_star = solve_inventory_model(v_init, model)

```{code-cell} ipython3
@numba.jit(nopython=True)
def sim_inventories(ts_length, σ, p, X_init=0, seed=0):
def sim_inventories(ts_length, σ, p, rng, X_init=0):
"""在策略 σ 下模拟库存动态。"""
np.random.seed(seed)
X = np.zeros(ts_length, dtype=np.int32)
X[0] = X_init
Comment on lines 368 to 372
for t in range(ts_length - 1):
d = np.random.geometric(p) - 1
d = rng.geometric(p) - 1
X[t+1] = max(X[t] - d, 0) + σ[X[t]]
return X
```
Expand All @@ -387,8 +381,8 @@ def sim_inventories(ts_length, σ, p, X_init=0, seed=0):
注意 **S-s 模式**:当库存降到较低水平时,公司下达一个大订单来补充库存(向上的跳跃),此后随着需求被满足,库存逐渐下降。

```{code-cell} ipython3
def plot_ts(ts_length=200, fontsize=10):
X = sim_inventories(ts_length, σ_star, p)
def plot_ts(ts_length=200, fontsize=10, seed=0):
X = sim_inventories(ts_length, σ_star, p, np.random.default_rng(seed))
fig, ax = plt.subplots()

ax.plot(X, label=r"$X_t$", alpha=0.7)
Expand All @@ -405,7 +399,6 @@ def plot_ts(ts_length=200, fontsize=10):
plot_ts()
```


## Q 学习

现在我们要问:一个智能体能否在不知道模型的情况下**学习**最优策略?
Expand All @@ -414,7 +407,6 @@ plot_ts()

相反,智能体只在与环境交互时观察到状态、动作和利润的序列。


### Q 因子贝尔曼方程

Q 学习的第一步是修改贝尔曼方程,将其转化为一种能够从这些有限信息中学习的形式。
Expand Down Expand Up @@ -451,7 +443,6 @@ $$

使用 $q$ 的一个优势是,最优策略可以直接读取为 $\sigma(x) = \arg\max_a q(x, a)$,而无需知道转移函数。


### Q 学习更新规则

Q 学习使用 **[随机逼近](https://en.wikipedia.org/wiki/Stochastic_approximation)** 来逼近 Q 因子贝尔曼方程的不动点。
Expand Down Expand Up @@ -484,7 +475,6 @@ $$

这些都是可直接观察的量——无需模型知识。


### Q 表和最大值的作用

理解更新规则如何与经理的动作相关联很重要。
Expand Down Expand Up @@ -525,7 +515,6 @@ $$

它衰减得足够慢,允许从后期(信息更充分)的更新中学习,同时仍满足收敛的 [Robbins–Monro 条件](https://en.wikipedia.org/wiki/Stochastic_approximation#Robbins%E2%80%93Monro_algorithm)。


### 探索:epsilon-贪婪

对于我们的行为策略,我们使用 $\varepsilon$-贪婪策略:
Expand Down Expand Up @@ -578,8 +567,7 @@ Q 学习循环在一条连续的轨迹中运行总共 `n_steps` 步——正如
```{code-cell} ipython3
@numba.jit(nopython=True)
def q_learning_kernel(K, p, c, κ, β, n_steps, X_init,
ε_init, ε_min, ε_decay, q_init, snapshot_steps, seed):
np.random.seed(seed)
ε_init, ε_min, ε_decay, q_init, snapshot_steps, rng):
q = np.full((K + 1, K + 1), q_init)
Comment on lines 569 to 571
n = np.zeros((K + 1, K + 1)) # 用于学习率的访问计数
ε = ε_init
Expand All @@ -590,7 +578,7 @@ def q_learning_kernel(K, p, c, κ, β, n_steps, X_init,

# 初始化状态和动作
x = X_init
a = np.random.randint(0, K - x + 1)
a = rng.integers(0, K - x + 1)

for t in range(n_steps):
# 如需要则记录策略快照
Expand All @@ -599,7 +587,7 @@ def q_learning_kernel(K, p, c, κ, β, n_steps, X_init,
snap_idx += 1

# === 抽取 D_{t+1} 并观察结果 ===
d = np.random.geometric(p) - 1
d = rng.geometric(p) - 1
reward = min(x, d) - c * a - κ * (a > 0)
x_next = max(x - d, 0) + a

Expand All @@ -619,8 +607,8 @@ def q_learning_kernel(K, p, c, κ, β, n_steps, X_init,

# === 行为策略:ε-贪婪(使用 a_next,即 argmax 动作)===
x = x_next
if np.random.random() < ε:
a = np.random.randint(0, K - x + 1)
if rng.random() < ε:
a = rng.integers(0, K - x + 1)
else:
a = a_next
ε = max(ε_min, ε * ε_decay)
Expand All @@ -638,8 +626,9 @@ def q_learning(model, n_steps=20_000_000, X_init=0,
K = len(x_values) - 1
if snapshot_steps is None:
snapshot_steps = np.array([], dtype=np.int64)
rng = np.random.default_rng(seed)
return q_learning_kernel(K, p, c, κ, β, n_steps, X_init,
ε_init, ε_min, ε_decay, q_init, snapshot_steps, seed)
ε_init, ε_min, ε_decay, q_init, snapshot_steps, rng)
```

接下来我们运行 $n$ = 500 万步,并在第 10,000 步、第 1,000,000 步和第 $n$ 步处拍摄策略快照。
Expand Down Expand Up @@ -716,7 +705,8 @@ X_init = K // 2
sim_seed = 5678

# 最优策略
X_opt = sim_inventories(ts_length, σ_star, p, X_init, seed=sim_seed)
X_opt = sim_inventories(ts_length, σ_star, p,
np.random.default_rng(sim_seed), X_init)
axes[0].plot(X_opt, alpha=0.7)
axes[0].set_ylabel("库存")
axes[0].set_title("最优 (VFI)")
Expand All @@ -725,7 +715,8 @@ axes[0].set_ylim(0, K + 2)
# Q 学习快照
for i in range(n_snaps):
σ_snap = snapshots[i]
X = sim_inventories(ts_length, σ_snap, p, X_init, seed=sim_seed)
X = sim_inventories(ts_length, σ_snap, p,
np.random.default_rng(sim_seed), X_init)
axes[i + 1].plot(X, alpha=0.7)
axes[i + 1].set_ylabel("库存")
axes[i + 1].set_title(f"第 {snap_steps[i]:,} 步")
Expand All @@ -734,4 +725,4 @@ for i in range(n_snaps):
axes[-1].set_xlabel(r"$t$")
plt.tight_layout()
plt.show()
```
```
Loading