mirror of
https://github.com/wassname/keras-js.git
synced 2026-09-09 11:25:25 +08:00
implement Convolution1D layer, with tests
This commit is contained in:
@@ -45,6 +45,8 @@
|
||||
<script src="/test/core/data_MaxoutDense.js"></script>
|
||||
<script src="/test/core/MaxoutDense.js"></script>
|
||||
|
||||
<script src="/test/convolutional/data_Convolution1D.js"></script>
|
||||
<script src="/test/convolutional/Convolution1D.js"></script>
|
||||
<script src="/test/convolutional/data_Convolution2D.js"></script>
|
||||
<script src="/test/convolutional/Convolution2D.js"></script>
|
||||
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Using TensorFlow backend.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"from keras.models import Model\n",
|
||||
"from keras.layers import Input\n",
|
||||
"from keras.layers.convolutional import Convolution1D\n",
|
||||
"from keras import backend as K"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def format_decimal(arr, places=6):\n",
|
||||
" return [round(x * 10**places) / 10**places for x in arr]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Convolution1D"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**[convolutional.Convolution1D.0] 4 length 3 filters on 5x2 input, activation='linear', border_mode='valid', subsample_length=1, bias=True**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"W shape: (4, 2, 3, 1)\n",
|
||||
"W: [0.895265, -0.546905, 0.18884, -0.143383, 0.528281, -0.994279, -0.285153, 0.81939, -0.087838, 0.963605, 0.734714, 0.972055, 0.846533, -0.392613, 0.692207, -0.757556, 0.571153, -0.49899, -0.807941, 0.886982, 0.6521, 0.03665, 0.747001, 0.156751]\n",
|
||||
"b shape: (4,)\n",
|
||||
"b: [0.895265, -0.546905, 0.18884, -0.143383]\n",
|
||||
"\n",
|
||||
"in shape: (5, 2)\n",
|
||||
"in: [0.528281, -0.994279, -0.285153, 0.81939, -0.087838, 0.963605, 0.734714, 0.972055, 0.846533, -0.392613]\n",
|
||||
"out shape: (3, 4)\n",
|
||||
"out: [1.124918, -0.342879, 1.42759, -0.153716, 0.251835, 1.840331, -0.064904, 1.390416, 1.340388, 1.266877, 0.433117, 1.831188]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"data_in_shape = (5, 2)\n",
|
||||
"conv = Convolution1D(4, 3, activation='linear', border_mode='valid', subsample_length=1, bias=True)\n",
|
||||
"\n",
|
||||
"layer_0 = Input(shape=data_in_shape)\n",
|
||||
"layer_1 = conv(layer_0)\n",
|
||||
"model = Model(input=layer_0, output=layer_1)\n",
|
||||
"\n",
|
||||
"# set weights to random (use seed for reproducibility)\n",
|
||||
"weights = []\n",
|
||||
"for w in model.get_weights():\n",
|
||||
" np.random.seed(200)\n",
|
||||
" weights.append(2 * np.random.random(w.shape) - 1)\n",
|
||||
"model.set_weights(weights)\n",
|
||||
"print('W shape:', weights[0].shape)\n",
|
||||
"print('W:', format_decimal(weights[0].ravel().tolist()))\n",
|
||||
"print('b shape:', weights[1].shape)\n",
|
||||
"print('b:', format_decimal(weights[1].ravel().tolist()))\n",
|
||||
"\n",
|
||||
"data_in = 2 * np.random.random(data_in_shape) - 1\n",
|
||||
"print('')\n",
|
||||
"print('in shape:', data_in_shape)\n",
|
||||
"print('in:', format_decimal(data_in.ravel().tolist()))\n",
|
||||
"result = model.predict(np.array([data_in]))\n",
|
||||
"print('out shape:', result[0].shape)\n",
|
||||
"print('out:', format_decimal(result[0].ravel().tolist()))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**[convolutional.Convolution1D.1] 4 length 3 filters on 6x3 input, activation='linear', border_mode='valid', subsample_length=1, bias=False**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"W shape: (4, 3, 3, 1)\n",
|
||||
"W: [-0.772191, 0.495762, 0.222119, 0.619384, 0.425715, -0.719926, -0.464976, -0.704791, -0.543864, -0.528877, 0.380048, -0.703304, -0.108788, 0.401685, -0.806723, 0.765265, 0.739665, 0.689188, -0.452596, 0.571359, 0.402272, -0.010539, 0.672675, -0.191632, -0.653554, -0.269196, 0.994178, -0.318691, 0.010759, 0.078695, -0.501326, 0.625487, -0.614715, -0.839499, -0.811676, -0.300069]\n",
|
||||
"\n",
|
||||
"in shape: (6, 3)\n",
|
||||
"in: [0.57107, 0.361384, -0.924121, -0.417132, -0.39254, 0.967698, -0.674584, 0.924125, 0.403362, 0.417301, 0.795356, -0.367641, -0.398474, 0.889135, -0.81216, 0.383587, 0.922044, 0.427167]\n",
|
||||
"out shape: (4, 4)\n",
|
||||
"out: [-1.877892, -0.642056, -0.468639, -1.365037, -0.876249, 0.22855, -0.661939, -0.585044, 1.423414, -0.225706, -0.233745, -0.120764, 0.283789, -1.702796, 1.034372, 0.323189]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"data_in_shape = (6, 3)\n",
|
||||
"conv = Convolution1D(4, 3, activation='linear', border_mode='valid', subsample_length=1, bias=False)\n",
|
||||
"\n",
|
||||
"layer_0 = Input(shape=data_in_shape)\n",
|
||||
"layer_1 = conv(layer_0)\n",
|
||||
"model = Model(input=layer_0, output=layer_1)\n",
|
||||
"\n",
|
||||
"# set weights to random (use seed for reproducibility)\n",
|
||||
"weights = []\n",
|
||||
"for w in model.get_weights():\n",
|
||||
" np.random.seed(201)\n",
|
||||
" weights.append(2 * np.random.random(w.shape) - 1)\n",
|
||||
"model.set_weights(weights)\n",
|
||||
"print('W shape:', weights[0].shape)\n",
|
||||
"print('W:', format_decimal(weights[0].ravel().tolist()))\n",
|
||||
"\n",
|
||||
"data_in = 2 * np.random.random(data_in_shape) - 1\n",
|
||||
"print('')\n",
|
||||
"print('in shape:', data_in_shape)\n",
|
||||
"print('in:', format_decimal(data_in.ravel().tolist()))\n",
|
||||
"result = model.predict(np.array([data_in]))\n",
|
||||
"print('out shape:', result[0].shape)\n",
|
||||
"print('out:', format_decimal(result[0].ravel().tolist()))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**[convolutional.Convolution1D.2] 2 length 3 filters on 4x6 input, activation='sigmoid', border_mode='valid', subsample_length=2, bias=True**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"W shape: (2, 6, 3, 1)\n",
|
||||
"W: [0.895265, -0.546905, 0.18884, -0.143383, 0.528281, -0.994279, -0.285153, 0.81939, -0.087838, 0.963605, 0.734714, 0.972055, 0.846533, -0.392613, 0.692207, -0.757556, 0.571153, -0.49899, -0.807941, 0.886982, 0.6521, 0.03665, 0.747001, 0.156751, -0.099831, 0.360314, -0.161149, 0.280787, 0.217313, -0.789132, 0.932089, 0.517401, 0.359284, -0.341304, -0.94709, 0.607321]\n",
|
||||
"b shape: (2,)\n",
|
||||
"b: [0.895265, -0.546905]\n",
|
||||
"\n",
|
||||
"in shape: (4, 6)\n",
|
||||
"in: [0.18884, -0.143383, 0.528281, -0.994279, -0.285153, 0.81939, -0.087838, 0.963605, 0.734714, 0.972055, 0.846533, -0.392613, 0.692207, -0.757556, 0.571153, -0.49899, -0.807941, 0.886982, 0.6521, 0.03665, 0.747001, 0.156751, -0.099831, 0.360314]\n",
|
||||
"out shape: (2, 2)\n",
|
||||
"out: [0.444624, 0.773535, 0.564385, 0.133453]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"data_in_shape = (4, 6)\n",
|
||||
"conv = Convolution1D(2, 3, activation='sigmoid', border_mode='same', subsample_length=2, bias=True)\n",
|
||||
"\n",
|
||||
"layer_0 = Input(shape=data_in_shape)\n",
|
||||
"layer_1 = conv(layer_0)\n",
|
||||
"model = Model(input=layer_0, output=layer_1)\n",
|
||||
"\n",
|
||||
"# set weights to random (use seed for reproducibility)\n",
|
||||
"weights = []\n",
|
||||
"for w in model.get_weights():\n",
|
||||
" np.random.seed(200)\n",
|
||||
" weights.append(2 * np.random.random(w.shape) - 1)\n",
|
||||
"model.set_weights(weights)\n",
|
||||
"print('W shape:', weights[0].shape)\n",
|
||||
"print('W:', format_decimal(weights[0].ravel().tolist()))\n",
|
||||
"print('b shape:', weights[1].shape)\n",
|
||||
"print('b:', format_decimal(weights[1].ravel().tolist()))\n",
|
||||
"\n",
|
||||
"data_in = 2 * np.random.random(data_in_shape) - 1\n",
|
||||
"print('')\n",
|
||||
"print('in shape:', data_in_shape)\n",
|
||||
"print('in:', format_decimal(data_in.ravel().tolist()))\n",
|
||||
"result = model.predict(np.array([data_in]))\n",
|
||||
"print('out shape:', result[0].shape)\n",
|
||||
"print('out:', format_decimal(result[0].ravel().tolist()))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**[convolutional.Convolution1D.4] 2 length 7 filters on 8x3 input, activation='tanh', border_mode='same', subsample_length=1, bias=True**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"W shape: (2, 3, 7, 1)\n",
|
||||
"W: [0.861113, -0.237594, 0.330694, 0.998309, 0.786447, 0.538158, -0.228315, 0.217332, -0.475436, -0.018066, -0.489741, -0.522387, 0.79989, 0.27058, -0.683115, -0.650208, 0.259853, -0.509243, 0.958185, 0.089546, 0.739799, 0.114385, -0.378872, -0.168716, 0.302124, 0.850416, -0.984343, 0.839927, -0.895196, 0.303711, 0.128826, 0.058159, 0.254989, -0.759101, 0.793844, 0.647309, 0.252074, 0.075576, -0.859305, 0.952613, -0.053285, -0.677361]\n",
|
||||
"b shape: (2,)\n",
|
||||
"b: [0.861113, -0.237594]\n",
|
||||
"\n",
|
||||
"in shape: (8, 3)\n",
|
||||
"in: [0.330694, 0.998309, 0.786447, 0.538158, -0.228315, 0.217332, -0.475436, -0.018066, -0.489741, -0.522387, 0.79989, 0.27058, -0.683115, -0.650208, 0.259853, -0.509243, 0.958185, 0.089546, 0.739799, 0.114385, -0.378872, -0.168716, 0.302124, 0.850416]\n",
|
||||
"out shape: (8, 2)\n",
|
||||
"out: [0.854959, 0.355561, 0.888899, -0.9834, -0.825345, 0.941694, -0.490434, -0.699848, 0.872523, -0.887886, -0.408331, -0.018902, 0.959544, 0.66633, -0.779488, 0.038157]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"data_in_shape = (8, 3)\n",
|
||||
"conv = Convolution1D(2, 7, activation='tanh', border_mode='same', subsample_length=1, bias=True)\n",
|
||||
"\n",
|
||||
"layer_0 = Input(shape=data_in_shape)\n",
|
||||
"layer_1 = conv(layer_0)\n",
|
||||
"model = Model(input=layer_0, output=layer_1)\n",
|
||||
"\n",
|
||||
"# set weights to random (use seed for reproducibility)\n",
|
||||
"weights = []\n",
|
||||
"for w in model.get_weights():\n",
|
||||
" np.random.seed(204)\n",
|
||||
" weights.append(2 * np.random.random(w.shape) - 1)\n",
|
||||
"model.set_weights(weights)\n",
|
||||
"print('W shape:', weights[0].shape)\n",
|
||||
"print('W:', format_decimal(weights[0].ravel().tolist()))\n",
|
||||
"print('b shape:', weights[1].shape)\n",
|
||||
"print('b:', format_decimal(weights[1].ravel().tolist()))\n",
|
||||
"\n",
|
||||
"data_in = 2 * np.random.random(data_in_shape) - 1\n",
|
||||
"print('')\n",
|
||||
"print('in shape:', data_in_shape)\n",
|
||||
"print('in:', format_decimal(data_in.ravel().tolist()))\n",
|
||||
"result = model.predict(np.array([data_in]))\n",
|
||||
"print('out shape:', result[0].shape)\n",
|
||||
"print('out:', format_decimal(result[0].ravel().tolist()))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.5.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
+4
-4
@@ -47,7 +47,7 @@ export default class Tensor {
|
||||
* 2-D only
|
||||
* see https://github.com/waylonflinn/weblas/wiki/Pipeline
|
||||
*/
|
||||
createWeblasTensor = () => {
|
||||
createWeblasTensor () {
|
||||
if (this.tensor.shape.length === 1) {
|
||||
const shape = [1, this.tensor.shape[0]]
|
||||
this.weblasTensor = new weblas.pipeline.Tensor(shape, this.tensor.data)
|
||||
@@ -60,7 +60,7 @@ export default class Tensor {
|
||||
/**
|
||||
* Transfers weblas pipeline tensor from GPU memory
|
||||
*/
|
||||
transferWeblasTensor = () => {
|
||||
transferWeblasTensor () {
|
||||
if (this.weblasTensor) {
|
||||
const shape = this.weblasTensor.shape
|
||||
const arr = this.weblasTensor.transfer(true)
|
||||
@@ -71,7 +71,7 @@ export default class Tensor {
|
||||
/**
|
||||
* Delete weblas pipeline tensor
|
||||
*/
|
||||
deleteWeblasTensor = () => {
|
||||
deleteWeblasTensor () {
|
||||
if (this.weblasTensor) {
|
||||
this.weblasTensor.delete()
|
||||
delete this.weblasTensor
|
||||
@@ -81,7 +81,7 @@ export default class Tensor {
|
||||
/**
|
||||
* Replaces data in the underlying ndarray.
|
||||
*/
|
||||
replaceTensorData = data => {
|
||||
replaceTensorData (data) {
|
||||
if (data && data.length && data instanceof this._type) {
|
||||
this.tensor.data = data
|
||||
} else if (data && data.length && data instanceof Array) {
|
||||
|
||||
+4
-4
@@ -27,7 +27,7 @@ export default class Layer {
|
||||
*
|
||||
* @param {Tensor[]} weightsArr - array of weights which are instances of Tensor
|
||||
*/
|
||||
setWeights = weightsArr => {
|
||||
setWeights (weightsArr) {
|
||||
this.params.forEach((p, i) => {
|
||||
this.weights[p] = weightsArr[i]
|
||||
})
|
||||
@@ -37,7 +37,7 @@ export default class Layer {
|
||||
* Create weblas pipeline tensor weights
|
||||
* 2-D only
|
||||
*/
|
||||
createWeblasWeights = () => {
|
||||
createWeblasWeights () {
|
||||
this.weblasWeights = {}
|
||||
|
||||
this.params.forEach((p, i) => {
|
||||
@@ -54,7 +54,7 @@ export default class Layer {
|
||||
/**
|
||||
* Transfer weblas pipeline tensor weights
|
||||
*/
|
||||
transferWeblasWeights = () => {
|
||||
transferWeblasWeights () {
|
||||
this.params.forEach((p, i) => {
|
||||
if (this.weblasWeights[p]) {
|
||||
const shape = this.weblasWeights[p].shape
|
||||
@@ -67,7 +67,7 @@ export default class Layer {
|
||||
/**
|
||||
* Delete weblas pipeline tensor weights
|
||||
*/
|
||||
deleteWeblasWeights = () => {
|
||||
deleteWeblasWeights () {
|
||||
this.params.forEach((p, i) => {
|
||||
if (this.weblasWeights[p]) {
|
||||
this.weblasWeights[p].delete()
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import Layer from '../../engine/Layer'
|
||||
import Convolution2D from './Convolution2D'
|
||||
import squeeze from 'ndarray-squeeze'
|
||||
import unsqueeze from 'ndarray-unsqueeze'
|
||||
|
||||
/**
|
||||
* Convolution1D layer class
|
||||
*/
|
||||
export default class Convolution1D extends Layer {
|
||||
/**
|
||||
* Creates a Convolution1D layer
|
||||
* @param {number} nbFilter - Number of convolution filters to use.
|
||||
* @param {number} filterLength - Length of 1D convolution kernel.
|
||||
* @param {Object} [attrs] - layer attributes
|
||||
*/
|
||||
constructor (nbFilter, filterLength, attrs = {}) {
|
||||
super(attrs)
|
||||
const {
|
||||
activation = 'linear',
|
||||
borderMode = 'valid',
|
||||
subsampleLength = 1,
|
||||
bias = true
|
||||
} = attrs
|
||||
|
||||
if (borderMode !== 'valid' && borderMode !== 'same') {
|
||||
throw new Error(`${this.name} [Convolution1D layer] Invalid borderMode.`)
|
||||
}
|
||||
|
||||
// Layer weights specification
|
||||
this.params = this.bias ? ['W', 'b'] : ['W']
|
||||
|
||||
// Bootstrap Convolution2D layer:
|
||||
// Convolution1D is actually a shim on top of Convolution2D, where
|
||||
// all of the computational action is performed
|
||||
// Note that Keras uses `th` dim ordering here.
|
||||
this._conv2d = new Convolution2D(nbFilter, filterLength, 1, {
|
||||
activation,
|
||||
borderMode,
|
||||
subsample: [subsampleLength, 1],
|
||||
dimOrdering: 'th',
|
||||
bias
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for setting layer weights
|
||||
* Override `super` method since weights must be set in `this._conv2d`
|
||||
* @param {Tensor[]} weightsArr - array of weights which are instances of Tensor
|
||||
*/
|
||||
setWeights (weightsArr) {
|
||||
this._conv2d.setWeights(weightsArr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for layer computational logic
|
||||
* @param {Tensor} x
|
||||
* @returns {Tensor} x
|
||||
*/
|
||||
call = x => {
|
||||
x.tensor = unsqueeze(x.tensor).transpose(1, 0, 2)
|
||||
const conv2dOutput = this._conv2d.call(x)
|
||||
x.tensor = squeeze(conv2dOutput.tensor).transpose(1, 0, 2)
|
||||
return x
|
||||
}
|
||||
}
|
||||
@@ -58,16 +58,12 @@ export default class Convolution2D extends Layer {
|
||||
* In `th` mode, W weight tensor has shape [nbFilter, inputChannels, nbRow, nbCol]
|
||||
* @param {Tensor[]} weightsArr - array of weights which are instances of Tensor
|
||||
*/
|
||||
setWeights = weightsArr => {
|
||||
setWeights (weightsArr) {
|
||||
if (this.dimOrdering === 'th') {
|
||||
const weightsArrTheano = weightsArr.map(w => {
|
||||
w.tensor = w.tensor.transpose(3, 2, 0, 1)
|
||||
return w
|
||||
})
|
||||
super.setWeights(weightsArrTheano)
|
||||
} else {
|
||||
super.setWeights(weightsArr)
|
||||
// W
|
||||
weightsArr[0].tensor = weightsArr[0].tensor.transpose(2, 3, 1, 0)
|
||||
}
|
||||
super.setWeights(weightsArr)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +189,7 @@ export default class Convolution2D extends Layer {
|
||||
call = x => {
|
||||
// convert to tf ordering
|
||||
if (this.dimOrdering === 'th') {
|
||||
x.tensor = x.tensor.transpose(2, 0, 1)
|
||||
x.tensor = x.tensor.transpose(1, 2, 0)
|
||||
}
|
||||
|
||||
this._calcOutputShape(x)
|
||||
@@ -239,6 +235,11 @@ export default class Convolution2D extends Layer {
|
||||
|
||||
this.activation(x)
|
||||
|
||||
// convert back to th ordering if necessary
|
||||
if (this.dimOrdering === 'th') {
|
||||
x.tensor = x.tensor.transpose(2, 0, 1)
|
||||
}
|
||||
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import Convolution1D from './Convolution1D'
|
||||
import Convolution2D from './Convolution2D'
|
||||
|
||||
export {
|
||||
Convolution1D,
|
||||
Convolution2D
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default class Merge extends Layer {
|
||||
* @param {Tensor[]} inputs
|
||||
* @returns {boolean} valid
|
||||
*/
|
||||
_validateInputs = inputs => {
|
||||
_validateInputs (inputs) {
|
||||
const shapes = inputs.map(x => x.tensor.shape.slice())
|
||||
if (['sum', 'mul', 'ave', 'cos', 'max'].indexOf(this.mode) > -1) {
|
||||
if (!shapes.every(shape => isEqual(shape, shapes[0]))) {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/* eslint-env browser, mocha */
|
||||
|
||||
describe('convolutional layer: Convolution1D', function () {
|
||||
const assert = chai.assert
|
||||
const styles = testGlobals.styles
|
||||
const logTime = testGlobals.logTime
|
||||
const stringifyCondensed = testGlobals.stringifyCondensed
|
||||
const approxEquals = KerasJS.testUtils.approxEquals
|
||||
const layers = KerasJS.layers
|
||||
|
||||
const testParams = [
|
||||
{
|
||||
inputShape: [5, 2],
|
||||
kernelShape: [4, 3],
|
||||
attrs: { activation: 'linear', borderMode: 'valid', subsampleLength: 1, bias: true }
|
||||
},
|
||||
{
|
||||
inputShape: [6, 3],
|
||||
kernelShape: [4, 3],
|
||||
attrs: { activation: 'linear', borderMode: 'valid', subsampleLength: 1, bias: false }
|
||||
},
|
||||
{
|
||||
inputShape: [4, 6],
|
||||
kernelShape: [2, 3],
|
||||
attrs: { activation: 'sigmoid', borderMode: 'same', subsampleLength: 2, bias: true }
|
||||
},
|
||||
{
|
||||
inputShape: [8, 3],
|
||||
kernelShape: [2, 7],
|
||||
attrs: { activation: 'tanh', borderMode: 'same', subsampleLength: 1, bias: true }
|
||||
}
|
||||
]
|
||||
|
||||
before(function () {
|
||||
console.log('\n%cconvolutional layer: Convolution1D', styles.h1)
|
||||
})
|
||||
|
||||
/*********************************************************
|
||||
* CPU
|
||||
*********************************************************/
|
||||
|
||||
describe('CPU', function () {
|
||||
before(function () {
|
||||
console.log('\n%cCPU', styles.h2)
|
||||
})
|
||||
|
||||
testParams.forEach(({ inputShape, kernelShape, attrs }, i) => {
|
||||
const key = `convolutional.Convolution1D.${i}`
|
||||
const [inputLength, inputFeatures] = inputShape
|
||||
const [nbFilter, filterLength] = kernelShape
|
||||
const title = `[${key}] [CPU] test: ${nbFilter} length ${filterLength} filters on ${inputLength}x${inputFeatures} input, activation='${attrs.activation}', border_mode='${attrs.borderMode}', subsampleLength=${attrs.subsampleLength}, bias=${attrs.bias}`
|
||||
|
||||
it(title, function () {
|
||||
console.log(`\n%c${title}`, styles.h3)
|
||||
let testLayer = new layers.Convolution1D(nbFilter, filterLength, attrs)
|
||||
testLayer.setWeights(TEST_DATA[key].weights.map(w => new KerasJS.Tensor(w.data, w.shape)))
|
||||
let t = new KerasJS.Tensor(TEST_DATA[key].input.data, TEST_DATA[key].input.shape)
|
||||
console.log('%cin', styles.h4, stringifyCondensed(t.tensor))
|
||||
const startTime = performance.now()
|
||||
t = testLayer.call(t)
|
||||
const endTime = performance.now()
|
||||
console.log('%cout', styles.h4, stringifyCondensed(t.tensor))
|
||||
logTime(startTime, endTime)
|
||||
const dataExpected = new Float32Array(TEST_DATA[key].expected.data)
|
||||
const shapeExpected = TEST_DATA[key].expected.shape
|
||||
assert.deepEqual(t.tensor.shape, shapeExpected)
|
||||
assert.isTrue(approxEquals(t.tensor, dataExpected))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
/*********************************************************
|
||||
* GPU
|
||||
*********************************************************/
|
||||
|
||||
describe('GPU', function () {
|
||||
before(function () {
|
||||
console.log('\n%cGPU', styles.h2)
|
||||
})
|
||||
|
||||
testParams.forEach(({ inputShape, kernelShape, attrs }, i) => {
|
||||
const key = `convolutional.Convolution1D.${i}`
|
||||
const [inputLength, inputFeatures] = inputShape
|
||||
const [nbFilter, filterLength] = kernelShape
|
||||
const title = `[${key}] [GPU] test: ${nbFilter} length ${filterLength} filters on ${inputLength}x${inputFeatures} input, activation='${attrs.activation}', border_mode='${attrs.borderMode}', subsampleLength=${attrs.subsampleLength}, bias=${attrs.bias}`
|
||||
|
||||
it(title, function () {
|
||||
console.log(`\n%c${title}`, styles.h3)
|
||||
let testLayer = new layers.Convolution1D(nbFilter, filterLength, attrs)
|
||||
testLayer.setWeights(TEST_DATA[key].weights.map(w => new KerasJS.Tensor(w.data, w.shape)))
|
||||
let t = new KerasJS.Tensor(TEST_DATA[key].input.data, TEST_DATA[key].input.shape, { useWeblas: true })
|
||||
console.log('%cin', styles.h4, stringifyCondensed(t.tensor))
|
||||
const startTime = performance.now()
|
||||
t = testLayer.call(t)
|
||||
const endTime = performance.now()
|
||||
console.log('%cout', styles.h4, stringifyCondensed(t.tensor))
|
||||
logTime(startTime, endTime)
|
||||
const dataExpected = new Float32Array(TEST_DATA[key].expected.data)
|
||||
const shapeExpected = TEST_DATA[key].expected.shape
|
||||
assert.deepEqual(t.tensor.shape, shapeExpected)
|
||||
assert.isTrue(approxEquals(t.tensor, dataExpected))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -63,7 +63,7 @@ describe('convolutional layer: Convolution2D', function () {
|
||||
const key = `convolutional.Convolution2D.${i}`
|
||||
const [inputRows, inputCols, inputChannels] = inputShape
|
||||
const [nbFilter, nbRow, nbCol] = kernelShape
|
||||
const title = `[${key}] [CPU] test 1: ${nbFilter} ${nbRow}x${nbCol} filters on ${inputRows}x${inputCols}x${inputChannels} input, activation='${attrs.activation}', border_mode='${attrs.borderMode}', subsample=${attrs.subsample}, dim_ordering='${attrs.dimOrdering}', bias=${attrs.bias}`
|
||||
const title = `[${key}] [CPU] test: ${nbFilter} ${nbRow}x${nbCol} filters on ${inputRows}x${inputCols}x${inputChannels} input, activation='${attrs.activation}', border_mode='${attrs.borderMode}', subsample=${attrs.subsample}, dim_ordering='${attrs.dimOrdering}', bias=${attrs.bias}`
|
||||
|
||||
it(title, function () {
|
||||
console.log(`\n%c${title}`, styles.h3)
|
||||
@@ -97,7 +97,7 @@ describe('convolutional layer: Convolution2D', function () {
|
||||
const key = `convolutional.Convolution2D.${i}`
|
||||
const [inputRows, inputCols, inputChannels] = inputShape
|
||||
const [nbFilter, nbRow, nbCol] = kernelShape
|
||||
const title = `[${key}] [GPU] test 1: ${nbFilter} ${nbRow}x${nbCol} filters on ${inputRows}x${inputCols}x${inputChannels} input, activation='${attrs.activation}', border_mode='${attrs.borderMode}', subsample=${attrs.subsample}, dim_ordering='${attrs.dimOrdering}', bias=${attrs.bias}`
|
||||
const title = `[${key}] [GPU] test: ${nbFilter} ${nbRow}x${nbCol} filters on ${inputRows}x${inputCols}x${inputChannels} input, activation='${attrs.activation}', border_mode='${attrs.borderMode}', subsample=${attrs.subsample}, dim_ordering='${attrs.dimOrdering}', bias=${attrs.bias}`
|
||||
|
||||
it(title, function () {
|
||||
console.log(`\n%c${title}`, styles.h3)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// TEST DATA
|
||||
// Keyed by mocha test ID
|
||||
// Python code for generating test data can be found in the matching jupyter notebook in folder `notebooks/`.
|
||||
|
||||
(function () {
|
||||
var DATA = {
|
||||
'convolutional.Convolution1D.0': {
|
||||
input: {
|
||||
data: [0.528281, -0.994279, -0.285153, 0.81939, -0.087838, 0.963605, 0.734714, 0.972055, 0.846533, -0.392613],
|
||||
shape: [5, 2]
|
||||
},
|
||||
weights: [
|
||||
{
|
||||
data: [0.895265, -0.546905, 0.18884, -0.143383, 0.528281, -0.994279, -0.285153, 0.81939, -0.087838, 0.963605, 0.734714, 0.972055, 0.846533, -0.392613, 0.692207, -0.757556, 0.571153, -0.49899, -0.807941, 0.886982, 0.6521, 0.03665, 0.747001, 0.156751],
|
||||
shape: [4, 2, 3, 1]
|
||||
},
|
||||
{
|
||||
data: [0.895265, -0.546905, 0.18884, -0.143383],
|
||||
shape: [4]
|
||||
}
|
||||
],
|
||||
expected: {
|
||||
data: [1.124918, -0.342879, 1.42759, -0.153716, 0.251835, 1.840331, -0.064904, 1.390416, 1.340388, 1.266877, 0.433117, 1.831188],
|
||||
shape: [3, 4]
|
||||
}
|
||||
},
|
||||
'convolutional.Convolution1D.1': {
|
||||
input: {
|
||||
data: [0.57107, 0.361384, -0.924121, -0.417132, -0.39254, 0.967698, -0.674584, 0.924125, 0.403362, 0.417301, 0.795356, -0.367641, -0.398474, 0.889135, -0.81216, 0.383587, 0.922044, 0.427167],
|
||||
shape: [6, 3]
|
||||
},
|
||||
weights: [
|
||||
{
|
||||
data: [-0.772191, 0.495762, 0.222119, 0.619384, 0.425715, -0.719926, -0.464976, -0.704791, -0.543864, -0.528877, 0.380048, -0.703304, -0.108788, 0.401685, -0.806723, 0.765265, 0.739665, 0.689188, -0.452596, 0.571359, 0.402272, -0.010539, 0.672675, -0.191632, -0.653554, -0.269196, 0.994178, -0.318691, 0.010759, 0.078695, -0.501326, 0.625487, -0.614715, -0.839499, -0.811676, -0.300069],
|
||||
shape: [4, 3, 3, 1]
|
||||
},
|
||||
{
|
||||
data: [0.895265, -0.546905, 0.18884, -0.143383],
|
||||
shape: [4]
|
||||
}
|
||||
],
|
||||
expected: {
|
||||
data: [-1.877892, -0.642056, -0.468639, -1.365037, -0.876249, 0.22855, -0.661939, -0.585044, 1.423414, -0.225706, -0.233745, -0.120764, 0.283789, -1.702796, 1.034372, 0.323189],
|
||||
shape: [4, 4]
|
||||
}
|
||||
},
|
||||
'convolutional.Convolution1D.2': {
|
||||
input: {
|
||||
data: [0.18884, -0.143383, 0.528281, -0.994279, -0.285153, 0.81939, -0.087838, 0.963605, 0.734714, 0.972055, 0.846533, -0.392613, 0.692207, -0.757556, 0.571153, -0.49899, -0.807941, 0.886982, 0.6521, 0.03665, 0.747001, 0.156751, -0.099831, 0.360314],
|
||||
shape: [4, 6]
|
||||
},
|
||||
weights: [
|
||||
{
|
||||
data: [0.895265, -0.546905, 0.18884, -0.143383, 0.528281, -0.994279, -0.285153, 0.81939, -0.087838, 0.963605, 0.734714, 0.972055, 0.846533, -0.392613, 0.692207, -0.757556, 0.571153, -0.49899, -0.807941, 0.886982, 0.6521, 0.03665, 0.747001, 0.156751, -0.099831, 0.360314, -0.161149, 0.280787, 0.217313, -0.789132, 0.932089, 0.517401, 0.359284, -0.341304, -0.94709, 0.607321],
|
||||
shape: [2, 6, 3, 1]
|
||||
},
|
||||
{
|
||||
data: [0.895265, -0.546905],
|
||||
shape: [2]
|
||||
}
|
||||
],
|
||||
expected: {
|
||||
data: [0.444624, 0.773535, 0.564385, 0.133453],
|
||||
shape: [2, 2]
|
||||
}
|
||||
},
|
||||
'convolutional.Convolution1D.3': {
|
||||
input: {
|
||||
data: [0.330694, 0.998309, 0.786447, 0.538158, -0.228315, 0.217332, -0.475436, -0.018066, -0.489741, -0.522387, 0.79989, 0.27058, -0.683115, -0.650208, 0.259853, -0.509243, 0.958185, 0.089546, 0.739799, 0.114385, -0.378872, -0.168716, 0.302124, 0.850416],
|
||||
shape: [8, 3]
|
||||
},
|
||||
weights: [
|
||||
{
|
||||
data: [0.861113, -0.237594, 0.330694, 0.998309, 0.786447, 0.538158, -0.228315, 0.217332, -0.475436, -0.018066, -0.489741, -0.522387, 0.79989, 0.27058, -0.683115, -0.650208, 0.259853, -0.509243, 0.958185, 0.089546, 0.739799, 0.114385, -0.378872, -0.168716, 0.302124, 0.850416, -0.984343, 0.839927, -0.895196, 0.303711, 0.128826, 0.058159, 0.254989, -0.759101, 0.793844, 0.647309, 0.252074, 0.075576, -0.859305, 0.952613, -0.053285, -0.677361],
|
||||
shape: [2, 3, 7, 1]
|
||||
},
|
||||
{
|
||||
data: [0.861113, -0.237594],
|
||||
shape: [2]
|
||||
}
|
||||
],
|
||||
expected: {
|
||||
data: [0.854959, 0.355561, 0.888899, -0.9834, -0.825345, 0.941694, -0.490434, -0.699848, 0.872523, -0.887886, -0.408331, -0.018902, 0.959544, 0.66633, -0.779488, 0.038157],
|
||||
shape: [8, 2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.TEST_DATA = Object.assign({}, window.TEST_DATA, DATA)
|
||||
})()
|
||||
Reference in New Issue
Block a user