强化学习零碎知识点笔记

np.array_equal

比较numpy数组尽量不用==,要用np.array_equal。
两个 numpy 数组用==时,不会像普通变量 / 列表那样返回「单个 True/False」,而是对两个数组中对应位置的元素逐一比较 ,返回一个和原数组形状完全相同的布尔数组。

1
2
3
4
5
6
7
8
9
import numpy as np
# 任务1的位置:[5,6],任务2的位置:[5,7],任务3的位置:[5,6]
loc1 = np.array([5,6])
loc2 = np.array([5,7])
loc3 = np.array([5,6])

# 数组用==,逐元素比较,返回布尔数组
print(loc1 == loc2) # 输出 [ True False] → x相等,y不相等
print(loc1 == loc3) # 输出 [ True True] → x、y都相等

np.array_equal(a, b)是专门判断两个数组是否完全相等的函数,核心特性:

  1. 先判断两个数组的形状是否一致(比如都是 2 维、都是 N×2),形状不同直接返回 False;
  2. 形状一致则逐元素比较,所有元素都相等才返回单个 True,否则返回单个 False;
  3. 返回值是单个布尔值,可以直接用在if判断里,完美解决数组比较的问题。
1
2
np.array_equal(loc1, loc2)  # False(元素不全等)
np.array_equal(loc1, loc3) # True(所有元素相等,形状也一致)

@dataclass、default_factory 与 Lambda

1. @dataclass 装饰器

来源from dataclasses import dataclass (Python 3.7+)
定义:一个用于简化类定义的装饰器,专门用于创建主要存储数据的类(Data Class)。
核心功能:自动生成 __init____repr____eq__ 等样板代码,让代码极度简洁。

写法对比

  • 传统写法(手动挡)
    1
    2
    3
    4
    5
    6
    class Point:
    def __init__(self, x, y):
    self.x = x
    self.y = y
    def __repr__(self):
    return f"Point(x={self.x}, y={self.y})"
  • Dataclass 写法(自动挡)
    1
    2
    3
    4
    @dataclass
    class Point:
    x: int
    y: int

2. field(default_factory=…)

核心痛点:可变默认参数陷阱
在 Python 类中,绝对不能直接使用可变对象(如 list, dict, set)作为类变量的默认值。
错误写法:items: list = []
后果:所有该类的实例会共享同一个列表内存地址。修改 A 对象的列表,B 对象的列表也会随之改变。
为了解决共享问题,我们需要告诉 Python:不要使用现成的对象,而是每次实例化时,调用一个“工厂函数”现场创建一个新对象。
示例:

1
taskTypes: List[str] = field(default_factory=lambda: ["search", "fire", "facility"])

ROS 2 与 Gazebo 桥接器 (ros_gz_bridge) 语法精讲

在 Launch 文件中配置 parameter_bridge 时,字符串格式非常严谨,核心公式为:
话题名称@ROS数据类型<方向号>Gazebo数据类型

1. 符号含义:

  • @:分隔符,分隔话题名称和数据类型。
  • ]:单向通信,数据从 ROS 2 流向 Gazebo(例如:下发控制指令、推力)。
  • [:单向通信,数据从 Gazebo 流向 ROS 2(例如:获取传感器数据、Odometry 里程计位置)。
  • @== (有些版本支持双向):双向通信。

2. 核心代码结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from launch_ros.actions import Node

# 1. 定义桥接规则列表
bridge_params = [
'/topic_name@ros_type]gz_type', # ROS -> Gazebo
]

# 2. 创建 Node 节点
bridge = Node(
package='ros_gz_bridge',
executable='parameter_bridge',
arguments=bridge_params,
output='screen'
)

Python 避坑笔记:为什么 VS Code 找不到代码引用/不高亮?

问题现象

在 VS Code 中选中一个函数(比如 step),但在其他类中调用它的地方(比如 self.bandit.step())却没有高亮显示。右键点击“查找所有引用”也毫无反应,让人误以为这个函数没被用过。

根本原因:Python 的“动态类型”特性

Python 是一门非常自由的语言,声明变量时不需要指定类型。
当你写下 def __init__(self, bandit): 时,VS Code 的代码分析器(Pylance/IntelliSense)并不知道传入的 bandit 到底是个什么对象(是数字?是字符串?还是老虎机?)。因为不知道身份,VS Code 为了避免报错,干脆就不进行跨文件/跨类的高亮关联。

终极解决办法:类型提示 (Type Hint)

在定义参数时,顺手给它“贴个标签”,明确告诉 VS Code 它的真实身份。

  • 修改前(VS Code 无法识别):
    1
    def __init__(self, bandit):
  • 修改后(VS Code 瞬间变聪明):
    1
    def __init__(self, bandit: BernoulliBandit): 
    (加上 : BernoulliBandit 后,VS Code 瞬间就能把两个类关联起来,代码高亮、Ctrl + 点击 跳转、自动补全全部复活!)

备用方案:暴力搜索法

如果是在阅读别人写的老代码(没有类型提示),千万别依赖高亮来判断函数有没有被调用。请直接使用:

  1. 单文件搜索Ctrl + F
  2. 全局搜索(最管用)Ctrl + Shift + F,在整个工程文件夹里直接搜索函数名。

强化学习:折扣回报与贝尔曼方程核心概念

1. 折扣回报 (Discounted Return) 的理论公式:
衡量一个状态的好坏,不仅看当下,还要看未来。
$G_t = R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + \dots = \sum_{k=0}^{\infty} \gamma^k R_{t+k+1}$

  • $\gamma$ (Gamma):折扣率 (0 到 1 之间)。决定了智能体是“目光短浅”($\gamma$ 接近 0)还是“高瞻远瞩”($\gamma$ 接近 1)。

2. 解决“未知未来”的两大落地方法:

  • 蒙特卡洛方法 (Monte Carlo):必须等一个回合 (Episode) 彻底结束,拿到所有真实的 $R$ 序列后,再反向计算 $G_t$ 进行更新。属于“事后结算”。
  • 时序差分法 (Temporal Difference, TD):不需要等回合结束,走一步算一步。核心在于利用贝尔曼方程
    $$V(s_t) = \mathbb{E}[R_{t+1} + \gamma V(s_{t+1})]$$
    利用【当下真实的即时奖励 $R_{t+1}$】加上【下一个状态的预估价值 $\gamma V(s_{t+1})$】,来更新【当前状态的价值 $V(s_t)$】。

核心机制:经验回放 (Experience Replay)

1. 经验元组 (Transition Tuple)
智能体与环境交互的基本记录格式:$[s, a, r, s’]$

  • $s$: 当前状态 (State)
  • $a$: 动作 (Action)
  • $r$: 奖励 (Reward)
  • $s’$: 下一状态 (Next State)
    (注:有时候还会加上一个 $done$ 标志,记录游戏是否结束,变成 $[s, a, r, s’, done]$)

2. 机制流程

  • 存储 (Store):智能体在探索环境时,将每一步的 $[s, a, r, s’]$ 存入一个固定容量的队列 (Memory/Buffer) 中。如果存满了,新的经验会挤掉最老的经验。
  • 采样 (Sample):在训练更新网络时,从 Memory 中随机抽取 (Random Sample) 一批大小为 batch_size 的经验进行学习。

3. 为什么必须随机抽样?

  • 打破时间相关性 (Break Correlation):连续的样本之间高度相似,会导致神经网络训练产生震荡甚至崩溃。随机抽样能让送入网络的数据分布更加均匀。
  • 提高数据利用率:过去犯下的惨痛错误(稀有经验),可以在未来被多次随机抽中并反复复习,而不是经历一次就被遗忘了。

机器学习核心概念:偏差、方差与拟合状态

在机器学习中,模型的总误差可以分解为:$Error = Bias^2 + Variance + Noise$

1. 欠拟合 (Underfitting) $\rightarrow$ 高偏差 (High Bias)

  • 表现:模型在“训练集”和“测试集”上的表现都很差,准确率都不高。
  • 原因:模型过于简单(如用直线拟合曲线),表达能力不足,无法捕捉数据中的真实规律。
  • 形象比喻:瞄准镜歪了。预测结果集体偏离真实值。

2. 过拟合 (Overfitting) $\rightarrow$ 高方差 (High Variance)

  • 表现:模型在“训练集”上表现极好(甚至 100% 准确),但在“测试集”上表现极其糟糕。
  • 原因:模型过于复杂,把训练数据里的噪声和偶然特征也当成规律死记硬背了下来。泛化能力极差。
  • 形象比喻:手抖得厉害。稍微改变一下训练数据,训练出的模型就会剧烈变动,极不稳定。

3. 终极目标:偏差-方差权衡 (Bias-Variance Tradeoff)
我们想要的是一个既能看懂规律(低偏差),又不过度敏感(低方差)的模型,也就是打靶时既对准靶心,手又稳(子弹密集击中靶心)。