Corso
La data science è un campo interdisciplinare che usa matematica e statistica avanzata per fare previsioni. Tutti gli algoritmi di data science utilizzano direttamente o indirettamente concetti matematici. Una solida comprensione della matematica ti aiuterà a sviluppare soluzioni innovative di data science, come un sistema di raccomandazione. Se sei bravo in matematica, il passaggio alla data science sarà più semplice. Come data scientist, devi utilizzare i concetti fondamentali della matematica per risolvere problemi.
Oltre alla matematica, ti servono anche conoscenze del dominio, capacità di programmazione, competenze di business, capacità analitiche e una mentalità curiosa. Per un data scientist non c’è modo di evitare la matematica. Devi acquisire e insegnare a te stesso le basi di matematica e statistica per diventare un data scientist.
Scalare e vettore
- Scalari: Uno scalare è un singolo numero che interagisce con un vettore nello spazio tramite la moltiplicazione scalare.
- Vettori: Un vettore V è un insieme ordinato di elementi. Un vettore è un array di numeri che può essere una riga o una colonna. I vettori possono essere sommati tra loro e moltiplicati per un numero reale noto come scalare.

# Import numpy module
import numpy as np
# creating a vector
v = np.array([1, 2, 3, 4, 5])
print(v)
[1 2 3 4 5]
# Vector Operations
# Import numpy module
import numpy as np
# Create two vector
a = np.array([1, 2, 3, 4, 5])
b = np.array([1, 2, 3, 4, 5])
# adding two vectors
add = a + b
print("Addition:",add)
# Vector Subtraction
sub = a - b
print("Subtraction:",sub)
# Vector Multiplication
mul = a * b
print("Multiplication:",mul)
# Vector Division
div = a / b
print("division",div)
Addition: [ 2 4 6 8 10]
Subtraction: [0 0 0 0 0]
Multiplication: [ 1 4 9 16 25]
division [1. 1. 1. 1. 1.]
Matrice e tensore
- Matrici: Una matrice è un array N-dimensionale di numeri che rappresentano trasformazioni. Puoi pensare a una matrice come a una trasformazione come "scala", "ruota", "deforma" e "ribalta". Trasforma punti singoli nello spazio in punti diversi nello spazio.
# Import numpy module
import numpy as np
# create 2*2 matrix
a1=np.array([[1, 2], [3, 4]])
a2=np.array([[1, 2], [3, 4]])
# Dot Product
dot_product = np.dot(a1,a2)
print("Dot Product: \n",dot_product)
# Cross Product
cross_product = np.cross(a1,a2)
print("Cross Product: \n", cross_product)
Dot Product:
[[ 7 10]
[15 22]]
Cross Product:
[0 0]
Il prodotto scalare di due vettori rappresenta la proiezione di un vettore su un altro, mentre il prodotto vettoriale di due vettori consente a un vettore di identificare il piano su cui entrambi i vettori possono giacere.
- Tensore: A volte ti servirà un array con più di due dimensioni e con dimensioni diverse per ciascun array, disposto in una griglia nota come tensore. La dimensione di un tensore è chiamata rango. Scalari e vettori sono anch’essi un tipo di tensore. I tensori di ordine zero sono scalari e i tensori di primo ordine sono vettori.

Determinante
Il determinante è un valore scalare che rappresenta un fattore di una matrice con cui si possono esplorare lunghezza (in 1 dimensione), area (2 dimensioni), volume (3 dimensioni). Se il determinante è 2, il volume (in 3 dimensioni) raddoppia; se è 1, non influisce sul volume (in 3 dimensioni). Se il determinante è 0, non esiste l’inversa, perché zero moltiplicato per qualsiasi cosa dà zero.

Proprietà:
-
Il prodotto di matrici ha come determinante il prodotto dei determinanti delle matrici: det(M1M2)=det(M1) det(M2).
-
Il determinante di una matrice è equivalente al prodotto degli autovalori.
-
Se moltiplichi una matrice per una costante, allora il determinante cambia secondo det(cM)=c^N det(M). N è la dimensione della matrice.
Se la tua matrice rappresenta un giocattolo elastico, allora il determinante di questa matrice indica di quanto hai allungato il giocattolo.
# Import numpy module
import numpy as np
# Create 2*2 matrix
arr=np.array([[1,2],[3,4]])
# Compute Determinant of a matrix
arr_det=np.linalg.det(arr)
# Print the Computed Determinant
print("Determinant:",arr_det)
Determinant: -2.0000000000000004
Autovalori e autovettori
Un autovettore di una matrice quadrata A è un vettore non nullo tale che la moltiplicazione per A ne modifica solo la scala.

L’autovettore è noto anche come vettore caratteristico. È un vettore non nullo che cambia solo per un fattore scalare quando gli si applica una trasformazione lineare.
Gli autovettori sono assi di rotazione della trasformazione lineare. Questi assi sono fissi in direzione e l’autovalore è il fattore di scala con cui la matrice viene ingrandita o ridotta. Gli autovalori sono noti anche come valori caratteristici o radici caratteristiche. In altre parole, puoi dire che gli autovettori sono rette o piani fissi che limitano il comportamento della trasformazione lineare e gli autovalori di una trasformazione lineare sono il fattore di distorsione.
Il determinante indica l’area delle forme che si espandono o si contraggono in una trasformazione lineare. Per questo il prodotto degli autovalori è uguale al determinante.
# Import numpy module
import numpy as np
# Create 2*2 matrix
arr=np.array([[1,2],[3,4]])
# Find eigenvalues and eigenvectors
eigenvalues, eigenvectors = np.linalg.eig(arr)
# print the eigenvalues and eigenvectors
print("Eigen Values: \n",eigenvalues)
print("Eigen Vectors:\n", eigenvectors)
Eigen Values:
[-0.37228132 5.37228132]
Eigen Vectors:
[[-0.82456484 -0.41597356]
[ 0.56576746 -0.90937671]]
Funzione NORM
A volte vuoi misurare la dimensione di un vettore. La funzione norma ti aiuta a misurare la dimensione di un vettore. Assegna una lunghezza strettamente positiva a un vettore in uno spazio vettoriale, eccetto al vettore nullo. Include la norma L^p. Mappa i vettori su valori non negativi. È equivalente alla distanza euclidea per un vettore e una matrice. È uguale al valore singolare più grande.

# import numpy module
import numpy as np
# Create 3*3 Matrix
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
# Compute norm
a_norm = np.linalg.norm(a)
# print the norm of function
print(a_norm)
16.881943016134134
Fattorizzazione di matrici
La fattorizzazione di matrici, nota anche come decomposizione di matrici, viene usata per scomporre una matrice nelle sue parti costitutive. È analoga alla fattorizzazione dei numeri, come scomporre 10 in 2 x 5. Si usa per risolvere sistemi lineari.
Le seguenti tecniche di fattorizzazione di matrici sono disponibili:
- Decomposizione LU per matrici quadrate, scompone una matrice in componenti L e U.
- Decomposizione QR per matrici m x n (non limitata a matrici quadrate) e scompone una matrice in componenti Q e R. Non è limitata alle matrici quadrate come la decomposizione LU.
- Decomposizione di Cholesky usata per risolvere i minimi quadrati nella regressione lineare, nonché in metodi di simulazione e ottimizzazione.
- Decomposizione ai valori singolari spiegata nella sezione successiva.
Decomposizione ai valori singolari
Nella sezione precedente abbiamo visto l’autodecomposizione di una matrice che si scompone in autovettori e autovalori. La decomposizione ai valori singolari è un tipo di fattorizzazione di matrici che si scompone in vettori e valori singolari. Offre varie applicazioni utili nell’elaborazione dei segnali, psicologia, sociologia, clima, scienze dell’atmosfera, statistica e astronomia.

- M è una matrice m×m
- U è una matrice singolare sinistra m×n
- Σ è una matrice diagonale n×n con numeri reali non negativi.
- V è una matrice singolare destra m×n
- V* è una matrice n×m, che è la trasposta di V.
# Import numpy module
import numpy as np
# Create 3*3 matrix
a = np.array([[1, 3, 4], [5, 6, 9], [1, 2, 3], [7, 6, 8]])
# Decomposition of matrix using SVD
U, s, Vh = np.linalg.svd(a, full_matrices=False)
U,s,Vh
(array([[-0.27067357, -0.61678044, 0.69789573],
[-0.65939972, -0.2937857 , -0.62152182],
[-0.20244298, -0.3480035 , -0.06765408],
[-0.67152413, 0.64200111, 0.34939247]]),
array([18.0376394 , 2.34360292, 0.38870323]),
array([[-0.46961711, -0.51018039, -0.72053851],
[ 0.87911451, -0.19502274, -0.43488368],
[-0.08134773, 0.83766467, -0.54009299]]))
# Generate the initial matrix
new_a = np.dot(U, np.dot(np.diag(s), Vh))
# Print original matrix
print(new_a)
[[1. 3. 4.]
[5. 6. 9.]
[1. 2. 3.]
[7. 6. 8.]]
Pseudoinversa di Moore-Penrose
La pseudoinversa di una matrice è una generalizzazione della matrice inversa. Si utilizza per calcolare soluzioni ai minimi quadrati. L’inversa di Moore-Penrose è la forma più diffusa di pseudoinversa di matrice.

# Import numpy module
import numpy as np
# Create 3*3 matrix
a = np.array([[1, 3, 4], [5, 6, 9], [1, 2, 3], [7, 6, 8]])
# Compute the (Moore-Penrose) pseudo-inverse of a matrix.
inv=np.linalg.pinv(a)
# Print pseudo-inverse of a matrix.
print(inv)
[[-0.37037037 0.03703704 -0.11111111 0.18518519]
[ 1.56296296 -1.2962963 -0.11111111 0.71851852]
[-0.84444444 0.94444444 0.16666667 -0.57777778]]
Prodotto di Hadamard
Il prodotto di Hadamard, o prodotto di Schur, è un prodotto elemento per elemento di due matrici originali della stessa dimensione. È noto anche come prodotto elemento per elemento. È più semplice del prodotto tra matrici. Il prodotto di Hadamard è utilizzato negli algoritmi JPEG con perdita. Il prodotto di Hadamard è commutativo, associativo e distributivo. Consente di ottenere facilmente l’inversa e semplifica il calcolo delle potenze di matrici.

Il prodotto di Hadamard è utilizzato in vari campi come la correzione di codice nelle trasmissioni satellitari, teoria dell’informazione, crittografia, riconoscimento di pattern, reti neurali, massima verosimiglianza, compressione JPEG con perdita, analisi statistica multivariata e modellazione lineare.
# Import numpy module
import numpy as np
# Create 2*2 matrix a1 and a2
a1=np.array([[1, 2], [3, 4]])
a2=np.array([[1, 2], [3, 4]])
# Element wise multiplication
hadamard_product = np.multiply(a1,a2)
# Print hadamard distance
print("Hadamard Product: \n", hadamard_product)
Hadamard Product:
[[ 1 4]
[ 9 16]]
Entropia
"L’entropia di una variabile casuale è una funzione che tenta di caratterizzare l’imprevedibilità di una variabile casuale." (Entropy and Mutual Information) È usata per la costruzione di un albero decisionale automatico: a ogni passo di costruzione dell’albero, la selezione delle feature avviene usando criteri basati sull’entropia. La scelta del modello si basa sul principio della massima entropia, secondo cui, tra modelli in conflitto, è migliore quello con entropia più alta.
"Se una variabile casuale X assume valori in un insieme χ={x1, x2,..., xn}, ed è definita da una distribuzione di probabilità P(X), allora scriveremo l’entropia della variabile casuale come" (Entropy and Mutual Information)

"Se il log nell’equazione sopra è in base 2, allora l’entropia è espressa in bit. Se il log è naturale, allora l’entropia è espressa in nat. Più comunemente, l’entropia è espressa in bit." (Entropy and Mutual Information)
# Import scipy and numpy module
import scipy.stats
import numpy as np
# Create an array
a=np.array([1,1,2,3,1,3,4,2,5,6,3,2,4,3])
# Compute probability distribution
a_pdf=scipy.stats.norm.pdf(a)
# Calculate the entropy of a distribution for given probability values.
entropy = scipy.stats.entropy(a_pdf) # get entropy from probability values
print("Entropy: ",entropy)
Entropy: 1.6688066853941022
Divergenza di Kullback-Leibler
La divergenza di Kullback–Leibler è l’entropia relativa di due distribuzioni di probabilità. Misura la distanza (somiglianza o dissimilarità) di una distribuzione da un’altra distribuzione di probabilità di riferimento. Un valore 0 della divergenza di Kullback–Leibler indica che le due distribuzioni sono identiche. Può essere espressa come:

Sembra una misura di distanza, ma non lo è. Questo perché è asimmetrica per natura, il che significa che la metrica non è commutativa. In generale, si può dire che D(p, q) ≠ D(q, p). La divergenza KL è spesso utilizzata nella tecnica di machine learning non supervisionato "Variational Autoencoders".
# Import scipy.stats and numpy module
import scipy.stats
import numpy as np
# Create numpy arrays
a=np.array([1,1,2,3,1,3,4,2,5,6,3,2,4,3])
b=np.array([1,1,3,4,2,4,5,2,5,6,3,2,4,3])
# Compute probability distribution
a_pdf=scipy.stats.norm.pdf(a)
b_pdf=scipy.stats.norm.pdf(b)
# compute relative entropy or KL Divergence
kl_div=scipy.stats.entropy(a_pdf,b_pdf)
print("KL Divergence: ",kl_div)
KL Divergence: 0.26732496641464365
Discesa del gradiente
La discesa del gradiente è uno degli algoritmi più noti usati per ottimizzare coefficienti e bias per regressione lineare, regressione logistica e reti neurali. È un processo iterativo che trova il minimo di una funzione data.


Esistono tre tipi di algoritmo di discesa del gradiente: full batch, stocastico e mini-batch. La discesa del gradiente full batch usa l’intero dataset per calcolare il gradiente, mentre la discesa del gradiente stocastica usa un campione del dataset. La discesa del gradiente mini-batch combina gli approcci stocastico e batch. Il training set è suddiviso in vari gruppi più piccoli chiamati batch. Questi piccoli batch calcolano la loss uno alla volta e fanno la media del risultato finale della loss.
Conclusione
Congratulazioni, sei arrivato alla fine di questo tutorial!
Hai imparato concetti matematici di base per il deep learning come scalare, vettore, matrice, tensore, determinante, autovalori, autovettori, funzione NORM, decomposizione ai valori singolari (SVD), pseudoinversa di Moore-Penrose, prodotto di Hadamard, entropia, divergenza di Kullback-Leibler e discesa del gradiente.
Lungo il percorso, hai anche messo in pratica questi concetti in Python usando NumPy e SciPy.
Se vuoi imparare di più su Python, segui questi corsi DataCamp:

