diff --git a/REQUIRE b/REQUIRE index 13fcb27..ed5812b 100644 --- a/REQUIRE +++ b/REQUIRE @@ -3,4 +3,5 @@ LinearOperators MAT PkgDev JLD -BenchmarkTools \ No newline at end of file +BenchmarkTools +DistributedArrays diff --git a/benchmarks/CIFAR10/cifar10_512_64.jl b/benchmarks/CIFAR10/cifar10_512_64.jl index 2c444f9..418dbc8 100644 --- a/benchmarks/CIFAR10/cifar10_512_64.jl +++ b/benchmarks/CIFAR10/cifar10_512_64.jl @@ -67,7 +67,7 @@ pLoss = getSoftMaxLoss(TYPE); objFun = dnnObjFctn(net,pLoss,pRegTh,pRegW) opt = getSGDsolver(TYPE,learningRate=1e-2,maxEpochs=1,miniBatch=miniBatchSize,out=true) -W = 0.1*vec(randn(TYPE,10,nFeatOut(net)+1)); +W = 0.01*vec(randn(TYPE,10,nFeatOut(net)+1)); W = min.(W,.2); W = max.(W,-.2); W = convert(Array{TYPE},W); diff --git a/benchmarks/micro/bm_batchnorm.jl b/benchmarks/micro/bm_batchnorm.jl index e38f39d..a2fa7f7 100644 --- a/benchmarks/micro/bm_batchnorm.jl +++ b/benchmarks/micro/bm_batchnorm.jl @@ -43,7 +43,8 @@ end function benchmarkApply(L, theta, Y, history) funcName = "apply" - Yout2,Yout2,tmp2 = apply(L,theta,Y,true) + Q = copy(Y) + Yout,Yout,tmp = apply(L,theta,Q,true) @code_warntype apply(L,theta,Y,true) @@ -52,4 +53,6 @@ function benchmarkApply(L, theta, Y, history) Meganet.updatehistory!(history, trial, "hist") hist = JLD.load(history, "hist") judge(hist) + + return trial end diff --git a/benchmarks/micro/bm_batchnorm.jld b/benchmarks/micro/bm_batchnorm.jld index 1f54d19..b449d91 100644 Binary files a/benchmarks/micro/bm_batchnorm.jld and b/benchmarks/micro/bm_batchnorm.jld differ diff --git a/examples/EResNN_CIFAR10.jl b/examples/EResNN_CIFAR10.jl index 9afcbbc..83e9b75 100644 --- a/examples/EResNN_CIFAR10.jl +++ b/examples/EResNN_CIFAR10.jl @@ -1,6 +1,6 @@ using MAT, Meganet - -n = 256; +# BLAS.set_num_threads(1) +n = 512; Y_train,C_train,Y_test,C_test = getCIFAR10(n,Pkg.dir("Meganet")*"/data/CIFAR10/"); # using PyPlot @@ -72,7 +72,7 @@ pRegTh = getTikhonovReg(TYPE;alpha=4e-4) pRegW = getTikhonovReg(TYPE;alpha=4e-4) pLoss = getSoftMaxLoss(TYPE); objFun = dnnObjFctn(net,pLoss,pRegTh,pRegW) -opt = getSGDsolver(TYPE,learningRate=1e-2,maxEpochs=1,miniBatch=miniBatchSize,out=true) +opt = getSGDsolver(TYPE,learningRate=1e-2,maxEpochs=1,miniBatch=miniBatchSize,out=true, nesterov=true) W = 0.1*vec(randn(TYPE,10,nFeatOut(net)+1)); W = min.(W,.2); diff --git a/src/Meganet.jl b/src/Meganet.jl index 3e63a76..8955351 100644 --- a/src/Meganet.jl +++ b/src/Meganet.jl @@ -17,6 +17,7 @@ include("kernelTypes/sparseKernel.jl") include("kernelTypes/convFFTKernel.jl"); include("kernelTypes/convGEMMKernel.jl"); include("kernelTypes/convCircKernel.jl"); +# include("kernelTypes/convDiagKernel.jl"); diff --git a/src/activations/identityActivation.jl b/src/activations/identityActivation.jl index 0c11e88..c84edcc 100644 --- a/src/activations/identityActivation.jl +++ b/src/activations/identityActivation.jl @@ -17,13 +17,15 @@ export identityActivation A - activation dA - derivatives """ -function identityActivation(Y::Array{T},doDerivative::Bool=false) where {T} +function identityActivation(Y::Array{T},dA,doDerivative::Bool=false) where {T} if doDerivative - dA = ones(T,Y); -else - dA = zeros(T,0) + if isempty(dA) + dA = ones(T,Y); + else + dA .= ones(T,Y); + end end -return A,dA +return A,dA #Depricated? A Isnt even declared lol end diff --git a/src/activations/reluActivation.jl b/src/activations/reluActivation.jl index cba57df..680c19f 100644 --- a/src/activations/reluActivation.jl +++ b/src/activations/reluActivation.jl @@ -1,31 +1,45 @@ -export reluActivation - -""" - relu activation A = relu(Y) - - Input: - - Y - array of features - - Optional Input: - - doDerivative - flag for computing derivative, set via varargin - Ex: reluActivation(Y,true); - - Output: - - A - activation - dA - derivatives -""" -function reluActivation(Y::Array{T},doDerivative::Bool=false) where {T} - -Y = max.(Y,0); - -if doDerivative - dA = sign(Y); -else - dA = zeros(T,0) -end - -return A,dA -end +export reluActivation, reluActivation! + +""" + relu activation A = relu(Y) + + Input: + + Y - array of features + + Optional Input: + + doDerivative - flag for computing derivative, set via varargin + Ex: reluActivation(Y,true); + + Output: + + A - activation + dA - derivatives +""" +function reluActivation(Y::Array{T},doDerivative::Bool=false) where {T} + + A = max.(Y,zero(T)); + + if doDerivative + dA = sign.(A); + else + dA = zeros(T,0) + end + + return A,dA +end + + + +function reluActivation!(A::Array{T},dA,doDerivative::Bool=false) where {T} + A .= max.(A,zero(T)); + if doDerivative + if isempty(dA) + dA = sign.(A); + else + dA .= sign.(A); + end + end + return A,dA +end diff --git a/src/activations/tanhActivation.jl b/src/activations/tanhActivation.jl index 06af3de..aa491dc 100644 --- a/src/activations/tanhActivation.jl +++ b/src/activations/tanhActivation.jl @@ -1,4 +1,4 @@ -export tanhActivation +export tanhActivation, tanhActivation! """ hyperbolic tan activation A = tanh(Y) @@ -19,7 +19,6 @@ export tanhActivation """ function tanhActivation(Y::Array{T,2},doDerivative::Bool=false) where {T <: Number} - A = tanh.(Y) dA = zeros(A) if doDerivative @@ -27,3 +26,16 @@ function tanhActivation(Y::Array{T,2},doDerivative::Bool=false) where {T <: Numb end return A, dA end + +function tanhActivation!(A::Array{T,2},dA=[],doDerivative::Bool=false) where {T <: Number} + + A .= tanh.(A) + if doDerivative + if isempty(dA) + dA = one(T) .- A.^2 + else + dA .= one(T) .- A.^2 + end + end + return A, dA +end diff --git a/src/integrators/NN.jl b/src/integrators/NN.jl index 9484a7e..65efde4 100644 --- a/src/integrators/NN.jl +++ b/src/integrators/NN.jl @@ -1,5 +1,6 @@ export NN,getNN,initTheta - +# using TimerOutputs +# to = TimerOutput() """ NN Neural Network block @@ -62,31 +63,47 @@ end # --------- forward problem ---------- -function apply(this::NN{T},theta::Array{T},Y0::Array{T,2},doDerivative=true) where {T<:Number} - Y::Array{T,2} = copy(Y0) +function apply(this::NN{T},theta::Array{T},Y::Array{T,2},tmp,doDerivative=true) where {T<:Number} + nex = div(length(Y),nFeatIn(this))::Int nt = length(this.layers) - tmp = Array{Any}(nt+1,2) + if isempty(tmp) #TODO Will have to make sure size of Y doesnt change + tmp = Array{Any}(nt+1,2) + end + if doDerivative - tmp[1,1] = Y0 + if isassigned(tmp,1,1) + #tmp[1,1] .= Y This does not work, need to hack like below :) + tmp11 = tmp[1,1] + tmp11 .= Y + else + tmp[1,1] = copy(Y) + end end Ydata::Array{T,2} = zeros(T,0,nex) cnt = 0 for i=1:nt ni = nTheta(this.layers[i])::Int + if !isassigned(tmp,i,2) + tmp[i,2] = Array{Any}(0) + end + Yd::Array{T,2}, Y, tmp[i,2] = apply(this.layers[i],theta[cnt+(1:ni)],Y,tmp[i,2],doDerivative) - Yd::Array{T,2}, Y, tmp[i,2] = apply(this.layers[i],theta[cnt+(1:ni)],Y,doDerivative) if this.outTimes[i]==1 Ydata = [Ydata; this.Q*Yd] end if doDerivative - tmp[i+1,1] = copy(Y) + if isassigned(tmp,i+1,1) + tmp1 = tmp[i+1,1] + tmp1 .= Y + else + tmp[i+1,1] = copy(Y) + end end cnt = cnt + ni end - return Ydata,Y,tmp end diff --git a/src/integrators/ResNN.jl b/src/integrators/ResNN.jl index 86f43e9..63a199f 100644 --- a/src/integrators/ResNN.jl +++ b/src/integrators/ResNN.jl @@ -48,27 +48,46 @@ function initTheta(this::ResNN{T}) where {T<:Number} end # ------- apply forward problems ----------- -function apply(this::ResNN{T},theta_in::Array{T},Y0::Array{T},doDerivative=true) where {T<:Number} +function apply(this::ResNN{T},theta_in::Array{T},Y0::Array{T},tmp,doDerivative=true) where {T<:Number} + if isempty(tmp) + tmp = Array{Any}(this.nt+1,2) + end - nex = div(length(Y0),nFeatIn(this)) - Y = reshape(Y0,:,nex) - tmp = Array{Any}(this.nt+1,2) if doDerivative - tmp[1,1] = Y0 + if isassigned(tmp,1,1) + tmp11 = tmp[1,1] + tmp11 .= Y0 + else + tmp[1,1] = copy(Y0) + end end + nex = div(length(Y0),nFeatIn(this)) + Y = reshape(Y0,:,nex) + + theta = reshape(theta_in,:,this.nt) Ydata::Array{T,2} = zeros(T,0,nex) for i=1:this.nt - Z,dummy,tmp[i,2] = apply(this.layer,theta[:,i],Y,doDerivative) - Y += this.h * Z - if doDerivative - tmp[i+1,1] = Y + if !isassigned(tmp,i,2) + tmp[i,2] = Array{Any}(0) end + Z,dummy,tmp[i,2] = apply(this.layer,theta[:,i],Y,tmp[i,2],doDerivative) + Y += this.h * Z if this.outTimes[i]==1 Ydata = [Ydata;this.Q*Y] end + + if doDerivative + if isassigned(tmp,i+1,1) + tmp1 = tmp[i+1,1] + tmp1 .= Y + else + tmp[i+1,1] = copy(Y) + end + end + end return Ydata,Y,tmp end diff --git a/src/integrators/batchNormNN.jl b/src/integrators/batchNormNN.jl index 7e1e789..83f1500 100644 --- a/src/integrators/batchNormNN.jl +++ b/src/integrators/batchNormNN.jl @@ -53,14 +53,21 @@ end # --------- forward problem ---------- -function apply(this::batchNormNN{T},theta::Array{T},Y0::Array{T,2},doDerivative=true) where {T<:Number} - Y::Array{T,2} = copy(Y0) +function apply(this::batchNormNN{T},theta::Array{T},Y::Array{T,2},tmp::Array,doDerivative=true) where {T<:Number} nex = div(length(Y),nFeatIn(this))::Int nt = length(this.layers) - tmp = Array{Any}(nt+1,2) + if isempty(tmp) #TODO Will have to make sure size of Y doesnt change + tmp = Array{Any}(nt+1,2) + end + if doDerivative - tmp[1,1] = Y0 + if isassigned(tmp,1,1) + tmp11 = tmp[1,1] + tmp11 .= Y + else + tmp[1,1] = copy(Y) + end end Ydata::Array{T,2} = zeros(T,0,nex) @@ -73,7 +80,12 @@ function apply(this::batchNormNN{T},theta::Array{T},Y0::Array{T,2},doDerivative= Ydata = [Ydata; this.Q*Yd] end if doDerivative - tmp[i+1,1] = copy(Y) + if isassigned(tmp,i+1,1) + tmp1 = tmp[i+1,1] + tmp1 .= Y + else + tmp[i+1,1] = copy(Y) + end end cnt = cnt + ni end diff --git a/src/integrators/connector.jl b/src/integrators/connector.jl index 5824642..cb81177 100644 --- a/src/integrators/connector.jl +++ b/src/integrators/connector.jl @@ -17,16 +17,24 @@ function getConnector(TYPE::Type, K; b = zero(TYPE),outTimes=0,Q=I) return Connector(K,b,outTimes,Q); end - -function apply(this::Connector{T},theta::Array{T},Y0::Array{T},doDerivative=true) where {T <: Number} +function apply(this::Connector{T},theta::Array{T},Y0::Array{T},tmp,doDerivative=true) where {T <: Number} nex = div(length(Y0),nFeatIn(this)) Y0 = reshape(Y0,:,nex) + + if doDerivative + if isempty(tmp) + tmp = copy(Y0) + else + tmp .= Y0 + end + end + Y = this.K*Y0 .+ this.b Ydata::Array{T,2} = Array{T, 2}(0, 0) # Temporary fix until we know what type Q is if this.outTimes==1 Ydata = this.Q*Y end - tmp = Y0; + return Ydata, Y, tmp end diff --git a/src/kernelTypes/abstractConvKernel.jl b/src/kernelTypes/abstractConvKernel.jl index 182a2ba..6e49529 100644 --- a/src/kernelTypes/abstractConvKernel.jl +++ b/src/kernelTypes/abstractConvKernel.jl @@ -1,50 +1,50 @@ -export nImgIn, nImgOut, nFeatIn, nFeatOut, nTheta, getOp, initTheta, AbstractConvKernel - -abstract type AbstractConvKernel{T} <: AbstractMeganetElement{T} end - -## All convKernel types are assumed to have fields nImage (size of the image) and sK (size of the Convolution Kernel) - -function nImgIn(this::AbstractConvKernel) - return [this.nImg[1]; this.nImg[2]; this.sK[3]] -end - -function nImgOut(this::AbstractConvKernel) - return [this.nImg[1]; this.nImg[2]; this.sK[4]] -end - -function nFeatIn(this::AbstractConvKernel) - return prod(nImgIn(this)); -end -function nFeatOut(this::AbstractConvKernel) - return prod(nImgOut(this)); -end - -function nTheta(this::AbstractConvKernel) - return prod(this.sK); -end - - -function getOp(this::AbstractConvKernel{T},theta::Array{T}) where {T <: Number} - - m = prod(nImgOut(this)) - n = prod(nImgIn(this)) - - A = LinearOperator{T}(m,n,false,false, - v -> Amv(this,theta,v), - Nullable{Function}(), - w -> ATmv(this,theta,w)) - return A -end - -function initTheta(this::AbstractConvKernel{T}) where {T <: Number} - - sd = T(0.1); - theta = sd*randn(T,prod(this.sK)); - #id1 = find(theta>2*sd); - #theta(id1[:]) = randn(numel(id1),1); - - #id2 = find(theta< -2*sd); - #theta(id2(:)) = randn(numel(id2),1); - #theta = max(min(2*sd, theta),-2*sd); - return theta -end +export nImgIn, nImgOut, nFeatIn, nFeatOut, nTheta, getOp, initTheta, AbstractConvKernel + +abstract type AbstractConvKernel{T} <: AbstractMeganetElement{T} end + +## All convKernel types are assumed to have fields nImage (size of the image) and sK (size of the Convolution Kernel) + +function nImgIn(this::AbstractConvKernel) + return [this.nImg[1]; this.nImg[2]; this.sK[3]] +end + +function nImgOut(this::AbstractConvKernel) + return [this.nImg[1]; this.nImg[2]; this.sK[4]] +end + +function nFeatIn(this::AbstractConvKernel) + return prod(nImgIn(this)); +end +function nFeatOut(this::AbstractConvKernel) + return prod(nImgOut(this)); +end + +function nTheta(this::AbstractConvKernel) + return prod(this.sK); +end + + +function getOp(this::AbstractConvKernel{T},theta::Array{T}) where {T <: Number} + + m = prod(nImgOut(this)) + n = prod(nImgIn(this)) + + A = LinearOperator{T}(m,n,false,false, + v -> Amv(this,theta,v), + Nullable{Function}(), + w -> ATmv(this,theta,w)) + return A +end + +function initTheta(this::AbstractConvKernel{T}) where {T <: Number} + + sd = T(0.01); + theta = sd*randn(T,prod(this.sK)); + #id1 = find(theta>2*sd); + #theta(id1[:]) = randn(numel(id1),1); + + #id2 = find(theta< -2*sd); + #theta(id2(:)) = randn(numel(id2),1); + #theta = max(min(2*sd, theta),-2*sd); + return theta +end diff --git a/src/kernelTypes/convFFTKernel.jl b/src/kernelTypes/convFFTKernel.jl index cfa480b..97f02d0 100644 --- a/src/kernelTypes/convFFTKernel.jl +++ b/src/kernelTypes/convFFTKernel.jl @@ -1,126 +1,126 @@ -export convFFTKernel, getEigs,getConvFFTKernel -## For the functions nImgIn, nImgOut, nFeatIn, nFeatOut, nTheta, getOp, initTheta : see AbstractConvKernel.jl -## All convKernel types are assumed to have fields nImage and sK -mutable struct convFFTKernel{T} <: AbstractConvKernel{T} - nImg :: Array{Int,1} - sK :: Array{Int,1} - S :: Array{Complex{T},2} -end - -function getConvFFTKernel(TYPE::Type,nImg,sK) - S = getEigs(Complex{TYPE},nImg,sK) - return convFFTKernel{TYPE}(nImg,sK,S) -end - -function getEigs(TYPE,nImg,sK) - S = zeros(TYPE,prod(nImg),prod(sK[1:2])); - for k=1:prod(sK[1:2]) - Kk = zeros(sK[1],sK[2]); - Kk[k] = 1; - Ak = getConvMatPeriodic(TYPE,Kk,[nImg[1],nImg[2], 1]); - Akk = full(convert(Array{TYPE},Ak[:,1])); - S[:,k] = vec(fft2(reshape(Akk,nImg[1],nImg[2]) )); - end - return S -end - -export Amv -function Amv(this::convFFTKernel{T},theta::Array{T},Y::Array{T}) where {T<:Number} - - nex = div(numel(Y),prod(nImgIn(this))) - - # compute convolution - AY = zeros(Complex{T},tuple([nImgOut(this); nex]...)); - theta = reshape(theta, tuple([prod(this.sK[1:2]); this.sK[3:4]]...)); - Yh = ifft2(reshape(Y,tuple([nImgIn(this); nex]...))); - #### allocate stuff for the loop - Sk = zeros(Complex{T},tuple(nImgOut(this)...)) - #T = zeros(Complex{eltype(Y)},tuple(nImgOut(this)...)) - nn = nImgOut(this); nn[3] = 1; - sumT = zeros(Complex{T},tuple([nn;nex]...)) - #### - - for k=1:this.sK[4] - Sk = reshape(this.S*theta[:,:,k],tuple(nImgIn(this)...)); - #T = Sk .* Yh; - #sumT = sum(T,3) - sumT = hadamardSum(sumT,Yh,Sk) - AY[:,:,k,:] = sumT[:,:,1,:]; - end - AY = real(fft2(AY)); - Y = reshape(AY,:,nex); - return Y -end - -function ATmv(this::convFFTKernel{T},theta::Array{T},Z::Array{T}) where {T<:Number} - - nex = div(numel(Z),prod(nImgOut(this))); - ATY = zeros(Complex{T},tuple([nImgIn(this); nex]...)); - theta = reshape(theta, prod(this.sK[1:2]),this.sK[3],this.sK[4]); - #### allocate stuff for the loop - Sk = zeros(Complex{T},tuple(nImgOut(this)...)) - #T = zeros(Complex{eltype(Z)},tuple(nImgOut(this)...)) - nn = nImgOut(this); nn[3] = 1; - sumT = zeros(Complex{T},tuple([nn;nex]...)) - #### - - Yh = fft2(reshape(Z,tuple([nImgOut(this); nex]...))); - for k=1:this.sK[3] - tk = theta[:,k,:] - #if size(this.S,2) == 1 - # tk = reshape(tk,1,:); - #end - Sk = reshape(this.S*tk,tuple(nImgOut(this)...)); - #T = Sk.*Yh; - #sumT = sum(T,3) - sumT = hadamardSum(sumT,Yh,Sk) - ATY[:,:,k] = sumT[:,:,1]; - end - ATY = real(ifft2(ATY)); - ATY = reshape(ATY,:,nex); - return ATY -end - -function Jthetamv(this::convFFTKernel{T},dtheta::Array{T},dummy::Array{T},Y::Array{T},temp=nothing) where {T<:Number} - - nex = div(numel(Y),nFeatIn(this)); - Y = reshape(Y,:,nex); - Z = Amv(this,dtheta,Y); - return Z -end - -function JthetaTmv(this::convFFTKernel{T},Z::Array{T},dummy::Array{T},Y::Array{T}) where {T<:Number} - # derivative of Z*(A(theta)*Y) w.r.t. theta - - nex = div(numel(Y),nFeatIn(this)); - - dth1 = zeros(this.sK[1]*this.sK[2],this.sK[3],this.sK[4]); - Y = permutedims(reshape(Y,tuple([nImgIn(this); nex ]...)),[1 2 4 3]); - Yh = reshape(fft2(Y),prod(this.nImg[1:2]),nex*this.sK[3]); - Zh = permutedims(ifft2(reshape(Z,tuple([nImgOut(this); nex]...))),[1 2 4 3]); - Zh = reshape(Zh,:, this.sK[4]); - - for k=1:prod(this.sK[1:2]) - temp = conj(this.S[:,k]) .* Yh - temp = reshape(temp,:,this.sK[3]) - dth1[k,:,:] = real(conj(temp)'*Zh); - end - - dtheta = reshape(dth1,tuple(this.sK...)); - return dtheta -end - -function hadamardSum(sumT::Array{T},Yh::Array{T},Sk::Array{T}) where {T<:Number} - sumT .= 0.0; - for i4 = 1:size(Yh,4) - for i3 = 1:size(Yh,3) - for i2 = 1:size(Yh,2) - for i1 = 1:size(Yh,1) - @inbounds tt = Sk[i1,i2,i3] - @inbounds sumT[i1,i2,1,i4] += tt * Yh[i1,i2,i3,i4] - end - end - end - end - return sumT -end +export convFFTKernel, getEigs,getConvFFTKernel +## For the functions nImgIn, nImgOut, nFeatIn, nFeatOut, nTheta, getOp, initTheta : see AbstractConvKernel.jl +## All convKernel types are assumed to have fields nImage and sK +mutable struct convFFTKernel{T} <: AbstractConvKernel{T} + nImg :: Array{Int,1} + sK :: Array{Int,1} + S :: Array{Complex{T},2} +end + +function getConvFFTKernel(TYPE::Type,nImg,sK) + S = getEigs(Complex{TYPE},nImg,sK) + return convFFTKernel{TYPE}(nImg,sK,S) +end + +function getEigs(TYPE,nImg,sK) + S = zeros(TYPE,prod(nImg),prod(sK[1:2])); + for k=1:prod(sK[1:2]) + Kk = zeros(sK[1],sK[2]); + Kk[k] = 1; + Ak = getConvMatPeriodic(TYPE,Kk,[nImg[1],nImg[2], 1]); + Akk = full(convert(Array{TYPE},Ak[:,1])); + S[:,k] = vec(fft2(reshape(Akk,nImg[1],nImg[2]) )); + end + return S +end + +export Amv +function Amv(this::convFFTKernel{T},theta::Array{T},Y::Array{T}) where {T<:Number} + + nex = div(numel(Y),prod(nImgIn(this))) + + # compute convolution + AY = zeros(Complex{T},tuple([nImgOut(this); nex]...)); + theta = reshape(theta, tuple([prod(this.sK[1:2]); this.sK[3:4]]...)); + Yh = ifft2(reshape(Y,tuple([nImgIn(this); nex]...))); + #### allocate stuff for the loop + Sk = zeros(Complex{T},tuple(nImgOut(this)...)) + #T = zeros(Complex{eltype(Y)},tuple(nImgOut(this)...)) + nn = nImgOut(this); nn[3] = 1; + sumT = zeros(Complex{T},tuple([nn;nex]...)) + #### + + for k=1:this.sK[4] + Sk = reshape(this.S*theta[:,:,k],tuple(nImgIn(this)...)); + #T = Sk .* Yh; + #sumT = sum(T,3) + sumT = hadamardSum(sumT,Yh,Sk) + AY[:,:,k,:] = sumT[:,:,1,:]; + end + AY = real(fft2(AY)); + Y = reshape(AY,:,nex); + return Y +end + +function ATmv(this::convFFTKernel{T},theta::Array{T},Z::Array{T}) where {T<:Number} + + nex = div(numel(Z),prod(nImgOut(this))); + ATY = zeros(Complex{T},tuple([nImgIn(this); nex]...)); + theta = reshape(theta, prod(this.sK[1:2]),this.sK[3],this.sK[4]); + #### allocate stuff for the loop + Sk = zeros(Complex{T},tuple(nImgOut(this)...)) + #T = zeros(Complex{eltype(Z)},tuple(nImgOut(this)...)) + nn = nImgOut(this); nn[3] = 1; + sumT = zeros(Complex{T},tuple([nn;nex]...)) + #### + + Yh = fft2(reshape(Z,tuple([nImgOut(this); nex]...))); + for k=1:this.sK[3] + tk = theta[:,k,:] + #if size(this.S,2) == 1 + # tk = reshape(tk,1,:); + #end + Sk = reshape(this.S*tk,tuple(nImgOut(this)...)); + #T = Sk.*Yh; + #sumT = sum(T,3) + sumT = hadamardSum(sumT,Yh,Sk) + ATY[:,:,k,:] = sumT[:,:,1,:]; + end + ATY = real(ifft2(ATY)); + ATY = reshape(ATY,:,nex); + return ATY +end + +function Jthetamv(this::convFFTKernel{T},dtheta::Array{T},dummy::Array{T},Y::Array{T},temp=nothing) where {T<:Number} + + nex = div(numel(Y),nFeatIn(this)); + Y = reshape(Y,:,nex); + Z = Amv(this,dtheta,Y); + return Z +end + +function JthetaTmv(this::convFFTKernel{T},Z::Array{T},dummy::Array{T},Y::Array{T}) where {T<:Number} + # derivative of Z*(A(theta)*Y) w.r.t. theta + + nex = div(numel(Y),nFeatIn(this)); + + dth1 = zeros(this.sK[1]*this.sK[2],this.sK[3],this.sK[4]); + Y = permutedims(reshape(Y,tuple([nImgIn(this); nex ]...)),[1 2 4 3]); + Yh = reshape(fft2(Y),prod(this.nImg[1:2]),nex*this.sK[3]); + Zh = permutedims(ifft2(reshape(Z,tuple([nImgOut(this); nex]...))),[1 2 4 3]); + Zh = reshape(Zh,:, this.sK[4]); + + for k=1:prod(this.sK[1:2]) + temp = conj(this.S[:,k]) .* Yh + temp = reshape(temp,:,this.sK[3]) + dth1[k,:,:] = real(conj(temp)'*Zh); + end + + dtheta = reshape(dth1,tuple(this.sK...)); + return dtheta +end + +function hadamardSum(sumT::Array{T},Yh::Array{T},Sk::Array{T}) where {T<:Number} + sumT .= 0.0; + for i4 = 1:size(Yh,4) + for i3 = 1:size(Yh,3) + for i2 = 1:size(Yh,2) + for i1 = 1:size(Yh,1) + @inbounds tt = Sk[i1,i2,i3] + @inbounds sumT[i1,i2,1,i4] += tt * Yh[i1,i2,i3,i4] + end + end + end + end + return sumT +end diff --git a/src/kernelTypes/convGEMMKernel.jl b/src/kernelTypes/convGEMMKernel.jl index 09c68b4..e22bc70 100644 --- a/src/kernelTypes/convGEMMKernel.jl +++ b/src/kernelTypes/convGEMMKernel.jl @@ -1,200 +1,258 @@ -export convGEMMKernel,Amv,ATmv,transposeTest,getConvGEMMKernel - -mutable struct convGEMMKernel{T} <: AbstractConvKernel{T} - nImg :: Array{Int,1} - sK :: Array{Int,1} -end -function getConvGEMMKernel(TYPE::Type,nImg,sK) - return convGEMMKernel{TYPE}(copy(nImg),copy(sK)); -end - -function Amv(this::convGEMMKernel{T},theta::Array{T},Y::Array{T}) where {T<:Number} - ## We assume that the data Y is held in the order XYCN. - sK = this.sK; - nImg = this.nImg; - nex = div(numel(Y),prod(nImgIn(this))) - # compute convolution - Y = reshape(Y,nImg[1],nImg[2],this.sK[3],nex); - AY = Array{T, 3}(nImg[1]*nImg[2],this.sK[4],nex); - aux = zeros(T,nImg[1],nImg[2],this.sK[3]); - AYk = zeros(T,nImg[1]*nImg[2],this.sK[4]); - ### reshape the kernels for gemm!: - K = reshape(theta, sK[1], sK[2], sK[3], sK[4]) - KK = Array{Array{T,2}}(sK[1],sK[2]); - for k1 = 1:sK[1] - for k2 = 1:sK[2] - @inbounds KK[k1,k2] = K[k1,k2,:,:]'; - end - end - shiftX = [0;-1;0;0;1;0]; - shiftT = [1;0;0;0;0;-1]; - - for k = 1:nex - AYk = multConv2Dblock(Y,KK, AYk,aux,shiftX,shiftT,k); - @inbounds AY[:,:,k] = AYk; - AYk[:] = zero(T) - end - AY_out = reshape(AY,:,nex); - return AY_out -end - -function ATmv(this::convGEMMKernel{T},theta::Array{T},Zin::Array{T}) where {T<:Number} - nImg = this.nImg; - sK = this.sK; - nex = div(numel(Zin),prod(nImgOut(this))); - K = reshape(theta, sK[1], sK[2], sK[3], sK[4]); - Z = reshape(Zin,nImg[1],nImg[2],sK[4],nex); - aux = zeros(T,nImg[1],nImg[2],sK[4]); - ATZ = zeros(T,nImg[1]*nImg[2],sK[3],nex); - ATZk = zeros(T,nImg[1]*nImg[2],sK[3]); - - ### reshape the kernels for gemm!: - KK = Array{Array{T,2}}(sK[1],sK[2]); - for k1 = 1:sK[1] - for k2 = 1:sK[2] - @inbounds KK[k1,k2] = K[k1,k2,:,:]; - end - end - ## flipping: - KK = flipdim(flipdim(KK,2),1); - shiftX = [0;-1;0;0;1;0]; - shiftT = [1;0;0;0;0;-1]; - for k = 1:nex - ATZk = multConv2Dblock(Z,KK, ATZk,aux,shiftX,shiftT,k); - @inbounds ATZ[:,:,k] = ATZk; - ATZk[:] = zero(T) - end - ATZ_out = reshape(ATZ,:,nex); - return ATZ_out -end - -function Jthetamv(this::convGEMMKernel{T},dtheta::Array{T},dummy::Array{T},Y::Array{T},temp=nothing) where {T<:Number} - nex = div(numel(Y),nFeatIn(this)); - Z = Amv(this,dtheta,Y); - return Z -end - -function JthetaTmv(this::convGEMMKernel{T}, Zin::Array{T}, dummy::Array{T}, Yin::Array{T}) where {T<:Number} - # derivative of Z*(A(theta)*Y) w.r.t. theta - sK = this.sK - nImg = this.nImg - nex = div(numel(Yin),prod(nImgIn(this))) - # compute convolution - Y = reshape(Yin, nImg[1], nImg[2], this.sK[3], nex) - Z = reshape(Zin, nImg[1]*nImg[2], this.sK[4], nex) - Zk = zeros(T, nImg[1]*nImg[2], this.sK[4]) - aux = zeros(T, nImg[1], nImg[2], this.sK[3]) - - ### reshape the kernels for gemm!: - dtheta = zeros(T, sK[1], sK[2], sK[3], sK[4]) - KK = Array{Array{T, 2}}(sK[1], sK[2]) - for k1 = 1:sK[1] - for k2 = 1:sK[2] - @inbounds KK[k1, k2] = zeros(T, sK[3], sK[4]) - end - end - shiftX = [0;-1;0;0;1;0] - shiftT = [1;0;0;0;0;-1] - for k = 1:nex - getColumn!(Z, Zk, k) - multConv2Dblock(Y, KK, Zk, aux, shiftX, shiftT, k, doDerivative = 1) - end - ### Assemble the kernels from gemm!: - for k1 = 1:sK[1] - for k2 = 1:sK[2] - @inbounds dtheta[k1, k2, :, :] = KK[k1, k2] - end - end - dtheta_out = reshape(dtheta, sK[1], sK[2], sK[3], sK[4]) - return dtheta_out -end - - - -function getColumn!(Z::Array{T},Zk::Array{T},k::Int64) where {T<:Number} -for c=1:size(Z,2) - for j=1:size(Z,1) - @inbounds Zk[j,c] = Z[j,c,k]; - end -end -end - -function multConv2Dblock(x::Array{T},K::Array{Array{T,2},2}, y::Array{T}, tin::Array{T},shiftX,shiftT,imIdx;doDerivative = 0) where {T<:Number} - ## y = K*x - ## K - 3X3 array of Arrays - ## x - a vector of length |nImgag+2|*cin (zero padded) - ## y - a vector of length |nImgag|*cout - - nImg1 = size(x,1); - nImg2 = size(x,2); - cin = size(x,3); - cout = size(y,2); - OneType = one(T); - t = reshape(tin,nImg1,nImg2,cin); - kernelWidth = size(K,1); - # y = reshape(y,nImg1*nImg2,cout); # it is supposed to be of this shape... - k=1; - jt=0;it=0;jt=0;jx=0; - for p = 1:2:2*kernelWidth - for q = 1:2:2*kernelWidth - lower = nImg2+shiftT[p+1] # Move outside of the forloop for increased speed - upper = nImg1+shiftT[q+1] # Move outside of the forloop for increased speed - for cc = 1:cin - jx = 1+shiftX[p]; # Moving these outside didn't seem to help - jt = 1+shiftT[p]; - if jt > 1 - @inbounds t[:,1:(jt-1),cc] = 0.0; - end - while jt <= lower - it = 1+shiftT[q]; - ix = 1+shiftX[q]; - if it > 1 - for ii = 1:(it-1) - @inbounds t[ii,jt,cc] = zero(T) #@inbounds t[1:(it-1),jt,cc] = 0.0 - faster unvectorized - end - end - while it <= upper - @inbounds t[it,jt,cc] = x[ix,jx,cc,imIdx]; - it+=1;ix+=1; - end - if it <= nImg1 - for ii = it:nImg1 - @inbounds t[ii,jt,cc] = zero(T) #@inbounds t[it:nImg1,jt,cc] = 0.0 - faster unvectorized - end - end - jt+=1;jx+=1; - - end - if jt <= nImg2 - @inbounds t[:,jt:nImg2,cc] = 0.0; - end - end - if doDerivative == 0 - BLAS.gemm!('N','T',OneType,reshape(t,nImg1*nImg2,cin),K[k],OneType,y); - else - BLAS.gemm!('T','N',OneType,reshape(t,nImg1*nImg2,cin),y,OneType,K[k]); - end - k+=1; - end - end - return y; -end - - -# function transposeTest() -# nImage = [16,16]; -# sK = [3,3,2,4]; -# TYPE = Float64; -# K = randn(TYPE,tuple(sK...)); -# Y = randn(TYPE,nImage[1],nImage[2],sK[3],2); -# Z = randn(TYPE,nImage[1],nImage[2],sK[4],2); -# Kernel2 = convGEMMKernel(nImage,sK); -# AY = Amv(Kernel2,K,Y); -# ATZ = ATmv(Kernel2,K,Z); -# println(vecdot(Z,AY)); -# println(vecdot(ATZ,Y)); -# -# println(vecdot(Z,Jthetamv(Kernel2,K,[],Y))); -# println(vecdot(K,JthetaTmv(Kernel2,Z,[],Y))); -# -# end +export convGEMMKernel,Amv,ATmv,transposeTest,getConvGEMMKernel +using DistributedArrays +mutable struct convGEMMKernel{T} <: AbstractConvKernel{T} + nImg :: Array{Int,1} + sK :: Array{Int,1} + shiftX :: Array{Int,1} + shiftT :: Array{Int,1} + aux_sk3 :: Array{T, 3} + aux_sk4 :: Array{T, 3} + KK :: Array{Array{T,2}} + + prev_nex_AMV :: Int + AY :: Array{T,3} + prev_nex_ATmv :: Int + ATZ :: Array{T,3} + dtheta :: Array{T,4} + + +end +function getConvGEMMKernel(TYPE::Type,nImg,sK) + + if sK[1] == 1 && sK[2] == 1 + shiftX = [0;0]; + shiftT = [0;0]; + elseif sK[1] == 3 && sK[2] == 3 + shiftX = [0;-1;0;0;1;0]; + shiftT = [1;0;0;0;0;-1]; + else + error("Code only supports 1X1 and 3X3 convolutions"); + end + + aux_sk3 = zeros(TYPE,nImg[1],nImg[2],sK[3]); + aux_sk4 = zeros(TYPE,nImg[1],nImg[2],sK[4]); + + KK = Array{Array{TYPE,2}}(sK[1],sK[2]); + prev_nex_AMV = -1 + AY = zeros(TYPE,0,0,0) + prev_nex_ATmv = -1 + ATZ = zeros(TYPE,0,0,0) + dtheta = zeros(TYPE, sK[1], sK[2], sK[3], sK[4]) + + return convGEMMKernel{TYPE}(copy(nImg),copy(sK),shiftX,shiftT,aux_sk3,aux_sk4,KK,prev_nex_AMV,AY,prev_nex_ATmv,ATZ,dtheta); +end + +function Amv(this::convGEMMKernel{T},theta::Array{T},Y::Array{T}) where {T<:Number} + ## We assume that the data Y is held in the order XYCN. + + sK = this.sK; + nImg = this.nImg; + nex = div(numel(Y),prod(nImgIn(this))) + KK = this.KK + + # compute convolution + Y = reshape(Y,nImg[1],nImg[2],this.sK[3],nex); + + if nex != this.prev_nex_AMV + this.AY = Array{T, 3}(nImg[1]*nImg[2],this.sK[4],nex); + end + + aux = this.aux_sk3; + AYk = reshape(this.aux_sk4,nImg[1]*nImg[2],sK[4]); + ### reshape the kernels for gemm!: + K = reshape(theta, sK[1], sK[2], sK[3], sK[4]) + + for k1 = 1:sK[1] + for k2 = 1:sK[2] + @inbounds KK[k1,k2] = K[k1,k2,:,:]'; + end + end + + for k = 1:nex + AYk[:] = zero(T) + AYk = multConv2Dblock(Y,KK, AYk,aux,this.shiftX,this.shiftT,k); + @inbounds this.AY[:,:,k] = AYk; + end + AY_out = reshape(this.AY,:,nex) + this.prev_nex_AMV = nex + + return AY_out +end + +function ATmv(this::convGEMMKernel{T},theta::Array{T},Zin::Array{T}) where {T<:Number} + + nImg = this.nImg; + sK = this.sK; + nex = div(numel(Zin),prod(nImgOut(this))); + K = reshape(theta, sK[1], sK[2], sK[3], sK[4]); + Z = reshape(Zin,nImg[1],nImg[2],sK[4],nex); + aux = this.aux_sk4; + # ATZ = this.ATZ + ATZk = reshape(this.aux_sk3,nImg[1]*nImg[2],sK[3]); + + if nex != this.prev_nex_ATmv + this.ATZ = zeros(T,nImg[1]*nImg[2],sK[3],nex); + end + + ### reshape the kernels for gemm!: + KK = this.KK + + for k1 = 1:sK[1] + for k2 = 1:sK[2] + @inbounds KK[k1,k2] = K[k1,k2,:,:]; + end + end + ## flipping: + KK = flipdim(flipdim(KK,2),1); + for k = 1:nex + ATZk[:] = zero(T) + ATZk = multConv2Dblock(Z,KK, ATZk,aux,this.shiftX,this.shiftT,k); + @inbounds this.ATZ[:,:,k] = ATZk; + end + ATZ_out = reshape(this.ATZ,:,nex); + this.prev_nex_ATmv = nex + + return ATZ_out +end + +function Jthetamv(this::convGEMMKernel{T},dtheta::Array{T},dummy::Array{T},Y::Array{T},temp=nothing) where {T<:Number} + nex = div(numel(Y),nFeatIn(this)); + Z = Amv(this,dtheta,Y); + return Z +end + +function JthetaTmv(this::convGEMMKernel{T}, Zin::Array{T}, dummy::Array{T}, Yin::Array{T}) where {T<:Number} + # derivative of Z*(A(theta)*Y) w.r.t. theta + sK = this.sK + nImg = this.nImg + nex = div(numel(Yin),prod(nImgIn(this))) + # compute convolution + Y = reshape(Yin, nImg[1], nImg[2], this.sK[3], nex) + Z = reshape(Zin, nImg[1]*nImg[2], this.sK[4], nex) + Zk = reshape(this.aux_sk4, nImg[1]*nImg[2], this.sK[4]); + aux = this.aux_sk3; + dtheta = zeros(T, sK[1], sK[2], sK[3], sK[4]) + ### reshape the kernels for gemm!: + KK = this.KK + for k1 = 1:sK[1] + for k2 = 1:sK[2] + @inbounds KK[k1, k2] = zeros(T, sK[3], sK[4]) + end + end + for k = 1:nex + getColumn!(Z, Zk, k) + multConv2Dblock(Y, KK, Zk, aux, this.shiftX, this.shiftT, k, doDerivative = 1) + end + ### Assemble the kernels from gemm!: + for k1 = 1:sK[1] + for k2 = 1:sK[2] + @inbounds dtheta[k1, k2, :, :] = KK[k1, k2] + end + end + dtheta_out = reshape(dtheta, sK[1], sK[2], sK[3], sK[4]) + return dtheta_out +end + +function getColumn!(Z::Array{T},Zk::Array{T},k::Int64) where {T<:Number} +for c=1:size(Z,2) + for j=1:size(Z,1) + @inbounds Zk[j,c] = Z[j,c,k]; + end +end +end + +function multConv2Dblock(x::Array{T},K::Array{Array{T,2},2}, y::Array{T}, tin::Array{T},shiftX,shiftT,imIdx;doDerivative = 0) where {T<:Number} + ## y = K*x + ## K - 3X3 array of Arrays + ## x - a vector of length |nImgag+2|*cin (zero padded) + ## y - a vector of length |nImgag|*cout + nImg1 = size(x,1); + nImg2 = size(x,2); + cin = size(x,3); + cout = size(y,2); + OneType = one(T); + t = reshape(tin,nImg1,nImg2,cin); + kernelWidth = size(K,1); + # y = reshape(y,nImg1*nImg2,cout); # it is supposed to be of this shape... + k=1; + jt=0;it=0;jt=0;jx=0; + + for p = 1:2:2*kernelWidth + for q = 1:2:2*kernelWidth + lower = nImg2+shiftT[p+1] # Move outside of the forloop for increased speed + upper = nImg1+shiftT[q+1] # Move outside of the forloop for increased speed + for cc = 1:cin + jx = 1+shiftX[p]; # Moving these outside didn't seem to help + jt = 1+shiftT[p]; + if jt > 1 + ###################### Dirichlet ####################### + @inbounds t[:,1:(jt-1),cc] = zero(T); + ###################### Periodic ####################### + # ix = 1+shiftX[q]; + # if shiftT[q] > 0 + #@inbounds t[1,1,cc] = x[end,end,cc,imIdx]; + # end + # for it = (1+shiftT[q]):upper + #@inbounds t[it,1,cc] = x[ix,end,cc,imIdx]; + # ix +=1; + # end + # if shiftT[q+1] < 0 + #@inbounds t[end,1,cc] = x[1,end,cc,imIdx]; + # end + ###################### End Periodic ####################### + end + while jt <= lower + it = 1+shiftT[q]; + ix = 1+shiftX[q]; + if it > 1 + for ii = 1:(it-1) + ###################### Dirichlet ####################### + @inbounds t[ii,jt,cc] = zero(T) #@inbounds t[1:(it-1),jt,cc] = 0.0 - faster unvectorized + ###################### Periodic ####################### + #@inbounds t[ii,jt,cc] = x[end,jx,cc,imIdx]; + end + end + while it <= upper + @inbounds t[it,jt,cc] = x[ix,jx,cc,imIdx]; + it+=1;ix+=1; + end + if it <= nImg1 + for ii = it:nImg1 + ###################### Dirichlet ####################### + @inbounds t[ii,jt,cc] = zero(T) #@inbounds t[it:nImg1,jt,cc] = 0.0 - faster unvectorized + ###################### Periodic ####################### + # @inbounds t[ii,jt,cc] = x[1,jx,cc,imIdx]; + end + end + jt+=1;jx+=1; + + end + if jt <= nImg2 + ###################### Dirichlet ####################### + @inbounds t[:,jt:nImg2,cc] = zero(T); + ###################### Periodic ####################### + # if shiftT[q] > 0 + # @inbounds t[1,end,cc] = x[end,1,cc,imIdx]; + # end + # ix = ix = 1+shiftX[q]; + # for it = (1+shiftT[q]):upper + # @inbounds t[it,end,cc] = x[ix,1,cc,imIdx]; + # ix +=1; + # end + # if shiftT[q+1] < 0 + # @inbounds t[end,end,cc] = x[1,1,cc,imIdx]; + # end + ###################### End Periodic ####################### + end + end + if doDerivative == 0 + + BLAS.gemm!('N','T',OneType,reshape(t,nImg1*nImg2,cin),K[k],OneType,y); + else + BLAS.gemm!('T','N',OneType,reshape(t,nImg1*nImg2,cin),y,OneType,K[k]); + end + k+=1; + end + end + return y; +end diff --git a/src/layers/affineScalingLayer.jl b/src/layers/affineScalingLayer.jl index 64a2da0..36a6fca 100644 --- a/src/layers/affineScalingLayer.jl +++ b/src/layers/affineScalingLayer.jl @@ -27,10 +27,10 @@ function scaleChannels!(Y::Array{T},s::Array{T},b::Array{T}) where {T <: Number} end end -function apply(this::AffineScalingLayer{T},theta::Array{T},Y::Array{T},doDerivative=false) where {T <: Number} +function apply(this::AffineScalingLayer{T},theta::Array{T},Y::Array{T},dA,doDerivative=false) where {T <: Number} Y = reshape(copy(Y),this.nData[1], this.nData[2],:) - dA = (T)[] + dA = Array{T,2}(0,0) nex = size(Y,3) s2,b2 = splitWeights(this,theta); diff --git a/src/layers/doubleSymLayer.jl b/src/layers/doubleSymLayer.jl index 5f48cf3..326312f 100644 --- a/src/layers/doubleSymLayer.jl +++ b/src/layers/doubleSymLayer.jl @@ -7,6 +7,7 @@ export DoubleSymLayer,getDoubleSymLayer """ mutable struct DoubleSymLayer{T, TK <: AbstractConvKernel{T}, TN <: Union{batchNormNN{T}, normLayer{T}}} <: AbstractMeganetElement{T} activation :: Function # activation function + activation! :: Function # in place activation function K :: TK # Kernel model, e.g., convMod nLayer :: TN # normalization layer Bin :: Array{T} # Bias inside the nonlinearity @@ -14,12 +15,12 @@ mutable struct DoubleSymLayer{T, TK <: AbstractConvKernel{T}, TN <: Union{batchN end -function getDoubleSymLayer(TYPE::Type,K,nLayer::AbstractMeganetElement{T}, +function getDoubleSymLayer(TYPE::Type,K,nLayer::AbstractMeganetElement{T}; Bin=zeros(nFeatOut(K),0),Bout=zeros(nFeatIn(K),0), - activation=tanhActivation) where {T <: Number} + activation=tanhActivation,activation_inplace=tanhActivation!) where {T <: Number} BinT = convert.(T, Bin) BoutT = convert.(T, Bout) - return DoubleSymLayer(activation,K,nLayer,BinT,BoutT); + return DoubleSymLayer(activation,activation_inplace,K,nLayer,BinT,BoutT); end @@ -37,23 +38,36 @@ function splitWeights(this::DoubleSymLayer{T},theta::Array{T}) where {T<:Number} return th1, th2, th3, th4 end -function apply(this::DoubleSymLayer{T},theta::Array{T},Yin::Array{T,2},doDerivative=true) where {T<:Number} - +function apply(this::DoubleSymLayer{T},theta::Array{T},Yin::Array{T,2},tmp,doDerivative=true) where {T<:Number} + if isempty(tmp) + tmp = Array{Any}(2) + tmp[1] = Array{Any}(0) + tmp[2] = Array{Any}(0) + end #QZ = [] - tmp = Array{Any}(2) nex = div(length(Yin),nFeatIn(this))::Int Y = reshape(Yin,:,nex) theta1,theta2,theta3,theta4 = splitWeights(this,theta) Kop = getOp(this.K,theta1) - KY = Kop*Y - KY,dummy,tmp[1] = apply(this.nLayer,theta4,KY) + KY = Kop*Y # TODO: Look into making convolution in place + + KY,dummy,tmp[1] = apply(this.nLayer,theta4,KY,tmp[1],doDerivative) Yt = KY if !isempty(theta2) Yt .+= this.Bin*theta2 end - tmp[2] = copy(Yt) - Z::Array{T,2}, = this.activation(Yt,doDerivative) + + if doDerivative + if isempty(tmp[2]) + tmp[2] = copy(Yt) + else + tmp2 = tmp[2] + tmp2 .= Yt + end + end + + Z::Array{T,2}, = this.activation!(Yt,[],false) #We don't want to do derivatives here? Z = -(Kop'*Z) if !isempty(theta3) Z .+= this.Bout*theta3 @@ -79,8 +93,8 @@ end function initTheta(this::DoubleSymLayer{T}) where {T<:Number} theta = [vec(initTheta(this.K)); - T(0.1)*ones(T,size(this.Bin,2),1); - T(0.1)*ones(T,size(this.Bout,2),1); + T(0.01)*ones(T,size(this.Bin,2),1); + T(0.01)*ones(T,size(this.Bout,2),1); initTheta(this.nLayer)]; return theta end @@ -103,7 +117,7 @@ function Jthetamv(this::DoubleSymLayer{T},dtheta::Array{T},theta::Array{T},Y::Ar end function JYmv(this::DoubleSymLayer{T},dY::Array{T},theta::Array{T},Y::Array{T},tmp) where {T<:Number} - + #TODO: Look into why this activation cannot be done in place (tests fail) dA = this.activation(tmp[2],true)[2] nex = div(length(dY),nFeatIn(this)) @@ -212,4 +226,4 @@ function JTmv(this::DoubleSymLayer{T}, Zin::Array{T}, dummy::Array{T}, KopTdAZ = ATmv(this.K, th1, dAZ_out) dY = -KopTdAZ return dtheta, dY -end +end \ No newline at end of file diff --git a/src/layers/normLayer.jl b/src/layers/normLayer.jl index be6d4f7..1e156cf 100644 --- a/src/layers/normLayer.jl +++ b/src/layers/normLayer.jl @@ -12,13 +12,17 @@ function getNormLayer(TYPE::Type, nData,doNorm,eps = convert(TYPE,1e-3)) end function getBatchNormLayer(TYPE::Type, nData; eps = convert(TYPE,1e-3),isTrainable::Bool=true) + L = normLayer{TYPE}(nData,3,eps) if isTrainable - SL = AffineScalingLayer{TYPE}(nData) - return getbatchNormNN((L,SL)); + SL = AffineScalingLayer{TYPE}(nData) + temp_var = getbatchNormNN((L,SL)) + return temp_var else - return L; + temp_var = L + return temp_var end + end function getTVNormLayer(TYPE::Type,nData;eps = convert(TYPE,1e-3),isTrainable::Bool=true) @@ -31,25 +35,27 @@ function getTVNormLayer(TYPE::Type,nData;eps = convert(TYPE,1e-3),isTrainable::B end end -function apply(this::normLayer{T},theta::Array{T},Yin::Array{T,2},doDerivative=true) where {T <: Number} +function apply(this::normLayer{T},theta::Array{T},Yin::Array{T,2},dA,doDerivative=true) where {T <: Number} - # first organize Y with channels + # first organize Y with channels nf = this.nData[2]::Int nex = div(length(Yin),nFeatIn(this))::Int Y = reshape(Yin,:,nf,nex) - dA = (T)[] + dA = Array{T,2}(0,0) # subtract mean across pixels - Yout = Y.-mean(Y,this.doNorm) + m = mean(Y, this.doNorm) + Y .-= m # normalize - S2 = sqrt.(mean(Yout.^2,this.doNorm) + this.eps) - Yout ./= S2 - - Yout2 = reshape(Yout,:,nex) + ep = this.eps + mean!(x -> x^2, m, Y) + m .= sqrt.(m .+ ep) + Y .= Y ./ m - return Yout2, Yout2, dA + Yout = reshape(Y,:,nex) + return Yout, Yout, dA end function nTheta(this::normLayer) diff --git a/src/layers/singleLayer.jl b/src/layers/singleLayer.jl index 875955e..2593578 100644 --- a/src/layers/singleLayer.jl +++ b/src/layers/singleLayer.jl @@ -1,16 +1,18 @@ export singleLayer,getSingleLayer mutable struct singleLayer{T, TK <: AbstractConvKernel{T}, TN <: Union{batchNormNN{T}, normLayer{T}}} <: AbstractMeganetElement{T} - activation :: Function # activation function - K :: TK # transformation type - nLayer :: TN # normalization layer - Bin :: Array{T} # bias inside nonlinearity - Bout :: Array{T} # bias outside nonlinearity + activation :: Function # activation function + activation! :: Function # in place activation function + K :: TK # transformation type + nLayer :: TN # normalization layer + Bin :: Array{T} # bias inside nonlinearity + Bout :: Array{T} # bias outside nonlinearity end -function getSingleLayer(TYPE::Type, K,nLayer;Bin=zeros(TYPE,nFeatOut(K),0),Bout=zeros(TYPE,nFeatOut(K),0),activation=tanhActivation) - singleLayer(activation,K,nLayer,Bin,Bout); +function getSingleLayer(TYPE::Type, K,nLayer;Bin=zeros(TYPE,nFeatOut(K),0),Bout=zeros(TYPE,nFeatOut(K),0), + activation=tanhActivation,activation_inplace=tanhActivation!) + singleLayer(activation,activation_inplace,K,nLayer,Bin,Bout); end @@ -27,16 +29,23 @@ function splitWeights(this::singleLayer{T},theta::Array{T}) where {T <: Number} return th1, th2, th3, th4 end -function apply(this::singleLayer{T},theta::Array{T},Yin::Array{T},doDerivative=false) where {T <: Number} - tmp = Array{Any}(2) +function apply(this::singleLayer{T},theta::Array{T},Yin::Array{T},tmp,doDerivative=false) where {T <: Number} + + if isempty(tmp) + tmp = Array{Any}(2) + tmp[1] = Array{Any}(0) + tmp[2] = Array{Any}(0) + end nex = div(length(Yin),nFeatIn(this)) Y = reshape(Yin,:,nex) th1,th2,th3,th4 = splitWeights(this,theta) Yout::Array{T,2} = getOp(this.K,th1)*Y Yout .+= this.Bin * th2 - Yout,dummy,tmp[1] = apply(this.nLayer,th4,Yout,doDerivative) - Yout,tmp[2] = this.activation(Yout,doDerivative) + Yout,dummy,tmp[1] = apply(this.nLayer,th4,Yout,tmp[1],doDerivative) + + Yout,tmp[2] = this.activation!(Yout,tmp[2],doDerivative) + Yout .+= this.Bout*th3 Ydata = Yout return Ydata, Yout, tmp @@ -59,7 +68,7 @@ function nDataOut(this::singleLayer) end function initTheta(this::singleLayer{T}) where {T <: Number} - return [vec(initTheta(this.K)); convert(T,0.1)*ones(T,size(this.Bin,2),1) ; convert(T,0.1)*ones(T,size(this.Bout,2),1); initTheta(this.nLayer) ] + return [vec(initTheta(this.K)); convert(T,0.01)*ones(T,size(this.Bin,2),1) ; convert(T,0.01)*ones(T,size(this.Bout,2),1); initTheta(this.nLayer) ] end @@ -155,4 +164,4 @@ function JYTmv(this::singleLayer{T},Zin::Array{T},dummy::Array{T},theta::Array{T dAZ = JYTmv(this.nLayer,dAZ,(T)[],th4,Kop*Y.+this.Bin*th2,tmp[1]) ret::Array{T,2} = Kop'*reshape(dAZ,:,nex) return ret #TODO: @lars or eldad rename this variable as I'm not sure what to call it -end +end \ No newline at end of file diff --git a/src/optimization/dnnBatchObjFctn.jl b/src/optimization/dnnBatchObjFctn.jl index 42fd760..b484da2 100644 --- a/src/optimization/dnnBatchObjFctn.jl +++ b/src/optimization/dnnBatchObjFctn.jl @@ -17,28 +17,28 @@ mutable struct dnnObjFctn splitWeights(this::dnnObjFctn,x) = (return x[1:nTheta(this.net)], x[nTheta(this.net)+1:end]) -function getMisfit(this::dnnObjFctn,thetaW::Vector{T},Y::Array{T},C::Array{T},doDerivative=true) where {T} +function getMisfit(this::dnnObjFctn,thetaW::Vector{T},Y::Array{T},C::Array{T},tmp::Array,doDerivative=true) where {T<:Number} theta,W = splitWeights(this,thetaW) - return getMisfit(this,theta,W,Y,C,doDerivative) + return getMisfit(this,theta,W,Y,C,tmp,doDerivative) end -function getMisfit(this::dnnObjFctn,theta::Array{T},W::Array{T},Y::Array{T},C::Array{T},doDerivative=true) where {T} +function getMisfit(this::dnnObjFctn,theta::Array{T},W::Array{T},Y::Array{T},C::Array{T},tmp::Array,doDerivative=true) where {T<:Number} - YN,dummy,tmp = apply(this.net,theta,Y,doDerivative) + YN,dummy,tmp = apply(this.net,theta,Y,tmp,doDerivative) Fc,hisF,dWF,d2WF,dYF,d2YF = getMisfit(this.pLoss,W,YN,C,doDerivative,doDerivative) if doDerivative - dYF = JthetaTmv(this.net,dYF,zeros(T,0),theta,Y,tmp) + dYF = JthetaTmv(this.net,dYF,zeros(T,0),theta,Y,tmp) end - return Fc,hisF,vec(dYF),vec(dWF) + return Fc,hisF,vec(dYF),vec(dWF),tmp end -function evalObjFctn(this::dnnObjFctn,thetaW::Array{T},Y::Array{T},C::Array{T},doDerivative=true) where {T} +function evalObjFctn(this::dnnObjFctn,thetaW::Array{T},Y::Array{T},C::Array{T},tmp::Array,doDerivative=true) where {T<:Number} theta,W = splitWeights(this,thetaW) # compute misfit - Fc,hisF,dFth,dFW = getMisfit(this,theta,W,Y,C,doDerivative) + Fc,hisF,dFth,dFW,tmp = getMisfit(this,theta,W,Y,C,tmp,doDerivative) # regularizer for weights Rth,dRth, = regularizer(this.pRegTheta,theta) @@ -49,5 +49,5 @@ function evalObjFctn(this::dnnObjFctn,thetaW::Array{T},Y::Array{T},C::Array{T},d Jc = Fc + Rth + RW dJ = [dFth+dRth; dFW+dRW] - return convert(T,Jc),hisF,convert(Array{T},dJ) + return convert(T,Jc),hisF,convert(Array{T},dJ),tmp end diff --git a/src/optimization/sgd.jl b/src/optimization/sgd.jl index a2feab5..e5bc560 100644 --- a/src/optimization/sgd.jl +++ b/src/optimization/sgd.jl @@ -25,7 +25,7 @@ end Base.display(this::SGD)=println("SGD(maxEpochs=$(this.maxEpochs),miniBatch=$(this.miniBatch),learningRate=$(this.learningRate),momentum=$(this.momentum),nesterov=$(this.nesterov),ADAM=$(this.ADAM))") -function solve(this::SGD{T},objFun::dnnObjFctn,xc::Array{T},Y::Array{T},C::Array{T},Yv::Array{T},Cv::Array{T}) where {T} +function solve(this::SGD{T},objFun::dnnObjFctn,xc::Array{T},Y::Array{T},C::Array{T},Yv::Array{T},Cv::Array{T}) where {T<:Number} # evaluate training and validation epoch = 1; @@ -44,6 +44,8 @@ function solve(this::SGD{T},objFun::dnnObjFctn,xc::Array{T},Y::Array{T},C::Array if this.out; display(this); end + # Declare tmp - We know nothing about its shape or datatypes + tmp = Array{Any}(0,0) while epoch <= this.maxEpochs nex = size(Y,2) @@ -52,9 +54,9 @@ function solve(this::SGD{T},objFun::dnnObjFctn,xc::Array{T},Y::Array{T},C::Array for k=1:ceil(Int64,nex/this.miniBatch) idk = ids[(k-1)*this.miniBatch+1: min(k*this.miniBatch,nex)] if this.nesterov && !this.ADAM - Jk,dummy,dJk = evalObjFctn(objFun,xc-this.momentum*dJ,Y[:,idk],C[:,idk]); + Jk,dummy,dJk,tmp = evalObjFctn(objFun,xc-this.momentum*dJ,Y[:,idk],C[:,idk],tmp); else - Jk,dummy,dJk = evalObjFctn(objFun,xc,Y[:,idk],C[:,idk]); + Jk,dummy,dJk,tmp = evalObjFctn(objFun,xc,Y[:,idk],C[:,idk],tmp); end if this.ADAM @@ -65,14 +67,19 @@ function solve(this::SGD{T},objFun::dnnObjFctn,xc::Array{T},Y::Array{T},C::Array dJ = lr*dJk + this.momentum*dJ end xc = xc - dJ + # xc = xc - zero(T)*dJ + # ss = randn(T,length(dJ))*T(1e-3) + # J1, = evalObjFctn(objFun,xc,Y,C) + # J2, = evalObjFctn(objFun,xc+ss,Y,C) + # println(abs(J1-J2)," ", abs(J1-J2-dot(dJk[:],ss[:]))) end # we sample 2^12 images from the training set for displaying the objective. idt = ids[1:min(nex,2^12)] - Jc,para = evalObjFctn(objFun,xc,Y[:,idt],C[:,idt]); - Jval,pVal = getMisfit(objFun,xc,Yv,Cv,false); + Jtrain,ptrain = getMisfit(objFun,xc,Y[:,idt],C[:,idt],tmp,false); + Jval,pVal = getMisfit(objFun,xc,Yv,Cv,tmp,false); if this.out; - @printf "%d\t%1.2e\t%1.2f\t%1.2e\t%1.2e\t%1.2f\n" epoch Jc 100*(1-para[3]/para[2]) norm(xOld-xc) Jval 100*(1-pVal[3]/pVal[2]) + @printf "%d\t%1.2e\t%1.2f\t%1.2e\t%1.2e\t%1.2f\n" epoch Jtrain 100*(1-ptrain[3]/ptrain[2]) norm(xOld-xc) Jval 100*(1-pVal[3]/pVal[2]) end xOld = copy(xc); diff --git a/src/utils/testAbstractMeganetElement.jl b/src/utils/testAbstractMeganetElement.jl index 8b38a2b..266ff74 100644 --- a/src/utils/testAbstractMeganetElement.jl +++ b/src/utils/testAbstractMeganetElement.jl @@ -10,8 +10,9 @@ function testAbstractMeganetElement(L::AbstractMeganetElement{T};out::Bool=false theta .+= .1 # To test if Y changes for affineScalingLayer Y = randn(T,nFeatIn(L),nex) Yo = copy(Y) - Zd,Z,tmp = apply(L,theta,Y,true) - @test norm(Y-Yo)/norm(Yo) < 1e4*eps(T) + # THE FOLLOWING HACK (copy(Y)) EFFECTIVELY CANCELS THIS TEST...COMMENTING IT OUT + Zd,Z,tmp = apply(L,theta,copy(Y),[],true) +# @test norm(Y-Yo)/norm(Yo) < 1e4*eps(T) dY = randn(T,nFeatIn(L),nex) Z1 = JYmv(L,dY,theta,Y,tmp)[2] @@ -42,7 +43,7 @@ function testAbstractMeganetElement(L::AbstractMeganetElement{T};out::Bool=false @testset "apply without derivatives" begin theta = initTheta(L) Y = randn(T,nFeatIn(L),nex) - Z = apply(L,theta,Y,false) + Z = apply(L,theta,Y,[],false) end @@ -53,10 +54,10 @@ function testAbstractMeganetElement(L::AbstractMeganetElement{T};out::Bool=false function testFun(x,v=[]) if !(isempty(v)) - Z = apply(L,theta,x,true) + Z = apply(L,theta,copy(x),[],true) return Z[2], reshape(JYmv(L,v,theta,x,Z[3])[2],size(Z[2])) else - return apply(L,theta,x)[2] + return apply(L,theta,copy(x),[])[2] end end chkDer, = checkDerivative(testFun,copy(Y),out=out) @@ -69,7 +70,7 @@ function testAbstractMeganetElement(L::AbstractMeganetElement{T};out::Bool=false dY = randn(T,nFeatIn(L),nex) Z = randn(T,nFeatOut(L),nex) - tmp = apply(L,theta,Y,true) + tmp = apply(L,theta,copy(Y),[],true) Z1 = JYmv(L,copy(dY),theta,copy(Y),tmp[3])[2] Z2 = JYTmv(L,copy(Z),(T)[],theta,copy(Y),tmp[3]) @@ -86,10 +87,10 @@ function testAbstractMeganetElement(L::AbstractMeganetElement{T};out::Bool=false function testFunTh(x,v=[]) if !(isempty(v)) - Z = apply(L,x,copy(Y),true) + Z = apply(L,x,copy(Y),[],true) return Z[2], reshape(Jthetamv(L,v,x,copy(Y),Z[3])[2],size(Z[2])) else - return apply(L,x,copy(Y))[2] + return apply(L,x,copy(Y),[])[2] end end chkDer, = checkDerivative(testFunTh,copy(theta),out=out) @@ -102,7 +103,7 @@ function testAbstractMeganetElement(L::AbstractMeganetElement{T};out::Bool=false dtheta = randn(T,nTheta(L)) Z = randn(T,nFeatOut(L),nex) - tmp = apply(L,theta,copy(Y),true) + tmp = apply(L,theta,copy(Y),[],true) Z1 = Jthetamv(L,copy(dtheta),copy(theta),copy(Y),copy(tmp[3]))[2] Z2 = JthetaTmv(L,copy(Z),(T)[],theta,copy(Y),tmp[3]) diff --git a/src/utils/utilities.jl b/src/utils/utilities.jl index 4769d25..5a009a6 100644 --- a/src/utils/utilities.jl +++ b/src/utils/utilities.jl @@ -63,3 +63,30 @@ function meshgrid(vx::AbstractVector{T}, vy::AbstractVector{T}, oo = ones(Int, o) (vx[om, :, oo], vy[:, on, oo], vz[om, on, :]) end + +""" + mean(f, A, region) + +Apply the function `f` to each element of `A`, and compute the mean along dimension in `region`. +""" +function Base.mean(f::Function, a::AbstractArray, region::Int) + x = Base.mapreducedim(f, +, a, region) + n = max(1, Base._length(x)) // Base._length(a) + x .= x .* n + + return x +end + +""" + mean!(f, r, A) + +Apply `f` to each element of A, and compute the mean over the singleton dimensions of `r`, and write the results to `r`. +""" +function Base.mean!(f::Function, r::AbstractArray{T}, a::AbstractArray) where {T<:Number} + fill!(r, zero(T)) + x = Base.mapreducedim!(f, +, r, a) + n = max(1, Base._length(x)) // Base._length(a) + x .= x .* n + + return x +end diff --git a/test/kernel/convFFTKernelTest.jl b/test/kernel/convFFTKernelTest.jl index c9afa3e..f6a8eb0 100644 --- a/test/kernel/convFFTKernelTest.jl +++ b/test/kernel/convFFTKernelTest.jl @@ -1,35 +1,37 @@ -using Base.Test -using Meganet -using LinearOperators - - -nImg = [8,10] -sK = [3,3,4,4] -for TYPE=[Float64,Float32] - K = getConvFFTKernel(TYPE,nImg,sK) - - @testset "adjoint test $TYPE" begin - theta = initTheta(K) - A = getOp(K,theta); - v = randn(TYPE,nFeatIn(K)) - w = randn(TYPE,nFeatOut(K)) - - t1 = dot(w,A*v) - t2 = dot(v,A'*w) - # println("adjointTest t1=$t1\t t2=$t2") - @test norm(t1-t2)/norm(t1) < 1e3*eps(TYPE) - end - - @testset "derivative Test" begin - th = initTheta(K); - dth = initTheta(K); - nex = 1; - Y = randn(TYPE,nFeatIn(K),nex)+nex; - Z = randn(TYPE,nFeatOut(K),nex)-nex; - - t1 = vec(Z)'*vec(Jthetamv(K,dth,th,Y)); - t2 = vec(dth)'*vec(JthetaTmv(K,Z,th,Y)); - # println("derivativeTest t1=$t1\t t2=$t2") - @test norm(t1-t2)/norm(t2) < 1e3*eps(TYPE) - end -end +using Base.Test +using Meganet +using LinearOperators + + +nImg = [8,10] +sK = [3,3,4,4] +for TYPE=[Float64,Float32] + K = getConvFFTKernel(TYPE,nImg,sK) + + @testset "adjoint test $TYPE" begin + nex = 2; + theta = initTheta(K) + A = getOp(K,theta); + v = randn(TYPE,nFeatIn(K),nex) + w = randn(TYPE,nFeatOut(K),nex) + + t1 = vecdot(w,A*v) + t2 = vecdot(v,A'*w) + + # println("adjointTest t1=$t1\t t2=$t2") + @test norm(t1-t2)/norm(t1) < 1e3*eps(TYPE) + end + + @testset "derivative Test" begin + th = initTheta(K); + dth = initTheta(K); + nex = 2; + Y = randn(TYPE,nFeatIn(K),nex)+nex; + Z = randn(TYPE,nFeatOut(K),nex)-nex; + + t1 = vec(Z)'*vec(Jthetamv(K,dth,th,Y)); + t2 = vec(dth)'*vec(JthetaTmv(K,Z,th,Y)); + # println("derivativeTest t1=$t1\t t2=$t2") + @test norm(t1-t2)/norm(t2) < 1e3*eps(TYPE) + end +end diff --git a/test/kernel/convGEMMKernelTest.jl b/test/kernel/convGEMMKernelTest.jl index 8b16941..f70bfd6 100644 --- a/test/kernel/convGEMMKernelTest.jl +++ b/test/kernel/convGEMMKernelTest.jl @@ -1,55 +1,40 @@ -using Base.Test -using Meganet -using LinearOperators - - -nImg = [8,10] -sK = [3,3,4,4] -for TYPE=[Float64,Float32] - K = getConvGEMMKernel(TYPE,nImg,sK) - - @testset "adjoint test $TYPE" begin - theta = initTheta(K) - A = getOp(K,theta); - v = randn(TYPE,nFeatIn(K)) - w = randn(TYPE,nFeatOut(K)) - - t1 = dot(w,A*v) - t2 = dot(v,A'*w) - # println("adjointTest t1=$t1\t t2=$t2") - @test norm(t1-t2)/norm(t1) < 1e3*eps(TYPE) - end - - @testset "derivative Test" begin - th = initTheta(K); - dth = initTheta(K); - nex = 1; - Y = randn(TYPE,nFeatIn(K),nex)+nex; - Z = randn(TYPE,nFeatOut(K),nex)-nex; - - t1 = vec(Z)'*vec(Jthetamv(K,dth,th,Y)); - t2 = vec(dth)'*vec(JthetaTmv(K,Z,th,Y)); - # println("derivativeTest t1=$t1\t t2=$t2") - @test norm(t1-t2)/norm(t2) < 1e3*eps(TYPE) - end - - @testset "new derivitive test" begin - nImage = [16,16]; - sK = [3,3,2,4]; - K = randn(TYPE,tuple(sK...)); - Y = randn(TYPE,nImage[1],nImage[2],sK[3],2); - Z = randn(TYPE,nImage[1],nImage[2],sK[4],2); - Kernel2 = getConvGEMMKernel(TYPE,nImage,sK); - AY = Amv(Kernel2,K,Y); - ATZ = ATmv(Kernel2,K,Z); - - v1 = vecdot(Z,AY); - v2 = vecdot(ATZ,Y); - - v3 = vecdot(Z,Jthetamv(Kernel2,K,(TYPE)[],Y)); - v4 = vecdot(K,JthetaTmv(Kernel2,Z,(TYPE)[],Y)); - @test norm(v1-v2)/norm(v2) < 1e3*eps(TYPE) && - norm(v2-v3)/norm(v3) < 1e3*eps(TYPE) && - norm(v3-v4)/norm(v4) < 1e3*eps(TYPE) - end -end +using Base.Test +using Meganet +using LinearOperators + + +nImg = [8,10] +sK = [3,3,2,4] +for TYPE=[Float64,Float32] + K = getConvGEMMKernel(TYPE,nImg,sK) + + @testset "adjoint test $TYPE" begin + nex = 2; + theta = initTheta(K) + A = getOp(K,theta); + v = randn(TYPE,nFeatIn(K),nex) + w = randn(TYPE,nFeatOut(K),nex) + + t1 = dot(w,A*v) + t2 = dot(v,A'*w) + # println("adjointTest t1=$t1\t t2=$t2") + @test norm(t1-t2)/norm(t1) < 1e3*eps(TYPE) + end + + @testset "derivative Test" begin + th = initTheta(K); + dth = initTheta(K); + nex = 2; + Y = randn(TYPE,nFeatIn(K),nex); + Z = randn(TYPE,nFeatOut(K),nex); + + t1 = vec(Z)'*vec(Jthetamv(K,dth,th,Y)); + t2 = vec(dth)'*vec(JthetaTmv(K,Z,th,Y)); + # println("derivativeTest t1=$t1\t t2=$t2") + @test norm(t1-t2)/norm(t2) < 1e3*eps(TYPE) + end +end + + + + diff --git a/test/layer/doubleSymLayerTest.jl b/test/layer/doubleSymLayerTest.jl index 13a8f1c..ac5c964 100644 --- a/test/layer/doubleSymLayerTest.jl +++ b/test/layer/doubleSymLayerTest.jl @@ -1,61 +1,61 @@ -using Base.Test -using Meganet - -for TYPE=[Float64,Float32] - K = getDenseKernel(TYPE,[32,18]) - nex = 8 - Bin = randn(TYPE,nFeatOut(K),4) - Bout = randn(TYPE,nFeatIn(K),3) - nLayer = getTVNormLayer(TYPE,[8,4]) - L = getDoubleSymLayer(TYPE,K,nLayer,Bin,Bout) - @testset "doubleSymLayer (dense/TV) $TYPE" begin - testAbstractMeganetElement(L) - end - - K = getDenseKernel(TYPE,[32,18]) - nex = 8 - Bin = randn(TYPE,nFeatOut(K),4) - Bout = randn(TYPE,nFeatIn(K),3) - nLayer = getBatchNormLayer(TYPE,[8,4]) - L = getDoubleSymLayer(TYPE,K,nLayer,Bin,Bout) - @testset "doubleSymLayer (dense/Batch) $TYPE" begin - testAbstractMeganetElement(L) - end - - nImg = [32 32] - nc = 16 - nex = 50 - K = getSparseConvKernel2D(TYPE,nImg,[3,3,1,nc]) - Bin = randn(TYPE,nFeatOut(K),4) - Bout = randn(TYPE,nFeatIn(K),3) - nLayer = getBatchNormLayer(TYPE,[prod(nImg),nc],isTrainable=false) - L = getDoubleSymLayer(TYPE,K,nLayer,Bin,Bout) - @testset "doubleSymLayer (conv/Batch/not trainable) $TYPE" begin - testAbstractMeganetElement(L,nex=nex) - end - - - nImg = [8 4] - nc = 3 - nex = 4 - K = getSparseConvKernel2D(TYPE,nImg,[3,3,1,nc]) - Bin = randn(TYPE,nFeatOut(K),4) - Bout = randn(TYPE,nFeatIn(K),3) - nLayer = getBatchNormLayer(TYPE,[prod(nImg),nc]) - L = getDoubleSymLayer(TYPE,K,nLayer,Bin,Bout) - @testset "doubleSymLayer (conv/Batch) $TYPE" begin - testAbstractMeganetElement(L,nex=nex) - end - - nImg = [16 8] - nc = 6 - nex = 8 - K = getSparseConvKernel2D(TYPE,nImg,[3,3,1,nc]) - Bin = randn(TYPE,nFeatOut(K),4) - Bout = randn(TYPE,nFeatIn(K),3) - nLayer = getTVNormLayer(TYPE,[prod(nImg),nc]) - L = getDoubleSymLayer(TYPE,K,nLayer,Bin,Bout) - @testset "doubleSymLayer (conv/TV) $TYPE" begin - testAbstractMeganetElement(L) - end -end +using Base.Test +using Meganet + +for TYPE=[Float64,Float32] + K = getDenseKernel(TYPE,[32,18]) + nex = 8 + Bin = randn(TYPE,nFeatOut(K),4) + Bout = randn(TYPE,nFeatIn(K),3) + nLayer = getTVNormLayer(TYPE,[8,4]) + L = getDoubleSymLayer(TYPE,K,nLayer,Bin=Bin,Bout=Bout) + @testset "doubleSymLayer (dense/TV) $TYPE" begin + testAbstractMeganetElement(L) + end + + K = getDenseKernel(TYPE,[32,18]) + nex = 8 + Bin = randn(TYPE,nFeatOut(K),4) + Bout = randn(TYPE,nFeatIn(K),3) + nLayer = getBatchNormLayer(TYPE,[8,4]) + L = getDoubleSymLayer(TYPE,K,nLayer,Bin=Bin,Bout=Bout) + @testset "doubleSymLayer (dense/Batch) $TYPE" begin + testAbstractMeganetElement(L) + end + + nImg = [32 32] + nc = 16 + nex = 50 + K = getSparseConvKernel2D(TYPE,nImg,[3,3,1,nc]) + Bin = randn(TYPE,nFeatOut(K),4) + Bout = randn(TYPE,nFeatIn(K),3) + nLayer = getBatchNormLayer(TYPE,[prod(nImg),nc],isTrainable=false) + L = getDoubleSymLayer(TYPE,K,nLayer,Bin=Bin,Bout=Bout) + @testset "doubleSymLayer (conv/Batch/not trainable) $TYPE" begin + testAbstractMeganetElement(L,nex=nex) + end + + + nImg = [8 4] + nc = 3 + nex = 4 + K = getSparseConvKernel2D(TYPE,nImg,[3,3,1,nc]) + Bin = randn(TYPE,nFeatOut(K),4) + Bout = randn(TYPE,nFeatIn(K),3) + nLayer = getBatchNormLayer(TYPE,[prod(nImg),nc]) + L = getDoubleSymLayer(TYPE,K,nLayer,Bin=Bin,Bout=Bout) + @testset "doubleSymLayer (conv/Batch) $TYPE" begin + testAbstractMeganetElement(L,nex=nex) + end + + nImg = [16 8] + nc = 6 + nex = 8 + K = getSparseConvKernel2D(TYPE,nImg,[3,3,1,nc]) + Bin = randn(TYPE,nFeatOut(K),4) + Bout = randn(TYPE,nFeatIn(K),3) + nLayer = getTVNormLayer(TYPE,[prod(nImg),nc]) + L = getDoubleSymLayer(TYPE,K,nLayer,Bin=Bin,Bout=Bout) + @testset "doubleSymLayer (conv/TV) $TYPE" begin + testAbstractMeganetElement(L) + end +end diff --git a/test/optimization/dnnObjFctnTest.jl b/test/optimization/dnnObjFctnTest.jl index d41853f..0c96e12 100644 --- a/test/optimization/dnnObjFctnTest.jl +++ b/test/optimization/dnnObjFctnTest.jl @@ -31,7 +31,7 @@ objFun = dnnObjFctn(net,pLoss,pRegTh,pRegW) @testset "dThLoss $TYPE" begin function testdThLoss(x,v=nothing) - F,his,dF, = getMisfit(objFun,x,W,Y,C,true) + F,his,dF, = getMisfit(objFun,x,W,Y,C,[],true) if v!==nothing return F,dot(dF,v) else @@ -44,7 +44,7 @@ end @testset "dWLoss $TYPE" begin function testdWLoss(x,v=nothing) - F,his,dFth,dF = getMisfit(objFun,theta,x,Y,C,true) + F,his,dFth,dF = getMisfit(objFun,theta,x,Y,C,[],true) if v!==nothing return F,dot(dF,v) else @@ -57,7 +57,7 @@ end @testset "dJ $TYPE" begin function testdJ(x,v=nothing) - F,his,dF = evalObjFctn(objFun,x,Y,C,true) + F,his,dF = evalObjFctn(objFun,x,Y,C,[],true) if v!==nothing return F,dot(dF,v) else