프로젝트/리뷰집

python 메모리와의 전쟁 - pandas의 거대함, pickle의 압축력

발전생 2021. 2. 15. 00:13

heroku 512MB 무료 서버에 만들어놓은 리뷰 분석 프로젝트를 올리기 위해 끙끙대고 있다. 거의 일주일 동안 배포에만 매진하고 있고 여러 가지 경우의 수들을 테스트 중이다.

매우 많은 경우의 수를 테스트해본 뒤에도 메모리에 개선이 없자 더는 경우의 수를 생각해낼 수 없었다. 그래서 이건 뭔가 고질적인 문제가 있다는 생각 하에 celery task가 돌아가는 중에 사용되는 메모리를 알아야겠다는 생각이 들었다.

 

진작 이걸 찾아봤어야 했다.

아래 코드만으로도 변수가 사용 중인 메모리(memory)를 확인할 수 있다.

import sys
sys.getsizeof(변수명)

 

사용 중인 변수들의 메모리를 확인해 보니 예상치도 못한 결과가 나왔다. 가장 큰 메모리를 차지하고 있던 건 리스트도 아니고 pandas가 csv 파일을 읽어 저장한 변수였다. data(csv) size는 보다시피 19289B를 차지하고 있다. 가장 크기는 하지만 솔직히 이게 몇백 MB까지는 되지 않으니 근본적인 문제는 아니었을 것이다. 단지 pandas가 생각보다 메모리 차지를 많이 한단 걸 깨달았다는 점에 만족한다.

 

 


[추가] 이전에 사용한 sys.getsizeof()는 부정확하다!

 

sys.getsizeof()는 오직 객체의 구조 크기만 반환한다고 한다. 따라서 정확한 수치가 아니다. 

그래서 찾아본 결과 pympler 패키지가 나름 정확하게 변수가 차지하고 있는 메모리 사이즈를 측정해준다고 한다.

아래 코드를 사용하면 된다.

from pympler import asizeof
asizeof.asizeof(변수명)

 

==결과==

[2021-02-14 23:54:36,791: WARNING/MainProcess] data(csv) size: 50384B
[2021-02-14 23:54:36,852: WARNING/MainProcess] reviews size: 784B
[2021-02-14 23:54:36,890: WARNING/MainProcess] loading model and tokenizer
[2021-02-14 23:54:37,705: WARNING/MainProcess] model size: 143416B
[2021-02-14 23:54:39,100: WARNING/MainProcess] tokenizer size: 48394128B
[2021-02-14 23:54:39,100: WARNING/MainProcess] spliting reviews with sentences
[2021-02-14 23:54:53,949: WARNING/MainProcess] pos_sents size: 17872B
[2021-02-14 23:54:53,950: WARNING/MainProcess] neg_sents size: 4848B
[2021-02-14 23:54:53,951: WARNING/MainProcess] okt size: 3776B
[2021-02-14 23:54:53,951: WARNING/MainProcess] getting tokenized sentences
[2021-02-14 23:55:01,082: WARNING/MainProcess] sent_tokeznied size: 40904B
[2021-02-14 23:55:01,084: WARNING/MainProcess] komoran size: 9384B
[2021-02-14 23:55:04,174: WARNING/MainProcess] sent_tokeznied size: 10168B
[2021-02-14 23:55:04,175: WARNING/MainProcess] komoran size: 9384B
[2021-02-14 23:55:04,176: WARNING/MainProcess] matching keywords with sentences
[2021-02-14 23:55:04,788: WARNING/MainProcess] tokenizer size: 50928B
[2021-02-14 23:55:04,790: WARNING/MainProcess] top_keywords size: 1184B
[2021-02-14 23:55:04,791: WARNING/MainProcess] token_existance_mat size: 16088B
[2021-02-14 23:55:04,812: WARNING/MainProcess] tokenizer size: 24136B
[2021-02-14 23:55:04,812: WARNING/MainProcess] top_keywords size: 264B
[2021-02-14 23:55:04,813: WARNING/MainProcess] token_existance_mat size: 4160B
[2021-02-14 23:55:04,814: WARNING/MainProcess] reviewzip size: 2336B

 

가장 큰 녀석은 맨 처음 호출된 tokenizer이다. 이 tokenizer는 RNN 모델을 학습시킨 뒤 모델을 저장하면서 같이 저장한 토크나이저이다. 구글링 결과 pickle을 사용해서 tokenizer를 저장할 수 있대서 그냥 따랐었다.

그런데 이렇게 큰 게 말이 되나? 진짜 전혀 예상치도 못했던 결과이다.

 

폴더에 저장된 tokenizer.pickle을 보면 4.09MB밖에 안 된다. pickle 확장자가 zip 확장자 급으로 압축을 잘해주고 있었나 보다. 메모리에 다시 로드시켜놨더니 48MB가 돼버렸다. 

 

heroku에서 실행해보면 komoran을 사용해서 문장들을 쪼개서 단어들의 리스트로 저장할 때 메모리 초과라며 뻗어 버린다. 그래서 당연히 okt나 komoran이 상당히 메모리 차지를 한다고 지레짐작을 했었다. 솔직히 아직도 왜 komoran을 쓸 때에서야 뻗는지는 잘 모르겠다. 보여주는 에러 메시지에 의하면 이전 함수보다 거의 300MB는 더 쓴다고 파악된다. 아니면 함수 종료 후에도 메모리 방출을 안 해주고 누적되어서 해당 함수에서 300MB를 쓰는 걸 수도 있겠다. 그런데 이럴 가능성은 희박하다고 본다.

그저 konlpy가 메모리를 많이 차지한다는 추측까지만 가능하다.

 

tokenizer를 로드하는 건 루트 함수의 거의 맨 처음에 시작되기 때문에 tokenizer 때문이었으면 루트 함수 호출 후 얼마 안 있어서 바로 뻗었어야 한다. 이 문제에 대해서는 깊이 고민을 해봐야겠다. 전체 변수의 메모리를 다 합해도 50MB 정도밖에 안된다. 역시 근본 원인은 konlpy로 보인다.