Converting a fully connected neural network with variable number of hidden layers from tensorflow to pytorch
I recently started learning pytorch and I am trying to convert a part of a large script including coding a MLP with variable number of hidden layers from Tensorflow to pytorch.
import tensorflow as tf

### Base neural network                                                                                                                  
def init_mlp(layer_sizes, std=.01, bias_init=0.):
    params = {'w':[], 'b':[]}
    for n_in, n_out in zip(layer_sizes[:-1], layer_sizes[1:]):
        params['w'].append(tf.Variable(tf.random_normal([n_in, n_out], stddev=std)))
        params['b'].append(tf.Variable(tf.mul(bias_init, tf.ones([n_out,]))))
    return params


def mlp(X, params):
    h = [X]
    for w,b in zip(params['w'][:-1], params['b'][:-1]):
        h.append( tf.nn.relu( tf.matmul(h[-1], w) + b ) )
        #h.append( tf.nn.tanh( tf.matmul(h[-1], w) + b ) ) 
    return tf.matmul(h[-1], params['w'][-1]) + params['b'][-1]


def compute_nll(x, x_recon_linear):
    return tf.reduce_sum(tf.nn.sigmoid_cross_entropy_with_logits(x_recon_linear, x), reduction_indices=1, keep_dims=True)


def gauss_cross_entropy(mean_post, std_post, mean_prior, std_prior):
    d = (mean_post - mean_prior)
    d = tf.mul(d,d)
    return tf.reduce_sum(-tf.div(d + tf.mul(std_post,std_post),(2.*std_prior*std_prior)) - tf.log(std_prior*2.506628), reduction_indices=1, keep_dims=True)


how could I write down similarly weights and bias variables and attach them in each hidden layer in pytorch?
how could I convert gauss_cross_entropy and compute_nll
functions as well (finding equivalent syntax)?

Are these two codes compatible?
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as func
from torch.distributions import Normal, Categorical, Independent
from copy import 

device = &quot;cpu&quot;
if torch.cuda.is_available():
    device = &quot;cuda:0&quot;
    if torch.cuda.device_count() &gt; 1:
        net = nn.DataParallel(net)
net.to(device)
def init_mlp(layer_sizes, std=.01, bias_init=0.):
    params = {'w':[], 'b':[]}
    for n_in, n_out in zip(layer_sizes[:-1], layer_sizes[1:]):
        params['w'].append(torch.tensor(Normal([n_in, n_out], torch.tensor([std])) ,requires_grad=True))
        params['b'].append(torch.tensor(torch.mul(bias_init, torch.ones([n_out,])),requires_grad=True))
    return params


def mlp(X, params):
    h = [X]
    for w,b in zip(params['w'][:-1], params['b'][:-1]):
        h.append( torch.nn.ReLU( tf.matmul(h[-1], w) + b ) )
    return torch.matmul(h[-1], params['w'][-1]) + params['b'][-1]

def compute_nll(x, x_recon_linear):
    return torch.sum(func.binary_cross_entropy_with_logits(x_recon_linear, x), reduction_indices=1, keep_dims=True)


def gauss_cross_entropy(mu_post, sigma_post, mu_prior, sigma_prior):
    d = (mu_post - mu_prior)
    d = torch.mul(d,d)
    return torch.sum(-torch.div(d + torch.mul(sigma_post,sigma_post),(2.*sigma_prior*sigma_prior)) - torch.log(sigma_prior*2.506628), reduction_indices=1, keep_dims=True)


What is the substitute function for tf.placeholder in pytorch? For instance here:
class VAE(object):

 def __init__(self, hyperParams):

     self.X = tf.placeholder(&quot;float&quot;, [None, hyperParams['input_d']])
     self.prior = hyperParams['prior']
     self.K = hyperParams['K']

     self.encoder_params = self.init_encoder(hyperParams)
     self.decoder_params = self.init_decoder(hyperParams)



and also how should I change tf.shape in this line: tf.random_normal(tf.shape(self.sigma[-1]))




How could I write down similar weights and bias variables and attach them in each hidden layer in PyTorch?

An easier way to define those is to create a list containing the params as (weight, bias) tuples:
def init_mlp(layer_sizes, std=.01, bias_init=0.):
    params = []
    for n_in, n_out in zip(layer_sizes[:-1], layer_sizes[1:]):
        params.append([
            nn.init.normal_(torch.empty(n_in, n_out)).requires_grad_(True),
            torch.empty(n_out).fill_(bias_init).requires_grad_(True)])
    return params

Above I define my parameters as 'empty' (created with uninitialized data) tensors with torch.empty. I have used in-place functions such as nn.init.normal_ (there are many others available) and torch.Tensor.fill_ to fill the tensor with an arbitrary value (maybe it is .mul_(bias_init) you are looking for, based on your TensorFlow sample?).
For the inference code, you don't actually need to store the intermediate layer results:
def mlp(x, params):
    for i, (W, b) in enumerate(params):
        x = x@W + b
        if i &lt; len(params) - 1:
            x = torch.relu(x)
    return x







How could I convert gauss_cross_entropy and compute_nll functions as well (finding equivalent syntax)?

You can use PyTorch functions and mathematical operators to define your logic. For compute_loss you were using the built-in, which actually does not require summation after it, by default the losses of the batch elements are averaged.
def compute_loss(y_pred, y_true):
    return F.binary_cross_entropy_with_logits(y_pred, y_true)







What is the substitute function for tf.placeholder in Pytorch?

You don't have placeholders in PyTorch, you compute your outputs explicitly using PyTorch operators, then you should be able to backpropagate through those operators and get the gradients for each parameter.






How should I change tf.shape in this line: tf.random_normal(tf.shape(self.sigma[-1]))

Function tf.shape returns the shape of the tensor, in PyTorch you call torch.Tensor.shape or by calling torch.Tensor.size: i.e. self.sigma[-1].shape or self.sigma[-1].size().


