diff --git a/.editorconfig b/.editorconfig
index cf59606e19..ee8f4639c0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -341,4 +341,7 @@ dotnet_style_readonly_field = false
[test/Microsoft.ML.TestFrameworkCommon/Utility/*.cs]
# IDE0073: Dont want license file header in code we are using from elsewhere
dotnet_diagnostic.IDE0073.severity = none
-file_header_template = unset
\ No newline at end of file
+file_header_template = unset
+
+[**/*.generated.cs]
+generated_code = true
diff --git a/.github/fabricbot.json b/.github/fabricbot.json
index 525380caca..dfe781d081 100644
--- a/.github/fabricbot.json
+++ b/.github/fabricbot.json
@@ -1513,19 +1513,19 @@
"subCapability": "IssuesOnlyResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Needs Triage",
+ "taskName": "[Area Pod: Michael / Tanner - Issue Triage] Needs Triage",
"actions": [
{
"name": "removeFromProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true
}
},
{
"name": "addToProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"columnName": "Needs Triage",
"isOrgProject": true
}
@@ -1581,7 +1581,7 @@
{
"name": "isInProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true
}
}
@@ -1590,7 +1590,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true,
"columnName": "Triaged"
}
@@ -1608,19 +1608,19 @@
"subCapability": "IssueCommentResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Needs Further Triage",
+ "taskName": "[Area Pod: Michael / Tanner - Issue Triage] Needs Further Triage",
"actions": [
{
"name": "removeFromProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true
}
},
{
"name": "addToProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"columnName": "Needs Triage",
"isOrgProject": true
}
@@ -1657,7 +1657,7 @@
{
"name": "isInProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true
}
}
@@ -1666,7 +1666,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"columnName": "Triaged",
"isOrgProject": true
}
@@ -1684,12 +1684,12 @@
"subCapability": "IssuesOnlyResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Triaged",
+ "taskName": "[Area Pod: Michael / Tanner - Issue Triage] Triaged",
"actions": [
{
"name": "addToProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"columnName": "Triaged",
"isOrgProject": true
}
@@ -1711,7 +1711,7 @@
{
"name": "isInProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true
}
},
@@ -1747,146 +1747,12 @@
"subCapability": "IssuesOnlyResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Drew Updated Issue",
+ "taskName": "[Area Pod: Michael / Tanner - Issue Triage] Michael Updated Issue",
"actions": [
{
"name": "moveToProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
- "columnName": "Triage: Drew",
- "isOrgProject": true
- }
- }
- ],
- "eventType": "issue",
- "eventNames": [
- "issues"
- ],
- "conditions": {
- "operator": "and",
- "operands": [
- {
- "name": "isInProjectColumn",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
- "isOrgProject": true,
- "columnName": "Needs Triage"
- }
- },
- {
- "name": "isActivitySender",
- "parameters": {
- "user": "dakersnar"
- }
- },
- {
- "name": "isOpen",
- "parameters": {}
- },
- {
- "operator": "not",
- "operands": [
- {
- "name": "isInMilestone",
- "parameters": {}
- }
- ]
- },
- {
- "operator": "not",
- "operands": [
- {
- "name": "hasLabel",
- "parameters": {
- "label": "needs-author-action"
- }
- }
- ]
- }
- ]
- }
- }
- },
- {
- "taskSource": "fabricbot-config",
- "taskType": "trigger",
- "capabilityId": "IssueResponder",
- "subCapability": "IssueCommentResponder",
- "version": "1.0",
- "config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Drew Commented",
- "actions": [
- {
- "name": "moveToProjectColumn",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
- "columnName": "Triage: Drew",
- "isOrgProject": true
- }
- }
- ],
- "eventType": "issue",
- "eventNames": [
- "issue_comment"
- ],
- "conditions": {
- "operator": "and",
- "operands": [
- {
- "name": "isInProjectColumn",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
- "isOrgProject": true,
- "columnName": "Needs Triage"
- }
- },
- {
- "name": "isActivitySender",
- "parameters": {
- "user": "dakersnar"
- }
- },
- {
- "name": "isOpen",
- "parameters": {}
- },
- {
- "operator": "not",
- "operands": [
- {
- "name": "isInMilestone",
- "parameters": {}
- }
- ]
- },
- {
- "operator": "not",
- "operands": [
- {
- "name": "hasLabel",
- "parameters": {
- "label": "needs-author-action"
- }
- }
- ]
- }
- ]
- }
- }
- },
- {
- "taskSource": "fabricbot-config",
- "taskType": "trigger",
- "capabilityId": "IssueResponder",
- "subCapability": "IssuesOnlyResponder",
- "version": "1.0",
- "config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Michael Updated Issue",
- "actions": [
- {
- "name": "moveToProjectColumn",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"columnName": "Triage: Michael",
"isOrgProject": true
}
@@ -1902,7 +1768,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true,
"columnName": "Needs Triage"
}
@@ -1914,26 +1780,31 @@
}
},
{
- "name": "isOpen",
- "parameters": {}
- },
- {
- "operator": "not",
+ "operator": "and",
"operands": [
{
- "name": "isInMilestone",
+ "name": "isOpen",
"parameters": {}
- }
- ]
- },
- {
- "operator": "not",
- "operands": [
+ },
{
- "name": "hasLabel",
- "parameters": {
- "label": "needs-author-action"
- }
+ "operator": "not",
+ "operands": [
+ {
+ "name": "isInMilestone",
+ "parameters": {}
+ }
+ ]
+ },
+ {
+ "operator": "not",
+ "operands": [
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "needs-author-action"
+ }
+ }
+ ]
}
]
}
@@ -1948,12 +1819,12 @@
"subCapability": "IssueCommentResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Michael Commented",
+ "taskName": "[Area Pod: Michael / Tanner - Issue Triage] Michael Commented",
"actions": [
{
"name": "moveToProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"columnName": "Triage: Michael",
"isOrgProject": true
}
@@ -1969,7 +1840,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true,
"columnName": "Needs Triage"
}
@@ -1981,26 +1852,31 @@
}
},
{
- "name": "isOpen",
- "parameters": {}
- },
- {
- "operator": "not",
+ "operator": "and",
"operands": [
{
- "name": "isInMilestone",
+ "name": "isOpen",
"parameters": {}
- }
- ]
- },
- {
- "operator": "not",
- "operands": [
+ },
{
- "name": "hasLabel",
- "parameters": {
- "label": "needs-author-action"
- }
+ "operator": "not",
+ "operands": [
+ {
+ "name": "isInMilestone",
+ "parameters": {}
+ }
+ ]
+ },
+ {
+ "operator": "not",
+ "operands": [
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "needs-author-action"
+ }
+ }
+ ]
}
]
}
@@ -2015,12 +1891,12 @@
"subCapability": "IssuesOnlyResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Tanner Updated Issue",
+ "taskName": "[Area Pod: Michael / Tanner - Issue Triage] Tanner Updated Issue",
"actions": [
{
"name": "moveToProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"columnName": "Triage: Tanner",
"isOrgProject": true
}
@@ -2036,7 +1912,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true,
"columnName": "Needs Triage"
}
@@ -2048,26 +1924,31 @@
}
},
{
- "name": "isOpen",
- "parameters": {}
- },
- {
- "operator": "not",
+ "operator": "and",
"operands": [
{
- "name": "isInMilestone",
+ "name": "isOpen",
"parameters": {}
- }
- ]
- },
- {
- "operator": "not",
- "operands": [
+ },
{
- "name": "hasLabel",
- "parameters": {
- "label": "needs-author-action"
- }
+ "operator": "not",
+ "operands": [
+ {
+ "name": "isInMilestone",
+ "parameters": {}
+ }
+ ]
+ },
+ {
+ "operator": "not",
+ "operands": [
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "needs-author-action"
+ }
+ }
+ ]
}
]
}
@@ -2082,12 +1963,12 @@
"subCapability": "IssueCommentResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - Issue Triage] Tanner Commented",
+ "taskName": "[Area Pod: Michael / Tanner - Issue Triage] Tanner Commented",
"actions": [
{
"name": "moveToProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"columnName": "Triage: Tanner",
"isOrgProject": true
}
@@ -2103,7 +1984,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - Issue Triage",
+ "projectName": "Area Pod: Michael / Tanner - Issue Triage",
"isOrgProject": true,
"columnName": "Needs Triage"
}
@@ -2115,26 +1996,31 @@
}
},
{
- "name": "isOpen",
- "parameters": {}
- },
- {
- "operator": "not",
+ "operator": "and",
"operands": [
{
- "name": "isInMilestone",
+ "name": "isOpen",
"parameters": {}
- }
- ]
- },
- {
- "operator": "not",
- "operands": [
+ },
{
- "name": "hasLabel",
- "parameters": {
- "label": "needs-author-action"
- }
+ "operator": "not",
+ "operands": [
+ {
+ "name": "isInMilestone",
+ "parameters": {}
+ }
+ ]
+ },
+ {
+ "operator": "not",
+ "operands": [
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "needs-author-action"
+ }
+ }
+ ]
}
]
}
@@ -2149,12 +2035,12 @@
"subCapability": "PullRequestResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - PRs] Closed, Merged, or Moved",
+ "taskName": "[Area Pod: Michael / Tanner - PRs] Closed, Merged, or Moved",
"actions": [
{
"name": "moveToProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Done",
"isOrgProject": true
}
@@ -2170,7 +2056,7 @@
{
"name": "isInProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"isOrgProject": true
}
},
@@ -2180,7 +2066,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Done",
"isOrgProject": true
}
@@ -2212,12 +2098,12 @@
"subCapability": "PullRequestResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - PRs] New PR Needs Champion",
+ "taskName": "[Area Pod: Michael / Tanner - PRs] New PR Needs Champion",
"actions": [
{
"name": "addToProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Needs Champion",
"isOrgProject": true
}
@@ -2237,30 +2123,6 @@
}
},
[
- [
- {
- "operator": "not",
- "operands": [
- {
- "name": "isAssignedToUser",
- "parameters": {
- "user": "dakersnar"
- }
- }
- ]
- },
- {
- "operator": "not",
- "operands": [
- {
- "name": "isActivitySender",
- "parameters": {
- "user": "dakersnar"
- }
- }
- ]
- }
- ],
[
{
"operator": "not",
@@ -2319,7 +2181,7 @@
{
"name": "isInProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"isOrgProject": true
}
}
@@ -2328,7 +2190,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Done",
"isOrgProject": true
}
@@ -2346,19 +2208,19 @@
"subCapability": "PullRequestResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - PRs] Updated PR Needs Champion",
+ "taskName": "[Area Pod: Michael / Tanner - PRs] Updated PR Needs Champion",
"actions": [
{
"name": "removeFromProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"isOrgProject": true
}
},
{
"name": "addToProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Needs Champion",
"isOrgProject": true
}
@@ -2386,17 +2248,6 @@
}
]
},
- {
- "operator": "not",
- "operands": [
- {
- "name": "isAssignedToUser",
- "parameters": {
- "user": "dakersnar"
- }
- }
- ]
- },
{
"operator": "not",
"operands": [
@@ -2428,7 +2279,7 @@
{
"name": "isInProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"isOrgProject": true
}
}
@@ -2437,108 +2288,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
- "columnName": "Done",
- "isOrgProject": true
- }
- }
- ]
- }
- ]
- }
- }
- },
- {
- "taskSource": "fabricbot-config",
- "taskType": "trigger",
- "capabilityId": "IssueResponder",
- "subCapability": "PullRequestResponder",
- "version": "1.0",
- "config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - PRs] Drew Assigned as Champion",
- "actions": [
- {
- "name": "removeFromProject",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
- "isOrgProject": true
- }
- },
- {
- "name": "addToProject",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
- "columnName": "Champion: Drew",
- "isOrgProject": true
- }
- }
- ],
- "eventType": "pull_request",
- "eventNames": [
- "pull_request"
- ],
- "conditions": {
- "operator": "and",
- "operands": [
- {
- "name": "isOpen",
- "parameters": {}
- },
- {
- "operator": "or",
- "operands": [
- {
- "name": "isAssignedToUser",
- "parameters": {
- "user": "dakersnar"
- }
- },
- {
- "operator": "and",
- "operands": [
- {
- "name": "isAction",
- "parameters": {
- "action": "opened"
- }
- },
- {
- "name": "isActivitySender",
- "parameters": {
- "user": "dakersnar"
- }
- }
- ]
- }
- ]
- },
- {
- "operator": "or",
- "operands": [
- {
- "operator": "not",
- "operands": [
- {
- "name": "isInProject",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
- "isOrgProject": true
- }
- }
- ]
- },
- {
- "name": "isInProjectColumn",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
- "columnName": "Needs Champion",
- "isOrgProject": true
- }
- },
- {
- "name": "isInProjectColumn",
- "parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Done",
"isOrgProject": true
}
@@ -2556,19 +2306,19 @@
"subCapability": "PullRequestResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - PRs] Michael Assigned as Champion",
+ "taskName": "[Area Pod: Michael / Tanner - PRs] Michael Assigned as Champion",
"actions": [
{
"name": "removeFromProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"isOrgProject": true
}
},
{
"name": "addToProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Champion: Michael",
"isOrgProject": true
}
@@ -2622,7 +2372,7 @@
{
"name": "isInProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"isOrgProject": true
}
}
@@ -2631,7 +2381,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Needs Champion",
"isOrgProject": true
}
@@ -2639,7 +2389,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Done",
"isOrgProject": true
}
@@ -2657,19 +2407,19 @@
"subCapability": "PullRequestResponder",
"version": "1.0",
"config": {
- "taskName": "[Area Pod: Drew / Michael / Tanner - PRs] Tanner Assigned as Champion",
+ "taskName": "[Area Pod: Michael / Tanner - PRs] Tanner Assigned as Champion",
"actions": [
{
"name": "removeFromProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"isOrgProject": true
}
},
{
"name": "addToProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Champion: Tanner",
"isOrgProject": true
}
@@ -2723,7 +2473,7 @@
{
"name": "isInProject",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"isOrgProject": true
}
}
@@ -2732,7 +2482,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Needs Champion",
"isOrgProject": true
}
@@ -2740,7 +2490,7 @@
{
"name": "isInProjectColumn",
"parameters": {
- "projectName": "Area Pod: Drew / Michael / Tanner - PRs",
+ "projectName": "Area Pod: Michael / Tanner - PRs",
"columnName": "Done",
"isOrgProject": true
}
diff --git a/Microsoft.ML.sln b/Microsoft.ML.sln
index 87fe950e9c..e69d535369 100644
--- a/Microsoft.ML.sln
+++ b/Microsoft.ML.sln
@@ -1,4 +1,4 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32120.378
MinimumVisualStudioVersion = 10.0.40219.1
@@ -71,6 +71,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.ImageAnalytics
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.Mkl.Components", "src\Microsoft.ML.Mkl.Components\Microsoft.ML.Mkl.Components.csproj", "{A7222F41-1CF0-47D9-B80C-B4D77B027A61}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.OneDal", "src\Microsoft.ML.OneDal\Microsoft.ML.OneDal.csproj", "{A7222F94-2AF1-10C9-A21C-C4D22B137A69}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.TensorFlow", "src\Microsoft.ML.TensorFlow\Microsoft.ML.TensorFlow.csproj", "{570A0B8A-5463-44D2-8521-54C0CA4CACA9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.TimeSeries", "src\Microsoft.ML.TimeSeries\Microsoft.ML.TimeSeries.csproj", "{5A79C7F0-3D99-4123-B0DA-7C9FFCD13132}"
@@ -90,6 +92,8 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.Samples", "docs\samples\Microsoft.ML.Samples\Microsoft.ML.Samples.csproj", "{ECB71297-9DF1-48CE-B93A-CD969221F9B6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.SamplesUtils", "src\Microsoft.ML.SamplesUtils\Microsoft.ML.SamplesUtils.csproj", "{11A5210E-2EA7-42F1-80DB-827762E9C781}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.Samples.OneDal", "docs\samples\Microsoft.ML.Samples.OneDal\Microsoft.ML.Samples.OneDal.csproj", "{38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}"
+EndProject
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.Recommender", "src\Microsoft.ML.Recommender\Microsoft.ML.Recommender.csproj", "{C8E1772B-DFD9-4A4D-830D-6AAB1C668BB3}"
EndProject
@@ -403,6 +407,14 @@ Global
{A7222F41-1CF0-47D9-B80C-B4D77B027A61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7222F41-1CF0-47D9-B80C-B4D77B027A61}.Debug|x64.ActiveCfg = Debug|Any CPU
{A7222F41-1CF0-47D9-B80C-B4D77B027A61}.Debug|x64.Build.0 = Debug|Any CPU
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69}.Debug|x64.Build.0 = Debug|Any CPU
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69}.Release|x64.ActiveCfg = Release|Any CPU
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69}.Release|x64.Build.0 = Release|Any CPU
{A7222F41-1CF0-47D9-B80C-B4D77B027A61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7222F41-1CF0-47D9-B80C-B4D77B027A61}.Release|Any CPU.Build.0 = Release|Any CPU
{A7222F41-1CF0-47D9-B80C-B4D77B027A61}.Release|x64.ActiveCfg = Release|Any CPU
@@ -747,6 +759,14 @@ Global
{C3D82402-F207-4F19-8C57-5AF0FBAF9682}.Release|Any CPU.Build.0 = Release|Any CPU
{C3D82402-F207-4F19-8C57-5AF0FBAF9682}.Release|x64.ActiveCfg = Release|Any CPU
{C3D82402-F207-4F19-8C57-5AF0FBAF9682}.Release|x64.Build.0 = Release|Any CPU
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}.Debug|x64.Build.0 = Debug|Any CPU
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}.Release|Any CPU.Build.0 = Release|Any CPU
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}.Release|x64.ActiveCfg = Release|Any CPU
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -782,6 +802,7 @@ Global
{802233D6-8CC0-46AD-9F23-FEE1E9AED9B3} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
{00E38F77-1E61-4CDF-8F97-1417D4E85053} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
{A7222F41-1CF0-47D9-B80C-B4D77B027A61} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
+ {A7222F94-2AF1-10C9-A21C-C4D22B137A69} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
{570A0B8A-5463-44D2-8521-54C0CA4CACA9} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
{5A79C7F0-3D99-4123-B0DA-7C9FFCD13132} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
{8C05642D-C3AA-4972-B02C-93681161A6BC} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
@@ -825,6 +846,7 @@ Global
{FF0BD187-4451-4A3B-934B-2AE3454896E2} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
{BBC3A950-BD68-45AC-9DBD-A8F4D8847745} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
{C3D82402-F207-4F19-8C57-5AF0FBAF9682} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
+ {38ED61F4-FA22-4DE9-B0C4-91F327F4EE31} = {DA452A53-2E94-4433-B08C-041EDEC729E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {41165AF1-35BB-4832-A189-73060F82B01D}
diff --git a/NuGet.config b/NuGet.config
index 976d8d3441..bf2f71f9a7 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -13,6 +13,7 @@
+
diff --git a/README-oneDAL.md b/README-oneDAL.md
new file mode 100644
index 0000000000..17d05e687c
--- /dev/null
+++ b/README-oneDAL.md
@@ -0,0 +1,16 @@
+# oneDAL supported algorithms
+
+oneAPI Data Analytics Library (oneDAL) is a library providing highly optimized machine learning and data analytics kernels. Some of these kernels is integrated into ML.NET via C++/C# interoperability.
+
+[oneDAL Documentation](http://oneapi-src.github.io/oneDAL/) | [oneDAL Repository](https://github.com/oneapi-src/oneDAL)
+
+> Please note that oneDAL acceleration paths are only available in x64 architectures
+
+Integration consists of:
+
+* A "native" component (under `src/Native/Microsoft.ML.OneDal`) implementing wrapper to pass data and parameters to oneDAL;
+* Dispatching to oneDAL kernels inside relevant learners: `OLS` (`src/Microsoft.ML.Mkl.Components`), `Logistic Regression` (`src/Microsoft.ML.StandardTrainers`), `Random Forest` (`src/Microsoft.ML.FastTree`);
+
+## Running ML.NET trainers with dispatching to oneDAL kernels
+
+Currently, dispatching to oneDAL inside ML.NET is regulated by `MLNET_BACKEND` environment variable. If it's set to `ONEDAL`, oneDAL kernel will be used, otherwise - default ML.NET.
diff --git a/ROADMAP.md b/ROADMAP.md
index b5d2b9b1e3..b5fb8ec296 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -38,9 +38,9 @@ As part of this plan, we will:
1. Make it easier to consume ONNX models in ML.NET using the ONNX Runtime (RT)
1. Continue to bring more scenario-based APIs backed by TorchSharp transformer-based architectures. The next few scenarios we're looking to enable are:
- - Object detection
- - Named Entity Recognition (NER)
- - Question Answering
+ - Object detection
+ - Named Entity Recognition (NER)
+ - Question Answering
1. Enable integrations with TorchSharp for scenarios and models not supported out of the box by ML.NET.
1. Accelerate deep learning workflows by improving batch support and enabling easier use of accelerators such as ONNX Runtime Execution Providers.
@@ -48,7 +48,7 @@ Read more about the deep learning plan and leave your feedback in this [tracking
Performance-related improvements are being tracked in this [issue](https://github.com/dotnet/machinelearning/issues/6422).
-### LightBGM
+### LightGBM
LightGBM is a flexible framework for classical machine learning tasks such as classification and regression. To make the best of the features LightGBM provides, we plan to:
diff --git a/build/ci/job-template.yml b/build/ci/job-template.yml
index 6f87d45d3b..17965b4e70 100644
--- a/build/ci/job-template.yml
+++ b/build/ci/job-template.yml
@@ -122,7 +122,7 @@ jobs:
- ${{ if eq(parameters.nightlyBuild, 'false') }}:
- ${{ if eq(parameters.innerLoop, 'false') }}:
- ${{ if and(eq(parameters.runSpecific, 'false'), eq(parameters.useVSTestTask, 'false')) }}:
- - script: set PATH=%PATH%;%USERPROFILE%\.nuget\packages\libtorch-cpu-win-x64\1.11.0.1\runtimes\win-x64\native;%USERPROFILE%\.nuget\packages\torchsharp\0.96.7\runtimes\win-x64\native & ${{ parameters.buildScript }} /p:Build=false -configuration $(_configuration) /p:TargetArchitecture=${{ parameters.architecture }} /p:TestArchitectures=${{ parameters.architecture }} -test -integrationTest /p:Coverage=${{ parameters.codeCoverage }} $(testTargetFramework)
+ - script: set PATH=%PATH%;%USERPROFILE%\.nuget\packages\libtorch-cpu-win-x64\1.13.0.1\runtimes\win-x64\native;%USERPROFILE%\.nuget\packages\torchsharp\0.99.5\runtimes\win-x64\native & ${{ parameters.buildScript }} /p:Build=false -configuration $(_configuration) /p:TargetArchitecture=${{ parameters.architecture }} /p:TestArchitectures=${{ parameters.architecture }} -test -integrationTest /p:Coverage=${{ parameters.codeCoverage }} $(testTargetFramework)
displayName: Run All Tests.
- ${{ if and(eq(parameters.runSpecific, 'true'), eq(parameters.useVSTestTask, 'false')) }}:
- script: ${{ parameters.buildScript }} /p:Build=false -configuration $(_configuration) /p:TargetArchitecture=${{ parameters.architecture }} /p:TestArchitectures=${{ parameters.architecture }} -test -integrationTest /p:TestRunnerAdditionalArguments='-trait$(spaceValue)Category=RunSpecificTest' /p:Coverage=${{ parameters.codeCoverage }} $(testTargetFramework)
diff --git a/build/codecoverage-ci.yml b/build/codecoverage-ci.yml
index a52c70f48b..2d0d082c23 100644
--- a/build/codecoverage-ci.yml
+++ b/build/codecoverage-ci.yml
@@ -15,4 +15,5 @@ jobs:
_targetFramework: net6.0
codeCoverage: true
pool:
- vmImage: windows-2019
+ name: NetCore-Public
+ demands: ImageOverride -equals 1es-windows-2019-open
diff --git a/build/vsts-ci.yml b/build/vsts-ci.yml
index 15502526f7..62ab0d7c01 100644
--- a/build/vsts-ci.yml
+++ b/build/vsts-ci.yml
@@ -100,7 +100,7 @@ stages:
pool:
vmImage: macOS-12
steps:
- - script: brew update && brew unlink python@3.8 && rm '/usr/local/bin/2to3-3.11' && brew unlink libomp && brew install $(Build.SourcesDirectory)/build/libomp.rb --build-from-source --formula
+ - script: brew update && rm '/usr/local/bin/2to3-3.11' && brew unlink libomp && brew install $(Build.SourcesDirectory)/build/libomp.rb --build-from-source --formula
displayName: Install build dependencies
# Only build native assets to avoid conflicts.
- script: ./build.sh -projects $(Build.SourcesDirectory)/src/Native/Native.proj -configuration $(BuildConfig) /p:TargetArchitecture=x64 /p:CopyPackageAssets=true
@@ -126,7 +126,7 @@ stages:
rm -rf /usr/local/bin/2to3
displayName: MacOS Homebrew bug Workaround
continueOnError: true
- - script: brew update && brew unlink python@3.8 && brew install libomp && brew link libomp --force
+ - script: brew update && brew install libomp && brew link libomp --force
displayName: Install build dependencies
# Only build native assets to avoid conflicts.
- script: ./build.sh -projects $(Build.SourcesDirectory)/src/Native/Native.proj -configuration $(BuildConfig) /p:TargetArchitecture=arm64 /p:CopyPackageAssets=true
diff --git a/docs/code/DeepLearningOverview.md b/docs/code/DeepLearningOverview.md
new file mode 100644
index 0000000000..ed383b8358
--- /dev/null
+++ b/docs/code/DeepLearningOverview.md
@@ -0,0 +1,97 @@
+# What is Deep Learning?
+
+Deep Learning is an umbrella term for an approach to Machine Learning
+that makes use of "deep" Neural Networks, a kind of models originally
+inspired by the function of biological brains. These days, Deep
+Learning is probably the most visible area of Machine Learning, and it
+has seen amazing successes in areas like Computer Vision, Natural
+Language Processing and, in combination with Reinforcement Learning,
+more complicated settings such as game playing, decision making and
+simulation.
+
+A crucial element of the success of Deep Learning ("DL" in what
+follows) has been the existence of software frameworks and runtimes
+that facilitate the creation of Neural Network models and their
+execution for inference. Examples of such frameworks include
+Tensorflow, (Py)Torch and onnx. ML.NET provides access to some of
+these frameworks, while maintaining the familiar pipeline interface.
+In this way, users of ML.NET can take advantage of some
+state-of-the-art models and applications of DL at a lower cost than
+the steep learning curve learning that other DL frameworks require.
+
+# Deep Learning vs Machine Learning?
+
+As mentioned above, DL relies on "Neural Network" models, in contrast
+with "traditional" Machine Learning techniques (which use a wider
+variety of architectures, such as, for example, generalized linear
+models, decision trees or Support Vector Machines). The most
+immediate, practical implication of this difference is that DL methods
+may be better or worse suited for some kind of data. The performance
+of DL methods on images, on textual and on other non- or
+less-structured data has been well documented in the literature.
+Traditional Machine Learning methods such as gradient-boosted trees
+(XGBoost, LightGBM and CatBoost) seem to still have an edge when it
+comes to tabular data. The best approach is always to experiment with
+your particular data source and use case and determine for yourself,
+and ML.NET makes this experimentation relatively straightforward and
+pain-free.
+
+# Neural Network architectures
+
+A crucial differentiating characteristic of DL from other classes (or
+schools) of ML is the use of artificial Neural Networks as models. At
+a high-level, one can think of a Neural Network as a configuration of
+"processing units" where the output of each unit constitutes the input
+of another. Each of these units can take one or many inputs, and
+essentially carries out a weighted sum of its inputs, applies an
+offset (or "bias") and then a non-linear transformation function
+(called "activation"). Different arrangements of these relatively
+simple components have been proven surprisingly rich to describe
+decision boundaries in classification, regression functions and other
+structures central to ML tasks.
+
+The past decade has seen an explosion of use cases, applications and
+techniques of DL, each more impressive than the last, pushing the
+boundaries of what functionalities we thought a computer program could
+feature. This expansion is fueled by an increasing variety of
+operations that can be incorporated into Neural Networks, by a richer
+set of arrangments that these operations can be configured in and by
+improved computational support for these improvements. In general, we
+can categorize these new Neural Architectures, and their use cases
+they enable, in (a more complete description can be found [here](https://learn.microsoft.com/en-us/azure/machine-learning/concept-deep-learning-vs-machine-learning#artificial-neural-networks) ):
+
+* Feed-forward Neural Network
+* Convolutional Neural Network
+* Recurrent Neural Network
+* Generative Adversarial Network
+* Transformers
+
+# What can I use deep learning for?
+
+As stated above, the scope of application of DL techniques is rapidly
+expanding. DL architectures, however, have shown amazing
+(close-to-human in some cases) performance in tasks having to do with
+"unstructured data": images, audio, free-form text and the like. In
+this way, DL is constantly featured in image/audio classification and
+generation applications. When it comes to text processing, more
+generally Natural Language Processing, DL methods have shown amazing
+results in tasks like translation, classification, generation and
+similar. Some of the more spectacular, recent applications of ML,
+such as "[Stable Diffusion](https://en.wikipedia.org/wiki/Stable_Diffusion)" are powered by sophisticated, large Neural
+Network architectures.
+
+# Deep learning in ML.NET
+
+A central concern of DL is what Neural Network architecture (specific configuration of operations) will the model have, and to this end, DL frameworks like Tensorflow and Pytorch feature expressive Domain-Specific Languages to describe in detail such architectures. ML.NET departs from this practice and concentrates on the consumption of pre-trained models (i.e., architectures that have been specified *and* trained in other frameworks).
+
+# Train custom models
+
+# Image classification
+
+# Text classification (Needs tutorial)
+# Sentence Similarity (Needs tutorial - P1)
+
+# Consume pretrained models
+
+# TensorFlow https://learn.microsoft.com/en-us/dotnet/machine-learning/tutorials/text-classification-tf
+# ONNX https://github.com/dotnet/csharp-notebooks/blob/main/machine-learning/E2E-Text-Classification-API-with-Yelp-Dataset.ipynb
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/BinaryClassificationExperiment.cs b/docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/BinaryClassificationExperiment.cs
similarity index 100%
rename from docs/samples/Microsoft.ML.AutoML.Samples/BinaryClassificationExperiment.cs
rename to docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/BinaryClassificationExperiment.cs
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/MulticlassClassificationExperiment.cs b/docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/MulticlassClassificationExperiment.cs
similarity index 100%
rename from docs/samples/Microsoft.ML.AutoML.Samples/MulticlassClassificationExperiment.cs
rename to docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/MulticlassClassificationExperiment.cs
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/RankingExperiment.cs b/docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/RankingExperiment.cs
similarity index 100%
rename from docs/samples/Microsoft.ML.AutoML.Samples/RankingExperiment.cs
rename to docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/RankingExperiment.cs
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/RecommendationExperiment.cs b/docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/RecommendationExperiment.cs
similarity index 100%
rename from docs/samples/Microsoft.ML.AutoML.Samples/RecommendationExperiment.cs
rename to docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/RecommendationExperiment.cs
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/RegressionExperiment.cs b/docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/RegressionExperiment.cs
similarity index 100%
rename from docs/samples/Microsoft.ML.AutoML.Samples/RegressionExperiment.cs
rename to docs/samples/Microsoft.ML.AutoML.Samples/AutoFit/RegressionExperiment.cs
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/AutoMLExperiment.cs b/docs/samples/Microsoft.ML.AutoML.Samples/AutoMLExperiment.cs
new file mode 100644
index 0000000000..836d741641
--- /dev/null
+++ b/docs/samples/Microsoft.ML.AutoML.Samples/AutoMLExperiment.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.ML.Data;
+
+namespace Microsoft.ML.AutoML.Samples
+{
+ public static class AutoMLExperiment
+ {
+ public static async Task RunAsync()
+ {
+ var seed = 0;
+
+ // Create a new context for ML.NET operations. It can be used for
+ // exception tracking and logging, as a catalog of available operations
+ // and as the source of randomness. Setting the seed to a fixed number
+ // in this example to make outputs deterministic.
+ var context = new MLContext(seed);
+
+ // Create a list of training data points and convert it to IDataView.
+ var data = GenerateRandomBinaryClassificationDataPoints(100, seed);
+ var dataView = context.Data.LoadFromEnumerable(data);
+
+ var trainTestSplit = context.Data.TrainTestSplit(dataView);
+
+ // Define the sweepable pipeline using predefined binary trainers and search space.
+ var pipeline = context.Auto().BinaryClassification(labelColumnName: "Label", featureColumnName: "Features");
+
+ // Create an AutoML experiment
+ var experiment = context.Auto().CreateExperiment();
+
+ // Redirect AutoML log to console
+ context.Log += (object o, LoggingEventArgs e) =>
+ {
+ if (e.Source == nameof(AutoMLExperiment) && e.Kind > Runtime.ChannelMessageKind.Trace)
+ {
+ Console.WriteLine(e.RawMessage);
+ }
+ };
+
+ // Config experiment to optimize "Accuracy" metric on given dataset.
+ // This experiment will run hyper-parameter optimization on given pipeline
+ experiment.SetPipeline(pipeline)
+ .SetDataset(trainTestSplit.TrainSet, fold: 5) // use 5-fold cross validation to evaluate each trial
+ .SetBinaryClassificationMetric(BinaryClassificationMetric.Accuracy, "Label")
+ .SetMaxModelToExplore(100); // explore 100 trials
+
+ // start automl experiment
+ var result = await experiment.RunAsync();
+
+ // Expected output samples during training:
+ // Update Running Trial - Id: 0
+ // Update Completed Trial - Id: 0 - Metric: 0.5536912515402218 - Pipeline: FastTreeBinary - Duration: 595 - Peak CPU: 0.00 % -Peak Memory in MB: 35.81
+ // Update Best Trial - Id: 0 - Metric: 0.5536912515402218 - Pipeline: FastTreeBinary
+
+ // evaluate test dataset on best model.
+ var bestModel = result.Model;
+ var eval = bestModel.Transform(trainTestSplit.TestSet);
+ var metrics = context.BinaryClassification.Evaluate(eval);
+
+ PrintMetrics(metrics);
+
+ // Expected output:
+ // Accuracy: 0.67
+ // AUC: 0.75
+ // F1 Score: 0.33
+ // Negative Precision: 0.88
+ // Negative Recall: 0.70
+ // Positive Precision: 0.25
+ // Positive Recall: 0.50
+
+ // TEST POSITIVE RATIO: 0.1667(2.0 / (2.0 + 10.0))
+ // Confusion table
+ // ||======================
+ // PREDICTED || positive | negative | Recall
+ // TRUTH ||======================
+ // positive || 1 | 1 | 0.5000
+ // negative || 3 | 7 | 0.7000
+ // ||======================
+ // Precision || 0.2500 | 0.8750 |
+ }
+
+ private static IEnumerable GenerateRandomBinaryClassificationDataPoints(int count,
+ int seed = 0)
+
+ {
+ var random = new Random(seed);
+ float randomFloat() => (float)random.NextDouble();
+ for (int i = 0; i < count; i++)
+ {
+ var label = randomFloat() > 0.5f;
+ yield return new BinaryClassificationDataPoint
+ {
+ Label = label,
+ // Create random features that are correlated with the label.
+ // For data points with false label, the feature values are
+ // slightly increased by adding a constant.
+ Features = Enumerable.Repeat(label, 50)
+ .Select(x => x ? randomFloat() : randomFloat() +
+ 0.1f).ToArray()
+
+ };
+ }
+ }
+
+ // Example with label and 50 feature values. A data set is a collection of
+ // such examples.
+ private class BinaryClassificationDataPoint
+ {
+ public bool Label { get; set; }
+
+ [VectorType(50)]
+ public float[] Features { get; set; }
+ }
+
+ // Class used to capture predictions.
+ private class Prediction
+ {
+ // Original label.
+ public bool Label { get; set; }
+ // Predicted label from the trainer.
+ public bool PredictedLabel { get; set; }
+ }
+
+ // Pretty-print BinaryClassificationMetrics objects.
+ private static void PrintMetrics(BinaryClassificationMetrics metrics)
+ {
+ Console.WriteLine($"Accuracy: {metrics.Accuracy:F2}");
+ Console.WriteLine($"AUC: {metrics.AreaUnderRocCurve:F2}");
+ Console.WriteLine($"F1 Score: {metrics.F1Score:F2}");
+ Console.WriteLine($"Negative Precision: " +
+ $"{metrics.NegativePrecision:F2}");
+
+ Console.WriteLine($"Negative Recall: {metrics.NegativeRecall:F2}");
+ Console.WriteLine($"Positive Precision: " +
+ $"{metrics.PositivePrecision:F2}");
+
+ Console.WriteLine($"Positive Recall: {metrics.PositiveRecall:F2}\n");
+ Console.WriteLine(metrics.ConfusionMatrix.GetFormattedConfusionTable());
+ }
+ }
+}
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/Microsoft.ML.AutoML.Samples.csproj b/docs/samples/Microsoft.ML.AutoML.Samples/Microsoft.ML.AutoML.Samples.csproj
index 215a265c26..e753ee59f6 100644
--- a/docs/samples/Microsoft.ML.AutoML.Samples/Microsoft.ML.AutoML.Samples.csproj
+++ b/docs/samples/Microsoft.ML.AutoML.Samples/Microsoft.ML.AutoML.Samples.csproj
@@ -2,9 +2,12 @@
Exe
- netcoreapp3.1
+ net6.0
false
- $(NoWarn);MSB3270
+ $(NoWarn)
+
+
+ None
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/Program.cs b/docs/samples/Microsoft.ML.AutoML.Samples/Program.cs
index 646c3546ae..8de189de4a 100644
--- a/docs/samples/Microsoft.ML.AutoML.Samples/Program.cs
+++ b/docs/samples/Microsoft.ML.AutoML.Samples/Program.cs
@@ -8,6 +8,8 @@ public static void Main(string[] args)
{
try
{
+ AutoMLExperiment.RunAsync().Wait();
+
RecommendationExperiment.Run();
Console.Clear();
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/ParameterExample.cs b/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/ParameterExample.cs
new file mode 100644
index 0000000000..bac5a60e18
--- /dev/null
+++ b/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/ParameterExample.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.ML.SearchSpace;
+
+namespace Microsoft.ML.AutoML.Samples
+{
+ public static class ParameterExample
+ {
+ public static void Run()
+ {
+ // Parameter is essentially a wrapper class over Json.
+ // Therefore it supports all json types, like integar, number, boolearn, string, etc..
+
+ // To create parameter over existing value, use Parameter.From
+ var intParam = Parameter.FromInt(10);
+ var doubleParam = Parameter.FromDouble(20);
+ var boolParam = Parameter.FromBool(false);
+
+ // To cast parameter to specific type, use Parameter.AsType
+ // NOTE: Casting to a wrong type will trigger an argumentException.
+ var i = intParam.AsType(); // i == 10
+ var d = doubleParam.AsType(); // d == 20
+ var b = boolParam.AsType(); // b == false
+ }
+ }
+}
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/SearchSpaceExample.cs b/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/SearchSpaceExample.cs
new file mode 100644
index 0000000000..0ca0e27dac
--- /dev/null
+++ b/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/SearchSpaceExample.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Text;
+using System.Text.Json;
+using Microsoft.ML.SearchSpace;
+using Microsoft.ML.SearchSpace.Option;
+
+namespace Microsoft.ML.AutoML.Samples
+{
+ public static class SearchSpaceExample
+ {
+ public static void Run()
+ {
+ // The following code shows how to create a SearchSpace for MyParameter.
+ var myParameterSearchSpace = new SearchSpace();
+
+ // Equivalently, you can also create myParameterSearchSpace from scratch.
+ var myParameterSearchSpace2 = new SearchSpace.SearchSpace();
+
+ // numeric options
+ myParameterSearchSpace2["IntOption"] = new UniformIntOption(min: -10, max: 10, logBase: false, defaultValue: 0);
+ myParameterSearchSpace2["SingleOption"] = new UniformSingleOption(min: 1, max: 10, logBase: true, defaultValue: 1);
+ myParameterSearchSpace2["DoubleOption"] = new UniformDoubleOption(min: -10, max: 10, logBase: false, defaultValue: 0);
+
+ // choice options
+ myParameterSearchSpace2["BoolOption"] = new ChoiceOption(true, false);
+ myParameterSearchSpace2["StrOption"] = new ChoiceOption("a", "b", "c");
+
+ // nest options
+ var nestedSearchSpace = new SearchSpace.SearchSpace();
+ nestedSearchSpace["IntOption"] = new UniformIntOption(min: -10, max: 10, logBase: false, defaultValue: 0);
+ myParameterSearchSpace2["Nest"] = nestedSearchSpace;
+
+ // the two search space should be equal
+ Debug.Assert(myParameterSearchSpace.GetHashCode() == myParameterSearchSpace2.GetHashCode());
+ }
+
+ public class MyParameter
+ {
+ [Range((int)-10, 10, 0, false)]
+ public int IntOption { get; set; }
+
+ [Range(1f, 10f, 1f, true)]
+ public float SingleOption { get; set; }
+
+ [Range(-10, 10, false)]
+ public double DoubleOption { get; set; }
+
+ [BooleanChoice]
+ public bool BoolOption { get; set; }
+
+ [Choice("a", "b", "c")]
+ public string StrOption { get; set; }
+
+ [NestOption]
+ public NestParameter Nest { get; set; }
+ }
+
+ public class NestParameter
+ {
+ [Range((int)-10, 10, 0, false)]
+ public int IntOption { get; set; }
+ }
+ }
+}
diff --git a/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/SweepableLightGBMBinaryExperiment.cs b/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/SweepableLightGBMBinaryExperiment.cs
new file mode 100644
index 0000000000..77d7fd3288
--- /dev/null
+++ b/docs/samples/Microsoft.ML.AutoML.Samples/Sweepable/SweepableLightGBMBinaryExperiment.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.ML.Data;
+using Microsoft.ML.SearchSpace;
+
+namespace Microsoft.ML.AutoML.Samples
+{
+ public static class SweepableLightGBMBinaryExperiment
+ {
+ class LightGBMOption
+ {
+ [Range(4, 32768, init: 4, logBase: false)]
+ public int NumberOfLeaves { get; set; } = 4;
+
+ [Range(4, 32768, init: 4, logBase: false)]
+ public int NumberOfTrees { get; set; } = 4;
+ }
+
+ public static async Task RunAsync()
+ {
+ // This example shows how to use Sweepable API to run hyper-parameter optimization over
+ // LightGBM trainer with a customized search space.
+
+ // Create a new context for ML.NET operations. It can be used for
+ // exception tracking and logging, as a catalog of available operations
+ // and as the source of randomness. Setting the seed to a fixed number
+ // in this example to make outputs deterministic.
+ var seed = 0;
+ var context = new MLContext(seed);
+
+ // Create a list of training data points and convert it to IDataView.
+ var data = GenerateRandomBinaryClassificationDataPoints(100, seed);
+ var dataView = context.Data.LoadFromEnumerable(data);
+
+ // Split the dataset into train and test sets with 10% of the data used for testing.
+ var trainTestSplit = context.Data.TrainTestSplit(dataView, testFraction: 0.1);
+
+ // Define a customized search space for LightGBM
+ var lgbmSearchSpace = new SearchSpace();
+
+ // Define the sweepable LightGBM estimator.
+ var lgbm = context.Auto().CreateSweepableEstimator((_context, option) =>
+ {
+ return _context.BinaryClassification.Trainers.LightGbm(
+ "Label",
+ "Features",
+ numberOfLeaves: option.NumberOfLeaves,
+ numberOfIterations: option.NumberOfTrees);
+ }, lgbmSearchSpace);
+
+ // Create sweepable pipeline
+ var pipeline = new EstimatorChain().Append(lgbm);
+
+ // Create an AutoML experiment
+ var experiment = context.Auto().CreateExperiment();
+
+ // Redirect AutoML log to console
+ context.Log += (object o, LoggingEventArgs e) =>
+ {
+ if (e.Source == nameof(AutoMLExperiment) && e.Kind > Runtime.ChannelMessageKind.Trace)
+ {
+ Console.WriteLine(e.RawMessage);
+ }
+ };
+
+ // Config experiment to optimize "Accuracy" metric on given dataset.
+ // This experiment will run hyper-parameter optimization on given pipeline
+ experiment.SetPipeline(pipeline)
+ .SetDataset(trainTestSplit.TrainSet, fold: 5) // use 5-fold cross validation to evaluate each trial
+ .SetBinaryClassificationMetric(BinaryClassificationMetric.Accuracy, "Label")
+ .SetMaxModelToExplore(100); // explore 100 trials
+
+ // start automl experiment
+ var result = await experiment.RunAsync();
+
+ // Expected output samples during training. The pipeline will be unknown because it's created using
+ // customized sweepable estimator, therefore AutoML doesn't have the knowledge of the exact type of the estimator.
+ // Update Running Trial - Id: 0
+ // Update Completed Trial - Id: 0 - Metric: 0.5105967259285338 - Pipeline: Unknown=>Unknown - Duration: 616 - Peak CPU: 0.00% - Peak Memory in MB: 35.54
+ // Update Best Trial - Id: 0 - Metric: 0.5105967259285338 - Pipeline: Unknown=>Unknown
+
+ // evaluate test dataset on best model.
+ var bestModel = result.Model;
+ var eval = bestModel.Transform(trainTestSplit.TestSet);
+ var metrics = context.BinaryClassification.Evaluate(eval);
+
+ PrintMetrics(metrics);
+
+ // Expected output:
+ // Accuracy: 0.67
+ // AUC: 0.75
+ // F1 Score: 0.33
+ // Negative Precision: 0.88
+ // Negative Recall: 0.70
+ // Positive Precision: 0.25
+ // Positive Recall: 0.50
+
+ // TEST POSITIVE RATIO: 0.1667(2.0 / (2.0 + 10.0))
+ // Confusion table
+ // ||======================
+ // PREDICTED || positive | negative | Recall
+ // TRUTH ||======================
+ // positive || 1 | 1 | 0.5000
+ // negative || 3 | 7 | 0.7000
+ // ||======================
+ // Precision || 0.2500 | 0.8750 |
+ }
+
+ private static IEnumerable GenerateRandomBinaryClassificationDataPoints(int count,
+ int seed = 0)
+
+ {
+ var random = new Random(seed);
+ float randomFloat() => (float)random.NextDouble();
+ for (int i = 0; i < count; i++)
+ {
+ var label = randomFloat() > 0.5f;
+ yield return new BinaryClassificationDataPoint
+ {
+ Label = label,
+ // Create random features that are correlated with the label.
+ // For data points with false label, the feature values are
+ // slightly increased by adding a constant.
+ Features = Enumerable.Repeat(label, 50)
+ .Select(x => x ? randomFloat() : randomFloat() +
+ 0.1f).ToArray()
+
+ };
+ }
+ }
+
+ // Example with label and 50 feature values. A data set is a collection of
+ // such examples.
+ private class BinaryClassificationDataPoint
+ {
+ public bool Label { get; set; }
+
+ [VectorType(50)]
+ public float[] Features { get; set; }
+ }
+
+ // Class used to capture predictions.
+ private class Prediction
+ {
+ // Original label.
+ public bool Label { get; set; }
+ // Predicted label from the trainer.
+ public bool PredictedLabel { get; set; }
+ }
+
+ // Pretty-print BinaryClassificationMetrics objects.
+ private static void PrintMetrics(BinaryClassificationMetrics metrics)
+ {
+ Console.WriteLine($"Accuracy: {metrics.Accuracy:F2}");
+ Console.WriteLine($"AUC: {metrics.AreaUnderRocCurve:F2}");
+ Console.WriteLine($"F1 Score: {metrics.F1Score:F2}");
+ Console.WriteLine($"Negative Precision: " +
+ $"{metrics.NegativePrecision:F2}");
+
+ Console.WriteLine($"Negative Recall: {metrics.NegativeRecall:F2}");
+ Console.WriteLine($"Positive Precision: " +
+ $"{metrics.PositivePrecision:F2}");
+
+ Console.WriteLine($"Positive Recall: {metrics.PositiveRecall:F2}\n");
+ Console.WriteLine(metrics.ConfusionMatrix.GetFormattedConfusionTable());
+ }
+ }
+}
diff --git a/docs/samples/Microsoft.ML.Samples.GPU/Microsoft.ML.Samples.GPU.csproj b/docs/samples/Microsoft.ML.Samples.GPU/Microsoft.ML.Samples.GPU.csproj
index c2cf24bf76..8b40f06dae 100644
--- a/docs/samples/Microsoft.ML.Samples.GPU/Microsoft.ML.Samples.GPU.csproj
+++ b/docs/samples/Microsoft.ML.Samples.GPU/Microsoft.ML.Samples.GPU.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6.0
Exe
false
@@ -44,6 +44,7 @@
+
diff --git a/docs/samples/Microsoft.ML.Samples.OneDal/Microsoft.ML.Samples.OneDal.csproj b/docs/samples/Microsoft.ML.Samples.OneDal/Microsoft.ML.Samples.OneDal.csproj
new file mode 100644
index 0000000000..527354dab4
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples.OneDal/Microsoft.ML.Samples.OneDal.csproj
@@ -0,0 +1,54 @@
+
+
+
+ Exe
+ net7.0
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/samples/Microsoft.ML.Samples.OneDal/Program.cs b/docs/samples/Microsoft.ML.Samples.OneDal/Program.cs
new file mode 100644
index 0000000000..3d834630a6
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples.OneDal/Program.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.ML;
+using Microsoft.ML.Data;
+using Microsoft.ML.Trainers;
+using Microsoft.ML.Trainers.FastTree;
+using Newtonsoft.Json;
+
+namespace Microsoft.ML.Samples.OneDal
+{
+ class Program
+ {
+ public static IDataView[] LoadData(
+ MLContext mlContext, string trainingFile, string testingFile,
+ string task, string label = "target", char separator = ',')
+ {
+ List dataList = new List();
+ System.IO.StreamReader file = new System.IO.StreamReader(trainingFile);
+ string header = file.ReadLine();
+ file.Close();
+ string[] headerArray = header.Split(separator);
+ List columns = new List();
+ foreach (string column in headerArray)
+ {
+ if (column == label)
+ {
+ if (task == "binary")
+ columns.Add(new TextLoader.Column(column, DataKind.Boolean, Array.IndexOf(headerArray, column)));
+ else
+ columns.Add(new TextLoader.Column(column, DataKind.Single, Array.IndexOf(headerArray, column)));
+ }
+ else
+ {
+ columns.Add(new TextLoader.Column(column, DataKind.Single, Array.IndexOf(headerArray, column)));
+ }
+ }
+
+ var loader = mlContext.Data.CreateTextLoader(
+ separatorChar: separator,
+ hasHeader: true,
+ columns: columns.ToArray()
+ );
+ dataList.Add(loader.Load(trainingFile));
+ dataList.Add(loader.Load(testingFile));
+ return dataList.ToArray();
+ }
+
+ public static string[] GetFeaturesArray(IDataView data, string labelName = "target")
+ {
+ List featuresList = new List();
+ var nColumns = data.Schema.Count;
+ var columnsEnumerator = data.Schema.GetEnumerator();
+ for (int i = 0; i < nColumns; i++)
+ {
+ columnsEnumerator.MoveNext();
+ if (columnsEnumerator.Current.Name != labelName)
+ featuresList.Add(columnsEnumerator.Current.Name);
+ }
+
+ return featuresList.ToArray();
+ }
+
+ public static double[] RunRandomForestClassification(MLContext mlContext, IDataView trainingData, IDataView testingData, string labelName, int numberOfTrees, int numberOfLeaves)
+ {
+ var featuresArray = GetFeaturesArray(trainingData, labelName);
+ var preprocessingPipeline = mlContext.Transforms.Concatenate("Features", featuresArray);
+ var preprocessedTrainingData = preprocessingPipeline.Fit(trainingData).Transform(trainingData);
+ var preprocessedTestingData = preprocessingPipeline.Fit(trainingData).Transform(testingData);
+
+ FastForestBinaryTrainer.Options options = new FastForestBinaryTrainer.Options();
+ options.LabelColumnName = labelName;
+ options.FeatureColumnName = "Features";
+ options.NumberOfTrees = numberOfTrees;
+ options.NumberOfLeaves = numberOfLeaves;
+ options.MinimumExampleCountPerLeaf = 5;
+ options.FeatureFraction = 1.0;
+
+ var trainer = mlContext.BinaryClassification.Trainers.FastForest(options);
+
+ ITransformer model = trainer.Fit(preprocessedTrainingData);
+
+ IDataView trainingPredictions = model.Transform(preprocessedTrainingData);
+ var trainingMetrics = mlContext.BinaryClassification.EvaluateNonCalibrated(trainingPredictions, labelColumnName: labelName);
+ IDataView testingPredictions = model.Transform(preprocessedTestingData);
+ var testingMetrics = mlContext.BinaryClassification.EvaluateNonCalibrated(testingPredictions, labelColumnName: labelName);
+
+ double[] metrics = new double[4];
+ metrics[0] = trainingMetrics.Accuracy;
+ metrics[1] = testingMetrics.Accuracy;
+ metrics[2] = trainingMetrics.F1Score;
+ metrics[3] = testingMetrics.F1Score;
+ return metrics;
+ }
+
+ public static double[] RunRandomForestRegression(MLContext mlContext, IDataView trainingData, IDataView testingData, string labelName, int numberOfTrees, int numberOfLeaves)
+ {
+ var featuresArray = GetFeaturesArray(trainingData, labelName);
+ var preprocessingPipeline = mlContext.Transforms.Concatenate("Features", featuresArray);
+ var preprocessedTrainingData = preprocessingPipeline.Fit(trainingData).Transform(trainingData);
+ var preprocessedTestingData = preprocessingPipeline.Fit(trainingData).Transform(testingData);
+
+ FastForestRegressionTrainer.Options options = new FastForestRegressionTrainer.Options();
+ options.LabelColumnName = labelName;
+ options.FeatureColumnName = "Features";
+ options.NumberOfTrees = numberOfTrees;
+ options.NumberOfLeaves = numberOfLeaves;
+ options.MinimumExampleCountPerLeaf = 5;
+ options.FeatureFraction = 1.0;
+
+ var trainer = mlContext.Regression.Trainers.FastForest(options);
+
+ ITransformer model = trainer.Fit(preprocessedTrainingData);
+
+ IDataView trainingPredictions = model.Transform(preprocessedTrainingData);
+ var trainingMetrics = mlContext.Regression.Evaluate(trainingPredictions, labelColumnName: labelName);
+ IDataView testingPredictions = model.Transform(preprocessedTestingData);
+ var testingMetrics = mlContext.Regression.Evaluate(testingPredictions, labelColumnName: labelName);
+
+ double[] metrics = new double[4];
+ metrics[0] = trainingMetrics.RootMeanSquaredError;
+ metrics[1] = testingMetrics.RootMeanSquaredError;
+ metrics[2] = trainingMetrics.RSquared;
+ metrics[3] = testingMetrics.RSquared;
+ return metrics;
+ }
+
+ public static double[] RunOLSRegression(MLContext mlContext, IDataView trainingData, IDataView testingData, string labelName)
+ {
+ var featuresArray = GetFeaturesArray(trainingData, labelName);
+ var preprocessingPipeline = mlContext.Transforms.Concatenate("Features", featuresArray);
+ var preprocessedTrainingData = preprocessingPipeline.Fit(trainingData).Transform(trainingData);
+ var preprocessedTestingData = preprocessingPipeline.Fit(trainingData).Transform(testingData);
+
+ OlsTrainer.Options options = new OlsTrainer.Options();
+ options.LabelColumnName = labelName;
+ options.FeatureColumnName = "Features";
+
+ var trainer = mlContext.Regression.Trainers.Ols(options);
+
+ ITransformer model = trainer.Fit(preprocessedTrainingData);
+
+ IDataView trainingPredictions = model.Transform(preprocessedTrainingData);
+ var trainingMetrics = mlContext.Regression.Evaluate(trainingPredictions, labelColumnName: labelName);
+ IDataView testingPredictions = model.Transform(preprocessedTestingData);
+ var testingMetrics = mlContext.Regression.Evaluate(testingPredictions, labelColumnName: labelName);
+
+ double[] metrics = new double[4];
+ metrics[0] = trainingMetrics.RootMeanSquaredError;
+ metrics[1] = testingMetrics.RootMeanSquaredError;
+ metrics[2] = trainingMetrics.RSquared;
+ metrics[3] = testingMetrics.RSquared;
+ return metrics;
+ }
+
+ static void Main(string[] args)
+ {
+ // args[0] - training data filename
+ // args[1] - testing data filename
+ // args[2] - machine learning task (regression, binary)
+ // args[3] - machine learning algorithm (RandomForest, OLS)
+ // Random Forest parameters:
+ // args[4] - NumberOfTrees
+ // args[5] - NumberOfLeaves
+ var mlContext = new MLContext(seed: 42);
+ // data[0] - training subset
+ // data[1] - testing subset
+ IDataView[] data = LoadData(mlContext, args[0], args[1], args[2]);
+ string labelName = "target";
+
+ var mainWatch = System.Diagnostics.Stopwatch.StartNew();
+ double[] metrics;
+ if (args[3] == "RandomForest")
+ {
+ int numberOfTrees = Int32.Parse(args[4]);
+ int numberOfLeaves = Int32.Parse(args[5]);
+ if (args[2] == "binary")
+ {
+
+ metrics = RunRandomForestClassification(mlContext, data[0], data[1], labelName, numberOfTrees, numberOfLeaves);
+ mainWatch.Stop();
+ Console.WriteLine("algorithm,all workflow time[ms],training accuracy,testing accuracy,training F1 score,testing F1 score");
+ Console.WriteLine($"Random Forest Binary,{mainWatch.Elapsed.TotalMilliseconds},{metrics[0]},{metrics[1]},{metrics[2]},{metrics[3]}");
+ }
+ else
+ {
+ metrics = RunRandomForestRegression(mlContext, data[0], data[1], labelName, numberOfTrees, numberOfLeaves);
+ mainWatch.Stop();
+ Console.WriteLine("algorithm,all workflow time[ms],training RMSE,testing RMSE,training R2 score,testing R2 score");
+ Console.WriteLine($"Random Forest Regression,{mainWatch.Elapsed.TotalMilliseconds},{metrics[0]},{metrics[1]},{metrics[2]},{metrics[3]}");
+ }
+ }
+ else if (args[3] == "OLS")
+ {
+ metrics = RunOLSRegression(mlContext, data[0], data[1], labelName);
+ mainWatch.Stop();
+ Console.WriteLine("algorithm,all workflow time[ms],training RMSE,testing RMSE,training R2 score,testing R2 score");
+ Console.WriteLine($"OLS Regression,{mainWatch.Elapsed.TotalMilliseconds},{metrics[0]},{metrics[1]},{metrics[2]},{metrics[3]}");
+ }
+ }
+ }
+}
+
diff --git a/docs/samples/Microsoft.ML.Samples.OneDal/requirements.txt b/docs/samples/Microsoft.ML.Samples.OneDal/requirements.txt
new file mode 100644
index 0000000000..ad5443d796
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples.OneDal/requirements.txt
@@ -0,0 +1,3 @@
+numpy
+pandas
+scikit-learn
diff --git a/docs/samples/Microsoft.ML.Samples.OneDal/run_bench.py b/docs/samples/Microsoft.ML.Samples.OneDal/run_bench.py
new file mode 100644
index 0000000000..f38b229975
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples.OneDal/run_bench.py
@@ -0,0 +1,113 @@
+import numpy as np
+import pandas as pd
+from sklearn.datasets import make_classification, make_regression
+from sklearn.model_selection import train_test_split
+import subprocess
+import sys
+import os
+import io
+
+
+RANDOM_STATE = 42
+np.random.seed(RANDOM_STATE)
+
+
+def convert_to_csv(data, filename, label='target'):
+ data = pd.DataFrame(data, columns=[f'f{i}' for i in range(data.shape[1] - 1)] + ['target'])
+ data[label] = data[label].astype(int)
+ data.to_csv(filename, index=False)
+
+
+def read_output_from_command(command, env):
+ res = subprocess.run(command.split(' '), stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, encoding='utf-8', env=env)
+ return res.stdout[:-1], res.stderr[:-1]
+
+
+def run_case(n_samples, n_features, n_trees, n_leaves, n_runs, task_type):
+ data_params = {
+ 'n_samples': 2 * n_samples,
+ 'n_features': n_features,
+ 'n_informative': n_features // 2,
+ 'random_state': RANDOM_STATE
+ }
+ if task_type == 'binary':
+ x, y = make_classification(**data_params, class_sep=0.7)
+ elif task_type == 'regression':
+ x, y = make_regression(**data_params, noise=0.33)
+
+ data = np.concatenate([x, y.reshape(-1, 1)], axis=1)
+ train_data, test_data = train_test_split(data, test_size=0.5, random_state=RANDOM_STATE)
+
+ train_filename, test_filename = "synth_data_train.csv", "synth_data_test.csv"
+ convert_to_csv(train_data, train_filename)
+ convert_to_csv(test_data, test_filename)
+
+ print(f'n_samples={n_samples}, n_features={n_features}, n_trees={n_trees}, n_leaves={n_leaves}')
+
+ case_dict = {'algorithm': [f'Random Forest {task_type.capitalize()}'],
+ 'n samples': [n_samples], 'n features': [n_features],
+ 'n trees': [n_trees], 'n leaves': [n_leaves]}
+
+ if task_type == 'binary':
+ metrics = ['accuracy', 'F1 score']
+ elif task_type == 'regression':
+ metrics = ['RMSE', 'R2 score']
+ metrics = ['all workflow time[ms]', f'training {metrics[0]}', f'testing {metrics[0]}', f'training {metrics[1]}', f'testing {metrics[1]}']
+ metrics_map_template = {el: '{} ' + f'{el}' for el in metrics}
+
+ result = None
+ for i in range(n_runs):
+ env_copy = os.environ.copy()
+ default_stdout, default_stderr = read_output_from_command(f'dotnet run {train_filename} {test_filename} {task_type} RandomForest {n_trees} {n_leaves}', env_copy)
+
+ print('DEFAULT STDOUT:', default_stdout, 'DEFAULT STDERR:', default_stderr, sep='\n')
+
+ default_res = io.StringIO(default_stdout + '\n')
+ default_res = pd.DataFrame(case_dict).merge(pd.read_csv(default_res))
+
+ env_copy['MLNET_BACKEND'] = 'ONEDAL'
+ optimized_stdout, optimized_stderr = read_output_from_command(f'dotnet run {train_filename} {test_filename} {task_type} RandomForest {n_trees} {n_leaves}', env_copy)
+
+ print('OPTIMIZED STDOUT:', optimized_stdout, 'OPTIMIZED STDERR:', optimized_stderr, sep='\n')
+
+ optimized_res = io.StringIO(optimized_stdout + '\n')
+ optimized_res = pd.read_csv(optimized_res)
+
+ default_res = default_res.rename(columns={k: v.format('ML.NET') for k, v in metrics_map_template.items()})
+ optimized_res = optimized_res.rename(columns={k: v.format('oneDAL') for k, v in metrics_map_template.items()})
+
+ if result is None:
+ result = default_res.merge(optimized_res)
+ else:
+ result = pd.concat([result, default_res.merge(optimized_res)], axis=0)
+
+ all_columns = list(result.columns)
+ groupby_columns = list(case_dict.keys())
+ metric_columns = list(result.columns)
+ for column in groupby_columns:
+ metric_columns.remove(column)
+ result = result.groupby(groupby_columns)[metric_columns].mean().reset_index()
+
+ return result
+
+n_samples_range = [5000, 10000]
+n_features_range = [8, 64]
+n_trees_range = [100]
+n_leaves_range = [128]
+n_runs = 5
+
+result = None
+for task_type in ['binary', 'regression']:
+ for n_samples in n_samples_range:
+ for n_features in n_features_range:
+ for n_trees in n_trees_range:
+ for n_leaves in n_leaves_range:
+ new_result = run_case(n_samples, n_features, n_trees, n_leaves, n_runs, task_type=task_type)
+ if result is None:
+ result = new_result
+ else:
+ result = pd.concat([result, new_result], axis=0)
+
+result.to_csv('result.csv', index=False)
+result.to_csv(sys.stdout, index=False)
diff --git a/docs/samples/Microsoft.ML.Samples.OneDal/run_sample.ps1 b/docs/samples/Microsoft.ML.Samples.OneDal/run_sample.ps1
new file mode 100644
index 0000000000..8b9a720a3d
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples.OneDal/run_sample.ps1
@@ -0,0 +1,14 @@
+$LIBS_LOCATION="..\..\..\artifacts\bin\Native\x64.Debug"
+#ls $LIBS_LOCATION
+$VENV_NAME=".venv"
+# $PYTHON_NAME="python3"
+$PYTHON_NAME="python"
+Write-Host ("Should be installing in directory [" + $VENV_NAME + "]")
+& $PYTHON_NAME -m venv $VENV_NAME
+& ($VENV_NAME + "/Scripts/Activate.ps1")
+& $PYTHON_NAME -m pip --proxy http://proxy-chain.intel.com:911 install -r requirements.txt
+#$env:PATH=("..\..\artifacts\bin\Native\x64.Debug;" + $env:PATH)
+$env:PATH=("$LIBS_LOCATION;" + $env:PATH)
+& $PYTHON_NAME run_bench.py
+deactivate
+
diff --git a/docs/samples/Microsoft.ML.Samples.XGBoost/Microsoft.ML.Samples.XGBoost.csproj b/docs/samples/Microsoft.ML.Samples.XGBoost/Microsoft.ML.Samples.XGBoost.csproj
new file mode 100644
index 0000000000..45907084be
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples.XGBoost/Microsoft.ML.Samples.XGBoost.csproj
@@ -0,0 +1,26 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+ false
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/samples/Microsoft.ML.Samples.XGBoost/Program.cs b/docs/samples/Microsoft.ML.Samples.XGBoost/Program.cs
new file mode 100644
index 0000000000..98dae71ec2
--- /dev/null
+++ b/docs/samples/Microsoft.ML.Samples.XGBoost/Program.cs
@@ -0,0 +1,196 @@
+using System.Reflection;
+using Microsoft.ML;
+using Microsoft.ML.Data;
+
+using Microsoft.ML.Trainers.XGBoost;
+using Newtonsoft.Json.Linq;
+using Microsoft.Data.Analysis;
+using Microsoft.ML.SamplesUtils;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
+namespace Microsoft.ML.Samples.XGBoost
+{
+ public static class Program
+ {
+ public static void Main(string[] args)
+ {
+ var vm = XGBoostUtils.XgbMajorVersion();
+ Console.WriteLine($"The output of checking the version is [{vm.Major}.{vm.Minor}]");
+
+
+ var housingDSet = DatasetUtils.GetFilePathFromDataDirectory("boston_housing.csv");
+ Console.WriteLine($"I'm reading a dataset from {housingDSet}");
+ Console.WriteLine($"The build information on the XGBoost library is {XGBoostUtils.BuildInfo()}");
+
+ // Create a new context for ML.NET operations. It can be used for
+ // exception tracking and logging, as a catalog of available operations
+ // and as the source of randomness. Setting the seed to a fixed number
+ // in this example to make outputs deterministic.
+ var mlContext = new MLContext(seed: 0);
+
+#if false
+ // Create a list of training data points.
+ var dataPoints = GenerateRandomDataPoints(250);
+
+#if false
+ foreach (var dataPoint in dataPoints)
+ {
+ var feats = dataPoint.Features;
+ if (feats != null)
+ {
+ string strVec = string.Join(",", feats.Select(x => x.ToString()).ToArray());
+ Console.WriteLine($"features: [{strVec}], label: {dataPoint.Label}");
+ }
+ }
+#endif
+
+
+ // Convert the list of data points to an IDataView object, which is
+ // consumable by ML.NET API.
+ var trainingData = mlContext.Data.LoadFromEnumerable(dataPoints);
+#else
+ var df = DataFrame.LoadCsv(housingDSet);
+ var trainingData = (df as IDataView);
+ foreach (var c in trainingData.Schema)
+ {
+ Console.WriteLine($"Column {c.Name} is of type {c.Type}");
+ }
+
+ string[] featureColNames = { "CRIM", "ZN", "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD", "TAX", "PTRATIO", "B", "LSTAT" };
+ Console.WriteLine($"Feature columns is of length: {featureColNames.Length}.");
+
+ Console.WriteLine($"The schema of this is {trainingData.Schema}");
+
+ var prepPipeline = mlContext.Transforms.Concatenate("Features", featureColNames);
+ var trainingDataClean = prepPipeline.Fit(trainingData).Transform(trainingData);
+ Console.WriteLine($"After preprocessing, the schema is: {trainingDataClean.Schema}.");
+#endif
+
+#if true
+ // Define the trainer.
+ var pipeline = mlContext.Regression.Trainers.
+ XGBoost(
+ //labelColumnName: nameof(DataPoint.Label),
+ labelColumnName: "MEDV",
+ //featureColumnName: nameof(DataPoint.Features),
+ featureColumnName: "Features",
+ numberOfLeaves: 8
+ );
+#else
+ var pipeline = mlContext.Regression.Trainers.XGBoost(
+ new XGBoostRegressionTrainer.Options
+ {
+ // LabelColumnName = labelName,
+ NumberOfLeaves = 4,
+ MinimumExampleCountPerLeaf = 6,
+ LearningRate = 0.001,
+ Booster = new DartBooster.Options()
+ {
+ TreeDropFraction = 0.124
+ }
+ });
+#endif
+
+#if false
+ try
+ {
+#endif
+ // Train the model.
+ var model = pipeline.Fit(/* trainingData */ trainingDataClean);
+#if false
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"In top level: Exception: {ex.Message}");
+ }
+#endif
+
+#if false
+ // Create testing data. Use different random seed to make it different
+ // from training data.
+ var testData = mlContext.Data.LoadFromEnumerable(
+ GenerateRandomDataPoints(5, seed: 123));
+
+ // Run the model on test data set.
+ var transformedTestData = model.Transform(testData);
+
+ // Convert IDataView object to a list.
+ var predictions = mlContext.Data.CreateEnumerable(
+ transformedTestData, reuseRowObject: false).ToList();
+
+ // Look at 5 predictions for the Label, side by side with the actual
+ // Label for comparison.
+ foreach (var p in predictions)
+ Console.WriteLine($"Label: {p.Label:F3}, Prediction: {p.Score:F3}");
+#endif
+
+#if false
+ // Expected output:
+ // Label: 0.985, Prediction: 0.864
+ // Label: 0.155, Prediction: 0.164
+ // Label: 0.515, Prediction: 0.470
+ // Label: 0.566, Prediction: 0.501
+ // Label: 0.096, Prediction: 0.138
+
+ // Evaluate the overall metrics
+ var metrics = mlContext.Regression.Evaluate(transformedTestData);
+ PrintMetrics(metrics);
+
+ // Expected output:
+ // Mean Absolute Error: 0.10
+ // Mean Squared Error: 0.01
+ // Root Mean Squared Error: 0.11
+ // RSquared: 0.89 (closer to 1 is better. The worst case is 0)
+#endif
+ Console.WriteLine("*** Done!!!");
+ }
+
+ private static IEnumerable GenerateRandomDataPoints(int count,
+ int seed = 0)
+ {
+ var random = new Random(seed);
+ for (int i = 0; i < count; i++)
+ {
+ float label = (float)random.NextDouble();
+ yield return new DataPoint
+ {
+ Label = label,
+ // Create random features that are correlated with the label.
+ Features = Enumerable.Repeat(label, 12).Select(
+ //Features = Enumerable.Repeat(label, 50).Select(
+ x => x + (float)random.NextDouble()).ToArray()
+ };
+ }
+ }
+
+ // Example with label and 50 feature values. A data set is a collection of
+ // such examples.
+ private class DataPoint
+ {
+ public float Label { get; set; }
+ //[VectorType(50)]
+ [VectorType(12)]
+ public float[]? Features { get; set; }
+ }
+
+ // Class used to capture predictions.
+ private class Prediction
+ {
+ // Original label.
+ public float Label { get; set; }
+ // Predicted score from the trainer.
+ public float Score { get; set; }
+ }
+
+ // Print some evaluation metrics to regression problems.
+ private static void PrintMetrics(RegressionMetrics metrics)
+ {
+ Console.WriteLine("Mean Absolute Error: " + metrics.MeanAbsoluteError);
+ Console.WriteLine("Mean Squared Error: " + metrics.MeanSquaredError);
+ Console.WriteLine(
+ "Root Mean Squared Error: " + metrics.RootMeanSquaredError);
+
+ Console.WriteLine("RSquared: " + metrics.RSquared);
+ }
+ }
+}
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/TensorFlow/ImageClassification.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/TensorFlow/ImageClassification.cs
index 88ec4d8d7d..5462982ddc 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/TensorFlow/ImageClassification.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/TensorFlow/ImageClassification.cs
@@ -2,7 +2,9 @@
using System.IO;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Text;
+using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using Microsoft.ML;
@@ -23,7 +25,9 @@ public static void Example()
string modelLocation = "resnet_v2_101_299_frozen.pb";
if (!File.Exists(modelLocation))
{
- modelLocation = Download(@"https://storage.googleapis.com/download.tensorflow.org/models/tflite_11_05_08/resnet_v2_101.tgz", @"resnet_v2_101_299_frozen.tgz");
+ var downloadTask = Download(@"https://storage.googleapis.com/download.tensorflow.org/models/tflite_11_05_08/resnet_v2_101.tgz", @"resnet_v2_101_299_frozen.tgz");
+ downloadTask.Wait();
+ modelLocation = downloadTask.Result;
Unzip(Path.Join(Directory.GetCurrentDirectory(), modelLocation),
Directory.GetCurrentDirectory());
@@ -111,11 +115,18 @@ class OutputScores
public float[] output { get; set; }
}
- private static string Download(string baseGitPath, string dataFile)
+ private static async Task Download(string baseGitPath, string dataFile)
{
- using (WebClient client = new WebClient())
+ if (File.Exists(dataFile))
+ return dataFile;
+
+ using (HttpClient client = new HttpClient())
{
- client.DownloadFile(new Uri($"{baseGitPath}"), dataFile);
+ var response = await client.GetStreamAsync(new Uri($"{baseGitPath}")).ConfigureAwait(false);
+ using (var fs = new FileStream(dataFile, FileMode.CreateNew))
+ {
+ await response.CopyToAsync(fs).ConfigureAwait(false);
+ }
}
return dataFile;
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ImageClassificationDefault.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ImageClassificationDefault.cs
index c9ea3adc9a..a8844c7335 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ImageClassificationDefault.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ImageClassificationDefault.cs
@@ -5,6 +5,7 @@
using System.IO.Compression;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ML;
@@ -244,14 +245,14 @@ public static string DownloadImageSet(string imagesDownloadFolder)
string fileName = "flower_photos_small_set.zip";
string url = $"https://aka.ms/mlnet-resources/datasets/flower_photos_small_set.zip";
- Download(url, imagesDownloadFolder, fileName);
+ Download(url, imagesDownloadFolder, fileName).Wait();
UnZip(Path.Combine(imagesDownloadFolder, fileName), imagesDownloadFolder);
return Path.GetFileNameWithoutExtension(fileName);
}
// Download file to destination directory from input URL.
- public static bool Download(string url, string destDir, string destFileName)
+ public static async Task Download(string url, string destDir, string destFileName)
{
if (destFileName == null)
destFileName = url.Split(Path.DirectorySeparatorChar).Last();
@@ -266,14 +267,18 @@ public static bool Download(string url, string destDir, string destFileName)
return false;
}
- var wc = new WebClient();
Console.WriteLine($"Downloading {relativeFilePath}");
- var download = Task.Run(() => wc.DownloadFile(url, relativeFilePath));
- while (!download.IsCompleted)
+
+ using (HttpClient client = new HttpClient())
{
- Thread.Sleep(1000);
- Console.Write(".");
+ var response = await client.GetStreamAsync(new Uri($"{url}")).ConfigureAwait(false);
+
+ using (var fs = new FileStream(relativeFilePath, FileMode.CreateNew))
+ {
+ await response.CopyToAsync(fs);
+ }
}
+
Console.WriteLine("");
Console.WriteLine($"Downloaded {relativeFilePath}");
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/LearningRateSchedulingCifarResnetTransferLearning.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/LearningRateSchedulingCifarResnetTransferLearning.cs
index c129e50314..c030d56968 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/LearningRateSchedulingCifarResnetTransferLearning.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/LearningRateSchedulingCifarResnetTransferLearning.cs
@@ -5,6 +5,7 @@
using System.IO.Compression;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ML;
@@ -275,7 +276,7 @@ public static string DownloadImageSet(string imagesDownloadFolder)
// https://github.com/YoongiKim/CIFAR-10-images
string url = $"https://github.com/YoongiKim/CIFAR-10-images/archive/refs/heads/master.zip";
- Download(url, imagesDownloadFolder, fileName);
+ Download(url, imagesDownloadFolder, fileName).Wait();
UnZip(Path.Combine(imagesDownloadFolder, fileName),
imagesDownloadFolder);
@@ -283,7 +284,7 @@ public static string DownloadImageSet(string imagesDownloadFolder)
}
// Download file to destination directory from input URL.
- public static bool Download(string url, string destDir, string destFileName)
+ public static async Task Download(string url, string destDir, string destFileName)
{
if (destFileName == null)
destFileName = url.Split(Path.DirectorySeparatorChar).Last();
@@ -298,14 +299,18 @@ public static bool Download(string url, string destDir, string destFileName)
return false;
}
- var wc = new WebClient();
Console.WriteLine($"Downloading {relativeFilePath}");
- var download = Task.Run(() => wc.DownloadFile(url, relativeFilePath));
- while (!download.IsCompleted)
+
+ using (HttpClient client = new HttpClient())
{
- Thread.Sleep(1000);
- Console.Write(".");
+ var response = await client.GetStreamAsync(new Uri($"{url}")).ConfigureAwait(false);
+
+ using (var fs = new FileStream(relativeFilePath, FileMode.CreateNew))
+ {
+ await response.CopyToAsync(fs);
+ }
}
+
Console.WriteLine("");
Console.WriteLine($"Downloaded {relativeFilePath}");
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ResnetV2101TransferLearningEarlyStopping.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ResnetV2101TransferLearningEarlyStopping.cs
index ba6243a498..d8374c143c 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ResnetV2101TransferLearningEarlyStopping.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ResnetV2101TransferLearningEarlyStopping.cs
@@ -5,6 +5,7 @@
using System.IO.Compression;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ML;
@@ -232,14 +233,14 @@ public static string DownloadImageSet(string imagesDownloadFolder)
string fileName = "flower_photos_small_set.zip";
string url = $"https://aka.ms/mlnet-resources/datasets/flower_photos_small_set.zip";
- Download(url, imagesDownloadFolder, fileName);
+ Download(url, imagesDownloadFolder, fileName).Wait();
UnZip(Path.Combine(imagesDownloadFolder, fileName), imagesDownloadFolder);
return Path.GetFileNameWithoutExtension(fileName);
}
// Download file to destination directory from input URL.
- public static bool Download(string url, string destDir, string destFileName)
+ public static async Task Download(string url, string destDir, string destFileName)
{
if (destFileName == null)
destFileName = url.Split(Path.DirectorySeparatorChar).Last();
@@ -254,14 +255,18 @@ public static bool Download(string url, string destDir, string destFileName)
return false;
}
- var wc = new WebClient();
Console.WriteLine($"Downloading {relativeFilePath}");
- var download = Task.Run(() => wc.DownloadFile(url, relativeFilePath));
- while (!download.IsCompleted)
+
+ using (HttpClient client = new HttpClient())
{
- Thread.Sleep(1000);
- Console.Write(".");
+ var response = await client.GetStreamAsync(new Uri($"{url}")).ConfigureAwait(false);
+
+ using (var fs = new FileStream(relativeFilePath, FileMode.CreateNew))
+ {
+ await response.CopyToAsync(fs);
+ }
}
+
Console.WriteLine("");
Console.WriteLine($"Downloaded {relativeFilePath}");
diff --git a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ResnetV2101TransferLearningTrainTestSplit.cs b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ResnetV2101TransferLearningTrainTestSplit.cs
index 6fffbe0ff4..4e4c809396 100644
--- a/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ResnetV2101TransferLearningTrainTestSplit.cs
+++ b/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/ImageClassification/ResnetV2101TransferLearningTrainTestSplit.cs
@@ -5,6 +5,7 @@
using System.IO.Compression;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ML;
@@ -253,14 +254,14 @@ public static string DownloadImageSet(string imagesDownloadFolder)
string fileName = "flower_photos_small_set.zip";
string url = $"https://aka.ms/mlnet-resources/datasets/flower_photos_small_set.zip";
- Download(url, imagesDownloadFolder, fileName);
+ Download(url, imagesDownloadFolder, fileName).Wait();
UnZip(Path.Combine(imagesDownloadFolder, fileName), imagesDownloadFolder);
return Path.GetFileNameWithoutExtension(fileName);
}
// Download file to destination directory from input URL.
- public static bool Download(string url, string destDir, string destFileName)
+ public static async Task Download(string url, string destDir, string destFileName)
{
if (destFileName == null)
destFileName = url.Split(Path.DirectorySeparatorChar).Last();
@@ -275,14 +276,18 @@ public static bool Download(string url, string destDir, string destFileName)
return false;
}
- var wc = new WebClient();
Console.WriteLine($"Downloading {relativeFilePath}");
- var download = Task.Run(() => wc.DownloadFile(url, relativeFilePath));
- while (!download.IsCompleted)
+
+ using (HttpClient client = new HttpClient())
{
- Thread.Sleep(1000);
- Console.Write(".");
+ var response = await client.GetStreamAsync(new Uri($"{url}")).ConfigureAwait(false);
+
+ using (var fs = new FileStream(relativeFilePath, FileMode.CreateNew))
+ {
+ await response.CopyToAsync(fs);
+ }
}
+
Console.WriteLine("");
Console.WriteLine($"Downloaded {relativeFilePath}");
diff --git a/docs/samples/Microsoft.ML.Samples/Microsoft.ML.Samples.csproj b/docs/samples/Microsoft.ML.Samples/Microsoft.ML.Samples.csproj
index e343bdbfe5..3fba560fd5 100644
--- a/docs/samples/Microsoft.ML.Samples/Microsoft.ML.Samples.csproj
+++ b/docs/samples/Microsoft.ML.Samples/Microsoft.ML.Samples.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6.0
Exe
false
@@ -977,6 +977,7 @@
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index bfbb22a1eb..f88769f603 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -7,25 +7,25 @@
-
+
https://github.com/dotnet/arcade
- 82d0268ba6ae13318bcf7fcbcccf83472593ca62
+ df8799988af6503cbcd9544713d30732328c8c57
-
+
https://github.com/dotnet/arcade
- 82d0268ba6ae13318bcf7fcbcccf83472593ca62
+ df8799988af6503cbcd9544713d30732328c8c57
-
+
https://github.com/dotnet/arcade
- 82d0268ba6ae13318bcf7fcbcccf83472593ca62
+ df8799988af6503cbcd9544713d30732328c8c57
-
+
https://github.com/dotnet/arcade
- 82d0268ba6ae13318bcf7fcbcccf83472593ca62
+ df8799988af6503cbcd9544713d30732328c8c57
-
+
https://github.com/dotnet/arcade
- 82d0268ba6ae13318bcf7fcbcccf83472593ca62
+ df8799988af6503cbcd9544713d30732328c8c57
https://github.com/dotnet/arcade-services
@@ -39,9 +39,9 @@
https://github.com/dotnet/xharness
89cb4b1d368e0f15b4df8e02a176dd1f1c33958b
-
+
https://github.com/dotnet/arcade
- 82d0268ba6ae13318bcf7fcbcccf83472593ca62
+ df8799988af6503cbcd9544713d30732328c8c57
https://github.com/dotnet/roslyn
diff --git a/eng/Versions.props b/eng/Versions.props
index 2d3bc97696..052539cb30 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -21,7 +21,7 @@
4.5.0
1.5.0
4.5.0
- 4.5.3
+ 4.5.5
4.3.0
4.3.0
6.0.0
@@ -37,19 +37,31 @@
3.9.0
1.0.0-beta.22504.6
1.0.0-beta.22504.6
- 1.10.0
+ 1.14.0
0.0.0.12
+
+ 2023.0.0.23189
+ 2023.0.0.23046
+ 2023.0.0.22995
+ 2023.0.0.23189
+ 2021.7.1.15305
+ 2021.7.1.15005
+ 2021.7.1.14939
+ 1
+ 1
2.1.0
2.1.0
13.0.1
2.1.3
0.0.1
- 1.3.3
+ 1.4.0
0.20.1
2
2.3.1
- 0.98.3
- 1.11.0.1
+ 0.99.5
+ 1.13.0.1
1.12.4
3.1.2
@@ -64,9 +76,10 @@
5.4.7
0.12.0
6.0.9
+ 8.0.0-preview.3.23174.8
5.10.2
1.1.2-beta1.22512.1
- 7.0.0-beta.22572.6
+ 7.0.0-beta.23228.7
2.1.0
3.0.1
0.0.6-test
diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1
index bab18543d6..bcb579e37a 100644
--- a/eng/common/generate-locproject.ps1
+++ b/eng/common/generate-locproject.ps1
@@ -34,6 +34,25 @@ $jsonTemplateFiles | ForEach-Object {
$jsonWinformsTemplateFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern
$wxlFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\.+\.wxl" -And -Not( $_.Directory.Name -Match "\d{4}" ) } # localized files live in four digit lang ID directories; this excludes them
+if (-not $wxlFiles) {
+ $wxlEnFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\\1033\\.+\.wxl" } # pick up en files (1033 = en) specifically so we can copy them to use as the neutral xlf files
+ if ($wxlEnFiles) {
+ $wxlFiles = @()
+ $wxlEnFiles | ForEach-Object {
+ $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)"
+ $wxlFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru
+ }
+ }
+}
+
+$macosHtmlEnFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\.lproj\\.+\.html" } # add installer HTML files
+$macosHtmlFiles = @()
+if ($macosHtmlEnFiles) {
+ $macosHtmlEnFiles | ForEach-Object {
+ $destinationFile = "$($_.Directory.Parent.FullName)\$($_.Name)"
+ $macosHtmlFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru
+ }
+}
$xlfFiles = @()
@@ -91,6 +110,7 @@ $locJson = @{
)
},
@{
+ LanguageSet = $LanguageSet
CloneLanguageSet = "WiX_CloneLanguages"
LssFiles = @( "wxl_loc.lss" )
LocItems = @(
@@ -98,8 +118,7 @@ $locJson = @{
$outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\"
$continue = $true
foreach ($exclusion in $exclusions.Exclusions) {
- if ($_.FullName.Contains($exclusion))
- {
+ if ($_.FullName.Contains($exclusion)) {
$continue = $false
}
}
@@ -110,7 +129,30 @@ $locJson = @{
SourceFile = $sourceFile
CopyOption = "LangIDOnPath"
OutputPath = $outputPath
- Languages = "cs-CZ;de-DE;es-ES;fr-FR;it-IT;ja-JP;ko-KR;pl-PL;pt-BR;ru-RU;tr-TR;zh-CN;zh-TW"
+ }
+ }
+ }
+ )
+ },
+ @{
+ LanguageSet = $LanguageSet
+ CloneLanguageSet = "VS_macOS_CloneLanguages"
+ LssFiles = @( ".\eng\common\loc\P22DotNetHtmlLocalization.lss" )
+ LocItems = @(
+ $macosHtmlFiles | ForEach-Object {
+ $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\"
+ $continue = $true
+ foreach ($exclusion in $exclusions.Exclusions) {
+ if ($_.FullName.Contains($exclusion)) {
+ $continue = $false
+ }
+ }
+ $sourceFile = ($_.FullName | Resolve-Path -Relative)
+ if ($continue) {
+ return @{
+ SourceFile = $sourceFile
+ CopyOption = "LangIDOnPath"
+ OutputPath = $outputPath
}
}
}
diff --git a/eng/common/loc/P22DotNetHtmlLocalization.lss b/eng/common/loc/P22DotNetHtmlLocalization.lss
new file mode 100644
index 0000000000..6661fed566
Binary files /dev/null and b/eng/common/loc/P22DotNetHtmlLocalization.lss differ
diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh
index 41a26d802a..f13b74080e 100644
--- a/eng/common/native/init-compiler.sh
+++ b/eng/common/native/init-compiler.sh
@@ -71,7 +71,7 @@ if [[ -z "$CLR_CC" ]]; then
# Set default versions
if [[ -z "$majorVersion" ]]; then
# note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero.
- if [[ "$compiler" == "clang" ]]; then versions=( 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5 )
+ if [[ "$compiler" == "clang" ]]; then versions=( 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5 )
elif [[ "$compiler" == "gcc" ]]; then versions=( 12 11 10 9 8 7 6 5 4.9 ); fi
for version in "${versions[@]}"; do
diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml
index aaeb83b4dc..7c164ac02f 100644
--- a/eng/common/templates/job/execute-sdl.yml
+++ b/eng/common/templates/job/execute-sdl.yml
@@ -46,6 +46,7 @@ jobs:
- template: /eng/common/templates/variables/sdl-variables.yml
- name: GuardianVersion
value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }}
+ - template: /eng/common/templates/variables/pool-providers.yml
pool:
# We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
@@ -53,7 +54,7 @@ jobs:
demands: Cmd
# If it's not devdiv, it's dnceng
${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
- name: NetCore1ESPool-Svc-Internal
+ name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64
steps:
- checkout: self
diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml
index e3ba939801..3cb5145eab 100644
--- a/eng/common/templates/job/job.yml
+++ b/eng/common/templates/job/job.yml
@@ -24,7 +24,7 @@ parameters:
enablePublishBuildAssets: false
enablePublishTestResults: false
enablePublishUsingPipelines: false
- disableComponentGovernance: false
+ disableComponentGovernance: ''
mergeTestResults: false
testRunTitle: ''
testResultsFormat: ''
@@ -142,9 +142,13 @@ jobs:
richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin
continueOnError: true
- - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), ne(parameters.disableComponentGovernance, 'true')) }}:
- - task: ComponentGovernanceComponentDetection@0
- continueOnError: true
+ - template: /eng/common/templates/steps/component-governance.yml
+ parameters:
+ ${{ if eq(parameters.disableComponentGovernance, '') }}:
+ ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(contains(variables['Build.SourceBranch'], 'internal/release'), eq(variables['Build.SourceBranch'], 'main'))) }}:
+ disableComponentGovernance: false
+ ${{ else }}:
+ disableComponentGovernance: true
- ${{ if eq(parameters.enableMicrobuild, 'true') }}:
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
diff --git a/eng/common/templates/job/onelocbuild.yml b/eng/common/templates/job/onelocbuild.yml
index 6b8fc99708..60ab00c4de 100644
--- a/eng/common/templates/job/onelocbuild.yml
+++ b/eng/common/templates/job/onelocbuild.yml
@@ -14,6 +14,7 @@ parameters:
ReusePr: true
UseLfLineEndings: true
UseCheckedInLocProjectJson: false
+ SkipLocProjectJsonGeneration: false
LanguageSet: VS_Main_Languages
LclSource: lclFilesInRepo
LclPackageId: ''
@@ -22,13 +23,25 @@ parameters:
MirrorRepo: ''
MirrorBranch: main
condition: ''
+ JobNameSuffix: ''
jobs:
-- job: OneLocBuild
+- job: OneLocBuild${{ parameters.JobNameSuffix }}
dependsOn: ${{ parameters.dependsOn }}
- displayName: OneLocBuild
+ displayName: OneLocBuild${{ parameters.JobNameSuffix }}
+
+ variables:
+ - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat
+ - name: _GenerateLocProjectArguments
+ value: -SourcesDirectory ${{ parameters.SourcesDirectory }}
+ -LanguageSet "${{ parameters.LanguageSet }}"
+ -CreateNeutralXlfs
+ - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}:
+ - name: _GenerateLocProjectArguments
+ value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson
+ - template: /eng/common/templates/variables/pool-providers.yml
${{ if ne(parameters.pool, '') }}:
pool: ${{ parameters.pool }}
@@ -40,27 +53,17 @@ jobs:
demands: Cmd
# If it's not devdiv, it's dnceng
${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
- name: NetCore1ESPool-Svc-Internal
+ name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64
- variables:
- - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat
- - name: _GenerateLocProjectArguments
- value: -SourcesDirectory ${{ parameters.SourcesDirectory }}
- -LanguageSet "${{ parameters.LanguageSet }}"
- -CreateNeutralXlfs
- - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}:
- - name: _GenerateLocProjectArguments
- value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson
-
-
steps:
- - task: Powershell@2
- inputs:
- filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1
- arguments: $(_GenerateLocProjectArguments)
- displayName: Generate LocProject.json
- condition: ${{ parameters.condition }}
+ - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}:
+ - task: Powershell@2
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1
+ arguments: $(_GenerateLocProjectArguments)
+ displayName: Generate LocProject.json
+ condition: ${{ parameters.condition }}
- task: OneLocBuild@2
displayName: OneLocBuild
diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml
index 1cbb6a0c56..c5fedd7f70 100644
--- a/eng/common/templates/job/publish-build-assets.yml
+++ b/eng/common/templates/job/publish-build-assets.yml
@@ -34,15 +34,15 @@ jobs:
- job: Asset_Registry_Publish
dependsOn: ${{ parameters.dependsOn }}
+ timeoutInMinutes: 150
${{ if eq(parameters.publishAssetsImmediately, 'true') }}:
displayName: Publish Assets
${{ else }}:
displayName: Publish to Build Asset Registry
- pool: ${{ parameters.pool }}
-
variables:
+ - template: /eng/common/templates/variables/pool-providers.yml
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- group: Publish-Build-Assets
- group: AzureDevOps-Artifact-Feeds-Pats
@@ -51,6 +51,16 @@ jobs:
- ${{ if eq(parameters.publishAssetsImmediately, 'true') }}:
- template: /eng/common/templates/post-build/common-variables.yml
+ pool:
+ # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
+ ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
+ name: VSEngSS-MicroBuild2022-1ES
+ demands: Cmd
+ # If it's not devdiv, it's dnceng
+ ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
+ name: $(DncEngInternalBuildPool)
+ demands: ImageOverride -equals windows.vs2019.amd64
+
steps:
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- task: DownloadBuildArtifacts@0
diff --git a/eng/common/templates/job/source-build.yml b/eng/common/templates/job/source-build.yml
index b6137f44ad..8a3deef2b7 100644
--- a/eng/common/templates/job/source-build.yml
+++ b/eng/common/templates/job/source-build.yml
@@ -44,13 +44,16 @@ jobs:
${{ if eq(parameters.platform.pool, '') }}:
# The default VM host AzDO pool. This should be capable of running Docker containers: almost all
# source-build builds run in Docker, including the default managed platform.
+ # /eng/common/templates/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic
pool:
${{ if eq(variables['System.TeamProject'], 'public') }}:
- name: NetCore-Svc-Public
+ name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')]
demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open
+
${{ if eq(variables['System.TeamProject'], 'internal') }}:
- name: NetCore1ESPool-Svc-Internal
+ name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')]
demands: ImageOverride -equals Build.Ubuntu.1804.Amd64
+
${{ if ne(parameters.platform.pool, '') }}:
pool: ${{ parameters.platform.pool }}
diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml
index 59a42c338a..09c506d118 100644
--- a/eng/common/templates/job/source-index-stage1.yml
+++ b/eng/common/templates/job/source-index-stage1.yml
@@ -22,16 +22,17 @@ jobs:
value: ${{ parameters.binlogPath }}
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- group: source-dot-net stage1 variables
+ - template: /eng/common/templates/variables/pool-providers.yml
${{ if ne(parameters.pool, '') }}:
pool: ${{ parameters.pool }}
${{ if eq(parameters.pool, '') }}:
pool:
${{ if eq(variables['System.TeamProject'], 'public') }}:
- name: NetCore-Svc-Public
+ name: $(DncEngPublicBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64.open
${{ if eq(variables['System.TeamProject'], 'internal') }}:
- name: NetCore1ESPool-Svc-Internal
+ name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64
steps:
diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml
index 297e7946b0..289bb2396c 100644
--- a/eng/common/templates/jobs/jobs.yml
+++ b/eng/common/templates/jobs/jobs.yml
@@ -88,15 +88,6 @@ jobs:
- ${{ job.job }}
- ${{ if eq(parameters.enableSourceBuild, true) }}:
- Source_Build_Complete
- pool:
- # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
- ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
- name: VSEngSS-MicroBuild2022-1ES
- demands: Cmd
- # If it's not devdiv, it's dnceng
- ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
- name: NetCore1ESPool-Svc-Internal
- demands: ImageOverride -equals windows.vs2019.amd64
runAsPublic: ${{ parameters.runAsPublic }}
publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }}
diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml
index 957375c1c1..c051f1b65e 100644
--- a/eng/common/templates/post-build/post-build.yml
+++ b/eng/common/templates/post-build/post-build.yml
@@ -95,6 +95,7 @@ stages:
displayName: Validate Build Assets
variables:
- template: common-variables.yml
+ - template: /eng/common/templates/variables/pool-providers.yml
jobs:
- job:
displayName: NuGet Validation
@@ -106,7 +107,7 @@ stages:
demands: Cmd
# If it's not devdiv, it's dnceng
${{ else }}:
- name: NetCore1ESPool-Svc-Internal
+ name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64
steps:
@@ -143,7 +144,7 @@ stages:
demands: Cmd
# If it's not devdiv, it's dnceng
${{ else }}:
- name: NetCore1ESPool-Svc-Internal
+ name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64
steps:
- template: setup-maestro-vars.yml
@@ -203,7 +204,7 @@ stages:
demands: Cmd
# If it's not devdiv, it's dnceng
${{ else }}:
- name: NetCore1ESPool-Svc-Internal
+ name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64
steps:
- template: setup-maestro-vars.yml
@@ -251,6 +252,7 @@ stages:
displayName: Publish using Darc
variables:
- template: common-variables.yml
+ - template: /eng/common/templates/variables/pool-providers.yml
jobs:
- job:
displayName: Publish Using Darc
@@ -262,7 +264,7 @@ stages:
demands: Cmd
# If it's not devdiv, it's dnceng
${{ else }}:
- name: NetCore1ESPool-Svc-Internal
+ name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64
steps:
- template: setup-maestro-vars.yml
@@ -282,4 +284,4 @@ stages:
-MaestroToken '$(MaestroApiAccessToken)'
-WaitPublishingFinish true
-ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}'
- -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}'
\ No newline at end of file
+ -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}'
diff --git a/eng/common/templates/steps/component-governance.yml b/eng/common/templates/steps/component-governance.yml
new file mode 100644
index 0000000000..babc2757d8
--- /dev/null
+++ b/eng/common/templates/steps/component-governance.yml
@@ -0,0 +1,10 @@
+parameters:
+ disableComponentGovernance: false
+
+steps:
+- ${{ if eq(parameters.disableComponentGovernance, 'true') }}:
+ - script: "echo ##vso[task.setvariable variable=skipComponentGovernanceDetection]true"
+ displayName: Set skipComponentGovernanceDetection variable
+- ${{ if ne(parameters.disableComponentGovernance, 'true') }}:
+ - task: ComponentGovernanceComponentDetection@0
+ continueOnError: true
\ No newline at end of file
diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml
index 12a8ff94d8..4624885e3b 100644
--- a/eng/common/templates/steps/source-build.yml
+++ b/eng/common/templates/steps/source-build.yml
@@ -63,6 +63,11 @@ steps:
targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}'
fi
+ runtimeOsArgs=
+ if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then
+ runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}'
+ fi
+
publishArgs=
if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then
publishArgs='--publish'
@@ -75,6 +80,7 @@ steps:
$internalRuntimeDownloadArgs \
$internalRestoreArgs \
$targetRidArgs \
+ $runtimeOsArgs \
/p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \
/p:ArcadeBuildFromSource=true
displayName: Build
diff --git a/eng/common/templates/variables/pool-providers.yml b/eng/common/templates/variables/pool-providers.yml
new file mode 100644
index 0000000000..99c80212ba
--- /dev/null
+++ b/eng/common/templates/variables/pool-providers.yml
@@ -0,0 +1,57 @@
+# Select a pool provider based off branch name. Anything with branch name containing 'release' must go into an -Svc pool,
+# otherwise it should go into the "normal" pools. This separates out the queueing and billing of released branches.
+
+# Motivation:
+# Once a given branch of a repository's output has been officially "shipped" once, it is then considered to be COGS
+# (Cost of goods sold) and should be moved to a servicing pool provider. This allows both separation of queueing
+# (allowing release builds and main PR builds to not intefere with each other) and billing (required for COGS)
+# Additionally, the pool provider name itself may be subject to change when the .NET Core Engineering Services
+# team needs to move resources around and create new and potentially differently-named pools. Using this template
+# file from an Arcade-ified repo helps guard against both having to update one's release/* branches and renaming.
+
+# How to use:
+# This yaml assumes your shipped product branches use the naming convention "release/..." (which many do).
+# If we find alternate naming conventions in broad usage these can be added to the condition below.
+#
+# First, import the template in an arcade-ified repo to pick up the variables, e.g.:
+#
+# variables:
+# - template: /eng/common/templates/variables/pool-providers.yml
+#
+# ... then anywhere specifying the pool provider use the runtime variables,
+# $(DncEngInternalBuildPool) and $ (DncEngPublicBuildPool), e.g.:
+#
+# pool:
+# name: $(DncEngInternalBuildPool)
+# demands: ImageOverride -equals windows.vs2019.amd64
+
+variables:
+ # Coalesce the target and source branches so we know when a PR targets a release branch
+ # If these variables are somehow missing, fall back to main (tends to have more capacity)
+
+ # Any new -Svc alternative pools should have variables added here to allow for splitting work
+ - name: DncEngPublicBuildPool
+ value: $[
+ replace(
+ replace(
+ eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'),
+ True,
+ 'NetCore-Svc-Public'
+ ),
+ False,
+ 'NetCore-Public'
+ )
+ ]
+
+ - name: DncEngInternalBuildPool
+ value: $[
+ replace(
+ replace(
+ eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'),
+ True,
+ 'NetCore1ESPool-Svc-Internal'
+ ),
+ False,
+ 'NetCore1ESPool-Internal'
+ )
+ ]
\ No newline at end of file
diff --git a/eng/common/tools.sh b/eng/common/tools.sh
index c110d0ed41..e9a7ed9af6 100755
--- a/eng/common/tools.sh
+++ b/eng/common/tools.sh
@@ -511,7 +511,7 @@ global_json_file="${repo_root}global.json"
# determine if global.json contains a "runtimes" entry
global_json_has_runtimes=false
if command -v jq &> /dev/null; then
- if jq -er '. | select(has("runtimes"))' "$global_json_file" &> /dev/null; then
+ if jq -e '.tools | has("runtimes")' "$global_json_file" &> /dev/null; then
global_json_has_runtimes=true
fi
elif [[ "$(cat "$global_json_file")" =~ \"runtimes\"[[:space:]\:]*\{ ]]; then
diff --git a/global.json b/global.json
index a28bce215a..3e51796287 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"tools": {
- "dotnet": "7.0.100",
+ "dotnet": "7.0.105",
"runtimes": {
"dotnet/x64": [
"$(DotNetRuntime60Version)"
@@ -11,8 +11,8 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22572.6",
- "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22572.6",
+ "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.23228.7",
+ "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.23228.7",
"Microsoft.Build.Traversal": "3.2.0",
"Microsoft.SourceLink.GitHub": "1.1.0-beta-20206-02",
"Microsoft.SourceLink.Common": "1.1.0-beta-20206-02"
diff --git a/src/Microsoft.Data.Analysis.Interactive/Microsoft.Data.Analysis.Interactive.csproj b/src/Microsoft.Data.Analysis.Interactive/Microsoft.Data.Analysis.Interactive.csproj
index 241a44cbca..e95ca64713 100644
--- a/src/Microsoft.Data.Analysis.Interactive/Microsoft.Data.Analysis.Interactive.csproj
+++ b/src/Microsoft.Data.Analysis.Interactive/Microsoft.Data.Analysis.Interactive.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6.0
false
diff --git a/src/Microsoft.Data.Analysis/IDataView.Extension.cs b/src/Microsoft.Data.Analysis/IDataView.Extension.cs
index aae5c38e63..545765f010 100644
--- a/src/Microsoft.Data.Analysis/IDataView.Extension.cs
+++ b/src/Microsoft.Data.Analysis/IDataView.Extension.cs
@@ -116,6 +116,10 @@ public static DataFrame ToDataFrame(this IDataView dataView, long maxRows, param
{
dataFrameColumns.Add(new StringDataFrameColumn(dataViewColumn.Name));
}
+ else if (type is VectorDataViewType vectorType)
+ {
+ dataFrameColumns.Add(GetVectorDataFrame(vectorType, dataViewColumn.Name));
+ }
else
{
throw new NotSupportedException(String.Format(Microsoft.Data.Strings.NotSupportedColumnType, type.RawType.Name));
@@ -143,6 +147,70 @@ public static DataFrame ToDataFrame(this IDataView dataView, long maxRows, param
return new DataFrame(dataFrameColumns);
}
+
+ private static DataFrameColumn GetVectorDataFrame(VectorDataViewType vectorType, string name)
+ {
+ var itemType = vectorType.ItemType;
+
+ if (itemType.RawType == typeof(bool))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(byte))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(double))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(float))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(int))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(long))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(sbyte))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(short))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(uint))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(ulong))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(ushort))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(char))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(decimal))
+ {
+ return new VBufferDataFrameColumn(name);
+ }
+ else if (itemType.RawType == typeof(ReadOnlyMemory))
+ {
+ return new VBufferDataFrameColumn>(name);
+ }
+
+ throw new NotSupportedException(String.Format(Microsoft.Data.Strings.VectorSubTypeNotSupported, itemType.ToString()));
+ }
}
}
diff --git a/src/Microsoft.Data.Analysis/Microsoft.Data.Analysis.csproj b/src/Microsoft.Data.Analysis/Microsoft.Data.Analysis.csproj
index c4e3ca036a..acfcd62199 100644
--- a/src/Microsoft.Data.Analysis/Microsoft.Data.Analysis.csproj
+++ b/src/Microsoft.Data.Analysis/Microsoft.Data.Analysis.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/src/Microsoft.Data.Analysis/PrimitiveColumnContainer.cs b/src/Microsoft.Data.Analysis/PrimitiveColumnContainer.cs
index 063a9b50af..df5a09c2a5 100644
--- a/src/Microsoft.Data.Analysis/PrimitiveColumnContainer.cs
+++ b/src/Microsoft.Data.Analysis/PrimitiveColumnContainer.cs
@@ -257,17 +257,10 @@ public void Apply(Func func, PrimitiveColumnContainer buffer = Buffers[b];
- long prevLength = checked(Buffers[0].Length * b);
- DataFrameBuffer mutableBuffer = DataFrameBuffer.GetMutableBuffer(buffer);
- Buffers[b] = mutableBuffer;
- Span span = mutableBuffer.Span;
- DataFrameBuffer mutableNullBitMapBuffer = DataFrameBuffer.GetMutableBuffer(NullBitMapBuffers[b]);
- NullBitMapBuffers[b] = mutableNullBitMapBuffer;
- Span nullBitMapSpan = mutableNullBitMapBuffer.Span;
+ ReadOnlyDataFrameBuffer sourceBuffer = Buffers[b];
+ ReadOnlySpan sourceNullBitMap = NullBitMapBuffers[b].ReadOnlySpan;
ReadOnlyDataFrameBuffer resultBuffer = resultContainer.Buffers[b];
- long resultPrevLength = checked(resultContainer.Buffers[0].Length * b);
DataFrameBuffer resultMutableBuffer = DataFrameBuffer.GetMutableBuffer(resultBuffer);
resultContainer.Buffers[b] = resultMutableBuffer;
Span resultSpan = resultMutableBuffer.Span;
@@ -275,13 +268,12 @@ public void Apply(Func func, PrimitiveColumnContainer resultNullBitMapSpan = resultMutableNullBitMapBuffer.Span;
- for (int i = 0; i < span.Length; i++)
+ for (int i = 0; i < Buffers[b].Length; i++)
{
- long curIndex = i + prevLength;
- bool isValid = IsValid(nullBitMapSpan, i);
- TResult? value = func(isValid ? span[i] : default(T?));
+ bool isValid = IsValid(sourceNullBitMap, i);
+ TResult? value = func(isValid ? sourceBuffer[i] : default(T?));
resultSpan[i] = value.GetValueOrDefault();
- SetValidityBit(resultNullBitMapSpan, i, value != null);
+ resultContainer.SetValidityBit(resultNullBitMapSpan, i, value != null);
}
}
}
diff --git a/src/Microsoft.Data.Analysis/Strings.Designer.cs b/src/Microsoft.Data.Analysis/Strings.Designer.cs
index 9cbf90f38e..38cabf7ecb 100644
--- a/src/Microsoft.Data.Analysis/Strings.Designer.cs
+++ b/src/Microsoft.Data.Analysis/Strings.Designer.cs
@@ -19,7 +19,7 @@ namespace Microsoft.Data {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Strings {
@@ -437,5 +437,14 @@ internal static string StreamDoesntSupportReading {
return ResourceManager.GetString("StreamDoesntSupportReading", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to Specified vector subtype {0} is not supported..
+ ///
+ internal static string VectorSubTypeNotSupported {
+ get {
+ return ResourceManager.GetString("VectorSubTypeNotSupported", resourceCulture);
+ }
+ }
}
}
diff --git a/src/Microsoft.Data.Analysis/Strings.resx b/src/Microsoft.Data.Analysis/Strings.resx
index 79764037cc..53120a4a73 100644
--- a/src/Microsoft.Data.Analysis/Strings.resx
+++ b/src/Microsoft.Data.Analysis/Strings.resx
@@ -243,4 +243,8 @@
Stream doesn't support reading
+
+ Specified vector subtype {0} is not supported.
+ {0} vectory subtype
+
\ No newline at end of file
diff --git a/src/Microsoft.Data.Analysis/VBufferDataFrameColumn.cs b/src/Microsoft.Data.Analysis/VBufferDataFrameColumn.cs
new file mode 100644
index 0000000000..35456f34c4
--- /dev/null
+++ b/src/Microsoft.Data.Analysis/VBufferDataFrameColumn.cs
@@ -0,0 +1,401 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using Apache.Arrow;
+using Apache.Arrow.Types;
+using Microsoft.ML;
+using Microsoft.ML.Data;
+
+namespace Microsoft.Data.Analysis
+{
+ ///
+ /// Column to hold VBuffer
+ ///
+ public partial class VBufferDataFrameColumn : DataFrameColumn, IEnumerable>
+ {
+ private readonly List>> _vBuffers = new List>>(); // To store more than intMax number of vbuffers
+
+ ///
+ /// Constructs an empty VBufferDataFrameColumn with the given .
+ ///
+ /// The name of the column.
+ /// Length of values
+ public VBufferDataFrameColumn(string name, long length = 0) : base(name, length, typeof(VBuffer))
+ {
+ int numberOfBuffersRequired = Math.Max((int)(length / int.MaxValue), 1);
+ for (int i = 0; i < numberOfBuffersRequired; i++)
+ {
+ long bufferLen = length - _vBuffers.Count * int.MaxValue;
+ List> buffer = new List>((int)Math.Min(int.MaxValue, bufferLen));
+ _vBuffers.Add(buffer);
+ for (int j = 0; j < bufferLen; j++)
+ {
+ buffer.Add(default);
+ }
+ }
+ }
+
+ public VBufferDataFrameColumn(string name, IEnumerable> values) : base(name, 0, typeof(VBuffer))
+ {
+ values = values ?? throw new ArgumentNullException(nameof(values));
+ if (_vBuffers.Count == 0)
+ {
+ _vBuffers.Add(new List>());
+ }
+ foreach (var value in values)
+ {
+ Append(value);
+ }
+ }
+
+ private long _nullCount;
+
+ public override long NullCount => _nullCount;
+
+ protected internal override void Resize(long length)
+ {
+ if (length < Length)
+ throw new ArgumentException(Strings.CannotResizeDown, nameof(length));
+
+ for (long i = Length; i < length; i++)
+ {
+ Append(default);
+ }
+ }
+
+ public void Append(VBuffer value)
+ {
+ List> lastBuffer = _vBuffers[_vBuffers.Count - 1];
+ if (lastBuffer.Count == int.MaxValue)
+ {
+ lastBuffer = new List>();
+ _vBuffers.Add(lastBuffer);
+ }
+ lastBuffer.Add(value);
+ Length++;
+ }
+
+ private int GetBufferIndexContainingRowIndex(ref long rowIndex)
+ {
+ if (rowIndex > Length)
+ {
+ throw new ArgumentOutOfRangeException(Strings.ColumnIndexOutOfRange, nameof(rowIndex));
+ }
+ return (int)(rowIndex / int.MaxValue);
+ }
+
+ protected override object GetValue(long rowIndex)
+ {
+ int bufferIndex = GetBufferIndexContainingRowIndex(ref rowIndex);
+ return _vBuffers[bufferIndex][(int)rowIndex];
+ }
+
+ protected override IReadOnlyList
@@ -68,6 +70,12 @@
+
+
+ PreserveNewest
+
+
+
diff --git a/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/Images.cs b/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/Images.cs
index 8998151e5e..ec2281f58e 100644
--- a/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/Images.cs
+++ b/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/Images.cs
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
+using Microsoft.ML.Vision;
+
namespace Microsoft.ML.AutoML.CodeGen
{
internal partial class LoadImages
@@ -40,8 +42,16 @@ internal partial class ImageClassificationMulti
{
public override IEstimator BuildFromOption(MLContext context, ImageClassificationOption param)
{
+ var option = new ImageClassificationTrainer.Options
+ {
+ Arch = param.Arch,
+ BatchSize = param.BatchSize,
+ LabelColumnName = param.LabelColumnName,
+ FeatureColumnName = param.FeatureColumnName,
+ ScoreColumnName = param.ScoreColumnName,
+ };
- return context.MulticlassClassification.Trainers.ImageClassification(param.LabelColumnName, param.FeatureColumnName, param.ScoreColumnName);
+ return context.MulticlassClassification.Trainers.ImageClassification(option);
}
}
diff --git a/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/MapValueToKey.cs b/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/MapValueToKey.cs
index 36dbd5de7f..a48827478f 100644
--- a/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/MapValueToKey.cs
+++ b/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/MapValueToKey.cs
@@ -8,7 +8,7 @@ internal partial class MapValueToKey
{
public override IEstimator BuildFromOption(MLContext context, MapValueToKeyOption param)
{
- return context.Transforms.Conversion.MapValueToKey(param.OutputColumnName, param.InputColumnName);
+ return context.Transforms.Conversion.MapValueToKey(param.OutputColumnName, param.InputColumnName, addKeyValueAnnotationsAsText: param.AddKeyValueAnnotationsAsText);
}
}
diff --git a/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/ObjectDetection.cs b/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/ObjectDetection.cs
new file mode 100644
index 0000000000..c9ac24737e
--- /dev/null
+++ b/src/Microsoft.ML.AutoML/SweepableEstimator/Estimators/ObjectDetection.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.ML.TorchSharp;
+using Microsoft.ML.TorchSharp.AutoFormerV2;
+
+namespace Microsoft.ML.AutoML.CodeGen
+{
+ internal partial class ObjectDetectionMulti
+ {
+ public override IEstimator BuildFromOption(MLContext context, ObjectDetectionOption param)
+ {
+ var option = new ObjectDetectionTrainer.Options
+ {
+ LabelColumnName = param.LabelColumnName,
+ PredictedLabelColumnName = param.PredictedLabelColumnName,
+ BoundingBoxColumnName = param.BoundingBoxColumnName,
+ ImageColumnName = param.ImageColumnName,
+ ScoreColumnName = param.ScoreColumnName,
+ MaxEpoch = param.MaxEpoch,
+ InitLearningRate = param.InitLearningRate,
+ WeightDecay = param.WeightDecay,
+ PredictedBoundingBoxColumnName = param.PredictedBoundingBoxColumnName,
+ ScoreThreshold = param.ScoreThreshold,
+ IOUThreshold = param.IOUThreshold,
+ };
+
+ return context.MulticlassClassification.Trainers.ObjectDetection(option);
+ }
+ }
+}
diff --git a/src/Microsoft.ML.AutoML/SweepableEstimator/SweepablePipeline.cs b/src/Microsoft.ML.AutoML/SweepableEstimator/SweepablePipeline.cs
index 6f754d12b5..05dc88cd64 100644
--- a/src/Microsoft.ML.AutoML/SweepableEstimator/SweepablePipeline.cs
+++ b/src/Microsoft.ML.AutoML/SweepableEstimator/SweepablePipeline.cs
@@ -153,7 +153,7 @@ public string ToString(Parameter parameter)
if (parameter.TryGetValue(AutoMLExperiment.PipelineSearchspaceName, out var pipelineParameter))
{
var schema = pipelineParameter["_SCHEMA_"].AsType();
- var estimatorStrings = Entity.FromExpression(_currentSchema)
+ var estimatorStrings = Entity.FromExpression(schema)
.ValueEntities()
.Where(e => e is StringEntity se && se.Value != "Nil")
.Select((se) =>
diff --git a/src/Microsoft.ML.AutoML/Tuner/AutoZeroTuner.cs b/src/Microsoft.ML.AutoML/Tuner/AutoZeroTuner.cs
new file mode 100644
index 0000000000..1a2161c99c
--- /dev/null
+++ b/src/Microsoft.ML.AutoML/Tuner/AutoZeroTuner.cs
@@ -0,0 +1,142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.Json;
+using Microsoft.ML.AutoML.CodeGen;
+using Microsoft.ML.SearchSpace;
+
+namespace Microsoft.ML.AutoML.Tuner
+{
+ internal class AutoZeroTuner : ITuner
+ {
+ private readonly List _configs = new List();
+ private readonly IEnumerator _configsEnumerator;
+ private readonly Dictionary _pipelineStrings;
+ private readonly SweepablePipeline _sweepablePipeline;
+ private readonly Dictionary _configLookBook = new Dictionary();
+ private readonly string _metricName;
+
+ public AutoZeroTuner(SweepablePipeline pipeline, AggregateTrainingStopManager aggregateTrainingStopManager, IEvaluateMetricManager evaluateMetricManager, AutoMLExperiment.AutoMLExperimentSettings settings)
+ {
+ _configs = LoadConfigsFromJson();
+ _sweepablePipeline = pipeline;
+ _pipelineStrings = _sweepablePipeline.Schema.ToTerms().Select(t => new
+ {
+ schema = t.ToString(),
+ pipelineString = string.Join("=>", t.ValueEntities().Select(e => _sweepablePipeline.Estimators[e.ToString()].EstimatorType)),
+ }).ToDictionary(kv => kv.schema, kv => kv.pipelineString);
+
+ // todo
+ // filter configs on trainers
+ var trainerEstimators = _sweepablePipeline.Estimators.Where(e => e.Value.EstimatorType.IsTrainer()).Select(e => e.Value.EstimatorType.ToString()).ToList();
+ _configs = evaluateMetricManager switch
+ {
+ BinaryMetricManager => _configs.Where(c => c.Task == "binary-classification" && trainerEstimators.Contains(c.Trainer)).ToList(),
+ MultiClassMetricManager => _configs.Where(c => c.Task == "multi-classification" && trainerEstimators.Contains(c.Trainer)).ToList(),
+ RegressionMetricManager => _configs.Where(c => c.Task == "regression" && trainerEstimators.Contains(c.Trainer)).ToList(),
+ _ => throw new Exception(),
+ };
+ _metricName = evaluateMetricManager switch
+ {
+ BinaryMetricManager bm => bm.Metric.ToString(),
+ MultiClassMetricManager mm => mm.Metric.ToString(),
+ RegressionMetricManager rm => rm.Metric.ToString(),
+ _ => throw new Exception(),
+ };
+
+ if (_configs.Count == 0)
+ {
+ throw new ArgumentException($"Fail to find available configs for given trainers: {string.Join(",", trainerEstimators)}");
+ }
+
+ _configsEnumerator = _configs.GetEnumerator();
+ aggregateTrainingStopManager.AddTrainingStopManager(new MaxModelStopManager(_configs.Count, null));
+ }
+
+ private List LoadConfigsFromJson()
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ var resourceName = "Microsoft.ML.AutoML.Tuner.Portfolios.json";
+
+ using (Stream stream = assembly.GetManifestResourceStream(resourceName))
+ using (StreamReader reader = new StreamReader(stream))
+ {
+ var json = reader.ReadToEnd();
+ var res = JsonSerializer.Deserialize>(json);
+
+ return res;
+ }
+ }
+
+ public Parameter Propose(TrialSettings settings)
+ {
+ if (_configsEnumerator.MoveNext())
+ {
+ var config = _configsEnumerator.Current;
+ IEnumerable> pipelineSchemas = default;
+ if (_pipelineStrings.Any(kv => kv.Value.Contains("OneHotHashEncoding") || kv.Value.Contains("OneHotEncoding")))
+ {
+ pipelineSchemas = _pipelineStrings.Where(kv => kv.Value.Contains(config.CatalogTransformer));
+ }
+ else
+ {
+ pipelineSchemas = _pipelineStrings;
+ }
+
+ pipelineSchemas = pipelineSchemas.Where(kv => kv.Value.Contains(config.Trainer));
+ var pipelineSchema = pipelineSchemas.First().Key;
+ var pipeline = _sweepablePipeline.BuildSweepableEstimatorPipeline(pipelineSchema);
+ var parameter = pipeline.SearchSpace.SampleFromFeatureSpace(pipeline.SearchSpace.Default);
+ var trainerEstimatorName = pipeline.Estimators.Where(kv => kv.Value.EstimatorType.IsTrainer()).First().Key;
+ var label = parameter[trainerEstimatorName]["LabelColumnName"].AsType();
+ var feature = parameter[trainerEstimatorName]["FeatureColumnName"].AsType();
+ parameter[trainerEstimatorName] = config.TrainerParameter;
+ parameter[trainerEstimatorName]["LabelColumnName"] = Parameter.FromString(label);
+ parameter[trainerEstimatorName]["FeatureColumnName"] = Parameter.FromString(feature);
+ settings.Parameter[AutoMLExperiment.PipelineSearchspaceName] = parameter;
+ _configLookBook[settings.TrialId] = config;
+ return settings.Parameter;
+ }
+
+ throw new OperationCanceledException();
+ }
+
+ public void Update(TrialResult result)
+ {
+ }
+
+ class Config
+ {
+ ///
+ /// one of OneHot, HashEncoding
+ ///
+ public string CatalogTransformer { get; set; }
+
+ ///
+ /// One of Lgbm, Sdca, FastTree,,,
+ ///
+ public string Trainer { get; set; }
+
+ public Parameter TrainerParameter { get; set; }
+
+ public string Task { get; set; }
+ }
+
+ class Rows
+ {
+ public string CustomDimensionsBestPipeline { get; set; }
+
+ public string CustomDimensionsOptionsTask { get; set; }
+
+ public Parameter CustomDimensionsParameter { get; set; }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.AutoML/Tuner/CostFrugalTuner.cs b/src/Microsoft.ML.AutoML/Tuner/CostFrugalTuner.cs
index a7a7f855a8..e674949477 100644
--- a/src/Microsoft.ML.AutoML/Tuner/CostFrugalTuner.cs
+++ b/src/Microsoft.ML.AutoML/Tuner/CostFrugalTuner.cs
@@ -24,7 +24,7 @@ internal class CostFrugalTuner : ITuner
private double _bestLoss;
public CostFrugalTuner(AutoMLExperiment.AutoMLExperimentSettings settings, ITrialResultManager trialResultManager = null)
- : this(settings.SearchSpace, settings.SearchSpace.SampleFromFeatureSpace(settings.SearchSpace.Default), trialResultManager.GetAllTrialResults(), settings.Seed)
+ : this(settings.SearchSpace, settings.SearchSpace.SampleFromFeatureSpace(settings.SearchSpace.Default), trialResultManager?.GetAllTrialResults(), settings.Seed)
{
}
diff --git a/src/Microsoft.ML.AutoML/Tuner/Portfolios.json b/src/Microsoft.ML.AutoML/Tuner/Portfolios.json
new file mode 100644
index 0000000000..eb950503e9
--- /dev/null
+++ b/src/Microsoft.ML.AutoML/Tuner/Portfolios.json
@@ -0,0 +1,1813 @@
+[
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 87,
+ "MinimumExampleCountPerLeaf": 127,
+ "NumberOfTrees": 3650,
+ "MaximumBinCountPerFeature": 42,
+ "FeatureFraction": 0.80060378946837,
+ "LearningRate": 0.0035449318926440276
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 23,
+ "MinimumExampleCountPerLeaf": 71,
+ "NumberOfTrees": 207,
+ "MaximumBinCountPerFeature": 170,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.26904495597775546
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 2866,
+ "MinimumExampleCountPerLeaf": 75,
+ "NumberOfTrees": 20,
+ "MaximumBinCountPerFeature": 133,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.0034217757770038746
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 24,
+ "MinimumExampleCountPerLeaf": 54,
+ "LearningRate": 0.8552033613780062,
+ "NumberOfTrees": 45,
+ "SubsampleFraction": 8.039913011326977E-05,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 0.00020810911669089135,
+ "L2Regularization": 0.007597609681395737
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 26,
+ "NumberOfLeaves": 3463,
+ "FeatureFraction": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 476,
+ "MinimumExampleCountPerLeaf": 33,
+ "NumberOfTrees": 37,
+ "MaximumBinCountPerFeature": 24,
+ "FeatureFraction": 0.7841884722574419,
+ "LearningRate": 0.12930004437797252
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 27926,
+ "MinimumExampleCountPerLeaf": 127,
+ "NumberOfTrees": 11,
+ "MaximumBinCountPerFeature": 54,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.16078907661831227
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "SdcaMaximumEntropyMulti",
+ "TrainerParameter": {
+ "L1Regularization": 0.03125,
+ "L2Regularization": 0.03125
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LbfgsLogisticRegressionBinary",
+ "TrainerParameter": {
+ "L1Regularization": 1.544239,
+ "L2Regularization": 0.03125
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 18564,
+ "MinimumExampleCountPerLeaf": 4,
+ "NumberOfTrees": 739,
+ "MaximumBinCountPerFeature": 35,
+ "FeatureFraction": 0.7191111861806592,
+ "LearningRate": 0.027880192420138245
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4095,
+ "MinimumExampleCountPerLeaf": 28,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 4095,
+ "SubsampleFraction": 2E-10,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.1846310442449399
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 131,
+ "MinimumExampleCountPerLeaf": 21,
+ "NumberOfTrees": 125,
+ "MaximumBinCountPerFeature": 116,
+ "FeatureFraction": 0.7921180259906164,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestOva",
+ "TrainerParameter": {
+ "NumberOfTrees": 228,
+ "NumberOfLeaves": 91,
+ "FeatureFraction": 0.8420064
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 19,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 189,
+ "FeatureFraction": 0.9459816795478228,
+ "LearningRate": 0.10898711358107055
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 16,
+ "MinimumExampleCountPerLeaf": 70,
+ "LearningRate": 0.2138828822356055,
+ "NumberOfTrees": 25,
+ "SubsampleFraction": 0.05380197471709659,
+ "MaximumBinCountPerFeature": 426,
+ "FeatureFraction": 0.6981585251308082,
+ "L1Regularization": 1.8832743738626092E-08,
+ "L2Regularization": 1.1634842271570635E-08
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 7,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.1607069068239919,
+ "NumberOfTrees": 16,
+ "SubsampleFraction": 0.04856069948639365,
+ "MaximumBinCountPerFeature": 296,
+ "FeatureFraction": 0.9470492408617684,
+ "L1Regularization": 6.929644224890629E-08,
+ "L2Regularization": 0.0011264388186604647
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestRegression",
+ "TrainerParameter": {
+ "NumberOfTrees": 32767,
+ "NumberOfLeaves": 4,
+ "FeatureFraction": 1
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 5,
+ "MinimumExampleCountPerLeaf": 8,
+ "NumberOfTrees": 826,
+ "MaximumBinCountPerFeature": 312,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.24415311049241878
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestRegression",
+ "TrainerParameter": {
+ "NumberOfTrees": 4,
+ "NumberOfLeaves": 4,
+ "FeatureFraction": 0.39762402
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 30,
+ "MinimumExampleCountPerLeaf": 2,
+ "NumberOfTrees": 7,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.43132548851524677,
+ "LearningRate": 1.7715889795284222E-05
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 24,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 5,
+ "SubsampleFraction": 0.8888612589023396,
+ "MaximumBinCountPerFeature": 174,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2.853856087345465E-10,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 36,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.6488080623332225,
+ "NumberOfTrees": 5,
+ "SubsampleFraction": 0.05377902547670367,
+ "MaximumBinCountPerFeature": 988,
+ "FeatureFraction": 0.8318215522085479,
+ "L1Regularization": 1.0693136058923942E-07,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 340,
+ "MinimumExampleCountPerLeaf": 67,
+ "LearningRate": 0.01760375575034171,
+ "NumberOfTrees": 1408,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 5.602593285766215E-10,
+ "L2Regularization": 0.0003496680724914571
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 29,
+ "MinimumExampleCountPerLeaf": 2,
+ "NumberOfTrees": 18,
+ "MaximumBinCountPerFeature": 281,
+ "FeatureFraction": 0.9844369303190436,
+ "LearningRate": 0.2689422276981913
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 1,
+ "NumberOfTrees": 4,
+ "SubsampleFraction": 1,
+ "MaximumBinCountPerFeature": 254,
+ "FeatureFraction": 1,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 43,
+ "MinimumExampleCountPerLeaf": 22,
+ "NumberOfTrees": 35,
+ "MaximumBinCountPerFeature": 239,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.027071590576339927
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 10,
+ "MinimumExampleCountPerLeaf": 12,
+ "NumberOfTrees": 5,
+ "MaximumBinCountPerFeature": 424,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.4533750801262437
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 5,
+ "MinimumExampleCountPerLeaf": 38,
+ "NumberOfTrees": 241,
+ "MaximumBinCountPerFeature": 520,
+ "FeatureFraction": 0.898327302972124,
+ "LearningRate": 0.1348356340063525
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 33,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.7977660917637557,
+ "NumberOfTrees": 131,
+ "SubsampleFraction": 0.9151039831504174,
+ "MaximumBinCountPerFeature": 69,
+ "FeatureFraction": 0.9409080285811853,
+ "L1Regularization": 3.1400695396628333E-07,
+ "L2Regularization": 0.4734149986346901
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 308,
+ "MinimumExampleCountPerLeaf": 2,
+ "NumberOfTrees": 35,
+ "MaximumBinCountPerFeature": 428,
+ "FeatureFraction": 0.6533323657647356,
+ "LearningRate": 3.514880612661438E-06
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 5045,
+ "MinimumExampleCountPerLeaf": 76,
+ "NumberOfTrees": 7470,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.005777249800844191
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 1216,
+ "MinimumExampleCountPerLeaf": 5,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 154,
+ "FeatureFraction": 0.7307294653075181,
+ "LearningRate": 0.0021580240771114654
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 19,
+ "NumberOfLeaves": 450,
+ "FeatureFraction": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 55,
+ "MinimumExampleCountPerLeaf": 53,
+ "LearningRate": 0.010175513636238035,
+ "NumberOfTrees": 938,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 774,
+ "FeatureFraction": 0.6708934267697179,
+ "L1Regularization": 0.020668403746115033,
+ "L2Regularization": 6.280685939563107E-05
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4095,
+ "MinimumExampleCountPerLeaf": 39,
+ "LearningRate": 0.1609298166363187,
+ "NumberOfTrees": 4095,
+ "SubsampleFraction": 2.0128704530267477E-05,
+ "MaximumBinCountPerFeature": 662,
+ "FeatureFraction": 0.7231434042052313,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.6157363701731406
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 50,
+ "MinimumExampleCountPerLeaf": 40,
+ "LearningRate": 0.042508276403038864,
+ "NumberOfTrees": 310,
+ "SubsampleFraction": 1.7357794917570536E-05,
+ "MaximumBinCountPerFeature": 533,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 4.383333916866736E-09,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 30,
+ "MinimumExampleCountPerLeaf": 17,
+ "NumberOfTrees": 5,
+ "MaximumBinCountPerFeature": 74,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.06467367001651869
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 5,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.2064267413802997,
+ "NumberOfTrees": 4,
+ "SubsampleFraction": 0.12000229582539053,
+ "MaximumBinCountPerFeature": 403,
+ "FeatureFraction": 0.9492463845809583,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.025226098020360813
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LbfgsMaximumEntropyMulti",
+ "TrainerParameter": {
+ "L1Regularization": 27.095572,
+ "L2Regularization": 52.326847
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 458,
+ "MinimumExampleCountPerLeaf": 64,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 1699,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 760,
+ "FeatureFraction": 0.9135681598986697,
+ "L1Regularization": 1.0596377616756006E-08,
+ "L2Regularization": 0.00019321038657956884
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 127,
+ "NumberOfTrees": 4831,
+ "MaximumBinCountPerFeature": 128,
+ "FeatureFraction": 0.7501639824805302,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 23,
+ "MinimumExampleCountPerLeaf": 94,
+ "LearningRate": 0.7540379201541587,
+ "NumberOfTrees": 380,
+ "SubsampleFraction": 0.04302859272138893,
+ "MaximumBinCountPerFeature": 952,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.6273362209972279
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 5,
+ "MinimumExampleCountPerLeaf": 13,
+ "NumberOfTrees": 10,
+ "MaximumBinCountPerFeature": 665,
+ "FeatureFraction": 0.9592379556018786,
+ "LearningRate": 0.0005364346218611147
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 42,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 6,
+ "SubsampleFraction": 0.19764945728930977,
+ "MaximumBinCountPerFeature": 822,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 1.9309089659582934E-09,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 873,
+ "MinimumExampleCountPerLeaf": 28,
+ "LearningRate": 0.002677592476613682,
+ "NumberOfTrees": 4095,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.7773927911851057,
+ "L1Regularization": 0.9999997766729865,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 1077,
+ "MinimumExampleCountPerLeaf": 8,
+ "NumberOfTrees": 82,
+ "MaximumBinCountPerFeature": 96,
+ "FeatureFraction": 0.9397924499604204,
+ "LearningRate": 0.5048038973443215
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 22,
+ "LearningRate": 0.014535555300654746,
+ "NumberOfTrees": 4095,
+ "SubsampleFraction": 0.0026133099012740865,
+ "MaximumBinCountPerFeature": 8,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.5746178276527408
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 20,
+ "MinimumExampleCountPerLeaf": 56,
+ "LearningRate": 0.031002833901693286,
+ "NumberOfTrees": 122,
+ "SubsampleFraction": 0.0177878235765325,
+ "MaximumBinCountPerFeature": 18,
+ "FeatureFraction": 0.7166882312740435,
+ "L1Regularization": 8.406276278405754E-06,
+ "L2Regularization": 0.9605427965053129
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastForestRegression",
+ "TrainerParameter": {
+ "NumberOfTrees": 61,
+ "NumberOfLeaves": 4,
+ "FeatureFraction": 0.8260069
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 36,
+ "MinimumExampleCountPerLeaf": 48,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 95,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.4198472012231081
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 218,
+ "MinimumExampleCountPerLeaf": 41,
+ "NumberOfTrees": 10,
+ "MaximumBinCountPerFeature": 291,
+ "FeatureFraction": 0.9657180775253634,
+ "LearningRate": 0.46510044550263674
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 387,
+ "MinimumExampleCountPerLeaf": 4,
+ "NumberOfTrees": 47,
+ "MaximumBinCountPerFeature": 349,
+ "FeatureFraction": 0.3469633042126186,
+ "LearningRate": 0.02810102564661168
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 131,
+ "MinimumExampleCountPerLeaf": 6,
+ "NumberOfTrees": 1671,
+ "MaximumBinCountPerFeature": 126,
+ "FeatureFraction": 0.4554971500601669,
+ "LearningRate": 0.01642767303115543
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 7,
+ "MinimumExampleCountPerLeaf": 26,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 4,
+ "SubsampleFraction": 0.046368763450735075,
+ "MaximumBinCountPerFeature": 236,
+ "FeatureFraction": 0.9997821238763959,
+ "L1Regularization": 8.702725153774787E-09,
+ "L2Regularization": 0.9082544287969905
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 28,
+ "LearningRate": 0.6545189219684858,
+ "NumberOfTrees": 25,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 364,
+ "FeatureFraction": 0.7448176714826046,
+ "L1Regularization": 1.2344624350669486E-06,
+ "L2Regularization": 0.13957996259479463
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 43,
+ "LearningRate": 0.8164648957299602,
+ "NumberOfTrees": 16,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LbfgsPoissonRegressionRegression",
+ "TrainerParameter": {
+ "L1Regularization": 2.568412,
+ "L2Regularization": 0.7046674
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 84,
+ "MinimumExampleCountPerLeaf": 75,
+ "LearningRate": 0.1556224741768498,
+ "NumberOfTrees": 301,
+ "SubsampleFraction": 0.20684593151088038,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.012166921416926199
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 5,
+ "NumberOfLeaves": 19,
+ "FeatureFraction": 0.86853236
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 19,
+ "MinimumExampleCountPerLeaf": 13,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 40,
+ "FeatureFraction": 0.9742554161601645,
+ "LearningRate": 2.6412180193809835E-05
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 574,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 80,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 387,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 0.9999997766729865,
+ "L2Regularization": 0.002827899752167108
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 25,
+ "MinimumExampleCountPerLeaf": 29,
+ "LearningRate": 0.9797647605786263,
+ "NumberOfTrees": 4,
+ "SubsampleFraction": 0.08531561133872057,
+ "MaximumBinCountPerFeature": 308,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 1154,
+ "MinimumExampleCountPerLeaf": 41,
+ "NumberOfTrees": 148,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.8850375370183471,
+ "LearningRate": 0.26839318987943295
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 20,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 254,
+ "FeatureFraction": 1,
+ "LearningRate": 0.09999999999999998
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 6,
+ "MinimumExampleCountPerLeaf": 35,
+ "LearningRate": 0.6225066318847601,
+ "NumberOfTrees": 4,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 491,
+ "FeatureFraction": 0.9926422107076923,
+ "L1Regularization": 7.977543420928789E-10,
+ "L2Regularization": 0.8785889785517109
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LbfgsPoissonRegressionRegression",
+ "TrainerParameter": {
+ "L1Regularization": 0.046509832,
+ "L2Regularization": 2.1919274
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LbfgsLogisticRegressionBinary",
+ "TrainerParameter": {
+ "L1Regularization": 0.06107197,
+ "L2Regularization": 0.46383783
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 15,
+ "MinimumExampleCountPerLeaf": 20,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 346,
+ "FeatureFraction": 0.8777534708332781,
+ "LearningRate": 0.3707156287378302
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 65,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 10,
+ "SubsampleFraction": 0.4475319104685073,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.7065048882466722,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.0001848650265484988
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 37,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 248,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 74,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 638,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 8,
+ "FeatureFraction": 0.9141725542503555,
+ "L1Regularization": 1.654030584712007E-05,
+ "L2Regularization": 1.4310122642197601E-08
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 53,
+ "MinimumExampleCountPerLeaf": 13,
+ "NumberOfTrees": 18,
+ "MaximumBinCountPerFeature": 398,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 20,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 254,
+ "FeatureFraction": 1,
+ "LearningRate": 0.09999999999999998
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 21,
+ "MinimumExampleCountPerLeaf": 10,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 260,
+ "FeatureFraction": 0.8618229630150351,
+ "LearningRate": 0.8286187833297257
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 16,
+ "MinimumExampleCountPerLeaf": 25,
+ "LearningRate": 0.024309951696254765,
+ "NumberOfTrees": 1626,
+ "SubsampleFraction": 0.05717830298385893,
+ "MaximumBinCountPerFeature": 63,
+ "FeatureFraction": 0.3445837105939972,
+ "L1Regularization": 6.172893634070921E-10,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 4,
+ "NumberOfTrees": 66,
+ "MaximumBinCountPerFeature": 52,
+ "FeatureFraction": 0.5305488725514618,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LbfgsMaximumEntropyMulti",
+ "TrainerParameter": {
+ "L1Regularization": 0.042985212,
+ "L2Regularization": 0.03125
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "SdcaRegression",
+ "TrainerParameter": {
+ "L1Regularization": 0.03125,
+ "L2Regularization": 0.03125
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 257,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 1427,
+ "SubsampleFraction": 3.105791251325442E-05,
+ "MaximumBinCountPerFeature": 39,
+ "FeatureFraction": 0.8752756480266736,
+ "L1Regularization": 1.1826348401622547E-09,
+ "L2Regularization": 0.00025544319795926335
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 288,
+ "NumberOfLeaves": 86,
+ "FeatureFraction": 0.96065336
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 48,
+ "MinimumExampleCountPerLeaf": 21,
+ "NumberOfTrees": 65,
+ "MaximumBinCountPerFeature": 310,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.1235737872865588
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 22,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 253,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.06033267458523351
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 5957,
+ "MinimumExampleCountPerLeaf": 9,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.48039801985216035,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 5,
+ "MinimumExampleCountPerLeaf": 23,
+ "LearningRate": 0.3589771843727137,
+ "NumberOfTrees": 28,
+ "SubsampleFraction": 0.00020376984896845667,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 7.628194205579231E-10,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 3,
+ "NumberOfTrees": 140,
+ "MaximumBinCountPerFeature": 482,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.06195522011262124
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastForestRegression",
+ "TrainerParameter": {
+ "NumberOfTrees": 15,
+ "NumberOfLeaves": 10,
+ "FeatureFraction": 0.87529165
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 20,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 254,
+ "FeatureFraction": 1,
+ "LearningRate": 0.09999999999999998
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 25,
+ "LearningRate": 0.2946617046427907,
+ "NumberOfTrees": 134,
+ "SubsampleFraction": 0.0013837596822020258,
+ "MaximumBinCountPerFeature": 8,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 24,
+ "NumberOfLeaves": 129,
+ "FeatureFraction": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 4095,
+ "SubsampleFraction": 7.157358089822813E-05,
+ "MaximumBinCountPerFeature": 851,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 0.9999997766729865,
+ "L2Regularization": 2E-10
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LbfgsLogisticRegressionBinary",
+ "TrainerParameter": {
+ "L1Regularization": 0.67462516,
+ "L2Regularization": 45.032707
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 13,
+ "MinimumExampleCountPerLeaf": 29,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 253,
+ "FeatureFraction": 0.8885670584473564,
+ "LearningRate": 0.7407276161384259
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestOva",
+ "TrainerParameter": {
+ "NumberOfTrees": 28,
+ "NumberOfLeaves": 4,
+ "FeatureFraction": 0.95436877
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 12,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 4,
+ "SubsampleFraction": 0.9919758651099816,
+ "MaximumBinCountPerFeature": 314,
+ "FeatureFraction": 0.8744235226749597,
+ "L1Regularization": 7.144305824276878E-10,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 4,
+ "NumberOfLeaves": 20,
+ "FeatureFraction": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 14,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.015736119195590082,
+ "NumberOfTrees": 268,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 238,
+ "FeatureFraction": 0.7549282403965342,
+ "L1Regularization": 3.985420694184181E-10,
+ "L2Regularization": 0.049886471931384915
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 92,
+ "MinimumExampleCountPerLeaf": 159,
+ "LearningRate": 0.18792169233546632,
+ "NumberOfTrees": 4095,
+ "SubsampleFraction": 0.0008672137319528135,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.9838197151701678,
+ "L1Regularization": 4.995549262995053E-10,
+ "L2Regularization": 3.3257594062003576E-07
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestOva",
+ "TrainerParameter": {
+ "NumberOfTrees": 4,
+ "NumberOfLeaves": 10,
+ "FeatureFraction": 0.9179285
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 6,
+ "MinimumExampleCountPerLeaf": 216,
+ "LearningRate": 0.4574367802827828,
+ "NumberOfTrees": 3068,
+ "SubsampleFraction": 0.0018753320593756912,
+ "MaximumBinCountPerFeature": 12,
+ "FeatureFraction": 0.6161102879529117,
+ "L1Regularization": 1.2039087596828993E-07,
+ "L2Regularization": 0.05645661408705994
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 20,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 254,
+ "FeatureFraction": 1,
+ "LearningRate": 0.09999999999999998
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 11030,
+ "NumberOfLeaves": 36,
+ "FeatureFraction": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 63,
+ "MinimumExampleCountPerLeaf": 20,
+ "NumberOfTrees": 17,
+ "MaximumBinCountPerFeature": 121,
+ "FeatureFraction": 0.99999999,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LbfgsLogisticRegressionBinary",
+ "TrainerParameter": {
+ "L1Regularization": 0.03125,
+ "L2Regularization": 0.06263834
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastForestOva",
+ "TrainerParameter": {
+ "NumberOfTrees": 4,
+ "NumberOfLeaves": 4,
+ "FeatureFraction": 1
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 32767,
+ "NumberOfLeaves": 154,
+ "FeatureFraction": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 20,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 254,
+ "FeatureFraction": 1,
+ "LearningRate": 0.09999999999999998
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 7,
+ "MinimumExampleCountPerLeaf": 13,
+ "NumberOfTrees": 32767,
+ "MaximumBinCountPerFeature": 32,
+ "FeatureFraction": 0.7022629028011045,
+ "LearningRate": 0.02665500111882839
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 6,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 24,
+ "SubsampleFraction": 0.02398658692944612,
+ "MaximumBinCountPerFeature": 402,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 2E-10,
+ "L2Regularization": 0.004701333090073423
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 58,
+ "MinimumExampleCountPerLeaf": 335,
+ "LearningRate": 0.07854028080287102,
+ "NumberOfTrees": 2035,
+ "SubsampleFraction": 0.0006497953957992051,
+ "MaximumBinCountPerFeature": 927,
+ "FeatureFraction": 0.18362996185211386,
+ "L1Regularization": 0.001030219250827897,
+ "L2Regularization": 0.026552293085077904
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LbfgsMaximumEntropyMulti",
+ "TrainerParameter": {
+ "L1Regularization": 0.33348912,
+ "L2Regularization": 0.03125
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LbfgsLogisticRegressionBinary",
+ "TrainerParameter": {
+ "L1Regularization": 0.15070061,
+ "L2Regularization": 0.03125
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LbfgsLogisticRegressionOva",
+ "TrainerParameter": {
+ "L1Regularization": 0.03125,
+ "L2Regularization": 0.14498326
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 119,
+ "MinimumExampleCountPerLeaf": 58,
+ "LearningRate": 0.06825226637267366,
+ "NumberOfTrees": 4095,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.6267797966465569,
+ "L1Regularization": 1.1227489355801138E-07,
+ "L2Regularization": 0.06600775121703452
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastForestRegression",
+ "TrainerParameter": {
+ "NumberOfTrees": 4,
+ "NumberOfLeaves": 4,
+ "FeatureFraction": 1
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 32767,
+ "NumberOfLeaves": 305,
+ "FeatureFraction": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 10,
+ "MinimumExampleCountPerLeaf": 8,
+ "NumberOfTrees": 14,
+ "MaximumBinCountPerFeature": 245,
+ "FeatureFraction": 0.986750888433752,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 355,
+ "MinimumExampleCountPerLeaf": 34,
+ "LearningRate": 0.1955402076262078,
+ "NumberOfTrees": 492,
+ "SubsampleFraction": 0.00012808557363770638,
+ "MaximumBinCountPerFeature": 180,
+ "FeatureFraction": 0.6228631994597027,
+ "L1Regularization": 2.3467870634880694E-10,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 28755,
+ "MinimumExampleCountPerLeaf": 2,
+ "NumberOfTrees": 23,
+ "MaximumBinCountPerFeature": 8,
+ "FeatureFraction": 0.7896620118098,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 256,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.2692333105179609,
+ "NumberOfTrees": 4,
+ "SubsampleFraction": 4.578140637986982E-06,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 1.8616775493439103E-06,
+ "L2Regularization": 0.6528449193068331
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "FastTreeRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 11,
+ "NumberOfTrees": 660,
+ "MaximumBinCountPerFeature": 775,
+ "FeatureFraction": 0.9623903441243219,
+ "LearningRate": 0.9999997766729865
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestBinary",
+ "TrainerParameter": {
+ "NumberOfTrees": 971,
+ "NumberOfLeaves": 1039,
+ "FeatureFraction": 1
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 20,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 254,
+ "FeatureFraction": 1,
+ "LearningRate": 0.09999999999999998
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LbfgsLogisticRegressionBinary",
+ "TrainerParameter": {
+ "L1Regularization": 0.046156637,
+ "L2Regularization": 0.29246366
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastTreeOva",
+ "TrainerParameter": {
+ "NumberOfLeaves": 2139,
+ "MinimumExampleCountPerLeaf": 33,
+ "NumberOfTrees": 4,
+ "MaximumBinCountPerFeature": 140,
+ "FeatureFraction": 0.7044397255286275,
+ "LearningRate": 0.00015715008951282033
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "FastForestOva",
+ "TrainerParameter": {
+ "NumberOfTrees": 4,
+ "NumberOfLeaves": 4,
+ "FeatureFraction": 0.5793991
+
+ },
+ "Task": "multi-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmRegression",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4095,
+ "MinimumExampleCountPerLeaf": 20,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 7,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 0.012472823102106186,
+ "L2Regularization": 0.01755275260937544
+
+ },
+ "Task": "regression"
+ },
+ {
+ "CatalogTransformer": "OneHotEncoding",
+ "Trainer": "LightGbmBinary",
+ "TrainerParameter": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 32,
+ "LearningRate": 0.9999997766729865,
+ "NumberOfTrees": 44,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 1023,
+ "FeatureFraction": 0.99999999,
+ "L1Regularization": 8.713858660867841E-07,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "binary-classification"
+ },
+ {
+ "CatalogTransformer": "OneHotHashEncoding",
+ "Trainer": "LightGbmMulti",
+ "TrainerParameter": {
+ "NumberOfLeaves": 75,
+ "MinimumExampleCountPerLeaf": 277,
+ "LearningRate": 0.36344733837824383,
+ "NumberOfTrees": 75,
+ "SubsampleFraction": 0.9999997766729865,
+ "MaximumBinCountPerFeature": 178,
+ "FeatureFraction": 0.8402457426124342,
+ "L1Regularization": 5.328274305679465E-08,
+ "L2Regularization": 0.9999997766729865
+
+ },
+ "Task": "multi-classification"
+ }
+]
diff --git a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj
index fa0da1b094..58b6f74f1b 100644
--- a/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj
+++ b/src/Microsoft.ML.CodeGenerator/Microsoft.ML.CodeGenerator.csproj
@@ -5,7 +5,10 @@
netstandard2.0
Microsoft.ML.CodeGenerator
ML.NET Code Generator
- $(NoWarn);MSB3270
+ $(NoWarn)
+
+
+ None
diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs
index 7ae7e11549..8acec761a7 100644
--- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs
+++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.cs
@@ -27,7 +27,7 @@ internal partial class PredictProject : PredictProjectBase
public virtual string TransformText()
{
this.Write("\r\n\r\n \r\n Exe\r\n netcoreapp3.1\r\n \r\n net6.0\r\n \r\n \r\n \r\n");
diff --git a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.tt b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.tt
index 65a4eb8a38..a23b575ccf 100644
--- a/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.tt
+++ b/src/Microsoft.ML.CodeGenerator/Templates/Console/PredictProject.tt
@@ -8,7 +8,7 @@
Exe
- netcoreapp3.1
+ net6.0
diff --git a/src/Microsoft.ML.Console/Microsoft.ML.Console.csproj b/src/Microsoft.ML.Console/Microsoft.ML.Console.csproj
index f4331b79fb..78a1e768af 100644
--- a/src/Microsoft.ML.Console/Microsoft.ML.Console.csproj
+++ b/src/Microsoft.ML.Console/Microsoft.ML.Console.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.1
+ net6.0
Exe
MML
Microsoft.ML.Tools.Console.Console
@@ -27,6 +27,7 @@
+
diff --git a/src/Microsoft.ML.Core/Environment/HostEnvironmentBase.cs b/src/Microsoft.ML.Core/Environment/HostEnvironmentBase.cs
index e35c13d206..31c0c003c5 100644
--- a/src/Microsoft.ML.Core/Environment/HostEnvironmentBase.cs
+++ b/src/Microsoft.ML.Core/Environment/HostEnvironmentBase.cs
@@ -392,6 +392,9 @@ protected HostEnvironmentBase(HostEnvironmentBase source, Random rand, boo
// This fork shares some stuff with the master.
Master = source;
+ GpuDeviceId = Master?.GpuDeviceId;
+ FallbackToCpu = Master?.FallbackToCpu ?? true;
+ Seed = Master?.Seed;
Root = source.Root;
ListenerDict = source.ListenerDict;
ProgressTracker = source.ProgressTracker;
diff --git a/src/Microsoft.ML.Core/Properties/AssemblyInfo.cs b/src/Microsoft.ML.Core/Properties/AssemblyInfo.cs
index bbfd94eee5..efe76ad5fb 100644
--- a/src/Microsoft.ML.Core/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.ML.Core/Properties/AssemblyInfo.cs
@@ -25,8 +25,10 @@
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Ensemble" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.FastTree" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Mkl.Components" + PublicKey.Value)]
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.OneDal" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.KMeansClustering" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.LightGbm" + PublicKey.Value)]
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.XGBoost" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.OnnxConverter" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.OnnxTransformer" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Parquet" + PublicKey.Value)]
diff --git a/src/Microsoft.ML.Core/Utilities/ResourceManagerUtils.cs b/src/Microsoft.ML.Core/Utilities/ResourceManagerUtils.cs
index bc06467159..f50ecc9174 100644
--- a/src/Microsoft.ML.Core/Utilities/ResourceManagerUtils.cs
+++ b/src/Microsoft.ML.Core/Utilities/ResourceManagerUtils.cs
@@ -3,9 +3,11 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@@ -152,19 +154,10 @@ private async Task DownloadFromUrlWithRetryAsync(IHostEnvironment env, I
/// Returns the error message if an error occurred, null if download was successful.
private async Task DownloadFromUrlAsync(IHostEnvironment env, IChannel ch, string url, string fileName, int timeout, string filePath)
{
- using (var webClient = new WebClient())
+ using (var client = new HttpClient())
using (var downloadCancel = new CancellationTokenSource())
{
- bool deleteNeeded = false;
- EventHandler disposed =
- (object sender, EventArgs e) =>
- {
- if (File.Exists(filePath) && deleteNeeded)
- TryDelete(ch, filePath);
- };
-
- webClient.Disposed += disposed;
- var t = Task.Run(() => DownloadResource(env, ch, webClient, new Uri(url), filePath, fileName, downloadCancel.Token));
+ var t = Task.Run(() => DownloadResource(env, ch, client, new Uri(url), filePath, fileName, downloadCancel.Token));
UpdateTimeout(ref timeout);
var timeoutTask = Task.Delay(timeout).ContinueWith(task => default(Exception), TaskScheduler.Default);
@@ -173,7 +166,8 @@ private async Task DownloadFromUrlAsync(IHostEnvironment env, IChannel c
if (completedTask != t || completedTask.CompletedResult() != null)
{
downloadCancel.Cancel();
- deleteNeeded = true;
+ if (File.Exists(filePath))
+ TryDelete(ch, filePath);
return (await t).Message;
}
return null;
@@ -252,7 +246,7 @@ private static string GetFilePath(IChannel ch, string fileName, string dir, out
return filePath;
}
- private Exception DownloadResource(IHostEnvironment env, IChannel ch, WebClient webClient, Uri uri, string path, string fileName, CancellationToken ct)
+ private async Task DownloadResource(IHostEnvironment env, IChannel ch, HttpClient httpClient, Uri uri, string path, string fileName, CancellationToken ct)
{
if (File.Exists(path))
return null;
@@ -271,41 +265,26 @@ private Exception DownloadResource(IHostEnvironment env, IChannel ch, WebClient
{
int blockSize = 4096;
- using (var s = webClient.OpenRead(uri))
+ var response = await httpClient.GetAsync(uri, ct).ConfigureAwait(false);
using (var fh = env.CreateOutputFile(tempPath))
using (var ws = fh.CreateWriteStream())
{
- var headers = webClient.ResponseHeaders.GetValues("Content-Length");
+ response.EnsureSuccessStatusCode();
+ IEnumerable headers;
+ var hasHeader = response.Headers.TryGetValues("content-length", out headers);
if (uri.Host == "aka.ms" && IsRedirectToDefaultPage(uri.AbsoluteUri))
throw new NotSupportedException($"The provided url ({uri}) redirects to the default url ({DefaultUrl})");
- if (Utils.Size(headers) == 0 || !long.TryParse(headers[0], out var size))
+ if (!hasHeader || !long.TryParse(headers.First(), out var size))
size = 10000000;
- long printFreq = (long)(size / 10.0);
- var buffer = new byte[blockSize];
- long total = 0;
+ var stream = await response.EnsureSuccessStatusCode().Content.ReadAsStreamAsync().ConfigureAwait(false);
+
+ await stream.CopyToAsync(ws, blockSize, ct);
- // REVIEW: use a progress channel instead.
- while (true)
+ if (ct.IsCancellationRequested)
{
- var task = s.ReadAsync(buffer, 0, blockSize, ct);
- task.Wait();
- int count = task.Result;
-
- if (count <= 0)
- {
- break;
- }
-
- ws.Write(buffer, 0, count);
- total += count;
- if ((total - (total / printFreq) * printFreq) <= blockSize)
- ch.Info($"{fileName}: Downloaded {total} bytes out of {size}");
- if (ct.IsCancellationRequested)
- {
- ch.Error($"{fileName}: Download timed out");
- return ch.Except("Download timed out");
- }
+ ch.Error($"{fileName}: Download timed out");
+ return ch.Except("Download timed out");
}
}
File.Move(tempPath, path);
@@ -314,7 +293,7 @@ private Exception DownloadResource(IHostEnvironment env, IChannel ch, WebClient
}
catch (WebException e)
{
- ch.Error($"{fileName}: Could not download. WebClient returned the following error: {e.Message}");
+ ch.Error($"{fileName}: Could not download. HttpClient returned the following error: {e.Message}");
return e;
}
finally
diff --git a/src/Microsoft.ML.CpuMath/Microsoft.ML.CpuMath.csproj b/src/Microsoft.ML.CpuMath/Microsoft.ML.CpuMath.csproj
index a0c141a8da..5fa62087e0 100644
--- a/src/Microsoft.ML.CpuMath/Microsoft.ML.CpuMath.csproj
+++ b/src/Microsoft.ML.CpuMath/Microsoft.ML.CpuMath.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netcoreapp3.1
+ netstandard2.0;net6.0
Microsoft.ML.CpuMath
Microsoft.ML.CpuMath contains optimized math routines for ML.NET.
true
@@ -15,7 +15,7 @@
-
+
@@ -31,7 +31,7 @@
-
+
diff --git a/src/Microsoft.ML.CpuMath/Properties/AssemblyInfo.cs b/src/Microsoft.ML.CpuMath/Properties/AssemblyInfo.cs
index 94c27a7fbf..2e961d1f3c 100644
--- a/src/Microsoft.ML.CpuMath/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.ML.CpuMath/Properties/AssemblyInfo.cs
@@ -9,6 +9,7 @@
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Internal.CpuMath" + InternalPublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Runtime.Internal.MklMath" + InternalPublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "LibSvmWrapper" + InternalPublicKey.Value)]
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.OneDal" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Runtime.NeuralNetworks" + InternalPublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.RServerScoring.NeuralNetworks" + InternalPublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.AutoML" + PublicKey.Value)]
diff --git a/src/Microsoft.ML.Data/DataLoadSave/Database/DatabaseLoaderCursor.cs b/src/Microsoft.ML.Data/DataLoadSave/Database/DatabaseLoaderCursor.cs
index ed092be157..c074d0d1f3 100644
--- a/src/Microsoft.ML.Data/DataLoadSave/Database/DatabaseLoaderCursor.cs
+++ b/src/Microsoft.ML.Data/DataLoadSave/Database/DatabaseLoaderCursor.cs
@@ -307,7 +307,7 @@ private ValueGetter CreateByteGetterDelegate(ColInfo colInfo)
private ValueGetter CreateDateTimeGetterDelegate(ColInfo colInfo)
{
int columnIndex = GetColumnIndex(colInfo);
- return (ref DateTime value) => value = DataReader.GetDateTime(columnIndex);
+ return (ref DateTime value) => value = DataReader.IsDBNull(columnIndex) ? default : DataReader.GetDateTime(columnIndex);
}
private ValueGetter CreateDoubleGetterDelegate(ColInfo colInfo)
diff --git a/src/Microsoft.ML.Data/MLContext.cs b/src/Microsoft.ML.Data/MLContext.cs
index 89c07c5715..f0e1986c5e 100644
--- a/src/Microsoft.ML.Data/MLContext.cs
+++ b/src/Microsoft.ML.Data/MLContext.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.Reflection;
using Microsoft.ML.Data;
using Microsoft.ML.Runtime;
@@ -171,5 +172,24 @@ private void ProcessMessage(IMessageSource source, ChannelMessage message)
[BestFriend]
internal void CancelExecution() => ((ICancelable)_env).CancelExecution();
+
+ [BestFriend]
+ internal static readonly bool OneDalDispatchingEnabled = InitializeOneDalDispatchingEnabled();
+
+ private static bool InitializeOneDalDispatchingEnabled()
+ {
+ try
+ {
+ var asm = Assembly.Load("Microsoft.ML.OneDal");
+ var type = asm.GetType("Microsoft.ML.OneDal.OneDalUtils");
+ var method = type.GetMethod("IsDispatchingEnabled", BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+ var result = method.Invoke(null, null);
+ return (bool)result;
+ }
+ catch
+ {
+ return false;
+ }
+ }
}
}
diff --git a/src/Microsoft.ML.Data/Properties/AssemblyInfo.cs b/src/Microsoft.ML.Data/Properties/AssemblyInfo.cs
index d8e6944256..1bebd86732 100644
--- a/src/Microsoft.ML.Data/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.ML.Data/Properties/AssemblyInfo.cs
@@ -24,8 +24,10 @@
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Ensemble" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.FastTree" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Mkl.Components" + PublicKey.Value)]
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.OneDal" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.KMeansClustering" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.LightGbm" + PublicKey.Value)]
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.XGBoost" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.OnnxConverter" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.OnnxTransformer" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Parquet" + PublicKey.Value)]
diff --git a/src/Microsoft.ML.Data/Scorers/GenericScorer.cs b/src/Microsoft.ML.Data/Scorers/GenericScorer.cs
index 9d818fbeb7..5df50dd777 100644
--- a/src/Microsoft.ML.Data/Scorers/GenericScorer.cs
+++ b/src/Microsoft.ML.Data/Scorers/GenericScorer.cs
@@ -250,8 +250,12 @@ protected override bool WantParallelCursors(Func predicate)
{
Host.AssertValue(predicate, "predicate");
+#if false
// Prefer parallel cursors iff some of our columns are active, otherwise, don't care.
return _bindings.AnyNewColumnsActive(predicate);
+#else
+ return false;
+#endif
}
private protected override IDataTransform ApplyToDataCore(IHostEnvironment env, IDataView newSource)
diff --git a/src/Microsoft.ML.Data/Training/TrainerEstimatorBase.cs b/src/Microsoft.ML.Data/Training/TrainerEstimatorBase.cs
index 4c7cbc324e..f77de7ac09 100644
--- a/src/Microsoft.ML.Data/Training/TrainerEstimatorBase.cs
+++ b/src/Microsoft.ML.Data/Training/TrainerEstimatorBase.cs
@@ -155,6 +155,7 @@ private protected TTransformer TrainTransformer(IDataView trainSet,
}
var pred = TrainModelCore(new TrainContext(trainRoleMapped, validRoleMapped, null, initPredictor));
+ System.Console.WriteLine("pred is null: " + (pred == null));
return MakeTransformer(pred, trainSet.Schema);
}
diff --git a/src/Microsoft.ML.DnnAnalyzer/Microsoft.ML.DnnAnalyzer/Microsoft.ML.DnnAnalyzer.csproj b/src/Microsoft.ML.DnnAnalyzer/Microsoft.ML.DnnAnalyzer/Microsoft.ML.DnnAnalyzer.csproj
index c3f7e6a57a..252cce0312 100644
--- a/src/Microsoft.ML.DnnAnalyzer/Microsoft.ML.DnnAnalyzer/Microsoft.ML.DnnAnalyzer.csproj
+++ b/src/Microsoft.ML.DnnAnalyzer/Microsoft.ML.DnnAnalyzer/Microsoft.ML.DnnAnalyzer.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net6.0
DnnAnalyzer
diff --git a/src/Microsoft.ML.FastTree/Microsoft.ML.FastTree.csproj b/src/Microsoft.ML.FastTree/Microsoft.ML.FastTree.csproj
index 52cc2f11a5..acbef6b851 100644
--- a/src/Microsoft.ML.FastTree/Microsoft.ML.FastTree.csproj
+++ b/src/Microsoft.ML.FastTree/Microsoft.ML.FastTree.csproj
@@ -19,6 +19,9 @@
all
+
+ all
+
diff --git a/src/Microsoft.ML.FastTree/Properties/AssemblyInfo.cs b/src/Microsoft.ML.FastTree/Properties/AssemblyInfo.cs
index 4a1575a5aa..3c6bf1a860 100644
--- a/src/Microsoft.ML.FastTree/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.ML.FastTree/Properties/AssemblyInfo.cs
@@ -9,6 +9,7 @@
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Predictor.Tests" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.LightGbm" + PublicKey.Value)]
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.OneDal" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Sweeper" + PublicKey.Value)]
[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Internal.FastTree" + InternalPublicKey.Value)]
diff --git a/src/Microsoft.ML.FastTree/RandomForestClassification.cs b/src/Microsoft.ML.FastTree/RandomForestClassification.cs
index ec7846f7dc..a63811576e 100644
--- a/src/Microsoft.ML.FastTree/RandomForestClassification.cs
+++ b/src/Microsoft.ML.FastTree/RandomForestClassification.cs
@@ -3,13 +3,17 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Collections.Generic;
using System.Linq;
+using System.Runtime.InteropServices;
using Microsoft.ML;
using Microsoft.ML.Calibrators;
using Microsoft.ML.CommandLine;
using Microsoft.ML.Data;
using Microsoft.ML.EntryPoints;
+using Microsoft.ML.Internal.Utilities;
using Microsoft.ML.Model;
+using Microsoft.ML.OneDal;
using Microsoft.ML.Runtime;
using Microsoft.ML.Trainers.FastTree;
@@ -219,7 +223,24 @@ private protected override FastForestBinaryModelParameters TrainModelCore(TrainC
trainData.CheckOptFloatWeight();
FeatureCount = trainData.Schema.Feature.Value.Type.GetValueCount();
ConvertData(trainData);
- TrainCore(ch);
+
+ if (!trainData.Schema.Weight.HasValue && MLContext.OneDalDispatchingEnabled)
+ {
+ if (FastTreeTrainerOptions.FeatureFraction != 1.0)
+ {
+ ch.Warning($"oneDAL decision forest doesn't support 'FeatureFraction'[per tree] != 1.0, changing it from {FastTreeTrainerOptions.FeatureFraction} to 1.0");
+ FastTreeTrainerOptions.FeatureFraction = 1.0;
+ }
+ CursOpt cursorOpt = CursOpt.Label | CursOpt.Features;
+ var cursorFactory = new FloatLabelCursor.Factory(trainData, cursorOpt);
+ TrainCoreOneDal(ch, cursorFactory, FeatureCount);
+ if (FeatureMap != null)
+ TrainedEnsemble.RemapFeatures(FeatureMap);
+ }
+ else
+ {
+ TrainCore(ch);
+ }
}
// LogitBoost is naturally calibrated to
// output probabilities when transformed using
@@ -230,6 +251,100 @@ private protected override FastForestBinaryModelParameters TrainModelCore(TrainC
return new FastForestBinaryModelParameters(Host, TrainedEnsemble, FeatureCount, InnerOptions);
}
+ internal static class OneDal
+ {
+ private const string OneDalLibPath = "OneDalNative";
+
+ [DllImport(OneDalLibPath, EntryPoint = "decisionForestClassificationCompute")]
+ public static extern unsafe int DecisionForestClassificationCompute(
+ void* featuresPtr, void* labelsPtr, long nRows, int nColumns, int nClasses, int numberOfThreads,
+ float featureFractionPerSplit, int numberOfTrees, int numberOfLeaves, int minimumExampleCountPerLeaf, int maxBins,
+ void* lteChildPtr, void* gtChildPtr, void* splitFeaturePtr, void* featureThresholdPtr, void* leafValuesPtr, void* modelPtr);
+ }
+
+ [BestFriend]
+ private void TrainCoreOneDal(IChannel ch, FloatLabelCursor.Factory cursorFactory, int featureCount)
+ {
+ CheckOptions(ch);
+ Initialize(ch);
+
+ List featuresList = new List();
+ List labelsList = new List();
+ int nClasses = 2;
+ int numberOfLeaves = FastTreeTrainerOptions.NumberOfLeaves;
+ int numberOfTrees = FastTreeTrainerOptions.NumberOfTrees;
+
+ int numberOfThreads = 0;
+ if (FastTreeTrainerOptions.NumberOfThreads.HasValue)
+ numberOfThreads = FastTreeTrainerOptions.NumberOfThreads.Value;
+
+ long n = OneDalUtils.GetTrainData(ch, cursorFactory, ref featuresList, ref labelsList, featureCount);
+
+ float[] featuresArray = featuresList.ToArray();
+ float[] labelsArray = labelsList.ToArray();
+
+ int[] lteChildArray = new int[(numberOfLeaves - 1) * numberOfTrees];
+ int[] gtChildArray = new int[(numberOfLeaves - 1) * numberOfTrees];
+ int[] splitFeatureArray = new int[(numberOfLeaves - 1) * numberOfTrees];
+ float[] featureThresholdArray = new float[(numberOfLeaves - 1) * numberOfTrees];
+ float[] leafValuesArray = new float[numberOfLeaves * numberOfTrees];
+
+ int oneDalModelSize = -1;
+ int projectedOneDalModelSize = 96 * nClasses * numberOfLeaves * numberOfTrees + 4096 * 16;
+ byte[] oneDalModel = new byte[projectedOneDalModelSize];
+
+ unsafe
+ {
+#pragma warning disable MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ fixed (void* featuresPtr = &featuresArray[0], labelsPtr = &labelsArray[0],
+ lteChildPtr = <eChildArray[0], gtChildPtr = >ChildArray[0], splitFeaturePtr = &splitFeatureArray[0],
+ featureThresholdPtr = &featureThresholdArray[0], leafValuesPtr = &leafValuesArray[0], oneDalModelPtr = &oneDalModel[0])
+#pragma warning restore MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ {
+ oneDalModelSize = OneDal.DecisionForestClassificationCompute(featuresPtr, labelsPtr, n, featureCount, nClasses,
+ numberOfThreads, (float)FastTreeTrainerOptions.FeatureFractionPerSplit, numberOfTrees,
+ numberOfLeaves, FastTreeTrainerOptions.MinimumExampleCountPerLeaf, FastTreeTrainerOptions.MaximumBinCountPerFeature,
+ lteChildPtr, gtChildPtr, splitFeaturePtr, featureThresholdPtr, leafValuesPtr, oneDalModelPtr
+ );
+ }
+ }
+ TrainedEnsemble = new InternalTreeEnsemble();
+ for (int i = 0; i < numberOfTrees; ++i)
+ {
+ int[] lteChildArrayPerTree = new int[numberOfLeaves - 1];
+ int[] gtChildArrayPerTree = new int[numberOfLeaves - 1];
+ int[] splitFeatureArrayPerTree = new int[numberOfLeaves - 1];
+ float[] featureThresholdArrayPerTree = new float[numberOfLeaves - 1];
+ double[] leafValuesArrayPerTree = new double[numberOfLeaves];
+
+ int[][] categoricalSplitFeaturesPerTree = new int[numberOfLeaves - 1][];
+ bool[] categoricalSplitPerTree = new bool[numberOfLeaves - 1];
+ double[] splitGainPerTree = new double[numberOfLeaves - 1];
+ float[] defaultValueForMissingPerTree = new float[numberOfLeaves - 1];
+
+ for (int j = 0; j < numberOfLeaves - 1; ++j)
+ {
+ lteChildArrayPerTree[j] = lteChildArray[(numberOfLeaves - 1) * i + j];
+ gtChildArrayPerTree[j] = gtChildArray[(numberOfLeaves - 1) * i + j];
+ splitFeatureArrayPerTree[j] = splitFeatureArray[(numberOfLeaves - 1) * i + j];
+ featureThresholdArrayPerTree[j] = featureThresholdArray[(numberOfLeaves - 1) * i + j];
+ leafValuesArrayPerTree[j] = leafValuesArray[numberOfLeaves * i + j];
+
+ categoricalSplitFeaturesPerTree[j] = null;
+ categoricalSplitPerTree[j] = false;
+ splitGainPerTree[j] = 0.0;
+ defaultValueForMissingPerTree[j] = 0.0f;
+ }
+ leafValuesArrayPerTree[numberOfLeaves - 1] = leafValuesArray[numberOfLeaves * i + numberOfLeaves - 1];
+
+ InternalQuantileRegressionTree newTree = new InternalQuantileRegressionTree(splitFeatureArrayPerTree, splitGainPerTree, null,
+ featureThresholdArrayPerTree, defaultValueForMissingPerTree, lteChildArrayPerTree, gtChildArrayPerTree, leafValuesArrayPerTree,
+ categoricalSplitFeaturesPerTree, categoricalSplitPerTree);
+ newTree.PopulateThresholds(TrainSet);
+ TrainedEnsemble.AddTree(newTree);
+ }
+ }
+
private protected override ObjectiveFunctionBase ConstructObjFunc(IChannel ch)
{
return new ObjectiveFunctionImpl(TrainSet, _trainSetLabels, FastTreeTrainerOptions);
diff --git a/src/Microsoft.ML.FastTree/RandomForestRegression.cs b/src/Microsoft.ML.FastTree/RandomForestRegression.cs
index 4410fc7d7c..8744fdf16c 100644
--- a/src/Microsoft.ML.FastTree/RandomForestRegression.cs
+++ b/src/Microsoft.ML.FastTree/RandomForestRegression.cs
@@ -3,6 +3,9 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
using Microsoft.ML;
using Microsoft.ML.CommandLine;
using Microsoft.ML.Data;
@@ -10,6 +13,7 @@
using Microsoft.ML.Internal.Utilities;
using Microsoft.ML.Model;
using Microsoft.ML.Model.OnnxConverter;
+using Microsoft.ML.OneDal;
using Microsoft.ML.Runtime;
using Microsoft.ML.Trainers.FastTree;
@@ -358,11 +362,121 @@ private protected override FastForestRegressionModelParameters TrainModelCore(Tr
trainData.CheckOptFloatWeight();
FeatureCount = trainData.Schema.Feature.Value.Type.GetValueCount();
ConvertData(trainData);
- TrainCore(ch);
+
+ if (!trainData.Schema.Weight.HasValue && MLContext.OneDalDispatchingEnabled)
+ {
+ if (FastTreeTrainerOptions.FeatureFraction != 1.0)
+ {
+ ch.Warning($"oneDAL decision forest doesn't support 'FeatureFraction'[per tree] != 1.0, changing it from {FastTreeTrainerOptions.FeatureFraction} to 1.0");
+ FastTreeTrainerOptions.FeatureFraction = 1.0;
+ }
+ CursOpt cursorOpt = CursOpt.Label | CursOpt.Features;
+ var cursorFactory = new FloatLabelCursor.Factory(trainData, cursorOpt);
+ TrainCoreOneDal(ch, cursorFactory, FeatureCount);
+ if (FeatureMap != null)
+ TrainedEnsemble.RemapFeatures(FeatureMap);
+ }
+ else
+ {
+ TrainCore(ch);
+ }
}
return new FastForestRegressionModelParameters(Host, TrainedEnsemble, FeatureCount, InnerOptions, FastTreeTrainerOptions.NumberOfQuantileSamples);
}
+ internal static class OneDal
+ {
+ private const string OneDalLibPath = "OneDalNative";
+
+ [DllImport(OneDalLibPath, EntryPoint = "decisionForestRegressionCompute")]
+ public static extern unsafe int DecisionForestRegressionCompute(
+ void* featuresPtr, void* labelsPtr, long nRows, int nColumns, int numberOfThreads,
+ float featureFractionPerSplit, int numberOfTrees, int numberOfLeaves, int minimumExampleCountPerLeaf, int maxBins,
+ void* lteChildPtr, void* gtChildPtr, void* splitFeaturePtr, void* featureThresholdPtr, void* leafValuesPtr, void* modelPtr);
+ }
+
+ [BestFriend]
+ private void TrainCoreOneDal(IChannel ch, FloatLabelCursor.Factory cursorFactory, int featureCount)
+ {
+ CheckOptions(ch);
+ Initialize(ch);
+
+ List featuresList = new List();
+ List labelsList = new List();
+ int numberOfLeaves = FastTreeTrainerOptions.NumberOfLeaves;
+ int numberOfTrees = FastTreeTrainerOptions.NumberOfTrees;
+
+ int numberOfThreads = 0;
+ if (FastTreeTrainerOptions.NumberOfThreads.HasValue)
+ numberOfThreads = FastTreeTrainerOptions.NumberOfThreads.Value;
+
+ long n = OneDalUtils.GetTrainData(ch, cursorFactory, ref featuresList, ref labelsList, featureCount);
+
+ float[] featuresArray = featuresList.ToArray();
+ float[] labelsArray = labelsList.ToArray();
+
+ int[] lteChildArray = new int[(numberOfLeaves - 1) * numberOfTrees];
+ int[] gtChildArray = new int[(numberOfLeaves - 1) * numberOfTrees];
+ int[] splitFeatureArray = new int[(numberOfLeaves - 1) * numberOfTrees];
+ float[] featureThresholdArray = new float[(numberOfLeaves - 1) * numberOfTrees];
+ float[] leafValuesArray = new float[numberOfLeaves * numberOfTrees];
+
+ int oneDalModelSize = -1;
+ int projectedOneDalModelSize = 96 * 1 * numberOfLeaves * numberOfTrees + 4096 * 16;
+ byte[] oneDalModel = new byte[projectedOneDalModelSize];
+
+ unsafe
+ {
+#pragma warning disable MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ fixed (void* featuresPtr = &featuresArray[0], labelsPtr = &labelsArray[0],
+ lteChildPtr = <eChildArray[0], gtChildPtr = >ChildArray[0], splitFeaturePtr = &splitFeatureArray[0],
+ featureThresholdPtr = &featureThresholdArray[0], leafValuesPtr = &leafValuesArray[0], oneDalModelPtr = &oneDalModel[0])
+#pragma warning restore MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ {
+ oneDalModelSize = OneDal.DecisionForestRegressionCompute(featuresPtr, labelsPtr, n, featureCount,
+ numberOfThreads, (float)FastTreeTrainerOptions.FeatureFractionPerSplit, numberOfTrees,
+ numberOfLeaves, FastTreeTrainerOptions.MinimumExampleCountPerLeaf, FastTreeTrainerOptions.MaximumBinCountPerFeature,
+ lteChildPtr, gtChildPtr, splitFeaturePtr, featureThresholdPtr, leafValuesPtr, oneDalModelPtr
+ );
+ }
+ }
+ TrainedEnsemble = new InternalTreeEnsemble();
+ for (int i = 0; i < numberOfTrees; ++i)
+ {
+ int[] lteChildArrayPerTree = new int[numberOfLeaves - 1];
+ int[] gtChildArrayPerTree = new int[numberOfLeaves - 1];
+ int[] splitFeatureArrayPerTree = new int[numberOfLeaves - 1];
+ float[] featureThresholdArrayPerTree = new float[numberOfLeaves - 1];
+ double[] leafValuesArrayPerTree = new double[numberOfLeaves];
+
+ int[][] categoricalSplitFeaturesPerTree = new int[numberOfLeaves - 1][];
+ bool[] categoricalSplitPerTree = new bool[numberOfLeaves - 1];
+ double[] splitGainPerTree = new double[numberOfLeaves - 1];
+ float[] defaultValueForMissingPerTree = new float[numberOfLeaves - 1];
+
+ for (int j = 0; j < numberOfLeaves - 1; ++j)
+ {
+ lteChildArrayPerTree[j] = lteChildArray[(numberOfLeaves - 1) * i + j];
+ gtChildArrayPerTree[j] = gtChildArray[(numberOfLeaves - 1) * i + j];
+ splitFeatureArrayPerTree[j] = splitFeatureArray[(numberOfLeaves - 1) * i + j];
+ featureThresholdArrayPerTree[j] = featureThresholdArray[(numberOfLeaves - 1) * i + j];
+ leafValuesArrayPerTree[j] = leafValuesArray[numberOfLeaves * i + j];
+
+ categoricalSplitFeaturesPerTree[j] = null;
+ categoricalSplitPerTree[j] = false;
+ splitGainPerTree[j] = 0.0;
+ defaultValueForMissingPerTree[j] = 0.0f;
+ }
+ leafValuesArrayPerTree[numberOfLeaves - 1] = leafValuesArray[numberOfLeaves * i + numberOfLeaves - 1];
+
+ InternalQuantileRegressionTree newTree = new InternalQuantileRegressionTree(splitFeatureArrayPerTree, splitGainPerTree, null,
+ featureThresholdArrayPerTree, defaultValueForMissingPerTree, lteChildArrayPerTree, gtChildArrayPerTree, leafValuesArrayPerTree,
+ categoricalSplitFeaturesPerTree, categoricalSplitPerTree);
+ newTree.PopulateThresholds(TrainSet);
+ TrainedEnsemble.AddTree(newTree);
+ }
+ }
+
private protected override void PrepareLabels(IChannel ch)
{
}
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
index e5ae4776ed..c193029668 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalQuantileRegressionTree.cs
@@ -29,6 +29,14 @@ public InternalQuantileRegressionTree(int maxLeaves)
{
}
+ public InternalQuantileRegressionTree(int[] splitFeatures, double[] splitGain, double[] gainPValue,
+ float[] rawThresholds, float[] defaultValueForMissing, int[] lteChild, int[] gtChild, double[] leafValues,
+ int[][] categoricalSplitFeatures, bool[] categoricalSplit)
+ : base(splitFeatures, splitGain, gainPValue, rawThresholds, defaultValueForMissing,
+ lteChild, gtChild, leafValues, categoricalSplitFeatures, categoricalSplit)
+ {
+ }
+
internal InternalQuantileRegressionTree(ModelLoadContext ctx, bool usingDefaultValue, bool categoricalSplits)
: base(ctx, usingDefaultValue, categoricalSplits)
{
diff --git a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
index bdfaabdcde..02a3cdb36e 100644
--- a/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
+++ b/src/Microsoft.ML.FastTree/TreeEnsemble/InternalRegressionTree.cs
@@ -51,7 +51,7 @@ internal class InternalRegressionTree
///
public int[][] CategoricalSplitFeatureRanges;
// These are the thresholds based on the binned values of the raw features.
- public uint[] Thresholds { get; }
+ public uint[] Thresholds { get; private set; }
// These are the thresholds based on the raw feature values. Populated after training.
public float[] RawThresholds { get; private set; }
public double[] SplitGains { get { return _splitGain; } }
@@ -190,9 +190,9 @@ public static InternalRegressionTree Create(int numLeaves, int[] splitFeatures,
}
internal InternalRegressionTree(int[] splitFeatures, double[] splitGain, double[] gainPValue,
- float[] rawThresholds, float[] defaultValueForMissing, int[] lteChild, int[] gtChild, double[] leafValues,
- int[][] categoricalSplitFeatures, bool[] categoricalSplit)
- : this()
+float[] rawThresholds, float[] defaultValueForMissing, int[] lteChild, int[] gtChild, double[] leafValues,
+int[][] categoricalSplitFeatures, bool[] categoricalSplit)
+: this()
{
Contracts.CheckParam(Utils.Size(splitFeatures) > 0, nameof(splitFeatures), "Number of split features must be positive");
@@ -201,6 +201,7 @@ internal InternalRegressionTree(int[] splitFeatures, double[] splitGain, double[
_splitGain = splitGain;
_gainPValue = gainPValue;
RawThresholds = rawThresholds;
+ Thresholds = new uint[NumLeaves - 1];
DefaultValueForMissing = defaultValueForMissing;
LteChild = lteChild;
GtChild = gtChild;
@@ -1099,6 +1100,32 @@ public void PopulateRawThresholds(Dataset dataset)
}
}
+ public void PopulateThresholds(Dataset dataset)
+ {
+ var features = dataset.Flocks;
+
+ int numNodes = NumLeaves - 1;
+ for (int n = 0; n < numNodes; n++)
+ {
+ int flock;
+ int subfeature;
+ dataset.MapFeatureToFlockAndSubFeature(SplitFeatures[n], out flock, out subfeature);
+ if (CategoricalSplit[n] == false)
+ {
+ uint numBins = (uint)dataset.Flocks[flock].BinUpperBounds(subfeature).Length;
+ for (uint i = 1; i < numBins; ++i)
+ {
+ double rawThreshold = dataset.Flocks[flock].BinUpperBounds(subfeature)[i];
+ if (RawThresholds[n] < rawThreshold)
+ {
+ Thresholds[n] = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
public void RemapFeatures(int[] oldToNewFeatures)
{
Contracts.AssertValue(oldToNewFeatures);
diff --git a/src/Microsoft.ML.ImageAnalytics/MLImage.cs b/src/Microsoft.ML.ImageAnalytics/MLImage.cs
index a7d37582b7..4bb3a0571d 100644
--- a/src/Microsoft.ML.ImageAnalytics/MLImage.cs
+++ b/src/Microsoft.ML.ImageAnalytics/MLImage.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using Microsoft.ML.Transforms.Image;
using SkiaSharp;
using System;
using System.Collections.Generic;
@@ -9,6 +10,7 @@
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using static Microsoft.ML.Transforms.Image.ImagePixelExtractingEstimator;
namespace Microsoft.ML.Data
{
@@ -126,6 +128,30 @@ private set
}
}
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "")]
+ public byte[] GetBGRPixels
+ {
+ get
+ {
+ ThrowInvalidOperationExceptionIfDisposed();
+
+ // 3 is because we only want RGB not alpha channels
+ byte[] pixels = new byte[Height * Width * 3];
+
+ var pixelData = _image.Pixels;
+ int idx = 0;
+ for (int i = 0; i < Height * Width * 3;)
+ {
+
+ pixels[i++] = pixelData[idx].Blue;
+ pixels[i++] = pixelData[idx].Green;
+ pixels[i++] = pixelData[idx++].Red;
+ }
+
+ return pixels;
+ }
+ }
+
///
/// Gets the image pixel data.
///
diff --git a/src/Microsoft.ML.LightGbm/LightGbmTrainerBase.cs b/src/Microsoft.ML.LightGbm/LightGbmTrainerBase.cs
index 4f17e19128..6377fc1791 100644
--- a/src/Microsoft.ML.LightGbm/LightGbmTrainerBase.cs
+++ b/src/Microsoft.ML.LightGbm/LightGbmTrainerBase.cs
@@ -577,6 +577,13 @@ private Dataset LoadTrainingData(IChannel ch, RoleMappedData trainData, out Cate
CheckAndUpdateParametersBeforeTraining(ch, trainData, labels, groups);
string param = LightGbmInterfaceUtils.JoinParameters(GbmOptions);
+ Console.WriteLine("**************** LightGBM parameters: " /*+ param*/);
+ foreach (var k in GbmOptions.Keys)
+ {
+ Console.WriteLine(k + " " + GbmOptions[k]);
+ }
+ Console.WriteLine("****************");
+
Dataset dtrain;
// To reduce peak memory usage, only enable one sampling task at any given time.
lock (LightGbmShared.SampleLock)
diff --git a/src/Microsoft.ML.Mkl.Components/Microsoft.ML.Mkl.Components.csproj b/src/Microsoft.ML.Mkl.Components/Microsoft.ML.Mkl.Components.csproj
index b944230fdb..56566d43df 100644
--- a/src/Microsoft.ML.Mkl.Components/Microsoft.ML.Mkl.Components.csproj
+++ b/src/Microsoft.ML.Mkl.Components/Microsoft.ML.Mkl.Components.csproj
@@ -19,6 +19,9 @@
all
+
+ all
+
diff --git a/src/Microsoft.ML.Mkl.Components/OlsLinearRegression.cs b/src/Microsoft.ML.Mkl.Components/OlsLinearRegression.cs
index 96b52fb8f2..1d072c1290 100644
--- a/src/Microsoft.ML.Mkl.Components/OlsLinearRegression.cs
+++ b/src/Microsoft.ML.Mkl.Components/OlsLinearRegression.cs
@@ -14,6 +14,7 @@
using Microsoft.ML.Internal.Internallearn;
using Microsoft.ML.Internal.Utilities;
using Microsoft.ML.Model;
+using Microsoft.ML.OneDal;
using Microsoft.ML.Runtime;
using Microsoft.ML.Trainers;
@@ -88,6 +89,12 @@ public sealed class Options : TrainerInputBaseWithWeight
///
[Argument(ArgumentType.LastOccurrenceWins, HelpText = "Whether to calculate per parameter significance statistics", ShortName = "sig")]
public bool CalculateStatistics = true;
+
+ ///
+ /// Number of data points per batch, when loading data.
+ ///
+ [Argument(ArgumentType.AtMostOnce, HelpText = "Number of entries in a batch when loading data (0 = auto).", Hide = true)]
+ public int BatchSize = 0;
}
internal const string LoadNameValue = "OLSLinearRegression";
@@ -98,6 +105,7 @@ public sealed class Options : TrainerInputBaseWithWeight
private readonly float _l2Weight;
private readonly bool _perParameterSignificance;
+ private readonly int _batchSize;
private protected override PredictionKind PredictionKind => PredictionKind.Regression;
@@ -117,6 +125,7 @@ internal OlsTrainer(IHostEnvironment env, Options options)
Host.CheckUserArg(options.L2Regularization >= 0, nameof(options.L2Regularization), "L2 regularization term cannot be negative");
_l2Weight = options.L2Regularization;
_perParameterSignificance = options.CalculateStatistics;
+ _batchSize = options.BatchSize;
}
private protected override RegressionPredictionTransformer MakeTransformer(OlsModelParameters model, DataViewSchema trainSchema)
@@ -170,24 +179,127 @@ private protected override OlsModelParameters TrainModelCore(TrainContext contex
}
}
- private OlsModelParameters TrainCore(IChannel ch, FloatLabelCursor.Factory cursorFactory, int featureCount)
+ internal static class OneDal
{
- Host.AssertValue(ch);
- ch.AssertValue(cursorFactory);
+ private const string OneDalLibPath = "OneDalNative";
- int m = featureCount + 1;
+ [DllImport(OneDalLibPath, EntryPoint = "ridgeRegressionOnlineCompute")]
+ public static extern unsafe int RidgeRegressionOnlineCompute(void* featuresPtr, void* labelsPtr, int nRows, int nColumns,
+ float l2Reg, void* partialResultPtr, int partialResultSize);
- // Check for memory conditions first.
- if ((long)m * (m + 1) / 2 > int.MaxValue)
- throw ch.Except("Cannot hold covariance matrix in memory with {0} features", m - 1);
+ [DllImport(OneDalLibPath, EntryPoint = "ridgeRegressionOnlineFinalize")]
+ public static extern unsafe void RidgeRegressionOnlineFinalize(void* featuresPtr, void* labelsPtr, long nAllRows, int nRows, int nColumns,
+ float l2Reg, void* partialResultPtr, int partialResultSize, void* betaPtr, void* xtyPtr, void* xtxPtr);
+ }
- // Track the number of examples.
- long n = 0;
- // Since we are accumulating over many values, we use Double even for the single precision build.
+ [BestFriend]
+ private void ComputeOneDalRegression(IChannel ch, FloatLabelCursor.Factory cursorFactory, int m, ref Double[] beta, Double[] xtx, ref long n, ref Double yMean)
+ {
var xty = new Double[m];
- // The layout of this algorithm is a packed row-major lower triangular matrix.
- var xtx = new Double[m * (m + 1) / 2];
+ int batchSize = _batchSize;
+ if (batchSize == 0)
+ {
+ // Set default batch size: 2 ^ 22 / number of features;
+ batchSize = (1 << 22) / m;
+ }
+
+ var labelsArray = new Double[batchSize];
+ var featuresArray = new Double[(m - 1) * batchSize];
+
+ // estimated size of oneDAL regression partial result
+ byte[] partialResultArray = new byte[2 * 1024 * m + 4096];
+ int partialResultSize = 0;
+
+ using (var cursor = cursorFactory.Create())
+ {
+ while (cursor.MoveNext())
+ {
+ var rowOffset = n % batchSize;
+
+ if (n != 0 && rowOffset == 0)
+ {
+ unsafe
+ {
+#pragma warning disable MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ fixed (void* featuresPtr = &featuresArray[0], labelsPtr = &labelsArray[0], partialResultPtr = &partialResultArray[0])
+#pragma warning restore MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ {
+ partialResultSize = OneDal.RidgeRegressionOnlineCompute(featuresPtr, labelsPtr, batchSize, m - 1, _l2Weight, partialResultPtr, partialResultSize);
+ }
+ }
+ }
+
+ labelsArray[rowOffset] = cursor.Label;
+ var values = cursor.Features.GetValues();
+
+ if (cursor.Features.IsDense)
+ {
+ ch.Assert(values.Length + 1 == m);
+
+ for (int j = 0; j < m - 1; ++j)
+ {
+ featuresArray[rowOffset * (m - 1) + j] = values[j];
+ }
+ }
+ else
+ {
+ var indices = cursor.Features.GetIndices();
+ int i = 0;
+ for (int j = 0; j < indices.Length; ++j)
+ {
+ for (int k = i; k < indices[j]; ++k)
+ {
+ featuresArray[rowOffset * (m - 1) + k] = 0;
+ }
+ featuresArray[rowOffset * (m - 1) + j] = values[indices[j]];
+ i = indices[j] + 1;
+ }
+ }
+ n++;
+ }
+ ch.Check(n > 0, "No training examples in dataset.");
+ if (cursor.BadFeaturesRowCount > 0)
+ ch.Warning("Skipped {0} instances with missing features/labelColumn during training", cursor.SkippedRowCount);
+
+ unsafe
+ {
+#pragma warning disable MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ fixed (void* featuresPtr = &featuresArray[0], labelsPtr = &labelsArray[0], partialResultPtr = &partialResultArray[0], betaPtr = &beta[0], xtyPtr = &xty[0], xtxPtr = &xtx[0])
+#pragma warning restore MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ {
+ OneDal.RidgeRegressionOnlineFinalize(featuresPtr, labelsPtr, n, (int)(n % batchSize), m - 1, _l2Weight, partialResultPtr, partialResultSize, betaPtr, xtyPtr, xtxPtr);
+ }
+ }
+ }
+
+ if (!(_l2Weight > 0) && n < m)
+ throw ch.Except("Ordinary least squares requires more examples than parameters. There are {0} parameters, but {1} examples. To enable training, use a positive L2 weight so this behaves as ridge regression.", m, n);
+
+ yMean = n == 0 ? 0 : xty[m - 1] / n;
+
+ ch.Info("Trainer solving for {0} parameters across {1} examples", m, n);
+ // Cholesky Decomposition of X'X into LL'
+ try
+ {
+ Mkl.Pptrf(Mkl.Layout.RowMajor, Mkl.UpLo.Lo, m, xtx);
+ }
+ catch (DllNotFoundException)
+ {
+ // REVIEW: Is there no better way?
+ throw ch.ExceptNotSupp("The MKL library (libMklImports) or one of its dependencies is missing.");
+ }
+ // Invert X'X:
+ Mkl.Pptri(Mkl.Layout.RowMajor, Mkl.UpLo.Lo, m, xtx);
+
+ // Check that the solution is valid.
+ for (int i = 0; i < beta.Length; ++i)
+ ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");
+ }
+
+ private void ComputeMklRegression(IChannel ch, FloatLabelCursor.Factory cursorFactory, int m, ref Double[] beta, Double[] xtx, ref long n, ref Double yMean)
+ {
+ var xty = new Double[m];
// Build X'X (lower triangular) and X'y incrementally (X'X+=X'X_i; X'y+=X'y_i):
using (var cursor = cursorFactory.Create())
{
@@ -267,7 +379,7 @@ private OlsModelParameters TrainCore(IChannel ch, FloatLabelCursor.Factory curso
if (!(_l2Weight > 0) && n < m)
throw ch.Except("Ordinary least squares requires more examples than parameters. There are {0} parameters, but {1} examples. To enable training, use a positive L2 weight so this behaves as ridge regression.", m, n);
- Double yMean = n == 0 ? 0 : xty[0] / n;
+ yMean = n == 0 ? 0 : xty[0] / n;
ch.Info("Trainer solving for {0} parameters across {1} examples", m, n);
// Cholesky Decomposition of X'X into LL'
@@ -282,14 +394,46 @@ private OlsModelParameters TrainCore(IChannel ch, FloatLabelCursor.Factory curso
}
// Solve for beta in (LL')beta = X'y:
Mkl.Pptrs(Mkl.Layout.RowMajor, Mkl.UpLo.Lo, m, 1, xtx, xty, 1);
- // Note that the solver overwrote xty so it contains the solution. To be more clear,
- // we effectively change its name (through reassignment) so we don't get confused that
- // this is somehow xty in the remaining calculation.
- var beta = xty;
- xty = null;
+
+ // Invert X'X:
+ Mkl.Pptri(Mkl.Layout.RowMajor, Mkl.UpLo.Lo, m, xtx);
+
// Check that the solution is valid.
- for (int i = 0; i < beta.Length; ++i)
- ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");
+ for (int i = 0; i < xty.Length; ++i)
+ ch.Check(FloatUtils.IsFinite(xty[i]), "Non-finite values detected in OLS solution");
+
+ beta = xty;
+ xty = null;
+ }
+
+ private OlsModelParameters TrainCore(IChannel ch, FloatLabelCursor.Factory cursorFactory, int featureCount)
+ {
+ Host.AssertValue(ch);
+ ch.AssertValue(cursorFactory);
+
+ int m = featureCount + 1;
+
+ // Check for memory conditions first.
+ if ((long)m * (m + 1) / 2 > int.MaxValue)
+ throw ch.Except("Cannot hold covariance matrix in memory with {0} features", m - 1);
+
+ // Track the number of examples.
+ long n = 0;
+
+ // Since we are accumulating over many values, we use Double even for the single precision build.
+ // The layout of this algorithm is a packed row-major lower triangular matrix.
+ var xtx = new Double[m * (m + 1) / 2];
+ var beta = new Double[m];
+ Double yMean = 0;
+
+ if (MLContext.OneDalDispatchingEnabled)
+ {
+ ComputeOneDalRegression(ch, cursorFactory, m, ref beta, xtx, ref n, ref yMean);
+ }
+ else
+ {
+ ComputeMklRegression(ch, cursorFactory, m, ref beta, xtx, ref n, ref yMean);
+ }
var weightsValues = new float[beta.Length - 1];
for (int i = 1; i < beta.Length; ++i)
@@ -343,8 +487,6 @@ private OlsModelParameters TrainCore(IChannel ch, FloatLabelCursor.Factory curso
var standardErrors = new Double[m];
var tValues = new Double[m];
var pValues = new Double[m];
- // Invert X'X:
- Mkl.Pptri(Mkl.Layout.RowMajor, Mkl.UpLo.Lo, m, xtx);
var s2 = rss / (n - m); // estimate of variance of y
for (int i = 0; i < m; i++)
diff --git a/src/Microsoft.ML.OneDal/Microsoft.ML.OneDal.csproj b/src/Microsoft.ML.OneDal/Microsoft.ML.OneDal.csproj
new file mode 100644
index 0000000000..ab1cc7a4cb
--- /dev/null
+++ b/src/Microsoft.ML.OneDal/Microsoft.ML.OneDal.csproj
@@ -0,0 +1,33 @@
+
+
+
+ netstandard2.0
+ Microsoft.ML.OneDal
+ true
+ ML.NET additional learners making use of Intel® oneAPI Data Analytics Library (oneDAL).
+ $(TargetsForTfmSpecificBuildOutput)
+
+
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+ all
+
+
+
+ win
+ linux
+ osx
+
+
+
+
+
diff --git a/src/Microsoft.ML.OneDal/OneDalCatalog.cs b/src/Microsoft.ML.OneDal/OneDalCatalog.cs
new file mode 100644
index 0000000000..98fa94d05e
--- /dev/null
+++ b/src/Microsoft.ML.OneDal/OneDalCatalog.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.ML.Data;
+using Microsoft.ML.Runtime;
+using Microsoft.ML.Trainers;
+using Microsoft.ML.Transforms;
+
+namespace Microsoft.ML
+{
+ public static class OneDalCatalog
+ {
+ }
+}
diff --git a/src/Microsoft.ML.OneDal/OneDalUtils.cs b/src/Microsoft.ML.OneDal/OneDalUtils.cs
new file mode 100644
index 0000000000..061c4a3a88
--- /dev/null
+++ b/src/Microsoft.ML.OneDal/OneDalUtils.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Microsoft.ML.Internal.Utilities;
+using Microsoft.ML.Runtime;
+using Microsoft.ML.Trainers;
+
+namespace Microsoft.ML.OneDal
+{
+ [BestFriend]
+ internal static class OneDalUtils
+ {
+
+ [BestFriend]
+ internal static bool IsDispatchingEnabled()
+ {
+ if (Environment.GetEnvironmentVariable("MLNET_BACKEND") == "ONEDAL" &&
+ System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture == System.Runtime.InteropServices.Architecture.X64)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+#if NETFRAMEWORK
+ // AppContext not available in the framework, user needs to set PATH manually
+ // this will probably result in a runtime error where the user needs to set the PATH
+#else
+ var currentDir = AppContext.BaseDirectory;
+ var nativeLibs = Path.Combine(currentDir, "runtimes", "win-x64", "native");
+ var originalPath = Environment.GetEnvironmentVariable("PATH");
+ Environment.SetEnvironmentVariable("PATH", nativeLibs + ";" + originalPath);
+#endif
+ }
+ return true;
+ }
+ return false;
+ }
+
+ [BestFriend]
+ internal static long GetTrainData(IChannel channel, FloatLabelCursor.Factory cursorFactory, ref List featuresList, ref List labelsList, int numberOfFeatures)
+ {
+ long n = 0;
+ using (var cursor = cursorFactory.Create())
+ {
+ while (cursor.MoveNext())
+ {
+ // label
+ labelsList.Add(cursor.Label);
+
+ // features
+ var values = cursor.Features.GetValues();
+ if (cursor.Features.IsDense)
+ {
+ channel.Assert(values.Length == numberOfFeatures);
+
+ for (int j = 0; j < numberOfFeatures; ++j)
+ {
+ featuresList.Add(values[j]);
+ }
+ }
+ else
+ {
+ var indices = cursor.Features.GetIndices();
+ int i = 0;
+ for (int j = 0; j < indices.Length; ++j)
+ {
+ for (int k = i; k < indices[j]; ++k)
+ {
+ featuresList.Add(0);
+ }
+ featuresList.Add(values[indices[j]]);
+ i = indices[j] + 1;
+ }
+ }
+ n++;
+ }
+ channel.Check(n > 0, "No training examples in dataset.");
+ if (cursor.BadFeaturesRowCount > 0)
+ channel.Warning("Skipped {0} instances with missing features/labelColumn during training", cursor.SkippedRowCount);
+ }
+ return n;
+ }
+ }
+}
diff --git a/src/Microsoft.ML.OneDal/Properties/AssemblyInfo.cs b/src/Microsoft.ML.OneDal/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..49fde74709
--- /dev/null
+++ b/src/Microsoft.ML.OneDal/Properties/AssemblyInfo.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+using Microsoft.ML;
+
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.FastTree" + PublicKey.Value)]
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Mkl.Components" + PublicKey.Value)]
+
+[assembly: InternalsVisibleTo(assemblyName: "Microsoft.ML.Tests" + PublicKey.TestValue)]
+[assembly: InternalsVisibleTo(assemblyName: "RunTests" + InternalPublicKey.Value)]
+
+[assembly: WantsToBeBestFriends]
diff --git a/src/Microsoft.ML.OnnxTransformer/OnnxTransform.cs b/src/Microsoft.ML.OnnxTransformer/OnnxTransform.cs
index 1f8ef34995..99d431f882 100644
--- a/src/Microsoft.ML.OnnxTransformer/OnnxTransform.cs
+++ b/src/Microsoft.ML.OnnxTransformer/OnnxTransform.cs
@@ -805,7 +805,7 @@ private class NamedOnnxValueGetterVec : INamedOnnxValueGetter
public NamedOnnxValueGetterVec(DataViewRow input, int colIndex, OnnxShape tensorShape)
{
_srcGetter = input.GetGetter>(input.Schema[colIndex]);
- _tensorShape = tensorShape;
+ _tensorShape = new OnnxShape(tensorShape);
_colName = input.Schema[colIndex].Name;
_vBuffer = default;
_vBufferDense = default;
diff --git a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
index 51ce75b3af..cd6d078a1e 100644
--- a/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
+++ b/src/Microsoft.ML.SamplesUtils/SamplesDatasetUtils.cs
@@ -7,6 +7,9 @@
using System.IO;
using System.Linq;
using System.Net;
+using System.Net.Http;
+using System.Runtime.InteropServices.ComTypes;
+using System.Threading.Tasks;
using Microsoft.ML.Data;
namespace Microsoft.ML.SamplesUtils
@@ -177,22 +180,26 @@ public static string DownloadTensorFlowSentimentModel()
if (!Directory.Exists(varPath))
Directory.CreateDirectory(varPath);
- Download(Path.Combine(remotePath, "saved_model.pb"), Path.Combine(path, "saved_model.pb"));
- Download(Path.Combine(remotePath, "imdb_word_index.csv"), Path.Combine(path, "imdb_word_index.csv"));
- Download(Path.Combine(remotePath, "variables", "variables.data-00000-of-00001"), Path.Combine(varPath, "variables.data-00000-of-00001"));
- Download(Path.Combine(remotePath, "variables", "variables.index"), Path.Combine(varPath, "variables.index"));
+ Download(Path.Combine(remotePath, "saved_model.pb"), Path.Combine(path, "saved_model.pb")).Wait();
+ Download(Path.Combine(remotePath, "imdb_word_index.csv"), Path.Combine(path, "imdb_word_index.csv")).Wait();
+ Download(Path.Combine(remotePath, "variables", "variables.data-00000-of-00001"), Path.Combine(varPath, "variables.data-00000-of-00001")).Wait();
+ Download(Path.Combine(remotePath, "variables", "variables.index"), Path.Combine(varPath, "variables.index")).Wait();
return path;
}
- private static string Download(string baseGitPath, string dataFile)
+ private static async Task Download(string baseGitPath, string dataFile)
{
if (File.Exists(dataFile))
return dataFile;
- using (WebClient client = new WebClient())
+ using (HttpClient client = new HttpClient())
{
- client.DownloadFile(new Uri($"{baseGitPath}"), dataFile);
+ var response = await client.GetStreamAsync(new Uri($"{baseGitPath}")).ConfigureAwait(false);
+ using (var fs = new FileStream(dataFile, FileMode.CreateNew))
+ {
+ await response.CopyToAsync(fs).ConfigureAwait(false);
+ }
}
return dataFile;
diff --git a/src/Microsoft.ML.SearchSpace/Converter/ChoiceOptionConverter.cs b/src/Microsoft.ML.SearchSpace/Converter/ChoiceOptionConverter.cs
new file mode 100644
index 0000000000..0cf010475f
--- /dev/null
+++ b/src/Microsoft.ML.SearchSpace/Converter/ChoiceOptionConverter.cs
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.ML.SearchSpace.Option;
+
+namespace Microsoft.ML.SearchSpace.Converter
+{
+ internal class ChoiceOptionConverter : JsonConverter
+ {
+ class Schema
+ {
+ ///
+ /// must be one of "int" | "float" | "double"
+ ///
+ [JsonPropertyName("default")]
+ public object Default { get; set; }
+
+ [JsonPropertyName("choices")]
+ public object[] Choices { get; set; }
+ }
+
+ public override ChoiceOption Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var schema = JsonSerializer.Deserialize(ref reader, options);
+
+ return new ChoiceOption(schema.Choices, schema.Default);
+ }
+
+ public override void Write(Utf8JsonWriter writer, ChoiceOption value, JsonSerializerOptions options)
+ {
+ var schema = new Schema
+ {
+ Choices = value.Choices,
+ Default = value.SampleFromFeatureSpace(value.Default),
+ };
+
+ JsonSerializer.Serialize(writer, schema, options);
+ }
+ }
+}
diff --git a/src/Microsoft.ML.SearchSpace/Converter/NumericOptionConverter.cs b/src/Microsoft.ML.SearchSpace/Converter/NumericOptionConverter.cs
new file mode 100644
index 0000000000..709213c69b
--- /dev/null
+++ b/src/Microsoft.ML.SearchSpace/Converter/NumericOptionConverter.cs
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.ML.SearchSpace.Option;
+
+namespace Microsoft.ML.SearchSpace.Converter
+{
+ internal class NumericOptionConverter : JsonConverter
+ {
+ class Schema
+ {
+ ///
+ /// must be one of "int" | "float" | "double"
+ ///
+ [JsonPropertyName("type")]
+ public string Type { get; set; }
+
+ [JsonPropertyName("default")]
+ public object Default { get; set; }
+
+ [JsonPropertyName("min")]
+ public object Min { get; set; }
+
+ [JsonPropertyName("max")]
+ public object Max { get; set; }
+
+ [JsonPropertyName("log_base")]
+ public bool LogBase { get; set; }
+ }
+
+ public override UniformNumericOption Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var schema = JsonSerializer.Deserialize(ref reader, options);
+
+ return schema.Type switch
+ {
+ "int" => new UniformIntOption(Convert.ToInt32(schema.Min), Convert.ToInt32(schema.Max), schema.LogBase, Convert.ToInt32(schema.Default)),
+ "float" => new UniformSingleOption(Convert.ToSingle(schema.Min), Convert.ToSingle(schema.Max), schema.LogBase, Convert.ToSingle(schema.Default)),
+ "double" => new UniformDoubleOption(Convert.ToDouble(schema.Min), Convert.ToDouble(schema.Max), schema.LogBase, Convert.ToDouble(schema.Default)),
+ _ => throw new ArgumentException($"unknown schema type: {schema.Type}"),
+ };
+ }
+
+ public override void Write(Utf8JsonWriter writer, UniformNumericOption value, JsonSerializerOptions options)
+ {
+ var schema = value switch
+ {
+ UniformIntOption intOption => new Schema
+ {
+ Type = "int",
+ Default = intOption.SampleFromFeatureSpace(intOption.Default).AsType(),
+ Min = Convert.ToInt32(intOption.Min),
+ Max = Convert.ToInt32(intOption.Max),
+ LogBase = intOption.LogBase,
+ },
+ UniformDoubleOption doubleOption => new Schema
+ {
+ Type = "double",
+ Default = doubleOption.SampleFromFeatureSpace(doubleOption.Default).AsType(),
+ Min = doubleOption.Min,
+ Max = doubleOption.Max,
+ LogBase = doubleOption.LogBase,
+ },
+ UniformSingleOption singleOption => new Schema
+ {
+ Type = "float",
+ Default = singleOption.SampleFromFeatureSpace(singleOption.Default).AsType(),
+ Min = Convert.ToSingle(singleOption.Min),
+ Max = Convert.ToSingle(singleOption.Max),
+ LogBase = singleOption.LogBase,
+ },
+ _ => throw new ArgumentException("unknown type"),
+ };
+
+ JsonSerializer.Serialize(writer, schema, options);
+ }
+ }
+}
diff --git a/src/Microsoft.ML.SearchSpace/Converter/OptionConverter.cs b/src/Microsoft.ML.SearchSpace/Converter/OptionConverter.cs
new file mode 100644
index 0000000000..ef2eb58e9a
--- /dev/null
+++ b/src/Microsoft.ML.SearchSpace/Converter/OptionConverter.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.ML.SearchSpace.Option;
+
+namespace Microsoft.ML.SearchSpace.Converter
+{
+ internal class OptionConverter : JsonConverter
+ {
+ public override OptionBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ try
+ {
+ return JsonSerializer.Deserialize(ref reader, options);
+ }
+ catch (Exception)
+ {
+ // try choice option
+ }
+
+ try
+ {
+ return JsonSerializer.Deserialize(ref reader, options);
+ }
+ catch (Exception)
+ {
+ // try numeric option
+ }
+
+ try
+ {
+ return JsonSerializer.Deserialize(ref reader, options);
+ }
+ catch (Exception)
+ {
+ throw new ArgumentException("unknown option type");
+ }
+ }
+
+ public override void Write(Utf8JsonWriter writer, OptionBase value, JsonSerializerOptions options)
+ {
+ if (value is SearchSpace ss)
+ {
+ JsonSerializer.Serialize(writer, ss, options);
+ }
+ else if (value is ChoiceOption choiceOption)
+ {
+ JsonSerializer.Serialize(writer, choiceOption, options);
+ }
+ else if (value is UniformNumericOption uniformNumericOption)
+ {
+ JsonSerializer.Serialize(writer, uniformNumericOption, options);
+ }
+ else
+ {
+ throw new ArgumentException("unknown option type");
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.SearchSpace/Converter/SearchSpaceConverter.cs b/src/Microsoft.ML.SearchSpace/Converter/SearchSpaceConverter.cs
new file mode 100644
index 0000000000..f0f9165c00
--- /dev/null
+++ b/src/Microsoft.ML.SearchSpace/Converter/SearchSpaceConverter.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.ML.SearchSpace.Option;
+
+namespace Microsoft.ML.SearchSpace.Converter
+{
+ internal class SearchSpaceConverter : JsonConverter
+ {
+ public override SearchSpace Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var optionKVPairs = JsonSerializer.Deserialize>(ref reader, options);
+
+ return new SearchSpace(optionKVPairs);
+ }
+
+ public override void Write(Utf8JsonWriter writer, SearchSpace value, JsonSerializerOptions options)
+ {
+ JsonSerializer.Serialize>(value, options);
+ }
+ }
+}
diff --git a/src/Microsoft.ML.SearchSpace/Microsoft.ML.SearchSpace.csproj b/src/Microsoft.ML.SearchSpace/Microsoft.ML.SearchSpace.csproj
index 85b50459f7..155feb6f84 100644
--- a/src/Microsoft.ML.SearchSpace/Microsoft.ML.SearchSpace.csproj
+++ b/src/Microsoft.ML.SearchSpace/Microsoft.ML.SearchSpace.csproj
@@ -2,7 +2,7 @@
netstandard2.0
- Microsoft.ML.AutoML
+ Microsoft.ML.Core
true
MSML_ContractsCheckMessageNotLiteralOrIdentifier
9.0
diff --git a/src/Microsoft.ML.SearchSpace/Option/ChoiceOption.cs b/src/Microsoft.ML.SearchSpace/Option/ChoiceOption.cs
index 3a66ce1fff..8f14f679e8 100644
--- a/src/Microsoft.ML.SearchSpace/Option/ChoiceOption.cs
+++ b/src/Microsoft.ML.SearchSpace/Option/ChoiceOption.cs
@@ -5,6 +5,8 @@
using System;
using System.Diagnostics.Contracts;
using System.Linq;
+using System.Text.Json.Serialization;
+using Microsoft.ML.SearchSpace.Converter;
#nullable enable
@@ -13,6 +15,7 @@ namespace Microsoft.ML.SearchSpace.Option
///
/// This class represent option for discrete value, such as string, enum, etc..
///
+ [JsonConverter(typeof(ChoiceOptionConverter))]
public sealed class ChoiceOption : OptionBase
{
private readonly UniformSingleOption _option;
diff --git a/src/Microsoft.ML.SearchSpace/Option/NestOption.cs b/src/Microsoft.ML.SearchSpace/Option/NestOption.cs
index a31aa74516..08c0847276 100644
--- a/src/Microsoft.ML.SearchSpace/Option/NestOption.cs
+++ b/src/Microsoft.ML.SearchSpace/Option/NestOption.cs
@@ -11,9 +11,9 @@
namespace Microsoft.ML.SearchSpace.Option
{
///
- /// This class represent nest option, which is an option that contains other options, like , or even itself.
+ /// This class represent nest option, which is an option that contains other options, like , or even itself.
///
- public sealed class NestOption : OptionBase, IDictionary
+ public sealed class SearchSpace : OptionBase, IDictionary
{
private readonly Dictionary _options = new Dictionary();
diff --git a/src/Microsoft.ML.SearchSpace/Option/OptionBase.cs b/src/Microsoft.ML.SearchSpace/Option/OptionBase.cs
index 6b218d242c..281e4137fc 100644
--- a/src/Microsoft.ML.SearchSpace/Option/OptionBase.cs
+++ b/src/Microsoft.ML.SearchSpace/Option/OptionBase.cs
@@ -5,11 +5,15 @@
#nullable enable
+using System.Text.Json.Serialization;
+using Microsoft.ML.SearchSpace.Converter;
+
namespace Microsoft.ML.SearchSpace.Option
{
///
/// abstrace class for Option.
///
+ [JsonConverter(typeof(OptionConverter))]
public abstract class OptionBase
{
///
@@ -37,7 +41,7 @@ public abstract class OptionBase
///
/// Gets the step of this option. The is used to determine the number of grid this option should be divided into. In , it's always the length of
- /// . And in , it's always [null]. And in , it's a combination of all in its options.
+ /// . And in , it's always [null]. And in , it's a combination of all in its options.
///
public abstract int?[] Step { get; }
}
diff --git a/src/Microsoft.ML.SearchSpace/Option/UniformNumericOption.cs b/src/Microsoft.ML.SearchSpace/Option/UniformNumericOption.cs
index 7680538889..a03bcbab0e 100644
--- a/src/Microsoft.ML.SearchSpace/Option/UniformNumericOption.cs
+++ b/src/Microsoft.ML.SearchSpace/Option/UniformNumericOption.cs
@@ -5,12 +5,15 @@
using System;
using System.Diagnostics.Contracts;
using System.Linq;
+using System.Text.Json.Serialization;
+using Microsoft.ML.SearchSpace.Converter;
namespace Microsoft.ML.SearchSpace.Option
{
///
/// abstract class for numeric option.
///
+ [JsonConverter(typeof(NumericOptionConverter))]
public abstract class UniformNumericOption : OptionBase
{
///
diff --git a/src/Microsoft.ML.SearchSpace/Parameter.cs b/src/Microsoft.ML.SearchSpace/Parameter.cs
index a2fb0406dd..87e85a803f 100644
--- a/src/Microsoft.ML.SearchSpace/Parameter.cs
+++ b/src/Microsoft.ML.SearchSpace/Parameter.cs
@@ -50,8 +50,15 @@ public enum ParameterType
}
///
- /// is used to save sweeping result from tuner and is used to restore mlnet pipeline from sweepable pipline.
+ /// is used to save sweeping result from tuner and is used to restore mlnet pipeline from sweepable pipeline.
///
+ ///
+ ///
+ ///
+ ///
+ ///
[JsonConverter(typeof(ParameterConverter))]
public sealed class Parameter : IDictionary, IEquatable, IEqualityComparer
{
diff --git a/src/Microsoft.ML.SearchSpace/Schema/SchemaBase.cs b/src/Microsoft.ML.SearchSpace/Schema/SchemaBase.cs
deleted file mode 100644
index d0b52142d9..0000000000
--- a/src/Microsoft.ML.SearchSpace/Schema/SchemaBase.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text.Json.Serialization;
-
-namespace Microsoft.ML.SearchSpace.Schema
-{
- [JsonConverter(typeof(JsonStringEnumConverter))]
- internal enum SchemaType
- {
- UniformDoubleOption = 0,
- IntegerOption = 1,
- ChoiceOption = 2,
- NestOption = 3,
- }
-
- internal abstract class SchemaBase
- {
- [JsonPropertyName("schema_type")]
- public abstract SchemaType SchemaType { get; }
-
- [JsonPropertyName("schema_type")]
- public abstract int Version { get; }
- }
-}
diff --git a/src/Microsoft.ML.SearchSpace/Schema/UniformDoubleOptionSchemaV0.cs b/src/Microsoft.ML.SearchSpace/Schema/UniformDoubleOptionSchemaV0.cs
deleted file mode 100644
index 21402fc3a0..0000000000
--- a/src/Microsoft.ML.SearchSpace/Schema/UniformDoubleOptionSchemaV0.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text.Json.Serialization;
-
-namespace Microsoft.ML.SearchSpace.Schema
-{
- internal class UniformDoubleOptionSchemaV0 : SchemaBase
- {
- public override SchemaType SchemaType => SchemaType.UniformDoubleOption;
-
- public override int Version => 0;
-
- [JsonPropertyName("min")]
- public double Min { get; set; }
-
- [JsonPropertyName("max")]
- public double Max { get; set; }
-
- [JsonPropertyName("log_base")]
- public bool? LogBase { get; set; }
- }
-}
diff --git a/src/Microsoft.ML.SearchSpace/SearchSpace.cs b/src/Microsoft.ML.SearchSpace/SearchSpace.cs
index b40651947c..ea29cc06a3 100644
--- a/src/Microsoft.ML.SearchSpace/SearchSpace.cs
+++ b/src/Microsoft.ML.SearchSpace/SearchSpace.cs
@@ -8,13 +8,22 @@
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
+using Microsoft.ML.SearchSpace.Converter;
using Microsoft.ML.SearchSpace.Option;
namespace Microsoft.ML.SearchSpace
{
///
- /// This class is used to represent a set of , which can be either one of , or .
+ /// This class is used to represent a set of , which can be either one of , or another nested search space.
///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [JsonConverter(typeof(SearchSpaceConverter))]
public class SearchSpace : OptionBase, IDictionary
{
private readonly Dictionary _options;
@@ -168,11 +177,11 @@ private Dictionary GetOptionsFromType(Type typeInfo)
}
- private NestOption GetNestOptionFromType(Type typeInfo)
+ private SearchSpace GetSearchSpaceOptionFromType(Type typeInfo)
{
var propertyOptions = GetOptionsFromProperty(typeInfo);
var fieldOptions = GetOptionsFromField(typeInfo);
- var nestOption = new NestOption();
+ var nestOption = new SearchSpace();
foreach (var kv in propertyOptions.Concat(fieldOptions))
{
nestOption[kv.Key] = kv.Value;
@@ -208,7 +217,7 @@ private Dictionary GetOptionsFromField(Type typeInfo)
ChoiceAttribute choice => choice.Option,
RangeAttribute range => range.Option,
BooleanChoiceAttribute booleanChoice => booleanChoice.Option,
- NestOptionAttribute nest => GetNestOptionFromType(field.FieldType),
+ NestOptionAttribute nest => GetSearchSpaceOptionFromType(field.FieldType),
_ => throw new NotImplementedException(),
};
@@ -246,7 +255,7 @@ private Dictionary GetOptionsFromProperty(Type typeInfo)
ChoiceAttribute choice => choice.Option,
RangeAttribute range => range.Option,
BooleanChoiceAttribute booleanChoice => booleanChoice.Option,
- NestOptionAttribute nest => GetNestOptionFromType(property.PropertyType),
+ NestOptionAttribute nest => GetSearchSpaceOptionFromType(property.PropertyType),
_ => throw new NotImplementedException(),
};
@@ -371,6 +380,13 @@ private Parameter Update(Parameter left, Parameter right)
}
///
+ ///
+ ///
+ ///
+ ///
+ ///
public sealed class SearchSpace : SearchSpace
where T : class, new()
{
diff --git a/src/Microsoft.ML.StandardTrainers/LdSvm/LdSvmTrainer.cs b/src/Microsoft.ML.StandardTrainers/LdSvm/LdSvmTrainer.cs
index 538fdce114..d749875d9d 100644
--- a/src/Microsoft.ML.StandardTrainers/LdSvm/LdSvmTrainer.cs
+++ b/src/Microsoft.ML.StandardTrainers/LdSvm/LdSvmTrainer.cs
@@ -13,6 +13,7 @@
using Microsoft.ML.Internal.Utilities;
using Microsoft.ML.Numeric;
using Microsoft.ML.Runtime;
+using Microsoft.ML.SearchSpace;
using Microsoft.ML.Trainers;
[assembly: LoadableClass(LdSvmTrainer.Summary, typeof(LdSvmTrainer), typeof(LdSvmTrainer.Options),
@@ -77,6 +78,7 @@ public sealed class Options : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Depth of Local Deep SVM tree", ShortName = "depth", SortOrder = 50)]
[TGUI(SuggestedSweeps = "1,3,5,7")]
[TlcModule.SweepableDiscreteParam("TreeDepth", new object[] { 1, 3, 5, 7 })]
+ [Range(1, 128, 1, true)]
public int TreeDepth = Defaults.TreeDepth;
///
@@ -85,6 +87,7 @@ public sealed class Options : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Regularizer for classifier parameter W", ShortName = "lw", SortOrder = 50)]
[TGUI(SuggestedSweeps = "0.1,0.01,0.001")]
[TlcModule.SweepableDiscreteParam("LambdaW", new object[] { 0.1f, 0.01f, 0.001f })]
+ [Range(1e-4f, 1f, 1e-4f, true)]
public float LambdaW = Defaults.LambdaW;
///
@@ -93,6 +96,7 @@ public sealed class Options : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Regularizer for kernel parameter Theta", ShortName = "lt", SortOrder = 50)]
[TGUI(SuggestedSweeps = "0.1,0.01,0.001")]
[TlcModule.SweepableDiscreteParam("LambdaTheta", new object[] { 0.1f, 0.01f, 0.001f })]
+ [Range(1e-4f, 1f, 1e-4f, true)]
public float LambdaTheta = Defaults.LambdaTheta;
///
@@ -101,6 +105,7 @@ public sealed class Options : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Regularizer for kernel parameter Thetaprime", ShortName = "lp", SortOrder = 50)]
[TGUI(SuggestedSweeps = "0.1,0.01,0.001")]
[TlcModule.SweepableDiscreteParam("LambdaThetaprime", new object[] { 0.1f, 0.01f, 0.001f })]
+ [Range(1e-4f, 1f, 1e-4f, true)]
public float LambdaThetaprime = Defaults.LambdaThetaprime;
///
@@ -109,6 +114,7 @@ public sealed class Options : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Parameter for sigmoid sharpness", ShortName = "s", SortOrder = 50)]
[TGUI(SuggestedSweeps = "1.0,0.1,0.01")]
[TlcModule.SweepableDiscreteParam("Sigma", new object[] { 1.0f, 0.1f, 0.01f })]
+ [Range(1e-4f, 1f, 1e-4f, true)]
public float Sigma = Defaults.Sigma;
///
@@ -116,6 +122,7 @@ public sealed class Options : TrainerInputBaseWithWeight
///
[Argument(ArgumentType.AtMostOnce, HelpText = "No bias", ShortName = "bias")]
[TlcModule.SweepableDiscreteParam("NoBias", null, isBool: true)]
+ [BooleanChoice(true)]
public bool UseBias = Defaults.UseBias;
///
@@ -125,6 +132,7 @@ public sealed class Options : TrainerInputBaseWithWeight
HelpText = "Number of iterations", ShortName = "iter,NumIterations", SortOrder = 50)]
[TGUI(SuggestedSweeps = "10000,15000")]
[TlcModule.SweepableDiscreteParam("NumIterations", new object[] { 10000, 15000 })]
+ [Range(1, int.MaxValue, 1, true)]
public int NumberOfIterations = Defaults.NumberOfIterations;
[Argument(ArgumentType.AtMostOnce, HelpText = "The calibrator kind to apply to the predictor. Specify null for no calibration", Visibility = ArgumentAttribute.VisibilityType.EntryPointsOnly)]
diff --git a/src/Microsoft.ML.StandardTrainers/Microsoft.ML.StandardTrainers.csproj b/src/Microsoft.ML.StandardTrainers/Microsoft.ML.StandardTrainers.csproj
index f3b20954de..d3a7a06c3c 100644
--- a/src/Microsoft.ML.StandardTrainers/Microsoft.ML.StandardTrainers.csproj
+++ b/src/Microsoft.ML.StandardTrainers/Microsoft.ML.StandardTrainers.csproj
@@ -10,6 +10,10 @@
+
+ all
+ true
+
diff --git a/src/Microsoft.ML.StandardTrainers/Standard/LogisticRegression/LbfgsPredictorBase.cs b/src/Microsoft.ML.StandardTrainers/Standard/LogisticRegression/LbfgsPredictorBase.cs
index a5993b9d85..c5008a76ca 100644
--- a/src/Microsoft.ML.StandardTrainers/Standard/LogisticRegression/LbfgsPredictorBase.cs
+++ b/src/Microsoft.ML.StandardTrainers/Standard/LogisticRegression/LbfgsPredictorBase.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.ML.CommandLine;
using Microsoft.ML.Data;
@@ -12,9 +13,19 @@
using Microsoft.ML.Internal.Utilities;
using Microsoft.ML.Numeric;
using Microsoft.ML.Runtime;
+using Microsoft.ML.SearchSpace;
namespace Microsoft.ML.Trainers
{
+ internal static class OneDalLbfgs
+ {
+ private const string OneDalLibPath = "OneDalNative";
+
+ [DllImport(OneDalLibPath, EntryPoint = "logisticRegressionLBFGSCompute")]
+ public static extern unsafe void LogisticRegressionCompute(void* featuresPtr, void* labelsPtr, void* weightsPtr, bool useSampleWeights, void* betaPtr,
+ long nRows, int nColumns, int nClasses, float l1Reg, float l2Reg, float accuracyThreshold, int nIterations, int m, int nThreads);
+ }
+
///
/// Base class for L-BFGS-based trainers.
///
@@ -34,6 +45,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "L2 regularization weight", ShortName = "l2, L2Weight", SortOrder = 50)]
[TGUI(Label = "L2 Weight", Description = "Weight of L2 regularizer term", SuggestedSweeps = "0,0.1,1")]
[TlcModule.SweepableFloatParamAttribute(0.0f, 1.0f, numSteps: 4)]
+ [Range(0.03125f, 32768f, 1, true)]
public float L2Regularization = Defaults.L2Regularization;
///
@@ -42,6 +54,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "L1 regularization weight", ShortName = "l1, L1Weight", SortOrder = 50)]
[TGUI(Label = "L1 Weight", Description = "Weight of L1 regularizer term", SuggestedSweeps = "0,0.1,1")]
[TlcModule.SweepableFloatParamAttribute(0.0f, 1.0f, numSteps: 4)]
+ [Range(0.03125f, 32768f, 1, true)]
public float L1Regularization = Defaults.L1Regularization;
///
@@ -51,6 +64,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
ShortName = "ot, OptTol", SortOrder = 50)]
[TGUI(Label = "Optimization Tolerance", Description = "Threshold for optimizer convergence", SuggestedSweeps = "1e-4,1e-7")]
[TlcModule.SweepableDiscreteParamAttribute(new object[] { 1e-4f, 1e-7f })]
+ [Range(1e-7f, 1e-1f, 1e-4f, true)]
public float OptimizationTolerance = Defaults.OptimizationTolerance;
///
@@ -59,6 +73,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Memory size for L-BFGS. Low=faster, less accurate", ShortName = "m, MemorySize", SortOrder = 50)]
[TGUI(Description = "Memory size for L-BFGS", SuggestedSweeps = "5,20,50")]
[TlcModule.SweepableDiscreteParamAttribute("MemorySize", new object[] { 5, 20, 50 })]
+ [Range(2, 512, 2, true)]
public int HistorySize = Defaults.HistorySize;
///
@@ -67,6 +82,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Maximum iterations.", ShortName = "maxiter, MaxIterations, NumberOfIterations")]
[TGUI(Label = "Max Number of Iterations")]
[TlcModule.SweepableLongParamAttribute("MaxIterations", 1, int.MaxValue)]
+ [Range(1, int.MaxValue, 1, true)]
public int MaximumNumberOfIterations = Defaults.MaximumNumberOfIterations;
///
@@ -96,6 +112,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.LastOccurrenceWins, HelpText = "Init weights diameter", ShortName = "initwts, InitWtsDiameter", SortOrder = 140)]
[TGUI(Label = "Initial Weights Scale", SuggestedSweeps = "0,0.1,0.5,1")]
[TlcModule.SweepableFloatParamAttribute("InitWtsDiameter", 0.0f, 1.0f, numSteps: 5)]
+ [Range(0f, 1f, 0f, false)]
public float InitialWeightsDiameter = 0;
// Deprecated
@@ -114,12 +131,14 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
///
[Argument(ArgumentType.AtMostOnce, HelpText = "Force densification of the internal optimization vectors", ShortName = "do")]
[TlcModule.SweepableDiscreteParamAttribute("DenseOptimizer", new object[] { false, true })]
+ [BooleanChoice]
public bool DenseOptimizer = false;
///
/// Enforce non-negative weights. Default is false.
///
[Argument(ArgumentType.AtMostOnce, HelpText = "Enforce non-negative weights", ShortName = "nn", SortOrder = 90)]
+ [BooleanChoice]
public bool EnforceNonNegativity = Defaults.EnforceNonNegativity;
[BestFriend]
@@ -429,11 +448,114 @@ private protected override TModel TrainModelCore(TrainContext context)
using (var ch = Host.Start("Training"))
{
- TrainCore(ch, data);
+ if (Environment.GetEnvironmentVariable("MLNET_BACKEND") == "ONEDAL" &&
+ System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture == System.Runtime.InteropServices.Architecture.X64)
+ {
+ TrainCoreOneDal(ch, data);
+ }
+ else
+ {
+ TrainCore(ch, data);
+ }
return CreatePredictor();
}
}
+ private protected virtual void TrainCoreOneDal(IChannel ch, RoleMappedData data)
+ {
+ Host.AssertValue(ch);
+ ch.AssertValue(data);
+
+ int numThreads = !UseThreads ? 1 : (NumThreads ?? Environment.ProcessorCount);
+ ch.Assert(numThreads > 0);
+
+ NumGoodRows = 0;
+ WeightSum = 0;
+
+ _features = null;
+ _labels = null;
+ _weights = null;
+
+ CursOpt cursorOpt = CursOpt.Label | CursOpt.Features;
+ bool useSampleWeights = false;
+ if (data.Schema.Weight.HasValue)
+ {
+ useSampleWeights = true;
+ cursorOpt |= CursOpt.Weight;
+ }
+
+ var typeFeat = data.Schema.Feature.Value.Type as VectorDataViewType;
+ int nFeatures = typeFeat.Size;
+
+ var cursorFactory = new FloatLabelCursor.Factory(data, cursorOpt);
+
+ var labelsList = new List();
+ var featuresList = new List();
+ var weightsList = new List();
+
+ using (var cursor = cursorFactory.Create())
+ {
+ while (cursor.MoveNext())
+ {
+ if (useSampleWeights)
+ {
+ WeightSum += cursor.Weight;
+ weightsList.Add(cursor.Weight);
+ }
+ labelsList.Add((int)cursor.Label);
+ var values = cursor.Features.GetValues();
+ if (cursor.Features.IsDense)
+ {
+ ch.Assert(values.Length == nFeatures);
+
+ for (int j = 0; j < nFeatures; ++j)
+ {
+ featuresList.Add(values[j]);
+ }
+ }
+ else
+ {
+ var indices = cursor.Features.GetIndices();
+ int i = 0;
+ for (int j = 0; j < indices.Length; ++j)
+ {
+ for (int k = i; k < indices[j]; ++k)
+ {
+ featuresList.Add(0);
+ }
+ featuresList.Add(values[indices[j]]);
+ i = indices[j] + 1;
+ }
+ }
+ }
+ NumGoodRows = cursor.KeptRowCount;
+ if (cursor.SkippedRowCount > 0)
+ ch.Warning("Skipped {0} instances with missing features/label/weight during training", cursor.SkippedRowCount);
+ }
+ ch.Check(NumGoodRows > 0, NoTrainingInstancesMessage);
+
+ int[] labelsArray = labelsList.ToArray();
+ float[] featuresArray = featuresList.ToArray();
+ if (!useSampleWeights)
+ {
+ weightsList.Add(1);
+ }
+ float[] weightsArray = weightsList.ToArray();
+ float[] betaArray = new float[WeightCount + BiasCount];
+
+ unsafe
+ {
+#pragma warning disable MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ fixed (void* featuresPtr = &featuresArray[0], labelsPtr = &labelsArray[0], weightsPtr = &weightsArray[0], betaPtr = &betaArray[0])
+#pragma warning restore MSML_SingleVariableDeclaration // Have only a single variable present per declaration
+ {
+ OneDalLbfgs.LogisticRegressionCompute(featuresPtr, labelsPtr, weightsPtr, useSampleWeights, betaPtr, NumGoodRows, nFeatures, ClassCount, L1Weight, L2Weight, OptTol, MaxIterations, MemorySize, numThreads);
+ }
+ }
+
+ CurrentWeights = new VBuffer(betaArray.Length, betaArray);
+ }
+
private protected virtual void TrainCore(IChannel ch, RoleMappedData data)
{
Host.AssertValue(ch);
diff --git a/src/Microsoft.ML.StandardTrainers/Standard/Online/AveragedLinear.cs b/src/Microsoft.ML.StandardTrainers/Standard/Online/AveragedLinear.cs
index 6197e0b4c5..e666caca3e 100644
--- a/src/Microsoft.ML.StandardTrainers/Standard/Online/AveragedLinear.cs
+++ b/src/Microsoft.ML.StandardTrainers/Standard/Online/AveragedLinear.cs
@@ -10,6 +10,7 @@
using Microsoft.ML.Internal.Utilities;
using Microsoft.ML.Numeric;
using Microsoft.ML.Runtime;
+using Microsoft.ML.SearchSpace;
// TODO: Check if it works properly if Averaged is set to false
@@ -26,6 +27,7 @@ public abstract class AveragedLinearOptions : OnlineLinearOptions
[Argument(ArgumentType.AtMostOnce, HelpText = "Learning rate", ShortName = "lr", SortOrder = 50)]
[TGUI(Label = "Learning rate", SuggestedSweeps = "0.01,0.1,0.5,1.0")]
[TlcModule.SweepableDiscreteParam("LearningRate", new object[] { 0.01, 0.1, 0.5, 1.0 })]
+ [Range(1e-4f, 1f, 1f, true)]
public float LearningRate = AveragedDefault.LearningRate;
///
@@ -38,6 +40,7 @@ public abstract class AveragedLinearOptions : OnlineLinearOptions
[Argument(ArgumentType.AtMostOnce, HelpText = "Decrease learning rate", ShortName = "decreaselr", SortOrder = 50)]
[TGUI(Label = "Decrease Learning Rate", Description = "Decrease learning rate as iterations progress")]
[TlcModule.SweepableDiscreteParam("DecreaseLearningRate", new object[] { false, true })]
+ [BooleanChoice]
public bool DecreaseLearningRate = AveragedDefault.DecreaseLearningRate;
///
@@ -66,6 +69,7 @@ public abstract class AveragedLinearOptions : OnlineLinearOptions
[Argument(ArgumentType.AtMostOnce, HelpText = "L2 Regularization Weight", ShortName = "reg,L2RegularizerWeight", SortOrder = 50)]
[TGUI(Label = "L2 Regularization Weight")]
[TlcModule.SweepableFloatParam("L2RegularizerWeight", 0.0f, 0.4f)]
+ [Range(0f, 32768f, 0f, false)]
public float L2Regularization = AveragedDefault.L2Regularization;
///
diff --git a/src/Microsoft.ML.StandardTrainers/Standard/Online/LinearSvm.cs b/src/Microsoft.ML.StandardTrainers/Standard/Online/LinearSvm.cs
index 1991df054a..2776eb94d3 100644
--- a/src/Microsoft.ML.StandardTrainers/Standard/Online/LinearSvm.cs
+++ b/src/Microsoft.ML.StandardTrainers/Standard/Online/LinearSvm.cs
@@ -13,6 +13,7 @@
using Microsoft.ML.Model;
using Microsoft.ML.Numeric;
using Microsoft.ML.Runtime;
+using Microsoft.ML.SearchSpace;
using Microsoft.ML.Trainers;
[assembly: LoadableClass(LinearSvmTrainer.Summary, typeof(LinearSvmTrainer), typeof(LinearSvmTrainer.Options),
@@ -82,18 +83,22 @@ public sealed class Options : OnlineLinearOptions
[Argument(ArgumentType.AtMostOnce, HelpText = "Regularizer constant", ShortName = "lambda", SortOrder = 50)]
[TGUI(SuggestedSweeps = "0.00001-0.1;log;inc:10")]
[TlcModule.SweepableFloatParamAttribute("Lambda", 0.00001f, 0.1f, 10, isLogScale: true)]
+ [Range(1e-6f, 1f, 1e-4f, true)]
public float Lambda = 0.001f;
[Argument(ArgumentType.AtMostOnce, HelpText = "Batch size", ShortName = "batch", SortOrder = 190)]
[TGUI(Label = "Batch Size")]
+ [Range(1, 128, 1, true)]
public int BatchSize = 1;
[Argument(ArgumentType.AtMostOnce, HelpText = "Perform projection to unit-ball? Typically used with batch size > 1.", ShortName = "project", SortOrder = 50)]
[TlcModule.SweepableDiscreteParam("PerformProjection", null, isBool: true)]
+ [BooleanChoice(defaultValue: false)]
public bool PerformProjection = false;
[Argument(ArgumentType.AtMostOnce, HelpText = "No bias")]
[TlcModule.SweepableDiscreteParam("NoBias", null, isBool: true)]
+ [BooleanChoice(defaultValue: false)]
public bool NoBias = false;
[Argument(ArgumentType.AtMostOnce, HelpText = "The calibrator kind to apply to the predictor. Specify null for no calibration", Visibility = ArgumentAttribute.VisibilityType.EntryPointsOnly)]
diff --git a/src/Microsoft.ML.StandardTrainers/Standard/Online/OnlineLinear.cs b/src/Microsoft.ML.StandardTrainers/Standard/Online/OnlineLinear.cs
index 9efe9d72f8..7bfd2e2afa 100644
--- a/src/Microsoft.ML.StandardTrainers/Standard/Online/OnlineLinear.cs
+++ b/src/Microsoft.ML.StandardTrainers/Standard/Online/OnlineLinear.cs
@@ -12,6 +12,7 @@
using Microsoft.ML.Model;
using Microsoft.ML.Numeric;
using Microsoft.ML.Runtime;
+using Microsoft.ML.SearchSpace;
namespace Microsoft.ML.Trainers
{
@@ -26,6 +27,7 @@ public abstract class OnlineLinearOptions : TrainerInputBaseWithLabel
[Argument(ArgumentType.AtMostOnce, HelpText = "Number of iterations", ShortName = "iter,numIterations", SortOrder = 50)]
[TGUI(Label = "Number of Iterations", Description = "Number of training iterations through data", SuggestedSweeps = "1,10,100")]
[TlcModule.SweepableLongParamAttribute("NumIterations", 1, 100, stepSize: 10, isLogScale: true)]
+ [Range(1, 512, 1, true)]
public int NumberOfIterations = OnlineDefault.NumberOfIterations;
///
@@ -45,6 +47,7 @@ public abstract class OnlineLinearOptions : TrainerInputBaseWithLabel
[Argument(ArgumentType.AtMostOnce, HelpText = "Init weights diameter", ShortName = "initwts,initWtsDiameter", SortOrder = 140)]
[TGUI(Label = "Initial Weights Scale", SuggestedSweeps = "0,0.1,0.5,1")]
[TlcModule.SweepableFloatParamAttribute("InitWtsDiameter", 0.0f, 1.0f, numSteps: 5)]
+ [Range(0f, 1f, 0f, false)]
public float InitialWeightsDiameter = 0;
///
diff --git a/src/Microsoft.ML.StandardTrainers/Standard/SdcaBinary.cs b/src/Microsoft.ML.StandardTrainers/Standard/SdcaBinary.cs
index a1e16ad283..d19bfc8c42 100644
--- a/src/Microsoft.ML.StandardTrainers/Standard/SdcaBinary.cs
+++ b/src/Microsoft.ML.StandardTrainers/Standard/SdcaBinary.cs
@@ -19,6 +19,7 @@
using Microsoft.ML.Model;
using Microsoft.ML.Numeric;
using Microsoft.ML.Runtime;
+using Microsoft.ML.SearchSpace;
using Microsoft.ML.Trainers;
using Microsoft.ML.Transforms;
@@ -162,6 +163,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "L2 regularizer constant. By default the l2 constant is automatically inferred based on data set.", NullName = "", ShortName = "l2, L2Const", SortOrder = 1)]
[TGUI(Label = "L2 Regularizer Constant", SuggestedSweeps = ",1e-7,1e-6,1e-5,1e-4,1e-3,1e-2")]
[TlcModule.SweepableDiscreteParam("L2Const", new object[] { "", 1e-7f, 1e-6f, 1e-5f, 1e-4f, 1e-3f, 1e-2f })]
+ [Range(1e-7f, 32768f, 1e-7f, true)]
public float? L2Regularization;
// REVIEW: make the default positive when we know how to consume a sparse model
@@ -172,6 +174,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
NullName = "", Name = "L1Threshold", ShortName = "l1", SortOrder = 2)]
[TGUI(Label = "L1 Soft Threshold", SuggestedSweeps = ",0,0.25,0.5,0.75,1")]
[TlcModule.SweepableDiscreteParam("L1Threshold", new object[] { "", 0f, 0.25f, 0.5f, 0.75f, 1f })]
+ [Range(1e-7f, 32768f, 1e-7f, true)]
public float? L1Regularization;
///
@@ -190,6 +193,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "The tolerance for the ratio between duality gap and primal loss for convergence checking.", ShortName = "tol")]
[TGUI(SuggestedSweeps = "0.001, 0.01, 0.1, 0.2")]
[TlcModule.SweepableDiscreteParam("ConvergenceTolerance", new object[] { 0.001f, 0.01f, 0.1f, 0.2f })]
+ [Range(1e-7f, 1f, 1e-7f, true)]
public float ConvergenceTolerance = 0.1f;
///
@@ -201,6 +205,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Maximum number of iterations; set to 1 to simulate online learning. Defaults to automatic.", NullName = "", ShortName = "iter, MaxIterations, NumberOfIterations")]
[TGUI(Label = "Max number of iterations", SuggestedSweeps = ",10,20,100")]
[TlcModule.SweepableDiscreteParam("MaxIterations", new object[] { "", 10, 20, 100 })]
+ [Range(10, 1000, 10, true)]
public int? MaximumNumberOfIterations;
///
@@ -229,6 +234,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "The learning rate for adjusting bias from being regularized.", ShortName = "blr")]
[TGUI(SuggestedSweeps = "0, 0.01, 0.1, 1")]
[TlcModule.SweepableDiscreteParam("BiasLearningRate", new object[] { 0.0f, 0.01f, 0.1f, 1f })]
+ [Range(1e-7f, 1f, 1e-7f, true)]
public float BiasLearningRate = 0;
internal virtual void Check(IHostEnvironment env)
@@ -1834,6 +1840,7 @@ public class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "L2 Regularization constant", ShortName = "l2, L2Weight", SortOrder = 50)]
[TGUI(Label = "L2 Regularization Constant", SuggestedSweeps = "1e-7,5e-7,1e-6,5e-6,1e-5")]
[TlcModule.SweepableDiscreteParam("L2Const", new object[] { 1e-7f, 5e-7f, 1e-6f, 5e-6f, 1e-5f })]
+ [Range((float)0.03125, (float)32768, init: (float)1F, logBase: true)]
public float L2Regularization = Defaults.L2Regularization;
///
@@ -1853,6 +1860,7 @@ public class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Exponential moving averaged improvement tolerance for convergence", ShortName = "tol")]
[TGUI(SuggestedSweeps = "1e-2,1e-3,1e-4,1e-5")]
[TlcModule.SweepableDiscreteParam("ConvergenceTolerance", new object[] { 1e-2f, 1e-3f, 1e-4f, 1e-5f })]
+ [Range(1e-6, 1e-1, init: 1e-1, logBase: true)]
public double ConvergenceTolerance = 1e-4;
///
@@ -1864,6 +1872,7 @@ public class OptionsBase : TrainerInputBaseWithWeight
[Argument(ArgumentType.AtMostOnce, HelpText = "Maximum number of iterations; set to 1 to simulate online learning.", ShortName = "iter, MaxIterations")]
[TGUI(Label = "Max number of iterations", SuggestedSweeps = "1,5,10,20")]
[TlcModule.SweepableDiscreteParam("MaxIterations", new object[] { 1, 5, 10, 20 })]
+ [Range((int)1, 20, init: 1, logBase: true)]
public int NumberOfIterations = Defaults.NumberOfIterations;
///
@@ -1871,6 +1880,7 @@ public class OptionsBase : TrainerInputBaseWithWeight
///
[Argument(ArgumentType.AtMostOnce, HelpText = "Initial learning rate (only used by SGD)", Name = "InitialLearningRate", ShortName = "ilr,lr,InitLearningRate")]
[TGUI(Label = "Initial Learning Rate (for SGD)")]
+ [Range(1e-3, 1, init: 1e-3, logBase: true)]
public double LearningRate = Defaults.LearningRate;
///
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/Anchors.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/Anchors.cs
new file mode 100644
index 0000000000..fdcbc070c8
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/Anchors.cs
@@ -0,0 +1,153 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using TorchSharp;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// Anchor boxes are a set of predefined bounding boxes of a certain height and width, whose location and size can be adjusted by the regression head of model.
+ ///
+ public class Anchors : Module
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly int[] pyramidLevels;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly int[] strides;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly int[] sizes;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly double[] ratios;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly double[] scales;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Pyramid levels.
+ /// Strides between adjacent bboxes.
+ /// Different sizes for bboxes.
+ /// Different ratios for height/width.
+ /// Scale size of bboxes.
+ public Anchors(int[] pyramidLevels = null, int[] strides = null, int[] sizes = null, double[] ratios = null, double[] scales = null)
+ : base(nameof(Anchors))
+ {
+ this.pyramidLevels = pyramidLevels != null ? pyramidLevels : new int[] { 3, 4, 5, 6, 7 };
+ this.strides = strides != null ? strides : this.pyramidLevels.Select(x => (int)Math.Pow(2, x)).ToArray();
+ this.sizes = sizes != null ? sizes : this.pyramidLevels.Select(x => (int)Math.Pow(2, x + 2)).ToArray();
+ this.ratios = ratios != null ? ratios : new double[] { 0.5, 1, 2 };
+ this.scales = scales != null ? scales : new double[] { Math.Pow(2, 0), Math.Pow(2, 1.0 / 3.0), Math.Pow(2, 2.0 / 3.0) };
+ }
+
+ ///
+ /// Generate anchors for an image.
+ ///
+ /// Image in Tensor format.
+ /// All anchors.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor image)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ var imageShape = torch.tensor(image.shape.AsSpan().Slice(2).ToArray());
+
+ // compute anchors over all pyramid levels
+ var allAnchors = torch.zeros(new long[] { 0, 4 }, dtype: torch.float32);
+
+ for (int idx = 0; idx < this.pyramidLevels.Length; ++idx)
+ {
+ var x = this.pyramidLevels[idx];
+ var shape = ((imageShape + Math.Pow(2, x) - 1) / Math.Pow(2, x)).to_type(torch.int32);
+ var anchors = GenerateAnchors(
+ baseSize: this.sizes[idx],
+ ratios: this.ratios,
+ scales: this.scales);
+ var shiftedAnchors = Shift(shape, this.strides[idx], anchors);
+ allAnchors = torch.cat(new List() { allAnchors, shiftedAnchors }, dim: 0);
+ }
+
+ var output = allAnchors.unsqueeze(dim: 0);
+ output = output.to(image.device);
+
+ return output.MoveToOuterDisposeScope();
+ }
+ }
+
+ ///
+ /// Generate a set of anchors given size, ratios and scales.
+ ///
+ /// Base size for width and height.
+ /// Ratios for height/width.
+ /// Scales to resize base size.
+ /// A set of anchors.
+ private static Tensor GenerateAnchors(int baseSize = 16, double[] ratios = null, double[] scales = null)
+ {
+ using (var anchorsScope = torch.NewDisposeScope())
+ {
+ ratios ??= new double[] { 0.5, 1, 2 };
+ scales ??= new double[] { Math.Pow(2, 0), Math.Pow(2, 1.0 / 3.0), Math.Pow(2, 2.0 / 3.0) };
+
+ var numAnchors = ratios.Length * scales.Length;
+
+ // initialize output anchors
+ var anchors = torch.zeros(new long[] { numAnchors, 4 }, dtype: torch.float32);
+
+ // scale base_size
+ anchors[.., 2..] = baseSize * torch.tile(scales, new long[] { 2, ratios.Length }).transpose(1, 0);
+
+ // compute areas of anchors
+ var areas = torch.mul(anchors[.., 2], anchors[.., 3]);
+
+ // correct for ratios
+ anchors[.., 2] = torch.sqrt(areas / torch.repeat_interleave(ratios, new long[] { scales.Length }));
+ anchors[.., 3] = torch.mul(anchors[.., 2], torch.repeat_interleave(ratios, new long[] { scales.Length }));
+
+ // transform from (x_ctr, y_ctr, w, h) -> (x1, y1, x2, y2)
+ anchors[.., torch.TensorIndex.Tensor(torch.tensor(new long[] { 0, 2 }, dtype: torch.int64))] -= torch.tile(anchors[.., 2] * 0.5, new long[] { 2, 1 }).T;
+ anchors[.., torch.TensorIndex.Tensor(torch.tensor(new long[] { 1, 3 }, dtype: torch.int64))] -= torch.tile(anchors[.., 3] * 0.5, new long[] { 2, 1 }).T;
+
+ return anchors.MoveToOuterDisposeScope();
+ }
+ }
+
+ ///
+ /// Duplicate and distribute anchors to different positions give border of positions and stride between positions.
+ ///
+ /// Border to distribute anchors.
+ /// Stride between adjacent anchors.
+ /// Anchors to distribute.
+ /// The shifted anchors.
+ private static Tensor Shift(Tensor shape, int stride, Tensor anchors)
+ {
+ using (var anchorsScope = torch.NewDisposeScope())
+ {
+ Tensor shiftX = (torch.arange(start: 0, stop: (int)shape[1]) + 0.5) * stride;
+ Tensor shiftY = (torch.arange(start: 0, stop: (int)shape[0]) + 0.5) * stride;
+
+ var shiftXExpand = torch.repeat_interleave(shiftX.reshape(new long[] { shiftX.shape[0], 1 }), shiftY.shape[0], dim: 1);
+ shiftXExpand = shiftXExpand.transpose(0, 1).reshape(-1);
+ var shiftYExpand = torch.repeat_interleave(shiftY, shiftX.shape[0]);
+
+ List tensors = new List { shiftXExpand, shiftYExpand, shiftXExpand, shiftYExpand };
+ var shifts = torch.vstack(tensors).transpose(0, 1);
+
+ var a = anchors.shape[0];
+ var k = shifts.shape[0];
+ var allAnchors = anchors.reshape(new long[] { 1, a, 4 }) + shifts.reshape(new long[] { 1, k, 4 }).transpose(0, 1);
+ allAnchors = allAnchors.reshape(new long[] { k * a, 4 });
+
+ return allAnchors.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/Attention.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/Attention.cs
new file mode 100644
index 0000000000..c7f6e11318
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/Attention.cs
@@ -0,0 +1,135 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The Attention layer.
+ ///
+ public class Attention : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly int numHeads;
+ private readonly double scale;
+ private readonly int keyChannels;
+ private readonly int nHkD;
+ private readonly int d;
+ private readonly int dh;
+ private readonly double attnRatio;
+
+ private readonly LayerNorm norm;
+ private readonly Linear qkv;
+ private readonly Linear proj;
+ private readonly Parameter attention_biases;
+ private readonly TensorIndex attention_bias_idxs;
+ private readonly Softmax softmax;
+#pragma warning restore MSML_PrivateFieldName
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The key channels.
+ /// The number of blocks.
+ /// The ratio of attention.
+ /// The resolution of window.
+ public Attention(int inChannels, int keyChannels, int numHeads = 8, int attnRatio = 4, List windowResolution = null)
+ : base(nameof(Attention))
+ {
+ windowResolution ??= new List() { 14, 14 };
+ this.numHeads = numHeads;
+ this.scale = System.Math.Pow(keyChannels, -0.5);
+ this.keyChannels = keyChannels;
+ this.nHkD = numHeads * keyChannels;
+ this.d = attnRatio * keyChannels;
+ this.dh = this.d * numHeads;
+ this.attnRatio = attnRatio;
+ int h = this.dh + (this.nHkD * 2);
+
+ this.norm = nn.LayerNorm(new long[] { inChannels });
+ this.qkv = nn.Linear(inChannels, h);
+ this.proj = nn.Linear(this.dh, inChannels);
+
+ var points = new List>();
+ for (int i = 0; i < windowResolution[0]; i++)
+ {
+ for (int j = 0; j < windowResolution[1]; j++)
+ {
+ points.Add(new List() { i, j });
+ }
+ }
+
+ int n = points.Count;
+ var attentionOffsets = new Dictionary, int>();
+ var idxs = new List();
+ var idxsTensor = torch.zeros(new long[] { n, n }, dtype: torch.int64);
+ for (int i = 0; i < n; i++)
+ {
+ for (int j = 0; j < n; j++)
+ {
+ var offset = new Tuple(Math.Abs(points[i][0] - points[j][0]), Math.Abs(points[i][1] - points[j][1]));
+ if (!attentionOffsets.ContainsKey(offset))
+ {
+ attentionOffsets.Add(offset, attentionOffsets.Count);
+ }
+
+ idxs.Add(attentionOffsets[offset]);
+ idxsTensor[i][j] = attentionOffsets[offset];
+ }
+ }
+
+ this.attention_biases = nn.Parameter(torch.zeros(numHeads, attentionOffsets.Count));
+ this.attention_bias_idxs = TensorIndex.Tensor(idxsTensor);
+ this.softmax = nn.Softmax(dim: -1);
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor x, Tensor mask)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ long b = x.shape[0];
+ long n = x.shape[1];
+ long c = x.shape[2];
+ x = this.norm.forward(x);
+ var qkv = this.qkv.forward(x);
+ qkv = qkv.view(b, n, this.numHeads, -1);
+ var tmp = qkv.split(new long[] { this.keyChannels, this.keyChannels, this.d }, dim: 3);
+ var q = tmp[0];
+ var k = tmp[1];
+ var v = tmp[2];
+ q = q.permute(0, 2, 1, 3);
+ k = k.permute(0, 2, 1, 3);
+ v = v.permute(0, 2, 1, 3);
+
+ var attn = (torch.matmul(q, k.transpose(-2, -1)) * this.scale) + this.attention_biases[.., this.attention_bias_idxs];
+ if (!(mask is null))
+ {
+ long nW = mask.shape[0];
+ attn = attn.view(-1, nW, this.numHeads, n, n) + mask.unsqueeze(1).unsqueeze(0);
+ attn = attn.view(-1, this.numHeads, n, n);
+ attn = this.softmax.forward(attn);
+ }
+ else
+ {
+ attn = this.softmax.forward(attn);
+ }
+
+ x = torch.matmul(attn, v).transpose(1, 2).reshape(b, n, this.dh);
+ x = this.proj.forward(x);
+
+ return x.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoFormerV2Backbone.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoFormerV2Backbone.cs
new file mode 100644
index 0000000000..25f622908e
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoFormerV2Backbone.cs
@@ -0,0 +1,159 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The backbone of AutoFormerV2 object detection network.
+ ///
+ public class AutoFormerV2Backbone : Module>
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly List outIndices;
+ private readonly List numFeatures;
+ private readonly PatchEmbed patch_embed;
+ private readonly ModuleList> layers;
+ private readonly LayerNorm norm1;
+ private readonly LayerNorm norm2;
+ private readonly LayerNorm norm3;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The embedding channels.
+ /// The number of blocks in each layer.
+ /// The number of heads in BasicLayer.
+ /// The sizes of window.
+ /// The ratio of MLP.
+ /// The ratio of drop.
+ /// The expand ratio of MBConv.
+ /// The indices of output.
+ /// Whether use shift window.
+ /// Whether use interpolation.
+ /// The channels of each outputs.
+ public AutoFormerV2Backbone(
+ int inChannels = 3,
+ List embedChannels = null,
+ List depths = null,
+ List numHeads = null,
+ List windowSizes = null,
+ double mlpRatio = 4.0,
+ double dropRate = 0.0,
+ double mbconvExpandRatio = 4.0,
+ List outIndices = null,
+ bool useShiftWindow = true,
+ bool useInterpolate = false,
+ List outChannels = null)
+ : base(nameof(AutoFormerV2Backbone))
+ {
+ embedChannels ??= new List() { 96, 192, 384, 576 };
+ depths ??= new List() { 2, 2, 6, 2 };
+ numHeads ??= new List() { 3, 6, 12, 18 };
+ windowSizes ??= new List() { 7, 7, 14, 7 };
+ outIndices ??= new List() { 1, 2, 3 };
+ outChannels ??= embedChannels;
+
+ this.outIndices = outIndices;
+ this.numFeatures = outChannels;
+
+ this.patch_embed = new PatchEmbed(inChannels: inChannels, embedChannels: embedChannels[0]);
+
+ var dpr = new List();
+ int depthSum = 0;
+ foreach (int depth in depths)
+ {
+ depthSum += depth;
+ }
+
+ for (int i = 0; i < depthSum; i++)
+ {
+ dpr.Add(0.0); // different from original AutoFormer, but ok with current model
+ }
+
+ this.layers = new ModuleList>();
+ this.layers.Add(new ConvLayer(
+ inChannels: embedChannels[0],
+ outChannels: embedChannels[1],
+ depth: depths[0],
+ convExpandRatio: mbconvExpandRatio));
+ for (int iLayer = 1; iLayer < depths.Count; iLayer++)
+ {
+ this.layers.Add(new BasicLayer(
+ inChannels: embedChannels[iLayer],
+ outChannels: embedChannels[Math.Min(iLayer + 1, embedChannels.Count - 1)],
+ depth: depths[iLayer],
+ numHeads: numHeads[iLayer],
+ windowSize: windowSizes[iLayer],
+ mlpRatio: mlpRatio,
+ dropRatio: dropRate,
+ localConvSize: 3,
+ useShiftWindow: useShiftWindow,
+ useInterpolate: useInterpolate));
+ }
+
+ this.norm1 = nn.LayerNorm(new long[] { outChannels[1] });
+ this.norm2 = nn.LayerNorm(new long[] { outChannels[2] });
+ this.norm3 = nn.LayerNorm(new long[] { outChannels[3] });
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override List forward(Tensor imgBatch)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ var x = this.patch_embed.forward(imgBatch);
+ var b = (int)x.shape[0];
+ var c = (int)x.shape[1];
+ var wh = (int)x.shape[2];
+ var ww = (int)x.shape[3];
+ var outs = new List();
+ Tensor xOut;
+ int h;
+ int w;
+ (xOut, h, w, x, wh, ww) = this.layers[0].forward(x, wh, ww);
+
+ for (int iLayer = 1; iLayer < this.layers.Count; iLayer++)
+ {
+
+ (xOut, h, w, x, wh, ww) = this.layers[iLayer].forward(x, wh, ww);
+
+ if (this.outIndices.Contains(iLayer))
+ {
+ switch (iLayer)
+ {
+ case 1:
+ xOut = this.norm1.forward(xOut);
+ break;
+ case 2:
+ xOut = this.norm2.forward(xOut);
+ break;
+ case 3:
+ xOut = this.norm3.forward(xOut);
+ break;
+ default:
+ break;
+ }
+
+ long n = xOut.shape[0];
+ var res = xOut.view(n, h, w, this.numFeatures[iLayer]).permute(0, 3, 1, 2).contiguous();
+ res = res.MoveToOuterDisposeScope();
+ outs.Add(res);
+ }
+ }
+
+ return outs;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoFormerV2Block.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoFormerV2Block.cs
new file mode 100644
index 0000000000..1c1cf99179
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoFormerV2Block.cs
@@ -0,0 +1,186 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using TorchSharp;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The block module of AutoFormer network.
+ ///
+ public class AutoFormerV2Block : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly int windowSize;
+ private readonly int shiftSize;
+ private readonly bool useShiftWindow;
+ private readonly bool useInterpolate;
+ private readonly Attention attn;
+ private readonly MLP mlp;
+ private readonly Conv2dBN local_conv;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The number of blocks.
+ /// The size of window.
+ /// The size of shift.
+ /// The ratio of MLP.
+ /// The ratio of drop.
+ /// The size of local convolution.
+ /// Whether use shift window.
+ /// Whether use interpolation.
+ public AutoFormerV2Block(int inChannels, int numHeads, int windowSize = 7, int shiftSize = 0, double mlpRatio = 4.0, double dropRatio = 0, int localConvSize = 3, bool useShiftWindow = false, bool useInterpolate = false)
+ : base(nameof(AutoFormerV2Block))
+ {
+ this.windowSize = windowSize;
+ if (useShiftWindow)
+ {
+ this.shiftSize = shiftSize;
+ }
+ else
+ {
+ this.shiftSize = 0;
+ }
+
+ this.useShiftWindow = useShiftWindow;
+ this.useInterpolate = useInterpolate;
+
+ int headChannels = inChannels / numHeads;
+ List windowResolution = new List() { windowSize, windowSize };
+ this.attn = new Attention(inChannels, headChannels, numHeads, attnRatio: 1, windowResolution: windowResolution);
+
+ int mlpHiddenChannels = (int)(inChannels * mlpRatio);
+ this.mlp = new MLP(inFeatures: inChannels, hiddenFeatures: mlpHiddenChannels, dropRatio: dropRatio);
+
+ int padding = localConvSize / 2;
+ this.local_conv = new Conv2dBN(inChannels, inChannels, kernalSize: localConvSize, stride: 1, padding: padding, groups: inChannels);
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor x, int h, int w, Tensor maskMatrix)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ long b = x.shape[0];
+ long l = x.shape[1];
+ long c = x.shape[2];
+ var resX = x;
+ x = x.view(b, h, w, c);
+ int padB = (this.windowSize - (h % this.windowSize)) % this.windowSize;
+ int padR = (this.windowSize - (w % this.windowSize)) % this.windowSize;
+ bool padding = false;
+ if (padB > 0 || padR > 0)
+ {
+ padding = true;
+ }
+
+ int pH = h + padB;
+ int pW = w + padR;
+ if (padding)
+ {
+ x = nn.functional.pad(x, new long[] { 0, 0, 0, padR, 0, padB });
+ }
+
+ Tensor shiftedX;
+ Tensor attnMask;
+ if (this.useShiftWindow && this.shiftSize > 0)
+ {
+ shiftedX = torch.roll(x, shifts: new long[] { -this.shiftSize, -this.shiftSize }, dims: new long[] { 1, 2 });
+ attnMask = maskMatrix;
+ }
+ else
+ {
+ shiftedX = x;
+ attnMask = null;
+ }
+
+ var xWindows = WindowPartition(shiftedX, this.windowSize);
+ xWindows = xWindows.view(-1, this.windowSize * this.windowSize, c);
+ var attnWindows = this.attn.forward(xWindows, mask: attnMask);
+
+ attnWindows = attnWindows.view(-1, this.windowSize, this.windowSize, c);
+ shiftedX = WindowsReverse(attnWindows, this.windowSize, pH, pW);
+
+ if (this.useShiftWindow && this.shiftSize > 0)
+ {
+ x = torch.roll(shiftedX, shifts: new long[] { this.shiftSize, this.shiftSize }, dims: new long[] { 1, 2 });
+ }
+ else
+ {
+ x = shiftedX;
+ }
+
+ if (padding)
+ {
+ if (this.useInterpolate)
+ {
+ x = nn.functional.interpolate(x.permute(0, 3, 1, 2), size: new long[] { h, w }, mode: torch.InterpolationMode.Bilinear, align_corners: true).permute(0, 2, 3, 1);
+ }
+ else
+ {
+ x = x[.., ..h, ..w].contiguous();
+ }
+ }
+
+ x = x.view(b, l, c);
+
+ x = resX + x;
+ x = x.transpose(1, 2).reshape(b, c, h, w);
+ x = this.local_conv.forward(x);
+ x = x.view(b, c, l).transpose(1, 2);
+ x = x + this.mlp.forward(x);
+
+ return x.MoveToOuterDisposeScope();
+ }
+ }
+
+ ///
+ /// Reverse input in window size to original shape.
+ ///
+ /// The input window tensor.
+ /// The size of window.
+ /// The height.
+ /// The width.
+ /// The reversed window tensor.
+ private static Tensor WindowsReverse(Tensor windows, int windowSize, int h, int w)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ int b = (int)windows.shape[0] / (h * w / windowSize / windowSize);
+ var x = windows.view(b, h / windowSize, w / windowSize, windowSize, windowSize, -1);
+ x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(b, h, w, -1);
+
+ return x.MoveToOuterDisposeScope();
+ }
+ }
+
+ ///
+ /// Partition input to window size.
+ ///
+ /// The input tensor.
+ /// The size of window.
+ /// The partition window.
+ private static Tensor WindowPartition(Tensor x, int windowSize)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ long b = x.shape[0];
+ long h = x.shape[1];
+ long w = x.shape[2];
+ long c = x.shape[3];
+ x = x.view(b, h / windowSize, windowSize, w / windowSize, windowSize, c);
+ var windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, windowSize, windowSize, c);
+
+ return windows.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoformerV2.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoformerV2.cs
new file mode 100644
index 0000000000..bf9232a69f
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/AutoformerV2.cs
@@ -0,0 +1,138 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The object detection network based on AutoFormerV2 backbone which is pretrained only in MS COCO dataset.
+ /// The network contains 3 scales with different parameters: small for 11MB, medium for 21MB and large for 41MB.
+ ///
+ public class AutoFormerV2 : Module
+ {
+
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly Device device;
+ private readonly AutoFormerV2Backbone backbone;
+ private readonly FPN neck;
+ private readonly RetinaHead bbox_head;
+ private readonly Anchors anchors;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The number of object categories.
+ /// The embedding channels, which control the scale of model.
+ /// The number of blocks, which control the scale of model.
+ /// The number of heads, which control the scale of model.
+ /// The device where the model inference.
+ public AutoFormerV2(int numClasses, List embedChannels, List depths, List numHeads, Device device = null)
+ : base(nameof(AutoFormerV2))
+ {
+ this.device = device;
+
+ this.backbone = new AutoFormerV2Backbone(embedChannels: embedChannels, depths: depths, numHeads: numHeads);
+ this.neck = new FPN(inChannels: new List() { embedChannels[1], embedChannels[2], embedChannels[3] });
+ this.bbox_head = new RetinaHead(numClasses);
+ this.anchors = new Anchors();
+
+ this.RegisterComponents();
+ this.InitializeWeight();
+ this.FreezeBN();
+
+ if (device != null)
+ {
+ this.to(device);
+ }
+
+ this.eval();
+ }
+
+ ///
+ /// Freeze the weight of BatchNorm2d in network.
+ ///
+ public void FreezeBN()
+ {
+ foreach (var (name, layer) in this.named_modules())
+ {
+ if (layer.GetType() == typeof(BatchNorm2d))
+ {
+ layer.eval();
+ }
+ }
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override (Tensor, Tensor, Tensor) forward(Tensor imgBatch)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ imgBatch = imgBatch.to(this.device);
+ var xList = this.backbone.forward(imgBatch);
+ var fpnOutput = this.neck.forward(xList);
+ var (classificationInput, regressionInput) = this.bbox_head.forward(fpnOutput);
+ var regression = torch.cat(regressionInput, dim: 1);
+ var classification = torch.cat(classificationInput, dim: 1);
+ var anchor = this.anchors.forward(imgBatch);
+
+ return (classification.MoveToOuterDisposeScope(),
+ regression.MoveToOuterDisposeScope(),
+ anchor.MoveToOuterDisposeScope());
+ }
+ }
+
+ ///
+ /// Initialize weight of layers in network.
+ ///
+ private void InitializeWeight()
+ {
+ foreach (var (name, layer) in this.named_modules())
+ {
+ if (layer.GetType() == typeof(Linear))
+ {
+ var module = layer as Linear;
+ var weightRequiresGrad = module.weight.requires_grad;
+ module.weight.requires_grad = false;
+ module.weight.normal_(0, 0.02);
+ module.weight.requires_grad = weightRequiresGrad;
+ var biasRequiresGrad = module.bias.requires_grad;
+ module.bias.requires_grad = false;
+ module.bias.zero_();
+ module.bias.requires_grad = biasRequiresGrad;
+ }
+ else if (layer.GetType() == typeof(LayerNorm))
+ {
+ var module = layer as LayerNorm;
+ var weightRequiresGrad = module.weight.requires_grad;
+ module.weight.requires_grad = false;
+ module.weight.fill_(1);
+ module.weight.requires_grad = weightRequiresGrad;
+ var biasRequiresGrad = module.bias.requires_grad;
+ module.bias.requires_grad = false;
+ module.bias.zero_();
+ module.bias.requires_grad = biasRequiresGrad;
+ }
+ else if (layer.GetType() == typeof(BatchNorm2d))
+ {
+ var module = layer as BatchNorm2d;
+ var weightRequiresGrad = module.weight.requires_grad;
+ module.weight.requires_grad = false;
+ module.weight.fill_(1.0);
+ module.weight.requires_grad = weightRequiresGrad;
+ var biasRequiresGrad = module.bias.requires_grad;
+ module.bias.requires_grad = false;
+ module.bias.zero_();
+ module.bias.requires_grad = biasRequiresGrad;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/BasicLayer.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/BasicLayer.cs
new file mode 100644
index 0000000000..6899efe8cf
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/BasicLayer.cs
@@ -0,0 +1,137 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The basic layer.
+ ///
+ public class BasicLayer : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly bool useShiftWindow;
+ private readonly int windowSize;
+ private readonly int shiftSize;
+ private readonly ModuleList blocks;
+ private readonly PatchMerging downsample;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The output channels.
+ /// The number of blocks.
+ /// The number of heads.
+ /// The size of window.
+ /// The ratio of MLP.
+ /// The ratio of drop.
+ /// The size of local convolution.
+ /// Whether use shift window.
+ /// Whether use interpolation.
+ public BasicLayer(int inChannels, int outChannels, int depth, int numHeads, int windowSize, double mlpRatio = 4.0, double dropRatio = 0, int localConvSize = 3, bool useShiftWindow = false, bool useInterpolate = false)
+ : base(nameof(BasicLayer))
+ {
+ this.useShiftWindow = useShiftWindow;
+ this.windowSize = windowSize;
+ this.shiftSize = windowSize / 2;
+
+ this.blocks = new ModuleList();
+ for (int i = 0; i < depth; i++)
+ {
+ this.blocks.Add(new AutoFormerV2Block(inChannels: inChannels, numHeads: numHeads, windowSize: windowSize, shiftSize: (i % 2 == 0) ? 0 : (windowSize / 2), mlpRatio: mlpRatio, dropRatio: dropRatio, localConvSize: localConvSize, useShiftWindow: useShiftWindow, useInterpolate: useInterpolate));
+ }
+
+ this.downsample = new PatchMerging(inChannels: inChannels, outChannels: outChannels);
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override (Tensor, int, int, Tensor, int, int) forward(Tensor x, int h, int w)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ Tensor attnMask;
+ if (this.useShiftWindow)
+ {
+ int hp = (int)Math.Ceiling((double)h / this.windowSize) * this.windowSize;
+ int wp = (int)Math.Ceiling((double)w / this.windowSize) * this.windowSize;
+ Tensor imgMask = torch.zeros(new long[] { 1, hp, wp, 1 }, device: x.device);
+ List hSlicesStartAndEnd = new List() { 0, hp - this.windowSize, hp - this.windowSize, hp - this.shiftSize, hp - this.shiftSize, hp };
+ List wSlicesStartAndEnd = new List() { 0, wp - this.windowSize, wp - this.windowSize, wp - this.shiftSize, wp - this.shiftSize, wp };
+ int cnt = 0;
+ for (int i = 0; i < hSlicesStartAndEnd.Count; i += 2)
+ {
+ for (int j = 0; j < wSlicesStartAndEnd.Count; j += 2)
+ {
+ int hStart = hSlicesStartAndEnd[i];
+ int hEnd = hSlicesStartAndEnd[i + 1];
+ int wStart = wSlicesStartAndEnd[j];
+ int wEnd = wSlicesStartAndEnd[j + 1];
+ for (int height = hStart; height < hEnd; height++)
+ {
+ for (int width = wStart; width < wEnd; width++)
+ {
+ imgMask[0, height, width, 0] = cnt;
+ }
+ }
+
+ cnt += 1;
+ }
+ }
+
+ var maskWindows = WindowPartition(imgMask, this.windowSize);
+ maskWindows = maskWindows.view(-1, this.windowSize * this.windowSize);
+
+ attnMask = maskWindows.unsqueeze(1) - maskWindows.unsqueeze(2);
+ attnMask = attnMask.masked_fill(attnMask != 0, -100.0).masked_fill(attnMask == 0, 0.0);
+ }
+ else
+ {
+ attnMask = null;
+ }
+
+ for (int i = 0; i < this.blocks.Count; i++)
+ {
+ x = this.blocks[i].forward(x, h, w, attnMask);
+ }
+
+ var xOut = x;
+ int nH = h;
+ int nW = w;
+ (x, nH, nW) = this.downsample.forward(x, h, w);
+
+ return (xOut.MoveToOuterDisposeScope(), h, w, x.MoveToOuterDisposeScope(), nH, nW);
+ }
+ }
+
+ ///
+ /// Partition input to window size.
+ ///
+ /// The input tensor.
+ /// The window size.
+ /// The output tensor.
+ private static Tensor WindowPartition(Tensor x, int windowSize)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ long b = x.shape[0];
+ long h = x.shape[1];
+ long w = x.shape[2];
+ long c = x.shape[3];
+ x = x.view(b, h / windowSize, windowSize, w / windowSize, windowSize, c);
+ var windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, windowSize, windowSize, c);
+
+ return windows.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/Conv2dBN.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/Conv2dBN.cs
new file mode 100644
index 0000000000..5b4461a6a4
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/Conv2dBN.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The Convolution and BN module.
+ ///
+ public class Conv2dBN : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly Conv2d c;
+ private readonly BatchNorm2d bn;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The output channels.
+ /// The kernel size of convolution layer.
+ /// The stride of convolution layer.
+ /// The padding of convolution layer.
+ /// The dilation of convolution layer.
+ /// The groups of convolution layer.
+ public Conv2dBN(int inChannels, int outChannels, int kernalSize = 1, int stride = 1, int padding = 0, int dilation = 1, int groups = 1)
+ : base(nameof(Conv2dBN))
+ {
+ this.c = nn.Conv2d(inChannels, outChannels, kernalSize, stride, padding, dilation, groups: groups, bias: false);
+ this.bn = nn.BatchNorm2d(outChannels);
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor x)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ var x1 = this.c.forward(x);
+ var x2 = this.bn.forward(x1);
+
+ return x2.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/ConvLayer.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/ConvLayer.cs
new file mode 100644
index 0000000000..301e23fa07
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/ConvLayer.cs
@@ -0,0 +1,60 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The layer of convolution blocks in AutoFormer network.
+ ///
+ public class ConvLayer : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly ModuleList blocks;
+ private readonly PatchMerging downsample;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The output channels.
+ /// The number of blocks.
+ /// The expand ratio of convolution layer.
+ public ConvLayer(int inChannels, int outChannels, int depth, double convExpandRatio = 4.0)
+ : base(nameof(ConvLayer))
+ {
+ this.blocks = new ModuleList();
+ for (int i = 0; i < depth; i++)
+ {
+ this.blocks.Add(new MBConv(inChannels, inChannels, convExpandRatio));
+ }
+
+ this.downsample = new PatchMerging(inChannels, outChannels);
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override (Tensor, int, int, Tensor, int, int) forward(Tensor x, int h, int w)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ foreach (var block in this.blocks)
+ {
+ x = block.forward(x);
+ }
+
+ var xOut = x;
+ var (xTmp, nH, nW) = this.downsample.forward(x, h, w);
+ x = xTmp;
+
+ return (xOut.MoveToOuterDisposeScope(), h, w, x.MoveToOuterDisposeScope(), nH, nW);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/ConvModule.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/ConvModule.cs
new file mode 100644
index 0000000000..05b732bfa7
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/ConvModule.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The convolution and activation module.
+ ///
+ public class ConvModule : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly Conv2d conv;
+ private readonly ReLU activation;
+ private readonly bool useRelu;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels of convolution layer.
+ /// The output channels of convolution layer.
+ /// The kernel size of convolution layer.
+ /// The stride of convolution layer.
+ /// The padding of convolution layer.
+ /// The dilation of convolution layer.
+ /// The bias of convolution layer.
+ /// Whether use Relu activation function.
+ public ConvModule(int inChannel, int outChannel, int kernelSize, int stride = 1, int padding = 0, int dilation = 1, bool bias = true, bool useRelu = true)
+ : base(nameof(ConvModule))
+ {
+ this.conv = nn.Conv2d(inputChannel: inChannel, outputChannel: outChannel, kernelSize: kernelSize, stride: stride, padding: padding, dilation: dilation, bias: bias);
+ this.useRelu = useRelu;
+ if (this.useRelu)
+ {
+ this.activation = nn.ReLU();
+ }
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor x)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ x = this.conv.forward(x);
+ if (this.useRelu)
+ {
+ x = this.activation.forward(x);
+ }
+
+ return x.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/FPN.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/FPN.cs
new file mode 100644
index 0000000000..c3916d0da9
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/FPN.cs
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The FPN (Feature Pyramid Networks) layer.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public class FPN : Module, List>
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly ModuleList> lateral_convs;
+ private readonly ModuleList> fpn_convs;
+ private readonly int numOuts;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The output channels.
+ /// The number of output tensors.
+ public FPN(List inChannels = null, int outChannel = 256, int numOuts = 5)
+ : base(nameof(FPN))
+ {
+ inChannels ??= new List() { 192, 384, 576 };
+ this.numOuts = numOuts;
+ int startLevel = 0;
+ int backboneEndLevel = 3;
+ this.lateral_convs = new ModuleList>();
+ this.fpn_convs = new ModuleList>();
+ for (int i = startLevel; i < backboneEndLevel; i++)
+ {
+ this.lateral_convs.Add(new ConvModule(inChannels[i], outChannel, 1, useRelu: false));
+ this.fpn_convs.Add(new ConvModule(outChannel, outChannel, 3, padding: 1, useRelu: false));
+ }
+
+ int extraLevel = 2;
+ for (int i = 0; i < extraLevel; i++)
+ {
+ int inChannel;
+ if (i == 0)
+ {
+ inChannel = inChannels[backboneEndLevel - 1];
+ }
+ else
+ {
+ inChannel = outChannel;
+ }
+
+ this.fpn_convs.Add(new ConvModule(inChannel, outChannel, 3, stride: 2, padding: 1, useRelu: false));
+ }
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override List forward(List inputs)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ int usedBackboneLevels = this.lateral_convs.Count;
+ var laterals = new List();
+ for (int i = 0; i < usedBackboneLevels; i++)
+ {
+ laterals.Add(this.lateral_convs[i].forward(inputs[i]));
+ }
+
+ for (int i = usedBackboneLevels - 1; i > 0; i--)
+ {
+ var prevShape = new long[] { laterals[i - 1].shape[2], laterals[i - 1].shape[3] };
+ laterals[i - 1] = laterals[i - 1] + nn.functional.interpolate(laterals[i], prevShape);
+ }
+
+ var outs = new List();
+ for (int i = 0; i < usedBackboneLevels; i++)
+ {
+ outs.Add(this.fpn_convs[i].forward(laterals[i]));
+ }
+
+ var extraSource = inputs[usedBackboneLevels - 1];
+ outs.Add(this.fpn_convs[usedBackboneLevels].forward(extraSource));
+ for (int i = usedBackboneLevels + 1; i < this.numOuts; i++)
+ {
+ outs.Add(this.fpn_convs[i].forward(outs[outs.Count - 1]));
+ }
+
+ for (int i = 0; i < outs.Count; i++)
+ {
+ outs[i] = outs[i].MoveToOuterDisposeScope();
+ }
+
+ return outs;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/MBConv.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/MBConv.cs
new file mode 100644
index 0000000000..9b7b76edd9
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/MBConv.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The MBConv layer.
+ ///
+ public class MBConv : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly Conv2dBN conv1;
+ private readonly GELU act1;
+ private readonly Conv2dBN conv2;
+ private readonly GELU act2;
+ private readonly Conv2dBN conv3;
+ private readonly GELU act3;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The output channels.
+ /// The expand ratio.
+ public MBConv(int inChannels, int outChannels, double expandRatio)
+ : base(nameof(MBConv))
+ {
+ int hiddenChans = (int)(inChannels * expandRatio);
+ this.conv1 = new Conv2dBN(inChannels, hiddenChans, kernalSize: 1);
+ this.act1 = nn.GELU();
+ this.conv2 = new Conv2dBN(hiddenChans, hiddenChans, kernalSize: 3, stride: 1, padding: 1, groups: hiddenChans);
+ this.act2 = nn.GELU();
+ this.conv3 = new Conv2dBN(hiddenChans, outChannels, kernalSize: 1);
+ this.act3 = nn.GELU();
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor x0)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ var shortcut = x0;
+ var x = this.conv1.forward(x0);
+ x = this.act1.forward(x);
+ x = this.conv2.forward(x);
+ x = this.act2.forward(x);
+ x = this.conv3.forward(x);
+ x += shortcut;
+ x = this.act3.forward(x);
+
+ return x.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/Mlp.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/Mlp.cs
new file mode 100644
index 0000000000..4a99faccfa
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/Mlp.cs
@@ -0,0 +1,62 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The MLP (Multilayer Perceptron) layer.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public class MLP : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly LayerNorm norm;
+ private readonly Linear fc1;
+ private readonly Linear fc2;
+ private readonly GELU act;
+ private readonly Dropout drop;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels of features.
+ /// The hidden layer channels of features.
+ /// The output channels of features.
+ /// The drop ratio.
+ public MLP(int inFeatures, int? hiddenFeatures = null, int? outFeatures = null, double dropRatio = 0)
+ : base(nameof(MLP))
+ {
+ outFeatures ??= inFeatures;
+ hiddenFeatures ??= inFeatures;
+ this.norm = nn.LayerNorm(new long[] { inFeatures });
+ this.fc1 = nn.Linear(inFeatures, (long)hiddenFeatures);
+ this.fc2 = nn.Linear((long)hiddenFeatures, (long)outFeatures);
+ this.act = nn.GELU();
+ this.drop = nn.Dropout(dropRatio);
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor x)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ x = this.norm.forward(x);
+ x = this.fc1.forward(x);
+ x = this.act.forward(x);
+ x = this.drop.forward(x);
+ x = this.fc2.forward(x);
+ x = this.drop.forward(x);
+
+ return x.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/ObjectDetectionMetrics.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/ObjectDetectionMetrics.cs
new file mode 100644
index 0000000000..205a080924
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/ObjectDetectionMetrics.cs
@@ -0,0 +1,381 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.ML.Data;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ public class ObjectDetectionMetrics
+ {
+ ///
+ /// Gets or sets mAP50 which means mean Average Precision(mAP) under IOU threshold 50%.
+ ///
+ public float MAP50 { get; set; }
+
+ ///
+ /// Gets or sets mAP , which is the average of mAP from IOU threshold 50% to 95% with step 5%, equaling to the
+ /// average of mAP50, mAP55, mAP60... and mAP95.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "")]
+ public float MAP50_95 { get; set; }
+
+ private class ObjectLabel
+ {
+ ///
+ /// Gets or sets category for this sample.
+ ///
+ public string Category { get; set; }
+
+ ///
+ /// Gets or sets height of bounding box.
+ ///
+ public float Height { get; set; }
+
+ ///
+ /// Gets or sets width of bounding box.
+ ///
+ public float Width { get; set; }
+
+ ///
+ /// Gets or sets left border of bounding box.
+ ///
+ public float Left { get; set; }
+
+ ///
+ /// Gets or sets top border of bounding box.
+ ///
+ public float Top { get; set; }
+
+ ///
+ /// Gets or sets confidence score for model output.
+ ///
+ public float Confidence { get; set; }
+ }
+
+ private static List> ThresholdFilter(List> objectLabelListIn, float scoreThreshold)
+ {
+ int filterNum = 0;
+ int total = 0;
+ List> objectLabelListOut = new();
+ objectLabelListIn?.ForEach(objectLabelList =>
+ {
+ objectLabelListOut.Add(
+ objectLabelList.Where(objLabel => objLabel.Confidence >= scoreThreshold).ToList());
+ filterNum += objectLabelList.Count - objectLabelListOut[^1].Count;
+ total += objectLabelList.Count;
+ });
+ Console.WriteLine($"total : {total}, filtered: {filterNum}, filter ratio: {(total == 0 ? 0f : ((double)filterNum / (double)total)):P}");
+ return objectLabelListOut;
+ }
+
+ internal static ObjectDetectionMetrics MeasureMetrics(IDataView dataView,
+ DataViewSchema.Column labelCol,
+ DataViewSchema.Column actualBoundingBoxColumn,
+ DataViewSchema.Column predictedLabelCol,
+ DataViewSchema.Column predictedBoundingBoxColumn,
+ DataViewSchema.Column scoreCol
+ )
+ {
+ var labelColEnumerable = dataView.GetColumn>>(labelCol);
+ var labelSet = new HashSet();
+
+ foreach (var labelRow in labelColEnumerable)
+ {
+ foreach (var label in labelRow.DenseValues())
+ labelSet.Add(label.ToString());
+ }
+ var labels = labelSet.ToList();
+
+ var expectedData = ActualIdvToObjLabl(dataView, labelCol, actualBoundingBoxColumn);
+ var actualData = PredIdvToObjLabl(dataView, predictedLabelCol, predictedBoundingBoxColumn, scoreCol);
+
+ actualData = ThresholdFilter(actualData, 0.05f);
+
+ Dictionary>> iouDic = new();
+ Dictionary groundTruthBoxes = new();
+ foreach (string label in labels)
+ {
+ iouDic.Add(label, new List>());
+ groundTruthBoxes.Add(label, 0);
+ }
+
+ GenerateIOUDic(expectedData, actualData, iouDic, groundTruthBoxes);
+
+ var evaluationMetrics = new ObjectDetectionMetrics
+ {
+ MAP50 = AP50(iouDic, groundTruthBoxes, 0.5f),
+ MAP50_95 = AP50_95(iouDic, groundTruthBoxes)
+ };
+
+ return evaluationMetrics;
+ }
+
+ private static float AP50(Dictionary>> iouDic, Dictionary groundTruthBoxes, float threshold = 0.5f)
+ {
+ int gt = groundTruthBoxes.Where(k => k.Value != 0).Count();
+ if (gt == 0)
+ {
+ return 1.0f; // no ground truth
+ }
+
+ float apSum = 0;
+ foreach (string label in iouDic?.Keys)
+ {
+ apSum += LabelAP(iouDic[label], groundTruthBoxes[label], threshold);
+ }
+
+ return apSum / gt;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "")]
+ private static float AP50_95(Dictionary>> iouDic, Dictionary groundTruthBoxes)
+ {
+ int gt = groundTruthBoxes.Where(k => k.Value != 0).Count();
+ if (gt == 0)
+ {
+ return 1.0f; // no ground truth
+ }
+
+ float ap5095 = 0f;
+ for (float thres = 0.5f; thres < 1f; thres += 0.05f)
+ {
+ float apSum = 0;
+ foreach (string label in iouDic.Keys)
+ {
+ apSum += LabelAP(iouDic[label], groundTruthBoxes[label], thres);
+ }
+
+ ap5095 += apSum / gt;
+ }
+
+ return ap5095 / ((1f - 0.5f) / 0.05f);
+ }
+
+ private static float CalculateIoU(ObjectLabel bbox, ObjectLabel gt)
+ {
+ // min overlap site
+ float xleft = Math.Max(bbox.Left, gt.Left);
+ float yleft = Math.Max(bbox.Top, gt.Top);
+ float xright = Math.Min(bbox.Left + bbox.Width, gt.Left + gt.Width);
+ float yright = Math.Min(bbox.Top + bbox.Height, gt.Top + gt.Height);
+
+ if (xleft >= xright || yleft >= yright)
+ {
+ return 0f;
+ }
+
+ float overlap = (xright - xleft) * (yright - yleft);
+ float sizePredict = bbox.Width * bbox.Height;
+ float sizeGroundTrue = gt.Width * gt.Height;
+
+ return overlap / (sizePredict + sizeGroundTrue - overlap);
+ }
+
+ private static void ImageIoU(List objectLabel, List annotation, Dictionary>> iouDic, Dictionary groundTruthBoxes)
+ {
+ // calculations each two iou
+ List> tupleList = new(); // annotaIndex, detectIndex, iouScore
+ for (int annotaIndex = 0; annotaIndex < annotation.Count; annotaIndex++)
+ {
+ var gt = annotation[annotaIndex];
+ groundTruthBoxes[gt.Category]++; // ground truth number
+ for (int detectIndex = 0; detectIndex < objectLabel.Count; detectIndex++)
+ {
+ var predBox = objectLabel[detectIndex];
+ if (predBox.Category != gt.Category)
+ {
+ continue;
+ }
+
+ float iou = CalculateIoU(predBox, gt);
+ if (iou != 0f)
+ {
+ tupleList.Add(Tuple.Create(annotaIndex, detectIndex, iou));
+ }
+ }
+ }
+
+ tupleList.Sort((x, y) => y.Item3.CompareTo(x.Item3)); // descending sort
+
+ bool[] annoSign = new bool[annotation.Count]; // whether a annotation bbox is used, default false
+ bool[] predSign = new bool[objectLabel.Count]; // whether a predict bbox is used, default false
+ foreach (var tuple in tupleList)
+ {
+ if (!annoSign[tuple.Item1] && !predSign[tuple.Item2])
+ {
+ iouDic[annotation[tuple.Item1].Category].Add(
+ Tuple.Create(objectLabel[tuple.Item2].Confidence, tuple.Item3));
+ annoSign[tuple.Item1] = true;
+ predSign[tuple.Item2] = true;
+ }
+ }
+
+ // add remain predict box as 0 iou
+ for (int predSignIndex = 0; predSignIndex < predSign.Length; predSignIndex++)
+ {
+ if (!predSign[predSignIndex])
+ {
+ iouDic[objectLabel[predSignIndex].Category].Add(
+ Tuple.Create(objectLabel[predSignIndex].Confidence, 0f));
+ }
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "")]
+ private static void GenerateIOUDic(List> annotations, List> objectLabelList, Dictionary>> iouDic, Dictionary groundTruthBoxes)
+ {
+ // each image
+ for (int annoIndex = 0; annoIndex < annotations?.Count; annoIndex++)
+ {
+ var objectLabels = objectLabelList?[annoIndex];
+ var annotation = annotations[annoIndex];
+ ImageIoU(objectLabels, annotation, iouDic, groundTruthBoxes); // calculate iou of one kind
+ }
+
+ // sort list by label
+ foreach (var detectionList in iouDic.Values)
+ {
+ detectionList.Sort((x, y) => y.Item1.CompareTo(x.Item1)); // compare score
+ }
+ }
+
+ private static float LabelAP(List> detectionList, int groundTruthBoxesNum, float threshold = 0.5f)
+ {
+ if (groundTruthBoxesNum == 0)
+ {
+ return 0f; // should be 0 or 1 here?
+ }
+
+ if (detectionList?.Count == 0)
+ {
+ return 0f;
+ }
+
+ float tp = 0; // true positive count
+ Stack> prStack = new(); // precision and recall * groundTruthBoxesNum (= true positive)
+ for (int index = 0; index < detectionList.Count; index++)
+ {
+ // compare iou
+ if (detectionList[index].Item2 >= threshold)
+ {
+ tp++;
+ }
+
+ prStack.Push(Tuple.Create(tp / (index + 1), tp)); // precision should be float
+ }
+
+ float precisionRecord = prStack.Peek().Item1;
+ float recallRecord = prStack.Pop().Item2;
+ float ap = 0f; // ap value
+ while (prStack.Count > 0)
+ {
+ Tuple pr = prStack.Pop();
+ float precision = pr.Item1;
+ float recall = pr.Item2;
+ if (precision > precisionRecord)
+ {
+ ap += precisionRecord * (recallRecord - recall);
+ precisionRecord = precision;
+ recallRecord = recall;
+ }
+ }
+
+ ap += precisionRecord * recallRecord;
+
+ return ap / groundTruthBoxesNum;
+ }
+
+ private static List> PredIdvToObjLabl(
+ IDataView idv,
+ DataViewSchema.Column predictedLabelCol,
+ DataViewSchema.Column predictedBoundingBoxColumn,
+ DataViewSchema.Column scoreCol)
+ {
+ var data = new List>();
+ var cursor = idv.GetRowCursor(predictedLabelCol, predictedBoundingBoxColumn, scoreCol);
+
+ var predLabGet = cursor.GetGetter>>(predictedLabelCol);
+ var scoreGet = cursor.GetGetter>(scoreCol);
+ var boxGet = cursor.GetGetter>(predictedBoundingBoxColumn);
+
+ VBuffer> predLab = default;
+ VBuffer score = default;
+ VBuffer box = default;
+
+ while (cursor.MoveNext())
+ {
+ predLabGet(ref predLab);
+ scoreGet(ref score);
+ boxGet(ref box);
+
+ var l = new List();
+ var boxes = box.GetValues();
+ var boxIdx = 0;
+
+ for (int i = 0; i < score.Length; i++)
+ {
+ var obj = new ObjectLabel();
+ obj.Confidence = score.GetValues()[i];
+ obj.Category = predLab.GetValues()[i].ToString();
+
+ obj.Left = boxes[boxIdx++];
+ obj.Top = boxes[boxIdx++];
+ obj.Width = (boxes[boxIdx++] - obj.Left + 1);
+ obj.Height = (boxes[boxIdx++] - obj.Top + 1);
+
+ l.Add(obj);
+ }
+ data.Add(l);
+ }
+
+ return data;
+ }
+
+ private static List> ActualIdvToObjLabl(
+ IDataView idv,
+ DataViewSchema.Column labelCol,
+ DataViewSchema.Column actualBoundingBoxColumn)
+ {
+ var data = new List>();
+ var cursor = idv.GetRowCursor(labelCol, actualBoundingBoxColumn);
+
+ var predLabGet = cursor.GetGetter>>(labelCol);
+ var boxGet = cursor.GetGetter>(actualBoundingBoxColumn);
+
+ VBuffer> predLab = default;
+ VBuffer box = default;
+
+ while (cursor.MoveNext())
+ {
+ predLabGet(ref predLab);
+ boxGet(ref box);
+
+ var l = new List();
+ var boxes = box.GetValues();
+ var boxIdx = 0;
+
+ for (int i = 0; i < predLab.Length; i++)
+ {
+ var obj = new ObjectLabel();
+ obj.Confidence = 1f;
+ obj.Category = predLab.GetValues()[i].ToString();
+
+ obj.Left = boxes[boxIdx++];
+ obj.Top = boxes[boxIdx++];
+ obj.Width = (boxes[boxIdx++] - obj.Left + 1);
+ obj.Height = (boxes[boxIdx++] - obj.Top + 1);
+
+ l.Add(obj);
+ }
+ data.Add(l);
+ }
+
+ return data;
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/ObjectDetectionTrainer.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/ObjectDetectionTrainer.cs
new file mode 100644
index 0000000000..01aefaa313
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/ObjectDetectionTrainer.cs
@@ -0,0 +1,996 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.ML.Data;
+using Microsoft.ML.Internal.Utilities;
+using Microsoft.ML.Runtime;
+using Microsoft.ML.Transforms;
+using TorchSharp;
+
+using static TorchSharp.torch;
+using static TorchSharp.TensorExtensionMethods;
+using static TorchSharp.torch.optim;
+using static TorchSharp.torch.optim.lr_scheduler;
+using Microsoft.ML.TorchSharp.Utils;
+using Microsoft.ML;
+using Microsoft.ML.TorchSharp.NasBert;
+using System.IO;
+using Microsoft.ML.Data.IO;
+using Microsoft.ML.TorchSharp.Loss;
+using Microsoft.ML.Transforms.Image;
+using static Microsoft.ML.TorchSharp.AutoFormerV2.ObjectDetectionTrainer;
+using Microsoft.ML.TorchSharp.AutoFormerV2;
+using Microsoft.ML.Tokenizers;
+using Microsoft.ML.TorchSharp.Extensions;
+using Microsoft.ML.TorchSharp.NasBert.Models;
+using static Microsoft.ML.TorchSharp.NasBert.NasBertTrainer;
+using TorchSharp.Modules;
+using System.Text;
+using static Microsoft.ML.Data.AnnotationUtils;
+
+[assembly: LoadableClass(typeof(ObjectDetectionTransformer), null, typeof(SignatureLoadModel),
+ ObjectDetectionTransformer.UserName, ObjectDetectionTransformer.LoaderSignature)]
+
+[assembly: LoadableClass(typeof(IRowMapper), typeof(ObjectDetectionTransformer), null, typeof(SignatureLoadRowMapper),
+ ObjectDetectionTransformer.UserName, ObjectDetectionTransformer.LoaderSignature)]
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ public class ObjectDetectionTrainer : IEstimator
+ {
+ public sealed class Options : TransformInputBase
+ {
+ ///
+ /// The label column name.
+ ///
+ public string LabelColumnName = DefaultColumnNames.Label;
+
+ ///
+ /// The label column name.
+ ///
+ public string PredictedLabelColumnName = DefaultColumnNames.PredictedLabel;
+
+ ///
+ /// The Bounding Box column name.
+ ///
+ public string BoundingBoxColumnName = "BoundingBoxes";
+
+ ///
+ /// The Predicted Bounding Box column name.
+ ///
+ public string PredictedBoundingBoxColumnName = "PredictedBoundingBoxes";
+
+ ///
+ /// The Image column name.
+ ///
+ public string ImageColumnName = "Image";
+
+ ///
+ /// The Confidence column name.
+ ///
+ public string ScoreColumnName = DefaultColumnNames.Score;
+
+ ///
+ /// Gets or sets the IOU threshold for removing duplicate bounding boxes.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "")]
+ public double IOUThreshold = 0.5;
+
+ ///
+ /// Gets or sets the confidenct threshold for bounding box category.
+ ///
+ public double ScoreThreshold = 0.5;
+
+ ///
+ /// Gets or sets the epoch steps in learning rate scheduler to reduce learning rate.
+ ///
+ public List Steps = new List { 6 };
+
+ ///
+ /// Stop training when reaching this number of epochs.
+ ///
+ public int MaxEpoch = 10;
+
+ ///
+ /// The validation set used while training to improve model quality.
+ ///
+ public IDataView ValidationSet = null;
+
+ ///
+ /// Number of classes for the data.
+ ///
+ internal int NumberOfClasses;
+
+ ///
+ /// Gets or sets the initial learning rate in optimizer.
+ ///
+ public double InitLearningRate = 1.0;
+
+ ///
+ /// Gets or sets the weight decay in optimizer.
+ ///
+ public double WeightDecay = 0.0;
+
+ ///
+ /// How often to log the loss.
+ ///
+ public int LogEveryNStep = 50;
+ }
+
+ private protected readonly IHost Host;
+ internal readonly Options Option;
+ private const string ModelUrl = "models/autoformer_11m_torchsharp.bin";
+
+ internal ObjectDetectionTrainer(IHostEnvironment env, Options options)
+ {
+ Host = Contracts.CheckRef(env, nameof(env)).Register(nameof(ObjectDetectionTrainer));
+ Contracts.Assert(options.MaxEpoch > 0);
+ Contracts.AssertValue(options.BoundingBoxColumnName);
+ Contracts.AssertValue(options.LabelColumnName);
+ Contracts.AssertValue(options.ImageColumnName);
+ Contracts.AssertValue(options.ScoreColumnName);
+ Contracts.AssertValue(options.PredictedLabelColumnName);
+
+ Option = options;
+ }
+
+ internal ObjectDetectionTrainer(IHostEnvironment env,
+ string labelColumnName = DefaultColumnNames.Label,
+ string predictedLabelColumnName = DefaultColumnNames.PredictedLabel,
+ string scoreColumnName = DefaultColumnNames.Score,
+ string boundingBoxColumnName = "BoundingBoxes",
+ string predictedBoundingBoxColumnName = "PredictedBoundingBoxes",
+ string imageColumnName = "Image",
+ int maxEpoch = 10) :
+ this(env, new Options
+ {
+ LabelColumnName = labelColumnName,
+ PredictedLabelColumnName = predictedLabelColumnName,
+ ScoreColumnName = scoreColumnName,
+ BoundingBoxColumnName = boundingBoxColumnName,
+ PredictedBoundingBoxColumnName = predictedBoundingBoxColumnName,
+ ImageColumnName = imageColumnName,
+ MaxEpoch = maxEpoch
+ })
+ {
+ }
+
+ public ObjectDetectionTransformer Fit(IDataView input)
+ {
+ CheckInputSchema(SchemaShape.Create(input.Schema));
+
+ ObjectDetectionTransformer transformer = default;
+
+ using (var ch = Host.Start("TrainModel"))
+ using (var pch = Host.StartProgressChannel("Training model"))
+ {
+ var header = new ProgressHeader(new[] { "Loss" }, new[] { "total images" });
+
+ var trainer = new Trainer(this, ch, input);
+ pch.SetHeader(header,
+ e =>
+ {
+ e.SetProgress(0, trainer.Updates, trainer.RowCount);
+ e.SetMetric(0, trainer.LossValue);
+ });
+
+ for (int i = 0; i < Option.MaxEpoch; i++)
+ {
+ ch.Trace($"Starting epoch {i}");
+ Host.CheckAlive();
+ trainer.Train(Host, input, pch);
+ ch.Trace($"Finished epoch {i}");
+ }
+ var labelCol = input.Schema.GetColumnOrNull(Option.LabelColumnName);
+
+ transformer = new ObjectDetectionTransformer(Host, Option, trainer.Model, new DataViewSchema.DetachedColumn(labelCol.Value));
+
+ transformer.GetOutputSchema(input.Schema);
+ }
+ return transformer;
+ }
+
+ internal class Trainer
+ {
+ public AutoFormerV2 Model;
+ public torch.Device Device;
+ public Optimizer Optimizer;
+ public LRScheduler LearningRateScheduler;
+ protected readonly ObjectDetectionTrainer Parent;
+ public FocalLoss Loss;
+ public int Updates;
+ public float LossValue;
+ public readonly int RowCount;
+ private readonly IChannel _channel;
+
+ public Trainer(ObjectDetectionTrainer parent, IChannel ch, IDataView input)
+ {
+ Parent = parent;
+ Updates = 0;
+ LossValue = 0;
+ _channel = ch;
+
+ // Get row count and figure out num of unique labels
+ RowCount = GetRowCountAndSetLabelCount(input);
+ Device = TorchUtils.InitializeDevice(Parent.Host);
+
+ // Initialize the model and load pre-trained weights
+ Model = new AutoFormerV2(
+ Parent.Option.NumberOfClasses,
+ embedChannels: new List() { 64, 128, 256, 448 },
+ depths: new List() { 2, 2, 6, 2 },
+ numHeads: new List() { 2, 4, 8, 14 },
+ device: Device);
+
+ Model.load(GetModelPath(), false);
+
+ // Figure out if we are running on GPU or CPU
+ Device = TorchUtils.InitializeDevice(Parent.Host);
+
+ // Move to GPU if we are running there
+ if (Device == CUDA)
+ Model.cuda();
+
+ // Get the parameters that need optimization and set up the optimizer
+ Optimizer = SGD(
+ Model.parameters(),
+ learningRate: Parent.Option.InitLearningRate,
+ weight_decay: Parent.Option.WeightDecay);
+
+ Loss = new FocalLoss();
+
+ LearningRateScheduler = MultiStepLR(Optimizer, Parent.Option.Steps);
+ }
+
+ private protected int GetRowCountAndSetLabelCount(IDataView input)
+ {
+ var labelCol = input.GetColumn>(Parent.Option.LabelColumnName);
+ var rowCount = 0;
+ var uniqueLabels = new HashSet();
+
+ foreach (var label in labelCol)
+ {
+ rowCount++;
+ label.DenseValues().ToList().ForEach(x => uniqueLabels.Add(x));
+ }
+
+ Parent.Option.NumberOfClasses = uniqueLabels.Count;
+ return rowCount;
+ }
+
+ private string GetModelPath()
+ {
+ var destDir = Path.Combine(((IHostEnvironmentInternal)Parent.Host).TempFilePath, "mlnet");
+ var destFileName = ModelUrl.Split('/').Last();
+
+ Directory.CreateDirectory(destDir);
+
+ string relativeFilePath = Path.Combine(destDir, destFileName);
+
+ int timeout = 10 * 60 * 1000;
+ using (var ch = (Parent.Host as IHostEnvironment).Start("Ensuring model file is present."))
+ {
+ var ensureModel = ResourceManagerUtils.Instance.EnsureResourceAsync(Parent.Host, ch, ModelUrl, destFileName, destDir, timeout);
+ ensureModel.Wait();
+ var errorResult = ResourceManagerUtils.GetErrorMessage(out var errorMessage, ensureModel.Result);
+ if (errorResult != null)
+ {
+ var directory = Path.GetDirectoryName(errorResult.FileName);
+ var name = Path.GetFileName(errorResult.FileName);
+ throw ch.Except($"{errorMessage}\nmodel file could not be downloaded!");
+ }
+ }
+
+ return relativeFilePath;
+ }
+
+ public void Train(IHost host, IDataView input, IProgressChannel pch)
+ {
+ // Get the cursor and the correct columns based on the inputs
+ DataViewRowCursor cursor = input.GetRowCursor(input.Schema[Parent.Option.LabelColumnName], input.Schema[Parent.Option.BoundingBoxColumnName], input.Schema[Parent.Option.ImageColumnName]);
+
+ var boundingBoxGetter = cursor.GetGetter>(input.Schema[Parent.Option.BoundingBoxColumnName]);
+ var imageGetter = cursor.GetGetter(input.Schema[Parent.Option.ImageColumnName]);
+ var labelGetter = cursor.GetGetter>(input.Schema[Parent.Option.LabelColumnName]);
+
+ var cursorValid = true;
+ Updates = 0;
+
+ Model.train();
+ Model.FreezeBN();
+
+ if (host is IHostEnvironmentInternal hostInternal)
+ {
+ torch.random.manual_seed(hostInternal.Seed ?? 1);
+ torch.cuda.manual_seed(hostInternal.Seed ?? 1);
+ }
+ else
+ {
+ torch.random.manual_seed(1);
+ torch.cuda.manual_seed(1);
+ }
+
+ while (cursorValid)
+ {
+ cursorValid = TrainStep(host, cursor, boundingBoxGetter, imageGetter, labelGetter, pch);
+ }
+
+ LearningRateScheduler.step();
+ }
+
+ private bool TrainStep(IHost host,
+ DataViewRowCursor cursor,
+ ValueGetter> boundingBoxGetter,
+ ValueGetter imageGetter,
+ ValueGetter> labelGetter,
+ IProgressChannel pch)
+ {
+ using var disposeScope = torch.NewDisposeScope();
+ var cursorValid = true;
+ Tensor imageTensor = default;
+ Tensor targetTensor = default;
+
+ host.CheckAlive();
+ cursorValid = cursor.MoveNext();
+ if (cursorValid)
+ {
+ (imageTensor, targetTensor) = PrepareData(labelGetter, imageGetter, boundingBoxGetter);
+ }
+ else
+ {
+ return cursorValid;
+ }
+
+ Updates++;
+ host.CheckAlive();
+
+ Optimizer.zero_grad();
+
+ var (classification, regression, anchors) = Model.forward(imageTensor);
+ var lossValue = Loss.forward(classification, regression, anchors, targetTensor);
+ lossValue.backward();
+ torch.nn.utils.clip_grad_norm_(Model.parameters(), 0.1);
+
+ Optimizer.step();
+ host.CheckAlive();
+
+ if (Updates % Parent.Option.LogEveryNStep == 0)
+ {
+ pch.Checkpoint(lossValue.ToDouble(), Updates);
+ _channel.Info($"Row: {Updates}, Loss: {lossValue.ToDouble()}");
+ }
+
+ return cursorValid;
+ }
+
+ private (Tensor image, Tensor Label) PrepareData(ValueGetter> labelGetter, ValueGetter imageGetter, ValueGetter> boundingBoxGetter)
+ {
+ using (var _ = torch.NewDisposeScope())
+ {
+ MLImage image = default;
+ imageGetter(ref image);
+ var midTensor0 = torch.tensor(image.GetBGRPixels, device: Device);
+ var midTensor1 = midTensor0.@float();
+ var midTensor2 = midTensor1.reshape(1, image.Height, image.Width, 3);
+ var midTensor3 = midTensor2.transpose(0, 3);
+ var midTensor4 = midTensor3.reshape(3, image.Height, image.Width);
+ var chunks = midTensor4.chunk(3, 0);
+
+ List part = new List();
+ part.Add(chunks[2]);
+ part.Add(chunks[1]);
+ part.Add(chunks[0]);
+
+ using var midTensor = torch.cat(part, 0);
+ using var reMidTensor = midTensor.reshape(1, 3, image.Height, image.Width);
+ var padW = 32 - (image.Width % 32);
+ var padH = 32 - (image.Height % 32);
+ using var transMidTensor = torch.zeros(1, 3, image.Height + padH, image.Width + padW, device: Device);
+ transMidTensor[.., .., ..image.Height, ..image.Width] = reMidTensor / 255.0;
+ var imageTensor = Normalize(transMidTensor, Device);
+
+ VBuffer labels = default;
+ labelGetter(ref labels);
+
+ VBuffer boxes = default;
+ boundingBoxGetter(ref boxes);
+
+ var labelValues = labels.GetValues();
+ var boxValues = boxes.GetValues();
+ Contracts.Assert(boxValues.Length == labelValues.Length * 4, "Must have 4 coordinates for each label");
+
+ int b = 0;
+ var labelTensor = torch.zeros(1, labels.Length, 5, dtype: ScalarType.Int64, device: Device);
+ for (int i = 0; i < labels.Length; i++)
+ {
+ long x0 = (long)boxValues[b++];
+ long y0 = (long)boxValues[b++];
+ long x1 = (long)boxValues[b++];
+ long y1 = (long)boxValues[b++];
+ // Our labels are 1 based, the TorchSharp model is 0 based so subtract 1 to they align correctly.
+ long cl = labelValues[i] - 1;
+ labelTensor[.., i, 0] = x0;
+ labelTensor[.., i, 1] = y0;
+ labelTensor[.., i, 2] = x1;
+ labelTensor[.., i, 3] = y1;
+ labelTensor[.., i, 4] = cl;
+ }
+ return (imageTensor.MoveToOuterDisposeScope(), labelTensor.MoveToOuterDisposeScope());
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:Private field name not in: _camelCase format", Justification = "")]
+ private static readonly double[] MEAN = { 0.406, 0.456, 0.485 };
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:Private field name not in: _camelCase format", Justification = "")]
+ private static readonly double[] STD = { 0.225, 0.224, 0.229 };
+
+ internal static Tensor Normalize(Tensor x, Device device)
+ {
+ using (var _ = torch.NewDisposeScope())
+ {
+ var meanTensor = MEAN.ToTensor(new long[4] { 1L, MEAN.Length, 1L, 1L }).to_type(ScalarType.Float32).to(device);
+ var stdTensor = STD.ToTensor(new long[4] { 1L, STD.Length, 1L, 1L }).to_type(ScalarType.Float32).to(device);
+ x = (x - meanTensor) / stdTensor;
+ return x.MoveToOuterDisposeScope();
+ }
+ }
+ }
+
+ public SchemaShape GetOutputSchema(SchemaShape inputSchema)
+ {
+ Host.CheckValue(inputSchema, nameof(inputSchema));
+
+ CheckInputSchema(inputSchema);
+
+ var outColumns = inputSchema.ToDictionary(x => x.Name);
+
+ var metadata = new List();
+ metadata.Add(new SchemaShape.Column(Kinds.KeyValues, SchemaShape.Column.VectorKind.Vector,
+ TextDataViewType.Instance, false));
+
+ var scoreMetadata = new List();
+
+ scoreMetadata.Add(new SchemaShape.Column(Kinds.ScoreColumnKind, SchemaShape.Column.VectorKind.Scalar,
+ TextDataViewType.Instance, false));
+ scoreMetadata.Add(new SchemaShape.Column(Kinds.ScoreValueKind, SchemaShape.Column.VectorKind.Scalar,
+ TextDataViewType.Instance, false));
+ scoreMetadata.Add(new SchemaShape.Column(Kinds.ScoreColumnSetId, SchemaShape.Column.VectorKind.Scalar,
+ NumberDataViewType.UInt32, true));
+ scoreMetadata.Add(new SchemaShape.Column(Kinds.TrainingLabelValues, SchemaShape.Column.VectorKind.Vector,
+ TextDataViewType.Instance, false));
+
+ // Get label column for score column annotations. Already verified it exists.
+ inputSchema.TryFindColumn(Option.LabelColumnName, out var labelCol);
+
+ outColumns[Option.PredictedLabelColumnName] = new SchemaShape.Column(Option.PredictedLabelColumnName, SchemaShape.Column.VectorKind.VariableVector,
+ NumberDataViewType.UInt32, true, new SchemaShape(metadata.ToArray()));
+
+ outColumns[Option.PredictedBoundingBoxColumnName] = new SchemaShape.Column(Option.PredictedBoundingBoxColumnName, SchemaShape.Column.VectorKind.VariableVector,
+ NumberDataViewType.Single, false);
+
+ outColumns[Option.ScoreColumnName] = new SchemaShape.Column(Option.ScoreColumnName, SchemaShape.Column.VectorKind.VariableVector,
+ NumberDataViewType.Single, false, new SchemaShape(scoreMetadata.ToArray()));
+
+
+ return new SchemaShape(outColumns.Values);
+ }
+
+ private void CheckInputSchema(SchemaShape inputSchema)
+ {
+ // Verify that all required input columns are present, and are of the same type.
+ if (!inputSchema.TryFindColumn(Option.LabelColumnName, out var labelCol))
+ throw Host.ExceptSchemaMismatch(nameof(inputSchema), "label", Option.LabelColumnName);
+ if (labelCol.Kind != SchemaShape.Column.VectorKind.VariableVector || labelCol.ItemType.RawType != typeof(UInt32))
+ throw Host.ExceptSchemaMismatch(nameof(inputSchema), "label", Option.LabelColumnName,
+ new VectorDataViewType(new KeyDataViewType(typeof(uint), uint.MaxValue)).ToString(), labelCol.GetTypeString());
+
+ if (!inputSchema.TryFindColumn(Option.BoundingBoxColumnName, out var boundingBoxCol))
+ throw Host.ExceptSchemaMismatch(nameof(inputSchema), "BoundingBox", Option.BoundingBoxColumnName);
+ if (boundingBoxCol.Kind != SchemaShape.Column.VectorKind.VariableVector || boundingBoxCol.ItemType.RawType != typeof(Single))
+ throw Host.ExceptSchemaMismatch(nameof(inputSchema), "BoundingBox", Option.BoundingBoxColumnName,
+ new VectorDataViewType(NumberDataViewType.Single).ToString(), boundingBoxCol.GetTypeString());
+
+ if (!inputSchema.TryFindColumn(Option.ImageColumnName, out var imageCol))
+ throw Host.ExceptSchemaMismatch(nameof(inputSchema), "Image", Option.ImageColumnName);
+ if (imageCol.ItemType.RawType != typeof(MLImage))
+ throw Host.ExceptSchemaMismatch(nameof(inputSchema), "Image", Option.ImageColumnName,
+ new ImageDataViewType().ToString(), imageCol.GetTypeString());
+ }
+ }
+
+ public class ObjectDetectionTransformer : RowToRowTransformerBase
+ {
+ private protected readonly Device Device;
+ private protected readonly AutoFormerV2 Model;
+ internal readonly ObjectDetectionTrainer.Options Options;
+
+ public readonly SchemaShape.Column PredictedLabelColumnName;
+ public readonly SchemaShape.Column PredictedBoundingBoxColumn;
+ public readonly SchemaShape.Column ConfidenceColumn;
+ public readonly DataViewSchema.DetachedColumn LabelColumn;
+
+ internal const string LoadName = "ObjDetTrainer";
+ internal const string UserName = "Obj Detection Trainer";
+ internal const string ShortName = "OBJDETC";
+ internal const string Summary = "Object Detection";
+ internal const string LoaderSignature = "OBJDETC";
+
+ private static readonly FuncStaticMethodInfo1 _decodeInitMethodInfo
+ = new FuncStaticMethodInfo1(DecodeInit);
+
+ internal ObjectDetectionTransformer(IHostEnvironment env, ObjectDetectionTrainer.Options options, AutoFormerV2 model, DataViewSchema.DetachedColumn labelColumn)
+ : base(Contracts.CheckRef(env, nameof(env)).Register(nameof(ObjectDetectionTransformer)))
+ {
+ Device = TorchUtils.InitializeDevice(env);
+
+ Options = options;
+ LabelColumn = labelColumn;
+ PredictedLabelColumnName = new SchemaShape.Column(Options.PredictedLabelColumnName, SchemaShape.Column.VectorKind.Vector, NumberDataViewType.UInt32, false);
+ PredictedBoundingBoxColumn = new SchemaShape.Column(Options.PredictedBoundingBoxColumnName, SchemaShape.Column.VectorKind.Vector, NumberDataViewType.Single, false);
+ ConfidenceColumn = new SchemaShape.Column(Options.ScoreColumnName, SchemaShape.Column.VectorKind.Vector, NumberDataViewType.Single, false);
+
+ Model = model;
+ Model.eval();
+
+ if (Device == CUDA)
+ Model.cuda();
+ }
+
+ public SchemaShape GetOutputSchema(SchemaShape inputSchema)
+ {
+ Host.CheckValue(inputSchema, nameof(inputSchema));
+
+ CheckInputSchema(inputSchema);
+
+ var outColumns = inputSchema.ToDictionary(x => x.Name);
+
+ var labelAnnotationsColumn = new SchemaShape.Column(AnnotationUtils.Kinds.SlotNames, SchemaShape.Column.VectorKind.Vector, LabelColumn.Annotations.Schema[AnnotationUtils.Kinds.SlotNames].Type, false);
+ var predLabelMetadata = new SchemaShape(new SchemaShape.Column[] { labelAnnotationsColumn }
+ .Concat(AnnotationUtils.GetTrainerOutputAnnotation()));
+
+ var scoreMetadata = new List();
+
+ scoreMetadata.Add(new SchemaShape.Column(Kinds.ScoreColumnKind, SchemaShape.Column.VectorKind.Scalar,
+ TextDataViewType.Instance, false));
+ scoreMetadata.Add(new SchemaShape.Column(Kinds.ScoreValueKind, SchemaShape.Column.VectorKind.Scalar,
+ TextDataViewType.Instance, false));
+ scoreMetadata.Add(new SchemaShape.Column(Kinds.ScoreColumnSetId, SchemaShape.Column.VectorKind.Scalar,
+ NumberDataViewType.UInt32, true));
+ scoreMetadata.Add(new SchemaShape.Column(Kinds.TrainingLabelValues, SchemaShape.Column.VectorKind.Vector,
+ TextDataViewType.Instance, false));
+
+ outColumns[Options.PredictedLabelColumnName] = new SchemaShape.Column(Options.PredictedLabelColumnName, SchemaShape.Column.VectorKind.VariableVector,
+ NumberDataViewType.UInt32, true, predLabelMetadata);
+
+ outColumns[Options.PredictedBoundingBoxColumnName] = new SchemaShape.Column(Options.PredictedBoundingBoxColumnName, SchemaShape.Column.VectorKind.VariableVector,
+ NumberDataViewType.Single, false);
+
+ outColumns[Options.ScoreColumnName] = new SchemaShape.Column(Options.ScoreColumnName, SchemaShape.Column.VectorKind.VariableVector,
+ NumberDataViewType.Single, false, new SchemaShape(scoreMetadata.ToArray()));
+
+ return new SchemaShape(outColumns.Values);
+ }
+
+ private void CheckInputSchema(SchemaShape inputSchema)
+ {
+ if (!inputSchema.TryFindColumn(Options.ImageColumnName, out var imageCol))
+ throw Host.ExceptSchemaMismatch(nameof(inputSchema), "Image", Options.ImageColumnName);
+ if (imageCol.ItemType != new ImageDataViewType())
+ throw Host.ExceptSchemaMismatch(nameof(inputSchema), "Image", Options.ImageColumnName,
+ new ImageDataViewType().ToString(), imageCol.GetTypeString());
+ }
+
+ private static VersionInfo GetVersionInfo()
+ {
+ return new VersionInfo(
+ modelSignature: "OBJ-DETC",
+ verWrittenCur: 0x00010001, // Initial
+ verReadableCur: 0x00010001,
+ verWeCanReadBack: 0x00010001,
+ loaderSignature: LoaderSignature,
+ loaderAssemblyName: typeof(ObjectDetectionTransformer).Assembly.FullName);
+ }
+
+ private protected override void SaveModel(ModelSaveContext ctx)
+ {
+ Host.AssertValue(ctx);
+ ctx.CheckAtModel();
+ ctx.SetVersionInfo(GetVersionInfo());
+
+ // *** Binary format ***
+ // int: id of label column name
+ // int: id of predicted label column name
+ // int: id of the BoundingBoxColumnName name
+ // int: id of the PredictedBoundingBoxColumnName name
+ // int: id of ImageColumnName name
+ // int: id of Score column name
+ // int: number of classes
+ // double: score threshold
+ // double: iou threshold
+ // LabelValues
+ // BinaryStream: TS Model
+
+ ctx.SaveNonEmptyString(Options.LabelColumnName);
+ ctx.SaveNonEmptyString(Options.PredictedLabelColumnName);
+ ctx.SaveNonEmptyString(Options.BoundingBoxColumnName);
+ ctx.SaveNonEmptyString(Options.PredictedBoundingBoxColumnName);
+ ctx.SaveNonEmptyString(Options.ImageColumnName);
+ ctx.SaveNonEmptyString(Options.ScoreColumnName);
+
+ ctx.Writer.Write(Options.NumberOfClasses);
+
+ ctx.Writer.Write(Options.ScoreThreshold);
+ ctx.Writer.Write(Options.IOUThreshold);
+
+ var labelColType = LabelColumn.Annotations.Schema[AnnotationUtils.Kinds.KeyValues].Type as VectorDataViewType;
+ Microsoft.ML.Internal.Utilities.Utils.MarshalActionInvoke(SaveLabelValues, labelColType.ItemType.RawType, ctx);
+
+ ctx.SaveBinaryStream("TSModel", w =>
+ {
+ Model.save(w);
+ });
+ }
+
+ private void SaveLabelValues(ModelSaveContext ctx)
+ {
+ ValueGetter> getter = LabelColumn.Annotations.GetGetter>(LabelColumn.Annotations.Schema[AnnotationUtils.Kinds.KeyValues]);
+ var val = default(VBuffer);
+ getter(ref val);
+
+ BinarySaver saver = new BinarySaver(Host, new BinarySaver.Arguments());
+ int bytesWritten;
+ var labelColType = LabelColumn.Annotations.Schema[AnnotationUtils.Kinds.KeyValues].Type as VectorDataViewType;
+ if (!saver.TryWriteTypeAndValue>(ctx.Writer.BaseStream, labelColType, ref val, out bytesWritten))
+ throw Host.Except("We do not know how to serialize label names of type '{0}'", labelColType.ItemType);
+ }
+
+ private protected override IRowMapper MakeRowMapper(DataViewSchema schema) => new ObjDetMapper(this, schema);
+
+ //Factory method for SignatureLoadRowMapper.
+ private static IRowMapper Create(IHostEnvironment env, ModelLoadContext ctx, DataViewSchema inputSchema)
+ => Create(env, ctx).MakeRowMapper(inputSchema);
+
+ // Factory method for SignatureLoadModel.
+ private static ObjectDetectionTransformer Create(IHostEnvironment env, ModelLoadContext ctx)
+ {
+ Contracts.CheckValue(env, nameof(env));
+ env.CheckValue(ctx, nameof(ctx));
+ ctx.CheckAtModel(GetVersionInfo());
+
+ // *** Binary format ***
+ // int: id of label column name
+ // int: id of predicted label column name
+ // int: id of the BoundingBoxColumnName name
+ // int: id of the PredictedBoundingBoxColumnName name
+ // int: id of ImageColumnName name
+ // int: id of Score column name
+ // int: number of classes
+ // double: score threshold
+ // double: iou threshold
+ // LabelValues
+ // BinaryStream: TS Model
+
+ var options = new Options()
+ {
+ LabelColumnName = ctx.LoadString(),
+ PredictedLabelColumnName = ctx.LoadString(),
+ BoundingBoxColumnName = ctx.LoadString(),
+ PredictedBoundingBoxColumnName = ctx.LoadString(),
+ ImageColumnName = ctx.LoadString(),
+ ScoreColumnName = ctx.LoadString(),
+ NumberOfClasses = ctx.Reader.ReadInt32(),
+ ScoreThreshold = ctx.Reader.ReadDouble(),
+ IOUThreshold = ctx.Reader.ReadDouble(),
+ };
+
+ var ch = env.Start("Load Model");
+
+ var model = new AutoFormerV2(options.NumberOfClasses,
+ embedChannels: new List() { 64, 128, 256, 448 },
+ depths: new List() { 2, 2, 6, 2 },
+ numHeads: new List() { 2, 4, 8, 14 },
+ device: TorchUtils.InitializeDevice(env));
+
+ BinarySaver saver = new BinarySaver(env, new BinarySaver.Arguments());
+ DataViewType type;
+ object value;
+ env.CheckDecode(saver.TryLoadTypeAndValue(ctx.Reader.BaseStream, out type, out value));
+ var vecType = type as VectorDataViewType;
+ env.CheckDecode(vecType != null);
+ env.CheckDecode(value != null);
+ var labelGetter = Microsoft.ML.Internal.Utilities.Utils.MarshalInvoke(_decodeInitMethodInfo, vecType.ItemType.RawType, value);
+
+ var meta = new DataViewSchema.Annotations.Builder();
+ meta.Add(AnnotationUtils.Kinds.KeyValues, type, labelGetter);
+
+ var labelCol = new DataViewSchema.DetachedColumn(options.LabelColumnName, type, meta.ToAnnotations());
+
+ if (!ctx.TryLoadBinaryStream("TSModel", r => model.load(r)))
+ throw env.ExceptDecode();
+
+ return new ObjectDetectionTransformer(env, options, model, labelCol);
+ }
+
+ private static Delegate DecodeInit(object value)
+ {
+ VBuffer buffValue = (VBuffer)value;
+ ValueGetter> buffGetter = (ref VBuffer dst) => buffValue.CopyTo(ref dst);
+ return buffGetter;
+ }
+
+ private class ObjDetMapper : MapperBase
+ {
+ private readonly ObjectDetectionTransformer _parent;
+ private readonly HashSet _inputColIndices;
+
+ private static readonly FuncInstanceMethodInfo1 _makeLabelAnnotationGetter
+ = FuncInstanceMethodInfo1.Create(target => target.GetLabelAnnotations);
+
+
+ public ObjDetMapper(ObjectDetectionTransformer parent, DataViewSchema inputSchema) :
+ base(Contracts.CheckRef(parent, nameof(parent)).Host.Register(nameof(ObjDetMapper)), inputSchema, parent)
+ {
+ _parent = parent;
+ _inputColIndices = new HashSet();
+
+ if (inputSchema.TryGetColumnIndex(parent.Options.ImageColumnName, out var col))
+ _inputColIndices.Add(col);
+
+ if (Host is IHostEnvironmentInternal hostInternal)
+ {
+ torch.random.manual_seed(hostInternal.Seed ?? 1);
+ torch.cuda.manual_seed(hostInternal.Seed ?? 1);
+ }
+ else
+ {
+ torch.random.manual_seed(1);
+ torch.cuda.manual_seed(1);
+ }
+ }
+
+ protected override DataViewSchema.DetachedColumn[] GetOutputColumnsCore()
+ {
+
+ var info = new DataViewSchema.DetachedColumn[3];
+ var keyType = _parent.LabelColumn.Annotations.Schema.GetColumnOrNull(AnnotationUtils.Kinds.KeyValues)?.Type as VectorDataViewType;
+ var getter = Microsoft.ML.Internal.Utilities.Utils.MarshalInvoke(_makeLabelAnnotationGetter, this, keyType.ItemType.RawType, _parent.LabelColumn);
+
+ var meta = new DataViewSchema.Annotations.Builder();
+ meta.Add(AnnotationUtils.Kinds.ScoreColumnKind, TextDataViewType.Instance, (ref ReadOnlyMemory value) => { value = AnnotationUtils.Const.ScoreColumnKind.MulticlassClassification.AsMemory(); });
+ meta.Add(AnnotationUtils.Kinds.ScoreColumnSetId, AnnotationUtils.ScoreColumnSetIdType, GetScoreColumnSetId(InputSchema));
+ meta.Add(AnnotationUtils.Kinds.ScoreValueKind, TextDataViewType.Instance, (ref ReadOnlyMemory value) => { value = AnnotationUtils.Const.ScoreValueKind.Score.AsMemory(); });
+ meta.Add(AnnotationUtils.Kinds.TrainingLabelValues, keyType, getter);
+
+ var labelBuilder = new DataViewSchema.Annotations.Builder();
+ labelBuilder.Add(AnnotationUtils.Kinds.KeyValues, keyType, getter);
+
+ info[0] = new DataViewSchema.DetachedColumn(_parent.Options.PredictedLabelColumnName, new VectorDataViewType(new KeyDataViewType(typeof(uint), _parent.Options.NumberOfClasses)), labelBuilder.ToAnnotations());
+
+ info[1] = new DataViewSchema.DetachedColumn(_parent.Options.ScoreColumnName, new VectorDataViewType(NumberDataViewType.Single), meta.ToAnnotations());
+
+ info[2] = new DataViewSchema.DetachedColumn(_parent.Options.PredictedBoundingBoxColumnName, new VectorDataViewType(NumberDataViewType.Single));
+ return info;
+
+ }
+
+ private Delegate GetLabelAnnotations(DataViewSchema.DetachedColumn labelCol)
+ {
+ return labelCol.Annotations.GetGetter>(labelCol.Annotations.Schema[AnnotationUtils.Kinds.KeyValues]);
+ }
+
+ private ValueGetter GetScoreColumnSetId(DataViewSchema schema)
+ {
+ int c;
+ var max = schema.GetMaxAnnotationKind(out c, AnnotationUtils.Kinds.ScoreColumnSetId);
+ uint id = checked(max + 1);
+ return
+ (ref uint dst) => dst = id;
+ }
+
+ protected override Delegate MakeGetter(DataViewRow input, int iinfo, Func activeOutput, out Action disposer)
+ => throw new NotImplementedException("This should never be called!");
+
+ private Delegate CreateGetter(DataViewRow input, int iinfo, TensorCacher outputCacher)
+ {
+ var ch = Host.Start("Make Getter");
+ if (iinfo == 0)
+ return MakePredictedLabelGetter(input, ch, outputCacher);
+ else if (iinfo == 1)
+ return MakeScoreGetter(input, ch, outputCacher);
+ else
+ return MakeBoundingBoxGetter(input, ch, outputCacher);
+ }
+
+ private Delegate MakeScoreGetter(DataViewRow input, IChannel ch, TensorCacher outputCacher)
+ {
+ ValueGetter getImage = default;
+
+ getImage = input.GetGetter(input.Schema[_parent.Options.ImageColumnName]);
+
+ MLImage image = default;
+
+ ValueGetter> score = (ref VBuffer dst) =>
+ {
+ using var disposeScope = torch.NewDisposeScope();
+ UpdateCacheIfNeeded(input.Position, outputCacher, ref image, ref getImage);
+ var editor = VBufferEditor.Create(ref dst, outputCacher.ScoresBuffer.Length);
+
+ for (var i = 0; i < outputCacher.ScoresBuffer.Length; i++)
+ {
+ editor.Values[i] = outputCacher.ScoresBuffer[i];
+ }
+ dst = editor.Commit();
+ };
+
+ return score;
+ }
+
+ private Delegate MakePredictedLabelGetter(DataViewRow input, IChannel ch, TensorCacher outputCacher)
+ {
+ ValueGetter getImage = default;
+
+ getImage = input.GetGetter(input.Schema[_parent.Options.ImageColumnName]);
+
+ MLImage image = default;
+
+ ValueGetter> predictedLabel = (ref VBuffer dst) =>
+ {
+ using var disposeScope = torch.NewDisposeScope();
+ UpdateCacheIfNeeded(input.Position, outputCacher, ref image, ref getImage);
+ var editor = VBufferEditor.Create(ref dst, outputCacher.PredictedLabelsBuffer.Length);
+
+ for (var i = 0; i < outputCacher.PredictedLabelsBuffer.Length; i++)
+ {
+ editor.Values[i] = outputCacher.PredictedLabelsBuffer[i];
+ }
+ dst = editor.Commit();
+ };
+
+ return predictedLabel;
+ }
+
+ private Delegate MakeBoundingBoxGetter(DataViewRow input, IChannel ch, TensorCacher outputCacher)
+ {
+ ValueGetter getImage = default;
+
+ getImage = input.GetGetter(input.Schema[_parent.Options.ImageColumnName]);
+
+ MLImage image = default;
+
+ ValueGetter> score = (ref VBuffer dst) =>
+ {
+ using var disposeScope = torch.NewDisposeScope();
+ UpdateCacheIfNeeded(input.Position, outputCacher, ref image, ref getImage);
+ var editor = VBufferEditor.Create(ref dst, outputCacher.BoxBuffer.Length);
+
+ for (var i = 0; i < outputCacher.BoxBuffer.Length; i++)
+ {
+ editor.Values[i] = outputCacher.BoxBuffer[i];
+ }
+ dst = editor.Commit();
+ };
+
+ return score;
+ }
+
+ public override Delegate[] CreateGetters(DataViewRow input, Func activeOutput, out Action disposer)
+ {
+ Host.AssertValue(input);
+ Contracts.Assert(input.Schema == base.InputSchema);
+
+ TensorCacher outputCacher = new TensorCacher(_parent.Options.NumberOfClasses);
+ var ch = Host.Start("Make Getters");
+ _parent.Model.eval();
+
+ int n = OutputColumns.Value.Length;
+ var result = new Delegate[n];
+ for (int i = 0; i < n; i++)
+ {
+ if (!activeOutput(i))
+ continue;
+ result[i] = CreateGetter(input, i, outputCacher);
+ }
+ disposer = () =>
+ {
+ outputCacher.Dispose();
+ };
+ return result;
+ }
+
+ private Tensor PrepInputTensors(ref MLImage image, ValueGetter imageGetter)
+ {
+ imageGetter(ref image);
+ using (var preprocessScope = torch.NewDisposeScope())
+ {
+ var midTensor0 = torch.tensor(image.GetBGRPixels, device: _parent.Device);
+ var midTensor1 = midTensor0.@float();
+ var midTensor2 = midTensor1.reshape(1, image.Height, image.Width, 3);
+ var midTensor3 = midTensor2.transpose(0, 3);
+ var midTensor4 = midTensor3.reshape(3, image.Height, image.Width);
+ var chunks = midTensor4.chunk(3, 0);
+ var part = new List();
+
+ part.Add(chunks[2]);
+ part.Add(chunks[1]);
+ part.Add(chunks[0]);
+
+ var midTensor = torch.cat(part, 0);
+ var reMidTensor = midTensor.reshape(1, 3, image.Height, image.Width);
+ var padW = 32 - (image.Width % 32);
+ var padH = 32 - (image.Height % 32);
+ var transMidTensor = torch.zeros(1, 3, image.Height + padH, image.Width + padW, device: _parent.Device);
+ transMidTensor[.., .., ..image.Height, ..image.Width] = reMidTensor / 255.0;
+ var imageTensor = ObjectDetectionTrainer.Trainer.Normalize(transMidTensor, _parent.Device);
+ return imageTensor.MoveToOuterDisposeScope();
+ }
+ }
+
+ private (Tensor, Tensor, Tensor) PrepAndRunModel(Tensor inputTensor)
+ {
+ return _parent.Model.forward(inputTensor);
+ }
+
+ private protected class TensorCacher : IDisposable
+ {
+ public long Position;
+
+ public int MaxLength;
+ public UInt32[] PredictedLabelsBuffer;
+ public Single[] ScoresBuffer;
+ public Single[] BoxBuffer;
+
+ public TensorCacher(int maxLength)
+ {
+ Position = -1;
+ MaxLength = maxLength;
+
+ PredictedLabelsBuffer = default;
+ ScoresBuffer = default;
+ BoxBuffer = default;
+ }
+
+ private bool _isDisposed;
+
+ public void Dispose()
+ {
+ if (_isDisposed)
+ return;
+
+ _isDisposed = true;
+ }
+ }
+
+ private protected void UpdateCacheIfNeeded(long position, TensorCacher outputCache, ref MLImage image, ref ValueGetter getImage)
+ {
+ if (outputCache.Position != position)
+ {
+
+ var imageTensor = PrepInputTensors(ref image, getImage);
+ _parent.Model.eval();
+
+ (var pred, var score, var box) = PrepAndRunModel(imageTensor);
+
+ ImageUtils.Postprocess(imageTensor, pred, score, box, out outputCache.PredictedLabelsBuffer, out outputCache.ScoresBuffer, out outputCache.BoxBuffer, _parent.Options.ScoreThreshold, _parent.Options.IOUThreshold);
+
+ pred.Dispose();
+ score.Dispose();
+ box.Dispose();
+
+ outputCache.Position = position;
+ }
+ }
+
+ private protected override void SaveModel(ModelSaveContext ctx) => _parent.SaveModel(ctx);
+
+ private protected override Func GetDependenciesCore(Func activeOutput)
+ {
+ return col => (activeOutput(0) || activeOutput(1) || activeOutput(2)) && _inputColIndices.Any(i => i == col);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/PatchEmbed.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/PatchEmbed.cs
new file mode 100644
index 0000000000..f8dcda1a2e
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/PatchEmbed.cs
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The PatchEmbed layer.
+ ///
+ public class PatchEmbed : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly ModuleList> seq;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The output channels.
+ public PatchEmbed(int inChannels, int embedChannels)
+ : base(nameof(PatchEmbed))
+ {
+ this.seq = ModuleList>();
+ this.seq.Add(new Conv2dBN(inChannels, embedChannels / 2, 3, 2, 1));
+ this.seq.Add(nn.GELU());
+ this.seq.Add(new Conv2dBN(embedChannels / 2, embedChannels, 3, 2, 1));
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor x)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ foreach (var submodule in this.seq)
+ {
+ x = submodule.forward(x);
+ }
+
+ return x.MoveToOuterDisposeScope();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/PatchMerging.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/PatchMerging.cs
new file mode 100644
index 0000000000..0e779b131a
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/PatchMerging.cs
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The PatchMerging layer.
+ ///
+ public class PatchMerging : Module
+ {
+#pragma warning disable MSML_PrivateFieldName // Need to match TorchSharp model names.
+ private readonly GELU act;
+ private readonly Conv2dBN conv1;
+ private readonly Conv2dBN conv2;
+ private readonly Conv2dBN conv3;
+#pragma warning restore MSML_PrivateFieldName
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The input channels.
+ /// The output channels.
+ public PatchMerging(int inChannels, int outChannels)
+ : base(nameof(PatchMerging))
+ {
+ this.act = nn.GELU();
+ this.conv1 = new Conv2dBN(inChannels, outChannels, 1, 1, 0);
+ this.conv2 = new Conv2dBN(outChannels, outChannels, 3, 2, 1, groups: outChannels);
+ this.conv3 = new Conv2dBN(outChannels, outChannels, 1, 1, 0);
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override (Tensor, int, int) forward(Tensor x, int h, int w)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ if (x.shape.Length == 3)
+ {
+ long b = x.shape[0];
+ x = x.view(b, h, w, -1).permute(0, 3, 1, 2);
+ }
+
+ x = this.conv1.forward(x);
+ x = this.act.forward(x);
+ x = this.conv2.forward(x);
+ x = this.act.forward(x);
+ x = this.conv3.forward(x);
+
+ x = x.flatten(2).transpose(1, 2);
+ h = (h + 1) / 2;
+ w = (w + 1) / 2;
+
+ return (x.MoveToOuterDisposeScope(), h, w);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/AutoFormerV2/RetinaHead.cs b/src/Microsoft.ML.TorchSharp/AutoFormerV2/RetinaHead.cs
new file mode 100644
index 0000000000..8cdfdb8c00
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/AutoFormerV2/RetinaHead.cs
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using TorchSharp;
+using TorchSharp.Modules;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.AutoFormerV2
+{
+ ///
+ /// The head of RetinaNet.
+ ///
+ public class RetinaHead : Module, (List, List)>
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly ModuleList> cls_convs;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly ModuleList> reg_convs;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly Conv2d retina_cls;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly Conv2d retina_reg;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly Sigmoid output_act;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly int numClasses;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The number of classes.
+ /// The input channels.
+ /// The number of stacked convolution layers.
+ /// The feature channels.
+ /// The number of base priors.
+ public RetinaHead(int numClasses, int inChannels = 256, int stackedConvs = 4, int featChannels = 256, int numBasePriors = 9)
+ : base(nameof(RetinaHead))
+ {
+ this.numClasses = numClasses;
+ this.cls_convs = new ModuleList>();
+ this.reg_convs = new ModuleList>();
+ for (int i = 0; i < stackedConvs; i++)
+ {
+ int chn = i == 0 ? inChannels : featChannels;
+ this.cls_convs.Add(new ConvModule(chn, featChannels, 3, stride: 1, padding: 1, useRelu: true));
+ this.reg_convs.Add(new ConvModule(chn, featChannels, 3, stride: 1, padding: 1, useRelu: true));
+ }
+
+ this.retina_cls = Conv2d(featChannels, numBasePriors * numClasses, 3, padding: 1);
+ this.retina_reg = Conv2d(featChannels, numBasePriors * 4, 3, padding: 1);
+ this.output_act = nn.Sigmoid();
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override (List, List) forward(List inputs)
+ {
+ using (var scope = torch.NewDisposeScope())
+ {
+ var clsOutputs = new List();
+ var regOutputs = new List();
+ for (int i = 0; i < inputs.Count; i++)
+ {
+ var clsOutput = inputs[i];
+ for (int j = 0; j < this.cls_convs.Count; j++)
+ {
+ clsOutput = this.cls_convs[j].forward(clsOutput);
+ }
+
+ clsOutput = this.retina_cls.forward(clsOutput);
+ clsOutput = this.output_act.forward(clsOutput);
+
+ // out is B x C x W x H, with C = num_classes * num_anchors
+ clsOutput = clsOutput.permute(0, 2, 3, 1);
+ clsOutput = clsOutput.contiguous().view(clsOutput.shape[0], -1, this.numClasses);
+ clsOutputs.Add(clsOutput.MoveToOuterDisposeScope());
+
+ var regOutput = inputs[i];
+ for (int j = 0; j < this.reg_convs.Count; j++)
+ {
+ regOutput = this.reg_convs[j].forward(regOutput);
+ }
+
+ regOutput = this.retina_reg.forward(regOutput);
+
+ // out is B x C x W x H, with C = 4*num_anchors
+ regOutput = regOutput.permute(0, 2, 3, 1);
+ regOutput = regOutput.contiguous().view(regOutput.shape[0], -1, 4);
+ regOutputs.Add(regOutput.MoveToOuterDisposeScope());
+ }
+
+ return (clsOutputs, regOutputs);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/Loss/FocalLoss.cs b/src/Microsoft.ML.TorchSharp/Loss/FocalLoss.cs
new file mode 100644
index 0000000000..3954677526
--- /dev/null
+++ b/src/Microsoft.ML.TorchSharp/Loss/FocalLoss.cs
@@ -0,0 +1,200 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using TorchSharp;
+using static TorchSharp.torch;
+using static TorchSharp.torch.nn;
+
+namespace Microsoft.ML.TorchSharp.Loss
+{
+ ///
+ /// A kind of loss function to balance easy and hard samples.
+ ///
+ public class FocalLoss : Module
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly double alpha;
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:private field names not in _camelCase format", Justification = "Need to match TorchSharp.")]
+ private readonly double gamma;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The alpha.
+ /// The gamma.
+ public FocalLoss(double alpha = 0.25, double gamma = 2.0)
+ : base(nameof(FocalLoss))
+ {
+ this.alpha = alpha;
+ this.gamma = gamma;
+ }
+
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp.")]
+ public override Tensor forward(Tensor classifications, Tensor regressions, Tensor anchors, Tensor annotations)
+ {
+ var batchSize = classifications.shape[0];
+ var classificationLosses = new List();
+ var regressionLosses = new List();
+
+ var anchor = anchors[0, .., ..];
+
+ var anchorWidths = anchor[.., 2] - anchor[.., 0];
+ var anchorHeights = anchor[.., 3] - anchor[.., 1];
+ var anchorCtrX = anchor[.., 0] + (0.5 * anchorWidths);
+ var anchorCtrY = anchor[.., 1] + (0.5 * anchorHeights);
+
+ for (int j = 0; j < batchSize; ++j)
+ {
+ var classification = classifications[j, .., ..];
+ var regression = regressions[j, .., ..];
+
+ var bboxAnnotation = annotations[j, .., ..];
+ bboxAnnotation = bboxAnnotation[bboxAnnotation[.., 4] != -1];
+
+ classification = torch.clamp(classification, 1e-4, 1.0 - 1e-4);
+
+ if (bboxAnnotation.shape[0] == 0)
+ {
+ var alphaFactor = this.alpha * torch.ones(classification.shape, dtype: ScalarType.Float32, device: classifications.device);
+ alphaFactor = 1.0f - alphaFactor;
+
+ var focalWeight = classification;
+ focalWeight = alphaFactor * torch.pow(focalWeight, this.gamma);
+
+ var bce = -torch.log(1.0f - classification);
+
+ var clsLoss = focalWeight * bce;
+ classificationLosses.Add(clsLoss.sum());
+ regressionLosses.Add(torch.tensor(0, dtype: ScalarType.Float32, device: classifications.device));
+ }
+ else
+ {
+ var iou = CalcIou(anchors[0, .., ..], bboxAnnotation[.., ..4]); // num_anchors x num_annotations
+
+ var (iou_max, iou_argmax) = torch.max(iou, dim: 1); // num_anchors x 1
+
+ // compute the loss for classification
+ var targets = (-1) * torch.ones(classification.shape, dtype: ScalarType.Float32, device: classifications.device);
+ targets[torch.lt(iou_max, 0.4)] = 0;
+
+ Tensor positiveIndices = torch.ge(iou_max, 0.5);
+
+ var numPositiveAnchors = positiveIndices.sum();
+
+ var assignedAnnotations = bboxAnnotation[iou_argmax];
+
+ targets[positiveIndices] = 0;
+
+ var assignedPositiveIndeces = positiveIndices.nonzero().squeeze(-1);
+ for (int i = 0; i < assignedPositiveIndeces.shape[0]; i++)
+ {
+ var t = assignedPositiveIndeces[i];
+ targets[t, assignedAnnotations[t, 4]] = 1;
+ }
+
+ var alphaFactor = torch.ones(targets.shape, dtype: ScalarType.Float32, device: classifications.device) * alpha;
+ alphaFactor = torch.where(targets.eq(1.0), alphaFactor, 1.0 - alphaFactor);
+
+ var focalWeight = torch.where(targets.eq(1.0), 1.0 - classification, classification);
+ focalWeight = alphaFactor * torch.pow(focalWeight, this.gamma);
+
+ var bce = -((targets * torch.log(classification)) +
+ ((1.0 - targets) * torch.log(1.0 - classification)));
+
+ var clsLoss = focalWeight * bce;
+ clsLoss = torch.where(targets.ne(-1.0), clsLoss,
+ torch.zeros(
+ clsLoss.shape,
+ dtype: ScalarType.Float32,
+ device: classifications.device));
+
+ var classificationLoss = clsLoss.sum() / torch.clamp(numPositiveAnchors.to_type(ScalarType.Float32), min: 1.0);
+ classificationLosses.Add(classificationLoss);
+
+ // compute the loss for regression
+ if (positiveIndices.sum().ToSingle() > 0)
+ {
+ assignedAnnotations = assignedAnnotations[positiveIndices];
+
+ var anchorWidthsPi = anchorWidths[positiveIndices];
+ var anchorHeightsPi = anchorHeights[positiveIndices];
+ var anchorCtrXPi = anchorCtrX[positiveIndices];
+ var anchorCtrYPi = anchorCtrY[positiveIndices];
+
+ var gtWidths = assignedAnnotations[.., 2] - assignedAnnotations[.., 0];
+ var gtHeights = assignedAnnotations[.., 3] - assignedAnnotations[.., 1];
+ var gtCtrX = assignedAnnotations[.., 0] + (0.5 * gtWidths);
+ var gtCtrY = assignedAnnotations[.., 1] + (0.5 * gtHeights);
+
+ // clip widths to 1
+ gtWidths = torch.clamp(gtWidths, min: 1);
+ gtHeights = torch.clamp(gtHeights, min: 1);
+
+ var targetsDx = (gtCtrX - anchorCtrXPi) / anchorWidthsPi;
+ var targetsDy = (gtCtrY - anchorCtrYPi) / anchorHeightsPi;
+
+ var targetsDw = torch.log(gtWidths / anchorWidthsPi);
+ var targetsDh = torch.log(gtHeights / anchorHeightsPi);
+
+ targets = torch.stack(new List { targetsDx, targetsDy, targetsDw, targetsDh });
+ targets = targets.t();
+ var factor = torch.from_array(new double[]
+ {
+ 0.1, 0.1, 0.2, 0.2
+ }).unsqueeze(0).to(classifications.device);
+ targets = targets / factor;
+
+ var negativeIndices = 1 + (~positiveIndices);
+
+ var regressionDiff = torch.abs(targets - regression[positiveIndices]);
+
+ var regressionLoss = torch.where(
+ regressionDiff.le(1.0 / 9.0),
+ 0.5 * 9.0 * torch.pow(regressionDiff, 2),
+ regressionDiff - (0.5 / 9.0));
+ regressionLosses.Add(regressionLoss.mean());
+ }
+ else
+ {
+ regressionLosses.Add(torch.tensor(0, dtype: ScalarType.Float32, device: classifications.device));
+ }
+ }
+ }
+
+ var finalClassificationLoss = torch.stack(classificationLosses).mean(dimensions: new long[] { 0 }, keepdim: true);
+ var finalRegressionLoss = torch.stack(regressionLosses).mean(dimensions: new long[] { 0 }, keepdim: true);
+ var loss = finalClassificationLoss.mean() + finalRegressionLoss.mean();
+ return loss;
+ }
+
+ private object ToTensorIndex()
+ {
+ throw new NotImplementedException();
+ }
+
+ private static Tensor CalcIou(Tensor a, Tensor b)
+ {
+ var area = (b[.., 2] - b[.., 0]) * (b[.., 3] - b[.., 1]);
+
+ var iw = torch.minimum(input: torch.unsqueeze(a[.., 2], dim: 1), b[.., 2]) -
+ torch.maximum(input: torch.unsqueeze(a[.., 0], 1), b[.., 0]);
+ var ih = torch.minimum(input: torch.unsqueeze(a[.., 3], dim: 1), b[.., 3]) -
+ torch.maximum(input: torch.unsqueeze(a[.., 1], 1), b[.., 1]);
+
+ iw = torch.clamp(iw, min: 0);
+ ih = torch.clamp(ih, min: 0);
+
+ var ua = torch.unsqueeze((a[.., 2] - a[.., 0]) * (a[.., 3] - a[.., 1]), dim: 1) + area - (iw * ih);
+ ua = torch.clamp(ua, min: 1e-8);
+
+ var intersection = iw * ih;
+ var iou = intersection / ua;
+
+ return iou;
+ }
+ }
+}
diff --git a/src/Microsoft.ML.TorchSharp/Microsoft.ML.TorchSharp.csproj b/src/Microsoft.ML.TorchSharp/Microsoft.ML.TorchSharp.csproj
index e1c1153518..d55779e964 100644
--- a/src/Microsoft.ML.TorchSharp/Microsoft.ML.TorchSharp.csproj
+++ b/src/Microsoft.ML.TorchSharp/Microsoft.ML.TorchSharp.csproj
@@ -3,13 +3,18 @@
netstandard2.0
- $(NoWarn);CS8002;MSB3270
+ $(NoWarn);CS8002
true
Microsoft.ML.TorchSharp contains ML.NET integration of TorchSharp.
AnyCPU
+
+
+ None
+
+
@@ -17,6 +22,7 @@
+
diff --git a/src/Microsoft.ML.TorchSharp/NasBert/Models/BaseModel.cs b/src/Microsoft.ML.TorchSharp/NasBert/Models/BaseModel.cs
index e99b463b53..e2b80188e2 100644
--- a/src/Microsoft.ML.TorchSharp/NasBert/Models/BaseModel.cs
+++ b/src/Microsoft.ML.TorchSharp/NasBert/Models/BaseModel.cs
@@ -13,18 +13,16 @@ namespace Microsoft.ML.TorchSharp.NasBert.Models
{
internal abstract class BaseModel : torch.nn.Module
{
- protected readonly NasBertTrainer.Options Options;
+ protected readonly NasBertTrainer.NasBertOptions Options;
public BertTaskType HeadType => Options.TaskType;
//public ModelType EncoderType => Options.ModelType;
#pragma warning disable CA1024 // Use properties where appropriate: Modules should be fields in TorchSharp
public abstract TransformerEncoder GetEncoder();
-
- public abstract BaseHead GetHead();
#pragma warning restore CA1024 // Use properties where appropriate
- protected BaseModel(TextClassificationTrainer.Options options)
+ protected BaseModel(NasBertTrainer.NasBertOptions options)
: base(nameof(BaseModel))
{
Options = options ?? throw new ArgumentNullException(nameof(options));
diff --git a/src/Microsoft.ML.TorchSharp/NasBert/Models/NasBertModel.cs b/src/Microsoft.ML.TorchSharp/NasBert/Models/NasBertModel.cs
index ec26eda919..2c76ebb165 100644
--- a/src/Microsoft.ML.TorchSharp/NasBert/Models/NasBertModel.cs
+++ b/src/Microsoft.ML.TorchSharp/NasBert/Models/NasBertModel.cs
@@ -15,12 +15,11 @@ internal class NasBertModel : BaseModel
{
private readonly PredictionHead _predictionHead;
- public override BaseHead GetHead() => _predictionHead;
public override TransformerEncoder GetEncoder() => Encoder;
protected readonly TransformerEncoder Encoder;
- public NasBertModel(TextClassificationTrainer.Options options, int padIndex, int symbolsCount, int numClasses)
+ public NasBertModel(NasBertTrainer.NasBertOptions options, int padIndex, int symbolsCount, int numClasses)
: base(options)
{
_predictionHead = new PredictionHead(
diff --git a/src/Microsoft.ML.TorchSharp/NasBert/Models/PredictionHead.cs b/src/Microsoft.ML.TorchSharp/NasBert/Models/PredictionHead.cs
index 5b6092d779..a449984401 100644
--- a/src/Microsoft.ML.TorchSharp/NasBert/Models/PredictionHead.cs
+++ b/src/Microsoft.ML.TorchSharp/NasBert/Models/PredictionHead.cs
@@ -11,7 +11,7 @@
namespace Microsoft.ML.TorchSharp.NasBert.Models
{
- internal sealed class PredictionHead : BaseHead, torch.nn.IModule
+ internal sealed class PredictionHead : torch.nn.Module
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_PrivateFieldName:Private field name not in: _camelCase format", Justification = "Has to match TorchSharp model.")]
private readonly Sequential Classifier;
@@ -34,7 +34,7 @@ public PredictionHead(int inputDim, int numClasses, double dropoutRate)
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "MSML_GeneralName:This name should be PascalCased", Justification = "Need to match TorchSharp")]
- public torch.Tensor forward(torch.Tensor features)
+ public override torch.Tensor forward(torch.Tensor features)
{
// TODO: try whitening-like techniques
// take token (equiv. to [CLS])
diff --git a/src/Microsoft.ML.TorchSharp/NasBert/Models/TransformerEncoder.cs b/src/Microsoft.ML.TorchSharp/NasBert/Models/TransformerEncoder.cs
index e9d8615085..256443fb82 100644
--- a/src/Microsoft.ML.TorchSharp/NasBert/Models/TransformerEncoder.cs
+++ b/src/Microsoft.ML.TorchSharp/NasBert/Models/TransformerEncoder.cs
@@ -16,7 +16,7 @@
namespace Microsoft.ML.TorchSharp.NasBert.Models
{
- internal sealed class TransformerEncoder : torch.nn.Module, torch.nn.IModule