동영상 제목에서 YouTuber를 맞췄어요.

개시하다


어제의 보도 계속 유행 주제!
  • 최근 자연 언어 처리가 필요한 장면이 있어 기본 지식을 얻고 싶다
  • 최근 휴일에는 특정 유튜버를 보기 위해 유튜브에 절임
  • 이런 배경이 있으니 애니메이션 제목에서 유튜버를 예측하는 모델을 만들어 보고 싶다.
    그나저나 이번에는 유튜브의 빛과 어두운'동해 온 에어'와'물이 고인 본드'의 유튜버가 분류될 수 있는지 시험해보자.

    Ajenda

  • 유튜버 채널에서 제목 다시 쓰기
  • 문자열의 사전 처리
  • 문서의 벡터화(Bag of Words)/비트 압축(LSI)
  • 데이터 시각화
  • 예측
  • 평가
  • 요약
  • 유튜버 채널에서 제목 재방송


    각 유튜버의 메인 채널과 하위 채널에서 각각 제목을 얻는다.
    get_title.py
    import json
    import time
    import requests
    from unicodedata import normalize
    
    def get_title_list(channelId):
        """動画タイトルを取得"""
        list_title = []
        nextPageToken = ''
        for i in range(0, 100):
            url = 'https://www.googleapis.com/youtube/v3/search?part=snippet'
            payload = {'key':API_KEY, 'channelId': channelId, 'maxResults':50, 'order':'relevance', 'pageToken':nextPageToken}
            r = requests.get(url, params=payload)
            json_data = r.json()
            if not 'nextPageToken' in json_data:
                break;
            for q in range(0,len(json_data['items'])):
                list_title.append(json_data['items'][q]['snippet']['title'])
            nextPageToken = json_data['nextPageToken']
    
            time.sleep(2)
    
        # タイトルに重複があれば除外
        list_title_set = set(list_title)
        print('全取得数:{0} 重複除外後:{1}'.format(len(list_title), len(list_title_set)))
        return list_title
    
    
    if __name__ == "__main__":
        f = open('./data/youtuber_channelId.json', 'r') # YouTuber名とチャンネルIDの辞書を用意しておく
        dict_youtuber_channelId = json.load(f) 
    
        API_KEY = ''
    
        dict_youtuber_titles = {}
        for name, channelId in dict_youtuber_channelId.items():
            print(name)
            list_title = get_title_list(channelId)
            dict_youtuber_titles['list_title_'+name] = list_title    
    
        # dump
        file = open("./outputs/youtuber_titles.json", 'w')
        json.dump(dict_youtuber_titles, file)
        file.close()
    
    이렇게 하면 각 유튜버의 애니메이션 제목 목록을 얻을 수 있다.
    for name, titles in dict_youtuber_titles.items():
        print(name, len(titles))
    
    >> tokaionair 1012
    >> mizutamaribond 972
    
    print(dict_youtuber_titles['tokaionair'][0:3])
    print(dict_youtuber_titles['mizutamaribond'][0:3])
    
    >> ['【応援は力なり】メンバー各自の応援歌つくってみた', '「人間ローションボウリング」', '【おやこでみてね】たのしいえほんのよみきかせ']
    >> ['【リベンジ】トミー1.5リットルコーラ早飲みで今度は吐血...', '【カンタと@小豆】ロケットサイダー踊ってみた', '【逃げ恥】恋ダンスを踊ってみた【家族?】']
    

    문자열 사전 처리


    자연 언어 처리의 가장 중요한 부분이라고 해도 과언이 아니지만, 이번에는 최소한의 처리만 한다.(시간 없음)
  • 귀일화
  • 기호의 제거
  • def clean_title(title):
        title_cleaned = normalize('NFKC', title)
    
        kigou = re.compile(u'[ \-/:@\[\]\{\}・\(\)#\&\'\,\.【】。『』~、×==「」\!\?<>★☆■□◆◇▲▼△▽♪※◎*]')
        title_cleaned = kigou.sub('', title_cleaned)
    
        return title_cleaned
    
    if __name__ == "__main__":
        list_titles_all = [] # 全Youtuberのタイトルリスト
        for list_titles in dict_youtuber_titles.values():
            list_titles_all += list_titles
        list_titles_all_cleaned = [clean_title(title) for title in list_titles_all]
    

    문서 벡터화(Bag of Words)/비트 압축(LSI)


    파일의 벡터 표현 방법은 여러 가지가 있는데 이번에는 BoW를 사용합니다.또 제작된 BoW에 대해서는 TF-IDF를 가중해 LSI로 차원을 깎았다.LSI는 특이한 값으로 분해하는 차원 압축 방법이다.
    차원을 얼마나 낮출지는 사람이 정해야 하는데 이번에 많이 시도해 액커레이시가 가장 높은 100차원으로 낮췄다.
    mecab = MeCab.Tagger('-Owakati -d /usr/lib64/mecab/dic/mecab-ipadic-neologd')
    
    def word_tokenize(title):
        """タイトルを分かち書きしてリストとして返す"""
        result = mecab.parse(title)
        list_words = result.split(" ")
        list_words.pop() # 分かち書きの結果の最後の要素が'\n'なので削除
        return list_words
    
    def title2bow(title):
        """タイトルをBoW化する"""
        title_words = word_tokenize(title)
        word_cnt = dictionary.doc2bow(title_words)
        dense = list(matutils.corpus2dense([word_cnt], num_terms=len(dictionary)).T[0])
        return dense
    
    if __name__ == "__main__":
        # 全タイトルから単語リストを作成
        documents = [word_tokenize(title) for title in list_titles_all_cleaned] # 全タイトルの単語リスト
        # 辞書を定義
        dictionary = corpora.Dictionary(documents)
        print('単語数:',len(dictionary.keys()))
        # dictionary.filter_extremes(no_above=0.5) # 低頻度、高頻度の単語を消す
        # print(dictionary.token2id)
        print('単語数:',len(dictionary.keys()))
    
        dictionary.save_as_text('./data/titles_dic.txt')
        # dictionary = corpora.Dictionary.load_from_text('./data/titles_dic.txt')
    
        # BoWベクトルの作成
        bow_corpus = [dictionary.doc2bow(d) for d in documents] 
    
        # TF-IDFによる重み付け
        tfidf_model = models.TfidfModel(bow_corpus)
        tfidf_corpus = tfidf_model[bow_corpus]
    
        # LSIによる次元削減
        lsi_model = models.LsiModel(tfidf_corpus, id2word=dictionary, num_topics=100)
        lsi_corpus = lsi_model[tfidf_corpus] # あるYoutuberの全タイトルのベクトル化を次元圧縮したもの
    

    데이터 시각화


    나는 LSI로 그것을 2차원으로 낮추어 산포도를 그려 보았다.
    ※ 황록색은 동해, 녹색은 물이 고여

    ・・・(´・ω'') 헤어질 수 있을까 걱정이 되네요.
    뭐, 약 4천 차원을 강제로 2차원으로 만들었으니까 그렇지.
    아무튼 공부 좀 할게요.

    학습 예측


    이번에는 RandomForest를 사용했습니다.(SVM에서도 시도해 보았지만 정밀도가 높지 않음)
    RF의 매개변수는 트리 깊이에 GridSearch 곱하기만 합니다.또한 cv3에는 CrossValidation이 있습니다.
    # calc class weight
    class_weight = {}
    i = 0
    for name, list_titles in dict_youtuber_titles.items():
        class_weight[i] = (len(list_titles_all)/len(list_titles))
        i += 1
    
    print(class_weight)
    >> {0: 1.9604743083003953, 1: 2.0411522633744856}
    
    from sklearn.model_selection import GridSearchCV
    from sklearn.ensemble import RandomForestClassifier
    
    rf = RandomForestClassifier()
    parameters = {'max_depth': [2,4,6,8], 'n_estimators':[100], 'class_weight':[class_weight], 'random_state':[3655]}
    clf = GridSearchCV(rf, parameters)
    clf.fit(X_train, ý_train)
    y_pred = clf.predict(X_test)
    

    평점


    몇 차례 차원수를 시험해 본 결과 100차원 정도가 비교적 좋은 것 같다.

    동해 중계(True)
    적수병
    동해 중계(Pred)
    155
    48
    물병
    32
    162
  • 특히 동해 실황 중계처럼 예상되는 제목들(소속 학급의 확률 0.85 이상)
  • >> オンエアの日常【オムライス】
    >> 寝顔に水
    >> てつやのお料理日記【13日目】
    >> てつやの買った透明なコート、値段当てたらあげます
    >> てつやのおしおき日記【1日目】
    >> 第1回 口ゲンカトーナメント!
    >> 【ポケモンSM】素人てつやの四天王挑戦日記 vsカヒリ
    >> てつやの高級ドライバーでてつやを起こします
    >> てつやにもう耐性ができてる寝顔に水25
    >> 第一回新しい漢字作り選手権!!!
    >> てつやのお料理日記【14日目】
    >> てつやのお料理日記【3日目】
    >> 【スマブラ】対戦実況Part3『vsはじめしゃちょー』
    >> てつやvsゆめまる 深夜のアート対決
    >> てつやのお料理日記【8日目】
    
  • 특히 고인 물처럼 예측되는 제목들(학급 소속 확률 0.85이상)
  • >> 【トミー怪我】1対1でガチ野球したら本当に面白すぎたwww
    >> ディズニーで日が暮れるまでトミーに見つからなかったら賞金ゲット
    >> くまカレーが絶品すぎた!!
    >> カンタのナンパ術が怖すぎてネットニュースにまとめられたwww
    >> トミーの超暴力的スーパープレイ集 が酷すぎたwww
    >> 世界一高いハンドスピナーの回し心地が予想外だったww
    >> カンタ復活
    >> まさかのカンタ健康診断がトミーより悪かった?
    >> 超ピカピカ光るお菓子作ったら味が不思議だった
    
    처화/몽환, 토미/제인 등 각 그룹의 멤버 이름을 넣으면 분류가 용이하다.
    (당연히...)

    총결산


    정밀도가 떨어지기 때문에 문자의 예처리와 벡터화 방법, 학습기 방법 등을 해보고 싶습니다.(개인적으로 빛과 어두운 그들은 항상 분류할 수 있다고 생각한다.)

    참조 링크

    좋은 웹페이지 즐겨찾기