Skip to content

Commit e83a1d5

Browse files
authored
Pin tensor_rt digest for PyTorch sentiment Dataflow benchmarks (#38374)
* Fix DistilBERT config compatibility in sentiment benchmark Pin tensor_rt digest for PyTorch sentiment Dataflow benchmarks * Harden Dataflow PyTorch sentiment benchmark worker compatibility * Added default for DisplayData for table row inference batch benchmark * used tensor_rt:latest for sentiment dataflow
1 parent 91f50ce commit e83a1d5

4 files changed

Lines changed: 69 additions & 21 deletions

File tree

.github/workflows/load-tests-pipeline-options/beam_Inference_Python_Benchmarks_Dataflow_Pytorch_Sentiment_Batch_DistilBert_Base_Uncased.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
--machine_type=n1-standard-2
1919
--num_workers=20
2020
--max_num_workers=250
21+
--timeout_ms=600000
2122
--disk_size_gb=50
2223
--autoscaling_algorithm=THROUGHPUT_BASED
2324
--staging_location=gs://temp-storage-for-perf-tests/loadtests
@@ -31,5 +32,7 @@
3132
--device=CPU
3233
--input_file=gs://apache-beam-ml/testing/inputs/sentences_50k.txt
3334
--runner=DataflowRunner
35+
--sdk_location=container
36+
--sdk_container_image=us.gcr.io/apache-beam-testing/python-postcommit-it/tensor_rt:latest
3437
--model_path=distilbert-base-uncased-finetuned-sst-2-english
3538
--model_state_dict_path=gs://apache-beam-ml/models/huggingface.sentiment.distilbert-base-uncased.pth

.github/workflows/load-tests-pipeline-options/beam_Inference_Python_Benchmarks_Dataflow_Pytorch_Sentiment_Streaming_DistilBert_Base_Uncased.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
--machine_type=n1-standard-2
1919
--num_workers=20
2020
--max_num_workers=250
21+
--timeout_ms=600000
2122
--disk_size_gb=50
2223
--autoscaling_algorithm=THROUGHPUT_BASED
2324
--staging_location=gs://temp-storage-for-perf-tests/loadtests
@@ -31,6 +32,8 @@
3132
--device=CPU
3233
--input_file=gs://apache-beam-ml/testing/inputs/sentences_50k.txt
3334
--runner=DataflowRunner
35+
--sdk_location=container
36+
--sdk_container_image=us.gcr.io/apache-beam-testing/python-postcommit-it/tensor_rt:latest
3437
--dataflow_service_options=worker_accelerator=type:nvidia-tesla-t4;count:1;install-nvidia-driver
3538
--model_path=distilbert-base-uncased-finetuned-sst-2-english
3639
--model_state_dict_path=gs://apache-beam-ml/models/huggingface.sentiment.distilbert-base-uncased.pth

sdks/python/apache_beam/examples/inference/pytorch_sentiment.py

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@
4747

4848
class SentimentPostProcessor(beam.DoFn):
4949
"""Processes PredictionResult to extract sentiment label and confidence."""
50-
def __init__(self, tokenizer: DistilBertTokenizerFast):
51-
self.tokenizer = tokenizer
52-
5350
def process(self, element: tuple[str, PredictionResult]) -> Iterable[dict]:
5451
text, prediction_result = element
5552
logits = prediction_result.inference['logits']
@@ -62,16 +59,34 @@ def process(self, element: tuple[str, PredictionResult]) -> Iterable[dict]:
6259
}
6360

6461

65-
def tokenize_text(text: str,
66-
tokenizer: DistilBertTokenizerFast) -> tuple[str, dict]:
67-
"""Tokenizes input text using the specified tokenizer."""
68-
tokenized = tokenizer(
69-
text,
70-
padding='max_length',
71-
truncation=True,
72-
max_length=128,
73-
return_tensors="pt")
74-
return text, {k: torch.squeeze(v) for k, v in tokenized.items()}
62+
class TokenizeTextDoFn(beam.DoFn):
63+
"""Initializes tokenizer per worker and tokenizes input text."""
64+
def __init__(self, model_path: str):
65+
self.model_path = model_path
66+
self.tokenizer = None
67+
68+
def setup(self):
69+
self.tokenizer = DistilBertTokenizerFast.from_pretrained(self.model_path)
70+
if self.tokenizer.pad_token is None:
71+
self.tokenizer.pad_token = '[PAD]'
72+
73+
def process(self, text: str) -> Iterable[tuple[str, dict]]:
74+
tokenized = self.tokenizer(
75+
text,
76+
padding='max_length',
77+
truncation=True,
78+
max_length=128,
79+
return_tensors="pt")
80+
yield text, {k: torch.squeeze(v, 0) for k, v in tokenized.items()}
81+
82+
83+
class DistilBertForSequenceClassificationCompat(
84+
DistilBertForSequenceClassification):
85+
"""Builds config in worker runtime to avoid cross-env config drift."""
86+
def __init__(self, model_name: str, num_labels: int = 2):
87+
config = _ensure_transformers_config_compat(
88+
DistilBertConfig.from_pretrained(model_name, num_labels=num_labels))
89+
super().__init__(config)
7590

7691

7792
class RateLimitDoFn(beam.DoFn):
@@ -83,6 +98,31 @@ def process(self, element):
8398
yield element
8499

85100

101+
def _ensure_transformers_config_compat(
102+
config: DistilBertConfig) -> DistilBertConfig:
103+
"""Adds missing config attributes for cross-version transformers compatibility.
104+
105+
The benchmark can run with container images whose transformers version differs
106+
from the launcher environment. Some versions assume these attributes exist.
107+
"""
108+
# Use a default config instance as the source of canonical attributes for the
109+
# transformers version available on the worker. This avoids chasing one
110+
# missing field at a time (e.g. torchscript, output_attentions).
111+
default_config = DistilBertConfig()
112+
for key, value in default_config.to_dict().items():
113+
if not hasattr(config, key):
114+
setattr(config, key, value)
115+
116+
# Keep non-serialized fields explicitly for older/newer transformers mixes.
117+
if not hasattr(config, 'pruned_heads'):
118+
config.pruned_heads = {}
119+
if not hasattr(config, 'torchscript'):
120+
config.torchscript = False
121+
if not hasattr(config, 'return_dict'):
122+
config.return_dict = True
123+
return config
124+
125+
86126
def parse_known_args(argv):
87127
"""Parses command-line arguments for pipeline execution."""
88128
parser = argparse.ArgumentParser()
@@ -235,13 +275,14 @@ def run(
235275
pipeline_options.view_as(StandardOptions).streaming = True
236276

237277
model_handler = PytorchModelHandlerKeyedTensor(
238-
model_class=DistilBertForSequenceClassification,
239-
model_params={'config': DistilBertConfig(num_labels=2)},
278+
model_class=DistilBertForSequenceClassificationCompat,
279+
model_params={
280+
'model_name': known_args.model_path,
281+
'num_labels': 2,
282+
},
240283
state_dict_path=known_args.model_state_dict_path,
241284
device='GPU')
242285

243-
tokenizer = DistilBertTokenizerFast.from_pretrained(known_args.model_path)
244-
245286
pipeline = test_pipeline or beam.Pipeline(options=pipeline_options)
246287

247288
# Main pipeline: read, process, write result to BigQuery output table
@@ -264,9 +305,9 @@ def run(
264305

265306
_ = (
266307
input
267-
| 'Tokenize' >> beam.Map(lambda text: tokenize_text(text, tokenizer))
308+
| 'Tokenize' >> beam.ParDo(TokenizeTextDoFn(known_args.model_path))
268309
| 'RunInference' >> RunInference(KeyedModelHandler(model_handler))
269-
| 'PostProcess' >> beam.ParDo(SentimentPostProcessor(tokenizer))
310+
| 'PostProcess' >> beam.ParDo(SentimentPostProcessor())
270311
| 'WriteToBigQuery' >> beam.io.WriteToBigQuery(
271312
known_args.output_table,
272313
schema='text:STRING, sentiment:STRING, confidence:FLOAT',
@@ -277,6 +318,7 @@ def run(
277318
result = pipeline.run()
278319
result.wait_until_finish(duration=1800000) # 30 min
279320
result.cancel()
321+
result.wait_until_finish(duration=600000) # up to 10 min to settle cancel
280322

281323
cleanup_pubsub_resources(
282324
project=known_args.project,

sdks/python/apache_beam/testing/benchmarks/inference/table_row_inference_benchmark.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ class TableRowInferenceOptions(
4444
@classmethod
4545
def _add_argparse_args(cls, parser):
4646
parser.add_argument('--mode', default='batch')
47-
parser.add_argument('--input_subscription')
48-
parser.add_argument('--input_file')
47+
parser.add_argument('--input_subscription', default='')
48+
parser.add_argument('--input_file', default='')
4949
parser.add_argument('--output_table')
5050
parser.add_argument('--model_path')
5151
parser.add_argument('--feature_columns')

0 commit comments

Comments
 (0)