Skip to content

Commit 26bb863

Browse files
authored
Refactor datasets and update dependencies (#19)
1 parent fb7f8b3 commit 26bb863

7 files changed

Lines changed: 545 additions & 480 deletions

File tree

poetry.lock

Lines changed: 498 additions & 429 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

projects/med_benchmarking/datasets/mimiciv_cxr.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import json
44
import logging
55
import os
6-
from typing import Callable, Literal, Optional, get_args
6+
from typing import Callable, Literal, Optional, Union, get_args
77

88
import numpy as np
99
import pandas as pd
@@ -69,7 +69,7 @@ def __init__(
6969
split: Literal["train", "validate", "test"],
7070
labeler: Literal["chexpert", "negbio", "double_image", "single_image"],
7171
transform: Optional[Callable[[Image.Image], torch.Tensor]] = None,
72-
tokenizer: Optional[Callable[[str], torch.Tensor]] = None,
72+
tokenizer: Optional[Callable[[str], Union[torch.Tensor, dict]]] = None,
7373
include_report: bool = False,
7474
) -> None:
7575
"""Initialize the dataset."""
@@ -103,20 +103,18 @@ def __init__(
103103
self._labeler = labeler
104104

105105
if self._labeler in ["double_image", "single_image"]:
106-
df = pd.read_csv(data_path)
107-
df = df.dropna(subset=["caption"]) # some captions are missing
108-
self.entries = df.to_dict("records")
106+
self.data_df = pd.read_csv(data_path)
107+
self.data_df = self.data_df.dropna(
108+
subset=["caption"]
109+
) # some captions are missing
109110
else:
110-
with open(data_path, "rb") as file:
111-
entries = json.load(file)
111+
self.data_df = pd.read_json(data_path)
112112

113113
# remove entries with no label if reports are not requested either
114-
old_num = len(entries)
115-
entries_df = pd.DataFrame(entries)
116-
entries_df = entries_df[entries_df["label"].apply(len) > 0]
117-
self.entries = entries_df.to_dict("records")
114+
old_num = len(self.data_df)
115+
entries_df = self.data_df[self.data_df["label"].apply(len) > 0]
118116
logger.info(
119-
f"{old_num - len(entries)} datapoints removed due to lack of a label."
117+
f"{old_num - len(self.data_df)} datapoints removed due to lack of a label."
120118
)
121119

122120
if transform is not None:
@@ -128,7 +126,7 @@ def __init__(
128126

129127
def __getitem__(self, idx: int) -> Example:
130128
"""Return all the images and the label vector of the idx'th study."""
131-
entry = self.entries[idx]
129+
entry = self.data_df.iloc[idx]
132130
img_path = entry["image_path"]
133131

134132
with Image.open(
@@ -171,7 +169,7 @@ def __getitem__(self, idx: int) -> Example:
171169

172170
def __len__(self) -> int:
173171
"""Return the length of the dataset."""
174-
return len(self.entries)
172+
return len(self.data_df)
175173

176174

177175
class CreateJSONFiles(object):

projects/med_benchmarking/datasets/pmcoa.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
"""PMC-OA dataset."""
22

33
import os
4-
from typing import Any, Callable, Dict, Literal, Optional, Tuple
4+
from typing import Any, Callable, Dict, Literal, Optional, Tuple, Union
55

6-
import jsonlines
7-
import pandas as pd
86
import torch
97
from omegaconf import MISSING
108
from PIL import Image
119
from torch.utils.data import Dataset
1210
from torchvision import transforms
11+
import pyarrow.json as pj
12+
from pyarrow import csv
13+
import pyarrow as pa
1314

1415
from mmlearn.conf import external_store
1516
from mmlearn.constants import EXAMPLE_INDEX_KEY
@@ -30,7 +31,7 @@ def __init__(
3031
caption_key: str = "caption",
3132
csv_separator: str = ",",
3233
transform: Optional[Callable[[Image.Image], torch.Tensor]] = None,
33-
tokenizer: Optional[Callable[[str], torch.Tensor]] = None,
34+
tokenizer: Optional[Callable[[str], Union[torch.Tensor, dict]]] = None,
3435
mask_generator: Optional[
3536
Callable[
3637
[Dict[str, torch.Tensor], Any],
@@ -104,13 +105,13 @@ def __len__(self) -> int:
104105
def __getitem__(self, idx: int) -> Example:
105106
"""Return items in the dataset."""
106107
image_path = os.path.join(
107-
self.root_dir, self.image_dir, self.image_filenames[idx]
108+
self.root_dir, self.image_dir, self.image_filenames[idx].as_py()
108109
)
109110

110111
with Image.open(image_path) as img:
111112
images = self.transform(img)
112113

113-
caption = str(self.captions[idx])
114+
caption = self.captions[idx].as_py()
114115
example = Example(
115116
{
116117
Modalities.RGB: images,
@@ -141,19 +142,18 @@ def __getitem__(self, idx: int) -> Example:
141142

142143
def _csv_loader(
143144
self, input_filename: str, img_key: str, caption_key: str, sep: str
144-
) -> Tuple[Any, Any]:
145+
) -> Tuple[pa.ChunkedArray, pa.ChunkedArray]:
145146
"""Load images, captions from CSV data."""
146-
df = pd.read_csv(input_filename, sep=sep)
147-
images, captions = df[img_key].tolist(), df[caption_key].tolist()
148-
return images, captions
147+
table = csv.read_csv(
148+
input_filename,
149+
parse_options=csv.ParseOptions(delimiter=sep, newlines_in_values=True),
150+
)
151+
return table[img_key], table[caption_key]
149152

150153
def _jsonl_loader(
151154
self, input_filename: str, img_key: str, caption_key: str
152-
) -> Tuple[Any, Any]:
155+
) -> Tuple[pa.ChunkedArray, pa.ChunkedArray]:
153156
"""Load images, captions from JSON data."""
154-
images, captions = [], []
155-
with jsonlines.open(input_filename) as reader:
156-
for obj in reader:
157-
images.append(obj[img_key])
158-
captions.append(obj[caption_key])
159-
return images, captions
157+
parse_options = pj.ParseOptions(newlines_in_values=True)
158+
table = pj.read_json(input_filename, parse_options=parse_options)
159+
return table[img_key], table[caption_key]

projects/med_benchmarking/datasets/quilt.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import ast
44
import os
5-
from typing import Callable, List, Literal, Optional
5+
from typing import Callable, List, Literal, Optional, Union
66

77
import pandas as pd
88
import torch
@@ -47,7 +47,7 @@ def __init__(
4747
split: Literal["train", "val"] = "train",
4848
subset: Optional[List[str]] = None,
4949
transform: Optional[Callable[[Image.Image], torch.Tensor]] = None,
50-
tokenizer: Optional[Callable[[str], torch.Tensor]] = None,
50+
tokenizer: Optional[Callable[[str], Union[torch.Tensor, dict]]] = None,
5151
processor: Optional[
5252
Callable[[Image.Image, str], tuple[torch.Tensor, torch.Tensor]]
5353
] = None,
@@ -128,17 +128,17 @@ def __getitem__(self, idx: int) -> Example:
128128
try:
129129
with Image.open(
130130
os.path.join(
131-
self.root_dir, "quilt_1m", self.data_df["image_path"].iloc[idx]
131+
self.root_dir, "quilt_1m", self.data_df.loc[idx, "image_path"]
132132
)
133133
) as img:
134134
image = img.convert("RGB")
135135
except Exception as e:
136-
print(f"ERROR: {e} on {self.data_df['image_path'].iloc[idx]}")
136+
print(f"ERROR: {e} on {self.data_df.loc[idx, 'image_path']}")
137137

138138
if self.transform is not None:
139139
image = self.transform(image)
140140

141-
caption = self.data_df["caption"].iloc[idx]
141+
caption = self.data_df.loc[idx, "caption"]
142142
tokens = self.tokenizer(caption) if self.tokenizer is not None else None
143143

144144
if self.processor is not None:
@@ -150,9 +150,9 @@ def __getitem__(self, idx: int) -> Example:
150150
Modalities.TEXT: caption,
151151
EXAMPLE_INDEX_KEY: idx,
152152
"qid": self.data_df.index[idx],
153-
"magnification": self.data_df["magnification"].iloc[idx],
154-
"height": self.data_df["height"].iloc[idx],
155-
"width": self.data_df["width"].iloc[idx],
153+
"magnification": self.data_df.loc[idx, "magnification"],
154+
"height": self.data_df.loc[idx, "height"],
155+
"width": self.data_df.loc[idx, "width"],
156156
}
157157
)
158158

@@ -169,7 +169,7 @@ def __getitem__(self, idx: int) -> Example:
169169

170170
def __len__(self) -> int:
171171
"""Return the length of the dataset."""
172-
return len(self.data_df.index)
172+
return len(self.data_df)
173173

174174

175175
def _safe_eval(x: str) -> list[str]:

projects/med_benchmarking/datasets/roco.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"""ROCO Dataset."""
22

3-
import json
43
import os
54
from typing import Callable, Dict, Literal, Optional, Union
65

76
import torch
7+
import pandas as pd
88
from omegaconf import MISSING
99
from PIL import Image
1010
from torch.utils.data import Dataset
@@ -55,9 +55,7 @@ def __init__(
5555
) -> None:
5656
"""Initialize the dataset."""
5757
data_path = os.path.join(root_dir, group + split + "_dataset.json")
58-
with open(data_path, encoding="utf-8") as file:
59-
entries = [json.loads(line) for line in file.readlines()]
60-
self.entries = entries
58+
self.data_df = pd.read_json(data_path, lines=True)
6159

6260
if processor is None and transform is None:
6361
self.transform = ToTensor()
@@ -80,14 +78,13 @@ def __getitem__(self, idx: int) -> Example:
8078
image and free text caption are returned. Otherwise, the image, free-
8179
text caption, and caption tokens are returned.
8280
"""
83-
entry = self.entries[idx]
84-
with Image.open(entry["image_path"]) as img:
81+
with Image.open(self.data_df.loc[idx, "image_path"]) as img:
8582
image = img.convert("RGB")
8683

8784
if self.transform is not None:
8885
image = self.transform(image)
8986

90-
caption = entry["caption"]
87+
caption = self.data_df.loc[idx, "caption"]
9188
tokens = self.tokenizer(caption) if self.tokenizer is not None else None
9289

9390
if self.processor is not None:
@@ -114,4 +111,4 @@ def __getitem__(self, idx: int) -> Example:
114111

115112
def __len__(self) -> int:
116113
"""Return the length of the dataset."""
117-
return len(self.entries)
114+
return len(self.data_df)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
timm~=1.0.7
22
torchvision~=0.19.0
3-
wandb~=0.17.7
3+
wandb~=0.18.0

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ packages = [
1313
[tool.poetry.dependencies]
1414
python = ">=3.9, <3.13"
1515
numpy = "^1.26.4"
16+
pyarrow = "^17.0.0"
1617
hydra-core = "^1.3.0"
1718
hydra-zen = "^0.13.0"
1819
hydra-submitit-launcher = "^1.2.0"
1920
transformers = "^4.44.0"
2021
torch = {version = "^2.4.0", source = "torch-cu121"}
2122
lightning = "^2.4.0"
22-
jsonlines = "^4.0.0"
2323
pandas = {version="^2.2.2", extras=["performance"]}
2424
torchvision = {version = "^0.19.0", source = "torch-cu121"}
2525

@@ -28,6 +28,7 @@ timm = {version = "^1.0.8", optional = true}
2828
torchaudio = {version = "^2.4.0", source = "torch-cu121", optional = true}
2929
peft = {version = "^0.12.0", optional = true}
3030

31+
3132
[tool.poetry.group.vision.dependencies]
3233
opencv-python = "^4.10.0.84"
3334
timm = "^1.0.8"
@@ -45,7 +46,7 @@ peft = "^0.12.0"
4546
optional = true
4647

4748
[tool.poetry.group.dev.dependencies]
48-
wandb = "^0.17.7"
49+
wandb = "^0.18.0"
4950
ipykernel = "^6.29.5"
5051
ipython = "8.18.0"
5152
h5py = "^3.11.0"

0 commit comments

Comments
 (0)