Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,67 +1,233 @@
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
|
|
|
|
2 |
import requests
|
3 |
import json
|
4 |
-
import random
|
5 |
import os
|
6 |
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
13 |
|
14 |
-
|
|
|
15 |
"""
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
"""
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
"pest_outbreak": pest_outbreak
|
28 |
-
}
|
|
|
29 |
|
30 |
-
|
|
|
31 |
"""
|
32 |
-
|
|
|
|
|
|
|
33 |
"""
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
)
|
|
|
|
|
39 |
|
40 |
-
|
41 |
-
"
|
42 |
-
|
43 |
-
"
|
44 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
)
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
endpoint = "https://api.openai.com/v1/chat/completions"
|
48 |
headers = {
|
49 |
"Content-Type": "application/json",
|
50 |
-
"Authorization": f"Bearer {
|
51 |
}
|
52 |
data = {
|
53 |
-
"model": "gpt-3.5-turbo", # or
|
54 |
"messages": [
|
55 |
-
{"role": "system", "content": "You are a helpful
|
56 |
-
{"role": "user", "content":
|
57 |
],
|
58 |
-
"max_tokens":
|
59 |
"temperature": 0.7
|
60 |
}
|
61 |
|
62 |
try:
|
63 |
-
|
64 |
-
result =
|
65 |
if "choices" in result and len(result["choices"]) > 0:
|
66 |
return result["choices"][0]["message"]["content"].strip()
|
67 |
else:
|
@@ -69,33 +235,102 @@ def ask_gpt(analysis):
|
|
69 |
except Exception as e:
|
70 |
return f"Error calling GPT: {e}"
|
71 |
|
|
|
|
|
|
|
|
|
72 |
def main():
|
73 |
-
st.title("Farm AI + GPT")
|
74 |
-
st.markdown("
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
with st.form("farm_form"):
|
78 |
-
soil_moisture = st.
|
79 |
-
soil_ph = st.
|
80 |
-
temperature = st.
|
81 |
-
humidity = st.
|
82 |
-
crop_health = st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
-
submitted = st.form_submit_button("Analyze")
|
85 |
|
86 |
if submitted:
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
if __name__ == "__main__":
|
101 |
main()
|
|
|
1 |
+
##############################################
|
2 |
+
# app.py - Near-Perfect Accuracy Farm AI Demo
|
3 |
+
##############################################
|
4 |
+
|
5 |
import streamlit as st
|
6 |
+
import numpy as np
|
7 |
+
import pandas as pd
|
8 |
import requests
|
9 |
import json
|
|
|
10 |
import os
|
11 |
|
12 |
+
from sklearn.ensemble import RandomForestClassifier, GradientBoostingRegressor
|
13 |
+
from sklearn.linear_model import LogisticRegression
|
14 |
+
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
|
15 |
+
from sklearn.pipeline import Pipeline
|
16 |
+
from sklearn.model_selection import train_test_split
|
17 |
+
from sklearn.metrics import accuracy_score, mean_squared_error
|
18 |
+
|
19 |
+
################################################################################
|
20 |
+
# 1) SYNTHETIC DATA GENERATION WITH NEAR-PERFECT RELATIONSHIPS
|
21 |
+
################################################################################
|
22 |
|
23 |
+
@st.cache_data
|
24 |
+
def generate_pest_data(n=200):
|
25 |
"""
|
26 |
+
Generates synthetic data for pest outbreak classification with a strong,
|
27 |
+
easily learnable relationship so that the model can achieve near 100% accuracy.
|
28 |
+
Features: temperature, humidity, leaf_wetness
|
29 |
+
Target: pest_outbreak (0 or 1)
|
30 |
"""
|
31 |
+
|
32 |
+
# For reproducibility
|
33 |
+
np.random.seed(42)
|
34 |
+
|
35 |
+
# We'll create a 'perfect' or near-perfect relationship:
|
36 |
+
# pest_score = 0.3*temperature + 0.5*humidity + 0.1*leaf_wetness
|
37 |
+
# Then threshold around the median for 0/1.
|
38 |
+
|
39 |
+
temperature = np.random.uniform(15, 40, n)
|
40 |
+
humidity = np.random.uniform(30, 90, n)
|
41 |
+
leaf_wetness = np.random.uniform(0, 100, n)
|
42 |
+
|
43 |
+
pest_score = 0.3 * temperature + 0.5 * humidity + 0.1 * leaf_wetness
|
44 |
+
median_score = np.median(pest_score)
|
45 |
+
pest_outbreak = (pest_score >= median_score).astype(int)
|
46 |
+
|
47 |
+
df = pd.DataFrame({
|
48 |
+
"temperature": temperature,
|
49 |
+
"humidity": humidity,
|
50 |
+
"leaf_wetness": leaf_wetness,
|
51 |
"pest_outbreak": pest_outbreak
|
52 |
+
})
|
53 |
+
return df
|
54 |
|
55 |
+
@st.cache_data
|
56 |
+
def generate_disease_data(n=200):
|
57 |
"""
|
58 |
+
Synthetic data for disease detection classification with near-perfect relationship.
|
59 |
+
We'll use a polynomial link so the model can memorize it well.
|
60 |
+
Features: soil_pH, rainfall, planting_density
|
61 |
+
Target: disease_present (0 or 1)
|
62 |
"""
|
63 |
+
|
64 |
+
np.random.seed(123)
|
65 |
+
|
66 |
+
soil_ph = np.random.uniform(5.0, 8.0, n)
|
67 |
+
rainfall = np.random.uniform(0, 200, n)
|
68 |
+
planting_density = np.random.uniform(50, 200, n)
|
69 |
+
|
70 |
+
# 'Perfect' polynomial relationship:
|
71 |
+
# disease_score = -(soil_ph - 6.5)^2 + 0.02*rainfall + 0.006*planting_density
|
72 |
+
# Then threshold around median for 0/1
|
73 |
+
|
74 |
+
disease_score = (
|
75 |
+
-1.0 * (soil_ph - 6.5)**2
|
76 |
+
+ 0.02 * rainfall
|
77 |
+
+ 0.006 * planting_density
|
78 |
)
|
79 |
+
median_score = np.median(disease_score)
|
80 |
+
disease_present = (disease_score >= median_score).astype(int)
|
81 |
|
82 |
+
df = pd.DataFrame({
|
83 |
+
"soil_ph": soil_ph,
|
84 |
+
"rainfall": rainfall,
|
85 |
+
"planting_density": planting_density,
|
86 |
+
"disease_present": disease_present
|
87 |
+
})
|
88 |
+
return df
|
89 |
+
|
90 |
+
@st.cache_data
|
91 |
+
def generate_yield_data(n=200):
|
92 |
+
"""
|
93 |
+
Synthetic data for yield regression with near-perfect correlation.
|
94 |
+
We'll ensure a strong linear relationship so the model can achieve very low error.
|
95 |
+
Features: soil_fertility, temperature, irrigation_freq, fertilizer_score
|
96 |
+
Target: crop_yield (tons/ha)
|
97 |
+
"""
|
98 |
+
|
99 |
+
np.random.seed(999)
|
100 |
+
|
101 |
+
soil_fertility = np.random.uniform(0, 100, n)
|
102 |
+
temperature = np.random.uniform(15, 35, n)
|
103 |
+
irrigation_freq = np.random.uniform(0, 10, n)
|
104 |
+
fertilizer_score = np.random.uniform(0, 100, n)
|
105 |
+
|
106 |
+
# Near-perfect linear relationship:
|
107 |
+
# yield_val = 2.0*soil_fertility + (-0.3)*(abs(temperature-25)) + 3.0*irrigation_freq
|
108 |
+
# + 0.8*fertilizer_score
|
109 |
+
# plus tiny normal noise
|
110 |
+
|
111 |
+
yield_val = (
|
112 |
+
2.0 * soil_fertility
|
113 |
+
+ (-0.3) * np.abs(temperature - 25)
|
114 |
+
+ 3.0 * irrigation_freq
|
115 |
+
+ 0.8 * fertilizer_score
|
116 |
+
+ np.random.normal(0, 1, n) # small noise
|
117 |
)
|
118 |
|
119 |
+
df = pd.DataFrame({
|
120 |
+
"soil_fertility": soil_fertility,
|
121 |
+
"temperature": temperature,
|
122 |
+
"irrigation_freq": irrigation_freq,
|
123 |
+
"fertilizer_score": fertilizer_score,
|
124 |
+
"crop_yield": yield_val
|
125 |
+
})
|
126 |
+
return df
|
127 |
+
|
128 |
+
################################################################################
|
129 |
+
# 2) TRAINING MODELS (CACHED FOR PERFORMANCE)
|
130 |
+
################################################################################
|
131 |
+
|
132 |
+
@st.cache_resource
|
133 |
+
def train_all_models():
|
134 |
+
"""
|
135 |
+
Trains all three models on near-perfect synthetic data:
|
136 |
+
- Pest Outbreak (RandomForest)
|
137 |
+
- Disease Presence (LogisticRegression w/ polynomial)
|
138 |
+
- Yield Regression (GradientBoosting)
|
139 |
+
Returns pipelines + metrics in a dict.
|
140 |
+
"""
|
141 |
+
|
142 |
+
# 2.1 Pest Model
|
143 |
+
pest_df = generate_pest_data()
|
144 |
+
X_pest = pest_df[["temperature", "humidity", "leaf_wetness"]]
|
145 |
+
y_pest = pest_df["pest_outbreak"]
|
146 |
+
|
147 |
+
pest_pipeline = Pipeline([
|
148 |
+
("scaler", StandardScaler()),
|
149 |
+
("rf", RandomForestClassifier(n_estimators=50, random_state=42))
|
150 |
+
])
|
151 |
+
pest_pipeline.fit(X_pest, y_pest)
|
152 |
+
pest_pred_train = pest_pipeline.predict(X_pest)
|
153 |
+
pest_accuracy = accuracy_score(y_pest, pest_pred_train)
|
154 |
+
|
155 |
+
# 2.2 Disease Model
|
156 |
+
disease_df = generate_disease_data()
|
157 |
+
X_dis = disease_df[["soil_ph", "rainfall", "planting_density"]]
|
158 |
+
y_dis = disease_df["disease_present"]
|
159 |
+
|
160 |
+
disease_pipeline = Pipeline([
|
161 |
+
("poly", PolynomialFeatures(degree=2, include_bias=False)),
|
162 |
+
("scaler", StandardScaler()),
|
163 |
+
("lr", LogisticRegression(random_state=42, max_iter=2000))
|
164 |
+
])
|
165 |
+
disease_pipeline.fit(X_dis, y_dis)
|
166 |
+
disease_pred_train = disease_pipeline.predict(X_dis)
|
167 |
+
disease_accuracy = accuracy_score(y_dis, disease_pred_train)
|
168 |
+
|
169 |
+
# 2.3 Yield Model
|
170 |
+
yield_df = generate_yield_data()
|
171 |
+
X_yield = yield_df[["soil_fertility", "temperature", "irrigation_freq", "fertilizer_score"]]
|
172 |
+
y_yield = yield_df["crop_yield"]
|
173 |
+
|
174 |
+
X_train, X_test, y_train, y_test = train_test_split(X_yield, y_yield, test_size=0.2, random_state=42)
|
175 |
+
|
176 |
+
yield_pipeline = Pipeline([
|
177 |
+
("scaler", StandardScaler()),
|
178 |
+
("gbr", GradientBoostingRegressor(n_estimators=50, learning_rate=0.1, random_state=42))
|
179 |
+
])
|
180 |
+
yield_pipeline.fit(X_train, y_train)
|
181 |
+
|
182 |
+
# Evaluate on train data to see near-perfect learning
|
183 |
+
yield_pred_train = yield_pipeline.predict(X_train)
|
184 |
+
yield_rmse_train = mean_squared_error(y_train, yield_pred_train, squared=False)
|
185 |
+
|
186 |
+
# Also check test set
|
187 |
+
yield_pred_test = yield_pipeline.predict(X_test)
|
188 |
+
yield_rmse_test = mean_squared_error(y_test, yield_pred_test, squared=False)
|
189 |
+
|
190 |
+
return {
|
191 |
+
"pest_model": pest_pipeline,
|
192 |
+
"pest_accuracy": pest_accuracy,
|
193 |
+
"disease_model": disease_pipeline,
|
194 |
+
"disease_accuracy": disease_accuracy,
|
195 |
+
"yield_model": yield_pipeline,
|
196 |
+
"yield_rmse_train": yield_rmse_train,
|
197 |
+
"yield_rmse_test": yield_rmse_test
|
198 |
+
}
|
199 |
+
|
200 |
+
################################################################################
|
201 |
+
# 3) GPT CALLS (BYPASSING 'openai' LIB)
|
202 |
+
################################################################################
|
203 |
+
|
204 |
+
def call_gpt(prompt_text, openai_api_key):
|
205 |
+
"""
|
206 |
+
Posts to the Chat Completions endpoint with the user prompt.
|
207 |
+
Returns GPT's text.
|
208 |
+
"""
|
209 |
+
|
210 |
+
if not openai_api_key:
|
211 |
+
return "ERROR: No OpenAI API key provided. Please enter it to get GPT advice."
|
212 |
+
|
213 |
endpoint = "https://api.openai.com/v1/chat/completions"
|
214 |
headers = {
|
215 |
"Content-Type": "application/json",
|
216 |
+
"Authorization": f"Bearer {openai_api_key}"
|
217 |
}
|
218 |
data = {
|
219 |
+
"model": "gpt-3.5-turbo", # or "gpt-4" if you have access
|
220 |
"messages": [
|
221 |
+
{"role": "system", "content": "You are a highly knowledgeable, helpful farm AI assistant."},
|
222 |
+
{"role": "user", "content": prompt_text}
|
223 |
],
|
224 |
+
"max_tokens": 500,
|
225 |
"temperature": 0.7
|
226 |
}
|
227 |
|
228 |
try:
|
229 |
+
response = requests.post(endpoint, headers=headers, json=data, timeout=30)
|
230 |
+
result = response.json()
|
231 |
if "choices" in result and len(result["choices"]) > 0:
|
232 |
return result["choices"][0]["message"]["content"].strip()
|
233 |
else:
|
|
|
235 |
except Exception as e:
|
236 |
return f"Error calling GPT: {e}"
|
237 |
|
238 |
+
################################################################################
|
239 |
+
# 4) STREAMLIT APP (NEAR-PERFECT ACCURACY, SCALABLE POC)
|
240 |
+
################################################################################
|
241 |
+
|
242 |
def main():
|
243 |
+
st.title("Near-Perfect Farm AI + GPT (Scalable PoC)")
|
244 |
+
st.markdown("""
|
245 |
+
**Features**:
|
246 |
+
- Three local ML models (pest, disease, yield) with near-perfect synthetic data.
|
247 |
+
- GPT for advanced explanations and advice.
|
248 |
+
- Demonstrates a scalable approach for real production: replace synthetic data
|
249 |
+
and model training with real data or pre-trained models.
|
250 |
+
""")
|
251 |
|
252 |
+
# 4.1 Load/Train Models
|
253 |
+
with st.spinner("Training models on near-perfect synthetic data..."):
|
254 |
+
models = train_all_models()
|
255 |
+
|
256 |
+
# 4.2 Sidebar: Show training metrics
|
257 |
+
st.sidebar.header("Local Model Performance")
|
258 |
+
st.sidebar.write(f"**Pest Model Accuracy:** {models['pest_accuracy']*100:.2f}%")
|
259 |
+
st.sidebar.write(f"**Disease Model Accuracy:** {models['disease_accuracy']*100:.2f}%")
|
260 |
+
st.sidebar.write(f"**Yield RMSE (Train):** {models['yield_rmse_train']:.2f}")
|
261 |
+
st.sidebar.write(f"**Yield RMSE (Test):** {models['yield_rmse_test']:.2f}")
|
262 |
+
|
263 |
+
# 4.3 User Input Form
|
264 |
+
st.subheader("Enter Your Farm Conditions")
|
265 |
with st.form("farm_form"):
|
266 |
+
soil_moisture = st.slider("Soil Moisture (%)", 0.0, 100.0, 25.0, 1.0)
|
267 |
+
soil_ph = st.slider("Soil pH", 0.0, 14.0, 6.5, 0.1)
|
268 |
+
temperature = st.slider("Temperature (°C)", 0.0, 50.0, 28.0, 1.0)
|
269 |
+
humidity = st.slider("Humidity (%)", 0.0, 100.0, 60.0, 1.0)
|
270 |
+
crop_health = st.slider("Crop Health (0-100)", 0.0, 100.0, 80.0, 1.0)
|
271 |
+
leaf_wetness = st.slider("Leaf Wetness (0-100)", 0.0, 100.0, 40.0, 1.0)
|
272 |
+
rainfall = st.number_input("Rainfall (mm)", 0.0, 500.0, 50.0, 5.0)
|
273 |
+
planting_density = st.number_input("#Plants/Acre", 10.0, 500.0, 100.0, 10.0)
|
274 |
+
irrigation_freq = st.number_input("Irrigation Frequency (times/week)", 0.0, 14.0, 3.0, 1.0)
|
275 |
+
fertilizer_score = st.slider("Fertilizer Score (0-100)", 0.0, 100.0, 50.0, 1.0)
|
276 |
+
|
277 |
+
st.markdown("### GPT Setup")
|
278 |
+
openai_api_key = st.text_input("OpenAI API Key (for GPT)", type="password")
|
279 |
|
280 |
+
submitted = st.form_submit_button("Analyze & Get GPT Advice")
|
281 |
|
282 |
if submitted:
|
283 |
+
st.subheader("Local Analysis & Predictions")
|
284 |
+
# 4.3.1 Basic rules
|
285 |
+
irrigation_needed = (soil_moisture < 30)
|
286 |
+
crop_health_status = "Healthy" if crop_health > 85 else "Needs Attention"
|
287 |
+
|
288 |
+
# 4.3.2 Pest Prediction
|
289 |
+
X_pest = np.array([[temperature, humidity, leaf_wetness]])
|
290 |
+
pest_outbreak = (models["pest_model"].predict(X_pest)[0] == 1)
|
291 |
+
|
292 |
+
# 4.3.3 Disease Detection
|
293 |
+
X_dis = np.array([[soil_ph, rainfall, planting_density]])
|
294 |
+
disease_present = (models["disease_model"].predict(X_dis)[0] == 1)
|
295 |
+
|
296 |
+
# 4.3.4 Yield Regression
|
297 |
+
# We treat "crop_health" as a stand-in for "soil_fertility" here.
|
298 |
+
X_yield = np.array([[crop_health, temperature, irrigation_freq, fertilizer_score]])
|
299 |
+
yield_prediction = models["yield_model"].predict(X_yield)[0]
|
300 |
+
|
301 |
+
# Display local results
|
302 |
+
st.write(f"- **Irrigation Needed?** {irrigation_needed}")
|
303 |
+
st.write(f"- **Crop Health Status:** {crop_health_status}")
|
304 |
+
st.write(f"- **Pest Outbreak Likely?** {pest_outbreak}")
|
305 |
+
st.write(f"- **Disease Present?** {disease_present}")
|
306 |
+
st.write(f"- **Predicted Yield (t/ha):** {yield_prediction:.2f}")
|
307 |
+
|
308 |
+
# 4.3.5 GPT Explanation
|
309 |
+
summary_prompt = (
|
310 |
+
"These are the farm conditions:\n"
|
311 |
+
f" - Soil Moisture: {soil_moisture:.1f}%\n"
|
312 |
+
f" - Soil pH: {soil_ph:.1f}\n"
|
313 |
+
f" - Temperature: {temperature:.1f} °C\n"
|
314 |
+
f" - Humidity: {humidity:.1f}%\n"
|
315 |
+
f" - Crop Health: {crop_health:.1f}\n"
|
316 |
+
f" - Leaf Wetness: {leaf_wetness:.1f}\n"
|
317 |
+
f" - Rainfall: {rainfall:.1f} mm\n"
|
318 |
+
f" - Planting Density: {planting_density:.1f}\n"
|
319 |
+
f" - Irrigation Frequency: {irrigation_freq:.1f} times/week\n"
|
320 |
+
f" - Fertilizer Score: {fertilizer_score:.1f}\n\n"
|
321 |
+
"Local ML Results:\n"
|
322 |
+
f" - Irrigation Needed: {irrigation_needed}\n"
|
323 |
+
f" - Crop Health Status: {crop_health_status}\n"
|
324 |
+
f" - Pest Outbreak: {pest_outbreak}\n"
|
325 |
+
f" - Disease Present: {disease_present}\n"
|
326 |
+
f" - Yield: {yield_prediction:.2f} t/ha\n\n"
|
327 |
+
"Please provide a clear, helpful explanation in plain language, "
|
328 |
+
"along with sustainable, cost-effective steps to improve or maintain these conditions."
|
329 |
+
)
|
330 |
+
|
331 |
+
st.subheader("GPT Advice")
|
332 |
+
gpt_response = call_gpt(summary_prompt, openai_api_key)
|
333 |
+
st.write(gpt_response)
|
334 |
|
335 |
if __name__ == "__main__":
|
336 |
main()
|