Skip to content

Commit c61a8e5

Browse files
qjia7fs-eire
authored andcommitted
[js/webgpu] Add hardSigmoid activation for fusedConv (#19233)
### Description Add hardSigmoid activation for fusedConv. It will be used by mobilenetv3-small-100 model.
1 parent 55cede9 commit c61a8e5

File tree

8 files changed

+207
-57
lines changed

8 files changed

+207
-57
lines changed

js/web/lib/wasm/jsep/webgpu/ops/3rd-party/conv2d_mm_webgpu.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {TensorView} from '../../../tensor-view';
2424
import {ProgramInfo, ProgramInputTensorInfoDependency, ProgramUniform} from '../../types';
2525
import {createTensorShapeVariables, inputVariable, outputVariable, ShaderHelper, tensorTypeToWsglStorageType, UniformsArrayType} from '../common';
2626
import {ConvAttributes} from '../conv';
27-
import {getActivationSnippet} from '../fuse-utils';
27+
import {appendActivationUniforms, appendActivationUniformsData, getActivationSnippet} from '../fuse-utils';
2828

2929
import {biasSnippet, typeSnippet} from './activation_util';
3030
import {utilFunctions} from './conv_util';
@@ -193,10 +193,7 @@ export const createConv2DMatMulProgramInfo =
193193
{type: 'int32', data: [attributes.pads[0], attributes.pads[1]]}, {type: 'int32', data: attributes.strides},
194194
{type: 'int32', data: attributes.dilations}
195195
];
196-
if (attributes.activation === 'Clip') {
197-
programUniforms.push(
198-
{type: 'float32', data: attributes.clipMax!}, {type: 'float32', data: attributes.clipMin!});
199-
}
196+
appendActivationUniformsData(attributes, programUniforms);
200197
programUniforms.push(
201198
...createTensorShapeVariables(inputs[0].dims), ...createTensorShapeVariables(inputs[1].dims));
202199
const inputDependencies: ProgramInputTensorInfoDependency[] = ['rank', 'rank'];
@@ -212,9 +209,7 @@ export const createConv2DMatMulProgramInfo =
212209
{name: 'pad', type: 'i32', length: 2}, {name: 'stride', type: 'i32', length: 2},
213210
{name: 'dilation', type: 'i32', length: 2}
214211
];
215-
if (attributes.activation === 'Clip') {
216-
uniforms.push({name: 'clip_max', type: 'f32'}, {name: 'clip_min', type: 'f32'});
217-
}
212+
appendActivationUniforms(attributes, uniforms);
218213

219214
// TODO: support component 2, 3.
220215
const components = isVec4 ? 4 : 1;

js/web/lib/wasm/jsep/webgpu/ops/3rd-party/conv_backprop_mm_webgpu.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {TensorView} from '../../../tensor-view';
2424
import {ProgramInfo, ProgramInputTensorInfoDependency, ProgramUniform} from '../../types';
2525
import {createTensorShapeVariables, inputVariable, outputVariable, ShaderHelper, UniformsArrayType} from '../common';
2626
import {ConvTransposeAttributes} from '../conv-transpose';
27-
import {getActivationSnippet} from '../fuse-utils';
27+
import {appendActivationUniforms, appendActivationUniformsData, getActivationSnippet} from '../fuse-utils';
2828

2929
import {biasSnippet, typeSnippet} from './activation_util';
3030
import {utilFunctions} from './conv_util';
@@ -201,10 +201,7 @@ export const createConv2DTransposeMatMulProgramInfo =
201201
{type: 'int32', data: attributes.strides}, {type: 'int32', data: attributes.dilations},
202202
{type: 'int32', data: filterDims}, {type: 'int32', data: pads}
203203
];
204-
if (attributes.activation === 'Clip') {
205-
programUniforms.push(
206-
{type: 'float32', data: attributes.clipMax!}, {type: 'float32', data: attributes.clipMin!});
207-
}
204+
appendActivationUniformsData(attributes, programUniforms);
208205
programUniforms.push(
209206
...createTensorShapeVariables(inputs[0].dims), ...createTensorShapeVariables(inputs[1].dims));
210207

@@ -237,9 +234,7 @@ export const createConv2DTransposeMatMulProgramInfo =
237234
{name: 'filter_dims', type: 'i32', length: filterDims.length},
238235
{name: 'pads', type: 'i32', length: pads.length}
239236
];
240-
if (attributes.activation === 'Clip') {
241-
uniforms.push({name: 'clip_max', type: 'f32'}, {name: 'clip_min', type: 'f32'});
242-
}
237+
appendActivationUniforms(attributes, uniforms);
243238
return `
244239
${utilFunctions('uniforms.result_strides')}
245240
${shaderHelper.registerUniforms(uniforms).declareVariables(...inputVariables, output)};

js/web/lib/wasm/jsep/webgpu/ops/3rd-party/matmul_packed_webgpu.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {TensorView} from '../../../tensor-view';
2323
import {ShapeUtil} from '../../../util';
2424
import {ProgramInfo, ProgramInputTensorInfoDependency, ProgramUniform} from '../../types';
2525
import {createTensorShapeVariables, getBroadcastDims, IndicesHelper, inputVariable, internalVariable, outputVariable, ShaderHelper, tensorTypeToWsglStorageType, UniformsArrayType} from '../common';
26-
import {getActivationSnippet, InternalActivationAttributes} from '../fuse-utils';
26+
import {appendActivationUniforms, appendActivationUniformsData, getActivationSnippet, InternalActivationAttributes} from '../fuse-utils';
2727

2828
import {typeSnippet} from './activation_util';
2929

@@ -449,11 +449,7 @@ export const createMatmulProgramInfo =
449449
const outputShapeTemp = [batchSize, dimAOuter, dimBOuter / components];
450450
const programUniforms: ProgramUniform[] =
451451
[{type: 'int32', data: dimAOuter}, {type: 'int32', data: dimBOuter}, {type: 'int32', data: dimInner}];
452-
if (activationAttributes.activation === 'Clip') {
453-
programUniforms.push(
454-
{type: 'float32', data: activationAttributes.clipMax!},
455-
{type: 'float32', data: activationAttributes.clipMin!});
456-
}
452+
appendActivationUniformsData(activationAttributes, programUniforms);
457453
programUniforms.push(
458454
...createTensorShapeVariables(outerDims), ...createTensorShapeVariables(aShapeTemp),
459455
...createTensorShapeVariables(bShapeTemp));
@@ -481,9 +477,7 @@ export const createMatmulProgramInfo =
481477
}
482478
const uniforms: UniformsArrayType =
483479
[{name: 'dim_a_outer', type: 'i32'}, {name: 'dim_b_outer', type: 'i32'}, {name: 'dim_inner', type: 'i32'}];
484-
if (activationAttributes.activation === 'Clip') {
485-
uniforms.push({name: 'clip_max', type: 'f32'}, {name: 'clip_min', type: 'f32'});
486-
}
480+
appendActivationUniforms(activationAttributes, uniforms);
487481
const applyActivation = getActivationSnippet(activationAttributes, output.type.value);
488482
const declareFunctions = matMulReadWriteFnSource(
489483
components, hasBias, applyActivation, [batchDims, A, B, output], [outerDimsA, outerDimsB, outerDims],

js/web/lib/wasm/jsep/webgpu/ops/conv-grouped.ts

+18-19
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {ProgramInfo, ProgramInputTensorInfoDependency, ProgramUniform} from '../
77

88
import {createTensorShapeVariables, getMaxComponents, inputVariable, outputVariable, ShaderHelper, UniformsArrayType} from './common';
99
import {calculateOutputShape, ConvAttributes} from './conv';
10-
import {getActivationSnippet} from './fuse-utils';
10+
import {appendActivationUniforms, appendActivationUniformsData, getActivationSnippet} from './fuse-utils';
1111

1212
/**
1313
* naive grouped conv implementation, supports 1d/2d conv
@@ -32,10 +32,7 @@ export const createGroupedConvProgramInfo =
3232
{type: 'uint32', data: [attributes.strides[0], attributes.strides[1]]},
3333
{type: 'uint32', data: [attributes.pads[0], attributes.pads[1]]}, {type: 'uint32', data: outputChannelsPerGroup}
3434
];
35-
if (attributes.activation === 'Clip') {
36-
programUniforms.push(
37-
{type: 'float32', data: attributes.clipMax!}, {type: 'float32', data: attributes.clipMin!});
38-
}
35+
appendActivationUniformsData(attributes, programUniforms);
3936
programUniforms.push(
4037
...createTensorShapeVariables(xShape), ...createTensorShapeVariables(wShape),
4138
...createTensorShapeVariables(outputShape));
@@ -61,9 +58,7 @@ export const createGroupedConvProgramInfo =
6158
{name: 'strides', type: 'u32', length: 2}, {name: 'pads', type: 'u32', length: 2},
6259
{name: 'output_channels_per_group', type: 'u32'}
6360
];
64-
if (attributes.activation === 'Clip') {
65-
uniforms.push({name: 'clip_max', type: 'f32'}, {name: 'clip_min', type: 'f32'});
66-
}
61+
appendActivationUniforms(attributes, uniforms);
6762
return `
6863
${shaderHelper.registerUniforms(uniforms).declareVariables(...inputVars, output)}
6964
@@ -132,10 +127,13 @@ export const createGroupedConvVectorizeProgramInfo =
132127
const outputShapeInShader = [outputShape[0], outputShape[1], outputShape[2], outputShape[3] / components];
133128

134129
const programUniforms: ProgramUniform[] = [
135-
{type: 'uint32', data: outputSize}, {type: 'int32', data: attributes.strides},
136-
{type: 'int32', data: attributes.pads}, ...createTensorShapeVariables(xShape),
137-
...createTensorShapeVariables(wShape), ...createTensorShapeVariables(outputShapeInShader)
130+
{type: 'uint32', data: outputSize}, {type: 'int32', data: [attributes.strides[0], attributes.strides[1]]},
131+
{type: 'int32', data: [attributes.pads[0], attributes.pads[1]]}
138132
];
133+
appendActivationUniformsData(attributes, programUniforms);
134+
programUniforms.push(
135+
...createTensorShapeVariables(xShape), ...createTensorShapeVariables(wShape),
136+
...createTensorShapeVariables(outputShapeInShader));
139137
const xNumber = (outputNumber - 1) * attributes.strides[1] + wShape[1];
140138
const getShaderSource = (shaderHelper: ShaderHelper) => {
141139
const output = outputVariable('output', inputs[0].dataType, outputShapeInShader.length, components);
@@ -147,13 +145,14 @@ export const createGroupedConvVectorizeProgramInfo =
147145
inputVars.push(inputVariable('b', inputs[2].dataType, inputs[2].dims, components));
148146
}
149147
const processBias = hasBias ? 'value += b[output_channel];' : '';
150-
148+
const uniforms: UniformsArrayType = [
149+
{name: 'output_size', type: 'u32'},
150+
{name: 'strides', type: 'i32', length: 2},
151+
{name: 'pads', type: 'i32', length: 2},
152+
];
153+
appendActivationUniforms(attributes, uniforms);
151154
return `
152-
${
153-
shaderHelper.registerUniform('output_size', 'u32')
154-
.registerUniform('strides', 'i32', 2)
155-
.registerUniform('pads', 'i32', 2)
156-
.declareVariables(...inputVars, output)}
155+
${shaderHelper.registerUniforms(uniforms).declareVariables(...inputVars, output)}
157156
${shaderHelper.mainStart()}
158157
${shaderHelper.guardAgainstOutOfBoundsWorkgroupSizes('uniforms.output_size')}
159158
let width0 = uniforms.output_shape[3];
@@ -173,7 +172,7 @@ export const createGroupedConvVectorizeProgramInfo =
173172
// Use constant instead of uniform can give better performance for w's height/width.
174173
for (var w_height: u32 = 0u; w_height < ${wShape[0]}; w_height++) {
175174
let x_height = x_corner.x + i32(w_height);
176-
if (x_height >= 0 || u32(x_height) < uniforms.x_shape[1]) {
175+
if (x_height >= 0 && u32(x_height) < uniforms.x_shape[1]) {
177176
for (var i = 0; i < ${xNumber}; i++) {
178177
let x_width = x_corner.y + i;
179178
if (x_width >= 0 && u32(x_width) < uniforms.x_shape[2]) {
@@ -185,7 +184,7 @@ export const createGroupedConvVectorizeProgramInfo =
185184
for (var w_width: u32 = 0u; w_width < ${wShape[1]}; w_width++) {
186185
let w_val = ${w.get('w_height', 'w_width', '0', 'output_channel')};
187186
for (var i = 0u; i < ${outputNumber}u; i++) {
188-
values[i] = fma(x_vals[i * ${attributes.strides[1]}u + w_width], w_val, values[i]);
187+
values[i] = fma(x_vals[i * u32(uniforms.strides[1]) + w_width], w_val, values[i]);
189188
}
190189
}
191190
}

js/web/lib/wasm/jsep/webgpu/ops/fuse-utils.ts

+32-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22
// Licensed under the MIT License.
33

44
import {MAX_CLIP, MIN_CLIP} from '../../util';
5+
import {ProgramUniform} from '../types';
6+
7+
import {UniformsArrayType} from './common';
58

69
export interface InternalActivationAttributes {
710
readonly activation: string;
811
readonly clipMin?: number;
912
readonly clipMax?: number;
13+
readonly alpha?: number;
14+
readonly beta?: number;
1015
}
1116

1217
export const getActivationSnippet = (attributes: InternalActivationAttributes, valueType: string): string => {
@@ -17,17 +22,41 @@ export const getActivationSnippet = (attributes: InternalActivationAttributes, v
1722
return `value = (${valueType}(1.0) / (${valueType}(1.0) + exp(-value)));`;
1823
case 'Clip':
1924
return `value = clamp(value, ${valueType}(uniforms.clip_min), ${valueType}(uniforms.clip_max));`;
25+
case 'HardSigmoid':
26+
return `value = max(${valueType}(0.0), min(${valueType}(1.0), ${valueType}(uniforms.alpha) * value + ${
27+
valueType}(uniforms.beta)));`;
28+
case '':
29+
return '';
2030
// TODO: adding other activations that can be fused.
2131
default:
22-
return '';
32+
throw new Error(`Unsupported activation ${attributes.activation}`);
33+
}
34+
};
35+
36+
export const appendActivationUniformsData =
37+
(attributes: InternalActivationAttributes, programUniform: ProgramUniform[]) => {
38+
if (attributes.activation === 'Clip') {
39+
programUniform.push({type: 'float32', data: attributes.clipMax!}, {type: 'float32', data: attributes.clipMin!});
40+
} else if (attributes.activation === 'HardSigmoid') {
41+
programUniform.push({type: 'float32', data: attributes.alpha!}, {type: 'float32', data: attributes.beta!});
42+
}
43+
};
44+
45+
export const appendActivationUniforms = (attributes: InternalActivationAttributes, uniforms: UniformsArrayType) => {
46+
if (attributes.activation === 'Clip') {
47+
uniforms.push({name: 'clip_max', type: 'f32'}, {name: 'clip_min', type: 'f32'});
48+
} else if (attributes.activation === 'HardSigmoid') {
49+
uniforms.push({name: 'alpha', type: 'f32'}, {name: 'beta', type: 'f32'});
2350
}
2451
};
2552

2653
export const parseInternalActivationAttributes =
2754
(attributes: Record<string, unknown>|undefined): InternalActivationAttributes => {
2855
const activation = attributes?.activation as string || '';
29-
30-
if (activation === 'Clip') {
56+
if (activation === 'HardSigmoid') {
57+
const [alpha, beta] = attributes?.activation_params as [number, number] || [0.2, 0.5];
58+
return {activation, alpha, beta};
59+
} else if (activation === 'Clip') {
3160
const [clipMin, clipMax] = attributes?.activation_params as [number, number] || [MIN_CLIP, MAX_CLIP];
3261
return {activation, clipMax, clipMin};
3362
}

js/web/lib/wasm/jsep/webgpu/ops/matmul.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {ComputeContext, ProgramInfo, ProgramUniform} from '../types';
77

88
import {createMatmulProgramInfo} from './3rd-party/matmul_packed_webgpu';
99
import {createTensorShapeVariables, getBroadcastDims, getMaxComponents, IndicesHelper, inputVariable, internalVariable, outputVariable, ShaderHelper, UniformsArrayType,} from './common';
10-
import {getActivationSnippet, InternalActivationAttributes} from './fuse-utils';
10+
import {appendActivationUniforms, appendActivationUniformsData, getActivationSnippet, InternalActivationAttributes} from './fuse-utils';
1111

1212
export const createNaiveMatmulProgramInfo =
1313
(inputs: readonly TensorView[], activationAttributes: InternalActivationAttributes, outputShape: readonly number[],
@@ -32,11 +32,7 @@ export const createNaiveMatmulProgramInfo =
3232
{type: 'uint32', data: outputSize}, {type: 'uint32', data: M}, {type: 'uint32', data: N},
3333
{type: 'uint32', data: K}
3434
];
35-
if (activationAttributes.activation === 'Clip') {
36-
programUniforms.push(
37-
{type: 'float32', data: activationAttributes.clipMax!},
38-
{type: 'float32', data: activationAttributes.clipMin!});
39-
}
35+
appendActivationUniformsData(activationAttributes, programUniforms);
4036
programUniforms.push(
4137
...createTensorShapeVariables(outerDims), ...createTensorShapeVariables(aShape),
4238
...createTensorShapeVariables(bShape));
@@ -69,9 +65,7 @@ export const createNaiveMatmulProgramInfo =
6965
{name: 'output_size', type: 'u32'}, {name: 'M', type: 'u32'}, {name: 'N', type: 'u32'},
7066
{name: 'K', type: 'u32'}
7167
];
72-
if (activationAttributes.activation === 'Clip') {
73-
uniforms.push({name: 'clip_max', type: 'f32'}, {name: 'clip_min', type: 'f32'});
74-
}
68+
appendActivationUniforms(activationAttributes, uniforms);
7569

7670
const getIndices = (variable: IndicesHelper, broadCastDims: number[]) => {
7771
const rank = variable.rank;

0 commit comments

Comments
 (0)