Skip to content

Instantly share code, notes, and snippets.

@KMarkert
Created September 22, 2020 15:05
Show Gist options
  • Save KMarkert/32200f444ac8032e3e429cc8d6e3bd6d to your computer and use it in GitHub Desktop.
Save KMarkert/32200f444ac8032e3e429cc8d6e3bd6d to your computer and use it in GitHub Desktop.
silvacarbon-gee-tensorflow.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "silvacarbon-gee-tensorflow.ipynb",
"provenance": [],
"private_outputs": true,
"collapsed_sections": [],
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/KMarkert/32200f444ac8032e3e429cc8d6e3bd6d/silvacarbon-gee-tensorflow.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "avyV6lS_BZC1",
"colab_type": "text"
},
"source": [
"# Setting up our environment\n",
"\n",
"Within this notebook we will use a combination of packages within Python for our workflow. To use these packages we either have to import the package if it is already installed or install the package then import. Additionally, *you* will need to be authorized to run certain portions of the workflow so we will do some authenticating."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "BXlJJ6gMEOlm",
"colab_type": "text"
},
"source": [
"The following blocks will either import Python packages that will be used in this notebook, install and import packages, or prompt authentication."
]
},
{
"cell_type": "code",
"metadata": {
"id": "Cm4sV9JQVxTv",
"colab_type": "code",
"colab": {}
},
"source": [
"# import basin installed packages\n",
"import math\n",
"import json\n",
"import subprocess\n",
"import numpy as np\n",
"from pprint import pprint\n",
"from google.colab import auth\n",
"from google.colab import drive\n",
"\n",
"# interactive visualization inline\n",
"%pylab inline"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "3UQEUy-BEVwJ",
"colab_type": "code",
"colab": {}
},
"source": [
"# Authenticate with our Google account so Colab can access data\n",
"auth.authenticate_user()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "wx0a9VlMWAM9",
"colab_type": "code",
"colab": {}
},
"source": [
"# check if the earth engine python api is install\n",
"# if not, install EarthEngine API and authenticate\n",
"try:\n",
" import ee\n",
" ee.Initialize()\n",
"\n",
"except ImportError:\n",
" import getpass\n",
" !pip install earthengine-api --quiet\n",
" !earthengine --no-use_cloud_api authenticate --quiet\n",
"\n",
" authCode = getpass.getpass(\"Authorization code:\")\n",
" \n",
" !earthengine --no-use_cloud_api authenticate --authorization-code=$authCode --quiet\n",
"\n",
" import ee\n",
" ee.Initialize()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "cxC_SyYNYK01",
"colab_type": "code",
"colab": {}
},
"source": [
"# install the *new* TensorFlow 2.0 API\n",
"!pip install tensorflow==2.0.0-beta1 --quiet\n",
"\n",
"# import tf specific modules for building the net\n",
"import tensorflow as tf\n",
"from tensorflow.python.keras import models\n",
"from tensorflow.python.keras import layers\n",
"from tensorflow.python.keras import backend as K\n",
"from tensorflow.python.keras.layers import Lambda\n",
"\n",
"print('\\nUsing TensorFlow version {}'.format(tf.__version__))"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "9sHlXSLLYRpk",
"colab_type": "code",
"colab": {}
},
"source": [
"# import visualizaition pacakge and define a helper for later on\n",
"import folium\n",
"from IPython.display import display\n",
"\n",
"# helper class to create an interactive map with ee.Objects\n",
"# somewhat mimics the EE code editor's Map (poor man's version)\n",
"class eeMap:\n",
" def __init__(self,center=[0, 0],zoom=3,width=1200,height=500,basemap='cartodbpositron',**kwargs):\n",
" self.fig = folium.Figure(width=width, height=height)\n",
" self._map = folium.Map(location=center,zoom_start=zoom,tiles=basemap,**kwargs)\n",
" return\n",
"\n",
" def show(self):\n",
" self._map.add_child(folium.LayerControl())\n",
" self.fig.add_child(self._map)\n",
" return display(self.fig)\n",
"\n",
" def addLayer(self,eeObj,vizParams=None,name=None):\n",
" map_id = eeObj.getMapId(vizParams)\n",
" tile_url_template = \"https://earthengine.googleapis.com/map/{mapid}/{{z}}/{{x}}/{{y}}?token={token}\"\n",
" mapurl = tile_url_template.format(**map_id)\n",
" self._map.add_child(folium.WmsTileLayer(mapurl,name=name,attr='Google Earth Engine'))\n",
"\n",
" return"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "7xn8OMP9ISRb",
"colab_type": "text"
},
"source": [
"# Working with Earth Engine to get data\n",
"\n",
"Now that we have our environment set up, we can start calling Earth Engine data to process and export what we need for our TensorFlow model.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "QeLO8Ip7IhtK",
"colab_type": "text"
},
"source": [
"First thing, we need a region to process."
]
},
{
"cell_type": "code",
"metadata": {
"id": "WORKg-fSa4n2",
"colab_type": "code",
"colab": {}
},
"source": [
"seed = 7 # random seed for replicability throughout the workflow\n",
"\n",
"# predefined region\n",
"region = ee.Geometry.Rectangle([-75.0, 0.0, -70.0, 5.0])\n",
"\n",
"empty = ee.Image().byte(); \n",
"bbox = empty.paint(\n",
" featureCollection= ee.FeatureCollection([region]),\n",
" color= 1,\n",
" width= 2\n",
")\n",
"\n",
"# visualize our region\n",
"Map = eeMap(center=[2.5,-72.5],zoom=6)\n",
"Map.addLayer(bbox,{},'Study Area')\n",
"Map.show()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "Sn7rIeaScBI5",
"colab_type": "text"
},
"source": [
"Next, we are going to get the imagery to process. There is an image asset already created so we will not have to wait too long to process it. This is a median composite from BRDF corrected data for 2017-2019. This is what we will use as features for our TF model.\n",
"\n",
"If you want to create your own image composite from Landsat 8 surface reflectance data, check out [this script](https://code.earthengine.google.com/6405ede6762cfe03171ad9c5a5fb4fd7)."
]
},
{
"cell_type": "code",
"metadata": {
"id": "3SbpG16jZkoX",
"colab_type": "code",
"colab": {}
},
"source": [
"img = ee.Image('users/kelmarkert/public/silvacarbon-tf-composite')\n",
"\n",
"# play around with the visualization parameters to explore the data\n",
"display_bands = ['swir2','nir','green']\n",
"vmin = 50\n",
"vmax = 5500\n",
"vgamma = 1.5\n",
"\n",
"Map = eeMap(center=[2.5,-72.5],zoom=6)\n",
"Map.addLayer(img,{'bands':display_bands,'min':vmin,'max':vmax,'gamma':vgamma},'Composite Image')\n",
"Map.addLayer(bbox,{},'AoI')\n",
"Map.show()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "FRVgds1ckU2x",
"colab_type": "text"
},
"source": [
"Now we want to select the features (bands) that will be used to train and ultimately predict within our model. We do this by selecting the bands that we want from the image."
]
},
{
"cell_type": "code",
"metadata": {
"id": "h6yzb_0oiPAq",
"colab_type": "code",
"colab": {}
},
"source": [
"# band names of the predictors we are going to use\n",
"# we can change these (try different ones!!!)\n",
"predictors = ['red','nir','swir1','swir2','ndbi','ndwi','evi2','mndwi']\n",
"label = ['label']\n",
"\n",
"# cast the predictor list as an ee.List\n",
"eePredictors = ee.List(predictors)\n",
"\n",
"featureImg = img.select(eePredictors)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "O0VaJZMVi6cR",
"colab_type": "text"
},
"source": [
"Usually with ML we want our [data to be normalized](https://medium.com/@urvashilluniya/why-data-normalization-is-necessary-for-machine-learning-models-681b65a05029) some how if the features have different ranges (such as surface reflectance [0-1] vs index [-1-1]). Since that is our case for this example, we are going to do a minimum/maximum normalization so all of our data is from 0-1. If you are feeling adventurous, you should try to normalize your data by standard deviation!!!\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "srYP6Wm9fQvo",
"colab_type": "code",
"colab": {}
},
"source": [
"# function to scale an image between the min and max value\n",
"def minMaxScalar(image,region,bandList,scale=1000):\n",
" def applyScaling(band):\n",
" band = ee.String(band)\n",
" imin = ee.Number(minMax.get(band.cat('_min')))\n",
" imax = ee.Number(minMax.get(band.cat('_max')))\n",
" return image.select(band).unitScale(imin,imax)\n",
"\n",
" # force cast of bandList\n",
" bandList = ee.List(bandList)\n",
"\n",
" # get the min/max values for each band over the region\n",
" minMax = image.reduceRegion(\n",
" reducer= ee.Reducer.minMax(),\n",
" geometry= region,\n",
" scale= scale,\n",
" bestEffort= True\n",
" )\n",
"\n",
" # map over each band and scale between the min and max\n",
" # returns an image with the original predictor names\n",
" normalized = ee.ImageCollection(bandList.map(applyScaling)).toBands()\\\n",
" .select(ee.List.sequence(0,bandList.size().subtract(1)),bandList)\n",
"\n",
" return normalized\n",
"\n",
"# apply the scaling on an image for a region and specific bands\n",
"normalized = minMaxScalar(img,region,eePredictors)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "m83cLb1SUqb8",
"colab_type": "text"
},
"source": [
"We have our features for training the model, now we need label data. Because deep learning models need a lot of training data, we are going to use the Hansen forest cover dataset to create a \"truth\" dataset.\n",
"\n",
"For semantic segmentation, we need an image of segmentations to classify hence we use the image here versus the traditional forest/non-forest."
]
},
{
"cell_type": "code",
"metadata": {
"id": "QCj26em5cfzV",
"colab_type": "code",
"colab": {}
},
"source": [
"hansenData = ee.Image('UMD/hansen/global_forest_change_2018_v1_6')\n",
"\n",
"forestThreshold = 90\n",
"\n",
"forest = hansenData.select('treecover2000').gte(forestThreshold)\\\n",
" .And(hansenData.select('loss').eq(0))\n",
"\n",
"# Define a kernel.\n",
"kernel = ee.Kernel.circle(radius= 1);\n",
"\n",
"#Perform an erosion followed by a dilation, display.\n",
"labels = forest.clip(region)\\\n",
" .focal_min(kernel= kernel, iterations= 2)\\\n",
" .focal_max(kernel= kernel, iterations= 2)\\\n",
" .focal_mode()\\\n",
" .rename(label)\n",
"\n",
"Map = eeMap(center=[2.5,-72.5],zoom=6)\n",
"Map.addLayer(labels.updateMask(labels),{'palette':'green'},'Forest Label Image')\n",
"Map.addLayer(bbox,{},'AoI')\n",
"Map.show()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "242wampYDMo4",
"colab_type": "text"
},
"source": [
"Here we define our window size/block size that we will use in the model for training and prediction. In Earth Engine, we convert an image to neighborhoods."
]
},
{
"cell_type": "code",
"metadata": {
"id": "fyKSTUCOcfHy",
"colab_type": "code",
"colab": {}
},
"source": [
"# Create a 256x256 patch per pixel\n",
"# first we build a kernel\n",
"kernelSize = 64\n",
"list_ = ee.List.repeat(1, kernelSize)\n",
"lists = ee.List.repeat(list_, kernelSize)\n",
"kernel = ee.Kernel.fixed(kernelSize, kernelSize, lists)\n",
"\n",
"# then we convert the feature image with labels to 256x256 neighborhoods\n",
"with_neighborhood = normalized.addBands(labels.unmask(0)).neighborhoodToArray(kernel);"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "WYKlfdH3Dd6G",
"colab_type": "text"
},
"source": [
"We have to specify where our samples for training/testing to export from EE. In this case we use a simple random sample, for a much more robust training/testing dataset you would need to perfom a better sampling."
]
},
{
"cell_type": "code",
"metadata": {
"id": "pY44rcFZYDoc",
"colab_type": "code",
"colab": {}
},
"source": [
"# helper function to convert FeatureCollection \n",
"# of points to a FeatureCollection of boxes\n",
"def pt_to_region(feature):\n",
" # we buffer points by 7680 (=256x30) and return the bounding box\n",
" return feature.buffer(30*kernelSize).bounds();\n",
"\n",
"nPts = 300 # change number of points if desired\n",
"\n",
"# create a feature collection with a random sample\n",
"pts = ee.FeatureCollection.randomPoints(region,nPts,seed)\n",
"\n",
"exampleRegions = pts.map(pt_to_region)\n",
"\n",
"# visualization \n",
"outlines = empty.paint(\n",
" featureCollection= exampleRegions,\n",
" color= 1,\n",
" width= 2\n",
")\n",
"\n",
"Map = eeMap(center=[2.5,-72.5],zoom=6)\n",
"Map.addLayer(pts,{'color':'red'},'Sample Points')\n",
"Map.addLayer(outlines,{},'Example Regions')\n",
"Map.show()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "M0fKg5HED0AP",
"colab_type": "text"
},
"source": [
"Now we sample the neighborhoods from the image for the center points of our sample."
]
},
{
"cell_type": "code",
"metadata": {
"id": "XFHS0o5ZjlYR",
"colab_type": "code",
"colab": {}
},
"source": [
"train_size = 0.7 # percent of samples to use for training vs testing\n",
"\n",
"# sample the neighborhoods over the sampling points \n",
"samples = with_neighborhood.sampleRegions(\n",
" collection= pts,\n",
" tileScale= 16\n",
").randomColumn('random',seed);\n",
"\n",
"training = samples.filter(ee.Filter.lte('random',train_size))\n",
"testing = samples.filter(ee.Filter.gt('random',train_size))\n",
"\n",
"feature_names = predictors+['label']"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "evXJW3ysdgsi",
"colab_type": "text"
},
"source": [
"All of the data resides on Earth Engine right now so we will need to move the data to a Google Cloud Storage bucket to run TensorFlow with it. Here we tee up export tasks and kick it off programmatically.\n",
"\n",
"You have been given read/write permissions to the folder so we can export the data together. ***Important***: change the `myfolder` variable to your name so that the exports and further processing are within \"your\" area. It can get very confusing if we all write to the same directory...also, please don't delete data to play around with your friend"
]
},
{
"cell_type": "code",
"metadata": {
"id": "rMty8SQhdMod",
"colab_type": "code",
"colab": {}
},
"source": [
"# *** change this variable to your name so we keep the storage bucket organized\n",
"myfolder = <YOUR_NAME>\n",
"\n",
"# GCS bucket to write data to, we will create a folder on there specifically for you\n",
"silvacarbonBucket = 'silvacarbon-gee-tf'\n",
"\n",
"# export our data in TFRecords to GCP Storage for TensorFlow!\n",
"trainingExport = ee.batch.Export.table.toCloudStorage(\n",
" collection= training,\n",
" bucket= silvacarbonBucket,\n",
" fileNamePrefix= myfolder+'/silvacarbon_tf_forest_training_',\n",
" fileFormat= 'TFRecord',\n",
" description= 'silvacarbon_tf_forest_training',\n",
" selectors= feature_names\n",
")\n",
"trainingExport.start()\n",
"\n",
"testingExport = ee.batch.Export.table.toCloudStorage(\n",
" collection= testing,\n",
" bucket= silvacarbonBucket,\n",
" fileNamePrefix= myfolder+'/silvacarbon_tf_forest_testing_',\n",
" fileFormat= 'TFRecord',\n",
" description= 'silvacarbon_tf_forest_testing',\n",
" selectors= feature_names\n",
")\n",
"testingExport.start()\n",
"\n",
"# get serializable geometry for image export\n",
"exportRegion = region.bounds().getInfo()['coordinates']\n",
"\n",
"imageExport = ee.batch.Export.image.toCloudStorage(\n",
" image= normalized.toFloat(),\n",
" region= exportRegion,\n",
" bucket = silvacarbonBucket,\n",
" description= 'silvacarbon_tf_forest_image',\n",
" fileNamePrefix= myfolder+'/silvacarbon_tf_forest_image_',\n",
" scale= 30,\n",
" maxPixels= 1e13,\n",
" fileFormat= 'TFRecord',\n",
" formatOptions= {\n",
" 'patchDimensions': [256,256],\n",
" 'kernelSize': [kernelSize//2, kernelSize//2],\n",
" 'compressed': True,\n",
" 'maxFileSize': 104857600\n",
" }\n",
")\n",
"# imageExport.start()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "JztYdoQja2o5",
"colab_type": "code",
"colab": {}
},
"source": [
"pprint(ee.batch.Task.list()[:3])"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "CpnSi2eni_rc",
"colab_type": "text"
},
"source": [
"![Wait meme](data:image/jpeg;base64,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)\n",
"\n",
"...about 15min"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "XCumkJvDzzyo",
"colab_type": "text"
},
"source": [
"Last a little setting of variables...we want to be able to call the file names we just exported so we are going to query the storage folder where our exports are and parse out which file is for training, testing, etc."
]
},
{
"cell_type": "code",
"metadata": {
"id": "Je9gu2veWLJU",
"colab_type": "code",
"colab": {}
},
"source": [
"cmd = 'gsutil ls gs://{}/{}'.format(silvacarbonBucket,myfolder)\n",
"proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n",
"out, err = proc.communicate()\n",
"flist = out.decode()[:-1].split('\\n')\n",
"\n",
"mixerFile = [flist.pop(i) for i,f in enumerate(flist) if 'mixer' in f][0]\n",
"testFile = [flist.pop(i) for i,f in enumerate(flist) if 'testing' in f][0]\n",
"trainFile = [flist.pop(i) for i,f in enumerate(flist) if 'training' in f][0]\n",
"predictFiles = flist[:]\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "sLgHrB5dOhHX",
"colab_type": "text"
},
"source": [
"Yay! Our data is exported. We get to start playing around with TensorFlow now. \n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "PxV7uU7ruMm9",
"colab_type": "text"
},
"source": [
"# Setting up our TensorFlow model and training\n",
"\n",
"In this section we will focus on creating a deep learning model to map forest, read in our traning/testing data, and train the model."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "-MBScwKf3-fI",
"colab_type": "text"
},
"source": [
"\n",
"There is a popular model architechure that is the basic concept for semantic segmentation, the [U-Net](https://arxiv.org/abs/1505.04597), that we will implement to predict forested areas. Although the U-Net was originally developed for biomedical imaging, it works really well on satellite imagery. Below is a schematic of the model.\n",
"\n",
"![U-Net Architecture](https://i.stack.imgur.com/EtyQs.png)\n",
"\n",
"Now, let's implement the code.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "uX08V6lNW1pf",
"colab_type": "code",
"colab": {}
},
"source": [
"# top-level function that is the network\n",
"def unet():\n",
" # A set of helper functions for defining a convolutional layer. We use batch\n",
" # normalization to speed up training given our limited training data, therefore\n",
" # we can't use vanilla conv2d(activation='relu', ...)\n",
" def conv_block(input_tensor, num_filters):\n",
" encoder = layers.Conv2D(num_filters, (3, 3), padding='same')(input_tensor)\n",
" encoder = layers.BatchNormalization()(encoder)\n",
" encoder = layers.Activation('relu')(encoder)\n",
" encoder = layers.Conv2D(num_filters, (3, 3), padding='same')(encoder)\n",
" encoder = layers.BatchNormalization()(encoder)\n",
" encoder = layers.Activation('relu')(encoder)\n",
" return encoder\n",
"\n",
" def encoder_block(input_tensor, num_filters):\n",
" encoder = conv_block(input_tensor, num_filters)\n",
" encoder_pool = layers.MaxPooling2D((2, 2), strides=(2, 2))(encoder)\n",
" return encoder_pool, encoder\n",
"\n",
" def decoder_block(input_tensor, concat_tensor, num_filters):\n",
" decoder = layers.Conv2DTranspose(num_filters, (2, 2), strides=(2, 2), padding='same')(input_tensor)\n",
" decoder = layers.concatenate([concat_tensor, decoder], axis=-1)\n",
" decoder = layers.BatchNormalization()(decoder)\n",
" decoder = layers.Activation('relu')(decoder)\n",
" decoder = layers.Conv2D(num_filters, (3, 3), padding='same')(decoder)\n",
" decoder = layers.BatchNormalization()(decoder)\n",
" decoder = layers.Activation('relu')(decoder)\n",
" decoder = layers.Conv2D(num_filters, (3, 3), padding='same')(decoder)\n",
" decoder = layers.BatchNormalization()(decoder)\n",
" decoder = layers.Activation('relu')(decoder)\n",
" return decoder\n",
"\n",
" inputs = layers.Input(shape=[None, None, len(predictors)])\n",
" # start encoding the image\n",
" encoder0_pool, encoder0 = encoder_block(inputs, 16)\n",
" encoder1_pool, encoder1 = encoder_block(encoder0_pool, 32) \n",
" encoder2_pool, encoder2 = encoder_block(encoder1_pool, 64)\n",
" # fully convolutional center\n",
" center = conv_block(encoder2_pool, 128) # center\n",
" # decode/upsample the convoluted image\n",
" decoder2 = decoder_block(center, encoder2, 64) \n",
" decoder1 = decoder_block(decoder2, encoder1, 32)\n",
" decoder0 = decoder_block(decoder1, encoder0, 16) \n",
" # predict final output using a softmax activation\n",
" # This is suitable for a [0,1] response. Use linear otherwise.\n",
" outputs = layers.Conv2D(1, (1, 1),activation='sigmoid')(decoder0)\n",
" return inputs,outputs\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "DLK0hjek5j3z",
"colab_type": "text"
},
"source": [
"Here we define a custom loss function, the [soft dice loss](https://arxiv.org/abs/1606.04797), to guide the optimization during training. This loss function has proven to perform well for semantic segmentation models.\n",
"\n",
"<!-- $loss = 1- \\frac{2 \\cdot \\sum Y_{true} \\cdot Y_{pred}}{\\sum Y_{true}^2 + \\sum Y_{pred}^2}$ -->"
]
},
{
"cell_type": "code",
"metadata": {
"id": "KyWqYenZ5Ave",
"colab_type": "code",
"colab": {}
},
"source": [
"def dice_loss(y_true, y_pred, smooth=1):\n",
" intersection = K.sum(K.abs(y_true * y_pred), axis=-1)\n",
" true_sum = K.sum(K.square(y_true),-1) \n",
" pred_sum = K.sum(K.square(y_pred),-1)\n",
" return 1 - ((2. * intersection + smooth) / (true_sum + pred_sum + smooth))"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "NfMIVyIxw2yT",
"colab_type": "text"
},
"source": [
"Here we compile the model using an [Adam optimizer](https://arxiv.org/abs/1412.6980v8) and the custom loss function we wrote."
]
},
{
"cell_type": "code",
"metadata": {
"id": "GU_rMA4Yd_Cc",
"colab_type": "code",
"colab": {}
},
"source": [
"# get the input and output from the unet function\n",
"ins,outs = unet()\n",
"# and define a keras.model from the inputs/outputs\n",
"model = models.Model(inputs=[ins], outputs=[outs])\n",
"\n",
"# compile model with Adam optimizer and soft dice loss\n",
"model.compile(optimizer='adam', loss=dice_loss, metrics=['accuracy'])\n",
"\n",
"model.summary()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "nU7pJZk4xwWX",
"colab_type": "text"
},
"source": [
" We now have to write a custom function to parse our inputs so we can use the data for training. We do this be extracting the parsing out individual feature information and creating [Tensor objects](https://www.tensorflow.org/guide/tensors) from the features."
]
},
{
"cell_type": "code",
"metadata": {
"id": "NzENQqJB0hi7",
"colab_type": "code",
"colab": {}
},
"source": [
"# Our input function will return n features, each a 'side' x 'side' tensor\n",
"# representing the 'label' image \n",
"# Since out data is in a TFRecord format we will need to use TF for parsing\n",
"def input_fn(fileNames,predictors,label, shuffle=True, training=True, batchSize=100, kernelShape=[64,64]):\n",
" \n",
" features = list(predictors)\n",
" features = features + label\n",
" columns = [\n",
" tf.io.FixedLenFeature(shape=kernelShape, dtype=tf.float32) for k in features\n",
" ] \n",
" feature_columns = dict(zip(features, columns))\n",
" \n",
" ds = tf.data.TFRecordDataset(fileNames, compression_type='GZIP')\n",
"\n",
" def parse(example_proto):\n",
" parsed_features = tf.io.parse_single_example(example_proto, feature_columns)\n",
" # Separate the class labels from the training features\n",
" labels = {l:parsed_features.pop(l) for l in label}\n",
" return parsed_features, labels\n",
"\n",
" ds = ds.map(parse, num_parallel_calls=5)\n",
" \n",
" def toTuple(inputs, labels):\n",
" inputsList = [inputs.get(key) for key in predictors]\n",
" stacked = tf.stack(inputsList, axis=0)\n",
" # Convert from CHW to HWC\n",
" stacked = tf.transpose(stacked, [1, 2, 0])\n",
"\n",
" # expand the dimensions of th label tensor to be CHW\n",
" labelsList = [labels.get(key) for key in label]\n",
" lstacked = tf.stack(labelsList, axis=0)\n",
" # Convert from CHW to HWC\n",
" lstacked = tf.transpose(lstacked, [1, 2, 0])\n",
"\n",
" return stacked, lstacked\n",
" \n",
" def udFlipInputs(inputs,labels):\n",
" return tf.image.flip_up_down(inputs), tf.image.flip_up_down(labels)\n",
" \n",
" def lrFlipInputs(inputs,labels):\n",
" return tf.image.flip_left_right(inputs), tf.image.flip_left_right(labels)\n",
" \n",
" def transpInputs(inputs,labels):\n",
" ud = tf.image.flip_up_down(inputs)\n",
" trans = tf.image.flip_left_right(ud)\n",
" udl = tf.image.flip_up_down(labels)\n",
" transl = tf.image.flip_left_right(udl)\n",
" return trans, transl\n",
" \n",
" def rotateInputs(inputs,labels): \n",
" return tf.image.rot90(inputs), tf.image.rot90(labels)\n",
" \n",
" ds = ds.map(toTuple)\n",
" \n",
" # because we have limited examples for training, we will do\n",
" # some rotating and flipping of the images to synthetically\n",
" # increase our training datasets\n",
" if training:\n",
" ds = ds.concatenate(ds.map(udFlipInputs))\\\n",
" .concatenate(ds.map(lrFlipInputs))\\\n",
" .concatenate(ds.map(transpInputs))\\\n",
" .concatenate(ds.map(rotateInputs))\n",
" \n",
" if shuffle:\n",
" # We choose 10 since, with a batch size of 100, we'll keep 1000 (approx.\n",
" # the size of the training data) examples in memory for the shuffle\n",
" ds = ds.shuffle(buffer_size=batchSize * 10)\n",
" \n",
" ds = ds.batch(batchSize).repeat()\n",
"\n",
" iterator = tf.compat.v1.data.make_one_shot_iterator(ds)\n",
" features, labels = iterator.get_next()\n",
" return features,labels"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "kWewAGE0B0a4",
"colab_type": "text"
},
"source": [
"Cool, we have out model and we have our data so let's train the model!!!"
]
},
{
"cell_type": "code",
"metadata": {
"id": "XyuiQ8Vr7iq_",
"colab_type": "code",
"colab": {}
},
"source": [
"nEpochs = 10 # number of epochs to run\n",
"batchSize = 100 # number of examples to run before batch u\n",
"nSteps = batchSize//nEpochs # number of steps per epoch (nExamples = nSteps * batchSize)\n",
"kernelShape = [kernelSize,kernelSize] # H x W size of tensors to parse\n",
"\n",
"training_features,training_label = input_fn(trainFile,predictors,label,batchSize=batchSize,kernelShape=kernelShape)\n",
"\n",
"training = model.fit(x=training_features,y=training_label,epochs=nEpochs,steps_per_epoch=nSteps)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "9rPCqJCVBUU-",
"colab_type": "code",
"cellView": "both",
"colab": {}
},
"source": [
"#@title Plot training results\n",
"\n",
"fig,ax = plt.subplots(ncols=2,sharex=True,figsize=(15,5))\n",
"ax[0].plot(range(1,nEpochs+1),training.history['loss'],marker='o',label='Training Loss')\n",
"ax[0].set_title('Training Loss')\n",
"ax[0].set_xlabel('Epoch')\n",
"\n",
"ax[1].plot(range(1,nEpochs+1),training.history['accuracy'],marker='o',label='Training Accuracy')\n",
"ax[1].set_title('Training Accuracy')\n",
"ax[1].set_xticks(range(1,nEpochs+1))\n",
"ax[1].set_xlabel('Epoch')\n",
"plt.show()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "ZNxQ8QQZ6LGh",
"colab_type": "code",
"colab": {}
},
"source": [
"# parse testing dataset\n",
"testing_features, testing_label = input_fn(\n",
" fileNames=testFile,\n",
" predictors=predictors,\n",
" label=label,\n",
" shuffle=False,\n",
" training=False,\n",
" batchSize=1,\n",
" kernelShape=kernelShape)\n",
"\n",
"# and evaluate the model\n",
"model.evaluate(x=testing_features,y=testing_label,steps=100)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "MC9C28OWDLYo",
"colab_type": "text"
},
"source": [
"Wow! Our model is over 99% accurate compared to a testing dataset...\n",
"\n",
"![overfitting](https://miro.medium.com/max/800/1*uEL2SwXLdrEZpAgzXQ_qBA.png)\n",
"\n",
"Let's apply the model on some actual imagery and see how the predicted image looks."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "VZBXB6cEOlfO",
"colab_type": "text"
},
"source": [
"# Running inference and uploading to Earth Engine\n",
"\n",
"We can now run predictions (or inference) on the full exported image to predict which pixels are forest."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "m96wyJgUukE3",
"colab_type": "text"
},
"source": [
"Earth Engine exports the image, again, in a TFRecord file format, which makes the image loose the spatial information. We need the spatial information to reconstruct the image after is this done so EE gives us a \"mixer file\" which describes the how the TFRecords are tiled. Here we get the mixer file because we will use the information to parse the TFRecords."
]
},
{
"cell_type": "code",
"metadata": {
"id": "giBr5LFL_BHl",
"colab_type": "code",
"colab": {}
},
"source": [
"# Load the contents of the mixer file to a JSON object.\n",
"jsonText = !gsutil cat {mixerFile}\n",
"\n",
"# Get a single string w/ newlines from the IPython.utils.text.SList\n",
"mixer = json.loads(jsonText.nlstr)\n",
"pprint(mixer)\n",
"\n",
"PATCH_WIDTH = mixer['patchDimensions'][0]\n",
"PATCH_HEIGHT = mixer['patchDimensions'][1]\n",
"PATCHES = mixer['totalPatches']"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "eTUS6Eb7viAl",
"colab_type": "text"
},
"source": [
"Again, the data is in a TFRecord file format so we need to parse the file and convert to Tensors. We have a similar function as before but we use the mixer file to derive some of the tensor shape information."
]
},
{
"cell_type": "code",
"metadata": {
"id": "dR8ue9m_Dg8y",
"colab_type": "code",
"colab": {}
},
"source": [
"# The default value of side is now 316, as our intent is to create predictions\n",
"# for 256x256 image patches with a 30 pixel wide border.\n",
"def infer_input_fn(fileNames, predictors, batchSize=100):\n",
" ds = tf.data.TFRecordDataset(fileNames, compression_type='GZIP')\n",
" \n",
" y = int(PATCH_HEIGHT)+(kernelSize/2)\n",
" x = int(PATCH_WIDTH)+(kernelSize/2)\n",
"\n",
" # Note that the tensors are in the shape of a patch, one patch for each band.\n",
" imageColumns = [\n",
" tf.io.FixedLenFeature(shape=[y,x], dtype=tf.float32) \n",
" for k in predictors\n",
" ]\n",
"\n",
" # Parsing dictionary.\n",
" imageFeaturesDict = dict(zip(predictors, imageColumns))\n",
"\n",
" def parse(example_proto):\n",
" parsed_features = tf.io.parse_single_example(example_proto, imageFeaturesDict)\n",
" parsed_features = {\n",
" k:v for (k,v) in parsed_features.items()}\n",
" return parsed_features\n",
" \n",
" ds = ds.map(parse, num_parallel_calls=5)\n",
" \n",
" # Return a tuple from a dict when there is no label (target). (i.e. inference)\n",
" def toTupleImage(dict):\n",
" inputsList = [dict.get(key) for key in predictors]\n",
" stacked = tf.stack(inputsList, axis=0)\n",
" # Convert from CHW to HWC\n",
" stacked = tf.transpose(stacked, [1, 2, 0])\n",
" return stacked\n",
"\n",
" ds = ds.map(toTupleImage).batch(batchSize)\n",
"\n",
" return ds"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "a7vrPUXwFZ-Y",
"colab_type": "code",
"colab": {}
},
"source": [
"# run predictions\n",
"predictions = [i[16:-16,16:-16] for i in model.predict(infer_input_fn(predictFiles,predictors,batchSize=1), steps=PATCHES, verbose=1)]\n",
"# outputs are a list of np.arrays"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "Af36m68Yg58J",
"colab_type": "text"
},
"source": [
"![slacking off](data:image/jpeg;base64,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)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "vLjAlrlvEyLn",
"colab_type": "text"
},
"source": [
"We ran our predictions on the image but how do we get the data back into Earth Engine? We do this by writing the data to storage and then uploading the data. We have to write a little code to write the data and create a TF example from the numpy arrays."
]
},
{
"cell_type": "code",
"metadata": {
"id": "49c35TCiFisP",
"colab_type": "code",
"colab": {}
},
"source": [
"def make_example(pred):\n",
" probs = pred.flatten()\n",
" example = tf.train.Example(\n",
" features=tf.train.Features(\n",
" feature={\n",
" 'probability': tf.train.Feature(\n",
" float_list=tf.train.FloatList(\n",
" value=probs))\n",
" }\n",
" )\n",
" )\n",
" return example\n",
"\n",
"# set this number to prevent massive file sizes\n",
"MAX_RECORDS_PER_FILE = 50\n",
"output_path = 'gs://{0}/{1}/silvacarbon_tf_forest_prediction_{2:05}.tfrecord'\n",
"\n",
"# Create the records we'll ingest into EE\n",
"file_number = 0\n",
"still_writing = True\n",
"total_patches = 0\n",
"while still_writing:\n",
" file_path = output_path.format(silvacarbonBucket,myfolder,file_number)\n",
" writer = tf.io.TFRecordWriter(file_path)\n",
" print(\"Writing file: {}\".format(file_path))\n",
" try:\n",
" written_records = 0\n",
" while True:\n",
" pred_arr = predictions[total_patches]\n",
"\n",
" writer.write(make_example(pred_arr).SerializeToString())\n",
"\n",
" written_records += 1 \n",
" total_patches += 1\n",
"\n",
" if written_records % 25 == 0:\n",
" print(\" Writing patch: {}\".format(written_records))\n",
"\n",
" if written_records == MAX_RECORDS_PER_FILE:\n",
" break\n",
" except Exception as e: \n",
" print(e)\n",
" # Stop writing for any exception. Note that reaching the end of the prediction\n",
" # dataset throws an exception.\n",
" still_writing=False\n",
" finally:\n",
" file_number += 1\n",
" writer.close()\n",
" \n",
"print('Wrote: {0} patches to {1} files'.format(total_patches,file_number))"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "7BxrbOsjFP46",
"colab_type": "text"
},
"source": [
"Data has been written and we just need to kick off an upload task. We define what the asset ID should be and which files to upload (the data we just wrote)"
]
},
{
"cell_type": "code",
"metadata": {
"id": "pvLhNTyJKTwK",
"colab_type": "code",
"colab": {}
},
"source": [
"# need to change the `toAssetId` variable to an asset folder you can write to\n",
"toAssetId = 'users/kelmarkert/public/silvacarbon_tf_forest_prediction'\n",
"\n",
"# get a formated string of all the tfrecords to upload\n",
"tfPredictions = ' '.join(['gs://{0}/{1}/silvacarbon_tf_forest_prediction_{2:05}.tfrecord'.format(silvacarbonBucket,myfolder,i) for i in range(file_number)])\n"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "YH25evqmNDQI",
"colab_type": "code",
"colab": {}
},
"source": [
"# kick off an upload process\n",
"!earthengine --no-use_cloud_api upload image --asset_id={toAssetId} {tfPredictions} {mixerFile}"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "QqI2IuH-NJ4H",
"colab_type": "code",
"colab": {}
},
"source": [
"pprint(ee.batch.Task.list()[0])"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "N32QfEoYdUyB",
"colab_type": "text"
},
"source": [
"You should now have an image in your asset folder that is the probablity of forest as predicted by our model! [Here](https://code.earthengine.google.com/7ab04f2c0f42105114afa33f4c7a0eac) is an example of the final output on Earth Engine"
]
},
{
"cell_type": "code",
"metadata": {
"id": "LPtCvizCh6PI",
"colab_type": "code",
"cellView": "both",
"colab": {}
},
"source": [
"#@title Congratulations, you used EE + TF!!!\n",
"\n",
"%%html\n",
"<div style=\"width:100%;\">\n",
" <p style=\"font-size:30px;text-align:center\"> How you feel after successfully running TensorFlow with Earth Engine </p>\n",
" <img style=\"width:50%;margin-left:auto;margin-right:auto;display:block;\" src=\"https://img.buzzfeed.com/buzzfeed-static/static/2019-06/29/17/asset/f36f57e98c55/sub-buzz-691-1561828556-1.jpg\"></img>\n",
"</div>"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "zp75G5nhOG4I",
"colab_type": "code",
"colab": {}
},
"source": [
""
],
"execution_count": null,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment