Возможная утечка памяти при использовании mplfinance / matplotlib. Как это решить?

#python #python-3.x #matplotlib #anaconda #mplfinance

#python #python-3.x #matplotlib #анаконда #mplfinance

Вопрос:

Я пытаюсь сделать много (~ 1,7 млн) изображений (диаграммы подсвечников с объемом) для CNN. Тем не менее, скрипт, который у меня есть в настоящее время, продолжает увеличивать использование памяти после каждой итерации примерно на 2-5 МБ на итерацию, насколько я могу судить. Это увеличивается до тех пор, пока моя память не будет полностью заполнена, независимо от того, сколько экземпляров я запускаю скрипта. (16 ГБ, из которых скрипт в конечном итоге использует 11-12 ГБ).

Цель состоит в том, чтобы запустить несколько экземпляров скрипта одновременно. Я попробовал параллельную обработку, но получилось не очень хорошо. Поэтому я просто использую несколько ядер. Я перепробовал множество методов, чтобы уменьшить использование памяти, но, похоже, ничего не работает.

Я использую ноутбуки Jupyter (Python 3.8.5) (anaconda) в VS code, имею 64-разрядную систему Windows. 16 ГБ оперативной памяти и Intel i7 8-го поколения.

Первая ячейка вызывает пакеты, загружает данные и задает параметры.

 # import required packages 
import matplotlib.dates as mpdates 
import matplotlib.pyplot as plt 
import mplfinance as mpf
import matplotlib as mpl
from PIL import Image
import pandas as pd 
import math as math
import numpy as np
import io   as io
import gc   as gc
import os as os


#set run instance number
run=1

#timeframe
tf = 20

#set_pixels
img_size=56

#colors
col_up = '#00FF00'
col_down = '#FF0000'
col_vol = "#0000FF"

#set directory
direct = "C:/Users/robin/1 - Scriptie/images/"

#loading the data
data1 = pd.read_csv(r'D:1 - SchoolEconometrics2020 - 2021ScriptieExplainable AIScriptsDatatest_data.csv',header=[0, 1] , index_col = 0 )
data1.index=pd.to_datetime(data1.index)

#subsetting the data
total_symbols = math.floor(len(data1.columns.unique(level=0))/6)
symbols1 = data1.columns.unique(level=0)[(run-1)*total_symbols:run*total_symbols]

#set the plot parameters
mc = mpf.make_marketcolors(up = col_up ,down = col_down, edge='inherit', volume= col_vol, wick='inherit')
s  = mpf.make_mpf_style(marketcolors=mc)   

 

Вторая ячейка определяет функцию, используемую для построения графиков:

 # creating candlestick chart with volume
def plot_candle(i,j,data,symbols,s,mc,direct,img_size, tf):
     
    #slicing data into 30 trading day windows
    data_temp=data[symbols[j]][i-tf:i]  

    #creating and saving the candlestick charts
    buf = io.BytesIO()
    save = dict(fname= buf, rc = (["boxplot.whiskerprops.linewidth",10]), 
                    pad_inches=0,bbox_inches='tight')
    mpf.plot(data_temp,savefig=save, type='candle',style=s, volume=True, axisoff=True,figratio=(1,1),closefig=True)
    buf.seek(0)
    im = Image.open(buf).resize((img_size,img_size))
    im.save(direct "/" str(symbols[j]) "/" str(i-tf 1) ".png", "PNG")
    buf.close()
    plt.close("all")
 

Третья ячейка перебирает данные и вызывает функции во 2-й ячейке.

 #check if images folder excists, if not, create it. 
if not os.path.exists(direct):
    os.mkdir("C:/Users/robin/1 - Scriptie/images")

for j in range(0,len(symbols1)):

    #Check if symbol folder excists, if not, create it 
    if not os.path.exists(direct "/" symbols1[j]):
             os.mkdir(direct   "/" symbols1[j])

    for i in range(tf,len(data1)) :

        #check if the file has already been created
        if not os.path.exists(direct "/" str(symbols1[j]) "/"    str(i-tf 1) ".png"):
            #call the functions and create the 
            plot_candle(i , j , data1 , symbols1 ,s ,mc ,direct , img_size, tf)
            gc.collect()
 

Комментарии:

1. Можете ли вы попробовать сделать matplotilb.use('agg') , прежде чем создавать первую фигуру? Я предполагаю, что в памяти висит целая куча объектов, связанных с графическим интерфейсом, ожидающих запуска цикла событий, чтобы позволить им самим быть снесенными.

2. @tacaswell , работает как шарм! Спасибо, использование памяти стабильно составляет около ~ 330 МБ. Вы только что сохранили мою диссертацию ;). Быстрый вопрос, что matplotlib.use('agg') на самом деле делает? Однако я просмотрел документацию, она довольно ограничена.

Ответ №1:

Продвижение из комментария:

Проблема заключается в том, что по умолчанию Matplotlib пытается использовать серверную часть на основе графического интерфейса (он создает окно графического интерфейса для каждого графика). Когда вы закрываете их, мы разрушаем нашу сторону вещей и говорим GUI, чтобы разрушить его (основанную на c ) сторону вещей. Однако это разрушение происходит в цикле событий GUI, который в этом случае никогда не запускается, поэтому объекты на стороне c накапливаются в состоянии «вот-вот будут удалены», пока не закончится память.

Установив для серверной 'agg' части значение, мы вообще не пытаемся создавать какие-либо окна с графическим интерфейсом, чтобы не было объектов GUI для удаления (лучшая оптимизация — не делать этого ;)). Я бы ожидал, что это также будет немного быстрее по времени (потому что, опять же, не выполняйте работу, которую вам не нужно делать!).

Видишь https://matplotlib.org/tutorials/introductory/usage.html#backends для получения более подробной информации о бэкэндах см. https://matplotlib.org/users/interactive.html и ссылки там на то, как работает интеграция с графическим интерфейсом.