avatar

Xiaojun Wu

Ph.D. Candidate, HKUST(GZ)
Affiliate LLM Researcher, IDEA Research
wuxiaojun@idea.edu.cn & wuxiaojun@pku.edu.cn & xwu647@connect.hkust-gz.edu.cn


← Blog

小白学强化学习

强化学习小白理解GRPO(三):适配自定义任务,以Finance-R1为例

在知乎查看原文

阅读 知乎未公开 收藏 17 点赞 8

互动数据为 2026-09-13 的知乎数据快照;阅读量未在当前可用的知乎公开接口中提供。

唉。。。因为很多事情鸽了很久的第三篇来了,年初前两篇受到很多同学的赞同和好评,这一年大家对于GRPO和强化学习也基本都是很熟悉,各种变体DAPO/VAPO/GSPO等层出不穷,是今年工业界和学术界的“显学“了。

这篇就给出第二篇中提出的任务吧,虽然对于技术人员并不难,但是可以作为技术小白的入门。

1.任务说明

我们的想法时根据一家公司过去5天的新闻,来判断该公司股价的涨跌,这个任务形式很简单是一个二分类任务,但是新闻和股价的关系十分复杂,新闻正面不一定股价一定会涨,新闻负面股价也不一定会跌,所以这个任务其实很难。

我用deepseek-R1生成了一批数据:

huggingface.co/datasets

<p data-pid="bBtVXkhP">这里我生成数据的方式是给了新闻和标签,当R1假装不知道标签去生成思考过程。这个生成其实是最简单粗暴的,最好是让R1不看到标签去生成思考过程,生成答案对了才是好的思考过程,当然这样也需要生成比较多的尝试。</p><h2>2.代码实现</h2><p data-pid="4KOYnq1P">其实需要改的地方不多,数据改为我们上面提供的这个,然后核心肯定改reward(src/open_r1/rewards.py),判断生成答案(涨或者跌)和ground thuth对不对:</p><div class="highlight"><pre>def accuracy_reward_finance(completions, solution, **kwargs): """ Reward function that checks if the label ("Rise" or "Decrease") is present in the <answer> section. """ rewards = []

for completion, sol in zip(completions, solution):
    # 提取生成答案的内容
    content = completion[0]["content"]
    
    # 检查是否包含 &lt;answer&gt; 标签
    if "&lt;answer&gt;" in content and "&lt;/answer&gt;" in content:
        # 提取 &lt;answer&gt; 部分的内容
        start_idx = content.find("&lt;answer&gt;") + len("&lt;answer&gt;")
        end_idx = content.find("&lt;/answer&gt;")
        answer_section = content[start_idx:end_idx].strip()
        
        # 判断标准答案是否在 &lt;answer&gt; 部分中
        if sol in answer_section:
            reward = 1.0  # 如果标准答案在 &lt;answer&gt; 中,奖励为 1
        else:
            reward = 0.0  # 否则奖励为 0
    else:
        # 如果没有 &lt;answer&gt; 标签,直接给 0 奖励
        reward = 0.0
    
    rewards.append(reward)

return rewards

ef get_cosine_scaled_reward_finance( min_value_wrong: float = -1.0, max_value_wrong: float = -0.5, min_value_correct: float = 0.5, max_value_correct: float = 1.0, max_len: int = 1000, ): def cosine_scaled_reward(completions, solution, **kwargs): “"”Reward function that assigns fixed rewards based on correctness.””” contents = [completion[0][“content”] for completion in completions] rewards = [] for content, sol in zip(contents, solution): # 检查 <answer> 部分是否包含标准答案 if “<answer>” in content and “</answer>” in content: start_idx = content.find(“<answer>”) + len(“<answer>”) end_idx = content.find(“</answer>”) answer_section = content[start_idx:end_idx].strip()

            # 判断是否匹配标准答案
            is_correct = sol in answer_section
        else:
            is_correct = False
        
        # 根据正确性分配奖励值
        if is_correct:
            reward = max_value_correct  # 正确答案的最大奖励
        else:
            reward = min_value_wrong  # 错误答案的最小惩罚
        rewards.append(float(reward))
    return rewards
return cosine_scaled_reward

def get_repetition_penalty_reward_finance(ngram_size: int, max_penalty: float): “”” Simplified repetition penalty function for stock prediction tasks. Since the generated answers are short labels (“Rise” or “Decrease”), no actual repetition penalty is needed. “”” if max_penalty > 0: raise ValueError(f”max_penalty {max_penalty} should not be positive”)

def repetition_penalty_reward(completions, **kwargs) -&gt; list:
    """
    Reward function that always returns 0 for stock prediction tasks.
    Args:
        completions: List of model completions
    """
    rewards = []
    for completion in completions:
        # 对于股票新闻任务,直接返回 0(无惩罚)
        rewards.append(0.0)
    return rewards

return repetition_penalty_reward</code></pre></div><p data-pid="_kqSoSDr">训练代码基本就是把数学任务reward改为上面这个reward(src/open_r1/grpo.py)就行了:</p><div class="highlight"><pre><code class="language-python3"><span class="kn">from</span> <span class="nn">open_r1.rewards</span> <span class="k">import</span> <span class="p">(</span>
<span class="n">accuracy_reward_finance</span><span class="p">,</span>
<span class="n">get_cosine_scaled_reward_finance</span><span class="p">,</span>
<span class="n">get_repetition_penalty_reward_finance</span> <span class="p">)</span>

… # Get reward functions REWARD_FUNCS_REGISTRY = { “accuracy”: accuracy_reward_finance, “format”: format_reward, “reasoning_steps”: reasoning_steps_reward, “cosine”: get_cosine_scaled_reward_finance( min_value_wrong=script_args.cosine_min_value_wrong, max_value_wrong=script_args.cosine_max_value_wrong, min_value_correct=script_args.cosine_min_value_correct, max_value_correct=script_args.cosine_max_value_correct, max_len=script_args.cosine_max_len, ), “repetition_penalty”: get_repetition_penalty_reward_finance( ngram_size=script_args.repetition_n_grams, max_penalty=script_args.repetition_max_penalty, ), } reward_funcs = [REWARD_FUNCS_REGISTRY[func] for func in script_args.reward_funcs]

</code></pre></div><h2>3.实验</h2><p data-pid="QpN7LDQ6">目前手头没有资源训练,因此如果有人想要尝试的话,可以使用我的数据集和代码实现去跑一下,或者以后我有资源了再补足这个实验。</p><h2>4.金融相关</h2><p data-pid="lSEf6UdT">只看新闻是否足以判断股价涨跌?其实学术上可以做做,刷刷sota,但是目前主流的量化投资是以量价数据、因子的方式去做高频数据的建模,然后交易,这是LLM所不擅长的。只依赖新闻消息、政策、基本面去做交易更倾向于是主观投资,价值投资的方式,投资周期一般比较长,这是LLM所擅长的,去分析语义,但是分析语义是不够的,真金白银去投资往往需要超越常人的洞见和预测。最近LLM+金融借着Alpha Arena火了一把,但是他做的也是比较简单其实,把财经的技术指标按照规则算好了,给LLM买入卖出的决策提供参考。这个领域目前还是没有被LLM充分探索其实。</p>

<figcaption>https://nof1.ai/</figcaption>
<p data-pid="MxZin107">对AI金融感兴趣的同学不妨可以读读下面的文章:</p><p data-pid="5yAdpBZ7">如何破解传统因子与新闻的融合困境?</p>


© 2025 Xiaojun Wu. All rights reserved. Powered by Jekyll and Minimal Light theme.