require 'torch' require 'nn' require 'fast_neural_style.GramMatrix' local StyleLoss, parent = torch.class('nn.StyleLoss', 'nn.Module') function StyleLoss:__init(strength, loss_type, agg_type) parent.__init(self) self.agg_type = agg_type or 'gram' self.strength = strength or 1.0 self.loss = 0 self.target = torch.Tensor() if self.agg_type == 'gram' then self.agg = nn.GramMatrix() elseif self.agg_type == 'mean' then self.agg = nn.Sequential() self.agg:add(nn.Mean(3)) self.agg:add(nn.Mean(3)) else error('Unsupported aggregation type ' .. agg_type) end self.agg_out = nil self.mode = 'none' loss_type = loss_type or 'L2' if loss_type == 'L2' then self.crit = nn.MSECriterion() elseif loss_type == 'SmoothL1' then self.crit = nn.SmoothL1Criterion() else error(string.format('invalid loss type "%s"', loss_type)) end end function StyleLoss:updateOutput(input) self.agg_out = self.agg:forward(input) if self.mode == 'capture' then self.target:resizeAs(self.agg_out):copy(self.agg_out) elseif self.mode == 'loss' then local target = self.target if self.agg_out:size(1) > 1 and self.target:size(1) == 1 then -- Handle minibatch inputs target = target:expandAs(self.agg_out) end self.loss = self.strength * self.crit(self.agg_out, target) self._target = target end self.output = input return self.output end function StyleLoss:updateGradInput(input, gradOutput) if self.mode == 'capture' or self.mode == 'none' then self.gradInput = gradOutput elseif self.mode == 'loss' then self.crit:backward(self.agg_out, self._target) self.crit.gradInput:mul(self.strength) self.agg:backward(input, self.crit.gradInput) self.gradInput:add(self.agg.gradInput, gradOutput) end return self.gradInput end function StyleLoss:setMode(mode) if mode ~= 'capture' and mode ~= 'loss' and mode ~= 'none' then error(string.format('Invalid mode "%s"', mode)) end self.mode = mode end