示例#1
0
class BN(object):
    '''A BN is a bidirectional NeuralNet. It is equivelant to two opposite direction feed forward nets.'''
    def __init__(self, layers, up_weights=None, down_weights=None):
        '''Initializes a BN from the layers given'''
        self.numlayers = len(layers)

        down_layers = [layer.__class__.from_layer(layer) for layer in layers[::-1]]#Copy list so that upnet and downnet layers are different objects
        self.upnet = NeuralNet(layers, up_weights)
        self.downnet = NeuralNet(down_layers, down_weights)

    @classmethod
    def from_rbms(cls, rbms):
        '''Initializes a BN from a list of RBMS. NOTE: the down weights and upweights are tied.
        Modifying one, modifies the other. To untie, call the __untie__ method.'''
        layers = []
        # First layer of dbn is the visible layer of the bottom rbm
        layers.append(rbms[0].get_vislayer())
        # Keep all hidden layers
        for rbm in rbms:
            layers.append(rbm.get_hidlayer())

        up_weights = [rbm.get_vishid() for rbm in rbms]
        down_weights = [rbm.get_vishid().transpose() for rbm in rbms[::-1]]

        return cls(layers, up_weights, down_weights)

    def bottom_up(self, data):
        '''Expects data to be probabilities'''
        self.upnet.layers[0].probs = data
        self.upnet.layers[0].activities = sample_binary_stochastic(data)
        return self.upnet.forward_pass(data, 1)

    def top_down(self, data):
        '''Expects data to be binary'''
        self.downnet.layers[0].activities = data
        return self.downnet.forward_pass(data, 1)

    def top_down_prob(self, data):
        '''Expects data to be binary'''
        self.downnet.layers[0].activities = data
        last = len(self.downnet.weights)
        data = dot(data, self.downnet.weights[0])

        for i in range(1, self.downnet.numlayers):
            self.downnet.layers[i].process(data)
            data = self.downnet.layers[i].probs
            if i < last:
                data = dot(data, self.downnet.weights[i])
        return [layer.activities for layer in self.downnet.layers]


    def __untie_weights__(self):
        '''This is an ugly step, and is only necessary when the db is initialized from RBMs.
        It unties the recognition weights from the generative ones.'''
        numweights = self.numlayers - 1
        for i in range(numweights):
            self.upnet.weights[i] = self.upnet.weights[i].copy()

    def wake_phase(self, data):
        '''The first step of wake-sleep and contrastive wake-sleep. Returns wake_deltas, a list of matrices by which the
        the weights of the down net should be adjusted. Also returns wake_bias_deltas, wake_visbias_delta, and hidden states 
        of top layer. Assumes DBN layers are binary stochastic layers.'''
        #Get the states and probabilities of every layer after doing a bottom-up pass
        hid_states = self.bottom_up(data)
        hid_probs = []
        for layer in self.upnet.layers:
            hid_probs.append(layer.probs)

        wake_deltas = []
        wake_bias_deltas = []
        #Bias deltas for the generative visible units
        wake_visbias_delta = (data - self.upnet.layers[0].probs).sum(0)/data.shape[0]
        #Iterate over each layer excluding bottom layer
        for i in range(self.upnet.numlayers - 1):
            upper_state = hid_states[i+1]
            upper_activity = hid_probs[i+1]
            lower_state = hid_states[i]
            lower_activity = hid_probs[i]

            delta = dot(upper_state.transpose(), (lower_state - lower_activity))/data.shape[0]
            #Get bias deltas as well to update hidden biases in downnet
            delta_bias = array([(upper_state - upper_activity).sum(0)/data.shape[0]])
            wake_deltas.insert(0,delta)
            wake_bias_deltas.insert(0, delta_bias)

        return wake_deltas, wake_bias_deltas, wake_visbias_delta, hid_states[-1]

    def sleep_phase(self, data):
        '''The last step of wake-sleep and contrastive wake-sleep. Returns sleep_deltas, a list of matrices by which the
        the weights of the up net should be adjusted. Also returns sleep_bias_deltas. Assumes DBN layers are binary stochastic layers.'''
        #Get the states and probabilities of every layer after doing a top-down pass
        hid_states = self.top_down(data)
        hid_probs = []
        for layer in self.downnet.layers:
            hid_probs.append(layer.probs)

        sleep_deltas = []
        sleep_bias_deltas = []
        #Iterate over each layer excluding top layer
        for i in range(self.downnet.numlayers -1):
            lower_state = hid_states[i+1]
            upper_state = hid_states[i]
            upper_activity = hid_probs[i]

            delta = dot(lower_state.transpose(), (upper_state - upper_activity))/data.shape[0]
            #Get bias deltas to update hidden biases in upnet
            delta_bias = array([(upper_state - upper_activity).sum(0)/data.shape[0]])
            sleep_deltas.insert(0,delta)
            sleep_bias_deltas.insert(0, delta_bias)

        return sleep_deltas, sleep_bias_deltas

    def wake_sleep(self, data, learning_rate):
        '''Combines wake and sleep phases'''

        downnet_deltas, downnet_hidbias_deltas, downnet_visbias_delta, top_state = self.wake_phase(data)
        upnet_deltas, upnet_bias_deltas = self.sleep_phase(top_state) #The top state is the input for the top-down pass
        recons_error = square(data - self.upnet.layers[0].probs).sum()
        print 'BN Reconstruction Error', recons_error

        self.downnet.layers[-1].bias += learning_rate*downnet_visbias_delta
        for i in range(len(downnet_deltas)):
            self.downnet.weights[i] += learning_rate*downnet_deltas[i]
            self.downnet.layers[i+1].bias += learning_rate*downnet_hidbias_deltas[i]

        for i in range(len(upnet_deltas)):
            self.upnet.weights[i] += learning_rate*upnet_deltas[i]
            self.upnet.layers[i+1].bias += learning_rate*upnet_bias_deltas[i]
        return recons_error