提交 f83eb40d authored 作者: Frédéric Bastien's avatar Frédéric Bastien

Merge pull request #2234 from yaoli/cache_opt

Cache opt
...@@ -969,65 +969,64 @@ class FunctionMaker(object): ...@@ -969,65 +969,64 @@ class FunctionMaker(object):
else: else:
raise TypeError("Unknown output type: %s (%s)", type(output), output) raise TypeError("Unknown output type: %s (%s)", type(output), output)
def retrieve_fgraph_from_opt_cache(): def optimize_graph_with_cache(self, optimizer, inputs, outputs):
# This function is not finished # This function is not finished
raise NotImplementedError('optimization cache is not finished! Should not be called.')
from theano.gof.compilelock import get_lock, release_lock from theano.gof.compilelock import get_lock, release_lock
import os.path import os.path
graph_db_file = os.path.join(theano.config.compiledir, 'optimized_graphs.pkl') graph_db_file = os.path.join(theano.config.compiledir, 'optimized_graphs.pkl')
# the inputs, outputs, and size of the graph to be optimized # the inputs, outputs, and size of the graph to be optimized
inputs_new = [inp.variable for inp in inputs] inputs_new = [inp.variable for inp in inputs]
outputs_new = [out.variable for out in outputs] outputs_new = [out.variable for out in outputs]
size_new = len(fgraph.apply_nodes) size_new = len(self.fgraph.apply_nodes)
need_optimize = False need_optimize = False
get_lock() get_lock()
key = None key = None
#Beginning of cache optimizations. #Beginning of cache optimizations.
#Could be refactored in different functions. #Could be refactored in different functions.
if theano.config.cache_optimizations: #set to false by default def load_graph_db():
'''
graph_db and need_optimize
'''
if os.path.isfile(graph_db_file): if os.path.isfile(graph_db_file):
print 'graph_db exists' print 'graph_db already exists'
else: else:
# create graph_db # create graph_db
f = open(graph_db_file, 'wb') f = open(graph_db_file, 'wb')
print 'created new graph_db %s' % graph_db_file print 'create new graph_db in %s' % graph_db_file
#file needs to be open and closed for every pickle #file needs to be open and closed for every pickle
f.close() f.close()
# load the graph_db dictionary # load the graph_db dictionary
try: try:
f = open(graph_db_file, 'rb') f = open(graph_db_file, 'rb')
#Temporary hack to allow theano.scan_module.tests.test_scan.T_Scan #Temporary hack to allow theano.scan_module.tests.test_scan.T_Scan
#to finish. Should be changed in definitive version. #to finish. Should be changed in definitive version.
tmp = theano.config.unpickle_function tmp = theano.config.unpickle_function
theano.config.unpickle_function = False theano.config.unpickle_function = False
graph_db = cPickle.load(f) graph_db = cPickle.load(f)
theano.config.unpickle_function = tmp
#hack end #hack end
f.close() f.close()
print 'graph_db is not empty' print 'graph_db loaded and it is not empty'
except EOFError, e: except EOFError, e:
# the file has nothing in it # the file has nothing in it
print e print e
print 'graph_db is empty' print 'graph_db loaded and it is empty'
graph_db = {} graph_db = {}
finally:
need_optimize = True theano.config.unpickle_function = tmp
print 'loaded graph_db from %s, size=%d' % (graph_db_file, len(graph_db)) return graph_db
# the sole purpose of this loop is to set 'need_optimize'
for i, graph_old in enumerate(graph_db.keys()): def find_same_graph_in_db(graph_db):
# If found_graph_in_db is None, then need to optimize.
# Otherwise, return the graph found.
found_graph_in_db = None
# The sole purpose of this loop is to set 'need_optimize' by
# going through graph_db, looking for graph that has the same
# computation performed.
for graph_old, graph_optimized in graph_db.iteritems():
inputs_old = graph_old.inputs inputs_old = graph_old.inputs
outputs_old = graph_old.outputs outputs_old = graph_old.outputs
size_old = len(graph_old.apply_nodes) size_old = len(graph_old.apply_nodes)
print 'looping through graph_db %d/%d' % (i + 1, len(graph_db))
# Some heuristics to check is the same graphs have # Some heuristics to check is the same graphs have
# already been optimized before. # already been optimized before.
if len(inputs_new) != len(inputs_old): if len(inputs_new) != len(inputs_old):
...@@ -1051,12 +1050,13 @@ class FunctionMaker(object): ...@@ -1051,12 +1050,13 @@ class FunctionMaker(object):
elif not size_old == size_new: elif not size_old == size_new:
print 'need to optimize, because numbers of nodes in graph are different' print 'need to optimize, because numbers of nodes in graph are different'
continue continue
else: else:
flags = [] flags = []
for output_new, output_old, i in zip(outputs_new, outputs_old, range(len(outputs_new))): for output_new, output_old, i in zip(
outputs_new, outputs_old, range(len(outputs_new))):
print 'loop through outputs node for both graphs' print 'loop through outputs node for both graphs'
graph_old.variables = set(gof.graph.variables(graph_old.inputs, graph_old.outputs)) graph_old.variables = set(gof.graph.variables(
graph_old.inputs, graph_old.outputs))
#using clone allowed to avoid a lot of errors #using clone allowed to avoid a lot of errors
#deep copy seemed to had. #deep copy seemed to had.
...@@ -1104,40 +1104,35 @@ class FunctionMaker(object): ...@@ -1104,40 +1104,35 @@ class FunctionMaker(object):
is_same = all(flags) is_same = all(flags)
if is_same: if is_same:
# found the match # found the match
print 'found #TODO: he match, no need to optimize' print 'found a match, no need to optimize'
need_optimize = False found_graph_in_db = graph_optimized
key = graph_old
break break
if need_optimize: return found_graph_in_db
# this is a brand new graph, optimize it, save it to graph_db
print 'optimizing the graph' graph_db = load_graph_db()
fgraph.variables = set(gof.graph.variables(fgraph.inputs, fgraph.outputs)) print 'loaded graph_db from %s, size=%d' % (graph_db_file, len(graph_db))
#check_integrity parameters was added to ignore found_graph = find_same_graph_in_db(graph_db)
#"excess cached variables" errors. Works that way if found_graph:
#but once again the error couldbe worth self.fgraph = found_graph
#investigating. optimizer_profile = None
before_opt = fgraph.clone(check_integrity=False) else:
start_optimizer = time.time() # this is a brand new graph, optimize it, save it to graph_db
optimizer_profile = optimizer(fgraph) print 'graph not found in graph_db, optimizing the graph'
end_optimizer = time.time() self.fgraph.variables = set(gof.graph.variables(
opt_time = end_optimizer - start_optimizer self.fgraph.inputs, self.fgraph.outputs))
graph_db.update({before_opt:fgraph}) #check_integrity parameters was added to ignore
f = open(graph_db_file, 'wb') #"excess cached variables" errors. Works that way
cPickle.dump(graph_db, f, -1) #but once again the error couldbe worth
f.close() #investigating.
print 'saved into graph_db' before_opt = self.fgraph.clone(check_integrity=False)
else: optimizer_profile = optimizer(self.fgraph)
print 'no opt, get graph from graph_db' graph_db.update({before_opt:self.fgraph})
# just read the optmized graph from graph_db f = open(graph_db_file, 'wb')
opt_time = 0 cPickle.dump(graph_db, f, -1)
f.close()
#"Naive" insertion. It's seems to work, but there may print 'new graph saved into graph_db'
#be some problems inserting it like that. release_lock()
self.fgraph = graph_db[key] return optimizer_profile
fgraph = self.fgraph
# release stuff
release_lock()
def __init__(self, inputs, outputs, def __init__(self, inputs, outputs,
mode=None, accept_inplace=False, function_builder=Function, mode=None, accept_inplace=False, function_builder=Function,
...@@ -1242,7 +1237,14 @@ class FunctionMaker(object): ...@@ -1242,7 +1237,14 @@ class FunctionMaker(object):
theano.config.compute_test_value = theano.config.compute_test_value_opt theano.config.compute_test_value = theano.config.compute_test_value_opt
gof.Op.add_stack_trace_on_call = False gof.Op.add_stack_trace_on_call = False
start_optimizer = time.time() start_optimizer = time.time()
optimizer_profile = optimizer(fgraph)
# now optimize the graph
if theano.config.cache_optimizations:
optimizer_profile = self.optimize_graph_with_cache(
optimizer, inputs, outputs)
else:
optimizer_profile = optimizer(fgraph)
end_optimizer = time.time() end_optimizer = time.time()
opt_time = end_optimizer - start_optimizer opt_time = end_optimizer - start_optimizer
if profile: if profile:
......
...@@ -547,9 +547,9 @@ AddConfigVar('check_input', ...@@ -547,9 +547,9 @@ AddConfigVar('check_input',
BoolParam(True)) BoolParam(True))
AddConfigVar('cache_optimizations', AddConfigVar('cache_optimizations',
"WARNING: work in progress, does not work yet." "WARNING: work in progress, does not work yet. "
"Specify if the optimization cache should be used. This cache will" "Specify if the optimization cache should be used. This cache will "
"any optimized graph and its optimization. Actually slow downs a lot" "any optimized graph and its optimization. Actually slow downs a lot "
"the first optimization, and could possibly still contains some bugs." "the first optimization, and could possibly still contains some bugs. "
"Use at your own risks.", "Use at your own risks.",
BoolParam(False)) BoolParam(False))
...@@ -748,7 +748,7 @@ class FunctionGraph(utils.object2): ...@@ -748,7 +748,7 @@ class FunctionGraph(utils.object2):
def __getstate__(self): def __getstate__(self):
"""This is needed as some feature introduce instancemethod and """This is needed as some feature introduce instancemethod and
this is not pickable. this is not picklable.
""" """
d = self.__dict__.copy() d = self.__dict__.copy()
for feature in self._features: for feature in self._features:
......
import unittest, os
import numpy
import cPickle
from theano.compat.python2x import DictMixin, OrderedDict
floatX = 'float32'
import theano
import theano.tensor as T
def test_graph_opt_caching():
opt_db_file = theano.config.compiledir+'/optimized_graphs.pkl'
os.system('rm %s'%opt_db_file)
mode = theano.config.mode
if mode in ["DEBUG_MODE", "DebugMode"]:
mode = "FAST_RUN"
default = theano.config.cache_optimizations
try:
theano.config.cache_optimizations = True
a = T.fmatrix('a')
b = T.fmatrix('b')
c = theano.shared(numpy.ones((10, 10), dtype=floatX))
d = theano.shared(numpy.ones((10, 10), dtype=floatX))
e = T.sum(T.sum(T.sum(a ** 2 + b) + c) + d)
f1 = theano.function([a, b], e, mode=mode)
m = T.fmatrix('x1')
n = T.fmatrix('x2')
p = theano.shared(numpy.ones((10, 10), dtype=floatX))
q = theano.shared(numpy.ones((10, 10), dtype=floatX))
j = T.sum(T.sum(T.sum(m ** 2 + n) + p) + q)
f2 = theano.function([m, n], j, mode=mode)
in1 = numpy.ones((10, 10), dtype=floatX)
in2 = numpy.ones((10, 10), dtype=floatX)
assert f1(in1, in2) == f2(in1, in2)
finally:
theano.config.cache_optimizations = default
if __name__ == '__main__':
test_graph_opt_caching()
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论