Spaces:
Running
Running
Uploading notebooks and data
Browse files- 1_explore_and_prepare_data.ipynb +250 -0
- 2_build_and_train_model.ipynb +414 -0
- README.md +1 -1
- login.html +2 -2
- younup_greenmorrow_data.parquet +3 -0
1_explore_and_prepare_data.ipynb
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"id": "804cef31-4c45-4456-bb2a-6a27680f7c93",
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"source": [
|
| 8 |
+
"# Explore and prepare data for Machine Learning"
|
| 9 |
+
]
|
| 10 |
+
},
|
| 11 |
+
{
|
| 12 |
+
"cell_type": "markdown",
|
| 13 |
+
"id": "8f83ebbe-33dc-4f11-a3a5-414d7f00dd09",
|
| 14 |
+
"metadata": {},
|
| 15 |
+
"source": [
|
| 16 |
+
"## Introduction\n",
|
| 17 |
+
"This file aims to present the **preliminary process for building and training a machine learning model**, that is analysing and preparing the training data.\n",
|
| 18 |
+
"\n",
|
| 19 |
+
"This file is a ***notebook***: it is a format in which you can write code, rich text (Markdown), formulae, and add multimedia content. To learn more, visit [the Jupyter project website](https://jupyter.org/).\\\n",
|
| 20 |
+
"Each piece of content is placed in a *cell*, which can be executed on the fly without having to rerun the entire notebook."
|
| 21 |
+
]
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"cell_type": "markdown",
|
| 25 |
+
"id": "1f93155a-5ede-407f-9731-825b9752899e",
|
| 26 |
+
"metadata": {},
|
| 27 |
+
"source": [
|
| 28 |
+
"### Model objective\n",
|
| 29 |
+
"\n",
|
| 30 |
+
"Here, the objective of the trained model is to **predict a plant’s seasonal stage based on the date, location and weather conditions**. This stage is called a *phenological stage*, which may correspond to flowering, fruit ripening, or leaf fall. The phenological stage is generally numbered from 0 to 9, with number 6 corresponding, for example, to flowering:\n",
|
| 31 |
+
"\n",
|
| 32 |
+
"\n",
|
| 33 |
+
"\n",
|
| 34 |
+
"*The BBCH scale. According to “Phenological stages of monocotyledonous and dicotyledonous crops”. U. Meier. Blackwell Wissenschafts-Verlag Berlin. 2001.*\n",
|
| 35 |
+
"\n",
|
| 36 |
+
"A stage can be seen as a category (0, 1, …, 8, 9): therefore, the model to be trained must be based on **a classification algorithm**, supervised by training data. A classification algorithm can be used to address many needs, such as diagnosing a patient (are they affected by a given disease based on their blood test results, yes/no?) or identifying an animal in a photo (cat or dog?)."
|
| 37 |
+
]
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"cell_type": "markdown",
|
| 41 |
+
"id": "a3d8a559-f71c-429a-9fc9-d82748aa7911",
|
| 42 |
+
"metadata": {},
|
| 43 |
+
"source": [
|
| 44 |
+
"## Python package imports"
|
| 45 |
+
]
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"cell_type": "code",
|
| 49 |
+
"execution_count": null,
|
| 50 |
+
"id": "fad0ce31-8a2f-4e4e-a99e-32104e288f6b",
|
| 51 |
+
"metadata": {},
|
| 52 |
+
"outputs": [],
|
| 53 |
+
"source": [
|
| 54 |
+
"import matplotlib.pyplot as plt\n",
|
| 55 |
+
"import numpy as np\n",
|
| 56 |
+
"import pandas as pd\n",
|
| 57 |
+
"\n",
|
| 58 |
+
"pd.set_option('display.max_columns', None) # Display all columns of a dataframe"
|
| 59 |
+
]
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
"cell_type": "markdown",
|
| 63 |
+
"id": "5f0fb927-4f09-4da9-8533-d09bdd9b8bda",
|
| 64 |
+
"metadata": {},
|
| 65 |
+
"source": [
|
| 66 |
+
"## Data loading and preparation"
|
| 67 |
+
]
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"cell_type": "markdown",
|
| 71 |
+
"id": "9d6f7826-ac8c-4a6b-9b30-9031b67dd0fe",
|
| 72 |
+
"metadata": {},
|
| 73 |
+
"source": [
|
| 74 |
+
"The first step in building a machine learning model is to **collect data, analyse it to understand it, and prepare it** for model training. Here, we have [a dataset](https://doi.org/10.5281/zenodo.15593446) in Parquet format called `younup_greenmorrow_data.parquet`, which we load using the `pandas` package."
|
| 75 |
+
]
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"cell_type": "code",
|
| 79 |
+
"execution_count": null,
|
| 80 |
+
"id": "482b8bd2-e30c-4e2b-a7df-b9e041b6c7b7",
|
| 81 |
+
"metadata": {},
|
| 82 |
+
"outputs": [],
|
| 83 |
+
"source": [
|
| 84 |
+
"data = pd.read_parquet('younup_greenmorrow_data.parquet')"
|
| 85 |
+
]
|
| 86 |
+
},
|
| 87 |
+
{
|
| 88 |
+
"cell_type": "markdown",
|
| 89 |
+
"id": "00a0b6d7-6dab-4882-8966-c58c901fc365",
|
| 90 |
+
"metadata": {},
|
| 91 |
+
"source": [
|
| 92 |
+
"### Basic analysis"
|
| 93 |
+
]
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"cell_type": "markdown",
|
| 97 |
+
"id": "bf827940-a7ac-4808-aa48-cc55f6542a9f",
|
| 98 |
+
"metadata": {},
|
| 99 |
+
"source": [
|
| 100 |
+
"Some useful `pandas` functions to get started:\n",
|
| 101 |
+
"- `head()` to display the first rows of the dataset,\n",
|
| 102 |
+
"- `info()` to obtain information about each column, such as the number of non-null entries or its type (numeric, categorical, etc.),\n",
|
| 103 |
+
"- `describe()` to view simple descriptive statistics for each column (mean, minimum and maximum values, etc.)."
|
| 104 |
+
]
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"cell_type": "code",
|
| 108 |
+
"execution_count": null,
|
| 109 |
+
"id": "0e971258-17dc-4a20-b67b-4b7771091327",
|
| 110 |
+
"metadata": {},
|
| 111 |
+
"outputs": [],
|
| 112 |
+
"source": [
|
| 113 |
+
"data.head()"
|
| 114 |
+
]
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"cell_type": "code",
|
| 118 |
+
"execution_count": null,
|
| 119 |
+
"id": "154c8e55-d1f9-43dc-a7aa-d8879920fbaa",
|
| 120 |
+
"metadata": {},
|
| 121 |
+
"outputs": [],
|
| 122 |
+
"source": [
|
| 123 |
+
"data.info()"
|
| 124 |
+
]
|
| 125 |
+
},
|
| 126 |
+
{
|
| 127 |
+
"cell_type": "code",
|
| 128 |
+
"execution_count": null,
|
| 129 |
+
"id": "65080519-94c8-4219-8606-8864d1ac1e5c",
|
| 130 |
+
"metadata": {},
|
| 131 |
+
"outputs": [],
|
| 132 |
+
"source": [
|
| 133 |
+
"data.describe()"
|
| 134 |
+
]
|
| 135 |
+
},
|
| 136 |
+
{
|
| 137 |
+
"cell_type": "markdown",
|
| 138 |
+
"id": "d8738e32-6898-41bb-b53e-3d55cba7861f",
|
| 139 |
+
"metadata": {},
|
| 140 |
+
"source": [
|
| 141 |
+
"We can see that this dataset contains 27,308 entries (rows) and 62 variables (columns). Using the `head()` function, we understand that **each row corresponds to an observation of a plant’s stage at a given date and location**, for which weather conditions are provided across many columns. The `info()` function tells us that no column contains missing values (\"27308 non-null\"), while the `describe()` function informs us (in the `year` column) about the temporal range of the data, from 1958 to 2024."
|
| 142 |
+
]
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"cell_type": "markdown",
|
| 146 |
+
"id": "8d4c1034-ff6d-4bcc-844f-285237511a83",
|
| 147 |
+
"metadata": {},
|
| 148 |
+
"source": [
|
| 149 |
+
"### In-depth analysis"
|
| 150 |
+
]
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
"cell_type": "markdown",
|
| 154 |
+
"id": "ff6fc931-2d32-46a0-8662-c0b8c18f4c99",
|
| 155 |
+
"metadata": {},
|
| 156 |
+
"source": [
|
| 157 |
+
"To go further in understanding the dataset, it is possible to explore it graphically or using more advanced statistical tools. One such tool is `ydata-profiling` ([official website](https://docs.profiling.ydata.ai/latest/)), which automatically generates a detailed and interactive analysis report, including a wide range of statistics and visualisations. The report can be viewed directly in the notebook or exported as HTML."
|
| 158 |
+
]
|
| 159 |
+
},
|
| 160 |
+
{
|
| 161 |
+
"cell_type": "code",
|
| 162 |
+
"execution_count": null,
|
| 163 |
+
"id": "f7f167e4-49e3-4006-9152-d4b8b944f8a3",
|
| 164 |
+
"metadata": {},
|
| 165 |
+
"outputs": [],
|
| 166 |
+
"source": [
|
| 167 |
+
"from ydata_profiling import ProfileReport\n",
|
| 168 |
+
"\n",
|
| 169 |
+
"profile = ProfileReport(data, title='Profiling Report')\n",
|
| 170 |
+
"profile.to_notebook_iframe() # Report in the notebook\n",
|
| 171 |
+
"# profile.to_file(\"test_report.html\") # Export report as HTML"
|
| 172 |
+
]
|
| 173 |
+
},
|
| 174 |
+
{
|
| 175 |
+
"cell_type": "markdown",
|
| 176 |
+
"id": "8ff73e8c-2e31-45df-9500-501f171a8014",
|
| 177 |
+
"metadata": {},
|
| 178 |
+
"source": [
|
| 179 |
+
"In the case of our dataset, the report generated with `ydata-profiling` highlights warnings about strong correlations between certain columns (for example, `fog` and `mist`), as well as columns that mainly contain zeros, which is not an issue in this case."
|
| 180 |
+
]
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"cell_type": "markdown",
|
| 184 |
+
"id": "7951facc-2835-4b7f-9540-365c33592257",
|
| 185 |
+
"metadata": {},
|
| 186 |
+
"source": [
|
| 187 |
+
"### Selecting columns to retain"
|
| 188 |
+
]
|
| 189 |
+
},
|
| 190 |
+
{
|
| 191 |
+
"cell_type": "markdown",
|
| 192 |
+
"id": "14c6f9b6-dc29-4e6e-9fe4-91487d907c6d",
|
| 193 |
+
"metadata": {},
|
| 194 |
+
"source": [
|
| 195 |
+
"Since our dataset has already been cleaned as part of Greenmorrow’s R&D work, we only need to select the relevant columns for training the machine learning model. Without going into detail, the `year`, `plant_name`, `plant_genus`, and `plant_species` columns can be discarded initially, as they are more likely to be used as filters rather than as features for predicting a plant’s phenological stage (for example, to refine predictions by period or species). \n",
|
| 196 |
+
"\n",
|
| 197 |
+
"In addition, the `pheno_stage_sec` column, which represents the secondary phenological stage (two digits, from 00 to 99), can also be excluded here, as we aim to predict a primary phenological stage (single digit, from 0 to 9), represented by the `pheno_stage_prim` column."
|
| 198 |
+
]
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"cell_type": "code",
|
| 202 |
+
"execution_count": null,
|
| 203 |
+
"id": "d2eb2ab3-60da-47c2-9a1f-00cd83a46ad7",
|
| 204 |
+
"metadata": {},
|
| 205 |
+
"outputs": [],
|
| 206 |
+
"source": [
|
| 207 |
+
"data = data.drop(columns=['year', 'plant_name', 'plant_genus', 'plant_species', 'pheno_stage_sec'])"
|
| 208 |
+
]
|
| 209 |
+
},
|
| 210 |
+
{
|
| 211 |
+
"cell_type": "code",
|
| 212 |
+
"execution_count": null,
|
| 213 |
+
"id": "fc4d48f6-d721-4e02-a24b-00be4bda804c",
|
| 214 |
+
"metadata": {},
|
| 215 |
+
"outputs": [],
|
| 216 |
+
"source": [
|
| 217 |
+
"data.head()"
|
| 218 |
+
]
|
| 219 |
+
},
|
| 220 |
+
{
|
| 221 |
+
"cell_type": "markdown",
|
| 222 |
+
"id": "6fc3b9ce-7f9f-4a99-a72f-fa1409c32cc4",
|
| 223 |
+
"metadata": {},
|
| 224 |
+
"source": [
|
| 225 |
+
"With the dataset ready, we can move on to building and training the model!"
|
| 226 |
+
]
|
| 227 |
+
}
|
| 228 |
+
],
|
| 229 |
+
"metadata": {
|
| 230 |
+
"kernelspec": {
|
| 231 |
+
"display_name": "Python 3 (ipykernel)",
|
| 232 |
+
"language": "python",
|
| 233 |
+
"name": "python3"
|
| 234 |
+
},
|
| 235 |
+
"language_info": {
|
| 236 |
+
"codemirror_mode": {
|
| 237 |
+
"name": "ipython",
|
| 238 |
+
"version": 3
|
| 239 |
+
},
|
| 240 |
+
"file_extension": ".py",
|
| 241 |
+
"mimetype": "text/x-python",
|
| 242 |
+
"name": "python",
|
| 243 |
+
"nbconvert_exporter": "python",
|
| 244 |
+
"pygments_lexer": "ipython3",
|
| 245 |
+
"version": "3.12.9"
|
| 246 |
+
}
|
| 247 |
+
},
|
| 248 |
+
"nbformat": 4,
|
| 249 |
+
"nbformat_minor": 5
|
| 250 |
+
}
|
2_build_and_train_model.ipynb
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"id": "804cef31-4c45-4456-bb2a-6a27680f7c93",
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"source": [
|
| 8 |
+
"# Build and train a Machine Learning model"
|
| 9 |
+
]
|
| 10 |
+
},
|
| 11 |
+
{
|
| 12 |
+
"cell_type": "markdown",
|
| 13 |
+
"id": "8f83ebbe-33dc-4f11-a3a5-414d7f00dd09",
|
| 14 |
+
"metadata": {},
|
| 15 |
+
"source": [
|
| 16 |
+
"## Introduction\n",
|
| 17 |
+
"This file aims to present **the usual process of building and training a machine learning model**.\n",
|
| 18 |
+
"\n",
|
| 19 |
+
"This file is a ***notebook***: it is a format in which you can write code, rich text (Markdown), formulas, and add multimedia content. To learn more, see [the website](https://jupyter.org/) of the Jupyter project.\\\n",
|
| 20 |
+
"Each piece of content is placed in a *cell* that can be executed on the fly, without having to rerun the entire notebook."
|
| 21 |
+
]
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"cell_type": "markdown",
|
| 25 |
+
"id": "1f93155a-5ede-407f-9731-825b9752899e",
|
| 26 |
+
"metadata": {},
|
| 27 |
+
"source": [
|
| 28 |
+
"### Model objective\n",
|
| 29 |
+
"\n",
|
| 30 |
+
"Here, the objective of the trained model is to **predict a plant’s seasonal stage based on the date, location and weather conditions**. This stage is called a *phenological stage*, which may correspond to flowering, fruit ripening, or leaf fall. The phenological stage is generally numbered from 0 to 9, with number 6 corresponding, for example, to flowering:\n",
|
| 31 |
+
"\n",
|
| 32 |
+
"\n",
|
| 33 |
+
"\n",
|
| 34 |
+
"*The BBCH scale. According to “Phenological stages of monocotyledonous and dicotyledonous crops”. U. Meier. Blackwell Wissenschafts-Verlag Berlin. 2001.*\n",
|
| 35 |
+
"\n",
|
| 36 |
+
"A stage can be seen as a category (0, 1, …, 8, 9): therefore, the model to be trained must be based on **a classification algorithm**, supervised by training data. A classification algorithm can be used to address many needs, such as diagnosing a patient (are they affected by a given disease based on their blood test results, yes/no?) or identifying an animal in a photo (cat or dog?)."
|
| 37 |
+
]
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"cell_type": "markdown",
|
| 41 |
+
"id": "a3d8a559-f71c-429a-9fc9-d82748aa7911",
|
| 42 |
+
"metadata": {},
|
| 43 |
+
"source": [
|
| 44 |
+
"## Python package imports"
|
| 45 |
+
]
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"cell_type": "code",
|
| 49 |
+
"execution_count": null,
|
| 50 |
+
"id": "fad0ce31-8a2f-4e4e-a99e-32104e288f6b",
|
| 51 |
+
"metadata": {},
|
| 52 |
+
"outputs": [],
|
| 53 |
+
"source": [
|
| 54 |
+
"import matplotlib.pyplot as plt\n",
|
| 55 |
+
"import numpy as np\n",
|
| 56 |
+
"import pandas as pd\n",
|
| 57 |
+
"\n",
|
| 58 |
+
"from sklearn.compose import ColumnTransformer\n",
|
| 59 |
+
"from sklearn.linear_model import LogisticRegression\n",
|
| 60 |
+
"from sklearn.metrics import accuracy_score, classification_report, ConfusionMatrixDisplay\n",
|
| 61 |
+
"from sklearn.model_selection import GridSearchCV, train_test_split\n",
|
| 62 |
+
"from sklearn.pipeline import Pipeline\n",
|
| 63 |
+
"from sklearn.preprocessing import StandardScaler\n",
|
| 64 |
+
"\n",
|
| 65 |
+
"pd.set_option('display.max_columns', None) # Display all columns of a dataframe"
|
| 66 |
+
]
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"cell_type": "markdown",
|
| 70 |
+
"id": "5f0fb927-4f09-4da9-8533-d09bdd9b8bda",
|
| 71 |
+
"metadata": {},
|
| 72 |
+
"source": [
|
| 73 |
+
"## Data loading and preparation"
|
| 74 |
+
]
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"cell_type": "markdown",
|
| 78 |
+
"id": "9d6f7826-ac8c-4a6b-9b30-9031b67dd0fe",
|
| 79 |
+
"metadata": {},
|
| 80 |
+
"source": [
|
| 81 |
+
"The first step in building a machine learning model is to **collect data, analyse it to understand it, and prepare it** for training the model. Here, we have [a dataset](https://doi.org/10.5281/zenodo.15593446) in Parquet format called `younup_greenmorrow_data.parquet`, which we load using the `pandas` package."
|
| 82 |
+
]
|
| 83 |
+
},
|
| 84 |
+
{
|
| 85 |
+
"cell_type": "code",
|
| 86 |
+
"execution_count": null,
|
| 87 |
+
"id": "482b8bd2-e30c-4e2b-a7df-b9e041b6c7b7",
|
| 88 |
+
"metadata": {},
|
| 89 |
+
"outputs": [],
|
| 90 |
+
"source": [
|
| 91 |
+
"data = pd.read_parquet('younup_greenmorrow_data.parquet')\n",
|
| 92 |
+
"data = data.drop(columns=['year', 'plant_name', 'plant_genus', 'plant_species', 'pheno_stage_sec'])"
|
| 93 |
+
]
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"cell_type": "markdown",
|
| 97 |
+
"id": "42f43d9f-9b1b-498d-8523-dc57621afe10",
|
| 98 |
+
"metadata": {},
|
| 99 |
+
"source": [
|
| 100 |
+
"## Model building"
|
| 101 |
+
]
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"cell_type": "markdown",
|
| 105 |
+
"id": "e8abe49e-de88-4398-bc64-407efc0a2eca",
|
| 106 |
+
"metadata": {},
|
| 107 |
+
"source": [
|
| 108 |
+
"In the case of a supervised classification problem, we want our model to be **trained on data for which we know both the observation conditions (date, location, weather) and the target variable (phenological stage)**. To reuse the medical analogy, the training data would consist, for example, of blood measurements (white blood cell count, cholesterol level, etc.) as observation conditions, along with a column indicating \"yes\" or \"no\" depending on whether the person has the corresponding disease."
|
| 109 |
+
]
|
| 110 |
+
},
|
| 111 |
+
{
|
| 112 |
+
"cell_type": "markdown",
|
| 113 |
+
"id": "d344fe7b-c01e-4f6f-b2dd-76e8a13b614b",
|
| 114 |
+
"metadata": {},
|
| 115 |
+
"source": [
|
| 116 |
+
"### Splitting the variables"
|
| 117 |
+
]
|
| 118 |
+
},
|
| 119 |
+
{
|
| 120 |
+
"cell_type": "markdown",
|
| 121 |
+
"id": "838d943d-6ee8-4088-a5a2-76d3e0e01f0b",
|
| 122 |
+
"metadata": {},
|
| 123 |
+
"source": [
|
| 124 |
+
"Before training the model *stricto sensu*, we therefore need to separate the input variables (`X`) from the target variable (`y`)."
|
| 125 |
+
]
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"cell_type": "code",
|
| 129 |
+
"execution_count": null,
|
| 130 |
+
"id": "7d8b4b53-b37c-4b50-bb0f-6a63801f934e",
|
| 131 |
+
"metadata": {},
|
| 132 |
+
"outputs": [],
|
| 133 |
+
"source": [
|
| 134 |
+
"X = data.drop(columns=['pheno_stage_prim'])\n",
|
| 135 |
+
"y = data['pheno_stage_prim']"
|
| 136 |
+
]
|
| 137 |
+
},
|
| 138 |
+
{
|
| 139 |
+
"cell_type": "markdown",
|
| 140 |
+
"id": "1e5f550f-dfd9-42a7-a8bf-9ff2fc63acc6",
|
| 141 |
+
"metadata": {},
|
| 142 |
+
"source": [
|
| 143 |
+
"### Splitting the training and test data"
|
| 144 |
+
]
|
| 145 |
+
},
|
| 146 |
+
{
|
| 147 |
+
"cell_type": "markdown",
|
| 148 |
+
"id": "601d9d22-d780-4f79-a234-27be26384c21",
|
| 149 |
+
"metadata": {},
|
| 150 |
+
"source": [
|
| 151 |
+
"Next, we need to set aside part of the original data in order to evaluate the model we will have trained. There are several ways to do this, some quite sophisticated, but the simplest is to **randomly split the dataset into a training set and a test set**, keeping the majority of the data for training (typically 80%)."
|
| 152 |
+
]
|
| 153 |
+
},
|
| 154 |
+
{
|
| 155 |
+
"cell_type": "code",
|
| 156 |
+
"execution_count": null,
|
| 157 |
+
"id": "07e75d34-a167-4656-9336-ad287a7721a6",
|
| 158 |
+
"metadata": {},
|
| 159 |
+
"outputs": [],
|
| 160 |
+
"source": [
|
| 161 |
+
"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)"
|
| 162 |
+
]
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
"cell_type": "markdown",
|
| 166 |
+
"id": "97a194f9-d87e-4d5c-9317-433973d84777",
|
| 167 |
+
"metadata": {},
|
| 168 |
+
"source": [
|
| 169 |
+
"A quick check shows that `X_train` indeed contains 80% of the original data (`X`), and `X_test` 20%:"
|
| 170 |
+
]
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"cell_type": "code",
|
| 174 |
+
"execution_count": null,
|
| 175 |
+
"id": "4cd5ad66-ef6f-40df-b986-0a18239a8077",
|
| 176 |
+
"metadata": {},
|
| 177 |
+
"outputs": [],
|
| 178 |
+
"source": [
|
| 179 |
+
"len(X), len(X_train), len(X_test)"
|
| 180 |
+
]
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"cell_type": "markdown",
|
| 184 |
+
"id": "9d71456a-8016-413d-b468-e6353df4e429",
|
| 185 |
+
"metadata": {},
|
| 186 |
+
"source": [
|
| 187 |
+
"### Naive model training"
|
| 188 |
+
]
|
| 189 |
+
},
|
| 190 |
+
{
|
| 191 |
+
"cell_type": "markdown",
|
| 192 |
+
"id": "e19f5c9c-cf4d-4c94-a46c-2e7277151d63",
|
| 193 |
+
"metadata": {},
|
| 194 |
+
"source": [
|
| 195 |
+
"The data has been cleaned, selected and properly split: it is now time to train the model! However, several variable transformation steps may be required:\n",
|
| 196 |
+
"- scaling numerical variables to improve training performance;\n",
|
| 197 |
+
"- binary encoding (0, 1) of categorical variables that are not already encoded, as the model cannot be trained on string values;\n",
|
| 198 |
+
"- resampling the data, in cases where one output class (here, a phenological stage) is much more represented in the dataset than another.\n",
|
| 199 |
+
"\n",
|
| 200 |
+
"In this case, **we choose to apply only feature scaling to the numerical variables** using the `StandardScaler()` class from `scikit-learn`, which subtracts the mean of each variable and divides it by its standard deviation: this has the effect of bringing all numerical variables in the dataset into the interval $[-1, 1]$. As a good practice, we include this step in a `Pipeline()`, which allows all data transformation and model training steps to be handled through a single object.\n",
|
| 201 |
+
"\n",
|
| 202 |
+
"For this example, we choose to train a model based on the logistic regression algorithm (the `LogisticRegression()` class in `scikit-learn`), which is lightweight and easy to interpret, as shown on [this page](https://www.geeksforgeeks.org/machine-learning/understanding-logistic-regression/)."
|
| 203 |
+
]
|
| 204 |
+
},
|
| 205 |
+
{
|
| 206 |
+
"cell_type": "code",
|
| 207 |
+
"execution_count": null,
|
| 208 |
+
"id": "38ed5cd5-76c1-4905-be97-42df13053ef9",
|
| 209 |
+
"metadata": {},
|
| 210 |
+
"outputs": [],
|
| 211 |
+
"source": [
|
| 212 |
+
"# Selection of numerical and categorical variables\n",
|
| 213 |
+
"num_cols = X_train.select_dtypes(include=['int64', 'float64']).columns\n",
|
| 214 |
+
"cat_cols = X_train.select_dtypes(include=['object']).columns\n",
|
| 215 |
+
"\n",
|
| 216 |
+
"# Definition of preprocessing steps\n",
|
| 217 |
+
"preprocessor = ColumnTransformer(\n",
|
| 218 |
+
" transformers=[\n",
|
| 219 |
+
" ('num', StandardScaler(), num_cols), # Scaling of numerical variables\n",
|
| 220 |
+
" ('cat', 'passthrough', cat_cols) # No transformation of categorical variables (there are none in the dataset)\n",
|
| 221 |
+
")\n",
|
| 222 |
+
"\n",
|
| 223 |
+
"# Pipeline definition\n",
|
| 224 |
+
"pipeline = Pipeline(\n",
|
| 225 |
+
" steps=[\n",
|
| 226 |
+
" ('preprocessor', preprocessor), # Applying preprocessing steps\n",
|
| 227 |
+
" ('classifier', LogisticRegression()) # Calling the model to be trained\n",
|
| 228 |
+
" ]\n",
|
| 229 |
+
")\n",
|
| 230 |
+
"\n",
|
| 231 |
+
"# Model training!\n",
|
| 232 |
+
"pipeline.fit(X_train, y_train)"
|
| 233 |
+
]
|
| 234 |
+
},
|
| 235 |
+
{
|
| 236 |
+
"cell_type": "markdown",
|
| 237 |
+
"id": "d4f4bef6-7575-4ef3-b29d-dd40554f4a7a",
|
| 238 |
+
"metadata": {},
|
| 239 |
+
"source": [
|
| 240 |
+
"After training the model using the `fit()` function, the pipeline structure is displayed, making it easier to understand the sequence of steps we have discussed. However, an error may appear, informing us that the model failed to converge, meaning it was unable to properly fit the data. This highlights the limitations of a “naive” training approach, which does not include any tuning and simply relies on the default parameters of the algorithm (here, `LogisticRegression()`).\n",
|
| 241 |
+
"\n",
|
| 242 |
+
"Nevertheless, we can compute the accuracy (between 0 and 1), which measures the model’s ability to correctly predict a phenological stage on the test data (`X_test`, `y_test`):"
|
| 243 |
+
]
|
| 244 |
+
},
|
| 245 |
+
{
|
| 246 |
+
"cell_type": "code",
|
| 247 |
+
"execution_count": null,
|
| 248 |
+
"id": "1b0752ee-3098-41de-801a-5669924b65e9",
|
| 249 |
+
"metadata": {},
|
| 250 |
+
"outputs": [],
|
| 251 |
+
"source": [
|
| 252 |
+
"y_pred = pipeline.predict(X_test)\n",
|
| 253 |
+
"accuracy_score(y_test, y_pred)"
|
| 254 |
+
]
|
| 255 |
+
},
|
| 256 |
+
{
|
| 257 |
+
"cell_type": "markdown",
|
| 258 |
+
"id": "2d2052df-4f1d-41b0-842c-d795348b281d",
|
| 259 |
+
"metadata": {},
|
| 260 |
+
"source": [
|
| 261 |
+
"We can see that the accuracy is only 66%: this indicates **a model that is able to correctly predict the phenological stage in only about two-thirds of cases**. Let’s see how the model can be improved."
|
| 262 |
+
]
|
| 263 |
+
},
|
| 264 |
+
{
|
| 265 |
+
"cell_type": "markdown",
|
| 266 |
+
"id": "26d12dc1-0f1e-4cd2-8460-f274e834285e",
|
| 267 |
+
"metadata": {},
|
| 268 |
+
"source": [
|
| 269 |
+
"## Model optimisation"
|
| 270 |
+
]
|
| 271 |
+
},
|
| 272 |
+
{
|
| 273 |
+
"cell_type": "markdown",
|
| 274 |
+
"id": "f36750a5-9722-4ece-9c73-10b39ff046c9",
|
| 275 |
+
"metadata": {},
|
| 276 |
+
"source": [
|
| 277 |
+
"To improve a model, it is generally necessary to adjust its parameters, known as *hyperparameters*. For `LogisticRegression()`, there are several of these, as shown in the `scikit-learn` documentation. Here, we choose to vary `C`, `penalty`, `solver`, and `max_iter`.\n",
|
| 278 |
+
"\n",
|
| 279 |
+
"To avoid having to test many hyperparameter combinations manually, `scikit-learn` provides automated methods, the most explicit of which is `GridSearchCV()`: this involves **defining a grid of values to test for the chosen hyperparameters**, and specifying the performance metric that `GridSearchCV()` should use to determine which hyperparameter values produce the best model. Naturally, the larger the grid of values to test, the longer the training will take. In addition, warning or error messages may appear when certain tested values are incompatible."
|
| 280 |
+
]
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"cell_type": "code",
|
| 284 |
+
"execution_count": null,
|
| 285 |
+
"id": "1702100f-35d9-46f5-b590-adbecbc1d25e",
|
| 286 |
+
"metadata": {},
|
| 287 |
+
"outputs": [],
|
| 288 |
+
"source": [
|
| 289 |
+
"# Definition of the hyperparameter grid\n",
|
| 290 |
+
"param_grid = {\n",
|
| 291 |
+
" 'classifier__C': [0.1, 1, 10],\n",
|
| 292 |
+
" 'classifier__penalty': ['l2'],\n",
|
| 293 |
+
" 'classifier__solver': ['lbfgs', 'saga'], \n",
|
| 294 |
+
" 'classifier__max_iter': [1000, 1500]\n",
|
| 295 |
+
"}\n",
|
| 296 |
+
"\n",
|
| 297 |
+
"# Definition of grid search\n",
|
| 298 |
+
"grid_search = GridSearchCV(\n",
|
| 299 |
+
" pipeline, # Previously defined pipeline\n",
|
| 300 |
+
" param_grid, # Hyperparameter grid to test\n",
|
| 301 |
+
" n_jobs=-1, # Number of jobs to run in parallel\n",
|
| 302 |
+
" scoring='accuracy' # Performance metric used to select the best model\n",
|
| 303 |
+
")\n",
|
| 304 |
+
"\n",
|
| 305 |
+
"# Training the model with hyperparameter search (⚠️ this may take around ten minutes)\n",
|
| 306 |
+
"grid_search.fit(X_train, y_train)"
|
| 307 |
+
]
|
| 308 |
+
},
|
| 309 |
+
{
|
| 310 |
+
"cell_type": "markdown",
|
| 311 |
+
"id": "6b56b57b-b8ca-4d7f-82a9-5cf0e8bc599d",
|
| 312 |
+
"metadata": {},
|
| 313 |
+
"source": [
|
| 314 |
+
"Similarly to before, we can compute the accuracy (between 0 and 1) using the test data (`X_test`, `y_test`):"
|
| 315 |
+
]
|
| 316 |
+
},
|
| 317 |
+
{
|
| 318 |
+
"cell_type": "code",
|
| 319 |
+
"execution_count": null,
|
| 320 |
+
"id": "8d2c62ee-1da7-4f48-94bf-758e2417e51c",
|
| 321 |
+
"metadata": {},
|
| 322 |
+
"outputs": [],
|
| 323 |
+
"source": [
|
| 324 |
+
"best_model = grid_search.best_estimator_ # Best model obtained from GridSearchCV()\n",
|
| 325 |
+
"\n",
|
| 326 |
+
"y_pred = best_model.predict(X_test)\n",
|
| 327 |
+
"accuracy_score(y_test, y_pred)"
|
| 328 |
+
]
|
| 329 |
+
},
|
| 330 |
+
{
|
| 331 |
+
"cell_type": "markdown",
|
| 332 |
+
"id": "3e8587bf-b3cd-4c62-afa0-5b2385c7d728",
|
| 333 |
+
"metadata": {},
|
| 334 |
+
"source": [
|
| 335 |
+
"In this specific case, hyperparameter optimisation does not appear to have improved the model: this may be due to the simplicity of the chosen algorithm relative to the training data, the limited hyperparameter grid defined for this example, or the imbalance in the dataset (phenological stage 6, corresponding to flowering, is over-represented).\n",
|
| 336 |
+
"\n",
|
| 337 |
+
"It is nevertheless possible to evaluate the model using other indicators, particularly graphical ones."
|
| 338 |
+
]
|
| 339 |
+
},
|
| 340 |
+
{
|
| 341 |
+
"cell_type": "markdown",
|
| 342 |
+
"id": "62eeb09f-d0be-4af4-b3fa-9f58e267ce31",
|
| 343 |
+
"metadata": {},
|
| 344 |
+
"source": [
|
| 345 |
+
"## Model evaluation"
|
| 346 |
+
]
|
| 347 |
+
},
|
| 348 |
+
{
|
| 349 |
+
"cell_type": "markdown",
|
| 350 |
+
"id": "ecc7c246-a518-4516-ad1e-4e59270d79eb",
|
| 351 |
+
"metadata": {},
|
| 352 |
+
"source": [
|
| 353 |
+
"To gain a better understanding of the model’s performance, the `scikit-learn` **classification report** provides various metrics, both for each predictable class (the phenological stages) and for the overall test set (as averages)."
|
| 354 |
+
]
|
| 355 |
+
},
|
| 356 |
+
{
|
| 357 |
+
"cell_type": "code",
|
| 358 |
+
"execution_count": null,
|
| 359 |
+
"id": "2274eb5a-9e18-40fa-aaba-ac5e2abeb2e3",
|
| 360 |
+
"metadata": {},
|
| 361 |
+
"outputs": [],
|
| 362 |
+
"source": [
|
| 363 |
+
"print(classification_report(y_test, y_pred))"
|
| 364 |
+
]
|
| 365 |
+
},
|
| 366 |
+
{
|
| 367 |
+
"cell_type": "markdown",
|
| 368 |
+
"id": "24cb2130-9d00-42e6-aa61-92e31bf34d7e",
|
| 369 |
+
"metadata": {},
|
| 370 |
+
"source": [
|
| 371 |
+
"To go further, we can display a **confusion matrix**, which shows the true values of the target variable compared to the values predicted by the trained model, again using the test data. We can see here that stages 8 and 9 are generally well predicted, but that stage 1 is often confused with stage 6: this information may lead to a deeper analysis of the data in order to make potential corrections."
|
| 372 |
+
]
|
| 373 |
+
},
|
| 374 |
+
{
|
| 375 |
+
"cell_type": "code",
|
| 376 |
+
"execution_count": null,
|
| 377 |
+
"id": "ec92ea7f-e3ad-4382-a71a-5f6f21df055d",
|
| 378 |
+
"metadata": {},
|
| 379 |
+
"outputs": [],
|
| 380 |
+
"source": [
|
| 381 |
+
"_ = ConfusionMatrixDisplay.from_predictions(y_test, y_pred)"
|
| 382 |
+
]
|
| 383 |
+
},
|
| 384 |
+
{
|
| 385 |
+
"cell_type": "markdown",
|
| 386 |
+
"id": "ec84a379-0216-4294-8685-b5a3de7bee1f",
|
| 387 |
+
"metadata": {},
|
| 388 |
+
"source": [
|
| 389 |
+
"Other indicators can also be considered to analyse the model’s performance, such as ROC curves, or the importance of each variable in the prediction, among others."
|
| 390 |
+
]
|
| 391 |
+
}
|
| 392 |
+
],
|
| 393 |
+
"metadata": {
|
| 394 |
+
"kernelspec": {
|
| 395 |
+
"display_name": "Python 3 (ipykernel)",
|
| 396 |
+
"language": "python",
|
| 397 |
+
"name": "python3"
|
| 398 |
+
},
|
| 399 |
+
"language_info": {
|
| 400 |
+
"codemirror_mode": {
|
| 401 |
+
"name": "ipython",
|
| 402 |
+
"version": 3
|
| 403 |
+
},
|
| 404 |
+
"file_extension": ".py",
|
| 405 |
+
"mimetype": "text/x-python",
|
| 406 |
+
"name": "python",
|
| 407 |
+
"nbconvert_exporter": "python",
|
| 408 |
+
"pygments_lexer": "ipython3",
|
| 409 |
+
"version": "3.12.9"
|
| 410 |
+
}
|
| 411 |
+
},
|
| 412 |
+
"nbformat": 4,
|
| 413 |
+
"nbformat_minor": 5
|
| 414 |
+
}
|
README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
---
|
| 2 |
title: TrainGreenmorrowMLModel
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: gray
|
| 5 |
colorTo: green
|
| 6 |
sdk: docker
|
|
|
|
| 1 |
---
|
| 2 |
title: TrainGreenmorrowMLModel
|
| 3 |
+
emoji: 🌱🌲
|
| 4 |
colorFrom: gray
|
| 5 |
colorTo: green
|
| 6 |
sdk: docker
|
login.html
CHANGED
|
@@ -8,10 +8,10 @@
|
|
| 8 |
|
| 9 |
<div id="jupyter-main-app" class="container">
|
| 10 |
|
| 11 |
-
<img src="https://
|
| 12 |
<h4>Welcome to JupyterLab</h4>
|
| 13 |
|
| 14 |
-
<h5>The default token is <span style="color:
|
| 15 |
|
| 16 |
{% if login_available %}
|
| 17 |
{# login_available means password-login is allowed. Show the form. #}
|
|
|
|
| 8 |
|
| 9 |
<div id="jupyter-main-app" class="container">
|
| 10 |
|
| 11 |
+
<img src="https://cdn.prod.website-files.com/625aea37d2019e1dd7782064/62617385c686c2d8d5820bc9_Logo%20Dark.svg" alt="Younup Logo">
|
| 12 |
<h4>Welcome to JupyterLab</h4>
|
| 13 |
|
| 14 |
+
<h5>The default token is <span style="color:rgb(251, 190, 1);">huggingface</span></h5>
|
| 15 |
|
| 16 |
{% if login_available %}
|
| 17 |
{# login_available means password-login is allowed. Show the form. #}
|
younup_greenmorrow_data.parquet
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bc36880cfc100f28ec3546b664b42e1863668489234d0b8a579363b7d13b34e0
|
| 3 |
+
size 857655
|