- 博客(64)
- 收藏
- 关注
原创 RL 系统 Infra 笔记:区分不同模型
本文系统梳理了强化学习系统(RLHF/PPO)的核心架构与训练流程。主要内容包括: 系统概览:通过"老师批改作文"类比解释RL系统的工作机制,核心流程为生成回答→评分→模型更新循环。 模型架构:明确系统包含3个主要模型实体(可训练的Actor模型、冻结的Reference模型和Reward模型),澄清常见混淆点。 模块详解: Rollout阶段:Actor生成回答并记录token概率 Reference模型:提供KL约束防止训练偏离 Reward模型:独立打分 Advantages计算:
2026-04-21 22:48:06
528
原创 RL系统中的异步编程:async & Ray
本文总结了异步编程与分布式强化学习系统的关键概念与实践方法。主要内容包括: 核心概念对比:同步(串行等待)、异步(并发切换)和分布式(多机协作)的执行模式差异。 Python异步编程工具:重点介绍asyncio的async/await语法、create_task后台执行、gather与wait的任务管理方式。 分布式计算基础:讲解远程任务标记(@remote)、异步句柄(Future)的使用,以及阻塞获取(get)与非阻塞监控(wait)的实现。 微服务部署:说明@de
2026-04-21 15:47:07
385
原创 verl 中序列长度相关配置梳理:理清数据、Rollout 与 PPO 训练边界
本文系统梳理了 verl 中与序列长度相关的参数配置,将其划分为三个关键层级:数据层、Rollout层和PPO训练层。数据层参数(如max_prompt_length、filter_overlong_prompts)决定样本能否进入系统;Rollout层参数(如max_model_len、max_num_batched_tokens)控制样本能否正常生成;PPO训练层参数(如ppo_max_token_len_per_gpu)则影响训练稳定性。文章通过流程图清晰展示了样本处理流程,并重点区分了容易混淆的参数
2026-03-18 17:01:15
360
原创 深入理解大模型并行:为什么 vLLM 强制要求 EP = TP × DP?
摘要:混合专家(MoE)模型部署时存在EP=TP×DP的约束问题。理论上有两种拓扑方案:DP组内EP(EP=2)和全局EP(EP=4)。vLLM强制采用后者主要基于三大优势:1)零冗余显存使用,专家权重完全切片;2)全局路由实现更好的负载均衡;3)简化通信拓扑结构。这种设计通过扩大通信域换取更高的显存利用率和更稳定的性能,体现了工程实现中对推理场景的针对性优化,而非理论限制。理解这一约束有助于合理规划集群配置。
2026-03-18 15:13:07
494
原创 AI 开发常用官网
本文整理了AI工程师常用的开发资源,涵盖代码托管平台(GitHub/Gitee)、AI社区(Hugging Face/ModelScope)、训练框架(VERL/MS-Swift)、推理引擎(vLLM/SGLang)以及国内Python镜像源(阿里/清华/华为)。提供各平台官方链接和文档地址,并包含pip镜像源的配置方法。适合AI开发者收藏参考,持续更新中。
2026-03-03 17:05:57
340
原创 Linux & AI 开发实用工具速查笔记
本文整理了高效开发中常用的命令速查指南,涵盖Docker、Git、rsync、screen及Linux日志管理等核心工具。重点包括:Docker镜像迁移:对比save(保留元数据)和export(文件快照)的区别,提供容器完整迁移方案Git操作:快速查询commit ID、切换历史版本及强制回退提交的方法文件同步:详解rsync的-avzP参数组合,实现高效本地/远程文件传输会话管理:screen命令创建持久化终端会话,避免任务中断日志管理:完整后台任务执行方案(> log 2
2026-02-24 14:55:21
812
原创 计算机基础八股记录
一个进程由多个线程组成。进程在执行过程中拥有独立的内存单元,而多个线程共享内存。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间。同步:生产者消费者;互斥:竞争某一资源。
2023-08-30 23:27:25
274
原创 C++八股记录
这样一个父类指针指向一个子类对象,想要调用某虚函数时,通过对象内存地址找到的是子类虚函数表,就可以调用子类的版本。这就实现了多态,即一个父类指针指向不同对象,调用相同的函数,表现出不同的形态。只要含有虚函数的类就会为该类生成一个全局唯一虚函数表,虚函数表中记录类中虚函数的入口地址,同时会在每个实例内存的开始生成一个虚函数表指针,指向虚函数表。而当一个子类继承一个有虚函数的父类后,同时也会继承父类的虚函数表,如果子类重写了一个虚函数,就会把虚函数表中该虚函数的地址改成重写后的版本。new分配的内存块;
2023-08-30 23:02:31
1016
原创 代码随想录 | Day 60(完结) - LeetCode 84. 柱状图中最大的矩形
第0位的左边界(不包含在内)应该是-1,末尾位的右边界(不包含在内)应该是“末尾 + 1”,所以indSmallerLeft[0]和indSmallerRight[height.size() - 1]分别初始化为-1和height.size()。相比接雨水,主要是从“找左、右两边各自最高的柱子”变成了“要找左、右两边各自第一个比当前柱子小的位置”,使其分别作为左、右边界(不包含在内),再令当前柱子的高作为矩形的高,两者相乘得到当前位置的矩形大小。今天是单调栈的最后一天,延续了昨天的接雨水问题。
2022-11-20 00:00:23
465
原创 代码随想录 | Day 59 - LeetCode 503. 下一个更大元素II、LeetCode 42. 接雨水
这个高度取决于当前位置左边部分的最高柱子、和右边部分最高柱子,两者中较小值,再减去当前位置的柱子高度,就是当前位置的雨水高度。在遍历之前,可以将最高柱子高度初始化为当前柱子高度,这样一来,如果左边或右边没有更高的柱子,最后当前位置的雨水量就是0。第3种情况的实现较为复杂,需要首先得到中间柱子,然后使其出栈,再得到左柱子,然后再计算宽度、高度,加雨水量。是三种方法中最难的一种,核心思想是维持单调递减的栈,在出现非递减元素时,弹出栈中元素的同时加雨水,每次所加的雨水数量都是其宽度和高度的乘积。
2022-11-19 17:14:54
809
原创 代码随想录 | Day 58 - LeetCode 739. 每日温度、LeetCode 496. 下一个更大元素 I
不符合的话则需要弹出所有比当前数值小的元素,并同时对弹出元素对应位置的结果赋值(结果就是当前元素下标与被弹出元素下标的差值),最后。为实现这一目的,理论上既需要知道每个元素的值,又需要知道其下标,似乎需要将两者都放入栈中。这样的做法会使得右边没有更大数字的元素永远无法进行res的赋值,而题目要求这些元素的结果均设置为0,所以可以在初始化时就将所有结果设置为0。判断当前元素是否在map中存在,既可以像上面一样使用map.find(),也可以通过判断map.count()的结果是否为0来判断。
2022-11-18 20:58:22
788
原创 代码随想录 | Day 57 - LeetCode 647. 回文子串、LeetCode 516. 最长回文子序列
前两种情况的dp取值都不依赖于其他dp值,而第3种情况的dp[i][j]则依赖于dp[i + 1][j - 1],也就是其左下角位置。所以需要初始化dp矩阵的对角线(对应情况1)以及对角线上每个值右边的第1个位置(对应情况2),这样才能保证在情况3下,每个位置的左下角被填充。)相比上一题,将“连续的子串”改为了“非连续的子序列”,问题也从“回文子串的数量”变成了“最长回文子序列的长度”。因为问题的转变,所以定义方面,这道题的dp[i][j](i ≥ j)变成了“s[i : j]的最长回文子串长度”。
2022-11-17 18:04:34
608
原创 代码随想录 | Day 56 - LeetCode 583. 两个字符串的删除操作、LeetCode 72. 编辑距离
对于第1种可能,由于dp[i - 1][j - 1] + 1要么与dp[i - 1][j]相等,要么与dp[i][j - 1]相等(因为删除次数+1对应多删1个元素,即多一个元素),所以dp[i - 1][j - 1] + 2等于dp[i - 1][j] + 1或dp[i][j - 1] + 1。两者相等时,相对于word1[0 : i - 2]和word1[0 : j - 2]不需要额外的删除次数,所以删除次数仍然是dp[i - 1][j - 1];另外这两道题的初始化部分,不再是全部初始化为0了。
2022-11-17 00:20:28
380
原创 代码随想录 | Day 55 - LeetCode 392. 判断子序列、LeetCode 115. 不同的子序列
第0行,对应s为空,dp[0][j](j > 0)对应空字符“删除任意字符后出现t[0 : i - 1]”的个数,空字符无论怎么删除都还是空字符,所以有0中删除方式,dp[0][j](j > 0)都应该初始化为0。)需要首先将问题转化为“求s和t的最大相同子序列长度”,用dp[i][j]表示“s[0 : i - 1]和t[0 : j - 1]的最大相同子序列长度”(使用i - 1、j - 1而不用i、j的原因与之前一样,还是为了后面的初始化方便)。不断的右移t指针,使t指针所指的值匹配s指针所指的值。
2022-11-16 02:48:08
303
原创 代码随想录 | Day 53 - LeetCode 1143. 最长公共子序列、LeetCode 1035. 不相交的线、LeetCode 53. 最大子序和
对于text1[i - 1]与text2[j - 1]相等的情况,最长公共子序列长度相比没有text1[i - 1]和text2[j - 1]时就要增加一位,所以dp[i][j] = dp[i - 1][j - 1] + 1;),子序列不再要求是连续的,剩余地方都与其一样。实现中需要注意,因为为了初始化的方便而采用了额外多一行、一列的dp矩阵,所以与dp[i][j]对应的是text1[i - 1]和text2[j - 1]是否想等的判断,而不是text1[i]和text2[j]。
2022-11-16 00:57:35
381
原创 代码随想录 | Day 52 - LeetCode 300. 最长递增子序列、LeetCode 674. 最长连续递增序列、LeetCode 718. 最长重复子数组
所以要计算dp[i]的话,就要从0遍历到(i - 1)位置,遇到比nums[i]小的数字nums[j],就将dp[j]加上1,然后取所有dp[j] + 1(j < i)当中的最大值作为dp[i]的结果。这一题要用到二维dp数组,dp[i][j]定义为“对于nums1的[0, i]部分,nums2的[0, j]部分,两者分别以nums1[i]和nums2[j]结尾的最长重复子数组长度”。当nums1[i]与nums2[j]相等时,这一长度就在dp[i - 1][j - 1]的基础上再增加1;
2022-11-15 00:03:51
346
原创 代码随想录 | Day 51 - LeetCode 309.最佳买卖股票时机含冷冻期、LeetCode 714.买卖股票的最佳时机含手续费
这道题的重点仍然是状态的定义和划分,状态整体上仍然分为“持有”和“不持有”两大类。其中“不持有”又分为“当天卖出”、“当天是冷冻期”、和“当天已经过了冷冻期”这3种状态,加上“持有”共计4种状态。将“持有”、“当天卖出”、“当天是冷冻期”、和“当天已经过了冷冻期”分别设为状态0~3。所以要做出的相应改变就是,从“持有”状态向“不持有”状态转移时,要减掉手续费,也就是将原本的dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] +主要是第1题的冷冻期问题比较复杂。
2022-11-14 22:14:16
403
原创 代码随想录 | Day 50 - LeetCode 123. 买卖股票的最佳时机III、LeetCode 188. 买卖股票的最佳时机IV
第0天就处于状态1的话,只可能买入第0天的股票,所以dp[0][1] = -prices[0];虽然第0天无法处于状态2,但一方面可以看作是第0天买入后又立即卖出,另一方面卖出时的利润都是不低于0,设置为0可以保证后面的比较大小顺利进行,所以dp[0][2] = 0;由于最多可以买卖两次,所以原本“持有”和“不持有”这两种状态不再够用,需要扩展为“未曾买卖”、“买过第1次”、“卖过第1次”、“买过第2次”、“买过第2次”这5种状态,第i天每个状态下能获得的最大利润对应dp[i][0]~dp[i][4]。
2022-11-14 21:07:10
390
原创 代码随想录 | Day 49 - LeetCode 121. 买卖股票的最佳时机、LeetCode 122.买卖股票的最佳时机II
所以“第i天持有”的第2种情况,也就是“之前未买入,第i天买入”需要改为“第(i - 1)天未持有,第i天买入”。所以状态转移方程就可以总结为dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])和dp[i][1] = max(dp[i - 1][1], -prices[i])。dp数组的第i天有两个数值,dp[i][0]表示第i天“不持有”股票对应的最大所得现金,dp[i][1]表示第i天“持有”股票对应的最大所得现金。
2022-11-13 18:06:34
432
原创 41.动态规划(9) | 打家劫舍、打家劫舍II(h)、打家劫舍III(h)
而如果打劫的话,就意味着上一个房屋不在考虑范围内,所以要取上“上一个房屋对应的dp值”与“当前房屋价值”的和,即dp[i - 2] + nums[i]。而如果打劫当前节点,那么其左右子节点都不能被打劫,只能选择各自dp的第0位,再加上当前节点的val,对应cur->val + dpLeft[0] + dpRight[0]。如果打劫当前节点,那么当前点的左右子节点就都不能被打劫了。所以应该考虑当前节点的孙子子树,也就是当前节点的“左子节点的左右子节点、右子节点的左右子节点”,这4个节点作为根节点的4棵树。
2022-11-07 22:00:50
454
原创 40.动态规划(8) | 单词拆分(h)、多重背包
代码中需要注意,递归的循环中,i一定是从indBegin开始的。自己起初是设置为从indBegin + 1开始的,那么下一层递归的indBegin参数就不再应该是(i + 1)了,而应该是i,否则会错误地略过当前层的第(indBegin + 1)个元素。该问题有多种解法,其中比较容易理解和实现的是转化为01背包问题,也就是把每个数量为n的物品拆分为n个数量为1的物品,然后用01背包的解法解决。,当前的目标字符串,也就是当前背包容量,对应的物品是“当前目标字符串的所有后缀”,而所有物品就是。
2022-11-07 15:46:33
345
原创 39.动态规划(7) | 爬楼梯、零钱兑换、完全平方数(h)
第1题(LeetCode 70. 爬楼梯 (进阶))是day 38中的第2题,当时用了斐波那契数列的解法。也可以将其看作完全背包问题,背包容量对应总台阶数,物品对应每次能上的台阶数。比如在该问题中物品数共2件,第一件物品的重量和价值都是1,第二件物品的重量和价值都是2。具体的背包问题跟day 44中的第3题(LeetCode 377. 组合总和 Ⅳ)一样,是求排列数量。所以按照dp[j] += dp[j - weight[i]]的状态转移方程,dp[0] = 1作为初始化,外层循环遍历背包、内层循环遍历物品
2022-11-05 20:23:44
625
原创 38.动态规划(6) | 完全背包(h)、零钱兑换 II、组合总和 Ⅳ(h)
dp[i][j]的定义是与01背包问题一样的。所以用dp[i][j]表示“有前i件物品,装满容量j的方法数”的话,状态转移方程就变成了dp[i][j] = dp[i - 1][j] + dp[i][j - weight[i]]。所以状态转移公式中的max()需要改为min(),同时将dp[0]以外的DP数值初始化为一个“很大的值”,dp[0]还是初始化为0。所以最终的状态转移方程是dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])。
2022-11-05 00:47:02
273
原创 37.动态规划(5) | 最后一块石头的重量 II(h)、目标和(h)、一和零(h)
多了当前的第i件物品后,会新增将当前第i件物品装进去的若干装法,为保证空间大小j需减小为(j - weight[i]),所以这部分装法数量对应dp[i - 1][j - weight[i]]。那么问题就转化为了“背包维度0容量为m,背包维度1容量为n,每个字符串对应一件物品,每个物品的价值都为1,维度0的重量为该字符串中字符'0'个数,维度1的重量为该字符串中字符'1'个数,这种情况下的01背包问题”。而对于这两个集合,只要求得其中一个的和,用“整体和”减去它,便能得到另一个的和了。两者之差尽可能地小。
2022-11-04 01:16:29
179
原创 36.动态规划(4) | 01背包(h)、分割等和子集(h)
另外在矩阵版本的双重循环中,由于(j - weights[i])可能会小于0,所以在其小于0时,直接选择上方的值dp[i - 1][j]。又因为遍历方向是从右向左,所以j是在递减的,那么(j - weights[i])就也是在递减。所以,只需要将内层循环进行到j - weights[i] < 0为止就可以了,所以内层循环条件可以设置为j - weights[i] >= 0,即j >= weights[i]。而对于容量,只能从0开始初始化,所以dp[i][0]对应背包最大容量为0,而不是最大容量为1的情况。
2022-11-02 23:56:46
288
原创 35.动态规划(3) | 整数拆分(h)、不同的二叉搜索树(h)
如果左子数有j个节点,那么右子树就有(i - 1 - j)个节点,j可以从1取到(i - 1),所以总共有(i - 1)种情况。这其中j和(i - 1 - j)都一定是小于i的,所以其左子树的类型就有dp[j]种,右子树的类型就有dp[i - 1 - j]种,两者相乘就得到了“左子树有j个节点情况下,整个树可能的结构数”。对于数字i,它总是会被拆分成2个或2个以上的数字。所以这道题的状态转移方程是dp[i] = max(j * (i - j), j * dp[j]),其中j需要从1遍历到(i - 1)。
2022-10-31 19:47:06
177
原创 34.动态规划(2) | 不同路径、不同路径 II
初始化方面,由于第0行的所有点都是只能由出发点向右走的,第0列所有点都是只能由出发点向下走的,所以第0行和第0列的值都应该是1。题解中还给出一种数论的解法。如果惯性思维将其初始化为1,那对于“第0行的第0个元素就是障碍物,但其他行的第0列元素并没有障碍物”的情况,由于是从第1行开始遍历的,那就会错误地将其他行的第0列元素设置为1。如果使用搜索的方法,那么搜索树的深度就是m + n - 1(深度按从1开始计算),二叉树的节点数就是2^(m+n-1)了,对应时间复杂度O( 2^(m+n-1) ),所以会超时。
2022-10-31 00:54:50
193
原创 33.动态规划(1) | 斐波那契数、爬楼梯、使用最小花费爬楼梯
所以已知了第i - 1阶和第i - 2阶楼梯的最小花费后,再令两者各自加上自己前进所需的花费,即cost[i - 1]和cost[i - 2],就得到了2种路线和对应的花费。从两者中取较小的一个当做第i阶的花费即可,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]。因为每次只能上1阶或2阶,所以对于i阶楼梯,它的上一步要么是第i - 1阶,要么就是第i - 2阶。
2022-10-30 22:47:54
226
原创 32.贪心(6) | 单调递增的数字(h)、买卖股票的最佳时机含手续费(h)、监控二叉树(h)
第1题(LeetCode 738. 单调递增的数字)自己的思路跟题解比较相近,但有缺陷,且最终没有实现成功。贪心策略是让不符合要求的右半部分数字都变为9,且同时左半部分也要改变,使得变化后的数字要小于原数字。所以关键问题是找到上述左右部分的分割点。 具体做法是首先将数字转化为对应的字符串。因为题目要求从左至右要满足非递减关系,所以如果从左向右遍历的话,如果为了满足当前数字小于右边数字的要求而让当前数字减1,那么当前数字左边的数字,或许又不满足要求了。所以应该从右向左遍历,只要遇到当前数字大于右
2022-10-30 19:51:52
151
原创 31.贪心(5) | 无重叠区间、划分字母区间(h)、合并区间
排序后,将首个区间的左边界设置为begin,右边界设置为end,然后开始遍历剩余区间,贪心目标是让当前区间尽可能多地与其他区间合并。所以,需要预设一个最短段下标,将其设置为第一个字母出现的最大下标,并在遍历时不断根据新字母出现的最大下标,将其更新为更大的值。只要遍历到该最短段下标,就说明段内的所有字母都出现在当前段内,不会出现在之后,于是就可以将当前遍历过的部分当作一段,并开始下一段的遍历。在遍历时,遇到区间的左边界大于或等于重叠区间右边界时,就对不重复区间数+1,并更新重叠区间右边界为当前区间的右边界。
2022-10-29 22:43:58
164
原创 30.贪心(4) | 柠檬水找零、根据身高重建队列(h)、用最少数量的箭引爆气球(h)
此外,可以用一个新的vector来保存结果,当要插入某个元素(对应下标i)时,因为结果中保存的元素身高都不小于该元素,其目标插入位置就是people[i][1],不需再用一个循环来确定。比如输入为[[7,0],[5,0],[7,2],[6,1],[5,2],[7,1]]时,上面错误代码的输出就会是[[7,0],[5,0],[7,1],[6,1],[7,2],[5,2]],而正确答案是[[5,0],[7,0],[5,2],[6,1],[7,1],[7,2]]。函数,需要再次熟悉下。
2022-10-29 19:31:19
400
原创 29.贪心(3) | K次取反后最大化的数组和、加油站(h)、分发糖果
首先对于第0个点,由于sumCur是第0~i个点的汽油剩余量的和,相当于模拟了从第0个点出发的过程,而到第i个点时汽油剩余量却变成了负数,说明从0个点出发不成功。而对于从第1个点出发的选择,因为第0个点的汽油剩余量一定是非负数(否则在第0个点时,sumCur就已经被更新为0了),所以从第1个点出发的sumCur一定是小于或等于从第0个点出发的sumCur的。那么既然从第0个点出发的sumCur都已经是负数,从第1个点出发的sumCur就也一定是相等或更小的负数了,所以从第1个点出发也是不行的。
2022-10-29 13:44:29
375
原创 28.贪心(2) | 买卖股票的最佳时机II、跳跃游戏(h)、跳跃游戏II
所以可以探索下下标1~3数字的覆盖范围,于是得到下标1数字2的覆盖范围是下标2~3,下标2数字1的覆盖范围是下标3,下标3数字0的覆盖范围为空。而对于数组[2, 3, 1, 1, 4],下标0数字2的覆盖范围是下标1~2。具体来说,如果设当前下标为0,当前最多能跳2步,对应的下两个数字是[5, 1],那么当前跳1步的话,下一步的最大跳跃下标是1 + 5 = 6;这一题同样有DP解法,但DP在之后的章节会专门练习,其中也会有这一题,所以这里和之后的贪心算法题目中不再研究DP解法,在之后的DP章节中再研究。
2022-10-28 22:49:17
1183
原创 27.贪心(1) | 分发饼干、摆动序列、最大子序和(h)
反之,如果上一个转折点是山峰,峰点数字被保存为pre,那么遇到比pre小的数字也不能说明就是山谷,只能说明比山峰小,它之后可能紧跟更小的数字。而对于一些在山腰的数字,虽然在最优解下是被认定为山腰的,但这并不妨碍它可以被认定为山峰/山谷,只不过得到的不是最优解,最坏情况下,它被认定为山峰/山谷后得到的答案是1。而初始化方面,因为题目说一个数字也是1个摆动序列,所以对应的最小值为1,所以就需将dp的第0行2个数字都初始化为1,并在遍历nums时,每次都将nums[i]的个数字也都初始化为1。
2022-10-28 18:19:06
382
原创 26.回溯(6) | 重新安排行程(h)、N皇后、解数独(h)
第1题(332. 重新安排行程)自己艰难地AC。这道题用DFS更合适些,但还没刷到那部分,就先练习下回溯解法。按照自己的思路,首先对备选车票按照目的地的字典序排序。然后向存放结果的vector,res中添加"JFK",再用day 29中排列问题的解法对车票进行循环遍历和递归。当当前元素对应的used表为true,或当前车票不是上一张车票的后继时跳过。 方法比较简单,但经历了多次失败。起先是因为将递归函数出口像之前的题目一样直接写成了:而其他地方也没有任何处理。如果写成这样,递归函数会返回
2022-10-24 00:07:09
471
原创 25.回溯(5) | 递增子序列(h)、全排列、全排列 II(h)
但本题中,由于数组中有重复元素,如果用上一题的方法则无法区分当前树枝上两个或多个数值相同的数字,所以应该将bool数组的大小调整为与备选数组一致,这样才能与备选数组中的数字一一对应。这是因为排列不同于组合,在树层中遍历的起始下标是0,而原本的i > begin也就替换为i > 0,这就导致如果上一层被选取的元素恰好与当前层第1个待选元素相等的话,当前层的元素无法被选取。一样,题目中可能有相等的数字,但要求结果不能有重复的序列。而题目又要求结果的排列不能重复,所以需要同一数层的去重,这里的去重针对的是。
2022-10-19 23:31:31
245
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅