AI/Projects

Projects Joing: Profile Evaluation 로직변경 - FastAPI 적용

문괜 2024. 11. 16. 20:42
반응형

일전의 포스트에서 말했듯이 두 가지 문제가 있었다.

  1. Selenium을 사용하기에 한 가지의 보조기능을 위해 너무 많은 클라우드 컴퓨팅 리소스가 Chrome Driver에서 사용된다.
  2. 실제로 이미지 평가가 정확성을 떠나 작동하지 않는 경우가 있다.

먼저 Youtube Data API를 활용하여 첫 번째 문제의 경우 확실히 해결을 했다. 그리고 두 번째 문제의 경우 Youtube Data API에서 Thumdnail과 동시에 조회해 올 수 있는 title과 description을 활용하여 text_evaluation을 추가하기로 했다. 그래서 아래와 같은 로직으로 FastAPI로 전환을 마무리했다.

 

  1. Youtube Data API로 최근에 업로드한 영상 4개 조회
  2. 해당 영상들읠 Title, Description 그리고 Thumbnail을 전처리 진행
  3. 먼저 Image에 대한 평가를 진행
  4. 평가에서 불합격의 경우 바로 결과 반환 하지만 합격 혹은 평가 불가의 경우 재평가 진행

그래서 해당 로직의 FastAPI의 service.py는 아래와 같이 구현하였다.

def profile_evaluation(request: ProfileEvaluationRequestDto):
    # Getting Youtube Data API Object
    youtube_data_api = youtube_data_api_request(api_key=YOUTUBE_API_KEY)

    # Getting Channel Info
    channel_response = youtube_channel_request(
        youtube_data_api=youtube_data_api,
        channel_id=request.channel_id)

    # Getting Playlist
    playlist_response = playlist__request(
        youtube_data_api=youtube_data_api,
        youtube_channel=channel_response)

    # Parsing response aka preprocessing
    videos_text_info, thumbnail_urls = response_preprocessing(
        playlist_response=playlist_response)
    
    evaluation_prompt = EvaluationPrompt
    
    # Image Evaluation
    # Image request & preprocessing
    image_response = image_request(thumbnail_urls)
    combined_image = image_preprocessing(image_response)
    
    image_evaluation_prompt = evaluation_prompt.image_evaluation_prompt.value
    try:    
        image_evaluation_result = image_evaluation(combined_image, image_evaluation_prompt)
        if (not image_evaluation_result['appropriate'] and len(image_evaluation_result['reason']) != 0):
            return ProfileEvaluationResponseDto(
                evaluation_status=False,
                reason=image_evaluation_result['reason']
            )
    except Exception as e:
        print(e)

    # Text Evaluation
    text_evaluation_prompt = evaluation_prompt.text_evaluation_prompt.value
    text_evaluation_result = text_evaluation(
        description=videos_text_info, prompt=text_evaluation_prompt)
    return ProfileEvaluationResponseDto(
        evaluation_status=text_evaluation_result['appropriate'],
        reason=text_evaluation_result['reason']
    )

 

추가적으로 구현한 요청함수와 전처리 함수는 아래와 같다.

요청함수 모음: request_methods.py

def youtube_data_api_request(api_key):
    youtube_data_api = googleapiclient.discovery.build(
        'youtube', 'v3', developerKey=api_key)
    return youtube_data_api


def youtube_channel_request(youtube_data_api, channel_id):
    channel_response = youtube_data_api.channels().list(
        part='contentDetails',
        id=channel_id
    ).execute()
    return channel_response


def playlist_request(youtube_data_api, youtube_channel):
    uploads_playlist_id = youtube_channel["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"]
    playlist_response = youtube_data_api.playlistItems().list(
        part='snippet',
        playlistId=uploads_playlist_id,
        maxResults=4,
        pageToken=None
    ).execute()
    return playlist_response

def image_request(image_urls):
    image_responses = []
    for i in range(len(image_urls)):
        image_responses.append(requests.get(image_urls[i]))
    return image_responses

 

전처리 함수 모음: preprocessing_methods.py

def response_preprocessing(playlist_response):
    text_info = []
    thumbnail_urls = []
    for item in playlist_response["items"]:
        snippet = item["snippet"]
        # title
        video_title = snippet["title"]
        # description
        video_desc = snippet['description']
        # thumbnails - urls
        video_thumbnail_url = snippet['thumbnails']['standard']['url']
        text_info.append({
            "title": video_title,
            "description": video_desc,
        })
        thumbnail_urls.append(video_thumbnail_url)
    return text_info, thumbnail_urls


def image_preprocessing(image_response):
    img_in_bytes = []
    for i in range(len(image_response)):
        img_in_bytes.append(Image.open(BytesIO(image_response[i].content)))

    img_width = img_in_bytes[0].size[0]
    img_height = img_in_bytes[0].size[1]

    combined_width = img_width*2
    combined_height = img_height*2

    combined_image = Image.new('RGB', (combined_width, combined_height))

    combined_image.paste(img_in_bytes[0], (0, 0))
    combined_image.paste(img_in_bytes[1], (img_width, 0))
    combined_image.paste(img_in_bytes[2], (0, img_height))
    combined_image.paste(img_in_bytes[3], (img_width, img_height))

    buffer = BytesIO()
    combined_image.save(buffer, format="JPEG")
    buffer.seek(0)
    combined_base64 = base64.b64encode(buffer.read()).decode('utf-8')

    return combined_base64

 

이렇게 하여 초기 프로필 평가로직의 두 문제를 해결하는 변경된 로직 구현을 마무리했다.

 

 

다음으로는 콘티생성과 최적화 및 비동기 적용 포스트로 돌아오겠다.

 

아래의 링크에서 전체 코드를 확인할 수 있다.

Project Joing Service Dev

 

GitHub - jwywoo/Project-Joing-Service-MVP-Dev

Contribute to jwywoo/Project-Joing-Service-MVP-Dev development by creating an account on GitHub.

github.com

 

반응형