Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGL backend doesn't work. #872

Open
jpnykw opened this issue Aug 1, 2018 · 2 comments
Open

WebGL backend doesn't work. #872

jpnykw opened this issue Aug 1, 2018 · 2 comments
Assignees

Comments

@jpnykw
Copy link

jpnykw commented Aug 1, 2018

All of the output became NaN without any error message.
model structure:

import numpy as np
import chainer
from chainer import Chain, Variable
import chainer.functions as F
import chainer.links as L
from chainer import serializers

class EqualizedConv2d(chainer.Chain):
    def __init__(self, in_dim, out_dim,ksize=3,stride=1,pad=1):
        w = chainer.initializers.Normal(1.0) # equalized learning rate
        self.inv_c = np.sqrt(2.0/(in_dim*ksize**2))
        super(EqualizedConv2d,self).__init__(
            normalize=normalize(),
        )
        with self.init_scope():
            self.c1=L.Convolution2D(in_dim, out_dim, ksize, stride, pad, initialW=w)
    def __call__(self,x,normalize = True,dropout = False):
        h = self.c1(x * self.inv_c)

        if normalize:
            h = self.normalize(h)

        if dropout:
          F.dropout(h)
        return h

class EqualizedLinear(chainer.Chain):
    def __init__(self, in_dim, out_dim):
        w = chainer.initializers.Normal(1.0) # equalized learning rate
        self.inv_c = np.sqrt(2.0/(in_dim))
        super(EqualizedLinear,self).__init__(
        )
        with self.init_scope():
            self.l1=L.Linear(in_dim, out_dim, initialW=w)
    def __call__(self,x):
        h = self.l1(x * self.inv_c)
        return h

class g_block(chainer.Chain):
    def __init__(self, in_dim, out_dim):
        super(g_block,self).__init__(
            normalize=normalize(),
            dc1=EqualizedConv2d(in_dim, out_dim, 3, stride=1, pad=1),
            dc2=EqualizedConv2d(out_dim, out_dim, 3, stride=1, pad=1),
            to_RGB=EqualizedConv2d(out_dim, 3, 1, stride=1, pad=0),
        )
    def __call__(self,x,to_rgb=False):
        h = F.unpooling_2d(x, 2, 2, 0, outsize=(x.shape[2]*2, x.shape[3]*2))
        h = F.leaky_relu(self.dc1(h))
        h = F.leaky_relu(self.dc2(h))
        if to_rgb:
            #h = F.tanh(self.to_RGB(h))
            h = self.to_RGB(h,False)
        return h

class normalize(chainer.Chain):
    def __init__(self):
        super(normalize,self).__init__(
        )
    def __call__(self,x):
        eps = 1e-8
        mean=F.mean(x ** 2,axis=1,keepdims=True)
        mean=(mean + eps) ** 0.5
        mean = F.broadcast_to(mean, (x.shape))
        h = x / mean
        return h

class generator(chainer.Chain):
    def __init__(self, width, height, z_size):
        dims = [64, 64, 64, 64, 64, 64, 64, 64]
        super(generator,self).__init__(
            normalize=normalize(),
            c0 = EqualizedConv2d(z_size, dims[0], 4, stride=1, pad=3),
            c1 = EqualizedConv2d(dims[0], dims[0], 3, stride=1, pad=1),

            b0=g_block(dims[0],dims[1]),
            b1=g_block(dims[1],dims[2]),
            b2=g_block(dims[2],dims[3]),
            b3=g_block(dims[3],dims[4]),
            b4=g_block(dims[4],dims[5]),
            b5=g_block(dims[5],dims[6]),
            b6=g_block(dims[6],dims[7]),

            to_RGB=EqualizedConv2d(dims[0], 3, 1, stride=1, pad=0),

        )

    def __call__(self, noise, tag,depth,alpha):
        h = F.reshape(noise,(len(noise),-1,1,1))
        h = F.leaky_relu(self.c0(h))
        h = F.leaky_relu(self.c1(h))

        for i in range(depth-1):
            h = getattr(self, "b%d" % i)(h)

        if 0<depth and alpha < 1:
            h2 = getattr(self, "b%d" % (depth-1))(h,True)
            if depth==1:
                #h = F.tanh(self.to_RGB(h))
                h = self.to_RGB(h,False)
            else:
                h = getattr(self, "b%d" % (depth-2)).to_RGB(h,False)
            h=F.unpooling_2d(h, 2, 2, 0, outsize=(2*h.shape[2], 2*h.shape[3]))

            h=h*(1.0-alpha)+h2*alpha
        elif depth == 0:
            #h = F.tanh(self.to_RGB(h))
            h = self.to_RGB(h,False)
        else:
            h = getattr(self, "b%d" % (depth-1))(h,True)

return h

convert code:

z_size = 128
NUMBER_OF_TAG = 1539
g = generator(512, 512, z_size)
#serializers.load_npz("generator.model", g)
x = chainer.Variable(np.zeros((1,z_size), dtype=np.float32))
y = g(x,np.zeros(NUMBER_OF_TAG),5,1)

from webdnn.frontend.chainer import ChainerConverter
graph = ChainerConverter().convert([x], [y])

from webdnn.backend import generate_descriptor

exec_info = generate_descriptor("webgl", graph)  # also "webassembly", "webgl", "fallback" are available.
exec_info.save("./output")

javascript code:

function generate() {
	// Generate
	let x = runner.inputs[0], y = runner.outputs[0], noise;

	noise_size = 128;
	noise = new Float32Array(noise_size);
	for(let i = 0; i < noise_size; i++) {
		noise[i] = Math.random() * 2 - 1;
	}

/*
	numberOfTag = 1539;
	var tags = new Float32Array(numberOfTag);
	for(let i = 0; i < 4; i++){
		tags[Math.floor(Math.random() * 1539) - 1] = 1;
	}
/*
	for(let i = 0; i < 1539; i++){
		tags[i] = Math.random();
	}
*/

	x.set(noise);
	console.log(x);

	runner.run().then(function() {
		console.log('Before', y); // debug
		let y_typed_array = y.toActual();
		console.log('After', y_typed_array);
		
		if (y_typed_array.includes(NaN)) {
			console.timeEnd();
			alert('Failed the generated.');
		} else {
			console.log('finished');
			generatedData = y_typed_array;
			drawGeneratedImage(generatedData);
		}a
    });
}

These codes don't work on Windows 7 but they work correctly on MacBook Air,my friend's PC.

@milhidaka milhidaka self-assigned this Aug 3, 2018
@milhidaka
Copy link
Member

I investigated the problem on Windows7 IE11 and Chrome and confirmed that IE produces NaN.
I found a workaround:

OPTIMIZE=0 python convert.py

There seems to be optimization bug in webdnn.
When optimization is enabled (by default), weight_webgl_4096.bin has 29521024 bytes. It is far larger than original chainer model (2549265 bytes in my experiment).
When optimization is disabled, the size decreased to 2150156 bytes, which is reasonable.
This model works on IE, at least looking at output value range.

To investigate more in this issue, please upload your generator.model if possible.

@milhidaka
Copy link
Member

I investigated what optimization caused the problem.
REPLACE_SCALAR_OPERATOR=0 python convert.py produces best optimized result which works with IE. REPLACE_SCALAR_OPERATOR replaces scalar operation such as x + 1 into x + [[1, 1, 1], [1, 1, 1]] (when x's shape is 2x3.) In this model, this optimization is applied to many large x, so parameter file size became too large. It may result in exceeding some internal limit of IE.

I found another problem when webassembly backend is used.
In webassembly backend, output value range is wrong, e.g. +5000 to -5000.
OPTIMIZE_MEMORY_ALLOCATION=0 resolves the problem. Note that OPTIMIZE_MEMORY_ALLOCATION does not affect webgl.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants