Skip to content

Commit e4d7605

Browse files
committed
add asymmetric customized e5 model
Signed-off-by: Fen Qin <[email protected]>
1 parent 423b9b5 commit e4d7605

File tree

12 files changed

+519
-0
lines changed

12 files changed

+519
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Multilingual E5 Small Model - SageMaker & OpenSearch Integration
2+
3+
Deploy the `intfloat/multilingual-e5-small` model to Amazon SageMaker and connect it to OpenSearch for semantic search.
4+
5+
## Project Structure
6+
7+
```
8+
asymmetric_e5_model/
9+
├── sagemaker_deployment/ # SageMaker model deployment
10+
│ ├── deploy_cli.sh # Deploy to SageMaker
11+
│ ├── validate_cli.sh # Validate SageMaker endpoint
12+
│ ├── model-config.json # Model configuration
13+
│ ├── inference.py # Custom inference code
14+
│ └── README.md
15+
├── opensearch_connector/ # OpenSearch integration
16+
│ ├── setup_connector.sh # Setup connector (auto-detects local/managed)
17+
│ ├── validate_connector.sh # Validate connector
18+
│ └── README.md
19+
└── README.md # This file
20+
```
21+
22+
## Quick Start
23+
24+
### 1. Deploy to SageMaker
25+
```bash
26+
cd sagemaker_deployment
27+
./deploy_cli.sh
28+
./validate_cli.sh <endpoint-name>
29+
```
30+
31+
### 2. Setup OpenSearch Connector
32+
```bash
33+
cd opensearch_connector
34+
./setup_connector.sh <opensearch-endpoint> <sagemaker-endpoint-name>
35+
./validate_connector.sh <opensearch-endpoint> <model-id>
36+
```
37+
38+
## Prerequisites
39+
40+
- AWS CLI configured with appropriate permissions
41+
- SageMaker execution role with necessary permissions
42+
- OpenSearch cluster with ML Commons plugin enabled
43+
- `jq` installed for JSON parsing
44+
45+
## Cost Considerations
46+
47+
- ml.t2.medium: ~$0.056/hour (used in deployment)
48+
- ml.m5.large: ~$0.115/hour (alternative for higher throughput)
49+
- Use auto-scaling for production workloads
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# OpenSearch Remote Connector
2+
3+
Connect deployed SageMaker endpoint to OpenSearch for ML inference.
4+
5+
## Files
6+
7+
- `setup_connector.sh` - Setup connector (auto-detects local vs managed OpenSearch)
8+
- `validate_connector.sh` - Validate connector functionality
9+
10+
## Prerequisites
11+
12+
- Deployed SageMaker endpoint
13+
- OpenSearch cluster with ML Commons plugin enabled
14+
- `jq` installed for JSON parsing
15+
16+
## Usage
17+
18+
The setup script automatically detects whether you're using local or managed OpenSearch:
19+
20+
- **Local OpenSearch** (localhost/127.0.0.1): Uses AWS access key credentials
21+
- **Managed OpenSearch** (AWS domain): Uses IAM role credentials
22+
23+
```bash
24+
chmod +x setup_connector.sh validate_connector.sh
25+
./setup_connector.sh <opensearch-endpoint> <sagemaker-endpoint-name>
26+
./validate_connector.sh <opensearch-endpoint> <model-id>
27+
```
28+
29+
## Examples
30+
31+
### Local OpenSearch
32+
```bash
33+
./setup_connector.sh http://localhost:9200 multilingual-e5-endpoint-1761349656
34+
./validate_connector.sh http://localhost:9200 hMW9GJoBeER1e719aVX6
35+
```
36+
37+
### Managed OpenSearch
38+
```bash
39+
./setup_connector.sh https://search-domain.us-east-1.es.amazonaws.com multilingual-e5-endpoint-1761349656
40+
./validate_connector.sh https://search-domain.us-east-1.es.amazonaws.com abc123def456
41+
```
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/bin/bash
2+
3+
if [ $# -ne 2 ]; then
4+
echo "Usage: $0 <opensearch-endpoint> <sagemaker-endpoint-name>"
5+
echo "Example: $0 http://localhost:9200 multilingual-e5-endpoint-1761349656"
6+
exit 1
7+
fi
8+
9+
OPENSEARCH_ENDPOINT=$1
10+
SAGEMAKER_ENDPOINT=$2
11+
REGION="us-east-1"
12+
13+
echo "Setting up asymmetric E5 remote model connector with post-processing..."
14+
15+
# Get AWS credentials
16+
AWS_ACCESS_KEY=$(aws configure get aws_access_key_id)
17+
AWS_SECRET_KEY=$(aws configure get aws_secret_access_key)
18+
AWS_SESSION_TOKEN=$(aws configure get aws_session_token)
19+
20+
# Create connector with post_process_function to flatten response
21+
CONNECTOR_RESPONSE=$(curl -s -X POST "${OPENSEARCH_ENDPOINT}/_plugins/_ml/connectors/_create" \
22+
-H "Content-Type: application/json" \
23+
-d "{
24+
\"name\": \"sagemaker-e5-asymmetric-connector\",
25+
\"description\": \"Connector for multilingual-e5-small asymmetric model with flattened response\",
26+
\"version\": \"1\",
27+
\"protocol\": \"aws_sigv4\",
28+
\"parameters\": {
29+
\"region\": \"${REGION}\",
30+
\"service_name\": \"sagemaker\"
31+
},
32+
\"credential\": {
33+
\"access_key\": \"${AWS_ACCESS_KEY}\",
34+
\"secret_key\": \"${AWS_SECRET_KEY}\",
35+
\"session_token\": \"${AWS_SESSION_TOKEN}\"
36+
},
37+
\"actions\": [
38+
{
39+
\"action_type\": \"predict\",
40+
\"method\": \"POST\",
41+
\"url\": \"https://runtime.sagemaker.${REGION}.amazonaws.com/endpoints/${SAGEMAKER_ENDPOINT}/invocations\",
42+
\"headers\": {
43+
\"content-type\": \"application/json\"
44+
},
45+
\"request_body\": \"{ \\\"texts\\\": \${parameters.texts}, \\\"content_type\\\": \\\"\${parameters.content_type}\\\" }\"
46+
}
47+
]
48+
}")
49+
50+
CONNECTOR_ID=$(echo $CONNECTOR_RESPONSE | jq -r '.connector_id')
51+
52+
if [ "$CONNECTOR_ID" = "null" ] || [ -z "$CONNECTOR_ID" ]; then
53+
echo "Failed to create connector:"
54+
echo $CONNECTOR_RESPONSE
55+
exit 1
56+
fi
57+
58+
echo "✓ Connector created with post-processing: $CONNECTOR_ID"
59+
60+
# Register model with asymmetric identifiers
61+
MODEL_RESPONSE=$(curl -s -X POST "${OPENSEARCH_ENDPOINT}/_plugins/_ml/models/_register" \
62+
-H "Content-Type: application/json" \
63+
-d "{
64+
\"name\": \"e5-asymmetric-remote\",
65+
\"function_name\": \"remote\",
66+
\"connector_id\": \"${CONNECTOR_ID}\",
67+
\"model_config\": {
68+
\"model_type\": \"text_embedding\",
69+
\"embedding_dimension\": 384,
70+
\"framework_type\": \"SENTENCE_TRANSFORMERS\",
71+
\"additional_config\": {
72+
\"space_type\": \"l2\",
73+
\"is_asymmetric\": true,
74+
\"model_family\": \"e5\",
75+
\"query_prefix\": \"query: \",
76+
\"passage_prefix\": \"passage: \"
77+
}
78+
}
79+
}")
80+
81+
TASK_ID=$(echo $MODEL_RESPONSE | jq -r '.task_id')
82+
sleep 10
83+
MODEL_ID=$(curl -s -X GET "${OPENSEARCH_ENDPOINT}/_plugins/_ml/tasks/$TASK_ID" | jq -r '.model_id')
84+
85+
# Deploy model
86+
curl -s -X POST "${OPENSEARCH_ENDPOINT}/_plugins/_ml/models/$MODEL_ID/_deploy" > /dev/null
87+
sleep 15
88+
89+
echo "✓ Model deployed: $MODEL_ID"
90+
echo ""
91+
echo "Run validation: ./validate_connector.sh $OPENSEARCH_ENDPOINT $MODEL_ID"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/bin/bash
2+
3+
if [ $# -ne 2 ]; then
4+
echo "Usage: $0 <opensearch-endpoint> <model-id>"
5+
echo "Example: $0 http://localhost:9200 abc123"
6+
exit 1
7+
fi
8+
9+
OPENSEARCH_ENDPOINT=$1
10+
MODEL_ID=$2
11+
12+
echo "Validating asymmetric remote model with OpenSearch ML Commons format..."
13+
14+
# Check model config
15+
echo "Model configuration:"
16+
curl -s -X GET "${OPENSEARCH_ENDPOINT}/_plugins/_ml/models/$MODEL_ID" | jq '.model_config.additional_config'
17+
18+
# Test query embedding
19+
echo -e "\nTesting query embedding..."
20+
QUERY_RESPONSE=$(curl -s -X POST "${OPENSEARCH_ENDPOINT}/_plugins/_ml/models/$MODEL_ID/_predict" \
21+
-H "Content-Type: application/json" \
22+
-d '{
23+
"parameters": {
24+
"texts": ["What is machine learning?"],
25+
"content_type": "query"
26+
}
27+
}')
28+
29+
# With simplified format, response is wrapped in an array, so access response[0]
30+
QUERY_DIM=$(echo $QUERY_RESPONSE | jq -r '.inference_results[0].output[0].dataAsMap.response[0] | length' 2>/dev/null)
31+
32+
# Test passage embedding
33+
echo "Testing passage embedding..."
34+
PASSAGE_RESPONSE=$(curl -s -X POST "${OPENSEARCH_ENDPOINT}/_plugins/_ml/models/$MODEL_ID/_predict" \
35+
-H "Content-Type: application/json" \
36+
-d '{
37+
"parameters": {
38+
"texts": ["Machine learning is a subset of artificial intelligence."],
39+
"content_type": "passage"
40+
}
41+
}')
42+
43+
PASSAGE_DIM=$(echo $PASSAGE_RESPONSE | jq -r '.inference_results[0].output[0].dataAsMap.response[0] | length' 2>/dev/null)
44+
45+
# Validation results
46+
if [ "$QUERY_DIM" != "null" ] && [ "$PASSAGE_DIM" != "null" ] && [ "$QUERY_DIM" -gt 0 ] && [ "$PASSAGE_DIM" -gt 0 ]; then
47+
echo -e "\n✓ Validation successful with flattened response!"
48+
echo "✓ Query embedding dimension: $QUERY_DIM"
49+
echo "✓ Passage embedding dimension: $PASSAGE_DIM"
50+
echo "✓ Post-processing function working correctly (no processing needed)"
51+
echo "✓ Asymmetric remote model ready for neural-search"
52+
else
53+
echo -e "\n✗ Validation failed"
54+
echo "Query response: $QUERY_RESPONSE"
55+
echo "Passage response: $PASSAGE_RESPONSE"
56+
exit 1
57+
fi
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# SageMaker Deployment
2+
3+
Deploy the `intfloat/multilingual-e5-small` model to Amazon SageMaker.
4+
5+
## Files
6+
7+
- `deploy_cli.sh` - Deploy model to SageMaker endpoint
8+
- `validate_cli.sh` - Validate deployed endpoint
9+
- `model-config.json` - Model configuration template
10+
- `inference.py` - Custom inference code (for future use)
11+
12+
## Usage
13+
14+
### Deploy Model
15+
```bash
16+
chmod +x deploy_cli.sh
17+
./deploy_cli.sh
18+
```
19+
20+
### Validate Deployment
21+
```bash
22+
chmod +x validate_cli.sh
23+
./validate_cli.sh <endpoint-name>
24+
```
25+
26+
## Example
27+
```bash
28+
./deploy_cli.sh
29+
./validate_cli.sh multilingual-e5-endpoint-1761349656
30+
```
31+
32+
## Cleanup
33+
```bash
34+
aws sagemaker delete-endpoint --endpoint-name <endpoint-name>
35+
aws sagemaker delete-endpoint-config --endpoint-config-name <config-name>
36+
aws sagemaker delete-model --model-name <model-name>
37+
```

docs/source/examples/asymmetric_e5_model/sagemaker_deployment/batch_response.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/bin/bash
2+
3+
# Set variables
4+
TIMESTAMP=$(date +%s)
5+
MODEL_NAME="multilingual-e5-small-$TIMESTAMP"
6+
ENDPOINT_CONFIG_NAME="multilingual-e5-config-$TIMESTAMP"
7+
ENDPOINT_NAME="multilingual-e5-endpoint"
8+
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
9+
ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/Admin"
10+
REGION="us-east-1"
11+
BUCKET="sagemaker-$REGION-$ACCOUNT_ID"
12+
13+
# Create code package
14+
echo "Creating code package..."
15+
tar -czf model.tar.gz inference.py
16+
17+
# Upload to S3
18+
echo "Uploading code to S3..."
19+
aws s3 cp model.tar.gz s3://$BUCKET/$MODEL_NAME/model.tar.gz
20+
21+
# Create temporary model config
22+
sed "s/MODEL_TIMESTAMP/$TIMESTAMP/g; s|ROLE_ARN_PLACEHOLDER|$ROLE_ARN|g; s/ACCOUNT_ID/$ACCOUNT_ID/g" model-config.json > temp-model-config.json
23+
24+
# Create model
25+
aws sagemaker create-model \
26+
--region $REGION \
27+
--cli-input-json file://temp-model-config.json
28+
29+
if [ $? -ne 0 ]; then
30+
echo "Failed to create model"
31+
rm -f temp-model-config.json model.tar.gz
32+
exit 1
33+
fi
34+
35+
# Create endpoint configuration
36+
aws sagemaker create-endpoint-config \
37+
--region $REGION \
38+
--endpoint-config-name $ENDPOINT_CONFIG_NAME \
39+
--production-variants VariantName=primary,ModelName=$MODEL_NAME,InitialInstanceCount=1,InstanceType=ml.m5.large,InitialVariantWeight=1
40+
41+
if [ $? -ne 0 ]; then
42+
echo "Failed to create endpoint config"
43+
rm -f temp-model-config.json model.tar.gz
44+
exit 1
45+
fi
46+
47+
# Create endpoint
48+
aws sagemaker create-endpoint \
49+
--region $REGION \
50+
--endpoint-name $ENDPOINT_NAME \
51+
--endpoint-config-name $ENDPOINT_CONFIG_NAME
52+
53+
if [ $? -ne 0 ]; then
54+
echo "Failed to create endpoint"
55+
rm -f temp-model-config.json model.tar.gz
56+
exit 1
57+
fi
58+
59+
echo "Deployment initiated:"
60+
echo "Model: $MODEL_NAME"
61+
echo "Endpoint Config: $ENDPOINT_CONFIG_NAME"
62+
echo "Endpoint: $ENDPOINT_NAME"
63+
64+
# Wait for endpoint to be in service
65+
echo "Waiting for endpoint to be ready..."
66+
aws sagemaker wait endpoint-in-service --region $REGION --endpoint-name $ENDPOINT_NAME
67+
68+
if [ $? -eq 0 ]; then
69+
echo "Endpoint $ENDPOINT_NAME is ready!"
70+
echo "You can now validate with: ./validate_cli.sh $ENDPOINT_NAME"
71+
else
72+
echo "Endpoint deployment failed or timed out"
73+
fi
74+
75+
# Cleanup
76+
rm -f temp-model-config.json model.tar.gz

0 commit comments

Comments
 (0)