From 4cbebfe8fd7c431cde14b70e31c6f52c41c63ef3 Mon Sep 17 00:00:00 2001 From: BOTLester <58360400+BOTLester@users.noreply.github.com> Date: Sun, 10 May 2020 23:11:15 +0200 Subject: [PATCH] Creating documentation, small fixes --- Game1/Sources/Crops/Farm.cs | 2 + Game1/Sources/ML/MLModel.cs | 1 - Game1/Sources/Pathing/A-Star/Astar.cs | 10 ++- Oskar_Nastaly_ML_Report.md | 114 ++++++++++++++++++++++++++ example_img.png | Bin 0 -> 4640 bytes 5 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 Oskar_Nastaly_ML_Report.md create mode 100644 example_img.png diff --git a/Game1/Sources/Crops/Farm.cs b/Game1/Sources/Crops/Farm.cs index 7c4ab53..b33d531 100644 --- a/Game1/Sources/Crops/Farm.cs +++ b/Game1/Sources/Crops/Farm.cs @@ -42,6 +42,7 @@ class Farm crops[i, j].init(); } } + /* int dirtCount = 0; for (int i = 0; i < 99; i++) { @@ -60,6 +61,7 @@ class Farm } if (dirtCount == 0) init(Size, housepos); + */ } public void updateFarm(Vector2 Size) diff --git a/Game1/Sources/ML/MLModel.cs b/Game1/Sources/ML/MLModel.cs index 7a1870a..2eec77c 100644 --- a/Game1/Sources/ML/MLModel.cs +++ b/Game1/Sources/ML/MLModel.cs @@ -73,7 +73,6 @@ class MLModel .Text.FeaturizeText("Soil_TypeF", "Soil_Type") .Append(mlContext.Transforms.Text.FeaturizeText("Crop_TypeF", "Crop_Type")) .Append(mlContext.Transforms.Concatenate("Features", "Temperature", "Humidity", "Moisture", "Soil_TypeF", "Crop_TypeF", "Nitrogen", "Potassium", "Phosphorous")) - .Append(mLContext.Transforms.NormalizeMinMax("Features")) .Append(mlContext.Transforms.Conversion.MapValueToKey("Fertilizer_NameF", "Fertilizer_Name"), TransformerScope.TrainTest) .AppendCacheCheckpoint(mLContext) .Append(mLContext.MulticlassClassification.Trainers.LightGbm(options)) diff --git a/Game1/Sources/Pathing/A-Star/Astar.cs b/Game1/Sources/Pathing/A-Star/Astar.cs index 357f39c..ff39f4c 100644 --- a/Game1/Sources/Pathing/A-Star/Astar.cs +++ b/Game1/Sources/Pathing/A-Star/Astar.cs @@ -77,7 +77,7 @@ class Astar else if (Math.Abs(currDir - newDir) == 1 || Math.Abs(currDir - newDir) == 3) return 3; else if (Math.Abs(currDir - newDir) == 0 || Math.Abs(currDir - newDir) == 2) - return 18; + return 9; return 0; } @@ -214,10 +214,16 @@ class Astar } } + while (current != null) + { + path.AddNode(current); + current = current.getParent(); + } + openList.deleteHeap(); closedList.deleteHeap(); - if (current.getCords() != targetPos) + if (path.getByIndex(0).getCords() != targetPos) return false; else return true; diff --git a/Oskar_Nastaly_ML_Report.md b/Oskar_Nastaly_ML_Report.md new file mode 100644 index 0000000..afdfb3f --- /dev/null +++ b/Oskar_Nastaly_ML_Report.md @@ -0,0 +1,114 @@ +# Machine Learning Method implementation report - Oskar Nastały + + +## Introduction + +Purpose of my ML implementation is for the agent (tractor) to decide what fertilizer it should use. +It's decision is mostly based on nutrients in soil, but also on few other properties. +Dataset is very small, it contains only 100 entries. +There are 7 types of fertilizers, each of them adding a specific amount of nutrients to the soil. +Example: +''' + FertilizerType[6] = new Fertilizer + { + ID = 5, + Name = "DAP", + Nitrogen = 14.52f / 5, + Phosphorus = 1.77f / 5, + Potassium = 9.5f / 5 + }; +''' + +Unfortunately values of nutrients are not based on real values. +That is because even though dataset intention (by it's creator) was to be used to classify fertilizers, it looks like instead it says what fertilizer WAS used and what will be the results of using that fertilizer on some field. +E.g: Urea has 46% of Nitrogen in it and nothing else. In dataset it was classified as best fertilizer to be used on fields with already really high Nitrogen levels. That would lead to oversaturation with Nitrogen and lack of other nutrients. +So i did some calculations and Urea now looks like this: + +''' + FertilizerType[7] = new Fertilizer + { + ID = 6, + Name = "Urea", + Nitrogen = 1.81f / 5, + Phosphorus = 21.0f / 5, + Potassium = 9.5f / 5 + }; + // an "inversed" and little modified counterpart of real-world version of this fertilizer. +''' + +## Implementation + +I used Gradient Boosting Decision Tree Algorithm for this task due to many features it has. + +First a csv file is loaded: + +''' + IDataView trainingDataView = mlContext.Data.LoadFromTextFile( + path: path, + hasHeader: true, + separatorChar: ',', + allowQuoting: true, + allowSparse: false); +''' + +Then it is passed to next function which will train, evaluate and build a model. +Also trainer parameters will be fine-tuned here to prevent overfitting as much as possible by: + - limiting number of leaves, + - limiting maximum tree depth, + - limiting the amount of bins per feature, +while maintaining high accuracy by: + - low learning rate combine with + - high number of iterations. + +''' + var options = new LightGbmMulticlassTrainer.Options + { + MaximumBinCountPerFeature = 8, + LearningRate = 0.00025, + NumberOfIterations = 40000, + NumberOfLeaves = 10, + LabelColumnName = "Fertilizer_NameF", + FeatureColumnName = "Features", + + Booster = new DartBooster.Options() + { + MaximumTreeDepth = 10 + } + }; +''' + +Creating pipeline for the model: + +''' + var pipeline = mlContext.Transforms + .Text.FeaturizeText("Soil_TypeF", "Soil_Type") + .Append(mlContext.Transforms.Text.FeaturizeText("Crop_TypeF", "Crop_Type")) + .Append(mlContext.Transforms.Concatenate("Features", "Temperature", "Humidity", "Moisture", "Soil_TypeF", "Crop_TypeF", "Nitrogen", "Potassium", "Phosphorous")) + .Append(mlContext.Transforms.Conversion.MapValueToKey("Fertilizer_NameF", "Fertilizer_Name"), TransformerScope.TrainTest) + .AppendCacheCheckpoint(mLContext) + .Append(mLContext.MulticlassClassification.Trainers.LightGbm(options)) + .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel", "PredictedLabel")); +''' + +Evaluation of the pipeline is done with cross-validation method with 10 folds. +Results are as follow: + +''' + Micro Accuracy: 0.95829 + LogLoss Average: 0.100171 + LogLoss Reduction: 0.933795 +''' + +Model is created and saved for later use, to skip long trainig and evaluation times. +Later that model is loaded and prediction engine is created when program is started. + + +## Integration Details + +Agent (tractor) navigates trough the grid looking for tiles where it can plant some plants. +Upon planting and visitin already growing plants agent decides if any fertilizer is needed (rule based decision), and what fertilizer to use (using ML prediction engine). +If field is properly fertilized it will have higher production rate, resulting in faster growth of a plant. +Production rate value is shown in the UI as well as it is represented by the colour of progression bar (right side of every tile). +At 100% bar will pure **Green**. Any value below will make bar more **Red**, while any value above will add **Blue**, eventually turning bar colour into cyan. + +![Example](https://git.wmi.amu.edu.pl/s425077/PotatoPlan/src/Oskar-ML/example_img.png) diff --git a/example_img.png b/example_img.png new file mode 100644 index 0000000000000000000000000000000000000000..7ab2dc9abfa8d7ccfe4379e5abc71a6245a4f10a GIT binary patch literal 4640 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 DVV2^G literal 0 HcmV?d00001