Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
pytensor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
testgroup
pytensor
Commits
e6ecae1c
提交
e6ecae1c
authored
8月 13, 2015
作者:
Iban Harlouchet
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
numpydoc for theano/compile/debugmode.py
上级
d10856f6
隐藏空白字符变更
内嵌
并排
正在显示
1 个修改的文件
包含
298 行增加
和
149 行删除
+298
-149
debugmode.py
theano/compile/debugmode.py
+298
-149
没有找到文件。
theano/compile/debugmode.py
浏览文件 @
e6ecae1c
"""Provides `DebugMode`, an evaluation mode for debugging theano internals.
"""
Provides `DebugMode`, an evaluation mode for debugging theano internals.
:
TODO: add support for IfElse Op, LazyLinker, PureOp, etc.
TODO: add support for IfElse Op, LazyLinker, PureOp, etc.
"""
from
__future__
import
print_function
...
...
@@ -123,7 +124,11 @@ _logger.addFilter(NoDuplicateOptWarningFilter())
#
########################
class
DebugModeError
(
Exception
):
"""Generic Exception raised to indicate an internal theano problem"""
"""
Generic Exception raised to indicate an internal theano problem.
"""
pass
...
...
@@ -135,21 +140,30 @@ class BadThunkOutput(DebugModeError):
do not agree, or if one of these methods do not give the same result
when called twice with the same inputs (but different memory layouts
for the output).
"""
r
=
None
"""The `Variable` instance for which conflicting values were computed"""
"""
The `Variable` instance for which conflicting values were computed.
"""
thunk1
=
''
val1
=
None
"""The value computed by `thunk1`"""
"""
The value computed by `thunk1`.
"""
thunk2
=
''
val2
=
None
"""The value computed by `thunk2`"""
"""
The value computed by `thunk2`.
"""
def
__init__
(
self
,
r
,
thunk1
,
val1
,
thunk2
,
val2
,
inputs_val
=
()):
"""Initialize members"""
super
(
BadThunkOutput
,
self
)
.
__init__
()
self
.
r
=
r
self
.
thunk1
=
thunk1
...
...
@@ -159,16 +173,22 @@ class BadThunkOutput(DebugModeError):
self
.
inputs_val
=
inputs_val
def
offending_op
(
self
):
"""Return the Op class whose c_code and perform
implementations didn't match"""
"""
Return the Op class whose c_code and perform implementations
didn't match.
"""
return
type
(
self
.
r
.
owner
.
op
)
def
__str__
(
self
):
return
self
.
str_diagnostic
()
def
str_diagnostic
(
self
):
"""Return a pretty multiline string representating the cause
of the exception"""
"""
Return a pretty multiline string representing the cause of
the exception.
"""
sio
=
StringIO
()
print
(
"BadThunkOutput"
,
file
=
sio
)
print
(
" Apply :"
,
self
.
r
.
owner
,
file
=
sio
)
...
...
@@ -202,41 +222,61 @@ class BadThunkOutput(DebugModeError):
class
BadOptimization
(
DebugModeError
):
"""Exception: some variable and its substitute take different
runtime values.
"""
Exception: some variable and its substitute take different runtime values.
"""
new_r
=
None
"""A `Variable` instance that took a different value from `old_r`,
but which replaced `old_r`."""
"""
A `Variable` instance that took a different value from `old_r`,
but which replaced `old_r`.
"""
old_r
=
None
"""A `Variable` instance that was replaced by `new_r`."""
"""
A `Variable` instance that was replaced by `new_r`.
"""
old_r_val
=
None
"""The value computed for `old_r`."""
"""
The value computed for `old_r`.
"""
new_r_val
=
None
"""The value computed for `new_r`."""
"""
The value computed for `new_r`.
"""
reason
=
None
"""An object that indicates why old_r was turned into new_r.
"""
An object that indicates why old_r was turned into new_r.
Convention is that this is the name of the optimization that
requested the replacement.
"""
old_graph
=
""
"""A multiline string representation of the graph leading to
old_r, at the time of the replacement."""
"""
A multiline string representation of the graph leading to
old_r, at the time of the replacement.
"""
new_graph
=
""
"""A multiline string representation of the graph leading to
new_r, at the time of the replacement."""
"""
A multiline string representation of the graph leading to
new_r, at the time of the replacement.
"""
def
__init__
(
self
,
old_r
,
new_r
,
old_r_val
,
new_r_val
,
reason
,
old_graph
,
new_graph
):
"""Initialize members"""
super
(
BadOptimization
,
self
)
.
__init__
()
self
.
old_r
=
old_r
self
.
new_r
=
new_r
...
...
@@ -250,8 +290,11 @@ class BadOptimization(DebugModeError):
return
self
.
str_diagnostic
()
def
str_diagnostic
(
self
):
"""Return a pretty multiline string representating the cause
of the exception"""
"""
Return a pretty multiline string representating the cause
of the exception.
"""
sio
=
StringIO
()
val_str_len_limit
=
800
print
(
"BadOptimization Error"
,
super
(
BadOptimization
,
...
...
@@ -340,8 +383,11 @@ class BadOptimization(DebugModeError):
class
BadDestroyMap
(
DebugModeError
):
"""Exception: Some perform() or c_code() modified an input that
wasn't in the destroy_map"""
"""
Exception: Some perform() or c_code() modified an input that
wasn't in the destroy_map.
"""
def
__init__
(
self
,
node
,
idx
,
old_val
,
new_val
,
perform
):
super
(
BadDestroyMap
,
self
)
.
__init__
()
self
.
node
=
node
...
...
@@ -395,8 +441,12 @@ class BadDestroyMap(DebugModeError):
class
BadViewMap
(
DebugModeError
):
"""Exception: Some perform() or c_code() created a memory alias
that wasn't in the view_map"""
"""
Exception: Some perform() or c_code() created a memory alias
that wasn't in the view_map.
"""
def
__init__
(
self
,
node
,
output_idx
,
out_storage
,
in_alias_idx
=
None
,
out_alias_idx
=
None
):
super
(
BadViewMap
,
self
)
.
__init__
()
...
...
@@ -426,7 +476,8 @@ class BadViewMap(DebugModeError):
class
StochasticOrder
(
DebugModeError
):
"""Exception: Repeated Optimizations of the same graph do not give
"""
Exception: Repeated Optimizations of the same graph do not give
identical results.
The most common cause is that an Optimization iterates over some
...
...
@@ -440,8 +491,12 @@ class StochasticOrder(DebugModeError):
class
InvalidValueError
(
DebugModeError
):
"""Exception: some Op an output value that is inconsistent with
the Type of that output"""
"""
Exception: some Op an output value that is inconsistent with
the Type of that output.
"""
def
__init__
(
self
,
r
,
v
,
client_node
=
None
,
hint
=
'none'
,
specific_hint
=
'none'
):
super
(
InvalidValueError
,
self
)
.
__init__
()
...
...
@@ -498,8 +553,11 @@ class InvalidValueError(DebugModeError):
def
char_from_number
(
number
):
""" Converts number to string by rendering it in base 26 using
capital letters as digits """
"""
Converts number to string by rendering it in base 26 using
capital letters as digits.
"""
base
=
26
...
...
@@ -523,31 +581,45 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
stop_on_name
=
False
,
prefix_child
=
None
,
scan_ops
=
None
,
profile
=
None
,
scan_inner_to_outer_inputs
=
None
):
"""Print the graph leading to `r` to given depth.
:param r: Variable instance
:param prefix: prefix to each line (typically some number of spaces)
:param depth: maximum recursion depth (Default -1 for unlimited).
:param done: dict of Apply instances that have already been printed
and their associated printed ids
:param print_type: whether to print the Variable type after the other infos
:param file: file-like object to which to print
:param print_destroy_map: whether to print the op destroy_map after
other info
:param print_view_map: whether to print the op view_map after other info
:param order: If not empty will print the index in the toposort.
:param ids: How do we print the identifier of the variable
id - print the python id value
int - print integer character
CHAR - print capital character
"" - don't print an identifier
:param stop_on_name: When True, if a node in the graph has a name,
we don't print anything below it.
:param scan_ops: Scan ops in the graph will be added inside this list
for later printing purposes.
:param scan_inner_to_outer_inputs: a dictionary mapping a scan ops
inner function inputs to the scan op inputs (outer inputs) for
printing purposes.
"""
Print the graph leading to `r` to given depth.
Parameters
----------
r
Variable instance.
prefix
Prefix to each line (typically some number of spaces).
depth
Maximum recursion depth (Default -1 for unlimited).
done
dict of Apply instances that have already been printed and their
associated printed ids.
print_type
Whether to print the Variable type after the other infos.
file
File-like object to which to print.
print_destroy_map
Whether to print the op destroy_map after other info.
print_view_map
Whether to print the op view_map after other info.
order
If not empty will print the index in the toposort.
ids
How do we print the identifier of the variable :
id - print the python id value,
int - print integer character,
CHAR - print capital character,
"" - don't print an identifier.
stop_on_name
When True, if a node in the graph has a name, we don't print anything
below it.
scan_ops
Scan ops in the graph will be added inside this list for later printing
purposes.
scan_inner_to_outer_inputs
A dictionary mapping a scan ops inner function inputs to the scan op
inputs (outer inputs) for printing purposes.
"""
if
depth
==
0
:
...
...
@@ -712,17 +784,24 @@ def debugprint(r, prefix='', depth=-1, done=None, print_type=False,
def
_optcheck_fgraph
(
input_specs
,
output_specs
,
accept_inplace
=
False
):
"""Create an FunctionGraph for debugging.
:param input_specs: fgraph inputs
:type input_specs: WRITEME
:param output_specs: fgraph outputs
:type output_specs: WRITEME
:param accept_inplace: are inplace ops permitted in the original graph?
:type accept_inplace: Bool
:rtype: `FunctionGraph`
:returns: a new FunctionGraph with a cloned graph, with debugging
`Feature` instances already installed.
"""
Create a FunctionGraph for debugging.
Parameters
----------
input_specs: WRITEME
fgraph inputs.
output_specs: WRITEME
fgraph outputs.
accept_inplace : bool
Are inplace ops permitted in the original graph?
Returns
-------
FunctionGraph
A new FunctionGraph with a cloned graph, with debugging `Feature`
instances already installed.
"""
orig_inputs
=
[
spec
.
variable
for
spec
in
input_specs
]
updates
=
[
spec
.
update
for
spec
in
input_specs
if
spec
.
update
]
...
...
@@ -784,7 +863,8 @@ def check_eq(var, val1, val2):
def
_check_inputs
(
node
,
storage_map
,
r_vals
,
dr_vals
,
active_nodes
,
clobber_dr_vals
=
True
,
perform
=
None
,
warn_input_not_reused
=
True
):
"""Raise BadDestroyMap if necessary, update dr_vals
"""
Raise BadDestroyMap if necessary, update dr_vals.
Returns a list of output variables that actually worked inplace
(their value is aliased to the value of at least one input).
...
...
@@ -871,10 +951,11 @@ def _check_viewmap(node, storage_map):
"""
This functions raises a BadViewMap exception when it detects the
following:
- output node storages aliased to input storage, with no declaration
in view_map
- if not aliased to an input, check if two outputs are aliased together
and used subsequently in the graph
- Output node storages aliased to input storage, with no declaration
in view_map.
- If not aliased to an input, check if two outputs are aliased together
and used subsequently in the graph.
"""
for
oi
,
onode
in
enumerate
(
node
.
outputs
):
...
...
@@ -937,14 +1018,24 @@ def _check_viewmap(node, storage_map):
def
_is_used_in_graph
(
var
):
"""
Returns True if `var` is used by another node in the graph
Returns
-------
bool
True if `var` is used by another node in the graph.
"""
return
not
(
var
.
clients
==
[(
'output'
,
1
)]
or
var
.
clients
==
[])
def
_check_strides_match
(
a
,
b
,
warn_err
,
op
):
"""
param: warn_err: if 0, no warning, if 1 warning, if 2 error
Parameters
----------
warn_err
If 0, no warning, if 1 warning, if 2 error.
"""
if
warn_err
==
0
:
return
...
...
@@ -965,12 +1056,20 @@ def _check_strides_match(a, b, warn_err, op):
def
_lessbroken_deepcopy
(
a
):
"""
:param a: any object
Returns a copy of `a` that shares no internal storage with the original
(a deep copy).
This function handles numpy arrays specially, because copy.deepcopy()
called on a 0-d array will return a numpy scalar, not an array.
Parameters
----------
a
Any object
Returns
-------
object
A copy of `a` that shares no internal storage with the original
(a deep copy). This function handles numpy arrays specially, because
copy.deepcopy() called on a 0-d array will return a numpy scalar,
not an array.
"""
# this exists because copy.deepcopy on numpy arrays is broken
# This logic is also in link.py
...
...
@@ -990,13 +1089,15 @@ def _lessbroken_deepcopy(a):
def
_find_bad_optimizations0
(
order
,
reasons
,
r_vals
):
"""Use a simple algorithm to find broken optimizations.
"""
Use a simple algorithm to find broken optimizations.
This algorithm is simple to understand, but sometimes when there's
a problem it identifies the wrong optimization as the culprit.
The problem stems from the fact that results are not evaluated in
chronological order (looking at when they were introduced to the
graph).
"""
# iterate over variables looking for values that don't match the
# values of the variables they replaced. This is the sign of a
...
...
@@ -1078,19 +1179,24 @@ def _find_bad_optimizations1(order, reasons, r_vals):
def
_find_bad_optimizations2
(
order
,
reasons
,
r_vals
):
"""Use a simple algorithm to find broken optimizations.
"""
Use a simple algorithm to find broken optimizations.
This algorithm is simple to understand, but sometimes when there's
a problem it identifies the wrong optimization as the culprit.
The problem stems from the fact that results are not evaluated in
chronological order (looking at when they were introduced to the
graph).
"""
checked_variables
=
set
()
def
check_variable_norec
(
new_r
):
"""Verify that `r` has the same value as the results it replaces """
"""
Verify that `r` has the same value as the results it replaces.
"""
for
reason
,
r
,
old_graph_str
,
new_graph_str
in
reasons
[
new_r
]:
new_r_val
=
r_vals
[
new_r
]
r_val
=
r_vals
[
r
]
...
...
@@ -1134,7 +1240,10 @@ _find_bad_optimizations = _find_bad_optimizations0
def
_get_preallocated_maps
(
node
,
thunk
,
prealloc_modes
,
def_val
,
storage_map
,
r_vals
,
dr_vals
,
perform
,
active_order_set
,
inplace_outs
,
init_outputs
):
'''Preallocate outputs in different memory layouts'''
"""
Preallocate outputs in different memory layouts.
"""
# To avoid circular imports
from
theano.tensor
import
TensorType
...
...
@@ -1357,7 +1466,10 @@ def _get_preallocated_maps(node, thunk, prealloc_modes, def_val,
def
_check_preallocated_output
(
node
,
thunk
,
prealloc_modes
,
def_val
,
storage_map
,
r_vals
,
dr_vals
,
perform
,
active_order_set
,
inplace_outs
,
init_outputs
):
'''Try to apply thunk() on different output storages'''
"""
Try to apply thunk() on different output storages.
"""
# If node has an inner compiled Theano function with mode DebugMode,
# disable memory checks in that mode, since they were already run.
...
...
@@ -1460,26 +1572,40 @@ def _check_preallocated_output(node, thunk, prealloc_modes, def_val,
class
_FunctionGraphEvent
(
object
):
"""A record of an event in the life of an FunctionGraph.
"""
A record of an event in the life of an FunctionGraph.
The __eq__ function is important here, as it is the basis for
comparing optimization runs.
"""
kind
=
""
"""One of 'import', 'change', 'prune'"""
"""
One of 'import', 'change', 'prune'.
"""
node
=
None
"""Either 'output' or an Apply instance"""
"""
Either 'output' or an Apply instance.
"""
op
=
None
"""Either 'output' or an Op instance"""
idx
=
None
"""change events involve an position index of the input variable"""
"""
Change events involve an position index of the input variable.
"""
reason
=
None
"""change events sometimes have a reason"""
"""
Change events sometimes have a reason.
"""
def
__init__
(
self
,
kind
,
node
,
idx
=
None
,
reason
=
None
):
self
.
kind
=
kind
...
...
@@ -1522,8 +1648,11 @@ class _FunctionGraphEvent(object):
class
_VariableEquivalenceTracker
(
object
):
"""A FunctionGraph Feature that keeps tabs on an FunctionGraph and
tries to detect problems."""
"""
A FunctionGraph Feature that keeps tabs on an FunctionGraph and
tries to detect problems.
"""
fgraph
=
None
"""WRITEME"""
...
...
@@ -1675,7 +1804,11 @@ class _DummyLinker(object):
class
_Linker
(
gof
.
link
.
LocalLinker
):
"""Special debugging linker"""
"""
Special debugging linker.
"""
def
__init__
(
self
,
maker
,
schedule
=
None
):
super
(
gof
.
LocalLinker
,
self
)
.
__init__
()
self
.
fgraph
=
None
...
...
@@ -2236,11 +2369,39 @@ _NODEFAULT = ['NODEFAULT']
class
_Maker
(
FunctionMaker
):
# inheritance buys a few helper functions
"""Special debugging FunctionMaker
"""
Special debugging FunctionMaker.
Parameters
----------
inputs : list of SymbolicInput instances
outputs : list of SymbolicOutput instances
Outputs may also be a single Variable (not a list), in which case
the functions produced by FunctionMaker will return their output
value directly.
accept_inplace
True iff it is acceptable to have inplace operations in the graph from
the inputs to the outputs.
on_unused_input
What to do if a variable in the 'inputs' list is not used in the
graph. Possible values are 'raise', 'warn' and 'ignore'.
output_keys
If the outputs argument for theano.function was a list, then
output_keys is None. If the outputs argument was a dict, then
output_keys is a sorted list of the keys from that dict.
Notes
-----
The constructor sets TensorType.filter_checks_isfinite when
`mode.check_isfinite` is True.
"""
verbose
=
0
"""Verbosity level of compile-time and run-time checks. (Default
0: silent)"""
"""
Verbosity level of compile-time and run-time checks. (Default 0: silent).
"""
def
__init__
(
self
,
inputs
,
outputs
,
optimizer
,
mode
,
accept_inplace
=
False
,
...
...
@@ -2248,33 +2409,6 @@ class _Maker(FunctionMaker): # inheritance buys a few helper functions
profile
=
None
,
on_unused_input
=
None
,
output_keys
=
None
):
"""
:type inputs: a list of SymbolicInput instances
:type outputs: a list of SymbolicOutput instances outputs may
also be a single Variable (not a list), in
which case the functions produced by
FunctionMaker will return their output value
directly
:param accept_inplace: True iff it is acceptable to have
inplace operations in the graph from the inputs to
the outputs
:param on_unused_input: What to do if a variable in the
'inputs' list is not used in the
graph. Possible values are 'raise',
'warn', and 'ignore'.
:param output_keys: If the outputs argument for
theano.function was a list, then
output_keys is None. If the outputs
argument was a dict, then output_keys is a
sorted list of the keys from that dict.
:note: this function sets TensorType.filter_checks_isfinite
when `mode.check_isfinite` is True
"""
self
.
profile
=
profile
# Handle the case where inputs and/or outputs is a single
# Variable (not in a list)
...
...
@@ -2395,11 +2529,15 @@ class _Maker(FunctionMaker): # inheritance buys a few helper functions
"""
Create a function.
defaults -> a list matching the inputs list and providing default
values if the default for an input is None, then that input
is a required input. For an input with an update, the
default acts as initialization.
trustme -> disables some exceptions, used internally
Parameters
----------
defaults
A list matching the inputs list and providing default values if the
default for an input is None, then that input is a required input.
For an input with an update, the default acts as initialization.
trustme
Disables some exceptions, used internally.
"""
if
defaults
is
None
:
defaults
=
[
None
]
*
len
(
self
.
inputs
)
...
...
@@ -2514,35 +2652,40 @@ copyreg.pickle(_Maker, _pickle_DebugMode_Maker)
class
DebugMode
(
Mode
):
"""Evaluation Mode that detects internal theano errors.
"""
Evaluation Mode that detects internal theano errors.
This mode catches several kinds of internal error:
-
i
nconsistent outputs when calling the same Op twice with the same
-
I
nconsistent outputs when calling the same Op twice with the same
inputs, for instance if c_code and perform implementations, are
inconsistent, or in case of incorrect handling of output memory
(see `BadThunkOutput`)
,
(see `BadThunkOutput`)
.
-
a
variable replacing another when their runtime values don't
-
A
variable replacing another when their runtime values don't
match. This is a symptom of an incorrect optimization step, or
faulty Op implementation (raises `BadOptimization`)
faulty Op implementation (raises `BadOptimization`)
.
-
stochastic optimization ordering (raises `StochasticOrder`)
-
Stochastic optimization ordering (raises `StochasticOrder`).
-
incomplete `destroy_map` specification (raises `BadDestroyMap`)
-
Incomplete `destroy_map` specification (raises `BadDestroyMap`).
-
a
n op that returns an illegal value not matching the output
Variable Type (raises InvalidValueError)
-
A
n op that returns an illegal value not matching the output
Variable Type (raises InvalidValueError)
.
Each of these exceptions inherits from the more generic `DebugModeError`.
If there are no internal errors, this mode behaves like FAST_RUN
or FAST_COMPILE, but takes a little longer and uses more memory.
If there are internal errors, this mode will raise an
`DebugModeError` exception.
Raises
------
DebugModeError
If there are internal errors.
:remark: The work of debugging is implemented by the `_Maker`, `_Linker`,
Notes
-----
The work of debugging is implemented by the `_Maker`, `_Linker`,
and `_VariableEquivalenceTracker` classes.
"""
...
...
@@ -2551,22 +2694,26 @@ class DebugMode(Mode):
"""
When checking for the stability of optimization, recompile the
graph this many times.
"""
check_c_code
=
config
.
DebugMode
.
check_c
"""
Should we evaluate (and check) the `c_code` implementations?
"""
check_py_code
=
config
.
DebugMode
.
check_py
"""
Should we evaluate (and check) the `perform` implementations?
Always checked if no `c_code`.
"""
check_isfinite
=
config
.
DebugMode
.
check_finite
"""
Should we check for (and complain about) NaN/Inf ndarray elements?
"""
require_matching_strides
=
config
.
DebugMode
.
check_strides
...
...
@@ -2574,6 +2721,7 @@ class DebugMode(Mode):
Should we check for (and complain about) Ops whose python and C
outputs are ndarrays with different strides? (This can catch bugs,
but is generally overly strict.) 0 no check, 1 warn, 2 err.
"""
check_preallocated_output
=
config
.
DebugMode
.
check_preallocated_output
...
...
@@ -2584,13 +2732,15 @@ class DebugMode(Mode):
"c_contiguous", "f_contiguous", "strided" (positive and negative
strides), "wrong_size" (larger and smaller dimensions), and "ALL"
(all of the above).
"""
# This function will be used to create a FunctionMaker in
# function_module.function
def
function_maker
(
self
,
i
,
o
,
m
,
*
args
,
**
kwargs
):
"""
Return an instance of `_Maker` which handles much of the debugging work
Return an instance of `_Maker` which handles much of the debugging work.
"""
assert
m
is
self
return
_Maker
(
i
,
o
,
self
.
optimizer
,
self
,
*
args
,
**
kwargs
)
...
...
@@ -2604,12 +2754,11 @@ class DebugMode(Mode):
check_preallocated_output
=
None
,
require_matching_strides
=
None
,
linker
=
_DummyLinker
()):
"""Initialize member variables.
"""
If any of these arguments (except optimizer) is not None, it overrides
the class default.
The linker argument is not used. It is set there to allow
Mode.requiring() and some other fct to work with DebugMode too.
the class default.
The linker argument is not used. It is set there to
allow Mode.requiring() and some other fct to work with DebugMode too.
"""
if
not
isinstance
(
linker
,
_DummyLinker
):
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论