Compare commits

...

10 Commits

Author SHA1 Message Date
Seeker 9d2319134e Fix typo in README regarding data path 2026-01-07 09:21:42 +08:00
Seeker 940c7616de Fix duplicate git clone command in README 2025-12-16 15:31:57 +08:00
seeker ee6bd86b94 Update repository links and instructions in README 2025-12-15 08:40:59 +08:00
coffee 1e8e0db3f5 update readme.md 2025-05-12 10:45:54 +08:00
coffee 2f934cc567 update readme.md 2025-05-12 10:45:21 +08:00
coffee 02e31618b7 del playground 2025-05-12 10:38:29 +08:00
hanluzhi0429@gmail.com 8efef733dc update README.md file 2025-04-13 11:58:41 +08:00
hanluzhi0429@gmail.com e85c1282bd update README.md file 2025-04-13 11:22:52 +08:00
hanluzhi0429@gmail.com 146e0d18c3 update code file 2025-04-13 11:19:40 +08:00
hanluzhi0429@gmail.com 53df4d14aa update README.md file 2025-04-13 10:25:49 +08:00
13 changed files with 140 additions and 1337 deletions
+39 -5
View File
@@ -1,6 +1,17 @@
**Read this in other languages: [English](README.md), [中文](README_zh.md).**
# OpenVLA Lightweight Version # OpenVLA Lightweight Version
OpenVLA的轻量化版本 Lightweight version of OpenVLA, based on the llava-next framework
Features:
1.The main model is only 0.5B, using qwen2-0.5B.
2.Does not use RLDS format datasets, instead fine-tuned using mllm format.
3.Does not occupy inherent tokens of the LLM model, instead using an additional 271 tokens to represent actions.
# How to use
## Installation ## Installation
@@ -11,7 +22,7 @@ Follow the steps below to install the OpenVLA-Qwen lightweight version.
Clone the repository to your local machine: Clone the repository to your local machine:
```bash ```bash
git clone https://github.com/IP127000/openVLA-Qwen.git git clone https://github.com/IP127000/openVLA-Qwen2-0.5B.git
cd openVLA-Qwen cd openVLA-Qwen
``` ```
### 2. Create a new conda environment and activate it: ### 2. Create a new conda environment and activate it:
@@ -24,9 +35,32 @@ conda activate vla-qwen
pip install --upgrade pip pip install --upgrade pip
pip install -e ".[train]" pip install -e ".[train]"
``` ```
### 4. Download the weights from huggingface
```bash
cd openVLA-Qwen2-0.5B
mkdir models
cd models
git clone https://huggingface.co/lmms-lab/llava-onevision-qwen2-0.5b-ov
```
### 5. Change the file path in scripts/train/finetune_ov_vla.sh
```bash
PREV_STAGE_CHECKPOINT="models/llava-onevision-qwen2-0.5b-ov"
--data_path scripts/train/vla.yaml
--image_folder /images \
```
### 6. Change the data path in scripts/train/vla.yaml
```bash
json_path: /root/data/vla_llava.json
```
### 7. start to train
```bash
cd /openVLA-Qwen2-0.5B
./scripts/train/finetune_ov_vla.sh
```
## Acknowledgements ## Acknowledgements
I would like to express our gratitude to the following repositories: This project makes use of the following open-source projects, and I would like to express my gratitude to their creators:
- [OpenVLA](https://github.com/openvla/openvla) - for providing the core structure and functionality that inspired OpenVLA-Qwen. - [OpenVLA](https://github.com/openvla/openvla) - For providing the core structure and functionality that inspired OpenVLA-Qwen.
- [LLaVA_OpenVLA](https://github.com/Darren-greenhand/LLaVA-Next) - for their contribution to data preprocessing techniques. - [LLaVA_OpenVLA](https://github.com/Darren-greenhand/LLaVA-Next) - For contribution to data preprocessing techniques.
+63
View File
@@ -0,0 +1,63 @@
**Read this in other languages: [English](README.md), [中文](README_zh.md).**
# OpenVLA Lightweight Version
基于llava-next框架的OpenVLA轻量版。
特性:
主模型只有0.5B,使用qwen2-0.5B。
不使用RLDS格式的数据集,而是采用mllm格式进行微调。
不占用LLM模型的固有token,而是使用额外的271个token来表示动作。
# 如何使用
## Installation
按照以下步骤安装OpenVLA-Qwen轻量版。
### 1. Clone the repository
Clone the repository to your local machine:
```bash
git clone https://github.com/IP127000/openVLA-Qwen.git
cd openVLA-Qwen
```
### 2. Create a new conda environment and activate it:
```bash
conda create -n vla-qwen python=3.10 -y
conda activate vla-qwen
```
### 3. Install the required dependencies
```bash
pip install --upgrade pip
pip install -e ".[train]"
```
### 4. Download the weights from huggingface
```bash
cd openVLA-Qwen/models
git clone git clone https://huggingface.co/lmms-lab/llava-onevision-qwen2-0.5b-ov
```
### 5. Change the file path in scripts/train/finetune_ov_vla.sh
```bash
PREV_STAGE_CHECKPOINT="models/llava-onevision-qwen2-0.5b-ov"
--data_path scripts/train/vla.yaml
--image_folder /images \
```
### 6. Change the data paht in scripts/train/vla.yaml
```bash
json_path: /root/data/vla_llava.json
```
### 7. 开始训练
```bash
cd /openVLA-Qwen2-0.5B
./scripts/train/finetune_ov_vla.sh
```
## 致谢
本项目使用了以下开源项目,在此向其开发者表示感谢:
- [OpenVLA](https://github.com/openvla/openvla) - 感谢提供核心结构和功能,启发了OpenVLA-Qwen。
- [LLaVA_OpenVLA](https://github.com/Darren-greenhand/LLaVA-Next) - 感谢他对数据处理技术的贡献。
+31 -66
View File
@@ -9,71 +9,9 @@ from typing import List, Union
import numpy as np import numpy as np
from transformers import PreTrainedTokenizerBase from transformers import PreTrainedTokenizerBase
# class ActionTokenizer:
# def __init__(
# self, tokenizer: PreTrainedTokenizerBase, bins: int = 256, min_action: int = -1, max_action: int = 1
# ) -> None:
# """
# Discretizes continuous robot actions into N bins per dimension and maps to the least used tokens.
# NOTE =>> by default, assumes a BPE-style tokenizer akin to the LlamaTokenizer, where *the least used tokens*
# appear at the end of the vocabulary!
# :param tokenizer: Base LLM/VLM tokenizer to extend.
# :param bins: Number of bins for each continuous value; we'll adopt a uniform binning strategy.
# :param min_action: Minimum action value (for clipping, setting lower bound on bin interval).
# :param max_action: Maximum action value (for clipping, setting upper bound on bin interval).
# """
# self.tokenizer, self.n_bins, self.min_action, self.max_action = tokenizer, bins, min_action, max_action
# # Create Uniform Bins + Compute Bin Centers
# self.bins = np.linspace(min_action, max_action, self.n_bins)
# self.bin_centers = (self.bins[:-1] + self.bins[1:]) / 2.0
# # [Contract] Set "action_token_begin_idx" based on `self.tokenizer.vocab_size - (self.n_bins + 1)`
# # =>> Assumes we're always overwriting the final `n_bins` tokens of the vocabulary!
# self.action_token_begin_idx: int = int(self.tokenizer.vocab_size - (self.n_bins + 1))
# def __call__(self, action: np.ndarray) -> Union[str, List[str]]:
# """Clip & bin actions to *the last `n_bins` tokens* of the vocabulary (e.g., tokenizer.vocab[-256:])."""
# action = np.clip(action, a_min=float(self.min_action), a_max=float(self.max_action))
# discretized_action = np.digitize(action, self.bins)
# # Handle single element vs. batch
# if len(discretized_action.shape) == 1:
# return self.tokenizer.decode(list(self.tokenizer.vocab_size - discretized_action))
# else:
# return self.tokenizer.batch_decode((self.tokenizer.vocab_size - discretized_action).tolist())
# def decode_token_ids_to_actions(self, action_token_ids: np.ndarray) -> np.ndarray:
# """
# Returns continuous actions for discrete action token IDs.
# NOTE =>> Because of the way the actions are discretized w.r.t. the bins (and not the bin centers), the
# digitization returns bin indices between [1, # bins], inclusive, when there are actually only
# (# bins - 1) bin intervals.
# Therefore, if the digitization returns the last possible index, we map this to the last bin interval.
# EXAMPLE =>> Let's say self._bins has 256 values. Then self._bin_centers has 255 values. Digitization returns
# indices between [1, 256]. We subtract 1 from all indices so that they are between [0, 255]. There
# is still one index (i==255) that would cause an out-of-bounds error if used to index into
# self._bin_centers. Therefore, if i==255, we subtract 1 from it so that it just becomes the index of
# the last bin center. We implement this simply via clipping between [0, 255 - 1].
# """
# discretized_actions = self.tokenizer.vocab_size - action_token_ids
# discretized_actions = np.clip(discretized_actions - 1, a_min=0, a_max=self.bin_centers.shape[0] - 1)
# return self.bin_centers[discretized_actions]
# @property
# def vocab_size(self) -> int:
# return self.n_bins
class ActionTokenizer: class ActionTokenizer:
def __init__( def __init__(
self, tokenizer: PreTrainedTokenizerBase, bins: int = 285, min_action = -0.0025, max_action = 0.0025 self, tokenizer: PreTrainedTokenizerBase, bins: int = 271, min_action = -1.0, max_action = 1.0
# self, tokenizer: PreTrainedTokenizerBase, bins: int = 256, min_action: int = -1, max_action: int = 1 # self, tokenizer: PreTrainedTokenizerBase, bins: int = 256, min_action: int = -1, max_action: int = 1
) -> None: ) -> None:
self.tokenizer, self.n_bins, self.min_action, self.max_action = tokenizer, bins, min_action, max_action self.tokenizer, self.n_bins, self.min_action, self.max_action = tokenizer, bins, min_action, max_action
@@ -93,8 +31,6 @@ class ActionTokenizer:
# print('initializing action tokenizer, the new len(tokenizer) is:', len(self.tokenizer)) # print('initializing action tokenizer, the new len(tokenizer) is:', len(self.tokenizer))
def __call__(self, action: np.ndarray) -> Union[str, List[str]]: def __call__(self, action: np.ndarray) -> Union[str, List[str]]:
action = np.clip(action, a_min=float(self.min_action), a_max=float(self.max_action)) action = np.clip(action, a_min=float(self.min_action), a_max=float(self.max_action))
discretized_action = np.digitize(action, self.bins) - 1 discretized_action = np.digitize(action, self.bins) - 1
discretized_action = np.clip(discretized_action, a_min=0, a_max=self.n_bins - 1) discretized_action = np.clip(discretized_action, a_min=0, a_max=self.n_bins - 1)
@@ -114,7 +50,36 @@ class ActionTokenizer:
action_indices = vectorized_lookup(action_token_ids) action_indices = vectorized_lookup(action_token_ids)
return self.bin_centers[action_indices] return self.bin_centers[action_indices]
# 返回所有 tokens 的数量,包括动作 tokens def actionValue_to_actionName(self,action):
action_token_ids=self.__call__(action)
actionName=self.tokenizer.decode(action_token_ids)
return actionName
def actionName_to_actionValue(self,actionName):
action_ids=self.tokenizer(actionName)
action_ids=action_ids["input_ids"]
actionValue=self.decode_token_ids_to_actions(action_ids)
return actionValue
def str_to_mllm(self, result_str):
new_result=result_str
for token in self.action_tokens:
value=self.actionName_to_actionValue(token)
new_result=new_result.replace(token, str(value[0]))
return new_result
def value_to_mllm(self, result_str):
new_result=result_str
data=[]
for token in self.action_tokens:
if new_result.startswith(token):
value=self.actionName_to_actionValue(token)
data.append(float(value))
elif token in new_result:
value=self.actionName_to_actionValue(token)
data.append(float(value))
return data
@property @property
def vocab_size(self) -> int: def vocab_size(self) -> int:
return len(self.tokenizer) return len(self.tokenizer)
-132
View File
@@ -1,132 +0,0 @@
import json
import os
from PIL import Image
from tqdm import tqdm
import matplotlib.pyplot as plt
import numpy as np
from multiprocessing import Pool
import functools
import argparse
def load_data(json_path):
with open(json_path, "r") as f:
return json.load(f)
def filter_data(data):
filtered_data = [item for item in data if "image" in item]
return filtered_data
def calculate_image_dimension(image_path, images_folder):
full_path = os.path.join(images_folder, image_path)
try:
with Image.open(full_path) as img:
width, height = img.size
return width, height
except Exception as e:
print(f"Error opening {full_path}: {e}")
return None, None
def calculate_image_dimensions_multiprocess(filtered_data, images_folder, num_processes=256):
image_paths = []
for item in filtered_data:
if isinstance(item["image"], list):
image_paths.extend(item["image"])
else:
image_paths.append(item["image"])
with Pool(num_processes) as p:
dimensions = list(
tqdm(
p.imap(functools.partial(calculate_image_dimension, images_folder=images_folder), image_paths),
total=len(image_paths),
desc="Calculating image dimensions",
)
)
widths, heights = zip(*[dim for dim in dimensions if dim[0] is not None])
return list(widths), list(heights)
def tokenize(text):
return text.split()
def calculate_tokenized_lengths(data):
lengths = []
for item in tqdm(data, desc="Tokenizing conversations"):
for conversation in item["conversations"]:
tokenized_value = tokenize(conversation["value"])
lengths.append(len(tokenized_value))
return lengths
def main():
parser = argparse.ArgumentParser(description="Process data for LLaVA_Next project.")
parser.add_argument(
"--json_path",
type=str,
help="Path to the JSON file containing data.",
default="/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_ofa_DEMON-FULL.json",
)
parser.add_argument(
"--images_folder",
type=str,
default="/mnt/bn/vl-research/data/llava_data",
help="Path to the folder containing images.",
)
args = parser.parse_args()
llava_instruct_name = os.path.basename(args.json_path).replace(".json", "")
images_folder = args.images_folder
data = load_data(args.json_path)
filtered_data = filter_data(data)
print(f"Total data items: {len(data)}, Filtered data items: {len(filtered_data)}")
widths, heights = calculate_image_dimensions_multiprocess(filtered_data, images_folder)
max_width, max_height = max(widths), max(heights)
print(f"Max width: {max_width}, Max height: {max_height}")
tokenized_lengths = calculate_tokenized_lengths(filtered_data)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 12))
# Plot 2D histogram
widths_bins = [min(widths), max(widths) + 1] if min(widths) == max(widths) else np.arange(min(widths), max(widths) + 100, 100)
heights_bins = [min(heights), max(heights) + 1] if min(heights) == max(heights) else np.arange(min(heights), max(heights) + 100, 100)
h, xedges, yedges, image = ax1.hist2d(widths, heights, bins=[widths_bins, heights_bins], cmap=plt.cm.jet, density=True)
fig.colorbar(image, ax=ax1)
ax1.set_xlabel("Width")
ax1.set_ylabel("Height")
ax1.set_title(
f"dist_{llava_instruct_name}_2d_w_h\nMax width: {max(widths)}, Max height: {max(heights)}",
fontsize=10,
)
# Plot histogram
hist, bin_edges = np.histogram(tokenized_lengths, bins=np.arange(0, max(tokenized_lengths) + 10, 10))
bins = np.arange(0, max(tokenized_lengths) + 10, 10)
ax2.bar(bin_edges[:-1], hist, width=7, edgecolor="black", log=True)
# Display every nth label on the x-axis
n = 8 # Adjust this value to control the number of labels displayed
ticks = bins[::n]
tick_labels = [int(tick) for tick in ticks]
ax2.set_xticks(ticks)
ax2.set_xticklabels(tick_labels, rotation=90, fontsize=8)
ax2.set_xlim(min(bin_edges), max(bin_edges))
ax2.set_xlabel("Tokenized Length")
ax2.set_ylabel("Count (log scale)")
ax2.set_title(f"dist_{llava_instruct_name}_tokenized_length", fontsize=8)
plt.tight_layout()
plt.savefig(f"./dist_{llava_instruct_name}_combined.png")
if __name__ == "__main__":
main()
-364
View File
@@ -1,364 +0,0 @@
import json
import os
from tqdm import tqdm
from multiprocessing import Pool, cpu_count
import yaml
class DataProcessor:
def __init__(self, file_path, image_root, video_root):
self.file_path = file_path
self.image_root = image_root
self.data = None
self.video_root = video_root
self.load_data()
def load_data(self):
if self.file_path.endswith(".json"):
with open(self.file_path, "r") as f:
self.data = json.load(f)
elif self.file_path.endswith(".yaml"):
with open(self.file_path, "r") as f:
self.data = yaml.safe_load(f)
elif self.file_path.endswith(".jsonl"):
with open(self.file_path, "r") as f:
self.data = [json.loads(line) for line in f.readlines()]
else:
raise ValueError("Unsupported file format")
def load_json_data(self, json_path):
if json_path.endswith(".jsonl"):
cur_data_dict = []
with open(json_path, "r") as json_file:
for line in json_file:
cur_data_dict.append(json.loads(line.strip()))
return cur_data_dict
elif json_path.endswith(".json"):
with open(json_path, "r") as f:
return json.load(f)
else:
raise ValueError("Unsupported file format")
def check_image_existence(self, data):
if "image" in data:
if type(data["image"]) == list:
images = data["image"]
else:
images = [data["image"]]
for image in images:
full_image_path = os.path.join(self.image_root, image)
if not os.path.exists(full_image_path):
print(f"WARNING!!! {full_image_path} not exists !!!")
if "video" in data:
full_video_path = os.path.join(self.video_root, data["video"])
if not os.path.exists(full_video_path):
print(f"WARNING!!! {full_video_path} not exists !!!")
# if data["conversations"][0]["value"].count("<image>") > 1:
# print(f"WARNING!!! {data['conversations'][0]['value']} has more than one <image> !!!")
def check_item_structure(self, item):
if not all(key in item for key in ["conversations"]):
print(f"WARNING!!! Item {item.get('id', 'unknown')} is missing required fields!")
return False
conversations = item["conversations"]
if not isinstance(conversations, list) or len(conversations) < 2 or len(conversations) % 2 != 0:
print(f"WARNING!!! Item {item['id']} has invalid conversations structure!")
return False
for i, conv in enumerate(conversations):
if not all(key in conv for key in ["from", "value"]):
print(f"WARNING!!! Item {item['id']} has invalid conversation format!")
return False
expected_from = "human" if i % 2 == 0 else "gpt"
if conv["from"] != expected_from:
print(f"WARNING!!! Item {item['id']} has incorrect conversation order!")
return False
return True
def check_image_and_structure(self, item):
if not self.check_item_structure(item):
return
# self.check_image_existence(item)
def process_images(self):
if isinstance(self.data, list):
args = [d for d in self.data]
with Pool(processes=cpu_count()) as pool:
list(tqdm(pool.imap(self.check_image_and_structure, args), total=len(self.data)))
elif isinstance(self.data, dict):
for d in self.data["datasets"]:
dd_json_path = d["json_path"]
data = self.load_json_data(dd_json_path)
args = [d for d in data]
with Pool(processes=cpu_count()) as pool:
list(tqdm(pool.imap(self.check_image_and_structure, args), total=len(data), desc=f"Processing {dd_json_path}"))
def count_items(self):
if isinstance(self.data, list): # Assuming JSON data loaded directly
return len(self.data)
elif isinstance(self.data, dict): # Assuming YAML data loaded
total_items_count = 0
for d in self.data["datasets"]:
dd_json_path = d["json_path"]
data = self.load_json_data(dd_json_path)
current_items_count = len(data)
sampling_strategy = d["sampling_strategy"]
try:
if sampling_strategy != "all":
percentage = float(sampling_strategy.split(":")[-1].replace("%", "")) / 100.0
else:
percentage = 1.0
except Exception as e:
print(f"Error: {e}")
percentage = 1.0
sampling_count = int(current_items_count * percentage)
total_items_count += sampling_count
print(f"{dd_json_path}: {sampling_count}")
return total_items_count
def stat_data(self):
if isinstance(self.data, dict):
cur_lens_list = []
single_image_count = 0
multiple_image_count = 0
video_count = 0
total_count = 0
text_count = 0
max_tokens_item = None
max_tokens = 0
for d in self.data["datasets"]:
dd_json_path = d["json_path"]
data = self.load_json_data(dd_json_path)
sampling_strategy = d["sampling_strategy"]
try:
if sampling_strategy != "all":
percentage = float(sampling_strategy.split(":")[-1].replace("%", "")) / 100.0
else:
percentage = 1.0
except Exception as e:
print(f"Error parsing sampling strategy: {e}")
percentage = 1.0
sampled_count = int(len(data) * percentage)
print(f"{dd_json_path}: {sampled_count} (sampled from {len(data)})")
for item in data[:sampled_count]:
conversations = item["conversations"]
cur_len = sum([len(conv["value"].split()) for conv in conversations])
cur_lens_list.append(cur_len)
if cur_len > max_tokens:
max_tokens = cur_len
max_tokens_item = item
total_count += 1
if "image" in item:
if isinstance(item["image"], list):
if len(item["image"]) > 1:
multiple_image_count += 1
else:
single_image_count += 1
else:
single_image_count += 1
elif "video" in item:
video_count += 1
else:
text_count += 1
print(f"Max length: {max(cur_lens_list)}, Min length: {min(cur_lens_list)}, Average length: {sum(cur_lens_list) / len(cur_lens_list)}")
print(f"Total items: {total_count}")
print(f"Text items: {text_count} ({text_count/total_count*100:.2f}%)")
print(f"Single image items: {single_image_count} ({single_image_count/total_count*100:.2f}%)")
print(f"Multiple image items: {multiple_image_count} ({multiple_image_count/total_count*100:.2f}%)")
print(f"Video items: {video_count} ({video_count/total_count*100:.2f}%)")
print("\nItem with the largest number of tokens:")
print(f"Token count: {max_tokens}")
print("Item content:")
print(json.dumps(max_tokens_item, indent=2))
def filter_data(self):
if isinstance(self.data, dict):
for d in self.data["datasets"]:
dd_json_path = d["json_path"]
print(f"Processing {dd_json_path}")
data = self.load_json_data(dd_json_path)
filtered_data = []
mismatch_data = []
mismatch_flag = False
for item in data:
try:
if "image" in item:
num_image = len(item["image"]) if isinstance(item["image"], list) else 1
else:
num_image = 0
if "video" in item:
num_video = len(item["video"]) if isinstance(item["video"], list) else 1
else:
num_video = 0
num_visuals = num_image + num_video
conv_text = ""
for conv in item["conversations"]:
conv_text += conv["value"]
num_img_token_appearance = conv_text.count("<image>")
if len(conv_text) == 0:
print(f"Conversation text is empty for {item}")
if num_img_token_appearance == num_visuals or num_img_token_appearance < num_visuals and len(conv_text) > 0:
filtered_data.append(item)
elif num_img_token_appearance > num_visuals:
item["num_img_token_appearance"] = num_img_token_appearance
item["num_visuals"] = num_visuals
mismatch_data.append(item)
if not mismatch_flag:
print(f"Data mismatch for {item}")
mismatch_flag = True
except Exception as e:
print(f"Error: {e}")
print()
if mismatch_flag:
print(f"Data mismatch for {dd_json_path}")
if len(filtered_data) < len(data):
saving_dd_json_path = dd_json_path.replace(".jsonl", f"fltd_{len(filtered_data)}.json").replace(".json", f"fltd_{len(filtered_data)}.json")
with open(saving_dd_json_path, "w") as f:
json.dump(filtered_data, f, indent=2)
print(f"Filtered data count: {len(filtered_data)}")
else:
pass
def stat_and_filter_data(self, threshold):
if isinstance(self.data, dict):
cur_lens_list = []
single_image_count = 0
multiple_image_count = 0
video_count = 0
total_count = 0
text_count = 0
for d in self.data["datasets"]:
dd_json_path = d["json_path"]
data = self.load_json_data(dd_json_path)
sampling_strategy = d["sampling_strategy"]
filtered_data = []
try:
if sampling_strategy != "all":
percentage = float(sampling_strategy.split(":")[-1].replace("%", "")) / 100.0
else:
percentage = 1.0
except Exception as e:
print(f"Error parsing sampling strategy: {e}")
percentage = 1.0
sampled_count = int(len(data) * percentage)
print(f"{dd_json_path}: {sampled_count} (sampled from {len(data)})")
save_flag = False
for item in data:
total_count += 1
conversations = item["conversations"]
filtered_conversations = []
current_token_count = 0
for i in range(0, len(conversations), 2):
if i + 1 < len(conversations):
human_conv = conversations[i]
gpt_conv = conversations[i + 1]
pair_tokens = len(human_conv["value"].split()) + len(gpt_conv["value"].split())
if current_token_count + pair_tokens <= threshold:
filtered_conversations.extend([human_conv, gpt_conv])
current_token_count += pair_tokens
else:
save_flag = True
break
if filtered_conversations:
item["conversations"] = filtered_conversations
cur_len = sum([len(conv["value"].split()) for conv in filtered_conversations])
cur_lens_list.append(cur_len)
filtered_data.append(item)
if "image" in item:
if isinstance(item["image"], list):
if len(item["image"]) > 1:
multiple_image_count += 1
else:
single_image_count += 1
else:
single_image_count += 1
elif "video" in item:
video_count += 1
else:
text_count += 1
# Save filtered data for each dataset
if filtered_data and save_flag:
if dd_json_path.endswith(".jsonl"):
output_file = dd_json_path.replace(".jsonl", f"_filtered_{threshold}tokens_{len(filtered_data)}.jsonl")
with open(output_file, "w") as f:
for item in filtered_data:
f.write(json.dumps(item) + "\n")
else:
output_file = dd_json_path.replace(".json", f"_filtered_{threshold}tokens_{len(filtered_data)}.json")
with open(output_file, "w") as f:
json.dump(filtered_data, f, indent=2)
print(f"Filtered data for {dd_json_path} saved to: {output_file}")
print(f"Max length: {max(cur_lens_list)}, Min length: {min(cur_lens_list)}, Average length: {sum(cur_lens_list) / len(cur_lens_list)}")
print(f"Total items: {total_count}")
print(f"Text items: {text_count} ({text_count/total_count*100:.2f}%)")
print(f"Single image items: {single_image_count} ({single_image_count/total_count*100:.2f}%)")
print(f"Multiple image items: {multiple_image_count} ({multiple_image_count/total_count*100:.2f}%)")
print(f"Video items: {video_count} ({video_count/total_count*100:.2f}%)")
def main(file_path, image_root, operation, video_root, threshold=None):
processor = DataProcessor(file_path, image_root, video_root)
if operation == "check":
processor.process_images()
elif operation == "count":
total_items = processor.count_items()
print(f"Total items: {total_items}")
elif operation == "filter":
processor.filter_data()
elif operation == "stat":
processor.stat_data()
elif operation == "stat_and_filter":
if threshold is None:
raise ValueError("Threshold must be provided for stat_and_filter operation")
processor.stat_and_filter_data(threshold)
else:
raise ValueError("Unsupported operation")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--file_path", type=str, default="/mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next/scripts/i18n/scale_llms/next_continual.yaml")
parser.add_argument("--image_root", type=str, default="/mnt/bn/vl-research/data/llava_data")
parser.add_argument("--video_root", type=str, default="/mnt/bn/vl-research/data/llava_video")
parser.add_argument("--operation", type=str, default="filter")
parser.add_argument("--threshold", type=int, default=None, help="Threshold for stat_and_filter operation")
args = parser.parse_args()
main(args.file_path, args.image_root, args.operation, args.video_root, args.threshold)
-335
View File
@@ -1,335 +0,0 @@
import argparse
import torch
from llava.constants import IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN
from llava.conversation import conv_templates, SeparatorStyle
from llava.model.builder import load_pretrained_model
from llava.utils import disable_torch_init
from llava.mm_utils import process_anyres_image,tokenizer_image_token, get_model_name_from_path, KeywordsStoppingCriteria
import json
import os
import math
from tqdm import tqdm
from decord import VideoReader, cpu
from transformers import AutoConfig
import cv2
import base64
import openai
from PIL import Image
import numpy as np
def split_list(lst, n):
"""Split a list into n (roughly) equal-sized chunks"""
chunk_size = math.ceil(len(lst) / n) # integer division
return [lst[i : i + chunk_size] for i in range(0, len(lst), chunk_size)]
def get_chunk(lst, n, k):
chunks = split_list(lst, n)
return chunks[k]
def parse_args():
"""
Parse command-line arguments.
"""
parser = argparse.ArgumentParser()
# Define the command-line arguments
parser.add_argument("--video_path", help="Path to the video files.", required=True)
parser.add_argument("--output_dir", help="Directory to save the model results JSON.", required=True)
parser.add_argument("--output_name", help="Name of the file for storing results JSON.", required=True)
parser.add_argument("--model-path", type=str, default="facebook/opt-350m")
parser.add_argument("--model-base", type=str, default=None)
parser.add_argument("--conv-mode", type=str, default=None)
parser.add_argument("--chunk-idx", type=int, default=0)
parser.add_argument("--mm_resampler_type", type=str, default="spatial_pool")
parser.add_argument("--mm_spatial_pool_stride", type=int, default=4)
parser.add_argument("--mm_spatial_pool_out_channels", type=int, default=1024)
parser.add_argument("--mm_spatial_pool_mode", type=str, default="average")
parser.add_argument("--image_aspect_ratio", type=str, default="anyres")
parser.add_argument("--image_grid_pinpoints", type=str, default="[(224, 448), (224, 672), (224, 896), (448, 448), (448, 224), (672, 224), (896, 224)]")
parser.add_argument("--mm_patch_merge_type", type=str, default="spatial_unpad")
parser.add_argument("--overwrite", type=lambda x: (str(x).lower() == 'true'), default=True)
parser.add_argument("--for_get_frames_num", type=int, default=4)
parser.add_argument("--load_8bit", type=lambda x: (str(x).lower() == 'true'), default=False)
parser.add_argument("--prompt", type=str, default=None)
parser.add_argument("--api_key", type=str, help="OpenAI API key")
parser.add_argument("--mm_newline_position", type=str, default="no_token")
parser.add_argument("--force_sample", type=lambda x: (str(x).lower() == 'true'), default=False)
parser.add_argument("--add_time_instruction", type=str, default=False)
return parser.parse_args()
def load_video(video_path,args):
if args.for_get_frames_num == 0:
return np.zeros((1, 336, 336, 3))
vr = VideoReader(video_path, ctx=cpu(0),num_threads=1)
total_frame_num = len(vr)
video_time = total_frame_num / vr.get_avg_fps()
fps = round(vr.get_avg_fps())
frame_idx = [i for i in range(0, len(vr), fps)]
frame_time = [i/fps for i in frame_idx]
if len(frame_idx) > args.for_get_frames_num or args.force_sample:
sample_fps = args.for_get_frames_num
uniform_sampled_frames = np.linspace(0, total_frame_num - 1, sample_fps, dtype=int)
frame_idx = uniform_sampled_frames.tolist()
frame_time = [i/vr.get_avg_fps() for i in frame_idx]
frame_time = ",".join([f"{i:.2f}s" for i in frame_time])
spare_frames = vr.get_batch(frame_idx).asnumpy()
# import pdb;pdb.set_trace()
return spare_frames,frame_time,video_time
def load_video_base64(path):
video = cv2.VideoCapture(path)
base64Frames = []
while video.isOpened():
success, frame = video.read()
if not success:
break
_, buffer = cv2.imencode(".jpg", frame)
base64Frames.append(base64.b64encode(buffer).decode("utf-8"))
video.release()
# print(len(base64Frames), "frames read.")
return base64Frames
def run_inference(args):
"""
Run inference on ActivityNet QA DataSet using the Video-ChatGPT model.
Args:
args: Command-line arguments.
"""
# Initialize the model
if "gpt4v" != args.model_path:
model_name = get_model_name_from_path(args.model_path)
# Set model configuration parameters if they exist
if args.overwrite == True:
overwrite_config = {}
overwrite_config["mm_spatial_pool_mode"] = args.mm_spatial_pool_mode
overwrite_config["mm_spatial_pool_stride"] = args.mm_spatial_pool_stride
overwrite_config["mm_newline_position"] = args.mm_newline_position
cfg_pretrained = AutoConfig.from_pretrained(args.model_path)
# import pdb;pdb.set_trace()
if "qwen" not in args.model_path.lower():
if "224" in cfg_pretrained.mm_vision_tower:
# suppose the length of text tokens is around 1000, from bo's report
least_token_number = args.for_get_frames_num*(16//args.mm_spatial_pool_stride)**2 + 1000
else:
least_token_number = args.for_get_frames_num*(24//args.mm_spatial_pool_stride)**2 + 1000
scaling_factor = math.ceil(least_token_number/4096)
if scaling_factor >= 2:
if "vicuna" in cfg_pretrained._name_or_path.lower():
print(float(scaling_factor))
overwrite_config["rope_scaling"] = {"factor": float(scaling_factor), "type": "linear"}
overwrite_config["max_sequence_length"] = 4096 * scaling_factor
overwrite_config["tokenizer_model_max_length"] = 4096 * scaling_factor
tokenizer, model, image_processor, context_len = load_pretrained_model(args.model_path, args.model_base, model_name, load_8bit=args.load_8bit, overwrite_config=overwrite_config)
else:
tokenizer, model, image_processor, context_len = load_pretrained_model(args.model_path, args.model_base, model_name)
else:
pass
# import pdb;pdb.set_trace()
if getattr(model.config, "force_sample", None) is not None:
args.force_sample = model.config.force_sample
else:
args.force_sample = False
# import pdb;pdb.set_trace()
if getattr(model.config, "add_time_instruction", None) is not None:
args.add_time_instruction = model.config.add_time_instruction
else:
args.add_time_instruction = False
# Create the output directory if it doesn't exist
if not os.path.exists(args.output_dir):
os.makedirs(args.output_dir)
output_name = args.output_name
answers_file = os.path.join(args.output_dir, f"{output_name}.json")
ans_file = open(answers_file, "w")
video_path = args.video_path
all_video_pathes = []
# Check if the video_path is a directory or a file
if os.path.isdir(video_path):
# If it's a directory, loop over all files in the directory
for filename in os.listdir(video_path):
# Load the video file
cur_video_path = os.path.join(video_path, f"{filename}")
all_video_pathes.append(os.path.join(video_path, cur_video_path))
else:
# If it's a file, just process the video
all_video_pathes.append(video_path)
# import pdb;pdb.set_trace()
for video_path in all_video_pathes:
sample_set = {}
question = args.prompt
sample_set["Q"] = question
sample_set["video_name"] = video_path
# Check if the video exists
if os.path.exists(video_path):
if "gpt4v" != args.model_path:
video,frame_time,video_time = load_video(video_path, args)
video = image_processor.preprocess(video, return_tensors="pt")["pixel_values"].half().cuda()
video = [video]
else:
spare_frames,frame_time,video_time = load_video_base64(video_path)
interval = int(len(video) / args.for_get_frames_num)
# try:
# Run inference on the video and add the output to the list
if "gpt4v" != args.model_path:
qs = question
if args.add_time_instruction:
time_instruciton = f"The video lasts for {video_time:.2f} seconds, and {len(video[0])} frames are uniformly sampled from it. These frames are located at {frame_time}.Please answer the following questions related to this video."
qs = f'{time_instruciton}\n{qs}'
if model.config.mm_use_im_start_end:
qs = DEFAULT_IM_START_TOKEN + DEFAULT_IMAGE_TOKEN + DEFAULT_IM_END_TOKEN + "\n" + qs
else:
qs = DEFAULT_IMAGE_TOKEN + "\n" + qs
conv = conv_templates[args.conv_mode].copy()
conv.append_message(conv.roles[0], qs)
conv.append_message(conv.roles[1], None)
prompt = conv.get_prompt()
input_ids = tokenizer_image_token(prompt, tokenizer, IMAGE_TOKEN_INDEX, return_tensors="pt").unsqueeze(0).cuda()
if tokenizer.pad_token_id is None:
if "qwen" in tokenizer.name_or_path.lower():
print("Setting pad token to bos token for qwen model.")
tokenizer.pad_token_id = 151643
attention_masks = input_ids.ne(tokenizer.pad_token_id).long().cuda()
stop_str = conv.sep if conv.sep_style != SeparatorStyle.TWO else conv.sep2
keywords = [stop_str]
stopping_criteria = KeywordsStoppingCriteria(keywords, tokenizer, input_ids)
cur_prompt = question
else:
prompt = question
system_error = ""
if "gpt4v" != args.model_path:
with torch.inference_mode():
# model.update_prompt([[cur_prompt]])
# import pdb;pdb.set_trace()
# output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=True, temperature=0.2, max_new_tokens=1024, use_cache=True, stopping_criteria=[stopping_criteria])
if "mistral" not in cfg_pretrained._name_or_path.lower():
output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=False, temperature=0.0, max_new_tokens=1024, top_p=0.1,num_beams=1,use_cache=True, stopping_criteria=[stopping_criteria])
# output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=True, temperature=0.2, max_new_tokens=1024, use_cache=True, stopping_criteria=[stopping_criteria])
else:
output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=False, temperature=0.0, max_new_tokens=1024, top_p=0.1, num_beams=1, use_cache=True)
# output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=True, temperature=0.2, max_new_tokens=1024, use_cache=True)
else:
openai.api_key = args.api_key # Your API key here
max_num_retries = 0
retry = 5
PROMPT_MESSAGES = [
{
"role": "user",
"content": [
f"These are frames from a video that I want to upload. Answer me one question of this video: {prompt}",
*map(lambda x: {"image": x, "resize": 336}, video[0::interval]),
],
},
]
params = {
"model": "gpt-4-vision-preview", #gpt-4-1106-vision-preview
"messages": PROMPT_MESSAGES,
"max_tokens": 1024,
}
sucess_flag=False
while max_num_retries < retry:
try:
result = openai.ChatCompletion.create(**params)
outputs = result.choices[0].message.content
sucess_flag = True
break
except Exception as inst :
if 'error' in dir(inst):
# import pdb;pdb.set_trace()
if inst.error.code == 'rate_limit_exceeded':
if "TPM" in inst.error.message:
time.sleep(30)
continue
else:
import pdb;pdb.set_trace()
elif inst.error.code == 'insufficient_quota':
print(f'insufficient_quota key')
exit()
elif inst.error.code == 'content_policy_violation':
print(f'content_policy_violation')
system_error = "content_policy_violation"
break
print('Find error message in response: ',str(inst.error.message), 'error code: ', str(inst.error.code))
continue
if not sucess_flag:
print(f'Calling OpenAI failed after retrying for {max_num_retries} times. Check the logs for details.')
exit()
if "gpt4v" != args.model_path:
outputs = tokenizer.batch_decode(output_ids, skip_special_tokens=True)[0].strip()
else:
print(len(video[0::interval]))
print(f"Question: {prompt}\n")
print(f"Response: {outputs}\n")
if "gpt4v" == args.model_path:
if system_error == 'content_policy_violation':
continue
elif system_error == "":
continue
else:
import pdb;pdb.set_trace()
# import pdb;pdb.set_trace()
if "mistral" not in cfg_pretrained._name_or_path.lower():
if outputs.endswith(stop_str):
outputs = outputs[: -len(stop_str)]
outputs = outputs.strip()
sample_set["pred"] = outputs
ans_file.write(json.dumps(sample_set, ensure_ascii=False) + "\n")
ans_file.flush()
ans_file.close()
if __name__ == "__main__":
args = parse_args()
run_inference(args)
Binary file not shown.
-38
View File
@@ -1,38 +0,0 @@
import json
from math import ceil
def split_json_file(input_file, n_splits):
# Read the JSON file
with open(input_file, "r") as file:
data = json.load(file)
# Calculate the size of each split
total_items = len(data)
items_per_split = ceil(total_items / n_splits)
# Split the data and save into separate files
for i in range(n_splits):
start_index = i * items_per_split
end_index = min((i + 1) * items_per_split, total_items)
split_data = data[start_index:end_index]
# Write the split data to a new JSON file
with open(f"{input_file.split('.')[0]}_split_{i}.json", "w") as split_file:
json.dump(split_data, split_file, indent=4)
def main():
import argparse
parser = argparse.ArgumentParser(description="Split a JSON file into multiple parts.")
parser.add_argument("--input_file", type=str, help="The JSON file to split")
parser.add_argument("--n_splits", type=int, help="The number of splits")
args = parser.parse_args()
split_json_file(args.input_file, args.n_splits)
if __name__ == "__main__":
main()
-35
View File
@@ -1,35 +0,0 @@
import os
import shutil
import glob
def remove_checkpoints(directory, pattern):
# Walk through the directory
for root, dirs, files in os.walk(directory):
# Use glob to find paths matching the pattern
for file_path in glob.glob(os.path.join(root, pattern)):
# Check if it is a directory
if "llava-1.6-mistral-7b" in file_path:
continue
if os.path.isdir(file_path):
# Remove the directory
print(f"Removing {file_path}")
input("Press Enter to continue...")
shutil.rmtree(file_path)
print(f"Removed directory: {file_path}")
else:
print(f"Removing {file_path}")
input("Press Enter to continue...")
# Remove the file
os.remove(file_path)
print(f"Removed file: {file_path}")
# Directory containing the checkpoints
directory = "/mnt/bn/vl-research/checkpoints/feng/"
# Pattern to match in the file names
pattern = "global_step*"
# Call the function
remove_checkpoints(directory, pattern)
-125
View File
@@ -1,125 +0,0 @@
import argparse
import json
import time
import os
import tqdm
import sglang as sgl
from sglang.test.test_utils import select_sglang_backend
from sglang.utils import dump_state_text
@sgl.function
def image_description(s, image_file):
prompt = "Please generate detailed descriptions of the given image."
s += sgl.user(sgl.image(image_file) + prompt)
s += sgl.assistant(sgl.gen("answer", max_tokens=1024, temperature=0.0))
def load_progress(progress_file):
print(f"Load progress from {progress_file}")
if os.path.exists(progress_file):
with open(progress_file, "r") as f:
return json.load(f)
return {"last_index": -1, "last_chunk": -1, "results": [], "annotations": []}
def save_progress(progress_file, progress_data):
with open(progress_file, "w") as f:
json.dump(progress_data, f, indent=2)
def find_images_in_subfolders(folder_path):
image_extensions = (".png", ".jpg", ".jpeg", ".gif", ".bmp")
image_files = []
for root, dirs, files in os.walk(folder_path):
for file in files:
if file.endswith(image_extensions):
image_files.append(os.path.join(root, file))
return image_files
def main(args):
dist_rank = args.dist
dist_size = args.total_dist
base_dir = os.path.dirname(args.result_file)
os.makedirs(base_dir, exist_ok=True) # Ensure the base directory exists
progress_file = f"{base_dir}/progress_{dist_rank}_or_{dist_size}.json"
progress_data = load_progress(progress_file)
with open(args.json_path, "r") as fp:
data = json.load(fp)
image_files = [os.path.join(args.images_root, item["image"]) for item in data]
image_files = image_files[: args.limit] if args.limit > 0 else image_files
# Shard the data
shard_size = len(image_files) // dist_size
start_index = shard_size * dist_rank
end_index = start_index + shard_size if dist_rank < dist_size - 1 else len(image_files)
shard_files = image_files[start_index:end_index]
print(f"Querying {len(shard_files)} images from index {start_index} to {end_index - 1}")
# Select backend
backend = select_sglang_backend(args)
sgl.set_default_backend(backend)
tic = time.time()
batch_size = args.parallel
for batch_start in tqdm.tqdm(range(0, len(shard_files), batch_size)):
batch_end = min(batch_start + batch_size, len(shard_files))
if batch_start <= progress_data.get("last_index", -1):
print(f"Skipping already processed batch starting at {batch_start}")
continue
batch_arguments = [{"image_file": image_file} for image_file in shard_files[batch_start:batch_end]]
try:
batch_states = image_description.run_batch(batch_arguments, temperature=0, num_threads=args.parallel, progress_bar=False)
for i, ret in enumerate(batch_states):
image_file = batch_arguments[i]["image_file"]
caption = ret.text().split("ASSISTANT:")[-1].strip()
progress_data["annotations"].append({"image_file": image_file, "caption": caption})
progress_data["last_index"] = batch_start + i # Update last_index relative to this rank's shard
save_progress(progress_file, progress_data)
except Exception as e:
print(f"Error during batch processing: {e}")
save_progress(progress_file, progress_data)
break
latency = time.time() - tic
print(f"Latency: {latency:.3f}")
value = {
"task": "image_captioning",
"backend": args.backend,
"num_gpus": 1,
"latency": round(latency, 3),
"num_requests": len(shard_files),
"parallel": args.parallel,
"results": progress_data["annotations"],
}
result_file = args.result_file.replace(".json", f"_shard_{dist_rank}_or_{dist_size}.json")
print(f"Write output to {result_file}")
with open(result_file, "w") as fout:
json.dump(value, fout, indent=2)
save_progress(progress_file, progress_data)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--images_root", type=str, default="/mnt/bn/vl-research/data/llava_data/cc3m")
parser.add_argument("--json_path", type=str, default="/mnt/bn/vl-research/data/llava_instruct/cc3m_recap_requery_363707.json")
parser.add_argument("--max_tokens", type=int, default=1024)
parser.add_argument("--parallel", type=int, default=32)
parser.add_argument("--backend", type=str, default="srt")
parser.add_argument("--host", type=str, default="http://127.0.0.1")
parser.add_argument("--port", type=int, default=30000)
parser.add_argument("--result_file", type=str, default="/mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next/playground/sgl_llava_inference.json")
parser.add_argument("--limit", type=int, default=-1)
parser.add_argument("--dist", type=int, default=0, help="The rank of the distributed machine")
parser.add_argument("--total_dist", type=int, default=6, help="Total number of distributed machines")
args = parser.parse_args()
main(args)
-217
View File
@@ -1,217 +0,0 @@
from datasets import Dataset, Features, Value, ClassLabel, Sequence, Image
import json
import PIL.Image as pil_image
from io import BytesIO
from tqdm import tqdm
json_paths = [
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mavis_math_metagen_87358.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mavis_math_rule_geo_100000.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/k12_printing_train_256646.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/iiit5k_annotations_2000.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/hme100k_train_clean_74502.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/ai2d_azuregpt_detailed_understanding_4874.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/infographic_vqa_4404.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/infographic_azuregpt4v_1992.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/lrv_chart_1787.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/lrv_normal_gpt4v_filtered_10500.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/scienceqa_nona_context_19218.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/allava_instruct_vflan4v_20000.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/allava_instruct_laion4v_50000.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/textocr_gpt4v_train_converted_25114.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/ai2d_train_internvl_single_12413.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/textcaps_train_21952.json",
# "/mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_qa_sft.json",
# "/mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_cap_sft.json",
# "/mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_ie_sft.json",
# "/mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_kg_sft.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/vision_flan_filtered_186070.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mathqa_29837.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo3k_2101.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo170k_qa_converted_67833.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo170k_align_converted_60252.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-coco-50k.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-knowledge-2k.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-llava-30k.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-sam-20k.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_CLEVR-Math_5290.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_FigureQA_17597.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_Geometry3K_9734.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_GeoQA+_17172.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_GEOS_508.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_IconQA_22599.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_MapQA_5235.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_PMC-VQA_35958.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_Super-CLEVR_8652.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_TabMWP_22462.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_UniGeo_11959.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_VizWiz_6614.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_qwen2_72b_st_300000_sp_token_fltd_299992.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_l3_80b_st_300000.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_l3_80b_mt_300000_sp_token_fltd_299998.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/image_textualization_dataset_filtered.json",
# "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/cambrian_filtered_gpt4vo_sp_token_fltd_max10k.json",
"/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4o_dataset.jsonl",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/ai2d_llava_format_2434.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/aokvqa_16539_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/chart2text_26961.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/chartqa_18265_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/clevr_70000_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/diagram_image_to_text_300.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/dvqa_200000_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/figureqa_100000_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/geomverse_9303.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/hateful_memes_8500_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/hitab_2500_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/iam_5663.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/raven_42000.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/iconqa_llava_format_27307.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/infographic_vqa_2118_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/intergps_1280_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/mapqa_37417_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/multihiertt_7619.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/rendered_text_10000.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/robut_sqa_8514.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/robut_wikisql_74989.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/robut_wtq_38246_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/screen2words_15730.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/scienceqa_llava_format_4976.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/tabmwp_22722.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/tallyqa_98680_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/st_vqa_17247_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/tqa_llava_format_27307.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/visual7w_llava_format_14366.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/visualmrc_3027.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/vqarad_313_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/vsr_2157_llava_format.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/vistext_9969.json",
"/mnt/bn/vl-research/data/llava_instruct/cauldron/websight_10000.json"
]
short_names = [
# "mavis_math_metagen",
# "mavis_math_rule_geo",
# "k12_printing",
# "iiit5k",
# "hme100k",
# "ai2d(gpt4v)",
# "infographic_vqa",
# "infographic(gpt4v)",
# "lrv_chart",
# "lrv_normal(filtered)",
# "scienceqa(nona_context)",
# "allava_instruct_vflan4v",
# "allava_instruct_laion4v",
# "textocr(gpt4v)",
# "ai2d(internvl)",
# "textcaps",
# "ureader_qa", # need to re-upload
# "ureader_cap", # need to re-upload
# "ureader_ie", # need to re-upload
# "ureader_kg", # need to re-upload
# "vision_flan(filtered)",
# "mathqa",
# "geo3k",
# "geo170k(qa)",
# "geo170k(align)",
# "sharegpt4v(coco)",
# "sharegpt4v(knowledge)",
# "sharegpt4v(llava)",
# "sharegpt4v(sam)",
# "CLEVR-Math(MathV360K)",
# "FigureQA(MathV360K)",
# "Geometry3K(MathV360K)",
# "GeoQA+(MathV360K)",
# "GEOS(MathV360K)",
# "IconQA(MathV360K)",
# "MapQA(MathV360K)",
# "PMC-VQA(MathV360K)",
# "Super-CLEVR(MathV360K)",
# "TabMWP(MathV360K)",
# "UniGeo(MathV360K)",
# "VizWiz(MathV360K)",
# "magpie_pro(qwen2_72b_st)",
# "magpie_pro(l3_80b_st)",
# "magpie_pro(l3_80b_mt)",
# "image_textualization(filtered)",
# "cambrian(filtered_gpt4vo)", # need to re-upload
"sharegpt4o",
"ai2d(cauldron,llava_format)",
"aokvqa(cauldron,llava_format)",
"chart2text(cauldron)",
"chartqa(cauldron,llava_format)",
"clevr(cauldron,llava_format)",
"diagram_image_to_text(cauldron)",
"dvqa(cauldron,llava_format)",
"figureqa(cauldron,llava_format)",
"geomverse(cauldron)",
"hateful_memes(cauldron,llava_format)",
"hitab(cauldron,llava_format)",
"iam(cauldron)",
"raven(cauldron)",
"iconqa(cauldron,llava_format)",
"infographic_vqa_llava_format",
"intergps(cauldron,llava_format)",
"mapqa(cauldron,llava_format)",
"multihiertt(cauldron)",
"rendered_text(cauldron)",
"robut_sqa(cauldron)",
"robut_wikisql(cauldron)",
"robut_wtq(cauldron,llava_format)",
"screen2words(cauldron)",
"scienceqa(cauldron,llava_format)",
"tabmwp(cauldron)",
"tallyqa(cauldron,llava_format)",
"st_vqa(cauldron,llava_format)",
"tqa(cauldron,llava_format)",
"visual7w(cauldron,llava_format)",
"visualmrc(cauldron)",
"vqarad(cauldron,llava_format)",
"vsr(cauldron,llava_format)",
"vistext(cauldron)",
"websight(cauldron)"
]
def upload_data(json_path, short_name):
def gen():
if json_path.endswith(".jsonl"):
with open(json_path, "r") as f:
data = [json.loads(line) for line in f]
else:
with open(json_path, "r") as f:
data = json.load(f)
preview_index = 5
idx = 0
for item in tqdm(data):
if preview_index > 0:
preview_index -= 1
print(item)
continue
try:
if "image" in item:
image_path = f"/mnt/bn/vl-research/data/llava_data/{item['image']}"
try:
with open(image_path, "rb") as img_file:
image = pil_image.open(BytesIO(img_file.read()))
except:
print(f"Failed to load image {item['image']}")
continue
else:
image = None
item_id = item["id"] if "id" in item else f"{idx:06d}"
yield {"id": item_id, "image": image, "conversations": item["conversations"], "data_source": short_name}
idx += 1
except Exception as e:
print(e)
continue
hf_dataset = Dataset.from_generator(generator=gen, num_proc=32)
hf_dataset.push_to_hub("lmms-lab/LLaVA-OneVision-Data", config_name=short_name, split="train")
for json_path, short_name in zip(json_paths, short_names):
upload_data(json_path, short_name)
+5 -18
View File
@@ -3,22 +3,11 @@ export NCCL_IB_DISABLE=0
export NCCL_IB_GID_INDEX=3 export NCCL_IB_GID_INDEX=3
export NCCL_SOCKET_IFNAME=eth0 export NCCL_SOCKET_IFNAME=eth0
export NCCL_DEBUG=INFO export NCCL_DEBUG=INFO
VISION_MODEL_VERSION="google/siglip-so400m-patch14-384"
VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}"
############### Pretrain ################
BASE_RUN_NAME="llavanext-google_siglip-so400m-patch14-384-Qwen_Qwen2-7B-Instruct-mlp2x_gelu-pretrain_blip558k_plain"
echo "BASE_RUN_NAME: ${BASE_RUN_NAME}"
############### Finetune ################ ############### Finetune ################
MIXTURE="qwen" MIXTURE="qwen"
# Stage 2
PROMPT_VERSION="qwen_vla" PROMPT_VERSION="qwen_vla"
RUN_NAME="llava-openvla-${MIXTURE}" RUN_NAME="openvla-${MIXTURE}"
PREV_STAGE_CHECKPOINT="/mnt/d/temp/huggingface/hub/llava_qwen_vla" PREV_STAGE_CHECKPOINT="models/llava-onevision-qwen2-0.5b-ov"
# PREV_STAGE_CHECKPOINT="/mnt/d/ckpt/llava-openvla-qwen"
echo "PREV_STAGE_CHECKPOINT: ${PREV_STAGE_CHECKPOINT}" echo "PREV_STAGE_CHECKPOINT: ${PREV_STAGE_CHECKPOINT}"
echo "MID_RUN_NAME: ${RUN_NAME}" echo "MID_RUN_NAME: ${RUN_NAME}"
@@ -30,8 +19,8 @@ ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NN
--deepspeed scripts/zero2.json \ --deepspeed scripts/zero2.json \
--model_name_or_path $PREV_STAGE_CHECKPOINT \ --model_name_or_path $PREV_STAGE_CHECKPOINT \
--version $PROMPT_VERSION \ --version $PROMPT_VERSION \
--data_path /root/code/openvla-llava/LLaVA-Next/scripts/train/vla.yaml \ --data_path scripts/train/vla.yaml \
--image_folder /root/code/openvla-llava/LLaVA-Next/images \ --image_folder /images \
--mm_tunable_parts="mm_language_model" \ --mm_tunable_parts="mm_language_model" \
--mm_vision_tower_lr=2e-6 \ --mm_vision_tower_lr=2e-6 \
--vision_tower google/siglip-so400m-patch14-384 \ --vision_tower google/siglip-so400m-patch14-384 \
@@ -45,7 +34,7 @@ ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NN
--mm_patch_merge_type spatial_unpad \ --mm_patch_merge_type spatial_unpad \
--bf16 True \ --bf16 True \
--run_name $RUN_NAME \ --run_name $RUN_NAME \
--output_dir /mnt/d/ckpt_3_28/$RUN_NAME \ --output_dir /$RUN_NAME \
--num_train_epochs 5 \ --num_train_epochs 5 \
--per_device_train_batch_size 8 \ --per_device_train_batch_size 8 \
--per_device_eval_batch_size 100 \ --per_device_eval_batch_size 100 \
@@ -70,5 +59,3 @@ ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NN
--dataloader_drop_last True \ --dataloader_drop_last True \
--frames_upbound 1 --frames_upbound 1
exit 0; exit 0;
# You can delete the sdpa attn_implementation if you want to use flash attn
+1 -1
View File
@@ -1,3 +1,3 @@
datasets: datasets:
- json_path: /root/code/openvla-llava/LLaVA-Next/vla_llava.json - json_path: /root/data/vla_llava.json
sampling_strategy: "all" sampling_strategy: "all"