λ²μ: μ μ©ν
autograd λ PyTorchλ‘ μ μ°νκ³ μλμ μΈ μ κ²½λ§μ ꡬμΆνκΈ° μν΄
νμμ μΈ ν¨ν€μ§μ
λλ€. PyTorch νμ΄μ¬ νλ‘ νΈμλμ μλ λ―ΈλΆ API λλΆλΆμ C++ νλ‘ νΈμλμμλ
μ¬μ©ν μ μμΌλ©°, νμ΄μ¬μμ C++λ‘ μλ λ―ΈλΆ μ½λλ₯Ό μ½κ² λ³νν μ μμ΅λλ€.
μ΄ νν 리μΌμμλ PyTorch C++ νλ‘ νΈμλμμ μλ λ―ΈλΆμ μννλ λͺ κ°μ§ μλ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€. μ΄ νν 리μΌμ μ¬λ¬λΆμ΄ νμ΄μ¬ νλ‘ νΈμλμ μλ λ―ΈλΆμ λν΄ κΈ°λ³Έμ μΌλ‘ μ΄ν΄νκ³ μλ€κ³ κ°μ ν©λλ€. κ·Έλ μ§ μμ κ²½μ° λ¨Όμ Autograd: Automatic Differentiation μ μ½μ΄λ³΄μΈμ.
(μ΄ νν λ¦¬μΌ μ λ΄μ©μ κΈ°λ°ν¨)
ν
μλ₯Ό μμ±νκ³ κ·Έκ²μ κ³μ°μ μΆμ νκΈ° μν΄ torch::requires_grad() λ₯Ό μ€νν΄λ΄
μλ€.
auto x = torch::ones({2, 2}, torch::requires_grad());
std::cout << x << std::endl;Out:
1 1
1 1
[ CPUFloatType{2,2} ]ν μ μ°μ°μ μνν΄λ³΄κ² μ΅λλ€.
auto y = x + 2;
std::cout << y << std::endl;Out:
3 3
3 3
[ CPUFloatType{2,2} ]y λ μ°μ°μ κ²°κ³Όλ‘ μμ±λμμΌλ―λ‘ grad_fn λ₯Ό κ°κ³ μμ΅λλ€.
std::cout << y.grad_fn()->name() << std::endl;Out:
AddBackward1y μ λν΄ λ λ§μ μ°μ°μ μνν΄λ΄
μλ€.
auto z = y * y * 3;
auto out = z.mean();
std::cout << z << std::endl;
std::cout << z.grad_fn()->name() << std::endl;
std::cout << out << std::endl;
std::cout << out.grad_fn()->name() << std::endl;Out:
27 27
27 27
[ CPUFloatType{2,2} ]
MulBackward1
27
[ CPUFloatType{} ]
MeanBackward0.requires_grad_( ... ) λ in-placeλ‘ ν
μμ κΈ°μ‘΄ requires_grad νλκ·Έλ₯Ό λ°κΏλλ€.
auto a = torch::randn({2, 2});
a = ((a * 3) / (a - 1));
std::cout << a.requires_grad() << std::endl;
a.requires_grad_(true);
std::cout << a.requires_grad() << std::endl;
auto b = (a * a).sum();
std::cout << b.grad_fn()->name() << std::endl;Out:
false
true
SumBackward0μ΄μ μμ νλ₯Ό μνν΄λ΄
μλ€. out μ΄ λ¨μΌ μ€μΉΌλΌλ§μ ν¬ν¨νλ―λ‘, out.backward() λ
out.backward(torch::tensor(1.)) μ κ°μ΅λλ€.
out.backward();λ³νλ d(out)/dxλ₯Ό μΆλ ₯ν΄λ³΄κ² μ΅λλ€.
std::cout << x.grad() << std::endl;Out:
4.5000 4.5000
4.5000 4.5000
[ CPUFloatType{2,2} ]4.5 νλ ¬μ΄ μΆλ ₯λΌμΌ ν©λλ€. μ΄ κ°μ μ»λ κ³Όμ μ λν μ€λͺ
μ μ΄ νν 리μΌμ ν΄λΉ μΉμ
μμ νμΈνμΈμ.
μ΄μ 벑ν°-μΌμ½λΉμ κ³±μ μλ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€.
x = torch::randn(3, torch::requires_grad());
y = x * 2;
while (y.norm().item<double>() < 1000) {
y = y * 2;
}
std::cout << y << std::endl;
std::cout << y.grad_fn()->name() << std::endl;Out:
-1021.4020
314.6695
-613.4944
[ CPUFloatType{3} ]
MulBackward1벑ν°-μΌμ½λΉμ κ³±μ μ»κΈ° μν΄ λ²‘ν°λ₯Ό backward μ μΈμλ‘ λ£μ΄μ€λλ€.
auto v = torch::tensor({0.1, 1.0, 0.0001}, torch::kFloat);
y.backward(v);
std::cout << x.grad() << std::endl;Out:
102.4000
1024.0000
0.1024
[ CPUFloatType{3} ]λν μ½λμ torch::NoGradGuard λ₯Ό λ£μ΄μ£Όλ©΄ μλ λ―ΈλΆμΌλ‘ νμ¬κΈ κ·ΈλλμΈνΈκ°
νμν ν
μλ₯Ό μΆμ νμ§ μλλ‘ ν μ μμ΅λλ€.
std::cout << x.requires_grad() << std::endl;
std::cout << x.pow(2).requires_grad() << std::endl;
{
torch::NoGradGuard no_grad;
std::cout << x.pow(2).requires_grad() << std::endl;
}Out:
true
true
falseνΉμ .detach() λ₯Ό μ¬μ©νμ¬ λ΄μ©μ λμΌνμ§λ§ κ·ΈλλμΈνΈκ° νμ μλ
μ ν
μλ₯Ό μ»μ μλ μμ΅λλ€.
std::cout << x.requires_grad() << std::endl;
y = x.detach();
std::cout << y.requires_grad() << std::endl;
std::cout << x.eq(y).all().item<bool>() << std::endl;Out:
true
false
truegrad / requires_grad / is_leaf / backward / detach / detach_ /
register_hook / retain_grad λ± C++ ν
μ μλ λ―ΈλΆ APIμ λν μμΈν λ΄μ©μ ν΄λΉ C++ API λ¬Έμ μμ νμΈνμΈμ.
κ³ μ°¨μ κ·ΈλλμΈνΈλ₯Ό μ¬μ©νλ μ¬λ‘λ‘ κ·ΈλλμΈνΈ ν¨λν° κ³μ°μ΄ μμ΅λλ€.
torch::autograd::grad λ₯Ό μ¬μ©νλ μλ₯Ό μ΄ν΄λ΄
μλ€.
#include <torch/torch.h>
auto model = torch::nn::Linear(4, 3);
auto input = torch::randn({3, 4}).requires_grad_(true);
auto output = model(input);
// Calculate loss
auto target = torch::randn({3, 3});
auto loss = torch::nn::MSELoss()(output, target);
// Use norm of gradients as penalty
auto grad_output = torch::ones_like(output);
auto gradient = torch::autograd::grad({output}, {input}, /*grad_outputs=*/{grad_output}, /*create_graph=*/true)[0];
auto gradient_penalty = torch::pow((gradient.norm(2, /*dim=*/1) - 1), 2).mean();
// Add gradient penalty to loss
auto combined_loss = loss + gradient_penalty;
combined_loss.backward();
std::cout << input.grad() << std::endl;Out:
-0.1042 -0.0638 0.0103 0.0723
-0.2543 -0.1222 0.0071 0.0814
-0.1683 -0.1052 0.0355 0.1024
[ CPUFloatType{3,4} ]torch::autograd::backward
(λ§ν¬) λ°
torch::autograd::grad
(λ§ν¬) λ¬Έμμμ
μ΄ ν¨μλ€μ μ¬μ©λ²μ λν΄ λ μμ보μΈμ.
(μ΄ νν λ¦¬μΌ μ λ΄μ©μ κΈ°λ°ν¨)
torch::autograd μ μλ‘μ΄ κΈ°λ³Έ(elementary) μ°μ°μ μΆκ°νλ €λ©΄ κ° μ°μ°μ λν΄ μλ‘μ΄ torch::autograd::Function
νμ ν΄λμ€(subclass)λ₯Ό ꡬνν΄μΌ ν©λλ€. torch::autograd λ κ²°κ³Όμ κ·ΈλλμΈνΈλ₯Ό κ³μ°νκ³ μ°μ° κΈ°λ‘μ μΈμ½λ©νκΈ° μν΄ μν΄
μ΄ torch::autograd::Function λ€μ μ¬μ©ν©λλ€. λͺ¨λ μλ‘μ΄ ν¨μμλ λ κ°μ§ λ°©λ², μ¦ forward μ backward λ₯Ό
ꡬνν΄μΌ νλ©° μμΈν μꡬμ¬νμ μ΄ λ§ν¬
μμ νμΈνμΈμ.
μλ μ½λλ torch::nn μ Linear ν¨μλ₯Ό μ¬μ©ν©λλ€.
#include <torch/torch.h>
using namespace torch::autograd;
// Inherit from Function
class LinearFunction : public Function<LinearFunction> {
public:
// Note that both forward and backward are static functions
// bias is an optional argument
static torch::Tensor forward(
AutogradContext *ctx, torch::Tensor input, torch::Tensor weight, torch::Tensor bias = torch::Tensor()) {
ctx->save_for_backward({input, weight, bias});
auto output = input.mm(weight.t());
if (bias.defined()) {
output += bias.unsqueeze(0).expand_as(output);
}
return output;
}
static tensor_list backward(AutogradContext *ctx, tensor_list grad_outputs) {
auto saved = ctx->get_saved_variables();
auto input = saved[0];
auto weight = saved[1];
auto bias = saved[2];
auto grad_output = grad_outputs[0];
auto grad_input = grad_output.mm(weight);
auto grad_weight = grad_output.t().mm(input);
auto grad_bias = torch::Tensor();
if (bias.defined()) {
grad_bias = grad_output.sum(0);
}
return {grad_input, grad_weight, grad_bias};
}
};μ΄μ μλμ κ°μ΄ LinearFunction μ μ¬μ©ν μ μμ΅λλ€.
auto x = torch::randn({2, 3}).requires_grad_();
auto weight = torch::randn({4, 3}).requires_grad_();
auto y = LinearFunction::apply(x, weight);
y.sum().backward();
std::cout << x.grad() << std::endl;
std::cout << weight.grad() << std::endl;Out:
0.5314 1.2807 1.4864
0.5314 1.2807 1.4864
[ CPUFloatType{2,3} ]
3.7608 0.9101 0.0073
3.7608 0.9101 0.0073
3.7608 0.9101 0.0073
3.7608 0.9101 0.0073
[ CPUFloatType{4,3} ]μ¬κΈ°μ, ν μκ° μλ μΈμλ₯Ό λ§€κ°λ³μλ‘ κ°λ λ λ€λ₯Έ ν¨μλ₯Ό μλ‘ λ€μ΄ λ³΄κ² μ΅λλ€.
#include <torch/torch.h>
using namespace torch::autograd;
class MulConstant : public Function<MulConstant> {
public:
static torch::Tensor forward(AutogradContext *ctx, torch::Tensor tensor, double constant) {
// ctx is a context object that can be used to stash information
// for backward computation
ctx->saved_data["constant"] = constant;
return tensor * constant;
}
static tensor_list backward(AutogradContext *ctx, tensor_list grad_outputs) {
// We return as many input gradients as there were arguments.
// Gradients of non-tensor arguments to forward must be `torch::Tensor()`.
return {grad_outputs[0] * ctx->saved_data["constant"].toDouble(), torch::Tensor()};
}
};μ΄μ μλμ κ°μ΄ MulConstant λ₯Ό μ¬μ©ν μ μμ΅λλ€.
auto x = torch::randn({2}).requires_grad_();
auto y = MulConstant::apply(x, 5.5);
y.sum().backward();
std::cout << x.grad() << std::endl;Out:
5.5000
5.5000
[ CPUFloatType{2} ]torch::autograd::Function μ λν λ λ§μ λ΄μ©μ
μ΄ λ¬Έμ μμ νμΈν μ μμ΅λλ€.
κ°λ΅μ μΌλ‘ λ§νλ©΄, C++μμ μλ λ―ΈλΆμ μ¬μ©νλ κ°μ₯ μ¬μ΄ λ°©λ²μ λ¨Όμ νμ΄μ¬μμ λμνλ μλ λ―ΈλΆ μ½λλ₯Ό μμ±ν ν, μλ νλ₯Ό μ°Έκ³ ν΄ C++ μ½λλ‘ λ³ννλ κ²μ λλ€.
| Python | C++ |
|---|---|
torch.autograd.backward |
torch::autograd::backward (λ§ν¬) |
torch.autograd.grad |
torch::autograd::grad (λ§ν¬) |
torch.Tensor.detach |
torch::Tensor::detach (λ§ν¬) |
torch.Tensor.detach_ |
torch::Tensor::detach_ (λ§ν¬) |
torch.Tensor.backward |
torch::Tensor::backward (λ§ν¬) |
torch.Tensor.register_hook |
torch::Tensor::register_hook (λ§ν¬) |
torch.Tensor.requires_grad |
torch::Tensor::requires_grad_ (λ§ν¬) |
torch.Tensor.retain_grad |
torch::Tensor::retain_grad (λ§ν¬) |
torch.Tensor.grad |
torch::Tensor::grad (λ§ν¬) |
torch.Tensor.grad_fn |
torch::Tensor::grad_fn (λ§ν¬) |
torch.Tensor.set_data |
torch::Tensor::set_data (λ§ν¬) |
torch.Tensor.data |
torch::Tensor::data (λ§ν¬) |
torch.Tensor.output_nr |
torch::Tensor::output_nr (λ§ν¬) |
torch.Tensor.is_leaf |
torch::Tensor::is_leaf (λ§ν¬) |
λλΆλΆμ λ³νλ νμ΄μ¬ μλ λ―ΈλΆ μ½λκ° C++μμλ μ λμν κ²μ λλ€. λμνμ§ μμ κ²½μ°, GitHub issues μ λ²κ·Έ 리ν¬νΈλ₯Ό μ μΆν΄ μ£Όμλ©΄ μ΅λν 빨리 κ³ μ³λλ¦¬κ² μ΅λλ€.
μ΄μ PyTorchμ C++ μλ λ―ΈλΆ APIμ λν κ°κ΄μ μΈ μ΄ν΄κ° μκ²Όμ κ²μ λλ€. μ¬κΈ°μ μ¬μ©λ μ½λ μμ λ€μ μ¬κΈ° μμ νμΈν μ μμ΅λλ€. μΈμ λ κ·Έλ λ―μ΄ μ΄λ€ λ¬Έμ κ° μκΈ°κ±°λ μ§λ¬Έμ΄ μμΌλ©΄ μ ν¬ ν¬λΌ μ μ΄μ©νκ±°λ Github μ΄μ λ‘ μ°λ½μ£ΌμΈμ.