Questo documento descrive la semantica di trasmissione di XLA.
Che cos'è la trasmissione?
La trasmissione è il processo di creazione di array con forme diverse che abbiano forme compatibili per le operazioni aritmetiche. La terminologia è presa in prestito dalla trasmissione NumPy.
La trasmissione potrebbe essere necessaria per operazioni tra array multidimensionali di ranghi diversi o tra array multidimensionali con forme diverse ma compatibili. Considera l'addizione X+v in cui X è una matrice (un array
con 2 dimensioni) e v è un vettore (un array con 1 dimensione). Per eseguire
l'addizione elemento per elemento, XLA deve "trasmettere" il vettore v allo stesso
numero di dimensioni della matrice X, replicando v un certo numero di
volte. La lunghezza del vettore deve corrispondere ad almeno una delle dimensioni della
matrice.
Ad esempio:
|1 2 3| + |7 8 9|
|4 5 6|
Le dimensioni della matrice sono (2,3) e quelle del vettore sono (3). Il vettore viene trasmesso replicandolo sulle righe per ottenere:
|1 2 3| + |7 8 9| = |8 10 12|
|4 5 6| |7 8 9| |11 13 15|
In NumPy, questa operazione è chiamata trasmissione.
Principi
Il linguaggio XLA è il più rigoroso ed esplicito possibile, evitando funzionalità "magiche" implicite. Queste funzionalità potrebbero rendere alcune computazioni leggermente più semplici da definire, ma a costo di un maggior numero di ipotesi incorporate nel codice utente che saranno difficili da modificare nel lungo periodo. Se necessario, è possibile aggiungere funzionalità magiche implicite nei wrapper a livello di cliente.
Per quanto riguarda la trasmissione, XLA richiede specifiche di trasmissione esplicite per le operazioni tra array di ranghi diversi. Questo è diverso da NumPy, che deduce la specifica quando possibile.
Trasmissione di un array di dimensioni inferiori su un array di dimensioni superiori
Gli scalari possono sempre essere trasmessi tramite array senza una specifica esplicita delle dimensioni di trasmissione. Un'operazione binaria elemento per elemento tra uno scalare e una matrice significa applicare l'operazione con lo scalare a ogni elemento della matrice. Ad esempio, aggiungere uno scalare a una matrice significa produrre una matrice in cui ogni elemento è la somma dello scalare e dell'elemento corrispondente della matrice di input.
|1 2 3| + 7 = |8 9 10|
|4 5 6| |11 12 13|
La maggior parte delle esigenze di trasmissione può essere acquisita utilizzando una tupla di dimensioni in un'operazione binaria. Quando gli input dell'operazione hanno ranghi diversi, questa tupla di trasmissione specifica quali dimensioni dell'array con dimensioni maggiori devono corrispondere all'array con dimensioni minori.
Considera l'esempio precedente. Anziché aggiungere uno scalare a una matrice (2,3), aggiungi un vettore di dimensione (3) a una matrice di dimensioni (2,3). Senza specificare la trasmissione, questa operazione non è valida. Per richiedere correttamente l'addizione matrice-vettore, specifica che la dimensione di trasmissione sia (1), il che significa che la dimensione del vettore corrisponde alla dimensione 1 della matrice. In 2D, se la dimensione 0 rappresenta le righe e la dimensione 1 le colonne, ogni elemento del vettore diventa una colonna di dimensioni corrispondenti al numero di righe della matrice:
|7 8 9| ==> |7 8 9|
|7 8 9|
Come esempio più complesso, considera di aggiungere un vettore a tre elementi (dimensione (3)) a una matrice 3x3 (dimensioni (3,3)). Esistono due modi per eseguire la trasmissione per questo esempio:
(1) È possibile utilizzare una dimensione di trasmissione pari a 1. Ogni elemento vettoriale diventa una colonna e il vettore viene duplicato per ogni riga della matrice.
|7 8 9| ==> |7 8 9|
|7 8 9|
|7 8 9|
(2) È possibile utilizzare una dimensione di trasmissione pari a 0. Ogni elemento vettoriale diventa una riga e il vettore viene duplicato per ogni colonna della matrice.
|7| ==> |7 7 7|
|8| |8 8 8|
|9| |9 9 9|
Le dimensioni di trasmissione possono essere una tupla che descrive come una forma di dimensioni inferiori viene trasmessa in una forma di dimensioni maggiori. Ad esempio, dato un cuboide 2x3x4 e una matrice 3x4, una tupla di trasmissione (1,2) significa abbinare la matrice alle dimensioni 1 e 2 del cuboide.
Questo tipo di trasmissione viene utilizzato nelle operazioni binarie in XlaBuilder, se viene fornito l'argomento broadcast_dimensions. Ad esempio, consulta
XlaBuilder::Add.
Nel codice sorgente XLA, questo tipo di trasmissione è talvolta chiamato "InDim"
trasmissione.
Definizione formale
L'attributo di trasmissione consente di abbinare un array di dimensioni inferiori a un array di dimensioni superiori specificando le dimensioni dell'array di dimensioni superiori da abbinare. Ad esempio, per un array con dimensioni MxNxPxQ, un vettore con dimensione T può essere abbinato come segue:
MxNxPxQ
dim 3: T
dim 2: T
dim 1: T
dim 0: T
In ogni caso, T deve essere uguale alla dimensione corrispondente dell'array con dimensioni maggiori. I valori del vettore vengono poi trasmessi dalla dimensione corrispondente a tutte le altre dimensioni.
Per far corrispondere una matrice TxV all'array MxNxPxQ, viene utilizzata una coppia di dimensioni di trasmissione:
MxNxPxQ
dim 2,3: T V
dim 1,2: T V
dim 0,3: T V
etc...
L'ordine delle dimensioni nella tupla di trasmissione deve essere l'ordine in cui le dimensioni dell'array a dimensioni inferiori devono corrispondere alle dimensioni dell'array a dimensioni superiori. Il primo elemento della tupla specifica quale dimensione dell'array di dimensioni superiori deve corrispondere alla dimensione 0 dell'array di dimensioni inferiori. Il secondo elemento della tupla specifica quale dimensione dell'array di dimensioni superiori deve corrispondere alla dimensione 1 dell'array di dimensioni inferiori e così via. L'ordine delle dimensioni di trasmissione deve essere strettamente crescente. Ad esempio, nell'esempio precedente è illegale associare V a N e T a P; è anche illegale associare V sia a P che a N.
Trasmissione di array di dimensioni simili con dimensioni degenerate
Un problema correlato è la trasmissione di due array con lo stesso numero di dimensioni, ma dimensioni diverse. Come per NumPy, ciò è possibile solo quando gli array sono compatibili. Due array sono compatibili quando tutte le loro dimensioni sono compatibili. Due dimensioni sono compatibili se:
- Sono uguali oppure
- Una di queste è 1 (una dimensione "degenere")
Quando vengono rilevati due array compatibili, la forma del risultato ha il massimo dei due input in ogni indice di dimensione.
Esempi:
- (2,1) e (2,3) trasmettono a (2,3).
- (1,2,5) e (7,2,5) trasmettono a (7,2,5).
- (7,2,5) e (7,1,5) vengono trasmessi a (7,2,5).
- (7,2,5) e (7,2,6) sono incompatibili e non possono essere trasmessi.
Si verifica e viene supportato anche un caso speciale in cui ciascuno degli array di input ha una dimensione degenere in un indice diverso. In questo caso, il risultato è un'"operazione esterna": (2,1) e (1,3) vengono trasmessi a (2,3). Per altri esempi, consulta la documentazione di NumPy sul broadcasting.
Composizione della trasmissione
Il broadcasting di un array di dimensioni inferiori a un array di dimensioni superiori e il broadcasting utilizzando dimensioni degenerate possono essere eseguiti nella stessa operazione binaria. Ad esempio, un vettore di dimensione 4 e una matrice di dimensione 1x2 possono essere sommati utilizzando le dimensioni di trasmissione del valore (0):
|1 2 3 4| + [5 6] // [5 6] is a 1x2 matrix, not a vector.
Innanzitutto, il vettore viene trasmesso fino a 2 dimensioni (matrice) utilizzando le dimensioni di trasmissione. Il singolo valore (0) nelle dimensioni di trasmissione indica che la dimensione zero del vettore corrisponde alla dimensione zero della matrice. In questo modo si ottiene una matrice di dimensioni 4xM, dove il valore M viene scelto in modo che corrisponda alle dimensioni della dimensione corrispondente nella matrice 1x2. Pertanto, viene prodotta una matrice 4x2:
|1 1| + [5 6]
|2 2|
|3 3|
|4 4|
Quindi, la "trasmissione di dimensioni degenerate" trasmette la dimensione zero della matrice 1x2 in modo che corrisponda alla dimensione corrispondente del lato destro:
|1 1| + |5 6| |6 7|
|2 2| + |5 6| = |7 8|
|3 3| + |5 6| |8 9|
|4 4| + |5 6| |9 10|
Un esempio più complesso è una matrice di dimensioni 1x2 aggiunta a un array di dimensioni 4x3x1 utilizzando le dimensioni di trasmissione (1, 2). Innanzitutto, la matrice 1x2 viene trasmessa fino a 3 dimensioni utilizzando le dimensioni di trasmissione per produrre un array intermedio Mx1x2 in cui la dimensione M è determinata dalla dimensione dell'operando più grande (l'array 4x3x1) che produce un array intermedio 4x1x2. La M si trova nella dimensione 0 (la dimensione più a sinistra) perché le dimensioni 1 e 2 sono mappate alle dimensioni della matrice 1x2 originale, poiché le dimensioni di trasmissione sono (1, 2). Questo array intermedio può essere aggiunto alla matrice 4x3x1 utilizzando la trasmissione di dimensioni degenerate per produrre un risultato di array 4x3x2.