Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature : Feedback Loop for Ramp #108

Merged
merged 39 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
16e293f
Added color change
kshitijrajsharma Apr 18, 2023
8e7a6d0
changed some css
kshitijrajsharma Apr 18, 2023
30264f8
Added feedback block
kshitijrajsharma Apr 18, 2023
c61e0d6
moved confident to paper
kshitijrajsharma Apr 18, 2023
1bdf2e7
Added feedback models serializers and its curd api
kshitijrajsharma Apr 18, 2023
be11c7a
added feedback post working
kshitijrajsharma Apr 18, 2023
9e48b0d
Added snackbar
kshitijrajsharma Apr 19, 2023
2c8d85c
added feedback map to model editor
kshitijrajsharma Apr 19, 2023
7427a34
added validate and delete buttons
kshitijrajsharma Apr 19, 2023
217b59b
change delete to discard
kshitijrajsharma Apr 19, 2023
e539ca9
added OAM imagery
kshitijrajsharma Apr 19, 2023
ae4bf19
formatted zoomlevels
kshitijrajsharma Apr 19, 2023
3a7c74d
add submitted feature in feedback
kshitijrajsharma Apr 19, 2023
9986b51
Include id of feature on geojson returned
kshitijrajsharma Apr 19, 2023
b5f5c4c
support partial update , only update validate field added patch
kshitijrajsharma Apr 19, 2023
0065b74
Added Gridlayers on prediction
kshitijrajsharma Apr 20, 2023
1fc798e
map refreshses
kshitijrajsharma Apr 20, 2023
cd7082b
added logic for highlighting neighbours
kshitijrajsharma Apr 21, 2023
60dd252
add box of containing feedback to ui
kshitijrajsharma Apr 22, 2023
3358cfa
remove gridlayer and changed grid to yellow
kshitijrajsharma Apr 22, 2023
7cc2fb5
added unique map function
kshitijrajsharma Apr 22, 2023
759f9db
added new method of feedback
kshitijrajsharma Apr 22, 2023
d010a1b
added feedback mapper view , admin feedback map
kshitijrajsharma Apr 27, 2023
a1a8df1
Fix bbox typo import bug
kshitijrajsharma May 2, 2023
fc55e30
Fix bug for serializer validation
kshitijrajsharma May 2, 2023
ccd123c
added bbox method for frontend box plot and dump bbox to geojson in i…
kshitijrajsharma May 3, 2023
6f82d10
remove layergroup from map in prediction
kshitijrajsharma May 3, 2023
a870d95
Added TMS Url to dataset page , Improved validator view for feedbacks
kshitijrajsharma May 8, 2023
bc573d1
Use default confidence level 90 percent
kshitijrajsharma May 14, 2023
2c5061e
Enable zoom level 19
kshitijrajsharma May 15, 2023
d4f1939
added default epochs and batchsize
kshitijrajsharma Jun 8, 2023
fd24277
Added link option for loaded model on predictions
kshitijrajsharma Jun 8, 2023
97ef93a
fix import lib missing
kshitijrajsharma Jun 8, 2023
1e75a00
set epochs and batch size optional on feedback
kshitijrajsharma Jun 8, 2023
e3fb892
add 20 to default checked zoom level as well
kshitijrajsharma Jun 8, 2023
24e33b4
added epochs and batch size for feedback loop
kshitijrajsharma Jun 12, 2023
90c2935
added freeze layers option on both feedback and training itself
kshitijrajsharma Jun 12, 2023
59f6698
added freeze layers in popup
kshitijrajsharma Jun 13, 2023
a710ba3
connected freeze layers in backend
kshitijrajsharma Jun 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ backend/**/__pycache__/**
backend/**/migrations/**
backend/media
backend/data/*
backend/log/*
backend/training/*
backend/.env
backend/config.txt
Expand Down
21 changes: 21 additions & 0 deletions backend/core/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.contrib.gis.db import models as geomodels
from django.contrib.postgres.fields import ArrayField
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from login.models import OsmUser

Expand Down Expand Up @@ -84,3 +85,23 @@ class Training(models.Model):
accuracy = models.FloatField(null=True, blank=True)
epochs = models.PositiveIntegerField()
batch_size = models.PositiveIntegerField()
freeze_layers = models.BooleanField(default=False)


class Feedback(models.Model):
ACTION_TYPE = (
("CREATE", "CREATE"),
("MODIFY", "MODIFY"),
("ACCEPT", "ACCEPT"),
("INITIAL", "INITIAL"),
)
geom = geomodels.GeometryField(srid=4326)
training = models.ForeignKey(Training, to_field="id", on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
zoom_level = models.PositiveIntegerField(
validators=[MinValueValidator(18), MaxValueValidator(23)]
)
action = models.CharField(choices=ACTION_TYPE, max_length=10)
last_modified = models.DateTimeField(auto_now=True)
user = models.ForeignKey(OsmUser, to_field="osm_id", on_delete=models.CASCADE)
validated = models.BooleanField(default=False)
36 changes: 33 additions & 3 deletions backend/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,25 @@ class Meta:
)


class FeedbackSerializer(GeoFeatureModelSerializer):
class Meta:
model = Feedback
geo_field = "geom"
fields = "__all__"
read_only_fields = ("created_at", "last_modified", "user")
partial = True

def create(self, validated_data):
user = self.context["request"].user
validated_data["user"] = user
return super().create(validated_data)

def to_representation(self, instance):
ret = super().to_representation(instance)
ret["properties"]["id"] = instance.id
return ret


class LabelSerializer(
GeoFeatureModelSerializer
): # serializers are used to translate models objects to api
Expand All @@ -84,9 +103,14 @@ class Meta:
model = Label
geo_field = "geom" # this will be used as geometry in order to create geojson api , geofeatureserializer will let you create api in geojson
# auto_bbox = True
fields = (
"osm_id",
) # defining all the fields to be included in curd for now , we can restrict few if we want
fields = ("osm_id",)


class FeedbackFileSerializer(GeoFeatureModelSerializer):
class Meta:
fields = ("training",)
model = Feedback
geo_field = "geom"


class ImageDownloadSerializer(serializers.Serializer):
Expand All @@ -106,6 +130,12 @@ def validate(self, data):
return data


class FeedbackParamSerializer(serializers.Serializer):
training_id = serializers.IntegerField(required=True)
epochs = serializers.IntegerField(required=False)
batch_size = serializers.IntegerField(required=False)
freeze_layers = serializers.BooleanField(required=False)

class PredictionParamSerializer(serializers.Serializer):
bbox = serializers.ListField(child=serializers.FloatField(), required=True)
model_id = serializers.IntegerField(required=True)
Expand Down
143 changes: 111 additions & 32 deletions backend/core/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
import ramp.utils
import tensorflow as tf
from celery import shared_task
from core.models import AOI, Label, Training
from core.serializers import LabelFileSerializer
from core.models import AOI, Feedback, Label, Training
from core.serializers import FeedbackFileSerializer, LabelFileSerializer
from core.utils import bbox, download_imagery, get_start_end_download_coords
from django.conf import settings
from django.contrib.gis.db.models.aggregates import Extent
from django.contrib.gis.geos import GEOSGeometry
from django.shortcuts import get_object_or_404
from django.utils import timezone
from hot_fair_utilities import preprocess, train
from hot_fair_utilities.training import run_feedback

logger = logging.getLogger(__name__)

Expand All @@ -28,9 +31,15 @@

@shared_task
def train_model(
dataset_id, training_id, epochs, batch_size, zoom_level, source_imagery
dataset_id,
training_id,
epochs,
batch_size,
zoom_level,
source_imagery,
feedback=None,
freeze_layers=False,
):

training_instance = get_object_or_404(Training, id=training_id)
training_instance.status = "RUNNING"
training_instance.started_at = timezone.now()
Expand All @@ -46,12 +55,6 @@ def train_model(
with open(log_file, "w") as f:
# redirect stdout to the log file
sys.stdout = f
try:
aois = AOI.objects.filter(dataset=dataset_id)
except AOI.DoesNotExist:
raise ValueError(
f"No AOI is attached with supplied dataset id:{dataset_id}, Create AOI first",
)
training_input_base_path = os.path.join(
settings.TRAINING_WORKSPACE, f"dataset_{dataset_id}"
)
Expand All @@ -61,16 +64,35 @@ def train_model(
if os.path.exists(training_input_image_source): # always build dataset
shutil.rmtree(training_input_image_source)
os.makedirs(training_input_image_source)
for obj in aois:
if feedback:
feedback_objects = Feedback.objects.filter(
training__id=feedback,
validated=True,
)
bbox_feedback = feedback_objects.aggregate(Extent("geom"))[
"geom__extent"
]
bbox_geo = GEOSGeometry(
f"POLYGON(({bbox_feedback[0]} {bbox_feedback[1]},{bbox_feedback[2]} {bbox_feedback[1]},{bbox_feedback[2]} {bbox_feedback[3]},{bbox_feedback[0]} {bbox_feedback[3]},{bbox_feedback[0]} {bbox_feedback[1]}))"
)
print(training_input_image_source)
print(bbox_feedback)
with open(
os.path.join(training_input_image_source, "labels_bbox.geojson"),
"w",
encoding="utf-8",
) as f:
f.write(bbox_geo.geojson)

for z in zoom_level:
zm_level = z
print(
f"""Running Download process for
aoi : {obj.id} - dataset : {dataset_id} , zoom : {zm_level}"""
feedback {training_id} - dataset : {dataset_id} , zoom : {zm_level}"""
)
try:
tile_size = DEFAULT_TILE_SIZE # by default
bbox_coords = bbox(obj.geom.coords[0])
bbox_coords = list(bbox_feedback)
start, end = get_start_end_download_coords(
bbox_coords, zm_level, tile_size
)
Expand All @@ -85,19 +107,58 @@ def train_model(
except Exception as ex:
raise ex

else:
try:
aois = AOI.objects.filter(dataset=dataset_id)
except AOI.DoesNotExist:
raise ValueError(
f"No AOI is attached with supplied dataset id:{dataset_id}, Create AOI first",
)

for obj in aois:
bbox_coords = bbox(obj.geom.coords[0])
for z in zoom_level:
zm_level = z
print(
f"""Running Download process for
aoi : {obj.id} - dataset : {dataset_id} , zoom : {zm_level}"""
)
try:
tile_size = DEFAULT_TILE_SIZE # by default

start, end = get_start_end_download_coords(
bbox_coords, zm_level, tile_size
)
# start downloading
download_imagery(
start,
end,
zm_level,
base_path=training_input_image_source,
source=source_imagery,
)
except Exception as ex:
raise ex

## -----------LABEL GENERATOR---------
aoi_list = [r.id for r in aois]
label = Label.objects.filter(aoi__in=aoi_list).values()
serialized_field = LabelFileSerializer(data=list(label), many=True)
logging.debug("Label Generator started")
if feedback:
feedback_objects = Feedback.objects.filter(
training__id=feedback,
validated=True,
)
serialized_field = FeedbackFileSerializer(feedback_objects, many=True)
else:
aoi_list = [r.id for r in aois]
label = Label.objects.filter(aoi__in=aoi_list)
serialized_field = LabelFileSerializer(label, many=True)

if serialized_field.is_valid(raise_exception=True):
with open(
os.path.join(training_input_image_source, "labels.geojson"),
"w",
encoding="utf-8",
) as f:
f.write(json.dumps(serialized_field.data))
f.close()
with open(
os.path.join(training_input_image_source, "labels.geojson"),
"w",
encoding="utf-8",
) as f:
f.write(json.dumps(serialized_field.data))

## --------- Data Preparation ----------
base_path = os.path.join(settings.RAMP_HOME, "ramp-data", str(dataset_id))
Expand Down Expand Up @@ -130,14 +191,32 @@ def train_model(
# train

train_output = f"{base_path}/train"
final_accuracy, final_model_path = train(
input_path=preprocess_output,
output_path=train_output,
epoch_size=epochs,
batch_size=batch_size,
model="ramp",
model_home=os.environ["RAMP_HOME"],
)
if feedback:
final_accuracy, final_model_path = run_feedback(
input_path=preprocess_output,
output_path=train_output,
feedback_base_model=os.path.join(
settings.TRAINING_WORKSPACE,
f"dataset_{dataset_id}",
"output",
f"training_{feedback}",
"checkpoint.tf",
),
model_home=os.environ["RAMP_HOME"],
epoch_size=epochs,
batch_size=batch_size,
freeze_layers=freeze_layers,
)
else:
final_accuracy, final_model_path = train(
input_path=preprocess_output,
output_path=train_output,
epoch_size=epochs,
batch_size=batch_size,
model="ramp",
model_home=os.environ["RAMP_HOME"],
freeze_layers=freeze_layers,
)

# copy final model to output
output_path = os.path.join(
Expand Down
4 changes: 4 additions & 0 deletions backend/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
AOIViewSet,
APIStatus,
DatasetViewSet,
FeedbackView,
FeedbackViewset,
GenerateGpxView,
LabelViewSet,
ModelViewSet,
Expand All @@ -28,6 +30,7 @@
router.register(r"label", LabelViewSet)
router.register(r"training", TrainingViewSet)
router.register(r"model", ModelViewSet)
router.register(r"feedback", FeedbackViewset)


urlpatterns = [
Expand All @@ -37,6 +40,7 @@
path("training/status/<str:run_id>/", run_task_status),
path("training/publish/<int:training_id>/", publish_training),
path("prediction/", PredictionView.as_view()),
path("apply/feedback/", FeedbackView.as_view()),
path("status/", APIStatus.as_view()),
path("geojson2osm/", geojson2osmconverter, name="geojson2osmconverter"),
path("aoi/gpx/<int:aoi_id>/", GenerateGpxView.as_view()),
Expand Down
Loading