1

Я пытаюсь воссоздать простую искусственную нейронную сеть MLP в Python, используя обратную прокрутку градиентного спуска. Моя цель - попытаться воссоздать точность, которую производит ANN MATLAB, но я даже не приближаюсь. Я использую те же параметры, что и MATLAB; одинаковое количество скрытых узлов (20), 1000 эпох, скорость обучения (альфа) 0,01 и те же данные (очевидно), но мой код не добивается прогресса в улучшении результатов, тогда как MATLAB получает точность в 98%.Gradient Descent ANN - Что делает MATLAB, что я не?

Я попытался отладить через MATLAB, чтобы посмотреть, что он делает, но мне не повезло. Я считаю, что MATLAB масштабирует входные данные между 0 и 1 и добавляет смещение к входу, оба из которых я использовал в своем коде Python.

Что такое MATLAB, что делает результаты намного выше? Или, возможно, более вероятно, что я сделал неправильно в моем коде Python, который приводит к таким плохим результатам? Все, о чем я могу думать, это плохое инициирование весов, неправильное считывание данных или неправильное манипулирование данными для обработки или некорректная/плохая функция активации (я тоже пытался использовать tanh, тот же результат).

Моя попытка приведена ниже, на основе кода, который я нашел в Интернете, и слегка изменил его для чтения в моих данных, тогда как сценарий MATLAB (всего 11 строк кода) находится ниже этого. Внизу есть ссылка на используемые мною наборы данных (которые я получил через MATLAB):

Спасибо за любую помощь.

Main.py

import numpy as np 
import Process 
import matplotlib.pyplot as plt 
from sklearn.metrics import confusion_matrix, classification_report 
from sklearn.cross_validation import train_test_split 
from sklearn.preprocessing import LabelBinarizer 
import warnings 


def sigmoid(x): 
    return 1.0/(1.0 + np.exp(-x)) 


def sigmoid_prime(x): 
    return sigmoid(x)*(1.0-sigmoid(x)) 


class NeuralNetwork: 

    def __init__(self, layers): 

     self.activation = sigmoid 
     self.activation_prime = sigmoid_prime 

     # Set weights 
     self.weights = [] 
     # layers = [2,2,1] 
     # range of weight values (-1,1) 
     # input and hidden layers - random((2+1, 2+1)) : 3 x 3 
     for i in range(1, len(layers) - 1): 
      r = 2*np.random.random((layers[i-1] + 1, layers[i] + 1)) - 1 
      self.weights.append(r) 
     # output layer - random((2+1, 1)) : 3 x 1 
     r = 2*np.random.random((layers[i] + 1, layers[i+1])) - 1 
     self.weights.append(r) 

    def fit(self, X, y, learning_rate, epochs): 
     # Add column of ones to X 
     # This is to add the bias unit to the input layer 
     ones = np.atleast_2d(np.ones(X.shape[0])) 
     X = np.concatenate((ones.T, X), axis=1) 

     for k in range(epochs): 

      i = np.random.randint(X.shape[0]) 
      a = [X[i]] 

      for l in range(len(self.weights)): 
        dot_value = np.dot(a[l], self.weights[l]) 
        activation = self.activation(dot_value) 
        a.append(activation) 
      # output layer 
      error = y[i] - a[-1] 
      deltas = [error * self.activation_prime(a[-1])] 

      # we need to begin at the second to last layer 
      # (a layer before the output layer) 
      for l in range(len(a) - 2, 0, -1): 
       deltas.append(deltas[-1].dot(self.weights[l].T)*self.activation_prime(a[l])) 

      # reverse 
      # [level3(output)->level2(hidden)] => [level2(hidden)->level3(output)] 
      deltas.reverse() 

      # backpropagation 
      # 1. Multiply its output delta and input activation 
      # to get the gradient of the weight. 
      # 2. Subtract a ratio (percentage) of the gradient from the weight. 
      for i in range(len(self.weights)): 
       layer = np.atleast_2d(a[i]) 
       delta = np.atleast_2d(deltas[i]) 
       self.weights[i] += learning_rate * layer.T.dot(delta) 

    def predict(self, x): 
     a = np.concatenate((np.ones(1).T, np.array(x))) 
     for l in range(0, len(self.weights)): 
      a = self.activation(np.dot(a, self.weights[l])) 
     return a 

# Create neural net, 13 inputs, 20 hidden nodes, 3 outputs 
nn = NeuralNetwork([13, 20, 3]) 
data = Process.readdata('wine') 
# Split data out into input and output 
X = data[0] 
y = data[1] 
# Normalise input data between 0 and 1. 
X -= X.min() 
X /= X.max() 

# Split data into training and test sets (15% testing) 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.15) 

# Create binay output form 
y_ = LabelBinarizer().fit_transform(y_train) 

# Train data 
lrate = 0.01 
epoch = 1000 
nn.fit(X_train, y_, lrate, epoch) 

# Test data 
err = [] 
for e in X_test: 
    # Create array of output data (argmax to get classification) 
    err.append(np.argmax(nn.predict(e))) 

# Hide warnings. UndefinedMetricWarning thrown when confusion matrix returns 0 in any one of the classifiers. 
warnings.filterwarnings('ignore') 
# Produce confusion matrix and classification report 
print(confusion_matrix(y_test, err)) 
print(classification_report(y_test, err)) 

# Plot actual and predicted data 
plt.figure(figsize=(10, 8)) 
target, = plt.plot(y_test, color='b', linestyle='-', lw=1, label='Target') 
estimated, = plt.plot(err, color='r', linestyle='--', lw=3, label='Estimated') 
plt.legend(handles=[target, estimated]) 
plt.xlabel('# Samples') 
plt.ylabel('Classification Value') 
plt.grid() 
plt.show() 

Process.py

import csv 
import numpy as np 


# Add constant column of 1's 
def addones(arrayvar): 
    return np.hstack((np.ones((arrayvar.shape[0], 1)), arrayvar)) 


def readdata(loc): 
    # Open file and calculate the number of columns and the number of rows. The number of rows has a +1 as the 'next' 
    # operator in num_cols has already pasted over the first row. 
    with open(loc + '.input.csv') as f: 
     file = csv.reader(f, delimiter=',', skipinitialspace=True) 
     num_cols = len(next(file)) 
     num_rows = len(list(file))+1 

    # Create a zero'd array based on the number of column and rows previously found. 
    x = np.zeros((num_rows, num_cols)) 
    y = np.zeros(num_rows) 

    # INPUT # 
    # Loop through the input file and put each row into a new row of 'samples' 
    with open(loc + '.input.csv', newline='') as csvfile: 
     file = csv.reader(csvfile, delimiter=',') 
     count = 0 
     for row in file: 
      x[count] = row 
      count += 1 

    # OUTPUT # 
    # Do the same and loop through the output file. 
    with open(loc + '.output.csv', newline='') as csvfile: 
     file = csv.reader(csvfile, delimiter=',') 
     count = 0 
     for row in file: 
      y[count] = row[0] 
      count += 1 

    # Set data type 
    x = np.array(x).astype(np.float) 
    y = np.array(y).astype(np.int) 

    return x, y 

MATLAB скрипт

%% LOAD DATA 
[x1,t1] = wine_dataset; 

%% SET UP NN 
net = patternnet(20); 
net.trainFcn = 'traingd'; 
net.layers{2}.transferFcn = 'logsig'; 
net.derivFcn = 'logsig'; 

%% TRAIN AND TEST 
[net,tr] = train(net,x1,t1); 

файлы данных могут быть Загруз.дорожек тины здесь: input output

+0

Возможно, это то, что вы уже сделали (как вы отметили отладку), но посмотрите внутри тренировочной функции MATLAB: 'edit train.m' – mikkola

+0

Спасибо, Миккола, но так как я действительно уже смотрел в поезде. Что-нибудь конкретное я должен искать? Я заметил, что Matlab вектурирует их веса, тогда как мой код проходит через каждый весовой слой. Это должно привести к тем же результатам, если бы уравнения были одинаковыми (что, по моему мнению, они есть). – ritchie888

ответ

0

Я считаю, что я нашел проблему. Это была комбинация самого набора данных (эта проблема не встречалась со всеми наборами данных) и способ, которым я масштабировал данные.Мой оригинальный метод масштабирования, который обрабатывается результаты в диапазоне от 0 до 1, не помогает ситуации, и вызвал плохие результаты видели:

# Normalise input data between 0 and 1. 
X -= X.min() 
X /= X.max() 

Я нашел другой способ масштабирования, предоставляет пакет sklearn предварительной обработки:

from sklearn import preprocessing 
X = preprocessing.scale(X) 

Этот метод масштабирования не находится между 0 и 1, и у меня есть дополнительное исследование, чтобы определить, почему он так много помог, но результаты теперь возвращаются с точностью от 96 до 100%. Очень похоже на результаты MATLAB, которые, как я полагаю, используют аналогичный (или такой же) метод масштабирования предварительной обработки.

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

0

Я думаю, что вы перепутали термины epoch и step. Если вы прошли обучение по одному epoch, это обычно означает прохождение всех данных.

Например: если у вас есть 10.000 образцов, вы поместите все 10.000 выборок (без учета рандомизированной выборки образцов) через вашу модель и сделайте шаг (обновляйте свои веса) каждый раз.

Чтобы исправить: Запуск сети гораздо дольше:

nn.fit(X_train, y_, lrate, epoch*len(X)) 

Бонус: документы MatLab переводится эпохи к (iterations)here, который вводит в заблуждение, но комментарии к нему here который поясню, что я написал выше.

+0

Спасибо за ваш вклад, Aske, но это не решило проблему. После отладки через MATLAB я обнаружил, что код только циклически проходит весь набор данных в размере эпохи (1000), как и в моем исходном коде. Тем не менее, я внедрил ваше предложение, и, как предполагалось, он улучшил результаты, однако он все еще на 20% ниже, чем точность MATLAB 98%. Я также запустил код за 1 000 000 эпох, что также мало помогло, и предлагает, чтобы номер эпохи не решал проблему. Кажется, что есть что-то еще под обложками, которые делает MATLAB (или я ошибаюсь). Еще раз спасибо. – ritchie888

Смежные вопросы