精华内容
下载资源
问答
  • 当学习完矩阵的定义以后,我们来学习矩阵的基本运算,与基本性质矩阵的基本运算:矩阵的加法,每一个对应元素相加,对应结果的矩阵例子:矩阵A和矩阵B表示的是同学上学期和下学期的课程的成绩,两个矩阵相加就表示一...

    当学习完矩阵的定义以后,我们来学习矩阵的基本运算,与基本性质

    998cae4b1bc1a662e42a8777888f96bf.png

    矩阵的基本运算:矩阵的加法,每一个对应元素相加,对应结果的矩阵

    1c60923e6bca073c9e0b7a28cc853aa4.png

    例子:矩阵A和矩阵B表示的是同学上学期和下学期的课程的成绩,两个矩阵相加就表示一学年科目成绩的总和

    b97b19bf31fde33235a67a514936e743.png

    矩阵的数量乘法:一个数乘于一个矩阵

    a184459d5c31f36549b4485a43ef9314.png

    还是接着上面学生成绩的例子:

    98cd07539bf57fc5dfda29ceb38d0871.png

    矩阵数量乘法可以理解为,求两学期学生科目成绩的平均分1/2(A+B),因为之前我们已经算出了一学年科目的成绩总和,现在只需要乘于二分之一就可以了。

    矩阵的数量乘法还有一个几何的直观理解:

    下图的矩阵P可以理解为3个行向量组成,这3个行向量表示的是二维平面坐标系中的一个点,就是表示一个三角形,矩阵的数量乘法2.P之后,这个三角形就缩放变大了

    033e08a928d1a1b3997a1a576b683461.png

    矩阵的基本运算性质

    73fc28b9fbe2209732087e4c9d928025.png

    50f1f5ef13261b8d2f1cb2b536a221e7.png

    简单证明:k ⋅(A + B) = k ⋅ A + k ⋅ B(这都还用证????不过出于数学逻辑思维的严谨,还是需要证明的)

    两个矩阵:

    083b7b719e5d62a778402046403efa1d.png

    c87f62185830fd9193ac245c99bbc31d.png

    a2eecced2a691a2524c7dd31b088d0fa.png

    实现矩阵的基本运算

    之前定义的向量类Vector:

    import math
    from ._globals import EPSILON
    class Vector:
     def __init__(self, lst):
     self._values = list(lst)
     @classmethod
     def zero(cls, dim):
     """返回一个dim维的零向量"""
     return cls([0] * dim)
     def __add__(self, another):
     """向量加法,返回结果向量"""
     assert len(self) == len(another), 
     "Error in adding. Length of vectors must be same."
     return Vector([a + b for a, b in zip(self, another)])
     def __sub__(self, another):
     """向量减法,返回结果向量"""
     assert len(self) == len(another), 
     "Error in subtracting. Length of vectors must be same."
     return Vector([a - b for a, b in zip(self, another)])
     def norm(self):
     """返回向量的模"""
     return math.sqrt(sum(e**2 for e in self))
     def normalize(self):
     """返回向量的单位向量"""
     if self.norm() < EPSILON:
     raise ZeroDivisionError("Normalize error! norm is zero.")
     return Vector(self._values) / self.norm()
     def dot(self, another):
     """向量点乘,返回结果标量"""
     assert len(self) == len(another), 
     "Error in dot product. Length of vectors must be same."
     return sum(a * b for a, b in zip(self, another))
     def __mul__(self, k):
     """返回数量乘法的结果向量:self * k"""
     return Vector([k * e for e in self])
     def __rmul__(self, k):
     """返回数量乘法的结果向量:k * self"""
     return self * k
     def __truediv__(self, k):
     """返回数量除法的结果向量:self / k"""
     return (1 / k) * self
     def __pos__(self):
     """返回向量取正的结果向量"""
     return 1 * self
     def __neg__(self):
     """返回向量取负的结果向量"""
     return -1 * self
     def __iter__(self):
     """返回向量的迭代器"""
     return self._values.__iter__()
     def __getitem__(self, index):
     """取向量的第index个元素"""
     return self._values[index]
     def __len__(self):
     """返回向量长度(有多少个元素)"""
     return len(self._values)
     def __repr__(self):
     return "Vector({})".format(self._values)
     def __str__(self):
     return "({})".format(", ".join(str(e) for e in self._values))
    

    定义一个内部使用的文件_globals,用来存储全局使用的变量 EPSILON,用来判断精度用的

    EPSILON = 1e-8
    

    定义的矩阵类Matrix:

    from .Vector import Vector
    class Matrix:
     def __init__(self, list2d):
     self._values = [row[:] for row in list2d]
     @classmethod
     def zero(cls, r, c):
     """返回一个r行c列的零矩阵"""
     return cls([[0] * c for _ in range(r)])
     def __add__(self, another):
     """返回两个矩阵的加法结果"""
     assert self.shape() == another.shape(), 
     "Error in adding. Shape of matrix must be same."
     return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))]
     for i in range(self.row_num())])
     def __sub__(self, another):
     """返回两个矩阵的减法结果"""
     assert self.shape() == another.shape(), 
     "Error in subtracting. Shape of matrix must be same."
     return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))]
     for i in range(self.row_num())])
     def __mul__(self, k):
     """返回矩阵的数量乘结果: self * k"""
     return Matrix([[e * k for e in self.row_vector(i)]
     for i in range(self.row_num())])
     def __rmul__(self, k):
     """返回矩阵的数量乘结果: k * self"""
     return self * k
     def __truediv__(self, k):
     """返回数量除法的结果矩阵:self / k"""
     return (1 / k) * self
     def __pos__(self):
     """返回矩阵取正的结果"""
     return 1 * self
     def __neg__(self):
     """返回矩阵取负的结果"""
     return -1 * self
     def row_vector(self, index):
     """返回矩阵的第index个行向量"""
     return Vector(self._values[index])
     def col_vector(self, index):
     """返回矩阵的第index个列向量"""
     return Vector([row[index] for row in self._values])
     def __getitem__(self, pos):
     """返回矩阵pos位置的元素"""
     r, c = pos
     return self._values[r][c]
     def size(self):
     """返回矩阵的元素个数"""
     r, c = self.shape()
     return r * c
     def row_num(self):
     """返回矩阵的行数"""
     return self.shape()[0]
     __len__ = row_num
     def col_num(self):
     """返回矩阵的列数"""
     return self.shape()[1]
     def shape(self):
     """返回矩阵的形状: (行数, 列数)"""
     return len(self._values), len(self._values[0])
     def __repr__(self):
     return "Matrix({})".format(self._values)
     __str__ = __repr__
    

    测试代码:

    from playLA.Matrix import Matrix
    if __name__ == "__main__":
     matrix = Matrix([[1, 2], [3, 4]])
     print(matrix)
     print("matrix.shape = {}".format(matrix.shape()))
     print("matrix.size = {}".format(matrix.size()))
     print("len(matrix) = {}".format(len(matrix)))
     print("matrix[0][0] = {}".format(matrix[0, 0]))
     matrix2 = Matrix([[5, 6], [7, 8]])
     print(matrix2)
     print("add: {}".format(matrix + matrix2))
     print("subtract: {}".format(matrix - matrix2))
     print("scalar-mul: {}".format(2 * matrix))
     print("scalar-mul: {}".format(matrix * 2))
     print("zero_2_3: {}".format(Matrix.zero(2, 3)))
    展开全文
  • 前言在上文章中,我们讲到了推荐系统中矩阵分解三种方法。而这三种基本方法中,Funk-SVD由于其对稀疏数据处理能力好以及空间复杂度低,是最合适推荐系统情景,(Funk-SVD只是这三基本方法里最好,不...

    前言

    在上一篇的文章中,我们讲到了推荐系统中矩阵分解的三种方法。而这三种基本方法中,Funk-SVD由于其对稀疏数据的处理能力好以及空间复杂度低,是最合适推荐系统情景的,(Funk-SVD只是这三个基本方法里最好的,不代表就是推荐系统中最好的,还有更多衍生出来的优秀的方法,未来会给大家介绍)我们这篇文章就以Funk-SVD为基础,为大家介绍下如何求解矩阵分解时运用的梯度下降法以及其具体代码的实现。(梯度下降法的思想不仅运用于求解矩阵分解,同时也是神经网络中反向传播的基础,因此,希望大家多多留意。)

    这是上一篇矩阵分解的基本方法和其优缺点,有兴趣的朋友可以看一看:
    推荐系统玩家:推荐系统玩家 之 矩阵分解(Matrix Factorization)的基本方法 及其 优缺点zhuanlan.zhihu.com

    矩阵分解的认识

    那么首先,我们来回顾一下矩阵分解的过程:

    1bfa484a827f13173d75cfeb7f981978.png

    矩阵分解算法是将
    维的矩阵
    分解为
    维的用户矩阵
    的物品矩阵
    相乘的形式。其中,
    为用户的数量,
    为物品的数量,
    为隐向量的维度。基于分解后的两个矩阵
    ,通过物品行向量和对应矩阵行向量的内积,我们就可以得到任何用户
    对物品
    的预估评分,即预测值:

    其中,

    是用户
    在矩阵
    中对应的行向量,
    是物品
    在物品矩阵
    中对应的行向量。从这里也可以很清楚看出矩阵分解在推荐系统中的意义:一个稀疏的矩阵通过分解成为两个低维的矩阵,再通过这两个矩阵的行向量之间的内积,就可以得到一个满秩的预测评分矩阵。

    那么问题来了,我们如何将矩阵

    分解成矩阵用户矩阵
    和物品矩阵
    的呢?

    首先,从上面的式子我们可以知道,

    是矩阵分解后,通过用户向量
    和物品向量
    的内积得到的预测评分。我们的目的是让预测评分
    和真实评分
    的差尽可能的小,即
    最小。这样内积后的新矩阵才会更好拟合原矩阵,保存原始的信息的同时,对未知评分有准确的预测。

    基于以上原理,我们采用均方差做为损失函数可以得到:

    其中,

    是所有用户评分样本的集合。因为为了减少拟合,我们在上式的后面加入正则化项。那么我们最终的
    损失函数就变为:

    在这里,我们需要说明下,损失函数的种类也有很多,Simon Funk 在这里运用均方根误差,也就是我们所熟悉的RMSE作为损失函数,同时这也就是说Funk-SVD引入了线性回归思想的体现。同时正则化的种类也不只以上这一种。因此,我们在后续的文章中还会专门介绍损失函数的种类以及什么是过拟合现象和正则化。

    那么当我们已经有了损失函数,下一步就是要求损失函数的最小值,这就是我们常说的最小化损失函数。那么,如何最小化损失函数呢?

    梯度下降法

    至此,我们来到了本篇文章的重点。说实话,在学习这部分的时候,我始终处于懵的状态,因为无论是书籍还是知乎上,都用了大面积的数学公式来讲解导数,偏导数,多元函数求偏导等等,导致我很难理解,那么在这里我会用简单的思路来解释。但希望大家看后有不一样的理解。其中,导数,微分的定义与推导的基本知识我默认大家都有,想要复习的朋友可以看下这篇:

    初中生能看懂的梯度下降算法mp.weixin.qq.com
    d7a57c59914bfc01cea00a330f8f1bb1.png

    那么,我在这里主要会解决这三个问题:

    • 什么是梯度?
    • 为什么梯度反方向是函数值局部下降最快的方向?为什么沿着梯度的反方向更新参数?
    • 梯度下降如何运用于推荐系统中求解矩阵分解

    1.什么是梯度

    528df183a6e99eb5cb129818775f2fde.png

    首先明确几个概念,一元函数曲线上的切线斜率叫做导数, 也就是函数在该点的变化率。那么多元函数是个曲面,曲面上的切线可以有无数条。那么,我们找曲面上沿

    轴方向的切线斜率和与沿着
    轴方向的切线斜率就是我们常说的偏导数。因此
    偏导数就是指的是多元函数沿坐标轴的变化率。那么,我们可以看出,偏导数是沿着坐标轴的变化率,当我要想求沿任意方向的变化率怎么办呢? 这时候方向导数的概念就出现了。因为数学公式对很多朋友都不太友好,我在这里就不大面积列举公式了。我们只需要知道方向导数可以用来表示曲面上任意方向的变化率。那么既然可以求任意方向的变化率了,是不是就可以找到一个变化率最大的方向呢?答案是肯定,而这个函数变化率最大的方向就是梯度的方向,这个点函数的最大变化率就是就是梯度

    这里有一个三维的视频非常好帮助大家理解的导数,偏导数以及梯度的意义:

    推荐系统玩家 之 导数,偏导数,方向导数的三维视图_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliwww.bilibili.com
    cd30cfe5d3c551f9272c56685b348a3d.png

    2.为什么梯度反方向是函数值局部下降最快的方向?(梯度下降的推导)

    高中的时候,我们学过一个公式叫做泰勒公式,在这里,我们以一阶泰勒展开式为例:

    bf81843e93b634ecfdb655e0baba835a.png

    如上图,函数

    上用黑色曲线表示。红色的线为函数在
    上的切线,那么利用极限近似的思想
    就可以用上式近似。 其中,
    为函数的导数,也就是切线的斜率。(因为导数在物理上有瞬时速度的意义,我们可以抽象的将上式理解为纵坐标路程就等于横坐标时间乘以斜率速度。)

    理解了一阶泰勒展开式我们继续向下看,公式中的

    是一个微小的矢量,它代表着变化的距离,它的大小我们可以用
    来表示,
    为标量,即一个确定的数。
    的单位向量则用
    来表示:

    因此上式就变为:

    我们做梯度下降的意义就在于最小化损失函数,也就是最小化

    ,同时,梯度下降是个迭代的过程。也就是说,我们希望每次更新
    后,都能够让
    的值变小。因此
    要小于0,则有:

    其中,

    为标量且一般为正值,可以忽略,因此:

    在这里,我们需要仔细研究下这个简单的不等式。首先,

    均为向量,且
    为导数,在这里我们叫做它梯度,它的方向是函数变化率最大的方向。
    就是我们下一步要前进的方向。从公式来看以上两个向量相乘需要小于0。向量相乘的公式是包含夹角的:

    fbd6e727739d4cacc5099c1eb7f9c6a6.png

    从上图可以看出,当

    也就是第三种情况,两个向量的方向完全相反,就可以让向量相乘的积小于0且是最小的。也就是说,当
    的方向相反时,
    且最小。

    从这里我们就可以看出,为什么梯度反方向是函数值局部下降最快的方向。在上面我们提到过梯度方向是指函数变化率最大的方向,也就是斜率越来越大的方向,曲线会越来越陡。那么梯度的反方向,就是函数值局部下降最快的方向,斜率是越来越小的,斜率最终会为0。这也就是为什么我们高中求解二元一次方程组的最小值的时候,求导后需要导数等于0。

    5c1fc44a8927c4a1b37fd6c42130916f.png

    当我们知道了

    的方向是
    的反方向后,我们就可以得到:

    因为

    是单位向量,所以除以
    的模。将上式带入到
    中可得:

    因为向量的模是个标量,因此:

    其中,

    就是当前参数,
    就代表
    步长,也就是我们所说的学习率
    为梯度的方向,
    减号就代表沿着梯度的负方向。

    至此,我们就得到了梯度下降法中参数

    更新的表达式

    说了这么多,我相信还有很多朋友很懵。因为我在学习梯度下降初期,也不明白为什要理解什么表达式,为什么要知道这么多导数微分的知识。所以,我用一个例子,来给大家解释下我们上面那么长篇幅所学到内容的意义所在。

    首先我们来看一个最简单的例子,我们目标函数为:

    。我们想最小化这个函数,即求这个函数的最小值。很多朋友会说,求这个函数的解很简单啊,不就求导数,然后另导数等于0不就行了么。是的,从初高中的知识来看,我们确实可以这么做,因为这个目标函数是一个一元二次方程。如果当目标函数是个多变量函数,例如:
    。还能那种方法来求么?显然是不行的。因此,我们就需要运用近似的方法,通过梯度下降的迭代方法逐渐逼近最优解。

    那么我来看下以

    为目标目标函数做梯度下降的过程:

    首先我们设

    的初始值为想
    ,学习率
    。迭代的次数为10次,也就是说
    更新10次。那么,根据梯度下降参数更新的公式:

    的导数为
    。因此,
    第一次更新:
    ,得
    ,将更新后的
    带入继续更新,以此类推。在这里我们放上python的代码:
    %matplotlib inline
    import d2lzh as d2l
    import math
    from mxnet import nd
    import numpy as np
    
    def gd(eta):
        x = 10
        results = [x]
        for i in range(10):
            x -= eta * 2 * x  # f(x) = x * x的导数为f'(x) = 2 * x
            results.append(x)
        print('epoch 10, x:', x)
        return results
    
    res = gd(0.2)

    绘制

    迭代轨迹图可以看到:
    def show_trace(res):
        n = max(abs(min(res)), abs(max(res)), 10)
        f_line = np.arange(-n, n, 0.1)
        d2l.set_figsize()
        d2l.plt.plot(f_line, [x * x for x in f_line])
        d2l.plt.plot(res, [x * x for x in res], '-o')
        d2l.plt.xlabel('x')
        d2l.plt.ylabel('f(x)')
    
    show_trace(res)

    5369b60d61c5a1e8e67638f89afc934a.png

    在最后的值为:0.0604661759999999,也就是最后的结果。我们都知道,原方程的解应该为
    ,那么为什么最后我们的解跟它有误差呢?因为梯度下降法是通过迭代的方法无线逼近于最小值,其结果和学习率以及迭代次数都有着很大的关系。因此,通过调整参数,就可以优化我们想要的结果。

    我们在里稍微讲一下学习率

    在梯度下降中起到的作用。学习率
    是一个超参数,在实际的实验中需要我们人为设定,合适的学习率可以很大程度上提高样本训练的精度。这个值,不能过大也不能过小,学习率太小可能会导致无法到达最优解,迭代就已经完成了。过大会让解来回震荡,无法走到最小值,我们来看个例子:

    同样是目标函数

    。当我们调整学习率
    时,其他参数不变,最终的
    如下图:

    b3e39361533f34a5507d528ad0e7028b.png

    当我们调整学习率为

    时:

    7ed107474c2f161db5bb17eae29415f7.png

    的值会在曲线两边来回震荡,无法达到最优解。

    因此可以看出,

    学习率的值是个非常关键的参数,可能直接影响模型的好坏。我们在实际运用过程中,需要经验以及不断的测试来找到最优参数。当然,如今也有很多研究在寻找一个自动调参数的方法,我们也希望有更好的方法来帮助我们找到合适的参数。

    3.随机梯度下降如何运用于推荐系统中求解矩阵分解

    能看到这里,我相信大家对于梯度下降应该已经有了一个很直观的理解。梯度下降的方法其实还分为批量梯度下降法BGD,随机梯度下降SGD以及min-batch 小批量梯度下降法MBGD,那么,在这里,我们会讲解随机梯度下降是如何求解推荐系统中的矩阵分解的。

    我们以上的概念和例子都是基于最简单的一维函数的。那么对于多维函数,又该怎么办呢?道理其实是一样,对一元函数,我们求其导数。那么对于多元函数,我们就分别求其偏导数

    那么梯度下降参数更新的公式就变为:

    还记得我们做矩阵分解时得到的损失函数么?

    这其中

    ,
    为我们需要优化的变量,
    为正则化稀疏,和
    学习率一样是一个参数,标量。
    为用户对物品真实的打分。因此,我们的目的就是优化
    找到损失函数的最小值,从而拟合出一个用户矩阵和一个物品矩阵,他们内积后得到一个与原评分矩阵误差最小的预测评分矩阵。

    那么,我们的对损失函数中的

    ,
    分别求偏导可得:

    将以上两个偏导分别带入梯度下降参数更新公式

    得:

    在以上公式中,

    即为当前真实评分和预测评分的误差
    ,因此化简可得:

    至此,随机梯度下降法的原理就解释完毕了。当然,梯度下降的方法还分为批量梯度下降法BGD,随机梯度下降SGD以及min-batch 小批量梯度下降法MBGD,我们就不在这里一一具体讲解,有兴趣的朋友可以看看这篇文章:

    忆臻:详解梯度下降法的三种形式BGD、SGD以及MBGDzhuanlan.zhihu.com
    a5955ecca1f0d28492a893f7e55cfcc6.png

    当我们有了参数更新的公式,就可以付诸于代码了。以下便为随机梯度下降求解矩阵分解的python代码和Matlab代码,同时数据集可以用Movielens 100k 开源数据集进行测试:

    MovieLens 100K Datasetgrouplens.org
    5808c61c75885a6904f2a57a47fa929f.png

    Python:

    def MF(train_list, test_list, N, M, K=10, learning_rate=0.001, lamda_regularizer=0.1, max_iteration=50):
        # train_list: train data 
        # test_list: test data
        # N:the number of user
        # M:the number of item
        # learning_rate: the learning rata
        # K: the number of latent factor
        # lamda_regularizer: regularization parameters
        # max_iteration: the max iteration
        
        P = np.random.normal(0, 0.1, (N, K))
        Q = np.random.normal(0, 0.1, (M, K))
        
        train_mat = sequence2mat(sequence = train_list, N = N, M = M)
        test_mat = sequence2mat(sequence = test_list, N = N, M = M)
        
        records_list = []
        for step in range(max_iteration):
            los=0.0
            for data in train_list:
                u,i,r = data
                P[u],Q[i],ls = update(P[u], Q[i], r=r, learning_rate=learning_rate, lamda_regularizer=lamda_regularizer)
                los += ls
            mae, rmse, recall, precision = evaluation(P, Q, train_mat, test_mat)
            records_list.append(np.array([los, mae, rmse, recall, precision]))
            
            if step % 10 ==0:
                print(' step:%d n loss:%.4f,mae:%.4f,rmse:%.4f,recall:%.4f,precision:%.4f'
                      %(step,los,mae,rmse,recall,precision))
            
        print(' end. n loss:%.4f,mae:%.4f,rmse:%.4f,recall:%.4f,precision:%.4f'
              %(records_list[-1][0],records_list[-1][1],records_list[-1][2],records_list[-1][3],records_list[-1][4]))
        return P,Q,np.array(records_list)
    
    
    def update(P, Q, r, learning_rate=0.001, lamda_regularizer=0.1):
        error = r - np.dot(P, Q.T)            
        P = P + learning_rate*(error*Q - lamda_regularizer*P)
        Q = Q + learning_rate*(error*P - lamda_regularizer*Q)
        loss = 0.5 * (error**2 + lamda_regularizer*(np.square(P).sum() + np.square(Q).sum()))
        return P,Q,loss

    Matlab:

    function [F, G] = mySVDBiasedV2(X,testSet,option)
    
    k = option.k;
    lRate = option.lRate;
    lambda1 = option.lambda1;
    iteLimit = option.iteLimit;
    
    [rowNum colNum] = size(X);
    
    XInd = find(X);
    
    a = -1;
    b = 1;
    
    F = (b-a).*rand(rowNum, k) + a;
    G = (b-a).* rand(colNum, k) + a;
    
    rowSum = (b-a) .* rand(rowNum, 1) + a;
    colSum = (b-a) .* rand(colNum, 1) + a;
    
    M = F * G';
    
    error = [Inf];
    errorRMSE = [Inf];
    
    trainErr = [Inf];
    trainRMSE = [Inf];
    
    iteNum = 0;
    
    while true
        for rInd = 1:length(XInd)
            curError = X(XInd(rInd)) - M(XInd(rInd));
            
            [curR, curC] = ind2sub([rowNum colNum], XInd(rInd));
            
            for kInd = 1:k
                F(curR, kInd) = F(curR, kInd) + lRate * curError * G(curC, kInd) - lambda1 * F(curR, kInd);
                G(curC, kInd) = G(curC, kInd) + lRate * curError * F(curR, kInd) - lambda1 * G(curC, kInd); 
            end
        end
        
        M = F*G';
        
        p = [];
        
        trainRMSE = [trainErr sqrt(sum((M(XInd) - X(XInd)).^2)/length(XInd))];
        trainErr = [trainErr sum(abs(M(XInd) - X(XInd)))/length(XInd)];
        
        if (~isempty(testSet))
            for testInd = 1:size(testSet, 1)
                p = [p M(testSet(testInd, 1), testSet(testInd, 2))];
            end
            errorRMSE = [error sqrt(sum((p - testSet(:, 3)').^2)/size(testSet, 1))];
            error = [error sum(abs(p - testSet(:, 3)'))/size(testSet, 1)];
        end
        
        iteNum = iteNum + 1;
        
        fprintf('iteNum: %d, (trainMAE = %f, testMAE = %f), (trainRMSE = %f, testRMSE = %f)n', iteNum, trainErr(end), error(end), trainRMSE(end), errorRMSE(end));
        if trainErr(end) > trainErr(end-1) || iteNum > iteLimit
            plot(trainErr, '-ob');
            if (~isempty(testSet))
                hold on
                plot(error, '-sr');
                hold off
            end
            return;
        end
    end

    上一篇:

    推荐系统玩家:推荐系统玩家 之 矩阵分解(Matrix Factorization)的基本方法 及其 优缺点zhuanlan.zhihu.com

    下一篇:

    推荐系统玩家:推荐系统玩家 之 推荐系统入门——推荐系统的发展历程(上)zhuanlan.zhihu.com

    参考文献

    1.https://zh.d2l.ai/chapter_optimization/gd-sgd.html

    2.为什么局部下降最快的方向就是梯度的负方向?_红色石头的专栏-CSDN博客_负梯度方向

    3.https://zhuanlan.zhihu.com/p/30759733

    4.https://mp.weixin.qq.com/s/jrMs8U_Mg82lPxZB3h4SbQ

    5.https://zh.d2l.ai/chapter_optimization/gd-sgd.html

    6.https://zhuanlan.zhihu.com/p/33321183

    7.https://zhuanlan.zhihu.com/p/25765735

    展开全文
  • 一道模板题,更重要省选难度..... 题目要求一个n*n矩阵,...(1)怎么求矩阵? 首先,我们要开一个二维数组,范围是a[401][801]; why?这就和求逆矩阵有关啦。 先输入n*n的矩阵,紧接着在右...

    原题链接 https://www.luogu.org/problemnew/show/P4783

    一道模板题,更重要的省选难度.....

    题目要求的是一个n*n的逆矩阵,还要对大数取膜。

    普通高中生:这…………

    来一步一步分析:

    (1)怎么求逆矩阵?

     

    首先,我们要开一个二维数组,范围是a[401][801];

    why?这就和求逆矩阵有关啦。

    先输入n*n的矩阵,紧接着在右边罗一个n*n的单位矩阵

    然后我们对左半边的矩阵进行高斯消元消成了单位矩阵,则此时右半边的单位矩阵就被消成了左半边原矩阵的逆矩阵

        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
           {
           for(int j=1;j<=n;j++)
           scanf("%lld",&a[i][j]);      
           a[i][i+n]=1;         //在输入矩阵的同时在右边罗列一个n*n的单位矩阵 
           }

    接着就要对左半边的矩阵进行高斯消元,怎么高斯消元呢?其实就是把高斯消元的板子套上去啊。P3389

    这是模板高斯消元的代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    int n,pl;
    double a[1001][1001];
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
           for(int j=1;j<=n+1;j++)
           cin>>a[i][j];
        for(int i=1;i<=n;i++)
        {
            pl=i;
            while(a[pl][i]==0&&pl<=n) 
            {pl++;}                                      // 判断第i列首元素非0的最上行,因为第i行第i列元素不能为0 
            if(pl==n+1) {cout<<"No Solution";return 0;}    //一直判到了n+1行,可是一共才只有n行,说明有一列全为0,无解 
            for(int j=1;j<=n+1;j++)             //将第i行第i列元素不为0的那一行与当前行交换 
            swap(a[i][j],a[pl][j]);
            double k=a[i][i];                          //让第i行每个元素都除以a[i][i]使得a[i][i]为1 
            for(int j=1;j<=n+1;j++)
            a[i][j]=a[i][j]/k;                         //将第i行第i列的元素消成1,注意同行进行同样的操作 
            for(int j=1;j<=n;j++)
            {
                if(i!=j)                        //将第i列除了第i行的元素全消成0 
                {                               //方法是第j行每个元素a[j][m]都减去a[j][1]*a[i][m] 
                    double ki=a[j][i];
                    for(int m=1;m<=n+1;m++)
                    a[j][m]=a[j][m]-ki*a[i][m];
                }
            }
        }
        for(int i=1;i<=n;i++)
        printf("%.2lf\n",a[i][n+1]);
        return 0;
    }

    (2)怎么对有理数取膜?

    这也是道模板题…………P2613

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const long long mod=19260817;
    inline long long read()
    {
        long long t=0;
        char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9')
        {
            t=(t*10+(ch-'0'))%mod;
            ch=getchar();
        }
        return t;
    }
    int exgcd(long long a,long long b,long long &x,long long &y)
    {
        if(b==0)
        {
            x=1;y=0;
            return a;
        }
        long long r=exgcd(b,a%b,x,y);
        long long q=x;
        x=y;
        y=q-a/b*y;
        return r;
    }
    int main()
    {
        long long a,b,x,y;
        a=read();
        b=read();
        if(b==0)
        {
            cout<<"Angry!";
            return 0;
        }
        exgcd(b,mod,x,y);
        x=(x%mod+mod)%mod;
        printf("%lld",(a%mod*x%mod)%mod);
        return 0;
    }

    证明过程:

    1.费马小定理

    如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。

    此题已经明确给出mod数19260817,显然它是一个质数,那么我们就可以用费马小定理转化一下,如下:

    因为a^(p-1)≡1(mod p)

    所以a^(p-2)≡a^(-1) (mod p)    (A)

    所以c=a/b=a*b^(-1)≡a*b^(p-2) (mod p)

    证毕!

    所以我们就可以将在膜p意义下的a/b转化成a*b^(p-2)的形式,所以我们只要求出b^(p-2)就大功告成啦,具体做法用快速幂。

    2.扩展欧几里德

    上面已经证过求在膜p意义下的a/b就是求a*b^(-1),b^(-1)就是b的逆元

    下面给出求b的逆元的一种方法:

    若存在一个数x,满足bx≡1 (mod p),那么x就是b的逆元

    可将bx≡1 (mod p)进一步转化:

    bx-1≡0 (mod p)

    bx-1=-yp    (注:这里说一下为什么是-y,其实这里是不是正负无所谓,写成负的更便于理解)

    bx+py=1

     

    二者一结合,就是此题的代码:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<math.h>
    using namespace std;
    const int mod=1000000007;
    long long n,a[501][1001];
    long long x,y;
    inline int read()
    {
        int t=0;
        char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'||ch<='9')
        {
            t=t*10+(ch-'0');
            ch=getchar();
        }
    }
    int inv(long long a,long long b)
    {
        long long ans=1;
        while(b)
        {
            if(b&1) ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans%mod;
    }
    int main()
    {
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
           {
           for(int j=1;j<=n;j++)
           scanf("%lld",&a[i][j]);      
           a[i][i+n]=1;         //在输入矩阵的同时在右边罗列一个n*n的单位矩阵 
           }
        for(int i=1;i<=n;i++)       //进行高斯消元 
        {
            for(int j=i;j<=n;j++)
            {
                if(a[j][i])
                {
                    for(int q=1;q<=2*n;q++)
                    swap(a[j][q],a[i][q]);
                    break;
                }
            }
            if(!a[i][i])             //判无解 
            {
                cout<<"No Solution";return 0;
            }
            long long k=inv(a[i][i],mod-2)%mod;      //利用费马小定理来求逆元 
            for(int j=1;j<=2*n;j++)  a[i][j]=a[i][j]*k%mod;     //利用矩阵性质将a[i][i]消成1,注意同样对右半边的单位矩阵操作 
            for(int j=1;j<=n;j++)                   //将第i列的其他行消成0 
            {
                if(j!=i)
                {
                    long long k=a[j][i];
                    for(int m=i;m<=2*n;m++)           //注意同时对右半边的单位矩阵进行操作 
                    {
                    a[j][m]=a[j][m]-k*a[i][m];
                    a[j][m]=(a[j][m]%mod+mod)%mod;
                    }
                }
            }
        }
        for(int i=1;i<=n;i++)
           {
               for(int j=1+n;j<=2*n;j++)                 //输出右半边的矩阵就是逆矩阵啦 
               cout<<a[i][j]<<" ";
               cout<<endl;
           }
        return 0;                                     //完结撒花 
    }

    话说真的这道题就是两个模板题的结合qwq~

    转载于:https://www.cnblogs.com/xcg123/p/10700507.html

    展开全文
  • 【模板】矩阵行列式

    2018-04-04 22:54:44
    对于一个矩阵的行列式,有很多性质 如下面的这个矩阵: ⎡⎣⎢147258369⎤⎦⎥&amp;amp;amp;nbsp;[123456789]&amp;amp;amp;nbsp; \begin{bmatrix} 1 &amp;amp;amp; 2 &amp;amp;amp; 3 \\ 4 &...

    先贴一波知乎的问题:行列式的本质是什么? (虽然本蒟蒻自己都没怎么看)

    对于一个矩阵的行列式,有很多性质
    如下面的这个矩阵:

    147258369 [123456789] 

    我们可以把它变成这样:
    147258369|||147258369|||147258369 123|123|123456|456|456789|789|789 

    于是行列式就是

    每条黑色连线上的数相乘之和减去每条红色连线上的数相乘之和。(如有错误,欢迎指正)

    那么可以发现,若该连线上有任何一个数为0,这条线就作废了(贡献为0)。这也给了我们不同暴力而用更优的方法求解矩阵行列式的机会。

    于是我们可以构造一个上三角矩阵,可以发现:在上三角矩阵里,行列式即为矩阵对角线上的数的乘积。因为其他线上都有0了。还是按上面的图来讲,4,7,8的位置为0了,于是两条黑线三条红线都废了。

    在构造上三角矩阵的过程中,我们可以模仿欧几里得求gcd的过程。

    代码如下:

    inline int getdet()
    {
        int det=1;int flag=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                int x=i,y=j;
                while(a[y][i]!=0)
                {
                    int t=a[x][i]/a[y][i];
                    for(int k=i;k<=n;k++)    a[x][k]-=t*a[y][k]
                    swap(x,y);
                }
                if(x!=i){
                   for(int k=1;k<=n;k++){
                       swap(a[x][k],a[i][k]);
                   }
                   flag^=1;
                }
            }
            if(a[i][i]==0)  return 0;
            det=det*a[i][i]%mod;
        }
        if(flag) det=-det;
        return det;
    }
    

    upd:在bzoj1494: [NOI2007]生成树计数中学习到另外一种非常便于手算的求行列式方法:
    这里写图片描述

    展开全文
  • 中文题意不在叙述,只是让求一个矩阵的乘法而已。 【思路】 终于可以做矩阵快速幂的题了,这个专题一直被我拖到现在,作为一个弱弱内心无比难受,终于可以把它学了,十分开心。 此题非常裸,直接重载一个乘号...
  • 矩阵一个神奇作用,它可以用来快速递推式第nnn项,学会这个技 能,你需要掌握这两个前置芝士 矩阵快速幂, 矩阵加速(数列) 具体怎么优化呢? 这个博客已经总结较为全面,在这里我就不再加赘述。 代码 贴...
  • 题意:给一个线性无关组A,再给一个B,要为A中每个向量在B中选一个可以代替向量,替换后仍然线性无关。判断可行和字典序最小解 PoPoQQQ orz 显然是一个二分图匹配模型 A是一个线性基,用它把B中每个向量...
  • 给定棵 nnn 树,点带点权。 有 mmm 次操作,每次操作给定 x,yx,yx,y ,表示修改点 xxx 权值为 yyy 。 你需要在每次操作之后出这棵树最大权独立集权值大小。 题目分析 假如没有修改操作,这题...
  • 关于CT图像重建,投影矩阵的产生是基础啊!!! 什么是投影矩阵呢???专业的术语我答不上来,但是我觉得就是实际医疗设备采集到的...我觉得有种用已知做未知再未知的过程,,,,这过程我还可以理解。 关键的
  • 为了方便,此后推导线性回归模型格式写为如此格式(权重和偏置项同处一个矩阵中)此步推导也写在下方: 下面正式进入推导: 因为标准正态分布累积分布函数无法用积分出,所以我们引入一个近似累积分布...
  • UVa 10870 & 矩阵快速幂

    2016-03-04 15:32:00
     求一个递推式(不好怎么概括。。)函数值。  即 f(n)=a1f(n-1)+a2f(n-2)+...+adf(n-d); SOL:  根据矩阵乘法定义我们可以很容易地构造出矩阵,每次乘法即可出下一位f(n)值并在距震中保存f(n)-----f...
  • pku3070 (矩阵1)

    2010-07-28 17:30:00
    http://162.105.81.212/JudgeOnline/problem?id=3070<br />题意很简单,给出一个数n,就是要求第n个斐波纳契数10000(就是最后4位数... 那么对于一个矩阵怎么来取模呢?实际上这与整数取模幂运算a^b m
  • 讲:层次分析法 ...一致矩阵的例子 一致性检验 一致性检验的步骤 两小问题 一致矩阵怎么计算权重 判断矩阵计算权重 方法1:算数平均法算权重 方法2:几何平均法权重 方法3特征值权重
  • 题意:一个长度为n项链,m种颜色染色每个珠子。一些限制给出有些颜色珠子不能相邻。旋转后相同视为相同。有多少种不同项链? 思路:这题有点综合,首先,我们对于每个n因数i,都考虑这个因数i下不变置换个...
  • 一个问题问下这两个属性是做什么用? 第二个问题,如果我想放大模型应该怎么做?是在osgb转换到b3dm过程中入手 还是在转换完成之后再做调整?如果是转换完成了之后,我应该怎么...
  • 导出模型失败

    2020-12-08 18:03:31
    不同于yolov3是直接输出一个[M, 6]张量,我网络输出了4个张量。 导出模型(export_model.py脚本)时是没有问题,最后提示 2020-03-29 16:11:12,077-INFO: Export inference model to ...
  • Floyd算法弗洛伊德算法是什么弗洛伊德算法与其他最短路径算法异同弗洛伊德算法伪代码:核心就是一个三重循环如何记录最短路径经过点弗洛伊德算法代码:Floyd_algorithm.mPrint_all_path.m实例:得到path矩阵...
  • 可我找不到任何方法来声明这样函数——感觉我需要一个返回指针函数,返回指针指向又是返回指针函数……,如此往复,以至无穷。 数组大小 1.23 能否声明和传入数组大小一致局部数组,或者由其他参数指定...
  • 大致是这样模型:有一个由0和1组成矩阵,然后要求最大的一个矩阵使得其中只包含0(1同理)。 那么这个怎么做呢? 当然是可以做(废话),结合最大正方形方法,我们可以这样乱搞: 首先预处理出所有点...
  • 《你必须知道495C语言问题》

    热门讨论 2010-03-20 16:41:18
    可我找不到任何方法来声明这样函数——感觉我需要一个返回指针函数,返回指针指向又是返回指针函数……,如此往复,以至无穷。 12  数组大小 13 1.23 能否声明和传入数组大小一致局部数组,或者由...
  • 可我找不到任何方法来声明这样函数——感觉我需要一个返回指针函数,返回指针指向又是返回指针函数……,如此往复,以至无穷。 12  数组大小 13 1.23 能否声明和传入数组大小一致局部数组,或者由...
  • 今天在写图算法时,想到如果要实现了求一个节点相邻节点,该怎么写:稀疏图是邻接表实现:稠密图是邻接矩阵实现:问题:得到g这个变量进行操作,保持g变量私有特性,而又让外部能遍历到这个数据?...
  • 关于D3D一些知识

    2012-11-22 23:06:49
    这个人写了很多D3D东西,有空...有一个方法是根据世界矩阵,相机矩阵,投影矩阵计算出视锥体(可以是模型坐标系下,可以是世界坐标系下,也可以是相机坐标系下),关于怎么求这个视锥体 http://www.cnbl
  • 这是我将我所有公开算法资料整理的一个电子书,全部题目信息中文化,以前会有一些英文描述,感谢 @CYL 中文整理。 限时免费下载!后期随时可能收费 有些动图,在做成电子书(比如 pdf)时候自然就变没了,...
  • ACM模版描述题解给定一个矩阵任选一个子矩阵所拥有不同颜色期望个数。大致就是这么个意思。这里我们换位思考,可以考虑为每一种颜色贡献次数,也就是每一个颜色所出现产生贡献子矩阵个数。最后将...
  • hdu 3255 Farming

    2013-03-30 01:14:00
    有一块田,上面有n个矩阵,每个矩阵对应一个权值,矩阵相交的部分取权值大的,问最后能获得多少值 我们可以转换一下模型,将权值看成矩阵的高,那么题目就成了n个长方体并,由于m只有3,所以我们可以枚举高度,在...

空空如也

空空如也

1 2 3
收藏数 55
精华内容 22
关键字:

一个矩阵的模怎么求