검증 세트란?
딥러닝에서 검증 세트(Validation Set)는 모델 훈련 과정에서 중요한 역할을 하는 데이터 집합이다.
이를 통해 모델의 일반화 성능을 평가하고, 과적합(overfitting)을 방지하는 데 도움을 준다.
검증 세트의 주요 목적
1. 모델 성능 평가:
- 훈련 세트(Training Set)는 모델을 학습시키는 데 사용되지만, 검증 세트는 훈련 과정 중에 모델의 성능을 평가하는 데 사용된다.
이를 통해 모델이 학습 데이터에 과적합되고 있는지 여부를 확인할 수 있다.2. 하이퍼파라미터 튜닝:
- 모델의 하이퍼파라미터(예: 학습률, 배치 크기, 네트워크 깊이 등)를 조정하는 과정에서 검증 세트의 성능을 기준으로 최적의 값을 찾는다.
3. 조기 종료(Early Stopping):
- 훈련 도중 검증 세트의 성능이 향상되지 않고 악화되기 시작하면, 더 이상의 훈련이 과적합으로 이어질 수 있다.
- 이때 조기 종료 기법을 사용하여 최적의 시점에서 훈련을 멈춘다.
검증 세트의 사용 방법
1. 데이터 분할:
- 원본 데이터를 훈련 세트, 검증 세트, 테스트 세트로 분할
- 일반적으로 60-80%는 훈련 세트, 10-20%는 검증 세트, 10-20%는 테스트 세트로 사용
2. 훈련 과정 중 평가:
- 각 에포크(epoch) 또는 일정한 간격으로 모델을 검증 세트에 대해 평가하여 손실(loss)과 정확도(accuracy) 등의 지표를 계산
3. 최적의 모델 선택:
- 검증 세트에서 가장 높은 성능을 보인 모델을 저장하여 최종 모델로 사용
검증 세트의 중요성
과적합 방지:
- 검증 세트를 통해 모델이 훈련 데이터에 과적합되지 않도록 조절
- 이는 모델의 일반화 능력을 향상시키는 데 중요
모델 비교:
- 여러 모델을 실험할 때 검증 세트를 사용하여 각 모델의 성능을 비교하고 최적의 모델을 선택할 수 있음
성능 예측:
- 검증 세트의 성능은 모델이 새로운 데이터에 대해 어떻게 동작할지에 대한 예측을 제공
예시
예를 들어, 이미지 분류 문제에서 10,000장의 이미지가 있다면, 이를 훈련 세트(6,000장), 검증 세트(2,000장), 테스트 세트(2,000장)로 분할한다.
모델은 훈련 세트로 학습하고, 각 에포크 후 검증 세트에서 성능을 평가하여 최적의 하이퍼파라미터를 찾고, 조기 종료를 결정한다.
최종 모델은 테스트 세트로 최종 성능을 평가한다.
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
cancer = load_breast_cancer()
x = cancer.data
y = cancer.target
x_train_all, x_test, y_train_all, y_test = train_test_split(x, y, stratify=y,
test_size=0.2, random_state=42)
from sklearn.linear_model import SGDClassifier
sgd = SGDClassifier(loss='log_loss', random_state=42)
sgd.fit(x_train_all, y_train_all)
sgd.score(x_test, y_test)
### 출력 결과
0.8333333333333334
sgd = SGDClassifier(loss='hinge', random_state=42)
sgd.fit(x_train_all, y_train_all)
sgd.score(x_test, y_test)
### 출력 결과
0.9385964912280702
SGDClassifier 모델에서 loss를 log_loss에서 hinge로 바꿨는데 점수는 10점이나 더 올랐다.
이렇게 모델에서 우리가 직접 수정할 수 있는 파라미터를 하이퍼 파라미터라고 부른다.
hinge는 서포트 벡터 머신 (SVM)이다.
x_train, x_val, y_train, y_val = train_test_split(x_train_all, y_train_all, stratify=y_train_all,
test_size=0.2, random_state=42)
print(len(x_train_all))
### 출력 결과
455
print(len(x_train), len(x_val))
### 출력 결과
364 91
이렇게 train과 test 데이터를 8:2로 나눠서, 한 번 더 train 데이터에서 검증 데이터와 나누어주었다.
sgd = SGDClassifier(loss='log_loss', random_state=42)
sgd.fit(x_train, y_train)
sgd.score(x_val, y_val)
### 출력 결과
0.6923076923076923
sgd = SGDClassifier(loss='hinge', random_state=42)
sgd.fit(x_train, y_train)
sgd.score(x_val, y_val)
### 출력 결과
0.9230769230769231
검증 데이터를 나눈뒤 log_loss로 확인해보니 점수가 상당히 낮아졌다.
하지만 hinge로 확인해보니 점수가 높은걸 확인 할 수 있다.
이는 데이터 전처리의 문제, 데이터 분할의 무작위성 등 여러가지 사유가 있을수 있다.
log_loss는 로지스틱 회귀를 의미한다.
해당 하이퍼 파라미터가 적절하지 않을수 있다.
print(cancer.feature_names[[2,3]])
plt.figure(figsize=(15,8))
plt.boxplot(x_train[:, 2:4])
plt.xlabel('feature')
plt.ylabel('value')
plt.show()
### 출력 결과

해당 그래프를 보면 feature 1과 2의 분포가 매우 다른것을 볼 수 있다.
특정 변수의 분포가 매우 비대칭적이거나 범위가 클 경우, 이를 고려하여 모델을 선택하고 하이퍼 파라미터를 튜닝하는것이 중요하다.
class SingleLayer:
def __init__(self, learning_rate=0.1, l1=0, l2=0):
self.w = None
self.b = None
self.losses = []
self.val_losses = []
self.w_history = []
self.lr = learning_rate
self.l1 = l1
self.l2 = l2
def forpass(self, x):
z = np.sum(x * self.w) + self.b # 직선 방정식을 계산합니다
return z
def backprop(self, x, err):
w_grad = x * err # 가중치에 대한 그래디언트를 계산합니다
b_grad = 1 * err # 절편에 대한 그래디언트를 계산합니다
return w_grad, b_grad
def activation(self, z):
z = np.clip(z, -100, None) # 안전한 np.exp() 계산을 위해
a = 1 / (1 + np.exp(-z)) # 시그모이드 계산
return a
def fit(self, x, y, epochs=100, x_val=None, y_val=None):
self.w = np.ones(x.shape[1]) # 가중치를 초기화합니다.
self.b = 0 # 절편을 초기화합니다.
self.w_history.append(self.w.copy()) # 가중치를 기록합니다.
np.random.seed(42) # 랜덤 시드를 지정합니다.
for i in range(epochs): # epochs만큼 반복합니다.
loss = 0
# 인덱스를 섞습니다
indexes = np.random.permutation(np.arange(len(x)))
for i in indexes: # 모든 샘플에 대해 반복합니다
z = self.forpass(x[i]) # 정방향 계산
a = self.activation(z) # 활성화 함수 적용
err = -(y[i] - a) # 오차 계산
w_grad, b_grad = self.backprop(x[i], err) # 역방향 계산
# 그래디언트에서 페널티 항의 미분 값을 더합니다
w_grad += self.l1 * np.sign(self.w) + self.l2 * self.w
self.w -= self.lr * w_grad # 가중치 업데이트
self.b -= self.lr * b_grad # 절편 업데이트
# 가중치를 기록합니다.
self.w_history.append(self.w.copy())
# 안전한 로그 계산을 위해 클리핑한 후 손실을 누적합니다
a = np.clip(a, 1e-10, 1-1e-10)
loss += -(y[i]*np.log(a)+(1-y[i])*np.log(1-a))
# 에포크마다 평균 손실을 저장합니다
self.losses.append(loss/len(y) + self.reg_loss())
# 검증 세트에 대한 손실을 계산합니다
self.update_val_loss(x_val, y_val)
def predict(self, x):
z = [self.forpass(x_i) for x_i in x] # 정방향 계산
return np.array(z) >= 0 # 스텝 함수 적용
def score(self, x, y):
return np.mean(self.predict(x) == y)
def reg_loss(self):
return self.l1 * np.sum(np.abs(self.w)) + self.l2 / 2 * np.sum(self.w**2)
def update_val_loss(self, x_val, y_val):
if x_val is None:
return
val_loss = 0
for i in range(len(x_val)):
z = self.forpass(x_val[i]) # 정방향 계산
a = self.activation(z) # 활성화 함수 적용
a = np.clip(a, 1e-10, 1-1e-10)
val_loss += -(y_val[i]*np.log(a)+(1-y_val[i])*np.log(1-a))
self.val_losses.append(val_loss/len(y_val) + self.reg_loss())
layer1 = SingleLayer()
layer1.fit(x_train, y_train)
layer1.score(x_val, y_val)
### 출력 결과
0.9120879120879121
w2 = []
w3 = []
for w in layer1.w_history:
w2.append(w[2])
w3.append(w[3])
plt.figure(figsize=(15,8))
plt.plot(w2, w3)
plt.plot(w2[-1], w3[-1], 'ro')
plt.xlabel('w[2]')
plt.ylabel('w[3]')
plt.show()
print(w2[-1], w3[-1])
### 출력 결과
2332.594400273708 491.24740993261946

이 코드는 훈련 과정에서 저장된 가중치 역사(w_history)를 사용하여 가중치 w[2] 와 w[3] 의 변화를 그래프로 나타낸다.
마지막 지점을 빨간색 점(ro)으로 표시하여 가중치의 최종 값을 강조하고 있다.
그래프 분석
1. 가중치 변화 패턴:
- 초기에는 w[2] 와 w[3] 가 모두 증가하는 패턴을 보인다.
- 훈련이 진행됨에 따라 가중치의 변화가 다소 불규칙해지지만, 전반적으로 증가하는 추세를 유지하고 있다.
- 마지막 단계에서는 변화가 안정화된 것처럼 보인다.
2. 최종 가중치:
- 빨간색 점으로 표시된 마지막 지점은 최종 가중치 값을 나타낸다.
- print(w2[-1], w3[-1])를 통해 최종 가중치 값을 출력한다. (값은 코드 실행 후 확인 가능)
결론 및 의미
1. 가중치 수렴:
- 그래프가 일정한 패턴으로 변화하고 있으며, 마지막에 수렴하는 모습을 보이는 것은 모델이 학습 과정에서 점진적으로 최적화되고 있음을 의미한다.
- 가중치의 최종 값이 안정화되는 것은 모델이 학습을 완료했다는 신호일 수 있다.
2. 훈련 과정 이해:
- 가중치 변화의 경향을 통해 학습률, 가중치 초기화 방법, 최적화 알고리즘의 영향을 파악할 수 있다.
- 만약 가중치가 계속 불안정하거나 크게 변동한다면 학습률이 너무 높거나, 최적화 알고리즘에 문제가 있을 수 있다.
3. 모델의 성능 향상:
- 가중치 변화 패턴을 분석하여 필요하다면 학습률을 조정하거나, 다른 최적화 알고리즘을 시도할 수 있다.
- 만약 가중치가 지나치게 큰 값으로 수렴한다면, 정규화를 적용하여 과적합을 방지할 수 있다.
개선 방법
학습률 조정:
- 학습률이 너무 높다면 가중치가 불안정하게 변할 수 있다.
- 이를 조정하여 안정적인 학습을 유도할 수 있다.
가중치 초기화 방법:
- 가중치 초기화 방법이 학습에 영향을 줄 수 있다.
- Xavier 초기화나 He 초기화를 시도할 수 있다.
정규화 기법:
- L2 정규화 등을 사용하여 가중치의 크기를 조절함으로써 과적합을 방지할 수 있다.
train_mean = np.mean(x_train, axis=0)
train_std = np.std(x_train, axis=0)
x_train_scaled = (x_train - train_mean) / train_std
layer2 = SingleLayer()
layer2.fit(x_train_scaled, y_train)
print(cancer.feature_names[[2,3]])
plt.figure(figsize=(15,8))
plt.boxplot(x_train_scaled[:, 2:4])
plt.xlabel('feature')
plt.ylabel('value')
plt.show()
### 출력 결과

표준화를 진행하자 데이터의 분포가 일정해진것을 볼 수 있다.
w2 = []
w3 = []
for w in layer2.w_history:
w2.append(w[2])
w3.append(w[3])
plt.figure(figsize=(15,8))
plt.plot(w2, w3)
plt.plot(w2[-1], w3[-1], 'ro')
plt.xlabel('w[2]')
plt.ylabel('w[3]')
plt.show()
print(w2[-1], w3[-1])

그래프 분석
1. 가중치 변화 패턴:
- 표준화 전의 그래프와 비교했을 때, 가중치 변화가 더 일정하고 안정적인 패턴을 보입니다.
- 초기 학습 단계에서는 ( w[2] )와 ( w[3] )이 큰 변동을 보였으나, 점차적으로 수렴하는 모습을 확인할 수 있습니다.
2. 가중치 수렴:
- 학습 후반부에서는 가중치 변화가 작아지며, 최종적으로 안정된 값을 가집니다.
- 최종 가중치 값이 그래프의 빨간 점으로 표시되어 있으며, 이 값에서 수렴했음을 알 수 있습니다.
3. 표준화의 효과:
- 표준화는 데이터의 분포를 평균이 0, 표준편차가 1로 변환하여 모델이 학습하는 동안 가중치의 변화가 더 안정적으로 이루어지게 합니다.
- 그래프의 ( w[2] )와 ( w[3] )가 비교적 좁은 범위 내에서 안정적으로 변동하는 것을 확인할 수 있습니다.
결론 및 의미
1. 학습 안정성 향상:
- 표준화된 데이터로 학습했을 때 가중치의 변화가 더 규칙적이고, 수렴하는 패턴을 보입니다.
- 이는 표준화가 학습의 안정성을 향상시키고, 더 빠르고 안정적으로 최적화할 수 있게 도와준다는 것을 의미합니다.
2. 모델 성능 향상:
- 표준화를 통해 모델의 성능이 향상될 수 있습니다. 이는 SGD와 같은 최적화 알고리즘이 표준화된 데이터에서 더 잘 작동하기 때문입니다.
- 학습 과정에서 가중치가 안정적으로 수렴하는 것은 모델이 더 일반화될 수 있는 능력을 가졌다는 것을 암시합니다.
3. 하이퍼파라미터 튜닝:
- 그래프를 통해 학습률과 같은 하이퍼파라미터가 적절히 설정되었음을 알 수 있습니다.
- 만약 가중치 변화가 지나치게 크거나 불안정하다면 학습률을 조정하는 등의 튜닝이 필요할 수 있습니다.
layer2.score(x_val, y_val)
### 출력 결과
0.37362637362637363
val_mean = np.mean(x_val, axis=0)
val_std = np.std(x_val, axis=0)
x_val_scaled = (x_val - val_mean) / val_std
layer2.score(x_val_scaled, y_val)
### 출력 결과
0.967032967032967
여기서도 스케일링을 해줘야 점수가 제대로 나오는것을 볼 수 있다.
과대/과소 적합이란?
과대 적합(overfitting)과 과소 적합(underfitting)은 기계 학습 및 데이터 분석에서 중요한 개념이다.
이 둘은 모델의 학습 과정과 그 결과를 설명하는 용어로, 모델의 성능에 중요한 영향을 미친다.
과대 적합 (Overfitting)
과대 적합은 모델이 학습 데이터에 너무 잘 맞춰져서 새로운 데이터에 대해 일반화되지 않는 상황을 말한다.
이는 모델이 학습 데이터의 노이즈나 비정상 패턴까지 학습하기 때문에 발생힌다.
과대 적합된 모델은 학습 데이터에 대해서는 높은 성능을 보이지만, 테스트 데이터나 실제 데이터에 대해서는 성능이 저하된다.특징:
- 학습 데이터에서의 정확도가 매우 높다.
- 테스트 데이터에서의 정확도가 낮다.
- 모델이 복잡하고 파라미터가 많다.
해결 방법:
- 더 많은 학습 데이터를 사용
- 모델의 복잡성을 줄임(예: 파라미터 수를 줄이거나 정규화를 적용).
- 교차 검증(cross-validation)을 사용하여 모델을 평가
- 드롭아웃(dropout) 같은 정규화 기법을 사용
과소 적합 (Underfitting)
과소 적합은 모델이 학습 데이터의 패턴을 제대로 학습하지 못한 상태를 말한다.
이는 모델이 너무 단순해서 데이터의 중요한 특성을 잡아내지 못하기 때문에 발생한다.
과소 적합된 모델은 학습 데이터와 테스트 데이터 모두에서 성능이 저조하다.특징:
- 학습 데이터와 테스트 데이터 모두에서 정확도가 낮다.
- 모델이 너무 단순하거나 파라미터가 적다.
해결 방법:
- 더 복잡한 모델을 사용
- 더 많은 특징(feature)을 추가
- 학습 알고리즘을 더 오래, 혹은 더 적절하게 학습
- 적절한 하이퍼파라미터 튜닝
시각적 예
- 과소 적합: 데이터의 패턴을 제대로 잡아내지 못하고 단순한 직선으로 모델링.
- 적절한 적합: 데이터의 패턴을 잘 반영하여 적절한 곡선으로 모델링.
- 과대 적합: 데이터의 노이즈까지 반영하여 지나치게 복잡한 곡선으로 모델링.
에포크(Epoch)와 손실 함수(Loss Function)는 기계 학습 모델을 학습시키는 과정에서 중요한 역할을 하는 두 가지 개념이다.
이 두 가지는 서로 밀접하게 관련되어 있으며 모델의 학습 과정을 설명하는 데 있어서 중요하다.
에포크 (Epoch)
정의:
- 에포크는 전체 학습 데이터셋을 모델에 한 번 완전히 학습시키는 단위를 말한다.
- 즉, 한 에포크 동안 모델은 모든 학습 데이터를 한 번씩 사용하여 학습한다.
- 에포크가 여러 번 반복되면서 모델은 데이터의 패턴을 점진적으로 학습하게 된다.
특징:
- 에포크의 수는 모델 학습의 주요 하이퍼파라미터 중 하나이다.
- 에포크가 많을수록 모델은 데이터를 더 많이 학습하지만, 너무 많은 에포크는 과대 적합(overfitting)을 유발할 수 있다.
- 일반적으로 학습 도중 손실 함수의 값을 모니터링하여 적절한 에포크 수를 결정한다.
손실 함수 (Loss Function)
정의:
- 손실 함수는 모델의 예측 값과 실제 값 사이의 차이를 측정하는 함수이다.
- 손실 함수의 값은 모델의 예측이 얼마나 정확한지를 나타내며, 모델의 학습 과정에서 이 값을 최소화하는 방향으로 가중치가 조정된다.
종류:
회귀 문제:
- 평균 제곱 오차(MSE, Mean Squared Error)
- 평균 절대 오차(MAE, Mean Absolute Error)
분류 문제:
- 교차 엔트로피 손실(Cross-Entropy Loss)
- 힌지 손실(Hinge Loss)
특징:
- 손실 함수의 값이 작을수록 모델의 예측이 실제 값에 가깝다는 것을 의미한다.
- 손실 함수는 모델의 학습을 지도하는 역할을 하며, 옵티마이저(optimizer)를 통해 손실 함수의 값을 최소화한다.
에포크와 손실 함수의 관계
에포크와 손실 함수는 모델의 학습 과정에서 밀접하게 관련되어 있다.
모델이 학습 데이터를 통해 반복적으로 학습할 때마다 손실 함수의 값을 계산하고, 이 값을 최소화하는 방향으로 모델의 가중치를 조정한다.
시각적 예
위 그래프는 에포크 수에 따른 학습 손실(Training Loss)과 검증 손실(Validation Loss)의 변화를 보여준다.학습 손실 (Training Loss):
- 에포크가 증가함에 따라 학습 데이터에 대한 손실이 점차 감소하는 모습을 볼 수 있다.
검증 손실 (Validation Loss):
- 초기에는 검증 손실도 감소하지만, 어느 순간부터 다시 증가하기 시작한다.
- 이는 모델이 학습 데이터에 과대 적합(overfitting)되기 시작했음을 나타낸다.
이 그래프를 통해 에포크 수가 적절할 때 손실 함수 값이 최소화되고, 에포크 수가 너무 많으면 과대 적합이 발생할 수 있음을 알 수 있습니다. 이를 기반으로 모델의 학습 중지 시점을 결정할 수 있습니다.
모델 복잡도와 손실 함수의 관계는 기계 학습 모델의 성능과 일반화 능력에 중요한 영향을 미치는 요소이다.
모델 복잡도가 손실 함수 값에 어떤 영향을 미치는지 시각적으로 설명하면, 모델 선택과 과대적합 및 과소적합을 이해하는 데 도움이 된다.
모델 복잡도
모델 복잡도는 모델이 학습할 수 있는 패턴의 다양성과 세부 사항을 나타낸다.
복잡한 모델은 많은 파라미터와 깊은 구조를 가지고 있어 데이터의 복잡한 패턴을 학습할 수 있다.
반면, 간단한 모델은 제한된 수의 파라미터와 단순한 구조를 가지고 있어 데이터의 단순한 패턴만 학습할 수 있다.복잡한 모델:
- 깊은 신경망, 높은 차수의 다항식 회귀, 많은 특성(feature)들
간단한 모델:
- 얕은 신경망, 낮은 차수의 다항식 회귀, 적은 특성들
모델 복잡도와 손실 함수의 관계
모델 복잡도와 손실 함수의 관계는 모델의 학습 데이터와 검증 데이터에 대한 성능을 설명할 수 있다.
1. 과소 적합 (Underfitting):
- 모델이 너무 단순하여 데이터의 패턴을 충분히 학습하지 못하는 상태
- 이 경우 학습 손실과 검증 손실 모두 높음
2. 적절한 적합 (Just Right):
- 모델이 적절한 복잡도로 데이터의 패턴을 잘 학습하는 상태
- 이 경우 학습 손실과 검증 손실이 모두 낮음
3. 과대 적합 (Overfitting):
- 모델이 너무 복잡하여 학습 데이터의 노이즈까지 학습하는 상태
- 이 경우 학습 손실은 매우 낮지만 검증 손실은 높음
시각적 예
위 그래프는 모델 복잡도에 따른 학습 손실(Training Loss)과 검증 손실(Validation Loss)의 변화를 보여준다.학습 손실 (Training Loss):
- 모델 복잡도가 증가할수록 학습 손실이 감소
- 이는 모델이 점점 더 많은 패턴을 학습할 수 있게 되기 때문
검증 손실 (Validation Loss):
- 초기에는 검증 손실도 감소하지만, 모델 복잡도가 너무 높아지면 다시 증가
- 이는 모델이 학습 데이터에 과대 적합(overfitting)되기 시작했음을 나타냄
이 그래프를 통해 모델 복잡도와 손실 함수 간의 관계를 이해할 수 있다.
적절한 모델 복잡도를 선택하는 것이 중요하며, 이를 통해 과대 적합과 과소 적합을 피할 수 있다.
일반적으로 검증 손실이 최소가 되는 모델 복잡도가 최적의 복잡도로 간주된다.
L1 & L2 규제란?
L1 규제와 L2 규제는 머신 러닝 모델의 과대 적합을 방지하고 일반화 성능을 향상시키기 위해 사용되는 정규화 기법이다.
이 두 가지 규제는 손실 함수에 추가적인 페널티(term)를 추가하여 모델의 가중치(weight)를 제어한다.
L1 규제 (L1 Regularization)
정의:
- L1 규제는 손실 함수에 가중치의 절대값 합을 더하는 방법이다.
- 이를 LASSO(Least Absolute Shrinkage and Selection Operator)라고도 한다.
수학적 표현:
- 여기서 는 규제 강도를 조절하는 하이퍼파라미터이다.
특징:
가중치 희소성 (Sparsity):
- L1 규제는 많은 가중치를 0으로 만들어 특성 선택(feature selection)을 자연스럽게 수행한다.
- 즉, 중요하지 않은 특성의 가중치를 0으로 만들어 모델이 더 단순해지고 해석이 용이해진다.
모델 단순화:
- 불필요한 특성을 제거하여 모델을 단순화하고 과대 적합을 방지한다.
사용 예:
- L1 규제는 변수 선택이 중요한 상황에서 주로 사용
- 예를 들어, 유전자 데이터 분석이나 텍스트 데이터에서 중요한 단어를 선택할 때 유용
L2 규제 (L2 Regularization)
정의:
- L2 규제는 손실 함수에 가중치의 제곱합을 더하는 방법이다.
- 이를 Ridge Regression이라고도 한다.
수학적 표현:
- 여기서 는 규제 강도를 조절하는 하이퍼파라미터이다.
특징:
가중치 감소:
- L2 규제는 가중치를 0에 가깝게 만들지만 완전히 0으로 만들지는 않는다.
- 모든 가중치가 조금씩 감소하여 모델의 복잡성을 줄인다.
과대 적합 방지:
- 모델이 과대 적합되는 것을 방지하고, 더 일반화된 모델을 만든다.
사용 예:
L2 규제는 모든 특성이 중요한 경우나, 데이터가 선형적인 관계를 가지고 있는 경우에 주로 사용된다.
L1 규제와 L2 규제의 비교
특징 L1 규제 L2 규제 수식 결과 많은 가중치를 0으로 만듦 모든 가중치를 0에 가깝게 만듦 해석 가능성 높음 (희소성으로 인해) 낮음 계산 효율성 낮음 (희소성 유도) 높음 주로 사용되는 경우 특성 선택이 필요할 때 모든 특성이 중요한 경우
혼합 규제 (Elastic Net)
L1 규제와 L2 규제를 혼합한 Elastic Net 규제도 있다.
이는 두 규제의 장점을 결합하여 사용힌다.수학적 표현:
- 여기서 , , 는 조절 파라미터이다.
Elastic Net은 L1과 L2 규제의 장점을 모두 활용할 수 있어, 특성 선택과 과대 적합 방지를 동시에 할 수 있다.
L1 L2 규제 정리
L1 규제:
- 그레디언트에서 alpha에 가중치의 부호를 곱하여 그레디언트에 더함
L2 규제:
- 그레디언트에서 alpha에 가중치를 곱하여 그레디언트에 더함
l1_list = [0.0001, 0.001, 0.01]
for l1 in l1_list:
lyr = SingleLayer(l1=l1)
lyr.fit(x_train_scaled, y_train, x_val=x_val_scaled, y_val=y_val)
plt.figure(figsize=(15,8))
plt.plot(lyr.losses)
plt.plot(lyr.val_losses)
plt.title('Learning Curve (l1={})'.format(l1))
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train_loss', 'val_loss'])
plt.ylim(0, 0.3)
plt.show()
plt.figure(figsize=(15,8))
plt.plot(lyr.w, 'bo')
plt.title('Weight (l1={})'.format(l1))
plt.ylabel('value')
plt.xlabel('weight')
plt.ylim(-4, 4)
plt.show()
print(lyr.w)
### 출력 결과
횟수를 반복할 수록 0에 수렴하는 것을 볼 수 있다.
layer5 = SingleLayer(l1=0.001)
layer5.fit(x_train_scaled, y_train, epochs=20)
layer5.score(x_val_scaled, y_val)
### 출력 결과
0.989010989010989
L1 규제 시각화 분석
Learning Curve (l1):
- 에포크에 따른 학습 손실과 검증 손실의 변화를 보여준다.
- L1 규제가 커질수록(즉, l1 값이 커질수록) 학습 손실과 검증 손실이 높아질 수 있으며, 이는 규제가 강해짐에 따라 모델이 더 보수적으로 학습됨을 의미한다.
Weight (l1):
- 학습된 가중치의 값을 보여준다.
- L1 규제는 많은 가중치를 0으로 만든다.
- 이를 통해 불필요한 특성들을 제거하고 중요한 특성만 남게 된다.
- 규제 강도가 높아질수록(즉, l1 값이 커질수록) 더 많은 가중치가 0으로 수렴하는 것을 확인할 수 있다.
l2_list = [0.0001, 0.001, 0.01]
for l2 in l2_list:
lyr = SingleLayer(l2=l2)
lyr.fit(x_train_scaled, y_train, x_val=x_val_scaled, y_val=y_val)
plt.figure(figsize=(15,8))
plt.plot(lyr.losses)
plt.plot(lyr.val_losses)
plt.title('Learning Curve (l2={})'.format(l2))
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train_loss', 'val_loss'])
plt.ylim(0, 0.3)
plt.show()
plt.figure(figsize=(15,8))
plt.plot(lyr.w, 'bo')
plt.title('Weight (l2={})'.format(l2))
plt.ylabel('value')
plt.xlabel('weight')
plt.ylim(-4, 4)
plt.show()
print(lyr.w)
### 출력 결과
횟수를 반복 할 수록 0에 수렴하나 값이 0이 되지는 않는 모습을 볼 수 있다.
layer6 = SingleLayer(l2=0.01)
layer6.fit(x_train_scaled, y_train, epochs=50)
layer6.score(x_val_scaled, y_val)
### 출력 결과
0.989010989010989
L2 규제 시각화 분석
Learning Curve (l2):
- 에포크에 따른 학습 손실과 검증 손실의 변화를 보여준다.
- L2 규제가 커질수록(즉, l2 값이 커질수록) 학습 손실과 검증 손실이 증가할 수 있다.
- 이는 과적합을 방지하고 모델을 일반화하는 데 도움이 된다.
Weight (l2):
- 학습된 가중치의 값을 보여준다.
- L2 규제는 가중치를 0에 가깝게 만든다.
- 이는 모든 가중치의 크기를 줄여 과적합을 방지한다.
- 규제 강도가 높아질수록(즉, l2 값이 커질수록) 가중치 값이 작아지는 경향이 있다.
교차 검증이란?
교차 검증(Cross-validation)은 기계 학습 모델의 성능을 평가하고 과적합(overfitting) 및 과소적합(underfitting)을 방지하기 위해 데이터를 여러 개의 폴드(fold)로 나누어 사용하는 기법이다.
이를 통해 모델의 일반화 성능을 보다 정확하게 추정할 수 있다.
k-폴드 교차 검증
- 데이터를 k개의 폴드로 나눈다.
- 각 폴드는 한 번씩 테스트 데이터로 사용되고, 나머지 폴드는 학습 데이터로 사용된다.
- 모델을 k번 학습하고 평가하여, 최종 성능은 k번의 평가 결과의 평균으로 계산된다.
- 예를 들어, 5-폴드 교차 검증에서는 데이터를 5개의 폴드로 나누고, 각 폴드가 한 번씩 테스트 데이터로 사용된다.
교차 검증의 장점
1. 일반화 성능 평가:
- 모델이 보지 못한 데이터에 대해 얼마나 잘 동작하는지 평가할 수 있다.
- 과적합 및 과소적합 여부를 더 정확하게 판단할 수 있다.
2. 데이터 활용도 향상:
- 데이터를 여러 번 사용할 수 있어, 데이터가 적은 경우에도 효과적으로 모델을 평가할 수 있다.
3. 하이퍼파라미터 튜닝:
- 다양한 하이퍼파라미터 조합을 평가하여 최적의 모델 설정을 찾는 데 유용하다.
교차 검증의 단점
1. 계산 비용:
- 데이터가 많거나 모델이 복잡할 경우 계산 비용이 높아질 수 있다.
2. 결과 변동:
- 데이터의 분할에 따라 결과가 다를 수 있으며, 이를 줄이기 위해 여러 번 반복하여 평균 성능을 계산할 수 있다.
validation_scores = []
k = 10
bins = len(x_train_all) // k
for i in range(k):
start = i*bins
end = (i+1)*bins
val_fold = x_train_all[start:end]
val_target = y_train_all[start:end]
train_index = list(range(0, start))+list(range(end, len(x_train_all)))
train_fold = x_train_all[train_index]
train_target = y_train_all[train_index]
train_mean = np.mean(train_fold, axis=0)
train_std = np.std(train_fold, axis=0)
train_fold_scaled = (train_fold - train_mean) / train_std
val_fold_scaled = (val_fold - train_mean) / train_std
lyr = SingleLayer(l2=0.01)
lyr.fit(train_fold_scaled, train_target, epochs=50)
score = lyr.score(val_fold_scaled, val_target)
validation_scores.append(score)
print(np.mean(validation_scores))
### 출력 결과
0.9777777777777779
validation_scores
### 출력 결과
[0.9333333333333333,
1.0,
1.0,
1.0,
0.9555555555555556,
1.0,
0.9555555555555556,
0.9777777777777777,
1.0,
0.9555555555555556]
from sklearn.model_selection import cross_validate
sgd = SGDClassifier(loss='log_loss', penalty='l2', alpha=0.001, random_state=42)
scores = cross_validate(sgd, x_train_all, y_train_all, cv=10)
print(np.mean(scores['test_score']))
### 출력 결과
0.850096618357488
scores
### 출력 결과
{'fit_time': array([0.0014081 , 0.00157809, 0.00114322, 0.00122428, 0.00095892,
0.00114703, 0.00113106, 0.00111413, 0.00104666, 0.0008049 ]),
'score_time': array([0.00023317, 0.00035405, 0.0001936 , 0.00019407, 0.00020623,
0.00019503, 0.00022793, 0.0001719 , 0.00016618, 0.00016403]),
'test_score': array([0.86956522, 0.93478261, 0.7826087 , 0.95652174, 0.91304348,
0.75555556, 0.88888889, 0.6 , 0.95555556, 0.84444444])}
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
pipe = make_pipeline(StandardScaler(), sgd)
scores = cross_validate(pipe, x_train_all, y_train_all, cv=10, return_train_score=True)
print(np.mean(scores['test_score']))
### 출력 결과
0.9694202898550724