chore: import upstream snapshot with attribution
Build / Build (macos-latest) (push) Has been cancelled
Build / Build (ubuntu-latest) (push) Has been cancelled
Build / Build (windows-latest) (push) Has been cancelled
Build / Analyze (javascript) (push) Has been cancelled
Build / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:45 +08:00
commit 7254f7b4d1
277 changed files with 596162 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
---
name: Bug report
about: Create a report to help us improve
labels: bug
---
- Netron app and version: <!-- For example: 3.8.2 Desktop app, 2.9.0 Website or 3.9.2 Python Server -->
- OS and browser version: <!-- For example macOS 10.15.6 (19G73) + Chrome 84.0.4147.89 -->
Steps to Reproduce:
1.
2.
Please attach or link model files to reproduce the issue.
@@ -0,0 +1,7 @@
---
name: Feature request
about: Suggest an idea for this project
labels: feature
---
<!-- Please search existing issues to avoid creating duplicates. -->
<!-- Describe the feature you'd like. -->
+55
View File
@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1120" height="280" viewBox="0 0 1120 280">
<g transform="matrix(2.08182,0,0,2.08182,-121.251,-49.4127)">
<g transform="matrix(100,0,0,100,60.9965,126)">
<path d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" fill="#C9D1D9" />
</g>
<g transform="matrix(100,0,0,100,164.341,126)">
<path d="M0.089,0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,0L0.089,0Z" fill="#C9D1D9" />
</g>
<g transform="matrix(100,0,0,100,244.491,126)">
<path d="M0.216,0L0.216,-0.654L0.019,-0.654L0.019,-0.715L0.478,-0.715L0.478,-0.654L0.281,-0.654L0.281,0L0.216,0Z" fill="#C9D1D9" />
</g>
<g transform="matrix(100,0,0,100,323.031,126)">
<path d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,0L0.444,0L0.193,-0.341L0.154,-0.341L0.154,0L0.089,0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,0Z" fill="#C9D1D9" />
</g>
<g transform="matrix(0.10084,0,0,0.10084,398.318,38.8234)">
<circle cx="512" cy="512" r="431" fill="none" stroke="#C9D1D9" stroke-width="42px"/>
<path d="M296,392L540,280" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M296,632L540,280" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M296,392L540,435" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M296,632L540,435" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M296,392L540,590" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M296,632L540,590" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M296,392L540,744" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M296,632L540,744" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M540,280L785,512" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M540,590L785,512" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M540,435L785,512" stroke="#C9D1D9" stroke-width="12px"/>
<path d="M540,744L785,512" stroke="#C9D1D9" stroke-width="12px"/>
<g transform="translate(296,392)">
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
</g>
<g transform="translate(296,632)">
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
</g>
<g transform="translate(540,280)">
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
</g>
<g transform="translate(540,435)">
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
</g>
<g transform="translate(540,590)">
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
</g>
<g transform="translate(540,744)">
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
</g>
<g transform="translate(785,512)">
<circle cx="0" cy="0" r="51" fill="#C9D1D9" />
</g>
</g>
<g transform="matrix(100,0,0,100,520.979,126)">
<path d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" fill="#C9D1D9" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

+55
View File
@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1120" height="280" viewBox="0 0 1120 280">
<g transform="matrix(2.08182,0,0,2.08182,-121.251,-49.4127)">
<g transform="matrix(100,0,0,100,60.9965,126)">
<path d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" fill="#24292F" />
</g>
<g transform="matrix(100,0,0,100,164.341,126)">
<path d="M0.089,0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,0L0.089,0Z" fill="#24292F" />
</g>
<g transform="matrix(100,0,0,100,244.491,126)">
<path d="M0.216,0L0.216,-0.654L0.019,-0.654L0.019,-0.715L0.478,-0.715L0.478,-0.654L0.281,-0.654L0.281,0L0.216,0Z" fill="#24292F" />
</g>
<g transform="matrix(100,0,0,100,323.031,126)">
<path d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,0L0.444,0L0.193,-0.341L0.154,-0.341L0.154,0L0.089,0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,0Z" fill="#24292F" />
</g>
<g transform="matrix(0.10084,0,0,0.10084,398.318,38.8234)">
<circle cx="512" cy="512" r="431" fill="none" stroke="#24292F" stroke-width="42px"/>
<path d="M296,392L540,280" stroke="#24292F" stroke-width="12px"/>
<path d="M296,632L540,280" stroke="#24292F" stroke-width="12px"/>
<path d="M296,392L540,435" stroke="#24292F" stroke-width="12px"/>
<path d="M296,632L540,435" stroke="#24292F" stroke-width="12px"/>
<path d="M296,392L540,590" stroke="#24292F" stroke-width="12px"/>
<path d="M296,632L540,590" stroke="#24292F" stroke-width="12px"/>
<path d="M296,392L540,744" stroke="#24292F" stroke-width="12px"/>
<path d="M296,632L540,744" stroke="#24292F" stroke-width="12px"/>
<path d="M540,280L785,512" stroke="#24292F" stroke-width="12px"/>
<path d="M540,590L785,512" stroke="#24292F" stroke-width="12px"/>
<path d="M540,435L785,512" stroke="#24292F" stroke-width="12px"/>
<path d="M540,744L785,512" stroke="#24292F" stroke-width="12px"/>
<g transform="translate(296,392)">
<circle cx="0" cy="0" r="51" fill="#24292F" />
</g>
<g transform="translate(296,632)">
<circle cx="0" cy="0" r="51" fill="#24292F" />
</g>
<g transform="translate(540,280)">
<circle cx="0" cy="0" r="51" fill="#24292F" />
</g>
<g transform="translate(540,435)">
<circle cx="0" cy="0" r="51" fill="#24292F" />
</g>
<g transform="translate(540,590)">
<circle cx="0" cy="0" r="51" fill="#24292F" />
</g>
<g transform="translate(540,744)">
<circle cx="0" cy="0" r="51" fill="#24292F" />
</g>
<g transform="translate(785,512)">
<circle cx="0" cy="0" r="51" fill="#24292F" />
</g>
</g>
<g transform="matrix(100,0,0,100,520.979,126)">
<path d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" fill="#24292F" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 KiB

+86
View File
@@ -0,0 +1,86 @@
name: Build
on:
push:
branches: [ '**' ]
tags-ignore: [ '**' ]
pull_request:
branches: [ main ]
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
matrix:
os: [ macos-latest, ubuntu-latest, windows-latest ]
steps:
- name: Clone Git repository
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: '24.15'
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Install Packages
run: npm run install
- name: Validate
run: npm run validate
- name: Build Python Server
run: npm run build python
- name: Build Electron
shell: bash
run: |
npx electron-builder install-app-deps
case "${{ matrix.os }}" in
macos*)
npm run build electron mac
;;
ubuntu*)
sudo apt-get install rpm --yes
npm run build electron linux
;;
windows*)
npm run build electron windows
;;
esac
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'python' ]
steps:
- name: Clone Git repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
+96
View File
@@ -0,0 +1,96 @@
name: Publish
on:
push:
tags: [ 'v**' ]
jobs:
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ macos-latest, ubuntu-latest, windows-latest ]
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Git credentials
run: |
git config --global user.name ${{ secrets.PUBLISH_GITHUB_USER_NAME }}
git config --global user.email ${{ secrets.PUBLISH_GITHUB_USER_EMAIL }}
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: latest
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Install Packages
run: npm run install
- name: Publish Electron
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.PUBLISH_GITHUB_TOKEN }}
APPLE_API_KEY: ~/.private_keys/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
run: |
npx electron-builder install-app-deps
case "${{ matrix.os }}" in
macos*)
mkdir -p ~/.private_keys
echo '${{ secrets.APPLE_API_KEY }}' > ~/.private_keys/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8
npm run publish electron mac
;;
ubuntu*)
sudo apt-get install rpm --yes
npm run publish electron linux
;;
windows*)
unset CSC_LINK;
unset CSC_KEY_PASSWORD;
npm run publish electron windows
;;
esac
- if: startsWith(matrix.os, 'ubuntu')
name: Publish Python
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
run: npm run publish python
- if: startsWith(matrix.os, 'ubuntu')
name: Publish Web
env:
GITHUB_TOKEN: ${{ secrets.PUBLISH_GITHUB_TOKEN }}
GITHUB_USER: ${{ secrets.PUBLISH_GITHUB_USER }}
run: git push origin HEAD:release --force
- if: false # startsWith(matrix.os, 'macos')
name: Publish cask
env:
GITHUB_TOKEN: ${{ secrets.PUBLISH_GITHUB_TOKEN }}
GITHUB_USER: ${{ secrets.PUBLISH_GITHUB_USER }}
run: npm run publish cask
- if: startsWith(matrix.os, 'windows')
name: Publish winget
env:
GITHUB_USER: ${{ secrets.PUBLISH_GITHUB_USER }}
GITHUB_TOKEN: ${{ secrets.PUBLISH_GITHUB_TOKEN }}
WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }}
run: npm run publish winget
+13
View File
@@ -0,0 +1,13 @@
.DS_Store
.DS_Store?
.claude
.eslintcache
.ruff_cache
.specify
.yarn
dist/*
node_modules/*
test-results/*
third_party/*
yarn.lock
*.pyc
+1
View File
@@ -0,0 +1 @@
loglevel=silent
+106
View File
@@ -0,0 +1,106 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Desktop View",
"type": "chrome",
"request": "launch",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"runtimeArgs": [
"${workspaceRoot}",
"--enable-logging",
"--remote-debugging-port=9222"
],
"webRoot": "${workspaceRoot}"
},
{
"name": "Desktop App",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/source/app.js",
"runtimeExecutable": "electron",
"osx": {
"runtimeExecutable": "${workspaceFolder}/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron"
}
},
{
"name": "Browser",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/package.py",
"args": [ "build", "start" ],
"justMyCode": true,
"pathMappings": [
{
"localRoot": "${workspaceFolder}/source",
"remoteRoot": "${workspaceFolder}/dist/pypi/netron",
}
],
"serverReadyAction": {
"action": "debugWithChrome",
"pattern": "Serving .*at http://localhost:([0-9]+)",
"uriFormat": "http://localhost:%s",
"webRoot": "${workspaceFolder}/source",
},
},
{
"name": "Test Models",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/test/models.js",
"args": [ "tag:validation" ],
"console": "integratedTerminal",
},
{
"name": "Test Desktop",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "test", "desktop"],
"console": "integratedTerminal",
},
{
"name": "Test Browser",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "test", "browser"],
"console": "integratedTerminal",
},
{
"name": "Test Backend",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/test/backend.py",
"args": [],
"console": "integratedTerminal",
"justMyCode": true,
"serverReadyAction": {
"action": "debugWithChrome",
"pattern": "Serving .*at http://localhost:([0-9]+)",
"uriFormat": "http://localhost:%s",
"webRoot": "${workspaceFolder}/source",
},
},
{
"name": "Tools Python",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"args": [],
"console": "integratedTerminal",
"justMyCode": true
},
{
"name": "Tools JavaScript",
"type": "node",
"request": "launch",
"program": "${file}",
"args": [],
"console": "integratedTerminal",
}
]
}
+13
View File
@@ -0,0 +1,13 @@
{
"eslint.lintTask.enable": true,
"files.exclude": {
"dist": true,
"node_modules": true,
"third_party": true
},
"search.exclude": {
"dist": true,
"node_modules": true,
"third_party": true
}
}
+12
View File
@@ -0,0 +1,12 @@
cff-version: 1.2.0
title: "Netron, Visualizer for neural network, deep learning, and machine learning models"
authors:
- family-names: "Roeder"
given-names: "Lutz"
orcid: "https://orcid.org/0000-0003-4994-7839"
doi: "10.5281/zenodo.5854962"
date-released: 2017-12-04
license: "MIT"
url: "https://github.com/lutzroeder/netron"
repository-code: "https://github.com/lutzroeder/netron"
message: "If you use Netron in your research, please cite it using these metadata."
+31
View File
@@ -0,0 +1,31 @@
# How to Develop Netron
## Debugging
Netron can run as both an [Electron](https://electronjs.org) app or a web app.
To start the Electron app, install [Node.js](https://nodejs.org) and run:
```bash
git clone https://github.com/lutzroeder/netron
cd netron
npm install
npm start
```
To debug the Electron app, open the folder in [Visual Studio Code](https://code.visualstudio.com) and press <kbd>F5</kbd>. To attach the debugger to the render process select the `Debug` tab and pick `Desktop View` before launching.
To build and launch the web app, pick `Browser` in the `Debug` tab or run this command:
```bash
python package.py build start --browse
```
## Validation
To validate changes run:
```bash
npm run lint
npm test [format] # e.g. npm test onnx
```
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Lutz Roeder
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+39
View File
@@ -0,0 +1,39 @@
<div align="center">
<img width="400px" height="100px" src="https://github.com/lutzroeder/netron/raw/main/.github/logo-light.svg#gh-light-mode-only">
<img width="400px" height="100px" src="https://github.com/lutzroeder/netron/raw/main/.github/logo-dark.svg#gh-dark-mode-only">
</div>
Netron is a viewer for neural network, deep learning and machine learning models.
Netron supports ONNX, TensorFlow Lite, PyTorch, torch.export, ExecuTorch, TorchScript, TensorFlow, Core ML, OpenVINO, Keras, Caffe, Darknet, Safetensors and NumPy.
Netron has experimental support for MLIR, JAX, GGUF, RKNN, ncnn, MNN, PaddlePaddle and scikit-learn.
<p align='center'><a href='https://www.lutzroeder.com/ai'><img src='.github/screenshot.png' width='800'></a></p>
## Install
**Browser**: [**Start**](https://netron.app) the browser version.
**macOS**: [**Download**](https://github.com/lutzroeder/netron/releases/latest) the `.dmg` file or run `brew install --cask netron`.
**Linux**: [**Download**](https://github.com/lutzroeder/netron/releases/latest) the `.deb` or `.rpm` file.
**Windows**: [**Download**](https://github.com/lutzroeder/netron/releases/latest) the `.exe` installer or run `winget install -s winget netron`.
**Python**: `pip install netron`, then run `netron [FILE]` or `netron.start('[FILE]')`.
## Models
Sample model files to download or open using the browser version:
* **ONNX**: [squeezenet](https://github.com/onnx/models/raw/main/validated/vision/classification/squeezenet/model/squeezenet1.0-3.onnx) [[open](https://netron.app?url=https://github.com/onnx/models/raw/main/validated/vision/classification/squeezenet/model/squeezenet1.0-3.onnx)]
* **TorchScript**: [traced_online_pred_layer](https://github.com/ApolloAuto/apollo/raw/master/modules/prediction/data/traced_online_pred_layer.pt) [[open](https://netron.app?url=https://github.com/ApolloAuto/apollo/raw/master/modules/prediction/data/traced_online_pred_layer.pt)]
* **TensorFlow Lite**: [yamnet](https://huggingface.co/thelou1s/yamnet/resolve/main/lite-model_yamnet_tflite_1.tflite) [[open](https://netron.app?url=https://huggingface.co/thelou1s/yamnet/blob/main/lite-model_yamnet_tflite_1.tflite)]
* **TensorFlow**: [chessbot](https://github.com/srom/chessbot/raw/master/model/chessbot.pb) [[open](https://netron.app?url=https://github.com/srom/chessbot/raw/master/model/chessbot.pb)]
* **Keras**: [mobilenet](https://github.com/aio-libs/aiohttp-demos/raw/master/demos/imagetagger/tests/data/mobilenet.h5) [[open](https://netron.app?url=https://github.com/aio-libs/aiohttp-demos/raw/master/demos/imagetagger/tests/data/mobilenet.h5)]
* **MLIR**: [edge_detection](https://github.com/iree-org/iree/raw/main/tests/e2e/stablehlo_models/edge_detection.mlir) [[open](https://netron.app?url=https://github.com/iree-org/iree/blob/main/tests/e2e/stablehlo_models/edge_detection.mlir)]
* **Core ML**: [exermote](https://github.com/Lausbert/Exermote/raw/master/ExermoteInference/ExermoteCoreML/ExermoteCoreML/Model/Exermote.mlmodel) [[open](https://netron.app?url=https://github.com/Lausbert/Exermote/raw/master/ExermoteInference/ExermoteCoreML/ExermoteCoreML/Model/Exermote.mlmodel)]
* **Darknet**: [yolo](https://github.com/AlexeyAB/darknet/raw/master/cfg/yolo.cfg) [[open](https://netron.app?url=https://github.com/AlexeyAB/darknet/raw/master/cfg/yolo.cfg)]
+7
View File
@@ -0,0 +1,7 @@
# WeHub 来源说明
- 原始项目:`lutzroeder/netron`
- 原始仓库:https://github.com/lutzroeder/netron
- 导入方式:上游默认分支的最新快照
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
+256
View File
@@ -0,0 +1,256 @@
export default [
{
ignores: [
'dist/**',
'third_party/**',
]
},
{
languageOptions: {
globals: {
atob: 'readonly',
BigInt: 'readonly',
console: 'readonly',
global: 'readonly',
process: 'readonly',
self: 'readonly',
TextDecoder: 'readonly',
TextEncoder: 'readonly',
window: 'readonly',
},
sourceType: 'module'
},
rules: {
'accessor-pairs': 'error',
'array-bracket-spacing': 'error',
'array-callback-return': 'error',
// 'arrow-body-style': 'error',
'arrow-parens': 'error',
'block-scoped-var': 'error',
'brace-style': 'error',
// 'camelcase': 'error',
// 'capitalized-comments': 'error',
// 'class-methods-use-this': 'error',
// 'complexity': 'error',
'computed-property-spacing': 'error',
'consistent-return': 'error',
'consistent-this': 'error',
'constructor-super': 'error',
'curly': 'error',
'default-case': 'error',
'default-case-last': 'error',
'default-param-last': 'error',
'dot-notation': 'error',
'eqeqeq': 'error',
'for-direction': 'error',
'func-name-matching': 'error',
// 'func-names': 'error',
'func-style': 'error',
'getter-return': 'error',
'grouped-accessor-pairs': 'error',
'guard-for-in': 'error',
'id-denylist': 'error',
// 'id-length': 'error',
'id-match': 'error',
'indent': ['error', 4, { 'SwitchCase': 1 }],
'init-declarations': 'error',
'keyword-spacing': 'error',
// 'line-comment-position': 'error',
// 'logical-assignment-operators': 'error',
// 'max-classes-per-file': 'error',
// 'max-depth': 'error',
// 'max-lines': 'error',
// 'max-lines-per-function': 'error',
'max-nested-callbacks': 'error',
// 'max-params': 'error',
// 'max-statements': 'error',
// 'multiline-comment-style': 'error',
// 'new-cap': 'error',
'no-alert': 'error',
'no-array-constructor': 'error',
'no-async-promise-executor': 'error',
'no-await-in-loop': 'error',
// 'no-bitwise': 'error',
'no-caller': 'error',
'no-case-declarations': 'error',
'no-class-assign': 'error',
'no-compare-neg-zero': 'error',
'no-cond-assign': 'error',
'no-console': 'error',
'no-const-assign': 'error',
'no-constant-binary-expression': 'error',
'no-constant-condition': 'error',
'no-constructor-return': 'error',
// 'no-continue': 'error',
'no-control-regex': 'error',
'no-debugger': 'error',
'no-delete-var': 'error',
'no-div-regex': 'error',
'no-dupe-args': 'error',
'no-dupe-class-members': 'error',
'no-dupe-else-if': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-duplicate-imports': 'error',
'no-else-return': 'error',
'no-empty': 'error',
'no-empty-character-class': 'error',
// 'no-empty-function': 'error',
'no-empty-pattern': 'error',
'no-empty-static-block': 'error',
'no-eq-null': 'error',
'no-eval': 'error',
'no-ex-assign': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-extra-boolean-cast': 'error',
'no-extra-label': 'error',
'no-extra-semi': 'error',
'no-fallthrough': 'error',
'no-func-assign': 'error',
'no-global-assign': 'error',
'no-implicit-coercion': 'error',
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-import-assign': 'error',
// 'no-inline-comments': 'error',
'no-inner-declarations': 'error',
'no-invalid-regexp': 'error',
'no-invalid-this': 'error',
'no-irregular-whitespace': 'error',
'no-iterator': 'error',
'no-label-var': 'error',
'no-labels': 'error',
'no-lone-blocks': 'error',
'no-lonely-if': 'error',
'no-loop-func': 'error',
'no-loss-of-precision': 'error',
// 'no-magic-numbers': 'error',
'no-misleading-character-class': 'error',
'no-multi-assign': 'error',
'no-multi-str': 'error',
'no-multiple-empty-lines': ['error', { 'max': 1 }],
'no-negated-condition': 'error',
'no-nested-ternary': 'error',
'no-new': 'error',
'no-new-func': 'error',
'no-new-native-nonconstructor': 'error',
'no-new-wrappers': 'error',
'no-nonoctal-decimal-escape': 'error',
'no-obj-calls': 'error',
'no-object-constructor': 'error',
'no-octal': 'error',
'no-octal-escape': 'error',
// 'no-param-reassign': 'error',
// 'no-plusplus': 'error',
'no-promise-executor-return': 'error',
'no-proto': 'error',
'no-prototype-builtins': 'error',
'no-redeclare': 'error',
'no-regex-spaces': 'error',
'no-restricted-exports': 'error',
'no-restricted-globals': 'error',
'no-restricted-imports': 'error',
'no-restricted-properties': 'error',
'no-restricted-syntax': 'error',
'no-return-assign': 'error',
'no-script-url': 'error',
'no-self-assign': 'error',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-setter-return': 'error',
// 'no-shadow': 'error',
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
'no-template-curly-in-string': 'error',
// 'no-ternary': 'error',
'no-this-before-super': 'error',
'no-throw-literal': 'error',
'no-trailing-spaces': 'error',
'no-undef': 'error',
'no-undef-init': 'error',
// 'no-undefined': 'error',
// 'no-underscore-dangle': 'error',
'no-unexpected-multiline': 'error',
'no-unmodified-loop-condition': 'error',
// 'no-unneeded-ternary': 'error',
'no-unreachable': 'error',
'no-unreachable-loop': 'error',
'no-unsafe-finally': 'error',
'no-unsafe-negation': 'error',
'no-unsafe-optional-chaining': 'error',
'no-unused-expressions': 'error',
'no-unused-labels': 'error',
'no-unused-private-class-members': 'error',
'no-unused-vars': 'error',
'no-use-before-define': 'error',
// 'no-useless-assignment': 'error',
'no-useless-backreference': 'error',
'no-useless-call': 'error',
'no-useless-catch': 'error',
'no-useless-computed-key': 'error',
'no-useless-concat': 'error',
'no-useless-constructor': 'error',
'no-useless-escape': 'error',
'no-useless-rename': 'error',
'no-useless-return': 'error',
'no-var': 'error',
'no-void': 'error',
'no-warning-comments': 'error',
'no-with': 'error',
'object-curly-spacing': ['error', 'always'],
'object-shorthand': 'error',
// 'one-var': 'error',
'operator-assignment': 'error',
'prefer-arrow-callback': 'error',
'prefer-const': 'error',
'prefer-destructuring': ['error', { 'array': false }],
// 'prefer-exponentiation-operator': 'error',
// 'prefer-named-capture-group': 'error',
'prefer-numeric-literals': 'error',
// 'prefer-object-has-own': 'error',
'prefer-object-spread': 'error',
'prefer-promise-reject-errors': 'error',
'prefer-regex-literals': 'error',
'prefer-rest-params': 'error',
'prefer-spread': 'error',
'prefer-template': 'error',
'radix': 'error',
'require-atomic-updates': 'error',
// 'require-await': 'error',
// 'require-unicode-regexp': 'error',
'require-yield': 'error',
'semi': ['error', 'always'],
'sort-imports': 'error',
// 'sort-keys': 'error',
'sort-vars': 'error',
'space-before-blocks': 'error',
'space-in-parens': 'error',
'space-infix-ops': 'error',
'strict': 'error',
'symbol-description': 'error',
'template-curly-spacing': 'error',
'unicode-bom': 'error',
'use-isnan': 'error',
'valid-typeof': 'error',
'vars-on-top': 'error',
'yoda': 'error'
}
},
{
files: ['source/index.js'],
languageOptions: {
sourceType: 'script',
ecmaVersion: 2015
},
rules: {
'no-var': 'off',
'prefer-arrow-callback': 'off',
'prefer-template': 'off',
'prefer-destructuring': 'off',
'vars-on-top': 'off',
'strict': 'off'
}
}
];
+4403
View File
File diff suppressed because it is too large Load Diff
+774
View File
@@ -0,0 +1,774 @@
import * as child_process from 'child_process';
import * as crypto from 'crypto';
import * as fs from 'fs/promises';
import * as os from 'os';
import * as path from 'path';
import * as url from 'url';
const args = process.argv.slice(2);
const read = (match) => {
if (args.length > 0 && (!match || args[0] === match)) {
return args.shift();
}
return null;
};
let configuration = null;
const dirname = (...args) => {
const file = url.fileURLToPath(import.meta.url);
const dir = path.dirname(file);
return path.join(dir, ...args);
};
const load = async () => {
const file = dirname('package.json');
const content = await fs.readFile(file, 'utf-8');
configuration = JSON.parse(content);
};
const clearLine = () => {
if (process.stdout.clearLine) {
process.stdout.clearLine();
}
};
const write = (message) => {
if (process.stdout.write) {
process.stdout.write(message);
}
};
const writeLine = (message) => {
write(message + os.EOL);
};
const access = async (path) => {
try {
await fs.access(path);
return true;
} catch {
return false;
}
};
const rm = async (...args) => {
const dir = dirname(...args);
const exists = await access(dir);
if (exists) {
const paths = path.join(...args);
writeLine(`rm ${paths}`);
const options = { recursive: true, force: true };
await fs.rm(dir, options);
}
};
const mkdir = async (...args) => {
const dir = dirname(...args);
const exists = await access(dir);
if (!exists) {
const paths = path.join(...args);
writeLine(`mkdir ${paths}`);
const options = { recursive: true };
await fs.mkdir(dir, options);
}
return dir;
};
const copy = async (source, target, filter) => {
let files = await fs.readdir(source);
files = filter ? files.filter((file) => filter(file)) : files;
const promises = files.map((file) => fs.copyFile(path.join(source, file), path.join(target, file)));
await Promise.all(promises);
};
const unlink = async (dir, filter) => {
let files = await fs.readdir(dir);
files = filter ? files.filter((file) => filter(file)) : files;
const promises = files.map((file) => fs.unlink(path.join(dir, file)));
await Promise.all(promises);
};
const exec = async (command, encoding, cwd) => {
cwd = cwd || dirname();
if (encoding) {
return child_process.spawnSync(command, { shell: true, cwd, encoding });
}
child_process.execSync(command, { cwd, stdio: [0,1,2] });
return '';
};
const sleep = (delay) => {
return new Promise((resolve) => {
global.setTimeout(resolve, delay);
});
};
const request = async (url, init, status) => {
const response = await global.fetch(url, init);
if (status !== false && !response.ok) {
throw new Error(`${response.status.toString()} ${response.statusText}`);
}
if (response.body) {
const reader = response.body.getReader();
let position = 0;
const stream = new global.ReadableStream({
start(controller) {
const read = async () => {
try {
const result = await reader.read();
if (result.done) {
clearLine();
controller.close();
} else {
position += result.value.length;
write(` ${position} bytes\r`);
controller.enqueue(result.value);
read();
}
} catch (error) {
controller.error(error);
}
};
read();
}
});
return new global.Response(stream, {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
}
return response;
};
const download = async (url) => {
writeLine(`download ${url}`);
const response = await request(url);
return response.arrayBuffer().then((buffer) => new Uint8Array(buffer));
};
const hash = async (url, algorithm) => {
const data = await download(url);
const hash = crypto.createHash(algorithm);
hash.update(data);
return hash.digest('hex');
};
const fork = async (organization, repository) => {
const headers = {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`
};
writeLine(`github delete ${repository}`);
await request(`https://api.github.com/repos/${process.env.GITHUB_USER}/${repository}`, {
method: 'DELETE',
headers
}, false);
await sleep(4000);
writeLine(`github fork ${repository}`);
await request(`https://api.github.com/repos/${organization}/${repository}/forks`, {
method: 'POST',
headers,
body: ''
});
await sleep(4000);
await rm('dist', repository);
writeLine(`github clone ${repository}`);
await exec(`git clone --depth=2 https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_USER}/${repository}.git dist/${repository}`);
};
const pullrequest = async (organization, repository, token, body) => {
writeLine(`github push ${repository}`);
await exec(`git -C dist/${repository} push`);
writeLine(`github pullrequest ${repository}`);
const headers = {
Authorization: `Bearer ${token}`
};
await request(`https://api.github.com/repos/${organization}/${repository}/pulls`, {
method: 'POST',
headers,
body: JSON.stringify(body)
});
};
const clean = async () => {
await rm('dist');
await rm('node_modules');
await rm('package-lock.json');
await rm('yarn.lock');
if (read('purge')) {
await rm('third_party');
}
};
const install = async () => {
const node_modules = dirname('node_modules');
let exists = await access(node_modules);
if (exists) {
const dependencies = { ...configuration.dependencies, ...configuration.devDependencies };
const matches = await Promise.all(Object.entries(dependencies).map(async ([name, version]) => {
const file = path.join('node_modules', name, 'package.json');
const exists = await access(file);
if (exists) {
const content = await fs.readFile(file, 'utf8');
const obj = JSON.parse(content);
return obj.version === version;
}
return false;
}));
exists = matches.every((match) => match);
if (!exists) {
await clean();
}
}
exists = await access(node_modules);
if (!exists) {
await exec('npm install');
}
await exec('npx install-electron');
try {
await exec('python --version', 'utf-8');
await exec('python -m pip install --upgrade --quiet setuptools ruff');
} catch {
// continue regardless of error
}
};
const start = async () => {
await install();
await exec('npx electron .');
};
const build = async (target) => {
switch (target || read()) {
case 'web': {
writeLine('build web');
await rm('dist', 'web');
await mkdir('dist', 'web');
writeLine('cp source/dir dist/dir');
const source_dir = dirname('source');
const dist_dir = dirname('dist', 'web');
const extensions = new Set(['html', 'css', 'js', 'json', 'ico', 'png']);
await copy(source_dir, dist_dir, (file) => extensions.has(file.split('.').pop()));
await rm('dist', 'web', 'app.js');
await rm('dist', 'web', 'node.js');
await rm('dist', 'web', 'desktop.mjs');
const contentFile = dirname('dist', 'web', 'index.html');
let content = await fs.readFile(contentFile, 'utf-8');
content = content.replace(/(<meta\s*name="version"\s*content=")(.*)(">)/m, (match, p1, p2, p3) => {
return p1 + configuration.version + p3;
});
content = content.replace(/(<meta\s*name="date"\s*content=")(.*)(">)/m, (match, p1, p2, p3) => {
return p1 + configuration.date + p3;
});
await fs.writeFile(contentFile, content, 'utf-8');
break;
}
case 'electron': {
const key = read();
const target = key ? `electron ${key}` : 'electron';
writeLine(`build ${target}`);
await install();
await exec('npx electron-builder install-app-deps');
const table = new Map([
['mac', 'npx electron-builder --mac --universal --publish never --config.mac.identity=null'],
['windows', 'npx electron-builder --win --x64 --arm64 --publish never --config.win.azureSignOptions='],
['linux', 'npx electron-builder --linux --publish never']
]);
const targets = table.has(key) ? [table.get(key)] : Array.from(table.values());
for (const target of targets) {
// eslint-disable-next-line no-await-in-loop
await exec(target);
}
break;
}
case 'python': {
writeLine('build python');
await exec('python package.py build version');
await exec('python -m pip install --user build wheel --quiet');
await exec('python -m build --wheel --outdir dist/pypi dist/pypi');
if (read('install')) {
await exec('python -m pip install --force-reinstall dist/pypi/*.whl');
}
break;
}
default: {
writeLine('build');
await rm('dist');
await install();
await build('web');
await build('electron');
await build('python');
break;
}
}
};
const publish = async (target) => {
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const GITHUB_USER = process.env.GITHUB_USER;
switch (target || read()) {
case 'web': {
writeLine('publish web');
await build('web');
await rm('dist', 'gh-pages');
const url = `https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_USER}/netron.git`;
await exec(`git clone --depth=1 ${url} --branch gh-pages ./dist/gh-pages 2>&1 > /dev/null`);
writeLine('cp dist/web dist/gh-pages');
const source_dir = dirname('dist', 'web');
const target_dir = dirname('dist', 'gh-pages');
await unlink(target_dir, (file) => file !== '.git');
await copy(source_dir, target_dir);
await exec('git -C dist/gh-pages add --all');
await exec('git -C dist/gh-pages commit --amend --no-edit');
await exec('git -C dist/gh-pages push --force origin gh-pages');
break;
}
case 'electron': {
const key = read();
const target = key ? ` ${key}` : '';
writeLine(`publish electron ${target}`);
await install();
await exec('npx electron-builder install-app-deps');
const table = new Map([
['mac', 'npx electron-builder --mac --universal --publish always'],
['windows', 'npx electron-builder --win --x64 --arm64 --publish always'],
['linux', 'npx electron-builder --linux --publish always']
]);
const targets = table.has(key) ? [table.get(key)] : Array.from(table.values());
for (const target of targets) {
// eslint-disable-next-line no-await-in-loop
await exec(target);
}
break;
}
case 'python': {
writeLine('publish python');
await build('python');
await exec('python -m pip install --user twine');
await exec('python -m twine upload --non-interactive --skip-existing --verbose dist/pypi/*.whl');
break;
}
case 'cask': {
writeLine('publish cask');
await fork('Homebrew', 'homebrew-cask');
const repository = `https://github.com/${configuration.repository}`;
const url = `${repository}/releases/download/v#{version}/${configuration.productName}-#{version}-mac.zip`;
const sha256 = await hash(url.replace(/#{version}/g, configuration.version), 'sha256');
writeLine('update manifest');
const dir = await mkdir('dist', 'homebrew-cask', 'Casks', 'n');
const file = path.join(dir, 'netron.rb');
await fs.writeFile(file, [
`cask "${configuration.name}" do`,
` version "${configuration.version}"`,
` sha256 "${sha256.toLowerCase()}"`,
'',
` url "${url}"`,
` name "${configuration.productName}"`,
` desc "${configuration.description.replace('Visualizer', 'Visualiser')}"`,
` homepage "${repository}"`,
'',
' auto_updates true',
'',
` app "${configuration.productName}.app"`,
'',
' zap trash: [',
` "~/Library/Application Support/${configuration.productName}",`,
` "~/Library/Preferences/${configuration.build.appId}.plist",`,
` "~/Library/Saved Application State/${configuration.build.appId}.savedState",`,
' ]',
'end',
''
].join('\n'));
writeLine('git push homebrew-cask');
await exec('git -C dist/homebrew-cask add --all');
await exec(`git -C dist/homebrew-cask commit -m "${configuration.name} ${configuration.version}"`);
await pullrequest('Homebrew', 'homebrew-cask', process.env.GITHUB_TOKEN, {
title: `${configuration.name} ${configuration.version}`,
body: 'Update version and sha256',
head: `${process.env.GITHUB_USER}:master`,
base: 'master'
});
await rm('dist', 'homebrew-cask');
break;
}
case 'winget': {
writeLine('publish winget');
await fork('microsoft', 'winget-pkgs');
const name = configuration.name;
const version = configuration.version;
const product = configuration.productName;
const publisher = configuration.author.name;
const identifier = `${publisher.replace(' ', '')}.${product}`;
const copyright = `Copyright (c) ${publisher}`;
const repository = `https://github.com/${configuration.repository}`;
const url = `${repository}/releases/download/v${version}/${product}-Setup-${version}.exe`;
const content = await fs.readFile(configuration.build.extends, 'utf-8');
const builder = JSON.parse(content);
const extensions = builder.fileAssociations.map((entry) => `- ${entry.ext}`).sort().join('\n');
const sha256 = await hash(url, 'sha256');
const paths = ['dist', 'winget-pkgs', 'manifests', publisher[0].toLowerCase(), publisher.replace(' ', ''), product, version];
await mkdir(...paths);
writeLine('update manifest');
const manifestFile = dirname(...paths, identifier);
await fs.writeFile(`${manifestFile}.yaml`, [
'# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.12.0.schema.json',
`PackageIdentifier: ${identifier}`,
`PackageVersion: ${version}`,
'DefaultLocale: en-US',
'ManifestType: version',
'ManifestVersion: 1.12.0',
''
].join('\n'));
await fs.writeFile(`${manifestFile}.installer.yaml`, [
'# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.12.0.schema.json',
`PackageIdentifier: ${identifier}`,
`PackageVersion: ${version}`,
'Platform:',
'- Windows.Desktop',
'InstallModes:',
'- silent',
'- silentWithProgress',
'Installers:',
'- Architecture: x86',
' Scope: user',
' InstallerType: nullsoft',
` InstallerUrl: ${url}`,
` InstallerSha256: ${sha256.toUpperCase()}`,
' InstallerLocale: en-US',
' InstallerSwitches:',
' Custom: /NORESTART',
' UpgradeBehavior: install',
'- Architecture: arm64',
' Scope: user',
' InstallerType: nullsoft',
` InstallerUrl: ${url}`,
` InstallerSha256: ${sha256.toUpperCase()}`,
' InstallerLocale: en-US',
' InstallerSwitches:',
' Custom: /NORESTART',
' UpgradeBehavior: install',
'FileExtensions:',
extensions,
'ManifestType: installer',
'ManifestVersion: 1.12.0',
''
].join('\n'));
await fs.writeFile(`${manifestFile}.locale.en-US.yaml`, [
'# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.12.0.schema.json',
`PackageIdentifier: ${identifier}`,
`PackageVersion: ${version}`,
`PackageName: ${product}`,
'PackageLocale: en-US',
`PackageUrl: ${repository}`,
`Publisher: ${publisher}`,
`PublisherUrl: ${repository}`,
`PublisherSupportUrl: ${repository}/issues`,
`Author: ${publisher}`,
`License: ${configuration.license}`,
`Copyright: ${copyright}`,
`CopyrightUrl: ${repository}/blob/main/LICENSE`,
`ShortDescription: ${configuration.description}`,
`Description: ${configuration.description}`,
`Moniker: ${name}`,
'Tags:',
'- machine-learning',
'- deep-learning',
'- neural-network',
'ManifestType: defaultLocale',
'ManifestVersion: 1.12.0',
''
].join('\n'));
writeLine('git push winget-pkgs');
await exec('git -C dist/winget-pkgs add --all');
await exec(`git -C dist/winget-pkgs commit -m "Update ${configuration.name} to ${configuration.version}"`);
await pullrequest('microsoft', 'winget-pkgs', process.env.WINGET_TOKEN, {
title: `Update ${configuration.productName} to ${configuration.version}`,
body: '',
head: `${process.env.GITHUB_USER}:master`,
base: 'master'
});
await rm('dist', 'winget-pkgs');
break;
}
default: {
writeLine('publish');
await rm('dist');
await install();
await publish('web');
await publish('electron');
await publish('python');
await publish('cask');
await publish('winget');
break;
}
}
};
const lint = async () => {
await install();
writeLine('eslint');
await exec('npx eslint --cache --cache-location ./dist/lint/.eslintcache');
writeLine('ruff');
await exec('python -m ruff check . --quiet');
};
const test = async (target) => {
let models = true;
while (true) {
if (target === 'desktop' || read('desktop')) {
target = null;
models = false;
// eslint-disable-next-line no-await-in-loop
await exec('npx playwright install --with-deps chromium');
const host = process.platform === 'linux' && (process.env.GITHUB_ACTIONS || process.env.CI) ? 'xvfb-run -a ' : '';
// eslint-disable-next-line no-await-in-loop
await exec(`${host}npx playwright test --config=test/playwright.config.js --project=desktop`);
continue;
}
if (target === 'browser' || read('browser')) {
target = null;
models = false;
if (process.platform !== 'win32') {
// eslint-disable-next-line no-await-in-loop
await exec('npx playwright install --with-deps chromium');
const headed = process.env.GITHUB_ACTIONS || process.env.CI ? '' : ' --headed';
// eslint-disable-next-line no-await-in-loop
await exec(`npx playwright test --config=test/playwright.config.js --project=browser${headed}`);
}
continue;
}
break;
}
if (models) {
target = target || args.join(' ');
await exec(`node test/models.js ${target}`);
}
};
const validate = async () => {
writeLine('lint');
await lint();
writeLine('test');
await test('tag:validation');
writeLine('test desktop');
await test('desktop');
writeLine('test browser');
await test('browser');
};
const update = async () => {
const staged = await exec('git diff --cached --name-only', 'utf-8');
if (staged.stdout.trim()) {
throw new Error('Staged changes.');
}
const modified = await exec('git diff --name-only -- package.json package-lock.json', 'utf-8');
if (modified.stdout.trim()) {
throw new Error('Uncommitted changes.');
}
const filter = new Set(process.argv.length > 3 ? process.argv.slice(3) : []);
if (filter.size === 0) {
const output = await exec('npm outdated --json', 'utf-8');
const entries = JSON.parse(output.stdout);
const compare = (a, b) => {
const regex = /^\d+\.\d+\.\d+$/;
if (!regex.test(a)) {
throw new Error(`Invalid version format '${a}'.`);
}
if (!regex.test(b)) {
throw new Error(`Invalid version format '${b}'.`);
}
a = a.split('.').map((v) => Number(v));
b = b.split('.').map((v) => Number(v));
if (a.length !== b.length) {
throw new Error(`Invalid version a=${a.join('.')} b=${b.join('.')}`);
}
for (let i = 0; i < 3; i++) {
if ((a[i] || 0) > (b[i] || 0)) {
return 1;
}
if ((a[i] || 0) < (b[i] || 0)) {
return -1;
}
}
return 0;
};
for (const [name, entry] of Object.entries(entries)) {
if (compare(entry.wanted, entry.latest) < 0) {
writeLine(`${name} ${entry.latest}`);
// eslint-disable-next-line no-await-in-loop
await exec(`npm install --quiet --no-progress --silent --save-exact ${name}@latest`);
// eslint-disable-next-line no-await-in-loop
await exec('git add package.json package-lock.json');
// eslint-disable-next-line no-await-in-loop
await exec(`git commit --quiet -m "Update to ${name} ${entry.latest}"`);
}
}
await exec('npx install-electron');
}
let targets = [
'armnn',
'bigdl',
'caffe', 'circle', 'cntk', 'coreml',
'dlc', 'dnn',
'espdl', 'executorch',
'gguf',
'jax',
'kann', 'keras',
'litertlm',
'megengine', 'mindspore', 'mlir', 'mnn',
'nnabla',
'onnx', 'om',
'paddle', 'pytorch',
'rknn',
'sentencepiece', 'sklearn',
'tf', 'tosa',
'uff',
'xmodel'
];
let commands = [
'sync',
'install',
'schema',
'metadata'
];
if (filter.size > 0 && targets.some((target) => filter.has(target))) {
targets = targets.filter((target) => filter.has(target));
}
if (filter.size > 0 && commands.some((target) => filter.has(target))) {
commands = commands.filter((command) => filter.has(command));
}
commands = commands.join(' ');
for (const target of targets) {
if (process.platform === 'win32') {
// eslint-disable-next-line no-await-in-loop
await exec(`bash tools/${target} ${commands}`);
} else {
// eslint-disable-next-line no-await-in-loop
await exec(`tools/${target} ${commands}`);
}
}
};
const pull = async () => {
await exec('git fetch --prune origin "refs/tags/*:refs/tags/*"');
let before = await exec('git rev-parse HEAD', 'utf-8');
if (before.status !== 0) {
throw new Error(before.stderr.trim());
}
try {
await exec('git pull --prune --rebase --autostash');
} catch (error) {
writeLine(error.message);
}
let after = await exec('git rev-parse HEAD', 'utf-8');
if (after.status !== 0) {
throw new Error(after.stderr.trim());
}
before = before.stdout.trim();
after = after.stdout.trim();
if (before !== after) {
const output = await exec(`git diff --name-only ${before} ${after}`, 'utf-8');
const files = new Set(output.stdout.split('\n'));
if (files.has('package.json')) {
await clean();
await install();
}
}
};
const coverage = async () => {
switch (read()) {
case 'desktop': {
await rm('dist', 'nyc');
await mkdir('dist', 'nyc');
await exec('cp package.json dist/nyc');
await exec('cp -R source dist/nyc');
await exec('nyc instrument --compact false source dist/nyc/source');
const file = dirname('dist', 'nyc', 'source', 'index.html');
let content = await fs.readFile(file, 'utf-8');
content = content.replace(`"script-src 'self'"`, `"script-src 'self' 'unsafe-eval'"`);
await fs.writeFile(file, content, 'utf-8');
await exec('nyc --instrument npx electron ./dist/nyc');
await exec('nyc report');
break;
}
case 'test': {
const target = args.join(' ');
await rm('dist', 'c8');
await exec(`npx c8 --reporter=html --report-dir=dist/c8/report node test/models.js ${target}`);
break;
}
default: {
throw new Error('Unsupported coverage target.');
}
}
};
const analyze = async () => {
const exists = await access('third_party/tools/codeql');
if (!exists) {
await exec('git clone --depth=1 https://github.com/github/codeql.git third_party/tools/codeql');
}
await rm('dist', 'codeql');
await mkdir('dist', 'codeql', 'netron');
await exec('cp -r publish source test tools dist/codeql/netron/');
await exec('codeql database create dist/codeql/database --source-root dist/codeql/netron --language=javascript --threads=3');
await exec('codeql database analyze dist/codeql/database ./third_party/tools/codeql/javascript/ql/src/codeql-suites/javascript-security-and-quality.qls --format=csv --output=dist/codeql/results.csv --threads=3');
await exec('cat dist/codeql/results.csv');
};
const release = async () => {
await pull();
const file = dirname('package.json');
let content = await fs.readFile(file, 'utf-8');
content = content.replace(/(\s*"version":\s")(\d\.\d\.\d)(",)/m, (match, p1, p2, p3) => {
const version = Array.from((parseInt(p2.split('.').join(''), 10) + 1).toString()).join('.');
return p1 + version + p3;
});
content = content.replace(/(\s*"date":\s")(.*)(",)/m, (match, p1, p2, p3) => {
const date = new Date().toISOString().split('.').shift().split('T').join(' ');
return p1 + date + p3;
});
await fs.writeFile(file, content, 'utf-8');
await exec('npm install --package-lock-only');
await load();
await exec('git add package.json');
await exec('git add package-lock.json');
await exec(`git commit -m "Update to ${configuration.version}"`);
await exec(`git tag v${configuration.version}`);
await exec('git push');
await exec('git push --tags');
};
const main = async () => {
await load();
try {
const task = read();
switch (task) {
case 'start': await start(); break;
case 'clean': await clean(); break;
case 'install': await install(); break;
case 'build': await build(); break;
case 'publish': await publish(); break;
case 'release': await release(); break;
case 'lint': await lint(); break;
case 'test': await test(); break;
case 'validate': await validate(); break;
case 'update': await update(); break;
case 'pull': await pull(); break;
case 'analyze': await analyze(); break;
case 'coverage': await coverage(); break;
default: throw new Error(`Unsupported task '${task}'.`);
}
} catch (error) {
if (process.stdout.write) {
process.stdout.write(error.message + os.EOL);
}
process.exit(1);
}
};
await main();
Executable
+53
View File
@@ -0,0 +1,53 @@
{
"name": "netron",
"productName": "Netron",
"author": {
"name": "Lutz Roeder",
"email": "lutzroeder@users.noreply.github.com",
"url": "https://www.lutzroeder.com"
},
"version": "9.1.5",
"date": "2026-07-10 23:12:08",
"description": "Visualizer for neural network, deep learning, and machine learning models",
"license": "MIT",
"repository": "lutzroeder/netron",
"type": "module",
"main": "source/app.js",
"scripts": {
"start": "node package.js start",
"server": "python package.py build start",
"clean": "node package.js clean",
"install": "node package.js install",
"lint": "node package.js lint",
"build": "node package.js build",
"publish": "node package.js publish",
"release": "node package.js release",
"test": "node package.js test",
"validate": "node package.js validate",
"coverage": "node package.js coverage",
"analyze": "node package.js analyze",
"update": "node package.js update",
"pull": "node package.js pull"
},
"dependencies": {
"electron-updater": "6.8.9"
},
"devDependencies": {
"@electron/notarize": "3.1.1",
"@playwright/test": "1.61.1",
"electron": "43.1.0",
"electron-builder": "26.15.3",
"eslint": "10.7.0"
},
"build": {
"extends": "publish/electron-builder.json"
},
"nyc": {
"reporter": [
"lcov",
"html"
],
"report-dir": "dist/nyc/report",
"temp-dir": "dist/nyc/.nyc_output"
}
}
+69
View File
@@ -0,0 +1,69 @@
import os
import sys
argv = sys.argv[1:]
root_dir = os.path.dirname(os.path.abspath(__file__))
dist_dir = os.path.join(root_dir, "dist")
dist_pypi_dir = os.path.join(dist_dir, "pypi")
def _build():
import shutil
source_dir = os.path.join(root_dir, "source")
shutil.rmtree(os.path.join(source_dir, "__pycache__"), ignore_errors=True)
shutil.rmtree(dist_pypi_dir, ignore_errors=True)
shutil.copytree(source_dir, os.path.join(dist_pypi_dir, "netron"))
shutil.copyfile(
os.path.join(root_dir, "pyproject.toml"),
os.path.join(dist_pypi_dir, "pyproject.toml"))
os.remove(os.path.join(dist_pypi_dir, "netron", "desktop.mjs"))
os.remove(os.path.join(dist_pypi_dir, "netron", "app.js"))
def _install():
import pip._internal.cli.main
pip._internal.cli.main.main(["install", dist_pypi_dir])
def _version():
import json
import re
path = os.path.join(root_dir, "package.json")
with open(path, encoding="utf-8") as file:
package = json.load(file)
version = package["version"]
date = package["date"]
entries = [
("pyproject.toml", '(version\\s*=\\s*")(.*)(")', version),
("netron/server.py", '(__version__\\s=\\s")(.*)(")', version),
("netron/index.html", '(<meta name="version" content=")(.*)(">)', version),
("netron/index.html", '(<meta name="date" content=")(.*)(">)', date)
]
for path, regex, value in entries:
path = os.path.join(dist_pypi_dir, path)
with open(path, encoding="utf-8") as file:
content = file.read()
content, count = re.subn(regex, rf"\g<1>{value}\g<3>", content)
if count == 0:
raise ValueError(f"Failed to update '{path}' with '{value}'.")
with open(path, "w", encoding="utf-8") as file:
file.write(content)
def _start():
""" Start server """
sys.path.insert(0, os.path.join(root_dir, "dist", "pypi"))
__import__("netron").main()
argv.clear()
def main():
table = {
"build": _build,
"install": _install,
"version": _version,
"start": _start
}
while len(argv) > 0:
command = argv.pop(0)
del sys.argv[1]
table[command]()
if __name__ == "__main__":
main()
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg xmlns='http://www.w3.org/2000/svg' width='1320' height='800' viewBox='0 0 1320 800'>
<rect x='0' y='0' width='1320' height='800' fill='#f8f8f8'/>
<path d='M 645 325 L 677 360 L 645 395' fill='none' stroke-width='14' stroke='#aaa' stroke-linecap='square' />
<path d='M 645 325 L 677 360 L 645 395' fill='none' stroke-width='12' stroke='#bbb' stroke-linecap='square' />
</svg>

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

+120
View File
@@ -0,0 +1,120 @@
{
"appId": "com.lutzroeder.netron",
"productName": "Netron",
"files": [ "source/**/*" ],
"directories": { "buildResources": "./publish" },
"fileAssociations": [
{ "ext": "armnn", "name": "Arm NN Model" },
{ "ext": "caffemodel", "name": "Caffe Model" },
{ "ext": "circle", "name": "Circle Model" },
{ "ext": "ckpt", "name": "Model Checkpoint" },
{ "ext": "cmf", "name": "CNTK Model" },
{ "ext": "dlc", "name": "DLC Model" },
{ "ext": "dnn", "name": "CNTK Model" },
{ "ext": "espdl", "name": "ESP-DL Model" },
{ "ext": "gguf", "name": "GGUF Model" },
{ "ext": "h5", "name": "Keras Model" },
{ "ext": "hd5", "name": "Keras Model" },
{ "ext": "hdf5", "name": "Keras Model" },
{ "ext": "hn", "name": "Hailo Model" },
{ "ext": "jax_export", "name": "JAX Export Model" },
{ "ext": "jax_exported", "name": "JAX Export Model" },
{ "ext": "kann", "name": "KaNN Model" },
{ "ext": "kgraph", "name": "KaNN Model" },
{ "ext": "keras", "name": "Keras Model" },
{ "ext": "kmodel", "name": "Kendryte Model" },
{ "ext": "lite", "name": "TensorFlow Lite Model" },
{ "ext": "litertlm", "name": "LiteRT-LM Model" },
{ "ext": "mar", "name": "MXNet Model" },
{ "ext": "maxviz", "name": "Modular Model" },
{ "ext": "meta", "name": "TensorFlow Meta Graph" },
{ "ext": "mge", "name": "MegEngine Mge Model" },
{ "ext": "mindir", "name": "MindIR Model" },
{ "ext": "mlmodel", "name": "Core ML Model" },
{ "ext": "mlir", "name": "MLIR Model" },
{ "ext": "mlirbc", "name": "MLIR Bytecode Model" },
{ "ext": "mlnet", "name": "ML.NET Model" },
{ "ext": "mlpackage", "name": "Core ML Model Package", "isPackage": true },
{ "ext": "mnn", "name": "MNN Model" },
{ "ext": "model", "name": "Model" },
{ "ext": "nb", "name": "Paddle Lite Model" },
{ "ext": "ngf", "name": "ONNX Model" },
{ "ext": "nn", "name": "Barracuda Model" },
{ "ext": "nnp", "name": "Neural Network Libraries" },
{ "ext": "npy", "name": "NumPy Array" },
{ "ext": "npz", "name": "NumPy Archive" },
{ "ext": "om", "name": "DaVinci OM Model" },
{ "ext": "onnx", "name": "ONNX Model" },
{ "ext": "ort", "name": "ONNX Runtime Model" },
{ "ext": "paddle", "name": "PaddlePaddle Model" },
{ "ext": "param", "name": "NCNN Model" },
{ "ext": "pb", "name": "Protocol Buffer" },
{ "ext": "pbtxt", "name": "Text Protocol Buffer" },
{ "ext": "pdiparams", "name": "PaddlePaddle Model" },
{ "ext": "pdmodel", "name": "PaddlePaddle Model" },
{ "ext": "pdopt", "name": "PaddlePaddle Model" },
{ "ext": "pdparams", "name": "PaddlePaddle Model" },
{ "ext": "pickle", "name": "Python Pickle File" },
{ "ext": "pkl", "name": "Python Pickle File" },
{ "ext": "prototxt", "name": "Text Protocol Buffer" },
{ "ext": "pt", "name": "PyTorch Model" },
{ "ext": "pt2", "name": "PyTorch Model" },
{ "ext": "pte", "name": "ExecuTorch Model" },
{ "ext": "pth", "name": "PyTorch Model" },
{ "ext": "ptl", "name": "PyTorch Model" },
{ "ext": "rknn", "name": "RKNN Model" },
{ "ext": "safetensors", "name": "Safetensors Checkpoint" },
{ "ext": "t7", "name": "Torch Model" },
{ "ext": "tfl", "name": "TensorFlow Lite Model" },
{ "ext": "tflite", "name": "TensorFlow Lite Model" },
{ "ext": "tmfile", "name": "Tengine Model" },
{ "ext": "tm", "name": "MegEngine Traced Model" },
{ "ext": "tnnproto", "name": "TNN Model" },
{ "ext": "tosa", "name": "TOSA Model" },
{ "ext": "torchscript", "name": "PyTorch Model" },
{ "ext": "uff", "name": "UFF Model" },
{ "ext": "xmodel", "name": "Vitis AI Model" }
],
"publish": [
{ "provider": "github", "releaseType": "release" }
],
"linux": {
"artifactName":"${productName}-${version}-${arch}.${ext}",
"executableName": "netron",
"target": [
{ "target": "deb", "arch": ["x64"] },
{ "target": "rpm", "arch": ["x64"] }
]
},
"mac": {
"artifactName": "${productName}-${version}-mac.${ext}",
"category": "public.app-category.developer-tools",
"darkModeSupport": true,
"gatekeeperAssess": false,
"hardenedRuntime": true,
"notarize": true,
"target": [ "dmg", "zip" ]
},
"win": {
"target": [ "nsis" ],
"azureSignOptions" : {
"publisherName": "Lutz Roeder",
"endpoint": "https://eus.codesigning.azure.net/",
"codeSigningAccountName": "lutzroeder",
"certificateProfileName": "lutzroeder"
}
},
"dmg": {
"artifactName": "${productName}-${version}.${ext}",
"title": "${productName} ${version}",
"writeUpdateInfo": false,
"iconSize": 160,
"contents": [
{ "x": 180, "y": 170 },
{ "x": 480, "y": 170, "type": "link", "path": "/Applications" }
]
},
"nsis": {
"differentialPackage": false
}
}
+27
View File
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<body>
<div style="height: 400px;">
<div style="background: #f6f6f6; width: 400px; height: 400px; float: left;">
<img src="icon.svg" width="300" style="position: relative; top: 50px; left: 50px;">
</div>
<div style="background: #d8d8d8; width: 400px; height: 400px; float: left;">
<img src="icon.svg" width="300" style="position: relative; top: 50px; left: 50px;">
</div>
<div style="background: #1e1e1e; width: 400px; height: 400px; float: left;">
<img src="icon.svg" width="300" style="position: relative; top: 50px; left: 50px;">
</div>
</div>
<div style="height: 400px;">
<div style="background: #f6f6f6; width: 400px; height: 400px; float: left;">
<img src="icon.icns.svg" width="300" style="position: relative; top: 50px; left: 50px;">
</div>
<div style="background: #d8d8d8; width: 400px; height: 400px; float: left;">
<img src="icon.icns.svg" width="300" style="position: relative; top: 50px; left: 50px;">
</div>
<div style="background: #1e1e1e; width: 400px; height: 400px; float: left;">
<img src="icon.icns.svg" width="300" style="position: relative; top: 50px; left: 50px;">
</div>
</div>
</body>
</html>
BIN
View File
Binary file not shown.
+175
View File
@@ -0,0 +1,175 @@
<svg xmlns='http://www.w3.org/2000/svg' width='1024' height='1024' viewBox='0 0 1024 1024'>
<style>
.background-shadow {
filter: url(#background-dropshadow);
}
.node-border {
fill: #eeeeee;
opacity: 1.0;
}
.node {
fill: #307295;
opacity: 1;
}
.node-shadow {
filter: url(#node-dropshadow);
opacity: 0.3;
}
.line-border {
stroke: #000000;
opacity: 0.2;
}
.line {
stroke: #eeeeee;
opacity: 1.0;
}
.line-shadow {
stroke: #000000;
filter: url(#node-dropshadow);
opacity: 0.2;
}
</style>
<filter id='background-dropshadow'>
<feGaussianBlur in='SourceAlpha' stdDeviation='20' />
</filter>
<linearGradient id='background-fill' gradientTransform='rotate(90)'>
<stop offset='0' stop-color='#ffffff'/>
<stop offset='1' stop-color='#d2d2d2'/>
</linearGradient>
<linearGradient id='circle-stroke' gradientUnits='userSpaceOnUse' x1='0' y1='62' x2='0' y2='962'>
<stop offset='0' stop-color='#aaaaaa'/>
<stop offset='1' stop-color='#ffffff'/>
</linearGradient>
<linearGradient id='circle-fill' gradientUnits='userSpaceOnUse' x1='0' y1='100' x2='0' y2='924'>
<stop offset='0' stop-color='#4baac5' stop-opacity="1" />
<stop offset='1' stop-color='#307295' stop-opacity="1" />
</linearGradient>
<filter id='node-dropshadow' x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur in='SourceGraphic' stdDeviation='15' />
</filter>
<rect x='117' y='124' width='790' height='790' rx='160' class='background-shadow' />
<rect x='100' y='100' width='824' height='824' rx='160' fill='url(#background-fill)' />
<circle cx='512' cy='512' r='356' stroke-width='4' stroke='url(#circle-stroke)' fill='url(#circle-fill)' />
<g transform='translate(512,512) scale(1.1)'>
<g transform='translate(6,12)' opacity='1.0'>
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='24' class='line-shadow' />
<line x1='20' y1='-180' x2='208' y2='0' stroke-width='24' class='line-shadow' />
<line x1='20' y1='-60' x2='208' y2='0' stroke-width='24' class='line-shadow' />
<line x1='20' y1='60' x2='208' y2='0' stroke-width='24' class='line-shadow' />
<line x1='20' y1='180' x2='208' y2='0' stroke-width='24' class='line-shadow' />
<g transform='translate(-168,-92)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(-168,92)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(20,-180)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(20,-60)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(20,60)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(20,180)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(208,0)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
</g>
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='24' class='line-border' />
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='24' class='line-border' />
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='24' class='line-border' />
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='24' class='line-border' />
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='24' class='line-border' />
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='24' class='line-border' />
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='24' class='line-border' />
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='24' class='line-border' />
<line x1='20' y1='-180' x2='208' y2='0' stroke-width='24' class='line-border' />
<line x1='20' y1='-60' x2='208' y2='0' stroke-width='24' class='line-border' />
<line x1='20' y1='+60' x2='208' y2='0' stroke-width='24' class='line-border' />
<line x1='20' y1='+180' x2='208' y2='0' stroke-width='24' class='line-border' />
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='10' class='line' />
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='10' class='line' />
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='10' class='line' />
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='10' class='line' />
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='10' class='line' />
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='10' class='line' />
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='10' class='line' />
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='10' class='line' />
<line x1='20' y1='-180' x2='208' y2=' 0' stroke-width='10' class='line' />
<line x1='20' y1='-60' x2='208' y2=' 0' stroke-width='10' class='line' />
<line x1='20' y1='60' x2='208' y2=' 0' stroke-width='10' class='line' />
<line x1='20' y1='180' x2='208' y2=' 0' stroke-width='10' class='line' />
<g transform='translate(-168,-92)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(-168,92)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(20,-180)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(20,-60)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(20,60)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(20,180)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(208,0)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

+179
View File
@@ -0,0 +1,179 @@
<svg xmlns='http://www.w3.org/2000/svg' width='1024' height='1024' viewBox='0 0 1024 1024'>
<style>
.background-shadow {
filter: url(#background-dropshadow);
}
.node-border {
fill: #eeeeee;
opacity: 1.0;
}
.node {
fill: #307295;
opacity: 1;
}
.node-shadow {
filter: url(#node-dropshadow);
opacity: 0.3;
}
.line-border {
stroke: #000000;
opacity: 0.2;
}
.line {
stroke: #eeeeee;
opacity: 1.0;
}
.line-shadow {
stroke: #000000;
filter: url(#node-dropshadow);
opacity: 0.2;
}
</style>
<filter id='background-dropshadow'>
<feGaussianBlur in='SourceAlpha' stdDeviation='20' />
</filter>
<linearGradient id='background-fill' gradientUnits='userSpaceOnUse' x1='0' y1='62' x2='0' y2='962'>
<stop offset='0' stop-color='#fefefe'/>
<stop offset='1' stop-color='#cacaca'/>
</linearGradient>
<linearGradient id='circle-stroke' gradientUnits='userSpaceOnUse' x1='0' y1='62' x2='0' y2='962'>
<stop offset='0' stop-color='#aaaaaa'/>
<stop offset='1' stop-color='#ffffff'/>
</linearGradient>
<linearGradient id='circle-fill' gradientUnits='userSpaceOnUse' x1='-512' y1='-412' x2='-512' y2='412'>
<stop offset='0' stop-color='#4baac5' stop-opacity="1" />
<stop offset='1' stop-color='#307295' stop-opacity="1" />
</linearGradient>
<filter id='node-dropshadow' x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur in='SourceGraphic' stdDeviation='15' />
</filter>
<g transform='translate(512,512) scale(1.06)'>
<circle cx='0' cy='0' r='438' class='background-shadow' />
<circle cx='0' cy='0' r='456' fill='url(#background-fill)'/>
<circle cx='0' cy='0' r='417' stroke-width='4' stroke='url(#circle-stroke)' fill='url(#circle-fill)' />
<g transform='scale(1.2)'>
<g transform='translate(6,12)' opacity='1.0'>
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='24' class='line-shadow' />
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='24' class='line-shadow' />
<line x1='20' y1='-180' x2='208' y2='0' stroke-width='24' class='line-shadow' />
<line x1='20' y1='-60' x2='208' y2='0' stroke-width='24' class='line-shadow' />
<line x1='20' y1='60' x2='208' y2='0' stroke-width='24' class='line-shadow' />
<line x1='20' y1='180' x2='208' y2='0' stroke-width='24' class='line-shadow' />
<g transform='translate(-168,-92)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(-168,92)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(20,-180)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(20,-60)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(20,60)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(20,180)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
<g transform='translate(208,0)'>
<circle cx='0' cy='0' r='40' class='node-shadow' />
</g>
</g>
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='24' class='line-border' />
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='24' class='line-border' />
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='24' class='line-border' />
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='24' class='line-border' />
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='24' class='line-border' />
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='24' class='line-border' />
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='24' class='line-border' />
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='24' class='line-border' />
<line x1='20' y1='-180' x2='208' y2='0' stroke-width='24' class='line-border' />
<line x1='20' y1='-60' x2='208' y2='0' stroke-width='24' class='line-border' />
<line x1='20' y1='+60' x2='208' y2='0' stroke-width='24' class='line-border' />
<line x1='20' y1='+180' x2='208' y2='0' stroke-width='24' class='line-border' />
<line x1='-168' y1='-92' x2='20' y2='-180' stroke-width='10' class='line' />
<line x1='-168' y1='+92' x2='20' y2='-180' stroke-width='10' class='line' />
<line x1='-168' y1='-92' x2='20' y2='-60' stroke-width='10' class='line' />
<line x1='-168' y1='+92' x2='20' y2='-60' stroke-width='10' class='line' />
<line x1='-168' y1='-92' x2='20' y2='60' stroke-width='10' class='line' />
<line x1='-168' y1='+92' x2='20' y2='60' stroke-width='10' class='line' />
<line x1='-168' y1='-92' x2='20' y2='180' stroke-width='10' class='line' />
<line x1='-168' y1='+92' x2='20' y2='180' stroke-width='10' class='line' />
<line x1='20' y1='-180' x2='208' y2=' 0' stroke-width='10' class='line' />
<line x1='20' y1='-60' x2='208' y2=' 0' stroke-width='10' class='line' />
<line x1='20' y1='60' x2='208' y2=' 0' stroke-width='10' class='line' />
<line x1='20' y1='180' x2='208' y2=' 0' stroke-width='10' class='line' />
<g transform='translate(-168,-92)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(-168,92)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(20,-180)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(20,-60)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(20,60)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(20,180)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
<g transform='translate(208,0)'>
<circle cx='0' cy='0' r='50' class='node-border' />
<circle cx='0' cy='0' r='38' class='node' />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

+67
View File
@@ -0,0 +1,67 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "netron"
version = "0.0.0"
description = "Viewer for neural network, deep learning and machine learning models."
authors = [
{ name = "Lutz Roeder", email = "lutzroeder@users.noreply.github.com" }
]
keywords = [
"onnx", "keras", "tensorflow", "tflite", "coreml", "mxnet", "caffe", "caffe2",
"torchscript", "pytorch", "ncnn", "mnn", "openvino", "darknet", "paddlepaddle", "chainer",
"artificial intelligence", "machine learning", "deep learning", "neural network",
"visualizer", "viewer"
]
classifiers = [
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Intended Audience :: Science/Research",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Topic :: Software Development",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Scientific/Engineering :: Visualization"
]
[project.urls]
homepage = "https://github.com/lutzroeder/netron"
[project.readme]
text = """
Netron is a viewer for neural network, deep learning and machine learning models.
Netron supports ONNX, TensorFlow Lite, PyTorch, torch.export, ExecuTorch, TorchScript, TensorFlow, Core ML, OpenVINO, Keras, Caffe, Darknet, Safetensors and NumPy.
Netron has experimental support for MLIR, JAX, GGUF, RKNN, ncnn, MNN, PaddlePaddle and scikit-learn.
"""
content-type = "text/markdown"
[project.license]
text = "MIT"
[project.scripts]
netron = "netron:main"
[tool.setuptools]
package-dir = { "netron" = "netron" }
[tool.setuptools.packages.find]
namespaces = false
[tool.setuptools.package-data]
netron = ["*.*"]
[tool.setuptools.exclude-package-data]
netron = ["app.js", "electron.*"]
[tool.ruff]
lint.select = ["B", "E", "F", "I", "UP", "W", "Q"]
cache-dir = "./dist/lint/.ruff_cache"
+47
View File
@@ -0,0 +1,47 @@
""" Python Server entry point """
import argparse
import logging
import os
import sys
from .server import __version__, serve, start, status, stop, wait, widget
__all__ = ["start", "stop", "status", "wait", "serve", "widget", "__version__"]
def main():
""" main entry point """
parser = argparse.ArgumentParser(description=
"Viewer for neural network, deep learning and machine learning models.")
parser.add_argument("file",
metavar="MODEL_FILE", help="model file to serve", nargs="?", default=None)
parser.add_argument("-b", "--browse",
help="launch web browser", action="store_true")
parser.add_argument("-p", "--port", help="port to serve", type=int)
parser.add_argument("--host",
metavar="ADDR", help="host to serve", default="localhost")
parser.add_argument("--verbosity",
metavar="LEVEL", help="log verbosity (quiet, default, debug)",
choices=[ "quiet", "debug", "default" ], default="default")
parser.add_argument("--version", help="print version", action="store_true")
args = parser.parse_args()
levels = {
"quiet": logging.CRITICAL,
"default": logging.INFO,
"debug": logging.DEBUG,
}
logging.basicConfig(level=levels[args.verbosity], format="%(message)s")
logger = logging.getLogger(__name__)
if args.file and not os.path.exists(args.file):
logger.error(f"Model file '{args.file}' does not exist.")
sys.exit(2)
if args.version:
logger.info(__version__)
sys.exit(0)
address = (args.host, args.port) if args.host else args.port if args.port else None
start(args.file, address=address, browse=args.browse)
wait()
sys.exit(0)
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+699
View File
@@ -0,0 +1,699 @@
const acuity = {};
acuity.ModelFactory = class {
async match(context) {
const obj = await context.peek('json');
if (obj && obj.MetaData && obj.Layers && Object.keys(obj).length < 256) {
return context.set('acuity', obj);
}
return null;
}
async open(context) {
const metadata = await context.metadata('acuity-metadata.json');
return new acuity.Model(metadata, context.value);
}
};
acuity.Model = class {
constructor(metadata, model, data, quantization) {
this.name = model.MetaData.Name;
this.format = `Acuity${model.MetaData && model.MetaData.AcuityVersion ? ` v${model.MetaData.AcuityVersion}` : ''}`;
this.runtime = model.MetaData.Platform;
this.modules = [new acuity.Graph(metadata, model, data, quantization)];
}
};
acuity.Graph = class {
constructor(metadata, model) {
this.nodes = [];
this.inputs = [];
this.outputs = [];
this.metrics = [];
const values = new Map();
const value = (name) => {
if (!values.has(name)) {
values.set(name, { name, shape: null });
}
return values.get(name);
};
let totalFlops = 0;
for (const [name, layer] of Object.entries(model.Layers)) {
layer.inputs = layer.inputs.map((input) => {
return value(input);
});
layer.outputs = layer.outputs.map((port) => {
const output = value(`@${name}:${port}`);
let shape = null;
if (layer.op.toLowerCase() === 'input' ||
layer.op.toLowerCase() === 'variable') {
if (Object.prototype.hasOwnProperty.call(layer.parameters, 'shape') && layer.parameters.shape.length > 0) {
shape = layer.parameters.shape;
} else if (Object.prototype.hasOwnProperty.call(layer.parameters, 'size') && Object.prototype.hasOwnProperty.call(layer.parameters, 'channels')) {
const sizes = layer.parameters.size.split(' ');
shape = [0, parseInt(sizes[0], 10), parseInt(sizes[1], 10), layer.parameters.channels];
} else if (Object.prototype.hasOwnProperty.call(layer.parameters, 'is_scalar')) {
shape = [1];
}
if (shape && shape.length === 4 && shape[0] === 0) {
shape[0] = 1;
}
}
output.shape = shape;
return output;
});
// Add other layer types (e.g., pooling, batch norm, etc.) as needed.
if (layer.type === 'Conv2D') {
const { kernelShape, inputShape, outputShape } = layer;
const [kH, kW] = kernelShape;
const [inC] = inputShape;
const [outC, oH, oW] = outputShape;
totalFlops += kH * kW * inC * oH * oW * outC;
} else if (layer.type === 'Dense') {
const { inputSize, outputSize } = layer;
totalFlops += inputSize * outputSize;
}
}
this.metrics.push(new acuity.Argument('flops', totalFlops));
acuity.Inference.infer(model.Layers);
for (const [name, obj] of values) {
const type = new acuity.TensorType(null, new acuity.TensorShape(obj.shape));
const value = new acuity.Value(name, type, null, null);
values.set(name, value);
}
for (const [name, layer] of Object.entries(model.Layers)) {
switch (layer.op.toLowerCase()) {
case 'input': {
const value = values.get(layer.outputs[0].name);
const argument = new acuity.Argument(name, [value]);
this.inputs.push(argument);
break;
}
case 'output': {
const value = values.get(layer.inputs[0].name);
const argument = new acuity.Argument(name, [value]);
this.outputs.push(argument);
break;
}
default: {
const node = new acuity.Node(metadata, name, layer, values);
this.nodes.push(node);
break;
}
}
}
}
};
acuity.Node = class {
constructor(metadata, name, layer, values) {
const op = layer.op;
this.name = name;
this.type = metadata.type(op) || { name: op };
this.inputs = [];
this.outputs = [];
this.attributes = [];
if (this.type) {
if (layer.parameters) {
for (const [name, value] of Object.entries(layer.parameters)) {
const meta = metadata.attribute(op, name);
const type = meta && meta.type ? meta.type : null;
const visible = meta && meta.default !== undefined && meta.default === value ? false : true;
const attribute = new acuity.Argument(name, value, type, visible);
this.attributes.push(attribute);
}
}
}
for (let i = 0; i < layer.inputs.length; i++) {
const input = layer.inputs[i];
const value = values.get(input.name);
const name = this.type && this.type.inputs && i < this.type.inputs.length ? this.type.inputs[i].name : `input${i}`;
const argument = new acuity.Argument(name, [value]);
this.inputs.push(argument);
}
if (this.type && this.type.constants) {
for (const constant of this.type.constants) {
// const name = "@" + this.name + ":" + constant.name;
const type = new acuity.TensorType(null, new acuity.TensorShape(null));
const value = new acuity.Value('', type, null, new acuity.Tensor(type));
const argument = new acuity.Argument(constant.name, [value]);
this.inputs.push(argument);
}
}
for (let i = 0; i < layer.outputs.length; i++) {
const output = layer.outputs[i];
const value = values.get(output.name);
const name = this.type && this.type.outputs && i < this.type.outputs.length ? this.type.outputs[i].name : `output${i}`;
const argument = new acuity.Argument(name, [value]);
this.outputs.push(argument);
}
}
};
acuity.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
acuity.Value = class {
constructor(name, type = null, quantization = null, initializer = null) {
if (typeof name !== 'string') {
throw new acuity.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = type;
this.quantization = quantization;
this.initializer = initializer;
}
};
acuity.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType || '?';
this.shape = shape;
}
toString() {
return (this.dataType || '?') + this.shape.toString();
}
};
acuity.TensorShape = class {
constructor(dimensions) {
this.dimensions = Array.isArray(dimensions) && dimensions.length === 1 && dimensions[0] === 0 ? [] : dimensions;
}
toString() {
if (!Array.isArray(this.dimensions) || this.dimensions.length === 0 || (this.dimensions.length === 1 && this.dimensions[0] === 0)) {
return '';
}
return `[${this.dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',')}]`;
}
};
acuity.Tensor = class {
constructor(type) {
this.type = type;
this.Category = 'Constant';
}
};
acuity.Inference = class {
static infer(layers) {
const outputs = new Map();
const outputLayers = [];
for (const [, layer] of Object.entries(layers)) {
if (layer.op.toLowerCase() === 'output') {
outputLayers.push(layer);
}
for (const output of layer.outputs) {
outputs.set(output.name, layer);
}
}
const broadcasts = new Set([
'add', 'equal', 'fllor_mod', 'floor_div', 'greater', 'greater_equal', 'less', 'less_equal',
'logical_and', 'logical_or', 'minimum', 'multiply', 'not_equal', 'pow', 'real_div',
'squared_difference', 'subtract', 'divide', 'addn', 'Divide', 'bitwise_and', 'bitwise_or',
'bitwise_xor', 'average', 'logical_not', 'logical_xor'
]);
const passthroughs = new Set([
'LocalResponseNormalization', 'a_times_b_plus_c', 'abs', 'batchnorm_single', 'batchnormalize',
'cast', 'cast', 'clipbyvalue', 'dequantize', 'dtype_converter', 'elu', 'exp', 'floor',
'groupnormalize', 'hard_sigmoid', 'hard_swish', 'instancenormalize', 'l2normalize', 'l2normalizescale',
'layernormalize', 'leakyrelu', 'log', 'log_softmax', 'mish', 'neg', 'norm_with_channel_mean',
'norm_with_min_max', 'norm_with_scale', 'pow', 'prelu', 'quantize', 'relu', 'relu_keras',
'relun', 'reverse', 'round', 'rsqrt', 'sigmoid', 'sin', 'softmax', 'softrelu', 'sqrt', 'square', 'tanh',
'swish', 'gelu', 'dropout', 'eltwise', 'cos', 'l1_layernormalize', 'inverse_sigmoid', 'selu', 'mod',
'mish', 'minimum_with_clip', 'celu', 'cumsum', 'dft', 'dropout2', 'erf', 'noop', 'squashing', 'tan', 'ceil',
'atan', 'atan2', 'atanh', 'alpha_dropout', 'acosh', 'rmsnormalize', 'sign'
]);
const reduces = new Set([
'reduceany', 'reducemax', 'reducemean', 'reducemin', 'reduceprod', 'reducesum'
]);
const poolings = new Set([
'pooling', 'l2pooling'
]);
const operators = new Map();
operators.set('broadcast', ([a, b]) => {
const longer = a.length >= b.length ? a.slice() : b.slice();
const shorter = a.length < b.length ? a.slice() : b.slice();
const remain = longer.length - shorter.length;
for (let i = 0; i < remain; i++) {
shorter.splice(0, 0, 1);
}
for (let i = 0; i < longer.length; i++) {
longer[i] = longer[i] > shorter[i] ? longer[i] : shorter[i];
}
return [longer];
});
operators.set('concat', (inputs, params) => {
const outputShape = inputs[0].slice();
outputShape[params.dim] = 0;
for (const shape of inputs) {
outputShape[params.dim] += shape[params.dim];
}
return [outputShape];
});
operators.set('conv1d', (inputs, params) => {
if (params.padding === 'VALID') {
const out_h = ~~((inputs[0][1] + params.stride - params.ksize) / params.stride);
return [[inputs[0][0], out_h, params.weights]];
} else if (params.padding === 'SAME') {
const out_h = ~~((inputs[0][1] + params.stride - 1) / params.stride);
return [[inputs[0][0], out_h, params.weights]];
}
return null;
});
operators.set('convolution', (inputs, params) => {
if (params.padding === 'VALID') {
const out_h = Math.floor((inputs[0][1] + params.stride_h + 2 * params.pad_h - params.ksize_h) / params.stride_h);
const out_w = Math.floor((inputs[0][2] + params.stride_w + 2 * params.pad_w - params.ksize_w) / params.stride_w);
return [[inputs[0][0], out_h, out_w, params.weights]];
} else if (params.padding === 'SAME') {
const out_h = Math.floor((inputs[0][1] + params.stride_h - 1) / params.stride_h);
const out_w = Math.floor((inputs[0][2] + params.stride_w - 1) / params.stride_w);
return [[inputs[0][0], out_h, out_w, params.weights]];
}
return null;
});
operators.set('depthwise_conv1d', (inputs, params) => {
if (params.padding === 'VALID') {
const out_h = ~~((inputs[0][1] + params.stride + params.pad[0] + params.pad[1] - params.ksize) / params.stride);
return [[inputs[0][0], out_h, inputs[0][2] * params.multiplier]];
} else if (params.padding === 'SAME') {
const out_h = ~~((inputs[0][1] + params.stride - 1) / params.stride);
return [[inputs[0][0], out_h, inputs[0][2] * params.multiplier]];
}
return null;
});
operators.set('depthwise_convolution', (inputs, params) => {
if (params.padding === 'VALID') {
const out_h = ~~((inputs[0][1] + params.stride_h + params.pad[0] + params.pad[1] - params.ksize_h) / params.stride_h);
const out_w = ~~((inputs[0][2] + params.stride_w + params.pad[2] + params.pad[3] - params.ksize_w) / params.stride_w);
return [[inputs[0][0], out_h, out_w, inputs[0][3] * params.multiplier]];
} else if (params.padding === 'SAME') {
const out_h = ~~((inputs[0][1] + params.stride_h - 1) / params.stride_h);
const out_w = ~~((inputs[0][2] + params.stride_w - 1) / params.stride_w);
return [[inputs[0][0], out_h, out_w, inputs[0][3] * params.multiplier]];
}
return null;
});
operators.set('deconvolution', (inputs, params) => {
return [params.output_shape.map((item, index) => item === 0 ? inputs[0][index] : item)];
});
operators.set('deconvolution1d', (inputs, params) => {
return [params.output_shape.map((item, index) => item === 0 ? inputs[0][index] : item)];
});
operators.set('fullconnect', (inputs, params) => {
return [inputs[0].slice(0, params.axis).concat([params.weights])];
});
operators.set('gather', (inputs, params) => {
const prefix = inputs[1].slice();
const suffix = inputs[0].slice(params.axis + 1);
return [prefix.concat(suffix)];
});
operators.set('lstm', (inputs, params) => {
const [input] = inputs;
const [a, b] = input;
let batch = a;
const output = params.num_proj === null ? params.weights : params.num_proj;
if (params.time_major) {
batch = b;
}
const newShape = params.return_sequences ? [a, b, output] : [batch, output];
return [newShape, [batch, output], [batch, params.weights]];
});
operators.set('matmul', ([a, b], params) => {
let newShape = a.slice(0, -2);
if (params.transpose_a) {
newShape = newShape.concat(a.slice(-1));
} else {
newShape = newShape.concat(a.slice(-2, -1));
}
if (params.transpose_b) {
newShape = newShape.concat(b.slice(-2, -1));
} else {
newShape = newShape.concat(b.slice(-1));
}
return [newShape];
});
operators.set('pad', (inputs, params) => {
return [inputs[0].map((item, index) => item + params.padding_value[index][0] + params.padding_value[index][1])];
});
operators.set('permute', (inputs, params) => {
return [inputs[0].map((item, index) => inputs[0][params.perm[index]])];
});
operators.set('pooling', (inputs, params) => {
if (params.padding === 'VALID') {
const out_h = ~~((inputs[0][1] + params.stride_h - params.ksize_h) / params.stride_h);
const out_w = ~~((inputs[0][2] + params.stride_w - params.ksize_w) / params.stride_w);
return [[inputs[0][0], out_h, out_w, inputs[0][3]]];
} else if (params.padding === 'SAME') {
const out_h = ~~((inputs[0][1] + params.stride_h - 1) / params.stride_h);
const out_w = ~~((inputs[0][2] + params.stride_w - 1) / params.stride_w);
return [[inputs[0][0], out_h, out_w, inputs[0][3]]];
}
return null;
});
operators.set('reduce', (inputs, params) => {
const newShape = inputs[0].slice();
const axis_list = params.axis_list.map((item) => {
return item < 0 ? newShape.length + item : item;
});
axis_list.sort((a, b) => {
return b - a;
});
axis_list.forEach((i) => {
newShape[i] = 1;
});
if (!params.keep_dims) {
axis_list.forEach((i) => {
newShape.splice(i, 1);
});
if (!newShape.length) {
newShape.splice(0, 0, 0);
}
}
return [newShape];
});
operators.set('repeat', (inputs, params) => {
const newShape = inputs[0].slice();
newShape[params.axis] = params.maxlen;
return [newShape];
});
operators.set('reshape', (inputs, params) => {
const negativeIndexs = [];
let shape = params.shape;
if (typeof params.shape === 'string') {
shape = params.shape.split(/\s+/).map((item) => {
return parseInt(item, 10);
});
}
const newShape = shape.map((item, index) => {
if (item === 0) {
return inputs[0][index];
}
if (item === -1) {
negativeIndexs.push(index);
return 1;
}
return item;
});
if (negativeIndexs.length > 0) {
newShape[negativeIndexs[0]] = inputs[0].reduce((a, c) => a * c) / newShape.reduce((a, c) => a * c);
}
return [newShape];
});
operators.set('sequence_mask', (inputs, params) => {
return [inputs[0].slice().concat([params.maxlen])];
});
operators.set('slice', (inputs, params) => {
return [params.size.map((item, index) => item === -1 ? inputs[0][index] : item)];
});
operators.set('squeeze', (inputs, params) => {
const newShape = inputs[0].slice();
const axis_list = [...new Set(params.axis_list)].sort((a, b) => b - a);
for (const item of axis_list) {
newShape.splice(item, 1);
}
return [newShape];
});
operators.set('space2depth', (inputs, params) => {
const h = inputs[0][1] / params.block_size[0];
const w = inputs[0][2] / params.block_size[1];
const c = inputs[0][3] * params.block_size[1] * params.block_size[1];
return [[inputs[0][0], h, w, c]];
});
operators.set('depth2space', (inputs, params) => {
const h = inputs[0][1] * params.block_size;
const w = inputs[0][2] * params.block_size;
const c = inputs[0][3] / (params.block_size * params.block_size);
return [[inputs[0][0], h, w, c]];
});
operators.set('upsampling', (inputs, params) => {
const h = inputs[0][1] * params.factor;
const w = inputs[0][2] * params.factor;
return [[inputs[0][0], h, w, inputs[0][3]]];
});
operators.set('crop_image', (inputs, params) => {
return [[inputs[0][0], params.crop_size[0], params.crop_size[1], inputs[0][3]]];
});
operators.set('split', (inputs, params) => {
const sizes = [];
const slices = params.slices.slice();
slices.splice(0, 0, 0);
slices.push(inputs[0][params.dim]);
slices.reduce((a, b) => {
sizes.push(b - a);
return b;
});
return sizes.map((item) => {
const shape = inputs[0].slice();
shape[params.dim] = item;
return shape;
});
});
operators.set('stack', (inputs, params) => {
const newShape = inputs[0].slice();
if (newShape.length === 1 && newShape[0] === 0) {
newShape[0] = 1;
} else {
newShape.splice(params.axis, 0, inputs.length);
}
return [newShape];
});
operators.set('stridedslice', (inputs, params) => {
const input_shape = inputs[0].slice();
const begin = params.slice_begin.slice();
const end = params.slice_end.slice();
if (params.slice_begin_mask > 0) {
for (let i = 0; i < begin.length; i++) {
if ((params.slice_begin_mask >>> i) & 0x1) {
begin[i] = -1;
}
}
}
if (params.slice_end_mask > 0) {
for (let i = 0; i < end.length; i++) {
if ((params.slice_end_mask >>> i) & 0x1) {
end[i] = -1;
}
}
}
for (let i = 0; i < begin.length; i++) {
if (begin[i] === -1) {
begin[i] = 0;
}
}
if (inputs[0].length === end.length) {
for (let i = 0; i < end.length; i++) {
if (end[i] === -1 || end[i] > input_shape[i]) {
end[i] = input_shape[i];
}
}
} else if (inputs[0].length < end.length) {
if (params.slice_new_axis_mask) {
const len = (params.slice_new_axis_mask >>> 0).toString(2).length;
for (let i = 0; i < len; i++) {
if ((params.slice_new_axis_mask >>> i) & 0x1) {
input_shape.splice(i, 0, 1);
}
}
for (let i = 0; i < end.length; i++) {
if (end[i] === -1) {
end[i] = input_shape[i];
}
}
}
}
let newShape = [];
for (let i = 0; i < begin.length; i++) {
newShape = newShape.concat([(end[i] - begin[i]) / params.slice_strides[i]]);
}
if (params.slice_shrink_axis_mask) {
const len = (params.slice_shrink_axis_mask >>> 0).toString(2).length;
for (let i = 0; i < len; i++) {
if ((params.slice_shrink_axis_mask >>> i) & 0x1) {
newShape.splice(i, 1);
}
}
}
if (params.slice_new_axis_mask) {
const len = (params.slice_new_axis_mask >>> 0).toString(2).length;
for (let i = 0; i < len; i++) {
if ((params.slice_new_axis_mask >>> i) & 0x1) {
if (inputs[0].length === begin.length) {
newShape.splice(i, 0, 1);
} else if (inputs[0].length < begin.length) {
newShape[i] = 1;
}
}
}
}
return [newShape];
});
operators.set('image_resize', (inputs, params) => {
const newShape = inputs[0].slice();
newShape[1] = params.new_size[0];
newShape[2] = params.new_size[1];
return [newShape];
});
operators.set('argmax', (inputs, params) => {
const newShape = inputs[0].slice();
if (params.keepdims) {
newShape[params.axis] = 1;
} else {
newShape.splice(params.axis, 1);
if (!newShape.length) {
newShape.splice(0, 0, 0);
}
}
return [newShape];
});
operators.set('argmin', operators.get('argmax'));
/* eslint-disable no-unused-vars */
operators.set('shapelayer', (inputs, params) => {
return [[inputs[0].length]];
});
operators.set('capsule_norm', (inputs, params) => {
return [[inputs[0][0], inputs[0][inputs[0].length - 1]]];
});
operators.set('size', (inputs, params) => {
return [[1]];
});
/* eslint-enable no-unused-vars */
operators.set('einsum', ((operators, inputs, params) => {
const identifyOperation = (inputs, equation) => {
const identifyFuncs = new Map();
identifyFuncs.set('matmul', (inputs, equation) => {
if (inputs.length !== 2) {
return { found: false };
}
const parts = equation.replace(/\s+/g, '').split(/,|->/);
if (parts.length !== 3) {
return { found: false };
}
const [first, second, output] = parts.map((p) => p.split(''));
if (!(first.length === output.length || second.length === output.length)) {
return { found: false };
}
let a = first.slice(-2);
const b = second.slice(-2);
const c = output.slice(-2);
let transpose_a = false;
let transpose_b = false;
if (a[0] === c[0]) {
transpose_a = false;
} else if (a[1] === c[0]) {
transpose_a = true;
a = [].concat(a.reverse());
} else {
return { found: false };
}
if (a[1] === b[0]) {
transpose_b = false;
} else if (a[1] === b[1]) {
transpose_b = true;
} else {
return { found: false };
}
return { found: true, op: 'matmul', params: { transpose_a, transpose_b } };
});
/* eslint-disable no-unused-vars */
for (const [name, func] of identifyFuncs.entries()) {
const result = func(inputs, equation);
if (result.found) {
return result;
}
}
/* eslint-enable no-unused-vars */
return { found: false };
};
const result = identifyOperation(inputs, params.equation);
if (result.found) {
if (operators.has(result.op)) {
return operators.get(result.op)(inputs, result.params);
}
}
return [];
}).bind(undefined, operators));
const infer = (output) => {
if (outputs.has(output.name)) {
let ready = true;
const layer = outputs.get(output.name);
for (const input of layer.inputs) {
if (input.shape === null) {
infer(input);
if (input.shape === null) {
ready = false;
break;
}
}
}
if (ready) {
let callback = null;
if (operators.has(layer.op)) {
callback = operators.get(layer.op);
} else if (passthroughs.has(layer.op)) {
callback = (inputs) => [inputs[0].slice()];
} else if (broadcasts.has(layer.op)) {
callback = operators.get('broadcast');
} else if (reduces.has(layer.op)) {
callback = operators.get('reduce');
} else if (poolings.has(layer.op)) {
callback = operators.get('pooling');
}
if (!callback) {
callback = () => [];
}
const parameters = layer.parameters;
const inputs = layer.inputs.map((input) => input.shape);
const outputs = callback(inputs, parameters);
for (let i = 0; i < outputs.length; i++) {
if (i < layer.outputs.length) {
layer.outputs[i].shape = outputs[i];
}
}
}
}
};
for (const layer of outputLayers) {
for (const output of layer.outputs) {
infer(output);
}
}
}
};
acuity.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Acuity model.';
}
};
export const ModelFactory = acuity.ModelFactory;
+1108
View File
File diff suppressed because it is too large Load Diff
+422
View File
@@ -0,0 +1,422 @@
[
{
"name": "ActivationLayer",
"category": "Activation",
"attributes": [
{ "name": "activationFunction", "type": "ActivationFunction" },
{ "name": "a", "type": "float32" },
{ "name": "b", "type": "float32" }
]
},
{
"name": "AdditionLayer",
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "BatchNormalizationLayer",
"category": "Normalization",
"attributes": [
{ "name": "eps", "type": "float32" },
{ "name": "dataLayout", "type": "DataLayout" }
],
"inputs": [
{ "name": "input" },
{ "name": "mean" },
{ "name": "variance" },
{ "name": "beta" },
{ "name": "gamma" }
]
},
{
"name": "BatchToSpaceNdLayer",
"category": "Layer",
"attributes": [
{ "name": "blockShape", "type": "string" },
{ "name": "crops", "type": "string" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "ConcatLayer",
"category": "Tensor",
"attributes": [
{ "name": "concatAxis", "type": "uint32" },
{ "name": "numViews", "type": "uint32" },
{ "name": "numDimensions", "type": "uint32" }
]
},
{
"name": "ConstantLayer",
"category": "Constant",
"inputs": [
{ "name": "input" }
]
},
{
"name": "Convolution2dLayer",
"category": "Layer",
"attributes": [
{ "name": "padTop", "type": "uint32" },
{ "name": "padRight", "type": "uint32" },
{ "name": "padBottom", "type": "uint32" },
{ "name": "padLeft", "type": "uint32" },
{ "name": "strideX", "type": "uint32" },
{ "name": "strideY", "type": "uint32" },
{ "name": "dilationX", "type": "uint32" },
{ "name": "dilationY", "type": "uint32" },
{ "name": "dataLayout", "type": "DataLayout" }
],
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "biases" }
]
},
{
"name": "DepthwiseConvolution2dLayer",
"category": "Layer",
"attributes": [
{ "name": "padTop", "type": "uint32" },
{ "name": "padRight", "type": "uint32" },
{ "name": "padBottom", "type": "uint32" },
{ "name": "padLeft", "type": "uint32" },
{ "name": "strideX", "type": "uint32" },
{ "name": "strideY", "type": "uint32" },
{ "name": "dilationX", "type": "uint32" },
{ "name": "dilationY", "type": "uint32" },
{ "name": "dataLayout", "type": "DataLayout" }
],
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "biases" }
]
},
{
"name": "DequantizeLayer",
"category": "Quantization"
},
{
"name": "DetectionPostProcessLayer",
"attributes": [
{ "name": "maxDetections", "type": "uint32" },
{ "name": "maxClassesPerDetection", "type": "uint32" },
{ "name": "detectionsPerClass", "type": "uint32" },
{ "name": "nmsScoreThreshold", "type": "float32" },
{ "name": "numIouThreshold", "type": "float32" },
{ "name": "numClasses", "type": "uint32" },
{ "name": "useRegularNms", "type": "boolean" },
{ "name": "scaleX", "type": "float32" },
{ "name": "scaleY", "type": "float32" },
{ "name": "scaleW", "type": "float32" },
{ "name": "scaleH", "type": "float32" }
]
},
{
"name": "DivisionLayer",
"category": "Layer"
},
{
"name": "EqualLayer",
"category": "Layer"
},
{
"name": "FloorLayer",
"category": "Layer"
},
{
"name": "FullyConnectedLayer",
"category": "Layer",
"attributes": [
{ "name": "transposeWeightsMatrix", "type": "boolean" }
],
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "biases" }
]
},
{
"name": "GatherLayer",
"category": "Tensor"
},
{
"name": "GreaterLayer",
"category": "Layer"
},
{
"name": "InputLayer"
},
{
"name": "L2NormalizationLayer",
"category": "Normalization",
"attributes": [
{ "name": "eps", "type": "float32" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "LstmLayer",
"category": "Layer",
"attributes": [
{ "name": "activationFunc", "type": "uint32" },
{ "name": "clippingThresCell", "type": "float32" },
{ "name": "clippingThresProj", "type": "float32" },
{ "name": "cifgEnabled", "type": "boolean" },
{ "name": "peepholeEnabled", "type": "boolean" },
{ "name": "projectionEnabled", "type": "boolean" },
{ "name": "layerNormEnabled", "type": "boolean" }
],
"inputs": [
{ "name": "input" },
{ "name": "inputToForgetWeights1" },
{ "name": "inputToCellWeights1" },
{ "name": "inputToOutputWeights1" },
{ "name": "recurrentToForgetWeights1" },
{ "name": "recurrentToCellWeights1" },
{ "name": "recurrentToOutputWeights1" },
{ "name": "forgetGateBias1" },
{ "name": "cellBias1" },
{ "name": "outputGateBias1" },
{ "name": "inputToInputWeights1" },
{ "name": "recurrentToInputWeights1" },
{ "name": "cellToInputWeights1" },
{ "name": "inputGateBias1" },
{ "name": "projectionWeights1" },
{ "name": "projectionBias1" },
{ "name": "cellToForgetWeights1" },
{ "name": "cellToOutputWeights1" },
{ "name": "inputLayerNormWeights1" },
{ "name": "forgetLayerNormWeights1" },
{ "name": "cellLayerNormWeights1" },
{ "name": "outputLayerNormWeights1" }
]
},
{
"name": "MaximumLayer",
"category": "Layer"
},
{
"name": "MeanLayer",
"attributes": [
{ "name": "axis", "type": "uint32" },
{ "name": "keepDims", "type": "boolean" }
]
},
{
"name": "MergeLayer",
"category": "Layer"
},
{
"name": "MergerLayer",
"category": "Tensor"
},
{
"name": "MinimumLayer",
"category": "Layer"
},
{
"name": "MultiplicationLayer",
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "NormalizationLayer",
"category": "Normalization",
"attributes": [
{ "name": "normChannelType", "type": "NormalizationAlgorithmChannel" },
{ "name": "normMethodType", "type": "NormalizationAlgorithmMethod" },
{ "name": "normSize", "type": "uint32" },
{ "name": "alpha", "type": "float32" },
{ "name": "beta", "type": "float32" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "OutputLayer",
"category": "Tensor"
},
{
"name": "PadLayer",
"category": "Layer",
"attributes": [
{ "name": "padList", "type": "uint32" },
{ "name": "padValue", "type": "float32" }
]
},
{
"name": "PermuteLayer",
"category": "Shape",
"attributes": [
{ "name": "dimMappings", "type": "string" }
]
},
{
"name": "Pooling2dLayer",
"category": "Pool",
"attributes": [
{ "name": "poolType", "type": "PoolingAlgorithm" },
{ "name": "padTop", "type": "uint32" },
{ "name": "padRight", "type": "uint32" },
{ "name": "padBottom", "type": "uint32" },
{ "name": "padLeft", "type": "uint32" },
{ "name": "poolWidth", "type": "uint32" },
{ "name": "poolHeight", "type": "uint32" },
{ "name": "strideX", "type": "uint32" },
{ "name": "strideY", "type": "uint32" },
{ "name": "outputShapeRounding", "type": "OutputShapeRounding" },
{ "name": "paddingMethod", "type": "PaddingMethod" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "PreluLayer",
"category": "Layer"
},
{
"name": "QuantizedLstmLayer",
"category": "Layer",
"inputs": [
{ "name": "input" },
{ "name": "inputToInputWeights1" },
{ "name": "inputToForgetWeights1" },
{ "name": "inputToCellWeights1" },
{ "name": "inputToOutputWeights1" },
{ "name": "recurrentToInputWeights1" },
{ "name": "recurrentToForgetWeights1" },
{ "name": "recurrentToCellWeights1" },
{ "name": "recurrentToOutputWeights1" },
{ "name": "inputGateBias1" },
{ "name": "forgetGateBias1" },
{ "name": "cellBias1" },
{ "name": "outputGateBias1" }
]
},
{
"name": "QuantizeLayer",
"category": "Quantization"
},
{
"name": "ReshapeLayer",
"category": "Shape",
"attributes": [
{ "name": "targetShape", "type": "uint32[]" }
]
},
{
"name": "ResizeBilinearLayer",
"category": "Layer",
"attributes": [
{ "name": "targetWidth", "type": "uint32" },
{ "name": "targetHeight", "type": "uint32" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "ResizeLayer",
"category": "Layer",
"attributes": [
{ "name": "targetWidth", "type": "uint32" },
{ "name": "targetHeight", "type": "uint32" },
{ "name": "method", "type": "ResizeMethod" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "RsqrtLayer",
"category": "Layer"
},
{
"name": "SoftmaxLayer",
"category": "Activation",
"attributes": [
{ "name": "beta", "type": "float32" }
]
},
{
"name": "SpaceToBatchNdLayer",
"category": "Layer",
"attributes": [
{ "name": "blockShape", "type": "string" },
{ "name": "padList", "type": "string" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "SpaceToDepthLayer",
"category": "Layer",
"attributes": [
{ "name": "blockSize", "type": "uint32" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "SplitterLayer",
"category": "Tensor",
"attributes": [
{ "name": "concatAxis", "type": "uint32" },
{ "name": "numViews", "type": "uint32" },
{ "name": "numDimensions", "type": "uint32" }
]
},
{
"name": "StackLayer",
"category": "Layer",
"attributes": [
{ "name": "axis", "type": "uint32" },
{ "name": "numInputs", "type": "uint32" },
{ "name": "inputShape", "type": "uint32" }
]
},
{
"name": "StridedSliceLayer",
"category": "Tensor",
"attributes": [
{ "name": "begin", "type": "int32" },
{ "name": "end", "type": "int32" },
{ "name": "stride", "type": "int32" },
{ "name": "beginMask", "type": "int32" },
{ "name": "endMask", "type": "int32" },
{ "name": "shrinkAxisMask", "type": "int32" },
{ "name": "ellipsisMask", "type": "int32" },
{ "name": "newAxisMask", "type": "int32" },
{ "name": "dataLayout", "type": "DataLayout" }
]
},
{
"name": "SubtractionLayer"
},
{
"name": "SwitchLayer",
"category": "Layer"
},
{
"name": "TransposeConvolution2dLayer",
"category": "Layer",
"attributes": [
{ "name": "padTop", "type": "uint32" },
{ "name": "padRight", "type": "uint32" },
{ "name": "padBottom", "type": "uint32" },
{ "name": "padLeft", "type": "uint32" },
{ "name": "strideX", "type": "uint32" },
{ "name": "strideY", "type": "uint32" },
{ "name": "dataLayout", "type": "DataLayout" }
],
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "biases" }
]
}
]
File diff suppressed because it is too large Load Diff
+294
View File
@@ -0,0 +1,294 @@
const armnn = {};
armnn.ModelFactory = class {
async match(context) {
const identifier = context.identifier;
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
if (extension === 'armnn') {
const reader = await context.peek('flatbuffers.binary');
if (reader) {
return context.set('armnn.flatbuffers', reader);
}
}
if (extension === 'json') {
const obj = await context.peek('json');
if (obj && obj.layers && obj.inputIds && obj.outputIds) {
return context.set('armnn.flatbuffers.json', obj);
}
}
return null;
}
async open(context) {
armnn.schema = await context.require('./armnn-schema');
armnn.schema = armnn.schema.armnnSerializer;
let model = null;
switch (context.type) {
case 'armnn.flatbuffers': {
try {
const reader = await context.read('flatbuffers.binary');
model = armnn.schema.SerializedGraph.create(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new armnn.Error(`File format is not armnn.SerializedGraph (${message.replace(/\.$/, '')}).`);
}
break;
}
case 'armnn.flatbuffers.json': {
try {
const reader = await context.read('flatbuffers.text');
model = armnn.schema.SerializedGraph.createText(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new armnn.Error(`File text format is not armnn.SerializedGraph (${message.replace(/\.$/, '')}).`);
}
break;
}
default: {
throw new armnn.Error(`Unsupported Arm NN format '${context.type}'.`);
}
}
const metadata = await context.metadata('armnn-metadata.json');
return new armnn.Model(metadata, model);
}
};
armnn.Model = class {
constructor(metadata, model) {
this.format = 'Arm NN';
this.modules = [new armnn.Graph(metadata, model)];
}
};
armnn.Graph = class {
constructor(metadata, graph) {
this.name = '';
this.nodes = [];
this.inputs = [];
this.outputs = [];
const counts = new Map();
for (const layer of graph.layers) {
const base = armnn.Node.getBase(layer);
for (const slot of base.inputSlots) {
const name = `${slot.connection.sourceLayerIndex}:${slot.connection.outputSlotIndex}`;
counts.set(name, counts.has(name) ? counts.get(name) + 1 : 1);
}
}
const values = new Map();
const value = (layerIndex, slotIndex, tensor) => {
const name = `${layerIndex}:${slotIndex}`;
if (!values.has(name)) {
const layer = graph.layers[layerIndex];
const base = layerIndex < graph.layers.length ? armnn.Node.getBase(layer) : null;
const tensorInfo = base && slotIndex < base.outputSlots.length ? base.outputSlots[slotIndex].tensorInfo : null;
values.set(name, new armnn.Value(name, tensorInfo, tensor));
}
return values.get(name);
};
const layers = graph.layers.filter((layer) => {
const base = armnn.Node.getBase(layer);
if (base.layerType === armnn.schema.LayerType.Constant && base.outputSlots.length === 1 && layer.layer.input) {
const [slot] = base.outputSlots;
const name = `${base.index}:${slot.index}`;
if (counts.get(name) === 1) {
const tensor = new armnn.Tensor(layer.layer.input, 'Constant');
value(base.index, slot.index, tensor);
return false;
}
}
return true;
});
for (const layer of layers) {
const base = armnn.Node.getBase(layer);
for (const slot of base.inputSlots) {
value(slot.connection.sourceLayerIndex, slot.connection.outputSlotIndex);
}
}
for (const layer of layers) {
const base = armnn.Node.getBase(layer);
switch (base.layerType) {
case armnn.schema.LayerType.Input: {
const name = base ? base.layerName : '';
for (const slot of base.outputSlots) {
const argument = new armnn.Argument(name, [value(base.index, slot.index)]);
this.inputs.push(argument);
}
break;
}
case armnn.schema.LayerType.Output: {
const base = armnn.Node.getBase(layer);
const name = base ? base.layerName : '';
for (const slot of base.inputSlots) {
const argument = new armnn.Argument(name, [value(slot.connection.sourceLayerIndex, slot.connection.outputSlotIndex)]);
this.outputs.push(argument);
}
break;
}
default:
this.nodes.push(new armnn.Node(metadata, layer, value));
break;
}
}
}
};
armnn.Node = class {
constructor(metadata, layer, value) {
const name = layer.layer.constructor.name;
const type = metadata.type(name);
this.type = type ? { ...type } : { name };
this.type.name = this.type.name.replace(/Layer$/, '');
this.name = '';
this.outputs = [];
this.inputs = [];
this.attributes = [];
const inputSchemas = (this.type && this.type.inputs) ? [...this.type.inputs] : [{ name: 'input' }];
const outputSchemas = (this.type && this.type.outputs) ? [...this.type.outputs] : [{ name: 'output' }];
const base = armnn.Node.getBase(layer);
if (base) {
this.name = base.layerName;
const inputs = [...base.inputSlots];
while (inputs.length > 0) {
const schema = inputSchemas.length > 0 ? inputSchemas.shift() : { name: '?' };
const count = schema.list ? inputs.length : 1;
const argument = new armnn.Argument(schema.name, inputs.splice(0, count).map((inputSlot) => {
return value(inputSlot.connection.sourceLayerIndex, inputSlot.connection.outputSlotIndex);
}));
this.inputs.push(argument);
}
const outputs = [...base.outputSlots];
while (outputs.length > 0) {
const schema = outputSchemas.length > 0 ? outputSchemas.shift() : { name: '?' };
const count = schema.list ? outputs.length : 1;
this.outputs.push(new armnn.Argument(schema.name, outputs.splice(0, count).map((outputSlot) => {
return value(base.index, outputSlot.index);
})));
}
}
if (layer.layer) {
if (layer.layer.descriptor && this.type.attributes) {
for (const [key, obj] of Object.entries(layer.layer.descriptor)) {
const schema = metadata.attribute(name, key);
const type = schema ? schema.type : null;
let value = ArrayBuffer.isView(obj) ? Array.from(obj) : obj;
const enumType = armnn.schema[type];
if (enumType) {
value = enumType[value] || value;
}
const attribute = new armnn.Argument(key, value, type);
this.attributes.push(attribute);
}
}
for (const [name, tensor] of Object.entries(layer.layer).filter(([, value]) => value instanceof armnn.schema.ConstTensor)) {
const value = new armnn.Value('', tensor.info, new armnn.Tensor(tensor));
const argument = new armnn.Argument(name, [value]);
this.inputs.push(argument);
}
}
}
static getBase(layer) {
return layer.layer.base.base ? layer.layer.base.base : layer.layer.base;
}
static makeKey(layer_id, index) {
return `${layer_id}_${index}`;
}
};
armnn.Argument = class {
constructor(name, value, type = null) {
this.name = name;
this.value = value;
this.type = type;
}
};
armnn.Value = class {
constructor(name, tensorInfo, initializer) {
if (typeof name !== 'string') {
throw new armnn.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = new armnn.TensorType(tensorInfo);
this.initializer = initializer;
if (tensorInfo.quantizationScale !== 0 ||
tensorInfo.quantizationOffset !== 0 ||
tensorInfo.quantizationScales.length > 0 ||
tensorInfo.quantizationDim !== 0) {
this.quantization = {
type: 'linear',
dimension: tensorInfo.quantizationDim,
scale: [tensorInfo.quantizationScale],
offset: [tensorInfo.quantizationOffset]
};
}
}
};
armnn.Tensor = class {
constructor(tensor, category = '') {
this.type = new armnn.TensorType(tensor.info);
this.category = category;
const data = tensor.data.data.slice(0);
this.values = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
}
};
armnn.TensorType = class {
constructor(tensorInfo) {
const dataType = tensorInfo.dataType;
switch (dataType) {
case 0: this.dataType = 'float16'; break;
case 1: this.dataType = 'float32'; break;
case 2: this.dataType = 'quint8'; break; // QuantisedAsymm8
case 3: this.dataType = 'int32'; break;
case 4: this.dataType = 'boolean'; break;
case 5: this.dataType = 'qint16'; break; // QuantisedSymm16
case 6: this.dataType = 'quint8'; break; // QAsymmU8
case 7: this.dataType = 'qint16'; break; // QSymmS16
case 8: this.dataType = 'qint8'; break; // QAsymmS8
case 9: this.dataType = 'qint8'; break; // QSymmS8
default:
throw new armnn.Error(`Unsupported data type '${JSON.stringify(dataType)}'.`);
}
this.shape = new armnn.TensorShape(tensorInfo.dimensions);
}
toString() {
return this.dataType + this.shape.toString();
}
};
armnn.TensorShape = class {
constructor(dimensions) {
this.dimensions = Array.from(dimensions);
}
toString() {
if (!this.dimensions || this.dimensions.length === 0) {
return '';
}
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
};
armnn.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Arm NN model.';
}
};
export const ModelFactory = armnn.ModelFactory;
+448
View File
@@ -0,0 +1,448 @@
// Experimental
const barracuda = {};
barracuda.ModelFactory = class {
async match(context) {
const stream = context.stream;
if (stream && stream.length > 12) {
const buffer = stream.peek(12);
if (buffer[0] <= 0x20 && buffer.subarray(1, 8).every((value) => value === 0x00)) {
return context.set('barracuda');
}
}
return null;
}
async open(context) {
const metadata = barracuda.Metadata.open();
const reader = await context.read('binary');
const model = new barracuda.NNModel(reader);
return new barracuda.Model(metadata, model);
}
};
barracuda.Model = class {
constructor(metadata, model) {
const version = model.version.toString();
this.format = `Barracuda v${version}`;
this.modules = [new barracuda.Graph(metadata, model)];
}
};
barracuda.Graph = class {
constructor(metadata, model) {
this.name = '';
this.inputs = [];
this.outputs = [];
this.nodes = [];
const values = new Map();
values.map = (name, type, tensor) => {
if (!values.has(name)) {
type = tensor ? tensor.type : type;
values.set(name, new barracuda.Value(name, type, tensor));
} else if (type || tensor) {
throw new barracuda.Error(`Duplicate value '${name}'.`);
}
return values.get(name);
};
const layers = [];
for (const layer of model.layers) {
if (layer.type !== 255 || layer.inputs.length > 0) {
layers.push(layer);
} else {
for (const tensor of layer.tensors) {
values.map(tensor.name, null, new barracuda.Tensor(tensor));
}
}
}
for (const input of model.inputs) {
const shape = new barracuda.TensorShape(input.shape);
const type = new barracuda.TensorType(4, shape);
const argument = new barracuda.Argument(input.name, [values.map(input.name, type)]);
this.inputs.push(argument);
}
for (const output of model.outputs) {
const argument = new barracuda.Argument(output, [values.map(output)]);
this.outputs.push(argument);
}
for (const layer of layers) {
const node = new barracuda.Node(metadata, layer, null, values);
this.nodes.push(node);
}
}
};
barracuda.Argument = class {
constructor(name, value, type = null) {
this.name = name;
this.value = value;
this.type = type;
}
};
barracuda.Value = class {
constructor(name, type = null, initializer = null) {
this.name = name;
this.type = type;
this.initializer = initializer;
}
};
barracuda.Node = class {
constructor(metadata, layer, type, values) {
this.name = layer.name || '';
this.type = type ? type : metadata.type(layer.type);
this.inputs = [];
this.outputs = [];
this.attributes = [];
const inputs = Array.prototype.slice.call(this.type.inputs || ['input']);
if (this.type.inputs && this.type.inputs.length === 1 && this.type.inputs[0].name === 'inputs') {
const argument = new barracuda.Argument('inputs', layer.inputs.map((input) => values.map(input)));
this.inputs.push(argument);
} else if (layer.inputs) {
for (let i = 0; i < layer.inputs.length; i++) {
const input = layer.inputs[i];
const name = inputs.length > 0 && inputs[0] ? inputs.shift().name : i.toString();
const argument = new barracuda.Argument(name, [values.map(input)]);
this.inputs.push(argument);
}
}
if (layer.tensors) {
for (let i = 0; i < layer.tensors.length; i++) {
const tensor = layer.tensors[i];
const initializer = new barracuda.Tensor(tensor);
const name = inputs.length > 0 && inputs[0] ? inputs.shift().name : i.toString();
const argument = new barracuda.Argument(name, [values.map(tensor.name, initializer.type, initializer)]);
this.inputs.push(argument);
}
}
if (layer.inputs !== undefined) {
const argument = new barracuda.Argument('output', [values.map(this.name)]);
this.outputs.push(argument);
}
if (layer.activation !== undefined && (layer.type === 50 || layer.activation !== 0)) {
const type = barracuda.Activation[layer.activation];
if (!type) {
throw new barracuda.Error(`Unsupported activation '${layer.activation}'.`);
}
const node = new barracuda.Node(metadata, {}, { name: type, category: 'Activation' }, values);
this.chain = [node];
}
const attributes = [
['strides', 'int32[]', []],
['pads', 'int32[]', (value) => Array.isArray(value) && (value.every((v) => v === 0) || value.every((v) => v === -1))],
['pool_size', 'int32[]', []],
['alpha', 'float32', 1],
['beta', 'float32', 0],
['axis', 'int32', -1]
];
for (const [name, type, defaultValue] of attributes) {
const value = layer[name];
if ((value === undefined) ||
(Array.isArray(defaultValue) && Array.isArray(value) && value.length === defaultValue.length && value.every((v, i) => v === defaultValue[i])) ||
(typeof defaultValue === 'function' && defaultValue(value)) ||
(defaultValue === value)) {
continue;
}
const attribute = new barracuda.Argument(name, value, type);
this.attributes.push(attribute);
}
}
};
barracuda.Tensor = class {
constructor(tensor) {
this.type = new barracuda.TensorType(tensor.itemsize, new barracuda.TensorShape(tensor.shape));
this.values = tensor.data;
}
};
barracuda.TensorType = class {
constructor(itemsize, shape) {
switch (itemsize) {
case 4: this.dataType = 'float32'; break;
default: throw new barracuda.Error(`Unsupported data type size '${itemsize}'.`);
}
this.shape = shape;
}
toString() {
return this.dataType + this.shape.toString();
}
};
barracuda.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
toString() {
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension ? dimension.toString() : '?').join(',')}]`) : '';
}
};
barracuda.NNModel = class {
constructor(reader) {
// https://github.com/Unity-Technologies/barracuda-release/blob/release/1.3.2/Barracuda/Runtime/Core/Model.cs
reader = new barracuda.BinaryReader(reader);
this.version = reader.int32();
reader.int32();
this.inputs = new Array(reader.int32());
for (let i = 0; i < this.inputs.length; i++) {
this.inputs[i] = {
name: reader.string(),
shape: reader.shape()
};
}
this.outputs = reader.strings();
this.memories = new Array(reader.int32());
for (let i = 0; i < this.memories.length; i++) {
this.memories[i] = {
shape: reader.shape(),
in: reader.string(),
out: reader.string()
};
}
this.layers = new Array(reader.int32());
for (let i = 0; i < this.layers.length; i++) {
const layer = {};
layer.name = reader.string();
layer.type = reader.int32();
layer.activation = reader.int32();
reader.int32();
reader.int32();
layer.pads = reader.int32s();
layer.strides = reader.int32s();
layer.pool_size = reader.int32s();
layer.axis = reader.int32();
layer.alpha = reader.float32();
layer.beta = reader.float32();
reader.int32();
layer.inputs = reader.strings();
layer.tensors = [];
const tensorsLength = reader.int32();
for (let j = 0; j < tensorsLength; j++) {
layer.tensors.push({
name: reader.string(),
shape: reader.shape(),
offset: reader.int64().toNumber(),
itemsize: reader.int32(),
length: reader.int32()
});
}
this.layers[i] = layer;
}
const position = reader.position;
for (const layer of this.layers) {
for (const tensor of layer.tensors) {
const offset = tensor.offset;
reader.seek(position + (offset * tensor.itemsize));
tensor.data = reader.read(tensor.length * tensor.itemsize);
}
}
}
};
barracuda.Activation = {
0: "Linear", 1: "Relu", 2: "Softmax", 3: "Tanh", 4: "Sigmoid", 5: "Elu", 6: "Relu6", 7: "LeakyRelu", 8: "Selu", 9: "Swish",
10: "LogSoftmax", 11: "Softplus", 12: "Softsign", 13: "PRelu",
20: "Hardmax", 21: "HardSigmoid",
100: "Abs", 101: "Neg", 102: "Ceil", 103: "Clip", 104: "Floor", 105: "Round",
110: "Reciprocal", 111: "Sqrt", 113: "Exp", 114: "Log",
200: "Acos", 201: "Acosh", 202: "Asin", 203: "Asinh", 204: "Atan", 205: "Atanh", 206: "Cos", 207: "Cosh", 208: "Sin", 209: "Sinh", 210: "Tan"
};
barracuda.BinaryReader = class {
constructor(reader) {
this._reader = reader;
}
get position() {
return this._reader.position;
}
seek(position) {
this._reader.seek(position);
}
skip(offset) {
this._reader.skip(offset);
}
read(length) {
return this._reader.read(length);
}
byte() {
return this._reader.byte();
}
int32() {
return this._reader.int32();
}
int32s() {
const values = new Array(this.int32());
for (let i = 0; i < values.length; i++) {
values[i] = this.int32();
}
return values;
}
int64() {
return this._reader.int64();
}
float32() {
return this._reader.float32();
}
string() {
let content = '';
const size = this.int32();
for (let i = 0; i < size; i++) {
const c = this.byte();
content += String.fromCharCode(c);
}
return content;
}
strings() {
const values = [];
const length = this.int32();
for (let i = 0; i < length; i++) {
values.push(this.string());
}
return values;
}
shape() {
return this.int32s();
}
};
barracuda.Metadata = class {
static open() {
barracuda.Metadata._metadata = barracuda.Metadata._metadata || new barracuda.Metadata();
return barracuda.Metadata._metadata;
}
constructor() {
this._types = new Map();
const register = (id, name, category, inputs) => {
this._types.set(id, { name, category, inputs: (inputs || []).map((input) => {
return { name: input };
}) });
};
register(0, 'Nop', '');
register(1, 'Dense', 'Layer', ['input', 'kernel', 'bias']);
register(2, 'MatMul', '', ['input', 'kernel', 'bias']);
register(20, 'Conv2D', 'Layer', ['input', 'kernel', 'bias']);
register(21, 'DepthwiseConv2D', 'Layer', ['input', 'kernel', 'bias']);
register(22, 'Conv2DTrans', 'Layer', ['input', 'kernel', 'bias']);
register(23, 'Upsample2D', 'Data');
register(25, 'MaxPool2D', 'Pool');
register(26, 'AvgPool2D', 'Pool');
register(27, 'GlobalMaxPool2D', 'Pool');
register(28, 'GlobalAvgPool2D', 'Pool');
register(29, 'Border2D', '');
register(30, 'Conv3D', 'Layer');
register(32, 'Conv3DTrans', 'Layer');
register(33, 'Upsample3D', 'Data');
register(35, 'MaxPool3D', 'Pool');
register(36, 'AvgPool3D', 'Pool');
register(37, 'GlobalMaxPool3D', 'Pool');
register(38, 'GlobalAvgPool3D', 'Pool');
register(39, 'Border3D', '');
register(50, 'Activation', '', ['input']);
register(51, 'ScaleBias', 'Normalization', ['input', 'scale', 'bias']);
register(52, 'Normalization', 'Normalization');
register(53, 'LRN', 'Normalization');
register(60, 'Dropout', 'Dropout');
register(64, 'RandomNormal', '');
register(65, 'RandomUniform', '');
register(66, 'Multinomial', '');
register(67, 'OneHot', '');
register(68, 'TopKIndices', '');
register(69, 'TopKValues', '');
register(100, 'Add', '', ['inputs']);
register(101, 'Sub', '', ['inputs']);
register(102, 'Mul', '', ['inputs']);
register(103, 'RealDiv', '', ['inputs']);
register(104, 'Pow', '', ['inputs']);
register(110, 'Minimum', '', ['inputs']);
register(111, 'Maximum', '', ['inputs']);
register(112, 'Mean', '', ['inputs']);
register(120, 'ReduceL1', '', ['inputs']);
register(121, 'ReduceL2', '', ['inputs']);
register(122, 'ReduceLogSum', '', ['inputs']);
register(123, 'ReduceLogSumExp', '', ['inputs']);
register(124, 'ReduceMax', '', ['inputs']);
register(125, 'ReduceMean', '', ['inputs']);
register(126, 'ReduceMin', '', ['inputs']);
register(127, 'ReduceProd', '', ['inputs']);
register(128, 'ReduceSum', '', ['inputs']);
register(129, 'ReduceSumSquare', '', ['inputs']);
register(140, 'Greater', '');
register(141, 'GreaterEqual', '');
register(142, 'Less', '');
register(143, 'LessEqual', '');
register(144, 'Equal', '');
register(145, 'LogicalOr', '');
register(146, 'LogicalAnd', '');
register(147, 'LogicalNot', '');
register(148, 'LogicalXor', '');
register(160, 'Pad2DReflect', '');
register(161, 'Pad2DSymmetric', '');
register(162, 'Pad2DEdge', '');
register(200, 'Flatten', 'Shape');
register(201, 'Reshape', 'Shape');
register(202, 'Transpose', '');
register(203, 'Squeeze', '');
register(204, 'Unsqueeze', '');
register(205, 'Gather', '');
register(206, 'DepthToSpace', '');
register(207, 'SpaceToDepth', '');
register(208, 'Expand', '');
register(209, 'Resample2D', '');
register(210, 'Concat', 'Tensor', ['inputs']);
register(211, 'StridedSlice', 'Shape');
register(212, 'Tile', '');
register(213, 'Shape', '');
register(214, 'NonMaxSuppression', '');
register(215, 'LSTM', '');
register(255, 'Load', '');
}
type(name) {
if (!this._types.has(name)) {
this._types.set(name, { name: name.toString() });
}
return this._types.get(name);
}
};
barracuda.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Barracuda model.';
}
};
export const ModelFactory = barracuda.ModelFactory;
+1435
View File
File diff suppressed because it is too large Load Diff
+95
View File
@@ -0,0 +1,95 @@
[
{
"name": "com.intel.analytics.bigdl.nn.Dropout",
"category": "Dropout"
},
{
"name": "com.intel.analytics.bigdl.nn.InferReshape",
"category": "Shape"
},
{
"name": "com.intel.analytics.bigdl.nn.JoinTable",
"category": "Tensor",
"inputs": [
{ "name": "inputs", "list": true }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "com.intel.analytics.bigdl.nn.Linear",
"category": "Layer",
"inputs": [
{ "name": "inputs" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "com.intel.analytics.bigdl.nn.NormalizeScale",
"category": "Normalization",
"inputs": [
{ "name": "inputs" },
{ "name": "w" }
]
},
{
"name": "com.intel.analytics.bigdl.nn.ReLU",
"category": "Activation"
},
{
"name": "Scale",
"category": "Layer",
"inputs": [
{ "name": "inputs" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "SoftMax",
"category": "Activation"
},
{
"name": "com.intel.analytics.bigdl.nn.SpatialAveragePooling",
"category": "Pool"
},
{
"name": "com.intel.analytics.bigdl.nn.SpatialBatchNormalization",
"category": "Normalization"
},
{
"name": "com.intel.analytics.bigdl.nn.quantized.SpatialConvolution",
"category": "Layer",
"inputs": [
{ "name": "inputs" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "com.intel.analytics.bigdl.nn.SpatialCrossMapLRN",
"category": "Normalization"
},
{
"name": "com.intel.analytics.bigdl.nn.SpatialDilatedConvolution",
"category": "Layer",
"inputs": [
{ "name": "inputs" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "com.intel.analytics.bigdl.nn.SpatialMaxPooling",
"category": "Pool"
},
{
"name": "com.intel.analytics.bigdl.nn.Transpose",
"category": "Shape"
},
{
"name": "com.intel.analytics.bigdl.nn.View"
}
]
+630
View File
@@ -0,0 +1,630 @@
export const com = {};
export const google = {};
com.intel = {};
com.intel.analytics = {};
com.intel.analytics.bigdl = {};
com.intel.analytics.bigdl.serialization = {};
com.intel.analytics.bigdl.serialization.BigDLModule = class BigDLModule {
constructor() {
this.subModules = [];
this.preModules = [];
this.nextModules = [];
this.attr = {};
this.parameters = [];
this.inputScales = [];
this.outputScales = [];
this.weightScales = [];
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.BigDLModule();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.name = reader.string();
break;
case 2:
message.subModules.push(com.intel.analytics.bigdl.serialization.BigDLModule.decode(reader, reader.uint32()));
break;
case 3:
message.weight = com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32());
break;
case 4:
message.bias = com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32());
break;
case 5:
message.preModules.push(reader.string());
break;
case 6:
message.nextModules.push(reader.string());
break;
case 7:
message.moduleType = reader.string();
break;
case 8:
reader.entry(message.attr, () => reader.string(), () => com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
break;
case 9:
message.version = reader.string();
break;
case 10:
message.train = reader.bool();
break;
case 11:
message.namePostfix = reader.string();
break;
case 12:
message.id = reader.int32();
break;
case 13:
message.inputShape = com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32());
break;
case 14:
message.outputShape = com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32());
break;
case 15:
message.hasParameters = reader.bool();
break;
case 16:
message.parameters.push(com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32()));
break;
case 17:
message.isMklInt8Enabled = reader.bool();
break;
case 18:
message.inputDimMasks = reader.int32();
break;
case 19:
message.inputScales.push(com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
break;
case 20:
message.outputDimMasks = reader.int32();
break;
case 21:
message.outputScales.push(com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
break;
case 22:
message.weightDimMasks = reader.int32();
break;
case 23:
message.weightScales.push(com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.name = "";
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.weight = null;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.bias = null;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.moduleType = "";
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.version = "";
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.train = false;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.namePostfix = "";
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.id = 0;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.inputShape = null;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.outputShape = null;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.hasParameters = false;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.isMklInt8Enabled = false;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.inputDimMasks = 0;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.outputDimMasks = 0;
com.intel.analytics.bigdl.serialization.BigDLModule.prototype.weightDimMasks = 0;
com.intel.analytics.bigdl.serialization.VarFormat = {
"EMPTY_FORMAT": 0,
"DEFAULT": 1,
"ONE_D": 2,
"IN_OUT": 3,
"OUT_IN": 4,
"IN_OUT_KW_KH": 5,
"OUT_IN_KW_KH": 6,
"GP_OUT_IN_KW_KH": 7,
"GP_IN_OUT_KW_KH": 8,
"OUT_IN_KT_KH_KW": 9
};
com.intel.analytics.bigdl.serialization.InitMethodType = {
"EMPTY_INITIALIZATION": 0,
"RANDOM_UNIFORM": 1,
"RANDOM_UNIFORM_PARAM": 2,
"RANDOM_NORMAL": 3,
"ZEROS": 4,
"ONES": 5,
"CONST": 6,
"XAVIER": 7,
"BILINEARFILLER": 8
};
com.intel.analytics.bigdl.serialization.RegularizerType = {
"L1L2Regularizer": 0,
"L1Regularizer": 1,
"L2Regularizer": 2
};
com.intel.analytics.bigdl.serialization.InputDataFormat = {
"NCHW": 0,
"NHWC": 1
};
com.intel.analytics.bigdl.serialization.TensorType = {
"DENSE": 0,
"QUANT": 1
};
com.intel.analytics.bigdl.serialization.InitMethod = class InitMethod {
constructor() {
this.data = [];
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.InitMethod();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.methodType = reader.int32();
break;
case 2:
message.data = reader.doubles(message.data, tag);
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.InitMethod.prototype.methodType = 0;
com.intel.analytics.bigdl.serialization.BigDLTensor = class BigDLTensor {
constructor() {
this.size = [];
this.stride = [];
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.BigDLTensor();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.datatype = reader.int32();
break;
case 2:
message.size = reader.array(message.size, () => reader.int32(), tag);
break;
case 3:
message.stride = reader.array(message.stride, () => reader.int32(), tag);
break;
case 4:
message.offset = reader.int32();
break;
case 5:
message.dimension = reader.int32();
break;
case 6:
message.nElements = reader.int32();
break;
case 7:
message.isScalar = reader.bool();
break;
case 8:
message.storage = com.intel.analytics.bigdl.serialization.TensorStorage.decode(reader, reader.uint32());
break;
case 9:
message.id = reader.int32();
break;
case 10:
message.tensorType = reader.int32();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.datatype = 0;
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.offset = 0;
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.dimension = 0;
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.nElements = 0;
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.isScalar = false;
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.storage = null;
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.id = 0;
com.intel.analytics.bigdl.serialization.BigDLTensor.prototype.tensorType = 0;
com.intel.analytics.bigdl.serialization.TensorStorage = class TensorStorage {
constructor() {
this.float_data = [];
this.double_data = [];
this.bool_data = [];
this.string_data = [];
this.int_data = [];
this.long_data = [];
this.bytes_data = [];
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.TensorStorage();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.datatype = reader.int32();
break;
case 2:
message.float_data = reader.floats(message.float_data, tag);
break;
case 3:
message.double_data = reader.doubles(message.double_data, tag);
break;
case 4:
message.bool_data = reader.array(message.bool_data, () => reader.bool(), tag);
break;
case 5:
message.string_data.push(reader.string());
break;
case 6:
message.int_data = reader.array(message.int_data, () => reader.int32(), tag);
break;
case 7:
message.long_data = reader.array(message.long_data, () => reader.int64(), tag);
break;
case 8:
message.bytes_data.push(reader.bytes());
break;
case 9:
message.id = reader.int32();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.TensorStorage.prototype.datatype = 0;
com.intel.analytics.bigdl.serialization.TensorStorage.prototype.id = 0;
com.intel.analytics.bigdl.serialization.Regularizer = class Regularizer {
constructor() {
this.regularData = [];
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.Regularizer();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.regularizerType = reader.int32();
break;
case 2:
message.regularData = reader.doubles(message.regularData, tag);
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.Regularizer.prototype.regularizerType = 0;
com.intel.analytics.bigdl.serialization.DataType = {
"INT32": 0,
"INT64": 1,
"FLOAT": 2,
"DOUBLE": 3,
"STRING": 4,
"BOOL": 5,
"CHAR": 6,
"SHORT": 7,
"BYTES": 8,
"REGULARIZER": 9,
"TENSOR": 10,
"VARIABLE_FORMAT": 11,
"INITMETHOD": 12,
"MODULE": 13,
"NAME_ATTR_LIST": 14,
"ARRAY_VALUE": 15,
"DATA_FORMAT": 16,
"CUSTOM": 17,
"SHAPE": 18
};
com.intel.analytics.bigdl.serialization.AttrValue = class AttrValue {
get value() {
com.intel.analytics.bigdl.serialization.AttrValue.valueSet = com.intel.analytics.bigdl.serialization.AttrValue.valueSet || new Set(["int32Value", "int64Value", "floatValue", "doubleValue", "stringValue", "boolValue", "regularizerValue", "tensorValue", "variableFormatValue", "initMethodValue", "bigDLModuleValue", "nameAttrListValue", "arrayValue", "dataFormatValue", "customValue", "shape"]);
return Object.keys(this).find((key) => com.intel.analytics.bigdl.serialization.AttrValue.valueSet.has(key) && this[key] !== null);
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.AttrValue();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.dataType = reader.int32();
break;
case 2:
message.subType = reader.string();
break;
case 3:
message.int32Value = reader.int32();
break;
case 4:
message.int64Value = reader.int64();
break;
case 5:
message.floatValue = reader.float();
break;
case 6:
message.doubleValue = reader.double();
break;
case 7:
message.stringValue = reader.string();
break;
case 8:
message.boolValue = reader.bool();
break;
case 9:
message.regularizerValue = com.intel.analytics.bigdl.serialization.Regularizer.decode(reader, reader.uint32());
break;
case 10:
message.tensorValue = com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32());
break;
case 11:
message.variableFormatValue = reader.int32();
break;
case 12:
message.initMethodValue = com.intel.analytics.bigdl.serialization.InitMethod.decode(reader, reader.uint32());
break;
case 13:
message.bigDLModuleValue = com.intel.analytics.bigdl.serialization.BigDLModule.decode(reader, reader.uint32());
break;
case 14:
message.nameAttrListValue = com.intel.analytics.bigdl.serialization.NameAttrList.decode(reader, reader.uint32());
break;
case 15:
message.arrayValue = com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue.decode(reader, reader.uint32());
break;
case 16:
message.dataFormatValue = reader.int32();
break;
case 17:
message.customValue = google.protobuf.Any.decode(reader, reader.uint32());
break;
case 18:
message.shape = com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32());
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.AttrValue.prototype.dataType = 0;
com.intel.analytics.bigdl.serialization.AttrValue.prototype.subType = "";
com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue = class ArrayValue {
constructor() {
this.i32 = [];
this.i64 = [];
this.flt = [];
this.dbl = [];
this.str = [];
this.boolean = [];
this.Regularizer = [];
this.tensor = [];
this.variableFormat = [];
this.initMethod = [];
this.bigDLModule = [];
this.nameAttrList = [];
this.dataFormat = [];
this.custom = [];
this.shape = [];
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.size = reader.int32();
break;
case 2:
message.datatype = reader.int32();
break;
case 3:
message.i32 = reader.array(message.i32, () => reader.int32(), tag);
break;
case 4:
message.i64 = reader.array(message.i64, () => reader.int64(), tag);
break;
case 5:
message.flt = reader.floats(message.flt, tag);
break;
case 6:
message.dbl = reader.doubles(message.dbl, tag);
break;
case 7:
message.str.push(reader.string());
break;
case 8:
message.boolean = reader.array(message.boolean, () => reader.bool(), tag);
break;
case 9:
message.Regularizer.push(com.intel.analytics.bigdl.serialization.Regularizer.decode(reader, reader.uint32()));
break;
case 10:
message.tensor.push(com.intel.analytics.bigdl.serialization.BigDLTensor.decode(reader, reader.uint32()));
break;
case 11:
message.variableFormat = reader.array(message.variableFormat, () => reader.int32(), tag);
break;
case 12:
message.initMethod.push(com.intel.analytics.bigdl.serialization.InitMethod.decode(reader, reader.uint32()));
break;
case 13:
message.bigDLModule.push(com.intel.analytics.bigdl.serialization.BigDLModule.decode(reader, reader.uint32()));
break;
case 14:
message.nameAttrList.push(com.intel.analytics.bigdl.serialization.NameAttrList.decode(reader, reader.uint32()));
break;
case 15:
message.dataFormat = reader.array(message.dataFormat, () => reader.int32(), tag);
break;
case 16:
message.custom.push(google.protobuf.Any.decode(reader, reader.uint32()));
break;
case 17:
message.shape.push(com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32()));
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue.prototype.size = 0;
com.intel.analytics.bigdl.serialization.AttrValue.ArrayValue.prototype.datatype = 0;
com.intel.analytics.bigdl.serialization.NameAttrList = class NameAttrList {
constructor() {
this.attr = {};
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.NameAttrList();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.name = reader.string();
break;
case 2:
reader.entry(message.attr, () => reader.string(), () => com.intel.analytics.bigdl.serialization.AttrValue.decode(reader, reader.uint32()));
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.NameAttrList.prototype.name = "";
com.intel.analytics.bigdl.serialization.Shape = class Shape {
constructor() {
this.shapeValue = [];
this.shape = [];
}
static decode(reader, length) {
const message = new com.intel.analytics.bigdl.serialization.Shape();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.shapeType = reader.int32();
break;
case 2:
message.ssize = reader.int32();
break;
case 3:
message.shapeValue = reader.array(message.shapeValue, () => reader.int32(), tag);
break;
case 4:
message.shape.push(com.intel.analytics.bigdl.serialization.Shape.decode(reader, reader.uint32()));
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
com.intel.analytics.bigdl.serialization.Shape.prototype.shapeType = 0;
com.intel.analytics.bigdl.serialization.Shape.prototype.ssize = 0;
com.intel.analytics.bigdl.serialization.Shape.ShapeType = {
"SINGLE": 0,
"MULTI": 1
};
google.protobuf = {};
google.protobuf.Any = class Any {
static decode(reader, length) {
const message = new google.protobuf.Any();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.type_url = reader.string();
break;
case 2:
message.value = reader.bytes();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
google.protobuf.Any.prototype.type_url = "";
google.protobuf.Any.prototype.value = new Uint8Array([]);
+302
View File
@@ -0,0 +1,302 @@
// Experimental
const bigdl = {};
bigdl.ModelFactory = class {
async match(context) {
const tags = await context.tags('pb');
if (tags.has(2) && tags.has(7) && tags.has(8) &&
tags.has(9) && tags.has(10) && tags.has(11) && tags.has(12)) {
return context.set('bigdl');
}
return null;
}
async open(context) {
bigdl.proto = await context.require('./bigdl-proto');
bigdl.proto = bigdl.proto.com.intel.analytics.bigdl.serialization;
let module = null;
try {
// https://github.com/intel-analytics/BigDL/blob/master/spark/dl/src/main/resources/serialization/bigdl.proto
const reader = await context.read('protobuf.binary');
module = bigdl.proto.BigDLModule.decode(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new bigdl.Error(`File format is not bigdl.BigDLModule (${message.replace(/\.$/, '')}).`);
}
const metadata = await context.metadata('bigdl-metadata.json');
return new bigdl.Model(metadata, module);
}
};
bigdl.Model = class {
constructor(metadata, module) {
const version = module && module.version ? module.version : '';
this.format = `BigDL${version ? ` v${version}` : ''}`;
this.modules = [new bigdl.Graph(metadata, module)];
}
};
bigdl.Graph = class {
constructor(metadata, module) {
this.inputs = [];
this.outputs = [];
this.nodes = [];
this.description = module.moduleType;
const tensors = module.attr && module.attr.global_storage && module.attr.global_storage.nameAttrListValue && module.attr.global_storage.nameAttrListValue.attr ? module.attr.global_storage.nameAttrListValue.attr : {};
const values = new Map();
values.map = (name) => {
if (!values.has(name)) {
values.set(name, new bigdl.Value(name));
}
return values.get(name);
};
const loadModule = (metadata, module, tensors) => {
switch (module.moduleType) {
case 'com.intel.analytics.bigdl.nn.StaticGraph':
case 'com.intel.analytics.bigdl.nn.Sequential': {
for (const submodule of module.subModules) {
loadModule(metadata, submodule, tensors);
}
break;
}
case 'com.intel.analytics.bigdl.nn.Input': {
const argument = new bigdl.Argument(module.name, [values.map(module.name)]);
this.inputs.push(argument);
break;
}
default: {
const node = new bigdl.Node(metadata, module, tensors, values);
this.nodes.push(node);
break;
}
}
};
loadModule(metadata, module, tensors);
}
};
bigdl.Argument = class {
constructor(name, value, type = null) {
this.name = name;
this.value = value;
this.type = type;
}
};
bigdl.Value = class {
constructor(name, type, initializer) {
if (typeof name !== 'string') {
throw new bigdl.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = !type && initializer ? initializer.type : type;
this.initializer = initializer;
}
};
bigdl.Node = class {
constructor(metadata, module, tensors, values) {
const type = module.moduleType;
this.name = module.name;
this.attributes = [];
this.inputs = [];
this.outputs = [];
this.inputs.push(new bigdl.Argument('input', module.preModules.map((id) => values.map(id))));
this.type = metadata.type(type) || { name: type };
const inputs = this.type && this.type.inputs ? this.type.inputs.slice() : [];
inputs.shift();
if (module.weight) {
inputs.shift();
this.inputs.push(new bigdl.Argument('weight', [
new bigdl.Value('', null, new bigdl.Tensor(module.weight, tensors))
]));
}
if (module.bias) {
inputs.shift();
this.inputs.push(new bigdl.Argument('bias', [
new bigdl.Value('', null, new bigdl.Tensor(module.bias, tensors))
]));
}
if (module.parameters && module.parameters.length > 0) {
for (const parameter of module.parameters) {
const input = inputs.shift();
const inputName = input ? input.name : this.inputs.length.toString();
this.inputs.push(new bigdl.Argument(inputName, [
new bigdl.Value('', null, new bigdl.Tensor(parameter, tensors))
]));
}
}
for (const [key, obj] of Object.entries(module.attr)) {
if (key === 'module_numerics' || key === 'module_tags') {
continue;
}
if (obj.dataType === bigdl.proto.DataType.TENSOR) {
if (obj.value) {
this.inputs.push(new bigdl.Argument(key, [new bigdl.Value('', null, new bigdl.Tensor(obj.tensorValue, tensors))]));
}
continue;
}
if (obj.dataType === bigdl.proto.DataType.REGULARIZER && obj.value === undefined) {
continue;
}
if (obj.dataType === bigdl.proto.DataType.ARRAY_VALUE && obj.arrayValue.datatype === bigdl.proto.DataType.TENSOR) {
this.inputs.push(new bigdl.Argument(key, obj.arrayValue.tensor.map((tensor) => new bigdl.Value('', null, new bigdl.Tensor(tensor, tensors)))));
continue;
}
let type = null;
let value = null;
switch (obj.dataType) {
case bigdl.proto.DataType.INT32: {
type = 'int32';
value = obj.int32Value;
break;
}
case bigdl.proto.DataType.FLOAT: {
type = 'float32';
value = obj.floatValue;
break;
}
case bigdl.proto.DataType.DOUBLE: {
type = 'float64';
value = obj.doubleValue;
break;
}
case bigdl.proto.DataType.BOOL: {
type = 'boolean';
value = obj.boolValue;
break;
}
case bigdl.proto.DataType.REGULARIZER: {
value = obj.value;
break;
}
case bigdl.proto.DataType.MODULE: {
value = obj.bigDLModule;
break;
}
case bigdl.proto.DataType.NAME_ATTR_LIST: {
value = value.nameAttrListValue;
break;
}
case bigdl.proto.DataType.ARRAY_VALUE: {
switch (obj.arrayValue.datatype) {
case bigdl.proto.DataType.INT32: {
type = 'int32[]';
value = obj.arrayValue.i32;
break;
}
case bigdl.proto.DataType.FLOAT: {
type = 'float32[]';
value = obj.arrayValue.flt;
break;
}
case bigdl.proto.DataType.STRING: {
type = 'string[]';
value = obj.arrayValue.str;
break;
}
case bigdl.proto.DataType.TENSOR: {
type = 'tensor[]';
value = obj.arrayValue.tensor;
break;
}
default: {
throw new bigdl.Error(`Unsupported attribute array data type '${obj.arrayValue.datatype}'.`);
}
}
break;
}
case bigdl.proto.DataType.DATA_FORMAT: {
switch (obj.dataFormatValue) {
case 0: value = 'NCHW'; break;
case 1: value = 'NHWC'; break;
default: throw new bigdl.Error(`Unsupported data format '${obj.dataFormatValue}'.`);
}
break;
}
default: {
throw new bigdl.Error(`Unsupported attribute data type '${obj.dataType}'.`);
}
}
const argument = new bigdl.Argument(key, value, type);
this.attributes.push(argument);
}
const output = this.name || this.type + module.namePostfix;
this.outputs.push(new bigdl.Argument('output', [values.map(output)]));
}
};
bigdl.Tensor = class {
constructor(tensor /*, tensors */) {
this.type = new bigdl.TensorType(tensor.datatype, new bigdl.TensorShape(tensor.size));
/*
if (tensor && tensor.id && tensors && tensors[tensor.id] && tensors[tensor.id].tensorValue && tensors[tensor.id].tensorValue.storage) {
const storage = tensors[tensor.id].tensorValue.storage;
switch (this.type.dataType) {
case 'float32':
if (storage.bytes_data && storage.bytes_data.length > 0) {
this.values = storage.bytes_data[0];
this.encoding = '<';
}
else if (storage.float_data && storage.float_data.length > 0) {
this.values = storage.float_data;
this.encoding = '|';
}
break;
default:
break;
}
}
*/
}
};
bigdl.TensorType = class {
constructor(dataType, shape) {
switch (dataType) {
case bigdl.proto.DataType.FLOAT: this.dataType = 'float32'; break;
case bigdl.proto.DataType.DOUBLE: this.dataType = 'float64'; break;
default: throw new bigdl.Error(`Unsupported tensor type '${dataType}'.`);
}
this.shape = shape;
}
toString() {
return (this.dataType || '?') + this.shape.toString();
}
};
bigdl.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
if (!dimensions.every((dimension) => Number.isInteger(dimension))) {
throw new bigdl.Error(`Invalid tensor shape '${JSON.stringify(dimensions)}'.`);
}
}
toString() {
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`) : '';
}
};
bigdl.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading BigDL model.';
}
};
export const ModelFactory = bigdl.ModelFactory;
+916
View File
@@ -0,0 +1,916 @@
import * as base from './base.js';
const browser = {};
browser.Host = class {
constructor() {
this._window = window;
this._navigator = window.navigator;
this._document = window.document;
this._telemetry = new base.Telemetry(this._window);
this._window.eval = () => {
throw new Error('window.eval() not supported.');
};
this._meta = {};
for (const element of Array.from(this._document.getElementsByTagName('meta'))) {
if (element.name !== undefined && element.name !== '' && element.content !== undefined) {
this._meta[element.name] = this._meta[element.name] || [];
this._meta[element.name].push(element.content);
}
}
this._environment = {
name: this._document.title,
type: this._meta.type ? this._meta.type[0] : 'Browser',
version: this._meta.version ? this._meta.version[0] : null,
date: Array.isArray(this._meta.date) && this._meta.date.length > 0 && this._meta.date[0] ? new Date(`${this._meta.date[0].split(' ').join('T')}Z`) : new Date(),
packaged: this._meta.version && this._meta.version[0] !== '0.0.0',
platform: /(Mac|iPhone|iPod|iPad)/i.test(this._navigator.platform) ? 'darwin' : undefined,
agent: this._navigator.userAgent.toLowerCase().indexOf('safari') !== -1 && this._navigator.userAgent.toLowerCase().indexOf('chrome') === -1 ? 'safari' : '',
repository: this._element('logo-github').getAttribute('href'),
menu: true
};
if (this.version && !/^\d+\.\d+\.\d+$/.test(this.version)) {
throw new Error('Invalid version.');
}
}
get window() {
return this._window;
}
get document() {
return this._document;
}
get version() {
return this._environment.version;
}
get type() {
return this._environment.type;
}
async view(view) {
const window = this.window;
const document = this.document;
this._view = view;
const age = async () => {
const days = (new Date() - new Date(this._environment.date)) / (24 * 60 * 60 * 1000);
if (days > 180) {
const link = this._element('logo-github').href;
document.body.classList.remove('spinner');
for (;;) {
// eslint-disable-next-line no-await-in-loop
await this.message('Please update to the newest version.', null, 'Update');
this.openURL(link);
}
}
return Promise.resolve();
};
const consent = async () => {
if (this._getCookie('consent') || this._getCookie('_ga')) {
return;
}
let consent = true;
try {
const text = await this._fetch('https://ipinfo.io/json', { 'Content-Type': 'application/json' }, 'utf-8', null, 2000);
const json = JSON.parse(text);
const countries = ['AT', 'BE', 'BG', 'HR', 'CZ', 'CY', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'SK', 'ES', 'SE', 'GB', 'UK', 'GR', 'EU', 'RO'];
if (json && json.country && countries.indexOf(json.country) === -1) {
consent = false;
}
} catch {
// continue regardless of error
}
if (consent) {
document.body.classList.remove('spinner');
await this.message('This app uses cookies to report errors and anonymous usage information.', null, 'Accept');
}
this._setCookie('consent', Date.now().toString(), 30);
};
const telemetry = async () => {
if (this._environment.packaged) {
window.addEventListener('error', (event) => {
if (event instanceof window.ErrorEvent && event.error && event.error instanceof Error) {
this.exception(event.error, true);
} else {
const message = event && event.message ? event.message : JSON.stringify(event);
const error = new Error(message);
this.exception(error, true);
}
});
const measurement_id = '848W2NVWVH';
const user = this._getCookie('_ga').replace(/^(GA1\.\d\.)*/, '');
const session = this._getCookie(`_ga${measurement_id}`);
await this._telemetry.start(`G-${measurement_id}`, user, session);
this._telemetry.set('page_location', document.location && document.location.href ? document.location.href : null);
this._telemetry.set('page_title', document.title ? document.title : null);
this._telemetry.set('page_referrer', document.referrer ? document.referrer : null);
this._telemetry.send('page_view', {
app_name: this.type,
app_version: this.version,
});
this._telemetry.send('scroll', {
percent_scrolled: 90,
app_name: this.type,
app_version: this.version
});
this._setCookie('_ga', `GA1.2.${this._telemetry.get('client_id')}`, 1200);
this._setCookie(`_ga${measurement_id}`, `GS1.1.${this._telemetry.session}`, 1200);
}
};
const capabilities = async () => {
const filter = (list) => {
return list.filter((capability) => {
const path = capability.split('.').reverse();
let obj = window[path.pop()];
while (obj && path.length > 0) {
obj = obj[path.pop()];
}
return obj;
});
};
const capabilities = filter(['fetch', 'DataView.prototype.getBigInt64', 'Worker', 'Array.prototype.flat']);
this.event('browser_open', {
browser_capabilities: capabilities.map((capability) => capability.split('.').pop()).join(',')
});
return Promise.resolve();
};
await age();
await consent();
await telemetry();
await capabilities();
}
async start() {
if (this._meta.file) {
const [url] = this._meta.file;
if (this._view.accept(url)) {
const identifier = Array.isArray(this._meta.identifier) && this._meta.identifier.length === 1 ? this._meta.identifier[0] : null;
const name = this._meta.name || null;
const status = await this._openModel(this._url(url), identifier || null, name);
if (status === '') {
return;
}
}
}
const window = this.window;
const document = this.document;
const search = window.location.search;
const params = new Map(search ? new window.URLSearchParams(window.location.search) : []);
const hash = window.location.hash ? window.location.hash.replace(/^#/, '') : '';
const url = hash ? hash : params.get('url');
if (url) {
const identifier = params.get('identifier') || null;
const location = url
.replace(/^https:\/\/github\.com\/([\w-]*\/[\w-]*)\/blob\/([\w/\-_.]*)(\?raw=true)?$/, 'https://raw.githubusercontent.com/$1/$2')
.replace(/^https:\/\/github\.com\/([\w-]*\/[\w-]*)\/raw\/([\w/\-_.]*)$/, 'https://raw.githubusercontent.com/$1/$2')
.replace(/^https:\/\/huggingface.co\/(.*)\/blob\/(.*)$/, 'https://huggingface.co/$1/resolve/$2');
if (this._view.accept(identifier || location) && location.indexOf('*') === -1) {
const status = await this._openModel(location, identifier);
if (status === '') {
return;
}
}
}
const gist = params.get('gist');
if (gist) {
this._openGist(gist);
return;
}
const openFileButton = this._element('open-file-button');
const openFileDialog = this._element('open-file-dialog');
if (openFileButton && openFileDialog) {
openFileButton.addEventListener('click', () => {
this.execute('open');
});
const mobileSafari = this.environment('platform') === 'darwin' && window.navigator.maxTouchPoints && window.navigator.maxTouchPoints > 1;
if (!mobileSafari) {
const extensions = new base.Metadata().extensions.map((extension) => `.${extension}`);
openFileDialog.setAttribute('accept', extensions.join(', '));
}
openFileDialog.addEventListener('change', (e) => {
if (e.target && e.target.files && e.target.files.length > 0) {
const files = Array.from(e.target.files);
const file = files.find((file) => this._view.accept(file.name, file.size));
if (file) {
this._open(file, files);
}
}
});
}
document.addEventListener('dragover', (e) => {
e.preventDefault();
});
document.addEventListener('drop', (e) => {
e.preventDefault();
});
document.body.addEventListener('drop', (e) => {
e.preventDefault();
if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
const files = Array.from(e.dataTransfer.files);
const file = files.find((file) => this._view.accept(file.name, file.size));
if (file) {
this._open(file, files);
}
}
});
this._view.show('welcome');
}
environment(name) {
return this._environment[name];
}
async require(id) {
return import(`${id}.js`);
}
worker(id) {
const window = this.window;
return new window.Worker(`${id}.js`, { type: 'module' });
}
async save(name, extension, defaultPath) {
return `${defaultPath}.${extension}`;
}
async export(file, blob) {
const window = this.window;
const document = this.document;
const element = document.createElement('a');
element.download = file;
const url = window.URL.createObjectURL(blob);
element.href = url;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
window.URL.revokeObjectURL(url);
}
async execute(name /*, value */) {
switch (name) {
case 'open': {
const openFileDialog = this._element('open-file-dialog');
if (openFileDialog) {
openFileDialog.value = '';
openFileDialog.click();
}
break;
}
case 'report-issue': {
this.openURL(`${this.environment('repository')}/issues/new`);
break;
}
case 'about': {
this._view.about();
break;
}
default: {
break;
}
}
}
async fetch(file, encoding, base) {
const url = base ? `${base}/${file}` : this._url(file);
return this._fetch(url, null, encoding);
}
async asset(file) {
this._assets = this._assets || new Map();
if (!this._assets.has(file)) {
const url = this._url(file);
const separator = (/\?/).test(url) ? '&' : '?';
const tag = this.version && this.version !== '0.0.0' ? `version=${this.version}` : `cb=${new Date().getTime()}`;
const request = this._request(`${url}${separator}${tag}`, null, 'utf-8');
request.catch(() => this._assets.delete(file));
this._assets.set(file, request);
}
return this._assets.get(file);
}
openURL(url) {
const window = this.window;
window.location = url;
}
exception(error, fatal) {
if (this._telemetry && error) {
const name = error.name ? `${error.name}: ` : '';
const message = error.message ? error.message : JSON.stringify(error);
let context = '';
let stack = '';
if (error.stack) {
const format = (file, line, column) => {
return `${file.split('\\').join('/').split('/').pop()}:${line}:${column}`;
};
const match = error.stack.match(/\n {4}at (.*) \((.*):(\d*):(\d*)\)/);
if (match) {
stack = `${match[1]} (${format(match[2], match[3], match[4])})`;
} else {
const match = error.stack.match(/\n {4}at (.*):(\d*):(\d*)/);
if (match) {
stack = `(${format(match[1], match[2], match[3])})`;
} else {
const match = error.stack.match(/\n {4}at (.*)\((.*)\)/);
if (match) {
stack = `(${format(match[1], match[2], match[3])})`;
} else {
const match = error.stack.match(/\s*@\s*(.*):(.*):(.*)/);
if (match) {
stack = `(${format(match[1], match[2], match[3])})`;
} else {
const match = error.stack.match(/.*\n\s*(.*)\s*/);
if (match) {
[, stack] = match;
}
}
}
}
}
}
if (error.context) {
context = typeof error.context === 'string' ? error.context : JSON.stringify(error.context);
}
this._telemetry.send('exception', {
app_name: this.type,
app_version: this.version,
error_name: name,
error_message: message,
error_context: context,
error_stack: stack,
error_fatal: fatal ? true : false
});
}
}
event(name, params) {
if (name && params) {
params.app_name = this.type;
params.app_version = this.version;
this._telemetry.send(name, params);
}
}
async _fetch(url, headers, encoding, callback, timeout) {
if (!url.startsWith('data:')) {
const separator = (/\?/).test(url) ? '&' : '?';
url = `${url}${separator}cb=${new Date().getTime()}`;
}
return this._request(url, headers, encoding, callback, timeout);
}
async _request(url, headers, encoding, callback, timeout) {
const window = this.window;
return new Promise((resolve, reject) => {
const request = new window.XMLHttpRequest();
if (!encoding) {
request.responseType = 'arraybuffer';
}
if (timeout) {
request.timeout = timeout;
}
const progress = (value) => {
if (callback) {
callback(value);
}
};
request.onload = () => {
progress(0);
if (request.status === 200) {
let value = null;
if (request.responseType === 'arraybuffer') {
const buffer = new Uint8Array(request.response);
value = new base.BinaryStream(buffer);
} else {
value = request.responseText;
}
resolve(value);
} else {
const error = new Error(`The web request failed with status code '${request.status}'.`);
error.context = url;
reject(error);
}
};
request.onerror = () => {
progress(0);
const error = new Error(`The web request failed.`);
error.context = url;
reject(error);
};
request.ontimeout = () => {
progress(0);
request.abort();
const error = new Error('The web request timed out.', 'timeout', url);
error.context = url;
reject(error);
};
request.onprogress = (e) => {
if (e && e.lengthComputable) {
progress(e.loaded / e.total * 100);
}
};
request.open('GET', url, true);
if (headers) {
for (const [name, value] of Object.entries(headers)) {
request.setRequestHeader(name, value);
}
}
request.send();
});
}
_url(file) {
if (file.startsWith('./')) {
file = file.substring(2);
} else if (file.startsWith('/')) {
file = file.substring(1);
}
const window = this.window;
const location = window.location;
const pathname = location.pathname.endsWith('/') ? location.pathname : `${location.pathname.split('/').slice(0, -1).join('/')}/`;
return `${location.protocol}//${location.host}${pathname}${file}`;
}
async _openModel(url, identifier, name) {
this._view.show('welcome spinner');
let context = null;
try {
const progress = (value) => {
this._view.progress(value);
};
let stream = await this._fetch(url, null, null, progress);
if (url.startsWith('https://raw.githubusercontent.com/') && stream.length < 150) {
const buffer = stream.peek();
const content = Array.from(buffer).map((c) => String.fromCodePoint(c)).join('');
if (content.split('\n')[0] === 'version https://git-lfs.github.com/spec/v1') {
url = url.replace('https://raw.githubusercontent.com/', 'https://media.githubusercontent.com/media/');
stream = await this._fetch(url, null, null, progress);
}
}
context = new browser.Context(this, url, identifier, name, stream);
this._telemetry.set('session_engaged', 1);
} catch (error) {
await this._view.error(error, 'Model load request failed.');
this._view.show('welcome');
return null;
}
return await this._openContext(context);
}
async _open(file, files) {
this._view.show('welcome spinner');
const context = new browser.BrowserFileContext(this, file, files);
try {
await context.open();
await this._openContext(context);
} catch (error) {
await this._view.error(error);
}
}
async _openGist(gist) {
this._view.show('welcome spinner');
const url = `https://api.github.com/gists/${gist}`;
try {
const text = await this._fetch(url, { 'Content-Type': 'application/json' }, 'utf-8');
const json = JSON.parse(text);
let message = json.message;
let file = null;
if (!message) {
file = Object.values(json.files).find((file) => this._view.accept(file.filename));
if (!file) {
message = 'Gist does not contain a model file.';
}
}
if (message) {
const error = new Error(message);
error.name = 'Error while loading Gist.';
throw error;
}
const identifier = file.filename;
const encoder = new TextEncoder();
const buffer = encoder.encode(file.content);
const stream = new base.BinaryStream(buffer);
const context = new browser.Context(this, '', identifier, null, stream);
await this._openContext(context);
} catch (error) {
await this._view.error(error, 'Error while loading Gist.');
this._view.show('welcome');
}
}
async _openContext(context) {
const document = this.document;
this._telemetry.set('session_engaged', 1);
try {
const attachment = await this._view.attach(context);
if (attachment) {
this._view.show(null);
return 'context-open-attachment';
}
const model = await this._view.open(context);
if (model) {
this._view.show(null);
document.title = context.name || context.identifier;
return '';
}
document.title = '';
return 'context-open-failed';
} catch (error) {
await this._view.error(error, error.name);
return 'context-open-error';
}
}
_setCookie(name, value, days) {
const window = this.window;
const document = this.document;
document.cookie = `${name}=; Max-Age=0`;
const location = window.location;
const domain = location && location.hostname && location.hostname.indexOf('.') !== -1 ? `;domain=.${location.hostname.split('.').slice(-2).join('.')}` : '';
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
document.cookie = `${name}=${value}${domain};path=/;expires=${date.toUTCString()}`;
}
_getCookie(name) {
const document = this.document;
for (const cookie of document.cookie.split(';')) {
const entry = cookie.split('=');
if (entry[0].trim() === name) {
return entry[1].trim();
}
}
return '';
}
get(name) {
const window = this.window;
try {
if (typeof window.localStorage !== 'undefined') {
const content = window.localStorage.getItem(name);
return JSON.parse(content);
}
} catch {
// continue regardless of error
}
return undefined;
}
set(name, value) {
const window = this.window;
try {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.setItem(name, JSON.stringify(value));
}
} catch {
// continue regardless of error
}
}
delete(name) {
const window = this.window;
try {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.removeItem(name);
}
} catch {
// continue regardless of error
}
}
_element(id) {
const document = this.document;
return document.getElementById(id);
}
update() {
}
async message(message, alert, action) {
return new Promise((resolve) => {
const document = this.document;
const type = document.body.getAttribute('class');
this._element('message-text').innerText = message || '';
const button = this._element('message-button');
if (action) {
button.style.removeProperty('display');
button.innerText = action;
button.onclick = () => {
button.onclick = null;
document.body.setAttribute('class', type);
resolve(0);
};
} else {
button.style.display = 'none';
button.onclick = null;
}
if (alert) {
document.body.setAttribute('class', 'alert');
} else {
document.body.classList.add('notification');
document.body.classList.remove('default');
}
if (action) {
button.focus();
}
});
}
};
browser.BrowserFileContext = class {
constructor(host, file, blobs) {
this._host = host;
this._file = file;
this._blobs = {};
for (const blob of blobs) {
this._blobs[blob.name] = blob;
}
}
get identifier() {
return this._file.name;
}
get stream() {
return this._stream;
}
async asset(file) {
return this._host.asset(file);
}
async fetch(file, encoding, basename) {
if (basename !== undefined) {
return this._host.fetch(file, encoding, basename);
}
const blob = this._blobs[file];
if (!blob) {
throw new Error(`File not found '${file}'.`);
}
return new Promise((resolve, reject) => {
const window = this._host.window;
const reader = new window.FileReader();
const size = 0x10000000;
let position = 0;
const chunks = [];
reader.onload = (e) => {
if (encoding) {
resolve(e.target.result);
} else {
const buffer = new Uint8Array(e.target.result);
if (position === 0 && buffer.length === blob.size) {
const stream = new base.BinaryStream(buffer);
resolve(stream);
} else {
chunks.push(buffer);
position += buffer.length;
if (position < blob.size) {
const slice = blob.slice(position, Math.min(position + size, blob.size));
reader.readAsArrayBuffer(slice);
} else {
const stream = new browser.FileStream(chunks, size, 0, position);
resolve(stream);
}
}
}
};
reader.onerror = (event) => {
event = event || this._host.window.event;
let message = '';
const error = event.target.error;
switch (error.code) {
case error.NOT_FOUND_ERR:
message = `File not found '${file}'.`;
break;
case error.NOT_READABLE_ERR:
message = `File not readable '${file}'.`;
break;
case error.SECURITY_ERR:
message = `File access denied '${file}'.`;
break;
default:
message = error.message ? error.message : `File read '${error.code}' error '${file}'.`;
break;
}
reject(new Error(message));
};
if (encoding === 'utf-8') {
reader.readAsText(blob, encoding);
} else {
const slice = blob.slice(position, Math.min(position + size, blob.size));
reader.readAsArrayBuffer(slice);
}
});
}
async require(id) {
return this._host.require(id);
}
error(error, fatal) {
this._host.exception(error, fatal);
}
async open() {
this._stream = await this.fetch(this._file.name, null);
}
};
browser.FileStream = class {
constructor(chunks, size, start, length) {
this._chunks = chunks;
this._size = size;
this._start = start;
this._length = length;
this._position = 0;
}
get position() {
return this._position;
}
get length() {
return this._length;
}
stream(length) {
const file = new browser.FileStream(this._chunks, this._size, this._start + this._position, length);
this.skip(length);
return file;
}
seek(position) {
this._position = position >= 0 ? position : this._length + position;
}
skip(offset) {
this._position += offset;
if (this._position > this._length) {
throw new Error(`Expected ${this._position - this._length} more bytes. The file might be corrupted. Unexpected end of file.`);
}
}
peek(length) {
length = length === undefined ? this._length - this._position : length;
if (length < 0x10000000) {
const position = this._fill(length);
this._position -= length;
return this._buffer.subarray(position, position + length);
}
const position = this._start + this._position;
if (position % this._size === 0) {
const index = Math.floor(position / this._size);
const chunk = this._chunks[index];
if (chunk && chunk.length === length) {
return chunk;
}
}
const buffer = new Uint8Array(length);
this._read(buffer, position);
return buffer;
}
read(length) {
length = length === undefined ? this._length - this._position : length;
if (length < 0x10000000) {
const position = this._fill(length);
return this._buffer.slice(position, position + length);
}
const position = this._start + this._position;
this.skip(length);
if (position % this._size === 0) {
const index = Math.floor(position / this._size);
const chunk = this._chunks[index];
if (chunk && chunk.length === length) {
return chunk;
}
}
const buffer = new Uint8Array(length);
this._read(buffer, position);
return buffer;
}
_fill(length) {
if (this._position + length > this._length) {
throw new Error(`Expected ${this._position + length - this._length} more bytes. The file might be corrupted. Unexpected end of file.`);
}
if (!this._buffer || this._position < this._offset || this._position + length > this._offset + this._buffer.length) {
this._offset = this._start + this._position;
const length = Math.min(0x10000000, this._start + this._length - this._offset);
if (!this._buffer || length !== this._buffer.length) {
this._buffer = new Uint8Array(length);
}
this._read(this._buffer, this._offset);
}
const position = this._start + this._position - this._offset;
this._position += length;
return position;
}
_read(buffer, offset) {
let index = Math.floor(offset / this._size);
offset -= index * this._size;
const chunk = this._chunks[index++];
let destination = Math.min(chunk.length - offset, buffer.length);
buffer.set(chunk.subarray(offset, offset + destination), 0);
while (destination < buffer.length) {
const chunk = this._chunks[index++];
const size = Math.min(this._size, buffer.length - destination);
buffer.set(chunk.subarray(0, size), destination);
destination += size;
}
}
};
browser.Context = class {
constructor(host, url, identifier, name, stream) {
this._host = host;
this._name = name;
this._stream = stream;
const parts = url.split('?')[0].split('/');
this._identifier = parts.pop();
this._base = parts.join('/');
if (identifier) {
this._identifier = identifier;
}
}
get identifier() {
return this._identifier;
}
get name() {
return this._name;
}
get stream() {
return this._stream;
}
async asset(file) {
return this._host.asset(file);
}
async fetch(file, encoding, base) {
base = base === undefined ? this._base : base;
return this._host.fetch(file, encoding, base);
}
async require(id) {
return this._host.require(id);
}
error(error, fatal) {
this._host.exception(error, fatal);
}
};
if (!('scrollBehavior' in window.document.documentElement.style)) {
const __scrollTo__ = window.Element.prototype.scrollTo;
window.Element.prototype.scrollTo = function(...args) {
const [options] = args;
if (options !== undefined) {
if (options === null || typeof options !== 'object' || options.behavior === undefined || options.behavior === 'auto' || options.behavior === 'instant') {
if (__scrollTo__) {
__scrollTo__.apply(this, args);
}
} else {
const now = () => window.performance && window.performance.now ? window.performance.now() : Date.now();
const ease = (k) => 0.5 * (1 - Math.cos(Math.PI * k));
const step = (context) => {
const value = ease(Math.min((now() - context.startTime) / 468, 1));
const x = context.startX + (context.x - context.startX) * value;
const y = context.startY + (context.y - context.startY) * value;
context.element.scrollLeft = x;
context.element.scrollTop = y;
if (x !== context.x || y !== context.y) {
window.requestAnimationFrame(step.bind(window, context));
}
};
const context = {
element: this,
x: typeof options.left === 'undefined' ? this.scrollLeft : ~~options.left,
y: typeof options.top === 'undefined' ? this.scrollTop : ~~options.top,
startX: this.scrollLeft,
startY: this.scrollTop,
startTime: now()
};
step(context);
}
}
};
}
if (typeof window !== 'undefined' && window.exports) {
window.exports.browser = browser;
}
export const Host = browser.Host;
+462
View File
@@ -0,0 +1,462 @@
[
{
"name": "Accuracy",
"inputs": [
{ "name": "predictions" },
{ "name": "labels" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "AnnotatedData",
"category": "Data",
"outputs": [
{ "name": "data" }
]
},
{
"name": "BatchNorm",
"category": "Normalization",
"attributes": [
{ "name": "use_global_stats", "type": "boolean", "visible": false },
{ "name": "eps", "type": "float32", "default": 0.00001 }
],
"inputs": [
{ "name": "input" },
{ "name": "gamma" },
{ "name": "beta" },
{ "name": "mean" },
{ "name": "variance" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "BN",
"category": "Normalization",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ColorConv",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Concat",
"category": "Tensor",
"inputs": [
{ "name": "inputs", "option": "variadic" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ContrastiveLossParameter",
"attributes": [
{ "name": "margin", "default": 1 },
{ "name": "legacy_version", "default": false }
]
},
{
"name": "Convolution",
"category": "Layer",
"attributes": [
{ "name": "bias_term", "visible": false },
{ "name": "weight_filler", "visible": false },
{ "name": "bias_filler", "visible": false },
{ "name": "num_output", "visible": false },
{ "name": "pad", "default": [ 0 ] },
{ "name": "kernel_size", "default": [] },
{ "name": "stride", "default": [ 1 ] },
{ "name": "dilation", "default": [] },
{ "name": "group", "default": 1 }
],
"inputs": [
{ "name": "input" },
{ "name": "filter" },
{ "name": "bias" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ConvolutionDepthwise",
"category": "Layer",
"attributes": [
{ "name": "pad", "default": [ 0 ] },
{ "name": "kernel_size", "default": [] },
{ "name": "stride", "default": [ 1 ] },
{ "name": "bias_term", "visible": false },
{ "name": "weight_filler", "visible": false },
{ "name": "bias_filler", "visible": false },
{ "name": "num_output", "visible": false }
],
"inputs": [
{ "name": "input" },
{ "name": "filter" },
{ "name": "bias" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Crop",
"category": "Data",
"inputs": [
{ "name": "data" },
{ "name": "size" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Data",
"category": "Data",
"outputs": [
{ "name": "data" },
{ "name": "label" }
]
},
{
"name": "Deconvolution",
"category": "Layer",
"attributes": [
{ "name": "bias_term", "visible": false },
{ "name": "weight_filler", "visible": false },
{ "name": "bias_filler", "visible": false },
{ "name": "num_output", "visible": false },
{ "name": "pad", "default": [] },
{ "name": "kernel_size", "default": [] },
{ "name": "stride", "default": [] },
{ "name": "dilation", "default": [] }
],
"inputs": [
{ "name": "input" },
{ "name": "filter" },
{ "name": "bias" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "DepthwiseConvolution",
"category": "Layer",
"attributes": [
{ "name": "bias_term", "visible": false },
{ "name": "weight_filler", "visible": false },
{ "name": "bias_filler", "visible": false },
{ "name": "num_output", "visible": false }
],
"inputs": [
{ "name": "input" },
{ "name": "filter" },
{ "name": "bias" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Dropout",
"category": "Dropout",
"attributes": [
{ "name": "dropout_ratio", "default": 0.5 }
],
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "DummyData",
"category": "Data",
"outputs": [
{ "name": "data" }
]
},
{
"name": "Eltwise",
"attributes": [
{ "name": "operation", "type": "EltwiseParameter.EltwiseOp", "default": 1 },
{ "name": "coeff", "type": "float32[]", "default": [] },
{ "name": "stable_prod_grad", "type": "boolean", "default": true }
],
"inputs": [
{ "name": "inputs", "option": "variadic" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "EuclideanLoss",
"inputs": [
{ "name": "predictions" },
{ "name": "targets" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Flatten",
"category": "Shape",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "HDF5Data",
"category": "Data",
"outputs": [
{ "name": "data" }
]
},
{
"name": "ImageData",
"category": "Data",
"outputs": [
{ "name": "data" },
{ "name": "label" }
]
},
{
"name": "InnerProduct",
"category": "Layer",
"attributes": [
{ "name": "bias_term", "visible": false },
{ "name": "weight_filler", "visible": false },
{ "name": "bias_filler", "visible": false },
{ "name": "num_output", "visible": false }
],
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "bias" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "LRN",
"category": "Normalization",
"attributes": [
{ "name": "local_size", "type": "uint32", "default": 5 },
{ "name": "alpha", "type": "float32", "default": 0.0001 },
{ "name": "beta", "type": "float32", "default": 0.75 }
],
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "LSTM",
"category": "Layer",
"attributes": [
{ "name": "weight_filler", "visible": false },
{ "name": "bias_filler", "visible": false },
{ "name": "num_output", "visible": false }
],
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "h_0" },
{ "name": "c_0" }
],
"outputs": [
{ "name": "output" },
{ "name": "h_T" },
{ "name": "c_T" }
]
},
{
"name": "Parameter",
"outputs": [
{ "name": "output" }
]
},
{
"name": "Permute",
"category": "Shape",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Pooling",
"category": "Pool",
"attributes": [
{ "name": "pool", "type": "PoolingParameter.PoolMethod", "default": 0 },
{ "name": "engine", "type": "PoolingParameter.Engine", "default": 0 }
],
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "PReLU",
"category": "Activation",
"inputs": [
{ "name": "input" },
{ "name": "slope" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Python"
},
{
"name": "ReLU",
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ReLU6",
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Reshape",
"category": "Shape",
"inputs": [
{ "name": "data" }
],
"outputs": [
{ "name": "reshaped" }
]
},
{
"name": "Scale",
"category": "Layer",
"attributes": [
{ "name": "filler", "visible": false },
{ "name": "bias_term", "visible": false },
{ "name": "bias_filler", "visible": false }
],
"inputs": [
{ "name": "input" },
{ "name": "scale" },
{ "name": "bias" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Sigmoid",
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Slice",
"category": "Tensor",
"attributes": [
{ "name": "axis", "default": 1 }
],
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "outputs", "option": "variadic" }
]
},
{
"name": "Softmax",
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "SoftmaxLoss",
"category": "Activation",
"inputs": [
{ "name": "input" },
{ "name": "labels" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "SoftmaxWithLoss",
"category": "Activation",
"inputs": [
{ "name": "input" },
{ "name": "labels" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Split",
"category": "Tensor",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "outputs", "option": "variadic" }
]
},
{
"name": "WindowData",
"category": "Data",
"outputs": [
{ "name": "data" },
{ "name": "label" }
]
}
]
File diff suppressed because it is too large Load Diff
+590
View File
@@ -0,0 +1,590 @@
const caffe = {};
caffe.ModelFactory = class {
async match(context) {
const identifier = context.identifier;
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
if (extension === 'caffemodel') {
return context.set('caffe.pb');
}
if (identifier === 'saved_model.pbtxt' || identifier === 'saved_model.prototxt' ||
identifier.endsWith('predict_net.pbtxt') || identifier.endsWith('predict_net.prototxt') ||
identifier.endsWith('init_net.pbtxt') || identifier.endsWith('init_net.prototxt')) {
return null;
}
const tags = await context.tags('pbtxt');
if (tags.has('layer') || tags.has('layers')) {
return context.set('caffe.pbtxt');
} else if (tags.has('net') || tags.has('train_net') || tags.has('net_param')) {
return context.set('caffe.pbtxt.solver');
}
return null;
}
async open(context) {
caffe.proto = await context.require('./caffe-proto');
caffe.proto = caffe.proto.caffe;
const openModel = async (context, netParameter) => {
const metadata = await context.metadata('caffe-metadata.json');
return new caffe.Model(metadata, netParameter);
};
const openNetParameterText = async (context, identifier, content) => {
let netParameter = null;
try {
const reader = await content.read('protobuf.text');
reader.field = function(tag, message) {
const type = message.constructor.name;
if (tag.endsWith('_param') && (type === 'LayerParameter' || type === 'V1LayerParameter' || type === 'V0LayerParameter')) {
message[tag] = caffe.ModelFactory._decodeText(reader);
return;
} else if (message.constructor.name.endsWith('Parameter') || message.constructor.name === 'ParamSpec') {
if (message[tag]) {
if (!Array.isArray(message[tag])) {
message[tag] = [message[tag]];
}
message[tag].push(this.read());
} else {
message[tag] = this.read();
}
return;
}
throw new Error(`Unknown field '${tag}' ${this.location()}`);
};
reader.enum = function(type) {
const token = this.token();
this.next();
this.semicolon();
if (!Object.prototype.hasOwnProperty.call(type, token)) {
const value = Number.parseInt(token, 10);
if (!Number.isNaN(token - value)) {
return value;
}
return token;
}
return type[token];
};
if (/MobileNetSSD_train_template.prototxt/.exec(identifier)) {
reader.integer = function() {
const token = this.token();
const value = Number.parseInt(token, 10);
this.next();
this.semicolon();
if (Number.isNaN(token - value)) {
return token;
}
return value;
};
}
netParameter = caffe.proto.NetParameter.decodeText(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new caffe.Error(`File text format is not caffe.NetParameter (${message.replace(/\.$/, '')}).`);
}
return openModel(context, netParameter);
};
switch (context.type) {
case 'caffe.pbtxt.solver': {
const reader = await context.read('protobuf.text');
reader.field = function(tag, message) {
if (message instanceof caffe.proto.SolverParameter) {
message[tag] = this.read();
return;
}
throw new Error(`Unknown field '${tag}'${this.location()}`);
};
const solver = caffe.proto.SolverParameter.decodeText(reader);
if (solver.net_param) {
return openModel(context, solver.net_param);
}
let name = solver.net || solver.train_net;
name = name.split('/').pop();
try {
const content = await context.fetch(name);
return await openNetParameterText(context, name, content);
} catch (error) {
const message = error.message ? error.message : error.toString();
throw new caffe.Error(`Failed to load '${name}' (${message.replace(/\.$/, '')}).`);
}
}
case 'caffe.pbtxt': {
return await openNetParameterText(context, context.identifier, context);
}
case 'caffe.pb': {
let netParameter = null;
try {
const reader = await context.read('protobuf.binary');
netParameter = caffe.proto.NetParameter.decode(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new caffe.Error(`File format is not caffe.NetParameter (${message.replace(/\.$/, '')}).`);
}
return await openModel(context, netParameter);
}
default: {
throw new caffe.Error(`Unsupported Caffe format '${context.type}'.`);
}
}
}
static _decodeText(reader) {
const message = {};
reader.start();
while (!reader.end()) {
const tag = reader.tag();
const value = reader.read();
if (message[tag]) {
if (!Array.isArray(message[tag])) {
message[tag] = [message[tag]];
}
message[tag].push(value);
} else {
message[tag] = value;
}
}
return message;
}
};
caffe.Model = class {
constructor(metadata, net) {
this.name = net.name;
this.format = 'Caffe';
this.modules = [];
let version = -1;
if (net.layers && net.layers.length > 0) {
if (net.layers.every((layer) => Object.prototype.hasOwnProperty.call(layer, 'layer'))) {
version = 0;
net.layer = net.layers;
} else {
version = 1;
net.layer = net.layers;
}
} else if (net.layer && net.layer.length > 0) {
version = 2;
}
this.format = `Caffe v${version}`;
const phases = new Set();
for (const layer of net.layer) {
for (const include of layer.include) {
if (include.phase !== undefined) {
phases.add(include.phase);
}
}
}
if (phases.size === 0) {
phases.add(-1);
}
for (const phase of phases) {
const graph = new caffe.Graph(metadata, phase, net, version);
this.modules.push(graph);
}
}
};
caffe.Graph = class {
constructor(metadata, phase, net, version) {
switch (phase) {
case 0: this.name = 'TRAIN'; break;
case 1: this.name = 'TEST'; break;
case -1: this.name = ''; break;
default: this.name = phase.toString(); break;
}
this.nodes = [];
this.inputs = [];
this.outputs = [];
for (const layer of net.layer) {
layer.input = layer.bottom.slice(0);
layer.output = layer.top.slice(0);
layer.chain = [];
}
const layers = [];
for (const layer of net.layer) {
if (phase === -1 || layer.include.every((include) => include.phase === phase)) {
layers.push(layer);
}
}
const scopes = new Map();
for (let i = 0; i < layers.length; i++) {
const layer = layers[i];
layer.input = layer.input.map((input) => scopes.has(input) ? scopes.get(input) : input);
layer.output = layer.output.map((output) => {
const value = scopes.has(output) ? `${output}\n${i}` : output;
scopes.set(output, value);
return value;
});
}
// Graph Inputs
const usedOutputs = new Set();
for (const layer of layers) {
for (const output of layer.output) {
usedOutputs.add(output);
}
}
const unusedInputs = [];
for (const layer of layers) {
for (const input of layer.input) {
if (!usedOutputs.has(input)) {
unusedInputs.push(input);
}
}
}
const values = new Map();
const value = (name, type) => {
if (!values.has(name)) {
values.set(name, new caffe.Value(name, type));
} else if (type) {
throw new caffe.Error(`Duplicate value '${name}'.`);
}
return values.get(name);
};
const nodes = [];
let lastLayer = null;
let lastTop = null;
while (layers.length > 0) {
let layer = layers.shift();
if (layer.output.length === 1 && layer.input.length === 1 &&
layer.output[0].split('\n').shift() === layer.input[0].split('\n').shift() &&
lastLayer &&
lastTop === layer.output[0].split('\n').shift()) {
lastLayer.chain = lastLayer.chain || [];
lastLayer.chain.push(layer);
} else {
if (layer.type === 'Input' && layer.input.length === 0) {
for (let i = 0; i < layer.output.length; i++) {
const output = layer.output[i];
const dim = layer.input_param && layer.input_param.shape && i < layer.input_param.shape.length ? layer.input_param.shape[i].dim : null;
const shape = dim ? new caffe.TensorShape(dim.map((dim) => dim.toNumber())) : null;
const type = shape ? new caffe.TensorType(null, shape) : null;
const argument = new caffe.Argument(output, [value(output, type)]);
this.inputs.push(argument);
}
layer = null;
}
if (layer) {
nodes.push(layer);
lastLayer = null;
lastTop = null;
if (layer.output.length === 1) {
lastLayer = layer;
lastTop = layer.output[0].split('\n').shift();
}
}
}
}
if (net.input) {
for (let i = 0; i < net.input.length; i++) {
const input = net.input[i];
if (this.inputs.some((item) => item.name === input)) {
continue;
}
let inputType = null;
if (net.input_shape && i < net.input_shape.length) {
const blobShape = net.input_shape[i];
if (blobShape && blobShape.dim) {
const shape = new caffe.TensorShape(blobShape.dim.map((dim) => dim.toNumber()));
inputType = new caffe.TensorType(null, shape);
}
}
const dim = i * 4;
if (!inputType && net.input_dim && net.input_dim.length >= dim) {
const shape = new caffe.TensorShape(net.input_dim.slice(dim, dim + 4));
inputType = new caffe.TensorType(null, shape);
}
this.inputs.push(new caffe.Argument(input, [value(input, inputType, null)]));
}
}
for (const layer of nodes) {
const node = new caffe.Node(metadata, layer, version, value);
if (layer.chain && layer.chain.length > 0) {
for (const chain of layer.chain) {
node.chain.push(new caffe.Node(metadata, chain, version, value));
}
}
this.nodes.push(node);
}
if (this.inputs.length === 0 && unusedInputs.length === 1) {
this.inputs.push(new caffe.Argument(unusedInputs[0], [value(unusedInputs[0], null)]));
}
}
};
caffe.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
caffe.Value = class {
constructor(name, type = null, initializer = null) {
if (typeof name !== 'string') {
throw new caffe.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = type;
this.initializer = initializer;
}
};
caffe.Node = class {
constructor(metadata, layer, version, value) {
this.attributes = [];
this.chain = [];
let type = '';
switch (version) {
case 0: {
this.name = layer.layer.name;
type = layer.layer.type;
break;
}
case 1: {
this.name = layer.name;
type = caffe.Utility.layerType(layer.type);
break;
}
case 2: {
this.name = layer.name;
type = layer.type;
break;
}
default: {
throw new caffe.Error(`Unsupported Caffe version '${version}'.`);
}
}
this.type = metadata.type(type) || { name: type };
let initializers = [];
const attributes = [];
switch (version) {
case 0: {
for (const name of Object.keys(layer.layer)) {
if (name !== 'type' && name !== 'name' && name !== 'blobs' && name !== 'blobs_lr') {
const value = layer.layer[name];
const schema = metadata.attribute(type, name);
attributes.push([schema, name, value]);
}
}
initializers = layer.layer.blobs.map((blob) => new caffe.Tensor(blob));
break;
}
case 1:
case 2: {
for (const layer_kind of Object.keys(layer)) {
if (layer_kind.endsWith('_param') || layer_kind === 'transform_param') {
const param = layer[layer_kind];
if (type === 'Deconvolution') {
type = 'Convolution';
}
const prototype = Object.getPrototypeOf(param);
for (const name of Object.keys(param)) {
const defaultValue = prototype[name];
const value = param[name];
const schema = metadata.attribute(type, name);
attributes.push([schema, name, value, defaultValue]);
}
}
}
if (layer.include && layer.include.length > 0) {
const schema = metadata.attribute(type, 'include');
attributes.push([schema, 'include', layer.include]);
}
if (layer.exclude && layer.exclude.length > 0) {
const schema = metadata.attribute(type, 'exclude');
attributes.push([schema, 'exclude', layer.exclude]);
}
if (this.type === 'Data' && layer.input_param && layer.input_param.shape) {
const schema = metadata.attribute(type, 'shape');
attributes.push([schema, 'shape', layer.input_param.shape]);
}
initializers = layer.blobs.map((blob) => new caffe.Tensor(blob));
break;
}
default: {
throw new caffe.Error(`Unsupported Caffe version '${version}'.`);
}
}
this.inputs = [];
const inputs = layer.input.concat(initializers);
let inputIndex = 0;
if (this.type && this.type.inputs) {
for (const inputDef of this.type.inputs) {
if (inputIndex < inputs.length || inputDef.option !== 'optional') {
const count = inputDef.option === 'variadic' ? inputs.length - inputIndex : 1;
const values = inputs.slice(inputIndex, inputIndex + count).filter((input) => input !== '' || inputDef.option !== 'optional').map((input) => {
return input instanceof caffe.Tensor ? new caffe.Value('', input.type, input) : value(input, null, null);
});
const argument = new caffe.Argument(inputDef.name, values);
this.inputs.push(argument);
inputIndex += count;
}
}
}
this.inputs.push(...inputs.slice(inputIndex).map((input) => {
return new caffe.Argument(inputIndex.toString(), [
input instanceof caffe.Tensor ? new caffe.Value('', input.type, input) : value(input, null, null)
]);
}));
this.outputs = [];
const outputs = layer.output;
let outputIndex = 0;
if (this.type && this.type.outputs) {
for (const outputDef of this.type.outputs) {
if (outputIndex < outputs.length) {
const count = (outputDef.option === 'variadic') ? (outputs.length - outputIndex) : 1;
const values = outputs.slice(outputIndex, outputIndex + count).map((output) => value(output, null, null));
const argument = new caffe.Argument(outputDef.name, values);
this.outputs.push(argument);
outputIndex += count;
}
}
}
this.outputs.push(...outputs.slice(outputIndex).map((output, index) => {
return new caffe.Argument((outputIndex + index).toString(), [value(output, null, null)]);
}));
this.attributes = attributes.map(([metadata, name, value, defaultValue]) => {
let visible = true;
let type = null;
if (metadata && metadata.type) {
type = metadata.type;
}
if (value instanceof caffe.proto.BlobShape) {
value = new caffe.TensorShape(value.dim.map((dim) => dim.toNumber()));
type = 'shape';
}
if (metadata && metadata.visible === false) {
visible = false;
}
if (metadata && metadata.default !== undefined) {
defaultValue = metadata.default;
}
if (defaultValue !== undefined) {
if (value === defaultValue) {
visible = false;
} else if (Array.isArray(value) && Array.isArray(defaultValue)) {
if (value.length === defaultValue.length && value.every((item, index) => item === defaultValue[index])) {
visible = false;
}
}
}
value = type ? caffe.Utility.enum(type, value) : value;
return new caffe.Argument(name, value, type, visible);
});
}
};
caffe.Tensor = class {
constructor(blob) {
let shape = [];
if (Object.prototype.hasOwnProperty.call(blob, 'num') &&
Object.prototype.hasOwnProperty.call(blob, 'channels') &&
Object.prototype.hasOwnProperty.call(blob, 'width') &&
Object.prototype.hasOwnProperty.call(blob, 'height')) {
if (blob.num !== 1) {
shape.push(blob.num);
}
if (blob.channels !== 1) {
shape.push(blob.channels);
}
if (blob.height !== 1) {
shape.push(blob.height);
}
if (blob.width !== 1) {
shape.push(blob.width);
}
} else if (Object.prototype.hasOwnProperty.call(blob, 'shape')) {
shape = blob.shape.dim.map((dim) => Number(dim));
}
let dataType = '?';
if (blob.data.length > 0) {
dataType = 'float32';
this.values = blob.data;
} else if (blob.double_data.length > 0) {
dataType = 'float64';
this.values = blob.double_data;
}
this.category = 'Blob';
this.encoding = '|';
this.type = new caffe.TensorType(dataType, new caffe.TensorShape(shape));
}
};
caffe.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType;
this.shape = shape;
}
toString() {
return (this.dataType || '?') + this.shape.toString();
}
};
caffe.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
toString() {
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`) : '';
}
};
caffe.Utility = class {
static layerType(type) {
type = type || 0;
if (!caffe.Utility._layerTypeMap) {
caffe.Utility._layerTypeMap = new Map();
const known = { 'BNLL': 'BNLL', 'HDF5': 'HDF5', 'LRN': 'LRN', 'RELU': 'ReLU', 'TANH': 'TanH', 'ARGMAX': 'ArgMax', 'MVN': 'MVN', 'ABSVAL': 'AbsVal' };
for (const key of Object.keys(caffe.proto.V1LayerParameter.LayerType)) {
const value = caffe.proto.V1LayerParameter.LayerType[key];
caffe.Utility._layerTypeMap.set(value, key.split('_').map((item) => known[item] || item.substring(0, 1) + item.substring(1).toLowerCase()).join(''));
}
}
return caffe.Utility._layerTypeMap.has(type) ? caffe.Utility._layerTypeMap.get(type) : type.toString();
}
static enum(name, value) {
let type = caffe.proto;
const parts = name.split('.');
while (type && parts.length > 0) {
type = type[parts.shift()];
}
if (type) {
caffe.Utility._enumKeyMap = caffe.Utility._enumKeyMap || new Map();
if (!caffe.Utility._enumKeyMap.has(name)) {
const map = new Map(Object.entries(type).map(([name, value]) => [value, name]));
caffe.Utility._enumKeyMap.set(name, map);
}
const map = caffe.Utility._enumKeyMap.get(name);
if (map.has(value)) {
return map.get(value);
}
}
return value;
}
};
caffe.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Caffe model.';
}
};
export const ModelFactory = caffe.ModelFactory;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+493
View File
@@ -0,0 +1,493 @@
const caffe2 = {};
caffe2.ModelFactory = class {
async match(context) {
const identifier = context.identifier.toLowerCase();
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
switch (extension) {
case 'pbtxt':
case 'prototxt': {
const tags = await context.tags('pbtxt');
if (tags.has('op') && !tags.has('op.attr') && !tags.has('op.graph_op_name') && !tags.has('op.endpoint')) {
return context.set('caffe2.pbtxt');
}
break;
}
case 'pb': {
const tags = await context.tags('pb');
if (tags.size > 0 &&
Array.from(tags.keys()).every((tag) => tag <= 9) &&
Array.from(tags.values()).every((type) => type <= 4)) {
if (tags.size === 1 && tags.get(2) === 2 && identifier.endsWith('saved_model.pb')) {
return null;
}
const schema = [[1,2],[2,2],[3,2],[4,0],[5,2],[6,2],[7,2],[8,2],[9,2]];
if (schema.every(([key, value]) => !tags.has(key) || tags.get(key) === value)) {
const stream = context.stream;
if (stream.length > 3) {
const buffer = stream.peek(Math.min(stream.length, 67));
const [signature, size] = buffer;
switch (signature) {
case 0x0A:
if (size < 64 &&
buffer.length > 2 + size + 1 &&
buffer.slice(2, 2 + size).every((c) => c >= 32 && c <= 127) &&
buffer[2 + size] === 0x12) {
return context.set('caffe2.pb');
}
break;
case 0x12:
return context.set('caffe2.pb');
default:
break;
}
}
}
}
break;
}
default: {
break;
}
}
return null;
}
async open(context) {
caffe2.proto = await context.require('./caffe2-proto');
caffe2.proto = caffe2.proto.caffe2;
const metadata = await context.metadata('caffe2-metadata.json');
const identifier = context.identifier;
const parts = identifier.split('.');
const extension = parts.pop().toLowerCase();
const base = parts.join('.');
let predict = null;
let init = null;
switch (context.type) {
case 'caffe2.pbtxt': {
if (base.toLowerCase().endsWith('init_net') || base.toLowerCase().startsWith('init_net')) {
init = context;
try {
const name = identifier.replace('init_net', 'predict_net');
predict = await context.fetch(name);
predict.set(context.type);
} catch {
// continue regardless of error
}
} else if (base.toLowerCase().endsWith('predict_net') || base.toLowerCase().startsWith('predict_net')) {
predict = context;
const name = identifier.replace('predict_net', 'init_net');
try {
init = await context.fetch(name.replace(/\.pbtxt/, '.pb'));
init.set('caffe2.pb');
} catch {
try {
init = await context.fetch(name);
init.set('caffe2.pbtxt');
} catch {
// continue regardless of error
}
}
} else {
predict = context;
try {
init = await context.fetch(`${base}_init.pb`);
init.set('caffe2.pb');
} catch {
// continue regardless of error
}
}
break;
}
case 'caffe2.pb': {
if (base.toLowerCase().endsWith('init_net')) {
init = context;
const extensions = new Set([extension, 'pb', 'pbtxt']);
for (const extension of extensions) {
try {
const name = `${base.replace(/init_net$/, '')}predict_net.${extension}`;
// eslint-disable-next-line no-await-in-loop
predict = await context.fetch(name);
predict.set(`caffe2.${extension}`);
break;
} catch {
// continue regardless of error
}
}
} else if (base.toLowerCase().endsWith('_init')) {
try {
const name = `${base.replace(/_init$/, '')}.${extension}`;
predict = await context.fetch(name);
predict.set(context.type);
} catch {
// continue regardless of error
}
} else if (base.toLowerCase().endsWith('predict_net') || base.toLowerCase().startsWith('predict_net')) {
predict = context;
try {
const name = identifier.replace('predict_net', 'init_net');
init = await context.fetch(name);
init.set(context.type);
} catch {
// continue regardless of error
}
} else {
predict = context;
try {
const file = `${base}_init.${extension}`;
init = await context.fetch(file, null);
init.set(context.type);
} catch {
// continue regardless of error
}
}
break;
}
default: {
throw new caffe2.Error(`Unsupported Caffe2 format '${context.type}'.`);
}
}
if (!predict && !init) {
throw new caffe2.Error(`Caffe2 model does not contain predict or init data.`);
}
const open = async (context) => {
if (context) {
switch (context.type) {
case 'caffe2.pb':
try {
const reader = await context.read('protobuf.binary');
return caffe2.proto.NetDef.decode(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new caffe2.Error(`File format is not caffe2.NetDef (${message.replace(/\.$/, '')}).`);
}
case 'caffe2.pbtxt':
try {
const reader = await context.read('protobuf.text');
reader.field = function(tag, message) {
if (message instanceof caffe2.proto.DeviceOption) {
message[tag] = this.read();
return;
}
throw new Error(`Unknown field '${tag}' ${this.location()}`);
};
return caffe2.proto.NetDef.decodeText(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new caffe2.Error(`File format is not caffe2.NetDef (${message.replace(/\.$/, '')}).`);
}
default:
throw new caffe2.Error(`Unsupported Caffe2 predict format '${context.type}'.`);
}
}
return null;
};
const predict_net = await open(predict);
const init_net = await open(init);
return new caffe2.Model(metadata, predict_net, init_net);
}
};
caffe2.Model = class {
constructor(metadata, predict_net, init_net) {
const net = predict_net || init_net;
this.format = 'Caffe2';
this.domain = net.domain || null;
this.modules = [new caffe2.Graph(metadata, predict_net, init_net)];
}
};
caffe2.Graph = class {
constructor(metadata, predict_net, init_net) {
const net = predict_net || init_net;
init_net = predict_net ? init_net : null;
this.name = net.name || '';
this.nodes = [];
this.description = net.type;
const initializers = new Set();
const tensors = new Map();
for (const name of net.external_input) {
tensors.set(name, new caffe2.Tensor(name, {}));
}
if (init_net) {
const dataTypes = new Map([
['GivenTensorFill', 'float32'],
['GivenTensorDoubleFill', 'float64'],
['GivenTensorBoolFill', 'boolean'],
['GivenTensorByteStringToUInt8Fill', 'uint8'],
['GivenTensorInt16Fill', 'int16'],
['GivenTensorSInt16Fill', 'int16'],
['GivenTensorIntFill', 'int32'],
['GivenTensorInt64Fill', 'int64'],
['GivenTensorStringFill', 'string'],
['Int8GivenIntTensorFill', 'int32'],
['Int8GivenTensorFill', 'int8'],
['XavierFill', null],
['ConstantFill', null]
]);
for (const op of init_net.op) {
if (op.output && op.output.length === 1) {
const [name] = op.output;
const tensor = {};
for (const arg of op.arg) {
tensor[arg.name] = arg;
}
if (!dataTypes.has(op.type)) {
throw new caffe2.Error(`Unsupported init op '${op.type}'.`);
}
tensor.dataType = dataTypes.get(op.type);
if (tensor.values && tensor.values.floats && (tensor.values.floats.length !== 1 || tensor.values.floats[0] !== 0)) {
initializers.add(name);
}
tensors.set(name, new caffe2.Tensor(name, tensor));
}
}
}
const scope = {};
for (let i = 0; i < net.op.length; i++) {
const op = net.op[i];
op.input = op.input.map((input) => scope[input] ? scope[input] : input);
op.output = op.output.map((output) => {
if (scope[output]) {
const next = `${output}\n${i}`; // custom argument id
scope[output] = next;
return next;
}
scope[output] = output;
return output;
});
}
const values = new Map();
values.map = (name, type, tensor) => {
if (!values.has(name)) {
values.set(name, new caffe2.Value(name, type || null, tensor || null));
} else if (type || tensor) {
throw new caffe2.Value(`Duplicate value '${name}'.`);
}
return values.get(name);
};
for (const op of net.op) {
let index = 0;
for (const name of op.input) {
if (index > 0 && tensors.has(name)) {
if (!values.has(name)) {
values.set(name, new caffe2.Value(name, null, tensors.get(name)));
}
initializers.add(name);
}
index++;
}
}
for (const op of net.op) {
for (const name of op.output) {
if (tensors.has(name)) {
initializers.add(name);
}
}
}
let lastNode = null;
let lastOutput = null;
for (const op of net.op) {
const node = new caffe2.Node(metadata, op, values);
if (op.input.length === 1 &&
op.output.length >= 1 &&
op.input[0].split('\n').shift() === op.output[0].split('\n').shift() &&
lastNode &&
lastOutput === op.input[0].split('\n').shift()) {
lastNode.chain.push(node);
} else {
this.nodes.push(node);
lastNode = null;
lastOutput = null;
if (op.output.length === 1) {
lastNode = node;
lastOutput = op.output[0].split('\n').shift();
}
}
}
this.inputs = [];
for (const input of net.external_input) {
if (net.external_input.length > 1 && initializers.has(input)) {
continue;
}
const argument = new caffe2.Argument(input, [values.map(input)]);
this.inputs.push(argument);
}
this.outputs = [];
for (const output of net.external_output) {
const argument = new caffe2.Argument(output, [values.map(output)]);
this.outputs.push(argument);
}
}
};
caffe2.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
caffe2.Value = class {
constructor(name, type, initializer = null) {
if (typeof name !== 'string') {
throw new caffe2.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = !type && initializer ? initializer.type : type;
this.quantization = initializer && initializer.quantization ? initializer.quantization : null;
this.initializer = initializer;
}
};
caffe2.Node = class {
constructor(metadata, op, values) {
this.name = op.name || '';
this.device = op.engine || '';
this.chain = [];
this.type = metadata.type(op.type);
this.attributes = op.arg.map((arg) => {
const schema = metadata.attribute(op.type, arg.name);
const name = arg.name;
let value = null;
let type = null;
let visible = true;
if (arg.floats && arg.floats.length > 0) {
value = arg.floats;
} else if (arg.ints && arg.ints.length > 0) {
value = arg.ints;
} else if (arg.nets && arg.nets.length > 0) {
value = arg.nets.map((net) => new caffe2.Graph(metadata, net, null));
type = 'graph[]';
} else if (arg.n) {
value = new caffe2.Graph(metadata, arg.n, null);
type = 'graph';
} else {
value = arg.i;
}
if (schema) {
type = !type && schema.type ? schema.type : type;
if (type === 'boolean') {
value = value !== 0 && value.toString() !== '0' ? true : false;
}
if (schema.visible === false) {
visible = false;
} else if (schema.default !== undefined) {
if (value === metadata.default || (value && value.toString() === schema.default.toString())) {
visible = false;
}
}
}
return new caffe2.Argument(name, value, type, visible);
});
const inputs = op.input;
const outputs = op.output;
this.inputs = [];
let inputIndex = 0;
if (this.type && this.type.inputs) {
for (const inputDef of this.type.inputs) {
if (inputIndex < inputs.length || inputDef.option !== 'optional') {
const inputCount = (inputDef.option === 'variadic') ? (inputs.length - inputIndex) : 1;
const inputArguments = inputs.slice(inputIndex, inputIndex + inputCount).filter((id) => id !== '' || inputDef.option !== 'optional').map((id) => values.map(id));
this.inputs.push(new caffe2.Argument(inputDef.name, inputArguments));
inputIndex += inputCount;
}
}
} else {
this.inputs.push(...inputs.slice(inputIndex).map((input, index) => {
const inputName = ((inputIndex + index) === 0) ? 'input' : (inputIndex + index).toString();
return new caffe2.Argument(inputName, [values.map(input)]);
}));
}
this.outputs = [];
let outputIndex = 0;
if (this.type && this.type.outputs) {
for (const outputDef of this.type.outputs) {
if (outputIndex < outputs.length || outputDef.option !== 'optional') {
const outputCount = (outputDef.option === 'variadic') ? (outputs.length - outputIndex) : 1;
const outputArguments = outputs.slice(outputIndex, outputIndex + outputCount).map((id) => values.map(id));
this.outputs.push(new caffe2.Argument(outputDef.name, outputArguments));
outputIndex += outputCount;
}
}
} else {
this.outputs.push(...outputs.slice(outputIndex).map((output, index) => {
const outputName = ((outputIndex + index) === 0) ? 'output' : (outputIndex + index).toString();
return new caffe2.Argument(outputName, [values.map(output)]);
}));
}
}
};
caffe2.Tensor = class {
constructor(name, tensor) {
this.name = name;
const shape = tensor.shape && tensor.shape.ints ? tensor.shape.ints : null;
this.type = new caffe2.TensorType(tensor.dataType, new caffe2.TensorShape(shape));
this.values = null;
this.category = 'Initializer';
this.encoding = '|';
if (tensor.Y_scale !== undefined || tensor.Y_zero_point !== undefined) {
this.quantization = {
type: 'linear',
scale: [tensor.Y_scale ? tensor.Y_scale.f : 0],
offset: [tensor.Y_zero_point && typeof tensor.Y_zero_point.i === 'bigint' ? tensor.Y_zero_point.i.toNumber() : 0]
};
}
if (tensor.values) {
switch (this.type.dataType) {
case 'float32': this.values = tensor.values.floats; break;
case 'boolean': this.values = tensor.values.ints; break;
case 'int8': this.values = new Int8Array(tensor.values.s); break;
case 'int32': this.values = tensor.values.ints; break;
default: break;
}
}
}
};
caffe2.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType || '?';
this.shape = shape;
}
toString() {
return this.dataType + this.shape.toString();
}
};
caffe2.TensorShape = class {
constructor(dimensions) {
this.dimensions = Array.isArray(dimensions) ? dimensions.map((dim) => typeof dim === 'bigint' ? dim.toNumber() : dim) : dimensions;
}
toString() {
if (Array.isArray(this.dimensions) && this.dimensions.length > 0) {
return `[${this.dimensions.map((dim) => dim.toString()).join(',')}]`;
}
return '';
}
};
caffe2.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Caffe2 model.';
}
};
export const ModelFactory = caffe2.ModelFactory;
+266
View File
@@ -0,0 +1,266 @@
export const NCatBoostFbs = {};
NCatBoostFbs.TGuid = class TGuid {
static decode(reader, position) {
const $ = new NCatBoostFbs.TGuid();
$.dw0 = reader.uint32(position + 0);
$.dw1 = reader.uint32(position + 4);
$.dw2 = reader.uint32(position + 8);
$.dw3 = reader.uint32(position + 12);
return $;
}
};
NCatBoostFbs.ENanValueTreatment = {
AsIs: 0, '0': 'AsIs',
AsFalse: 1, '1': 'AsFalse',
AsTrue: 2, '2': 'AsTrue'
};
NCatBoostFbs.TFloatFeature = class TFloatFeature {
static decode(reader, position) {
const $ = new NCatBoostFbs.TFloatFeature();
$.HasNans = reader.bool_(position, 4, false);
$.Index = reader.int32_(position, 6, -1);
$.FlatIndex = reader.int32_(position, 8, -1);
$.Borders = reader.array(position, 10, Float32Array);
$.FeatureId = reader.string_(position, 12, null);
$.NanValueTreatment = reader.int8_(position, 14, 0);
return $;
}
};
NCatBoostFbs.TCatFeature = class TCatFeature {
static decode(reader, position) {
const $ = new NCatBoostFbs.TCatFeature();
$.Index = reader.int32_(position, 4, -1);
$.FlatIndex = reader.int32_(position, 6, -1);
$.FeatureId = reader.string_(position, 8, null);
$.UsedInModel = reader.bool_(position, 10, true);
return $;
}
};
NCatBoostFbs.TTextFeature = class TTextFeature {
static decode(reader, position) {
const $ = new NCatBoostFbs.TTextFeature();
$.Index = reader.int32_(position, 4, -1);
$.FlatIndex = reader.int32_(position, 6, -1);
$.FeatureId = reader.string_(position, 8, null);
$.UsedInModel = reader.bool_(position, 10, true);
return $;
}
};
NCatBoostFbs.TEmbeddingFeature = class TEmbeddingFeature {
static decode(reader, position) {
const $ = new NCatBoostFbs.TEmbeddingFeature();
$.Index = reader.int32_(position, 4, -1);
$.FlatIndex = reader.int32_(position, 6, -1);
$.FeatureId = reader.string_(position, 8, null);
$.Dimension = reader.int32_(position, 10, 0);
$.UsedInModel = reader.bool_(position, 12, true);
return $;
}
};
NCatBoostFbs.ESourceFeatureType = {
Text: 0, '0': 'Text',
Embedding: 1, '1': 'Embedding'
};
NCatBoostFbs.TEstimatedFeature = class TEstimatedFeature {
static decode(reader, position) {
const $ = new NCatBoostFbs.TEstimatedFeature();
$.SourceFeatureIndex = reader.int32_(position, 4, -1);
$.CalcerId = reader.struct(position, 6, NCatBoostFbs.TGuid);
$.LocalIndex = reader.int32_(position, 8, -1);
$.Borders = reader.array(position, 10, Float32Array);
$.SourceFeatureType = reader.int8_(position, 12, 0);
return $;
}
};
NCatBoostFbs.TOneHotFeature = class TOneHotFeature {
static decode(reader, position) {
const $ = new NCatBoostFbs.TOneHotFeature();
$.Index = reader.int32_(position, 4, -1);
$.Values = reader.array(position, 6, Int32Array);
$.StringValues = reader.strings_(position, 8);
return $;
}
};
NCatBoostFbs.TFloatSplit = class TFloatSplit {
static decode(reader, position) {
const $ = new NCatBoostFbs.TFloatSplit();
$.Index = reader.int32(position + 0);
$.Border = reader.float32(position + 4);
return $;
}
};
NCatBoostFbs.TOneHotSplit = class TOneHotSplit {
static decode(reader, position) {
const $ = new NCatBoostFbs.TOneHotSplit();
$.Index = reader.int32(position + 0);
$.Value = reader.int32(position + 4);
return $;
}
};
NCatBoostFbs.TFeatureCombination = class TFeatureCombination {
static decode(reader, position) {
const $ = new NCatBoostFbs.TFeatureCombination();
$.CatFeatures = reader.array(position, 4, Int32Array);
$.FloatSplits = reader.structs(position, 6, NCatBoostFbs.TFloatSplit, 8);
$.OneHotSplits = reader.structs(position, 8, NCatBoostFbs.TOneHotSplit, 8);
return $;
}
};
NCatBoostFbs.ECtrType = {
Borders: 0, '0': 'Borders',
Buckets: 1, '1': 'Buckets',
BinarizedTargetMeanValue: 2, '2': 'BinarizedTargetMeanValue',
FloatTargetMeanValue: 3, '3': 'FloatTargetMeanValue',
Counter: 4, '4': 'Counter',
FeatureFreq: 5, '5': 'FeatureFreq'
};
NCatBoostFbs.TModelCtrBase = class TModelCtrBase {
static decode(reader, position) {
const $ = new NCatBoostFbs.TModelCtrBase();
$.FeatureCombination = reader.table(position, 4, NCatBoostFbs.TFeatureCombination);
$.CtrType = reader.int8_(position, 6, 0);
$.TargetBorderClassifierIdx = reader.int32_(position, 8, 0);
return $;
}
};
NCatBoostFbs.TModelCtr = class TModelCtr {
static decode(reader, position) {
const $ = new NCatBoostFbs.TModelCtr();
$.Base = reader.table(position, 4, NCatBoostFbs.TModelCtrBase);
$.TargetBorderIdx = reader.int32_(position, 6, 0);
$.PriorNum = reader.float32_(position, 8, 0);
$.PriorDenom = reader.float32_(position, 10, 1);
$.Shift = reader.float32_(position, 12, 0);
$.Scale = reader.float32_(position, 14, 1);
return $;
}
};
NCatBoostFbs.TCtrFeature = class TCtrFeature {
static decode(reader, position) {
const $ = new NCatBoostFbs.TCtrFeature();
$.Ctr = reader.table(position, 4, NCatBoostFbs.TModelCtr);
$.Borders = reader.array(position, 6, Float32Array);
return $;
}
};
NCatBoostFbs.TCtrValueTable = class TCtrValueTable {
static create(reader) {
return NCatBoostFbs.TCtrValueTable.decode(reader, reader.root);
}
static decode(reader, position) {
const $ = new NCatBoostFbs.TCtrValueTable();
$.ModelCtrBase = reader.table(position, 4, NCatBoostFbs.TModelCtrBase);
$.IndexHashRaw = reader.array(position, 6, Uint8Array);
$.CTRBlob = reader.array(position, 8, Uint8Array);
$.CounterDenominator = reader.int32_(position, 10, 0);
$.TargetClassesCount = reader.int32_(position, 12, 0);
return $;
}
};
NCatBoostFbs.TKeyValue = class TKeyValue {
static decode(reader, position) {
const $ = new NCatBoostFbs.TKeyValue();
$.Key = reader.string_(position, 4, null);
$.Value = reader.string_(position, 6, null);
return $;
}
};
NCatBoostFbs.TNonSymmetricTreeStepNode = class TNonSymmetricTreeStepNode {
static decode(reader, position) {
const $ = new NCatBoostFbs.TNonSymmetricTreeStepNode();
$.LeftSubtreeDiff = reader.uint16(position + 0);
$.RightSubtreeDiff = reader.uint16(position + 2);
return $;
}
};
NCatBoostFbs.TRepackedBin = class TRepackedBin {
static decode(reader, position) {
const $ = new NCatBoostFbs.TRepackedBin();
$.FeatureIndex = reader.uint16(position + 0);
$.XorMask = reader.uint8(position + 2);
$.SplitIdx = reader.uint8(position + 3);
return $;
}
};
NCatBoostFbs.TModelTrees = class TModelTrees {
static decode(reader, position) {
const $ = new NCatBoostFbs.TModelTrees();
$.ApproxDimension = reader.int32_(position, 4, 0);
$.TreeSplits = reader.array(position, 6, Int32Array);
$.TreeSizes = reader.array(position, 8, Int32Array);
$.TreeStartOffsets = reader.array(position, 10, Int32Array);
$.CatFeatures = reader.tables(position, 12, NCatBoostFbs.TCatFeature);
$.FloatFeatures = reader.tables(position, 14, NCatBoostFbs.TFloatFeature);
$.OneHotFeatures = reader.tables(position, 16, NCatBoostFbs.TOneHotFeature);
$.CtrFeatures = reader.tables(position, 18, NCatBoostFbs.TCtrFeature);
$.LeafValues = reader.array(position, 20, Float64Array);
$.LeafWeights = reader.array(position, 22, Float64Array);
$.NonSymmetricStepNodes = reader.structs(position, 24, NCatBoostFbs.TNonSymmetricTreeStepNode, 4);
$.NonSymmetricNodeIdToLeafId = reader.array(position, 26, Uint32Array);
$.TextFeatures = reader.tables(position, 28, NCatBoostFbs.TTextFeature);
$.EstimatedFeatures = reader.tables(position, 30, NCatBoostFbs.TEstimatedFeature);
$.Scale = reader.float64_(position, 32, 1);
$.Bias = reader.float64_(position, 34, 0);
$.MultiBias = reader.array(position, 36, Float64Array);
$.RepackedBins = reader.structs(position, 38, NCatBoostFbs.TRepackedBin, 4);
$.EmbeddingFeatures = reader.tables(position, 40, NCatBoostFbs.TEmbeddingFeature);
return $;
}
};
NCatBoostFbs.TModelCore = class TModelCore {
static create(reader) {
return NCatBoostFbs.TModelCore.decode(reader, reader.root);
}
static decode(reader, position) {
const $ = new NCatBoostFbs.TModelCore();
$.FormatVersion = reader.string_(position, 4, null);
$.ModelTrees = reader.table(position, 6, NCatBoostFbs.TModelTrees);
$.InfoMap = reader.tables(position, 8, NCatBoostFbs.TKeyValue);
$.ModelPartIds = reader.strings_(position, 10);
return $;
}
};
+187
View File
@@ -0,0 +1,187 @@
import * as flatbuffers from './flatbuffers.js';
import * as python from './python.js';
const catboost = {};
catboost.ModelFactory = class {
async match(context) {
const stream = context.stream;
if (stream && stream.length > 4) {
const buffer = stream.peek(4);
const signature = Array.from(buffer).map((c) => String.fromCharCode(c)).join('');
if (signature === 'CBM1') {
return context.set('catboost.flatbuffers');
}
}
const obj = await context.peek('pkl');
if (obj && obj.__class__ && obj.__class__.__module__) {
const name = `${obj.__class__.__module__}.${obj.__class__.__name__}`;
if (name.startsWith('catboost.') || name.startsWith('autogluon.tabular.models.catboost.')) {
return context.set('catboost.pickle', obj);
}
}
return null;
}
async open(context) {
switch (context.type) {
case 'catboost.flatbuffers': {
const stream = context.stream;
const buffer = stream.peek();
const execution = new python.Execution();
const obj = execution.invoke('catboost.CatBoostClassifier', []);
obj._object.flatbuffers = flatbuffers;
obj._object.schema = await context.require('./catboost-schema');
obj.load_model(buffer);
return new catboost.Model(obj._object);
}
case 'catboost.pickle': {
let obj = context.value;
const name = obj && obj.__class__ && obj.__class__.__module__ && obj.__class__.__name__ ? `${obj.__class__.__module__}.${obj.__class__.__name__}` : '';
if (name === 'autogluon.tabular.models.catboost.catboost_model.CatBoostModel') {
obj = obj.model;
}
obj._object.flatbuffers = flatbuffers;
obj._object.schema = await context.require('./catboost-schema');
obj.load_model();
return new catboost.Model(obj._object);
}
default: {
throw new catboost.Error(`Unsupported CatBoost format '${context.type}'.`);
}
}
}
};
catboost.Model = class {
constructor(obj) {
this.format = 'CatBoost';
this.metadata = [];
for (const [name, value] of obj._get_info_map()) {
this.metadata.push({ name, value });
}
this.modules = [new catboost.Graph(obj)];
}
};
catboost.Graph = class {
constructor(obj) {
this.inputs = [];
this.outputs = [];
this.nodes = [];
const features = [];
for (const feature of obj._get_float_features()) {
const name = feature.FeatureId || `float_feature_${feature.FlatIndex}`;
const value = new catboost.Value(name, new catboost.TensorType('float32'));
features.push(value);
this.inputs.push(new catboost.Argument(name, [value]));
}
for (const feature of obj._get_cat_features()) {
const name = feature.FeatureId || `cat_feature_${feature.FlatIndex}`;
const value = new catboost.Value(name, new catboost.TensorType('int32'));
features.push(value);
this.inputs.push(new catboost.Argument(name, [value]));
}
for (const feature of obj._get_text_features()) {
const name = feature.FeatureId || `text_feature_${feature.FlatIndex}`;
const value = new catboost.Value(name, new catboost.TensorType('string'));
features.push(value);
this.inputs.push(new catboost.Argument(name, [value]));
}
for (const feature of obj._get_embedding_features()) {
const name = feature.FeatureId || `embedding_feature_${feature.FlatIndex}`;
const value = new catboost.Value(name, new catboost.TensorType('float32[]'));
features.push(value);
this.inputs.push(new catboost.Argument(name, [value]));
}
const node = new catboost.Node(obj, features);
this.nodes.push(node);
}
};
catboost.Argument = class {
constructor(name, value) {
this.name = name;
this.value = value;
}
};
catboost.Value = class {
constructor(name, type) {
this.name = name;
this.type = type || null;
}
};
catboost.TensorType = class {
constructor(dataType) {
this.dataType = dataType;
this.shape = null;
}
toString() {
return this.dataType;
}
};
catboost.Node = class {
constructor(obj, features) {
this.name = '';
this.type = { name: 'CatBoost' };
this.inputs = [];
this.outputs = [];
this.attributes = [];
if (features.length > 0) {
this.inputs.push(new catboost.Argument('features', features));
}
const treeCount = obj._get_tree_count();
if (treeCount > 0) {
this.attributes.push(new catboost.Argument('tree_count', treeCount));
}
const treeSizes = obj._get_tree_sizes();
if (treeSizes.length > 0) {
this.attributes.push(new catboost.Argument('tree_sizes', Array.from(treeSizes)));
}
const treeSplits = obj._get_tree_splits();
if (treeSplits.length > 0) {
this.attributes.push(new catboost.Argument('tree_splits', Array.from(treeSplits)));
}
const leafValues = obj._get_leaf_values();
if (leafValues.length > 0) {
this.attributes.push(new catboost.Argument('leaf_values', Array.from(leafValues)));
}
const leafWeights = obj._get_leaf_weights();
if (leafWeights.length > 0) {
this.attributes.push(new catboost.Argument('leaf_weights', Array.from(leafWeights)));
}
const borders = obj._get_borders();
if (borders.length > 0) {
this.attributes.push(new catboost.Argument('borders', borders));
}
const [scale, bias] = obj._get_scale_and_bias();
if (scale !== undefined && scale !== 1) {
this.attributes.push(new catboost.Argument('scale', scale));
}
if (bias !== undefined && bias !== 0) {
this.attributes.push(new catboost.Argument('bias', bias));
}
}
};
catboost.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading CatBoost model.';
}
};
export const ModelFactory = catboost.ModelFactory;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+546
View File
@@ -0,0 +1,546 @@
import * as flatbuffers from './flatbuffers.js';
import * as flexbuffers from './flexbuffers.js';
import * as zip from './zip.js';
const circle = {};
circle.ModelFactory = class {
async match(context) {
const reader = await context.peek('flatbuffers.binary');
if (reader && reader.identifier === 'CIR0') {
return context.set('circle.flatbuffers', reader);
}
const obj = await context.peek('json');
if (obj && obj.subgraphs && obj.operator_codes) {
return context.set('circle.flatbuffers.json', obj);
}
return null;
}
async open(context) {
circle.schema = await context.require('./circle-schema');
circle.schema = circle.schema.circle;
let model = null;
const attachments = new Map();
switch (context.type) {
case 'circle.flatbuffers.json': {
try {
const reader = await context.read('flatbuffers.text');
model = circle.schema.Model.createText(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new circle.Error(`File text format is not circle.Model (${message.replace(/\.$/, '')}).`);
}
break;
}
case 'circle.flatbuffers': {
try {
const reader = context.value;
model = circle.schema.Model.create(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new circle.Error(`File format is not circle.Model (${message.replace(/\.$/, '')}).`);
}
try {
const stream = context.stream;
const archive = zip.Archive.open(stream);
if (archive) {
for (const [name, value] of archive.entries) {
attachments.set(name, value);
}
}
} catch {
// continue regardless of error
}
break;
}
default: {
throw new circle.Error(`Unsupported Circle format '${context.type}'.`);
}
}
const stream = context.stream;
const metadata = await context.metadata('circle-metadata.json');
return new circle.Model(metadata, model, stream);
}
};
circle.Model = class {
constructor(metadata, model, stream) {
this.format = 'Circle';
this.format = `${this.format} v${model.version}`;
this.description = model.description || '';
this.modules = [];
this.metadata = [];
const builtinOperators = new Map();
const upperCase = new Set(['2D', 'LSH', 'SVDF', 'RNN', 'L2', 'LSTM']);
for (const key of Object.keys(circle.schema.BuiltinOperator)) {
const value = key === 'BATCH_MATMUL' ? 'BATCH_MAT_MUL' : key;
const name = value.split('_').map((s) => (s.length < 1 || upperCase.has(s)) ? s : s[0] + s.substring(1).toLowerCase()).join('');
const index = circle.schema.BuiltinOperator[key];
builtinOperators.set(index, name);
}
const operators = model.operator_codes.map((operator) => {
const code = Math.max(operator.deprecated_builtin_code, operator.builtin_code || 0);
const value = {};
if (code === circle.schema.BuiltinOperator.CUSTOM) {
value.name = operator.custom_code ? operator.custom_code : 'Custom';
value.version = operator.version;
value.custom = true;
} else {
value.name = builtinOperators.has(code) ? builtinOperators.get(code) : code.toString();
value.version = operator.version;
value.custom = false;
}
return value;
});
let modelMetadata = null;
for (const metadata of model.metadata) {
const buffer = model.buffers[metadata.buffer];
let data = null;
const position = stream.position;
if (buffer && buffer.data && buffer.data.length > 0) {
data = buffer.data;
} else if (buffer && buffer.offset !== 0n && buffer.size !== 0n) {
const offset = buffer.offset.toNumber();
const size = buffer.size.toNumber();
stream.seek(offset);
data = stream.read(size);
}
stream.seek(position);
if (data) {
switch (metadata.name) {
case 'min_runtime_version': {
const decoder = new TextDecoder('utf-8');
this.runtime = decoder.decode(data);
break;
}
case 'TFLITE_METADATA': {
const reader = flatbuffers.BinaryReader.open(data);
if (!reader || !circle.schema.ModelMetadata.identifier(reader)) {
throw new circle.Error('Invalid TensorFlow Lite metadata.');
}
modelMetadata = circle.schema.ModelMetadata.create(reader);
if (modelMetadata.name) {
this.name = modelMetadata.name;
}
if (modelMetadata.version) {
this.version = modelMetadata.version;
}
if (modelMetadata.description) {
this.description = this.description ? [this.description, modelMetadata.description].join(' ') : modelMetadata.description;
}
if (modelMetadata.author) {
this.metadata.push(new circle.Argument('author', modelMetadata.author));
}
if (modelMetadata.license) {
this.metadata.push(new circle.Argument('license', modelMetadata.license));
}
break;
}
default: {
const value = data.length < 256 && data.every((c) => c >= 32 && c < 128) ? String.fromCharCode.apply(null, data) : '?';
const argument = new circle.Argument(metadata.name, value);
this.metadata.push(argument);
break;
}
}
}
}
const subgraphs = model.subgraphs;
const subgraphsMetadata = modelMetadata ? modelMetadata.subgraph_metadata : null;
for (let i = 0; i < subgraphs.length; i++) {
const subgraph = subgraphs[i];
const name = subgraphs.length > 1 ? i.toString() : '';
const subgraphMetadata = subgraphsMetadata && i < subgraphsMetadata.length ? subgraphsMetadata[i] : null;
const signatures = model.signature_defs.filter((signature) => signature.subgraph_index === i);
const graph = new circle.Graph(metadata, subgraph, signatures, subgraphMetadata, name, operators, model, stream);
this.modules.push(graph);
}
}
};
circle.Graph = class {
constructor(metadata, subgraph, signatures, subgraphMetadata, name, operators, model, stream) {
this.name = subgraph.name || name;
if (subgraph.operators.length === 0 && subgraph.tensors.length > 0 && operators.length === 0) {
operators.push({ name: 'Weights', custom: true });
const layers = new Map();
for (let i = 0; i < subgraph.tensors.length; i++) {
const tensor = subgraph.tensors[i];
const parts = tensor.name.split('.');
parts.pop();
const key = parts.join('.');
if (!layers.has(key)) {
const operator = { opcode_index: 0, inputs: [], outputs: [] };
layers.set(key, operator);
subgraph.operators.push(operator);
}
const operator = layers.get(key);
operator.inputs.push(i);
}
}
const tensors = new Map();
tensors.map = (index, metadata) => {
if (index === -1) {
return null;
}
if (!tensors.has(index)) {
let tensor = { name: '' };
let initializer = null;
let description = '';
let denotation = '';
if (index < subgraph.tensors.length) {
tensor = subgraph.tensors[index];
const buffer = model.buffers[tensor.buffer];
const is_variable = tensor.is_variable;
const variable = is_variable || (buffer && buffer.data && buffer.data.length > 0) || (buffer && buffer.offset !== 0n && buffer.size !== 0n);
initializer = variable ? new circle.Tensor(index, tensor, buffer, stream, is_variable) : null;
}
if (metadata) {
description = metadata.description;
const content = metadata.content;
if (content) {
const contentProperties = content.content_properties;
if (contentProperties instanceof circle.schema.FeatureProperties) {
denotation = 'Feature';
} else if (contentProperties instanceof circle.schema.ImageProperties) {
denotation = 'Image';
switch (contentProperties.color_space) {
case 0: denotation += '(Unknown)'; break;
case 1: denotation += '(RGB)'; break;
case 2: denotation += '(Grayscale)'; break;
default: throw circle.Error(`Unsupported image color space '${contentProperties.color_space}'.`);
}
} else if (contentProperties instanceof circle.schema.BoundingBoxProperties) {
denotation = 'BoundingBox';
} else if (contentProperties instanceof circle.schema.AudioProperties) {
denotation = `Audio(${contentProperties.sample_rate},${contentProperties.channels})`;
}
}
}
const value = new circle.Value(index, tensor, initializer, description, denotation);
tensors.set(index, value);
}
return tensors.get(index);
};
this.inputs = Array.from(subgraph.inputs).map((tensor_index, index) => {
const metadata = subgraphMetadata && index < subgraphMetadata.input_tensor_metadata.length ? subgraphMetadata.input_tensor_metadata[index] : null;
const value = tensors.map(tensor_index, metadata);
const values = value ? [value] : [];
const name = value ? value.name.split('\n')[0] : '?';
return new circle.Argument(name, values);
});
this.outputs = Array.from(subgraph.outputs).map((tensor_index, index) => {
const metadata = subgraphMetadata && index < subgraphMetadata.output_tensor_metadata.length ? subgraphMetadata.output_tensor_metadata[index] : null;
const value = tensors.map(tensor_index, metadata);
const values = value ? [value] : [];
const name = value ? value.name.split('\n')[0] : '?';
return new circle.Argument(name, values);
});
this.signatures = signatures.map((signature) => {
return new circle.Signature(signature, tensors);
});
this.nodes = Array.from(subgraph.operators).map((operator, index) => {
const opcode_index = operator.opcode_index;
const opcode = opcode_index < operators.length ? operators[opcode_index] : { name: `(${opcode_index})` };
return new circle.Node(metadata, operator, opcode, index.toString(), tensors);
});
}
};
circle.Signature = class {
constructor(signature, tensors) {
this.name = signature.signature_key;
this.inputs = signature.inputs.map((input) => {
const value = tensors.map(input.tensor_index);
const values = value ? [value] : [];
return new circle.Argument(input.name, values);
});
this.outputs = signature.outputs.map((output) => {
const value = tensors.map(output.tensor_index);
const values = value ? [value] : [];
return new circle.Argument(output.name, values);
});
}
};
circle.Node = class {
constructor(metadata, node, type, identifier, tensors) {
this.name = '';
this.identifier = identifier;
this.type = type.custom ? { name: type.name } : metadata.type(type.name);
this.inputs = [];
this.outputs = [];
this.attributes = [];
if (node) {
const attributes = [];
const inputs = Array.from(node.inputs || new Int32Array(0));
for (let i = 0; i < inputs.length;) {
let count = 1;
let name = null;
let visible = true;
const values = [];
if (this.type && this.type.inputs && i < this.type.inputs.length) {
const input = this.type.inputs[i];
name = input.name;
if (input.list) {
count = inputs.length - i;
}
if (input.visible === false) {
visible = false;
}
}
for (const index of inputs.slice(i, i + count)) {
const value = tensors.map(index);
if (value) {
values.push(value);
}
}
name = name ? name : (i + 1).toString();
i += count;
const argument = new circle.Argument(name, values, null, visible);
this.inputs.push(argument);
}
const outputs = Array.from(node.outputs || new Int32Array(0));
for (let i = 0; i < outputs.length; i++) {
const index = outputs[i];
const value = tensors.map(index);
const values = value ? [value] : [];
let name = (i + 1).toString();
if (this.type && this.type.outputs && i < this.type.outputs.length) {
const output = this.type.outputs[i];
if (output && output.name) {
name = output.name;
}
}
const argument = new circle.Argument(name, values);
this.outputs.push(argument);
}
if (type.custom && node.custom_options && node.custom_options.length > 0) {
let decoded = false;
if (node.custom_options_format === circle.schema.CustomOptionsFormat.FLEXBUFFERS) {
try {
const reader = flexbuffers.BinaryReader.open(node.custom_options);
if (reader) {
const custom_options = reader.read();
if (Array.isArray(custom_options)) {
attributes.push([null, 'custom_options', custom_options]);
decoded = true;
} else if (custom_options) {
for (const [key, value] of Object.entries(custom_options)) {
const schema = metadata.attribute(type.name, key);
attributes.push([schema, key, value]);
}
decoded = true;
}
}
} catch {
// continue regardless of error
}
}
if (!decoded) {
const schema = metadata.attribute(type.name, 'custom');
attributes.push([schema, 'custom', Array.from(node.custom_options)]);
}
}
const options = node.builtin_options;
if (options) {
for (const [name, value] of Object.entries(options)) {
if (name === 'fused_activation_function' && value) {
const ActivationFunctionType = circle.schema.ActivationFunctionType;
let type = '';
switch (value) {
case ActivationFunctionType.RELU: type = 'Relu'; break;
case ActivationFunctionType.RELU_N1_TO_1: type = 'ReluN1To1'; break;
case ActivationFunctionType.RELU6: type = 'Relu6'; break;
case ActivationFunctionType.TANH: type = 'Tanh'; break;
case ActivationFunctionType.SIGN_BIT: type = 'SignBit'; break;
case 6: type = 'Sigmoid'; break;
default: type = value.toString(); break;
}
const node = new circle.Node(metadata, null, { name: type }, null, []);
this.chain = [node];
}
const schema = metadata.attribute(type.name, name);
attributes.push([schema, name, value]);
}
}
this.attributes = attributes.map(([metadata, name, value]) => {
const type = metadata && metadata.type ? metadata.type : null;
value = ArrayBuffer.isView(value) ? Array.from(value) : value;
let visible = true;
if (name === 'fused_activation_function') {
visible = false;
}
if (type) {
const enumType = circle.schema[type];
if (enumType) {
value = enumType[value] || value;
}
}
if (metadata) {
if (metadata.visible === false) {
visible = false;
} else if (metadata.default !== undefined) {
if (typeof value === 'function') {
value = value();
}
if (value === metadata.default) {
visible = false;
}
}
}
return new circle.Argument(name, value, type, visible);
});
}
}
};
circle.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
circle.Value = class {
constructor(index, tensor, initializer, description, denotation) {
const name = tensor.name || '';
this.name = `${name}\n${index}`;
this.identifier = index.toString();
this.type = tensor.type !== undefined && tensor.shape !== undefined ? new circle.TensorType(tensor, denotation) : null;
this.initializer = initializer;
this.description = description;
const quantization = tensor.quantization;
if (quantization && (quantization.scale.length > 0 || quantization.zero_point.length > 0 || quantization.min.length > 0 || quantization.max.length)) {
this.quantization = {
type: 'linear',
dimension: quantization.quantized_dimension,
scale: quantization.scale,
offset: quantization.zero_point,
min: quantization.min,
max: quantization.max
};
}
}
};
circle.Tensor = class {
constructor(index, tensor, buffer, stream, is_variable) {
this.identifier = index.toString();
this.name = tensor.name;
this.type = new circle.TensorType(tensor);
this.category = is_variable ? 'Variable' : '';
this.encoding = this.type.dataType === 'string' ? '|' : '<';
if (buffer && buffer.data && buffer.data.length > 0) {
this._data = buffer.data.slice(0);
} else if (buffer && buffer.offset !== 0n && buffer.size !== 0n) {
const offset = buffer.offset.toNumber();
const size = buffer.size.toNumber();
stream.seek(offset);
this._data = stream.stream(size);
} else {
this._data = null;
}
}
get values() {
switch (this.type.dataType) {
case 'string': {
let offset = 0;
if (!this._data || this._data.byteLength < 4) {
throw new circle.Error(`Invalid string tensor '${this.name}'.`);
}
const data = new DataView(this._data.buffer, this._data.byteOffset, this._data.byteLength);
const count = data.getInt32(0, true);
if (count < 0 || count > Math.floor((data.byteLength - 4) / 4)) {
throw new circle.Error(`Invalid string tensor '${this.name}'.`);
}
offset += 4;
const offsetTable = [];
for (let j = 0; j < count; j++) {
offsetTable.push(data.getInt32(offset, true));
offset += 4;
}
offsetTable.push(this._data.length);
const stringTable = [];
const utf8Decoder = new TextDecoder('utf-8');
for (let k = 0; k < count; k++) {
const start = offsetTable[k];
const end = offsetTable[k + 1];
if (start < offset || start > end || end > this._data.length) {
throw new circle.Error(`Invalid string tensor '${this.name}'.`);
}
const textArray = this._data.subarray(start, end);
stringTable.push(utf8Decoder.decode(textArray));
}
return stringTable;
}
default: {
if (this._data instanceof Uint8Array) {
return this._data;
}
if (this._data && this._data.peek) {
return this._data.peek();
}
return null;
}
}
}
};
circle.TensorType = class {
constructor(tensor, denotation) {
const shape = tensor.shape_signature && tensor.shape_signature.length > 0 ? tensor.shape_signature : tensor.shape;
switch (tensor.type) {
case circle.schema.TensorType.BOOL: this.dataType = 'boolean'; break;
default: {
const name = circle.schema.TensorType[tensor.type];
this.dataType = name ? name.toLowerCase() : '?';
break;
}
}
this.shape = new circle.TensorShape(Array.from(shape || []));
this.denotation = denotation;
}
toString() {
return this.dataType + this.shape.toString();
}
};
circle.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
toString() {
if (!this.dimensions || this.dimensions.length === 0) {
return '';
}
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
};
circle.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Circle model.';
}
};
export const ModelFactory = circle.ModelFactory;
+795
View File
@@ -0,0 +1,795 @@
[
{
"name": "Abs",
"identifier": 8
},
{
"name": "Acos",
"identifier": 82
},
{
"name": "Asin",
"identifier": 81
},
{
"name": "Asinh",
"identifier": 86
},
{
"name": "Assign",
"identifier": 73
},
{
"name": "Atan",
"identifier": 96
},
{
"name": "Atanh",
"identifier": 85
},
{
"name": "AveragePooling",
"category": "Pool",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "BatchNormalization",
"identifier": 40,
"category": "Normalization",
"attributes": [
{ "name": "disableRegularization", "default": false },
{ "name": "useCuDNNEngine", "visible": false },
{ "name": "useCntkEngine", "visible": false },
{ "name": "runCountUntied", "visible": false },
{ "name": "epsilon", "default": 0.00001 },
{ "name": "normalizationTimeConstant", "default": 0 },
{ "name": "disableRegularization", "default": false },
{ "name": "blendTimeConstant", "default": 0 },
{ "name": "imageLayoutKind", "type": "ImageLayoutKind", "visible": false }
],
"inputs": [
{ "name": "input" },
{ "name": "scale" },
{ "name": "bias" },
{ "name": "mean" },
{ "name": "variance" },
{ "name": "count" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Block",
"identifier": 57
},
{
"name": "Cast",
"identifier": 91
},
{
"name": "ClassificationError",
"identifier": 36,
"outputs": [
{ "name": "output" }
]
},
{
"name": "Clip",
"identifier": 41
},
{
"name": "Combine",
"identifier": 44,
"category": "Tensor",
"inputs": [
{ "name": "inputs", "type": "Tensor[]" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ConstantOp",
"identifier": 89
},
{
"name": "Convolution",
"identifier": 33,
"category": "Layer",
"attributes": [
{ "name": "transpose", "default": false },
{ "name": "maxTempMemSizeInSamples", "default": 0 },
{ "name": "dilation", "default": [ 1, null ] },
{ "name": "outputShape", "default": 0 },
{ "name": "sharing", "default": [ true, null ] },
{ "name": "strides", "default": [ 1, null ] },
{ "name": "includePad", "default": false },
{ "name": "ceilOutDim", "default": false },
{ "name": "autoPadding", "default": [ true, null ] },
{ "name": "lowerPad", "default": [ 0, null ] },
{ "name": "upperPad", "default": [ 0, null ] },
{ "name": "convolution2D", "visible": false },
{ "name": "poolKind", "type": "PoolKind", "default": "None" },
{ "name": "imageLayoutKind", "type": "ImageLayoutKind", "visible": false }
],
"inputs": [
{ "name": "input" },
{ "name": "W" },
{ "name": "b" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ConvolutionSequenceShape",
"identifier": 97
},
{
"name": "Cos",
"identifier": 55
},
{
"name": "CosDistance",
"identifier": 53
},
{
"name": "CosDistanceWithNegativeSamples",
"identifier": 67
},
{
"name": "Cosh",
"identifier": 78
},
{
"name": "Crop",
"identifier": 84,
"category": "Data"
},
{
"name": "CrossEntropyWithSoftmax",
"identifier": 35,
"outputs": [
{ "name": "output" }
]
},
{
"name": "CustomProxyOp",
"identifier": 93
},
{
"name": "Dropout",
"identifier": 15,
"category": "Dropout",
"attributes": [
{ "name": "rngSeed", "visible": false },
{ "name": "rngOffset", "visible": false }
],
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "EditDistanceError",
"identifier": 61
},
{
"name": "ElementTimes",
"identifier": 21,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "ELU",
"identifier": 65
},
{
"name": "Equal",
"identifier": 22,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "Exp",
"identifier": 4,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "EyeLikeOp",
"identifier": 92
},
{
"name": "Floor",
"identifier": 7,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ForwardBackward",
"identifier": 66
},
{
"name": "FutureValue",
"identifier": 38,
"attributes": [
{ "name": "offset", "type": "uint32", "default": 1 }
],
"inputs": [
{ "name": "input" },
{ "name": "initialState" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Gather",
"identifier": 74,
"category": "Transform"
},
{
"name": "GatherPacked",
"identifier": 29,
"inputs": [
{ "name": "index" },
{ "name": "source" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Greater",
"identifier": 26,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "GreaterEqual",
"identifier": 27,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "Hardmax",
"identifier": 11,
"category": "Activation"
},
{
"name": "InvStdDev",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "LabelsToGraph",
"identifier": 63
},
{
"name": "LambdaRank",
"identifier": 59
},
{
"name": "LatticeSequenceWithSoftmax",
"identifier": 90
},
{
"name": "Less",
"identifier": 24,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "LessEqual",
"identifier": 25,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "Log",
"identifier": 5,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Logistic",
"identifier": 48
},
{
"name": "LogPlus",
"identifier": 52
},
{
"name": "LogSoftmax",
"identifier": 51
},
{
"name": "MaxPooling",
"category": "Pool",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Mean",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Minus",
"identifier": 20,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "NDCG",
"identifier": 60
},
{
"name": "Negate",
"identifier": 0,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "NoOp",
"identifier": 62,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "NotEqual",
"identifier": 23,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "OneHot",
"identifier": 68
},
{
"name": "OptimizedRNNStack",
"identifier": 49
},
{
"name": "PackedIndex",
"identifier": 28,
"inputs": [
{ "name": "source" },
{ "name": "index" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Pad",
"identifier": 83,
"category": "Tensor",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Pass",
"identifier": 56
},
{
"name": "PastValue",
"identifier": 37,
"attributes": [
{ "name": "offset", "type": "uint32", "default": 1 }
],
"inputs": [
{ "name": "input" },
{ "name": "initialState" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Plus",
"identifier": 19,
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "Pooling",
"identifier": 17,
"category": "Pool",
"attributes": [
{ "name": "transpose", "default": false },
{ "name": "includePad", "default": false },
{ "name": "ceilOutDim", "default": false },
{ "name": "autoPadding", "default": [ false, null ] },
{ "name": "sharing", "default": [ true, null ] },
{ "name": "strides", "default": [ 1, null ] },
{ "name": "lowerPad", "default": [ 0, null ] },
{ "name": "upperPad", "default": [ 0, null ] },
{ "name": "outputShape", "default": 0 },
{ "name": "maxTempMemSizeInSamples", "default": 0 },
{ "name": "poolingType", "type": "PoolingType", "default": "Max" },
{ "name": "poolKind", "type": "PoolKind", "default": "None" },
{ "name": "imageLayoutKind", "type": "ImageLayoutKind", "visible": false }
],
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Pow",
"identifier": 69
},
{
"name": "RandomDistribution",
"identifier": 76
},
{
"name": "RandomSample",
"identifier": 45
},
{
"name": "RandomSampleInclusionFrequency",
"identifier": 46
},
{
"name": "Reciprocal",
"identifier": 9,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ReconcileDynamicAxis",
"identifier": 50
},
{
"name": "RectifiedLinear",
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ReduceElements",
"identifier": 39,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ReLU",
"identifier": 3,
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Reshape",
"identifier": 16,
"category": "Shape",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ROIPooling",
"identifier": 47,
"category": "Pool",
"attributes": [
{ "name": "spatialScale", "default": 0.0625 },
{ "name": "poolKind", "type": "PoolKind", "default": "None" }
],
"inputs": [
{ "name": "inputs" },
{ "name": "ROIs" }
],
"outputs": [
{ "name": "outputs" }
]
},
{
"name": "ScatterPacked",
"identifier": 30
},
{
"name": "Select",
"identifier": 42
},
{
"name": "Sigmoid",
"identifier": 1,
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Sin",
"identifier": 54
},
{
"name": "Sinh",
"identifier": 77
},
{
"name": "Slice",
"identifier": 14,
"category": "Tensor",
"inputs": [
{ "name": "input" },
{ "name": "begin" },
{ "name": "end" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Softmax",
"identifier": 10,
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Splice",
"identifier": 43,
"category": "Tensor",
"inputs": [
{ "name": "inputs", "type": "Tensor[]" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Sqrt",
"identifier": 6,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "SquaredError",
"identifier": 34
},
{
"name": "Squeeze",
"identifier": 88,
"category": "Transform"
},
{
"name": "StableSigmoid",
"identifier": 75,
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "StopGradient",
"identifier": 64
},
{
"name": "StraightThrough",
"identifier": 94
},
{
"name": "SumAll",
"identifier": 18
},
{
"name": "Tan",
"identifier": 95
},
{
"name": "Tanh",
"identifier": 2,
"category": "Activation",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "Times",
"identifier": 31,
"attributes": [
{ "name": "outputRank", "default": 1 },
{ "name": "inferInputRankToMap", "visible": false, "default": -1 }
],
"inputs": [
{ "name": "A" },
{ "name": "B" }
],
"outputs": [
{ "name": "C" }
]
},
{
"name": "ToBatch",
"identifier": 80
},
{
"name": "TopK",
"identifier": 87,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "ToSequence",
"identifier": 70
},
{
"name": "ToSequenceLike",
"identifier": 71
},
{
"name": "TransposeAxes",
"identifier": 12,
"category": "Activation"
},
{
"name": "TransposeDimensions",
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
},
{
"name": "TransposeTimes",
"identifier": 32
},
{
"name": "UnpackBatch",
"identifier": 79
},
{
"name": "UnpackSequence",
"identifier": 72
},
{
"name": "Unpooling",
"identifier": 58
},
{
"name": "Where",
"identifier": 13,
"inputs": [
{ "name": "input" }
],
"outputs": [
{ "name": "output" }
]
}
]
+346
View File
@@ -0,0 +1,346 @@
export const CNTK = {};
CNTK.proto = {};
CNTK.proto.NDShape = class NDShape {
constructor() {
this.shape_dim = [];
}
static decode(reader, length) {
const message = new CNTK.proto.NDShape();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.shape_dim = reader.array(message.shape_dim, () => reader.uint64(), tag);
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.Axis = class Axis {
static decode(reader, length) {
const message = new CNTK.proto.Axis();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.static_axis_idx = reader.int32();
break;
case 2:
message.name = reader.string();
break;
case 3:
message.is_ordered_dynamic_axis = reader.bool();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.Axis.prototype.static_axis_idx = 0;
CNTK.proto.Axis.prototype.name = "";
CNTK.proto.Axis.prototype.is_ordered_dynamic_axis = false;
CNTK.proto.NDArrayView = class NDArrayView {
get values() {
CNTK.proto.NDArrayView.valuesSet = CNTK.proto.NDArrayView.valuesSet || new Set(["float_values", "double_values", "bytes_value", "sint32_values"]);
return Object.keys(this).find((key) => CNTK.proto.NDArrayView.valuesSet.has(key) && this[key] !== null);
}
static decode(reader, length) {
const message = new CNTK.proto.NDArrayView();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.data_type = reader.int32();
break;
case 2:
message.storage_format = reader.int32();
break;
case 3:
message.shape = CNTK.proto.NDShape.decode(reader, reader.uint32());
break;
case 4:
message.float_values = CNTK.proto.NDArrayView.FloatValues.decode(reader, reader.uint32());
break;
case 5:
message.double_values = CNTK.proto.NDArrayView.DoubleValues.decode(reader, reader.uint32());
break;
case 6:
message.bytes_value = CNTK.proto.NDArrayView.BytesValue.decode(reader, reader.uint32());
break;
case 7:
message.sint32_values = CNTK.proto.NDArrayView.IntValues.decode(reader, reader.uint32());
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.NDArrayView.prototype.data_type = 0;
CNTK.proto.NDArrayView.prototype.storage_format = 0;
CNTK.proto.NDArrayView.prototype.shape = null;
CNTK.proto.NDArrayView.DataType = {
"Unknown": 0,
"Float": 1,
"Double": 2,
"Float16": 4,
"Int8": 5,
"Int16": 6
};
CNTK.proto.NDArrayView.StorageFormat = {
"Dense": 0,
"SparseCSC": 1,
"SparseBlockCol": 2
};
CNTK.proto.NDArrayView.FloatValues = class FloatValues {
constructor() {
this.value = [];
}
static decode(reader, length) {
const message = new CNTK.proto.NDArrayView.FloatValues();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.value = reader.floats(message.value, tag);
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.NDArrayView.DoubleValues = class DoubleValues {
constructor() {
this.value = [];
}
static decode(reader, length) {
const message = new CNTK.proto.NDArrayView.DoubleValues();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.value = reader.doubles(message.value, tag);
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.NDArrayView.BytesValue = class BytesValue {
static decode(reader, length) {
const message = new CNTK.proto.NDArrayView.BytesValue();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.value = reader.bytes();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.NDArrayView.BytesValue.prototype.value = new Uint8Array([]);
CNTK.proto.NDArrayView.IntValues = class IntValues {
constructor() {
this.value = [];
}
static decode(reader, length) {
const message = new CNTK.proto.NDArrayView.IntValues();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.value = reader.array(message.value, () => reader.sint32(), tag);
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.Vector = class Vector {
constructor() {
this.value = [];
}
static decode(reader, length) {
const message = new CNTK.proto.Vector();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.value.push(CNTK.proto.DictionaryValue.decode(reader, reader.uint32()));
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.Dictionary = class Dictionary {
constructor() {
this.data = {};
}
static decode(reader, length) {
const message = new CNTK.proto.Dictionary();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.version = reader.uint64();
break;
case 2:
reader.entry(message.data, () => reader.string(), () => CNTK.proto.DictionaryValue.decode(reader, reader.uint32()));
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.Dictionary.prototype.version = 0n;
CNTK.proto.DictionaryValue = class DictionaryValue {
get value() {
CNTK.proto.DictionaryValue.valueSet = CNTK.proto.DictionaryValue.valueSet || new Set(["bool_value", "int_value", "size_t_value", "float_value", "double_value", "string_value", "nd_shape_value", "axis_value", "vector_value", "dictionary_value", "nd_array_view_value"]);
return Object.keys(this).find((key) => CNTK.proto.DictionaryValue.valueSet.has(key) && this[key] !== null);
}
static decode(reader, length) {
const message = new CNTK.proto.DictionaryValue();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.version = reader.uint64();
break;
case 2:
message.value_type = reader.int32();
break;
case 3:
message.bool_value = reader.bool();
break;
case 4:
message.int_value = reader.int32();
break;
case 5:
message.size_t_value = reader.uint64();
break;
case 6:
message.float_value = reader.float();
break;
case 7:
message.double_value = reader.double();
break;
case 8:
message.string_value = reader.string();
break;
case 9:
message.nd_shape_value = CNTK.proto.NDShape.decode(reader, reader.uint32());
break;
case 10:
message.axis_value = CNTK.proto.Axis.decode(reader, reader.uint32());
break;
case 11:
message.vector_value = CNTK.proto.Vector.decode(reader, reader.uint32());
break;
case 12:
message.dictionary_value = CNTK.proto.Dictionary.decode(reader, reader.uint32());
break;
case 13:
message.nd_array_view_value = CNTK.proto.NDArrayView.decode(reader, reader.uint32());
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
CNTK.proto.DictionaryValue.prototype.version = 0n;
CNTK.proto.DictionaryValue.prototype.value_type = 0;
CNTK.proto.DictionaryValue.Type = {
"None": 0,
"Bool": 1,
"Int": 2,
"SizeT": 3,
"Float": 4,
"Double": 5,
"String": 6,
"NDShape": 7,
"Axis": 8,
"Vector": 9,
"Dictionary": 10,
"NDArrayView": 11
};
+1129
View File
File diff suppressed because it is too large Load Diff
+497
View File
@@ -0,0 +1,497 @@
[
{
"name": "activation",
"category": "Activation",
"description": "Applies specified type of activation function to input."
},
{
"name": "add",
"description": "A layer that performs elementwise addition.",
"inputs": [
{ "name": "x" },
{ "name": "y" }
],
"outputs": [
{ "name": "z" }
]
},
{
"name": "average",
"description": "A layer that computes the elementwise average of the inputs."
},
{
"name": "batchnorm",
"category": "Normalization",
"description": "A layer that performs batch normalization, which is performed along the channel axis, and repeated along the other axes, if present.",
"attributes": [
{ "name": "epsilon", "default": 0.000009999999747378752 },
{ "name": "computeMeanVar", "visible": false },
{ "name": "instanceNormalization", "visible": false }
]
},
{
"name": "bias",
"category": "Layer",
"description": "A layer that performs elementwise addition of a bias, which is broadcasted to match the input shape."
},
{
"name": "biDirectionalLSTM",
"category": "Layer",
"description": "Bidirectional long short-term memory (LSTM) layer. The first LSTM operates on the input sequence in the forward direction. The second LSTM operates on the input sequence in the reverse direction.",
"inputs": [
{ "name": "input" },
{ "name": "h" },
{ "name": "c" },
{ "name": "h_rev" },
{ "name": "c_rev" },
{ "name": "inputGateWeightMatrix", "visible": false },
{ "name": "forgetGateWeightMatrix", "visible": false },
{ "name": "blockInputWeightMatrix", "visible": false },
{ "name": "outputGateWeightMatrix", "visible": false },
{ "name": "inputGateRecursionMatrix", "visible": false },
{ "name": "forgetGateRecursionMatrix", "visible": false },
{ "name": "blockInputRecursionMatrix", "visible": false },
{ "name": "outputGateRecursionMatrix", "visible": false },
{ "name": "inputGateBiasVector", "visible": false },
{ "name": "forgetGateBiasVector", "visible": false },
{ "name": "blockInputBiasVector", "visible": false },
{ "name": "outputGateBiasVector", "visible": false },
{ "name": "inputGateWeightMatrix_rev", "visible": false },
{ "name": "forgetGateWeightMatrix_rev", "visible": false },
{ "name": "blockInputWeightMatrix_rev", "visible": false },
{ "name": "outputGateWeightMatrix_rev", "visible": false },
{ "name": "inputGateRecursionMatrix_rev", "visible": false },
{ "name": "forgetGateRecursionMatrix_rev", "visible": false },
{ "name": "blockInputRecursionMatrix_rev", "visible": false },
{ "name": "outputGateRecursionMatrix_rev", "visible": false },
{ "name": "inputGateBiasVector_rev", "visible": false },
{ "name": "forgetGateBiasVector_rev", "visible": false },
{ "name": "blockInputBiasVector_rev", "visible": false },
{ "name": "outputGateBiasVector_rev", "visible": false }
],
"outputs": [
{ "name": "output" },
{ "name": "h" },
{ "name": "c" },
{ "name": "h_rev" },
{ "name": "c_rev" }
]
},
{
"name": "concat",
"category": "Tensor",
"description": "A layer that concatenates along the channel axis (default) or sequence axis.",
"inputs": [
{ "name": "inputs", "type": "Tensor[]" }
]
},
{
"name": "convolution",
"category": "Layer",
"description": "A layer that performs spatial convolution or deconvolution.",
"attributes": [
{ "name": "outputShape", "type": "uint64[]", "description": "Either None or a 2-tuple, specifying the output shape (output_height, output_width). Used only when is_deconv == True. When is_deconv == False, this parameter is ignored. If it is None, the output shape is calculated automatically using the border_mode. Kindly refer to NeuralNetwork.proto for details.", "visible": false },
{ "name": "outputChannels", "type": "uint64", "description": "The number of kernels. Same as ``C_out`` used in the layer description.", "visible": false },
{ "name": "kernelChannels", "type": "uint64", "description": "Channel dimension of the kernels. Must be equal to ``inputChannels / nGroups``, if isDeconvolution == False. Must be equal to ``inputChannels``, if isDeconvolution == True.", "visible": false },
{ "name": "nGroups", "type": "uint64", "description": "Group convolution, i.e. weight reuse along channel axis. Input and kernels are divided into g groups and convolution / deconvolution is applied within the groups independently. If not set or 0, it is set to the default value 1.", "default": 1 },
{ "name": "isDeconvolution", "type": "boolean", "description": "Flag to specify whether it is a deconvolution layer." },
{ "name": "valid", "type": "ValidPadding", "visible": false },
{ "name": "same", "type": "SamePadding", "visible": false },
{ "name": "dilationFactor", "type": "uint64[]", "default": [ 1, 1 ] },
{ "name": "stride", "type": "uint64[]", "default": [ 1, 1 ] },
{ "name": "kernelSize", "type": "uint64[]", "default": [ 3, 3 ] },
{ "name": "hasBias", "type": "boolean", "description": "Flag to specify whether a bias is to be added or not.", "visible": false }
]
},
{
"name": "crop",
"category": "Data",
"description": "A layer that crops the spatial dimensions of an input. If two inputs are provided, the shape of the second input is used as the reference shape.",
"inputs": [
{ "name": "x1" },
{ "name": "x2" }
],
"outputs": [
{ "name": "y" }
]
},
{
"name": "dot",
"description": "If true, inputs are normalized first, thereby computing the cosine similarity."
},
{
"name": "embedding",
"category": "Transform",
"description": "A layer that performs a matrix lookup and optionally adds a bias."
},
{
"name": "featureVectorizer",
"inputs": [
{ "name": "inputs", "type": "Tensor[]" }
]
},
{
"name": "flatten",
"category": "Shape",
"description": "A layer that flattens the input.",
"attributes": [
{ "name": "mode", "type": "FlattenLayerParams.FlattenOrder" }
]
},
{
"name": "gather",
"category": "Transform",
"description": "Gather layer that gathers elements from the first input, along a specified axis, at indices specified in the second input.",
"inputs": [
{ "name": "input", "type": "Tensor" },
{ "name": "indices", "type": "Tensor" }
]
},
{
"name": "gelu",
"category": "Activation",
"description": "Gaussian error linear unit activation.",
"attributes": [
{ "name": "mode", "type": "GeluLayerParams.GeluMode" }
]
},
{
"name": "gru",
"category": "Layer",
"description": "Gated-Recurrent Unit (GRU) Layer",
"inputs": [
{ "name": "input" },
{ "name": "h" },
{ "name": "updateGateWeightMatrix", "visible": false },
{ "name": "resetGateWeightMatrix", "visible": false },
{ "name": "outputGateWeightMatrix", "visible": false },
{ "name": "updateGateRecursionMatrix", "visible": false },
{ "name": "resetGateRecursionMatrix", "visible": false },
{ "name": "outputGateRecursionMatrix", "visible": false },
{ "name": "updateGateBiasVector", "visible": false },
{ "name": "resetGateBiasVector", "visible": false },
{ "name": "outputGateBiasVector", "visible": false }
],
"outputs": [
{ "name": "output" },
{ "name": "h" }
]
},
{
"name": "innerProduct",
"category": "Layer",
"description": "A layer that performs a matrix vector product. This is equivalent to a fully-connected, or dense layer.",
"attributes": [
{ "name": "inputChannels", "type": "uint64", "visible": false },
{ "name": "outputChannels", "type": "uint64", "visible": false },
{ "name": "hasBias", "type": "boolean", "visible": false }
]
},
{
"name": "int64ClassLabels",
"category": "Data",
"outputs": [
{ "name": "probabilities" },
{ "name": "feature" }
]
},
{
"name": "itemSimilarityRecommender",
"inputs": [
{ "name": "item" },
{ "name": "numRecommendations" },
{ "name": "itemRestriction" },
{ "name": "itemExclusion" }
],
"outputs": [
{ "name": "recommendedItemList" },
{ "name": "recommendedItemScore" }
]
},
{
"name": "l2normalize",
"category": "Normalization",
"description": "A layer that performs L2 normalization, i.e. divides by the the square root of the sum of squares of all elements of input."
},
{
"name": "loadConstant",
"category": "Data"
},
{
"name": "lrn",
"category": "Normalization",
"description": "A layer that performs local response normalization (LRN).",
"attributes": [
{ "name": "k", "default": 1 }
]
},
{
"name": "max",
"description": "A layer that computes the elementwise maximum over the inputs."
},
{
"name": "min",
"description": "A layer that computes the elementwise minimum over the inputs."
},
{
"name": "multiply",
"description": "A layer that performs elementwise multiplication.",
"inputs": [
{ "name": "x" },
{ "name": "y" }
],
"outputs": [
{ "name": "z" }
]
},
{
"name": "mvn",
"category": "Normalization",
"description": "A layer that performs mean variance normalization, along axis = -3."
},
{
"name": "nonMaximumSuppression",
"attributes": [
{ "name": "iouThreshold" },
{ "name": "confidenceThreshold" }
],
"inputs": [
{ "name": "confidence" },
{ "name": "coordinates" },
{ "name": "iouThreshold" },
{ "name": "confidenceThreshold" }
],
"outputs": [
{ "name": "confidence" },
{ "name": "coordinates" }
]
},
{
"name": "padding",
"category": "Shape",
"description": "Fill a constant value in the padded region.",
"attributes": [
{ "name": "paddingAmounts", "visible": false }
]
},
{
"name": "permute",
"category": "Shape",
"description": "A layer that rearranges the dimensions and data of an input."
},
{
"name": "pooling",
"category": "Pool",
"description": "Spatial Pooling layer to reduce dimensions of input using the specified kernel size and type.",
"attributes": [
{ "name": "includeLastPixel", "type": "ValidCompletePadding", "visible": false },
{ "name": "same", "type": "SamePadding", "visible": false },
{ "name": "valid", "type": "ValidCompletePadding", "visible": false },
{ "name": "type", "type": "PoolingLayerParams.PoolingType" },
{ "name": "globalPooling", "type": "boolean", "default": false },
{ "name": "stride", "type": "uint64", "default": [ 1, 1 ] },
{ "name": "kernelSize", "type": "uint64[]", "default": [ 3, 3 ] },
{ "name": "avgPoolExcludePadding", "type": "boolean", "default": false }
]
},
{
"name": "reduce",
"description": "A layer that reduces the input using a specified operation."
},
{
"name": "reorganizeData",
"category": "Shape",
"description": "A layer that reorganizes data in the input in: 1. SPACE_TO_DEPTH, 2. DEPTH_TO_SPACE."
},
{
"name": "reshape",
"category": "Shape",
"description": "A layer that recasts the input into a new shape."
},
{
"name": "scale",
"category": "Layer",
"description": "A layer that performs elmentwise multiplication by a scale factor and optionally adds a bias.",
"attributes": [
{ "name": "hasBias", "type": "boolean", "visible": false }
]
},
{
"name": "scaler",
"category": "Data"
},
{
"name": "sequenceRepeat",
"category": "Shape",
"description": "A layer that repeats a sequence."
},
{
"name": "slice",
"description": "A layer that uniformly splits across the channel dimension to produce a specified number of outputs."
},
{
"name": "softmax",
"category": "Activation",
"description": "A layer that performs softmax normalization. Normalization is done along the channel axis."
},
{
"name": "softmaxND",
"category": "Activation",
"description": "A layer that performs softmax normalization along a specified axis."
},
{
"name": "squeeze",
"category": "Transform"
},
{
"name": "stringClassLabels",
"category": "Data",
"outputs": [
{ "name": "probabilities" },
{ "name": "feature" }
]
},
{
"name": "textClassifier",
"attributes": [
{ "name": "revision", "visible": false }
]
},
{
"name": "unary",
"description": "A layer that applies a unary function.",
"attributes": [
{ "name": "type", "type": "UnaryFunctionLayerParams.Operation" },
{ "name": "alpha", "default": 1 },
{ "name": "scale", "default": 1 },
{ "name": "epsilon", "default": 9.999999974752427e-7 }
],
"inputs": [
{ "name": "x" }
],
"outputs": [
{ "name": "z" }
]
},
{
"name": "uniDirectionalLSTM",
"category": "Layer",
"description": "A unidirectional long short-term memory (LSTM) layer.",
"inputs": [
{ "name": "input" },
{ "name": "h" },
{ "name": "c" },
{ "name": "inputGateWeightMatrix", "visible": false },
{ "name": "forgetGateWeightMatrix", "visible": false },
{ "name": "blockInputWeightMatrix", "visible": false },
{ "name": "outputGateWeightMatrix", "visible": false },
{ "name": "inputGateRecursionMatrix", "visible": false },
{ "name": "forgetGateRecursionMatrix", "visible": false },
{ "name": "blockInputRecursionMatrix", "visible": false },
{ "name": "outputGateRecursionMatrix", "visible": false },
{ "name": "inputGateBiasVector", "visible": false },
{ "name": "forgetGateBiasVector", "visible": false },
{ "name": "blockInputBiasVector", "visible": false },
{ "name": "outputGateBiasVector", "visible": false }
],
"outputs": [
{ "name": "output" },
{ "name": "h" },
{ "name": "c" }
]
},
{
"name": "upsample",
"category": "Data",
"description": "A layer that scales up spatial dimensions. It supports two modes: nearest neighbour (default) and bilinear."
},
{
"name": "transpose",
"category": "Transform"
},
{
"name": "wordTagger",
"attributes": [
{ "name": "revision", "visible": false }
],
"outputs": [
{ "name": "tokens" },
{ "name": "tags" },
{ "name": "locations" },
{ "name": "lengths" }
]
},
{
"name": "program:conv",
"category": "Layer",
"inputs": [
{ "name": "x" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "program:batch_norm",
"category": "Normalization",
"inputs": [
{ "name": "x" },
{ "name": "mean" },
{ "name": "variance" },
{ "name": "gamma" },
{ "name": "beta" }
]
},
{
"name": "program:linear",
"category": "Layer",
"inputs": [
{ "name": "x" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "program:pad",
"category": "Tensor"
},
{
"name": "program:transpose",
"category": "Transform"
},
{
"name": "program:sigmoid",
"category": "Activation"
},
{
"name": "program:softmax",
"category": "Activation"
},
{
"name": "program:relu",
"category": "Activation"
},
{
"name": "program:relu6",
"category": "Activation"
},
{
"name": "program:reshape",
"category": "Shape"
},
{
"name": "program:concat",
"category": "Tensor"
},
{
"name": "program:layer_norm",
"category": "Normalization"
},
{
"name": "program:max_pool",
"category": "Pool"
},
{
"name": "program:gather",
"category": "Transform"
}
]
+18441
View File
File diff suppressed because it is too large Load Diff
+1569
View File
File diff suppressed because it is too large Load Diff
+2450
View File
File diff suppressed because it is too large Load Diff
+480
View File
@@ -0,0 +1,480 @@
[
{
"name": "avgpool",
"category": "Pool"
},
{
"name": "batchnorm",
"category": "Normalization"
},
{
"name": "connected",
"category": "Layer",
"attributes": [
{ "name": "output", "type": "int32", "visible": false, "default": 1 },
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" },
{ "name": "batch_normalize", "type": "int32", "default": 0 }
]
},
{
"name": "conv_lstm",
"category": "Layer",
"attributes": [
{ "name": "batch_normalize", "type": "int32", "default": 0 },
{ "name": "activation", "type": "string", "default": "linear", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" },
{ "name": "size", "type": "int32", "default": 3 },
{ "name": "stride", "type": "int32", "default": 1 },
{ "name": "dilation", "default": 1 },
{ "name": "groups", "type": "int32", "default": 1 },
{ "name": "padding", "type": "int32", "default": 0 },
{ "name": "pad", "type": "int32", "default": 0 },
{ "name": "xnor", "type": "int32", "default": 0 },
{ "name": "shortcut", "default": 0 },
{ "name": "output", "type": "int32", "default": 1 },
{ "name": "state_constrain", "type": "int32", "default": 16 },
{ "name": "peephole", "type": "int32", "default": 0 }
]
},
{
"name": "convolutional",
"category": "Layer",
"attributes": [
{ "name": "filters", "type": "int32", "default": 1 },
{ "name": "size", "type": "int32", "default": 1 },
{ "name": "stride", "type": "int32", "default": 1 },
{ "name": "stride_x", "type": "int32", "default": -1 },
{ "name": "stride_y", "type": "int32", "default": -1 },
{ "name": "groups", "type": "int32", "default": 1 },
{ "name": "padding", "type": "int32", "default": 0 },
{ "name": "pad", "type": "int32", "default": 0 },
{ "name": "dilation", "default": 1 },
{ "name": "share_index", "default": -1000000000 },
{ "name": "binary", "type": "int32", "default": 0 },
{ "name": "xnor", "type": "int32", "default": 0 },
{ "name": "bin_output", "type": "int32", "default": 0 },
{ "name": "flipped", "type": "int32", "default": 0 },
{ "name": "dot", "type": "float32", "default": 0 },
{ "name": "batch_normalize", "type": "int32", "default": 0 },
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" }
]
},
{
"name": "cost",
"attributes": [
{ "name": "type", "type": "string", "default": "sse" },
{ "name": "scale", "type": "float32", "default": 1 },
{ "name": "ratio", "type": "float32", "default": 0 }
]
},
{
"name": "crnn",
"category": "Layer",
"attributes": [
{ "name": "batch_normalize", "type": "int32", "default": 0 },
{ "name": "activation", "type": "string", "default": "logistic" },
{ "name": "dilation", "default": 1 },
{ "name": "padding", "default": 0 },
{ "name": "pad", "type": "int32", "default": 0 },
{ "name": "groups", "type": "int32", "default": 1 },
{ "name": "xnor", "type": "int32", "default": 0 },
{ "name": "shortcut", "type": "int32", "default": 0 },
{ "name": "output_filters", "default": 1 },
{ "name": "hidden_filters", "default": 1 }
]
},
{
"name": "crop",
"category": "Shape",
"attributes": [
{ "name": "crop_height", "type": "int32", "default": 1 },
{ "name": "crop_width", "type": "int32", "default": 1 },
{ "name": "flip", "type": "int32", "default": 0 },
{ "name": "exposure", "type": "float32", "default": 1 },
{ "name": "saturation", "type": "float32", "default": 1 },
{ "name": "angle", "type": "float32", "default": 0 },
{ "name": "noadjust", "default": 0 },
{ "name": "shift", "type": "float32", "default": 0 }
]
},
{
"name": "deconvolutional",
"category": "Layer",
"attributes": [
{ "name": "filters", "type": "int32", "visible": false, "default": 1 },
{ "name": "size", "type": "int32", "default": 1 },
{ "name": "stride", "type": "int32", "default": 1 },
{ "name": "padding", "type": "int32", "default": 0 },
{ "name": "pad", "type": "int32", "default": 0 },
{ "name": "batch_normalize", "type": "int32", "default": 0 },
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" }
]
},
{
"name": "detection",
"category": "Layer",
"attributes": [
{ "name": "classes", "type": "int32", "default": 1 },
{ "name": "coord", "type": "int32", "default": 1 },
{ "name": "num", "type": "int32", "default": 1 },
{ "name": "jitter", "type": "float32", "default": 0.2 },
{ "name": "coord_scale", "type": "float32", "default": 1 },
{ "name": "object_scale", "type": "float32", "default": 1 },
{ "name": "noobject_scale", "type": "float32", "default": 1 },
{ "name": "class_scale", "type": "float32", "default": 1 },
{ "name": "forced", "type": "int32", "default": 0 },
{ "name": "side", "type": "int32", "default": 7 },
{ "name": "softmax", "type": "int32", "default": 0 },
{ "name": "sqrt", "type": "int32", "default": 0 },
{ "name": "max", "type": "int32", "default": 30 },
{ "name": "rescore", "type": "int32", "default": 0 },
{ "name": "random", "type": "int32", "default": 0 },
{ "name": "reorg", "type": "int32", "default": 0 }
]
},
{
"name": "dropout",
"category": "Dropout",
"attributes": [
{ "name": "probability", "type": "float32", "default": 0.5 },
{ "name": "dropblock", "type": "int32", "default": 0 },
{ "name": "dropblock_size_rel", "type": "float32", "default": 0 },
{ "name": "dropblock_size_abs ", "type": "int32", "default": 7 }
]
},
{
"name": "elu",
"category": "Activation"
},
{
"name": "gaussian_yolo",
"category": "Layer",
"attributes": [
{ "name": "classes", "type": "int32", "default": 20 },
{ "name": "num", "type": "int32", "default": 1 },
{ "name": "mask", "type": "string", "default": 0 },
{ "name": "jitter", "type": "float32", "default": 0.2 },
{ "name": "label_smooth_eps", "type": "float32", "default": 0 },
{ "name": "scale_x_y", "type": "float32", "default": 1 },
{ "name": "uc_normalizer", "type": "float32", "default": 1 },
{ "name": "iou_normalizer", "type": "float32", "default": 0.75 },
{ "name": "cls_normalizer", "type": "float32", "default": 1 },
{ "name": "iou_loss", "type": "string", "default": "mse", "description": "options are: mse, giou, diou, and ciou" },
{ "name": "max", "default": 90 },
{ "name": "ignore_thresh", "type": "float32", "default": 0.5 },
{ "name": "truth_thresh", "default": 1 },
{ "name": "iou_thresh", "type": "float32", "default": 1, "description": "recommended to use iou_thresh=0.213" },
{ "name": "random", "type": "int32", "default": 0 },
{ "name": "map", "type": "string", "default": 0 },
{ "name": "beta_nms", "type": "float32", "default": 0.6 },
{ "name": "nms_kind", "type": "string", "default": "default", "description": "options are: greedynms, diounms, cornersnms, or defaultnms" },
{ "name": "anchors", "type": "string", "default": 0 },
{ "name": "yolo_point", "type": "string", "default": "center", "description": "options are: center, left_top, and right_bottom" }
]
},
{
"name": "gru",
"category": "Layer",
"attributes": [
{ "name": "output", "type": "int32", "visible": false, "default": 1 },
{ "name": "batch_normalize", "type": "int32", "default": 0 }
]
},
{
"name": "h_swish",
"category": "Activation"
},
{
"name": "hardtan",
"category": "Activation"
},
{
"name": "leaky",
"category": "Activation"
},
{
"name": "lhtan",
"category": "Activation"
},
{
"name": "linear",
"category": "Activation"
},
{
"name": "local",
"category": "Layer",
"attributes": [
{ "name": "filters", "type": "int32", "visible": false, "default": 1 },
{ "name": "size", "type": "int32", "default": 1 },
{ "name": "stride", "type": "int32", "default": 1 },
{ "name": "padding", "type": "int32", "default": 0 },
{ "name": "pad", "type": "int32", "default": 0 },
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" }
]
},
{
"name": "loggy",
"category": "Activation"
},
{
"name": "logistic",
"category": "Activation"
},
{
"name": "lstm",
"category": "Layer",
"attributes": [
{ "name": "output", "type": "int32", "visible": false, "default": 1 },
{ "name": "batch_normalize", "type": "int32", "default": 0 }
]
},
{
"name": "maxpool",
"category": "Pool",
"attributes": [
{ "name": "stride", "type": "int32", "default": 1 },
{ "name": "stride_x", "type": "int32", "default": 1 },
{ "name": "stride_y", "type": "int32", "default": 1 },
{ "name": "size", "type": "int32", "default": 1 },
{ "name": "padding", "type": "int32", "default": 0 },
{ "name": "maxpool_depth", "type": "int32", "default": 0 },
{ "name": "out_channels", "default": 1 },
{ "name": "antialiasing", "type": "int32", "default": 0 }
]
},
{
"name": "mish",
"category": "Activation"
},
{
"name": "net",
"attributes": [
{ "name": "batch", "type": "int32", "default": 1 },
{ "name": "max_batches", "type": "int32", "default": 0, "description": "Limits the maximum number of iterations" },
{ "name": "learning_rate", "type": "float32", "default": 0.001 },
{ "name": "momentum", "type": "float32", "default": 0.9 },
{ "name": "decay", "type": "float32", "default": 0.0001 },
{ "name": "subdivisions", "type": "int32", "default": 1, "description": "In concert with batch property, this greatly affect memory usage, minimal working number is recommended" },
{ "name": "time_steps", "type": "int32", "default": 1 },
{ "name": "notruth", "type": "int32", "default": 0 },
{ "name": "random", "type": "int32", "default": 0 },
{ "name": "adam", "type": "int32", "default": 0 },
{ "name": "B1", "type": "float32", "default": 0.9 },
{ "name": "B2", "type": "float32", "default": 0.999 },
{ "name": "eps", "type": "float32", "default": 1e-7 },
{ "name": "height", "type": "int32", "default": 0 },
{ "name": "width", "type": "int32", "default": 0 },
{ "name": "channels", "type": "int32", "default": 0 },
{ "name": "inputs", "type": "int32" },
{ "name": "max_crop", "type": "int32" },
{ "name": "min_crop", "type": "int32" },
{ "name": "max_ratio", "type": "float32" },
{ "name": "min_ratio", "type": "float32" },
{ "name": "center", "type": "int32", "default": 0 },
{ "name": "clip", "type": "int32", "default": 0 },
{ "name": "angle", "type": "float32", "default": 0 },
{ "name": "aspect", "type": "float32", "default": 1 },
{ "name": "saturation", "type": "float32", "default": 1 },
{ "name": "exposure", "type": "float32", "default": 1 },
{ "name": "hue", "type": "float32", "default": 0 },
{ "name": "power", "type": "float32", "default": 4 },
{ "name": "flip", "type": "int32", "default": 1, "description": "Enables augmentation method: horizontal flip" },
{ "name": "blur", "type": "int32", "default": 0, "description": "Enables augmentation method: backgound blurring" },
{ "name": "mixup", "type": "int32", "default": 0, "description": "Enables augmentation method: images mixup" },
{ "name": "cutmix", "type": "int32", "default": 0, "description": "Enables augmentation method: images cutmix" },
{ "name": "mosaic", "type": "int32", "default": 0, "description": "Enables augmentation method: images mosaicing" },
{ "name": "letter_box", "type": "int32", "default": 0, "description": "Enables letter-box resizing (keeping the aspect ratio)" },
{ "name": "policy", "type": "string", "default": "constant" },
{ "name": "burn_in", "type": "int32", "default": 0, "description": "Is used for MAP calculation: permit a minimal number of iteration before first MAP check" },
{ "name": "letter_box", "type": "int32", "default": 0 },
{ "name": "optimized_memory", "type": "int32", "default": 0, "description": "can offload memory from GPU into CPU at the cost of speed, 3 options are possible please look at: https://github.com/AlexeyAB/darknet/issues/4386" },
{ "name": "workspace_size_limit_MB", "type": "float32", "default": 1024 }
]
},
{
"name": "norm_chan",
"category": "Activation"
},
{
"name": "norm_chan_softmax",
"category": "Activation"
},
{
"name": "normalization",
"category": "Normalization",
"attributes": [
{ "name": "alpha", "type": "float32", "default": 0.0001 },
{ "name": "beta", "type": "float32", "default": 0.75 },
{ "name": "kappa", "type": "float32", "default": 1 },
{ "name": "size", "default": 5 }
]
},
{
"name": "plse",
"category": "Activation"
},
{
"name": "ramp",
"category": "Activation"
},
{
"name": "region",
"category": "Layer",
"attributes": [
{ "name": "classes", "type": "int32", "default": 20 },
{ "name": "coord", "default": 4 },
{ "name": "num", "default": 1 },
{ "name": "mask", "type": "string", "default": 0 },
{ "name": "jitter", "type": "float32", "default": 0.2 },
{ "name": "classfix", "type": "int32", "default": 0 },
{ "name": "coord_scale", "type": "float32", "default": 1 },
{ "name": "object_scale", "type": "float32", "default": 1 },
{ "name": "noobject_scale", "type": "float32", "default": 1 },
{ "name": "mask_scale", "type": "float32", "default": 1 },
{ "name": "class_scale", "type": "float32", "default": 1 },
{ "name": "bias_match", "type": "int32", "default": 0 },
{ "name": "focal_loss", "type": "int32", "default": 0 },
{ "name": "max", "type": "int32", "default": 90 },
{ "name": "softmax", "type": "int32", "default": 0 },
{ "name": "rescore", "type": "int32", "default": 0 },
{ "name": "thresh", "type": "float32", "default": 0.5 },
{ "name": "random", "type": "int32", "default": 0 },
{ "name": "map", "type": "string", "default": 0 },
{ "name": "tree", "type": "string", "default": 0 },
{ "name": "anchors", "type": "string", "default": 0 },
{ "name": "absolute", "default": 0 },
{ "name": "log", "default": 0 },
{ "name": "sqrt", "default": 0 }
]
},
{
"name": "relie",
"category": "Activation"
},
{
"name": "relu",
"category": "Activation"
},
{
"name": "relu6",
"category": "Activation"
},
{
"name": "reorg",
"category": "Shape",
"attributes": [
{ "name": "stride", "default": 1 },
{ "name": "reverse", "type": "int32", "default": 0 }
]
},
{
"name": "rnn",
"category": "Layer",
"attributes": [
{ "name": "output", "type": "int32", "visible": false },
{ "name": "hidden", "visible": false, "default": 1 },
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" },
{ "name": "groups", "type": "int32", "default": 1 },
{ "name": "xnor", "type": "int32", "default": 0 },
{ "name": "shortcut", "default": 0 },
{ "name": "logistic", "default": 0 },
{ "name": "batch_normalize", "type": "int32", "default": 0 }
]
},
{
"name": "route",
"category": "Tensor",
"attributes": [
{ "name": "groups_id", "type": "int32", "default": 0 },
{ "name": "groups", "type": "int32", "default": 1 }
]
},
{
"name": "sam",
"category": "Tensor",
"attributes": [
{ "name": "activation", "type": "string", "default": "linear", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU" },
{ "name": "from", "description": "This params link the layer to another one, the index of the layer is either positive in which case it's a direct address, if negative it's relative to the layer position" }
]
},
{
"name": "scale_channels",
"category": "Tensor",
"attributes": [
{ "name": "activation", "type": "string", "default": "linear", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU" },
{ "name": "scale_wh", "default": 0 },
{ "name": "from", "description": "This params link the layer to another one, the index of the layer is either positive in which case it's a direct address, if negative it's relative to the layer position" }
]
},
{
"name": "selu",
"category": "Activation"
},
{
"name": "silu",
"category": "Activation"
},
{
"name": "shortcut",
"category": "Tensor",
"attributes": [
{ "name": "activation", "type": "string", "default": "logistic", "description": "options are: LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, SWISH, MISH" },
{ "name": "assisted_excitation", "default": 0 },
{ "name": "from", "description": "This params link the layer to another one, the index of the layer is either positive in which case it's a direct address, if negative it's relative to the layer position" }
]
},
{
"name": "softmax",
"category": "Activation",
"attributes": [
{ "name": "groups", "type": "int32", "default": 1 },
{ "name": "temperature", "type": "float32", "default": 1 },
{ "name": "tree", "type": "string", "default": 0 },
{ "name": "spatial", "type": "int32", "default": 0 },
{ "name": "noloss", "type": "int32", "default": 0 }
]
},
{
"name": "stair",
"category": "Activation"
},
{
"name": "swish",
"category": "Activation"
},
{
"name": "tanh",
"category": "Activation"
},
{
"name": "upsample",
"category": "Data",
"attributes": [
{ "name": "stride", "type": "int32", "default": 2 },
{ "name": "scale", "type": "float32", "default": 1 }
]
},
{
"name": "yolo",
"category": "Layer",
"attributes": [
{ "name": "classes", "type": "int32", "default": 20 },
{ "name": "num", "type": "int32", "default": 1 },
{ "name": "mask", "type": "int32[]", "default": 0 },
{ "name": "jitter", "type": "float32", "default": 0.2 },
{ "name": "label_smooth_eps", "type": "float32", "default": 0 },
{ "name": "scale_x_y", "type": "float32", "default": 1 },
{ "name": "iou_normalizer", "type": "float32", "default": 0.75 },
{ "name": "cls_normalizer", "type": "float32", "default": 1 },
{ "name": "iou_loss", "type": "string", "default": "mse", "description": "options are: mse, giou, diou, and ciou" },
{ "name": "focal_loss", "type": "int32", "default": 0 },
{ "name": "max", "type": "int32", "default": 90 },
{ "name": "ignore_thresh", "type": "float32", "default": 0.5 },
{ "name": "truth_thresh", "type": "float32", "default": 1 },
{ "name": "iou_thresh", "type": "float32", "default": 1, "description": "recommended to use iou_thresh=0.213" },
{ "name": "random", "type": "int32", "default": 0 },
{ "name": "map", "type": "string", "default": 0 },
{ "name": "nms_kind", "type": "string", "default": "default", "description": "options are: greedynms, diounms, cornersnms, or defaultnms" },
{ "name": "anchors", "type": "int32[]", "default": 0 }
]
}
]
+1021
View File
File diff suppressed because it is too large Load Diff
+681
View File
@@ -0,0 +1,681 @@
import * as base from './base.js';
import * as child_process from 'child_process';
import * as electron from 'electron';
import * as fs from 'fs';
import * as http from 'http';
import * as https from 'https';
import * as node from './node.js';
import * as os from 'os';
import * as path from 'path';
import * as url from 'url';
import * as view from './view.js';
const desktop = {};
desktop.Host = class {
constructor() {
this._document = window.document;
this._window = window;
this._global = global;
this._telemetry = new base.Telemetry(this._window);
process.on('uncaughtException', (error) => {
this.exception(error, true);
this.message(error.message);
});
this._global.eval = () => {
throw new Error('eval.eval() not supported.');
};
this._window.eval = () => {
throw new Error('window.eval() not supported.');
};
this._window.addEventListener('unload', () => {
if (typeof __coverage__ !== 'undefined') {
const dir = path.join(process.cwd(), 'dist', 'nyc', '.nyc_output');
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const base = path.basename(window.location.pathname, '.html');
const file = path.join(dir, `${base}.json`);
/* eslint-disable-next-line no-undef */
fs.writeFileSync(file, JSON.stringify(__coverage__));
}
});
this._environment = electron.ipcRenderer.sendSync('get-environment', {});
this._environment.menu = this._environment.titlebar && this._environment.platform !== 'darwin';
this._files = [];
electron.ipcRenderer.on('open', (sender, data) => {
this._open(data);
});
this._element('menu-button').style.opacity = 0;
if (!/^\d+\.\d+\.\d+$/.test(this.version)) {
throw new Error('Invalid version.');
}
const metadata = [];
metadata.push(os.arch());
let packager = '';
if (process.platform === 'linux') {
try {
child_process.execFileSync('dpkg', ['-S', process.execPath]);
packager = 'deb';
} catch {
try {
child_process.execFileSync("rpm", ["-qf", process.execPath]);
packager = 'rpm';
} catch {
// continue regardless of error
}
}
}
metadata.push(packager);
this._metadata = metadata.join('|');
}
get window() {
return this._window;
}
get document() {
return this._document;
}
get version() {
return this._environment.version;
}
get type() {
return 'Electron';
}
get metadata() {
return this._metadata;
}
async view(view) {
this._view = view;
const age = async () => {
const days = (new Date() - new Date(this._environment.date)) / (24 * 60 * 60 * 1000);
if (days > 180) {
this.document.body.classList.remove('spinner');
const link = this._element('logo-github').href;
for (;;) {
/* eslint-disable-next-line no-await-in-loop */
await this.message('Please update to the newest version.', null, 'Download');
this.openURL(link);
}
}
return Promise.resolve();
};
const consent = async () => {
const time = this.get('consent');
if (!time || (Date.now() - time) > 30 * 24 * 60 * 60 * 1000) {
let consent = true;
try {
const content = await this._request('https://ipinfo.io/json', { 'Content-Type': 'application/json' }, 2000);
const json = JSON.parse(content);
const countries = ['AT', 'BE', 'BG', 'HR', 'CZ', 'CY', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'SK', 'ES', 'SE', 'GB', 'UK', 'GR', 'EU', 'RO'];
if (json && json.country && countries.indexOf(json.country) === -1) {
consent = false;
}
} catch {
// continue regardless of error
}
if (consent) {
this.document.body.classList.remove('spinner');
await this.message('This app uses cookies to report errors and anonymous usage information.', null, 'Accept');
}
this.set('consent', Date.now());
}
};
const telemetry = async () => {
if (this._environment.packaged) {
const measurement_id = '848W2NVWVH';
const user = this.get('user') || null;
const session = this.get('session') || null;
await this._telemetry.start(`G-${measurement_id}`, user && user.indexOf('.') !== -1 ? user : null, session);
this._telemetry.send('page_view', {
app_name: this.type,
app_version: this.version,
app_metadata: this.metadata
});
this._telemetry.send('scroll', {
percent_scrolled: 90,
app_name: this.type,
app_version: this.version,
app_metadata: this.metadata
});
this.set('user', this._telemetry.get('client_id'));
this.set('session', this._telemetry.session);
}
};
await age();
await consent();
await telemetry();
}
async start() {
if (this._files) {
const files = this._files;
delete this._files;
if (files.length > 0) {
const data = files.pop();
this._open(data);
}
}
this._window.addEventListener('focus', () => {
this._document.body.classList.add('active');
});
this._window.addEventListener('blur', () => {
this._document.body.classList.remove('active');
});
if (this._document.hasFocus()) {
this._document.body.classList.add('active');
}
electron.ipcRenderer.on('recents', (sender, data) => {
this._view.recents(data);
});
electron.ipcRenderer.on('export', (sender, data) => {
this._view.export(data.file);
});
electron.ipcRenderer.on('cut', () => {
this.document.execCommand('cut');
});
electron.ipcRenderer.on('copy', () => {
this.document.execCommand('copy');
});
electron.ipcRenderer.on('paste', () => {
if (this.document.queryCommandSupported('paste')) {
this.document.execCommand('paste');
} else if (this.document.queryCommandSupported('insertText')) {
const content = electron.clipboard.readText();
this.document.execCommand('insertText', false, content);
}
});
electron.ipcRenderer.on('selectall', () => {
this.document.execCommand('selectall');
});
electron.ipcRenderer.on('toggle', (sender, name) => {
this._view.toggle(name);
this.update({ ...this._view.options });
});
electron.ipcRenderer.on('zoom-in', () => {
this._element('zoom-in-button').click();
});
electron.ipcRenderer.on('zoom-out', () => {
this._element('zoom-out-button').click();
});
electron.ipcRenderer.on('zoom-reset', () => {
this._view.resetZoom();
});
electron.ipcRenderer.on('show-properties', () => {
this._element('sidebar-target-button').click();
});
electron.ipcRenderer.on('find', () => {
this._view.find();
});
electron.ipcRenderer.on('about', () => {
this._view.about();
});
this._element('titlebar-close').addEventListener('click', () => {
electron.ipcRenderer.sendSync('window-close', {});
});
this._element('titlebar-toggle').addEventListener('click', () => {
electron.ipcRenderer.sendSync('window-toggle', {});
});
this._element('titlebar-minimize').addEventListener('click', () => {
electron.ipcRenderer.sendSync('window-minimize', {});
});
electron.ipcRenderer.on('window-state', (sender, data) => {
if (this._environment.titlebar) {
this._element('target').style.marginTop = '32px';
this._element('target').style.height = 'calc(100% - 32px)';
this._element('sidebar-title').style.marginTop = '24px';
this._element('sidebar-closebutton').style.marginTop = '24px';
this._element('titlebar').classList.add('titlebar-visible');
}
if (this._environment.titlebar && this._environment.platform !== 'darwin' && !data.fullscreen) {
this._element('titlebar-control-box').classList.add('titlebar-control-box-visible');
} else {
this._element('titlebar-control-box').classList.remove('titlebar-control-box-visible');
}
this._element('menu-button').style.opacity = this._environment.menu ? 1 : 0;
this._element('titlebar-maximize').style.opacity = data.maximized ? 0 : 1;
this._element('titlebar-restore').style.opacity = data.maximized ? 1 : 0;
this._element('titlebar-toggle').setAttribute('title', data.maximized ? 'Restore' : 'Maximize');
});
electron.ipcRenderer.sendSync('update-window-state', {});
const openFileButton = this._element('open-file-button');
if (openFileButton) {
openFileButton.addEventListener('click', async () => {
await this.execute('open');
});
}
this.document.addEventListener('dragover', (e) => {
e.preventDefault();
});
this.document.addEventListener('drop', (e) => {
e.preventDefault();
});
this.document.body.addEventListener('drop', (e) => {
e.preventDefault();
const files = Array.from(e.dataTransfer.files);
const paths = files.map((file) => electron.webUtils.getPathForFile(file));
if (paths.length > 0) {
electron.ipcRenderer.send('drop-paths', { paths });
}
return false;
});
this._view.show('welcome');
}
environment(name) {
return this._environment[name];
}
async error(message) {
await this.message(message, true, 'OK');
}
async require(id) {
return import(`${id}.js`);
}
worker(id) {
return new this.window.Worker(`${id}.js`, { type: 'module' });
}
async save(name, extension, defaultPath) {
return new Promise((resolve, reject) => {
electron.ipcRenderer.once('show-save-dialog-complete', (event, data) => {
if (data.error) {
reject(new Error(data.error));
} else if (data.canceled) {
resolve(null);
} else {
resolve(data.filePath);
}
});
electron.ipcRenderer.send('show-save-dialog', {
title: 'Export Tensor',
defaultPath,
buttonLabel: 'Export',
filters: [{ name, extensions: [extension] }]
});
});
}
async export(file, blob) {
const window = this.window;
const reader = new window.FileReader();
reader.onload = (e) => {
const data = new Uint8Array(e.target.result);
fs.writeFile(file, data, null, async (error) => {
if (error) {
await this._view.error(error, 'Error writing file.');
}
});
};
let error = null;
if (!blob) {
error = new Error(`Export blob is '${JSON.stringify(blob)}'.`);
} else if (blob instanceof window.Blob === false) {
error = new Error(`Export blob type is '${typeof blob}'.`);
}
if (error) {
await this._view.error(error, 'Error exporting image.');
} else {
reader.readAsArrayBuffer(blob);
}
}
async execute(name, value) {
return new Promise((resolve, reject) => {
electron.ipcRenderer.once('execute-complete', (event, data) => {
if (data.error) {
reject(new Error(data.error));
} else {
resolve(data.value);
}
});
electron.ipcRenderer.send('execute', { name, value });
});
}
async asset(file) {
return this.fetch(file, 'utf-8', null);
}
async fetch(file, encoding, basename) {
return new Promise((resolve, reject) => {
const dirname = path.dirname(url.fileURLToPath(import.meta.url));
const root = path.resolve(basename || dirname);
const pathname = path.resolve(root, file);
const relative = path.relative(root, pathname);
if (relative !== '' && (relative.startsWith('..') || path.isAbsolute(relative))) {
reject(new Error(`The path '${pathname}' is invalid.`));
return;
}
fs.stat(pathname, (err, stat) => {
if (err && err.code === 'ENOENT') {
reject(new Error(`The file '${file}' does not exist.`));
} else if (err) {
reject(err);
} else if (!stat.isFile()) {
reject(new Error(`The path '${file}' is not a file.`));
} else if (stat && stat.size < 0x40000000) {
fs.readFile(pathname, encoding, (err, data) => {
if (err) {
reject(err);
} else {
resolve(encoding ? data : new base.BinaryStream(data));
}
});
} else if (encoding) {
reject(new Error(`The file '${file}' size (${stat.size.toString()}) for encoding '${encoding}' is greater than 2 GB.`));
} else {
const stream = new node.FileStream(pathname, 0, stat.size, stat.mtimeMs);
resolve(stream);
}
});
});
}
openURL(url) {
electron.shell.openExternal(url);
}
exception(error, fatal) {
if (this._telemetry && error) {
try {
const name = error.name ? `${error.name}: ` : '';
const message = error.message ? error.message : JSON.stringify(error);
let context = '';
let stack = '';
if (error.stack) {
const format = (file, line, column) => {
return `${file.split('\\').join('/').split('/').pop()}:${line}:${column}`;
};
const match = error.stack.match(/\n {4}at (.*) \((.*):(\d*):(\d*)\)/);
if (match) {
stack = `${match[1]} (${format(match[2], match[3], match[4])})`;
} else {
const match = error.stack.match(/\n {4}at (.*):(\d*):(\d*)/);
if (match) {
stack = `(${format(match[1], match[2], match[3])})`;
} else {
const match = error.stack.match(/.*\n\s*(.*)\s*/);
if (match) {
[, stack] = match;
}
}
}
}
if (error.context) {
context = typeof error.context === 'string' ? error.context : JSON.stringify(error.context);
}
this._telemetry.send('exception', {
app_name: this.type,
app_version: this.version,
app_metadata: this.metadata,
error_name: name,
error_message: message,
error_context: context,
error_stack: stack,
error_fatal: fatal ? true : false
});
} catch {
// continue regardless of error
}
}
}
event(name, params) {
if (name && params) {
params.app_name = this.type;
params.app_version = this.version;
params.app_metadata = this.metadata;
this._telemetry.send(name, params);
}
}
async _context(location) {
const basename = path.basename(location);
const stat = fs.statSync(location);
if (stat.isFile()) {
const dirname = path.dirname(location);
const stream = await this.fetch(basename, null, dirname);
return new desktop.Context(this, dirname, basename, stream);
} else if (stat.isDirectory()) {
const entries = new Map();
const walk = (dir) => {
for (const item of fs.readdirSync(dir)) {
const pathname = path.join(dir, item);
const stat = fs.statSync(pathname);
if (stat.isDirectory()) {
walk(pathname);
} else if (stat.isFile()) {
const stream = new node.FileStream(pathname, 0, stat.size, stat.mtimeMs);
const name = pathname.split(path.sep).join(path.posix.sep);
entries.set(name, stream);
}
}
};
walk(location);
return new desktop.Context(this, location, basename, null, entries);
}
throw new Error(`Unsupported path stat '${JSON.stringify(stat)}'.`);
}
async _open(location) {
if (this._files) {
this._files.push(location);
return;
}
const path = location.path;
const stat = fs.existsSync(path) ? fs.statSync(path) : null;
const size = stat && stat.isFile() ? stat.size : 0;
if (path && this._view.accept(path, size)) {
this._view.show('welcome spinner');
let context = null;
try {
context = await this._context(path);
this._telemetry.set('session_engaged', 1);
} catch (error) {
await this._view.error(error, 'Error while reading file.');
this.update({ path: null });
return;
}
try {
const attachment = await this._view.attach(context);
if (attachment) {
this._view.show(null);
} else {
const model = await this._view.open(context);
this._view.show(null);
const options = { ...this._view.options };
if (model) {
options.path = path;
this._title(location.label);
} else {
options.path = path;
this._title('');
}
electron.ipcRenderer.send('update-recents', { path });
this.update(options);
}
} catch (error) {
const options = { ...this._view.options };
if (error) {
await this._view.error(error);
}
this.update(options);
}
}
}
_request(location, headers, timeout) {
const window = this.window;
return new Promise((resolve, reject) => {
const url = new window.URL(location);
const protocol = url.protocol === 'https:' ? https : http;
const options = {};
options.headers = headers;
if (timeout) {
options.timeout = timeout;
}
const request = protocol.request(location, options, (response) => {
if (response.statusCode === 200) {
let data = '';
response.on('data', (chunk) => {
data += chunk;
});
response.on('error', (err) => {
reject(err);
});
response.on('end', () => {
resolve(data);
});
} else {
const error = new Error(`The web request failed with status code '${response.statusCode}'.`);
error.context = location;
reject(error);
}
});
request.on("error", (err) => {
reject(err);
});
request.on("timeout", () => {
request.destroy();
const error = new Error('The web request timed out.');
error.context = url;
reject(error);
});
request.end();
});
}
get(name) {
try {
return electron.ipcRenderer.sendSync('get-configuration', { name });
} catch {
// continue regardless of error
}
return undefined;
}
set(name, value) {
try {
electron.ipcRenderer.sendSync('set-configuration', { name, value });
} catch {
// continue regardless of error
}
}
delete(name) {
try {
electron.ipcRenderer.sendSync('delete-configuration', { name });
} catch {
// continue regardless of error
}
}
_title(label) {
const element = this._element('titlebar-content-text');
if (element) {
element.innerHTML = '';
if (label) {
const path = label.split(this._environment.separator || '/');
for (let i = 0; i < path.length; i++) {
const span = this.document.createElement('span');
span.innerHTML = ` ${path[i]} ${i === path.length - 1 ? '' : '<svg class="titlebar-icon" aria-hidden="true"><use xlink:href="#icon-arrow-right"></use></svg>'}`;
element.appendChild(span);
}
}
}
}
_element(id) {
return this.document.getElementById(id);
}
update(data) {
electron.ipcRenderer.send('window-update', data);
}
async message(message, alert, action) {
return new Promise((resolve) => {
const type = this.document.body.getAttribute('class');
this._element('message-text').innerText = message || '';
const button = this._element('message-button');
if (action) {
button.style.removeProperty('display');
button.innerText = action;
button.onclick = () => {
button.onclick = null;
this.document.body.setAttribute('class', type);
resolve(0);
};
} else {
button.style.display = 'none';
button.onclick = null;
}
if (alert) {
this.document.body.setAttribute('class', 'alert');
} else {
this.document.body.classList.add('notification');
this.document.body.classList.remove('default');
}
if (action) {
button.focus();
}
});
}
};
desktop.Context = class {
constructor(host, folder, identifier, stream, entries) {
this._host = host;
this._folder = folder;
this._identifier = identifier;
this._stream = stream;
this._entries = entries || new Map();
}
get identifier() {
return this._identifier;
}
get stream() {
return this._stream;
}
get entries() {
return this._entries;
}
async asset(file) {
return this._host.asset(file);
}
async fetch(file, encoding, base) {
return this._host.fetch(file, encoding, base === undefined ? this._folder : base);
}
async require(id) {
return this._host.require(id);
}
error(error, fatal) {
this._host.exception(error, fatal);
}
};
if (typeof window !== 'undefined') {
window.addEventListener('load', () => {
const value = new desktop.Host();
window.__view__ = new view.View(value);
window.__view__.start();
});
}
+72
View File
@@ -0,0 +1,72 @@
[
{
"name": "BatchNormalization",
"category": "Normalization",
"attributes": [
{ "name": "eps" },
{ "name": "gamma" },
{ "name": "decay" }
]
},
{
"name": "Convolution",
"category": "Layer",
"attributes": [
{ "name": "dilation" },
{ "name": "kernelSize" },
{ "name": "padding" }
]
},
{
"name": "Dense",
"category": "Layer"
},
{
"name": "Dropout",
"category": "Dropout"
},
{
"name": "GlobalPooling",
"category": "Pool"
},
{
"name": "LReLU",
"category": "Activation"
},
{
"name": "Merge",
"category": "Tensor"
},
{
"name": "Output",
"category": "Layer"
},
{
"name": "ReLU",
"category": "Activation"
},
{
"name": "SeparableConvolution2D",
"category": "Layer"
},
{
"name": "Sigmoid",
"category": "Activation"
},
{
"name": "Softmax",
"category": "Activation"
},
{
"name": "Subsampling",
"category": "Layer"
},
{
"name": "TanH",
"category": "Activation"
},
{
"name": "Upsampling2D",
"category": "Layer"
}
]
+433
View File
@@ -0,0 +1,433 @@
// Experimental
const dl4j = {};
dl4j.ModelFactory = class {
async match(context) {
const identifier = context.identifier;
if (identifier === 'configuration.json') {
const obj = await context.peek('json');
if (obj && (obj.confs || obj.vertices)) {
return context.set('dl4j.configuration', obj);
}
} else if (identifier === 'coefficients.bin') {
const signature = [0x00, 0x07, 0x4A, 0x41, 0x56, 0x41, 0x43, 0x50, 0x50]; // JAVACPP
const stream = context.stream;
if (signature.length <= stream.length && stream.peek(signature.length).every((value, index) => value === signature[index])) {
return context.set('dl4j.coefficients');
}
}
return null;
}
filter(context, match) {
return context.type !== 'dl4j.configuration' || (match.type !== 'dl4j.coefficients' && match.type !== 'openvino.bin');
}
async open(context) {
const metadata = await context.metadata('dl4j-metadata.json');
switch (context.type) {
case 'dl4j.configuration': {
const obj = context.value;
try {
const content = await context.fetch('coefficients.bin');
const reader = await content.read('binary.big-endian');
return new dl4j.Model(metadata, obj, reader);
} catch {
return new dl4j.Model(metadata, obj, null);
}
}
case 'dl4j.coefficients': {
const content = await context.fetch('configuration.json');
const obj = await content.read('json');
const reader = await context.read('binary.big-endian');
return new dl4j.Model(metadata, obj, reader);
}
default: {
throw new dl4j.Error(`Unsupported Deeplearning4j format '${context.type}'.`);
}
}
}
};
dl4j.Model = class {
constructor(metadata, configuration, coefficients) {
this.format = 'Deeplearning4j';
this.modules = [new dl4j.Graph(metadata, configuration, coefficients)];
}
};
dl4j.Graph = class {
constructor(metadata, configuration, coefficients) {
this.inputs = [];
this.outputs = [];
this.nodes = [];
coefficients = coefficients ? new dl4j.NDArray(coefficients) : null;
const dataType = coefficients ? coefficients.dataType : '?';
const values = new Map();
values.map = (name, type, tensor) => {
if (name.length === 0 && tensor) {
return new dl4j.Value(name, type || null, tensor);
}
if (!values.has(name)) {
values.set(name, new dl4j.Value(name, type || null, tensor || null));
} else if (type || tensor) {
throw new dl4j.Error(`Duplicate value '${name}'.`);
}
return values.get(name);
};
if (configuration.networkInputs) {
for (const input of configuration.networkInputs) {
const value = values.map(input);
const argument = new dl4j.Argument(input, [value]);
this.inputs.push(argument);
}
}
if (configuration.networkOutputs) {
for (const output of configuration.networkOutputs) {
const value = values.map(output);
const argument = new dl4j.Argument(output, [value]);
this.outputs.push(argument);
}
}
let inputs = null;
// Computation Graph
if (configuration.vertices) {
for (const [name,obj] of Object.entries(configuration.vertices)) {
const vertex = dl4j.Node._object(obj);
inputs = configuration.vertexInputs[name];
let variables = [];
let layer = null;
switch (vertex.__type__) {
case 'LayerVertex':
layer = dl4j.Node._object(vertex.layerConf.layer);
variables = vertex.layerConf.variables;
break;
case 'MergeVertex':
layer = { __type__: 'Merge', layerName: name };
break;
case 'ElementWiseVertex':
layer = { __type__: 'ElementWise', layerName: name, op: vertex.op };
break;
case 'PreprocessorVertex':
layer = { __type__: 'Preprocessor', layerName: name };
break;
default:
throw new dl4j.Error(`Unsupported vertex class '${vertex['@class']}'.`);
}
const node = new dl4j.Node(metadata, layer, inputs, dataType, variables, values);
this.nodes.push(node);
}
}
// Multi Layer Network
if (configuration.confs) {
inputs = ['input'];
this.inputs.push(new dl4j.Argument('input', [values.map('input')]));
for (const conf of configuration.confs) {
const layer = dl4j.Node._object(conf.layer);
const node = new dl4j.Node(metadata, layer, inputs, dataType, conf.variables, values);
this.nodes.push(node);
inputs = [layer.layerName];
}
if (inputs && inputs.length > 0) {
const argument = new dl4j.Argument('output', [values.map(inputs[0])]);
this.outputs.push(argument);
}
}
}
};
dl4j.Argument = class {
constructor(name, value, visible = true) {
this.name = name;
this.value = value;
this.visible = visible;
}
};
dl4j.Value = class {
constructor(name, type, initializer) {
if (typeof name !== 'string') {
throw new dl4j.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = initializer ? initializer.type : type;
this.initializer = initializer;
}
};
dl4j.Node = class {
constructor(metadata, layer, inputs, dataType, variables, values) {
this.name = layer.layerName || '';
this.inputs = [];
this.outputs = [];
this.attributes = [];
const type = layer.__type__;
this.type = metadata.type(type) || { name: type };
if (inputs && inputs.length > 0) {
const argument = new dl4j.Argument(values.length < 2 ? 'input' : 'inputs', inputs.map((input) => values.map(input)));
this.inputs.push(argument);
}
if (variables) {
for (const variable of variables) {
let tensor = null;
switch (type) {
case 'Convolution':
switch (variable) {
case 'W':
tensor = new dl4j.Tensor(dataType, layer.kernelSize.concat([layer.nin, layer.nout]));
break;
case 'b':
tensor = new dl4j.Tensor(dataType, [layer.nout]);
break;
default:
throw new dl4j.Error(`Unsupported '${type}' variable '${variable}'.`);
}
break;
case 'SeparableConvolution2D':
switch (variable) {
case 'W':
tensor = new dl4j.Tensor(dataType, layer.kernelSize.concat([layer.nin, layer.nout]));
break;
case 'pW':
tensor = new dl4j.Tensor(dataType, [layer.nout]);
break;
default:
throw new dl4j.Error(`Unsupported '${type}' variable '${variable}'.`);
}
break;
case 'Output':
case 'Dense':
switch (variable) {
case 'W':
tensor = new dl4j.Tensor(dataType, [layer.nout, layer.nin]);
break;
case 'b':
tensor = new dl4j.Tensor(dataType, [layer.nout]);
break;
default:
throw new dl4j.Error(`Unsupported '${this.type}' variable '${variable}'.`);
}
break;
case 'BatchNormalization':
tensor = new dl4j.Tensor(dataType, [layer.nin]);
break;
default:
throw new dl4j.Error(`Unsupported '${type}' variable '${variable}'.`);
}
const argument = new dl4j.Argument(variable, [values.map('', null, tensor)]);
this.inputs.push(argument);
}
}
if (this.name) {
const value = values.map(this.name);
const argument = new dl4j.Argument('output', [value]);
this.outputs.push(argument);
}
let attributes = layer;
if (layer.activationFn) {
const activation = dl4j.Node._object(layer.activationFn);
if (activation.__type__ !== 'ActivationIdentity' && activation.__type__ !== 'Identity') {
if (activation.__type__.startsWith('Activation')) {
activation.__type__ = activation.__type__.substring('Activation'.length);
}
if (this.type === 'Activation') {
this.type = activation.__type__;
attributes = activation;
} else {
this.chain = this.chain || [];
this.chain.push(new dl4j.Node(metadata, activation, [], null, null, values));
}
}
}
for (const [name, value] of Object.entries(attributes)) {
switch (name) {
case '__type__':
case 'constraints':
case 'layerName':
case 'activationFn':
case 'idropout':
case 'hasBias':
continue;
default:
break;
}
const definition = metadata.attribute(type, name);
const visible = definition && definition.visible === false ? false : true;
const attribute = new dl4j.Argument(name, value, visible);
this.attributes.push(attribute);
}
if (layer.idropout) {
const dropout = dl4j.Node._object(layer.idropout);
if (dropout.p !== 1.0) {
throw new dl4j.Error("Layer 'idropout' not implemented.");
}
}
}
static _object(value) {
let result = {};
if (value['@class']) {
result = value;
let type = value['@class'].split('.').pop();
if (type.endsWith('Layer')) {
type = type.substring(0, type.length - 5);
}
delete value['@class'];
result.__type__ = type;
} else {
let [key] = Object.keys(value);
result = value[key];
if (key.length > 0) {
key = key[0].toUpperCase() + key.substring(1);
}
result.__type__ = key;
}
return result;
}
};
dl4j.Tensor = class {
constructor(dataType, shape) {
this.type = new dl4j.TensorType(dataType, new dl4j.TensorShape(shape));
}
};
dl4j.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType;
this.shape = shape;
}
toString() {
return (this.dataType || '?') + this.shape.toString();
}
};
dl4j.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
toString() {
if (this.dimensions) {
if (this.dimensions.length === 0) {
return '';
}
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
return '';
}
};
dl4j.NDArray = class {
constructor(reader) {
reader = new dl4j.BinaryReader(reader);
const readHeader = (reader) => {
const alloc = reader.string();
let length = 0;
switch (alloc) {
case 'DIRECT':
case 'HEAP':
case 'JAVACPP':
length = reader.int32();
break;
case 'LONG_SHAPE':
case 'MIXED_DATA_TYPES':
length = reader.int64().toNumber();
break;
default:
throw new dl4j.Error(`Unsupported header alloc '${alloc}'.`);
}
const type = reader.string();
return [alloc, length, type];
};
const headerShape = readHeader(reader);
if (headerShape[2] !== 'INT') {
throw new dl4j.Error(`Unsupported header shape type '${headerShape[2]}'.`);
}
const shapeInfo = new Array(headerShape[1]);
for (let i = 0; i < shapeInfo.length; i++) {
shapeInfo[i] = reader.int32();
}
const [rank] = shapeInfo;
const shapeInfoLength = rank * 2 + 4;
this.shape = shapeInfo.slice(1, 1 + rank);
this.strides = shapeInfo.slice(1 + rank, 1 + (rank * 2));
this.order = shapeInfo[shapeInfoLength - 1];
const headerData = readHeader(reader);
const dataTypes = new Map([
['INT', ['int32', 4]],
['FLOAT', ['float32', 4]],
['DOUBLE', ['float64', 8]]
]);
if (!dataTypes.has(headerData[2])) {
throw new dl4j.Error(`Unsupported header data type '${headerData[2]}'.`);
}
const [dataType, itemSize] = dataTypes.get(headerData[2]);
this.dataType = dataType;
const size = headerData[1] * itemSize;
if ((reader.position + size) <= reader.length) {
this.data = reader.read(size);
}
}
};
dl4j.BinaryReader = class {
constructor(reader) {
this._reader = reader;
}
get length() {
return this._reader.length;
}
get position() {
return this._reader.position;
}
read(length) {
return this._reader.read(length);
}
int32() {
return this._reader.int32();
}
int64() {
return this._reader.int64();
}
uint16() {
return this._reader.uint16();
}
string() {
const size = this.uint16();
const buffer = this.read(size);
this._decoder = this._decoder || new TextDecoder('ascii');
return this._decoder.decode(buffer);
}
};
dl4j.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Deeplearning4j model.';
}
};
export const ModelFactory = dl4j.ModelFactory;
+146
View File
@@ -0,0 +1,146 @@
[
{
"name": "BatchNorm",
"category": "Normalization"
},
{
"name": "Batchnorm",
"category": "Normalization",
"inputs": [
{ "name": "input" },
{ "name": "mean" },
{ "name": "variance" }
]
},
{
"name": "Concat",
"category": "Tensor"
},
{
"name": "Conv2d",
"category": "Layer",
"inputs": [
{ "name": "input" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "Convolutional",
"category": "Layer"
},
{
"name": "Deconvolution",
"category": "Layer"
},
{
"name": "DepthWiseConv2d",
"category": "Layer",
"inputs": [
{ "name": "input" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "FullyConnected",
"category": "Layer",
"inputs": [
{ "name": "input" },
{ "name": "weight" },
{ "name": "bias" }
]
},
{
"name": "Gather",
"category": "Transform",
"inputs": [
{ "name": "input" },
{ "name": "indices" }
]
},
{
"name": "Neuron",
"category": "Activation",
"attributes": [
{ "name": "type", "type": "Activation" }
]
},
{
"name": "Pad",
"category": "Shape"
},
{
"name": "Permute",
"category": "Shape"
},
{
"name": "Pool",
"category": "Pool"
},
{
"name": "PoolAvg2d",
"category": "Pool"
},
{
"name": "Pooling",
"category": "Pool"
},
{
"name": "PoolMax2d",
"category": "Pool"
},
{
"name": "Prelu",
"category": "Activation",
"inputs": [
{ "name": "input" },
{ "name": "weight" }
]
},
{
"name": "Relu",
"category": "Activation"
},
{
"name": "Reshape",
"category": "Shape"
},
{
"name": "Sigmoid",
"category": "Activation"
},
{
"name": "Slice",
"category": "Tensor"
},
{
"name": "SoftMax",
"category": "Activation"
},
{
"name": "Softmax",
"category": "Activation"
},
{
"name": "Split",
"category": "Tensor"
},
{
"name": "StridedSlice",
"category": "Tensor"
},
{
"name": "Transpose",
"category": "Transform"
},
{
"name": "TransposeConv2d",
"category": "Layer",
"inputs": [
{ "name": "input" },
{ "name": "weight" },
{ "name": "bias" }
]
}
]
+283
View File
@@ -0,0 +1,283 @@
export const dlc = {};
dlc.v3 = dlc.v3 || {};
dlc.v3.Model = class Model {
static decode(reader, position) {
const $ = new dlc.v3.Model();
$.unk1 = reader.int32_(position, 4, 0);
$.nodes = reader.tables(position, 6, dlc.v3.Node);
$.unk2 = reader.array(position, 8, Int32Array);
$.unk3 = reader.array(position, 10, Int32Array);
$.attributes = reader.tables(position, 12, dlc.v3.Attribute);
return $;
}
};
dlc.v3.Node = class Node {
static decode(reader, position) {
const $ = new dlc.v3.Node();
$.index = reader.int32_(position, 4, 0);
$.name = reader.string_(position, 6, null);
$.type = reader.string_(position, 8, null);
$.inputs = reader.strings_(position, 10);
$.outputs = reader.strings_(position, 12);
$.attributes = reader.tables(position, 14, dlc.v3.Attribute);
return $;
}
};
dlc.v3.Tensor = class Tensor {
static decode(reader, position) {
const $ = new dlc.v3.Tensor();
$.name = reader.string_(position, 4, null);
$.shape = reader.array(position, 6, Int32Array);
$.data = reader.table(position, 8, dlc.v3.TensorData);
$.attributes = reader.tables(position, 10, dlc.v3.Attribute);
return $;
}
};
dlc.v3.TensorData = class TensorData {
static decode(reader, position) {
const $ = new dlc.v3.TensorData();
$.dtype = reader.uint8_(position, 4, 0);
$.bytes = reader.array(position, 6, Uint8Array);
$.floats = reader.array(position, 8, Float32Array);
return $;
}
};
dlc.v3.Attribute = class Attribute {
static decode(reader, position) {
const $ = new dlc.v3.Attribute();
$.name = reader.string_(position, 4, null);
$.type = reader.uint8_(position, 6, 0);
$.bool_value = reader.bool_(position, 8, false);
$.int32_value = reader.int32_(position, 10, 0);
$.uint32_value = reader.uint32_(position, 12, 0);
$.float32_value = reader.float32_(position, 14, 0);
$.string_value = reader.string_(position, 16, null);
$.unk6 = reader.array(position, 18, Int8Array);
$.byte_list = reader.array(position, 20, Int8Array);
$.int32_list = reader.array(position, 22, Int32Array);
$.float32_list = reader.array(position, 24, Float32Array);
$.unk10 = reader.array(position, 26, Int8Array);
$.attributes = reader.tables(position, 28, dlc.v3.Attribute);
return $;
}
};
dlc.v3.Activation = {
ReLU: 1, '1': 'ReLU',
Sigmoid: 3, '3': 'Sigmoid'
};
dlc.v3.ModelParameters = class ModelParameters {
static decode(reader, position) {
const $ = new dlc.v3.ModelParameters();
$.nodes = reader.tables(position, 4, dlc.v3.NodeParameters);
return $;
}
};
dlc.v3.NodeParameters = class NodeParameters {
static decode(reader, position) {
const $ = new dlc.v3.NodeParameters();
$.name = reader.string_(position, 4, null);
$.weights = reader.tables(position, 6, dlc.v3.Tensor);
return $;
}
};
dlc.v4 = dlc.v4 || {};
dlc.v4.Model = class Model {
static decode(reader, position) {
const $ = new dlc.v4.Model();
$.graphs = reader.tables(position, 4, dlc.v4.Graph);
return $;
}
};
dlc.v4.Graph = class Graph {
static decode(reader, position) {
const $ = new dlc.v4.Graph();
$.name = reader.string_(position, 4, null);
$.nodes = reader.tables(position, 6, dlc.v4.Node);
$.tensors = reader.tables(position, 8, dlc.v4.Tensor);
return $;
}
};
dlc.v4.Node = class Node {
static decode(reader, position) {
const $ = new dlc.v4.Node();
$.name = reader.string_(position, 4, null);
$.type = reader.string_(position, 6, null);
$.inputs = reader.strings_(position, 8);
$.outputs = reader.strings_(position, 10);
$.attributes = reader.tables(position, 12, dlc.v4.Attribute);
return $;
}
};
dlc.v4.Attribute = class Attribute {
static decode(reader, position) {
const $ = new dlc.v4.Attribute();
$.name = reader.string_(position, 4, null);
$.kind = reader.int32_(position, 6, 0);
$.flag = reader.uint8_(position, 8, 0);
$.value = reader.table(position, 10, dlc.v4.Value);
$.tensor = reader.table(position, 12, dlc.v4.Tensor);
return $;
}
};
dlc.v4.Value = class Value {
static decode(reader, position) {
const $ = new dlc.v4.Value();
$.kind = reader.int32_(position, 4, 0);
$.int32_value = reader.int32_(position, 6, 0);
$.float32_value = reader.float32_(position, 8, 0);
$.string_value = reader.string_(position, 10, null);
return $;
}
};
dlc.v4.Tensor = class Tensor {
static decode(reader, position) {
const $ = new dlc.v4.Tensor();
$.unk1 = reader.uint32_(position, 4, 0);
$.name = reader.string_(position, 6, null);
$.location = reader.int32_(position, 8, 0);
$.shape = reader.array(position, 10, Int32Array);
$.unk2 = reader.int32_(position, 12, 0);
$.info = reader.table(position, 14, dlc.v4.TensorInfo);
$.dtype = reader.int32_(position, 16, 0);
$.output_dtype = reader.int32_(position, 18, 0);
$.unk6 = reader.uint8_(position, 20, 0);
return $;
}
};
dlc.v4.TensorInfo = class TensorInfo {
static decode(reader, position) {
const $ = new dlc.v4.TensorInfo();
$.i1 = reader.int32_(position, 4, 0);
$.b1 = reader.uint8_(position, 6, 0);
$.a = reader.table(position, 8, dlc.v4.TensorInfo1);
$.b = reader.table(position, 10, dlc.v4.TensorInfo2);
return $;
}
};
dlc.v4.TensorInfo1 = class TensorInfo1 {
static decode(reader, position) {
const $ = new dlc.v4.TensorInfo1();
$.i1 = reader.int32_(position, 4, 0);
$.f1 = reader.float32_(position, 6, 0);
$.f2 = reader.float32_(position, 8, 0);
$.f3 = reader.float32_(position, 10, 0);
$.i2 = reader.int32_(position, 12, 0);
return $;
}
};
dlc.v4.TensorInfo2 = class TensorInfo2 {
static decode(reader, position) {
const $ = new dlc.v4.TensorInfo2();
$.i1 = reader.int32_(position, 4, 0);
$.l = reader.tables(position, 6, dlc.v4.TensorInfo3);
return $;
}
};
dlc.v4.TensorInfo3 = class TensorInfo3 {
static decode(reader, position) {
const $ = new dlc.v4.TensorInfo3();
$.i1 = reader.int32_(position, 4, 0);
$.f1 = reader.float32_(position, 6, 0);
$.f2 = reader.float32_(position, 8, 0);
$.f3 = reader.float32_(position, 10, 0);
$.i2 = reader.int32_(position, 12, 0);
$.b1 = reader.uint8_(position, 14, 0);
return $;
}
};
dlc.v4.ModelParameters64 = class ModelParameters64 {
static decode(reader, position) {
const $ = new dlc.v4.ModelParameters64();
$.buffers = reader.tables(position, 4, dlc.v4.Buffer);
$.params = reader.array(position, 6, Uint8Array);
return $;
}
};
dlc.v4.ModelParameters = class ModelParameters {
static decode(reader, position) {
const $ = new dlc.v4.ModelParameters();
$.graphs = reader.tables(position, 4, dlc.v4.GraphParameters);
return $;
}
};
dlc.v4.GraphParameters = class GraphParameters {
static decode(reader, position) {
const $ = new dlc.v4.GraphParameters();
$.name = reader.string_(position, 4, null);
$.tensors = reader.tables(position, 6, dlc.v4.TensorData);
$.nodes = reader.tables(position, 8, dlc.v4.NodeParameters);
return $;
}
};
dlc.v4.NodeParameters = class NodeParameters {
static decode(reader, position) {
const $ = new dlc.v4.NodeParameters();
$.tensors = reader.tables(position, 4, dlc.v4.TensorData);
return $;
}
};
dlc.v4.TensorData = class TensorData {
static decode(reader, position) {
const $ = new dlc.v4.TensorData();
$.name = reader.string_(position, 4, null);
$.bytes = reader.array(position, 6, Uint8Array);
return $;
}
};
dlc.v4.Buffer = class Buffer {
static decode(reader, position) {
const $ = new dlc.v4.Buffer();
$.bytes = reader.array(position, 4, Uint8Array);
return $;
}
};
+679
View File
@@ -0,0 +1,679 @@
import * as flatbuffers from './flatbuffers.js';
import * as text from './text.js';
const dlc = {};
dlc.ModelFactory = class {
async match(context) {
const container = await dlc.Container.open(context);
if (container) {
return context.set('dlc', container);
}
return null;
}
async open(context) {
dlc.schema = await context.require('./dlc-schema');
dlc.schema = dlc.schema.dlc;
await context.value.read();
const metadata = await context.metadata('dlc-metadata.json');
return new dlc.Model(metadata, context.value);
}
};
dlc.Model = class {
constructor(metadata, target) {
this.format = target.format;
this.metadata = [];
if (target.metadata.size > 0) {
const version = target.metadata.get('model-version');
if (version) {
this.version = version;
}
const converter = target.metadata.get('converter-command');
if (converter) {
const source = converter.split(' ').shift().trim();
if (source.length > 0) {
const version = target.metadata.get('converter-version');
this.source = version ? `${source} v${version}` : source;
}
}
const license = target.metadata.get('model-copyright');
if (license && license !== 'N/A') {
this.metadata.push(new dlc.Argument('license', license));
}
}
for (const graph of target.graphs) {
this.modules = [new dlc.Graph(metadata, target.version.major, graph)];
}
}
};
dlc.Graph = class {
constructor(metadata, version, graph) {
this.name = graph.name;
this.inputs = [];
this.outputs = [];
const values = new Map();
switch (version) {
case 3: {
for (const node of graph.nodes) {
for (const name of node.inputs) {
if (!values.has(name)) {
values.set(name, {});
}
}
for (const name of node.outputs) {
if (!values.has(name)) {
values.set(name, {});
}
}
let shapes = new Array(node.outputs.length);
for (const attribute of node.attributes) {
if (attribute.name === 'OutputDims' &&
Array.isArray(attribute.attributes) && attribute.attributes.length > 0) {
shapes = attribute.data;
break;
}
}
for (let i = 0; i < node.outputs.length; i++) {
const name = node.outputs[i];
const value = values.get(name);
if (!value.shape && i < shapes.length) {
value.shape = shapes[i];
}
}
}
break;
}
case 4: {
for (const tensor of graph.tensors) {
values.set(tensor.name, tensor);
}
break;
}
default: {
break;
}
}
for (const [name, tensor] of values) {
const type = tensor.shape ? new dlc.TensorType(tensor.dtype, tensor.shape) : null;
const initializer = tensor.data && tensor.data ? new dlc.Tensor(tensor.name, type, tensor.data) : null;
const value = new dlc.Value(name, type, initializer);
values.set(name, value);
}
const value = (name) => {
if (!values.has(name)) {
values.set(name, new dlc.Value(name));
}
return values.get(name);
};
this.nodes = [];
for (const node of graph.nodes) {
if (node.type === 'Input') {
this.inputs.push(new dlc.Argument(node.name, node.inputs.map((input) => value(input))));
continue;
}
this.nodes.push(new dlc.Node(metadata, version, node, value));
}
}
};
dlc.Argument = class {
constructor(name, value, type = null) {
this.name = name;
this.value = value;
this.type = type;
}
};
dlc.Value = class {
constructor(name, type, initializer) {
if (typeof name !== 'string') {
throw new dlc.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = type;
this.initializer = initializer;
}
};
dlc.Node = class {
constructor(metadata, version, obj, value) {
this.type = metadata.type(obj.type);
this.name = obj.name;
this.inputs = [];
this.outputs = [];
this.attributes = [];
const inputs = Array.isArray(obj.inputs) ? Array.from(obj.inputs).map((name) => value(name)) : [];
if (version !== 3 && Array.isArray(this.type.inputs) && inputs.length === this.type.inputs.length) {
for (let i = 0; i < inputs.length; i++) {
const argument = new dlc.Argument(this.type.inputs[i].name, [inputs[i]]);
this.inputs.push(argument);
}
} else if (inputs.length > 0) {
const argument = new dlc.Argument(inputs.length === 1 ? 'input' : 'inputs', inputs);
this.inputs.push(argument);
}
const outputs = Array.isArray(obj.outputs) ? Array.from(obj.outputs).map((name) => value(name)) : [];
if (Array.isArray(this.type.outputs) && outputs.length === this.type.outputs.length) {
for (let i = 0; i < outputs.length; i++) {
const argument = new dlc.Argument(this.type.outputs[i].name, [outputs[i]]);
this.outputs.push(argument);
}
} else if (outputs.length > 0) {
const argument = new dlc.Argument(outputs.length === 1 ? 'output' : 'outputs', outputs);
this.outputs.push(argument);
}
if (obj.attributes) {
for (const attr of obj.attributes) {
if (attr.name === 'OutputDims') {
continue;
}
const schema = metadata.attribute(obj.type, attr.name);
let type = attr.type;
switch (type) {
case 'tensor': {
const tensor = attr.data;
const type = new dlc.TensorType(tensor.dtype, tensor.shape);
value = new dlc.Tensor(tensor.name, type, tensor.data);
break;
}
default: {
value = attr.data;
}
}
if (schema && schema.type) {
type = schema.type;
let enumType = null;
switch (version) {
case 3: enumType = dlc.schema.v3[type]; break;
case 4: enumType = dlc.schema.v4[type]; break;
default: throw new dlc.Error(`Unsupported version '${version}'.`);
}
if (enumType) {
value = enumType[value] || value;
}
}
const attribute = new dlc.Argument(attr.name, value, type);
this.attributes.push(attribute);
}
}
if (obj.weights) {
for (const tensor of obj.weights) {
const type = new dlc.TensorType(tensor.data.dtype, tensor.shape);
const value = new dlc.Value('', type, new dlc.Tensor(tensor.name, type, tensor.data));
this.inputs.push(new dlc.Argument(tensor.name, [value]));
}
}
}
};
dlc.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType || '?';
this.shape = new dlc.TensorShape(shape);
}
toString() {
return this.dataType + this.shape.toString();
}
};
dlc.TensorShape = class {
constructor(dimensions) {
this.dimensions = Array.from(dimensions);
}
toString() {
if (Array.isArray(this.dimensions) && this.dimensions.length > 0) {
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
return '';
}
};
dlc.Tensor = class {
constructor(name, type, data) {
this.name = name;
this.type = type;
if (data instanceof Uint8Array) {
this.encoding = '<';
this.values = data;
} else {
this.encoding = '|';
switch (type.dataType) {
case 'uint8': this.values = data.bytes; break;
case 'float32': this.values = data.floats; break;
default: throw new dlc.Error(`Unsupported tensor data type '${type.dataType}'.`);
}
}
}
};
dlc.Container = class {
static async open(context) {
const entries = await context.peek('zip');
if (entries instanceof Map) {
const model = entries.get('model');
const params = entries.get('model.params');
const metadata = entries.get('dlc.metadata');
if (model) {
const signature = dlc.Container._signature(model);
if (signature && (signature.identifier === 'NETD' || signature.major === 2)) {
return new dlc.Container(context, model, params, metadata);
}
}
if (params) {
const signature = dlc.Container._signature(params);
if (signature && signature.identifier === 'NETP') {
return new dlc.Container(context, model, params, metadata);
}
}
return null;
}
const stream = context.stream;
const signature = dlc.Container._signature(stream);
switch (signature.identifier) {
case 'NETD':
return new dlc.Container(context, stream, undefined, undefined);
case 'NETP':
case 'NR64':
return new dlc.Container(context, undefined, stream, undefined);
default:
return null;
}
}
constructor(context, model, params, metadata) {
this.context = context;
this._model = model;
this._params = params;
this._metadata = metadata;
}
async read() {
if (this._model === undefined) {
this._model = await this._fetch('model');
}
if (this._params === undefined) {
this._params = await this._fetch('model.params');
}
if (this._metadata === undefined) {
this._metadata = await this._fetch('dlc.metadata');
}
delete this.context;
this.graphs = [];
this.metadata = new Map();
if (this._model) {
this.format = 'DLC';
const stream = this._model;
delete this._model;
const signature = dlc.Container._signature(stream);
if (signature.major === 2) {
throw new dlc.Error("File contains undocumented DLC v2 data.");
} else if (signature.identifier === 'NETD' && (signature.major === 3 || signature.major === undefined)) {
this.version = { major: signature.major || 3, minor: signature.minor || 0 };
this.graph = dlc.Container._model3(stream, signature.offset);
this.graphs = [this.graph];
} else if (signature.identifier === 'NETD' && signature.major === 4) {
this.version = { major: signature.major, minor: signature.minor };
this.graphs = dlc.Container._model4(stream);
} else {
const buffer = stream.peek(Math.min(stream.length, 16));
const content = Array.from(buffer).map((c) => (c < 16 ? '0' : '') + c.toString(16)).join('');
throw new dlc.Error(`File contains undocumented '${content}' data.`);
}
}
if (this._params) {
this.format = this.format || 'DLC Weights';
const stream = this._params;
delete this._params;
const signature = dlc.Container._signature(stream);
if (signature.major === 2) {
throw new dlc.Error("File contains undocumented DLC v2 data.");
} else if (signature.identifier === 'NETP' && (signature.major === 3 || signature.major === undefined)) {
this.version = this.graphs.length > 0 ? this.version : { major: signature.major || 3, minor: signature.minor || 0 };
this.graph = dlc.Container._params3(stream, signature, this.graph);
this.graphs = [this.graph];
} else if ((signature.identifier === 'NETP' || signature.identifier === 'NR64') && signature.major === 4) {
dlc.Container._params4(stream, this.graphs, signature);
} else {
const buffer = stream.peek(Math.min(stream.length, 16));
const content = Array.from(buffer).map((c) => (c < 16 ? '0' : '') + c.toString(16)).join('');
throw new dlc.Error(`File contains undocumented '${content}' data.`);
}
}
if (this._metadata) {
const stream = this._metadata;
delete this._metadata;
const reader = text.Reader.open(stream);
for (;;) {
const line = reader.read('\n');
if (line === undefined) {
break;
}
const index = line.indexOf('=');
if (index === -1) {
break;
}
const key = line.substring(0, index);
const value = line.substring(index + 1);
this.metadata.set(key, value);
}
}
}
static _model3(stream, offset) {
let model = null;
try {
const buffer = new Uint8Array(offset > 0 ? stream.peek().subarray(offset) : stream.peek());
const reader = flatbuffers.BinaryReader.open(buffer);
model = dlc.schema.v3.Model.decode(reader, reader.root);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new dlc.Error(`File format is not dlc.v3.NETD (${message.replace(/\.$/, '')}).`);
}
model.tensors = [];
const updateAttribute = (attr) => {
switch (attr.type) {
case 1: return ['boolean', attr.bool_value];
case 2: return ['int32', attr.int32_value];
case 3: return ['uint32', attr.uint32_value];
case 4: return ['float32', attr.float32_value];
case 5: return ['string', attr.string_value];
case 7: return ['byte[]', Array.from(attr.byte_list)];
case 8: return ['int32[]', Array.from(attr.int32_list)];
case 9: return ['float32[]', Array.from(attr.float32_list)];
case 11: {
const obj = {};
let index = 0;
let list = true;
for (const attribute of attr.attributes) {
const name = attribute.name;
const [, data] = updateAttribute(attribute);
obj[name] = data;
list = list && index.toString() === attribute.name;
index++;
}
return list ? ['', Object.values(obj)] : ['', obj];
}
default:
throw new dlc.Error(`Unsupported attribute type '${attr.type}'.`);
}
};
for (const node of model.nodes) {
for (const attribute of node.attributes) {
const [type, data] = updateAttribute(attribute);
attribute.type = type;
attribute.data = data;
}
}
return model;
}
static _model4(stream) {
let model = null;
try {
const buffer = new Uint8Array(stream.peek().subarray(8));
const reader = flatbuffers.BinaryReader.open(buffer);
model = dlc.schema.v4.Model.decode(reader, reader.root);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new dlc.Error(`File format is not dlc.v4.NETD (${message.replace(/\.$/, '')}).`);
}
const dataType = (value) => {
switch (value) {
case 0x0008: return 'int8';
case 0x0016: return 'int16';
case 0x0032: return 'int32';
case 0x0064: return 'int64';
case 0x0108: return 'uint8';
case 0x0116: return 'uint16';
case 0x0132: return 'uint32';
case 0x0164: return 'uint64';
case 0x0216: return 'float16';
case 0x0232: return 'float32';
case 0x0304: return 'qint4';
case 0x0308: return 'qint8';
case 0x0316: return 'qint16';
case 0x0332: return 'qint32';
case 0x0404: return 'quint4';
case 0x0408: return 'quint8';
case 0x0416: return 'quint16';
case 0x0432: return 'quint32';
case 0x0508: return 'boolean';
case 0x0608: return 'string';
case 0x7fffffff: return 'undefined';
default: throw new dlc.Error(`Unsupported data type '${JSON.stringify(value)}'.`);
}
};
const updateTensor = (tensor) => {
tensor.dtype = dataType(tensor.dtype);
tensor.output_dtype = dataType(tensor.output_dtype);
};
for (const graph of model.graphs) {
for (const node of graph.nodes) {
for (const attribute of node.attributes) {
switch (attribute.kind) {
case 0: {
const value = attribute.value;
switch (value.kind) {
case 0x7fffffff:
attribute.data = value.string_value;
attribute.type = 'string';
break;
case 0x0032:
attribute.data = value.int32_value;
break;
case 0x0108:
attribute.data = value.int32_value;
attribute.type = 'int8';
break;
case 0x0132:
attribute.data = value.int32_value;
attribute.type = 'int32';
break;
case 0x0232:
attribute.data = value.float32_value;
attribute.type = 'float32';
break;
case 0x0508:
attribute.data = value.int32_value !== 0;
attribute.type = 'boolean';
break;
case 0x0608:
attribute.data = value.string_value;
attribute.type = 'string';
break;
default:
throw new dlc.Error(`Unknown attribute value kind '${value.kind}'.`);
}
break;
}
case 1: {
const tensor = attribute.tensor;
updateTensor(tensor);
attribute.type = 'tensor';
attribute.data = tensor;
break;
}
default: {
throw new dlc.Error(`Unknown attribute kind '${attribute.kind}'.`);
}
}
}
}
for (const tensor of graph.tensors) {
updateTensor(tensor);
}
}
return model.graphs;
}
static _params3(stream, signature, graph) {
let params = null;
try {
const buffer = new Uint8Array(signature === 'NETP' ? stream.peek() : stream.peek().subarray(8));
const reader = flatbuffers.BinaryReader.open(buffer);
params = dlc.schema.v3.ModelParameters.decode(reader, reader.root);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new dlc.Error(`File format is not dlc.v3.NETP (${message.replace(/\.$/, '')}).`);
}
if (!graph) {
graph = new dlc.schema.v3.ModelParameters();
graph.nodes = new Array(params.nodes.length);
graph.tensors = [];
for (let i = 0; i < graph.nodes.length; i++) {
const node = new dlc.schema.v3.Node();
node.type = 'Weights';
node.name = params.nodes[i].name;
node.inputs = [];
node.outputs = [];
node.attributes = [];
graph.nodes[i] = node;
}
}
const dataType = (value) => {
switch (value) {
case null: return '?';
case 6: return 'uint8';
case 9: return 'float32';
default:
throw new dlc.Error(`Unsupported data type '${JSON.stringify(value)}'.`);
}
};
const weights = new Map(params.nodes.map((node) => [node.name, node.weights]));
for (const node of graph.nodes) {
if (weights.has(node.name)) {
const tensors = weights.get(node.name);
for (const tensor of tensors) {
tensor.data.dtype = dataType(tensor.data.dtype);
}
node.weights = tensors;
}
}
return graph;
}
static _params4(stream, graphs, signature) {
let buffer = stream.peek().subarray(8);
let buffers = null;
if (signature.major === 4 && signature.identifier === 'NR64') {
try {
const reader = flatbuffers.BinaryReader.open(buffer);
const nr64 = dlc.schema.v4.ModelParameters64.decode(reader, reader.root);
buffers = nr64.buffers;
buffer = nr64.params;
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new dlc.Error(`File format is not dlc.v4.NR64 (${message.replace(/\.$/, '')}).`);
}
}
let params = null;
try {
const reader = flatbuffers.BinaryReader.open(buffer);
params = dlc.schema.v4.ModelParameters.decode(reader, reader.root);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new dlc.Error(`File format is not dlc.v4.NETP (${message.replace(/\.$/, '')}).`);
}
if (graphs.length === 0) {
throw new dlc.Error('Model definition not available.');
}
const weights = new Map(params.graphs.map((graph) => [graph.name, graph]));
for (const graph of graphs) {
const params = weights.get(graph.name);
const tensors = new Map(params.tensors.map((tensor) => [tensor.name, tensor]));
let index = 0;
graph.tensors.sort((a, b) => a.name.localeCompare(b.name));
for (const tensor of graph.tensors) {
if (tensor.location === 4) {
if (buffers && index < buffers.length) {
tensor.data = buffers[index++].bytes;
} else if (tensors.has(tensor.name)) {
tensor.data = tensors.get(tensor.name).bytes;
} else {
throw new dlc.Error(`Unknown tensor `);
}
}
}
for (let i = 0; i < graph.nodes.length; i++) {
const node = graph.nodes[i];
const tensors = new Map(params.nodes[i].tensors.map((tensor) => [tensor.name, tensor]));
for (const attribute of node.attributes) {
const tensor = attribute.tensor;
if (tensor) {
if (buffers && index < buffers.length) {
tensor.data = buffers[index++].bytes;
} else if (tensors.has(tensor.name)) {
tensor.data = tensors.get(tensor.name).bytes;
} else {
throw new dlc.Error(`Unknown tensor `);
}
}
}
}
}
}
async _fetch(name) {
try {
const context = await this.context.fetch(name);
return context.stream;
} catch {
return null;
}
}
static _signature(stream) {
const signature = {};
signature.identifier = '?';
signature.offset = 0;
if (stream) {
const buffer = stream.peek(Math.min(stream.length, 16));
if (buffer[0] === 0xD5 && buffer[1] === 0x0A) {
delete signature.identifier;
if (buffer[3] === 0x00 && buffer[5] === 0x00 && buffer[6] === 0x00) {
signature.major = buffer[2] | buffer[3] << 8;
signature.minor = buffer[4] | buffer[5] << 8;
if (signature.major > 2) {
signature.identifier = '?';
}
}
}
if (signature.identifier === '?') {
const offset = signature.major === undefined ? 0 : 8;
const reader = flatbuffers.BinaryReader.open(stream, offset);
if (reader) {
signature.identifier = reader.identifier;
signature.offset = offset;
}
}
}
return signature;
}
};
dlc.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading DLC model.';
}
};
export const ModelFactory = dlc.ModelFactory;
+109
View File
@@ -0,0 +1,109 @@
[
{
"name": "batchnorm",
"category": "Normalization",
"inputs": [
{ "name": "input" },
{ "name": "scale" },
{ "name": "bias" }
]
},
{
"name": "concat",
"category": "Tensor",
"inputs": [
{ "name": "input", "option": "variadic" }
]
},
{
"name": "const_v2",
"category": "Constant"
},
{
"name": "conv",
"category": "Layer",
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "biases" }
]
},
{
"name": "deconv",
"category": "Layer",
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "biases" }
]
},
{
"name": "depthdeconv",
"category": "Layer",
"inputs": [
{ "name": "input" },
{ "name": "weights" },
{ "name": "biases" }
]
},
{
"name": "eltwise",
"inputs": [
{ "name": "input", "option": "variadic" }
]
},
{
"name": "linear",
"category": "Activation",
"inputs": [
{ "name": "input" }
]
},
{
"name": "pad",
"category": "Shape"
},
{
"name": "pool",
"category": "Pool",
"inputs": [
{ "name": "input" }
]
},
{
"name": "prelu",
"category": "Activation",
"inputs": [
{ "name": "input" },
{ "name": "slope" }
]
},
{
"name": "relu",
"category": "Activation",
"inputs": [
{ "name": "input" }
]
},
{
"name": "relu6",
"category": "Activation",
"inputs": [
{ "name": "input" }
]
},
{
"name": "sigmoid",
"category": "Activation",
"inputs": [
{ "name": "input" }
]
},
{
"name": "softmax",
"category": "Activation",
"inputs": [
{ "name": "input" }
]
}
]
+353
View File
@@ -0,0 +1,353 @@
export const dnn = {};
dnn.Model = class Model {
constructor() {
this.input_shape = [];
this.input_name = [];
this.node = [];
this.input = [];
this.output = [];
}
static decode(reader, length) {
const message = new dnn.Model();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.name = reader.string();
break;
case 2:
message.version = reader.int32();
break;
case 4:
message.input_shape = reader.array(message.input_shape, () => reader.int32(), tag);
break;
case 7:
message.input_name.push(reader.string());
break;
case 10:
message.node.push(dnn.Node.decode(reader, reader.uint32()));
break;
case 12:
message.input.push(dnn.Parameter.decode(reader, reader.uint32()));
break;
case 13:
message.output.push(dnn.Parameter.decode(reader, reader.uint32()));
break;
case 14:
message.a014 = reader.double();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
dnn.Model.prototype.name = "";
dnn.Model.prototype.version = 0;
dnn.Model.prototype.a014 = 0;
dnn.Parameter = class Parameter {
static decode(reader, length) {
const message = new dnn.Parameter();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.name = reader.string();
break;
case 2:
message.shape = dnn.Shape.decode(reader, reader.uint32());
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
dnn.Parameter.prototype.name = "";
dnn.Parameter.prototype.shape = null;
dnn.Shape = class Shape {
static decode(reader, length) {
const message = new dnn.Shape();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.dim0 = reader.int32();
break;
case 2:
message.dim1 = reader.int32();
break;
case 3:
message.dim2 = reader.int32();
break;
case 4:
message.dim3 = reader.int32();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
dnn.Shape.prototype.dim0 = 0;
dnn.Shape.prototype.dim1 = 0;
dnn.Shape.prototype.dim2 = 0;
dnn.Shape.prototype.dim3 = 0;
dnn.Node = class Node {
constructor() {
this.input = [];
this.output = [];
}
static decode(reader, length) {
const message = new dnn.Node();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.layer = dnn.Layer.decode(reader, reader.uint32());
break;
case 2:
message.input.push(reader.string());
break;
case 3:
message.output.push(reader.string());
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
dnn.Node.prototype.layer = null;
dnn.Layer = class Layer {
constructor() {
this.weight = [];
}
static decode(reader, length) {
const message = new dnn.Layer();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.name = reader.string();
break;
case 2:
message.type = reader.string();
break;
case 3:
message.filters = reader.int32();
break;
case 7:
message.a007 = reader.int32();
break;
case 8:
message.a008 = reader.int32();
break;
case 9:
message.groups = reader.int32();
break;
case 10:
message.a010 = reader.int32();
break;
case 11:
message.a011 = reader.int32();
break;
case 14:
message.slope = reader.float();
break;
case 15:
message.intercept = reader.float();
break;
case 50:
message.weight.push(dnn.Tensor.decode(reader, reader.uint32()));
break;
case 72:
message.operation = reader.int32();
break;
case 65:
message.axis = reader.int32();
break;
case 77:
message.a077 = reader.int32();
break;
case 79:
message.scale = reader.float();
break;
case 80:
message.pad_1 = reader.int32();
break;
case 81:
message.pad_2 = reader.int32();
break;
case 82:
message.pad_3 = reader.int32();
break;
case 83:
message.pad_4 = reader.int32();
break;
case 84:
message.pad_5 = reader.int32();
break;
case 85:
message.a085 = reader.int32();
break;
case 90:
message.a090 = reader.int32();
break;
case 101:
message.is_quantized = reader.bool();
break;
case 104:
message.quantization = dnn.Buffer.decode(reader, reader.uint32());
break;
case 109:
message.stride_w = reader.int32();
break;
case 110:
message.stride_h = reader.int32();
break;
case 111:
message.kernel_w = reader.int32();
break;
case 112:
message.kernel_h = reader.int32();
break;
case 115:
message.a115 = reader.int32();
break;
case 116:
message.a116 = reader.int32();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
dnn.Layer.prototype.name = "";
dnn.Layer.prototype.type = "";
dnn.Layer.prototype.filters = 0;
dnn.Layer.prototype.a007 = 0;
dnn.Layer.prototype.a008 = 0;
dnn.Layer.prototype.groups = 0;
dnn.Layer.prototype.a010 = 0;
dnn.Layer.prototype.a011 = 0;
dnn.Layer.prototype.slope = 0;
dnn.Layer.prototype.intercept = 0;
dnn.Layer.prototype.operation = 0;
dnn.Layer.prototype.axis = 0;
dnn.Layer.prototype.a077 = 0;
dnn.Layer.prototype.scale = 0;
dnn.Layer.prototype.pad_1 = 0;
dnn.Layer.prototype.pad_2 = 0;
dnn.Layer.prototype.pad_3 = 0;
dnn.Layer.prototype.pad_4 = 0;
dnn.Layer.prototype.pad_5 = 0;
dnn.Layer.prototype.a085 = 0;
dnn.Layer.prototype.a090 = 0;
dnn.Layer.prototype.is_quantized = false;
dnn.Layer.prototype.quantization = null;
dnn.Layer.prototype.stride_w = 0;
dnn.Layer.prototype.stride_h = 0;
dnn.Layer.prototype.kernel_w = 0;
dnn.Layer.prototype.kernel_h = 0;
dnn.Layer.prototype.a115 = 0;
dnn.Layer.prototype.a116 = 0;
dnn.Buffer = class Buffer {
static decode(reader, length) {
const message = new dnn.Buffer();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 5:
message.data = reader.bytes();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
dnn.Buffer.prototype.data = new Uint8Array([]);
dnn.Tensor = class Tensor {
static decode(reader, length) {
const message = new dnn.Tensor();
const end = length === undefined ? reader.length : reader.position + length;
while (reader.position < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.dim0 = reader.int32();
break;
case 2:
message.dim1 = reader.int32();
break;
case 3:
message.dim2 = reader.int32();
break;
case 4:
message.dim3 = reader.int32();
break;
case 5:
message.data = reader.bytes();
break;
case 6:
message.quantized_data = reader.bytes();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
}
};
dnn.Tensor.prototype.dim0 = 0;
dnn.Tensor.prototype.dim1 = 0;
dnn.Tensor.prototype.dim2 = 0;
dnn.Tensor.prototype.dim3 = 0;
dnn.Tensor.prototype.data = new Uint8Array([]);
dnn.Tensor.prototype.quantized_data = new Uint8Array([]);
+247
View File
@@ -0,0 +1,247 @@
// Experimental
const dnn = {};
dnn.ModelFactory = class {
async match(context) {
const tags = await context.tags('pb');
if (tags.get(4) === 0 && tags.get(10) === 2) {
return context.set('dnn');
}
return null;
}
async open(context) {
dnn.proto = await context.require('./dnn-proto');
dnn.proto = dnn.proto.dnn;
let model = null;
try {
const reader = await context.read('protobuf.binary');
model = dnn.proto.Model.decode(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new dnn.Error(`File format is not dnn.Graph (${message.replace(/\.$/, '')}).`);
}
const metadata = await context.metadata('dnn-metadata.json');
return new dnn.Model(metadata, model);
}
};
dnn.Model = class {
constructor(metadata, model) {
this.name = model.name || '';
this.format = `SnapML${model.version ? ` v${model.version}` : ''}`;
this.modules = [new dnn.Graph(metadata, model)];
}
};
dnn.Graph = class {
constructor(metadata, model) {
this.inputs = [];
this.outputs = [];
this.nodes = [];
const scope = {};
for (let i = 0; i < model.node.length; i++) {
const node = model.node[i];
node.input = node.input.map((input) => scope[input] ? scope[input] : input);
node.output = node.output.map((output) => {
scope[output] = scope[output] ? `${output}\n${i}` : output; // custom argument id
return scope[output];
});
}
const values = new Map();
values.map = (name, type) => {
if (!values.has(name)) {
values.set(name, new dnn.Value(name, type));
}
return values.get(name);
};
for (const input of model.input) {
const shape = input.shape;
const type = new dnn.TensorType('float32', new dnn.TensorShape([shape.dim0, shape.dim1, shape.dim2, shape.dim3]));
const argument = new dnn.Argument(input.name, [values.map(input.name, type)]);
this.inputs.push(argument);
}
for (const output of model.output) {
const shape = output.shape;
const type = new dnn.TensorType('float32', new dnn.TensorShape([shape.dim0, shape.dim1, shape.dim2, shape.dim3]));
const argument = new dnn.Argument(output.name, [values.map(output.name, type)]);
this.outputs.push(argument);
}
if (this.inputs.length === 0 && model.input_name && model.input_shape && model.input_shape.length === model.input_name.length * 4) {
for (let i = 0; i < model.input_name.length; i++) {
const name = model.input_name[i];
const shape = model.input_shape.slice(i * 4, (i * 4 + 4));
const type = new dnn.TensorType('float32', new dnn.TensorShape([shape[1], shape[3], shape[2], shape[0]]));
const argument = new dnn.Argument(name, [values.map(name, type)]);
this.inputs.push(argument);
}
}
if (this.inputs.length === 0 && model.input_shape && model.input_shape.length === 4 && model.node.length > 0 && model.node[0].input.length > 0) {
const [name] = model.node[0].input;
const shape = model.input_shape;
const type = new dnn.TensorType('float32', new dnn.TensorShape([shape[1], shape[3], shape[2], shape[0]]));
const argument = new dnn.Argument(name, [values.map(name, type)]);
this.inputs.push(argument);
}
for (const node of model.node) {
this.nodes.push(new dnn.Node(metadata, node, values));
}
}
};
dnn.Argument = class {
constructor(name, value) {
this.name = name;
this.value = value;
}
};
dnn.Value = class {
constructor(name, type = null, initializer = null, quantization = null) {
if (typeof name !== 'string') {
throw new dnn.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = type;
this.initializer = initializer;
if (quantization) {
this.quantization = {
type: 'lookup',
value: quantization
};
}
}
};
dnn.Node = class {
constructor(metadata, node, values) {
const layer = node.layer;
this.name = layer.name;
const type = layer.type;
this.type = metadata.type(type) || { name: type };
this.attributes = [];
this.inputs = [];
this.outputs = [];
const inputs = node.input.map((input) => values.map(input));
for (const weight of layer.weight) {
let quantization = null;
if (layer.is_quantized && weight === layer.weight[0] && layer.quantization && layer.quantization.data) {
const data = layer.quantization.data;
quantization = new Array(data.length >> 2);
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
for (let i = 0; i < quantization.length; i++) {
quantization[i] = view.getFloat32(i << 2, true);
}
}
const initializer = new dnn.Tensor(weight, quantization);
inputs.push(new dnn.Value('', initializer.type, initializer, quantization));
}
const outputs = node.output.map((output) => values.map(output));
if (inputs && inputs.length > 0) {
let inputIndex = 0;
if (this.type && this.type.inputs) {
for (const inputSchema of this.type.inputs) {
if (inputIndex < inputs.length || inputSchema.option !== 'optional') {
const inputCount = (inputSchema.option === 'variadic') ? (node.input.length - inputIndex) : 1;
const inputArguments = inputs.slice(inputIndex, inputIndex + inputCount);
this.inputs.push(new dnn.Argument(inputSchema.name, inputArguments));
inputIndex += inputCount;
}
}
}
this.inputs.push(...inputs.slice(inputIndex).map((input, index) => {
const inputName = ((inputIndex + index) === 0) ? 'input' : (inputIndex + index).toString();
return new dnn.Argument(inputName, [input]);
}));
}
if (outputs.length > 0) {
this.outputs = outputs.map((output, index) => {
const inputName = (index === 0) ? 'output' : index.toString();
return new dnn.Argument(inputName, [output]);
});
}
for (const [key, obj] of Object.entries(layer)) {
switch (key) {
case 'name':
case 'type':
case 'weight':
case 'is_quantized':
case 'quantization':
break;
default: {
const attribute = new dnn.Argument(key, obj);
this.attributes.push(attribute);
break;
}
}
}
}
};
dnn.Tensor = class {
constructor(weight, quantization) {
const shape = new dnn.TensorShape([weight.dim0, weight.dim1, weight.dim2, weight.dim3]);
this.values = quantization ? weight.quantized_data : weight.data;
const size = shape.dimensions.reduce((a, b) => a * b, 1);
const itemsize = Math.floor(this.values.length / size);
const remainder = this.values.length - (itemsize * size);
if (remainder < 0 || remainder > itemsize) {
throw new dnn.Error(`Invalid tensor data size '${this.values.length}' tensor shape '[${shape.dimensions}]' '.`);
}
let dataType = '?';
switch (itemsize) {
case 1: dataType = 'int8'; break;
case 2: dataType = 'float16'; break;
case 4: dataType = 'float32'; break;
default: dataType = '?'; break;
}
this.type = new dnn.TensorType(dataType, shape);
}
};
dnn.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType;
this.shape = shape;
}
toString() {
return this.dataType + this.shape.toString();
}
};
dnn.TensorShape = class {
constructor(shape) {
this.dimensions = shape;
}
toString() {
if (!this.dimensions || this.dimensions.length === 0) {
return '';
}
return `[${this.dimensions.join(',')}]`;
}
};
dnn.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading SnapML model.';
}
};
export const ModelFactory = dnn.ModelFactory;
+662
View File
@@ -0,0 +1,662 @@
const dot = {};
dot.ModelFactory = class {
async match(context) {
const reader = await context.read('text', 0x10000);
if (reader) {
try {
for (let i = 0; i < 64; i++) {
const line = reader.read('\n');
if (line === undefined) {
break;
}
if (line.trim().startsWith('//') || line.trim().startsWith('#')) {
continue;
}
if (line.trim().match(/^(strict)?\s*digraph/)) {
return context.set('dot');
}
}
} catch {
// continue regardless of error
}
}
return null;
}
async open(context) {
const decoder = await context.read('text.decoder');
const parser = new dot.Parser(decoder);
const graph = parser.parse();
if (graph.kind !== 'digraph') {
throw new dot.Error(`Graph type '${graph.type}' is not supported.`);
}
return new dot.Model(graph);
}
};
dot.Model = class {
constructor(graph) {
this.format = 'DOT';
this.modules = [new dot.Graph(graph)];
}
};
dot.Graph = class {
constructor(graph) {
this.name = graph.name || '';
this.nodes = [];
this.inputs = [];
this.outputs = [];
const values = new Map();
values.map = (name, type, tensor, metadata) => {
if (typeof name !== 'string') {
throw new dot.Error('Invalid value name.');
}
if (!values.has(name) || tensor) {
values.set(name, new dot.Value(name, type, tensor, metadata));
}
return values.get(name);
};
const nodes = new Map();
nodes.map = (name) => {
if (typeof name !== 'string') {
throw new dot.Error('Invalid node name.');
}
if (!nodes.has(name)) {
const node = {
kind: 'node',
name: { id: name, key: name },
type: { name },
inputs: [],
outputs: [],
attributes: new Map(),
metadata: new Map()
};
nodes.set(name, node);
}
return nodes.get(name);
};
for (const node of graph.statements) {
if (node.kind === 'node') {
node.inputs = [];
node.outputs = [];
node.metadata = new Map([...node.defaults, ...node.attributes]);
node.attributes = new Map();
delete node.defaults;
const metadata = node.metadata;
if (metadata.has('label')) {
const label = metadata.get('label');
if (label.startsWith('{') && label.endsWith('}')) {
const lines = label.substring(1, label.length - 1).split('|');
if (lines.length > 1 && node.name.id === lines[0] && lines[1].startsWith('op_code=')) {
const def = lines[1].split('\\l');
const op_code = def[0].split('=').pop();
node.type = { name: op_code };
if (op_code === 'call_module') {
node.type = { name: def[1], type: 'function' };
} else if (op_code === 'call_function') {
const vals = lines[2].split('\\l');
node.type = { name: vals[0] };
} else if (op_code.startsWith('get_parameter')) {
node.attributes.set('type', op_code.substring(13, op_code.length).trim());
node.type = { name: 'get_parameter' };
}
if (lines.length > 2) {
const attributes = lines[2].split('\\l');
for (const attribute of attributes) {
const parts = attribute.split(':');
if (parts.length === 2) {
const key = parts[0].trim();
let value = parts[1].trim();
if (value.startsWith('(') && value.endsWith(')')) {
value = JSON.parse(`[${value.substring(1, value.length - 1)}]`);
}
node.attributes.set(key, value);
}
}
}
metadata.delete('label');
} else if (lines.length === 1 && lines[0].startsWith('buffer\\l')) {
const def = lines[0].split('\\l');
node.type = { name: def[0] };
if (def.length > 1) {
node.attributes.set('type', def[1]);
}
metadata.delete('label');
}
} else {
const match = label.match(/^name:\s*([A-Za-z][A-Za-z0-9_]*)\stype:\s*([A-Za-z][A-Za-z0-9_]*)$/);
if (match && node.name.id === match[1]) {
node.type = { name: match[2] };
metadata.delete('label');
}
}
}
if (!node.type) {
const lines = node.name.id.split('\\n');
const match = lines[0].match(/^([A-Z][A-Za-z0-9_]*)\/([A-Z][A-Za-z0-9_]*)\s\(op#(\d+)\)$/);
if (match) {
node.type = { name: match[2] };
} else {
const match = lines[0].match(/^([A-Z][A-Za-z0-9_]*)\s\(op#(\d+)\)$/);
if (match) {
node.type = { name: match[1] };
} else {
// debugger;
}
}
}
if (!node.type) {
node.type = { name: node.name.id };
}
nodes.set(node.name.id, node);
}
}
for (const edge of graph.statements) {
if (edge.kind === 'edge') {
edge.uses = edge.uses || [];
const to = nodes.map(edge.to.id);
to.inputs.push(edge);
edge.uses.push(to);
edge.from = nodes.map(edge.name.id);
edge.from.outputs.push(edge);
}
}
for (const [key, node] of nodes) {
const keys = new Set(['pos', 'height', 'width', 'shape', 'label']);
if (node.metadata.get('shape') === 'octagon' && node.metadata.keys().every((key) => keys.has(key)) &&
node.inputs.length === 1 && node.inputs[0].uses.length === 1 && node.inputs[0].from.outputs.length === 1 && node.inputs[0].from.outputs[0].uses.length === 1 &&
new Set(node.outputs.map((output) => output.name.id)).size === 1 && node.outputs.every((output) => output.uses.length === 1)) {
const [from] = node.inputs[0].from.outputs;
for (const e of node.outputs) {
const [n] = e.uses;
n.inputs = n.inputs.map((edge) => edge === e ? from : edge);
}
nodes.delete(key);
}
}
for (const [key, node] of nodes) {
if ((node.type.name === 'get_parameter' || node.type.name === 'buffer' || node.type.name === 'Constant') &&
node.inputs.length === 0 &&
node.outputs.length === 1 && node.outputs[0].uses.length === 1) {
node.outputs[0].initializer = node;
nodes.delete(key);
}
}
for (const [, obj] of nodes) {
const node = new dot.Node(obj, values);
this.nodes.push(node);
}
for (const edge of graph.statements) {
if (edge.kind === 'edge') {
const value = values.map(edge.name.id);
const metadata = new Map([...edge.defaults, ...edge.attributes]);
value.metadata = Array.from(metadata).map(([key, value]) => new dot.Argument(key, value));
}
}
}
};
dot.Argument = class {
constructor(name, value, type = null) {
this.name = name;
this.value = value;
this.type = type;
}
};
dot.Value = class {
constructor(name, type, initializer, metadata) {
this.name = name;
this.type = !type && initializer ? initializer.type : type;
this.initializer = initializer || null;
this.metadata = metadata;
}
};
dot.Node = class {
constructor(node, values) {
this.name = node.name.key;
this.type = node.type;
this.inputs = [];
this.outputs = [];
this.attributes = [];
this.metadata = [];
for (let i = 0; i < node.inputs.length; i++) {
const edge = node.inputs[i];
const initializer = edge.initializer ? new dot.Tensor(edge.initializer) : null;
const value = values.map(edge.name.key, null, initializer);
const argument = new dot.Argument(i.toString(), [value]);
this.inputs.push(argument);
}
for (let i = 0; i < node.outputs.length; i++) {
const edge = node.outputs[i];
const value = values.map(edge.name.key);
const argument = new dot.Argument(i.toString(), [value]);
this.outputs.push(argument);
}
for (const [name, value] of node.attributes) {
const argument = new dot.Argument(name, value, 'attribute');
this.attributes.push(argument);
}
for (const [name, value] of node.metadata) {
const argument = new dot.Argument(name, value);
this.metadata.push(argument);
}
}
};
dot.TensorType = class {
constructor(type) {
const index = type.indexOf('[');
const dtype = type.substring(0, index);
this.dataType = dtype.split('.').pop();
if (index > 0) {
const dimensions = JSON.parse(type.substring(index, type.length));
this.shape = new dot.TensorShape(dimensions);
} else {
this.shape = new dot.TensorShape([]);
}
}
toString() {
return this.dataType + this.shape.toString();
}
};
dot.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
toString() {
if (this.dimensions && this.dimensions.length > 0) {
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
return '';
}
};
dot.Tensor = class {
constructor(stmt) {
if (stmt.attributes.has('type')) {
const type = stmt.attributes.get('type');
this.type = new dot.TensorType(type);
} else {
this.type = new dot.TensorType('?');
}
}
};
dot.Parser = class {
constructor(decoder) {
// https://graphviz.org/doc/info/lang.html
this._tokenizer = new dot.Tokenizer(decoder);
this._token = this._tokenizer.read();
}
parse() {
const graph = {};
if (this._eat('id', 'strict')) {
graph.strict = true;
}
let edgeop = '';
if (this._match('id', 'graph')) {
graph.kind = this._read();
edgeop = '--';
} else if (this._match('id', 'digraph')) {
graph.kind = this._read();
edgeop = '->';
} else {
throw new dot.Error('Invalid graph type.');
}
if (this._match('id')) {
graph.name = this._read();
}
const defaults = {};
defaults.graph = new Map();
defaults.node = new Map();
defaults.edge = new Map();
graph.statements = this._parseBlock(defaults, edgeop, 0);
graph.defaults = new Map(defaults.graph);
return graph;
}
_parseBlock(defaults, edgeop) {
defaults = {
graph: new Map(defaults.graph),
node: new Map(defaults.node),
edge: new Map(defaults.edge)
};
const list = [];
this._read('{');
while (!this._match('}')) {
if (this._eat('id', 'subgraph')) {
const stmt = {};
stmt.kind = 'subgraph';
if (this._match('id')) {
stmt.name = this._read();
}
stmt.statements = this._parseBlock(defaults, edgeop);
} else if (this._match('{')) {
const stmt = {};
const statements = this._parseBlock(defaults, edgeop);
if (this._eat(edgeop)) {
if (!statements.every((stmt) => stmt.kind === 'node' && stmt.attributes.size === 0)) {
throw new dot.Error('Invalid edge group statement.');
}
const sources = statements.map((stmt) => stmt.name);
list.push(...this._parseEdges(sources, edgeop, defaults.edge));
} else {
stmt.kind = 'subgraph';
stmt.statements = statements;
}
} else if (this._match('id')) {
const name = this._parseNodeId();
if (this._eat('=')) { // attr
if (this._match('id')) {
const value = this._read();
defaults.graph.set(name, value);
} else {
throw new dot.Error('Invalid attribute value.');
}
} else if (this._eat(edgeop)) {
list.push(...this._parseEdges([name], edgeop, defaults.edge));
} else {
const attributes = this._parseAttributes();
if (name.key === 'node' || name.key === 'edge' || name.key === 'graph') {
for (const [key, value] of attributes) {
defaults[name.key].set(key, value);
}
} else {
list.push({ kind: 'node', name, attributes, defaults: new Map(defaults.node) });
}
}
}
if (this._match(';') || this._match(',')) {
this._read();
}
}
this._read('}');
return list;
}
_parseNodeIds() {
const list = [];
const open = this._eat('{');
while (!this._match('}')) {
const value = this._parseNodeId();
list.push(value);
if (this._match(',')) {
this._read();
continue;
} else if (this._match(';')) {
this._read();
if (!open) {
break;
}
} else if (!open) {
break;
}
}
if (open) {
this._read('}');
}
return list;
}
_parseNodeId() {
const name = {};
const list = [];
name.id = this._read('id');
list.push(name.id);
if (this._eat(':')) {
name.port = this._read('id');
list.push(name.port);
if (this._eat(':')) {
name.compass_pt = this._read('id');
list.push(name.compass_pt);
}
}
name.key = list.join(':');
return name;
}
_parseAttributes() {
const table = new Map();
if (this._eat('[')) {
while (this._match('id')) {
const name = this._read('id');
this._read('=');
const value = this._read('id');
table.set(name, value);
if (this._match(';') || this._match(',')) {
this._read();
}
}
this._read(']');
}
return table;
}
_parseEdges(sources, edgeop, defaults) {
const list = [];
do {
const targets = this._parseNodeIds();
for (const name of sources) {
for (const to of targets) {
list.push({ kind: 'edge', name, to });
}
}
sources = targets;
} while (this._eat(edgeop));
const attributes = this._parseAttributes();
for (const edge of list) {
edge.attributes = attributes;
edge.defaults = new Map(defaults.edge);
}
return list;
}
_match(kind, value) {
return (this._token.kind === kind && (!value || this._token.value === value));
}
_read(kind, value) {
if (kind && this._token.kind !== kind) {
throw new dot.Error(`Expected token of type '${kind}', but got '${this._token.kind}' ${this._tokenizer.location()}`);
}
if (value && this._token.value !== value) {
throw new dot.Error(`Expected token with value '${value}', but got '${this._token.value}' ${this._tokenizer.location()}`);
}
const token = this._token;
this._token = this._tokenizer.read();
return token.value;
}
_eat(kind, value) {
if (this._match(kind, value)) {
return this._read();
}
return null;
}
};
dot.Tokenizer = class {
constructor(decoder) {
this._decoder = decoder;
this._position = 0;
this._char = this._decoder.decode();
}
_read() {
if (this._char === undefined) {
this._unexpected();
}
const char = this._char;
this._position = this._decoder.position;
this._char = this._decoder.decode();
return char;
}
_peek() {
const position = this._decoder.position;
const char = this._decoder.decode();
this._decoder.position = position;
return char;
}
read() {
while (this._char) {
if (/\s/.test(this._char)) {
this._skipWhitespace();
continue;
}
if (this._char === '/' || this._char === '#') {
this._skipComment();
continue;
}
if (/[{}[\]=:;,]/.test(this._char)) {
const value = this._read();
return { kind: value, value };
} else if (this._char === '-') {
let value = this._read();
if (this._char === '>' || this._char === '-') {
value += this._read();
return { kind: value, value };
}
throw new dot.Error(`Unexpected character '${value}' ${this.location()}`);
} else if (/[a-zA-Z0-9_$"<]/.test(this._char)) {
const value = this._identifier();
return { kind: 'id', value };
} else {
throw new dot.Error(`Unexpected character '${this._char}' ${this.location()}`);
}
}
return { type: 'eof' };
}
_skipWhitespace() {
while (this._char !== undefined && /\s/.test(this._char)) {
this._read();
}
}
_skipComment() {
if (this._char === '#' || (this._char === '/' && this._peek() === '/')) {
while (this._char && this._char !== '\n') {
this._read();
}
return;
}
if (this._char === '/' && this._peek() === '*') {
while (this._char && (this._char !== '*' || this._peek() !== '/')) {
this._read();
}
this._read();
this._read();
return;
}
throw new dot.Error('Invalid comment.');
}
_identifier() {
let value = '';
if (this._char === '"') { // double quoted string
this._read();
while (this._char && this._char !== '"') {
value += this._read();
}
this._read('"');
} if (this._char === '<') { // HTML String
value += this._read();
let depth = 0;
while (depth > 0 || this._char !== '>') {
const c = this._read();
value += c;
if (c === '<') {
depth += 1;
} else if (c === '>') {
depth -= 1;
}
}
value += this._read();
} else {
while (/[a-zA-Z0-9_$.*]/.test(this._char)) {
value += this._read();
}
}
return value;
}
_unexpected() {
let c = this._char;
if (c === undefined) {
throw new dot.Error('Unexpected end of input.');
} else if (c === '"') {
c = 'string';
} else if ((c >= '0' && c <= '9') || c === '-') {
c = 'number';
} else {
if (c < ' ' || c > '\x7F') {
const name = Object.keys(this._escape).filter((key) => this._escape[key] === c);
c = (name.length === 1) ? `\\${name}` : `\\u${(`000${c.charCodeAt(0).toString(16)}`).slice(-4)}`;
}
c = `token '${c}'`;
}
this._throw(`Unexpected ${c}`);
}
_throw(message) {
message = message.replace(/\.$/, '');
throw new dot.Error(`${message} ${this._location()}`);
}
location() {
let line = 1;
let column = 1;
const position = this._decoder.position;
this._decoder.position = 0;
let c = '';
do {
if (this._decoder.position === this._position) {
this._decoder.position = position;
return `at ${line}:${column}.`;
}
c = this._decoder.decode();
if (c === '\n') {
line++;
column = 1;
} else {
column++;
}
}
while (c !== undefined);
this._decoder.position = position;
return `at ${line}:${column}.`;
}
};
dot.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loadig DOT graph';
}
};
export const ModelFactory = dot.ModelFactory;
+321
View File
@@ -0,0 +1,321 @@
[
{
"name": "Add",
"module": "espdl",
"version": 1,
"description": "Element-wise addition of two tensors.",
"inputs": [
{ "name": "A", "description": "First input tensor" },
{ "name": "B", "description": "Second input tensor" }
],
"outputs": [
{ "name": "C", "description": "Output tensor" }
],
"category": "Tensor"
},
{
"name": "AveragePool",
"module": "espdl",
"version": 1,
"description": "Average pooling operation.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
],
"category": "Pool"
},
{
"name": "BatchNormalization",
"module": "espdl",
"version": 1,
"description": "Batch normalization.",
"inputs": [
{ "name": "X", "description": "Input data tensor" },
{ "name": "scale", "description": "Scale tensor" },
{ "name": "B", "description": "Bias tensor" },
{ "name": "mean", "description": "Mean tensor" },
{ "name": "var", "description": "Variance tensor" }
],
"outputs": [
{ "name": "Y", "description": "Output data tensor" },
{ "name": "mean", "description": "Updated mean tensor (optional)" },
{ "name": "var", "description": "Updated variance tensor (optional)" },
{ "name": "saved_mean", "description": "Saved mean tensor (optional)" },
{ "name": "saved_var", "description": "Saved variance tensor (optional)" }
],
"category": "Normalization"
},
{
"name": "Clip",
"module": "espdl",
"version": 1,
"category": "Activation",
"description": "Clip operator limits values to a specified range.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Clipped output tensor" }
]
},
{
"name": "Concat",
"module": "espdl",
"version": 1,
"description": "Concatenates tensors along a given axis.",
"inputs": [
{ "name": "inputs", "list": true, "description": "Input tensors to concatenate" }
],
"outputs": [
{ "name": "output", "description": "Concatenated output tensor" }
],
"category": "Tensor"
},
{
"name": "Conv",
"module": "espdl",
"version": 1,
"description": "Convolution operator. Applies a convolution filter to the input.",
"inputs": [
{ "name": "input", "description": "Input feature map" },
{ "name": "weight", "description": "Convolution kernel weights" },
{ "name": "bias", "option": "optional", "description": "Bias values (optional)" }
],
"outputs": [
{ "name": "output", "description": "Output feature map" }
],
"category": "Layer"
},
{
"name": "Gemm",
"module": "espdl",
"version": 1,
"description": "General matrix multiplication: alpha * A * B + beta * C",
"inputs": [
{ "name": "A", "description": "Input tensor A" },
{ "name": "B", "description": "Input tensor B" },
{ "name": "C", "option": "optional", "description": "Input tensor C (optional)" }
],
"outputs": [
{ "name": "Y", "description": "Output tensor" }
],
"category": "Layer"
},
{
"name": "GlobalAveragePool",
"module": "espdl",
"version": 1,
"description": "Global average pooling operation for temporal data.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
],
"category": "Pool"
},
{
"name": "HardSwish",
"module": "espdl",
"version": 1,
"category": "Activation",
"description": "Hard swish activation function.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
]
},
{
"name": "LeakyRelu",
"module": "espdl",
"version": 1,
"category": "Activation",
"description": "Leaky Rectified Linear Unit activation function.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
]
},
{
"name": "MaxPool",
"module": "espdl",
"version": 1,
"description": "Max pooling operation.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
],
"category": "Pool"
},
{
"name": "Mul",
"module": "espdl",
"version": 1,
"description": "Element-wise multiplication of two tensors.",
"inputs": [
{ "name": "A", "description": "First input tensor" },
{ "name": "B", "description": "Second input tensor" }
],
"outputs": [
{ "name": "C", "description": "Output tensor" }
],
"category": "Tensor"
},
{
"name": "Pad",
"module": "espdl",
"version": 1,
"category": "Tensor",
"description": "Pad operator adds padding to tensor dimensions.",
"inputs": [
{ "name": "input", "description": "Input tensor" },
{ "name": "pads", "description": "Padding values" }
],
"outputs": [
{ "name": "output", "description": "Padded output tensor" }
]
},
{
"name": "Relu",
"module": "espdl",
"version": 1,
"description": "Rectified Linear Unit activation function.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
],
"category": "Activation"
},
{
"name": "RequantizeLinear",
"module": "espdl",
"version": 1,
"category": "Quantization",
"description": "Requantize linear quantization operator.",
"inputs": [
{ "name": "input", "description": "Input tensor to requantize" },
{ "name": "scale", "description": "Scale for requantization" },
{ "name": "zero_point", "description": "Zero point for requantization" }
],
"outputs": [
{ "name": "output", "description": "Requantized output tensor" }
]
},
{
"name": "Reshape",
"module": "espdl",
"version": 1,
"description": "Reshapes a tensor to a new shape.",
"inputs": [
{ "name": "data", "description": "Input tensor" },
{ "name": "shape", "description": "New shape" }
],
"outputs": [
{ "name": "reshaped", "description": "Reshaped output tensor" }
],
"category": "Shape"
},
{
"name": "Resize",
"module": "espdl",
"version": 1,
"category": "Data",
"description": "Resize operator for spatial dimensions.",
"inputs": [
{ "name": "input", "description": "Input tensor" },
{ "name": "scales", "description": "Scale factors for each dimension" }
],
"outputs": [
{ "name": "output", "description": "Resized output tensor" }
]
},
{
"name": "Sigmoid",
"module": "espdl",
"version": 1,
"description": "Sigmoid activation function.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
],
"category": "Activation"
},
{
"name": "Softmax",
"module": "espdl",
"version": 1,
"description": "Softmax activation function.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
],
"category": "Activation"
},
{
"name": "Split",
"module": "espdl",
"version": 1,
"description": "Splits a tensor into multiple tensors along a given axis.",
"inputs": [
{ "name": "input", "description": "Input tensor to split" },
{ "name": "split", "description": "Optional list of split sizes or number of splits" }
],
"outputs": [
{ "name": "outputs", "list": true, "description": "Output tensors" }
],
"category": "Tensor"
},
{
"name": "Swish",
"module": "espdl",
"version": 1,
"category": "Activation",
"description": "Swish activation function.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
]
},
{
"name": "Tanh",
"module": "espdl",
"version": 1,
"description": "Hyperbolic tangent activation function.",
"inputs": [
{ "name": "input", "description": "Input tensor" }
],
"outputs": [
{ "name": "output", "description": "Output tensor" }
],
"category": "Activation"
},
{
"name": "Transpose",
"module": "espdl",
"version": 1,
"description": "Transposes the dimensions of a tensor.",
"inputs": [
{ "name": "data", "description": "Input tensor" }
],
"outputs": [
{ "name": "transposed", "description": "Transposed output tensor" }
],
"category": "Tensor"
}
]
+329
View File
@@ -0,0 +1,329 @@
export const espdl = {};
espdl.Version = {
_START_VERSION: 0, '0': '_START_VERSION',
IR_VERSION_2023_12_22: 1, '1': 'IR_VERSION_2023_12_22'
};
espdl.AttributeType = {
UNDEFINED: 0, '0': 'UNDEFINED',
FLOAT: 1, '1': 'FLOAT',
INT: 2, '2': 'INT',
STRING: 3, '3': 'STRING',
TENSOR: 4, '4': 'TENSOR',
GRAPH: 5, '5': 'GRAPH',
FLOATS: 6, '6': 'FLOATS',
INTS: 7, '7': 'INTS',
STRINGS: 8, '8': 'STRINGS',
TENSORS: 9, '9': 'TENSORS',
GRAPHS: 10, '10': 'GRAPHS',
TYPE_FBS: 11, '11': 'TYPE_FBS',
TYPE_FBSS: 12, '12': 'TYPE_FBSS'
};
espdl.TensorDataType = {
UNDEFINED: 0, '0': 'UNDEFINED',
FLOAT: 1, '1': 'FLOAT',
UINT8: 2, '2': 'UINT8',
INT8: 3, '3': 'INT8',
UINT16: 4, '4': 'UINT16',
INT16: 5, '5': 'INT16',
INT32: 6, '6': 'INT32',
INT64: 7, '7': 'INT64',
STRING: 8, '8': 'STRING',
BOOL: 9, '9': 'BOOL',
FLOAT16: 10, '10': 'FLOAT16',
DOUBLE: 11, '11': 'DOUBLE',
UINT32: 12, '12': 'UINT32',
UINT64: 13, '13': 'UINT64'
};
espdl.DataLocation = {
DEFAULT: 0, '0': 'DEFAULT',
EXTERNAL: 1, '1': 'EXTERNAL'
};
espdl.AttributeF = class AttributeF {
static decode(reader, position) {
const $ = new espdl.AttributeF();
$.f = reader.float32(position + 0);
return $;
}
};
espdl.AttributeI = class AttributeI {
static decode(reader, position) {
const $ = new espdl.AttributeI();
$.i = reader.int64(position + 0);
return $;
}
};
espdl.Attribute = class Attribute {
static decode(reader, position) {
const $ = new espdl.Attribute();
$.name = reader.string_(position, 4, null);
$.ref_attr_name = reader.string_(position, 6, null);
$.doc_string = reader.string_(position, 8, null);
$.attr_type = reader.int32_(position, 10, 0);
$.f = reader.struct(position, 12, espdl.AttributeF);
$.i = reader.struct(position, 14, espdl.AttributeI);
$.s = reader.array(position, 16, Uint8Array);
$.t = reader.table(position, 18, espdl.Tensor);
$.g = reader.table(position, 20, espdl.Graph);
$.tp = reader.table(position, 22, espdl.TypeInfo);
$.floats = reader.array(position, 24, Float32Array);
$.ints = reader.int64s_(position, 26);
$.strings = reader.strings_(position, 28);
$.tensors = reader.tables(position, 30, espdl.Tensor);
$.graphs = reader.tables(position, 32, espdl.Graph);
$.type_protos = reader.tables(position, 34, espdl.TypeInfo);
return $;
}
};
espdl.ValueInfo = class ValueInfo {
static decode(reader, position) {
const $ = new espdl.ValueInfo();
$.name = reader.string_(position, 4, null);
$.value_info_type = reader.table(position, 6, espdl.TypeInfo);
$.doc_string = reader.string_(position, 8, null);
$.exponents = reader.int64s_(position, 10);
return $;
}
};
espdl.Node = class Node {
static decode(reader, position) {
const $ = new espdl.Node();
$.input = reader.strings_(position, 4);
$.output = reader.strings_(position, 6);
$.name = reader.string_(position, 8, null);
$.op_type = reader.string_(position, 10, null);
$.domain = reader.string_(position, 12, null);
$.attribute = reader.tables(position, 14, espdl.Attribute);
$.doc_string = reader.string_(position, 16, null);
return $;
}
};
espdl.Model = class Model {
static create(reader) {
return espdl.Model.decode(reader, reader.root);
}
static decode(reader, position) {
const $ = new espdl.Model();
$.ir_version = reader.int32_(position, 4, 0);
$.opset_import = reader.tables(position, 6, espdl.OperatorSetId);
$.producer_name = reader.string_(position, 8, null);
$.producer_version = reader.string_(position, 10, null);
$.domain = reader.string_(position, 12, null);
$.model_version = reader.int64_(position, 14, 0n);
$.doc_string = reader.string_(position, 16, null);
$.graph = reader.table(position, 18, espdl.Graph);
$.metadata_props = reader.tables(position, 20, espdl.StringStringEntry);
$.functions = reader.tables(position, 22, espdl.Function);
return $;
}
};
espdl.StringStringEntry = class StringStringEntry {
static decode(reader, position) {
const $ = new espdl.StringStringEntry();
$.key = reader.string_(position, 4, null);
$.value = reader.string_(position, 6, null);
return $;
}
};
espdl.TensorAnnotation = class TensorAnnotation {
static decode(reader, position) {
const $ = new espdl.TensorAnnotation();
$.tensor_name = reader.string_(position, 4, null);
$.quant_parameter_tensor_names = reader.tables(position, 6, espdl.StringStringEntry);
return $;
}
};
espdl.Graph = class Graph {
static decode(reader, position) {
const $ = new espdl.Graph();
$.node = reader.tables(position, 4, espdl.Node);
$.name = reader.string_(position, 6, null);
$.initializer = reader.tables(position, 8, espdl.Tensor);
$.doc_string = reader.string_(position, 10, null);
$.input = reader.tables(position, 12, espdl.ValueInfo);
$.output = reader.tables(position, 14, espdl.ValueInfo);
$.value_info = reader.tables(position, 16, espdl.ValueInfo);
$.quantization_annotation = reader.tables(position, 18, espdl.TensorAnnotation);
$.test_inputs_value = reader.tables(position, 20, espdl.Tensor);
$.test_outputs_value = reader.tables(position, 22, espdl.Tensor);
return $;
}
};
espdl.AlignedBytes = class AlignedBytes {
static decode(reader, position) {
const $ = new espdl.AlignedBytes();
$.bytes = reader.read(position + 0, 16);
return $;
}
};
espdl.Tensor = class Tensor {
static decode(reader, position) {
const $ = new espdl.Tensor();
$.dims = reader.int64s_(position, 4);
$.data_type = reader.int32_(position, 6, 0);
$.float_data = reader.array(position, 8, Float32Array);
$.int32_data = reader.array(position, 10, Int32Array);
$.string_data = reader.strings_(position, 12);
$.int64_data = reader.int64s_(position, 14);
$.name = reader.string_(position, 16, null);
$.doc_string = reader.string_(position, 18, null);
$.raw_data = reader.structs(position, 20, espdl.AlignedBytes, 16);
$.external_data = reader.tables(position, 22, espdl.StringStringEntry);
$.data_location = reader.int32_(position, 24, 0);
$.double_data = reader.array(position, 26, Float64Array);
$.uint64_data = reader.uint64s_(position, 28);
$.exponents = reader.int64s_(position, 30);
return $;
}
};
espdl.TensorShape = class TensorShape {
static decode(reader, position) {
const $ = new espdl.TensorShape();
$.dim = reader.tables(position, 4, espdl.Dimension);
return $;
}
};
espdl.Dimension = class Dimension {
static decode(reader, position) {
const $ = new espdl.Dimension();
$.value = reader.table(position, 4, espdl.DimensionValue);
$.denotation = reader.string_(position, 6, null);
return $;
}
};
espdl.DimensionValueType = {
UNKNOWN: 0, '0': 'UNKNOWN',
VALUE: 1, '1': 'VALUE',
PARAM: 2, '2': 'PARAM'
};
espdl.DimensionValue = class DimensionValue {
static decode(reader, position) {
const $ = new espdl.DimensionValue();
$.dim_type = reader.int8_(position, 4, 0);
$.dim_value = reader.int64_(position, 6, 0n);
$.dim_param = reader.string_(position, 8, null);
return $;
}
};
espdl.TensorTypeAndShape = class TensorTypeAndShape {
static decode(reader, position) {
const $ = new espdl.TensorTypeAndShape();
$.elem_type = reader.int32_(position, 4, 0);
$.shape = reader.table(position, 6, espdl.TensorShape);
return $;
}
};
espdl.SequenceType = class SequenceType {
static decode(reader, position) {
const $ = new espdl.SequenceType();
$.elem_type = reader.table(position, 4, espdl.TypeInfo);
return $;
}
};
espdl.MapType = class MapType {
static decode(reader, position) {
const $ = new espdl.MapType();
$.key_type = reader.int32_(position, 4, 0);
$.value_type = reader.table(position, 6, espdl.TypeInfo);
return $;
}
};
espdl.OptionalType = class OptionalType {
static decode(reader, position) {
const $ = new espdl.OptionalType();
$.elem_type = reader.table(position, 4, espdl.TypeInfo);
return $;
}
};
espdl.TypeInfoValue = class {
static decode(reader, position, type) {
switch (type) {
case 1: return espdl.TensorTypeAndShape.decode(reader, position);
case 2: return espdl.SequenceType.decode(reader, position);
case 3: return espdl.MapType.decode(reader, position);
case 4: return espdl.OptionalType.decode(reader, position);
default: return undefined;
}
}
};
espdl.TypeInfo = class TypeInfo {
static decode(reader, position) {
const $ = new espdl.TypeInfo();
$.value = reader.union(position, 4, espdl.TypeInfoValue);
$.denotation = reader.string_(position, 8, null);
return $;
}
};
espdl.OperatorSetId = class OperatorSetId {
static decode(reader, position) {
const $ = new espdl.OperatorSetId();
$.domain = reader.string_(position, 4, null);
$.version = reader.int64_(position, 6, 0n);
return $;
}
};
espdl.Function = class Function {
static decode(reader, position) {
const $ = new espdl.Function();
$.name = reader.string_(position, 4, null);
$.input = reader.strings_(position, 6);
$.output = reader.strings_(position, 8);
$.attribute = reader.strings_(position, 10);
$.attribute_proto = reader.tables(position, 12, espdl.Attribute);
$.node = reader.tables(position, 14, espdl.Node);
$.doc_string = reader.string_(position, 16, null);
$.opset_import = reader.tables(position, 18, espdl.OperatorSetId);
$.domain = reader.string_(position, 20, null);
return $;
}
};
+404
View File
@@ -0,0 +1,404 @@
import * as flatbuffers from './flatbuffers.js';
const espdl = {};
espdl.ModelFactory = class {
async match(context) {
const identifier = context.identifier;
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
if (extension === 'espdl') {
const stream = context.stream;
if (stream && stream.length >= 16) {
const buffer = stream.peek(16);
const header = String.fromCharCode(...buffer.slice(0, 4));
if (header === 'EDL2') {
return context.set('espdl.binary', null);
}
}
}
return null;
}
async open(context) {
espdl.schema = await context.require('./espdl-schema');
espdl.schema = espdl.schema.espdl;
const stream = context.stream;
const reader = flatbuffers.BinaryReader.open(stream, 16);
let model = null;
try {
model = espdl.schema.Model.create(reader);
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new espdl.Error(`File format is not espdl.Model (${message.replace(/\.$/, '')}).`);
}
const metadata = await espdl.Metadata.open(context);
return new espdl.Model(metadata, model, stream);
}
};
espdl.Model = class {
constructor(metadata, model, stream) {
this.format = `ESP-DL v${model.ir_version}`;
this.description = model.doc_string || '';
this.modules = [];
this.metadata = [];
if (model.metadata_props) {
for (const prop of model.metadata_props) {
this.metadata.push(new espdl.Argument(prop.key, prop.value));
}
}
if (model.graph) {
const graph = new espdl.Graph(metadata, model.graph, model, stream);
this.modules.push(graph);
}
}
};
espdl.Graph = class {
constructor(metadata, graph) {
this.name = graph.name || '';
this.inputs = [];
this.outputs = [];
this.nodes = [];
this.signatures = [];
const context = new espdl.Context(graph);
if (graph.node) {
for (let i = 0; i < graph.node.length; i++) {
const node = graph.node[i];
const nodeObj = new espdl.Node(metadata, context, node, i.toString());
this.nodes.push(nodeObj);
}
}
if (graph.input) {
for (let i = 0; i < graph.input.length; i++) {
const valueInfo = graph.input[i];
const tensor = context.initializer(valueInfo.name);
if (!tensor) {
const value = context.value(valueInfo.name);
const values = value ? [value] : [];
const argument = new espdl.Argument(valueInfo.name, values);
this.inputs.push(argument);
}
}
}
if (graph.output) {
for (let i = 0; i < graph.output.length; i++) {
const valueInfo = graph.output[i];
const value = context.value(valueInfo.name);
const values = value ? [value] : [];
const argument = new espdl.Argument(valueInfo.name, values);
this.outputs.push(argument);
}
}
}
};
espdl.Argument = class {
constructor(name, value, type, visible) {
this.name = name;
this.value = value;
this.type = type || null;
this.visible = visible !== false;
}
};
espdl.Value = class {
constructor(name, type, initializer) {
this.name = name;
this.type = type || null;
this.initializer = initializer || null;
}
};
espdl.Node = class {
constructor(metadata, context, node, identifier) {
this.name = node.name || '';
this.identifier = identifier;
this.type = null;
if (metadata) {
this.type = metadata.type('espdl', node.op_type);
}
if (!this.type) {
this.type = { name: node.op_type };
}
this.inputs = [];
this.outputs = [];
this.attributes = [];
if (node.input) {
for (let i = 0; i < node.input.length;) {
const inputMeta = this.type && Array.isArray(this.type.inputs) && i < this.type.inputs.length ? this.type.inputs[i] : { name: i.toString() };
const count = inputMeta.list ? node.input.length - i : 1;
const list = node.input.slice(i, i + count);
const values = list.map((inputName) => {
if (!inputName) {
return null;
}
return context.value(inputName);
}).filter((v) => v);
const argument = new espdl.Argument(inputMeta.name, values);
this.inputs.push(argument);
i += count;
}
}
if (node.output) {
for (let i = 0; i < node.output.length;) {
const outputMeta = this.type && Array.isArray(this.type.outputs) && i < this.type.outputs.length ? this.type.outputs[i] : { name: i.toString() };
const count = outputMeta.list ? node.output.length - i : 1;
const list = node.output.slice(i, i + count);
const values = list.map((outputName) => {
if (!outputName) {
return null;
}
return context.value(outputName);
}).filter((v) => v);
const argument = new espdl.Argument(outputMeta.name, values);
this.outputs.push(argument);
i += count;
}
}
if (node.attribute) {
for (const attr of node.attribute) {
const name = attr.name || '';
let value = null;
let type = null;
switch (attr.attr_type) {
case espdl.schema.AttributeType.FLOAT:
value = attr.f ? attr.f.f : 0;
type = 'float32';
break;
case espdl.schema.AttributeType.INT:
value = attr.i ? Number(attr.i.i) : 0;
type = 'int64';
break;
case espdl.schema.AttributeType.STRING:
value = attr.s ? new TextDecoder('utf-8').decode(attr.s) : '';
type = 'string';
break;
case espdl.schema.AttributeType.TENSOR:
value = attr.t ? new espdl.Tensor(0, attr.t) : null;
type = 'tensor';
break;
case espdl.schema.AttributeType.FLOATS:
value = attr.floats ? Array.from(attr.floats) : [];
type = 'float32[]';
break;
case espdl.schema.AttributeType.INTS:
value = attr.ints ? Array.from(attr.ints).map((i) => Number(i)) : [];
type = 'int64[]';
break;
case espdl.schema.AttributeType.STRINGS:
value = attr.strings ? attr.strings.map((s) => new TextDecoder('utf-8').decode(s)) : [];
type = 'string[]';
break;
default:
break;
}
const attribute = new espdl.Argument(name, value, type);
this.attributes.push(attribute);
}
}
}
};
espdl.Tensor = class {
constructor(index, tensor) {
this.identifier = index.toString();
this.name = tensor.name || '';
this.type = new espdl.TensorType(tensor);
this.category = '';
this.encoding = this.type.dataType === 'string' ? '|' : '<';
this.values = null;
if (tensor.float_data && tensor.float_data.length > 0) {
this.values = new Float32Array(tensor.float_data);
} else if (tensor.int32_data && tensor.int32_data.length > 0) {
this.values = new Int32Array(tensor.int32_data);
} else if (tensor.int64_data && tensor.int64_data.length > 0) {
this.values = new BigInt64Array(tensor.int64_data);
} else if (tensor.string_data && tensor.string_data.length > 0) {
this.values = tensor.string_data;
} else if (tensor.raw_data && tensor.raw_data.length > 0) {
const length = tensor.raw_data.length * 16;
const data = new Uint8Array(length);
for (let i = 0; i < tensor.raw_data.length; i++) {
data.set(tensor.raw_data[i].bytes, i * 16);
}
this.values = data;
}
}
};
espdl.TensorType = class {
constructor(tensor) {
let dataType = '';
if (tensor.value_info_type === undefined) {
dataType = tensor.data_type;
this.shape = new espdl.TensorShape(tensor.dims ? Array.from(tensor.dims).map((d) => Number(d)) : []);
} else {
const value = tensor.value_info_type.value;
dataType = value ? value.elem_type : undefined;
let shape = [];
const dim = value && value.shape && value.shape.dim;
if (dim && dim.length > 0) {
shape = dim.map((d) => {
if (d && d.value) {
if (d.value.dim_type === 1) {
return Number(d.value.dim_value);
} else if (d.value.dim_type === 2) {
return d.value.dim_param || '?';
}
}
return '?';
});
}
this.shape = new espdl.TensorShape(shape);
}
switch (dataType) {
case espdl.schema.TensorDataType.FLOAT: this.dataType = 'float32'; break;
case espdl.schema.TensorDataType.DOUBLE: this.dataType = 'float64'; break;
case espdl.schema.TensorDataType.BOOL: this.dataType = 'boolean'; break;
default: this.dataType = espdl.schema.TensorDataType[dataType] ? espdl.schema.TensorDataType[dataType].toLowerCase() : '?'; break;
}
}
toString() {
return this.dataType + this.shape.toString();
}
};
espdl.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
toString() {
if (!this.dimensions || this.dimensions.length === 0) {
return '';
}
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
};
espdl.Metadata = class {
static async open(context) {
if (!espdl.Metadata._metadata) {
let data = null;
try {
data = await context.asset('espdl-metadata.json');
} catch {
// continue regardless of error
}
espdl.Metadata._metadata = new espdl.Metadata(data);
}
return espdl.Metadata._metadata;
}
constructor(data) {
this._types = new Map();
if (data) {
const types = JSON.parse(data);
for (const type of types) {
if (!this._types.has(type.module)) {
this._types.set(type.module, new Map());
}
const types = this._types.get(type.module);
if (!types.has(type.name)) {
types.set(type.name, []);
}
types.get(type.name).push(type);
}
}
}
type(domain, name) {
domain = domain || 'espdl';
let current = null;
if (this._types.has(domain)) {
const types = this._types.get(domain);
if (types.has(name)) {
for (const type of types.get(name)) {
if (!current || type.version > current.version) {
current = type;
}
}
}
}
return current;
}
};
espdl.Context = class {
constructor(graph) {
this._initializers = new Map();
this._values = new Map();
if (graph.initializer) {
for (let i = 0; i < graph.initializer.length; i++) {
const tensor = graph.initializer[i];
const name = tensor.name || '';
if (name) {
const initializer = new espdl.Tensor(i, tensor);
this._initializers.set(name, initializer);
this._values.set(name, new espdl.Value(name, initializer.type, initializer));
}
}
}
if (graph.input) {
for (const valueInfo of graph.input) {
const name = valueInfo.name || '';
if (name && !this._values.has(name)) {
const type = valueInfo.value_info_type ? new espdl.TensorType(valueInfo) : null;
this._values.set(name, new espdl.Value(name, type, null));
}
}
}
if (graph.output) {
for (const valueInfo of graph.output) {
const name = valueInfo.name || '';
if (name && !this._values.has(name)) {
const type = valueInfo.value_info_type ? new espdl.TensorType(valueInfo) : null;
this._values.set(name, new espdl.Value(name, type, null));
}
}
}
if (graph.value_info) {
for (const valueInfo of graph.value_info) {
const name = valueInfo.name || '';
if (name && !this._values.has(name)) {
const type = valueInfo.value_info_type ? new espdl.TensorType(valueInfo) : null;
this._values.set(name, new espdl.Value(name, type, null));
}
}
}
}
value(name) {
if (!this._values.has(name)) {
this._values.set(name, new espdl.Value(name, null, null));
}
return this._values.get(name);
}
initializer(name) {
return this._initializers.get(name) || null;
}
};
espdl.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading ESP-DL model.';
}
};
export const ModelFactory = espdl.ModelFactory;
+86
View File
@@ -0,0 +1,86 @@
[
{
"name": "convolution",
"category": "Layer"
},
{
"name": "inner_product",
"category": "Layer"
},
{
"name": "activation",
"category": "Activation"
},
{
"name": "softmax",
"category": "Activation"
},
{
"name": "transpose",
"category": "Transform"
},
{
"name": "pool",
"category": "Pool"
},
{
"name": "instancenorm_1d",
"category": "Normalization"
},
{
"name": "batch_norm",
"category": "Normalization"
},
{
"name": "batchnorm",
"category": "Normalization"
},
{
"name": "reshape",
"category": "Shape"
},
{
"name": "dynamic_quantize",
"category": "Quantization"
},
{
"name": "dynamic_dequantize",
"category": "Quantization"
},
{
"name": "concat",
"category": "Tensor"
},
{
"name": "general_concat",
"category": "Tensor"
},
{
"name": "upsample",
"category": "Data"
},
{
"name": "relu",
"category": "Activation"
},
{
"name": "prelu",
"category": "Activation"
},
{
"name": "tanh",
"category": "Activation"
},
{
"name": "squeeze",
"category": "Transform"
},
{
"name": "rnn_arch",
"category": "Layer"
},
{
"name": "flatten",
"category": "Shape"
}
]
+461
View File
@@ -0,0 +1,461 @@
const espresso = {};
espresso.ModelFactory = class {
async match(context) {
const identifier = context.identifier.toLowerCase();
if (identifier.endsWith('.espresso.net')) {
const obj = await context.peek('json');
if (obj && Array.isArray(obj.layers) && obj.format_version) {
return context.set('espresso.net', obj);
}
}
if (identifier.endsWith('.espresso.shape')) {
const obj = await context.peek('json');
if (obj && obj.layer_shapes) {
return context.set('espresso.shape', obj);
}
}
if (identifier.endsWith('.espresso.weights')) {
const target = await context.read('binary');
return context.set('espresso.weights', target);
}
return null;
}
filter(context, match) {
if (context.type === 'espresso.net' && (match.type === 'espresso.weights' || match.type === 'espresso.shape' || match.type === 'coreml.metadata.mlmodelc')) {
return false;
}
if (context.type === 'espresso.shape' && (match.type === 'espresso.weights' || match.type === 'coreml.metadata.mlmodelc')) {
return false;
}
return true;
}
async open(context) {
const metadata = await context.metadata('espresso-metadata.json');
switch (context.type) {
case 'espresso.net': {
const reader = new espresso.Reader(context.value, null, null);
await reader.read(context);
return new espresso.Model(metadata, reader);
}
case 'espresso.weights': {
const reader = new espresso.Reader(null, context.value, null);
await reader.read(context);
return new espresso.Model(metadata, reader);
}
case 'espresso.shape': {
const reader = new espresso.Reader(null, null, context.value);
await reader.read(context);
return new espresso.Model(metadata, reader);
}
default: {
throw new espresso.Error(`Unsupported Core ML format '${context.type}'.`);
}
}
}
};
espresso.Model = class {
constructor(metadata, reader) {
this.format = reader.format;
this.metadata = [];
this.modules = [new espresso.Graph(metadata, reader)];
if (reader.version) {
this.version = reader.version;
}
if (reader.description) {
this.description = reader.description;
}
for (const argument of reader.properties) {
this.metadata.push(argument);
}
}
};
espresso.Graph = class {
constructor(metadata, reader) {
this.name = '';
this.type = reader.type;
for (const value of reader.values.values()) {
const name = value.name;
const type = value.type;
const description = value.description;
const initializer = value.initializer;
if (!value.value) {
value.value = new espresso.Value(name, type, description, initializer);
}
}
this.inputs = reader.inputs.map((argument) => {
const values = argument.value.map((value) => value.value);
return new espresso.Argument(argument.name, values, null, argument.visible);
});
this.outputs = reader.outputs.map((argument) => {
const values = argument.value.map((value) => value.value);
return new espresso.Argument(argument.name, values, null, argument.visible);
});
for (const obj of reader.nodes) {
const attributes = obj.attributes;
switch (obj.type) {
case 'loop':
attributes.conditionNetwork = new espresso.Graph(attributes.conditionNetwork);
attributes.bodyNetwork = new espresso.Graph(attributes.bodyNetwork);
break;
case 'branch':
attributes.ifBranch = new espresso.Graph(attributes.ifBranch);
attributes.elseBranch = new espresso.Graph(attributes.elseBranch);
break;
default:
break;
}
}
this.nodes = reader.nodes.map((obj) => new espresso.Node(metadata, obj));
}
};
espresso.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
espresso.Value = class {
constructor(name, type, description = null, initializer = null) {
if (typeof name !== 'string') {
throw new espresso.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = !type && initializer ? initializer.type : type;
this.description = description;
this.initializer = initializer;
this.quantization = initializer ? initializer.quantization : null;
}
};
espresso.Node = class {
constructor(metadata, obj) {
if (!obj.type) {
throw new Error('Undefined node type.');
}
const type = metadata.type(obj.type);
this.type = type ? { ...type } : { name: obj.type };
this.type.name = obj.type.split(':').pop();
this.name = obj.name || '';
this.description = obj.description || '';
this.inputs = (obj.inputs || []).map((argument) => {
const values = argument.value.map((value) => value.value);
return new espresso.Argument(argument.name, values, null, argument.visible);
});
this.outputs = (obj.outputs || []).map((argument) => {
const values = argument.value.map((value) => value.value);
return new espresso.Argument(argument.name, values, null, argument.visible);
});
this.attributes = Object.entries(obj.attributes || []).map(([name, value]) => {
const schema = metadata.attribute(obj.type, name);
let type = null;
let visible = true;
if (schema) {
type = schema.type ? schema.type : type;
if (schema.visible === false) {
visible = false;
} else if (schema.default !== undefined) {
if (Array.isArray(value)) {
value = value.map((item) => Number(item));
}
if (typeof value === 'bigint') {
value = value.toNumber();
}
if (JSON.stringify(schema.default) === JSON.stringify(value)) {
visible = false;
}
}
}
return new espresso.Argument(name, value, type, visible);
});
if (Array.isArray(obj.chain)) {
this.chain = obj.chain.map((obj) => new espresso.Node(metadata, obj));
}
}
};
espresso.Tensor = class {
constructor(type, data, quantization, category) {
this.type = type;
this.values = data;
this.quantization = quantization;
this.category = category;
this.encoding = '<';
}
};
espresso.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType;
this.shape = shape || new espresso.TensorShape([]);
}
equals(obj) {
return obj && this.dataType === obj.dataType && this.shape && this.shape.equals(obj.shape);
}
toString() {
return this.dataType + this.shape.toString();
}
};
espresso.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions.map((dim) => typeof dim === 'bigint' ? dim.toNumber() : dim);
}
equals(obj) {
return obj && Array.isArray(obj.dimensions) && Array.isArray(this.dimensions) &&
this.dimensions.length === obj.dimensions.length &&
obj.dimensions.every((value, index) => this.dimensions[index] === value);
}
toString() {
return Array.isArray(this.dimensions) && this.dimensions.length > 0 ?
`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]` : '';
}
};
espresso.Reader = class {
constructor(net, weights, shape) {
this.targets = [net, shape, weights];
}
async read(context) {
this.format = 'Espresso';
this.properties = [];
this.inputs = [];
this.outputs = [];
this.nodes = [];
let [net, shape, weights] = this.targets;
delete this.targets;
if (!net) {
const name = context.identifier.replace(/\.espresso\.(net|weights|shape)$/i, '.espresso.net');
const content = await context.fetch(name);
net = await content.read('json');
}
this.shapes = new Map();
if (!shape) {
const name = context.identifier.replace(/\.espresso\.(net|weights|shape)$/i, '.espresso.shape');
try {
const content = await context.fetch(name);
shape = await content.read('json');
} catch {
// continue regardless of error
}
}
if (shape && shape.layer_shapes) {
for (const [name, value] of Object.entries(shape.layer_shapes)) {
const dimensions = [value.n, value.k, value.w, value.h];
const shape = new espresso.TensorShape(dimensions);
this.shapes.set(name, shape);
}
}
this.blobs = new Map();
if (!weights) {
const name = net && net.storage ? net.storage : context.identifier.replace(/\.espresso\.(net|weights|shape)$/i, '.espresso.weights');
try {
const content = await context.fetch(name);
weights = await content.read('binary');
} catch {
// continue regardless of error
}
}
if (weights) {
const reader = weights;
const length = reader.uint64().toNumber();
for (let i = 0; i < length; i++) {
const key = reader.uint64().toNumber();
const size = reader.uint64().toNumber();
this.blobs.set(key, size);
}
for (const [key, size] of this.blobs) {
const buffer = reader.read(size);
this.blobs.set(key, buffer);
}
}
this.values = new Map();
if (net.format_version) {
const major = Math.floor(net.format_version / 100);
const minor = net.format_version % 100;
this.format += ` v${major}.${minor}`;
}
if (net && Array.isArray(net.layers)) {
for (const layer of net.layers) {
const type = layer.type;
const data = { ...layer };
const top = layer.top.split(',').map((name) => this._value(name));
const bottom = layer.bottom.split(',').map((name) => this._value(name));
const obj = {};
obj.type = type;
obj.name = layer.name;
obj.attributes = data;
obj.inputs = [{ name: 'inputs', value: bottom }];
obj.outputs = [{ name: 'outputs', value: top }];
obj.chain = [];
switch (type) {
case 'convolution':
case 'deconvolution': {
this._weights(obj, data, [data.C, data.K, data.Nx, data.Ny]);
if (data.has_biases) {
obj.inputs.push(this._initializer('biases', data.blob_biases, 'float32', [data.C]));
}
delete data.has_biases;
delete data.blob_biases;
break;
}
case 'batchnorm': {
obj.inputs.push(this._initializer('params', data.blob_batchnorm_params, 'float32', [4, data.C]));
delete data.blob_batchnorm_params;
break;
}
case 'inner_product': {
this._weights(obj, data, [data.nC, data.nB]);
if (data.has_biases) {
obj.inputs.push(this._initializer('biases', data.blob_biases, 'float32', [data.nC]));
}
delete data.has_biases;
delete data.blob_biases;
break;
}
case 'conv3d': {
this._weights(obj, data, null);
if (data.has_biases) {
obj.inputs.push(this._initializer('biases', data.blob_biases, 'float32', null));
}
delete data.has_biases;
delete data.blob_biases;
break;
}
case 'instancenorm_1d':
case 'dynamic_dequantize': {
this._weights(obj, data, null);
break;
}
default: {
break;
}
}
const blobs = Object.keys(data).filter((key) => key.startsWith('blob_'));
if (blobs.length > 0) {
throw new espresso.Error(`Unknown blob '${blobs.join(',')}' for type '${type}'.`);
}
if (data.has_prelu) {
obj.chain.push({ type: 'prelu' });
}
if (data.fused_relu || data.has_relu) {
obj.chain.push({ type: 'relu' });
}
if (data.fused_tanh || data.has_tanh) {
obj.chain.push({ type: 'tanh' });
}
if (data.has_batch_norm) {
obj.chain.push({ type: 'batch_norm' });
}
if (data.weights) {
for (const [name, identifier] of Object.entries(data.weights)) {
obj.inputs.push(this._initializer(name, identifier, 'float32', null));
}
delete data.weights;
}
delete data.name;
delete data.type;
delete data.top;
delete data.bottom;
delete data.fused_tanh;
delete data.fused_relu;
delete data.has_prelu;
delete data.has_relu;
delete data.has_tanh;
delete data.has_batch_norm;
this.nodes.push(obj);
}
}
delete this.shapes;
delete this.blobs;
}
_value(name) {
if (!this.values.has(name)) {
const shape = this.shapes.get(name);
const type = shape ? new espresso.TensorType('float32', shape) : null;
this.values.set(name, { name, type });
}
return this.values.get(name);
}
_weights(obj, data, dimensions) {
if (data.blob_weights !== undefined) {
obj.inputs.push(this._initializer('weights', data.blob_weights, 'float32', dimensions));
delete data.blob_weights;
return;
}
if (data.blob_weights_f16 !== undefined) {
obj.inputs.push(this._initializer('weights', data.blob_weights_f16, 'float16', dimensions));
delete data.blob_weights_f16;
return;
}
const keys = ['wBeta', 'wGamma', 'W_S8', 'W_int8', 'W_t_int8'];
for (const key of keys) {
if (data.weights && data.weights[key] !== undefined) {
let dataType = 'float32';
let name = key;
if (key.endsWith('_S8')) {
dataType = 'int8';
name = key.replace(/_S8$/, '');
} else if (key.endsWith('_int8')) {
dataType = 'int8';
name = key.replace(/_int8$/, '');
}
obj.inputs.push(this._initializer(name, data.weights[key], dataType, dimensions));
delete data.weights[key];
}
}
}
_initializer(name, identifier, dataType, dimensions) {
if (!Number.isInteger(identifier)) {
throw new espresso.Error(`Invalid '${identifier}' blob identifier.`);
}
dataType = dataType || 'float32';
const blob = this.blobs.get(identifier);
if (!dimensions) {
const itemsize = dataType === 'float32' ? 4 : 1;
dimensions = blob ? [blob.length / itemsize] : ['?'];
}
const shape = new espresso.TensorShape(dimensions);
const type = new espresso.TensorType(dataType, shape);
const value = {};
const initializer = new espresso.Tensor(type, blob, null, 'Blob');
value.value = new espresso.Value(`${identifier}\nblob`, type, null, initializer);
return { name, value: [value] };
}
};
espresso.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Espresso model.';
}
};
export const ModelFactory = espresso.ModelFactory;
File diff suppressed because it is too large Load Diff
+1295
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+526
View File
@@ -0,0 +1,526 @@
const flatbuffers = {};
flatbuffers.BinaryReader = class {
static open(data, offset) {
offset = offset || 0;
if (data && data.length >= (offset + 8)) {
const position = data instanceof Uint8Array ? -1 : data.position;
const reader = data instanceof Uint8Array ?
new flatbuffers.BufferReader(data) :
new flatbuffers.StreamReader(data);
reader.root = reader.int32(offset) + offset;
let value = false;
if (reader.root > 0 && reader.root < reader.length) {
const buffer = reader.read(offset + 4, 4);
reader.identifier = buffer.every((c) => c >= 32 && c <= 128) ? String.fromCharCode(...buffer) : '';
const vtable = reader.int32(reader.root);
if (vtable < 0 || (vtable > 4 && vtable < 1024)) {
const start = reader.root - vtable;
if (start > 0 && (start + 4) < reader.length) {
const last = reader.int16(start) + start;
if (last < reader.length) {
const max = reader.int16(start + 2);
if (max > 0 && (max & 1) === 0) {
const offsets = [];
for (let i = start + 4; i < last; i += 2) {
const offset = reader.int16(i);
offsets.push(offset);
}
value = max > Math.max(...offsets);
}
}
}
}
}
if (position !== -1) {
data.seek(position);
}
if (value) {
return reader;
}
}
return null;
}
bool(offset) {
return Boolean(this.int8(offset));
}
bool_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.bool(position + offset) : defaultValue;
}
int8(offset) {
return this.uint8(offset) << 24 >> 24;
}
int8_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.int8(position + offset) : defaultValue;
}
uint8_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.uint8(position + offset) : defaultValue;
}
int16_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.int16(position + offset) : defaultValue;
}
uint16_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.uint16(position + offset) : defaultValue;
}
int32_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.int32(position + offset) : defaultValue;
}
uint32_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.uint32(position + offset) : defaultValue;
}
int64_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.int64(position + offset) : defaultValue;
}
uint64_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.uint64(position + offset) : defaultValue;
}
float32_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.float32(position + offset) : defaultValue;
}
float64_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.float64(position + offset) : defaultValue;
}
string(offset, encoding) {
offset += this.int32(offset);
const length = this.int32(offset);
offset += 4;
if (encoding === 1) {
return this.read(offset, length);
}
let text = '';
for (let i = 0; i < length;) {
let codePoint = 0;
const a = this.uint8(offset + i++);
if (a < 0xC0) {
codePoint = a;
} else {
const b = this.uint8(offset + i++);
if (a < 0xE0) {
codePoint = ((a & 0x1F) << 6) | (b & 0x3F);
} else {
const c = this.uint8(offset + i++);
if (a < 0xF0) {
codePoint = ((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F);
} else {
const d = this.uint8(offset + i++);
codePoint = ((a & 0x07) << 18) | ((b & 0x3F) << 12) | ((c & 0x3F) << 6) | (d & 0x3F);
}
}
}
// Encode UTF-16
if (codePoint < 0x10000) {
text += String.fromCharCode(codePoint);
} else {
codePoint -= 0x10000;
text += String.fromCharCode((codePoint >> 10) + 0xD800, (codePoint & ((1 << 10) - 1)) + 0xDC00);
}
}
return text;
}
string_(position, offset, defaultValue) {
offset = this.__offset(position, offset);
return offset ? this.string(position + offset) : defaultValue;
}
bools_(position, offset) {
offset = this.__offset(position, offset);
if (offset) {
const length = this.__vector_len(position + offset);
offset = this.__vector(position + offset);
const array = new Array(length);
for (let i = 0; i < length; i++) {
array[i] = this.uint8(offset + i) ? true : false;
}
return array;
}
return [];
}
int64s_(position, offset) {
offset = this.__offset(position, offset);
if (offset) {
const length = this.__vector_len(position + offset);
offset = this.__vector(position + offset);
const array = new Array(length);
for (let i = 0; i < length; i++) {
array[i] = this.int64(offset + (i << 3));
}
return array;
}
return [];
}
uint64s_(position, offset) {
offset = this.__offset(position, offset);
if (offset) {
const length = this.__vector_len(position + offset);
offset = this.__vector(position + offset);
const array = new Array(length);
for (let i = 0; i < length; i++) {
array[i] = this.uint64(offset + (i << 3));
}
return array;
}
return [];
}
strings_(position, offset) {
offset = this.__offset(position, offset);
if (offset) {
const length = this.__vector_len(position + offset);
offset = this.__vector(position + offset);
const array = new Array(length);
for (let i = 0; i < length; i++) {
array[i] = this.string(offset + i * 4);
}
return array;
}
return [];
}
struct(position, offset, type) {
offset = this.__offset(position, offset);
return offset ? type.decode(this, position + offset) : null;
}
table(position, offset, type) {
offset = this.__offset(position, offset);
return offset ? type.decode(this, this.__indirect(position + offset)) : null;
}
union(position, offset, type) {
const type_offset = this.__offset(position, offset);
const union_type = type_offset ? this.uint8(position + type_offset) : 0;
offset = this.__offset(position, offset + 2);
return offset ? type.decode(this, this.__union(position + offset), union_type) : null;
}
array(position, offset, type) {
offset = this.__offset(position, offset);
if (offset) {
const length = this.__vector_len(position + offset);
offset = this.__vector(position + offset);
const buffer = this.read(offset, length * type.BYTES_PER_ELEMENT);
return new type(buffer.buffer, buffer.byteOffset, length);
}
return new type(0);
}
unions(/* position, offset, decode */) {
return new flatbuffers.Error('Not implemented.');
}
structs(position, offset, type, size) {
offset = this.__offset(position, offset);
const length = offset ? this.__vector_len(position + offset) : 0;
const list = new Array(length);
for (let i = 0; i < length; i++) {
list[i] = type.decode(this, this.__vector(position + offset) + i * size);
}
return list;
}
tables(position, offset, type) {
offset = this.__offset(position, offset);
const length = offset ? this.__vector_len(position + offset) : 0;
const list = new Array(length);
for (let i = 0; i < length; i++) {
list[i] = type.decode(this, this.__indirect(this.__vector(position + offset) + i * 4));
}
return list;
}
__offset(bb_pos, vtableOffset) {
const vtable = bb_pos - this.int32(bb_pos);
return vtableOffset < this.int16(vtable) ? this.int16(vtable + vtableOffset) : 0;
}
__indirect(offset) {
return offset + this.int32(offset);
}
__vector(offset) {
return offset + this.int32(offset) + 4;
}
__vector_len(offset) {
return this.int32(offset + this.int32(offset));
}
__union(offset) {
return offset + this.int32(offset);
}
};
flatbuffers.BufferReader = class extends flatbuffers.BinaryReader {
constructor(data) {
super();
this.length = data.length;
this._buffer = data;
this._view = new DataView(data.buffer, data.byteOffset, data.byteLength);
}
read(offset, length) {
return this._buffer.slice(offset, offset + length);
}
uint8(offset) {
return this._buffer[offset];
}
int16(offset) {
return this._view.getInt16(offset, true);
}
uint16(offset) {
return this._view.getUint16(offset, true);
}
int32(offset) {
return this._view.getInt32(offset, true);
}
uint32(offset) {
return this._view.getUint32(offset, true);
}
int64(offset) {
return this._view.getBigInt64(offset, true);
}
uint64(offset) {
return this._view.getBigUint64(offset, true);
}
float32(offset) {
return this._view.getFloat32(offset, true);
}
float64(offset) {
return this._view.getFloat64(offset, true);
}
};
flatbuffers.StreamReader = class extends flatbuffers.BinaryReader {
constructor(stream) {
super();
this._length = stream.length;
this._stream = stream;
this._size = 0x10000000;
this._offset = 0;
this._window = Math.min(0x1000, stream.length);
const buffer = this._stream.peek(this._window);
this._buffer = buffer;
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
this._chunk = -1;
}
get length() {
return this._length;
}
read(offset, length) {
const buffer = new Uint8Array(length);
this._read(buffer, offset);
return buffer;
}
uint8(offset) {
const position = this._fill(offset, 1);
return this._view.getUint8(position);
}
int16(offset) {
const position = this._fill(offset, 2);
return this._view.getInt16(position, true);
}
uint16(offset) {
const position = this._fill(offset, 2);
return this._view.getUint16(position, true);
}
int32(offset) {
const position = this._fill(offset, 4);
return this._view.getInt32(position, true);
}
uint32(offset) {
const position = this._fill(offset, 4);
return this._view.getUint32(position, true);
}
int64(offset) {
const position = this._fill(offset, 8);
return this._view.getBigInt64(position, true);
}
uint64(offset) {
const position = this._fill(offset, 8);
return this._view.getBigUint64(position, true);
}
float32(offset) {
const position = this._fill(offset, 4);
return this._view.getFloat32(position, true);
}
float64(offset) {
const position = this._fill(offset, 8);
return this._view.getFloat64(position, true);
}
_fill(offset, length) {
if (offset + length > this._length) {
throw new Error(`Expected ${offset + length - this._length} more bytes. The file might be corrupted. Unexpected end of file.`);
}
if (offset < this._offset || offset + length > this._offset + this._window) {
const remainder = offset % this. _size;
const last = this._last;
if (this._chunk !== -1) {
this._last = [this._chunk, this._buffer, this._view];
}
if (remainder + length > this._size) {
const buffer = new Uint8Array(length);
this._read(buffer, offset);
this._chunk = -1;
this._offset = offset;
this._window = length;
this._buffer = buffer;
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
} else {
const chunk = Math.floor(offset / this._size);
this._offset = chunk * this._size;
this._window = Math.min(this._length - this._offset, this._size);
if (last && last[0] === chunk) {
[this._chunk, this._buffer, this._view] = last;
} else {
this._chunk = chunk;
this._stream.seek(this._offset);
const buffer = this._stream.read(this._window);
this._buffer = buffer;
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
this._stream.seek(0);
}
}
}
return offset - this._offset;
}
_read(buffer, offset) {
const length = buffer.length;
if (offset < this._offset || offset + length > this._offset + this._window) {
this._stream.seek(offset);
const data = this._stream.read(length);
buffer.set(data, 0);
this._stream.seek(0);
} else {
offset -= this._offset;
const data = this._buffer.subarray(offset, offset + length);
buffer.set(data, 0);
}
}
};
flatbuffers.TextReader = class {
static open(obj) {
return new flatbuffers.TextReader(obj);
}
constructor(obj) {
this._root = obj;
}
get root() {
return this._root;
}
int64(obj, defaultValue) {
return obj === undefined ? defaultValue : BigInt(obj);
}
uint64(obj, defaultValue) {
return obj === undefined ? defaultValue : BigInt(obj);
}
value(obj, defaultValue) {
return obj === undefined ? defaultValue : obj;
}
object(obj, type) {
return obj === undefined ? obj : type.decodeText(this, obj);
}
array(obj, type) {
type = type || Array;
if (Array.isArray(obj)) {
const length = obj.length;
const target = new type(length);
for (let i = 0; i < length; i++) {
target[i] = obj[i];
}
return target;
}
if (obj) {
throw new flatbuffers.Error('Inalid value array.');
}
return new type(0);
}
objects(obj, type) {
if (Array.isArray(obj)) {
const target = new Array(obj.length);
for (let i = 0; i < obj.length; i++) {
target[i] = type.decodeText(this, obj[i]);
}
return target;
}
if (!obj) {
return [];
}
throw new flatbuffers.Error('Inalid object array.');
}
};
flatbuffers.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'FlatBuffers Error';
this.message = message;
}
};
export const BinaryReader = flatbuffers.BinaryReader;
export const TextReader = flatbuffers.TextReader;
+209
View File
@@ -0,0 +1,209 @@
// Experimental
import * as python from './python.js';
const flax = {};
flax.ModelFactory = class {
async match(context) {
const stream = context.stream;
if (stream.length > 4) {
const buffer = stream.peek(1);
if (buffer[0] === 0xDE || buffer[0] === 0xDF || ((buffer[0] & 0x80) === 0x80)) {
return context.set('flax.msgpack.map');
}
}
return null;
}
async open(context) {
const stream = context.stream;
const packed = stream.peek();
const execution = new python.Execution();
const msgpack = execution.__import__('msgpack');
const numpy = execution.__import__('numpy');
// https://github.com/google/flax/blob/main/flax/serialization.py
const ext_hook = (code, data) => {
switch (code) {
case 1: { // _MsgpackExtType.ndarray
const tuple = msgpack.unpackb(data);
const dtype = new numpy.dtype(tuple[1]);
dtype.byteorder = '<';
return new numpy.ndarray(tuple[0], dtype, tuple[2]);
}
default: {
throw new flax.Error(`Unsupported MessagePack extension '${code}'.`);
}
}
};
const obj = msgpack.unpackb(packed, ext_hook);
return new flax.Model(obj);
}
};
flax.Model = class {
constructor(obj) {
this.format = 'Flax';
this.modules = [new flax.Graph(obj)];
}
};
flax.Graph = class {
constructor(obj) {
this.inputs = [];
this.outputs = [];
const layers = new Map();
const layer = (path) => {
const name = path.join('.');
if (!layers.has(name)) {
layers.set(name, {});
}
return layers.get(name);
};
const flatten = (path, obj) => {
for (const [name, value] of Object.entries(obj)) {
if (flax.Utility.isTensor(value)) {
const obj = layer(path);
obj[name] = value;
} else if (Array.isArray(value)) {
const obj = layer(path);
obj[name] = value;
} else if (Object(value) === value) {
flatten(path.concat(name), value);
} else {
const obj = layer(path);
obj[name] = value;
}
}
};
if (Array.isArray(obj)) {
layer([]).value = obj;
} else {
flatten([], obj);
}
this.nodes = Array.from(layers).map(([name, value]) => new flax.Node(name, value));
}
};
flax.Argument = class {
constructor(name, value) {
this.name = name;
this.value = value;
}
};
flax.Value = class {
constructor(name, initializer = null) {
if (typeof name !== 'string') {
throw new flax.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = initializer ? initializer.type : null;
this.initializer = initializer;
}
};
flax.Node = class {
constructor(name, layer) {
this.name = name;
this.type = { name: 'Module' };
this.attributes = [];
this.inputs = [];
this.outputs = [];
for (const [name, value] of Object.entries(layer)) {
if (flax.Utility.isTensor(value)) {
const tensor = new flax.Tensor(value);
const argument = new flax.Argument(name, [new flax.Value('', tensor)]);
this.inputs.push(argument);
} else if (Array.isArray(value)) {
const attribute = new flax.Argument(name, value);
this.attributes.push(attribute);
} else {
const attribute = new flax.Argument(name, value);
this.attributes.push(attribute);
}
}
}
};
flax.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType || '?';
this.shape = shape;
}
toString() {
return this.dataType + this.shape.toString();
}
};
flax.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
toString() {
return (Array.isArray(this.dimensions) && this.dimensions.length > 0) ?
`[${this.dimensions.join(',')}]` : '';
}
};
flax.Tensor = class {
constructor(array) {
this.type = new flax.TensorType(array.dtype.__name__, new flax.TensorShape(array.shape));
const dataType = this.type.dataType;
this.encoding = dataType === 'string' || dataType === 'object' ? '|' : array.dtype.byteorder;
this._data = array.tobytes();
this._itemsize = array.dtype.itemsize;
}
get values() {
switch (this.type.dataType) {
case 'string': {
if (this._data instanceof Uint8Array) {
const data = this._data;
const decoder = new TextDecoder('utf-8');
const size = this.type.shape.dimensions.reduce((a, b) => a * b, 1);
this._data = new Array(size);
let offset = 0;
for (let i = 0; i < size; i++) {
const buffer = data.subarray(offset, offset + this._itemsize);
const index = buffer.indexOf(0);
this._data[i] = decoder.decode(index >= 0 ? buffer.subarray(0, index) : buffer);
offset += this._itemsize;
}
}
return this._data;
}
default:
return this._data;
}
}
};
flax.Utility = class {
static isTensor(obj) {
return obj && obj.__class__ && obj.__class__.__module__ === 'numpy' && obj.__class__.__name__ === 'ndarray';
}
};
flax.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Flax model.';
}
};
export const ModelFactory = flax.ModelFactory;
+196
View File
@@ -0,0 +1,196 @@
const flexbuffers = {};
flexbuffers.BinaryReader = class {
static open(buffer) {
const length = buffer.length;
if (length >= 3) {
const byteWidth = buffer[length - 1];
if (byteWidth <= 8) {
const packedType = buffer[length - 2];
return new flexbuffers.BinaryReader(buffer, length - 2 - byteWidth, byteWidth, 1 << (packedType & 3), packedType >> 2);
}
}
return null;
}
constructor(buffer, offset, parentWidth, byteWidth, type) {
this._buffer = buffer;
this._length = buffer.length;
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
this._utf8Decoder = new TextDecoder('utf-8');
this._root = new flexbuffers.Reference(this, offset, parentWidth, byteWidth, type);
}
read() {
return this._root.read();
}
get length() {
return this._length;
}
int(offset, size) {
switch (size) {
case 1: return this._view.getInt8(offset);
case 2: return this._view.getInt16(offset, true);
case 4: return this._view.getInt32(offset, true);
case 8: return this._view.getBigInt64(offset, true);
default: throw new flexbuffers.Error(`Invalid int size '${size}'.`);
}
}
uint(offset, size) {
switch (size) {
case 1: return this._view.getUint8(offset);
case 2: return this._view.getUint16(offset, true);
case 4: return this._view.getUint32(offset, true);
case 8: return this._view.getBigUint64(offset, true);
default: throw new flexbuffers.Error(`Invalid uint size '${size}'.`);
}
}
float(offset, size) {
switch (size) {
case 4: return this._view.getFloat32(offset, true);
case 8: return this._view.getFloat64(offset, true);
default: throw new flexbuffers.Error(`Invalid float size '${size}'.`);
}
}
string(offset, size) {
let end = size === undefined ? this._buffer.indexOf(0, offset) : offset + size;
end = end === -1 ? this._buffer.length : end;
const bytes = this._buffer.subarray(offset, end);
return this._utf8Decoder.decode(bytes);
}
bytes(offset, size) {
return this._buffer.slice(offset, offset + size);
}
};
flexbuffers.Reference = class {
constructor(reader, offset, parentWidth, byteWidth, type) {
this._reader = reader;
this._offset = offset;
this._parentWidth = parentWidth;
this._byteWidth = byteWidth;
this._type = type;
}
read() {
switch (this._type) {
case 0x00: // null
return null;
case 0x01: // int
return this._reader.int(this._offset, this._parentWidth);
case 0x02: // uint
return this._reader.uint(this._offset, this._parentWidth);
case 0x03: // float
return this._reader.float(this._offset, this._parentWidth);
case 0x04: { // key
return this._reader.string(this._indirect());
}
case 0x05: { // string
const offset = this._indirect();
const size = this._reader.uint(offset - this._byteWidth, this._byteWidth);
return this._reader.string(offset, size);
}
case 0x06: // indirect int
return this._reader.int(this._indirect(), this._byteWidth);
case 0x07: // indirect uint
return this._reader.uint(this._indirect(), this._byteWidth);
case 0x08: // indirect float
return this._reader.float(this._indirect(), this._byteWidth);
case 0x09: { // map
const offset = this._indirect();
const keysOffset = offset - (this._byteWidth * 3);
const keysVectorOffset = keysOffset - this._reader.uint(keysOffset, this._byteWidth);
const keysByteWidth = this._reader.uint(keysOffset + this._byteWidth, this._byteWidth);
const keys = this._typedVector(keysVectorOffset, keysByteWidth, 0x04);
const values = this._vector(offset, this._byteWidth);
const map = {};
for (let i = 0; i < keys.length; i++) {
map[keys[i]] = values[i];
}
return map;
}
case 0x0a: { // vector
return this._vector(this._indirect(), this._byteWidth);
}
case 0x0b: // vector int
case 0x0c: // vector uint
case 0x0d: // vector float
case 0x0e: // vector key
case 0x0f: // vector string deprecated
case 0x24: { // vector bool
return this._typedVector(this._indirect(), this._byteWidth, this._type - 0x0b + 0x01);
}
case 0x10: // vector int2
case 0x11: // vector uint2
case 0x12: // vector float2
case 0x13: // vector int3
case 0x14: // vector uint3
case 0x15: // vector float3
case 0x16: // vector int4
case 0x17: // vector uint4
case 0x18: { // vector float4
const offset = this._indirect();
const size = (((this._type - 0x10) / 3) >> 0) + 2;
const type = ((this._type - 0x10) % 3) + 0x01;
return this._typedVector(offset, this._byteWidth, type, size);
}
case 0x19: { // blob
const offset = this._indirect();
const size = this._reader.uint(offset - this._byteWidth, this._byteWidth);
return this._reader.bytes(offset, size);
}
case 0x1a: { // bool
return this._reader.uint(this._offset, this._parentWidth) !== 0;
}
default: {
throw new flexbuffers.Error(`Unsupported reference type '${this._type}`);
}
}
}
_indirect() {
return this._offset - this._reader.uint(this._offset, this._parentWidth);
}
_vector(offset, byteWidth) {
const size = this._reader.uint(offset - byteWidth, byteWidth);
const packedTypeOffset = offset + (size * byteWidth);
const vector = new Array(size);
for (let i = 0; i < size; i++) {
const packedType = this._reader.uint(packedTypeOffset + i, 1);
const reference = new flexbuffers.Reference(this._reader, offset + (i * byteWidth), byteWidth, 1 << (packedType & 3), packedType >> 2);
vector[i] = reference.read();
}
return vector;
}
_typedVector(offset, byteWidth, type, size) {
size = size === undefined ? this._reader.uint(offset - byteWidth, byteWidth) : size;
const vector = new Array(size);
for (let i = 0; i < size; i++) {
const reference = new flexbuffers.Reference(this._reader, offset + (i * byteWidth), byteWidth, 1, type);
vector[i] = reference.read();
}
return vector;
}
};
flexbuffers.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'FlexBuffers Error';
this.message = message;
}
};
export const BinaryReader = flexbuffers.BinaryReader;
+2
View File
@@ -0,0 +1,2 @@
[
]
+72
View File
@@ -0,0 +1,72 @@
// Experimental
const flux = {};
flux.ModelFactory = class {
async match(context) {
const identifier = context.identifier;
const extension = identifier.lastIndexOf('.') > 0 ? identifier.split('.').pop().toLowerCase() : '';
const stream = context.stream;
if (stream && extension === 'bson') {
return context.set('flux.bson');
}
return null;
}
async open(context) {
let root = null;
try {
root = await context.read('bson');
} catch (error) {
const message = error && error.message ? error.message : error.toString();
throw new flux.Error(`File format is not Flux BSON (${message.replace(/\.$/, '')}).`);
}
/* const metadata = */ context.metadata('flux-metadata.json');
const backref = (obj, root) => {
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
obj[i] = backref(obj[i], root);
}
} else if (obj === Object(obj)) {
if (obj.tag === 'backref' && obj.ref) {
if (!root._backrefs[obj.ref - 1n]) {
throw new flux.Error(`Invalid backref '${obj.ref}'.`);
}
obj = root._backrefs[obj.ref - 1n];
}
for (const key of Object.keys(obj)) {
if (obj !== root || key !== '_backrefs') {
obj[key] = backref(obj[key], root);
}
}
}
return obj;
};
const obj = backref(root, root);
const model = obj.model;
if (!model) {
throw new flux.Error('File does not contain Flux model.');
}
throw new flux.Error("File contains unsupported Flux data.");
}
};
flux.Model = class {
constructor(/* root */) {
this.format = 'Flux';
this.modules = [];
}
};
flux.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Flux Error';
}
};
export const ModelFactory = flux.ModelFactory;
File diff suppressed because it is too large Load Diff
+1187
View File
File diff suppressed because it is too large Load Diff
+158
View File
@@ -0,0 +1,158 @@
.node path { stroke: #333; fill: none; stroke-width: 1px; }
.node line { stroke: #333; fill: none; stroke-width: 1px; }
.node-item path { stroke-width: 0; stroke: #000; fill: #fff; }
.node-item text { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; font-size: 11px; text-rendering: geometricPrecision; user-select: none; }
.node-item-function path { fill: #fff; }
.node-item-function text { fill: #000; }
.node-item-function:hover { cursor: pointer; }
.node-item-function:hover path { fill: #eee; }
.node-item-type path { fill: #333; }
.node-item-type text { fill: #fff; }
.node-item-type:hover { cursor: pointer; }
.node-item-type:hover path { fill: #fff; }
.node-item-type:hover text { fill: #000; }
.node-item-type-constant path { fill: #eee; }
.node-item-type-constant text { fill: #000; }
.node-item-type-constant:hover path { fill: #fff; }
.node-item-type-control path { fill: #eee; }
.node-item-type-control text { fill: #000; }
.node-item-type-control:hover path { fill: #fff; }
.node-item-type-layer path { fill: rgb(51, 85, 136); }
.node-item-type-activation path { fill: rgb(112, 41, 33); }
.node-item-type-pool path { fill: rgb(51, 85, 51); }
.node-item-type-normalization path { fill: rgb(51, 85, 68); }
.node-item-type-dropout path { fill: rgb(69, 71, 112); }
.node-item-type-shape path { fill: rgb(108, 79, 71); }
.node-item-type-tensor path { fill: rgb(89, 66, 59); }
.node-item-type-transform path { fill: rgb(51, 85, 68); }
.node-item-type-data path { fill: rgb(85, 85, 85); }
.node-item-type-quantization path { fill: rgb(80, 40, 0); }
.node-item-type-attention path { fill: rgb(120, 60, 0); }
.node-item-input path { fill: #fff; }
.node-item-input:hover { cursor: pointer; }
.node-item-input:hover path { fill: #fff; }
.node-item-constant path { fill: #eee; }
.node-item-constant:hover { cursor: pointer; }
.node-item-constant:hover path { fill: #fff; }
.node-item-undefined path { fill: #f00; }
.node-item-undefined:hover { cursor: pointer; }
.node-item-undefined:hover path { fill: #fff; }
.node-argument-list > path { fill: #fff; stroke-width: 0; stroke: #000; }
.node-argument-list:hover { cursor: pointer; }
.node-argument-list:hover > path { fill: #f6f6f6; }
.node-argument > text { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; font-size: 9px; font-weight: normal; text-rendering: geometricPrecision; user-select: none; }
.node-argument > rect { fill: transparent; }
.graph-item-input path { fill: #eee; }
.graph-item-input:hover { cursor: pointer; }
.graph-item-input:hover path { fill: #fff; }
.graph-item-output path { fill: #eee; }
.graph-item-output:hover { cursor: pointer; }
.graph-item-output:hover path { fill: #fff; }
#arrowhead { fill: #000; }
#arrowhead-hover { fill: rgba(220, 0, 0, 0.9); }
#arrowhead-select { fill: rgba(220, 0, 0, 0.9); }
.edge-paths { pointer-events: none; }
.edge-path { stroke: #000; stroke-width: 1px; fill: none; marker-end: url("#arrowhead"); }
.edge-paths-hit-test { pointer-events: stroke; stroke-width: 0.5em; fill: none; stroke: #000; stroke-opacity: 0.001; }
.select > .node.node-border { stroke: rgba(220, 0, 0, 0.9); stroke-width: 2px; }
.select.edge-path { stroke: rgba(220, 0, 0, 0.9); stroke-width: 1px; marker-end: url("#arrowhead-select"); }
.select.node-argument > rect { fill: transparent; stroke: rgba(220, 0, 0, 0.9); }
.edge-label { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; font-size: 10px; }
.edge-path-control-dependency { stroke-dasharray: 3, 2; }
.edge-path-tunnel { stroke-dasharray: 5, 3; marker-end: url("#arrowhead-tunnel"); opacity: 0.5; }
#arrowhead-tunnel { fill: #000; }
.cluster rect { stroke: #000; fill: #000; fill-opacity: 0.02; stroke-opacity: 0.06; stroke-width: 1px; }
.node-block > .node-block-background { fill: #fff; stroke: none; stroke-width: 0; }
.node-block .edge-path { stroke: #000; stroke-width: 1px; fill: none; }
.node-block .select.edge-path { stroke: rgba(220, 0, 0, 0.9); marker-end: url("#arrowhead-select"); }
@keyframes pulse { from { stroke-dashoffset: 100px; } to { stroke-dashoffset: 0; } }
@media (prefers-color-scheme: dark) {
.edge-path { stroke: #888; }
.node path { stroke: #242424; }
.node line { stroke: #242424; }
.select > .node.node-border { stroke: rgba(192, 0, 0, 0.8); }
.select.edge-path { stroke: rgba(192, 0, 0, 0.8); }
.select.node-argument > rect { fill: transparent; stroke: rgba(192, 0, 0, 0.8); }
#arrowhead { fill: #888; }
#arrowhead-hover { fill: rgba(192, 0, 0, 0.8); }
#arrowhead-select { fill: rgba(192, 0, 0, 0.8); }
.edge-label { fill: #b2b2b2; }
.node-item-function path { fill: #404040; }
.node-item-function text { fill: #dfdfdfdf; }
.node-item-function:hover { cursor: pointer; }
.node-item-function:hover path { fill: #666666; }
.node-item-type path { fill: #303030; }
.node-item-type text { fill: #dfdfdf; }
.node-item-type:hover { cursor: pointer; }
.node-item-type:hover path { fill: #808080; }
.node-item-type:hover text { fill: #dfdfdf; }
.node-item path { stroke: #fff; }
.node-item text { fill: #dfdfdf; }
.node-argument > text { fill: #b2b2b2; }
.node-argument-list > path { fill: #2d2d2d; }
.node-argument-list:hover > path { fill: #303030; }
.graph-item-input path { fill: #404040; }
.graph-item-input:hover { cursor: pointer; }
.graph-item-input:hover path { fill: #666666; }
.graph-item-output path { fill: #404040; }
.graph-item-output:hover { cursor: pointer; }
.graph-item-output:hover path { fill: #666666; }
.node-item-input path { fill: #404040; }
.node-item-input:hover path { fill: #666666; }
.node-item-constant path { fill: #4b4b4b; }
.node-item-constant:hover path { fill: #666666; }
.node-item-type-layer path { fill: rgba(51, 85, 136, 0.7); }
.node-item-type-activation path { fill: rgba(75, 27, 22, 0.7); }
.node-item-type-activation path { fill: rgba(75, 27, 22, 0.7); }
.node-item-type-pool path { fill: rgba(51, 85, 51, 0.7); }
.node-item-type-pool path { fill: rgba(51, 85, 51, 0.7); }
.node-item-type-normalization path { fill: rgba(51, 85, 68, 0.7); }
.node-item-type-dropout path { fill: rgba(69, 71, 112, 0.7); }
.node-item-type-shape path { fill: rgba(108, 79, 71, 0.7); }
.node-item-type-tensor path { fill: rgba(89, 66, 59, 0.7); }
.node-item-type-transform path { fill: rgba(51, 85, 68, 0.7); }
.node-item-type-data path { fill: rgba(85, 85, 85, 0.7); }
.node-item-type-quantization path { fill: rgb(80, 40, 0, 0.7); }
.node-item-type-attention path { fill: rgb(100, 50, 0, 0.7); }
.node-item-type-custom path { fill: rgb(64, 64, 64, 0.7); }
.node-block > .node-block-background { fill: #404040; stroke: none; }
.node-block .edge-path { stroke: #888; }
.node-block .select.edge-path { stroke: rgba(192, 0, 0, 0.8); marker-end: url("#arrowhead-select"); }
.edge-path-tunnel { stroke: #888; opacity: 0.5; }
#arrowhead-tunnel { fill: #888; }
}
+1092
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+353
View File
@@ -0,0 +1,353 @@
// Experimental
const hailo = {};
hailo.ModelFactory = class {
async match(context) {
const container = await hailo.Container.open(context);
if (container) {
return context.set(container.type, container);
}
return null;
}
filter(context, match) {
if (context.type === 'hailo.metadata' && (match.type === 'hailo.configuration' || match.type === 'npz' || match.type === 'onnx.proto')) {
return false;
}
if (context.type === 'hailo.configuration' && match.type === 'npz') {
return false;
}
return true;
}
async open(context) {
const metadata = await context.metadata('hailo-metadata.json');
const target = context.value;
await target.read();
return new hailo.Model(metadata, target);
}
};
hailo.Model = class {
constructor(metadata, container) {
const configuration = container.configuration;
this.modules = [new hailo.Graph(metadata, configuration, container.weights)];
this.name = configuration && configuration.name || "";
this.format = container.format + (container.metadata && container.metadata.sdk_version ? ` v${container.metadata.sdk_version}` : '');
this.metadata = [];
if (container.metadata && container.metadata.state) {
this.metadata.push(new hailo.Argument('state', container.metadata.state));
}
}
};
hailo.Graph = class {
constructor(metadata, configuration, weights) {
this.inputs = [];
this.outputs = [];
this.nodes = [];
const values = new Map();
values.map = (name, type, tensor) => {
if (name.length === 0 && tensor) {
return new hailo.Value(name, type || null, tensor);
}
if (!values.has(name)) {
values.set(name, new hailo.Value(name, type || null, tensor || null));
} else if (tensor) {
throw new hailo.Error(`Duplicate value '${name}'.`);
} else if (type && !type.equals(values.get(name).type)) {
throw new hailo.Error(`Duplicate value '${name}'.`);
}
return values.get(name);
};
const layers = Object.entries(configuration.layers || {}).map(([name, value]) => {
value.name = name;
return value;
});
const inputs = new Set();
for (const layer of layers) {
switch (layer.type) {
case 'input_layer': {
for (let i = 0; i < layer.output.length; i++) {
const shape = Array.isArray(layer.output_shapes) && layer.output_shapes.length > 0 ? layer.output_shapes[0] : null;
const type = shape ? new hailo.TensorType('?', new hailo.TensorShape(shape)) : null;
const output = layer.output[i];
if (!inputs.has(output)) {
const name = `${layer.name}\n${output}`;
const argument = new hailo.Argument('input', [values.map(name, type)]);
this.inputs.push(argument);
inputs.add(output);
}
}
break;
}
case 'output_layer': {
for (let i = 0; i < layer.input.length; i++) {
const shape = Array.isArray(layer.input_shapes) && layer.input_shapes.length > 0 ? layer.input_shapes[i] : null;
const type = shape ? new hailo.TensorType('?', new hailo.TensorShape(shape)) : null;
const input = layer.input[i];
const name = `${input}\n${layer.name}`;
const argument = new hailo.Argument('output', [values.map(name, type)]);
this.outputs.push(argument);
}
break;
}
default: {
const node = new hailo.Node(metadata, layer, values, weights.get(layer.name));
this.nodes.push(node);
break;
}
}
}
}
};
hailo.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
hailo.Value = class {
constructor(name, type, initializer) {
if (typeof name !== 'string') {
throw new hailo.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = initializer ? initializer.type : type;
this.initializer = initializer;
}
};
hailo.Node = class {
constructor(metadata, layer, values, weights) {
weights = weights || new Map();
this.name = layer.name || '';
this.type = metadata.type(layer.type);
if (layer.type === 'activation') {
const name = layer.params.activation || layer.name || '';
this.type = { ...this.type, name };
}
this.inputs = layer.input.map((name, index) => {
const shape = layer.input_shapes ? layer.input_shapes[index] : null;
const type = shape ? new hailo.TensorType('?', new hailo.TensorShape(shape)) : null;
name = `${name}\n${layer.name}`;
return new hailo.Argument("input", [values.map(name, type, null)]);
});
const layer_params = layer.params ? Object.entries(layer.params) : [];
const params_list = layer_params.reduce((acc, [name, value]) => {
const schema = metadata.attribute(layer.type, name) || {};
if (schema.visible) {
const label = schema.label ? schema.label : name;
if (!weights.has(label)) {
const array = weights.get(label);
const tensor = new hailo.Tensor(array, value);
acc.push(new hailo.Argument(label, [values.map('', tensor.type, tensor)]));
}
}
return acc;
}, []);
const params_from_npz = Array.from(weights).filter(([, value]) => value).map(([name, value]) => {
const tensor = new hailo.Tensor(value);
return new hailo.Argument(name, [values.map('', tensor.type, tensor)]);
});
this.inputs = this.inputs.concat(params_list).concat(params_from_npz);
this.outputs = (layer.output || []).map((name, index) => {
const shape = layer.output_shapes ? layer.output_shapes[index] : null;
const type = shape ? new hailo.TensorType('?', new hailo.TensorShape(shape)) : null;
name = `${layer.name}\n${name}`;
return new hailo.Argument("output", [values.map(name, type, null)]);
});
this.attributes = [];
const attrs = Object.assign(layer.params || {}, { original_names: layer.original_names || [] });
for (const [name, value] of Object.entries(attrs)) {
const schema = metadata.attribute(layer.type, name);
const type = schema && schema.type ? schema.type : '';
const visible = name === 'original_names' || (schema && schema.visible === false) ? false : true;
const attribute = new hailo.Argument(name, value, type, visible);
this.attributes.push(attribute);
}
this.chain = [];
if (layer && layer.params && layer.params.activation && layer.params.activation !== 'linear' && layer.type !== 'activation') {
const activation = {
type: layer.params.activation,
name: layer.params.activation,
input: [],
output: []
};
const node = new hailo.Node(metadata, activation, values.map);
this.chain.push(node);
}
}
};
hailo.Tensor = class {
constructor(array, shape) {
const dataType = array && array.dtype ? array.dtype.__name__ : '?';
shape = array && array.shape ? array.shape : shape;
this.type = new hailo.TensorType(dataType, new hailo.TensorShape(shape));
if (array) {
this.stride = array.strides.map((stride) => stride / array.itemsize);
this.layout = this.type.dataType === 'string' || this.type.dataType === 'object' ? '|' : array.dtype.byteorder;
this.values = this.type.dataType === 'string' || this.type.dataType === 'object' || this.type.dataType === 'void' ? array.tolist() : array.tobytes();
}
}
};
hailo.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType;
this.shape = shape;
}
equals(obj) {
return obj && this.dataType === obj.dataType && this.shape && this.shape.equals(obj.shape);
}
toString() {
return (this.dataType || '?') + this.shape.toString();
}
};
hailo.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
equals(obj) {
if (obj && Array.isArray(obj.dimensions) && Array.isArray(this.dimensions)) {
if (this.dimensions.length === obj.dimensions.length) {
return obj.dimensions.every((value, index) => this.dimensions[index] === value);
}
const a = this.dimensions.filter((value, index) => index === 0 || index === this.dimensions.length - 1 || value !== 1);
const b = obj.dimensions.filter((value, index) => index === 0 || index === obj.dimensions.length - 1 || value !== 1);
if (a.length === b.length) {
return a.every((value, index) => b[index] === value);
}
}
return false;
}
toString() {
if (this.dimensions && this.dimensions.length > 0) {
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
return '';
}
};
hailo.Container = class {
static async open(context) {
const identifier = context.identifier;
const basename = identifier.split('.');
basename.pop();
if (identifier.toLowerCase().endsWith('.hn')) {
if (basename.length > 1 && (basename[basename.length - 1] === 'native' || basename[basename.length - 1] === 'fp')) {
basename.pop();
}
const configuration = await context.peek('json');
if (configuration && configuration.name && configuration.net_params && configuration.layers) {
return new hailo.Container(context, 'hailo.configuration', basename.join('.'), configuration, null);
}
} else if (identifier.toLowerCase().endsWith('.metadata.json')) {
basename.pop();
const metadata = await context.peek('json');
if (metadata && metadata.state && metadata.hn) {
return new hailo.Container(context, 'hailo.metadata', basename.join('.'), null, metadata);
}
}
return null;
}
constructor(context, type, basename, configuration, metadata) {
this.type = type;
this.context = context;
this.basename = basename;
this.configuration = configuration;
this.metadata = metadata;
}
async _request(name, type) {
try {
const content = await this.context.fetch(name);
if (content) {
return await content.read(type);
}
} catch {
// continue regardless of error
}
return null;
}
async read() {
this.format = 'Hailo NN';
this.weights = new Map();
if (!this.metadata) {
this.metadata = await this._request(`${this.basename}.metadata.json`, 'json');
}
if (this.metadata) {
this.format = 'Hailo Archive';
this.configuration = await this._request(this.metadata.hn, 'json');
if (!this.configuration) {
throw new hailo.Error("Archive does not contain '.nn' configuration.");
}
let extension = '';
switch (this.metadata.state) {
case 'fp_optimized_model': extension = '.fpo.npz'; break;
case 'quantized_model': extension = '.q.npz'; break;
case 'compiled_model': extension = '.q.npz'; break;
default: extension = '.npz'; break;
}
const entries = await this._request(this.basename + extension, 'npz');
if (entries && entries.size > 0) {
const inputs = new Set([
'kernel', 'bias',
'input_activation_bits', 'output_activation_bits',
'weight_bits', 'bias_decomposition'
]);
for (const [name, value] of entries) {
const key = name.split('.').slice(0, -1).join('.');
const match = key.match(/.*?(?=:[0-9])/);
if (match) {
const path = match[0].split('/');
if (inputs.has(path[2])) {
const layer = `${path[0]}/${path[1]}`;
if (!this.weights.has(layer)) {
this.weights.set(layer, new Map());
}
const weights = this.weights.get(layer);
weights.set(path[2], value);
}
}
}
}
}
delete this.context;
delete this.basename;
}
};
hailo.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Hailo model.';
}
};
export const ModelFactory = hailo.ModelFactory;
Executable
+1832
View File
File diff suppressed because it is too large Load Diff
+168
View File
@@ -0,0 +1,168 @@
const hickle = {};
hickle.ModelFactory = class {
async match(context) {
const group = await context.peek('hdf5');
if (group && group.attributes && group.attributes.get('CLASS') === 'hickle') {
return context.set('hickle', group);
}
return null;
}
async open(context) {
return new hickle.Model(context.value);
}
};
hickle.Model = class {
constructor(group) {
this.format = 'Hickle Weights';
this.modules = [new hickle.Graph(group)];
}
};
hickle.Graph = class {
constructor(group) {
this.inputs = [];
this.outputs = [];
const deserialize = (group) => {
if (group && group.attributes.has('type')) {
const type = group.attributes.get('type');
if (Array.isArray(type) && type.length && typeof type[0] === 'string') {
switch (type[0]) {
case 'hickle':
case 'dict_item': {
if (group.groups.size === 1) {
return deserialize(group.groups.values().next().value);
}
throw new hickle.Error(`Invalid Hickle type value '${type[0]}'.`);
}
case 'dict': {
const dict = new Map();
for (const [name, obj] of group.groups) {
const value = deserialize(obj);
dict.set(name, value);
}
return dict;
}
case 'ndarray': {
return group.value;
}
default: {
throw new hickle.Error(`Unsupported Hickle type '${type[0]}'`);
}
}
}
throw new hickle.Error(`Unsupported Hickle type '${JSON.stringify(type)}'`);
}
throw new hickle.Error('Unsupported Hickle group.');
};
const obj = deserialize(group);
const layers = new Map();
if (obj && obj instanceof Map && Array.from(obj.values()).every((value) => value.type && value.shape)) {
for (const [key, value] of obj) {
const tensor = new hickle.Tensor(key, value.shape, value.type, value.littleEndian, value.type === 'string' ? value.value : value.data);
const bits = key.split('.');
const parameter = bits.pop();
const layer = bits.join('.');
if (!layers.has(layer)) {
layers.set(layer, []);
}
layers.get(layer).push({ name: parameter, value: tensor });
}
}
this.nodes = Array.from(layers).map(([name, value]) => new hickle.Node(name, value));
}
};
hickle.Argument = class {
constructor(name, value) {
this.name = name;
this.value = value;
}
};
hickle.Value = class {
constructor(name, type, initializer = null) {
if (typeof name !== 'string') {
throw new hickle.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = !type && initializer ? initializer.type : type;
this.initializer = initializer;
}
};
hickle.Node = class {
constructor(name, parameters) {
this.type = { name: 'Weights' };
this.name = name;
this.inputs = parameters.map((parameter) => {
return new hickle.Argument(parameter.name, [
new hickle.Value(parameter.value.name, null, parameter.value)
]);
});
this.outputs = [];
this.attributes = [];
}
};
hickle.Tensor = class {
constructor(name, shape, type, littleEndian, data) {
this.name = name;
this.type = new hickle.TensorType(type, new hickle.TensorShape(shape));
this.encoding = littleEndian ? '<' : '>';
this._data = data;
}
get values() {
if (Array.isArray(this._data) || this._data === null) {
return null;
}
if (this._data instanceof Uint8Array) {
return this._data;
}
return this._data.peek();
}
};
hickle.TensorType = class {
constructor(dataType, shape) {
this.dataType = dataType;
this.shape = shape;
}
toString() {
return this.dataType + this.shape.toString();
}
};
hickle.TensorShape = class {
constructor(dimensions) {
this.dimensions = dimensions;
}
toString() {
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`) : '';
}
};
hickle.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Hickle model.';
}
};
export const ModelFactory = hickle.ModelFactory;
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

+46
View File
@@ -0,0 +1,46 @@
const imgdnn = {};
imgdnn.ModelFactory = class {
async match(context) {
const stream = context.stream;
const signature = [0x49, 0x4d, 0x47, 0x44, 0x4e, 0x4e]; // IMGDNN
if (stream && stream.length >= signature.length && stream.peek(6).every((value, index) => value === signature[index])) {
return 'imgdnn';
}
return null;
}
open(/* context */) {
throw new imgdnn.Error('Invalid file content. File contains undocumented IMGDNN data.');
}
};
imgdnn.Model = class {
constructor(metadata, model) {
this.format = 'IMGDNN';
this.modules = [new imgdnn.Graph(metadata, model)];
}
};
imgdnn.Graph = class {
constructor(/* metadata, model */) {
this.inputs = [];
this.outputs = [];
this.nodes = [];
}
};
imgdnn.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading IMGDNN model.';
}
};
export const ModelFactory = imgdnn.ModelFactory;
+600
View File
@@ -0,0 +1,600 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="description" content="Visualizer for neural network, deep learning and machine learning models." />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; frame-src 'none'">
<meta name="version" content="0.0.0">
<meta name="date" content="">
<title>Netron</title>
<link rel="stylesheet" type="text/css" href="grapher.css">
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
<link rel="icon" type="image/png" href="icon.png">
<link rel="apple-touch-icon" type="image/png" href="icon.png">
<link rel="apple-touch-icon-precomposed" type="image/png" href="icon.png">
<link rel="fluid-icon" type="image/png" href="icon.png">
<script type="text/javascript" src="index.js"></script>
<style>
html { touch-action: none; overflow: hidden; width: 100%; height: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; text-rendering: optimizeLegibility; -webkit-text-rendering: optimizeLegibility; -moz-text-rendering: optimizeLegibility; -ms-text-rendering: optimizeLegibility; -o-text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-font-smoothing: antialiased; -ms-font-smoothing: antialiased; -o-font-smoothing: antialiased; }
body { touch-action: none; overflow: hidden; width: 100%; height: 100%; margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; font-size: 12px; text-rendering: geometricPrecision; }
button { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC"; }
.center { position: absolute; margin: auto; top: 0; right: 0; bottom: 0; left: 0; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.select { user-select: text; -webkit-user-select: text; -moz-user-select: text; }
.target { display: flex; height: 100%; width: 100%; overflow: auto; outline: none; touch-action: pan-x pan-y; }
.canvas { margin: auto; flex-shrink: 0; text-rendering: geometricPrecision; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.open-file-dialog { display: none; }
.default { background-color: #ffffff; }
.default .logo { display: none; }
.default .target { display: flex; opacity: 1; }
.default .toolbar { display: flex; }
.toolbar { display: flex; align-items: center; position: absolute; bottom: 10px; left: 10px; padding: 0; margin: 0; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.toolbar button:focus { outline: 0; }
.toolbar-button { background: None; border-radius: 6px; border: 0; margin: 0; margin-right: 1px; padding: 0; fill: None; stroke: #777; cursor: pointer; width: 32px; height: 32px; user-select: none; }
.toolbar-path { display: flex; align-items: center; }
.toolbar-select { background: transparent; position: relative; width: 170px; margin: 4px 4px 4px 4px; }
.toolbar-select select { width: 100%; appearance: none; -webkit-appearance: none; -moz-appearance: none; font-family: inherit; font-size: 12px; line-height: 16px; border: 1px solid; padding: 3px 18px 3px 10px; border-radius: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; box-sizing: border-box; }
.toolbar-select select { background: #777; border-color: #777; color: #fff; }
.toolbar-select-arrow { position: absolute; top: 50%; right: 8px; transform: translateY(-50%); pointer-events: none; }
.toolbar-select-arrow { fill: #ffffff; }
.toolbar-select select:hover { background: #000000; border-color: #000000; }
.toolbar-select select:focus { outline: 0; }
.toolbar-path-back-button { background: #777; border-top-left-radius: 6px; border-bottom-left-radius: 6px; border: 1px solid; border-color: #777; margin: 4px 0px 4px 4px; padding: 5px 8px 5px 8px; cursor: pointer; color: #ffffff; font-size: 12px; line-height: 12px; transition: 0.1s; }
.toolbar-path-back-button:hover { background: #000000; border-color: #000000; }
.toolbar-path-name-button { background: #777; border: 0px; border-color: #777; color: #ffffff; border-left: 1px; border-left-color: #ffffff; margin: 4px 0 4px 1px; padding: 6px 8px 6px 8px; cursor: pointer; width: auto; font-size: 12px; line-height: 12px; transition: 0.1s; }
.toolbar-path-name-button:hover { background: #000000; border-color: #000000; }
.toolbar-path-name-button:last-child { border-top-right-radius: 6px; border-bottom-right-radius: 6px; }
.toolbar-icon .border { stroke: #fff; }
.toolbar-icon .stroke { stroke: #808080; }
.toolbar-icon .fill { fill: #808080; }
.toolbar-icon:hover .stroke { stroke: #000000; }
.toolbar-icon:hover .fill { fill: #000000; }
.message { display: none; opacity: 0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; flex-direction: column; justify-content: flex-start; }
.message-text { display: inline; text-align: center; width: 562px; font-size: 13px; line-height: 20px; margin-top: 50vh; padding-top: 56px; padding-bottom: 20px; margin-left: auto; margin-right: auto; user-select: text; -webkit-user-select: text; -moz-user-select: text; }
.welcome .message-text a { text-decoration: none; color: #666666; }
.welcome .message-text a:visited { color: inherit; }
.welcome .message-text a:hover { color: #242424; text-decoration: underline; }
.message-button { display: inline; text-align: center; width: 125px; margin-left: auto; margin-right: auto; }
.logo-text { top: -57px; width: 582px; transition: 0.1s; }
.logo-name { top: -170px; width: 582px; transition: 0.1s; }
.logo-icon { left: 248px; top: -18px; width: 106px; height: 106px; transition: 0.1s; }
.logo-spinner { left: 248px; top: -18px; width: 106px; height: 106px; display: none; }
.logo-stroke { stroke: #444444; }
.logo-fill { fill: #444444; }
.logo-border { stroke: #555555; }
.logo-glyph { fill: #444444; }
.logo-button { font-size: 12px; font-weight: bold; line-height: 1.25; text-align: center; vertical-align: middle; min-width: 5em; height: 2.7em; border-radius: 1.3em; transition: 0.1s; user-select: none; -webkit-user-select: none; -moz-user-select: none; color: #444444; background-color: #ececec; border: 1px solid #444444; }
.logo-button:hover { color: #ececec; background-color: #444444; cursor: pointer; transition: 0.2s; }
.logo-button:focus { outline: 0; }
.logo-message { display: none; height: 0px; }
.logo-github { display: none; }
.open-file-button { top: 170px; left: 0px; width: 10.5em; }
.progress { top: 120px; height: 2px; width: 400px; }
.progress-bar { height: 100%; width: 0%; background-color: #444444; }
.notification .logo-name { display: none; }
.notification .open-file-button { display: none; }
.notification .progress { display: none; }
.welcome body { background-color: #ececec; }
.welcome { background-color: #ececec; color: #242424; }
.welcome .message-text { display: none; opacity: 0; }
.welcome .message-button { display: none; opacity: 0; }
.welcome .target { display: none; opacity: 0; }
.welcome .menu { background-color: #ffffff; }
.welcome.spinner .logo-spinner { display: block; -webkit-animation: orbit 0.5s infinite linear; animation: orbit 0.5s infinite linear; cursor: wait; }
.welcome.spinner .menu-button { display: none; }
.welcome.notification .menu-button { display: none; }
.notification body { background-color: #ececec; }
.notification .message { display: flex; opacity: 1; }
.notification .message-text { display: inline; opacity: 1; }
.notification .message-button { display: inline; opacity: 1; }
.alert { background-color: #ececec; color: #242424; }
.alert .target { display: none; opacity: 0; }
.alert .toolbar { display: none; opacity: 0; }
.alert .menu { display: none; opacity: 0; }
.alert .logo { display: none; opacity: 0; }
.alert .message { display: flex; opacity: 1; }
.alert .message-text { display: inline; opacity: 1; width: 50%; padding-top: 0px; }
.alert .message-button { display: inline; opacity: 1; }
.about { overflow: hidden; }
.about .toolbar { display: none; }
.about .logo { display: block; background-color: #ececec; color: #666666; }
.about .logo-message { display: block; top: 132px; font-size: 14px; }
.about .logo-github { display: block; top: 340px; width: 48px; height: 48px; }
.about a { text-decoration: none; color: #666666; }
.about a:visited { color: inherit; }
.about a:hover { color: #242424; }
.about .open-file-button { display: none; }
.about .logo-name { display: none; }
.about .notification { display: none; }
.about .progress { display: none; }
.about .menu-button { display: none; }
.titlebar { color: #aaaaaa; display: none; height: 32px; position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 2; -webkit-app-region: drag; }
.titlebar-visible { display: block; }
.titlebar-content { display: block; padding: 0 142px; height: 100%; text-align: center; font-size: 14px; line-height: 32px; transition: all .1s ease-in-out; user-select: none; }
.titlebar-content-text { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.spinner .titlebar-content { opacity: 0; }
.active .titlebar { color: #464646; transition: all 0.05s ease-in-out; }
.titlebar-control-box { display: none; align-items: center; flex-direction: row-reverse; height: 100%; position: absolute; top: 0; right: 0; width: 138px; }
.titlebar-control-box-visible { display: flex; }
.titlebar-icon { width: 1em; height: 1em; vertical-align: -0.15em; fill: currentColor; overflow: hidden; }
.titlebar-button { display: flex; justify-content: center; align-items: center; width: 46px; height: 32px; user-select: none; -webkit-app-region: no-drag; }
.titlebar-button:hover { color: #000000; background-color: rgba(0, 0, 0, 0.15); }
.titlebar-button-close:hover { color: #ffffff; background-color: #b43029; }
.menu-button { display: flex; justify-content: center; align-items: center; color: #aaaaaa; font-size: 20px; height: 32px; width: 32px; position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 2; -webkit-app-region: no-drag; -webkit-app-region: no-drag; user-select: none; }
.menu-button:hover { color: #000000; }
.menu { display: block; position: absolute; left: -17em; width: 17em; top: 0; height: 100%; z-index: 2; background-color: #ececec; border-right: 1px solid rgba(255, 255, 255, 0.5); padding-top: 40px; padding-bottom: 2px; margin-left: 0; margin-top: 0; overflow: hidden; transition: 0.1s; }
.menu .menu-group { margin-bottom: 12px; }
.menu .menu-group .menu-group-header { display: block; border: none; border-radius: 0; color: black; width: 100%; text-align: left; margin: 4px 12px 5px 12px; white-space: no-wrap; font-size: 11px; font-weight: bold; color: #bbbbbb; white-space: nowrap; }
.menu .menu-group .menu-command { display: block; border: none; border-radius: 0; background-color: transparent; color: black; width: 100%; text-align: left; padding: 4px 12px 5px 12px; font-size: 12px; }
.menu .menu-group .menu-command:focus { color: #ffffff; background-color: #2e6bd2; outline: none; }
.menu .menu-group .menu-command:disabled { color: #888888; }
.menu .menu-group .menu-command .menu-label { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.menu .menu-group .menu-command .menu-shortcut { display: block; float: right; margin-left: 25px; color: #888888; }
.menu .menu-group .menu-separator { border-top: 1px; border-bottom: 0; border-style: solid; border-color: #e5e5e5; margin-left: 12px; margin-right: 12px; }
.about .titlebar-visible { opacity: 0; }
@-webkit-keyframes orbit { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }
@keyframes orbit { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }
.welcome.spinner .logo-spinner-stroke { stroke: #ececec; }
.welcome.spinner .logo-name { display: none; }
.welcome.spinner .open-file-button { display: none; }
.welcome.spinner .target { display: flex; opacity: 0; }
.welcome .notification .logo-name { display: none; }
.welcome .toolbar { display: none; }
@media (prefers-color-scheme: dark) {
:root { color-scheme: dark; }
.default { background-color: #404040; }
.target { background-color: #404040; }
.welcome { background-color: #1e1e1e; color: #888888; }
.alert { background-color: #1e1e1e; color: #888888; }
.logo-stroke { stroke: #888888; }
.logo-fill { fill: #888888; }
.logo-border { stroke: #000000; }
.logo-glyph { fill: #888888; }
.logo-spinner-stroke { stroke: #ffffff; }
.logo-button { color: #888888; background-color: #1e1e1e; border-color: #888888; }
.logo-button:hover { color: #1e1e1e; background-color: #888888; }
.welcome .progress-bar { background-color: #888888; }
.welcome .menu { background-color: #2d2d2d }
.about .logo { background-color: #1e1e1e; color: #888888; }
.about a { color: #c6c6c6; }
.about a:hover { color: #565656; }
.welcome .message-text a { color: #c6c6c6; }
.welcome .message-text a:visited { color: #c6c6c6; }
.welcome .message-text a:hover { color: #565656; }
.toolbar-icon .border { stroke: #333333; }
.toolbar-icon .stroke { stroke: #aaaaaa; }
.toolbar-icon .fill { fill: #aaaaaa; }
.toolbar-icon:hover .stroke { stroke: #dfdfdf; }
.toolbar-icon:hover .fill { fill: #dfdfdf; }
.toolbar-path-back-button { background: #aaaaaa; border-color: #aaaaaa; color: #333333; }
.toolbar-path-back-button:hover { background: #dfdfdf; border-color: #dfdfdf; }
.toolbar-path-name-button { background: #aaaaaa ; border-color: #aaaaaa; color: #404040; }
.toolbar-path-name-button:hover { background: #dfdfdf; border-color: #dfdfdf; }
.toolbar-select select { background: #aaaaaa; border-color: #aaaaaa; color: #404040; }
.toolbar-select select:hover { background: #dfdfdf; border-color: #dfdfdf; color: #404040; }
.toolbar-select-arrow { fill: #404040; }
.titlebar { color: #949494; }
.welcome body { background-color: #1e1e1e; }
.default body { background-color: #404040; }
.active .titlebar { color: #c4c4c4; }
.titlebar-button:hover { color: #ffffff; background-color: rgba(0, 0, 0, 0.15); }
.titlebar-button-close:hover { color: #ffffff; background-color: #b43029; }
.menu-button { color: #aaaaaa; }
.menu-button:hover { color: #ffffff; }
.menu { background-color: #2d2d2d; border-color: rgba(0, 0, 0, 0); }
.menu .menu-group .menu-group-header { color: #666666; }
.menu .menu-group .menu-command { color: #ffffff; }
.menu .menu-group .menu-command:focus { color: #ffffff; background-color: #2e6bd2; }
.menu .menu-group .menu-command:disabled { color: #888888; }
.menu .menu-group .menu-command .shortcut { color: #888888; }
.menu .menu-group .menu-separator { border-color: #363636; }
}
@media all and (max-width: 640px) {
.logo { width: 240px; }
.logo-text { opacity: 0; }
.logo-name { opacity: 0; }
.logo-icon { left: 0; width: 128px; height: 128px; }
.logo-spinner { left: 0; width: 128px; height: 128px; }
.logo .open-file-button { top: 204px; left: 0; }
.message-text { padding-top: 68px; width: 320px; }
.progress { top: 160px; height: 2px; width: 100px; }
.about .logo { width: 100%; padding-left: 0; padding-right: 0; }
.about .logo-message { top: 175px; font-size: 12px; }
.about .logo-github { top: 370px; }
}
.sidebar { display: flex; flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; font-size: 12px; height: 100%; right: -100%; position: fixed; transition: 0.1s; top: 0; background-color: #ececec; color: #242424; overflow: hidden; border-left: 1px solid rgba(255, 255, 255, 0.5); opacity: 0; }
.sidebar-title { font-weight: bold; font-size: 12px; letter-spacing: 0.5px; text-transform: uppercase; height: 20px; margin: 0; padding: 20px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.sidebar-closebutton { padding: 8px 8px 8px 32px; text-decoration: none; font-size: 25px; color: #777777; opacity: 1.0; display: block; transition: 0.2s; position: absolute; top: 0; right: 15px; margin-left: 50px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.sidebar-closebutton:hover { color: #242424; }
.sidebar-content { display: flex; flex-direction: column; flex-grow: 1; height: 0; }
.sidebar-header { font-weight: bold; font-size: 12px; letter-spacing: 0.5px; text-transform: uppercase; height: 20px; margin: 0; margin-top: 30px; padding-top: 10px; padding-bottom: 10px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.sidebar-section { font-weight: bold; font-size: 11px; text-transform: uppercase; line-height: 1.25; margin-top: 16px; margin-bottom: 16px; display: block; user-select: none; -webkit-user-select: none; -moz-user-select: none; cursor: default; }
.sidebar-object { flex-grow: 1; padding: 0px 20px 20px 20px; overflow-y: auto; }
.sidebar-item { margin-bottom: 0px; display: block; }
.sidebar-item-name { float: left; font-size: 11px; min-width: 95px; max-width: 95px; padding-right: 5px; padding-top: 7px; display: block; }
.sidebar-item-name input { color: #777; font-family: inherit; font-size: inherit; color: inherit; background-color: inherit; width: 100%; text-align: right; margin: 0; padding: 0; border: 0; outline: none; text-overflow: ellipsis; }
.sidebar-item-value-list { margin: 0; margin-left: 105px; overflow: hidden; display: block; padding: 0; }
.sidebar-item-value { font-size: 11px; background-color: #fcfcfc; border-radius: 2px; border: 1px solid #fcfcfc; margin-top: 3px; margin-bottom: 3px; overflow: auto; }
.sidebar-item-value-content { background-color: #f8f8f8; border: 1px solid #f8f8f8; }
.sidebar-item-value b { font-weight: bold; }
.sidebar-item-value code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; overflow: auto; white-space: pre-wrap; word-wrap: break-word; }
.sidebar-item-value pre { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; margin: 0; overflow: auto; white-space: pre; word-wrap: normal; display: block; }
.sidebar-item-value-line { padding: 4px 6px 4px 6px; overflow-x: auto; white-space: pre; }
.sidebar-item-value-line-wrap { white-space: pre-wrap; overflow-x: hidden; overflow-wrap: break-word; }
.sidebar-item-value-line-break { padding: 4px 6px 4px 6px; overflow-x: auto; white-space: pre; }
.sidebar-item-value-line-link { padding: 4px 6px 4px 6px; cursor: default; overflow-x: auto; white-space: nowrap; }
.sidebar-item-value-line-link:hover { text-decoration: underline; }
.sidebar-item-value-line-border { padding: 4px 6px 4px 6px; border-top: 1px solid rgba(27, 31, 35, 0.05); }
.sidebar-item-value-line-content { white-space: pre; word-wrap: normal; overflow: auto; display: block; }
.sidebar-item-value-expander { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; float: right; color: #aaa; cursor: pointer; user-select: none; -webkit-user-select: none; -moz-user-select: none; padding: 4px 6px 4px 4px; }
.sidebar-item-value-expander:hover { color: #333; }
.sidebar-item-value-button { display: flex; justify-content: center; align-items: center; font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; width: 22px; height: 22px; cursor: pointer; user-select: none; -webkit-user-select: none; -moz-user-select: none; color: #aaa; }
.sidebar-item-value-button svg use { fill: #aaa; stroke: #aaa; }
.sidebar-item-value-button:hover svg use { fill: #333; stroke: #333; }
.sidebar-item-value-button-tool { float: right; padding-left: 3px; }
.sidebar-item-value-button-context { float: right; }
.sidebar-item-selector {
font-family: inherit; font-size: 12px;
background-color: #fcfcfc; border: #fcfcfc; color: #333;
border-radius: 2px; width: 100%; height: 23px; padding: 3px 12px 3px 7px;
margin-top: 3px; margin-bottom: 3px; outline: none;
box-sizing: border-box; -moz-box-sizing: border-box;
appearance: none; -webkit-appearance: none; -moz-appearance: none;
background-image: linear-gradient(45deg, transparent 50%, #333 50%), linear-gradient(135deg, #333 50%, transparent 50%);
background-position: calc(100% - 12px) calc(10px), calc(100% - 7px) calc(10px);
background-size: 5px 5px, 5px 5px;
background-repeat: no-repeat;
}
.sidebar-separator { margin-bottom: 20px; }
.sidebar-find-search { display: flex; align-items: center; background: #fff; border-radius: 16px; margin: 0px 20px 8px 20px; padding: 0px 8px 0px 8px; }
.sidebar-find-query { width: 100vw; background: none; font-family: inherit; font-size: 13px; padding: 8px 8px 8px 8px; border: 0; outline: 0; }
.sidebar-find-toggle { margin-left: auto; margin-right: 2px; width: 16px; height: 16px; cursor: pointer; }
.sidebar-find-toggle input[type="checkbox"] { display: none; }
.sidebar-find-toggle input[type="checkbox"]:not(:checked) + svg { fill: #ccc; stroke: #ccc; }
.sidebar-find-toggle input[type="checkbox"]:checked + svg { fill: #555; stroke: #555; }
.sidebar-find-toggle-icon { stroke: #555; fill: #555; width: 16px; height: 16px; }
.sidebar-find-content { flex-grow: 1; padding: 0px 20px 20px 20px; overflow-y: auto; list-style-type: none; margin: 0; outline: 0; }
.sidebar-find-content li *:first-child { margin-right: 2px; }
.sidebar-find-content li { color: #666; font-size: 13px; height: 22px; line-height: 22px; padding: 0 12px 0 12px; outline: 0; white-space: nowrap; border-radius: 3px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.sidebar-find-content li.focus { background: #e5e5e5; color: #000; }
.sidebar-find-content-icon { stroke: #555; fill: #555; float: left; width: 16px; height: 16px; padding: 3px; pointer-events: none; }
.sidebar-documentation { flex-grow: 1; padding: 0px 20px 20px 20px; overflow-y: auto; font-size: 13px; line-height: 1.5; margin: 0; }
.sidebar-documentation h1 { font-weight: bold; font-size: 13px; line-height: 1.25; border-bottom: 1px solid #e8e8e8; padding-bottom: 0.3em; margin-top: 0; margin-bottom: 16px; }
.sidebar-documentation h2 { font-weight: bold; font-size: 13px; line-height: 1.25; margin-top: 20px; margin-bottom: 16px; text-transform: uppercase; border: 0; }
.sidebar-documentation h3 { font-weight: bold; font-size: 11px; line-height: 1.25; }
.sidebar-documentation p { margin-top: 4px; margin-bottom: 4px; margin-left: 0px; }
.sidebar-documentation a { color: #237; }
.sidebar-documentation code { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 12px; background-color: rgba(27, 31, 35, 0.05); padding: 0.2em 0.4em; margin: 0; border-radius: 3px; }
.sidebar-documentation pre { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 12px; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; }
.sidebar-documentation pre code { font-size: 13px; padding: 16px; line-height: 1.45; background-color: transparent; padding: 0; border-radius: 0; }
.sidebar-documentation tt { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-weight: bold; font-size: 90%; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; padding: 0.2em 0.4em; margin: 0; }
.sidebar-documentation dl dt { font-size: 13px; font-weight: bold; padding: 0; margin-top: 16px; margin-left: 0px; }
.sidebar-documentation dd { padding: 0 16px; margin-left: 0; margin-bottom: 16px; }
.sidebar-documentation ul { margin-top: 6px; margin-bottom: 6px; padding-left: 20px; }
.sidebar-documentation blockquote { margin-left: 15px; margin-right: 15px; }
@media (prefers-color-scheme: dark) {
.sidebar html { color: #dfdfdf; }
.sidebar { background-color: #2d2d2d; color: #dfdfdf; border-left: 1px solid rgba(0, 0, 0, 0); }
.sidebar-closebutton { padding: 8px 8px 8px 32px; text-decoration: none; font-size: 25px; color: #777777; opacity: 1.0; display: block; transition: 0.2s; position: absolute; top: 0; right: 15px; margin-left: 50px; user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.sidebar-closebutton:hover { color: #ffffff; }
.sidebar-item-value { background-color: #383838; border-color: #383838; }
.sidebar-item-value-content { background-color: #3e3e3e; border-color: #3e3e3e; }
.sidebar-item-value-line-border { border-color: rgba(0, 0, 0, 0.09); }
.sidebar-item-selector { background-color: #383838; border: #383838; color: #dfdfdf; background-image: linear-gradient(45deg, transparent 50%, #aaa 50%), linear-gradient(135deg, #aaa 50%, transparent 50%); }
.sidebar-item-disable-select { user-select: none; -webkit-user-select: none; -moz-user-select: none; }
.sidebar-header { border-bottom-color: #2d2d2d; color: #dfdfdf; }
.sidebar-documentation h1 { border-bottom: 1px solid #424242; color: #dfdfdf; }
.sidebar-documentation h2 { color: #dfdfdf; }
.sidebar-documentation p { color: #aaaaaa; }
.sidebar-documentation a { color: #6688aa; }
.sidebar-documentation tt { background-color:#1e1e1e; }
.sidebar-documentation code { background-color: #1e1e1e; }
.sidebar-documentation pre { background-color: #1e1e1e; }
.sidebar-find-search { background: #383838; color: #dfdfdf; border-color: #424242; }
.sidebar-find-toggle input[type="checkbox"]:not(:checked) + svg { fill: #555; stroke: #555; }
.sidebar-find-toggle input[type="checkbox"]:checked + svg { fill: #aaa; stroke: #aaa; }
.sidebar-find-content li { color: #aaaaaa; }
.sidebar-find-content li.focus { background: #383838; color: #dfdfdf; }
.sidebar-find-content-icon { stroke: #888888; fill: #888888; }
.sidebar-item-value-expander { color: #888; }
.sidebar-item-value-expander:hover { color: #e5e5e5; }
.sidebar-item-value-button { color: #888; }
.sidebar-item-value-button svg use { fill: #888; stroke: #888; }
.sidebar-item-value-button:hover svg use { fill: #e5e5e5; stroke: #e5e5e5; }
}
@media screen and (prefers-reduced-motion: reduce) {
.menu { transition: none; }
.sidebar { transition: none; }
}
</style>
</head>
<body class="welcome spinner">
<div id="target" class="target" tabindex="0">
</div>
<div id="sidebar" class="sidebar">
<h1 id="sidebar-title" class="sidebar-title"></h1>
<a id="sidebar-closebutton" class="sidebar-closebutton" href="javascript:void(0)" draggable="false">&times;</a>
<div id="sidebar-content" class="sidebar-content"></div>
<svg width="0" height="0" display="none">
<defs>
<symbol id="sidebar-icon-node" viewBox="0 0 20 20">
<circle cx="10" cy="10" r="6" stroke-width="2" fill="none"/>
</symbol>
<symbol id="sidebar-icon-connection" viewBox="0 0 20 20">
<line x1="4" y1="10" x2="15" y2="10" stroke-width="2" />
<polyline points="11,6 15,10 11,14" stroke-width="2" />
</symbol>
<symbol id="sidebar-icon-weight" viewBox="0 0 20 20">
<circle cx="5" cy="5" r="1" />
<circle cx="10" cy="5" r="1" />
<circle cx="15" cy="5" r="1" />
<circle cx="5" cy="10" r="1" />
<circle cx="10" cy="10" r="1" />
<circle cx="15" cy="10" r="1" />
<circle cx="5" cy="15" r="1" />
<circle cx="10" cy="15" r="1" />
<circle cx="15" cy="15" r="1" />
</symbol>
</defs>
</svg>
</div>
<div id="toolbar" class="toolbar">
<button id="zoom-in-button" class="toolbar-button" title="Zoom In">
<svg class="toolbar-icon" viewbox="0 0 100 100">
<circle class="border" cx="50" cy="50" r="35" stroke-width="8" stroke="#fff"></circle>
<line class="border" x1="50" y1="38" x2="50" y2="62" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<line class="border" x1="38" y1="50" x2="62" y2="50" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<line class="border" x1="78" y1="78" x2="82" y2="82" stroke-width="12" stroke-linecap="square" stroke="#fff"></line>
<circle class="stroke" cx="50" cy="50" r="35" stroke-width="4"></circle>
<line class="stroke" x1="50" y1="38" x2="50" y2="62" stroke-width="4" stroke-linecap="round"></line>
<line class="stroke" x1="38" y1="50" x2="62" y2="50" stroke-width="4" stroke-linecap="round"></line>
<line class="stroke" x1="78" y1="78" x2="82" y2="82" stroke-width="8" stroke-linecap="square"></line>
</svg>
</button>
<button id="zoom-out-button" class="toolbar-button" title="Zoom Out">
<svg class="toolbar-icon" viewbox="0 0 100 100">
<circle class="border" cx="50" cy="50" r="35" stroke-width="8" stroke="#fff"></circle>
<line class="border" x1="38" y1="50" x2="62" y2="50" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<line class="border" x1="78" y1="78" x2="82" y2="82" stroke-width="12" stroke-linecap="square" stroke="#fff"></line>
<circle class="stroke" cx="50" cy="50" r="35" stroke-width="4"></circle>
<line class="stroke" x1="38" y1="50" x2="62" y2="50" stroke-width="4" stroke-linecap="round"></line>
<line class="stroke" x1="78" y1="78" x2="82" y2="82" stroke-width="8" stroke-linecap="square"></line>
</svg>
</button>
<button id="sidebar-model-button" class="toolbar-button" title="Model Properties">
<svg class="toolbar-icon" viewbox="0 0 100 100">
<rect class="border" x="12" y="12" width="76" height="76" rx="16" ry="16" stroke-width="8"></rect>
<line class="border" x1="28" y1="37" x2="32" y2="37" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<line class="border" x1="28" y1="50" x2="32" y2="50" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<line class="border" x1="28" y1="63" x2="32" y2="63" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<line class="border" x1="40" y1="37" x2="70" y2="37" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<line class="border" x1="40" y1="50" x2="70" y2="50" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<line class="border" x1="40" y1="63" x2="70" y2="63" stroke-width="8" stroke-linecap="round" stroke="#fff"></line>
<rect class="stroke" x="12" y="12" width="76" height="76" rx="16" ry="16" stroke-width="4"></rect>
<line class="stroke" x1="28" y1="37" x2="32" y2="37" stroke-width="4" stroke-linecap="round"></line>
<line class="stroke" x1="28" y1="50" x2="32" y2="50" stroke-width="4" stroke-linecap="round"></line>
<line class="stroke" x1="28" y1="63" x2="32" y2="63" stroke-width="4" stroke-linecap="round"></line>
<line class="stroke" x1="40" y1="37" x2="70" y2="37" stroke-width="4" stroke-linecap="round"></line>
<line class="stroke" x1="40" y1="50" x2="70" y2="50" stroke-width="4" stroke-linecap="round"></line>
<line class="stroke" x1="40" y1="63" x2="70" y2="63" stroke-width="4" stroke-linecap="round"></line>
</svg>
</button>
<button id="sidebar-target-button" class="toolbar-button" title="Target Properties">
<svg class="toolbar-icon" viewBox="0 0 100 100">
<rect class="border" x="12" y="12" width="76" height="76" rx="16" ry="16" stroke-width="8"></rect>
<circle class="border" cx="50" cy="50" r="18" stroke-width="8"></circle>
<circle class="border" cx="50" cy="50" r="3" stroke-width="8"></circle>
<rect class="stroke" x="12" y="12" width="76" height="76" rx="16" ry="16" stroke-width="4"></rect>
<circle class="stroke" cx="50" cy="50" r="18" stroke-width="4"></circle>
<circle class="stroke fill" cx="50" cy="50" r="3" stroke-width="4"></circle>
</svg>
</button>
<div id="toolbar-navigator" class="toolbar-select">
<select id="toolbar-target-selector">
<option value="Graphs" disabled="true">&#x2014; Graphs &#x2014;&#x2014;</option>
<option value="add">add</option>
<option value="subtract">subtract</option>
<option value="multiply">multiply</option>
<option value="Functions" disabled="true">&#x2014; Functions &#x2014;&#x2014;</option>
<option value="line">foo_bar.foo</option>
</select>
<svg class="toolbar-select-arrow" viewBox="0 0 10 6" width="10" height="6">
<path d="M0 0L5 6L10 0Z" />
</svg>
</div>
<div id="toolbar-path" class="toolbar-path">
<button id="toolbar-path-back-button" class="toolbar-path-back-button" title="Back">
&#x276E;
</button>
</div>
</div>
<div id="logo" class="center logo">
<a href="https://github.com/lutzroeder/netron" target="blank_">
<svg class="center logo-text" viewbox="0 0 5120 1024">
<g transform="scale(9) translate(-44,-15)">
<g transform="matrix(100,0,0,100,60.9965,126)">
<path class="logo-glyph" d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(100,0,0,100,164.341,126)">
<path class="logo-glyph" d="M0.089,0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,0L0.089,0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(100,0,0,100,244.491,126)">
<path class="logo-glyph" d="M0.216,0L0.216,-0.654L0.019,-0.654L0.019,-0.715L0.478,-0.715L0.478,-0.654L0.281,-0.654L0.281,0L0.216,0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(100,0,0,100,323.031,126)">
<path class="logo-glyph" d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,0L0.444,0L0.193,-0.341L0.154,-0.341L0.154,0L0.089,0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(100,0,0,100,520.979,126)">
<path class="logo-glyph" d="M0.089,0L0.089,-0.745L0.595,-0.147L0.595,-0.715L0.656,-0.715L0.656,0.021L0.15,-0.578L0.15,0L0.089,0Z" style="fill-rule:nonzero;"/>
</g>
</g>
</svg>
<svg class="center logo-icon" viewbox="0 0 1024 1024">
<circle class="logo-stroke" cx="512" cy="512" r="431" fill="none" stroke-width="32"></circle>
<circle class="logo-border" cx="512" cy="512" r="450" fill="none" stroke-width="6"></circle>
<circle class="logo-border" cx="512" cy="512" r="412" fill="none" stroke-width="6"></circle>
<line class="logo-stroke" x1="296" y1="392" x2="540" y2="280" stroke-width="12"></line>
<line class="logo-stroke" x1="296" y1="632" x2="540" y2="280" stroke-width="12"></line>
<line class="logo-stroke" x1="296" y1="392" x2="540" y2="435" stroke-width="12"></line>
<line class="logo-stroke" x1="296" y1="632" x2="540" y2="435" stroke-width="12"></line>
<line class="logo-stroke" x1="296" y1="392" x2="540" y2="590" stroke-width="12"></line>
<line class="logo-stroke" x1="296" y1="632" x2="540" y2="590" stroke-width="12"></line>
<line class="logo-stroke" x1="296" y1="392" x2="540" y2="744" stroke-width="12"></line>
<line class="logo-stroke" x1="296" y1="632" x2="540" y2="744" stroke-width="12"></line>
<line class="logo-stroke" x1="540" y1="280" x2="785" y2="512" stroke-width="12"></line>
<line class="logo-stroke" x1="540" y1="590" x2="785" y2="512" stroke-width="12"></line>
<line class="logo-stroke" x1="540" y1="435" x2="785" y2="512" stroke-width="12"></line>
<line class="logo-stroke" x1="540" y1="744" x2="785" y2="512" stroke-width="12"></line>
<g transform="translate(296, 392)">
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
</g>
<g transform="translate(296, 632)">
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
</g>
<g transform="translate(540, 280)">
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
</g>
<g transform="translate(540, 435)">
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
</g>
<g transform="translate(540, 590)">
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
</g>
<g transform="translate(540, 744)">
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
</g>
<g transform="translate(785, 512)">
<circle class="logo-fill" cx="0" cy="0" r="51"></circle>
<circle class="logo-border" cx="0" cy="0" r="51" fill="none" stroke-width="6"></circle>
</g>
</svg>
<svg id="logo-spinner" class="center logo-spinner" viewbox="0 0 1024 1024">
<g transform="translate(512, 512)" style="opacity: 1">
<path class="logo-spinner-stroke" d="M-431,0 A-431,-431 0 0,1 0,-431" stroke-width="24" fill="None"></path>
</g>
</svg>
</a>
<a href="https://www.lutzroeder.com" target="blank_">
<svg class="center logo-name" viewbox="0 0 5120 300">
<g transform="scale(5.8) translate(20, 0)">
<g transform="matrix(30,0,0,30,18.9123,38)">
<path class="logo-glyph" d="M0.089,-0L0.089,-0.715L0.154,-0.715L0.154,-0.061L0.399,-0.061L0.399,-0L0.089,-0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,46.7613,38)">
<path class="logo-glyph" d="M0.086,-0.715L0.15,-0.715L0.15,-0.248C0.15,-0.177 0.166,-0.125 0.198,-0.091C0.23,-0.056 0.28,-0.039 0.346,-0.039C0.412,-0.039 0.46,-0.056 0.493,-0.091C0.525,-0.125 0.541,-0.177 0.541,-0.248L0.541,-0.715L0.606,-0.715L0.606,-0.269C0.606,-0.172 0.584,-0.1 0.542,-0.052C0.499,-0.005 0.433,0.019 0.346,0.019C0.259,0.019 0.193,-0.005 0.15,-0.052C0.107,-0.1 0.086,-0.172 0.086,-0.269L0.086,-0.715Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,83.5133,38)">
<path class="logo-glyph" d="M0.216,-0L0.216,-0.654L0.019,-0.654L0.019,-0.715L0.478,-0.715L0.478,-0.654L0.281,-0.654L0.281,-0L0.216,-0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,114.421,38)">
<path class="logo-glyph" d="M0.012,-0L0.437,-0.656L0.074,-0.656L0.074,-0.715L0.548,-0.715L0.125,-0.06L0.505,-0.06L0.505,-0L0.012,-0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,171.777,38)">
<path class="logo-glyph" d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,-0L0.444,-0L0.193,-0.341L0.154,-0.341L0.154,-0L0.089,-0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,-0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,203.607,38)">
<path class="logo-glyph" d="M0.437,-0.039C0.479,-0.039 0.519,-0.047 0.557,-0.063C0.595,-0.078 0.629,-0.101 0.659,-0.131C0.689,-0.161 0.712,-0.196 0.727,-0.234C0.743,-0.273 0.751,-0.313 0.751,-0.356C0.751,-0.399 0.743,-0.44 0.728,-0.478C0.712,-0.516 0.689,-0.55 0.659,-0.581C0.63,-0.611 0.596,-0.634 0.558,-0.649C0.52,-0.665 0.48,-0.673 0.437,-0.673C0.395,-0.673 0.355,-0.665 0.317,-0.649C0.28,-0.634 0.246,-0.611 0.216,-0.581C0.186,-0.55 0.163,-0.516 0.147,-0.478C0.132,-0.44 0.124,-0.399 0.124,-0.356C0.124,-0.313 0.132,-0.272 0.147,-0.234C0.163,-0.196 0.186,-0.161 0.216,-0.131C0.246,-0.101 0.279,-0.078 0.316,-0.062C0.354,-0.047 0.394,-0.039 0.437,-0.039ZM0.82,-0.356C0.82,-0.306 0.81,-0.258 0.791,-0.212C0.772,-0.167 0.744,-0.126 0.708,-0.091C0.671,-0.055 0.63,-0.028 0.583,-0.009C0.537,0.01 0.488,0.019 0.437,0.019C0.386,0.019 0.337,0.01 0.291,-0.009C0.245,-0.028 0.203,-0.055 0.167,-0.091C0.131,-0.127 0.103,-0.168 0.084,-0.213C0.065,-0.258 0.055,-0.306 0.055,-0.356C0.055,-0.407 0.065,-0.455 0.084,-0.501C0.103,-0.546 0.131,-0.587 0.167,-0.623C0.203,-0.659 0.244,-0.685 0.29,-0.704C0.335,-0.722 0.385,-0.731 0.437,-0.731C0.49,-0.731 0.539,-0.722 0.585,-0.703C0.631,-0.685 0.672,-0.658 0.708,-0.623C0.744,-0.587 0.772,-0.546 0.791,-0.501C0.81,-0.455 0.82,-0.407 0.82,-0.356Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,245.853,38)">
<path class="logo-glyph" d="M0.089,-0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,-0L0.089,-0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,277.243,38)">
<path class="logo-glyph" d="M0.154,-0.056L0.245,-0.056C0.319,-0.056 0.371,-0.06 0.402,-0.069C0.433,-0.077 0.459,-0.091 0.481,-0.111C0.511,-0.139 0.534,-0.174 0.549,-0.215C0.564,-0.257 0.572,-0.305 0.572,-0.358C0.572,-0.413 0.564,-0.461 0.549,-0.502C0.533,-0.544 0.51,-0.578 0.48,-0.605C0.457,-0.625 0.429,-0.64 0.396,-0.648C0.364,-0.657 0.306,-0.661 0.224,-0.661L0.154,-0.661L0.154,-0.056ZM0.089,-0L0.089,-0.715L0.2,-0.715C0.299,-0.715 0.37,-0.71 0.412,-0.7C0.453,-0.69 0.489,-0.674 0.519,-0.65C0.559,-0.618 0.589,-0.578 0.61,-0.528C0.631,-0.478 0.641,-0.421 0.641,-0.357C0.641,-0.293 0.631,-0.236 0.61,-0.186C0.589,-0.136 0.559,-0.096 0.52,-0.066C0.489,-0.042 0.454,-0.025 0.414,-0.015C0.374,-0.005 0.31,-0 0.222,-0L0.089,-0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,314.142,38)">
<path class="logo-glyph" d="M0.089,-0L0.089,-0.715L0.443,-0.715L0.443,-0.654L0.154,-0.654L0.154,-0.43L0.443,-0.43L0.443,-0.369L0.154,-0.369L0.154,-0.061L0.443,-0.061L0.443,-0L0.089,-0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,345.532,38)">
<path class="logo-glyph" d="M0.154,-0.658L0.154,-0.394L0.219,-0.394C0.28,-0.394 0.322,-0.404 0.346,-0.423C0.37,-0.442 0.382,-0.475 0.382,-0.522C0.382,-0.571 0.369,-0.606 0.345,-0.627C0.32,-0.648 0.278,-0.658 0.219,-0.658L0.154,-0.658ZM0.523,-0L0.444,-0L0.193,-0.341L0.154,-0.341L0.154,-0L0.089,-0L0.089,-0.715L0.22,-0.715C0.298,-0.715 0.356,-0.699 0.394,-0.667C0.433,-0.634 0.452,-0.585 0.452,-0.52C0.452,-0.464 0.436,-0.421 0.403,-0.389C0.37,-0.357 0.324,-0.341 0.266,-0.341L0.523,-0Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,376.911,38)">
<path class="logo-glyph" d="M0.06,-0.468L0.155,-0.731L0.228,-0.699L0.1,-0.452L0.06,-0.468Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(30,0,0,30,401.549,38)">
<path class="logo-glyph" d="M0.034,-0.12L0.09,-0.15C0.1,-0.115 0.118,-0.087 0.144,-0.068C0.169,-0.049 0.2,-0.039 0.236,-0.039C0.281,-0.039 0.316,-0.052 0.342,-0.079C0.367,-0.106 0.38,-0.143 0.38,-0.19C0.38,-0.224 0.371,-0.253 0.354,-0.276C0.337,-0.299 0.3,-0.325 0.244,-0.355C0.172,-0.393 0.124,-0.427 0.101,-0.456C0.077,-0.485 0.065,-0.519 0.065,-0.56C0.065,-0.611 0.082,-0.652 0.116,-0.684C0.151,-0.716 0.195,-0.732 0.25,-0.732C0.286,-0.732 0.317,-0.724 0.344,-0.709C0.37,-0.694 0.392,-0.671 0.408,-0.641L0.358,-0.611C0.347,-0.631 0.333,-0.647 0.314,-0.658C0.295,-0.668 0.272,-0.674 0.246,-0.674C0.211,-0.674 0.183,-0.663 0.162,-0.643C0.141,-0.622 0.131,-0.594 0.131,-0.559C0.131,-0.509 0.172,-0.462 0.255,-0.419C0.27,-0.411 0.281,-0.405 0.289,-0.401C0.35,-0.367 0.391,-0.336 0.411,-0.306C0.432,-0.276 0.442,-0.237 0.442,-0.19C0.442,-0.126 0.423,-0.075 0.386,-0.037C0.348,0 0.297,0.019 0.233,0.019C0.186,0.019 0.146,0.007 0.113,-0.016C0.079,-0.039 0.053,-0.074 0.034,-0.12Z" style="fill-rule:nonzero;"/>
</g>
</g>
</svg>
</a>
<div class="center logo-message">
<div style="height: 30px; text-align: center;">Version <span id="version" class="select">{version}</span></div>
<div style="height: 30px; text-align: center;">Copyright &copy; <a href="https://www.lutzroeder.com" target="blank_">Lutz Roeder</a></div>
</div>
<a id="logo-github" class="center logo-github" href="https://github.com/lutzroeder/netron" target="blank_">
<svg viewbox="0 0 438.549 438.549">
<path class="logo-fill" d="M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365
c-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63
c0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996
c2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136
c-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559
c-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559
c-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997
c-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851
c5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136
c6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41
c-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126
c-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817
c-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994
c5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849
c7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24
c15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979
c-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146
c9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995
c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906
C438.536,184.851,428.728,148.168,409.132,114.573z"/>
</svg>
</a>
<button id="open-file-button" class="center logo-button open-file-button" tabindex="0">Open Model&hellip;</button>
<div class="center progress">
<div id="progress-bar" class="progress-bar"></div>
</div>
<input type="file" id="open-file-dialog" class="open-file-dialog" multiple="false" accept="">
<!-- Preload fonts to workaround Chrome SVG layout issue -->
<div style="font-weight: normal; color: rgba(0, 0, 0, 0.01); user-select: none;">.</div>
<div style="font-weight: bold; color: rgba(0, 0, 0, 0.01); user-select: none;">.</div>
<div style="font-weight: bold; color: rgba(0, 0, 0, 0.01); user-select: none;">.</div>
</div>
<div id="message" class="message">
<div id="message-text" class="message-text"></div>
<button id="message-button" class="logo-button message-button" tabindex="0">OK</button>
</div>
<div id="titlebar" class="titlebar">
<svg style="position: absolute; width: 0px; height: 0px; overflow: hidden;" aria-hidden="true">
<symbol id="icon-arrow-right" viewBox="0 0 1024 1024">
<path d="M698.75712 565.02272l-191.488 225.4848a81.73568 81.73568 0 0 1-62.48448 28.89728 81.89952 81.89952 0 0 1-62.40256-134.94272l146.432-172.4416-146.432-172.4416a81.92 81.92 0 0 1 124.88704-106.06592l191.488 225.4848a81.87904 81.87904 0 0 1 0 106.02496z"></path>
</symbol>
</svg>
<div id="titlebar-content" class="titlebar-content">
<span id="titlebar-content-text" class="titlebar-content-text"></span>
</div>
<div id="titlebar-control-box" class="titlebar-control-box">
<div id="titlebar-close" class="titlebar-button titlebar-button-close" title="Close">
<svg class="titlebar-icon" aria-hidden="true">
<path d="M 0,0 0,0.7 4.3,5 0,9.3 0,10 0.7,10 5,5.7 9.3,10 10,10 10,9.3 5.7,5 10,0.7 10,0 9.3,0 5,4.3 0.7,0 Z"></path>
</svg>
</div>
<div id="titlebar-toggle" class="titlebar-button" title="Maximize">
<svg id="titlebar-maximize" class="titlebar-icon" aria-hidden="true" style="position: absolute;">
<path d="M 0,0 0,10 10,10 10,0 Z M 1,1 9,1 9,9 1,9 Z"></path>
</svg>
<svg id="titlebar-restore" class="titlebar-icon" aria-hidden="true" style="position: absolute;">
<path d="m 2,1e-5 0,2 -2,0 0,8 8,0 0,-2 2,0 0,-8 z m 1,1 6,0 0,6 -1,0 0,-5 -5,0 z m -2,2 6,0 0,6 -6,0 z"></path>
</svg>
</div>
<div id="titlebar-minimize" class="titlebar-button" title="Minimize">
<svg class="titlebar-icon" aria-hidden="true">
<path d="M 0,5 10,5 10,6 0,6 Z"></path>
</svg>
</div>
</div>
</div>
<div id="menu" class="menu"></div>
<div id="menu-button" class="menu-button">&#8801;</div>
</body>
</html>
+118
View File
@@ -0,0 +1,118 @@
window.exports = {};
window.exports.require = function(id, callback) {
if (!/^[a-zA-Z0-9_-]+$/.test(id)) {
throw new Error("Invalid module '" + id + "'.");
}
var base = window.location.href || '';
base = base.split('?')[0].split('#')[0];
const index = base.lastIndexOf('/');
base = index > 0 ? base.substring(0, index + 1) : base;
base = base.lastIndexOf('/') === base.length - 1 ? base : base + '/';
var url = base + id + '.js';
var document = window.document;
var scripts = document.head.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (url === scripts[i].getAttribute('src')) {
throw new Error("Duplicate import of '" + url + "'.");
}
}
var script = document.createElement('script');
script.setAttribute('id', id);
script.setAttribute('type', 'module');
/* eslint-disable no-use-before-define */
var loadHandler = function() {
script.removeEventListener('load', loadHandler);
script.removeEventListener('error', errorHandler);
callback();
};
var errorHandler = function(e) {
script.removeEventListener('load', loadHandler);
script.removeEventListener('error', errorHandler);
callback(null, new Error("The script '" + e.target.src + "' failed to load."));
};
/* eslint-enable no-use-before-define */
script.addEventListener('load', loadHandler, false);
script.addEventListener('error', errorHandler, false);
script.setAttribute('src', url);
document.head.appendChild(script);
};
window.exports.preload = function(callback) {
var modules = [
['view'],
['json', 'xml', 'protobuf', 'hdf5', 'grapher', 'browser'],
['base', 'text', 'flatbuffers', 'flexbuffers', 'zip', 'tar', 'python']
];
var next = function() {
if (modules.length === 0) {
callback();
} else {
var ids = modules.pop();
/* eslint-disable no-loop-func */
var resolved = ids.length;
for (var i = 0; i < ids.length; i++) {
window.exports.require(ids[i], function(module, error) {
if (error) {
callback(null, error);
} else {
resolved--;
if (resolved === 0) {
next();
}
}
});
}
/* eslint-enable no-loop-func */
}
};
next();
};
window.exports.terminate = function(message) {
var document = window.document;
document.getElementById('message-text').innerText = message;
var button = document.getElementById('message-button');
button.style.display = 'none';
button.onclick = null;
document.body.setAttribute('class', 'welcome message');
if (window.__view__) {
/* eslint-disable no-unused-vars */
try {
window.__view__.show('welcome message');
} catch (error) {
// continue regardless of error
}
/* eslint-enable no-unused-vars */
}
};
window.addEventListener('error', function (event) {
var error = event instanceof window.ErrorEvent && event.error && event.error instanceof Error ? event.error : new Error(event && event.message ? event.message : JSON.stringify(event));
window.exports.terminate(error.message);
});
window.addEventListener('load', function() {
if (typeof Symbol !== 'function' || typeof Symbol.asyncIterator !== 'symbol' ||
typeof BigInt !== 'function' || typeof BigInt.asIntN !== 'function' || typeof BigInt.asUintN !== 'function' || typeof DataView.prototype.getBigInt64 !== 'function') {
throw new Error('Please update your browser to use this application.');
}
var ua = window.navigator.userAgent;
var chrome = ua.match(/Chrom(e|ium)\/([0-9]+)\./);
var safari = ua.match(/Version\/(\d+)\.(\d+).*Safari/);
var firefox = ua.match(/Firefox\/([0-9]+)\./);
if ((Array.isArray(chrome) && parseInt(chrome[2], 10) < 86) ||
(Array.isArray(safari) && (parseInt(safari[1], 10) < 16 || (parseInt(safari[1], 10) === 16 && parseInt(safari[2], 10) < 4))) ||
(Array.isArray(firefox) && parseInt(firefox[1], 10) < 114)) {
throw new Error('Please update your browser to use this application.');
}
window.exports.preload(function(value, error) {
if (error) {
window.exports.terminate(error.message);
} else {
var host = new window.exports.browser.Host();
window.__view__ = new window.exports.view.View(host);
window.__view__.start();
}
});
});

Some files were not shown because too many files have changed in this diff Show More