UNPKG

echogarden

Version:

An easy-to-use speech toolset. Includes tools for synthesis, recognition, alignment, speech translation, language detection, source separation and more.

610 lines 20.8 kB
import { clip } from "../utilities/Utilities.js"; export function covarianceMatrixOfSamples(samples, weights, biased = false) { if (samples.length == 0) { throw new Error('No vectors given'); } const { centeredVectors: centeredSamples, mean } = centerVectors(samples, weights); let covarianceMatrix; if (weights) { covarianceMatrix = weightedCovarianceMatrixOfCenteredSamples(centeredSamples, weights); } else { covarianceMatrix = covarianceMatrixOfCenteredSamples(centeredSamples, biased); } return { covarianceMatrix, mean }; } export function covarianceMatrixOfCenteredSamples(centeredSamples, biased = false, diagonalRegularizationAmount = 1e-6) { const sampleCount = centeredSamples.length; if (sampleCount == 0) { throw new Error('No vectors given'); } const sampleSizeMetric = biased || sampleCount == 1 ? sampleCount : sampleCount - 1; const featureCount = centeredSamples[0].length; const covarianceMatrix = createVectorArray(featureCount, featureCount); if (sampleCount == 1) { return covarianceMatrix; } for (let i = 0; i < featureCount; i++) { for (let j = 0; j < featureCount; j++) { if (i > j) { covarianceMatrix[i][j] = covarianceMatrix[j][i]; continue; } let matrixElement = 0.0; for (const sample of centeredSamples) { matrixElement += sample[i] * sample[j]; } matrixElement /= sampleSizeMetric; if (i == j) { matrixElement += diagonalRegularizationAmount; } covarianceMatrix[i][j] = matrixElement; } } return covarianceMatrix; } export function weightedCovarianceMatrixOfCenteredSamples(centeredSamples, weights, diagonalRegularizationAmount = 1e-6) { const sampleCount = centeredSamples.length; if (sampleCount == 0) { throw new Error('No vectors given'); } const featureCount = centeredSamples[0].length; const covarianceMatrix = createVectorArray(featureCount, featureCount); if (sampleCount == 1) { return covarianceMatrix; } for (let i = 0; i < featureCount; i++) { for (let j = 0; j < featureCount; j++) { if (i > j) { covarianceMatrix[i][j] = covarianceMatrix[j][i]; continue; } let matrixElement = 0.0; for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) { const sample = centeredSamples[sampleIndex]; const weight = weights[sampleIndex]; matrixElement += weight * (sample[i] * sample[j]); } if (i == j) { matrixElement += diagonalRegularizationAmount; } covarianceMatrix[i][j] = matrixElement; } } return covarianceMatrix; } export function centerVectors(vectors, weights) { const vectorCount = vectors.length; if (vectorCount == 0) { return { centeredVectors: [], mean: new Float32Array(0) }; } let mean; if (weights) { mean = weightedMeanOfVectors(vectors, weights); } else { mean = meanOfVectors(vectors); } const centeredVectors = []; for (let i = 0; i < vectorCount; i++) { const centeredVector = subtractVectors(vectors[i], mean); centeredVectors.push(centeredVector); } return { centeredVectors, mean }; } export function centerVector(vector) { const mean = meanOfVector(vector); const centeredVector = new Float32Array(vector.length); for (let i = 0; i < vector.length; i++) { centeredVector[i] = vector[i] - mean; } return centeredVector; } export function scaleToSumTo1(vector) { if (vector.length == 0) { return new Float32Array(0); } if (vector.length == 1) { return Float32Array.from([1]); } const minValue = vector[indexOfMin(vector)]; const scaledVector = Float32Array.from(vector); if (minValue < 0) { const addedOffset = -minValue * 2; for (let i = 0; i < scaledVector.length; i++) { scaledVector[i] += addedOffset; } } const sum = sumVector(scaledVector); if (sum == 0) { return scaledVector; } if (sum == Infinity) { throw new Error('Vector sum is infinite'); } for (let i = 0; i < vector.length; i++) { scaledVector[i] /= sum; scaledVector[i] = zeroIfNaN(scaledVector[i]); } return scaledVector; } export function normalizeVector(vector, kind = 'population') { if (vector.length == 0) { throw new Error('Vector is empty'); } const mean = meanOfVector(vector); const stdDeviation = stdDeviationOfVector(vector, kind, mean); const normalizedVector = createVector(vector.length); for (let i = 0; i < vector.length; i++) { normalizedVector[i] = (vector[i] - mean) / stdDeviation; normalizedVector[i] = zeroIfNaN(normalizedVector[i]); } return { normalizedVector, mean, stdDeviation }; } export function normalizeVectors(vectors, kind = 'population') { const vectorCount = vectors.length; if (vectorCount == 0) { return { normalizedVectors: [], mean: new Float32Array(0), stdDeviation: new Float32Array(0) }; } const featureCount = vectors[0].length; const mean = meanOfVectors(vectors); const stdDeviation = stdDeviationOfVectors(vectors, kind, mean); const normalizedVectors = []; for (const vector of vectors) { const normalizedVector = createVector(featureCount); for (let featureIndex = 0; featureIndex < featureCount; featureIndex++) { normalizedVector[featureIndex] = (vector[featureIndex] - mean[featureIndex]) / stdDeviation[featureIndex]; normalizedVector[featureIndex] = zeroIfNaN(normalizedVector[featureIndex]); } normalizedVectors.push(normalizedVector); } return { normalizedVectors, mean, stdDeviation }; } export function deNormalizeVectors(normalizedVectors, originalMean, originalStdDeviation) { const vectorCount = normalizeVectors.length; if (vectorCount == 0) { return []; } const featureCount = normalizedVectors[0].length; const deNormalizedVectors = []; for (const normalizedVector of normalizedVectors) { const deNormalizedVector = createVector(featureCount); for (let featureIndex = 0; featureIndex < featureCount; featureIndex++) { deNormalizedVector[featureIndex] = originalMean[featureIndex] + (normalizedVector[featureIndex] * originalStdDeviation[featureIndex]); } deNormalizedVectors.push(deNormalizedVector); } return deNormalizedVectors; } export function meanOfVectors(vectors) { const vectorCount = vectors.length; if (vectorCount == 0) { return new Float32Array(0); } const featureCount = vectors[0].length; const result = createVector(featureCount); for (const vector of vectors) { for (let featureIndex = 0; featureIndex < featureCount; featureIndex++) { result[featureIndex] += vector[featureIndex]; } } for (let featureIndex = 0; featureIndex < featureCount; featureIndex++) { result[featureIndex] /= vectorCount; } return result; } export function weightedMeanOfVectors(vectors, weights) { const vectorCount = vectors.length; if (vectorCount == 0) { return new Float32Array(0); } const featureCount = vectors[0].length; const result = createVector(featureCount); for (let vectorIndex = 0; vectorIndex < vectorCount; vectorIndex++) { const vector = vectors[vectorIndex]; const vectorWeight = weights[vectorIndex]; for (let featureIndex = 0; featureIndex < featureCount; featureIndex++) { result[featureIndex] += vectorWeight * vector[featureIndex]; } } return result; } export function stdDeviationOfVectors(vectors, kind = 'population', mean) { return varianceOfVectors(vectors, kind, mean).map(v => Math.sqrt(v)); } export function varianceOfVectors(vectors, kind = 'population', mean) { const vectorCount = vectors.length; if (vectorCount == 0) { return new Float32Array(0); } const sampleSizeMetric = kind == 'population' || vectorCount == 1 ? vectorCount : vectorCount - 1; const featureCount = vectors[0].length; if (!mean) { mean = meanOfVectors(vectors); } const result = createVector(featureCount); for (const vector of vectors) { for (let i = 0; i < featureCount; i++) { result[i] += (vector[i] - mean[i]) ** 2; } } for (let i = 0; i < featureCount; i++) { result[i] /= sampleSizeMetric; } return result; } export function meanOfVector(vector) { if (vector.length == 0) { return 0; } return sumVector(vector) / vector.length; } export function medianOfVector(vector) { if (vector.length == 0) { throw new Error('Vector is empty'); } const sortedArray = Array.from(vector).sort((a, b) => a - b); const median = sortedArray[Math.floor(sortedArray.length / 2)]; return median; } export function stdDeviationOfVector(vector, kind = 'population', mean) { return Math.sqrt(varianceOfVector(vector, kind, mean)); } export function varianceOfVector(vector, kind = 'population', mean) { if (vector.length == 0) { return 0; } const sampleSizeMetric = kind == 'population' || vector.length == 1 ? vector.length : vector.length - 1; if (mean == null) { mean = meanOfVector(vector); } let result = 0.0; for (let i = 0; i < vector.length; i++) { result += (vector[i] - mean) ** 2; } return result / sampleSizeMetric; } export function logOfVector(vector, minVal = 1e-40) { const result = new Float32Array(vector.length); for (let i = 0; i < vector.length; i++) { const value = vector[i]; result[i] = Math.log(value + minVal); } return result; } export function expOfVector(vector) { const result = new Float32Array(vector.length); for (let i = 0; i < vector.length; i++) { const value = vector[i]; result[i] = Math.exp(value); } return result; } export function transpose(matrix) { const vectorCount = matrix.length; const featureCount = matrix[0].length; const transposedMatrix = createVectorArray(featureCount, vectorCount); for (let i = 0; i < vectorCount; i++) { for (let j = 0; j < featureCount; j++) { transposedMatrix[j][i] = matrix[i][j]; } } return transposedMatrix; } export function movingAverageOfWindow3(vector) { const elementCount = vector.length; if (elementCount == 0) { return new Float32Array(0); } if (elementCount == 1) { return Float32Array.from(vector); } const result = new Float32Array(elementCount); result[0] = (vector[0] + vector[0] + vector[1]) / 3; for (let i = 1; i < elementCount - 1; i++) { result[i] = (vector[i - 1] + vector[i] + vector[i + 1]) / 3; } result[elementCount - 1] = (vector[elementCount - 2] + vector[elementCount - 1] + vector[elementCount - 1]) / 3; return result; } export function averageMeanSquaredError(actual, expected) { if (actual.length != expected.length) { throw new Error('Vectors are not the same length'); } const vectorCount = actual.length; if (vectorCount == 0) { return 0; } let sum = 0.0; for (let i = 0; i < vectorCount; i++) { sum += meanSquaredError(actual[i], expected[i]); } return sum / vectorCount; } export function meanSquaredError(actual, expected) { if (actual.length != expected.length) { throw new Error('Vectors are not the same length'); } const featureCount = actual.length; if (featureCount == 0) { return 0; } let sum = 0.0; for (let i = 0; i < featureCount; i++) { sum += (actual[i] - expected[i]) ** 2; } return sum / featureCount; } export function euclideanDistance(vector1, vector2) { return Math.sqrt(squaredEuclideanDistance(vector1, vector2)); } export function squaredEuclideanDistance(vector1, vector2) { if (vector1.length !== vector2.length) { throw new Error('Vectors are not the same length'); } const elementCount = vector1.length; if (elementCount === 0) { return 0; } let sum = 0.0; for (let i = 0; i < elementCount; i++) { sum += (vector1[i] - vector2[i]) ** 2; } return sum; } export function euclideanDistance13Dim(vector1, vector2) { return Math.sqrt(squaredEuclideanDistance13Dim(vector1, vector2)); } export function squaredEuclideanDistance13Dim(vector1, vector2) { // Assumes the input has 13 dimensions (optimized for 13-dimensional MFCC vectors) const result = (vector1[0] - vector2[0]) ** 2 + (vector1[1] - vector2[1]) ** 2 + (vector1[2] - vector2[2]) ** 2 + (vector1[3] - vector2[3]) ** 2 + (vector1[4] - vector2[4]) ** 2 + (vector1[5] - vector2[5]) ** 2 + (vector1[6] - vector2[6]) ** 2 + (vector1[7] - vector2[7]) ** 2 + (vector1[8] - vector2[8]) ** 2 + (vector1[9] - vector2[9]) ** 2 + (vector1[10] - vector2[10]) ** 2 + (vector1[11] - vector2[11]) ** 2 + (vector1[12] - vector2[12]) ** 2; return result; } export function cosineDistance(vector1, vector2) { return 1 - cosineSimilarity(vector1, vector2); } export function cosineSimilarity(vector1, vector2) { if (vector1.length !== vector2.length) { throw new Error('Vectors are not the same length'); } if (vector1.length === 0) { return 0; } const elementCount = vector1.length; let dotProduct = 0.0; let squaredMagnitude1 = 0.0; let squaredMagnitude2 = 0.0; for (let i = 0; i < elementCount; i++) { const element1 = vector1[i]; const element2 = vector2[i]; dotProduct += element1 * element2; squaredMagnitude1 += element1 ** 2; squaredMagnitude2 += element2 ** 2; } let result = dotProduct / (Math.sqrt(squaredMagnitude1) * Math.sqrt(squaredMagnitude2) + 1e-40); result = zeroIfNaN(result); result = clip(result, -1.0, 1.0); return result; } export function cosineDistancePrecomputedMagnitudes(vector1, vector2, magnitude1, magnitude2) { return 1 - cosineSimilarityPrecomputedMagnitudes(vector1, vector2, magnitude1, magnitude2); } export function cosineSimilarityPrecomputedMagnitudes(vector1, vector2, magnitude1, magnitude2) { if (vector1.length != vector2.length) { throw new Error('Vectors are not the same length'); } if (vector1.length == 0) { return 0; } const featureCount = vector1.length; let dotProduct = 0.0; for (let i = 0; i < featureCount; i++) { dotProduct += vector1[i] * vector2[i]; } let result = dotProduct / (magnitude1 * magnitude2 + 1e-40); result = zeroIfNaN(result); result = clip(result, -1.0, 1.0); return result; } export function minkowskiDistance(vector1, vector2, power) { if (vector1.length != vector2.length) { throw new Error('Vectors are not the same length'); } const elementCount = vector1.length; if (elementCount == 0) { return 0; } let sum = 0.0; for (let i = 0; i < elementCount; i++) { sum += Math.abs(vector1[i] - vector2[i]) ** power; } return sum ** (1 / power); } export function subtractVectors(vector1, vector2) { const elementCount = vector1.length; if (vector1.length != vector2.length) { throw new Error('Vectors are not the same length'); } const result = createVector(vector1.length); for (let i = 0; i < elementCount; i++) { result[i] = vector1[i] - vector2[i]; } return result; } export function sumVector(vector) { const elementCount = vector.length; let result = 0.0; for (let i = 0; i < elementCount; i++) { result += vector[i]; } return result; } export function sumOfSquaresOfVector(vector) { const elementCount = vector.length; let result = 0.0; for (let i = 0; i < elementCount; i++) { result += vector[i] ** 2; } return result; } export function sumAndSumOfSquaresOfVector(vector) { const elementCount = vector.length; let sum = 0.0; let sumOfSquares = 0.0; for (let i = 0; i < elementCount; i++) { const value = vector[i]; sum += value; sumOfSquares += value ** 2; } return { sum, sumOfSquares }; } export function dotProduct(vector1, vector2) { if (vector1.length != vector2.length) { throw new Error('Vectors are not the same length'); } const elementCount = vector1.length; let result = 0.0; for (let i = 0; i < elementCount; i++) { result += vector1[i] * vector2[i]; } return result; } export function magnitude(vector) { const featureCount = vector.length; let squaredMagnitude = 0.0; for (let i = 0; i < featureCount; i++) { squaredMagnitude += vector[i] ** 2; } return Math.sqrt(squaredMagnitude); } export function maxValue(vector) { return vector[indexOfMax(vector)]; } export function indexOfMax(vector) { if (vector.length == 0) { return -1; } let maxValue = vector[0]; let result = 0; for (let i = 1; i < vector.length; i++) { if (vector[i] > maxValue) { maxValue = vector[i]; result = i; } } return result; } export function minValue(vector) { return vector[indexOfMin(vector)]; } export function indexOfMin(vector) { const elementCount = vector.length; let minValue = Infinity; let result = -1; for (let i = 0; i < elementCount; i++) { if (vector[i] < minValue) { minValue = vector[i]; result = i; } } return result; } export function sigmoid(x) { const result = 1 / (1 + Math.exp(-x)); return zeroIfNaN(result); } export function softmax(logits, temperature = 1.0) { temperature = Math.max(temperature, 0.00001); const logitCount = logits.length; if (logitCount === 0) { return new Float32Array(0); } let maxValue = -Infinity; for (let i = 0; i < logitCount; i++) { const value = logits[i]; if (value > maxValue) { maxValue = value; } } const temperatureReciprocal = 1 / temperature; const results = new Float32Array(logitCount); let sumOfExponentiatedLogits = 0.0; for (let i = 0; i < logitCount; i++) { const logit = logits[i]; const eToLogit = Math.exp((logit - maxValue) * temperatureReciprocal); sumOfExponentiatedLogits += eToLogit; results[i] = eToLogit; } const sumOfExponentiatedValuesReciprocal = 1 / (sumOfExponentiatedLogits + 1e-20); for (let i = 0; i < logitCount; i++) { results[i] *= sumOfExponentiatedValuesReciprocal; } return results; } export function hammingDistance(value1, value2, bitLength = 32) { let valueXor = value1 ^ value2; let result = 0; for (let i = 0; i < bitLength; i++) { result += valueXor & 1; valueXor = valueXor >> 1; } return result; } export function createVectorArray(vectorCount, featureCount, initialValue = 0.0) { const result = new Array(vectorCount); for (let i = 0; i < vectorCount; i++) { result[i] = createVector(featureCount, initialValue); } return result; } export function createVector(elementCount, initialValue = 0.0) { const result = new Float32Array(elementCount); if (initialValue !== 0) { result.fill(initialValue); } return result; } export function zeroIfNaN(val) { if (isNaN(val)) { return 0; } else { return val; } } export function logSumExp(values, minVal = 1e-40) { return Math.log(minVal + sumExp(values)); } export function sumExp(values) { let sumOfExp = 0; for (let i = 0; i < values.length; i++) { const value = values[i]; sumOfExp += Math.exp(value); } return sumOfExp; } export function logSoftmax(values, minVal = 1e-40) { const softMaxOfValues = softmax(values); return logOfVector(softMaxOfValues, minVal); } export class IncrementalMean { currentElementCount = 0; currentMean = 0.0; addValueToMean(value) { this.currentElementCount += 1; this.currentMean += (value + this.currentMean) / this.currentElementCount; } } //# sourceMappingURL=VectorMath.js.map