Реализация нейронной сети в DirectML и застревание в обратном распространении ошибкиC++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Реализация нейронной сети в DirectML и застревание в обратном распространении ошибки

Сообщение Anonymous »

Я пытаюсь построить обучение на основе MNIST в DirectML.
Итак, оператор кода прямого распространения выглядит в ЦП следующим образом:

Код: Выделить всё

Matrix ForwardPropagation(Matrix input)
{
// First layer, put the inputs as flattened
Layers[0].output = input.flatten();

// Next layers, forward
for (size_t i = 1; i < Layers.size(); i++)
{
auto& current = Layers[i];
auto& prev = Layers[i - 1];

if (prev.output.cols() != current.weights.rows())
current.output = (prev.output.flatten() * current.weights);
else
current.output = (prev.output * current.weights);

current.output += current.biases;
if (current.ActType == 0)
current.output.sigmoid();
else
current.output.relu();
}

return  Layers.back().output;
}
Попытка реализовать это как оператор DirectML выглядит следующим образом:

Код: Выделить всё

    MLBUFFER B_InputOp1;
if (1)
{
int nexttid = 0;
std::vector bindings_in;
std::vector bindings_out;
std::vector outputs;

// Input Tensor
B_InputOp1.Create(ml.d3D12Device, dml::InputTensor(graph1, nexttid++, { DML_TENSOR_DATA_TYPE_FLOAT32, { batch,1,1,(unsigned int)data[0].input.cols() * data[0].input.rows()} }));
bindings_in.push_back(B_InputOp1.BindingDesc());

// Weights and Biases
for (size_t i = 1; i < Layers.size() && Layers.size() >= 3; i++)
{
auto& layer = Layers[i];
layer.B_Weights.Create(ml.d3D12Device, (dml::InputTensor(graph1, nexttid++, { DML_TENSOR_DATA_TYPE_FLOAT32, { batch,1,(unsigned int)layer.weights.rows(),(unsigned int)layer.weights.cols()} })));
bindings_in.push_back(layer.B_Weights.BindingDesc());

// If MNIST Hidden, [batch,1,784,128] 401408
// If MNIST Output, [batch,1,128,10] 5120

layer.B_Biases.Create(ml.d3D12Device, (dml::InputTensor(graph1, nexttid++, { DML_TENSOR_DATA_TYPE_FLOAT32, { batch,1,1,(unsigned int)layer.biases.cols()} })));
bindings_in.push_back(layer.B_Biases.BindingDesc());

// If MNIST Hidden, [batch,1,1,128] 512
// If MNIST Output, [batch,1,1,10] 40
}

// Create Forward Propagation Operators
for (int i = 0; i < Layers.size() ; i++)
{
auto& layer = Layers[i];
if (i == 0) // input layer -> first hidden
{
layer.B_Outputs.Create(ml.d3D12Device, dml::Identity(B_InputOp1.ee));
bindings_out.push_back(layer.B_Outputs.BindingDesc());
// If MNIST, [1,1,1,784] 3136
}
else
{
auto& pl = Layers[i - 1];
auto mul1 = dml::Gemm(pl.B_Outputs.ee, layer.B_Weights.ee);  // batch,1,1,128 if mnist hidden, batch,1,1,10 if mnist output
auto add1 = dml::Add(mul1, layer.B_Biases.ee);
if (layer.ActType == 1) // Relu
layer.B_Outputs.Create(ml.d3D12Device, dml::ActivationRelu(add1));
if (layer.ActType == 0) // sigmoid
layer.B_Outputs.Create(ml.d3D12Device, dml::ActivationSigmoid(add1));
bindings_out.push_back(layer.B_Outputs.BindingDesc());
// If MNIST Hidden, [batch,1,1,128] 512
// If MNIST Output, [batch,1,1,10] 40
}
// We want the outputs as output
outputs.push_back(layer.B_Outputs.ee);
}

// Create the operator
auto OutputCompiledOperator2 = graph1.Compile(DML_EXECUTION_FLAG_ALLOW_HALF_PRECISION_COMPUTATION, outputs);
MLOP op2;
op2.dmlCompiledOperator.Attach(OutputCompiledOperator2.Detach());
op2.bindings_out = bindings_out;
op2.bindings_in = bindings_in;
ml.ops.push_back(op2);
}
Это работает. Когда я выполняю оператор в D3D12, он создает выходные данные в тензоре «Выход» выходного слоя, аналогичные результатам вычислений ЦП.
Класс MLBUFFER — это класс, который просто оборачивает ID3D12Resource как буфер и предоставляет методы загрузки и выгрузки.
Код BP в ЦП выглядит следующим образом:

Код: Выделить всё

    void BackPropagation(Matrix label)
{
auto OurOutput = Layers.back().output; // 1x10

// Calculation of the derivation of MSE, 2 is ignored for simplicity because it doesn't affect gradient descent
auto delta = OurOutput - label; // 1x10

for (int i = (int)(Layers.size() - 1); i > 0; i--)
{
Layer& curr = Layers[i];
Layer& prev = Layers[i - 1];

// biased += σ'(z) , delta = 1x10,
curr.biases += delta * ((float)-curr.lr);

// weights += prev.Y.T * σ'(z)
Matrix gradient = (prev.output.transpose() * delta); // 128x10, 784x128

/*              float clip_value = 5.0f;
for (auto& value : gradient.data()) {
value = std::max(-clip_value, std::min(value, clip_value));
}
// to be implemented later
*/
curr.weights += gradient * ((float)-curr.lr);

// σ'(z) = σ(z) * (1 - σ(z))
Matrix one = Matrix(prev.output.rows(), prev.output.cols(), 1); // 1x128 ,1x784

// Sigmoid Derivative
Matrix der = prev.output; // 1x128, 1x784
if (prev.ActType == 0)
der.sigmoid_derivative();
else
der.relu_derivative();
// delta = (delta * prev.W.T) x σ'(z);
// This multiplication implements the chain rule, combining the loss gradient with the derivative of the activation function for each layer.
if (i > 1) // don't calculate for the first layer
delta = (delta * curr.weights.transpose()).multiply_inplace(der); // 1x128 second time
}
}
Когда я пытаюсь реализовать это в DirectML, кажется, что это повреждает мои буферы, и я не знаю, что происходит не так.

Код: Выделить всё

        // Backward propagation operator
dml::Expression T_InputOp2;
MLBUFFER B_Final;
MLBUFFER B_Label;
if (1)
{
int nexttid = 0;
std::vector bindings_in;
std::vector bindings_out;
std::vector outputs;

// Input Tensor
T_InputOp2 = dml::InputTensor(graph2, nexttid++, { DML_TENSOR_DATA_TYPE_FLOAT32, Layers.back().B_Outputs.GetOutputDesc().sizes });
bindings_in.push_back(Layers.back().B_Outputs.BindingDesc());

// Label Tensor
B_Label.Create(ml.d3D12Device, dml::InputTensor(graph2, nexttid++, { DML_TENSOR_DATA_TYPE_FLOAT32, Layers.back().B_Outputs.GetOutputDesc().sizes }));
bindings_in.push_back(B_Label.BindingDesc());

// Delta Loop
Layers.back().T_Delta = dml::Subtract(T_InputOp2, B_Label.ee);
for (int i = (int)(Layers.size() - 1); i >= 0; i--)
{
Layer& curr = Layers[i];
if (i == 0)
{
B_Final.Create(ml.d3D12Device, dml::Identity(curr.T_Delta));
bindings_out.push_back(B_Final.BindingDesc());
break;
}

Layer& prev = Layers[i - 1];

// biased += σ'(z)
// curr.biases += delta * ((float)-curr.lr);
auto bp_mincurrlr = ml.ConstantValueTensor(graph2, (float)-curr.lr, curr.T_Delta.GetOutputDesc().sizes);
auto bp_mul2 = dml::Multiply(curr.T_Delta, bp_mincurrlr);

if (curr.T_BiasesBPIn.Impl() == 0)
{
curr.T_BiasesBPIn = (dml::InputTensor(graph2, nexttid++, { DML_TENSOR_DATA_TYPE_FLOAT32, curr.B_Biases.ee.GetOutputDesc().sizes }));
bindings_in.push_back(curr.B_Biases.BindingDesc());
}
curr.B_BiasesOut.Create(ml.d3D12Device, dml::Add(curr.T_BiasesBPIn, bp_mul2));
bindings_out.push_back(curr.B_BiasesOut.BindingDesc());

// curr.weights += gradient * ((float)-curr.lr);  // Gradient will be first  128x10,  second 784x128 for MNIST
// 1,1,1,10, 1,1,1,128
// Matrix gradient = (prev.output.transpose() * delta); // 128x10, 784x128
if (prev.T_OutputsBPIn.Impl() == 0)
{
prev.T_OutputsBPIn = (dml::InputTensor(graph2, nexttid++, { DML_TENSOR_DATA_TYPE_FLOAT32, prev.B_Outputs.GetOutputDesc().sizes }));
bindings_in.push_back(prev.B_Outputs.BindingDesc());
}
curr.bp_PrevOutputTranspose = dml::Reinterpret(prev.T_OutputsBPIn, DML_TENSOR_DATA_TYPE_FLOAT32, { 1,1,(unsigned int)prev.T_OutputsBPIn.GetOutputDesc().sizes[3],(unsigned int)prev.T_OutputsBPIn.GetOutputDesc().sizes[2] }, dml::NullOpt);
curr.bp_gradient = dml::Gemm(curr.bp_PrevOutputTranspose, curr.T_Delta); // 128x10, 784x128 for MNIST
curr.bp_mincurrlr2 = ml.ConstantValueTensor(graph2, (float)-curr.lr, curr.bp_gradient.GetOutputDesc().sizes);
auto bp_mul3 = dml::Multiply(curr.bp_gradient, curr.bp_mincurrlr2);
if (curr.T_WeightsBPIn.Impl() == 0)
{
curr.T_WeightsBPIn = (dml::InputTensor(graph2, nexttid++, { DML_TENSOR_DATA_TYPE_FLOAT32,curr.B_Weights.ee.GetOutputDesc().sizes }));
bindings_in.push_back(curr.B_Weights.BindingDesc());
}
curr.B_WeightsOut.Create(ml.d3D12Device, dml::Add(curr.T_WeightsBPIn, bp_mul3));
bindings_out.push_back(curr.B_WeightsOut.BindingDesc());

// 1,1,128,10, 1,1,784,128

//                  curr.bp_one = ml.ConstantValueTensor(graph3, 1.0f, { 1,1,prev.T_OutputsBP.GetOutputDesc().sizes[2],prev.T_OutputsBP.GetOutputDesc().sizes[3] });
auto bp_der1 = dml::Identity(prev.T_OutputsBPIn);

if (prev.ActType == 0)
{
// Sigmoid Derivative
auto bp_dx = dml::ActivationSigmoid(bp_der1);
auto bp_one1 = ml.ConstantValueTensor(graph2, 1.0f, bp_dx.GetOutputDesc().sizes);
auto bp_oneMinusSigmoid = dml::Subtract(bp_one1, bp_dx);
curr.bp_der2 = dml::Multiply(bp_dx, bp_oneMinusSigmoid);
}
else
{
// Relu derivative
auto bp_dx = dml::ActivationRelu(bp_der1);
auto bp_zeroTensor = ml.ConstantValueTensor(graph2, 0.0f, bp_dx.GetOutputDesc().sizes);
auto bp_reluMask = dml::GreaterThan(bp_dx, bp_zeroTensor);
curr.bp_der2 = dml::Cast(bp_reluMask, DML_TENSOR_DATA_TYPE_FLOAT32);
}

// Calculate delta again
auto bp_CurrWT = dml::Reinterpret(curr.B_WeightsOut.ee, DML_TENSOR_DATA_TYPE_FLOAT32, { 1,1,(unsigned int)curr.B_WeightsOut.ee.GetOutputDesc().sizes[3],(unsigned int)curr.B_WeightsOut.ee.GetOutputDesc().sizes[2] }, dml::NullOpt);
auto bp_Delta2 = dml::Gemm(curr.T_Delta, bp_CurrWT);
auto bp_Delta3 = dml::Multiply(bp_Delta2, curr.bp_der2);
prev.T_Delta = dml::Identity(bp_Delta3);

}

// Create the operator
for (int i = (int)(Layers.size() - 1); i >= 0; i--)
{
auto& l = Layers[i];
if (l.B_BiasesOut.b)
outputs.push_back(l.B_BiasesOut.ee);
if (l.B_WeightsOut.b)
outputs.push_back(l.B_WeightsOut.ee);
}
outputs.push_back(B_Final.ee);

auto OutputCompiledOperator2 = graph2.Compile(DML_EXECUTION_FLAG_ALLOW_HALF_PRECISION_COMPUTATION, outputs);
MLOP op2;
op2.dmlCompiledOperator.Attach(OutputCompiledOperator2.Detach());
op2.bindings_out = bindings_out;
op2.bindings_in = bindings_in;
ml.ops.push_back(op2);
}
После выполнения этого оператора вещь не работает, повредились буферы. Я не знаю, как это отладить, кроме ручной настройки пользовательских буферов вывода для второго оператора и их проверки, но даже после выполнения этих двух строк:

Код: Выделить всё

            Layer& prev = Layers[i - 1];
// biased += σ'(z)
// curr.biases += delta * ((float)-curr.lr);
auto bp_mincurrlr = ml.ConstantValueTensor(graph2, (float)-curr.lr, curr.T_Delta.GetOutputDesc().sizes);
auto bp_mul2 = dml::Multiply(curr.T_Delta, bp_mincurrlr);
буфер вывода поврежден.
Буду признателен за ваш совет.

Подробнее здесь: https://stackoverflow.com/questions/793 ... ropagation
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»