(input, normalized_shape, { eps = 1e-5 } = {})
| 1203 | * @returns {Tensor} The normalized tensor. |
| 1204 | */ |
| 1205 | export function layer_norm(input, normalized_shape, { eps = 1e-5 } = {}) { |
| 1206 | if (input.dims.length !== 2) { |
| 1207 | throw new Error('`layer_norm` currently only supports 2D input.'); |
| 1208 | } |
| 1209 | |
| 1210 | const [batchSize, featureDim] = input.dims; |
| 1211 | |
| 1212 | if (normalized_shape.length !== 1 && normalized_shape[0] !== featureDim) { |
| 1213 | throw new Error('`normalized_shape` must be a 1D array with shape `[input.dims[1]]`.'); |
| 1214 | } |
| 1215 | |
| 1216 | const [std, mean] = std_mean(input, 1, 0, true); |
| 1217 | const stdData = /** @type {Float32Array} */ (std.data); |
| 1218 | const meanData = /** @type {Float32Array} */ (mean.data); |
| 1219 | |
| 1220 | const inputData = /** @type {Float32Array} */ (input.data); |
| 1221 | |
| 1222 | // @ts-ignore |
| 1223 | const returnedData = new inputData.constructor(inputData.length); |
| 1224 | |
| 1225 | for (let i = 0; i < batchSize; ++i) { |
| 1226 | const offset = i * featureDim; |
| 1227 | for (let j = 0; j < featureDim; ++j) { |
| 1228 | const offset2 = offset + j; |
| 1229 | returnedData[offset2] = (inputData[offset2] - meanData[i]) / (stdData[i] + eps); |
| 1230 | } |
| 1231 | } |
| 1232 | return new Tensor(input.type, returnedData, input.dims); |
| 1233 | } |
| 1234 | |
| 1235 | /** |
| 1236 | * Helper function to calculate new dimensions when performing a squeeze operation. |
no test coverage detected