forked from sheetjs/docs.sheetjs.com
TensorFlow NodeJS / Kaioken demos
This commit is contained in:
parent
6836b4b450
commit
bbaf012efd
@ -5,6 +5,11 @@ pagination_prev: demos/index
|
||||
pagination_next: demos/frontend/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
<head>
|
||||
<script src="https://docs.sheetjs.com/tfjs/tf.min.js"></script>
|
||||
</head>
|
||||
@ -25,27 +30,151 @@ results back to spreadsheets.
|
||||
- ["JS Array Interchange"](#js-array-interchange) uses SheetJS to process sheets
|
||||
and generate rows of objects that can be post-processed.
|
||||
|
||||
:::info pass
|
||||
|
||||
Live code blocks in this page use the TF.js `4.14.0` standalone build.
|
||||
|
||||
For use in web frameworks, the `@tensorflow/tfjs` module should be used.
|
||||
|
||||
For use in NodeJS, the native bindings module is `@tensorflow/tfjs-node`.
|
||||
|
||||
:::
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
Each browser demo was tested in the following environments:
|
||||
|
||||
| Browser | TF.js version | Date |
|
||||
|:------------|:--------------|:-----------|
|
||||
| Chrome 122 | `4.14.0` | 2024-04-07 |
|
||||
| Safari 17.4 | `4.14.0` | 2024-03-23 |
|
||||
| Chrome 127 | `4.20.0` | 2024-08-16 |
|
||||
| Safari 17.4 | `4.20.0` | 2024-08-16 |
|
||||
|
||||
The NodeJS demo was tested in the following environments:
|
||||
|
||||
| NodeJS | TF.js version | Date |
|
||||
|:---------|:------------------------------|:-----------|
|
||||
| `22.3.0` | `4.20.0` (`@tensorflow/tfjs`) | 2024-08-16 |
|
||||
|
||||
The Kaioken demo was tested in the following environments:
|
||||
|
||||
| Kaioken | TF.js version | Date |
|
||||
|:----------|:--------------|:-----------|
|
||||
| `0.25.3` | `4.20.0` | 2024-08-16 |
|
||||
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
#### Standalone Browser Scripts
|
||||
|
||||
Live code blocks in this page use the TF.js `4.20.0` standalone build.
|
||||
|
||||
Standalone scripts are available on various CDNs including UNPKG. The latest
|
||||
version can be loaded with the following `SCRIPT` tag.
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded after the TF.js standalone script.
|
||||
|
||||
<CodeBlock language="html">{`\
|
||||
<!-- latest version of TF.js -->
|
||||
<script src="https://unpkg.com/@tensorflow/tfjs@latest/dist/tf.min.js"></script>
|
||||
<!-- use version ${current} -->
|
||||
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||||
</CodeBlock>
|
||||
|
||||
#### Frameworks and Bundlers
|
||||
|
||||
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||||
installation with Yarn and other package managers.
|
||||
|
||||
`@tensorflow/tfjs` and SheetJS modules should be installed using a package manager:
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
```
|
||||
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
|
||||
Try running the command again with the package name prefixed: yarn add my-package@https:...
|
||||
```
|
||||
|
||||
The workaround is to prepend the URL with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### NodeJS
|
||||
|
||||
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
||||
imported in NodeJS scripts that use TF.js.
|
||||
|
||||
There are two options for NodeJS:
|
||||
|
||||
- the pure JavaScript bindings module is `@tensorflow/tfjs`
|
||||
- the native bindings module is `@tensorflow/tfjs-node`
|
||||
|
||||
:::danger pass
|
||||
|
||||
When this demo was last tested, there were issues with the native binding:
|
||||
|
||||
```
|
||||
Error: The specified module could not be found.
|
||||
\\?\C:\Users\SheetJS\node_modules\@tensorflow\tfjs-node\lib\napi-v8\tfjs_binding.node
|
||||
```
|
||||
|
||||
For general compatibility, the demos use the pure `@tensorflow/tfjs` binding.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
<Tabs groupId="pm">
|
||||
<TabItem value="npm" label="npm">
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="pnpm" label="pnpm">
|
||||
<CodeBlock language="bash">{`\
|
||||
pnpm install --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="yarn" label="Yarn" default>
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
:::caution pass
|
||||
|
||||
Newer releases of Yarn may throw an error:
|
||||
|
||||
```
|
||||
Usage Error: It seems you are trying to add a package using a https:... url; we now require package names to be explicitly specified.
|
||||
Try running the command again with the package name prefixed: yarn add my-package@https:...
|
||||
```
|
||||
|
||||
The workaround is to prepend the URL with `xlsx@`:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
yarn add xlsx@https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## CSV Data Interchange
|
||||
|
||||
`tf.data.csv`[^1] generates a Dataset from CSV data. The function expects a URL.
|
||||
@ -126,6 +255,8 @@ The demo builds a model for predicting MPG from Horsepower data. It:
|
||||
- builds a model and trains with `fitDataset`[^8]
|
||||
- predicts MPG from a set of sample inputs and displays results in a table
|
||||
|
||||
#### Live Demo
|
||||
|
||||
<details>
|
||||
<summary><b>Live Demo</b> (click to show)</summary>
|
||||
|
||||
@ -186,8 +317,8 @@ function SheetJSToTFJSCSV() {
|
||||
hasHeader: true,
|
||||
configuredColumnsOnly: true,
|
||||
columnConfigs:{
|
||||
"Horsepower": {required: false, default: 0},
|
||||
"Miles_per_Gallon":{required: false, default: 0, isLabel:true}
|
||||
"Horsepower": { required: false, default: 0},
|
||||
"Miles_per_Gallon": { required: false, default: 0, isLabel: true }
|
||||
}
|
||||
});
|
||||
|
||||
@ -234,6 +365,111 @@ function SheetJSToTFJSCSV() {
|
||||
|
||||
</details>
|
||||
|
||||
#### NodeJS Demo
|
||||
|
||||
0) Create a new project:
|
||||
|
||||
```bash
|
||||
mkdir sheetjs-tfjs-csv
|
||||
cd sheetjs-tfjs-csv
|
||||
npm init -y
|
||||
```
|
||||
|
||||
1) Download [`SheetJSTF.js`](pathname:///tfjs/SheetJSTF.js):
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/tfjs/SheetJSTF.js
|
||||
```
|
||||
|
||||
2) Install SheetJS and TF.js dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs @tensorflow/tfjs-node`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Run the script:
|
||||
|
||||
```bash
|
||||
node SheetJSTF.js
|
||||
```
|
||||
|
||||
#### Kaioken Demo
|
||||
|
||||
:::tip pass
|
||||
|
||||
[Kaioken](/docs/demos/frontend/kaioken) is a popular front-end framework that
|
||||
uses patterns that will be familiar to ReactJS developers.
|
||||
|
||||
The SheetJS team strongly recommends using Kaioken in projects using TF.js.
|
||||
|
||||
:::
|
||||
|
||||
1) Create a new site.
|
||||
|
||||
```bash
|
||||
npm create vite sheetjs-tfjs-kaioken -- --template vanilla-ts
|
||||
cd sheetjs-tfjs-kaioken
|
||||
npm add --save kaioken
|
||||
npm add --save vite-plugin-kaioken -D
|
||||
```
|
||||
|
||||
2) Create a new file `vite.config.ts` with the following content:
|
||||
|
||||
```ts title="vite.config.ts (create new file)"
|
||||
import { defineConfig } from "vite"
|
||||
import kaioken from "vite-plugin-kaioken"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [kaioken()],
|
||||
})
|
||||
```
|
||||
|
||||
3) Edit `tsconfig.json` and add `"jsx": "preserve"` within `compilerOptions`:
|
||||
|
||||
```js title="tsconfig.json (add highlighted line)"
|
||||
{
|
||||
"compilerOptions": {
|
||||
// highlight-next-line
|
||||
"jsx": "preserve",
|
||||
```
|
||||
|
||||
4) Replace `src/main.ts` with the following codeblock:
|
||||
|
||||
```js title="src/main.ts"
|
||||
import { mount } from "kaioken";
|
||||
import App from "./SheetJSTF";
|
||||
|
||||
const root = document.getElementById("app");
|
||||
mount(App, root!);
|
||||
```
|
||||
|
||||
5) Download [`SheetJSTF.tsx`](pathname:///tfjs/SheetJSTF.tsx) to the `src` directory:
|
||||
|
||||
```bash
|
||||
curl -L -o src/SheetJSTF.tsx https://docs.sheetjs.com/tfjs/SheetJSTF.tsx
|
||||
```
|
||||
|
||||
6) Install SheetJS and TF.js dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @tensorflow/tfjs`}
|
||||
</CodeBlock>
|
||||
|
||||
7) Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The process will display a URL:
|
||||
|
||||
```
|
||||
➜ Local: http://localhost:5173/
|
||||
```
|
||||
|
||||
Open the displayed URL (`http://localhost:5173/` in this example) with a web
|
||||
browser. Click the "Click to Run" button to see the results.
|
||||
|
||||
## JS Array Interchange
|
||||
|
||||
[The official Linear Regression tutorial](https://www.tensorflow.org/js/tutorials/training/linear_regression)
|
||||
@ -306,7 +542,7 @@ The SheetJS `sheet_to_json` method[^10] will translate worksheet objects into an
|
||||
array of row objects:
|
||||
|
||||
```js
|
||||
var aoo = [
|
||||
const aoo = [
|
||||
{"sepal length": 5.1, "sepal width": 3.5, ...},
|
||||
{"sepal length": 4.9, "sepal width": 3, ...},
|
||||
...
|
||||
@ -316,18 +552,18 @@ var aoo = [
|
||||
TF.js and other libraries tend to operate on individual columns, equivalent to:
|
||||
|
||||
```js
|
||||
var sepal_lengths = [5.1, 4.9, ...];
|
||||
var sepal_widths = [3.5, 3, ...];
|
||||
const sepal_lengths = [5.1, 4.9, ...];
|
||||
const sepal_widths = [3.5, 3, ...];
|
||||
```
|
||||
|
||||
When a `tensor2d` can be exported, it will look different from the spreadsheet:
|
||||
|
||||
```js
|
||||
var data_set_2d = [
|
||||
const data_set_2d = [
|
||||
[5.1, 4.9, ...],
|
||||
[3.5, 3, ...],
|
||||
...
|
||||
]
|
||||
// ...
|
||||
];
|
||||
```
|
||||
|
||||
This is the transpose of how people use spreadsheets!
|
||||
@ -340,35 +576,35 @@ transposed. To export multiple data sets, the data should be transposed:
|
||||
|
||||
```js
|
||||
/* assuming data is an array of typed arrays */
|
||||
var aoa = [];
|
||||
for(var i = 0; i < data.length; ++i) {
|
||||
for(var j = 0; j < data[i].length; ++j) {
|
||||
const aoa = [];
|
||||
for(let i = 0; i < data.length; ++i) {
|
||||
for(let j = 0; j < data[i].length; ++j) {
|
||||
if(!aoa[j]) aoa[j] = [];
|
||||
aoa[j][i] = data[i][j];
|
||||
}
|
||||
}
|
||||
/* aoa can be directly converted to a worksheet object */
|
||||
var ws = XLSX.utils.aoa_to_sheet(aoa);
|
||||
const ws = XLSX.utils.aoa_to_sheet(aoa);
|
||||
```
|
||||
|
||||
### Importing Data from a Spreadsheet
|
||||
|
||||
`sheet_to_json` with the option `header:1`[^12] will generate a row-major array
|
||||
`sheet_to_json` with the option `header: 1`[^12] will generate a row-major array
|
||||
of arrays that can be transposed. However, it is more efficient to walk the
|
||||
sheet manually:
|
||||
|
||||
```js
|
||||
/* find worksheet range */
|
||||
var range = XLSX.utils.decode_range(ws['!ref']);
|
||||
var out = []
|
||||
const range = XLSX.utils.decode_range(ws['!ref']);
|
||||
const out = []
|
||||
/* walk the columns */
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
for(let C = range.s.c; C <= range.e.c; ++C) {
|
||||
/* create the typed array */
|
||||
var ta = new Float32Array(range.e.r - range.s.r + 1);
|
||||
const ta = new Float32Array(range.e.r - range.s.r + 1);
|
||||
/* walk the rows */
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
for(let R = range.s.r; R <= range.e.r; ++R) {
|
||||
/* find the cell, skip it if the cell isn't numeric or boolean */
|
||||
var cell = ws["!data"] ? (ws["!data"][R]||[])[C] : ws[XLSX.utils.encode_cell({r:R, c:C})];
|
||||
const cell = ws["!data"] ? (ws["!data"][R]||[])[C] : ws[XLSX.utils.encode_cell({r:R, c:C})];
|
||||
if(!cell || cell.t != 'n' && cell.t != 'b') continue;
|
||||
/* assign to the typed array */
|
||||
ta[R - range.s.r] = cell.v;
|
||||
@ -393,38 +629,38 @@ const tensor = tf.tensor1d(lengths);
|
||||
`tf.Tensor` objects can be directly transposed using `transpose`:
|
||||
|
||||
```js
|
||||
var aoo = XLSX.utils.sheet_to_json(worksheet);
|
||||
const aoo = XLSX.utils.sheet_to_json(worksheet);
|
||||
// "x" and "y" are the fields we want to pull from the data
|
||||
var data = aoo.map(row => ([row["x"], row["y"]]));
|
||||
con st data = aoo.map(row => ([row["x"], row["y"]]));
|
||||
|
||||
// create a tensor representing two column datasets
|
||||
var tensor = tf.tensor2d(data).transpose();
|
||||
const tensor = tf.tensor2d(data).transpose();
|
||||
|
||||
// individual columns can be accessed
|
||||
var col1 = tensor.slice([0,0], [1,tensor.shape[1]]).flatten();
|
||||
var col2 = tensor.slice([1,0], [1,tensor.shape[1]]).flatten();
|
||||
const col1 = tensor.slice([0,0], [1,tensor.shape[1]]).flatten();
|
||||
const col2 = tensor.slice([1,0], [1,tensor.shape[1]]).flatten();
|
||||
```
|
||||
|
||||
For exporting, `stack` can be used to collapse the columns into a linear array:
|
||||
|
||||
```js
|
||||
/* pull data into a Float32Array */
|
||||
var result = tf.stack([col1, col2]).transpose();
|
||||
var shape = tensor.shape;
|
||||
var f32 = tensor.dataSync();
|
||||
const result = tf.stack([col1, col2]).transpose();
|
||||
const shape = tensor.shape;
|
||||
const f32 = tensor.dataSync();
|
||||
|
||||
/* construct an array of arrays of the data in spreadsheet order */
|
||||
var aoa = [];
|
||||
for(var j = 0; j < shape[0]; ++j) {
|
||||
const aoa = [];
|
||||
for(let j = 0; j < shape[0]; ++j) {
|
||||
aoa[j] = [];
|
||||
for(var i = 0; i < shape[1]; ++i) aoa[j][i] = f32[j * shape[1] + i];
|
||||
for(let i = 0; i < shape[1]; ++i) aoa[j][i] = f32[j * shape[1] + i];
|
||||
}
|
||||
|
||||
/* add headers to the top */
|
||||
aoa.unshift(["x", "y"]);
|
||||
|
||||
/* generate worksheet */
|
||||
var worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(aoa);
|
||||
```
|
||||
|
||||
[^1]: See [`tf.data.csv`](https://js.tensorflow.org/api/latest/#data.csv) in the TensorFlow.js documentation
|
||||
|
@ -828,4 +828,5 @@ file named `SheetJSNS.xls`.
|
||||
[^4]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^5]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
|
||||
[^6]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
|
||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^8]: See ["Local setup"](https://docs.nativescript.org/setup/#local-setup) in the NativeScript documentation. For Windows and Linux, follow the "Android" instructions. For macOS, follow both the iOS and Android instructions.
|
||||
|
68
docz/static/tfjs/SheetJSTF.js
Normal file
68
docz/static/tfjs/SheetJSTF.js
Normal file
@ -0,0 +1,68 @@
|
||||
const XLSX = require('xlsx');
|
||||
const tf = require("@tensorflow/tfjs");
|
||||
//const tf = require("@tensorflow/tfjs-node");
|
||||
|
||||
function worksheet_to_csv_url(worksheet) {
|
||||
/* generate CSV */
|
||||
const csv = XLSX.utils.sheet_to_csv(worksheet);
|
||||
|
||||
/* CSV -> Uint8Array -> Blob */
|
||||
const u8 = new TextEncoder().encode(csv);
|
||||
const blob = new Blob([u8], { type: "text/csv" });
|
||||
|
||||
/* generate a blob URL */
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
(async() => { try {
|
||||
/* fetch file */
|
||||
const f = await fetch("https://docs.sheetjs.com/cd.xls");
|
||||
const ab = await f.arrayBuffer();
|
||||
/* parse file and get first worksheet */
|
||||
const wb = XLSX.read(ab);
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
|
||||
/* generate blob URL */
|
||||
const url = worksheet_to_csv_url(ws);
|
||||
|
||||
/* feed to tf.js */
|
||||
const dataset = tf.data.csv(url, {
|
||||
hasHeader: true,
|
||||
configuredColumnsOnly: true,
|
||||
columnConfigs:{
|
||||
"Horsepower": {required: false, default: 0},
|
||||
"Miles_per_Gallon":{required: false, default: 0, isLabel:true}
|
||||
}
|
||||
});
|
||||
|
||||
/* pre-process data */
|
||||
let flat = dataset
|
||||
.map(({xs,ys}) =>({xs: Object.values(xs), ys: Object.values(ys)}))
|
||||
.filter(({xs,ys}) => [...xs,...ys].every(v => v>0));
|
||||
|
||||
/* normalize manually :( */
|
||||
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
||||
await flat.forEachAsync(({xs, ys}) => {
|
||||
minX = Math.min(minX, xs[0]); maxX = Math.max(maxX, xs[0]);
|
||||
minY = Math.min(minY, ys[0]); maxY = Math.max(maxY, ys[0]);
|
||||
});
|
||||
flat = flat.map(({xs, ys}) => ({xs:xs.map(v => (v-minX)/(maxX - minX)),ys:ys.map(v => (v-minY)/(maxY-minY))}));
|
||||
flat = flat.batch(32);
|
||||
|
||||
/* build and train model */
|
||||
const model = tf.sequential();
|
||||
model.add(tf.layers.dense({inputShape: [1], units: 1}));
|
||||
model.compile({ optimizer: tf.train.sgd(0.000001), loss: 'meanSquaredError' });
|
||||
await model.fitDataset(flat, { epochs: 100, callbacks: { onEpochEnd: async (epoch, logs) => {
|
||||
console.error(`${epoch}:${logs.loss}`);
|
||||
}}});
|
||||
|
||||
/* predict values */
|
||||
const inp = tf.linspace(0, 1, 9);
|
||||
const pred = model.predict(inp);
|
||||
const xs = await inp.dataSync(), ys = await pred.dataSync();
|
||||
|
||||
for (let i=0; i<xs.length; ++i) {
|
||||
console.log([xs[i] * (maxX - minX) + minX, ys[i] * (maxY - minY) + minY].join(" "));
|
||||
}
|
||||
} catch(e) { console.error(`ERROR: ${String(e)}`); }})();
|
92
docz/static/tfjs/SheetJSTF.tsx
Normal file
92
docz/static/tfjs/SheetJSTF.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import { useState, useCallback } from "kaioken";
|
||||
import { TensorContainerObject, data, layers, linspace, train, sequential } from "@tensorflow/tfjs";
|
||||
import { read, utils } from "xlsx";
|
||||
|
||||
import type { Tensor, Rank } from "@tensorflow/tfjs";
|
||||
import type { WorkSheet } from "xlsx";
|
||||
|
||||
interface Data extends TensorContainerObject {
|
||||
xs: Tensor;
|
||||
ys: Tensor;
|
||||
}
|
||||
type DSet = data.Dataset<Data>;
|
||||
|
||||
export default function SheetJSToTFJSCSV() {
|
||||
const [output, setOutput] = useState("");
|
||||
const [results, setResults] = useState<[number, number][]>([]);
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
|
||||
function worksheet_to_csv_url(worksheet: WorkSheet) {
|
||||
/* generate CSV */
|
||||
const csv = utils.sheet_to_csv(worksheet);
|
||||
|
||||
/* CSV -> Uint8Array -> Blob */
|
||||
const u8 = new TextEncoder().encode(csv);
|
||||
const blob = new Blob([u8], { type: "text/csv" });
|
||||
|
||||
/* generate a blob URL */
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
const doit = useCallback(async () => {
|
||||
setResults([]); setOutput(""); setDisabled(true);
|
||||
try {
|
||||
/* fetch file */
|
||||
const f = await fetch("https://docs.sheetjs.com/cd.xls");
|
||||
const ab = await f.arrayBuffer();
|
||||
/* parse file and get first worksheet */
|
||||
const wb = read(ab);
|
||||
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||
|
||||
/* generate blob URL */
|
||||
const url = worksheet_to_csv_url(ws);
|
||||
|
||||
/* feed to tf.js */
|
||||
const dataset = data.csv(url, {
|
||||
hasHeader: true,
|
||||
configuredColumnsOnly: true,
|
||||
columnConfigs:{
|
||||
"Horsepower": {required: false, default: 0},
|
||||
"Miles_per_Gallon":{required: false, default: 0, isLabel:true}
|
||||
}
|
||||
});
|
||||
|
||||
/* pre-process data */
|
||||
let flat = (dataset as unknown as DSet)
|
||||
.map(({xs,ys}) =>({xs: Object.values(xs), ys: Object.values(ys)}))
|
||||
.filter(({xs,ys}) => [...xs,...ys].every(v => v>0));
|
||||
|
||||
/* normalize manually :( */
|
||||
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
||||
await flat.forEachAsync(({xs, ys}) => {
|
||||
minX = Math.min(minX, xs[0]); maxX = Math.max(maxX, xs[0]);
|
||||
minY = Math.min(minY, ys[0]); maxY = Math.max(maxY, ys[0]);
|
||||
});
|
||||
flat = flat.map(({xs, ys}) => ({xs:xs.map(v => (v-minX)/(maxX - minX)),ys:ys.map(v => (v-minY)/(maxY-minY))}));
|
||||
let batch = flat.batch(32);
|
||||
|
||||
/* build and train model */
|
||||
const model = sequential();
|
||||
model.add(layers.dense({inputShape: [1], units: 1}));
|
||||
model.compile({ optimizer: train.sgd(0.000001), loss: 'meanSquaredError' });
|
||||
await model.fitDataset(batch, { epochs: 100, callbacks: { onEpochEnd: async (epoch, logs) => {
|
||||
setOutput(`${epoch}:${logs?.loss}`);
|
||||
}}});
|
||||
|
||||
/* predict values */
|
||||
const inp = linspace(0, 1, 9);
|
||||
const pred = model.predict(inp) as Tensor<Rank>;
|
||||
const xs = await inp.dataSync(), ys = await pred.dataSync();
|
||||
setResults(Array.from(xs).map((x, i) => [ x * (maxX - minX) + minX, ys[i] * (maxY - minY) + minY ]));
|
||||
setOutput("");
|
||||
} catch(e) { setOutput(`ERROR: ${String(e)}`); } finally { setDisabled(false);}
|
||||
}, []);
|
||||
|
||||
return ( <>
|
||||
<button onclick={doit} disabled={disabled}>Click to run</button><br/>
|
||||
{output && <pre>{output}</pre> || <></>}
|
||||
{results.length && <table><thead><tr><th>Horsepower</th><th>MPG</th></tr></thead><tbody>
|
||||
{results.map((r,i) => <tr key={i}><td>{r[0]}</td><td>{r[1].toFixed(2)}</td></tr>)}
|
||||
</tbody></table> || <></>}
|
||||
</> );
|
||||
}
|
4
docz/static/tfjs/tf.min.js
vendored
4
docz/static/tfjs/tf.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user