ад происходит со стохастическим градиентным спуском - PullRequest
0 голосов
/ 25 января 2019

Я работаю с многомерной линейной регрессией и использую стохастический градиентный спуск для оптимизации.

Работа с этими данными http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/

для каждого запуска все гиперпараметры и все остальные вещи одинаковы, эпох = 200 и альфа = 0,1

при первом запуске я получил final_cost = 0.0591, при повторном запуске программы, сохраняя все то же самое, я получил final_cost = 1.0056 работает снова, сохраняя все то же самое, я получил final_cost = 0.8214 работает снова final_cost = 15.9591, снова запускается final_cost = 2.3162 и так далее и так далее ...

Как вы можете видеть, при том, что все остается в рабочем состоянии, снова и снова, каждый раз, когда конечная стоимость изменяется на большую величину, иногда такую ​​большую, как от 0,8 до прямой 15,9, от 0,05 до прямой 1,00, и не только это график конечной стоимости после каждой эпохи в одном и том же прогоне каждый зигзаг отличается от серийного GD, в котором график затрат плавно уменьшается.

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

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

Но в случае с SGD я буквально плакал,

class Abalone : 
    def __init__(self,df,epochs=200,miniBatchSize=250,alpha=0.1) :

        self.df = df.dropna()
        self.epochs = epochs
        self.miniBatchSize = miniBatchSize
        self.alpha = alpha

        print("abalone created")
        self.modelTheData()


    def modelTheData(self) :

        self.TOTAL_ATTR = len(self.df.columns) - 1
        self.TOTAL_DATA_LENGTH = len(self.df.index)

        self.df_trainingData = 
        df.drop(df.index[int(self.TOTAL_DATA_LENGTH * 0.6):])

        self.TRAINING_DATA_SIZE = len(self.df_trainingData)

        self.df_testingData = 
        df.drop(df.index[:int(self.TOTAL_DATA_LENGTH * 0.6)])

        self.TESTING_DATA_SIZE = len(self.df_testingData)

        self.miniBatchSize = int(self.TRAINING_DATA_SIZE / 10)

        self.thetaVect = np.zeros((self.TOTAL_ATTR+1,1),dtype=float)

        self.stochasticGradientDescent()


    def stochasticGradientDescent(self) :

        self.finalCostArr = np.array([])

        startTime = time.time()    

        for i in range(self.epochs) :

            self.df_trainingData = 
            self.df_trainingData.sample(frac=1).reset_index(drop=True)

            miniBatches=[self.df_trainingData.loc[x:x+self.miniBatchSize-
                  ((x+self.miniBatchSize)/(self.TRAINING_DATA_SIZE-1)),:]
            for x in range(0,self.TRAINING_DATA_SIZE,self.miniBatchSize)]          

            self.epochCostArr = np.array([])

            for j in miniBatches : 

                tempMat = j.values
                self.actualValVect = tempMat[ : , self.TOTAL_ATTR:]
                tempMat = tempMat[ : , :self.TOTAL_ATTR]
                self.desMat = np.append( 
                np.ones((len(j.index),1),dtype=float) , tempMat , 1 )                
                del tempMat

                self.trainData()

                currCost = self.costEvaluation()
                self.epochCostArr = np.append(self.epochCostArr,currCost)

            self.finalCostArr = np.append(self.finalCostArr,
                                   self.epochCostArr[len(miniBatches)-1])

        endTime = time.time()
        print(f"execution time : {endTime-startTime}")
        self.graphEvaluation()
        print(f"final cost : 
                {self.finalCostArr[len(self.finalCostArr)-1]}")
        print(self.thetaVect)


    def trainData(self) :

        self.predictedValVect = self.predictResult()
        diffVect = self.predictedValVect - self.actualValVect
        partialDerivativeVect = np.matmul(self.desMat.T , diffVect)
        self.thetaVect -= 
                      (self.alpha/len(self.desMat))*partialDerivativeVect


    def predictResult(self) :

        return np.matmul(self.desMat,self.thetaVect)


    def costEvaluation(self) : 

        cost =  sum((self.predictedValVect - self.actualValVect)**2)
        return cost / (2*len(self.actualValVect))


    def graphEvaluation(self) : 

        plt.title("cost at end of all epochs")
        x = range(len(self.epochCostArr))
        y = self.epochCostArr
        plt.plot(x,y)
        plt.xlabel("iterations")
        plt.ylabel("cost")
        plt.show()

Я держал эпохи = 200 и альфа = 0,1 для всех прогонов, но я получал совершенно разные результаты в каждом прогоне.

Вектор, упомянутый ниже, является тета-вектором, где первая запись - это смещение, а оставшиеся - веса

RUN 1 =>>

[[  5.26020144]
[ -0.48787333]
[  4.36479114]
[  4.56848299]
[  2.90299436]
[  3.85349625]
[-10.61906207]
[ -0.93178027]
[  8.79943389]]

final cost : 0.05917831328836957


RUN 2 =>>

[[  5.18355814]
[ -0.56072668]
[  4.32621647]
[  4.58803884]
[  2.89157598]
[  3.7465471 ]
[-10.75751065]
[ -1.03302031]
[  8.87559247]]

final cost: 1.0056239103948563


RUN 3 =>>

[[  5.12836056]
[ -0.43672936]
[  4.25664898]
[  4.53397465]
[  2.87847224]
[  3.74693215]
[-10.73960775]
[ -1.00461585]
[  8.85225402]]

final cost : 0.8214901206702101


RUN 4 =>>

[[  5.38794798]
[  0.23695412]
[  4.43522951]
[  4.66093372]
[  2.9460605 ]
[  4.13390252]
[-10.60071883]
[ -0.9230675 ]
[  8.87229324]]

final cost: 15.959132174895712


RUN 5 =>>

[[  5.19643132]
[ -0.76882106]
[  4.35445135]
[  4.58782119]
[  2.8908931 ]
[  3.63693031]
[-10.83291949]
[ -1.05709616]
[  8.865904  ]]

final cost: 2.3162151072779804

Я не могу понять, что происходит Неправильно. Ведет ли SGD подобное поведение, или я сделал какую-то глупость при преобразовании своего кода из пакетного GD в SGD. И если SGD ведет себя так, то как я узнаю, сколько раз мне нужно перезапускаться, потому что мне не так повезло, что каждый раз, когда в первом запуске я получал такую ​​небольшую стоимость, как 0,05, иногда первый запуск дает стоимость около 10,5, иногда 0,6 и, возможно, многократно повторяя его, я получал стоимость даже меньше, чем 0,05.

когда я подошел к точно такой же проблеме с точно таким же кодом и гиперпараметрами, просто заменив функцию SGD обычной пакетной GD, я получил ожидаемый результат, т. Е. После каждой итерации по одним и тем же данным моя стоимость плавно снижалась, т. Е. Монотонная убывающая функция и независимо от того, сколько раз я запускал одну и ту же программу, я получал один и тот же результат, так как это очень очевидно.

"Сохраняя все то же самое, но используя пакетный GD для эпох = 20000 и альфа = 0,1 Я получил final_cost = 2.7474 "

def BatchGradientDescent(self) :

    self.costArr = np.array([])

    startTime = time.time()

    for i in range(self.epochs) :

            tempMat = self.df_trainingData.values
            self.actualValVect = tempMat[ : , self.TOTAL_ATTR:]
            tempMat = tempMat[ : , :self.TOTAL_ATTR]
            self.desMat = np.append( np.ones((self.TRAINING_DATA_SIZE,1),dtype=float) , tempMat , 1 )                
            del tempMat

            self.trainData()

            if i%100 == 0 :

                currCost = self.costEvaluation()
                self.costArr = np.append(self.costArr,currCost)

    endTime = time.time()

    print(f"execution time : {endTime - startTime} seconds")
    self.graphEvaluation()
    print(self.thetaVect)
    print(f"final cost : {self.costArr[len(self.costArr)-1]}")

Кто-то, помогите мне понять, что на самом деле происходит. Каждое мнение / решение - это большой доход для меня в этой новой области:)

1 Ответ

0 голосов
/ 25 января 2019

Вы пропустили самое важное и единственное различие между GD («Градиентный спуск») и SGD (« Стохастик Градиентный спуск»).

Стохастичность - Буквально означает «качество отсутствия какого-либо предсказуемого порядка или плана». Значение случайности.

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

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

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

...