Я написал довольно стандартный код 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?Какие стратегии другие нашли полезными?