학습 내용
python에서의 정수형 데이터 처리 방식을 이해한다
numpy의 정수형 데이터 처리방식을 이해한다 (C언어)
들어가며
p 값의 변화에 따른 norm 값의 변화를 직접 확인해보고자 아래 코드를 실행시키다 보니 19부터 numpy 패키지를 통해 계산한 값과 직접 하드코딩으로 계산한 값의 차이가 나타나기 시작했다. 이에 대한 힌트를 찾기 위해 numpy github를 찾아봤다.
- np.linalg.norm(x, ord=p)의 처리방식을 비교하여 이 문제를 해결할 수 있었다.
위 데이터는 정수형으로 저장되어 있어서 Python에서 정수형 데이터와 실수형 데이터의 처리 방식이 달라서 발생한 문제였다.
# x와 p를 바꾸어가며 norm 값이 어떻게 변하는지 실험해봅시다!
# --------------------------- #
x = np.array([1,10,1,1,1])
x = x.astype('float')
p = 19
# --------------------------- #
norm_x = np.linalg.norm(x, ord=p)
making_norm = (sum(x**p))**(1/p)
print("result of numpy package norm function : %0.5f "%norm_x)
print("result of making norm : %0.5f "%making_norm)
위 처럼 코드를 수정하면 문제없이 같은 값으로 나오는 것을 볼 수 있다.
하지만 위 해결 방식에서 든 의문점이 있었다.
파이썬의 정수는 크기의 제한이 없다.
import sys
t1 = sys.maxsize
t2 = sys.maxsize + 1 #이것도 type int
print(t1)
print(t2)
print(type(t1))
print(type(t2))
- 파이썬은 임의 정밀도 방식을 채택하여 값이 커질 때마다 4비트씩 메모리를 더 할당하여 더해줌으로써 메모리가 허용하는 최대한의 정수를 표현할 수 있게 한다.
위 처럼 2 ** 30을 기준으로 4비트씩 크기가 커지는 것을 확인할 수 있다.
그렇다면 본질적인 문제는 무엇인가?
- 따라서 실제로 값을 계산 해보았고, 이 문제는 numpy 에서 일어나는 문제임을 알게되었다.
위의 개념을 토대로 새로운 해결방안을 하나더 도출 할 수 있게 되었다.
- np.array의 type을 object로 바꾸어도 문제가 해결된다.
결론
위 문제는 NumPy 배열을 사용할 때 발생한 int64
자료형의 오버플로우 문제라고 볼 수 있다.
NumPy 배열에서는 기본적으로 고정 크기 정수형 (int32
, int64
등)을 사용하므로, 이러한 자료형의 범위를 초과하는 큰 수를 연산할 때 오버플로우가 발생할 수 있다.
NumPy의 정수형 오버플로우 문제
NumPy는 내부적으로 C 언어의 배열 구조를 사용하므로, 데이터 타입의 크기가 고정되어 있다.
int64
의 경우, 64비트 정수로 표현 가능한 값의 범위는 다음과 같다:
- 최소값: -2^63 (-9223372036854775808)
- 최대값: 2^63 - 1 (9223372036854775807)
이를 넘는 값을 계산할 때 오버플로우가 발생한다.
문제 해결 방법
1. numpy.float64
사용하기
부동 소수점 타입으로 변환하면, 큰 수를 다룰 때 오버플로우 문제를 피할 수 있다.
하지만 근사값이 사용되기 때문에 일부 정밀도가 손실될 수 있다.
import numpy as np
x = np.array([1, 10, 1, 1, 1], dtype=np.float64)
p = 19
norm_x = np.linalg.norm(x, ord=p)
making_norm = (sum(x**p))**(1/p)
print(f"Result of numpy package norm function: {norm_x:.5f}")
print(f"Result of homemade norm: {making_norm:.5f}")
2. object
데이터 타입 사용하기
Python의 무제한 정수형을 NumPy 배열에서 사용하려면, 데이터 타입을 object
로 설정하면 된다.
이렇게 하면 NumPy는 Python의 무제한 정수를 사용할 수 있게 된다.
import numpy as np
x = np.array([1, 10, 1, 1, 1], dtype=object)
p = 19
# NumPy의 무제한 정수형을 이용한 연산
norm_x = np.linalg.norm(x, ord=p)
making_norm = (sum(x**p))**(1/p)
print(f"Result of numpy package norm function: {norm_x:.5f}")
print(f"Result of homemade norm: {making_norm:.5f}")
이 코드를 사용하면, 무제한 정수형을 사용하여 계산할 수 있으므로 오버플로우 문제가 발생하지 않는다.
요약
- NumPy 배열에서
int64
타입은 고정된 크기를 가지므로, 큰 수의 연산에서 오버플로우 문제가 발생할 수 있다. - 이를 해결하기 위해 부동 소수점 타입 (
np.float64
) 또는object
타입을 사용하여 무제한 정수형 연산을 수행할 수 있다. object
타입을 사용하면 Python의 무제한 정수형을 활용할 수 있어, 큰 수의 계산에서도 정밀도를 유지할 수 있게 된다.
이러한 방법을 통해, NumPy 배열을 사용할 때 큰 수의 연산 문제를 쉽게 해결할 수 있다.
회고
- float로 변환하기만 하는 쉬운 해결방법이였지만 그 속에 담긴 구체적인 의미를 풀어내다 보니 많은 걸 배울 수 있었다
- float의 부동소수점 이야기까지 찾아보게 되었는데 추후 그와 관련한 이야기도 정리해봐야겠다
참고자료
- numpy github : https://github.com/numpy/numpy/blob/d35cd07ea997f033b2d89d349734c61f5de54b0d/numpy/linalg/linalg.py#L2536
'AIFFLE > INFO' 카테고리의 다른 글
데이터 분석 학습 및 프로젝트를 위한 데이터 수집 사이트, 꿀팁 정리 (0) | 2024.06.03 |
---|---|
다양한 평균 측정 방식에 대한 이해 - 산술, 조화, 기하평균 (2) | 2024.05.23 |