언제 validation data 를 체크하는 시점

Overftting : 뉴럴 네트워크를 학습하다보면, 오버피팅이 쉽게 일어난다.

오버피팅은 training set을 너무 많이 학습해서, training set에대한 error는 매우 줄어들지만, 문제에대한 일반성이 떨어져서 test set 또는 validation set(오버피팅 여부를 확인하기위해 error를 측정하기위한 데이터 셋으로, test set도 아니고 training set도 아닌 데이터)에대한 error가 증가하는 것을 의미한다.

Regularization : 이러한 현상은 weight가 처음에는 매우 작게 세팅되어 있다가, 점점 커지는 것과 비례해서 오버피팅이 일어나는 것으로 해석된다.

따라서 이것을 막기위해 weight decay(=regularization) 을 사용한다.

이것은 Error에대한 정의를 수정해서, W의 제곱만큼을 다시 빼줌으로써 weight가 너무 빠르게 상승하는 것을 방지한다.

weight decay를 쓰면 test set 에대한 성능향상이 많이 일어난다고 한다.
(물론 validation set 을 따로 분리하면, 학습할 training set이 그만큼 줄어드는 것이지만, 이는 학습을 언제 끝낼 것인가를 결정하게 해주어 결과적으로 test set에 대한 generalization이 좋아지는 효과를 낸다.)

validation set을 통해 error가 최소인 지점을 찾을때 주의할 점은, 항상 U자 형태의 그래프로 error가 나타나지 않는 다는 것이다. W자 형태로 error가 변하는 경우등이 있으니 조심해서 결정해야 한다.

또한 validation set 이 어떤 것이냐에 따라, error 변화의 양상이다를 수 있다.

그렇기 때문에 k-cross-validation 을 써서, K등분한 셋 중 1개씩을 validation 으로 바꿔가면서 error의 평균 변화양상을 측정한다.

그리고 나서 test set의 error가 최소가 되는 최적 iteration 을 찾고, 그때는 validation set 없이 전부 training set 으로 학습한다.

Drop out : 현 시점에서 가장 강력한 Regularization 기법의 하나이다. Deep Neural Network의 학습 단계에서(Test에서는 아님) 각각의 Hidden Layer에서 랜덤하게 몇개의 Neuron 들만(보통 50%) 사용하여 학습하는 것이다. 이렇게 할 경우 매우 다양한 장점이 있다.

매 학습시 마다 랜덤하게 50%의 Neuron들만 사용하여 학습 할 경우, nC2 가지 만큼의 조합이 탄생하게 된다. 따라서 하나의 거대한 DNN에 여러개의 작은 뉴럴 넷이 앙상블되어지는(합쳐진) 효과가 있다.

앙상블을 할 경우 일반적으로 Overftting이 크게 줄어든다고 알려져 있다.(하나의 Classifier에 의해서만 결정되지 않고 여러개의 Classifier가 같이 결정하기 때문에 편견(Bias)가 줄어든다.)

또한 Drop out을 사용 할 경우 비슷한 Weight를 갖는 뉴런들이 줄어들게 되어, 중복된 판단을 하는 뉴런 들이 줄어들게 되어 각 뉴런을 100%(실제론 아니지만) 활용할 수 있게 된다고 한다.

ReLU(Rectified Linear Unit) : 이 또한 Sigmoid를 대신하는 현 시점에서 가장 강력한 Activation Function 이다. 아래 그래프처럼 매우 간단한 두개의 직선 (y=x 과 y=ax)으로 이루어진 ReLU는 간단한 y=max(0,x) 함수로 정의 된다. 이것이 강력한 이유는 우선 Gradient Descent를 하기위해 합성 함수미분을 하게되고, 그 결과 Activation Function을 미분해서 곱해야 하는데 기존의 Sigmoid는 기울기가 최대 1/4이기 때문에 Gradient Descent를 여러 Layer로 해나갈 때 마다 Error가 (1/4)^2 씩 소멸되는(Vanish) 문제가 있다.

그러나 ReLU의 경우 기울기가 0 or 1로 학습이 되는 경우는 100% Error가 전파되어 이러한 문제가 없다.
또한 Activation 값이 sigmoid 처럼 [0,1]로 제한되는 것이 아니고 무제한이기 때문에 좀 더 확실한 표현력을 가진다고 볼 수 있다. 그 결과 Regularization 도 강력하고 Representation 도 강력한 효과를 지니게 된다.

이 글은 Bean Validation 과 그 구현체인 Hibernate Validator 그리고 Spring Validation 에 대해서 다루고 있습니다.

Java Bean Validation

출처 Hibernate Validator 6.0.11.Final — JSR 380 Reference Implementation: Reference Guide

일반적으로 데이터 검증 (Validation) 은 여러 계층에 걸쳐서 이루어지게 됩니다.
거의 동일한 내용의 검증로직이 각 계층별로 구현된다면 그것은 중복이고 낭비가 심한 작업일것입니다.
또한 그러한 경우 각 계층별로 구현된 검증로직간 불일치로 인하여 오류가 발생하기도 쉽습니다.

출처 Hibernate Validator 6.0.11.Final — JSR 380 Reference Implementation: Reference Guide

이를 해결하기 위하여 데이터 검증을 위한 로직을 도메인 모델 자체에 묶어서 표현하는 방법이 있습니다.
실제 코드로 표현된다면 너무 장황하고 복잡할것이기 때문에, Java 에서는 Bean Validation 라는 이름으로 애노테이션 (Annotation) 을 데이터 검증을 위한 메타데이터로 사용하는 방법을 제시하고 있습니다.

Bean Validation 명세는 현재 2.0 (JSR 380) 까지 나와있습니다.

  • Bean Validation 1.0 (JSR-303)
  • Bean Validation 1.1 (JSR-349)
  • Bean Validation 2.0 (JSR-380)

Hibernate Validator

Hibernate Validator 는 Bean Validation 명세에 대한 구현체입니다.

Bean Validation 2.0 에 대한 구현은 Hibernate Validator 6.0.1.Final 이며 Spring Boot 2.0 이상에서 이것을 사용하고 있습니다.

제약조건의 작성

위에서 언급했던것처럼 Bean Validation 은 애노테이션을 사용하여 제약조건을 명시하게 됩니다.
즉 아래와 같은 모습이 됩니다.

위 제약조건에 의하면 name 은 null 이거나 빈문자열이어서는 안되며, age 는 0 이상이어야만 합니다.

Java 에서 기본적으로 제공해주는 제약조건들이 있으며, Hibernate 에서 추가적으로 제공하는 제약조건이나 필요한 경우 직접 제약조건을 만드는것도 가능합니다.

필드, 클래스, 메소드 또는 파라메터에 애노테이션을 작성하는게 가능하며, 위치에 따라 제약조건의 적용 범위가 달라지게 됩니다.

Java 와 Hibernate 에서 제공하는 제약조건들은 Hibernate Validator 6.0.12.Final#builtin-constraints 에서 확인하실 수 있습니다.

제약조건에 대한 유효성검증

제약조건의 확인은 javax.validation.Validator 를 사용해서 이루어집니다.

가장 쉽고 빠르게 Validator 를 가져오는 방법은 ValidatorFactory 를 사용하는 방법입니다.

그리고 validate() 를 사용해서 빈의 유효성검증을 실행합니다.
제약조건을 위반한 내용은 ConstraintViolation 인터페이스로 반환됩니다.

제약조건 위반내용에 대한 확인

위반내용은 아래와 같이 ConstraintViolation 인터페이스에 포함되어 있습니다.

위에 언급했던 Person 을 사용한 예제는 아래와 같습니다.

그룹핑된 제약조건

Bean Validation 1.1 (JSR-349) 부터 포함된 내용으로, 그룹을 사용하여 유효성검증을 위한 세트를 제한할 수 있습니다.

그룹에 해당하는 인터페이스 (혹은 클래스) 를 생성하고, 해당 그룹세트에 해당하는 제약조건 애노테이션의 groups 에 인터페이스를 넣으면 됩니다.

위 코드는 PersonGroups.Driver 그룹세트에 포함된 경우, age 가 18 이상이어야 하고 driverLicenseNumber 가 있어야 한다는 제약조건을 명시하고 있습니다.

제약조건은 복수의 그룹세트에 포함될 수 있습니다.

그리고 name 에 선언된 @NotEmpty 또는 age 에 선언된 @Positive 와 같이 groups 가 지정되지 않은 제약조건의 경우 javax.validation.groups.Default 그룹세트에 포함되어 동작하게됩니다.

특정 그룹세트에 위반되는 사항들을 확인하기 위해서는 validate() 에 PersonGroups.Driver 그룹클래스를 추가적으로 전달하면 됩니다.

하지만 대부분의 경우는 javax.validation.groups.Default 에 해당하는 제약조건들도 같이 확인하기를 원할것입니다.

따라서 javax.validation.groups.Default 와 PersonGroups.Driver 2개 그룹세트를 넣어주는 방식으로 사용하시면 됩니다.

매번 javax.validation.groups.Default 그룹세트를 추가하는것이 번거롭다면, PersonGroups.Driver 가 javax.validation.groups.Default 를 확장클래스로 만들면 됩니다.

그렇다면 validate() 시 PersonGroups.Driver동 만을 넘겨주더라도 javax.validation.groups.Default 포함하여 유효성검증을 하게 됩니다.

필드에 대한 제약조건과 속성에 대한 제약조건

유효성검사에서 필드와 속성에 대한 제약조건을 구분해서 생각할 필요성이 있습니다.

위와 같은 코드에서 1번 제약조건의 경우는 필드에 대한 제약조건입니다. getName() 이라는 name 필드에 대한 접근자가 있지만 무시되며 “송정훈” 이라는 값에 대해서 유효성검사가 진행되게 됩니다.

2번 제약조건은 속성에 대한 제약조건입니다. “이름은 송정훈” 이라는 값에 대해서 유효성검사가 진행됩니다.

getXXX 또는 isXXX 와 같은 이름의 응답값을 가진 메소드들이 속성에 속하게되고, 빈에 대한 유효성검사를 진행하면 속성들에 대해서도 검사가 진행이 되게 됩니다.

계단식 유효성검사 (cascaded validation)

계단식 유효성검사를 위해서는 해당되는 필드에 @Valid 를 추가해 주셔야 합니다.

위 예제에서는 driver 필드에 @Valid 애노테이션이 선언되어 있습니다.

따라서 driver 객체에 대한 유효성검사도 이뤄지게 됩니다.

컨테이너 요소에 대한 유효성검사

Iterable (Set, List 등), Map, Optional 과 같은 컨테이너형의 요소들에 대한 유효성검사가 Bean Validation 2.0 (JSR-380) 에 포함되었습니다.

유효성검사를 진행하기 위한 요소 앞에 애노테이션을 추가하면 되는데, 위에 언급한 Car 에서 여러명의 (하나 이상의) 운전자가 있어야 한다면 아래와 작성하게 됩니다.

그룹전환

우리는 Car 에 대해서 유효성검사를 진행할때 drivers 는 PersonGroups.Driver 그룹세트로 유효성검사가 이루어지기를 바라겠지만, 아쉽게도 Car 와 동일하게 javax.validation.groups.Default 에 대한유효성검사만 이뤄지게 됩니다.

이런경우에 @ConvertGroup 을 사용하여 그룹전환을 제약조건에 추가하는것이 유용합니다.

참고로 @ConvertGroup 에서 from 은 아무것도 적지 않으면 javax.validation.groups.Default 를 가지게 됩니다.

따라서 위의 경우 from 을 생략하셔도 됩니다.

매개변수에 대한 유효성검사

메소드의 매개변수 또는 응답값에 대한 유효성검사도 가능합니다.

먼저 매개변수에 대한 제약조건은 아래와 같이 작성하면 됩니다.

매개변수에 대한 유효성검사는 ExecutableValidator 를 사용하게 됩니다.

응답값에 대한 유효성검사

메소드에 응답값에 대한 유효성검사는 아래와 같이 가능합니다.

위에서 나온 속성에 대한 유효성검사와는 다르게, 이 경우에는 ExecutableValidator를 사용하게 됩니다.

복수의 매개변수에 대한 유효성검사

매개변수 하나하나가 아닌, 여러 매개변수에 대한 유효성검증이 필요할 경우가 있습니다.

이럴때는 사용자정의 애노테이션과 ConstraintValidator 를 만드는 방법으로 해결이 가능합니다.

위 update 메소드는 18세 미만의 경우 운전면허번호는 null 만 허용해야 합니다. 즉 2개의 매개변수를 같이 검증해야 합니다.

우선 검증을 위한 사용자정의 애노테이션을 추가하였습니다.

이 제약조건에 대한 유효성을 검증하기 위하여 PersonUpdateValidator 를 사용하도록 되어있습니다.

이 검사기는 age (첫번째 매개변수) 가 18 이상인경우 유효하지만, 미만인 경우에는 운전면허번호 (두번째 매개변수) 가 null 이어야만 유효한것으로 판단합니다.

이 사용자정의 ConstraintValidator 은 @SupportedValidationTarget 을 ValidationTarget.PARAMETERS 로 하여 매개변수에 대한 유효성검증기임을 나타내고 있습니다.

제약조건의 합성

여러 제약조건 애노테이션을 합성한 애노테이션을 만드는것도 가능합니다.

boolean-constraint-composition 를 참고하시기 바랍니다.

스크립트를 사용한 유효성검증

Hibernate 에서 제공하는 @ScriptAssert 와 같은것을 사용하여, 스크립트를 사용한 유효성검증도 가능합니다.

예를든다면 18세 미만인 경우 운전면허번호가 null 이어야 하는 제약조건은 아래와 같이 작성하는게 가능합니다.

Spring Validation

Spring 에서는 Java Bean Validation 을 완벽하게 지원하면서, 검사기를 직접 사용하지 않고 AOP 와 같은 방식으로 더 편리하게 유효성검사를 할 수 있는 장치들을 제공해주고 있습니다.

그것들에 대해 알아보도록 하겠습니다.

Validated

Bean Validation 에서 @Valid 를 사용하여 유효성검사가 진행될 대상을 지정하고는 하였습니다.

하지만 이것은 어떤 그룹세트에서 유효성검사가 일어날지 표현하기 적합하지는 않습니다.

Spring 에서는 Validator.validate() 를 사용하여 유효성검사를 진행하기보다는 AOP 와 같은 방법을 사용하므로 진입점이 되는 애노테이션에 그룹세트를 명시적으로 지정해야 합니다.

따라서 스프링에서는 유효성검사에 진입하게되는 지점에 @Validated 라는 애노테이션을 사용하는 방법을 제공하고 있습니다.

Spring 에서 메소드에 대한 유효성검사

AOP 를 사용하여 메소드를 실행할때 유효성검사를 실행하는것이 가능합니다.

MethodValidationPostProcessor 를 빈을 정의하고, 필요한 클래스 혹은 메소드에 @Validated 애노테이션을 추가하면 됩니다.

만약 제약조건에 위반되는 내용이 발견된다면 ConstraintViolationException 이 발생하게 됩니다.

SpringBoot 의 ValidationAutoConfiguration 를 살펴보면 이미 MethodValidationPostProcessor 빈이 정의되어 있다는것을 알 수 있습니다.

Spring Validator

Spring 내부에서 사용하는 독자적인 Validator 인터페이스가 있습니다.

org.springframework.validation.Validator 로서 아래와 같은 코드를 가지고 있습니다.

기본적으로 Spring 에서는 Java Bean Validation 을 지원하는 완벽하게 지원하는 LocalValidatorFactoryBean 과 SpringValidatorAdapter 을 제공하고 있습니다.

즉 SpringValidatorAdapter 는 org.springframework.validation.Validator 이외에도 javax.validation.ValidatorFactory, javax.validation.Validator 의 구현체로 동작합니다.

SpringBoot 의 ValidationAutoConfiguration 를 살펴보면 LocalValidatorFactoryBean 가 defaultValidator 로 동작한다는것을 알 수 있습니다.

Spring MVC 에서 유효성검사

Spring MVC 에서 Data Binding 시점에 유효성검사를 실행하게 됩니다.

이 부분은 WebMvcConfigurationSupport 또는 WebFluxConfigurationSupport 를 보시면 좀 더 자세하게 아실 수 있습니다. ConfigurableWebBindingInitializer 를 생성할때 Validator 를 주입하는걸 확인하실 수 있습니다.

바인딩에 실패한경우 org.springframework.web.bind.MethodArgumentNotValidException 이 발생하게 되는데, 만약 필요하다면 이 예외에 대한 ExceptionHandler 를 만들어서 처리하는것이 가능합니다.

ExceptionHandler 를 사용하지 않고 직접 BindingResult 를 받아서 처리하는것도 가능합니다.

사용자정의 Validator 를 사용하여 처리하는것도 가능합니다.

org.springframework.validation.Validator 를 확장한 Validator 를 만들어서 아래와 같이 추가해주도록 합니다.

Toplist

최신 우편물

태그