项目说明:
A Simple JavaScript Library to make it easy for people to use KMeans algorithms with Tensorflow JS.
The library was born out of another project in which except KMeans, our code completely depended on TF.JS
As such, moving to TF.JS helped standardise our code base substantially and reduce dependency on other libraries
需要安装的依赖库:“@tensorflow/tfjs-core”
npm install “@tensorflow/tfjs-core” -g
调用程序Demo:
//const KMeans = require("tf-kmeans");
const KMeans = require("../lib/index.js");
const tf = require("@tensorflow/tfjs");
function SyncTest() {
tf.tidy(() => {
const kmeans = new KMeans.default({
k: 2,
maxIter: 30,
distanceFunction: KMeans.default.EuclideanDistance
});
const dataset = tf.tensor([[2, 2, 2], [5, 5, 5], [3, 3, 3], [4, 4, 4], [7, 8, 7]]);
const predictions = kmeans.Train(
dataset
);
console.log("Assigned To ", predictions.arraySync());
console.log("Centroids Used are ", kmeans.Centroids().arraySync());
console.log("Prediction for Given Value is");
kmeans.Predict(tf.tensor([2, 3, 2])).print();
console.log("Amount of Memory Used is ", tf.memory());
// Use this In case kmeans not executed in Tidy Function
kmeans.Dispose();
predictions.dispose();
dataset.dispose();
});
}
async function AsyncTest() {
const kmeans = new KMeans.default({
k: 3,
maxIter: 30,
distanceFunction: KMeans.default.EuclideanDistance
});
const dataset = tf.tensor([[2, 2, 2], [5, 5, 5], [3, 3, 3], [4, 4, 4], [7, 8, 7]]);
console.log("\n\nAsync Test");
const predictions = await kmeans.TrainAsync(
dataset,
// Called At End of Every Iteration
async(iter, centroid, preds)=>{
console.log("===");
console.log("Iteration Count", iter);
console.log("Centroid ", await centroid.array());
console.log("Prediction ", await preds.array());
console.log("===");
// You could instead use TFVIS for Plotting Here
}
);
console.log("Assigned To ", await predictions.array());
console.log("Centroids Used are ", await kmeans.Centroids().array());
console.log("Prediction for Given Value is");
kmeans.Predict(tf.tensor([2, 3, 2])).print();
console.log("Amount of Memory Used is ", tf.memory());
kmeans.Dispose();
predictions.dispose();
dataset.dispose();
}
SyncTest();
AsyncTest().then(() => console.log("Hi"));
输出的结果:
[Running] node "d:\tf-kmeans-master\samples\index.js"
============================
Hi there ?. Looks like you are running TensorFlow.js in Node.js. To speed things up dramatically, install our node backend, which binds to TensorFlow C++, by running npm i @tensorflow/tfjs-node, or npm i @tensorflow/tfjs-node-gpu if you have CUDA. Then call require('@tensorflow/tfjs-node'); (-gpu suffix for CUDA) at the start of your program. Visit https://github.com/tensorflow/tfjs-node for more details.
============================
Assigned To [ 1, 0, 1, 0, 0 ]
Centroids Used are [ [ 5.333333492279053, 5.666666507720947, 5.333333492279053 ],
[ 2.5, 2.5, 2.5 ] ]
Prediction for Given Value is
Tensor
[1]
Amount of Memory Used is { unreliable: true,
reasons:
[ 'The reported memory is an upper bound. Due to automatic garbage collection, the true allocated memory may be less.' ],
numTensors: 7,
numDataBuffers: 7,
numBytes: 160 }
Async Test
===
Iteration Count 0
Centroid [ [ 4.5, 4.5, 4.5 ], [ 7, 8, 7 ], [ 2.5, 2.5, 2.5 ] ]
Prediction [ 2, 0, 2, 0, 1 ]
===
Assigned To [ 2, 0, 2, 0, 1 ]
Centroids Used are [ [ 4.5, 4.5, 4.5 ], [ 7, 8, 7 ], [ 2.5, 2.5, 2.5 ] ]
Prediction for Given Value is
Tensor
[2]
Amount of Memory Used is { unreliable: true,
reasons:
[ 'The reported memory is an upper bound. Due to automatic garbage collection, the true allocated memory may be less.' ],
numTensors: 6,
numDataBuffers: 6,
numBytes: 152 }
Hi
[Done] exited with code=0 in 0.304 seconds
对库函数的说明:
Constructor
Takes 3 Optional parametersTrain
Takes Dataset as Parameter
Performs Training on This Dataset
Sync callback function is optionalTrainAsync
Takes Dataset as Parameter
Performs Training on This Dataset
Also takes async callback function called at the end of every iterationCentroids
Returns the Centroids found for the dataset on which KMeans was TrainedPredict
Performs Predictions on the data Provided as Input通过typescript编译后的k-means库:
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
exports.__esModule = true;
var tf = require("@tensorflow/tfjs-core");
var KMeans = /** @class */ (function () {
function KMeans(_a) {
var _b = _a === void 0 ? {} : _a, _c = _b.k, k = _c === void 0 ? 2 : _c, _d = _b.maxIter, maxIter = _d === void 0 ? 10 : _d, _e = _b.distanceFunction, distanceFunction = _e === void 0 ? KMeans.EuclideanDistance : _e;
this.k = 2;
this.maxIter = 200;
this.distanceFunction = KMeans.EuclideanDistance;
this.k = k;
this.maxIter = maxIter;
this.distanceFunction = distanceFunction;
}
KMeans.EuclideanDistance = function (values, centroids) {
return tf.tidy(function () { return values.squaredDifference(centroids).sum(1).sqrt(); });
};
KMeans.prototype.GenerateIndices = function (rows) {
var indices = [];
indices.length = rows;
for (var i = 0; i < indices.length; ++i)
indices[i] = i;
return indices;
};
KMeans.prototype.NewCentroidSingle = function (values, assignments, cluster, rows) {
return tf.tidy(function () {
// Make All Values Of Array to be of Same Size as Our Cluster
var selectedIndices = [];
selectedIndices.length = rows;
selectedIndices = selectedIndices.fill(cluster);
var selectedIndicesT = tf.tensor(selectedIndices);
var where = tf.equal(assignments, selectedIndicesT).asType("int32");
where = where.reshape([where.shape[0], 1]);
var count = where.sum();
var newCentroid = values.mul(where).sum(0).div(count);
return newCentroid;
});
};
KMeans.prototype.NewCentroids = function (values, assignments) {
var _this = this;
return tf.tidy(function () {
var rows = values.shape[0];
var centroids = [];
for (var cluster = 0; cluster < _this.k; ++cluster) {
centroids.push(_this.NewCentroidSingle(values, assignments, cluster, rows));
}
return tf.stack(centroids);
});
};
KMeans.prototype.AssignCluster = function (value, centroids) {
var _this = this;
return tf.tidy(function () { return _this.distanceFunction(value, centroids).argMin(0); });
};
KMeans.prototype.AssignClusters = function (values, centroids) {
var _this = this;
return tf.tidy(function () {
var rows = values.shape[0];
var minIndexes = [];
for (var _i = 0, _a = _this.GenerateIndices(rows); _i < _a.length; _i++) {
var index = _a[_i];
var value = values.gather(index);
minIndexes.push(_this.AssignCluster(value, centroids));
value.dispose();
}
return tf.stack(minIndexes);
});
};
KMeans.prototype.RandomSample = function (vals) {
var _this = this;
return tf.tidy(function () {
var rows = vals.shape[0];
if (rows < _this.k)
throw new Error("Rows are Less than K");
var indicesRaw = tf.util.createShuffledIndices(rows).slice(0, _this.k);
var indices = [];
indicesRaw.forEach(function (index) { return indices.push(index); });
// Extract Random Indices
return tf.gatherND(vals, tf.tensor(indices, [_this.k, 1], "int32"));
});
};
KMeans.prototype.CheckCentroidSimmilarity = function (newCentroids, centroids, vals) {
var _this = this;
return tf.tidy(function () { return newCentroids
.equal(centroids)
.asType("int32")
.sum(1)
.div(vals.shape[1])
.sum()
.equal(_this.k)
.dataSync()[0]; });
};
KMeans.prototype.TrainSingleStep = function (values) {
var _this = this;
return tf.tidy(function () {
var predictions = _this.Predict(values);
var newCentroids = _this.NewCentroids(values, predictions);
return [newCentroids, predictions];
});
};
KMeans.prototype.Train = function (values, callback) {
if (callback === void 0) { callback = function (_centroid, _predictions) { }; }
this.centroids = this.RandomSample(values);
var iter = 0;
while (true) {
var _a = this.TrainSingleStep(values), newCentroids = _a[0], predictions = _a[1];
var same = this.CheckCentroidSimmilarity(newCentroids, this.centroids, values);
if (same || iter >= this.maxIter) {
newCentroids.dispose();
return predictions;
}
this.centroids.dispose();
this.centroids = newCentroids;
++iter;
callback(this.centroids, predictions);
}
};
KMeans.prototype.TrainAsync = function (values, callback) {
var _this = this;
if (callback === void 0) { callback = function (_iter, _centroid, _predictions) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/];
}); }); }; }
return __awaiter(this, void 0, void 0, function () {
var iter, _a, newCentroids, predictions, same;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
this.centroids = this.RandomSample(values);
iter = 0;
_b.label = 1;
case 1:
if (!true) return [3 /*break*/, 3];
_a = this.TrainSingleStep(values), newCentroids = _a[0], predictions = _a[1];
same = this.CheckCentroidSimmilarity(newCentroids, this.centroids, values);
if (same || iter >= this.maxIter) {
newCentroids.dispose();
return [2 /*return*/, predictions];
}
this.centroids.dispose();
this.centroids = newCentroids;
return [4 /*yield*/, callback(iter, this.centroids, predictions)];
case 2:
_b.sent();
++iter;
return [3 /*break*/, 1];
case 3: return [2 /*return*/];
}
});
});
};
KMeans.prototype.Predict = function (y) {
var _this = this;
return tf.tidy(function () {
if (y.shape[1] == null)
y = y.reshape([1, y.shape[0]]);
return _this.AssignClusters(y, _this.centroids);
});
};
KMeans.prototype.Centroids = function () {
return this.centroids;
};
KMeans.prototype.Dispose = function () {
this.centroids.dispose();
};
return KMeans;
}());
exports["default"] = KMeans;
编译前:
import * as tf from "@tensorflow/tfjs-core";
export default class KMeans {
public k: number = 2;
public maxIter: number = 200;
public distanceFunction = KMeans.EuclideanDistance;
public centroids!: tf.Tensor;
public constructor({ k = 2, maxIter = 10, distanceFunction = KMeans.EuclideanDistance } = {}) {
this.k = k;
this.maxIter = maxIter;
this.distanceFunction = distanceFunction;
}
public static EuclideanDistance(values: tf.Tensor, centroids: tf.Tensor) {
return tf.tidy(() => values.squaredDifference(centroids).sum(1).sqrt());
}
private GenerateIndices(rows: number) {
const indices: number[] = [];
indices.length = rows;
for (let i = 0; i < indices.length; ++i)
indices[i] = i;
return indices;
}
private NewCentroidSingle(values: tf.Tensor, assignments: tf.Tensor, cluster: number, rows: number) {
return tf.tidy(() => {
// Make All Values Of Array to be of Same Size as Our Cluster
let selectedIndices: number[] = [];
selectedIndices.length = rows;
selectedIndices = selectedIndices.fill(cluster);
const selectedIndicesT = tf.tensor(selectedIndices);
let where = tf.equal(assignments, selectedIndicesT).asType("int32");
where = where.reshape([where.shape[0], 1]);
const count = where.sum();
const newCentroid = values.mul(where).sum(0).div(count)
return newCentroid;
})
}
private NewCentroids(values: tf.Tensor, assignments: tf.Tensor) {
return tf.tidy(() => {
const rows = values.shape[0];
const centroids: tf.Tensor[] = [];
for (let cluster = 0; cluster < this.k; ++cluster) {
centroids.push(this.NewCentroidSingle(values, assignments, cluster, rows));
}
return tf.stack(centroids);
});
}
private AssignCluster(value: tf.Tensor, centroids: tf.Tensor) {
return tf.tidy(() => this.distanceFunction(value, centroids).argMin(0));
}
private AssignClusters(values: tf.Tensor, centroids: tf.Tensor) {
return tf.tidy(() => {
const rows = values.shape[0];
const minIndexes: tf.Tensor[] = [];
for (const index of this.GenerateIndices(rows)) {
const value = values.gather(index);
minIndexes.push(this.AssignCluster(value, centroids));
value.dispose();
}
return tf.stack(minIndexes);
});
}
private RandomSample(vals: tf.Tensor) {
return tf.tidy(() => {
const rows = vals.shape[0];
if (rows < this.k)
throw new Error("Rows are Less than K");
const indicesRaw = tf.util.createShuffledIndices(rows).slice(0, this.k);
const indices: number[] = [];
indicesRaw.forEach((index: number) => indices.push(index))
// Extract Random Indices
return tf.gatherND(vals, tf.tensor(indices, [this.k, 1], "int32"))
})
}
private CheckCentroidSimmilarity(newCentroids: tf.Tensor, centroids: tf.Tensor, vals: tf.Tensor) {
return tf.tidy(() => newCentroids
.equal(centroids)
.asType("int32")
.sum(1)
.div(vals.shape[1]!)
.sum()
.equal(this.k)
.dataSync()[0]
);
}
private TrainSingleStep(values: tf.Tensor) {
return tf.tidy(() => {
const predictions = this.Predict(values);
const newCentroids = this.NewCentroids(values, predictions);
return [newCentroids, predictions];
});
}
public Train(values: tf.Tensor, callback = (_centroid: tf.Tensor, _predictions: tf.Tensor) => { }) {
this.centroids = this.RandomSample(values);
let iter = 0;
while (true) {
let [newCentroids, predictions] = this.TrainSingleStep(values);
const same = this.CheckCentroidSimmilarity(newCentroids, this.centroids, values);
if (same || iter >= this.maxIter) {
newCentroids.dispose();
return predictions;
}
this.centroids.dispose();
this.centroids = newCentroids;
++iter;
callback(this.centroids, predictions);
}
}
public async TrainAsync(values: tf.Tensor, callback = async (_iter: number, _centroid: tf.Tensor, _predictions: tf.Tensor) => { }) {
this.centroids = this.RandomSample(values);
let iter = 0;
while (true) {
let [newCentroids, predictions] = this.TrainSingleStep(values);
const same = this.CheckCentroidSimmilarity(newCentroids, this.centroids, values);
if (same || iter >= this.maxIter) {
newCentroids.dispose();
return predictions;
}
this.centroids.dispose();
this.centroids = newCentroids;
await callback(iter, this.centroids, predictions);
++iter;
}
}
public Predict(y: tf.Tensor) {
return tf.tidy(() => {
if (y.shape[1] == null)
y = y.reshape([1, y.shape[0]]);
return this.AssignClusters(y, this.centroids);
});
}
public Centroids() {
return this.centroids;
}
public Dispose() {
this.centroids.dispose();
}
}
先不说了,广告时间又到了,现在植入广告:几个《传热学》相关的小程序总结如下,可在微信中点击体验:
《传热学》相关小程序演示动画如下(其中下图1D非稳态导热计算发散,调小时间步长后重新计算,结果收敛!):
黑体单色辐射力如下图,可见温度越高,同频率辐射力越大:
《(计算)流体力学》中的几个小程序,可在微信中点击体验:
关于《(计算)流体力学》相关的几个小程序演示动画如下:
LBM(=Lattice Boltzmann Method)计算得到的圆柱绕流“卡门涡街”演示(由于网格较少,分辨率低,圆柱近乎正方形):
顺便,《(热工过程)自动控制》中关于PID控制器的仿真可点击此处体验:PID控制演示小程序,(PID控制相关视频见:基础/整定/重要补充)。动画如下:
(正文完!)
现将往期内容制成目录,内容如下:
1 前言(已完成)
2.2.7 webAssemble简介/工具链配置/应用DemoCode
3.1.2 使用绘图API绘制Contour的思路(已完成)
5.1.1一维无内热源温度场数值模拟(基于基于HTML5编程)(已完成)
5.2.5.1 webGL显式迭代计算温度场的shader[显卡风扇不能停]
5.2.5.2 webGL隐式迭代计算温度场的shader[显卡风扇不能停]
5.3.5 [视频]二维常物性不可压流体对流换热问题的数学描述
[python从入门到放弃系列]
pyautogui+acrobat去PDF水印一例(已完成)
[瞎侃系列]
本文分享自 传输过程数值模拟学习笔记 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有