Replicated

경사하강법 실습 본문

빅데이터마이닝

경사하강법 실습

라구넹 2025. 4. 8. 13:49
def gradient(self, X, y, theta):
        # 경사하강법을 위한 기울기 계산
        return X.T.dot(self.hypothesis_function(X, theta) - y) / len(X) ######## 코드 작성 ##########

일단 이건 알파(학습률)랑 곱해지는 경사를 구하는 수식임

어떻게 계산되는가?

 

이걸 기반으로 하는데, 저기서 J.. 즉 손실함수를 기울기로 사용한다

 

이때 손실 함수는 늘 보는 그거, MSE 미분한 거다

 

미분 시 이렇게 정리된다.

 

위의 gradient 함수는 그냥 이 수식 그 자체이다

부호가 좀 다른 거 같긴 한데, 그마저도 내부 - 정리하면 같아진다.

 

def fit(self, X, y):
        self._new_X = np.array(X) 
        y = y.reshape(-1, 1) 
        if self.fit_intercept:  
            intercept_vector = np.ones([len(self._new_X), 1])
            self._new_X = np.concatenate((intercept_vector, self._new_X), axis=1)

        theta_init = np.random.normal(0, 1, self._new_X.shape[1])
        self._w_history = [theta_init] 
        self._cost_history = [self.cost(self.hypothesis_function(self._new_X, theta_init), y)] 

        theta = theta_init 

        for epoch in range(self._epochs): 
            X_copy, y_copy = np.copy(self._new_X), np.copy(y) 

            if self._is_SGD: 
                indices = np.arange(len(X_copy))  
                np.random.shuffle(indices)
                X_copy, y_copy = X_copy[indices], y_copy[indices] 

            num_batches = len(X_copy) // self._batch_size  

            for batch_count in range(num_batches): 
                start =  batch_count * self._batch_size 
                end =  start + self._batch_size

                X_batch = X_copy[start:end]  
                y_batch = y_copy[start:end]  

                gradient = self.gradient(X_batch, y_batch, theta).flatten()  #
                theta = theta - self._eta0 * gradient 

            if epoch % 100 == 0: 
                self._w_history.append(theta)
                cost = self.cost(self.hypothesis_function(self._new_X, theta), y)  
                self._cost_history.append(cost)  

            self._eta0 *= self._weight_decay  

        if self.fit_intercept: 
            self._intercept = theta[0]
            self._coef = theta[1:]
        else: 
            self._coef = theta

모델 학습시키는 코드..

 

def fit(self, X, y):
        self._new_X = np.array(X) 
        y = y.reshape(-1, 1) 
        if self.fit_intercept:  
            intercept_vector = np.ones([len(self._new_X), 1])
            self._new_X = np.concatenate((intercept_vector, self._new_X), axis=1)

일단 여기까지는 기본 세팅

X를 numpy 어레이로 만들어 저장하고, y를 열벡터로 변환

* 절편 요구 시 np.ones로 행, 열 주고 1로 채움

 

* np.concatenate((intercept_vector, self._new_X), axis=1)와 np.c_[ , ] 차이

둘다 numpy 배열 연결임

gpt 정리

 

        theta_init = np.random.normal(0, 1, self._new_X.shape[1])
        self._w_history = [theta_init]  
        self._cost_history = [self.cost(self.hypothesis_function(self._new_X, theta_init), y)] 

        theta = theta_init

np.random.normal(0, 1, size)

평균 0, 표준편차 1인 정규분포에서 size만큼 랜덤 샘플 뽑는 거

그 다음 가중치 초기화하고, 가설함수 돌려서 모든 입력 X에 대해 히스토리 저장 (모든 X에 초기 비용 처리)

일단 초기화

 

for epoch in range(self._epochs): 
            X_copy, y_copy = np.copy(self._new_X), np.copy(y) 

            if self._is_SGD: 
                indices = np.arange(len(X_copy))  
                np.random.shuffle(indices)
                X_copy, y_copy = X_copy[indices], y_copy[indices] 

            num_batches = len(X_copy) // self._batch_size  

            for batch_count in range(num_batches): 
                start =  batch_count * self._batch_size 
                end =  start + self._batch_size

                X_batch = X_copy[start:end]  
                y_batch = y_copy[start:end]  

                gradient = self.gradient(X_batch, y_batch, theta).flatten()  #
                theta = theta - self._eta0 * gradient 

            if epoch % 100 == 0: 
                self._w_history.append(theta)
                cost = self.cost(self.hypothesis_function(self._new_X, theta), y)  
                self._cost_history.append(cost)  

            self._eta0 *= self._weight_decay

바깥 루프에서 에폭만큼 돌리고, 내부 루프에서 배치 별 학습

SGD이면 셔플

start랑 end 배치 사이즈 기준으로 구하고, 미니 배치 추출

그리고 각 미니배치에 대해 경사하강법 수행 (gradient 구하고 학습률과 구해서 세타에 빼기)

* flatten은 벡터 모양을 1차원으로 펼치는 거

 

그리고 100 에폭마다 가중치 및 비용 저장

그리고 학습률도 점점 감소시켜줌

 

def cost(self, h, y):
        return 1 / (2 * len(y)) * np.sum((h - y).flatten() ** 2)

비용 함수 (MSE)

 

def hypothesis_function(self, X, theta):
        return X.dot(theta).reshape(-1, 1)

가설함수

그냥 입력에 세타 곱하면 됨

 

   def predict(self, X):
        test_X = np.array(X)

        if self.fit_intercept:
            intercept_vector = np.ones([len(test_X), 1]) 
            test_X = np.concatenate((intercept_vector, test_X), axis=1) 
            weights = np.concatenate(([self._intercept], self._coef), axis=0) 
        else:
            weights = self._coef
            
        return test_X.dot(weights)

예측 함수

절편 만들어야 하면 1로된 벡터 만들어서 인풋에 붙이고, 가중치는 절편을 [] 씌워서 가중치랑 연결

* 스칼라는 numpy 배열이랑 연결 안되서 []로 배열로 만들어줘야 함. 위쪽 코드 보면 intercept는 theta[0]로 저장함.

axis 0이면 행 방향으로 이어 붙임 => [intercept, coef] 로 연결됨

이거 그냥 곱하면 됨

 

근데 뭔가 x랑 weights랑 행벡터냐 열벡터냐가 고민이 됨

=> np.dot는 차원 맞으면 방향 신경 안써도 됨

마지막 축 기준으로 내적해줌 Shape만 보면 됨

'빅데이터마이닝' 카테고리의 다른 글

로지스틱 회귀  (0) 2025.04.11
과대적합과 정규화  (0) 2025.04.11
경사하강법의 종류  (0) 2025.04.07
경사하강법 선형회귀  (0) 2025.04.07
최소자승법 선형회귀 - 수식 유도, 장단점  (0) 2025.04.07