[2DUB] 크롤링 + TFIDF

6 minute read

데이터 셋 만들고 전처리 하기

2dub 동영상과 가장 비슷한 데이터를 실습으로 이용하기 위해 10~15 문장 정도 구성되어 있는 유튜브 trailer에 있는 영어 자막을 크롤링 하여 예제 데이터 셋으로 사용합니다.

image

크롤링 하기 전 유튜브 api를 이용하기 위해 key를 얻어야 하는데 이곳을 참고 하여 개인 key를 받습니다.

api key를 받았다면 다음 bitbucket에 있는 파이썬 파일들을 pull하거나 개인적으로 다운받아 사용합니다.

그리고 아래 그림처럼 영화의 URL주소와 API key를 입력하면 (pymysql, sqlite 선택 가능) 자신의 데이터 베이스에 information과 caption 테이블이 생성되며 영화 정보와 자막이 따로 저장됩니다. (저는 조커라는 영화를 다운받기 전에 7개의 영화를 미리 저장 해놓은 상태이기에 데이터가 더 많이 보입니다. 데이터 베이스는 먼저 아무 이름으로 생성 해주세요. 저는 youtube라는 이름의 db를 먼저 생성했습니다.)

image

image

image

위 사진과 같이 자막(caption) 데이터를 보시면 ‘[]’ 안에 Music(Applause, …)같은 필요없는 데이터가 들어가 있는 것을 보실 수 있습니다. 자막 데이터를 충분히 다운로드 받으셨다면 SQL 쿼리로 전처리를 해줍니다.

대괄호로 시작해서 대괄호로 끝나는 caption 행 삭제

delete from caption where caption like '[%%]'

image


TFIDF

데이터 불러오기

데이터베이스에 존재하는 자막들을 파이썬으로 불러와 TFIDF를 통해 각 문장의 단어별로 점수를 부여합니다. 코드를 보면서 알아봅시다.

  • Get_data.py
def get_data(host, user, password, db):

    conn = pymysql.connect(host=host, #localhost
                            user=user, #user
                            password=password,
                            db = db, #youtube
                            charset='utf8mb4')
    cursor = conn.cursor()

    # ==== select example ====
    sql = "select caption from caption"
    cursor.execute(sql)

    # 데이타 Fetch
    captions = cursor.fetchall()
    captions = list(captions)
    captions = [i[0] for i in captions]
    cursor.close()
    return captions

전처리

아래와 같은 전처리 과정은 나중에 데이터 셋이 변경될 때 추가적으로 전처리 과정을 추가하거나 변경해 줍니다.

  • PreProcessing.py
import re

# '[Music, Applause, ...]' <- 전처리 and remove tuple

def preprocessing(captions):
    clean_captions = []

    for caption in captions:
        caption = re.sub("[^a-zA-Z]", " ", str(caption)).strip()
        words = caption.lower().split()
        clean_caption = ' '.join(words)
        clean_captions.append(clean_caption)
    return clean_captions


# 어떤 단어에 몇번 째 number가 할당 되었는지 확인
print(tfidf_vectorizer.vocabulary_)    

{'have': 216, 'never': 333, 'had': 210, 'feeling': 151, 'as': 28, 'pure': 381, 'proud': 379, 'completing': 97, 'mission': 315, 'all': 13, 'you': 571, 'everything': 142, 'we': 539,... 생략}

그러면 아래 그림처럼 tf-idf를 자동으로 계산해주고 tfidf_matrix에 저장합니다. 이때 tfidf_matrix는 그냥은 확인할 수 없고 cosine 계산시에 할당을 해줄 수 있습니다.

image

cosine_similarity로 유사도를 구한 후 top 5 문장 추출하기

그리고 단어 유사도를 구하기 위해 단어간 거리 뿐만 아니라 ‘각도’의 개념도 포함이 되어 가장 성능이 좋은 Cosine-Similarity를 사용하여 input() 단어와 다른 모든 단어들 간의 Similarity를 구한 후 TOP 5개의 문장을 추출해 봅니다.

  • Extract_top.py
from sklearn.metrics.pairwise import cosine_similarity
from operator import itemgetter

# extract top 5

def extract_top5(captions, clean_captions, tfidf_matrix):
    checked_similarity = []

    for i in range(len(clean_captions)):

        if cosine_similarity(tfidf_matrix[-1], tfidf_matrix[i]) != 0:

            checked_similarity.append([captions[i][0], cosine_similarity(tfidf_matrix[-1], tfidf_matrix[i])])

    checked_similarity = sorted(checked_similarity, key=itemgetter(1), reverse=True)
    top_5 = checked_similarity[1:6]
    return top_5

top_5 문장 출력하기

  • main.py
# from nltk.corpus import stopwords
# from nltk.tokenize import word_tokenize, sent_tokenize
from sklearn.feature_extraction.text import TfidfVectorizer
from PreProcessing import preprocessing
from Get_data import get_data
from Extract_top import extract_top5

input_sentence = input('Input sentence: ')

captions = get_data('localhost', 'root', '111111', db='youtube')
captions.append(input_sentence)

clean_captions = preprocessing(captions)

tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(clean_captions)

# extract top 5

top_5 = extract_top5(captions, clean_captions, tfidf_matrix)

for i in range(len(top_5)):
    print("\n" + str(i+1) + ". " + top_5[i][0] + " | similarity: " + str(top_5[i][1][0][0]))

image

아직 데이터 양 자체가 많지 않아 cosine_similarity가 낮고 문장들도 그렇게 비슷해 보이지는 않지만 처음 의도했던 대로 tfidf와 cosine_similarity를 이용해 비슷한 문장을 출력하는 작업을 완성했다.

  • 덧: 추가적으로 유튜브 크롤링 할 때 하나씩의 영상이 아닌 여러 영상을 한번에 크롤링 하는법을 공부해서 데이터 양 자체를 늘려보고 tfidf가 아닌 현재 많이 사용중인 word2vec(예측 기반 방법 - 특정 문맥에서 어떤 단어가 나올지 예측하면서 단어를 벡터로 만드는 방식)을 사용해봐야겠다.

  • 덧: http://www.williamsportwebdeveloper.com/FavBackUp.aspx 여기서 youtube playlist에 있는 url을 한번에 다운받아서 pandas로 csv로 바꿔준다음 Video url정보를 추출하여 리스트로 만든 다음 jupyter에서 for문으로 한번에 caption을 받아오는 코드를 주피터 환경에서 실행해 봤는데 already exists 에러가 계속 발생했다. db 코드 저장에서 에러가난 것으로 보인다.

Audio

텍스트에서 tfidf로 벡터화 하여 하나의 sentence마다 벡터화를 통한 특징(feature)값을 구했다. 이제 유튜브 크롤링을 통해 오디오 파일을 다운받고 그 파일에서 특징(feature)을 추출하여 텍스트에서 추출한 특징과 함께 가장 유사한 sentence를 찾는 model 구현에 사용될 feature를 구성시킨다.

유튜브 url을 input으로 .wav format(소리 파동의 진폭을 일정한 시간 간격으로 기록한 파일. 이러한 저장 방식을 PCM(Pulse-Code Modulation) 이라고 부름.

출처: https://newsight.tistory.com/199?category=46353 [New Sight])의 오디오 파일만 다운받습니다.

from __future__ import print_function
import librosa

# get mp3 file from youtube
import youtube_dl


ydl_opts = {
    'format': 'bestaudio/best',
    'postprocessors': [{
        'key': 'FFmpegExtractAudio',
        'preferredcodec': 'wav',
        'preferredquality': '192',
    }],
}

with youtube_dl.YoutubeDL(ydl_opts) as ydl:
    ydl.download(['https://www.youtube.com/watch?v=AXbP_toJWq0&t=1s'])

밑의 코드를 통해 어떤 오디오 파일이 들어있는지 파이썬에서 확인할 수 있습니다.

import IPython.display as ipd
filename = './star_wars.wav'
ipd.Audio(filename)

오디오 파일을 다운로드 받았다면 이제 컴퓨터가 이해할 수 있는 언어로 바꾸어 주어야 합니다. 파이썬 모듈에서 librosa를 통해 수행할 수 있습니다.

y, sr = librosa.load(filename, offset=25, duration=10)
  • arguments 설명
  • sr=22050 : input 샘플링 주파수입니다. 아마도 갖고있는 오디오 파일의 샘플링 주파수는 22050이 아닐 확률이 큽니다. 이렇게 값을 설정해주는 것은 11025 Hz 까지의 값만 써도 된다는 가정을 한 것이죠. 잘 모르시면 그냥 두세요.
  • mono=True : 스테레오 음원일경우 모노로 바꿔준다는 말입니다. 역시 그냥 두시면 됩니다. 대부분의 경우 모노면 충분합니다. 이 글의 타겟이시면 스테레오 음원이 필요한 경우가 아닐거에요.
  • offset, duration: 오디오 파일의 특정 구간만 쓰실경우 설정하시면 됩니다. 여기선 그나마 음성이 뚜렷하게 나오는 25초부터 시작해서 10초동안의 오디오 파일만을 보겠습니다.
length = 220500

 array([-0.00091023, -0.00266716, -0.00339361, ..., -0.05961395,
        -0.05546921, -0.04137753], dtype=float32)

데이터를 확인해보면 위와 같은데 sampling rate 22050으로 10초를 받아왔으니 데이터의 수는 220500이고 밑에는 array형식으로 -1~1 범위의 amplitude 값들이 저장되어 있습니다.

이제 이 amplitude를 이용해 음성인식에서 가장 널리쓰이는 MFCC(Mel-frequency cepstrum)를 사용합니다.

mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=20)
  • arguments 설명
  • n_mfcc=20 : mfcc 계수의 개수입니다. mfcc가 가장 활발하게 쓰이는 음성 인식에서는 대략 이 값을 수십개 (20~50)로 설정하고 씁니다. 즉 그정도면 충분하다고 알려져있습니다. 다만 주어진 상황과 목표에 따라 그 값은 다를 수 있습니다. 우선 20으로 두시면 됩니다.

speech data in non-stationary. Then very small window(20ms) can make speech to stationary. The samll duration is called Frame.

mfcc 프레임 기반 특징값입니다. 즉, 각 프레임 (대체로 수 십 ms)마다 하나의 mfcc vector가 나오게 됩니다. 여기선 default 값으로 frame의 기본 값이 20ms마다 설정되어 있습니다. 20ms면 10초를 0.02초로 나누었으니 500개의 frame이 나오게 됩니다. 이 500개의 frame으로 위에서 나온 전체 sampling data(y) = 220500을 나누어주면 441개가 나오는데 이는 mfcc.shape = (20, 431)에서 431에 해당합니다.(10개는 1초에 한번씩 overapping 되는 것 같습니다.) (20, 431)의 구조는 첫번 째 feature값에 각 프레임의 feature값이 431개 들어가고 두번 째 feature 값에 각 프레임의 feature값이 431개 들어가는 구성을 되어있습니다.

mfcc.shape

(20, 431)
mfcc

array([[-491.16265645, -497.43793945, -508.15438933, ..., -128.28631787,
        -134.87857431, -148.88893057],
       [  80.1025811 ,   75.78345701,   71.56969135, ...,  121.91064918,
         121.80988383,  122.00154468],
       [ -16.09956884,  -10.13398718,   -3.43189873, ...,  -21.01005624,
         -24.69734777,  -14.73554511],
       ...,
       [   4.8997799 ,    5.57542773,    5.2128032 , ...,  -13.86383626,
         -16.50541521,  -14.56307027],
       [  -5.707017  ,   -5.79392596,   -5.64015381, ...,   23.49590878,
          12.91557772,   15.40681384],
       [  -5.4352415 ,   -6.55778324,   -2.45038934, ...,   -5.84773952,
          -3.2097984 ,    3.36205172]])

개인적인 생각으로 tfidf score를 기준으로 10개의 similar sentences를 추출한 다음, MFCC 값이 가장 유사한 sentence를 출력해주면 좋을 것 같다.

MFCC를 CNN 모델링 하여 classify하기(or mel spectogram + CNN) - 추후 진행 예정

(Mind that MFCCs are sensitive to noise, so do check first how your samples sound)

목표

Input voice: “I love you” (with high and happy voice)

  1. I love you (speaking with sobs)
  2. I love you (speaking archly)
  3. I love you (speaking with angry)
  4. I love you (speaking with high and happy)
  5. I love you (speaking with desperately)

라는 5개의 문장을 tfidf를 통해 추출했다.

이 문장에서 MFCC를 추출하여 TFIDF를 통해 얻은 score feature과 함께 CNN 모델링을 구성하여 clssifier하고 Input으로 주어진 sentence(“I love you”)가 모델에서 가장 유사한 I love you를 output 해준다.

Elasticsearch를 이용한 유사 문장 찾기

  1. elastic search가 mysql db를 사용하기
  2. “More like this query” 쿼리 사용
  3. Youtube 자막 데이터 늘리기

2. elastic search가 mysql db를 사용하기

Categories:

Updated:

Leave a comment