Nehmen wir an, ich habe ein numpy 1d Array
a = array([1,0,3])
Ich möchte dieses als 2d 1-hot-Array kodieren
b = array([[0,1,0,0], [1,0,0,0], [0,0,0,1]])
Gibt es einen schnellen Weg, dies zu tun? Schneller als nur das Schleifen von a
, um Elemente von b
zu setzen.
Ihr Array a
definiert die Spalten der Nicht-Null-Elemente im Ausgabearray. Sie müssen auch die Zeilen definieren und dann die fantastische Indizierung verwenden:
>>> a = np.array([1, 0, 3])
>>> b = np.zeros((3, 4))
>>> b[np.arange(3), a] = 1
>>> b
array([[ 0., 1., 0., 0.],
[ 1., 0., 0., 0.],
[ 0., 0., 0., 1.]])
>>> values = [1, 0, 3]
>>> n_values = np.max(values) + 1
>>> np.eye(n_values)[values]
array([[ 0., 1., 0., 0.],
[ 1., 0., 0., 0.],
[ 0., 0., 0., 1.]])
Sie können sklearn.preprocessing.LabelBinarizer
verwenden:
Beispiel:
import sklearn.preprocessing
a = [1,0,3]
label_binarizer = sklearn.preprocessing.LabelBinarizer()
label_binarizer.fit(range(max(a)+1))
b = label_binarizer.transform(a)
print('{0}'.format(b))
ausgabe:
[[0 1 0 0]
[1 0 0 0]
[0 0 0 1]]
Sie können ua sklearn.preprocessing.LabelBinarizer()
so initialisieren, dass die Ausgabe von transform
spärlich ist.
Folgendes finde ich nützlich:
def one_hot(a, num_classes):
return np.squeeze(np.eye(num_classes)[a.reshape(-1)])
Hier steht num_classes
für die Anzahl der Klassen, die Sie haben. Wenn Sie also einen a
-Vektor mit der Form (10000,) haben, transformiert diese Funktion ihn in (10000, C). Beachten Sie, dass a
nullindiziert ist, d. H. one_hot(np.array([0, 1]), 2)
ergibt [[1, 0], [0, 1]]
.
Genau das, was Sie haben wollten, glaube ich.
PS: Die Quelle ist Sequenzmodelle - deeplearning.ai
Falls Sie Keras verwenden, gibt es dafür ein eingebautes Hilfsprogramm:
from keras.utils.np_utils import to_categorical
categorical_labels = to_categorical(int_labels, num_classes=3)
Und es ist so ziemlich das Gleiche wie @ YXDs Antwort (siehe Quellcode ).
numpy.eye (Größe der Klasse) [zu konvertierender Vektor]
Hier ist eine Funktion, die einen 1-D-Vektor in ein 2D-Hot-Array konvertiert.
#!/usr/bin/env python
import numpy as np
def convertToOneHot(vector, num_classes=None):
"""
Converts an input 1-D vector of integers into an output
2-D array of one-hot vectors, where an i'th input value
of j will set a '1' in the i'th row, j'th column of the
output array.
Example:
v = np.array((1, 0, 4))
one_hot_v = convertToOneHot(v)
print one_hot_v
[[0 1 0 0 0]
[1 0 0 0 0]
[0 0 0 0 1]]
"""
assert isinstance(vector, np.ndarray)
assert len(vector) > 0
if num_classes is None:
num_classes = np.max(vector)+1
else:
assert num_classes > 0
assert num_classes >= np.max(vector)
result = np.zeros(shape=(len(vector), num_classes))
result[np.arange(len(vector)), vector] = 1
return result.astype(int)
Nachfolgend einige Verwendungsbeispiele:
>>> a = np.array([1, 0, 3])
>>> convertToOneHot(a)
array([[0, 1, 0, 0],
[1, 0, 0, 0],
[0, 0, 0, 1]])
>>> convertToOneHot(a, num_classes=10)
array([[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]])
Ich denke, die kurze Antwort ist nein. Für einen allgemeineren Fall in n
-Dimensionen habe ich Folgendes gefunden:
# For 2-dimensional data, 4 values
a = np.array([[0, 1, 2], [3, 2, 1]])
z = np.zeros(list(a.shape) + [4])
z[list(np.indices(z.shape[:-1])) + [a]] = 1
Ich frage mich, ob es eine bessere Lösung gibt - ich mag es nicht, dass ich diese Listen in den letzten beiden Zeilen erstellen muss. Wie auch immer, ich habe einige Messungen mit timeit
durchgeführt und es scheint, dass die numpy
-basiert (indices
/arange
) und die iterativen Versionen ungefähr das gleiche bewirken.
Sie können den folgenden Code zum Konvertieren in einen One-Hot-Vektor verwenden:
let x ist der normale Klassenvektor mit einer einzelnen Spalte mit Klassen 0 bis zu einer bestimmten Zahl:
import numpy as np
np.eye(x.max()+1)[x]
wenn 0 keine Klasse ist; dann entferne +1.
Ich bin kürzlich auf ein Problem derselben Art gestoßen und habe diese Lösung gefunden, die sich nur dann als zufriedenstellend erwies, wenn Sie Zahlen haben, die in eine bestimmte Formation passen. Zum Beispiel, wenn Sie die folgende Liste mit einem Hot-Code kodieren möchten:
all_good_list = [0,1,2,3,4]
los, die veröffentlichten Lösungen sind bereits oben erwähnt. Was aber, wenn man diese Daten betrachtet:
problematic_list = [0,23,12,89,10]
Wenn Sie dies mit den oben genannten Methoden tun, werden Sie wahrscheinlich 90 One-Hot-Spalten erhalten. Dies liegt daran, dass alle Antworten so etwas wie n = np.max(a)+1
enthalten. Ich habe eine allgemeinere Lösung gefunden, die für mich funktioniert hat und mit Ihnen teilen wollte:
import numpy as np
import sklearn
sklb = sklearn.preprocessing.LabelBinarizer()
a = np.asarray([1,2,44,3,2])
n = np.unique(a)
sklb.fit(n)
b = sklb.transform(a)
Ich hoffe, dass bei den oben genannten Lösungen die gleichen Einschränkungen aufgetreten sind, und dies könnte hilfreich sein
Verwenden Sie den folgenden Code. Es funktioniert am besten.
def one_hot_encode(x):
"""
argument
- x: a list of labels
return
- one hot encoding matrix (number of labels, number of class)
"""
encoded = np.zeros((len(x), 10))
for idx, val in enumerate(x):
encoded[idx][val] = 1
return encoded
Fand es hier P.S Sie müssen nicht in den Link gehen.
saubere und einfache Lösung:
max_elements_i = np.expand_dims(np.argmax(p, axis=1), axis=1)
one_hot = np.zeros(p.shape)
np.put_along_axis(one_hot, max_elements_i, 1, axis=1)
Um nur auf die excellent answer from K3 --- rnc einzugehen, hier eine allgemeinere Version:
def onehottify(x, n=None, dtype=float):
"""1-hot encode x with the max value n (computed from data if n is None)."""
x = np.asarray(x)
n = np.max(x) + 1 if n is None else n
return np.eye(n, dtype=dtype)[x]
Hier ist auch ein schneller und schmutziger Benchmark dieser Methode und eine Methode aus der aktuell akzeptierten Antwort by YXD (etwas geändert, so dass sie dieselbe API anbieten, außer dass die letztere funktioniert nur bei 1D ndarrays):
def onehottify_only_1d(x, n=None, dtype=float):
x = np.asarray(x)
n = np.max(x) + 1 if n is None else n
b = np.zeros((len(x), n), dtype=dtype)
b[np.arange(len(x)), x] = 1
return b
Die letztere Methode ist ~ 35% schneller (MacBook Pro 13 2015), aber die erstere ist allgemeiner:
>>> import numpy as np
>>> np.random.seed(42)
>>> a = np.random.randint(0, 9, size=(10_000,))
>>> a
array([6, 3, 7, ..., 5, 8, 6])
>>> %timeit onehottify(a, 10)
188 µs ± 5.03 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit onehottify_only_1d(a, 10)
139 µs ± 2.78 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Diese Art der Codierung ist normalerweise Teil eines numpy-Arrays. Wenn Sie ein numpy-Array wie folgt verwenden:
a = np.array([1,0,3])
dann gibt es eine sehr einfache Möglichkeit, diese in 1-hot-Kodierung umzuwandeln
out = (np.arange(4) == a[:,None]).astype(np.float32)
Das ist es.
Hier ist eine dimensionalitätsunabhängige eigenständige Lösung.
Dies konvertiert jedes N-dimensionale Array arr
von nichtnegativen Ganzzahlen in ein N + 1-dimensionales Array one_hot
, wobei one_hot[i_1,...,i_N,c] = 1
arr[i_1,...,i_N] = c
bedeutet. Sie können die Eingabe über np.argmax(one_hot, -1)
wiederherstellen.
def expand_integer_grid(arr, n_classes):
"""
:param arr: N dim array of size i_1, ..., i_N
:param n_classes: C
:returns: one-hot N+1 dim array of size i_1, ..., i_N, C
:rtype: ndarray
"""
one_hot = np.zeros(arr.shape + (n_classes,))
axes_ranges = [range(arr.shape[i]) for i in range(arr.ndim)]
flat_grids = [_.ravel() for _ in np.meshgrid(*axes_ranges, indexing='ij')]
one_hot[flat_grids + [arr.ravel()]] = 1
assert((one_hot.sum(-1) == 1).all())
assert(np.allclose(np.argmax(one_hot, -1), arr))
return one_hot
Hier ist eine Beispielfunktion, die ich dazu geschrieben habe, basierend auf den obigen Antworten und meinem eigenen Anwendungsfall:
def label_vector_to_one_hot_vector(vector, one_hot_size=10):
"""
Use to convert a column vector to a 'one-hot' matrix
Example:
vector: [[2], [0], [1]]
one_hot_size: 3
returns:
[[ 0., 0., 1.],
[ 1., 0., 0.],
[ 0., 1., 0.]]
Parameters:
vector (np.array): of size (n, 1) to be converted
one_hot_size (int) optional: size of 'one-hot' row vector
Returns:
np.array size (vector.size, one_hot_size): converted to a 'one-hot' matrix
"""
squeezed_vector = np.squeeze(vector, axis=-1)
one_hot = np.zeros((squeezed_vector.size, one_hot_size))
one_hot[np.arange(squeezed_vector.size), squeezed_vector] = 1
return one_hot
label_vector_to_one_hot_vector(vector=[[2], [0], [1]], one_hot_size=3)
Ich füge zur Vervollständigung eine einfache Funktion hinzu, die nur numpy-Operatoren verwendet:
def probs_to_onehot(output_probabilities):
argmax_indices_array = np.argmax(output_probabilities, axis=1)
onehot_output_array = np.eye(np.unique(argmax_indices_array).shape[0])[argmax_indices_array.reshape(-1)]
return onehot_output_array
Als Eingabe wird eine Wahrscheinlichkeitsmatrix verwendet: z.
[[0.03038822 0,65810204 0,16549407 0,3797123] ... [0,02771272 0,2760752 0,3280924 0,33458805]]
Und es wird wiederkommen
[[0 1 0 0] ... [0 0 0 1]]