Emmanuel Frimpong Asante
commited on
Commit
·
b6da652
1
Parent(s):
529f412
update space
Browse files- .env +5 -0
- app.py +1 -9
- services/disease_detection_service.py +76 -27
.env
CHANGED
@@ -13,3 +13,8 @@ FROM_EMAIL="your_from_email_address"
|
|
13 |
# TensorFlow Settings
|
14 |
TF_ENABLE_ONEDNN_OPTS=0
|
15 |
TF_FORCE_GPU_ALLOW_GROWTH=true
|
|
|
|
|
|
|
|
|
|
|
|
13 |
# TensorFlow Settings
|
14 |
TF_ENABLE_ONEDNN_OPTS=0
|
15 |
TF_FORCE_GPU_ALLOW_GROWTH=true
|
16 |
+
|
17 |
+
|
18 |
+
# MODELS
|
19 |
+
DISEASE_MODEL_PATH=models/Final_Chicken_disease_model.h5
|
20 |
+
LLAMA_MODEL_NAME=meta-llama/Llama-3.2-1B
|
app.py
CHANGED
@@ -19,12 +19,6 @@ from huggingface_hub import login
|
|
19 |
logging.basicConfig(level=logging.INFO)
|
20 |
logger = logging.getLogger(__name__)
|
21 |
|
22 |
-
# Load Hugging Face Token
|
23 |
-
HF_TOKEN = os.environ.get('HF_Token')
|
24 |
-
if HF_TOKEN:
|
25 |
-
login(token=HF_TOKEN, add_to_git_credential=True)
|
26 |
-
else:
|
27 |
-
logger.warning("Hugging Face token not found in environment variables.")
|
28 |
|
29 |
# Check GPU availability for TensorFlow
|
30 |
gpu_devices = tf.config.list_physical_devices('GPU')
|
@@ -46,9 +40,7 @@ else:
|
|
46 |
logger.error("Static directory not found.")
|
47 |
raise HTTPException(status_code=500, detail="Static directory not found.")
|
48 |
|
49 |
-
|
50 |
-
disease_model = load_disease_model()
|
51 |
-
llama_model, llama_tokenizer = load_llama_model()
|
52 |
|
53 |
# Include routers for authentication, disease detection, and health dashboard
|
54 |
app.include_router(auth_router, prefix="/auth", tags=["Authentication"])
|
|
|
19 |
logging.basicConfig(level=logging.INFO)
|
20 |
logger = logging.getLogger(__name__)
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
# Check GPU availability for TensorFlow
|
24 |
gpu_devices = tf.config.list_physical_devices('GPU')
|
|
|
40 |
logger.error("Static directory not found.")
|
41 |
raise HTTPException(status_code=500, detail="Static directory not found.")
|
42 |
|
43 |
+
|
|
|
|
|
44 |
|
45 |
# Include routers for authentication, disease detection, and health dashboard
|
46 |
app.include_router(auth_router, prefix="/auth", tags=["Authentication"])
|
services/disease_detection_service.py
CHANGED
@@ -9,25 +9,44 @@ import numpy as np
|
|
9 |
from huggingface_hub import login
|
10 |
from pymongo import MongoClient
|
11 |
from transformers import TFAutoModelForCausalLM, AutoTokenizer
|
|
|
12 |
|
13 |
# Logging setup
|
14 |
logging.basicConfig(level=logging.INFO)
|
15 |
logger = logging.getLogger(__name__)
|
16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
# Load environment variables
|
18 |
MONGO_URI = os.getenv("MONGO_URI")
|
19 |
db_client = MongoClient(MONGO_URI)
|
20 |
db = db_client.poultry_farm # MongoDB for record-keeping
|
21 |
|
|
|
|
|
|
|
|
|
22 |
# Mixed precision and GPU configuration
|
23 |
gpu_devices = tf.config.list_physical_devices('GPU')
|
24 |
if gpu_devices:
|
25 |
for gpu in gpu_devices:
|
26 |
-
|
|
|
27 |
from tensorflow.keras import mixed_precision
|
28 |
-
|
29 |
-
|
30 |
-
|
|
|
|
|
|
|
|
|
|
|
31 |
else:
|
32 |
logger.info("Using CPU without mixed precision.")
|
33 |
|
@@ -35,23 +54,29 @@ else:
|
|
35 |
def load_disease_model():
|
36 |
"""Load the disease detection model."""
|
37 |
try:
|
38 |
-
|
|
|
|
|
39 |
device = '/GPU:0' if gpu_devices else '/CPU:0'
|
40 |
with tf.device(device):
|
41 |
model = load_model(model_path, compile=True)
|
42 |
logger.info(f"Disease detection model loaded successfully on {device}.")
|
43 |
return model
|
44 |
except Exception as e:
|
45 |
-
|
46 |
-
|
|
|
47 |
|
48 |
def load_llama_model():
|
49 |
"""Load the Llama 3.2 model for text generation using TensorFlow."""
|
50 |
try:
|
51 |
-
|
|
|
|
|
52 |
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
53 |
model = TFAutoModelForCausalLM.from_pretrained(model_name)
|
54 |
|
|
|
55 |
if tokenizer.pad_token is None:
|
56 |
tokenizer.add_special_tokens({'pad_token': '[PAD]'})
|
57 |
model.resize_token_embeddings(len(tokenizer))
|
@@ -59,11 +84,16 @@ def load_llama_model():
|
|
59 |
logger.info("Llama 3.2 model loaded successfully with TensorFlow.")
|
60 |
return model, tokenizer
|
61 |
except Exception as e:
|
62 |
-
|
63 |
-
|
|
|
64 |
|
|
|
|
|
|
|
65 |
|
66 |
# Disease mapping and treatment guidelines
|
|
|
67 |
name_disease = {0: 'Coccidiosis', 1: 'Healthy', 2: 'New Castle Disease', 3: 'Salmonella'}
|
68 |
status_map = {0: 'Critical', 1: 'No issue', 2: 'Critical', 3: 'Critical'}
|
69 |
recommendations = {
|
@@ -75,66 +105,85 @@ recommendations = {
|
|
75 |
|
76 |
class PoultryFarmBot:
|
77 |
def __init__(self, db):
|
|
|
78 |
self.db = db
|
79 |
|
80 |
-
def preprocess_image(self, image):
|
81 |
"""Preprocess image for model input."""
|
82 |
try:
|
|
|
|
|
|
|
|
|
83 |
image = cv2.resize(image, (224, 224))
|
84 |
-
|
|
|
85 |
logger.info("Image preprocessed successfully.")
|
86 |
return image
|
87 |
except Exception as e:
|
88 |
-
|
89 |
-
|
|
|
90 |
|
91 |
-
def predict_disease(self, image):
|
92 |
"""Predict the disease from a preprocessed image and return detailed information."""
|
93 |
-
preprocessed_image = self.preprocess_image(image)
|
94 |
-
if preprocessed_image is None:
|
95 |
-
return "Image preprocessing failed.", None, None, None
|
96 |
-
|
97 |
try:
|
|
|
|
|
|
|
98 |
prediction = disease_model.predict(preprocessed_image)
|
|
|
99 |
predicted_class = np.argmax(prediction, axis=1).item()
|
|
|
100 |
disease_name = name_disease.get(predicted_class, "Unknown disease")
|
101 |
disease_status = status_map.get(predicted_class, "Unknown status")
|
102 |
recommendation = recommendations.get(predicted_class, "No recommendation available")
|
|
|
103 |
confidence = prediction[0][predicted_class] * 100
|
104 |
logger.info(f"Predicted: {disease_name} with {confidence:.2f}% confidence.")
|
105 |
|
106 |
-
# Generate detailed response using Llama 3.2
|
107 |
detailed_response = self.generate_detailed_response(disease_name, disease_status, recommendation)
|
108 |
return detailed_response, disease_name, disease_status, recommendation, confidence
|
109 |
except Exception as e:
|
110 |
-
|
|
|
111 |
return "Prediction failed.", None, None, None, None
|
112 |
|
113 |
-
def generate_detailed_response(self, disease_name, status, recommendation):
|
114 |
"""Generate a detailed response using Llama 3.2 model for additional disease insights."""
|
|
|
115 |
prompt = (
|
116 |
f"The disease detected is {disease_name}, classified as {status}. "
|
117 |
f"Recommended action: {recommendation}. "
|
118 |
f"Here is more information about {disease_name}, including its causes, symptoms, and treatments "
|
119 |
"for effective management on a poultry farm."
|
120 |
)
|
|
|
121 |
response = self.llama_response(prompt)
|
|
|
122 |
return response.replace(prompt, "").strip()
|
123 |
|
124 |
-
def llama_response(self, prompt):
|
125 |
"""Generate a response from the Llama 3.2 model based on a given prompt."""
|
126 |
try:
|
127 |
-
|
128 |
-
|
|
|
|
|
|
|
129 |
return llama_tokenizer.decode(outputs[0], skip_special_tokens=True)
|
130 |
except Exception as e:
|
131 |
-
|
|
|
132 |
return "Error generating detailed response."
|
133 |
|
134 |
-
def diagnose_and_respond(self, image):
|
135 |
"""Diagnose the disease and respond with detailed information and recommendations."""
|
|
|
136 |
if image is None or image.size == 0:
|
137 |
return "Please provide a valid poultry fecal image.", None, None, None, None
|
|
|
138 |
return self.predict_disease(image)
|
139 |
|
140 |
# Initialize the bot instance
|
|
|
9 |
from huggingface_hub import login
|
10 |
from pymongo import MongoClient
|
11 |
from transformers import TFAutoModelForCausalLM, AutoTokenizer
|
12 |
+
import configparser
|
13 |
|
14 |
# Logging setup
|
15 |
logging.basicConfig(level=logging.INFO)
|
16 |
logger = logging.getLogger(__name__)
|
17 |
|
18 |
+
# Load Hugging Face Token
|
19 |
+
HF_TOKEN = os.environ.get('HF_Token')
|
20 |
+
if HF_TOKEN:
|
21 |
+
login(token=HF_TOKEN, add_to_git_credential=True)
|
22 |
+
else:
|
23 |
+
logger.warning("Hugging Face token not found in environment variables.")
|
24 |
+
|
25 |
+
|
26 |
# Load environment variables
|
27 |
MONGO_URI = os.getenv("MONGO_URI")
|
28 |
db_client = MongoClient(MONGO_URI)
|
29 |
db = db_client.poultry_farm # MongoDB for record-keeping
|
30 |
|
31 |
+
# Load configuration file
|
32 |
+
config = configparser.ConfigParser()
|
33 |
+
config.read('config.ini')
|
34 |
+
|
35 |
# Mixed precision and GPU configuration
|
36 |
gpu_devices = tf.config.list_physical_devices('GPU')
|
37 |
if gpu_devices:
|
38 |
for gpu in gpu_devices:
|
39 |
+
# Enable memory growth for each GPU to avoid allocating all memory at once
|
40 |
+
tf.config.set_memory_growth(gpu, True)
|
41 |
from tensorflow.keras import mixed_precision
|
42 |
+
# Check if GPU supports mixed precision (compute capability >= 7)
|
43 |
+
gpu_details = tf.config.experimental.get_device_details(gpu_devices[0])
|
44 |
+
if gpu_details.get('compute_capability', (0, 0))[0] >= 7:
|
45 |
+
policy = mixed_precision.Policy('mixed_float16')
|
46 |
+
mixed_precision.set_global_policy(policy)
|
47 |
+
logger.info("Using mixed precision with GPU.")
|
48 |
+
else:
|
49 |
+
logger.info("GPU detected but does not support mixed precision.")
|
50 |
else:
|
51 |
logger.info("Using CPU without mixed precision.")
|
52 |
|
|
|
54 |
def load_disease_model():
|
55 |
"""Load the disease detection model."""
|
56 |
try:
|
57 |
+
# Load the model path from the configuration file or use the default path
|
58 |
+
model_path = config.get('MODELS', 'DISEASE_MODEL_PATH', fallback="models/Final_Chicken_disease_model.h5")
|
59 |
+
# Use GPU if available, otherwise use CPU
|
60 |
device = '/GPU:0' if gpu_devices else '/CPU:0'
|
61 |
with tf.device(device):
|
62 |
model = load_model(model_path, compile=True)
|
63 |
logger.info(f"Disease detection model loaded successfully on {device}.")
|
64 |
return model
|
65 |
except Exception as e:
|
66 |
+
# Log the error and raise an exception to indicate failure
|
67 |
+
logger.error(f"Error loading disease model: {e}", exc_info=True)
|
68 |
+
raise RuntimeError("Failed to load disease detection model") from e
|
69 |
|
70 |
def load_llama_model():
|
71 |
"""Load the Llama 3.2 model for text generation using TensorFlow."""
|
72 |
try:
|
73 |
+
# Load the model name from environment variable or use the default model
|
74 |
+
model_name = os.getenv("LLAMA_MODEL_NAME", "meta-llama/Llama-3.2-1B")
|
75 |
+
# Load the tokenizer and model from the Hugging Face hub
|
76 |
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
77 |
model = TFAutoModelForCausalLM.from_pretrained(model_name)
|
78 |
|
79 |
+
# Add padding token if not already present
|
80 |
if tokenizer.pad_token is None:
|
81 |
tokenizer.add_special_tokens({'pad_token': '[PAD]'})
|
82 |
model.resize_token_embeddings(len(tokenizer))
|
|
|
84 |
logger.info("Llama 3.2 model loaded successfully with TensorFlow.")
|
85 |
return model, tokenizer
|
86 |
except Exception as e:
|
87 |
+
# Log the error and raise an exception to indicate failure
|
88 |
+
logger.error(f"Error loading Llama model: {e}", exc_info=True)
|
89 |
+
raise RuntimeError("Failed to load Llama model") from e
|
90 |
|
91 |
+
# Load models at startup
|
92 |
+
disease_model = load_disease_model()
|
93 |
+
llama_model, llama_tokenizer = load_llama_model()
|
94 |
|
95 |
# Disease mapping and treatment guidelines
|
96 |
+
# Maps predicted class indices to disease names, status, and treatment recommendations
|
97 |
name_disease = {0: 'Coccidiosis', 1: 'Healthy', 2: 'New Castle Disease', 3: 'Salmonella'}
|
98 |
status_map = {0: 'Critical', 1: 'No issue', 2: 'Critical', 3: 'Critical'}
|
99 |
recommendations = {
|
|
|
105 |
|
106 |
class PoultryFarmBot:
|
107 |
def __init__(self, db):
|
108 |
+
# Initialize the bot with a reference to the MongoDB database
|
109 |
self.db = db
|
110 |
|
111 |
+
def preprocess_image(self, image: np.ndarray) -> np.ndarray:
|
112 |
"""Preprocess image for model input."""
|
113 |
try:
|
114 |
+
# Check if the image is valid
|
115 |
+
if image is None or image.size == 0:
|
116 |
+
raise ValueError("Empty or invalid image provided.")
|
117 |
+
# Resize the image to the input size required by the model (224x224)
|
118 |
image = cv2.resize(image, (224, 224))
|
119 |
+
# Normalize the image (scaling pixel values to range [0, 1]) and add a batch dimension
|
120 |
+
image = np.expand_dims(image, axis=0) / 255.0
|
121 |
logger.info("Image preprocessed successfully.")
|
122 |
return image
|
123 |
except Exception as e:
|
124 |
+
# Log the error and raise an exception to indicate failure
|
125 |
+
logger.error(f"Error in image preprocessing: {e}", exc_info=True)
|
126 |
+
raise
|
127 |
|
128 |
+
def predict_disease(self, image: np.ndarray):
|
129 |
"""Predict the disease from a preprocessed image and return detailed information."""
|
|
|
|
|
|
|
|
|
130 |
try:
|
131 |
+
# Preprocess the input image for prediction
|
132 |
+
preprocessed_image = self.preprocess_image(image)
|
133 |
+
# Make a prediction using the disease detection model
|
134 |
prediction = disease_model.predict(preprocessed_image)
|
135 |
+
# Get the predicted class index (the class with the highest probability)
|
136 |
predicted_class = np.argmax(prediction, axis=1).item()
|
137 |
+
# Map the predicted class to the disease name, status, and recommendation
|
138 |
disease_name = name_disease.get(predicted_class, "Unknown disease")
|
139 |
disease_status = status_map.get(predicted_class, "Unknown status")
|
140 |
recommendation = recommendations.get(predicted_class, "No recommendation available")
|
141 |
+
# Calculate the confidence of the prediction (as a percentage)
|
142 |
confidence = prediction[0][predicted_class] * 100
|
143 |
logger.info(f"Predicted: {disease_name} with {confidence:.2f}% confidence.")
|
144 |
|
145 |
+
# Generate a detailed response using Llama 3.2 for additional insights
|
146 |
detailed_response = self.generate_detailed_response(disease_name, disease_status, recommendation)
|
147 |
return detailed_response, disease_name, disease_status, recommendation, confidence
|
148 |
except Exception as e:
|
149 |
+
# Log the error and return failure information
|
150 |
+
logger.error(f"Prediction error: {e}", exc_info=True)
|
151 |
return "Prediction failed.", None, None, None, None
|
152 |
|
153 |
+
def generate_detailed_response(self, disease_name: str, status: str, recommendation: str) -> str:
|
154 |
"""Generate a detailed response using Llama 3.2 model for additional disease insights."""
|
155 |
+
# Construct a prompt for the Llama model with detailed information about the disease
|
156 |
prompt = (
|
157 |
f"The disease detected is {disease_name}, classified as {status}. "
|
158 |
f"Recommended action: {recommendation}. "
|
159 |
f"Here is more information about {disease_name}, including its causes, symptoms, and treatments "
|
160 |
"for effective management on a poultry farm."
|
161 |
)
|
162 |
+
# Generate a response using the Llama model based on the constructed prompt
|
163 |
response = self.llama_response(prompt)
|
164 |
+
# Remove the prompt text from the response (if included) and return it
|
165 |
return response.replace(prompt, "").strip()
|
166 |
|
167 |
+
def llama_response(self, prompt: str) -> str:
|
168 |
"""Generate a response from the Llama 3.2 model based on a given prompt."""
|
169 |
try:
|
170 |
+
# Tokenize the prompt and prepare it for the Llama model
|
171 |
+
inputs = llama_tokenizer(prompt, return_tensors="tf", max_length=200, truncation=True, padding=True)
|
172 |
+
# Generate a response using the Llama model with sampling options for variety
|
173 |
+
outputs = llama_model.generate(inputs["input_ids"], max_length=200, do_sample=True, temperature=0.7, top_p=0.9)
|
174 |
+
# Decode the generated output to obtain the response text
|
175 |
return llama_tokenizer.decode(outputs[0], skip_special_tokens=True)
|
176 |
except Exception as e:
|
177 |
+
# Log the error and return an error message indicating failure
|
178 |
+
logger.error(f"Llama model response error: {e}", exc_info=True)
|
179 |
return "Error generating detailed response."
|
180 |
|
181 |
+
def diagnose_and_respond(self, image: np.ndarray):
|
182 |
"""Diagnose the disease and respond with detailed information and recommendations."""
|
183 |
+
# Check if the provided image is valid (not None and has a non-zero size)
|
184 |
if image is None or image.size == 0:
|
185 |
return "Please provide a valid poultry fecal image.", None, None, None, None
|
186 |
+
# Predict the disease using the disease detection model and return the response
|
187 |
return self.predict_disease(image)
|
188 |
|
189 |
# Initialize the bot instance
|