vla-sft-code-motus / data /utils /calc_stat.py
poet70's picture
Upload folder using huggingface_hub
3903652 verified
Raw
History Blame Contribute Delete
13.8 kB
#!/usr/bin/env python3
import os
import json
import numpy as np
import torch
from tqdm import tqdm
from glob import glob
import multiprocessing as mp
from functools import partial
import time
import argparse
def process_single_file(file_path):
"""
Process a single qpos.pt file to extract action statistics
Args:
file_path: Path to qpos.pt file
Returns:
tuple: (file_path, success, min_vals, max_vals, error_msg, outlier_dims)
"""
try:
# Load qpos data
qpos_data = torch.load(file_path, map_location='cpu')
# Convert to numpy array if it's a tensor
if isinstance(qpos_data, torch.Tensor):
action_data = qpos_data.numpy()
else:
action_data = np.array(qpos_data)
# Check for outliers (absolute value > 4)
outlier_dims = []
if np.any(np.abs(action_data) > 3):
outlier_dims = np.where(np.any(np.abs(action_data) > 3, axis=0))[0].tolist()
# Calculate min/max for this file
file_min = np.min(action_data, axis=0)
file_max = np.max(action_data, axis=0)
return (file_path, True, file_min, file_max, None, outlier_dims)
except Exception as e:
return (file_path, False, None, None, str(e), [])
def process_task_files(task_info):
"""
Process all qpos.pt files in a single task directory
Args:
task_info: tuple of (task_name, task_dir, dataset_type)
Returns:
dict: Results for this task
"""
task_name, task_dir, dataset_type = task_info
# Find all qpos.pt files in this task directory
if dataset_type == "robotwin":
# RobotWin structure: task_dir/qpos/*.pt
qpos_dir = os.path.join(task_dir, "qpos")
if os.path.exists(qpos_dir):
qpos_files = glob(os.path.join(qpos_dir, "*.pt"))
else:
qpos_files = []
elif dataset_type in ["ac_one", "aloha_agilex_2"]:
# AC-One / Aloha-Agilex2 structure: nested task directories; recursively search for any 'qpos' folder
# Apply pruning to avoid heavy descent into irrelevant dirs (videos, instructions, backups, hidden)
qpos_files = []
if os.path.exists(task_dir):
for current_root, dirnames, filenames in os.walk(task_dir, topdown=True):
# Prune directories to reduce IO
pruned = []
for d in list(dirnames):
if d == "videos" or d == "instructions" or d == "umt5_wan":
continue
if d.endswith("_bak") or d.startswith("."):
continue
pruned.append(d)
dirnames[:] = pruned
# If this level contains a qpos directory, collect and do not descend into it
if "qpos" in dirnames:
qpos_dir = os.path.join(current_root, "qpos")
found = glob(os.path.join(qpos_dir, "*.pt"))
found_numeric = [f for f in found if os.path.basename(f).replace('.pt', '').isdigit()]
if found_numeric:
qpos_files.extend(found_numeric)
print(f" Found {len(found_numeric)} qpos files in {qpos_dir}")
# Do not walk into qpos directory
dirnames.remove("qpos")
else:
# Aloha structure: task_dir/*_qpos.pt (fallback for other aloha datasets)
qpos_files = glob(os.path.join(task_dir, "*_qpos.pt"))
results = {
'task_name': task_name,
'file_count': 0,
'success_count': 0,
'error_files': [],
'outlier_files': [],
'global_min': None,
'global_max': None
}
print(f"Processing task '{task_name}' with {len(qpos_files)} files...")
print(f" Task directory: {task_dir}")
print(f" Looking for files in subdirectories of {task_dir}")
# Debug: list some directories
if os.path.exists(task_dir):
for item in os.listdir(task_dir)[:3]: # Show first 3 items
item_path = os.path.join(task_dir, item)
if os.path.isdir(item_path):
print(f" Subdir: {item}")
subdir_items = os.listdir(item_path)[:3]
print(f" Contains: {subdir_items}")
# Process each file in this task
for file_path in tqdm(qpos_files, desc=f"Task {task_name}"):
file_path, success, file_min, file_max, error_msg, outlier_dims = process_single_file(file_path)
results['file_count'] += 1
if success:
results['success_count'] += 1
# Update global min/max for this task
if results['global_min'] is None:
results['global_min'] = file_min
results['global_max'] = file_max
else:
results['global_min'] = np.minimum(results['global_min'], file_min)
results['global_max'] = np.maximum(results['global_max'], file_max)
# Record outlier files
if outlier_dims:
results['outlier_files'].append((file_path, outlier_dims))
else:
results['error_files'].append((file_path, error_msg))
return results
def collect_action_stats_multiprocess(root_dir, output_path, outlier_path, num_processes=16, dataset_type="aloha"):
"""
Collect action statistics from qpos.pt files using multiprocessing
Args:
root_dir: Root directory of qpos.pt files
output_path: Output JSON file path
outlier_path: Output text file path for outlier files
num_processes: Number of processes to use
dataset_type: "aloha", "robotwin", or "ac_one"
"""
print(f"Starting multiprocess statistics calculation for {dataset_type} with {num_processes} processes...")
if dataset_type == "robotwin":
# RobotWin has clean/ and randomized/ subdirs - only use clean
all_task_dirs = []
for split in ["clean", "randomized"]: # Only process clean split
split_dir = os.path.join(root_dir, split)
if os.path.exists(split_dir):
task_dirs = [d for d in os.listdir(split_dir)
if os.path.isdir(os.path.join(split_dir, d))]
for task in task_dirs:
all_task_dirs.append((f"{split}_{task}", os.path.join(split_dir, task)))
task_dirs = all_task_dirs
elif dataset_type in ["ac_one", "aloha_agilex_2"]:
# AC-One structure: root_dir/task_category/
task_dirs = [(d, os.path.join(root_dir, d)) for d in os.listdir(root_dir)
if os.path.isdir(os.path.join(root_dir, d))]
else:
# Aloha structure
task_dirs = [(d, os.path.join(root_dir, d)) for d in os.listdir(root_dir)
if os.path.isdir(os.path.join(root_dir, d))]
print(f"Found {len(task_dirs)} task directories")
for task_name, task_dir in task_dirs:
print(f" - {task_name}: {task_dir}")
# Prepare task info for multiprocessing
task_infos = [(task, task_dir, dataset_type) for task, task_dir in task_dirs]
# Record start time
start_time = time.time()
# Process tasks in parallel
with mp.Pool(processes=num_processes) as pool:
task_results = pool.map(process_task_files, task_infos)
# Aggregate results from all tasks
total_file_count = 0
total_success_count = 0
all_error_files = []
all_outlier_files = []
global_min = None
global_max = None
for result in task_results:
total_file_count += result['file_count']
total_success_count += result['success_count']
all_error_files.extend(result['error_files'])
all_outlier_files.extend(result['outlier_files'])
# Update global min/max across all tasks
if result['global_min'] is not None:
if global_min is None:
global_min = result['global_min']
global_max = result['global_max']
else:
global_min = np.minimum(global_min, result['global_min'])
global_max = np.maximum(global_max, result['global_max'])
# Calculate elapsed time
elapsed_time = time.time() - start_time
# Generate statistics dictionary
if dataset_type == "robotwin":
dataset_key = "robotwin2"
elif dataset_type == "ac_one":
dataset_key = "ac_one"
elif dataset_type == "aloha_agilex_2":
dataset_key = "aloha_agilex_2"
else:
dataset_key = "aloha_agilex"
stat_dict = {
dataset_key: {
"min": global_min.tolist() if global_min is not None else [],
"max": global_max.tolist() if global_max is not None else [],
"file_count": total_success_count,
"total_files_scanned": total_file_count,
"action_dim": len(global_min) if global_min is not None else 0,
"processing_time_seconds": elapsed_time,
"num_processes_used": num_processes
}
}
# Load existing statistics if file exists and append new data
if os.path.exists(output_path):
try:
with open(output_path, 'r', encoding='utf-8') as f:
existing_stats = json.load(f)
# Merge with existing stats
existing_stats.update(stat_dict)
stat_dict = existing_stats
except Exception as e:
print(f"Warning: Could not load existing stats from {output_path}: {e}")
# Save statistics results
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(stat_dict, f, indent=4, ensure_ascii=False)
# Save outlier files list
with open(outlier_path, 'w', encoding='utf-8') as f:
f.write(f"Outlier files (absolute value > 4) - Total: {len(all_outlier_files)}\n")
f.write("=" * 80 + "\n")
for file_path, dims in all_outlier_files:
f.write(f"{file_path} - Outlier dimensions: {dims}\n")
# Print summary statistics
print(f"\n{'='*60}")
print(f"MULTIPROCESS STATISTICS CALCULATION COMPLETED")
print(f"{'='*60}")
print(f"Processing time: {elapsed_time:.2f} seconds")
print(f"Processes used: {num_processes}")
print(f"Average time per process: {elapsed_time/num_processes:.2f} seconds")
print(f"Files per second: {total_file_count/elapsed_time:.2f}")
print(f"\nResults:")
print(f"- Total files scanned: {total_file_count}")
print(f"- Successfully processed: {total_success_count}")
print(f"- Failed files: {len(all_error_files)}")
print(f"- Files with outliers: {len(all_outlier_files)}")
print(f"- Action dimensions: {len(global_min) if global_min is not None else 'N/A'}")
print(f"- Min values: {global_min}")
print(f"- Max values: {global_max}")
# Show task-wise breakdown
print(f"\nTask-wise breakdown:")
for result in task_results:
print(f"- {result['task_name']}: {result['success_count']}/{result['file_count']} files")
# Show some error examples
if all_error_files:
print(f"\nError examples (showing first 10 out of {len(all_error_files)}):")
for i, (path, err) in enumerate(all_error_files[:10]):
print(f"- {path}: {err}")
if len(all_error_files) > 10:
print(f"... and {len(all_error_files) - 10} more errors")
def main():
"""Main function to run the multiprocess statistics calculation"""
parser = argparse.ArgumentParser(description='Calculate dataset statistics')
parser.add_argument('--root_dir', type=str,
default="/share/dataset/preprocess/aloha_agilex_2",
help='Root directory of qpos.pt files')
parser.add_argument('--output_path', type=str,
default="/share/home/bhz/test/latent_action_world_model/lawm/data/utils/stat.json",
help='Output JSON file path for statistics')
parser.add_argument('--outlier_path', type=str,
default="/share/home/bhz/test/latent_action_world_model/lawm/data/aloha_agilex_2_outlier_files.txt",
help='Output text file path for outlier files')
parser.add_argument('--num_processes', type=int, default=16,
help='Number of processes to use')
parser.add_argument('--dataset_type', type=str, default="aloha_agilex_2", choices=["aloha", "robotwin", "ac_one", "aloha_agilex_2"],
help='Dataset type: aloha, robotwin, or ac_one')
args = parser.parse_args()
print(f"Configuration:")
print(f"- Root directory: {args.root_dir}")
print(f"- Output file: {args.output_path}")
print(f"- Outlier file: {args.outlier_path}")
print(f"- Number of processes: {args.num_processes}")
print(f"- Dataset type: {args.dataset_type}")
# Ensure output directories exist
os.makedirs(os.path.dirname(args.output_path), exist_ok=True)
os.makedirs(os.path.dirname(args.outlier_path), exist_ok=True)
# Run the calculation
collect_action_stats_multiprocess(
root_dir=args.root_dir,
output_path=args.output_path,
outlier_path=args.outlier_path,
num_processes=args.num_processes,
dataset_type=args.dataset_type
)
print(f"\nResults saved to:")
print(f"- Statistics: {args.output_path}")
print(f"- Outliers: {args.outlier_path}")
if __name__ == "__main__":
# Ensure proper multiprocessing start method
mp.set_start_method('spawn', force=True)
main()