BERTによる、歌詞に基づくMr.Childrenの曲の分類に挑戦!【Part①】

Mr.Childrenの曲を20曲取り上げ、BERTを利用して、歌詞に基づいた分類を実行してみました。
はじめに
BERT(Bidirectional Encoder Representations from Transformers)というモデルをご存知でしょうか?BERTは、Googleが開発した自然言語処理のための深層学習モデルです。これは、高度な言語理解タスクを実行するために開発され、文章の要約、文書分類、質問応答、言語翻訳など、様々なところで応用されています。
本記事では、BERTを用いて、歌詞に基づいたMr.Childrenの曲の分類を試してみようと思います。
対象とするのは、次の20曲です。
「365日」「しるし」「終わりなき旅」「Sign」「GIFT」「名もなき詩」「Tomorrow never knows」「HERO」「エソラ」「innocent world」「くるみ」「抱きしめたい」「旅立ちの唄」「君が好き」「シーソーゲーム ~勇敢な恋の歌~」「祈り ~涙の軌道」「youthful days」「掌」「Marshmallow day」
分類は次の手順で行います。
- 歌詞(文字列)を数値化
- 数値化したベクトルをクラスター分析(K平均法)
準備
はじめに、諸々のダウンロードを済ませます。
読み込むcsvファイルには、1列目に「曲名」、2列目に「歌詞」の情報が入っています。
!pip install japanize_matplotlib
!pip install fugashi
!pip install ipadic
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.manifold import TSNE
from transformers import BertJapaneseTokenizer, BertModel
from sklearn.cluster import KMeans
df= pd.read_csv("Mr.Children.csv")
df= df.iloc[1:20]#今回は歌詞閲覧ランキング上位20個を対象とします
次に、BERTの日本語モデルをダウンロードします。
# BERTの日本語モデル、tokenizer、modelのダウンロード
BERT_model_jp = 'cl-tohoku/bert-base-japanese-whole-word-masking'
tokenizer = BertJapaneseTokenizer.from_pretrained(BERT_model_jp)
model = BertModel.from_pretrained(BERT_model_jp)
データフレームの中身は下の画像のようになっています。
これで準備完了です。

歌詞を数値化
早速、歌詞を数値データに変換したいと思います。文字列を数値に変換する役割を担うのが、先ほどインストールした、BERTモデルです。
#歌詞のベクトル化
max_length = 256
sentence_vec= []
for i in range(len(df)):
lines = df.iloc[i, 1].splitlines()
text = '\n'.join(lines)
encoding = tokenizer(
text,
max_length=max_length,
padding='max_length',
truncation=True,
return_tensors='pt'
)
with torch.no_grad():
output = model(**encoding)
last_hidden_state = output.last_hidden_state
attention_mask = encoding['attention_mask']
normalized_hidden_state = (last_hidden_state * attention_mask.unsqueeze(-1)).sum(1) / attention_mask.sum(1, keepdim=True)
sentence_vec.append(normalized_hidden_state.numpy())
#リストの形式を変更
sentence_vectors = np.vstack(sentence_vectors)
最後の、sentence_vectorsの次元は、
sentence_vectors.shape
1曲の歌詞が、768次元の数値ベクトルに変換されており、それが20曲分並んだものが、sentence_vectorsに格納されています。ここまでで、歌詞(文字)の情報を数値に変換することができました。
歌詞に基づいて、曲をグループ分け
今回は、K平均法という手法を用いてクラスタリングを行います。K平均法の実行は、次の2行のコードで行うことができます。モデルを作る際に、グループの数(n_clustersの部分)を解析者が選ぶ必要がありますが、今回は4つとしました。
kmeans = KMeans(n_clusters=4, random_state=22)
kmeans.fit(sentence_vectors)
最後に、K平均法によるクラスタリングの結果を見てみます。
from collections import defaultdict #クラスターごとに所属するデータポイントの行をまとめる辞書を作成
cluster_dict = defaultdict(list)
#各データポイントをクラスターごとに分類
for i, label in enumerate(kmeans.labels_): cluster_dict[label].append(df.iloc[i])
sorted_clusters = sorted(cluster_dict.keys())
#各クラスターに所属するクラスターを表示
for cluster in sorted_clusters:
print(f"クラスター {cluster} に所属する曲名:")
for data_point in cluster_dict[cluster]:
print(data_point.iloc[0])
print(" ")
結果発表!
先ほどのクラスタリングの結果、次のようにグループ分けされました。
- クラスター0: 「しるし」「終わりなき旅」「名もなき詩」「くるみ」「抱きしめたい」「旅立ちの唄」「君が好き」「シーソーゲーム ~勇敢な恋の歌~」「祈り ~涙の軌道」「掌」
- クラスター 1 : 「365日」「Sign」「Tomorrow never knows」「innocent world」「youthful days」 「Marshmallow day」
- クラスター 2 :「エソラ」
- クラスター3:「HANABI」「GIFT」「HERO」

最後に
クラスター分析によって、歌詞に基づいて曲を、4つのグループに分類をすることができました。
しかしながら、ここで、重要な過失に気がつきました。「筆者は、今回扱った20曲のMr.Childrenの歌詞、全部把握できていません。」そのため、クラスター分析の結果が、非常に良いのか、ある程度良いのか、今ひとつなのか、はっきりしたことは言えません…。
BERTを用いることで、文章データを数値データに変換することができました。文章のままだと扱いにくいデータも、数値データに変換することで、色々な分析が可能になります。今回扱ったK平均法以外にもクラスタリング手法はありますし、次元削減など他の方法を試しても面白そうです。
最後まで、読んでいただきありがとうございました!