見出し画像

#37 認知シャッフル睡眠法用音声をAIで作ってみた

どうも、まりなです。
もう9月になるのにまだまだ暑く寝苦しい夜が続きますね。私は寝付きが元々悪い方なので暑さのせいで余計に眠りづらくなっています。
最近眠れない日にはよくシャッフル睡眠法の音声を聞いています。

シャッフル睡眠法というのは、「人間は理論立ったことを考えていると眠りにつきにくいため、全く関係ないランダムな単語を次々に聞いて、理論立った思考を止めることで眠ることができる」という睡眠法だそうです。

よく上の動画を使っているのですが、何度も聴くうちに次の単語が何だったかを覚えたり考えたりしてしまい、逆に頭を使うようになってしまいました……
より質の高いシャッフル睡眠法を行うには本当に毎回ランダムな単語を聞く必要があると思います。
ということで今回はシャッフル睡眠法用の単語生成器をPythonで作ることにしました!
(※音声ファイル出力部分には外部サイトを使用します)


Word2Vec

シャッフル睡眠法のポイントは関係のないランダムな単語を聞いて論理的な思考をストップすることです。例えば「えんぴつ、ノート、机、本、…」のような関連がありそうな単語を聞いてしまうと頭に関連性のイメージが浮かび、入眠しにくくなってしまうようです。
この「関連性」を定量的に評価するため、まず単語をベクトルに変換します。

「は?単語が矢印になる訳がないのだが??」と思った方も一旦落ち着いてください。
単語にその特徴量ごとの値を設定してやれば多次元のベクトルとして表すことができるんです。

実際は「大きさ」や「重さ」のような人間にとって分かりやすい特徴量ではなく人間には理解できない複雑なパラメータをAIに割り振らせます。

AIはそのようなパラメータをどうやって割り振るのかというと、似た意味の単語は似た文脈で使われやすい、という自然言語の性質を利用します。
例えば「私はを飼っている」という文章と、「私はを飼っている」という文章が教師データに含まれていた場合「犬」と「猫」が近いパラメータになるようニューラルネットワークを学習します。

引用: https://arxiv.org/pdf/1301.3781.pdf

※ニューラルネットワークについてもっと詳しく知りたい方はこちらの過去記事をご覧ください↓

これがWord2Vecというアルゴリズムです。

今回はこちらのサイト(https://fasttext.cc/docs/en/crawl-vectors.html)で配布されているfastTextという学習済みのモデルを使用します。FacebookのAIResearchラボによって作成されたモデルだそうです。
gensimというライブラリを用いてモデルの読み込みは次のように書けます。

# 学習済モデルのパス
model_path = '/content/drive/MyDrive/cc.ja.300.vec.gz'
# ロード
model = gensim.models.KeyedVectors.load_word2vec_format(model_path, binary=False)

実際に「犬」という単語のベクトルを見てみましょう。

print(model.get_vector("犬"))
[-2.146e-01 -2.391e-01  7.200e-02  1.586e-01  2.620e-02  2.139e-01
  4.140e-02  1.504e-01  2.698e-01  2.355e-01  3.390e-02  7.970e-02
  4.562e-01  6.500e-02 -3.562e-01 -1.534e-01 -8.980e-02  1.315e-01
 -2.982e-01  3.270e-02  1.018e-01 -1.499e-01 -3.323e-01 -6.900e-02
  2.310e-02  2.250e-02 -9.540e-02  1.700e-01  9.960e-02  1.583e-01
  1.552e-01 -1.448e-01 -1.736e-01 -7.980e-02  1.910e-02 -7.260e-02
  2.581e-01 -5.820e-02  1.700e-01 -1.019e-01 -4.980e-02 -5.540e-02
 -7.700e-03 -6.000e-03 -1.013e-01  1.012e-01  4.710e-02 -1.359e-01
  4.191e-01 -2.940e-02 -1.750e-01 -2.090e-02  3.520e-02  5.300e-02
  2.678e-01 -9.900e-03 -4.720e-02  2.566e-01 -3.740e-02 -8.370e-02
 -8.020e-02 -1.039e-01 -6.000e-02  1.731e-01  9.650e-02  2.316e-01
  2.926e-01  2.535e-01 -4.500e-02 -3.760e-02  1.500e-02  1.639e-01
  1.612e-01  2.435e-01  1.772e-01 -3.797e-01 -1.366e-01 -1.700e-03
 -2.027e-01  5.860e-02  1.375e-01  1.325e-01 -1.204e-01  2.890e-01
 -1.896e-01 -1.228e-01 -5.220e-02  2.045e-01  1.460e-02  6.470e-02
 -2.718e-01 -8.110e-02  5.810e-02 -5.470e-02 -3.001e-01  2.455e-01
 -2.183e-01  2.651e-01  2.000e-03 -1.060e-02  1.557e-01  2.735e-01
 -2.950e-02 -1.353e-01  3.344e-01  4.334e-01 -5.620e-02 -4.880e-02
  9.540e-02 -1.057e-01 -1.280e-01 -1.900e-02  1.774e-01  2.860e-01
  2.112e-01 -6.010e-02 -2.677e-01  6.510e-02 -1.483e-01 -1.207e-01
  1.389e-01 -4.780e-02  5.770e-02  1.820e-02  1.210e-02  9.000e-04
  1.201e-01 -7.490e-02 -2.658e-01 -1.249e-01  4.000e-04  3.920e-02
  3.840e-02 -1.599e-01  2.856e-01  3.690e-02 -2.525e-01 -4.320e-02
 -1.285e-01  3.340e-02  2.610e-02 -2.480e-02  2.559e-01  1.164e-01
  3.714e-01  1.478e-01  1.134e-01  2.953e-01  1.109e-01 -2.169e-01
  1.345e-01 -1.226e-01 -8.800e-02  1.826e-01  6.400e-02  1.580e-01
  5.060e-02  2.471e-01 -7.890e-02  5.130e-02 -2.380e-01 -1.241e-01
  1.775e-01 -1.095e-01 -1.165e-01 -1.215e-01 -1.522e-01 -1.577e-01
 -1.767e-01 -1.251e-01  7.090e-02 -1.178e-01  2.944e-01 -5.260e-02
 -2.780e-02 -3.810e-02 -1.774e-01  1.741e-01 -2.150e-02  1.879e-01
  2.550e-02 -1.630e-01  1.435e-01  1.630e-01 -1.461e-01  7.570e-02
  1.031e-01 -1.273e-01 -1.400e-01 -1.190e-01  2.585e-01  0.000e+00
  1.174e-01  1.103e-01  8.420e-02 -2.000e-03  4.330e-02 -4.010e-02
 -1.777e-01 -2.860e-01  1.960e-02 -2.490e-02 -2.328e-01  1.121e-01
 -1.230e-01 -1.659e-01  3.350e-01 -1.509e-01  1.726e-01  1.585e-01
  7.780e-02 -8.800e-03 -4.660e-02  4.150e-02 -5.280e-02 -2.281e-01
  8.880e-02 -8.000e-01  4.031e-01  5.500e-03  1.800e-03 -1.059e-01
  1.124e-01 -7.640e-02  1.858e-01 -1.132e-01 -1.454e-01  3.827e-01
 -1.099e-01  6.890e-02  2.289e-01  8.690e-02 -3.853e-01 -2.173e-01
 -1.080e-01  1.235e-01 -3.646e-01  8.080e-02  6.620e-02 -1.008e-01
 -4.275e-01  1.413e-01  1.475e-01 -1.793e-01 -1.133e-01 -1.303e-01
 -2.317e-01  2.997e-01 -1.555e-01 -6.610e-02  1.660e-01 -4.442e-01
  5.780e-02 -3.490e-02 -1.930e-02  2.897e-01 -9.610e-02  1.773e-01
  6.010e-02  2.049e-01  9.830e-02 -6.330e-02  2.220e-02  2.200e-02
  1.731e-01 -2.469e-01  1.992e-01  5.830e-02 -5.630e-02 -2.653e-01
 -1.007e-01 -8.800e-03  9.360e-02 -1.303e-01 -2.606e-01  1.112e-01
  1.538e-01  4.049e-01 -1.077e-01  2.075e-01 -2.512e-01  1.062e-01
 -3.190e-02 -2.691e-01  1.189e-01 -1.704e-01 -1.565e-01 -5.410e-02
  1.039e-01  1.737e-01  2.176e-01 -5.860e-02 -2.596e-01 -5.470e-02
 -1.838e-01 -2.234e-01  2.169e-01 -2.100e-03  3.145e-01 -2.677e-01]

こわっ。
各単語が300次元のベクトルになっているようです。

では単語をベクトルにできたところで「関連性」をどう定量的に評価するのかというと高校の時に習ったベクトルの内積が役に立ちます。

内積の式
$${\displaystyle cosθ = \frac{\vec{a}\cdot\vec{b}}{|\vec{a}| |\vec{b}|}}$$

二つのベクトルのなす角$${θ}$$のコサインは内積の定義式を変形して上のように計算できます。$${cosθ}$$は-1から1の値をとり、値が大きいほどベクトルは近く、小さいほどベクトルは遠いと言えますね。($${cosθ}$$は$${0°≦θ≦180°}$$の範囲で単調減少なので)そのためこの値のことを二単語のコサイン類似度と呼びます。

つまり$${cosθ}$$が大きいほど二つの単語は関連性が高く、小さいほど関連性が低いと言えると思います。
試しに「犬」という単語と関連性の高い単語を出力してみます。gensimではmost_similar()というメソッドを使ってコサイン類似度の大きい単語を出力できます。

model.most_similar(positive=["犬"])

こんな出力が出てきました。

[('飼い犬'0.7061354517936707),
 ('ワンコ'0.6905006170272827),
 ('飼い主'0.6632921695709229),
 ('愛犬'0.6605983376502991),
 ('猫'0.6505385041236877),
 ('子犬'0.650025486946106),
 ('仔犬'0.6445448398590088),
 ('柴犬'0.6354683637619019),
 ('ドーベルマン'0.6330671310424805),
 ('猟犬'0.628188967704773)]

単語の後ろの値がコサイン類似度です。「犬」と意味の近そうな単語が並びましたね。

逆に関連性の低い単語も探してみましょう。

'ニキビホワイトニングアンチエイジング'

たしかに関連性は低そうです。犬はニキビできなさそうですもんね。しらんけど。


実装方法

今回考えたアルゴリズムはこうです。

  1. モデルにある単語をランダムに$${n}$$個取り出す($${n}$$はバッチサイズ)

  2. $${n}$$個の単語それぞれと前の単語のコサイン類似度を計算する

  3. コサイン類似度が最小の単語を採用する

  4. 1.に戻る

バッチサイズ$${n}$$は大きくするほど関連性の低い単語が見つかる可能性が上がりますが処理が重くなります。今回は$${n=10}$$で実装しました。

欲しい単語数を得られたら繰り返しを終了し単語列をテキストとして出力します。
得られたテキストを音声読み上げソフト「音読さん」(https://ondoku3.com/ja/)に読み込ませて音声にすればシャッフル睡眠音声の完成です!


実験

では実際に動かしてみたいと思います。mainは次のような実装です。

num_words = 10 # 取得する単語数

out_txt = "<speak>"
idx = rd.randrange(vocab_size)
word = model.index2word[idx]
out_txt +=  word + '<break time="10s"/>'

for i in range(num_words):
    word = get_next_word(word)
    out_txt +=  word + '<break time="10s"/>'

out_txt += "</speak>"

print(out_txt)

# txtファイルに出力
with open("output.txt"'w'as f:
  print(out_txt, end='', file=f)

とりあえず10単語分の音声を作ってみます。実行して出力されたtxtファイルを音読さんに貼り付け、音声ファイルにしました。
あとなんか荘厳な感じのBGMと映像もつけて、
できた音声がこちら↓

うまくできました!これで寝苦しい夜も安心です!!


まとめ

今回は自然言語処理の基本的なアルゴリズムを用いてシャッフル睡眠法の単語列を作ってみました。言語学という数学や情報工学と離れていそうな分野も計算機で上手く扱おうとしてうまれたWord2Vecやコサイン類似度などのアルゴリズムには先人たちの驚くような発想の豊かさを感じます。
jupyter notebookのソースコードも添付するので睡眠にお困りの方は是非動かしてシャッフル睡眠法音声を作ってみてください。それでは。


ソースコード


この記事が気に入ったらサポートをしてみませんか?