ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

强化学习稀疏奖励难题:Hindsight Relabeling核心原理与实战指南

强化学习稀疏奖励难题:Hindsight Relabeling核心原理与实战指南 1. 项目概述别让“事后诸葛亮”背锅它其实是强化学习的救星很多人看到 hindsight 这个词第一反应是“后见之明”、“马后炮”常带点贬义。但在强化学习这块hindsight 恰恰是个宝贝——它对应的技术叫hindsight relabeling后见之明经验回放专门用来治稀疏奖励问题。我最早接触它是在做机器人抓取任务时智能体试了大半天成功率还是零整个训练基本就是“瞎子摸象”。后来把 hindsight relabeling 接进去训练曲线才开始有了像样的上升趋势。可以说这个思路帮我省下了至少一个月的调参时间。这篇内容适合谁看正在做goal-conditioned RL目标条件强化学习、被稀疏奖励坑得满头包的人或者你只是想搞清楚 HERHindsight Experience Replay到底怎么work、怎么复现都可以参考。我会从最核心的思路讲到可落地的实现细节再到我实际踩过的坑尽量把每个“为什么”都讲清楚。hindsight relabeling 解决的核心问题一句话就能概括在目标任务上失败的经验放到另一个更简单的目标上其实是一次完美的成功。这个“另一个目标”就是我们事后看到的真实终点。好比学生考完试对着答案发现自己做错了但这道题本身也是知识点的训练——你重新调整目标把“考高分”换成“把这道题搞懂”那刚才的失败就变成了宝贵的学习样本。强化学习也一样拿失败经验换个目标重新学习往往比硬刚原目标高效得多。2. 核心思路拆解为什么稀疏奖励会让强化学习寸步难行2.1 先从问题本身说起稀疏奖励到底难在哪做强化学习的人都清楚reward 设计是个玄学。幸运的是很多任务能设计出稠密奖励比如跑步有速度奖励、机械臂有距离奖励。但现实里大量任务没法这么做——拿“把方块推到目标位置”来说你可以设计“离目标越近奖励越高”可一旦目标换成一幅复杂装配图距离函数根本写不出来。这时候只能用稀疏奖励成功给 1失败给 0。稀疏奖励带来的问题是探索极其低效。智能体随机探索时要碰巧做出一个能获得正奖励的动作概率低到离谱。没有正奖励policy 就得不到任何有意义的梯度最终策略会退化到随机游走。形象点说你在大草原上找一枚丢了的戒指找对了方向没有任何反馈只有踩到戒指才算成功——你大概率永远找不到。常见解决办法有三类。第一类是reward shaping人工构造稠密奖励但任务复杂时根本构造不出来而且设计不好会引入局部最优。第二类是curriculum learning从简单任务开始逐渐变难但需要人为设计课程且课程进度不易控制。第三类是curiosity-driven exploration让智能体因为“好奇心”去探索新状态但在目标条件任务里容易分心——到处乱逛就是不干正事。hindsight relabeling 走的是另一条路不改变环境不改变奖励直接改造 replay buffer 里的数据。它认为“失败的经验不是没用而是目标定错了”。这让我当时眼前一亮因为它把问题从“如何探索得更聪明”变成了“如何利用已有数据更聪明”后者显然更可控。2.2 Hindsight 的核心假设从失败里提取“隐式成功”hindsight relabeling 的关键动作是当一条经验transition在原始目标 g 上失败时把它重新标注成在另一个目标 g 上的成功经验。这个 g 通常是这条经验结束后智能体实际到达的状态。这样一来同一段轨迹就有了双重身份对原目标是失败对新目标是成功。policy 能从新目标里学到“这个动作序列能够完成 g”这是实打实的正样本。它的核心假设是目标空间存在某种连续性或可迁移性学会把物体推到位置 A会帮助你学会推到位置 B学会抓取某个杯子会帮助你学习抓取另一个杯子。很多操作任务天然满足这个性质所以效果出奇地好。但如果目标任务之间毫无相似性比如“开门”和“倒水”这种完全不同的操作HER 的收益就很有限。还有一个非常重要的点hindsight 必须配合 off-policy 算法使用。因为 relabeling 本质上是改了 reward 和 goal生成这条经验时的行为和现在的 policy 已经不是同一个分布了只能用 off-policy 算法从 replay buffer 里反复学习。我当时用的是DDPG HER的组合也有人用 SAC HER都可以。on-policy 算法如 PPO基本与 HER 无缘道理就在这里。2.3 为什么选择 hindsight 而不是别的方案单看思路有人会问这不会让目标变得太简单吗会不会学偏我也纠结过这个问题。实际用下来发现只要控制好“重标注的比例”和“新目标的选择策略”偏不偏其实是可控的。对比 reward shapingHER 的显著优势是不需要任何环境先验知识不需要你手动设计奖励函数省掉一大块人工成本。对比 curriculum learningHER 是“自动生成课程”——从简单目标开始离终点近的状态逐步逼近真实目标原本想完成的目标省去了人为编排课程的麻烦。对比 curiosityHER 直接利用真实经验不引入额外探索噪声训练更稳定。但 HER 也不是银弹。我后面做机械臂任务时发现如果目标空间维度特别高比如图像级目标或者目标超出智能体当前能力范围太多HER 的重标注目标会严重偏离原始任务分布训练反而会变得不稳定。所以实战中我一般把 HER 作为基础框架再叠加其他策略比如辅助奖励、状态归一化来稳住训练。3. 核心实现细节与实操要点手把手拆解 HER 的每个环节3.1 完整算法流程从采样到重标注的七步走一个标准的 HER 训练循环大概是这个样子。我以 DDPG 结构为例换 SAC 也一样核心思想不变从环境中采样一批经验每条经验包括状态 obs、动作 act、奖励 reward、下一状态 next_obs、完成标志 done、原始目标 goal g。把原始经验存入 replay buffer同时记录这条轨迹的序号。训练更新时从 replay buffer 里随机抽出一个 batch但每条经验不是直接用来计算 loss而是有概率被“重标注”。对选中的经验设定一个新目标 g。常用策略是从这条轨迹后续的未来状态中随机选一个状态future strategy或者用轨迹最终状态final state甚至直接随机采一个已经达到过的状态。根据新目标 g重新计算这条经验的奖励 reward R(s, a, s, g) 和 done 标志obs 和 action 不变。把重标注后的目标 g 也拼进 observation 里形成新的输入向量交给 actor 和 critic 计算梯度。更新 critic 时用重标注后的目标去算 TD 误差更新 actor 时用当前目标下的 Q 值梯度回传。核心点在于obs 里通常拼接了 goal所以重标注不光是改 reward还要同步换掉 obs 和 next_obs 里的 goal 部分。这一步容易被忽略一旦忘了模型看到的还是旧目标奖励又是新目标整个训练就会逻辑错乱。3.2 目标重标注的四种策略我用下来哪种最稳看论文和源码时会发现目标重标注有四种常见策略官方叫法一般叫final、episode、random、future策略做法特点我的评价final直接用整条轨迹的最终状态作为新目标最简单成功概率高适合短轨迹任务轨迹长了会丢中间信息episode从当前时刻所在的整个 episode 状态里随机挑一个简单覆盖范围广中等任务可用但可能选到太早的状态目标太简单random从已经见过的所有状态里随机挑一个覆盖历史分布不太推荐容易选到和目标空间无关的状态future从当前时刻之后的未来状态里随机挑 k 个之一目标难度自然递进课程学习效果最稳尤其配合未来状态集合采样我用下来最顺手的是future 策略尤其配合一个参数future_k。做法是这条轨迹从当前时间步往后数收集接下来的 k 个状态在这 k 个状态里随机选一个重新作为当前经验的目标。k 通常取 4也可以根据轨迹长度调整。之所以效果最好是因为它把“简单目标”和“原始目标”自然过渡起来——刚开局时目标容易后段逐步逼近真实目标天然形成课程。另一个隐蔽但重要的点是不是每条经验都要重标注。代码里一般用一个概率值控制比如每条经验有 80% 的概率执行重标注剩下 20% 保留原始目标。这么做可以确保 agent 不忘记真实任务也避免重标注目标分布和真实目标分布偏差太大。3.3 奖励函数怎么设计稀疏还是稠密的取舍HER 最纯正的用法是配合稀疏奖励这也是论文里强调的。但我在实际项目里发现如果你的任务能写出“半稠密”奖励混着用反而更好。这里说的“半稠密”指的是主奖励还是稀疏的成功/失败但额外给一个距离或进展的小信号。比如抓取任务成功给 1失败给 0但如果手爪离物体近了额外加一个0.1 * max(0, old_dist - new_dist)这样的小奖励。这个信号不是为了给 agent 详细指导而是让训练早期更容易有梯度。不过要小心如果这个“进展奖励”设计得不好agent 可能会钻空子比如故意把手靠近再远离反复刷距离差。我的经验是进展奖励的系数要远小于主奖励确保最终目标还是“完成任务”而不是“刷进度条”。另外在某些任务里HER 和稠密奖励其实是冲突的——因为重标注后新目标不同稠密奖励函数在不同目标之间可能不可比较导致 Q 值估计不稳定。所以如果你用 HER我建议初始版本老老实实做稀疏奖励等稳定了再考虑加辅助奖励。3.4 网络输入和状态处理Goal 怎么拼进去效果最好目标条件 RL 里policy 和 Q 函数通常输入的是“观测 目标”的拼接向量。以机械臂为例observation 是机械臂关节角度、末端位置、物体位置等goal 是目标位置直接把两者 concat 起来喂给网络是最常见的做法。但这里有一个我踩过的坑observation 里可能已经包含了当前物体位置而 goal 又是目标物体位置两者数值范围可能差异很大。比如关节角是弧度制范围在 -3 到 3 之间而目标位置是米范围在 -0.5 到 0.5 之间直接 concat 会让网络训练时尺度失衡早期梯度全被大数值特征主导。解决办法很简单分别对 obs 和 goal 做归一化或者至少统一量纲。我习惯把 obs 和 goal 都用 running mean/std 归一化效果很显著。还有一点如果你的任务里用了图像观测HER 的实现会复杂很多因为“目标”本身就是一张图。这种情况下不能简单 concat通常要引入目标编码器把图像编码成向量再拼到 policy 输入里。这类任务里 HER 依然有效但工程复杂度会翻倍我建议新手先别碰图片输入从向量状态的仿真环境开始练手。3.5 一个可直接参考的 HER 核心伪代码下面这段伪代码是我在项目里服役过很久的版本经过删减只留主干方便大家对照理解# 假设采用 DDPG HER(future) replay_buffer ReplayBuffer(capacity1_000_000) future_k 4 relabel_prob 0.8 for episode in range(total_episodes): trajectory [] # 记录本回合所有 transition obs env.reset() goal env.goal # 原始目标 for t in range(max_steps): goal_input concat(obs, goal) action actor(goal_input) noise next_obs, reward, done, info env.step(action) # 原始经验先存一份后面可能被重标注 trajectory.append({ obs: obs, next_obs: next_obs, action: action, reward: reward, done: done, goal: goal }) obs next_obs if done: break # 把原始轨迹放入 buffer for transition in trajectory: replay_buffer.push(transition) # 重标注对轨迹中的每条 transition 都可能做一次 for t, transition in enumerate(trajectory): if random.random() relabel_prob: continue # 采集 future 状态池从 t1 到回合末尾的 next_obs 里随机抽 future_states [trajectory[i][next_obs][:goal_dim] for i in range(t, len(trajectory))] if len(future_states) 0: continue # 随机抽 future_k 个候选再随机选一个 candidates random.sample(future_states, min(future_k, len(future_states))) new_goal random.choice(candidates) # 重算奖励和 done new_reward compute_sparse_reward(transition[next_obs], new_goal) new_done 1.0 if new_reward success_reward else 0.0 # 替换目标后压入 replay buffer replay_buffer.push({ obs: concat(transition[obs], new_goal), next_obs: concat(transition[next_obs], new_goal), action: transition[action], reward: new_reward, done: new_done, goal: new_goal }) # 正常 off-policy 更新 if replay_buffer.size() batch_size: for step in range(updates_per_episode): batch replay_buffer.sample(batch_size) update_actor_critic(batch)这段代码有几个细节值得说明。一是future_states里我只抽了next_obs中的 goal 维度的部分因为很多环境里 goal 只是状态的一个子集。二是random.sample里用min(future_k, len(...))防止越界这个边界问题我后面还会提到。三是重标注后的 experience 也正常参与训练和原始 experience 混在一起不需要区分优先级。3.6 环境选择建议从一个简单到你能盯住每个环节的任务开始我第一次在gym里尝试 HER 时为了快速验证没有直接用 FetchReach而是先用了一个手写的bit-flip 环境状态是一个 n 位的 0/1 向量目标是把这个向量变成另一个目标向量每一步只能翻转一位。这个环境极度稀疏——只有完全匹配才给奖励但目标空间有限随机探索也能偶尔碰到成功非常适合验证 HER 实现是否真的有效。如果你连 bit-flip 都嫌麻烦直接在gymnasium里用FetchReach-v4这类环境也行它已经是 mujoco 移植版本状态观测比较干净。但我仍然建议先从简单环境过一遍全流程确认重标注、奖励计算、done 逻辑都正确再上机器人环境。否则一旦训练不收敛你根本分不清是 HER 实现问题还是环境本身太难。4. 实操过程与核心环节实现一次完整的 HER 训练复现记录4.1 从零到一准备环境和模型用 DDPG 还是 SAC我在实际项目中综合下来SAC HER 的组合通常比 DDPG HER 更稳数据利用率也很高但代码复杂度稍高。如果只是验证思路DDPG HER 足够如果要上复杂任务建议直接上 SAC HER。原因很简单SAC 自带熵正则探索更充分配合 HER 的“经验发掘”能让成功率天花板更高。模型结构方面我一般用一个三层的 MLP第一层concat(obs, goal)作为输入维度是 obs_dim goal_dim隐藏层(256, 256, 256)激活函数用 ReLU输出层actor 输出动作连续任务用 tanhcritic 输出 Q 值critic 更新时我的 loss 是标准的 TD lossL ( Q(s, g, a) - (r gamma * (1 - done) * Q(s, g, a) ) )^2注意这里r是重标注后的奖励g是重标注后的目标。actor 更新时最大化Q(s, g, a)其中输入动作由 actor 输出输入目标用当前 batch 里的 goal可能是原始目标也可能是重标注目标取决于我们采样到哪条经验。4.2 训练参数与超参数设置贴一份我常用的配置超参数我贴一份自己跑通的配置环境是FetchReach-v4机械臂末端到达目标位置。这个环境目标维度是 3状态维度大概 10 左右动作维度 4非常适合先跑通流程超参数数值说明replay buffer 容量1_000_000要足够大HER 对 buffer 需求很高batch size256我用 256稳定性和显存占用平衡gamma0.98目标条件任务不需要太大太大容易震荡actor / critic 学习率1e-3 / 1e-3用 Adam同步衰减tau软更新0.05不同于默认的 0.005我调大了一些更新更快每回合更新次数40一个 episode 结束后更新 40 次梯度future_k4future 策略采样范围relabel_prob0.8重标注概率探索噪声OU Noise 或 Gaussian 0.1早期大噪声后期衰减强调一下 tau 这个参数。很多人直接抄 DDPG 默认的 0.005目标网络更新太慢配合 HER 这种数据效率极高的方法会显得训练特别慢。我调到 0.05 之后训练明显更跟手了。当然调太大也会不稳定建议在 0.01 到 0.1 之间做个小扫描。4.3 训练效果对比HER 到底能把成功率拉到多高我在相同环境、相同预算下跑过一组对比纯 DDPG 稀疏奖励跑了 80 个 epoch成功率几乎为 0只能靠运气偶尔碰到目标然后马上又忘掉。DDPG HERfinal 策略成功率能到 50% 左右但波动很大有时一个 epoch 能到 70%下一个 epoch 又掉回 20%。DDPG HERfuture 策略成功率稳步上升80 个 epoch 时能达到 85% 以上波动也小很多。SAC HERfuture 策略同样的 epoch 数成功率能到 90% 以上而且样本效率略高。表里的“成功率”不是测试时的贪心成功率而是训练过程中的评估成功率评估时不加噪声。我见过很多人测试时用随机动作去模拟训练过程这是错的——评估一定要用确定性动作否则噪声会影响你的判断。另外我强烈建议在训练过程中定期保存 checkpoint每 5 个 epoch 存一次。HER 训练极容易在后期出现“震荡性遗忘”就是明明已经学会 80% 成功率突然某个 epoch 又跌回 30%。如果有 checkpoint可以回溯到之前好的权重而不是从头再来。4.4 一个容易被忽略的实现细节状态归一化与目标缩放的先后顺序前面提到要归一化 obs 和 goal这里再细化一下。我的做法是先收集一些随机探索数据算出各维度的 mean 和 std然后对 obs 和 goal 分别归一化。但是有个先后顺序的问题如果你要把 goal 从状态空间里拆出来归一化一定要保证 obs 里没有被归一化后的 goal 混着。否则同样是“目标位置”这个信息一处归一化一处不归一化模型会学到混乱的特征。在gymnasium的 Fetch 类环境里observation是完整状态achieved_goal和desired_goal是单独字段。我的处理方法是只拼obs不包含 goal 部分和desired_goal的归一化向量。当重标注时新的desired_goal是从achieved_goal里采样得到的也要走同一套归一化参数。这个细节看起来小但直接影响收敛速度。我试过不归一化直接拼训练曲线跟心电图一样归一化之后曲线才变成正常的上坡形态。如果你的环境状态本身就是 0 到 1 的连续值归一化可以省掉但只要现实到机器人任务几乎必须做。4.5 延伸一步从 HER 到 HIRhindsight 思想在上游指令数据清洗中的应用既然是“hindsight”标题我顺便扩展一个方向Hindsight Instruction RelabelingHIR很多人也叫 LLM 版的 hindsight。它把 HER 的思想搬到了指令微调数据上。传统指令微调需要人工写“输入-输出”对但自动生成数据时经常出现模型答非所问、输出和指令不匹配的情况。HIR 的思路是既然模型给了这个输出那就根据输出反推一个指令再把“指令 输出”作为新样本喂给模型。具体操作上拿一个 LLM比如 GPT-4 或开源模型给定原始错误样本的输入输出让它用几句话分析“这段输出实际上回答了什么问题”再生成一条符合该输出的新指令然后组合成新的训练对。这和 HER 的 relabeling 完全同构失败样本被改写成成功样本目标空间从“原始指令”重标到“模型实际完成的问题”。在清洗带噪自动标注数据时这个思路很有用相当于自动把低质量样本“救活”。不过要提醒的是HIR 会引入“模型擅长什么就说什么”的自证偏差如果全部重标注可能让模型只学会自己偏好的输出模式。所以我一般控制重标注比例只对一小部分样本做改写当作数据增强手段而不是主数据来源。这和 HER 里relabel_prob不能设成 1 是一个道理。5. 常见问题与排查技巧实录我在 HER 项目里踩过的坑5.1 训练不上升先别急着加网络复杂度去查这三个地方我在多个项目里碰到过 HER 训练曲线长时间不动的现象排到最后大多不是算法问题而是三个低级错误reward 计算里用的是 obs 而不是 next_obs。后来才发现“到达目标”这个判断应该用动作执行后的下一状态而不是动作执行前的状态。写成reward(s, a, s, g)而不是reward(s, g)这个是最典型的 ER 错误。done 标志和 reward 没对齐。有的版本稀疏奖励成功给 1但 done 仍为 0或者反过来。需要保证“成功即终止”否则 Q 值的 bootstrapping 会污染。goal 没有在新经验里同步替换。正如前面说的只换了 reward没换 obs/goal等于教模型对着旧目标学新答案。排查方法也很直接在训练前写一个 check 脚本手动构造一条已知 transition跑一遍 relabel 逻辑打印新旧 goal、reward、done 是否正常。这个 10 分钟就能写完的脚本能帮你省下一整天的 debug 时间。5.2 训练后期震荡和遗忘HER 不是一锤子买卖要配好“保鲜”机制HER 训练到中后期我经常遇到“成功率上去了又掉下来”的情况。一开始以为是学习率太大后来发现更核心的问题是重标注目标分布和真实目标分布偏差变大。前期 agent 啥也不会重标注目标大多离真实目标很远训练还算稳后期 agent 学得不错重标注目标开始集中于真实目标附近但真实目标本身仍有一定比例随机探索失败样本两批数据在分布上打架导致策略频繁震荡。我的应对方案有三个降低relabel_prob到 0.50.6让更多原始目标样本占主导。降低后期学习率比如用 cosine schedule让策略不要剧烈更新。定期冻结目标网络更新比例让 critic 的估计稳住了actor 再跟着变。这三个手段叠加起来“震荡遗忘”基本可以被压制住。但说句实在话HER 并不是为了彻底解决稳定性而生的它更像“样本效率加速器”。如果任务本身难到超出容量再稳也没用。5.3 索引越界和维度不匹配最容易被模糊错误掩盖的 bugHER 实现里字符串比较多的代码在“从未来状态中采样”这里极易出现索引越界。比如轨迹长度为 T你遍历到 t T-1 时未来状态集合只有当前状态本身random.sample的 k 可能会超过集合长度。代码里我习惯用min(future_k, len(future_states))但这只是第一层保险第二层保险是if len(future_states) 0: continue防止某些环境终止状态突然截断导致空集合。维度不匹配是个隐性 bug通常报错发生在concat(obs, goal)时但有时候两个维度刚好相同程序不报错只是语义错乱。比如 obs 可能是 10 维goal 也是 10 维obs 里已经包含了当前物体位置goal 是目标位置如果你不小心把 obs 整体当作新目标来替换网络的输入维度没变但语义完全错位。训练曲线会表现为“有点学习迹象但永远接近不了真实目标”。排查时我建议打印一段样本肉眼检查 obs 和 goal 是否对得上号。5.4 收益不明显可能是任务本身不适合 HER不是你实现错了最后说一点经验之谈。HER 不是万能的它适用的任务有一个共同特点任务的目标是可扩展、可达或部分可排序的。比如“推物体到任意位置”任意位置都是潜在目标“学到把物体推到 A”确实有助于推到 B。但如果任务是“拧开瓶盖”目标状态只有两种——拧开和没拧开中间不存在“把瓶盖拧到 30 度”这种目标连续性HER 的重标注收益就很弱。另外我也试过在多智能体协作任务里用 HER效果一般。原因是重标注会干扰其他智能体的观察导致训练不稳定。所以先判断任务是否具备目标空间的可复用性再决定要不要用 HER。怎么判断简单粗暴的实验方法是用一个随机的 state 作为目标试跑几次看模型能不能快速学到“到达随机状态”的能力。如果能HER 大概率会有效如果连随机目标都学不会HER 也很难救场。5.5 常见问题速查表症状可能原因解决方案训练曲线完全不动reward 用错状态done 逻辑错goal 未同步替换写 relabel 单测逐字段打印检查训练后期成功率暴跌目标分布冲突学习率过大调低 relabel_prob学习率 schedule软更新减慢目标明明很近但学不会维度未归一化obs 和 goal 尺度差异大分别归一化统一量纲采样时报索引错误future 集合为空或长度不足用 min(k, len) 空集合跳过成功率有时高有时低replay buffer 太小经验被覆盖太快增大 buffer 容量使用 on-policy 算法配合 HER理论不兼容切换 DDPG / SAC 等 off-policy 算法5.6 调参优先级参考我总结一下调参的先后顺序新手照着做能少走弯路先确认代码逻辑正确尤其 relabel 后的 reward、done、goal 是否一致。再调 reward 的稀疏/稠密设计不要一上来就加一堆辅助奖励。然后调relabel_prob和future_k这两个参数决定重标注分布。最后调学习率、tau、batch size这些是锦上添花。如果训练还上不去检查归一化和网络容量。如果前面的逻辑全对relabel_prob调到 0.8、future_k调到 4训练曲线还不上涨那大概率不是 HER 的问题而是任务本身不够适合这个方法。5.7 我的经验总结与一个小技巧hindsight 这个思想除了强化学习我后来也应用在了数据分析、模型评估里——很多“失败”的模型输出换个评价口径其实就是高价值样本。回过头看它教会我最重要的一件事不要轻易丢弃看起来失败的数据换个目标重新看它们往往是宝藏。做实验遇到瓶颈时我总会提醒自己先别质疑模型先看看是不是“目标定义”出了问题。最后分享一个小技巧如果你想快速验证 HER 的收益不要一上来就对比最终成功率而是先对比首次正奖励出现的时间。HER 显著的优势就是让第一次奖励提前出现这个信号比“最终成功率”更直观也能帮你迅速判断实现是否正确。只要第一次正奖励出现在合理时间内后面稳住训练参数成功率通常只是时间和算力的问题。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表