From dbaf0e7ddf003801002e27f46201f1d6bb88945e Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Fri, 13 Jun 2025 11:28:06 +0800 Subject: [PATCH 01/14] =?UTF-8?q?=E7=9F=A5=E4=B9=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../html/recognizer/cc_math/tag_script.py | 32 + .../extractor/html/recognizer/ccmath.py | 5 +- .../good_data/html/math_zhihu_custom_tag.html | 4286 +++++++++++++++++ .../good_data/html_data_input.jsonl | 1 + .../extractor/test_extractor_chain.py | 20 +- .../zhihucase1/output_zhihu_custom_tagV1.md | 392 ++ .../output_zhihu_custom_tag_inline.md | 328 ++ .../output_zhihu_custom_tag_interline.md | 384 ++ .../zhihucase1/output_zhihu_custom_tag_ori.md | 360 ++ .../zhihucase1/output_zhihu_custom_tag_v2.md | 328 ++ 11 files changed, 6136 insertions(+), 2 deletions(-) create mode 100644 tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html/math_zhihu_custom_tag.html create mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tagV1.md create mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_inline.md create mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_interline.md create mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_ori.md create mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_v2.md diff --git a/.gitignore b/.gitignore index f5183022..eb56537a 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ coverage.xml llm_web_kit.egg-info/* .llm-web-kit.jsonc .llm-web-kit-pageclassify.jsonc + +/tests/llm_web_kit/extractor/*.md diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py index 77060697..3fc0223e 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py @@ -88,3 +88,35 @@ def process_katex_mathml(cm, math_render, node): html=None ) replace_element(node, new_span) + + +def process_zhihu_custom_tag(cm, math_render, node): + """知乎公式逻辑比较特殊:所有公式都是行内公式,通过双斜杠进行换行,md渲染时需要begin{aligned}和end{aligned}包裹.""" + try: + # 从data-tex属性获取LaTeX公式 + latex = node.get('data-tex') + if latex: + if r'\\' in latex: + # 检查是否已经被\begin{*}和\end{*}包裹 + is_already_aligned = re.search(r'\\begin\{[^}]+\}.*\\end\{[^}]+\}', latex, re.DOTALL) is not None + if not is_already_aligned: + lines = re.split(r'\\\\[\s]*', latex) + # 为每一行添加 & 符号使其左对齐(匹配知乎逻辑) + formatted_lines = [] + for line in lines: + line = line.strip() + if not line.startswith('&'): + line = '&' + line + formatted_lines.append(line) + latex = r'\begin{aligned} ' + r' \\ '.join(formatted_lines) + r' \end{aligned}' + new_span = build_cc_element( + html_tag_name='ccmath-inline', + text=cm.wrap_math_md(latex), + tail=text_strip(node.tail), + type='latex', + by=math_render, + html=element_to_html(node) + ) + replace_element(node, new_span) + except Exception as e: + raise HtmlMathRecognizerException(f'处理知乎数学公式失败: {e}') diff --git a/llm_web_kit/extractor/html/recognizer/ccmath.py b/llm_web_kit/extractor/html/recognizer/ccmath.py index 4bc94228..8c49bc44 100644 --- a/llm_web_kit/extractor/html/recognizer/ccmath.py +++ b/llm_web_kit/extractor/html/recognizer/ccmath.py @@ -133,9 +133,12 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe parent = node.getparent() # 针对csdn博客中的katex-mathml标签,提取latex公式 - if node.tag == 'span' and node.get('class') == 'katex-mathml' and 'blog.csdn.net' in self.cm.url: + if 'blog.csdn.net' in self.cm.url and node.tag == 'span' and node.get('class') == 'katex-mathml': tag_script.process_katex_mathml(self.cm, math_render_type, node) + if 'zhuanlan.zhihu.com' in self.cm.url and node.tag == 'span' and node.get('class') == 'ztext-math': + tag_script.process_zhihu_custom_tag(self.cm, math_render_type, node) + # tag = span, class 为 math-containerm, 或者 mathjax 或者 wp-katex-eq if node.tag == 'span' and node.get('class') and ( 'math-container' in node.get('class') or diff --git a/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html/math_zhihu_custom_tag.html b/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html/math_zhihu_custom_tag.html new file mode 100644 index 00000000..8ff808e2 --- /dev/null +++ b/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html/math_zhihu_custom_tag.html @@ -0,0 +1,4286 @@ + + + + + 强化学习(RL)(从入门到PPO)(一、基础部分) - 知乎 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+
+
+
+ + +
+ +
+ +
+
+
+
+
+
+

+ 强化学习(RL)(从入门到PPO)(一、基础部分)

+ + +
+ + +
+ + + + + + + + + +
+ +
来自专栏 · 强化学习
+
+ + + + +
+
+ +
+
+ +
+ +
+ + + + +

开始前的碎碎念

+

+ 强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。

+

感性认识

+

+ 首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识:

+
    +
  • + 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 +
  • +
  • 强化学习是个很抽象的系统内容,有on policy和off + policy,online和offline,model base和model + free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 +
  • +
  • + 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human + feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 +
  • +
  • LLM预训练和监督微调可以看做是单步的off line + 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off + line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on + line和on + policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 +
  • +
+

直观认识

+

示意图:

+
+
+
+

智能体有3种关键要素,即感知、决策和奖励

+
    +
  • + 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 +
  • +
  • + 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 +
  • +
  • + 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 +
  • +
+

+ 强化学习的目标:在当前状态下找到一个最优策略到达目标状态。

+

马尔科夫决策过程

+

State

+

智能体当前所处的状态

+
+

Action

+

智能体在某个状态下可能执行的动作

+
+

State transition

+

+ 智能体在某个状态下执行某个动作会进入到新的状态

+

表格形式描述状态转移矩阵:

+
+
+

状态转移概率(总和为1),示意:

+

\begin{gathered} p(s_{1}|s_{1},a_{2})=0, \\ p(s_{2}|s_{1},a_{2})=1, \\ p(s_{3}|s_{1},a_{2})=0, \\ p(s_{4}|s_{1},a_{2})=0, \\ p(s_{5}|s_{1},a_{2})=0, \end{gathered} +

+

Policy

+

+ 智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略

+
    +
  • 确定性策略
  • +
+
+
+

\begin{gathered} \pi(a_1|s_1)=0, \\ \pi(a_{2}|s_{1})=1, \\ \pi(a_{3}|s_{1})=0, \\ \pi(a_{4}|s_{1})=0, \\ \pi(a_{5}|s_{1})=0, \end{gathered} +

+
    +
  • 不确定性策略
  • +
+
+

表格形式:

+
+
+

Reward

+

智能体在某个状态下执行动作的奖惩值

+

表格形式:

+
+
+

Trajectories, returns, and episodes

+
+
+
    +
  • + trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 +
  • +
  • + returns(回报):智能体在trajectory上所有的reward总和。 +
  • +
  • episodes:trajectory的一部分。
  • +
+

马尔科夫链描述trajectory:

+

s_1\xrightarrow{a_2}s_2\xrightarrow{a_3}s_5\xrightarrow{a_3}s_8\xrightarrow{a_2}s_9. +

+

returns:

+

\mathrm{return}=0+0+0+1=1. +

+

即时收益:

+

当前状态执行某个动作马上会获得的收益(奖惩值)

+

未来收益:

+

+ 当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和

+

s_1\overset{a_2}{\operatorname*{\longrightarrow}}s_2\overset{a_3}{\operatorname*{\longrightarrow}}s_5\overset{a_3}{\operatorname*{\longrightarrow}}s_8\overset{a_2}{\operatorname*{\longrightarrow}}s_9\overset{a_5}{\operatorname*{\longrightarrow}}s_9\overset{a_5}{\operatorname*{\longrightarrow}}s_9\ldots +

+
+ s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 +
+

+ trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ + ∈ (0, 1)。

+

引入衰减因子的return叫做disconted return。 +

+

\mathrm{return}=0+0+0+1+1+1+\cdots=\infty, +

+

\text{discounted return}=0+\gamma0+\gamma^20+\gamma^31+\gamma^41+\gamma^51+\ldots, +

+

\text{discounted return}=\gamma^3(1+\gamma+\gamma^2+\ldots)=\gamma^3\frac{1}{1-\gamma}. +

+

+ 注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 +

+

State Space

+

状态空间:所有可能的状态,一个集合,可能无穷

+

Action Space

+

+ 动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷

+

+ 注意:动作空间依赖于状态,每个状态下动作空间不一定相同

+

马尔科夫决策过程

+

符号描述

+
    +
  • 集合:
  • +
      +
    • S:状态空间,大写,集合
    • +
    • + A(s):状态S下的动作空间,A大写,s小写,A是集合 +
    • +
    • R(s, + a):状态S下执行动作a的奖励,R大写,s,a小写,R是集合 +
    • +
    +
  • 模型:
  • +
      +
    • 状态转移概率:
    • +
    +
+

\begin{aligned} & p(s^{\prime}|s,a) \\ & \sum_{s^{\prime}\in\mathcal{S}}p(s^{\prime}|s,a)=1\text{ for any }(s,a). \end{aligned} +

+
    +
  • 奖励概率:
  • +
+

\begin{aligned} & p(r|s,a) \\ & \sum_{r\in\mathcal{R}(s,a)}p(r|s,a)=1\text{ for any }(s,a). \end{aligned} +

+
    +
  • 策略:
  • +
      +
    • 在s状态下执行a的概率
    • +
    +
+

\begin{aligned} \pi(a|s) \\ \sum_{a\in\mathcal{A}(s)}\pi(a|s) & =1\text{ for any }s\in\mathcal{S}. \end{aligned} +

+

马尔科夫决策过程:

+

马尔可夫决策过程由元组(S,A,P,r,γ)组成

+

s_1\xrightarrow{a_2}s_2\xrightarrow{a_3}s_5\xrightarrow{a_3}s_8\xrightarrow{a_2}s_9\xrightarrow{a_5}s_9\xrightarrow{a_5}s_9\ldots +

+

马尔科夫奖励过程/马尔科夫过程:

+

s_1 \xrightarrow[r=0]{a_2} s_2 \xrightarrow[r=0]{a_3} s_5 \xrightarrow[r=0]{a_3} s_8 \xrightarrow[r=1]{a_2} s_9 \xrightarrow[r=1]{a_5} s_9 \xrightarrow[r=1]{a_5} s_9 \ldots +

+

+ 未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 +

+

价值函数

+

价值函数分为两种:状态价值以及动作价值

+

状态价值:从当前状态到目标状态的所有trajectory的return的期望。 +

+

动作价值:当前状态及动作之后所有trajectory的return的期望。 +

+

回顾returns:

+

G_t=R_t+\gamma R_{t+1}+\gamma^2R_{t+2}+\cdots=\sum_{k=0}^\infty\gamma^kR_{t+k} +

+

价值函数的数学符号的展开表示:

+

\begin{aligned} V(s) & =\mathbb{E}[G_t|S_t=s] \\ & =\mathbb{E}[R_t+\gamma R_{t+1}+\gamma^2R_{t+2}+\ldots|S_t=s] \\ & =\mathbb{E}[R_t+\gamma(R_{t+1}+\gamma R_{t+2}+\ldots)|S_t=s] \\ & =\mathbb{E}[R_t+\gamma G_{t+1}|S_t=s] \\ & =\mathbb{E}[R_t+\gamma V(S_{t+1})|S_t=s] \end{aligned} +

+

价值函数的转移概率形式:

+

V(s)=r(s)+\gamma\sum_{s^{\prime}\in S}p(s^{\prime}|s)V(s^{\prime}) +

+

价值函数的矩阵展开形式:

+

\begin{aligned} \mathcal{V}=\mathcal{R}+\gamma\mathcal{PV} \\ \begin{bmatrix} V(s_1) \\ V(s_2) \\ \cdots \\ V(s_n) \end{bmatrix}= \begin{bmatrix} r(s_1) \\ r(s_2) \\ \cdots \\ r(s_n) \end{bmatrix}+\gamma \begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \ldots & P(s_n|s_2) \\ \cdots \\ P(s_1|s_n) & P(s_2|s_n) & \ldots & P(s_n|s_n) \end{bmatrix} \begin{bmatrix} V(s_1) \\ V(s_2) \\ \cdots \\ V(s_n) \end{bmatrix} \end{aligned} +

+

以上都是贝尔曼公式

+

注意:

+
    +
  • + 贝尔曼公式不是唯一的,存在非常多种表现形式
  • +
  • + 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 +
  • +
+

状态价值函数

+

基于某个策略的价值函数:

+

V^\pi(s)=\mathbb{E}_\pi[G_t|S_t=s] +

+

动作价值函数

+

在一个状态下执行动作a的期望价值函数:

+

Q^\pi(s,a)=\mathbb{E}_\pi[G_t|S_t=s,A_t=a] +

+

状态价值与动作价值之间的关系

+

数学上表示:

+

+ 直观理解:当前状态的状态价值是动作价值的期望

+

+ 注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 +

+

V^\pi(s) = \sum_{a \in A} \pi(a|s)Q^\pi(s,a) +

+

Q^\pi(s,a) = r(s,a) + \gamma \sum_{s' \in S} P(s'|s,a)V^\pi(s') +

+

带策略的贝尔曼方程:

+

V^\pi(s) = \mathbb{E}_\pi[R_t + \gamma V^\pi(S_{t+1})|S_t = s] \\ = \sum_{a \in A} \pi(a|s) \left(r(s,a) + \gamma \sum_{s' \in S} p(s'|s,a)V^\pi(s')\right) \\ Q^\pi(s,a) = \mathbb{E}_\pi[R_t + \gamma Q^\pi(S_{t+1}, A_{t+1})|S_t = s, A_t = a] \\ = r(s,a) + \gamma \sum_{s' \in S} p(s'|s,a) \sum_{a' \in A} \pi(a'|s')Q^\pi(s',a') +

+

注意:

+
    +
  • 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 +
  • +
  • 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 +
  • +
  • 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 +
  • +
  • + 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。
  • +
+


+

下面:如何更新策略,或者如何计算贝尔曼公式

+

计算贝尔曼公式

+

+ 直接对矩阵求逆,实际使用起来不现实,这里不展示

+

实际中是使用迭代算法进行求解,分为两种,值迭代策略迭代

+

迭代算法有个基础直观认识:只要不断的提升State + Value,最终会收敛到最优的State Value

+

在此之前介绍贝尔曼最优公式: \text{For every } s \in \mathcal{S}, \text{ the elementwise expression of the BOE is} \\ v(s) = \max_{\pi(s) \in \Pi(s)} \sum_{a \in A} \pi(a|s) \left(\sum_{r \in \mathcal{R}} p(r|s,a)r + \gamma \sum_{s' \in \mathcal{S}} p(s'|s,a)v(s')\right) \\ = \max_{\pi(s) \in \Pi(s)} \sum_{a \in A} \pi(a|s)q(s,a) +

+

贝尔曼最优公式的作用:寻找策略提升点

+

+ 直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all + in最大的a,所有最优贝尔曼公式多了max。

+

注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 +

+

+ 如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。

+

以下两种迭代算法介绍都是model + base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model + base的场景。

+

值迭代

+

直观理解:为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State + Value,通过当前所有动作的即时收益获取到最优的Action + Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State + Value收敛。

+

伪代码:

+
+
+

值迭代示例:

+
+
+

\diamond \; k = 0: \\ \text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\[1em] v_1(s_1) = 0, \quad v_1(s_2) = 1, \quad v_1(s_3) = 1, \quad v_1(s_4) = 1. \\[1em] \diamond \; k = 1: \\ \textit{q-value calculation}: \text{ Substituting } v_1(s_i) \text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\[0.5em] \textit{Policy update}: \pi_2 \text{ is obtained by selecting the greatest q-values:} \\[0.5em] \pi_2(a_3|s_1) = 1, \quad \pi_2(a_3|s_2) = 1, \quad \pi_2(a_2|s_3) = 1, \quad \pi_2(a_5|s_4) = 1. \\[0.5em] \text{This policy is visualized in Figure 4.2 (the right subfigure).} \\[0.5em] \textit{Value update}: v_2 \text{ is obtained by updating the v-value to the greatest q-value for each state:} \\[0.5em] v_2(s_1) = \gamma1, \quad v_2(s_2) = 1 + \gamma1, \quad v_2(s_3) = 1 + \gamma1, \quad v_2(s_4) = 1 + \gamma1. \\[1em] \diamond \; k = 2,3,4,\ldots +

+

策略迭代

+

策略迭代分为两个部分:策略评估和策略提升

+

直观上:首先初始化一个策略和State + Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action + Value,得到新的策略,重复执行,不断迭代…。

+

策略评估是计算一个策略的状态价值函数

+

策略提升是选择每个状态在当前策略下即时收益最大的Action + Value

+

注:策略提升是找到了策略提升点,即保证更新后的State + Value不低于旧策略的State Value

+

策略提升定理的证明:

+

V^\pi(s) \leq Q^\pi(s,\pi'(s)) \\ = \mathbb{E}_{\pi'}[R_t + \gamma V^\pi(S_{t+1})|S_t = s] \\ \leq \mathbb{E}_{\pi'}[R_t + \gamma Q^\pi(S_{t+1},\pi'(S_{t+1}))|S_t = s] \\ = \mathbb{E}_{\pi'}[R_t + \gamma R_{t+1} + \gamma^2 V^\pi(S_{t+2})|S_t = s] \\ \leq \mathbb{E}_{\pi'}[R_t + \gamma R_{t+1} + \gamma^2 R_{t+2} + \gamma^3 V^\pi(S_{t+3})|S_t = s] \\ \vdots \\ \leq \mathbb{E}_{\pi'}[R_t + \gamma R_{t+1} + \gamma^2 R_{t+2} + \gamma^3 R_{t+3} + \cdots|S_t = s] \\ = V^{\pi'}(s) +

+

策略提升定理保证了新策略的State + Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。

+

直观步骤示意:

+

\pi^0 \xrightarrow[\text{策略评估}]{} V^{\pi^0} \xrightarrow[\text{策略提升}]{} \pi^1 \xrightarrow[\text{策略评估}]{} V^{\pi^1} \xrightarrow[\text{策略提升}]{} \pi^2 \xrightarrow[\text{策略评估}]{} \cdots \xrightarrow[\text{策略提升}]{} \pi^* +

+

策略迭代伪代码:

+
+
+

策略迭代简单示例:

+
+
+

初始条件:

+
+
+

1.策略评估:

+
+
+

2.策略提升:

+
+
+

+ 注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 +

+

上面两种算法本质上是动态规划算法

+

+ 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。

+

V(s)=r(s)+\gamma\sum_{s^{\prime}\in S}p(s^{\prime}|s)V(s^{\prime}) +

+

在实际场景应用中,会存在以下问题:

+
    +
  • 环境有多少状态未知
  • +
  • 当前状态执行某个动作进入每种状态的概率未知 +
  • +
  • 状态可能是无穷的
  • +
  • 动作可能是无穷的
  • +
  • 迭代更新策略是离散的,但某些场景是连续的。 +
  • +
  • 等等,一大堆
  • +
+

一般来说model + base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。

+

+ 以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model + base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model + free。

+

参考资料:

+

书籍:

+ +

视频:

+ +
+
+
+
+
+
+ +
+ +
还没有人送礼物,鼓励一下作者吧
+ +
+
编辑于 2025-04-22 18:07 + ・北京 +
+
+
+ + + + +
+
+
+
+
+ +
+ +
+ + + +
+
+ +
+
+
+
+
+
+
+
+ +
+ + +
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html_data_input.jsonl b/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html_data_input.jsonl index 86da0703..93b075e8 100644 --- a/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html_data_input.jsonl +++ b/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html_data_input.jsonl @@ -88,3 +88,4 @@ {"track_id": "test_math_namespace", "dataset_name": "test_math_namespace", "url": "https://www.mdpi.com/1424-8220/14/3/5536","data_source_category": "HTML", "path":"math_miss_namespace.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}} {"track_id": "test_math_paragraph", "dataset_name": "test_math_paragraph", "url": "https://worldscientific.com/author/Wang%2C+Zhaojie?ConceptID=130214&ConceptID=130213&startPage=&ContribRaw=Zhang%2C+Jinbao","data_source_category": "HTML", "path":"math_extra_line_breaks.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}} {"track_id": "test_csnd_none_formular", "dataset_name": "test_csnd_none_formular", "url": "https://blog.csdn.net/m0_66248056/article/details/142639406?ops_request_misc=%257B%2522request%255Fid%2522%253A%25221c25a5e5a991ed02124823f82305fd4c%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=1c25a5e5a991ed02124823f82305fd4c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-142639406-null-null.142^v102^pc_search_result_base4&utm_term=%E5%BE%AE%E7%A7%AF%E5%88%86&spm=1018.2226.3001.4187","data_source_category": "HTML", "path":"math_csdn_none_formula.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}} +{"track_id": "test_zhihu_custom_tag", "dataset_name": "test_zhihu_custom_tag", "url": "https://zhuanlan.zhihu.com/p/22925478480","data_source_category": "HTML", "path":"math_zhihu_custom_tag.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}} \ No newline at end of file diff --git a/tests/llm_web_kit/extractor/test_extractor_chain.py b/tests/llm_web_kit/extractor/test_extractor_chain.py index b7d481d3..2d9c421c 100644 --- a/tests/llm_web_kit/extractor/test_extractor_chain.py +++ b/tests/llm_web_kit/extractor/test_extractor_chain.py @@ -63,7 +63,7 @@ def setUp(self): continue self.data_json.append(json.loads(line)) - assert len(self.data_json) == 90 + assert len(self.data_json) == 91 # Config for HTML extraction self.config = load_pipe_tpl('html-test') @@ -763,3 +763,21 @@ def test_csnd_none_formula(self): # print('Markdown Content:', md_content) self.assertIn(r'$\lim\limits_{x \to 1}\dfrac{x^2-1}{x-1}$', md_content) self.assertIn(r'\begin{aligned} \frac{f(1.01)-f(1)}{1.01-1} &= \frac{1.01^2-1^2}{0.01} \\ &= \frac{0.0201}{0.01} \\ &= 2.01\end{aligned}', md_content) + + def test_zhihu_custom_tag(self): + """测试知乎自定义标签.""" + chain = ExtractSimpleFactory.create(self.config) + self.assertIsNotNone(chain) + test_data = self.data_json[90] + self.assertIn('zhuanlan.zhihu.com', test_data['url']) + # Create DataJson from test data + input_data = DataJson(test_data) + result = chain.extract(input_data) + md_content = result.get_content_list().to_nlp_md() + with open('output_zhihu_custom_tag_inline1.md', 'w', encoding='utf-8') as f: + f.write(md_content) + + # print('Markdown Content:', md_content) + self.assertIn(r'$\begin{gathered} p(s_{1}|s_{1},a_{2})=0, \\ p(s_{2}|s_{1},a_{2})=1, \\ p(s_{3}|s_{1},a_{2})=0, \\ p(s_{4}|s_{1},a_{2})=0, \\ p(s_{5}|s_{1},a_{2})=0, \end{gathered}$', md_content) + self.assertIn(r'$s_1\xrightarrow{a_2}s_2\xrightarrow{a_3}s_5\xrightarrow{a_3}s_8\xrightarrow{a_2}s_9.$', md_content) + self.assertIn(r'$\begin{aligned} & p(s^{\prime}|s,a) \\ & \sum_{s^{\prime}\in\mathcal{S}}p(s^{\prime}|s,a)=1\text{ for any }(s,a). \end{aligned}$', md_content) diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tagV1.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tagV1.md new file mode 100644 index 00000000..5db9af0a --- /dev/null +++ b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tagV1.md @@ -0,0 +1,392 @@ +# 强化学习(RL)(从入门到PPO)(一、基础部分) + +## 开始前的碎碎念 + +强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 + +## 感性认识 + +首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: + +- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 +- 强化学习是个很抽象的系统内容,有on policy和off + policy,online和offline,model base和model + free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 +- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human + feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 +- LLM预训练和监督微调可以看做是单步的off line + 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off + line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on + line和on + policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 + +## 直观认识 + +### 示意图: + +智能体有3种关键要素,即感知、决策和奖励 + +- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 +- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 +- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 + +强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 + +## 马尔科夫决策过程 + +### State + +智能体当前所处的状态 + +### Action + +智能体在某个状态下可能执行的动作 + +### State transition + +智能体在某个状态下执行某个动作会进入到新的状态 + +表格形式描述状态转移矩阵: + +状态转移概率(总和为1),示意: + +$$ +\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered} +$$ + +### Policy + +智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 + +- 确定性策略 + +$$ +\\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} +$$ + +- 不确定性策略 + +表格形式: + +### Reward + +智能体在某个状态下执行动作的奖惩值 + +表格形式: + +## Trajectories, returns, and episodes + +- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 +- returns(回报):智能体在trajectory上所有的reward总和。 +- episodes:trajectory的一部分。 + +马尔科夫链描述trajectory: + +$$ +s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9. +$$ + +returns: + +$$ +\\mathrm{return}=0+0+0+1=1. +$$ + +即时收益: + +当前状态执行某个动作马上会获得的收益(奖惩值) + +未来收益: + +当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 + +$$ +s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots +$$ + +s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 + +trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ +∈ (0, 1)。 + +引入衰减因子的return叫做disconted return。 + +$$ +\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty, +$$ + +$$ +\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots, +$$ + +$$ +\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}. +$$ + +注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 + +### State Space + +状态空间:所有可能的状态,一个集合,可能无穷 + +### Action Space + +动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 + +注意:动作空间依赖于状态,每个状态下动作空间不一定相同 + +### 马尔科夫决策过程 + +符号描述 + +- 集合: +- 模型: + +$$ +\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned} +$$ + +- 奖励概率: + +$$ +\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned} +$$ + +- 策略: + +$$ +\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned} +$$ + +马尔科夫决策过程: + +马尔可夫决策过程由元组(S,A,P,r,γ)组成 + +$$ +s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots +$$ + +马尔科夫奖励过程/马尔科夫过程: + +$$ +s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots +$$ + +未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 + +### 价值函数 + +价值函数分为两种:状态价值以及动作价值 + +状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 + +动作价值: 当前状态及动作之后所有trajectory的return的期望。 + +回顾returns: + +$$ +G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k} +$$ + +价值函数的数学符号的展开表示: + +$$ +\\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} +$$ + +价值函数的转移概率形式: + +$$ +V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) +$$ + +价值函数的矩阵展开形式: + +$$ +\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix}= \\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix}+\\gamma \\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} \\end{aligned} +$$ + +以上都是 贝尔曼公式 + +注意: + +- 贝尔曼公式不是唯一的,存在非常多种表现形式 +- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 + +### 状态价值函数 + +基于某个策略的价值函数: + +$$ +V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\] +$$ + +### 动作价值函数 + +在一个状态下执行动作a的期望价值函数: + +$$ +Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\] +$$ + +### 状态价值与动作价值之间的关系 + +数学上表示: + +直观理解: 当前状态的状态价值是动作价值的期望 + +注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 + +$$ +V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a) +$$ + +$$ +Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s') +$$ + +带策略的贝尔曼方程: + +$ +\\begin{gathered} V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') \\end{gathered} +$ + +$ +\\begin{aligned} V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') \\end{aligned} +$ + +$ +\\begin{aligned}\[t\] V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') \\end{aligned} +$ + +注意: + +- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 +- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 +- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 +- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 + +下面:如何更新策略,或者如何计算贝尔曼公式 + +## 计算贝尔曼公式 + +直接对矩阵求逆,实际使用起来不现实,这里不展示 + +实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 + +迭代算法有个基础直观认识:只要不断的提升State +Value,最终会收敛到最优的State Value + +在此之前介绍贝尔曼最优公式: + +$$ +\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a) +$$ + +贝尔曼最优公式的作用:寻找策略提升点 + +直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all +in最大的a,所有最优贝尔曼公式多了max。 + +注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 + +如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 + +以下两种迭代算法介绍都是model +base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model +base的场景。 + +### 值迭代 + +直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State +Value,通过当前所有动作的即时收益获取到最优的Action +Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State +Value收敛。 + +伪代码: + +值迭代示例: + +$$ +\\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots +$$ + +### 策略迭代 + +策略迭代分为两个部分:策略评估和策略提升 + +直观上:首先初始化一个策略和State +Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action +Value,得到新的策略,重复执行,不断迭代…。 + +策略评估是计算一个策略的状态价值函数 + +策略提升是选择每个状态在当前策略下即时收益最大的Action +Value + +注:策略提升是找到了策略提升点,即保证更新后的State +Value不低于旧策略的State Value + +策略提升定理的证明: + +$$ +V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s) +$$ + +策略提升定理保证了新策略的State +Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 + +直观步骤示意: + +$$ +\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\* +$$ + +策略迭代伪代码: + +策略迭代简单示例: + +初始条件: + +1.策略评估: + +2.策略提升: + +注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 + +上面两种算法本质上是 动态规划算法 + +### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 + +$$ +V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) +$$ + +在实际场景应用中,会存在以下问题: + +- 环境有多少状态未知 +- 当前状态执行某个动作进入每种状态的概率未知 +- 状态可能是无穷的 +- 动作可能是无穷的 +- 迭代更新策略是离散的,但某些场景是连续的。 +- 等等,一大堆 + +一般来说model +base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 + +以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model +base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model +free。 + +## 参考资料: + +### 书籍: + +- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ +- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning +- 动手学强化学习: https:// hrl.boyuai.com/ + +### 视频: + +编辑于 2025-04-22 18:07 +・北京 diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_inline.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_inline.md new file mode 100644 index 00000000..bb52789c --- /dev/null +++ b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_inline.md @@ -0,0 +1,328 @@ +# 强化学习(RL)(从入门到PPO)(一、基础部分) + +## 开始前的碎碎念 + +强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 + +## 感性认识 + +首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: + +- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 +- 强化学习是个很抽象的系统内容,有on policy和off + policy,online和offline,model base和model + free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 +- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human + feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 +- LLM预训练和监督微调可以看做是单步的off line + 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off + line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on + line和on + policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 + +## 直观认识 + +### 示意图: + +智能体有3种关键要素,即感知、决策和奖励 + +- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 +- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 +- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 + +强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 + +## 马尔科夫决策过程 + +### State + +智能体当前所处的状态 + +### Action + +智能体在某个状态下可能执行的动作 + +### State transition + +智能体在某个状态下执行某个动作会进入到新的状态 + +表格形式描述状态转移矩阵: + +状态转移概率(总和为1),示意: + +$\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered}$ + +### Policy + +智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 + +- 确定性策略 + +## \\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} + +- 不确定性策略 + +表格形式: + +### Reward + +智能体在某个状态下执行动作的奖惩值 + +表格形式: + +## Trajectories, returns, and episodes + +- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 +- returns(回报):智能体在trajectory上所有的reward总和。 +- episodes:trajectory的一部分。 + +马尔科夫链描述trajectory: + +$s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9.$ + +returns: + +$\\mathrm{return}=0+0+0+1=1.$ + +即时收益: + +当前状态执行某个动作马上会获得的收益(奖惩值) + +未来收益: + +当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 + +## s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots + +s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 + +trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ +∈ (0, 1)。 + +引入衰减因子的return叫做disconted return。 + +$\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty,$ + +$\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots,$ + +$\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}.$ + +注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 + +### State Space + +状态空间:所有可能的状态,一个集合,可能无穷 + +### Action Space + +动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 + +注意:动作空间依赖于状态,每个状态下动作空间不一定相同 + +### 马尔科夫决策过程 + +符号描述 + +- 集合: +- 模型: + +$\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned}$ + +- 奖励概率: + +$\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned}$ + +- 策略: + +$\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned}$ + +马尔科夫决策过程: + +马尔可夫决策过程由元组(S,A,P,r,γ)组成 + +$s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots$ + +马尔科夫奖励过程/马尔科夫过程: + +$s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots$ + +未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 + +### 价值函数 + +价值函数分为两种:状态价值以及动作价值 + +状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 + +动作价值: 当前状态及动作之后所有trajectory的return的期望。 + +回顾returns: + +$G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k}$ + +价值函数的数学符号的展开表示: + +## \\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} + +价值函数的转移概率形式: + +## V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) + +价值函数的矩阵展开形式: + +$\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix}= \\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix}+\\gamma \\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} \\end{aligned}$ + +以上都是 贝尔曼公式 + +注意: + +- 贝尔曼公式不是唯一的,存在非常多种表现形式 +- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 + +### 状态价值函数 + +基于某个策略的价值函数: + +$V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\]$ + +### 动作价值函数 + +在一个状态下执行动作a的期望价值函数: + +$Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\]$ + +### 状态价值与动作价值之间的关系 + +数学上表示: + +直观理解: 当前状态的状态价值是动作价值的期望 + +注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 + +$V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a)$ + +$Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s')$ + +带策略的贝尔曼方程: + +$V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a')$ + +注意: + +- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 +- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 +- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 +- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 + +下面:如何更新策略,或者如何计算贝尔曼公式 + +## 计算贝尔曼公式 + +直接对矩阵求逆,实际使用起来不现实,这里不展示 + +实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 + +迭代算法有个基础直观认识:只要不断的提升State +Value,最终会收敛到最优的State Value + +在此之前介绍贝尔曼最优公式: $\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a)$ + +贝尔曼最优公式的作用:寻找策略提升点 + +直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all +in最大的a,所有最优贝尔曼公式多了max。 + +注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 + +如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 + +以下两种迭代算法介绍都是model +base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model +base的场景。 + +### 值迭代 + +直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State +Value,通过当前所有动作的即时收益获取到最优的Action +Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State +Value收敛。 + +伪代码: + +值迭代示例: + +## \\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots + +### 策略迭代 + +策略迭代分为两个部分:策略评估和策略提升 + +直观上:首先初始化一个策略和State +Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action +Value,得到新的策略,重复执行,不断迭代…。 + +策略评估是计算一个策略的状态价值函数 + +策略提升是选择每个状态在当前策略下即时收益最大的Action +Value + +注:策略提升是找到了策略提升点,即保证更新后的State +Value不低于旧策略的State Value + +策略提升定理的证明: + +$V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s)$ + +策略提升定理保证了新策略的State +Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 + +直观步骤示意: + +$\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\*$ + +策略迭代伪代码: + +策略迭代简单示例: + +初始条件: + +1.策略评估: + +2.策略提升: + +注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 + +上面两种算法本质上是 动态规划算法 + +### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 + +$V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime})$ + +在实际场景应用中,会存在以下问题: + +- 环境有多少状态未知 +- 当前状态执行某个动作进入每种状态的概率未知 +- 状态可能是无穷的 +- 动作可能是无穷的 +- 迭代更新策略是离散的,但某些场景是连续的。 +- 等等,一大堆 + +一般来说model +base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 + +以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model +base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model +free。 + +## 参考资料: + +### 书籍: + +- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ +- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning +- 动手学强化学习: https:// hrl.boyuai.com/ + +### 视频: + +编辑于 2025-04-22 18:07 +・北京 diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_interline.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_interline.md new file mode 100644 index 00000000..af0dbb70 --- /dev/null +++ b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_interline.md @@ -0,0 +1,384 @@ +# 强化学习(RL)(从入门到PPO)(一、基础部分) + +## 开始前的碎碎念 + +强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 + +## 感性认识 + +首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: + +- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 +- 强化学习是个很抽象的系统内容,有on policy和off + policy,online和offline,model base和model + free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 +- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human + feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 +- LLM预训练和监督微调可以看做是单步的off line + 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off + line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on + line和on + policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 + +## 直观认识 + +### 示意图: + +智能体有3种关键要素,即感知、决策和奖励 + +- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 +- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 +- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 + +强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 + +## 马尔科夫决策过程 + +### State + +智能体当前所处的状态 + +### Action + +智能体在某个状态下可能执行的动作 + +### State transition + +智能体在某个状态下执行某个动作会进入到新的状态 + +表格形式描述状态转移矩阵: + +状态转移概率(总和为1),示意: + +$$ +\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered} +$$ + +### Policy + +智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 + +- 确定性策略 + +$$ +\\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} +$$ + +- 不确定性策略 + +表格形式: + +### Reward + +智能体在某个状态下执行动作的奖惩值 + +表格形式: + +## Trajectories, returns, and episodes + +- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 +- returns(回报):智能体在trajectory上所有的reward总和。 +- episodes:trajectory的一部分。 + +马尔科夫链描述trajectory: + +$$ +s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9. +$$ + +returns: + +$$ +\\mathrm{return}=0+0+0+1=1. +$$ + +即时收益: + +当前状态执行某个动作马上会获得的收益(奖惩值) + +未来收益: + +当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 + +$$ +s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots +$$ + +s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 + +trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ +∈ (0, 1)。 + +引入衰减因子的return叫做disconted return。 + +$$ +\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty, +$$ + +$$ +\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots, +$$ + +$$ +\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}. +$$ + +注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 + +### State Space + +状态空间:所有可能的状态,一个集合,可能无穷 + +### Action Space + +动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 + +注意:动作空间依赖于状态,每个状态下动作空间不一定相同 + +### 马尔科夫决策过程 + +符号描述 + +- 集合: +- 模型: + +$$ +\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned} +$$ + +- 奖励概率: + +$$ +\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned} +$$ + +- 策略: + +$$ +\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned} +$$ + +马尔科夫决策过程: + +马尔可夫决策过程由元组(S,A,P,r,γ)组成 + +$$ +s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots +$$ + +马尔科夫奖励过程/马尔科夫过程: + +$$ +s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots +$$ + +未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 + +### 价值函数 + +价值函数分为两种:状态价值以及动作价值 + +状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 + +动作价值: 当前状态及动作之后所有trajectory的return的期望。 + +回顾returns: + +$$ +G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k} +$$ + +价值函数的数学符号的展开表示: + +$$ +\\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} +$$ + +价值函数的转移概率形式: + +$$ +V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) +$$ + +价值函数的矩阵展开形式: + +$$ +\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix}= \\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix}+\\gamma \\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} \\end{aligned} +$$ + +以上都是 贝尔曼公式 + +注意: + +- 贝尔曼公式不是唯一的,存在非常多种表现形式 +- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 + +### 状态价值函数 + +基于某个策略的价值函数: + +$$ +V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\] +$$ + +### 动作价值函数 + +在一个状态下执行动作a的期望价值函数: + +$$ +Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\] +$$ + +### 状态价值与动作价值之间的关系 + +数学上表示: + +直观理解: 当前状态的状态价值是动作价值的期望 + +注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 + +$$ +V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a) +$$ + +$$ +Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s') +$$ + +带策略的贝尔曼方程: + +$$ +V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') +$$ + +注意: + +- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 +- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 +- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 +- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 + +下面:如何更新策略,或者如何计算贝尔曼公式 + +## 计算贝尔曼公式 + +直接对矩阵求逆,实际使用起来不现实,这里不展示 + +实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 + +迭代算法有个基础直观认识:只要不断的提升State +Value,最终会收敛到最优的State Value + +在此之前介绍贝尔曼最优公式: + +$$ +\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a) +$$ + +贝尔曼最优公式的作用:寻找策略提升点 + +直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all +in最大的a,所有最优贝尔曼公式多了max。 + +注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 + +如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 + +以下两种迭代算法介绍都是model +base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model +base的场景。 + +### 值迭代 + +直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State +Value,通过当前所有动作的即时收益获取到最优的Action +Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State +Value收敛。 + +伪代码: + +值迭代示例: + +$$ +\\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots +$$ + +### 策略迭代 + +策略迭代分为两个部分:策略评估和策略提升 + +直观上:首先初始化一个策略和State +Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action +Value,得到新的策略,重复执行,不断迭代…。 + +策略评估是计算一个策略的状态价值函数 + +策略提升是选择每个状态在当前策略下即时收益最大的Action +Value + +注:策略提升是找到了策略提升点,即保证更新后的State +Value不低于旧策略的State Value + +策略提升定理的证明: + +$$ +V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s) +$$ + +策略提升定理保证了新策略的State +Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 + +直观步骤示意: + +$$ +\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\* +$$ + +策略迭代伪代码: + +策略迭代简单示例: + +初始条件: + +1.策略评估: + +2.策略提升: + +注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 + +上面两种算法本质上是 动态规划算法 + +### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 + +$$ +V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) +$$ + +在实际场景应用中,会存在以下问题: + +- 环境有多少状态未知 +- 当前状态执行某个动作进入每种状态的概率未知 +- 状态可能是无穷的 +- 动作可能是无穷的 +- 迭代更新策略是离散的,但某些场景是连续的。 +- 等等,一大堆 + +一般来说model +base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 + +以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model +base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model +free。 + +## 参考资料: + +### 书籍: + +- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ +- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning +- 动手学强化学习: https:// hrl.boyuai.com/ + +### 视频: + +编辑于 2025-04-22 18:07 +・北京 diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_ori.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_ori.md new file mode 100644 index 00000000..e15cb405 --- /dev/null +++ b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_ori.md @@ -0,0 +1,360 @@ +# 强化学习(RL)(从入门到PPO)(一、基础部分) + +## 开始前的碎碎念 + +强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 + +## 感性认识 + +首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: + +- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 +- 强化学习是个很抽象的系统内容,有on policy和off + policy,online和offline,model base和model + free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 +- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human + feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 +- LLM预训练和监督微调可以看做是单步的off line + 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off + line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on + line和on + policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 + +## 直观认识 + +### 示意图: + +智能体有3种关键要素,即感知、决策和奖励 + +- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 +- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 +- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 + +强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 + +## 马尔科夫决策过程 + +### State + +智能体当前所处的状态 + +### Action + +智能体在某个状态下可能执行的动作 + +### State transition + +智能体在某个状态下执行某个动作会进入到新的状态 + +表格形式描述状态转移矩阵: + +状态转移概率(总和为1),示意: + +$$ +\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered} +$$ + +### Policy + +智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 + +- 确定性策略 + +$$ +\\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} +$$ + +- 不确定性策略 + +表格形式: + +### Reward + +智能体在某个状态下执行动作的奖惩值 + +表格形式: + +## Trajectories, returns, and episodes + +- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 +- returns(回报):智能体在trajectory上所有的reward总和。 +- episodes:trajectory的一部分。 + +马尔科夫链描述trajectory: + +s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9. + +returns: + +\\mathrm{return}=0+0+0+1=1. + +即时收益: + +当前状态执行某个动作马上会获得的收益(奖惩值) + +未来收益: + +当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 + +## s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots + +s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 + +trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ +∈ (0, 1)。 + +引入衰减因子的return叫做disconted return。 + +\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty, + +\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots, + +\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}. + +注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 + +### State Space + +状态空间:所有可能的状态,一个集合,可能无穷 + +### Action Space + +动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 + +注意:动作空间依赖于状态,每个状态下动作空间不一定相同 + +### 马尔科夫决策过程 + +符号描述 + +- 集合: +- 模型: + +$$ +\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned} +$$ + +- 奖励概率: + +$$ +\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned} +$$ + +- 策略: + +$$ +\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned} +$$ + +马尔科夫决策过程: + +马尔可夫决策过程由元组(S,A,P,r,γ)组成 + +s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots + +马尔科夫奖励过程/马尔科夫过程: + +s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots + +未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 + +### 价值函数 + +价值函数分为两种:状态价值以及动作价值 + +状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 + +动作价值: 当前状态及动作之后所有trajectory的return的期望。 + +回顾returns: + +G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k} + +价值函数的数学符号的展开表示: + +$$ +\\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} +$$ + +价值函数的转移概率形式: + +## V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) + +价值函数的矩阵展开形式: + +$$ +\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} +$$ + +\= + +$$ +\\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix} +$$ + ++\\gamma + +$$ +\\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} +$$ + +$$ +\\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} +$$ + +\\end{aligned} + +以上都是 贝尔曼公式 + +注意: + +- 贝尔曼公式不是唯一的,存在非常多种表现形式 +- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 + +### 状态价值函数 + +基于某个策略的价值函数: + +V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\] + +### 动作价值函数 + +在一个状态下执行动作a的期望价值函数: + +Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\] + +### 状态价值与动作价值之间的关系 + +数学上表示: + +直观理解: 当前状态的状态价值是动作价值的期望 + +注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 + +V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a) + +Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s') + +带策略的贝尔曼方程: + +V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') + +注意: + +- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 +- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 +- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 +- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 + +下面:如何更新策略,或者如何计算贝尔曼公式 + +## 计算贝尔曼公式 + +直接对矩阵求逆,实际使用起来不现实,这里不展示 + +实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 + +迭代算法有个基础直观认识:只要不断的提升State +Value,最终会收敛到最优的State Value + +在此之前介绍贝尔曼最优公式:\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a) + +贝尔曼最优公式的作用:寻找策略提升点 + +直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all +in最大的a,所有最优贝尔曼公式多了max。 + +注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 + +如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 + +以下两种迭代算法介绍都是model +base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model +base的场景。 + +### 值迭代 + +直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State +Value,通过当前所有动作的即时收益获取到最优的Action +Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State +Value收敛。 + +伪代码: + +值迭代示例: + +## \\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots + +### 策略迭代 + +策略迭代分为两个部分:策略评估和策略提升 + +直观上:首先初始化一个策略和State +Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action +Value,得到新的策略,重复执行,不断迭代…。 + +策略评估是计算一个策略的状态价值函数 + +策略提升是选择每个状态在当前策略下即时收益最大的Action +Value + +注:策略提升是找到了策略提升点,即保证更新后的State +Value不低于旧策略的State Value + +策略提升定理的证明: + +V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s) + +策略提升定理保证了新策略的State +Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 + +直观步骤示意: + +\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\* + +策略迭代伪代码: + +策略迭代简单示例: + +初始条件: + +1.策略评估: + +2.策略提升: + +注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 + +上面两种算法本质上是 动态规划算法 + +### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 + +V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) + +在实际场景应用中,会存在以下问题: + +- 环境有多少状态未知 +- 当前状态执行某个动作进入每种状态的概率未知 +- 状态可能是无穷的 +- 动作可能是无穷的 +- 迭代更新策略是离散的,但某些场景是连续的。 +- 等等,一大堆 + +一般来说model +base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 + +以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model +base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model +free。 + +## 参考资料: + +### 书籍: + +- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ +- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning +- 动手学强化学习: https:// hrl.boyuai.com/ + +### 视频: + +编辑于 2025-04-22 18:07 +・北京 diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_v2.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_v2.md new file mode 100644 index 00000000..bb52789c --- /dev/null +++ b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_v2.md @@ -0,0 +1,328 @@ +# 强化学习(RL)(从入门到PPO)(一、基础部分) + +## 开始前的碎碎念 + +强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 + +## 感性认识 + +首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: + +- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 +- 强化学习是个很抽象的系统内容,有on policy和off + policy,online和offline,model base和model + free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 +- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human + feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 +- LLM预训练和监督微调可以看做是单步的off line + 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off + line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on + line和on + policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 + +## 直观认识 + +### 示意图: + +智能体有3种关键要素,即感知、决策和奖励 + +- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 +- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 +- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 + +强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 + +## 马尔科夫决策过程 + +### State + +智能体当前所处的状态 + +### Action + +智能体在某个状态下可能执行的动作 + +### State transition + +智能体在某个状态下执行某个动作会进入到新的状态 + +表格形式描述状态转移矩阵: + +状态转移概率(总和为1),示意: + +$\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered}$ + +### Policy + +智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 + +- 确定性策略 + +## \\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} + +- 不确定性策略 + +表格形式: + +### Reward + +智能体在某个状态下执行动作的奖惩值 + +表格形式: + +## Trajectories, returns, and episodes + +- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 +- returns(回报):智能体在trajectory上所有的reward总和。 +- episodes:trajectory的一部分。 + +马尔科夫链描述trajectory: + +$s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9.$ + +returns: + +$\\mathrm{return}=0+0+0+1=1.$ + +即时收益: + +当前状态执行某个动作马上会获得的收益(奖惩值) + +未来收益: + +当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 + +## s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots + +s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 + +trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ +∈ (0, 1)。 + +引入衰减因子的return叫做disconted return。 + +$\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty,$ + +$\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots,$ + +$\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}.$ + +注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 + +### State Space + +状态空间:所有可能的状态,一个集合,可能无穷 + +### Action Space + +动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 + +注意:动作空间依赖于状态,每个状态下动作空间不一定相同 + +### 马尔科夫决策过程 + +符号描述 + +- 集合: +- 模型: + +$\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned}$ + +- 奖励概率: + +$\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned}$ + +- 策略: + +$\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned}$ + +马尔科夫决策过程: + +马尔可夫决策过程由元组(S,A,P,r,γ)组成 + +$s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots$ + +马尔科夫奖励过程/马尔科夫过程: + +$s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots$ + +未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 + +### 价值函数 + +价值函数分为两种:状态价值以及动作价值 + +状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 + +动作价值: 当前状态及动作之后所有trajectory的return的期望。 + +回顾returns: + +$G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k}$ + +价值函数的数学符号的展开表示: + +## \\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} + +价值函数的转移概率形式: + +## V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) + +价值函数的矩阵展开形式: + +$\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix}= \\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix}+\\gamma \\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} \\end{aligned}$ + +以上都是 贝尔曼公式 + +注意: + +- 贝尔曼公式不是唯一的,存在非常多种表现形式 +- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 + +### 状态价值函数 + +基于某个策略的价值函数: + +$V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\]$ + +### 动作价值函数 + +在一个状态下执行动作a的期望价值函数: + +$Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\]$ + +### 状态价值与动作价值之间的关系 + +数学上表示: + +直观理解: 当前状态的状态价值是动作价值的期望 + +注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 + +$V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a)$ + +$Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s')$ + +带策略的贝尔曼方程: + +$V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a')$ + +注意: + +- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 +- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 +- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 +- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 + +下面:如何更新策略,或者如何计算贝尔曼公式 + +## 计算贝尔曼公式 + +直接对矩阵求逆,实际使用起来不现实,这里不展示 + +实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 + +迭代算法有个基础直观认识:只要不断的提升State +Value,最终会收敛到最优的State Value + +在此之前介绍贝尔曼最优公式: $\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a)$ + +贝尔曼最优公式的作用:寻找策略提升点 + +直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all +in最大的a,所有最优贝尔曼公式多了max。 + +注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 + +如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 + +以下两种迭代算法介绍都是model +base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model +base的场景。 + +### 值迭代 + +直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State +Value,通过当前所有动作的即时收益获取到最优的Action +Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State +Value收敛。 + +伪代码: + +值迭代示例: + +## \\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots + +### 策略迭代 + +策略迭代分为两个部分:策略评估和策略提升 + +直观上:首先初始化一个策略和State +Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action +Value,得到新的策略,重复执行,不断迭代…。 + +策略评估是计算一个策略的状态价值函数 + +策略提升是选择每个状态在当前策略下即时收益最大的Action +Value + +注:策略提升是找到了策略提升点,即保证更新后的State +Value不低于旧策略的State Value + +策略提升定理的证明: + +$V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s)$ + +策略提升定理保证了新策略的State +Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 + +直观步骤示意: + +$\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\*$ + +策略迭代伪代码: + +策略迭代简单示例: + +初始条件: + +1.策略评估: + +2.策略提升: + +注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 + +上面两种算法本质上是 动态规划算法 + +### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 + +$V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime})$ + +在实际场景应用中,会存在以下问题: + +- 环境有多少状态未知 +- 当前状态执行某个动作进入每种状态的概率未知 +- 状态可能是无穷的 +- 动作可能是无穷的 +- 迭代更新策略是离散的,但某些场景是连续的。 +- 等等,一大堆 + +一般来说model +base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 + +以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model +base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model +free。 + +## 参考资料: + +### 书籍: + +- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ +- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning +- 动手学强化学习: https:// hrl.boyuai.com/ + +### 视频: + +编辑于 2025-04-22 18:07 +・北京 From c3ae1607264461df3bf1ab3e378a6bb2d3036136 Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Fri, 13 Jun 2025 11:36:05 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../good_data/output_expected/1.md | 40 -- .../good_data/output_expected/csdn_lineno.md | 299 ------------- .../recognizer/assets/cccode/mathworks.md | 97 ----- .../zhihucase1/output_zhihu_custom_tagV1.md | 392 ------------------ .../output_zhihu_custom_tag_inline.md | 328 --------------- .../output_zhihu_custom_tag_interline.md | 384 ----------------- .../zhihucase1/output_zhihu_custom_tag_ori.md | 360 ---------------- .../zhihucase1/output_zhihu_custom_tag_v2.md | 328 --------------- 8 files changed, 2228 deletions(-) delete mode 100644 tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/1.md delete mode 100644 tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/csdn_lineno.md delete mode 100644 tests/llm_web_kit/extractor/html/recognizer/assets/cccode/mathworks.md delete mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tagV1.md delete mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_inline.md delete mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_interline.md delete mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_ori.md delete mode 100644 tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_v2.md diff --git a/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/1.md b/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/1.md deleted file mode 100644 index bc193e04..00000000 --- a/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/1.md +++ /dev/null @@ -1,40 +0,0 @@ -# Heading 1 - -Paragraph 1 - -Paragraph 2 - -| 1 | 2 | -|---|---| -| 3 | 4 | - -
123
4
567
- -- 1 -- 2 - -- 1 - - 1.1 - - 1.2 -- 2 - - 2.1 - - 2.2 - -$$ -x=\frac{-b±\sqrt{{b}^{2}-4ac}}{2a}\text{.} -$$ - -```js -const Prism = require('prismjs'); - - // The code snippet you want to highlight, as a string - const code = `var data = 1;`; - - // Returns a highlighted HTML string - const html = Prism.highlight(code, Prism.languages.javascript, 'javascript'); -``` - -1. 100 -2. 200 - -reference: `#include` diff --git a/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/csdn_lineno.md b/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/csdn_lineno.md deleted file mode 100644 index b6d3fdaf..00000000 --- a/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/csdn_lineno.md +++ /dev/null @@ -1,299 +0,0 @@ -<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> -<% -String path = request.getContextPath(); -String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; -%> - - - - - My JSP 'register.jsp' starting page - - - - - -
- - 用户名:
- 密 码:
- - 登录 -
- - - - - - -package com.servlet; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.dao.UsersDao; - -public class servlet3 extends HttpServlet { - - public servlet3() { - super(); - } - - - public void destroy() { - super.destroy(); // Just puts "destroy" string in log - // Put your code here - } - - - public void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - doPost (request, response); - - } - - - public void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - String uname = request.getParameter("uname"); - String upwd = request.getParameter("upwd"); - UsersDao usersDao = new UsersDao(); - int i=usersDao.reg(uname, upwd); - if(i>0){ - - response.setHeader("refresh", "2;url=login.jsp"); - }else{ - - response.setHeader("refresh", "2;url=reg.jsp"); - } - } - - /** - * Initialization of the servlet.
- * - * @throws ServletException if an error occurs - */ - public void init() throws ServletException { - // Put your code here - } - -} - - - - - -package com.sf.servlet; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.sf.dao.MsgDao; -import com.sf.dao.UsersDao; - -public class Doregservlet extends HttpServlet { - - /** - * Constructor of the object. - */ - public Doregservlet() { - super(); - } - - /** - * Destruction of the servlet.
- */ - public void destroy() { - super.destroy(); // Just puts "destroy" string in log - // Put your code here - } - - public void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - response.setContentType("text/html"); - PrintWriter out = response.getWriter(); - request.setCharacterEncoding("utf-8"); - String uname = request.getParameter("uname"); - String upwd = request.getParameter("upwd"); - - UsersDao ud = new UsersDao(); - MsgDao md = new MsgDao(); - if (ud.register(uname, upwd) > 0) { - request.getSession().setAttribute("uname", uname); - request.getRequestDispatcher("denglu.jsp").forward(request, - response); - } else { - out.print("注册失败,请重新注册......."); - response.setHeader("refresh", "3;url=reg.jsp"); - } - } - public void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - doGet(request,response); - } - - /** - * Initialization of the servlet.
- * - * @throws ServletException if an error occurs - */ - public void init() throws ServletException { - // Put your code here - } - -} - - - - - -package com.servlet; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.dao.MsgDao; - -public class servlet5 extends HttpServlet { - - public servlet5() { - super(); - } - - public void destroy() { - super.destroy(); // Just puts "destroy" string in log - // Put your code here - } - - - public void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - doPost(request, response); - } - - - public void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - request.setCharacterEncoding("utf-8"); - - int id=Integer.parseInt(request.getParameter("id")); - MsgDao md=new MsgDao(); - md.delMail(id); - response.getWriter().print("刪除成功....."); - response.setHeader("refresh", "2;url=main.jsp"); - response.sendRedirect("main2.jsp"); - } - - - public void init() throws ServletException { - - } - -} - - - - - - - -package com.sf.servlet; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.sf.dao.MsgDao; -import com.sf.entity.Msg; - -public class Dowriteservlet extends HttpServlet { - - /** - * Constructor of the object. - */ - public Dowriteservlet() { - super(); - } - - /** - * Destruction of the servlet.
- */ - public void destroy() { - super.destroy(); // Just puts "destroy" string in log - // Put your code here - } - - public void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - response.setContentType("text/html"); - PrintWriter out = response.getWriter(); - request.setCharacterEncoding("utf-8"); - String uname = (String) request.getSession().getAttribute("uname"); - String sendto = request.getParameter("receiver"); - String title = request.getParameter("title"); - String content = request.getParameter("content"); - - Msg m = new Msg(); - m.setMsgcontent(content); - m.setUsername(uname); - m.setSendto(sendto); - m.setTitle(title); - - MsgDao md = new MsgDao(); - md.addMsg(m); - - out.print("发送成功....."); - response.setHeader("refresh", "3;url=main.jsp"); - } - - public void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - doGet(request,response); } - - /** - * Initialization of the servlet.
- * - * @throws ServletException if an error occurs - */ - public void init() throws ServletException { - } - -} \ No newline at end of file diff --git a/tests/llm_web_kit/extractor/html/recognizer/assets/cccode/mathworks.md b/tests/llm_web_kit/extractor/html/recognizer/assets/cccode/mathworks.md deleted file mode 100644 index 5eda3398..00000000 --- a/tests/llm_web_kit/extractor/html/recognizer/assets/cccode/mathworks.md +++ /dev/null @@ -1,97 +0,0 @@ -主要内容 - -## Single-Precision Conversion Best Practices - -### Use Integers for Index Variables - -In MATLAB® code that you want to convert to single precision, -it is a best practice to use integers for index variables. However, -if the code does not use integers for index variables, when possible `convertToSingle` tries to detect -the index variables and select `int32` types for -them. - -### Limit Use of `assert` Statements - -- Do not use `assert` statements -to define the properties of input arguments. -- Do not use `assert` statements -to test the type of a variable. For example, do not use - -``` -assert(isa(a, 'double')) -``` - -### Initialize MATLAB Class Properties in Constructor - -Do not initialize MATLAB class properties in the `properties` block. -Instead, use the constructor to initialize the class properties. - -### Provide a Test File That Calls Your MATLAB Function - -Separate your core algorithm from other code that you use to -test and verify the results. Create a test file that calls your double-precision MATLAB algorithm. -You can use the test file to: - -- Automatically define properties of the top-level function -inputs. -- Verify that the double-precision algorithm behaves -as you expect. The double-precision behavior is the baseline against -which you compare the behavior of the single-precision versions of -your algorithm. -- Compare the behavior of the single-precision version -of your algorithm to the double-precision baseline. - -For best results, the test file must exercise the algorithm -over its full operating range. - -### Prepare Your Code for Code Generation - -MATLAB code that you want to convert to single precision -must comply with code generation requirements. See MATLAB Language Features Supported for C/C++ Code Generation. - -To help you identify unsupported functions or constructs in -your MATLAB code, add the `%#codegen` pragma -to the top of your MATLAB file. When you edit your code in the MATLAB editor, -the MATLAB Code Analyzer flags functions and constructs that -are not supported for code generation. See Check Code Using the MATLAB Code Analyzer. When you use the MATLAB - Coder™ app, -the app screens your code for code generation readiness. At the function -line, you can use the Code Generation Readiness Tool. See Check Code Using the Code Generation Readiness Tool. - -### Use the `-args` Option to Specify Input Properties - -When you generate single-precision MATLAB code, if you -specify a test file, you do not have to specify argument properties -with the `-args` option. In this case, the code generator -runs the test file to determine the properties of the input types. -However, running the test file can slow the code generation. It is -a best practice to pass the properties to the `-args` option -so that `convertToSingle` does not run the test -file to determine the argument properties. If you have a MATLAB - Coder license, -you can use `coder.getArgTypes` to determine the -argument properties. For example: - -``` -types = coder.getArgTypes('myfun_test', 'myfun'); -scfg = coder.config('single'); -convertToSingle -config scfg -args types myfun -``` - -### Test Numerics and Log I/O Data - -When you use the convertToSingle function to -generate single-precision MATLAB code, enable numerics testing -and I/O data logging for comparison plots. To use numerics testing, -you must provide a test file that calls your MATLAB function. -To enable numerics testing and I/O data logging, create a `coder.SingleConfig` object. -Set the `TestBenchName` , `TestNumerics` , -and `LogIOForComparisonPlotting` properties. For -example: - -``` -scfg = coder.config('single'); -scfg.TestBenchName = 'mytest'; -scfg.TestNumerics = true; -scfg.LogIOForComparisonPlotting = true; -``` diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tagV1.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tagV1.md deleted file mode 100644 index 5db9af0a..00000000 --- a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tagV1.md +++ /dev/null @@ -1,392 +0,0 @@ -# 强化学习(RL)(从入门到PPO)(一、基础部分) - -## 开始前的碎碎念 - -强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 - -## 感性认识 - -首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: - -- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 -- 强化学习是个很抽象的系统内容,有on policy和off - policy,online和offline,model base和model - free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 -- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human - feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 -- LLM预训练和监督微调可以看做是单步的off line - 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off - line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on - line和on - policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 - -## 直观认识 - -### 示意图: - -智能体有3种关键要素,即感知、决策和奖励 - -- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 -- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 -- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 - -强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 - -## 马尔科夫决策过程 - -### State - -智能体当前所处的状态 - -### Action - -智能体在某个状态下可能执行的动作 - -### State transition - -智能体在某个状态下执行某个动作会进入到新的状态 - -表格形式描述状态转移矩阵: - -状态转移概率(总和为1),示意: - -$$ -\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered} -$$ - -### Policy - -智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 - -- 确定性策略 - -$$ -\\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} -$$ - -- 不确定性策略 - -表格形式: - -### Reward - -智能体在某个状态下执行动作的奖惩值 - -表格形式: - -## Trajectories, returns, and episodes - -- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 -- returns(回报):智能体在trajectory上所有的reward总和。 -- episodes:trajectory的一部分。 - -马尔科夫链描述trajectory: - -$$ -s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9. -$$ - -returns: - -$$ -\\mathrm{return}=0+0+0+1=1. -$$ - -即时收益: - -当前状态执行某个动作马上会获得的收益(奖惩值) - -未来收益: - -当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 - -$$ -s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots -$$ - -s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 - -trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ -∈ (0, 1)。 - -引入衰减因子的return叫做disconted return。 - -$$ -\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty, -$$ - -$$ -\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots, -$$ - -$$ -\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}. -$$ - -注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 - -### State Space - -状态空间:所有可能的状态,一个集合,可能无穷 - -### Action Space - -动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 - -注意:动作空间依赖于状态,每个状态下动作空间不一定相同 - -### 马尔科夫决策过程 - -符号描述 - -- 集合: -- 模型: - -$$ -\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned} -$$ - -- 奖励概率: - -$$ -\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned} -$$ - -- 策略: - -$$ -\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned} -$$ - -马尔科夫决策过程: - -马尔可夫决策过程由元组(S,A,P,r,γ)组成 - -$$ -s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots -$$ - -马尔科夫奖励过程/马尔科夫过程: - -$$ -s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots -$$ - -未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 - -### 价值函数 - -价值函数分为两种:状态价值以及动作价值 - -状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 - -动作价值: 当前状态及动作之后所有trajectory的return的期望。 - -回顾returns: - -$$ -G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k} -$$ - -价值函数的数学符号的展开表示: - -$$ -\\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} -$$ - -价值函数的转移概率形式: - -$$ -V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) -$$ - -价值函数的矩阵展开形式: - -$$ -\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix}= \\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix}+\\gamma \\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} \\end{aligned} -$$ - -以上都是 贝尔曼公式 - -注意: - -- 贝尔曼公式不是唯一的,存在非常多种表现形式 -- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 - -### 状态价值函数 - -基于某个策略的价值函数: - -$$ -V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\] -$$ - -### 动作价值函数 - -在一个状态下执行动作a的期望价值函数: - -$$ -Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\] -$$ - -### 状态价值与动作价值之间的关系 - -数学上表示: - -直观理解: 当前状态的状态价值是动作价值的期望 - -注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 - -$$ -V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a) -$$ - -$$ -Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s') -$$ - -带策略的贝尔曼方程: - -$ -\\begin{gathered} V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') \\end{gathered} -$ - -$ -\\begin{aligned} V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') \\end{aligned} -$ - -$ -\\begin{aligned}\[t\] V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') \\end{aligned} -$ - -注意: - -- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 -- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 -- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 -- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 - -下面:如何更新策略,或者如何计算贝尔曼公式 - -## 计算贝尔曼公式 - -直接对矩阵求逆,实际使用起来不现实,这里不展示 - -实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 - -迭代算法有个基础直观认识:只要不断的提升State -Value,最终会收敛到最优的State Value - -在此之前介绍贝尔曼最优公式: - -$$ -\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a) -$$ - -贝尔曼最优公式的作用:寻找策略提升点 - -直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all -in最大的a,所有最优贝尔曼公式多了max。 - -注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 - -如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 - -以下两种迭代算法介绍都是model -base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model -base的场景。 - -### 值迭代 - -直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State -Value,通过当前所有动作的即时收益获取到最优的Action -Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State -Value收敛。 - -伪代码: - -值迭代示例: - -$$ -\\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots -$$ - -### 策略迭代 - -策略迭代分为两个部分:策略评估和策略提升 - -直观上:首先初始化一个策略和State -Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action -Value,得到新的策略,重复执行,不断迭代…。 - -策略评估是计算一个策略的状态价值函数 - -策略提升是选择每个状态在当前策略下即时收益最大的Action -Value - -注:策略提升是找到了策略提升点,即保证更新后的State -Value不低于旧策略的State Value - -策略提升定理的证明: - -$$ -V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s) -$$ - -策略提升定理保证了新策略的State -Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 - -直观步骤示意: - -$$ -\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\* -$$ - -策略迭代伪代码: - -策略迭代简单示例: - -初始条件: - -1.策略评估: - -2.策略提升: - -注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 - -上面两种算法本质上是 动态规划算法 - -### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 - -$$ -V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) -$$ - -在实际场景应用中,会存在以下问题: - -- 环境有多少状态未知 -- 当前状态执行某个动作进入每种状态的概率未知 -- 状态可能是无穷的 -- 动作可能是无穷的 -- 迭代更新策略是离散的,但某些场景是连续的。 -- 等等,一大堆 - -一般来说model -base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 - -以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model -base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model -free。 - -## 参考资料: - -### 书籍: - -- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ -- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning -- 动手学强化学习: https:// hrl.boyuai.com/ - -### 视频: - -编辑于 2025-04-22 18:07 -・北京 diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_inline.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_inline.md deleted file mode 100644 index bb52789c..00000000 --- a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_inline.md +++ /dev/null @@ -1,328 +0,0 @@ -# 强化学习(RL)(从入门到PPO)(一、基础部分) - -## 开始前的碎碎念 - -强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 - -## 感性认识 - -首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: - -- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 -- 强化学习是个很抽象的系统内容,有on policy和off - policy,online和offline,model base和model - free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 -- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human - feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 -- LLM预训练和监督微调可以看做是单步的off line - 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off - line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on - line和on - policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 - -## 直观认识 - -### 示意图: - -智能体有3种关键要素,即感知、决策和奖励 - -- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 -- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 -- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 - -强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 - -## 马尔科夫决策过程 - -### State - -智能体当前所处的状态 - -### Action - -智能体在某个状态下可能执行的动作 - -### State transition - -智能体在某个状态下执行某个动作会进入到新的状态 - -表格形式描述状态转移矩阵: - -状态转移概率(总和为1),示意: - -$\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered}$ - -### Policy - -智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 - -- 确定性策略 - -## \\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} - -- 不确定性策略 - -表格形式: - -### Reward - -智能体在某个状态下执行动作的奖惩值 - -表格形式: - -## Trajectories, returns, and episodes - -- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 -- returns(回报):智能体在trajectory上所有的reward总和。 -- episodes:trajectory的一部分。 - -马尔科夫链描述trajectory: - -$s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9.$ - -returns: - -$\\mathrm{return}=0+0+0+1=1.$ - -即时收益: - -当前状态执行某个动作马上会获得的收益(奖惩值) - -未来收益: - -当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 - -## s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots - -s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 - -trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ -∈ (0, 1)。 - -引入衰减因子的return叫做disconted return。 - -$\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty,$ - -$\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots,$ - -$\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}.$ - -注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 - -### State Space - -状态空间:所有可能的状态,一个集合,可能无穷 - -### Action Space - -动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 - -注意:动作空间依赖于状态,每个状态下动作空间不一定相同 - -### 马尔科夫决策过程 - -符号描述 - -- 集合: -- 模型: - -$\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned}$ - -- 奖励概率: - -$\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned}$ - -- 策略: - -$\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned}$ - -马尔科夫决策过程: - -马尔可夫决策过程由元组(S,A,P,r,γ)组成 - -$s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots$ - -马尔科夫奖励过程/马尔科夫过程: - -$s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots$ - -未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 - -### 价值函数 - -价值函数分为两种:状态价值以及动作价值 - -状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 - -动作价值: 当前状态及动作之后所有trajectory的return的期望。 - -回顾returns: - -$G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k}$ - -价值函数的数学符号的展开表示: - -## \\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} - -价值函数的转移概率形式: - -## V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) - -价值函数的矩阵展开形式: - -$\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix}= \\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix}+\\gamma \\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} \\end{aligned}$ - -以上都是 贝尔曼公式 - -注意: - -- 贝尔曼公式不是唯一的,存在非常多种表现形式 -- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 - -### 状态价值函数 - -基于某个策略的价值函数: - -$V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\]$ - -### 动作价值函数 - -在一个状态下执行动作a的期望价值函数: - -$Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\]$ - -### 状态价值与动作价值之间的关系 - -数学上表示: - -直观理解: 当前状态的状态价值是动作价值的期望 - -注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 - -$V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a)$ - -$Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s')$ - -带策略的贝尔曼方程: - -$V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a')$ - -注意: - -- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 -- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 -- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 -- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 - -下面:如何更新策略,或者如何计算贝尔曼公式 - -## 计算贝尔曼公式 - -直接对矩阵求逆,实际使用起来不现实,这里不展示 - -实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 - -迭代算法有个基础直观认识:只要不断的提升State -Value,最终会收敛到最优的State Value - -在此之前介绍贝尔曼最优公式: $\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a)$ - -贝尔曼最优公式的作用:寻找策略提升点 - -直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all -in最大的a,所有最优贝尔曼公式多了max。 - -注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 - -如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 - -以下两种迭代算法介绍都是model -base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model -base的场景。 - -### 值迭代 - -直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State -Value,通过当前所有动作的即时收益获取到最优的Action -Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State -Value收敛。 - -伪代码: - -值迭代示例: - -## \\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots - -### 策略迭代 - -策略迭代分为两个部分:策略评估和策略提升 - -直观上:首先初始化一个策略和State -Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action -Value,得到新的策略,重复执行,不断迭代…。 - -策略评估是计算一个策略的状态价值函数 - -策略提升是选择每个状态在当前策略下即时收益最大的Action -Value - -注:策略提升是找到了策略提升点,即保证更新后的State -Value不低于旧策略的State Value - -策略提升定理的证明: - -$V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s)$ - -策略提升定理保证了新策略的State -Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 - -直观步骤示意: - -$\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\*$ - -策略迭代伪代码: - -策略迭代简单示例: - -初始条件: - -1.策略评估: - -2.策略提升: - -注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 - -上面两种算法本质上是 动态规划算法 - -### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 - -$V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime})$ - -在实际场景应用中,会存在以下问题: - -- 环境有多少状态未知 -- 当前状态执行某个动作进入每种状态的概率未知 -- 状态可能是无穷的 -- 动作可能是无穷的 -- 迭代更新策略是离散的,但某些场景是连续的。 -- 等等,一大堆 - -一般来说model -base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 - -以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model -base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model -free。 - -## 参考资料: - -### 书籍: - -- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ -- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning -- 动手学强化学习: https:// hrl.boyuai.com/ - -### 视频: - -编辑于 2025-04-22 18:07 -・北京 diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_interline.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_interline.md deleted file mode 100644 index af0dbb70..00000000 --- a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_interline.md +++ /dev/null @@ -1,384 +0,0 @@ -# 强化学习(RL)(从入门到PPO)(一、基础部分) - -## 开始前的碎碎念 - -强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 - -## 感性认识 - -首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: - -- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 -- 强化学习是个很抽象的系统内容,有on policy和off - policy,online和offline,model base和model - free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 -- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human - feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 -- LLM预训练和监督微调可以看做是单步的off line - 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off - line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on - line和on - policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 - -## 直观认识 - -### 示意图: - -智能体有3种关键要素,即感知、决策和奖励 - -- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 -- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 -- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 - -强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 - -## 马尔科夫决策过程 - -### State - -智能体当前所处的状态 - -### Action - -智能体在某个状态下可能执行的动作 - -### State transition - -智能体在某个状态下执行某个动作会进入到新的状态 - -表格形式描述状态转移矩阵: - -状态转移概率(总和为1),示意: - -$$ -\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered} -$$ - -### Policy - -智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 - -- 确定性策略 - -$$ -\\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} -$$ - -- 不确定性策略 - -表格形式: - -### Reward - -智能体在某个状态下执行动作的奖惩值 - -表格形式: - -## Trajectories, returns, and episodes - -- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 -- returns(回报):智能体在trajectory上所有的reward总和。 -- episodes:trajectory的一部分。 - -马尔科夫链描述trajectory: - -$$ -s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9. -$$ - -returns: - -$$ -\\mathrm{return}=0+0+0+1=1. -$$ - -即时收益: - -当前状态执行某个动作马上会获得的收益(奖惩值) - -未来收益: - -当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 - -$$ -s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots -$$ - -s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 - -trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ -∈ (0, 1)。 - -引入衰减因子的return叫做disconted return。 - -$$ -\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty, -$$ - -$$ -\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots, -$$ - -$$ -\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}. -$$ - -注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 - -### State Space - -状态空间:所有可能的状态,一个集合,可能无穷 - -### Action Space - -动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 - -注意:动作空间依赖于状态,每个状态下动作空间不一定相同 - -### 马尔科夫决策过程 - -符号描述 - -- 集合: -- 模型: - -$$ -\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned} -$$ - -- 奖励概率: - -$$ -\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned} -$$ - -- 策略: - -$$ -\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned} -$$ - -马尔科夫决策过程: - -马尔可夫决策过程由元组(S,A,P,r,γ)组成 - -$$ -s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots -$$ - -马尔科夫奖励过程/马尔科夫过程: - -$$ -s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots -$$ - -未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 - -### 价值函数 - -价值函数分为两种:状态价值以及动作价值 - -状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 - -动作价值: 当前状态及动作之后所有trajectory的return的期望。 - -回顾returns: - -$$ -G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k} -$$ - -价值函数的数学符号的展开表示: - -$$ -\\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} -$$ - -价值函数的转移概率形式: - -$$ -V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) -$$ - -价值函数的矩阵展开形式: - -$$ -\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix}= \\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix}+\\gamma \\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} \\end{aligned} -$$ - -以上都是 贝尔曼公式 - -注意: - -- 贝尔曼公式不是唯一的,存在非常多种表现形式 -- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 - -### 状态价值函数 - -基于某个策略的价值函数: - -$$ -V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\] -$$ - -### 动作价值函数 - -在一个状态下执行动作a的期望价值函数: - -$$ -Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\] -$$ - -### 状态价值与动作价值之间的关系 - -数学上表示: - -直观理解: 当前状态的状态价值是动作价值的期望 - -注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 - -$$ -V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a) -$$ - -$$ -Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s') -$$ - -带策略的贝尔曼方程: - -$$ -V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') -$$ - -注意: - -- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 -- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 -- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 -- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 - -下面:如何更新策略,或者如何计算贝尔曼公式 - -## 计算贝尔曼公式 - -直接对矩阵求逆,实际使用起来不现实,这里不展示 - -实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 - -迭代算法有个基础直观认识:只要不断的提升State -Value,最终会收敛到最优的State Value - -在此之前介绍贝尔曼最优公式: - -$$ -\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a) -$$ - -贝尔曼最优公式的作用:寻找策略提升点 - -直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all -in最大的a,所有最优贝尔曼公式多了max。 - -注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 - -如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 - -以下两种迭代算法介绍都是model -base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model -base的场景。 - -### 值迭代 - -直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State -Value,通过当前所有动作的即时收益获取到最优的Action -Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State -Value收敛。 - -伪代码: - -值迭代示例: - -$$ -\\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots -$$ - -### 策略迭代 - -策略迭代分为两个部分:策略评估和策略提升 - -直观上:首先初始化一个策略和State -Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action -Value,得到新的策略,重复执行,不断迭代…。 - -策略评估是计算一个策略的状态价值函数 - -策略提升是选择每个状态在当前策略下即时收益最大的Action -Value - -注:策略提升是找到了策略提升点,即保证更新后的State -Value不低于旧策略的State Value - -策略提升定理的证明: - -$$ -V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s) -$$ - -策略提升定理保证了新策略的State -Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 - -直观步骤示意: - -$$ -\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\* -$$ - -策略迭代伪代码: - -策略迭代简单示例: - -初始条件: - -1.策略评估: - -2.策略提升: - -注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 - -上面两种算法本质上是 动态规划算法 - -### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 - -$$ -V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) -$$ - -在实际场景应用中,会存在以下问题: - -- 环境有多少状态未知 -- 当前状态执行某个动作进入每种状态的概率未知 -- 状态可能是无穷的 -- 动作可能是无穷的 -- 迭代更新策略是离散的,但某些场景是连续的。 -- 等等,一大堆 - -一般来说model -base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 - -以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model -base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model -free。 - -## 参考资料: - -### 书籍: - -- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ -- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning -- 动手学强化学习: https:// hrl.boyuai.com/ - -### 视频: - -编辑于 2025-04-22 18:07 -・北京 diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_ori.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_ori.md deleted file mode 100644 index e15cb405..00000000 --- a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_ori.md +++ /dev/null @@ -1,360 +0,0 @@ -# 强化学习(RL)(从入门到PPO)(一、基础部分) - -## 开始前的碎碎念 - -强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 - -## 感性认识 - -首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: - -- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 -- 强化学习是个很抽象的系统内容,有on policy和off - policy,online和offline,model base和model - free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 -- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human - feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 -- LLM预训练和监督微调可以看做是单步的off line - 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off - line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on - line和on - policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 - -## 直观认识 - -### 示意图: - -智能体有3种关键要素,即感知、决策和奖励 - -- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 -- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 -- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 - -强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 - -## 马尔科夫决策过程 - -### State - -智能体当前所处的状态 - -### Action - -智能体在某个状态下可能执行的动作 - -### State transition - -智能体在某个状态下执行某个动作会进入到新的状态 - -表格形式描述状态转移矩阵: - -状态转移概率(总和为1),示意: - -$$ -\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered} -$$ - -### Policy - -智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 - -- 确定性策略 - -$$ -\\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} -$$ - -- 不确定性策略 - -表格形式: - -### Reward - -智能体在某个状态下执行动作的奖惩值 - -表格形式: - -## Trajectories, returns, and episodes - -- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 -- returns(回报):智能体在trajectory上所有的reward总和。 -- episodes:trajectory的一部分。 - -马尔科夫链描述trajectory: - -s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9. - -returns: - -\\mathrm{return}=0+0+0+1=1. - -即时收益: - -当前状态执行某个动作马上会获得的收益(奖惩值) - -未来收益: - -当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 - -## s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots - -s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 - -trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ -∈ (0, 1)。 - -引入衰减因子的return叫做disconted return。 - -\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty, - -\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots, - -\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}. - -注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 - -### State Space - -状态空间:所有可能的状态,一个集合,可能无穷 - -### Action Space - -动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 - -注意:动作空间依赖于状态,每个状态下动作空间不一定相同 - -### 马尔科夫决策过程 - -符号描述 - -- 集合: -- 模型: - -$$ -\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned} -$$ - -- 奖励概率: - -$$ -\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned} -$$ - -- 策略: - -$$ -\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned} -$$ - -马尔科夫决策过程: - -马尔可夫决策过程由元组(S,A,P,r,γ)组成 - -s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots - -马尔科夫奖励过程/马尔科夫过程: - -s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots - -未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 - -### 价值函数 - -价值函数分为两种:状态价值以及动作价值 - -状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 - -动作价值: 当前状态及动作之后所有trajectory的return的期望。 - -回顾returns: - -G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k} - -价值函数的数学符号的展开表示: - -$$ -\\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} -$$ - -价值函数的转移概率形式: - -## V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) - -价值函数的矩阵展开形式: - -$$ -\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} -$$ - -\= - -$$ -\\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix} -$$ - -+\\gamma - -$$ -\\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} -$$ - -$$ -\\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} -$$ - -\\end{aligned} - -以上都是 贝尔曼公式 - -注意: - -- 贝尔曼公式不是唯一的,存在非常多种表现形式 -- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 - -### 状态价值函数 - -基于某个策略的价值函数: - -V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\] - -### 动作价值函数 - -在一个状态下执行动作a的期望价值函数: - -Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\] - -### 状态价值与动作价值之间的关系 - -数学上表示: - -直观理解: 当前状态的状态价值是动作价值的期望 - -注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 - -V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a) - -Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s') - -带策略的贝尔曼方程: - -V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a') - -注意: - -- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 -- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 -- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 -- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 - -下面:如何更新策略,或者如何计算贝尔曼公式 - -## 计算贝尔曼公式 - -直接对矩阵求逆,实际使用起来不现实,这里不展示 - -实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 - -迭代算法有个基础直观认识:只要不断的提升State -Value,最终会收敛到最优的State Value - -在此之前介绍贝尔曼最优公式:\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a) - -贝尔曼最优公式的作用:寻找策略提升点 - -直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all -in最大的a,所有最优贝尔曼公式多了max。 - -注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 - -如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 - -以下两种迭代算法介绍都是model -base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model -base的场景。 - -### 值迭代 - -直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State -Value,通过当前所有动作的即时收益获取到最优的Action -Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State -Value收敛。 - -伪代码: - -值迭代示例: - -## \\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots - -### 策略迭代 - -策略迭代分为两个部分:策略评估和策略提升 - -直观上:首先初始化一个策略和State -Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action -Value,得到新的策略,重复执行,不断迭代…。 - -策略评估是计算一个策略的状态价值函数 - -策略提升是选择每个状态在当前策略下即时收益最大的Action -Value - -注:策略提升是找到了策略提升点,即保证更新后的State -Value不低于旧策略的State Value - -策略提升定理的证明: - -V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s) - -策略提升定理保证了新策略的State -Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 - -直观步骤示意: - -\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\* - -策略迭代伪代码: - -策略迭代简单示例: - -初始条件: - -1.策略评估: - -2.策略提升: - -注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 - -上面两种算法本质上是 动态规划算法 - -### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 - -V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) - -在实际场景应用中,会存在以下问题: - -- 环境有多少状态未知 -- 当前状态执行某个动作进入每种状态的概率未知 -- 状态可能是无穷的 -- 动作可能是无穷的 -- 迭代更新策略是离散的,但某些场景是连续的。 -- 等等,一大堆 - -一般来说model -base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 - -以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model -base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model -free。 - -## 参考资料: - -### 书籍: - -- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ -- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning -- 动手学强化学习: https:// hrl.boyuai.com/ - -### 视频: - -编辑于 2025-04-22 18:07 -・北京 diff --git a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_v2.md b/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_v2.md deleted file mode 100644 index bb52789c..00000000 --- a/tests/llm_web_kit/extractor/zhihucase1/output_zhihu_custom_tag_v2.md +++ /dev/null @@ -1,328 +0,0 @@ -# 强化学习(RL)(从入门到PPO)(一、基础部分) - -## 开始前的碎碎念 - -强化学习的内容也是对之前学习强化学习课程所记录的笔记进行的归纳总结,由于内容较多,整体分为多个篇章,强化学习涉及到的理论比较抽象,以下所涉及到的内容来源为李宏毅、赵世钰、张伟楠三位教授的课程以及对应的书籍,对应的网页链接放置在文末的参考文献部分。由于本篇内容主要是强化学习笔记的总结部分,不会对每个公式都进行讲解,主要是梳理强化学习学习的脉络,如果需要系统的学习,还是推荐上面提到的三位教授的课程,由于本人是从事LLM方向,所以内容上会侧重RL在LLM训练上的应用,主体脉络也是这个方向。 - -## 感性认识 - -首先在学习之前写一下对强化学习的感性认知,主要是有个感性认识: - -- 强化学习可以理解为强化和学习,其中学习就代表这训练,从真实实践或者自己或者专家经验中来学习,强化代表行为策略的强化,通过学习不断强化在某个状态下选择合适行为策略。 -- 强化学习是个很抽象的系统内容,有on policy和off - policy,online和offline,model base和model - free等多种分类方式,有些学者认为特定条件下的学习属于强化学习,也有学者认为强化学习范围十分宽泛,包括目前神经网络的训练也属于强化学习,后面会展开讨论。 -- 强化学习在LLM中应用越来越大,最早是RLHF的训练,也叫做基于人类反馈的强化学习,之所以带上HF是因为其对齐目标是人类价值观,人类价值观是很难通过一个特定规则的监督模型来给出奖惩值,因此通过人来标注反馈数据去训练一个奖励模型,但随着LLM发展,openai发布了o1和o3模型,其中下游任务中直接使用强化学习进行训练,也叫做RFT或者RLFT(强化学习微调),之所以不需要HF(human - feedback)是因为在代码或者逻辑数学运算,再或者其他下游任务下天然是存在奖励模型的,如生成的代码是否可以运行,数学运算结果是否正确,Agent任务是否执行成功等等,在这些任务上是不需要人类反馈去训练奖励模型的。 -- LLM预训练和监督微调可以看做是单步的off line - 和 off policy的强化学习,o1的长思维链及agent的长action提示词可以看做是多步的off - line 和 off policy的强化学习。在强化学习中只进行offline训练没有进行on - line和on - policy训练会造agent拟合到数据经验中从而形成与真实世界的GAP,这也是LLM为什么经常出现幻觉的原因。 - -## 直观认识 - -### 示意图: - -智能体有3种关键要素,即感知、决策和奖励 - -- 感知。智能体在某种程度上感知环境的状态,从而知道自己所处的现状。例如,下围棋的智能体感知当前的棋盘情况;无人车感知周围道路的车辆、行人和红绿灯等情况;机器狗通过摄像头感知面前的图像,通过脚底的力学传感器来感知地面的摩擦功率和倾斜度等情况。 -- 智能体根据当前的状态计算出达到目标需要采取的动作的过程叫作决策。例如,针对当前的棋盘决定下一颗落子的位置;针对当前的路况,无人车计算出方向盘的角度和刹车、油门的力度;针对当前收集到的视觉和力觉信号,机器狗给出4条腿的齿轮的角速度。策略是智能体最终体现出的智能形式,是不同智能体之间的核心区别。 -- 奖励。环境根据状态和智能体采取的动作,产生一个标量信号作为奖励反馈。这个标量信号衡量智能体这一轮动作的好坏。例如,围棋博弈是否胜利;无人车是否安全、平稳且快速地行驶;机器狗是否在前进而没有摔倒。最大化累积奖励期望是智能体提升策略的目标,也是衡量智能体策略好坏的关键指标。 - -强化学习的目标:在当前状态下找到一个最优策略到达目标状态。 - -## 马尔科夫决策过程 - -### State - -智能体当前所处的状态 - -### Action - -智能体在某个状态下可能执行的动作 - -### State transition - -智能体在某个状态下执行某个动作会进入到新的状态 - -表格形式描述状态转移矩阵: - -状态转移概率(总和为1),示意: - -$\\begin{gathered} p(s\_{1}|s\_{1},a\_{2})=0, \\ p(s\_{2}|s\_{1},a\_{2})=1, \\ p(s\_{3}|s\_{1},a\_{2})=0, \\ p(s\_{4}|s\_{1},a\_{2})=0, \\ p(s\_{5}|s\_{1},a\_{2})=0, \\end{gathered}$ - -### Policy - -智能体在每个状态下所有可能执行的动作的概率,一般分为确定性的策略和不确定性策略 - -- 确定性策略 - -## \\begin{gathered} \\pi(a_1|s_1)=0, \\ \\pi(a\_{2}|s\_{1})=1, \\ \\pi(a\_{3}|s\_{1})=0, \\ \\pi(a\_{4}|s\_{1})=0, \\ \\pi(a\_{5}|s\_{1})=0, \\end{gathered} - -- 不确定性策略 - -表格形式: - -### Reward - -智能体在某个状态下执行动作的奖惩值 - -表格形式: - -## Trajectories, returns, and episodes - -- trajectory:智能体在某一初始状态开始进行状态转移,最终到达target状态的路径。 -- returns(回报):智能体在trajectory上所有的reward总和。 -- episodes:trajectory的一部分。 - -马尔科夫链描述trajectory: - -$s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9.$ - -returns: - -$\\mathrm{return}=0+0+0+1=1.$ - -即时收益: - -当前状态执行某个动作马上会获得的收益(奖惩值) - -未来收益: - -当前状态执行某个动作后进入了新的状态,新的状态之后的收益总和 - -## s_1\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_2\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_5\\overset{a_3}{\\operatorname\*{\\longrightarrow}}s_8\\overset{a_2}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\overset{a_5}{\\operatorname\*{\\longrightarrow}}s_9\\ldots - -s1状态执行动作a2后即时收益是0,未来收益是s2之后的收益 - -trajectory可能是无限长或者很长的,这样return会非常大,但未来收益相对于即时收益来说重要性要小一些,因此引入一个超参数γ作为衰减因子,γ -∈ (0, 1)。 - -引入衰减因子的return叫做disconted return。 - -$\\mathrm{return}=0+0+0+1+1+1+\\cdots=\\infty,$ - -$\\text{discounted return}=0+\\gamma0+\\gamma^20+\\gamma^31+\\gamma^41+\\gamma^51+\\ldots,$ - -$\\text{discounted return}=\\gamma^3(1+\\gamma+\\gamma^2+\\ldots)=\\gamma^3\\frac{1}{1-\\gamma}.$ - -注:当衰减因子越接近于1越关注未来收益,越接近于0越关注当前收益。 - -### State Space - -状态空间:所有可能的状态,一个集合,可能无穷 - -### Action Space - -动作空间:依赖于状态,某个状态下所有的动作,也是一个集合,可能无穷 - -注意:动作空间依赖于状态,每个状态下动作空间不一定相同 - -### 马尔科夫决策过程 - -符号描述 - -- 集合: -- 模型: - -$\\begin{aligned} & p(s^{\\prime}|s,a) \\ & \\sum\_{s^{\\prime}\\in\\mathcal{S}}p(s^{\\prime}|s,a)=1\\text{ for any }(s,a). \\end{aligned}$ - -- 奖励概率: - -$\\begin{aligned} & p(r|s,a) \\ & \\sum\_{r\\in\\mathcal{R}(s,a)}p(r|s,a)=1\\text{ for any }(s,a). \\end{aligned}$ - -- 策略: - -$\\begin{aligned} \\pi(a|s) \\ \\sum\_{a\\in\\mathcal{A}(s)}\\pi(a|s) & =1\\text{ for any }s\\in\\mathcal{S}. \\end{aligned}$ - -马尔科夫决策过程: - -马尔可夫决策过程由元组(S,A,P,r,γ)组成 - -$s_1\\xrightarrow{a_2}s_2\\xrightarrow{a_3}s_5\\xrightarrow{a_3}s_8\\xrightarrow{a_2}s_9\\xrightarrow{a_5}s_9\\xrightarrow{a_5}s_9\\ldots$ - -马尔科夫奖励过程/马尔科夫过程: - -$s_1 \\xrightarrow\[r=0\]{a_2} s_2 \\xrightarrow\[r=0\]{a_3} s_5 \\xrightarrow\[r=0\]{a_3} s_8 \\xrightarrow\[r=1\]{a_2} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\xrightarrow\[r=1\]{a_5} s_9 \\ldots$ - -未来的状态和收益只依赖于当前状态和动作,与以前的动作和状态无关。 - -### 价值函数 - -价值函数分为两种:状态价值以及动作价值 - -状态价值: 从当前状态到目标状态的所有trajectory的return的期望。 - -动作价值: 当前状态及动作之后所有trajectory的return的期望。 - -回顾returns: - -$G_t=R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\cdots=\\sum\_{k=0}^\\infty\\gamma^kR\_{t+k}$ - -价值函数的数学符号的展开表示: - -## \\begin{aligned} V(s) & =\\mathbb{E}\[G_t|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma R\_{t+1}+\\gamma^2R\_{t+2}+\\ldots|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma(R\_{t+1}+\\gamma R\_{t+2}+\\ldots)|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma G\_{t+1}|S_t=s\] \\ & =\\mathbb{E}\[R_t+\\gamma V(S\_{t+1})|S_t=s\] \\end{aligned} - -价值函数的转移概率形式: - -## V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime}) - -价值函数的矩阵展开形式: - -$\\begin{aligned} \\mathcal{V}=\\mathcal{R}+\\gamma\\mathcal{PV} \\ \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix}= \\begin{bmatrix} r(s_1) \\ r(s_2) \\ \\cdots \\ r(s_n) \\end{bmatrix}+\\gamma \\begin{bmatrix} P(s_1|s_1) & p(s_2|s_1) & \\ldots & P(s_n|s_1) \\ P(s_1|s_2) & P(s_2|s_2) & \\ldots & P(s_n|s_2) \\ \\cdots \\ P(s_1|s_n) & P(s_2|s_n) & \\ldots & P(s_n|s_n) \\end{bmatrix} \\begin{bmatrix} V(s_1) \\ V(s_2) \\ \\cdots \\ V(s_n) \\end{bmatrix} \\end{aligned}$ - -以上都是 贝尔曼公式 - -注意: - -- 贝尔曼公式不是唯一的,存在非常多种表现形式 -- 贝尔曼公式不是针对单独状态的,每个状态都存在一个贝尔曼公式 - -### 状态价值函数 - -基于某个策略的价值函数: - -$V^\\pi(s)=\\mathbb{E}\_\\pi\[G_t|S_t=s\]$ - -### 动作价值函数 - -在一个状态下执行动作a的期望价值函数: - -$Q^\\pi(s,a)=\\mathbb{E}\_\\pi\[G_t|S_t=s,A_t=a\]$ - -### 状态价值与动作价值之间的关系 - -数学上表示: - -直观理解: 当前状态的状态价值是动作价值的期望 - -注意:状态价值是一个期望,动作价值中当前收益是常数,后面的其他状态的状态价值是一个期望。 - -$V^\\pi(s) = \\sum\_{a \\in A} \\pi(a|s)Q^\\pi(s,a)$ - -$Q^\\pi(s,a) = r(s,a) + \\gamma \\sum\_{s' \\in S} P(s'|s,a)V^\\pi(s')$ - -带策略的贝尔曼方程: - -$V^\\pi(s) = \\mathbb{E}_\\pi\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ = \\sum\_{a \\in A} \\pi(a|s) \\left(r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a)V^\\pi(s')\\right) \\ Q^\\pi(s,a) = \\mathbb{E}_\\pi\[R_t + \\gamma Q^\\pi(S_{t+1}, A\_{t+1})|S_t = s, A_t = a\] \\ = r(s,a) + \\gamma \\sum\_{s' \\in S} p(s'|s,a) \\sum\_{a' \\in A} \\pi(a'|s')Q^\\pi(s',a')$ - -注意: - -- 状态价值函数的作用是对当前策略进行评估。动作价值函数的作用可以看做对于当前状态执行某个动作的评估。 -- 强化学习的目标是找到一个最优策略,这是一个迭代的过程,如何评估策略每次更好了一些,就通过状态价值函数来体现。 -- 我们的目标是在当前策略下根据贝尔曼公式求得所有状态的状态价值,根据状态价值来找到策略提升点从而更新一个新的策略,在这个新的策略下再次计算状态价值,不断迭代,直到状态价值收敛。 -- 最终的最优策略是状态价值收敛,注意最优策略不是唯一的。 - -下面:如何更新策略,或者如何计算贝尔曼公式 - -## 计算贝尔曼公式 - -直接对矩阵求逆,实际使用起来不现实,这里不展示 - -实际中是使用迭代算法进行求解,分为两种, 值迭代 和 策略迭代 - -迭代算法有个基础直观认识:只要不断的提升State -Value,最终会收敛到最优的State Value - -在此之前介绍贝尔曼最优公式: $\\text{For every } s \\in \\mathcal{S}, \\text{ the elementwise expression of the BOE is} \\ v(s) = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s) \\left(\\sum\_{r \\in \\mathcal{R}} p(r|s,a)r + \\gamma \\sum\_{s' \\in \\mathcal{S}} p(s'|s,a)v(s')\\right) \\ = \\max\_{\\pi(s) \\in \\Pi(s)} \\sum\_{a \\in A} \\pi(a|s)q(s,a)$ - -贝尔曼最优公式的作用:寻找策略提升点 - -直观理解:当前s有n个动作,每个动作都有自己的q(s,a),如何选择a可以使得v(s)最大【我们的目标就是最大化v】,毫无疑问是all -in最大的a,所有最优贝尔曼公式多了max。 - -注意:上面迭代公式在数学上通过不动点理论及收敛定理可以求得是存在唯一解,也就是最优的s是存在的,即存在最优的状态价值,但策略不一定唯一。 - -如何迭代的去求解出最优的s,即最优的状态价值,可以通过值迭代和策略迭代两种方法。 - -以下两种迭代算法介绍都是model -base的,即站在了上帝视角,知道所有状态及每个状态执行每个动作会进入哪个状态,同时也知道在某个状态下执行某个动作得到的奖励值。在现实中基本上不存在model -base的场景。 - -### 值迭代 - -直观理解: 为每个状态初始化一个Value,一般均设置为0,迭代计算出每个状态的State -Value,通过当前所有动作的即时收益获取到最优的Action -Value并使用贝尔曼最优公式进行更新,循环迭代,直到每个状态的State -Value收敛。 - -伪代码: - -值迭代示例: - -## \\diamond ; k = 0: \\ \\text{Without loss of generality, select the initial values as } v_0(s_1) = v_0(s_2) = v_0(s_3) = v_0(s_4) = 0. \\\[1em\] v_1(s_1) = 0, \\quad v_1(s_2) = 1, \\quad v_1(s_3) = 1, \\quad v_1(s_4) = 1. \\\[1em\] \\diamond ; k = 1: \\ \\textit{q-value calculation}: \\text{ Substituting } v_1(s_i) \\text{ into Table 4.1 yields the q-values shown in Table 4.3.} \\\[0.5em\] \\textit{Policy update}: \\pi_2 \\text{ is obtained by selecting the greatest q-values:} \\\[0.5em\] \\pi_2(a_3|s_1) = 1, \\quad \\pi_2(a_3|s_2) = 1, \\quad \\pi_2(a_2|s_3) = 1, \\quad \\pi_2(a_5|s_4) = 1. \\\[0.5em\] \\text{This policy is visualized in Figure 4.2 (the right subfigure).} \\\[0.5em\] \\textit{Value update}: v_2 \\text{ is obtained by updating the v-value to the greatest q-value for each state:} \\\[0.5em\] v_2(s_1) = \\gamma1, \\quad v_2(s_2) = 1 + \\gamma1, \\quad v_2(s_3) = 1 + \\gamma1, \\quad v_2(s_4) = 1 + \\gamma1. \\\[1em\] \\diamond ; k = 2,3,4,\\ldots - -### 策略迭代 - -策略迭代分为两个部分:策略评估和策略提升 - -直观上:首先初始化一个策略和State -Value,计算当前策略下所有策略最优State Value,贪心选择最优的Action -Value,得到新的策略,重复执行,不断迭代…。 - -策略评估是计算一个策略的状态价值函数 - -策略提升是选择每个状态在当前策略下即时收益最大的Action -Value - -注:策略提升是找到了策略提升点,即保证更新后的State -Value不低于旧策略的State Value - -策略提升定理的证明: - -$V^\\pi(s) \\leq Q^\\pi(s,\\pi'(s)) \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma V^\\pi(S_{t+1})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma Q^\\pi(S_{t+1},\\pi'(S\_{t+1}))|S_t = s\] \\ = \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 V^\\pi(S\_{t+2})|S_t = s\] \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 V^\\pi(S\_{t+3})|S_t = s\] \\ \\vdots \\ \\leq \\mathbb{E}_{\\pi'}\[R_t + \\gamma R_{t+1} + \\gamma^2 R\_{t+2} + \\gamma^3 R\_{t+3} + \\cdots|S_t = s\] \\ = V^{\\pi'}(s)$ - -策略提升定理保证了新策略的State -Value一定优于旧策略,则不断的进行策略提升最终会得到最优策略。 - -直观步骤示意: - -$\\pi^0 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^0} \\xrightarrow\[\\text{策略提升}\]{} \\pi^1 \\xrightarrow\[\\text{策略评估}\]{} V^{\\pi^1} \\xrightarrow\[\\text{策略提升}\]{} \\pi^2 \\xrightarrow\[\\text{策略评估}\]{} \\cdots \\xrightarrow\[\\text{策略提升}\]{} \\pi^\*$ - -策略迭代伪代码: - -策略迭代简单示例: - -初始条件: - -1.策略评估: - -2.策略提升: - -注意:实际上值迭代是策略迭代的一种特殊情况,由此还会引申出介于2者之间的迭代方法,不再赘述。 - -上面两种算法本质上是 动态规划算法 - -### 值迭代与策略迭代来更新策略的最终获得最优策略的方法是建立在存在环境状态转移模型基础上,说人话就是开了挂,站在了上帝视角,即目前知道有多少个状态,目标状态是什么,每个状态执行一个动作后会进入哪种新的状态。并且知道每个状态有多少动作。在贝尔曼公式中p(s’|s)是未知的。 - -$V(s)=r(s)+\\gamma\\sum\_{s^{\\prime}\\in S}p(s^{\\prime}|s)V(s^{\\prime})$ - -在实际场景应用中,会存在以下问题: - -- 环境有多少状态未知 -- 当前状态执行某个动作进入每种状态的概率未知 -- 状态可能是无穷的 -- 动作可能是无穷的 -- 迭代更新策略是离散的,但某些场景是连续的。 -- 等等,一大堆 - -一般来说model -base的值迭代和策略迭代方案在实际需求中是无法使用的,根本原因在于状态转移概率的不确定性,但如果使用世界模型来代替实际环境,世界模型可以预测出所有的状态转移情况,值迭代和策略迭代也是很高效方案,缺点在于世界模型作为环境模型会造成误差,这种误差会随时间步逐渐增大,最终训练失败。 - -以上内容是RL的最基础的部分,熟悉每个术语是读懂后续高阶RL算法的前提,本文中介绍的值迭代和策略迭代进行策略更新的方案都是基于model -base的,但实际场景中很少存在model base的情况,因此下一章节开始介绍model -free。 - -## 参考资料: - -### 书籍: - -- 蘑菇书: https:// datawhalechina.github.io /easy-rl/#/ -- 强化学习的数学原理: https:// github.com/MathFoundati onRL/Book-Mathematical-Foundation-of-Reinforcement-Learning -- 动手学强化学习: https:// hrl.boyuai.com/ - -### 视频: - -编辑于 2025-04-22 18:07 -・北京 From 48015fdf0be1e9a33347a5e1028664e15919d5f8 Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Fri, 13 Jun 2025 11:36:18 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20.gitignore=EF=BC=8C?= =?UTF-8?q?=E5=BF=BD=E7=95=A5=20extractor=20=E7=9B=AE=E5=BD=95=E4=B8=8B?= =?UTF-8?q?=E7=9A=84=20.md=20=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | Bin 451 -> 571 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.gitignore b/.gitignore index eb56537a2636209d031e16425b572fdf5e05d264..2085cd6952ae28f3ce2ce7f1a883814c2c1305e9 100644 GIT binary patch literal 571 zcmYjNL5_qV5adk4A58W}B79s#H3RHj`7T^+VMFHAcl&Otg+s>xOlN znhT$fWdP%?(|~l;DK7oya`AC#V`6A3YcpRgSP4!vu$n$ z{djxQDaq3zXSBLGp<1s$S}$Lp0Bkf_SvN#SG|WzQ32 zaNd9&eW5&VzP3Oa_!>U;VF|5M-65|8^82Q5ta(Ug0Vd=0ld9w8lX)u?W| zsjgDs%H%6WAjYJhe$tQ7rJv_9 Date: Fri, 13 Jun 2025 11:41:01 +0800 Subject: [PATCH 04/14] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20.gitignore=EF=BC=8C?= =?UTF-8?q?=E5=8F=AA=E5=BF=BD=E7=95=A5=20extractor/ygq=5Ftestmd=20?= =?UTF-8?q?=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | Bin 571 -> 546 bytes .../good_data/output_expected/1.md | 40 +++ .../good_data/output_expected/csdn_lineno.md | 299 ++++++++++++++++++ .../recognizer/assets/cccode/mathworks.md | 97 ++++++ 4 files changed, 436 insertions(+) create mode 100644 tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/1.md create mode 100644 tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/csdn_lineno.md create mode 100644 tests/llm_web_kit/extractor/html/recognizer/assets/cccode/mathworks.md diff --git a/.gitignore b/.gitignore index 2085cd6952ae28f3ce2ce7f1a883814c2c1305e9..64ee37f1d74f419a20db9090e21fe96c2c624cce 100644 GIT binary patch delta 36 qcmdnZvWR8FCC16$87(9#8PXXF8R8jA7*ZLEfn+X23Ii_#7Xtv)%n2L- delta 60 zcmZ3)vYTbYB}QdlF8z|!;*w(hoSfYF^31234567 + +- 1 +- 2 + +- 1 + - 1.1 + - 1.2 +- 2 + - 2.1 + - 2.2 + +$$ +x=\frac{-b±\sqrt{{b}^{2}-4ac}}{2a}\text{.} +$$ + +```js +const Prism = require('prismjs'); + + // The code snippet you want to highlight, as a string + const code = `var data = 1;`; + + // Returns a highlighted HTML string + const html = Prism.highlight(code, Prism.languages.javascript, 'javascript'); +``` + +1. 100 +2. 200 + +reference: `#include` diff --git a/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/csdn_lineno.md b/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/csdn_lineno.md new file mode 100644 index 00000000..b6d3fdaf --- /dev/null +++ b/tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/output_expected/csdn_lineno.md @@ -0,0 +1,299 @@ +<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> +<% +String path = request.getContextPath(); +String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; +%> + + + + + My JSP 'register.jsp' starting page + + + + + +
+ + 用户名:
+ 密 码:
+ + 登录 +
+ + + + + + +package com.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.dao.UsersDao; + +public class servlet3 extends HttpServlet { + + public servlet3() { + super(); + } + + + public void destroy() { + super.destroy(); // Just puts "destroy" string in log + // Put your code here + } + + + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + doPost (request, response); + + } + + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + String uname = request.getParameter("uname"); + String upwd = request.getParameter("upwd"); + UsersDao usersDao = new UsersDao(); + int i=usersDao.reg(uname, upwd); + if(i>0){ + + response.setHeader("refresh", "2;url=login.jsp"); + }else{ + + response.setHeader("refresh", "2;url=reg.jsp"); + } + } + + /** + * Initialization of the servlet.
+ * + * @throws ServletException if an error occurs + */ + public void init() throws ServletException { + // Put your code here + } + +} + + + + + +package com.sf.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.sf.dao.MsgDao; +import com.sf.dao.UsersDao; + +public class Doregservlet extends HttpServlet { + + /** + * Constructor of the object. + */ + public Doregservlet() { + super(); + } + + /** + * Destruction of the servlet.
+ */ + public void destroy() { + super.destroy(); // Just puts "destroy" string in log + // Put your code here + } + + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + request.setCharacterEncoding("utf-8"); + String uname = request.getParameter("uname"); + String upwd = request.getParameter("upwd"); + + UsersDao ud = new UsersDao(); + MsgDao md = new MsgDao(); + if (ud.register(uname, upwd) > 0) { + request.getSession().setAttribute("uname", uname); + request.getRequestDispatcher("denglu.jsp").forward(request, + response); + } else { + out.print("注册失败,请重新注册......."); + response.setHeader("refresh", "3;url=reg.jsp"); + } + } + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + doGet(request,response); + } + + /** + * Initialization of the servlet.
+ * + * @throws ServletException if an error occurs + */ + public void init() throws ServletException { + // Put your code here + } + +} + + + + + +package com.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.dao.MsgDao; + +public class servlet5 extends HttpServlet { + + public servlet5() { + super(); + } + + public void destroy() { + super.destroy(); // Just puts "destroy" string in log + // Put your code here + } + + + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + doPost(request, response); + } + + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + request.setCharacterEncoding("utf-8"); + + int id=Integer.parseInt(request.getParameter("id")); + MsgDao md=new MsgDao(); + md.delMail(id); + response.getWriter().print("刪除成功....."); + response.setHeader("refresh", "2;url=main.jsp"); + response.sendRedirect("main2.jsp"); + } + + + public void init() throws ServletException { + + } + +} + + + + + + + +package com.sf.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.sf.dao.MsgDao; +import com.sf.entity.Msg; + +public class Dowriteservlet extends HttpServlet { + + /** + * Constructor of the object. + */ + public Dowriteservlet() { + super(); + } + + /** + * Destruction of the servlet.
+ */ + public void destroy() { + super.destroy(); // Just puts "destroy" string in log + // Put your code here + } + + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + request.setCharacterEncoding("utf-8"); + String uname = (String) request.getSession().getAttribute("uname"); + String sendto = request.getParameter("receiver"); + String title = request.getParameter("title"); + String content = request.getParameter("content"); + + Msg m = new Msg(); + m.setMsgcontent(content); + m.setUsername(uname); + m.setSendto(sendto); + m.setTitle(title); + + MsgDao md = new MsgDao(); + md.addMsg(m); + + out.print("发送成功....."); + response.setHeader("refresh", "3;url=main.jsp"); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + doGet(request,response); } + + /** + * Initialization of the servlet.
+ * + * @throws ServletException if an error occurs + */ + public void init() throws ServletException { + } + +} \ No newline at end of file diff --git a/tests/llm_web_kit/extractor/html/recognizer/assets/cccode/mathworks.md b/tests/llm_web_kit/extractor/html/recognizer/assets/cccode/mathworks.md new file mode 100644 index 00000000..5eda3398 --- /dev/null +++ b/tests/llm_web_kit/extractor/html/recognizer/assets/cccode/mathworks.md @@ -0,0 +1,97 @@ +主要内容 + +## Single-Precision Conversion Best Practices + +### Use Integers for Index Variables + +In MATLAB® code that you want to convert to single precision, +it is a best practice to use integers for index variables. However, +if the code does not use integers for index variables, when possible `convertToSingle` tries to detect +the index variables and select `int32` types for +them. + +### Limit Use of `assert` Statements + +- Do not use `assert` statements +to define the properties of input arguments. +- Do not use `assert` statements +to test the type of a variable. For example, do not use + +``` +assert(isa(a, 'double')) +``` + +### Initialize MATLAB Class Properties in Constructor + +Do not initialize MATLAB class properties in the `properties` block. +Instead, use the constructor to initialize the class properties. + +### Provide a Test File That Calls Your MATLAB Function + +Separate your core algorithm from other code that you use to +test and verify the results. Create a test file that calls your double-precision MATLAB algorithm. +You can use the test file to: + +- Automatically define properties of the top-level function +inputs. +- Verify that the double-precision algorithm behaves +as you expect. The double-precision behavior is the baseline against +which you compare the behavior of the single-precision versions of +your algorithm. +- Compare the behavior of the single-precision version +of your algorithm to the double-precision baseline. + +For best results, the test file must exercise the algorithm +over its full operating range. + +### Prepare Your Code for Code Generation + +MATLAB code that you want to convert to single precision +must comply with code generation requirements. See MATLAB Language Features Supported for C/C++ Code Generation. + +To help you identify unsupported functions or constructs in +your MATLAB code, add the `%#codegen` pragma +to the top of your MATLAB file. When you edit your code in the MATLAB editor, +the MATLAB Code Analyzer flags functions and constructs that +are not supported for code generation. See Check Code Using the MATLAB Code Analyzer. When you use the MATLAB + Coder™ app, +the app screens your code for code generation readiness. At the function +line, you can use the Code Generation Readiness Tool. See Check Code Using the Code Generation Readiness Tool. + +### Use the `-args` Option to Specify Input Properties + +When you generate single-precision MATLAB code, if you +specify a test file, you do not have to specify argument properties +with the `-args` option. In this case, the code generator +runs the test file to determine the properties of the input types. +However, running the test file can slow the code generation. It is +a best practice to pass the properties to the `-args` option +so that `convertToSingle` does not run the test +file to determine the argument properties. If you have a MATLAB + Coder license, +you can use `coder.getArgTypes` to determine the +argument properties. For example: + +``` +types = coder.getArgTypes('myfun_test', 'myfun'); +scfg = coder.config('single'); +convertToSingle -config scfg -args types myfun +``` + +### Test Numerics and Log I/O Data + +When you use the convertToSingle function to +generate single-precision MATLAB code, enable numerics testing +and I/O data logging for comparison plots. To use numerics testing, +you must provide a test file that calls your MATLAB function. +To enable numerics testing and I/O data logging, create a `coder.SingleConfig` object. +Set the `TestBenchName` , `TestNumerics` , +and `LogIOForComparisonPlotting` properties. For +example: + +``` +scfg = coder.config('single'); +scfg.TestBenchName = 'mytest'; +scfg.TestNumerics = true; +scfg.LogIOForComparisonPlotting = true; +``` From 9dac241b79145534235af58781e5f299eb7a52e7 Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Fri, 13 Jun 2025 11:51:19 +0800 Subject: [PATCH 05/14] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A3=8E=E6=A0=BC?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | Bin 546 -> 455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.gitignore b/.gitignore index 64ee37f1d74f419a20db9090e21fe96c2c624cce..44fad1ccd5395fbf594525d2623c005c5805fb0e 100644 GIT binary patch literal 455 zcmYk3L2|<&3`O_<6rSmh8N(%dgB*atpkNv>6tENL^p%^q(~U;@Ncs;DvYQHVMCqTL zJVU`o%O(^!HiZ_~nsm&b^O&uPx>M_1K4 zaAd$8ea_3cQEeeFz{l`vALh_{t~=zJKz?5Ig*Ad?7GN??-*a_rKA9U6m{Qb>MxawF z3jH1aAl@(v*;ef+Wk{RZ+<2-MhkYw^SRRARwn0y8h>kZSuf43D=>9gc+?o#n literal 546 zcmYjNQI3Nk6nx(#+`(l3OvFp<4SE2AK$R^B3utRkzeBrjHb&>YH#3jHUR0Gi>l=vq z*{Y{Yob7hq`;jw10TjRGAbp%$G7o7afh|%-Ct0 zq3(rC$2x&=#wtKM>Jrysx4F2uwsjxf+te+t)R+&5{wma;7kR-LI|afu!=#M7$81`f zN!?zVR7&GJVb58$Az@n2FRW*--vVsZSsEK6NF5PJ%{Jo(??fdnZhdqn>Vo@DoAc&% z3~XCXTSe3NI4~q!=xsTdbXVtF*t18TvlNfuo7npx?{<7#XFM66G+k>vk7=32WCX~0A^C8ff3b62hH>i&FK$h Hh!dk9(Hoqm From c85498acc0753fdb9632fda7a6dad75241e42bc9 Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Fri, 13 Jun 2025 18:33:19 +0800 Subject: [PATCH 06/14] =?UTF-8?q?1.=20csdn=E5=85=AC=E5=BC=8F=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=88=A4=E6=96=AD=E6=95=B4=E5=90=88=E5=88=B0common?= =?UTF-8?q?=E4=B8=AD=202.=20=E7=9F=A5=E4=B9=8E=E5=85=AC=E5=BC=8F=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=88=A4=E6=96=AD=E4=BD=BF=E7=94=A8=E7=8E=B0=E6=9C=89?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../html/recognizer/cc_math/common.py | 15 +++ .../html/recognizer/cc_math/tag_script.py | 91 ++++++++----------- .../extractor/html/recognizer/ccmath.py | 2 + .../extractor/test_extractor_chain.py | 13 +-- 4 files changed, 61 insertions(+), 60 deletions(-) diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/common.py b/llm_web_kit/extractor/html/recognizer/cc_math/common.py index 4db57343..f79b1bbf 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/common.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/common.py @@ -272,6 +272,21 @@ def check_delimiters(delims_list, s): if (sub_elements and any(text_strip(elem.text) for elem in sub_elements)) or \ (sup_elements and any(text_strip(elem.text) for elem in sup_elements)): result.append((EQUATION_INLINE, MathType.HTMLMATH)) + + # 检查当前节点是否是katex-mathml元素(CSDN) + if node.tag == 'span' and node.get('class') == 'katex-mathml': + parent = node.getparent() + while parent is not None: + parent_class = parent.get('class') or '' + if 'katex--inline' in parent_class: + result.append((EQUATION_INLINE, MathType.LATEX)) + break + if 'katex--display' in parent_class: + result.append((EQUATION_INTERLINE, MathType.LATEX)) + break + parent = parent.getparent() + else: + result.append((EQUATION_INLINE, MathType.LATEX)) return self.equation_type_to_tag(result) def equation_type_to_tag(self, type_math_type: List[Tuple[str, str]]) -> List[Tuple[str, str]]: diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py index 3fc0223e..6914f672 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py @@ -59,64 +59,53 @@ def extract_katex_formula(text: str) -> Dict[str, str]: def process_katex_mathml(cm, math_render, node): - # 只处理katex-mathml节点 - if node.tag == 'span' and node.get('class') == 'katex-mathml': - # 向上查找父节点,判断行内/行间 - parent = node.getparent() - is_inline = False - while parent is not None: - parent_class = parent.get('class') or '' - if 'katex--inline' in parent_class: - is_inline = True - break - if 'katex--display' in parent_class: - is_inline = False - break - parent = parent.getparent() - # 提取latex公式(取最后一行非空内容) - lines = [line.strip() for line in node.text_content().splitlines() if line.strip()] - if not lines: - return - latex = lines[-1] - tag = 'ccmath-inline' if is_inline else 'ccmath-interline' - new_span = build_cc_element( - html_tag_name=tag, - text=cm.wrap_math_md(latex), - tail=None, - type='latex', - by=math_render, - html=None - ) - replace_element(node, new_span) - - -def process_zhihu_custom_tag(cm, math_render, node): - """知乎公式逻辑比较特殊:所有公式都是行内公式,通过双斜杠进行换行,md渲染时需要begin{aligned}和end{aligned}包裹.""" try: - # 从data-tex属性获取LaTeX公式 - latex = node.get('data-tex') - if latex: - if r'\\' in latex: - # 检查是否已经被\begin{*}和\end{*}包裹 - is_already_aligned = re.search(r'\\begin\{[^}]+\}.*\\end\{[^}]+\}', latex, re.DOTALL) is not None - if not is_already_aligned: - lines = re.split(r'\\\\[\s]*', latex) - # 为每一行添加 & 符号使其左对齐(匹配知乎逻辑) - formatted_lines = [] - for line in lines: - line = line.strip() - if not line.startswith('&'): - line = '&' + line - formatted_lines.append(line) - latex = r'\begin{aligned} ' + r' \\ '.join(formatted_lines) + r' \end{aligned}' + # 只处理katex-mathml节点 + if node.tag == 'span' and node.get('class') == 'katex-mathml': + # 提取latex公式(取最后一行非空内容) + lines = [line.strip() for line in node.text_content().splitlines() if line.strip()] + if not lines: + return + latex = lines[-1] + if not latex: + return + html_with_formula = element_to_html(node) + tag_math_type_list = cm.get_equation_type(html_with_formula) + # 如果无法确定类型,使用默认值 + if not tag_math_type_list: + tag_math_type_list = [('ccmath-inline', 'latex')] new_span = build_cc_element( - html_tag_name='ccmath-inline', + html_tag_name=tag_math_type_list[0][0], text=cm.wrap_math_md(latex), tail=text_strip(node.tail), - type='latex', + type=tag_math_type_list[0][1], by=math_render, html=element_to_html(node) ) replace_element(node, new_span) + except Exception as e: + raise HtmlMathRecognizerException(f'处理CSDN博客数学公式失败: {e}') + + +def process_zhihu_custom_tag(cm, math_render, node): + try: + # 从data-tex属性获取LaTeX公式 + latex = node.get('data-tex') + if not latex: + return + html_with_formula = f'{latex}' + tag_math_type_list = cm.get_equation_type(html_with_formula) + # 如果无法确定类型,默认为行内公式 + if not tag_math_type_list: + tag_math_type_list = [('ccmath-inline', 'latex')] + new_span = build_cc_element( + html_tag_name=tag_math_type_list[0][0], + text=cm.wrap_math_md(latex), + tail=text_strip(node.tail), + type=tag_math_type_list[0][1], + by=math_render, + html=element_to_html(node) + ) + replace_element(node, new_span) except Exception as e: raise HtmlMathRecognizerException(f'处理知乎数学公式失败: {e}') diff --git a/llm_web_kit/extractor/html/recognizer/ccmath.py b/llm_web_kit/extractor/html/recognizer/ccmath.py index 8c49bc44..52a77daf 100644 --- a/llm_web_kit/extractor/html/recognizer/ccmath.py +++ b/llm_web_kit/extractor/html/recognizer/ccmath.py @@ -139,6 +139,8 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe if 'zhuanlan.zhihu.com' in self.cm.url and node.tag == 'span' and node.get('class') == 'ztext-math': tag_script.process_zhihu_custom_tag(self.cm, math_render_type, node) + # if 'mathinsight.org' in self.cm.url and node.tag == 'span' and node.get('class') == '': + # tag = span, class 为 math-containerm, 或者 mathjax 或者 wp-katex-eq if node.tag == 'span' and node.get('class') and ( 'math-container' in node.get('class') or diff --git a/tests/llm_web_kit/extractor/test_extractor_chain.py b/tests/llm_web_kit/extractor/test_extractor_chain.py index 2d9c421c..ca3503ad 100644 --- a/tests/llm_web_kit/extractor/test_extractor_chain.py +++ b/tests/llm_web_kit/extractor/test_extractor_chain.py @@ -756,11 +756,9 @@ def test_csnd_none_formula(self): test_data = self.data_json[89] # 验证URL中包含blog.csdn.net self.assertIn('blog.csdn.net', test_data['url']) - # Create DataJson from test data input_data = DataJson(test_data) result = chain.extract(input_data) md_content = result.get_content_list().to_nlp_md() - # print('Markdown Content:', md_content) self.assertIn(r'$\lim\limits_{x \to 1}\dfrac{x^2-1}{x-1}$', md_content) self.assertIn(r'\begin{aligned} \frac{f(1.01)-f(1)}{1.01-1} &= \frac{1.01^2-1^2}{0.01} \\ &= \frac{0.0201}{0.01} \\ &= 2.01\end{aligned}', md_content) @@ -769,15 +767,12 @@ def test_zhihu_custom_tag(self): chain = ExtractSimpleFactory.create(self.config) self.assertIsNotNone(chain) test_data = self.data_json[90] + # 验证URL中包含zhuanlan.zhihu.com self.assertIn('zhuanlan.zhihu.com', test_data['url']) - # Create DataJson from test data input_data = DataJson(test_data) result = chain.extract(input_data) md_content = result.get_content_list().to_nlp_md() - with open('output_zhihu_custom_tag_inline1.md', 'w', encoding='utf-8') as f: - f.write(md_content) - - # print('Markdown Content:', md_content) - self.assertIn(r'$\begin{gathered} p(s_{1}|s_{1},a_{2})=0, \\ p(s_{2}|s_{1},a_{2})=1, \\ p(s_{3}|s_{1},a_{2})=0, \\ p(s_{4}|s_{1},a_{2})=0, \\ p(s_{5}|s_{1},a_{2})=0, \end{gathered}$', md_content) + self.assertIn(r'\begin{gathered} p(s_{1}|s_{1},a_{2})=0, \\ p(s_{2}|s_{1},a_{2})=1, \\ p(s_{3}|s_{1},a_{2})=0, \\ p(s_{4}|s_{1},a_{2})=0, \\ p(s_{5}|s_{1},a_{2})=0, \end{gathered}', md_content) self.assertIn(r'$s_1\xrightarrow{a_2}s_2\xrightarrow{a_3}s_5\xrightarrow{a_3}s_8\xrightarrow{a_2}s_9.$', md_content) - self.assertIn(r'$\begin{aligned} & p(s^{\prime}|s,a) \\ & \sum_{s^{\prime}\in\mathcal{S}}p(s^{\prime}|s,a)=1\text{ for any }(s,a). \end{aligned}$', md_content) + self.assertIn(r'$\mathrm{return}=0+0+0+1=1.$', md_content) + self.assertIn(r'\begin{aligned} V(s) & =\mathbb{E}[G_t|S_t=s] \\ & =\mathbb{E}[R_t+\gamma R_{t+1}+\gamma^2R_{t+2}+\ldots|S_t=s] \\ & =\mathbb{E}[R_t+\gamma(R_{t+1}+\gamma R_{t+2}+\ldots)|S_t=s] \\ & =\mathbb{E}[R_t+\gamma G_{t+1}|S_t=s] \\ & =\mathbb{E}[R_t+\gamma V(S_{t+1})|S_t=s] \end{aligned}', md_content) From 11c9bca6109a7e9329e0d6d86b23492501c54f89 Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Mon, 16 Jun 2025 14:36:53 +0800 Subject: [PATCH 07/14] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=B8=80?= =?UTF-8?q?=E4=B8=AAcsnd=E8=A1=8C=E9=97=B4/=E8=A1=8C=E5=86=85=E5=88=A4?= =?UTF-8?q?=E6=96=AD=E9=80=BB=E8=BE=91=E5=90=88=E5=B9=B6=E5=88=B0common.py?= =?UTF-8?q?=E4=B9=8B=E5=90=8E=E7=9A=84=E5=B0=8Fbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../html/recognizer/cc_math/common.py | 19 +++++--------- .../html/recognizer/cc_math/tag_script.py | 26 ++++++++++++------- .../extractor/html/recognizer/ccmath.py | 6 +++-- .../extractor/html/recognizer/test_math.py | 6 +++-- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/common.py b/llm_web_kit/extractor/html/recognizer/cc_math/common.py index f79b1bbf..f4a0f3b0 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/common.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/common.py @@ -273,20 +273,13 @@ def check_delimiters(delims_list, s): (sup_elements and any(text_strip(elem.text) for elem in sup_elements)): result.append((EQUATION_INLINE, MathType.HTMLMATH)) - # 检查当前节点是否是katex-mathml元素(CSDN) - if node.tag == 'span' and node.get('class') == 'katex-mathml': - parent = node.getparent() - while parent is not None: - parent_class = parent.get('class') or '' - if 'katex--inline' in parent_class: - result.append((EQUATION_INLINE, MathType.LATEX)) - break - if 'katex--display' in parent_class: - result.append((EQUATION_INTERLINE, MathType.LATEX)) - break - parent = parent.getparent() - else: + # 检查当前节点是否是katex元素(CSDN) + if node.tag == 'span' and node.get('class'): + node_class = node.get('class') + if 'katex--inline' in node_class: result.append((EQUATION_INLINE, MathType.LATEX)) + elif 'katex--display' in node_class: + result.append((EQUATION_INTERLINE, MathType.LATEX)) return self.equation_type_to_tag(result) def equation_type_to_tag(self, type_math_type: List[Tuple[str, str]]) -> List[Tuple[str, str]]: diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py index 6914f672..9741c7ce 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py @@ -60,27 +60,33 @@ def extract_katex_formula(text: str) -> Dict[str, str]: def process_katex_mathml(cm, math_render, node): try: - # 只处理katex-mathml节点 - if node.tag == 'span' and node.get('class') == 'katex-mathml': + # 处理katex节点(CSDN) + if node.tag == 'span' and node.get('class') in ['katex--inline', 'katex--display']: + # 根据节点class确定公式类型 + equation_type = 'ccmath-inline' # 默认行内公式 + if 'katex--display' in node.get('class'): + equation_type = 'ccmath-interline' # 行间公式 + # 查找内部的katex-mathml节点提取公式 + mathml_nodes = node.xpath('.//span[@class="katex-mathml"]') + if not mathml_nodes: + return + mathml_node = mathml_nodes[0] # 提取latex公式(取最后一行非空内容) - lines = [line.strip() for line in node.text_content().splitlines() if line.strip()] + lines = [line.strip() for line in mathml_node.text_content().splitlines() if line.strip()] if not lines: return latex = lines[-1] if not latex: return html_with_formula = element_to_html(node) - tag_math_type_list = cm.get_equation_type(html_with_formula) - # 如果无法确定类型,使用默认值 - if not tag_math_type_list: - tag_math_type_list = [('ccmath-inline', 'latex')] + # 创建新元素替换原节点 new_span = build_cc_element( - html_tag_name=tag_math_type_list[0][0], + html_tag_name=equation_type, text=cm.wrap_math_md(latex), tail=text_strip(node.tail), - type=tag_math_type_list[0][1], + type='latex', by=math_render, - html=element_to_html(node) + html=html_with_formula ) replace_element(node, new_span) except Exception as e: diff --git a/llm_web_kit/extractor/html/recognizer/ccmath.py b/llm_web_kit/extractor/html/recognizer/ccmath.py index 52a77daf..ae206416 100644 --- a/llm_web_kit/extractor/html/recognizer/ccmath.py +++ b/llm_web_kit/extractor/html/recognizer/ccmath.py @@ -132,8 +132,10 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe original_html = self._element_to_html(node) parent = node.getparent() - # 针对csdn博客中的katex-mathml标签,提取latex公式 - if 'blog.csdn.net' in self.cm.url and node.tag == 'span' and node.get('class') == 'katex-mathml': + # 针对csdn博客中的katex标签,提取latex公式 + if ('blog.csdn.net' in self.cm.url and + node.tag == 'span' and + node.get('class') in ['katex--inline', 'katex--display']): tag_script.process_katex_mathml(self.cm, math_render_type, node) if 'zhuanlan.zhihu.com' in self.cm.url and node.tag == 'span' and node.get('class') == 'ztext-math': diff --git a/tests/llm_web_kit/extractor/html/recognizer/test_math.py b/tests/llm_web_kit/extractor/html/recognizer/test_math.py index a2efac69..d6afb069 100644 --- a/tests/llm_web_kit/extractor/html/recognizer/test_math.py +++ b/tests/llm_web_kit/extractor/html/recognizer/test_math.py @@ -550,11 +550,13 @@ def test_csdn_katex_mathml(self): with self.subTest(input=test_case['input']): # 解析HTML为元素树 element = html_to_element(test_case['input']) - katex_node = element.xpath('//span[@class="katex-mathml"]')[0] + parent_class = 'katex--inline' if test_case['expected_tag'] == 'ccmath-inline' else 'katex--display' + katex_parent = element.xpath(f'//span[@class="{parent_class}"]')[0] # 处理前验证没有ccmath标签 expected_tag = test_case['expected_tag'] self.assertEqual(len(element.xpath(f'//{expected_tag}')), 0) - process_katex_mathml(cm, 'katex', katex_node) + # 传入父节点进行处理 + process_katex_mathml(cm, 'katex', katex_parent) # 验证处理后的标签类型是否正确 self.assertEqual(len(element.xpath(f'//{expected_tag}')), 1) # 验证公式内容是否正确 From e591c36fdd0acb897ca1965fb5cf8693a1f03efe Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Mon, 16 Jun 2025 14:49:27 +0800 Subject: [PATCH 08/14] =?UTF-8?q?1.=20=E8=A6=86=E7=9B=96=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../html/recognizer/cc_math/tag_script.py | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py index 9741c7ce..30633291 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py @@ -63,32 +63,27 @@ def process_katex_mathml(cm, math_render, node): # 处理katex节点(CSDN) if node.tag == 'span' and node.get('class') in ['katex--inline', 'katex--display']: # 根据节点class确定公式类型 - equation_type = 'ccmath-inline' # 默认行内公式 - if 'katex--display' in node.get('class'): - equation_type = 'ccmath-interline' # 行间公式 + equation_type = 'ccmath-inline' if 'katex--inline' in node.get('class') else 'ccmath-interline' # 查找内部的katex-mathml节点提取公式 mathml_nodes = node.xpath('.//span[@class="katex-mathml"]') - if not mathml_nodes: - return - mathml_node = mathml_nodes[0] - # 提取latex公式(取最后一行非空内容) - lines = [line.strip() for line in mathml_node.text_content().splitlines() if line.strip()] - if not lines: - return - latex = lines[-1] - if not latex: - return - html_with_formula = element_to_html(node) - # 创建新元素替换原节点 - new_span = build_cc_element( - html_tag_name=equation_type, - text=cm.wrap_math_md(latex), - tail=text_strip(node.tail), - type='latex', - by=math_render, - html=html_with_formula - ) - replace_element(node, new_span) + if mathml_nodes: + mathml_node = mathml_nodes[0] + # 提取latex公式(取最后一行非空内容) + lines = [line.strip() for line in mathml_node.text_content().splitlines() if line.strip()] + if lines: + latex = lines[-1] + if latex: + html_with_formula = element_to_html(node) + # 创建新元素替换原节点 + new_span = build_cc_element( + html_tag_name=equation_type, + text=cm.wrap_math_md(latex), + tail=text_strip(node.tail), + type='latex', + by=math_render, + html=html_with_formula + ) + replace_element(node, new_span) except Exception as e: raise HtmlMathRecognizerException(f'处理CSDN博客数学公式失败: {e}') From a2d5f717309b47546da93afaa090b7c9b2363464 Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Mon, 16 Jun 2025 14:56:40 +0800 Subject: [PATCH 09/14] =?UTF-8?q?1.=20=E6=8B=93=E5=AE=BD=E7=9F=A5=E4=B9=8E?= =?UTF-8?q?=E5=9F=9F=E5=90=8D=E8=8C=83=E5=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- llm_web_kit/extractor/html/recognizer/ccmath.py | 2 +- tests/llm_web_kit/extractor/test_extractor_chain.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llm_web_kit/extractor/html/recognizer/ccmath.py b/llm_web_kit/extractor/html/recognizer/ccmath.py index ae206416..bd5156e8 100644 --- a/llm_web_kit/extractor/html/recognizer/ccmath.py +++ b/llm_web_kit/extractor/html/recognizer/ccmath.py @@ -138,7 +138,7 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe node.get('class') in ['katex--inline', 'katex--display']): tag_script.process_katex_mathml(self.cm, math_render_type, node) - if 'zhuanlan.zhihu.com' in self.cm.url and node.tag == 'span' and node.get('class') == 'ztext-math': + if 'zhihu.com' in self.cm.url and node.tag == 'span' and node.get('class') == 'ztext-math': tag_script.process_zhihu_custom_tag(self.cm, math_render_type, node) # if 'mathinsight.org' in self.cm.url and node.tag == 'span' and node.get('class') == '': diff --git a/tests/llm_web_kit/extractor/test_extractor_chain.py b/tests/llm_web_kit/extractor/test_extractor_chain.py index ca3503ad..7f827a4e 100644 --- a/tests/llm_web_kit/extractor/test_extractor_chain.py +++ b/tests/llm_web_kit/extractor/test_extractor_chain.py @@ -768,7 +768,7 @@ def test_zhihu_custom_tag(self): self.assertIsNotNone(chain) test_data = self.data_json[90] # 验证URL中包含zhuanlan.zhihu.com - self.assertIn('zhuanlan.zhihu.com', test_data['url']) + self.assertIn('zhihu.com', test_data['url']) input_data = DataJson(test_data) result = chain.extract(input_data) md_content = result.get_content_list().to_nlp_md() From 66eef7b41e5b206c704f89e77f8b02a19ae7adc2 Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Mon, 16 Jun 2025 16:38:21 +0800 Subject: [PATCH 10/14] =?UTF-8?q?1.=20=E4=B8=80=E4=BA=9B=E5=B0=8F=E7=9A=84?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=202.=20=E7=BB=99=E7=9F=A5=E4=B9=8E=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=E6=B7=BB=E5=8A=A0=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../html/recognizer/cc_math/common.py | 2 +- .../html/recognizer/cc_math/tag_script.py | 50 +++++++++---------- .../extractor/html/recognizer/ccmath.py | 3 +- .../extractor/html/recognizer/test_math.py | 35 ++++++++++--- 4 files changed, 56 insertions(+), 34 deletions(-) diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/common.py b/llm_web_kit/extractor/html/recognizer/cc_math/common.py index f4a0f3b0..fc0ef18b 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/common.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/common.py @@ -274,7 +274,7 @@ def check_delimiters(delims_list, s): result.append((EQUATION_INLINE, MathType.HTMLMATH)) # 检查当前节点是否是katex元素(CSDN) - if node.tag == 'span' and node.get('class'): + if 'blog.csdn.net' in self.url and node.tag == 'span' and node.get('class'): node_class = node.get('class') if 'katex--inline' in node_class: result.append((EQUATION_INLINE, MathType.LATEX)) diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py index 30633291..379c2052 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py @@ -5,7 +5,7 @@ from llm_web_kit.exception.exception import HtmlMathRecognizerException from llm_web_kit.extractor.html.recognizer.cc_math.common import ( - CCMATH, CCMATH_INTERLINE, MathType, text_strip) + CCMATH, CCMATH_INLINE, CCMATH_INTERLINE, MathType, text_strip) from llm_web_kit.libs.html_utils import (build_cc_element, element_to_html, replace_element) @@ -60,30 +60,28 @@ def extract_katex_formula(text: str) -> Dict[str, str]: def process_katex_mathml(cm, math_render, node): try: - # 处理katex节点(CSDN) - if node.tag == 'span' and node.get('class') in ['katex--inline', 'katex--display']: - # 根据节点class确定公式类型 - equation_type = 'ccmath-inline' if 'katex--inline' in node.get('class') else 'ccmath-interline' - # 查找内部的katex-mathml节点提取公式 - mathml_nodes = node.xpath('.//span[@class="katex-mathml"]') - if mathml_nodes: - mathml_node = mathml_nodes[0] - # 提取latex公式(取最后一行非空内容) - lines = [line.strip() for line in mathml_node.text_content().splitlines() if line.strip()] - if lines: - latex = lines[-1] - if latex: - html_with_formula = element_to_html(node) - # 创建新元素替换原节点 - new_span = build_cc_element( - html_tag_name=equation_type, - text=cm.wrap_math_md(latex), - tail=text_strip(node.tail), - type='latex', - by=math_render, - html=html_with_formula - ) - replace_element(node, new_span) + # 根据节点class确定公式类型 + equation_type = 'ccmath-inline' if 'katex--inline' in node.get('class') else 'ccmath-interline' + # 查找内部的katex-mathml节点提取公式 + mathml_nodes = node.xpath('.//span[@class="katex-mathml"]') + if mathml_nodes: + mathml_node = mathml_nodes[0] + # 提取latex公式(取最后一行非空内容) + lines = [line.strip() for line in mathml_node.text_content().splitlines() if line.strip()] + if lines: + latex = lines[-1] + if latex: + html_with_formula = element_to_html(node) + # 创建新元素替换原节点 + new_span = build_cc_element( + html_tag_name=equation_type, + text=cm.wrap_math_md(latex), + tail=text_strip(node.tail), + type='latex', + by=math_render, + html=html_with_formula + ) + replace_element(node, new_span) except Exception as e: raise HtmlMathRecognizerException(f'处理CSDN博客数学公式失败: {e}') @@ -98,7 +96,7 @@ def process_zhihu_custom_tag(cm, math_render, node): tag_math_type_list = cm.get_equation_type(html_with_formula) # 如果无法确定类型,默认为行内公式 if not tag_math_type_list: - tag_math_type_list = [('ccmath-inline', 'latex')] + tag_math_type_list = [(CCMATH_INLINE, MathType.LATEX)] new_span = build_cc_element( html_tag_name=tag_math_type_list[0][0], text=cm.wrap_math_md(latex), diff --git a/llm_web_kit/extractor/html/recognizer/ccmath.py b/llm_web_kit/extractor/html/recognizer/ccmath.py index bd5156e8..01dfb990 100644 --- a/llm_web_kit/extractor/html/recognizer/ccmath.py +++ b/llm_web_kit/extractor/html/recognizer/ccmath.py @@ -155,7 +155,8 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe # script[type="math/tex"] # if node.tag == 'script' and node.get('type') and 'math/tex' in node.get('type'): - # tag_common_modify.modify_tree(cm, math_render_type, original_html, node, parent) + # print('匹配到script标签: ', node.get('type')) + # tag_common_modify.modify_tree(cm, math_render_type, original_html, node, parent) # math tags if node.tag == 'math' or node.tag.endswith(':math'): diff --git a/tests/llm_web_kit/extractor/html/recognizer/test_math.py b/tests/llm_web_kit/extractor/html/recognizer/test_math.py index d6afb069..48c9c71d 100644 --- a/tests/llm_web_kit/extractor/html/recognizer/test_math.py +++ b/tests/llm_web_kit/extractor/html/recognizer/test_math.py @@ -2,8 +2,8 @@ from pathlib import Path from llm_web_kit.exception.exception import HtmlMathRecognizerException -from llm_web_kit.extractor.html.recognizer.cc_math.tag_script import \ - process_katex_mathml +from llm_web_kit.extractor.html.recognizer.cc_math.tag_script import ( + process_katex_mathml, process_zhihu_custom_tag) from llm_web_kit.extractor.html.recognizer.ccmath import CCMATH, MathRecognizer from llm_web_kit.extractor.html.recognizer.recognizer import CCTag from llm_web_kit.libs.html_utils import element_to_html, html_to_element @@ -409,6 +409,19 @@ 'expected_formula': r'\frac{f(1)-f(1)}{1-1} = \frac{0}{0}' } ] + +TEST_ZHIHU_ZTEXT_HTML = [ + { + 'input': r'', + 'expected_tag': 'ccmath-inline', + 'expected_formula': r's_1\overset{a_2}{\operatorname*{\longrightarrow}}s_2\overset{a_3}{\operatorname*{\longrightarrow}}s_5\overset{a_3}{\operatorname*{\longrightarrow}}s_8\overset{a_2}{\operatorname*{\longrightarrow}}s_9\overset{a_5}{\operatorname*{\longrightarrow}}s_9\overset{a_5}{\operatorname*{\longrightarrow}}s_9\ldots' + }, + { + 'input': r'', + 'expected_tag': 'ccmath-interline', + 'expected_formula': r'\begin{aligned} & p(r|s,a) \\ & \sum_{r\in\mathcal{R}(s,a)}p(r|s,a)=1\text{ for any }(s,a). \end{aligned}' + } +] base_dir = Path(__file__).parent @@ -548,14 +561,10 @@ def test_csdn_katex_mathml(self): cm = CCMATH() for test_case in TEST_CSDN_KATEX_MATHML: with self.subTest(input=test_case['input']): - # 解析HTML为元素树 element = html_to_element(test_case['input']) parent_class = 'katex--inline' if test_case['expected_tag'] == 'ccmath-inline' else 'katex--display' katex_parent = element.xpath(f'//span[@class="{parent_class}"]')[0] - # 处理前验证没有ccmath标签 expected_tag = test_case['expected_tag'] - self.assertEqual(len(element.xpath(f'//{expected_tag}')), 0) - # 传入父节点进行处理 process_katex_mathml(cm, 'katex', katex_parent) # 验证处理后的标签类型是否正确 self.assertEqual(len(element.xpath(f'//{expected_tag}')), 1) @@ -563,6 +572,20 @@ def test_csdn_katex_mathml(self): formula = element.xpath(f'//{expected_tag}/text()')[0] self.assertIn(test_case['expected_formula'], formula) + def test_zhihu_ztext_math(self): + cm = CCMATH() + for test_case in TEST_ZHIHU_ZTEXT_HTML: + with self.subTest(input=test_case['input']): + element = html_to_element(test_case['input']) + ztext_math = element.xpath('//span[@class="ztext-math"]')[0] + expected_tag = test_case['expected_tag'] + process_zhihu_custom_tag(cm, 'MathJax', ztext_math) + # 验证处理后的标签类型是否正确 + self.assertEqual(len(element.xpath(f'//{expected_tag}')), 1) + # 验证公式内容是否正确 + formula = element.xpath(f'//{expected_tag}/text()')[0] + self.assertIn(test_case['expected_formula'], formula) + if __name__ == '__main__': r = TestMathRecognizer() From 541a5b36082842cb16cf799636369b94ad58d788 Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Tue, 17 Jun 2025 14:14:34 +0800 Subject: [PATCH 11/14] =?UTF-8?q?1.=20=E5=B0=86=E4=B8=80=E4=BA=9B=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E5=8C=85=E6=8B=AC=E6=96=B0=E5=A2=9E=E6=A0=87=E7=AD=BE?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E4=B8=BA=E5=85=A8=E5=B1=80=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../html/recognizer/cc_math/common.py | 14 +++++++++++ .../html/recognizer/cc_math/tag_script.py | 6 ++--- .../extractor/html/recognizer/ccmath.py | 9 ++++---- .../extractor/html/recognizer/test_math.py | 6 +++-- .../extractor/test_extractor_chain.py | 23 +++++++++++++++---- 5 files changed, 45 insertions(+), 13 deletions(-) diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/common.py b/llm_web_kit/extractor/html/recognizer/cc_math/common.py index fc0ef18b..3bd7a055 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/common.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/common.py @@ -74,6 +74,20 @@ class MATH_TYPE_PATTERN: DISPLAYMATH = 'displayMath' +# CSDN博客的KaTeX标签 +class CSDN: + INLINE = 'katex--inline' + DISPLAY = 'katex--display' + MATH = 'katex-mathml' + DOMAIN = 'blog.csdn.net' + + +# 知乎的数学公式标签 +class ZHIHU: + MATH = 'ztext-math' + DOMAIN = 'zhihu.com' + + # 行内行间公式,MathJax中一般也可以通过配置来区分行内行间公式 EQUATION_INLINE = DocElementType.EQUATION_INLINE EQUATION_INTERLINE = DocElementType.EQUATION_INTERLINE diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py index 379c2052..2208b4b5 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py @@ -5,7 +5,7 @@ from llm_web_kit.exception.exception import HtmlMathRecognizerException from llm_web_kit.extractor.html.recognizer.cc_math.common import ( - CCMATH, CCMATH_INLINE, CCMATH_INTERLINE, MathType, text_strip) + CCMATH, CCMATH_INLINE, CCMATH_INTERLINE, CSDN, MathType, text_strip) from llm_web_kit.libs.html_utils import (build_cc_element, element_to_html, replace_element) @@ -61,9 +61,9 @@ def extract_katex_formula(text: str) -> Dict[str, str]: def process_katex_mathml(cm, math_render, node): try: # 根据节点class确定公式类型 - equation_type = 'ccmath-inline' if 'katex--inline' in node.get('class') else 'ccmath-interline' + equation_type = CCMATH_INLINE if CSDN.INLINE in node.get('class') else CCMATH_INTERLINE # 查找内部的katex-mathml节点提取公式 - mathml_nodes = node.xpath('.//span[@class="katex-mathml"]') + mathml_nodes = node.xpath(f'.//span[@class="{CSDN.MATH}"]') if mathml_nodes: mathml_node = mathml_nodes[0] # 提取latex公式(取最后一行非空内容) diff --git a/llm_web_kit/extractor/html/recognizer/ccmath.py b/llm_web_kit/extractor/html/recognizer/ccmath.py index 01dfb990..8f45b359 100644 --- a/llm_web_kit/extractor/html/recognizer/ccmath.py +++ b/llm_web_kit/extractor/html/recognizer/ccmath.py @@ -9,7 +9,8 @@ tag_common_modify, tag_img, tag_math, tag_mjx, tag_script) -from llm_web_kit.extractor.html.recognizer.cc_math.common import CCMATH +from llm_web_kit.extractor.html.recognizer.cc_math.common import (CCMATH, CSDN, + ZHIHU) from llm_web_kit.extractor.html.recognizer.cc_math.render.render import ( BaseMathRender, MathRenderType) from llm_web_kit.extractor.html.recognizer.recognizer import ( @@ -133,12 +134,12 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe parent = node.getparent() # 针对csdn博客中的katex标签,提取latex公式 - if ('blog.csdn.net' in self.cm.url and + if (CSDN.DOMAIN in self.cm.url and node.tag == 'span' and - node.get('class') in ['katex--inline', 'katex--display']): + node.get('class') in [CSDN.INLINE, CSDN.DISPLAY]): tag_script.process_katex_mathml(self.cm, math_render_type, node) - if 'zhihu.com' in self.cm.url and node.tag == 'span' and node.get('class') == 'ztext-math': + if ZHIHU.DOMAIN in self.cm.url and node.tag == 'span' and node.get('class') == ZHIHU.MATH: tag_script.process_zhihu_custom_tag(self.cm, math_render_type, node) # if 'mathinsight.org' in self.cm.url and node.tag == 'span' and node.get('class') == '': diff --git a/tests/llm_web_kit/extractor/html/recognizer/test_math.py b/tests/llm_web_kit/extractor/html/recognizer/test_math.py index 48c9c71d..1ecadfdc 100644 --- a/tests/llm_web_kit/extractor/html/recognizer/test_math.py +++ b/tests/llm_web_kit/extractor/html/recognizer/test_math.py @@ -2,6 +2,8 @@ from pathlib import Path from llm_web_kit.exception.exception import HtmlMathRecognizerException +from llm_web_kit.extractor.html.recognizer.cc_math.common import ( + CCMATH_INLINE, CSDN, ZHIHU) from llm_web_kit.extractor.html.recognizer.cc_math.tag_script import ( process_katex_mathml, process_zhihu_custom_tag) from llm_web_kit.extractor.html.recognizer.ccmath import CCMATH, MathRecognizer @@ -562,7 +564,7 @@ def test_csdn_katex_mathml(self): for test_case in TEST_CSDN_KATEX_MATHML: with self.subTest(input=test_case['input']): element = html_to_element(test_case['input']) - parent_class = 'katex--inline' if test_case['expected_tag'] == 'ccmath-inline' else 'katex--display' + parent_class = CSDN.INLINE if test_case['expected_tag'] == CCMATH_INLINE else CSDN.DISPLAY katex_parent = element.xpath(f'//span[@class="{parent_class}"]')[0] expected_tag = test_case['expected_tag'] process_katex_mathml(cm, 'katex', katex_parent) @@ -577,7 +579,7 @@ def test_zhihu_ztext_math(self): for test_case in TEST_ZHIHU_ZTEXT_HTML: with self.subTest(input=test_case['input']): element = html_to_element(test_case['input']) - ztext_math = element.xpath('//span[@class="ztext-math"]')[0] + ztext_math = element.xpath(f'//span[@class="{ZHIHU.MATH}"]')[0] expected_tag = test_case['expected_tag'] process_zhihu_custom_tag(cm, 'MathJax', ztext_math) # 验证处理后的标签类型是否正确 diff --git a/tests/llm_web_kit/extractor/test_extractor_chain.py b/tests/llm_web_kit/extractor/test_extractor_chain.py index 7f827a4e..04cd5ef4 100644 --- a/tests/llm_web_kit/extractor/test_extractor_chain.py +++ b/tests/llm_web_kit/extractor/test_extractor_chain.py @@ -18,7 +18,8 @@ from llm_web_kit.config.cfg_reader import load_pipe_tpl from llm_web_kit.extractor.extractor_chain import ExtractSimpleFactory -from llm_web_kit.extractor.html.recognizer.cc_math.common import MathType +from llm_web_kit.extractor.html.recognizer.cc_math.common import (CSDN, ZHIHU, + MathType) from llm_web_kit.input.datajson import DataJson from llm_web_kit.libs.doc_element_type import DocElementType, ParagraphTextType @@ -63,7 +64,7 @@ def setUp(self): continue self.data_json.append(json.loads(line)) - assert len(self.data_json) == 91 + assert len(self.data_json) == 92 # Config for HTML extraction self.config = load_pipe_tpl('html-test') @@ -755,7 +756,7 @@ def test_csnd_none_formula(self): self.assertIsNotNone(chain) test_data = self.data_json[89] # 验证URL中包含blog.csdn.net - self.assertIn('blog.csdn.net', test_data['url']) + self.assertIn(CSDN.DOMAIN, test_data['url']) input_data = DataJson(test_data) result = chain.extract(input_data) md_content = result.get_content_list().to_nlp_md() @@ -768,7 +769,7 @@ def test_zhihu_custom_tag(self): self.assertIsNotNone(chain) test_data = self.data_json[90] # 验证URL中包含zhuanlan.zhihu.com - self.assertIn('zhihu.com', test_data['url']) + self.assertIn(ZHIHU.DOMAIN, test_data['url']) input_data = DataJson(test_data) result = chain.extract(input_data) md_content = result.get_content_list().to_nlp_md() @@ -776,3 +777,17 @@ def test_zhihu_custom_tag(self): self.assertIn(r'$s_1\xrightarrow{a_2}s_2\xrightarrow{a_3}s_5\xrightarrow{a_3}s_8\xrightarrow{a_2}s_9.$', md_content) self.assertIn(r'$\mathrm{return}=0+0+0+1=1.$', md_content) self.assertIn(r'\begin{aligned} V(s) & =\mathbb{E}[G_t|S_t=s] \\ & =\mathbb{E}[R_t+\gamma R_{t+1}+\gamma^2R_{t+2}+\ldots|S_t=s] \\ & =\mathbb{E}[R_t+\gamma(R_{t+1}+\gamma R_{t+2}+\ldots)|S_t=s] \\ & =\mathbb{E}[R_t+\gamma G_{t+1}|S_t=s] \\ & =\mathbb{E}[R_t+\gamma V(S_{t+1})|S_t=s] \end{aligned}', md_content) + + # def test_mathinsight_custom_latex(self): + # """测试mathinsight自定义latex.""" + # chain = ExtractSimpleFactory.create(self.config) + # self.assertIsNotNone(chain) + # test_data = self.data_json[91] + # # 验证URL中包含mathinsight.org + # self.assertIn('mathinsight.org', test_data['url']) + # input_data = DataJson(test_data) + # result = chain.extract(input_data) + # md_content = result.get_content_list().to_nlp_md() + # with open('output_mathinsight.md', 'w', encoding='utf-8') as f: + # f.write(md_content) + # self.assertIn(r'', md_content) From eb505508dfbffa072110bee966ee7fc5c00058da Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Tue, 17 Jun 2025 14:23:43 +0800 Subject: [PATCH 12/14] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/llm_web_kit/extractor/test_extractor_chain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/llm_web_kit/extractor/test_extractor_chain.py b/tests/llm_web_kit/extractor/test_extractor_chain.py index 04cd5ef4..5d4822d9 100644 --- a/tests/llm_web_kit/extractor/test_extractor_chain.py +++ b/tests/llm_web_kit/extractor/test_extractor_chain.py @@ -64,7 +64,7 @@ def setUp(self): continue self.data_json.append(json.loads(line)) - assert len(self.data_json) == 92 + assert len(self.data_json) == 91 # Config for HTML extraction self.config = load_pipe_tpl('html-test') From 88e275c4a4deb73e46fe69f6f8bf1e3616de1c0d Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Tue, 17 Jun 2025 14:48:54 +0800 Subject: [PATCH 13/14] =?UTF-8?q?=E8=A1=A5=E5=85=85=E9=81=97=E6=BC=8F?= =?UTF-8?q?=E7=9A=84=E5=85=A8=E5=B1=80=E5=8F=98=E9=87=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- llm_web_kit/extractor/html/recognizer/cc_math/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llm_web_kit/extractor/html/recognizer/cc_math/common.py b/llm_web_kit/extractor/html/recognizer/cc_math/common.py index 3bd7a055..712902bc 100644 --- a/llm_web_kit/extractor/html/recognizer/cc_math/common.py +++ b/llm_web_kit/extractor/html/recognizer/cc_math/common.py @@ -288,11 +288,11 @@ def check_delimiters(delims_list, s): result.append((EQUATION_INLINE, MathType.HTMLMATH)) # 检查当前节点是否是katex元素(CSDN) - if 'blog.csdn.net' in self.url and node.tag == 'span' and node.get('class'): + if CSDN.DOMAIN in self.url and node.tag == 'span' and node.get('class'): node_class = node.get('class') - if 'katex--inline' in node_class: + if CSDN.INLINE in node_class: result.append((EQUATION_INLINE, MathType.LATEX)) - elif 'katex--display' in node_class: + elif CSDN.DISPLAY in node_class: result.append((EQUATION_INTERLINE, MathType.LATEX)) return self.equation_type_to_tag(result) From cadb88a11c90fba9711a285b6d35038ad3dfb73f Mon Sep 17 00:00:00 2001 From: Yanggq <1041206149@qq.com> Date: Tue, 17 Jun 2025 16:51:10 +0800 Subject: [PATCH 14/14] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/llm_web_kit/extractor/test_extractor_chain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/llm_web_kit/extractor/test_extractor_chain.py b/tests/llm_web_kit/extractor/test_extractor_chain.py index b43a20af..a7578fbf 100644 --- a/tests/llm_web_kit/extractor/test_extractor_chain.py +++ b/tests/llm_web_kit/extractor/test_extractor_chain.py @@ -762,7 +762,7 @@ def test_csnd_none_formula(self): md_content = result.get_content_list().to_nlp_md() self.assertIn(r'$\lim\limits_{x \to 1}\dfrac{x^2-1}{x-1}$', md_content) self.assertIn(r'\begin{aligned} \frac{f(1.01)-f(1)}{1.01-1} &= \frac{1.01^2-1^2}{0.01} \\ &= \frac{0.0201}{0.01} \\ &= 2.01\end{aligned}', md_content) - + def test_zhihu_custom_tag(self): """测试知乎自定义标签.""" chain = ExtractSimpleFactory.create(self.config)