Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20240826 파인튜닝(Fine-Tuning) 본문

개발Article

[TIL]20240826 파인튜닝(Fine-Tuning)

최밤빵 2024. 8. 26. 21:48

파인튜닝(Fine-Tuning)은 머신러닝, 특히 딥러닝 모델에서 사전 학습된 모델을 특정 작업에 맞게 추가로 학습시키는 과정이다. 파인튜닝을 통해 기본적인 패턴을 이미 학습한 모델을 사용해서 새롭고 특정한 데이터에 대해 더 나은 성능을 얻을 수 있다. 예를 들어, 기본적으로 이미지 인식을 학습한 모델을 기반으로 고양이와 개를 구분하는 모델을 파인튜닝할 수 있다. 예시 코드는 지금 구현하고있는 결제기능에서 서비스클래스 코드를 예시 코드로 사용했다. 

 

▶ 파인튜닝의 개념과 필요성

 

▷ 파인튜닝의 개념
파인튜닝은 사전 학습된 모델을 재사용하여 특정 도메인이나 데이터셋에 맞게 최적화하는 과정이다. 사전 학습된 모델은 대규모 데이터셋으로 일반적인 패턴을 학습하며, 이를 "전이 학습(Transfer Learning)"이라고 한다. 파인튜닝은 이 전이 학습을 기반으로 모델의 일부 레이어를 동결(freeze)하거나 새로운 데이터를 추가로 학습시켜서 모델을 세밀하게 조정하는 작업을 포함한다.

 

→ 필요성
파인튜닝은 다음과 같은 이유로 필요하다.

 

1. 빠른 학습 시간:

사전 학습된 모델은 기본적인 패턴을 이미 학습했기 때문에, 처음부터 새롭게 학습하는 것보다 훨씬 빠른 시간 내에 학습을 완료할 수 있다.

2. 적은 데이터로도 좋은 성능:

파인튜닝은 기존 모델의 지식을 활용하기 때문에, 적은 양의 데이터로도 좋은 성능을 얻을 수 있다.

3. 리소스 절약:

대규모 모델을 처음부터 학습시키려면 많은 리소스와 시간이 필요하지만, 파인튜닝은 기존 모델을 재사용하기 때문에 리소스를 절약할 수 있다.

 

▶파인튜닝을 위한 주요 단계

파인튜닝을 수행하기 위한 주요 단계는 다음과 같다.

 

→ 사전 학습된 모델 로드:

파인튜닝을 수행하기 위해 먼저 사전 학습된 모델을 불러온다.

→ 모델 아키텍처 수정:

문제에 맞게 모델의 마지막 레이어를 수정하거나 새로운 레이어를 추가한다.

→ 동결 및 학습:

모델의 일부 레이어를 동결(freeze)하여 기본적인 패턴은 그대로 유지하면서, 새로 추가된 레이어만 학습시킨다.

→ 모델 컴파일 및 학습:

수정된 모델을 컴파일하고 새로운 데이터로 학습을 수행한다.

 

▶예시 코드

지금 구현하고있는 결제 기능 서비스 클래스 코드에서 completePayment 메서드를 파인튜닝 개념에 맞게 간단한 예시로 작성했다. 이 예시에서는 사전 학습된 모델(여기서는 기존 결제 처리 로직)을 새로운 조건에 맞게 파인튜닝하는 과정을 보여준다.

▽ 코드 예시: 결제 금액 검증 파인튜닝

@Service
public class PaymentService {

    private final PortOneConfig portOneConfig;
    private final RestTemplate restTemplate;
    private final PaymentRepository paymentRepository;

    public PaymentService(PortOneConfig portOneConfig, RestTemplate restTemplate, PaymentRepository paymentRepository) {
        this.portOneConfig = portOneConfig;
        this.restTemplate = restTemplate;
        this.paymentRepository = paymentRepository;
    }

    // 파인튜닝된 결제 완료 메서드
    @Transactional
    public PaymentResponseDto completePayment(PaymentRequestDto paymentRequestDto) {
        String url = portOneConfig.getApiUrl() + "/payments/" + paymentRequestDto.getImpUid();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + portOneConfig.getApiKey());

        ResponseEntity<PaymentResponseDto> response = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), PaymentResponseDto.class);

        if (response.getStatusCode() == HttpStatus.OK) {
            PaymentResponseDto paymentResponseDto = response.getBody();

            // 새로운 조건 추가: 결제 금액이 특정 금액 이상인 경우만 처리 (파인튜닝)
            if (paymentResponseDto.getAmount() >= 10000) {
                Payment payment = new Payment(
                        paymentRequestDto.getImpUid(),
                        paymentResponseDto.getAmount(),
                        paymentResponseDto.getStatus(),
                        paymentResponseDto.getReceiptUrl(),
                        "결제 완료",
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                );
                paymentRepository.save(payment);
                return paymentResponseDto;
            } else {
                throw new RuntimeException("결제 금액이 너무 낮습니다.");
            }
        } else {
            throw new RuntimeException("결제 완료 처리 실패: " + response.getStatusCode());
        }
    }
}

→ 이 예시에서는 결제 금액이 특정 기준 이상인 경우에만 결제 완료 처리를 수행하는 조건을 추가했다. 기존의 결제 처리 로직을 유지하면서 새로운 조건을 추가해서, 이 메서드는 특정 요구 사항에 맞게 파인튜닝 한 건데... 이 방식보단 좀 더 현실적인 예시가 필요하다..! 

 

▽ 수정된 코드 예시 : (결제기능 최적화와 개선) 결제 상태와 금액에 따른 최적화 로직 추가 

특정 결제 상태와 금액에 따라 다른 로직을 수행하는 방법으로 개선한 예시 코드 

@Service
public class PaymentService {

    private final PortOneConfig portOneConfig;
    private final RestTemplate restTemplate;
    private final PaymentRepository paymentRepository;

    public PaymentService(PortOneConfig portOneConfig, RestTemplate restTemplate, PaymentRepository paymentRepository) {
        this.portOneConfig = portOneConfig;
        this.restTemplate = restTemplate;
        this.paymentRepository = paymentRepository;
    }

    // 결제 완료 메서드: 결제 상태와 금액에 따른 로직 추가
    @Transactional
    public PaymentResponseDto completePayment(PaymentRequestDto paymentRequestDto) {
        String url = portOneConfig.getApiUrl() + "/payments/" + paymentRequestDto.getImpUid();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + portOneConfig.getApiKey());

        ResponseEntity<PaymentResponseDto> response = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), PaymentResponseDto.class);

        if (response.getStatusCode() == HttpStatus.OK) {
            PaymentResponseDto paymentResponseDto = response.getBody();

            // 결제 상태 확인 후 처리 로직 추가
            if ("paid".equals(paymentResponseDto.getStatus()) && paymentResponseDto.getAmount() > 5000) {
                // 결제 완료 처리
                Payment payment = new Payment(
                        paymentRequestDto.getImpUid(),
                        paymentResponseDto.getAmount(),
                        paymentResponseDto.getStatus(),
                        paymentResponseDto.getReceiptUrl(),
                        "결제 완료",
                        LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                );
                paymentRepository.save(payment);
            } else if ("cancelled".equals(paymentResponseDto.getStatus())) {
                // 결제 취소 처리
                // 결제 취소 관련 로직 추가
                throw new RuntimeException("결제가 취소되었습니다.");
            } else {
                throw new RuntimeException("잘못된 결제 상태입니다.");
            }

            return paymentResponseDto;
        } else {
            throw new RuntimeException("결제 완료 처리 실패: " + response.getStatusCode());
        }
    }
}

→ 이 예시코드에서는 결제 상태와 결제 금액에 따른 다양한 비즈니스 로직을 처리하는 방식을 추가했다. 결제가 완료된 상태("paid")이면서 특정 금액을 초과하는 경우에만 저장하고, 결제가 취소된 상태("cancelled")라면 예외를 발생시키는 구조로, 이런 방식은 실제 결제 시스템에서 상황에 따라 다양한 로직을 추가하고 최적화할 때 유용하다. 이 예시를 통해 결제 기능에서 여러 가지 상황을 고려한 로직을 구현하는 것이 어떻게 이루어지는지 이해할 수 있다. 실제 서비스에서 결제 처리에 필요한 다양한 경우를 예측하고 처리하는 것이 중요한 학습 포인트가 될 것이다.

 

▶ 파인튜닝의 장점:

기존 코드 재사용으로 빠른 개발 가능.

새로운 요구 사항에 맞는 기능 확장 가능.

유지보수와 수정이 용이해짐.

 

▶ 파인튜닝의 단점:

복잡한 구조의 경우, 잘못된 수정이 기존 기능에 영향을 줄 수 있다.

사전 학습된 모델의 한계에 묶여 있을 수 있다.

 

▶ 정리

파인튜닝의 개념과 필요성, 주요 단계, 그리고 간단한 결제 금액 검증을 추가한 코드 예시를 통해 파인튜닝을 알아봤다. 기존 코드나 모델을 새롭게 학습시키는 대신, 특정 요구 사항에 맞게 미세 조정하는 방법과, 파인튜닝의 장단점도 알아봤는데, 지금 내 결제 코드도 완성된 게 아니라서 예시를 들면서도 이게 맞나...? 이걸 예시로 써도 되나 라는 생각이 많이 들었다. 하지만 그냥 던져주는 예시보다는 내가 작성한 코드에서 이해하고싶어서 적용해봤는데, 결제기능이 완성되면 관련 코드들을 파인튜닝해보는 것도 좋은 공부가 될 것 같다.