티스토리 뷰

반응형

목차

프로젝트 진행 동기
프로젝트 요약
프로젝트 진행 상황과 본인 역할
YouTube API를 활용한 데이터 수집
썸네일에서 사람 얼굴을 인식해 feature를 추출
얼굴 인식 데이터를 바탕으로 조회수를 예측하는 모델 개발
프로토타입 개발

 

프로젝트 진행 동기

다들 유튜브 많이 보시죠? 저도 많은 시간을 유튜브를 보며 보내곤 합니다.

국민 83%가 한 달에 평균 30시간을 유튜브 시청에 사용하고 있다는 통계 자료가 있습니다. 유튜브 시장과 크리에이터 수도 꾸준히 증가하고 있습니다.

 

주변에 유튜브 크리에이터로서 영상을 만들고 업로드하는 친구들도 늘어났습니다.

유튜브 채널을 운영하는 사람들은 어떻게 하면 조회수가 잘 나올지에 대해 많은 고민을 합니다.

저는 영상 컨텐츠나 퀄리티도 중요하지만 사람들의 이목을 끄는 제목이나 썸네일도 못지 않게 중요하다고 생각했습니다.

유튜브 댓글 중 "지나칠 수 없는 썸네일", "제목 때문에 궁금해서 들어왔다" 같은 댓글을 자주 봤던 기억이 있습니다.

실제로 사람들의 시청을 유도하는 제목과 썸네일의 특징이 있을까 궁금해졌습니다. 

 

구글링을 해보니 위와 같은 컨텐츠들이 나왔고, 무언가 공식이나 features가 있을 것이라는 생각이 좀 더 확실해졌습니다. 더불어 유튜버가 제목과 썸네일을 정하는 데 많은 노력을 쏟고 있다는 것도 확인했습니다.

인공지능 모델을 활용해 유튜브 조회수가 높게 나오는 제목 및 썸네일을 찾는 프로젝트를 해보면 재미있겠다(그리고 수익성이 있겠다)라는 생각이 들었습니다. 그렇게 졸업프로젝트로 "유튜브 조회수 상승을 위한 제목 및 썸네일 추천 서비스"  개발을 하게 되었습니다.

 

프로젝트 요약

사용자가 원하는 제목이나 썸네일 후보를 입력하면 예상 조회수가 가장 높은 제목 및 썸네일을 추천하는 서비스입니다.

1. 유튜브 api를 활용해 필요한 데이터를 수집합니다.

2. 수집한 데이터를 기반으로 제목 / 썸네일의 텍스트 / 썸네일의 사람 얼굴을 추출하고, 추출한 데이터를 모델에 학습시켜 조회수를 예측하는 모델을 개발합니다.

3. 조회수 예측 모델을 사용해 사용자가 후보로 넣은 제목과 썸네일의 조회수를 예측합니다.

4. 사용자가 넣은 후보 중 가장 조회수가 높게 예측된 제목과 서비스를 추천해줍니다. 부가적으로 넣은 후보 간의 수치 데이터를 시각화한 비교 분석 레포트 제공, 조언까지 제공할 계획입니다. 

 

프로젝트 진행 상황과 본인 역할

본 프로젝트는 2학기간 진행됩니다.(2021.03~2021.11)

1학기에는 유튜브 제목과 썸네일을 학습시켰을 때 모델이 조회수를 제대로 예측할 수 있는지를 확인하는 것을 목표로 요소 기술 검증을 진행했습니다.

제목 / 썸네일의 텍스트 / 썸네일의 사람 얼굴로 나누어서 각각에 대해 조회수를 예측하는 모델을 개발했습니다.

전 먼저 유튜브 api를 활용해 필요한 데이터를 수집했습니다.

이후 썸네일의 사람 얼굴을 인식해 feature를 추출하고, 추출한 데이터를 바탕으로 조회수를 예측하는 모델을 개발했습니다.

또한 실제 유저에게 서비스할 웹서비스의 프로토타입을 개발했습니다.

 

제목에 대해서 조회수 예측 모델을 개발한 친구의 블로그 글입니다! 저희 프로젝트에 대해 더 궁금하시다면 방문해주세요!

https://tae-hi.tistory.com/2

 

문장 벡터 기반 유사도 검색시스템 & LSTM을 활용한 텍스트 분류

유튜브에서 발표한 자료에 의하면 광고로 인한 순수익이 매년 한국돈 1조원 가량씩 증가하고 있다고 한다. 시장 규모가 큰 만큼 유튜브에 도전해보는 사람들(크리에이터)도 계속 증가하는 추세

tae-hi.tistory.com

 

YouTube API를 활용한 데이터 수집

API를 사용하기 위해서 우선적으로 구글에 API키를 요청해 받아야합니다.

먼저, 사용할 구글 계정으로 로그인합니다. 

그 다음, 구글 클라우드 플랫폼 > API 및 서비스에 들어갑니다. (https://console.cloud.google.com/apis)

 

Google Cloud Platform

하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요.

accounts.google.com

 

이렇게 프로젝트가 없다면 프로젝트 만들기를 눌러 프로젝트를 생성해주세요.

프로젝트 이름은 아무거나 하셔도 상관 없습니다. 저는 그냥 My Project로 했습니다. 프로젝트를 만들고 나면 아래와 같은 화면이 뜹니다.

 

여기서 위의 검색창을 활용해 youtube api를 검색합니다. 여기서 YouTube Data API를 선택해줍니다.

 

사용 버튼을 클릭해줍니다.

 

사용버튼을 클릭하면 아래 화면으로 리다이렉트 됩니다. 여기서 사용자 인증 정보 만들기를 선택해주세요. 혹시 이미 사용자 인증 정보가 있는 경우인데 API key를 새로 발급 받고 싶으면 왼쪽 사이드바의 사용자 인증 정보(Credentials)를 클릭해주세요.

위와 같이 API key를 발급 받을 수 있습니다. 키를 복사해서 사용하시면 됩니다.

 

유튜브 API 사용법은 공식 문서를 확인하시면 됩니다.

 

시작하기  |  YouTube Data API  |  Google Developers

소개 이 문서는 YouTube와 상호작용할 수 있는 애플리케이션을 개발하려는 개발자를 위해 작성되었습니다. 여기에서는 YouTube 및 API의 기본 개념에 대해 설명합니다. 또한 API가 지원하는 다양한 기

developers.google.com

API 공식문서 바로가기 

 

API Reference  |  YouTube Data API  |  Google Developers

YouTube Data API를 사용하면 YouTube 웹사이트에서 일반적으로 실행하는 기능을 사용자의 웹사이트 또는 애플리케이션에 통합할 수 있습니다. 아래 목록에서는 API를 사용하여 검색할 수 있는 다양한

developers.google.com

 

딥러닝 모델 학습에 사용하기 위해 목표 데이터(영상) 개수를 10만개 이상으로 잡았습니다. 

처음 생각은 search.list API를 활용해 어떤 검색어를 입력했을 때 나오는 결과를 몇십만개를 저장하자는 것이었습니다. 그렇게 하니 최대 몇백개의 데이터 밖에 수집할 수 없었습니다. 그래서 아래와 같이 방식을 바꿨습니다.

  1. 검색어를 넣어 검색된 결과 영상들의 채널 목록을 가져온다
  2. 채널 목록에 있는 채널들의 플레이리스트 아이디를 가져온다
  3. 플레이리스트 목록에 있는모든 영상 목록을 가져온다
  4. 영상 목록의 조회수를 찾아 추가한다

위 방식으로 하니 십만개 넘는 데이터를 수집할 수 있었습니다.

아래 링크에 가시면 본 설명의 코드를 확인하실 수 있습니다.

https://github.com/hits-hike/youtube-views-prediction-model/blob/main/data/collect_data_using_youtube_api.ipynb

 

hits-hike/youtube-views-prediction-model

Contribute to hits-hike/youtube-views-prediction-model development by creating an account on GitHub.

github.com

 

먼저 유튜브 API를 이용하기 위해 아래와 같이 초기 설정 코드를 입력해줍니다.

깃허브에 코드를 업로드할 때 API key 노출을 주의해야합니다. 저는 같은 폴더에 config 파일을 만들고 해당 파일에 API key를 저장해두었습니다. 이후 .gitignore에 config파일을 넣어주었습니다. 로컬에서만 작업하신다면 developerKey = '위에서 발급 받은 키'로 해두셔도 무방합니다.

저처럼 따로 작성하실 분은 이렇게 해주세요.

# ./config.py
API_KEY = '위에서 발급 받은 키를 여기 붙여넣으시면 됩니다'

 

 

 

search.list API를 사용해서 검색어를 '브이로그'로 설정했을 때 나오는 결과의 채널 아이디를 저장했습니다. 

get_youtube_channel_search_list(keyword)는 인자로 검색어를 받아 해당 검색어로 검색된 결과의 채널 아이디를 channel_list에 추가하는 함수입니다. 

검색 결과의 유형이 영상일 때는 해당 영상을 올린 채널 아이디를, 채널일 때는 그 채널 아이디를 추가해주었습니다.

몇개의 채널 아이디를 수집했는지 궁금해서 print('data num: ' + str(len(channel_list)))를 통해 확인했습니다.

중복된 채널을 수집했을 수 있으니 중복된 채널 아이디는 하나만 저장하도록 channel_list = channel_list.drop_duplicates() 해주었습니다.

위에서 수집한 채널 아이디로 해당 채널에서 업로드한 모든 영상 정보를 가져오고자 했습니다. 위와 같이 두 단계로 나눠 진행했습니다. 두 단계로 나눠 진행한 이유는 search.list api가 잘 동작하지 않았기 때문입니다. 그래서 channels.list api를 활용해 수집된 채널들의 모든 플레이리스트 아이디를 받아왔습니다.

3번에서 플레이리스트 목록의 모든 영상 정보를 가져왔습니다.

search.list API 응답 리소스 구조는 아래와 같습니다.

{
  "kind": "youtube#searchResult",
  "etag": etag,
  "id": {
    "kind": string,
    "videoId": string,
    "channelId": string,
    "playlistId": string
  },
  "snippet": {
    "publishedAt": datetime,
    "channelId": string,
    "title": string,
    "description": string,
    "thumbnails": {
      (key): {
        "url": string,
        "width": unsigned integer,
        "height": unsigned integer
      }
    },
    "channelTitle": string
  }
}

저희는 모델에 활용할 정보로 제목, 업로드 시각을 활용할 것이라 해당 정보를 저장했습니다. 추후에도 해당 영상을 구분해주기 위해 영상 아이디, 채널명, 채널 아이디를 추가로 저장해주었습니다.

(위 데이터 수집 코드를 작성할 시기엔 우선적으로 제목에 대한 조회수 예측만 하고자 했기 때문에 썸네일을 따로 저장해주지 않았습니다. 이후에 썸네일만 따로 수집하는 모듈을 만들었습니다.)

 

위 search.list API로는 조회수가 같이 응답으로 오지 않기 때문에 영상마다 조회수를 찾아 추가하기 위해 videos.list API를 사용했습니다.

조회수(views)를 함께 저장했습니다. 

dropna를 통해 결측값을 제거했고, 조회수가 제대로 수집되지 않은 영상들을 제거해주었습니다.

이후 to_csv 함수를 통해 수집한 데이터를 csv형태로 저장했습니다.

 

썸네일에서 사람 얼굴을 인식해 feature를 추출

썸네일에서 어떤 요소가 사람들의 이목을 끌고 시청을 유도할지 고민했습니다. 고민 결과 썸네일에 사람이 등장한다면 해당 사람의 특징, 유명인 여부, 썸네일에서 사람이 차지하는 비율(크기) 등이 좌우할 것으로 추측했습니다.

저희 팀에서 자체적으로 얼굴 인식을 구현할 수도 있었지만 기존 API를 활용하는 것이 정확도나 수집할 수 있는 데이터 측면에서 더 나을 것 같다는 조언을 듣고 아마존의 rekognition API를 사용하기로 결정했습니다.

 

What is Amazon Rekognition? - Amazon Rekognition

What is Amazon Rekognition? Amazon Rekognition makes it easy to add image and video analysis to your applications. You just provide an image or video to the Amazon Rekognition API, and the service can identify objects, people, text, scenes, and activities.

docs.aws.amazon.com

 

썸네일에서 유명인 인식을 하는 기능도 있었으나, 한국 셀럽들을 제대로 인식하지 못하기도 하고 저희 데이터가 십만개가 조금 넘는 수준이기 때문에 현 상황에서 유명인을 하나의 feature로 두는 것은 무리가 있다고 판단해 현재는 유명인 판단을 하고 있지 않습니다.

 

썸네일에서 얼굴이 썸네일에서 차지하는 면적, 나오는 사람 수, 얼굴로 파악된 사람의 나이, 성별, 표정 등이 사람들이 썸네일을 보고 시청할지 안할지에 영향을 미친다고 생각해 해당 정보를 Amazon Rekognition > Detect Faces를 통해 수집했습니다.

 

아마존 api를 사용하기 위해서는 일단 계정 가입을 해야합니다. 가입하고 로그인을 합니다. 이후 서비스에서 Machine Learning > Amazon Rekognition을 선택해줍니다.

https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/ 

 

https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/

 

ap-northeast-2.console.aws.amazon.com

데모를 선택하시면 원하는 기능의 데모를 체험하실 수 있습니다.

 

저는 여기서 얼굴 분석(Detecting and Analyzing Faces)를 사용했습니다.

https://docs.aws.amazon.com/rekognition/latest/dg/faces.html

 

Detecting and analyzing faces - Amazon Rekognition

Detecting and analyzing faces Amazon Rekognition can detect faces in images and videos. This section covers non-storage operations for analyzing faces. With Amazon Rekognition, you can get information about where faces are detected in an image or video, fa

docs.aws.amazon.com

API_DetectFaces API 문서 바로가기

 

DetectFaces - Amazon Rekognition

This is a stateless API operation. That is, the operation does not persist any data.

docs.aws.amazon.com

 

먼저 아마존 웹 서비스를 사용하기 위해서는 계정을 만들고 IAM 사용자를 생성해야 합니다.

그다음 AWS CLI를 다운받습니다. 

https://docs.aws.amazon.com/rekognition/latest/dg/setup-awscli-sdk.html

 

Step 2: Set up the AWS CLI and AWS SDKs - Amazon Rekognition

If you don't select a region, then us-east-1 will be used by default.

docs.aws.amazon.com

여기서 시키는 대로 설정해주면 됩니다. config 파일에 aws_access_key_id와 aws_secret_access_key를 설정했으면 이젠 aws api를 사용할 준비가 된 것입니다.

 

다음은 aws rekognition.detect_faces api 사용 코드입니다. 아마존 api docs에는 S3 bucket에 이미지를 업로드해서 사용하라고 나와있으나 그냥 이미지 url을 아래처럼 인풋으로 넣어줘도 상관 없습니다. 

import boto3
from urllib.request import urlopen
from PIL import Image 
 
client=boto3.client('rekognition')

response = client.detect_faces(
	Image={'Bytes': urlopen(url).read()},
	Attributes=['ALL']
)

 

Response Syntax(응답 구조)입니다.

{
   "FaceDetails": [ 
      { 
         "AgeRange": { 
            "High": number,
            "Low": number
         },
         "Beard": { 
            "Confidence": number,
            "Value": boolean
         },
         "BoundingBox": { 
            "Height": number,
            "Left": number,
            "Top": number,
            "Width": number
         },
         "Confidence": number,
         "Emotions": [ 
            { 
               "Confidence": number,
               "Type": "string"
            }
         ],
         "Eyeglasses": { 
            "Confidence": number,
            "Value": boolean
         },
         "EyesOpen": { 
            "Confidence": number,
            "Value": boolean
         },
         "Gender": { 
            "Confidence": number,
            "Value": "string"
         },
         "Landmarks": [ 
            { 
               "Type": "string",
               "X": number,
               "Y": number
            }
         ],
         "MouthOpen": { 
            "Confidence": number,
            "Value": boolean
         },
         "Mustache": { 
            "Confidence": number,
            "Value": boolean
         },
         "Pose": { 
            "Pitch": number,
            "Roll": number,
            "Yaw": number
         },
         "Quality": { 
            "Brightness": number,
            "Sharpness": number
         },
         "Smile": { 
            "Confidence": number,
            "Value": boolean
         },
         "Sunglasses": { 
            "Confidence": number,
            "Value": boolean
         }
      }
   ],
   "OrientationCorrection": "string"
}

전 여기서 AgeRange, Gender, BoundingBox, Emotions 정보를 활용했습니다.

 

아래는 이미지 url을 인풋으로 받아 aws rekognition.detect_faces api를 사용해 썸네일에서 얼굴이 썸네일에서 차지하는 면적, 나오는 사람 수, 얼굴로 파악된 사람의 나이, 성별, 표정을 반환하는 함수입니다.

def detect_faces(url):

    client=boto3.client('rekognition')

    try:
        response = client.detect_faces(
            Image={'Bytes': urlopen(url).read()},
            Attributes=['ALL']
        )
    except:
        return None
    
    people_num = len(response['FaceDetails'])
    
    # 감지된 얼굴이 없는 경우
    if people_num == 0:
        return [int(people_num), -1, -1, -1, '', '']
    
    # 가장 큰 얼굴 하나만 데이터로 저장
    max_height = 0
    
    for faceDetail in response['FaceDetails']:
        # size
        height = faceDetail['BoundingBox']['Height']
        
        if height > max_height:
            max_height = height
        else: 
            continue
        
        width = faceDetail['BoundingBox']['Width']
        
        # age는 예상 나이 범위의 (low+high)/2
        age = (faceDetail['AgeRange']['Low'] + faceDetail['AgeRange']['High']) / 2
        
        # size
        width = faceDetail['BoundingBox']['Width']
        height = faceDetail['BoundingBox']['Height']
        
        # emotion
        emotion_confidence = 0
        emotion = ''
        
        for item in faceDetail['Emotions']:
            if item['Confidence'] > emotion_confidence:
                emotion_confidence = item['Confidence']
                emotion = item['Type']
        
        # gender
        gender = faceDetail['Gender']['Value']
    
    
    data = [int(people_num), int(age), width, height, emotion, gender]
    return data

사람이 없는 경우 사람수로 0을, 예상 나이/너비/높이는 -1을, 감정과 성별은 빈 문자열을 반환해 주었습니다.

사람이 여러명 나오는 경우 여러명의 데이터를 다 저장해 feature로 활용할 수 있었지만 그렇게 되면 feature가 너무 많아질 수 있고 모델 학습에 무리가 있을 것이라 판단했습니다. 따라서 가장 면적을 크게 차지하는(얼굴의 높이가 가장 긴) 사람 한 명의 얼굴에 대해서만 데이터를 분석했습니다. 해당 얼굴의 예상 나이는 예상 나이 범위의 평균을 내서 저장했습니다.

이미지 url이 제대로 오픈되지 않는 등의 에러가 나면 api가 에러를 반환합니다. 이 때는 함수에서 None을 반환하도록 했습니다. 이후 None인 값들은 제거하고 모델에 학습시킵니다.

 

아마존 서비스는 유료입니다. 참고로, 이 서비스의 프리티어 사용량은 한달에 최대 5000 이미지입니다.

저는 가난한 대학생이기 때문에 과금이 무섭습니다. 왜 학교에서는 후불 서비스에 대해서는 비용 지원을 해주지 않고, 아마존에서는 크레딧을 학교에 팔지 않는 걸까요? 무척 아쉽습니다. 😩😔😂

아무튼 그렇기 때문에 함부로 모든 이미지에 대해 api 요청을 보낼 수는 없습니다. 그래서 영상 아이디와 썸네일이 저장된 데이터프레임의 모든 아이템이 아니라 시작 인덱스와 끝 인덱스를 받아 df.loc[start_index] ~ df.loc[end_index - 1]까지의 데이터에 대해서만 얼굴 인식을 할 수 있도록 함수를 만들었습니다. 

 

def get_data(start_index, end_index):
    if end_index > len(df):
        end_index = len(df)
    for index in range(start_index, end_index):
        url = df['thumbnail'][index]
        data = detect_faces(url)
        df.loc[index, ['people_num', 'age', 'width', 'height', 'emotion', 'gender']] = data

 

데이터프레임 df 구조입니다. api를 통해 잘 수집된 것을 확인할 수 있습니다. 상위 10000개의 데이터에 대해서만 수집했습니다. 수집하기 전에 조회수가 1000 미만인 영상들은 목록에서 제거했습니다.

 

얼굴 인식 데이터를 바탕으로 조회수를 예측하는 모델 개발

해당 모델에 필요한 라이브러리 등을 import합니다.

 

 위에서 수집해 csv형태로 저장한 데이터를 모델에 학습시키기 위해 불러왔습니다.

 

학습시키기 전에 전처리부터 했습니다.

얼굴 인식을 통해 데이터를 추출한 10000개만 남겨주었습니다. 또한 에러 등으로 인해 생긴 결측값을 제거해주었습니다.

영상 아이디와 썸네일 주소는 학습에 쓰이지 않는 column이기 때문에 제거했습니다.

# 데이터 수집한 10000개에 대해서만
df = df.head(10000)

# 결측값 있는 행 제거
df = df.dropna(subset=['people_num'])

# 필요없는 열 제거
df = df.drop(columns = ['video_id', 'thumbnail'])
df

 

위 전처리코드를 수행한 결과 df입니다.

 

전 RandomForestRegressor을 통해 조회수 예측을 할 것이기 때문에 문자열로 되어 있는 emotion과 gender를 수치 데이터로 바꿔줘야 합니다. 

수치 데이터로 변환할 때 주의할 점이 있습니다. 예를 들어 gender 값을 Male = 1, Female = 2로 변환할 경우 RandomForestRegressor가 연속적인 데이터로 인식하기 때문에 여성이 남성보다 더 높은 값을 갖는다고 생각합니다. 이 경우 결과가 이상하게 나올 수 있기 때문에 원핫인코딩 작업을 해주어야 합니다.

판다스의 get_dummies를 이용하면 쉽게 가능합니다. 아래와 같이 작업해줍니다.

감정과 성별에 None값이 들어가 있는 것도 제거하지 않고 dummy_na=True 옵션을 통해 따로 처리해주었습니다.

 

학습 데이터 셋과 테스트용 데이터 셋 비율을 8대 2로 나누어 주었습니다.

 

자, RandomForestRegressor를 사용해 조회수를 한 번 예측해봅시다!

결과는 아쉽게 되었습니다.

일단 성능을 개선하기 위해서 학습시킬 데이터를 더 수집할 계획입니다. 

혹시 사람이 없을 때 나이, 너비, 높이 기본 값을 -1로 준 것이 영향이 있을까 싶어 0으로 바꿔서도 한 번 테스트 해볼 계획입니다.

지금은 조회수 숫자 자체를 예측하는데 조회수 구간을 나누어 분류하는 모델로 변경하면 조회수를 더 잘 예측할 수 있을지 알아보고자 합니다.

 

프로토타입 개발

https://hits-hike.vercel.app/

 

히츠하이크

조회수가 잘 나오는 영상의 핵심, 바로 제목! 예상 조회수가 가장 높은 제목을 추천해주는 인공지능 솔루션, 히츠하이크

hits-hike.vercel.app

저희 프로젝트의 프로토타입 url입니다. 

 

npx create-next-app

create-next-app으로 nextjs app을 만들었습니다.

이후 타입스크립트, prettier, eslint, babel 설정 등을 해줬고, vercel을 통해 배포했습니다.

 

 

hits-hike/hits-hike-web

Contribute to hits-hike/hits-hike-web development by creating an account on GitHub.

github.com

https://github.com/hits-hike/hits-hike-web에 가셔서 git clone을 통해 다운 받으신 후

npm install
npm run dev

명령어를 통해 저희 프로토타입을 실행해보실 수 있습니다.

 

프로토타입의 현재 UI입니다. 지금은 제목 추천 받기 UI만 구현되어 있습니다. 

곧 썸네일 추천 받기 UI를 구현하고 전체적인 UI를 개선할 계획입니다.

 

나중에 좋은 결과있길 기대하면서 열심히 프로젝트를 진행해보도록 하겠습니다.

궁금한 것 혹은 조언해주실 부분이 있다면 언제든 댓글 달아주세요! 감사합니다😊

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함