Skip to content

Commit 9c168cc

Browse files
authored
Add UpSampling layer (#972)
1 parent 03de4b9 commit 9c168cc

File tree

8 files changed

+434
-3
lines changed

8 files changed

+434
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ for (let i = 0; i < n; i++) {
213213
| function | absolute, acos, acosh, APL, Aranda, asin, asinh, atan, atanh, attention, batch normalization, BDAA, Bent identity, BLU, BReLU, ceil, CELU, cloglog, cloglogm, cos, cosh, CReLU, EELU, (hard) ELiSH, Elliott, ELU, embedding, EReLU, erf, ESwish, exp, FELU, full, floor, FReLU, gaussian, GELU, Hard shrink, Hexpo, identity, ISigmoid, layer normalization, Leaky ReLU, LiSHT, log, loglog, logsigmoid, mish, MPELU, MTLU, negative, NLReLU, PAU, PDELU, PELU, PLU, PReLU, PREU, PSF, pTanh, PTELU, reciprocal, ReLU, RePU, ReSech, REU, rootsig, round, RReLU, RTReLU, SELU, (hard) sigmoid, sign, SiLU, sin, sinh, SLAF, SLU, softmax, softplus, Soft shrink, softsign, sqrt, square, SReLU, SRS, sSigmoid, sTanh, (hard) Swish, TAF, tan, (hard) tanh, tanhExp, tanShrink, Thresholded ReLU |
214214
| operator | add, sub, mult, div, mod, matmul, power, max, min |
215215
| logical | and, bitwise and, bitwise not, bitwise or, bitwise xor, equal, greater, greater or equal, is inf, is nan, left bitshift, less, less or equal, not, or, right bitshift, xor |
216-
| convolute | convolution, (Global) MaxPool, (Global) AveragePool, (Global) LpPool, LRN |
216+
| convolute | convolution, (Global) MaxPool, (Global) AveragePool, (Global) LpPool, LRN, UpSampling |
217217
| recurrent | GRU, LSTM, Simple RNN |
218218
| reduce | sum, mean, prod, variance, std, reduce max, reduce min, argmax, softargmax |
219219
| graph | convolutional, SAGE, readout |

lib/model/nns/layer/conv.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default class ConvLayer extends Layer {
1010
* @param {number | number[]} config.kernel Size of kernel
1111
* @param {number} [config.channel] Number of output channel
1212
* @param {number | number[]} [config.stride] Step of stride
13-
* @param {number | number[]} [config.padding] size of padding
13+
* @param {number | number[] | [number, number][]} [config.padding] size of padding
1414
* @param {number[][] | Tensor | string} [config.w] Weight of kernel
1515
* @param {string | object} [config.activation] Name of activation or activation layer object
1616
* @param {number} [config.l2_decay] L2 decay

lib/model/nns/layer/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export { default as SwishLayer } from './swish.js'
9999
export { default as TrainableAFLayer } from './taf.js'
100100
export { default as ThresholdedReLULayer } from './thresholded_relu.js'
101101
export { default as TransposeLayer } from './transpose.js'
102+
export { default as UpSamplingLayer } from './upsampling.js'
102103
export { default as VariableLayer } from './variable.js'
103104
export { default as VarLayer } from './variance.js'
104105

@@ -145,7 +146,7 @@ export { default as VarLayer } from './variance.js'
145146
* { type: 'concat', axis?: number } |
146147
* { type: 'cond' } |
147148
* { type: 'const', value: number } |
148-
* { type: 'conv', kernel: number | number[], channel?: number, stride?: number | number[], padding?: number | number[], w?: number[][] | Tensor | string, activation?: string | object, l2_decay?: number, l1_decay?: number, channel_dim?: number } |
149+
* { type: 'conv', kernel: number | number[], channel?: number, stride?: number | number[], padding?: number | number[] | [number, number][], w?: number[][] | Tensor | string, activation?: string | object, l2_decay?: number, l1_decay?: number, channel_dim?: number } |
149150
* { type: 'cos' } |
150151
* { type: 'cosh' } |
151152
* { type: 'crelu' } |
@@ -283,6 +284,7 @@ export { default as VarLayer } from './variance.js'
283284
* { type: 'tanhshrink' } |
284285
* { type: 'thresholded_relu', a?: number } |
285286
* { type: 'transpose', axis: number[] } |
287+
* { type: 'up_sampling', size: number | number[], channel_dim?: number } |
286288
* { type: 'variable', size: number[] | string, l2_decay?: number, l1_decay?: number, value?: number[] | number[][] | Tensor } |
287289
* { type: 'variance', axis?: number | number[] | string, keepdims?: boolean } |
288290
* { type: 'xor' }

lib/model/nns/layer/upsampling.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import Layer, { NeuralnetworkLayerException } from './base.js'
2+
import Tensor from '../../../util/tensor.js'
3+
4+
/**
5+
* Max pool layer
6+
*/
7+
export default class UpSamplingLayer extends Layer {
8+
/**
9+
* @param {object} config object
10+
* @param {number | number[]} config.size Size of kernel
11+
* @param {number} [config.channel_dim] Dimension of the channel
12+
*/
13+
constructor({ size, channel_dim = -1, ...rest }) {
14+
super(rest)
15+
this._size = size
16+
this._channel_dim = channel_dim
17+
if (this._channel_dim !== -1 && this._channel_dim !== 1) {
18+
throw new NeuralnetworkLayerException('Invalid channel dimension.')
19+
}
20+
}
21+
22+
_index(i, c, k) {
23+
return this._channel_dim === -1 ? [i, ...k, c] : [i, c, ...k]
24+
}
25+
26+
calc(x) {
27+
if (!Array.isArray(this._size)) {
28+
this._size = Array(x.dimension - 2).fill(this._size)
29+
}
30+
if (x.dimension !== this._size.length + 2) {
31+
throw new NeuralnetworkLayerException('Invalid size', [this, x])
32+
}
33+
this._i = x
34+
const koff = this._channel_dim === -1 ? 1 : 2
35+
const outSize = [x.sizes[0], ...this._size.map((k, d) => x.sizes[d + koff] * k)]
36+
if (this._channel_dim === -1) {
37+
outSize.push(x.sizes[x.dimension - 1])
38+
} else if (this._channel_dim === 1) {
39+
outSize.splice(1, 0, x.sizes[1])
40+
}
41+
const channels = this._channel_dim === -1 ? x.sizes[x.dimension - 1] : x.sizes[1]
42+
this._o = new Tensor(outSize)
43+
for (let i = 0; i < x.sizes[0]; i++) {
44+
for (let c = 0; c < channels; c++) {
45+
const idx = Array(x.dimension - 2).fill(0)
46+
do {
47+
const offset = Array(x.dimension - 2).fill(0)
48+
do {
49+
const p = idx.map((v, i) => v * this._size[i] + offset[i])
50+
this._o.set(this._index(i, c, p), x.at(this._index(i, c, idx)))
51+
for (let k = 0; k < offset.length; k++) {
52+
offset[k]++
53+
if (offset[k] < this._size[k]) {
54+
break
55+
}
56+
offset[k] = 0
57+
}
58+
} while (offset.some(v => v > 0))
59+
for (let k = 0; k < idx.length; k++) {
60+
idx[k]++
61+
if (idx[k] < this._i.sizes[k + koff]) {
62+
break
63+
}
64+
idx[k] = 0
65+
}
66+
} while (idx.some(v => v > 0))
67+
}
68+
}
69+
return this._o
70+
}
71+
72+
grad(bo) {
73+
this._bo = bo
74+
this._bi = new Tensor(this._i.sizes)
75+
const koff = this._channel_dim === -1 ? 1 : 2
76+
const channels = this._channel_dim === -1 ? this._i.sizes[this._i.dimension - 1] : this._i.sizes[1]
77+
for (let i = 0; i < this._i.sizes[0]; i++) {
78+
for (let c = 0; c < channels; c++) {
79+
const idx = Array(this._i.dimension - 2).fill(0)
80+
do {
81+
const offset = Array(this._i.dimension - 2).fill(0)
82+
let sum = 0
83+
do {
84+
const p = idx.map((v, i) => v * this._size[i] + offset[i])
85+
sum += this._bo.at(this._index(i, c, p))
86+
for (let k = 0; k < offset.length; k++) {
87+
offset[k]++
88+
if (offset[k] < this._size[k]) {
89+
break
90+
}
91+
offset[k] = 0
92+
}
93+
} while (offset.some(v => v > 0))
94+
95+
this._bi.set(this._index(i, c, idx), sum)
96+
for (let k = 0; k < idx.length; k++) {
97+
idx[k]++
98+
if (idx[k] < this._i.sizes[k + koff]) {
99+
break
100+
}
101+
idx[k] = 0
102+
}
103+
} while (idx.some(v => v > 0))
104+
}
105+
}
106+
return this._bi
107+
}
108+
109+
toObject() {
110+
return {
111+
type: 'up_sampling',
112+
size: this._size,
113+
channel_dim: this._channel_dim,
114+
}
115+
}
116+
}
117+
118+
UpSamplingLayer.registLayer()

lib/model/nns/onnx/layer/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,6 @@ export { default as tanhexp } from './tanhexp.js'
146146
export { default as tanhshrink } from './tanhshrink.js'
147147
export { default as thresholded_relu } from './thresholded_relu.js'
148148
export { default as transpose } from './transpose.js'
149+
export { default as up_sampling } from './up_sampling.js'
149150
export { default as variance } from './variance.js'
150151
export { default as xor } from './xor.js'
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { onnx } from '../onnx_exporter.js'
2+
3+
/**
4+
* Handle upsampling layer
5+
*/
6+
export default {
7+
/**
8+
* Export to onnx object.
9+
* @param {onnx.ModelProto} model Model object
10+
* @param {import("../../graph").LayerObject & {type: 'up_sampling'}} obj Node object
11+
* @param {{[key: string]: {type: onnx.TensorProto.DataType; size: number[]}}} info Output informatino of other layers
12+
* @returns {{type: onnx.TensorProto.DataType; size: number[]}} Output information of this layer
13+
*/
14+
export(model, obj, info) {
15+
const input = Array.isArray(obj.input) ? obj.input[0] : obj.input
16+
const inSize = info[input].size
17+
const scale = Array.isArray(obj.size) ? obj.size : Array(inSize.length - 2).fill(obj.size)
18+
scale.unshift(1)
19+
if (obj.channel_dim == null || obj.channel_dim === -1) {
20+
scale.push(1)
21+
} else if (obj.channel_dim === 1) {
22+
scale.splice(1, 0, 1)
23+
}
24+
const outSize = inSize.map((v, i) => (v == null ? null : v * scale[i]))
25+
26+
const tensor_scale = new onnx.TensorProto()
27+
tensor_scale.setName(obj.name + '_scale')
28+
tensor_scale.setDataType(onnx.TensorProto.DataType.FLOAT)
29+
tensor_scale.setDimsList([scale.length])
30+
tensor_scale.setFloatDataList(scale)
31+
32+
const node = new onnx.NodeProto()
33+
node.setOpType('Resize')
34+
node.addInput(input)
35+
node.addInput('')
36+
node.addInput(obj.name + '_scale')
37+
node.addOutput(obj.name)
38+
const mode = new onnx.AttributeProto()
39+
mode.setName('mode')
40+
mode.setType(onnx.AttributeProto.AttributeType.STRING)
41+
mode.setS(new TextEncoder().encode('nearest'))
42+
node.addAttribute(mode)
43+
44+
const graph = model.getGraph()
45+
graph.addInitializer(tensor_scale)
46+
graph.addNode(node)
47+
return { size: outSize }
48+
},
49+
}

0 commit comments

Comments
 (0)