iTorch / Torch7 implementation

Gm = d in lua/torch for LuaJIT.

Prerequisites

You need to install torch and (if you want to run this notebook) iTorch. Works for me on Ubuntu 16.04 and MacOS 10.11. Not gonna say the Mac installation is easy though. Everything was very smooth on Ubuntu.

Demo

We'll start with the example from the torch7 documentation.


In [50]:
G = torch.Tensor({{6.80, -2.11,  5.66,  5.97,  8.23},
                  {-6.05, -3.30,  5.36, -4.44,  1.08},
                  {-0.45,  2.58, -2.70,  0.27,  9.04},
                  {8.32,  2.71,  4.35,  -7.17,  2.14},
                  {-9.67, -5.14, -7.26,  6.08, -6.87}}):t()  -- transpose

In [51]:
d = torch.Tensor({{4.02,  6.19, -8.22, -7.57, -3.03},
                  {-1.56,  4.00, -8.67,  1.75,  2.86},
                  {9.81, -4.09, -4.57, -8.61,  8.99}}):t()

Later we can use lower-level linear algebraic functions, but for now here's the solution using LAPACK's gesv, which is described like this:

Computes the solution to a real system of linear equations A*X=B (simple driver). LU decomposition with partial pivoting and row interchanges is used to solve the system.


In [52]:
timer = torch.Timer()
m_est = torch.gesv(d, G)
print(timer:time().real)


Out[52]:
0.00014591217041016	

In [53]:
d:dist(G*m_est)


Out[53]:
9.8001946516719e-15	

In [54]:
-- pseudo-inverse
torch.inverse(G:t()*G) * G:t()


Out[54]:
 0.0476 -0.1069  0.0034  0.0267  0.0331
-0.0516 -0.0623  0.0448 -0.0472  0.0300
-0.0046 -0.0460 -0.0964 -0.0524  0.0963
 0.1463 -0.2994 -0.1721 -0.1734  0.0465
 0.0884 -0.2916 -0.1693 -0.0984  0.0400
[torch.DoubleTensor of size 5x5]

Mauricio's example


In [55]:
-- Define model
M = 50
m = torch.zeros(M, 1)
m[{{11,15}, {}}] = 1.0
m[{{16,27}, {}}] = -0.3
m[{{28,35}, {}}] = 2.1

In [56]:
Plot = require 'itorch.Plot'

In [57]:
x = torch.range(1,m:size(1))
plot = Plot():line(x, m[{{},1}]):circle(x, m[{{},1}]):title('Model m'):draw()



In [58]:
-- Discrete kernel G
N = 20
L = 100
alpha = 0.8

In [59]:
M = 50
x = torch.range(0, M, 1) * L/(M-1)
dx = L/(M-1)
r = torch.range(0, N, 1) * L/(N-1)

In [60]:
G = torch.zeros(N, M)

In [61]:
for j = 1,M do
    for k = 1,N do
        t = torch.abs(r[{k}] - x[{j}])
        G[{{k},{j}}] = dx * torch.exp(-alpha * torch.pow(t, 2))
    end
end

In [62]:
d = G * m

In [63]:
Plot = require 'itorch.Plot'
x = torch.range(1,d:size(1))
plot = Plot():line(x, d[{{},1}]):circle(x, d[{{},1}]):title('Data d'):draw()


Noise free: minimum norm


In [64]:
-- Compute data
d = G * m

In [65]:
-- Minimum norm solution
m_est = G:t() * torch.inverse(G * G:t()) * d
d_pred = G * m_est

In [66]:
-- Plot as before
Plot = require 'itorch.Plot'
x = torch.range(1,d_pred:size(1))
plot = Plot():line(x, d_pred[{{},1}]):circle(x, d_pred[{{},1}]):title('Predicted data d_pred'):draw()


Least squares solution using GESV

This uses factorization instead of finding the inverse...


In [67]:
-- Use torch.gels()
m_est = torch.gels(d, G)
d_pred = G * m_est

-- Plot
Plot = require 'itorch.Plot'
x = torch.range(1,d_pred:size(1))
plot = Plot():line(x, d[{{},1}], 'blue'):circle(x, d[{{},1}], 'blue')
             :line(x, d_pred[{{},1}]):circle(x, d_pred[{{},1}]):title('blue: d — red: d_pred')
             :draw()



In [68]:
d:dist(G * m_est)


Out[68]:
1.4174378710523e-15	

With noise: damped least squares


In [69]:
-- Add noise
dc = G * m
s = 1
d = dc + s * torch.DoubleTensor(d:size()):uniform(0,1)

In [21]:
-- Compute using the second form
I = torch.eye(N)
mu = 2.5
m_est = G:t() * torch.inverse(G*G:t() + mu*I) * d
d_pred = G * m_est

In [22]:
-- Plot
Plot = require 'itorch.Plot'
x = torch.range(1,d_pred:size(1))
plot = Plot():line(x, d[{{},1}], 'blue'):circle(x, d[{{},1}], 'blue')
             :line(x, d_pred[{{},1}]):circle(x, d_pred[{{},1}]):title('blue: d — red: d_pred')
             :draw()


Try using Cholesky decomposition

It's not a good idea to use the matrix inverse. So let's try factorization:


In [23]:
chol = torch.potrf(G * G:t() + mu * I)

In [24]:
m_est = torch.potrs(G:t() * d, chol)

In [25]:
d_pred = G * m_est

In [26]:
-- Plot
Plot = require 'itorch.Plot'
x = torch.range(1,d_pred:size(1))
plot = Plot():line(x, d[{{},1}], 'blue'):circle(x, d[{{},1}], 'blue')
             :line(x, d_pred[{{},1}]):circle(x, d_pred[{{},1}]):title('blue: d — red: d_pred')
             :draw()


OK, I guess I screwed that up.

Interlude: convmtx

We need to implement convmtx, the matrix equivalent of the convolutional operator. This helps us make the first derivative.

There's no Toeplitz helper function that I can find, so we'll have to do it a slightly different way from the one we used in the NumPy notebook.


In [27]:
function convmtx (h, n)
    local m = h:size()[1] - 1
    local c = torch.Tensor(n, n+m):fill(0)
    for i = 1, n do
        c[{ i,{i,i+m} }] = h
    end
    return c
end

In [28]:
h = torch.Tensor({1,-1})
n = 8  -- number of rows in conv. matrix

In [29]:
convmtx(h, n)


Out[29]:
 1 -1  0  0  0  0  0  0  0
 0  1 -1  0  0  0  0  0  0
 0  0  1 -1  0  0  0  0  0
 0  0  0  1 -1  0  0  0  0
 0  0  0  0  1 -1  0  0  0
 0  0  0  0  0  1 -1  0  0
 0  0  0  0  0  0  1 -1  0
 0  0  0  0  0  0  0  1 -1
[torch.DoubleTensor of size 8x9]

As far as I can tell, there's no simple way to index off the last column, as with the -1 trick in Python. But we know the number of columns is 8 + h.size()[1] - 1 so we can just substract one from that...


In [30]:
cols = n + h:size()[1] - 1
print(convmtx(h, n)[{ {},{1, cols-1} }])


Out[30]:
 1 -1  0  0  0  0  0  0
 0  1 -1  0  0  0  0  0
 0  0  1 -1  0  0  0  0
 0  0  0  1 -1  0  0  0
 0  0  0  0  1 -1  0  0
 0  0  0  0  0  1 -1  0
 0  0  0  0  0  0  1 -1
 0  0  0  0  0  0  0  1
[torch.DoubleTensor of size 8x8]

With noise: damped LS with 1st derivative regularization


In [31]:
cols = M + h:size()[1] - 1
W = convmtx(h, M)[{ {},{1, cols-1} }]

Now we solve:

$$ \hat{\mathbf{m}} = (\mathbf{G}^\mathrm{T} \mathbf{G} + \mu \mathbf{W}^\mathrm{T} \mathbf{W})^{-1} \mathbf{G}^\mathrm{T} \mathbf{d} \ \ $$

In [32]:
m_est = torch.inverse(G:t()*G + mu*W:t()*W) * G:t() * d
d_pred = G * m_est

In [33]:
-- Plot
Plot = require 'itorch.Plot'
x = torch.range(1,d_pred:size(1))
plot = Plot():line(x, d[{{},1}], 'blue'):circle(x, d[{{},1}], 'blue')
             :line(x, d_pred[{{},1}]):circle(x, d_pred[{{},1}]):title('blue: d — red: d_pred')
             :draw()



In [34]:
-- Plot
Plot = require 'itorch.Plot'
x = torch.range(1,m_est:size(1))
plot = Plot():line(x, m[{{},1}], 'blue', 'blue')
             :line(x, m_est[{{},1}]):title('blue: m — red: m_est')
             :draw()


Plotting demo

From the iTorch readme.


In [35]:
x1 = torch.randn(40):mul(100)
y1 = torch.randn(40):mul(100)
x2 = torch.randn(40):mul(100)
y2 = torch.randn(40):mul(100)
x3 = torch.randn(40):mul(200)
y3 = torch.randn(40):mul(200)


-- scatter plots
plot = Plot():circle(x1, y1, 'red', 'hi'):circle(x2, y2, 'blue', 'bye'):draw()
plot:circle(x3,y3,'green', 'yolo'):redraw()
plot:title('Scatter Plot Demo'):redraw()
plot:xaxis('length'):yaxis('width'):redraw()
plot:legend(true)
plot:redraw()


BLAS and CUDA examples

From Nicholas Leonard's examples.


In [36]:
batchSize, inputSize, outputSize = 4000, 2000, 3000
input = torch.FloatTensor(batchSize, inputSize):uniform(0,1)
weight = torch.FloatTensor(outputSize, inputSize):uniform(0,1)
output = torch.FloatTensor(batchSize, outputSize)  -- Must give size here, AFAICT

In [37]:
timer = torch.Timer()
output:addmm(0, output, 1, input, weight:t())  -- transpose of weight
print(timer:time().real)


Out[37]:
1.2307860851288	

How about CudaTensors?

Note that you need an nVidia graphics card with a recent version of CUDA installed, and you will need to install cutorch with luarocks.


In [38]:
require 'cutorch'
input = torch.CudaTensor(batchSize, inputSize):uniform(0,1)
weight = torch.CudaTensor(outputSize, inputSize):uniform(0,1)
output = torch.CudaTensor(batchSize, outputSize)


[string "require 'cutorch'..."]:1: module 'cutorch' not found:
	no field package.preload['cutorch']
	no file '/home/matt/.luarocks/share/lua/5.1/cutorch.lua'
	no file '/home/matt/.luarocks/share/lua/5.1/cutorch/init.lua'
	no file '/home/matt/torch/install/share/lua/5.1/cutorch.lua'
	no file '/home/matt/torch/install/share/lua/5.1/cutorch/init.lua'
	no file './cutorch.lua'
	no file '/home/matt/torch/install/share/luajit-2.1.0-beta1/cutorch.lua'
	no file '/usr/local/share/lua/5.1/cutorch.lua'
	no file '/usr/local/share/lua/5.1/cutorch/init.lua'
	no file '/home/matt/torch/install/lib/cutorch.so'
	no file '/home/matt/.luarocks/lib/lua/5.1/cutorch.so'
	no file '/home/matt/torch/install/lib/lua/5.1/cutorch.so'
	no file './cutorch.so'
	no file '/usr/local/lib/lua/5.1/cutorch.so'
	no file '/usr/local/lib/lua/5.1/loadall.so'
stack traceback:
	[C]: in function 'require'
	[string "require 'cutorch'..."]:1: in main chunk
	[C]: in function 'xpcall'
	/home/matt/torch/install/share/lua/5.1/itorch/main.lua:209: in function </home/matt/torch/install/share/lua/5.1/itorch/main.lua:173>
	/home/matt/torch/install/share/lua/5.1/lzmq/poller.lua:75: in function 'poll'
	/home/matt/torch/install/share/lua/5.1/lzmq/impl/loop.lua:307: in function 'poll'
	/home/matt/torch/install/share/lua/5.1/lzmq/impl/loop.lua:325: in function 'sleep_ex'
	/home/matt/torch/install/share/lua/5.1/lzmq/impl/loop.lua:370: in function 'start'
	/home/matt/torch/install/share/lua/5.1/itorch/main.lua:381: in main chunk
	[C]: in function 'require'
	(command line):1: in main chunk
	[C]: at 0x00405d50

In [39]:
time = timer:reset()
output:addmm(0, output, 1, input, weight:t())
print(timer:time().real)


Out[39]:
1.2176659107208	

In [35]:
-- output