五个简单示例 在 GitHub 上编辑

以下是五个简单的动手步骤,可以帮助您开始使用火炬!本教程假定已经通过以下方式安装了 torch 包:

require 'torch'

或者您正在使用 REPL th(它会自动安装该包)。

1. 定义一个正定二次型

这里,我们依赖于几个火炬函数:

  • rand(),它从均匀分布中创建张量
  • t(),它转置张量(注意它返回一个新的视图)
  • dot(),它执行两个张量之间的点积
  • eye(),它返回一个单位矩阵
  • * 运算符用于矩阵(执行矩阵-向量或矩阵-矩阵乘法)

我们首先确保随机种子对每个人都相同

torch.manualSeed(1234)
-- choose a dimension
N = 5

-- create a random NxN matrix
A = torch.rand(N, N)

-- make it symmetric positive
A = A*A:t()

-- make it definite
A:add(0.001, torch.eye(N))

-- add a linear term
b = torch.rand(N)

-- create the quadratic form
function J(x)
   return 0.5*x:dot(A*x)-b:dot(x)
end

可以使用以下命令轻松打印函数值(此处为随机点):

print(J(torch.rand(N)))

2. 找到确切的最小值

我们可以反转矩阵(这可能在数值上不是最优的)

xs = torch.inverse(A)*b
print(string.format('J(x^*) = %g', J(xs)))

3. 通过梯度下降搜索最小值

我们首先定义 J(x) 相对于 x 的梯度

function dJ(x)
  return A*x-b
end

然后我们定义一些当前解

x = torch.rand(N)

然后应用梯度下降(以给定的学习率 lr)一段时间

lr = 0.01
for i=1,20000 do
  x = x - dJ(x)*lr
  -- we print the value of the objective function at each iteration
  print(string.format('at iter %d J(x) = %f', i, J(x)))
end

您应该看到

...
at iter 19995 J(x) = -3.135664
at iter 19996 J(x) = -3.135664
at iter 19997 J(x) = -3.135665
at iter 19998 J(x) = -3.135665
at iter 19999 J(x) = -3.135665
at iter 20000 J(x) = -3.135666

4. 使用 optim 包

想要使用更高级的优化技术,比如共轭梯度或 LBFGS?optim 包可以满足您的需求!首先,我们需要安装它

luarocks install optim

关于局部变量的一句话

实际上,在实践中,使用全局变量永远不是一个好主意。在任何地方使用 local。在我们的示例中,我们已在全局范围内定义了一切,这样就可以将它们剪切粘贴到解释器命令行中。实际上,定义一个像

local A = torch.rand(N, N)

这样的局部变量,它只对当前作用域可用,而运行解释器时,它只限于当前输入行。随后的行将无法访问此局部变量。

在 lua 中,可以使用 do...end 指令定义一个作用域

do
   local A = torch.rand(N, N)
   print(A)
end
print(A)

如果您将此代码剪切粘贴到命令行中,第一个打印输出将是一个 5x5 矩阵(因为局部变量 Ado...end 作用域持续期间定义),但在之后将为 nil

定义具有上值的闭包

我们需要定义一个闭包,它返回 J(x)dJ(x)。这里,我们使用 do...end 定义一个作用域,这样局部变量 neval 就是 JdJ(x) 的上值:只有 JdJ(x) 才能知道它。请注意,在脚本中,不需要使用 do...end 作用域,因为 neval 的作用域将持续到脚本文件结束(而不是像命令行那样持续到行结束)。

do
   local neval = 0
   function JdJ(x)
      local Jx = J(x)
      neval = neval + 1
      print(string.format('after %d evaluations J(x) = %f', neval, Jx))
      return Jx, dJ(x)
   end
end

使用 optim 进行训练

该包默认情况下不会加载,因此让我们加载它

require 'optim'

我们首先定义共轭梯度的一个状态

state = {
   verbose = true,
   maxIter = 100
}

现在我们开始训练

x = torch.rand(N)
optim.cg(JdJ, x, state)

您应该看到类似以下内容

after 120 evaluation J(x) = -3.136835
after 121 evaluation J(x) = -3.136836
after 122 evaluation J(x) = -3.136837
after 123 evaluation J(x) = -3.136838
after 124 evaluation J(x) = -3.136840
after 125 evaluation J(x) = -3.136838

5. 绘制图形

绘制图形可以通过多种方式实现。例如,可以使用最近的 iTorch 包。这里,我们将使用 gnuplot

luarocks install gnuplot

存储中间函数评估结果

我们对之前使用的闭包进行了一些修改,以便它可以存储中间函数评估结果(以及到目前为止训练所花费的实际时间)

evaluations = {}
time = {}
timer = torch.Timer()
neval = 0
function JdJ(x)
   local Jx = J(x)
   neval = neval + 1
   print(string.format('after %d evaluations, J(x) = %f', neval, Jx))
   table.insert(evaluations, Jx)
   table.insert(time, timer:time().real)
   return Jx, dJ(x)
end

现在我们可以训练它

state = {
   verbose = true,
   maxIter = 100
}

x0 = torch.rand(N)
cgx = x0:clone() -- make a copy of x0
timer:reset()
optim.cg(JdJ, cgx, state)

-- we convert the evaluations and time tables to tensors for plotting:
cgtime = torch.Tensor(time)
cgevaluations = torch.Tensor(evaluations)

添加对随机梯度下降的支持

让我们使用 optim 添加随机梯度下降训练

evaluations = {}
time = {}
neval = 0
state = {
  lr = 0.1
}

-- we start from the same starting point than for CG
x = x0:clone()

-- reset the timer!
timer:reset()

-- note that SGD optimizer requires us to do the loop
for i=1,1000 do
  optim.sgd(JdJ, x, state)
  table.insert(evaluations, Jx)
end
  
sgdtime = torch.Tensor(time)
sgdevaluations = torch.Tensor(evaluations)

最终绘图

现在我们可以绘制图形。一个简单的方法是使用 gnuplot.plot(x, y)。这里,我们在前面加上 gnuplot.figure() 以确保图形在不同的图形上显示。

require 'gnuplot'
gnuplot.figure(1)
gnuplot.title('CG loss minimisation over time')
gnuplot.plot(cgtime, cgevaluations)

gnuplot.figure(2)
gnuplot.title('SGD loss minimisation over time')
gnuplot.plot(sgdtime, sgdevaluations)

一种更高级的方法是将所有内容都绘制在同一个图形上,方法如下。这里,我们将所有内容保存到 PNG 文件中。

gnuplot.pngfigure('plot.png')
gnuplot.plot(
   {'CG',  cgtime,  cgevaluations,  '-'},
   {'SGD', sgdtime, sgdevaluations, '-'})
gnuplot.xlabel('time (s)')
gnuplot.ylabel('J(x)')
gnuplot.plotflush()

CG vs SGD