Skip to content

Commit ba5885c

Browse files
author
Ori Levari
authored
Merge Debug functionality into master (#206)
add debugging functionality into WinML Dashboard
1 parent b5fac6d commit ba5885c

35 files changed

+1692
-90
lines changed

Tools/WinMLDashboard/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ public/*
3636
!public/convert.py
3737
!public/requirements.txt
3838
!public/ConverterView.png
39+
!public/ConfigureDebug.png
3940
!public/EditorView.png
41+
!public/DebugView.png
4042
!public/progress_bar.gif
4143
!public/WinMLRunner.exe
4244
!public/libsvm-3.22-cp36-cp36m-win_amd64.whl

Tools/WinMLDashboard/README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ Note that model validation feature is only available on [Windows 10 October 2018
6060

6161
<img src="./public/RunView.png" width=800/>
6262

63+
## Debugging Inference
64+
65+
You can utilize the debug feature of WinML Dashboard to gain insight into how raw data is flowing through operators in your model. You can also choose to visualize this data for computer vision inference.
66+
67+
To debug your model follow these steps:
68+
69+
1) Navigate to the **Edit** tab and click on the operator for which you wish to capture intermediate data. On the left side panel there will be a Debug menu where you can select the formats of intermediate data you wish to capture. The options are currently **text** and **png**. **Text** will output a text file containing the dimensions, data type and raw tensor data produced by this operator. **Png** will format this data into an image file which can be useful for computer vision applications.
70+
2) Navigate to the **Run** tab and select the model you wish to debug.
71+
3) For the Capture field, select Debug from the dropdown.
72+
4) Select an input image or csv to supply to your model at execution. Note that this is required when capturing Debug data.
73+
5) Select an output folder to export debug data.
74+
6) Click Run. Once execution is complete you can navigate to this selected folder to view your Debug capture.
75+
76+
<img src="./public/ConfigureDebug.png" width=800/>
77+
<img src="./public/DebugView.png" width=800/>
78+
79+
6380
## Install
6481

6582
You can install an early preview version of WinML Dashboard from here.
@@ -80,8 +97,11 @@ Alternatively, you can build the app from source following the instructions belo
8097
|Yarn |latest |[here](https://yarnpkg.com/en/docs/install)|`yarn --version`|
8198
|Node.js |latest |[here](https://nodejs.org/en/)|`node --version`|
8299
|Git |latest |[here](https://git-scm.com/download/win)|`git --version`|
100+
|MSBuild |latest |[here](https://visualstudio.microsoft.com/downloads/)|`msbuild -version`|
101+
|Nuget |latest |[here](https://www.nuget.org/downloads)|`nuget help`|
102+
83103

84-
> All four prerequisites should be **added to Enviroment Path**.
104+
> All six prerequisites should be **added to Environment Path**. Note that MSBuild and Nuget will be included in a Visual Studio 2017 installation.
85105
86106
#### Steps to build and run
87107

Tools/WinMLDashboard/package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
"postinstall": "node scripts/postinstall.js && yarn copy-netron",
1616
"copy-netron": "yarn --cwd deps/Netron && python deps/copy_netron.py",
1717
"build-electron": "react-app-rewired build --scripts-version react-scripts-ts",
18+
"build-cpp": "msbuild src/cpp/DebugRunner.sln",
19+
"nuget-restore-cpp": "nuget restore src/cpp/DebugRunner.sln",
1820
"electron-dev": "electron . http://localhost:3000",
19-
"electron-prod": "yarn build-electron && electron ."
21+
"electron-prod": "yarn nuget-restore-cpp && yarn build-cpp && yarn build-electron && electron ."
2022
},
2123
"build": {
2224
"appId": "com.microsoft.dashboard.winml",
@@ -30,11 +32,14 @@
3032
}
3133
},
3234
"dependencies": {
35+
"@types/ncp": "^2.0.1",
3336
"electron-log": "^2.2.17",
3437
"electron-squirrel-startup": "^1.0.0"
3538
},
3639
"devDependencies": {
40+
"@types/fs-extra": "^5.0.4",
3741
"@types/jest": "^23.3.0",
42+
"@types/md5": "^2.1.33",
3843
"@types/node": "^10.5.3",
3944
"@types/prop-types": "^15.5.4",
4045
"@types/react": "^16.4.7",
@@ -47,9 +52,12 @@
4752
"babel-minify": "^0.4.3",
4853
"chai": "^4.2.0",
4954
"chai-as-promised": "^7.1.1",
50-
"electron": "^3.0.4",
55+
"electron": "^3.0.11",
5156
"electron-winstaller": "^2.7.0",
57+
"fs-extra": "^7.0.1",
58+
"md5": "^2.2.1",
5259
"mocha": "^5.2.0",
60+
"ncp": "^2.0.0",
5361
"npm-font-open-sans": "^1.1.0",
5462
"office-ui-fabric-react": "^6.40.0",
5563
"react": "^16.5.2",
199 KB
Loading
90.7 KB
Loading
121 KB
Loading

Tools/WinMLDashboard/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as React from 'react';
44
import { initializeIcons } from './fonts';
55
import ConvertView from './view/convert/View';
66
import EditView from './view/edit/View';
7-
import RunView from './view/run/View'
7+
import RunView from './view/run/View';
88

99
import './App.css';
1010

@@ -16,7 +16,7 @@ const views = {
1616
Edit: <EditView />,
1717
// tslint:disable-next-line:object-literal-sort-keys
1818
Convert: <ConvertView />,
19-
Run: <RunView />,
19+
Run: <RunView />
2020
};
2121

2222
class App extends React.Component<{}, IComponentState> {

Tools/WinMLDashboard/src/components/Collapsible.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
transition: 200ms;
1818
transition-timing-function: ease-out;
1919
max-height: 2000px;
20-
overflow: hidden;
20+
overflow: visible;
2121
}
2222

2323
pre {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.28010.2050
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DebugRunner", "DebugRunner\DebugRunner.vcxproj", "{39720242-5380-4546-AD6A-45A4CBECCBBB}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|x64 = Debug|x64
11+
Debug|x86 = Debug|x86
12+
Release|x64 = Release|x64
13+
Release|x86 = Release|x86
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{39720242-5380-4546-AD6A-45A4CBECCBBB}.Debug|x64.ActiveCfg = Debug|x64
17+
{39720242-5380-4546-AD6A-45A4CBECCBBB}.Debug|x64.Build.0 = Debug|x64
18+
{39720242-5380-4546-AD6A-45A4CBECCBBB}.Debug|x86.ActiveCfg = Debug|Win32
19+
{39720242-5380-4546-AD6A-45A4CBECCBBB}.Debug|x86.Build.0 = Debug|Win32
20+
{39720242-5380-4546-AD6A-45A4CBECCBBB}.Release|x64.ActiveCfg = Release|x64
21+
{39720242-5380-4546-AD6A-45A4CBECCBBB}.Release|x64.Build.0 = Release|x64
22+
{39720242-5380-4546-AD6A-45A4CBECCBBB}.Release|x86.ActiveCfg = Release|Win32
23+
{39720242-5380-4546-AD6A-45A4CBECCBBB}.Release|x86.Build.0 = Release|Win32
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
GlobalSection(ExtensibilityGlobals) = postSolution
29+
SolutionGuid = {456E03F7-1DDD-41C2-B3BA-5D7AE43F6154}
30+
EndGlobalSection
31+
EndGlobal
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
#pragma once
2+
#include "Common.h"
3+
#include "ModelBinding.h"
4+
5+
using namespace winrt::Windows::Media;
6+
using namespace winrt::Windows::Storage;
7+
using namespace winrt::Windows::AI::MachineLearning;
8+
9+
namespace BindingUtilities
10+
{
11+
SoftwareBitmap LoadImageFile(const TensorFeatureDescriptor& imageDescriptor, ImageDataType inputDataType, const hstring& filePath)
12+
{
13+
// We assume NCHW and NCDHW
14+
uint64_t width = imageDescriptor.Shape().GetAt(imageDescriptor.Shape().Size() - 1);
15+
uint64_t height = imageDescriptor.Shape().GetAt(imageDescriptor.Shape().Size() - 2);
16+
uint64_t channelCount = imageDescriptor.Shape().GetAt(1);
17+
uint64_t batchCount = imageDescriptor.Shape().GetAt(0);
18+
19+
try
20+
{
21+
// open the file
22+
StorageFile file = StorageFile::GetFileFromPathAsync(filePath).get();
23+
// get a stream on it
24+
auto stream = file.OpenAsync(FileAccessMode::Read).get();
25+
// Create the decoder from the stream
26+
BitmapDecoder decoder = BitmapDecoder::CreateAsync(stream).get();
27+
28+
// If input dimensions are different from tensor input, then scale / crop while reading
29+
if (decoder.PixelHeight() != height ||
30+
decoder.PixelWidth() != width)
31+
{
32+
33+
// Create a transform object with default parameters (no transform)
34+
auto transform = BitmapTransform();
35+
transform.ScaledHeight(static_cast<uint32_t>(height));
36+
transform.ScaledWidth(static_cast<uint32_t>(width));
37+
transform.InterpolationMode(BitmapInterpolationMode::Cubic);
38+
39+
// get the bitmap
40+
return decoder.GetSoftwareBitmapAsync(TypeHelper::GetBitmapPixelFormat(inputDataType),
41+
BitmapAlphaMode::Ignore,
42+
transform,
43+
ExifOrientationMode::RespectExifOrientation,
44+
ColorManagementMode::DoNotColorManage).get();
45+
}
46+
else
47+
{
48+
// get the bitmap
49+
return decoder.GetSoftwareBitmapAsync(TypeHelper::GetBitmapPixelFormat(inputDataType), BitmapAlphaMode::Ignore).get();
50+
}
51+
}
52+
catch (...)
53+
{
54+
std::cout << "BindingUtilities: could not open image file, make sure you are using fully qualified paths." << std::endl;
55+
return nullptr;
56+
}
57+
}
58+
59+
std::vector<std::string> ReadCsvLine(std::ifstream& fileStream)
60+
{
61+
std::vector<std::string> elementStrings;
62+
// Read next line.
63+
std::string line;
64+
if (!std::getline(fileStream, line))
65+
{
66+
ThrowFailure(L"BindingUtilities: expected more input rows.");
67+
}
68+
69+
// Split the line into strings for each value.
70+
std::istringstream elementsString(line);
71+
std::string elementString;
72+
while (std::getline(elementsString, elementString, ','))
73+
{
74+
elementStrings.push_back(elementString);
75+
}
76+
return elementStrings;
77+
}
78+
79+
template <typename T>
80+
void WriteDataToBinding(const std::vector<std::string>& elementStrings, ModelBinding<T>& binding)
81+
{
82+
/*if (binding.GetDataBufferSize() != elementStrings.size())
83+
{
84+
throw hresult_invalid_argument(L"CSV Input is size/shape is different from what model expects");
85+
}*/
86+
T* data = binding.GetData();
87+
for (const auto &elementString : elementStrings)
88+
{
89+
T value;
90+
std::stringstream(elementString) >> value;
91+
*data = value;
92+
data++;
93+
}
94+
}
95+
96+
std::vector<std::string> ParseCSVElementStrings(const std::wstring& csvFilePath)
97+
{
98+
std::ifstream fileStream;
99+
fileStream.open(csvFilePath);
100+
if (!fileStream.is_open())
101+
{
102+
ThrowFailure(L"BindingUtilities: could not open data file.");
103+
}
104+
105+
std::vector<std::string> elementStrings = ReadCsvLine(fileStream);
106+
107+
return elementStrings;
108+
}
109+
110+
// Binds tensor floats, ints, doubles from CSV data.
111+
ITensor CreateBindableTensor(const ILearningModelFeatureDescriptor& description, std::wstring inputPath)
112+
{
113+
auto name = description.Name();
114+
auto tensorDescriptor = description.try_as<TensorFeatureDescriptor>();
115+
116+
if (!tensorDescriptor)
117+
{
118+
std::cout << "BindingUtilities: Input Descriptor type isn't tensor." << std::endl;
119+
throw;
120+
}
121+
122+
std::vector<std::string> elementStrings;
123+
switch (tensorDescriptor.TensorKind())
124+
{
125+
case TensorKind::Undefined:
126+
{
127+
std::cout << "BindingUtilities: TensorKind is undefined." << std::endl;
128+
throw hresult_invalid_argument();
129+
}
130+
case TensorKind::Float:
131+
{
132+
ModelBinding<float> binding(description);
133+
134+
elementStrings = ParseCSVElementStrings(inputPath);
135+
WriteDataToBinding<float>(elementStrings, binding);
136+
return TensorFloat::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
137+
}
138+
break;
139+
case TensorKind::Float16:
140+
{
141+
ModelBinding<float> binding(description);
142+
elementStrings = ParseCSVElementStrings(inputPath);
143+
WriteDataToBinding<float>(elementStrings, binding);
144+
return TensorFloat16Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
145+
}
146+
break;
147+
case TensorKind::Double:
148+
{
149+
ModelBinding<double> binding(description);
150+
elementStrings = ParseCSVElementStrings(inputPath);
151+
WriteDataToBinding<double>(elementStrings, binding);
152+
return TensorDouble::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
153+
}
154+
break;
155+
case TensorKind::Int8:
156+
{
157+
ModelBinding<uint8_t> binding(description);
158+
elementStrings = ParseCSVElementStrings(inputPath);
159+
WriteDataToBinding<uint8_t>(elementStrings, binding);
160+
return TensorInt8Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
161+
}
162+
break;
163+
case TensorKind::UInt8:
164+
{
165+
ModelBinding<uint8_t> binding(description);
166+
elementStrings = ParseCSVElementStrings(inputPath);
167+
WriteDataToBinding<uint8_t>(elementStrings, binding);
168+
return TensorUInt8Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
169+
}
170+
break;
171+
case TensorKind::Int16:
172+
{
173+
ModelBinding<int16_t> binding(description);
174+
elementStrings = ParseCSVElementStrings(inputPath);
175+
WriteDataToBinding<int16_t>(elementStrings, binding);
176+
return TensorInt16Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
177+
}
178+
break;
179+
case TensorKind::UInt16:
180+
{
181+
ModelBinding<uint16_t> binding(description);
182+
elementStrings = ParseCSVElementStrings(inputPath);
183+
WriteDataToBinding<uint16_t>(elementStrings, binding);
184+
return TensorUInt16Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
185+
}
186+
break;
187+
case TensorKind::Int32:
188+
{
189+
ModelBinding<int32_t> binding(description);
190+
elementStrings = ParseCSVElementStrings(inputPath);
191+
WriteDataToBinding<int32_t>(elementStrings, binding);
192+
return TensorInt32Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
193+
}
194+
break;
195+
case TensorKind::UInt32:
196+
{
197+
ModelBinding<uint32_t> binding(description);
198+
elementStrings = ParseCSVElementStrings(inputPath);
199+
WriteDataToBinding<uint32_t>(elementStrings, binding);
200+
return TensorUInt32Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
201+
}
202+
break;
203+
case TensorKind::Int64:
204+
{
205+
ModelBinding<int64_t> binding(description);
206+
elementStrings = ParseCSVElementStrings(inputPath);
207+
WriteDataToBinding<int64_t>(elementStrings, binding);
208+
return TensorInt64Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
209+
}
210+
break;
211+
case TensorKind::UInt64:
212+
{
213+
ModelBinding<uint64_t> binding(description);
214+
elementStrings = ParseCSVElementStrings(inputPath);
215+
WriteDataToBinding<uint64_t>(elementStrings, binding);
216+
return TensorUInt64Bit::CreateFromArray(binding.GetShapeBuffer(), binding.GetDataBuffer());
217+
}
218+
break;
219+
}
220+
221+
std::cout << "BindingUtilities: TensorKind has not been implemented." << std::endl;
222+
throw hresult_not_implemented();
223+
}
224+
225+
ImageFeatureValue CreateBindableImage(
226+
const ILearningModelFeatureDescriptor&
227+
featureDescriptor,
228+
const std::wstring& imagePath,
229+
ImageDataType inputDataType )
230+
{
231+
auto imageDescriptor = featureDescriptor.try_as<TensorFeatureDescriptor>();
232+
233+
if (!imageDescriptor)
234+
{
235+
std::cout << "BindingUtilities: Input Descriptor type isn't tensor." << std::endl;
236+
throw;
237+
}
238+
239+
auto softwareBitmap = LoadImageFile(imageDescriptor, inputDataType, imagePath.c_str());
240+
241+
auto videoFrame = VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
242+
243+
return ImageFeatureValue::CreateFromVideoFrame(videoFrame);
244+
}
245+
};

0 commit comments

Comments
 (0)