# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# + [markdown] id="ke3Z3T-DPLMu" colab_type="text"
# # Chapter 14: Data Privacy

# + [markdown] id="94oXR---PLMv" colab_type="text"
# ### NOTE as of September 2020, tf-privacy relies on the updated Keras optimizer which will be part of the TensorFlow 2.4 release
#
# Until the release of a stable 2.4 version, this notebook requires the TensorFlow's nightly builds. Due to the unstable nature of the nightly builds, this notebook might fail intermittently.

# + id="V5GVnk0RPLMv" colab_type="code" colab={"base_uri": "https://localhost:8080/", "height": 943} outputId="bf7cb24f-c6fd-41ee-e598-b314c3c3ffdd"
# !pip install tensorflow_privacy
# !pip install tf-nightly

# + colab_type="code" id="4N10jj3R47Sj" colab={}
import tensorflow as tf
import tensorflow_hub as hub
import pandas as pd
import numpy as np
import os

# + id="JvieVVCNPLM0" colab_type="code" colab={"base_uri": "https://localhost:8080/", "height": 34} outputId="6682e680-6222-4bb2-93bf-f3fe19546f76"

from pathlib import Path

repo_dir = Path.cwd().parents[1]
data_file_path = os.path.join(repo_dir, 'data/consumer_complaints_with_narrative.csv')
print(data_file_path)

# + id="pakq9qGqQXdn" colab_type="code" colab={"base_uri": "https://localhost:8080/", "height": 34} outputId="0e6c2168-683d-4884-bc05-89db9a4a863e"
# !ls /content/

# + [markdown] id="Ep69FHWVPLM3" colab_type="text"
# ## Feature engineering

# + colab_type="code" id="5lDj1OJCWfTy" colab={}
ONE_HOT_FEATURES = {
    "product": None,
    "sub_product": None,
    "company_response": None, 
    "state": None,
    "issue": None
}

# feature name, bucket count
BUCKET_FEATURES = {
    "zip_code": 10
}

# feature name, value is unused
TEXT_FEATURES = {
    "consumer_complaint_narrative": None
}

# + colab_type="code" id="3o-EurrDk7Fi" colab={}
feature_names = ["product", "sub_product", "issue", "sub_issue", "consumer_complaint_narrative", "company", "state", "zip_code", "company_response", "timely_response", "consumer_disputed"]
df = pd.read_csv(data_file_path, usecols=feature_names)


# + colab_type="code" id="nH5SAgmOOL6K" colab={}
def make_one_hot(df):
    one_hot_array = []
    for feature_name in ONE_HOT_FEATURES.keys():
        temp_array = pd.np.asarray(tf.keras.utils.to_categorical(df[feature_name].values))
        ONE_HOT_FEATURES[feature_name] = temp_array.shape[1]
        one_hot_array.append(temp_array)

    return one_hot_array


# + colab_type="code" id="jxbSJIw3lDOj" colab={"base_uri": "https://localhost:8080/", "height": 107} outputId="a889dfe6-98f5-4d6e-823f-08aef87b3ec2"
for feature in ONE_HOT_FEATURES.keys():
    df[feature] = df[feature].astype("category").cat.codes

one_hot_x = make_one_hot(df)

embedding_x = [pd.np.asarray(df[feature_name].values).reshape(-1) for feature_name in TEXT_FEATURES.keys()]

df['zip_code'] = df['zip_code'].str.replace('X', '0', regex=True)
df['zip_code'] = df['zip_code'].str.replace(r'\[|\*|\+|\-|`|\.|\ |\$|\/|!|\(', '0', regex=True)
df['zip_code'] = df['zip_code'].fillna(0)
df['zip_code'] = df['zip_code'].astype('int32')
# one bucket per 10k
df['zip_code'] = df['zip_code'].apply(lambda x: x//10000)
numeric_x = [df['zip_code'].values]

X = one_hot_x + numeric_x + embedding_x
y = np.asarray(df["consumer_disputed"], dtype=np.uint8).reshape(-1)

# + [markdown] colab_type="text" id="t9Eo3zrCVRPm"
# ## Adding DP

# + colab_type="code" id="Yto8Cmn7VErQ" colab={}
# DP parameters
NOISE_MULTIPLIER = 1.1
NUM_MICROBATCHES = 32
LEARNING_RATE = 0.1
POPULATION_SIZE = 1000
L2_NORM_CLIP = 1.0
BATCH_SIZE = 32 
EPOCHS = 1

# + colab_type="code" id="u0JJ_EnmVTk6" colab={}
from tensorflow_privacy.privacy.optimizers.dp_optimizer import DPGradientDescentGaussianOptimizer

optimizer = DPGradientDescentGaussianOptimizer(
    l2_norm_clip=L2_NORM_CLIP,
    noise_multiplier=NOISE_MULTIPLIER,
    num_microbatches=NUM_MICROBATCHES,
    learning_rate=LEARNING_RATE)
    
loss = tf.keras.losses.BinaryCrossentropy(
        from_logits=True, reduction=tf.losses.Reduction.NONE)


# + [markdown] colab_type="text" id="LoQHOGsh5Anr"
# The model is unchanged, we just pass in the differentially private optimizer and loss.

# + colab_type="code" id="JZ7Z1LHd4-kb" colab={}
def transformed_name(key):
    return key + '_xf'

def get_model(dp_optimizer, dp_loss, show_summary=True):
    """
    This function defines a Keras model and returns the model as a Keras object.
    """
    
    # one-hot categorical features
    input_features = []
    for key, dim in ONE_HOT_FEATURES.items():
        input_features.append(tf.keras.Input(shape=(dim), name=transformed_name(key)))

    # adding bucketized features 
    for key, dim in BUCKET_FEATURES.items():
        input_features.append(tf.keras.Input(1, name=transformed_name(key)))

    # adding text input features
    input_texts = []
    for key in TEXT_FEATURES.keys():
        input_texts.append(tf.keras.Input(shape=(1,), name=transformed_name(key), dtype=tf.string))

    # embed text features
    MODULE_URL = "https://tfhub.dev/google/universal-sentence-encoder/4"
    embed = hub.KerasLayer(MODULE_URL)
    reshaped_narrative = tf.reshape(input_texts[0], [-1])
    embed_narrative = embed(reshaped_narrative) 
    deep_ff = tf.keras.layers.Reshape((512, ), input_shape=(1, 512))(embed_narrative)
    
    deep = tf.keras.layers.Dense(256, activation='relu')(deep_ff)
    deep = tf.keras.layers.Dense(64, activation='relu')(deep)
    deep = tf.keras.layers.Dense(16, activation='relu')(deep)

    wide_ff = tf.keras.layers.concatenate(input_features)
    wide = tf.keras.layers.Dense(16, activation='relu')(wide_ff)

    both = tf.keras.layers.concatenate([deep, wide])

    output = tf.keras.layers.Dense(1, activation='sigmoid')(both) 

    inputs = input_features + input_texts

    keras_model = tf.keras.models.Model(inputs, output)
    keras_model.compile(optimizer=dp_optimizer,
                        loss=dp_loss,  
                        metrics=[
                            tf.keras.metrics.BinaryAccuracy(),
                            tf.keras.metrics.TruePositives()
                        ])
    if show_summary:
        keras_model.summary()

    return keras_model


# + colab_type="code" id="Y4TTGI9glD_M" colab={}
model = get_model(show_summary=False, dp_optimizer=optimizer, dp_loss=loss)

# + colab_type="code" id="yAmaGolZl4cX" colab={"base_uri": "https://localhost:8080/", "height": 72} outputId="f5161960-ada8-4717-bf8d-cfa0551ad358"
model.fit(x=X, y=y, batch_size=32, validation_split=0.1, epochs=EPOCHS)

# + [markdown] colab_type="text" id="P1gtS5tFfZau"
# ### Calculate Epsilon

# + colab_type="code" id="q6u5MIUkMrpS" colab={"base_uri": "https://localhost:8080/", "height": 70} outputId="68bf00b6-1bbc-4529-82a9-50479772522c"
from tensorflow_privacy.privacy.analysis import compute_dp_sgd_privacy


compute_dp_sgd_privacy.compute_dp_sgd_privacy(n=POPULATION_SIZE, 
                                              batch_size=BATCH_SIZE, 
                                              noise_multiplier=NOISE_MULTIPLIER, 
                                              epochs=EPOCHS, 
                                              delta=1e-3)

# + colab_type="code" id="gBzK9bK1gBab" colab={}


StarcoderdataJupyterScriptsDedupFiltered