<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>etherwindy</title><description>Blog</description><link>https://etherwindy.github.io/</link><language>en</language><item><title>大语言模型 On-Policy Distillation</title><link>https://etherwindy.github.io/AstroBlog/posts/kl-llm/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/kl-llm/</guid><description>介绍了大语言模型 On-Policy Distillation 的发展脉络以及 KL 散度的应用</description><pubDate>Wed, 13 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;知识蒸馏（KD）中的KL&lt;/h2&gt;
&lt;p&gt;Geoffrey Hinton 在其 2015 年的论文&lt;a href=&quot;https://arxiv.org/pdf/1503.02531&quot;&gt;Distilling the Knowledge in a Neural Network&lt;/a&gt;中使用了KL散度进行判别模型度知识蒸馏，形成了Supervised KL Distillation的经典范式。其训练目标函数可以写作&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\mathcal{L}&amp;amp;=\alpha\mathcal{L}&lt;em&gt;\text{hard}+\beta\mathcal{L}&lt;/em&gt;\text{soft}\
&amp;amp;=-\left(1-\lambda\right)\mathbb{E}&lt;em&gt;{\left(\boldsymbol{x},\boldsymbol{y}\right)\sim\left(X,Y \right)}\boldsymbol{y}\log q&lt;/em&gt;\theta^{\left(1\right)}\left(\boldsymbol{y}\mid\boldsymbol{x}\right)+\lambda T^2\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X}D&lt;/em&gt;{KL}\left(p^{\left(T\right)}\left(\cdot\mid\boldsymbol{x}\right)\Vert q_\theta^{\left(T\right)}\left(\cdot\mid\boldsymbol{x}\right)\right) \
&amp;amp;=-\left(1-\lambda\right)\mathbb{E}&lt;em&gt;{\left(\boldsymbol{x},\boldsymbol{y}\right)\sim\left(X,Y\right)}\boldsymbol{y}\log q&lt;/em&gt;\theta^{\left(1\right)}\left(\boldsymbol{y}\mid\boldsymbol{x}\right)+\lambda T^2\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}^&lt;em&gt;\sim p^{\left (T\right)}\left(\cdot\mid\boldsymbol{x}\right)}\log\frac{p^{\left(T\right)}\left(\boldsymbol{y}^&lt;/em&gt;\mid\boldsymbol{x}\right)}{q&lt;/em&gt;\theta^{\left(T\right)}\left(\boldsymbol{y}^*\mid\boldsymbol{x}\right)}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;其中 $p^{\left(T\right)}$ 和 $q_\theta^{\left(T\right)}$ 分别代表教师模型和学生模型在温度 $T$ 下的概率。公式中对于简单的判别任务，直接在所有类别上计算KL散度，可以得到精确的数值。&lt;/p&gt;
&lt;h2&gt;GKD: On-Pollicy LLM 蒸馏&lt;/h2&gt;
&lt;p&gt;论文 &lt;a href=&quot;https://arxiv.org/pdf/2306.13649&quot;&gt;On-Policy Distillation of Language Models: Learning from Self-Generated Mistakes&lt;/a&gt;将上述方法直接迁移到LLM，将传统的监督信号和LLM强化学习的On-policy生成相结合，提出了GKD（Gererative Knowledge Distillation）的训练范式。其训练目标函数可以写作&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\mathcal{L}&amp;amp;=\left(1-\lambda\right)\mathbb{E}&lt;em&gt;{\left(\boldsymbol{x},\boldsymbol{y}\right)\sim\left(X,Y\right)}\frac{1}{\lvert \boldsymbol{y}\rvert}\sum&lt;/em&gt;{n=1}^{\lvert \boldsymbol{y}\rvert}D_\text{KL}\left(p\left(\cdot\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\Vert q&lt;/em&gt;\theta\left(\cdot\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\right)\
&amp;amp;+\lambda\mathbb{E}&lt;/em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}^&lt;em&gt;\sim q_\theta\left(\cdot\mid\boldsymbol{x}\right)}\frac{1}{\lvert \boldsymbol{y}^&lt;/em&gt;\rvert}\sum_{n=1}^{\lvert \boldsymbol{y}^&lt;em&gt;\rvert}D_\text{KL}\left(p\left(\cdot\mid \boldsymbol{y}^&lt;/em&gt;&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\Vert q&lt;/em&gt;\theta\left(\cdot\mid \boldsymbol{y}^&lt;em&gt;&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\right)\
&amp;amp;=\left(1-\lambda\right)\mathbb{E}&lt;/em&gt;{\left(\boldsymbol{x},\boldsymbol{y}\right)\sim\left(X,Y\right)}\frac{1}{\lvert \boldsymbol{y}\rvert}\sum_{n=1}^{\lvert \boldsymbol{y}\rvert}\mathbb{E}&lt;em&gt;{v\sim p\left(\cdot\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\log\frac{p\left(v\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{q&lt;/em&gt;\theta\left(v\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\
&amp;amp;+\lambda\mathbb{E}&lt;/em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}^&lt;/em&gt;\sim q_\theta\left(\cdot\mid\boldsymbol{x}\right)}\frac{1}{\lvert \boldsymbol{y}^&lt;em&gt;\rvert}\sum_{n=1}^{\lvert \boldsymbol{y}^&lt;/em&gt;\rvert}\mathbb{E}&lt;em&gt;{v\sim p\left(\cdot\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\log\frac{p\left(v\mid \boldsymbol{y}^&lt;em&gt;&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{q&lt;/em&gt;\theta\left(v\mid \boldsymbol{y}^&lt;/em&gt;_{&amp;lt;n},\boldsymbol{x}\right)}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;当 $\lambda=1$ 时即为 On-Policy GKD：&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\mathcal{L}&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}\sim p\left(\cdot\mid\boldsymbol{x}\right)}\frac{1}{\lvert \boldsymbol{y}\rvert}\sum&lt;/em&gt;{n=1}^{\lvert \boldsymbol{y}\rvert}D_\text{KL}\left(p\left(\cdot\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\Vert q&lt;/em&gt;\theta\left(\cdot\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\right)\
&amp;amp;=\mathbb{E}&lt;/em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}\sim p\left(\cdot\mid\boldsymbol{x}\right)}\frac{1}{\lvert \boldsymbol{y}\rvert}\sum_{n=1}^{\lvert \boldsymbol{y}\rvert}\mathbb{E}&lt;em&gt;{v\sim p\left(\cdot\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\log\frac{p\left(v\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{q&lt;/em&gt;\theta\left(v\mid \boldsymbol{y}_{&amp;lt;n},\boldsymbol{x}\right)}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;实际应用中，为了加快计算，常使用 Top-K 概率截断来加速计算，同时使用 K3 估计来近似计算：&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\mathcal{L}&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}\sim p\left(\cdot\mid\boldsymbol{x}\right)}\frac{1}{\lvert \boldsymbol{y}\rvert}\sum&lt;/em&gt;{n=1}^{\lvert \boldsymbol{y}\rvert}D_\text{KL}\left(p\left(\cdot\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\Vert q&lt;/em&gt;\theta\left(\cdot\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\right)\
&amp;amp;\approx\mathbb{E}&lt;/em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}\sim p\left(\cdot\mid\boldsymbol{x}\right)}\frac{1}{\lvert \boldsymbol{y}\rvert}\sum_{n=1}^{\lvert \boldsymbol{y}\rvert}\sum_{v\in\mathcal{V}^\text{top-k}}\left(q_\theta\left(v\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)-p\left(v\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)+p\left(v\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\log\frac{p\left(v\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{q_\theta\left(v\mid \boldsymbol{y}_{&amp;lt;n},\boldsymbol{x}\right)}\right)
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;K3 估计很好地保证了在 Top-k 截断后 KL 估算的非负性。&lt;/p&gt;
&lt;h2&gt;MiniLLM: 反向 KL + 蒙特卡洛采样实现高效蒸馏&lt;/h2&gt;
&lt;p&gt;论文 &lt;a href=&quot;https://proceedings.iclr.cc/paper_files/paper/2024/file/8ac015d409635f196f9e3e9dcfb9a94e-Paper-Conference.pdf&quot;&gt;MiniLLM: Knowledge Distillation of Large Language Models&lt;/a&gt;提出了使用Reverse KL进行模型蒸馏。其训练目标为最小化序列级别分布的Reverse KL，可以写作&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\mathcal{L}=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}\sim q&lt;/em&gt;\theta\left(\cdot\mid\boldsymbol{x}\right)}\log\frac{q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}{p\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;其中$q_\theta$为学生模型，$p$为教师模型。&lt;/p&gt;
&lt;p&gt;考虑其梯度：&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\nabla\mathcal{L}&amp;amp;=\nabla\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}\sim q&lt;/em&gt;\theta\left(\cdot\mid\boldsymbol{x}\right)}\log\frac{q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}{p\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}\
&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X}\sum&lt;/em&gt;{\boldsymbol{y}}\nabla\left(q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)\log\frac{q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}{p\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}\right)\
&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X}\sum&lt;/em&gt;{\boldsymbol{y}}\log\frac{q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}{p\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}\nabla q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)
+\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X}\sum&lt;/em&gt;{\boldsymbol{y}}q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)\nabla\log q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)\
&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X}\sum&lt;/em&gt;{\boldsymbol{y}}q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)\log\frac{q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}{p\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}\nabla\log q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)
+\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X}\sum&lt;/em&gt;{\boldsymbol{y}}q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)\nabla\log q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)\
&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}\sim q&lt;/em&gt;\theta\left(\cdot\mid\boldsymbol{x}\right)}\left(\log\frac{q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}{p\left(\boldsymbol{y}\mid\boldsymbol{x}\right)}+1\right)\nabla\log q_\theta\left(\boldsymbol{y}\mid\boldsymbol{x}\right)\
&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}\sim q&lt;/em&gt;\theta\left(\cdot\mid\boldsymbol{x}\right)}\sum_{n=1}^{\lvert \boldsymbol{y}\rvert}\left(\sum_{m=1}^{\lvert \boldsymbol{y}\rvert}\log\frac{q_\theta\left(y_m\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;m},\boldsymbol{x}\right)}{p\left(y_m\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;m},\boldsymbol{x}\right)}+1\right)\nabla\log q_\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\
&amp;amp;=\mathbb{E}&lt;/em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}\sim q_\theta\left(\cdot\mid\boldsymbol{x}\right)}\sum_{n=1}^{\lvert \boldsymbol{y}\rvert}\left(\sum_{m=n}^{\lvert \boldsymbol{y}\rvert}\log\frac{q_\theta\left(y_m\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;m},\boldsymbol{x}\right)}{p\left(y_m\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;m},\boldsymbol{x}\right)}+1\right)\nabla\log q_\theta\left(y_n\mid \boldsymbol{y}_{&amp;lt;n},\boldsymbol{x}\right)\
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;可以发现梯度可以写成策略梯度的形式。因此可以使用强化学习的方法，通过蒙特卡洛采样的方式估计梯度。&lt;/p&gt;
&lt;p&gt;Reverse KL的主要优势如下图所示，学生模型不会尝试拟合教师的完整分布，而是去拟合和学生模型分布相近的一个子分布。该特点使得蒸馏后学生模型不容易损失原有的能力。
&lt;img src=&quot;./reverse_KL.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;蒙特卡洛采样会引入极大的方差，在整个 sequence 级别计算 KL 还会使方差累积。因此，当前更常见的方法是仅优化单步的 KL 损失，即&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\mathcal{L}=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}\sim q&lt;/em&gt;\theta(\cdot\mid\boldsymbol{x})}\sum_{n=1}^{\lvert \boldsymbol{y}\rvert}\mathbb{E}&lt;em&gt;{y^*&lt;em&gt;n\sim q&lt;/em&gt;\theta\left(\cdot\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\log\frac{q_\theta\left(y^&lt;em&gt;&lt;em&gt;n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(y^&lt;/em&gt;&lt;em&gt;n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;其梯度为&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\nabla\mathcal{L}&amp;amp;=\sum_{n\geqslant 1}\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}\sim q&lt;/em&gt;\theta(\cdot\mid\boldsymbol{x})}\mathbb{E}&lt;em&gt;{y^*&lt;em&gt;n\sim q&lt;/em&gt;\theta\left(\cdot\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\mathbf 1\left{\lvert \boldsymbol{y}\rvert\geqslant n\right}\left(\log\frac{q_\theta\left(y^&lt;em&gt;&lt;em&gt;n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(y^&lt;/em&gt;&lt;em&gt;n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}-1\right)\nabla\log q_\theta\left(y^&lt;em&gt;&lt;em&gt;n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)\
&amp;amp;=\sum_{n\geqslant 1}\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}&lt;/em&gt;{&amp;lt;n}\sim q_\theta(\cdot\mid\boldsymbol{x})}\mathbb{E}_{y^&lt;/em&gt;&lt;em&gt;n\sim q&lt;/em&gt;\theta\left(\cdot\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\mathbf 1\left{\lvert \boldsymbol{y}&lt;/em&gt;{&amp;lt;n}\rvert=n-1\right}\left(\log\frac{q_\theta\left(y^&lt;em&gt;&lt;em&gt;n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(y^&lt;/em&gt;&lt;em&gt;n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}-1\right)\nabla\log q_\theta\left(y^*&lt;em&gt;n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)\
&amp;amp;=\sum_{n\geqslant 1}\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}&lt;/em&gt;{\leqslant n}\sim q_\theta(\cdot\mid\boldsymbol{x})}\mathbf 1\left{\lvert \boldsymbol{y}&lt;em&gt;{\leqslant n}\rvert=n\right}\left(\log\frac{q&lt;/em&gt;\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(y_n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}-1\right)\nabla\log q_\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\
&amp;amp;=\sum&lt;/em&gt;{n\geqslant 1}\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}\sim q&lt;/em&gt;\theta(\cdot\mid\boldsymbol{x})}\mathbf 1\left{\lvert \boldsymbol{y}\rvert\geqslant n\right}\left(\log\frac{q_\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(y_n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}-1\right)\nabla\log q_\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\
&amp;amp;=\mathbb{E}&lt;/em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}\sim q_\theta(\cdot\mid\boldsymbol{x})}\sum_{n=1}^{\lvert \boldsymbol{y}\rvert}\left(\log\frac{q_\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(y_n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}-1\right)\nabla\log q_\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\
&amp;amp;=\mathbb{E}&lt;/em&gt;{\boldsymbol{x}\sim X,\boldsymbol{y}\sim q_\theta(\cdot\mid\boldsymbol{x})}\sum_{n=1}^{\lvert \boldsymbol{y}\rvert}\log\frac{q_\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(y_n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}\nabla\log q_\theta\left(y_n\mid \boldsymbol{y}_{&amp;lt;n},\boldsymbol{x}\right)\
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;因此可以直接将 $\log\frac{q_\theta\left(y_n\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(y_n\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}$ 作为强化学习的 Advantage 进行训练。&lt;/p&gt;
&lt;h2&gt;GLM-5: On-Policy 多阶段蒸馏&lt;/h2&gt;
&lt;p&gt;GLM-5 的技术报告 &lt;a href=&quot;https://arxiv.org/pdf/2602.15763&quot;&gt;GLM-5: from Vibe Coding to Agentic Engineering&lt;/a&gt; 将 MiniLLM 的 On-Policy Distillation 引入模型自蒸馏中。自蒸馏的模型来自学生模型进行多阶段 RL 训练后得到的检查点。通过这种方式，学生模型通过密集奖励同时学习多个专家模型的分布，实现多任务的泛化能力。&lt;/p&gt;
&lt;h2&gt;DeepSeek-V4: 全词表精确反向 KL 计算&lt;/h2&gt;
&lt;p&gt;DeepSeek-V4的技术报告 &lt;a href=&quot;https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro/blob/main/DeepSeek_V4.pdf&quot;&gt;DeepSeek-V4: Towards Highly Efficient Million-Token Context Intelligence&lt;/a&gt; 中提到了 DeepSeek-V4 在全词表上计算反向 KL 以实现精确的数值计算，即&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
\mathcal{L}&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}\sim p\left(\cdot\mid\boldsymbol{x}\right)}\frac{1}{\lvert \boldsymbol{y}\rvert}\sum&lt;/em&gt;{n=1}^{\lvert \boldsymbol{y}\rvert}D_\text{KL}\left(q_\theta\left(\cdot\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\Vert p\left(\cdot\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)\right)\
&amp;amp;=\mathbb{E}&lt;em&gt;{\boldsymbol{x}\sim X, \boldsymbol{y}\sim p\left(\cdot\mid\boldsymbol{x}\right)}\frac{1}{\lvert \boldsymbol{y}\rvert}\sum&lt;/em&gt;{n=1}^{\lvert \boldsymbol{y}\rvert}\sum_{v\in\mathcal{V}}q_\theta\left(v\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)\log\frac{q&lt;/em&gt;\theta\left(v\mid \boldsymbol{y}&lt;em&gt;{&amp;lt;n},\boldsymbol{x}\right)}{p\left(v\mid \boldsymbol{y}&lt;/em&gt;{&amp;lt;n},\boldsymbol{x}\right)}
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;这在超大参数量、多教师模型以及超长上下文的推理场景下造成了极大的性能挑战。为了解决这个问题，DeepSeek-V4 在 OPD 训练阶段将教师权重全部卸载至集中式分布式存储中，并在教师前向传播期间按需加载，采用类似 ZeRO 的参数分片技术以缓解 I/O 与 DRAM 压力。此外，在教师模型前向传播中缓存最后一层 hidden state 并在训练中重新复原 logits，避免显式实例化 logits 带来极大的内存负担。&lt;/p&gt;
</content:encoded></item><item><title>SAPO：用&quot;软门控&quot;重塑大模型强化学习的稳定性</title><link>https://etherwindy.github.io/AstroBlog/posts/sapo/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/sapo/</guid><description>深度解析阿里巴巴Qwen团队的SAPO算法，探索软门控机制如何革新LLM强化学习训练</description><pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;论文链接：&lt;a href=&quot;https://arxiv.org/abs/2511.20347&quot;&gt;Soft Adaptive Policy Optimization&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;::github{repo=&quot;QwenLM/Qwen3-VL&quot;}&lt;/p&gt;
&lt;p&gt;在大语言模型（LLM）的强化学习（RL）训练中，&lt;strong&gt;稳定性&lt;/strong&gt;与&lt;strong&gt;学习效率&lt;/strong&gt;之间永恒的拉锯战一直是研究者面临的重大挑战。阿里巴巴Qwen团队最新提出的 &lt;strong&gt;Soft Adaptive Policy Optimization (SAPO)&lt;/strong&gt; 算法，通过引入&lt;strong&gt;软门控机制&lt;/strong&gt;和&lt;strong&gt;非对称温度设计&lt;/strong&gt;，为这一难题提供了优雅的解决方案。&lt;/p&gt;
&lt;p&gt;本文将深入剖析SAPO的技术原理，揭示其如何通过数学上的巧妙设计，在保持稳定性的同时最大化学习效率。&lt;/p&gt;
&lt;h2&gt;问题背景：RL训练的&quot;硬裁剪困境&quot;&lt;/h2&gt;
&lt;h3&gt;GRPO与GSPO的局限&lt;/h3&gt;
&lt;p&gt;在当前的LLM RL训练中，&lt;strong&gt;GRPO&lt;/strong&gt;（Group Relative Policy Optimization）和&lt;strong&gt;GSPO&lt;/strong&gt;（Group Sequence Policy Optimization）是两种主流方法，但它们都存在一个共同问题——&lt;strong&gt;硬裁剪（Hard Clipping）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GRPO的核心问题&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在token级别进行硬裁剪&lt;/li&gt;
&lt;li&gt;当重要性比率 $r_{i,t}(\theta)$ 超出 $[1-\epsilon, 1+\epsilon]$ 范围时，梯度被完全截断&lt;/li&gt;
&lt;li&gt;这种&quot;全有或全无&quot;的策略导致信息损失&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;GSPO的困境&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在序列级别进行硬裁剪&lt;/li&gt;
&lt;li&gt;当序列中少数token严重偏离时，整个序列的梯度被抑制&lt;/li&gt;
&lt;li&gt;损失了序列中其他正常token的宝贵学习信号&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
在Mixture-of-Experts (MoE) 模型中，由于路由异构性，token级别的重要性比率方差更大，硬裁剪问题被进一步放大。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./gating-comparison.jpg&quot; alt=&quot;GRPO、GSPO与SAPO的门控函数对比&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上图清晰展示了三种方法的核心差异：GRPO和GSPO使用阶梯式的硬裁剪，而SAPO采用平滑的S型门控函数。&lt;/p&gt;
&lt;h2&gt;SAPO的核心创新：软门控机制&lt;/h2&gt;
&lt;h3&gt;数学 formulation&lt;/h3&gt;
&lt;p&gt;SAPO的目标函数优雅而简洁：&lt;/p&gt;
&lt;p&gt;$$J(\theta) = \mathbb{E}\left[ \frac{1}{G} \sum_{i} \frac{1}{|y_i|} \sum_{t} f_{i,t}(r_{i,t}(\theta)) \cdot \hat{A}_{i,t} \right]$$&lt;/p&gt;
&lt;p&gt;其中，&lt;strong&gt;软门控函数&lt;/strong&gt;是SAPO的灵魂：&lt;/p&gt;
&lt;p&gt;$$f_{i,t}(x) = \sigma(\tau_{i,t}(x - 1)) \cdot \frac{4}{\tau_{i,t}}$$&lt;/p&gt;
&lt;p&gt;这里 $\sigma$ 是sigmoid函数，$\tau_{i,t}$ 是温度参数。&lt;/p&gt;
&lt;h3&gt;梯度权重分析&lt;/h3&gt;
&lt;p&gt;求导后得到梯度权重：&lt;/p&gt;
&lt;p&gt;$$w_{i,t}(\theta) = 4p_{i,t}(\theta)(1 - p_{i,t}(\theta))$$&lt;/p&gt;
&lt;p&gt;其中 $p_{i,t}(\theta) = \sigma(\tau_{i,t}(r_{i,t}(\theta) - 1))$&lt;/p&gt;
&lt;p&gt;这个权重函数具有以下关键特性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;在 on-policy 点（r=1）达到峰值&lt;/strong&gt;：权重为1，保留完整梯度&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;平滑衰减&lt;/strong&gt;：随偏离程度平滑下降，而非硬截断&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有界性&lt;/strong&gt;：权重始终在[0,1]范围内&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
这个权重函数实际上是 &lt;strong&gt;sech²&lt;/strong&gt; 函数的形式，在物理学中常用于描述连续过渡现象，保证了优化的平滑性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;非对称温度设计：正负token区别对待&lt;/h2&gt;
&lt;h3&gt;核心洞察&lt;/h3&gt;
&lt;p&gt;SAPO的另一大创新是&lt;strong&gt;为正向和负向优势（advantage）设置不同的温度参数&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;正向token&lt;/strong&gt;（$\hat{A} &amp;gt; 0$）：$\tau_{pos} = 1.0$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;负向token&lt;/strong&gt;（$\hat{A} \leq 0$）：$\tau_{neg} = 1.05$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;为什么需要这种不对称性？&lt;/h3&gt;
&lt;p&gt;通过数学推导可以揭示其中的奥妙。考虑logits梯度的传播：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正向优势的作用&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;增加&lt;strong&gt;采样token&lt;/strong&gt;的logit&lt;/li&gt;
&lt;li&gt;减少&lt;strong&gt;所有未采样token&lt;/strong&gt;的logit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;负向优势的作用&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;减少&lt;strong&gt;采样token&lt;/strong&gt;的logit&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;增加大量未采样token的logit&lt;/strong&gt; ← 问题在这里！&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在大词汇表（数十万token）的场景下，负向梯度会扩散到大量不相关token，虽然提供了正则化，但也引入了严重的不稳定性。因此，我们需要让负向token的梯度&lt;strong&gt;更快衰减&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./temperature-ablation.jpg&quot; alt=&quot;温度消融实验结果&quot; /&gt;&lt;/p&gt;
&lt;p&gt;实验结果充分验证了这一设计：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\tau_{neg} &amp;gt; \tau_{pos}$：最稳定&lt;/li&gt;
&lt;li&gt;$\tau_{neg} = \tau_{pos}$：中等稳定&lt;/li&gt;
&lt;li&gt;$\tau_{neg} &amp;lt; \tau_{pos}$：&lt;strong&gt;显著不稳定&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;理论保证：序列连贯性与token自适应性&lt;/h2&gt;
&lt;h3&gt;两个关键假设&lt;/h3&gt;
&lt;p&gt;SAPO的理论分析基于两个在实际中普遍成立的假设：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(A1) 小步长假设&lt;/strong&gt;：$r_{i,t}(\theta) \approx 1$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这意味着 $\log r_{i,t}(\theta) \approx r_{i,t}(\theta) - 1$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;(A2) 低序列内方差&lt;/strong&gt;：token级别的重要性比率在一个序列内波动不大&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./moe-validation.jpg&quot; alt=&quot;MoE模型假设验证&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./dense-validation.jpg&quot; alt=&quot;Dense模型假设验证&quot; /&gt;&lt;/p&gt;
&lt;p&gt;实证数据显示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Token比率 $r_{i,t}$ 高度集中在1附近&lt;/li&gt;
&lt;li&gt;序列内方差通常低于0.02&lt;/li&gt;
&lt;li&gt;MoE模型的方差略高于Dense模型（受专家路由异构性影响）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;从token级到序列级的平滑过渡&lt;/h3&gt;
&lt;p&gt;在满足上述假设时，SAPO可以优雅地退化为类似GSPO的序列级更新：&lt;/p&gt;
&lt;p&gt;$$\text{平均token门控} \approx \text{序列级门控 } g_\tau(\log s_i(\theta)) = \text{sech}^2\left(\frac{\tau}{2} \cdot \log s_i(\theta)\right)$$&lt;/p&gt;
&lt;p&gt;更重要的是，当这些假设被违反（如出现离群token）时，SAPO会自动退回到token级门控，&lt;strong&gt;选择性降低离群token的权重，同时保留正常token的学习信号&lt;/strong&gt;——这是GSPO无法做到的。&lt;/p&gt;
&lt;h2&gt;实验验证：从数学推理到大规模生产&lt;/h2&gt;
&lt;h3&gt;控制实验：Qwen3-30B-A3B冷启动&lt;/h3&gt;
&lt;p&gt;在数学推理基准（AIME25、HMMT25、BeyondAIME）上的测试显示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./training-comparison.jpg&quot; alt=&quot;训练对比结果&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键发现&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;稳定性&lt;/strong&gt;：SAPO保持持续稳定学习，而GSPO和GRPO-R2在训练早期就发生崩溃&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最终性能&lt;/strong&gt;：SAPO在所有基准上达到更高的Pass@1准确率&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无需路由重放&lt;/strong&gt;：SAPO不依赖routing replay就能实现稳定训练，简化了工程实现&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;大规模生产验证：Qwen3-VL系列&lt;/h3&gt;
&lt;p&gt;SAPO的真正价值在于&lt;strong&gt;工业级大规模训练&lt;/strong&gt;中的验证：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./qwen3vl-training.jpg&quot; alt=&quot;Qwen3-VL训练结果&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实验设置&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模型：Qwen3-VL-30B-A3B&lt;/li&gt;
&lt;li&gt;任务：数学、编程、逻辑推理等多任务混合&lt;/li&gt;
&lt;li&gt;基准：AIME25、LiveCodeBench v6、ZebraLogic、MathVision&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：SAPO在相同计算预算下，一致性地优于GSPO和GRPO-R2，证明了其在实际生产环境中的可靠性。&lt;/p&gt;
&lt;h2&gt;实践启示：何时选择SAPO？&lt;/h2&gt;
&lt;h3&gt;适用场景&lt;/h3&gt;
&lt;p&gt;✅ &lt;strong&gt;MoE架构模型&lt;/strong&gt;：专家路由异构性导致的高方差问题&lt;br /&gt;
✅ &lt;strong&gt;长序列生成&lt;/strong&gt;：token级别方差累积更严重&lt;br /&gt;
✅ &lt;strong&gt;多任务学习&lt;/strong&gt;：不同任务的稳定性需求各异&lt;br /&gt;
✅ &lt;strong&gt;大规模训练&lt;/strong&gt;：需要长期稳定的训练动态&lt;/p&gt;
&lt;h3&gt;实施建议&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;温度参数调优&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;起始值：$\tau_{pos} = 1.0$, $\tau_{neg} = 1.05$&lt;/li&gt;
&lt;li&gt;根据训练稳定性动态调整&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;监控指标&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Token级重要性比率分布&lt;/li&gt;
&lt;li&gt;序列内log-ratio方差&lt;/li&gt;
&lt;li&gt;梯度范数变化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;与传统方法对比&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;GRPO&lt;/th&gt;
&lt;th&gt;GSPO&lt;/th&gt;
&lt;th&gt;SAPO&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;门控机制&lt;/td&gt;
&lt;td&gt;硬裁剪&lt;/td&gt;
&lt;td&gt;硬裁剪&lt;/td&gt;
&lt;td&gt;软门控&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;序列连贯性&lt;/td&gt;
&lt;td&gt;弱&lt;/td&gt;
&lt;td&gt;强&lt;/td&gt;
&lt;td&gt;自适应&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token级自适应性&lt;/td&gt;
&lt;td&gt;有&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;有&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;超参数敏感度&lt;/td&gt;
&lt;td&gt;中&lt;/td&gt;
&lt;td&gt;中&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;局限性与未来方向&lt;/h2&gt;
&lt;h3&gt;当前局限&lt;/h3&gt;
&lt;p&gt;⚠️ &lt;strong&gt;超参数调优成本&lt;/strong&gt;：虽然比硬裁剪稳定，但仍需针对具体任务调节温度参数&lt;br /&gt;
⚠️ &lt;strong&gt;计算开销&lt;/strong&gt;：软门控的指数运算比简单的阈值判断计算成本更高&lt;br /&gt;
⚠️ &lt;strong&gt;可解释性&lt;/strong&gt;：连续门控使得调试和分析比二元决策更复杂&lt;/p&gt;
&lt;h3&gt;未来研究方向&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自适应温度调度&lt;/strong&gt;：根据训练阶段动态调整温度参数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;与探索策略结合&lt;/strong&gt;：研究SAPO如何更好地支持高效的在线探索&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;理论深化&lt;/strong&gt;：建立更严格的收敛性保证和样本复杂度分析&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多模态扩展&lt;/strong&gt;：在视觉-语言等更多模态场景中验证有效性&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;SAPO代表了LLM强化学习训练方法的重要进步。通过&lt;strong&gt;软门控机制&lt;/strong&gt;替代硬裁剪，SAPO在保持训练稳定性的同时最大化保留了学习信号；通过&lt;strong&gt;非对称温度设计&lt;/strong&gt;，针对性地解决了负向更新带来的不稳定性问题。&lt;/p&gt;
&lt;p&gt;更重要的是，SAPO不是纸上谈兵的理论创新——它已经在Qwen3-VL系列的大规模生产训练中证明了其价值，为工业界提供了一种更可靠、可扩展且高效的RL训练策略。&lt;/p&gt;
&lt;p&gt;对于正在从事LLM RL训练的研究者和工程师，SAPO值得认真考虑作为GRPO和GSPO的替代方案。正如论文所言：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;SAPO provides a more reliable, scalable, and effective optimization strategy for RL training of LLMs.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;参考链接&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;论文：&lt;a href=&quot;https://arxiv.org/abs/2511.20347&quot;&gt;arXiv:2511.20347&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Qwen3-VL：&lt;a href=&quot;https://github.com/QwenLM/Qwen3-VL&quot;&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;相关方法：
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2402.03300&quot;&gt;GRPO: DeepSeekMath&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2507.18071&quot;&gt;GSPO: Group Sequence Policy Optimization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>DeepSeek-V3.2 技术报告阅读</title><link>https://etherwindy.github.io/AstroBlog/posts/deepseek-v3_2/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/deepseek-v3_2/</guid><description>《DeepSeek-V3.2: Pushing the Frontier of Open Large Language Models》阅读笔记</description><pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;论文链接：&lt;a href=&quot;https://modelscope.cn/models/deepseek-ai/DeepSeek-V3.2/resolve/master/assets/paper.pdf&quot;&gt;DeepSeek-V3.2: Pushing the Frontier of Open Large Language Models&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;DeepSeek-V3.2 架构&lt;/h2&gt;
&lt;h3&gt;DeepSeek 稀疏注意力（DeepSeek Sparse Attention，DSA）&lt;/h3&gt;
&lt;p&gt;符号约定：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;符号&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$I_{t,s}$&lt;/td&gt;
&lt;td&gt;index score&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\bold{h}_t\in\mathbb{R}^d$&lt;/td&gt;
&lt;td&gt;query token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\bold{h}_s\in\mathbb{R}^d$&lt;/td&gt;
&lt;td&gt;preceding token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$H^I$&lt;/td&gt;
&lt;td&gt;number of indexer heads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\bold{q}^I_{t,j}\in\mathbb{R}^{d^I}$&lt;/td&gt;
&lt;td&gt;derived from $\bold{h}_t$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\omega^I_{t,j}\in\mathbb{R}$&lt;/td&gt;
&lt;td&gt;derived from $\bold{h}_t$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$\bold{k}^I_s\in\mathbb{R}^{d^I}$&lt;/td&gt;
&lt;td&gt;derived from $\bold{h}_s$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;当前 token 和第 $s$ 个 token 的 index score 为：&lt;/p&gt;
&lt;p&gt;$$
I _ {t, s} = \sum_ {j = 1} ^ {H ^ {I}} w _ {t, j} ^ {I} \cdot \operatorname {R e L U} \left(\mathbf {q} _ {t, j} ^ {I} \cdot \mathbf {k} _ {s} ^ {I}\right), \tag {1}
$$&lt;/p&gt;
&lt;p&gt;选取 Top-k index score 的历史 token 参与注意力计算：&lt;/p&gt;
&lt;p&gt;$$
\mathbf {u} _ {t} = \operatorname {A t t n} \left(\mathbf {h} _ {t}, \left{\mathbf {c} _ {s} \mid I _ {t, s} \in \operatorname {T o p} - \mathrm {k} \left(I _ {t;}\right) \right}\right). \tag {2}
$$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./architecture.png&quot; alt=&quot;&quot; /&gt;
Figure 2 | DeepSeek-V3.2 的注意力架构，其中 DSA 在 MLA 下实例化。绿色部分展示了 DSA 如何根据索引器选择前k个键值条目。&lt;/p&gt;
&lt;p&gt;复杂度：$O(L^2)\rightarrow O(kL)$，其中 $k\ll L$.&lt;/p&gt;
&lt;h3&gt;持续预训练（Continued Pre-Training）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基模：DeepSeek-V3.1-Terminus 的一个检查点&lt;/li&gt;
&lt;li&gt;训练设置：DeepSeek-V3.2的持续预训练由两个训练阶段组成。在这两个阶段中，训练数据的分布与用于DeepSeek-V3.1-Terminus的128K长上下文扩展数据完全对齐&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;稠密预热阶段（Dense Warm-up Stage）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;目的：初始化 lighting indexer&lt;/li&gt;
&lt;li&gt;方法：保持密集注意力，并冻结所有模型参数，除了 lighting indexer。为了使索引器的输出与主要注意力分布对齐，对于第 $t$ 个查询标记，首先通过对所有注意力头的得分进行求和来聚合主要注意力得分。然后沿序列维度对这个和进行 L1 归一化，以产生目标分布 $p*{t,:} \in \mathbb{R}^t$。基于 $p*{t,:}$，将 KL 散度损失设定为索引器的训练目标：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$$
\mathcal {L} ^ {I} = \sum_ {t} \mathbb {D} _ {\mathrm {K L}} \left(p _ {t,:} | \operatorname {Softmax} \left(I _ {t,:}\right)\right). \tag {3}
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;训练设置：&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;数值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;学习率&lt;/td&gt;
&lt;td&gt;$10^{-3}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;步数&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;每步序列数&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;单序列长度&lt;/td&gt;
&lt;td&gt;128K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;总 token 数&lt;/td&gt;
&lt;td&gt;21亿&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;稀疏训练阶段（Sparse Training Stage）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;目的：在 indexer 预热之后，引入了细粒度的 token 选择机制，并优化所有模型参数，以使模型适应 DSA 的稀疏模式&lt;/li&gt;
&lt;li&gt;方法：保持将索引器输出与主要注意力分布对齐，但仅考虑所选的 token 集 $S_{t} = {s\mid I_{t,s}\in \mathrm{Top - k}(I_{t;})}$ :&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$$
\mathcal {L} ^ {I} = \sum_ {t} \mathbb {D} _ {\mathrm {K L}} \left(p _ {t, S _ {t}} | \operatorname {S o f t m a x} \left(I _ {t, S _ {t}}\right)\right). \tag {4}
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;训练时，索引器输入与计算图分离，以便进行单独优化&lt;/strong&gt;。索引器的训练信号仅来自 $\mathcal{L}^I$ ，而主模型的优化仅根据语言建模损失进行。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;训练设置：&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;数值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;学习率&lt;/td&gt;
&lt;td&gt;$10^{-6}$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;步数&lt;/td&gt;
&lt;td&gt;15000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;每步序列数&lt;/td&gt;
&lt;td&gt;480&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;单序列长度&lt;/td&gt;
&lt;td&gt;128K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;总 token 数&lt;/td&gt;
&lt;td&gt;943.7B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;后训练（Post-Training）&lt;/h2&gt;
&lt;p&gt;DeepSeek-V3.2 的后训练同样以与稀疏持续预训练阶段相同的方式采用稀疏注意力。DeepSeek-V3.2 保持与 DeepSeek-V3.2-Exp 相同的后训练流程，其中包括专家蒸馏和混合强化学习训练。&lt;/p&gt;
&lt;h3&gt;专家蒸馏&lt;/h3&gt;
&lt;p&gt;对于每个任务，开发一个专门针对该特定领域的专用模型，所有专家模型均从同一预训练的DeepSeek-V3.2基础检查点进行微调。除了写作任务和通用问答外，该框架还涵盖六个专业领域：数学、编程、通用逻辑推理、通用智能体任务、智能体编程和智能体搜索，所有这些领域都支持思考和非思考两种模式。每个专家模型都通过大规模强化学习（RL）计算进行训练。此外，采用不同的模型来生成用于长链式思维推理（思考模式）和直接响应生成（非思考模式）的训练数据。专家模型准备就绪后，它们被用来为最终检查点生成领域特定的数据。实验结果表明，在蒸馏数据上训练的模型，其性能水平仅略低于领域特定专家模型的性能，而通过后续的强化学习训练，这一性能差距得到了有效消除。&lt;/p&gt;
&lt;h3&gt;混合RL训练&lt;/h3&gt;
&lt;p&gt;将推理、代理和人类对齐训练合并为一个强化学习阶段。这种方法有效平衡了不同领域的表现，同时避免了多阶段训练范式中常见的灾难性遗忘问题。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;推理和agent任务: 基于规则的结果奖励、长度惩罚和语言一致性奖励。&lt;/li&gt;
&lt;li&gt;一般任务: 生成奖励模型，每个提示都有自己的评分标准。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Scaling GRPO&lt;/h3&gt;
&lt;p&gt;DeepSeek-V3.2 的 GRPO 主要增加了无偏 KL 散度估计以及非策略序列掩码（Off-Policy Sequence Masking）。公式如下：&lt;/p&gt;
&lt;p&gt;$$
\begin{array}{l} \mathcal {J} _ {\mathrm {G R P O}} (\theta) = \mathbb {E} _ {q \sim P (Q), {o _ {i} } _ {i = 1} ^ {G} \sim \pi_ {\mathrm {o l d}} (\cdot | q)} \left[ \frac {1}{G} \sum_ {i = 1} ^ {G} \frac {1}{| o _ {i} |} \sum_ {t = 1} ^ {| o _ {i} |} \right. \ \left. \min \left(r _ {i, t} (\theta) \hat {A} _ {i, t}, \operatorname {c l i p} \left(r _ {i, t} (\theta), 1 - \varepsilon , 1 + \varepsilon\right) \hat {A} _ {i, t}\right) M _ {i, t} - \beta \mathbb {D} _ {\mathrm {K L}} \left(\pi_ {\theta} (o _ {i, t}) \left| \pi_ {\mathrm {r e f}} (o _ {i, t})\right) \right], \quad (8) \right. \ \end{array}
$$&lt;/p&gt;
&lt;p&gt;其中&lt;/p&gt;
&lt;p&gt;$$
\mathbb {D} _ {\mathrm {K L}} \big (\pi_ {\theta} (o _ {i, t}) \left| \pi_ {\mathrm {r e f}} (o _ {i, t}) \right. \big) = \frac {\pi_ {\theta} (o _ {i , t} | q , o _ {i , &amp;lt;   t})}{\pi_ {\mathrm {o l d}} (o _ {i , t} | q , o _ {i , &amp;lt;   t})} \left(\frac {\pi_ {\mathrm {r e f}} (o _ {i , t} | q , o _ {i , &amp;lt;   t})}{\pi_ {\theta} (o _ {i , t} | q , o _ {i , &amp;lt;   t})} - \log \frac {\pi_ {\mathrm {r e f}} (o _ {i , t} | q , o _ {i , &amp;lt;   t})}{\pi_ {\theta} (o _ {i , t} | q , o _ {i , &amp;lt;   t})} - 1\right),
$$&lt;/p&gt;
&lt;p&gt;$$
M _ {i, t} = \left{ \begin{array}{l l} 0 &amp;amp; \hat {A} _ {i, t} &amp;lt;   0, \frac {1}{| o _ {i} |} \sum_ {t = 1} ^ {| o _ {i} |} \log \frac {\pi_ {\text {o l d}} \left(o _ {i , t} \mid q , o _ {i , &amp;lt;   t}\right)}{\pi_ {\theta} \left(o _ {i , t} \mid q , o _ {i , &amp;lt;   t}\right)} &amp;gt; \delta, \ 1 &amp;amp; \text {o t h e r w i s e}. \end{array} \right.
$$&lt;/p&gt;
&lt;p&gt;非策略序列掩码的目的在于掩盖了引入显著策略偏差的负序列，从而稳定训练并提高对非策略更新的容忍度。&lt;/p&gt;
</content:encoded></item><item><title>rustdesk + EasyTier 远程桌面办公</title><link>https://etherwindy.github.io/AstroBlog/posts/rustdesk-easytier/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/rustdesk-easytier/</guid><description>使用 EasyTier 组网 + rustdesk 远程桌面实现广域网下远程桌面办公</description><pubDate>Wed, 02 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;相关链接:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://rustdesk.com/zh-cn/&quot;&gt;rustdesk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://easytier.cn&quot;&gt;EasyTier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;::github{repo=&quot;rustdesk/rustdesk&quot;}
::github{repo=&quot;EasyTier/EasyTier&quot;}&lt;/p&gt;
&lt;p&gt;今年六月份我趁着 618 和国补买了一台 MacBook Air M4 作为主力机，自此我的手上一共有三台笔记本（一台 MBA、一台 MBP 和一台 Win 本）。由于一些原因，有些工作必须在另外一台 Mac 上完成，因此每天往返寝室和工位不得不背两台电脑。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;主播，你这种打法还是太吃体力了&lt;/s&gt;，有没有什么方法可以只背一台电脑上班吗？有的！有的！答案就是使用&lt;strong&gt;远程桌面&lt;/strong&gt;。之前我也使用过一些集成的远程桌面软件，比如向日葵和网易 uu 远程，但它们要么性能不佳，要么对 Mac 没有进行适配。这次我选择使用 rustdesk + easytier 的方案。&lt;/p&gt;
&lt;h2&gt;rustdesk 远程桌面&lt;/h2&gt;
&lt;h3&gt;简介&lt;/h3&gt;
&lt;p&gt;rustdesk 是一款开源的远程桌面软件，支持自托管和安全性。其功能特性包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持 Windows、MacOS、Linux、iOS、Android 多平台&lt;/li&gt;
&lt;li&gt;支持 VP8 / VP9 / AV1 软件编解码器，以及 H264 / H265 硬件编解码器&lt;/li&gt;
&lt;li&gt;可以自行建立自托管服务器&lt;/li&gt;
&lt;li&gt;基于 NaCl 的端到端加密&lt;/li&gt;
&lt;li&gt;支持远程复制文件、剪切板共享&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;下载&lt;/h3&gt;
&lt;p&gt;Homebrew:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;brew install --cask rustdesk

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scoop:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scoop install rustdesk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;rustdesk 在局域网内可以通过 IP 直连。对于公网环境，rustdesk 有官方的服务器，但个人实测非常难用。如果有公网 IP 的服务器的话可以部署自托管服务。自托管服务优先使用 ID 服务器尝试 P2P 连接，如果无法连接会转为使用中介服务器。网上也可以找到一些公开的自托管服务器。&lt;/p&gt;
&lt;p&gt;如果没有自托管服务器，也可以使用 EasyTier、Tailscale、ZeroTier 等工具远程组网，并用局域网连接的方法使用 rustdesk。&lt;/p&gt;
&lt;h2&gt;EasyTier 远程组网&lt;/h2&gt;
&lt;h3&gt;简介&lt;/h3&gt;
&lt;p&gt;与 Tailscale 不同，EasyTier 是一款去中心化的组网方案。理论上网络中的所有节点地位相同，不存在 server 节点和 client 节点的区别。&lt;/p&gt;
&lt;h3&gt;下载&lt;/h3&gt;
&lt;p&gt;EasyTier 可以下载 GUI 版本和 CLI 版本。个人比较推荐使用 CLI 版本，因为 CLI 版本可以注册成服务开机自启，可以配合远程桌面进行远程重启。此外，CLI 还可以通过 webui 远程配置其他节点的网络，这样当网络设置出现问题时可以进行修改。&lt;/p&gt;
&lt;p&gt;GUI 下载方式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;brew tap brewforge/chinese
brew install --cask easytier-gui
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CLI 可前往 GitHub 发布界面下载：&lt;a href=&quot;https://github.com/EasyTier/EasyTier/releases&quot;&gt;releases&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;rustdesk + EasyTier 使用体验&lt;/h2&gt;
&lt;p&gt;这一套方案最大的好处是不需要部署任何的中心服务器，十分方便。在网络状况好的情况下能到 30 ～ 40 帧，足以满足日常办公的需要，但不知道为什么帧率无法再高了。打游戏的话还得靠 Sunshine + Moonlight（日月也可以使用 EasyTier 远程连接，有空会写一个日月&amp;amp;EasyTier 的游戏体验 Blog）。&lt;/p&gt;
</content:encoded></item><item><title>从 Hexo 移民到 Astro</title><link>https://etherwindy.github.io/AstroBlog/posts/astro-blog/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/astro-blog/</guid><description>Astro 博客初体验</description><pubDate>Tue, 04 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover image source: &lt;a href=&quot;https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/208fc754-890d-4adb-9753-2c963332675d/width=2048/01651-1456859105-(colour_1.5),girl,_Blue,yellow,green,cyan,purple,red,pink,_best,8k,UHD,masterpiece,male%20focus,%201boy,gloves,%20ponytail,%20long%20hair,.jpeg&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;::github{repo=&quot;saicaca/fuwari&quot;}&lt;/p&gt;
&lt;p&gt;起因是在校内论坛上偶然看到校友的博客主页，很是心动，查了一下博客的框架，发现是用 Astro 写的，于是动了从 Hexo 迁移到 Astro 到心思。由于本人的前端知识十分浅薄，Astro 的具体优势我也说不出什么名堂，对我而言最大的优势就是魔改模版十分方便。之前我使用的 Hexo 模版是 Butterfly，迁移到 Astro 后选择了风格接近的 Fuwari，并按照自己的喜好对布局和动画进行了一些修改。迁移工作十分顺利，只需要修改 markdown 文件 YAML 元信息即可。有空的话未来可能会对模版进行进一步魔改。&lt;/p&gt;
</content:encoded></item><item><title>在远程容器上部署 Ollama 和 Open WebUI</title><link>https://etherwindy.github.io/AstroBlog/posts/ollama-openwebui-ssh/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/ollama-openwebui-ssh/</guid><description>用 ssh 端口转发访问远程容器上的 Ollama 和 Open WebUI 服务</description><pubDate>Wed, 26 Feb 2025 20:11:58 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover Image Source: &lt;a href=&quot;https://ollama.com/public/blog/embedding-models.png&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;相关链接:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.deepseek.com/&quot;&gt;DeepSeek&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ollama.com/&quot;&gt;Ollama&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.openwebui.com/&quot;&gt;Open WebUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chatboxai.app/zh&quot;&gt;Chatbox&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;::github{repo=&quot;deepseek-ai/DeepSeek-R1&quot;}
::github{repo=&quot;ollama/ollama&quot;}
::github{repo=&quot;open-webui/open-webui&quot;}&lt;/p&gt;
&lt;h2&gt;访问远程容器内的 Ollama 服务&lt;/h2&gt;
&lt;p&gt;DeepSeek-R1 问世之后，我打算自己用 Ollama 部署一个 DeepSeek-R1 的 32B 蒸馏版玩玩，手上能用的算力资源只有部署在实验室服务器上的 docker 容器。刚开始上手时，我打算使用 Chatbox 连接远程的 Ollama 服务，却发现容器部署的时候没有设置端口映射，因此不能远程直接访问 Ollama 服务监听的 &lt;code&gt;localhost:11434&lt;/code&gt; 端口，只能作罢，改用 ssh 直接访问容器进行使用。&lt;/p&gt;
&lt;p&gt;一段时间后我突然想到：为什么不直接使用&lt;code&gt;ssh&lt;/code&gt;的端口转发功能呢？想毕，直接上手尝试，果然可行，遂记录下使用方法。&lt;/p&gt;
&lt;h3&gt;SSH 端口转发介绍&lt;/h3&gt;
&lt;p&gt;以下内容由 deepseek-r1:32b 生成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SSH（Secure Shell）不仅用于安全地登录远程服务器，还可以通过端口转发功能在不同的网络之间传输数据。这在需要访问被防火墙保护的内部服务或从外部访问本地服务时非常有用。&lt;/p&gt;
&lt;h4&gt;1. &lt;code&gt;ssh -L&lt;/code&gt;（本地端口转发）&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;功能描述：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-L&lt;/code&gt; 是“Local”（本地）的缩写，用于将本地计算机的一个端口上的流量通过 SSH 连接转发到远程服务器，然后进一步转发到目标地址和端口。&lt;/li&gt;
&lt;li&gt;这种方式适用于访问位于远程网络内部、无法直接从外部访问的服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;语法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -L [本地端口]:[目标地址]:[目标端口] 用户名@远程服务器
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;工作流程：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;本地机器&lt;/strong&gt;监听指定的本地端口（如 8080）。&lt;/li&gt;
&lt;li&gt;当有流量通过该端口发送到 SSH 客户端时，SSH 将这些数据加密并转发到 &lt;code&gt;远程服务器&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;远程服务器接收到请求后，将数据解密并转发到 &lt;code&gt;[目标地址]:[目标端口]&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;
假设你希望通过本地机器访问公司内部的 Web 服务（运行在服务器 &lt;code&gt;internal-web&lt;/code&gt; 的 80 端口），而该服务仅对公司内部网络开放。你可以这样做：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -L 8080:internal-web:80 用户名@example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;这样，当你访问本地的 &lt;code&gt;localhost:8080&lt;/code&gt; 时，实际连接的是 &lt;code&gt;example.com&lt;/code&gt; 的 SSH 服务器，然后被转发到 &lt;code&gt;internal-web:80&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. &lt;code&gt;ssh -R&lt;/code&gt;（远程端口转发）&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;功能描述：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-R&lt;/code&gt; 是“Remote”（远程）的缩写，允许远程服务器上的一个端口接收到的数据通过 SSH 连接转发到本地机器或其他目标地址和端口。&lt;/li&gt;
&lt;li&gt;这种方式适用于从外部访问本地服务，例如让朋友连接你的本地开发环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;语法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -R [远程端口]:[目标地址]:[目标端口] 用户名@远程服务器
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;工作流程：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;远程服务器&lt;/strong&gt;监听指定的远程端口（如 2222）。&lt;/li&gt;
&lt;li&gt;当有流量通过该端口发送到 SSH 服务器时，SSH 将这些数据加密并转发到本地机器或其他目标地址和端口。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;示例：&lt;/strong&gt;
假设你想让朋友能够连接你本地运行的一个服务（如 SSH 服务在本地的 22 端口），你可以这样做：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -R 2222:localhost:22 用户名@example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;这样，当有人连接到 &lt;code&gt;example.com&lt;/code&gt; 的 2222 端口时，数据会被转发到你的本地机器的 22 端口。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 区别与应用场景&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;&lt;code&gt;-L&lt;/code&gt;（本地端口转发）&lt;/th&gt;
&lt;th&gt;&lt;code&gt;-R&lt;/code&gt;（远程端口转发）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;数据流向&lt;/td&gt;
&lt;td&gt;本地 → 远程服务器 → 目标地址&lt;/td&gt;
&lt;td&gt;远程服务器 → 本地或其他目标地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;常见用途&lt;/td&gt;
&lt;td&gt;访问内部网络服务，如公司内部 Web 应用&lt;/td&gt;
&lt;td&gt;允许外部访问本地服务，如开发环境&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;注意事项&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;权限问题：&lt;/strong&gt; 确保 SSH 服务器允许端口转发。默认情况下，一些服务器可能禁用了 &lt;code&gt;-R&lt;/code&gt; 转发以防止潜在的安全风险。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目标地址可达性：&lt;/strong&gt; 在 &lt;code&gt;-L&lt;/code&gt; 中，目标地址必须是远程服务器能够访问的；在 &lt;code&gt;-R&lt;/code&gt; 中，目标地址可以是本地或其他网络中的机器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过合理使用 &lt;code&gt;ssh -L&lt;/code&gt; 和 &lt;code&gt;ssh -R&lt;/code&gt;，你可以在复杂的网络环境中安全地访问所需的服务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可以看出利用 &lt;code&gt;ssh -L&lt;/code&gt; 指令可以将本地计算机的端口上的端口流量转发到远程服务器的目标端口上，因此考虑将本地的 11434 端口转发到 labserver （远程容器的 Host Name）的 11434 端口，命令如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -L 11434:localhost:11434 labserver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行之后在 Chatbox 中成功访问远程容器中的 Ollama 服务。&lt;/p&gt;
&lt;h2&gt;访问远程容器上的 Open WebUI 服务&lt;/h2&gt;
&lt;p&gt;成功访问 ollama 服务，我想到：能否使用 Open WebUI 来调用 Ollama 服务上的模型呢？这里存在两种方案：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在本地部署 Open WebUI，连接远程的 Ollama 服务&lt;/li&gt;
&lt;li&gt;在远程容器上同时部署 Open WebUI 和 Ollama&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了能够在多台电脑上使用 Open WebUI，我选择第二种方案。我在远程容器上用 &lt;code&gt;pip&lt;/code&gt; 安装了 Open WebUI，服务运行在 &lt;code&gt;localhost:8080&lt;/code&gt; 端口上。由于 Open WebUI 和 Ollama 在同一容器中，因此使用 &lt;code&gt;ssh&lt;/code&gt; 时不再需要转发 &lt;code&gt;11434&lt;/code&gt; 端口。命令如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -L 8080:localhost:8080 labserver
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;联网搜索&lt;/h2&gt;
&lt;p&gt;成功访问远程容器上的 Open WebUI 服务后，我又遇到了新的困难。使用联网搜索功能时，远程容器无法访问许多网页，导致联网搜索功能失效。由于本地个人电脑可以启动网络代理访问这些网页，因此考虑使用 &lt;code&gt;ssh -R&lt;/code&gt; 命令来反向转发流量。假设个人电脑上的网络代理为 &lt;code&gt;127.0.0.1:7897&lt;/code&gt;，在远程服务器上运行如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export https_proxy=http://127.0.0.1:7890
export http_proxy=http://127.0.0.1:7890
export all_proxy=socks5://127.0.0.1:7890
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并在同一终端中启动 Open WebUI 服务。接着在本地服务器上运行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -R labserver:7890:localhost:7897 -L 8080:localhost:8080 labserver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然而打开网页后发现 Open WebUI 无法找到 Ollama 上的模型了。略加思索发现由于远程服务器的网络流量转发到个人电脑上了，而个人电脑的 &lt;code&gt;localhost:11434&lt;/code&gt; 端口并没有运行 Ollama 服务，因此只要将流量转发回去就行了。新的命令如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -R labserver:7890:localhost:7897 -L 8080:localhost:8080 -L 11434:localhost:11434 labserver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功访问！&lt;/p&gt;
&lt;p&gt;如果想要后台挂起，可以使用如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -fN -R labserver:7890:localhost:7897 -L 8080:localhost:8080 -L 11434:localhost:11434 labserver
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>通过 freesound api 下载音频数据</title><link>https://etherwindy.github.io/AstroBlog/posts/freesound-api/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/freesound-api/</guid><pubDate>Wed, 27 Nov 2024 17:06:43 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover Image Source：&lt;a href=&quot;https://analyticsindiamag.com/wp-content/uploads/2020/12/image-1.png&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;详细内容请参考 &lt;a href=&quot;https://freesound.org/docs/api/client_libs.html&quot;&gt;https://freesound.org/docs/api/client_libs.html&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;::github{repo=&quot;MTG/freesound-python&quot;}&lt;/p&gt;
&lt;h2&gt;准备工作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在 freesound.org 官网上请求 APIv2 凭证（&lt;a href=&quot;https://freesound.org/apiv2/apply&quot;&gt;https://freesound.org/apiv2/apply&lt;/a&gt;）。需要注意，APIv2 的使用仅限于一定的使用率。标准使用费率设置为每分钟 60 个请求和每天 2000 个请求。包括上传、描述、评论、评分和书签声音在内的资源具有更严格的速率，即每分钟 30 个请求和每天 500 个请求。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;安装相关的 python 包：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install freesound-python
pip install requests-oauthlib
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;获取音频元信息&lt;/h2&gt;
&lt;h3&gt;搜索&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import freesound

client = freesound.FreesoundClient()
client.set_token(&quot;&amp;lt;your_api_key&amp;gt;&quot;,&quot;token&quot;)

results = client.text_search(
  query=&quot;violoncello&quot;,
    filter=&quot;tag:tenuto duration:[1.0 TO 15.0]&quot;,
    sort=&quot;rating_desc&quot;,
    fields=&quot;id,name,previews,username&quot;,
)

for sound in results:
    print(sound.name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;query&lt;/code&gt; 为查询，&lt;code&gt;fields&lt;/code&gt; 为需要获取的元信息，&lt;code&gt;filter&lt;/code&gt; 为过滤器，&lt;code&gt;sort&lt;/code&gt; 为排序方式。&lt;code&gt;text_search&lt;/code&gt; 返回的是一个 &lt;code&gt;Pager&lt;/code&gt; 对象，结构如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;count&quot;: &amp;lt;total number of results&amp;gt;,
  &quot;next&quot;: &amp;lt;link to the next page of results (null if none)&amp;gt;,
  &quot;results&quot;: [
      &amp;lt;sound result #1 info&amp;gt;,
      &amp;lt;sound result #2 info&amp;gt;,
      ...
      &amp;lt;sound result #page_size info&amp;gt;
  ],
  &quot;previous&quot;: &amp;lt;link to the previous page of results (null if none)&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以通过设置 &lt;code&gt;text_search&lt;/code&gt; 的 &lt;code&gt;page&lt;/code&gt; 和 &lt;code&gt;page_size&lt;/code&gt; 参数设置需要返回哪一页以及每页有多少结果。&lt;/p&gt;
&lt;h3&gt;获取随机音频&lt;/h3&gt;
&lt;p&gt;通过 &lt;a href=&quot;https://freesound.org/browse/random/&quot;&gt;https://freesound.org/browse/random/&lt;/a&gt; 可以获取随机的音频 id，接着通过 api 搜索即可获取音频&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import freesound
import request
import re

response = requests.get(&quot;https://freesound.org/browse/random/&quot;)
sound_id = re.search(r&apos;/sounds/(\d+)/&apos;, reponse.text).group(1)
sound = client.get_sound(sound_id)
print(sound.name)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;下载音频&lt;/h2&gt;
&lt;p&gt;下载预览音频的代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# sound 为 client 获取到的音频，例如 sound = client.get_sound(&amp;lt;sound_id&amp;gt;)
sound.retrieve_preivew(file_path, name=f&quot;{sound[&apos;id&apos;]}.mp3&quot;, quality=&quot;hq&quot;, file_format=&quot;mp3&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;quality&lt;/code&gt; 为预览音频的质量，有 &lt;code&gt;&quot;lq&quot;&lt;/code&gt; 和 &lt;code&gt;&quot;hq&quot;&lt;/code&gt; 两个选项，分别代表低质量和高质量。默认下载的是低质量音频。低质量音频的下载速度会比高质量音频快很多。音频文件格式可以选择 mp3 和 ogg 两种。&lt;/p&gt;
&lt;p&gt;下载原始音频需要 OAuth2 认证，OAuth2 认证需要以下步骤：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from requests_oauthlib import OAuth2Session

import freesound

client_id = &quot;&amp;lt;your_client_id&amp;gt;&quot;
client_secret = &quot;&amp;lt;your_client_secret&amp;gt;&quot;

# do the OAuth dance
oauth = OAuth2Session(client_id)

authorization_url, state = oauth.authorization_url(
    &quot;https://freesound.org/apiv2/oauth2/authorize/&quot;
)
print(f&quot;Please go to {authorization_url} and authorize access.&quot;)

authorization_code = input(&quot;Please enter the authorization code:&quot;)
oauth_token = oauth.fetch_token(
    &quot;https://freesound.org/apiv2/oauth2/access_token/&quot;,
    authorization_code,
    client_secret=client_secret,
)

client = freesound.FreesoundClient()
client.set_token(oauth_token[&quot;access_token&quot;], &quot;oauth&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码会生成一个网址，用户通过访问该网址获得认证码进行认证。&lt;/p&gt;
&lt;p&gt;下载原始音频代码为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sound.retrieve(file_path, name=None)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认会使用音频的原始名称，保留音频的原始上传格式。&lt;/p&gt;
</content:encoded></item><item><title>MinHash LSH</title><link>https://etherwindy.github.io/AstroBlog/posts/minhash-lsh/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/minhash-lsh/</guid><description>MinHash Locality Sensitive Hashing 算法简介</description><pubDate>Wed, 13 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt;MinHash LSH（MinHash Locality-Sensitive Hashing）是一种用于高效近似最近邻搜索的技术，广泛应用于处理大规模集合数据的相似性比较，特别是在文本、图像和其他高维数据的相似性搜索中。&lt;/p&gt;
&lt;h3&gt;MinHash 的基本原理&lt;/h3&gt;
&lt;p&gt;MinHash 是一种用于估计集合相似性（通常是 Jaccard 相似性）的方法。Jaccard 相似性是两个集合交集的大小与它们并集的大小之比。MinHash 通过以下步骤来估计这个相似性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;哈希函数&lt;/strong&gt;：使用多个哈希函数将集合中的元素映射到一个较小的值域中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最小哈希值&lt;/strong&gt;：对于每个集合，计算所有哈希值的最小值，这个最小值称为 MinHash 值。通过多个不同的哈希函数，可以得到多个 MinHash 值，从而形成一个 MinHash 签名。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;相似性估计&lt;/strong&gt;：两个集合的 MinHash 签名的相似度（即它们的 MinHash 值的相等比例）可以用来估计这两个集合的 Jaccard 相似度。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;LSH 的基本原理&lt;/h3&gt;
&lt;p&gt;局部敏感哈希（Locality-Sensitive Hashing，LSH）是一种技术，旨在通过将相似的对象映射到同一个桶中来加速相似性搜索。LSH 的关键是设计哈希函数，使得相似的输入数据在哈希后有较高的概率被映射到同一个桶中，而不相似的数据则有较低的概率。&lt;/p&gt;
&lt;h3&gt;MinHash LSH 的结合&lt;/h3&gt;
&lt;p&gt;MinHash LSH 将 MinHash 和 LSH 结合起来，具体步骤如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;生成 MinHash 签名&lt;/strong&gt;：对每个集合使用 MinHash 方法生成一个 MinHash 签名。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;哈希签名&lt;/strong&gt;：将 MinHash 签名通过 LSH 哈希函数进行映射。每个签名会被映射到一个或多个桶中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;桶的查询&lt;/strong&gt;：在进行相似性查询时，只需检查与查询对象在同一个桶中的其他对象，从而大幅减少需要比较的对象数量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;应用场景&lt;/h3&gt;
&lt;p&gt;MinHash LSH 在许多应用中非常有用，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;文本相似性检测&lt;/strong&gt;：例如，检测文档或网页的重复内容。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推荐系统&lt;/strong&gt;：为用户提供相似兴趣的物品。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;图像查找&lt;/strong&gt;：寻找相似的图像或图像特征。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;社交网络分析&lt;/strong&gt;：寻找社交网络中相似的用户或群组。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过使用 MinHash LSH，系统可以高效地处理大规模数据集，实现快速的相似性搜索，同时保持较高的准确性。&lt;/p&gt;
&lt;h2&gt;计算方法&lt;/h2&gt;
&lt;p&gt;当然可以！以下是计算 MinHash LSH 的具体步骤，包括如何生成 MinHash 签名、如何进行局部敏感哈希（LSH）以及如何使用这些信息进行相似性搜索。&lt;/p&gt;
&lt;h3&gt;步骤 1：准备数据&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;输入数据&lt;/strong&gt;：准备一组集合数据，例如文档的词集、用户的兴趣标签等。每个集合可以用一个唯一的标识符表示。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;步骤 2：生成 MinHash 签名&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;选择哈希函数&lt;/strong&gt;：选择一组哈希函数。通常使用多个独立的哈希函数来生成不同的 MinHash 值。每个哈希函数 ( h_i ) 可以将集合元素映射到一个整数值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;计算 MinHash 值&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于每个集合 $S_j$，执行以下操作：
&lt;ul&gt;
&lt;li&gt;对集合中的每个元素 $x$ 计算哈希值 $h_i(x)$。&lt;/li&gt;
&lt;li&gt;记录每个哈希函数 $h_i$ 的最小值：
$$
\text{MinHash}&lt;em&gt;i(S_j) = \min&lt;/em&gt;{x \in S_j}(h_i(x))
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;这样，对于每个集合 $S_j$，你将得到一个 MinHash 签名 $\text{MinHash}(S_j) = [\text{MinHash}_1(S_j), \text{MinHash}_2(S_j), \ldots, \text{MinHash}_k(S_j)]$，其中 $k$ 是哈希函数的数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;步骤 3：构建 LSH 哈希表&lt;/h3&gt;
&lt;p&gt;在这个步骤中，我们将生成 LSH 哈希表，以便将 MinHash 签名进行分组，便于后续的相似性查询。以下是更详细的步骤：&lt;/p&gt;
&lt;h4&gt;3.1 选择 LSH 参数&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;选择桶的数量 $b$&lt;/strong&gt;：决定将生成的 MinHash 签名分成多少个部分。每个部分对应一个桶。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每个桶的签名数量 $r$&lt;/strong&gt;：决定每个桶中包含多少个 MinHash 值。通常情况下，$r$ 的值应该小于 MinHash 签名的总长度 $k$（即使用的哈希函数数量）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3.2 创建 LSH 哈希表&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;初始化哈希表&lt;/strong&gt;：创建一个空的哈希表（可以使用字典或其他数据结构），每个桶的键是哈希值，值是存储集合标识符的列表。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;为每个集合生成 MinHash 签名&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设你已经为每个集合 $S_j$ 计算了 MinHash 签名：
$$
\text{MinHash}(S_j) = [\text{MinHash}_1(S_j), \text{MinHash}_2(S_j), \ldots, \text{MinHash}_k(S_j)]
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;将 MinHash 签名划分为桶&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于每个 MinHash 签名，将其划分为 $b$ 个部分，每个部分包含 $r$ 个 MinHash 值（即每个部分的长度为 $r$）。&lt;/li&gt;
&lt;li&gt;例如，如果 $k = br$，那么每个 MinHash 签名可以被分成 $b$ 个长度为 $r$ 的子签名：
$$
\text{MinHash}(S_j) = [\underbrace{\text{MinHash}&lt;em&gt;1(S_j), \ldots, \text{MinHash}&lt;em&gt;r(S_j)}&lt;/em&gt;{\text{Part 1}}, \underbrace{\text{MinHash}&lt;/em&gt;{r+1}(S_j), \ldots, \text{MinHash}&lt;em&gt;{2r}(S_j)}&lt;/em&gt;{\text{Part 2}},\\ldots, \underbrace{\text{MinHash}&lt;em&gt;{(b-1)r+1}(S_j), \ldots, \text{MinHash}&lt;/em&gt;{br}(S_j)}_{\text{Part b}}]
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;为每个部分生成哈希值&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于每个部分（子签名），生成一个哈希值。可以使用简单的哈希函数，如 SHA-1、MD5 或自定义的哈希函数。&lt;/li&gt;
&lt;li&gt;对于第 $i$ 个桶（$i$ 从 1 到 $b$），计算哈希值：
$$
h_i(S_j) = \text{hash}(\text{MinHash}&lt;em&gt;{(i-1)r+1}(S_j), \ldots, \text{MinHash}&lt;/em&gt;{ir}(S_j))
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;将集合标识符存入哈希表&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用哈希值 $h_i(S_j)$ 作为键，将集合标识符 $S_j$ 存入哈希表中对应的桶。如果该哈希值不存在，则创建一个新的列表并添加 $S_j$；如果已存在，则将 $S_j$ 添加到现有的列表中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如，如果哈希表是一个字典，添加的代码可能如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hash_table[h_i(S_j)] = hash_table.get(h_i(S_j), []) + [S_j]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3.3 处理所有集合&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;对于每一个集合 $S_j$，重复上述过程，直到所有集合都被处理并存入 LSH 哈希表中。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;示例&lt;/h3&gt;
&lt;p&gt;假设我们有三个集合和我们选择的参数 $b = 2$，$r = 2$，并且每个集合的 MinHash 签名长度为 4（即 $k = 4$）。我们将每个 MinHash 签名划分为两个部分，每个部分包含两个 MinHash 值。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集合 $S_1$ 的 MinHash 签名：
$$
\text{MinHash}(S_1) = [3, 1, 4, 2]  \quad \text{(分为两部分: [3, 1], [4, 2])}
$$&lt;/li&gt;
&lt;li&gt;集合 $S_2$ 的 MinHash 签名：
$$
\text{MinHash}(S_2) = [2, 0, 3, 1]  \quad \text{(分为两部分: [2, 0], [3, 1])}
$$&lt;/li&gt;
&lt;li&gt;集合 $S_3$ 的 MinHash 签名：
$$
\text{MinHash}(S_3) = [1, 1, 2, 0]  \quad \text{(分为两部分: [1, 1], [2, 0])}
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后，计算每个部分的哈希值并将集合存入哈希表中。&lt;/p&gt;
&lt;h3&gt;步骤 4：查询&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;查询集合&lt;/strong&gt;：对于待查询的集合 $Q$，首先计算其 MinHash 签名 $\text{MinHash}(Q)$。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;生成查询的 LSH 哈希值&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将查询的 MinHash 签名划分为 $b$ 个部分，并为每个部分生成哈希值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;查找候选集合&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;根据查询生成的哈希值，查找 LSH 哈希表中对应的桶，获取与查询集合 $Q$ 可能相似的候选集合。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;步骤 5：相似性计算&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;精确相似性计算&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;对于候选集合，计算其与查询集合 $Q$ 的 Jaccard 相似性（或其他相似性度量），以确认哪些集合是最相似的。这可以通过以下公式计算：
$$
J(S_j, Q) = \frac{|S_j \cap Q|}{|S_j \cup Q|}
$$&lt;/li&gt;
&lt;li&gt;根据计算的相似性得分，选出最相似的集合。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;p&gt;以上步骤概述了如何计算 MinHash LSH 的流程。通过使用 MinHash 签名和 LSH 哈希表，可以高效地进行大规模集合数据的相似性搜索，从而显著提高搜索效率，特别是在处理高维数据时。&lt;/p&gt;
</content:encoded></item><item><title>Markdown Extended Features</title><link>https://etherwindy.github.io/AstroBlog/posts/markdown-extended/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/markdown-extended/</guid><description>Read more about Markdown features in Fuwari</description><pubDate>Wed, 01 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;GitHub Repository Cards&lt;/h2&gt;
&lt;p&gt;You can add dynamic cards that link to GitHub repositories, on page load, the repository information is pulled from the GitHub API.&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;Fabrizz/MMM-OnSpotify&quot;}&lt;/p&gt;
&lt;p&gt;Create a GitHub repository card with the code &lt;code&gt;::github{repo=&quot;&amp;lt;owner&amp;gt;/&amp;lt;repo&amp;gt;&quot;}&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;::github{repo=&quot;saicaca/fuwari&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Admonitions&lt;/h2&gt;
&lt;p&gt;Following types of admonitions are supported: &lt;code&gt;note&lt;/code&gt; &lt;code&gt;tip&lt;/code&gt; &lt;code&gt;important&lt;/code&gt; &lt;code&gt;warning&lt;/code&gt; &lt;code&gt;caution&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;:::note
Highlights information that users should take into account, even when skimming.
:::&lt;/p&gt;
&lt;p&gt;:::tip
Optional information to help a user be more successful.
:::&lt;/p&gt;
&lt;p&gt;:::important
Crucial information necessary for users to succeed.
:::&lt;/p&gt;
&lt;p&gt;:::warning
Critical content demanding immediate user attention due to potential risks.
:::&lt;/p&gt;
&lt;p&gt;:::caution
Negative potential consequences of an action.
:::&lt;/p&gt;
&lt;h3&gt;Basic Syntax&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;:::note
Highlights information that users should take into account, even when skimming.
:::

:::tip
Optional information to help a user be more successful.
:::
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Custom Titles&lt;/h3&gt;
&lt;p&gt;The title of the admonition can be customized.&lt;/p&gt;
&lt;p&gt;:::note[MY CUSTOM TITLE]
This is a note with a custom title.
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::note[MY CUSTOM TITLE]
This is a note with a custom title.
:::
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;GitHub Syntax&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;a href=&quot;https://github.com/orgs/community/discussions/16925&quot;&gt;The GitHub syntax&lt;/a&gt; is also supported.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; [!NOTE]
&amp;gt; The GitHub syntax is also supported.

&amp;gt; [!TIP]
&amp;gt; The GitHub syntax is also supported.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Spoiler&lt;/h3&gt;
&lt;p&gt;You can add spoilers to your text. The text also supports &lt;strong&gt;Markdown&lt;/strong&gt; syntax.&lt;/p&gt;
&lt;p&gt;The content :spoiler[is hidden &lt;strong&gt;ayyy&lt;/strong&gt;]!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The content :spoiler[is hidden **ayyy**]!

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>接雨水</title><link>https://etherwindy.github.io/AstroBlog/posts/leetcode-collecting-rainwater/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/leetcode-collecting-rainwater/</guid><pubDate>Sat, 20 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover Image Source: &lt;a href=&quot;https://cdn.sanity.io/images/oaglaatp/production/fe299a608603b8364a6ed4e20916cd6da1c9ec74-5001x3334.jpg?w=5001&amp;amp;h=3334&amp;amp;auto=format&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;题目链接：&lt;a href=&quot;https://leetcode.cn/problems/trapping-rain-water/&quot;&gt;42. 接雨水&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;本题思路有动态规划、单调栈、双指针等，详见官方解答。这里提供一种动态规划和单调栈结合的方法。&lt;/p&gt;
&lt;h3&gt;总体思路&lt;/h3&gt;
&lt;p&gt;雨水面积可表示为&lt;/p&gt;
&lt;p&gt;$$
S_{rain} = (S_{rain} + S_{pillar}) - S_{pillar} = S_{total} - S_{pillar}.
$$&lt;/p&gt;
&lt;p&gt;$S_{pillar}$直接求和可以得出，因此关键在于如何求出$S_{total}$。下面通过动态规划解决这个问题。&lt;/p&gt;
&lt;h3&gt;动态规划&lt;/h3&gt;
&lt;p&gt;用 $dp[i]$ 表示仅存在下标在 $[0, i]$ 之中的柱子时，柱子和雨水的面积之和。考虑添加第 i+1 号柱子。如果 $[0, i]$ 中存在比第 i+1 号柱子更高的柱子，假设第 $front[i+1]$ 号柱子是这些柱子中最靠后的柱子，则&lt;/p&gt;
&lt;p&gt;$$
dp[i + 1] = dp[front[i+1]] + (i + 1 - front[i+1])height[i + 1].
$$&lt;/p&gt;
&lt;p&gt;如果不存在，则找到 $[0, i]$ 中最高的柱子，假设是第 $k_{i+1}$ 号柱子，则&lt;/p&gt;
&lt;p&gt;$$
dp[i + 1] = dp[k_{i+1}] + (i - k_{i+1})height[k_{i+1}] + height[i + 1].
$$&lt;/p&gt;
&lt;p&gt;为了区分以上两种情况，用 $front[i] = i$ 表示第 i 号柱子之前不存在更高的柱子。&lt;/p&gt;
&lt;p&gt;由此可以得出状态转移方程：&lt;/p&gt;
&lt;p&gt;$$
dp[i] = \left{
\begin{aligned}
&amp;amp; dp[i] = dp[front[i]] + (i - front[i])height[], &amp;amp; front[i] \neq i, \
&amp;amp; dp[i] = dp[k_i] + (i - k_i - 1)height[k_i] + height[i], &amp;amp; otherwise.
\end{aligned}
\right.
$$&lt;/p&gt;
&lt;p&gt;现在最大的问题是如何求出 $front[i]$ 。这里使用单调栈来求解。&lt;/p&gt;
&lt;h3&gt;单调栈&lt;/h3&gt;
&lt;p&gt;从右往左将数列中的元素加入单调递减栈，当单调性不满足时，将栈顶元素弹出直至满足单调性。每轮循环中，对于任意被弹出的元素 a ，新加入的元素 b 即为所有在 a 左侧且比 a 大的元素中距离 a 最近的元素。由此可求得 $front[\cdot]$ 数组。&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;p&gt;代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int trap(vector&amp;lt;int&amp;gt; &amp;amp;height) {
        int n = height.size();
        stack&amp;lt;int&amp;gt; mono; // monotone stack
        vector&amp;lt;int&amp;gt; front(height.size(), 0);
        vector&amp;lt;int&amp;gt; dp(height.size() + 1, 0);
        int sum = 0;  // 高度之和
        int maxIndex = 0; // 当前高度最高的柱子

        // 单调栈
        for (int i = n - 1; i &amp;gt;= 0; --i) {
            sum += height[i];
            front[i] = i;
            while (!mono.empty() &amp;amp;&amp;amp; height[mono.top()] &amp;lt; height[i]) {
                front[mono.top()] = i;
                mono.pop();
            }
            mono.push(i);
        }

        dp[0] = height[0];
        for (int i = 1; i &amp;lt; n; ++i) {
            if (front[i] &amp;lt; i)
                dp[i] = dp[front[i]] + height[i] * (i - front[i]);
            else // front[i] == i
                dp[i] = dp[maxIndex] + height[maxIndex] * (i - maxIndex - 1) + height[i];

            maxIndex = height[i] &amp;gt; height[maxIndex] ? i : maxIndex;
        }

        return dp[n - 1] - sum;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;评测结果&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;执行用时：16 ms, 在所有 C++ 提交中击败了54.90%的用户&lt;/p&gt;
&lt;p&gt;内存消耗：20.6 MB, 在所有 C++ 提交中击败了5.04%的用户&lt;/p&gt;
&lt;p&gt;通过测试用例：322 / 322&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>最长有效括号</title><link>https://etherwindy.github.io/AstroBlog/posts/leetcode-longest-valid-parentheses/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/leetcode-longest-valid-parentheses/</guid><pubDate>Sat, 20 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover Image Source: &lt;a href=&quot;https://cdn.sanity.io/images/oaglaatp/production/fe299a608603b8364a6ed4e20916cd6da1c9ec74-5001x3334.jpg?w=5001&amp;amp;h=3334&amp;amp;auto=format&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;题目链接：&lt;a href=&quot;https://leetcode.cn/problems/longest-valid-parentheses/&quot;&gt;32. 最长有效括号&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;误入歧途&lt;/h2&gt;
&lt;p&gt;本题一眼顶真，鉴定为动态规划，关键在于怎么动态规划。&lt;/p&gt;
&lt;p&gt;一开始我的想法比较暴力，用一个二维数组&lt;code&gt;f[n][n+1]&lt;/code&gt;来保存状态（实际用向量来实现）。其中&lt;code&gt;f[i][j]&lt;/code&gt;表示从$[i, j)$范围内的连续子串是否满足括号匹配规则（ $i=j$ 时表示空串，此时&lt;code&gt;f[i][j]&lt;/code&gt;值为&lt;code&gt;true&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;对于一个合法的子串，一定以&lt;code&gt;&apos;)&apos;&lt;/code&gt;结尾。故合法子串可表示为 $A(B)$ ，A和B均可为空串。对于每一个满足上式的子串，只需遍历所有的有序对 $(A, B)$ 即可判断是否是合法子串。转移方程如下：&lt;/p&gt;
&lt;p&gt;$$
f[i][j]= \left{
\begin{aligned}
&amp;amp;1, &amp;amp; \sum_{k\in[i, j-1), s[i+k]=&apos;(&apos;}f[i][k]f[i+k+1][j] \neq 0, \
&amp;amp;0, &amp;amp; otherwise.
\end{aligned}
\right.
$$&lt;/p&gt;
&lt;p&gt;代码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.length();
        int maxn = 0;
        vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; f(n, vector&amp;lt;int&amp;gt;(n + 1, 0));
        for (int i = 0; i &amp;lt; n; ++i) f[i][0] = 1;
        for (int j = 2; j &amp;lt;= n; j+=2)
            for (int i = 0; i + j &amp;lt;= n; ++i)
                if (s[i + j - 1] == &apos;)&apos;)
                    for (int k = 0; k &amp;lt; j - 1; k+=2)
                        if (s[i + k] == &apos;(&apos; &amp;amp;&amp;amp; f[i][k] &amp;amp;&amp;amp; f[i + k + 1][j - k - 2]) {
                            f[i][j] = true;
                            maxn = j;
                            break;
                        }
        return maxn;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度为$O(N^3)$。很不幸的是这个方法超时了。&lt;/p&gt;
&lt;h2&gt;另起炉灶&lt;/h2&gt;
&lt;p&gt;重新思考后，我尝试了新的算法，将时间复杂度降到了 $O(N)$ 。新的算法用一维数组&lt;code&gt;dp[n+1]&lt;/code&gt;保存状态。其中&lt;code&gt;dp[i]&lt;/code&gt;表示所有以第 $i-1$ 个字符为结尾的合法子串中最长的子串的第一个字符的下标，即下标在 $[dp[i], i)$ 中的所有字符组成一个合法子串且该子串无法向左扩充为新的子串。如果没有这样的子串，则$dp[i] = i$。&lt;/p&gt;
&lt;p&gt;沿用之前的思路，合法子串可表示为$A(B)$。当&lt;code&gt;s[i-1]&lt;/code&gt;为&lt;code&gt;&apos;)&apos;&lt;/code&gt;时，通过&lt;code&gt;dp[i-1]&lt;/code&gt;可获取$B$，若&lt;code&gt;s[dp[i-1]-1]&lt;/code&gt;为&lt;code&gt;&apos;(&apos;&lt;/code&gt;，则通过&lt;code&gt;dp[dp[i-1]-1]&lt;/code&gt;可获取$A$。转移方程如下：&lt;/p&gt;
&lt;p&gt;$$
dp[i] = \left{
\begin{aligned}
&amp;amp; dp[dp[i-1]-1], &amp;amp; s[i-1]=&apos;)&apos;,\ dp[i-1]\neq 0,\ [dp[i-1]-1]=&apos;(&apos;, \
&amp;amp; i, &amp;amp; otherwise.
\end{aligned}
\right.
$$&lt;/p&gt;
&lt;p&gt;代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.length();
        int maxn = 0;
        vector&amp;lt;int&amp;gt; dp(n + 1, 0); // dp[i]表示所有以第i-1个字符为结尾的合法子串中最长的子串的第一个字符的下标
        for (int i = 0; i &amp;lt;= n; ++i) dp[i] = i;
        for (int i = 2; i &amp;lt;= n; ++i) {
            if (s[i - 1] != &apos;)&apos;) continue;
            if (dp[i - 1] == 0 || s[dp[i - 1] - 1] != &apos;(&apos;) continue;
            dp[i] = dp[dp[i - 1] - 1];
            maxn = max(i - dp[i], maxn);
        }
        return maxn;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;评测结果&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;执行用时：4 ms, 在所有 C++ 提交中击败了77.37%的用户&lt;/p&gt;
&lt;p&gt;内存消耗：7.2 MB, 在所有 C++ 提交中击败了21.63%的用户&lt;/p&gt;
&lt;p&gt;通过测试用例：231 / 231&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Swin Transformer 论文阅读</title><link>https://etherwindy.github.io/AstroBlog/posts/swin-transformer/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/swin-transformer/</guid><description>《Swin Transformer: Hierarchical Vision Transformer using Shifted Window》阅读笔记</description><pubDate>Thu, 18 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;论文链接：&lt;a href=&quot;https://openaccess.thecvf.com/content/ICCV2021/html/Liu_Swin_Transformer_Hierarchical_Vision_Transformer_Using_Shifted_Windows_ICCV_2021_paper&quot;&gt;Swin Transformer: Hierarchical Vision Transformer using Shifted Window&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;::github{repo=&quot;microsoft/Swin-Transformer&quot;}&lt;/p&gt;
&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt;计算机视觉（Computer Vision, CV）领域长期以来被卷积神经网络（convolutional neural networks, CNNs）统治。自从 Transformer 在自然语言处理（natural language processing, NLP）领域横空出世，CV 的模型版图又焕然一新了。以 ViT 为首的 Transformer 架构模型打破了 CNN 统治 CV 的面貌。本文将介绍 ViT 之后的出现的基于 Transformer 的 CV 模型 Swin Transformer。该模型在许多方面均优于 ViT。&lt;/p&gt;
&lt;h2&gt;方法&lt;/h2&gt;
&lt;h3&gt;Overall Architecture&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/microsoft/Swin-Transformer/blob/main/figures/teaser.png?raw=true&quot; alt=&quot;Overall Architecture&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Image Source: &lt;a href=&quot;https://github.com/microsoft/Swin-Transformer/blob/main/figures/teaser.png?raw=true&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;和 ViT 一样，Swin Transformer 一开始也通过片（patch）分割模块将输入的 RGB 图像分割成不重叠的片。每个 patch 被视为一个“记号（token）”，其特征（feature）为每个像素的 RGB 原始值的连结。论文中使用大小为 $4\times4$ 的的片来分割图像，因此每个 patch 的特征的维度为 $4\times4\times3=48$。。&lt;/p&gt;
&lt;p&gt;Stage 1： 首先将原始特征上传入一个线性嵌入层（linear embedding layer），将其映射到任意维度（用 $C$ 来表示）。接着将映射后的 patch tockens 传入多个使用改良自注意力的 Transformer 块（Swin Transformer blocks）。这些 Transformer 块会保留 token 的数量（$\frac{H}{4}\times\frac{W}{4}
$）。&lt;/p&gt;
&lt;p&gt;Stage 2 ~ 4： 为了获得层级化的表示，Swin Transformer 使用片合并层（patch merging layer）使 token 数量随着网络加深而减少。第一个片合并层将每个 $2\times2$ 的邻接片进行连接得到维度为 $4C$ 的特征，接着使用一个线性层对其进行映射。最终 token 的数量减少 $2\times2=4$ 倍，输出的特征维度为 $2C$。接着将得到的特征传入 Swin Transformer blocks。阶段二到四输出的特征图分辨率分别为 $\frac{H}{8}\times\frac{W}{8}$，$\frac{H}{16}\times\frac{W}{16}$，和 $\frac{H}{32}\times\frac{W}{32}$。这三个阶段一起产生了层级化的表示，拥有和典型卷积网络（VGG 和 ResNet）相同的特征图分辨率。&lt;/p&gt;
&lt;p&gt;该结构可以方便地取代现有方法中的骨干网络，用于各种视觉任务。&lt;/p&gt;
&lt;h4&gt;Swin Transformer block&lt;/h4&gt;
&lt;p&gt;Swin Transformer 使用滑动窗口（shifted windows）来替代标准的多头自注意力（multi-head self attention, MSA）模块，其他部分与标准的 Transformer 架构相同。&lt;/p&gt;
&lt;h3&gt;Shifted Window based Self-Attention&lt;/h3&gt;
&lt;p&gt;标准的 Transformer 架构以及它的图像分类迁移版本都使用全局的自注意力，这意味着每个 token 和其他所有 token 的关系都会被计算。这种全局计算会导致 token 数的平方级别的复杂度，这使得它不适合许多需要大量标记来进行密集预测或表示高分辨率图像的视觉问题。&lt;/p&gt;
&lt;h4&gt;Self-attention in non-overlapped windows&lt;/h4&gt;
&lt;p&gt;为了提高建模效率，作者提出在局部窗口内计算自注意力。窗口被布置成以非重叠方式均匀地分割图像。假设每个窗口有 $M\times M$ 个 patch，对于一张被 分割为 $h\times w$ 个 patch 的图片而言，全局 MSA 的计算复杂度和基于窗口的计算复杂度分别为：&lt;/p&gt;
&lt;p&gt;$$
\Omega(\text{MSA})=4hwC^2+(hw)^2C,
$$&lt;/p&gt;
&lt;p&gt;$$
\Omega(\text{W-MSA})=4hwC^2+cM^2hwC,
$$&lt;/p&gt;
&lt;p&gt;其中前者是 $hw$ 的平方量级，后者对 $hw$ 是线性的。对于较大的 $hw$ 而言，全局自注意力的计算往往是负担不起的，但是基于窗口的自注意力是可行的。&lt;/p&gt;
&lt;h4&gt;Shifted window partitioning in successive blocks&lt;/h4&gt;
&lt;p&gt;基于窗口的自注意力模块缺乏窗口之间的联系，这限制了其建模能力。为了在引入跨窗口联系的同时保留不重叠窗口的高效计算能力，作者提出一种滑动的窗口分区方法，该方法在连续的 Swin Transformer blocks 中交替使用两种分区配置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;常规窗口分区：将 $8\times8$ 的特征图均匀地划分为 $2\times2$ 的大小为 $4\times4
$ （$M=4$）的窗口。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;滑动窗口分区：将上一层的窗口移动 $(\lfloor\frac{M}{2}\rfloor,\lfloor\frac{M}{2}\rfloor)$ 像素点的位置。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过这种滑动窗口的方法，连续的 Swin Transformer blocks 可以用下面的方法来计算:&lt;/p&gt;
&lt;p&gt;$$
\widehat{\bold{z}}^l=\text{W-MSA}(\text{LN}(\bold{z}^{l-1})+\bold{z}^{l-1}),
$$&lt;/p&gt;
&lt;p&gt;$$
\bold{z}^l=\text{MLP}(\text{LN}(\widehat{\bold{z}}^l))+\widehat{\text{z}}^l,
$$&lt;/p&gt;
&lt;p&gt;$$
\widehat{\bold{z}}^{l+1}=\text{SW-MSA}(\text{LN}(\bold{z}^l)+\bold{z}^l),
$$&lt;/p&gt;
&lt;p&gt;$$
\bold{z}^{l+1}=\text{MLP}(\text{LN}(\widehat{\bold{z}}^{l+1}))+\widehat{\text{z}}^{l+1},
$$&lt;/p&gt;
&lt;p&gt;其中 $\widehat{\bold{z}}^l$ 和 $\bold{z}^l$ 分别表示第 $l$ 个块中 (S)W-MSA 模块和 MLP 模块的输出特征。W-MSA 和 SW-MSA 分别表示使用常规窗口分区和滑动窗口分区的多头自注意力。&lt;/p&gt;
&lt;p&gt;移位窗口分割方法引入了前一层相邻非重叠窗口之间的联系，在图像分类、目标检测和语义分割方面效果显著。&lt;/p&gt;
&lt;h4&gt;Efficient batch computation for shifted configuration&lt;/h4&gt;
&lt;p&gt;滑动窗口分区的一个问题在于滑动会使窗口的数量从 $\lceil\frac{h}{M}\rceil\times\lceil\frac{w}{M}\rceil$ 增加到 $(\lceil\frac{h}{M}\rceil+1)\times(\lceil\frac{w}{M}\rceil+1)$，而其中一些窗口的大小会小于 $M\times M$。一个简单的解决方法是将较小的窗口填充到 $M\times M$ 的大小，并在计算注意力的时候屏蔽填充值。这回导致当窗口较小时计算量显著增加（$2\times2\rightarrow3\times3$，增加了 $2.25$ 倍）。作者提出了一种更加高效的计算方法。该方法将图片往左上角循环滑动（超出的部分补到右下方）。移动后，一个窗口可能由多个不相邻的子窗口构成。通过循环滑动，窗口数量保持不变。&lt;/p&gt;
&lt;h4&gt;Relative position bias&lt;/h4&gt;
&lt;p&gt;在计算自注意力时，Swin Transformer 在计算相似度时对每个头使用了一个相对位置偏置 $B\in\mathbb{R}^{M^2\times M^2}$：&lt;/p&gt;
&lt;p&gt;$$
\text{Attention}(Q,K,V)=\text{SoftMax}(QK^T/\sqrt{d}+B)V,
$$&lt;/p&gt;
&lt;p&gt;其中 $Q,K,V\in\mathbb{R}^{M^2\times d}$ 为 &lt;em&gt;query&lt;/em&gt; 、&lt;em&gt;key&lt;/em&gt; 和 &lt;em&gt;value&lt;/em&gt; 矩阵；$d$ 是 &lt;em&gt;query/key&lt;/em&gt; 的维度，$M^2$ 是一个窗口中 patch 的数量。因为每个像素的相对位置落在 $[-M+1,M-1]$ 区间内，作者参数化了一个更小规模的偏置矩阵 $\widehat{B}\in\mathbb{R}^{(2M-1)\times(2M-1)}$，$B$ 中的值取自 $\widehat{B}$。实验结果表明，与未使用偏置项或使用绝对位置嵌入（absolute position embedding）相比，使用相对位置偏置效果有了显著的提升，而相对位置偏置的基础上再添加绝对位置嵌入会略微降低性能。&lt;/p&gt;
</content:encoded></item><item><title>Expressive Code Example</title><link>https://etherwindy.github.io/AstroBlog/posts/expressive-code/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/expressive-code/</guid><description>How code blocks look in Markdown using Expressive Code.</description><pubDate>Wed, 10 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here, we&apos;ll explore how code blocks look using &lt;a href=&quot;https://expressive-code.com/&quot;&gt;Expressive Code&lt;/a&gt;. The provided examples are based on the official documentation, which you can refer to for further details.&lt;/p&gt;
&lt;h2&gt;Expressive Code&lt;/h2&gt;
&lt;h3&gt;Syntax Highlighting&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/syntax-highlighting/&quot;&gt;Syntax Highlighting&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Regular syntax highlighting&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;This code is syntax highlighted!&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Rendering ANSI escape sequences&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ANSI colors:
- Regular: [31mRed[0m [32mGreen[0m [33mYellow[0m [34mBlue[0m [35mMagenta[0m [36mCyan[0m
- Bold:    [1;31mRed[0m [1;32mGreen[0m [1;33mYellow[0m [1;34mBlue[0m [1;35mMagenta[0m [1;36mCyan[0m
- Dimmed:  [2;31mRed[0m [2;32mGreen[0m [2;33mYellow[0m [2;34mBlue[0m [2;35mMagenta[0m [2;36mCyan[0m

256 colors (showing colors 160-177):
[38;5;160m160 [38;5;161m161 [38;5;162m162 [38;5;163m163 [38;5;164m164 [38;5;165m165[0m
[38;5;166m166 [38;5;167m167 [38;5;168m168 [38;5;169m169 [38;5;170m170 [38;5;171m171[0m
[38;5;172m172 [38;5;173m173 [38;5;174m174 [38;5;175m175 [38;5;176m176 [38;5;177m177[0m

Full RGB colors:
[38;2;34;139;34mForestGreen - RGB(34, 139, 34)[0m

Text formatting: [1mBold[0m [2mDimmed[0m [3mItalic[0m [4mUnderline[0m
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Editor &amp;amp; Terminal Frames&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/frames/&quot;&gt;Editor &amp;amp; Terminal Frames&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Code editor frames&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;Title attribute example&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- src/content/index.html --&amp;gt;
&amp;lt;div&amp;gt;File name comment example&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Terminal frames&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;This terminal frame has no title&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;Write-Output &quot;This one has a title!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Overriding frame types&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Look ma, no frame!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;# Without overriding, this would be a terminal frame
function Watch-Tail { Get-Content -Tail 20 -Wait $args }
New-Alias tail Watch-Tail
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Text &amp;amp; Line Markers&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/text-markers/&quot;&gt;Text &amp;amp; Line Markers&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Marking full lines &amp;amp; line ranges&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Line 1 - targeted by line number
// Line 2
// Line 3
// Line 4 - targeted by line number
// Line 5
// Line 6
// Line 7 - targeted by range &quot;7-8&quot;
// Line 8 - targeted by range &quot;7-8&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Selecting line marker types (mark, ins, del)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  console.log(&apos;this line is marked as deleted&apos;)
  // This line and the next one are marked as inserted
  console.log(&apos;this is the second inserted line&apos;)

  return &apos;this line uses the neutral default marker type&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Adding labels to line markers&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// labeled-line-markers.jsx
&amp;lt;button
  role=&quot;button&quot;
  {...props}
  value={value}
  className={buttonClassName}
  disabled={disabled}
  active={active}
&amp;gt;
  {children &amp;amp;&amp;amp;
    !active &amp;amp;&amp;amp;
    (typeof children === &apos;string&apos; ? &amp;lt;span&amp;gt;{children}&amp;lt;/span&amp;gt; : children)}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Adding long labels on their own lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// labeled-line-markers.jsx
&amp;lt;button
  role=&quot;button&quot;
  {...props}

  value={value}
  className={buttonClassName}

  disabled={disabled}
  active={active}
&amp;gt;

  {children &amp;amp;&amp;amp;
    !active &amp;amp;&amp;amp;
    (typeof children === &apos;string&apos; ? &amp;lt;span&amp;gt;{children}&amp;lt;/span&amp;gt; : children)}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Using diff-like syntax&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;+this line will be marked as inserted
-this line will be marked as deleted
this is a regular line
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+this is an actual diff file
-all contents will remain unmodified
 no whitespace will be removed either
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Combining syntax highlighting with diff-like syntax&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  function thisIsJavaScript() {
    // This entire block gets highlighted as JavaScript,
    // and we can still add diff markers to it!
-   console.log(&apos;Old code to be removed&apos;)
+   console.log(&apos;New and shiny code!&apos;)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Marking individual text inside lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  // Mark any given text inside lines
  return &apos;Multiple matches of the given text are supported&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Regular expressions&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;The words yes and yep will be marked.&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Escaping forward slashes&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Test&quot; &amp;gt; /home/test.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Selecting inline marker types (mark, ins, del)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  console.log(&apos;These are inserted and deleted marker types&apos;);
  // The return statement uses the default marker type
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Word Wrap&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/word-wrap/&quot;&gt;Word Wrap&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Configuring word wrap per block&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Example with wrap
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Example with wrap=false
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Configuring indentation of wrapped lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Example with preserveIndent (enabled by default)
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Example with preserveIndent=false
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Collapsible Sections&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/plugins/collapsible-sections/&quot;&gt;Collapsible Sections&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// All this boilerplate setup code will be collapsed
import { someBoilerplateEngine } from &apos;@example/some-boilerplate&apos;
import { evenMoreBoilerplate } from &apos;@example/even-more-boilerplate&apos;

const engine = someBoilerplateEngine(evenMoreBoilerplate())

// This part of the code will be visible by default
engine.doSomething(1, 2, 3, calcFn)

function calcFn() {
  // You can have multiple collapsed sections
  const a = 1
  const b = 2
  const c = a + b

  // This will remain visible
  console.log(`Calculation result: ${a} + ${b} = ${c}`)
  return c
}

// All this code until the end of the block will be collapsed again
engine.closeConnection()
engine.freeMemory()
engine.shutdown({ reason: &apos;End of example boilerplate code&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Line Numbers&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/plugins/line-numbers/&quot;&gt;Line Numbers&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Displaying line numbers per block&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// This code block will show line numbers
console.log(&apos;Greetings from line 2!&apos;)
console.log(&apos;I am on line 3&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Line numbers are disabled for this block
console.log(&apos;Hello?&apos;)
console.log(&apos;Sorry, do you know what line I am on?&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Changing the starting line number&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;Greetings from line 5!&apos;)
console.log(&apos;I am on line 6&apos;)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Simple Guides for Fuwari</title><link>https://etherwindy.github.io/AstroBlog/posts/guide/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/guide/</guid><description>How to use this blog template.</description><pubDate>Mon, 01 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover image source: &lt;a href=&quot;https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/208fc754-890d-4adb-9753-2c963332675d/width=2048/01651-1456859105-(colour_1.5),girl,_Blue,yellow,green,cyan,purple,red,pink,_best,8k,UHD,masterpiece,male%20focus,%201boy,gloves,%20ponytail,%20long%20hair,.jpeg&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This blog template is built with &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;. For the things that are not mentioned in this guide, you may find the answers in the &lt;a href=&quot;https://docs.astro.build/&quot;&gt;Astro Docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Front-matter of Posts&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;---
title: My First Blog Post
published: 2023-09-09
description: This is the first post of my new Astro blog.
image: ./cover.jpg
tags: [Foo, Bar]
category: Front-end
draft: false
---
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The title of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;published&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The date the post was published.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A short description of the post. Displayed on index page.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The cover image path of the post.&amp;lt;br/&amp;gt;1. Start with &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;: Use web image&amp;lt;br/&amp;gt;2. Start with &lt;code&gt;/&lt;/code&gt;: For image in &lt;code&gt;public&lt;/code&gt; dir&amp;lt;br/&amp;gt;3. With none of the prefixes: Relative to the markdown file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The tags of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The category of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;draft&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If this post is still a draft, which won&apos;t be displayed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Where to Place the Post Files&lt;/h2&gt;
&lt;p&gt;Your post files should be placed in &lt;code&gt;src/content/posts/&lt;/code&gt; directory. You can also create sub-directories to better organize your posts and assets.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/content/posts/
├── post-1.md
└── post-2/
    ├── cover.png
    └── index.md
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Markdown Example</title><link>https://etherwindy.github.io/AstroBlog/posts/markdown/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/markdown/</guid><description>A simple example of a Markdown blog post.</description><pubDate>Sun, 01 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;An h1 header&lt;/h1&gt;
&lt;p&gt;Paragraphs are separated by a blank line.&lt;/p&gt;
&lt;p&gt;2nd paragraph. &lt;em&gt;Italic&lt;/em&gt;, &lt;strong&gt;bold&lt;/strong&gt;, and &lt;code&gt;monospace&lt;/code&gt;. Itemized lists
look like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this one&lt;/li&gt;
&lt;li&gt;that one&lt;/li&gt;
&lt;li&gt;the other one&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that --- not considering the asterisk --- the actual text
content starts at 4-columns in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Block quotes are
written like so.&lt;/p&gt;
&lt;p&gt;They can span multiple paragraphs,
if you like.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., &quot;it&apos;s all
in chapters 12--14&quot;). Three dots ... will be converted to an ellipsis.
Unicode is supported. ☺&lt;/p&gt;
&lt;h2&gt;An h2 header&lt;/h2&gt;
&lt;p&gt;Here&apos;s a numbered list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;first item&lt;/li&gt;
&lt;li&gt;second item&lt;/li&gt;
&lt;li&gt;third item&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note again how the actual text starts at 4 columns in (4 characters
from the left side). Here&apos;s a code sample:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Let me re-iterate ...
for i in 1 .. 10 { do-something(i) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you probably guessed, indented 4 spaces. By the way, instead of
indenting the block, you can use delimited blocks, if you like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;define foobar() {
    print &quot;Welcome to flavor country!&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(which makes copying &amp;amp; pasting easier). You can optionally mark the
delimited block for Pandoc to syntax highlight it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import time
# Quick, count to ten!
for i in range(10):
    # (but not *too* quick)
    time.sleep(0.5)
    print i
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;An h3 header&lt;/h3&gt;
&lt;p&gt;Now a nested list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, get these ingredients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;carrots&lt;/li&gt;
&lt;li&gt;celery&lt;/li&gt;
&lt;li&gt;lentils&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Boil some water.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dump everything in the pot and follow
this algorithm:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; find wooden spoon
 uncover pot
 stir
 cover pot
 balance wooden spoon precariously on pot handle
 wait 10 minutes
 goto first step (or shut off burner when done)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do not bump wooden spoon or it will fall.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice again how text always lines up on 4-space indents (including
that last line which continues item 3 above).&lt;/p&gt;
&lt;p&gt;Here&apos;s a link to &lt;a href=&quot;http://foo.bar&quot;&gt;a website&lt;/a&gt;, to a &lt;a href=&quot;local-doc.html&quot;&gt;local
doc&lt;/a&gt;, and to a &lt;a href=&quot;#an-h2-header&quot;&gt;section heading in the current
doc&lt;/a&gt;. Here&apos;s a footnote [^1].&lt;/p&gt;
&lt;p&gt;[^1]: Footnote text goes here.&lt;/p&gt;
&lt;p&gt;Tables can look like this:&lt;/p&gt;
&lt;p&gt;size material color&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;9 leather brown
10 hemp canvas natural
11 glass transparent&lt;/p&gt;
&lt;p&gt;Table: Shoes, their sizes, and what they&apos;re made of&lt;/p&gt;
&lt;p&gt;(The above is the caption for the table.) Pandoc also supports
multi-line tables:&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;keyword text&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;red Sunsets, apples, and
other red or reddish
things.&lt;/p&gt;
&lt;p&gt;green Leaves, grass, frogs
and other things it&apos;s
not easy being.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;A horizontal rule follows.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Here&apos;s a definition list:&lt;/p&gt;
&lt;p&gt;apples
: Good for making applesauce.
oranges
: Citrus!
tomatoes
: There&apos;s no &quot;e&quot; in tomatoe.&lt;/p&gt;
&lt;p&gt;Again, text is indented 4 spaces. (Put a blank line between each
term/definition pair to spread things out more.)&lt;/p&gt;
&lt;p&gt;Here&apos;s a &quot;line block&quot;:&lt;/p&gt;
&lt;p&gt;| Line one
| Line too
| Line tree&lt;/p&gt;
&lt;p&gt;and images can be specified like so:&lt;/p&gt;
&lt;p&gt;Inline math equations go in like so: $\omega = d\phi / dt$. Display
math should get its own line and be put in in double-dollarsigns:&lt;/p&gt;
&lt;p&gt;$$I = \int \rho R^{2} dV$$&lt;/p&gt;
&lt;p&gt;$$
\begin{equation*}
\pi
=3.1415926535
;8979323846;2643383279;5028841971;6939937510;5820974944
;5923078164;0628620899;8628034825;3421170679;\ldots
\end{equation*}
$$&lt;/p&gt;
&lt;p&gt;And note that you can backslash-escape any punctuation characters
which you wish to be displayed literally, ex.: `foo`, *bar*, etc.&lt;/p&gt;
</content:encoded></item><item><title>Include Video in the Posts</title><link>https://etherwindy.github.io/AstroBlog/posts/video/</link><guid isPermaLink="true">https://etherwindy.github.io/AstroBlog/posts/video/</guid><description>This post demonstrates how to include embedded video in a blog post.</description><pubDate>Tue, 01 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just copy the embed code from YouTube or other platforms, and paste it in the markdown file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: Include Video in the Post
published: 2023-10-19
// ...
---

&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;YouTube&lt;/h2&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;h2&gt;Bilibili&lt;/h2&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;//player.bilibili.com/player.html?bvid=BV1fK4y1s7Qf&amp;amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&amp;gt; &amp;lt;/iframe&amp;gt;&lt;/p&gt;
</content:encoded></item></channel></rss>