Skip to content

Commit 83bcc00

Browse files
authored
fix(sagemaker-templates): Fix batch inference parameter resolution (#425)
* fix(sagemaker-templates): Fix batch inference parameter resolution The batch inference template was passing ParameterString objects directly to job_arguments, which caused them to be serialized as literal JSON strings instead of being resolved at runtime. Changes: - Use ProcessingInput with parameter as source for dynamic S3 paths - Specify explicit destination filename to avoid path ambiguity - Follows the same pattern as xgboost_abalone template This fixes the issue where InputDataUrl parameter was passed as '{"Get": "Parameters.InputDataUrl"}' instead of the actual S3 path, causing preprocessing jobs to fail with IndexError. Resolves parameter resolution for batch inference pipelines. * fix(sagemaker-templates): Fix batch inference parameter resolution The batch inference template was passing ParameterString objects directly to job_arguments, which caused them to be serialized as literal JSON strings instead of being resolved at runtime. Changes: - Use ProcessingInput with parameter as source for dynamic S3 paths - Pass S3 URL parameter to script so it can extract filename - Script constructs local path from filename - Remove S3 download logic (ProcessingInput handles it) This fixes the issue where InputDataUrl parameter was passed as '{"Get": "Parameters.InputDataUrl"}' instead of the actual S3 path, causing preprocessing jobs to fail with IndexError. Resolves parameter resolution for batch inference pipelines. * fix(sagemaker-templates): Fix batch inference parameter resolution The batch inference template was passing ParameterString objects directly to job_arguments, which caused them to be serialized as literal JSON strings instead of being resolved at runtime. Root cause: ParameterString objects cannot be passed to job_arguments in SageMaker SDK - they get serialized as '{"Get": "Parameters.Name"}' instead of being resolved. This is unique to batch_inference template; all other templates use hardcoded strings. Solution: - Use ProcessingInput to download file from S3 URL parameter - Script finds CSV file in /opt/ml/processing/input/ directory - Remove --input-data from job_arguments (can't pass parameters there) This allows users to specify different input data via InputDataUrl parameter while working around the SDK limitation. * fix(sagemaker-templates): Convert sparse matrix to dense in batch inference preprocessing The preprocessing script was outputting sparse matrix format instead of CSV, causing the Transformer step to fail with 'could not convert string to float'. Fix: Convert sparse matrix to dense array before writing to CSV. * style(sagemaker-templates): Fix linting issues in batch inference preprocessing - Remove unused boto3 import - Move glob import to top - Fix line length violations (E501) - Remove trailing whitespace (W293) * style(sagemaker-templates): Apply ruff formatting to preprocessing.py --------- Co-authored-by: Nuri Boardman <nurboard@amazon.com>
1 parent 6140600 commit 83bcc00

2 files changed

Lines changed: 20 additions & 14 deletions

File tree

  • modules/sagemaker/sagemaker-templates/templates/batch_inference/seed_code/build_app

modules/sagemaker/sagemaker-templates/templates/batch_inference/seed_code/build_app/ml_pipelines/transformer/pipeline.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import sagemaker.session
77
from sagemaker import ModelPackage
88
from sagemaker.inputs import TransformInput
9-
from sagemaker.processing import ProcessingOutput
9+
from sagemaker.processing import ProcessingInput, ProcessingOutput
1010
from sagemaker.sklearn.processing import SKLearnProcessor
1111
from sagemaker.transformer import Transformer
1212
from sagemaker.workflow.execution_variables import ExecutionVariables
@@ -141,13 +141,17 @@ def get_pipeline(
141141
step_process = ProcessingStep(
142142
name="PreprocessData",
143143
processor=sklearn_processor,
144+
inputs=[
145+
ProcessingInput(
146+
source=input_data,
147+
destination="/opt/ml/processing/input",
148+
),
149+
],
144150
outputs=[
145151
ProcessingOutput(output_name="output_data", source="/opt/ml/processing/output_data"),
146152
],
147153
code="source_scripts/preprocessing.py",
148154
job_arguments=[
149-
"--input-data",
150-
input_data,
151155
"--do-train-test-split",
152156
"False",
153157
],

modules/sagemaker/sagemaker-templates/templates/batch_inference/seed_code/build_app/source_scripts/preprocessing.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"""Feature engineers the abalone dataset."""
22

33
import argparse
4+
import glob
45
import logging
56
import os
67
import pathlib
78
from typing import Any, Dict
89

9-
import boto3
1010
import numpy as np
1111
import pandas as pd
1212
from sklearn.compose import ColumnTransformer
@@ -55,23 +55,22 @@ def merge_two_dicts(x: Dict[str, Any], y: Dict[str, Any]) -> Dict[str, Any]:
5555
if __name__ == "__main__":
5656
logger.debug("Starting preprocessing.")
5757
parser = argparse.ArgumentParser()
58-
parser.add_argument("--input-data", type=str, required=True)
58+
parser.add_argument("--input-data", type=str, required=False)
5959
parser.add_argument("--do-train-test-split", type=str, default="True")
6060
args = parser.parse_args()
6161

6262
base_dir = "/opt/ml/processing"
6363
pathlib.Path(f"{base_dir}/data").mkdir(parents=True, exist_ok=True)
64-
input_data = args.input_data
65-
logger.info("Input data path: %s", input_data)
66-
bucket = input_data.split("/")[2]
67-
key = "/".join(input_data.split("/")[3:])
6864

69-
logger.info("Downloading data from bucket: %s, key: %s", bucket, key)
70-
fn = f"{base_dir}/data/abalone-dataset.csv"
71-
s3 = boto3.resource("s3")
72-
s3.Bucket(bucket).download_file(key, fn)
65+
# ProcessingInput downloads file to /opt/ml/processing/input/
66+
# Find the CSV file (parameter can't be passed to job_arguments)
67+
csv_files = glob.glob("/opt/ml/processing/input/*.csv")
68+
if csv_files:
69+
fn = csv_files[0]
70+
logger.info("Found CSV file: %s", fn)
71+
else:
72+
raise ValueError("No CSV files found in /opt/ml/processing/input/")
7373

74-
logger.debug("Reading downloaded data.")
7574
df = pd.read_csv(
7675
fn,
7776
header=None,
@@ -117,4 +116,7 @@ def merge_two_dicts(x: Dict[str, Any], y: Dict[str, Any]) -> Dict[str, Any]:
117116
pd.DataFrame(test).to_csv(f"{base_dir}/test/test.csv", header=False, index=False)
118117
else:
119118
logger.info("Writing out datasets to %s.", base_dir)
119+
# Convert sparse matrix to dense array if needed
120+
if hasattr(X_pre, "toarray"):
121+
X_pre = X_pre.toarray()
120122
pd.DataFrame(X_pre).to_csv(f"{base_dir}/output_data/data.csv", header=False, index=False)

0 commit comments

Comments
 (0)