<jupyter_start><jupyter_text>[![Open in SageMaker Studio Lab](https://studiolab.sagemaker.aws/studiolab.svg)](https://8dd6shpox5gp5pz.studio.us-east-2.sagemaker.aws/studiolab/default/jupyter/lab/tree/sagemaker-studiolab-notebooks/AWS-disaster-response/Wildfires.ipynb) In this notebook we will demonstrate how to predict wildfire occurrences using machine learning based on Remote Sensing data The dataset comes from remote sensors is preprocessed and contains the following variables, (NDVI: Normalized Difference Vegetation Index), meteorological conditions (LST: Land Surface Temperature) as well as the fire indicator “Thermal Anomalies” (BURNED_AREA).  - The NDVI is a simple graphical indicator that can be used to analyze remote sensing measurements, often from a space platform, assessing whether or not the target being observed contains live green vegetation. NDVI values are between 0 and 1, values near 0 indicate very sparse vegetation and values near 1 indicate dense vegetation.- The LST represents the radiative skin temperature of the land surface derived from solar radiation, it depends on the vegetation cover and the soil moisture- Thermal Anomalies are a fire detection strategy based on complete detection of a fire (only when the fire is strong enough to be detected. All three parameters were collected from MODIS (Moderate Resolution Imaging Spectroradiometer), an instrument carried on board the NASA's Terra platform. The fourth variable which will be our response is whether a fire occurred or not based on those parameters. Reference: <NAME>, <NAME>, <NAME>assime [Predictive Modeling of Wildfires: A New Dataset and Machine Learning Approach](https://pdf.sciencedirectassets.com/271100/1-s2.0-S0379711219X00028/1-s2.0-S0379711218303941/am.pdf?X-Amz-Security-Token=<KEY>JHMEUCIQCGBnF8JddFcIL77%2BM4y0cARiUTMypLs%2FExPRLAX%2BsjgQIgH2XGfPwWwiaSqLm5WkHpv5JJzwylfT3RN2fRSsVj6wUqgwQIjP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARAEGgwwNTkwMDM1NDY4NjUiDAETElCpoiE0SOxkyyrXAzpzR2%2FLAjHBaDFMW4whSniOG2Hedg5RbPveXgNU1mPGhj6Oz%2FKThf5%2BsfZaz4UhA0emyZxHbRppEUpEmii4l9Pn3lPU%2FQJItFktjgvFMXBOyC%2FEF7URINF493oURcStp3cwmAIl4WZ6ZeD7%2F9TPbUJ2khW348Lcqi1iBaI6rJH9O3nHXeFFariOhRFxaraz7fFd%2B65t1CQTpR%2FTwZyoDWHdSfjoeoW4Ecpj8jwvV8TOsUxyNprZXX9XpJt1GraF9sIIDutBVWor0evxo9K4vmZ47hVXzfH5ObYQ4JNBiD2PXJqBUO9CzW2hpENqi5YC%2Fz3vGKbdNx5uzY26YHKO%2F9BR%2FcuTg5forNfvwUF1w8WCn6SMx6KCK%2FeQbxT1IsYXWexDwo7LQTVoWl6nyuPhtgl9iqmRmqqoMDKF1qDXhr4oTmhZPqq9t5Y%2FytZ2xQGLptV832s%2FSK8DJgZzOgbUJb3JAvwlhon9IFC5Nsd8Hr%2FrhanKj2a%2FAsloSVfLSaJBby6OM%2BtJMIdA%2Bv2VVWk90X7rsg2CUt6dcYsSAB6%2Bqn8boJ0JsUu0awghrdcjOQKSLR4NFj8RUYfQUIxP%2BJeXSpoW4Z8ZfcBF%2F%2Bqb%2FdINXbA8m9QIYmlxJzC9jvSPBjqlAaQ1hKa%2BQLv%2BVDFKUxArBbx9JWru7W2OjauvtJcwBl8WKrnJkgtq4WvaZl8M0q34BtUOAUGZQqxZ%2BOPvp6W8Df377ovwbhc%2FrS07r2soW2pIHX2QKV5OukZ7ANOeohquv8TkhMmYQi4XkDvnXKPxJujh6tA4ZOvyQa992HV3u9PdzNHhQ5rvcpAX7vAkKYmmO2k%2BOvE7ez7zp%2FGrftco%2BVaGZ2zRcw%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20220204T114927Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=ASIAQ3PHCVTYQQVHBCQE%2F20220204%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=105f44320308b3aa81336ef300831b2c111da5fca475355d10d0594649bccf10&hash=6e5cf2719f6409013fc0ca0b735b617cb48b73635ab255e05e41c4e42b4a9525&host=68042c943591013ac2b2430a89b270f6af2c76d8dfd086a07176afe7c76c2c61&pii=S0379711218303941&tid=pdf-10c6c5d8-5edd-420f-9d36-0e4f70bfa8d9&sid=7e7f4a2654f7754682087e3287c2e6bb6e53gxrqb&type=client)  Import required libraries<jupyter_code>!pip install pandas
!pip install matplotlib scikit-learn xgboost
import pandas as pd
import numpy as np


from sklearn.metrics import classification_report, confusion_matrix, accuracy_score, f1_score
import matplotlib.pyplot as plt

import xgboost as xgb
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_val_score, validation_curve, train_test_split, GridSearchCV
from sklearn.model_selection import StratifiedKFold, RepeatedStratifiedKFold

import warnings
warnings.filterwarnings('ignore') 

from IPython.display import Image
pwd
import os
#os.mkdir('/home/studio-lab-user/sagemaker-studiolab-notebooks/AWS-disaster-response')
#os.chdir('/home/studio-lab-user/sagemaker-studiolab-notebooks/AWS-disaster-response')
pwd
pip install requests
import requests

url = 'https://raw.githubusercontent.com/rapha18th/Wildfires-prediction-ignis/master/Wildfires.csv'
res = requests.get(url, allow_redirects=True)
with open('Wildfires.csv', 'wb') as file:
    file.write(res.content)
ls
df = pd.read_csv("Wildfires.csv")
df.head()
df.shape
df.describe()<jupyter_output><empty_output><jupyter_text>Convert response variable from categorical to numeric<jupyter_code>df['CLASS'].replace({"no_fire":0, "fire":1}, inplace=True)
df.describe()<jupyter_output><empty_output><jupyter_text>Visualise distribution of variables<jupyter_code>pip install seaborn
import seaborn as sns
sns.displot(df, x="NDVI")
sns.displot(df, x="LST")
sns.displot(df, x="BURNED_AREA")
sns.displot(df, x="CLASS")<jupyter_output><empty_output><jupyter_text>Build and Train Machine learning models<jupyter_code>#features
X = df[["NDVI", "LST", "BURNED_AREA"]]

#response
y = df["CLASS"]
# Train Test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.30, random_state = 42)<jupyter_output><empty_output><jupyter_text>Model 1: Random Forest<jupyter_code>rf_model=RandomForestClassifier(random_state=42)

rf_model.fit(X_train, y_train)
pred_rf = rf_model.predict(X_test)
print("Accuracy for baseline random forest:", accuracy_score(y_test, pred_rf))
print("F1 score for baseline randomforest:", f1_score(y_test, pred_rf))
print("Confusion Matrix: \n", pd.DataFrame(confusion_matrix(y_test, pred_rf),columns=['No Fire','Fire'],index=['No Fire','Fire']))<jupyter_output>Confusion Matrix: 
          No Fire  Fire
No Fire      384    18
Fire          63    49<jupyter_text>Hyperparameter Tuning for randomforest classifier<jupyter_code>param_grid = [
{'n_estimators':list(range(0, 110, 10))[1:], 'max_features': [1, 3], 
 'max_depth': [10, 50, None], 'criterion':['gini', 'entropy'], 'class_weight':['balanced']}
]

grid_search_forest = GridSearchCV(rf_model, param_grid, cv=5, scoring='f1', refit="true",
                                  verbose=1, n_jobs=1)
grid_search_forest.fit(X_train, y_train)
grid_search_forest.best_estimator_
rf_model1=RandomForestClassifier(class_weight='balanced', max_depth=10, max_features=1,
                       random_state=42)

rf_model1.fit(X_train, y_train)
pred_rf1 = rf_model1.predict(X_test)
print("Accuracy for tuned random forest:", accuracy_score(y_test, pred_rf1))
print("F1 score for tuned randomforest:", f1_score(y_test, pred_rf1))
print("Confusion Matrix: \n", pd.DataFrame(confusion_matrix(y_test, pred_rf1),columns=['No Fire','Fire'],index=['No Fire','Fire']))<jupyter_output>Confusion Matrix: 
          No Fire  Fire
No Fire      365    37
Fire          52    60<jupyter_text>Since there is a heavy imbalance in the response variable, 1327 instances of “no_fire” and 386 instances of the “fire”, the appropriate evaluation metric will be F1-score.  It is apt because the minority class is at risk for a higher proportion of false positives and false negatives due to less observations compared to the majority class. A higher F1-score means higher precision and recall (less false positives and false negatives).<jupyter_code># Export the model as serialised object for use in apps
import joblib
 
joblib.dump(rf_model1, 'model_randomforest1.pkl')
ls<jupyter_output>Wildfires.csv  Wildfires.ipynb  model_randomforest1.pkl<jupyter_text>Model 2: XGBoost<jupyter_code>xgb_model = xgb.XGBClassifier(random_state=42, use_label_encoder=False)
xgb_model.fit(X_train, y_train)<jupyter_output>/home/studio-lab-user/.conda/envs/default/lib/python3.9/site-packages/xgboost/sklearn.py:1224: UserWarning: The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].
  warnings.warn(label_encoder_deprecation_msg, UserWarning)
/home/studio-lab-user/.conda/envs/default/lib/python3.9/site-packages/xgboost/data.py:262: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
  elif isinstance(data.columns, (pd.Int64Index, pd.RangeIndex)):<jupyter_text>Model Evaluation<jupyter_code>pred_xgb = xgb_model.predict(X_test)
print("Accuracy for baseline XGBoost:", accuracy_score(y_test, pred_xgb))
print("F1 score for baseline XGBoost:", f1_score(y_test, pred_xgb))
print("Confusion Matrix: \n", pd.DataFrame(confusion_matrix(y_test, pred_xgb),columns=['No Fire','Fire'],index=['No Fire','Fire']))<jupyter_output>Confusion Matrix: 
          No Fire  Fire
No Fire      373    29
Fire          56    56<jupyter_text>Hyperparameter tuning for XGBoost<jupyter_code>xgb_1 = xgb.XGBClassifier(random_state=42, silent=True, verbosity =0, use_label_encoder=False)

params_grid = { 'max_depth': [3,6,10],
           'learning_rate': [0.01, 0.05, 0.1],
           'n_estimators': [10, 50, 100],
           'colsample_bytree': [0.3, 0.7]}

grid_search_xgb = GridSearchCV(xgb_1, param_grid, cv=5, scoring='f1', refit="true",
                                  verbose=1, n_jobs=1)
grid_search_xgb.fit(X_train, y_train)
print(grid_search_xgb.best_estimator_)
xgb_model1 = xgb.XGBClassifier(class_weight='balanced',learning_rate = 0.3002,
                               random_state=42,refit='true', scoring='f1',verbose=1)
xgb_model1.fit(X_train, y_train)
pred_xgb1 = xgb_model1.predict(X_test)<jupyter_output><empty_output><jupyter_text>Model Evaluation<jupyter_code>print("Accuracy for tuned XGBoost:", accuracy_score(y_test, pred_xgb1))
print("F1 score for tuned XGBoost:", f1_score(y_test, pred_xgb1))
print("Confusion Matrix: \n", pd.DataFrame(confusion_matrix(y_test, pred_xgb1),columns=['No Fire','Fire'],index=['No Fire','Fire']))<jupyter_output>Confusion Matrix: 
          No Fire  Fire
No Fire      372    30
Fire          57    55<jupyter_text>With both grid search and "trial and error" I could not tune the hyperparameters to improve the xgboost baseline, so the model of choice will be random forest.  Application  Now that the model has been built, it is time to apply it. An example application would be a simulation of a forest fire based on the chances of it occuring. We take our serialised model and use it in our application. The model has two functions of interest `predict` and `predict_proba`. In this case we will use the latter which outputs the probabilty value of whether a fire will occur or not.<jupyter_code># Load the model
import joblib
rf_classifier = joblib.load(open("model_randomforest1.pkl", "rb"))
NDVI = 0.5
LST = 15333
BURNED_AREA = 0.4
prob_fire = rf_classifier.predict_proba([[float(NDVI), float(LST),float(BURNED_AREA)]])
fire_chance = prob_fire[0][1]
fire_chance
pip install imageio<jupyter_output>Requirement already satisfied: imageio in /home/studio-lab-user/.conda/envs/default/lib/python3.9/site-packages (2.14.1)
Requirement already satisfied: pillow>=8.3.2 in /home/studio-lab-user/.conda/envs/default/lib/python3.9/site-packages (from imageio) (9.0.0)
Requirement already satisfied: numpy in /home/studio-lab-user/.conda/envs/default/lib/python3.9/site-packages (from imageio) (1.22.1)
Note: you may need to restart the kernel to use updated packages.<jupyter_text>Simulation The simulation demonstrates that the higher the probabilty of a fire occuring, the bigger the expected spread. Reference: [How to simulate wildfires with python](https://medium.com/@tetraktyz/how-to-simulate-wildfires-with-python-6562e2eed266)<jupyter_code>import imageio
import numpy as np
prob = fire_chance # probability of fire
total_time = 300 # simulation time
terrain_size = [100,100] # size of the simulation: 10000 cells
# states hold the state of each cell
states = np.zeros((total_time,*terrain_size))
# initialize states by creating random fuel and clear cells
states[0] = np.random.choice([0,1],size=terrain_size,p=[1-prob,prob])
# set the middle cell on fire!!!
states[0,terrain_size[0]//2,terrain_size[1]//2] = 2

for t in range(1,total_time):
    # Make a copy of the original states
    states[t] = states[t-1].copy()

    for x in range(1,terrain_size[0]-1):
        for y in range(1,terrain_size[1]-1):

            if states[t-1,x,y] == 2: # It's on fire
                states[t,x,y] = 0 # Put it out and clear it
                
                # If there's fuel surrounding it
                # set it on fire!
                if states[t-1,x+1,y] == 1: 
                    states[t,x+1,y] = 2
                if states[t-1,x-1,y] == 1:
                    states[t,x-1,y] = 2
                if states[t-1,x,y+1] == 1:
                    states[t,x,y+1] = 2
                if states[t-1,x,y-1] == 1:
                    states[t,x,y-1] = 2
                    
colored = np.zeros((total_time,*terrain_size,3),dtype=np.uint8)

# Color
for t in range(states.shape[0]):
    for x in range(states[t].shape[0]):
        for y in range(states[t].shape[1]):
            value = states[t,x,y].copy()

            if value == 0:
                colored[t,x,y] = [139,69,19] # Clear
            elif value == 1: 
                colored[t,x,y] = [0,255,0]   # Fuel
            elif value == 2: 
                colored[t,x,y] = [255,0,0]   # Burning
            
# Crop
cropped = colored[:200,1:terrain_size[0]-1,1:terrain_size[1]-1]

imageio.mimsave('./video.gif', cropped)


# define image_url variable
image_url = "video.gif"

# display image using url and specific width set at 500
Image(url=image_url, width=500)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10255484988452655, 'fraction_numerical': 0.05614896073903002, 'mean_word_length': 5.2900590104403085, 'pattern_counts': {'":': 2, '<': 50, '<?xml version=': 0, '>': 51, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '216717', 'license': 'MIT', 'n_tokens_mistral': 5654, 'n_tokens_neox': 5148, 'n_words': 1087, 'path': 'Wildfires.ipynb', 'repo_name': 'rapha18th/Wildfires-prediction-ignis', 'size': '13916'}