Commit 4efbdf61 authored by Federico Mestrone's avatar Federico Mestrone
Browse files

Added files for the Datalab demos

parent b6d5162e
%% Cell type:markdown id: tags:
<h1>Analisi di dati con BigQuery</h1>
In questa demo e nella successiva svilupperemo un modello di machine learning per predirre la richiesta di taxi in New York, utilizzando dei dati pubblici messi a disposizione del governo della città e dalle stazioni metereologiche statunitensi. Questi dati sono riportati in tabelle di BigQuery cui chiunque può avere accesso.
Abbiamo tre obiettivi principali in questa demo
1) imparare a utilizzare BigQuery, scrivere query SQL sofisticate, capire come le query che scriviamo hanno impatto sui costi
2) fare un po' di pratica con Datalab, stabilire quando e per cosa si possa utilizzare, imparare i comandi per creare e gestire le istanze
3) farsi un'idea del processo di analisi prelimare dei dati per stabilire le correlazioni da approfondire con il Machine Learning
Non si può diventare esperti di ML in cinque minuti, ma speriamo di darvi una buona introduzione e stimolarvi a volerne sapere di più!
%% Cell type:code id: tags:
``` python
# importiamo le librerie necessarie per utilizzare BigQuery in Python e per la manipolazione avanzata dei dati in Python
import google.datalab.bigquery as bq
import pandas as pd # Pandas (strutture dati avanzate per l'analisi)
import numpy as np # NumPy (libreria per calcoli scientifici)
import shutil # operazioni su file e gruppi di file
```
%% Cell type:code id: tags:
``` python
%bq tables describe --name bigquery-public-data.new_york.tlc_yellow_trips_2015
# vediamo la struttura della tabella che utilizzeremo nel resto del notebook
```
%% Cell type:markdown id: tags:
<h2> Analisi della richiesta di taxi per giorno </h2>
Ora usiamo SQL in BigQuery per ottenere la domanda totale di taxi ogni giorno dell'anno nel 2015. Cominciamo con l'estrazione del giorno dell'anno dalla data di inizio di ogni viaggio.
%% Cell type:code id: tags:
``` python
%bq query
# la funzione EXTRACT tira fuori parti specifiche di una data dal valore passato come parametro
# in questo caso noi prendiamo solo il giorno dell'anno
SELECT
EXTRACT (DAYOFYEAR from pickup_datetime) AS daynumber # minimizzare i costi restituendo solo i valori che servono
FROM `bigquery-public-data.new_york.tlc_yellow_trips_2015`
LIMIT 15 # limitare il numero di risultati non diminuisce il costo della query
```
%% Cell type:markdown id: tags:
Ora che abbiamo l'elenco dei giorni, calcoliamo il totale di viaggi in taxi effettuati ogni giorno.
Utilizzeremo una query con nome in Datalab, che ci consente di passare il valore di uno o più parametri e di riutilizzare la query più volte.
Nel nostro caso la query prenderà l'anno come input e calcola il totale di viaggi per ogni giorno di quell'anno.
%% Cell type:code id: tags:
``` python
%bq query -n taxiquery
# l'opzione -n crea una query con nome utilizzabile in Python come funzione
WITH trips AS ( # WITH trips : crea una view temporanea utilizzabile in questo comando
SELECT EXTRACT (DAYOFYEAR from pickup_datetime) AS daynumber
FROM `bigquery-public-data.new_york.tlc_yellow_trips_*` # tabella partizionata per anno
where _TABLE_SUFFIX = @YEAR # variabile speciale utlizzata con tabelle partizionate
)
SELECT daynumber, COUNT(1) AS numtrips FROM trips # utilizza la view creata prima
GROUP BY daynumber ORDER BY daynumber
```
%% Cell type:code id: tags:
``` python
# prepara il parametro YEAR per la query con nome
query_parameters = [
{
'name': 'YEAR',
'parameterType': {'type': 'STRING'},
'parameterValue': {'value': 2015}
}
]
# invoca la query con nome e transforma il risultato in un dataframe di Pandas
trips = taxiquery.execute(query_params=query_parameters).result().to_dataframe()
# mostra solo i primi 15 risultati del dataframe
trips[:15]
```
%% Cell type:markdown id: tags:
<h2> Dati sul tempo </h2>
Abbiamo il sospetto che il tempo possa influenzare l'utilizzo dei taxi. Magari in un giorno di pioggia o quando fa particolarmente freddo le persone che normalmente andrebbero a piedi preferiscono prendere un taxi...
Con BigQuery possiamo unire e combinare le informazioni da qualunque fonte che il prodotto sia in grado di leggere e a cui - ovviamente - abbiamo accesso, inlcusi dati federati o in altri progetti.
%% Cell type:code id: tags:
``` python
%bq tables describe --name bigquery-public-data.noaa_gsod.stations
# vediamo la struttura della tabella che contiene l'elenco delle stazioni metereologiche
```
%% Cell type:code id: tags:
``` python
%bq tables describe --name bigquery-public-data.noaa_gsod.gsod2015
# e la struttura della tabella con i dati metereologici di ciascuna stazione
```
%% Cell type:code id: tags:
``` python
%bq query
# Prendiamo i dati di tutte le stazioni metereologiche di New York nella zone dell'aeroporto di La Guardia
SELECT * FROM `bigquery-public-data.noaa_gsod.stations`
WHERE state = 'NY' AND wban != '99999' AND name LIKE '%LA GUARDIA%' # troviamo la stazione che ci serve poi limitiamo le query successive per minimizzare i costi
```
%% Cell type:markdown id: tags:
Ora che abbiamo trovato la stazione metereologica di La Guardia, andiamo a raccogliere la temperature minima e massima e la quantità di pioggia per i vari giorni dell'anno.
%% Cell type:code id: tags:
``` python
%bq query -n wxquery
# -n per una query con nome
SELECT EXTRACT (DAYOFYEAR FROM CAST(CONCAT(@YEAR,'-',mo,'-',da) AS TIMESTAMP)) AS daynumber, # calcola il giorno dell'anno
EXTRACT (DAYOFWEEK FROM CAST(CONCAT(@YEAR,'-',mo,'-',da) AS TIMESTAMP)) AS dayofweek, # calcola il giorno della settimana
`min` AS mintemp, `max` AS maxtemp, IF(prcp=99.99,0,prcp) AS rain # backtick per chiarezza (min e max sono anche nomi di funzioni)
FROM `bigquery-public-data.noaa_gsod.gsod*` # tabella partizionata con wildcard - il backtick è obbligatorio qui!
WHERE stn='725030' AND _TABLE_SUFFIX = @YEAR # variabile speciale per il suffisso (_TABLE_SUFFIX) e parametro della query con nome (@YEAR)
ORDER BY daynumber DESC
```
%% Cell type:code id: tags:
``` python
# prepara il parametro YEAR per la query con nome
query_parameters = [
{
'name': 'YEAR',
'parameterType': {'type': 'STRING'},
'parameterValue': {'value': 2015}
}
]
# invoca la query con nome e transforma il risultato in un dataframe di Pandas
weather = wxquery.execute(query_params=query_parameters).result().to_dataframe()
# mostra solo i primi 15 risultati del dataframe
weather[:15]
```
%% Cell type:markdown id: tags:
<h2> Unire i dati </h2>
Con Pandas possiamo prendere le due dataframe create in precedenza e unirle, utilizzando il giorno dell'anno come punto di unione. Il concetto è simile a quello di una JOIN in SQL.
%% Cell type:code id: tags:
``` python
# metti insieme le informazioni sul tempo con quelle sui viaggi in taxi, utilizzando il giorno dell'anno come collante
data = pd.merge(weather, trips, on='daynumber')
# mostra i primi 15 risultati
data[:15]
```
%% Cell type:markdown id: tags:
Ora che abbiamo messo insieme i dati metereologici con quelli sui viaggi in taxi, vediamo se troviamo delle correlazioni evidenti che meritino di essere approfondite e che possono aiutarci a creare un modello di previsione per la domanda di taxi in determinati giorni.
Cominciamo col disegnare un grafico di viaggi in relazione alla temperatura massima...
Pandas ha una funzione "plot" che fa esattamente quello che ci serve adesso.
%% Cell type:code id: tags:
``` python
j = data.plot(kind='scatter', x='maxtemp', y='numtrips')
```
%% Cell type:markdown id: tags:
Anche se il grafico ha molta dispersione e non si possono raggiungere conclusioni definitive, possiamo comunque notare una certa tendenza, quindi decidiamo di utilizzare le informazioni metereologiche come correlatore per determinare il numero di viaggi in taxi in una giornata.
Proviamo a vedere se c'è una relazione tra numero di viaggi e giorno della settimana.
%% Cell type:code id: tags:
``` python
j = data.plot(kind='scatter', x='dayofweek', y='numtrips')
```
%% Cell type:markdown id: tags:
Sembra che qui ci sia un pattern più chiaro. La gente usa i taxi di più verso la fine della settimana. Potrebbe avere a che fare con il turismo, o con il desiderio di essere più comodi man mano che la stanchezza della settimana lavorativa si fa sentire... ma possiamo dire che sicuramente venerdì, sabato e domenica il numero di viaggi in taxi è maggiore.
Proviamo ancora una cosa! Fissiamo il giorno della settimana, e vediamo se tolto quel fattore, notiamo delle correlazioni più chiare tra temperatura e viaggi in taxi...
%% Cell type:code id: tags:
``` python
j = data[data['dayofweek'] == 7].plot(kind='scatter', x='maxtemp', y='numtrips')
```
%% Cell type:markdown id: tags:
Il un grafico è ancora non molto definitivo, ma parrebbe esserci una più stretta relazione vista così. Decidiamo pertanto di utilizzare anche il giorno della settimana come correlatore per determinare il numero di viaggi in taxi in una giornata.
%% Cell type:markdown id: tags:
Tenete a mente queste due decisioni che abbiamo preso in questa demo, perché ne riparleremo nella prossima!
%% Cell type:markdown id: tags:
<em>Copyright 2019 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License</em>
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
<h1> Machine Learning con Tensorflow </h1>
In questa demo svilupperemo un modello di machine learning per predirre la richiesta di taxi a New York in determinati giorni, utilizzando i dati pubblici sui viaggi in taxi a New York e sulle condizioni metereologiche all'aereoporto di La Guardia che abbiamo usato anche nella demo precedente.
Abbiamo tre obiettivi principali in questa demo
1) avere un'idea di TensorFlow, come funziona, e quando usarlo
2) fare ancora un po' di pratica con Datalab
3) farsi un'idea del processo di analisi cui si sottopongono i dati nella creazione di modelli di Machine Learning
Non si può diventare esperti di ML in cinque minuti, ma speriamo di darvi una buona introduzione e stimolarvi a volerne sapere di più!
%% Cell type:markdown id: tags:
Se vogliamo inquadrare quello che stiamo facendo nel processo di sviluppo di modelli ML che ci ha spiegato Riccardo, a questo punto noi abbiamo terminato la fase 1, quella della raccolta di dati e analisi preliminare per stabilire feature e farci un'idea del modello che plausibilmente ci darà delle previsioni affidabili.
Per riassumere quindi, presi dei dati pubblicamente disponibili sui viaggi in taxi a NY e informazioni metereologiche all'aeroporto di La Guardia a NY, ci siamo cimentati nel capire possibili relazioni tra le varie feature con query in BigQuery, calcoli in Pandas, e grafici plottati all'interno di Datalab, e siamo così arrivati alla conclusione che il giorno della settimana e il tempo in un determinato giorno hanno un chiaro effetto sul numero di viaggi in taxi. Vogliamo quindi approfondire con Machine Learning, ripartendo da dove eravamo rimasti nella demo su BigQuery, e concludendo la prima fase di raccolta e analisi dei dati vista nella lezione con l'aggiunta di ancora più dati al nostro dataframe: uniamo le statistiche anche per il 2014 e il 2016.
%% Cell type:code id: tags:
``` python
# importiamo le librerie necessarie per utilizzare BigQuery in Python e per la manipolazione avanzata dei dati in Python
import google.datalab.bigquery as bq
import pandas as pd # Pandas (strutture dati avanzate per l'analisi)
import numpy as np # NumPy (libreria per calcoli scientifici)
import shutil # operazioni su file e gruppi di file
```
%% Cell type:code id: tags:
``` python
%bq query -n taxiquery
# l'opzione -n crea una query con nome utilizzabile in Python come funzione
WITH trips AS ( # WITH trips : crea una view temporanea utilizzabile in questo comando
SELECT EXTRACT (DAYOFYEAR from pickup_datetime) AS daynumber
FROM `bigquery-public-data.new_york.tlc_yellow_trips_*` # tabella partizionata per anno
where _TABLE_SUFFIX = @YEAR # variabile speciale utlizzata con tabelle partizionate
)
SELECT daynumber, COUNT(1) AS numtrips FROM trips # utilizza la view creata prima
GROUP BY daynumber ORDER BY daynumber
```
%% Cell type:code id: tags:
``` python
%bq query -n wxquery
# -n per una query con nome
SELECT EXTRACT (DAYOFYEAR FROM CAST(CONCAT(@YEAR,'-',mo,'-',da) AS TIMESTAMP)) AS daynumber, # calcola il giorno dell'anno
EXTRACT (DAYOFWEEK FROM CAST(CONCAT(@YEAR,'-',mo,'-',da) AS TIMESTAMP)) AS dayofweek, # calcola il giorno della settimana
`min` AS mintemp, `max` AS maxtemp, IF(prcp=99.99,0,prcp) AS rain # backtick per chiarezza (min e max sono anche nomi di funzioni)
FROM `bigquery-public-data.noaa_gsod.gsod*` # tabella partizionata con wildcard - il backtick è obbligatorio qui!
WHERE stn='725030' AND _TABLE_SUFFIX = @YEAR # variabile speciale per il suffisso (_TABLE_SUFFIX) e parametro della query con nome (@YEAR)
ORDER BY daynumber DESC
```
%% Cell type:code id: tags:
``` python
# crea un dataframe vuoto
data2 = pd.DataFrame()
# aggiungi i dati dei tre anni disponibili
for year in [2014, 2015, 2016]:
query_parameters = [
{
'name': 'YEAR',
'parameterType': {'type': 'STRING'},
'parameterValue': {'value': year}
}
]
weather = wxquery.execute(query_params=query_parameters).result().to_dataframe()
trips = taxiquery.execute(query_params=query_parameters).result().to_dataframe()
data_for_year = pd.merge(weather, trips, on='daynumber') # unisci dati sui viaggi in taxi e dati metereologici
data2 = pd.concat([data2, data_for_year]) # aggiungi l'anno in elaborazione al dataframe finale
# mostra i primi 15 valori del dataframe finale
data2[:15]
```
%% Cell type:markdown id: tags:
Come Riccardo ci ha spiegato, nell'addestramento di un modello solitamente si dividono i dati in training data e test data. Noi utilizzeremo l'80% per l'addestramento e il 20% per il testing.
We'll use 80% of our dataset for training and 20% of the data for testing the model we have trained. Let's shuffle the rows of the Pandas dataframe so that this division is random. The predictor (or input) columns will be every column in the database other than the number-of-trips (which is our target, or what we want to predict).
The machine learning models that we will use -- linear regression and neural networks -- both require that the input variables are numeric in nature.
The day of the week, however, is a categorical variable (i.e. Tuesday is not really greater than Monday). So, we should create separate columns for whether it is a Monday (with values 0 or 1), Tuesday, etc.
Against that, we do have limited data (remember: the more columns you use as input features, the more rows you need to have in your training dataset), and it appears that there is a clear linear trend by day of the week. So, we will opt for simplicity here and use the data as-is. Try uncommenting the code that creates separate columns for the days of the week and re-run the notebook if you are curious about the impact of this simplification.
%% Cell type:code id: tags:
``` python
import tensorflow as tf
shuffled = data2.sample(frac=1, random_state=13)
# It would be a good idea, if we had more data, to treat the days as categorical variables
# with the small amount of data, we have though, the model tends to overfit
#predictors = shuffled.iloc[:,2:5]
#for day in range(1,8):
# matching = shuffled['dayofweek'] == day
# key = 'day_' + str(day)
# predictors[key] = pd.Series(matching, index=predictors.index, dtype=float)
predictors = shuffled.iloc[:,1:5]
predictors[:5]
```
%% Cell type:code id: tags:
``` python
shuffled[:5]
```
%% Cell type:code id: tags:
``` python
targets = shuffled.iloc[:,5]
targets[:5]
```
%% Cell type:markdown id: tags:
Let's update our benchmark based on the 80-20 split and the larger dataset.
%% Cell type:code id: tags:
``` python
trainsize = int(len(shuffled['numtrips']) * 0.8)
avg = np.mean(shuffled['numtrips'][:trainsize])
rmse = np.sqrt(np.mean((targets[trainsize:] - avg)**2))
print('Just using average={0} has RMSE of {1}'.format(avg, rmse))
```
%% Cell type:markdown id: tags:
<h2> Linear regression with tf.contrib.learn </h2>
We scale the number of taxicab rides by 400,000 so that the model can keep its predicted values in the [0-1] range. The optimization goes a lot faster when the weights are small numbers. We save the weights into ./trained_model_linear and display the root mean square error on the test dataset.
%% Cell type:code id: tags:
``` python
SCALE_NUM_TRIPS = 600000.0
trainsize = int(len(shuffled['numtrips']) * 0.8)
testsize = len(shuffled['numtrips']) - trainsize
npredictors = len(predictors.columns)
noutputs = 1
tf.logging.set_verbosity(tf.logging.WARN) # change to INFO to get output every 100 steps ...
shutil.rmtree('./trained_model_linear', ignore_errors=True) # so that we don't load weights from previous runs
estimator = tf.contrib.learn.LinearRegressor(model_dir='./trained_model_linear',
feature_columns=tf.contrib.learn.infer_real_valued_columns_from_input(predictors.values))
print("starting to train ... this will take a while ... use verbosity=INFO to get more verbose output")
def input_fn(features, targets):
return tf.constant(features.values), tf.constant(targets.values.reshape(len(targets), noutputs)/SCALE_NUM_TRIPS)
estimator.fit(input_fn=lambda: input_fn(predictors[:trainsize], targets[:trainsize]), steps=10000)
pred = np.multiply(list(estimator.predict(predictors[trainsize:].values)), SCALE_NUM_TRIPS )
rmse = np.sqrt(np.mean(np.power((targets[trainsize:].values - pred), 2)))
print('LinearRegression has RMSE of {0}'.format(rmse))
```
%% Cell type:markdown id: tags:
The RMSE here (57K) is lower than the benchmark (62K) indicates that we are doing about 10% better with the machine learning model than we would be if we were to just use the historical average (our benchmark).
%% Cell type:markdown id: tags:
<h2> Neural network with tf.contrib.learn </h2>
Let's make a more complex model with a few hidden nodes.
%% Cell type:code id: tags:
``` python
SCALE_NUM_TRIPS = 600000.0
trainsize = int(len(shuffled['numtrips']) * 0.8)
testsize = len(shuffled['numtrips']) - trainsize
npredictors = len(predictors.columns)
noutputs = 1
tf.logging.set_verbosity(tf.logging.WARN) # change to INFO to get output every 100 steps ...
shutil.rmtree('./trained_model', ignore_errors=True) # so that we don't load weights from previous runs
estimator = tf.contrib.learn.DNNRegressor(model_dir='./trained_model',
hidden_units=[5, 5],
feature_columns=tf.contrib.learn.infer_real_valued_columns_from_input(predictors.values))
print("starting to train ... this will take a while ... use verbosity=INFO to get more verbose output")
def input_fn(features, targets):
return tf.constant(features.values), tf.constant(targets.values.reshape(len(targets), noutputs)/SCALE_NUM_TRIPS)
estimator.fit(input_fn=lambda: input_fn(predictors[:trainsize], targets[:trainsize]), steps=10000)
pred = np.multiply(list(estimator.predict(predictors[trainsize:].values)), SCALE_NUM_TRIPS )
rmse = np.sqrt(np.mean((targets[trainsize:].values - pred)**2))
print('Neural Network Regression has RMSE of {0}'.format(rmse))
```
%% Cell type:markdown id: tags:
Using a neural network results in similar performance to the linear model when I ran it -- it might be because there isn't enough data for the NN to do much better. (NN training is a non-convex optimization, and you will get different results each time you run the above code).
%% Cell type:markdown id: tags:
<h2> Running a trained model </h2>
So, we have trained a model, and saved it to a file. Let's use this model to predict taxicab demand given the expected weather for three days.
Here we make a Dataframe out of those inputs, load up the saved model (note that we have to know the model equation -- it's not saved in the model file) and use it to predict the taxicab demand.
%% Cell type:code id: tags:
``` python
input = pd.DataFrame.from_dict(data =
{'dayofweek' : [4, 5, 6],
'mintemp' : [60, 40, 50],
'maxtemp' : [70, 90, 60],
'rain' : [0, 0.5, 0]})
# read trained model from ./trained_model
estimator = tf.contrib.learn.LinearRegressor(model_dir='./trained_model_linear',
feature_columns=tf.contrib.learn.infer_real_valued_columns_from_input(input.values))
pred = np.multiply(list(estimator.predict(input.values)), SCALE_NUM_TRIPS )
print(pred)
```
%% Cell type:markdown id: tags:
Copyright 2017 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment