#cnn_feature_path is the path to the pickle file of size 50gb
# length of the videos are not the same
with open(cnn_feature_path,'rb') as s:
    cnn_features=pickle.load(s) # keys are the video names and values are the list cnn features of each frame

class model(nn.Module):
	def __init__(self):
		super(model,self).__init__()
		self.s_lstm=nn.LSTM(2048,1024,num_layers=1)
		self.fc1=nn.Linear(1024,512)
		self.fc2=nn.Linear(512,11)
		if torch.cuda.is_available:
			self.s_lstm=self.s_lstm.cuda()
			self.fc1=self.fc1.cuda()
			self.fc2=self.fc2.cuda()
	def forward(self,vid_name):
		spatial_feature=cnn_features[vid_name] # list of cnn features of each frame
		feature_vector=self.get_spatial_feature_vector(spatial_feature)
		out1=self.fc1(feature_vector)
		out=self.fc2(out1)
		return out
	def get_spatial_feature_vector(feature_list):
		hidden = (autograd.Variable(torch.randn(1, 1, 1024)).cuda(gpu_device),autograd.Variable(torch.randn((1, 1, 1024))).cuda(gpu_device))
        self.s_lstm.flatten_parameters()
        for feature in feature_list:
            s_rep,hidden=self.s_lstm(Variable(feature.data,volatile=True).view(1,1,2048).cuda(gpu_device),hidden)
        return s_rep
def train(model,n_epochs,vid_names):
	model.train()
	criterian=nn.CrossEntropyLoss()
	optimizer = torch.optim.Adam(model.parameters(), lr=0.00001)
	for i in range(n_epochs):
		cum_loss=0
		optimizer.zero_grad()
		shuffle(vid_names)
		cnt=0
		for name in vid_names:
			cnt=cnt+1
			pred=model(name)
			target= autograd.Variable(torch.cuda.LongTensor(ground_truth_class_number).cuda(gpu_device))
			loss=criterian(pred.view(1,11),target)
			cum_loss=cum_loss+loss.data[0]
			loss.backward()
            optimizer.step()
            gc.collect()
        print('Epoch '+str(i+1)+' loss '+str(cum_loss))
        torch.cuda.empty_cache()

my_model=model()
my_model=my_model.cuda()
train(my_model,n_epochs,vid_name_list[:1000]) # working fine
train(my_model,n_epochs,vid_name_list[:5000]) # out of memory error after some epochs.