UNPKG

@tensorflow/tfjs-core

Version:

Hardware-accelerated JavaScript library for machine intelligence

721 lines 138 kB
/** * @license * Copyright 2017 Google LLC. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============================================================================= */ import * as tf from '../index'; import { ALL_ENVS, describeWithFlags } from '../jasmine_util'; import { expectArraysClose } from '../test_util'; function generateCaseInputs(totalSizeTensor, totalSizeFilter) { const inp = new Array(totalSizeTensor); const filt = new Array(totalSizeFilter); for (let i = 0; i < totalSizeTensor; i++) { inp[i] = i + 1; } for (let i = 0; i < totalSizeFilter; i++) { filt[i] = i + 1; } return { input: inp, filter: filt }; } describeWithFlags('conv2d', ALL_ENVS, () => { it('x=[1,4,4,1] f=[1,1,1,3] s=2 d=1 p=same', async () => { const inputDepth = 1; const inputShape = [4, 4, inputDepth]; const outputDepth = 3; const fSize = 1; const pad = 'same'; const stride = [2, 2]; const x = tf.tensor3d([ 10, 30, 50, 70, 20, 40, 60, 80, -10, -30, -50, -70, -20, -40, -60, -80 ], inputShape); const w = tf.tensor4d([1, 0.5, 1], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad); expectArraysClose(await result.data(), [10, 5, 10, 50, 25, 50, -10, -5, -10, -50, -25, -50]); }); it('x=[2,2,2,2] f=[1,1,2,2] s=1 d=1 p=0', async () => { const inputDepth = 2; const inShape = [2, 2, 2, inputDepth]; const outputDepth = 2; const fSize = 1; const pad = 0; const stride = 1; const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], inShape); const w = tf.tensor4d([-1, 1, -2, 0.5], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad); expect(result.shape).toEqual([2, 2, 2, 2]); const expected = [-5, 2, -11, 5, -17, 8, -23, 11, -29, 14, -35, 17, -41, 20, -47, 23]; expectArraysClose(await result.data(), expected); }); it('x=[2,2,1] f=[1,1,1,2] s=1 d=1 p=0', async () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 1; const pad = 0; const stride = 1; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.tensor4d([2], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad); expectArraysClose(await result.data(), [2, 4, 6, 8]); }); it('x=[3,3,2] f=[2,2,2,1] s=1 d=1 p=valid', async () => { const pad = 'valid'; const stride = 1; const x = tf.tensor3d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90], [3, 3, 2]); const w = tf.tensor4d([.1, .2, .3, .4, .5, .6, .7, .8], [2, 2, 2, 1]); const result = tf.conv2d(x, w, stride, pad); const resultData = await result.data(); expect(result.shape).toEqual([2, 2, 1]); expectArraysClose(resultData, new Float32Array([25.6, 53.5, 157.0, 220.9])); }); it('x=[2,2,2,1] f=[1,1,1,1] s=1 d=1 p=0', async () => { const inputDepth = 1; const inShape = [2, 2, 2, inputDepth]; const outputDepth = 1; const fSize = 1; const pad = 0; const stride = 1; const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8], inShape); const w = tf.tensor4d([2], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad); expect(result.shape).toEqual([2, 2, 2, 1]); const expected = [2, 4, 6, 8, 10, 12, 14, 16]; expectArraysClose(await result.data(), expected); }); it('x=[2,1,2,2] f=[1,1,1,1] s=1 d=1 p=0 NCHW', async () => { const inputDepth = 1; const inShape = [2, inputDepth, 2, 2]; const outputDepth = 1; const fSize = 1; const pad = 0; const stride = 1; const dataFormat = 'NCHW'; const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8], inShape); const w = tf.tensor4d([2], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat); expect(result.shape).toEqual([2, 1, 2, 2]); const expected = [2, 4, 6, 8, 10, 12, 14, 16]; expectArraysClose(await result.data(), expected); }); it('x=[4,2,1] f=[4,2,1,1] s=1 d=1 p=same', async () => { const inputDepth = 1; const outputDepth = 1; const pad = 'same'; const stride = 1; const dataFormat = 'NHWC'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4, 5, 6, 7, 8], [4, 2, inputDepth]); const w = tf.tensor4d([3, 1, 5, 0, 2, 7, 8, 9], [4, 2, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([4, 2, 1]); expectArraysClose(resultData, [133, 66, 200, 102, 108, 58, 56, 58]); }); it('x=[4,2,1] f=[4,2,1,1] s=1 d=1 p=explicit', async () => { const inputDepth = 1; const outputDepth = 1; const pad = [[0, 0], [1, 2], [0, 1], [0, 0]]; const stride = 1; const dataFormat = 'NHWC'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4, 5, 6, 7, 8], [4, 2, inputDepth]); const w = tf.tensor4d([3, 1, 5, 0, 2, 7, 8, 9], [4, 2, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([4, 2, 1]); expectArraysClose(resultData, [133, 66, 200, 102, 108, 58, 56, 58]); }); it('x=[2,2,1] f=[2,2,1,1] s=1 d=1 p=same', async () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = 'same'; const stride = 1; const dataFormat = 'NHWC'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([2, 2, 1]); expectArraysClose(resultData, new Float32Array([20, 26, 13, 12])); }); it('x=[1,2,2] f=[2,2,1,1] s=1 d=1 p=same NCHW', async () => { const inputDepth = 1; const inputShape = [inputDepth, 2, 2]; const outputDepth = 1; const fSize = 2; const pad = 'same'; const stride = 1; const dataFormat = 'NCHW'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([1, 2, 2]); expectArraysClose(resultData, [20, 26, 13, 12]); }); it('x=[4,2,2] f=[1,1,4,4] s=1 d=1 p=same NCHW', async () => { // Skip tensorflow backend due to NCHW not supported. if (tf.getBackend() === 'tensorflow') { return; } const inputDepth = 4; const inputShape = [inputDepth, 2, 2]; const outputDepth = 4; const fSize = 1; const pad = 'same'; const stride = 1; const dataFormat = 'NCHW'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], inputShape); const w = tf.tensor4d([3, 3, 3, 3, 1, 1, 1, 1, 5, 5, 5, 5, 0, 0, 0, 0], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([4, 2, 2]); expectArraysClose(resultData, [9, 18, 27, 36, 9, 18, 27, 36, 9, 18, 27, 36, 9, 18, 27, 36]); }); it('x=[3,2,2] f=[1,1,3,4] s=1 d=1 p=same NCHW', async () => { // Skip tensorflow backend due to NCHW not supported. if (tf.getBackend() === 'tensorflow') { return; } const inputDepth = 3; const inputShape = [inputDepth, 2, 2]; const outputDepth = 4; const fSize = 1; const pad = 'same'; const stride = 1; const dataFormat = 'NCHW'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], inputShape); const w = tf.tensor4d([3, 3, 3, 3, 1, 1, 1, 1, 5, 5, 5, 5], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([4, 2, 2]); expectArraysClose(resultData, [9, 18, 27, 36, 9, 18, 27, 36, 9, 18, 27, 36, 9, 18, 27, 36]); }); it('x=[2,2,2,2] f=[1,1,4,4] s=1 d=1 p=same NCHW', async () => { // Skip tensorflow backend due to NCHW not supported. if (tf.getBackend() === 'tensorflow') { return; } const inputDepth = 2; const inputShape = [2, inputDepth, 2, 2]; const outputDepth = 2; const fSize = 1; const pad = 'same'; const stride = 1; const dataFormat = 'NCHW'; const dilation = 1; const x = tf.tensor4d([1, 3, 5, 7, 2, 4, 6, 8, 9, 11, 13, 15, 10, 12, 14, 16], inputShape); const w = tf.tensor4d([-1, 1, -2, 0.5], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([2, 2, 2, 2]); expectArraysClose(resultData, [-5, -11, -17, -23, 2, 5, 8, 11, -29, -35, -41, -47, 14, 17, 20, 23]); }); it('x=[4,2,2] f=[2,2,4,4] s=1 d=1 p=same NCHW', async () => { // Skip tensorflow backend due to NCHW not supported. if (tf.getBackend() === 'tensorflow') { return; } const inputDepth = 4; const inputShape = [inputDepth, 2, 2]; const outputDepth = 4; const fSize = 2; const pad = 'same'; const stride = 1; const dataFormat = 'NCHW'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], inputShape); const w = tf.tensor4d([ 3, 3, 3, 3, 1, 1, 1, 1, 5, 5, 5, 5, 0, 0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 5, 5, 5, 5, 0, 0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 5, 5, 5, 5, 0, 0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 5, 5, 5, 5, 0, 0, 0, 0, ], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([4, 2, 2]); expectArraysClose(resultData, [90, 54, 63, 36, 90, 54, 63, 36, 90, 54, 63, 36, 90, 54, 63, 36]); }); it('x=[1,2,2] f=[2,2,1,1] s=1 d=1 p=explicit NCHW', async () => { const inputDepth = 1; const inputShape = [inputDepth, 2, 2]; const outputDepth = 1; const fSize = 2; const pad = [[0, 0], [0, 0], [0, 1], [0, 1]]; const stride = 1; const dataFormat = 'NCHW'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([1, 2, 2]); expectArraysClose(resultData, [20, 26, 13, 12]); }); it('x=[2,2,2] f=[2,2,2,1] s=1 d=1 p=same NCHW', async () => { const inputDepth = 2; const inputShape = [inputDepth, 2, 2]; const outputDepth = 1; const fSize = 2; const pad = 'same'; const stride = 1; const dataFormat = 'NCHW'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4, 5, 6, 7, 8], inputShape); const w = tf.tensor4d([3, 1, 5, 0, 0, 5, 1, 3], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([1, 2, 2]); expectArraysClose(resultData, [81, 52, 36, 20]); }); it('x=[2,1,2,2] f=[2,2,1,1] s=1 d=1 p=same NCHW', async () => { const inputDepth = 1; const inputShape = [2, inputDepth, 2, 2]; const outputDepth = 1; const fSize = 2; const pad = 'same'; const stride = 1; const dataFormat = 'NCHW'; const dilation = 1; const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8], inputShape); const w = tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const resultData = await result.data(); expect(result.shape).toEqual([2, 1, 2, 2]); expectArraysClose(resultData, [20, 26, 13, 12, 56, 58, 29, 24]); }); it('x=[2,2,1] f=[2,2,1,1] s=1 d=1 p=0', async () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = 0; const stride = 1; const dataFormat = 'NHWC'; const dilation = 1; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); expectArraysClose(await result.data(), [20]); }); it('x=[4,4,1] f=[2,2,1,1] s=1 d=2 p=0', async () => { const inputDepth = 1; const inputShape = [4, 4, inputDepth]; const outputDepth = 1; const fSize = 2; const fSizeDilated = 3; const pad = 0; const stride = 1; const dataFormat = 'NHWC'; const dilation = 2; const noDilation = 1; const x = tf.tensor3d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], inputShape); const w = tf.tensor4d([3, 1, 5, 2], [fSize, fSize, inputDepth, outputDepth]); // adding a dilation rate is equivalent to using a filter // with 0s for the dilation rate const wDilated = tf.tensor4d([3, 0, 1, 0, 0, 0, 5, 0, 2], [fSizeDilated, fSizeDilated, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad, dataFormat, dilation); const expectedResult = tf.conv2d(x, wDilated, stride, pad, dataFormat, noDilation); expect(result.shape).toEqual(expectedResult.shape); expectArraysClose(await result.data(), await expectedResult.data()); expect(result.shape).toEqual(expectedResult.shape); expect(result.dtype).toBe(expectedResult.dtype); }); it('x=[1,3,6,1] f=[2,2,1,1] s=[1,2] d=1 p=valid', async () => { const inputDepth = 1; const inputShape = [1, 3, 6, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = 'valid'; const stride = [1, 2]; const inputs = generateCaseInputs(1 * 3 * 6 * inputDepth, fSize * fSize); const x = tf.tensor4d(inputs.input, inputShape); const w = tf.tensor4d(inputs.filter, [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad); expectArraysClose(await result.data(), [58.0, 78.0, 98.0, 118.0, 138.0, 158.0]); }); it('x=[1,8,8,16] f=[3,3,16,1] s=[2,2] d=1 p=same', async () => { const inputDepth = 16; const xSize = 8; const inputShape = [1, xSize, xSize, inputDepth]; const outputDepth = 1; const fSize = 3; const pad = 'same'; const stride = [2, 2]; // TODO(annxingyuan): Make this test work with large inputs using // generateCaseInputs https://github.com/tensorflow/tfjs/issues/3143 const inputData = []; for (let i = 0; i < xSize * xSize * inputDepth; i++) { inputData.push(i % 5); } const wData = []; for (let i = 0; i < fSize * fSize * inputDepth * outputDepth; i++) { wData.push(i % 5); } const x = tf.tensor4d(inputData, inputShape); const w = tf.tensor4d(wData, [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad); expect(result.shape).toEqual([1, 4, 4, 1]); expectArraysClose(await result.data(), new Float32Array([ 854, 431, 568, 382, 580, 427, 854, 288, 431, 568, 580, 289, 285, 570, 285, 258 ])); }); it('x=[1,8,8,3] f=[3,3,3,4] s=[2,2] d=1 p=same', async () => { const inputDepth = 3; const xSize = 8; const inputShape = [1, xSize, xSize, inputDepth]; const outputDepth = 4; const fSize = 3; const pad = 'same'; const stride = [2, 2]; // TODO(annxingyuan): Make this test work with large inputs using // generateCaseInputs https://github.com/tensorflow/tfjs/issues/3143 const inputData = []; for (let i = 0; i < xSize * xSize * inputDepth; i++) { inputData.push(i % 5); } const wData = []; for (let i = 0; i < fSize * fSize * inputDepth * outputDepth; i++) { wData.push(i % 5); } const x = tf.tensor4d(inputData, inputShape); const w = tf.tensor4d(wData, [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad); expect(result.shape).toEqual([1, 4, 4, 4]); expectArraysClose(await result.data(), new Float32Array([ 104, 125, 126, 102, 133, 126, 104, 57, 137, 102, 57, 112, 64, 40, 76, 92, 116, 53, 110, 142, 50, 104, 133, 137, 104, 125, 126, 102, 83, 88, 78, 33, 133, 126, 104, 57, 137, 102, 57, 112, 116, 53, 110, 142, 37, 76, 100, 99, 33, 68, 83, 88, 70, 83, 76, 64, 92, 88, 64, 40, 51, 44, 27, 50 ])); }); it('x=[1,8,8,3] f=[3,3,3,4] s=[2,2] d=1 p=valid', async () => { const inputDepth = 3; const xSize = 8; const inputShape = [1, xSize, xSize, inputDepth]; const outputDepth = 4; const fSize = 3; const pad = 'valid'; const stride = [2, 2]; const inputData = []; for (let i = 0; i < xSize * xSize * inputDepth; i++) { inputData.push(i % 5); } const wData = []; for (let i = 0; i < fSize * fSize * inputDepth * outputDepth; i++) { wData.push(i % 5); } const x = tf.tensor4d(inputData, inputShape); const w = tf.tensor4d(wData, [fSize, fSize, inputDepth, outputDepth]); const result = tf.conv2d(x, w, stride, pad); expect(result.shape).toEqual([1, 3, 3, 4]); expectArraysClose(await result.data(), new Float32Array([ 104, 125, 126, 102, 133, 126, 104, 57, 137, 102, 57, 112, 116, 53, 110, 142, 50, 104, 133, 137, 104, 125, 126, 102, 133, 126, 104, 57, 137, 102, 57, 112, 116, 53, 110, 142 ])); }); it('x=[1,2,2,3] f=[1,1] s=2 p=1 fractional outputs default rounding', async () => { const inputDepth = 3; const inShape = [1, 2, 2, inputDepth]; const outputDepth = 1; const fSize = 1; const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], inShape); const w = tf.tensor4d([2, 2, 1], [fSize, fSize, inputDepth, outputDepth]); const pad = [[0, 0], [1, 1], [1, 1], [0, 0]]; const strides = 2; const result = tf.conv2d(x, w, strides, pad); expect(result.shape).toEqual([1, 2, 2, 1]); expectArraysClose(await result.data(), [0, 0, 0, 54]); }); it('throws when x is not rank 3', () => { const inputDepth = 1; const outputDepth = 1; const fSize = 2; const pad = 0; const stride = 1; // tslint:disable-next-line:no-any const x = tf.tensor2d([1, 2, 3, 4], [2, 2]); const w = tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad)).toThrowError(); }); it('throws when weights is not rank 4', () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const pad = 0; const stride = 1; const x = tf.tensor3d([1, 2, 3, 4], inputShape); // tslint:disable-next-line:no-any const w = tf.tensor3d([3, 1, 5, 0], [2, 2, 1]); expect(() => tf.conv2d(x, w, stride, pad)).toThrowError(); }); it('throws when x depth does not match weight depth', () => { const inputDepth = 1; const wrongInputDepth = 5; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = 0; const stride = 1; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.randomNormal([fSize, fSize, wrongInputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad)).toThrowError(); }); it('throws when x depth does not match weight depth NCHW', () => { const inputDepth = 1; const wrongInputDepth = 5; const inputShape = [inputDepth, 2, 2]; const outputDepth = 1; const fSize = 2; const pad = 0; const stride = 1; const dataFormat = 'NCHW'; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.randomNormal([fSize, fSize, wrongInputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad, dataFormat)).toThrowError(); }); it('throws when dimRoundingMode is set and pad is same', () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = 'same'; const stride = 1; const dataFormat = 'NHWC'; const dilation = 1; const dimRoundingMode = 'round'; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.randomNormal([fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad, dataFormat, dilation, dimRoundingMode)) .toThrowError(); }); it('throws when dimRoundingMode is set and pad is valid', () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = 'valid'; const stride = 1; const dataFormat = 'NHWC'; const dilation = 1; const dimRoundingMode = 'round'; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.randomNormal([fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad, dataFormat, dilation, dimRoundingMode)) .toThrowError(); }); it('throws when dimRoundingMode is set and pad is a non-integer number', () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = 1.2; const stride = 1; const dataFormat = 'NHWC'; const dilation = 1; const dimRoundingMode = 'round'; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.randomNormal([fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad, dataFormat, dilation, dimRoundingMode)) .toThrowError(); }); it('throws when dimRoundingMode is set and pad is explicit by non-integer ' + 'number', () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = [[0, 0], [0, 2.1], [1, 1], [0, 0]]; const stride = 1; const dataFormat = 'NHWC'; const dilation = 1; const dimRoundingMode = 'round'; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.randomNormal([fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad, dataFormat, dilation, dimRoundingMode)) .toThrowError(); }); it('throws when stride is less than or equal to 0', async () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 1; const pad = 0; const stride = [1, 0]; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.tensor4d([2], [fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad)).toThrowError(); }); it('throws when dilation is less than or equal to 0', async () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 1; const pad = 0; const stride = 1; const dataFormat = 'NHWC'; const dilation = [1, 0]; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.tensor4d([2], [fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad, dataFormat, dilation)) .toThrowError(); }); it('throws when both stride and dilation are greater than 1', () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const outputDepth = 1; const fSize = 2; const pad = 0; const stride = [2, 1]; const dataFormat = 'NHWC'; const dilation = [1, 2]; const x = tf.tensor3d([1, 2, 3, 4], inputShape); const w = tf.tensor4d([3, 1, 5, 0], [fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad, dataFormat, dilation)) .toThrowError(); }); it('gradient with clones input=[3,3,1] f=[2,2,1,1] s=1 p=0', async () => { const inputDepth = 1; const outputDepth = 1; const inputShape = [3, 3, inputDepth]; const filterSize = 2; const stride = 1; const pad = 0; const filterShape = [filterSize, filterSize, inputDepth, outputDepth]; const filter = tf.ones(filterShape); const x = tf.tensor3d([1, 2, 3, 4, 5, 6, 7, 8, 9], inputShape); const dy = tf.tensor3d([3, 1, 2, 0], [2, 2, 1]); const grads = tf.grads((x, filter) => x.clone().conv2d(filter.clone(), stride, pad).clone()); const [dx, dfilter] = grads([x, filter], dy); expect(dx.shape).toEqual(x.shape); expectArraysClose(await dx.data(), [3, 4, 1, 5, 6, 1, 2, 2, 0]); expect(dfilter.shape).toEqual(filterShape); expectArraysClose(await dfilter.data(), [13, 19, 31, 37]); }); it('gradient x=[2,3,3,1] f=[2,2,1,1] s=1 p=0', async () => { const inputDepth = 1; const outputDepth = 1; const inputShape = [2, 3, 3, inputDepth]; const filterSize = 2; const stride = 1; const pad = 0; const filterShape = [filterSize, filterSize, inputDepth, outputDepth]; const filter = tf.ones(filterShape); const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9], inputShape); const dy = tf.tensor4d([3, 1, 2, 0, 3, 1, 2, 0], [2, 2, 2, 1]); const grads = tf.grads((x, filter) => x.conv2d(filter, stride, pad)); const [dx, dfilter] = grads([x, filter], dy); expect(dx.shape).toEqual(x.shape); expectArraysClose(await dx.data(), [3, 4, 1, 5, 6, 1, 2, 2, 0, 3, 4, 1, 5, 6, 1, 2, 2, 0]); expect(dfilter.shape).toEqual(filterShape); expectArraysClose(await dfilter.data(), [13 * 2, 19 * 2, 31 * 2, 37 * 2]); }); it('gradient x=[1,1,3,3] f=[2,2,1,1] s=1 p=0 NCHW', async () => { const inputDepth = 1; const outputDepth = 1; const inputShape = [1, inputDepth, 3, 3]; const filterSize = 2; const stride = 1; const pad = 0; const dataFormat = 'NCHW'; const filterShape = [filterSize, filterSize, inputDepth, outputDepth]; const filter = tf.ones(filterShape); const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9], inputShape); const dy = tf.tensor4d([3, 1, 2, 0], [1, 1, 2, 2]); const grads = tf.grads((x, filter) => x.conv2d(filter, stride, pad, dataFormat)); const [dx, dfilter] = grads([x, filter], dy); expect(dx.shape).toEqual(x.shape); expectArraysClose(await dx.data(), [3, 4, 1, 5, 6, 1, 2, 2, 0]); expect(dfilter.shape).toEqual(filterShape); expectArraysClose(await dfilter.data(), [13, 19, 31, 37]); }); it('gradient x=[2,1,3,3] f=[2,2,1,1] s=1 p=0 NCHW', async () => { const inputDepth = 1; const outputDepth = 1; const inputShape = [2, inputDepth, 3, 3]; const filterSize = 2; const stride = 1; const pad = 0; const dataFormat = 'NCHW'; const filterShape = [filterSize, filterSize, inputDepth, outputDepth]; const filter = tf.ones(filterShape); const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9], inputShape); const dy = tf.tensor4d([3, 1, 2, 0, 3, 1, 2, 0], [2, 1, 2, 2]); const grads = tf.grads((x, filter) => x.conv2d(filter, stride, pad, dataFormat)); const [dx, dfilter] = grads([x, filter], dy); expect(dx.shape).toEqual(x.shape); expectArraysClose(await dx.data(), [3, 4, 1, 5, 6, 1, 2, 2, 0, 3, 4, 1, 5, 6, 1, 2, 2, 0]); expect(dfilter.shape).toEqual(filterShape); expectArraysClose(await dfilter.data(), [26, 38, 62, 74]); }); it('throws when passed x as a non-tensor', () => { const inputDepth = 1; const outputDepth = 1; const fSize = 1; const pad = 0; const stride = 1; const w = tf.tensor4d([2], [fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d({}, w, stride, pad)) .toThrowError(/Argument 'x' passed to 'conv2d' must be a Tensor/); }); it('throws when passed filter as a non-tensor', () => { const inputDepth = 1; const inputShape = [2, 2, inputDepth]; const pad = 0; const stride = 1; const x = tf.tensor3d([1, 2, 3, 4], inputShape); expect(() => tf.conv2d(x, {}, stride, pad)) .toThrowError(/Argument 'filter' passed to 'conv2d' must be a Tensor/); }); it('throws when input is int32', async () => { const inputDepth = 2; const inShape = [2, 2, 2, inputDepth]; const outputDepth = 2; const fSize = 1; const pad = 0; const stride = 1; const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], inShape, 'int32'); const w = tf.tensor4d([-1, 1, -2, 0.5], [fSize, fSize, inputDepth, outputDepth]); expect(() => tf.conv2d(x, w, stride, pad)) .toThrowError(/Argument 'x' passed to 'conv2d' must be float32/); }); it('throws when filter is int32', async () => { const inputDepth = 2; const inShape = [2, 2, 2, inputDepth]; const outputDepth = 2; const fSize = 1; const pad = 0; const stride = 1; const x = tf.tensor4d([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], inShape); const w = tf.tensor4d([-1, 1, -2, 0.5], [fSize, fSize, inputDepth, outputDepth], 'int32'); expect(() => tf.conv2d(x, w, stride, pad)) .toThrowError(/Argument 'filter' passed to 'conv2d' must be float32/); }); it('accepts a tensor-like object', async () => { const pad = 0; const stride = 1; const x = [[[1], [2]], [[3], [4]]]; // 2x2x1 const w = [[[[2]]]]; // 1x1x1x1 const result = tf.conv2d(x, w, stride, pad); expectArraysClose(await result.data(), [2, 4, 6, 8]); }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udjJkX3Rlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi90ZmpzLWNvcmUvc3JjL29wcy9jb252MmRfdGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFFSCxPQUFPLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUMvQixPQUFPLEVBQUMsUUFBUSxFQUFFLGlCQUFpQixFQUFDLE1BQU0saUJBQWlCLENBQUM7QUFDNUQsT0FBTyxFQUFDLGlCQUFpQixFQUFDLE1BQU0sY0FBYyxDQUFDO0FBRy9DLFNBQVMsa0JBQWtCLENBQUMsZUFBdUIsRUFBRSxlQUF1QjtJQUMxRSxNQUFNLEdBQUcsR0FBRyxJQUFJLEtBQUssQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUN2QyxNQUFNLElBQUksR0FBRyxJQUFJLEtBQUssQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUV4QyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsZUFBZSxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ3hDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0tBQ2hCO0lBQ0QsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLGVBQWUsRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUN4QyxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztLQUNqQjtJQUVELE9BQU8sRUFBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUMsQ0FBQztBQUNwQyxDQUFDO0FBRUQsaUJBQWlCLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUU7SUFDekMsRUFBRSxDQUFDLHdDQUF3QyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ3RELE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBQztRQUNyQixNQUFNLFVB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QyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRTlDLGlCQUFpQixDQUFDLE1BQU0sTUFBTSxDQUFDLElBQUksRUFBRSxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ25ELENBQUMsQ0FBQyxDQUFDO0lBRUgsRUFBRSxDQUFDLHNDQUFzQyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ3BELE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBQztRQUNyQixNQUFNLFdBQVcsR0FBRyxDQUFDLENBQUM7UUFDdEIsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDO1FBQ25CLE1BQU0sTUFBTSxHQUFHLENBQUMsQ0FBQztRQUNqQixNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUM7UUFDMUIsTUFBTSxRQUFRLEdBQUcsQ0FBQyxDQUFDO1FBRW5CLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUM7UUFDcEUsTUFBTSxDQUFDLEdBQ0gsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsVUFBVSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7UUFFM0UsTUFBTSxNQUFNLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsVUFBVSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBRWxFLE1BQU0sVUFBVSxHQUFHLE1BQU0sTUFBTSxDQUFDLElB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