From f011c9ff902a9f2f1706d3278e2b58b1d3e9f10c Mon Sep 17 00:00:00 2001 From: yangguohao <1901212980@pku.edu.cn> Date: Tue, 28 Jul 2026 12:49:06 -0500 Subject: [PATCH 1/2] [SWE-Paddle] Complete task package for PaddlePaddle__Paddle-52948 Add instruction, gold patch, test patch, test command, environment notes, and README. Gold covers Hackathon No.91 PRs #52948 and #53572 as one sample. Co-authored-by: Cursor --- .../PaddlePaddle__Paddle-52948/README.md | 50 ++++ .../environment/README.md | 45 ++++ .../PaddlePaddle__Paddle-52948/instruction.md | 48 ++++ .../PaddlePaddle__Paddle-52948/proposal.md | 15 +- .../solution/code.patch | 146 ++++++++++++ .../tests/test.patch | 221 ++++++++++++++++++ .../PaddlePaddle__Paddle-52948/tests/test.sh | 9 + 7 files changed, 527 insertions(+), 7 deletions(-) create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-52948/README.md create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-52948/environment/README.md create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-52948/instruction.md create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-52948/solution/code.patch create mode 100644 swe-paddle/tasks/PaddlePaddle__Paddle-52948/tests/test.patch create mode 100755 swe-paddle/tasks/PaddlePaddle__Paddle-52948/tests/test.sh diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/README.md new file mode 100644 index 000000000..faf9cc4db --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/README.md @@ -0,0 +1,50 @@ +# PaddlePaddle__Paddle-52948 + +This directory converts Paddle PR #52948 and follow-up PR #53572 into one SWE-Paddle community task candidate (Hackathon No.91: `register_hook` for static / dy2static). + +## Source + +| Field | Value | +| --- | --- | +| Repo | `PaddlePaddle/Paddle` | +| Primary PR | [52948](https://github.com/PaddlePaddle/Paddle/pull/52948) | +| Follow-up PR | [53572](https://github.com/PaddlePaddle/Paddle/pull/53572) | +| PR titles | `【Hackathon No.91】` / `【Hackathon No.91】Following updates` | +| Base commit | `cf6cbc347970a1fd2c9d76e427880139789497af` | +| Gold endpoint | `f3f3d57a159caf3b77f93a4d86cb233e6a1c159a` (after #53572) | +| Merged at | `2023-04-27` (#52948), `2023-05-08` (#53572) | +| Task type | `feature_enhancement` | +| Resource | CPU (pure Python; era-matched Paddle / source checkout) | + +## Summary + +在静态图与动转静(`to_static`)场景下支持 `Tensor.register_hook`,使反向 hook +能正确触发,且梯度结果与动态图一致。完整 gold 覆盖首个合入实现及其后续规范化 +接入(独立 Transformer 进入统一 AST 变换流水线、测试断言风格对齐)。 + +## Why This Sample + +- **真实 Hackathon 闭环**:合入前相关单测显式断言 static / dy2static 下 hook 不可用。 +- **框架级能力**:同时覆盖静态图 hook 运行时语义与动转静路径下的行为对齐。 +- **双 PR 合一**:#52948 落地能力,#53572 按 review 意见完成 Transformer 规范化;合并后样本对应当前更合理的最终形态。 +- **边界清晰**:目标集中在 hook 可运行且梯度一致;不要求内部函数 hook 与 `hook.remove`。 + +## Files + +- `proposal.md`: approved proposal (maintainer triage context). +- `instruction.md`: self-contained problem statement for the coding agent. +- `solution/code.patch`: gold patch net of #52948 + #53572 (production files only). +- `tests/test.patch`: test patch exposing the target behavior. +- `tests/test.sh`: minimal target test command. +- `environment/README.md`: base commit, apply order, and reproduction notes. + +## Verification + +```bash +bash tests/test.sh +``` + +Expected behavior: with `tests/test.patch` applied on `base_commit`, the static / +dy2static `register_hook` cases should fail/error. After also applying +`solution/code.patch`, the target tests should pass. Existing dygraph +`register_hook` cases in the same module should remain pass-to-pass. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/environment/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/environment/README.md new file mode 100644 index 000000000..713394215 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/environment/README.md @@ -0,0 +1,45 @@ +# Environment Notes + +SWE-Paddle task candidate for PaddlePaddle/Paddle PR #52948 + follow-up #53572. + +## Expected Environment + +- Repository: `PaddlePaddle/Paddle` +- Base commit: `cf6cbc347970a1fd2c9d76e427880139789497af` (parent of #52948 squash-merge `db30aa1`) +- Gold endpoint: `f3f3d57a159caf3b77f93a4d86cb233e6a1c159a` (after #53572) +- Resource: CPU +- GPU required: no +- Patch type: **pure Python**. Production changes are limited to static-graph / + dy2static Python modules; no C++ / CUDA / kernel / infermeta rebuild is + required for the gold patch itself. +- Paddle install: prefer an era-matched (2023-04/05) wheel or a source checkout + at `base_commit` with a working Python package layout so that + `paddle.jit.to_static` and static-graph execution are available. + +## Run Order (Run / Test / Fix) + +1. Check out `PaddlePaddle/Paddle` at the base commit and ensure Paddle is + importable (era-matched wheel or source build/install). +2. Apply `tests/test.patch`. +3. Run `bash tests/test.sh`; the static / dy2static `register_hook` cases should + **fail / error** before the fix. Existing dygraph-only hook cases should still + pass (P2P candidates). +4. Apply `solution/code.patch`. +5. Run `bash tests/test.sh` again; all target cases should **pass**. + +## Minimal Test Command + +```bash +bash tests/test.sh +``` + +## Known Risks + +- Historical (2023-04) wheels may be hard to pin exactly; if using a newer + wheel, confirm `to_static` / static Variable APIs still match the base-era + contracts exercised by the tests. +- `test_hook_in_init_for_layer` uses random input; prefer fixed seeds when + deriving stable F2P / P2P node IDs. +- Gold patch is the **net** of #52948 and #53572 relative to the base commit + (intermediate helpers introduced in #52948 and removed/relocated in #53572 + are not left as dead intermediate state). diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/instruction.md b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/instruction.md new file mode 100644 index 000000000..000875e0a --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/instruction.md @@ -0,0 +1,48 @@ +# 在静态图与动转静场景下支持 Tensor.register_hook + +## 详细描述 + +当前动态图中可以对 Tensor 调用 `register_hook` 注册反向 hook,并在 `backward` 时按预期改写梯度。但在静态图,以及经 `paddle.jit.to_static`(或等价装饰器)转换后的路径上,该能力不可用或行为与动态图不一致。 + +需要达成的目标: + +- 在静态图模式下,对可求导的 Tensor / Variable 调用 `register_hook` 应能成功执行,而不再因接口不可用而直接失败。 +- 对经过 `to_static` 转换的函数或 `nn.Layer`,在中间结果或参数上注册的反向 hook 应能被触发。 +- 同一计算在动态图与 `to_static` 路径下,hook 对梯度的影响应保持一致。至少覆盖以下场景: + - 多个中间变量分别注册 hook + - 变量被重新赋值后再注册 hook + - 同一变量重复注册 hook + - 在 `nn.Layer.__init__` 中对参数注册 hook,再对 `to_static` 后的网络做前向与反向 +- 不要求支持:嵌套内部函数中的 `register_hook`,以及 `hook.remove`。 + +## 问题复现(示意) + +在 `base_commit` 对应版本上: + +1. 开启静态图,构造简单网络并对中间结果调用 `register_hook`,或对 `@to_static` / `to_static(...)` 包装后的函数/Layer 注册 hook 再 `backward`。 +2. 可观察到接口不可用(例如断言失败),或 hook 未按动态图语义影响梯度。 +3. 对照同一计算的纯动态图路径,梯度行为不一致或静态 / 动转静路径无法完成。 + +## 期望行为 + +- 静态图与动转静路径下 `register_hook` 可运行。 +- hook 触发后的梯度结果与动态图一致(在上述覆盖场景内)。 +- 既有动态图 `register_hook` 行为保持不变。 + +## 验收说明 + +- 上述目标行为可用,相关用例通过。 +- 已有动态图 hook 语义不被破坏。 +- 不允许通过删除测试、弱化断言或大范围绕过校验来「通过」任务。 + +## 技术要求 + +- 熟悉 Python +- 了解 Paddle 动态图 / 静态图与动转静(`to_static`)基本机制 +- 了解 autograd hook 的基本语义 + +## Acceptance Criteria + +- The behavior described above should be implemented. +- Existing valid behavior should remain unchanged. +- Do not satisfy the task by deleting tests, weakening assertions, or bypassing validation broadly. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/proposal.md b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/proposal.md index a990c73c5..35188b3de 100644 --- a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/proposal.md +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/proposal.md @@ -3,11 +3,11 @@ ## 1. 来源信息 - Instance ID:`PaddlePaddle__Paddle-52948` -- PR 链接:https://github.com/PaddlePaddle/Paddle/pull/52948 -- PR 标题:`【Hackathon No.91】`(register_hook for static mode) +- PR 链接:https://github.com/PaddlePaddle/Paddle/pull/52948 (主 PR);https://github.com/PaddlePaddle/Paddle/pull/53572 (follow-up,一并纳入 gold) +- PR 标题:`【Hackathon No.91】` / `【Hackathon No.91】Following updates` - `base_commit`:`cf6cbc347970a1fd2c9d76e427880139789497af` -- gold commit:`db30aa1d7fac77b98105e40878d0746327fea86a` -- merged 时间:`2023-04-27` +- gold endpoint:`f3f3d57a159caf3b77f93a4d86cb233e6a1c159a`(#53572 合入后) +- merged 时间:`2023-04-27`(#52948)、`2023-05-08`(#53572) - 你的身份:原 PR 作者(GitHub @yangguohao) - 后续联系人:GitHub @yangguohao @@ -22,6 +22,7 @@ - **边界清楚**:目标行为是静态模式与 `to_static` 下 `register_hook` 可运行,且梯度与动态图一致;不要求覆盖嵌套内部函数中的 hook 注册,也不要求实现 `hook.remove`。 - **非平凡性**:仅放开静态接口或只改测试期望不够;需要同时保证静态图运行时可挂接 hook,以及动转静后 hook 仍作用在正确对象与时机上。 - **区分度潜力**:只修静态侧、只修动转静侧,或只放宽测试,都会被完整验收用例拦住。 +- **双 PR 合一**:#53572 按 review 意见完成规范化接入;本样本 gold 取两者净效果,避免停留在中间实现形态。 ## 4. 任务类型和标签 @@ -44,7 +45,7 @@ - Paddle 来源:`PaddlePaddle/Paddle` source checkout at `base_commit` - 是否能提供 Docker:暂无 - 是否需要 GPU:否 -- patch 类型:纯 Python +- patch 类型:纯 Python(#52948 + #53572 净变更) - 最小测试命令:`bash tests/test.sh` - 是否有 oracle 日志:由 SWE-Paddle verifier 结果另行维护 @@ -52,5 +53,5 @@ - 泄露风险:正式 `instruction.md` 只写问题、复现、期望行为与验收标准;不包含衍生 PR 链接、diff、具体修改文件、具体实现步骤或答案路径。 - 环境风险:历史 commit(2023-04)对应 wheel 可能较难精确获取;优先匹配时期 wheel,否则 source build。 -- flaky 风险:主要为确定性小张量比较;含随机输入的用例需在 test patch 中固定 seed,并由 verifier 确认稳定 nodeid。 -- 拆分风险:该 PR 目标集中在 static / dy2static 下的 `register_hook` 支持,适合作为一个独立样本;后续规范化重构不纳入本样本。 +- flaky 风险:主要为确定性小张量比较;含随机输入的用例需在抽取稳定 nodeid 时注意 seed。 +- 拆分风险:两 PR 同属 Hackathon No.91 同一目标,合并为一个样本更合理;gold 使用相对 `base_commit` 的净效果,不保留被 follow-up 替换的中间实现。 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/solution/code.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/solution/code.patch new file mode 100644 index 000000000..e745e2412 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/solution/code.patch @@ -0,0 +1,146 @@ +diff --git a/python/paddle/fluid/framework.py b/python/paddle/fluid/framework.py +index 1111111..2222222 100644 +--- a/python/paddle/fluid/framework.py ++++ b/python/paddle/fluid/framework.py +@@ -1640,9 +1640,24 @@ + """ + pass + +- @fake_interface_only + def register_hook(self, hook): +- pass ++ import paddle ++ ++ def backward_hook_wrapper(dy): ++ """call the backward hook in .""" ++ return hook(np.array(dy)) ++ ++ def forward_hook_wrapper(x): ++ """do nothing but return a new variable.""" ++ return x ++ ++ paddle.static.py_func( ++ func=forward_hook_wrapper, ++ x=self, ++ out=self, ++ backward_func=backward_hook_wrapper, ++ skip_vars_in_backward_input=[self], ++ ) + + def __str__(self): + return self._to_readable_code() +diff --git a/python/paddle/jit/dy2static/ast_transformer.py b/python/paddle/jit/dy2static/ast_transformer.py +index 1111111..2222222 100644 +--- a/python/paddle/jit/dy2static/ast_transformer.py ++++ b/python/paddle/jit/dy2static/ast_transformer.py +@@ -38,6 +38,7 @@ + from .return_transformer import ReturnTransformer + from .static_analysis import StaticAnalysisVisitor + from .tensor_shape_transformer import TensorShapeTransformer ++from .tensorhook_transformer import RegisterHookTransformer + from .typehint_transformer import TypeHintTransformer + from .utils import ast_to_source_code + +@@ -92,6 +93,7 @@ + self.visit(node_wrapper.node) + + transformers = [ ++ RegisterHookTransformer, + EarlyReturnTransformer, + BasicApiTransformer, # Basic Api + TensorShapeTransformer, # Tensor.shape -> paddle.shape(Tensor) +diff --git a/python/paddle/jit/dy2static/tensorhook_transformer.py b/python/paddle/jit/dy2static/tensorhook_transformer.py +new file mode 100644 +index 0000000..1111111 +--- /dev/null ++++ b/python/paddle/jit/dy2static/tensorhook_transformer.py +@@ -0,0 +1,89 @@ ++# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++import collections ++ ++from paddle.utils import gast ++ ++from .base_transformer import BaseTransformer ++ ++ ++class RegisterHookTransformer(BaseTransformer): ++ def __init__(self, wrapper_root): ++ self.register_hook_pos_map = collections.defaultdict(list) ++ self.assignment_pos_map = collections.defaultdict(list) ++ self.root = wrapper_root.node ++ ++ def transform(self): ++ """ ++ Main function to transform AST. ++ """ ++ self.visit(self.root) ++ ++ def visit_FunctionDef(self, func_def): ++ # The inner function that has register_hook will not be processed ++ check_register_hook = next( ++ ( ++ node ++ for node in gast.walk(func_def) ++ if isinstance(node, gast.Attribute) ++ and node.attr == 'register_hook' ++ ), ++ None, ++ ) ++ if check_register_hook is None: ++ return func_def ++ ++ register_hook_pos_map = self.register_hook_pos_map ++ assignment_pos_map = self.assignment_pos_map ++ ++ for i in range(len(func_def.body) - 1, -1, -1): ++ ++ body = func_def.body[i] ++ # Check if the code body contains the register_hook ++ if isinstance(body, gast.Expr): ++ for node in gast.walk(body): ++ if ( ++ isinstance(node, gast.Attribute) ++ and node.attr == 'register_hook' ++ ): ++ # parameter name for register_hook ++ param_name = node.value.id ++ register_hook_pos_map[param_name].append(i) ++ elif isinstance(body, gast.Assign): ++ for target in body.targets: ++ assignment_pos_map[target.id].append(i) ++ ++ # Confirm the order ++ order_map = {} ++ for k, idx_list in register_hook_pos_map.items(): ++ for idx in idx_list: ++ if k not in assignment_pos_map: ++ order_map[idx] = 1 ++ else: ++ for assignment_idx in assignment_pos_map[k]: ++ if idx > assignment_idx: ++ order_map[idx] = assignment_idx + 1 ++ break ++ code_order = [*range(len(func_def.body))] ++ for k, v in sorted(order_map.items(), key=lambda x: x[1], reverse=True): ++ if k == v: ++ continue ++ code_order.remove(k) ++ code_order.insert(v, k) ++ ++ # rearrange the code according to the specified order ++ new_body = [func_def.body[i] for i in code_order] ++ func_def.body = new_body ++ return func_def diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/tests/test.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/tests/test.patch new file mode 100644 index 000000000..85ea29168 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/tests/test.patch @@ -0,0 +1,221 @@ +diff --git a/python/paddle/fluid/tests/unittests/test_tensor_register_hook.py b/python/paddle/fluid/tests/unittests/test_tensor_register_hook.py +index 1111111..2222222 100644 +--- a/python/paddle/fluid/tests/unittests/test_tensor_register_hook.py ++++ b/python/paddle/fluid/tests/unittests/test_tensor_register_hook.py +@@ -45,9 +45,10 @@ + self.linear1 = nn.Linear(in_size, in_size) + self.linear2 = nn.Linear(in_size, out_size) + +- def forward(self, x): ++ def forward(self, x, hook=False): + ret1 = self.linear1(x) +- ret1.register_hook(lambda grad: grad * 2) ++ if hook: ++ ret1.register_hook(lambda grad: grad * 2) + + ret2 = self.linear2(ret1) + out = paddle.mean(ret2, axis=-1) +@@ -512,8 +513,7 @@ + ) + + net = SimpleNetForStatic(self.in_size, self.out_size) +- with self.assertRaises(AssertionError): +- out = net(x) ++ out = net(x) + + paddle.disable_static() + +@@ -527,9 +527,17 @@ + 'float32' + ) + data_t = paddle.to_tensor(data) +- +- with self.assertRaises(AssertionError): +- out = jit_net(data_t) ++ data_t2 = paddle.to_tensor(data) ++ data_t.stop_gradient = False ++ data_t2.stop_gradient = False ++ ++ out1 = jit_net(data_t) ++ out2 = jit_net(data_t2, True) ++ out1.backward() ++ out2.backward() ++ np.testing.assert_array_equal( ++ 2 * data_t.grad.numpy(), data_t2.grad.numpy() ++ ) + + + HOOK_INIT_VALUE = 10 +diff --git a/test/dygraph_to_static/test_tensor_hook.py b/test/dygraph_to_static/test_tensor_hook.py +new file mode 100644 +index 0000000..1111111 +--- /dev/null ++++ b/test/dygraph_to_static/test_tensor_hook.py +@@ -0,0 +1,167 @@ ++# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++import unittest ++ ++import numpy as np ++ ++import paddle ++from paddle import nn ++from paddle.jit import to_static ++ ++ ++class TestStaticAnalysis(unittest.TestCase): ++ def test_hook_for_different_parameter(self): ++ def f(x): ++ def h(g): ++ return 2 * g ++ ++ y = x + 4 ++ f = y + x ++ z = f**2 ++ y.register_hook(h) ++ f.register_hook(h) ++ x.register_hook(h) ++ return z ++ ++ x = paddle.to_tensor([2.0]) ++ x.stop_gradient = False ++ loss = f(x) ++ loss.backward() ++ ++ x_jit = paddle.to_tensor([2.0]) ++ x_jit.stop_gradient = False ++ jit_f = to_static(f) ++ loss = jit_f(x_jit) ++ loss.backward() ++ np.testing.assert_allclose(x.grad.numpy(), x_jit.grad.numpy()) ++ ++ def test_hook_for_reassignment_parameter(self): ++ def f(x): ++ def h(g): ++ return 2 * g ++ ++ y = x + 4 ++ x = y * 5 ++ z = x**2 ++ x.register_hook(h) ++ return z ++ ++ x = paddle.to_tensor([2.0]) ++ x.stop_gradient = False ++ loss = f(x) ++ loss.backward() ++ ++ x_jit = paddle.to_tensor([2.0]) ++ x_jit.stop_gradient = False ++ jit_f = to_static(f) ++ loss = jit_f(x_jit) ++ loss.backward() ++ np.testing.assert_allclose(x.grad.numpy(), x_jit.grad.numpy()) ++ ++ def test_hook_for_repeat_register(self): ++ def f(x): ++ def h(g): ++ return 2 * g ++ ++ y = x + 4 ++ z = y**2 ++ x.register_hook(h) ++ x.register_hook(h) ++ return z ++ ++ x = paddle.to_tensor([2.0]) ++ x.stop_gradient = False ++ loss = f(x) ++ loss.backward() ++ ++ x_jit = paddle.to_tensor([2.0]) ++ x_jit.stop_gradient = False ++ jit_f = to_static(f) ++ loss = jit_f(x_jit) ++ loss.backward() ++ np.testing.assert_allclose(x.grad.numpy(), x_jit.grad.numpy()) ++ ++ def test_hook_in_init_for_layer(self): ++ def hook(grad): ++ return grad * 2 ++ ++ IMAGE_SIZE = 784 ++ CLASS_NUM = 10 ++ ++ class LinearNet(nn.Layer): ++ def __init__(self): ++ super().__init__() ++ self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM) ++ # register_hook in init ++ self._linear.parameters()[0].register_hook(hook) ++ ++ def forward(self, x): ++ return self._linear(x) ++ ++ # create network ++ layer = LinearNet() ++ jit_layer = to_static(LinearNet()) ++ data = np.random.random([IMAGE_SIZE]).astype('float32') ++ image = paddle.to_tensor(data) ++ image_jit = paddle.to_tensor(data) ++ loss = layer(image) ++ loss_jit = jit_layer(image_jit) ++ loss_jit.backward() ++ loss.backward() ++ np.testing.assert_allclose( ++ layer.parameters()[0].grad.numpy(), ++ jit_layer.parameters()[0].grad.numpy(), ++ ) ++ ++ # def test_hook_in_forward_for_layer(self): ++ # ++ # IMAGE_SIZE = 784 ++ # CLASS_NUM = 10 ++ # ++ # class LinearNet(nn.Layer): ++ # def __init__(self): ++ # super().__init__() ++ # self._linear = nn.Linear(IMAGE_SIZE, CLASS_NUM) ++ # ++ # def forward(self, x): ++ # def hook(grad): ++ # return grad * 2 ++ # ++ # res = self._linear(x) ++ # ++ # # register_hook in forward ++ # self._linear.parameters()[0].register_hook(hook) ++ # return res ++ # ++ # # create network ++ # layer = LinearNet() ++ # jit_layer = to_static(LinearNet()) ++ # data = np.random.random([IMAGE_SIZE]).astype('float32') ++ # image = paddle.to_tensor(data) ++ # image_jit = paddle.to_tensor(data) ++ # loss = layer(image) ++ # loss_jit = jit_layer(image_jit) ++ # loss_jit.backward() ++ # loss.backward() ++ # self.assertTrue( ++ # np.allclose( ++ # layer.parameters()[0].grad.numpy(), ++ # jit_layer.parameters()[0].grad.numpy(), ++ # ) ++ # ) ++ ++ ++if __name__ == '__main__': ++ unittest.main() diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/tests/test.sh b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/tests/test.sh new file mode 100755 index 000000000..758653a70 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/tests/test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Target tests for PaddlePaddle__Paddle-52948 (#52948 + #53572). +# Run from the root of a PaddlePaddle/Paddle checkout with Paddle importable. +python -m pytest \ + test/dygraph_to_static/test_tensor_hook.py \ + python/paddle/fluid/tests/unittests/test_tensor_register_hook.py \ + -q From 62b26bc897843dee837be4a18b6795ee114c2c57 Mon Sep 17 00:00:00 2001 From: yangguohao <1901212980@pku.edu.cn> Date: Tue, 28 Jul 2026 15:32:17 -0500 Subject: [PATCH 2/2] [SWE-Paddle] Keep merged proposal.md unchanged for Paddle-52948 Co-authored-by: Cursor --- .../tasks/PaddlePaddle__Paddle-52948/proposal.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/proposal.md b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/proposal.md index 35188b3de..a990c73c5 100644 --- a/swe-paddle/tasks/PaddlePaddle__Paddle-52948/proposal.md +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-52948/proposal.md @@ -3,11 +3,11 @@ ## 1. 来源信息 - Instance ID:`PaddlePaddle__Paddle-52948` -- PR 链接:https://github.com/PaddlePaddle/Paddle/pull/52948 (主 PR);https://github.com/PaddlePaddle/Paddle/pull/53572 (follow-up,一并纳入 gold) -- PR 标题:`【Hackathon No.91】` / `【Hackathon No.91】Following updates` +- PR 链接:https://github.com/PaddlePaddle/Paddle/pull/52948 +- PR 标题:`【Hackathon No.91】`(register_hook for static mode) - `base_commit`:`cf6cbc347970a1fd2c9d76e427880139789497af` -- gold endpoint:`f3f3d57a159caf3b77f93a4d86cb233e6a1c159a`(#53572 合入后) -- merged 时间:`2023-04-27`(#52948)、`2023-05-08`(#53572) +- gold commit:`db30aa1d7fac77b98105e40878d0746327fea86a` +- merged 时间:`2023-04-27` - 你的身份:原 PR 作者(GitHub @yangguohao) - 后续联系人:GitHub @yangguohao @@ -22,7 +22,6 @@ - **边界清楚**:目标行为是静态模式与 `to_static` 下 `register_hook` 可运行,且梯度与动态图一致;不要求覆盖嵌套内部函数中的 hook 注册,也不要求实现 `hook.remove`。 - **非平凡性**:仅放开静态接口或只改测试期望不够;需要同时保证静态图运行时可挂接 hook,以及动转静后 hook 仍作用在正确对象与时机上。 - **区分度潜力**:只修静态侧、只修动转静侧,或只放宽测试,都会被完整验收用例拦住。 -- **双 PR 合一**:#53572 按 review 意见完成规范化接入;本样本 gold 取两者净效果,避免停留在中间实现形态。 ## 4. 任务类型和标签 @@ -45,7 +44,7 @@ - Paddle 来源:`PaddlePaddle/Paddle` source checkout at `base_commit` - 是否能提供 Docker:暂无 - 是否需要 GPU:否 -- patch 类型:纯 Python(#52948 + #53572 净变更) +- patch 类型:纯 Python - 最小测试命令:`bash tests/test.sh` - 是否有 oracle 日志:由 SWE-Paddle verifier 结果另行维护 @@ -53,5 +52,5 @@ - 泄露风险:正式 `instruction.md` 只写问题、复现、期望行为与验收标准;不包含衍生 PR 链接、diff、具体修改文件、具体实现步骤或答案路径。 - 环境风险:历史 commit(2023-04)对应 wheel 可能较难精确获取;优先匹配时期 wheel,否则 source build。 -- flaky 风险:主要为确定性小张量比较;含随机输入的用例需在抽取稳定 nodeid 时注意 seed。 -- 拆分风险:两 PR 同属 Hackathon No.91 同一目标,合并为一个样本更合理;gold 使用相对 `base_commit` 的净效果,不保留被 follow-up 替换的中间实现。 +- flaky 风险:主要为确定性小张量比较;含随机输入的用例需在 test patch 中固定 seed,并由 verifier 确认稳定 nodeid。 +- 拆分风险:该 PR 目标集中在 static / dy2static 下的 `register_hook` 支持,适合作为一个独立样本;后续规范化重构不纳入本样本。