MachineLearningDebugTrainingWindow.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Source/Debug/MachineLearningDebugTrainingWindow.h>
  9. #include <Source/Assets/MnistDataLoader.h>
  10. #include <Source/Algorithms/Activations.h>
  11. #include <AzCore/Console/IConsole.h>
  12. #include <AzCore/Console/ILogger.h>
  13. #ifdef IMGUI_ENABLED
  14. # include <ImGuiContextScope.h>
  15. # include <ImGui/ImGuiPass.h>
  16. # include <imgui/imgui.h>
  17. # include <imgui/imgui_internal.h>
  18. #endif
  19. namespace MachineLearning
  20. {
  21. AZ_CVAR(bool, ml_logAccuracyValues, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Dumps the actual and expected labels during accuracy calculations");
  22. #ifdef IMGUI_ENABLED
  23. int32_t AZStdStringResizeCallback(ImGuiInputTextCallbackData* data)
  24. {
  25. if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
  26. {
  27. AZStd::string* azString = (AZStd::string*)data->UserData;
  28. AZ_Assert(azString->begin() == data->Buf, "Invalid type");
  29. azString->resize(data->BufSize);
  30. data->Buf = azString->begin();
  31. }
  32. return 0;
  33. }
  34. void TextInputHelper(const char* label, AZStd::string& data)
  35. {
  36. ImGui::InputText(label, data.begin(), data.size(), ImGuiInputTextFlags_CallbackResize, AZStdStringResizeCallback, (void*)(&data));
  37. }
  38. MachineLearningDebugTrainingWindow::~MachineLearningDebugTrainingWindow()
  39. {
  40. for (auto iter : m_trainingInstances)
  41. {
  42. delete iter.second;
  43. }
  44. }
  45. TrainingInstance* MachineLearningDebugTrainingWindow::RetrieveTrainingInstance(INeuralNetworkPtr modelPtr)
  46. {
  47. TrainingInstance* trainingInstance = m_trainingInstances[m_selectedModel];
  48. if (trainingInstance == nullptr)
  49. {
  50. m_trainingInstances[m_selectedModel] = new TrainingInstance();
  51. trainingInstance = m_trainingInstances[m_selectedModel];
  52. trainingInstance->m_trainingCycle.m_model = m_selectedModel;
  53. trainingInstance->m_testHistogram.Init("Test Cost", 250, ImGui::LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.2f, ImGui::LYImGuiUtils::HistogramContainer::AutoExpand);
  54. trainingInstance->m_testHistogram.SetMoveDirection(ImGui::LYImGuiUtils::HistogramContainer::PushRightMoveLeft);
  55. trainingInstance->m_trainHistogram.Init("Train Cost", 250, ImGui::LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.2f, ImGui::LYImGuiUtils::HistogramContainer::AutoExpand);
  56. trainingInstance->m_trainHistogram.SetMoveDirection(ImGui::LYImGuiUtils::HistogramContainer::PushRightMoveLeft);
  57. trainingInstance->m_testDataName = m_selectedModel->GetAssetFile(AssetTypes::TestData);
  58. trainingInstance->m_testLabelName = m_selectedModel->GetAssetFile(AssetTypes::TestLabels);
  59. trainingInstance->m_trainDataName = m_selectedModel->GetAssetFile(AssetTypes::TrainingData);
  60. trainingInstance->m_trainLabelName = m_selectedModel->GetAssetFile(AssetTypes::TrainingLabels);
  61. }
  62. return trainingInstance;
  63. }
  64. void MachineLearningDebugTrainingWindow::LoadTestTrainData(TrainingInstance* trainingInstance)
  65. {
  66. if (!trainingInstance->m_trainingCycle.m_trainData.IsValid())
  67. {
  68. ILabeledTrainingDataPtr dataPtr = AZStd::make_shared<MnistDataLoader>();
  69. trainingInstance->m_trainingCycle.m_trainData.SetSourceData(dataPtr);
  70. trainingInstance->m_trainingCycle.m_trainData.LoadArchive(trainingInstance->m_trainDataName, trainingInstance->m_trainLabelName);
  71. }
  72. if (!trainingInstance->m_trainingCycle.m_testData.IsValid())
  73. {
  74. ILabeledTrainingDataPtr dataPtr = AZStd::make_shared<MnistDataLoader>();
  75. trainingInstance->m_trainingCycle.m_testData.SetSourceData(dataPtr);
  76. trainingInstance->m_trainingCycle.m_testData.LoadArchive(trainingInstance->m_testDataName, trainingInstance->m_testLabelName);
  77. }
  78. }
  79. void LogVectorN(const AZ::VectorN& value, const char* label)
  80. {
  81. AZStd::string vectorString(label);
  82. for (AZStd::size_t iter = 0; iter < value.GetDimensionality(); ++iter)
  83. {
  84. vectorString += AZStd::string::format(" %.02f", value.GetElement(iter));
  85. }
  86. AZLOG_INFO(vectorString.c_str());
  87. }
  88. void MachineLearningDebugTrainingWindow::RecalculateAccuracy(TrainingInstance* trainingInstance, ILabeledTrainingData& data)
  89. {
  90. trainingInstance->m_trainingCycle.InitializeContexts();
  91. trainingInstance->m_totalSamples = static_cast<int32_t>(data.GetSampleCount());
  92. trainingInstance->m_correctPredictions = 0;
  93. trainingInstance->m_incorrectPredictions = 0;
  94. for (int32_t iter = 0; iter < trainingInstance->m_totalSamples; ++iter)
  95. {
  96. const AZ::VectorN& activations = data.GetDataByIndex(iter);
  97. const AZ::VectorN& label = data.GetLabelByIndex(iter);
  98. const AZ::VectorN* output = m_selectedModel->Forward(trainingInstance->m_trainingCycle.m_inferenceContext.get(), activations);
  99. AZStd::size_t prediction = ArgMaxDecode(*output);
  100. AZStd::size_t actual = ArgMaxDecode(label);
  101. if (prediction == actual)
  102. {
  103. ++trainingInstance->m_correctPredictions;
  104. }
  105. else
  106. {
  107. ++trainingInstance->m_incorrectPredictions;
  108. }
  109. if (ml_logAccuracyValues)
  110. {
  111. LogVectorN(label, "Actual");
  112. LogVectorN(*output, "Output");
  113. }
  114. }
  115. }
  116. void DrawDataPanel(TrainingDataView& data, AZStd::string& dataName, AZStd::string& labelName)
  117. {
  118. ImGui::PushID(&data);
  119. int32_t firstElement = static_cast<int32_t>(data.m_first);
  120. int32_t span = static_cast<int32_t>(data.m_last) - firstElement;
  121. TextInputHelper("Asset file", dataName);
  122. TextInputHelper("Label file", labelName);
  123. ImGui::SliderInt("First", &firstElement, 0, static_cast<int32_t>(data.GetOriginalSize()));
  124. ImGui::SameLine();
  125. ImGui::SliderInt("Count", &span, 0, static_cast<int32_t>(data.GetOriginalSize() - firstElement));
  126. data.m_first = firstElement;
  127. data.m_last = firstElement + span;
  128. ImGui::PopID();
  129. }
  130. void MachineLearningDebugTrainingWindow::OnImGuiUpdate()
  131. {
  132. const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
  133. const ImGuiTableFlags flags = ImGuiTableFlags_BordersV
  134. | ImGuiTableFlags_BordersOuterH
  135. | ImGuiTableFlags_Resizable
  136. | ImGuiTableFlags_RowBg
  137. | ImGuiTableFlags_NoBordersInBody;
  138. IMachineLearning* machineLearning = MachineLearningInterface::Get();
  139. const ModelSet& modelSet = machineLearning->GetModelSet();
  140. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
  141. ImGui::BeginChild("LeftPanel", ImVec2(m_trainingSplitWidth, -1.0f), true);
  142. if (ImGui::BeginTable("Models", 1, flags))
  143. {
  144. ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
  145. ImGui::TableHeadersRow();
  146. AZStd::size_t index = 0;
  147. for (auto& neuralNetwork : modelSet)
  148. {
  149. ImGui::TableNextRow();
  150. ImGui::TableNextColumn();
  151. const bool isSelected = (m_selectedModelIndex == index);
  152. if (ImGui::Selectable(neuralNetwork->GetName().c_str(), isSelected))
  153. {
  154. m_selectedModel = neuralNetwork;
  155. m_selectedModelIndex = index;
  156. }
  157. ++index;
  158. }
  159. ImGui::EndTable();
  160. ImGui::NewLine();
  161. }
  162. ImGui::EndChild();
  163. ImGui::SameLine();
  164. ImGui::InvisibleButton("vsplitter", ImVec2(8.0f, -1.0f));
  165. if (ImGui::IsItemActive())
  166. {
  167. m_trainingSplitWidth += ImGui::GetIO().MouseDelta.x;
  168. }
  169. ImGui::SameLine();
  170. ImGui::BeginChild("RightPanel", ImVec2(0.0f, -1.0f), true);
  171. if (m_selectedModel != nullptr)
  172. {
  173. ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 4.0f));
  174. TrainingInstance* trainingInstance = RetrieveTrainingInstance(m_selectedModel);
  175. {
  176. AZStd::deque<float> costs;
  177. trainingInstance->m_trainingCycle.m_testCosts.Swap(costs);
  178. for (float item : costs)
  179. {
  180. trainingInstance->m_testHistogram.PushValue(item);
  181. }
  182. }
  183. {
  184. AZStd::deque<float> costs;
  185. trainingInstance->m_trainingCycle.m_trainCosts.Swap(costs);
  186. for (float item : costs)
  187. {
  188. trainingInstance->m_trainHistogram.PushValue(item);
  189. }
  190. }
  191. int32_t batchSize = static_cast<int32_t>(trainingInstance->m_trainingCycle.m_batchSize);
  192. int32_t totalIterations = static_cast<int32_t>(trainingInstance->m_trainingCycle.m_totalIterations);
  193. if (!trainingInstance->m_trainingCycle.m_trainingComplete)
  194. {
  195. if (ImGui::Button("Stop training"))
  196. {
  197. trainingInstance->m_trainingCycle.StopTraining();
  198. }
  199. ImGui::SameLine();
  200. int32_t epoch = static_cast<int32_t>(trainingInstance->m_trainingCycle.m_currentEpoch);
  201. ImGui::Text("Epoch: %d", epoch);
  202. }
  203. else
  204. {
  205. if (ImGui::Button("Start training"))
  206. {
  207. LoadTestTrainData(trainingInstance);
  208. trainingInstance->m_testHistogram.Init("Test Cost", totalIterations, ImGui::LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.2f, ImGui::LYImGuiUtils::HistogramContainer::AutoExpand);
  209. trainingInstance->m_trainHistogram.Init("Train Cost", totalIterations, ImGui::LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 0.2f, ImGui::LYImGuiUtils::HistogramContainer::AutoExpand);
  210. trainingInstance->m_trainingCycle.StartTraining();
  211. }
  212. ImGui::SameLine();
  213. if (ImGui::Button("Save"))
  214. {
  215. m_selectedModel->SaveModel();
  216. }
  217. ImGui::SameLine();
  218. if (ImGui::Button("Load"))
  219. {
  220. m_selectedModel->LoadModel();
  221. }
  222. ImGui::SameLine();
  223. if (ImGui::Button("Recalculate accuracy on test data"))
  224. {
  225. LoadTestTrainData(trainingInstance);
  226. RecalculateAccuracy(trainingInstance, trainingInstance->m_trainingCycle.m_testData);
  227. }
  228. ImGui::SameLine();
  229. if (ImGui::Button("Recalculate accuracy on training data"))
  230. {
  231. LoadTestTrainData(trainingInstance);
  232. RecalculateAccuracy(trainingInstance, trainingInstance->m_trainingCycle.m_trainData);
  233. }
  234. }
  235. ImGui::NewLine();
  236. ImGui::Text("Model Name: %s", m_selectedModel->GetName().c_str());
  237. if (ImGui::BeginTable("Accuracy", 2, flags))
  238. {
  239. ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 32.0f);
  240. ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
  241. ImGui::TableHeadersRow();
  242. ImGui::TableNextRow();
  243. ImGui::TableNextColumn();
  244. ImGui::Text("Total samples");
  245. ImGui::TableNextColumn();
  246. ImGui::Text("%d", trainingInstance->m_totalSamples);
  247. ImGui::TableNextRow();
  248. ImGui::TableNextColumn();
  249. ImGui::Text("Correct predictions");
  250. ImGui::TableNextColumn();
  251. ImGui::Text("%d", trainingInstance->m_correctPredictions);
  252. ImGui::TableNextRow();
  253. ImGui::TableNextColumn();
  254. ImGui::Text("Incorrect predictions");
  255. ImGui::TableNextColumn();
  256. ImGui::Text("%d", trainingInstance->m_incorrectPredictions);
  257. ImGui::TableNextRow();
  258. ImGui::TableNextColumn();
  259. ImGui::Text("Accuracy");
  260. ImGui::TableNextColumn();
  261. const float accuracy = (static_cast<float>(trainingInstance->m_correctPredictions) * 100.0f) / static_cast<float>(trainingInstance->m_totalSamples);
  262. ImGui::Text("%f", accuracy);
  263. ImGui::TableNextRow();
  264. ImGui::TableNextColumn();
  265. ImGui::Text("Test score");
  266. ImGui::TableNextColumn();
  267. ImGui::Text("%f", trainingInstance->m_testHistogram.GetLastValue());
  268. ImGui::TableNextRow();
  269. ImGui::TableNextColumn();
  270. ImGui::Text("Train score");
  271. ImGui::TableNextColumn();
  272. ImGui::Text("%f", trainingInstance->m_trainHistogram.GetLastValue());
  273. ImGui::EndTable();
  274. ImGui::NewLine();
  275. }
  276. trainingInstance->m_testHistogram.Draw(ImGui::GetColumnWidth(), 200.0f);
  277. trainingInstance->m_trainHistogram.Draw(ImGui::GetColumnWidth(), 200.0f);
  278. ImGui::NewLine();
  279. ImGui::SliderInt("Batch size", &batchSize, 1, 1000);
  280. trainingInstance->m_trainingCycle.m_batchSize = batchSize;
  281. ImGui::SliderInt("Number of iterations", &totalIterations, 1, 1000);
  282. trainingInstance->m_trainingCycle.m_totalIterations = totalIterations;
  283. int32_t costMetric = static_cast<int32_t>(trainingInstance->m_trainingCycle.m_costFunction);
  284. ImGui::Combo("Cost metric", &costMetric, "MeanSquaredError\0");
  285. trainingInstance->m_trainingCycle.m_costFunction = static_cast<LossFunctions>(costMetric);
  286. ImGui::NewLine();
  287. ImGui::SliderFloat("LearningRate", &trainingInstance->m_trainingCycle.m_learningRate, 0.0f, 0.1f);
  288. ImGui::SliderFloat("LearningRateDecay", &trainingInstance->m_trainingCycle.m_learningRateDecay, 0.0f, 1.0f);
  289. ImGui::SliderFloat("EarlyStop", &trainingInstance->m_trainingCycle.m_earlyStopCost, 0.0f, 1.0f);
  290. ImGui::NewLine();
  291. ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.4f);
  292. ImGui::Checkbox("Shuffle data", &trainingInstance->m_trainingCycle.m_shuffleTrainingData);
  293. if (ImGui::CollapsingHeader("Test data", ImGuiTreeNodeFlags_Framed))
  294. {
  295. DrawDataPanel(trainingInstance->m_trainingCycle.m_testData, trainingInstance->m_testDataName, trainingInstance->m_testLabelName);
  296. }
  297. ImGui::NewLine();
  298. if (ImGui::CollapsingHeader("Train data", ImGuiTreeNodeFlags_Framed))
  299. {
  300. DrawDataPanel(trainingInstance->m_trainingCycle.m_trainData, trainingInstance->m_trainDataName, trainingInstance->m_trainLabelName);
  301. }
  302. ImGui::PopItemWidth();
  303. ImGui::PopStyleVar();
  304. }
  305. ImGui::EndChild();
  306. ImGui::PopStyleVar();
  307. }
  308. #endif
  309. }