Spaces:
Sleeping
Sleeping
viraj
commited on
Commit
·
dacf14a
1
Parent(s):
e5afeb9
Initial Commit
Browse files- .gitignore +14 -0
- FineTuning.ipynb +0 -0
- Makefile +13 -0
- backend/.DS_Store +0 -0
- backend/Dockerfile +10 -0
- backend/README.md +0 -0
- backend/__init__.py +0 -0
- backend/app/__init__.py +1 -0
- backend/app/api/__init__.py +0 -0
- backend/app/api/v1/__init__.py +0 -0
- backend/app/api/v1/config.py +20 -0
- backend/app/api/v1/qa.py +21 -0
- backend/app/api/v1/schemas.py +7 -0
- backend/app/main.py +12 -0
- backend/app/models.py +23 -0
- backend/app/schemas.py +7 -0
- backend/requirements.txt +6 -0
- backend/tests/__init__.py +0 -0
- backend/tests/test_config.py +10 -0
- backend/tests/test_qa.py +11 -0
- docker-compose.yml +22 -0
- frontend/.streamlit/config.toml +4 -0
- frontend/Dockerfile +10 -0
- frontend/README.md +0 -0
- frontend/app.py +23 -0
- frontend/requirements.txt +4 -0
- setup_template.py +52 -0
.gitignore
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/env
|
2 |
+
__pycache__/
|
3 |
+
*.pyc
|
4 |
+
*.pyo
|
5 |
+
*.pyd
|
6 |
+
*.db
|
7 |
+
*.sqlite
|
8 |
+
.env
|
9 |
+
venv/
|
10 |
+
*.log
|
11 |
+
/venv
|
12 |
+
/.venv
|
13 |
+
.venv
|
14 |
+
backend/fine-tuned-model
|
FineTuning.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
Makefile
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Define variables for backend and frontend directories
|
2 |
+
BACKEND_DIR = backend
|
3 |
+
FRONTEND_DIR = frontend
|
4 |
+
|
5 |
+
start-backend:
|
6 |
+
cd $(BACKEND_DIR) && pip3 install -r requirements.txt && uvicorn app.main:app --host 0.0.0.0 --port 8000
|
7 |
+
|
8 |
+
start-frontend:
|
9 |
+
cd $(FRONTEND_DIR) && pip3 install -r requirements.txt && streamlit run app.py
|
10 |
+
|
11 |
+
up:
|
12 |
+
$(MAKE) start-backend &
|
13 |
+
$(MAKE) start-frontend
|
backend/.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
backend/Dockerfile
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.11-slim
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
COPY requirements.txt .
|
6 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
7 |
+
|
8 |
+
COPY app /app
|
9 |
+
|
10 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
backend/README.md
ADDED
File without changes
|
backend/__init__.py
ADDED
File without changes
|
backend/app/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
from .models import predict_sentiment
|
backend/app/api/__init__.py
ADDED
File without changes
|
backend/app/api/v1/__init__.py
ADDED
File without changes
|
backend/app/api/v1/config.py
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, HTTPException
|
2 |
+
from .schemas import ConfigRequest
|
3 |
+
from transformers import AutoModelForQuestionAnswering, AutoTokenizer, pipeline, AutoModelForSequenceClassification
|
4 |
+
from app.models import predict_sentiment
|
5 |
+
|
6 |
+
router = APIRouter()
|
7 |
+
|
8 |
+
model_name = "fine-tuned-model"
|
9 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name, use_safetensors=True)
|
10 |
+
model = AutoModelForSequenceClassification.from_pretrained(model_name)
|
11 |
+
|
12 |
+
@router.post("/")
|
13 |
+
def configure_model(config: ConfigRequest):
|
14 |
+
global model, tokenizer, qa_pipeline
|
15 |
+
try:
|
16 |
+
model = AutoModelForQuestionAnswering.from_pretrained(config.model_name)
|
17 |
+
tokenizer = AutoTokenizer.from_pretrained(config.model_name)
|
18 |
+
return {"message": f"Model loaded successfully: {config.model_name}"}
|
19 |
+
except Exception as e:
|
20 |
+
raise HTTPException(status_code=500, detail=str(e))
|
backend/app/api/v1/qa.py
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, HTTPException
|
2 |
+
from .schemas import QARequest
|
3 |
+
from transformers import AutoModelForSequenceClassification
|
4 |
+
from transformers import AutoModelForQuestionAnswering, AutoTokenizer
|
5 |
+
from app.models import predict_sentiment
|
6 |
+
|
7 |
+
router = APIRouter()
|
8 |
+
|
9 |
+
model_name = "fine-tuned-model"
|
10 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name, use_safetensors=True)
|
11 |
+
model = AutoModelForSequenceClassification.from_pretrained(model_name)
|
12 |
+
|
13 |
+
@router.post("/")
|
14 |
+
def get_answer(request: QARequest):
|
15 |
+
try:
|
16 |
+
sentiment, probs = predict_sentiment(request.command)
|
17 |
+
probs = [float(prob) for prob in probs]
|
18 |
+
print({"review": request.command, "sentiment": sentiment, "probs": probs})
|
19 |
+
return {"review": request.command, "sentiment": sentiment, "probs": probs}
|
20 |
+
except Exception as e:
|
21 |
+
raise HTTPException(status_code=500, detail=str(e))
|
backend/app/api/v1/schemas.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel
|
2 |
+
|
3 |
+
class QARequest(BaseModel):
|
4 |
+
command: str
|
5 |
+
|
6 |
+
class ConfigRequest(BaseModel):
|
7 |
+
model_name: str
|
backend/app/main.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from .api.v1 import qa, config
|
3 |
+
|
4 |
+
app = FastAPI()
|
5 |
+
|
6 |
+
# Include routers for the API
|
7 |
+
app.include_router(qa.router, prefix="/qa", tags=["qa"])
|
8 |
+
app.include_router(config.router, prefix="/config", tags=["config"])
|
9 |
+
|
10 |
+
if __name__ == "__main__":
|
11 |
+
import uvicorn
|
12 |
+
uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
|
backend/app/models.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
2 |
+
import torch
|
3 |
+
import os
|
4 |
+
|
5 |
+
model_name = "fine-tuned-model"
|
6 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name, use_safetensors=True)
|
7 |
+
model = AutoModelForSequenceClassification.from_pretrained(model_name)
|
8 |
+
|
9 |
+
device = torch.device("cpu")
|
10 |
+
|
11 |
+
def predict_sentiment(review_text):
|
12 |
+
inputs = tokenizer(review_text, padding=True, truncation=True, return_tensors="pt").to(device)
|
13 |
+
|
14 |
+
with torch.no_grad():
|
15 |
+
outputs = model(**inputs)
|
16 |
+
|
17 |
+
logits = outputs.logits
|
18 |
+
predictions = torch.softmax(logits, dim=-1)
|
19 |
+
|
20 |
+
predicted_label = torch.argmax(predictions, dim=-1).item()
|
21 |
+
sentiment = "Positive" if predicted_label == 1 else "Negative"
|
22 |
+
|
23 |
+
return sentiment, predictions[0].cpu().numpy()
|
backend/app/schemas.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel
|
2 |
+
|
3 |
+
class QARequest(BaseModel):
|
4 |
+
command: str
|
5 |
+
|
6 |
+
class ConfigRequest(BaseModel):
|
7 |
+
model_name: str
|
backend/requirements.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
fastapi==0.95.2
|
2 |
+
uvicorn==0.22.0
|
3 |
+
transformers==4.34.0
|
4 |
+
torch==2.3.1
|
5 |
+
pydantic==1.10.7
|
6 |
+
requests==2.31.0
|
backend/tests/__init__.py
ADDED
File without changes
|
backend/tests/test_config.py
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pytest
|
2 |
+
from fastapi.testclient import TestClient
|
3 |
+
from backend.app.main import app
|
4 |
+
|
5 |
+
client = TestClient(app)
|
6 |
+
|
7 |
+
def test_configure_model():
|
8 |
+
response = client.post("/config/", json={"model_name": "distilbert-base-uncased-distilled-squad"})
|
9 |
+
assert response.status_code == 200
|
10 |
+
assert "message" in response.json()
|
backend/tests/test_qa.py
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pytest
|
2 |
+
from fastapi.testclient import TestClient
|
3 |
+
from backend.app.main import app
|
4 |
+
|
5 |
+
client = TestClient(app)
|
6 |
+
|
7 |
+
def test_get_answer():
|
8 |
+
response = client.post("/qa/", json={"context": "Hello world", "question": "What is this?"})
|
9 |
+
assert response.status_code == 200
|
10 |
+
assert "answer" in response.json()
|
11 |
+
assert "score" in response.json()
|
docker-compose.yml
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: '3.11'
|
2 |
+
|
3 |
+
services:
|
4 |
+
backend:
|
5 |
+
build:
|
6 |
+
context: ./backend
|
7 |
+
ports:
|
8 |
+
- "8000:8000"
|
9 |
+
volumes:
|
10 |
+
- ./backend:/app
|
11 |
+
|
12 |
+
frontend:
|
13 |
+
build:
|
14 |
+
context: ./frontend
|
15 |
+
ports:
|
16 |
+
- "8501:8501"
|
17 |
+
volumes:
|
18 |
+
- ./frontend:/app
|
19 |
+
environment:
|
20 |
+
- API_URL=http://0.0.0.0:8000
|
21 |
+
depends_on:
|
22 |
+
- backend
|
frontend/.streamlit/config.toml
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[theme]
|
2 |
+
base="dark"
|
3 |
+
primaryColor="#00b4ff"
|
4 |
+
backgroundColor="#000000"
|
frontend/Dockerfile
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.11-slim
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
COPY requirements.txt .
|
6 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
7 |
+
|
8 |
+
COPY app.py /app
|
9 |
+
|
10 |
+
CMD ["streamlit", "run", "app.py", "--server.port", "8501"]
|
frontend/README.md
ADDED
File without changes
|
frontend/app.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import requests
|
3 |
+
|
4 |
+
API_URL = "http://localhost:8000"
|
5 |
+
|
6 |
+
st.title("BERT Fine Tuned Model on IMBD dataset")
|
7 |
+
|
8 |
+
st.header("try with reviews")
|
9 |
+
question = st.text_input("Review")
|
10 |
+
|
11 |
+
if st.button("Get Answer"):
|
12 |
+
if question:
|
13 |
+
print(f"{API_URL}/qa/")
|
14 |
+
response = requests.post(f"{API_URL}/qa/", json={"command": question})
|
15 |
+
if response.status_code == 200:
|
16 |
+
answer = response.json()
|
17 |
+
st.write(f"**Review**: {answer['review']}")
|
18 |
+
st.write(f"**Sentiment**: {answer['sentiment']}")
|
19 |
+
st.write(f"**Probs**: {answer['probs']}")
|
20 |
+
else:
|
21 |
+
st.error(f"Error: {response.json().get('detail', 'Unknown error')}")
|
22 |
+
else:
|
23 |
+
st.error("Please provide both context and question.")
|
frontend/requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit==1.37.0
|
2 |
+
pandas==2.2.2
|
3 |
+
requests==2.31.0
|
4 |
+
|
setup_template.py
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from pathlib import Path
|
3 |
+
import logging
|
4 |
+
|
5 |
+
logging.basicConfig(
|
6 |
+
level=logging.INFO,
|
7 |
+
format='[%(asctime)s]: %(message)s',
|
8 |
+
)
|
9 |
+
|
10 |
+
project_name = 'my_project'
|
11 |
+
|
12 |
+
list_of_files = [
|
13 |
+
# Backend
|
14 |
+
f"backend/app/__init__.py",
|
15 |
+
f"backend/app/main.py",
|
16 |
+
f"backend/app/models.py",
|
17 |
+
f"backend/app/schemas.py",
|
18 |
+
f"backend/app/api/__init__.py",
|
19 |
+
f"backend/app/api/v1/__init__.py",
|
20 |
+
f"backend/app/api/v1/qa.py",
|
21 |
+
f"backend/app/api/v1/config.py",
|
22 |
+
f"backend/tests/__init__.py",
|
23 |
+
f"backend/tests/test_qa.py",
|
24 |
+
f"backend/tests/test_config.py",
|
25 |
+
f"backend/Dockerfile",
|
26 |
+
f"backend/requirements.txt",
|
27 |
+
f"backend/README.md",
|
28 |
+
|
29 |
+
# Frontend
|
30 |
+
f"frontend/app.py",
|
31 |
+
f"frontend/Dockerfile",
|
32 |
+
f"frontend/requirements.txt",
|
33 |
+
f"frontend/README.md",
|
34 |
+
|
35 |
+
# Root
|
36 |
+
".gitignore",
|
37 |
+
"README.md"
|
38 |
+
]
|
39 |
+
|
40 |
+
for path in list_of_files:
|
41 |
+
file_path = Path(path)
|
42 |
+
filedir, filename = os.path.split(file_path)
|
43 |
+
|
44 |
+
if filedir != "":
|
45 |
+
os.makedirs(filedir, exist_ok=True)
|
46 |
+
logging.info(f"Created directory {filedir}")
|
47 |
+
if (not os.path.exists(file_path)) or (os.path.getsize(file_path) == 0):
|
48 |
+
with open(file_path, "w") as f:
|
49 |
+
pass
|
50 |
+
logging.info(f"Created file {filename}")
|
51 |
+
else:
|
52 |
+
logging.info(f"File {filename} already exists")
|