parthraninga commited on
Commit
c1f3888
·
verified ·
1 Parent(s): f39d337

Upload 8 files

Browse files
Files changed (8) hide show
  1. .gitignore +1 -0
  2. DEPLOYMENT_GUIDE.md +116 -0
  3. Dockerfile +19 -0
  4. README.md +112 -0
  5. README_HF.md +8 -0
  6. app.py +207 -0
  7. requirements.txt +7 -0
  8. test_api.py +57 -0
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ venv/
DEPLOYMENT_GUIDE.md ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Spaces Deployment Guide
2
+
3
+ ## Steps to Deploy on Hugging Face Spaces
4
+
5
+ ### 1. Create a New Space
6
+ 1. Go to [Hugging Face Spaces](https://huggingface.co/new-space)
7
+ 2. Choose a name for your space (e.g., `content-classifier`)
8
+ 3. Select **Docker** as the SDK
9
+ 4. Set the space to **Public** or **Private** as needed
10
+ 5. Click **Create Space**
11
+
12
+ ### 2. Upload Files to Your Space
13
+
14
+ You need to upload these files to your Space repository:
15
+
16
+ ```
17
+ contextClassifier.onnx # Your ONNX model
18
+ app.py # FastAPI application
19
+ requirements.txt # Python dependencies
20
+ Dockerfile # Docker configuration
21
+ README.md # This will become the Space's README
22
+ ```
23
+
24
+ ### 3. Required Files Content
25
+
26
+ **For the Space's README.md header, add this at the top:**
27
+ ```yaml
28
+ ---
29
+ title: Content Classifier
30
+ emoji: 🔍
31
+ colorFrom: blue
32
+ colorTo: purple
33
+ sdk: docker
34
+ pinned: false
35
+ license: mit
36
+ app_port: 7860
37
+ ---
38
+ ```
39
+
40
+ ### 4. Deployment Process
41
+
42
+ 1. **Via Git (Recommended):**
43
+ ```bash
44
+ git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
45
+ cd YOUR_SPACE_NAME
46
+
47
+ # Copy your files
48
+ copy contextClassifier.onnx .
49
+ copy app.py .
50
+ copy requirements.txt .
51
+ copy Dockerfile .
52
+
53
+ # Commit and push
54
+ git add .
55
+ git commit -m "Add content classifier API"
56
+ git push
57
+ ```
58
+
59
+ 2. **Via Web Interface:**
60
+ - Use the **Files** tab in your Space
61
+ - Upload each file individually
62
+ - Or drag and drop all files at once
63
+
64
+ ### 5. Monitor Deployment
65
+
66
+ 1. Go to your Space URL: `https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME`
67
+ 2. Check the **Logs** tab to monitor the build process
68
+ 3. The Space will show "Building" status during deployment
69
+ 4. Once ready, you'll see the API documentation interface
70
+
71
+ ### 6. Access Your API
72
+
73
+ Once deployed, your API will be available at:
74
+ - **Swagger UI:** `https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space/docs`
75
+ - **API Endpoints:**
76
+ - `POST /predict` - Main prediction endpoint
77
+ - `GET /health` - Health check
78
+ - `GET /model-info` - Model information
79
+
80
+ ### 7. Example Usage
81
+
82
+ ```python
83
+ import requests
84
+
85
+ # Replace with your actual Space URL
86
+ api_url = "https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space"
87
+
88
+ # Make a prediction
89
+ response = requests.post(
90
+ f"{api_url}/predict",
91
+ json={"text": "This is a test message"}
92
+ )
93
+
94
+ print(response.json())
95
+ ```
96
+
97
+ ### 8. Important Notes
98
+
99
+ - **Model Size:** Make sure your `contextClassifier.onnx` file is under the Space's size limit
100
+ - **Cold Start:** The first request might take longer as the Space wakes up
101
+ - **Logs:** Monitor the logs for any runtime errors
102
+ - **Updates:** Any push to the repository will trigger a rebuild
103
+
104
+ ### 9. Troubleshooting
105
+
106
+ **Common Issues:**
107
+ - **Build Fails:** Check logs for dependency issues
108
+ - **Model Not Found:** Ensure `contextClassifier.onnx` is in the root directory
109
+ - **Port Issues:** Make sure the app uses port 7860
110
+ - **Memory Issues:** Large models might exceed memory limits
111
+
112
+ **Solutions:**
113
+ - Review requirements.txt for compatible versions
114
+ - Check model file path in app.py
115
+ - Verify Dockerfile exposes port 7860
116
+ - Consider model optimization for deployment
Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Copy requirements first for better caching
6
+ COPY requirements.txt .
7
+
8
+ # Install dependencies
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+
11
+ # Copy application files
12
+ COPY app.py .
13
+ COPY contextClassifier.onnx .
14
+
15
+ # Expose port
16
+ EXPOSE 7860
17
+
18
+ # Run the application
19
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,3 +1,115 @@
1
  ---
 
 
 
 
 
 
2
  license: mit
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Content Classifier
3
+ emoji: 🔍
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
  license: mit
9
+ app_port: 7860
10
  ---
11
+
12
+ # Content Classifier API
13
+
14
+ A FastAPI-based content classification service using an ONNX model for threat detection and sentiment analysis.
15
+
16
+ ## Features
17
+
18
+ - Content threat classification
19
+ - Sentiment analysis
20
+ - RESTful API with automatic documentation
21
+ - Health check endpoints
22
+ - Model information endpoints
23
+ - Docker support for easy deployment
24
+
25
+ ## API Endpoints
26
+
27
+ - `POST /predict` - Classify text content
28
+ - `GET /` - API status
29
+ - `GET /health` - Health check
30
+ - `GET /model-info` - Model information
31
+ - `GET /docs` - Interactive API documentation (Swagger)
32
+
33
+ ## Installation
34
+
35
+ 1. Install dependencies:
36
+ ```bash
37
+ pip install -r requirements.txt
38
+ ```
39
+
40
+ 2. Run the application:
41
+ ```bash
42
+ python app.py
43
+ ```
44
+
45
+ The API will be available at `http://localhost:8000`
46
+
47
+ ## Usage
48
+
49
+ ### Example Request
50
+
51
+ ```bash
52
+ curl -X POST "http://localhost:8000/predict" \
53
+ -H "Content-Type: application/json" \
54
+ -d '{"text": "This is a sample text to classify"}'
55
+ ```
56
+
57
+ ### Example Response
58
+
59
+ ```json
60
+ {
61
+ "is_threat": false,
62
+ "final_confidence": 0.75,
63
+ "threat_prediction": 0.25,
64
+ "sentiment_analysis": {
65
+ "label": "POSITIVE",
66
+ "score": 0.5
67
+ },
68
+ "onnx_prediction": {
69
+ "threat_probability": 0.25,
70
+ "raw_output": [[0.75, 0.25]]
71
+ },
72
+ "models_used": ["contextClassifier.onnx"],
73
+ "raw_predictions": {
74
+ "onnx": {
75
+ "threat_probability": 0.25,
76
+ "raw_output": [[0.75, 0.25]]
77
+ },
78
+ "sentiment": {
79
+ "label": "POSITIVE",
80
+ "score": 0.5
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ ## Docker Deployment
87
+
88
+ 1. Build the Docker image:
89
+ ```bash
90
+ docker build -t content-classifier .
91
+ ```
92
+
93
+ 2. Run the container:
94
+ ```bash
95
+ docker run -p 8000:8000 content-classifier
96
+ ```
97
+
98
+ ## Hugging Face Spaces Deployment
99
+
100
+ To deploy on Hugging Face Spaces:
101
+
102
+ 1. Create a new Space on Hugging Face
103
+ 2. Upload all files to your Space repository
104
+ 3. The Space will automatically build and deploy
105
+
106
+ ## Model Requirements
107
+
108
+ The ONNX model should accept text inputs and return classification predictions. You may need to adjust the preprocessing and postprocessing functions in `app.py` based on your specific model requirements.
109
+
110
+ ## Configuration
111
+
112
+ You can modify the following in `app.py`:
113
+ - `MODEL_PATH`: Path to your ONNX model file
114
+ - `max_length`: Maximum text length for processing
115
+ - Preprocessing and postprocessing logic based on your model's requirements
README_HF.md ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ title: Content Classifier
2
+ emoji: 🔍
3
+ colorFrom: blue
4
+ colorTo: purple
5
+ sdk: docker
6
+ pinned: false
7
+ license: mit
8
+ app_port: 7860
app.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import onnxruntime as ort
4
+ from fastapi import FastAPI, HTTPException
5
+ from pydantic import BaseModel
6
+ import uvicorn
7
+ import json
8
+ from typing import Dict, Any, List, Optional
9
+
10
+ app = FastAPI(title="Content Classifier API", description="Content classification using ONNX model")
11
+
12
+ # Model configuration
13
+ MODEL_PATH = "contextClassifier.onnx"
14
+ session = None
15
+
16
+ class TextInput(BaseModel):
17
+ text: str
18
+ max_length: Optional[int] = 512
19
+
20
+ class PredictionResponse(BaseModel):
21
+ is_threat: bool
22
+ final_confidence: float
23
+ threat_prediction: float
24
+ sentiment_analysis: Optional[Dict[str, Any]]
25
+ onnx_prediction: Optional[Dict[str, Any]]
26
+ models_used: List[str]
27
+ raw_predictions: Dict[str, Any]
28
+
29
+ def load_model():
30
+ """Load the ONNX model"""
31
+ global session
32
+ try:
33
+ session = ort.InferenceSession(MODEL_PATH)
34
+ print(f"Model loaded successfully from {MODEL_PATH}")
35
+ print(f"Model inputs: {[input.name for input in session.get_inputs()]}")
36
+ print(f"Model outputs: {[output.name for output in session.get_outputs()]}")
37
+ except Exception as e:
38
+ print(f"Error loading model: {e}")
39
+ raise e
40
+
41
+ def preprocess_text(text: str, max_length: int = 512):
42
+ """
43
+ Preprocess text for the model
44
+ This is a placeholder - you'll need to adjust this based on your model's requirements
45
+ """
46
+ # This is a simple tokenization example
47
+ # You may need to use a specific tokenizer depending on your model
48
+
49
+ # Convert text to token IDs (this is just an example)
50
+ # You might need to use transformers tokenizer or similar
51
+ tokens = text.lower().split()[:max_length]
52
+
53
+ # Pad or truncate to fixed length
54
+ if len(tokens) < max_length:
55
+ tokens.extend(['[PAD]'] * (max_length - len(tokens)))
56
+
57
+ # Convert to input format expected by your model
58
+ # This is a placeholder - adjust based on your model's input requirements
59
+ input_ids = np.array([hash(token) % 30000 for token in tokens], dtype=np.int64).reshape(1, -1)
60
+ attention_mask = np.array([1 if token != '[PAD]' else 0 for token in tokens], dtype=np.int64).reshape(1, -1)
61
+
62
+ return {
63
+ "input_ids": input_ids,
64
+ "attention_mask": attention_mask
65
+ }
66
+
67
+ def postprocess_predictions(outputs, predictions_dict):
68
+ """
69
+ Process model outputs into the expected format
70
+ Adjust this based on your model's actual outputs
71
+ """
72
+ # This is a placeholder implementation
73
+ # Adjust based on your actual model outputs
74
+
75
+ # Assuming the model outputs probabilities or logits
76
+ if len(outputs) > 0:
77
+ raw_output = outputs[0]
78
+
79
+ # Calculate threat prediction (adjust logic as needed)
80
+ threat_prediction = float(raw_output[0][1]) if len(raw_output[0]) > 1 else 0.5
81
+ final_confidence = abs(threat_prediction - 0.5) * 2 # Scale to 0-1
82
+ is_threat = threat_prediction > 0.5
83
+
84
+ predictions_dict.update({
85
+ "onnx": {
86
+ "threat_probability": threat_prediction,
87
+ "raw_output": raw_output.tolist()
88
+ }
89
+ })
90
+
91
+ # Mock sentiment analysis (replace with actual logic if available)
92
+ sentiment_score = (threat_prediction - 0.5) * -2 # Inverse relationship
93
+ predictions_dict["sentiment"] = {
94
+ "label": "NEGATIVE" if sentiment_score < 0 else "POSITIVE",
95
+ "score": abs(sentiment_score)
96
+ }
97
+
98
+ models_used = ["contextClassifier.onnx"]
99
+
100
+ return {
101
+ "is_threat": is_threat,
102
+ "final_confidence": final_confidence,
103
+ "threat_prediction": threat_prediction,
104
+ "sentiment_analysis": predictions_dict.get("sentiment"),
105
+ "onnx_prediction": predictions_dict.get("onnx"),
106
+ "models_used": models_used,
107
+ "raw_predictions": predictions_dict
108
+ }
109
+
110
+ # Fallback response
111
+ return {
112
+ "is_threat": False,
113
+ "final_confidence": 0.0,
114
+ "threat_prediction": 0.0,
115
+ "sentiment_analysis": None,
116
+ "onnx_prediction": None,
117
+ "models_used": [],
118
+ "raw_predictions": predictions_dict
119
+ }
120
+
121
+ @app.on_event("startup")
122
+ async def startup_event():
123
+ """Load model on startup"""
124
+ load_model()
125
+
126
+ @app.get("/")
127
+ async def root():
128
+ return {"message": "Content Classifier API is running", "model": MODEL_PATH}
129
+
130
+ @app.post("/predict", response_model=PredictionResponse)
131
+ async def predict(input_data: TextInput):
132
+ """
133
+ Predict content classification for the given text
134
+ """
135
+ if session is None:
136
+ raise HTTPException(status_code=500, detail="Model not loaded")
137
+
138
+ try:
139
+ # Preprocess the text
140
+ model_inputs = preprocess_text(input_data.text, input_data.max_length)
141
+
142
+ # Get input names from the model
143
+ input_names = [input.name for input in session.get_inputs()]
144
+
145
+ # Prepare inputs for ONNX Runtime
146
+ ort_inputs = {}
147
+ for name in input_names:
148
+ if name in model_inputs:
149
+ ort_inputs[name] = model_inputs[name]
150
+ else:
151
+ # Handle case where expected input is not provided
152
+ print(f"Warning: Expected input '{name}' not found in processed inputs")
153
+
154
+ # Run inference
155
+ outputs = session.run(None, ort_inputs)
156
+
157
+ # Initialize predictions dictionary
158
+ predictions = {}
159
+
160
+ # Process outputs
161
+ result = postprocess_predictions(outputs, predictions)
162
+
163
+ return result
164
+
165
+ except Exception as e:
166
+ print(f"Prediction error: {e}")
167
+ raise HTTPException(status_code=500, detail=f"Prediction failed: {str(e)}")
168
+
169
+ @app.get("/health")
170
+ async def health_check():
171
+ """Health check endpoint"""
172
+ return {
173
+ "status": "healthy",
174
+ "model_loaded": session is not None,
175
+ "model_path": MODEL_PATH
176
+ }
177
+
178
+ @app.get("/model-info")
179
+ async def model_info():
180
+ """Get model information"""
181
+ if session is None:
182
+ raise HTTPException(status_code=500, detail="Model not loaded")
183
+
184
+ inputs = []
185
+ for input_meta in session.get_inputs():
186
+ inputs.append({
187
+ "name": input_meta.name,
188
+ "type": str(input_meta.type),
189
+ "shape": input_meta.shape
190
+ })
191
+
192
+ outputs = []
193
+ for output_meta in session.get_outputs():
194
+ outputs.append({
195
+ "name": output_meta.name,
196
+ "type": str(output_meta.type),
197
+ "shape": output_meta.shape
198
+ })
199
+
200
+ return {
201
+ "model_path": MODEL_PATH,
202
+ "inputs": inputs,
203
+ "outputs": outputs
204
+ }
205
+
206
+ if __name__ == "__main__":
207
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn==0.24.0
3
+ onnxruntime==1.16.3
4
+ numpy==1.24.3
5
+ pydantic==2.5.0
6
+ python-multipart==0.0.6
7
+ requests==2.31.0
test_api.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+
4
+ # Test the API
5
+ base_url = "http://localhost:7860"
6
+
7
+ def test_api():
8
+ # Test root endpoint
9
+ print("Testing root endpoint...")
10
+ try:
11
+ response = requests.get(f"{base_url}/")
12
+ print(f"Root: {response.status_code} - {response.json()}")
13
+ except Exception as e:
14
+ print(f"Root endpoint error: {e}")
15
+
16
+ # Test health endpoint
17
+ print("\nTesting health endpoint...")
18
+ try:
19
+ response = requests.get(f"{base_url}/health")
20
+ print(f"Health: {response.status_code} - {response.json()}")
21
+ except Exception as e:
22
+ print(f"Health endpoint error: {e}")
23
+
24
+ # Test model info endpoint
25
+ print("\nTesting model info endpoint...")
26
+ try:
27
+ response = requests.get(f"{base_url}/model-info")
28
+ print(f"Model Info: {response.status_code} - {response.json()}")
29
+ except Exception as e:
30
+ print(f"Model info endpoint error: {e}")
31
+
32
+ # Test prediction endpoint
33
+ print("\nTesting prediction endpoint...")
34
+ test_texts = [
35
+ "This is a normal, safe message.",
36
+ "I will harm you and your family!",
37
+ "Hello, how are you doing today?",
38
+ "This product is amazing, I love it!"
39
+ ]
40
+
41
+ for text in test_texts:
42
+ try:
43
+ payload = {"text": text}
44
+ response = requests.post(f"{base_url}/predict", json=payload)
45
+ if response.status_code == 200:
46
+ result = response.json()
47
+ print(f"\nText: '{text}'")
48
+ print(f"Is Threat: {result['is_threat']}")
49
+ print(f"Confidence: {result['final_confidence']:.3f}")
50
+ print(f"Threat Prediction: {result['threat_prediction']:.3f}")
51
+ else:
52
+ print(f"Error {response.status_code}: {response.text}")
53
+ except Exception as e:
54
+ print(f"Prediction error for '{text}': {e}")
55
+
56
+ if __name__ == "__main__":
57
+ test_api()