본문 바로가기
R, Python

[Python, AI] 효율적인 리스트 연산

by 방구석 몽상가 2022. 1. 19.

파이썬에서 리스트는 내부적으로 많은 연산이 정의된 데이터 구조 클래스이다.

딥러닝을 하다보면, 특히 자연어 처리에서 데이터셋 클래스를 구현할 때 리스트 연산을 많이 사용하게 된다. 사전에 초기화 함수에서 모든 리스트를 만들어두는 경우가 많지만 데이터가 엄청 큰 경우 메모리에 전부 적재하기가 쉽지 않다. 또한, RoBERTa와 비슷하게 학습 시에 랜덤하게 데이터 객체를 조작하는 경우, 학습 시에 리스트 연산을 하게 될 수 있다. 그렇지만 대규모 학습은 많은 학습 횟수로 이어지므로 하나의 batch를 얻는데 시간을 줄이는 것은 필수적이다.

동일한 결과를 내놓는 다음 실험의 5가지 변형을 통해 간단하게 조사해보았다.

실험 내용) 고정된 길이의 리스트가 있을 때, 어떤 가변 길이의 리스트의 길이만큼은 1로 채우고 나머지는 0으로 채운다.

import time, random

max_seq = 300
n_iter = 1000000
zero_mask = [0]*max_seq
one_mask = [1]*max_seq

start = time.time()
for _ in range(n_iter):
    k = [2]*int(random.random()*max_seq)
    c = one_mask[:len(k)] + zero_mask[len(k):]
print('test 1:', time.time()-start)

첫 번째 실험은 각각 0과 1로만 이루어진 고정된 길이의 리스트를 미리 생성해놓고 나중에 각각 슬라이싱한 뒤 더하기 연산으로 생성해낸다.

start = time.time()
for _ in range(n_iter):
    c = [0]*max_seq
    k = [2]*int(random.random()*max_seq)
    c[:len(k)] = [1]*len(k)
print('test 2:', time.time()-start)

두 번째 실험은 매번 반복 연산으로 0으로 이루어진 고정된 길이의 리스트를 생성하고 해당 리스트의 앞부분을 1로 바꿔버린다.

start = time.time()
for _ in range(n_iter):
    k = [2]*int(random.random()*max_seq)
    c = [1]*len(k) + [0]*(max_seq-len(k))
print('test 3:', time.time()-start)

세 번째 실험은 매번 반복 연산으로 두 리스트를 만든 다음 더하기 연산으로 합쳐버리는 방식이다.

start = time.time()
for _ in range(n_iter):
    k = [2]*int(random.random()*max_seq)
    l = len(k)
    c = [1]*l + [0]*(max_seq-l)
print('test 4:', time.time()-start)

네 번째 실험은 세 번째 실험에서 길이를 미리 계산해 길이 계산을 한 번 줄인다.

start = time.time()
for _ in range(n_iter):
    k = [2]*int(random.random()*max_seq)
    l = len(k)
    c = [1]*l
    c.extend([0]*(max_seq-l))
print('test 5:', time.time()-start)

마지막 실험은 네 번째 실험에서 더하기 연산 대신 리스트 내장 함수 extend를 사용한다.

실험 결과

1번 실험과 3번 실험 결과를 봤을 때, 미리 만들어놓고 슬라이싱 연산을 사용하는 것보다는 반복 연산으로 만드는 것이 더 빨랐다. 2번 실험과 3번 실험을 보면 슬라이싱을 한 번 정도 사용하더라도 리스트를 하나만 만드는 것이 더 빨랐다. 3번 실험과 4번 실험의 속도가 거의 차이가 없긴 하지만 전부 4번 실험이 근소한 차로 빠른 것으로 보아 len 연산을 줄이는 방향이 속도 향상에 도움이 된다는 것을 알 수 있다. 마지막으로 5번 실험에서 더하기 연산을 extend 함수로 변경만 했음에도 굉장히 유의미한 속도 차를 얻을 수 있었다.

결론
1) 슬라이싱 연산 속도는 빠르지 않다.
2) 리스트를 합칠 때, + 연산 대신 extend 함수를 사용해라.
3) 리스트 길이를 여러 번 사용해야 한다면 미리 구해놓고 사용해라.

댓글