Skip to content
This repository was archived by the owner on Aug 15, 2019. It is now read-only.

Commit f164662

Browse files
author
Nikhil Thorat
authored
iOS (#109)
* ios * merge * Merge remote-tracking branch 'origin/master' into ios * ios * its working...? haha, not. * improve precision * fix accuracy on iOS * fix the faster / slightly less precise version * slight speedup * fix numerical issues on ios. (use resultUV instead of gl_FragCoord, and highp int) * merge master * actually merge * flag guard byte textures * Merge remote-tracking branch 'origin' into ios * Merge remote-tracking branch 'origin' into ios * more changes * merge * start pulling tests apart * tests * merge * get remaining tests to pass * Merge remote-tracking branch 'origin' into ios * remove comments, remove imagenet util change * imagenet * test_util commits * ndarray tests * test_util blank space * softmax underflow on mac, copy gpu test revert * revert _gpu_tests * remove console.log * fix lint errors * respond to comments
1 parent 61cdbe9 commit f164662

34 files changed

+4777
-2181
lines changed

demos/mnist/mnist.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ reader.getAllVariables().then(vars => {
3939
const probsVal = sess.eval(probs, [{tensor: input, data: inputData}]);
4040
console.log(`Item ${i}, probsVal ${probsVal.get()}.`);
4141
const label = data.labels[i];
42-
const predictedLabel = probsVal.get();
42+
const predictedLabel = Math.round(probsVal.get());
4343
if (label === predictedLabel) {
4444
numCorrect++;
4545
}
@@ -79,12 +79,10 @@ export function buildModelMathAPI(
7979

8080
return (x: Array1D): Scalar => {
8181
return math.scope(() => {
82-
const hidden1 =
83-
math.relu(math.add(math.vectorTimesMatrix(x, hidden1W), hidden1B)) as
84-
Array1D;
85-
const hidden2 =
86-
math.relu(math.add(
87-
math.vectorTimesMatrix(hidden1, hidden2W), hidden2B)) as Array1D;
82+
const hidden1 = math.relu(
83+
math.add(math.vectorTimesMatrix(x, hidden1W), hidden1B)) as Array1D;
84+
const hidden2 = math.relu(math.add(
85+
math.vectorTimesMatrix(hidden1, hidden2W), hidden2B)) as Array1D;
8886
const logits =
8987
math.add(math.vectorTimesMatrix(hidden2, softmaxW), softmaxB);
9088
return math.argMax(logits);

demos/one_plus_one/one_plus_one.ts

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,12 @@
1616
*/
1717

1818
// tslint:disable-next-line:max-line-length
19-
import {Graph, NDArrayMath, NDArrayMathGPU, Scalar, Session, Tensor} from '../deeplearn';
19+
import {NDArrayMathGPU, Scalar} from '../deeplearn';
2020

21-
class Adder {
22-
inputTensorA: Tensor;
23-
inputTensorB: Tensor;
24-
sum: Tensor;
25-
session: Session;
26-
math: NDArrayMath = new NDArrayMathGPU();
27-
setupSession(): void {
28-
const graph = new Graph();
21+
const math = new NDArrayMathGPU();
22+
const a = Scalar.new(1);
23+
const b = Scalar.new(1);
2924

30-
this.inputTensorA = graph.placeholder('A', []);
31-
this.inputTensorB = graph.placeholder('B', []);
32-
this.sum = graph.add(this.inputTensorA, this.inputTensorB);
33-
this.session = new Session(graph, this.math);
34-
}
25+
const result = math.add(a, b).get();
3526

36-
computeSum(a: number, b: number): number {
37-
const feeds = [
38-
{tensor: this.inputTensorA, data: Scalar.new(a)},
39-
{tensor: this.inputTensorB, data: Scalar.new(b)}
40-
];
41-
let result;
42-
this.math.scope(() => {
43-
result = this.session.eval(this.sum, feeds).get();
44-
});
45-
return result;
46-
}
47-
}
48-
49-
const adder = new Adder();
50-
adder.setupSession();
51-
const result = adder.computeSum(1, 1);
52-
53-
const outputEl = document.getElementById('output');
54-
if (!outputEl) throw new Error('output element not found');
55-
outputEl.innerText = String(result);
27+
document.getElementById('output').innerText = '' + result;

src/environment.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,16 @@ export interface Features {
3131
'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_RELIABLE'?: boolean;
3232
// 0: No WebGL, 1: WebGL 1.0, 2: WebGL 2.0.
3333
'WEBGL_VERSION'?: number;
34+
// Whether writing & reading floating point textures is enabled. When
35+
// false, fall back to using unsigned byte textures.
36+
'WEBGL_FLOAT_TEXTURE_ENABLED'?: boolean;
3437
}
3538

3639
export const URL_PROPERTIES: URLProperty[] = [
3740
{name: 'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_ENABLED', type: Type.BOOLEAN},
3841
{name: 'WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_RELIABLE', type: Type.BOOLEAN},
39-
{name: 'WEBGL_VERSION', type: Type.NUMBER}
42+
{name: 'WEBGL_VERSION', type: Type.NUMBER},
43+
{name: 'WEBGL_FLOAT_TEXTURE_ENABLED', type: Type.BOOLEAN}
4044
];
4145

4246
export interface URLProperty {
@@ -91,6 +95,37 @@ function isWebGLDisjointQueryTimerEnabled(webGLVersion: number) {
9195
return isExtEnabled;
9296
}
9397

98+
function isFloatTextureReadPixelsEnabled(webGLVersion: number): boolean {
99+
if (webGLVersion === 0) {
100+
return false;
101+
}
102+
103+
if (webGLVersion === 2) {
104+
// WebGL 2 has floating point textures enabled by default.
105+
return true;
106+
}
107+
108+
const gl = getWebGLRenderingContext(webGLVersion);
109+
gl.getExtension('OES_texture_float');
110+
gl.getExtension('WEBGL_color_buffer_float');
111+
112+
const frameBuffer = gl.createFramebuffer();
113+
const texture = gl.createTexture();
114+
115+
gl.bindTexture(gl.TEXTURE_2D, texture);
116+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.FLOAT, null);
117+
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
118+
gl.framebufferTexture2D(
119+
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
120+
121+
const frameBufferComplete =
122+
(gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE);
123+
124+
loseContext(gl);
125+
126+
return frameBufferComplete;
127+
}
128+
94129
export class Environment {
95130
private features: Features = {};
96131

@@ -129,6 +164,8 @@ export class Environment {
129164
return 1;
130165
}
131166
return 0;
167+
} else if (feature === 'WEBGL_FLOAT_TEXTURE_ENABLED') {
168+
return isFloatTextureReadPixelsEnabled(this.get('WEBGL_VERSION'));
132169
}
133170
throw new Error(`Unknown feature ${feature}.`);
134171
}
@@ -139,7 +176,7 @@ const DEEPLEARNJS_FLAGS_PREFIX = 'dljsflags';
139176
function getFeaturesFromURL(): Features {
140177
const features: Features = {};
141178

142-
if(typeof window === 'undefined') {
179+
if (typeof window === 'undefined') {
143180
return features;
144181
}
145182

src/graph/ops/argmax_test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import {NDArrayMathCPU} from '../../math/math_cpu';
1919
import {Array1D, Array2D} from '../../math/ndarray';
20+
import * as test_util from '../../test_util';
2021
import {Tensor} from '../graph';
2122
import {TensorArrayMap} from '../tensor_array_map';
2223

@@ -50,7 +51,7 @@ describe('Argmax oper', () => {
5051
const yVal = tensorArrayMap.get(y);
5152

5253
expect(yVal.shape).toEqual([]);
53-
expect(yVal.get()).toEqual(1);
54+
test_util.expectNumbersClose(yVal.get(), 1);
5455
});
5556

5657
it('argmax of Array2D', () => {
@@ -64,6 +65,6 @@ describe('Argmax oper', () => {
6465
const yVal = tensorArrayMap.get(y);
6566

6667
expect(yVal.shape).toEqual([]);
67-
expect(yVal.get()).toEqual(4);
68+
test_util.expectNumbersClose(yVal.get(), 4);
6869
});
6970
});

src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import * as xhr_dataset from './data/xhr-dataset';
19+
import * as environment from './environment';
1920
import * as conv_util from './math/conv_util';
2021
import * as gpgpu_util from './math/webgl/gpgpu_util';
2122
import * as render_ndarray_gpu_util from './math/webgl/render_ndarray_gpu_util';
@@ -28,7 +29,7 @@ export {DataStats, InMemoryDataset} from './data/dataset';
2829
// tslint:disable-next-line:max-line-length
2930
export {InCPUMemoryShuffledInputProviderBuilder, InGPUMemoryShuffledInputProviderBuilder, InputProvider} from './data/input_provider';
3031
export {XhrDataset, XhrDatasetConfig, XhrModelConfig} from './data/xhr-dataset';
31-
export {ENV, Features} from './environment';
32+
export {ENV, Environment, Features} from './environment';
3233
export {Graph, Tensor} from './graph/graph';
3334
export {AdadeltaOptimizer} from './graph/optimizers/adadelta_optimizer';
3435
export {AdagradOptimizer} from './graph/optimizers/adagrad_optimizer';
@@ -51,6 +52,7 @@ export {GPGPUContext} from './math/webgl/gpgpu_context';
5152
// Second level exports.
5253
export {
5354
conv_util,
55+
environment,
5456
gpgpu_util,
5557
render_ndarray_gpu_util,
5658
test_util,

src/math/batchnorm_test.ts

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/**
2+
* @license
3+
* Copyright 2017 Google Inc. All Rights Reserved.
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
* =============================================================================
16+
*/
17+
18+
import * as test_util from '../test_util';
19+
import {MathTests} from '../test_util';
20+
21+
import {Array1D, Array3D} from './ndarray';
22+
23+
// math.batchNormalization3D
24+
{
25+
// TODO(nsthorat): Fix the precision for byte-packed batchnorm.
26+
const epsilon = 1e-1;
27+
const tests: MathTests = it => {
28+
it('simple batchnorm, no offset or scale, 2x1x2', math => {
29+
const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400]));
30+
const mean = Array1D.new([1, 2]);
31+
const variance = Array1D.new([2, 3]);
32+
const varianceEpsilon = .001;
33+
34+
const result = math.batchNormalization3D(
35+
x, mean, variance, varianceEpsilon, undefined, undefined);
36+
37+
test_util.expectArraysClose(
38+
result.getValues(), new Float32Array([
39+
(x.get(0, 0, 0) - mean.get(0)) * 1 /
40+
Math.sqrt(variance.get(0) + varianceEpsilon),
41+
(x.get(0, 0, 1) - mean.get(1)) * 1 /
42+
Math.sqrt(variance.get(1) + varianceEpsilon),
43+
(x.get(1, 0, 0) - mean.get(0)) * 1 /
44+
Math.sqrt(variance.get(0) + varianceEpsilon),
45+
(x.get(1, 0, 1) - mean.get(1)) * 1 /
46+
Math.sqrt(variance.get(1) + varianceEpsilon)
47+
]),
48+
epsilon);
49+
50+
x.dispose();
51+
mean.dispose();
52+
variance.dispose();
53+
});
54+
55+
it('simple batchnorm, no offset, 2x1x2', math => {
56+
const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400]));
57+
const mean = Array1D.new([1, 2]);
58+
const variance = Array1D.new([2, 3]);
59+
const scale = Array1D.new([4, 5]);
60+
const varianceEpsilon = .001;
61+
62+
const result = math.batchNormalization3D(
63+
x, mean, variance, varianceEpsilon, scale, undefined);
64+
65+
test_util.expectArraysClose(
66+
result.getValues(), new Float32Array([
67+
(x.get(0, 0, 0) - mean.get(0)) * scale.get(0) /
68+
Math.sqrt(variance.get(0) + varianceEpsilon),
69+
(x.get(0, 0, 1) - mean.get(1)) * scale.get(1) /
70+
Math.sqrt(variance.get(1) + varianceEpsilon),
71+
(x.get(1, 0, 0) - mean.get(0)) * scale.get(0) /
72+
Math.sqrt(variance.get(0) + varianceEpsilon),
73+
(x.get(1, 0, 1) - mean.get(1)) * scale.get(1) /
74+
Math.sqrt(variance.get(1) + varianceEpsilon)
75+
]),
76+
epsilon);
77+
78+
x.dispose();
79+
mean.dispose();
80+
variance.dispose();
81+
scale.dispose();
82+
});
83+
84+
it('simple batchnorm, no scale, 2x1x2', math => {
85+
const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400]));
86+
const mean = Array1D.new([1, 2]);
87+
const variance = Array1D.new([2, 3]);
88+
const offset = Array1D.new([4, 5]);
89+
90+
const varianceEpsilon = .001;
91+
92+
const result = math.batchNormalization3D(
93+
x, mean, variance, varianceEpsilon, undefined, offset);
94+
95+
test_util.expectArraysClose(
96+
result.getValues(), new Float32Array([
97+
offset.get(0) +
98+
(x.get(0, 0, 0) - mean.get(0)) * 1 /
99+
Math.sqrt(variance.get(0) + varianceEpsilon),
100+
offset.get(1) +
101+
(x.get(0, 0, 1) - mean.get(1)) * 1 /
102+
Math.sqrt(variance.get(1) + varianceEpsilon),
103+
offset.get(0) +
104+
(x.get(1, 0, 0) - mean.get(0)) * 1 /
105+
Math.sqrt(variance.get(0) + varianceEpsilon),
106+
offset.get(1) +
107+
(x.get(1, 0, 1) - mean.get(1)) * 1 /
108+
Math.sqrt(variance.get(1) + varianceEpsilon)
109+
]),
110+
epsilon);
111+
x.dispose();
112+
mean.dispose();
113+
variance.dispose();
114+
offset.dispose();
115+
});
116+
117+
it('simple batchnorm, 2x1x2', math => {
118+
const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400]));
119+
const mean = Array1D.new([1, 2]);
120+
const variance = Array1D.new([2, 3]);
121+
const offset = Array1D.new([3, 4]);
122+
const scale = Array1D.new([4, 5]);
123+
124+
const varianceEpsilon = .001;
125+
126+
const result = math.batchNormalization3D(
127+
x, mean, variance, varianceEpsilon, scale, offset);
128+
129+
test_util.expectArraysClose(
130+
result.getValues(), new Float32Array([
131+
offset.get(0) +
132+
(x.get(0, 0, 0) - mean.get(0)) * scale.get(0) /
133+
Math.sqrt(variance.get(0) + varianceEpsilon),
134+
offset.get(1) +
135+
(x.get(0, 0, 1) - mean.get(1)) * scale.get(1) /
136+
Math.sqrt(variance.get(1) + varianceEpsilon),
137+
offset.get(0) +
138+
(x.get(1, 0, 0) - mean.get(0)) * scale.get(0) /
139+
Math.sqrt(variance.get(0) + varianceEpsilon),
140+
offset.get(1) +
141+
(x.get(1, 0, 1) - mean.get(1)) * scale.get(1) /
142+
Math.sqrt(variance.get(1) + varianceEpsilon)
143+
]),
144+
epsilon);
145+
x.dispose();
146+
mean.dispose();
147+
variance.dispose();
148+
scale.dispose();
149+
offset.dispose();
150+
});
151+
152+
it('batchnorm matches tensorflow, 2x3x3', math => {
153+
const x = Array3D.new(
154+
[2, 3, 3], new Float32Array([
155+
0.49955603, 0.04158615, -1.09440524, 2.03854165, -0.61578344,
156+
2.87533573, 1.18105987, 0.807462, 1.87888837, 2.26563962,
157+
-0.37040935, 1.35848753, -0.75347094, 0.15683117, 0.91925946,
158+
0.34121279, 0.92717143, 1.89683965
159+
]));
160+
const mean = Array1D.new([0.39745062, -0.48062894, 0.4847822]);
161+
const variance = Array1D.new([0.32375343, 0.67117643, 1.08334653]);
162+
const offset = Array1D.new([0.69398749, -1.29056387, 0.9429723]);
163+
const scale = Array1D.new([-0.5607271, 0.9878457, 0.25181573]);
164+
const varianceEpsilon = .001;
165+
166+
const result = math.batchNormalization3D(
167+
x, mean, variance, varianceEpsilon, scale, offset);
168+
169+
test_util.expectArraysClose(
170+
result.getValues(), new Float32Array([
171+
0.59352049, -0.66135202, 0.5610874, -0.92077015, -1.45341019,
172+
1.52106473, -0.07704776, 0.26144429, 1.28010017, -1.14422404,
173+
-1.15776136, 1.15425493, 1.82644104, -0.52249442, 1.04803919,
174+
0.74932291, 0.40568101, 1.2844412
175+
]));
176+
177+
x.dispose();
178+
mean.dispose();
179+
variance.dispose();
180+
scale.dispose();
181+
offset.dispose();
182+
});
183+
};
184+
185+
test_util.describeMathCPU('batchNormalization3D', [tests]);
186+
test_util.describeMathGPU('batchNormalization3D', [tests], [
187+
{'WEBGL_FLOAT_TEXTURE_ENABLED': true, 'WEBGL_VERSION': 1},
188+
{'WEBGL_FLOAT_TEXTURE_ENABLED': true, 'WEBGL_VERSION': 2},
189+
{'WEBGL_FLOAT_TEXTURE_ENABLED': false, 'WEBGL_VERSION': 1}
190+
]);
191+
}

0 commit comments

Comments
 (0)