实验室工作
Report this week (5.30 — 6.5)
Inverse kinematics
to control Piper going up and down;- Try to reproduce
GraspNet-1B
.
使用 IK 控制 Piper 机械臂的运动
Use curobo
project, needs the urdf
and usd
of Piper
Python 代码示例 (IK/FK 控制 Piper 机械臂)
1 |
|
安装 GraspNet 环境
python
needs 3.8, the only one which can compile drivers successfully, butopen3d
errors (needglibc 2.18
)- Compile
open3d
by source code ot install byconda
(conda cannot do this)?(needclang
but I do not haveSUDO
) python3.7
, cannot compile other cuda drivers.
Report this week (6.6 – 6.12)
Curobo in server
- load pytorch by
pip
,conda
getsglibc
error, - need to run
pip install scikit_build_core
after load pytorch, module load cuda/12.1 gcc/9.3.0
beforepip install -e . --no-build-isolation
- 暂时无法解决
Configure Curobo of Piper
- urdf and usd,
- use Lula Robot to create sphere,
- some configure need to change,
- current result:
- possible result: FK different from hands and gripper.
Report this week (6.26 – 7.3)
- reproduce Minimind-V and understand the code of Vision-Language model;
VLM model
Assume that the batch size is , seqence length is , hidden size is , vocab size is .
- Input example: “<image>\n这个图像中有什么内容?”
- Prompt: “\n这个图像中有什么内容?”, input_ids: “ … … ”
- To tokens:
- Image feature:
- Replace the position of to image feature, so the input shape of transformer layers is
- Output shape of transformer(after RMSNorm) is also
- Output shape after the linear classification layer is
VLA model
Use the structure of OpenVLA, the last token ids are action tokens.
- Input example: “<image>\n机器人应该怎么做才能完成{任务}?答:”
For OpenVLA, image features are always placed at the beginning of the prompt. - Input_ids: “ … … ”
- To tokens:
- …
- Output shape after the linear classification layer is , and assume the last tokens are “ ”
- This last tokens would be decoded into action ranged , that is
Formula:
VLM to VLA
- Training mode: freeze vision model and train language model;
- Training data: , or common dataset, first using libero data?
- Model Architecture: requires implementing an action tokenizer and model architecture needs fine-tuning: for example, the image’s position must always be at the very beginning of the prompt.
- Inference Code: using the Libero simulation environment.
VLM Pretrained Result
The pretrained model has not been evaluated yet, given the huge loss gap compared to what author reported, I suspect there’s an issue with loading the pre-trained language model.
Next week
- Complete the VLA structure and training script;
- turning LIBERO dataset to RLDS or Lerobot or common dataset;
- 训练时的输入架构?查看openvla
- 直接通过训好的 vlm 进行训练
Report this week (7.3 – 7.10)
OpenVLA
架构研究与实现:深入分析了OpenVLA
的输入架构,并着手实现了类似的结构。Action Token
筛选:从 个词汇中,筛选出使用频率最低的 个token
序号,并整理生成了action_token_map.json
文件。Libero
数据集处理:成功下载了Libero
数据集,并将其处理成模型训练所需的格式。
Work
1. OpenVLA Input Structure
-
输入与输出形状 (Input/Output Shape):
输入/输出部分 维度/形状 (Dimension/Shape) <bos>
(起始符)[bs, 1, hidden_size]
image features
(图像特征)[bs, num * 196, hidden_size]
text features
(文本特征)[bs, text_length, hidden_size]
action tokens
(动作指令)[bs, 7, hidden_size]
<eos>
(结束符)[bs, 1, hidden_size]
-
时间错位机制 (Temporal Misalignment):
- 模型输入 (Input):
- 模型输出 (Output):
-
损失计算 (Loss Calculation) 策略:
- 官方策略:同时计算
text features
和action tokens
的损失。 - 备选策略:仅计算
action tokens
的损失。
- 官方策略:同时计算
-
后续优化点:
- 根据原论文,当前实现缺少本体感知特征和历史信息特征,可在后续版本中迭代添加。
2. Least Used Tokens
-
发现问题:与
Llama
不同,minimind
的词汇表并非按使用频率倒序排列。例如,词汇表的最后5个是常见英文词:"Ġeconomy": 6395
"Ġethically": 6396
"éĻĪ": 6397
"Ġschools": 6398
"Ġnetworks": 6399
-
解决方案:为精确找到真正低频的
token
以用作action token
,我下载了约 13G 的中英文语料数据集,并使用minimind
的tokenizer
对其进行词频统计,筛选出频率最低的词汇,并将其索引存入action_token_map.json
文件。
3. Libero Dataset
-
数据加载:直接利用 Hugging Face 提供的
lerobot
格式Libero
数据集,简化了数据获取流程。1
2from datasets import load_dataset
ds = load_dataset("physical-intelligence/libero")['train'] -
数据适配:对数据集进行了格式处理,当前阶段主要使用
ds['image']
(主视角图像),ds['wrist_image']
(腕部图像) 和ds['action']
(动作)。任务描述则通过json
文件加载。 -
LiberoDataset
类代码预览:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27class LiberoDataset(Dataset):
def __init__(self, hf_dataset, transform=None, task_file_path='dataset/meta/tasks.jsonl'):
self.dataset = hf_dataset
self.transform = transform
# 加载任务信息
self.task_info = load_task_info(task_file_path)
def __len__(self):
return len(self.dataset)
def __getitem__(self, idx):
sample = self.dataset[idx]
image = self.transform(sample['image'])
wrist_image = self.transform(sample['wrist_image'])
state = torch.tensor(sample['state'], dtype=torch.float32)
actions = torch.tensor(sample['actions'], dtype=torch.float32)
task_index = sample['task_index']
# 根据 task_index 获取任务描述
task_description = self.task_info.get(task_index, f"Unknown task {task_index}")
return {
'image': image,
'wrist_image': wrist_image,
'state': state,
'actions': actions,
'task_description': task_description,
}
Problems
- 模型格式问题:发现
minimind-v
预训练模型似乎没有可用的.pth
格式权重。加载官方提供的.pth
文件会导致输出乱码,只有Hugging Face
格式的model.bin
则可以正常使用。这是否意味着我们还是需要再进行预训练? - 语言能力限制:当前模型主要处理中文,这是否会对
VLA
(Vision-Language-Action) 模型的性能,产生负面影响?
Report this week (7.10 – 7.17)
- 成功让训练代码跑起来,但是结果还没有进行验证,也就是并没有训练到底,有可能训练代码存在问题;
- 全量微调正好需要吃掉24G显存
- 下周先训练一下这个模型,并且查看一下代码是否有问题,写一下 evaluation 的脚本
- 感觉可以在训练时给长任务分配大一点权重。
Report this week (7.17 – 7.24)
- 训练代码中增加动作正确率检测环节;
- 写好Libero的evaluation代码,但是还没有检查细节;
- 对数据集的处理进行了一些改动;
Training Scripts
训练代码中增加动作检测环节:
1 |
|
目前动作输出正确率如下,不知道结果是否正常。
训练轮数 | 训练集Loss | 测试集Loss | 训练集动作准确率 | 测试集动作准确率 |
---|---|---|---|---|
1 | ||||
2 | 0.6424 | 0.8550 | 0.1272 | 0.1165 |
3 | 0.5552 | 0.9446 | 0.1001 | 0.1016 |
4 | 0.4880 | 1.0350 | 0.0862 | 0.0949 |
5 | 0.4332 | 1.1186 | 0.0785 | 0.0793 |
LIBERO Evaluation
这个代码已经写好,现在正在检查细节是否有问题,预计下周可以测试一下训练出来的模型是否有效果。
Dataset
- 对main image 和wrist image使用不同的归一化代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17self.stats = load_stats(self.stats_path)
self.main_tfs = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=self.stats['image']['mean'], # [0.485, 0.456, 0.406]
std=self.stats['image']['std'] # [0.229, 0.224, 0.225]
)
])
self.wrist_tfs = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=self.stats['wrist_image']['mean'], # [0.512, 0.398, 0.321]
std=self.stats['wrist_image']['std'] # [0.201, 0.189, 0.243]
)
]) - 由于我认为动作准确率不高,所以对action数据,根据openvla论文中说的,首先收集好了每个动作维度的1st和99th中位数,将其放入stats.json中,接下来以它们为min action和max action,这样就可以减少异常动作的影响。(这个因为有7维动作,似乎需要7个min和max,但openvla中好像没有这个考虑,具体代码我还在实现中。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32def compute_and_save_action_quantiles(self, quantiles=[1, 99]):
"""
计算动作数据的分位数并保存到stats.json文件
quantiles: 要计算的分位数列表,例如 [1, 99]
stats_path: stats.json文件路径
"""
stats_path = self.stats_path
total_samples = len(self.dataset)
all_actions = []
for i in tqdm(range(total_samples), desc="Loading actions"):
sample = self.dataset[i]
actions = np.array(sample['actions'])
all_actions.append(actions)
all_actions = np.array(all_actions)
print(f"Action data shape: {all_actions.shape}")
# 计算每个维度的分位数
action_quantiles = {}
for q in quantiles:
action_quantiles[f'{q}th_percentile'] = np.percentile(all_actions, q, axis=0).tolist()
# 读取现有的stats.json
with open(stats_path, 'r', encoding='utf-8') as f:
stats_data = json.load(f)
# 更新actions部分
if 'actions' not in stats_data: stats_data['actions'] = {}
stats_data['actions'].update(action_quantiles)
with open(stats_path, 'w', encoding='utf-8') as f:
json.dump(stats_data, f, indent=4, ensure_ascii=False)
Problems
我觉得可以增加一些框架,对state进行处理。
Report this week (7.25 – 7.31)
- 给ActionTokenizer每个维度加上1st - 99th 分位数;
- 完成大部分的推理代码撰写,但是还没有调试通KV cache的部分;
- 完成ddp加速训练代码的撰写。
- 完成对action-only的loss计算。
1st Percentile
实现方式是把分位数提取出来放进config中,然后在解码的时候,根据不同的动作维度进行解码。
1 |
|
DDP
- 初始化:获得当前rank(0为主进程),所有的print信息只在主进程上打印
- 数据集需要并行处理:用DistributedSampler
1
2
3
4train_sampler = DistributedSampler(train_dataset, num_replicas=world_size, rank=rank)
val_sampler = DistributedSampler(val_dataset, num_replicas=world_size, rank=rank, shuffle=False)
sampler.set_epoch(epoch) # 保证每个epoch数据打乱的方式不同 - 模型需要封装:保存模型时,需要保存model.module.state_dict()。
1
model = DDP(model, device_ids=[local_rank], find_unused_parameters=True)
1
torch.save(model.module.state_dict(), checkpoint_path)
Action Only Loss
思路:在输出的结果中,找到那些是action_token 的输出位置,并将其设置为计算loss。
1 |
|
发现有问题。希望的修改方式:严格按照格式,mask掉除了7个action以外的任何位置。
Evaluate
其他部分已经全部调试好,目前只剩下model输出时候的函数predict_action还没有完全实现,主要难点在如何实现kv cache上。
- kv cache: 即只在在没有缓存(即首次调用)时,才进行图像特征的提取和拼接;在有缓存时,则跳过这些操作,因为其所需要的信息都储存在past_key_value中。
- 动作token选择:找到概率最高的动作token,然后输出。