Я пытаюсь разработать генератор текста на C ++, используя CNTK.
Я основан на коде Python, который хорошо работает:
# Sample from the network
def sample(root, ix_to_char, vocab_dim, char_to_ix, prime_text='',
use_hardmax=True, length=100, temperature=1.0):
def sample_word(p):
w = np.argmax(p, axis=2)[0,0]
return w
plen = 1
prime = -1
# start sequence with first input
x = np.zeros((1, vocab_dim), dtype=np.float32)
if prime_text != '':
plen = len(prime_text)
prime = char_to_ix[prime_text[0]]
else:
prime = np.random.choice(range(vocab_dim))
x[0, prime] = 1
arguments = ([x], [True])
# setup a list for the output characters and add the initial prime text
output = []
output.append(prime)
# loop through prime text
for i in range(plen):
p = root.eval(arguments)
# reset
x = np.zeros((1, vocab_dim), dtype=np.float32)
if i < plen-1:
idx = char_to_ix[prime_text[i+1]]
else:
idx = sample_word(p)
output.append(idx)
x[0, idx] = 1
arguments = ([x], [False])
# loop through length of generated text, sampling along the way
for i in range(length-plen):
p = root.eval(arguments)
idx = sample_word(p)
output.append(idx)
x = np.zeros((1, vocab_dim), dtype=np.float32)
x[0, idx] = 1
arguments = ([x], [False])
# return output
return ''.join([ix_to_char[c] for c in output])
Я разработал следующий код на C ++:
void EvaluationSingleSequenceUsingOneHotPoem(const wchar_t* modelFile, const wchar_t* vocabularyFile, const DeviceDescriptor& device)
{
// Load the model.
FunctionPtr modelFunc = Function::Load(modelFile, device);
// Get input variable. The model has only one single input.
Variable inputVar = modelFunc->Arguments()[0];
size_t vocabSize = inputVar.Shape().TotalSize();
std::string inputSentence = "Т";
std::vector<size_t> seqData;
size_t index;
// build one-hot index for the input sequence.
for (auto word:inputSentence)
{
index = vocabToIndex.at(std::string(1, word));
seqData.push_back(index);
}
bool seqStartFlag = true;
for (int i = 0; i < 100; i++) {
// Create input value using one-hot vector and input data map
ValuePtr inputVal = Value::CreateSequence<float>(vocabSize, seqData, seqStartFlag, device);
std::unordered_map<Variable, ValuePtr> inputDataMap = { { inputVar, inputVal } };
// The model has only one output.
Variable outputVar = modelFunc->Output();
// Create output data map.
ValuePtr outputVal;
std::unordered_map<Variable, ValuePtr> outputDataMap = { { outputVar, outputVal } };
// Start evaluation on the device
modelFunc->Evaluate(inputDataMap, outputDataMap, device);
// Get evaluate result as dense output
outputVal = outputDataMap[outputVar];
std::vector<std::vector<float>> outputData;
outputVal->CopyVariableValueTo(outputVar, outputData);
}
}
Вывод точно такой же, как в Python при первом проходе. Но все по-другому, начиная со второго.
Пожалуйста, помогите найти ошибку или приведите пример LSTM с использованием CNTK C ++.
Насколько я понимаю, есть различия между Python и C ++ API:
1) в Python — только следующий вход должен быть отправлен в метод оценки; в C ++ — вся последовательность должна быть отправлена в метод CreateSequence;
2) в Python — результатом метода оценки является только следующий вывод; в C ++ — выход метода CopyVariableValueTo представляет собой общее количество выходных данных первого вызова, и требуется только последний слот.
Мой окончательный код:
void EvaluationSingleSequenceUsingOneHotPoem(const wchar_t* modelFile, const wchar_t* vocabularyFile, const DeviceDescriptor& device)
{
// Load the model.
FunctionPtr modelFunc = Function::Load(modelFile, device);
// Get input variable. The model has only one single input.
Variable inputVar = modelFunc->Arguments()[0];
size_t vocabSize = inputVar.Shape().TotalSize();
std::string inputSentence = "Т";
std::vector<size_t> seqData;
size_t index;
// build one-hot index for the input sequence.
for (auto word:inputSentence)
{
inputWords.push_back(std::string(1,word));
index = vocabToIndex.at(std::string(1, word));
seqData.push_back(index);
}
// Create input value using one-hot vector and input data map
ValuePtr inputVal = Value::CreateSequence<float>(vocabSize, seqData, device);
std::unordered_map<Variable, ValuePtr> inputDataMap = { { inputVar, inputVal } };
// The model has only one output.
Variable outputVar = modelFunc->Output();
// Create output data map. Using null as Value to indicate using system allocated memory.
std::unordered_map<Variable, ValuePtr> outputDataMap = { { outputVar, nullptr } };
// Start evaluation on the device
modelFunc->Evaluate(inputDataMap, outputDataMap, device);
for (int i = 0; i < 100; i++) {
// Get evaluate result as dense output
ValuePtr outputVal = outputDataMap[outputVar];
std::vector<std::vector<float>> outputData;
outputVal->CopyVariableValueTo(outputVar, outputData);
// output the result
size_t outputSampleSize = outputVar.Shape().TotalSize();
std::vector<float> slotSeq = outputData[0];
size_t numOfSlotsInOutput = slotSeq.size() / outputSampleSize;
size_t slot = numOfSlotsInOutput - 1;
float max = slotSeq[slot * outputSampleSize];
size_t maxIndex = 0;
for (size_t j = 1; j < outputSampleSize; j++)
{
if (slotSeq[slot * outputSampleSize + j] > max)
{
max = slotSeq[slot * outputSampleSize + j];
maxIndex = j;
}
}
printf("%s", indexToSlots[maxIndex].c_str());
seqData.push_back(maxIndex);
inputWords.push_back(indexToSlots[maxIndex]);
// Create input value using one-hot vector and input data map
inputVal = Value::CreateSequence<float>(vocabSize, seqData, device);
inputDataMap = { { inputVar, inputVal } };
outputDataMap = { { outputVar, nullptr } };
// Start evaluation on the device
modelFunc->Evaluate(inputDataMap, outputDataMap, device);
}
printf("\n");
}
Других решений пока нет …