UNPKG

@astermind/astermind-premium

Version:

Astermind Premium - Premium ML Toolkit

186 lines 6.67 kB
// fuzzy-elm.ts — Fuzzy ELM // Fuzzy logic + ELM for uncertainty handling and soft classification import { ELM } from '@astermind/astermind-elm'; import { requireLicense } from '../core/license.js'; /** * Fuzzy ELM * Features: * - Fuzzy logic integration * - Uncertainty handling * - Soft classification * - Membership functions */ export class FuzzyELM { constructor(options) { this.trained = false; this.membershipParams = new Map(); requireLicense(); // Premium feature - requires valid license this.categories = options.categories; this.options = { categories: options.categories, hiddenUnits: options.hiddenUnits ?? 256, fuzzyMembership: options.fuzzyMembership ?? 'gaussian', fuzzificationLevel: options.fuzzificationLevel ?? 0.5, activation: options.activation ?? 'relu', maxLen: options.maxLen ?? 100, useTokenizer: options.useTokenizer ?? true, }; this.elm = new ELM({ useTokenizer: this.options.useTokenizer ? true : undefined, hiddenUnits: this.options.hiddenUnits, categories: this.options.categories, maxLen: this.options.maxLen, activation: this.options.activation, }); } /** * Train with fuzzy logic */ train(X, y) { // Prepare labels const labelIndices = y.map(label => typeof label === 'number' ? label : this.options.categories.indexOf(label)); // Fuzzify input features const fuzzifiedX = this._fuzzifyFeatures(X); // Compute membership parameters this._computeMembershipParams(X, labelIndices); // Train ELM on fuzzified features this.elm.setCategories?.(this.options.categories); this.elm.trainFromData?.(fuzzifiedX, labelIndices); this.trained = true; } /** * Fuzzify input features */ _fuzzifyFeatures(X) { const fuzzified = []; for (const x of X) { const fuzzy = x.map(val => this._fuzzifyValue(val)); fuzzified.push(fuzzy); } return fuzzified; } /** * Fuzzify a single value */ _fuzzifyValue(value) { // Apply fuzzification based on membership function if (this.options.fuzzyMembership === 'triangular') { // Triangular membership const center = 0; const width = this.options.fuzzificationLevel; if (Math.abs(value - center) <= width) { return 1 - Math.abs(value - center) / width; } return 0; } else if (this.options.fuzzyMembership === 'gaussian') { // Gaussian membership const center = 0; const sigma = this.options.fuzzificationLevel; return Math.exp(-Math.pow(value - center, 2) / (2 * sigma * sigma)); } else if (this.options.fuzzyMembership === 'trapezoidal') { // Trapezoidal membership const center = 0; const width = this.options.fuzzificationLevel; const dist = Math.abs(value - center); if (dist <= width * 0.5) { return 1; } else if (dist <= width) { return 1 - (dist - width * 0.5) / (width * 0.5); } return 0; } return value; // Default: no fuzzification } /** * Compute membership parameters for each category */ _computeMembershipParams(X, y) { // Compute mean and std for each category const categoryData = new Map(); for (let i = 0; i < X.length; i++) { const label = y[i]; if (!categoryData.has(label)) { categoryData.set(label, []); } categoryData.get(label).push(X[i]); } for (const [label, data] of categoryData) { const mean = this._computeMean(data); const std = this._computeStd(data, mean); this.membershipParams.set(this.options.categories[label], { center: mean, width: std * 2, // 2 standard deviations }); } } _computeMean(data) { if (data.length === 0) return 0; const sum = data.reduce((s, x) => s + x.reduce((a, b) => a + b, 0), 0); const count = data.length * (data[0]?.length || 1); return sum / count; } _computeStd(data, mean) { if (data.length === 0) return 1; const variance = data.reduce((s, x) => s + x.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0), 0) / (data.length * (data[0]?.length || 1)); return Math.sqrt(variance); } /** * Compute fuzzy membership for a prediction */ _computeMembership(label, features) { const params = this.membershipParams.get(label); if (!params) return 0.5; // Default membership const mean = features.reduce((a, b) => a + b, 0) / features.length; const dist = Math.abs(mean - params.center); if (this.options.fuzzyMembership === 'gaussian') { return Math.exp(-Math.pow(dist, 2) / (2 * params.width * params.width)); } else { // Triangular if (dist <= params.width) { return 1 - dist / params.width; } return 0; } } /** * Predict with fuzzy logic */ predict(X, topK = 3) { if (!this.trained) { throw new Error('Model must be trained before prediction'); } const XArray = Array.isArray(X[0]) ? X : [X]; const results = []; for (const x of XArray) { // Fuzzify input const fuzzified = this._fuzzifyFeatures([x])[0]; // Get base prediction const preds = this.elm.predictFromVector?.([fuzzified], topK) || []; for (const pred of preds.slice(0, topK)) { const label = pred.label || this.options.categories[pred.index || 0]; const prob = pred.prob || 0; // Compute fuzzy membership const membership = this._computeMembership(label, x); // Combine probability with membership const confidence = prob * membership; results.push({ label, prob, membership, confidence, }); } } return results; } } //# sourceMappingURL=fuzzy-elm.js.map