【実験】 水樹奈々さんと田村ゆかりさんの曲の歌詞を比べてみた

私は水樹奈々さんの曲をよく聴きます.
水樹奈々さんの曲と言えば, ETERNAL BLAZEを始めとするカッコいい曲が多いイメージです.
また奈々語と呼ばれる難解な読みの歌詞が特徴的です.

そんな水樹奈々さんとよく比較されるのが田村ゆかりさんですね.
歌手としての田村ゆかりさんはあまり知らないのですが, 可愛い曲を歌っているという勝手なイメージがあります.

そこで, 水樹奈々さんと田村ゆかりさんの曲はどの程度違うのかを視覚化してみました.

まずそれぞれの歌詞を250個くらいずつ収集し, MeCab形態素解析を行いました.
次に, 単語のBoW表現に変換し, 一つの曲についてBoWベクトルを作りました.
これを主成分分析で, 2次元のベクトルに変換し, グラフにプロットしました.

青が水樹奈々さんの曲で赤が田村ゆかりさんの曲です.

f:id:umashika5555:20190119194354p:plain
水樹奈々田村ゆかりの歌詞をPCAに掛けた結果


思った以上に点の重なり具合が大きく, 識別が難しく感じました.
点が重なった理由として水樹さんの曲の中にも可愛い系の曲が多数あるからだろうと思いました.
なんとか目視で上手く識別できるような二人を探そうと思い, 水樹奈々さんのようにかっこいい系の曲と可愛い系の曲を持ち合わせている歌手ではなく, 一つのジャンルに絞ったような歌手を探そうと思いました.
一人は先程と同様に可愛い系一筋であろう田村ゆかりさんを固定するとして, もう一人を北島三郎さんにしました.
北島さんも全く曲を知らないのですが, おそらく渋い系一筋の人だろうと予測したからです.
結果は下のようになりました.

f:id:umashika5555:20190119195149p:plain
北島三郎さん(緑)と田村ゆかりさん(赤)の歌詞をPCAに掛けた結果

これは思っていた以上に分離できました.
北島三郎さんの曲はグラフの上あたりに分布し, 田村ゆかりさんの曲はグラフの下あたりに分布しています.





面白い結果になりそうなPCAにかけるべき歌手(2人〜5人程度)や, BoW->PCA以外に有用な手法などあったら教えてください.



mind.kittttttan.info

物体認識をお手軽に試す

ImageNetにおける物体認識をしたいとする.
通常ならイチからモデルを組み, データを入手し, 学習させ……という作業が必要なわけだが, ディープラーニングフレームワークKearsならこれが簡単にできる.
さらに, VGGやResNet, Xception, MobileNetなど自宅の計算機スペックでは学習が難しそうなモデルでも予め用意されたパラメータを取得することで, 研究所並の精度の予測ができる.

import numpy as np
from keras.applications.vgg16 import VGG16
from keras.applications.vgg16 import preprocess_input, decode_predictions
import keras.preprocessing.image as Image

# モデルの宣言
model = VGG16(weights="imagenet")


続いて予測を行う.
画像は次の画像を用いる.

f:id:umashika5555:20190115142604p:plain
アンゴラウサギ

# 予測
image_path = "angora.png"
image = Image.load_img(image_path, target_size=(224, 224))  # ImageNetのサイズ
x = Image.img_to_array(image)
x = np.expand_dims(x, axis=0)  # 次元を追加。(224, 224)->(1, 224, 224)
x = preprocess_input(x) # ImageNetでやってる前処理を同じようにやる

result = model.predict(x)
result = decode_predictions(result, top=3)[0]
print(result)  # show description

結果は以下の通り「この画像がアンゴラである確率は99.99%以上」スゴイッ!!

[('n02328150', 'Angora', 0.9999628), ('n02123394', 'Persian_cat', 1.3111491e-05), ('n02111889', 'Samoyed', 1.070684e-05)]

【linux】コマンドラインから画像をリサイズする

拡張子を変換したりするconvert コマンドでリサイズもできるそう.
a.pngを640x640のb.pngにリサイズしたい場合

$ convert a.png -resize 640x640! b.png


imagemagick.rulez.jp

(曲線, 曲面) の法線と接線

機械学習をやる上で重要な線形代数学と微分のお話.

多次元の曲線に関するベクトルの計算を導入する.
厳密な説明ではないので, 簡単な二次元の例から多次元へと拡張していく.

曲線の法線ベクトル

曲線


の座標(a, b) における法線ベクトルが

であることを示す.
まず座標(a, b)は曲線f(x, y) = 0上の点なので

次に, 座標(a, b)から(Δx, Δy)動かしたときの曲線上の点(a+Δx, b+Δy)において, この点は曲線上にあるので

二変数のTaylor展開より,

ここで(a, b), (a+Δx, b+Δy)は曲線f上の点なので

であった.
また, Taylor展開の式に代入して,

ここで,


と定義すると,
Taylor展開の式は,

と更に書き直せる.
ここで, ... の部分は, Δx, Δy の二次以上の項なので, Δx, Δyを小さくしていくとO(n^2)で0に収束していく. すると,

これは, 内積が0であることを表している. 内積が0ということは, 2つのベクトルが直交しているということである. すなわち, ∇f(a, b)は, Δxの法線ベクトルであることを表している.
f:id:umashika5555:20181225095846p:plain
法線ベクトルの図

曲線の接線

先程は, 曲面に対する法線を考えたが, 今度は接線を考える.


について法線ベクトルは,

また求める接線の方向ベクトルは(x-a, y-b)^Tである. したがって, 法線ベクトルと, 方向ベクトルが直交しているということを表したものが接線の式である. すなわち

ただし,

f:id:umashika5555:20181225101955p:plain
曲線の接線

曲面の法線ベクトル

次に3次元上の空間を考える.
f(x, y, z) = 0の曲面において, (a, b, c)における法線ベクトルを考える.
まず(a, b, c)は曲面上にあるので,


(a, b, c)から少しずらした点(a+Δx, b+Δy, c+Δz)も曲面上にあるので,

これをTaylor展開すると,

となり曲線(2次元)と同様に, 最終的に

という式が導ける.
f:id:umashika5555:20181225103416p:plain
曲面の法線ベクトル

曲面の接平面


について, 法線ベクトルは∇f(a, b, c) = (A, B, C)^T
また方向ベクトルは(x-a, y-b, c-z)なので, これらが直交する式が接平面の式である.
よって,

f:id:umashika5555:20181225104534p:plain
曲面の接平面


このようにしてp次元空間においても, 法線ベクトルをp変数におけるTaylor展開で求めてから, (p-1)次元の接超平面を求めていけば, 多次元空間に拡張していけそうだ.

k-NN法をフルスクラッチ実装

今日はk-NN法(k-NearestNeighbor)を実装する.

k-NN法の概念

k-NN法は教師データから未知データを予測する教師あり学習の一つで, また, 線形回帰などのようにパラメータを最適化するような手法を取らない.
すなわち, 「教師データから識別境界のようなものを学習してから未知データが境界のどちらにあるか」という手法をとらずに, 「それぞれの未知データに対して教師データとの関係を見てどちらのクラスかを予測する」という方法をとる. これを怠惰学習というらしい.
k-NN法に関しては, 名前の通り, 未知データに対して最も近い教師データk個のうち多い方のクラスラベルを予測クラスとする.
まず下図のようなデータがあるとする. 赤色がクラス1, 緑色がクラス0 とする. 星がクラスラベルのわかっていない未知データである.
この未知データがどちらのクラスに入れた方が良いのかを予測したい.

f:id:umashika5555:20181223013754p:plain
データ


今k=3すると未知データから最も近い3つの教師データを抽出できる.
f:id:umashika5555:20181223020457p:plain
3-NN
赤色(クラス1)のデータが1つで緑色(クラス0)のデータが2つである.
よって多数決をとって, 緑(クラス0)データの方が多いので, 未知データは緑(クラス0)と予想する.
式で表すとこのようになるが, わざわざ式で表さなくてもよい.
f:id:umashika5555:20181223015047g:plain

データの生成

k-NN法を実装するために二次元のデータを生成する.
k-NN法は分布を仮定した方法ではないため, 分布から生成する必要は無いと思うが, ラベル付きのデータを生成したかったので取り敢えずガウス分布を使ってデータを生成した.

f:id:umashika5555:20181223015313p:plain
データ

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# 乱数固定
np.random.seed(0)
# 平均ベクトル
Mu1, Mu2 = np.array([1, 10]), np.array([9,2])
# 分散共分散行列
sigma11, sigma12, sigma21, sigma22 = 4, 5,  6, 3
SIGMA1, SIGMA2 = np.array([[sigma11**2, np.sqrt(sigma11*sigma12)],[np.sqrt(sigma11*sigma12), sigma12**2]]), np.array([[sigma21**2, np.sqrt(sigma21*sigma22)],[np.sqrt(sigma21*sigma22), sigma22**2]])
# データ生成
values1 = np.random.multivariate_normal(Mu1, SIGMA1, 200)
values2 = np.random.multivariate_normal(Mu2, SIGMA2, 200)
# データ保存
np.save("./data/values1.npy", values1)
np.save("./data/values2.npy", values2)
# 散乱図の表示
plt.scatter(values1[:,0], values1[:,1], color="r")
plt.scatter(values2[:,0], values2[:,1], color="g")
plt.xlabel("x")
plt.ylabel("y")
plt.xlim(-10, 30)
plt.ylim(-10, 25)
plt.savefig("./img/data.png")
plt.show()

k-NN法による未知データの分類

格子点を未知データとして考え領域分割を行う.
格子点から各データ点までの距離を計算し, 最も近いデータ点k個のラベルから多数決を行う.
閾値が0.5のとき, kが奇数の場合は多数決が成立するのだが, kが偶数の場合で各ラベルが同じ個数あるとき多数決が成立しない.
よってkは奇数にするほうが良いことが分かる.
またkが大きくなると, 汎用性が高くなることが分かる.

f:id:umashika5555:20181223015636p:plain
k=1
f:id:umashika5555:20181223015651p:plain
k=2
f:id:umashika5555:20181223015705p:plain
k=3
f:id:umashika5555:20181223015720p:plain
k=4
f:id:umashika5555:20181223015818p:plain
k=5

f:id:umashika5555:20181223015836p:plain
k=7

f:id:umashika5555:20181223015854p:plain
k=9

f:id:umashika5555:20181223151116p:plain
k=51

import numpy as np
import matplotlib.pyplot as plt

# k-NN法のパラメータkを設定
k = 200
# データのロード
values1 = np.load("./data/values1.npy")
values2 = np.load("./data/values2.npy")

# データを
# 統合, その際に教師情報もつける
data = np.vstack((values1, values2))
labels = np.hstack((np.ones(len(values1)), np.zeros(len(values2))))

# 未知のデータとして格子点を作成
# 2つのデータから最小, 最大のx, yを探す
x_min, x_max = min(np.min(values1[:,0]), np.min(values2[:,0])), max(np.max(values1[:,0]), np.max(values2[:,0]))
y_min, y_max = min(np.min(values1[:,1]), np.min(values2[:,1])), max(np.max(values1[:,1]), np.max(values2[:,1]))
# 格子点を作成
xx = np.linspace(x_min-1, x_max+1, 300)
yy = np.linspace(y_min-1, y_max+1, 300)
xxx, yyy = np.meshgrid(xx, yy)# 格子点(xxx, yyy)が生成された. これを未知データとみなしてk-NN法を適用する.

class0 = []
class1 = []
thresholds = []

# k-NN法の適用
for i, (xx, yy) in enumerate(zip(xxx, yyy)):
    for j, (x, y) in enumerate(zip(xx, yy)):
        # 未知データ(x,y)から既知データdata各点への距離を計算する
        # 距離はユークリッド距離の二乗, すなわちl2ノルムの二乗を計算する
        tmp = data - (x, y)
        distances = np.linalg.norm(tmp, axis=1)
        # 最も小さいスコア上位k点のラベルを参照
        supervisers_index = np.argsort(distances)[:k]
        # print(supervisers_index)
        supervisers = labels[supervisers_index]
        # print(supervisers)
        res = np.sum(supervisers)/ k 
        # if res > 0.5 then x_res in class1
        if res > 0.5:
            class1.append((x, y))
        elif res == 0.5:
            thresholds.append((x, y))
        else:
            class0.append((x, y))
        
class0, class1, thresholds = np.array(class0), np.array(class1), np.array(thresholds)
plt.scatter(values1[:,0], values1[:,1], color="r", marker="x", s=20)
plt.scatter(values2[:,0], values2[:,1], color="g", marker="x", s=20)
plt.scatter(class1[:,0], class1[:,1], color="r", marker="o", s=1, alpha=0.1)
plt.scatter(class0[:,0], class0[:,1], color="g", marker="o", s=1, alpha=0.1)
try:# 奇数の場合, 必ず多数決が成立するので閾値(0.5)とイコールになるものがないためエラー処理しておく
    plt.scatter(thresholds[:,0], thresholds[:,1], color="b", s=1, alpha=0.1)
except:
    pass

plt.title("./img/{}-NN methods".format(k))
plt.xlabel("x")
plt.ylabel("y")
plt.xlim(-9, 23)
plt.ylim(-7, 23)
plt.savefig("./img/{}-NN.png".format(k))
plt.show()

最小二乗法をフルスクラッチで実装する

ココで言うフルスクラッチは行列演算以外である.
行列演算は本質ではないので, 行列演算ライブラリは使うことにする.

前回の記事で線形モデルの最小二乗法について, パラメータの解が

\begin{align*}
\boldsymbol{w} = (\boldsymbol{X}^T\boldsymbol{X})^{-1}\boldsymbol{X}^T\boldsymbol{y}
\end{align*}
で得られることが分かった.
これをPythonによって実装してみる. 今回は一次関数のパラメータa,b を求め直線フィッティングを行う.
まずデータを生成する. データ生成のアルゴリズムは以下の通りに行った.

  1. a, b, σを決定する
  2. [-10, 10]の範囲でデータxをランダムに生成する
  3. 決定したa, bからt=ax+bを計算する
  4. t=ax+b+εを計算する. ただしε~N(0, σ^2)
### 単純な線形モデルy = ax + b についてのパラメータa,b を求める問題におけるデータを生成する
### 
### a, b, σの値を決定する
### [-x_min, x_max]のデータxをランダムにN個用意 
### 各データに対して, N(ax+b, σ^2)に従うガウス分布からtを生成
### 

import numpy as np
from numpy.random import seed
import matplotlib.pyplot as plt

seed(0)

a, b, sigma = 2, 3, 3
N = 100
x_min, x_max = -10, 10

# データ生成
X = (x_max-x_min) * np.random.rand(N) + x_min
Epsiron = np.random.normal(0, sigma**2, N)# N(0, sigma^2)のN個の正規乱数
T = (a*X + b) + Epsiron

# データをファイルに出力
f = open("data00.csv", "w")
for (x, t) in zip(X, T):
    f.write("{}, {}\n".format(x,t))
f.close()

# データをプロット
plt.scatter(X, T)
plt.show()

以下の図のようになった.

f:id:umashika5555:20181221091859p:plain
データの様子
このデータから,

\begin{align*}
\boldsymbol{w} = (\boldsymbol{X}^T\boldsymbol{X})^{-1}\boldsymbol{X}^T\boldsymbol{y}
\end{align*}
に従ってパラメータを決定する.

import numpy as np
import matplotlib.pyplot as plt

# データの読み込み
name_data = "data00"
path_data = name_data + ".csv"
data = np.loadtxt(path_data,delimiter=",")
x, t = data[:,0], data[:,1]
p = x.shape[0]
ones = np.ones(p)
x_ = np.c_[ones, x]

# パラメータベクトルの計算
A = np.dot(x_.T, x_)
w = np.dot(np.dot(np.linalg.inv(A), x_.T), t)
print(w)

# データのプロット
plt.scatter(x, t)

# 回帰曲線の描画
b, a = w
xx = np.linspace(np.min(x)-2, np.max(x)+2, 100)
yy = a * xx + b
plt.plot(xx, yy, color="red")

plt.xlabel("x")
plt.ylabel("y")
plt.title("a=2, b=3, σ=2")
plt.savefig(name_data.format(a,b))
plt.show()

f:id:umashika5555:20181221092149p:plain
data00
上図のように実際に目で観ても綺麗にフィットできていることがわかる.


次回はこの線形モデルを更に発展させ, 基底関数, 正則化あたりをやっていきたい.