Как уменьшить использование памяти для алгоритмов глубокого обучения? - PullRequest
0 голосов
/ 25 января 2019

Я написал скрипт DQN для игры в BreakoutDeterministic и запустил его на своем школьном GPU-сервере. Тем не менее, кажется, что код занимает 97% от общего объема оперативной памяти (более 100 ГБ)!

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

Я написал скрипт на PyCharm, python 3.6. Мой ноутбук 12 ГБ оперативной памяти без графического процессора, но школьный сервер использует Ubuntu, графический процессор p100.

import gym
import numpy as np
import random
from collections import deque
from keras.layers import Dense, Input, Lambda, convolutional, core
from keras.models import Model
from keras.optimizers import Adam
import matplotlib.pyplot as plt
import os
import time as dt
plt.switch_backend('agg')

def preprocess(state):
    process_state = np.mean(state, axis=2).astype(np.uint8) 
    process_state = process_state[::2, ::2] 
    process_state_size = list(process_state.shape)
    process_state_size.append(1)
    process_state = np.reshape(process_state, process_state_size)
    return process_state


class DQNAgent:
    def __init__(self, env):
        self.env = env
        self.action_size = env.action_space.n
        self.state_size = self.select_state_size()

        self.memory = deque(maxlen=1000000)  # specify memory size
        self.gamma = 0.99
        self.eps = 1.0
        self.eps_min = 0.01
        self.decay = 0.95
        self.lr = 0.00025
        self.start_life = 5 # get from environment

        self.tau = 0.125  # special since 2 models to be trained


        self.model = self.create_cnnmodel()
        self.target_model = self.create_cnnmodel() 

    def select_state_size(self):
        process_state = preprocess(self.env.reset())
        state_size = process_state.shape
        return state_size

    def create_cnnmodel(self):

        data_input = Input(shape=self.state_size, name='data_input', dtype='int32')
        normalized = Lambda(lambda x: x/255)(data_input)  
        conv1 = convolutional.Convolution2D(32, 8, strides=(4, 4), activation='relu')(normalized)  
        conv2 = convolutional.Convolution2D(64, 4, strides=(2,2), activation='relu')(conv1)
        conv3 = convolutional.Convolution2D(64, 3, strides=(1,1), activation='relu')(conv2)
        conv_flatten = core.Flatten()(conv3)  # flatten to feed cnn to fc
        h4 = Dense(512, activation='relu')(conv_flatten)
        prediction_output = Dense(self.action_size, name='prediction_output', activation='linear')(h4)

        model = Model(inputs=data_input, outputs=prediction_output)
        model.compile(optimizer=Adam(lr=self.lr),
                  loss='mean_squared_error') # 'mean_squared_error') keras.losses.logcosh(y_true, y_pred)

        return model

    def remember(self, state, action, reward, new_state, done): # store past experience as a pre-defined table
        self.memory.append([state, action, reward, new_state, done])

    def replay(self, batch_size):
        if batch_size > len(self.memory):
            return

        all_states = []
        all_targets = []
        samples = random.sample(self.memory, batch_size)  
        for sample in samples:
            state, action, reward, new_state, done = sample
            target = self.target_model.predict(state)  
            if done:
                target[0][action] = reward  
            else:                   
                target[0][action] = reward + self.gamma*np.max(self.target_model.predict(new_state)[0])
            all_states.append(state)
            all_targets.append(target)
        history = self.model.fit(np.vstack(all_states), np.vstack(all_targets), epochs=1, verbose=0) 
        return history

    def act(self, state):
        self.eps *= self.decay  
        self.eps = max(self.eps_min, self.eps)
        if np.random.random() < self.eps:
            return self.env.action_space.sample()  
        return np.argmax(self.model.predict(state)[0])  

    def train_target(self):
        weights = self.model.get_weights()
        target_weights = self.target_model.get_weights()
        for i in range(len(target_weights)):
            target_weights[i] = (1-self.tau)*target_weights[i] + self.tau*weights[i] 
        self.target_model.set_weights(target_weights) #


def main(episodes):

    env = gym.make('BreakoutDeterministic-v4')

    agent = DQNAgent(env, cnn)  
    time = env._max_episode_steps
    batch_size = 32

    save_model = 'y'

    filepath = os.getcwd() 
    date = dt.strftime('%d%m%Y')
    clock = dt.strftime('%H.%M.%S')

    print('++ Training started on {} at {} ++'.format(date, clock))
    start_time = dt.time()

    tot_r = []
    tot_loss = []
    it_r = []
    it_loss = []
    tot_frames = 0

    for e in range(episodes):
        r = []
        loss = []

        state = env.reset()
        state = preprocess(state)
        state = state[None,:]

        current_life = agent.start_life

        for t in range(time):
            if rend_env == 'y':

            action = agent.act(state)
            new_state, reward, terminal_life, life = env.step(action)
            new_state = preprocess(new_state)
            new_state = new_state[None,:]

            if life['ale.lives'] < current_life:
                reward = -1
               current_life = life['ale.lives']

            agent.remember(state, action, reward, new_state, terminal_life)

            hist = agent.replay(batch_size)
            agent.train_target()

            state = new_state

            r.append(reward)
            tot_frames += 1

            if hist is None:
                loss.append(0.0)
            else:
                loss.append(hist.history['loss'][0])

            if t%20 == 0:
                print('Frame : {}, Cum Reward = {}, Avg Loss = {}, Curr Life: {}'.format(t,
                                                                                       np.sum(r),
                                                                                       round(np.mean(loss[-20:-1]),3),
                                                                                       current_life))

                agent.model.save('{}/Mod_Fig/DQN_BO_model_{}.h5'.format(filepath, date))
                agent.model.save_weights('{}/Mod_Fig/DQN_BO_weights_{}.h5'.format(filepath, date))

            if current_life == 0 or terminal_life:
                print('Episode {} of {}, Cum Reward = {}, Avg Loss = {}'.format(e, episodes, np.sum(r), np.mean(loss)))
                break

        tot_r.append(np.sum(r))
        tot_loss.append(np.mean(loss))
        it_r.append(r)
        it_loss.append(loss)

    print('Training ended on {} at {}'.format(date, clock))
    run_time = dt.time() - start_time
    print('Total Training time: %d Hrs %d Mins $d s' % (run_time // 3600, (run_time % 3600) // 60),
      (run_time % 3600) % 60 // 1)

    if save_model == 'y':
        agent.model.save('{}/Mod_Fig/DQN_BO_finalmodel_{}_{}.h5'.format(filepath, date, clock))
        agent.model.save_weights('{}/Mod_Fig/DQN_BO_finalweights_{}_{}.h5'.format(filepath, date, clock))

    agent.model.summary()

    return tot_r, tot_loss, it_r, it_loss, tot_frames


if __name__ == '__main__':
    episodes = 3
    total_reward, total_loss, rewards_iter, loss_iter, frames_epi = main(episodes=episodes)

Буду очень признателен за ваши комментарии и помощь в написании памяти и быстродействующих глубоких кодов RL! Я надеюсь обучить свой DQN на прорыве для 5000 эпизодов, но удаленный сервер позволяет проводить максимум 48 часов обучения. Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 30 января 2019

У меня были похожие проблемы с памятью, и я до сих пор это делаю.

Основная причина большого потребления памяти - states.Но вот что я сделал, чтобы сделать его лучше:

Шаг 1 : Изменить размер их до 84 x 84 образца, используя openCV.Некоторые люди вместо уменьшают изображения до 84 x 84.Это приводит к тому, что каждый state имеет форму (84,84,3).

Шаг 2 : преобразование этих кадров в оттенки серого (в основном, черно-белое ).Это должно изменить форму на (84,84,1).

Шаг 3 : используйте dtype=np.uint8 для хранения states.Они потребляют минимальное количество памяти и идеально подходят для значений интенсивности пикселей в диапазоне 0-255.

Дополнительная информация

Я запускаю свой код на free Записные книжки Google Collab (графический процессор K80 Tesla и 13 ГБ ОЗУ), периодически сохраняющие буфер воспроизведения на моем диске.

Для шагов 1 и 2 рассмотрите возможность использования базовых OpenAI Atari wrappers , поскольку нет смысла заново изобретать колесо.

Вы также можете использовать этот фрагмент для проверки объема ОЗУ, используемого вашей собственной программой на каждом шаге, как я делал:

import os
import psutil

def show_RAM_usage(self):
    py = psutil.Process(os.getpid())
    print('RAM usage: {} GB'.format(py.memory_info()[0]/2. ** 30))

Этот фрагмент изменен для использования в моей собственной программе из оригинального ответа

0 голосов
/ 26 января 2019

Похоже, у вас утечка памяти.

Эта строка

agent.remember(state, action, reward, new_state, terminal_life)

вызывается 5000 * env._max_episode_steps раз, и каждый state является массивом (210, 160, 3). Первым делом нужно уменьшить размер self.memory = deque(maxlen=1000000) # specify memory size, чтобы убедиться, что это единственная причина.

Если вы действительно считаете, что вам нужна такая большая емкость, вам следует выгрузить self.memory на диск и оставить в памяти только небольшой подвыбор.

Дополнительно: подвыборка из deque очень медленная, deque реализован в виде связанного списка, поэтому каждая подвыборка имеет значение O (N * M). Вы должны рассмотреть возможность реализации собственного кольцевого буфера для self.memory.

В качестве альтернативы: вы могли бы рассмотреть вероятностный буфер (я не знаю правильного имени), где каждый раз, когда вы добавляете в полный буфер, произвольно удаляете элемент и добавляете новый элемент. Это означает, что любой обнаруженный кортеж (состояние, действие, вознаграждение, ...) имеет ненулевую вероятность того, что он будет содержаться в буфере, причем недавние кортежи более вероятны, чем старые.

...