CS🏅/해체 분석기📝

[궁금한건 못참아]라이브러리 vs. 직접구현

Dobby98 2023. 11. 16. 19:01

피어슨 상관계수와 코사인 유사도에 글을 정리하던 중

이미 해당 공식들은 numpy나 scipy에 존재했다 

 

피어슨 상관계수?

 

[머신러닝+선형대수] 상관관계와 코사인 유사도

통계와 머신러닝에서 가장 근본적이면서 중요한 분석 방법은 상관관계를 분석하는 것이다 상관관계를 살펴보기 위해서는 상관계수를 구하는 방법이 있다 상관계수는 -1 부터 +1까지의 범위로

eumgill98.tistory.com

 

이때 갑자기 의문점이 하나 생겼다

 

과연 직접 구현함 함수와 라이브러리에 있는 함수의 속도 차이가 존재할까?

 

오늘은 이런 의문점을 해결하기 위해서 간단한 실험을 진행해 보았다

 

실험 설계 

오늘 비교할 공식 재료 : `피어슨 상관계수`

 

비교할 대상

(1) 직접 구현한 피어슨 상관계수 함수

(2) Numpy의 np.corrcoef

(3) Scipy의 stats.pearsonr

 

이때 직접 구현한 상관계수 함수는 아래와 같다

def corrcoef(v1, v2):
  v1m = v1 - np.mean(v1)
  v2m = v2 - np.mean(v2)

  return np.dot(v1m, v2m) / (np.linalg.norm(v1m) * np.linalg.norm(v2m))

 

 

비교 방법

(1) 해당 함수들의 인자로 들어가는 vector의 size를 순차적으로 늘려가면서 비교해본다.

 

(2) 해당 함수들의 인자로 들아가는 vector의 value에 따른 차이도 비교한다.

 

(3) 해당 함수들을 각각 연속적으로 수행하는 횟수를 늘려 비교한다.

 

실험결과

 

조건

- 10^10 범위의 랜덤 값을 같는 배열의 길이를 순차적으로 증가시키며 비교

 

3000까지의 길이를 증가했을때 sicpy가 압도적으로 느렸음

numpy와 직접 구현을 비교했을 때는 아래와 같음

큰 차이는 없었지만 미세하게 직접구현이 빨랐음

 

| 알수 있는 것 |

- Scipy의 경우 `피어슨 상관계수` 뿐만 아니라 `p-value`까지 계산을 하기 때문에 연산에서 조금더 오래걸리는 것 같다

- 직접구현이 numpy 보다 빨라보임

 

| 코드 |

# 실험 1
def ex1(max_v, iter):
  """
  max_v : range of values
  iter : max_num of vector
  """
  result = defaultdict()

  for num in tqdm(range(2,iter+1)):
    v1 = np.random.randint(max_v, size=num)
    v2 = np.random.randint(max_v, size=num)
    
    #case1
    tic = time.time()
    corrcoef(v1, v2)
    t1 = time.time() - tic

    #case2
    tic = time.time()
    np.corrcoef(v1, v2)
    t2 = time.time() - tic

    #case3
    tic = time.time()
    pearsonr(v1, v2)
    t3 = time.time() - tic
	
    #값 저장
    result[num] = [t1, t2, t3]
  
  return result

 

이때 문제점으로 보이는 지점을 발견했다

중간중간에 time이 높은 이상치들이 존재 -> 이는 배열의 value의 크기에도 영향을 받아서가 아닐까하는 생각이 들었다

따라서 배열의 value를 동일한 값으로 고정한채 (1로 고정) 배열의 길이만 증가시켜보기로 했다

 

수정된 조건

- 배열의 value를 1로 고정

 

갑자기 scipy의 속도가 가장 빨라졌다??

아무래도 values의 영향도 있는 것 같다

 

직접구현과 numpy 비교했을때 아직도 numpy의 속도가 더 느렸다

 

| 알수 있는 것 |

- 아무래도 value의 영향도 존재한다

- sicpy가 느린건 p-value 때문이 아니라 value의 크기때문?

def ex2(iter):
  """
  iter : max_num of vector
  """
  result = defaultdict()

  for num in tqdm(range(2,iter+1)):
  	#모든 값을 1로 고정
    v1 = np.ones(num)
    v2 = np.ones(num)
    
    #case1
    tic = time.time()
    corrcoef(v1, v2)
    t1 = time.time() - tic

    #case2
    tic = time.time()
    np.corrcoef(v1, v2)
    t2 = time.time() - tic

    #case3
    tic = time.time()
    pearsonr(v1, v2)
    t3 = time.time() - tic

    result[num] = [t1, t2, t3]
  
  return result

 


이번에는 최대 배열의 크기를 3000에서 30만까지 증가 시켜서 진행해 보았다. (참고로 오래걸림)

참고로 values는 1로 고정했다 - 예상 scipy가 가장빠를 듯

 

놀랍게도? scipy가 가장 빨랐다

그리고 진짜 놀란건 직접 구현이 가장 오래 걸렸다는 것 -> numpy가 역전!

 

아무래도 배열의 크기가 증가하면 numpy와 직접구현의 차이가 사라지는듯하다

왜? 

 


이번에는 각 함수별로 연산을 지정횟수 만큼 반복했을 때 걸리는 시간을 비교해 보았다

즉, 지정횟수가 300번이라고 했을때

직접구현 300번 걸리는 시간, numpy 300번 걸리는 시간 ... 비교

 

앞 그래프에서 차이점이 잘 보이지 않아서 이를 중첩시켜 차이를 늘려보기 위해서 이다

 

조건)

-300 길이의 벡터까지 각 함수를 500번씩 반복 수행

-values는 1로 고정

이방법을 통해서 조금더 차이점을 눈으로 볼 수 있었다 

왜냐하면 차이가 존재한다면 반복 수행할 수록 그 차이는 더 벌어질 것이기 때문이다

 

역시 배열의 길이를 짧게 했을때는 직접 구현이 numpy 보다 빨랐다

 


각 함수의 반복을 10배 올려 5000번으로 수정해 보았다

먼가 시계열 데이터 같이 변했다 - 일정한 주기로 증가하고 감소하는 부분은 CPU 클록 차이인가 하는 생각도 해본다


마지막으로 values를 1 고정이 아니라 범위 안에서 랜덤값으로 지정해서 실햄해 보았다

처음에 한방법과 지정반복횟수를 합친 방법으로 수행한 것이다

역시 values의 값을 변경하니 scipy 시간이 가장 오래 거렸다...

 

def ex4(max_v, iter, loops):
  """
  max_v : range of values
  iter : max_num of vector
  loops : 반복횟수 지정
  """
  result = defaultdict()

  for num in tqdm(range(2,iter+1)):
    v1 = np.random.randint(max_v, size=num)
    v2 = np.random.randint(max_v, size=num)
    
    #case1
    tic = time.time()
    for i in range(loops):
      corrcoef(v1, v2)
    t1 = time.time() - tic

    #case2
    tic = time.time()
    for i in range(loops):
      np.corrcoef(v1, v2)
    t2 = time.time() - tic

    #case3
    tic = time.time()
    for i in range(loops):
      pearsonr(v1, v2)
    t3 = time.time() - tic

    result[num] = [t1, t2, t3]
  
  return result

결론

- Scipy는 value의 크기의 영향을 많이 받는다 - 아무래도 p-value 연산 때문인가? 

- 참고로 Scipy는 numpy 기반이다 그래서 더 걸리는 것 일 수도 

- 직접 구현이 numpy보다 빠르다 (in 피어슨 상관계수)

- 하지만 이는 피어슨 상관계수를 구하는 함수에만 국한 된 것이기 때문에 전체 라이브러리의 속도 차이는 아니다

- 일반화 하지 말것...

 

 

+) 입력 벡터 형태를 넘파이 array가 아니라 list일 경우 달라질 수도? (변환과정이 추가되니까)

입력을 리스트로 넣었을때 scipy와의 속도는 더 벌어지고 직접구현과 numpy의 차이는 줄어들었다

벡터의 크기가 작을때는 직접 구현이 더 빨랐지만 벡터의 사이즈가 증가했을 때는 차이점이 사라지는 것 같았다