동영상 제목에서 YouTuber를 맞췄어요.
개시하다
※ 어제의 보도 계속 유행 주제!
그나저나 이번에는 유튜브의 빛과 어두운'동해 온 에어'와'물이 고인 본드'의 유튜버가 분류될 수 있는지 시험해보자.
Ajenda
유튜버 채널에서 제목 재방송
각 유튜버의 메인 채널과 하위 채널에서 각각 제목을 얻는다.
get_title.pyimport 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リットルコーラ早飲みで今度は吐血...', '【カンタと@小豆】ロケットサイダー踊ってみた', '【逃げ恥】恋ダンスを踊ってみた【家族?】']
문자열 사전 처리
자연 언어 처리의 가장 중요한 부분이라고 해도 과언이 아니지만, 이번에는 최소한의 처리만 한다.(시간 없음)
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
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
# 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
>> オンエアの日常【オムライス】
>> 寝顔に水
>> てつやのお料理日記【13日目】
>> てつやの買った透明なコート、値段当てたらあげます
>> てつやのおしおき日記【1日目】
>> 第1回 口ゲンカトーナメント!
>> 【ポケモンSM】素人てつやの四天王挑戦日記 vsカヒリ
>> てつやの高級ドライバーでてつやを起こします
>> てつやにもう耐性ができてる寝顔に水25
>> 第一回新しい漢字作り選手権!!!
>> てつやのお料理日記【14日目】
>> てつやのお料理日記【3日目】
>> 【スマブラ】対戦実況Part3『vsはじめしゃちょー』
>> てつやvsゆめまる 深夜のアート対決
>> てつやのお料理日記【8日目】
>> 【トミー怪我】1対1でガチ野球したら本当に面白すぎたwww
>> ディズニーで日が暮れるまでトミーに見つからなかったら賞金ゲット
>> くまカレーが絶品すぎた!!
>> カンタのナンパ術が怖すぎてネットニュースにまとめられたwww
>> トミーの超暴力的スーパープレイ集 が酷すぎたwww
>> 世界一高いハンドスピナーの回し心地が予想外だったww
>> カンタ復活
>> まさかのカンタ健康診断がトミーより悪かった?
>> 超ピカピカ光るお菓子作ったら味が不思議だった
처화/몽환, 토미/제인 등 각 그룹의 멤버 이름을 넣으면 분류가 용이하다.(당연히...)
총결산
정밀도가 떨어지기 때문에 문자의 예처리와 벡터화 방법, 학습기 방법 등을 해보고 싶습니다.(개인적으로 빛과 어두운 그들은 항상 분류할 수 있다고 생각한다.)
참조 링크
Reference
이 문제에 관하여(동영상 제목에서 YouTuber를 맞췄어요.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/_rio_/items/03416500705200208c8a
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(동영상 제목에서 YouTuber를 맞췄어요.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/_rio_/items/03416500705200208c8a텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)