Написание кода для класса автокодировщиков. Часть 1

Описание класса автокодировщиков в коде

Здравствуйте и вновь добро пожаловать на занятия по теме «Глубокое обучение без учителя на языке Python».

Настало время нам с вами написать код для класса автокодировщиков.

Если вы не хотите писать его сами, вы можете перейти по адресу https://github.com/lazyprogrammer/machine_learning_examples и зайти в папку unsupervised_class2. Нужный для этой лекции файл называется autoencoder.py

Итак, мы начинаем с импорта библиотек.

import numpy as np

import theano

import theano.tensor as T

import matplotlib.pyplot as plt

from sklearn.utils import shuffle

from util import relu, error_rate, getKaggleMNIST, init_weights

Теперь определим класс автокодировщиков. Мы создадим своеобразный «конструктор» для них, поэтому вводятся произвольный параметр M, который не зависит от данных, а также параметр an_id, который мы используем для определения имён переменных Theano.

class AutoEncoder(object):

    def __init__(self, M, an_id):

        self.M = M

        self.id = an_id

Далее определим функцию fit. В качестве аргумента она принимает только X, но не Y, ведь это обучение без учителя. В ней мы определяем форму нашего X, количество пакетов для пакетного градиентного спуска и инициируем весовые коэффициенты с использованием переменных Theano.

def fit(self, X, learning_rate=0.5, mu=0.99, epochs=1, batch_sz=100, show_fig=False):

        N, D = X.shape

        n_batches = N / batch_sz

        W0 = init_weights((D, self.M))

        self.W = theano.shared(W0, ‘W_%s’ % self.id)

        self.bh = theano.shared(np.zeros(self.M), ‘bh_%s’ % self.id)

        self.bo = theano.shared(np.zeros(D), ‘bo_%s’ % self.id)

При этом нам нужно отслеживать параметры в массиве, поскольку они понадобятся нам для градиентного спуска, а также отслеживать, какие из параметров являются первыми, поскольку они понадобятся при построении глубокой нейронной сети.

        self.params = [self.W, self.bh, self.bo]

        self.forward_params = [self.W, self.bh]

Теперь определим изменение каждой переменной, поскольку мы будем использовать импульс.

        self.dW = theano.shared(np.zeros(W0.shape), ‘dW_%s’ % self.id)

        self.dbh = theano.shared(np.zeros(self.M), ‘dbh_%s’ % self.id)

        self.dbo = theano.shared(np.zeros(D), ‘dbo_%s’ % self.id)

        self.dparams = [self.dW, self.dbh, self.dbo]

        self.forward_dparams = [self.dW, self.dbh]

Далее определим тензорный вход X_in. Это будет матрица. Также определим переменную X_hat, которая будет восстановленными данными, хотя саму функцию определим чуть позже.

        X_in = T.matrix(‘X_%s’ % self.id)

        X_hat = self.forward_output(X_in)

Теперь определим операцию скрытого слоя в качестве функции Theano; это связано с тем, что она нам понадобится и при написании класса глубокой нейронной сети.

        H = T.nnet.sigmoid(X_in.dot(self.W) + self.bh)

        self.hidden_op = theano.function(

            inputs=[X_in],

            outputs=H,

        )

Далее определим функцию затрат. Это можно сделать двумя способами – или как квадрат ошибок, и тогда запишем

cost = ((X_in – X_hat) * (X_in – X_hat)).sum() / N

Или использовать (что мы и сделаем) кросс-энтропийную функцию ошибок. Поэтому запишем сразу вместе с функцией Theano для расчёта:

        cost = -(X_in * T.log(X_hat) + (1 – X_in) * T.log(1 – X_hat)).sum() / N

        cost_op = theano.function(

            inputs=[X_in],

            outputs=cost,

        )

Теперь определим наши обновления весовых коэффициентов с помощью градиентного спуска.

    updates = [

        (p, p + mu*dp – learning_rate*T.grad(cost, p)) for p, dp in zip(self.params, self.dparams)

    ] + [

        (dp, mu*dp – learning_rate*T.grad(cost, p)) for for p, dp in zip(self.params, self.dparams)

    ]

Теперь функция обучения.

        train_op = theano.function(

            inputs=[X_in],

            updates=updates,

        ) 

И переходим к циклу.

        costs = []

        print(“training autoencoder: %s” % self.id)

        for i in xrange(epochs):

            print(“epoch:”, i)

            X = shuffle(X)

            for j in xrange(n_batches):

                batch = X[j*batch_sz:(j*batch_sz + batch_sz)]

                train_op(batch)

                the_cost = cost_op(X)

                    print(“j / n_batches:”, j, “/”, n_batches, “cost:”, the_cost)

                costs.append(the_cost)

        if show_fig:

            plt.plot(costs)

            plt.show()

И теперь определим функцию forward_hidden для перехода к скрытым исходящим данным:

    def forward_hidden(self, X):

        Z = T.nnet.sigmoid(X.dot(self.W) + self.bh)

        return Z

Здесь использована сигмоида, но я настоятельно предлагаю попробовать также и гиперболический тангенс, и relu.

И ещё одна функция forward_output:

    def forward_output(self, X):

        Z = self.forward_hidden(X)

        Y = T.nnet.sigmoid(Z.dot(self.W.T) + self.bo)

        return Y

Это, собственно, и всё, что касается класса автокодировщиков.

Описание класса глубокой нейронной сети в коде

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

class DNN(object):

def __init__(self, hidden_layer_sizes, UnsupervisedModel=AutoEncoder):

        self.hidden_layers = []

        count = 0

        for M in hidden_layer_sizes:

            ae = UnsupervisedModel(M, count)

            self.hidden_layers.append(ae)

            count += 1

Далее напишем функцию fit. Это уже сфера обучения с учителем, поэтому в ней есть и X, и Y. Кроме того, я хочу отобразить функцию затрат для проверочного набора, поэтому добавлены также Xtest и Ytest, хотя в «настоящем» коде делать это совершенно необязательно.

    def fit(self, X, Y, Xtest, Ytest, pretrain=True, learning_rate=0.01, mu=0.99, reg=0.1, epochs=1, batch_sz=100):

        pretrain_epochs = 1

        if not pretrain:

            pretrain_epochs = 0

Приступаем к «жадному» послойному предварительному обучению:

        current_input = X

        for ae in self.hidden_layers:

            ae.fit(current_input, epochs=pretrain_epochs)

            current_input = ae.hidden_op(current_input)

Теперь инициируем слой логистической регрессии:

        N = len(Y)

        K = len(set(Y))

        W0 = init_weights((self.hidden_layers[-1].M, K))

        self.W = theano.shared(W0, “W_logreg”)

        self.b = theano.shared(np.zeros(K), “b_logreg”)

        self.params = [self.W, self.b]

        for ae in self.hidden_layers:

                self.params += ae.forward_params

        self.dW = theano.shared(np.zeros(W0.shape), “dW_logreg”)

        self.db = theano.shared(np.zeros(K), “db_logreg”)

        self.dparams = [self.dW, self.db]

        for ae in self.hidden_layers:

                self.dparams += ae.forward_dparams

Теперь определим входные и исходящие переменные. Они являются тензорами.

        X_in = T.matrix(‘X_in’)

        targets = T.ivector(‘Targets’)

        pY = self.forward(X_in)

        squared_magnitude = [(p*p) for p in self.params]

        reg_cost = -T.sum(squared_magnitude)

        cost = -T.mean( T.log(pY[T.arange(pY.shape[0]), targets])) + reg_cost

        prediction = self.predict(X_in)

        cost_predict_op = theano.function(

            inputs=[X_in, targets],

            outputs=[cost, prediction],

        )

Теперь определим наши обновления. Этот код идентичен тому, который был написан на предыдущей лекции.

    updates = [

        (p, p + mu*dp – learning_rate*T.grad(cost, p)) for p, dp in zip(self.params, self.dparams)

    ] + [

        (dp, mu*dp – learning_rate*T.grad(cost, p)) for for p, dp in zip(self.params, self.dparams)

    ]

        train_op = theano.function(

            inputs=[X_in, targets],

            updates=updates,

        )

        n_batches = N / batch_sz

        costs = []

        print(“supervised training…”)

        for i in xrange(epochs):

            print(“epoch:”, i)

            X, Y = shuffle(X, Y)

            for j in xrange(n_batches):

                Xbatch = X[j*batch_sz:(j*batch_sz + batch_sz)]

                Ybatch = Y[j*batch_sz:(j*batch_sz + batch_sz)]

                train_op(Xbatch, Ybatch)

                the_cost, the_prediction = cost_predict_op(Xtest, Ytest)

                error = error_rate(the_prediction, Ytest)

                print(“j / n_batches:”, j, “/”, n_batches, “cost:”, the_cost, “error:”, error)

                costs.append(the_cost)

        plt.plot(costs)

        plt.show()

На этом с функцией fit покончено. Теперь определим другие необходимые функции predict и forward.

def predict(self, X):

        return T.argmax(self.forward(X), axis=1)

def forward(self, X):

        current_input = X

        for ae in self.hidden_layers:

            Z = ae.forward_hidden(current_input)

            current_input = Z

        Y = T.nnet.softmax(T.dot(current_input, self.W) + self.b)

        return Y

Вот и всё, что касается класса глубокой нейронной сети.

Проверка. Обучение “жадного” послойного автокодировщика и обычный метод обратного распространения

Итак, последний этап – написание функции main.

Установим для нашей нейронной сети 1000 узлов для первого скрытого слоя, 750 для второго и 500 для третьего.

def main():

    Xtrain, Ytrain, Xtest, Ytest = getKaggleMNIST()

    dnn = DNN([1000, 750, 500])

    dnn.fit(Xtrain, Ytrain, Xtest, Ytest, epochs=3)

Вначале мы используем функцию квадрата ошибок, то есть для переменной cost используем выражение

cost = ((X_in – X_hat) * (X_in – X_hat)).sum() / N

И возвращаемся к функции main.

if __name__ == ‘__main__’:

main()

Запустим наш код.

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

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

Попробуем теперь использовать кросс-энтропийную функцию ошибок, то есть запишем

cost = -(X_in * T.log(X_hat) + (1 – X_in) * T.log(1 – X_hat)).sum() / N

И запустим код ещё раз.

Как мы видим, и в этом случае код хорошо работает.

Как мы видим, и в этом случае код хорошо работает.

А теперь попробуем ещё раз, но не используя предварительного обучения.

def main():

    Xtrain, Ytrain, Xtest, Ytest = getKaggleMNIST()

    dnn = DNN([1000, 750, 500])

    # dnn.fit(Xtrain, Ytrain, Xtest, Ytest, epochs=3)

    # vs

    dnn.fit(Xtrain, Ytrain, Xtest, Ytest, pretrain=False, epochs=10)

Попробуем.

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

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

Андрей Никитенко
Андрей Никитенко
Задать вопрос эксперту
Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

Share via
Copy link