# Notebook from brianreyes3/PyLabs-Analisis-de-Datos
Path: Laboratorio 0 Brian Reyes.ipynb

### Primer Notebook
### ESMA 3016
### Edgar Acuna
### Febrero 19, 2018_____no_output_____

<code>
# Una operacion basica elemental
(3+5*6)/float(12)_____no_output_____# Entrando el dato con el teclado
age = input("How old are you? ")How old are you? 55
print "Your age is", ageYour age is 55
print "You have", 65 - age, "years until retirement"You have 10 years until retirement
name = "Edgar Acuna Fernandez"
length = len(name)_____no_output_____#imprimiendo en Mayuscula e imprimiendo la longitud
big_name = str.upper(name)
print big_name, "tiene", length, "caracteres"EDGAR ACUNA FERNANDEZ tiene 21 caracteres

names = ["Ana", "Rosa", "Julia"]

_____no_output_____names[0]_____no_output_____names[-2]_____no_output_____#uso de la funcion range
range(5)_____no_output_____

range(5,10)

_____no_output_____range(5,20,3)_____no_output_____#uso del condicional If
gpa = 3.4
if gpa > 2.0:
    print "Su solicitud de admision es aceptada."Su solicitud de admision es aceptada.
# Uso  de if/else
gpa = 1.4
if gpa >= 2.5:
    print "Bienvenido al Colegio de Mayaguez!"
else:
    print "Su solicitud de admision ha sido denegada."Su solicitud de admision ha sido denegada.
#Ejemplo con operadores logicos
Ana=3
Rosa=25
if (Ana <= 5 and Rosa >= 10):
    print "Ana and Rosa"Ana and Rosa

if (Rosa == 500 or Ana != 5):
     print "Otra vez Aana y Rosa"

Otra vez Aana y Rosa
#Ejemplo de loop
for x in range(1, 4):
    print x, "squared is", x * x1 squared is 1
2 squared is 4
3 squared is 9

# Otro ejemplo de loop
names = ["Ana", "Rosa", "Julia"]
for name in names:
    print name

Ana
Rosa
Julia
# Ejemplo de break y continue
for value in [3, 1, 4, 1, 5, 9, 2]:
    print "Checking", value
    if value > 8:
        print "Exiting for loop"
        break
    elif value < 3:
        print "Ignoring"
        continue
    print "The square is", value**2Checking 3
The square is 9
Checking 1
Ignoring
Checking 4
The square is 16
Checking 1
Ignoring
Checking 5
The square is 25
Checking 9
Exiting for loop
#Ejemplo de while
number = 1
while number < 200:
    print number,
    number = number * 21 2 4 8 16 32 64 128
#Sunamndo una constante 10 a una lista
vec1=[3,4,5]
[x +10 for x   in vec1]_____no_output_____#summando dos vectores componenete a componente
vec2=[9,10,11]
for a,b in zip(vec1,vec2):
    print a+b12
14
16
#usando el modulo matematico math
import math
math.pi_____no_output_____#usando el modulo matematico math con el alias m
import math as m
m.pi_____no_output_____#importando solamente la funcion pi del modulo math
from math import pi
pi_____no_output_____import pandas as pd
df=pd.read_csv("http://academic.uprm.edu/eacuna/Animals2.csv")_____no_output_____print df                       Specie       body    brain
0   Lesser short-tailed shrew      0.005     0.14
1            Little brown bat      0.010     0.25
2               Big brown bat      0.023     0.30
3                       Mouse      0.023     0.40
4                  Musk shrew      0.048     0.33
5             Star-nosed mole      0.060     1.00
6            E. American mole      0.075     1.20
7             Ground squirrel      0.101     4.00
8                  Tree shrew      0.104     2.50
9              Golden hamster      0.120     1.00
10                       Mole      0.122     3.00
11                     Galago      0.200     5.00
12                        Rat      0.280     1.90
13                 Chinchilla      0.425     6.40
14                 Owl monkey      0.480    15.50
15            Desert hedgehog      0.550     2.40
16               Rock hyrax-a      0.750    12.30
17          European hedgehog      0.785     3.50
18                     Tenrec      0.900     2.60
19      Artic ground squirrel      0.920     5.70
20  African giant pouched rat      1.000     6.60
21                 Guinea pig      1.040     5.50
22            Mountain beaver      1.350     8.10
23                 Slow loris      1.400    12.50
24                      Genet      1.410    17.50
25                  Phalanger      1.620    11.40
26               N.A. opossum      1.700     6.30
27                 Tree hyrax      2.000    12.30
28                     Rabbit      2.500    12.10
29                    Echidna      3.000    25.00
..                        ...        ...      ...
35      Yellow-bellied marmot      4.050    17.00
36                     Verbet      4.190    58.00
37                    Red fox      4.235    50.40
38                    Raccoon      4.288    39.20
39              Rhesus monkey      6.800   179.00
40               Potar monkey     10.000   115.00
41                     Baboon     10.550   179.50
42                   Roe deer     14.830    98.20
43                       Goat     27.660   115.00
44                   Kangaroo     35.000    56.00
45                  Grey wolf     36.330   119.50
46                 Chimpanzee     52.160   440.00
47                      Sheep     55.500   175.00
48            Giant armadillo     60.000    81.00
49                      Human     62.000  1320.00
50                  Grey seal     85.000   325.00
51                     Jaguar    100.000   157.00
52            Brazilian tapir    160.000   169.00
53                     Donkey    187.100   419.00
54                        Pig    192.000   180.00
55                    Gorilla    207.000   406.00
56                      Okapi    250.000   490.00
57                        Cow    465.000   423.00
58                      Horse    521.000   655.00
59                    Giraffe    529.000   680.00
60             Asian elephant   2547.000  4603.00
61           African elephant   6654.000  5712.00
62                Triceratops   9400.000    70.00
63                Dipliodocus  11700.000    50.00
64              Brachiosaurus  87000.000   154.50

[65 rows x 3 columns]

</code>


# Notebook from Calinou/practicalAI
Path: notebooks/basic_ml/07_Data_and_Models.ipynb

Data and Models

In the subsequent lessons, we will continue to learn deep learning. But we've ignored a fundamental concept about data and modeling: quality and quantity._____no_output_____

 View on practicalAI

 Run in Google Colab

 View code on GitHub

_____no_output_____# Set up_____no_output_____

In a nutshell, a machine learning model consumes input data and produces predictions. The quality of the predictions directly corresponds to the quality and quantity of data you train the model with; **garbage in, garbage out**. Check out this [VentureBeat article](https://venturebeat.com/2018/06/30/understanding-the-practical-applications-of-business-ai/) on where it makes sense to use AI and how to properly apply it._____no_output_____We're going to go through all the concepts with concrete code examples and some synthesized data to train our models on. The task is to determine whether a tumor will be benign (harmless) or malignant (harmful) based on leukocyte (white blood cells) count and blood pressure. This is a synethic dataset that we created and has no clinical relevance._____no_output_____

# Use TensorFlow 2.x
%tensorflow_version 2.xTensorFlow 2.x selected.
import os
import numpy as np
import tensorflow as tf_____no_output_____# Arguments
SEED = 1234
DATA_FILE = 'tumors.csv'
REDUCED_DATA_FILE = 'tumors_reduced.csv'
SHUFFLE = True
TRAIN_SIZE = 0.70
VAL_SIZE = 0.15
TEST_SIZE = 0.15
NUM_EPOCHS = 5
BATCH_SIZE = 32
HIDDEN_DIM = 100
LEARNING_RATE = 1e-3_____no_output_____# Set seed for reproducability
np.random.seed(SEED)
tf.random.set_seed(SEED)_____no_output_____

# Data_____no_output_____

import matplotlib.pyplot as plt
import pandas as pd
from pandas.plotting import scatter_matrix
import urllib_____no_output_____

### Operations_____no_output_____

# Upload data from GitHub to notebook's local drive
url = "https://raw.githubusercontent.com/practicalAI/practicalAI/master/data/tumors.csv"
response = urllib.request.urlopen(url)
html = response.read()
with open(DATA_FILE, 'wb') as fp:
    fp.write(html)_____no_output_____# Raw data
df = pd.read_csv(DATA_FILE, header=0)
df.head()_____no_output_____# Define X and y
X = df[['leukocyte_count', 'blood_pressure']].values
y = df['tumor_class'].values_____no_output_____# Plot data
colors = {'benign': 'red', 'malignant': 'blue'}
plt.scatter(X[:, 0], X[:, 1], c=[colors[_y] for _y in y], s=25, edgecolors='k')
plt.xlabel('leukocyte count')
plt.ylabel('blood pressure')
plt.legend(['malignant ', 'benign'], loc="upper right")
plt.show()_____no_output_____

We want to choose features that have strong predictive signal for our task. If you want to improve performance, you need to continuously do feature engineering by collecting and adding new signals. So you may run into a new feature that has high correlation (orthogonal signal) with your existing features but it may still possess som unique signal to boost your predictive performance. _____no_output_____

# Correlation matrix
scatter_matrix(df, figsize=(5, 5));
df.corr()_____no_output_____

# Split data_____no_output_____

import collections
import json
from sklearn.model_selection import train_test_split_____no_output_____

### Components_____no_output_____

def train_val_test_split(X, y, val_size, test_size, shuffle):
    """Split data into train/val/test datasets.
    """
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=test_size, stratify=y, shuffle=shuffle)
    X_train, X_val, y_train, y_val = train_test_split(
        X_train, y_train, test_size=val_size, stratify=y_train, shuffle=shuffle)
    return X_train, X_val, X_test, y_train, y_val, y_test_____no_output_____

### Operations_____no_output_____

# Create data splits
X_train, X_val, X_test, y_train, y_val, y_test = train_val_test_split(
    X=X, y=y, val_size=VAL_SIZE, test_size=TEST_SIZE, shuffle=SHUFFLE)
class_counts = dict(collections.Counter(y))
print (f"X_train: {X_train.shape}, y_train: {y_train.shape}")
print (f"X_val: {X_val.shape}, y_val: {y_val.shape}")
print (f"X_test: {X_test.shape}, y_test: {y_test.shape}")
print (f"X_train[0]: {X_train[0]}")
print (f"y_train[0]: {y_train[0]}")
print (f"Classes: {class_counts}")X_train: (722, 2), y_train: (722,)
X_val: (128, 2), y_val: (128,)
X_test: (150, 2), y_test: (150,)
X_train[0]: [18.01865938 15.48133647]
y_train[0]: benign
Classes: {'malignant': 611, 'benign': 389}

# Label encoder_____no_output_____

import json
from sklearn.preprocessing import LabelEncoder_____no_output_____# Output vectorizer
y_tokenizer = LabelEncoder()_____no_output_____# Fit on train data
y_tokenizer = y_tokenizer.fit(y_train)
print (f"classes: {y_tokenizer.classes_}")classes: ['benign' 'malignant']
# Convert labels to tokens
print (f"y_train[0]: {y_train[0]}")
y_train = y_tokenizer.transform(y_train)
y_val = y_tokenizer.transform(y_val)
y_test = y_tokenizer.transform(y_test)
print (f"y_train[0]: {y_train[0]}")y_train[0]: benign
y_train[0]: 0
# Class weights
counts = collections.Counter(y_train)
class_weights = {_class: 1.0/count for _class, count in counts.items()}
print (f"class counts: {counts},\nclass weights: {class_weights}")class counts: Counter({1: 441, 0: 281}),
class weights: {0: 0.0035587188612099642, 1: 0.0022675736961451248}

# Standardize data_____no_output_____

from sklearn.preprocessing import StandardScaler_____no_output_____# Standardize the data (mean=0, std=1) using training data
X_scaler = StandardScaler().fit(X_train)_____no_output_____# Apply scaler on training and test data (don't standardize outputs for classification)
standardized_X_train = X_scaler.transform(X_train)
standardized_X_val = X_scaler.transform(X_val)
standardized_X_test = X_scaler.transform(X_test)_____no_output_____# Check
print (f"standardized_X_train: mean: {np.mean(standardized_X_train, axis=0)[0]}, std: {np.std(standardized_X_train, axis=0)[0]}")
print (f"standardized_X_val: mean: {np.mean(standardized_X_val, axis=0)[0]}, std: {np.std(standardized_X_val, axis=0)[0]}")
print (f"standardized_X_test: mean: {np.mean(standardized_X_test, axis=0)[0]}, std: {np.std(standardized_X_test, axis=0)[0]}")standardized_X_train: mean: 3.938600753633857e-15, std: 0.9999999999999998
standardized_X_val: mean: 0.06571155649025341, std: 0.9625041074006321
standardized_X_test: mean: -0.09679265967370689, std: 0.9864056087200104

# Model_____no_output_____Let's fit a model on this synthetic data._____no_output_____

import itertools
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Input
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.models import Model
from tensorflow.keras.models import Model_____no_output_____

### Components_____no_output_____

# MLP
class MLP(Model):
    def __init__(self, hidden_dim, num_classes):
        super(MLP, self).__init__()
        self.fc1 = Dense(units=hidden_dim, activation='relu')
        self.fc2 = Dense(units=num_classes, activation='softmax')

    def call(self, x_in, training=False):
        """Forward pass."""
        z = self.fc1(x_in)
        y_pred = self.fc2(z)
        return y_pred

    def sample(self, input_shape):
        x_in = Input(shape=input_shape)
        return Model(inputs=x_in, outputs=self.call(x_in)).summary()_____no_output_____def plot_multiclass_decision_boundary(model, X, y, savefig_fp=None):
    """Plot the multiclass decision boundary for a model that accepts 2D inputs.

    Arguments:
        model {function} -- trained model with function model.predict(x_in).
        X {numpy.ndarray} -- 2D inputs with shape (N, 2).
        y {numpy.ndarray} -- 1D outputs with shape (N,).
    """
    # Axis boundaries
    x_min, x_max = X[:, 0].min() - 0.1, X[:, 0].max() + 0.1
    y_min, y_max = X[:, 1].min() - 0.1, X[:, 1].max() + 0.1
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 101),
                         np.linspace(y_min, y_max, 101))

    # Create predictions
    x_in = np.c_[xx.ravel(), yy.ravel()]
    y_pred = model.predict(x_in)
    y_pred = np.argmax(y_pred, axis=1).reshape(xx.shape)

    # Plot decision boundary
    plt.contourf(xx, yy, y_pred, cmap=plt.cm.Spectral, alpha=0.8)
    plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.RdYlBu)
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())

    # Plot
    if savefig_fp:
        plt.savefig(savefig_fp, format='png')_____no_output_____

### Operations_____no_output_____

# Model Arguments
INPUT_DIM = X_train.shape[-1]
NUM_CLASSES = len(df.tumor_class.unique())_____no_output_____# Initialize the model
model = MLP(hidden_dim=HIDDEN_DIM,
            num_classes=NUM_CLASSES)
model.sample(input_shape=(INPUT_DIM,))Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         [(None, 2)]               0
_________________________________________________________________
dense (Dense)                (None, 100)               300
_________________________________________________________________
dense_1 (Dense)              (None, 2)                 202
=================================================================
Total params: 502
Trainable params: 502
Non-trainable params: 0
_________________________________________________________________
# Compile
model.compile(optimizer=Adam(lr=LEARNING_RATE),
              loss=SparseCategoricalCrossentropy(),
              metrics=['accuracy'])_____no_output_____# Training
model.fit(x=standardized_X_train,
          y=y_train,
          validation_data=(standardized_X_val, y_val),
          epochs=NUM_EPOCHS,
          batch_size=BATCH_SIZE,
          class_weight=class_weights,
          shuffle=False,
          verbose=1)Train on 722 samples, validate on 128 samples
Epoch 1/5
722/722 [==============================] - 2s 3ms/sample - loss: 0.0016 - accuracy: 0.7008 - val_loss: 0.0014 - val_accuracy: 0.7812
Epoch 2/5
722/722 [==============================] - 0s 149us/sample - loss: 0.0012 - accuracy: 0.8864 - val_loss: 0.0011 - val_accuracy: 0.8359
Epoch 3/5
722/722 [==============================] - 0s 142us/sample - loss: 8.6852e-04 - accuracy: 0.9072 - val_loss: 8.7229e-04 - val_accuracy: 0.8672
Epoch 4/5
722/722 [==============================] - 0s 150us/sample - loss: 6.7633e-04 - accuracy: 0.9321 - val_loss: 7.1961e-04 - val_accuracy: 0.8828
Epoch 5/5
722/722 [==============================] - 0s 143us/sample - loss: 5.4061e-04 - accuracy: 0.9571 - val_loss: 6.0240e-04 - val_accuracy: 0.9219
# Predictions
pred_train = model.predict(standardized_X_train)
pred_test = model.predict(standardized_X_test)
print (f"sample probability: {pred_test[0]}")
pred_train = np.argmax(pred_train, axis=1)
pred_test = np.argmax(pred_test, axis=1)
print (f"sample class: {pred_test[0]}")sample probability: [0.21060377 0.7893962 ]
sample class: 1
# Accuracy
train_acc = accuracy_score(y_train, pred_train)
test_acc = accuracy_score(y_test, pred_test)
print (f"train acc: {train_acc:.2f}, test acc: {test_acc:.2f}")train acc: 0.96, test acc: 0.93

We're going to plot a white point, which we know belongs to the malignant tumor class. Our well trained model here would accurately predict that it is indeed a malignant tumor!_____no_output_____

# Visualize the decision boundary
plt.figure(figsize=(8,5))
plt.title("Test")
plot_multiclass_decision_boundary(model=model, X=standardized_X_test, y=y_test)

# Sample point near the decision boundary
mean_leukocyte_count, mean_blood_pressure = X_scaler.transform(
    [[np.mean(df.leukocyte_count), np.mean(df.blood_pressure)]])[0]
plt.scatter(mean_leukocyte_count+0.05, mean_blood_pressure-0.05, s=200,
            c='b', edgecolor='w', linewidth=2)

# Annotate
plt.annotate('true: malignant,\npred: malignant',
             color='white',
             xy=(mean_leukocyte_count, mean_blood_pressure),
             xytext=(0.4, 0.65),
             textcoords='figure fraction',
             fontsize=16,
             arrowprops=dict(facecolor='white', shrink=0.1)
            )
plt.show()_____no_output_____

Great! We received great performances on both our train and test data splits. We're going to use this dataset to show the importance of data quality and quantity._____no_output_____# Data quality and quantity_____no_output_____Let's remove some training data near the decision boundary and see how robust the model is now._____no_output_____

# Upload data from GitHub to notebook's local drive
url = "https://raw.githubusercontent.com/practicalAI/practicalAI/master/data/tumors_reduced.csv"
response = urllib.request.urlopen(url)
html = response.read()
with open(REDUCED_DATA_FILE, 'wb') as fp:
    fp.write(html)_____no_output_____# Raw reduced data
df_reduced = pd.read_csv(REDUCED_DATA_FILE, header=0)
df_reduced.head()_____no_output_____# Define X and y
X = df_reduced[['leukocyte_count', 'blood_pressure']].values
y = df_reduced['tumor_class'].values_____no_output_____# Plot data
colors = {'benign': 'red', 'malignant': 'blue'}
plt.scatter(X[:, 0], X[:, 1], c=[colors[_y] for _y in y], s=25, edgecolors='k')
plt.xlabel('leukocyte count')
plt.ylabel('blood pressure')
plt.legend(['malignant ', 'benign'], loc="upper right")
plt.show()_____no_output_____# Create data splits
X_train, X_val, X_test, y_train, y_val, y_test = train_val_test_split(
    X, y, val_size=VAL_SIZE, test_size=TEST_SIZE, shuffle=SHUFFLE)
class_counts = dict(collections.Counter(y_train))
print (f"X_train: {X_train.shape}, y_train: {y_train.shape}")
print (f"X_val: {X_val.shape}, y_val: {y_val.shape}")
print (f"X_test: {X_test.shape}, y_test: {y_test.shape}")
print (f"X_train[0]: {X_train[0]}")
print (f"y_train[0]: {y_train[0]}")
print (f"Classes: {class_counts}")X_train: (520, 2), y_train: (520,)
X_val: (92, 2), y_val: (92,)
X_test: (108, 2), y_test: (108,)
X_train[0]: [14.4110029  13.14842457]
y_train[0]: benign
Classes: {'benign': 281, 'malignant': 239}
# Encode class labels
y_tokenizer = LabelEncoder()
y_tokenizer = y_tokenizer.fit(y_train)
num_classes = len(y_tokenizer.classes_)
y_train = y_tokenizer.transform(y_train)
y_val = y_tokenizer.transform(y_val)
y_test = y_tokenizer.transform(y_test)_____no_output_____# Class weights
counts = collections.Counter(y_train)
class_weights = {_class: 1.0/count for _class, count in counts.items()}
print (f"class counts: {counts},\nclass weights: {class_weights}")class counts: Counter({0: 281, 1: 239}),
class weights: {0: 0.0035587188612099642, 1: 0.0041841004184100415}
# Standardize inputs using training data
X_scaler = StandardScaler().fit(X_train)
standardized_X_train = X_scaler.transform(X_train)
standardized_X_val = X_scaler.transform(X_val)
standardized_X_test = X_scaler.transform(X_test)_____no_output_____# Initialize the model
model = MLP(hidden_dim=HIDDEN_DIM,
            num_classes=NUM_CLASSES)
model.sample(input_shape=(INPUT_DIM,))Model: "model_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_2 (InputLayer)         [(None, 2)]               0
_________________________________________________________________
dense_2 (Dense)              (None, 100)               300
_________________________________________________________________
dense_3 (Dense)              (None, 2)                 202
=================================================================
Total params: 502
Trainable params: 502
Non-trainable params: 0
_________________________________________________________________
# Compile
model.compile(optimizer=Adam(lr=LEARNING_RATE),
              loss=SparseCategoricalCrossentropy(),
              metrics=['accuracy'])_____no_output_____# Training
model.fit(x=standardized_X_train,
          y=y_train,
          batch_size=BATCH_SIZE,
          epochs=NUM_EPOCHS,
          validation_data=(standardized_X_val, y_val),
          shuffle=False,
          class_weight=class_weights,
          verbose=1)Train on 520 samples, validate on 92 samples
Epoch 1/5
520/520 [==============================] - 1s 2ms/sample - loss: 0.0024 - accuracy: 0.8615 - val_loss: 0.0020 - val_accuracy: 0.9239
Epoch 2/5
520/520 [==============================] - 0s 173us/sample - loss: 0.0017 - accuracy: 0.9942 - val_loss: 0.0016 - val_accuracy: 0.9457
Epoch 3/5
520/520 [==============================] - 0s 174us/sample - loss: 0.0012 - accuracy: 0.9981 - val_loss: 0.0012 - val_accuracy: 0.9674
Epoch 4/5
520/520 [==============================] - 0s 177us/sample - loss: 8.8105e-04 - accuracy: 0.9981 - val_loss: 9.2301e-04 - val_accuracy: 0.9674
Epoch 5/5
520/520 [==============================] - 0s 172us/sample - loss: 6.4092e-04 - accuracy: 0.9981 - val_loss: 7.2682e-04 - val_accuracy: 0.9674
# Predictions
pred_train = model.predict(standardized_X_train)
pred_test = model.predict(standardized_X_test)
print (f"sample probability: {pred_test[0]}")
pred_train = np.argmax(pred_train, axis=1)
pred_test = np.argmax(pred_test, axis=1)
print (f"sample class: {pred_test[0]}")sample probability: [0.9758981  0.02410194]
sample class: 0
# Accuracy
train_acc = accuracy_score(y_train, pred_train)
test_acc = accuracy_score(y_test, pred_test)
print (f"train acc: {train_acc:.2f}, test acc: {test_acc:.2f}")train acc: 1.00, test acc: 1.00
# Visualize the decision boundary
plt.figure(figsize=(8,5))
plt.title("Test")
plot_multiclass_decision_boundary(model=model, X=standardized_X_test, y=y_test)

# Sample point near the decision boundary (same point as before)
plt.scatter(mean_leukocyte_count+0.05, mean_blood_pressure-0.05, s=200,
            c='b', edgecolor='w', linewidth=2)

# Annotate
plt.annotate('true: malignant,\npred: benign',
             color='white',
             xy=(mean_leukocyte_count, mean_blood_pressure),
             xytext=(0.45, 0.60),
             textcoords='figure fraction',
             fontsize=16,
             arrowprops=dict(facecolor='white', shrink=0.1)
            )
plt.show()_____no_output_____

This is a very scary but highly realistic scenario. Based on our reduced synthetic dataset, we have achieved a model that generalized really well on the test data. But when we ask for the prediction for the same point tested earlier (which we known is malignant), the prediction is now a benign tumor. We would have completely missed the tumor. To mitigate this, we can:
1. get more data around the space we are concerned about
2. consume predictions with caution when they are close to the decision boundary_____no_output_____

**MODELS ARE NOT CRYSTAL BALLS**
It's so important that before any machine learning, we really look at our data and ask ourselves if it is truly representative for the task we want to solve. The model itself may fit really well and generalize well on your data but if the data is of poor quality to begin with, the model cannot be trusted._____no_output_____# Models_____no_output_____Once you are confident that your data is of good quality and quantity, you can finally start thinking about modeling. The type of model you choose depends on many factors, including the task, type of data, complexity required, etc.

So once you figure out what type of model your task needs, start with simple models and then slowly add complexity. You don’t want to start with neural networks right away because that may not be right model for your data and task. Striking this balance in model complexity is one of the key tasks of your data scientists. **simple models → complex models**

_____no_output_____---

Subscribe to our newsletter and follow us on social media to get the latest updates!

_____no_output_____


# Notebook from lskywolfll/Red-Neuronal
Path: Red_Neuronal.ipynb

_____no_output_____

import numpy as np
import scipy as sc
import matplotlib.pyplot as plt

from sklearn.datasets import make_circles_____no_output_____# CREAR EL DATASET

#Numero de registro de nuestros datos
n = 500
#Cuantas caracteristicas tenemos de nuestros datos (en este caso son 2)
p = 2

#Factor = a la distancia entre los circulos, noise = ruido(puntos)
X, Y = make_circles(n_samples = n, factor= 0.5, noise=0.05)

#todos los valores y Añadir nueva dimension
Y = Y[:, np.newaxis]

plt.scatter(X[Y[:, 0] == 0, 0], X[Y[:, 0] == 0, 1], c="skyblue")
plt.scatter(X[Y[:, 0] == 1, 0], X[Y[:, 0] == 1, 1], c="salmon")
#Equal es igual a los ejes con la misma proporcion
plt.axis("equal")
plt.show()_____no_output_____# CLASE DE LA CAPA DE LA RED

class neural_layer():

  def __init__(self, n_conn, n_neur, act_f):
    #funcion de activacion de la capa (self ^) y poder usarlo para poder iniciar la funcion de la capa
    self.act_f = act_f
    #Vector que se refiera al parametro de baias(English) y cantidad de parametros de nuestras neuronas
    self.b = np.random.rand(1, n_neur)      * 2 - 1
    #Matriz W (cosas u objetos)
    self.W = np.random.rand(n_conn, n_neur) * 2 - 1
    _____no_output_____# FUNCIONES DE ACTIVACION

#lambda = funcion anonima en python para hacer algunas ejecuciones y funcion sigmoide
sigm = (lambda x: 1 / (1 + np.e ** (-x)),
        lambda x: x * (1 - x))
#^^ despues de la primera funcion viene la derivada de esta para el gradiente

#Funcion Relu
relu = lambda x: np.maximum(0, x)

#-5 a 5 es una forma lineal y cantidad de valores
#Comienzos osea => start, stop, cantidad, viendo la propiedad con ? se ven otros parametros opcionales dependiendo de la complejidad de lo que vayamos a crear
_x = np.linspace(-5 , 5, 100)
plt.plot(_x, relu(_x))_____no_output_____#Capa 0 de la red neuronal
   #parametros, neuronas, funcion
l0 = neural_layer(p, 4, sigm)
#parametros son 4 de acuerdo a las neuronas definidas en la primera capa
l1 = neural_layer(4, 8, sigm)
#...

#No es necesario que todas las capas tengan la misma funcion
def create_nn(topology, act_f):
  #La funcion enumerate nos da el indice y el objeto (video time= 27:41)
  #nn = neural network
  nn = []

  for l, layer in enumerate(topology[:-1]):
    #Tantas neuronas que tengan l + 1, capa oculta a la primera 4 neuroas por p
    nn.append(neural_layer(topology[l], topology[l + 1], act_f))

  return nn_____no_output_____#Numeros de neuronas que va a tener nuestra red y la ultima es 1 por ser la neurona de salida (resultados binaros 0 o 1)
topology = [p, 4, 8, 1]

neural_net = create_nn(topology, sigm)

#Como el error cuadratico medio, diferencia entre valor predicho y el real (derivada)
l2_cost = (lambda Yp, Yr: np.mean((Yp - Yr) ** 2),
           lambda Yp, Yr: (Yp - Yr))

def train(neural_net, X, Y, l2_cost, lr=0.5, train=True):
    #None y X por que no se permitia introducir datos aceptable se introdujeron datos para que este las procesara de manera correcta
    out = [(None, X)]

    #Forward Pass(pasarlo capa tras capa de las neuronas)
    for l, layer in enumerate(neural_net):

      #Suma Ponderada multiplicado matriz mente
      z = out[-1][1] @ neural_net[l].W + neural_net[l].b
      #Activacion
      a = neural_net[l].act_f[0](z)
      #Introducir dato al objeto out siendo la suma ponderada y la activacion
      out.append((z, a))

    if train:

      #Backward pass
      deltas = []
      #Len es lo que nos da todos los objetos dentro de neural_net u array, tambien la propiedad de revertir todo el rango de la columna 0 y neural_net
      for l in reversed(range(0, len(neural_net))):

        z = out[l + 1][0]
        a = out[l + 1][1]

        if l == len(neural_net) - 1:
          #Calcular Delta ultima capa NT: video min 54:03
          deltas.insert(0, l2_cost[1](a, Y) * neural_net[l].act_f[1](a))
        else:
          #Calcular delta respecto a capa previa
          deltas.insert(0, deltas[0] @ _W.T * neural_net[l].act_f[1](a))
        #baias en este caso no es nesaria declarar l + 1 ya que solo l ya esta hecha con ello por declararse en el codigo ya estaba hecha de esta manera
        _W = neural_net[l].W

        #Gradient descent
        neural_net[l].b = neural_net[l].b - np.mean(deltas[0], axis=0, keepdims=True) * lr
        neural_net[l].W = neural_net[l].W - out[l][1].T @ deltas[0] * lr
    #Suma ponderada -1 y activacion 1
    return out[-1][1]

train(neural_net, X, Y, l2_cost, 0.5)
print("")
# VISUALIZACIÓN Y TEST

import time
from IPython.display import clear_output

#Nueva red neuronal
neural_n = create_nn(topology, sigm)

#El Coste para obtener el dato
loss = []

#Iterar muchas veces el entrenamiento
for i in range(2500):

  # Entrenemos a la red!
  pY = train(neural_n, X, Y, l2_cost, lr=0.05)

  #Cada 25 iteraciones
  if i % 25 == 0:

    print(pY)
    #Calcular el coste y agrega un objeto al final usando la propiedad append(reservada del lenguaje)
    loss.append(l2_cost[0](pY, Y))
    #Resolucion
    res = 50
    #Malla de 50 x 50 osea lo que contendra nuestros datos de fondo se vera
    _x0 = np.linspace(-1.5, 1.5, res)
    _x1 = np.linspace(-1.5, 1.5, res)

    _Y = np.zeros((res, res))

    for i0, x0 in enumerate(_x0):
      for i1, x1 in enumerate(_x1):
        #Prediccion y por que es una prediccion es falso(rojo y azul)
        _Y[i0, i1] = train(neural_n, np.array([[x0, x1]]), Y, l2_cost, train=False)[0][0]
    #Visualizacion mostrada e ejecutando aqui
    plt.pcolormesh(_x0, _x1, _Y, cmap="coolwarm")
    plt.axis("equal")

    plt.scatter(X[Y[:,0] == 0, 0], X[Y[:,0] == 0, 1], c="skyblue")
    plt.scatter(X[Y[:,0] == 1, 0], X[Y[:,0] == 1, 1], c="salmon")
    #Borrar el contenido y volver a dibujarlo con los colores
    clear_output(wait=True)
    plt.show()
    plt.plot(range(len(loss)), loss)
    plt.show()
    time.sleep(0.5)  _____no_output_____np.zeros?_____no_output__________no_output_____


# Notebook from saketkc/EE-546-project
Path: notebooks/01.Simulate Data.ipynb

%pylab inline
import time
import scanpy
import pandas as pd
import seaborn as sns
sns.set_context('talk', font_scale=2)

from sklearn.manifold import MDS
from sklearn.cluster import SpectralClustering

from sklearn import cluster, datasets
from sklearn.neighbors import kneighbors_graph
from sklearn.preprocessing import StandardScalerPopulating the interactive namespace from numpy and matplotlib
def simulate_uniform_2d(n, low1=-1, high1=1, low2=-2, high2=2):
    """

    Parameters
    ----------
    n: integer
       Number of points

    d: integer
       Number of dimensions
    """
    X1 = np.random.random((n, 1)) * (high1-low1) + low1
    X2 = np.random.random((n, 1)) * (high2-low2) + low2
    return np.hstack([X1, X2])
_____no_output_____X1 = simulate_uniform_2d(5000, low1=-1, high1=1)
plt.scatter(X1[:,0], X1[:,1], alpha=0.4)_____no_output_____X1 = simulate_uniform_2d(1000, low1=-2, high1=2, low2=-1, high2=1)
fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(X1[:,0], X1[:,1], alpha=0.4)
X2 = simulate_uniform_2d(1000, low1=-1, high1=1, low2=-2, high2=2)
ax.scatter(X2[:,0], X2[:,1], alpha=0.4)

X = np.vstack([X1, X2])_____no_output_____embedding = MDS(n_components=2)
X_transformed = embedding.fit_transform(X)
color = pd.Series([sns.color_palette('Paired')[0]]*1000 + [sns.color_palette('Paired')[1]]*1000)
colors = pd.Series(sns.color_palette('Paired'))
plt.scatter(X_transformed[:, 0], X_transformed[:, 1], c=color, alpha=0.4)_____no_output_____

# Spectral Clustering_____no_output_____

clustering = SpectralClustering(n_clusters=2, assign_labels="discretize", random_state=0)
X_transformed = clustering.fit(X).fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=colors[X_transformed], alpha=0.4)_____no_output_____

clustering_names = [
    'MiniBatchKMeans', 'AffinityPropagation', 'MeanShift',
    'SpectralClustering', 'Ward', 'AgglomerativeClustering',
    'DBSCAN', 'Birch']

plt.figure(figsize=(len(clustering_names) * 2 + 3, 9.5))
plt.subplots_adjust(left=.02, right=.98, bottom=.001, top=.96, wspace=.05,
                    hspace=.01)

plot_num = 1

datasets = [(X, [0]*1000 + [1]*1000)]
for i_dataset, dataset in enumerate(datasets):
    X, y = dataset
    # normalize dataset for easier parameter selection
    X = StandardScaler().fit_transform(X)

    # estimate bandwidth for mean shift
    bandwidth = cluster.estimate_bandwidth(X, quantile=0.3)

    # connectivity matrix for structured Ward
    connectivity = kneighbors_graph(X, n_neighbors=1000, include_self=False)
    # make connectivity symmetric
    connectivity = 0.5 * (connectivity + connectivity.T)

    # create clustering estimators
    ms = cluster.MeanShift(bandwidth=bandwidth, bin_seeding=True)
    two_means = cluster.MiniBatchKMeans(n_clusters=2)
    ward = cluster.AgglomerativeClustering(n_clusters=2, linkage='ward',
                                           connectivity=connectivity)
    spectral = cluster.SpectralClustering(n_clusters=2,
                                          eigen_solver='arpack',
                                          affinity="nearest_neighbors")
    dbscan = cluster.DBSCAN(eps=.2)
    affinity_propagation = cluster.AffinityPropagation(damping=.9,
                                                       preference=-200)

    average_linkage = cluster.AgglomerativeClustering(
        linkage="average", affinity="cityblock", n_clusters=2,
        connectivity=connectivity)

    birch = cluster.Birch(n_clusters=2)
    clustering_algorithms = [
        two_means, affinity_propagation, ms, spectral, ward, average_linkage,
        dbscan, birch]

    for name, algorithm in zip(clustering_names, clustering_algorithms):
        # predict cluster memberships
        t0 = time.time()
        algorithm.fit(X)
        t1 = time.time()
        if hasattr(algorithm, 'labels_'):
            y_pred = algorithm.labels_.astype(np.int)
        else:
            y_pred = algorithm.predict(X)

        # plot
        plt.subplot(4, len(clustering_algorithms), plot_num)
        if i_dataset == 0:
            plt.title(name, size=18)
        plt.scatter(X[:, 0], X[:, 1], color=colors[y_pred].tolist(), s=10, alpha=0.4)

        if hasattr(algorithm, 'cluster_centers_'):
            centers = algorithm.cluster_centers_
            center_colors = colors[:len(centers)]
            plt.scatter(centers[:, 0], centers[:, 1], s=100, c=center_colors, alpha=0.4)
        plt.xlim(-2, 2)
        plt.ylim(-2, 2)
        plt.xticks(())
        plt.yticks(())
        plt.text(.99, .01, ('%.2fs' % (t1 - t0)).lstrip('0'),
                 transform=plt.gca().transAxes, size=15,
                 horizontalalignment='right')
        plot_num += 1
_____no_output_____from dipy.denoise.localpca import localpca
from dipy.denoise.pca_noise_estimate import pca_noise_estimate
from time import time
_____no_output_____sigma = pca_noise_estimate(data, gtab, correct_bias=True, smooth=3)
_____no_output_____print(pca_noise_estimate.__doc__)_____no_output_____


# Notebook from hariby/amazon-sagemaker-examples
Path: introduction_to_applying_machine_learning/gluon_recommender_system/gluon_recommender_system.ipynb

# Implementing a Recommender System with SageMaker, MXNet, and Gluon
_**Making Video Recommendations Using Neural Networks and Embeddings**_

---

---

*This work is based on content from the [Cyrus Vahid's 2017 re:Invent Talk](https://github.com/cyrusmvahid/gluontutorials/blob/master/recommendations/MLPMF.ipynb)*

## Contents

1. [Background](#Background)
1. [Setup](#Setup)
1. [Data](#Data)
  1. [Explore](#Explore)
  1. [Clean](#Clean)
  1. [Prepare](#Prepare)
1. [Train Locally](#Train-Locally)
  1. [Define Network](#Define-Network)
  1. [Set Parameters](#Set-Parameters)
  1. [Execute](#Execute)
1. [Train with SageMaker](#Train-with-SageMaker)
  1. [Wrap Code](#Wrap-Code)
  1. [Move Data](#Move-Data)
  1. [Submit](#Submit)
1. [Host](#Host)
  1. [Evaluate](#Evaluate)
1. [Wrap-up](#Wrap-up)

---

## Background

In many ways, recommender systems were a catalyst for the current popularity of machine learning.  One of Amazon's earliest successes was the "Customers who bought this, also bought..." feature, while the million dollar Netflix Prize spurred research, raised public awareness, and inspired numerous other data science competitions.

Recommender systems can utilize a multitude of data sources and ML algorithms, and most combine various unsupervised, supervised, and reinforcement learning techniques into a holistic framework.  However, the core component is almost always a model which which predicts a user's rating (or purchase) for a certain item based on that user's historical ratings of similar items as well as the behavior of other similar users.  The minimal required dataset for this is a history of user item ratings.  In our case, we'll use 1 to 5 star ratings from over 2M Amazon customers on over 160K digital videos.  More details on this dataset can be found at its [AWS Public Datasets page](https://s3.amazonaws.com/amazon-reviews-pds/readme.html).

Matrix factorization has been the cornerstone of most user-item prediction models.  This method starts with the large, sparse, user-item ratings in a single matrix, where users index the rows, and items index the columns.  It then seeks to find two lower-dimensional, dense matrices which, when multiplied together, preserve the information and relationships in the larger matrix.

![image](https://data-artisans.com/img/blog/factorization.svg)

Matrix factorization has been extended and genarlized with deep learning and embeddings.  These techniques allows us to introduce non-linearities for enhanced performance and flexibility.  This notebook will fit a neural network-based model to generate recommendations for the Amazon video dataset.  It will start by exploring our data in the notebook and even training a model on a sample of the data.  Later we'll expand to the full dataset and fit our model using a SageMaker managed training cluster.  We'll then deploy to an endpoint and check our method.

---

## Setup

_This notebook was created and tested on an ml.p2.xlarge notebook instance._

Let's start by specifying:

- The S3 bucket and prefix that you want to use for training and model data.  This should be within the same region as the Notebook Instance, training, and hosting.
- The IAM role arn used to give training and hosting access to your data. See the documentation for how to create these.  Note, if more than one role is required for notebook instances, training, and/or hosting, please replace the `get_execution_role()` call with the appropriate full IAM role arn string(s)._____no_output_____

bucket = '<your_s3_bucket_name_here>'
prefix = 'sagemaker/DEMO-gluon-recsys'

import sagemaker
role = sagemaker.get_execution_role()_____no_output_____

Now let's load the Python libraries we'll need for the remainder of this example notebook._____no_output_____

import os
import mxnet as mx
from mxnet import gluon, nd, ndarray
from mxnet.metric import MSE
import pandas as pd
import numpy as np
import sagemaker
from sagemaker.mxnet import MXNet
import boto3
import json
import matplotlib.pyplot as plt_____no_output_____

---
## Data

### Explore

Let's start by bringing in our dataset from an S3 public bucket.  As mentioned above, this contains 1 to 5 star ratings from over 2M Amazon customers on over 160K digital videos.  More details on this dataset can be found at its [AWS Public Datasets page](https://s3.amazonaws.com/amazon-reviews-pds/readme.html).

_Note, because this dataset is over a half gigabyte, the load from S3 may take ~10 minutes.  Also, since Amazon SageMaker Notebooks start with a 5GB persistent volume by default, and we don't need to keep this data on our instance for long, we'll bring it to the temporary volume (which has up to 20GB of storage).______no_output_____

!mkdir /tmp/recsys/
!aws s3 cp s3://amazon-reviews-pds/tsv/amazon_reviews_us_Digital_Video_Download_v1_00.tsv.gz /tmp/recsys/_____no_output_____

Let's read the data into a [Pandas DataFrame](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html) so that we can begin to understand it.

*Note, we'll set `error_bad_lines=False` when reading the file in as there appear to be a very small number of records which would create a problem otherwise.*_____no_output_____

df = pd.read_csv('/tmp/recsys/amazon_reviews_us_Digital_Video_Download_v1_00.tsv.gz', delimiter='\t',error_bad_lines=False)
df.head()_____no_output_____

We can see this dataset includes information like:

- `marketplace`: 2-letter country code (in this case all "US").
- `customer_id`: Random identifier that can be used to aggregate reviews written by a single author.
- `review_id`: A unique ID for the review.
- `product_id`: The Amazon Standard Identification Number (ASIN).  `http://www.amazon.com/dp/<ASIN>` links to the product's detail page.
- `product_parent`: The parent of that ASIN.  Multiple ASINs (color or format variations of the same product) can roll up into a single parent parent.
- `product_title`: Title description of the product.
- `product_category`: Broad product category that can be used to group reviews (in this case digital videos).
- `star_rating`: The review's rating (1 to 5 stars).
- `helpful_votes`: Number of helpful votes for the review.
- `total_votes`: Number of total votes the review received.
- `vine`: Was the review written as part of the [Vine](https://www.amazon.com/gp/vine/help) program?
- `verified_purchase`: Was the review from a verified purchase?
- `review_headline`: The title of the review itself.
- `review_body`: The text of the review.
- `review_date`: The date the review was written.

For this example, let's limit ourselves to `customer_id`, `product_id`, and `star_rating`.  Including additional features in our recommendation system could be beneficial, but would require substantial processing (particularly the text data) which would take us beyond the scope of this notebook.

*Note: we'll keep `product_title` on the dataset to help verify our recommendations later in the notebook, but it will not be used in algorithm training.*_____no_output_____

df = df[['customer_id', 'product_id', 'star_rating', 'product_title']]_____no_output_____

Because most people haven't seen most videos, and people rate fewer videos than we actually watch, we'd expect our data to be sparse.  Our algorithm should work well with this sparse problem in general, but we may still want to clean out some of the long tail.  Let's look at some basic percentiles to confirm._____no_output_____

customers = df['customer_id'].value_counts()
products = df['product_id'].value_counts()

quantiles = [0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.96, 0.97, 0.98, 0.99, 1]
print('customers\n', customers.quantile(quantiles))
print('products\n', products.quantile(quantiles))_____no_output_____

As we can see, only about 5% of customers have rated 5 or more videos, and only 25% of videos have been rated by 9+ customers.

### Clean

Let's filter out this long tail._____no_output_____

customers = customers[customers >= 5]
products = products[products >= 10]

reduced_df = df.merge(pd.DataFrame({'customer_id': customers.index})).merge(pd.DataFrame({'product_id': products.index}))_____no_output_____

Now, we'll recreate our customer and product lists since there are customers with more than 5 reviews, but all of their reviews are on products with less than 5 reviews (and vice versa)._____no_output_____

customers = reduced_df['customer_id'].value_counts()
products = reduced_df['product_id'].value_counts()_____no_output_____

Next, we'll number each user and item, giving them their own sequential index.  This will allow us to hold the information in a sparse format where the sequential indices indicate the row and column in our ratings matrix._____no_output_____

customer_index = pd.DataFrame({'customer_id': customers.index, 'user': np.arange(customers.shape[0])})
product_index = pd.DataFrame({'product_id': products.index,
                              'item': np.arange(products.shape[0])})

reduced_df = reduced_df.merge(customer_index).merge(product_index)
reduced_df.head()_____no_output_____

### Prepare

Let's start by splitting in training and test sets.  This will allow us to estimate the model's accuracy on videos our customers rated, but wasn't included in our training._____no_output_____

test_df = reduced_df.groupby('customer_id').last().reset_index()

train_df = reduced_df.merge(test_df[['customer_id', 'product_id']],
                            on=['customer_id', 'product_id'],
                            how='outer',
                            indicator=True)
train_df = train_df[(train_df['_merge'] == 'left_only')]_____no_output_____

Now, we can convert our Pandas DataFrames into MXNet NDArrays, use those to create a member of the SparseMatrixDataset class, and add that to an MXNet Data Iterator.  This process is the same for both test and control._____no_output_____

batch_size = 1024

train = gluon.data.ArrayDataset(nd.array(train_df['user'].values, dtype=np.float32),
                                nd.array(train_df['item'].values, dtype=np.float32),
                                nd.array(train_df['star_rating'].values, dtype=np.float32))
test  = gluon.data.ArrayDataset(nd.array(test_df['user'].values, dtype=np.float32),
                                nd.array(test_df['item'].values, dtype=np.float32),
                                nd.array(test_df['star_rating'].values, dtype=np.float32))

train_iter = gluon.data.DataLoader(train, shuffle=True, num_workers=4, batch_size=batch_size, last_batch='rollover')
test_iter = gluon.data.DataLoader(train, shuffle=True, num_workers=4, batch_size=batch_size, last_batch='rollover')_____no_output_____

---

## Train Locally

### Define Network

Let's start by defining the neural network version of our matrix factorization task.  In this case, our network is quite simple.  The main components are:
- [Embeddings](https://mxnet.incubator.apache.org/api/python/gluon/nn.html#mxnet.gluon.nn.Embedding) which turn our indexes into dense vectors of fixed size.  In this case, 64.
- [Dense layers](https://mxnet.incubator.apache.org/api/python/gluon.html#mxnet.gluon.nn.Dense) with ReLU activation.  Each dense layer has the same number of units as our number of embeddings.  Our ReLU activation here also adds some non-linearity to our matrix factorization.
- [Dropout layers](https://mxnet.incubator.apache.org/api/python/gluon.html#mxnet.gluon.nn.Dropout) which can be used to prevent over-fitting.
- Matrix multiplication of our user matrix and our item matrix to create an estimate of our rating matrix._____no_output_____

class MFBlock(gluon.HybridBlock):
    def __init__(self, max_users, max_items, num_emb, dropout_p=0.5):
        super(MFBlock, self).__init__()

        self.max_users = max_users
        self.max_items = max_items
        self.dropout_p = dropout_p
        self.num_emb = num_emb

        with self.name_scope():
            self.user_embeddings = gluon.nn.Embedding(max_users, num_emb)
            self.item_embeddings = gluon.nn.Embedding(max_items, num_emb)

            self.dropout_user = gluon.nn.Dropout(dropout_p)
            self.dropout_item = gluon.nn.Dropout(dropout_p)

            self.dense_user   = gluon.nn.Dense(num_emb, activation='relu')
            self.dense_item = gluon.nn.Dense(num_emb, activation='relu')

    def hybrid_forward(self, F, users, items):
        a = self.user_embeddings(users)
        a = self.dense_user(a)

        b = self.item_embeddings(items)
        b = self.dense_item(b)

        predictions = self.dropout_user(a) * self.dropout_item(b)
        predictions = F.sum(predictions, axis=1)
        return predictions_____no_output_____num_embeddings = 64

net = MFBlock(max_users=customer_index.shape[0],
              max_items=product_index.shape[0],
              num_emb=num_embeddings,
              dropout_p=0.5)
_____no_output_____

### Set Parameters

Let's initialize network weights and set our optimization parameters._____no_output_____

# Initialize network parameters
ctx = mx.gpu()
net.collect_params().initialize(mx.init.Xavier(magnitude=60),
                                ctx=ctx,
                                force_reinit=True)
net.hybridize()

# Set optimization parameters
opt = 'sgd'
lr = 0.02
momentum = 0.9
wd = 0.

trainer = gluon.Trainer(net.collect_params(),
                        opt,
                        {'learning_rate': lr,
                         'wd': wd,
                         'momentum': momentum})_____no_output_____

### Execute

Let's define a function to carry out the training of our neural network._____no_output_____

def execute(train_iter, test_iter, net, epochs, ctx):

    loss_function = gluon.loss.L2Loss()
    for e in range(epochs):

        print("epoch: {}".format(e))

        for i, (user, item, label) in enumerate(train_iter):
                user = user.as_in_context(ctx)
                item = item.as_in_context(ctx)
                label = label.as_in_context(ctx)

                with mx.autograd.record():
                    output = net(user, item)
                    loss = loss_function(output, label)

                loss.backward()
                trainer.step(batch_size)

        print("EPOCH {}: MSE ON TRAINING and TEST: {}. {}".format(e,
                                                                   eval_net(train_iter, net, ctx, loss_function),
                                                                   eval_net(test_iter, net, ctx, loss_function)))
    print("end of training")
    return net_____no_output_____

Let's also define a function which evaluates our network on a given dataset.  This is called by our `execute` function above to provide mean squared error values on our training and test datasets._____no_output_____

def eval_net(data, net, ctx, loss_function):
    acc = MSE()
    for i, (user, item, label) in enumerate(data):

            user = user.as_in_context(ctx)
            item = item.as_in_context(ctx)
            label = label.as_in_context(ctx)
            predictions = net(user, item).reshape((batch_size, 1))
            acc.update(preds=[predictions], labels=[label])

    return acc.get()[1]_____no_output_____

Now, let's train for a few epochs._____no_output_____

%%time

epochs = 3

trained_net = execute(train_iter, test_iter, net, epochs, ctx)_____no_output_____

#### Early Validation

We can see our training error going down, but our validation accuracy bounces around a bit.  Let's check how our model is predicting for an individual user.  We could pick randomly, but for this case, let's try user #6._____no_output_____

product_index['u6_predictions'] = trained_net(nd.array([6] * product_index.shape[0]).as_in_context(ctx),
                                              nd.array(product_index['item'].values).as_in_context(ctx)).asnumpy()
product_index.sort_values('u6_predictions', ascending=False)_____no_output_____

Now let's compare this to the predictions for another user (we'll try user #7)._____no_output_____

product_index['u7_predictions'] = trained_net(nd.array([7] * product_index.shape[0]).as_in_context(ctx),
                                              nd.array(product_index['item'].values).as_in_context(ctx)).asnumpy()
product_index.sort_values('u7_predictions', ascending=False)_____no_output_____

The predicted ratings are different between the two users, but the same top (and bottom) items for user #6 appear for #7 as well.  Let's look at the correlation across the full set of 38K items to see if this relationship holds._____no_output_____

product_index[['u6_predictions', 'u7_predictions']].plot.scatter('u6_predictions', 'u7_predictions')
plt.show()_____no_output_____

We can see that this correlation is nearly perfect.  Essentially the average rating of items dominates across users and we'll recommend the same well-reviewed items to everyone.  As it turns out, we can add more embeddings and this relationship will go away since we're better able to capture differential preferences across users.

However, with just a 64 dimensional embedding, it took 7 minutes to run just 3 epochs.  If we ran this outside of our Notebook Instance we could run larger jobs and move on to other work would improve productivity.

---

## Train with SageMaker

Now that we've trained on this smaller dataset, we can expand training in SageMaker's distributed, managed training environment.

### Wrap Code

To use SageMaker's pre-built MXNet container, we'll need to wrap our code from above into a Python script.  There's a great deal of flexibility in using SageMaker's pre-built containers, and detailed documentation can be found [here](https://github.com/aws/sagemaker-python-sdk#mxnet-sagemaker-estimators), but for our example, it consisted of:
1. Wrapping all data preparation into a `prepare_train_data` function (we could name this whatever we like)
1. Copying and pasting classes and functions from above word-for-word
1. Defining a `train` function that:
  1. Adds a bit of new code to pick up the input TSV dataset on the SageMaker Training cluster
  1. Takes in a dict of hyperparameters (which we specified as globals above)
  1. Creates the net and executes training_____no_output_____

!cat recommender.py_____no_output_____

### Test Locally

Now we can test our train function locally.  This helps ensure we don't have any bugs before submitting our code to SageMaker's pre-built MXNet container._____no_output_____

# %%time

# import recommender

# local_test_net, local_customer_index, local_product_index = recommender.train(
#     {'train': '/tmp/recsys/'},
#     {'num_embeddings': 64,
#      'opt': 'sgd',
#      'lr': 0.02,
#      'momentum': 0.9,
#      'wd': 0.,
#      'epochs': 3},
#     ['local'],
#     1)_____no_output_____

### Move Data

Holding our data in memory works fine when we're interactively exploring a sample of data, but for larger, longer running processes, we'd prfer to run them in the background with SageMaker Training.  To do this, let's move the dataset to S3 so that it can be picked up by SageMaker training.  This is perfect for use cases like periodic re-training, expanding to a larger dataset, or moving production workloads to larger hardware._____no_output_____

boto3.client('s3').copy({'Bucket': 'amazon-reviews-pds',
                         'Key': 'tsv/amazon_reviews_us_Digital_Video_Download_v1_00.tsv.gz'},
                        bucket,
                        prefix + '/train/amazon_reviews_us_Digital_Video_Download_v1_00.tsv.gz')_____no_output_____

### Submit

Now, we can create an MXNet estimator from the SageMaker Python SDK.  To do so, we need to pass in:
1. Instance type and count for our SageMaker Training cluster.  SageMaker's MXNet containers support distributed GPU training, so we could easily set this to multiple ml.p2 or ml.p3 instances if we wanted.
  - *Note, this would require some changes to our recommender.py script as we would need to setup the context an key value store properly, as well as determining if and how to distribute the training data.*
1. An S3 path for out model artifacts and a role with access to S3 input and output paths.
1. Hyperparameters for our neural network.  Since with a 64 dimensional embedding, our recommendations reverted too closely to the mean, let's increase this by an order of magnitude when we train outside of our local instance.  We'll also increase the epochs to see how our accuracy evolves over time. We'll leave all other hyperparameters the same.

Once we use `.fit()` this creates a SageMaker Training Job that spins up instances, loads the appropriate packages and data, runs our `train` function from `recommender.py`, wraps up and saves model artifacts to S3, and finishes by tearing down the cluster._____no_output_____

m = MXNet('recommender.py',
          py_version='py3',
          role=role,
          train_instance_count=1,
          train_instance_type="ml.p2.xlarge",
          output_path='s3://{}/{}/output'.format(bucket, prefix),
          hyperparameters={'num_embeddings': 512,
                           'opt': opt,
                           'lr': lr,
                           'momentum': momentum,
                           'wd': wd,
                           'epochs': 10},
         framework_version='1.1')

m.fit({'train': 's3://{}/{}/train/'.format(bucket, prefix)})_____no_output_____

---

## Host

Now that we've trained our model, deploying it to a real-time, production endpoint is easy._____no_output_____

predictor = m.deploy(initial_instance_count=1,
                     instance_type='ml.m4.xlarge')
predictor.serializer = None_____no_output_____

Now that we have an endpoint, let's test it out.  We'll predict user #6's ratings for the top and bottom ASINs from our local model.

*This could be done by sending HTTP POST requests from a separate web service, but to keep things easy, we'll just use the `.predict()` method from the SageMaker Python SDK.*_____no_output_____

predictor.predict(json.dumps({'customer_id': customer_index[customer_index['user'] == 6]['customer_id'].values.tolist(),
                              'product_id': ['B00KH1O9HW', 'B00M5KODWO']}))_____no_output_____

*Note, some of our predictions are actually greater than 5, which is to be expected as we didn't do anything special to account for ratings being capped at that value.  Since we are only looking to ranking by predicted rating, this won't create problems for our specific use case.*_____no_output_____### Evaluate

Let's start by calculating a naive baseline to approximate how well our model is doing.  The simplest estimate would be to assume every user item rating is just the average rating over all ratings.

*Note, we could do better by using each individual video's average, however, in this case it doesn't really matter as the same conclusions would hold.*_____no_output_____

print('Naive MSE:', np.mean((test_df['star_rating'] - np.mean(train_df['star_rating'])) ** 2))_____no_output_____

Now, we'll calculate predictions for our test dataset.

*Note, this will align closely to our CloudWatch output above, but may differ slightly due to skipping partial mini-batches in our eval_net function.*_____no_output_____

test_preds = []
for array in np.array_split(test_df[['customer_id', 'product_id']].values, 40):
    test_preds += predictor.predict(json.dumps({'customer_id': array[:, 0].tolist(),
                                                'product_id': array[:, 1].tolist()}))

test_preds = np.array(test_preds)
print('MSE:', np.mean((test_df['star_rating'] - test_preds) ** 2))_____no_output_____

We can see that our neural network and embedding model produces substantially better results (~1.27 vs 1.65 on mean square error).

For recommender systems, subjective accuracy also matters.  Let's get some recommendations for a random user to see if they make intuitive sense._____no_output_____

reduced_df[reduced_df['user'] == 6].sort_values(['star_rating', 'item'], ascending=[False, True])_____no_output_____

As we can see, user #6 seems to like sprawling dramamtic television series and sci-fi, but they dislike silly comedies.

Now we'll loop through and predict user #6's ratings for every common video in the catalog, to see which ones we'd recommend and which ones we wouldn't._____no_output_____

predictions = []
for array in np.array_split(product_index['product_id'].values, 40):
    predictions += predictor.predict(json.dumps({'customer_id': customer_index[customer_index['user'] == 6]['customer_id'].values.tolist() * array.shape[0],
                                                 'product_id': array.tolist()}))

predictions = pd.DataFrame({'product_id': product_index['product_id'],
                            'prediction': predictions})_____no_output_____titles = reduced_df.groupby('product_id')['product_title'].last().reset_index()
predictions_titles = predictions.merge(titles)
predictions_titles.sort_values(['prediction', 'product_id'], ascending=[False, True])_____no_output_____

Indeed, our predicted highly rated shows have some well-reviewed TV dramas and some sci-fi.  Meanwhile, our bottom rated shows include goofball comedies.

*Note, because of random initialization in the weights, results on subsequent runs may differ slightly.*

Let's confirm that we no longer have almost perfect correlation in recommendations with user #7._____no_output_____

predictions_user7 = []
for array in np.array_split(product_index['product_id'].values, 40):
    predictions_user7 += predictor.predict(json.dumps({'customer_id': customer_index[customer_index['user'] == 7]['customer_id'].values.tolist() * array.shape[0],
                                                       'product_id': array.tolist()}))
plt.scatter(predictions['prediction'], np.array(predictions_user7))
plt.show()_____no_output_____

---

## Wrap-up

In this example, we developed a deep learning model to predict customer ratings.  This could serve as the foundation of a recommender system in a variety of use cases.  However, there are many ways in which it could be improved.  For example we did very little with:
- hyperparameter tuning
- controlling for overfitting (early stopping, dropout, etc.)
- testing whether binarizing our target variable would improve results
- including other information sources (video genres, historical ratings, time of review)
- adjusting our threshold for user and item inclusion

In addition to improving the model, we could improve the engineering by:
- Setting the context and key value store up for distributed training
- Fine tuning our data ingestion (e.g. num_workers on our data iterators) to ensure we're fully utilizing our GPU
- Thinking about how pre-processing would need to change as datasets scale beyond a single machine

Beyond that, recommenders are a very active area of research and techniques from active learning, reinforcement learning, segmentation, ensembling, and more should be investigated to deliver well-rounded recommendations.

### Clean-up (optional)

Let's finish by deleting our endpoint to avoid stray hosting charges._____no_output_____

sagemaker.Session().delete_endpoint(predictor.endpoint)_____no_output_____