<jupyter_start><jupyter_text>INTRO<jupyter_code>%matplotlib inline<jupyter_output><empty_output><jupyter_text>ML TASKS![](./img/ai_ml_dl_rl.png)  ML PROJECT WORKFLOWWith Scikit Learn ![](./img/proj_workflow_sklearn_mapping.png) ---  Datasetsscikit-learn comes with a few standard datasets, for instance the iris and digits datasets for classification and the diabetes dataset for regression.<jupyter_code>import pandas as pd
import numpy as np
from sklearn import datasets
iris = datasets.load_iris() # https://scikit-learn.org/stable/datasets/toy_dataset.html#iris-plants-dataset
# diabetes = datasets.load_diabetes()<jupyter_output><empty_output><jupyter_text>Utilizziamo IRIS Dataset [wiki](https://it.wikipedia.org/wiki/Dataset_Iris)Il dataset Iris è un dataset multivariato introdotto da <NAME> nel 1936. Consiste in 150 istanze di Iris misurate da <NAME> e classificate secondo tre specie: - Iris setosa- Iris virginica- Iris versicolorLe quattro variabili considerate sono:- la lunghezza del sepalo- la larghezza del sepalo- la lunghezza del petalo- la larghezza del petalo creo DataFrame con il mio dataset IRIS<jupyter_code>iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
iris_df.head()<jupyter_output><empty_output><jupyter_text>aggiungo la variabile target (numerica) al DataFrame<jupyter_code>iris_df["target_num"] = iris.target
iris_df.head()<jupyter_output><empty_output><jupyter_text>aggiungo la variabile target (categorica) al DataFrame<jupyter_code>iris_df["target_class"] = iris_df["target_num"].apply(lambda x: iris.target_names[x])
iris_df.head()<jupyter_output><empty_output><jupyter_text>aggiungo una ulteriore variabile target binaria (per distinguere fra class = setosa o altro)<jupyter_code>iris_df["target_is_setosa"] = (iris_df["target_class"] == "setosa").astype(int)
iris_df.head()
import seaborn as sns
sns.pairplot(iris_df, hue="target_class")<jupyter_output><empty_output><jupyter_text>Model selectionModel selection methods: Train test split / Cross Validation / hyper-parameters tuning / etcVediamo ora la prima fase: split dei dati fra train e test set.<jupyter_code>from sklearn.model_selection import train_test_split<jupyter_output><empty_output><jupyter_text>Creazione di:- X (features: input per il modello)- y (variabile target per classificazione multi-classe) - y_binary (variabile target per classificazione binaria)<jupyter_code>X = iris_df[iris.feature_names]
y = iris_df["target_class"]
y_binary = iris_df["target_is_setosa"]
X.head()
y.head()
y.value_counts()
y_binary.value_counts() # classi sbilanciate!!<jupyter_output><empty_output><jupyter_text>suddivisione in train dataset e test dataset per classificazione binariaAttenzione: le nostre classi sono sbilanciate<jupyter_code>X_train_binary, X_test_binary, y_train_binary, y_test_binary, = train_test_split(X, y_binary, test_size=0.3, shuffle=True, random_state=42) 
X_train_binary.shape, X_test_binary.shape, y_train_binary.shape, y_test_binary.shape
y_binary.value_counts() / len(y_binary)
y_train_binary.value_counts() / len(y_train_binary)
y_test_binary.value_counts() / len(y_test_binary)<jupyter_output><empty_output><jupyter_text>La percentuale di positivi nel train e test non viene mantenuta.Sia il Train che il Test dataset devono essere il più rappresentativi possibile dei dati completi originali, quindi utilizziamo una suddivisione *stratified*.Soprattuto quando il dataset è sbilanciato e non troppo grande (come in questo caso), è consigliato usare il parametro **stratify**.<jupyter_code>X_train_binary, X_test_binary, y_train_binary, y_test_binary, = train_test_split(X, y_binary, test_size=0.3, shuffle=True, stratify=y_binary, random_state=42) 
X_train_binary.shape, X_test_binary.shape, y_train_binary.shape, y_test_binary.shape
y_train_binary.value_counts() / len(y_train_binary)
y_test_binary.value_counts() / len(y_test_binary)<jupyter_output><empty_output><jupyter_text>suddivisione in train dataset e test dataset per classificazione multiclasse<jupyter_code>X_train, X_test, y_train, y_test, = train_test_split(X, y, test_size=0.3, shuffle=True, stratify=y, random_state=42) 
X.shape, y.shape
X_train.shape, X_test.shape, y_train.shape, y_test.shape
y_train.value_counts()
y_test.value_counts()<jupyter_output><empty_output><jupyter_text>Preprocessing (Transformers)Includes scaling, centering, normalization, binarization methods.Durante la fase di preprocessing è possibile trasformare sia le features in input (X) che la variabile target. Vediamo qualche esempio.<jupyter_code>from sklearn.preprocessing import LabelBinarizer, LabelEncoder # transformers for labels (y)
from sklearn.preprocessing import StandardScaler, MinMaxScaler, OneHotEncoder # transformers for features (X)<jupyter_output><empty_output><jupyter_text>Trasformiamo la label multiclasse in numerica<jupyter_code>le = LabelEncoder()

y_train_le = pd.Series(le.fit_transform(y_train), index=y_train.index)

y_test_le = pd.Series(le.transform(y_test), index=y_test.index)
y_train_le.shape, y_test_le.shape
y_train[:10]
y_train_le[:10]
le.classes_
le.inverse_transform([0,1,2])<jupyter_output><empty_output><jupyter_text>Trasformiamo le features in input (X) standardizzandole: - rimuovendo la media- scalandole su 1 unità di varianzaLa standardizzazione applicata è quello dello z-score.```z = (x - u) / s```dove:- *u* è la madia degli esempi di training- *s* è la deviazione standard degli esempi di trainingQuesta è una trasformazione richeista da molti algoritmi di Machine Learning (*estimators*) che altrimenti funzionerebbero in modo non ottimale. Per es. le SVM o alcuni modelli lineari potrebbero dare importanza maggiore a features con magnitudo maggiore, trascurando il potere predittivo delle altre.<jupyter_code>ss = StandardScaler()
ss.fit(X_train)
X_train_ss = pd.DataFrame(ss.transform(X_train), columns=X_train.columns, index=X_train.index)
X_test_ss = pd.DataFrame(ss.transform(X_test), columns=X_test.columns, index=X_test.index)

X_train.shape, X_train_ss.shape, X_test.shape, X_test_ss.shape
X_train[:10]
X_train_ss[:10]<jupyter_output><empty_output><jupyter_text>Gli z-scores vengono calcolati indipendentemente per ogni feature, possiamo verificarlo osservando le *u* e le *s* di ogni feature nel nostro transformer.<jupyter_code>ss.feature_names_in_
ss.mean_
ss.scale_<jupyter_output><empty_output><jupyter_text>Non lo vedremo negli esempi, ma un'altra tecnica molto usata è quella del [MinMaxScaler](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html).Non esiste una regola a priori che ci dica quando è meglio usare una tecnica oppure un'altra.In linea di massima possiamo dire che se la distribuzione è "a campana" è meglio usare StandardScaler, altrimenti MinMaxScaler.  EstimatorsVediamo ora alcuni esempi di classificatori per capire come funzionano gli estimators.<jupyter_code>from sklearn.tree import DecisionTreeClassifier, plot_tree<jupyter_output><empty_output><jupyter_text>definiamo un Decision Tree per una classificazione binaria<jupyter_code>clf = DecisionTreeClassifier(max_depth=2, random_state=42)
clf<jupyter_output><empty_output><jupyter_text>addestramento<jupyter_code>%%time
clf.fit(X_train_binary, y_train_binary)
# previsione sul test dataset
y_pred_binary = clf.predict(X_test_binary)
X_test_binary.shape, y_pred_binary.shape
y_pred_binary[:10]
y_test_binary[:10].values<jupyter_output><empty_output><jupyter_text>visualizzazione del modello addestrato<jupyter_code>from matplotlib import pyplot as plt
plot_tree(clf)
plt.show()
X.columns[2]<jupyter_output><empty_output><jupyter_text>osserviamo che viene creato un albero binario che possiamo convertire nella regola:- se *lunghezza petalo = 2.6* allora è *iris-setosa* altrimenti *no*  vediamo ora un classificatore multiclasseUtilizzando un meta-classificatore che ci permetta di fare un apprendimento multi-classe (con classi esclusive: ogni elemento può appartenere ad una sola classe).Questo approccio, chiamato One-vs-Rest (o One-vs-All) consiste nel fare training di 1 classificatore binario per ogni classe usando come variabile target una classe contro tutte le altre - o, in altro modo, la presenza o assenza di tale classe.Esattamente quello che abbiamo fatto con il DecisionTree sulla classe *iris-setosa* replicato per tutte le classi del dataset.<jupyter_code>from sklearn.svm import SVC
from sklearn.multiclass import OneVsRestClassifier # estimator<jupyter_output><empty_output><jupyter_text>In questo caso useremo una SVM.Come anticipato prima questo algoritmo è sensibile ai dati di grande magnitudo, quindi andremo ad utilizzare le variabili precedentemente standardizzate.<jupyter_code>ovr_clf = OneVsRestClassifier(estimator=SVC(random_state=42))
display(X_train_ss.head())
display(y_train_le.head())<jupyter_output><empty_output><jupyter_text>addestramento<jupyter_code>%%time
ovr_clf.fit(X_train_ss, y_train_le)<jupyter_output>Wall time: 11 ms<jupyter_text>previsione sul test dataset<jupyter_code>y_pred_ovr = ovr_clf.predict(X_test_ss)
y_test_le.shape, y_pred_ovr.shape
y_test_le[:10].values
y_pred_ovr[:10]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08665131073818141, 'fraction_numerical': 0.005484260173302622, 'mean_word_length': 8.06361829025845, 'pattern_counts': {'":': 0, '<': 94, '<?xml version=': 0, '>': 94, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '23', 'dir': 'jupyter-structured-clean-dedup', 'id': '264008', 'license': 'MIT', 'n_tokens_mistral': 3278, 'n_tokens_neox': 3227, 'n_words': 758, 'path': 'PercorsoIntroML/notebooks/01-intro-ml-sklearn-parte1.ipynb', 'repo_name': 'nick87ds/MaterialeSerate', 'size': '9132'}