Python Forum

Full Version: Class function is not running at all even though it is clearly run in the code
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I'm very confused as to why my code won't run properly. Below I put the last lines of the main file which just runs some functions of the class. I put print statements around the line of code that runs the function and a print statement on the first line of the function, but only the two print statements outside the function execute. I put the full code at the bottom if you want to try running it yourself. Also if you were wondering, the reason I'm not using a library like TorchPy is because I wanted to try making some Machine learning code by myself from scratch. Thanks in advance for any suggestions on why this may be happening.

The bit of code at the end of main.py:
data = DataHandler()
AI = AIClass()
for _ in range(10):
    batch = list(data.get_batch())
    for info in batch:
        print("Execute Training function")
        AI.train_neurons(info[0], info[1])
        print('Training finished')
The class:
class AIClass:
    def __init__(self):
        .
        .

    def train_neurons(self, image, number, test=False):
        print("Training")
        .
        .
Full code:
from data_handler import DataHandler
import random, numpy


def sigmoid(number):
    return 1 / (1 + numpy.exp(-number))


def get_desired_output(number):
    output = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    output[number] = 1
    return output


def position_or_negative(number):
    if number < 0:
        return -1
    elif number > 0:
        return 1
    return 0


class AIClass:
    def __init__(self):
        self.nodes = [{}, {}, {}]
        self.weight_changes = [{}, {}, {}]
        self.nodeOutput = [{}, {}]
        self.costNodes = [{}, {}]
        self.output = []
        self.decay = 1

        # self.nodes setup
        for node in range(16):
            self.nodes[0].update({node: {}})
            self.weight_changes[0].update({node: {}})
            self.costNodes[0].update({node: []})
            self.nodes[0][node].update({'bias': 0})
            for pixel in range(28*28):
                self.nodes[0][node].update({pixel: random.uniform(-1, 1)})
                self.weight_changes[0][node].update({pixel: []})
        for node in range(16):
            self.nodes[1].update({node: {}})
            self.weight_changes[1].update({node: {}})
            self.costNodes[1].update({node: []})
            self.nodes[1][node].update({'bias': 0})
            for previousNode in range(16):
                self.nodes[1][node].update({previousNode: random.uniform(-1, 1)})
                self.weight_changes[1][node].update({previousNode: []})
        for node in range(10):
            self.nodes[2].update({node: {}})
            self.weight_changes[2].update({node: {}})
            self.nodes[2][node].update({'bias': 0})
            for previousNode in range(16):
                self.nodes[2][node].update({previousNode: random.uniform(-1, 1)})
                self.weight_changes[2][node].update({previousNode: []})

        # self.nodeOutput setup
        for node in range(16):
            self.nodeOutput[0].update({node: 0})
        for node in range(16):
            self.nodeOutput[1].update({node: 0})

    def train_neurons(self, image, number, test=False):
        print("Training")
        if not test:
            self.output.append([])
        for node in range(16):
            nodesum = 0
            for pixel in range(28*28):
                nodesum += image[pixel]*self.nodes[0][node][pixel]
            if not test:
                self.nodeOutput[0][node] = sigmoid(nodesum)
        for node in range(16):
            nodesum = 0
            for previousNode in range(16):
                nodesum += self.nodeOutput[0][node] * self.nodes[1][node][previousNode]
            if not test:
                self.nodeOutput[1][node] = sigmoid(nodesum)
        for node in range(10):
            nodesum = 0
            for previousNode in range(16):
                nodesum += self.nodeOutput[1][node] * self.nodes[2][node][previousNode]
            if not test:
                self.output[len(self.output)-1].append(sigmoid(nodesum))
            else:
                yield sigmoid(nodesum)
        if not test:
            self.backprop(number, image)

    def backprop(self, number, image):
        desiredOutput = get_desired_output(number)
        for node in range(10):
            output = self.output[len(self.output) - 1][node]
            cost = (output - desiredOutput[node])**2
            direction = position_or_negative(desiredOutput[node] - output)
            for affectNode in range(16):
                value = self.nodeOutput[1][affectNode]
                # print(f"Node: {node}; Output: {output}; Desired Output: {desiredOutput[node]}\nCost: {cost}; Direction: {direction}; Value: {value}\n__________________")
                self.costNodes[1][affectNode].append(value*direction)
                self.weight_changes[2][node][affectNode].append(value * direction * cost * self.decay)
        for node in range(16):
            cost = sum(self.costNodes[1][node])/len(self.costNodes[1][node])
            self.costNodes[1][node] = []
            for affectNode in range(16):
                value = self.nodeOutput[0][affectNode]
                self.costNodes[0][affectNode].append(value * position_or_negative(cost))
                self.weight_changes[1][node][affectNode].append(value * cost * self.decay)
        for node in range(16):
            cost = sum(self.costNodes[0][node]) / len(self.costNodes[0][node])
            self.costNodes[0][node] = []
            for pixel in range(28*28):
                value = image[pixel]
                self.weight_changes[0][node][pixel].append(value * cost * self.decay)

    def update_weights_and_biases(self):
        for row in range(len(self.weight_changes)):
            for node in range(len(self.weight_changes[row])):
                for weight in range(len(self.weight_changes[row][node])):
                    weight_changes = [weight + 1 for weight in self.weight_changes[row][node][weight]]
                    self.output = []
                    self.nodes[row][node][weight] *= sum(weight_changes)/len(weight_changes)
                    self.weight_changes[row][node][weight] = []
        self.decay *= 9/10

    def test(self, data):
        batch = data.get_batch()
        for info in batch:
            results = list(self.train_neurons(info[0], info[1], True))
            print("Test Results")
            print(f"Prediction:    {results}")
            print(f"Wanted output: {get_desired_output(info[1])}")

data = DataHandler()
AI = AIClass()
for _ in range(10):
    batch = list(data.get_batch())
    for info in batch:
        print("Execute Training function")
        AI.train_neurons(info[0], info[1])
        print('Training finished')
    AI.update_weights_and_biases()

AI.test(data)
Code for data_handler.py
from mnist import MNIST


class DataHandler:
    def __init__(self):
        data = MNIST('MNIST_DATA')  # You need a folder named "MNIST_DATA" inside the same directory as main.py, and it needs to have the files train-images-idx3-ubyte and train-labels-idx1-ubyte from http://yann.lecun.com/exdb/mnist/ just download the first two files listed and unpack them with some program into this folder.
        self.images, self.labels = data.load_training()
        self.index = -1

    def get_batch(self):
        for number in range(10):
            self.index += 1
            yield self.images[self.index], number
Because the method yields, it returns a generator object. The code isn't run until you get data from the generator.

Because you don't assign the return value, the generator is never accessed. Maybe change

        print("Execute Training function")
        i = AI.train_neurons(info[0], info[1])
        print(next(i))
        print('Training finished')
Oh I never knew that generator objects worked that way. I defined a list, appended all the values into that list, and then simply returned it, rather than trying to simplify my code a little with a yield. Thanks for the help!