Murat Aykanat
Murat Aykanat

Reputation: 1708

A variable changing without any apparent reason

In my doctorate classes we were assigned to do a single neuron neural network that calculates OR, AND , XOR operations with a Python script. I have a very weird error that drives me crazy in my code.

First of all I have a Vector class:

class Vector3D:                                                                            # Defines the Vector3D class
    def __init__(self,bias,x,y):                                                           # Defines the variables for the Vector3D class
        self.bias = bias
        self.x = x
        self.y = y
    def __add__(self,other):                                                               # Defines the built-in "add" ("+") operation for Vector3D
        return Vector3D(self.bias+other.bias,self.x+other.x,self.y+other.y)
    def __mul__(self,other):                                                               # Defines the built-in "multipication" ("*") operation for Vector3D
        if(isinstance(other,int)):
            return Vector3D(self.bias * other, self.x * other, self.y * other)
        else:
            return Vector3D(self.bias * other.bias, self.x * other.x, self.y * other.y)
    def __str__(self):                                                                     # Defines the built-in string return value for Vector3D
        return "Vector(%f,%f,%f)" % (self.bias, self.x, self.y)
    def UpdateWeights(self,eta, targetOutput, currentOutput, valueX, valueY, valueBias):   # Function for updating the weights
        self.bias = self.bias + (eta * (targetOutput - currentOutput) * valueBias)
        self.x = self.x + (eta * (targetOutput - currentOutput) * valueX)
        self.y = self.y + (eta * (targetOutput - currentOutput) * valueY)
        return Vector3D(self.bias,self.x, self.y)
    def getX(self):                                                                        # Function for getting the x value of a vector
        return self.x
    def getY(self):                                                                        # Function for getting the y value of a vector
        return self.y
    def getBias(self):                                                                     # Function for getting the bias value of a vector
        return self.bias

Secondly, I have a neuron class:

class Neuron:                                                                                   # Defines the Neuron class
    def __init__(self, dataTable, eta, theta, targetArrayOr, targetArrayAnd, targetArrayXor):    # Function for defining the variables for initialization
        self.dataTable = dataTable

        self.eta = eta
        self.theta = theta

        self.targetArrayOr = targetArrayOr
        self.targetArrayAnd = targetArrayAnd
        self.targetArrayXor = targetArrayXor

        self.wVbias = random.uniform(-0.2, 0.2)
        self.wVX = random.uniform(-0.2, 0.2)
        self.wVY = random.uniform(-0.2, 0.2)
        self.weightVector = Vector3D(self.wVbias,self.wVX,self.wVY)

        self.weightVectorOr = Vector3D(0,0,0)
        self.weightVectorAnd = Vector3D(0,0,0)
        self.weightVectorXor = Vector3D(0,0,0)

    def TrainForOr(self) :                                                                       # Function training the weight vector for OR operation
        iteration = 0                                                                            # Number of iterations
        check = 0                                                                                # Initial value of the while loop
        finalCheck = 200                                                                         # Final value of the while loop
        targetReached = False                                                                    # Boolean variable for if the target is reached
        rowNb = 0                                                                                # Initial value of the index number in the data table
        weightVector = self.weightVector                                                # Initial weight vector
        print(self.weightVector)
        while check < finalCheck :                                                               # Makes sure that the entire loop runs 200 times for accuracy
            while rowNb < len(self.dataTable) :                                                  # Makes sure every row is iterated
                while targetReached == False:
                    D1dotW = DotProduct(self.dataTable[rowNb],weightVector)                      # Dot product of the input vector and the weight vector
                    if(D1dotW > self.theta):
                        currentOutput = 1
                    elif(D1dotW <= self.theta):
                        currentOutput = 0
                    if(currentOutput == self.targetArrayOr[rowNb]):
                        targetReached = True
                    else:
                        iteration = iteration + 1
                        print(self.weightVector)
                        weightVector = weightVector.UpdateWeights(self.eta,self.targetArrayOr[rowNb], currentOutput, self.dataTable[rowNb].getX(), self.dataTable[rowNb].getY(), self.dataTable[rowNb].getBias())
                        print(self.weightVector)
                        targetReached = False


                targetReached = False
                rowNb = rowNb + 1

            check = check + 1
            rowNb = 0
        self.weightVectorOr = weightVector                                                       # Sets the OR weight vector
        return "OR - Final weight vector is " + str(weightVector) + " " + "("+ str(iteration) + " iteration(s) )"

I also have other methods for AND and XOR but they are the same with above with minor changes.

Now the above code "works" as the "error" is very minor and does not change the end result. But I want to understand why it happens.

When I run the above snippet along with the rest of the GUI code etc., I get the console result:

Vector(-0.051856,-0.099352,0.079270)
Vector(-0.051856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)
Vector(-0.001856,-0.099352,0.079270)

This means that the initial self.weightVector is changing at the line:

weightVector = weightVector.UpdateWeights(self.eta,self.targetArrayOr[rowNb], currentOutput, self.dataTable[rowNb].getX(), self.dataTable[rowNb].getY(), self.dataTable[rowNb].getBias())

I don't understand this because I am not changing self.weightVector in any way in the UpdateWeights method.

If anyone can explain why this happens it will be appreciated.

Upvotes: 1

Views: 112

Answers (2)

BartoszKP
BartoszKP

Reputation: 35911

Look at this method:

def UpdateWeights(self,eta, targetOutput, currentOutput, valueX, valueY, valueBias):
    self.bias = self.bias + (eta * (targetOutput - currentOutput) * valueBias)
    self.x = self.x + (eta * (targetOutput - currentOutput) * valueX)
    self.y = self.y + (eta * (targetOutput - currentOutput) * valueY)
    return Vector3D(self.bias,self.x, self.y)

It not only returns a new Vector3D but also modifies itself (self). And earlier you set:

weightVector = self.weightVector

So calling weightVector.UpdateWeights will cause altering the self of the same object.

Upvotes: 3

Dietrich Epp
Dietrich Epp

Reputation: 213768

The UpdateWeights() method modifies the vector. This is why the vector changes when you call UpdateWeights().

Here is a fixed version:

def UpdateWeights(self, eta, targetOutput, currentOutput,
                  valueX, valueY, valueBias):
    """Returns a new vector with updated weights."""
    bias = self.bias + (eta * (targetOutput - currentOutput) * valueBias)
    x = self.x + (eta * (targetOutput - currentOutput) * valueX)
    y = self.y + (eta * (targetOutput - currentOutput) * valueY)
    return Vector3D(bias, x, y)

P.S. Also note that documentation should go in docstrings, not comments.

Upvotes: 1

Related Questions