Skip to content

Commit 6b8277b

Browse files
authored
Merge pull request #129 from Leengit/performance_profiling
TEST: Add performance scripts in `example` directory: `performance-EfficientNetV2S.py` `performance-EfficientNet_V2_S_Weights.IMAGENET1K_V1.py` `performance-detection.py` `performance-mnist.py`. DOC: Also add research and notes regarding speeding up `torch` using multiple workers / multiple processes.
2 parents f9546fa + 9c29646 commit 6b8277b

File tree

7 files changed

+920
-13
lines changed

7 files changed

+920
-13
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# =========================================================================
2+
#
3+
# Copyright NumFOCUS
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# https://www.apache.org/licenses/LICENSE-2.0.txt
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
# =========================================================================
18+
19+
import histomics_stream as hs
20+
import histomics_stream.tensorflow
21+
import os
22+
import pooch
23+
import tensorflow as tf
24+
import time
25+
26+
"""
27+
This is a script that is used to make timings of histomics_stream. To some extent, it
28+
may be specific to the computer / docker image it is used with and need minor tweaks to
29+
run on another computer.
30+
"""
31+
32+
"""
33+
# If you've just started a fresh docker container you may need some of this:
34+
apt update ; apt install -y git emacs ; \
35+
rm -rf /.local ; \
36+
pip install -U pip setuptools wheel ; \
37+
pip install \
38+
'batchbald_redux' \
39+
'black[jupyter]' \
40+
'large_image[openslide,tiff]' \
41+
'nbformat>=5.2.0' \
42+
'pooch' \
43+
'protobuf<3.20' \
44+
'tensorflow_datasets' \
45+
'torch==1.12.1+cu113' \
46+
'/tf/notebooks/histomics_stream' \
47+
--extra-index-url https://download.pytorch.org/whl/cu113 \
48+
--find-links https://girder.github.io/large_image_wheels
49+
"""
50+
51+
52+
def get_data():
53+
start_time = time.time()
54+
wsi_path = pooch.retrieve(
55+
fname="TCGA-AN-A0G0-01Z-00-DX1.svs",
56+
url="https://drive.google.com/uc"
57+
"?export=download"
58+
"&id=19agE_0cWY582szhOVxp9h3kozRfB4CvV"
59+
"&confirm=t"
60+
"&uuid=6f2d51e7-9366-4e98-abc7-4f77427dd02c"
61+
"&at=ALgDtswlqJJw1KU7P3Z1tZNcE01I:1679111148632",
62+
known_hash="d046f952759ff6987374786768fc588740eef1e54e4e295a684f3bd356c8528f",
63+
path=str(pooch.os_cache("pooch")) + os.sep + "wsi",
64+
)
65+
print(f"Retrieved {wsi_path} in {time.time() - start_time}s", flush=True)
66+
67+
# download binary mask image
68+
start_time = time.time()
69+
mask_path = pooch.retrieve(
70+
fname="TCGA-AN-A0G0-01Z-00-DX1.mask.png",
71+
url="https://drive.google.com/uc"
72+
"?export=download"
73+
"&id=17GOOHbL8Bo3933rdIui82akr7stbRfta",
74+
known_hash="bb657ead9fd3b8284db6ecc1ca8a1efa57a0e9fd73d2ea63ce6053fbd3d65171",
75+
path=str(pooch.os_cache("pooch")) + os.sep + "wsi",
76+
)
77+
print(f"Retrieved {mask_path} in {time.time() - start_time}s", flush=True)
78+
return wsi_path, mask_path
79+
80+
81+
class WrappedModel(tf.keras.Model):
82+
def __init__(self, unwrapped_model, *args, **kwargs):
83+
super(WrappedModel, self).__init__(*args, **kwargs)
84+
self.unwrapped_model = unwrapped_model
85+
86+
def call(self, element):
87+
return self.unwrapped_model(element[0]), element[1]
88+
89+
90+
def normalize_img(image, label):
91+
"""Normalizes images: `uint8` -> `float32`."""
92+
return tf.cast(image, tf.float32) / 255.0, label
93+
94+
95+
def build_model(training_batch, epochs):
96+
start_time = time.time()
97+
unwrapped_model = tf.keras.applications.efficientnet_v2.EfficientNetV2S(
98+
include_top=False, weights="imagenet", input_shape=(224, 224, 3), pooling="avg"
99+
)
100+
unwrapped_model.compile(
101+
optimizer=tf.keras.optimizers.Adam(0.001),
102+
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
103+
metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
104+
)
105+
# unwrapped_model.fit(ds_train, epochs=epochs, validation_data=ds_test)
106+
107+
wrapped_model = WrappedModel(unwrapped_model)
108+
109+
print(f"Finished model in {time.time() - start_time}s", flush=True)
110+
return unwrapped_model, wrapped_model
111+
112+
113+
def create_study(wsi_path, mask_path, chunk_size):
114+
start_time = time.time()
115+
slide_name = os.path.splitext(os.path.split(wsi_path)[1])[0]
116+
slide_group = "Group 3"
117+
118+
study = dict(
119+
version="version-1",
120+
tile_height=224,
121+
tile_width=224,
122+
overlap_height=0,
123+
overlap_width=0,
124+
slides=dict(
125+
Slide_0=dict(
126+
filename=wsi_path,
127+
slide_name=slide_name,
128+
slide_group=slide_group,
129+
chunk_height=chunk_size,
130+
chunk_width=chunk_size,
131+
)
132+
),
133+
)
134+
135+
find_slide_resolution = hs.configure.FindResolutionForSlide(
136+
study, target_magnification=20, magnification_source="exact"
137+
)
138+
tiles_by_grid_and_mask = hs.configure.TilesByGridAndMask(
139+
study, mask_filename=mask_path
140+
)
141+
# We could apply these to a subset of the slides, but we will apply it to all slides
142+
# in this example.
143+
for slide in study["slides"].values():
144+
find_slide_resolution(slide)
145+
tiles_by_grid_and_mask(slide)
146+
print(f"Masked study in {time.time() - start_time}s", flush=True)
147+
148+
start_time = time.time()
149+
create_tensorflow_dataset = hs.tensorflow.CreateTensorFlowDataset()
150+
tiles = create_tensorflow_dataset(study, num_workers=1, worker_index=0)
151+
print(f"#tiles = {len(create_tensorflow_dataset.get_tiles(study)[0][1])}")
152+
print(f"Chunked study in {time.time() - start_time}s", flush=True)
153+
154+
return study, tiles
155+
156+
157+
def predict(take_predictions, prediction_batch, model, tiles):
158+
start_time = time.time()
159+
tiles = tiles.batch(prediction_batch)
160+
if take_predictions > 0:
161+
predictions = model.predict(
162+
tiles.take(1 + (take_predictions - 1) // prediction_batch)
163+
)
164+
else:
165+
predictions = model.predict(tiles)
166+
print(f"predictions[0].shape = {predictions[0].shape}")
167+
print(f"Made predictions in {time.time() - start_time}s", flush=True)
168+
return predictions
169+
170+
171+
if True:
172+
gpus = [gpu.name for gpu in tf.config.list_logical_devices("GPU")]
173+
print(f"gpus = {repr(gpus)}")
174+
175+
# if __name__ == "__main__":
176+
with tf.device(gpus[0]):
177+
device = "gpu" if True else "cpu"
178+
print(f"***** device = {device} *****")
179+
training_batch = 2**7
180+
num_epochs = 6
181+
take_predictions = 2**10 if False else 0
182+
183+
wsi_path, mask_path = get_data()
184+
unwrapped_model, model = build_model(training_batch, num_epochs)
185+
186+
for prediction_batch in [2**j for j in range(5, 11)]:
187+
for chunk_size in [256] + [2**j for j in range(8, 14)]:
188+
print(
189+
f"***** chunk_size = {chunk_size},"
190+
f" prediction_batch = {prediction_batch},"
191+
f" take_predictions = {take_predictions} ****",
192+
flush=True,
193+
)
194+
study, tiles = create_study(wsi_path, mask_path, chunk_size)
195+
predictions = predict(take_predictions, prediction_batch, model, tiles)
196+
print(f"***** Finished with device = {device} *****")

0 commit comments

Comments
 (0)