ちゃんとしたデータがないので現実的ではないでしょうけど、グーグルアンケートでやれるだけやってみようかな。
この手の記事の書き方がわからないので、地の文で書き続けていきます。
【0.ライブラリ読み込み】
作った関数はfunctions_cf.pyに入ってます。末尾参照。
import numpy as np
from functions_cf import *
import matplotlib.pyplot as plt
【1.データ作成】
登場する変数
music_size:扱う楽曲数 今回は5とした
sample_size:アンケート回答者 今回は100とした
cover_size:アンケート回答者が知らなかった楽曲数 今回は50
楽曲5曲はそれぞれA,B,C,D,Eと呼ぶことにします。100人の回答者がその5曲に対して順位付けを5-1とつけてもらうことにします(数字が大きいほど好き)。それを100*5の行列scoreとします。
ひとまず次のような設定にしました。
・AとB、CとDは同系統
・AとBが好きな人はCとDが好きではなく、Eはさらに好きではない
・CとDが好きな人は、Eが好きではなく、AとBはさらに好きではない
・Eが好きな人は、CとDは好きではなく、AとBはさらに好きではない
という前提でデータ作り。つまり言い換えれば
・(A,B)が(5,4)or(4,5)な人:(C,D)は(3,2)(2,3)のいずれか、Eは1
・(C,D)が(5,4)or(4,5)な人:(A,B)は(2,1)(1,2)のいずれか、Eは3
・Eが5な人:(A,B)は(2,1)or(1,2)、(C,D)は(4,3)or(3,4)
こんな行列を作るmaking_score関数を作って
score=making_score(sample_size, music_size)
で作ると。
ところが現実問題、全員が全員アンケートの全曲を知っているとは限らないので、scoreからcover_sizeの数の分だけランダムに0にしたdata、および「dataのうち0でないものを真、0のものを偽」とした行列Rを作ります。こちらもmaking_data関数を作って
data, R=making_data(score, cover_size)
で吐いてもらいました。
なお0が意味するのは評価が0というわけではなく、値がないことを意味する便宜上の0です。学習するときにはこのdataを学習し、scoreを予測することを目標とします。
【2.学習準備】
登場する変数
feature_size:機械学習で学んでもらう特徴の数 今回は3としました
theta:各サンプルの好みの特徴を表す行列(これを学習してもらう)
features:各楽曲の持つ特徴の行列(これも学習してもらう)
learning_rate:学習率
lam:正規化項
iter:学習回数
J_list:誤差関数記録
まず学習する2つを乱数で決めてやる。
features=np.random.randn(feature_size, music_size)
theta=np.random.randn(sample_size, feature_size+1)
featuresは実際に使う時に全部1の列を先頭に追加するので、thetaの余分な1行との内積でバイアスとして使われます。
他初期設定
learning_rate=0.3
lam=0.003
iter=2000
J_list=np.array([])
なおlearning_rateとlamはクロスバリデーションで求めました。クロスバリデーションといっても、データセットの出所は同じ関数ですけど。
【3.学習】
0. iter回繰り返し
1. featuresとthetaからscoreの予測値predictionを出させる
2. 記録用に二乗誤差Jを求める
3. 誤差関数の偏微分delta_theta, delta_featuresを求める
4. thetaの調整
5. featuresの調整
6. 記録用に今回のJをJ_listに追加
てな具合。以下。
for i in range(iter):
prediction=predict(np.r_[np.ones(features.shape[1]).reshape(1, -1), features], theta)
J=cost_func(data, prediction, R, theta, lam)
delta_theta, delta_features=delta_func(data, prediction, np.r_[np.ones(features.shape[1]).reshape(1, -1), features], R, theta, lam)
theta=theta-learning_rate*delta_theta
features=features-learning_rate*delta_features
J_list=np.r_[J_list, J]
【4.結果観測】
matplotlibの使い方はこの3行以外知らない模様。
fig=plt.figure()
plt.plot(np.arange(0, J_list.shape[0], 1), J_list)
plt.show()
sub=prediction-score
result=np.c_[prediction.reshape(-1), data.reshape(-1), sub.reshape(-1)]
np.save('result.npy', result)
二乗誤差をプロットしたのがこんな感じ
ちなみにresult[0:100]についてはこんな感じ。
左から順にprediction, data, prediction-scoreです。
array([[ 1.24964192, 1. , 0.24964192],
[ 1.37293008, 2. , -0.62706992],
[ 3.89820064, 4. , -0.10179936],
[ 3.65735034, 3. , 0.65735034],
[ 4.47454032, 5. , -0.52545968],
[ 1.52484356, 0. , -0.47515644],
[ 1.55074509, 1. , 0.55074509],
[ 3.98696449, 5. , -1.01303551],
[ 3.97059504, 4. , -0.02940496],
[ 3.85918495, 3. , 0.85918495],
[ 1.24810015, 1. , 0.24810015],
[ 1.62459262, 2. , -0.37540738],
[ 3.66986787, 4. , -0.33013213],
[ 3.54198014, 3. , 0.54198014],
[ 4.79943488, 5. , -0.20056512],
[ 1.69793326, 1. , 0.69793326],
[ 1.58617317, 2. , -0.41382683],
[ 4.1376663 , 4. , 0.1376663 ],
[ 4.10344883, 5. , -0.89655117],
[ 3.63891658, 3. , 0.63891658],
[ 1.71531344, 1. , 0.71531344],
[ 1.69460638, 2. , -0.30539362],
[ 4.02300241, 5. , -0.97699759],
[ 3.93272179, 4. , -0.06727821],
[ 3.91481096, 3. , 0.91481096],
[ 1.57404885, 1. , 0.57404885],
[ 1.29974807, 2. , -0.70025193],
[ 3.43582389, 3. , 0.43582389],
[ 3.5243598 , 4. , -0.4756402 ],
[ 2.27410224, 0. , -2.72589776],
[ 1.10700249, 1. , 0.10700249],
[ 1.16714062, 2. , -0.83285938],
[ 4.07213299, 4. , 0.07213299],
[ 4.12135044, 3. , 1.12135044],
[ 3.87078936, 5. , -1.12921064],
[ 1.68449955, 2. , -0.31550045],
[ 1.60428171, 1. , 0.60428171],
[ 4.14871254, 5. , -0.85128746],
[ 4.28263841, 4. , 0.28263841],
[ 3.41619002, 3. , 0.41619002],
[ 4.62560833, 4. , 0.62560833],
[ 4.05205221, 5. , -0.94794779],
[ 2.55317212, 3. , -0.44682788],
[ 2.66680138, 2. , 0.66680138],
[ 0.83417602, 1. , -0.16582398],
[ 4.58030892, 4. , 0.58030892],
[ 4.0197366 , 5. , -0.9802634 ],
[ 2.57726657, 3. , -0.42273343],
[ 2.79122591, 2. , 0.79122591],
[ 0.69624294, 1. , -0.30375706],
[ 1.20624761, 2. , -0.79375239],
[ 1.10612152, 1. , 0.10612152],
[ 4.20693688, 3. , 1.20693688],
[ 4.23614686, 4. , 0.23614686],
[ 3.56631529, 5. , -1.43368471],
[ 1.73888174, 2. , -0.26111826],
[ 1.64337964, 1. , 0.64337964],
[ 4.12220661, 5. , -0.87779339],
[ 4.14964689, 4. , 0.14964689],
[ 3.55789362, 3. , 0.55789362],
[ 1.13147051, 1. , 0.13147051],
[ 1.32433383, 2. , -0.67566617],
[ 3.94218514, 3. , 0.94218514],
[ 4.01280332, 4. , 0.01280332],
[ 4.10728622, 5. , -0.89271378],
[ 1.28113118, 2. , -0.71886882],
[ 1.63935595, 1. , 0.63935595],
[ 3.71823625, 3. , 0.71823625],
[ 3.71832546, 4. , -0.28167454],
[ 4.5450203 , 5. , -0.4549797 ],
[ 1.77791569, 2. , -0.22208431],
[ 1.66842039, 1. , 0.66842039],
[ 4.11773873, 4. , 0.11773873],
[ 4.09306547, 5. , -0.90693453],
[ 3.61683769, 3. , 0.61683769],
[ 1.47290005, 1. , 0.47290005],
[ 1.50925726, 2. , -0.49074274],
[ 3.92312391, 0. , 0.92312391],
[ 3.9062157 , 4. , -0.0937843 ],
[ 3.82848362, 0. , -1.17151638],
[ 1.46103827, 1. , 0.46103827],
[ 1.58404067, 2. , -0.41595933],
[ 3.50582218, 0. , -1.49417782],
[ 3.73732263, 4. , -0.26267737],
[ 3.22673005, 3. , 0.22673005],
[ 1.72104299, 2. , -0.27895701],
[ 1.838129 , 1. , 0.838129 ],
[ 3.95118648, 4. , -0.04881352],
[ 4.11197255, 5. , -0.88802745],
[ 3.77340792, 3. , 0.77340792],
[ 1.34400899, 2. , -0.65599101],
[ 1.40548812, 1. , 0.40548812],
[ 3.93117907, 4. , -0.06882093],
[ 3.67388799, 3. , 0.67388799],
[ 4.36215808, 5. , -0.63784192],
[ 2.19485857, 0. , 1.19485857],
[ 1.96226504, 2. , -0.03773496],
[ 4.32091318, 4. , 0.32091318],
[ 4.46082193, 5. , -0.53917807],
[ 3.15809731, 3. , 0.15809731]])
【全体像】
貼っておきます。誤差関数と偏微分あたりがエッセンスかと思われるので、その辺に間違いがないかチェックしてもらえると助かります。
main.py
# coding:utf-8
import numpy as np
from functions_cf import *
import matplotlib.pyplot as plt
sample_size=100
music_size=5
feature_size=3
cover_size=np.int(sample_size*music_size/10)
score=make_score(sample_size, music_size)
data, R=make_data(score, cover_size)
features=np.random.randn(feature_size, music_size)
theta=np.random.randn(sample_size, feature_size+1)
learning_rate=0.3
lam=0.003
iter=2000
J_list=np.array([])
for i in range(iter):
prediction=predict(np.r_[np.ones(features.shape[1]).reshape(1, -1), features], theta)
J=cost_func(data, prediction, R, theta, lam)
delta_theta, delta_features=delta_func(data, prediction, np.r_[np.ones(features.shape[1]).reshape(1, -1), features], R, theta, lam)
theta=theta-learning_rate*delta_theta
features=features-learning_rate*delta_features
J_list=np.r_[J_list, J]
if i%500==0:
print(J)
fig=plt.figure()
plt.plot(np.arange(0, J_list.shape[0], 1), J_list)
plt.show()
sub=prediction-score
result=np.c_[prediction.reshape(-1), data.reshape(-1), sub.reshape(-1)]
np.load('result.npy', result)
functions_cf.py
import numpy as np
def predict(features, theta):
out=np.dot(theta, features)
return out
def cost_func(data, prediction, R, theta, lam):
sub=prediction-data
J=np.sum(sub*sub*R)/(np.sum(R)*2)+np.sum(theta*theta)/(np.sum(R)*2)*lam
return J
def delta_func(data, prediction, features, R, theta, lam):
delta_theta=np.dot((prediction-data)*R, features.T)/np.sum(R)
delta_theta[:, 1:]=delta_theta[:, 1:]+theta[:, 1:]*lam/np.sum(R)
delta_features=np.dot(theta.T, (prediction-data)*R)/np.sum(R)
delta_features=delta_features+features*lam
return delta_theta, delta_features[1:, :]
def make_score(s, m):
score=np.zeros(s*m).reshape(s, m)
mask=np.random.randint(0, 3, s)
for i in range(mask.shape[0]):
if mask[i]==0:
temp=np.random.randn(1)
if temp>=0:
score[i, 0]=5
score[i, 1]=4
else:
score[i, 0]=4
score[i, 1]=5
temp=np.random.randn(1)
if temp>=0:
score[i, 2]=3
score[i, 3]=2
else:
score[i, 2]=2
score[i, 3]=3
score[i, 4]=1
elif mask[i]==1:
temp=np.random.randn(1)
if temp>=0:
score[i, 0]=1
score[i, 1]=2
else:
score[i, 0]=2
score[i, 1]=1
temp=np.random.randn(1)
if temp>=0:
score[i, 2]=5
score[i, 3]=4
else:
score[i, 2]=4
score[i, 3]=5
score[i, 4]=3
else:
temp=np.random.randn(1)
if temp>=0:
score[i, 0]=1
score[i, 1]=2
else:
score[i, 0]=2
score[i, 1]=1
temp=np.random.randn(1)
if temp>=0:
score[i, 2]=3
score[i, 3]=4
else:
score[i, 2]=4
score[i, 3]=3
score[i, 4]=5
return score
def make_data(score, cover_size):
data=score.copy()
data=data.reshape(-1)
mask=np.random.choice(data.size, cover_size)
data[mask]=0
data=data.reshape(score.shape)
R=(data!=0)
return data, R
PS
ガチの機械学習の人に見られたらボコボコにされそう。
0 件のコメント:
コメントを投稿