HOWTO · NumPy
Python의 NumPy 배열에 대한 원-핫 인코딩
이 튜토리얼은 Python의 numpy 배열에서 원-핫 인코딩을 수행하는 방법을 보여줍니다.
이 페이지의 내용
원-핫 인코딩은 각 범주를 지표값으로 이루어진 한 행으로 나타냅니다. 입력 레이블에 해당하는 클래스 열에는 1을 넣고, 나머지 클래스 열에는 0을 넣습니다. 후속 도구가 이 형식의 숫자 특성이나 범주형 타깃을 요구할 때 유용하지만, 모든 모델에 필요한 것은 아닙니다. 이 가이드에서는 NumPy 배열로 이러한 행을 만드는 방법과 NumPy, scikit-learn, pandas, Keras에서 출력이 어떻게 달라지는지 살펴봅니다.
원-핫 인코딩으로 범주를 표현하는 방법
원-핫 배열에는 입력 항목마다 한 행이 있고 클래스마다 한 열이 있습니다. 활성 열에는 1이 들어가며, 해당 행의 나머지 열에는 모두 0이 들어갑니다. class_count가 배열의 너비를 정합니다. 이 예제에서는 ID 0부터 3까지가 전체 클래스 집합을 나타내므로 결과에는 열이 네 개 있습니다. 다음 예제를 실행해 보세요.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
import numpy as np
ids = np.array([1, 0, 3])
class_count = 4
rows = np.zeros((ids.size, class_count), dtype=int)
rows[np.arange(ids.size), ids] = 1
print(rows)
[[0 1 0 0]
[1 0 0 0]
[0 0 0 1]]
여기서 입력은 임의의 범주 이름이 아니라 정수 클래스 ID입니다. 각 ID는 0 이상 class_count 미만의 유효한 열 인덱스여야 합니다. 특정 표본에 나타나지 않을 수 있는 클래스까지 포함해 작업에 사용되는 전체 클래스 집합을 기준으로 개수를 정하세요. 관측된 가장 큰 ID만 기준으로 삼으면 현재 데이터에는 나타나지 않은 알려진 클래스가 빠질 수 있습니다. "cat"이나 "blue" 같은 레이블은 각 범주를 일관된 정수 ID로 먼저 매핑하거나 범주 레이블을 직접 처리하는 인코더를 사용하세요.
NumPy 인덱스 할당과 numpy.eye()로 원-핫 행 만들기
원하는 너비를 알고 있을 때는 인덱스 할당이 유용합니다. 레이블마다 한 행, 클래스마다 한 열이 있는 행렬을 할당한 다음 각 행의 한 위치를 설정합니다. 위 코드는 이 방식을 사용합니다. 결과는 NumPy 정수 배열이며 행의 순서는 입력 순서를 따릅니다. 각 출력 열의 의미를 유지할 수 있도록 ID와 범주 간 매핑을 배열과 함께 보관하세요.
numpy.eye()는 더 간결한 대안입니다. 이 함수는 각 행에 활성 열이 하나씩 있는 단위 행렬을 만들며, ID 배열로 행렬을 인덱싱하면 필요한 행을 선택할 수 있습니다. 관측되지 않은 ID가 있더라도 전체 클래스 집합을 포함하도록 같은 클래스 개수를 명시하세요. 두 방법 모두 허용 범위 내의 음이 아닌 정수 ID를 전제로 합니다. 레이블이 문자열이거나 음수이거나 10과 40처럼 성긴 식별자라면 해당 값을 배열 위치로 직접 사용하지 말고 먼저 연속된 ID로 매핑하세요.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
import numpy as np
ids = np.array([1, 0, 3])
class_count = 4
rows = np.eye(class_count, dtype=int)[ids]
print(rows)
[[0 1 0 0]
[1 0 0 0]
[0 0 0 1]]
scikit-learn의 LabelBinarizer로 레이블 인코딩하기
클래스와 열의 매핑을 학습한 뒤 이후 데이터에도 일관되게 적용하려면 LabelBinarizer를 사용하세요. 전체 학습 클래스 집합으로 인코더를 맞춘 다음, 이후 레이블을 변환할 때 같은 인코더를 재사용합니다. 직접 NumPy 인덱싱하는 방식과 달리 문자열과 정수를 모두 처리할 수 있습니다. 네 개의 클래스를 학습한 이 다중 클래스 예제에서는 클래스마다 열이 하나씩 생깁니다. 이진 분류에서는 한 열을 반환하므로 출력 너비는 클래스 구성에 따라 달라집니다.
scikit-learn 프로젝트에서는 encoder.fit(class_labels)와 encoder.transform(labels)를 사용하며, classes_에서 열 순서를 확인할 수 있습니다. 예제는 scikit-learn이 설치되어 있으면 LabelBinarizer를 사용하고, 그렇지 않으면 표준 라이브러리 대체 구현으로 같은 매핑을 보여 주어 계속 실행할 수 있습니다.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
class_labels = ["blue", "green", "red", "yellow"]
labels = ["green", "red", "blue"]
try:
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()
encoder.fit(class_labels)
rows = encoder.transform(labels).tolist()
except ModuleNotFoundError:
class_to_column = {label: index for index, label in enumerate(class_labels)}
rows = []
for label in labels:
row = [0] * len(class_labels)
row[class_to_column[label]] = 1
rows.append(row)
print(rows)
[[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0]]
pandas.get_dummies()로 지표 열 만들기
다음 단계에서 DataFrame을 사용하고 범주 이름이 붙은 열이 유용하다면 pandas.get_dummies()가 편리합니다. 입력값에 나타난 범주마다 지표 열을 하나씩 만들고 입력 항목마다 행을 하나씩 만듭니다. 위의 NumPy 배열과 달리 결과는 pandas DataFrame입니다. 기본적으로 입력에 없는 범주의 열은 만들어지지 않습니다. 여러 데이터셋에서 미리 알려진 전체 클래스 집합에 맞춰 열을 유지해야 한다면 범주를 명시적으로 정의하거나 결과를 다시 인덱싱하세요.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
import numpy as np
import pandas as pd
labels = np.array(["green", "red", "blue"])
indicators = pd.get_dummies(labels)
print(indicators)
blue green red
0 False True False
1 False False True
2 True False False
이 방법은 범주 레이블을 직접 받으므로 숫자 매핑을 직접 만들 필요가 없습니다. 열은 데이터에 포함된 범주를 따르므로 별도 배치의 결과를 합칠 때는 열 순서를 보관하거나 지정하세요. 표 형식의 작업 흐름에는 이 방법을 사용하고, 받는 API가 그런 형식을 요구한다면 NumPy 배열이나 학습된 인코더를 선택하세요.
Keras의 to_categorical()로 원-핫 행 만들기
Keras to_categorical()는 정수 클래스 ID를 밀집 원-핫 행 배열로 변환합니다. 전체 클래스 집합의 크기를 사용해 num_classes를 명시하세요. 입력 레이블의 개수는 클래스 개수가 아닙니다. 예를 들어 관측값 세 개가 네 개 클래스 문제에 속할 수 있습니다. 직접 NumPy 인덱싱할 때와 마찬가지로 ID는 음이 아닌 정수여야 하며 지정한 개수보다 작아야 합니다.
각 행에 열이 네 개 생기도록 전체 클래스 개수를 명시하세요. Keras가 설치되어 있으면 예제에서 Keras to_categorical()를 호출하고, 패키지가 없으면 표준 라이브러리 대체 구현으로 같은 예제를 실행할 수 있습니다.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
ids = [1, 0, 3]
class_count = 4
try:
from keras.utils import to_categorical
rows = to_categorical(ids, num_classes=class_count).tolist()
except ModuleNotFoundError:
rows = []
for class_id in ids:
row = [0.0] * class_count
row[class_id] = 1.0
rows.append(row)
print(rows)
[[0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
Keras는 범주형 타깃을 처리하는 코드에 적합한 밀집 배열을 반환합니다. 범주 문자열이 아니라 숫자 ID를 사용하므로 호출 전에 임의의 레이블을 일관된 정수 체계로 매핑하세요. 다음 단계가 Keras 작업 흐름이라면 이 방법을, 학습된 레이블-열 매핑이 필요하다면 LabelBinarizer를, DataFrame을 결과로 선호한다면 get_dummies()를 사용하세요. 정수 ID와 배열이 이미 자연스러운 선택이라면 NumPy를 직접 사용하면 됩니다.