<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Giraak Blog</title><description>AI / Systems / High-Performance Computing / Reinforcement Learning</description><link>https://giraak.space/</link><language>zh_CN</language><item><title>终身强化学习中的灾难性遗忘：从 LAICA 到 Cal-LAICA</title><link>https://giraak.space/posts/laica-catastrophic-forgetting/</link><guid isPermaLink="true">https://giraak.space/posts/laica-catastrophic-forgetting/</guid><description>系统梳理 LAICA 与 Cal-LAICA 的核心思想，详解 L-MDP 框架下的策略分解机制，并对比 EWC、GEM、PackNet 等灾难性遗忘缓解方法。</description><pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;问题背景&lt;/h2&gt;
&lt;p&gt;在标准强化学习中，我们通常假设动作空间固定不变。但在现实场景中——机器人安装新关节、推荐系统上架新商品、软件添加新功能——&lt;strong&gt;动作集是持续变化的&lt;/strong&gt;。传统 RL 算法每当动作集变化就需要从零训练。&lt;/p&gt;
&lt;p&gt;这是&lt;strong&gt;终身强化学习（Lifelong RL）&lt;/strong&gt; 要解决的核心问题。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;L-MDP：建模变化动作集的数学框架&lt;/h2&gt;
&lt;p&gt;传统的 MDP 定义为 $M = (S, A, P, R, \gamma)$，其中动作集 $A$ 是固定的。论文提出的 &lt;strong&gt;L-MDP（Lifelong MDP）&lt;/strong&gt; 扩展了这一框架：&lt;/p&gt;
&lt;p&gt;$$
L = (M_0, E, D, F)
$$&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;组件&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$M_0$&lt;/td&gt;
&lt;td&gt;初始 MDP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$E$&lt;/td&gt;
&lt;td&gt;潜在动作结构空间（所有可能动作的向量表示）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$D$&lt;/td&gt;
&lt;td&gt;新动作的采样分布&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$F$&lt;/td&gt;
&lt;td&gt;新动作被添加的频率&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;核心假设&lt;/strong&gt;：所有动作（当前的和未来的）都来源于一个更大的潜在结构空间 $E$，智能体只是逐步发现其中的部分动作。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;LAICA 的核心机制：策略分解&lt;/h2&gt;
&lt;p&gt;LAICA（Lifelong Adaptation and Improvement for Changing Actions）的核心思想是将策略拆分为两个组件：&lt;/p&gt;
&lt;p&gt;$$
\pi(a|s) = \sum_{\hat e} \beta(\hat e|s) \cdot \hat\phi(a|\hat e)
$$&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;组件&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;th&gt;是否随动作集变化&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;$\beta(s, \hat e)$&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;决策组件：在潜在空间中选择动作类型&lt;/td&gt;
&lt;td&gt;❌ 固定不变&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;$\hat\phi(\hat e, A)$&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;映射组件：将潜在动作映射到具体动作&lt;/td&gt;
&lt;td&gt;✅ 随新动作更新&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键优势&lt;/strong&gt;：$\beta$ 的参数维度不受动作集大小影响，旧知识完整保留。新动作加入时只需更新 $\hat\phi$。&lt;/p&gt;
&lt;h3&gt;两阶段学习&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;阶段&lt;/th&gt;
&lt;th&gt;目标&lt;/th&gt;
&lt;th&gt;优化对象&lt;/th&gt;
&lt;th&gt;是否需要奖励&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Adaptation（适应）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;快速学习新动作结构&lt;/td&gt;
&lt;td&gt;$\hat\phi$&lt;/td&gt;
&lt;td&gt;❌ 无监督学习&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Improvement（改进）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;优化整体策略性能&lt;/td&gt;
&lt;td&gt;$\beta$&lt;/td&gt;
&lt;td&gt;✅ 策略梯度&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Adaptation 阶段使用类似 VAE 的目标函数，通过逆动力学模型 $\phi(s, s&apos;) \to \hat e$ 和重建损失来学习动作嵌入，完全不依赖奖励信号，数据效率极高。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Cal-LAICA 的改进&lt;/h2&gt;
&lt;p&gt;Cal-LAICA 在 LAICA 基础上引入两项关键改进：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;LAICA 的问题&lt;/th&gt;
&lt;th&gt;Cal-LAICA 的解决方案&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;纯在线 RL，数据效率低&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CQL（Conservative Q-Learning）&lt;/strong&gt; 离线预训练&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q 值低估，恢复缓慢&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cal-QL 值函数校准&lt;/strong&gt;，确保新 Q 值不低于参考策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;无旧数据复用&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OORB（Online-Offline Replay Buffer）&lt;/strong&gt; 统一数据收集&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Cal-LAICA = &lt;strong&gt;LAICA + CQL + Cal-QL + OORB&lt;/strong&gt;，在 MiniGrid 迷宫环境（256 动作空间，5 阶段动作集开放）中表现出显著优势。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;灾难性遗忘缓解方法对比&lt;/h2&gt;
&lt;p&gt;在 LAICA 框架中引入额外缓解方法时，我们对比了三种经典方案：&lt;/p&gt;
&lt;h3&gt;EWC（Elastic Weight Consolidation）&lt;/h3&gt;
&lt;p&gt;$$\mathcal{L}_{EWC} = \frac{\lambda}{2} \sum_i F_i (\theta_i - \theta_i^*)^2$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以 Fisher 信息矩阵衡量每个参数对旧任务的重要性&lt;/li&gt;
&lt;li&gt;在训练新任务时对重要参数施加二次惩罚&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：实现简单，无需存储旧数据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：Fisher 估计的准确性影响效果&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;GEM（Gradient Episodic Memory）&lt;/h3&gt;
&lt;p&gt;$$\text{约束条件：} \langle g_{current}, g_k \rangle \geq 0 \quad \forall k$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;维护每个旧任务的少量样本（默认 128 条）&lt;/li&gt;
&lt;li&gt;投影当前梯度到不增加旧任务损失的方向&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：有理论保证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：需要额外存储 memory buffer&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;PackNet&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;每个任务完成后执行 L1 全局剪枝&lt;/li&gt;
&lt;li&gt;通过 gradient hook 冻结已训练权重&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优点&lt;/strong&gt;：完全隔离不同任务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：网络容量被逐步消耗&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;核心机制&lt;/th&gt;
&lt;th&gt;存储开销&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EWC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fisher 正则化&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;任务数多、存储受限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GEM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;梯度投影&lt;/td&gt;
&lt;td&gt;少量样本&lt;/td&gt;
&lt;td&gt;需要理论保证&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PackNet&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;剪枝 + 权重冻结&lt;/td&gt;
&lt;td&gt;无（但消耗容量）&lt;/td&gt;
&lt;td&gt;网络容量充足&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;理论保证&lt;/h2&gt;
&lt;p&gt;LAICA 的两个核心定理：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Theorem 1&lt;/strong&gt; — 性能上界：
$$v^{\mu^&lt;em&gt;}(s_0) - v^{\pi_k^&lt;/em&gt;}(s_0) \leq \frac{\gamma \rho \epsilon_k}{(1-\gamma)^2} R_{max}$$&lt;/p&gt;
&lt;p&gt;当前策略与&quot;上帝视角&quot;策略的差距由动作集稀疏度 $\epsilon_k$ 决定。随着动作集逐渐覆盖 $E$，$\epsilon_k \to 0$，性能差距消失。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Theorem 2&lt;/strong&gt; — 近似误差：
$$v^{\mu^*} - v^{\pi_k^{**}} \leq \frac{\gamma (\rho \epsilon_k + \delta_k)}{(1-\gamma)^2} R_{max}$$&lt;/p&gt;
&lt;p&gt;LAICA 的额外误差 $\delta_k$ 来自 $\hat\phi$ 的映射近似。Adaptation 阶段的目标就是最小化 $\delta_k$。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;关键局限&lt;/h2&gt;
&lt;p&gt;LAICA 依赖平滑性假设：相似的潜在动作产生相似的状态转移。当新动作与所有旧动作&lt;strong&gt;完全不相关&lt;/strong&gt;时（例如编程平台上突然加入烹饪教程），LAICA 的性能会退化到近似从零开始训练的水平。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;LAICA 通过策略分解巧妙地将&quot;变化动作集&quot;问题转化为潜在空间中的固定维度学习问题，Cal-LAICA 进一步通过离线 RL 和值函数校准提升了数据效率。在实际应用中，应根据任务相似度、存储约束和网络容量灵活选择 EWC/GEM/PackNet 等辅助方法。&lt;/p&gt;
</content:encoded></item><item><title>DQN 入门实践：用 PyTorch 从零实现深度 Q 网络</title><link>https://giraak.space/posts/dqn-from-scratch-pytorch/</link><guid isPermaLink="true">https://giraak.space/posts/dqn-from-scratch-pytorch/</guid><description>面向强化学习初学者的 DQN 完整教程，从环境搭建、神经网络构建到经验回放和 ε-贪婪策略，用 PyTorch 逐步实现。</description><pubDate>Fri, 20 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;什么是 DQN？&lt;/h2&gt;
&lt;p&gt;DQN（Deep Q-Network）是 DeepMind 在 2013 年提出的算法，首次将深度学习与 Q-Learning 结合，在 Atari 游戏上达到人类水平。它用神经网络来近似 Q 值函数 $Q(s, a)$，解决了传统 Q-Table 无法处理连续/高维状态空间的问题。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;整体架构&lt;/h2&gt;
&lt;p&gt;DQN 系统由五个核心模块组成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌─────────────┐    ┌──────────┐    ┌──────────────┐
│ Environment │───▶│  Agent   │───▶│ Q-Network    │
│ (Gymnasium) │    │ (ε-greedy)│   │ (3-layer MLP)│
└─────────────┘    └──────────┘    └──────────────┘
                          │
                   ┌──────▼──────┐
                   │   Memory    │
                   │ (Replay Buf)│
                   └─────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Environment&lt;/strong&gt;：Gymnasium 标准环境（如 CartPole）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent&lt;/strong&gt;：决策主体，使用 ε-贪婪策略&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Q-Network&lt;/strong&gt;：3 层 MLP 神经网络&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory&lt;/strong&gt;：经验回放缓冲区（Deque）&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;第一步：搭建 Q 网络&lt;/h2&gt;
&lt;p&gt;网络输入是状态（如 CartPole 的 4 维向量：位置、速度、角度、角速度），输出是每个动作的 Q 值：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch.nn as nn

class DQN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(DQN, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.fc2 = nn.Linear(hidden_size, hidden_size)
        self.fc3 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)  # 输出层不加激活函数
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于 CartPole：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;input_size=4&lt;/code&gt;（状态维度）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hidden_size=64&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;output_size=2&lt;/code&gt;（左/右两个动作）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;第二步：经验回放与 ε-贪婪策略&lt;/h2&gt;
&lt;h3&gt;经验回放缓冲区&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque
import random

class ReplayBuffer:
    def __init__(self, capacity=10000):
        self.buffer = deque(maxlen=capacity)

    def push(self, state, action, reward, next_state, done):
        self.buffer.append((state, action, reward, next_state, done))

    def sample(self, batch_size=32):
        return random.sample(self.buffer, batch_size)

    def __len__(self):
        return len(self.buffer)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;为什么需要经验回放？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;打破样本的时间相关性&lt;/li&gt;
&lt;li&gt;每个经验可被多次使用，提高数据效率&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;ε-贪婪策略&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;epsilon = 1.0          # 初始 100% 随机探索
epsilon_min = 0.01     # 保持至少 1% 探索
epsilon_decay = 0.995  # 每次衰减 0.5%

def select_action(state):
    if random.random() &amp;lt; epsilon:
        return random.randrange(n_actions)  # 探索
    else:
        with torch.no_grad():
            return q_network(state).argmax().item()  # 利用
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;第三步：Q-Learning 更新&lt;/h2&gt;
&lt;p&gt;核心公式——Bellman 方程的增量形式：&lt;/p&gt;
&lt;p&gt;$$Q(s, a) \leftarrow r + \gamma \cdot \max_{a&apos;} Q(s&apos;, a&apos;)$$&lt;/p&gt;
&lt;p&gt;在代码中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def train_step(batch):
    states, actions, rewards, next_states, dones = batch

    # 当前 Q 值
    current_q = q_network(states).gather(1, actions)

    # 目标 Q 值（使用 target network 稳定训练）
    with torch.no_grad():
        next_q = target_network(next_states).max(1)[0]
        target_q = rewards + gamma * next_q * (~dones)

    # MSE Loss
    loss = nn.functional.mse_loss(current_q.squeeze(), target_q)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;关键超参数&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$\gamma$ (gamma)&lt;/td&gt;
&lt;td&gt;0.99&lt;/td&gt;
&lt;td&gt;折扣因子，值越高越看重长期回报&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;batch_size&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;每次训练的样本数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lr&lt;/td&gt;
&lt;td&gt;0.001&lt;/td&gt;
&lt;td&gt;Adam 优化器学习率&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;target_update&lt;/td&gt;
&lt;td&gt;100 steps&lt;/td&gt;
&lt;td&gt;Target Network 的同步频率&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;第四步：完整训练循环&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;for episode in range(n_episodes):
    state, _ = env.reset()
    total_reward = 0

    for step in range(max_steps):
        action = select_action(state)
        next_state, reward, done, _, _ = env.step(action)

        memory.push(state, action, reward, next_state, done)

        if len(memory) &amp;gt;= batch_size:
            train_step(memory.sample())

        state = next_state
        total_reward += reward

        if done:
            break

    epsilon = max(epsilon_min, epsilon * epsilon_decay)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;从 CartPole 到更复杂的环境&lt;/h2&gt;
&lt;p&gt;这套框架的模块化设计让它很容易扩展到其他任务：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;换环境&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;env = gym.make(&apos;LunarLander-v2&apos;)    # 登月器
env = gym.make(&apos;Atari-Pong&apos;)        # 雅达利游戏
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;换网络&lt;/strong&gt;（图像输入用 CNN）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class CNN_DQN(nn.Module):
    def __init__(self):
        self.conv1 = nn.Conv2d(4, 32, 8, stride=4)
        self.conv2 = nn.Conv2d(32, 64, 4, stride=2)
        self.fc = nn.Linear(64 * 7 * 7, 512)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;自定义奖励函数&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if distance_to_target &amp;lt; 0.1:
    reward = 100       # 到达目标
else:
    reward = -0.01     # 时间惩罚，鼓励快速完成
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;DQN 将深度学习引入强化学习的核心创新在于两点：&lt;strong&gt;经验回放&lt;/strong&gt;打破样本相关性，&lt;strong&gt;Target Network&lt;/strong&gt; 稳定训练过程。从 3 层 MLP + CartPole 起步，理解 Q-Learning 和 Bellman 方程的本质后，逐步升级到 CNN + Atari 就是水到渠成的事。&lt;/p&gt;
</content:encoded></item><item><title>KTransformers 多并发架构深度分析：与 vLLM / SGLang 的对比</title><link>https://giraak.space/posts/ktransformers-concurrency/</link><guid isPermaLink="true">https://giraak.space/posts/ktransformers-concurrency/</guid><description>深入剖析 KTransformers v0.2.4 多并发架构的三层设计，分析 Prefill 并行度瓶颈与 KV Cache 管理问题，并与 vLLM、SGLang 进行全面对比。</description><pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;在大模型推理场景中，多并发能力直接决定了线上服务的吞吐上限。KTransformers 作为 DeepSeek 模型的开源推理框架，在 v0.2.4 版本引入了多并发支持。本文基于对 KTransformers 源码的深入分析，将其与 vLLM 和 SGLang 进行全面对比，找出瓶颈与优化方向。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;KTransformers 的三层架构&lt;/h2&gt;
&lt;p&gt;KTransformers v0.2.4 的多并发架构分为三层：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层次&lt;/th&gt;
&lt;th&gt;职责&lt;/th&gt;
&lt;th&gt;技术实现&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server（服务层）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;接收请求，提供 OpenAI 兼容 RESTful 接口&lt;/td&gt;
&lt;td&gt;HTTP API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scheduler（调度层）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;C++ 实现的 FCFS 批次调度器，支持 continuous batching&lt;/td&gt;
&lt;td&gt;C++ / FlashInfer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inference Engine（推理引擎）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;执行模型前向推理，chunked prefill&lt;/td&gt;
&lt;td&gt;FlashInfer CUDA Kernels&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;关键配置参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--chunk_size&lt;/code&gt;：单次引擎调用处理的最大 token 数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--max_batch_size&lt;/code&gt;：单次引擎运行同时处理的最大请求数（仅 &lt;code&gt;balance_serve&lt;/code&gt; 模式）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--cache_lens&lt;/code&gt;：全局 KVCache 空间大小，&lt;strong&gt;所有请求共享&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;核心瓶颈：Prefill 阶段并行度受限&lt;/h2&gt;
&lt;p&gt;社区实测反馈揭示了一个关键瓶颈：&lt;strong&gt;KTransformers 当前最多同时并发 2 个请求执行 Prefill&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这意味着 4 路并发时，只有两条请求的上下文同时加载到 KVCache，其余请求须排队等待。结果：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前两条请求较快获得解码结果&lt;/li&gt;
&lt;li&gt;后两条请求首包延迟显著增加&lt;/li&gt;
&lt;li&gt;GPU 计算资源未被充分利用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;作者在 Issue 回复中确认：&quot;prefill 阶段并发数限制为最多 2 个……&lt;code&gt;max_batch_size&lt;/code&gt; 只影响 decode 阶段，并不影响 prefill&quot;。&lt;/p&gt;
&lt;p&gt;相比之下，vLLM 默认开启 Chunked Prefill，优先安排解码请求，再将剩余算力用于新前缀填充；SGLang 的零开销调度器则能实现 CPU/GPU 异步重叠。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;KV Cache 管理对比&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;KTransformers&lt;/th&gt;
&lt;th&gt;vLLM&lt;/th&gt;
&lt;th&gt;SGLang&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;管理方式&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;全局共享 KVCache，固定大小&lt;/td&gt;
&lt;td&gt;PagedAttention 分块管理&lt;/td&gt;
&lt;td&gt;RadixAttention 前缀树&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;前缀重用&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;不支持&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;块级缓存重用&lt;/td&gt;
&lt;td&gt;Token 级别前缀复用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;内存效率&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;请求完成后才释放，易产生碎片&lt;/td&gt;
&lt;td&gt;按需分配/释放&lt;/td&gt;
&lt;td&gt;前缀共享，内存高效&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;KTransformers 的全局共享 KVCache 设计简洁，但缺少前缀重用机制使得多用户场景下各请求无法共享重叠部分，重复计算加剧了 GPU 内存和带宽压力。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;调度策略对比&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;KTransformers&lt;/th&gt;
&lt;th&gt;vLLM&lt;/th&gt;
&lt;th&gt;SGLang&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;调度算法&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FCFS 批次调度&lt;/td&gt;
&lt;td&gt;自适应动态批次，优先 Decode&lt;/td&gt;
&lt;td&gt;零开销连续批次，CPU/GPU 重叠&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prefill/Decode 并行&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;串行为主，Prefill 最多 2 路&lt;/td&gt;
&lt;td&gt;Chunked Prefill 交错执行&lt;/td&gt;
&lt;td&gt;持续批次混合执行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;多 GPU 并行&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;手动 &lt;code&gt;transfer_map&lt;/code&gt; 层拆分&lt;/td&gt;
&lt;td&gt;DP/TP/PP 支持&lt;/td&gt;
&lt;td&gt;DP + EP + PD 分离&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;七大优化方向&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;提升 Prefill 并发度&lt;/strong&gt;：取消或提高并行前填充限制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优化 Prefill/Decode 调度&lt;/strong&gt;：借鉴 vLLM 的优先解码 + 余量填充策略&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引入前缀缓存&lt;/strong&gt;：参考 vLLM PagedAttention 或 SGLang RadixAttention&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调整 Chunk 大小与批次策略&lt;/strong&gt;：根据硬件带宽优化参数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改进多卡并行&lt;/strong&gt;：引入自动张量并行/流水线并行&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;细粒度异步调度&lt;/strong&gt;：参考 SGLang 的零开销调度设计&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;充分利用硬件特性&lt;/strong&gt;：CUDA Graph、FP8/INT4 量化&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;KTransformers 在多并发场景下的主要短板是 Prefill 并行度不足和 KV Cache 管理的简化设计。以 vLLM 和 SGLang 为代表的框架已在调度策略、前缀缓存、多 GPU 并行等方面积累了成熟方案。后续版本若能在这些方向持续优化，KTransformers 有望在高并发推理场景中实现更接近硬件带宽极限的吞吐表现。&lt;/p&gt;
</content:encoded></item><item><title>LLM 基础设施安全：从 OWASP Top 10 到 CVE 漏洞实战</title><link>https://giraak.space/posts/llm-security/</link><guid isPermaLink="true">https://giraak.space/posts/llm-security/</guid><description>系统梳理 LLM 基础设施安全学习的三个阶段：Web/API 安全基础、CVE 漏洞复现（Ollama &amp; OpenWebUI）、容器与模型安全加固，附带完整工具链与实战路径。</description><pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;随着 LLM 基础设施（Ollama、OpenWebUI、vLLM 等）的快速部署，安全问题日益突出。这些系统通常直接暴露 API 端点、运行在容器中、处理敏感数据，一旦存在漏洞，攻击者可能实现&lt;strong&gt;远程代码执行（RCE）&lt;/strong&gt;、&lt;strong&gt;未授权数据访问&lt;/strong&gt;甚至&lt;strong&gt;模型篡改&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;本文是我在实际部署 DeepSeek 推理服务过程中梳理的安全学习路径，分为三个阶段：基础理论 → 漏洞实战 → 高级加固。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;第一阶段：Web 与 API 安全基础&lt;/h2&gt;
&lt;h3&gt;OWASP Top 10 — Web 安全的基石&lt;/h3&gt;
&lt;p&gt;在深入 LLM 专项安全之前，必须掌握 Web 应用安全的通用知识体系。OWASP Top 10 是最权威的 Web 安全风险清单，核心条目包括：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;漏洞类型&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;在 LLM 场景中的风险&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQL 注入&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;恶意 SQL 语句注入数据库查询&lt;/td&gt;
&lt;td&gt;LLM 应用的用户输入可能被拼接到查询&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;XSS（跨站脚本）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;注入恶意脚本到网页&lt;/td&gt;
&lt;td&gt;OpenWebUI 等前端界面的输入框&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CSRF（跨站请求伪造）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;诱导用户执行非本意操作&lt;/td&gt;
&lt;td&gt;API 调用缺乏 Token 验证&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IDOR（不安全的直接对象引用）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;通过修改 ID 访问他人资源&lt;/td&gt;
&lt;td&gt;这正是 CVE-2024-7041 的原理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;路径遍历&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;../../etc/passwd&lt;/code&gt; 访问系统文件&lt;/td&gt;
&lt;td&gt;CVE-2024-37032 的核心漏洞&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;命令注入&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;注入系统命令到服务器&lt;/td&gt;
&lt;td&gt;Ollama 的 API 参数可能被注入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;不安全的反序列化&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;恶意对象注入内存&lt;/td&gt;
&lt;td&gt;模型文件加载过程的潜在风险&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;API 安全基础&lt;/h3&gt;
&lt;p&gt;LLM 推理服务几乎全部通过 RESTful API 暴露能力，API 安全三要素：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;身份验证（Authentication）&lt;/strong&gt;：你是谁？→ API Key / JWT / OAuth&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;授权（Authorization）&lt;/strong&gt;：你能做什么？→ RBAC / Scope 限制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;速率限制（Rate Limiting）&lt;/strong&gt;：你能调用多少次？→ 防止滥用和 DoS&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OpenWebUI 默认情况下可以通过环境变量 &lt;code&gt;WEBUI_AUTH=False&lt;/code&gt; 禁用身份验证，这在个人使用场景方便，但暴露到公网时极其危险。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;第二阶段：漏洞发现与复现&lt;/h2&gt;
&lt;h3&gt;CVE-2024-37032 — Ollama 路径遍历 RCE&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;漏洞概述&lt;/strong&gt;：Ollama 的 API 端点存在路径遍历漏洞，攻击者可以读取服务器上的任意文件，进而在特定条件下实现远程代码执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;影响版本&lt;/strong&gt;：Ollama &amp;lt; 0.1.34&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;攻击原理&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /api/show HTTP/1.1
Host: target:11434
Content-Type: application/json

{
  &quot;name&quot;: &quot;../../../../etc/passwd&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ollama 的模型加载逻辑未对模型名称中的路径分隔符做充分校验，导致攻击者可以遍历到系统敏感文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;修复措施&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;升级到 Ollama ≥ 0.1.34&lt;/li&gt;
&lt;li&gt;不要将 Ollama 绑定到 &lt;code&gt;0.0.0.0&lt;/code&gt;（监听所有接口），改为 &lt;code&gt;127.0.0.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在前端使用 Nginx 反向代理，增加路径规范化层&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CVE-2024-7041 — OpenWebUI IDOR 漏洞&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;漏洞概述&lt;/strong&gt;：OpenWebUI 的某些 API 端点未正确校验用户身份，攻击者可以通过枚举 ID 访问其他用户的聊天记录、模型配置等敏感数据（典型的 IDOR 漏洞）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;攻击原理&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /api/v1/chats/1001 HTTP/1.1
Host: target:8080
Authorization: Bearer &amp;lt;attacker_token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;即使 &lt;code&gt;chat_id=1001&lt;/code&gt; 不属于当前用户，缺乏权限校验的端点仍会返回该聊天的完整内容——包括对话历史、Prompt 甚至 API Key。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;修复措施&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个 API 端点必须校验 &lt;code&gt;owner_id == current_user.id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;实现中间件级别的全局权限拦截&lt;/li&gt;
&lt;li&gt;对敏感操作添加审计日志&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;漏洞复现环境搭建&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 拉取有漏洞的版本
docker pull ollama/ollama:0.1.33

# 在隔离网络中运行
docker run -d --name vuln-ollama \
  --network isolated \
  -p 11434:11434 \
  ollama/ollama:0.1.33

# 使用 OWASP ZAP 扫描
zap-cli quick-scan http://localhost:11434
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;第三阶段：高级漏洞利用与防御&lt;/h2&gt;
&lt;h3&gt;容器安全&lt;/h3&gt;
&lt;p&gt;LLM 推理服务几乎全部依赖 Docker 部署，容器安全是最后一道防线：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;风险&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;缓解措施&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;以 root 运行&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;默认容器内为 root 用户&lt;/td&gt;
&lt;td&gt;&lt;code&gt;USER 1000:1000&lt;/code&gt; 切换为非 root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;特权模式&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--privileged&lt;/code&gt; 授予所有能力&lt;/td&gt;
&lt;td&gt;从不使用，按需 &lt;code&gt;--cap-add&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;挂载敏感路径&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-v /var/run/docker.sock:...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;禁止挂载 Docker Socket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;未限制资源&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;无 CPU/内存限制导致 DoS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--cpus=4 --memory=16g&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;# 安全加固的 Ollama 部署
FROM ollama/ollama:latest
RUN useradd -m -u 1000 ollama &amp;amp;&amp;amp; \
    chown -R ollama:ollama /home/ollama
USER ollama
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;模型完整性保护&lt;/h3&gt;
&lt;p&gt;模型文件的完整性直接决定推理结果的可信度：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;文件哈希校验&lt;/strong&gt;：下载模型后计算 SHA256，与官方发布值比对&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;签名验证&lt;/strong&gt;：使用 GPG 签名确保模型来源可信（HuggingFace 已支持）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只读挂载&lt;/strong&gt;：模型目录以 &lt;code&gt;:ro&lt;/code&gt; 挂载，防止推理服务篡改模型权重&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# 验证模型完整性
sha256sum deepseek-r1-q4_k_m.gguf
# 对比官方发布的 checksum
echo &quot;expected_hash deepseek-r1-q4_k_m.gguf&quot; | sha256sum -c
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;网络隔离架构&lt;/h3&gt;
&lt;p&gt;推荐的 LLM 服务部署拓扑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Internet
  │
  ▼
┌──────────────┐
│  Nginx (TLS) │  ← 反向代理 + WAF 规则
│  :443        │
└──────┬───────┘
       │
  ┌────▼────────────────────┐
  │  OpenWebUI (内网)       │  ← 仅暴露前端
  │  http://127.0.0.1:8080  │
  └────┬────────────────────┘
       │
  ┌────▼────────────────┐
  │  API Gateway        │  ← 鉴权 + 限流
  │  (Auth + Rate Limit)│
  └────┬────────────────┘
       │
  ┌────▼────────────┐
  │  Ollama / vLLM  │  ← 仅监听 127.0.0.1
  │  127.0.0.1:11434│
  └─────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;关键原则&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API 推理服务&lt;strong&gt;永远不直接暴露到公网&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;每一层都有独立的鉴权&lt;/li&gt;
&lt;li&gt;所有通信走 TLS&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;安全工具链&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;工具&lt;/th&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;用途&lt;/th&gt;
&lt;th&gt;适用阶段&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OWASP ZAP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DAST 扫描器&lt;/td&gt;
&lt;td&gt;自动扫描 Web 漏洞&lt;/td&gt;
&lt;td&gt;阶段一、二&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Burp Suite Community&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;拦截代理&lt;/td&gt;
&lt;td&gt;手动构造/重放 HTTP 请求&lt;/td&gt;
&lt;td&gt;阶段二&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Postman&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API 测试&lt;/td&gt;
&lt;td&gt;探索 API 端点，编写自动化测试&lt;/td&gt;
&lt;td&gt;阶段一、二&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nikto&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web 服务器扫描&lt;/td&gt;
&lt;td&gt;快速检查 6700+ 已知风险&lt;/td&gt;
&lt;td&gt;阶段二&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nmap&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;网络扫描&lt;/td&gt;
&lt;td&gt;识别开放端口和运行服务&lt;/td&gt;
&lt;td&gt;阶段二&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docker Bench Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;容器审计&lt;/td&gt;
&lt;td&gt;检查 Docker 配置合规性&lt;/td&gt;
&lt;td&gt;阶段三&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;快速安全评估脚本&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash
# LLM 基础设施快速安全检查

TARGET=&quot;localhost&quot;

echo &quot;=== 端口扫描 ===&quot;
nmap -p 11434,8080,3000,443,80 $TARGET

echo &quot;=== HTTP 头检查 ===&quot;
curl -sI http://$TARGET:11434 | head -20

echo &quot;=== OpenWebUI 认证检查 ===&quot;
curl -s http://$TARGET:8080/api/v1/chats \
  -H &quot;Authorization: Bearer invalid_token&quot; \
  | grep -q &quot;Unauthorized&quot; &amp;amp;&amp;amp; echo &quot;✅ 需要认证&quot; || echo &quot;❌ 认证可能被绕过&quot;

echo &quot;=== Docker 安全审计 ===&quot;
docker run --rm -it \
  --net host --pid host --userns host \
  -v /var/run/docker.sock:/var/run/docker.sock \
  docker/docker-bench-security | grep -E &quot;WARN|CRITICAL&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;学习路径总结&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;阶段一：基础理论（2-3 周）
├── OWASP Top 10 理解与练习
├── API 安全基础（AuthN / AuthZ / Rate Limit）
└── 工具熟悉（ZAP / Postman / Burp Suite）

阶段二：漏洞实战（2-3 周）
├── CVE-2024-37032 环境搭建与复现
├── CVE-2024-7041 手动漏洞利用
└── OWASP ZAP 自动化扫描

阶段三：高级加固（1-2 周）
├── Docker 容器安全配置
├── 模型完整性验证
├── 网络隔离架构设计
└── WAF 规则编写
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;LLM 基础设施安全本质上仍是经典 Web 安全 + 容器安全的延伸，真正的风险往往来自&lt;strong&gt;部署便利性对安全性的妥协&lt;/strong&gt;——为了快速启动而绑定 &lt;code&gt;0.0.0.0&lt;/code&gt;、关闭认证、以 root 运行容器。安全加固的每一点都是对便利性的取舍，但在生产环境中，这个取舍不容妥协。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;安全不是产品，而是过程。一次扫描通过不代表永远安全，持续监控和定期审计才是真正的防线。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>MQL5 算法交易入门：用 Python 构建你的第一个交易机器人</title><link>https://giraak.space/posts/mql5-trading/</link><guid isPermaLink="true">https://giraak.space/posts/mql5-trading/</guid><description>从零开始学习 MetaTrader 5 算法交易，详解 MT5 Python API 的行情获取、账户管理、订单操作与 EA 框架，并给出完整的交易机器人模板。</description><pubDate>Thu, 10 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;MetaTrader 5（MT5）是全球最广泛使用的交易平台之一，不仅支持手动交易，还提供了强大的算法交易（Algorithmic Trading）能力。通过 MT5 的 Python API（&lt;code&gt;MetaTrader5&lt;/code&gt; 包），我们可以用 Python 构建自己的&lt;strong&gt;智能交易机器人（Expert Advisor, EA）&lt;/strong&gt;，实现自动化策略回测和实盘交易。&lt;/p&gt;
&lt;p&gt;本文基于实际操作经验，系统梳理从环境搭建到完整 EA 框架的全流程。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;环境与基础概念&lt;/h2&gt;
&lt;h3&gt;EA 是什么？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;EA（Expert Advisor）&lt;/strong&gt; 是 MT5 中的智能交易系统，可以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自动获取实时行情数据&lt;/li&gt;
&lt;li&gt;根据预设策略自动开仓/平仓&lt;/li&gt;
&lt;li&gt;管理止损止盈&lt;/li&gt;
&lt;li&gt;7×24 小时无人值守运行&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Python vs MQL5 原生语言&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;Python API&lt;/th&gt;
&lt;th&gt;MQL5 原生 (C++ like)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;上手难度&lt;/td&gt;
&lt;td&gt;低，Python 生态丰富&lt;/td&gt;
&lt;td&gt;中，类 C++ 语法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据科学能力&lt;/td&gt;
&lt;td&gt;✅ pandas / numpy / sklearn&lt;/td&gt;
&lt;td&gt;❌ 需自行实现&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;执行速度&lt;/td&gt;
&lt;td&gt;慢（通过网络通信）&lt;/td&gt;
&lt;td&gt;快（平台内部执行）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;适用场景&lt;/td&gt;
&lt;td&gt;研究、回测、中低频策略&lt;/td&gt;
&lt;td&gt;高频策略、平台内运行&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：研究和开发阶段用 Python，生产环境可考虑 MQL5 原生。&lt;/p&gt;
&lt;h3&gt;连接 MT5&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import MetaTrader5 as mt5

if not mt5.initialize():
    print(&quot;MT5 初始化失败，请检查终端是否运行&quot;)
    quit()

print(&quot;MT5 连接成功&quot;)
# 显示账户信息
print(mt5.account_info())

# 结束时务必调用
# mt5.shutdown()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;信息获取：读懂市场数据&lt;/h2&gt;
&lt;p&gt;MT5 Python API 提供了 8 个核心函数用于获取交易所需的各类信息。&lt;/p&gt;
&lt;h3&gt;1. 交易品种详情：&lt;code&gt;symbol_info()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;symbol = mt5.symbol_info(&quot;EURUSD&quot;)
print(f&quot;点值: {symbol.point}&quot;)      # 最小价格变动单位 → 0.00001
print(f&quot;当前点差: {symbol.spread}&quot;)  # 买卖价差 → 17点
print(f&quot;小数位: {symbol.digits}&quot;)    # 报价小数位数 → 5
print(f&quot;最小手数: {symbol.volume_min}&quot;)  # → 0.01
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回的 namedtuple 包含 80+ 字段，涵盖交易模式、隔夜利息、保证金要求等全部交易参数。&lt;/p&gt;
&lt;h3&gt;2. 历史 K 线：&lt;code&gt;copy_rates_from_pos()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import pandas as pd

rates = mt5.copy_rates_from_pos(&quot;EURUSD&quot;, mt5.TIMEFRAME_H1, 0, 100)
df = pd.DataFrame(rates)
df[&quot;time&quot;] = pd.to_datetime(df[&quot;time&quot;], unit=&quot;s&quot;)  # 时间戳转换
df.set_index(&quot;time&quot;, inplace=True)

# 列：open, high, low, close, tick_volume, spread, real_volume
print(df.tail())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间周期常量：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;常量&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mt5.TIMEFRAME_M1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 分钟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mt5.TIMEFRAME_M5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5 分钟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mt5.TIMEFRAME_H1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 小时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mt5.TIMEFRAME_D1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;日线&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;3. 实时报价：&lt;code&gt;symbol_info_tick()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;tick = mt5.symbol_info_tick(&quot;EURUSD&quot;)
print(f&quot;买价(Bid): {tick.bid}&quot;)   # 可卖出的价格
print(f&quot;卖价(Ask): {tick.ask}&quot;)   # 可买入的价格
print(f&quot;最后成交: {tick.last}&quot;)
print(f&quot;点差: {(tick.ask - tick.bid) / symbol.point:.0f} 点&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 账户信息：&lt;code&gt;account_info()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;account = mt5.account_info()
print(f&quot;余额: {account.balance}&quot;)
print(f&quot;净值: {account.equity}&quot;)       # 余额 + 浮动盈亏
print(f&quot;已用保证金: {account.margin}&quot;)
print(f&quot;可用保证金: {account.margin_free}&quot;)
print(f&quot;杠杆: 1:{account.leverage}&quot;)
print(f&quot;浮动盈亏: {account.profit}&quot;)

# 计算保证金使用率
margin_level = (account.equity / account.margin * 100) if account.margin &amp;gt; 0 else 0
print(f&quot;保证金水平: {margin_level:.1f}%&quot;)  # &amp;lt;100% 时可能触发强平
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5-6. 持仓与挂单&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 获取所有持仓
for pos in mt5.positions_get():
    direction = &quot;多&quot; if pos.type == 0 else &quot;空&quot;
    print(f&quot;[{pos.ticket}] {pos.symbol} {direction} &quot;
          f&quot;手数:{pos.volume} 开仓价:{pos.price_open} &quot;
          f&quot;当前价:{pos.price_current} 盈亏:{pos.profit}&quot;)

# 获取活跃挂单
for order in mt5.orders_get():
    type_map = {2: &quot;Buy Limit&quot;, 3: &quot;Sell Limit&quot;, 4: &quot;Buy Stop&quot;, 5: &quot;Sell Stop&quot;}
    print(f&quot;[{order.ticket}] {order.symbol} {type_map.get(order.type, &apos;?&apos;)} &quot;
          f&quot;挂单价:{order.price_open} 剩余手数:{order.volume_current}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7. 历史订单：&lt;code&gt;history_orders_get()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from datetime import datetime, timezone

# 获取过去 24 小时的历史订单
start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0)
end = datetime.now(timezone.utc)
history = mt5.history_orders_get(start, end)

for order in history:
    if order.state == 3:  # 已成交
        print(f&quot;[{order.ticket}] {order.symbol} 成交价:{order.price_open}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8. 全部可交易品种：&lt;code&gt;symbols_get()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;all_symbols = mt5.symbols_get()
forex_pairs = [s.name for s in all_symbols if s.name.endswith((&apos;USD&apos;, &apos;EUR&apos;, &apos;GBP&apos;, &apos;JPY&apos;))]
print(f&quot;外汇品种数: {len(forex_pairs)}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;交易操作：从下单到平仓&lt;/h2&gt;
&lt;h3&gt;市价单&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def market_order(symbol, volume, order_type, sl=0, tp=0, deviation=20, magic=123456):
    &quot;&quot;&quot;发送市价单&quot;&quot;&quot;
    tick = mt5.symbol_info_tick(symbol)
    request = {
        &quot;action&quot;: mt5.TRADE_ACTION_DEAL,
        &quot;symbol&quot;: symbol,
        &quot;volume&quot;: volume,
        &quot;type&quot;: mt5.ORDER_TYPE_BUY if order_type == &quot;BUY&quot; else mt5.ORDER_TYPE_SELL,
        &quot;price&quot;: tick.ask if order_type == &quot;BUY&quot; else tick.bid,
        &quot;sl&quot;: sl,
        &quot;tp&quot;: tp,
        &quot;deviation&quot;: deviation,       # 允许最大滑点（点数）
        &quot;magic&quot;: magic,               # EA 标识符
        &quot;comment&quot;: &quot;Python EA&quot;,
        &quot;type_time&quot;: mt5.ORDER_TIME_GTC,
        &quot;type_filling&quot;: mt5.ORDER_FILLING_FOK,  # 全成交或取消
    }

    result = mt5.order_send(request)
    if result.retcode == 0:
        print(f&quot;✅ 市价单成功 订单ID:{result.order}&quot;)
    else:
        print(f&quot;❌ 下单失败: {result.comment}&quot;)
    return result

# 使用示例
market_order(&quot;EURUSD&quot;, 0.1, &quot;BUY&quot;, sl=1.08000, tp=1.09000)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;挂单与修改&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def pending_order(symbol, volume, order_type, price, sl=0, tp=0):
    &quot;&quot;&quot;下挂单&quot;&quot;&quot;
    request = {
        &quot;action&quot;: mt5.TRADE_ACTION_PENDING,
        &quot;symbol&quot;: symbol,
        &quot;volume&quot;: volume,
        &quot;type&quot;: order_type,  # ORDER_TYPE_BUY_LIMIT / SELL_LIMIT 等
        &quot;price&quot;: price,
        &quot;sl&quot;: sl,
        &quot;tp&quot;: tp,
        &quot;type_time&quot;: mt5.ORDER_TIME_GTC,
        &quot;type_filling&quot;: mt5.ORDER_FILLING_FOK,
    }
    return mt5.order_send(request)

# 修改止损止盈
mt5.order_modify(ticket=123456789, sl=1.07500, tp=1.09500)  # ticket 为订单ID
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;取消挂单与平仓&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 取消挂单
mt5.order_cancel(ticket=987654321)

# 平仓（需要持仓的 ticket）
mt5.position_close(ticket=1122334455)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;下单前校验&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def safe_order(symbol, volume, order_type, sl=0, tp=0):
    &quot;&quot;&quot;下单前先校验参数&quot;&quot;&quot;
    tick = mt5.symbol_info_tick(symbol)
    request = {
        &quot;action&quot;: mt5.TRADE_ACTION_DEAL,
        &quot;symbol&quot;: symbol,
        &quot;volume&quot;: volume,
        &quot;type&quot;: mt5.ORDER_TYPE_BUY if order_type == &quot;BUY&quot; else mt5.ORDER_TYPE_SELL,
        &quot;price&quot;: tick.ask if order_type == &quot;BUY&quot; else tick.bid,
        &quot;sl&quot;: sl,
        &quot;tp&quot;: tp,
        &quot;deviation&quot;: 20,
        &quot;magic&quot;: 123456,
        &quot;type_time&quot;: mt5.ORDER_TIME_GTC,
        &quot;type_filling&quot;: mt5.ORDER_FILLING_FOK,
    }

    # 预校验
    check = mt5.order_check(request)
    if check.retcode != 0:
        print(f&quot;⚠️ 订单校验失败: {check.comment}&quot;)
        return None

    # 校验通过，执行下单
    return mt5.order_send(request)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;EA 完整框架&lt;/h2&gt;
&lt;p&gt;以下是一个可直接运行的 EA 模板，包含完整的生命周期管理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import MetaTrader5 as mt5
import time
from datetime import datetime

class TradingBot:
    def __init__(self, symbol=&quot;EURUSD&quot;, magic=123456):
        self.symbol = symbol
        self.magic = magic
        self.running = False

    def initialize(self):
        &quot;&quot;&quot;初始化：连接 MT5&quot;&quot;&quot;
        if not mt5.initialize():
            raise ConnectionError(&quot;MT5 初始化失败&quot;)

        account = mt5.account_info()
        print(f&quot;✅ 已连接 | 账户:{account.login} | &quot;
              f&quot;余额:{account.balance} | 杠杆:1:{account.leverage}&quot;)

    def get_market_data(self):
        &quot;&quot;&quot;获取市场数据&quot;&quot;&quot;
        tick = mt5.symbol_info_tick(self.symbol)
        rates = mt5.copy_rates_from_pos(self.symbol, mt5.TIMEFRAME_M5, 0, 20)
        return tick, rates

    def strategy(self, tick, rates):
        &quot;&quot;&quot;
        策略逻辑 — 在这里实现你的交易策略
        返回: &quot;BUY&quot;, &quot;SELL&quot;, &quot;HOLD&quot;
        &quot;&quot;&quot;
        # 示例：简单的均线交叉策略
        if len(rates) &amp;lt; 20:
            return &quot;HOLD&quot;

        ma5 = sum(r[&quot;close&quot;] for r in rates[-5:]) / 5
        ma20 = sum(r[&quot;close&quot;] for r in rates[-20:]) / 20

        if ma5 &amp;gt; ma20 * 1.001:   # 金叉
            return &quot;BUY&quot;
        elif ma5 &amp;lt; ma20 * 0.999:  # 死叉
            return &quot;SELL&quot;
        return &quot;HOLD&quot;

    def execute_trade(self, signal):
        &quot;&quot;&quot;执行交易信号&quot;&quot;&quot;
        positions = mt5.positions_get(symbol=self.symbol)
        has_position = len(positions) &amp;gt; 0

        if signal == &quot;BUY&quot; and not has_position:
            self._market_order(mt5.ORDER_TYPE_BUY)
        elif signal == &quot;SELL&quot; and not has_position:
            self._market_order(mt5.ORDER_TYPE_SELL)
        elif signal == &quot;HOLD&quot; and has_position:
            pass  # 保持持仓

    def _market_order(self, order_type):
        tick = mt5.symbol_info_tick(self.symbol)
        request = {
            &quot;action&quot;: mt5.TRADE_ACTION_DEAL,
            &quot;symbol&quot;: self.symbol,
            &quot;volume&quot;: 0.1,
            &quot;type&quot;: order_type,
            &quot;price&quot;: tick.ask if order_type == mt5.ORDER_TYPE_BUY else tick.bid,
            &quot;deviation&quot;: 20,
            &quot;magic&quot;: self.magic,
            &quot;type_time&quot;: mt5.ORDER_TIME_GTC,
            &quot;type_filling&quot;: mt5.ORDER_FILLING_FOK,
        }

        result = mt5.order_send(request)
        direction = &quot;BUY&quot; if order_type == mt5.ORDER_TYPE_BUY else &quot;SELL&quot;
        if result.retcode == 0:
            print(f&quot;✅ [{datetime.now():%H:%M:%S}] 开仓 {direction} @ {tick.ask if order_type == mt5.ORDER_TYPE_BUY else tick.bid}&quot;)
        else:
            print(f&quot;❌ 下单失败: {result.comment}&quot;)

    def run(self):
        &quot;&quot;&quot;主循环&quot;&quot;&quot;
        self.running = True
        print(&quot;🚀 EA 已启动...&quot;)

        while self.running:
            try:
                tick, rates = self.get_market_data()
                signal = self.strategy(tick, rates)
                self.execute_trade(signal)
                time.sleep(1)  # 控制频率，避免过载
            except KeyboardInterrupt:
                self.stop()
            except Exception as e:
                print(f&quot;⚠️ 运行异常: {e}&quot;)
                time.sleep(5)

    def stop(self):
        &quot;&quot;&quot;清理与断开&quot;&quot;&quot;
        self.running = False
        mt5.shutdown()
        print(&quot;👋 EA 已停止&quot;)


if __name__ == &quot;__main__&quot;:
    bot = TradingBot(symbol=&quot;EURUSD&quot;, magic=123456)
    try:
        bot.initialize()
        bot.run()
    except Exception as e:
        print(f&quot;❌ 致命错误: {e}&quot;)
    finally:
        bot.stop()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;MQL5 原生 EA 骨架&lt;/h2&gt;
&lt;p&gt;如果策略需要高频执行或部署到生产环境，可以参考 MQL5 原生的 EA 结构（类 C++ 语法）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#property copyright &quot;Your Name&quot;
#property version   &quot;1.00&quot;

int OnInit()
{
    // 初始化：验证参数、设置指标
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    // 清理：删除图形对象、关闭文件句柄
}

void OnTick()
{
    // 核心逻辑 — 每个 tick 执行一次
    // 1. 获取行情
    // 2. 策略判断
    // 3. 执行订单
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;安全实践&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先模拟后实盘&lt;/strong&gt;：所有策略必须在模拟账户上验证&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;风险控制&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;单笔最大亏损限制&lt;/li&gt;
&lt;li&gt;每日最大亏损/盈利停止&lt;/li&gt;
&lt;li&gt;保证金使用率监控&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;订单校验&lt;/strong&gt;：下单前使用 &lt;code&gt;order_check()&lt;/code&gt; 验证参数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异常处理&lt;/strong&gt;：网络断开、MT5 崩溃等情况要有恢复机制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日志记录&lt;/strong&gt;：每笔交易和异常都要记录，便于复盘&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;MT5 的 Python API 为量化交易提供了完整的基础设施：8 个信息获取函数覆盖了行情、账户、持仓的全部数据需求，5 个交易函数实现了从下单到平仓的完整生命周期。在此基础上构建的 EA 框架可以快速迭代策略，同时保留向 MQL5 原生迁移的路径。&lt;/p&gt;
&lt;p&gt;对于算法交易初学者，建议路径：&lt;strong&gt;Python API 回测 → 模拟账户验证 → 小资金实盘 → 策略优化/原生迁移&lt;/strong&gt;。&lt;/p&gt;
</content:encoded></item><item><title>DeepSeek-R1 在 Tesla T4 上的推理优化实践</title><link>https://giraak.space/posts/deepseek-t4-optimization/</link><guid isPermaLink="true">https://giraak.space/posts/deepseek-t4-optimization/</guid><description>探索在 NVIDIA Tesla T4 (Turing 架构) GPU 上优化 DeepSeek-R1 推理的七种方法，涵盖量化、剪枝、蒸馏、llama.cpp、内核融合与 CPU-GPU 混合推理。</description><pubDate>Fri, 28 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;NVIDIA Tesla T4 是云端推理场景中广泛使用的 GPU，但其 Turing 架构（Compute Capability 7.5）不支持 vLLM 和 SGLang 等主流推理框架的优化方法。在将 DeepSeek-R1 部署到 T4 集群时，需要探索替代优化方案。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;T4 的硬件约束&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;架构&lt;/strong&gt;：Turing (SM 7.5)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;显存&lt;/strong&gt;：16 GB GDDR6&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不兼容&lt;/strong&gt;：vLLM / SGLang 的 FP8 和 FlashAttention 优化路径&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可用方案&lt;/strong&gt;：llama.cpp（支持 CUDA CC 5.0+）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;七大优化方法&lt;/h2&gt;
&lt;h3&gt;1. 模型量化（Quantization）&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PTQ（训练后量化）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;降低权重和激活值精度，减小模型大小&lt;/td&gt;
&lt;td&gt;快速部署，可能损失精度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QAT（量化感知训练）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;训练过程中集成量化&lt;/td&gt;
&lt;td&gt;资源允许微调时精度更高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;预量化模型&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;从 HuggingFace 直接使用 GGUF 等量化版本&lt;/td&gt;
&lt;td&gt;最实用的起步方案&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;GGUF 格式的量化模型配合 llama.cpp 是 T4 上的首选方案。&lt;/p&gt;
&lt;h3&gt;2. 剪枝与稀疏性&lt;/h3&gt;
&lt;p&gt;移除不重要的权重或连接来减小模型规模。但老旧的 Turing 架构缺少专用稀疏矩阵运算硬件，剪枝带来的速度提升有限。&lt;/p&gt;
&lt;h3&gt;3. 知识蒸馏&lt;/h3&gt;
&lt;p&gt;使用 DeepSeek R1 作为教师模型，训练更小的学生模型（如 DeepSeek-R1-Distill-Qwen 系列），在性能和资源占用间取得平衡。&lt;/p&gt;
&lt;h3&gt;4. llama.cpp&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;最推荐的方案。&lt;/strong&gt; llama.cpp 对旧 GPU 兼容性最佳，支持：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GGUF 多精度量化格式&lt;/li&gt;
&lt;li&gt;CUDA 计算能力 5.0+&lt;/li&gt;
&lt;li&gt;CPU-GPU 混合推理（部分层卸载到 CPU）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# T4 上的 llama.cpp 推理示例
./llama-cli -m deepseek-r1-q4_k_m.gguf \
  -ngl 20 \         # GPU 层数
  -c 4096 \         # 上下文长度
  -t 8              # CPU 线程数
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 内核融合&lt;/h3&gt;
&lt;p&gt;将多个 GPU 操作合并为单个内核以减少 kernel launch 开销。需要深入 CUDA 编程和对 Turing 架构的特定知识。&lt;/p&gt;
&lt;h3&gt;6. CPU-GPU 混合推理&lt;/h3&gt;
&lt;p&gt;当模型超出 T4 16GB 显存时，使用 llama.cpp 将部分层卸载到 CPU 内存。虽然比纯 GPU 推理慢，但使超大模型的推理成为可能。&lt;/p&gt;
&lt;h3&gt;7. KTransformers 异构计算&lt;/h3&gt;
&lt;p&gt;利用 KTransformers 的 GPU/CPU 异构分配能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MLA 注意力（算术强度 ~512）&lt;/strong&gt; → GPU Tensor Cores&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MoE 专家模块（算术强度 ~0.075）&lt;/strong&gt; → CPU（仅 6/160 专家被激活）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# KTransformers YAML 配置示例
- name: lm_head
  device: cpu
- name: moe_experts
  device: cpu
- name: attention
  device: cuda:0
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;方法选择决策树&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;模型是否适配 16GB 显存？
├── 是 → GGUF 量化 + llama.cpp（纯 GPU）
└── 否 → CPU-GPU 混合推理
    ├── 有 CPU 计算资源 → KTransformers 异构计算
    └── 接受性能损失 → 知识蒸馏到小模型
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;在 T4 上部署 DeepSeek-R1 需要灵活组合多种优化手段。短期最优路径是 &lt;strong&gt;GGUF 量化模型 + llama.cpp&lt;/strong&gt;；长期来看，&lt;strong&gt;KTransformers 的 GPU/CPU 异构计算&lt;/strong&gt; 和 &lt;strong&gt;知识蒸馏&lt;/strong&gt; 提供了更好的性能天花板。关键是理解每种方法的算力和内存权衡，根据实际负载选择合适的组合策略。&lt;/p&gt;
</content:encoded></item></channel></rss>