CNTK Обучение низкому использованию GPU через C ++ API - PullRequest
0 голосов
/ 03 февраля 2019

Я написал довольно стандартный код CNTK для обучения простой сети с прямой связью с использованием API C ++.Во время обучения я ускоряюсь примерно в 4 раза, используя GPU над CPU (CPU используется на 100%), но это практически с нулевым использованием GPU во время обучения, когда я использую GPU.Используя API CNTK C ++, как можно увеличить использование графического процессора?Мои результаты хороши, но кажутся слишком медленными.

Я использую CNTK на недавнем Lenovo, Windows 10, Intel CPU, Quadro P3200.Я применяю CNTK к данным в памяти, сгенерированным на лету в моей программе.Простой набор данных с 2 скалярными функциями и 1 скалярной меткой, скажем, 10 000 элементов.Данные фактически получены из моделирования Монте-Карло.Поскольку нет API чтения C ++ для данных в памяти, я использую API Value :: CreateSequence для создания данных для передачи тренеру.Обратите внимание, что данные создаются на карте, только для чтения (при условии, что параметром «device» является GPU (0)), чтобы уменьшить передачу хоста / карты.Структуры данных изначально являются собственными матрицами C ++.Я использую ученика Нестерова в примере кода, но пробовал SGD и SGD с такими же результатами.Все кальки используют float, который должен быть быстрым на Quadro.

void RMLearning::OLSDeepLearning(const MatrixXd& X, const MatrixXd& Y, const DeviceDescriptor& device)
{

    auto inputVarName = L"features";
    auto inputVar = InputVariable({ m_inputDim }, DataType::Float, inputVarName);
    auto regressionOutput = FullyConnectedFeedForwardRegressionNet(inputVar, m_outputDim, m_hiddenLayersDim, m_numHiddenLayers, device, m_nonLinearity, L"regressionOutput");

    auto labelsVarName = L"Labels";
    auto labelsVar = InputVariable({ m_outputDim }, DataType::Float, labelsVarName);


    auto trainingLoss = ReduceSum(CNTK::SquaredError(regressionOutput, labelsVar, L"SquaredErrorLossFunction"), Axis::AllAxes(), L"SquaredErrorLossFunction");

    if (m_SaveAndReLoadModel)
        SaveReloadESModel(regressionOutput, trainingLoss, inputVar, labelsVar, device, inputVarName, labelsVarName);
    m_prediction = regressionOutput;

    ProgressWriterPtr pw = MakeSharedObject<MyProgressWriter>(0, 0, 0, 0, 0, 0);
    // Nesterov learner (SGD with momentum) 
    m_learner = MomentumSGDLearner(regressionOutput->Parameters(), m_learningRate, m_Momentum, true);
    m_trainer = CreateTrainer(regressionOutput, trainingLoss, m_prediction, { m_learner }, { pw });

    size_t numSamples = X.rows();
    m_inputData.resize(m_inputDim * numSamples);
    m_labelData.resize(m_outputDim * numSamples, 0);
    m_olsFittedValues.resize(numSamples);

    flattenXYData(numSamples, X, Y);

    NDShape inputShape({ m_inputDim });
    auto dim = inputShape.Dimensions();
    ValuePtr inputValue = Value::CreateSequence(inputShape, m_inputData, device,true);

    NDShape labelShape({ m_outputDim });
    ValuePtr labelValue = Value::CreateSequence(labelShape, m_labelData, device,true);

    ValuePtr outputValue, predictionErrorValue;

    //main training loop
    //at the moment CNTK C++ API lacks "reader" functions for taking data from memory.
    //the reader support the automatic minibatch extraction from full batch.
    //Here we are training full batch.


    if (m_printTrainingProgress)  std::cout << "OLS learning..." << std::endl;
    for (size_t i = 0; i < m_iterationCount; ++i)
    {
        m_trainer->TrainMinibatch({ {inputVar, inputValue}, {labelsVar, labelValue} }, device);
        if (m_printTrainingProgress) m_trainer->SummarizeTrainingProgress();
    }



    //record fitted training weights
    getTrainingWeightsVaR();
    //get fitted values and VaR weights
    EvaluationSequenceUsingDense(m_trainer->EvaluationFunction(), m_inputData, m_olsFittedValues, false, device);

    if (m_printTrainingSummary)
    {
        std::unordered_map<Variable, ValuePtr> tmap = { {inputVar, inputValue} };
        m_trainer->TestMinibatch(tmap, device, false);
        m_trainer->SummarizeTestProgress();
    }

}



void RMLearning::flattenXYData(const size_t &numSamples, const Eigen::MatrixXd & X, const Eigen::MatrixXd & Y)
{
    //map X matrix to input data vector
    size_t j = 0;
    for (size_t i1 = 0; i1 < numSamples; ++i1)
    {
        for (size_t i2 = 0; i2 < m_inputDim; ++i2)
        {
            m_inputData[j] = (float)X(i1, i2);
            j++;
        }
    }

    //map Y matrix to label data vector
    j = 0;
    for (size_t i1 = 0; i1 < numSamples; ++i1)
    {
        for (size_t i2 = 0; i2 < m_outputDim; ++i2)
        {
            m_labelData[j] = (float)Y(i1, i2);
            j++;
        }
    }
}

//Neural network structure setup
inline FunctionPtr RMLearning::FullyConnectedFeedForwardRegressionNet(Variable input,
    size_t outputLayerDim,
    size_t hiddenLayerDim,
    size_t numHiddenLayers,
    const DeviceDescriptor& device,
    const std::function<FunctionPtr(const FunctionPtr&)>& nonLinearity,
    const std::wstring& outputName,
    unsigned long seed)
{
    assert(numHiddenLayers >= 1);
    auto regressionRoot = FullyConnectedDNNLayer(input, hiddenLayerDim, device, nonLinearity, L"", seed);
    for (size_t i = 1; i < numHiddenLayers; ++i)
        regressionRoot = FullyConnectedDNNLayer(regressionRoot, hiddenLayerDim, device, nonLinearity, L"", seed);

    //output layer
    regressionRoot = FullyConnectedLinearLayer(regressionRoot, outputLayerDim, device, outputName, seed);
    return regressionRoot;
}

С NN, скажем, 3 скрытых слоя dim 20, тренировка полных партий, скажем, 20000 записей на 1000 итераций занимает около 2 секунд с использованием графического процессора (с почти нулевым использованием) и 8 секунд с использованием процессора (6-ядерное использование Intel на 100%).Учитывая, что CNTK защищает разработчика от большинства низкоуровневых деталей CUDA, он предоставляет какой-либо API или параметры, которые могут использоваться для увеличения использования?В моей ситуации, вероятно, GPU ждет чего-то на CPU?Какие стратегии другие нашли полезными?

...