1
2 import logging
3 from operator import attrgetter, concat
4 from collections import namedtuple
5
6 import numpy as np
7 import pandas as pd
8
9 from .. import archive
10
11 log = logging.getLogger(__name__)
14 """ abstract class keep track of a set of parameters in the model or the
15 learning process"""
16
17 ftype_frm = {int : "%d", float : "%.6f", str : "%s"}
18 fzero = {int : 0, float : 0.0, str : "NA"}
19
20 @property
21 - def csv(self, sep=","):
23
24 @classmethod
27
28 @property
31
32 @classmethod
34 """compares a summary of the last `patience` values defined by the `what` field
35 with the `patience`-1 value"""
36
37
38 if len(data) < patience + 1:
39 raise ValueError("not enough history (%d) for this patience (%d)" % (len(data), patience))
40
41 what_f = attrgetter(what)
42
43
44 patience_best = summ_f(map(what_f, data[-patience:]))
45
46 history_mark = what_f(data[-patience-1])
47
48 assert type(patience_best) == type(history_mark)
49 log.debug("%f, [%f]" % (history_mark, patience_best))
50
51 return cmp_f(patience_best, history_mark)
52
53 @classmethod
55 "(final-init)/max(final, init)"
56
57 M = float( max(final, init) )
58 if M:
59 return (final-init)/M
60 return 0.
61
62 @classmethod
64 return cls._has_changed(what, memory, data, min, lambda summarized, mark: summarized > mark)
65
66 @classmethod
68 return cls._has_changed(what, memory, data, min, lambda summarized, mark: summarized < mark)
69
70 @classmethod
72 return cls._has_changed(what, memory, data, min, lambda summarized, mark: summarized == mark)
73
74 @classmethod
76 return cls._has_changed(what, memory, data, max, lambda summarized, mark: summarized > mark)
77
78 @classmethod
80 return cls._has_changed(what, memory, data, max, lambda summarized, mark: summarized < mark)
81
82 @classmethod
84 return cls._has_changed(what, memory, data, max, lambda summarized, mark: summarized == mark)
85
86 @classmethod
87 - def _dump(cls, path, data):
88 with open(path, 'w') as fd:
89 log.info("saving %s log to %s ..." % (str(cls), fd.name))
90 fd.write(",".join( cls._fields )+"\n")
91 map(lambda r: fd.write("%s\n" % r.csv), data)
92
93 @classmethod
100
101
102 -class DaeLearnMonitor(Monitor, namedtuple("learnmonitor", "epoch lrate traincost validcost")):
103 _ftypes = (int, float, float, float)
104 _report_frm = ("Epoch {epoch} / lrate: {lrate:.6f}\n\t"
105 "Cost: {traincost:.6f} / {validcost:.6f} \n\t")
106
107 @classmethod
108 - def _from_fs(cls, ds, mtr, epoch, cost_f):
109 train_batches = ds.train_batches
110 valid_batches = ds.valid_batches
111
112 ct = np.array( map(cost_f, train_batches) ).mean()
113 cv = np.array( map(cost_f, valid_batches) ).mean()
114
115 return cls._make( [epoch, mtr.lr, ct, cv] )
116
117
118 -class LearnMonitor(Monitor, namedtuple("learnmonitor", "epoch lrate traincost trainCE trainMC validcost validCE validMC") ):
119 """learning stats"""
120
121 _ftypes = (int, float,
122 float, float,
123 float, float,
124 float, float)
125 _report_frm = ("Epoch {epoch} / lrate: {lrate:.6f}\n\t"
126 "Cost: {traincost:.6f} / {validcost:.6f} \n\t"
127 "Cross entropy: {trainCE:.6f} / {validCE:.6f} \n\t"
128 "Missclass.: {trainMC:.2%} / {validMC:.2%}")
129
130 @classmethod
131 - def _from_fs(cls, ds, mtr, epoch, cost_f, ce_f, mcl_f):
132 train_batches = ds.train_batches
133 valid_batches = ds.valid_batches
134
135 train_s = ds.batch_size * len(ds.train_batches)
136 valid_s = ds.batch_size * len(ds.valid_batches)
137 a = [np.mean(np.array(map(cost_f, train_batches))),
138 np.mean(np.array(map(ce_f, train_batches))),
139 sum(map(mcl_f, train_batches)) / float(train_s),
140
141 np.mean(np.array(map(cost_f, valid_batches))),
142 np.mean(np.array(map(ce_f, valid_batches))),
143 sum(map(mcl_f, valid_batches)) / float(valid_s)]
144
145 return cls._make( [epoch, mtr.lr] + a )
146
147 -class RegrLearnMonitor(Monitor, namedtuple("learnmonitor", "epoch lrate traincost validcost") ):
148 """learning stats for a linear regression """
149
150 _ftypes = (int, float, float, float)
151 _report_frm = ("Epoch {epoch} / lrate: {lrate:.6f}\n\t"
152 "Cost: {traincost:.6f} / {validcost:.6f}")
153
154 @classmethod
155 - def _from_fs(cls, ds, mtr, epoch, cost_f, train_batches, valid_batches):
156 a = [np.mean(np.array(map(cost_f, train_batches))),
157 np.mean(np.array(map(cost_f, valid_batches)))]
158 return cls._make( [epoch, mtr.lr] + a )
159
160
161 -class WeightMonitor( Monitor, namedtuple("w_monitor", "epoch layer wshp wmin wmean wmedian wsd wmax bshp bmin bmean bmedian bsd bmax") ):
162 """weight and activity information on the network"""
163
164 _ftypes = (int, int,
165 str, float, float, float, float, float,
166 str, float, float, float, float, float)
167 _report_frm = ("{epoch:d} {layer:d} "
168 "W({wshp:s}): {wmin:.6f} {wmean:.6f} {wmedian:.6f} {wsd:.6f} {wmax:.6f}\n\t"
169 "B({bshp:s}): {bmin:.6f} {bmean:.6f} {bmedian:.6f} {bsd:.6f} {bmax:.6f}")
170
171 @classmethod
173 def stat_record(a):
174 return map(lambda f: f(a), (np.min, np.mean, np.median, np.std, np.max))
175
176 def shape_str(a):
177 return str(np.prod(a.shape))
178
179 w_lst = model[l].get_weights()
180 w_shapes = map(shape_str, w_lst)
181 w_stats = map(stat_record, w_lst)
182 w_rec = map(lambda (a,b): [a] + b, zip(w_shapes, w_stats))
183
184 return cls._make( [epoch, l] + reduce(concat, w_rec) )
185
186
187 if __name__ == "__main__":
188 for C in (WeightMonitor, LearnMonitor):
189 assert len(C._ftypes) == len(C._fields), "%s) C._ftypes %d != C._fields = %d" % (str(C), len(C._ftypes) , len(C._fields))
190