赞
踩
在前面几节中,我们向大家介绍了基于深度学习的推荐系统的数学原理,在这一节中,我们讨论怎样使用TensorFlow来实现这些数学原理。我们知道,TensorFlow对于深度学习算法的实现有很多资料参考,但是我们前面介绍的推荐系统,与一般的深度学习网络有很大的不同,属于Matrix Factorization的一种,所以在具体实现中,需要对TensorFlow有一个较为深入的了解,才能写出一个较好的解决方案。
我们首先设计个性化题库类,并进行必要的初始化,如下所示:
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from app_global import appGlobal as ag
class PqlEngine(object):
def __init__(self):
print('initialize personalized question lib engine')
self.n = 2 # 知识点数量
self.nm = 5 # 题目总数
self.nu = 5 # 学生总数
self.lanmeda = 0.1 # L2调整项系数
self.epochs = 8000 # 训练遍数
def run(self):
self.Y_ph, self.r, self.mu = self.load_dataset()
self.train()
self.predict(4, 2)
我大家能更好的理解数学原理的代码实现,我们这里的数据,就是前面章节数学理论中的数据,共有五道题,两个知识点,共有5个学生,其中4个学生有做题记录,第5个学生没有做题记录,就是上一节中冷启动时的情况。
程序的入口是run方法,该方法中,首先读入我们的试验数据,然后调用train方法训练模型,然后调用predict方法,预测第4个学生对第2道题目的需要程度。
下面我们首先来看试验数据准备工作,这里我们为了与前面理论部分一致,我们选择了在程序中写死的数据,在实际应用中,这部分数据应该是从数据库学生做题记录中读取出来的。这部分代码如下所示:
def load_dataset(self):
ph = np.zeros(shape=(self.nm, self.nu), dtype=np.float32)
r = np.ones(shape=(self.nm, self.nu), dtype=np.int32)
# first row
ph[0][0] = 5.0
r[0][0] = 1
ph[0][1] = 5.0
r[0][1] = 1
ph[0][2] = 0.0
r[0][2] = 1
ph[0][3] = 0.0
r[0][3] = 1
ph[0][4] = -1.0
r[0][4] = 0
# second row
ph[1][0] = 5.0
r[1][0] = 1
ph[1][1] = -1.0
r[1][1] = 0
ph[1][2] = -1.0
r[1][2] = 0
ph[1][3] = 0.0
r[1][3] = 1
ph[1][4] = -1.0
r[1][4] = 0
# third row
ph[2][0] = -1.0
r[2][0] = 0
ph[2][1] = 4.0
r[2][1] = 1
ph[2][2] = 0.0
r[2][2] = 1
ph[2][3] = -1.0
r[2][3] = 0
ph[2][4] = -1.0
r[2][4] = 0
# forth row
ph[3][0] = 0.0
r[3][0] = 1
ph[3][1] = 0.0
r[3][1] = 1
ph[3][2] = 5.0
r[3][2] = 1
ph[3][3] = 4.0
r[3][3] = 1
ph[3][4] = -1.0
r[3][4] = 0
# fifth row
ph[4][0] = 0.0
r[4][0] = 1
ph[4][1] = 0.0
r[4][1] = 1
ph[4][2] = 5.0
r[4][2] = 1
ph[4][3] = 0.0
r[4][3] = 1
ph[4][4] = -1.0
r[4][4] = 0
# 求出mu(因为仅统计大于等于零项,所以不能用tf.reduce_mean函数)
mu = np.zeros(shape=(self.nm, 1))
for row in range(self.nm):
sum = 0.0
num = 0
for col in range(self.nu):
if 1 == r[row][col]:
sum += ph[row][col]
num += 1
mu[row][0] = sum / num
print('mu={0}!'.format(mu))
print('ph={0}!'.format(ph))
self.refine_ph(ph, mu)
ph = ph - mu
print('ph={0}!'.format(ph))
return ph, r, mu
def refine_ph(self, ph, mu):
for row in range(self.nm):
for col in range(self.nu):
if ph[row][col] < 0.0:
ph[row][col] = mu[row][0]
在这段程序中有几点需要注意的地方,我们用ph表保存前面章节中表格中的数据,r用来保存学生是否做过某道题目,mu表示均值向量。这里需要特别注意的是,我们在求均值向量mu时,我们不能直接用TensorFlow的内置函数tf.reduce_mean,因为我们只统计学生已经做过的题目,而不统计没有做过的题目。最后就是refine_ph函数,我们将所有学生未做过的题目,全部设置为相应的均值。
下面我们来看训练方法:
def calDeltaY(self, Y, Y_):
sum = 0.0
for row in range(self.nm):
for col in range(self.nu):
if 1 == self.r[row][col]:
sum += (Y[row][col] - Y_[row][col])*(Y[row][col] - Y_[row][col])
return sum
def build_model(self):
print('build model')
self.Y_ = tf.placeholder(shape=[self.nm, self.nu], dtype=tf.float32, name='Y_')
self.X = tf.Variable(tf.truncated_normal(shape=[self.nm, self.n], mean=0.0, stddev=0.01, seed=1.0), dtype=tf.float32, name='X')
self.UT = tf.Variable(tf.truncated_normal(shape=[self.n, self.nu], mean=0.0, stddev=0.01, seed=1.0), dtype=tf.float32, name='X')
self.Y = tf.matmul(self.X, self.UT)
self.L = self.calDeltaY(self.Y, self.Y_) #tf.reduce_sum((self.Y - self.Y_)*(self.Y - self.Y_))
self.J = self.L + self.lanmeda*tf.reduce_sum(self.X**2) + self.lanmeda*tf.reduce_sum(self.UT**2)
self.train_step = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.9,
beta2=0.999, epsilon=1e-08, use_locking=False,
name='Adam').minimize(self.J)
def train(self):
self.build_model()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(self.epochs):
X, UT, Y, J, train_step = sess.run([self.X, self.UT, self.Y, self.J, self.train_step], feed_dict={self.Y_: self.Y_ph})
#print(Y)
print('{0}:{1}'.format(epoch, J))
self.Xv = X
self.UTv = UT
在训练方法中,我们首先调用build_model来创建模型。所以我们先来看这个方法。我们先把当前我们通过学生做题情况得到的学习对题目的需要信息,保存到self.Y_中。接下来的处理就与普通深度学习网络不同了,因为我们这里题目和学生向量都是变量,都需要通过梯度下降算法进行优化,所以我们将题目向量self.X和学生向量self.UT都设置为变量,并用均值为0,标准差为0.01的高斯分布随机数进行初始化。我们计算在当前参数情况下,学生对每道题目需求程度的计算值self.Y,我们定义学生对每道题目需求程度的计算值与学生对每道题目需求程度的真实需求值之差为损失函数self.L,我们在损失函数的基础上,添加L2调整项后,作为最终的代价函数,最后我们选用AdamOptimizer对代价函数进行优化,求出其最小值。
在上面需要注意的是在求self.L时,因为我们只对学生做过的题目进行计算,不处理未做过的题目,所以不能直接使用tf.reduce_sum((self.Y - self.Y_)*(self.Y - self.Y_))来计算,而是需要调用calDeltaY方法来计算。
接下来我们启动TensorFlow的Session,开始进行训练,在每一步中,首先根据现有学生向量self.UT和题目向量self.X,计算出对每道题目的需要程度计算值,求出与实际的差距,根据Adam优化随机梯度下降算法,调整学生向量self.UT和题目向量self.X,最后使对每道题目的需要程度计算值与实际值的差最小,将最终的题目向量保存到self.Xv,最终的学生向量保存到self.UTv中。
当模型训练完成后,就可以来预测学生对题目的需要程度了,代码如下所示:
def predict(self, ui, xi):
print(self.Xv[xi])
Uv = np.transpose(self.UTv)
print(Uv[ui])
print(np.dot(self.Xv[xi], Uv[ui]) + self.mu[xi][0])
for row in range(self.nm):
for col in range(self.nu):
print(' {0} '.format(np.dot(self.Xv[row], Uv[col]) + self.mu[row][0]))
print('\r\n')
运行上面的程序,可以得到如下结果:
均值计算结果
学生对每道题目需要程度的实际值:
图中-1代表前面章节表格中的问号,代表学生没有做过这些题。
为了解决冷启动问题,对上述数据进行均值化后的结果:
最后学完的题目向量和学生向量,以及学生4对题目2需要程度的预测值:
我们求出学生对题目的需要程度:
图中的每一块代表之前表格中的一行,由上图可以看出,我们计算出来的需要程度,与实际需要程度还是非常接近的,这从一个侧面证明了我们算法实现是正确的。
通过这5篇文章,读者对基于深度学习的推荐系统,相信已经有了一个清晰的了解,可以根据自己项目的实际情况,来定制化不同的系统了。
最后还要说的一点就是,我们在这里采用的是Matrix Factorization方法,实际上我们可以把上面的算法转换成标准的神经网络形式,变成一个基于神经网络的回归问题,在实际应用中,采用神经网络形式,在很多情况下,结果还是会优于Matrix Factorization的。读者可以想想怎么来实现。我在这里给大家一个提示,就是输入层为学生数量的one-hot矩阵,隐藏层为学生向量的维数,输出层为题目总数。在这里我就不详细展开了,请大家自己动手来实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。