Проблема реализации Backprop - PullRequest
4 голосов
/ 03 ноября 2010

Что я должен делать. У меня есть черно-белое изображение (100x100px):

alt text

Я должен обучить обратное распространение нейронной сети с этим изображением. Входными данными являются координаты x, y изображения (от 0 до 99), а выходными данными являются 1 (белый цвет) или 0 (черный цвет).

Как только сеть узнала, я хотел бы, чтобы она воспроизводила изображение на основе его веса и получала максимально приближенное изображение к оригиналу.

Вот моя реализация backprop:

import os
import math
import Image
import random
from random import sample

#------------------------------ class definitions

class Weight:
    def __init__(self, fromNeuron, toNeuron):
        self.value = random.uniform(-0.5, 0.5)
        self.fromNeuron = fromNeuron
        self.toNeuron = toNeuron
        fromNeuron.outputWeights.append(self)
        toNeuron.inputWeights.append(self)
        self.delta = 0.0 # delta value, this will accumulate and after each training cycle used to adjust the weight value

    def calculateDelta(self, network):
        self.delta += self.fromNeuron.value * self.toNeuron.error

class Neuron:
    def __init__(self):
        self.value = 0.0        # the output
        self.idealValue = 0.0   # the ideal output
        self.error = 0.0        # error between output and ideal output
        self.inputWeights = []
        self.outputWeights = []

    def activate(self, network):
        x = 0.0;
        for weight in self.inputWeights:
            x += weight.value * weight.fromNeuron.value
        # sigmoid function
        if x < -320:
            self.value = 0
        elif x > 320:
            self.value = 1
        else:
            self.value = 1 / (1 + math.exp(-x))

class Layer:
    def __init__(self, neurons):
        self.neurons = neurons

    def activate(self, network):
        for neuron in self.neurons:
            neuron.activate(network)

class Network:
    def __init__(self, layers, learningRate):
        self.layers = layers
        self.learningRate = learningRate # the rate at which the network learns
        self.weights = []
        for hiddenNeuron in self.layers[1].neurons:
            for inputNeuron in self.layers[0].neurons:
                self.weights.append(Weight(inputNeuron, hiddenNeuron))
            for outputNeuron in self.layers[2].neurons:
                self.weights.append(Weight(hiddenNeuron, outputNeuron))

    def setInputs(self, inputs):
        self.layers[0].neurons[0].value = float(inputs[0])
        self.layers[0].neurons[1].value = float(inputs[1])

    def setExpectedOutputs(self, expectedOutputs):
        self.layers[2].neurons[0].idealValue = expectedOutputs[0]

    def calculateOutputs(self, expectedOutputs):
        self.setExpectedOutputs(expectedOutputs)
        self.layers[1].activate(self) # activation function for hidden layer
        self.layers[2].activate(self) # activation function for output layer        

    def calculateOutputErrors(self):
        for neuron in self.layers[2].neurons:
            neuron.error = (neuron.idealValue - neuron.value) * neuron.value * (1 - neuron.value)

    def calculateHiddenErrors(self):
        for neuron in self.layers[1].neurons:
            error = 0.0
            for weight in neuron.outputWeights:
                error += weight.toNeuron.error * weight.value
            neuron.error = error * neuron.value * (1 - neuron.value)

    def calculateDeltas(self):
        for weight in self.weights:
            weight.calculateDelta(self)

    def train(self, inputs, expectedOutputs):
        self.setInputs(inputs)
        self.calculateOutputs(expectedOutputs)
        self.calculateOutputErrors()
        self.calculateHiddenErrors()
        self.calculateDeltas()

    def learn(self):
        for weight in self.weights:
            weight.value += self.learningRate * weight.delta

    def calculateSingleOutput(self, inputs):
        self.setInputs(inputs)
        self.layers[1].activate(self)
        self.layers[2].activate(self)
        #return round(self.layers[2].neurons[0].value, 0)
        return self.layers[2].neurons[0].value


#------------------------------ initialize objects etc

inputLayer = Layer([Neuron() for n in range(2)])
hiddenLayer = Layer([Neuron() for n in range(10)])
outputLayer = Layer([Neuron() for n in range(1)])

learningRate = 0.4

network = Network([inputLayer, hiddenLayer, outputLayer], learningRate)


# let's get the training set
os.chdir("D:/stuff")
image = Image.open("backprop-input.gif")
pixels = image.load()
bbox = image.getbbox()
width = 5#bbox[2] # image width
height = 5#bbox[3] # image height

trainingInputs = []
trainingOutputs = []
b = w = 0
for x in range(0, width):
    for y in range(0, height):
        if (0, 0, 0, 255) == pixels[x, y]:
            color = 0
            b += 1
        elif (255, 255, 255, 255) == pixels[x, y]:
            color = 1
            w += 1
        trainingInputs.append([float(x), float(y)])
        trainingOutputs.append([float(color)])

print "\nOriginal image ... Black:"+str(b)+" White:"+str(w)+"\n"

#------------------------------ let's train

for i in range(500):
    for j in range(len(trainingOutputs)):
        network.train(trainingInputs[j], trainingOutputs[j])
        network.learn()
    for w in network.weights:
        w.delta = 0.0

#------------------------------ let's check

b = w = 0
for x in range(0, width):
    for y in range(0, height):
        out = network.calculateSingleOutput([float(x), float(y)])
        if 0.0 == round(out):
            color = (0, 0, 0, 255)
            b += 1
        elif 1.0 == round(out):
            color = (255, 255, 255, 255)
            w += 1
        pixels[x, y] = color
        #print out

print "\nAfter learning the network thinks ... Black:"+str(b)+" White:"+str(w)+"\n"

Очевидно, что есть некоторые проблемы с моей реализацией. Код выше возвращает:

Исходное изображение ... Черный: 21 Белый: 4

После обучения сеть думает ... Черный: 25 Белый: 0

То же самое происходит, если я пытаюсь использовать тренировочный набор большего размера (для тестирования я тестирую всего 25 пикселей от изображения выше). Возвращает, что все пиксели должны быть черными после обучения.

Теперь, если я вместо этого использую набор для ручной тренировки:

trainingInputs = [
    [0.0,0.0],
    [1.0,0.0],
    [2.0,0.0],
    [0.0,1.0],
    [1.0,1.0],
    [2.0,1.0],
    [0.0,2.0],
    [1.0,2.0],
    [2.0,2.0]
]
trainingOutputs = [
    [0.0],
    [1.0],
    [1.0],
    [0.0],
    [1.0],
    [0.0],
    [0.0],
    [0.0],
    [1.0]
]

#------------------------------ let's train

for i in range(500):
    for j in range(len(trainingOutputs)):
        network.train(trainingInputs[j], trainingOutputs[j])
        network.learn()
    for w in network.weights:
        w.delta = 0.0

#------------------------------ let's check

for inputs in trainingInputs:
    print network.calculateSingleOutput(inputs)

Например, вывод:

0.0330125791296   # this should be 0, OK
0.953539182136    # this should be 1, OK
0.971854575477    # this should be 1, OK
0.00046146137467  # this should be 0, OK
0.896699762781    # this should be 1, OK
0.112909223162    # this should be 0, OK
0.00034058462280  # this should be 0, OK
0.0929886299643   # this should be 0, OK
0.940489647869    # this should be 1, OK

Другими словами, сеть угадала все пиксели вправо (как черные, так и белые). Почему говорится, что все пиксели должны быть черными, если я использую фактические пиксели из изображения вместо жестко заданного обучающего набора, как указано выше?

Я попытался изменить количество нейронов в скрытых слоях (до 100 нейронов), но безуспешно.

Это домашнее задание.

Это также продолжение моего предыдущего вопроса о backprop.

1 Ответ

5 голосов
/ 03 ноября 2010

Это было давно, но я получил свою степень в этом деле, поэтому я надеюсь, что некоторые из них застряли.

Из того, что я могу сказать, вы слишком сильно перегружаете свои нейроны среднего слояс входным набором.То есть ваш входной набор состоит из 10000 дискретных входных значений (100 x 100 x);Вы пытаетесь закодировать эти 10000 значений в 10 нейронов.Этот уровень кодирования сложен (я подозреваю, что это возможно, но, конечно, сложно);по крайней мере, вам понадобится МНОГО тренировок (более 500 пробежек), чтобы разумно воспроизвести его.Даже при 100 нейронах для среднего слоя вы смотрите на относительно плотный уровень сжатия (от 100 пикселей до 1 нейрона).

Что делать с этими проблемами;ну, это сложно.Вы можете резко увеличить количество средних нейронов, и вы получите разумный эффект, но, конечно, это займет много времени для обучения.Тем не менее, я думаю, что может быть другое решение;если возможно, вы можете рассмотреть возможность использования полярных координат вместо декартовых координат для ввода;быстрое сглаживание входного паттерна указывает на высокий уровень симметрии, и фактически вы будете смотреть на линейный паттерн с повторяющейся предсказуемой деформацией вдоль угловой координаты, которая, по-видимому, хорошо кодируется в небольшом количестве нейронов среднего слоя.1005 *

Этот материал хитрый;поиск общего решения для кодирования паттернов (как делает ваше оригинальное решение) очень сложен и обычно (даже с большим количеством нейронов среднего уровня) может потребовать много тренировочных проходов;с другой стороны, некоторая предварительная эвристическая разбивка задач и небольшое переопределение задачи (т.е. предварительное преобразование из декартовой системы в полярные координаты) могут дать хорошие решения для четко определенных наборов задач .В этом, конечно, вечный руб;трудно найти общие решения, но несколько более конкретные решения могут быть действительно хорошими.

Интересные вещи, в любом случае!

...