-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathtest_pixel_model.lua
355 lines (310 loc) · 11.6 KB
/
test_pixel_model.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
--[[
Courtesy of Karpathy
Unit tests for the PixelModel implementation, making sure
that nothing crashes, that we can overfit a small dataset
and that everything gradient checks.
--]]
require 'torch'
require 'pm'
local gradcheck = require 'misc.gradcheck'
local tests = {}
local tester = torch.Tester()
-- validates the size and dimensions of a given
-- tensor a to be size given in table sz
function tester:assertTensorSizeEq(a, sz)
tester:asserteq(a:nDimension(), #sz)
for i=1,#sz do
tester:asserteq(a:size(i), sz[i])
end
end
-- Test the API of the Pixel Model
local function forwardApiTestFactory(dtype)
if dtype == 'torch.CudaTensor' then
require 'cutorch'
require 'cunn'
end
local function f()
-- create PixelModel instance
local opt = {}
opt.pixel_size = 3
opt.num_mixtures = 2
opt.recurrent_stride = 3
opt.rnn_size = 8
opt.num_layers = 2
opt.dropout = 0
opt.seq_length = 7
opt.batch_size = 10
local pm = nn.PixelModel(opt)
local crit = nn.PixelModelCriterion(opt.num_mixtures)
pm:type(dtype) --type function set the module parameters
crit:type(dtype)
-- construct some input to feed in
local pixels = torch.rand(opt.seq_length, opt.batch_size, opt.pixel_size)
local borders = torch.ge(torch.rand(opt.seq_length, opt.batch_size, 1), 0.5):type(pixels:type())
local seq = torch.cat(pixels, borders, 3):type(dtype)
-- forward
local output = pm:forward(seq)
-- returned output is a table with each element is a sequence
tester:asserteq(#output, opt.seq_length)
-- for each sequnece, it's also a table, with 4 tensors corresponding to gmm encodings
tester:asserteq(#output[1], 4)
-- the first is the mean
tester:assertTensorSizeEq(output[1][1], {opt.batch_size, opt.num_mixtures, opt.pixel_size})
-- the second is the covariance
tester:assertTensorSizeEq(output[1][2], {opt.batch_size, opt.num_mixtures, opt.pixel_size, opt.pixel_size})
tester:assertTensorEq(output[1][2][{{},{},1,2}], output[1][2][{{},{},2,1}], 1e-6) -- should be symmetric
tester:assertTensorEq(output[1][2][{{},{},1,3}], output[1][2][{{},{},3,1}], 1e-6)
tester:assertTensorEq(output[1][2][{{},{},2,3}], output[1][2][{{},{},3,2}], 1e-6)
-- the third is the gmm weights
tester:assertTensorSizeEq(output[1][3], {opt.batch_size, opt.num_mixtures})
tester:assertTensorEq(torch.sum(output[1][3], 2), 1, 1e-6) -- weights should be summed to 1
-- the fourth is the border
tester:assertTensorSizeEq(output[1][4], {opt.batch_size})
local loss = crit:forward(output, seq)
local gradOutput = crit:backward(output, seq)
-- this should be the same size as the output
tester:asserteq(#gradOutput, opt.seq_length)
tester:asserteq(#gradOutput[1], 4)
-- the first is the mean
tester:assertTensorSizeEq(gradOutput[1][1], {opt.batch_size, opt.num_mixtures, opt.pixel_size})
-- the second is the covariance
tester:assertTensorSizeEq(gradOutput[1][2], {opt.batch_size, opt.num_mixtures, opt.pixel_size, opt.pixel_size})
tester:assertTensorEq(gradOutput[1][2][{{},{},1,2}], gradOutput[1][2][{{},{},2,1}], 1e-6) -- should be symmetric
tester:assertTensorEq(gradOutput[1][2][{{},{},1,3}], gradOutput[1][2][{{},{},3,1}], 1e-6)
tester:assertTensorEq(gradOutput[1][2][{{},{},2,3}], gradOutput[1][2][{{},{},3,2}], 1e-6)
-- the third is the gmm weights
tester:assertTensorSizeEq(gradOutput[1][3], {opt.batch_size, opt.num_mixtures})
-- the fourth is the border
tester:assertTensorSizeEq(gradOutput[1][4], {opt.batch_size})
local gradInput = pm:backward(seq, gradOutput)
tester:assertTensorSizeEq(gradInput, {opt.seq_length, opt.batch_size, opt.pixel_size+1})
end
return f
end
-- test just the language model alone (without the criterion)
local function gradCheckPM()
local dtype = 'torch.DoubleTensor'
local opt = {}
opt.pixel_size = 3
opt.num_mixtures = 2
opt.recurrent_stride = 3
opt.rnn_size = 8
opt.num_layers = 2
opt.dropout = 0
opt.seq_length = 9
opt.batch_size = 5
opt.mult_in = false
opt.num_neighbors = 4
opt.border_init = 0
local pm = nn.PixelModel4N(opt)
pm:type(dtype)
local pixels = torch.rand(opt.seq_length, opt.batch_size, opt.pixel_size*opt.num_neighbors)
-- evaluate the analytic gradient
local output = pm:forward(pixels)
local w = torch.randn(output:size())
-- generate random weighted sum criterion
local loss = torch.sum(torch.cmul(output, w))
local gradOutput = w
local gradInput = pm:backward(pixels, gradOutput)
-- create a loss function wrapper
local function f(x)
local output = pm:forward(x)
local loss = torch.sum(torch.cmul(output, w))
return loss
end
local gradInput_num = gradcheck.numeric_gradient(f, pixels, 1, 1e-6)
-- print(gradInput)
-- print(gradInput_num)
-- local g = gradInput:view(-1)
-- local gn = gradInput_num:view(-1)
-- for i=1,g:nElement() do
-- local r = gradcheck.relative_error(g[i],gn[i])
-- print(i, g[i], gn[i], r)
-- end
tester:assertTensorEq(gradInput, gradInput_num, 1e-4)
tester:assertlt(gradcheck.relative_error(gradInput, gradInput_num, 1e-8), 1e-4)
end
local function gradCheckCrit()
local dtype = 'torch.DoubleTensor'
local opt = {}
opt.pixel_size = 3
opt.num_mixtures = 2
opt.recurrent_stride = 3
opt.rnn_size = 8
opt.num_layers = 2
opt.dropout = 0
opt.seq_length = 9
opt.batch_size = 2
opt.mult_in = true
opt.num_neighbors = 4
opt.border_init = 0
local crit = nn.PixelModelCriterion(opt.pixel_size, opt.num_mixtures, {policy='const', val=0.9})
crit:type(dtype)
local gmms = torch.rand(opt.seq_length, opt.batch_size, crit.output_size)
local targets = torch.rand(opt.seq_length, opt.batch_size, opt.pixel_size)
--local borders = torch.ge(torch.rand(opt.seq_length, opt.batch_size, 1), 0.5):type(pixels:type())
--local seq = torch.cat(pixels, borders, 3):type(dtype)
local gmmsc = gmms:clone()
-- evaluate the analytic gradient
local loss = crit:forward(gmms, targets)
local gradInput = crit:backward(gmms, targets)
-- create a loss function wrapper
local function f(x)
local loss = crit:forward(x, targets)
return loss
end
local gradInput_num = gradcheck.numeric_gradient(f, gmmsc, 1, 1e-6)
print(gradInput)
print(gradInput_num)
local g = gradInput:view(-1)
local gn = gradInput_num:view(-1)
for i=1,g:nElement() do
local r = gradcheck.relative_error(g[i],gn[i])
print(i, g[i], gn[i], r)
end
tester:assertTensorEq(gradInput, gradInput_num, 1e-4)
tester:assertlt(gradcheck.relative_error(gradInput, gradInput_num, 1e-8), 5e-4)
end
local function gradCheck()
local dtype = 'torch.DoubleTensor'
local opt = {}
opt.pixel_size = 3
opt.num_mixtures = 5
opt.recurrent_stride = 3
opt.rnn_size = 8
opt.num_layers = 2
opt.dropout = 0
opt.seq_length = 9
opt.batch_size = 2
opt.mult_in = true
opt.num_neighbors = 4
opt.border_init = 0
local pm = nn.PixelModel4N(opt)
local crit = nn.PixelModelCriterion(opt.pixel_size, opt.num_mixtures, {runs=2})
pm:type(dtype)
crit:type(dtype)
local pixels = torch.rand(opt.seq_length, opt.batch_size, opt.pixel_size*opt.num_neighbors)
local targets = torch.rand(opt.seq_length, opt.batch_size, opt.pixel_size)
--local borders = torch.ge(torch.rand(opt.seq_length, opt.batch_size, 1), 0.5):type(pixels:type())
--local seq = torch.cat(pixels, borders, 3):type(dtype)
-- evaluate the analytic gradient
local output = pm:forward(pixels)
local loss = crit:forward(output, targets)
local gradOutput = crit:backward(output, targets)
local gradInput = pm:backward(pixels, gradOutput)
-- create a loss function wrapper
local function f(x)
local output = pm:forward(x)
local loss = crit:forward(output, targets)
return loss
end
local gradInput_num = gradcheck.numeric_gradient(f, pixels, 1, 1e-6)
--print(gradInput)
--print(gradInput_num)
--local g = gradInput:view(-1)
--local gn = gradInput_num:view(-1)
--for i=1,g:nElement() do
--local r = gradcheck.relative_error(g[i],gn[i])
-- print(i, g[i], gn[i], r)
--end
tester:assertTensorEq(gradInput, gradInput_num, 1e-4)
tester:assertlt(gradcheck.relative_error(gradInput, gradInput_num, 1e-8), 5e-4)
end
local function overfit()
local dtype = 'torch.DoubleTensor'
local opt = {}
opt.pixel_size = 3
opt.num_mixtures = 2
opt.recurrent_stride = 3
opt.rnn_size = 8
opt.num_layers = 2
opt.dropout = 0
opt.seq_length = 7
opt.batch_size = 2
opt.mult_in = true
local pm = nn.PixelModel(opt)
local crit = nn.PixelModelCriterion(opt.pixel_size, opt.num_mixtures)
pm:type(dtype)
crit:type(dtype)
local pixels = torch.rand(opt.seq_length, opt.batch_size, opt.pixel_size)
local targets = torch.rand(opt.seq_length, opt.batch_size, opt.pixel_size)
local params, grad_params = pm:getParameters()
local function lossFun()
grad_params:zero()
local output = pm:forward(pixels)
local loss = crit:forward(output, targets)
local gradOutput = crit:backward(output, targets)
pm:backward(pixels, gradOutput)
return loss
end
local loss
local grad_cache = grad_params:clone():fill(1e-8)
print('trying to overfit the pixel model on toy data:')
for t=1,5000 do
loss = lossFun()
-- test that initial loss makes sense
grad_cache:addcmul(1, grad_params, grad_params)
params:addcdiv(-1e-3, grad_params, torch.sqrt(grad_cache)) -- adagrad update
print(string.format('iteration %d/5000: loss %f', t, loss))
end
-- holy crap adagrad destroys the loss function!
tester:assertlt(loss, 0.2)
end
--[[
-- check that we can call :sample_beam() and that correct-looking things happen
-- these are not very exhaustive tests and basic sanity checks
local function sample_beam()
local dtype = 'torch.DoubleTensor'
torch.manualSeed(1)
local opt = {}
opt.pixel_size = 3
opt.output_size = 10
opt.rnn_size = 8
opt.num_layers = 1
opt.dropout = 0
opt.seq_length = 7
opt.batch_size = 6
local pm = nn.PixelModel(opt)
local imgs = torch.randn(opt.batch_size, opt.input_encoding_size):type(dtype)
local seq_vanilla, logprobs_vanilla = pm:sample(imgs)
local seq, logprobs = pm:sample(imgs, {beam_size = 1})
-- check some basic I/O, types, etc.
tester:assertTensorSizeEq(seq, {opt.seq_length, opt.batch_size})
tester:asserteq(seq:type(), 'torch.LongTensor')
tester:assertge(torch.min(seq), 0)
tester:assertle(torch.max(seq), opt.pixel_size+1)
-- doing beam search with beam size 1 should return exactly what we had before
print('')
print('vanilla sampling:')
print(seq_vanilla)
print('beam search sampling with beam size 1:')
print(seq)
tester:assertTensorEq(seq_vanilla, seq, 0) -- these are LongTensors, expect exact match
tester:assertTensorEq(logprobs_vanilla, logprobs, 1e-6) -- logprobs too
-- doing beam search with higher beam size should yield higher likelihood sequences
local seq2, logprobs2 = pm:sample(imgs, {beam_size = 8})
local logsum = torch.sum(logprobs, 1)
local logsum2 = torch.sum(logprobs2, 1)
print('')
print('beam search sampling with beam size 1:')
print(seq)
print('beam search sampling with beam size 8:')
print(seq2)
print('logprobs:')
print(logsum)
print(logsum2)
-- the logprobs should always be >=, since beam_search is better argmax inference
tester:assert(torch.all(torch.gt(logsum2, logsum)))
end
--]]
--tests.doubleApiForwardTest = forwardApiTestFactory('torch.DoubleTensor')
--tests.floatApiForwardTest = forwardApiTestFactory('torch.FloatTensor')
-- tests.cudaApiForwardTest = forwardApiTestFactory('torch.CudaTensor')
--tests.gradCheckPM = gradCheckPM
tests.gradCheckCrit = gradCheckCrit
--tests.gradCheck = gradCheck
-- tests.overfit = overfit
--tests.sample = sample
--tests.sample_beam = sample_beam
tester:add(tests)
tester:run()