{
    "version": "https://jsonfeed.org/version/1",
    "title": "PAB's Blog",
    "subtitle": "",
    "icon": "https://www.bondrewd.com/assets/favicon.ico",
    "description": "记录生活与技术",
    "home_page_url": "https://www.bondrewd.com",
    "items": [
        {
            "id": "https://www.bondrewd.com/2026/07/19/hello-world/",
            "url": "https://www.bondrewd.com/2026/07/19/hello-world/",
            "title": "Hello World",
            "date_published": "2026-07-19T11:07:32.556Z",
            "content_html": "<p>Welcome to <a href=\"https://hexo.io/\">Hexo</a>! This is your very first post. Check <a href=\"https://hexo.io/docs/\">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href=\"https://hexo.io/docs/troubleshooting.html\">troubleshooting</a> or you can ask me on <a href=\"https://github.com/hexojs/hexo/issues\">GitHub</a>.</p>\n<h3 id=\"quick-start\"><a class=\"anchor\" href=\"#quick-start\">#</a> Quick Start</h3>\n<h4 id=\"create-a-new-post\"><a class=\"anchor\" href=\"#create-a-new-post\">#</a> Create a new post</h4>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>$ hexo new \"My New Post\"</span></span></code></pre>\n<p>More info: <a href=\"https://hexo.io/docs/writing.html\">Writing</a></p>\n<h4 id=\"run-server\"><a class=\"anchor\" href=\"#run-server\">#</a> Run server</h4>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>$ hexo server</span></span></code></pre>\n<p>More info: <a href=\"https://hexo.io/docs/server.html\">Server</a></p>\n<h4 id=\"generate-static-files\"><a class=\"anchor\" href=\"#generate-static-files\">#</a> Generate static files</h4>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>$ hexo generate</span></span></code></pre>\n<p>More info: <a href=\"https://hexo.io/docs/generating.html\">Generating</a></p>\n<h4 id=\"deploy-to-remote-sites\"><a class=\"anchor\" href=\"#deploy-to-remote-sites\">#</a> Deploy to remote sites</h4>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>$ hexo deploy</span></span></code></pre>\n<p>More info: <a href=\"https://hexo.io/docs/one-command-deployment.html\">Deployment</a></p>\n",
            "tags": []
        },
        {
            "id": "https://www.bondrewd.com/2026/07/17/seldon-core-1/",
            "url": "https://www.bondrewd.com/2026/07/17/seldon-core-1/",
            "title": "Seldon Core 1",
            "date_published": "2026-07-17T07:58:34.000Z",
            "content_html": "<p>Seldon Core一个运行在 Kubernetes 上的机器学习模型服务平台，用来把训练好的模型部署成可访问、可扩缩、可观测的在线推理服务。这里学习的是1版本。</p>\n<p>它解决的不是“怎么训练模型”，而是：</p>\n<ul>\n<li>模型训练好了，怎么部署上线？</li>\n<li>怎么暴露 REST / gRPC 接口？</li>\n<li>怎么做版本切换、流量分配？</li>\n<li>怎么同时管理大量模型？</li>\n<li>怎么监控延迟、错误率和资源使用？</li>\n<li>怎么把多个模型、预处理和后处理串成推理流水线？</li>\n</ul>\n<blockquote>\n<p>当前官方重点已经转向 <strong>Seldon Core 2</strong>。Core 2 被定位为 Kubernetes 上的 MLOps/LLMOps 框架，可以管理单模型、多个模型以及模块化推理应用。</p>\n</blockquote>\n<h3 id=\"架构\"><a class=\"anchor\" href=\"#架构\">#</a> 架构</h3>\n<p>Seldon Core 1 在K8s 上增加了一个更高层的抽象：SeldonDeployment。</p>\n<p>这里描述的是模型推理服务，Seldon Operator 再将它转换成 k8s 能运行的资源。</p>\n<h4 id=\"认识-seldondeployment\"><a class=\"anchor\" href=\"#认识-seldondeployment\">#</a> 认识 SeldonDeployment</h4>\n<p>官方将 <code>SeldonDeployment</code> 定义为 Kubernetes 自定义资源，用户通过它描述模型组件和推理图。</p>\n<p>一个简单示例如下：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-yaml\"><span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">apiVersion</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> machinelearning.seldon.io/v1</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">kind</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> SeldonDeployment</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">metadata</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> iris-model</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  namespace</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> seldon</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">spec</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> iris</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  predictors</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> default</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      replicas</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      graph</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> classifier</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        type</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MODEL</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        implementation</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> SKLEARN_SERVER</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        modelUri</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> gs://seldon-models/sklearn/iris</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      componentSpecs</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> spec</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">            containers</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">              -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> classifier</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">                resources</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">                  requests</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">                    cpu</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 100m</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">                    memory</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 256Mi</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">                  limits</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">                    cpu</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">                    memory</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1Gi</span></span></code></pre>\n<p>这一份 YAML 里混合了两类信息：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>模型语义</span></span>\n<span class=\"line\"><span>+</span></span>\n<span class=\"line\"><span>Kubernetes 运行配置</span></span></code></pre>\n<ol>\n<li>\n<p><code>spec.predictors</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>predictors:</span></span>\n<span class=\"line\"><span>    - name: default</span></span></code></pre>\n<p>一个 predictors 可以理解为：一套可以独立接收推理请求的模型服务配置。</p>\n<p>其中可以包含：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>模型；</span></span>\n<span class=\"line\"><span>输入转换器；</span></span>\n<span class=\"line\"><span>输出转换器；</span></span>\n<span class=\"line\"><span>路由器；</span></span>\n<span class=\"line\"><span>组合器；</span></span>\n<span class=\"line\"><span>副本数量；</span></span>\n<span class=\"line\"><span>Pod 配置。</span></span></code></pre>\n</li>\n<li>\n<p><code>replicas</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>repicas: 1</span></span></code></pre>\n<p>描述该 predictor 的副本数量。但实际生成资源时，可能根据图结构和组件配置创建一个或多个 Deployment，因此不要机械认为一个 predictor 永远只对应一个 Deployment。</p>\n</li>\n<li>\n<p><code>graph</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>graph:</span></span>\n<span class=\"line\"><span>  name: classifier</span></span>\n<span class=\"line\"><span>  type: MODEL</span></span>\n<span class=\"line\"><span>  implementation: SKLEARN_SERVER</span></span>\n<span class=\"line\"><span>  modelUri: gs://seldon-models/sklearn/iris</span></span></code></pre>\n<p>是 Seldon 相比普通 Deployment 最核心的能力之一，描述的是推理组件之间的调用关系。</p>\n<p>只有一个模型时：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>request</span></span>\n<span class=\"line\"><span>   ↓</span></span>\n<span class=\"line\"><span>classifier</span></span>\n<span class=\"line\"><span>   ↓</span></span>\n<span class=\"line\"><span>response</span></span></code></pre>\n<p>稍微复杂一点：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>request</span></span>\n<span class=\"line\"><span>   ↓</span></span>\n<span class=\"line\"><span>input-transformer</span></span>\n<span class=\"line\"><span>   ↓</span></span>\n<span class=\"line\"><span>classifier</span></span>\n<span class=\"line\"><span>   ↓</span></span>\n<span class=\"line\"><span>output-transformer</span></span>\n<span class=\"line\"><span>   ↓</span></span>\n<span class=\"line\"><span>response</span></span></code></pre>\n<p>还可以存在：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>request</span></span>\n<span class=\"line\"><span>   ↓</span></span>\n<span class=\"line\"><span>router</span></span>\n<span class=\"line\"><span> ┌─┴─────────┐</span></span>\n<span class=\"line\"><span> ↓           ↓</span></span>\n<span class=\"line\"><span>model-a    model-b</span></span>\n<span class=\"line\"><span> └────┬──────┘</span></span>\n<span class=\"line\"><span>      ↓</span></span>\n<span class=\"line\"><span>   combiner</span></span>\n<span class=\"line\"><span>      ↓</span></span>\n<span class=\"line\"><span>  response</span></span></code></pre>\n<p>Seldon Core 1 官方文档说明，推理图可以由模型以及 router、combiner、输入和输出 transformer 等组件构成。</p>\n<p>普通 Kubernetes Deployment 只知道：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>运行哪些容器</span></span></code></pre>\n<p>SeldonDeployment 还知道：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>这些模型组件之间应该如何调用</span></span></code></pre>\n<p><code>graph</code> 是根节点，<code>children</code> 表示连接的下一层节点，多个 <code>-</code> 表示多个并列节点，<code>implementation</code> 选择预打包服务器，<code>modelUri</code> 指向模型目录。</p>\n</li>\n<li>\n<p><code>implementation</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>implementation: SKLEARN_SERVER</span></span></code></pre>\n<p>意思是：</p>\n<blockquote>\n<p>使用 Seldon 已经准备好的 sklearn 推理服务器运行模型。</p>\n</blockquote>\n<p>Seldon Core 1 提供了多种预打包推理服务器，例如：</p>\n<ul>\n<li>SKLearn Server；</li>\n<li>XGBoost Server；</li>\n<li>TensorFlow Serving；</li>\n<li>MLflow Server；</li>\n<li>自定义服务器。</li>\n</ul>\n<p>因此你不一定要自己完整实现：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>HTTP Server</span></span>\n<span class=\"line\"><span>请求反序列化</span></span>\n<span class=\"line\"><span>模型加载</span></span>\n<span class=\"line\"><span>predict 调用</span></span>\n<span class=\"line\"><span>响应序列化</span></span></code></pre>\n<p>这是 Seldon 比“直接写 Deployment”多做的一层标准化。</p>\n<blockquote>\n<p>什么是预打包服务器？</p>\n<p><strong>预</strong>：提前准备好</p>\n<p><strong>打包</strong>：把代码、依赖库、运行环境放进一个容器镜像里</p>\n<p>“服务器”则表示它不是普通脚本，而是一个会持续运行、接收网络请求并返回预测结果的程序。</p>\n<p>所以预打包服务器指：提前打包好的，用于运行模型的服务器程序。之所以叫这个名字，核心原因就是：服务器代码和运行环境已经由 Seldon 提前封装好了，你不需要自己再写和配置。</p>\n</blockquote>\n</li>\n<li>\n<p><code>modelUri</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>modelUri: gs://seldon-models/sklearn/iris</span></span></code></pre>\n<p>描述模型文件的位置，而不是容器镜像的位置。</p>\n<p>Seldon 可以使用预打包模型服务器，然后根据 <code>modelUri</code> 加载模型。官方 SKLearn 示例就是通过 <code>implementation</code> 和 <code>modelUri</code> 描述模型服务。</p>\n</li>\n<li>\n<p><code>conponentSpec</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>componentSpecs:</span></span>\n<span class=\"line\"><span>  - spec:</span></span>\n<span class=\"line\"><span>      containers:</span></span>\n<span class=\"line\"><span>        - name: classifier</span></span>\n<span class=\"line\"><span>          resources:</span></span>\n<span class=\"line\"><span>            requests:</span></span>\n<span class=\"line\"><span>              cpu: 100m</span></span>\n<span class=\"line\"><span>              memory: 256Mi</span></span></code></pre>\n<p>非常接近 Kubernetes 的：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>PodTemplateSpec</span></span></code></pre>\n<p>它用于描述：</p>\n<ul>\n<li>容器；</li>\n<li>环境变量；</li>\n<li>CPU、内存；</li>\n<li>Volume；</li>\n<li>VolumeMount；</li>\n<li>安全上下文；</li>\n<li>节点选择；</li>\n<li>GPU 资源；</li>\n<li>其他 Pod 配置。</li>\n</ul>\n<p>因此可以这样理解：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>graph</span></span></code></pre>\n<p>描述模型推理逻辑。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>componentSpecs</span></span></code></pre>\n<p>描述模型组件如何在 Kubernetes 中运行。</p>\n</li>\n</ol>\n<h4 id=\"sldon-operator\"><a class=\"anchor\" href=\"#sldon-operator\">#</a> Sldon Operator</h4>\n<p>核心循环抽象：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>func Reconcile(request Request) &#123;</span></span>\n<span class=\"line\"><span>    // 1. 获取 SeldonDeployment</span></span>\n<span class=\"line\"><span>    sdep := getSeldonDeployment(request)</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>    // 2. 根据 spec 计算期望资源</span></span>\n<span class=\"line\"><span>    desiredDeployments := buildDeployments(sdep)</span></span>\n<span class=\"line\"><span>    desiredServices := buildServices(sdep)</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>    // 3. 查询当前资源</span></span>\n<span class=\"line\"><span>    currentDeployments := getDeployments(sdep)</span></span>\n<span class=\"line\"><span>    currentServices := getServices(sdep)</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>    // 4. 对比期望状态与当前状态</span></span>\n<span class=\"line\"><span>    diff := compare(</span></span>\n<span class=\"line\"><span>        desiredDeployments,</span></span>\n<span class=\"line\"><span>        currentDeployments,</span></span>\n<span class=\"line\"><span>    )</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>    // 5. 创建、更新或删除资源</span></span>\n<span class=\"line\"><span>    apply(diff)</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>    // 6. 更新状态</span></span>\n<span class=\"line\"><span>    updateStatus(sdep)</span></span>\n<span class=\"line\"><span>&#125;</span></span></code></pre>\n<h4 id=\"与直接写-deployment-相比\"><a class=\"anchor\" href=\"#与直接写-deployment-相比\">#</a> 与直接写 Deployment 相比</h4>\n<ol>\n<li>\n<p>模型语义 Deployment 只知道运行镜像，SeldonDeployment 能表达：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>这是 MODEL</span></span>\n<span class=\"line\"><span>这是 ROUTER</span></span>\n<span class=\"line\"><span>这是 TRANSFORMER</span></span>\n<span class=\"line\"><span>这是 COMBINER</span></span></code></pre>\n</li>\n<li>\n<p>推理图</p>\n<p>Deployment 本身不能直接表达：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>请求 → 预处理 → 模型 → 后处理</span></span></code></pre>\n<p>SeldonDeployment 可以通过 <code>graph</code> 描述这种关系。</p>\n</li>\n<li>\n<p>标准推理服务器</p>\n<p>普通 Deployment 通常需要你自己准备完整模型服务镜像。</p>\n<p>Seldon 可以使用预打包服务器运行 sklearn、XGBoost、MLflow 等模型。</p>\n</li>\n</ol>\n",
            "tags": [
                "seldon core"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/22-%E7%B3%BB%E7%BB%9F%E5%8A%A0%E5%9B%BA%EF%BC%9A%E7%89%B9%E6%9D%83%E5%AE%B9%E5%99%A8%E9%80%83%E9%80%B8%E4%B8%8E%E9%85%8D%E7%BD%AE%E5%AE%89%E5%85%A8%E4%B8%8A%E4%B8%8B%E6%96%87%E9%99%90%E5%88%B6/",
            "url": "https://www.bondrewd.com/2026/07/16/22-%E7%B3%BB%E7%BB%9F%E5%8A%A0%E5%9B%BA%EF%BC%9A%E7%89%B9%E6%9D%83%E5%AE%B9%E5%99%A8%E9%80%83%E9%80%B8%E4%B8%8E%E9%85%8D%E7%BD%AE%E5%AE%89%E5%85%A8%E4%B8%8A%E4%B8%8B%E6%96%87%E9%99%90%E5%88%B6/",
            "title": "22-系统加固：特权容器逃逸与配置安全上下文限制",
            "date_published": "2026-07-16T06:01:22.000Z",
            "content_html": "<h5 id=\"心智模型\"><a class=\"anchor\" href=\"#心智模型\">#</a> 心智模型</h5>\n<p>攻击者容易利用特权容器从受限容器环境侵入宿主机操作系统。</p>\n<h5 id=\"️-实操手册逃逸演示与防御配置\"><a class=\"anchor\" href=\"#️-实操手册逃逸演示与防御配置\">#</a> 🛠️ 实操手册：逃逸演示与防御配置</h5>\n<h5 id=\"第一阶段演示特权容器逃逸危险操作仅限实验\"><a class=\"anchor\" href=\"#第一阶段演示特权容器逃逸危险操作仅限实验\">#</a> 第一阶段：演示特权容器逃逸（危险操作，仅限实验）</h5>\n<p>我们将演示攻击者如何利用特权容器直接访问宿主机的磁盘。</p>\n<ol>\n<li><strong>部署一个特权 Pod</strong>： 注意 <code>privileged: true</code> 这个开关。</li>\n</ol>\n<p>YAML</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: v1</span></span>\n<span class=\"line\"><span>kind: Pod</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: privileged-escape-demo</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  containers:</span></span>\n<span class=\"line\"><span>  - name: attacker-container</span></span>\n<span class=\"line\"><span>    image: alpine</span></span>\n<span class=\"line\"><span>    command: [\"sh\", \"-c\", \"sleep 3600\"]</span></span>\n<span class=\"line\"><span>    securityContext:</span></span>\n<span class=\"line\"><span>      privileged: true  # 开启特权模式</span></span></code></pre>\n<ol>\n<li><strong>执行逃逸攻击</strong>： 特权容器可以看到宿主机的设备文件。我们可以直接挂载宿主机的根磁盘。</li>\n</ol>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>## 进入容器</span></span>\n<span class=\"line\"><span>kubectl exec -it privileged-escape-demo -- sh</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>## 在容器内，查看宿主机的磁盘设备（通常是 /dev/sda1 或 /dev/nvme0n1p1）</span></span>\n<span class=\"line\"><span>fdisk -l</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>## 创建一个挂载点并挂载宿主机根目录</span></span>\n<span class=\"line\"><span>mkdir /host_root</span></span>\n<span class=\"line\"><span>mount /dev/sda1 /host_root  # 请根据 fdisk 结果替换设备名</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>## 现在，你已经在容器里看到了宿主机的所有敏感文件</span></span>\n<span class=\"line\"><span>ls /host_root/etc/shadow</span></span>\n<span class=\"line\"><span>ls /host_root/root/.ssh/</span></span></code></pre>\n<blockquote>\n<p><strong>结论</strong>：一旦开启 <code>privileged: true</code>，容器与宿主机之间已无安全可言。</p>\n</blockquote>\n<hr />\n<h5 id=\"第二阶段配置安全上下文限制权限提升\"><a class=\"anchor\" href=\"#第二阶段配置安全上下文限制权限提升\">#</a> 第二阶段：配置安全上下文限制权限提升</h5>\n<p>仅仅不开启 <code>privileged</code> 就够了吗？不够。 很多进程会利用 <code>setuid</code>（如 <code>sudo</code> 命令）在运行时尝试获取更高级别的权限。我们需要通过 <code>allowPrivilegeEscalation: false</code> 封死这条路。</p>\n<ol>\n<li><strong>编写安全加固的 Pod 配置</strong>：</li>\n</ol>\n<p>YAML</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: v1</span></span>\n<span class=\"line\"><span>kind: Pod</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: hardened-pod</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  containers:</span></span>\n<span class=\"line\"><span>  - name: safe-container</span></span>\n<span class=\"line\"><span>    image: alpine</span></span>\n<span class=\"line\"><span>    command: [\"sh\", \"-c\", \"sleep 3600\"]</span></span>\n<span class=\"line\"><span>    securityContext:</span></span>\n<span class=\"line\"><span>      # 1. 明确禁止特权模式</span></span>\n<span class=\"line\"><span>      privileged: false</span></span>\n<span class=\"line\"><span>      # 2. 禁止进程通过 setuid 等方式获取比父进程更高的权限</span></span>\n<span class=\"line\"><span>      allowPrivilegeEscalation: false</span></span>\n<span class=\"line\"><span>      # 3. 配合之前的实验，以非 root 用户运行</span></span>\n<span class=\"line\"><span>      runAsNonRoot: true</span></span>\n<span class=\"line\"><span>      runAsUser: 1000</span></span></code></pre>\n<hr />\n<h5 id=\"核心参数深度解析\"><a class=\"anchor\" href=\"#核心参数深度解析\">#</a> 🔍 核心参数深度解析</h5>\n<p><code>allowPrivilegeEscalation</code> (禁止权限提升)</p>\n<ul>\n<li><strong>它是干什么的？</strong> 它控制 <code>no_new_privs</code> 内核标志。如果设为 <code>false</code>，即使一个程序设置了 <code>SUID</code> 位（比如一个只有 root 才能跑的程序），普通用户也无法通过它获得 root 权限。</li>\n<li><strong>为什么重要？</strong> 很多提权漏洞（Exploit）依赖于在容器内通过特殊手段获取 root。关掉这个开关，就砍掉了攻击者的“阶梯”。</li>\n</ul>\n<p><code>readOnlyRootFilesystem</code> (只读根文件系统)</p>\n<ul>\n<li>\n<p><strong>建议配合使用</strong>：</p>\n<p>YAML</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>securityContext:</span></span>\n<span class=\"line\"><span>  readOnlyRootFilesystem: true</span></span></code></pre>\n</li>\n<li>\n<p><strong>效果</strong>：容器的整个根目录变成只读。如果黑客想下载一个木马脚本到 <code>/tmp</code> 或 <code>/bin</code>，会直接被内核拦截。</p>\n</li>\n</ul>\n",
            "tags": [
                "k8s 实验"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/21-%E7%B3%BB%E7%BB%9F%E5%8A%A0%E5%9B%BA%EF%BC%9Aseccomp/",
            "url": "https://www.bondrewd.com/2026/07/16/21-%E7%B3%BB%E7%BB%9F%E5%8A%A0%E5%9B%BA%EF%BC%9Aseccomp/",
            "title": "21-系统加固：Seccomp",
            "date_published": "2026-07-16T06:00:35.000Z",
            "content_html": "<p><strong>Seccomp (Secure Computing mode)</strong> 是 Linux 内核的一项功能，用于限制容器可以发起的<strong>系统调用 (System Calls)</strong>。</p>\n<h5 id=\"️-实操手册配置-seccomp-profile\"><a class=\"anchor\" href=\"#️-实操手册配置-seccomp-profile\">#</a> 🛠️ 实操手册：配置 Seccomp Profile</h5>\n<p>在 Kubernetes 中，Seccomp 已经比 AppArmor 更加标准化，直接集成在 <code>securityContext</code> 字段中。</p>\n<h5 id=\"第一阶段使用-runtimedefault-最推荐的生产配置\"><a class=\"anchor\" href=\"#第一阶段使用-runtimedefault-最推荐的生产配置\">#</a> 第一阶段：使用 <code>RuntimeDefault</code> (最推荐的生产配置)</h5>\n<p>这是容器运行时（如 containerd 或 Docker）自带的一套“安全模板”，它禁用了大约 40 多个危险的系统调用。</p>\n<ol>\n<li><strong>编写 Pod 配置文件</strong>： 与 AppArmor 不同，Seccomp 建议直接写在 <code>spec</code> 字段中。</li>\n</ol>\n<p>YAML</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: v1</span></span>\n<span class=\"line\"><span>kind: Pod</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: seccomp-default-pod</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  securityContext:</span></span>\n<span class=\"line\"><span>    # 在 Pod 级别应用 seccomp</span></span>\n<span class=\"line\"><span>    seccompProfile:</span></span>\n<span class=\"line\"><span>      type: RuntimeDefault</span></span>\n<span class=\"line\"><span>  containers:</span></span>\n<span class=\"line\"><span>  - name: web-app</span></span>\n<span class=\"line\"><span>    image: nginx</span></span></code></pre>\n<hr />\n<h5 id=\"第二阶段自定义-seccomp-profile-精细化控制\"><a class=\"anchor\" href=\"#第二阶段自定义-seccomp-profile-精细化控制\">#</a> 第二阶段：自定义 Seccomp Profile (精细化控制)</h5>\n<p>有时候默认配置还是太宽松，或者你需要运行极其特殊的应用。</p>\n<ol>\n<li>\n<p><strong>在 Worker 节点创建 Profile 目录</strong>： Kubelet 默认在特定的安全目录下寻找 Profile。</p>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span># 在所有 Worker 节点执行</span></span>\n<span class=\"line\"><span>sudo mkdir -p /var/lib/kubelet/seccomp/profiles</span></span></code></pre>\n</li>\n<li>\n<p><strong>编写 JSON Profile</strong>： 创建一个名为 <code>audit-only.json</code> 的文件，它的作用是：允许大部分操作，但对某些操作记录日志（用于审计）。</p>\n</li>\n</ol>\n<p>JSON</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>&#123;</span></span>\n<span class=\"line\"><span>    \"defaultAction\": \"SCMP_ACT_ALLOW\",</span></span>\n<span class=\"line\"><span>    \"architectures\": [</span></span>\n<span class=\"line\"><span>        \"SCMP_ARCH_X86_64\"</span></span>\n<span class=\"line\"><span>    ],</span></span>\n<span class=\"line\"><span>    \"syscalls\": [</span></span>\n<span class=\"line\"><span>        &#123;</span></span>\n<span class=\"line\"><span>            \"names\": [</span></span>\n<span class=\"line\"><span>                \"reboot\"</span></span>\n<span class=\"line\"><span>            ],</span></span>\n<span class=\"line\"><span>            \"action\": \"SCMP_ACT_KILL\"</span></span>\n<span class=\"line\"><span>        &#125;</span></span>\n<span class=\"line\"><span>    ]</span></span>\n<span class=\"line\"><span>&#125;</span></span></code></pre>\n<blockquote>\n<p><strong>语法解释</strong>：如果进程尝试调用 <code>reboot</code>（重启），内核会直接杀掉 (<code>SCMP_ACT_KILL</code>) 该进程。</p>\n</blockquote>\n<ol>\n<li><strong>将文件放入 Worker 节点指定路径</strong>： 将此 JSON 放在 <code>/var/lib/kubelet/seccomp/profiles/audit-only.json</code>。</li>\n<li><strong>在 Pod 中引用自定义 Profile</strong>：</li>\n</ol>\n<p>YAML</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: v1</span></span>\n<span class=\"line\"><span>kind: Pod</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: seccomp-custom-pod</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  containers:</span></span>\n<span class=\"line\"><span>  - name: test-container</span></span>\n<span class=\"line\"><span>    image: busybox</span></span>\n<span class=\"line\"><span>    command: [\"sh\", \"-c\", \"sleep 3600\"]</span></span>\n<span class=\"line\"><span>    securityContext:</span></span>\n<span class=\"line\"><span>      seccompProfile:</span></span>\n<span class=\"line\"><span>        type: Localhost</span></span>\n<span class=\"line\"><span>        # 注意：这里的路径是相对于 /var/lib/kubelet/seccomp/ 的相对路径</span></span>\n<span class=\"line\"><span>        localhostProfile: profiles/audit-only.json</span></span></code></pre>\n<hr />\n<h5 id=\"第三阶段验证实验结果\"><a class=\"anchor\" href=\"#第三阶段验证实验结果\">#</a> 第三阶段：验证实验结果</h5>\n<ol>\n<li>\n<p><strong>部署 Pod</strong>：</p>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>kubectl apply -f seccomp-custom-pod.yaml</span></span></code></pre>\n</li>\n<li>\n<p><strong>测试拦截效果</strong>： 由于我们设置了 <code>reboot</code> 就会被 <code>KILL</code>，我们可以进入容器尝试触发这个调用。</p>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>kubectl exec seccomp-custom-pod -- reboot</span></span></code></pre>\n</li>\n<li>\n<p><strong>预期现象</strong>： 你会发现连接被断开，或者提示 <code>command terminated with exit code 137</code>。这是因为内核检测到非法调用，瞬间终止了进程。</p>\n</li>\n</ol>\n<h5 id=\"避坑指南\"><a class=\"anchor\" href=\"#避坑指南\">#</a> 避坑指南</h5>\n<ul>\n<li><strong>路径陷阱</strong>：在 Pod YAML 中写 <code>localhostProfile</code> 时，<strong>不要</strong>写绝对路径 <code>/var/lib/...</code>。Kubelet 会自动拼凑前缀。</li>\n<li><strong>权限陷阱</strong>：由于 Seccomp 需要在每个节点部署 JSON 文件，建议在生产中使用 <strong>DaemonSet</strong> 来自动把 Profile 文件同步到所有节点的磁盘上。</li>\n</ul>\n",
            "tags": [
                "k8s 实验"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/20-%E7%B3%BB%E7%BB%9F%E5%8A%A0%E5%9B%BA%EF%BC%9Aapparmor/",
            "url": "https://www.bondrewd.com/2026/07/16/20-%E7%B3%BB%E7%BB%9F%E5%8A%A0%E5%9B%BA%EF%BC%9Aapparmor/",
            "title": "20-系统加固：Apparmor",
            "date_published": "2026-07-16T05:59:46.000Z",
            "content_html": "<h5 id=\"心智模型\"><a class=\"anchor\" href=\"#心智模型\">#</a> 心智模型</h5>\n<p>最小权限原则，要求每个程序或系统进程这你能访问其任务所必须的信息和资源。</p>\n<h5 id=\"实验部署和引用-apparmor-profile\"><a class=\"anchor\" href=\"#实验部署和引用-apparmor-profile\">#</a> 实验：部署和引用 AppArmor Profile</h5>\n<h5 id=\"第一阶段在-worker-节点准备-profile\"><a class=\"anchor\" href=\"#第一阶段在-worker-节点准备-profile\">#</a> 第一阶段：在 Worker 节点准备 Profile</h5>\n<p>AppArmor 是 Linux 内核模块，因此 Profile 必须加载到<strong>每一个</strong>运行 Pod 的节点内核中。</p>\n<ol>\n<li><strong>登录到你的 Worker 节点</strong>。</li>\n<li><strong>创建 Profile 文件</strong>：创建一个名为 <code>k8s-deny-write</code> 的配置文件，禁止容器写入任何文件。</li>\n</ol>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>cat &#x3C;&#x3C;EOF > /etc/apparmor.d/k8s-deny-write</span></span>\n<span class=\"line\"><span>profile k8s-deny-write flags=(attach_disconnected) &#123;</span></span>\n<span class=\"line\"><span>  # 包含基本抽象</span></span>\n<span class=\"line\"><span>  include &#x3C;abstractions/base></span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>  # 拒绝所有写入操作</span></span>\n<span class=\"line\"><span>  deny /** w,</span></span>\n<span class=\"line\"><span>&#125;</span></span>\n<span class=\"line\"><span>EOF</span></span></code></pre>\n<ol>\n<li><strong>加载 Profile 到内核</strong>： 使用 <code>apparmor_parser</code> 命令解析并加载该配置文件。</li>\n</ol>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>sudo apparmor_parser -r -W /etc/apparmor.d/k8s-deny-write</span></span></code></pre>\n<ol>\n<li><strong>确认加载成功</strong>：</li>\n</ol>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>sudo aa-status  grep k8s-deny-write</span></span></code></pre>\n<hr />\n<p>第二阶段：在 Pod 中通过 Annotation 引用</p>\n<p>在 Kubernetes 1.30 之前的版本中，AppArmor 主要通过 <strong>Annotation (注解)</strong> 进行配置。</p>\n<ol>\n<li><strong>编写 Pod 部署文件</strong>： 注意 Annotation 的格式：<code>container.apparmor.security.beta.kubernetes.io/&lt;container_name&gt;: localhost/&lt;profile_name&gt;</code>。</li>\n</ol>\n<p>YAML</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: v1</span></span>\n<span class=\"line\"><span>kind: Pod</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: apparmor-test-pod</span></span>\n<span class=\"line\"><span>  annotations:</span></span>\n<span class=\"line\"><span>    # 这里的 'test-container' 必须匹配下方的容器名</span></span>\n<span class=\"line\"><span>    # 'localhost/k8s-deny-write' 表示引用节点本地加载的 profile</span></span>\n<span class=\"line\"><span>    container.apparmor.security.beta.kubernetes.io/test-container: localhost/k8s-deny-write</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  containers:</span></span>\n<span class=\"line\"><span>  - name: test-container</span></span>\n<span class=\"line\"><span>    image: busybox</span></span>\n<span class=\"line\"><span>    command: [\"sh\", \"-c\", \"echo 'Hello AppArmor' &#x26;&#x26; sleep 3600\"]</span></span></code></pre>\n<ol>\n<li><strong>部署 Pod</strong>：</li>\n</ol>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl apply -f apparmor-pod.yaml</span></span></code></pre>\n<hr />\n<h5 id=\"第三阶段验证防御效果\"><a class=\"anchor\" href=\"#第三阶段验证防御效果\">#</a> 第三阶段：验证防御效果</h5>\n<p>我们要验证“最小权限”是否生效：</p>\n<ol>\n<li><strong>尝试在容器内创建文件</strong>：</li>\n</ol>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl exec apparmor-test-pod -- touch /tmp/test.txt</span></span></code></pre>\n<ol>\n<li><strong>预期结果</strong>： 系统应报错：<code>touch: /tmp/test.txt: Permission denied</code>。</li>\n<li><strong>查看内核日志</strong>（在 Worker 节点上）：</li>\n</ol>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>dmesg  grep -i apparmor</span></span></code></pre>\n<p>你会看到类似 <code>apparmor=&quot;DENIED&quot;</code> 的记录，这证明内核成功拦截了违规操作。</p>\n<h5 id=\"关键要点总结\"><a class=\"anchor\" href=\"#关键要点总结\">#</a> 💡 关键要点总结</h5>\n<p><strong>步骤</strong></p>\n<p><strong>核心操作</strong></p>\n<p><strong>注意事项</strong></p>\n<p><strong>节点侧</strong></p>\n<p>加载 Profile 到内核</p>\n<p>必须在<strong>所有</strong>潜在的 Worker 节点上执行。</p>\n<p><strong>集群侧</strong></p>\n<p>Pod Annotation 引用</p>\n<p>格式必须极其精确，否则 Pod 可能无法启动或静默失败。</p>\n<p><strong>维护</strong></p>\n<p>审计与调优</p>\n<p>生产环境通常先使用 <code>complain</code> 模式（仅记录不拦截）观察业务需求，再转为 <code>enforce</code>。</p>\n<blockquote>\n<p><strong>注意：</strong> 从 Kubernetes 1.30 开始，AppArmor 已进入正式版 (GA)，推荐开始使用 <code>securityContext.appArmorProfile</code> 字段替代 Annotation，但目前社区中 Annotation 仍是最通用的学习和迁移方式。</p>\n</blockquote>\n",
            "tags": [
                "k8s 实验"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/19-%E9%9B%86%E7%BE%A4%E8%AE%BE%E7%BD%AE%EF%BC%9Atls-ingress-%E5%AE%9E%E9%AA%8C/",
            "url": "https://www.bondrewd.com/2026/07/16/19-%E9%9B%86%E7%BE%A4%E8%AE%BE%E7%BD%AE%EF%BC%9Atls-ingress-%E5%AE%9E%E9%AA%8C/",
            "title": "19-集群设置：TLS Ingress 实验",
            "date_published": "2026-07-16T05:56:40.000Z",
            "content_html": "<p>实验在 Killercoda 进行，自己搭的网络实在是太麻烦了。</p>\n<h5 id=\"步骤-1安装-nginx-ingress-controller\"><a class=\"anchor\" href=\"#步骤-1安装-nginx-ingress-controller\">#</a> <strong>步骤 1：安装 NGINX Ingress Controller</strong></h5>\n<p>Killercoda 的默认集群没有自带 Ingress 控制器，我们需要手动部署官方的裸机（Bare-metal）版本。</p>\n<p>在终端中执行以下命令：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> apply</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/baremetal/deploy.yaml</span></span></code></pre>\n<p><em>提示：安装需要拉取镜像并启动 Pod，你可以运行 <code>kubectl get pods -n ingress-nginx</code> 查看状态，等到 <code>ingress-nginx-controller</code> 开头的 Pod 变成 <code>Running</code> 状态再继续。</em></p>\n<hr />\n<h5 id=\"步骤-2生成自签名的-tls-证书\"><a class=\"anchor\" href=\"#步骤-2生成自签名的-tls-证书\">#</a> <strong>步骤 2：生成自签名的 TLS 证书</strong></h5>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">openssl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> req</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -x509</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -nodes</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -days</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 365</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -newkey</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> rsa:2048</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">  -keyout</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> tls.key</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -out</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> tls.crt</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">  -subj</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">/CN=my-secure-app.com/O=my-local-test</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span></code></pre>\n<p><strong><code>-x509</code></strong>: <strong>最关键的参数</strong>。告诉 OpenSSL 直接输出一个<strong>自签名证书</strong></p>\n<p><strong><code>-nodes</code></strong>: 读作 “no DES”。意思是<strong>不对私钥进行加密</strong>。这样生成的 <code>tls.key</code> 文件就不需要密码保护。在 Kubernetes Secret 中使用时，必须加这个参数，否则 Pod 启动时会因为无法输入密码解密私钥而报错。</p>\n<p><strong><code>-newkey rsa:2048</code></strong>: 同时生成一个新的私钥。指定算法为 <strong>RSA</strong>，长度为 <strong>2048</strong> 位</p>\n<p><strong><code>-days 365</code></strong>: 设置证书的有效期。这里是 1 年。</p>\n<p><strong><code>-keyout tls.key</code></strong>: 指定生成的<strong>私钥</strong>保存的文件名。</p>\n<p><strong><code>-out tls.crt</code></strong>: 指定生成的<strong>公钥证书</strong>保存的文件名。</p>\n<p><strong><code>-subj &quot;...&quot;</code></strong>: 以命令行方式直接提供证书的身份信息，避免进入交互式问答模式。</p>\n<ul>\n<li><strong><code>/CN</code> (Common Name)</strong>：域名。比如 <code>my-secure-app.com</code>，这要和你的 Ingress 规则里的 <code>host</code> 匹配。</li>\n<li><strong><code>/O</code> (Organization)</strong>：组织机构名称。</li>\n</ul>\n<hr />\n<h5 id=\"步骤-3在-kubernetes-中创建-tls-secret\"><a class=\"anchor\" href=\"#步骤-3在-kubernetes-中创建-tls-secret\">#</a> <strong>步骤 3：在 Kubernetes 中创建 TLS Secret</strong></h5>\n<p>将生成的证书存入 Secret 中：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl create secret tls my-tls-secret \\</span></span>\n<span class=\"line\"><span>  --key tls.key \\</span></span>\n<span class=\"line\"><span>  --cert tls.crt</span></span></code></pre>\n<hr />\n<h5 id=\"步骤-4部署测试应用-后端服务\"><a class=\"anchor\" href=\"#步骤-4部署测试应用-后端服务\">#</a> <strong>步骤 4：部署测试应用 (后端服务)</strong></h5>\n<p>部署简单的 NGINX 容器作为后端。可以直接在终端粘贴以下命令，它会使用 <code>cat</code> 直接生成并应用配置文件：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">cat</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">EOF</span><span style=\"color:#59873A;--shiki-dark:#80A665\">  kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> apply</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">apiVersion: apps/v1</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">kind: Deployment</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">metadata:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  name: hello-app</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">spec:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  replicas: 1</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  selector:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    matchLabels:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">      app: hello-app</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  template:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    metadata:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">      labels:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        app: hello-app</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    spec:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">      containers:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">      - name: hello-app</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        image: nginxdemos/hello:plain-text</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        ports:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        - containerPort: 80</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">---</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">apiVersion: v1</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">kind: Service</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">metadata:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  name: hello-service</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">spec:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  selector:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    app: hello-app</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  ports:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  - port: 80</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    targetPort: 80</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">EOF</span></span></code></pre>\n<hr />\n<h5 id=\"步骤-5配置带有-tls-的-ingress\"><a class=\"anchor\" href=\"#步骤-5配置带有-tls-的-ingress\">#</a> <strong>步骤 5：配置带有 TLS 的 Ingress</strong></h5>\n<p>与 Minikube 稍有不同，在标准集群中，显式声明 <code>ingressClassName: nginx</code> 是一个好习惯，确保流量被正确接管。</p>\n<p>Bash</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">cat</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">EOF</span><span style=\"color:#59873A;--shiki-dark:#80A665\">  kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> apply</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">apiVersion: networking.k8s.io/v1</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">kind: Ingress</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">metadata:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  name: secure-ingress</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">spec:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  ingressClassName: nginx  # 指定使用我们刚刚安装的 Nginx Ingress</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  tls:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  - hosts:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    - my-secure-app.com</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    secretName: my-tls-secret</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  rules:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  - host: my-secure-app.com</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    http:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">      paths:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">      - path: /</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        pathType: Prefix</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        backend:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">          service:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            name: hello-service</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            port:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">              number: 80</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">EOF</span></span></code></pre>\n<hr />\n<h5 id=\"步骤-6在-killercoda-中测试-https\"><a class=\"anchor\" href=\"#步骤-6在-killercoda-中测试-https\">#</a> <strong>步骤 6：在 Killercoda 中测试 HTTPS</strong></h5>\n<p>我们刚才安装的裸机版 Ingress Controller 是通过 <strong>NodePort</strong> 暴露在集群上的。我们需要获取它暴露的 443 (HTTPS) 端口的映射端口。</p>\n<p><strong>1. 自动提取 HTTPS 的 NodePort 端口号并存入变量：</strong></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">HTTPS_NODEPORT</span><span style=\"color:#999999;--shiki-dark:#666666\">=$</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> svc</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ingress-nginx-controller</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ingress-nginx</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -o</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> jsonpath=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">&#123;.spec.ports[?(@.name==\"https\")].nodePort&#125;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">echo</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">HTTPS NodePort is: $HTTPS_NODEPORT</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span></code></pre>\n<p><strong>2. 发起请求测试：</strong> 我们直接在 Killercoda 的终端里，向本机的（<code>localhost</code>）这个 NodePort 发起请求，同样强制指定 Host 头：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">curl</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -kv</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> https://localhost:</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">$HTTPS_NODEPORT</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -H</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Host: my-secure-app.com</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span></code></pre>\n<p>-k： 允许连接到“不安全”的 SSL 站点。</p>\n<p>-v：输出<strong>详细日志</strong>。</p>\n<p>-H：手动注入 HTTP Header 中的 <strong>Host 字段</strong></p>\n<p><strong>期望结果：</strong> 会在输出信息中看到 <code>Server certificate: my-secure-app.com</code> 的 TLS 握手信息，并看到底层 NGINX 应用返回的纯文本内容（如 <code>Server address</code> 和 <code>Server name</code> 等信息）。这证明在 Killercoda 标准集群上，TLS 终结实验圆满成功！</p>\n",
            "tags": [
                "k8s 实验"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/18-%E9%9B%86%E7%BE%A4%E8%AE%BE%E7%BD%AE%EF%BC%9Akube-bench/",
            "url": "https://www.bondrewd.com/2026/07/16/18-%E9%9B%86%E7%BE%A4%E8%AE%BE%E7%BD%AE%EF%BC%9Akube-bench/",
            "title": "18-集群设置：kube-bench",
            "date_published": "2026-07-16T05:54:29.000Z",
            "content_html": "<p><code>kube-bench</code> 进行集群安全加固是 Kubernetes 运维中的高级进阶任务。它主要基于 <strong>CIS (Center for Internet Security) Kubernetes Benchmark</strong> 标准来检查集群配置。</p>\n<h5 id=\"心智模型\"><a class=\"anchor\" href=\"#心智模型\">#</a> 心智模型：</h5>\n<p>在操作之前，需要建立这样一个逻辑回路：</p>\n<ol>\n<li><strong>扫描 (Scan)：</strong> <code>kube-bench</code> 作为一个二进制工具或容器运行，它会读取主机的进程参数（如 <code>ps -ef grep kube-apiserver</code>）和配置文件。</li>\n<li><strong>比对 (Compare)：</strong> 它将获取到的参数与 CIS 标准进行比对。</li>\n<li><strong>报告 (Report)：</strong> 输出 <code>[PASS]</code>（通过）、<code>[FAIL]</code>（失败）或 <code>[WARN]</code>（警告）。每个 <code>[FAIL]</code> 都会附带一个 <code>Remediation</code>（修复建议）。</li>\n<li><strong>修复 (Fix)：</strong> 修改 <code>/etc/kubernetes/manifests/</code> 下的 YAML。由于这些是 <strong>Static Pod</strong>，<code>kubelet</code> 会监控文件变化并自动重启组件应用新参数。</li>\n</ol>\n<h5 id=\"实验步骤指南\"><a class=\"anchor\" href=\"#实验步骤指南\">#</a> 实验步骤指南</h5>\n<h5 id=\"方式一推荐直接用-job\"><a class=\"anchor\" href=\"#方式一推荐直接用-job\">#</a> 方式一（推荐）：直接用 Job</h5>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml</span></span></code></pre>\n<p>查看结果：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl logs job/kube-bench</span></span></code></pre>\n<hr />\n<p>你会看到类似：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>[PASS] 1.1.1 Ensure API server pod specification file permissions are set to 644 or more restrictive</span></span>\n<span class=\"line\"><span>[FAIL] 1.2.7 Ensure that the --authorization-mode argument includes Node</span></span></code></pre>\n<p><strong>2.定位漏洞与修复</strong></p>\n<p>假设报告中出现以下失败项：</p>\n<ul>\n<li><strong>[FAIL] 1.2.7 Ensure that the --authorization-mode argument includes Node</strong></li>\n<li><strong>[FAIL] 1.2.19 Ensure that the --profiling argument is set to false</strong></li>\n</ul>\n<p><strong>3. 修改 Manifest</strong></p>\n<p>进入目录并编辑 API Server 的定义文件：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>cd /etc/kubernetes/manifests/</span></span>\n<span class=\"line\"><span>vi kube-apiserver.yaml</span></span></code></pre>\n<p>在 <code>spec.containers.command</code> 列表下添加或修改参数：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>- --authorization-mode=Node,RBAC  # 确保包含 Node</span></span>\n<span class=\"line\"><span>- --profiling=false               # 关闭分析接口以减少攻击面</span></span></code></pre>\n<p>保存退出后，等待约 30 秒，使用 <code>kubectl get pods -n kube-system</code> 查看 api-server 是否重启成功</p>\n<h5 id=\"kubernetes-安全-4层防线\"><a class=\"anchor\" href=\"#kubernetes-安全-4层防线\">#</a> Kubernetes 安全 = 4层防线</h5>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>① 身份（RBAC / authn）</span></span>\n<span class=\"line\"><span>② 权限（authorization）</span></span>\n<span class=\"line\"><span>③ 数据（etcd encryption）</span></span>\n<span class=\"line\"><span>④ 节点（kubelet security）</span></span></code></pre>\n",
            "tags": [
                "k8s 实验"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/430/",
            "url": "https://www.bondrewd.com/2026/07/16/430/",
            "title": "Untitled Post - 4",
            "date_published": "2026-07-16T05:52:01.000Z",
            "content_html": "",
            "tags": [
                "uncategorized"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/17-%E4%BD%BF%E7%94%A8-etcdctl-%E7%BB%83%E4%B9%A0%E5%A4%87%E4%BB%BD%E4%B8%8E%E6%81%A2%E5%A4%8D%E9%9B%86%E7%BE%A4%E6%95%B0%E6%8D%AE/",
            "url": "https://www.bondrewd.com/2026/07/16/17-%E4%BD%BF%E7%94%A8-etcdctl-%E7%BB%83%E4%B9%A0%E5%A4%87%E4%BB%BD%E4%B8%8E%E6%81%A2%E5%A4%8D%E9%9B%86%E7%BE%A4%E6%95%B0%E6%8D%AE/",
            "title": "17-使用 etcdctl 练习备份与恢复集群数据",
            "date_published": "2026-07-16T05:46:01.000Z",
            "content_html": "<p>ectdctl 是一个命令行界面 （CLI）工具，专门用于与 etcd 数据库进行交互。</p>\n<p>目前 ectd 普遍使用 v3 版本的 API ，操作前需要指定环境变量 <code>ETCDCTL_API=3</code>。</p>\n<p>新版将 etcdctl 功能拆分了：</p>\n<ul>\n<li><code>ectdctl</code>:用于与正在运行的 etcd 服务交互 （通过网络）</li>\n<li><code>etcdutl</code>:用于处理离线文件</li>\n</ul>\n<h5 id=\"第一部分etcd-备份与恢复的心智模型\"><a class=\"anchor\" href=\"#第一部分etcd-备份与恢复的心智模型\">#</a> 第一部分：etcd 备份与恢复的心智模型</h5>\n<ol>\n<li>集群所有状态都以键值对形式存储在 etcd 中。除了 kube-apiserver，没有任何组件可以直接和 etcd 对话。</li>\n<li>备份的本质是拷贝那一瞬间 etcd 底层 BoltDB 引擎的物理文件。</li>\n<li>恢复操作不是覆盖，而是那旧的快照文件，在一个全新的目录下解压重现当时的数据结构，然后修改 etcd 配置文件，指向这个新目录。</li>\n</ol>\n<h5 id=\"第二部分实战演练指南\"><a class=\"anchor\" href=\"#第二部分实战演练指南\">#</a> 第二部分：实战演练指南</h5>\n<h5 id=\"第一步准备万能钥匙设置-alias\"><a class=\"anchor\" href=\"#第一步准备万能钥匙设置-alias\">#</a> 第一步：准备“万能钥匙”（设置 Alias）</h5>\n<p>在 kubeadm 搭建的集群中，etcd 启用了双向 TLS 认证。每次敲击 <code>etcdctl</code> 都需要带上一长串证书路径，非常反人类。我们先设置一个临时别名来简化操作：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">## 确认 etcdctl 已安装 (如果没有，可通过 apt/yum 安装 etcd-client)</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">## 设置别名，指定 API 版本为 3，并带上 kubeadm 默认的 etcd 证书路径</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">alias</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> etcdctl</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">ETCDCTL_API=3 etcdctl \\</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  --endpoints=https://127.0.0.1:2379 \\</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  --cacert=/etc/kubernetes/pki/etcd/ca.crt \\</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  --cert=/etc/kubernetes/pki/etcd/server.crt \\</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  --key=/etc/kubernetes/pki/etcd/server.key</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span></span></code></pre>\n<p><em>验证一下：</em> 运行 <code>sudo etcdctl endpoint health</code>，如果返回 <code>is healthy</code>，说明钥匙管用。</p>\n<h5 id=\"第二步制造案发现场创造测试数据\"><a class=\"anchor\" href=\"#第二步制造案发现场创造测试数据\">#</a> 第二步：制造“案发现场”（创造测试数据）</h5>\n<p>我们在集群里留下一些独特的痕迹，证明当前的“时间线”。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> create</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> namespace</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> disaster-recovery</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> run</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> survivor-pod</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --image=nginx</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> disaster-recovery</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pods</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> disaster-recovery</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">## 确保 pod 处于 Running 状态</span></span></code></pre>\n<h5 id=\"第三步时空冻结执行备份\"><a class=\"anchor\" href=\"#第三步时空冻结执行备份\">#</a> 第三步：时空冻结（执行备份）</h5>\n<p>现在，我们将当前的集群状态备份到 <code>/opt/etcd-backup.db</code> 文件中。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">etcdctl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> snapshot</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> save</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /opt/etcd-backup.db</span></span></code></pre>\n<p><em>验证一下：</em> 运行 <code>etcdutl snapshot status /opt/etcd-backup.db -w table</code>，你会看到备份文件的详细哈希和体积。</p>\n<h5 id=\"第四步模拟灾难破坏数据\"><a class=\"anchor\" href=\"#第四步模拟灾难破坏数据\">#</a> 第四步：模拟灾难（破坏数据）</h5>\n<p>现在，假装有新手误删了整个核心业务线。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> delete</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> namespace</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> disaster-recovery</span></span></code></pre>\n<p><em>验证一下：</em> 此时再运行 <code>kubectl get pods -n disaster-recovery</code>，会提示 Namespace 不存在。案发现场已被破坏。</p>\n<h5 id=\"第五步解压快照生成新目录\"><a class=\"anchor\" href=\"#第五步解压快照生成新目录\">#</a> 第五步：解压快照（生成新目录）</h5>\n<p>注意心智模型：我们要把备份恢复到一个<strong>全新的空目录</strong>（例如 <code>/var/lib/etcd-restore</code>），而不是直接覆盖现有的 <code>/var/lib/etcd</code>。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">etcdutl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> snapshot</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> restore</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /opt/etcd-backup.db</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">  --data-dir=/var/lib/etcd-restore</span></span></code></pre>\n<p>执行完毕后，你可以 <code>ls -l /var/lib/etcd-restore</code>，会发现里面生成了新的数据文件。</p>\n<h5 id=\"第六步大脑移植切换-etcd-数据目录\"><a class=\"anchor\" href=\"#第六步大脑移植切换-etcd-数据目录\">#</a> 第六步：大脑移植（切换 etcd 数据目录）</h5>\n<p>在 kubeadm 集群中，etcd 是以 Static Pod（静态 Pod）的形式运行的。kubelet 会死死盯着 <code>/etc/kubernetes/manifests/</code> 目录。只要我们修改了里面的 yaml，kubelet 就会自动重启对应的 Pod。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">vi</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/kubernetes/manifests/etcd.yaml</span></span></code></pre>\n<p>翻到文件最下面，找到 <code>volumes</code> 挂载部分的 <code>hostPath</code>。 <strong>将原来的 <code>/var/lib/etcd</code> 修改为 <code>/var/lib/etcd-restore</code></strong>：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-yaml\"><span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  volumes</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">  -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> hostPath</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      path</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/kubernetes/pki/etcd</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      type</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> DirectoryOrCreate</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> etcd-certs</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">  -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> hostPath</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      path</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /var/lib/etcd-restore</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # &#x3C;--- 修改这里！</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      type</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> DirectoryOrCreate</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> etcd-data</span></span></code></pre>\n<p>保存并退出 (<code>:wq</code>)。</p>\n<h5 id=\"第七步见证奇迹\"><a class=\"anchor\" href=\"#第七步见证奇迹\">#</a> 第七步：见证奇迹</h5>\n<p>保存退出后，kubelet 会发现 yaml 发生了变化，开始销毁旧的 etcd pod 并挂载新目录启动新的 etcd。这个过程大概需要 30 秒到 1 分钟。在这期间，你敲 <code>kubectl</code> 可能会卡住或报错（因为 apiserver 连不上数据库了），这是正常现象。</p>\n<p>耐心等待一小会儿，再次输入：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pods</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> disaster-recovery</span></span></code></pre>\n<p>你会发现，刚才被删除的 <code>disaster-recovery</code> 命名空间和里面的 <code>survivor-pod</code> 又奇迹般地复活了！这就标志着你的集群时光倒流成功。</p>\n<h5 id=\"总结\"><a class=\"anchor\" href=\"#总结\">#</a> 总结</h5>\n<p>常用命令：</p>\n<p><strong>数据备份</strong>：<code>etcdctl snapshot save backup.db</code></p>\n<p><strong>查看备份文件哈希和体积</strong>： <code>etcdutl snapshot status /opt/etcd-backup.db -w table</code></p>\n<p><strong>备份恢复到指定路径：</strong></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">etcdutl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> snapshot</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> restore</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /opt/etcd-backup.db</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">  --data-dir=/var/lib/etcd-restore</span></span></code></pre>\n",
            "tags": [
                "k8s 实验"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/%E5%A4%9A%E5%AE%B9%E5%99%A8%E6%9E%B6%E6%9E%84%E5%AE%9E%E6%88%98%EF%BC%88sidecar-%E8%BE%B9%E8%BD%A6%E6%A8%A1%E5%BC%8F%E6%94%B6%E9%9B%86%E6%97%A5%E5%BF%97%EF%BC%89/",
            "url": "https://www.bondrewd.com/2026/07/16/%E5%A4%9A%E5%AE%B9%E5%99%A8%E6%9E%B6%E6%9E%84%E5%AE%9E%E6%88%98%EF%BC%88sidecar-%E8%BE%B9%E8%BD%A6%E6%A8%A1%E5%BC%8F%E6%94%B6%E9%9B%86%E6%97%A5%E5%BF%97%EF%BC%89/",
            "title": "16-多容器架构实战（Sidecar 边车模式收集日志）",
            "date_published": "2026-07-16T05:35:25.000Z",
            "content_html": "<p>在真实的生产环境中，很多老旧系统（Legacy Systems）并不符合云原生标准。它们不会把日志输出到终端（<code>stdout</code>），而是死板地写进容器内的某个文件里（比如 <code>/var/log/app.log</code>）。这就导致 Kubernetes 原生的 <code>kubectl logs</code> 命令直接失效（因为它只抓取容器的 <code>stdout</code>）。</p>\n<h4 id=\"核心技术逻辑sidecar边车模式与共享卷\"><a class=\"anchor\" href=\"#核心技术逻辑sidecar边车模式与共享卷\">#</a> 核心技术逻辑：Sidecar（边车）模式与共享卷</h4>\n<p>要在不修改老旧代码的前提下解决这个问题，K8s 给出的标准答案是：<strong>Sidecar 模式</strong>。</p>\n<ol>\n<li><strong>Pod 的本质：</strong> Pod 不是一个容器，而是一个“容器组”。同一个 Pod 内的多个容器共享网络（localhost）和存储卷。</li>\n<li><strong>EmptyDir 卷（桥梁）：</strong> 我们创建一个生命周期与 Pod 绑定的临时存储卷 <code>emptyDir</code>，把它同时挂载给这两个容器。</li>\n<li><strong>数据流向：</strong>\n<ul>\n<li><strong>业务容器 (Main)：</strong> 把日志不断写入挂载的目录中（例如 <code>/var/log/app/sys.log</code>）。</li>\n<li><strong>边车容器 (Sidecar)：</strong> 同样挂载这个目录，只运行一个极为简单的命令（如 <code>tail -f /var/log/app/sys.log</code>），把文件内容实时读取出来，并输出到<strong>自己的</strong>终端（<code>stdout</code>）。</li>\n</ul>\n</li>\n<li><strong>最终结果：</strong> 此时，你只需要用 <code>kubectl logs</code> 去看那个 Sidecar 容器，就能完美获取老旧系统的日志了！</li>\n</ol>\n<hr />\n<h4 id=\"实战挑战\"><a class=\"anchor\" href=\"#实战挑战\">#</a> 实战挑战</h4>\n<p>我们将从零手写一个包含两个容器的 Pod YAML。</p>\n<p><strong>1. 编写多容器 Pod 配置文件 <code>sidecar-pod.yaml</code>：</strong></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: v1</span></span>\n<span class=\"line\"><span>kind: Pod</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: legacy-app-pod</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  # 1. 声明一个共享的空目录卷</span></span>\n<span class=\"line\"><span>  volumes:</span></span>\n<span class=\"line\"><span>  - name: shared-logs</span></span>\n<span class=\"line\"><span>    emptyDir: &#123;&#125;</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>  containers:</span></span>\n<span class=\"line\"><span>  # 2. 这是老旧的业务主容器（它只往文件里写，不往屏幕上打）</span></span>\n<span class=\"line\"><span>  - name: main-app</span></span>\n<span class=\"line\"><span>    image: busybox</span></span>\n<span class=\"line\"><span>    command: [\"/bin/sh\", \"-c\", \"while true; do echo '$(date) - ERROR: legacy system failure' >> /var/log/app/sys.log; sleep 2; done\"]</span></span>\n<span class=\"line\"><span>    volumeMounts:</span></span>\n<span class=\"line\"><span>    - name: shared-logs</span></span>\n<span class=\"line\"><span>      mountPath: /var/log/app   # 挂载到业务容器的这个路径</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>  # 3. 这是我们注入的 Sidecar 日志收集容器</span></span>\n<span class=\"line\"><span>  - name: log-sidecar</span></span>\n<span class=\"line\"><span>    image: busybox</span></span>\n<span class=\"line\"><span>    command: [\"/bin/sh\", \"-c\", \"tail -f /var/log/app/sys.log\"] # 实时读取文件并输出到标准输出</span></span>\n<span class=\"line\"><span>    volumeMounts:</span></span>\n<span class=\"line\"><span>    - name: shared-logs</span></span>\n<span class=\"line\"><span>      mountPath: /var/log/app   # 必须挂载同一个卷到相同的（或不同的）路径</span></span></code></pre>\n<p><strong>2. 部署这个“双黄蛋” Pod：</strong></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl apply -f sidecar-pod.yaml</span></span></code></pre>\n<p><strong>3. 观察多容器的就绪状态：</strong></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl get pod legacy-app-pod -w</span></span></code></pre>\n<blockquote>\n<p><strong>预期现象：</strong> 注意看 <code>READY</code> 那一列。普通的 Pod 是 <code>1/1</code>，而这个 Pod 在启动时会显示 <code>0/2</code>，最终变成 <code>2/2</code>。这代表里面的两个容器都已成功运行。</p>\n</blockquote>\n<hr />\n<h4 id=\"阶段二见证-sidecar-的魔法-cka-必考命令\"><a class=\"anchor\" href=\"#阶段二见证-sidecar-的魔法-cka-必考命令\">#</a> 阶段二：见证 Sidecar 的魔法 (CKA 必考命令)</h4>\n<p>现在，我们来验证日志提取。</p>\n<p><strong>1. 尝试直接获取 Pod 日志（会报错）：</strong> 在单容器时代，你习惯直接敲 <code>kubectl logs &lt;pod-name&gt;</code>。但在多容器时代，这行不通了：</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl logs legacy-app-pod</span></span></code></pre>\n<blockquote>\n<p><strong>预期输出：</strong> <code>error: a container name must be specified for pod legacy-app-pod...</code> K8s 会抱怨：你这 Pod 里有两个容器，你到底要看谁的？</p>\n</blockquote>\n<p><strong>2. 明确指定容器名称（查看主容器）：</strong> 使用 <code>-c</code> 参数指定容器名。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl logs legacy-app-pod -c main-app</span></span></code></pre>\n<blockquote>\n<p><strong>预期输出：</strong> 空空如也。因为老旧系统没有向终端输出任何东西。</p>\n</blockquote>\n<p><strong>3. 见证奇迹时刻（查看 Sidecar 容器）：</strong></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl logs legacy-app-pod -c log-sidecar</span></span></code></pre>\n<blockquote>\n<p><strong>预期输出：</strong> 你会看到屏幕上源源不断地打印出带有时间戳的 <code>ERROR: legacy system failure</code>！</p>\n</blockquote>\n<p>这就是 Sidecar 模式的魅力：<strong>解耦</strong>。业务容器专心跑业务，日志容器专心搬运日志，互不干扰，却完美协同。</p>\n<hr />\n<h4 id=\"清理\"><a class=\"anchor\" href=\"#清理\">#</a> 清理</h4>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>kubectl delete pod legacy-app-pod</span></span></code></pre>\n",
            "tags": [
                "k8s 实验"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/07/16/wp-global-styles-sakurairo/",
            "url": "https://www.bondrewd.com/2026/07/16/wp-global-styles-sakurairo/",
            "title": "Custom Styles",
            "date_published": "2026-07-16T05:32:52.000Z",
            "content_html": "<p version:=\"\" 3,=\"\" isGlobalStylesUserThemeJSON:=\"\" true=\"\"></p>\n",
            "tags": [
                "uncategorized"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/26/wp-global-styles-twentytwentythree/",
            "url": "https://www.bondrewd.com/2026/03/26/wp-global-styles-twentytwentythree/",
            "title": "Custom Styles",
            "date_published": "2026-03-26T04:38:10.000Z",
            "content_html": "<p version:=\"\" 3,=\"\" isGlobalStylesUserThemeJSON:=\"\" true=\"\"></p>\n",
            "tags": [
                "uncategorized"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/24/transformer-%E7%AE%80%E4%BB%8B/",
            "url": "https://www.bondrewd.com/2026/03/24/transformer-%E7%AE%80%E4%BB%8B/",
            "title": "Transformer 简介",
            "date_published": "2026-03-24T04:44:17.000Z",
            "content_html": "<h3 id=\"背景\"><a class=\"anchor\" href=\"#背景\">#</a> 背景</h3>\n<p>给定一个翻译任务：将 I love you baby 翻译为中文。</p>\n<p>先把每一个词转换成对应的 <code>词向量</code>，考虑按照下面方式处理：</p>\n<ol>\n<li>直接丢到 MLP 中，那么：\n<ul>\n<li>每个词都会<strong>失去上下文信息</strong>，且长度只能一一对应</li>\n</ul>\n</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774354582-image-744x1024.png\" alt=\"\" /></p>\n<ol start=\"2\">\n<li>用 RNN，那么：\n<ul>\n<li>会面临<strong>串行计算</strong></li>\n<li>句子太长会导致<strong>记不住长距离的信息</strong></li>\n</ul>\n</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774354614-image-1024x450.png\" alt=\"\" /></p>\n<hr />\n<h3 id=\"注意力机制-attention\"><a class=\"anchor\" href=\"#注意力机制-attention\">#</a> 注意力机制 Attention</h3>\n<p>上面两个方法都有缺陷，可以考虑按照下面的方法处理：</p>\n<ol>\n<li>首先，给每个 <code>词向量</code><strong>加上</strong>各自的 <code>位置编码</code>（表示该词出现在整个句子中的位置），现在这个词向量就具备了位置信息</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774354656-image-1024x366.png\" alt=\"\" /></p>\n<ol start=\"2\">\n<li>但现在每个词都还没有其他词的上下文信息（注意不到其他词的存在）。用<code>权重矩阵</code> <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><mi>q</mi></mrow><annotation encoding=\"application/x-tex\">W\\_q</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9933em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span></span></span></span> 与第一个词向量相乘(<code>MatMul</code>)，得到维度不变的 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>q</mi><mi mathvariant=\"normal\">_</mi><mn>1</mn></mrow><annotation encoding=\"application/x-tex\">q\\_1</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9544em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mord\">_1</span></span></span></span> 矩阵。同理，用 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><mi>k</mi><mo separator=\"true\">,</mo><mi>W</mi><mi mathvariant=\"normal\">_</mi><mi>v</mi></mrow><annotation encoding=\"application/x-tex\">W\\_k, W\\_v</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">v</span></span></span></span> 矩阵处理得到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>k</mi><mi mathvariant=\"normal\">_</mi><mn>1</mn><mo separator=\"true\">,</mo><mi>v</mi><mi mathvariant=\"normal\">_</mi><mn>1</mn></mrow><annotation encoding=\"application/x-tex\">k\\_1, v\\_1</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mord\">_1</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">v</span><span class=\"mord\">_1</span></span></span></span>。同理，得到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>q</mi><mi mathvariant=\"normal\">_</mi><mrow><mn>1</mn><mo separator=\"true\">,</mo><mn>2</mn><mo separator=\"true\">,</mo><mn>3</mn><mo separator=\"true\">,</mo><mn>4</mn></mrow><mo separator=\"true\">,</mo><mi>k</mi><mi mathvariant=\"normal\">_</mi><mrow><mn>1</mn><mo separator=\"true\">,</mo><mn>2</mn><mo separator=\"true\">,</mo><mn>3</mn><mo separator=\"true\">,</mo><mn>4</mn></mrow><mo separator=\"true\">,</mo><mi>v</mi><mi mathvariant=\"normal\">_</mi><mrow><mn>1</mn><mo separator=\"true\">,</mo><mn>2</mn><mo separator=\"true\">,</mo><mn>3</mn><mo separator=\"true\">,</mo><mn>4</mn></mrow></mrow><annotation encoding=\"application/x-tex\">q\\_{1,2,3,4}, k\\_{1,2,3,4}, v\\_{1,2,3,4}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord\">1</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">2</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">3</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">4</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord\">1</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">2</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">3</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">4</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">v</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord\">1</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">2</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">3</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord\">4</span></span></span></span></span>。\n<ul>\n<li><em>其中 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><mi>q</mi><mo separator=\"true\">,</mo><mi>W</mi><mi mathvariant=\"normal\">_</mi><mi>k</mi><mo separator=\"true\">,</mo><mi>W</mi><mi mathvariant=\"normal\">_</mi><mi>v</mi></mrow><annotation encoding=\"application/x-tex\">W\\_q, W\\_k, W\\_v</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">v</span></span></span></span> 矩阵都是可以经过训练学习到的一组权重值</em>。</li>\n<li><em>实际在 GPU 中运算时，是把词向量拼接成一个矩阵，再与 W 矩阵相乘。</em></li>\n</ul>\n</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355126-image-1024x607.png\" alt=\"\" /></p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355131-image-1024x607.png\" alt=\"\" /></p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355170-image-1024x731.png\" alt=\"\" /></p>\n<ol start=\"3\">\n<li>现在，原来的词向量已经通过线性变换映射成了维度相同的 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>q</mi><mo separator=\"true\">,</mo><mi>k</mi><mo separator=\"true\">,</mo><mi>v</mi></mrow><annotation encoding=\"application/x-tex\">q, k, v</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8889em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">v</span></span></span></span> 。将 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>q</mi><mi mathvariant=\"normal\">_</mi><mn>1</mn></mrow><annotation encoding=\"application/x-tex\">q\\_1</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9544em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mord\">_1</span></span></span></span> 与 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>k</mi><mi mathvariant=\"normal\">_</mi><mn>2</mn></mrow><annotation encoding=\"application/x-tex\">k\\_2</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mord\">_2</span></span></span></span> 做 <code>内积</code> 得到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>12</mn></mrow><annotation encoding=\"application/x-tex\">a\\_{12}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9544em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord\">12</span></span></span></span></span>，表示在第一个词的视角下，与第二个词的 <code>相似度系数</code>。同理，得到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>11</mn><mo separator=\"true\">,</mo><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>12</mn><mo separator=\"true\">,</mo><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>13</mn><mo separator=\"true\">,</mo><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>14</mn></mrow><annotation encoding=\"application/x-tex\">a\\_{11}, a\\_{12}, a\\_{13}, a\\_{14}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9544em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord\">11</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord\">12</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord\">13</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord\">14</span></span></span></span></span>。</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355272-image-1024x490.png\" alt=\"\" /></p>\n<ol start=\"4\">\n<li>得到相似度系数后，分别和 v 向量相乘，再相加，得到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>1</mn></mrow><annotation encoding=\"application/x-tex\">a\\_1</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9544em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\">_1</span></span></span></span>，表示在第一个词的视角下的<strong>全部上下文信息</strong>。同理，得到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>1</mn><mo separator=\"true\">,</mo><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>2</mn><mo separator=\"true\">,</mo><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>3</mn><mo separator=\"true\">,</mo><mi>a</mi><mi mathvariant=\"normal\">_</mi><mn>4</mn></mrow><annotation encoding=\"application/x-tex\">a\\_1, a\\_2, a\\_3, a\\_4</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9544em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\">_1</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\">_2</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\">_3</span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\">_4</span></span></span></span>。</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355339-image-1024x296.png\" alt=\"\" /></p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355350-image-1024x696.png\" alt=\"\" /></p>\n<ol start=\"5\">\n<li>从全局的视角看，就是把最初的词向量，处理得到新的词向量（包含了<strong>位置信息</strong>和<strong>其他词上下文信息</strong>），这就是 <code>注意力机制 Attention</code> 的原理</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355506-image-1024x653.png\" alt=\"\" /></p>\n<hr />\n<h3 id=\"多头注意力机制-multi-head-attention\"><a class=\"anchor\" href=\"#多头注意力机制-multi-head-attention\">#</a> 多头注意力机制 Multi-Head Attention</h3>\n<p>对于注意力机制而言，通过一种方式计算一次相关性，那么灵活性会大大降低。</p>\n<ol>\n<li>之前是每个词向量计算一组 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>q</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi><mo separator=\"true\">,</mo><mi>k</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi><mo separator=\"true\">,</mo><mi>v</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi></mrow><annotation encoding=\"application/x-tex\">q\\_{i}, k\\_{i}, v\\_{i}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">v</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span></span></span></span> ，现在基于原来的 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>q</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi><mo separator=\"true\">,</mo><mi>k</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi><mo separator=\"true\">,</mo><mi>v</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi></mrow><annotation encoding=\"application/x-tex\">q\\_{i}, k\\_{i}, v\\_{i}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">v</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span></span></span></span>，再经过两个<code>权重矩阵</code> W 变成两组 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>q</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi><mo separator=\"true\">,</mo><mi>k</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi><mo separator=\"true\">,</mo><mi>v</mi><mi mathvariant=\"normal\">_</mi><mi>i</mi></mrow><annotation encoding=\"application/x-tex\">q\\_{i}, k\\_{i}, v\\_{i}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03148em;\">k</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span><span class=\"mpunct\">,</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">v</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">i</span></span></span></span></span>，相当于<strong>给每个词向量两次学习机会</strong>，学习到不同的待计算相似度的 $q_{ij}, k_{ij}, v_{ij} $（<code>头部 head</code>）来增加语言的灵活性。</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355598-image-1024x622.png\" alt=\"\" /></p>\n<ol start=\"2\">\n<li>两个 <code>head</code> 经过注意力层的计算，得到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>a</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>i</mi><mi>j</mi></mrow></mrow><annotation encoding=\"application/x-tex\">a\\_{ij}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9695em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.05724em;\">ij</span></span></span></span></span> 向量，再把两个 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>a</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>i</mi><mi>j</mi></mrow></mrow><annotation encoding=\"application/x-tex\">a\\_{ij}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9695em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">a</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.05724em;\">ij</span></span></span></span></span> 向量拼接起来，得到和原词向量相同维度的新词向量。这种方式也即 <code>多头注意力机制机制 Multi-Head Attention</code>。</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355635-image-1024x502.png\" alt=\"\" /></p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355638-image-1024x642.png\" alt=\"\" /></p>\n<hr />\n<h3 id=\"transformer-论文架构图\"><a class=\"anchor\" href=\"#transformer-论文架构图\">#</a> Transformer 论文架构图</h3>\n<h4 id=\"整体架构\"><a class=\"anchor\" href=\"#整体架构\">#</a> 整体架构</h4>\n<ol>\n<li>把输入的内容通过 <code>词嵌入 Word Embedding</code> 的方式转换为向量矩阵</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355930-image-1024x710.png\" alt=\"\" /></p>\n<ol start=\"2\">\n<li>加入位置信息</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355902-image-1024x618.png\" alt=\"\" /></p>\n<ol start=\"3\">\n<li>经过多头注意力的处理，输入、输出的矩阵在维度上没有变化，输出的矩阵的每个词向量都增加了<strong>上下文信息</strong>。\n<ul>\n<li>后面的 <code>Add &amp; Norm</code> 是优化步骤。表示 <code>残差网络</code> 和 <code>归一化</code>，是为了<strong>解决梯度消失问题、让分布更加稳定</strong>。</li>\n</ul>\n</li>\n</ol>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355939-image-1024x636.png\" alt=\"\" /></p>\n<hr />\n<h3 id=\"多头注意力架构\"><a class=\"anchor\" href=\"#多头注意力架构\">#</a> 多头注意力架构</h3>\n<ol>\n<li>单头注意力：先让 q, k 内积，得到一个 <code>相似度系数</code> 的矩阵，再和 v 相乘，得到包含上下文信息的词向量矩阵\n<ul>\n<li>Scale：缩放</li>\n<li>Mask：掩码</li>\n<li>SoftMax：将输出映射到(0, 1)区间</li>\n</ul>\n</li>\n</ol>\n<p>Attention(Q,K,V)=softmax(QKTdk)VAttention(Q,K,V)=softmax(\\frac<ruby>QK<rp>(</rp><rt>T</rt><rp>)</rp></ruby>{\\sqrt{d_k}})V</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774355952-image.png\" alt=\"\" /></p>\n<ol start=\"2\">\n<li>多头注意力机制<br />\nq,k,v 分别经过 <code>线性变换 Linear</code>（<code>矩阵乘法</code>）拆分成多组（相当于给了多次机会学习到不同的相似度关系），依次经过 <code>单头注意力机制</code> 运算后，把运算结果拼接 Concat 起来。最后再用权重矩阵 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mi>W</mi><mi>O</mi></msup></mrow><annotation encoding=\"application/x-tex\">W^O</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8413em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8413em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mathnormal mtight\" style=\"margin-right:0.02778em;\">O</span></span></span></span></span></span></span></span></span></span></span> 进行线性变换</li>\n</ol>\n<p>MultiHead(Q,K,V)=Concat(head1,…,headh)WOheadi=Attention(QWiQ,KWiK,VWiV)\\begin<ruby>align\\*} \\\\text{MultiHead}(Q, K, V) &amp;= \\\\text{Concat}(\\\\text{head}\\_1, \\\\dots, \\\\text{head}\\_h) W<rp>(</rp><rt>O \\\\\\\\ \\\\text{head</rt><rp>)</rp></ruby>_i &amp;= \\text<ruby>Attention}(Q W\\_i<rp>(</rp><rt>Q, K W\\_i^K, V W\\_i^V) \\\\\\\\ \\\\end{align\\*</rt><rp>)</rp></ruby></p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774356093-image.png\" alt=\"\" /></p>\n<hr />\n<h4 id=\"编码器与解码器\"><a class=\"anchor\" href=\"#编码器与解码器\">#</a> 编码器与解码器</h4>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774356122-image-670x1024.png\" alt=\"\" /></p>\n<p>用原来的翻译任务为例：</p>\n<p><strong>编码器</strong>：</p>\n<ul>\n<li>输入要翻译的文本</li>\n<li>经过 词嵌入 Embedding，引入位置编码 Positional Encoding</li>\n<li>经过多头注意力、残差和归一化</li>\n<li>送入一个全连接神经网络 Feed Forward，再残差和归一化</li>\n<li><strong>结果送入解码器的一个多头注意力机制的两个输入中</strong>（作为 <code>K</code>, <code>V</code>）</li>\n</ul>\n<p><strong>解码器</strong>：</p>\n<ul>\n<li><em>输入要翻译的文本</em></li>\n<li><em>经过 词嵌入 Embedding，引入位置编码 Positional Encoding</em></li>\n<li>经过 <code>掩码后的多头注意力</code>、残差和归一化，送入多头注意力的一个输入中（作为 <code>Q</code>）</li>\n<li><code>K,V,Q</code> 进行多头注意力，再残差和归一化</li>\n<li>最后结果一层线性变换的神经网络，把向量投射到词表向量中</li>\n<li>Softmax 转化为概率</li>\n</ul>\n<blockquote>\n<p>Masked Attention</p>\n<ul>\n<li>作用：<strong>屏蔽未来位置信息</strong>，保证解码器只能基于已生成的前缀序列预测下一个词</li>\n<li>Transformer 解码器是<strong>自回归生成模型</strong>：每一步的输出都依赖于之前所有的输出</li>\n<li>掩码是一个上三角矩阵，未来位置的注意力权重置为 −∞（或极小值）</li>\n</ul>\n</blockquote>\n<hr />\n<p><em>参考链接：<a href=\"https://www.bilibili.com/video/BV1C3dqYxE3q?spm_id_from=333.788.videopod.sections&amp;vd_source=c5551749b3e52456db43941bf55d8d6b\">Transformer 其实是个简单到令人困惑的模型【AI入门06】_哔哩哔哩_bilibili</a></em></p>\n<hr />\n",
            "tags": [
                "深度学习"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/24/%E9%95%BF%E7%9F%AD%E6%9C%9F%E8%AE%B0%E5%BF%86-lstm/",
            "url": "https://www.bondrewd.com/2026/03/24/%E9%95%BF%E7%9F%AD%E6%9C%9F%E8%AE%B0%E5%BF%86-lstm/",
            "title": "长短期记忆 LSTM",
            "date_published": "2026-03-24T04:05:53.000Z",
            "content_html": "<p>长短期记忆（Long Short-Term Memory，<code>LSTM</code>） 是 <a href=\"https://iplusjia.top/2026/03/24/%e5%be%aa%e7%8e%af%e7%a5%9e%e7%bb%8f%e7%bd%91%e7%bb%9c-rnn/\">RNN</a> 最重要的变体，也是深度学习 NLP 发展史上的<strong>里程碑</strong>。在 Transformer 出现之前（2017 年），LSTM 是序列建模的绝对王者。</p>\n<hr />\n<h3 id=\"背景rnn-梯度消失\"><a class=\"anchor\" href=\"#背景rnn-梯度消失\">#</a> 背景：RNN 梯度消失</h3>\n<blockquote>\n<p>句子：&quot;我住在法国，...（中间 200 个字）... 所以我会说___&quot;<br />\n空格应该填 <em>“法语”</em></p>\n</blockquote>\n<p><strong>RNN 的问题</strong>：<br />\n需要记住 200 个字前的&quot;法国&quot;。<code>反向传播</code> 时，梯度要连乘 200 次，导致前面的信息传不过来。<br />\n<em>如果每次乘 0.9 → <code>0.9^200 ≈ 0.000000001</code> → <strong>梯度消失</strong></em></p>\n<hr />\n<h3 id=\"lstm-的结构及接口\"><a class=\"anchor\" href=\"#lstm-的结构及接口\">#</a> LSTM 的结构及接口</h3>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774353528-image-1024x403.png\" alt=\"\" /></p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774353542-image.png\" alt=\"\" /></p>\n<p>LSTM 与 RNN 的接口的不同之处在于，LSTM 还有路径 <code>c</code>。这个 c 称为 <code>记忆单元</code>（或者简称为“单元”），相当于 LSTM 专用的记忆部门。</p>\n<ul>\n<li>记忆单元的特点：<strong>仅在 LSTM 层内部接收和传递数据</strong>，对外部不可见，我们甚至不用考虑它的存在</li>\n<li><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mspace linebreak=\"newline\"></mspace><mi>s</mi><mi>i</mi><mi>g</mi><mi>m</mi><mi>a</mi></mrow><annotation encoding=\"application/x-tex\">\\\\sigma</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:0.854em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">g</span><span class=\"mord mathnormal\">ma</span></span></span></span>：<code>sigmoid函数</code>用于求门的开合程度（sigmoid函数的输出范围在0.0 ~ 1.0）</li>\n</ul>\n<hr />\n<h4 id=\"输出门-output-gate\"><a class=\"anchor\" href=\"#输出门-output-gate\">#</a> 输出门 Output Gate</h4>\n<ul>\n<li><strong>作用</strong>：决定<strong>输出</strong>哪些信息作为隐藏状态 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>h</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><annotation encoding=\"application/x-tex\">h\\_t</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span></span></span>（求出隐藏状态 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>h</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><annotation encoding=\"application/x-tex\">h\\_t</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span></span></span>）</li>\n<li><strong>公式</strong>：<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>o</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mo>=</mo><mspace linebreak=\"newline\"></mspace><mi>s</mi><mi>i</mi><mi>g</mi><mi>m</mi><mi>a</mi><mspace linebreak=\"newline\"></mspace><mi>l</mi><mi>e</mi><mi>f</mi><mi>t</mi><mo stretchy=\"false\">(</mo><mi>x</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mi>W</mi><mi mathvariant=\"normal\">_</mi><msup><mi>x</mi><mrow><mo stretchy=\"false\">(</mo><mi>o</mi><mo stretchy=\"false\">)</mo></mrow></msup><mo>+</mo><mi>h</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><msup><mi>h</mi><mrow><mo stretchy=\"false\">(</mo><mi>o</mi><mo stretchy=\"false\">)</mo></mrow></msup><mo>+</mo><msup><mi>b</mi><mrow><mo stretchy=\"false\">(</mo><mi>o</mi><mo stretchy=\"false\">)</mo></mrow></msup><mspace linebreak=\"newline\"></mspace><mi>r</mi><mi>i</mi><mi>g</mi><mi>h</mi><mi>t</mi><mo stretchy=\"false\">)</mo></mrow><annotation encoding=\"application/x-tex\">o\\_t = \\\\sigma\\\\left(x\\_t W\\_x^{(o)} + h\\_{t-1} W\\_h^{(o)} + b^{(o)}\\\\right)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9251em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">o</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:0.854em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">g</span><span class=\"mord mathnormal\">ma</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.198em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">e</span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span><span class=\"mord mathnormal\">t</span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">x</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">x</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\">o</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1.198em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\">1</span></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">h</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\">o</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.888em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">b</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\">o</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">g</span><span class=\"mord mathnormal\">h</span><span class=\"mord mathnormal\">t</span><span class=\"mclose\">)</span></span></span></span></li>\n</ul>\n<p><code>隐藏状态</code> <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>h</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mo>=</mo><mi>o</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mo>⊙</mo><mspace linebreak=\"newline\"></mspace><mi>t</mi><mi>a</mi><mi>n</mi><mi>h</mi><mo stretchy=\"false\">(</mo><mi>c</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mo stretchy=\"false\">)</mo></mrow><annotation encoding=\"application/x-tex\">h\\_t=o\\_t ⊙ \\\\tanh(c\\_t)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.9251em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">o</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">⊙</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.06em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\">anh</span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">c</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mclose\">)</span></span></span></span>，记忆单元 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>c</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><annotation encoding=\"application/x-tex\">c\\_t</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9251em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">c</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span></span></span> 和隐藏状态 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>h</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><annotation encoding=\"application/x-tex\">h\\_t</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span></span></span> 的关系只是按元素应用 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mspace linebreak=\"newline\"></mspace><mi>t</mi><mi>a</mi><mi>n</mi><mi>h</mi></mrow><annotation encoding=\"application/x-tex\">\\\\tanh</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6944em;\"></span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\">anh</span></span></span></span> 函数。这意味着，记忆单元 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>c</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><annotation encoding=\"application/x-tex\">c\\_t</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9251em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">c</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span></span></span> 和隐藏状态 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>h</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><annotation encoding=\"application/x-tex\">h\\_t</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span></span></span> 的_元素个数相同_。</p>\n<blockquote>\n<p>⊙ 表示 <code>阿达玛乘积</code>。即，对应元素的乘积</p>\n</blockquote>\n<hr />\n<h4 id=\"遗忘门-forget-gate\"><a class=\"anchor\" href=\"#遗忘门-forget-gate\">#</a> 遗忘门 Forget Gate</h4>\n<ul>\n<li><strong>作用</strong>：决定从细胞状态中<strong>丢弃</strong>哪些信息</li>\n<li><strong>公式</strong>：<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>f</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mo>=</mo><mspace linebreak=\"newline\"></mspace><mi>s</mi><mi>i</mi><mi>g</mi><mi>m</mi><mi>a</mi><mspace linebreak=\"newline\"></mspace><mi>l</mi><mi>e</mi><mi>f</mi><mi>t</mi><mo stretchy=\"false\">(</mo><mi>x</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mi>W</mi><mi mathvariant=\"normal\">_</mi><msup><mi>x</mi><mrow><mo stretchy=\"false\">(</mo><mi>f</mi><mo stretchy=\"false\">)</mo></mrow></msup><mo>+</mo><mi>h</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><msup><mi>h</mi><mrow><mo stretchy=\"false\">(</mo><mi>f</mi><mo stretchy=\"false\">)</mo></mrow></msup><mo>+</mo><msup><mi>b</mi><mrow><mo stretchy=\"false\">(</mo><mi>f</mi><mo stretchy=\"false\">)</mo></mrow></msup><mspace linebreak=\"newline\"></mspace><mi>r</mi><mi>i</mi><mi>g</mi><mi>h</mi><mi>t</mi><mo stretchy=\"false\">)</mo></mrow><annotation encoding=\"application/x-tex\">f\\_t = \\\\sigma\\\\left(x\\_t W\\_x^{(f)} + h\\_{t-1} W\\_h^{(f)} + b^{(f)}\\\\right)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:0.854em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">g</span><span class=\"mord mathnormal\">ma</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.198em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">e</span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span><span class=\"mord mathnormal\">t</span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">x</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">x</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\" style=\"margin-right:0.10764em;\">f</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1.198em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\">1</span></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">h</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\" style=\"margin-right:0.10764em;\">f</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.888em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">b</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\" style=\"margin-right:0.10764em;\">f</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">g</span><span class=\"mord mathnormal\">h</span><span class=\"mord mathnormal\">t</span><span class=\"mclose\">)</span></span></span></span></li>\n</ul>\n<hr />\n<h4 id=\"输入门-input-gate\"><a class=\"anchor\" href=\"#输入门-input-gate\">#</a> 输入门 Input Gate</h4>\n<ul>\n<li><strong>作用</strong>：决定<strong>更新</strong>哪些新信息到细胞状态</li>\n<li><strong>公式</strong>：<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>i</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mo>=</mo><mspace linebreak=\"newline\"></mspace><mi>s</mi><mi>i</mi><mi>g</mi><mi>m</mi><mi>a</mi><mspace linebreak=\"newline\"></mspace><mi>l</mi><mi>e</mi><mi>f</mi><mi>t</mi><mo stretchy=\"false\">(</mo><mi>x</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mi>W</mi><mi mathvariant=\"normal\">_</mi><msup><mi>x</mi><mrow><mo stretchy=\"false\">(</mo><mi>i</mi><mo stretchy=\"false\">)</mo></mrow></msup><mo>+</mo><mi>h</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><msup><mi>h</mi><mrow><mo stretchy=\"false\">(</mo><mi>i</mi><mo stretchy=\"false\">)</mo></mrow></msup><mo>+</mo><msup><mi>b</mi><mrow><mo stretchy=\"false\">(</mo><mi>i</mi><mo stretchy=\"false\">)</mo></mrow></msup><mspace linebreak=\"newline\"></mspace><mi>r</mi><mi>i</mi><mi>g</mi><mi>h</mi><mi>t</mi><mo stretchy=\"false\">)</mo></mrow><annotation encoding=\"application/x-tex\">i\\_t = \\\\sigma\\\\left(x\\_t W\\_x^{(i)} + h\\_{t-1} W\\_h^{(i)} + b^{(i)}\\\\right)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9695em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">i</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:0.854em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\">s</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">g</span><span class=\"mord mathnormal\">ma</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.198em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">e</span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span><span class=\"mord mathnormal\">t</span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">x</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">x</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\">i</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1.198em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\">1</span></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">h</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\">i</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.888em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">b</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\">i</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">g</span><span class=\"mord mathnormal\">h</span><span class=\"mord mathnormal\">t</span><span class=\"mclose\">)</span></span></span></span></li>\n</ul>\n<hr />\n<h4 id=\"新的记忆单元\"><a class=\"anchor\" href=\"#新的记忆单元\">#</a> 新的记忆单元</h4>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774353784-image-1024x712.png\" alt=\"\" /></p>\n<p>向记忆单元添加的<code>新信息</code> ：<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mspace linebreak=\"newline\"></mspace><mi>h</mi><mi>a</mi><mi>t</mi><mrow><mi>c</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><mo>=</mo><mspace linebreak=\"newline\"></mspace><mi>t</mi><mi>a</mi><mi>n</mi><mi>h</mi><mspace linebreak=\"newline\"></mspace><mi>l</mi><mi>e</mi><mi>f</mi><mi>t</mi><mo stretchy=\"false\">(</mo><mi>x</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mi>W</mi><mi mathvariant=\"normal\">_</mi><msup><mi>x</mi><mrow><mo stretchy=\"false\">(</mo><mi>g</mi><mo stretchy=\"false\">)</mo></mrow></msup><mo>+</mo><mi>h</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><msup><mi>h</mi><mrow><mo stretchy=\"false\">(</mo><mi>g</mi><mo stretchy=\"false\">)</mo></mrow></msup><mo>+</mo><msup><mi>b</mi><mrow><mo stretchy=\"false\">(</mo><mi>g</mi><mo stretchy=\"false\">)</mo></mrow></msup><mspace linebreak=\"newline\"></mspace><mi>r</mi><mi>i</mi><mi>g</mi><mi>h</mi><mi>t</mi><mo stretchy=\"false\">)</mo></mrow><annotation encoding=\"application/x-tex\">\\\\hat{c\\_t} = \\\\tanh\\\\left(x\\_t W\\_x^{(g)} + h\\_{t-1} W\\_h^{(g)} + b^{(g)}\\\\right)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">ha</span><span class=\"mord mathnormal\">t</span><span class=\"mord\"><span class=\"mord mathnormal\">c</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6944em;\"></span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\">anh</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.198em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">e</span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span><span class=\"mord mathnormal\">t</span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">x</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">x</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\" style=\"margin-right:0.03588em;\">g</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1.198em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">h</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\">1</span></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">h</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\" style=\"margin-right:0.03588em;\">g</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.888em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">b</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.888em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mopen mtight\">(</span><span class=\"mord mathnormal mtight\" style=\"margin-right:0.03588em;\">g</span><span class=\"mclose mtight\">)</span></span></span></span></span></span></span></span></span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">g</span><span class=\"mord mathnormal\">h</span><span class=\"mord mathnormal\">t</span><span class=\"mclose\">)</span></span></span></span><br />\n<code>新的记忆单元</code>： <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>c</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi><mo>=</mo><mi>f</mi><mo>⊙</mo><mi>c</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow><mo>+</mo><mi>i</mi><mo>⊙</mo><mspace linebreak=\"newline\"></mspace><mi>h</mi><mi>a</mi><mi>t</mi><mrow><mi>c</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow></mrow><annotation encoding=\"application/x-tex\">c\\_t=f ⊙ c\\_{t-1} + i ⊙ \\\\hat{c\\_t}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.9251em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">c</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.8889em;vertical-align:-0.1944em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">⊙</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.9544em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">c</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\">1</span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7429em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">i</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">⊙</span></span><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">ha</span><span class=\"mord mathnormal\">t</span><span class=\"mord\"><span class=\"mord mathnormal\">c</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span></span></span></span>，<em>细胞状态 = 遗忘 × 旧细胞 + 输入 × 新候选</em></p>\n<p><em><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mspace linebreak=\"newline\"></mspace><mi>h</mi><mi>a</mi><mi>t</mi><mrow><mi>c</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow></mrow><annotation encoding=\"application/x-tex\">\\\\hat{c\\_t}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\">ha</span><span class=\"mord mathnormal\">t</span><span class=\"mord\"><span class=\"mord mathnormal\">c</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span></span></span></span> 也即上面的 g</em></p>\n<p><strong>这个 tanh 节点的作用不是门，而是将新的信息添加到记忆单元中</strong>。因此，它不用 <code>sigmoid 函数</code> 作为激活函数，而是使用 <code>tanh 函数</code>。</p>\n<hr />\n<h3 id=\"lstm-不会梯度消失\"><a class=\"anchor\" href=\"#lstm-不会梯度消失\">#</a> LSTM 不会梯度消失</h3>\n<p>观察记忆单元的反向传播：</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774353862-image-1024x298.png\" alt=\"\" /></p>\n<p>记忆单元的反向传播仅流过 <code>+</code> 和 <code>×</code> 节点。</p>\n<ul>\n<li><code>+ 节点</code> 将上游传来的<strong>梯度原样流出</strong>，所以梯度没有变化（退化）</li>\n<li><code>× 节点</code> 的计算并<strong>不是矩阵乘积</strong>，而是对应元素的乘积（阿达玛积）</li>\n</ul>\n<hr />\n<h3 id=\"pytorch-代码示例\"><a class=\"anchor\" href=\"#pytorch-代码示例\">#</a> PyTorch 代码示例</h3>\n<p>基础 LSTM：</p>\n<p>import torch<br />\nimport torch.nn as nn</p>\n<p>class SimpleLSTM(nn.Module):<br />\ndef __init__(self, input_size, hidden_size, num_layers, num_classes):<br />\nsuper().__init__()<br />\nself.hidden_size = hidden_size<br />\nself.num_layers = num_layers</p>\n<pre><code>    # LSTM 层\n    self.lstm = nn.LSTM(input\\_size, hidden\\_size, \n                       num\\_layers=num\\_layers,\n                       batch\\_first=True,\n                       dropout=0.3)  # 层间 Dropout\n    \n    # 全连接层\n    self.fc = nn.Linear(hidden\\_size, num\\_classes)\n\ndef forward(self, x):\n    # x: (batch\\_size, seq\\_len, input\\_size)\n    \n    # 初始化隐藏状态\n    h0 = torch.zeros(self.num\\_layers, x.size(0), self.hidden\\_size)\n    c0 = torch.zeros(self.num\\_layers, x.size(0), self.hidden\\_size)\n    \n    # 前向传播\n    out, (hn, cn) = self.lstm(x, (h0, c0))\n    # out: (batch, seq\\_len, hidden\\_size)\n    # hn: (num\\_layers, batch, hidden\\_size)\n    \n    # 取最后一个时间步的输出\n    out = self.fc(out\\[:, -1, :\\])\n    return out\n</code></pre>\n<h2 id=\"使用\"><a class=\"anchor\" href=\"#使用\">#</a> 使用</h2>\n<p>model = SimpleLSTM(input_size=100, hidden_size=128,<br />\nnum_layers=2, num_classes=2)</p>\n<p>IMDB 情感分析:</p>\n<p>import torch.optim as optim</p>\n<h2 id=\"超参数\"><a class=\"anchor\" href=\"#超参数\">#</a> 超参数</h2>\n<p>batch_size = 64<br />\nseq_len = 100<br />\nlearning_rate = 0.001<br />\nepochs = 10</p>\n<h2 id=\"模型-损失-优化器\"><a class=\"anchor\" href=\"#模型-损失-优化器\">#</a> 模型、损失、优化器</h2>\n<p>model = SentimentBiLSTM(vocab_size=10000, embed_dim=300,<br />\nhidden_size=128, num_classes=2)<br />\ncriterion = nn.CrossEntropyLoss()<br />\noptimizer = optim.Adam(model.parameters(), lr=learning_rate)</p>\n<h2 id=\"训练循环\"><a class=\"anchor\" href=\"#训练循环\">#</a> 训练循环</h2>\n<p>for epoch in range(epochs):<br />\nmodel.train()<br />\ntotal_loss = 0</p>\n<pre><code>for batch\\_x, batch\\_y in train\\_loader:\n    # batch\\_x: (64, 100), batch\\_y: (64,)\n    \n    optimizer.zero\\_grad()\n    output = model(batch\\_x)\n    loss = criterion(output, batch\\_y)\n    loss.backward()\n    \n    # 梯度裁剪（防止 LSTM 梯度爆炸）⭐\n    torch.nn.utils.clip\\_grad\\_norm\\_(model.parameters(), 1.0)\n    \n    optimizer.step()\n    total\\_loss += loss.item()\n\n# 验证\nmodel.eval()\ncorrect = 0\ntotal = 0\nwith torch.no\\_grad():\n    for batch\\_x, batch\\_y in val\\_loader:\n        output = model(batch\\_x)\n        predicted = torch.argmax(output, dim=1)\n        correct += (predicted == batch\\_y).sum().item()\n        total += batch\\_y.size(0)\n\nval\\_acc = correct / total\nprint(f&quot;Epoch &#123;epoch+1&#125;: Loss=&#123;total\\_loss:.4f&#125;, Val Acc=&#123;val\\_acc:.4f&#125;&quot;)\n</code></pre>\n<hr />\n",
            "tags": [
                "uncategorized"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/24/%E5%BE%AA%E7%8E%AF%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C-rnn/",
            "url": "https://www.bondrewd.com/2026/03/24/%E5%BE%AA%E7%8E%AF%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C-rnn/",
            "title": "循环神经网络 RNN",
            "date_published": "2026-03-24T03:54:06.000Z",
            "content_html": "<h3 id=\"背景及推导\"><a class=\"anchor\" href=\"#背景及推导\">#</a> 背景及推导</h3>\n<p><strong>需求</strong>：输入一句话，输出每个单词的褒贬性</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352814-image-1024x509.png\" alt=\"\" /></p>\n<p>假设对于上面 5 个单词，每个单词采用 300 维的 <code>词向量</code>，那么输入端就需要 1500 个词向量，这会带来几个<strong>缺点</strong>：</p>\n<ol>\n<li>输入层的<strong>节点太多</strong>了，并且是<strong>变长</strong>的，会随着句子的长短发生变化</li>\n<li>无法体现词语之间的<strong>先后顺序</strong>，仅仅只是把他们拉直展开成一个大向量，直接送入输入层（类似于 MLP）</li>\n</ol>\n<p>为了解决上述缺点，<strong>可以让上一步的信息参与下一步的运算</strong>，具体可以这样做：</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352850-image-1024x521.png\" alt=\"\" /></p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352856-image.png\" alt=\"\" /></p>\n<ul>\n<li>\n<p>第一个词 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mi>X</mi><mrow><mo>&lt;</mo><mn>1</mn><mo>&gt;</mo></mrow></msup></mrow><annotation encoding=\"application/x-tex\">X^{&lt;1&gt;}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.07847em;\">X</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mrel mtight\">&lt;</span><span class=\"mord mtight\">1</span><span class=\"mrel mtight\">&gt;</span></span></span></span></span></span></span></span></span></span></span></span> 经过非线性变换 g(<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mspace linebreak=\"newline\"></mspace><mi>c</mi><mi>d</mi><mi>o</mi><mi>t</mi></mrow><annotation encoding=\"application/x-tex\">\\\\cdot</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6944em;\"></span><span class=\"mord mathnormal\">c</span><span class=\"mord mathnormal\">d</span><span class=\"mord mathnormal\">o</span><span class=\"mord mathnormal\">t</span></span></span></span>) 后得到一个中间结果(<code>隐藏状态</code>) <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mi>h</mi><mrow><mo>&lt;</mo><mn>1</mn><mo>&gt;</mo></mrow></msup></mrow><annotation encoding=\"application/x-tex\">h^{&lt;1&gt;}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">h</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mrel mtight\">&lt;</span><span class=\"mord mtight\">1</span><span class=\"mrel mtight\">&gt;</span></span></span></span></span></span></span></span></span></span></span></span> ，它再经过一次非线性变换得到第一个输出 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mi>Y</mi><mrow><mo>&lt;</mo><mn>1</mn><mo>&gt;</mo></mrow></msup></mrow><annotation encoding=\"application/x-tex\">Y^{&lt;1&gt;}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.22222em;\">Y</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mrel mtight\">&lt;</span><span class=\"mord mtight\">1</span><span class=\"mrel mtight\">&gt;</span></span></span></span></span></span></span></span></span></span></span></span></p>\n</li>\n<li>\n<p>接着，第二个词 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mi>X</mi><mrow><mo>&lt;</mo><mn>2</mn><mo>&gt;</mo></mrow></msup></mrow><annotation encoding=\"application/x-tex\">X^{&lt;2&gt;}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.07847em;\">X</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mrel mtight\">&lt;</span><span class=\"mord mtight\">2</span><span class=\"mrel mtight\">&gt;</span></span></span></span></span></span></span></span></span></span></span></span> 与刚才的隐藏状态 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mi>h</mi><mrow><mo>&lt;</mo><mn>1</mn><mo>&gt;</mo></mrow></msup></mrow><annotation encoding=\"application/x-tex\">h^{&lt;1&gt;}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">h</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mrel mtight\">&lt;</span><span class=\"mord mtight\">1</span><span class=\"mrel mtight\">&gt;</span></span></span></span></span></span></span></span></span></span></span></span> 一起参与非线性变换，得到隐藏状态 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mi>h</mi><mrow><mo>&lt;</mo><mn>2</mn><mo>&gt;</mo></mrow></msup></mrow><annotation encoding=\"application/x-tex\">h^{&lt;2&gt;}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">h</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mrel mtight\">&lt;</span><span class=\"mord mtight\">2</span><span class=\"mrel mtight\">&gt;</span></span></span></span></span></span></span></span></span></span></span></span> ，它再经过一次非线性变换得到 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><msup><mi>Y</mi><mrow><mo>&lt;</mo><mn>2</mn><mo>&gt;</mo></mrow></msup></mrow><annotation encoding=\"application/x-tex\">Y^{&lt;2&gt;}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.8141em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.22222em;\">Y</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.8141em;\"><span style=\"top:-3.063em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mrel mtight\">&lt;</span><span class=\"mord mtight\">2</span><span class=\"mrel mtight\">&gt;</span></span></span></span></span></span></span></span></span></span></span></span></p>\n</li>\n<li>\n<p>g(<span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mspace linebreak=\"newline\"></mspace><mi>c</mi><mi>d</mi><mi>o</mi><mi>t</mi></mrow><annotation encoding=\"application/x-tex\">\\\\cdot</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6944em;\"></span><span class=\"mord mathnormal\">c</span><span class=\"mord mathnormal\">d</span><span class=\"mord mathnormal\">o</span><span class=\"mord mathnormal\">t</span></span></span></span>)：<em>激活函数，一般取双曲正切 tanh</em></p>\n</li>\n<li>\n<p><em><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>x</mi><mi>h</mi></mrow></mrow><annotation encoding=\"application/x-tex\">W\\_{xh}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">x</span><span class=\"mord mathnormal\">h</span></span></span></span></span>：专门针对词向量的矩阵</em></p>\n</li>\n<li>\n<p><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>h</mi><mi>h</mi></mrow></mrow><annotation encoding=\"application/x-tex\">W\\_{hh}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">hh</span></span></span></span></span>：<em>专门针对隐藏状态的矩阵</em></p>\n</li>\n<li>\n<p><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>W</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>h</mi><mi>y</mi></mrow></mrow><annotation encoding=\"application/x-tex\">W\\_{hy}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.13889em;\">W</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">h</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">y</span></span></span></span></span>：<em>专门针对输出结果的矩阵</em></p>\n</li>\n<li>\n<p><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>b</mi></mrow><annotation encoding=\"application/x-tex\">b</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.6944em;\"></span><span class=\"mord mathnormal\">b</span></span></span></span>：<em>偏置项 bias</em></p>\n</li>\n</ul>\n<p>上图流程可以简化为下面两种等效表达方式：</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352905-image-1024x507.png\" alt=\"\" /></p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352921-image-1024x377.png\" alt=\"\" /></p>\n<blockquote>\n<p><em>参考链接：<a href=\"https://www.bilibili.com/video/BV1MNoRYEEVM/?spm_id_from=333.1387.favlist.content.click&amp;vd_source=c5551749b3e52456db43941bf55d8d6b\">语言居然可以被计算出来？从 RNN 到 Transformer【AI入门05】_哔哩哔哩_bilibili</a></em></p>\n</blockquote>\n<hr />\n<h3 id=\"rnn-的问题梯度消失爆炸\"><a class=\"anchor\" href=\"#rnn-的问题梯度消失爆炸\">#</a> RNN 的问题：梯度消失/爆炸</h3>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352959-image.png\" alt=\"\" /></p>\n<p><strong>反向传播时</strong>，梯度需要从后往前传：</p>\n<p>∂L∂W=∂L∂sn×∂sn∂sn−1×∂sn−1∂sn−2×...×∂s1∂W\\frac{\\partial L}{\\partial W}=\\frac{\\partial L}{\\partial s_n}\\times\\frac{\\partial s_n}{\\partial s_{n-1}}\\times\\frac{\\partial s_{n-1}}{\\partial s_{n-2}}\\times ... \\times\\frac{\\partial s_1}{\\partial W}</p>\n<ul>\n<li>如果 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mspace linebreak=\"newline\"></mspace><mi>f</mi><mi>r</mi><mi>a</mi><mi>c</mi><mrow><mspace linebreak=\"newline\"></mspace><mi>p</mi><mi>a</mi><mi>r</mi><mi>t</mi><mi>i</mi><mi>a</mi><mi>l</mi><mi>s</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><mrow><mspace linebreak=\"newline\"></mspace><mi>p</mi><mi>a</mi><mi>r</mi><mi>t</mi><mi>i</mi><mi>a</mi><mi>l</mi><mi>s</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></mrow><mo>&lt;</mo><mn>1</mn></mrow><annotation encoding=\"application/x-tex\">\\\\frac{\\\\partial s\\_t}{\\\\partial s\\_{t-1}} &lt; 1</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">a</span><span class=\"mord mathnormal\">c</span><span class=\"mord\"><span class=\"mspace newline\"></span><span class=\"mord mathnormal\">p</span><span class=\"mord mathnormal\">a</span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\">ia</span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">s</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span><span class=\"mord\"><span class=\"mspace newline\"></span><span class=\"mord mathnormal\">p</span><span class=\"mord mathnormal\">a</span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\">ia</span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">s</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\">1</span></span></span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&lt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">1</span></span></span></span>（如 0.5），连乘 100 次 → <code>0.5^100 ≈ 0</code> → <strong>梯度消失</strong> → 前面的权重几乎不更新 → 模型<strong>记不住长距离信息</strong></li>\n<li>如果 <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mspace linebreak=\"newline\"></mspace><mi>f</mi><mi>r</mi><mi>a</mi><mi>c</mi><mrow><mspace linebreak=\"newline\"></mspace><mi>p</mi><mi>a</mi><mi>r</mi><mi>t</mi><mi>i</mi><mi>a</mi><mi>l</mi><mi>s</mi><mi mathvariant=\"normal\">_</mi><mi>t</mi></mrow><mrow><mspace linebreak=\"newline\"></mspace><mi>p</mi><mi>a</mi><mi>r</mi><mi>t</mi><mi>i</mi><mi>a</mi><mi>l</mi><mi>s</mi><mi mathvariant=\"normal\">_</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></mrow><mo>&gt;</mo><mn>1</mn></mrow><annotation encoding=\"application/x-tex\">\\\\frac{\\\\partial s\\_t}{\\\\partial s\\_{t-1}} &gt; 1</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"mspace newline\"></span><span class=\"base\"><span class=\"strut\" style=\"height:1.0044em;vertical-align:-0.31em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.10764em;\">f</span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">a</span><span class=\"mord mathnormal\">c</span><span class=\"mord\"><span class=\"mspace newline\"></span><span class=\"mord mathnormal\">p</span><span class=\"mord mathnormal\">a</span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\">ia</span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">s</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord mathnormal\">t</span></span><span class=\"mord\"><span class=\"mspace newline\"></span><span class=\"mord mathnormal\">p</span><span class=\"mord mathnormal\">a</span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">r</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\">ia</span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">s</span><span class=\"mord\" style=\"margin-right:0.02778em;\">_</span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\">1</span></span></span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">&gt;</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.6444em;\"></span><span class=\"mord\">1</span></span></span></span>（如 1.5），连乘 100 次 → <code>1.5^100 ≈ 亿</code> → <strong>梯度爆炸</strong> → 权重变成 <code>NaN</code> → 模型训练崩溃</li>\n</ul>\n<p><strong>解决方案</strong>：<br />\n原始的 RNN 几乎已被淘汰</p>\n<p>方法</p>\n<p>说明</p>\n<p><strong>梯度裁剪</strong></p>\n<p>限制梯度最大值（如 1.0）</p>\n<p><strong>ReLU 激活</strong></p>\n<p>缓解梯度消失（但 RNN 常用 tanh）</p>\n<p><strong>LSTM/GRU</strong></p>\n<p><strong>最有效的方案</strong> ⭐ LSTM 和 GRU 中增加了一种名为“门”的结构</p>\n<blockquote>\n<p>Gated RNN</p>\n<p>在 RNN 的学习中，<code>梯度消失</code> 也是一个大问题。为了解决这个问题，需要从根本上改变 RNN 层的结构。人们已经提出了诸多 Gated RNN 框架，其中具有代表性的有 <a href=\"https://iplusjia.top/2026/03/24/%e9%95%bf%e7%9f%ad%e6%9c%9f%e8%ae%b0%e5%bf%86-lstm/\">LSTM</a> 和 <code>GRU</code></p>\n</blockquote>\n<hr />\n<h3 id=\"pytorch-代码示例\"><a class=\"anchor\" href=\"#pytorch-代码示例\">#</a> PyTorch 代码示例</h3>\n<p>基础 RNN：</p>\n<p>import torch<br />\nimport torch.nn as nn</p>\n<p>class SimpleRNN(nn.Module):<br />\ndef __init__(self, input_size, hidden_size, num_classes):<br />\nsuper().__init__()<br />\nself.rnn = nn.RNN(input_size, hidden_size, batch_first=True)<br />\nself.fc = nn.Linear(hidden_size, num_classes)</p>\n<pre><code>def forward(self, x):\n    # x: (batch, seq\\_len, input\\_size)\n    out, hidden = self.rnn(x)  # out: (batch, seq\\_len, hidden)\n    # 取最后一个时间步\n    out = self.fc(hidden\\[-1\\])\n    return out\n</code></pre>\n<h2 id=\"使用\"><a class=\"anchor\" href=\"#使用\">#</a> 使用</h2>\n<p>model = SimpleRNN(input_size=100, hidden_size=128, num_classes=2)</p>\n<p>用上面的 model，结合 <code>IMDB 数据集</code>进行情感分析：</p>\n<p># 超参数<br />\nbatch_size = 64<br />\nseq_len = 100<br />\nlearning_rate = 0.001</p>\n<h2 id=\"损失和优化器\"><a class=\"anchor\" href=\"#损失和优化器\">#</a> 损失和优化器</h2>\n<p>criterion = nn.CrossEntropyLoss()<br />\noptimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)</p>\n<h2 id=\"训练\"><a class=\"anchor\" href=\"#训练\">#</a> 训练</h2>\n<p>for epoch in range(10):<br />\nmodel.train()<br />\nfor batch_x, batch_y in train_loader:<br />\noptimizer.zero_grad()<br />\noutput = model(batch_x)<br />\nloss = criterion(output, batch_y)<br />\nloss.backward()</p>\n<pre><code>    # 梯度裁剪（防止 RNN 梯度爆炸）⭐\n    torch.nn.utils.clip\\_grad\\_norm\\_(model.parameters(), 1.0)\n    \n    optimizer.step()\n\n# 验证\nmodel.eval()\nval\\_acc = evaluate(model, val\\_loader)\nprint(f&quot;Epoch &#123;epoch&#125;: Val Acc = &#123;val\\_acc:.4f&#125;&quot;)\n</code></pre>\n<hr />\n",
            "tags": [
                "uncategorized"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/24/word2vec/",
            "url": "https://www.bondrewd.com/2026/03/24/word2vec/",
            "title": "Word2Vec",
            "date_published": "2026-03-24T03:46:01.000Z",
            "content_html": "<p><code>Word2Vec</code> 其实就是一个<strong>没有隐藏层激活函数</strong>的 2 层神经网络（<code>浅层神经网络</code>），用来将单词映射为<strong>低维稠密向量</strong>。</p>\n<hr />\n<h3 id=\"1-核心原理\"><a class=\"anchor\" href=\"#1-核心原理\">#</a> 1. 核心原理</h3>\n<p>基于<strong>分布假设</strong>。通过大量文本语料学习词语的共现规律，将语义相似的词映射到向量空间中彼此靠近的位置</p>\n<blockquote>\n<p>分布假设</p>\n<p>&quot;A word is characterized by the company it keeps.&quot;</p>\n<p>（一个词是由它周围的词决定的。）</p>\n</blockquote>\n<hr />\n<p><code>Word2Vec</code> 包含两种浅层神经网络结构，核心目标是学习词向量，输入层到隐藏层为<code>词嵌入映射</code>，隐藏层到输出层为<code>概率预测</code>。</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352468-image.png\" alt=\"\" /></p>\n<h5 id=\"21-cbowcontinuous-bag-of-words连续词袋模型\"><a class=\"anchor\" href=\"#21-cbowcontinuous-bag-of-words连续词袋模型\">#</a> 2.1 CBOW（Continuous Bag-of-Words，连续词袋模型）</h5>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352492-image-1024x672.png\" alt=\"\" /></p>\n<ul>\n<li><strong>核心任务</strong>：用<strong>上下文词</strong>预测<strong>中心词</strong></li>\n<li><strong>特点</strong>：计算效率高，适合<strong>大规模语料</strong>；对高频词效果更优</li>\n<li><strong>类比</strong>：像 “<em>完形填空</em>”，根据周围词语推测中间缺失的词</li>\n</ul>\n<h5 id=\"22-skip-gram跳字模型\"><a class=\"anchor\" href=\"#22-skip-gram跳字模型\">#</a> 2.2 Skip-gram（跳字模型）</h5>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774352527-image-1024x805.png\" alt=\"\" /></p>\n<ul>\n<li><strong>核心任务</strong>：用<strong>中心词</strong>预测<strong>上下文词</strong></li>\n<li><strong>特点</strong>：对<strong>低频词、生僻词</strong>效果更好；适合小数据集</li>\n<li><strong>类比</strong>：像 “联想扩散”，由一个词联想到周围相关的词</li>\n</ul>\n<blockquote>\n<p>💡 <strong>经验</strong>：默认首选 <code>Skip-Gram</code>，除非数据量极大且追求速度。</p>\n</blockquote>\n<hr />\n<h3 id=\"3-训练优化技巧\"><a class=\"anchor\" href=\"#3-训练优化技巧\">#</a> 3. 训练优化技巧</h3>\n<p>为解决<strong>大规模语料</strong>下的计算瓶颈，<code>Word2Vec</code> 引入两大核心优化，大幅提升训练效率。</p>\n<h5 id=\"0-关键点我们要的是什么\"><a class=\"anchor\" href=\"#0-关键点我们要的是什么\">#</a> 0. 🔑 关键点：我们要的是什么？</h5>\n<p>训练完成后：</p>\n<ul>\n<li><strong>输出层权重</strong>：扔掉 ❌</li>\n<li><strong>投影层权重矩阵</strong>：保留 ✅</li>\n</ul>\n<p>这个 <strong>V×N 的矩阵</strong>，<strong>每一行就是一个词的向量</strong></p>\n<h5 id=\"1-负采样negative-sampling-最常用\"><a class=\"anchor\" href=\"#1-负采样negative-sampling-最常用\">#</a> 1. 负采样（Negative Sampling）⭐ 最常用</h5>\n<ul>\n<li><strong>思想</strong>：不需要更新所有词的权重，只更新<strong>正确的词</strong>和<strong>几个错误的词</strong>。</li>\n<li><strong>原理</strong>：将多分类问题转化为<strong>二分类任务</strong></li>\n<li><strong>操作</strong>：对每个正样本（中心词 + 真实上下文词），随机采样 K 个负样本（中心词 + 噪声词）</li>\n<li><strong>优势</strong>：仅更新目标词与采样词的参数，避免全词表计算，<strong>训练速度提升显著</strong></li>\n</ul>\n<h5 id=\"2-层次-softmaxhierarchical-softmax\"><a class=\"anchor\" href=\"#2-层次-softmaxhierarchical-softmax\">#</a> 2. 层次 Softmax（Hierarchical Softmax）</h5>\n<ul>\n<li><strong>原理</strong>：用 <code>Huffman树</code> 组织词汇表，将根节点到叶节点的 <code>路径概率乘积</code> 作为预测结果，类似于<code>二叉搜索</code></li>\n<li><strong>优势</strong>：将时间复杂度从 <code>O (V)</code> 降至 <code>O (logV)</code>，V 为词汇量，适合<strong>超大词汇表</strong></li>\n</ul>\n<hr />\n<h3 id=\"4-局限性与发展\"><a class=\"anchor\" href=\"#4-局限性与发展\">#</a> 4. 局限性与发展</h3>\n<h5 id=\"41-主要局限\"><a class=\"anchor\" href=\"#41-主要局限\">#</a> 4.1 主要局限</h5>\n<ul>\n<li><strong>静态向量</strong>：一词一向量，无法处理<strong>一词多义</strong>（如 “bank” 既指银行也指河岸）</li>\n<li><strong>忽略词序</strong>：依赖固定窗口，无法捕捉<strong>长距离语义依赖</strong>和<strong>词序信息</strong></li>\n<li><strong>未登录词 OOV</strong>：无法处理训练语料外的新词（如新兴术语、专有名词）</li>\n<li><strong>上下文无关</strong>：同一词在不同语境下向量相同，语义表达不够精细</li>\n</ul>\n<h5 id=\"42-后续演进\"><a class=\"anchor\" href=\"#42-后续演进\">#</a> 4.2 后续演进</h5>\n<ul>\n<li><strong>FastText</strong>：引入子词信息，支持未登录词生成</li>\n<li><strong>GloVe</strong>：结合全局统计与局部上下文，提升语义表达</li>\n<li><strong>Transformer 系列</strong>：动态上下文编码，解决一词多义与长距离依赖问题（如 BERT、GPT）</li>\n</ul>\n<hr />\n<h3 id=\"word2vec-示例代码\"><a class=\"anchor\" href=\"#word2vec-示例代码\">#</a> Word2Vec 示例代码</h3>\n<p>安装依赖库：<code>pip install gensim nltk</code></p>\n<p>import gensim<br />\nfrom gensim.models import Word2Vec<br />\nfrom gensim.utils import simple_preprocess<br />\nimport nltk<br />\nfrom nltk.corpus import stopwords</p>\n<h2 id=\"-1-数据准备与预处理-\"><a class=\"anchor\" href=\"#-1-数据准备与预处理-\">#</a> ---------------------- 1. 数据准备与预处理 ----------------------</h2>\n<h2 id=\"下载nltk停用词仅首次运行需要\"><a class=\"anchor\" href=\"#下载nltk停用词仅首次运行需要\">#</a> 下载nltk停用词（仅首次运行需要）</h2>\n<p>nltk.download('stopwords')<br />\nstop_words = stopwords.words('english')</p>\n<h2 id=\"示例语料可替换为你自己的文本文件大规模语料\"><a class=\"anchor\" href=\"#示例语料可替换为你自己的文本文件大规模语料\">#</a> 示例语料（可替换为你自己的文本文件/大规模语料）</h2>\n<p>corpus = [<br />\n&quot;Natural language processing is a subfield of artificial intelligence&quot;,<br />\n&quot;Word2Vec is a popular technique for word embedding in NLP&quot;,<br />\n&quot;Word embedding converts words into numerical vectors&quot;,<br />\n&quot;Semantic similarity can be measured using word vectors&quot;,<br />\n&quot;King minus man plus woman equals queen in word2vec&quot;,<br />\n&quot;Tokyo is the capital of Japan, Beijing is the capital of China&quot;,<br />\n&quot;Machine learning models use word vectors as input features&quot;,<br />\n&quot;Cat likes eating fish and dog loves enjoying bones&quot;<br />\n]</p>\n<h2 id=\"文本预处理函数分词-去除停用词-过滤短词\"><a class=\"anchor\" href=\"#文本预处理函数分词-去除停用词-过滤短词\">#</a> 文本预处理函数：分词 + 去除停用词 + 过滤短词</h2>\n<p>def preprocess(text):<br />\n# 分词（gensim内置的简单分词，适合英文）<br />\ntokens = simple_preprocess(text, deacc=True)  # deacc=True去除标点<br />\n# 过滤停用词和长度&lt;2的词<br />\ntokens = [token for token in tokens if token not in stop_words and len(token) &gt; 2]<br />\nreturn tokens</p>\n<h2 id=\"对语料库进行预处理得到训练用的句子列表每个句子是分词后的token列表\"><a class=\"anchor\" href=\"#对语料库进行预处理得到训练用的句子列表每个句子是分词后的token列表\">#</a> 对语料库进行预处理，得到训练用的句子列表（每个句子是分词后的token列表）</h2>\n<p>processed_corpus = [preprocess(sentence) for sentence in corpus]<br />\nprint(&quot;预处理后的语料：&quot;)<br />\nfor sent in processed_corpus:<br />\nprint(sent)</p>\n<h2 id=\"-2-训练word2vec模型-\"><a class=\"anchor\" href=\"#-2-训练word2vec模型-\">#</a> ---------------------- 2. 训练Word2Vec模型 ----------------------</h2>\n<h2 id=\"核心参数说明新手重点关注\"><a class=\"anchor\" href=\"#核心参数说明新手重点关注\">#</a> 核心参数说明（新手重点关注）：</h2>\n<h2 id=\"-vector_size词向量维度常用50100300小语料用50即可\"><a class=\"anchor\" href=\"#-vector_size词向量维度常用50100300小语料用50即可\">#</a> - vector_size：词向量维度（常用50/100/300，小语料用50即可）</h2>\n<h2 id=\"-window上下文窗口大小中心词左右各window个词\"><a class=\"anchor\" href=\"#-window上下文窗口大小中心词左右各window个词\">#</a> - window：上下文窗口大小（中心词左右各window个词）</h2>\n<h2 id=\"-min_count最小词频低于该值的词忽略过滤低频噪声\"><a class=\"anchor\" href=\"#-min_count最小词频低于该值的词忽略过滤低频噪声\">#</a> - min_count：最小词频，低于该值的词忽略（过滤低频噪声）</h2>\n<h2 id=\"-sg训练模型类型sg0为cbowsg1为skip-gram\"><a class=\"anchor\" href=\"#-sg训练模型类型sg0为cbowsg1为skip-gram\">#</a> - sg：训练模型类型，sg=0为CBOW，sg=1为Skip-gram</h2>\n<h2 id=\"-workers并行训练线程数根据cpu核心数调整\"><a class=\"anchor\" href=\"#-workers并行训练线程数根据cpu核心数调整\">#</a> - workers：并行训练线程数（根据CPU核心数调整）</h2>\n<p>model = Word2Vec(<br />\nsentences=processed_corpus,<br />\nvector_size=50,        # 词向量维度<br />\nwindow=3,              # 上下文窗口<br />\nmin_count=1,           # 保留所有出现过的词（小语料专用）<br />\nsg=1,                  # 使用Skip-gram模型（对小语料更友好）<br />\nworkers=4,             # 4线程训练<br />\nepochs=100             # 训练轮数（小语料需增加轮数保证效果）<br />\n)</p>\n<h2 id=\"保存模型可选后续可直接加载\"><a class=\"anchor\" href=\"#保存模型可选后续可直接加载\">#</a> 保存模型（可选，后续可直接加载）</h2>\n<p>model.save(&quot;word2vec_demo.model&quot;)</p>\n<h2 id=\"加载模型后续使用时model-word2vecloadword2vec_demomodel\"><a class=\"anchor\" href=\"#加载模型后续使用时model-word2vecloadword2vec_demomodel\">#</a> 加载模型（后续使用时）：model = Word2Vec.load(&quot;word2vec_demo.model&quot;)</h2>\n<h2 id=\"-3-模型使用示例-\"><a class=\"anchor\" href=\"#-3-模型使用示例-\">#</a> ---------------------- 3. 模型使用示例 ----------------------</h2>\n<h2 id=\"31-查看单个词的向量\"><a class=\"anchor\" href=\"#31-查看单个词的向量\">#</a> 3.1 查看单个词的向量</h2>\n<p>print(&quot;\\n=== 查看'plus'的词向量（前10维）===&quot;)<br />\nprint(model.wv['plus'][:10])  # wv是word vector的缩写，存储所有词向量量</p>\n<h2 id=\"32-计算两个词的语义相似度\"><a class=\"anchor\" href=\"#32-计算两个词的语义相似度\">#</a> 3.2 计算两个词的语义相似度</h2>\n<p>print(&quot;\\n=== 计算语义相似度 ===&quot;)<br />\nprint(f&quot;like vs love: {model.wv.similarity('like', 'love'):.4f}&quot;)<br />\nprint(f&quot;beijing vs tokyo: {model.wv.similarity('beijing', 'tokyo'):.4f}&quot;)<br />\nprint(f&quot;king vs queen: {model.wv.similarity('king', 'queen'):.4f}&quot;)<br />\nprint(f&quot;china vs japan: {model.wv.similarity('china', 'japan'):.4f}&quot;)</p>\n<h2 id=\"33-找最相似的词\"><a class=\"anchor\" href=\"#33-找最相似的词\">#</a> 3.3 找最相似的词</h2>\n<p>print(&quot;\\n=== 找与'like'最相似的5个词 ===&quot;)<br />\nsimilar_words = model.wv.most_similar('like', topn=5)<br />\nfor word, score in similar_words:<br />\nprint(f&quot;{word}: {score:.4f}&quot;)</p>\n<h2 id=\"34-语义类比国王-男人女人王后\"><a class=\"anchor\" href=\"#34-语义类比国王-男人女人王后\">#</a> 3.4 语义类比（国王-男人+女人=王后）</h2>\n<p>print(&quot;\\n=== 语义类比：king - man + woman == ? ===&quot;)<br />\ntry:<br />\nanalogy = model.wv.most_similar(positive=['king', 'woman'], negative=['man'], topn=1)<br />\nprint(f&quot;结果：{analogy[0][0]} (相似度：{analogy[0][1]:.4f})&quot;)<br />\nexcept KeyError as e:<br />\nprint(f&quot;类比失败：缺少词 {e}（语料太小导致）&quot;)</p>\n<p><strong>参数调整</strong>：</p>\n<ul>\n<li>大规模语料：可将<code>vector_size</code>调至 100/300，<code>min_count</code>调至 5/10（过滤低频词），<code>epochs</code>调至 10-20；</li>\n<li>小语料：保持 <code>min_count=1</code>，增加 <code>epochs</code>（如 100）保证训练效果。</li>\n</ul>\n<p><strong>如果结果奇怪</strong>：<br />\n<em>根本原因：语料库太小了</em><br />\n直接用别人训练好的模型，不要自己训练小语料：</p>\n<p>from gensim.models import KeyedVectors</p>\n<h2 id=\"加载-google-预训练的-word2vec-300-维300-万词\"><a class=\"anchor\" href=\"#加载-google-预训练的-word2vec-300-维300-万词\">#</a> 加载 Google 预训练的 Word2Vec (300 维，300 万词)</h2>\n<h2 id=\"下载地址httpscodegooglecomarchivepword2vec\"><a class=\"anchor\" href=\"#下载地址httpscodegooglecomarchivepword2vec\">#</a> 下载地址：<a href=\"https://code.google.com/archive/p/word2vec/\">https://code.google.com/archive/p/word2vec/</a></h2>\n<p>model = KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True)</p>\n<h2 id=\"现在测试结果会正常\"><a class=\"anchor\" href=\"#现在测试结果会正常\">#</a> 现在测试结果会正常！</h2>\n<p>print(model.wv.similarity('like', 'love'))  # 应该 &gt; 0.5<br />\nprint(model.wv.similarity('king', 'queen'))  # 应该 &gt; 0.6</p>\n<h2 id=\"类比也会正确\"><a class=\"anchor\" href=\"#类比也会正确\">#</a> 类比也会正确</h2>\n<p>result = model.wv.most_similar(positive=['king', 'woman'], negative=['man'])<br />\nprint(result[0][0])  # 应该输出 'queen'</p>\n<hr />\n",
            "tags": [
                "uncategorized"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/24/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%92%8C%E5%8D%95%E8%AF%8D%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E8%A1%A8%E7%A4%BA/",
            "url": "https://www.bondrewd.com/2026/03/24/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%92%8C%E5%8D%95%E8%AF%8D%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E8%A1%A8%E7%A4%BA/",
            "title": "自然语言和单词的分布式表示",
            "date_published": "2026-03-24T03:19:50.000Z",
            "content_html": "<p><strong>核心问题</strong>：神经网络只能处理<strong>数字</strong>，但人类语言是<strong>文字</strong>。<br />\n<em>如下，怎么把&quot;我&quot;、&quot;喜欢&quot;、&quot;深度&quot;、&quot;学习&quot;变成向量？</em></p>\n<blockquote>\n<p>人类： &quot;我喜欢深度学习&quot;<br />\n↓ ???<br />\n计算机： [?, ?, ?, ?, ?, ?]</p>\n</blockquote>\n<hr />\n<h3 id=\"表示方法的演进\"><a class=\"anchor\" href=\"#表示方法的演进\">#</a> 表示方法的演进</h3>\n<h5 id=\"1️⃣-最早期one-hot-编码独热编码\"><a class=\"anchor\" href=\"#1️⃣-最早期one-hot-编码独热编码\">#</a> 1️⃣ 最早期：One-Hot 编码（独热编码）</h5>\n<p><strong>方法</strong>：每个词用一个<strong>超长向量</strong>表示，只有一个位置是 1，其他都是 0。</p>\n<p>词典：[&quot;我&quot;, &quot;喜欢&quot;, &quot;讨厌&quot;, &quot;深度&quot;, &quot;学习&quot;, &quot;电影&quot;, ...]  假设共 10000 词</p>\n<p>&quot;我&quot;   → [1, 0, 0, 0, 0, 0, ..., 0]  (第 1 位是 1)<br />\n&quot;喜欢&quot; → [0, 1, 0, 0, 0, 0, ..., 0]  (第 2 位是 1)<br />\n&quot;讨厌&quot; → [0, 0, 1, 0, 0, 0, ..., 0]  (第 3 位是 1)<br />\n&quot;学习&quot; → [0, 0, 0, 0, 1, 0, ..., 0]  (第 5 位是 1)</p>\n<p>优点</p>\n<p>缺点</p>\n<p>简单直观</p>\n<p><strong>维度灾难</strong>：词典 10 万词 = 10 万维向量</p>\n<p>容易实现</p>\n<p><strong>语义鸿沟</strong>：任意两个词向量正交（内积=0）</p>\n<p><strong>无法表示相似性</strong>：&quot;喜欢&quot;和&quot;讨厌&quot;距离一样远（如下例）</p>\n<p>向量空间示意（One-Hot）：</p>\n<p>&quot;喜欢&quot; ●                 所有词都在坐标轴上<br />\n│                彼此距离相等<br />\n│<br />\n●──────┼──────●  &quot;讨厌&quot;<br />\n&quot;me&quot;     │      &quot;学习&quot;<br />\n│<br />\n●<br />\n&quot;电影&quot;</p>\n<p>问题：无法表达&quot;喜欢&quot;和&quot;爱&quot;更接近 ❌</p>\n<hr />\n<h5 id=\"2️⃣-突破分布式表示distributed-representation\"><a class=\"anchor\" href=\"#2️⃣-突破分布式表示distributed-representation\">#</a> 2️⃣ 突破：分布式表示（Distributed Representation）⭐</h5>\n<p><strong>核心思想</strong>（Hinton, 1986）：用低维稠密向量表示单词，语义相似的词向量也相似。</p>\n<p>词典：10000 词  →  向量维度：300</p>\n<p>&quot;我&quot;   → [0.25, -0.31, 0.67, ..., 0.12]  (300 维稠密向量)<br />\n&quot;喜欢&quot; → [0.82, -0.45, 0.33, ..., 0.56]<br />\n&quot;爱&quot;   → [0.79, -0.42, 0.35, ..., 0.54]  ← 和&quot;喜欢&quot;很接近！<br />\n&quot;讨厌&quot; → [-0.75, 0.51, -0.28, ..., -0.61] ← 和&quot;喜欢&quot;距离远<br />\n&quot;学习&quot; → [0.45, -0.12, 0.78, ..., 0.33]</p>\n<p>向量空间示意（分布式表示）：</p>\n<pre><code>    &quot;爱&quot; ●\n        ╱\n       ╱   ← 语义相近，向量距离近 ✅\n      ╱\n</code></pre>\n<p>&quot;喜欢&quot; ●───────● &quot;热爱&quot;<br />\n╲<br />\n╲<br />\n╲<br />\n● &quot;讨厌&quot;  ← 语义相反，距离远</p>\n<p>&quot;学习&quot; ●          ● &quot;电影&quot;  ← 无关词，距离中等</p>\n<blockquote>\n<p><strong>为什么叫&quot;分布式&quot;？</strong></p>\n<ul>\n<li><strong>One-Hot</strong>：信息集中在<strong>一个位置</strong>（向量中只有 1 个 1，其余都是 0）</li>\n<li><strong>分布式</strong>：信息<strong>分布在整个向量</strong>的所有维度</li>\n</ul>\n</blockquote>\n<hr />\n<h3 id=\"向量相似度\"><a class=\"anchor\" href=\"#向量相似度\">#</a> 向量相似度</h3>\n<p>可以用两个词向量之间的 <code>内积</code> 或 <code>余弦相似度</code> 来表示向量之间的相关性</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774350936-image-1024x588.png\" alt=\"\" /></p>\n<ul>\n<li>把所有词向量组成的大矩阵称为 <code>嵌入矩阵</code>：每一列都是一个词向量。</li>\n<li>这个矩阵可以通过 <code>word2vec</code> 等方式训练获得</li>\n</ul>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1774350962-image-1024x605.png\" alt=\"\" /></p>\n<ul>\n<li><em>词向量的维度很高，所以它所在空间的维度也很高，这个空间叫做 <code>潜空间</code>。对于高维潜空间，可以通过降维投影等操作直观感受，参考： <a href=\"https://projector.tensorflow.org/\">https://projector.tensorflow.org/</a> 。</em></li>\n</ul>\n<hr />\n<h4 id=\"学习词向量的经典方式\"><a class=\"anchor\" href=\"#学习词向量的经典方式\">#</a> 学习词向量的经典方式</h4>\n<h5 id=\"1️⃣-word2vec2013-最经典\"><a class=\"anchor\" href=\"#1️⃣-word2vec2013-最经典\">#</a> 1️⃣ Word2Vec（2013）⭐ 最经典</h5>\n<ul>\n<li><strong>提出者</strong>：Google 的 Tomas Mikolov</li>\n<li><strong>核心思想</strong>：<strong>一个词的语义由它周围的词决定，文本中离得越近的词语相似度越高</strong>（分布假说）</li>\n<li><strong>两种训练方式</strong>：</li>\n</ul>\n<p>模型</p>\n<p>输入</p>\n<p>输出</p>\n<p>比喻</p>\n<p><strong>Skip-Gram 跳元模型</strong>（<em>常用</em>）</p>\n<p><code>中心词</code></p>\n<p><code>上下文词</code></p>\n<p>&quot;给定这个词，预测周围&quot;</p>\n<p><strong>CBOW 连续词袋</strong></p>\n<p><code>上下文词</code></p>\n<p><code>中心词</code></p>\n<p>&quot;猜中间是什么词&quot;</p>\n<p>CBOW:     [&quot;我&quot;, &quot;深度&quot;, &quot;学习&quot;]  →  &quot;喜欢&quot;<br />\n(上下文)                  (中心词)</p>\n<p>Skip-Gram:  &quot;喜欢&quot;  →  [&quot;我&quot;, &quot;深度&quot;, &quot;学习&quot;]<br />\n(中心词)    (上下文)</p>\n<p><strong>Word2Vec 的架构</strong>：</p>\n<pre><code>    输入层          隐藏层         输出层\n   (词索引)        (词向量)       (概率分布)\n      \n      ●              ●              ● ● ● ●\n      │              │              │ │ │ │\n      │              │              │ │ │ │\n输入词索引  →   查找向量表  →   Softmax  →  预测词\n(one-hot)      (V×N 矩阵)        (V 维)\n\nV = 词典大小 (如 10000)\nN = 向量维度 (如 300)\n</code></pre>\n<blockquote>\n<p>💡 关键：训练完成后，<strong>隐藏层的权重矩阵</strong>就是<code>词向量表</code>！</p>\n</blockquote>\n<hr />\n<h5 id=\"2️⃣-glove2014\"><a class=\"anchor\" href=\"#2️⃣-glove2014\">#</a> 2️⃣ GloVe（2014）</h5>\n<ul>\n<li><strong>提出者</strong>：Stanford 的 Jeffrey Pennington</li>\n<li><strong>核心思想</strong>：基于<strong>全局词共现统计</strong>，而不是局部窗口。<br />\n500</li>\n<li><code>GloVe</code> <strong>目标</strong>：让上面 <code>共现矩阵</code>（<code>co-occurence matrix</code>）向量内积 ≈ log(共现次数)</li>\n</ul>\n<hr />\n<h5 id=\"3️⃣-fasttext2016\"><a class=\"anchor\" href=\"#3️⃣-fasttext2016\">#</a> 3️⃣ FastText（2016）</h5>\n<ul>\n<li><strong>提出者</strong>：Facebook AI</li>\n<li><strong>核心思想</strong>：考虑<strong>词内部的字符 n-gram</strong>。</li>\n</ul>\n<p>优点</p>\n<p>缺点</p>\n<p>能处理<strong>未登录词</strong>（<code>OOV</code>）</p>\n<p>向量维度更大</p>\n<p>适合形态丰富的语言（如德语、俄语）</p>\n<p>计算稍慢</p>\n<p>中文效果提升有限</p>\n<hr />\n<h3 id=\"静态-vs-动态词向量\"><a class=\"anchor\" href=\"#静态-vs-动态词向量\">#</a> 静态 vs 动态词向量</h3>\n<h5 id=\"1️⃣-静态词向量2013-2017\"><a class=\"anchor\" href=\"#1️⃣-静态词向量2013-2017\">#</a> 1️⃣ 静态词向量（2013-2017）</h5>\n<p>方法</p>\n<p>特点</p>\n<p><code>Word2Vec</code></p>\n<p>一个词只有一个向量</p>\n<p><code>GloVe</code></p>\n<p>不管上下文</p>\n<p><code>FastText</code></p>\n<p>考虑子词，但仍静态</p>\n<p>&quot;苹果&quot;在以下句子中意思不同，但向量相同 ❌</p>\n<ol>\n<li>&quot;我吃了一个苹果&quot;  →  水果</li>\n<li>&quot;我买了苹果股票&quot;  →  公司</li>\n<li>&quot;苹果发布了新手机&quot; →  公司</li>\n</ol>\n<p>Word2Vec: &quot;苹果&quot; → [0.5, -0.3, 0.8, ...]  (固定不变)</p>\n<hr />\n<h5 id=\"2️⃣-动态词向量2018-至今主流\"><a class=\"anchor\" href=\"#2️⃣-动态词向量2018-至今主流\">#</a> 2️⃣ 动态词向量（2018-至今）主流⭐</h5>\n<p>方法</p>\n<p>特点</p>\n<p><strong>ELMo</strong> (2018)</p>\n<p>用双向 <code>LSTM</code>，上下文相关</p>\n<p><strong>BERT</strong> (2018)</p>\n<p>用 <code>Transformer</code>，上下文相关 ⭐</p>\n<p><strong>GPT</strong> 系列</p>\n<p>自回归，上下文相关</p>\n<p>BERT:<br />\n&quot;我吃了一个苹果&quot;  →  &quot;苹果&quot; → [0.6, -0.2, 0.9, ...]  (水果义)<br />\n&quot;我买了苹果股票&quot;  →  &quot;苹果&quot; → [0.8, -0.5, 0.4, ...]  (公司义)</p>\n<p>同一个词，不同上下文，向量不同 ✅</p>\n<hr />\n",
            "tags": [
                "uncategorized"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/24/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86-nlp/",
            "url": "https://www.bondrewd.com/2026/03/24/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86-nlp/",
            "title": "自然语言处理 NLP",
            "date_published": "2026-03-24T03:10:03.000Z",
            "content_html": "<p>自然语言处理（<code>Natural Language Processing</code>，<code>NLP</code>） = 让计算机<strong>理解、生成、处理</strong>人类语言的技术。NLP 的核心任务著有可以分为 3 类：理解类、生成类、其他类。</p>\n<hr />\n<h3 id=\"nlp-技术演进史\"><a class=\"anchor\" href=\"#nlp-技术演进史\">#</a> NLP 技术演进史</h3>\n<h5 id=\"1️⃣-规则系统时代1950s-1990s\"><a class=\"anchor\" href=\"#1️⃣-规则系统时代1950s-1990s\">#</a> 1️⃣ 规则系统时代（1950s-1990s）</h5>\n<ul>\n<li><strong>方法</strong>：人工编写语法规则、词典</li>\n<li><strong>例子</strong>：ELIZA 聊天机器人（1966）</li>\n<li><strong>缺点</strong>：规则太多，维护困难，无法处理例外</li>\n</ul>\n<h5 id=\"2️⃣-统计机器学习时代1990s-2010s\"><a class=\"anchor\" href=\"#2️⃣-统计机器学习时代1990s-2010s\">#</a> 2️⃣ 统计机器学习时代（1990s-2010s）</h5>\n<ul>\n<li><strong>方法</strong>：<code>HMM</code>、<code>CRF</code>、<code>SVM</code> 等<strong>统计模型</strong></li>\n<li><strong>优点</strong>：可以从数据中学习，不需要手工写规则</li>\n<li><strong>缺点</strong>：特征工程复杂，需要大量人工设计特征</li>\n</ul>\n<h5 id=\"3️⃣-深度学习时代2013-2017\"><a class=\"anchor\" href=\"#3️⃣-深度学习时代2013-2017\">#</a> 3️⃣ 深度学习时代（2013-2017）⭐</h5>\n<ul>\n<li><strong>突破</strong>：<strong>Word2Vec</strong>（2013）让词有了向量表示</li>\n<li><strong>模型</strong>：<code>RNN</code>、<code>LSTM</code>、<code>GRU</code>、<code>CNN</code></li>\n<li><strong>优点</strong>：自动学习特征，效果大幅提升</li>\n</ul>\n<h5 id=\"4️⃣-transformer-时代2017-至今\"><a class=\"anchor\" href=\"#4️⃣-transformer-时代2017-至今\">#</a> 4️⃣ Transformer 时代（2017-至今）🚀</h5>\n<ul>\n<li><strong>突破</strong>：<strong>Attention Is All You Need</strong>（2017）</li>\n<li><strong>模型</strong>：<code>BERT</code>（2018）、<code>GPT</code> 系列、<code>T5</code></li>\n<li><strong>优点</strong>：并行计算、长距离依赖、效果碾压 RNN</li>\n</ul>\n<h5 id=\"5️⃣-大语言模型时代2020-至今\"><a class=\"anchor\" href=\"#5️⃣-大语言模型时代2020-至今\">#</a> 5️⃣ 大语言模型时代（2020-至今）🦄</h5>\n<ul>\n<li><strong>特点</strong>：超大规模参数（千亿级）、预训练 + 微调</li>\n<li><strong>代表</strong>：GPT-3/4、Claude、LLaMA、ChatGLM</li>\n<li><strong>能力</strong>：零样本学习、多任务通用、接近人类水平</li>\n</ul>\n<hr />\n<h3 id=\"nlp-的核心概念\"><a class=\"anchor\" href=\"#nlp-的核心概念\">#</a> NLP 的核心概念</h3>\n<h5 id=\"1️⃣-分词tokenization\"><a class=\"anchor\" href=\"#1️⃣-分词tokenization\">#</a> 1️⃣ 分词（Tokenization）</h5>\n<p>把句子切成模型能处理的&quot;单元&quot;</p>\n<p>英文：&quot;I love NLP&quot; → [&quot;I&quot;, &quot;love&quot;, &quot;NLP&quot;]<br />\n中文：&quot;我爱 NLP&quot; → [&quot;我&quot;, &quot;爱&quot;, &quot;NLP&quot;]</p>\n<p>更细粒度（Subword）：<br />\n&quot;playing&quot; → [&quot;play&quot;, &quot;##ing&quot;]<br />\n&quot;不喜欢&quot; → [&quot;不&quot;, &quot;喜欢&quot;]</p>\n<p><strong>空格分词</strong></p>\n<p>英文适用，中文不行</p>\n<p><strong>字符级</strong></p>\n<p>每个字独立，序列太长</p>\n<p><strong>词级</strong></p>\n<p>需要词典，未登录词问题</p>\n<p><strong>Subword</strong></p>\n<p>BPE、WordPiece，平衡粒度和词汇量 ⭐</p>\n<hr />\n<h5 id=\"2️⃣-词嵌入word-embedding\"><a class=\"anchor\" href=\"#2️⃣-词嵌入word-embedding\">#</a> 2️⃣ 词嵌入（Word Embedding）</h5>\n<p>把单词变成稠密向量，保留语义信息</p>\n<p>&quot;国王&quot; → [0.85, -0.32, 0.67, ...]<br />\n&quot;王后&quot; → [0.83, -0.30, 0.69, ...]<br />\n&quot;男人&quot; → [0.75, -0.45, 0.55, ...]<br />\n&quot;女人&quot; → [0.73, -0.43, 0.57, ...]</p>\n<p>向量运算：&quot;国王&quot; - &quot;男人&quot; + &quot;女人&quot; ≈ &quot;王后&quot; ✅</p>\n<p><strong>One-Hot</strong></p>\n<p>早期</p>\n<p>稀疏，无语义关系</p>\n<p><strong>Word2Vec</strong></p>\n<p>2013</p>\n<p>稠密，有语义，静态 ⭐</p>\n<p><strong>GloVe</strong></p>\n<p>2014</p>\n<p>基于全局共现统计</p>\n<p><strong>FastText</strong></p>\n<p>2016</p>\n<p>支持子词，处理未登录词</p>\n<p><strong>BERT Embedding</strong></p>\n<p>2018</p>\n<p>上下文相关，动态 ⭐⭐⭐</p>\n<hr />\n<h5 id=\"3️⃣-注意力机制attention-核心突破\"><a class=\"anchor\" href=\"#3️⃣-注意力机制attention-核心突破\">#</a> 3️⃣ 注意力机制（Attention）⭐ 核心突破</h5>\n<p>让模型学会&quot;关注重要的部分&quot;</p>\n<p>翻译：&quot;The animal didn't cross the street because it was too tired&quot;<br />\n↓<br />\n&quot;it&quot; 指的是谁？<br />\n↓<br />\n注意力指向 &quot;animal&quot; ✅</p>\n<p><strong>自注意力（Self-Attention）</strong>：</p>\n<ul>\n<li>每个词都可以&quot;看&quot;句子中的其他词</li>\n<li>计算权重，决定关注谁</li>\n<li><strong>并行计算</strong>，比 <code>RNN</code> 快得多</li>\n</ul>\n<hr />\n<h5 id=\"4️⃣-预训练-微调pretrain-finetune\"><a class=\"anchor\" href=\"#4️⃣-预训练-微调pretrain-finetune\">#</a> 4️⃣ 预训练 + 微调（Pretrain + Finetune）</h5>\n<p>现代 NLP 的标准范式：</p>\n<p>第 1 步：预训练<br />\n用海量无标签文本（维基百科、网页）训练通用语言模型<br />\n学习：语法、语义、常识、推理...<br />\n↓<br />\n第 2 步：微调<br />\n用少量有标签数据（如情感分析数据）训练特定任务<br />\n学习：分类边界、任务特定模式</p>\n<blockquote>\n<p>💡 <strong>类比</strong>：预训练 = 读万卷书（通识教育），微调 = 专业训练（职业技能）</p>\n</blockquote>\n<hr />\n",
            "tags": [
                "uncategorized"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/13/networkpolicy/",
            "url": "https://www.bondrewd.com/2026/03/13/networkpolicy/",
            "title": "NetworkPolicy",
            "date_published": "2026-03-13T23:03:39.000Z",
            "content_html": "<p><strong>NetworkPolicy = Kubernetes 里 Pod 级别的网络防火墙</strong>，它基于标签选择器定义<strong>允许</strong>的入站（Ingress）和出站（Egress）流量，默认遵循“白名单”原则：一旦某个 Pod 被任何 NetworkPolicy 选中，未明确允许的流量都会被拒绝。</p>\n<hr />\n<p><strong>实验要求</strong>：</p>\n<ul>\n<li>从提供的 YAML 样本中查看并应用适当的 NetworkPolicy。</li>\n<li>确保选择的 NetworkPolicy <strong>不过于宽松</strong>，同时允许运行在 <code>frontend</code> 和 <code>backend</code> namespaces 中的 <code>frontend</code> 和 <code>backend</code> Deployment 之间的通信。\n<ul>\n<li>首先，分析 <code>frontend</code> 和 <code>backend</code> Deployment，以确定需要应用的 NetworkPolicy 的具体要求。</li>\n<li>接下来，检查位于 <code>~/netpo</code>l 文件夹中的 NetworkPolicy YAML 示例。\n<ul>\n<li><em>注意：请勿删除或修改提供的示例。仅应用其中一个。否则可能会导致分数降低。</em></li>\n</ul>\n</li>\n<li>最后，应用启用 frontend 和 backend Deployment 之间的通信的 NetworkPolicy，但不要过于宽容。\n<ul>\n<li><em>注意：请勿删除或修改现有的默认拒绝所有入站流量或出口流量 NetworkPolicy。否则可能导致零分。</em></li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n<p>参考链接：无<br />\n难度：⭐⭐</p>\n<hr />\n<h3 id=\"0-环境确认\"><a class=\"anchor\" href=\"#0-环境确认\">#</a> 0. 环境确认</h3>\n<p>题干要求：“不过于宽松”，即最小权限原则</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773471099-image.png\" alt=\"\" /></p>\n<p>查看`~/netpol`目录，发现存在 3 个文件，内容如下：</p>\n<h4 id=\"netpol1yaml-过于宽松\"><a class=\"anchor\" href=\"#netpol1yaml-过于宽松\">#</a> netpol1.yaml ❌ 过于宽松</h4>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: networking.k8s.io/v1</span></span>\n<span class=\"line\"><span>kind: NetworkPolicy</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: netpol-1</span></span>\n<span class=\"line\"><span>  namespace: backend</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  podSelector: &#123;&#125;</span></span>\n<span class=\"line\"><span>  policyTypes:</span></span>\n<span class=\"line\"><span>  - Ingress</span></span>\n<span class=\"line\"><span>  ingress:</span></span>\n<span class=\"line\"><span>  - from:</span></span>\n<span class=\"line\"><span>    - namespaceSelector:</span></span>\n<span class=\"line\"><span>        matchLabels:</span></span>\n<span class=\"line\"><span>          kubernetes.io/metadata.name: frontend</span></span></code></pre>\n<p><strong>第 7 行，⚠️ 会选择 backend 命名空间 ALL Pods</strong></p>\n<hr />\n<h4 id=\"netpol2yaml-正确选择\"><a class=\"anchor\" href=\"#netpol2yaml-正确选择\">#</a> netpol2.yaml ✅ 正确选择</h4>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: networking.k8s.io/v1</span></span>\n<span class=\"line\"><span>kind: NetworkPolicy</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: netpol-2</span></span>\n<span class=\"line\"><span>  namespace: backend</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  podSelector:</span></span>\n<span class=\"line\"><span>    matchLabels:</span></span>\n<span class=\"line\"><span>      app: backend</span></span>\n<span class=\"line\"><span>  policyTypes:</span></span>\n<span class=\"line\"><span>  - Ingress</span></span>\n<span class=\"line\"><span>  ingress:</span></span>\n<span class=\"line\"><span>  - from:</span></span>\n<span class=\"line\"><span>    - namespaceSelector:</span></span>\n<span class=\"line\"><span>        matchLabels:</span></span>\n<span class=\"line\"><span>          kubernetes.io/metadata.name: frontend</span></span>\n<span class=\"line\"><span>      podSelector:</span></span>\n<span class=\"line\"><span>        matchLabels:</span></span>\n<span class=\"line\"><span>          app: frontend</span></span></code></pre>\n<ul>\n<li><strong>第 7 行，只选择 backend Pods</strong></li>\n<li><strong>第 19 行，同时检查 namespace + Pod 标签</strong></li>\n</ul>\n<hr />\n<h4 id=\"netpol3yaml-配置错误\"><a class=\"anchor\" href=\"#netpol3yaml-配置错误\">#</a> netpol3.yaml ❌ 配置错误</h4>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: networking.k8s.io/v1</span></span>\n<span class=\"line\"><span>kind: NetworkPolicy</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: netpol-3</span></span>\n<span class=\"line\"><span>  namespace: backend</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  podSelector:</span></span>\n<span class=\"line\"><span>    matchLabels:</span></span>\n<span class=\"line\"><span>      app: database</span></span>\n<span class=\"line\"><span>  policyTypes:</span></span>\n<span class=\"line\"><span>  - Ingress</span></span>\n<span class=\"line\"><span>  ingress:</span></span>\n<span class=\"line\"><span>  - from:</span></span>\n<span class=\"line\"><span>    - namespaceSelector:</span></span>\n<span class=\"line\"><span>        matchLabels:</span></span>\n<span class=\"line\"><span>          kubernetes.io/metadata.name: frontend</span></span>\n<span class=\"line\"><span>      podSelector:</span></span>\n<span class=\"line\"><span>        matchLabels:</span></span>\n<span class=\"line\"><span>          app: frontend</span></span>\n<span class=\"line\"><span>    - ipBlock:</span></span>\n<span class=\"line\"><span>        cidr: 10.0.0.0/24</span></span></code></pre>\n<ul>\n<li><strong>第 9 行，错误标签（应该是 backend）</strong></li>\n<li><strong>第 21 行，⚠️ 额外开放整个网段，过于宽松</strong></li>\n</ul>\n<hr />\n<h3 id=\"1-选择-netpol2yaml-并应用\"><a class=\"anchor\" href=\"#1-选择-netpol2yaml-并应用\">#</a> <strong>1. 选择 netpol2.yaml 并应用</strong></h3>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773471298-image.png\" alt=\"\" /></p>\n<h3 id=\"2-检查\"><a class=\"anchor\" href=\"#2-检查\">#</a> 2. 检查</h3>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773471312-image.png\" alt=\"\" /></p>\n<p><em><code>netpol2.yaml</code> 中配置的 <code>NetworkPolicy</code> 位于命名空间 <code>backend</code></em></p>\n<hr />\n",
            "tags": [
                "k8s 实验"
            ]
        },
        {
            "id": "https://www.bondrewd.com/2026/03/13/gateway/",
            "url": "https://www.bondrewd.com/2026/03/13/gateway/",
            "title": "Gateway",
            "date_published": "2026-03-13T22:40:27.000Z",
            "content_html": "<p>K8s <strong>Gateway</strong> 是<strong>官方新一代流量入口标准（Gateway API）</strong>，用来<strong>替代 / 升级传统 Ingress</strong>，专门管理<strong>外部流量进入 Kubernetes 集群</strong>的南北向流量。</p>\n<p><strong>Gateway = 更强大、更规范、云原生的下一代 Ingress</strong></p>\n<hr />\n<p><strong>实验要求</strong>：</p>\n<ul>\n<li>将现有 Web 应用程序从 Ingress 迁移到 Gateway API。您必须维护 HTTPS 访问权限。\n<ul>\n<li><em>注意：集群中安装了一个名为 nginx 的 GatewayClass 。</em></li>\n</ul>\n</li>\n<li>首先，创建一个名为 <code>web-gateway</code> 的 Gateway ，主机名为 <code>gateway.web.k8s.local</code> ，并保持现有名为 <code>web</code> 的 Ingress 资源的现有 TLS 和侦听器配置。</li>\n<li>接下来，创建一个名为 <code>web-route</code> 的 <code>HTTPRoute</code> ，主机名为 <code>gateway.web.k8s.local</code> ，并保持现有名为 <code>web</code> 的 Ingress 资源的现有路由规则。\n<ul>\n<li><em>您可以使用以下命令测试 Gateway API 配置：</em>\n<ul>\n<li>[candidate@cka000057]$ curl -Lk <a href=\"https://gateway.web.k8s.local:31443\">https://gateway.web.k8s.local:31443</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>最后，删除名为 <code>web</code> 的现有 Ingress 资源。</li>\n</ul>\n<p>参考链接： <a href=\"https://kubernetes.io/docs/concepts/services-networking/gateway/\">https://kubernetes.io/docs/concepts/services-networking/gateway/</a><br />\n难度：⭐⭐⭐</p>\n<hr />\n<h3 id=\"0-环境确认\"><a class=\"anchor\" href=\"#0-环境确认\">#</a> 0. 环境确认</h3>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773469142-image.png\" alt=\"\" /></p>\n<p><code>kubectl get ingress web -o yaml</code>：</p>\n<ul>\n<li>创建 <code>Gateway</code> 时需要： 查看 <code>spec.tls.secretName</code></li>\n<li>创建 <code>HTTPRoute</code> 时需要：查看 <code>spec.rules.http.paths.backend</code> 和 <code>spec.rules.http.paths.path</code></li>\n</ul>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773469169-image.png\" alt=\"\" /></p>\n<h3 id=\"1-编写-gatewayyaml-并应用\"><a class=\"anchor\" href=\"#1-编写-gatewayyaml-并应用\">#</a> 1. 编写 gateway.yaml 并应用</h3>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773469195-image.png\" alt=\"\" /></p>\n<h3 id=\"2-编写-httprouteyaml-并应用\"><a class=\"anchor\" href=\"#2-编写-httprouteyaml-并应用\">#</a> 2. 编写 httproute.yaml 并应用</h3>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773469239-image.png\" alt=\"\" /></p>\n<h3 id=\"3-验证并删除-ingress-web\"><a class=\"anchor\" href=\"#3-验证并删除-ingress-web\">#</a> 3. 验证并删除 ingress web</h3>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773469270-image.png\" alt=\"\" /></p>\n<hr />\n<h3 id=\"补充内容\"><a class=\"anchor\" href=\"#补充内容\">#</a> 补充内容</h3>\n<h4 id=\"ingressgateway-和-httproute\"><a class=\"anchor\" href=\"#ingressgateway-和-httproute\">#</a> Ingress，Gateway 和 HTTPRoute</h4>\n<p>Gateway 和 HTTPRoute 是 Kubernetes <code>Gateway API</code> 的两个核心资源，共同替代传统的 Ingress 实现更灵活的流量管理。</p>\n<ul>\n<li><strong>Gateway 负责基础设施层</strong>，定义<code>监听器</code>（端口、协议、主机名）和 <code>TLS 配置</code>，由平台团队管理；</li>\n<li><strong>HTTPRoute 负责应用层</strong>，定义<code>路由规则</code>（路径匹配、权重分发、后端服务），由应用团队管理。两者通过 <code>parentRefs</code> 关联，实现职责分离。</li>\n</ul>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773469306-image.png\" alt=\"\" /></p>\n<p>Gateway 和 HTTPRoute</p>\n<p><img loading=\"lazy\" src=\"/wp-uploads/2026/03/1773469873-image.png\" alt=\"\" /></p>\n<p>Ingress</p>\n<p>关系</p>\n<p>说明</p>\n<p><code>Ingress</code></p>\n<p>第一代流量入口资源（已成熟，但功能有限）</p>\n<p><code>Gateway</code></p>\n<p>第二代流量入口的<strong>基础设施层</strong>（监听器+TLS）</p>\n<p><code>HTTPRoute</code></p>\n<p>第二代流量入口的<strong>应用层</strong>（路由规则）</p>\n<p><code>Gateway + HTTPRoute</code></p>\n<p><strong>替代 Ingress</strong> 的新标准</p>\n<h4 id=\"配置对比\"><a class=\"anchor\" href=\"#配置对比\">#</a> 配置对比</h4>\n<p><strong>Ingress 配置（全部在一起）</strong>:</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>apiVersion: networking.k8s.io/v1</span></span>\n<span class=\"line\"><span>kind: Ingress</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: web-ingress</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  # ⚠️ TLS 配置在这里</span></span>\n<span class=\"line\"><span>  tls:</span></span>\n<span class=\"line\"><span>  - hosts:</span></span>\n<span class=\"line\"><span>    - example.com</span></span>\n<span class=\"line\"><span>    secretName: tls-secret</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>  # ⚠️ 路由规则也在这里</span></span>\n<span class=\"line\"><span>  rules:</span></span>\n<span class=\"line\"><span>  - host: example.com</span></span>\n<span class=\"line\"><span>    http:</span></span>\n<span class=\"line\"><span>      paths:</span></span>\n<span class=\"line\"><span>      - path: /</span></span>\n<span class=\"line\"><span>        pathType: Prefix</span></span>\n<span class=\"line\"><span>        backend:</span></span>\n<span class=\"line\"><span>          service:</span></span>\n<span class=\"line\"><span>            name: web-service</span></span>\n<span class=\"line\"><span>            port:</span></span>\n<span class=\"line\"><span>              number: 80</span></span></code></pre>\n<p><strong>Gateway + HTTPRoute 配置（职责分离）</strong>:</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span></span></span>\n<span class=\"line\"><span>## ===== Gateway（基础设施团队管理）=====</span></span>\n<span class=\"line\"><span>apiVersion: gateway.networking.k8s.io/v1</span></span>\n<span class=\"line\"><span>kind: Gateway</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: web-gateway</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  gatewayClassName: nginx</span></span>\n<span class=\"line\"><span>  listeners:</span></span>\n<span class=\"line\"><span>  - name: https</span></span>\n<span class=\"line\"><span>    protocol: HTTPS</span></span>\n<span class=\"line\"><span>    port: 443</span></span>\n<span class=\"line\"><span>    hostname: example.com</span></span>\n<span class=\"line\"><span>    # ⚠️ TLS 配置在这里</span></span>\n<span class=\"line\"><span>    tls:</span></span>\n<span class=\"line\"><span>      mode: Terminate</span></span>\n<span class=\"line\"><span>      certificateRefs:</span></span>\n<span class=\"line\"><span>      - kind: Secret</span></span>\n<span class=\"line\"><span>        name: tls-secret</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>---</span></span>\n<span class=\"line\"><span>## ===== HTTPRoute（应用团队管理）=====</span></span>\n<span class=\"line\"><span>apiVersion: gateway.networking.k8s.io/v1</span></span>\n<span class=\"line\"><span>kind: HTTPRoute</span></span>\n<span class=\"line\"><span>metadata:</span></span>\n<span class=\"line\"><span>  name: web-route</span></span>\n<span class=\"line\"><span>spec:</span></span>\n<span class=\"line\"><span>  # ⚠️ 引用 Gateway</span></span>\n<span class=\"line\"><span>  parentRefs:</span></span>\n<span class=\"line\"><span>  - name: web-gateway</span></span>\n<span class=\"line\"><span>    kind: Gateway</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>  hostnames:</span></span>\n<span class=\"line\"><span>  - example.com</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>  # ⚠️ 路由规则在这里</span></span>\n<span class=\"line\"><span>  rules:</span></span>\n<span class=\"line\"><span>  - matches:</span></span>\n<span class=\"line\"><span>    - path:</span></span>\n<span class=\"line\"><span>        type: PathPrefix</span></span>\n<span class=\"line\"><span>        value: /</span></span>\n<span class=\"line\"><span>    backendRefs:</span></span>\n<span class=\"line\"><span>    - name: web-service</span></span>\n<span class=\"line\"><span>      kind: Service</span></span>\n<span class=\"line\"><span>      port:</span></span>\n<span class=\"line\"><span>        number: 80</span></span></code></pre>\n<h4 id=\"功能对比\"><a class=\"anchor\" href=\"#功能对比\">#</a> 功能对比</h4>\n<p>功能</p>\n<p>Ingress</p>\n<p>Gateway + HTTPRoute</p>\n<p><strong>HTTP 路由</strong></p>\n<p>✅ 支持</p>\n<p>✅ 支持</p>\n<p><strong>HTTPS/TLS</strong></p>\n<p>✅ 支持</p>\n<p>✅ 支持（更灵活）</p>\n<p><strong>路径匹配</strong></p>\n<p>✅ 前缀/精确</p>\n<p>✅ 前缀/精确/正则</p>\n<p><strong>主机名匹配</strong></p>\n<p>✅ 支持</p>\n<p>✅ 支持</p>\n<p><strong>多监听器</strong></p>\n<p>❌ 有限</p>\n<p>✅ 原生支持</p>\n<p><strong>TCP/UDP</strong></p>\n<p>❌ 不支持</p>\n<p>✅ 支持 (TCPRoute/UDPRoute)</p>\n<p><strong>gRPC</strong></p>\n<p>❌ 不支持</p>\n<p>✅ 支持 (GRPCRoute)</p>\n<p><strong>流量拆分</strong></p>\n<p>❌ 困难</p>\n<p>✅ 原生支持</p>\n<p><strong>跨命名空间</strong></p>\n<p>❌ 困难</p>\n<p>✅ 原生支持</p>\n<p><strong>角色分离</strong></p>\n<p>❌ 混合</p>\n<p>✅ 清晰分离</p>\n<p><strong>API 成熟度</strong></p>\n<p>✅ GA</p>\n<p>✅ GA (v1)</p>\n<hr />\n",
            "tags": [
                "k8s 实验"
            ]
        }
    ]
}