# change these to try this notebook out
BUCKET = 'cloud-training-demos-ml'
PROJECT = 'cloud-training-demos'
REGION = 'us-central1'

import os
os.environ['BUCKET'] = BUCKET
os.environ['PROJECT'] = PROJECT
os.environ['REGION'] = REGION
os.environ['TFVERSION'] = '1.8'

if 'COLAB_GPU' in os.environ:  # this is always set on Colab, the value is 0 or 1 depending on whether a GPU is attached
  from google.colab import auth
  auth.authenticate_user()
  # download "sidecar files" since on Colab, this notebook will be on Drive
  !rm -rf txtclsmodel
  !git clone --depth 1 https://github.com/GoogleCloudPlatform/training-data-analyst
  !mv  training-data-analyst/courses/machine_learning/deepdive/09_sequence/txtclsmodel/ .
  !rm -rf training-data-analyst
  # downgrade TensorFlow to the version this notebook has been tested with
  !pip install --upgrade tensorflow==$TFVERSION

import tensorflow as tf
print(tf.__version__)

"""
Explanation: <h1> Text Classification using Native Tensorflow Pre-processing</h1>

This notebook continues from <a href="text_classification.ipynb">text_classification.ipynb</a> -- in particular, we assume that you have run the "Create Dataset from BigQuery" and "Rerun with Pre-trained embedding" sections of that notebook.
End of explanation
"""


%%bash
gsutil ls gs://$BUCKET/txtcls/eval.tsv
gsutil ls gs://$BUCKET/txtcls/train.tsv
gsutil ls gs://$BUCKET/txtcls/glove.6B.200d.txt

"""
Explanation: Pre-requisites
Ensure you have the data files and pre-trained embedding file in your GCS bucket:
End of explanation
"""


%%bash
OUTDIR=gs://${BUCKET}/txtcls/trained_finetune_native
JOBNAME=txtcls_$(date -u +%y%m%d_%H%M%S)
gsutil -m rm -rf $OUTDIR
gcloud ml-engine jobs submit training $JOBNAME \
 --region=$REGION \
 --module-name=trainer.task \
 --package-path=${PWD}/txtclsmodel/trainer \
 --job-dir=$OUTDIR \
 --scale-tier=BASIC_GPU \
 --runtime-version=$TFVERSION \
 -- \
 --output_dir=$OUTDIR \
 --train_data_path=gs://${BUCKET}/txtcls/train.tsv \
 --eval_data_path=gs://${BUCKET}/txtcls/eval.tsv \
 --embedding_path=gs://${BUCKET}/txtcls/glove.6B.200d.txt \
 --native \
 --num_epochs=5

"""
Explanation: If you don't, go back and run the <a href="text_classification.ipynb">text_classification</a> notebook first. Specifically, run the "Create Dataset from BigQuery" and "Rerun with Pre-trained embedding" sections of that notebook.
Native Tensorflow Predictions
Why Native?
Up until now we've been using python functions to do our data pre-processing. This is fine during training, but during serving it adds the limitation that we need a python client in the prediction pipeline.
This limits your serving flexibility. For example, lets say you want to be able to serve this model locally (offline) on a mobile phone. How would you do it? It's non trivial to execute python code on Android. Plus if your vocabulary tokenization ever changed you'd need to update all the clients or else they would be out of sync with the model.
A better way would be to have all of our serving pre-processing to be done using native Tensorflow operations. This way we can take advantage of Tensorflow's hardware agnostic execution engine, and leverage the engineering efforts the Tensorflow team put into making sure our code works whether we're running on a server, mobile, or an embedded device!
TensorFlow/Keras Code
Please explore the code in this <a href="txtclsmodel/trainer">directory</a>: model_native.py contains the TensorFlow model and task.py parses command line arguments and launches off the training job. 
In particular look for the follwing:

tf.keras.preprocessing.text.Tokenizer.fit_on_texts() to generate a mapping from our word vocabulary to integers
tf.gfile to write the vocabulary mapping to disk
tf.contrib.lookup.index_table_from_file() to encode our sentences into a tensor of their respective word-integers, based on the vocabulary mapping written to disk in the previous step
tf.pad to pad all sequences to be the same length

Train on the Cloud
Note the new --native parameter. This tells task.py to call model_native.py instead of model.py
End of explanation
"""


%%bash
MODEL_NAME="txtcls"
MODEL_VERSION="v1_finetune_native"
MODEL_LOCATION=$(gsutil ls gs://${BUCKET}/txtcls/trained_finetune_native/export/exporter/ | tail -1)
#gcloud ml-engine versions delete ${MODEL_VERSION} --model ${MODEL_NAME} --quiet
#gcloud ml-engine models delete ${MODEL_NAME}
#gcloud ml-engine models create ${MODEL_NAME} --regions $REGION
gcloud ml-engine versions create ${MODEL_VERSION} --model ${MODEL_NAME} --origin ${MODEL_LOCATION} --runtime-version=$TFVERSION

"""
Explanation: Deploy trained model
End of explanation
"""


techcrunch=[
  'Uber shuts down self-driving trucks unit',
  'Grover raises €37M Series A to offer latest tech products as a subscription',
  'Tech companies can now bid on the Pentagon’s $10B cloud contract'
]
nytimes=[
  '‘Lopping,’ ‘Tips’ and the ‘Z-List’: Bias Lawsuit Explores Harvard’s Admissions',
  'A $3B Plan to Turn Hoover Dam into a Giant Battery',
  'A MeToo Reckoning in China’s Workplace Amid Wave of Accusations'
]
github=[
  'Show HN: Moon – 3kb JavaScript UI compiler',
  'Show HN: Hello, a CLI tool for managing social media',
  'Firefox Nightly added support for time-travel debugging'
]

"""
Explanation: Sample prediction instances
Here are some actual hacker news headlines gathered from July 2018. These titles were not part of the training or evaluation datasets.
End of explanation
"""


from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
import json

# JSON format the requests
requests = (techcrunch+nytimes+github)
requests = [x.lower() for x in requests] 
request_data = {'instances': requests}

# Authenticate and call CMLE prediction API 
credentials = GoogleCredentials.get_application_default()
api = discovery.build('ml', 'v1', credentials=credentials,
            discoveryServiceUrl='https://storage.googleapis.com/cloud-ml/discovery/ml_v1_discovery.json')

parent = 'projects/%s/models/%s/versions/%s' % (PROJECT, 'txtcls', 'v1_finetune_native')
response = api.projects().predict(body=request_data, name=parent).execute()

# Format and print response
for i in range(len(requests)):
  print('\n{}'.format(requests[i]))
  print(' github    : {}'.format(response['predictions'][i]['dense_1'][0]))
  print(' nytimes   : {}'.format(response['predictions'][i]['dense_1'][1]))
  print(' techcrunch: {}'.format(response['predictions'][i]['dense_1'][2]))

"""
Explanation: Get Predictions
Note how we can now feed the titles directly to the model! No need for the client to load a vocabulary tokenization mapping. The text tokenization is done for us inside of the Tensorflow model's serving_input_fn.
End of explanation
"""


