File size: 2,452 Bytes
31c7d49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
export function layerNormRows(
  input: Float32Array,
  width: number,
  epsilon = 1e-5,
): Float32Array {
  if (!Number.isInteger(width) || width <= 0 || input.length % width !== 0) {
    throw new Error(`layerNormRows width ${width} does not divide input length ${input.length}.`)
  }
  const output = new Float32Array(input.length)
  for (let offset = 0; offset < input.length; offset += width) {
    let mean = 0
    for (let index = 0; index < width; index += 1) mean += input[offset + index]
    mean /= width
    let variance = 0
    for (let index = 0; index < width; index += 1) {
      const centered = input[offset + index] - mean
      variance += centered * centered
    }
    const inverseStandardDeviation = 1 / Math.sqrt(variance / width + epsilon)
    for (let index = 0; index < width; index += 1) {
      output[offset + index] = (input[offset + index] - mean) * inverseStandardDeviation
    }
  }
  return output
}

export interface TensorComparison {
  count: number
  maxAbsoluteError: number
  meanAbsoluteError: number
  rmse: number
  cosineSimilarity: number
  finite: boolean
}

export function compareFloat32(reference: Float32Array, candidate: Float32Array): TensorComparison {
  if (reference.length !== candidate.length) {
    throw new Error(`Tensor lengths differ: reference=${reference.length}, candidate=${candidate.length}.`)
  }
  let maxAbsoluteError = 0
  let absoluteErrorSum = 0
  let squaredErrorSum = 0
  let dot = 0
  let referenceSquared = 0
  let candidateSquared = 0
  let finite = true
  for (let index = 0; index < reference.length; index += 1) {
    const expected = reference[index]
    const actual = candidate[index]
    if (!Number.isFinite(expected) || !Number.isFinite(actual)) finite = false
    const delta = actual - expected
    const absolute = Math.abs(delta)
    maxAbsoluteError = Math.max(maxAbsoluteError, absolute)
    absoluteErrorSum += absolute
    squaredErrorSum += delta * delta
    dot += expected * actual
    referenceSquared += expected * expected
    candidateSquared += actual * actual
  }
  const denominator = Math.sqrt(referenceSquared * candidateSquared)
  return {
    count: reference.length,
    maxAbsoluteError,
    meanAbsoluteError: reference.length > 0 ? absoluteErrorSum / reference.length : 0,
    rmse: reference.length > 0 ? Math.sqrt(squaredErrorSum / reference.length) : 0,
    cosineSimilarity: denominator > 0 ? dot / denominator : 1,
    finite,
  }
}