理解梯度下降,随机梯度下降,附电影推荐系统的简单代码小样 1.

来源:互联网 发布:布哈里圣训软件 编辑:程序博客网 时间:2024/05/12 04:40

白话梯度下降:

梯度下降的官方概念网上有很多,说了也没用,反正我刚学的时候是没太看懂。

需要的背景知识,偏导率(很重要)

两个坐标点的距离,是两个向量的点乘积


以实际问题为例,一个电影推荐系统,向量v(v1,v2)代表这个电影本应存在的位置,用户向量u(u1,u2)代表用户存在的位置,现在有实际数据是每个电影的网站评分,和一些用户对某些电影的评分,去评估每个电影应该存在的位置以及每个用户应该存在的位置。


用梯度下降的方法就是,首先得出一个目标方程,这里的目标方程我们选择的是误差方程,所以如下图:



目标方程中 s-ij 代表 0 或者 1, 如果第i个用户对第j个电影有评分,则是1,如果这个用户没有评价这个电影则为0(因为不可能每个用户对所有的电影都有评分,所以这个coefficient 是调整矩阵值的。


图片中的最后一个公式就是对u求的偏导,以下的代码都是根据这个公式展开的。

这里用到梯度下降的原理就是,这个偏导数是现有值对真实值的方向指向,因为方程是根据误差方程得出来的,所以每次用这个偏导值 * 学习速率,就可以使得这个点在固定的步长下,一步一步的逼近正确的值。(在计算过程中,除了电影本身评分和用户的打分 这些观测值不变之外,其他的数值在每一次迭代的时候都是会更新的。

而对v求偏导的结果,和对u的偏导差不多,过程也是假设除v外其他的值都是常量,然后求v的导数。

null——————这部分是数据处理,老师给的代码,就不贴了。

数据处理完成之后,我们需要把数据读取出来:

import pandas as pd
import os
movies = pd.read_csv(os.path.join('/Users/zhoumeng/Documents/Sheffield/machine learning/mlai17/labs/class_movie', 'movies.csv'),encoding='latin-1').set_index('index')

user_names = list(set(movies.columns)-set(movies.columns[:9]))
Y = pd.melt(movies.reset_index(), id_vars=['Film', 'index'], var_name='user', value_name='rating', value_vars=user_names)
Y = Y.dropna(axis=0)

我们可以看一下原始数据的样子:

 Filmindexuserrating148Ghost Rider148David1.0275Pearl Harbor275David3.0319The Scorpion King319David2.0392V for Vendetta392David5.0409X-Men409David3.04248MM3Terry1.0461Ying hung boon sik II40Terry4.0     

下面我们随机生成两个二维数组 分别是用户和电影的预测值,然后通过不断迭代找到他们的值应该存在的位置:

U = pd.DataFrame(np.random.normal(size=(len(user_names), 2))*0.001, index=user_names)
V = pd.DataFrame(np.random.normal(size=(len(movies.index), 2))*0.001, index=movies.index)

将原始数组的评分减去他们的均值:#在我看来 这样做,高于均值的rating为正,低于它的为负,计算的时候自带符号

Y['rating'] -= Y['rating'].mean()


下面就开始描述如何用代码表达梯度下降了:

def objective_gradient(Y, U, V):
    gU = pd.DataFrame(np.zeros((U.shape)), index=U.index)
    gV = pd.DataFrame(np.zeros((V.shape)), index=V.index)
    obj = 0.
    for ind, series in Y.iterrows():
        film = series['index']
        user = series['user']
        rating = series['rating']
        prediction = np.dot(U.loc[user], V.loc[film]) # u^T v
        diff = prediction - rating # u^T v - y
        obj += diff*diff
        gU.loc[user] += 2*diff*V.loc[film]##??????
        gV.loc[film] += 2*diff*U.loc[user]
    return obj, gU, gV

定义了一个随机梯度下降的方法,用pd数组的series将所有数据都迭代一遍,因为一个用户可能评论多个电影,不同用户评论的次数也有所不同,这里将所有次数全部迭代进去的意义在于,评论次数多的用户,我们默认他活跃,所以斜率比重占得高。

接下来是梯度下降的迭代:

learn_rate = 0.01
iterations = 100
for i in range(iterations):
    obj, gU, gV = objective_gradient(Y, U, V)
    print("iteration", i+1, " objective function:", obj)
    U -= learn_rate*gU
    V -= learn_rate*gV

uv被不断修正,整体误差稳定在两位数。

下面是自己探索的一个随机梯度下降:

def stochastic_descent(Y,U,V):
    gU = pd.DataFrame(np.zeros((U.shape)), index=U.index)
    gV = pd.DataFrame(np.zeros((V.shape)), index=V.index)
    
    
    #get a random name
    names = np.array(list(set(np.array(Y.user))))
    random_name = names[np.random.randint(names.shape[0]-1)]
    #get films rated by this user
    films = Y[Y.user==random_name]
    
    for ind, serise in films.iterrows():#using index to represent the film, because one user are connected to more than one film
        rating = serise.rating
        pre = np.dot(U_new.loc[random_name], V_new.loc[serise['index']])
        diff = pre - rating
        gU.loc[random_name] += 2*diff*V.loc[serise['index']]
        gV.loc[serise['index']] += 2*U.loc[random_name]
        
    return gU, gV

主要思想是取用户,然后更新这个用户以及该用户评论的电影们,但是效果不太理想,可能是我的想法有问题,希望大神们给我评论指出。

这里是我不断修改learning_rate 以及iterations 得到的不同结果:

learn_rate = 0.01  #0.01 3700 384.~~~ # 0.03  848 404.~~  #0.05 411 428.~~~  #0.1 335 416.~~~  #0.005 6500 385.~~
                    # 0.015 2180 393.~~ # 0.013 2553 389.~~ #0.11 3331 384.~~  # 0.009 3936 385.~~  #0.001 35507 385.~~
                    # 0.005 40000 386.~~# 0.8 56 474.~~

算上没写的,总过得迭代了超过十万次,也找不到一个很好的rate使得整个误差方程收敛,所以,很无力。。。



原创粉丝点击